This is a personal Neovim configuration (~/.config/nvim/) using
lazy.nvim as plugin manager, catppuccin as colorscheme, and
Neovim's experimental built-in UI (vim._core.ui2) for
cmdline/messages/popupmenu.
init.lua → vim.loader, vim._core.ui2, ColorScheme autocmd, vim.cmd.colorscheme
├── lsp/ → built-in vim.lsp.Config files (filename = server name)
└── lua/init.lua → loads config/{key,options,autocmd,lazy,lsp}
├── config/key.lua → basic keymaps (leader = Space)
├── config/options.lua → editor options (default indent=4 spaces, winborder=rounded)
├── config/autocmd.lua → global autocmds (winfixbuf, yank highlight, treesitter auto-enable/install)
├── config/lazy.lua → lazy.nvim bootstrap, `spec = 'plugins'`
├── config/lsp.lua → global LSP enable list + diagnostic config
└── plugins/init.lua → imports plugins.core + plugins.lang only
Plugins are split into two categories under lua/plugins/:
plugins/core/— Infrastructure plugins used across all languages (LSP base, completion, formatter, treesitter, UI, git, navigation, etc.). Each file = one plugin concern.plugins/lang/— Language-specific plugin and formatter extensions. These files should only extend lazy specs or add language plugins; they are not where LSP server configs live anymore.
This pattern means:
- LSP config belongs in
lsp/<server>.luaand should return avim.lsp.Configtable:-- lsp/foo_ls.lua ---@type vim.lsp.Config return { settings = { foo = { enable = true, }, }, }
- LSP enablement is centralized in
lua/config/lsp.luaviavim.g.lsp+vim.lsp.enable(vim.g.lsp). - Never duplicate core plugin specs in lang files. Use lang files to merge into existing specs like
conform.nvim, or to add language-specific plugins such asflutter-tools.nvim.
LSP uses Neovim's built-in vim.lsp.config() + vim.lsp.enable() API (no custom wrapper types):
- All server config tables live under
lsp/*.lua, both generic and language-specific. - All enabled servers are listed in
lua/config/lsp.lua. - Mason:
plugins/core/lspconfig.luamaintains a flatmason_ensurelist of mason-managed packages (servers + formatters/linters). Servers not managed by mason, such asfish_lsp,clangd, andruff, are omitted from this list. - LSP keymaps and defaults live in
plugins/core/lspconfig.luainside theLspAttachautocmd, not in language files.
Treesitter is split across two places:
plugins/core/treesitter.luaconfigures textobjects and context plugins.config/autocmd.luaownsvim.g.ts_langsand aFileTypeautocmd that lazily installs parsers and enables treesitter features on demand.
Do not add ensure_installed lists in language files unless the architecture changes again. If a new filetype should participate in the current auto-install flow, update vim.g.ts_langs.
Catppuccin integrations are enabled by returning a catppuccin spec alongside the plugin spec:
---@type LazyPluginSpec[]
return {
{ 'some/plugin.nvim', opts = { ... } },
{ 'catppuccin/nvim', opts = { integrations = { plugin_name = true } } },
}This is used across plugins such as aerial, blink, dropbar, gitsigns, and snacks.
- Lua indent: 2 spaces (see
.stylua.tomland.editorconfig) - StyLua is the formatter:
column_width = 80,quote_style = AutoPreferSingle,call_parentheses = None,collapse_simple_statement = Always - Function calls without parens where idiomatic:
require 'foo',vim.cmd.colorscheme 'catppuccin' - Plugin specs use
---@type LazyPluginSpecor---@type LazyPluginSpec[]annotations - Each plugin file returns either a single spec (
LazyPluginSpec) or a list (LazyPluginSpec[]), never a function - Keymaps inside plugin specs use the
keys = { { lhs, rhs, desc = '...' } }lazy.nvim format - Use
fzf-luafor fuzzy finding, search, andvim.ui.select - Use
Snacks.togglefor toggle mappings - If filetype-specific buffer options are needed, prefer
after/ftplugin/
- Leader: Space
- Keymap groups (defined in which-key):
<leader>c(Code),<leader>b(Buffer),<leader>f(Find Files),<leader>s(Search Grep),<leader>g(Git),<leader>n(Notification),<leader>t(Toggle) - LSP keymaps are set in lspconfig's
LspAttachautocmd, not in individual lang files - Window navigation:
<C-hjkl>in normal,<Tab>cycles windows - Visual line move:
<C-jk>in visual mode - File explorer:
\(MiniFiles cwd),<leader>\(MiniFiles current file)
- Do NOT introduce noice.nvim — it was deliberately removed in favor of
vim._core.ui2 - Do NOT introduce telescope.nvim —
fzf-luais the fuzzy finder - Do NOT introduce neo-tree — mini.files is the file explorer
- Avoid adding more folke ecosystem plugins unless clearly necessary;
snacks.nvimis already used for a few focused utilities, but not as the general picker layer - Neovim version is bleeding-edge (uses
vim._core.ui2,vim.lsp.config(),vim.lsp.enable())
- Create
lua/plugins/lang/<language>.lua - If the language needs LSP config, create
lsp/<server>.luareturning avim.lsp.Configtable - Add the server name to
vim.g.lspinlua/config/lsp.lua - Add the server to
mason_ensureinplugins/core/lspconfig.luaunless it is not mason-managed - Return a
LazyPluginSpecorLazyPluginSpec[]from the lang file that extends core specs:- Add formatter(s) to
conform.nvimopts if needed - Add language-specific plugins as additional specs if needed
- Add formatter(s) to
- If the language should use the current treesitter auto-install flow, add its filetype to
vim.g.ts_langsinlua/config/autocmd.lua - If the language needs custom buffer options, add
after/ftplugin/<filetype>.lua - No changes to
plugins/init.luaare needed; lazy.nvim auto-imports fromplugins/lang/
- Create
lua/plugins/core/<plugin-name>.lua - Return
LazyPluginSpec(single) orLazyPluginSpec[](if catppuccin integration needed) - Use lazy-loading (
event,ft,cmd,keys) wherever possible - No changes to
plugins/init.luaneeded