How to set up Vim in 2021? (2/3)

Enrique Domínguez
Geek Culture
Published in
19 min readNov 8, 2021

A comprehensive guide to setting up Vim like an IDE Part 2: plugins

This is part of a series aimed at teaching you the vim fundamentals. Part one is about my .vimrc configurations. Part two is all about the plugins. Part three will be about navigating Vim like a pro.

I’m writing these articles for the Vim newbie I once was. But even as of yesterday I’m still learning new things about Vim, so who knows, stay awhile and listen!

It’s plugin time

Plugins everywhere meme
Everywhere indeed

I might as well refer you to the most upvoted comment of this post, but my point is you definitely should try all the plugins and configurations that get your attention. As time goes by, you’ll find yourself deleting more and more plugins as you come to the realization you no longer need them, or even worse, you never really used them in the first place.

This is my selection of plugins, one year into the vimworld. Same as before, if you just wanna look a it and move on, here it is:

I’ll link the configs at the end. Now, let’s break it down.

Plug

First things first: let’s get you a plugin manager. Do you need one? No, not really. You can always manage your plugins by hand, and I’d argue a plugin manager merely simplifies the process of cloning the git repository into the right folder, keeping it updated, cleaning unused plugins, etc. That’s it. Now, do you want one? Yes you do. It’s about quality of life. There are many things I like doing by hand, since I hate handing over control just for the sake of convenience, but there’s nothing magical going on behind the curtains. This is a thing I don’t even wanna think about doing by hand.

Plugin manager updating packages
I mean, look at this… of course you want one.

You can find everything you need to know about plug in their git repo, from installation to automation and commands. I can give you a TL;DR for Vim on unix-like systems. Not Neovim, not Windows. All that info is in their repo.

Open your favorite terminal emulator and curl that mofo:

curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim

Basically you’re creating a folder called autoload/ inside your $HOME/.vim/ directory, and downloading plug.vim from GitHub into that folder.

Then, you only have to add a call plug#begin() and a call plug#end() to your .vimrc and add the plugins you want to install in between those sections. Refer to my gist up above. When you’re done, save, reload and :PlugInstall away.

You can notice I specified a path on the plug#begin() section, like so:

call plug#begin('~/.vim/plugged')

That’s where the plugins will get installed. You can name it whatever you want, but as per the documentation, you should avoid using standard Vim directory names like ‘plugin’.

Oh, and why plug and not, say, vundle? I used vundle at first, then switched to plug because I heard it hasn’t been actively maintained for years. Honestly I never had a problem with vundle. But well, here we are.

Grubvox

Gruvbox is a beautiful theme with a retro look. It was love at first sight and I can’t see myself using any other theme in the foreseeable future.

Gruvbox colors in terminal emulator
I even set my terminal colors to match Gruvbox

Maybe you’re a Dracula person. It doesn’t matter, the logic is all the same, but I’ll be discussing a few tweaks that may or may not be particular to gruvbox so it’s a good idea to keep them in mind. Remember these settings from Part 1?

set termguicolors
let g:gruvbox_italic=1
colorscheme gruvbox
set background=dark
hi Normal guibg=NONE ctermbg=NONE
let g:terminal_ansi_colors = [
\ '#282828', '#cc241d', '#98971a', '#d79921',
\ '#458588', '#b16286', '#689d6a', '#a89984',
\ '#928374', '#fb4934', '#b8bb26', '#fabd2f',
\ '#83a598', '#d3869b', '#8ec07c', '#ebdbb2',
\]

truecolor is a specification for 24-bit color values for your pixels. It amounts to 16,777,216 possible colors; that’s a couple more than the 256 possible colors from 8-bit. You should check if your terminal supports truecolor by running echo $COLORTERM, and performing the tests suggested in this repo. If your terminal supports truecolor, then we can safely set termguicolors in our .vimrc to enable 24-bit colors in Vim. Believe or not, I unknowingly spent almost a year without termguicolors, it felt like I’d been living in a lie all that time.

24-bit color support on and off
It’s night and day

Note that if you’re not using Vim from the terminal but rather from a GUI, you probably don’t need to mingle with the colors.

To enforce italics, you must declare the variablelet g:gruvbox_italic=1 before calling colorscheme gruvbox or else they might not work. background=dark sets gruvbox’s dark theme, as opposed to background=light. Give it a try if you want but my eyes can’t stand it, light themes on text editors bring back painful memories of some old macromedia dreamweaver version.

hi Normal guibg=NONE ctermbg=NONE sets the vim editor and vim terminal backgrounds to none , rendering them transparent, as seen on my screenshots. The slightly translucent background is actually my terminal emulator’s, which is set to 95% opacity.

The array of hex colors assigned to the terminal_ansi_colors variable are the vim terminal colors. Since we enabled termguicolors, for some reason the Vim terminal falls back to your system’s default palette, which is definitely not pretty, so we have to set it by hand. And yeah, in case you didn’t know, you can run a terminal inside vim with :term. Check out :help terminal for more info.

If you didn’t get the termguicolors to work, try :help xterm-true-color where they talk about setting explicitly some settings. If you’re using tmux, you might need to also set it up accordingly.

Check this site out for more themes.

NerdTree

NerdTree file system explorer
NerdTree file system explorer

There’s not much to say here. You need a file system explorer on an IDE. Some might argue you don’t need NerdTree since you can achieve the same results with the native Vim explorer, try :Explore or :Vexplore for a vertical split. Check this article out. Decide for yourself. Meh.

NerdTree has a couple of interesting plugins from which I use absolutely none, but you can get icons for your different file formats like on VSCode, or show Git flags, etc. Were you to give NerdTree a try, you should check out the full list of commands by pressing ? within the tree. The ones I use the most are s to open a file in a vertical split, t to open a file in a new tab, and R to refresh the tree and show changes to the directory made outside of Vim.

Just in case you didn’t know it, you can navigate between splits by hitting control w + direction, w as in window. ^wl goes right, ^wk goes up, and so on. I’m telling you this because not only NerdTree allows you to open splits, but is a split itself: you can navigate back into NedTree with ^wh. The arrows work too. Also, you navigate through tabs with gt and gT to the right and to the left respectively. Yes, Vim support tabs. More on that in Part 3.

nnoremap <C-t> :NERDTreeToggle<CR>
let NERDTreeShowHidden=1
let NERDTreeRespectWildIgnore=1
set wildignore+=*.DS_Store,*.min.*
autocmd BufWinEnter * silent NERDTreeMirror.

The nnoremap remaps ^t (control + t) to toggle the NerdTree on or off. NERDTreeShowHidden does exactly what it sounds like, it shows hidden files; I want to be able to access my .env files from within NerdTree, but not the annoying .DS_Store system files. That’s why I enabled NERDTreeRespectWildIgnore and set the list to include .DS_Store and any minified .min file. You can make that list grow as needed. The last line with the autocmf BufWinEnter is telling Vim to open a copy of your current NerdTree whenever you open a new tab, hence the NERDTreeMirror.

There are many more setting examples in their repo, you should check it out.

Airline

Airline status bar form Vim
Airline in action

Of course, you can always create your own custom status line, like a good self-respecting Vim nerd. Maybe someday I will, but in the meantime…

Airline is a lightweight status line form Vim. It gives you info on the mode (normal, insert, visual, etc.), the file path, the file format, errors, and the cursor position. It can also handle tabs but I never liked the feel of it.

If you want something more system-wide, check out Powerline. It’s not a Vim plugin anymore, it’s written in Python and it integrates with tmux, bash, zsh and several other applications.

You should consider installing and using a Powerline Font or a Nerd Font for the nice symbols to show up in Airline, albeit it works just fine without.

Polyglot

Polyglot is an extensive collection of language packs that allow Vim to correctly highlight syntax. Instead of installing different languages as different plugins, just install polyglot once and be done with it. Only one plugin to update, only one line in your .vimrc, and each language pack is loaded automatically on demand so it won’t affect your startup time. Take a look at the list of supported languages, it’s actually pretty impressive; half of those I’ve never even heard of.

By the way, Polyglot comes with some pre-configured concealing options. Actually not Polyglot, but each individual language package. Look at this and you’ll get it:

You can conceal keywords into single characters

Behind curtains, it’s doing something like this:

syntax match jsFunction function conceal cchar=ƒ
syntax match { tag } { keyword } conceal cchar={ character }

Only it does it with a ternary operator that checks your .vimrc to see if you declared the javascript_conceal_function somewhere, and if you did, it assigns its value to the cchar parameter. So all you really have to do is to actually declare this:

let g:javascript_conceal_function="ƒ"

You have the full javascript list here. Remember polyglot is a collection, so if you want language-specific details, check the list in their repo.

You can set your custom conceals with that pattern. I’ll let you google the specifics. It can also take regex instead of a keyword, and tag is whatever you name that group of concealed characters. Say you want to conceal <= into and >= into , you’ll probably tag them both as jsOperatos for example. But that’s beyond the scope of this article.

Note that this feature might conflict with another plugin, Indentline. We’ll discuss more about concealing when we talk about Indentline down below.

Emmet

Emmet demonstration

If you’ve done some HTML in the past, you probably know emmet. If you haven’t, you’re missing out. Simply put, you must only write one line of instructions and emmet quickly expands it into markup. I’ll leave you the docs here.

It even works with React, automatically inserting className="" instead of class="" and so on. If you’re like me and you always add the .jsx extension to React files as one should, you need to add this to your .vimrc to ensure emmet recognizes the file extension:

let g:user_emmet_settings = {
\ 'javascript' : {
\ 'extends' : 'jsx',
\ },
\}

The only thing to bare in mind is how one expands the emmet-string in Vim. You must hit ^y, , that’s control + y + ,. You can remap the ^y to ^z or whatever combination you like, but the , is always the final trigger.

Commentary

Commenting out sections
Easy

gcc to comment a line, gc to comment visual selection, like on the gif above. Repeat to uncomment.

gc in Normal mode also takes distance + direction parameters, so for example gc5j will comment your line and five below. Now you understand why we set up the relativenumber in our .vimrc.

There’s also the Nerd Commenter, but I haven’t tried it yet.

Surround

Adding and changing surrounding characters with ease
Surround with ease. Ease is the keyword here.

Your bread and butter is going to be, in Normal mode, ysw + character. Think of it as “you surround word” with “something”. Just keep in mind that if you enter an opening or a closing brace, it’s gonna behave differently: ysw} is going to surround your word like this {word} as you would expect, but an opening brace ysw{ will give you { word } with inner spaces. The same goes for [, ], (, and ). The w is a distance parameter, so you totally can do stuff like ys5w' and surround five words in single quotes. In Visual mode, make a selection and surround it directly with capital S + character.

Is your stuff is already surrounded and you want to change or delete it, you cs or ds respectively. Think “change surround this for that” and “delete surrounding character”. For instance, cs"' would change the surrounding double quotes for single quotes, and ds( would delete the surrounding parenthesis altogether. You don’t need to input the distance anymore since you are telling it what to target.

And what about html tags like <p>...</p>? You use the t, t as in tag. For example, yswt li > would surround your word in <li>...</li> tags. When you enter t, Vim expects an input and you tell him you’re done with the closing bracket >. Read the full list of commands in the repo.

ALE

ALE stands for Asynchronous Lint Engine. It’s only the engine, not the linter. A linter is a tool that checks your code for errors, bugs and dubious style. ALE integrates effortlessly with many standard services including ESlint or various LSPs like Microsoft’s tsserver. Remember we enabled a signcolumn in part 1? That’s where ALE will flag your errors.

Personally, I use ALE only for ESLint integration. After installing and initializing ESLint on your Javasript project with your package manager of choice, ALE will automatically lint your code in real time and flag any mistakes whenever you exit Insert mode. I always use the Airbnb javascript style rules, but ESLint will prompt you to chose which ever ruleset you prefer upon initialization.

ESLint error flag
Error flag and description

It goes without saying that we don’t want to have to set up ESLint for every single .js file we create. Sometimes we just wanna quickly try some code, and it’s neither worth the time nor the effort to install and configure all the packages, but we would still like some kind of linting. Well, as I said, ALE integrates with the Typescript tsserver to provide you with VSCode-like IntelliSense. That means you don’t only get error flags but also autocompletion, for which ALE provides integration with Deoplete, a plugin that manages the autocompletion list. You can read the details here. But as I said, I exclusively use ALE for ESLint because I use another plugin for autocompletion. We’ll talk about in a minute.

It’s worth noting that ALE can auto-fix your code based on whatever rules you use. We already talked about ESLint, but if you use Prettier (I don’t), ALE can also use it for auto-fixing on save. There’s also a dedicated Prettier plugin for Vim. You have choices. If you’d like to try the autofix feature, you can set up ALE like this in your .vimrc:

let g:ale_fixers = {
\ '*': ['remove_trailing_lines', 'trim_whitespace'],
\ 'javascript': ['prettier', 'eslint'],
\ 'javascriptreact': ['prettier', 'eslint'],
\}

You can set up the rules manually, or tell ALE to pick them from either eslint, prettier, both, or whatever services you use for your language: you only have to create an array of services for the language in question. To run the auto-fixer just enter the command :ALEFix, or set it up to do it automatically on save like this in your .vimrc:

let g:ale_fix_on_save = 1

COC

Autocompletion in action
IntelliSense suggest ‘return’. TabNine correctly infers the module import.

COC is my preferred autocompletion tool. I already mentioned Deoplete, and I’ve also used YouCompleteMe in the past. Feel free to try them yourself. I can only tell you that once I tried COC I never looked back. Zing.

So, you install the plugin. Now what? You want tsserver integration for IntelliSense? :CocInstall coc-tsserver. Want AI-assisted autocompletion? :CocInstall coc-tabnine. Instead of suggesting completions from a language documentation dictionary, TabNine learns from your code and suggests stuff in a way that makes you say “how did it know what I was going to type?”.

I already know my setup, so I did it in one line:

:CocInstall coc-tsserver coc-tabnine coc-pyright coc-html coc-css

Check out the full list of supported language servers. Hell, if you don’t want to use ALE for ESLint you can even :CocInstall coc-eslint, and set it up the way you want in your :CocConfig.

I encourage you to read their example configuration, to add some useful lines to your .vimrc. Since I disabled swapfiles set updatetime=300 is not needed, that’s the swapfiles update time, but I did add this one:

set shortmess+=c

It appends the c to the messages configuration so you don’t get unnecessary info in your suggestions dropdown, like “match 1 of 2”. Probably c stands for cropped, but who knows for sure.

Lastly, since I’m using ALE for linting, and I want ALE to display the error messages in my status line (and not COC in a dialog box), I added this line to my :CocConfig :

"diagnostic.displayByAle": true

And this one to my .vimrc, telling ALE to leave the Language Server Protocol part all to COC:

let g:ale_disable_lsp = 1

If you just fresh installed COC, your:CocConfig file will probably be empty. Remember it’s a .json file, so simply wrap it in curly braces.

UltiSnips

UltiSnips snippet demonstration
‘React Functional Component’ snippet

UltiSnips allows you to save chunks of code that you happen to find yourself reusing quite often, a.k.a snippets, and call them with a simple abridged command. UltiSnips is actually only the engine, if you want a full library of functioning snippets, install these snippets.

I erased it the snippets pluggin (the snippets themselves, not the engine), since I realized the only snippet I really found myself using was a React simple functional component with export, and I din’t even like the style of the default one, so I created one myself. To do so, open a file with your desired language extensions, in my case I was already working with a .jsx file, and trigger :UltiSnipsEdit to automatically create a new file in ~/.vim/UltiSnips/ for that specific language, in this case it’s javascriptreact.

Polyglot calls it ‘javascriptreact’
snippet rfc "react functional component" b
import React from 'react';
function ${1:`!p snip.rv = snip.basename`}(${2}) {
return (
<>${3:}
</>
);
}
export default $4`!p snip.rv = snip.basename`;
endsnippet

I wrote the above snippet, you can see it in action in the gif above. If you copy/paste it you might have to retab or make sure to replace the spaces with tabs. A good source of inspiration are the snippet files I mentioned above, you can just look at them in the repo, no need to install them.

Now, whenever I want to expand my snippet, just type rfc and trigger it with ^j, (control + j), because I set it that way in my .vimrc:

let g:UltiSnipsExpandTrigger="<c-j>"
let g:UltiSnipsListSnippets="<c-l>"

^j, or whatever combination you want to set it to, also jumps between the “stops”. See the 1:, 2:, etc? Those are the stops the jump is gonna make, they allow me to quickly insert props if needed, or insert some code inside of the <> fragments. On the other hand, ^l allows you to enter a partial snippet name and deploy a list of matches, in case you don’t recall what the snippet complete name was. In my case it’s totally useless since I only have the one and only rfc snippet.

Although I don’t use it, you might want your snippets to appear in the COC autocompletion list. In that case, take a loot at this extension, that you can install as usual with :CocInstall coc-snippets.

One last thing, if you do decide to install the Vim snippets collection, make sure to take a good a how the files are named and how your status line calls your file formats. Let me explain: probably because of Polyglot’s React library, my Vim calls .jsx files 'javascriptreact' and the snippets are read in a file with that exact name. If you take a look at the Vim snippets repo, their file for React is called 'javascript_react' and UltiSnip will not read it with my actual settings, so I would have to delete the underscore by hand. I don’t know why this is, maybe for compatibility with another library, but it’s a detail to keep in mind.

FZF & FZF.VIM

fzf running in the shell with partial names

fzf is a fuzzy search process that lets you quickly search a filename match within the contents of a directory, directly from your terminal. It’s way faster than my OS’ spotlight, specially if I’m already in the terminal.

I installed it with brew install fzf but you can get it with your system’s package manager, even Window’s choco. I also included it as a Vim plugin but I honestly don’t know if that’s redundant or necessary.

The thing is that even if you can keep fzf up to date with your plugin manager, fzf is not really a Vim plugin with proper integration, hence fzf.vim! It has a couple of dependencies you need to install in your system to be able to fully access its functionalities, but believe me, they all are worth it.

You obviously need fzf for fzf.vim to work properly. You trigger file searches with :File and it looks like this:

Files fuzzy search

You also need either the Rg (ripgrep) or Ag (the silver searcher) for fzf.vim to be able to search within your project files. You can get them system wide via your packet manager, and you trigger them within Vim with :Rg or :Ag. I use Rg, and it looks like this:

Matching patterns within files
Rg gets regex matches within files

You can also install a ctags implementation, that allows you to index the ‘tags’ in your files (functions, variables, classes…) and then browse through them project-wide with :Tags. Tags will also come in handy for the next plugin.

Tagbar

Same as last plugin’s :Tags but for a buffer

It doesnt’ really makes sense for a small file like the one above, but this screenshot clearly illustrates how Tagbar indexes the tags in your current file. It gets classes, functions, variables, primitives, etc. It really shines when you have to edit a behemoth undocumented 1500-lines ES5 file. It might never happen to you, but if it does, you’ll be glad to have Tagbar.

Remember to throw in something like this in your .vimrc:

nmap <F8> :TagbarToggle<CR>

And there you have it, toggled by the f8 key.

IndentLine

Indentation marks
Visual lines for space-indentation

I don’t want to revive the decades old tab vs space indentation dilema. I can provide sensible arguments for both sides, but I do happen to use spaces. If you use tabs, you don’t need IndentLine, simple go back to your .vimrc and set whatever character you want the tabs to be shown as. Refer to part 1 if you need a refresher.

If you use spaces on the other hand, you’ might find this plugin useful. Or not. In any case, it takes advantage of Vim’s concealing feature, and it’s quite configurable since it allows you override the default highlighting settings for the conceal group. You want the lines to be green over an orange bg, go crazy with something like:

let g:indentLine_color_gui = '#A4E57E'
let g:indentLine_bgcolor_gui = '#FF5F00'

or something like:

let g:indentLine_color_term = 239
let g:indentLine_bgcolor_term = 202

if you didn't enable the termguicolors in your .vimrc.

The thing is I want them to be as subtle as possible, I don’t want to get distracted by them, so I set them to an appropriate gray with low contrast. However, I told you these settings override the conceal group default highlighting settings, so if you were using concealing for keywords like ‘null’ or ‘function’ as we discussed above, they also will get greyed out and will be barely visible, which in my opinion is a no-no.

You can always pick a color that is visible enough to clearly see the concealing characters, but not distracting enough so that the indent lines become all noisy in your screen. I could not find such color so I chose subtle IndentLines over concealing syntax altogether, and ended up disabling it. You might chose the opposite. I may happen to change my mind tomorrow. Who knows.

Check out more configuration options for IndentLine in their repo. You can change the indent character with something like:

let g:indentLine_char = '.'

and your file will be indented with dots instead. Just make sure your .vimrc’s character encoding is set to UTF-8.

Furthermore…

EditorConfig

Just as I was wrapping this article, Simone Conti pointed out that instead of hard-coding the tab settings for each language, like I suggested in part 1, it would actually be better to use the EditorConfig convention ensuring consistency across editors, files and projects. Here’s the Vim plugin. I still have to try it out but if it’s about enforcing consistency, count me in.

Vimspector

A graphical debugger very much like the VSCode one. A must have.

Kite

Provides both AI and documentation assisted autocompletion. Instead of using COC+tsserver+TabNine, Kite provides all of those services in one plugin, but better. The main problem is that you need to run the Kite engine as a dedicated desktop app to which the Vim plugin connects to, but the download has been unavailable for almost a year now. I happen to have a .dmg lying around, but I don’t think the Kite guys would like me to share it. I’m totally NOT offering you the installer. I’m seriously NOT encouraging you to ask me for it so you can try it. DON’T, because I will definitely NOT send you a download link. Wink, wink.

Rainbow Parenthesis

If you’d rather have coloured bracket pairs instead of indent lines, give it a try.

Fugitive

I just realized Fugitive has ‘git’ in the middle of the word. Another one by Tim Pope, the creator of Surround and Commentary, Fugitive allows you to call Git commands from your buffer.

Since we’re talking Git, You might also want to check the Git Gutter. Just giving you options here, I never really used it.

Pathogen

Also by Tim Pope, this one allows you “to install plugins and runtime files in their own private directories” if you’re into that stuff.

As promised, here’s my plugin configuration:

I tried to configure the tagbar to index .jsx files, but I never got it quite on point. Then I realized it was a waste of time since my components are always small and I don’t really need tagbar for .jsx…

That’s all folks, stay tuned for part 3 where we will discuss all about Vim navigation.

--

--