LSP Integration

Vim Intermediate/Advanced

Chapter 7 — LSP Integration

What the Language Server Protocol Is

Before the Language Server Protocol existed, every editor needed its own custom integration for every language it wanted to support well — autocomplete, go-to-definition, inline errors, all built and maintained separately, per editor, per language. With N editors and M languages, that's N×M separate integrations.

LSP standardizes the conversation instead: a language server (one implementation per language — pyright for Python, gopls for Go, a TypeScript server for JS/TS) speaks a common JSON-RPC protocol, the same client/server RPC model the site's own grpc1 course covers from the opposite side. Any editor that implements the client half of LSP can talk to any language server — N+M integrations instead of N×M. Neovim has shipped a built-in LSP client since version 0.5; you don't install a separate plugin for the client itself, only for configuration convenience (below) and completion UI.

nvim-lspconfig — Configuring Language Servers

nvim-lspconfig is a collection of ready-made configuration templates for popular language servers — it is not the LSP client itself (that's built in), just the sensible defaults that spare you from hand-writing each server's specific startup command and settings:

-- init.lua
require('lspconfig').pyright.setup({})
Configuring lspconfig doesn't install the language server itself. pyright.setup({}) tells Neovim how to talk to pyright — but pyright's actual executable still has to exist on your machine first (e.g. npm install -g pyright). "I configured lspconfig and nothing happens" is almost always this: the config is correct, but the server binary was never installed. Mason.nvim is the modern convenience layer that manages installing and updating language server binaries directly from within Neovim, sparing you the separate package-manager step.

Autocompletion with nvim-cmp

The LSP client receives completion data from the language server, but Neovim's own built-in completion UI is minimal. nvim-cmp is the standard plugin providing an actual completion popup menu, pulling suggestions from multiple sources at once — the LSP client, buffer words, snippets:

-- init.lua
local cmp = require('cmp')
cmp.setup({
  sources = {
    { name = 'nvim_lsp' },   -- suggestions from the language server
    { name = 'buffer' },      -- suggestions from words already in the buffer
  },
})

Diagnostics — Errors and Warnings Inline

A language server continuously reports diagnostics — errors, warnings, hints — which Neovim renders as inline virtual text and gutter signs, updating as you type:

]d    # jump to the next diagnostic in the buffer
[d    # jump to the previous diagnostic
:lua vim.diagnostic.open_float()   # show full diagnostic detail for the current line in a floating window

This is the same underlying concept as the Problems panel from the site's own VS Code course — errors surfaced directly in the editor rather than only at compile/run time — just rendered inline instead of in a separate panel.

Navigation — Go to Definition, Hover, References

LSP-powered navigation is wired up through Neovim's vim.lsp.buf functions, typically bound to keys inside an on_attach function — code that runs automatically each time a language server attaches to a buffer:

local on_attach = function(client, bufnr)
  vim.keymap.set('n', 'gd', vim.lsp.buf.definition, { buffer = bufnr })
  vim.keymap.set('n', 'K',  vim.lsp.buf.hover,      { buffer = bufnr })
  vim.keymap.set('n', 'gr', vim.lsp.buf.references, { buffer = bufnr })
end

require('lspconfig').pyright.setup({ on_attach = on_attach })
KeyAction
gdGo to definition — jump to where the symbol under the cursor is actually defined
KHover — show documentation/type info for the symbol under the cursor
grReferences — list every place the symbol is used across the project

These keymaps reuse the exact vim.keymap.set mechanism from Chapter 6, just scoped to the buffer ({ buffer = bufnr }) so they only apply where a language server is actually attached, rather than globally.

LSP Is Not the Same as Debugging

It's easy to conflate LSP with the debugging covered in the site's VS Code course (breakpoints, stepping, the call stack) — but they're genuinely different protocols solving different problems. LSP covers editing-time intelligence: completion, diagnostics, navigation, all without running your code. Runtime debugging uses a separate standard, the Debug Adapter Protocol (DAP) — Neovim's equivalent tooling there is nvim-dap, conceptually parallel to VS Code's launch.json/breakpoints, but a distinct plugin and protocol from everything in this chapter.

A Worked Example — a Minimal LSP Setup for Python

Workflow — wiring up pyright end to end

1 npm install -g pyright Install the actual language server binary first, per this chapter's own warn-box.
2 on_attach = function... Define the on_attach function with gd/K/gr keymaps, as shown above.
3 lspconfig.pyright.setup({ on_attach }) Register pyright with lspconfig, passing in the on_attach function.
4 cmp.setup({ sources = {...} }) Wire nvim-cmp to the nvim_lsp source so completions actually appear while typing.
5 open a .py file Diagnostics, gd/K/gr, and completion should all now be active — confirm with K on any function call.

Commands Introduced in This Chapter

Chapter 7 — Command Reference

Setup
require('lspconfig').{server}.setup({})Configure a language server
require('cmp').setup({...})Configure the completion popup and its sources
Diagnostics
]d / [dJump to the next/previous diagnostic
vim.diagnostic.open_float()Show full diagnostic detail for the current line
Navigation (via on_attach keymaps)
gdGo to definition
KHover documentation
grList references