Using Neovim’s Built‑in LSP Client for Lightweight IDE Features
Learn how to configure Neovim’s native LSP client, attach a language server, verify diagnostics and completion, and understand the trade‑offs of this lightweight IDE approach.
24 Jul 2025, 10:35 UTC

The Problem: Missing IDE Comfort in a Minimal Editor
When you rely on Neovim for editing but miss features like real-time diagnostics, smart completion, or hover documentation, you often reach for heavyweight plugin bundles. This adds startup time and maintenance overhead.
Thesis: Neovim’s Built‑in LSP Client Gives IDE‑Like Features Without Extra Plugins
The editor ships with a native Language Server Protocol (LSP) client that can talk to external language servers (e.g., pylsp, clangd, pyright) over JSON‑RPC. By attaching a server to a buffer you get diagnostics, code actions, completion, and hover while keeping the UI responsive.
Setting Up the LSP Client
- Verify Neovim version: run
:versioninside Neovim and confirm the output showsNVIM v0.5.0or higher. - Install a language server for your language. For Python, the
pylspserver can be installed with:
# Install pylsp via pip (user or venv)
pip install python-lsp-server
Make sure the executable is reachable from your shell’s $PATH. You can test with which pylsp or pylsp --version.
Attaching the Server to a Buffer
Neovim’s Lua API lets you start a client and attach it to the current buffer. A minimal configuration in init.lua looks like:
-- Enable LSP logging (optional)
vim.lsp.set_log_level("debug")
-- Define an on_attach function that runs when the client connects
local on_attach = function(client, bufnr)
-- Enable completion triggered by
vim.api.nvim_buf_set_option(bufnr, 'omnifunc', 'v:lua.vim.lsp.omnifunc')
-- Optional: set keymaps
vim.api.nvim_buf_set_keystroke(bufnr, 'n', 'K', 'lua vim.lsp.buf.hover()', { noremap=true, silent=true })
end
-- Start the pylsp client and attach it
vim.lsp.start_client({
name = 'pylsp',
cmd = { 'pylsp' },
root_dir = vim.fn.getcwd(),
on_attach = on_attach,
})
Place the block in your init.lua (or a plugin folder) and restart Neovim.
Worked Example: Python File with a Syntax Error
- Create a file
test.pywith intentional error:
def foo(:
return 42
- Open the file in Neovim:
nvim test.py - After a moment, you should see red underline (or virtual text) under the missing parenthesis.
- Press
Kwhile the cursor is ondefto view the hover documentation for the built‑indefstatement. - Trigger completion with
; the menu should suggestfooand other symbols from the buffer. - Run
:LspInfoto verify that thepylspclient appears under “attached clients” for the current buffer.
If any of these steps do not produce the expected result, check:
- The language server executable is in
$PATH(runwhich pylspfrom your shell). - Neovim version is at least 0.5 (
:version). - The
init.luablock executed without errors (look at:messages).
Trade‑offs and Limitations
- You must install and maintain the appropriate language server binary for each language you work with.
- Version mismatches between Neovim’s LSP client and the server can cause missing features or crashes; keep both up‑to‑date.
- The initial configuration requires writing Lua, which may feel steep compared to zero‑configuration plugins.
- Because the server runs as a separate process, memory usage is the sum of Neovim plus the server; large projects may need a server with adequate resources.
You can verify that the server is running by checking its process with ps aux | grep pylsp or by looking at the PID shown in :LspInfo.
Actionable Closing
Start with a single language server (e.g., pylsp for Python) and the minimal Lua snippet above. Once diagnostics and completion appear reliably, repeat the process for other languages you use. Keep your init.lua under version control so you can roll back by removing the block if needed. This approach gives you IDE‑like feedback while preserving Neovim’s fast, plugin‑light core.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.