May 21, 2017

Vim Plugins I Like

UPDATED FEBRUARY 2019

Vim gains much functionality through the inclusion of plugins.

This post contains a curated set of my favourite Vim plugins. Note, many of these plugins are the usual suspects. I also fully acknowledge that there are numerous useful plugins beyond those discussed here, hence, I do encourage Vim users to explore, test and discuss those plugins they appreciate.

The full set of plugins and mappings I use are available in my vimrc.

vim-plug

The first decision a Vim user, looking to enter the plugin world, must decide is which plugin manager to use. There are many to choose from: Vundle, Pathogen, vim-plug, Dein and Vim 8 native package management to name a few.

Each will do the job, but for simplicity, performance and features the vim-plug plugin manager is the one I use.

Note, if you are a Vundle user, like I was, transferring over to vim-plug is simple, just follow this advice.

All snippets for the remainder of this post will use vim-plug notation.

vim-moonfly-colors

Plug 'bluz71/vim-moonfly-colors'

Some self-advertising, I have written my own Vim colorscheme named moonfly.

The moonfly colorscheme is yet another dark theme, but unlike all other dark themes this one is my dark theme. That means it has been tuned to my particular tastes. Whether those tastes match up with anyone else’s taste will be in the eye of the beholder.

vim-moonfly-statusline

Plug 'bluz71/vim-moonfly-statusline'

Personally I am not a fan of heavy statusline plugins like powerline and airline.

Instead, I have created a simple matching statusline for the moonfly color scheme named appropriately moonfly-statusline. It provides all relevant information I find useful whilst also clearly indicating which mode you are in: normal, insert, replace or visual modes.

vim-one

Plug 'rakr/vim-one'

The vim-one colorscheme advertises itself as a light and dark Vim colorscheme, shamelessly stolen from Atom. It is basically a port of Atom’s One theme. When I get bored with my own moonfly color scheme I change over to One.

Plug 'nelstrom/vim-visual-star-search'

The vim-visual-star-search plugin allows * and # searches to occur on the current visual selection.

vim-lion

Plug 'tommcdo/vim-lion'
let g:lion_squeeze_spaces = 1

The vim-lion plugin is used to align text around a chosen character.

I find it easiest to select a visual region and then invoke gl<character> to re-align text around a chosen character (which will often be equals).

For example, gl= will convert this:

i = 5;
username = 'tommcdo';
stuff = [1, 2, 3];

into this:

i        = 5;
username = 'tommcdo';
stuff    = [1, 2, 3];

The alternative vim-easy-align and tabular plugins can also align text.

targets.vim

Plug 'wellle/targets.vim'

The Targets.vim plugin provides additional text objects. The highest compliment I can provide a plugin is to say that it feels like a natural part of Vim itself, this tremendous plugin exhibits that nice characteristic.

Vim’s text objects allow for easy selection and operation on regions of text.

Common text object operations, provided by default with Vim, include:

The Targets.vim plugin provides additional text object separators such as: *, |, =, and _ to name a few.

Examples of those separators:

The *-based text object is handy for changing emphasized text in Mardown files whilst _-based text objects are useful for language that use snake_case such as Ruby and Elixir.

I also appreciate the comma smarts this plugin provides.

For example, given this text with the cursor positioned inside second:

foobar(first, second, third)

The operation da, will delete the comma before second but not the one trailing it. Most of the time this is the preferred result when dealing with source code.

I highly recommend this excellent plugin to all Vim users.

vim-indent-object

Plug 'michaeljsmith/vim-indent-object'

The vim-indent-object plugin adds yet another text object to Vim, this one based on the indentation of the current cursor line. This new text object is invoked by either i or I. Some examples:

These indent based text objects are handy because they are language agnostic, they work just as well for Python code as they do for JavaScript code.

clever-f

Plug 'rhysd/clever-f.vim'
let g:clever_f_across_no_line = 1
let g:clever_f_fix_key_direction = 1
let g:clever_f_timeout_ms = 3000

The clever-f plugin makes f, F, t and T movements more informative and convenient.

The more informative aspect is achieved by clever-f highlighting all the matches for the chosen movement.

The more convenient aspect is achieved by simply using the f and F characters to navigate forward and backward through the matches unlike Vim’s inconvenient and hard to remember defaults of ; and ,. In my case I map the leader key to , and I map ; as a duplicate of :, hence those repeat characters are not even available.

vim-wordmotion

Plug 'chaoren/vim-wordmotion'

The wordmotion plugin expands Vim’s definition of a word. This plugin will take into account programming-related camel and snake-case (and other unusal word definitions) and allow navigation, using w and b, within such words.

This plugin will not suit everyone, but for certain language, such as Ruby which uses both camel and snake-case, it has proven invaluable in practice.

CtrlP

Plug 'ctrlpvim/ctrlp.vim'
let g:ctrlp_user_command = 'fd --type f --color=never "" %s'
let g:ctrlp_use_caching = 0
let g:ctrlp_match_window_reversed = 0

The CtrlP plugin is a fuzzy file finder.

I recommend installing fd and configuring CtrlP to use it and to not cache results. fd is a highly performant file find utility and it mates very well with CtrlP as noted here and here.

Also, here are some handy mappings for Ruby on Rails or Elixir/Phoneix developers:

if filereadable('config/routes.rb')
    " This looks like a Rails app.
    nnoremap <localleader>ec :CtrlP app/controllers<CR>
    nnoremap <localleader>eh :CtrlP app/helpers<CR>
    nnoremap <localleader>em :CtrlP app/models<CR>
    nnoremap <localleader>es :CtrlP spec<CR>
    nnoremap <localleader>ev :CtrlP app/views<CR>
elseif filereadable('web/router.ex')
    " This looks like an Elixir/Phoenix app.
    nnoremap <localleader>ec :CtrlP web/controllers<CR>
    nnoremap <localleader>em :CtrlP web/models<CR>
    nnoremap <localleader>et :CtrlP test<CR>
    nnoremap <localleader>ev :CtrlP web/views<CR>
    nnoremap <localleader>ex :CtrlP web/templates<CR>
endif

Those mappings, which can also easily be extended for other frameworks, provide quick and direct access to model/view/controller files (among others) from their standard directories.

Lastly, be aware that quite a few Vim users are now using fzf.vim for their fuzzy file finding needs instead of CtrlP.

UPDATE (DEC 2018): I now use fzf.vim instead of CtrlP as noted here.

NERDTree

Plug 'scrooloose/nerdtree', { 'on': ['NERDTreeToggle', 'NERDTreeFind'] }
let NERDTreeHijackNetrw = 0
noremap <silent> <leader>n :NERDTreeToggle<CR> <C-w>=
noremap <silent> <leader>f :NERDTreeFind<CR> <C-w>=

Most Vim users are aware of NERDTree. Not much explanation is needed, NERDTree is a simple file explorer that open up on the left-hand side of a Vim workspace.

I use <leader>n to toggle NERDTree whilst also equalizing all existing splits. I also have a <leader>f mapping to open NERDTree and reveal the current buffer in the file tree.

One inconvenience is that NERDTree, by default, will not refresh itself when one enters the file-tree window. For instance, it won’t display new files not created within NERDTree unless a manual refresh is executed. This can be overcome with the following auto-refreshing snippet:

function! NERDTreeRefresh()
    if &filetype == "nerdtree"
        silent exe substitute(mapcheck("R"), "<CR>", "", "")
    endif
endfunction

autocmd BufEnter * call NERDTreeRefresh()

NERDTree Git Plugin

Plug 'Xuyuanp/nerdtree-git-plugin', { 'on': 'NERDTreeToggle' }
let g:NERDTreeUpdateOnCursorHold = 0
let g:NERDTreeUpdateOnWrite      = 0

The NERDTree Git plugin adds git status indicators in the NERDTree window.

I find the visual information provided by this plugin to be genuinely useful. I recommend all NERDTree users, who manage code via Git repositories, to give this plugin a try.

VimCompletesMe

Plug 'ajh17/VimCompletesMe'
autocmd FileType css,ruby,scss let b:vcm_tab_complete = "omni"

The VimCompletesMe plugin uses the TAB character, whilst in insert mode, to carry out completions using Vim’s various built-in completions. The plugin itself usually determines the appropriate type of completion, be it keyword, file or omni completion, based on the current context.

Note, the above listed autocmd will result in omni completion being used as the primary completion choice for the listed language types. Feel free to vary for your needs.

If VimCompletesMe doesn’t float your boat then rest assured there are many completion choices available ranging from the simple to the advanced, some even support Language Server Protocol. Such Vim plugin completion choices include:

I would classify VimCompletesMe, supertab, MUcomplete and vim-simple-complete as being towards the simple/light end of completion spectrum whilst the others I would classify as being towards the advanced, sometimes heavy end of the spectrum. Personally, I recommend starting simple then moving up only when necessary.

UltiSnips

Plug 'SirVer/ultisnips'
let g:UltiSnipsExpandTrigger       = "<C-j>"
let g:UltiSnipsJumpForwardTrigger  = "<C-j>"
let g:UltiSnipsJumpBackwardTrigger = "<C-k>"

The UltiSnips plugin allows one to easily insert predefined text segments in the current buffer.

The following Vimcasts are an excellent introduction to UltiSnips, please view:

By default, UltiSnips will use the TAB character to expand a snippet, however that will conflict with the VimCompletesMe plugin. I recommend the use of Control-j, as defined above, to expand and then navigate forward through a snippet’s tabstops and Control-k to navigate back up through the tabstops.

The small set of snippets I use are declared here.

indentLine

Plug 'Yggdroot/indentLine'
let g:indentLine_faster = 1
let g:indentLine_setConceal = 0

The indentLine plugin is used to display indentation guide markers as often seen in Sublime and Atom editors. This is a simple and useful visual aid, though indentLine is not quite as slick-looking as the guide markers in Sublime and Atom.

Note, the two let options listed should be set for maximum performance. The default settings for the indentLine plugin will have a negative impact on Vim scroll performance.

vim-grepper

Plug 'mhinz/vim-grepper'
let g:grepper = {}
let g:grepper.tools = ["rg"]
runtime autoload/grepper.vim
let g:grepper.jump = 1
nnoremap <leader>g :GrepperRg<Space>
nnoremap gr :Grepper -cword -noprompt<CR>
xmap gr <plug>(GrepperOperator)

The vim-grepper plugin is a simple Vim interface to various text search utilities including my favourite, the ripgrep search utility.

For more details about ripgrep please read this post. Short summary, ripgrep is fast and is repository aware (aka it will skip ignores).

Upon execution Grepper search matches will populate Vim’s quickfix list allowing easy navigation through the matches. Note, when run on a modern version of Vim or Neovim the search will be executed asynchronously.

I have a simple mapping <leader>gr to invoke an interactive Grepper search. The normal mode mapping gr will invoke a search on the word under the cursor whilst the visual mode mapping gr will invoke a search on the current visual selection.

vim-polyglot

Plug 'sheerun/vim-polyglot'

The vim-polyglot plugin is a comprehensive language pack collection for Vim. This plugin consolidates all the best standalone language plugins, such as vim-ruby and vim-go, into one master-plugin. And best of all this plugin will configure all language scripts to only load when required.

ALE

Plug 'w0rp/ale'
let g:ale_linters = {
\  'css':        ['csslint'],
\  'javascript': ['eslint'],
\  'json':       ['jsonlint'],
\  'markdown':   ['mdl'],
\  'ruby':       ['rubocop'],
\  'scss':       ['sasslint'],
\  'yaml':       ['yamllint']
\}
let g:ale_linters_explicit = 1
let g:ale_open_list = 1

The ALE plugin is used to asynchronously run language linters, or compilers, within modern versions of Vim.

ALE ships with configurations for most common languages, such as JavaScript and Ruby to name a few, hence very little configuration is usually required. Note, the tools that ALE uses, such as eslint or rubocop, will need to be installed on the host, the ALE plugin will not install the underlying lint tools.

I like to use a mapping, <leader>l, to invoke ALE when I desire, others however prefer to have ALE lint code is it is being written (the default behaviour). My ALE settings:

let g:ale_lint_on_enter = 0
let g:ale_lint_on_filetype_changed = 0
let g:ale_lint_on_insert_leave = 0
let g:ale_lint_on_save = 0
let g:ale_lint_on_text_changed = 'never'
nmap <localleader>l <Plug>(ale_lint)
nmap <localleader><BS> <Plug>(ale_reset_buffer)

Note, use the :ALEInfo command to display runtime information per the current file type, use it when you need to debug any ALE linting issues.

ALE is not the only asynchronous linting solution for Vim, an alternative is Neomake which does much the same job.

Developers should integrate linting into their development flow, either one of these two modern plugins should satisfy that need.

vim-test

Plug 'janko-m/vim-test'
let test#javascript#jest#executable = 'CI=true yarn test --colors'
nnoremap <silent> <leader>tt :TestNearest<CR>
nnoremap <silent> <leader>tf :TestFile<CR>
nnoremap <silent> <leader>ts :TestSuite<CR>
nnoremap <silent> <leader>tl :TestLast<CR>
if has("nvim")
    let test#strategy = "neovim"
else
    let test#strategy = "vimterminal"
endif

The vim-test plugin provides a universal interface to various back-end testing frameworks. This plugin allows one to agnostically run tests for different languages and their associated testing frameworks.

Since we are using modern versions of Vim, let’s use the terminal capabilities provided to run tests in a split terminal window.

Note, the following configuration is required to use vim-test with Create React App projects:

let test#javascript#jest#executable = 'CI=true yarn test --colors'

vim-closer

Plug 'rstacruz/vim-closer'

The vim-closer plugin will automatically close (, { and [ after enter is pressed when in insert mode for supported languages such as: C, C++, JavaScript and Go (to name a few). This plugin is a natural companion to the vim-endwise plugin noted below.

vim-auto-save

Plug '907th/vim-auto-save'
let g:auto_save = 1
let g:auto_save_silent = 1
let g:auto_save_events = ["InsertLeave", "TextChanged", "FocusLost"]

The vim-auto-save plugin automatically saves changes to disk without required manual :w invocations. I prefer to automatically save after: normal mode changes (TextChanged), exiting insert mode (InsertLeave) and when focussing away from Vim (FocusLost).

Tim Pope Plugins

A special mention should be given to Tim Pope who has crafted some of Vim’s most useful plugins. He deserves a place in the Vim hall of fame alongside Bram Moolenaar himself.

Abolish

Plug 'tpope/vim-abolish'

The abolish plugin is really a couple plugins in one, those being:

I primarily use the first two.

The abolish plugin can be set to automatically correct text as you type it. An example use is correcting seperate into separate and delimeter into delimiter. It can do this no matter the case and even with pluralization. One sets up these corrections in their own ~/.vim/after/plugin/abolish.vim file.

Here are my abolish corrections:

Abolish {despa,sepe}rat{e,es,ed,ing,ely,ion,ions,or} {despe,sepa}rat{}
Abolish {,in}consistant{,ly}                         {}consistent{}
Abolish lan{gauge,gue,guege,guegae,ague,agueg}       language
Abolish delimeter{,s}                                delimiter{}
Abolish {,non}existan{ce,t}                          {}existen{}
Abolish d{e,i}screp{e,a}nc{y,ies}                    d{i}screp{a}nc{}
Abolish {,un}nec{ce,ces,e}sar{y,ily}                 {}nec{es}sar{}
Abolish persistan{ce,t,tly}                          persisten{}
Abolish {,ir}releven{ce,cy,t,tly}                    {}relevan{}
Abolish cal{a,e}nder{,s}                             cal{e}ndar{}
Abolish reproducable                                 reproducible
Abolish retreive                                     retrieve
Abolish compeletly                                   completely

The abolish plugin can also carry smart substitutions. What is a smart substitution? Such a substitution would intelligently change old to new and Old to New and OLD to NEW in one command. The abolish substitute command does just that and more.

An example abolish substitute:

:%S/facilit{y,ies}/building{,s}/

This plugin does more than I have documented here, please refer to the abolish documentation.

Commentry

Plug 'tpope/vim-commentary'

The vim-commentary plugin is a simple language agnostic commenter. I usually use it with a visual line selection to comment out or uncomment out a block of code with the gc command the plugin provides.

No need to remember what the comment characters are for a certain language, is it // or # or , just gc it.

Fugitive

Plug 'tpope/vim-fugitive'
nnoremap <silent> <leader>B :Gblame<CR>
nnoremap <silent> <leader>G :Gstatus<CR>

The vim-fugitive plugin is a Git wrapper. I do most of my git work at the command line, however I find fugitive’s Gblame command to be supremely useful within Vim.

Endwise

Plug 'tpope/vim-endwise'

The vim-endwise plugin will automatically insert end, in insert mode, to code blocks for languages such as: Ruby, Elixir and Crystal. This plugin is a natural companion to the vim-closer plugin noted above.

Projectionist

The vim-projectionist plugin, primarily, provides infrastructure to navigate around projects. This plugin is effectively the core of the vim-rails plugin extracted into a standalone plugin.

Here is a simple configuration for Elixir/Phoenix projects:

Plug 'tpope/vim-projectionist'
let g:projectionist_heuristics = {
      \  "config/prod.exs": {
      \    "web/controllers/*_controller.ex": {
      \      "type": "controller",
      \      "alternate": "test/controllers/{}_controller_test.exs",
      \    },
      \    "web/models/*.ex": {
      \      "type": "model",
      \      "alternate": "test/models/{}_test.exs",
      \    },
      \    "web/views/*_view.ex": {
      \      "type": "view",
      \      "alternate": "test/views/{}_view_test.exs",
      \    },
      \    "web/templates/*.html.eex": {
      \      "type": "template",
      \      "alternate": "web/views/{dirname|basename}_view.ex"
      \    },
      \    "test/*_test.exs": {
      \      "type": "test",
      \      "alternate": "web/{}.ex",
      \    }
      \  }
      \}
nnoremap <leader>ec :Econtroller<Space>
nnoremap <leader>em :Emodel<Space>
nnoremap <leader>et :Etest<Space>
nnoremap <leader>ev :Eview<Space>
nnoremap <leader>ex :Etemplate<Space>
nnoremap <leader>A  :A<CR>

The above configuration will result in the following commands being created: Econtroller, Emodel, Eview, Etemplate and Etest. Those commands are then mapped for quick access. Hence, <leader>ec <TAB> will list all available controllers, in the status line if wildmenu and wildmode are set appropriately, allowing a developer to quickly go to the controller they wish. The <leader>A mapping provides quick switching to an alternate file, which will usually be the associated test suite for the current file.

Configuring vim-projectionist for other frameworks like react or Ember should not be too difficult.

Setting up this plugin does require a bit of upfront work, but once done, and then used, you will really appreciate the navigation capabilities this plugin provides.

Note, Rails developers should still use vim-rails in preference to vim-projectionist, think of vim-rails as a pre-configured vim-projectionist with a little bit of added sugar on top; also vim-rails and vim-projectionist do happily live side by side.

sleuth

Plug 'tpope/vim-sleuth'

The vim-sleuth plugin automatically adjusts shiftwidth and expandtab intelligently based on the existing indentation within the file or within the directory tree for like files. With this plugin in effect there is little need to manually define indentation settings.

Ragtag

Plug 'tpope/vim-endwise'

The vim-ragtag plugins provides a set of helpers for TAG-based languages such as HTML, XML and JSX.

These are the ragtag helpers I find most handy whilst in insert mode:

Surround

Plug 'tpope/vim-surround'

The vim-surround plugin allows one to add, change or delete surrounding pairs.

What is a surrounding pair? It may be the quote characters or <div> tags or anything else that surrounds some text.

To delete a surrounding pair use d. Here are some examples, the first example will delete double quotes, the second will delete a tag (like <div>) and the third will delete * :

ds"
dst
ds*

To change a surrounding pair use c. Note, you must provide the old and new surround:

cs'"
cs*<div>

To add a surround pair one can visually select the candidate text and enter S followed by the surround character(s) of choice.

It is even possible to add in surrounding pairs whilst in insert mode. Use a single Ctrl-S followed by the surround character(s) to create a surround on the current line. Use a double Ctrl-S-S to spread the surround over multiple lines. Note, in both cases the cursor will be inserted between the surrounding pair. The double Ctrl-S-S is especially useful for inserting curly braces in C/C++/Java/JavaScript type languages.

Note, terminal Vim users need to make sure flow-control is disabled otherwise the above Ctrl-S will lock your terminal. I recommend starting Vim this way to avoid that issue, stty -ixon && vim.

This plugin is a little harder to explain than it is to use, however once you get it you can’t imagine life without it.

Repeat

Plug 'tpope/vim-repeat'

The vim-repeat enhances the . operator to work as one would expect with a number of Vim plugins, most notably the vim-surround plugin noted above.

Unimpaired

Plug 'tpope/vim-unimpaired'

The vim-unimpaired plugin provides a set of mappings for many operations that have natural pairings. A pairing may be: up and down, or forward and backward, set or unset or above and below.

Of the mappings provided by this plugin these are the mappings I use most often:

The full set of mappings is documented here.

The vim-unimpaired plugin negates the need to provide your own custom set of mappings for these types of operation.