Full Code of terryma/vim-multiple-cursors for AI

master 6456718e1d30 cached
15 files
97.1 KB
27.6k tokens
10 symbols
1 requests
Download .txt
Repository: terryma/vim-multiple-cursors
Branch: master
Commit: 6456718e1d30
Files: 15
Total size: 97.1 KB

Directory structure:
gitextract_2zx8hs8j/

├── .gitignore
├── .rspec
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Gemfile
├── MIT-LICENSE.txt
├── README.md
├── Rakefile
├── autoload/
│   └── multiple_cursors.vim
├── doc/
│   └── multiple_cursors.txt
├── plugin/
│   └── multiple_cursors.vim
└── spec/
    ├── benchmark_spec.rb
    ├── multiple_cursors_spec.rb
    └── spec_helper.rb

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
/doc/tags


================================================
FILE: .rspec
================================================
--color
--format d


================================================
FILE: .travis.yml
================================================
os: linux
dist: bionic
language: ruby

addons:
  apt:
    packages:
      - vim-gtk
      - xvfb

script:
  - xvfb-run bundle exec rake


================================================
FILE: CHANGELOG.md
================================================
## 2.2 (06/10/2013)
Bugfixes:
  - Fix plugin break in PASTE mode. This fixes #44.

## 2.1 (04/26/2013)

Bugfixes:
  - Fix 1 regression where cursors could potentially get out of sync in insert mode

Features:
  - Added some logic to debug latency. Fanning out to 30 cursors in insert mode with my vimrc took over 300ms. It's like than 20ms with a plain vimrc. Need to debug what setting is causing the slowing down in insert mode and inform users.

## 2.0 (04/24/2013)

Bugfixes:
  - Fix inconsistent undo behavior. Changes made in multicursor insert mode are now undone together. This fixes #22.
  - Single key commands that do not terminate properly no longer cause ghostly cursors to linger on screen. An error message is now displayed informing the user the number of cursor locations that the input cannot be properly played back at. This fixes #28.

## 1.16 (04/23/2013)

Features:
  - Add integration tests using vimrunner. Hook up travis-ci to run continous integration on commit.

## 1.15 (04/22/2013)

Bugfixes:
  - Fix plugin causing error bell. This fixes #29.

## 1.14 (04/22/2013)

Features:
  - Allow users to separate start key from next key. (credit: @xanderman)

## 1.13 (04/22/2013)

Bugfixes:
  - Add support for switching to visual line mode from inside multicursor mode
  - Fix highlight issue where extra character at end of line is highlighted for visual selections covering more than 2 lines.

## 1.12 (04/19/2013)

Bugfixes:
  - Fix tab character causing highlight errors. This fixes #18 and fixes #32

## 1.11 (04/18/2013)

Bugfixes:
  - Fix regression where `C-n` doesn't exhibit correct behavior when all matches have been found
  - Clear echo messages when a new input is received

## 1.10 (04/17/2013)

Bugfixes:
  - `O` works now in normal mode. This fixes #24
  - Turn on `lazyredraw` during multicursor mode to prevent the sluggish screen redraws

Features:
  - Add command **MultipleCursorsFind** to add multiple virtual cursors using regexp. This closes #20

## 1.9 (04/17/2013)

Bugfixes:
  - Fix starting multicursor mode in visual line mode. This fixes #25
  - Major refactoring to avoid getting in and out of visual mode as much as possible

## 1.8 (04/16/2013)

Bugfixes:
  - Fix regression that causes call stack to explode with too many cursors

## 1.7 (04/15/2013)

Bugfixes:
  - Finally fix the annoying highlighting problem when the last virtual cursor is on the last character of the line. The solution is a hack, but it should be harmless

## 1.6 (04/15/2013)

Bugfixes:
  - Stop chaining dictionary function calls. This fixes #10 and #11

## 1.5 (04/15/2013)

Bugfixes:
  - Exit Vim's visual mode before waiting for user's next input. This fixes #14

## 1.4 (04/14/2013)

Bugfixes:
  - Don't use clearmatches(). It clears highlighting from other plugins. This fixes #13

## 1.3 (04/14/2013)

Bugfixes:
  - Change mapping from using expression-quote syntax to using raw strings

## 1.2 (04/14/2013)

Bugfixes:
  - Restore view when exiting from multicursor mode. This fixes #5
  - Remove the unnecessary user level mapping for 'prev' and 'skip' in visual mode, since we can purely detect those keys from multicursor mode

## 1.1 (04/14/2013)

Bugfixes:
  - Stop hijacking escape key in normal mode. This fixes #1, #2, and #3

## 1.0 (04/13/2013)

Initial release


================================================
FILE: CONTRIBUTING.md
================================================
# Problems summary

## Expected

## Environment Information
 * OS:
 * Neovim/Vim/Gvim version:

## Provide a minimal .vimrc with less than 50 lines

    " Your minimal.vimrc

## Generate a logfile if appropriate

 1. export NVIM_PYTHON_LOG_FILE=/tmp/log
 2. export NVIM_PYTHON_LOG_LEVEL=DEBUG
 3. nvim -u minimal.vimrc
 4. recreate your issue
 5. cat /tmp/log_{PID}

## Screen shot (if possible)

## Upload the log file


================================================
FILE: Gemfile
================================================
source 'https://rubygems.org'
gem 'vimrunner'
gem 'rake'
gem 'rspec'


================================================
FILE: MIT-LICENSE.txt
================================================
Copyright 2013 Terry Ma

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: README.md
================================================
# **❗ This plugin is deprecated, use [vim-visual-multi](https://github.com/mg979/vim-visual-multi) instead ❗**

# vim-multiple-cursors
[![Build Status](https://travis-ci.org/terryma/vim-multiple-cursors.svg?branch=master)](https://travis-ci.org/github/terryma/vim-multiple-cursors)

## Contents
 - [About](#about)
 - [Installation](#installation)
 - [Quick Start](#quick-start)
 - [Mapping](#mapping)
 - [Settings](#settings)
 - [Interactions with other plugins](#interactions-with-other-plugins)
 - [Highlight](#highlight)
 - [FAQ](#faq)
 - [Contributing](#contributing)
 - [Credit](#credit)

## About
[There](https://github.com/paradigm/vim-multicursor) [have](https://github.com/felixr/vim-multiedit) [been](https://github.com/hlissner/vim-multiedit) [many](https://github.com/adinapoli/vim-markmultiple) [attempts](https://github.com/AndrewRadev/multichange.vim) at bringing Sublime Text's awesome [multiple selection][sublime-multiple-selection] feature into Vim, but none so far have been in my opinion a faithful port that is simplistic to use, yet powerful and intuitive enough for an existing Vim user. [vim-multiple-cursors] is yet another attempt at that.

### It's great for quick refactoring
![Example1](assets/example1.gif?raw=true)

Vim command sequence: `fp<C-n><C-n><C-n>cname`

### Add a cursor to each line of your visual selection
![Example2](assets/example2.gif?raw=true)

Vim command sequence: `vip<C-n>i"<Right><Right><Right>",<Esc>vipgJ$r]Idays = [`

### Match characters from visual selection
![Example3](assets/example3.gif?raw=true)

Vim command sequence: `df[$r,0f,v<C-n>…<C-n>c<CR><Up><Del><Right><Right><Right><Del>`

### Use the command to match regexp
![Example4](assets/example4.gif?raw=true)

To see what keystrokes are used for the above examples, see [the wiki page](https://github.com/terryma/vim-multiple-cursors/wiki/Keystrokes-for-example-gifs).

## Installation
Install using [Pathogen], [Vundle], [Neobundle], [vim-plug], or your favorite Vim package manager.

Requires vim 7.4 or newer for full functionality.

### vim-plug instructions

1. Paste this block into the top of `~/.vimrc`.

```vim script
call plug#begin()

Plug 'terryma/vim-multiple-cursors'

call plug#end()
```

2. Start vim and execute `:PlugInstall`.

## Quick Start
### normal mode / visual mode
  * start:          `<C-n>` start multicursor and add a _virtual cursor + selection_ on the match
    * next:         `<C-n>` add a new _virtual cursor + selection_ on the next match
    * skip:         `<C-x>` skip the next match
    * prev:         `<C-p>` remove current _virtual cursor + selection_ and go back on previous match
  * select all:     `<A-n>` start multicursor and directly select all matches

You can now change the _virtual cursors + selection_ with **visual mode** commands.
For instance: `c`, `s`, `I`, `A` work without any issues.
You could also go to **normal mode** by pressing `v` and use normal commands there.

At any time, you can press `<Esc>` to exit back to regular Vim.

**NOTE**: start with `g<C-n>` to match without boundaries (behaves like `g*` instead of `*`)

### visual mode when multiple lines are selected
  * start: `<C-n>` add _virtual cursors_ on each line

You can now change the _virtual cursors_ with **normal mode** commands.
For instance: `ciw`.

### command
The command `MultipleCursorsFind` accepts a range and a pattern (regexp), it creates a _visual cursor_ at the end of each match.
If no range is passed in, then it defaults to the entire buffer.


## Mapping
If you don't like the plugin taking over your key bindings, you can turn it off and reassign them the way you want:
```viml
let g:multi_cursor_use_default_mapping=0

" Default mapping
let g:multi_cursor_start_word_key      = '<C-n>'
let g:multi_cursor_select_all_word_key = '<A-n>'
let g:multi_cursor_start_key           = 'g<C-n>'
let g:multi_cursor_select_all_key      = 'g<A-n>'
let g:multi_cursor_next_key            = '<C-n>'
let g:multi_cursor_prev_key            = '<C-p>'
let g:multi_cursor_skip_key            = '<C-x>'
let g:multi_cursor_quit_key            = '<Esc>'
```

**NOTE:** Please make sure to always map something to `g:multi_cursor_quit_key`, otherwise you'll have a tough time quitting from multicursor mode.

## Settings
Currently there are four additional global settings one can tweak:

### ```g:multi_cursor_support_imap``` (Default: 1)
If set to 0, insert mappings won't be supported in _Insert_ mode anymore.

### ```g:multi_cursor_exit_from_visual_mode``` (Default: 0)
If set to 1, then pressing `g:multi_cursor_quit_key` in _Visual_ mode will quit and
delete all existing cursors, just skipping normal mode with multiple cursors.

### ```g:multi_cursor_exit_from_insert_mode``` (Default: 0)
If set to 1, then pressing `g:multi_cursor_quit_key` in _Insert_ mode will quit and
delete all existing cursors, just skipping normal mode with multiple cursors.

### ```g:multi_cursor_normal_maps``` (Default: see below)
`{'@': 1, 'F': 1, 'T': 1, '[': 1, '\': 1, ']': 1, '!': 1, '"': 1, 'c': 1, 'd': 1, 'f': 1, 'g': 1, 'm': 1, 'q': 1, 'r': 1, 't': 1, 'y': 1, 'z': 1, '<': 1, '=': 1, '>': 1}`

Any key in this map (values are ignored) will cause multi-cursor _Normal_ mode
to pause for map completion just like normal vim. Otherwise keys mapped in
normal mode will "fail to replay" when multiple cursors are active.
For example: `{'d':1}` makes normal-mode command `dw` work in multi-cursor mode.

The default list contents should work for anybody, unless they have remapped a
key from an operator-pending command to a non-operator-pending command or
vice versa.

These keys must be manually listed because vim doesn't provide a way to
automatically see which keys _start_ mappings, and trying to run motion commands
such as `j` as if they were operator-pending commands can break things.

### ```g:multi_cursor_visual_maps``` (Default: see below)
`{'T': 1, 'a': 1, 't': 1, 'F': 1, 'f': 1, 'i': 1}`

Same principle as `g:multi_cursor_normal_maps`

### Interactions with other plugins

### ```Multiple_cursors_before/Multiple_cursors_after``` (Default: `nothing`)

Other plugins may be incompatible in insert mode.
That is why we provide hooks to disable those plug-ins when vim-multiple-cursors is active:

For example, if you are using [Neocomplete](https://github.com/Shougo/neocomplete.vim),
add this to your vimrc to prevent conflict:

```viml
function! Multiple_cursors_before()
  if exists(':NeoCompleteLock')==2
    exe 'NeoCompleteLock'
  endif
endfunction

function! Multiple_cursors_after()
  if exists(':NeoCompleteUnlock')==2
    exe 'NeoCompleteUnlock'
  endif
endfunction
```

Plugins themselves can register `User` autocommands on `MultipleCursorsPre` and
`MultipleCursorsPost` for automatic integration.

### Highlight
The plugin uses the highlight group `multiple_cursors_cursor` and `multiple_cursors_visual` to highlight the virtual cursors and their visual selections respectively. You can customize them by putting something similar like the following in your vimrc:

```viml
" Default highlighting (see help :highlight and help :highlight-link)
highlight multiple_cursors_cursor term=reverse cterm=reverse gui=reverse
highlight link multiple_cursors_visual Visual
```

## FAQ

#### **Q**  Pressing <kbd>i</kbd> after selecting words with <kbd>C-n</kbd> makes the plugin hang, why?
**A** When selecting words with <kbd>C-n</kbd>, the plugin behaves like in **visual** mode.
Once you pressed <kbd>i</kbd>, you can still press <kbd>I</kbd> to insert text.

#### **Q** <kbd>ALT</kbd>+<kbd>n</kbd> doesn't seem to work in VIM but works in gVIM, why?
**A** This is a well known terminal/Vim [issue](http://vim.wikia.com/wiki/Get_Alt_key_to_work_in_terminal), different terminal have different ways to send ```Alt+key```.
Try adding this in your `.vimrc` and **make sure to replace the string**:
```vim
if !has('gui_running')
  map "in Insert mode, type Ctrl+v Alt+n here" <A-n>
endif
```
Or remap the following:
```vim
g:multi_cursor_start_key
g:multi_cursor_select_all_key
```

#### **Q** <kbd>CTRL</kbd>+<kbd>n</kbd> doesn't seem to work in gVIM?
**A** Try setting `set selection=inclusive` in your `~/.gvimrc`

**A** Alternatively, you can just temporarily disable _exclusive_ selection whenever the plugin is active:
```VimL
augroup MultipleCursorsSelectionFix
    autocmd User MultipleCursorsPre  if &selection ==# 'exclusive' | let g:multi_cursor_save_selection = &selection | set selection=inclusive | endif
    autocmd User MultipleCursorsPost if exists('g:multi_cursor_save_selection') | let &selection = g:multi_cursor_save_selection | unlet g:multi_cursor_save_selection | endif
augroup END
```

### **Q** deoplete insert giberrish, how to fix this?
**A** use the `Multiple_cursors` functions, add this in your vimrc:

```VimL
    func! Multiple_cursors_before()
      if deoplete#is_enabled()
        call deoplete#disable()
        let g:deoplete_is_enable_before_multi_cursors = 1
      else
        let g:deoplete_is_enable_before_multi_cursors = 0
      endif
    endfunc
    func! Multiple_cursors_after()
      if g:deoplete_is_enable_before_multi_cursors
        call deoplete#enable()
      endif
    endfunc
```

#### **Q** is it also working on Mac?
**A** On Mac OS, [MacVim](https://code.google.com/p/macvim/) is known to work.

#### **Q** How can I select `n` keywords with several keystrokes? `200<C-n>` does not work.
**A** You can use :MultipleCursorsFind keyword. I have this binding in my vimrc:

```VimL
nnoremap <silent> <M-j> :MultipleCursorsFind <C-R>/<CR>
vnoremap <silent> <M-j> :MultipleCursorsFind <C-R>/<CR>
```

This allows one to search for the keyword using `*` and turn search results into cursors with `Alt-j`.


## Contributing
Patches and suggestions are always welcome! A list of open feature requests can be found [here](https://github.com/terryma/vim-multiple-cursors/labels/pull%20request%20welcome).

### Issue Creation
Contributor's time is precious and limited. Please ensure it meets the requirements outlined in [CONTRIBUTING.md](CONTRIBUTING.md).

### Pull Requests
Running the test suite requires ruby and rake as well as vim of course. Before submitting PR, please ensure the checks are passing:
```bash
cd vim-multiple-cursors/spec/
bundle exec rake
```

### Contributors
This is a community supported project. Here is the list of all the [Contributors](https://github.com/terryma/vim-multiple-cursors/graphs/contributors)

## Credit
Obviously inspired by Sublime Text's [multiple selection][sublime-multiple-selection] feature, also encouraged by Emac's [multiple cursors][emacs-multiple-cursors] implementation by Magnar Sveen

[vim-multiple-cursors]:http://github.com/terryma/vim-multiple-cursors
[sublime-multiple-selection]:http://www.sublimetext.com/docs/2/multiple_selection_with_the_keyboard.html
[Pathogen]:http://github.com/tpope/vim-pathogen
[Vundle]:http://github.com/gmarik/vundle
[Neobundle]:http://github.com/Shougo/neobundle.vim
[vim-plug]:https://github.com/junegunn/vim-plug
[emacs-multiple-cursors]:https://github.com/magnars/multiple-cursors.el


================================================
FILE: Rakefile
================================================
require 'rspec/core/rake_task'

RSpec::Core::RakeTask.new(:spec) do |t|
  t.pattern = 'spec/multiple_cursors_spec.rb'
end

RSpec::Core::RakeTask.new(:benchmark) do |t|
  t.pattern = 'spec/benchmark_spec.rb'
end

task :default => :spec


================================================
FILE: autoload/multiple_cursors.vim
================================================
"===============================================================================
" Initialization
"===============================================================================

" Tweak key settings. If the key is set using 'expr-quote' (h: expr-quote), then
" there's nothing that we need to do. If it's set using raw strings, then we
" need to convert it.  We need to resort to such voodoo exec magic here to get
" it to work the way we like. '<C-n>' is converted to '\<C-n>' by the end and
" the global vars are replaced by their new value. This is ok since the mapping
" using '<C-n>' should already have completed in the plugin file.
for s:key in [ 'g:multi_cursor_next_key',
           \ 'g:multi_cursor_prev_key',
           \ 'g:multi_cursor_skip_key',
           \ 'g:multi_cursor_quit_key' ]
  if exists(s:key)
    " Translate raw strings like "<C-n>" into key code like "\<C-n>"
    exec 'let s:temp = '.s:key
    if s:temp =~ '^<.*>$'
      exec 'let '.s:key.' = "\'.s:temp.'"'
    endif
  else
    " If the user didn't define it, initialize it to an empty string so the
    " logic later don't break
    exec 'let '.s:key.' = ""'
  endif
endfor
unlet! s:key s:temp

" These keys will not be replicated at every cursor location. Make sure that
" this assignment happens AFTER the key tweak setting above
let s:special_keys = {
      \ 'v': [ g:multi_cursor_next_key, g:multi_cursor_prev_key, g:multi_cursor_skip_key ],
      \ 'n': [ g:multi_cursor_next_key ],
      \ }

" The highlight group we use for all the cursors
let s:hi_group_cursor = 'multiple_cursors_cursor'

" The highlight group we use for all the visual selection
let s:hi_group_visual = 'multiple_cursors_visual'

" Used for preventing multiple calls on before function
let s:before_function_called = 0

" Used for searching whole words (search pattern is wrapped with \< and \>)
" Keep old behaviour by default (act like g*)
let s:use_word_boundary = 1

" Set up highlighting
if !hlexists(s:hi_group_cursor)
  exec "highlight ".s:hi_group_cursor." term=reverse cterm=reverse gui=reverse"
endif
if !hlexists(s:hi_group_visual)
  exec "highlight link ".s:hi_group_visual." Visual"
endif

" Temporary buffer that is used for individual paste buffer save/restore
" operations
let s:paste_buffer_temporary_text = ''
let s:paste_buffer_temporary_type = ''

"===============================================================================
" Internal Mappings
"===============================================================================

inoremap <silent> <Plug>(multiple-cursors-input) <C-o>:call <SID>process_user_input()<CR>
nnoremap <silent> <Plug>(multiple-cursors-input) :call <SID>process_user_input()<CR>
xnoremap <silent> <Plug>(multiple-cursors-input) :<C-u>call <SID>process_user_input()<CR>

inoremap <silent> <Plug>(multiple-cursors-apply) <C-o>:call <SID>apply_user_input_next('i')<CR>
nnoremap <silent> <Plug>(multiple-cursors-apply) :call <SID>apply_user_input_next('n')<CR>
xnoremap <silent> <Plug>(multiple-cursors-apply) :<C-u>call <SID>apply_user_input_next('v')<CR>

inoremap <silent> <Plug>(multiple-cursors-detect) <C-o>:call <SID>detect_bad_input()<CR>
nnoremap <silent> <Plug>(multiple-cursors-detect) :call <SID>detect_bad_input()<CR>
xnoremap <silent> <Plug>(multiple-cursors-detect) :<C-u>call <SID>detect_bad_input()<CR>

inoremap <silent> <Plug>(multiple-cursors-wait) <C-o>:call <SID>wait_for_user_input('')<CR>
nnoremap <silent> <Plug>(multiple-cursors-wait) :call <SID>wait_for_user_input('')<CR>
xnoremap <silent> <Plug>(multiple-cursors-wait) :<C-u>call <SID>wait_for_user_input('')<CR>

" Note that although these mappings are seemingly triggerd from Visual mode,
" they are in fact triggered from Normal mode. We quit visual mode to allow the
" virtual highlighting to take over
nnoremap <silent> <Plug>(multiple-cursors-prev) :<C-u>call multiple_cursors#prev()<CR>
nnoremap <silent> <Plug>(multiple-cursors-skip) :<C-u>call multiple_cursors#skip()<CR>
nnoremap <silent> <Plug>(multiple-cursors-new) :<C-u>call multiple_cursors#new('v', 0)<CR>
nnoremap <silent> <Plug>(multiple-cursors-new-word) :<C-u>call multiple_cursors#new('v', 1)<CR>

"===============================================================================
" Public Functions
"===============================================================================

" Print some debugging info
function! multiple_cursors#debug()
  call s:cm.debug()
endfunction

function! multiple_cursors#get_latency_debug_file()
  return s:latency_debug_file
endfunction


function! s:fire_pre_triggers()
  if !s:before_function_called
    silent doautocmd User MultipleCursorsPre
    if exists('*Multiple_cursors_before')
      exe "call Multiple_cursors_before()"
    endif
    let s:before_function_called = 1
  endif
endfunction

" Creates a new cursor. Different logic applies depending on the mode the user
" is in and the current state of the buffer.
" 1. In normal mode, a new cursor is created at the end of the word under Vim's
" normal cursor
" 2. In visual mode, if the visual selection covers more than one line, a new
" cursor is created at the beginning of each line
" 3. In visual mode, if the visual selection covers a single line, a new cursor
" is created at the end of the visual selection. Another cursor will be
" attempted to be created at the next occurrence of the visual selection
function! multiple_cursors#new(mode, word_boundary)
  " Call before function if exists only once until it is canceled (<Esc>)
  call s:fire_pre_triggers()
  let s:use_word_boundary = a:word_boundary
  if a:mode ==# 'n'
    " Reset all existing cursors, don't restore view and setting
    call s:cm.reset(0, 0)

    " Select the word under cursor to set the '< and '> marks
    exec "normal! viw"
    call s:exit_visual_mode()

    " Add cursor with the current visual selection
    call s:cm.add(s:pos("'>"), s:region("'<", "'>"))
    call s:wait_for_user_input('v')
  elseif a:mode ==# 'v'
    " If the visual area covers the same line, then do a search for next
    " occurrence
    let start = line("'<")
    let finish = line("'>")
    if start != finish
      call s:cm.reset(0, 0)
      let col = col("'<")
      for line in range(line("'<"), line("'>"))
        let pos = [line, col]
        call s:cm.add(pos)
      endfor
      " Start in normal mode
      call s:wait_for_user_input('n')
    else
      " Came directly from visual mode
      if s:cm.is_empty()
        call s:cm.reset(0, 0)

        if visualmode() ==# 'V'
          let left = [line('.'), 1]
          let right = [line('.'), col('$')-1]
          if right[1] == 0 " empty line
            return
          endif
          call s:cm.add(right, [left, right])
        else
          call s:cm.add(s:pos("'>"), s:region("'<", "'>"))
        endif
      endif
      let content = s:get_text(s:region("'<", "'>"))
      let next = s:find_next(content)
      if s:cm.add(next[1], next)
        call s:update_visual_markers(next)
      else
        call cursor(s:cm.get_current().position)
        echohl WarningMsg | echo 'No more matches' | echohl None
      endif
      call s:wait_for_user_input('v')
    endif
  endif
endfunction

" Quit out of multicursor mode, fixes #27.
function! multiple_cursors#quit()
  call s:exit()
endfunction

" Delete the current cursor. If there's no more cursors, stop the loop
function! multiple_cursors#prev()
  call s:cm.delete_current()
  if !s:cm.is_empty()
    call s:update_visual_markers(s:cm.get_current().visual)
    call cursor(s:cm.get_current().position)
    call s:wait_for_user_input('v')
  endif
endfunction

" Skip the current cursor and move to the next cursor
function! multiple_cursors#skip()
  call s:cm.delete_current()
  let content = s:get_text(s:region("'<", "'>"))
  let next = s:find_next(content)
  call s:cm.add(next[1], next)
  call s:update_visual_markers(next)
  call s:wait_for_user_input('v')
endfunction

" Search for pattern between the start and end line number. For each match, add
" a virtual cursor at the end and start multicursor mode
" This function is called from a command. User commands in Vim do not support
" passing in column ranges. If the user selects a block of text in visual mode,
" but not visual line mode, we only want to match patterns within the actual
" visual selection. We get around this by checking the last visual selection and
" see if its start and end lines match the input. If so, we assume that the user
" did a normal visual selection and we use the '< and '> marks to define the
" region instead of start and end from the method parameter.
function! multiple_cursors#find(start, end, pattern)
  let s:cm.saved_winview = winsaveview()
  let s:cm.start_from_find = 1
  if visualmode() ==# 'v' && a:start == line("'<") && a:end == line("'>")
    let pos1 = s:pos("'<")
    let pos2 = s:pos("'>")
  else
    let pos1 = [a:start, 1]
    let pos2 = [a:end, col([a:end, '$'])]
  endif
  call cursor(pos1)
  let first = 1
  while 1
    if first
      " Set `virtualedit` to 'onemore' for the first search to consistently
      " match patterns like '$'
      let saved_virtualedit = &virtualedit
      let &virtualedit = "onemore"
      " First search starts from the current position
      let match = search(a:pattern, 'cW')
    else
      let match = search(a:pattern, 'W')
    endif
    if !match
      break
    endif
    let left = s:pos('.')
    " Perform an intermediate backward search to correctly match patterns like
    " '^' and '$'
    let match = search(a:pattern, 'bceW')
    let right = s:pos('.')
    " Reset the cursor and perform a normal search if the intermediate search
    " wasn't successful
    if !match || s:compare_pos(right, left) != 0
      call cursor(left)
      call search(a:pattern, 'ceW')
      let right = s:pos('.')
    endif
    if first
      let &virtualedit = saved_virtualedit
      let first = 0
    endif
    if s:compare_pos(right, pos2) > 0
      " Position the cursor at the end of the previous match so it'll be on a
      " virtual cursor when multicursor mode is started. The `winrestview()`
      " call below 'undoes' unnecessary repositionings
      call search(a:pattern, 'be')
      break
    endif
    call s:cm.add(right, [left, right])
    " Redraw here forces the cursor movement to be updated. This prevents the
    " jerky behavior when doing any action once the cursors are added. But it
    " also slows down adding the cursors dramatically. We need to a better
    " solution here
    " redraw
  endwhile
  if s:cm.is_empty()
    call winrestview(s:cm.saved_winview)
    echohl ErrorMsg | echo 'No match found' | echohl None
    return
  else
    echohl Normal | echo 'Added '.s:cm.size().' cursor'.(s:cm.size()>1?'s':'') | echohl None

    " If we've created any cursors, we need to call the before function, end
    " function will be called via normal routes
    call s:fire_pre_triggers()

    call s:wait_for_user_input('v')
  endif
endfunction

" apply multiple_cursors#find() on the whole buffer
function! multiple_cursors#select_all(mode, word_boundary)
  if a:mode == 'v'
    let a_save = @a
    normal! gv"ay
    let pattern = @a
    let @a = a_save
  elseif a:mode == 'n'
    let pattern = expand('<cword>')
  endif
  if a:word_boundary == 1
    let pattern = '\<'.pattern.'\>'
  endif
  call multiple_cursors#find(1, line('$'), pattern)
endfunction

"===============================================================================
" Cursor class
"===============================================================================
let s:Cursor = {}

" Create a new cursor. Highlight it and save the current line length
function! s:Cursor.new(position)
  let obj = copy(self)
  let obj.position = copy(a:position)
  let obj.visual = []
  let obj.saved_visual = []
  " Stores text that was yanked after any commands in Normal or Visual mode
  let obj.paste_buffer_text = getreg('"')
  let obj.paste_buffer_type = getregtype('"')
  let obj.cursor_hi_id = s:highlight_cursor(a:position)
  let obj.visual_hi_id = 0
  let obj.line_length = col([a:position[0], '$'])
  if has('folding')
    silent! execute a:position[0] . "foldopen!"
  endif
  return obj
endfunction

" Return the line the cursor is on
function! s:Cursor.line() dict
  return self.position[0]
endfunction

" Return the column the cursor is on
function! s:Cursor.column() dict
  return self.position[1]
endfunction

" Move the cursor location by the number of lines and columns specified in the
" input. The input can be negative.
function! s:Cursor.move(line, column) dict
  let self.position[0] += a:line
  let self.position[1] += a:column
  if !empty(self.visual)
    let self.visual[0][0] += a:line
    let self.visual[0][1] += a:column
    let self.visual[1][0] += a:line
    let self.visual[1][1] += a:column
  endif
  call self.update_highlight()
endfunction

" Update the current position of the cursor
function! s:Cursor.update_position(pos) dict
  let self.position[0] = a:pos[0]
  let self.position[1] = a:pos[1]
  call self.update_highlight()
endfunction

" Reapply the highlight on the cursor
function! s:Cursor.update_highlight() dict
  call s:cm.remove_highlight(self.cursor_hi_id)
  let self.cursor_hi_id = s:highlight_cursor(self.position)
endfunction

" Refresh the length of the line the cursor is on. This could change from
" underneath
function! s:Cursor.update_line_length() dict
  let self.line_length = col([self.line(), '$'])
endfunction

" Update the visual selection and its highlight
function! s:Cursor.update_visual_selection(region) dict
  let self.visual = deepcopy(a:region)
  call s:cm.remove_highlight(self.visual_hi_id)
  let self.visual_hi_id = s:highlight_region(a:region)
endfunction

" Remove the visual selection and its highlight
function! s:Cursor.remove_visual_selection() dict
  let self.saved_visual = deepcopy(self.visual)
  let self.visual = []
  " TODO(terryma): Move functionality into separate class
  call s:cm.remove_highlight(self.visual_hi_id)
  let self.visual_hi_id = 0
endfunction

" Restore unnamed register from paste buffer
function! s:Cursor.restore_unnamed_register() dict
  call setreg('"', self.paste_buffer_text, self.paste_buffer_type)
endfunction

" Save contents of the unnamed register into paste buffer
function! s:Cursor.save_unnamed_register() dict
  let self.paste_buffer_text = getreg('"')
  let self.paste_buffer_type = getregtype('"')
endfunction

"===============================================================================
" CursorManager class
"===============================================================================
let s:CursorManager = {}

" Constructor
function! s:CursorManager.new()
  let obj = copy(self)
  " List of Cursors we're managing
  let obj.cursors = []
  " Current index into the s:cursors array
  let obj.current_index = -1
  " This marks the starting cursor index into the s:cursors array
  let obj.starting_index = -1
  " We save some user settings when the plugin loads initially
  let obj.saved_settings = {
        \ 'virtualedit': &virtualedit,
        \ 'cursorline': &cursorline,
        \ 'lazyredraw': &lazyredraw,
        \ 'paste': &paste,
        \ 'clipboard': &clipboard,
        \ }
  " We save the window view when multicursor mode is entered
  let obj.saved_winview = []
  " Track whether we started multicursor mode from calling multiple_cursors#find
  let obj.start_from_find = 0
  return obj
endfunction

" Clear all cursors and their highlights
function! s:CursorManager.reset(restore_view, restore_setting, ...) dict
  if a:restore_view
    " Return the view back to the beginning
    if !empty(self.saved_winview)
      call winrestview(self.saved_winview)
    endif

    " If the cursor moved, just restoring the view could get confusing, let's
    " put the cursor at where the user left it. Only do this if we didn't start
    " from find mode
    if !self.is_empty() && !self.start_from_find
      call cursor(self.get(0).position)
    endif
  endif

  " Delete all cursors and clear their highlights. Don't do clearmatches() as
  " that will potentially interfere with other plugins
  if !self.is_empty()
    for i in range(self.size())
      call self.remove_highlight(self.get(i).cursor_hi_id)
      call self.remove_highlight(self.get(i).visual_hi_id)
    endfor
  endif

  let self.cursors = []
  let self.current_index = -1
  let self.starting_index = -1
  let self.saved_winview = []
  let self.start_from_find = 0
  let s:char = ''
  let s:saved_char = ''
  if a:restore_setting
    call self.restore_user_settings()
  endif
  " Call after function if exists and only if action is canceled (<Esc>)
  if a:0 && s:before_function_called
    if exists('*Multiple_cursors_after')
      exe "call Multiple_cursors_after()"
    endif
    silent doautocmd User MultipleCursorsPost
    let s:before_function_called = 0
  endif
endfunction

" Returns 0 if it's not managing any cursors at the moment
function! s:CursorManager.is_empty() dict
  return self.size() == 0
endfunction

" Returns the number of cursors it's managing
function! s:CursorManager.size() dict
  return len(self.cursors)
endfunction

" Returns the current cursor
function! s:CursorManager.get_current() dict
  return self.cursors[self.current_index]
endfunction

" Returns the cursor at index i
function! s:CursorManager.get(i) dict
  return self.cursors[a:i]
endfunction

" Removes the current cursor and all its associated highlighting. Also update
" the current index
function! s:CursorManager.delete_current() dict
  call self.remove_highlight(self.get_current().cursor_hi_id)
  call self.remove_highlight(self.get_current().visual_hi_id)
  call remove(self.cursors, self.current_index)
  let self.current_index -= 1
endfunction

" Remove the highlighting if its matchid exists
function! s:CursorManager.remove_highlight(hi_id) dict
  if a:hi_id
    " If the user did a matchdelete or a clearmatches, we don't want to barf if
    " the matchid is no longer valid
    silent! call matchdelete(a:hi_id)
  endif
endfunction

function! s:CursorManager.debug() dict
  let i = 0
  for c in self.cursors
    echom 'cursor #'.i.': pos='.string(c.position).' visual='.string(c.visual)
    let i+=1
  endfor
  echom 'input = '.s:char
  echom 'index = '.self.current_index
  echom 'pos = '.string(s:pos('.'))
  echom '''< = '.string(s:pos("'<"))
  echom '''> = '.string(s:pos("'>"))
  echom 'to mode = '.s:to_mode
  echom 'from mode = '.s:from_mode
  " echom 'special keys = '.string(s:special_keys)
  echom ' '
endfunction

" Sync the current cursor to the current Vim cursor. This includes updating its
" location, its highlight, and potentially its visual region. Return true if the
" position changed, false otherwise
function! s:CursorManager.update_current() dict
  let cur = self.get_current()
  if s:to_mode ==# 'v' || s:to_mode ==# 'V'
    " If we're in visual line mode, we need to go to visual mode before we can
    " update the visual region
    if s:to_mode ==# 'V'
      exec "normal! gvv\<Esc>"
    endif
    " Sets the cursor at the right place
    exec "normal! gv\<Esc>"
    call cur.update_visual_selection(s:get_visual_region(s:pos('.')))
  elseif s:from_mode ==# 'v' || s:from_mode ==# 'V'
    " Save contents of unnamed register after each operation in Visual mode.
    " This should be executed after user input is processed, when unnamed
    " register already contains the text.
    call cur.save_unnamed_register()
    call cur.remove_visual_selection()
  elseif s:from_mode ==# 'i' && s:to_mode ==# 'n' && self.current_index != 0
    normal! h
  elseif s:from_mode ==# 'n'
    " Save contents of unnamed register after each operation in Normal mode.
    call cur.save_unnamed_register()
  endif
  let pos = s:pos('.')

  " If the total number of lines changed in the buffer, we need to potentially
  " adjust other cursor locations
  let vdelta = line('$') - s:saved_linecount
  if vdelta != 0
    if self.current_index != self.size() - 1
      let cur_column_offset = (cur.column() - col('.')) * -1
      let new_line_length = len(getline('.'))
      for i in range(self.current_index+1, self.size()-1)
        let hdelta = 0
        " Note: some versions of Vim don't like chaining function calls like
        " a.b().c(). For compatibility reasons, don't do it
        let c = self.get(i)
        " If there're other cursors on the same line, we need to adjust their
        " columns. This needs to happen before we adjust their line!
        if cur.line() == c.line() || cur.position == pos
          if vdelta > 0
            " Added a line
            let hdelta = cur_column_offset
          else
            " Removed a line
            let hdelta = new_line_length
          endif
        endif
        call c.move(vdelta, hdelta)
      endfor
    endif
  else
    " If the line length changes, for all the other cursors on the same line as
    " the current one, update their cursor location as well
    let hdelta = col('$') - cur.line_length
    " Only do this if we're still on the same line as before
    if hdelta != 0 && cur.line() == line('.')
      " Update all the cursor's positions that occur after the current cursor on
      " the same line
      if self.current_index != self.size() - 1
        for i in range(self.current_index+1, self.size()-1)
          let c = self.get(i)
          " Only do it for cursors on the same line
          if cur.line() == c.line()
            call c.move(0, hdelta)
          else
            " Early exit, if we're not on the same line, neither will any cursor
            " that come after this
            break
          endif
        endfor
      endif
    endif
  endif

  if cur.position == pos
    return 0
  endif
  call cur.update_position(pos)
  return 1
endfunction

" Advance to the next cursor
function! s:CursorManager.next() dict
  let self.current_index = (self.current_index + 1) % self.size()
endfunction

" Start tracking cursor updates
function! s:CursorManager.start_loop() dict
  let self.current_index  = 0
  let self.starting_index = 0
endfunction

" Returns true if we're cycled through all the cursors
function! s:CursorManager.loop_done() dict
  return self.current_index == self.starting_index
endfunction

" Tweak some user settings, and save our current window view. This is called
" every time multicursor mode is entered.
" virtualedit needs to be set to onemore for updates to work correctly
" cursorline needs to be turned off for the cursor highlight to work on the line
" where the real vim cursor is
" lazyredraw needs to be turned on to prevent jerky screen behavior with many
" cursors on screen
" paste mode needs to be switched off since it turns off a bunch of features
" that's critical for the plugin to function
" clipboard should not have unnamed and unnamedplus otherwise plugin cannot
" reliably use unnamed register ('"')
function! s:CursorManager.initialize() dict
  let self.saved_settings['virtualedit'] = &virtualedit
  let self.saved_settings['cursorline'] = &cursorline
  let self.saved_settings['lazyredraw'] = &lazyredraw
  let self.saved_settings['paste'] = &paste
  let self.saved_settings['clipboard'] = &clipboard
  let &virtualedit = "onemore"
  let &cursorline = 0
  let &lazyredraw = 1
  let &paste = 0
  set clipboard-=unnamed clipboard-=unnamedplus
  " We could have already saved the view from multiple_cursors#find
  if !self.start_from_find
    let self.saved_winview = winsaveview()
  endif

  " Save contents and type of unnamed register upon entering multicursor mode
  " to restore it later when leaving mode
  let s:paste_buffer_temporary_text = getreg('"')
  let s:paste_buffer_temporary_type = getregtype('"')
endfunction

" Restore user settings.
function! s:CursorManager.restore_user_settings() dict
  if !empty(self.saved_settings)
    let &virtualedit = self.saved_settings['virtualedit']
    let &cursorline = self.saved_settings['cursorline']
    let &lazyredraw = self.saved_settings['lazyredraw']
    let &paste = self.saved_settings['paste']
    let &clipboard = self.saved_settings['clipboard']
  endif

  " Restore original contents and type of unnamed register. This method is
  " called from reset, which calls us only when restore_setting argument is
  " true, which happens only when we leave multicursor mode. This should be
  " symmetrical to saving of unnamed register upon the start of multicursor
  " mode.
  call setreg('"', s:paste_buffer_temporary_text, s:paste_buffer_temporary_type)
endfunction

" Reposition all cursors to the start or end of their region
function! s:CursorManager.reposition_all_within_region(start) dict
  for c in self.cursors
    call c.update_position(c.saved_visual[a:start ? 0 : 1])
  endfor
endfunction

" Reselect the current cursor's region in visual mode
function! s:CursorManager.reapply_visual_selection() dict
  call s:select_in_visual_mode(self.get_current().visual)
endfunction

" Creates a new virtual cursor as 'pos'
" Optionally a 'region' object can be passed in as second argument. If set, the
" visual region of the cursor will be set to it
" Return true if the cursor has been successfully added, false otherwise
" Mode change: Normal -> Normal
" Cursor change: None (TODO Should we set Vim's cursor to pos?)
function! s:CursorManager.add(pos, ...) dict
  " Lazy init
  if self.is_empty()
    call self.initialize()
  endif

  " Don't add duplicates
  let i = 0
  for c in self.cursors
    if c.position == a:pos
      return 0
    endif
    let i+=1
  endfor

  let cursor = s:Cursor.new(a:pos)

  " Save the visual selection
  if a:0 > 0
    call cursor.update_visual_selection(a:1)
  endif

  call add(self.cursors, cursor)
  let self.current_index += 1
  return 1
endfunction

"===============================================================================
" Variables
"===============================================================================

" This is the last user input that we're going to replicate, in its string form
let s:char = ''
" This is either `I` or `A`, as input in Visual mode, that we're going to use
" to make the appropriate transition into Insert mode
let s:saved_char = ''
" This is the mode the user is in before s:char
let s:from_mode = ''
" This is the mode the user is in after s:char
let s:to_mode = ''
" This is the total number of lines in the buffer before processing s:char
let s:saved_linecount = -1
" This is used to apply the highlight fix. See s:apply_highight_fix()
let s:saved_line = 0
" This is the number of cursor locations where we detected an input that we
" cannot play back
let s:bad_input = 0
" Singleton cursor manager instance
let s:cm = s:CursorManager.new()

"===============================================================================
" Utility functions
"===============================================================================

" Return the position of the input marker as a two element array. First element
" is the line number, second element is the column number
function! s:pos(mark)
  let pos = getpos(a:mark)
  return [pos[1], pos[2]]
endfunction

" Return the region covered by the input markers as a two element array. First
" element is the position of the start marker, second element is the position of
" the end marker
function! s:region(start_mark, end_mark)
  return [s:pos(a:start_mark), s:pos(a:end_mark)]
endfunction

" Exit visual mode and go back to normal mode
" The reason for the additional gv\<Esc> is that it allows the cursor to stay
" on where it was before exiting
" Mode change: Normal -> Normal or Visual -> Normal
" Cursor change: If in visual mode, changed to exactly where it was on screen in
" visual mode. If in normal mode, changed to where the cursor was when the last
" visual selection ended
function! s:exit_visual_mode()
  exec "normal! \<Esc>gv\<Esc>"

  " Call before function if exists only once until it is canceled (<Esc>)
  if exists('*Multiple_cursors_before') && !s:before_function_called
    exe "call Multiple_cursors_before()"
    let s:before_function_called = 1
  endif
endfunction

" Visually select input region, where region is an array containing the start
" and end position. If start is after end, the selection simply goes backwards.
" Typically m<, m>, and gv would be a simple way of accomplishing this, but on
" some systems, the m< and m> marks are not supported. Note that v`` has random
" behavior if `` is the same location as the cursor location.
" Mode change: Normal -> Visual
" Cursor change: Set to end of region
" TODO: Refactor this and s:update_visual_markers
" FIXME: By using m` we're destroying the user's jumplist. We should use a
" different mark and use :keepjump
function! s:select_in_visual_mode(region)
  if a:region[0] == a:region[1]
    normal! v
  else
    call cursor(a:region[1])
    normal! m`
    call cursor(a:region[0])
    normal! v``
  endif

  " Unselect and reselect it again to properly set the '< and '> markers
  exec "normal! \<Esc>gv"
endfunction

" Update '< and '> to the input region
" Mode change: Normal -> Normal
" Cursor change: Set to the end of the region
function! s:update_visual_markers(region)
  if a:region[0] == a:region[1]
    normal! v
  else
    call cursor(a:region[1])
    normal! m`
    call cursor(a:region[0])
    normal! v``
  endif
  call s:exit_visual_mode()
endfunction

" Finds the next occurrence of the input text in the current buffer.
" Search is case sensitive
" Mode change: Normal -> Normal
" Cursor change: Set to the end of the match
function! s:find_next(text)
  let pattern = substitute(escape(a:text, '\'), '\n', '\\n', 'g')
  if s:use_word_boundary == 1
      let pattern = '\<'.pattern.'\>'
  endif
  let pattern = '\V\C'.pattern
  call search(pattern)
  let start = s:pos('.')
  call search(pattern, 'ce')
  let end = s:pos('.')
  return [start, end]
endfunction

" Highlight the position using the cursor highlight group
function! s:highlight_cursor(pos)
  " Give cursor highlight high priority, to overrule visual selection
  return matchadd(s:hi_group_cursor, '\%'.a:pos[0].'l\%'.a:pos[1].'c', 99999)
endfunction

" Compare two position arrays. Return a negative value if lhs occurs before rhs,
" positive value if after, and 0 if they are the same.
function! s:compare_pos(l, r)
  " If number lines are the same, compare columns
  return a:l[0] ==# a:r[0] ? a:l[1] - a:r[1] : a:l[0] - a:r[0]
endfunction

" Highlight the area bounded by the input region. The logic here really stinks,
" it's frustrating that Vim doesn't have a built in easier way to do this. None
" of the \%V or \%'m solutions work because we need the highlighting to stay for
" multiple places.
function! s:highlight_region(region)
  let s = sort(copy(a:region), "s:compare_pos")
  if s:to_mode ==# 'V'
    let pattern = '\%>'.(s[0][0]-1).'l\%<'.(s[1][0]+1).'l.*\ze.\_$'
  else
    if (s[0][0] == s[1][0])
      " Same line
      let pattern = '\%'.s[0][0].'l\%>'.(s[0][1]-1).'c.*\%<'.(s[1][1]+1).'c.'
    else
      " Two lines
      let s1 = '\%'.s[0][0].'l.\%>'.s[0][1].'c.*'
      let s2 = '\%'.s[1][0].'l.*\%<'.s[1][1].'c..'
      let pattern = s1.'\|'.s2
      " More than two lines
      if (s[1][0] - s[0][0] > 1)
        let pattern = pattern.'\|\%>'.s[0][0].'l\%<'.s[1][0].'l.*\ze.\_$'
      endif
    endif
  endif
  return matchadd(s:hi_group_visual, pattern)
endfunction

" Perform the operation that's necessary to revert us from one mode to another
function! s:revert_mode(from, to)
  if a:to ==# 'v'
    call s:cm.reapply_visual_selection()
  elseif a:to ==# 'V'
    call s:cm.reapply_visual_selection()
    normal! V
  elseif a:to ==# 'n' && a:from ==# 'i'
    stopinsert
  endif
endfunction

" Consume all the additional character the user typed between the last
" getchar() and here, to avoid potential race condition.
let s:saved_keys = ""
function! s:feedkeys(keys)
  while 1
    let c = getchar(0)
    let char_type = type(c)
    " Checking type is important, when strings are compared with integers,
    " strings are always converted to ints, and all strings are equal to 0
    if char_type == 0
      if c == 0
        break
      else
        let s:saved_keys .= nr2char(c)
      endif
    elseif char_type == 1 " char with more than 8 bits (as string)
      let s:saved_keys .= c
    endif
  endwhile
  call feedkeys(a:keys)
endfunction

" Take the user input and apply it at every cursor
function! s:process_user_input()
  " Grr this is frustrating. In Insert mode, between the feedkey call and here,
  " the current position could actually CHANGE for some odd reason. Forcing a
  " position reset here
  let cursor_position = s:cm.get_current()
  call cursor(cursor_position.position)

  " Before applying the user input, we need to revert back to the mode the user
  " was in when the input was entered
  call s:revert_mode(s:to_mode, s:from_mode)

  " Update the line length BEFORE applying any actions. TODO(terryma): Is there
  " a better place to do this?
  " let cursor_position = s:cm.get_current()
  call cursor_position.update_line_length()
  let s:saved_linecount = line('$')

  " Restore unnamed register only in Normal mode. This should happen before user
  " input is processed.
  if s:from_mode ==# 'n' || s:from_mode ==# 'v' || s:from_mode ==# 'V'
    call cursor_position.restore_unnamed_register()
  endif

  " Apply the user input. Note that the above could potentially change mode, we
  " use the mapping below to help us determine what the new mode is
  " Note that it's possible that \<Plug>(multiple-cursors-apply) never gets called, we have a
  " detection mechanism using \<Plug>(multiple-cursors-detect). See its documentation for more details

  " Assume that input is not valid
  let s:valid_input = 0

  " If we're coming from insert mode or going into insert mode, always chain the
  " undos together.
  " FIXME(terryma): Undo always places the cursor at the beginning of the line.
  " Figure out why.
  if s:from_mode ==# 'i' || s:to_mode ==# 'i'
    silent! undojoin | call s:feedkeys(s:char."\<Plug>(multiple-cursors-apply)")
  else
    call s:feedkeys(s:char."\<Plug>(multiple-cursors-apply)")
  endif

  " Even when s:char produces invalid input, this method is always called. The
  " 't' here is important
  call feedkeys("\<Plug>(multiple-cursors-detect)", 't')
endfunction

" This method is always called during fanout, even when a bad user input causes
" s:apply_user_input_next to not be called. We detect that and force the method
" to be called to continue the fanout process
function! s:detect_bad_input()
  if !s:valid_input
    " To invoke the appropriate `<Plug>(multiple-cursors-apply)` mapping, we
    " need to revert back to the mode the user was in when the input was entered
    call s:revert_mode(s:to_mode, s:from_mode)
    " We ignore the bad input and force invoke s:apply_user_input_next
    call feedkeys("\<Plug>(multiple-cursors-apply)")
    let s:bad_input += 1
  endif
endfunction

" Complete transition into Insert mode when `I` or `A` is input in Visual mode
function! s:handle_visual_IA_to_insert()
  if !empty(s:saved_char) && s:char =~# 'v\|V' && s:to_mode ==# 'n'
    if s:saved_char ==# 'I'
      call s:cm.reposition_all_within_region(1)
    endif
    call feedkeys(tolower(s:saved_char))
    let s:saved_char = ''
  endif
endfunction

" Begin transition into Insert mode when `I` or `A` is input in Visual mode
function! s:handle_visual_IA_to_normal()
  if s:char =~# 'I\|A' && s:from_mode =~# 'v\|V'
    let s:saved_char = s:char
    let s:char = s:from_mode " spoof a 'v' or 'V' input to transiton from Visual into Normal mode
  endif
endfunction

" Apply the user input at the next cursor location
function! s:apply_user_input_next(mode)
  let s:valid_input = 1

  " Save the current mode, only if we haven't already
  if empty(s:to_mode)
    let s:to_mode = a:mode
    if s:to_mode ==# 'v'
      if visualmode() ==# 'V'
        let s:to_mode = 'V'
      endif
    endif
  endif

  " Update the current cursor's information
  let changed = s:cm.update_current()

  " Advance the cursor index
  call s:cm.next()

  " We're done if we're made the full round
  if s:cm.loop_done()
    if s:to_mode ==# 'v' || s:to_mode ==# 'V'
      " This is necessary to set the "'<" and "'>" markers properly
      call s:update_visual_markers(s:cm.get_current().visual)
    endif
    call feedkeys("\<Plug>(multiple-cursors-wait)")
    call s:handle_visual_IA_to_insert()
  else
    " Continue to next
    call feedkeys("\<Plug>(multiple-cursors-input)")
  endif
endfunction

" If pos is equal to the left side of the visual selection, the region start
" from end to start
function! s:get_visual_region(pos)
  let left = s:pos("'<")
  let right = s:pos("'>")
  if a:pos == left
    let region = [right, left]
  else
    let region = [left, right]
  endif
  return region
endfunction

function! s:strpart(s, i, l)
  if a:l == 0
    return ''
  endif
  let [s, l] = ['', 0]
  for c in split(a:s[a:i :], '\zs')
    let s .= c
    let l += len(c)
    if l >= a:l
      break
    endif
  endfor
  return s
endfunction

" Return the content of the buffer between the input region. This is used to
" find the next match in the buffer
" Mode change: Normal -> Normal
" Cursor change: None
function! s:get_text(region)
  let lines = getline(a:region[0][0], a:region[1][0])
  let lines[-1] = s:strpart(lines[-1], 0, a:region[1][1])
  let lines[0] = lines[0][a:region[0][1] - 1:]
  return join(lines, "\n")
endfunction

" Wrapper around getchar() that returns the string representation of the user
" input
function! s:get_char(...)
  let c = (a:0 == 0) ? getchar() : getchar(a:1)
  " If the character is a number, then it's not a special key
  if type(c) == 0
    let c = nr2char(c)
  endif
  return c
endfunction

" Quits multicursor mode and clears all cursors. Return true if exited
" successfully.
function! s:exit()
  if s:char !=# g:multi_cursor_quit_key
    return 0
  endif
  let exit = 0
  if s:from_mode ==# 'n'
    let exit = 1
  elseif (s:from_mode ==# 'v' || s:from_mode ==# 'V') &&
        \ g:multi_cursor_exit_from_visual_mode
    let exit = 1
  elseif s:from_mode ==# 'i' && g:multi_cursor_exit_from_insert_mode
    stopinsert
    let exit = 1
  endif
  if exit
    call s:cm.reset(1, 1, 1)
    return 1
  endif
  return 0
endfunction

" These keys don't get faned out to all cursor locations. Instead, they're used
" to add new / remove existing cursors
" Precondition: The function is only called when the keys and mode respect the
" setting in s:special_keys
function! s:handle_special_key(key, mode)
  " Use feedkeys here instead of calling the function directly to prevent
  " increasing the call stack, since feedkeys execute after the current call
  " finishes
  if a:key == g:multi_cursor_next_key
    if s:use_word_boundary == 1
      call s:feedkeys("\<Plug>(multiple-cursors-new-word)")
    else
      call s:feedkeys("\<Plug>(multiple-cursors-new)")
    endif
  elseif a:key == g:multi_cursor_prev_key
    call s:feedkeys("\<Plug>(multiple-cursors-prev)")
  elseif a:key == g:multi_cursor_skip_key
    call s:feedkeys("\<Plug>(multiple-cursors-skip)")
  endif
endfunction

" The last line where the normal Vim cursor is always seems to highlighting
" issues if the cursor is on the last column. Vim's cursor seems to override the
" highlight of the virtual cursor. This won't happen if the virtual cursor isn't
" the last character on the line. This is a hack to add an empty space on the
" Vim cursor line right before we do the redraw, we'll revert the change
" immedidately after the redraw so the change should not be intrusive to the
" user's buffer content
function! s:apply_highlight_fix()
  " Only do this if we're on the last character of the line
  if col('.') == col('$')
    let s:saved_line = getline('.')
    if s:from_mode ==# 'i'
      silent! undojoin | call setline('.', s:saved_line.' ')
    else
      call setline('.', s:saved_line.' ')
    endif
  endif
endfunction

" Revert the fix if it was applied earlier
function! s:revert_highlight_fix()
  if type(s:saved_line) == 1
    if s:from_mode ==# 'i'
      silent! undojoin | call setline('.', s:saved_line)
    else
      call setline('.', s:saved_line)
    endif
  endif
  let s:saved_line = 0
endfunction

let s:retry_keys = ""
function! s:display_error()
  if s:bad_input == s:cm.size()
        \ && ((s:from_mode ==# 'n'    && has_key(g:multi_cursor_normal_maps, s:char[0]))
        \ ||  (s:from_mode =~# 'v\|V' && has_key(g:multi_cursor_visual_maps, s:char[0])))
    " we couldn't replay it anywhere but we're told it's the beginning of a
    " multi-character map like the `d` in `dw`
    let s:retry_keys = s:char
  else
    let s:retry_keys = ""
    if s:bad_input > 0
      echohl ErrorMsg |
            \ echo "Key '".s:char."' cannot be replayed at ".
            \ s:bad_input." cursor location".(s:bad_input == 1 ? '' : 's') |
            \ echohl Normal
    endif
  endif
  let s:bad_input = 0
endfunction

let s:latency_debug_file = ''
function! s:start_latency_measure()
  if g:multi_cursor_debug_latency
    let s:start_time = reltime()
  endif
endfunction

function! s:skip_latency_measure()
  if g:multi_cursor_debug_latency
    let s:skip_latency_measure = 1
  endif
endfunction

function! s:end_latency_measure()
  if g:multi_cursor_debug_latency && !empty(s:char)
    if empty(s:latency_debug_file)
      let s:latency_debug_file = tempname()
      exec 'redir >> '.s:latency_debug_file
        silent! echom "Starting latency debug at ".reltimestr(reltime())
      redir END
    endif

    if !s:skip_latency_measure
      exec 'redir >> '.s:latency_debug_file
        silent! echom "Processing '".s:char."' took ".string(str2float(reltimestr(reltime(s:start_time)))*1000).' ms in '.s:cm.size().' cursors. mode = '.s:from_mode
      redir END
    endif
  endif
  let s:skip_latency_measure = 0
endfunction

function! s:get_time_in_ms()
  return str2nr(substitute(reltimestr(reltime()), '\.\(...\).*', '\1', ''))
endfunction

function! s:last_char()
  return s:char[len(s:char)-1]
endfunction

function! s:wait_for_user_input(mode)
  call s:display_error()

  let s:from_mode = a:mode
  if empty(a:mode)
    let s:from_mode = s:to_mode
  endif
  let s:to_mode = ''

  " Right before redraw, apply the highlighting bug fix
  call s:apply_highlight_fix()

  redraw

  " Immediately revert the change to leave the user's buffer unchanged
  call s:revert_highlight_fix()

  call s:end_latency_measure()

  let s:char = s:retry_keys . s:saved_keys
  if len(s:saved_keys) == 0
    let s:char .= s:get_char()
    call s:handle_visual_IA_to_normal()
  else
    let s:saved_keys = ""
  endif

  " ambiguous mappings are not supported; e.g.:
  "   imap jj JJ
  "   imap jjj JJJ
  " will always trigger the 'jj' mapping
  if s:from_mode ==# 'i' && mapcheck(s:char, "i") != "" && g:multi_cursor_support_imap
    let map_dict = {}
    let s_time = s:get_time_in_ms()
    while 1
      let map_dict = maparg(s:char, "i", 0, 1)
      " break if chars exactly match mapping
      if map_dict != {}
        if get(map_dict, 'expr', 0)
          " handle case where {rhs} is a function
          exec 'let char_mapping = ' . map_dict['rhs']
        else
          let char_mapping = maparg(s:char, "i")
        endif
        " handle case where mapping is <esc>
        exec 'let s:char = "'.substitute(char_mapping, '<', '\\<', 'g').'"'
        break
      endif
      " break if chars don't match beginning of mapping anymore
      if mapcheck(s:char, "i") == ""
        break
      endif
      if s:get_time_in_ms() > (s_time + &timeoutlen)
        break
      endif
      let new_char = s:get_char(0)
      let s:char .= new_char
      if new_char == ''
        sleep 50m
      endif
    endwhile
  elseif s:from_mode !=# 'i' && s:char[0] ==# ":"
    call feedkeys(s:char)
    call s:cm.reset(1, 1, 1)
    return
  elseif s:from_mode ==# 'n' || s:from_mode =~# 'v\|V'
    while match(s:last_char(), "\\d") == 0
      if match(s:char, '\(^\|\a\)0') == 0
        " fixes an edge case concerning the `0` key.
        " The 0 key behaves differently from [1-9].
        " It's consumed immediately when it is the
        " first key typed while we're waiting for input.
        " References: issue #152, pull #241
        break
      endif
      let s:char .= s:get_char()
    endwhile
  endif

  call s:start_latency_measure()

  " Clears any echoes we might've added
  normal! :

  " add chars to s:char if it start like a special/quit key
  let is_special_key = 0
  let sk_list = get(s:special_keys, s:from_mode, [])
  let is_special_key = (index(sk_list, s:char) != -1)
  let is_quit_key = 0
  let s_time = s:get_time_in_ms()
  while 1
    let start_special_key = (index(map(sk_list[:], 'v:val[0:len(s:char)-1] == s:char'), 1) > -1)
    let start_quit_key = (g:multi_cursor_quit_key[0:len(s:char)-1] == s:char)
    if start_special_key == 0 && start_quit_key == 0
      break
    else
      let is_special_key = (index(sk_list, s:char) != -1)
      let is_quit_key = (g:multi_cursor_quit_key == s:char)
      if is_special_key == 1 || is_quit_key == 1
        break
      else
        if s:get_time_in_ms() > (s_time + &timeoutlen)
          break
        endif
        let new_char = s:get_char(0)
        let s:char .= new_char
        if new_char == ''
          sleep 50m
        endif
      endif
    end
  endwhile

  if s:exit()
    return
  endif

  " If the key is a special key and we're in the right mode, handle it
  if is_special_key == 1
    call s:handle_special_key(s:char, s:from_mode)
    call s:skip_latency_measure()
  else
    call s:cm.start_loop()
    call s:feedkeys("\<Plug>(multiple-cursors-input)")
  endif
endfunction


================================================
FILE: doc/multiple_cursors.txt
================================================
*vim-multiple-cursors.txt* True Sublime Text multiple selection in Vim

                    ____  _       __
   ____ ___  __  __/ / /_(_)___  / /__     _______  ________________  __________
  / __ `__ \/ / / / / __/ / __ \/ / _ \   / ___/ / / / ___/ ___/ __ \/ ___/ ___/
 / / / / / / /_/ / / /_/ / /_/ / /  __/  / /__/ /_/ / /  (__  ) /_/ / /  (__  )
/_/ /_/ /_/\__,_/_/\__/_/ .___/_/\___/   \___/\__,_/_/  /____/\____/_/  /____/
                       /_/


                              Reference Manual~


==============================================================================

CONTENTS                                            *multiple-cursors-contents*
    1.Intro...................................|multiple-cursors-intro|
    2.Usage...................................|multiple-cursors-usage|
    3.Mappings................................|multiple-cursors-mappings|
    4.Global Options..........................|multiple-cursors-global-options|
    5.Interactions with other plugins.........|multiple-cursors-other-plugins|
    6.Highlight...............................|multiple-cursors-highlight|
    7.FAQ.....................................|multiple-cursors-faq|
    8.Contributing............................|multiple-cursors-contributing|
    9.License.................................|multiple-cursors-license|
   10.Credit..................................|multiple-cursors-credit|
   11.References..............................|multiple-cursors-references|

==============================================================================
1. Intro                                              *multiple-cursors-intro*

There [1] have [2] been [3] many [4] attempts [5] at bringing Sublime Text's
awesome multiple selection [6] feature into Vim, but none so far have been in
my opinion a faithful port that is simplistic to use, yet powerful and
intuitive enough for an existing Vim user. *vim-multiple-cursors* is yet
another attempt at that.

==============================================================================
2. Usage                                              *multiple-cursors-usage*

normal mode / visual mode~

    - start:      `<C-n>` start multicursor and add a virtual cursor + visual selection on the match
        + next:   `<C-n>` add a new virtual cursor + visual selection on the next match
        + skip:   `<C-x>` skip the next match
        + prev:   `<C-p>` remove current virtual cursor + visual selection and go back on previous match
    - select all: `<A-n>` start muticursor and directly select all matches

You can now change the virtual cursors + visual selection with |visual-mode| commands.
For instance: `c`, `s`, `I`, `A` work without any issues.
You could also go to |normal-mode| by pressing `v` and use normal commands there.

At any time, you can press `<Esc>` to exit back to regular Vim.

NOTE: start with `g<C-n>` to match without boundaries (behaves like `g*` instead of `*`, see |gstar|)

visual mode when multiple lines are selected~

    - start: `<C-n>` add virtual cursors on each line

You can now change the virtual cursors with |normal-mode| commands.
For instance: `ciw`.

command~

The command `MultipleCursorsFind` accepts a range and a pattern (regexp), it
creates a visual cursor at the end of each match.
If no range is passed in, then it defaults to the entire buffer.

==============================================================================
3. Mappings                                        *multiple-cursors-mappings*

If you don't like the plugin taking over your favorite key bindings, you can
turn off the default with >

    let g:multi_cursor_use_default_mapping=0

    " Default mapping
    let g:multi_cursor_start_word_key      = '<C-n>'
    let g:multi_cursor_select_all_word_key = '<A-n>'
    let g:multi_cursor_start_key           = 'g<C-n>'
    let g:multi_cursor_select_all_key      = 'g<A-n>'
    let g:multi_cursor_next_key            = '<C-n>'
    let g:multi_cursor_prev_key            = '<C-p>'
    let g:multi_cursor_skip_key            = '<C-x>'
    let g:multi_cursor_quit_key            = '<Esc>'
<

NOTE: Please make sure to always map something to |g:multi_cursor_quit_key|,
otherwise you'll have a tough time quitting from multicursor mode.

==============================================================================
4. Global Options                            *multiple-cursors-global-options*

Currently there are four additional global settings one can tweak:

*g:multi_cursor_support_imap* (Default: 1)

If set to 0, insert mappings won't be supported in |insert-mode| anymore.

*g:multi_cursor_exit_from_visual_mode* (Default: 0)

If set to 0, then pressing |g:multi_cursor_quit_key| in |visual-mode| will quit
and delete all existing cursors, skipping normal mode with multiple cursors.

*g:multi_cursor_exit_from_insert_mode* (Default: 0)

If set to 1, then pressing |g:multi_cursor_quit_key| in |insert-mode| will quit
and delete all existing cursors, skipping normal mode with multiple cursors.

*g:multi_cursor_normal_maps* (Default: see below)

`{'@': 1, 'F': 1, 'T': 1, '[': 1, '\': 1, ']': 1, '!': 1, '"': 1, 'c': 1, 'd': 1, 'f': 1, 'g': 1, 'm': 1, 'q': 1, 'r': 1, 't': 1, 'y': 1, 'z': 1, '<': 1, '=': 1, '>': 1}`

Any key in this map (values are ignored) will cause multi-cursor _Normal_ mode
to pause for map completion just like normal vim. Otherwise keys mapped in
normal mode will "fail to replay" when multiple cursors are active. For
example: `{'d':1}` makes normal-mode command `dw` work in multi-cursor mode.

The default list contents should work for anybody, unless they have remapped a
key from an operator-pending command to a non-operator-pending command or
vice versa.

These keys must be manually listed because vim doesn't provide a way to
automatically see which keys _start_ mappings, and trying to run motion commands
such as `j` as if they were operator-pending commands can break things.

*g:multi_cursor_visual_maps* (Default: )

`{'T': 1, 'a': 1, 't': 1, 'F': 1, 'f': 1, 'i': 1}`

Same principle as |g:multi_cursor_normal_maps|

==============================================================================
5. Interactions with other plugins                    *multiple-cursors-other-plugins*

Other plugins may be incompatible in insert mode. That is why we provide
hooks to disable those plug-ins when vim-multiple-cursors is active:

For example, if you are using `Neocomplete`, add this to your vimrc to prevent
conflict:
 >
    function! Multiple_cursors_before()
      if exists(':NeoCompleteLock')==2
        exe 'NeoCompleteLock'
      endif
    endfunction

    function! Multiple_cursors_after()
      if exists(':NeoCompleteUnlock')==2
        exe 'NeoCompleteUnlock'
      endif
    endfunction

Plugins themselves can register |User| |autocommand| on `MultipleCursorsPre` and
`MultipleCursorsPost` for automatic integration.

==============================================================================
6. Highlight                                          *multiple-cursors-highlight*
>
The plugin uses the highlight group `multiple_cursors_cursor` and
`multiple_cursors_visual` to highlight the virtual cursors and their visual
selections respectively. You can customize them by putting something similar
like the following in your vimrc: >
    " Default highlighting (see help :highlight and help :highlight-link)
    highlight multiple_cursors_cursor term=reverse cterm=reverse gui=reverse
    highlight link multiple_cursors_visual Visual

==============================================================================
7. FAQ                                              *multiple-cursors-faq*

Q:  Pressing <i> after selecting words with <C-n> makes the plugin hang, why?
A:  When selecting words with <C-n>, the plugin behaves like in `visual` mode.
    Once you pressed <i>, you can still press <I> to insert text.

Q:  <A-n> doesn't seem to work in VIM but works in gVIM, why?
A:  This is a well known terminal/Vim [9], different terminal have different
    ways to send `Alt+key`.  Try adding this in your `.vimrc` and make sure
    to replace the string: >
    if !has('gui_running')
      map "in Insert mode, type Ctrl+v Alt+n here" <A-n>
    endif
Or remap the following: >
    g:multi_cursor_start_key
    g:multi_cursor_select_all_key

Q:  <C-n> doesn't seem to work in gVIM?
A:  Try setting `set selection=inclusive` in your `~/.gvimrc`

Q:  deoplete insert giberrish, how to fix this?
A:  use the `Multiple_cursors` functions, add this in your vimrc: >
    func! Multiple_cursors_before()
      if deoplete#is_enabled()
        call deoplete#disable()
        let g:deoplete_is_enable_before_multi_cursors = 1
      else
        let g:deoplete_is_enable_before_multi_cursors = 0
      endif
    endfunc
    func! Multiple_cursors_after()
      if g:deoplete_is_enable_before_multi_cursors
        call deoplete#enable()
      endif
    endfunc

Q:  is it also working on Mac?
A:  On Mac OS, MacVim[10] is known to work.

Q:  How can I select `n` keywords with several keystrokes? `200<C-n>` does not work.
A:  You can use :MultipleCursorsFind keyword. I have this binding in my vimrc: >
    nnoremap <silent> <M-j> :MultipleCursorsFind <C-R>/<CR>
    vnoremap <silent> <M-j> :MultipleCursorsFind <C-R>/<CR>
This allows one to search for the keyword using `*` and turn search results into cursors with `Alt-j`.

==============================================================================
8. Contributing                                *multiple-cursors-contributing*

The project is hosted on Github. Patches, feature requests and suggestions are
always welcome!

Find the latest version of the plugin here:
    http://github.com/terryma/vim-multiple-cursors

==============================================================================
9. License                                          *multiple-cursors-license*

The project is licensed under the MIT license [7]. Copyright 2013 Terry Ma

==============================================================================
10. Credit                                           *multiple-cursors-credit*

The plugin is obviously inspired by Sublime Text's awesome multiple selection
[6] feature. Some inspiration was also taken from Emac's multiple cursors [8]
implementation.

==============================================================================
10. References                                    *multiple-cursors-references*

[1] https://github.com/paradigm/vim-multicursor
[2] https://github.com/felixr/vim-multiedit
[3] https://github.com/hlissner/vim-multiedit
[4] https://github.com/adinapoli/vim-markmultiple
[5] https://github.com/AndrewRadev/multichange.vim
[6] http://www.sublimetext.com/docs/2/multiple_selection_with_the_keyboard.html
[7] http://opensource.org/licenses/MIT
[8] https://github.com/magnars/multiple-cursors.el
[9] http://vim.wikia.com/wiki/Get_Alt_key_to_work_in_terminal
[10] https://code.google.com/p/macvim

 vim:tw=78:sw=4:ft=help:norl:


================================================
FILE: plugin/multiple_cursors.vim
================================================
"===============================================================================
" File: multiple_cursors.vim
" Author: Terry Ma
" Description: Emulate Sublime Text's multi selection feature
" Potential Features:
" - Create a blinking cursor effect? Good place to do it would be instead of
"   waiting for user input, cycle through the highlight
" - Integrate with the status line? Maybe show a special multicursor mode?
" - Support mouse? Ctrl/Cmd click to set cursor?
"===============================================================================
let s:save_cpo = &cpo
set cpo&vim

function! s:init_settings(settings)
  for [key, value] in items(a:settings)
    let sub = ''
    if type(value) == 0
      let sub = '%d'
    elseif type(value) == 1
      let sub = '"%s"'
    endif
    let fmt = printf("let g:multi_cursor_%%s=get(g:, 'multi_cursor_%%s', %s)",
          \ sub)
    exec printf(fmt, key, key, value)
  endfor
endfunction

" Settings
let s:settings = {
      \ 'exit_from_visual_mode': 0,
      \ 'exit_from_insert_mode': 0,
      \ 'use_default_mapping': 1,
      \ 'debug_latency': 0,
      \ 'support_imap': 1,
      \ }

let s:settings_if_default = {
      \ 'quit_key':            '<Esc>',
      \ 'start_key':           'g<C-n>',
      \ 'start_word_key':      '<C-n>',
      \ 'next_key':            '<C-n>',
      \ 'prev_key':            '<C-p>',
      \ 'skip_key':            '<C-x>',
      \ 'select_all_key':      'g<A-n>',
      \ 'select_all_word_key': '<A-n>',
      \ }

let s:default_normal_maps = {'!':1, '@':1, '=':1, 'q':1, 'r':1, 't':1, 'T':1, 'y':1, '[':1, ']':1, '\':1, 'd':1, 'f':1, 'F':1, 'g':1, '"':1, 'z':1, 'c':1, 'm':1, '<':1, '>':1}
let s:default_visual_maps = {'i':1, 'a':1, 'f':1, 'F':1, 't':1, 'T':1}

let g:multi_cursor_normal_maps =
      \ get(g:, 'multi_cursor_normal_maps', s:default_normal_maps)
let g:multi_cursor_visual_maps =
      \ get(g:, 'multi_cursor_visual_maps', s:default_visual_maps)

call s:init_settings(s:settings)

if g:multi_cursor_use_default_mapping
  call s:init_settings(s:settings_if_default)
endif

if !exists('g:multi_cursor_start_word_key')
  if exists('g:multi_cursor_next_key')
    let g:multi_cursor_start_word_key = g:multi_cursor_next_key
  endif
endif

" External mappings
if exists('g:multi_cursor_start_key')
  exec 'nnoremap <silent> '.g:multi_cursor_start_key.
        \' :call multiple_cursors#new("n", 0)<CR>'
  exec 'xnoremap <silent> '.g:multi_cursor_start_key.
        \' :<C-u>call multiple_cursors#new("v", 0)<CR>'
endif

if exists('g:multi_cursor_start_word_key')
  exec 'nnoremap <silent> '.g:multi_cursor_start_word_key.
        \' :call multiple_cursors#new("n", 1)<CR>'
  " In Visual mode word boundary is not used
  exec 'xnoremap <silent> '.g:multi_cursor_start_word_key.
        \' :<C-u>call multiple_cursors#new("v", 0)<CR>'
endif

if exists('g:multi_cursor_select_all_key')
  exec 'nnoremap <silent> '.g:multi_cursor_select_all_key.
        \' :call multiple_cursors#select_all("n", 0)<CR>'
  exec 'xnoremap <silent> '.g:multi_cursor_select_all_key.
        \' :<C-u>call multiple_cursors#select_all("v", 0)<CR>'
endif

if exists('g:multi_cursor_select_all_word_key')
  exec 'nnoremap <silent> '.g:multi_cursor_select_all_word_key.
        \' :call multiple_cursors#select_all("n", 1)<CR>'
  " In Visual mode word boundary is not used
  exec 'xnoremap <silent> '.g:multi_cursor_select_all_word_key.
        \' :<C-u>call multiple_cursors#select_all("v", 0)<CR>'
endif

" Commands
command! -nargs=1 -range=% MultipleCursorsFind
      \ call multiple_cursors#find(<line1>, <line2>, <q-args>)

let &cpo = s:save_cpo
unlet s:save_cpo


================================================
FILE: spec/benchmark_spec.rb
================================================
require 'vimrunner'
require 'vimrunner/rspec'

Vimrunner::RSpec.configure do |config|

  # Use a single Vim instance for the test suite. Set to false to use an
  # instance per test (slower, but can be easier to manage).
  config.reuse_server = false

  # Decide how to start a Vim instance. In this block, an instance should be
  # spawned and set up with anything project-specific.
  config.start_vim do
    # vim = Vimrunner.start
    # vim = Vimrunner::Server.new("/usr/local/bin/vim").start

    # Or, start a GUI instance:
    vim = Vimrunner.start_gvim

    # Setup your plugin in the Vim instance
    plugin_path = File.expand_path('../..', __FILE__)
    vim.add_plugin(plugin_path, 'plugin/multiple_cursors.vim')

    # The returned value is the Client available in the tests.
    vim
  end
end

def set_file_content(string)
  string = normalize_string_indent(string)
  File.open(filename, 'w'){ |f| f.write(string) }
  vim.edit filename
end

def get_file_content()
  vim.write
  IO.read(filename).strip
end

def before(string)
  set_file_content(string)
end

def after(string)
  get_file_content().should eq normalize_string_indent(string)
  type ":q<CR>"
end

def type(string)
  string.scan(/<.*?>|./).each do |key|
    if /<.*>/.match(key)
      vim.feedkeys "\\#{key}"
    else
      vim.feedkeys key
    end
  end
  sleep 0.2
end

describe "Multiple Cursors" do
  let(:filename) { 'test.txt' }
  let(:options) { [] }

  specify "#benchmark" do
    before <<-EOF
      hello
      hello
      hello
      hello
      hello
      hello
      hello
      hello
      hello
      hello
      hello
      hello
      hello
      hello
      hello
      hello
      hello
      hello
      hello
      hello
      hello
      hello
      hello
      hello
      hello
      hello
      hello
      hello
      hello
      hello
    EOF

    # type ':profile start /tmp/test.result<CR>'
    # type ':profile! file *multiple_cursors.vim<CR>'
    type ':let g:multi_cursor_debug_latency=1<CR>'

    type 'VG<C-n>Vchellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello<Esc>'

    type ':echo multiple_cursors#get_latency_debug_file()<CR>'
    sleep 3
    latency_file = vim.command 'echo multiple_cursors#get_latency_debug_file()'
    puts 'latency file = ' + latency_file

    after <<-EOF
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
      hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
    EOF
  end

end


================================================
FILE: spec/multiple_cursors_spec.rb
================================================
# -*- encoding: utf-8 -*-
require 'spec_helper'

def set_file_content(string)
  string = normalize_string_indent(string)
  File.open(filename, 'w'){ |f| f.write(string) }
  vim.edit filename
end

def get_file_content()
  vim.write
  IO.read(filename).strip
end

def before(string)
  options.each { |x| vim.command(x) }
  set_file_content(string)
end

def after(string)
  expect(get_file_content()).to eq normalize_string_indent(string)
end

def type(string)
  string.scan(/<.*?>|./).each do |key|
    if /<.*>/.match(key)
      vim.feedkeys "\\#{key}"
    else
      vim.feedkeys key
    end
  end
end

describe "Multiple Cursors op pending & exit from insert|visual mode" do
  let(:filename) { 'test.txt' }
  let(:options) { ['let g:multi_cursor_exit_from_insert_mode = 0',
                   'let g:multi_cursor_exit_from_visual_mode = 0'] }
  # the default value of g:multi_cursor_normal_maps already works
  # for testing operator-pending

  specify "#paste from unnamed register to 3 cursors" do
    before <<-EOF
      yankme
      a b c
      a b c
      a b c
    EOF

    type 'yiwj<C-n><C-n><C-n>vwwp<Esc>'

    after <<-EOF
      yankme
      a b cyankme
      a b cyankme
      a b cyankme
    EOF
  end

  specify "#paste buffer normal caw then p" do
    before <<-EOF
      hello jan world
      hello feb world
      hello mar world
    EOF

    type '<C-n><C-n><C-n>vwcaw<Esc>bP<Esc>'

    after <<-EOF
      jan hello world
      feb hello world
      mar hello world
    EOF
  end

  specify "#paste buffer normal C then ABC then p" do
    before <<-EOF
      hello jan world
      hello feb world
      hello mar world
    EOF

    type '<C-n><C-n><C-n>vwCABC <Esc>p<Esc>'

    after <<-EOF
      hello ABC jan world
      hello ABC feb world
      hello ABC mar world
    EOF
  end

  specify "#paste buffer normal daw then P" do
    before <<-EOF
      hello jan world
      hello feb world
      hello mar world
    EOF

    type '<C-n><C-n><C-n>vwdawbP<Esc>'

    after <<-EOF
      jan hello world
      feb hello world
      mar hello world
    EOF
  end

  specify "#paste buffer normal D then P" do
    before <<-EOF
      hello jan world
      hello feb world
      hello mar world
    EOF

    type '<C-n><C-n><C-n>vwwhDbhP<Esc>'

    after <<-EOF
      hello world jan
      hello world feb
      hello world mar
    EOF
  end

  specify "#paste buffer normal s then p" do
    before <<-EOF
      hello jan world
      hello feb world
      hello mar world
    EOF

    type '<C-n><C-n><C-n>vws1<Esc>p<Esc>'

    after <<-EOF
      hello 1jan world
      hello 1feb world
      hello 1mar world
    EOF
  end

  specify "#normal mode '0': goes to 1st char of line" do
    before <<-EOF
      hello jan world
      hello feb world
      hello mar world
    EOF

    type '<C-n><C-n><C-n>vw0dw<Esc><Esc>'

    after <<-EOF
      jan world
      feb world
      mar world
    EOF
  end

  specify "#normal mode 'd0': deletes backward to 1st char of line" do
    before <<-EOF
      hello jan world
      hello feb world
      hello mar world
    EOF

    type '<C-n><C-n><C-n>vwd0<Esc><Esc>'

    after <<-EOF
      jan world
      feb world
      mar world
    EOF
  end

end

describe "Multiple Cursors when using insert mappings" do
  let(:filename) { 'test.txt' }
  let(:options) { ['set timeoutlen=10000',
                   'imap jj <esc>',
                   'imap jojo dude',
                   'imap jk <esc>:%s/bla/hey/g<cr>',
                   'let g:multi_cursor_exit_from_insert_mode = 1',
                   'let g:multi_cursor_exit_from_visual_mode = 1'] }
  specify "#mapping doing <Esc>" do
    before <<-EOF
      hello world!
      hello world!
      bla bla bla
      bla bla bla
    EOF

    type 'w<C-n><C-n>cjjidude<Esc>'

    after <<-EOF
      hello dude!
      hello !
      bla bla bla
      bla bla bla
    EOF
  end

  specify "#mapping doing <Esc> and running a command" do
    before <<-EOF
      hello world!
      hello world!
      bla bla bla
      bla bla bla
    EOF

    type 'w<C-n><C-n>ctherejk'

    after <<-EOF
      hello there!
      hello there!
      hey hey hey
      hey hey hey
    EOF
  end

  specify "#mapping using more than 2 characters" do
    before <<-EOF
      hello
      hello
      bla bla bla
      bla bla bla
    EOF

    type '<C-n><C-n>A jojo<Esc>'

    after <<-EOF
      hello dude
      hello dude
      bla bla bla
      bla bla bla
    EOF
  end

  specify "#unused mapping" do
    before <<-EOF
      hello world!
      hello world!
      bla bla bla
      bla bla bla
    EOF

    type 'w<C-n><C-n>chey joseph blah blah blah<Esc>'

    after <<-EOF
      hello hey joseph blah blah blah!
      hello hey joseph blah blah blah!
      bla bla bla
      bla bla bla
    EOF
  end

end

describe "Multiple Cursors when normal_maps is empty" do
  let(:filename) { 'test.txt' }
  let(:options) { ['let g:multi_cursor_normal_maps = {}'] }

  # Operator-pending commands are handled correctly thanks to their inclusion
  # in `g:multi_cursor_normal_maps`.
  #
  # When an operator-pending command like 'd' is missing from that setting's
  # value, then it should result in a no-op, but we should still remain in
  # multicursor mode.
  specify "#normal mode 'd'" do
    before <<-EOF
      hello
      hello
    EOF

    type '<C-n><C-n>vdx<Esc>'

    after <<-EOF
      hell
      hell
    EOF
  end

end

describe "Multiple Cursors when visual_maps is empty" do
  let(:filename) { 'test.txt' }
  let(:options) { ['let g:multi_cursor_visual_maps = {}'] }

  # Operator-pending commands are handled correctly thanks to their inclusion
  # in `g:multi_cursor_visual_maps`.
  #
  # When an operator-pending command like 'f' is missing from that setting's
  # value, then it should result in a no-op, but we should still remain in
  # multicursor mode.
  specify "#visual mode 'i'" do
    before <<-EOF
      hello world x
      hello world x
    EOF

    type 'fw<C-n><C-n>fx<Esc>'

    after <<-EOF
      hello  x
      hello  x
    EOF
  end

end

describe "Multiple Cursors when changing the line count" do
  let(:filename) { 'test.txt' }
  let(:options) { ['set backspace=indent,eol,start'] }

  specify "#backspace on first char of the line, then carriage return" do
    before <<-EOF
      madec

      antoine
      andre
      joseph
    EOF

    type 'Gvip<C-n>i<BS><cr>'

    after <<-EOF
      madec

      antoine
      andre
      joseph
    EOF
  end

  specify "#del at EOL, then carriage return" do
    before <<-EOF
      madec
      antoine
      joseph

      andre
    EOF

    type 'vip<C-n>A<DEL><cr>'

    after <<-EOF
      madec
      antoine
      joseph

      andre
    EOF
  end

end

describe "Multiple Cursors misc" do
  let(:filename) { 'test.txt' }
  let(:options) { ['set autoindent'] }

  specify "#paste buffer normal x then p" do
    before <<-EOF
      jan
      feb
      mar
    EOF

    type '<C-v>jj<C-n>xp<Esc>'

    after <<-EOF
      ajn
      efb
      amr
    EOF
  end

  specify "#paste buffer visual y then p" do
    before <<-EOF
      hello jan world
      hello feb world
      hello mar world
    EOF

    type '<C-n><C-n><C-n>vwvelywhp<Esc>'

    after <<-EOF
      hello jan jan world
      hello feb feb world
      hello mar mar world
    EOF
  end

  specify "#paste buffer initial visual y then P" do
    before <<-EOF
      hello jan world
      hello feb world
      hello mar world
    EOF

    type 'wywb<C-n><C-n><C-n>p<Esc>'

    after <<-EOF
      jan  jan world
      jan  feb world
      jan  mar world
    EOF
  end

  specify "#paste buffer visual y then P" do
    before <<-EOF
      hello jan world
      hello feb world
      hello mar world
    EOF

    type '<C-n><C-n><C-n>vwvely^P<Esc>'

    after <<-EOF
      jan hello jan world
      feb hello feb world
      mar hello mar world
    EOF
  end

  specify "#paste buffer visual Y then P" do
    before <<-EOF
      hello jan world
      hello feb world
      hello mar world
    EOF

    type '<C-n><C-n><C-n>vwvY^P<Esc>'

    after <<-EOF
      hello jan world
      hello jan world
      hello feb world
      hello feb world
      hello mar world
      hello mar world
    EOF
  end

  specify "#multiline replacement" do
    before <<-EOF
      hello
      hello
      hello
    EOF

    type '<C-n><C-n><C-n>cworld<Esc>'

    after <<-EOF
      world
      world
      world
    EOF
  end

  specify "#single line replacement" do
    before <<-EOF
      hello hello hello
    EOF

    type '<C-n><C-n><C-n>cworld<Esc>'

    after <<-EOF
      world world world
    EOF
  end

  specify "#mixed line replacement" do
    before <<-EOF
      hello hello
      hello
    EOF

    type '<C-n><C-n><C-n>cworld<Esc>'

    after <<-EOF
      world world
      world
    EOF
  end

  specify "#new line in insert mode" do
    before <<-EOF
      hello
      hello
    EOF

    type '<C-n><C-n>chello<CR>world<Esc>'

    after <<-EOF
      hello
      world
      hello
      world
    EOF
  end

  specify "#new line in insert mode middle of line" do
    before <<-EOF
      hello world
      hello world
    EOF

    type '<C-n><C-n>vlxi<cr><Esc>'

    after <<-EOF
      hello
      world
      hello
      world
    EOF
  end

  specify "#multiple new lines on one line in insert mode" do
    before <<-EOF
      'a','b','c','d','e'
    EOF

    type 'f,v<C-n><C-n><C-n>c<CR><Esc>'

    after <<-EOF
      'a'
      'b'
      'c'
      'd'
      'e'
    EOF
  end

  specify "#multiple new lines on one line in insert mode with indents" do
    before <<-EOF
      'a','b','c','d','e'
    EOF

    type '4i<Space><Esc>f,v<C-n><C-n><C-n>c<CR><Esc>:%s/^/^<CR>'

    after <<-EOF
      ^    'a'
      ^    'b'
      ^    'c'
      ^    'd'
      ^    'e'
    EOF
  end

  specify "#normal mode 'o'" do
    before <<-EOF
      hello
      hello
    EOF

    type '<C-n><C-n>voworld<Esc>'

    after <<-EOF
      hello
      world
      hello
      world
    EOF
  end

  specify "#normal mode 'O'" do
    before <<-EOF
      hello
      hello
    EOF

    type '<C-n><C-n>vOworld<Esc>'

    after <<-EOF
      world
      hello
      world
      hello
    EOF
  end

  specify "#find command basic" do
    before <<-EOF
      hello
      hello
    EOF

    vim.normal ':MultipleCursorsFind hello<CR>'
    type 'cworld<Esc>'

    after <<-EOF
      world
      world
    EOF
  end

  specify "#find command start-of-line" do
    before <<-EOF
      hello
      world

      hello
      world
    EOF

    vim.normal ':MultipleCursorsFind ^<CR>'
    type 'Ibegin<Esc>'

    after <<-EOF
      beginhello
      beginworld
      begin
      beginhello
      beginworld
    EOF
  end

  specify "#find command end-of-line" do
    before <<-EOF
      hello
      world

      hello
      world
    EOF

    vim.normal ':MultipleCursorsFind $<CR>'
    type 'Iend<Esc>'

    after <<-EOF
      helloend
      worldend
      end
      helloend
      worldend
    EOF
  end

  specify "#visual line mode replacement" do
    before <<-EOF
      hello world
      hello world
    EOF

    type '<C-n><C-n>Vchi!<Esc>'

    after <<-EOF
      hi!
      hi!
    EOF
  end

  specify "#skip key" do
    before <<-EOF
      hello
      hello
      hello
    EOF

    type '<C-n><C-n><C-x>cworld<Esc>'

    after <<-EOF
      world
      hello
      world
    EOF
  end

  specify "#prev key" do
    before <<-EOF
      hello
      hello
      hello
    EOF

    type '<C-n><C-n><C-n><C-p>cworld<Esc>'

    after <<-EOF
      world
      world
      hello
    EOF
  end

  specify "#visual mode 'i'" do
    before <<-EOF
      hi (hello world jan) bye
      hi (hello world feb) bye
      hi (hello world mar) bye
    EOF

    type 'fw<C-n><C-n><C-n>ibcone<Esc>'

    after <<-EOF
      hi (one) bye
      hi (one) bye
      hi (one) bye
    EOF
  end

  specify "#visual mode 'a'" do
    before <<-EOF
      hi (hello world jan) bye
      hi (hello world feb) bye
      hi (hello world mar) bye
    EOF

    type 'fw<C-n><C-n><C-n>abcone<Esc>'

    after <<-EOF
      hi one bye
      hi one bye
      hi one bye
    EOF
  end

  specify "#visual mode 'f'" do
    before <<-EOF
      hi (hello world jan) bye
      hi (hello world feb) bye
      hi (hello world mar) bye
    EOF

    type 'fw<C-n><C-n><C-n>f)cone<Esc>'

    after <<-EOF
      hi (hello one bye
      hi (hello one bye
      hi (hello one bye
    EOF
  end

  specify "#visual mode 'F'" do
    before <<-EOF
      hi (hello world jan) bye
      hi (hello world feb) bye
      hi (hello world mar) bye
    EOF

    type 'fw<C-n><C-n><C-n>F(cbefore<Esc>'

    after <<-EOF
      hi beforeorld jan) bye
      hi beforeorld feb) bye
      hi beforeorld mar) bye
    EOF
  end

  specify "#visual mode 't'" do
    before <<-EOF
      hello.jan
      hello hi.feb
      hello hi bye.mar
    EOF

    type '<C-n><C-n><C-n>t.cone<Esc>'

    after <<-EOF
      one.jan
      one.feb
      one.mar
    EOF
  end

  specify "#visual mode 'T'" do
    before <<-EOF
      jan.world
      feb.hi world
      mar.bye hi world
    EOF

    type 'fw<C-n><C-n><C-n>T.cbefore<Esc>'

    after <<-EOF
      jan.beforeorld
      feb.beforeorld
      mar.beforeorld
    EOF
  end

  specify "#visual line mode 'f'" do
    before <<-EOF
      hello jan world
      hello feb world
      hello mar world
    EOF

    type '<C-n><C-n><C-n>VfwvAafter<Esc>'

    after <<-EOF
      hello jan wafterorld
      hello feb wafterorld
      hello mar wafterorld
    EOF
  end

  specify "#visual mode 'I'" do
    before <<-EOF
      hello world jan
      hello world feb
      hello world mar
    EOF

    type 'w<C-n><C-n><C-n>Ibefore<Esc>'

    after <<-EOF
      hello beforeworld jan
      hello beforeworld feb
      hello beforeworld mar
    EOF
  end

  specify "#visual mode 'A'" do
    before <<-EOF
      hello world jan
      hello world feb
      hello world mar
    EOF

    type 'w<C-n><C-n><C-n>Aafter<Esc>'

    after <<-EOF
      hello worldafter jan
      hello worldafter feb
      hello worldafter mar
    EOF
  end

  specify "#resize regions visual mode 'I'" do
    before <<-EOF
      hello world jan
      hello world feb
      hello world mar
    EOF

    type 'w<C-n><C-n><C-n>hhhIbefore<Esc>'

    after <<-EOF
      hello beforeworld jan
      hello beforeworld feb
      hello beforeworld mar
    EOF
  end

  specify "#resize regions visual mode 'A'" do
    before <<-EOF
      hello world jan
      hello world feb
      hello world mar
    EOF

    type 'w<C-n><C-n><C-n>hhhAbefore<Esc>'

    after <<-EOF
      hello wobeforerld jan
      hello wobeforerld feb
      hello wobeforerld mar
    EOF
  end

  specify "#no word boundries visual mode 'I'" do
    before <<-EOF
      hello hibye world
      hello hibye world
      hello hibye world
    EOF

    vim.normal ':MultipleCursorsFind bye<CR>'
    type 'Ibefore<Esc>'

    after <<-EOF
      hello hibeforebye world
      hello hibeforebye world
      hello hibeforebye world
    EOF
  end

  specify "#variable-length regions visual mode 'I'" do
    before <<-EOF
      hello hii world
      hello hiiii world
      hello hiiiiii world
    EOF

    vim.normal ':MultipleCursorsFind \<hi*\><CR>'
    type 'Ibefore<Esc>'

    after <<-EOF
      hello beforehii world
      hello beforehiiii world
      hello beforehiiiiii world
    EOF
  end

  specify "#normal mode 'I'" do
    before <<-EOF
      hello
      hello
    EOF

    type '<C-n><C-n>vIworld <Esc>'

    after <<-EOF
      world hello
      world hello
    EOF
  end

  specify "#normal mode 'A'" do
    before <<-EOF
      hello
      hello
    EOF

    type '<C-n><C-n>vA world<Esc>'

    after <<-EOF
      hello world
      hello world
    EOF
  end

  specify "#undo" do
    before <<-EOF
      hello
      hello
    EOF

    type '<C-n><C-n>cworld<Esc>u'

    after <<-EOF
      hello
      hello
    EOF
  end

  specify "#multiline visual mode" do
    before <<-EOF
      hello
      hello
    EOF

    type 'Vj<C-n>A world<Esc>'

    after <<-EOF
      hello world
      hello world
    EOF
  end

  specify "#set paste mode" do
    before <<-EOF
      hello
      hello
    EOF

    type ':set paste<CR><C-n><C-n>cworld<Esc>:set nopaste<CR>'

    after <<-EOF
      world
      world
    EOF
  end

  specify "#multi-byte strings" do
    before <<-EOF
      こんにちわビム
      世界の中心でビムを叫ぶ
      ビム大好き
    EOF

    type '/ビム<CR><C-n><C-n><C-n>cヴィム<ESC>'

    after <<-EOF
      こんにちわヴィム
      世界の中心でヴィムを叫ぶ
      ヴィム大好き
    EOF
  end

end


================================================
FILE: spec/spec_helper.rb
================================================
require 'vimrunner'
require 'vimrunner/rspec'

Vimrunner::RSpec.configure do |config|

  # Use a single Vim instance for the test suite. Set to false to use an
  # instance per test (slower, but can be easier to manage).
  config.reuse_server = false

  # Decide how to start a Vim instance. In this block, an instance should be
  # spawned and set up with anything project-specific.
  config.start_vim do
    # vim = Vimrunner.start

    # Or, start a GUI instance:
    vim = Vimrunner.start_gvim

    # Setup your plugin in the Vim instance
    plugin_path = File.expand_path('../..', __FILE__)
    vim.add_plugin(plugin_path, 'plugin/multiple_cursors.vim')

    # The returned value is the Client available in the tests.
    vim
  end
end
Download .txt
gitextract_2zx8hs8j/

├── .gitignore
├── .rspec
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Gemfile
├── MIT-LICENSE.txt
├── README.md
├── Rakefile
├── autoload/
│   └── multiple_cursors.vim
├── doc/
│   └── multiple_cursors.txt
├── plugin/
│   └── multiple_cursors.vim
└── spec/
    ├── benchmark_spec.rb
    ├── multiple_cursors_spec.rb
    └── spec_helper.rb
Download .txt
SYMBOL INDEX (10 symbols across 2 files)

FILE: spec/benchmark_spec.rb
  function set_file_content (line 28) | def set_file_content(string)
  function get_file_content (line 34) | def get_file_content()
  function before (line 39) | def before(string)
  function after (line 43) | def after(string)
  function type (line 48) | def type(string)

FILE: spec/multiple_cursors_spec.rb
  function set_file_content (line 4) | def set_file_content(string)
  function get_file_content (line 10) | def get_file_content()
  function before (line 15) | def before(string)
  function after (line 20) | def after(string)
  function type (line 24) | def type(string)
Condensed preview — 15 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (104K chars).
[
  {
    "path": ".gitignore",
    "chars": 10,
    "preview": "/doc/tags\n"
  },
  {
    "path": ".rspec",
    "chars": 19,
    "preview": "--color\n--format d\n"
  },
  {
    "path": ".travis.yml",
    "chars": 136,
    "preview": "os: linux\ndist: bionic\nlanguage: ruby\n\naddons:\n  apt:\n    packages:\n      - vim-gtk\n      - xvfb\n\nscript:\n  - xvfb-run b"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 3311,
    "preview": "## 2.2 (06/10/2013)\nBugfixes:\n  - Fix plugin break in PASTE mode. This fixes #44.\n\n## 2.1 (04/26/2013)\n\nBugfixes:\n  - Fi"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 420,
    "preview": "# Problems summary\n\n## Expected\n\n## Environment Information\n * OS:\n * Neovim/Vim/Gvim version:\n\n## Provide a minimal .vi"
  },
  {
    "path": "Gemfile",
    "chars": 69,
    "preview": "source 'https://rubygems.org'\ngem 'vimrunner'\ngem 'rake'\ngem 'rspec'\n"
  },
  {
    "path": "MIT-LICENSE.txt",
    "chars": 1048,
    "preview": "Copyright 2013 Terry Ma\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software a"
  },
  {
    "path": "README.md",
    "chars": 11082,
    "preview": "# **❗ This plugin is deprecated, use [vim-visual-multi](https://github.com/mg979/vim-visual-multi) instead ❗**\n\n# vim-mu"
  },
  {
    "path": "Rakefile",
    "chars": 235,
    "preview": "require 'rspec/core/rake_task'\n\nRSpec::Core::RakeTask.new(:spec) do |t|\n  t.pattern = 'spec/multiple_cursors_spec.rb'\nen"
  },
  {
    "path": "autoload/multiple_cursors.vim",
    "chars": 45625,
    "preview": "\"===============================================================================\n\" Initialization\n\"====================="
  },
  {
    "path": "doc/multiple_cursors.txt",
    "chars": 11037,
    "preview": "*vim-multiple-cursors.txt* True Sublime Text multiple selection in Vim\n\n                    ____  _       __\n   ____ ___"
  },
  {
    "path": "plugin/multiple_cursors.vim",
    "chars": 3641,
    "preview": "\"===============================================================================\n\" File: multiple_cursors.vim\n\" Author: "
  },
  {
    "path": "spec/benchmark_spec.rb",
    "chars": 5571,
    "preview": "require 'vimrunner'\nrequire 'vimrunner/rspec'\n\nVimrunner::RSpec.configure do |config|\n\n  # Use a single Vim instance for"
  },
  {
    "path": "spec/multiple_cursors_spec.rb",
    "chars": 16526,
    "preview": "# -*- encoding: utf-8 -*-\nrequire 'spec_helper'\n\ndef set_file_content(string)\n  string = normalize_string_indent(string)"
  },
  {
    "path": "spec/spec_helper.rb",
    "chars": 742,
    "preview": "require 'vimrunner'\nrequire 'vimrunner/rspec'\n\nVimrunner::RSpec.configure do |config|\n\n  # Use a single Vim instance for"
  }
]

About this extraction

This page contains the full source code of the terryma/vim-multiple-cursors GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 15 files (97.1 KB), approximately 27.6k tokens, and a symbol index with 10 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!