Neovim language services, completion and formatting
On this page
Highlighting, completion, definition lookup and test execution come from different components. Identify the responsible component before reinstalling tools.
What opening a file triggers
| Component | Responsibility | Limit |
|---|---|---|
| File type detection | Identify Rust, Lua and other types | Does not install tools |
| Treesitter parser | Syntax structure for highlights and selections | Not project type checking |
| LSP client/server | Definitions, references, diagnostics, rename | Does not run all tests |
| Completion interface | Present LSP, snippet and other candidates | Does not supply all semantics |
| Formatter | Rearrange text by rules | Does not prove behaviour |
| DAP client/adapter | Breakpoints, variables and execution control | Still needs SDKs, targets and symbols |
Language extras connect some of these layers. Optional plugin settings apply only when the plugin is installed; runtimes and project dependencies remain separate. LazyVim extras
Start with one language
For Rust, make cargo check work outside the editor before enabling Rust through :LazyExtras. Resolve toolchain, dependency and compiler failures there first.
Open a Rust file in the Cargo project:
:set filetype?
:pwd
:lua print(vim.inspect(vim.lsp.get_clients({ bufnr = 0 })))
Expect rust and an attached language client. :pwd is the editor working directory, not necessarily the LSP root; inspect the client's configuration as well. Neovim LSP
A workspace server may use an outer root. A file copied outside its project can lack module and dependency context.
Verify individual language features
Try definitions, references, hover and rename on a known local function. Direct calls help separate API behaviour from key mappings:
:lua vim.lsp.buf.definition()
:lua vim.lsp.buf.references()
:lua vim.lsp.buf.hover()
No result can mean an unsupported target, unresolved dependency or incomplete indexing rather than no server. Establish a known working local target first.
After rename, inspect affected buffers, save intended changes and review Git diff. A successful request is not the final correctness check.
Choose who formats
LazyVim commonly uses Conform. :ConformInfo reports configured formatters, availability and logs. To make changes explicit while learning, put this in lua/config/options.lua:
vim.g.autoformat = false
Trigger formatting manually and review differences. Respect project files such as .editorconfig, .clang-format and rustfmt.toml.
This override in lua/plugins/formatting.lua selects StyLua for Lua:
return {
{
"stevearc/conform.nvim",
opts = function(_, opts)
opts.formatters_by_ft = opts.formatters_by_ft or {}
opts.formatters_by_ft.lua = { "stylua" }
end,
},
}
It selects a program but does not install it. Avoid adding another save autocmd that also invokes LSP formatting, which can duplicate work or apply conflicting rules.
Diagnostics, tests and debugging
Static diagnostics can find syntax and type problems while missing incorrect business boundaries. The project exercise demonstrates code that compiles but fails a requirement.
Debugging additionally needs a launchable target, working directory, arguments and adapter. Make the same target run from the command line first. Rust, Go, C# and Java use different backends; nvim-dap alone does not provide all of them.
A diagnostic sequence
- Check
:set filetype?. - Verify project context, configuration and dependencies.
- Inspect executable resolution, for example
:lua print(vim.fn.exepath("rust-analyzer")); plugins may also specify their own path. - Check client attachment and startup logs.
- Verify the specific server capability and whether failure is file-specific.
- Inspect formatter and debugger tools independently rather than repeatedly reinstalling LSP.
Change one condition at a time. Reproduce the result with logs and a small project before applying the fix to the normal configuration.