feat: nix keybind system, delete snacks, add lazygit/fzf

i've had issues with snacks' file tree, to the point where the delete
operation would corrupt my editor state. the worst of which being a bug
that caused my entire project to get deleted when pressing 'd' on a
directory with corrupt state[1]. it's unacceptable, and i no longer
trust any of the 'snacks.nvim' project.

the neotree plugin is much nicer in my opinion, and what's great about
it is there is a easy command list by pressing '?' on the file view.

the big part of this commit is introducing 'vim.keybinds' (not to be
confused with the existing 'keymaps' array). we now have a centralized
system for defining keyboard shortcuts. in theory, user configs can
override my default keymap, but i think the current users will
appreciate these default settings. theres a handful of built-in keybinds
for activating the newly added fzf-lua plugin (replaces telescope and
snacks' pickers, losing no functionality).

as one bonus treat, leader g g for integrated lazygit!

[1]:
af60d1172f
This commit is contained in:
clover caruso 2025-08-18 00:25:23 -07:00
parent ce8580e840
commit 6cb28f1a2d
3 changed files with 160 additions and 90 deletions

View file

@ -6,8 +6,9 @@
}:
{
imports = [
./options.nix
./formatter.nix
./keybind.nix
./options.nix
];
# based on default options from upstream:
# https://github.com/NotAShelf/nvf/blob/main/configuration.nix
@ -18,6 +19,10 @@
# override level 999 is used to not conflict with mkDefault as used by nvf.
# which allows user configurations to disable/override anything here.
config.vim = lib.mkOverride 999 {
extraLuaFiles = [
./lib.lua
];
theme = {
enable = true;
};
@ -60,8 +65,8 @@
listReferences = "gr";
goToType = "gy";
hover = "K";
nextDiagnostic = "<leader>d";
openDiagnosticFloat = "<leader>df";
nextDiagnostic = null; # ]d
openDiagnosticFloat = "<leader>d";
renameSymbol = "rn";
documentHighlight = null;
listDocumentSymbols = null;
@ -78,11 +83,9 @@
enable = true;
addDefaultGrammars = true;
};
debugger = {
nvim-dap = {
enable = true;
ui.enable = true;
};
debugger.nvim-dap = {
enable = true;
ui.enable = true;
};
languages = {
enableFormat = true;
@ -96,36 +99,58 @@
clang.enable = true;
css.enable = true;
html.enable = true;
lua.enable = true;
markdown.enable = true;
nix.enable = true;
python.enable = true;
rust.crates.enable = true;
rust.enable = true;
ts.enable = true;
zig.enable = true;
lua.enable = true;
# sort-lines: off
nix = {
enable = true;
format.type = "nixfmt"; # looks so much nicer
};
filetree.neo-tree = {
enable = true;
setupOpts = {
enable_cursor_hijack = true;
git_status_async = true;
};
};
filetree = {
neo-tree = {
enable = false;
fzf-lua = {
enable = true;
setupOpts = {
fzf_colors = true;
};
};
tabline = {
nvimBufferline.enable = true;
};
autocomplete = {
blink-cmp = {
enable = true;
sourcePlugins = {
ripgrep.enable = true;
autocomplete.blink-cmp = {
enable = true;
mappings = {
close = null;
complete = null;
confirm = null;
next = null;
previous = null;
scrollDocsDown = null;
scrollDocsUp = null;
};
setupOpts = {
keymap = {
preset = "super-tab";
};
friendly-snippets.enable = true;
completion = {
ghost_text.enabled = false;
list.selection.preselect = true;
trigger = {
show_in_snippet = true;
};
accept.auto_brackets.enabled = true;
};
signature.enabled = true;
};
sourcePlugins = {
ripgrep.enable = true;
};
friendly-snippets.enable = true;
};
statusline = {
lualine = {
@ -137,71 +162,6 @@
};
};
};
utility = {
snacks-nvim = {
enable = true;
setupOpts = {
bigfile.enable = true;
dashboard = {
preset.keys = [
{
icon = " ";
key = "n";
desc = "New File";
action = ":ene | startinsert";
}
{
icon = " ";
key = "r";
desc = "Recent Files";
action = ":lua Snacks.dashboard.pick('oldfiles')";
}
];
sections = [
{ section = "header"; }
{
section = "keys";
indent = 2;
padding = 1;
}
{
icon = " ";
title = "Projects";
section = "projects";
indent = 2;
padding = 1;
}
{
icon = " ";
title = "Git";
section = "terminal";
enabled = lib.options.literalExpression ''
function()
return Snacks.git.get_root() ~= nil
end
'';
cmd = "git status --short --branch --renames";
height = 10;
padding = 1;
ttl = 5 * 60;
indent = 3;
}
];
};
image = {
enable = true;
math.enabled = false;
};
notifier.timeout = 3000;
picker = {
enable = true;
sources = {
};
};
};
};
};
binds = {
whichKey.enable = true;
cheatsheet.enable = true;

View file

@ -0,0 +1,83 @@
# this file implements a keybind system, which is a higher level system
# to configure vim.keymaps (note the different name bind vs map)
{
pkgs,
lib,
config,
...
}:
let
keyRemap = mode: key: action: { inherit mode key action; };
keyCmd =
mode: key: cmd:
keyRemap mode key ":${cmd}<Return>";
in
{
# default binds
config.vim.keybinds = {
search-commands = keyCmd "n" "<leader>?" "FzfLua keymaps";
# user interface
toggle-explorer = keyCmd "n" "<leader>e" "Neotree toggle";
reveal-active-file = keyCmd "n" "<leader>E" "Neotree reveal<CR>:Neotree focus";
lazygit = keyCmd "n" "<leader>gg" "FullscreenTerm ${pkgs.lazygit}/bin/lazygit";
# pickers
pick-file = keyCmd "n" "<leader><leader>" "FzfLua files";
pick-mark = keyCmd "n" "<leader>'" "FzfLua marks";
#pick-buffer = keyCmd "n" "<leader>b" "FzfLua buffers";
pick-grep = keyCmd "n" "<leader>ff" "FzfLua grep_project";
pick-recent-command = keyCmd "n" "<leader>fc" "FzfLua command_history";
pick-other = keyCmd "n" "<leader>f?" "FzfLua builtin"; # picker of Fzf pickers
# lsp
code-action =
keyCmd "n" "<leader>ca"
"FzfLua lsp_code_actions winopts.height=15 winopts.backdrop=100 winopts.title=false winopts.preview.title=false winopts.row=1";
# subtle nice features
visual-dedent = keyRemap "v" "<" "<gv"; # keep selection
visual-indent = keyRemap "v" ">" ">gv"; # keep selection
clear-search-highlights = keyRemap "n" "<esc" ":noh<Return><esc>";
};
# implementation
options.vim.keybinds = lib.mkOption {
type = lib.types.attrsOf (
lib.types.nullOr (
lib.types.submodule {
options = {
mode = lib.mkOption { type = lib.types.str; };
key = lib.mkOption { type = lib.types.str; };
action = lib.mkOption { type = lib.types.str; };
};
}
)
);
default = { };
};
config.vim.keymaps =
let
titleCase =
str:
lib.concatStringsSep " " (
map (
word:
lib.strings.toUpper (builtins.substring 0 1 word)
+ builtins.substring 1 (builtins.stringLength word) word
) (lib.splitString "-" str)
);
in
builtins.filter (f: f != null) (
lib.attrsets.mapAttrsToList (
desc: bind:
if bind != null then
{
desc = titleCase desc;
inherit (bind) mode key action;
}
else
null
) config.vim.keybinds
);
}

27
modules/neovim/lib.lua Normal file
View file

@ -0,0 +1,27 @@
-- Ported from https://www.reddit.com/r/neovim/comments/vemydn
vim.api.nvim_create_user_command("FullscreenTerm", function(opts)
vim.cmd("tab terminal " .. opts.args)
local laststatus = vim.o.laststatus
local showtabline = vim.o.showtabline
local cmdheight = vim.o.cmdheight
vim.o.laststatus = 0
vim.o.cmdheight = 0
vim.o.showtabline = 0
vim.wo.signcolumn = "no"
vim.wo.relativenumber = false
vim.wo.number = false
vim.cmd(
"autocmd! TermClose <buffer=abuf> "
.. "if !v:event.status"
.. " | exec 'bd! '..expand('<abuf>')"
.. " | endif"
.. " | checktime"
.. " | set laststatus="
.. laststatus
.. " | set cmdheight="
.. cmdheight
.. " | set showtabline="
.. showtabline
)
vim.cmd("startinsert")
end, { nargs = "*" })