Pueden leer la versión en español aquí.
When the moment comes to "add support for LSP" in neovim lots of people use three key components: the native LSP client, nvim-cmp (a completion engine) and mason.nvim. Making those three things work together takes a non-trivial amount of effort. It's not a problem, is not difficult... it just takes effort, and not everyone is willing to do it.
I'm here to tell you there is an easy way. I created a plugin (lsp-zero) that bundles all the boilerplate code necessary to have nvim-cmp
, mason.nvim
and the native LSP client working with sane defaults.
Update 2023-04-17: Version 2 of lsp-zero was released on March 30th. Read the official tutorial for version 2 if you are interested in using lsp-zero.
The configuration needed for a quick start (using version 1) is going to be very minimal:
local lsp = require('lsp-zero').preset({
name = 'minimal',
set_lsp_keymaps = true,
manage_nvim_cmp = true,
suggest_lsp_servers = false,
})
lsp.setup()
Here is a preview of what you'll get.
See complete video.
Featured in the video:
- Fully functional completion engine (
nvim-cmp
). - Completions provided by the language server (
sumneko_lua
), as well as other sources. - Snippet expansion and navigation between placeholders.
- Diagnostic icon showing in the gutter.
- Showing diagnostic message in a floating window.
- Code actions.
You must be thinking "all of that in 7 lines of code?" Yes... and no. Technically we need to install some dependencies to make it work.
If you are using packer.nvim you would need to add this in your list of plugins.
use {
'VonHeikemen/lsp-zero.nvim',
branch = 'v1.x',
requires = {
-- LSP Support
{'neovim/nvim-lspconfig'},
{
'williamboman/mason.nvim',
run = function() pcall(vim.cmd, 'MasonUpdate') end
},
{'williamboman/mason-lspconfig.nvim'},
-- Autocompletion
{'hrsh7th/nvim-cmp'},
{'hrsh7th/cmp-buffer'},
{'hrsh7th/cmp-path'},
{'saadparwaiz1/cmp_luasnip'},
{'hrsh7th/cmp-nvim-lsp'},
{'hrsh7th/cmp-nvim-lua'},
-- Snippets
{'L3MON4D3/LuaSnip'},
{'rafamadriz/friendly-snippets'},
}
}
We can test all of this by recreating the config I used for the demo.
Starting from scratch
Requirements
First things first, let's name some requirements. What do we need?
- Neovim v0.7.0 or greater.
- For unix systems we need: git, curl or wget, unzip, tar, gzip.
- For windows + powershell: git, tar, and 7zip or peazip or archiver or winzip or WinRAR.
We need all of this because we want to manage our language server from inside neovim.
For the following examples I'll assume you have a linux system. I'll also assume you know how to enter commands inside neovim.
We begin with a little lua
Let's figure out where our config file should live. Open neovim, then use the command :echo stdpath('config')
, it'll show you the path where the lua config should be. In my case it shows /home/dev/.config/nvim
. So now we use neovim to create that file, enter the command.
:edit ~/.config/nvim/init.lua
Let's start with a simple test to make sure everything works. If you press <Tab>
in insert mode you'll notice it takes 8 spaces, we are going to change that by adding this to init.lua.
-- Tab set to two spaces
vim.opt.tabstop = 2
vim.opt.shiftwidth = 2
vim.opt.softtabstop = 2
vim.opt.expandtab = true
We save the file and quit with the command :wq
.
We enter neovim again, go to insert mode then press <Tab>
. If tab expands to two spaces we know everything is fine. If not, you're most likely editing the wrong file.
Plugin manager
To download plugins we are going to use packer
, only because that's what the cool kids are doing these days.
Go to packer.nvim's github repo, in the quickstart section, grab the git clone
command for your operating system. I'll take the linux one:
git clone --depth 1 https://github.com/wbthomason/packer.nvim\
~/.local/share/nvim/site/pack/packer/start/packer.nvim
Now we return to our init.lua
. At the end of the file we add.
require('packer').startup(function(use)
-- Packer can manage itself
use 'wbthomason/packer.nvim'
-- Colorscheme
use 'joshdick/onedark.vim'
end)
A couple of things are happening in here. We are initializing packer
and we are telling it to manage two plugins. The pattern you should notice is: use 'github-user/repo'
, that's all packer needs to download a plugin from github.
At this point we don't need to do the save-quit dance. Save the file with :write
, then evaluate it with :source %
. Now you can install the new plugins. Execute :PackerSync
. A split window will show up telling us the progress of the download. Once the download is finished you can press q
to close the progress window.
To test that everything went well we are going to use the new colorscheme. At the end of init.lua
add.
vim.opt.signcolumn = 'yes'
vim.opt.termguicolors = true
pcall(vim.cmd, 'colorscheme onedark')
Save, evaluate. You'll notice the pretty colors.
LSP Support
Finally, we are in good shape to add the cool LSP features. Okay, we'll add lsp-zero and all its dependencies after the use
statement that has the colorscheme.
Our plugin list should look like this.
require('packer').startup(function(use)
-- Packer can manage itself
use 'wbthomason/packer.nvim'
-- Colorscheme
use 'joshdick/onedark.vim'
-- LSP
use {
'VonHeikemen/lsp-zero.nvim',
branch = 'v1.x',
requires = {
-- LSP Support
{'neovim/nvim-lspconfig'},
{
'williamboman/mason.nvim',
run = function() pcall(vim.cmd, 'MasonUpdate') end
},
{'williamboman/mason-lspconfig.nvim'},
-- Autocompletion
{'hrsh7th/nvim-cmp'},
{'hrsh7th/cmp-buffer'},
{'hrsh7th/cmp-path'},
{'saadparwaiz1/cmp_luasnip'},
{'hrsh7th/cmp-nvim-lsp'},
{'hrsh7th/cmp-nvim-lua'},
-- Snippets
{'L3MON4D3/LuaSnip'},
{'rafamadriz/friendly-snippets'},
}
}
end)
Save the file, evaluate (again). Install with :PackerSync
.
Add the configuration for lsp-zero
.
local lsp = require('lsp-zero').preset({
name = 'minimal',
set_lsp_keymaps = true,
manage_nvim_cmp = true,
suggest_lsp_servers = false,
})
lsp.setup()
Save and quit neovim.
Open neovim again. Now try to edit init.lua
. Use :edit ~/.config/nvim/init.lua
. Then use the command :LspInstall
. Now mason.nvim
will suggest a language server.
Please select which server you want to install for filetype "lua":
1: lua_ls
Type number and <Enter> or click with the mouse (q or empty cancels):
Choose 1 for lua_ls
, then press enter. A floating window appear, it will show the progress of the installation.
At the moment there is a good chance the language server can't start automatically after install. Use the command :edit
to refresh the file or restart neovim if that doesn't work. Once the server starts you'll notice warning signs in the global variable vim
, that means everything is well and good.
If you wanted to, you could add a completion source and setup lua_ls
specifically for neovim, all with one line of code.
lsp.nvim_workspace()
You need to add it before calling .setup()
.
local lsp = require('lsp-zero').preset({
name = 'minimal',
set_lsp_keymaps = true,
manage_nvim_cmp = true,
suggest_lsp_servers = false,
})
lsp.nvim_workspace()
lsp.setup()
That's it. You are all set. Exit and open neovim again, you should have full support for neovim's lua api.
Complete Example
---
-- Settings
---
-- Tab set to two spaces
vim.opt.tabstop = 2
vim.opt.shiftwidth = 2
vim.opt.softtabstop = 2
vim.opt.expandtab = true
-- Give me space
vim.opt.signcolumn = 'yes'
---
-- Plugins
---
require('packer').startup(function(use)
-- packer can update itself
use 'wbthomason/packer.nvim'
-- colorscheme
use 'joshdick/onedark.vim'
-- LSP
use {
'VonHeikemen/lsp-zero.nvim',
branch = 'v1.x',
requires = {
-- LSP Support
{'neovim/nvim-lspconfig'},
{
'williamboman/mason.nvim',
run = function() pcall(vim.cmd, 'MasonUpdate') end
},
{'williamboman/mason-lspconfig.nvim'},
-- Autocompletion
{'hrsh7th/nvim-cmp'},
{'hrsh7th/cmp-buffer'},
{'hrsh7th/cmp-path'},
{'saadparwaiz1/cmp_luasnip'},
{'hrsh7th/cmp-nvim-lsp'},
{'hrsh7th/cmp-nvim-lua'},
-- Snippets
{'L3MON4D3/LuaSnip'},
{'rafamadriz/friendly-snippets'},
}
}
end)
-- Setup colorscheme
vim.opt.termguicolors = true
pcall(vim.cmd, 'colorscheme onedark')
-- LSP setup
local lsp = require('lsp-zero').preset({
name = 'minimal',
set_lsp_keymaps = true,
manage_nvim_cmp = true,
suggest_lsp_servers = false,
})
lsp.nvim_workspace()
lsp.setup()
What's next?
You should checkout lsp-zero v1 on github. Read the docs either on github or the help page in neovim :help lsp-zero
.
Learn the default keybindings:
Take a look at a more advance setup, to learn how to configure LSP servers.
Also, read the documentation of mason.nvim.
Ask me anything about lsp-zero
here, in the discussion tab on github, or matrix #lsp-zero-nvim:matrix.org.
Thank you for your time. If you find this article useful and want to support my efforts, consider leaving a tip in buy me a coffee ☕.
Top comments (2)
Here is the same example config written in vimscript + vim-plug.
Thank you so much for this. I have followed your article and have finally switched to init.lua. It was easier than I initially thought. Better yet, I have all the nice features of LSP ans snippets easily available. Thanks again.