Skip to content
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
446 lines (334 sloc) 15.2 KB

A VIM Notebook

Theory, tips, and tricks after reading Drew Neil's Practical VIM & An unordered list of tips picked up along the way

Beginners: if you need to force yourself to stop using the arrow keys:

nnoremap <Left> :echo "Use h"<CR>
nnoremap <Right> :echo "Use l"<CR>
nnoremap <Up> :echo "Use k"<CR>
nnoremap <Down> :echo "Use j"<CR>

Global yanking with the OS clipboard:

set clipboard=unnamed

Added to .vimrc will make Vim yank to the clipboard, to be pasted anywhere.

Working with registers: 

  1. If you keep yanking code and messing it up somehow with d or x, you can always use the yank dedicated register "0p will tell vim to "put from yank register and not from the unnamed one"
  2. You can also cut to a specific register: "adw will delete a word to the "a" register.
  3. Last thing - you can interact in the same way with the OS clipboard! using  "+{command} will tell vim to do the command to/from the clipboard register.

Commenting out code:

Can be done with:

  1. get into vertical-block mode by using ctrl-V. 2. mark the lines you want to modify. 3. shift-I # space esc the best way to add a prefix to lines. adding a suffix also works if after ctrl-V you use $ and replace shift-I with shift-A

  2. using the super awesome Vim-Commentary plugin by tpope.


Vim uses * to search the current word on normal mode, but when using visual mode you'd expect it would search your highlighted text, but it doesn't, fix it!

" Operating on a complete search match
xnoremap * :<C-u>call <SID>VSetSearch()<CR>/<C-R>=@/<CR><CR>
xnoremap # :<C-u>call <SID>VSetSearch()<CR>?<C-R>=@/<CR><CR>
Some search tips:
1. You can specify search direction in VIM; use `/` to search forward and `?` to search backwards (`n` and `N` will iterate over results backwards too)
2. Highlight search results with setting `set hlsearch`
3. After using the highlight, it can be a annoying if it stays, disable using `:noh` or take it another step and map to something like `Ctrl+l`: `nnoremap <C-l> :noh<CR>`
4. Find number of search matches (without a plugin): `:%s///gn` (telling vim to search and replace without contents makes it use the last search and then tells it to count.
5. Want to position cursor on the *end* of the search result instead of the beginning? use `e`: 
`/searchphrase/e`. Forgot and already searched? use `//e` - becomes useful when you'd like to operate on phrases that can be part of words.
6. Ignore / force search case sensitivity: use `/c` to ignore case, and `/C` to force it. This overrides the setting of `ignorecase`
function s:VSetSearch()
 let temp = @s
 norm! gv"sy
 let @/ = '\V' . substitute(escape(@s, '/\'), '\n', '\\n', 'g')
 let @s = temp

Now, on visual mode, mark a text and press * to search forward, and # to do the same backwards.

Search your VIM command history!

: then <Ctrl-f> you get a history list you can browse If you want to vim-search it just use / and search

Find case insensitive results

set ignorecase if you want vim to

Better VIM scroll 

(inspired by DAS talks about moving in large chunks, I added the scroll feel):

nnoremap <C-j> 10jzz
nnoremap <C-k> 10kzz

Maps moving up or down with Ctrl pressed, to a bigger motion (10) while keeping the line in the center of the screen (zz). This gives a better "scroll" feel.


Mapping shell commands is useful, e.g for running tests, this is something usually set right in the session and not as a permanent setting. Something like this: nmap tt :! crystal spec <enter> Maps tt in normal mode to run crystal spec and press enter.

Using the black hole register:

If you yank a text and right before pasting it you use d or c to remove the text you want replaced you need to go back and yank it again. One solution is to mark v the unwanted text and then p on it. BUT - using the black hole register is a good option too -  If you want to remove text whether using x/s/c/d use "_ before " is sending your yanked / deleted text into a register, by providing "_ it goes to /dev/null, allowing you to keep working with the unnamed register without changing it in the process.


Incrementing numbers in vim: Ctrl-a Decrementing numbers in vim: Ctrl-x Very useful in macros where you want to programmatically run through lists and increment numbers.

Applying macros on multiple files (programmatically with NERDTree)

Using NERDTree is a convenient way to browse through files if you're not familiar with the project (if you are, use If you have a macro running on a file, and you'd like to run it on many of them, you can integrate commands jumping to nerdtree and opening the next file: <macro-commands>^Whj^M^Wl Where ^W being ctrl-W to switch to window selector, h moving left to the file browser, j moving down to the next file on the list, ^M is Enter which opens the file, and again ^Wl is ctrl-W and l moving to the right window again. Running this combination in a loop can apply the same <macro-commands> on a list of files with relative ease.

Going directly to your last opened file and last location of the cursor!

Open Vim and hit


Editing a read only file and forgot to use sudo?

You don't need to leave VIM!

:w !sudo tee %

Of course this can be aliased to anything e.g sudowrite

Delete a line without storing it in the unnamed register

Added this to my .vimrc: nnoremap DD ""dd . I find it useful when many times I want to delete before pasting.

See the file name you're working on:



Mark your lines and :sort But you can also: Sort and remove duplications with unique flag: :sort u Sort in reverse: :sort! Numeric sort: :sort n More at

See your tabs and spaces

:set list

Useful for yaml and indentation based structures debugging :set nolist to disable listchars has different ways of highlighting tabs spaces etc. Try set listchars=tab:..,trail:_,extends:>,precedes:<,nbsp:~ and then insert a tab (when :set list is on) and see the changes

Delete trailing white space on save

func! DeleteTrailingWS()
 exe "normal mz"
 exe "normal `z"
au BufWrite * silent call DeleteTrailingWS()

Reloading .vimrc while working:

:so %

Solving merge conflicts with vim fugitive(!)

When in a merge conflict try :Gdiff (or :Gvdiff to open vertically) You'll get a 3-way split panes, the middle one is the working file and on the sides the two options: HEAD or target branch, which can be referred to by buffer names //2 and //3 (:ls will show all buffer full names). You now have two options:

  1. When standing in the working copy you can diffget <buffer name/number>
  2. Go to other pane and diffput <buffer name/number> I mapped the process to ease the flow like that:
" Open a vertical diff
nnoremap <leader>gd :Gvdiff<CR>
" Get the left pane changes
nnoremap gdh :diffget //2<CR>
" Get the right pane changes
nnoremap gdl :diffget //3<CR>
" Stage the changes (will automatically close all buffers leaving you in the center)
nnoremap <leader>gw :Gwrite<CR>


go while browsing will open a file without changing the context gi will do the same in a split ? will transform the tree pane to the help will all options

Vim Terminal (Vim8 / neovim)

Open a terminal with :terminal While using the Terminal go to normal mode with CTRL-W N or  CTRL-\ CTRL-N Kill the session with CTRL-W CTRL-C

Solving merge conflicts with vim fugitive(!):

When in a merge conflict try :Gdiff (or :Gvdiff to open vertically) You’ll get a 3-way split panes, the middle one is the working file and on the sides the two options: HEAD or target branch, which can be referred to by buffer names //2 and //3 (:ls will show all buffer full names).

You now have two options:

  1. When standing in the working copy you can diffget <buffer name/number>
  2. Go to other pane and diffput <buffer name/number>

I mapped the process to ease the flow like that:

" Open a vertical diff
nnoremap <leader>gd :Gvdiff<CR>
" Get the left pane changes
nnoremap gdh :diffget //2<CR>
" Get the right pane changes
nnoremap gdl :diffget //3<CR>
" Stage the changes (will automatically close all buffers leaving you in the center)
nnoremap <leader>gw :Gwrite<CR>

Another useful shortcut is to jump to the next hunk of changes with [c or ]c Really nice and visible way to solve conflicts!

I've turned this into a blog post

Zooming into split panes

Natively you can enlarge a pane by <C-w> _ for horizontal and <C-w> | for vertical ones. I added a script that uses these like Tmux’s zoom-in feature:

" Pane Zoom
func! PaneZoom()
  res 100
  vert res 1000
nnoremap <C-w>z :call PaneZoom()<CR>

Now I can <C-w> z anywhere. (Revert the split to be even with <C-w> =)

The meaning of life:

:help 42

Utilize search and replace

Putting aside the :%s/<text>/<replace text>/g option and its interactive version :%s/<text>/<replace text>/gc, you can use the gn tool for quick find and replace:

  1. Search for your pattern e.g /const
  2. Use cgn which basically edits the next match cg and them jumps to the next one n
  3. Then you can simply use . to repeat the last command on as many matches you want.

Movement and surrounding

w jumps one word, if you have svc.cluster.local these are 3 words. But if you use W vim will address word as the characters between 2 spaces, so that svc.cluster.local becomes one. Why is it useful? First - better movement around VIM, another example is trying to wrap with parentheses for example, where I can use ysiW (using tpope/vim-surround) to include exactly what I want

Save all buffers (and panes):


Search code in a project!

Using fzf you can :Ag to search for any string which immediately shows up results. BUT better yet, you can send the word under the cursor to it and populate results to a preview pane

Utilizing Ag the silver searcher

This is the mapping that sends the result to ag with preview:

nnoremap <leader>F
  \ :call fzf#vim#ag('', fzf#vim#with_preview({'options': ['--query', expand('<cword>')]}))<cr>

Tabs with sessions

  1. Open any number of tabs you wish to work with
  2. Type :mksession header-files-work.vim and hit enter
  3. Your current session of open tabs will be stored in a file header-files-work.vim
  4. Close all tabs and Vim
  5. Either start vim with your session using : vim -S header-files-work.vim or open vim with any other file and enter command mode to type: :source header-files-work.vim and BOOM!

Save As

If you work on a file and would like to save it with a new name (like save as) you can:

  1. :w <NAME> would write an external file and let you keep working on the original one
  2. :sav <NAME> would write an external file and change your current buffer to the new one as well (a more likely scenario)


Sometimes it’s visibly comfortable to fold pieces of code. z is the controller here, when combined with f it folds the motion added to it (zf'm will fold until the marker “m”). Most intuitive way is to have a visual block marked and just zf to fold. za toggles the created fold on and off. zd deletes a fold.

These can be applied automatically with other modes of folding: :setlocal foldmethod=indent for example will create basic folding under indentation levels of code. More helpful docs:

zf#j creates a fold from the cursor down # lines.
zf/ string creates a fold from the cursor to string .
zj moves the cursor to the next fold.
zk moves the cursor to the previous fold.
za toggle a fold at the cursor.
zo opens a fold at the cursor.
zO opens all folds at the cursor.
zc closes a fold under cursor.
zm increases the foldlevel by one.
zM closes all open folds.
zr decreases the foldlevel by one.
zR decreases the foldlevel to zero -- all folds will be open.
zd deletes the fold at the cursor.
zE deletes all folds.
[z move to start of open fold.
]z move to end of open fold.

Vim marks

An awesome way to jump between sections when editing a file (

  1. ma marks a line as a (or any other letter)
  2. Jump to the mark with 'a
  3. TIP: ' is always a marker of your previous location, so by '' you can always jump back (kinda like cd - in bash)

I looked for a way to see these markers visible (when you have more than 2-3 its hard to remember them), so there’s a nice plugin

Join two lines

If you have a line broken to many parts or just want to join two lines into 1 try Shift + j

Small surrounding Tip

In vim surround when you wrap with { or [ you always get an unwanted space (e.g { $var }) to lose the spaces, use the closing brace (})

Spanning Vim splits

You can send a vim split to span full screen with J: If you have two splits side by side, and you open another one it stays on the focused side when creating it. To make it span both original splits on the bottom: <CR-W>J

Syntax refresh when Vim messes it up

Refresh syntax highlight when it gets messed up in long files : :syntax sync fromstart

Open your current file (e.g. in the browser)

:!open % (macOS)
:!xdg-open % (Linux)

so if you’re editing an html file for example it goes straight to the browser


If you visually mark a text and press u it turns the text to lowercase completely.

Remove last search result highlighting

Probably not that exciting, but stopping a highlight of a current search, I used to search for a non existing string until today like /sss which is dumb.. I just added a map of nnoremap ss :noh<CR> which for me ss is something I was used to do and also an abbreviation of “stop search”


Vim has an internal spell check (:help spell).

  1. Set it with :setlocal spell spelllang=en_us
  2. Run through the marked bad words with [s / ]s
  3. Use zg to mark a word as “good” (“add to dictionary”)
  4. zw will mark an untagged word as “bad”

Some more options like file-local words for spelling, undo options etc can be found in the help section.

Insert mode quick editting

If you’re in insert mode and want to use a normal mode command without completely changing mode, you can Ctrl + o which lets you send a single normal command without leaving.

Consider this: I’m in insert mode and want to delete a word, Option 1: esc + dw + i Option 2: Ctrl + o + dw

30% less strokes :sl

You can’t perform that action at this time.