Multiple Cursors in 500 bytes of Vimscript!



Tags: vim Posted on January 19, 2017Tags: programming

In this post we’ll introduce a handful of keybindings that implement (and surpass!) the multiple cursors (MC) functionality in Sublime Text (ST). While the built-in commands :s and :g cover far more ground, MC have the advantage of immediate visual feedback throughout the editing operation—this is the same reason why one may prefer the longer sequence Vjy to copy two lines instead of the faster yj .

The full source code is found in the conclusion near the bottom of the post.

Changing a word Mappings used: nnoremap cn *``cgn The simplest and most common case of MC deals with changing a word to another word in several locations. We would like to change poisson to exponential and int to double in the snippet above. With the Vimscript mapping above , we can accomplish the same in Vim by Position the cursor anywhere in the word poisson ; hit cn , type exponential , then go back to Normal mode; hit . n-1 times, where n is the number of replacements. Do the same in changing int to double . To do this in Sublime Text, we would Position the cursor on poisson ; hit Ctrl-D n times, where n is the number of replacements; type exponential . Do the same in changing int to double . The process is comparable: both takes approximately n keystrokes for n replacements.

Changing a selection Mappings used: let g:mc = "y/\\V\<C-r>=escape(@\", '/')\<CR>\<CR>" vnoremap <expr> cn g:mc . "``cgn" Sometimes, the target to change is not a whole word. In these cases, we would first manually select the word (using appropriate motions and text objects) and then issue cn . In the screencast below, we want to change occurrences of pet into cat , except for the generic pet on the third line. To do this, we Select pet in visual mode. Hit cn to start the replacement process. Enter in cat , then hit . an appropriate number of times. Use n to skip over the matches in the third line. Note that step #3 is impossible to do in Sublime Text! Thus, this is one place where the multiple cursors in vim surpasses its Subliem Text counterpart.

Playing a macro on searches Mappings used: let g:mc = "y/\\V\<C-r>=escape(@\", '/')\<CR>\<CR>" function! SetupCR() nnoremap <Enter> :nnoremap <lt>Enter> [email protected]<CR>q:<C-u>let @z=strpart(@z,0,strlen(@z)-1)<CR>[email protected] endfunction nnoremap cq :call SetupCR()<CR>*``qz vnoremap <expr> cq ":\<C-u>call SetupCR()\<CR>" . "gv" . g:mc . "``qz" In the most advanced usage, we want to execute a macro over a search match, instead of a usual replacement. To this end, the above code introduces the keybindings cq ; it works on the current word in normal mode and works on the selection in visual mode. Since macros can’t be replayed with . , we create a <Enter> mapping instead; we could have used @@ but using <Enter> saves one keystroke, which matters if we are replaying the macro over a large number of lines. Here are the steps: Position the cursor over a word; alternatively, make a selection. Hit cq to start recording the macro. Once you are done with the macro, go back to normal mode. Hit Enter to repeat the macro over search matches. Macros allow the full editing power of vim commands to be used with multiple cursors. Note that we used the command ytS in the screencast to copy the country name in CountryStock , which is not easily done in Sublime Text.

Full Source Code for Copy and Paste The full source code is let g:mc = "y/\\V\<C-r>=escape(@\", '/')\<CR>\<CR>" nnoremap cn *``cgn nnoremap cN *``cgN vnoremap <expr> cn g:mc . "``cgn" vnoremap <expr> cN g:mc . "``cgN" function! SetupCR() nnoremap <Enter> :nnoremap <lt>Enter> [email protected]<CR>q:<C-u>let @z=strpart(@z,0,strlen(@z)-1)<CR>[email protected] endfunction nnoremap cq :call SetupCR()<CR>*``qz nnoremap cQ :call SetupCR()<CR>#``qz vnoremap <expr> cq ":\<C-u>call SetupCR()\<CR>" . "gv" . g:mc . "``qz" vnoremap <expr> cQ ":\<C-u>call SetupCR()\<CR>" . "gv" . substitute(g:mc, '/', '?', 'g') . "``qz" Here I also added a capital letter of the command which iterates through matches in reverse. If you found this post helpful, please consider making a donation; I am a PhD student in need of funding :-). Thank you for your support!!

Final Thoughts These commands implement and surpass (in my opinion) the multiple cursor feature in Sublime Text, and weights just a bit over 500 bytes. It is not perfect, but good enough for everyday use and in some cases surpass the functionality in ST. Note I did not include a feature which executes a macro or replacement on consecutive lines—this corresponds to Sublime Text’s “add cursor below” feature. But this can be more easily accomplished just by using a macro: 0qq <sequence of commands here> <position cursor in the next line>q then replaying the macro q . Finally, here are some caveats to keep in mind: cn has a built-in meaning to change up to the next search (combination of operation c and the motion n ). However, I have never found a use for cn since I’m never sure, without checking, where the next search is. If you do use cn I suggest the alternative vnc . The <Enter> mappings above replace the register @z . If you regularly use this register (I don’t), change the code above to adapt to your needs.