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

Enrique Domínguez
10 min readNov 4, 2021

--

A comprehensive guide to setting up Vim like an IDE Part 1: the .vimrc

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!

Looking good

Great, another Vim article, just what we needed in 2021… Hold on. My aim here is to share not only the whats and the hows, but most importantly the whys of my setup. There are many articles out there telling you what plugins to install or what configs to copy/paste, and I’m grateful for them as they did solve my issues one way or another. The problem was that most of the time I didn’t really understand what I was doing; it took me about a year of progressive experimentation get where I am now. Hopefully all my past struggles will save your precious time.

Without further to do, grab a drink and let’s get into it. This one is gonna be a thorough one.

Let’s break down my .vimrc

I’m assuming nothing about your level of Vim expertise, but If you just wanna take a look at my .vimrc and move on, here it is:

Actually I’m assuming you do know how to exit vim. You surely know how to enter commands like :q or :w. Keep in mind that colon logic, as it is going to pop up down below.

Remember when you tried to edit your first commit?

I’m also assuming you know your can configure Vim via a file $HOME/.vimrc located in your home directory in unix-like systems. You can edit it with Vim itself or with whatever text editor you happen to have at hand. It’s a hidden file, hence the . in the filename.

Now, what do all of those settings even do? Let’s go block by block.

syntax on

This one is pretty straightforward: syntax on enables syntax highlighting on your code, like on any proper text editor.

set fileformat=unix
set encoding=UTF-8

Vim recognizes three types of file-formats: Unix, DOS & Mac. The latter is deprecated, as modern Mac OS’ use unix file formatting. The difference between DOS and Unix is what line terminators each one uses (CR+LF and only LF respectively); I’ll let you google the specifics. If your garden-variety Windows VSCode user shares a file with you, you might get an ESLint error (because of the CRLF line terminators) depending on the ruleset you’re using, but do note that unix linebreaks are enforced by default.

Also, I like to explicitly set my encoding to UTF-8, just in case.

au BufNewFile,BufRead *.py
\ set tabstop=4 |
\ set softtabstop=4 |
\ set shiftwidth=4 |

I’m a big believer in enforcing code style. Readability, consistency and so on. But I don’t wanna be pressing those tabs twice when I’m writing python code! That’s why I’m telling Vim “whenever you open a new buffer for a .py file, tabs are 4 spaces long”. shiftwidth is the value used for auto-indentation. | means “and”, and \ means “this new line is part of the previous scope”; if you were to remove those characters, Vim would interpret each new line as a global setting, not one specifically for python.

If my file is not .py, fall back to my global tab settings:

set tabstop=2
set softtabstop=2
set shiftwidth=2
set autoindent
set smartindent
set smarttab
set expandtab

autoindent, smartindent and smarttab preserve the indentation when you break a new line, and add a new indentation level when appropiate. Smarttab is related to the softtabstop, but I rendered it irrelevant by setting them all to the same value. expandtab breaks your “tab” character into spaces.

set nowrap
set list
set listchars=eol:.,tab:>-,trail:~,extends:>,precedes:<

nowrap prevents line wrapping, which means that if a line length exceeds vim’s window width, the text will not be show in a new “intermediate” line. It will simply be cut off the screen. Think “overflow: scroll”. It’s a matter of preference, but why is your line so long in the first place? list and listchars allow me to see the “invisible characters”, the most useful for me being the trailing spaces at the end of a line. If you use a code formatter, you can set it to erase them automatically but I like terminating them by hand. More on that below.

set cursorline
set number
set relativenumber
set scrolloff=8
set signcolumn=yes

For this section, a picture is worth a thousand words:

Can you see those pesky trailing spaces?

cursorline highlights the whole line my cursor is on, making it easier to find it. Currently it’s on line 6. number enables the line-number column, and relativenumber makes it so instead of the line numbers, I’m shown the offset of any given line to the one I’m positioned on. Why? Because I’d rather jump to that error 11 lines below by pressing 11j (offset+direction) than :17⏎ (colon + line number + return). But it’s totally personal, do as you please. By the way, that error is ESlint telling me I’ve got a bunch of trailing spaces that need to be terminated.

signcolumn enables the left-most column where the error is marked. You can set it to signcolumn=number if you want, and the errors will be shown on top of the line-numbers instead of a separate column. Lastly, scrolloff will make the document start scrolling 8 lines before my cursor reaches the bottom line. You can set it to whatever offset works for your screen size.

The scroll offset is set to 8
set showcmd
set noshowmode
set conceallevel=1

noshowmode tells Vim not to show me if I’m in visual mode, insert mode, etc. Why? because I’ve got a beautiful plugin doing it for me as seen above. I don’t want them both showing me the same information at the bottom of my screen. showcmd “shows the command” I’m currently typing on the bottom-right of the screen, it’s useful for when you’re typing escape commands like control+W+shift+h... conceallevel refers to how the characters are concealed, more on that in part 2, but if you payed attention to the React code above, the “function” keyword is concealed as an “ƒ”.

Showcmd: Control + W
set noerrorbells visualbell t_vb=
set noswapfile
set nobackup
set undodir=~/.vim/undodir
set undofile
set clipboard=unnamed

noerrorbells visualbell t_vb= (note that it’s set to nothing) disables sounds, bells, beeps and my screen flashing white whenever an error occurs. Why? most errors are dumb errors like pressing the wrong key, and it quickly becomes annoying. Very annoying. Having it do nothing is already telling me I did the wrong thing, I don’t need an extra layer of irritation.

Swapfiles store changes made on the buffer, and if Vim crashes they allow us to recover those changes. I’ve disabled them with noswapfile alongside nobackup because I’m already saving my all my changes to an undofile located in the undodir path. And that allows me to have infinite undos even after I save, close and reopen a file a week later!

The clipboard thing is because in unix-like systems, there is more than one clipboard. I want my system command+c clipboard to be the same as my Vim y and p clipboard, to be able to copy from Vim into other applications and vice versa. I’ll let you research the proper clipboard settings for your OS.

set ignorecase
set smartcase
set incsearch
set hlsearch
nnoremap <CR> :noh<CR><CR>:<backspace>

These are the search settings. Just in case you didn’t know, you can search for a character sequence in your file by directly hitting / and typing your search terms. You press return to confirm it and with n you navigate to the next search result.

ignorecase ignores the uppercases, for instance “norwegianblue” will get both “norwegianBlue” and “NORWEGIANBLUE”. smartcase will make it so that if I intentionally put an uppercase character in my search term, then it will no longer be ignored: “norgegianBlue” will not get “norwegianblue” nor “NORWEGIANBLUE”. incsearch highlights the pattern matches as typed so far. hlsearch highlights all matches simultaneously. So far so good.

Smart case in action

But what’s that weird last line with the nnoremap? Well, say you hlsearch, you find what you were looking for, and you start typing fresh code… only to realize all previous search results are still highlighted. They won’t clear off. Not until you enter the :noh command and return it. Hey, maybe you want them to keep highlighted, that’s what vim’s all about: options. I don’t, so I remapped my return key to execute three things: when in Normal mode enter the :noh command, return the command, and then return, so that it keeps its normal functionality after clearing out the search results. The last bit about the <backspace> was because Vim keeps showig the last command you typed and it was bugging me seeing that :noh there, all the time, in the bottom of my screen. It clears the clearing. You can always disable the hlsearch, I encourage you to try searching with and without it and the decide if you like it. But at least now you know about remaps. More on that later.

so ~/.vim/plugins.vim
so ~/.vim/plugin-config.vim
so ~/.vim/autoclose.vim

That’s me linking three Vim config files to my main .vimrc, with the intention ‘keeping concerns separated’ and not having a bloated humungous .vimrc. You can separate your configuration in as many files as you deem necesary with that pattern. Note how I load plugin-config after plugins is loaded: order matters, just as if those configs were inside my .vimrc, you would’t try to tell Vim what to do with the plugins before they were even called. We’re gonna discuss plugins in part 2, but before the end of this article we’re gonna see what that autoclose file is all about.

Lastly there’s the color config, but I’m gonna talk about that also in part 2 because the theme I use, gruvbox, is installed as a plugin. Why aren’t those configs in the plugin-config.vim file? Because I feel they are mainly telling Vim what to do and not the plugin itself. They are down below, after the plugins were loaded because I do happen to call the plugin itself. You can put them in a separate file if you want, remember it’s your .vimrc now, not mine.

Important considerations

If you’re using python a lot, you might consider throwing in a textwidth setting telling Vim to have a maximum line length of 80 as per the PEP 8 recommendations.

set textwidth=79

Also, when sharing your screen or working with someone else, consider disabling the relative numbers, so that people can easily tell you “line 72, do this and that” instead of “line fift…wait wtf ”. You can do that for your current buffer only. In fact you can override any setting from and for your buffer only, because when Vim loads, it sets the .vimrc settings and any further manual change won’t affect your .vimrc.

:set nornu
:set norelativenumber

As you can see, most commands have abbreviated versions. Which one would you rather type?

Don’t let the mouse distract you from the :nornu

Oh, yeah. You can enable your mouse in Vim. Even if you like running it from the terminal for some reason, like I do. The choice is yours, I won’t judge you.

set mouse=a

If you have any further doubts about any setting, you can always google, it or read the documentation from within vim. It’s actually not too shabby!

:help 'command_name'

What’s with that autoclose file anyway?

There are many working plugins that manage the closing of bracket pairs, parenthesis pairs and so on. Try them out, by all means, maybe you’ll like them better. But why install a plugin if a couple of remaps can do the trick, natively and all?

Basically the remaps (below) are telling Vim “whenever one types one of those characters, automatically insert the closing pair”. But there’s little more depth into it: where do you want the cursor to be thereafter?

Typing, return and tab do different things. Plus all the smartindent we already set up.

I know there are better ways to check for an empty string but I wanted to showcase all three behaviours, so I don’t want to hear it. The rules work for all remapped characters, (, { ,[ , “, etc.

If you open a ( and you start typing immediately, the cursor will be between the open and the automatically inserted closing parenthesis as in the (string) function parameter. If you open a { and return, the cursor will be in a new line in between the open and closing braces. If you enter a and tab away, the cursor will be placed right after the closing quotation mark. You can also alter the closing character with a , or a ; and it won’t affect the cursor position: in the example above I actually entered { ; return to get a semicolon inserted after the closing brace. You can see how useful it becomes when dealing with a bidimensional array of strings for example.

If you pay close attention, the remaps are basically the same character sequences you would input to manually get there: { return } ; then escape to get into Normal mode and finally capital O, meaning shift o, to get a new line above. A new line that gets autoindented because we set it that way!

Well folks, that’s about it for now. Thank you for reading, I really hope you learnt something today. Part 2 is out!

--

--