Collection of functions that will help you setup Neovim's LSP client, so you can get IDE-like features with minimum effort.
Out of the box it will help you integrate nvim-cmp (an autocompletion plugin) and nvim-lspconfig (a collection of configurations for various language servers). So a minimal config can look like this.
require('lsp-zero')
require('lspconfig').lua_ls.setup({})
-- don't copy/paste this if you don't know what is `lua_ls`.
-- yes, lsp-zero has changed since ThePrimeagen released his video "0 to LSP".
With this code when lua_ls
(a language server) is active you'll get all the features Neovim offers by default plus autocompletion.
If you came here from a tutorial read the migration guide section
Expand: What happens under the hood?
When require('lsp-zero')
is called this is what happens:
- lsp-zero makes sure the configuration provided by cmp-nvim-lsp is applied to every language server configured by
nvim-lspconfig
. - Sets up a "backup" configuration for the essential options in nvim-cmp. So autocompletion can work even if the user forgets something important.
- Reserves a space for the signcolumn.
- Adds border to floating windows on diagnostics, the documentation window of the hover handler and signature help handler.
Here's the simplified code of these steps. In case you want to understand it without looking at source code of the plugin itself.
Note that lsp-zero offers more features but those are opt-in, see usage section.
If you are new to neovim and you don't have a configuration file (init.lua
) follow this step by step tutorial.
If you know how to configure neovim go to Quickstart (for the impatient).
Also consider you might not need lsp-zero.
Feel free to open a new discussion in this repository. Or join the chat #lsp-zero-nvim:matrix.org.
If you have problems with a language server read this guide: What to do when the language server doesn't start?.
v3.x
is the current version of lsp-zero. If you are using a previous version follow one of these guides.
- Migrate from v1.x branch
- Migrate from v2.x branch
- ThePrimeagen's 0 to LSP config
- Migrate away from lsp-zero
-
LSP
- How does it work?
- Commands
- Creating new keybindings
- Disable keybindings
- Install new language servers
- Configure language servers
- Disable semantic highlights
- Exclude a language server from the automatic setup
- Custom servers
- Enable Format on save
- Format buffer using a keybinding
- Format buffer using random cli tool
- Use icons in the sign column
- What to do when a language server doesn't start?
-
Autocompletion
- How does it work?
- Keybindings
- Use Enter to confirm completion
- Adding a source
- Add an external collection of snippets
- Preselect first item
- Basic completions for Neovim's lua api
- Enable "Super Tab"
- Regular tab complete
- Invoke completion menu manually
- Adding borders to completion menu
- Change formatting of completion item
- lsp-kind
-
Reference and guides
- API Reference
- Tutorial: Step by step setup from scratch
- What to do when the language server doesn't start?
- Migrate from v1.x branch
- Migrate from v2.x branch
- lsp-zero under the hood
- You might not need lsp-zero
- Lazy loading with lazy.nvim
- Integrate with mason.nvim
- Enable folds with nvim-ufo
- Enable inlay hints with lsp-inlayhints.nvim
- Setup copilot.lua + nvim-cmp
- Setup with nvim-navic
- Setup with rustaceanvim
- Setup with flutter-tools
- Setup with nvim-jdtls
- Setup with nvim-metals
- Setup with haskell-tools
lsp-zero requires Neovim v0.8 or greater. If you need support for Neovim v0.7 use the branch compat-07.
If you know your way around Neovim and how to configure it, take a look at this examples:
- Lua template configuration
- Vimscript template configuration
- ThePrimeagen's "0 to LSP" config adapted to version 3
The following sections will show how to create a basic configuration.
Use your favorite plugin manager to install this plugin and all its lua dependencies.
Expand: lazy.nvim
For a more advance config that lazy loads everything take a look at the example on this link: Lazy loading guide.
--- Uncomment the two plugins below if you want to manage the language servers from neovim
-- {'williamboman/mason.nvim'},
-- {'williamboman/mason-lspconfig.nvim'},
{'VonHeikemen/lsp-zero.nvim', branch = 'v3.x'},
{'neovim/nvim-lspconfig'},
{'hrsh7th/cmp-nvim-lsp'},
{'hrsh7th/nvim-cmp'},
{'L3MON4D3/LuaSnip'},
Expand: packer.nvim
use {
'VonHeikemen/lsp-zero.nvim',
branch = 'v3.x',
requires = {
--- Uncomment the two plugins below if you want to manage the language servers from neovim
-- {'williamboman/mason.nvim'},
-- {'williamboman/mason-lspconfig.nvim'},
-- LSP Support
{'neovim/nvim-lspconfig'},
-- Autocompletion
{'hrsh7th/nvim-cmp'},
{'hrsh7th/cmp-nvim-lsp'},
{'L3MON4D3/LuaSnip'},
}
}
Expand: paq.nvim
{'VonHeikemen/lsp-zero.nvim', branch = 'v3.x'};
--- Uncomment the two plugins below if you want to manage the language servers from neovim
-- {'williamboman/mason.nvim'};
-- {'williamboman/mason-lspconfig.nvim'};
-- LSP Support
{'neovim/nvim-lspconfig'};
-- Autocompletion
{'hrsh7th/nvim-cmp'};
{'hrsh7th/cmp-nvim-lsp'};
{'L3MON4D3/LuaSnip'};
Expand: vim-plug
" Uncomment the two plugins below if you want to manage the language servers from neovim
" Plug 'williamboman/mason.nvim'
" Plug 'williamboman/mason-lspconfig.nvim'
" LSP Support
Plug 'neovim/nvim-lspconfig'
" Autocompletion
Plug 'hrsh7th/nvim-cmp'
Plug 'hrsh7th/cmp-nvim-lsp'
Plug 'L3MON4D3/LuaSnip'
Plug 'VonHeikemen/lsp-zero.nvim', {'branch': 'v3.x'}
When using vimscript you can wrap lua code in lua <<EOF ... EOF
.
" Don't copy this example
lua <<EOF
print('this an example code')
print('written in lua')
EOF
First thing you will want to do setup some default keybindings. The common convention here is to setup these keybindings only when you have a language server active in the current file. Here is the code to achieve that.
local lsp_zero = require('lsp-zero')
lsp_zero.on_attach(function(client, bufnr)
-- see :help lsp-zero-keybindings
-- to learn the available actions
lsp_zero.default_keymaps({buffer = bufnr})
end)
-- here you can setup the language servers
Next step is to install a language server. Go to nvim-lspconfig's documentation, in the server_configuration.md file you'll find a list of language servers and how to install them.
Once you have a language server installed in your system, add the setup in your Neovim config. Use the module lspconfig
, like this.
require('lspconfig').example_server.setup({})
--- in your own config you should replace `example_server`
--- with the name of a language server you have installed
If you need to customize the language server, add your settings inside the {}
. To know more details about lspconfig use the command :help lspconfig
or click here.
If you did install lua_ls
and you want to configure it specifically for Neovim these are your options.
If you decided to install mason.nvim and mason-lspconfig.nvim you can manage the installation of the language servers from inside Neovim, and then use lsp-zero to handle the configuration.
Here is a basic usage example.
local lsp_zero = require('lsp-zero')
lsp_zero.on_attach(function(client, bufnr)
-- see :help lsp-zero-keybindings
-- to learn the available actions
lsp_zero.default_keymaps({buffer = bufnr})
end)
-- to learn how to use mason.nvim with lsp-zero
-- read this: https://github.com/VonHeikemen/lsp-zero.nvim/blob/v3.x/doc/md/guides/integrate-with-mason-nvim.md
require('mason').setup({})
require('mason-lspconfig').setup({
ensure_installed = {},
handlers = {
lsp_zero.default_setup,
},
})
If you need to configure a language server installed by mason.nvim
, add a "handler function" to the handlers
option. Something like this:
require('mason-lspconfig').setup({
ensure_installed = {},
handlers = {
lsp_zero.default_setup,
--- replace `example_server` with the name of a language server
example_server = function()
--- in this function you can setup
--- the language server however you want.
--- in this example we just use lspconfig
require('lspconfig').example_server.setup({
---
-- in here you can add your own
-- custom configuration
---
})
end,
},
})
For more details about how to use mason.nvim with lsp-zero see the guide on how to integrate with mason.nvim.
If you choose to use the function .default_keymaps() you'll be able to use Neovim's built-in functions for various actions. Things like jump to definition, rename variable, format current file, and some more.
Note that the keybindings have to be enabled explicitly, like this.
local lsp_zero = require('lsp-zero')
lsp_zero.on_attach(function(client, bufnr)
lsp_zero.default_keymaps({buffer = bufnr})
end)
Here's the full list:
-
K
: Displays hover information about the symbol under the cursor in a floating window. See :help vim.lsp.buf.hover(). -
gd
: Jumps to the definition of the symbol under the cursor. See :help vim.lsp.buf.definition(). -
gD
: Jumps to the declaration of the symbol under the cursor. Some servers don't implement this feature. See :help vim.lsp.buf.declaration(). -
gi
: Lists all the implementations for the symbol under the cursor in the quickfix window. See :help vim.lsp.buf.implementation(). -
go
: Jumps to the definition of the type of the symbol under the cursor. See :help vim.lsp.buf.type_definition(). -
gr
: Lists all the references to the symbol under the cursor in the quickfix window. See :help vim.lsp.buf.references(). -
gs
: Displays signature information about the symbol under the cursor in a floating window. See :help vim.lsp.buf.signature_help(). If a mapping already exists for this key this function is not bound. -
<F2>
: Renames all references to the symbol under the cursor. See :help vim.lsp.buf.rename(). -
<F3>
: Format code in current buffer. See :help vim.lsp.buf.format(). -
<F4>
: Selects a code action available at the current cursor position. See :help vim.lsp.buf.code_action(). -
gl
: Show diagnostics in a floating window. See :help vim.diagnostic.open_float(). -
[d
: Move to the previous diagnostic in the current buffer. See :help vim.diagnostic.goto_prev(). -
]d
: Move to the next diagnostic. See :help vim.diagnostic.goto_next().
By default lsp-zero will not create a keybinding if its "taken". This means if you already use one of these in your config, or some other plugins uses it (which-key might be one), then lsp-zero's bindings will not work.
You can force lsp-zero's bindings by adding preserve_mappings = false
to .default_keymaps().
lsp_zero.on_attach(function(client, bufnr)
lsp_zero.default_keymaps({
buffer = bufnr,
preserve_mappings = false
})
end)
When you open a file compatible with a language server lspconfig
will search for a set of files in the current folder or any of the parent folders. If it finds them, the language server will start analyzing that folder. So the "root directory" is basically your project folder.
Some language servers have "single file support" enabled, this means if lspconfig
can't determine the root directory then the current working directory becomes your root directory.
If your language server doesn't attach to a file, make sure the file and the project folder meet the requirements of the language server.
How do you know what are the requirements? Search the list of language servers and read their documentation, or inspect the configuration provided by lspconfig using the command LspZeroViewConfigSource
.
For example, this command will open the configuration for the lua language server.
LspZeroViewConfigSource lua_ls
The plugin responsable for autocompletion is nvim-cmp. The default config in lsp-zero will only add the minimum required to integrate lspconfig, nvim-cmp and luasnip.
The default keybindings in lsp-zero are meant to emulate Neovim's default whenever possible.
-
<Ctrl-y>
: Confirms selection. -
<Ctrl-e>
: Cancel completion. -
<Down>
: Navigate to the next item on the list. -
<Up>
: Navigate to previous item on the list. -
<Ctrl-n>
: If the completion menu is visible, go to the next item. Else, trigger completion menu. -
<Ctrl-p>
: If the completion menu is visible, go to the previous item. Else, trigger completion menu.
To add more keybindings I recommend you use nvim-cmp directly.
Here is an example configuration.
local cmp = require('cmp')
local cmp_action = require('lsp-zero').cmp_action()
cmp.setup({
mapping = cmp.mapping.preset.insert({
-- `Enter` key to confirm completion
['<CR>'] = cmp.mapping.confirm({select = false}),
-- Ctrl+Space to trigger completion menu
['<C-Space>'] = cmp.mapping.complete(),
-- Navigate between snippet placeholder
['<C-f>'] = cmp_action.luasnip_jump_forward(),
['<C-b>'] = cmp_action.luasnip_jump_backward(),
-- Scroll up and down in the completion documentation
['<C-u>'] = cmp.mapping.scroll_docs(-4),
['<C-d>'] = cmp.mapping.scroll_docs(4),
})
})
Some language servers already provide basic snippets, but maybe this is not enough for you. Or maybe the language server you are using doesn't provide snippets. In this case you have the option to install an external collection of snippets, like these ones.
These can be installed like any other Neovim plugin. But don't install both, you'll get duplicate snippets.
Anyway, the process to load these snippets into your completion menu involves a few steps so the details are in the autocomplete documentation:
Changed/Removed features from the v2.x
branch.
Note: You can disable the warnings about removed functions by setting the global variable lsp_zero_api_warnings
to 0
. Before you require the module lsp-zero, put this vim.g.lsp_zero_api_warnings = 0
.
- .preset() was removed. Most settings were remove. The remaining settings can be changed using global variables, see global variables.
- .set_preferences() was removed in favor of overriding option directly in the .preset().
- .setup_nvim_cmp() was be removed. Use the
cmp
module to customize nvim-cmp. - .setup_servers() will no longer take an options argument. It'll only be a convenient way to initialize a list of servers.
- .default.diagnostics() was removed. Diagnostics are not configured anymore.
- .defaults.cmp_sources() was removed. Sources for nvim-cmp should be handled by the user.
- .defaults.cmp_mappings() was removed. All nvim-cmp mappings can be overriden using the
cmp
module. - .nvim_workspace() was removed. Use .nvim_lua_ls() to get the config for the lua language server.
- .defaults.nvim_workspace() was replaced by .nvim_lua_ls().
- .ensure_installed() was removed. Use the module
mason-lspconfig
to install the language servers. - .new_server() was renamed to .new_client().
You have two choices, and the details about them are on this guide: lua_ls for Neovim.
Yes, you can. You can find the details in the autocomplete documentation: Enter key to confirm completion.
Yes. Full answer is here.
I hope you mean custom snippets like friendly snippets, 'cause some language servers already provide snippets. Anyway, the answer is here.
If you find this tool useful and want to support my efforts, buy me a coffee ☕.