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.