Getting started with neovim's native LSP client: The easy way
Tags: neovim, vim, shell
Published: 2022-02-25 05:53:29

---
title: Getting started with neovim's native LSP client: The easy way
published: true
description: Add cool LSP related features to neovim
tags: #neovim #vim #shell
//cover_image: https://direct_url_to_image.jpg
---

When the moment comes to "add support for LSP" in neovim lots of people use these plugins: 

* [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig)
* [nvim-cmp](https://github.com/hrsh7th/nvim-cmp)
* [mason.nvim](https://github.com/williamboman/mason.nvim)

Making those 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](https://github.com/VonHeikemen/lsp-zero.nvim)) that will help you integrate all those things with minimum effort.

The configuration needed for a quick start is going to look like this.

```lua
local lsp_zero = require('lsp-zero')

lsp_zero.on_attach(function(client, bufnr)
  lsp_zero.default_keymaps({buffer = bufnr})
end)

require('mason').setup({})
require('mason-lspconfig').setup({
  handlers = {
    lsp_zero.default_setup,
  },
})
```

You must be thinking "a complete setup in 10 lines of code?" Yes... and no. Technically we need to install some dependencies to make it work. So let's do that.

## Starting from scratch

### Requirements

* Basic knowledge about Neovim: what is `normal mode`, `insert mode`, `command mode` and how to navigate between them.
* Neovim v0.8 or greater
* git
* 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 servers from inside neovim.

### The Entry Point

To start we will create the file known as `init.lua`. The location of this file depends on your operating system. If you want to know where that is execute this command on your terminal.

```sh
nvim --headless -c 'echo stdpath("config")' -c 'echo ""' -c 'quit'
```

Create that folder and then navigate to it. Use whatever method you know best, use a terminal or a file explorer.

Now create an empty file called `init.lua`.

Once the configuration exists in your system you can access it from the terminal using this command.

```sh
nvim -c 'edit $MYVIMRC'
```

Now let's make sure Neovim is actually loading our file. We will change the colorscheme to a light theme. So, open your `init.lua` and add this line.

```lua
vim.cmd.colorscheme('morning')
```

Open Neovim again and you should notice the light theme. If you get an error it means your Neovim version does not meet the requirements. Visit Neovim's github repository, in the [release section](https://github.com/neovim/neovim/releases) you'll find prebuilt executables for the latest versions.

What can you do?

* If you have Neovim v0.7 you can install a special branch of lsp-zero called `compat-07`. Follow [this tutorial](https://github.com/VonHeikemen/lsp-zero.nvim/blob/compat-07/doc/md/tutorial.md) instead.

* If you Neovim v0.6 or v0.5 you can install `v1` of lsp-zero. Follow the [tutorial for version 1](https://github.com/VonHeikemen/lsp-zero.nvim/blob/v1.x/doc/md/tutorial.md).

Assuming everything went well, you can now change the theme to something else.

```lua
vim.cmd.colorscheme('habamax')
```

### Install the Plugin Manager

> Note: We don't **need** a plugin manager but they make our lives easier.

We are going to use [lazy.nvim](https://github.com/folke/lazy.nvim), only because that's what the cool kids are doing these days. You can do a lot with lazy.nvim but I'm just going to show the most basic usage.

First step is to install it from github. It just so happens we can do this using lua. In lazy.nvim's documentation they show us how to do it.

Add this to your init.lua.

```lua
local lazypath = vim.fn.stdpath('data') .. '/lazy/lazy.nvim'

-- Auto-install lazy.nvim if not present
if not vim.loop.fs_stat(lazypath) then
  print('Installing lazy.nvim....')
  vim.fn.system({
    'git',
    'clone',
    '--filter=blob:none',
    'https://github.com/folke/lazy.nvim.git',
    '--branch=stable', -- latest stable release
    lazypath,
  })
  print('Done.')
end

vim.opt.rtp:prepend(lazypath)
```

Notice in `lazypath` we use `stdpath('data')`, this will return the path to Neovim's data folder. So now we don't need to worry about changing our paths depending on the operating system, Neovim will do that for us. If you want to inspect the path, use this command inside Neovim.

```vim
:echo stdpath('data') . '/lazy/lazy.nvim'
```

To actually use lazy.nvim we need to call the `.setup()` function of the lua module called `lazy`. Like this.

```lua
require('lazy').setup({
  ---
  -- List of plugins...
  ---
})
```

### Adding a new plugin

Now let's use lazy.nvim for real this time. We are going to test it with a plugin called [tokyonight.nvim](https://github.com/folke/tokyonight.nvim), this is a colorscheme that will make Neovim look pretty.

Ready? We are going to follow these steps.

1 - Add the plugin in lazy's setup.

```lua
require('lazy').setup({
  {'folke/tokyonight.nvim'},
})
```

To download a plugin the basic information we need is this: the user name of the author in github and the name of the repository.

2 - We need to delete the old colorscheme line if it's still there.

3 - Call the new colorscheme at the end of the `init.lua` file.

```lua
vim.opt.termguicolors = true
vim.cmd.colorscheme('tokyonight')
```

4 - Save the file.

5 - Restart Neovim.

When Neovim starts it should show a message telling us is cloning the plugin manager. After it's done another window will show up, it'll tell us the progress of the plugin's download. After the plugins are installed they will be loaded.

### Setup lsp-zero

Now we need to add lsp-zero and all its dependencies in lazy's list of plugins.

```lua
require('lazy').setup({
  {'folke/tokyonight.nvim'},
  {'VonHeikemen/lsp-zero.nvim', branch = 'v3.x'},
  {'williamboman/mason.nvim'},
  {'williamboman/mason-lspconfig.nvim'},
  {'neovim/nvim-lspconfig'},
  {'hrsh7th/nvim-cmp'},
  {'hrsh7th/cmp-nvim-lsp'},
  {'L3MON4D3/LuaSnip'},
})
```

Then we add the configuration at the end of the file.

```lua
local lsp_zero = require('lsp-zero')

lsp_zero.on_attach(function(client, bufnr)
  lsp_zero.default_keymaps({buffer = bufnr})
end)

require('mason').setup({})
require('mason-lspconfig').setup({
  handlers = {
    lsp_zero.default_setup,
  },
})
```

Save the file, restart Neovim and wait for everything to be downloaded.

### Install a language server

Let's try to use the language server for lua. 

Open your `init.lua` and execute the command `:LspInstall`. Now `mason.nvim` will suggest a language server. Neovim should show a message like this.

```
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 will show up. When the server is done installing, a message should appear.

At the moment the language server can't start automatically, restart Neovim so the language server can be configured properly. Once the server starts you'll notice warning signs in the global variable vim, that means everything is well and good.

To make sure `lua_ls` can detect the "root directory" of our config we need to create a file called `.luarc.json` in the Neovim config folder. This file can be empty, it just need to exists.

If you wanted to, you could setup `lua_ls` specifically for Neovim.

```lua
require('mason-lspconfig').setup({
  handlers = {
    lsp_zero.default_setup,
    lua_ls = function()
      local lua_opts = lsp_zero.nvim_lua_ls()
      require('lspconfig').lua_ls.setup(lua_opts)
    end,
  }
})
```

### Complete Example

```lua
local lazypath = vim.fn.stdpath('data') .. '/lazy/lazy.nvim'

-- Auto-install lazy.nvim if not present
if not vim.loop.fs_stat(lazypath) then
  print('Installing lazy.nvim....')
  vim.fn.system({
    'git',
    'clone',
    '--filter=blob:none',
    'https://github.com/folke/lazy.nvim.git',
    '--branch=stable', -- latest stable release
    lazypath,
  })
  print('Done.')
end

vim.opt.rtp:prepend(lazypath)

require('lazy').setup({
  {'folke/tokyonight.nvim'},
  {'VonHeikemen/lsp-zero.nvim', branch = 'v3.x'},
  {'williamboman/mason.nvim'},
  {'williamboman/mason-lspconfig.nvim'},
  {'neovim/nvim-lspconfig'},
  {'hrsh7th/nvim-cmp'},
  {'hrsh7th/cmp-nvim-lsp'},
  {'L3MON4D3/LuaSnip'},
})

-- Set colorscheme
vim.opt.termguicolors = true
vim.cmd.colorscheme('tokyonight')

-- LSP
local lsp_zero = require('lsp-zero')

lsp_zero.on_attach(function(client, bufnr)
  lsp_zero.default_keymaps({buffer = bufnr})
end)

require('mason').setup({})
require('mason-lspconfig').setup({
  handlers = {
    lsp_zero.default_setup,
    lua_ls = function()
      local lua_opts = lsp_zero.nvim_lua_ls()
      require('lspconfig').lua_ls.setup(lua_opts)
    end,
  }
})
```

## What's next?

You should checkout [lsp-zero](https://github.com/VonHeikemen/lsp-zero.nvim/tree/v3.x) on github. Read the docs either on github or the help page in neovim `:help lsp-zero`.

Learn the default keybindings:

* [Keybindings for Autocompletion](https://github.com/VonHeikemen/lsp-zero.nvim/tree/v3.x#keybindings-1)
* [Keybindings for the LSP actions](https://github.com/VonHeikemen/lsp-zero.nvim/tree/v3.x#keybindings)

Also, read the documentation of [mason.nvim](https://github.com/williamboman/mason.nvim).

Ask me anything about `lsp-zero` here, in the [discussion tab](https://github.com/VonHeikemen/lsp-zero.nvim/discussions) on github, or matrix [#lsp-zero-nvim:matrix.org](https://matrix.to/#/#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 ☕](https://www.buymeacoffee.com/vonheikemen).

[![buy me a coffee](https://res.cloudinary.com/vonheikemen/image/upload/v1618466522/buy-me-coffee_ah0uzh.png)](https://www.buymeacoffee.com/vonheikemen)