Build your own Vim configuration

Maybe the config you were using is no longer maintained for general use. Maybe you just want to get to know your editor a little better. You've been a Vim user for a long time, but now it's time to build your own lightsaber. You need to learn how to write your own Vim config.

Build your own Vim configuration
Photo by Greg Rosenke / Unsplash

Hey there,

I'm Nat Bennett, consultant and Vim addict, and boy do I have a big ol' chonker of an update for you this week. You're reading Simpler Machines, a newsletter about getting better at making software, and this week we're talking about Vim.

If you're reading this, it's time.

It's a rite of passage for every Vim user. Your training has begun, but to move forward, you know. You need to forge your own personal connection with the force your keybindings.

Maybe there's a specific change you want to make. Maybe the config you were using (Luan's Vim 😭) is no longer maintained for general use. Maybe you just want to get to know your editor a little better. You've been a Vim user for a long time, but now it's time to build your own lightsaber. You need to learn how to write your own Vim config.

I happened to do this a few months ago, and it wasn't as nearly as bad as I thought it was going to be. So since I know at least a few of you are looking at this because of the aforementioned changes to Luan's Vim configuration, I thought I'd walk through the choices that I made and the problems I had to solve, to help you get started on your own configuration adventure.

Getting started: What is Vim configuration?

Vim configuration can get complex but if you understand basically how your shell gets configured you also understand Vim. Vim has a bunch of settings, and a scripting language. (More on that in a minute.) When you start Vim, it loads a config file from its config directory and executes whatever commands it finds there. Those commands can change settings, and do basically anything else you can do with Vim's configuration language. That includes loading other files and executing the code in them.

So if you're trying to customize an existing Vim configuration, look for a file in that configuration named init.vim (or possibly init.lua, if you're using Neovim). If it's a complicated config there are probably a bunch of require statements in that file which will show you where else in the directory to look to find what the configuration actually does.

I want to start from scratch! Just give me some instructions already!!

Okay okay okay.

If you're not using it already, install Neovim.

Create a file at $HOME/.config/nvim/init.lua

Add Packer to that config, and use it to install any plugins you want.

If you don't have any plugins, a good one to get started with is a color scheme. I like NeoSolarized

If you're just starting with vim you also probably want these settings.

  • turn line numbers on
  • enable syntax highlighting

Your initfile will end up looking something like this:

require('packer').startup(function(use)
  use 'wbthomason/packer.nvim'
  use 'overcache/NeoSolarized'
end)

--- set line numbers for pairing
vim.opt.number = true

--- enable syntax highlighting
vim.opt.syntax = "on"

--- enable 24-bit RGB colors
vim.opt.termguicolors = true

--- set the colorscheme
vim.cmd("colorscheme NeoSolarized")

When you start Neovim it'll complain about not being able to find your colorscheme. That's because Packer doesn't actually install plugins until you run :PackerSync. Run :PackerSync , then close and restart Vim.

Now you've got a basic Vim config!

Choose Wisely!

But we're not talking about building a basic lightsaber here. We're talking about building your lightsaber. There are a bunch of choices that you can make about how to configure your Vim setup.

  • Vim or Neovim?
  • What plugin manager to use?
  • What init script to use?
  • How to get your init script into the spot where Vim can load it?
  • What settings to set?
  • What plugins to use, and how many to use?

If you're working from an existing configuration that you know, you should probably use most of the choices that configuration makes, but review the settings and plugins and use a smaller set. It's unlikely that you actually use everything in a general purpose config, and there's often a small performance cost to loading plugins, so not it's faster to load only the plugins you use.

Later we'll also talk about why you might different choices for your own config, especially the Vim vs. Neovim decision, and what plugin manager you want to use.

Why use Vim?

This is a little bit of a digression but if you're getting this involved in your editor you should probably have a good reason. I use Vim rather than something like JetBrains or VScode for four reasons, in order of importance.

  • I already have the muscle memory. Vim is comfy!
  • Credibility. People assume that I know what I'm doing when they see me using Vim, and that's valuable to me.
  • Aesthetics. I like being a Vim-using morlock, and fucking around with my system's internals.
  • Flexibility. I write a lot of different programming languages, and I write a lot of Bash. Vim works basically the same no matter what I'm doing.

The "credibility" one is a bit controversial. A lot of folks think it shouldn't be this way, programmers shouldn't assume that Vim and command-line skills = competence. And, sure, I agree. But I also don't care how the world should be. And using Vim is a "competence trigger" for a lot of programmers. They see me being reasonably fast in Vim and they subconsciously put me into a different bucket than they would if they saw me using a more GUI-driven editor.

That said, the most important competence trigger is being fast. Whatever editor you use, you should learn its keybindings and its special features. This will establish credibility and credibility, like it or not, is important.

My Vim configuration

I manage my workstation with a script that's checked in on Github (which is great, and I highly recommend you do the same) so you can see exactly the Vim setup I use.

My config as of this writing

local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
  vim.fn.system({
    "git",
    "clone",
    "--filter=blob:none",
    "https://github.com/folke/lazy.nvim.git",
    "--branch=stable", -- latest stable release
    lazypath,
  })
end
vim.opt.rtp:prepend(lazypath)

plugins = {
  { "elixir-editors/vim-elixir" },
  { 'overcache/NeoSolarized' },
  { 'github/copilot.vim' },
--- make things a bit Luan's vim-ier
  { 'ctrlpvim/ctrlp.vim' }
}

require("lazy").setup(plugins)

--- set line numbers for pairing
vim.opt.number = true

--- enable syntax highlighting
vim.opt.syntax = "on"

--- enable 24-bit RGB colors
vim.opt.termguicolors = true

--- set the colorscheme
vim.cmd("colorscheme NeoSolarized")

I symlink this into the place where it's used like in my install script, like so:

ln -sf ~/workspace/workstation/init.lua ~/.config/nvim/init.lua

This setup uses:

  • Neovim, rather than "classic" Vim
  • Lazy as my plugin manager
  • Lua for configuration, instead of Vimscript
  • a symlink to get the file from where it's under version control into the correct place

I've made these choices for specific but somewhat stupid pragmatic reasons.

  • Luan's Vim switched to Neovim ages ago so I did too. Also, my partner likes Neovim.
  • Before I started writing this I was using Plug, since that was what the version of Luan's vim I copied from ages ago was using. As I was researching other plugin managers as I was writing this, I saw that Luan has switched to Lazy, and decided to switch basically because it's very fast and it doesn't require me to run additional steps to install plugins after I add them. I'm not 100% I'll stick with it but I'm giving it a try.
  • I also switched to Lua as part of writing this because it's much easier to install and configure Lazy with Lua than with Vimscript.
  • I want to keep my vim config checked in with the rest of my config rather than in a separate repo, and a symlink means it gets updated instantly when I make changes to my config in the repo.

Should you use Vim or Neovim?

The short version is, there's probably no downside to using Neovim, and switching is basically seamless, so if you're already messing with your config you might as well. A few configuration details will be different by default.

Vim has picked up several of the features that originally prompted the creation of Neovim, so this is less of an obvious slam-dunk than it was when I first started using Neovim. However, if you're using Vim, there are some reasons to consider switching.

  • Plugin development and management
  • LSP support
  • Terminal Emulator
  • Project architecture and politics

The main reason to use Neovim generally has better support for plugins and customization than Vim. There's an API designed to be used by plugins. You don't have to use Vimscript, you can use Lua. Lua is useful to learn generally, and it's less weird than Vimscript. Even if you never use Lua yourself, though, in Neovim you can use plugins written by other people in Lua. So there are more plugins, and in theory better plugins.

Conversely if you don't use many plugins, and don't have much interest in programming against Vim yourself, you probably won't get as much out of Neovim.

Neovim's support for the Language Server Protocol means that it's much easier to write plugins that do things like autocompletion and "go-to-definition" for a particular programming language. I don't actually use this feature much but it's on my list of things to learn to get faster. You can do LSP stuff in Vim but if you lookup any LSP plugin today you're likely to see something like "this works better/is faster in Neovim."

The terminal emulator is another thing I don't use myself and should probably learn. It also lets you write and use plugins that do command-line things – which probably includes building your project and running tests, which it can be handy to do directly from your IDE. (I tend to just do this from a separate terminal, but remember: I'm a morlock, and I'm doing it from a combination of laziness and Morlock Aesthetics.)

Finally, a lot of the above differences are driven by differences in the way the projects are organized. There's some personal/political/style difference, but one concrete difference is that the Vim project cares a lot more about broad compability and backwards compatibility than Neovim does. Vim maintains compatibility with vi and is shipped in Linux versions by default. Neovim cares about support with Vim itself, but not whether it runs on a large array of older operating systems and hardware. For most people and most projects this isn't a downside, but there are cases where that compatibility goal is key.

As a result, the Neovim project is a lot more willing to radically reorganize code to make it easier to develop features.

What plugin manager should I use?

I'm going to try to avoid discussing specific managers here since that part is likely to go out of date. It also just honestly doesn't matter that much for most people. Use something other people on your team are using ideally so you can share troubleshooting tips.

There are two basic criteria to consider when you're looking at plugin managers

  • Plugin loading speed and fanciness
  • Configuration fiddliness

If your editor is taking a noticeable amount of time to start up or load files, and you have a lot of plugins, you might be able to make it faster by switching to a different plugin manager. (Remember that "noticeable" here is very short – 120ms is noticeable!) A plugin manager that has better "lazy loading" or cacheing features can load plugins only when their commands are actually called, which can speed up startup times quite a bit.

The other reason to consider switching is that some managers let you download, install, and load plugins all in one step, and others require separate steps to make plugins available and then to actually turn them on. Some plugin managers also include more package management features like lock files, while others rely on you to install, update, and track your plugins. And some installers let you very precisely control the order in which plugins get loaded.

If you don't have many plugins it doesn't matter very much which manager you use, but if you have a lot of plugins, or you develop your own plugins, you should take a look at what other complex distributions are doing, since you might be able to get meaningful performance or ergonomics gains out of switching.

Structuring your init file

If you're just getting started, and you're using Neovim, it's probably a bit better to write an init.lua file rather than an init.vim file. Lua is slightly more general-purpose useful to learn than Vimscript.

This choice is very marginal, though, because it's easy to call Vimscript from a Lua file and vice versa. There are things that are easier to do with one or the other but you always have the option of just doing them in a script that you call from your main file.

Managing your init file

You will save yourself a lot of trouble if you manage your config file as code and check it into a git repo, but your config file needs to be in a specific place for Vim to read it, and that might not be the place where you want to edit from.

So you've got three choices:

  • git clone your vim config repo directly into $HOME/.config/nvim/
  • Clone it somewhere else, but symlink the config file and and related directories into place
  • Clone it somewhere else, but cp the file and any directories it needs periodically into place

If you manage your vim config and only your vim config in its own repo, cloning it directly into the directory where it lives is the simplest thing you can do. Otherwise it basically depends on how comfortable you are with symlinking.

Settings and Plugins

This is a huge topic, and one I'm still learning about myself. The most I can do is offer a few general pointers.

If you're moving off of a maintained vim configuration, you could do a lot worse than going through it line-by-line and copying in the parts that you, personally, actually use. This will give you a good sense of what you can control with a vim config, and give you a leaner and more personalized config.

I personally like a really light config, because I enjoy learning to use the basic Vim toolset really well, and I'm still pretty early in my Vim journey. There are a lot of things – search, navigating the filesystem, running tests – that you can do within Vim that I just keep a separate terminal open for. A lot of people who have been doing this for a while often like very rich, IDE-like configs.

Whatever language(s) and frameworks you use regularly, there are probably plugins specifically for them. You can get a ton of refactoring and autocomplete power out of the right plugins.

Another good way to learn about settings or plugins is to pair with people who also use Vim.

Some good configs

fygm/init.lua at main · SamirTalwar/fygm
Samir’s dot files and shell scripts. Use with caution. - fygm/init.lua at main · SamirTalwar/fygm
GitHub - luan/nvim: Luan’s Neovim distribution
Luan’s Neovim distribution. Contribute to luan/nvim development by creating an account on GitHub.
GitHub - njbennett/workstation
Contribute to njbennett/workstation development by creating an account on GitHub.

May the force be with you

By this point you should have the beginning of your own Vim config, and a pretty good idea of why all the bits in it are there. If you've got your own config, especially if you set it up or made changes to it based on this guide, I'd love to see it. Email me at nat @ this website and I'll add them to this guide as examples.

And... hey, do you want to learn more about Vim? Got a question you're stuck on? Anything I touched on in this guide that you'd like to understand in more detail? Send me an e-mail! I'd love to write about this more but man, the topic is vast, so some specifics about what would be useful to you would be a great help.