How to have a Neovim configuration compatible with Vim

Alberto de Murga
4 min readDec 1, 2021

--

So you can have your cake and eat it too.

I have been a Vim user for about 8 years already. Most of this time I have written code using Neovim, and when they announced the Lua integration, I was excited about the possibilities. I use my Vim configuration at home but also at work. In Booking.com, we do a reasonable amount of development in remote machines. Typically, you use ssh to remotely access a machine and do your work there. Many of those machines are quite liberal in regards of installing external packages (which includes Neovim), but other are heavily locked. However, we have an internal system that allows you to copy your dot files when you connect to a remote machine.

For most of these years, I have kept my configuration compatible between Vim and Neovim. Every Vim configuration should work on Neovim by default, but the other way around. I managed to keep the especial Neovim features and plug-ins under wards so if needed I could launch the configuration in Vim if Neovim wasn’t available in the environment. This solution will not work anymore when with Lua plug-ins are involved.

The requirements

These are the MUST have for the configuration.

  • It must work in Neovim and Vim with similar functionalities so it does not break my muscle memory.
  • It must be a single configuration that works out of the box. If not, there will be two different configurations de facto.
  • Neovim can use Lua and external software (for example, language servers or tools like eslint) without limitations. It will be running in my local machine or in a non critical server.
  • Vim can only use Vimscript. It can use plug-ins as far as they are written exclusively in Vimscript and can be tracked on git. This will run on restricted servers that only allow to copy configuration files.
  • Minimise dependencies but keep it fast (Lua plug-ins > Vim plug-ins > external tools).

The solution

Output of the command tree -L 2 -I plugged on $HOME/.config/nvim
How my Neovim configuration directory looks like.

This is how my (Neo)vim configuration looks like. The directory lives in $HOME/.config/nvim, which is the default location for Neovim. When I need to use Vim, I set two symbolic links: $HOME/.config/nvim$HOME/.vim and $HOME/.config/nvim/init.m$HOME/.vimrc.

  • init.vim: It is the entry point of the configuration. It loads the plug-ins using vim-plug, which will work for both. It also defined where the plug-ins should be located and which plug-ins to load depending on if it is Neovim or Vim. Additionally, it loads all the other configuration files.
  • common.vim: It contains the common configuration like some of the bindings, tabs, word wrapping, etc. All these configurations are classical vim configurations and are the same for both. The rule of thumbs is that anything that is valid for both and not a plug-in goes here. If there are inconsistencies between platforms, the Neovim versions takes priority.
  • plugins.vim: The complementary of common.vim. Every plug-in configuration used in both versions goes here for instance, like NERDTree, easymotion or lightline.vim.
  • only_vim.vim: Self explanatory. These are the configurations that should be loaded only in Vim. They are mostly inconsistencies between platforms that require extra configuration or Vim only plug-ins. Because it is loaded after, it will overwrite Neovim’s configurations.
  • languages: This directory contains custom languages configurations used by both versions, with one file per language and one additional file as a kitchen sink. For instance, the file languages/golang.vim changes the configuration of the tab to use tabs instead of spaces when editing a *.go file.
  • lua: Contains all the Lua code for Neovim. It is a requirement of Neovim to place the Lua code here. Neovim configuration takes place and precedence in the common files, which means that this folder mostly contains the configuration of the Lua plug-ins. Every file is the configuration for a concrete plug-in, plus other.lua as a kitchen sink. These plug-ins typically require heavy configuration because they set up language servers bindings, syntax highlight, autocomplete, etc. There is an additional folder, languages, which adds additional information required by the plug-ins that can be split by language. Most of the content is the configuration of the different language servers.
The last lines in the init.vim
The order of loading the plug-ins in init.vim matter

Some cool plug-ins that I use and their equivalences

This is a bit off-topic, but this is a selection of some of the plug-ins I use and their equivalences between Vim and Neovim. For the full list of all the plug-ins in my configurations, you can check the init.vimfile on Github. Remember that you can always use the Vim plug-ins in Neovim, but not the other way around.

Extra tricks

This snippet will help you to load plug-ins conditionally using vim-plug.

" Lazy loading
" From https://github.com/junegunn/vim-plug/wiki/tips#conditional-activation
function! Cond(cond, ...)
let opts = get(a:000, 0, {})
return a:cond ? opts : extend(opts, { 'on': [], 'for': [] }) endfunction

These two variables can be used for distinguishing between Vim and Neovim. Technically you only need one, but it is more descriptive to have both. Defining the base path also helps as vim-plug needs to be able to find them in the right place.

let is_vim = !has('nvim')
let is_nvim = has('nvim')

if is_nvim
let $BASE = stdpath('config')
else
let $BASE = '$HOME/.vim'
endif
call plug#begin($BASE.'/plugged')

Did you like this post? Let me know on Twitter!

--

--

Alberto de Murga
Alberto de Murga

Written by Alberto de Murga

Software engineer at @bookingcom. I like to make things, and write about what I learn. I am interested in Linux, git, JavaScript and Go, in no particular order.

Responses (1)