Neovim Plugins - Enhancing Your Neovim Editor with Awesome Plugins
Although Neovim supports most Vim plugins, it also comes with full support for Lua as a language for plugin authoring. This choice has led to a renaissance of sorts in the world of Neovim plugins. This article has a collection of plugins that I’ve found useful and interesting, and also some guidance about how to write your own plugins.
Table of Contents
Plugins
- UI and User experience
- noice.nvim is a highly experimental plugin that takes advantage of some of the latest neovim features to completely replace the UI for messages, cmdline and the popupmenu.
- nvim-notify is a notification manager for neovim (noice uses it to show fancy notifications)
- mini.nvim is a collection of modules that improve neovim’s user experience in small ways:
- mini.pairs provides minimal and fast autopairs (for symbols like quotes or parenthesis)
- bufferline.nvim provides a “bufferline”, which is a tab-like user interface where open buffers appear at the top of the screen as tabs.
- vim-illuminate automatically highlights other uses of the word under the cursor using either the neovim LSP, Treesitter or reg-ex matching.
- Fuzzy finders
- telescope.nvim is a fuzzy finder for Neovim
- Motions and moving around quickly
- Coding
- LuaSnip is a powerful snippet engine written in lua.
- Plugin managers
- packer.nvim is a feature rich package manager for Neovim.
- lazy.nvim is a modern plugin manager for Neovim by the famed folke@
- Language Server Protocol. LSP plugins bring IDE-like functionality into Neovim.
- nvim-lspconfig provides a collection of LSP configurations for many programming languages.
- mason is a package manager for neovim that lets you install and update LSP servers, DAP servers, linters and formatters.
- nvim-cmp is a completion engine for Neovim written in lua. It’s the de facto standard completion plugin for the native LSP client.
- trouble.nvim for a pretty list of diagnostics and other lists (LSP symbols, quickfix list, location list, etc).
- coc.nvim provides an alternative experience to the native LSP client and family of plugins. With coc.nvim you get everything within the same plugin: LSP client configuration, LSP server installation, completion, diagnostics, formatters, etc.
- Lua programming
- The nlua.nvim improves your lua development experience in Vim
- The plenary.nvim is a Neovim plugin to help you write plugins for neovim in lua.
- neodev.nvim is a plugin for setting up Neovim setup for init.lua and plugin development with full signature help, docs and completion for the nvim lua API. This is an alternative to nlua.nvim.
- nvim-luaref gives you a reference for builtin Lua functions within neovim.
- More lua development plugins
- AI plugins
- Distributions
- A distribution is a preconfigured version of neovim that comes with a number of bundled plugins, mappings and configuration.
- LazyVim is a lightweight neovim setup with a number of configured plugins and a flexible configuration and extension model. For my personal LazyVim setup read this other article.
- Kickstart.nvim is a starting configuration for Neovim with lots of functionality in under 500 lines.
- More neovim plugins can be found in awesome-neovim.
Below there are some extended summaries about different plugins, useful commands and recommended mappings.
UI and User Experience
Noice
noice.nvim is a highly experimental plugin that takes advantage of some of the latest neovim features to completely replace the UI for messages, cmdline and the popupmenu:
- Instead of the normal vim/neovim cmdline, noice adds a VSCode-like command window to run your ex-commands. When you type a search or a filter the command window updates its icon to reflect a search or a filter. It also provides syntax highlighting.
- Instead of the traditional
:messages
that brings a non-interactive window with a more prompt, noice puts:messages
into a normal buffer - As a enhancement over
:messages
, noice has the:Noice
command that provides a more aesthetically pleasing full messages history. - You can open the message history with telescope and fuzzy find stuff using
:Noice telescope
.
Some useful commands are:
:Noice " shows the message history
:Noice history " ditto
:Noice last " shows the last message in a popup
:Noice errors " shows the error messages in a split. Last errors on top
:Noice disable " disables Noice
:Noice enable " enables Noice
:Noice stats " shows debugging stats
:Noice telescope " opens message history in Telescope
And there’s a lot more.
Bufferline
bufferline.nvim adds a buffer line to neovim, a tab-like user interface where every buffer looks like a tab with additional UI indicators like file types, lsp diagnostics, etc. Aside from the helpful indicators one can also:
- Pin buffers
- Group buffers
- Re-order buffers
- Move quickly between buffers using
H
andL
- Interact with all buffers with a mouse
vim-illuminate
vim-illuminate automatically highlights other uses of the word under the cursor using either the neovim LSP, Treesitter or reg-ex matching.
You can also use the following mappings to jump between matches:
<a-n>
jump to next match<a-p>
jump to previous match<a-i>
as a text object that represents the match e.g.d<a-i>
deletes the match
For more information, take a look at the vim-illuminate documentation.
Fuzzy finders
Telescope nvim
Telescope.nvim is a fuzzy find matcher for Neovim centered around modularity and extensibility. It builds on the mental model of pickers, sorters and previewers, all of which can be combined with each other. It comes with a great number of built-in pickers and supports additional extensions.
Here is an example of some helpful built-in pickers:
" File system
:Telescope find_files -- fuzzy file find in current working directory
:Telescope live_grep -- search for a string in current working directory live as you type
...
" Vim
:Telescope buffers -- fuzzy finding over opened buffers
:Telescope oldfiles -- fuzzy finding over previously opened files
:Telescope help -- fuzzy finding over neovim help keywords
:Telescope quickfix -- fuzzy finding over your quickfix list
:Telescope command_history -- fuzzy finding over your command history
:Telescope search_history -- fuzzy finding over your search history
...
" LSP
:Telescope lsp_references -- fuzzy find for references for word under cursor
:Telescope diagnostics -- fuzzy find over diagnostics
...
" Treesitter
:Telescope treesitter -- fuzzy find over treesitter objects
Some of the most useful extensions are:
- telescope-fzf-native.nvim: Native FZF sorter that uses compiled C to do the matching, supports fzf syntax
- telescope-coc.nvim: Integration between coc.nvim and telescope. It allows you to use telescope to find, filter, preview and pick results from coc.nvim.
- telescope-emoji.nvim. An emoji picker for telescope.
- telescope-tmux.nvim. A telescope.nvim extension for finding tmux targets like sessions or panes.
Some recommended mappings:
-- From https://github.com/nvim-lua/kickstart.nvim
-- See `:help telescope.builtin`
vim.keymap.set('n', '<leader>?', require('telescope.builtin').oldfiles, { desc = '[?] Find recently opened files' })
vim.keymap.set('n', '<leader><space>', require('telescope.builtin').buffers, { desc = '[ ] Find existing buffers' })
vim.keymap.set('n', '<leader>/', function()
-- You can pass additional configuration to telescope to change theme, layout, etc.
require('telescope.builtin').current_buffer_fuzzy_find(require('telescope.themes').get_dropdown {
winblend = 10,
previewer = false,
})
end, { desc = '[/] Fuzzily search in current buffer]' })
vim.keymap.set('n', '<leader>sf', require('telescope.builtin').find_files, { desc = '[S]earch [F]iles' })
vim.keymap.set('n', '<leader>sh', require('telescope.builtin').help_tags, { desc = '[S]earch [H]elp' })
vim.keymap.set('n', '<leader>sw', require('telescope.builtin').grep_string, { desc = '[S]earch current [W]ord' })
vim.keymap.set('n', '<leader>sg', require('telescope.builtin').live_grep, { desc = '[S]earch by [G]rep' })
vim.keymap.set('n', '<leader>sd', require('telescope.builtin').diagnostics, { desc = '[S]earch [D]iagnostics' })
For additional extensions take a look at the telescope wiki.
Motions and Moving Around Quickly
Leap
leap.nvim is a plugin similar to vim-sneak that adds labels to the targets and allows motions across files. To use it:
- Type
s
to leap forward,S
to leap backwards orgs
to leap into another buffer window - Type two characters that mark your motion target. E.g. if you want to jump to
banana
you could typesba
- If there’s only a match you jump immediately there, otherwise you jump to the first match and see a number of labels that you can jump directly to by typing the label (e.g.
s
). Alternatively use;
to jump to the next match,
to jump backwards. (Like withf
andt
)
For more info take a look at the leap.nvim documentation.
Flit
flit enhances the f/F
and t/T
motions in neovim by providing visual feedback and label across multiple lines. Essentially it’s like leap.nvim but instead of using a special command s
it extends the behavior of the built-in f
and t
motions.
Coding
LuaSnip
LuaSnip is a powerful snippet engine written in lua.
How Do Snippets Work in LuaSnip?
Creating a Snippet
If you want to learn more about LuaSnip, the official docs have lots of useful information and interactive gifs that showcase LuaSnip in action. Alternatively, take a look at these resources for new LuaSnip users.
Language Server Protocol
The Minimal LSP Setup
For a minimal LSP setup for neovim I recommend the following plugins:
- nvim-lspconfig provides a collection of LSP configurations for many programming languages.
- mason is a package manager for neovim that lets you install and update LSP servers, DAP servers, linters and formatters.
- mason-lspconfig integrates mason with nvim-lspconfig.
- nvim-cmp is a completion engine for Neovim written in lua. It’s the de facto standard completion plugin for the native LSP client. It provides completions by using different sources like the LSP, buffers, path, emojis, etc. Sources are typically provided by additional plugins.
- trouble.nvim for a pretty list of diagnostics and other lists (LSP symbols, quickfix list, location list, etc).
This means that nvim-lspconfig will manage all your LSP configurations, mason will let you install and update LSP servers, nvim-cmp will provide you with completions using the LSP and other sources, and trouble.nvim will give you beautiful lists of diagnostics.
Alternatively, you can use the lsp-zero which bundles all of these plugins (and some other ones) with a minimal configuration.
All-in-one alternative
On the other side of the spectrum you have another alternative: coc.nvim. One plugin with a simple configuration that brings everything you need:
- LSP client configuration and server installation in the form of coc extensions
- completion
- code actions
- diagnostics
- formatters
- and more…
Plugin Managers
Lazy.nvim
For more information about lazy.nvim take a look at my notes on LazyVim.
Packer.nvim
packer.nvim is a feature rich package manager for neovim that takes advantage of vim/neovim built-in package system (:h packages
). An example configuration for packer looks like this:
-- This file can be loaded by calling `lua require('plugins')` from your init.vim
-- Only required if you have packer configured as `opt`
vim.cmd [[packadd packer.nvim]]
return require('packer').startup(function(use)
-- Packer can manage itself
use 'wbthomason/packer.nvim'
-- Simple plugins can be specified as strings
use 'rstacruz/vim-closer'
-- etc
end)
When you’ve configured it you can run the following commands to manage and update your plugins:
-- You must run this or `PackerSync` whenever you make changes to your plugin configuration
-- Regenerate compiled loader file
:PackerCompile
-- Remove any disabled or unused plugins
:PackerClean
-- Clean, then install missing plugins
:PackerInstall
-- Clean, then update and install plugins
-- supports the `--preview` flag as an optional first argument to preview updates
:PackerUpdate
-- Perform `PackerUpdate` and then `PackerCompile`
-- supports the `--preview` flag as an optional first argument to preview updates
:PackerSync
-- Show list of installed plugins
:PackerStatus
-- Loads opt plugin immediately
:PackerLoad completion-nvim alek
See packer.nvim docs on GitHub for more information about how to install and configure it.
AI plugins
Copilot
Two useful plugins to integrate copilot in neovim are:
- zbirenbaum/copilot.lua, a copilot client for neovim written in lua
- zbirenbaum/copilot-cmp which adds a copilot source to nvim-cmp.
- An script to add copilot to lualine.
For additional configurations for the copilot panel, suggestions, filetypes supported, etc, take a look at the zbirenbaum/copilot.lua docs.
If you are using LazyVim you can add copilot as an extra with one single step.
Writing plugins for neovim
The official way to write neovim plugins is to use the lua programming language.
Creating a simple plugin
A simple way to get started writing plugins in lua is to start with a simple function:
function Hello() print("hello world!") end
You can make neovim aware of this function by either sourcing the buffer :so %
or by loading it with the :lua
command like :lua function Hello() print("....") end
.
Now you can call the function :lua Hello()
and you’ll see how it prints the "hello world!
as a message.
A simple way to reuse this functionality and have it be ready to use the next time you open neovim is to create a user command:
Creating a custom user command
In traditional vim we can create user commands using the :command
ex-command. With neovim, in addition to that, we can create user commands from lua using the nvim_create_user_command
command:
vim.api.nvim_create_user_command("Hello", Hello, {})
Again to execute this code either source the buffer :so %
or use the :lua
command :lua vim.api.nvim_create_user_command("Hello", Hello, {})
.
Now you can use the :Hello
user defined command to print the "Hello world!"
message.
Creating auto commands
In traditional vim we can create autocommands using the :autocmd
or :au
ex-command. With neovim, in addition to that, we can create autocommands from lua using the nvim_create_autocmd
api:
vim.api.nvim_create_autocmd("CursorHold", { callback = Hello})
This will call the Hello
function every time you hold your cursor for a little bit. You can find which events you can subscribe to using :h {event}
.
Creating a key mapping
In traditional vim we can create mappings using the family of mapping commands :map
, :nmap
, :vmap
, :noremap
, :nnoremap
, etc. With neovim, in addition to that, we can create key mappings using the keymap.set
api:
vim.keymap.set("n", "<leader>h", Hello());
The anatomy of a neovim plugin
Now let’s say that you want to distribute your plugin so others can use it. In order to achieve that we first need to create the plugin in a shape that neovim can understand.
The most minimal lua neovim plugin has the following anatomy:
-- helloworld/
|
|-- lua/ # this is where you put the source code of your plugin
| |
| |
| |------- helloworld.lua
|
|-- plugin/ # lua files in this folder gets executed when neovim loads your plugin
| # this is where you'd put initialization logic like registering mappings
# or commands for your plugin
|
|------- helloworld.lua
Where the lua/helloworld.lua
is a lua module that encapsulates the source code for your plugin:
-- This is the convention lua uses to define modules
local M = {}
-- Here comes your plugin code
M.hello = function() print("hello world!") end
-- here we return the lua table that represents the API of this module to consumers of the plugin
return M
And the plugin/helloworld.lua
is a lua module that gets executed by neovim when your plugin is loaded (often on neovim startup) and which can setup your plugin:
-- Here we can do plugin initialization
-- Like register custom commands or mappings
-- We start by importing our plugin functionality using require
-- require("helloworld") will look for lua modules named:
-- * helloworld.lua
-- * helloworld/init.lua
-- require("helloworld.strings") will look for lua modules named:
-- * helloworld/strings.lua
-- * helloworld/strings/init.lua
local hello = require("helloworld").hello
vim.api.nvim_create_user_command("Hello", hello, {})
vim.keymap.set("n", "<leader>h", hello);
Installing your plugin
Everytime you start neovim, neovim looks in special paths (the runtime path and packpath) and loads plugins that it finds at those locations. So in order to install your plugin (make neovim aware of your plugin) you need to add your plugin to your runtime path or packpath. There’s different ways to achieve this, you can do it through a package manager which normally simplifies things, or manually add it to your runtime path or packpath (by creating a new package for your plugin). In summary:
- Use a package manager. Some package managers like packer.nvim allow you to specify path. You can tell the package manager to install your plugin by using relative path to wherever you’re working on it.
- Create a package
my-plugins
where you can develop your own plugins (e.g.{neovim_config_dir}/pack/my-plugins/start/my-plugin
). Any plugin that lives in that package will be automatically loaded by neovim on startup. See:h package
for more information. - Add the plugin folder to your runtime path by opening neovim with a
--cmd "set rtp+=~/plugin-folder"
or running:set rtp+=~/plugin-folder
from within neovim.
Testing plugins
plenary.nvim is a neovim plugin to help you write plugins for neovim in lua. It comes with a simple lua testing framework that let’s you write tests for your lua plugins.
Start by creating a folder for your tests and a file for your test:
-- helloworld/
|
|-- lua/
| |------- helloworld.lua
|-- plugin/
|------- helloworld.lua
|-- tests/
|------- helloworld_spec.lua -- every plenary spec must end with _spec.lua
Now you can write the actual test:
describe("hello", function()
it("can be required", function()
require("helloworld")
end)
it("prints hello world", function()
require("helloworld").hello()
end)
it("hello world returns hello world", function()
-- after updating the function to return the message so we
-- can assert it and showcase how this works :D
local helloWorld = require("helloworld").hello()
-- asserts work with the luassert library:
-- https://github.com/lunarmodules/luassert
assert.are.same("hello world", helloWorld)
end)
end)
To run this spec you can use the :PlenaryTestFile
that runs the test in the current buffer. This spec will run in a new instance of neovim in isolation from your current session.
You can find the full documentation for writing tests with plenary on GitHub.
Additional tips
Useful lua utilities
:lua vim.inspect(table) -- prints a table
:source % -- source current file. Useful to run lua script under development.
Reloading cached modules
When using require
to require lua modules the required module is cached. That means that if you’re working developing a plugin and want to have neovim evaluate your updated function calling require
again won’t work since the lua runtime will have cached the module already. Under these circumstances you’ll likely want to refresh the cache so you can re-require that module with your latest changes. You can do that like so:
package.loaded.mymodule = nil
-- alternatively:
-- package.loaded['mymodule'] = nil
-- loads latest version of module 'mymodule'
require('mymodule')
plenary.nvim is a neovim plugin to help you write plugins for neovim in lua. It comes with a number of helper functions amongst which there’s one to refresh a lua module: reload:
require("plenary.reload").reload_module "mymodule"
Useful lua functions to have in your config
P = function(value)
print(vim.inspect(value))
return value
end
RELOAD = function(...)
return require("plenary.reload").reload_module(...)
end
R = function(name)
RELOAD(name)
return require(name)
end
More information
Here are some interesting resources to help you get started writing your own plugins for neovim:
- nvim lua guide
- Learn lua in Y minutes
- Articles
- Videos
You can find all information about lua plugins in neovim taking a look at the help:
:h lua
:h vim.api
Using Vim plugins with Neovim
Neovim still has full compatibility with Vim plugins. Take a look at the Vim plugins for more interesting plugins that are not written in lua but still supported in Neovim.
Resources
Written by Jaime González García , dad, husband, software engineer, ux designer, amateur pixel artist, tinkerer and master of the arcane arts. You can also find him on Twitter jabbering about random stuff.