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": {
|
||||
"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": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@@ -37,10 +58,34 @@
|
||||
"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": {
|
||||
"inputs": {
|
||||
"home-manager": "home-manager",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixvim": "nixvim",
|
||||
"rust-overlay": "rust-overlay"
|
||||
}
|
||||
},
|
||||
@@ -63,6 +108,21 @@
|
||||
"repo": "rust-overlay",
|
||||
"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",
|
||||
|
||||
@@ -17,9 +17,20 @@
|
||||
url = "github:oxalica/rust-overlay";
|
||||
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
|
||||
system = "x86_64-linux";
|
||||
in {
|
||||
@@ -28,7 +39,11 @@
|
||||
# imports = [ config.nixosModules.default ];
|
||||
# home-manager.users.<you>.imports = [ config.homeModules.default ];
|
||||
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 -----------------------------------
|
||||
# Real and buildable; dogfoods the modules above. Clone, replace
|
||||
|
||||
+1
-1
@@ -122,7 +122,7 @@ let
|
||||
'';
|
||||
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
|
||||
# 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