1
0
mirror of https://github.com/amix/vimrc synced 2025-07-18 09:35:00 +08:00
This commit is contained in:
huangqundl
2017-03-17 23:12:53 +08:00
parent 47b213d974
commit cba39b7326
855 changed files with 59981 additions and 35298 deletions

View File

@ -0,0 +1 @@
/doc/tags

View File

@ -1,7 +1,11 @@
sudo: false
language: ruby
rvm:
- 1.9.3
before_install: sudo apt-get install vim-gtk
addons:
apt:
packages:
- vim-gtk
before_script:
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"

View File

@ -0,0 +1,23 @@
# 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

View File

@ -1,17 +1,22 @@
GEM
remote: https://rubygems.org/
specs:
diff-lcs (1.2.4)
rake (10.0.4)
rspec (2.13.0)
rspec-core (~> 2.13.0)
rspec-expectations (~> 2.13.0)
rspec-mocks (~> 2.13.0)
rspec-core (2.13.1)
rspec-expectations (2.13.0)
diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.13.1)
vimrunner (0.3.0)
diff-lcs (1.2.5)
rake (10.4.2)
rspec (3.4.0)
rspec-core (~> 3.4.0)
rspec-expectations (~> 3.4.0)
rspec-mocks (~> 3.4.0)
rspec-core (3.4.1)
rspec-support (~> 3.4.0)
rspec-expectations (3.4.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.4.0)
rspec-mocks (3.4.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.4.0)
rspec-support (3.4.1)
vimrunner (0.3.1)
PLATFORMS
ruby
@ -20,3 +25,6 @@ DEPENDENCIES
rake
rspec
vimrunner
BUNDLED WITH
1.10.6

View File

@ -1,4 +1,28 @@
# vim-multiple-cursors [![Build Status](https://travis-ci.org/terryma/vim-multiple-cursors.png)](https://travis-ci.org/terryma/vim-multiple-cursors)
# vim-multiple-cursors
[![Build Status](https://travis-ci.org/terryma/vim-multiple-cursors.svg)](https://travis-ci.org/terryma/vim-multiple-cursors)
[![Issue Stats](http://issuestats.com/github/terryma/vim-multiple-cursors/badge/pr?style=flat)](http://issuestats.com/github/terryma/vim-multiple-cursors)
[![Issue Stats](http://issuestats.com/github/terryma/vim-multiple-cursors/badge/issue?style=flat)](http://issuestats.com/github/terryma/vim-multiple-cursors)
## Contents
- [About](#about)
- [Features](#features)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Mapping](#mapping)
- [Settings](#settings)
- [Interactions with other plugins](#interactions-with-other-plugins)
- [Highlight](#highlight)
- *[FAQ](#faq)*
- *[Known issues](#known-issues)*
- *[Issue creation requirements](#issue-creation-requirements)*
- [Changelog](#changelog)
- [Contributing](#contributing)
- [Credit](#credit)
###Contributors
- [eapache](https://github.com/eapache)
- [aschrab](https://github.com/aschrab)
- [kristijanhusak](https://github.com/kristijanhusak)
- [faceleg](https://github.com/faceleg)
## 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.
@ -6,27 +30,36 @@
### It's great for quick refactoring
![Example1](assets/example1.gif?raw=true)
Vim command sequence: `2Gfp<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: `2Gvip<C-n>i"<Right><Right><Right>",<Esc>vipJ$r]Idays = [`
### Do it backwards too! This is not just a replay of the above gif :)
![Example3](assets/example3.gif?raw=true)
Vim command sequence: `2Gdf[$r,0f,v<C-n>…<C-n>c<CR><Up><Del><Right><Right><Right><Del>`
### Add multiple cursors using regexes
![Example4](assets/example4.gif?raw=true)
To see what keystrokes are used for the above example, see [this issue](https://github.com/terryma/vim-multiple-cursors/issues/39).
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).
## Features
- Live update in Insert mode
- One key to rule it all! See [Quick Start](#quick-start) on what the key does in different scenarios
- Works in Normal, Insert, and Visual mode for SINGLE key command
- Works in Normal, Insert, and Visual mode for any commands (including
multi-key commands, assuming you set `g:multicursor_insert_maps` and
`g:multicursor_normal_maps`; see Settings below for details)
## Installation
Install using [Pathogen], [Vundle], [Neobundle], or your favorite Vim package manager.
Requires vim 7.4 or later for full functionality.
## Quick Start
Out of the box, all you need to know is a single key `Ctrl-n`. Pressing the key in Normal mode highlights the current word under the cursor in Visual mode and places a virtual cursor at the end of it. Pressing it again finds the next ocurrence and places another virtual cursor at the end of the visual selection. If you select multiple lines in Visual mode, pressing the key puts a virtual cursor at every line and leaves you in Normal mode.
Out of the box, all you need to know is a single key `Ctrl-n`. Pressing the key in Normal mode highlights the current word under the cursor in Visual mode and places a virtual cursor at the end of it. Pressing it again finds the next occurrence and places another virtual cursor at the end of the visual selection. If you select multiple lines in Visual mode, pressing the key puts a virtual cursor at every line and leaves you in Normal mode.
After you've marked all your locations with `Ctrl-n`, you can change the visual selection with normal Vim motion commands in Visual mode. You could go to Normal mode by pressing `v` and wield your motion commands there. Single key command to switch to Insert mode such as `c` or `s` from Visual mode or `i`, `a`, `I`, `A` in Normal mode should work without any issues.
@ -36,18 +69,18 @@ Two additional keys are also mapped:
- `Ctrl-p` in Visual mode will remove the current virtual cursor and go back to the previous virtual cursor location. This is useful if you are trigger happy with `Ctrl-n` and accidentally went too far.
- `Ctrl-x` in Visual mode will remove the current virtual cursor and skip to the next virtual cursor location. This is useful if you don't want the current selection to be a candidate to operate on later.
You can also add multiple cursors using a regular expression. The command `MultipleCursorsFind` accepts a range and a pattern, and it will create a virtual cursor at the end of every match within the range. If no range is passed in, then it defaults to the entire buffer.
You can also add multiple cursors using a regular expression. The command `MultipleCursorsFind` accepts a range and a pattern, and it will create a virtual cursor at the end of every match within the range. If no range is passed in, then it defaults to the entire buffer.
**NOTE:** If at any time you have lingering cursors on screen, you can press `Ctrl-n` in Normal mode and it will remove all prior cursors before starting a new one.
## Mapping
Out of the box, only the single key `Ctrl-n` is mapped in regular Vim's Normal mode and Visual mode to provide the functionality mentioned above. `Ctrl-n`, `Ctrl-p`, `Ctrl-x`, and `<Esc>` are mapped in the special multicursor mode once you've added at least one virtual cursor to the buffer. If you don't like the plugin taking over your favorite key bindings, you can turn off the default with
```
```viml
let g:multi_cursor_use_default_mapping=0
```
You can then map the 'next', 'previous', 'skip', and 'exit' keys like the following:
```
```viml
" Default mapping
let g:multi_cursor_next_key='<C-n>'
let g:multi_cursor_prev_key='<C-p>'
@ -56,48 +89,149 @@ let g:multi_cursor_quit_key='<Esc>'
```
By default, the 'next' key is also used to enter multicursor mode. If you want to use a different key to start multicursor mode than for selecting the next location, do like the following:
```
```viml
" Map start key separately from next key
let g:multi_cursor_start_key='<F6>'
```
**IMPORTANT:** Please note that currently only single keystrokes and special keys can be mapped. This contraint is also the reason why multikey commands such as `ciw` do not work and cause unexpected behavior in Normal mode. This means that a mapping like `<Leader>n` will NOT work correctly. For a list of special keys that are supported, see `help :key-notation`
Note that when multicursor mode is started, it selects current word with boundaries, i.e. it behaves like `*`. If you want to avoid word boundaries in Normal mode (as `g*` does) but still have old behaviour up your sleeve, you can do the following:
```viml
let g:multi_cursor_start_key='<C-n>'
let g:multi_cursor_start_word_key='g<C-n>'
```
In this configuration `<C-n>` will start multicursor mode without word boundaries (but only in Normal mode, as it does not make much sense to use it in Visual mode). Old behaviour with word boundaries is still available using `g<C-n>`.
**IMPORTANT:** Please note that currently only single keystrokes and special keys can be mapped. This means that a mapping like `<Leader>n` will NOT work correctly. For a list of special keys that are supported, see `help :key-notation`
**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.
**NOTE:** Prior to version 1.3, the recommended way to map the keys is using the expressoin quote syntax in Vim, using something like `"\<C-n>"` or `"\<Esc>"` (see h: expr-quote). After 1.3, the recommended way is to use a raw string like above. If your key mappings don't appear to work, give the new syntax a try.
**NOTE:** Prior to version 1.3, the recommended way to map the keys is using the expression quote syntax in Vim, using something like `"\<C-n>"` or `"\<Esc>"` (see h: expr-quote). After 1.3, the recommended way is to use a raw string like above. If your key mappings don't appear to work, give the new syntax a try.
You can also map your own keys to quit, if ``g:multi_cursor_quit_key`` won't work:
```
let g:multi_cursor_quit_key='<C-c>'
nnoremap <C-c> :call multiple_cursors#quit()<CR>
```
## Settings
Currently there are four additional global settings one can tweak:
## Setting
Currently there're two additional global settings one can tweak:
### ```g:multi_cursor_exit_from_visual_mode``` (Default: 1)
If set to 0, then pressing `g:multi_cursor_quit_key` in _Visual_ mode will not quit and delete all existing cursors. This is useful if you want to press Escape and go back to Normal mode, and still be able to operate on all the cursors.
### ```g:multi_cursor_exit_from_insert_mode``` (Default: 1)
If set to 0, then pressing `g:multi_cursor_quit_key` in _Insert_ mode will not quit and delete all existing cursors. This is useful if you want to press Escape and go back to Normal mode, and still be able to operate on all the cursors.
### ```g:multi_cursor_insert_maps``` (Default: `{}`)
Any key in this map (values are ignored) will cause multi-cursor _Insert_ mode
to pause for `timeoutlen` waiting for map completion just like normal vim.
Otherwise keys mapped in insert mode are ignored when multiple cursors are
active. For example, setting it to `{'\':1}` will make insert-mode mappings
beginning with the default leader key work in multi-cursor mode. You have to
manually set this because vim doesn't provide a way to see which keys _start_
mappings.
### ```g:multi_cursor_normal_maps``` (Default: see below)
Default value: `{'!':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}`
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,
changing it from `{}` to `{'d':1}` makes normal-mode mappings beginning with `d`
(such as `dw` to delete a word) work in multi-cursor mode.
### ```g:multi_cursor_visual_maps``` (Default: see below)
Default value: `{'i':1, 'a':1, 'f':1, 'F':1, 't':1, 'T':1}`
Any key in this map (values are ignored) will cause multi-cursor _Visual_ mode
to pause for map completion just like normal vim. Otherwise keys mapped in
visual mode will "fail to replay" when multiple cursors are active. For example,
changing it from `{}` to `{'i':1}` makes visual-mode mappings beginning with `i`
(such as `it` to select an "inner tag block") 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.
### Interactions with other plugins
### ```Multiple_cursors_before/Multiple_cursors_after``` (Default: `nothing`)
Other plugins may trigger on keypresses when in insert mode. These plugins
generally provide a means to toggle their active state. These hooks allow
the user to provide functions in their .vimrc to do this when multiple-cursor-mode
is entered.
For example, if you are using [Neocomplete](https://github.com/Shougo/neocomplete.vim),
add this to your vimrc to prevent conflict:
```viml
" Called once right before you start selecting multiple cursors
function! Multiple_cursors_before()
if exists(':NeoCompleteLock')==2
exe 'NeoCompleteLock'
endif
endfunction
" Called once only when the multiple selection is canceled (default <Esc>)
function! Multiple_cursors_after()
if exists(':NeoCompleteUnlock')==2
exe 'NeoCompleteUnlock'
endif
endfunction
```
With this locking and unlocking we prevent neocomplete to trigger it's function calls until we are finished with multiple cursors editing.
### 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
```
## Issues
- Multi key commands like `ciw` do not work at the moment
- All user input typed before Vim is able to fan out the last operation to all cursors is lost. This is a implementation decision to keep the input perfectly synced in all locations, at the cost of potentially losing user input.
## FAQ
#### **Q** <kbd>CTRL</kbd>+<kbd>n</kbd> doesn't seem to work in gVIM?
**A** Try setting `set selection=inclusive` in your `~/.gvimrc`
#### **Q** How can I select `n` keywords with several keystrokes? I have tried `200<C-n>` which 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 a) search for the keyword using `*` b) turn search results into cursors with `Alt-j`.
## Known Issues
- Select mode is not implemented
## Issue Creation Requirements
This is a community supported project. Contributor's time is precious and limited. To ensure your issue is not closed out of hand, please ensure it meets the requirements outlined in [CONTRIBUTING.md](CONTRIBUTING.md).
## Changelog
See [CHANGELOG.md](CHANGELOG.md)
## Contributing
As one can see, there're still many issues to be resolved, patches and suggestions are always welcome! A list of open feature requests can be found [here](../../issues?labels=enhancement&state=open).
Running the test suite requires ruby and rake as well as vim of course. On Mac
OS, [MacVim](https://code.google.com/p/macvim/) is known to work.
## Credit
Obviously inspired by Sublime Text's [multiple selection][sublime-multiple-selection] feature, also encouraged by Emac's [multiple cursors][emacs-multiple-cursors] implemetation by Magnar Sveen
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
@ -107,6 +241,4 @@ Obviously inspired by Sublime Text's [multiple selection][sublime-multiple-selec
[emacs-multiple-cursors]:https://github.com/magnars/multiple-cursors.el
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/terryma/vim-multiple-cursors/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

View File

@ -8,22 +8,23 @@
" 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 key in [ 'g:multi_cursor_next_key',
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(key)
if exists(s:key)
" Translate raw strings like "<C-n>" into key code like "\<C-n>"
exec 'let temp = '.key
if temp =~ '^<.*>$'
exec 'let '.key.' = "\'.temp.'"'
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 '.key.' = ""'
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
@ -38,6 +39,13 @@ 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"
@ -46,32 +54,38 @@ 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>(i) <C-o>:call <SID>process_user_input()<CR>
nnoremap <silent> <Plug>(i) :call <SID>process_user_input()<CR>
xnoremap <silent> <Plug>(i) :<C-u>call <SID>process_user_input()<CR>
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>(a) <C-o>:call <SID>apply_user_input_next('i')<CR>
nnoremap <silent> <Plug>(a) :call <SID>apply_user_input_next('n')<CR>
xnoremap <silent> <Plug>(a) :<C-u>call <SID>apply_user_input_next('v')<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>(d) <C-o>:call <SID>detect_bad_input()<CR>
nnoremap <silent> <Plug>(d) :call <SID>detect_bad_input()<CR>
xnoremap <silent> <Plug>(d) :<C-u>call <SID>detect_bad_input()<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>(w) <C-o>:call <SID>wait_for_user_input('')<CR>
nnoremap <silent> <Plug>(w) :call <SID>wait_for_user_input('')<CR>
xnoremap <silent> <Plug>(w) :<C-u>call <SID>wait_for_user_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>(p) :<C-u>call multiple_cursors#prev()<CR>
nnoremap <silent> <Plug>(s) :<C-u>call multiple_cursors#skip()<CR>
nnoremap <silent> <Plug>(n) :<C-u>call multiple_cursors#new('v')<CR>
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
@ -95,7 +109,13 @@ endfunction
" 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)
function! multiple_cursors#new(mode, word_boundary)
" 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
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)
@ -150,6 +170,11 @@ function! multiple_cursors#new(mode)
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()
@ -193,9 +218,12 @@ function! multiple_cursors#find(start, end, pattern)
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')
let first = 0
else
let match = search(a:pattern, 'W')
endif
@ -203,9 +231,26 @@ function! multiple_cursors#find(start, end, pattern)
break
endif
let left = s:pos('.')
call search(a:pattern, 'ceW')
" 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])
@ -221,6 +266,14 @@ function! multiple_cursors#find(start, end, pattern)
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
if exists('*Multiple_cursors_before') && !s:before_function_called
exe "call Multiple_cursors_before()"
let s:before_function_called = 1
endif
call s:wait_for_user_input('v')
endif
endfunction
@ -235,9 +288,16 @@ 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
@ -293,12 +353,24 @@ 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
"===============================================================================
@ -319,6 +391,7 @@ function! s:CursorManager.new()
\ 'cursorline': &cursorline,
\ 'lazyredraw': &lazyredraw,
\ 'paste': &paste,
\ 'clipboard': &clipboard,
\ }
" We save the window view when multicursor mode is entered
let obj.saved_winview = []
@ -328,7 +401,7 @@ function! s:CursorManager.new()
endfunction
" Clear all cursors and their highlights
function! s:CursorManager.reset(restore_view, restore_setting) dict
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)
@ -358,9 +431,15 @@ function! s:CursorManager.reset(restore_view, restore_setting) dict
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 exists('*Multiple_cursors_after') && a:0 && s:before_function_called
exe "call Multiple_cursors_after()"
let s:before_function_called = 0
endif
endfunction
" Returns 0 if it's not managing any cursors at the moment
@ -434,14 +513,24 @@ function! s:CursorManager.update_current() dict
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 != self.size() - 1
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 vdelta = line('$') - s:saved_linecount
" If the total number of lines changed in the buffer, we need to potentially
" adjust other cursor locations
if vdelta != 0
if self.current_index != self.size() - 1
let cur_line_length = len(getline(cur.line()))
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
@ -453,7 +542,7 @@ function! s:CursorManager.update_current() dict
if cur.line() == c.line()
if vdelta > 0
" Added a line
let hdelta = cur_line_length * -1
let hdelta = cur_column_offset
else
" Removed a line
let hdelta = new_line_length
@ -518,19 +607,28 @@ endfunction
" 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.
@ -540,7 +638,22 @@ function! s:CursorManager.restore_user_settings() dict
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
@ -587,6 +700,9 @@ endfunction
" 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
@ -628,6 +744,12 @@ endfunction
" 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
@ -643,7 +765,7 @@ endfunction
function! s:select_in_visual_mode(region)
if a:region[0] == a:region[1]
normal! v
else
else
call cursor(a:region[1])
normal! m`
call cursor(a:region[0])
@ -660,7 +782,7 @@ endfunction
function! s:update_visual_markers(region)
if a:region[0] == a:region[1]
normal! v
else
else
call cursor(a:region[1])
normal! m`
call cursor(a:region[0])
@ -674,7 +796,11 @@ endfunction
" Mode change: Normal -> Normal
" Cursor change: Set to the end of the match
function! s:find_next(text)
let pattern = '\V\C'.substitute(escape(a:text, '\'), '\n', '\\n', 'g')
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')
@ -714,7 +840,7 @@ function! s:highlight_region(region)
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.\_$'
let pattern = pattern.'\|\%>'.s[0][0].'l\%<'.s[1][0].'l.*\ze.\_$'
endif
endif
endif
@ -725,28 +851,31 @@ endfunction
function! s:revert_mode(from, to)
if a:to ==# 'v'
call s:cm.reapply_visual_selection()
endif
if a:to ==# 'V'
elseif a:to ==# 'V'
call s:cm.reapply_visual_selection()
normal! V
endif
if a:to ==# 'n' && a:from ==# 'i'
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.
" TODO(terryma): This solves the problem of cursors getting out of sync, but
" we're potentially losing user input. We COULD replay these characters as
" well...
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 type(c) == 0 && c == 0
break
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)
@ -757,7 +886,8 @@ 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
call cursor(s:cm.get_current().position)
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
@ -765,13 +895,20 @@ function! s:process_user_input()
" Update the line length BEFORE applying any actions. TODO(terryma): Is there
" a better place to do this?
call s:cm.get_current().update_line_length()
" 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>(a) never gets called, we have a
" detection mechanism using \<Plug>(d). See its documentation for more details
" 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
@ -781,14 +918,14 @@ function! s:process_user_input()
" 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>(a)")
silent! undojoin | call s:feedkeys(s:char."\<Plug>(multiple-cursors-apply)")
else
call s:feedkeys(s:char."\<Plug>(a)")
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>(d)", 't')
call feedkeys("\<Plug>(multiple-cursors-detect)", 't')
endfunction
" This method is always called during fanout, even when a bad user input causes
@ -796,12 +933,34 @@ endfunction
" 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>(a)")
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
@ -828,10 +987,11 @@ function! s:apply_user_input_next(mode)
" This is necessary to set the "'<" and "'>" markers properly
call s:update_visual_markers(s:cm.get_current().visual)
endif
call feedkeys("\<Plug>(w)")
call feedkeys("\<Plug>(multiple-cursors-wait)")
call s:handle_visual_IA_to_insert()
else
" Continue to next
call feedkeys("\<Plug>(i)")
call feedkeys("\<Plug>(multiple-cursors-input)")
endif
endfunction
@ -873,7 +1033,7 @@ endfunction
" Quits multicursor mode and clears all cursors. Return true if exited
" successfully.
function! s:exit()
if s:char !=# g:multi_cursor_quit_key
if s:last_char() !=# g:multi_cursor_quit_key
return 0
endif
let exit = 0
@ -887,7 +1047,7 @@ function! s:exit()
let exit = 1
endif
if exit
call s:cm.reset(1, 1)
call s:cm.reset(1, 1, 1)
return 1
endif
return 0
@ -902,11 +1062,15 @@ function! s:handle_special_key(key, mode)
" increasing the call stack, since feedkeys execute after the current call
" finishes
if a:key == g:multi_cursor_next_key
call s:feedkeys("\<Plug>(n)")
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>(p)")
call s:feedkeys("\<Plug>(multiple-cursors-prev)")
elseif a:key == g:multi_cursor_skip_key
call s:feedkeys("\<Plug>(s)")
call s:feedkeys("\<Plug>(multiple-cursors-skip)")
endif
endfunction
@ -941,12 +1105,22 @@ function! s:revert_highlight_fix()
let s:saved_line = 0
endfunction
let s:retry_keys = ""
function! s:display_error()
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
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
@ -982,15 +1156,19 @@ function! s:end_latency_measure()
let s:skip_latency_measure = 0
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 = ''
call s:display_error()
" Right before redraw, apply the highlighting bug fix
call s:apply_highlight_fix()
@ -1001,7 +1179,39 @@ function! s:wait_for_user_input(mode)
call s:end_latency_measure()
let s:char = s:get_char()
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
if s:from_mode ==# 'i' && has_key(g:multi_cursor_insert_maps, s:last_char())
let c = getchar(0)
let char_type = type(c)
let poll_count = 0
while char_type == 0 && c == 0 && poll_count < &timeoutlen
sleep 1m
let c = getchar(0)
let char_type = type(c)
let poll_count += 1
endwhile
if char_type == 0 && c != 0
let s:char .= nr2char(c)
elseif char_type == 1 " char with more than 8 bits (as string)
let s:char .= c
endif
elseif s:from_mode !=# 'i' && s:char[0] ==# ":"
call feedkeys(s:char)
call s:cm.reset(1, 1)
return
elseif s:from_mode ==# 'n'
while match(s:last_char(), "\\d") == 0
let s:char .= s:get_char()
endwhile
endif
call s:start_latency_measure()
@ -1013,11 +1223,11 @@ function! s:wait_for_user_input(mode)
endif
" If the key is a special key and we're in the right mode, handle it
if index(get(s:special_keys, s:from_mode, []), s:char) != -1
call s:handle_special_key(s:char, s:from_mode)
if index(get(s:special_keys, s:from_mode, []), s:last_char()) != -1
call s:handle_special_key(s:last_char(), s:from_mode)
call s:skip_latency_measure()
else
call s:cm.start_loop()
call s:feedkeys("\<Plug>(i)")
call s:feedkeys("\<Plug>(multiple-cursors-input)")
endif
endfunction

View File

@ -1,11 +1,11 @@
*vim-multiple-cursors.txt* True Sublime Text multiple selection in Vim
____ _ __
____ _ __
____ ___ __ __/ / /_(_)___ / /__ _______ ________________ __________
/ __ `__ \/ / / / / __/ / __ \/ / _ \ / ___/ / / / ___/ ___/ __ \/ ___/ ___/
/ / / / / / /_/ / / /_/ / /_/ / / __/ / /__/ /_/ / / (__ ) /_/ / / (__ )
/_/ /_/ /_/\__,_/_/\__/_/ .___/_/\___/ \___/\__,_/_/ /____/\____/_/ /____/
/_/
/ / / / / / /_/ / / /_/ / /_/ / / __/ / /__/ /_/ / / (__ ) /_/ / / (__ )
/_/ /_/ /_/\__,_/_/\__/_/ .___/_/\___/ \___/\__,_/_/ /____/\____/_/ /____/
/_/
Reference Manual~
@ -71,7 +71,7 @@ CTRL-N in Normal mode and it will remove all prior cursors before starting a
new one.
==============================================================================
3. Mappings *multiple-cursors-mappings*
3. Mappings *multiple-cursors-mappings*
*g:multi_cursor_use_default_mapping* (Default: 1)
@ -108,7 +108,22 @@ mode than for selecting the next location, do like the following: >
let g:multi_cursor_start_key='<F6>'
<
IMPORTANT: Please note that currently only single keystroes and special
*g:multi_cursor_start_word_key*
When multicursor mode is started, it selects current word without
boundaries, i.e. it behaves like `g*`. If you want to use word boundaries in
Normal mode (as `*` does) but still have old behaviour up your sleeve, you can
do the following: >
let g:multi_cursor_start_key='g<C-n>'
let g:multi_cursor_start_word_key='<C-n>'
<
In this configuration <C-n> will start multicursor mode using word boundaries
(but only in Normal mode, as it does not make much sense to use it in Visual
mode). Old behaviour without word boundaries is still available using
g<C-n>.
IMPORTANT: Please note that currently only single keystrokes and special
keys can be mapped. This contraint is also the reason why multikey commands
such as `ciw` do not work and cause unexpected behavior in Normal mode. This
means that a mapping like `<Leader>n` will NOT work correctly. For a list of
@ -118,17 +133,17 @@ 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.
NOTE: Prior to version 1.3, the recommended way to map the keys is using the
expressoin quote syntax in Vim, using something like `"\<C-n>"` or `"\<Esc>"`
expression quote syntax in Vim, using something like `"\<C-n>"` or `"\<Esc>"`
(see h: expr-quote). After 1.3, the recommended way is to use a raw string
like above. If your key mappings don't appear to work, give the new syntax a
try.
==============================================================================
4. Global Options *multiple-cursors-global-options*
4. Global Options *multiple-cursors-global-options*
Currently there're two additional global settings one can tweak:
Currently there are four additional global settings one can tweak:
*g:multi_cursor_exit_from_visual_mode* (Defaut: 1)
*g:multi_cursor_exit_from_visual_mode* (Default: 1)
If set to 0, then pressing |g:multi_cursor_quit_key| in Visual mode will not
quit and delete all existing cursors. This is useful if you want to press
@ -142,6 +157,45 @@ quit and delete all existing cursors. This is useful if you want to press
Escape and go back to Normal mode, and still be able to operate on all the
cursors.
*g:multi_cursor_insert_maps* (Default: `{}`)
Any key in this map (values are ignored) will cause multi-cursor _Insert_ mode
to pause for `timeoutlen` waiting for map completion just like normal vim.
Otherwise keys mapped in insert mode are ignored when multiple cursors are
active. For example, setting it to `{'\':1}` will make insert-mode mappings
beginning with the default leader key work in multi-cursor mode. You have to
manually set this because vim doesn't provide a way to see which keys _start_
mappings.
*g:multi_cursor_normal_maps* (Default: see below)
Default value: `{'!':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}`
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,
changing it from `{}` to `{'d':1}` makes normal-mode mappings beginning with `d`
(such as `dw` to delete a word) work in multi-cursor mode.
*g:multi_cursor_visual_maps* (Default: )
Default value: `{'i':1, 'a':1, 'f':1, 'F':1, 't':1, 'T':1}`
Any key in this map (values are ignored) will cause multi-cursor _Visual_ mode
to pause for map completion just like normal vim. Otherwise keys mapped in
visual mode will "fail to replay" when multiple cursors are active. For example,
changing it from `{}` to `{'i':1}` makes visual-mode mappings beginning with `i`
(such as `it` to select an "inner tag block") 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.
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
@ -150,10 +204,11 @@ 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
<
==============================================================================
5. Issues *multiple-cursors-issues*
5. Issues *multiple-cursors-issues*
- Multi key commands like ciw do not work at the moment
- All user input typed before Vim is able to fan out the last operation to all
@ -163,7 +218,7 @@ like the following in your vimrc: >
- Select mode is not implemented
==============================================================================
6. Contributing *multiple-cursors-contributing*
6. Contributing *multiple-cursors-contributing*
The project is hosted on Github. Patches, feature requests and suggestions are
always welcome!
@ -172,19 +227,19 @@ Find the latest version of the plugin here:
http://github.com/terryma/vim-multiple-cursors
==============================================================================
7. License *multiple-cursors-license*
7. License *multiple-cursors-license*
The project is licensed under the MIT license [7]. Copyrigth 2013 Terry Ma
==============================================================================
8. Credit *multiple-cursors-credit*
8. 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.
implementation.
==============================================================================
9. References *multiple-cursors-references*
9. References *multiple-cursors-references*
[1] https://github.com/paradigm/vim-multicursor
[2] https://github.com/felixr/vim-multiedit

View File

@ -1,12 +1,16 @@
MultipleCursorsFind multiple_cursors.txt /*MultipleCursorsFind*
g:multi_cursor_exit_from_insert_mode multiple_cursors.txt /*g:multi_cursor_exit_from_insert_mode*
g:multi_cursor_exit_from_visual_mode multiple_cursors.txt /*g:multi_cursor_exit_from_visual_mode*
g:multi_cursor_insert_maps multiple_cursors.txt /*g:multi_cursor_insert_maps*
g:multi_cursor_next_key multiple_cursors.txt /*g:multi_cursor_next_key*
g:multi_cursor_normal_maps multiple_cursors.txt /*g:multi_cursor_normal_maps*
g:multi_cursor_prev_key multiple_cursors.txt /*g:multi_cursor_prev_key*
g:multi_cursor_quit_key multiple_cursors.txt /*g:multi_cursor_quit_key*
g:multi_cursor_skip_key multiple_cursors.txt /*g:multi_cursor_skip_key*
g:multi_cursor_start_key multiple_cursors.txt /*g:multi_cursor_start_key*
g:multi_cursor_start_word_key multiple_cursors.txt /*g:multi_cursor_start_word_key*
g:multi_cursor_use_default_mapping multiple_cursors.txt /*g:multi_cursor_use_default_mapping*
g:multi_cursor_visual_maps multiple_cursors.txt /*g:multi_cursor_visual_maps*
multiple-cursors-contents multiple_cursors.txt /*multiple-cursors-contents*
multiple-cursors-contributing multiple_cursors.txt /*multiple-cursors-contributing*
multiple-cursors-credit multiple_cursors.txt /*multiple-cursors-credit*

View File

@ -1,7 +1,7 @@
"===============================================================================
" File: multiple_cursors.vim
" Author: Terry Ma
" Description: Emulate Sublime Text's multi selection feature
" 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
@ -40,26 +40,49 @@ let s:settings_if_default = {
\ 'skip_key': '<C-x>',
\ }
let s:default_insert_maps = {}
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_insert_maps =
\ get(g:, 'multi_cursor_insert_maps', s:default_insert_maps)
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_key') && exists('g:multi_cursor_next_key')
let g:multi_cursor_start_key = g:multi_cursor_next_key
if !exists('g:multi_cursor_start_word_key')
if exists('g:multi_cursor_start_key')
let g:multi_cursor_start_word_key = g:multi_cursor_start_key
elseif 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")<CR>'
\' :call multiple_cursors#new("n", 0)<CR>'
exec 'xnoremap <silent> '.g:multi_cursor_start_key.
\' :<C-u>call multiple_cursors#new("v")<CR>'
\' :<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
" Commands
command! -nargs=1 -range=% MultipleCursorsFind
command! -nargs=1 -range=% MultipleCursorsFind
\ call multiple_cursors#find(<line1>, <line2>, <q-args>)
let &cpo = s:save_cpo

View File

@ -58,6 +58,7 @@ end
describe "Multiple Cursors" do
let(:filename) { 'test.txt' }
let(:options) { [] }
specify "#benchmark" do
before <<-EOF

View File

@ -12,11 +12,12 @@ def get_file_content()
end
def before(string)
options.each { |x| vim.command(x) }
set_file_content(string)
end
def after(string)
get_file_content().should eq normalize_string_indent(string)
expect(get_file_content()).to eq normalize_string_indent(string)
end
def type(string)
@ -29,8 +30,251 @@ def type(string)
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
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" 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
@ -106,6 +350,38 @@ describe "Multiple Cursors" do
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
@ -153,6 +429,48 @@ describe "Multiple Cursors" do
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
@ -199,6 +517,216 @@ describe "Multiple Cursors" do
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
@ -241,22 +769,6 @@ describe "Multiple Cursors" do
EOF
end
# 'd' is an operator pending command, which are not supported at the moment.
# This should result in a nop, 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
specify "#multiline visual mode" do
before <<-EOF
hello

View File

@ -5,7 +5,7 @@ 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 = true
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.