May 15, 2017

Vim Tips & Tricks

UPDATED JULY 2019

As a long-term Vim user myself, here are some of my favourite tips n’ tricks that I have picked up along the journey.

Note, by no means is this an exclusive list, many of these tips will be well-known to the Vim community at large. However, even the most seasoned of Vim users can sometimes learn a trick or two from posts like this.

Quite a few of these tips n’ tricks are baked into my vimrc.

Set relativenumber

I encourage Vim users to enable relative line numbering. This setting makes it easy to figure out how many lines up or down you have to jump, via j and k movements, to get to where you want to go when in normal mode.

set relativenumber

One time normal mode command whilst in insert mode

Whilst in insert mode you can quickly execute a single normal operation with:

Control-o <<command>>

Once the normal mode command has completed you will be returned to insert mode where you were last editing.

A useful example would be centering the text being edited in the current window:

Control-o zz

Expression register in insert mode

Use the expression register, whilst in insert mode, to edit-in simple math values.

Control-r= <<math expression>>

An example could be:

Control-r= 43 + 139 + 761

The inserted value would be 943.

Set global replacement as the default

Force Vim to always do global substitutions.

set gdefault

This removes the need to tack on g at the end of substitute commands. Once gdefault is set the following will be a global substitute command:

:%s/old/new

All the following substitute examples will assume gdefault has been set.

Substitute in a visual block

Do the following to substitute old with new only within a rectangular visual block, that being a Control-v style visual selection:

:'<,'>s/\%Vold/new

Count the number of pattern matches

Use the substitute command to count the number of matches for a particular pattern.

First, execute a search:

/<<some-pattern>>

Then execute a counting substitute:

:%s///n

Note, this particular form of substitute will not actually substitute anything, it will instead just print out the number of matches.

UPDATE: In the comments Smylers suggested this even more compact variant:

:%~n

Sorting

Use the sort command to sort a selection, say a visual selection:

:'<,'>sort

By default, lines starting with 0-9 will be sorted before A-Z followed lastly by lines starting with a-z.

Use the i option to ignore case when sorting, more often than not you want to do this:

:'<,'>sort i

Use a ! to reverse the sort:

:'<,'>sort! i

Lastly the u option can be used to remove duplicates much like the uniq system command:

:'<,'>sort u

Diffing

if has('nvim-0.3.2') || has("patch-8.1.0360")
    set diffopt=filler,internal,algorithm:histogram,indent-heuristic
endif

Vim, starting with patch 8.1.0360, and Neovim, starting with version 0.3.2, incorporates Git’s xdiff library.

This library can improve diffing within Vim for certain kinds of diff. With this enhancement, there is now no need to install diff-specific plugins.

I like and recommend the histogram algorithm with indent heuristic.

Project wide substitution using cfdo

Historically it has been awkward to carry out multi-file substitutions in Vim. Many possibilities exist, some involving argo, others involving sed, but all are convoluted and none are elegant.

The new cfdo command is ideal for substituting, or refactoring in programmer speak, across multiple files. The cfdo command allows a normal mode command, such as a substitute, to be invoked only on the files in the quickfix list.

Note, cfdo is only available in relatively recent versions of Vim or Neovim. Please upgrade to Vim 8 or the newest version of Neovim.

To carry out a cfdo substitute one must first populate the quickfix list with a candidate set of files containing the term wanting to be refactored.

Using vimgrep:

:vimgrep oldterm **

Or using the excellent ripgrep utility via the vim-grepper plugin:

:GrepperRg -i oldterm

From there one simply executes the desired substitution over the list of files in the quickfix list.

:cfdo %s/oldterm/newterm/ | update

UPDATE (MAR 2019): This find & replace helpers for Vim post may be of interest.

Substitute word under cursor and dot repeat

nnoremap <silent> <Leader>c :let @/='\<'.expand('<cword>').'\>'<CR>cgn
xnoremap <silent> <Leader>c "sy:let @/=@s<CR>cgn

The relatively new gn command allows easy operation on the next match of a completed search. These <Leader>c normal and visual mode mappings make use of gn to provide easy word-under-cursor or visual-selected-term substitution, aka in-file refactoring. Best of all simply use . (dot) to repeat that substitution for the next match instead of n. as has usually been necessary in Vim when doing such changes.

Star search that does not move the cursor

The * operator will find and navigate to the next instance of the word under the cursor. The visual-star-search plugin expands that paradigm to visual selections.

But in certain instances one may want to search for the word under the cursor or the current visual selection but not move to the next instance. For instance one may wish to only highlight matches.

These g* mappings will behave like * whilst keeping the cursor where it currently is.

nnoremap <silent> g* :let @/='\<'.expand('<cword>').'\>'<CR>
xnoremap <silent> g* "sy:let @/=@s<CR>

Complete a line with Control-x Control-l

Sometimes in insert mode you may wish to repeat an existing full line; line completion is up to the task:

Control-x Control-l

The above is a little difficult to type, I prefer to use the following mapping:

inoremap <C-l> <C-x><C-l>

One only need type Control-l whilst in insert mode to complete the current line by repeating an existing line.

Dictionary complete current word with Control-x Control-k

Complete the current word with dictionary completion whilst in insert mode:

Control-x Control-k

This requires that the dictionary option be set, for Unix-based systems I recommend:

set dictionary=/usr/share/dict/words

If that key combination is a little difficult to type, then it may be worthwhile to set the following mapping:

inoremap <C-d> <C-x><C-k>

One only need type Control-d (d for dictionary) whilst in insert mode to complete the current word by using dictionary-based completion.

Repeat last visual selection with gv

Simply enter gv to reselect the last visual selection.

Launch browser with gx command

Launch a browser window by moving the cursor, in normal mode, into a URL text area and simply enter the gx command. I only recently came across this nice tip, I wish I knew this years ago!

Delete all lines containing pattern

Use the global command with the delete option to remove all lines that contain the desired pattern.

:g/<<some-pattern>>/d

Delete all lines not containing pattern

Use the vglobal command to achieve the opposite effect, that is to delete all lines not containing the pattern:

:v/<<some-pattern>>/d

Vim as a sed replacement

A Vim script file can be used as a poor man’s sed replacement.

Create a file with the desired operations. For example this file, do.vim, will substitute new for old:

%s/old/new
wq

Then use the -es option of Vim to execute the Vim script.

This example will execute the above do.vim script over all Ruby files in the current directory tree:

vim -es **/*.rb < do.vim

If one uses Bash, please enable globstar in your ~/.bashrc file via the shopt -s globstar statement. For more details please read Bash shell tweaks & tips.

Better wrapping with breakindent

The relatively new breakindent indent option is an excellent way to wrap long code lines. When set, long lines will wrap with an indentation thus preserving the clean indented look of code.

Note, a modern version of Vim, and Neovim, will be required. All the more reason to upgrade!

Set the following options to wrap long lines with indentation:

set breakindent
set breakindentopt=shift:2
set showbreak=\\\\\

UPDATE (MAR 2019): When using a modern font, such as Iosevka, then I recommend using a bent-arrow glyph as the showbreak:

set showbreak=

Smarter j and k navigation

When any form of wrapping is in effect, I recommend breakindent as noted above, then it is natural to convert the j and k movement commands from strict linewise movements to onscreen display line movements via the gj and gk commands. However, when preceded with a count, useful when relativenumber is in effect, then we want to go back to strict linewise movements.

The following mapping achieves both aims, display line movements unless preceded by a count:

nnoremap <expr> j v:count ? 'j' : 'gj'
nnoremap <expr> k v:count ? 'k' : 'gk'

UPDATE: In the comments p1xelHer0 suggested this enhancement:

nnoremap <expr> j v:count ? (v:count > 5 ? "m'" . v:count : '') . 'j' : 'gj'
nnoremap <expr> k v:count ? (v:count > 5 ? "m'" . v:count : '') . 'k' : 'gk'

Similar to the first version except this version will automatically save movements larger than 5 lines to the jumplist. Use Ctrl-o/Ctrl-i to navigate backwards and forwards through the jumplist. Yes, a nice enhancement, thanks p1xelHer0.

Set infercase

Most folks set ignorecase when searching. However, that option does not play nicely with completion which will then completely ignore case. Set the infercase option for smarter completions that will be case aware:

set infercase

Enable wildmenu and wildmode

The wildmenu option makes setting an option, or opening new files via :e, a breeze with TAB expansion.

I recommend these options.

set wildmenu
set wildmode=full

For example, once set you can quickly tab complete an option via:

:set auto<<TAB>>

The TAB invocation will then list the available options in the status line, use TAB again to quickly scroll through the options (left and right arrows also work). Note, when opening a file via :e use up and down arrows to enter or leave directories.

Fast alternate buffer switching

Hit Control-6 to switch to the previously edited file, hit Control-6 again to switch forward to the original file.

Use this handy key sequence to toggle between the last two buffers.

Fast buffer switching via the wildmenu

Expanding on the previous tips, use wildmenu to quickly switch buffers.

set wildcharm=<Tab>
nnoremap <Leader><Tab> :buffer<Space><Tab>

Hitting Leader + TAB in normal mode will list the open buffers in the status line via wildmenu, use TAB or arrow-keys to navigate the choices and then hit Enter to select the desired buffer.

Note, do NOT use raw TAB as the normal-mode mapping, as I initially did, since it will break Control-i inward navigation.

Make dot work over visual line selections

By default, the . repeat operator does not work on visual selections.

Add this mapping in your vimrc file to enable a simple form of dot repetition over visual line selections.

xnoremap . :norm.<CR>

It is recommended that only simple operations that start from the beginning of a line be dot repeated. For example ct= (change up until =) or 5dw (delete the first five words of the line) are good candidates for visual dot repetition.

Execute a macro over visual line selections

Somewhat related to the previous tip is having the ability to run a macro only over a visual line selection.

I use the qq command to record a macro into the q register. I then setup the following Q mapping:

xnoremap Q :'<,'>:normal @q<CR>

Typing Q with a visual line selection in effect will execute the q macro over just the selected lines.

Automatically equalize splits when Vim is resized

It is an annoyance to have to manually equalize Vim splits that have been munged by some type of resize event, for example zooming in and out a tmux pane.

The following autocmd will take care of split equalization for you:

autocmd VimResized * wincmd =

Autoread

Add the following snippet to your vimrc to enable functional autoread behaviour in terminal Vim:

set autoread

augroup autoRead
    autocmd!
    autocmd CursorHold * silent! checktime
augroup END

Autoread will automatically update an open buffer if it has been changed outside the current edit session, usually by an external program.

A natural companion for this tip is the autosaving vim-auto_save plugin. For more details please refer to the appropriate section of the Vim Plugins I Like post.

Improve scroll performance

Add the following snippet to your vimrc to improve scroll performance for certain file types:

augroup syntaxSyncMinLines
    autocmd!
    autocmd Syntax * syntax sync minlines=2000
augroup END

Computing syntax highlight information on the fly whilst scrolling can be slow in Vim. How much of a file Vim highlight’s is determined by the filetype in use. For some languages the value of minlines is small hence Vim can occasionally stutter whilst scrolling.

The above setting can markedly improve scroll performance for certain file types. Also, I have yet to encounter a detriment with this setting on modern machines.