From 81dc673ac2be04f9faa81d5c07ee105f7f2e971a Mon Sep 17 00:00:00 2001 From: Jelle Spreeuwenberg Date: Thu, 10 Sep 2026 10:38:36 +0200 Subject: [PATCH] feat: add Neovim agent review workflow --- modules/capabilities/neovim/default.nix | 1 + .../neovim/lua-config/lua/autocmds.lua | 6 +++ .../neovim/lua-config/lua/plugins/ui.lua | 21 ++++++++ tests/neovim-review.lua | 53 +++++++++++++++++++ 4 files changed, 81 insertions(+) create mode 100644 tests/neovim-review.lua diff --git a/modules/capabilities/neovim/default.nix b/modules/capabilities/neovim/default.nix index 186744c..fdf1729 100644 --- a/modules/capabilities/neovim/default.nix +++ b/modules/capabilities/neovim/default.nix @@ -174,6 +174,7 @@ in render-markdown-nvim lualine-nvim gitsigns-nvim + diffview-nvim zen-mode-nvim which-key-nvim ]; diff --git a/modules/capabilities/neovim/lua-config/lua/autocmds.lua b/modules/capabilities/neovim/lua-config/lua/autocmds.lua index a5f9900..4127f8e 100644 --- a/modules/capabilities/neovim/lua-config/lua/autocmds.lua +++ b/modules/capabilities/neovim/lua-config/lua/autocmds.lua @@ -1,3 +1,9 @@ +vim.api.nvim_create_autocmd({ "FocusGained", "BufEnter", "TermLeave" }, { + desc = "Reload externally changed files without discarding unsaved edits", + group = vim.api.nvim_create_augroup("lux-checktime", { clear = true }), + command = "checktime", +}) + vim.api.nvim_create_autocmd("TextYankPost", { desc = "Highlight yanked text", group = vim.api.nvim_create_augroup("lux-highlight-yank", { clear = true }), diff --git a/modules/capabilities/neovim/lua-config/lua/plugins/ui.lua b/modules/capabilities/neovim/lua-config/lua/plugins/ui.lua index 7088809..b75068b 100644 --- a/modules/capabilities/neovim/lua-config/lua/plugins/ui.lua +++ b/modules/capabilities/neovim/lua-config/lua/plugins/ui.lua @@ -74,10 +74,30 @@ require("lz.n").load({ end map_hunk("]h", "next", "Next git hunk") map_hunk("[h", "prev", "Previous git hunk") + vim.keymap.set("n", "gp", gs.preview_hunk, { buffer = bufnr, desc = "Preview git hunk" }) + vim.keymap.set("n", "gs", gs.stage_hunk, { buffer = bufnr, desc = "Stage git hunk" }) end, }) end, }, + { + "diffview.nvim", + cmd = { "DiffviewOpen", "DiffviewClose", "DiffviewRefresh", "DiffviewFileHistory" }, + keys = { + { + "gd", + function() + vim.cmd("DiffviewOpen -C" .. vim.fn.fnameescape(require("project").root())) + end, + desc = "Review project changes", + }, + { "gq", "DiffviewClose", desc = "Close git review" }, + { "gr", "DiffviewRefresh", desc = "Refresh git review" }, + }, + after = function() + require("diffview").setup({}) + end, + }, { "zen-mode.nvim", cmd = "ZenMode", @@ -108,6 +128,7 @@ require("lz.n").load({ { "b", group = "Buffers" }, { "c", group = "Code" }, { "d", group = "Diagnostics" }, + { "g", group = "Git" }, { "n", group = "Notes" }, { "q", group = "Database" }, { "r", group = "HTTP" }, diff --git a/tests/neovim-review.lua b/tests/neovim-review.lua new file mode 100644 index 0000000..d27b67a --- /dev/null +++ b/tests/neovim-review.lua @@ -0,0 +1,53 @@ +-- Run with the configured Neovim, using the same headless invocation as neovim-ergonomics.lua. +assert(package.loaded["diffview"] == nil, "Diffview should load on demand") +vim.cmd("doautocmd VimEnter") +local root = vim.fn.tempname() .. " review" +vim.fn.mkdir(root, "p") +local function git(...) + local result = vim.system({ "git", "-C", root, ... }, { text = true }):wait() + assert(result.code == 0, result.stderr) +end +local file = root .. "/tracked.txt" +git("init", "--quiet") +vim.fn.writefile({ "original" }, file) +git("add", ".") +git("-c", "user.name=Review Test", "-c", "user.email=review@example.invalid", "commit", "--quiet", "-m", "Baseline") +vim.cmd.edit(vim.fn.fnameescape(file)) +assert( + vim.wait(5000, function() + return vim.fn.maparg(" gs", "n") ~= "" + end), + "Gitsigns should attach to the tracked file" +) + +vim.fn.writefile({ "staged change" }, file) +git("add", "tracked.txt") +vim.fn.writefile({ "unstaged change after staging" }, file) +vim.fn.writefile({ "new file" }, root .. "/untracked.txt") +vim.api.nvim_exec_autocmds("FocusGained", { modeline = false }) +vim.api.nvim_feedkeys(" gd", "xt", false) +assert(package.loaded["diffview"] ~= nil, "Review shortcut should load Diffview") +local view = require("diffview.lib").get_current_view() +assert(view, "Review shortcut should open a view even when cwd is outside the repository") +assert( + vim.wait(5000, function() + return #view.files.working == 2 and #view.files.staged == 1 and view.cur_entry and view.cur_entry.opened + end), + "Review should include the tracked and untracked files" +) +local refreshed = false +view.emitter:on("file_open_post", function() + refreshed = true +end) +vim.cmd("DiffviewRefresh") +assert( + vim.wait(5000, function() + return refreshed + end), + "Refresh should finish before closing the view" +) +vim.cmd("DiffviewClose") +assert(require("diffview.lib").get_current_view() == nil) +vim.cmd("%bwipeout!") +vim.fn.delete(root, "rf") +print("Neovim agent review smoke check passed")