Personal Neovim configuration built around lazy.nvim, Mason, native Neovim LSP, nvim-cmp, Treesitter, and Conform.
The config is organized so language support is added in one place, while plugin files stay small and predictable.
.
├── init.lua
├── lazy-lock.json
├── lua
│ ├── config
│ │ ├── languages.lua
│ │ └── lazy.lua
│ └── plugins
│ ├── completion.lua
│ ├── formatting.lua
│ ├── lsp.lua
│ ├── treesitter.lua
│ └── ...
└── docs
└── languages.md
init.lua
Loads the config entrypoint.
lua/config/lazy.lua
Bootstraps lazy.nvim, defines general editor options, and imports every plugin spec from lua/plugins.
lua/config/languages.lua
The central language registry. This is where Treesitter parsers, formatter mappings, Mason tools, Mason LSP servers, root markers, and LSP server options live.
lua/plugins/lsp.lua
Wires Mason, mason-lspconfig, mason-tool-installer, and native Neovim LSP to the shared language registry.
lua/plugins/formatting.lua
Configures Conform and reads formatter mappings from lua/config/languages.lua.
lua/plugins/treesitter.lua
Configures Treesitter and reads parser names from lua/config/languages.lua.
docs/languages.md
Detailed guide for adding new language support.
Language support should be centralized.
Add languages in lua/config/languages.lua first. Avoid scattering the same language across lsp.lua, formatting.lua, and treesitter.lua unless there is a strong reason.
Plugin specs should stay thin.
Simple plugins use opts. Plugins that need extra glue use config = function(_, opts) ... end. This keeps the plugin folder easy to scan.
Project-aware tools should use root markers.
Formatters like Prettier and Biome only run when their project config exists. LSP servers can either require a project root or fall back to the current file directory.
Most language changes happen here:
-- lua/config/languages.lua
M.treesitter = {}
M.formatters_by_ft = {}
M.mason_lsp_servers = {}
M.mason_tools = {}
function M.lsp_servers(root_pattern, root_pattern_or_file)
return {}
endUse root_pattern(...) for servers that should only attach when a project config exists.
Use root_pattern_or_file(...) for servers that should still work in standalone files. TypeScript uses this so completion works in loose .ts and .tsx files.
See docs/languages.md for examples.
TypeScript and JavaScript:
- LSP:
vtsls - Optional lint/fix:
eslint - Formatters:
biome,prettierd,prettier - Treesitter:
javascript,typescript,tsx
PHP:
- LSP:
intelephense - Formatter:
php_cs_fixer - Mason tool:
php-cs-fixer - Treesitter:
php
Lua:
- LSP:
lua_ls - Formatter:
stylua - Treesitter:
lua
Go:
- LSP:
gopls - Treesitter:
go
Also configured: CSS, HTML, Tailwind, Svelte, Terraform, Swift, C#, Markdown, JSON, YAML, SQL, GraphQL, and related parsers/tools.
Completion is configured in lua/plugins/completion.lua.
Sources:
nvim_lspluasnipbuffer
Relevant plugins:
hrsh7th/nvim-cmphrsh7th/cmp-nvim-lsphrsh7th/cmp-bufferL3MON4D3/LuaSnipsaadparwaiz1/cmp_luasnip
If completion is missing for a language, first check whether the LSP client attached:
:lua vim.print(vim.lsp.get_clients({ bufnr = 0 }))Formatting is handled by Conform in lua/plugins/formatting.lua.
Format-on-save skips files in node_modules and uses external formatters only:
return { timeout_ms = 1000, lsp_format = "never" }Formatter mappings are defined in lua/config/languages.lua.
For JavaScript and TypeScript, the formatter order is:
{ "biome", "prettierd", "prettier", stop_after_first = true }That means Biome wins when configured, then Prettierd, then Prettier.
LSP is configured with Neovim's native vim.lsp.config API.
Mason installs configured language servers from:
M.mason_lsp_serversExtra CLI tools come from:
M.mason_toolsUseful LSP commands:
:LspInfo
:lua vim.print(vim.lsp.get_clients({ bufnr = 0 }))Treesitter parser installation is driven by:
M.treesitterTreesitter auto-install is enabled, so opening a file can trigger parser installation when needed.
Git signs and blame are handled by gitsigns.nvim.
Mappings:
<leader>gb blame current line
<leader>gB blame current line with full details
LazyGit is available with:
;c
nvim-tree is configured on the right side.
Mapping:
<leader>t toggle file tree
Inside the tree:
t open node in a tab
toggleterm.nvim is configured as a floating terminal.
Mappings:
<C-\> toggle default terminal
;t toggle terminal 1
;g toggle terminal 2
File search and live grep use fff.nvim.
Mappings:
;f find files
;r live grep
;w search current word
Open Mason:
:MasonOpen Conform info:
:ConformInfoInspect attached LSP clients:
:lua vim.print(vim.lsp.get_clients({ bufnr = 0 }))Format Lua files:
/Users/farrell/.local/share/nvim/mason/bin/stylua luaCheck Lua formatting:
/Users/farrell/.local/share/nvim/mason/bin/stylua --check luaStart with docs/languages.md.
Short version:
- Add Treesitter parser names to
M.treesitter. - Add formatter mappings to
M.formatters_by_ft. - Add CLI formatter tools to
M.mason_tools. - Add LSP server names to
M.mason_lsp_servers. - Add LSP config entries inside
M.lsp_servers(...). - Add root marker lists when project detection matters.
Completion does not work:
- Check the buffer filetype with
:set filetype?. - Check attached clients with
:lua vim.print(vim.lsp.get_clients({ bufnr = 0 })). - Check that the LSP server exists in
M.mason_lsp_servers. - Check that the server config exists in
M.lsp_servers(...). - Check that
cmp-nvim-lspis installed and listed incompletion.lua.
Formatter does not run:
- Run
:ConformInfo. - Check the filetype key in
M.formatters_by_ft. - Check that the formatter binary exists or is in
M.mason_tools. - Check project root files for tools like Prettier or Biome.
LSP does not attach:
- Confirm the filetype matches the server's configured filetypes.
- Inspect root markers in
lua/config/languages.lua. - Use
root_pattern_or_file(...)if the server should work in standalone files. - Use
root_pattern(...)if the server should require a project config.
Mason did not install a tool:
- Open
:Mason. - Confirm the Mason package name is correct.
- Remember that Mason names and Conform names can differ, such as
php-cs-fixervsphp_cs_fixer.