Split config.fish into repo fragments, concatenated by Nix

Break the interactive init into concern-scoped fish files under
modules/fish/config/ (bindings, env, done, path) and assemble them with
lib.concatMapStringsSep + readFile into a single interactiveShellInit, so
home-manager still writes one ~/.config/fish/config.fish. The pieces stay
editable as separate fish files; the rendered file is unchanged.
This commit is contained in:
2026-07-18 19:05:39 -04:00
parent 5791903b46
commit 0718098c13
7 changed files with 32 additions and 25 deletions

View File

@@ -39,4 +39,6 @@ Configure the plugins natively through home-manager rather than a fish plugin ma
- **Abbreviation-first.** Every non-eza alias is now a `shellAbbr` (the eza `ls` family stays an alias), `preferAbbrs = true`, and `generateCompletions = true` is pinned rather than left to the upstream default. - **Abbreviation-first.** Every non-eza alias is now a `shellAbbr` (the eza `ls` family stays an alias), `preferAbbrs = true`, and `generateCompletions = true` is pinned rather than left to the upstream default.
- **vi command-line editing.** `interactiveShellInit` sets `fish_key_bindings fish_vi_key_bindings`; the `bang-bang` plugin re-binds `!`/`$` in insert mode via its own `--on-variable fish_key_bindings` handler, so the switch keeps them working. - **vi command-line editing.** `interactiveShellInit` sets `fish_key_bindings fish_vi_key_bindings`; the `bang-bang` plugin re-binds `!`/`$` in insert mode via its own `--on-variable fish_key_bindings` handler, so the switch keeps them working.
- **Trimmed aliases.** Navigation capped at four dots (`.....`/`......` dropped); `psmem`, `psmem10`, `dir`, `vdir`, and `please` removed. - **Trimmed aliases.** Navigation capped at four dots (`.....`/`......` dropped); `psmem`, `psmem10`, `dir`, `vdir`, and `please` removed.
- **Module directory mirrors `~/.config/fish/`.** The Module lives at `modules/fish/fish.nix` with its hand-written fish laid out as in a real fish config: `config.fish` (the whole interactive init, read into `interactiveShellInit`) and `functions/copy.fish` (the non-trivial `copy` body, read into the `functions` option). Trivial one-liner functions stay inline in `fish.nix`. Everything Nix assembles at build time — nothing of ours is autoloaded from a separate runtime file — so the `done` plugin tuning stays inside `config.fish` rather than a `conf.d` fragment (`conf.d` would only earn its name if fish autoloaded it at runtime). `functions/copy.fish` is the sole exception, because fish autoloads function files lazily and that is the idiomatic home for a function. `completions/`/`themes/`/`conf.d/` are omitted as they hold no content of ours and git cannot track empty directories. The Auto-loader only collects `.nix`, so every `.fish` file is inert to it. - **Interactive init is split in the repo, assembled by Nix.** The Module lives at `modules/fish/fish.nix`. Its `interactiveShellInit` is concatenated (in order) from concern-scoped fragments under `modules/fish/config/``bindings.fish` (vi editing), `env.fish` (`EDITOR`/`VISUAL` and the bat manpager), `done.fish` (the done plugin tuning), and `path.fish` (`~/.local/bin` and `~/.fish_profile`) — via `lib.concatMapStringsSep "\n" builtins.readFile`. Each concern stays an editable fish file, but home-manager still writes one `~/.config/fish/config.fish`; nothing of ours is autoloaded from a separate runtime file. The concatenated result is byte-identical to the previous monolithic `config.fish` (same toplevel hash).
- **`copy` stays a function file.** `functions/copy.fish` holds the non-trivial `copy` body, read into the `functions` option; fish autoloads function files lazily, so that is the idiomatic home for a function. Trivial one-liner functions stay inline in `fish.nix`.
- The Auto-loader only collects `.nix`, so every `.fish` file under `modules/fish/` is inert to it.

View File

@@ -1,23 +0,0 @@
# vi-style modal editing on the command line.
set -g fish_key_bindings fish_vi_key_bindings
set -gx EDITOR nvim
set -gx VISUAL nvim
# Render man pages through bat.
set -x MANROFFOPT "-c"
set -x MANPAGER "sh -c 'col -bx | bat -l man -p'"
# Tune the done plugin: only notify for commands past 10s, at low urgency.
set -g __done_min_cmd_duration 10000
set -g __done_notification_urgency_level low
# Prepend ~/.local/bin to PATH when it exists.
if test -d ~/.local/bin
fish_add_path ~/.local/bin
end
# Apply fish-compatible profile overrides if present.
if test -f ~/.fish_profile
source ~/.fish_profile
end

View File

@@ -0,0 +1,2 @@
# vi-style modal editing on the command line.
set -g fish_key_bindings fish_vi_key_bindings

View File

@@ -0,0 +1,3 @@
# Tune the done plugin: only notify for commands past 10s, at low urgency.
set -g __done_min_cmd_duration 10000
set -g __done_notification_urgency_level low

View File

@@ -0,0 +1,6 @@
set -gx EDITOR nvim
set -gx VISUAL nvim
# Render man pages through bat.
set -x MANROFFOPT "-c"
set -x MANPAGER "sh -c 'col -bx | bat -l man -p'"

View File

@@ -0,0 +1,9 @@
# Prepend ~/.local/bin to PATH when it exists.
if test -d ~/.local/bin
fish_add_path ~/.local/bin
end
# Apply fish-compatible profile overrides if present.
if test -f ~/.fish_profile
source ~/.fish_profile
end

View File

@@ -115,7 +115,15 @@ in
}; };
}; };
interactiveShellInit = builtins.readFile ./config.fish; # config.fish is assembled here, in order, from the fragments under
# ./config so each concern stays an editable fish file while
# home-manager still writes a single ~/.config/fish/config.fish.
interactiveShellInit = lib.concatMapStringsSep "\n" builtins.readFile [
./config/bindings.fish
./config/env.fish
./config/done.fish
./config/path.fish
];
}; };
}; };
}; };