07a78b6012
New `nixvim` flake input (release branch, follows nixpkgs) drives the whole editor from `modules/nvim.nix`, wired into `homeModules.default`. Minimal LSP baseline (`nixd`, `bashls`) exposed as `local.nvim.lspServers`; blink-cmp, Telescope, neo-tree, lualine, treesitter, colorizer, gitsigns, toggleterm, and a catppuccin theme. which-key groups plus `<leader>?`/`<leader>sk` give an in-editor keybind cheatsheet; per-directory session persistence with `sessionoptions` + a `PersistenceSavePre` neo-tree close for clean layouts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
273 lines
12 KiB
Nix
273 lines
12 KiB
Nix
# Neovim, configured declaratively via nixvim. The editor — plugins, LSP,
|
|
# completion, keymaps — is defined here and pinned by the flake lock; nothing is
|
|
# downloaded at runtime, unlike an imperative distro (NvChad/LazyVim). The nixvim
|
|
# home-manager module that provides `programs.nixvim` is imported for consumers by
|
|
# `homeModules.default` in flake.nix, so this file only sets options.
|
|
#
|
|
# Consumer knobs (the option-with-default contract): `local.nvim.enable` to opt
|
|
# out wholesale, `local.nvim.colorscheme` to swap theme in one line, and
|
|
# `local.nvim.lspServers` to reshape the language set. Everything else is
|
|
# opinionated — but since nixvim exposes every plugin as an option, a consumer can
|
|
# still override any `programs.nixvim.plugins.*` directly from their own config.
|
|
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
let
|
|
cfg = config.local.nvim;
|
|
in
|
|
{
|
|
options.local.nvim = {
|
|
enable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
description = "Whether to enable the nixvim-based Neovim setup.";
|
|
};
|
|
|
|
colorscheme = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "catppuccin";
|
|
example = "tokyonight";
|
|
description = ''
|
|
nixvim colorscheme module to enable (the attr under `colorschemes.*`,
|
|
e.g. "catppuccin", "tokyonight", "gruvbox"). Catppuccin is tuned below
|
|
(mocha flavour); for another scheme set its `colorschemes.<name>.settings`
|
|
from your own config if you want to tweak it.
|
|
'';
|
|
};
|
|
|
|
lspServers = lib.mkOption {
|
|
type = lib.types.listOf lib.types.str;
|
|
default = [
|
|
"nixd" # Nix — this repo's own language, so a sensible public baseline
|
|
"bashls" # Shell — likewise generic enough to default on
|
|
];
|
|
example = [ "nixd" "bashls" "rust_analyzer" "basedpyright" ];
|
|
description = ''
|
|
lspconfig server names to enable under `plugins.lsp.servers.<name>`.
|
|
Each must exist in nixvim's server list and its binary in nixpkgs. The
|
|
public default is deliberately minimal (Nix + shell); a consumer sets its
|
|
own language set here — and any per-server tuning a pick needs (e.g.
|
|
rust_analyzer's `installRustc`) via `programs.nixvim.plugins.lsp.servers`.
|
|
Note: standalone Lua wants "lua_ls", but Lua embedded in Nix strings is
|
|
invisible to any LSP, so it is not needed just to edit this config.
|
|
'';
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
programs.nixvim = {
|
|
enable = true;
|
|
|
|
# Point nixvim at our (followed) nixpkgs instead of the rev it pins. Both
|
|
# track nixos-26.05, so the version skew nixvim warns about does not arise,
|
|
# and this keeps a single nixpkgs in the closure. Setting it explicitly is
|
|
# also what silences that warning.
|
|
nixpkgs.source = pkgs.path;
|
|
|
|
# System clipboard on Wayland: yanks go to / puts read from wl-clipboard,
|
|
# so copy/paste crosses the nvim boundary. Enabling the provider pulls the
|
|
# wl-clipboard binaries in as a dependency.
|
|
clipboard = {
|
|
register = "unnamedplus";
|
|
providers.wl-copy.enable = true;
|
|
};
|
|
|
|
globals = {
|
|
mapleader = " ";
|
|
maplocalleader = " ";
|
|
};
|
|
|
|
opts = {
|
|
number = true;
|
|
relativenumber = true; # relative line numbers for quick j/k motions
|
|
signcolumn = "yes"; # always show the gutter so text doesn't jump
|
|
cursorline = true;
|
|
termguicolors = true; # 24-bit colour, required by catppuccin & co.
|
|
scrolloff = 8; # keep 8 lines of context around the cursor
|
|
|
|
expandtab = true; # spaces, not tabs
|
|
shiftwidth = 2;
|
|
tabstop = 2;
|
|
smartindent = true;
|
|
|
|
ignorecase = true; # case-insensitive search…
|
|
smartcase = true; # …unless the query has a capital
|
|
undofile = true; # persistent undo across sessions
|
|
splitright = true;
|
|
splitbelow = true;
|
|
|
|
# What :mksession stores (persistence.nvim). Drops "blank" and "terminal"
|
|
# from nvim's default so empty/plugin windows (neo-tree, toggleterm) aren't
|
|
# serialised — they restore as broken empty splits otherwise. Adds
|
|
# "globals", which some plugins rely on. Pairs with the PersistenceSavePre
|
|
# neo-tree close below.
|
|
sessionoptions = "buffers,curdir,folds,globals,tabpages,winsize";
|
|
};
|
|
|
|
# Theme. Swap via local.nvim.colorscheme; catppuccin gets the mocha flavour.
|
|
# mkMerge keeps the computed `${cfg.colorscheme}` key from colliding with the
|
|
# literal `catppuccin` one when they happen to be the same attr.
|
|
colorschemes = lib.mkMerge [
|
|
{ ${cfg.colorscheme}.enable = true; }
|
|
(lib.mkIf (cfg.colorscheme == "catppuccin") {
|
|
catppuccin.settings.flavour = "mocha";
|
|
})
|
|
];
|
|
|
|
plugins = {
|
|
# Shared icon set for the tree, statusline, completion menu.
|
|
web-devicons.enable = true;
|
|
|
|
# Syntax via tree-sitter. The default grammar set is broad, so most
|
|
# languages get highlighting without an LSP or any per-language wiring.
|
|
treesitter = {
|
|
enable = true;
|
|
settings = {
|
|
highlight.enable = true;
|
|
indent.enable = true;
|
|
};
|
|
};
|
|
|
|
# LSP. Servers come from local.nvim.lspServers; blink-cmp feeds its
|
|
# completion capabilities in automatically (setupLspCapabilities).
|
|
lsp = {
|
|
enable = true;
|
|
servers = lib.genAttrs cfg.lspServers (_: { enable = true; });
|
|
keymaps = {
|
|
silent = true;
|
|
lspBuf = {
|
|
gd = "definition";
|
|
gD = "declaration";
|
|
K = "hover";
|
|
"<leader>rn" = "rename";
|
|
"<leader>ca" = "code_action";
|
|
};
|
|
# gr/gi (references, implementations) are wired to Telescope pickers in
|
|
# the global keymaps below instead — fuzzy + preview browsing beats the
|
|
# quickfix list that vim.lsp.buf.references would populate. gd stays a
|
|
# direct jump here (usually one target; Ctrl-o returns).
|
|
diagnostic = {
|
|
# Idiomatic vim bracket pair for stepping a list, kept as the public
|
|
# default. A consumer whose layout makes brackets awkward (they need
|
|
# AltGr on QWERTZ) can add comfortable aliases from their own config:
|
|
# this option is attrsOf, so extra keys merge in without conflict.
|
|
"[d" = "goto_prev";
|
|
"]d" = "goto_next";
|
|
"<leader>ds" = "open_float"; # show the diagnostic under the cursor
|
|
};
|
|
};
|
|
};
|
|
|
|
# Completion. blink.cmp — faster and lighter to configure than nvim-cmp.
|
|
blink-cmp = {
|
|
enable = true;
|
|
settings = {
|
|
keymap.preset = "default"; # C-space open, C-y confirm, C-n/C-p move
|
|
appearance.nerd_font_variant = "mono";
|
|
sources.default = [ "lsp" "path" "snippets" "buffer" ];
|
|
completion.documentation.auto_show = true;
|
|
};
|
|
};
|
|
|
|
# Fuzzy finder — files, live grep, open buffers (buffer switching lives
|
|
# here rather than a bufferline; see the S-h/S-l binds below too).
|
|
telescope.enable = true;
|
|
|
|
# File tree.
|
|
neo-tree.enable = true;
|
|
|
|
# Statusline, NvChad-ish blocks: powerline separators, one global bar,
|
|
# theme following the colorscheme.
|
|
lualine = {
|
|
enable = true;
|
|
settings.options = {
|
|
theme = "auto";
|
|
globalstatus = true;
|
|
section_separators = { left = ""; right = ""; };
|
|
component_separators = { left = ""; right = ""; };
|
|
};
|
|
};
|
|
|
|
# Inline colour swatches for #rrggbb / colour names.
|
|
colorizer.enable = true;
|
|
|
|
# Git signs in the gutter.
|
|
gitsigns.enable = true;
|
|
|
|
# Session persistence: auto-saves a session per project directory into XDG
|
|
# state, restorable later. <leader>S restores the current dir's session; a
|
|
# consumer wanting auto-restore on a bare `nvim` adds a VimEnter autocmd.
|
|
persistence.enable = true;
|
|
|
|
# Floating terminal toggled with Ctrl-\ (shares nvim's cwd). Try it; if
|
|
# a tiled foot next to nvim serves you better, drop this block.
|
|
toggleterm = {
|
|
enable = true;
|
|
settings = {
|
|
open_mapping = { __raw = "[[<c-\\>]]"; };
|
|
direction = "float";
|
|
};
|
|
};
|
|
|
|
# Popup of the pending keybinds after leader — a live, always-accurate
|
|
# cheatsheet. The group labels name each leader subtree so the popup reads
|
|
# by category; <leader>? opens the full list on demand (keymaps below).
|
|
which-key = {
|
|
enable = true;
|
|
settings.spec = [
|
|
{ __unkeyed-1 = "<leader>f"; group = "Find (Telescope)"; }
|
|
{ __unkeyed-1 = "<leader>d"; group = "Diagnostics"; }
|
|
{ __unkeyed-1 = "<leader>b"; group = "Buffer"; }
|
|
{ __unkeyed-1 = "<leader>c"; group = "Code"; }
|
|
{ __unkeyed-1 = "<leader>r"; group = "Refactor"; }
|
|
{ __unkeyed-1 = "<leader>s"; group = "Search"; }
|
|
];
|
|
};
|
|
};
|
|
|
|
# Global (non-LSP) keymaps. Deliberately small — a starting point to grow.
|
|
keymaps = [
|
|
{ mode = "n"; key = "<leader>w"; action = "<cmd>w<cr>"; options.desc = "Save"; }
|
|
{ mode = "n"; key = "<leader>q"; action = "<cmd>q<cr>"; options.desc = "Quit window"; }
|
|
{ mode = "n"; key = "<leader>Q"; action = "<cmd>qa<cr>"; options.desc = "Quit all (exit nvim)"; }
|
|
# Restore the saved session for the current directory (persistence.nvim).
|
|
{ mode = "n"; key = "<leader>S"; action.__raw = "function() require('persistence').load() end"; options.desc = "Restore session"; }
|
|
{ mode = "n"; key = "<leader>e"; action = "<cmd>Neotree toggle<cr>"; options.desc = "File tree"; }
|
|
{ mode = "n"; key = "<Esc>"; action = "<cmd>nohlsearch<cr>"; }
|
|
|
|
# Telescope
|
|
{ mode = "n"; key = "<leader>ff"; action = "<cmd>Telescope find_files<cr>"; options.desc = "Find files"; }
|
|
{ mode = "n"; key = "<leader>fg"; action = "<cmd>Telescope live_grep<cr>"; options.desc = "Live grep"; }
|
|
{ mode = "n"; key = "<leader>fb"; action = "<cmd>Telescope buffers<cr>"; options.desc = "Buffers"; }
|
|
{ mode = "n"; key = "<leader>fh"; action = "<cmd>Telescope help_tags<cr>"; options.desc = "Help tags"; }
|
|
|
|
# LSP navigation via Telescope pickers (fuzzy + preview) for the
|
|
# many-result lookups; gd/gD stay direct jumps (plugins.lsp.keymaps above).
|
|
{ mode = "n"; key = "gr"; action = "<cmd>Telescope lsp_references<cr>"; options.desc = "References"; }
|
|
{ mode = "n"; key = "gi"; action = "<cmd>Telescope lsp_implementations<cr>"; options.desc = "Implementations"; }
|
|
|
|
# Buffer navigation (no bufferline: cycle with Shift-h/l, list via <leader>fb)
|
|
{ mode = "n"; key = "<S-l>"; action = "<cmd>bnext<cr>"; options.desc = "Next buffer"; }
|
|
{ mode = "n"; key = "<S-h>"; action = "<cmd>bprevious<cr>"; options.desc = "Prev buffer"; }
|
|
{ mode = "n"; key = "<leader>bd"; action = "<cmd>bdelete<cr>"; options.desc = "Close buffer"; }
|
|
|
|
# Cheatsheet: <leader>? pops the full which-key list; <leader>sk fuzzy-
|
|
# searches every active mapping via Telescope (catches non-leader binds too).
|
|
{ mode = "n"; key = "<leader>?"; action = "<cmd>WhichKey<cr>"; options.desc = "All keybinds (which-key)"; }
|
|
{ mode = "n"; key = "<leader>sk"; action = "<cmd>Telescope keymaps<cr>"; options.desc = "Search keymaps"; }
|
|
];
|
|
|
|
# persistence saves the window layout via :mksession, but neo-tree's special
|
|
# buffer doesn't serialise — a saved tree window restores as a blank split.
|
|
# Close neo-tree just before a session is written so the layout stays clean.
|
|
autoCmd = [
|
|
{
|
|
event = "User";
|
|
pattern = "PersistenceSavePre";
|
|
callback.__raw = ''function() pcall(vim.cmd, "Neotree close") end'';
|
|
}
|
|
];
|
|
};
|
|
};
|
|
}
|