Things About Vim I Wish I Knew Earlier

I have been using Vim as my primary (and only) text editor since 2009. Over the years, I have discovered many useful things about Vim that I wish I knew earlier because they dramatically improved my text-editing efficiency. In this post, I would like to share the most important ones with you.

HJKL

Usually, the first advice a new Vim user gets is “Do not use arrow keys, use h/j/k/l instead!” For a very long time, I ignored this advice because it seemed absurd to me. Why on earth would I want to do that? One day, however, I decided to give it a try. And since then, I have never looked back. It took me many days to get used to it, but when I did, I was amazed what I was missing. The h/j/k/l keys are directly on the home row, so you do not have to move your hands that much. This not only helped me to move faster, but it also caused my hands to be more relaxed. I believe this was one of the reasons my wrist pain went away. The other one was a wrist pad that I put under my hands.

If you want to learn to use h/j/k/l instead of arrow keys, I suggest to either disable the arrow keys or remap them to something more useful. Another benefit of not using them is that you will get used to better movement commands, like f/F/t/T, w/W/b/B, 0/^/$, or searching. Use of such commands enables you to move faster than you would by simply holding arrow keys or h/j/k/l. Holding a key in Vim is an antipattern.

Relative Line Numbers

As a lot of users, I used absolute line numbers because, well, all editors use them. One day, however, I stumbled upon this article, which got me thinking. What if relative numbers are better? I decided to give them a shot. And, as Ben said in this video, I realized that my life with absolute line numbers was a lie. Relative numbers are the correct way of using Vim.

To start using relative numbers, I recommend to put the following combination of settings into your .vimrc:

set number
set relativenumber

Together, they make Vim show the absolute number for the current line, and relative numbers for other lines.

Next, let me illustrate the usefulness of relative numbers. In Vim, many commands can be prefixed by a number. For example, to move 10 lines below the current line, you can use 10j. To move 4 lines above the current line, use 4k. To indent the current line and 2 lines below, use >2j. You get the idea. With relative numbers, you simply see how far the other lines are. If you were using absolute numbers, you would need to mentally subtract two (possible large) numbers to get the number of lines between them. The use of relative numbers alleviates the need to do math in your head.

You can still use absolute numbers, though. For example, when you compile a source file and the compiler tells you that there is a syntax error on line 943, you can get to this line by pressing either 943G or :943<CR>.

One very useful feature of Vim is that you can set it to visually wrap long lines. Many users, including me, remap j/k to gj/gk to make j/k move by virtual lines instead of physical lines. This remapping, however, breaks the counting functionality described above. To remedy this, use the following mapping instead, based on this stackoverflow.com post:

noremap <silent> <expr> j (v:count == 0 ? 'gj' : 'j')
noremap <silent> <expr> k (v:count == 0 ? 'gk' : 'k')

This makes gj/gk move by virtual lines when used without a count, and by physical lines when used with a count. This is perfect in tandem with relative numbers.

Vim and Tmux

For a very long time, I used GVim instead of terminal Vim. It looks better, integrates better with window environments, etc. Then, however, I discovered Tmux, which drastically improved my workflow. In short, it allows you to have separate terminal sessions inside a single terminal window. Inside each session, you can have multiple windows, and each window can be separated into multiple panes. Take a look at this screenshot to get an idea.

The use of Tmux created a dilemma for me. How to use it with GVim? TMux is a terminal application while GVim is a stand-alone GUI application. Fortunately, I was able to set up Vim to look nearly identically to GVim. The most useful plugin was CSApprox. It made my GVim-only color scheme work transparently in terminal Vim. See my .vimrc for other settings that helped me in the transition to terminal Vim.

The greatest advantage the Tmux+Vim combo brings is that it enables you to perform nearly all the development in a terminal. This allows you, for example, to ssh into a remote server and do your development in there. You may then seamlessly resume working from other places, even from a different computer, because you do your work on a remote server through SSH. Another benefit is that development servers are usually much faster than notebooks or desktop PCs. Depending on your company’s budget, you may even be able to develop on a machine with a massive number of cores and memory. Such an experience will make you never want to go back to your work PC.

If you have never used TMux, I urge you to at least give it a try. It is an awesome tool. To help you get started, I have described my Tmux configuration here. Check it out, you may find some nice tweaks in there that will help you to speed up your workflow. For example, the article describes a way of making the navigation between Vim and Tmux seamless.

Operations and Motions

A unique feature of Vim is that it feels like you can speak with it through a language. For example, there are verbs like d (delete) or y (yank), nouns like b (brackets) or s (sentence), and adverbs like i (inside/inner) or a (a/all/around). You can freely combine them, so dib will delete all text inside brackets and yap will copy (yank) the current paragraph. After you learn this simple language, you will never have to remember what to do to delete a paragraph: you simply type dap, without thinking about. The reason why many people think Vim is difficult is that they are not used to think this way about their editor.

The even better part is that the Vim’s language is extensible. Indeed, you can create you own verbs and nouns (operations and motions/text objects in Vim parlance). There are some very useful plugins that I use on a daily base, which I describe next.

Operations

  • tcomment-vim – Adds a code commenting/uncommenting operation: gc. For example, to comment-out the current paragraph, you use gcap. Think of it as go comment a paragraph. To comment the current line and 5 lines below it, you use gc5j (thanks, relative numbers!).
  • vim-sort-motion – Adds a sorting operation: gs. For example, to sort Python imports under the cursor, you use gsip. Think of it as go sort inside paragraph.
  • ReplaceWithRegister – Adds a replacement operation: gr. It allows you to replace text without overwriting other text stored in registers. For example, to replace the current word with the contents of the default register, you use griw. Think of it as go replace inner word. The gr operation also works with the dot command (., repeats the last action), which makes it even better. It is one of my favorite operations.

Motions/Text Objects

  • targets.vim – Adds a lot of useful text objects. For example, daa deletes an argument of a function call, and ci$ changes text between dollar signs (very useful in LaTeX). Another feature of this plugin is that it allows you to use text objects even when you are not directly inside them. For example, to delete everything inside the nearest double quotes, you use di" (delete inside quotes).
  • vim-indent-object – Allows you apply an operation to the current indentation level. For example, to shift the current block to the left, you use <ii (left-shift inside indent). This is very useful in Python, which uses whitespace instead of curly braces to structure the code.
  • vim-surround – Allows you to operate on surroundings. For example, cs"' changes surrounding double quotes with single quotes, and dsb deletes the surrounding brackets.

Of course, you can combine the operations and motions from the above-mentioned plugins. For example, to sort items of a Python list where each item is on a separate line, you use gsii (go sort inside indent).

Some simple text objects can be defined even without plugins. For example, to operate on an entire file, put the following line to your .vimrc:

onoremap af :<C-u>normal! ggVG<CR>

Then, to copy the contents of the entire file (more precisely, the entire buffer), use yaf (yank a file).

Fuzzy Finding

To open a file in Vim, you can use edit or tabnew to open it in the current buffer or a new tab, respectively. A disadvantage of this way is that it is very slow when you are inside of a large project with many nested directories and files. For example, to edit file frontend/scripts/js/view/viewer.js, you have to type the entire path. Hmm. There has to be a better way…

Fortunately, there is! You can use a fuzzy searcher, such as Command-T. It allows you edit the above file by simply starting the searcher (like pressing ,t), typing fsviewer (f and s are parts of the path to the file), and opening it either in the current window, in a split, or in a new tab. Once you know the hierarchy or file names in your project, the use of a fuzzy searcher massively increases the speed of file openings.

Grepping

Often, you want to list all files that contain the given phrase or word, like the name of a function. When I started using Vim, I would switch to the terminal and type gvim `grep -r FUNC | cut -d: -f1` to open the found files in GVim. However, why leaving Vim to do such a simple task?

You can either use Vim’s built-in grep command, or use a higher-level plugin, such as vim-grepper. It allows you to select a search tool (grep, git grep, or even ag) and choose how to present the results. For example, you can set up a mapping ,/ to search in all files in a project for a certain phrase, or ,* to perform a project-wide search for the word under the cursor. After the search is done, it opens a window with the found files, and you can open them directly in the Vim instance you work in.

Vim As an External Editor

When you use a text editor, it is important to try to use it as much as possible. After all, why learning another editor, like textarea forms on the web, when you already know Vim? Using Vim everywhere allows you to utilize its strengths and features even on places you previously would not imagine.

For example, for Firefox, there is a great add-on called It’s All Text!, which enables you to edit textarea elements in Vim. This facilitates editing wiki pages, writing blog posts, creating bug reports, etc. by using your favorite editor. For Vim, this means you can utilize syntax highlighting, spell checking, dictionary completion, snippets, or any other feature.

For Thunderbird, there is also the It’s All Text! add-on. It helps you to write emails faster and with more comfort than you would get by using the built-in “editor”.

Many terminal applications also support Vim as an external editor. For example, you may set up Midnight Commander to open files in Vim. Another useful use of Vim is when writing commit messages in version control systems, like in Git. Are you tired of typos? Enable spell checking in Vim. Would you like to be able to complete words from the commited diff by using Ctrl+n? Commit with the –verbose parameter. It will copy the whole diff into Vim so you can use completion, like for function names. Writing commit messages has never been easier.

The last Firefox add-on I would like to mention is Vimperator. It makes the browser behave like Vim, which allows you to utilize Vim principles even when browsing the web. For example, you can quickly open links, copy text, or make marks on web pages and returning to them later, without a need to touch your mouse. Vimperator even lets you configure Firefox in a .vimrc-like file. Here is mine.

Running External Commands

Imagine you are writing Python code and you would like to run unit tests in a file that you are currently editing. Or imagine you are writing a LaTeX document and would like to compile it to see the results. One way is to move to a terminal and run the tests or compilation from there. This is, however, not very efficient because it causes a delay in your workflow.

A better way is to utilize the fact that you can run external commands directly from Vim. For example, the following autocommand allows you to simply press F9 to save and run the currently edited unit tests for your Python code:

au FileType python nnoremap <buffer> <F9> :wa<CR>:!clear; nosetests %<CR>

Another very useful feature when running Vim inside Tmux is the possibility to run external commands in a given Tmux pane or window. For example, you may divide your current Tmux window into two panes, edit files in one pane, and run tests in the second pane, without a need to switch between them. To give you a taste of my workflow, I like to do the following. I use a dual monitor setup. For a project, I open two terminals with two Tmux sessions. I put one of them to the first monitor and the other one on the second monitor. In the first Tmux session, I edit code, and when I want to run tests, generate documentation, or check the code via a lint-like tool, I send a command from Vim to the second Tmux session, so the tests run on the second monitor. This allows me to edit code and tests in a split window on the first monitor and run git commands, tests, etc. on the second monitor. To toggle focus between the monitors, I have configured Fluxbox to use Ctrl+Alt+h/l. See my configuration files. And the best part? This works even when I run Tmux over SSH on a remote server.

Configuration Options and Mappings

Vim is highly extensible. You can configure it anyway you like, and add mappings and commands for actions you use frequently. I have grown my .vimrc over the years to contain many useful settings and mappings. Below, you can find some of them. However, I encourage you to take a look at my .vimrc to see all of them.

" Quickly select the text that was just pasted. This allows you to, e.g.,
" indent it after pasting.
noremap gV `[v`]
" Stay in visual mode when indenting. You will never have to run gv after
" performing an indentation.
vnoremap < <gv
vnoremap > >gv
" Make Y yank everything from the cursor to the end of the line. This makes Y
" act more like C or D because by default, Y yanks the current line (i.e. the
" same as yy).
noremap Y y$
" Make Ctrl-e jump to the end of the current line in the insert mode. This is
" handy when you are in the middle of a line and would like to go to its end
" without switching to the normal mode.
inoremap <C-e> <C-o>$
" In command mode (i.e. after pressing ':'), expand %% to the path of the current
" buffer. This allows you to easily open files from the same directory as the
" currently opened file.
cnoremap <expr> %% getcmdtype() == ':' ? expand('%:h').'/' : '%%'
" Allows you to easily replace the current word and all its occurrences.
nnoremap <Leader>rc :%s/\<<C-r><C-w>\>/
vnoremap <Leader>rc y:%s/<C-r>"/
" Allows you to easily change the current word and all occurrences to something
" else. The difference between this and the previous mapping is that the mapping
" below pre-fills the current word for you to change.
nnoremap <Leader>cc :%s/\<<C-r><C-w>\>/<C-r><C-w>
vnoremap <Leader>cc y:%s/<C-r>"/<C-r>"
" Replace tabs with four spaces. Make sure that there is a tab character between
" the first pair of slashes when you copy this mapping into your .vimrc!
nnoremap <Leader>rts :%s/	/    /g<CR>
" Remove ANSI color escape codes for the edited file. This is handy when
" piping colored text into Vim.
nnoremap <Leader>rac :%s/<C-v><Esc>\[\(\d\{1,2}\(;\d\{1,2}\)\{0,2\}\)\?[m\|K]//g<CR>

Argument Wrapping

Last, but certainly not least, I would like to mention the vim-argwrap plugin. It allows you to quickly wrap and unwrap function arguments, lists, and dictionaries. For example, it allows you to convert

decompiler = Decompiler(api_url=args.api_url, api_key=args.api_key)

to

decompiler = Decompiler(
    api_url=args.api_url,
    api_key=args.api_key
)

and vice versa with a single mapping.

Discussion

Apart from comments below, you can also discuss this post at /r/programming, r/linux, /r/vim, and /r/sysadmin.

40 Comments

  1. Relative numbers for navigation are an anti-pattern, for two reasons.

    The first is that movement by relative numbers in non-idempotent. If you make a mistake with relative numbers, you are moved to an indeterminate location and must reorient yourself and recalculate the now-different move operation.

    Movement by absolute numbers is idempotent. If you fat-finger your move, you may repeat the same movement command without having to think about it and end up in your intended location instantaneously. The absolute number movement command is “[line number]G”.

    The second reason is that to communicate with others about file locations or use the output of tools which report file locations, you need an absolute line number, not a relative one. There are plugins dedicated just to making the transition between relative and absolute numbering simple (e.g. numbertoggle).

    If you aren’t seduced into using relative numbering in the first place, there is no need to spend time or effort switching modes nor installing plugins. :)

    Of course, most people think relative numbering is the bees knees and who am I to criticize. I did too until I learned the more natural command which was there all along.

    Reply
    • I don’t believe idempotence is a valid measurement of usefulness here, since the cognitive and typing load from encountering different and many-digited line-numbers can’t begin to match the consistency of a 1 – 2 digit movement. Cite cases of cursor movement on a moment-to-moment basis, and you’ll be hard-pressed to argue that the occasions you move further than ninety-nine lines from your current position outnumber smaller, relative movements.

      With relative numbering, you get to keep your “[line number]G”. Relative motion has no effect on being able to use that, or “:[line number]” for that matter. Really want that line number? Ctrl-G (Command-G) does the trick.

      For other users, I suggest you try out other plugins to your liking, like vim-easymotion. Targeted motion to points *within* lines is so convenent. Along with relative numbering, ctrlp.vim (Command-T’s more convenient successor), and Ag.vim have made getting up to speed with any codebase more sublime than any other editing workflow I’ve encountered.

      Reply
      • > With relative numbering, you get to keep your “[line number]G”. Relative motion has no effect on being able to use that, or “:[line number]” for that matter.

        Relative numbers remove the ability to readily _see_ absolute line numbers, making it impractical/impossible to know what to do.

        > Really want that line number? Ctrl-G (Command-G) does the trick.

        Assuming you’re already on that line. That royally defeats most purposes. (Also assuming you meant g<C-g>)

        Reply
    • It’s only idempotent in the case where your edit doesn’t add or remove lines (unless you undo, but in that case there is no difference anyway).

      The line numbers issue is real though. In Visual Studio, I have a plugin that shows both which I miss in my regular VIM, but I find that what I mostly need is to go to a specific line which obviously works as usual.

      Reply
    • Although I think anti-pattern may be a little strong – I’d put it in the different strokes category, I recently switched from relative line numbers to absolute.

      An important facet of what you said that people new to vim may not realize – an extension of what you said about G movement, is that you can perform an operation in between the number and the G. 5dG or 5yG

      But a big reason I changed over was because the relative line numbering doesn’t correctly account for folding. If you have a section folded it only displays in the line numbers as one row.

      Reply
  2. Replacing tabs with spaces is much better done with

    :retab

    as it will rely on `expandtab` setting (i.e.: so you don’t ruin your Makefiles which REQUIRE tabs) and value of `shiftwidth`

    Reply
    • Yes, :retab is very useful and generally a better choice. However, sometimes, I want to just replace tabs with spaces, disregarding the currently set expandtab etc. For example, before copy&pasting something to another place, like to a web form or to IRC/Jabber. This is where my mapping comes handy.

      Reply
  3. honestly, holding a key is sometimes more convenient. mindblowing anti-anti-pattern on this – go to your OS keyboard prefs and set key repeat rate to highest speed. feel the power!

    Reply
    • Yop, sometimes, it is more convenient, and increasing keyboard repeat rate is useful in this regard. However, I often find myself in a situation where I wonder whether this five-second key holding was really worth it. Usually, the answer is negative because I would be better off using a more direct command.

      Reply
  4. For those who love Vim and use Visual Studio, check out the VsVim extension. Works beautifully inside Visual Studio.

    Reply
    • I am using it, it is not as good as Vim per se but still better than bare IDE editor (not all features are implemented and there is plenty of conflict with other IDE shortcuts, especially if you are using ReSharper). There is other plugin for VS called viemu, I heard that it is better unfortunately it is not free.

      It is interesting that almost any product I use has some form of vim suport e.g. Chrome has Vimium (I using it currently), IntelliJ Idea has vim plugin, and even Evince supports HJKL :)

      Reply
  5. Love Vim; however, using extensions is cheating. Resorting to an editor extensions means the designers failed.

    Reply
    • Your reasoning doesn’t make sense. Why would I want the editor to be opinionated about every thing that I want it to do? I would WAY rather have good extensibility and install what I want, rather than have everything included. How would they possibly implement all these ideas as one small team anyways?

      I would recommend that you rethink this comment.

      Reply
    • I know that you probably feel attacked right now, but your comment doesn’t make sense. vim is supposed to be extensible. The vim team creates the core, and makes a plugin API for other developers to add in functionality that the designers didn’t add/didn’t expect. Some of these end up getting adopted into the core vim project (e.g. spell checking), some stay as extensions (vim-gnupg), but in neither case are extensions “cheating” nor designers failing.

      Reply
    • That makes no sense. Making a decently powerful, yet very extensible core that others can build on should be the ideal if anything for something like vim. Many people will want many different features so even if core vim had all of the possible features and simply required people to turn them on or off, that would be an incredibly bloated system. The idea of starting with a capable editor like vanilla vim and expanding it as you see fit is exactly how it should be.

      Reply
  6. A valuable use I found is “in editor” sed pattern substitutions.
    1,$s/string/string/g
    Using wildcard patterns like \(area to find\) substitute with \1
    And the [A-z|0-9l ] pattern matching with he sed type substitute , I always use

    Reply
    • Thanks for the tips. I once tried ranger, but did not like it very much. Maybe I will try it again in the future. As for nerdcommenter vs tcomment-vim, I actually used nerdcommenter for a long time. Then, however, I discovered tcomment-vim, which integrates better with Vim. For example, you can use its gc operation to comment any range you want, without the need to visually select it. This is much more straightforward and faster.

      Reply
    • I’ve found that instead of using the Vimperator for firefox, its much easier and more beginner friendly to use VimFx. It gives you all the pleasantries of vim in firefox but also keeps the defaults of Firefox so it makes the transition to the vim-like behaviour much smoother.

      Reply
  7. I think there needs to be a “5-minutes a day for a week” set of tutorials in vim.

    I’m a sysadmin who, as much as he loves the command line, spends a lot of time in web interfaces as well. I develop Puppet code locally on Atom, but edit remotely in vim (vim-enhanced is always installed, thanks to Puppet ;)

    Nice article, I learned a lot! (and the Merriweather font is great, I hadn’t really seen it before)

    Reply
  8. Nice article. I tried using vim when I discovered Linux. I could never get the hang of it. For some reason my brain blocked that I had to press i to enter insert mode. So I used emacs for a year or 2. Then one day I couldn’t remember the 4-5 key combo to search/replace and I’d had enough. I read through all the inbuilt vim tutorials.

    Learned about ed and vi. Soaked it all up. Today, 10 years later I can slice and dice text like nobody’s business! I love having vim in a shell or a gvim instance. I wrote my own .vimrc, .exrc, .gvimrc. The time spent was well worth the effort.

    I do use the arrow keys unless I’m creating a macro. I am a lefty don’t discriminate against us. A lot of the things you mentioned in your article I do differently. Still, I’m efficient and you’re efficient. That’s what makes vim so great, its ability to be customized.

    Reply
  9. For years I used to be a console-only vim user, heavily using split views both in vim and screen, but I got fed up select/copy/paste issues between it and the rest of my desktop applications, especially when having a view with line numbers and/or other stuff, often shown in the view in addition to the code.

    I ended up switching to terminator, which allows splitting a terminal window in a similar way, and gradually I migrated from vim to SublimeText’s vim mode, which fixes a lot of the issues I had in my initial setup.

    Reply
  10. Thanks for this article! I’ve been using vim forever and missed most of the tips that you listed here. I must now practice all of them, especially some of the operations and text objects you mentioned.

    Reply
  11. I discovered this article on Reddit recently :)

    I like this kind of article, as there is always something interesting to learn about vim.

    I’m trying to stop using the arrows to move around, but I’m having some troubles in insert mode, because my use case is often something like:

    I start by typing the opening and closing quotes/parenthesis/brackets, so I’m sure not to forget them (e.g. def my_function():).
    I then press the left arrow key to get back inside my quotes/parenthesis/brackets, and type what I need (e.g. def my_function(a, b):).

    Sometimes, I also notice a typo when writing sentences, and in that case too, arrows are very useful in insert mode.

    What would you recommend for these use cases if you don’t use arrows?

    Thanks :)

    Reply
    • Hi Pierre!

      When writing brackets, quotes etc., there are essentially two options. The first one is to write just the opening bracket, then the contents of the brackets, and only after that the closing bracket. This is what I do. The second option is to use a plugin that automatically inserts the matching closing bracket whenever you write an opening bracket. There are many plugins that do that (e.g. auto-pair or delimitMate). Either way, you will not have to use arrow keys in the insert mode, which will result in less movement of your hands on the keyboard.

      Upon finding a typo in a sentence, it depends on the position of the typo. When it is in the last word you just typed, you can use Ctrl-w to delete the word and start again. It is often faster to just rewrite the word instead of going to the position of the typo and correcting it. When the typo is deeper inside of the sentence, you can switch into the normal mode, move there as fast as you can (e.g. b, B, f, or ?), and fix the typo. Sometimes, to fix it, you don’t even have to go into the insert mode. Indeed, sometimes x (deletes a character) or r (replaces a character) suffice.

      Reply
  12. For years and years, I refused to use vim because it was so complicated and used nano instead. These days, when I am forced to use nano for some reason, I tend to save and exit with :wq

    Reply

Leave a Comment.