This document covers every language supported by this Neovim configuration, including LSP servers, Treesitter parsers, formatters, linters, special plugins, keybindings, and project-detection root markers.
- Lua
- Go
- Python
- TypeScript / JavaScript
- Deno
- C / C++
- Rust
- Elixir
- PHP
- Svelte
- Gleam
- Solidity
- HTML / CSS (Tailwind)
- Markdown
- Treesitter Parsers (Full List)
- TypeScript vs Deno -- Avoiding Conflicts
- Adding a New Language
- lua_ls (lua-language-server), installed via Mason.
- Configuration file:
after/lsp/lua_ls.lua - Settings:
Lua.completion.callSnippet = "Replace"-- when completing a function call, the snippet replaces the word under the cursor rather than inserting next to it.
-- after/lsp/lua_ls.lua
return {
settings = {
Lua = {
completion = {
callSnippet = "Replace",
},
},
},
}lualuadoc
- stylua -- configured in
conform.nvimand installed via Mason. - Runs on save with LSP fallback.
-- formatters_by_ft (from formatting.lua)
lua = { "stylua" },- None configured explicitly. lua_ls provides diagnostics.
- lazydev.nvim (
folke/lazydev.nvim) -- loaded only forft = "lua". Provides completion and type information for the Neovim Lua API,vim.uv, and the luvit library. - luvit-meta (
Bilal2453/luvit-meta) -- lazy-loaded type stubs consumed by lazydev.
- Default lua_ls detection (no custom root markers configured).
- gopls, installed via Mason.
- Configuration file:
after/lsp/gopls.lua - Settings:
analyses.unusedparams = true-- flags unused function parameters.staticcheck = true-- enables the staticcheck analyzer for additional diagnostics.gofumpt = true-- uses gofumpt (a stricter gofmt) for formatting.
-- after/lsp/gopls.lua
return {
settings = {
gopls = {
analyses = {
unusedparams = true,
},
staticcheck = true,
gofumpt = true,
},
},
}go
- None configured in conform.nvim. Formatting is handled by gopls itself (via
gofumpt = true), which runs through thelsp_format = "fallback"path on save.
- golangci-lint -- configured in
nvim-lintand installed via Mason. - Triggers on
BufWritePost,InsertLeave, andBufReadPost.
-- linters_by_ft (from linting.lua)
go = { "golangcilint" },- Default gopls detection (go.mod, go.sum, .git).
- pyright, installed via Mason.
- Configuration file:
after/lsp/pyright.lua-- returns an empty table, meaning all defaults are used.
python
- None configured in conform.nvim for Python. Formatting is delegated to the LSP fallback (pyright does not format, but ruff can act as both formatter and linter via its own integration).
- ruff -- configured in
nvim-lintand installed via Mason. - Ruff provides both linting and formatting capabilities for Python.
- Triggers on
BufWritePost,InsertLeave, andBufReadPost.
-- linters_by_ft (from linting.lua)
python = { "ruff" },- Default pyright detection (pyrightconfig.json, pyproject.toml, setup.py, .git).
- ts_ls (typescript-language-server), installed via Mason.
- Configuration file:
after/lsp/ts_ls.lua - Root markers:
package.json,tsconfig.json single_file_support = false-- the server will NOT attach to standalone .ts/.js files that are not inside a project with one of the root markers. This is critical for coexistence with Deno (see TypeScript vs Deno).
- Configuration file:
-- after/lsp/ts_ls.lua
return {
root_markers = { "package.json", "tsconfig.json" },
single_file_support = false,
}- eslint-lsp -- installed via Mason. Provides ESLint diagnostics as an LSP server. No custom configuration file; uses defaults.
javascripttypescriptjson
- prettierd (preferred) or prettier (fallback) -- configured in conform.nvim, installed via Mason.
- Uses
stop_after_first = true, meaning if prettierd succeeds, prettier is not invoked.
-- formatters_by_ft (from formatting.lua)
javascript = { "prettierd", "prettier", stop_after_first = true },
typescript = { "prettierd", "prettier", stop_after_first = true },- ESLint diagnostics provided via eslint-lsp (LSP-based, not via nvim-lint).
package.json,tsconfig.json(for ts_ls).
- denols, installed via Mason.
- Configuration file:
after/lsp/denols.lua - Root markers:
deno.json,deno.jsonc single_file_support = false-- denols will NOT attach unless one of the root markers is found.
-- after/lsp/denols.lua
return {
root_markers = { "deno.json", "deno.jsonc" },
single_file_support = false,
}- Uses the same
javascriptandtypescripttreesitter parsers as TypeScript/JavaScript.
- Deno has a built-in formatter (
deno fmt). Formatting falls back to denols via LSP on save.
- Deno has a built-in linter. Diagnostics are provided by denols.
deno.json,deno.jsonc
See TypeScript vs Deno for details on how the two servers avoid conflicts.
- clangd, installed via Mason.
- Configuration file:
after/lsp/clangd.lua - Custom command:
clangd --offset-encoding=utf-16-- overrides the default offset encoding to UTF-16 for compatibility with Neovim's LSP client.
-- after/lsp/clangd.lua
return {
cmd = {
"clangd",
"--offset-encoding=utf-16",
},
}ccpp
- clang-format -- configured in conform.nvim and installed via Mason.
- Applies to both
c++andcppfiletypes.
-- formatters_by_ft (from formatting.lua)
["c++"] = { "clang-format" },
cpp = { "clang-format" },- None configured in nvim-lint. Diagnostics are provided by clangd.
- LSP formatting is disabled on save for C and C++ files. The
format_on_savefunction informatting.luachecks the filetype and setslsp_format = "never"forcandcpp. This means clangd will not auto-format on save; only manual formatting via<leader>fwill invoke clang-format.
-- from formatting.lua
format_on_save = function(bufnr)
local disable_filetypes = { c = true, cpp = true }
local lsp_format_opt
if disable_filetypes[vim.bo[bufnr].filetype] then
lsp_format_opt = "never"
else
lsp_format_opt = "fallback"
end
return {
timeout_ms = 500,
lsp_format = lsp_format_opt,
}
end,- Default clangd detection (compile_commands.json, .clangd, .git).
- rust_analyzer -- managed entirely by rustaceanvim, NOT by Mason.
- rust_analyzer is explicitly excluded from Mason's automatic LSP enabling:
-- from lsp.lua
require("mason-lspconfig").setup({
automatic_enable = {
exclude = { "rust_analyzer", "rescriptls", "stylua_lsp" },
},
})- rustaceanvim (
mrcjkb/rustaceanvim) version^8. - Loaded eagerly (
lazy = false). - Provides a complete Rust development experience: LSP management, debugging, inlay hints, runnables, and more.
- Automatically downloads and manages rust_analyzer.
-- lua/plugins/lang-rust.lua
return {
"mrcjkb/rustaceanvim",
version = "^8",
lazy = false,
}rust
- Not configured in conform.nvim. Formatting is handled by rust_analyzer (rustfmt) via the LSP fallback on save.
- Diagnostics provided by rust_analyzer (including clippy if configured in the project).
- Default rust_analyzer detection (Cargo.toml, rust-project.json).
Managed by elixir-tools.nvim (elixir-tools/elixir-tools.nvim), NOT by Mason. Two language servers run simultaneously:
- nextls (Next LS) -- enabled with default settings.
- elixirls (ElixirLS) -- enabled with custom settings:
dialyzerEnabled = false-- Dialyzer is disabled to avoid slow startup and high memory usage.enableTestLenses = false-- code lenses for running tests inline are disabled.
-- lua/plugins/lang-elixir.lua
elixir.setup({
nextls = { enable = true },
elixirls = {
enable = true,
settings = elixirls.settings({
dialyzerEnabled = false,
enableTestLenses = false,
}),
on_attach = function(_, _)
-- keymaps set here (see below)
end,
},
projectionist = { enable = true },
})elixir
- Not configured in conform.nvim. Formatting is handled by ElixirLS/NextLS via LSP fallback, which delegates to
mix format.
- Diagnostics provided by ElixirLS and NextLS.
These keymaps are buffer-local and only active in Elixir files (set via on_attach):
| Keymap | Mode | Command | Description |
|---|---|---|---|
<leader>exp |
Normal | :ElixirFromPipe |
Convert a pipe chain back to nested function calls |
<leader>exo |
Normal | :ElixirToPipe |
Convert nested function calls into a pipe chain |
<leader>exm |
Visual | :ElixirExpandMacro |
Expand the selected macro to see its generated code |
- elixir-tools.nvim (
elixir-tools/elixir-tools.nvim) -- all-in-one Elixir plugin.- Depends on
nvim-lua/plenary.nvim. - Loaded on
BufReadPreandBufNewFile. - Includes projectionist support (
projectionist = { enable = true }) for alternate file navigation.
- Depends on
- Default ElixirLS/NextLS detection (mix.exs, .git).
- phpactor, installed via Mason.
- Configuration file:
after/lsp/phpactor.lua-- returns an empty table, meaning all defaults are used.
php
- pint (preferred) or php_cs_fixer (fallback) -- configured in conform.nvim.
- Uses
stop_after_first = true.
-- formatters_by_ft (from formatting.lua)
php = { "pint", "php_cs_fixer", stop_after_first = true },- None configured in nvim-lint. Diagnostics provided by phpactor.
- blade-nav.nvim (
ricardoramirezr/blade-nav.nvim) -- provides navigation support for Laravel Blade templates.- Loaded for
bladeandphpfiletypes. - Depends on
saghen/blink.cmpfor completion integration.
- Loaded for
-- lua/plugins/lang-php.lua
return {
"ricardoramirezr/blade-nav.nvim",
dependencies = { "saghen/blink.cmp" },
ft = { "blade", "php" },
}A custom autocmd in lua/core/autocmds.lua detects .blade.php files and sets their filetype to blade:
-- lua/core/autocmds.lua
vim.api.nvim_create_augroup("BladeFiletypeRelated", { clear = true })
vim.api.nvim_create_autocmd({ "BufRead", "BufNewFile" }, {
pattern = "*.blade.php",
group = "BladeFiletypeRelated",
callback = function()
vim.bo.filetype = "blade"
end,
})Additionally, in lua/plugins/treesitter.lua, the blade filetype is registered with treesitter:
vim.treesitter.language.register("blade", "blade")A compatibility shim is also provided for blade-nav.nvim, which relies on the removed parsers.get_parser API:
local parsers = require("nvim-treesitter.parsers")
if not parsers.get_parser then
parsers.get_parser = function(bufnr, lang)
return vim.treesitter.get_parser(bufnr, lang)
end
end- Default phpactor detection (composer.json, .git).
- svelte (svelte-language-server).
- Configuration file:
after/lsp/svelte.lua-- returns an empty table, meaning all defaults are used. - Note: svelte-language-server is NOT in the Mason
ensure_installedlist. It must be installed manually or will be auto-detected if available on$PATH.
svelte
- Not configured in conform.nvim. Formatting falls back to the Svelte LSP.
- Diagnostics provided by svelte-language-server.
- Default svelte-language-server detection (svelte.config.js, .git).
- gleam (Gleam's built-in language server).
- Configuration file:
after/lsp/gleam.lua-- returns an empty table, meaning all defaults are used. - Note: The Gleam LSP is bundled with the Gleam compiler itself. It is NOT in the Mason
ensure_installedlist; thegleambinary must be available on$PATH.
- Not in the
ensure_installedlist. Treesitter hasauto_install = true, so the gleam parser will be installed automatically when a.gleamfile is opened (if a gleam parser is available).
- Not configured in conform.nvim. Gleam has a built-in formatter (
gleam format) accessed via LSP fallback.
- Diagnostics provided by the Gleam LSP.
- Default Gleam LSP detection (gleam.toml, .git).
- solidity_ls_nomicfoundation (Nomic Foundation's Solidity language server for Hardhat/Foundry projects).
- Configuration file:
after/lsp/solidity.lua - Custom command:
nomicfoundation-solidity-language-server --stdio - Filetypes:
solidity - Root markers:
foundry.toml,.git single_file_support = true-- will attach to standalone Solidity files.
-- after/lsp/solidity.lua
return {
cmd = { "nomicfoundation-solidity-language-server", "--stdio" },
filetypes = { "solidity" },
root_markers = { "foundry.toml", ".git" },
single_file_support = true,
}- Not in the
ensure_installedlist. Will be auto-installed if available due toauto_install = true.
- Not configured in conform.nvim. Formatting falls back to the Solidity LSP.
- Diagnostics provided by the Solidity LSP.
foundry.toml,.git
- tailwindcss-language-server, installed via Mason.
- No custom configuration file in
after/lsp/. - Provides Tailwind CSS class completions, hover previews, and diagnostics for HTML, JSX, TSX, Svelte, and other template files.
htmlcss
- Not configured explicitly. HTML/CSS formatting falls back to LSP or can be handled by prettierd/prettier if configured for those filetypes.
- Diagnostics provided by tailwindcss-language-server (class conflicts, unknown utilities).
- None configured specifically for Markdown.
markdownmarkdown_inline
- markdown-preview.nvim -- provides live Markdown preview in the browser.
- Keymap:
<leader>mpto toggle the Markdown preview.
- Keymap:
- Not configured in conform.nvim for Markdown.
- None configured.
The following parsers are in the ensure_installed list and will be downloaded automatically. Treesitter also has auto_install = true, so any language not listed here will have its parser installed on first use if one is available.
| Parser | Language |
|---|---|
bash |
Bash / Shell |
c |
C |
cpp |
C++ |
css |
CSS |
diff |
Diff / Patch |
elixir |
Elixir |
go |
Go |
html |
HTML |
javascript |
JavaScript |
json |
JSON |
lua |
Lua |
luadoc |
Lua documentation comments |
markdown |
Markdown |
markdown_inline |
Markdown inline elements |
php |
PHP |
python |
Python |
query |
Treesitter query language |
rust |
Rust |
svelte |
Svelte |
typescript |
TypeScript |
vim |
Vimscript |
vimdoc |
Vim help files |
Additionally, the blade filetype is registered with treesitter for Laravel Blade template support:
vim.treesitter.language.register("blade", "blade")The nvim-treesitter-textobjects plugin provides structural selection, movement, and swapping:
Selection (Visual/Operator-pending mode):
| Keymap | Textobject |
|---|---|
af / if |
Function (outer / inner) |
ac / ic |
Class (outer / inner) |
aa / ia |
Parameter/Argument (outer / inner) |
al / il |
Loop (outer / inner) |
Movement (Normal/Visual/Operator-pending mode):
| Keymap | Description |
|---|---|
]f / [f |
Next / Previous function start |
]c / [c |
Next / Previous class start |
]a / [a |
Next / Previous argument |
Swapping (Normal mode):
| Keymap | Description |
|---|---|
<leader>xa |
Swap current parameter with the next one |
<leader>xA |
Swap current parameter with the previous one |
Both TypeScript/JavaScript and Deno use the same file extensions (.ts, .js, .tsx, .jsx), which means two LSP servers could attach to the same buffer and produce conflicting diagnostics, completions, and formatting. This configuration avoids that problem through root markers and disabled single-file support.
-
ts_ls only activates when it finds
package.jsonortsconfig.jsonin the project root:-- after/lsp/ts_ls.lua return { root_markers = { "package.json", "tsconfig.json" }, single_file_support = false, }
-
denols only activates when it finds
deno.jsonordeno.jsoncin the project root:-- after/lsp/denols.lua return { root_markers = { "deno.json", "deno.jsonc" }, single_file_support = false, }
-
Both servers have
single_file_support = false, meaning neither will attach to a loose.tsor.jsfile that is not inside a recognized project.
| Project type | Root file present | LSP attached |
|---|---|---|
| Node.js / npm project | package.json |
ts_ls |
| TypeScript project | tsconfig.json |
ts_ls |
| Deno project | deno.json |
denols |
Standalone .ts file (no project) |
None | Neither (no LSP) |
- A project should never contain both
package.jsonanddeno.jsonat the same root level. If it does, both servers will attach and conflict. - If you need LSP for standalone TypeScript/JavaScript files, you must create a minimal
tsconfig.jsonorpackage.jsonin the directory.
Follow these steps to add full support for a new language.
Edit lua/plugins/treesitter.lua and add the parser name to the ensure_installed list:
ensure_installed = {
-- ... existing parsers ...
"your_language",
},Alternatively, since auto_install = true is set, simply opening a file of that language will auto-install the parser if one exists.
Edit lua/plugins/lsp.lua and add the server to the ensure_installed list inside mason-tool-installer:
require("mason-tool-installer").setup({
ensure_installed = {
-- ... existing servers ...
"your-language-server",
},
})Find the correct Mason package name by running :Mason and searching for your language server.
Create a new file at after/lsp/<server_name>.lua that returns a configuration table. The filename must match the LSP server name exactly as registered with nvim-lspconfig.
Minimal example (no custom settings):
-- after/lsp/your_server.lua
return {}Example with custom settings:
-- after/lsp/your_server.lua
return {
root_markers = { "your_project_file.toml", ".git" },
settings = {
yourServer = {
someOption = true,
},
},
}Mason-lspconfig's automatic_enable will automatically start the server for the correct filetypes. If the server should NOT be auto-enabled by Mason (e.g., it is managed by a dedicated plugin like rustaceanvim), add it to the exclude list:
require("mason-lspconfig").setup({
automatic_enable = {
exclude = { "your_server" },
},
})Edit lua/plugins/formatting.lua and add an entry to formatters_by_ft:
formatters_by_ft = {
-- ... existing formatters ...
your_language = { "your_formatter" },
},If the formatter needs to be installed via Mason, also add it to the ensure_installed list in lua/plugins/lsp.lua.
If you want to disable LSP formatting on save for this filetype (like C/C++), add it to the disable_filetypes table in the format_on_save function:
local disable_filetypes = { c = true, cpp = true, your_language = true }Edit lua/plugins/linting.lua and add an entry to linters_by_ft:
lint.linters_by_ft = {
-- ... existing linters ...
your_language = { "your_linter" },
}If the linter needs to be installed via Mason, also add it to the ensure_installed list in lua/plugins/lsp.lua.
If the language benefits from a specialized plugin (like rustaceanvim for Rust or elixir-tools.nvim for Elixir), create a new plugin file at lua/plugins/lang-<language>.lua:
-- lua/plugins/lang-your_language.lua
return {
"author/your-language-plugin.nvim",
ft = { "your_language" },
config = function()
-- plugin setup
end,
}| Step | File to Edit/Create | Required? |
|---|---|---|
| Treesitter parser | lua/plugins/treesitter.lua |
Recommended |
| Mason install | lua/plugins/lsp.lua (ensure_installed) |
Yes (unless externally managed) |
| LSP config | after/lsp/<server>.lua |
Yes |
| Formatter | lua/plugins/formatting.lua |
Optional |
| Linter | lua/plugins/linting.lua |
Optional |
| Dedicated plugin | lua/plugins/lang-<language>.lua |
Optional |
These keybindings are available in any buffer where an LSP server is attached (defined in lua/plugins/lsp.lua via the LspAttach autocmd):
| Keymap | Mode | Description |
|---|---|---|
gd |
Normal | Go to definition (via Telescope) |
gr |
Normal | Go to references (via Telescope) |
gI |
Normal | Go to implementation (via Telescope) |
gD |
Normal | Go to declaration |
<leader>D |
Normal | Type definition (via Telescope) |
<leader>ds |
Normal | Document symbols (via Telescope) |
<leader>ws |
Normal | Workspace symbols (via Telescope) |
<leader>rn |
Normal | Rename symbol |
<leader>ca |
Normal, Visual | Code action |
<leader>th |
Normal | Toggle inlay hints |
<leader>f |
All modes | Format buffer (via conform.nvim) |
- Highlight references: When the cursor rests on a symbol (
CursorHold), all references to that symbol in the buffer are highlighted. Highlights clear when the cursor moves. - Format on save: All files are formatted on save (500ms timeout) with LSP fallback, except C/C++ where LSP formatting is disabled.
- Lint on events: nvim-lint runs on
BufWritePost,InsertLeave, andBufReadPostfor configured filetypes.
All tools managed by Mason via mason-tool-installer:
LSP Servers:
lua-language-servergoplspyrighttypescript-language-serverdenoclangdtailwindcss-language-servereslint-lspphpactor
Formatters:
styluaclang-formatprettierd
Linters:
ruffgolangci-lint
Not managed by Mason (external):
rust_analyzer(managed by rustaceanvim)nextls/elixirls(managed by elixir-tools.nvim)gleam(bundled with the Gleam compiler)svelte-language-server(must be installed separately)nomicfoundation-solidity-language-server(must be installed separately)