published on

Vim Tips

Tips parsed from Reddit weekly tip threads.

I'm not sure how well known this is, but when I was first shown it blew my mind. Create a directory in ~/.vim named undodir then put this in your .vimrc and you'll be able to undo changes to a file after closing and reopening:

set undofile
set undodir=~/.vim/undodir

I just realized I never saw this little thing anywhere. Basically I wanted to have extra indication where focus is. So this little snippet will show cursorline only in Vim window with focus, this includes application windows as well.

augroup highlight_follows_focus
    autocmd!
    autocmd WinEnter * set cursorline
    autocmd WinLeave * set nocursorline
augroup END

augroup highligh_follows_vim
    autocmd!
    autocmd FocusGained * set cursorline
    autocmd FocusLost * set nocursorline
augroup END

Let me start with this snippet that automatically opens the location/quickfix window whenever a location/quickfix command is executed and there's a valid location/quickfix list.

augroup autoquickfix
    autocmd!
    autocmd QuickFixCmdPost [^l]* cwindow
    autocmd QuickFixCmdPost    l* lwindow
augroup END

select whatever's just been pasted, or read into the buffer via :r! etc.

nnoremap gV `[V`]

[I list all the lines where the word under the cursor occurs.

edit to add: ]I will list from the current cursor position rather than the start of the file = [I


Make arrow keys do something useful, resize the viewports accordingly.

nnoremap <Left> :vertical resize +2<CR>
nnoremap <Right> :vertical resize -2<CR>
nnoremap <Up> :resize -2<CR>
nnoremap <Down> :resize +2<CR>

Poor man's TagBar:

:g/func/#<CR>

Slightly more powerful variants (lists includes too, may require a bit of setup):

:ilist /func<CR>
:dlist /<CR>

If you're at the beginning of a line you can type ci" and it'll automatically go to first set of "" quotes (same with ''). Once I realized this it saved me soo much time. Instead of going f"ci" you can just type ci" right away.


Not sure how popular this is but pressing in command line mode (triggered by :) or in "search mode" (/ or ?) gives you a history of previous commands and search in a small window known as command line window which is a regular Vim buffer with all the editing commands (including plugins like surround.vim) and auto-complete. You can search for previous searches, and just press on the line to use that as the input. Super useful if you make a mistake or want to construct some long command/search or just want to use a previous search/command.

Edit1: As many have pointed out, you can also invoke this via q:, q? and q: which I forgot to mention. However, since I invoke it usually mid-command, I find more useful.

Edit: A small gif for visual benefit - https://asciinema.org/a/0gutmej8i4ccnaovym5uqmwwu


If you've searched for something and want to replace it, you don't have to type it in again in the command-line:

s//text_to_replace_with

instead of

s/prev_searched_text/text_to_replace_with

A very nice feature of vim is its integration with terminal programs to modify the current buffer.

For this you put :%! <command-name> in command mode

For example,

:%! sort -k2 will sort the buffer based on column 2

:%! column -t will format the text in columns - useful when working with tabular data

:%! markdown will change the current markdown file to html

You get the idea.


Sometimes you want to paste text without leaving insert mode. In insert mode, ctrl+r followed by a register number will paste that register. This is also useful when you're in vim's command-line. I particularly use this with vim's "%" register, which stores the current file name, when I want to save a backup version of a file: ":w ctrl+r %" expands to ":w ctrl+r myfile.txt" and then I just append ".backup" to it and save.


I personally advocate for learning vim's basic commands. A very useful normal command is gv. At the same time is so simple! Here, straight from :help gv:

gv  Start Visual mode with the same area as the previous
    area and the same mode.
    In Visual mode the current and the previous Visual
    area are exchanged.
    After using "p" or "P" in Visual mode the text that
    was put will be selected.

Vim already has lots of very useful text-objects but we are pigs… we always want more. Here are 24:

for char in [ '_', '.', ':', ',', ';', '<bar>', '/', '<bslash>', '*', '+', '%', '`' ]
    execute 'xnoremap i' . char . ' :<C-u>normal! T' . char . 'vt' . char . '<CR>'
    execute 'onoremap i' . char . ' :normal vi' . char . '<CR>'
    execute 'xnoremap a' . char . ' :<C-u>normal! F' . char . 'vf' . char . '<CR>'
    execute 'onoremap a' . char . ' :normal va' . char . '<CR>'
endfor

starstar! ** -- this is magic! It can be used in tons of places. Want to open a file in your path and think you need ctrl-p or some other plugin, you don't!

:e **/*part<tab>

if you are lazy (as I am), you can just map it

nnoremap <leader>e :e **/*

Quick wildcard searching over your whole tree, wow!

But, it gets even better, opening files 1 by 1 is exhausting! Lets open all the markdown files in our tree!

:arga **/*.md

But... damnit, now all those files are in buffers, how will I ever switch to the one I want...

You can use wildcards with full path

:b */*<tab>

But, I am too lazy to type that each time, so a mapping to the secure again (and this one lists them by default with C-d)

nnoremap <leader>b :b */*<C-d>

https://github.com/tpope/vim-abolish

Lets you use :S/cat/dog/ to turn:

    cat CAT Cat
        into
    dog DOG Dog

Use abbreviations to add computable but common items like timestamps:

" Insert mode ddate adds date stamp
iab <expr> ddate strftime("%b %d - %a")

In insertmode typing ddate will result with: Apr 11 - Mon using the string formating above. I use this all the time for daily notes.


The new-ish :cdo command. Combine this with vimgrep or other searching plugins and you can quickly apply mods to each match result that is listed in the quickfix. Combine :cdo with normal mode and you have some serious magic. For example, Piggybacking on the other suggestion about vim-abolish, I recently used this:

 :cdo normal e2wcrs 

to quickly do camel to snake conversion for a set of match lines in my qf (where my qf match lines were based on searching for "foo::")

 foo::SomeClass

to

foo::some_class

The "crs" is provided by vim-abolish to do the camel-->snake conversion.


To paste text into the current buffer without using "set paste" at a *nix system:

:r!cat

Press Ctrl+d when done. Simple, but useful.


Everyone probs already knows, this, but you can do

:mks ~/mysession.vim

then

vim -S ~/mysession.vim

When opening vim to load all your tabs, files and windows back just how they were.

Literally only just found this out:c


While in insert mode, Ctrl-d decreases indentation, Ctrl-t increases it, and Ctrl-m (also Ctrl-j) inserts a newline. Maybe these are well known; I wasn't aware of them until recently and find them quite convenient.

Note from editor Ctrl-f "fixes" indentation in insert mode too.


In the replacement section of the :s command, & corresponds to the whole matched pattern. It can be uppercased with \U and lowercased with \L: :s#aaaaaaa#\U&#


Weekly text-objects:

" line text-objects
xnoremap il g_o0
omap il :<C-u>normal vil<CR>
xnoremap al $o0
omap al :<C-u>normal val<CR>

" buffer text-object
xnoremap i% GoggV
omap i% :<C-u>normal vi%<CR>

:b# -- stupidly simple but useful, lets you swap back and forth between buffers, I use it bound to q for quick switch.

    nnoremap <leader>q :b#<cr>

Yank and paste a line without moving the cursor.

:10t.

will paste line 10 to current position. Relative numbers works as well.

:-10t.

I find this easier to do than jumping around in the file, yanking and jumping back.


As you all probably know, we can replace occurrences of a word in a file using the :%s/search/replace/g Ex command. Something I learned recently is that we can actually tell Vim to prompt us for a confirmation before replacing each word using :%s/search/replace/gc (notice the c at the end for confirm).

Here's a screenshot to illustrate the feature. We can press y to replace the current occurrence of the word or n to to skip it. Simple.

We can also press a to replace all words, q to abort (quit), and l to change the current occurrence of the word and then immediately abort (as in, this is the last change you want to make before aborting).

Just like in Normal mode, we can scroll up and down using Control + Y and Control + E respectively.

I can't say this feature revolutionized my workflow but I was giddy when I learned about it 😆!

Update: Here's a practical demonstration of this feature in action (that is a link to a specific timestamp namely, 14:13)


For an easy "repeat macro over all selected lines" you can use the following mapping:

vnoremap @ :norm@

I got this one from /u/cherryberryterry a couple of weeks ago.

This is for people who use relative line numbers:

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

It adds motions like 25j and 30k to the jump list, so you can cycle through them with control-o and control-i.


If you edit a lot of text files (for example, if you use Vim to write your emails), you might want to tweak your formatlistpat to recognize and format lists better. I have the following in ~/.vim/after/ftplugin/text.vim:

setlocal formatlistpat=^\\s*[\\[({]\\\?\\([0-9]\\+\\\|[iIvVxXlLcCdDmM]\\+\\\|[a-zA-Z]\\)[\\]:.)}]\\s\\+\\\|^\\s*[-+o*]\\s\\+

It will recognize lists starting with numbers, letters, roman numerals, symbols like +, -, etc. As an example of how this works, say I have the following list:

1. Et dolor quia qui omnis inventore. Quos voluptatem quae sit sint dicta qui beatae totam. Quibusdam qui occaecati voluptatibus id.

XXV. Nemo aut est quo optio et culpa rerum. Non totam aut sint placeat non. Et repellat eveniet molestiae. Eius dolorem molestiae non asperiores suscipit dolor dolorem.

(a) Eaque dolor qui perferendis. Impedit consequatur sit qui. Qui molestiae ut nemo magni quo veritatis voluptatem. Ut aliquid tenetur recusandae necessitatibus quas est nesciunt. Fugiat quis aut est impedit hic architecto. Ut et maiores soluta est ipsa cum autem itaque.

+ Ut dignissimos molestiae ducimus rerum dolores aut. Dolorum alias molestiae nam sed laudantium. Culpa dolorum iste quo harum ipsa quidem. Qui ea ratione et.

Now, if I use gq to format these lines, I get:

1. Et dolor quia qui omnis inventore. Quos voluptatem quae sit sint
   dicta qui beatae totam. Quibusdam qui occaecati voluptatibus id.

XXV. Nemo aut est quo optio et culpa rerum. Non totam aut sint
     placeat non. Et repellat eveniet molestiae. Eius dolorem
     molestiae non asperiores suscipit dolor dolorem.

(a) Eaque dolor qui perferendis. Impedit consequatur sit qui. Qui
    molestiae ut nemo magni quo veritatis voluptatem. Ut aliquid
    tenetur recusandae necessitatibus quas est nesciunt. Fugiat quis
    aut est impedit hic architecto. Ut et maiores soluta est ipsa
    cum autem itaque.

+ Ut dignissimos molestiae ducimus rerum dolores aut. Dolorum alias
  molestiae nam sed laudantium. Culpa dolorum iste quo harum ipsa
  quidem. Qui ea ratione et.

Note that you'll also need the n flag in your formatoptions to tell Vim that numbered lists should be recognized (:help fo-table).


Add a # comment block:

nnoremap <leader>b :center 80<CR>hhv0r#A<SPACE><ESC>40A#<ESC>d80<BAR>YppVr#kk.

Run this on a word, like "INIT", and you will get:

################################################################################
##################################### INIT #####################################
################################################################################

Have to give credit to Doorknob: http://vi.stackexchange.com/a/421/307


Tired of .swp files being strewn all around your folders? Create a directory and point to it in your .vimrc . For instance, create ~/.swp_files folder. Then in your .vimrc add:

set backupdir=~/.swp_files
set directory=~/.swp_files

Much more convenient to have a single directory to check...and I usually end up just deleting .swp files anyways. I don't think they've once saved me from a crash/power outage, but this is a safer solution than set noswapfile.

EDIT: Forgot to mention yesterday, if you share your .vimrc across users (notably, root), then you will get errors if you don't create/link the .swp_files directory for that user. It's not catastrophic or anything, but you may wind up without .swp files for that user (but you will see an error message when opening vim).


If you want your cursor to stay in the middle of the screen when browsing long files, you can set scroll on with

set so=999

Now the page will move around while the cursor stays centered.


:cq

exits vim immediately like :qa!, but with nonzero exit code. This is useful if you are calling vim from some other application and you want to abort. I often use this to abort git commits, moreutil's vidir and the like. Unfortunately, it doesn't work with all applications.

And don't get tempted to just use it as a quicker :qa!, that's just wrong :)


If you've ever wanted to wrap some text foo with some other, arbitrary text using c{motion} (change) followed by " (paste the last deleted text), then you've probably noticed that you cannot repeat the action on a different text bar using .. For example: Starting with

foo
bar

if you put the cursor on foo and do ciw("), and then put the cursor on bar and hit ., you'll end up with

(foo)
(foo)

instead of

(foo)
(bar)

The solution is to do " instead of ". (See :h i_CTRL-R_CTRL-O, and related, for more info.) (Of course, for this particular example, it would be better to use a plugin, like vim-surround, but the general point is still helpful, I hope.)


Do you want an actionable TOC of your markdown file?

:g/^#/#

Do you want an actionable overview of your code?

:g/def /#
:g/func/#

I'm not a textwrap user and I like to keep most of my lines under the 80 character mark. So when I (for example) paste a long line into vim, I can use gq to split it into multiple lines with each line being at most 80 chars wide. For one line, gql will break it up for me. I believe gq also works. Note: you need to have set textwidth=80 set for this to work.


You can use 'suffixes' to give certain filenames less priority in the wildmenu. I set suffixes+=.sty,.bst,*.cls while editing LaTeX since I rarely edit anything other than the LaTeX or BiBTeX source. There's also 'wildignore' to completely ignore certain files (one downside of this is that file name completions in the insert mode using CTRL-X CTRL-F will also be affected).

Editors note: set suffixesadd+=.js,.ml is useful too


Read the user manual. I don't mean that with any snark, it is a sincere tip. The Vim documentation is two parts, (1) user manual, (2) reference manual. The user manual is well worth reading, and far too few people do it.

You are going to spend hundreds, maybe even thousands of hours using Vim -- take the time to read the basic manual and your time will be far better spent.


While visually selecting a block, press 'o' to switch to the other end of the block. This lets you adjust either the starting or ending positions of the block until you're ready to issue a command. That is, you can expand or contract either end of the visual block, you're not stuck changing just one end.


:browse oldfiles or :bro ol to edit a file from your recently opened files.


Thanks to the Netrw plugin (default in vim) you can do ssh remote work out of the box:

:e dav://machine[:port]/path                  uses cadaver 
:e fetch://[user@]machine/path                uses fetch 
:e ftp://[user@]machine[[:#]port]/path        uses ftp   autodetects <.netrc> 
:e http://[user@]machine/path                 uses http  uses wget 
:e rcp://[user@]machine/path                  uses rcp 
:e rsync://[user@]machine[:port]/path         uses rsync 
:e scp://[user@]machine[[:#]port]/path        uses scp 
:e sftp://[user@]machine/path                 uses sftp 

Of course you can use whichever of the above to just open a remote folder directly. If you don't know netrw very well but use some file tree plugin (like NERDTree) I really recommend checking out the plugin as it might be enough for most people and it's already built-in.


Executing >3j will shift the current line and the three lines below it to the right. Subsequently, . will repeat the shift over the same lines as expected. On the other hand, executing and repeating >3k will not operate over the same lines because the cursor is moved to the first shifted line after the shift operation. Here's a gif of the issue: http://i.imgur.com/SRUpnEP.gif (>3k.. is demonstrated with and without the below "fix") onoremap k 'V' . v:count1 . 'k' . v:operator


Use | to chain commands.

E.g. :sp|e test will open a horizontal split containing a session to edit test

Editors note: :w|so % is useful when editing vimrc


Use g to rearrange lines. The classic example is that you can reverse lines with :5,25g/^/m4, but you can also swap lines around out like :'<,'>g/LOG/m-2 which turns:

doTheThing();
LOG.message('doing the thing');
doSomethingElse();
LOG.message('doing something else');

into:

LOG.message('doing the thing');
doTheThing();
LOG.message('doing something else');
doSomethingElse();

Changing the m (move) to a t (copy) inserts lines like with :'<,'>g/LOG/t-2|+2s/doing/did/ (note you can use | to string commands together in the context of the :g), which, given the same input, yields:

LOG.message('doing the thing');
doTheThing();
LOG.message('did the thing');
LOG.message('doing something else');
doSomethingElse();
LOG.message('did something else');

If you want to use the current word in the command window, e.g. to do a substitution, you can press Ctrl-R followed by Ctrl-W.

Example: :%s/<C-r><C-w>/newword/


This is a simple one but I really like it because I don't like holding Shift for LONG_VARIABLE_NAMES.

" make last typed word uppercase
inoremap <c-u> <esc>viwUea
inoremap <Plug>UpCase <Esc>hgUaweA
imap ;u <Plug>UpCase

I have this: :H topic will open an 80 columns vertical split containing the help page. The complete argument ensures that the completion works exactly like the :h command.

command! -complete=help -nargs=1 H call VerticalHelp(<f-args>)
function! VerticalHelp(topic)
    execute "vertical botright help " . a:topic
    execute "vertical resize 78"
endfunction

Edit: autocompletion* thanks /u/_ntnn


I can't believe I just found this, where have you been all my life!

" make . work with visually selected lines
vnoremap . :norm.<CR>

Most people probably now that you can use gv to reselect the last visual selection or pasted texted.

But in visual mode gv can cycle between the two last visual selections!


Make directory(ies) if they don't exist when creating a new buffer/file.

function! <SID>AutoMkdir() abort
    let l:dir = expand('<afile>:p:h')
    let l:file = expand('<afile>:t')
    if !isdirectory(l:dir)
        call mkdir(l:dir, 'p')
        silent execute 'bw ' . l:dir . '/' . l:file
        silent execute 'e ' . l:dir . '/' . l:file
    endif
endfunction

augroup AutoMkdir
    autocmd!
    autocmd BufWritePre,FileWritePre,BufNewFile *
        \ call <SID>AutoMkdir()
augroup END

Same thing here but with user input.


First, how to create a scratch window:

command! SC vnew | setlocal nobuflisted buftype=nofile bufhidden=wipe noswapfile

Second, how to execute arbitrary vimscript from a buffer:

  • Yank what you want to execute.
  • :@"

And now you have a nice low-rent vimscript playground.


:set cc=80 will show a vertical ruler at column 80.


syntime on was a revelation to me. I turns out to be a great way to identify errant plugins and macros that bog down your vim.