DaleSchool

Use Neovim Like an IDE with LSP

Beginner20min

Learning Objectives

  • Explain what LSP is
  • Hook up a language server with nvim-lspconfig
  • Use autocompletion, go-to-definition, and diagnostics
  • Search files and symbols with Telescope

The secret behind VS Code's autocompletion and error indicators is LSP (Language Server Protocol). Neovim has built-in LSP support, so once configured, you get IDE-grade features.

Working Code

What is LSP?

LSP is a standard protocol between editors and language servers. One editor can support many languages, and one language server can work across editors.

Editor (Neovim)  ←→  LSP protocol  ←→  Language server (ts_ls, pyright, etc.)

Install plugins

Add the following to ~/.config/nvim/init.lua. We use vim-plug here:

-- declare vim-plug plugins
vim.cmd([[
call plug#begin('~/.local/share/nvim/plugged')

Plug 'neovim/nvim-lspconfig'
Plug 'williamboman/mason.nvim'
Plug 'williamboman/mason-lspconfig.nvim'
Plug 'nvim-telescope/telescope.nvim'
Plug 'nvim-lua/plenary.nvim'

call plug#end()
]])

Open Neovim and run :PlugInstall.

LSP setup

After installing the plugins, add this to init.lua:

-- Mason: auto-install language servers
require('mason').setup()
require('mason-lspconfig').setup({
  ensure_installed = { 'ts_ls', 'pyright' },
})

-- LSP keymaps (activated once a server attaches)
vim.api.nvim_create_autocmd('LspAttach', {
  callback = function(args)
    local opts = { buffer = args.buf }
    vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts)
    vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts)
    vim.keymap.set('n', '<leader>rn', vim.lsp.buf.rename, opts)
    vim.keymap.set('n', 'gr', vim.lsp.buf.references, opts)
    vim.keymap.set('n', '<leader>ca', vim.lsp.buf.code_action, opts)
  end,
})

-- enable language servers
require('lspconfig').ts_ls.setup({})
require('lspconfig').pyright.setup({})

Try It Yourself

Open a TypeScript or Python project and try the LSP keybindings:

KeyAction
gdGo to Definition
KHover documentation
<leader>rnRename
grList References
<leader>caCode Action
[d / ]dPrevious / next diagnostic

Search with Telescope

Telescope is a fuzzy finder. Search files, text, and LSP symbols with it:

local telescope = require('telescope.builtin')
vim.keymap.set('n', '<leader>ff', telescope.find_files, { desc = 'Find files' })
vim.keymap.set('n', '<leader>fg', telescope.live_grep, { desc = 'Live grep' })
vim.keymap.set('n', '<leader>fb', telescope.buffers, { desc = 'Find buffers' })
vim.keymap.set('n', '<leader>fs', telescope.lsp_document_symbols, { desc = 'Find symbols' })

Press <leader>ff to jump through every file in your project.

"Why?"

Why use LSP? In the old days, you needed a separate plugin per editor per language. M editors × N languages meant M×N plugins. LSP solves that:

  • Editor authors: implement an LSP client once and support every language.
  • Language authors: build a language server once and it works in every editor.
  • Users: get the same level of language support no matter which editor you pick.

Mason automates installing and managing language servers. Run :Mason to see the list of available servers.

Deep Dive

Autocompletion with nvim-cmp

LSP alone gives you K (hover) and gd (go-to-definition), but as-you-type autocomplete requires the nvim-cmp plugin:

-- add these to vim-plug
-- Plug 'hrsh7th/nvim-cmp'
-- Plug 'hrsh7th/cmp-nvim-lsp'

-- basic setup
local cmp = require('cmp')
cmp.setup({
  sources = {
    { name = 'nvim_lsp' },
  },
  mapping = cmp.mapping.preset.insert({
    ['<CR>'] = cmp.mapping.confirm({ select = true }),
    ['<C-Space>'] = cmp.mapping.complete(),
  }),
})

Set up LSP yourself

  1. Install mason.nvim and nvim-lspconfig.
  2. In :Mason, install the language server for the language you use most.
  3. Open a project and test:
    • gd to jump to a function definition
    • K to show function documentation
    • <leader>rn to rename a variable
  4. Add Telescope keymaps and search files with <leader>ff.

What's the biggest advantage of LSP?