Home

Why Neovim over Vim

Written on 2020-09-12

I have been giving quite a few presentations about Vim lately and have been asked what the differences between Vim and Neovim are and why I use Neovim over Vim. Since a full answer takes more time than I usually have at the end of a presentation and I tend to forget at least one of the key differences, I am documenting the ones that appeal to me the most.

I am splitting this overview into two parts: technical features and the project direction. Most end users only care about technical features, but the way the projects are led and their vision are also very important to me.

Technical features

Neovim provides more features than Vim. Some people love them, others see them as unnecessary bloat.

Modernity

"Modern" is quite a buzzword, but it is justified when talking about Neovim. The project started as a big refactoring of the Vim codebase, throwing away a lot of code and removing support for legacy systems. It uses a brand new VimL parser which is much faster than Vim's one, leverages libuv for asynchronous IO and is generally much more modular.

Neovim also ships with (much) better defaults than Vim, the difference being documented in :help nvim-defaults. This makes using Neovim much more comfortable and facilitates the adoption by new users.

Finally, Neovim follows the XDG Base Directory Specification. This is a minor feature but one that I enjoy a lot nonetheless.

RPC architecture

Architecturally, Vim and Neovim are very different. Vim is a standalone terminal application, which also ships with a graphical version. On the other hand, Neovim is a UI-less program that can be controlled by any client via RPC. Neovim happens to ship with a terminal frontend so this distinction is not obvious at first, but it can be leveraged by other projects.

The most evident application of this is that anyone can easily write an alternative Neovim frontend in any language. Some have been written in Rust, Go, F#, JavaScript and probably many other languages. These frontends can leverage the full power of Neovim, but can expose any interface to their users and add features that are not native to Neovim like support for VSCode plugins, drag and drop, eye candy, integration with the OS file picker, etc.

One particularly interesting frontend is Firenvim, which embeds Neovim in web browsers by turning text areas into Neovim instances.

This architecture can also be leveraged at a smaller scale by writing plugins, which can also be written in any language. It is trivial to get/set all Neovim properties like the content of its buffers or its options (see :help api).

Lua

Both Vim and Neovim can be scripted using VimL. Neovim can also be scripted with Lua, which is embedded into it. VimL is a good language for configuration (i.e. setting options) but it has terrible ergonomics and is very slow. It is a toy language that grew over time but never got better. Lua is orders of magnitude faster (in part because it can be JITed) and is actually pleasant to use.

For most end users, this does not matter much, unless they realize that some of their plugins are slower than they should be. For plugin authors (or users with complicated configurations) however, this is a game changer.

Language Server Protocol

Neovim ships with an LSP client written in Lua. In a nutshell, LSP adds IDE features like completion and go to definition to any text editor that implements the same protocol. Any programming language can provide a server that uses the same protocol to serve information to said text editors. This way, all editors get the same IDE features, which avoids duplicated work and provides an overall better experience for end users, plugin maintainers and language authors.

Third party LSP client plugins are available for (Neo)Vim, but the built-in one is easier to setup, guaranteed to be maintained, requires no external dependencies, is fast and is highly reusable.

Treesitter

Neovim also has native support for Treesitter. It is a code intelligence tool, just like LSP, but with different priorities. Instead of requiring an external server to provide information about the code, it uses incremental parsers to extract information from the current buffer. This allows it to be much faster and responsive, at the cost of not providing project-wide features. It can provide features like great syntax highlighting (highly superior to regex-based highlighting in terms of speed and accuracy), limited auto-completion, incremental selection and can define text objects that are specific to the file type that is being edited (like classes and functions in C++, impl blocks in Rust, body tags in HTML, etc.).

Project direction

Besides technical features, the two projects operate in very different ways.

BDFL status

First of all, Vim has a Benevolent Dictator For Life (Bram Moolenaar) whereas Neovim is purely a community effort. This means that the Vim project has a clearer, more consistent vision. It also means that only the features that Bram likes are added to Vim. In practice, the difference is a little more subtle as many features that Bram was opposed to were added to Vim after Neovim implemented them. Vim is still a very centralized project whereas Neovim is highly decentralized.

Attitude regarding new features

Neovim is much more open to adding new features compared to Vim. This includes major features such as the addition of terminal buffers, asynchronous IO, floating windows, LSP and Treesitter integration, but also minor ones like blinking text after it has been copied.

Bad attitude

The development of Neovim has put some pressure on Vim. Features that had been requested for years were added to Neovim. For instance, Vim's documentation explicitly stated that Vim would never support running shells, which is a defensible position. However, Bram awkwardly changed his mind and decided to finally implement a terminal mode, asynchronous IO and floating windows after Neovim did. This is also defensible: he could have realized that those were not bad ideas after all. The issue is that he implemented these features in incompatible ways with Neovim, sabotaging compatibility between the two editors.

Arguments were made about the Neovim interfaces not being good enough. These arguments are weak because any constructive comments about those interfaces would have been considered before those features got released.

In the end, the end users are the ones being punished by these decisions. If Neovim had never happened, the users would still be using a worse, blocking version of Vim providing less features, so Neovim is legitimate, but the users (especially plugin authors) need to either deal with these incompatibilities or pick a side.

Bram also refused to follow Neovim in embedding Lua to solve the shortcomings of VimL. Once again, this is a defensible position. Alternative solutions include making VimL faster or simply deciding that this problem is minor. Instead, Bram decided that Vim9 would ship with a second toy language, based on VimL but incompatible with it. This language will end up being slower than Lua, foreign to all plugin authors at first and will force them to pick sides even more aggressively.