feat(home): add declarative Neovim via nixvim
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>
This commit is contained in:
Generated
+60
@@ -1,5 +1,26 @@
|
|||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
|
"flake-parts": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": [
|
||||||
|
"nixvim",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1778716662,
|
||||||
|
"narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"home-manager": {
|
"home-manager": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -37,10 +58,34 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nixvim": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-parts": "flake-parts",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1782919967,
|
||||||
|
"narHash": "sha256-pRwjfB5HQJ3m8J8bOR43pPHtHI7VUJSqwLA3P06cOY0=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixvim",
|
||||||
|
"rev": "667c8471f4a0fb24d702d1a61af8609f1a5f1ba6",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"ref": "nixos-26.05",
|
||||||
|
"repo": "nixvim",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"home-manager": "home-manager",
|
"home-manager": "home-manager",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
|
"nixvim": "nixvim",
|
||||||
"rust-overlay": "rust-overlay"
|
"rust-overlay": "rust-overlay"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -63,6 +108,21 @@
|
|||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": "root",
|
"root": "root",
|
||||||
|
|||||||
@@ -17,9 +17,20 @@
|
|||||||
url = "github:oxalica/rust-overlay";
|
url = "github:oxalica/rust-overlay";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# nixvim: declarative Neovim as home-manager options — the whole editor
|
||||||
|
# (plugins, LSP, keymaps) lives in modules/nvim.nix, pinned by this flake's
|
||||||
|
# lock. Track the release branch matching nixpkgs (a mismatched branch fails
|
||||||
|
# with `vimPlugins.<name> not found`). `follows` keeps one nixpkgs: both are
|
||||||
|
# on the same stable channel, so the version skew nixvim warns about (only an
|
||||||
|
# issue against unstable) does not arise here.
|
||||||
|
nixvim = {
|
||||||
|
url = "github:nix-community/nixvim/nixos-26.05";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = inputs@{ self, nixpkgs, home-manager, rust-overlay, ... }:
|
outputs = inputs@{ self, nixpkgs, home-manager, rust-overlay, nixvim, ... }:
|
||||||
let
|
let
|
||||||
system = "x86_64-linux";
|
system = "x86_64-linux";
|
||||||
in {
|
in {
|
||||||
@@ -28,7 +39,11 @@
|
|||||||
# imports = [ config.nixosModules.default ];
|
# imports = [ config.nixosModules.default ];
|
||||||
# home-manager.users.<you>.imports = [ config.homeModules.default ];
|
# home-manager.users.<you>.imports = [ config.homeModules.default ];
|
||||||
nixosModules.default = import ./modules/nixos.nix;
|
nixosModules.default = import ./modules/nixos.nix;
|
||||||
homeModules.default = import ./modules/home.nix;
|
# nixvim's home-manager module rides along so `modules/nvim.nix` can set
|
||||||
|
# `programs.nixvim.*`; consumers get the editor by importing this one module.
|
||||||
|
homeModules.default = {
|
||||||
|
imports = [ nixvim.homeModules.nixvim ./modules/home.nix ];
|
||||||
|
};
|
||||||
|
|
||||||
# ---- The example / template host -----------------------------------
|
# ---- The example / template host -----------------------------------
|
||||||
# Real and buildable; dogfoods the modules above. Clone, replace
|
# Real and buildable; dogfoods the modules above. Clone, replace
|
||||||
|
|||||||
+1
-1
@@ -122,7 +122,7 @@ let
|
|||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [ ./gpg.nix ./media.nix ./texlive.nix ./librewolf.nix ./mc.nix ./mime.nix ./hyprland-workspaces.nix ];
|
imports = [ ./gpg.nix ./media.nix ./texlive.nix ./librewolf.nix ./mc.nix ./mime.nix ./hyprland-workspaces.nix ./nvim.nix ];
|
||||||
|
|
||||||
# Put ~/.local/bin (the XDG user-bin dir) on PATH so hand-dropped scripts run
|
# Put ~/.local/bin (the XDG user-bin dir) on PATH so hand-dropped scripts run
|
||||||
# by name without wiring each through nix.
|
# by name without wiring each through nix.
|
||||||
|
|||||||
@@ -0,0 +1,272 @@
|
|||||||
|
# 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'';
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user