Skip to content

Code Explanation

Load order

init.lua is four requires, executed in order:

require("config.options")
require("config.lazy")
require("config.keymaps")
require("config.autocmds")
  1. config/options.lua — plain vim.o.* settings (line numbers, relative numbers, mouse support, split direction, 2-space indentation, etc). No plugins involved.
  2. config/lazy.lua — bootstraps lazy.nvim itself: if it isn’t already cloned into Neovim’s data directory, git clones it, prepends it to the runtimepath, then calls require("lazy").setup("plugins", { change_detection = { notify = false } }). That call auto-discovers and loads every spec file under lua/plugins/*.lua — adding a new plugin is just adding a new file there, no manual registration step.
  3. config/keymaps.lua — global keymaps, centralized here rather than scattered per-plugin file (see Usage for the full table).
  4. config/autocmds.lua — currently just the format-on-save autocmd.

Plugin spec files

Each file under lua/plugins/ returns a lazy.nvim spec table (or a list of tables), and filenames map roughly 1:1 to the plugin they configure:

FilePlugin(s)Configures
lsp.luanvim-lspconfigHand-rolled LSP startup — see below
cmp.luanvim-cmp, LuaSnipCompletion sources and snippet expansion
treesitter.luanvim-treesitterSyntax highlighting/indent for a fixed language list
telescope.luatelescope.nvimFuzzy finder layout
nvimtree.luanvim-tree.luaFile explorer
gitsigns.luagitsigns.nvimInline git hunk signs and blame
neogit.luaneogit, diffview.nvimFull git UI, lazy-loaded on the Neogit command/keymap
remote-sshfs.luaremote-sshfs.nvimRemote SSH/SSHFS browsing, registered as a Telescope extension
claudecode.luaclaudecode.nvimClaude Code AI integration
codecompanion.luacodecompanion.nvimCodeCompanion AI integration
catppuccion.luacatppuccin/nvimColorscheme (see note on the filename below)
lualine.lualualine.nvimStatusline
tabby.luatabby.nvimTabline
alpha.luaalpha-nvimStartup dashboard
noice.luanoice.nvimCmdline/message UI
snacks.luasnacks.nvimSmall-utilities library — mainly a shared dependency (claudecode.nvim, neogit)
misc.luaplenary.nvim, popup.nvim, nvim-web-devicons, Comment.nvim, which-key.nvim, firenvimSmall plugins bundled together rather than getting a file each

Note

catppuccion.lua is a typo — the filename is intentional/historical and shouldn’t be “fixed” without checking for references elsewhere in the Dotfiles repo.

LSP: hand-rolled, not nvim-lspconfig presets

lua/plugins/lsp.lua doesn’t use require("lspconfig").<server>.setup{} the way most LazyVim/kickstart tutorials do. Instead it defines a local helper:

local function setup_lsp(server_name, config)
  if vim.fn.executable(config.cmd[1]) ~= 1 then return end
  vim.lsp.start({
    name         = server_name,
    cmd          = config.cmd,
    root_dir     = config.root_dir or vim.loop.cwd(),
    settings     = config.settings or {},
    init_options = config.init_options or {},
    on_attach    = config.on_attach,
    capabilities = config.capabilities,
  })
end

and calls it once per server:

setup_lsp("pyright",  { cmd = { "pyright-langserver", "--stdio" }, ... })
setup_lsp("ts_ls",    { cmd = { "typescript-language-server", "--stdio" }, ... })
setup_lsp("bashls",   { cmd = { "bash-language-server", "start" }, ... })

The vim.fn.executable(config.cmd[1]) ~= 1 guard means a missing language server silently no-ops instead of erroring on startup — there’s no Mason-style auto-install, so if pyright-langserver isn’t on PATH, Python buffers just don’t get LSP, quietly.

Buffer-local LSP keymaps (gd, K, gr, <leader>rn) are set inside the shared on_attach function in this same file, rather than in config/keymaps.lua — they only make sense once a language server has actually attached to a buffer.

To add a new server, call setup_lsp with a new cmd table inside lsp.lua’s config function.

Two AI assistants, deliberately not merged

claudecode.nvim (depends on snacks.nvim) and codecompanion.nvim (depends on plenary.nvim) are both installed and configured independently. Their keymaps live in the same central config/keymaps.lua but under different prefixes — <leader>a* for Claude Code, <leader>c* for CodeCompanion — precisely so they don’t collide. They serve overlapping but distinct workflows (Claude Code drives an actual claude CLI session from inside Neovim; CodeCompanion is a more general chat/actions layer that can target other providers), so when editing AI-related keymaps or behavior, both claudecode.lua and codecompanion.lua need checking — they are not aliases of each other.

Format-on-save

config/autocmds.lua runs on every BufWritePre:

vim.lsp.buf.format({
  timeout_ms = 200,
  filter = function(client)
    return client.supports_method("textDocument/formatting")
  end,
})

This is global and LSP-driven — there’s no per-filetype formatter config (no conform.nvim, no null-ls/none-ls). Formatting quality depends entirely on whatever LSP client is attached to that buffer and supports the formatting method; if none do, the write just goes through unformatted (the tight 200ms timeout also means a slow formatter can silently miss its window).

Theming

Catppuccin (macchiato flavour, transparent background) is the single source of truth for colors. Rather than hardcoding hex values, both lualine.lua (via the theme = "catppuccin-macchiato" option) and alpha.lua (via require("catppuccin.palettes").get_palette("macchiato") for the dashboard header highlight) pull from Catppuccin’s own palette module. catppuccion.lua also clears the background highlight group on Normal, NormalFloat, FloatBorder, SignColumn, CursorLine, and EndOfBuffer to keep the terminal’s own background showing through. If the colorscheme ever changes, both of those files need checking for consistency.

Startup dashboard

alpha.lua reads an ASCII banner from the absolute path ~/.config/nvim/ascii/ascii-w2i.txt at startup, line by line, falling back to the literal string "Neovim" if the file can’t be opened — relevant if testing this config somewhere that path isn’t deployed. The banner is highlighted in Catppuccin’s blue, and the dashboard offers quick buttons for finding files, recent files, a new buffer, editing $MYVIMRC, and quitting.

Last updated on