The evolution of 10 years of vim configuration

So naturally, I accumulated a ton of plugins, configuration, and various cruft. It's really interesting and nostalgic to look through the git log, and see what I have used in the part, what replaced it, or what got dropped altogether.

Initially I had some configuration for detecting when I'm on windows and tweaking some backup and temporary folder paths. This was probably Windows XP at the time, since I skipped on Vista altogether. I eventually installed linux on all my computers, so I dropped everything related to Windows. There was also some separate configuration for the GUI version of vim, which I also dropped, since now I just use the terminal. Also very interesting, I found some references to a folder in dropbox, which I was probably using to share my configuration between computers.

A lot of tools/plugins have stayed, or has been superseded by better ones. For searching, I went from a grep integration to ack, to ag, to ripgrep. Each of these were faster than the previous one. For diagnostics, I went from syntastic, to ale, to coc, and now I use the built in LSP stuff. I don't remember why I kept replacing these, but I remember that all of them seemed to eventually slow down the editor, to a point where I had to exit and restart it. The built-in LSP was the first that did not have this problem, and it also had some awesome tools around it, like Telescope, and Trouble, and it had support for virtual-text, which comes in extremely handy, even today.

For plugin managers, I switched away from pathogen, to vim-plug, to lazy.nvim now. For navigating inside files, I kept flip-flopping between sneak, easymotion, and clever-f for a while, then settled on leap.

For file managers, I started with the built in netrw, then used nerdtree, and now I'm on neotree, but contemplating removing it, I almost never use it. It's quite rare that I want to go through the tree view of the file system to find something, I either use telescope to fuzzy find the filename, or use something more accurate to jump like lsp symbols, or find references.

I threw out all of the various filetype syntax plugins, and I just use treesitter for everything.

Switching to neovim

I think I switched to neovim about 1.5 years ago. I remember that there were a few tools/plugins that seemed interesting, but only worked with neovim (like Telescope, Trouble, the built in LSP), so I looked up some guides on how to switch. Turns out the entire migration was basically:

init.vim
vimscript
set runtimepath^=~/.vim runtimepath+=~/.vim/after
let &packpath = &runtimepath
source ~/.vimrc

I don't know how deliberate it was to create such an easy migration path for Neovim, but it was an extremely good decision, well done. This basically let me use all the lua ecosystem, without changing anything in my config. Really, if anyone is still on the fence about switching to neovim, all you need is the above, maybe read :help nvim-from-vim.

Today it's obvious that neovim has won the popularity contest, even though there is a new scripting language in vim (vim9), I haven't seen a single plugin written in it. There are already several plugins that are written in lua, and there is a major ecosystem built on top of the LSP and treesitter support in neovim. There is also a lot of quality of life improvements that makes it much more approachable for newcomers, out of the box. I doubt anyone looking for their first editor today is going to go with vim instead of neovim.

Switching the config to lua

The main reason I wanted to convert my config to lua mostly boiled down to two things:

My configuration was getting unruly, especially true for the LSP stuff. I copy pasted a lot of stuff from here and there, without really understanding what's happening, because I couldn't be arsed to piece together the details. I managed to get it working, but I dreaded updating and dealing with breaking changes in plugins. It was obvious that I needed to get some lua knowledge, to have a properly maintainable config.

Adding, removing and just trying plugins was becoming cumbersome, this was mostly due to how the package manager I was using (vim-plug) was written in vimscript, but a lot of the plugins I used were written in lua, which meant some piece of vimscript code in one file (including the plugin itself in my config), and some piece of lua code in another (configuring the plugin). This wasn't solved by lua itself, but a new package manager called lazy.nvim.

Lazy.nvim is really really great. There are a bunch of plugin managers already, and they pretty much offer the same functionality, but lazy.nvim has two killer features.

First, it gives you the ability to structure your plugin setup like this:

init.lua
lua
require("lazy").setup("my.plugins")

And it will go through every *.lua file in your my/plugins folder, and expect them to return a table like this:

my/plugins/colorizer.lua
lua
return {
    'norcalli/nvim-colorizer.lua',
    config = function ()
        require('colorizer').setup({'*'}, {
            mode = 'foreground', rgb_fn = true
        })
    end
}

This let's you have the inclusion of the plugin itself, and its configuration in the same file close to each other, but separate plugin configurations can live in separate files. This is a really nice way to structure your plugin setup.

Second, it has a lockfile. I might be wrong, but it seems it's the only package manager for neovim that has this, and I don't understand why, it's a no-brainer for any kind of package management.

Lua

Switching the configuration was pretty much just replacing every nnoremap and similar calls to vim.keymap.set, and set options to vim.opt.option = true. There were a few tricky bits that I had to actually "program" in lua to solve. I have had 0 experience with lua before, so I basically picked up everything in a few days, and based on this few days of experience, I'm going to say that lua is one of the most annoying languages I have ever had to use.

  1. There is no distinction between sequential lists (like arrays or vectors in other languages), and hashmaps (dictionaries, associative arrays). They are the same type ala PHP, but called a table
  2. You use ~= for not equal, instead of !=
  3. Tables (arrays) index from 1, not 0
  4. The standard library is even worse than JS. Basic stuff like merging lists, or array.map, array.any, etc. is not included
  5. There are often multiple different syntaxes for the same thing

The first one leads to a lot of APIs where you have a table that has an "ordered list", and a "keyed hashmap" part together in one, like you smashed *args **kwargs in python together. I find it so hard to read this, but it might just be that I'm not used to it yet.

I have no idea what the reasoning behind 2. and 3. are. I guess it's due to the fact that lua is almost 30 years old, and changing these would be a major BC break, but there is really no excuse for the 4th.

The 5th is also interesting. You can separate table keys with a ,, or a ;. You can put semicolons at the end of statements, or not. You can use require('my.module') or require('my/module'). You can leave out the parenthesis on function calls, but only if the function expects 1 single argument.

With all this said, it's still much easier to work in lua than vimscript.

Finishing

It took about ~20 hours in the duration of ~6 days to move everything over, and completely remove every .vim file from my repository. The final diff ended up at 42 files changed, 1066 insertions(+), 973 deletions. Even though my config is about ~1200 lines long, neovim starts up instantly. (This might be due to how aggressively lazy.nvim caches things, I've heard.)

If you are interested, my repo is up on github.

I feel happy that I did this, it was a nice way to pick up some lua knowledge.