DEV Community

Heiker
Heiker

Posted on • Updated on

Getting started with neovim's native LSP client in the year of 2022: The easy way

Pueden leer la versión en español aquí.

In 2022 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 nvim-lsp-installer. 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, nvim-lsp-installer and the native LSP client working with sane defaults. And the best part, the configuration needed for a quick start is going to be very minimal:

local lsp = require('lsp-zero')
lsp.preset('recommended')

lsp.setup()
Enter fullscreen mode Exit fullscreen mode

Here is a preview of what you'll get.

Demo screenshot. Completion menu with two items

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 3 lines of code?" Yes... and no. Technically we need to install some dependencies to make it work, so it takes more than 3 lines of code.

If you are using packer.nvim you would need to add this in your list of plugins.

use {
  'VonHeikemen/lsp-zero.nvim',
  requires = {
    -- LSP Support
    {'neovim/nvim-lspconfig'},
    {'williamboman/nvim-lsp-installer'},

    -- 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'},
  }
}
Enter fullscreen mode Exit fullscreen mode

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.6.1 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
Enter fullscreen mode Exit fullscreen mode

Let's start with a simple test to make sure everything works. If you press <Tab> 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
Enter fullscreen mode Exit fullscreen mode

We save the file and quit with the command :wq.

We enter neovim again and 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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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')
Enter fullscreen mode Exit fullscreen mode

Save, evaluate. You'll notice the pretty colors.

LSP Support

Finally, we are in good shape to add the cool LSP features. Remember that list of dependencies? Okay, we'll add it 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',
    requires = {
      -- LSP Support
      {'neovim/nvim-lspconfig'},
      {'williamboman/nvim-lsp-installer'},

      -- 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)
Enter fullscreen mode Exit fullscreen mode

Save the file, evaluate (again). Install with :PackerSync.

Add the configuration for lsp-zero.

local lsp = require('lsp-zero')

lsp.preset('recommended')
lsp.setup()
Enter fullscreen mode Exit fullscreen mode

Save and quit neovim.

Open neovim again. Now try to edit init.lua. Use :edit ~/.config/nvim/init.lua. You should get a message like this:

Would you like to install a language server for this filetype?
[Y]es, (N)o:
Enter fullscreen mode Exit fullscreen mode

Press y to confirm. Now it'll suggest a language server. At the moment the message should be this:

Please select which server you want to install for filetype "lua":
1: sumneko_lua
Type number and <Enter> or click with the mouse (q or empty cancels):
Enter fullscreen mode Exit fullscreen mode

Choose 1 for sumneko_lua, then press enter. A floating window will show up. When the server is done installing it should show its information.

  Installed servers (1)
      ◍ sumneko_lua (lua)
        installed 25 Feb 2022 07:02
        filetypes lua
        path      ~/.local/share/nvim/lsp_servers/sumneko_lua
        homepage  https://github.com/sumneko/lua-language-server
Enter fullscreen mode Exit fullscreen mode

At the moment the new languages server can't start automatically after installation. Use the command :LspZeroSetupServers! sumneko_lua... or restart neovim if that's more convenient. 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 sumneko_lua specifically for neovim, all with one line of code.

lsp.nvim_workspace()
Enter fullscreen mode Exit fullscreen mode

You need to add it before calling .setup().

local lsp = require('lsp-zero')

lsp.preset('recommended')
lsp.nvim_workspace()
lsp.setup()
Enter fullscreen mode Exit fullscreen mode

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',
    requires = {
      -- LSP Support
      {'neovim/nvim-lspconfig'},
      {'williamboman/nvim-lsp-installer'},

      -- 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')

lsp.preset('recommended')
lsp.nvim_workspace()
lsp.setup()
Enter fullscreen mode Exit fullscreen mode

What's next?

You should checkout lsp-zero's repository on github. Read the docs either on github or the help page in neovim :help lsp-zero.

Also, read the documentation of nvim-lsp-installer.

Ask me anything about lsp-zero here or in the discussion tab on github.


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 ☕.

buy me a coffee

Discussion (1)

Collapse
vonheikemen profile image
Heiker Author

Here is the same example config written in vimscript + vim-plug.

" Tab set to two spaces
set tabstop=2
set shiftwidth=2
set softtabstop=2
set expandtab

call plug#begin()
  " Colorscheme
  Plug 'joshdick/onedark.vim'

  " LSP Support
  Plug 'neovim/nvim-lspconfig'
  Plug 'williamboman/nvim-lsp-installer'

  " Autocompletion
  Plug 'hrsh7th/nvim-cmp'
  Plug 'hrsh7th/cmp-buffer'
  Plug 'hrsh7th/cmp-path'
  Plug 'saadparwaiz1/cmp_luasnip'
  Plug 'hrsh7th/cmp-nvim-lsp'
  Plug 'hrsh7th/cmp-nvim-lua'

  " Snippets
  Plug 'L3MON4D3/LuaSnip'
  Plug 'rafamadriz/friendly-snippets'

  " LSP Setup
  Plug 'VonHeikemen/lsp-zero.nvim'
call plug#end()

try
  set signcolumn=yes
  set termguicolors
  colorscheme onedark
catch
  " do nothing
endtry

lua <<EOF
local lsp = require('lsp-zero')

lsp.preset('recommended')
lsp.setup()
EOF
Enter fullscreen mode Exit fullscreen mode