LSP Debugging
LSP issues are the most common source of frustration in a Neovim setup. This lesson provides a systematic debugging approach.
Step 1: Check Basic Health
:checkhealth lsp
This shows:
- Active clients
- Any error messages from LSP initialization
- Provider status (Node, Python, etc.)
Step 2: Verify LSP Attachment
:LspInfo
Shows which servers are attached to the current buffer. If it says "No clients attached" — read on.
Step 3: Check Filetype Detection
LSP servers attach based on filetype, not filename:
:set filetype? → shows current filetype
If it shows iletype= (empty) or the wrong type:
:set filetype=python → manually set it
Add an autocmd to fix it permanently:
vim.api.nvim_create_autocmd({ "BufRead", "BufNewFile" }, {
pattern = "*.env",
callback = function() vim.bo.filetype = "sh" end,
})
Step 4: Check Root Directory Detection
Most LSPs require a project root directory (where they look for config files). If the root isn't detected, LSP won't start.
:lua print(vim.lsp.buf.list_workspace_folders())
Common root markers:
- pyright: looks for pyproject.toml, setup.py, .git
- s_ls: looks for package.json, sconfig.json
- lua_ls: looks for .luarc.json, .git
Fix: Create the relevant config file in your project root, or initialize a git repo.
Step 5: Enable LSP Logging
-- Enable debug-level LSP logs
vim.lsp.set_log_level("debug")
-- View logs
:lua vim.cmd("edit " .. vim.lsp.get_log_path())
This is verbose — disable after debugging:
vim.lsp.set_log_level("warn")
Step 6: Check Mason Installation
:Mason → UI showing all installed servers
:MasonLog → installation logs
Verify the server binary is accessible:
# Check if server is in PATH after Mason installs
which lua-language-server
which pyright-langserver
Mason installs to ~/.local/share/nvim/mason/bin/. Add to PATH if needed:
export PATH="$HOME/.local/share/nvim/mason/bin:$PATH"
Common LSP Scenarios
Intelephense Not Finding WordPress Functions
- Ensure stubs = wordpress is in lspconfig settings
- Install WordPress stubs: composer require --dev php-stubs/wordpress-stubs
- Restart LSP: :LspRestart
ts_ls Too Many Errors for Old JS Codebase
lspconfig.ts_ls.setup({
settings = {
typescript = { diagnostics = { ignoredCodes = { 7006, 7016 } } },
javascript = { diagnostics = { ignoredCodes = { 7006, 7016 } } },
},
})
Formatter Uses Wrong Server
-- Specify which LSP handles formatting
vim.lsp.buf.format({
filter = function(client)
return client.name == "null-ls" -- only null-ls formats
end,
})
Restarting LSP
:LspStop → stop all LSP clients
:LspStart → restart LSP for current filetype
:LspRestart → stop + start
In LazyVim
LazyVim automatically configures LSP keymaps, attaches servers cleanly using vim-lspconfig, and manages formatters automatically via conform.nvim. The debugging steps remain similar, but LazyVim provides a helpful UI to see what LSPs are attached.
| Aspect | Vanilla Neovim | LazyVim |
|---|---|---|
| LSP Info | :LspInfo | :LspInfo or <leader>cl (Lsp Info) |
| Mason Info | :Mason | :Mason or <leader>cm (Mason Info) |
| LSP Restart | :LspRestart | Same command works perfectly |
| Formatter Info | Custom null-ls check | :ConformInfo (since LazyVim uses conform.nvim) |
LazyVim Specific Debugging
- Check Formatter Status: Run :ConformInfo to see exactly which formatter is active for the current buffer, whether it's available, and if there are any errors.
- LSP Linter Status: Run :lua print(vim.inspect(require('lint').linters_by_ft)) to see which linters are configured for the current filetype (if using vim-lint).
- Disabling Format on Save: If an LSP or formatter is hanging and blocking your workflow, you can quickly toggle formatting on save using the
<leader>uf keymap instead of modifying your configuration.