From cb05820ac141a6f41db054b847643b28aba66134 Mon Sep 17 00:00:00 2001 From: Sam Natale Date: Tue, 14 Apr 2026 11:40:31 +0100 Subject: [PATCH 1/5] add restore diagnostics command --- README.md | 1 + doc/coderabbit.txt | 8 +++ lua/coderabbit/init.lua | 4 ++ lua/coderabbit/review.lua | 28 +++++++++ plugin/coderabbit.lua | 12 ++++ tests/coderabbit/restore_spec.lua | 97 +++++++++++++++++++++++++++++++ 6 files changed, 150 insertions(+) create mode 100644 tests/coderabbit/restore_spec.lua diff --git a/README.md b/README.md index cdf983f..4f50d32 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ Run `:checkhealth coderabbit` to verify everything is wired up. | `:CodeRabbitStop` | Cancel a running review | | `:CodeRabbitClear` | Clear diagnostics | | `:CodeRabbitShow [id]` | View results (float or buffer). Defaults to the latest review | +| `:CodeRabbitRestore [id]` | Reapply diagnostics from a saved review. Defaults to the most recent | | `:CodeRabbitHistory` | Browse past reviews | For your statusline: diff --git a/doc/coderabbit.txt b/doc/coderabbit.txt index ab09994..016ba07 100644 --- a/doc/coderabbit.txt +++ b/doc/coderabbit.txt @@ -107,6 +107,11 @@ COMMANDS *coderabbit-commands* Pass an `id` from `:CodeRabbitHistory` to view a saved review. Press `q` to close. +:CodeRabbitRestore [id] *:CodeRabbitRestore* + Reapply diagnostics from a saved review. Pass an `id` from + `:CodeRabbitHistory` to restore a specific review. Without an `id`, + restores the most recent review. + :CodeRabbitHistory *:CodeRabbitHistory* Browse saved reviews via |vim.ui.select|. @@ -128,6 +133,9 @@ require("coderabbit").clear() *coderabbit.clear()* require("coderabbit").show({id}) *coderabbit.show()* Open the review buffer. `nil` = current, number = saved. +require("coderabbit").restore({id}) *coderabbit.restore()* + Reapply diagnostics from a saved review. `nil` = most recent. + require("coderabbit").history() *coderabbit.history()* Open the review history picker. diff --git a/lua/coderabbit/init.lua b/lua/coderabbit/init.lua index e5e1bd9..ebe768d 100644 --- a/lua/coderabbit/init.lua +++ b/lua/coderabbit/init.lua @@ -20,6 +20,10 @@ function M.clear() require("coderabbit.review").clear() end +function M.restore(id) + require("coderabbit.review").restore(id) +end + function M.show(id) require("coderabbit.show").open(id) end diff --git a/lua/coderabbit/review.lua b/lua/coderabbit/review.lua index ecab15d..0dd5399 100644 --- a/lua/coderabbit/review.lua +++ b/lua/coderabbit/review.lua @@ -229,6 +229,34 @@ function M.stop() end end +function M.restore(id) + local entries = storage.list() + if #entries == 0 then + vim.notify("CodeRabbit: No saved reviews found", vim.log.levels.WARN) + return + end + + if not id then + id = entries[#entries].id + end + + local review = storage.load(id) + if not review then + vim.notify("CodeRabbit: Review #" .. id .. " not found", vim.log.levels.WARN) + return + end + + diagnostics.clear() + for _, finding in ipairs(review.findings) do + diagnostics.set(finding.filepath, { finding.diagnostic }) + end + + vim.notify( + string.format("CodeRabbit: Restored %d findings from review #%d", #review.findings, id), + vim.log.levels.INFO + ) +end + function M.clear() diagnostics.clear() state.findings = {} diff --git a/plugin/coderabbit.lua b/plugin/coderabbit.lua index cffb328..fb3f91d 100644 --- a/plugin/coderabbit.lua +++ b/plugin/coderabbit.lua @@ -48,6 +48,18 @@ end, { desc = "Show CodeRabbit review results in a buffer (optional: review ID)", }) +vim.api.nvim_create_user_command("CodeRabbitRestore", function(args) + ensure_setup() + local id = args.fargs[1] and tonumber(args.fargs[1]) or nil + require("coderabbit").restore(id) +end, { + nargs = "?", + complete = function() + return require("coderabbit.storage").ids() + end, + desc = "Restore diagnostics from a previous CodeRabbit review (default: most recent)", +}) + vim.api.nvim_create_user_command("CodeRabbitHistory", function() ensure_setup() require("coderabbit").history() diff --git a/tests/coderabbit/restore_spec.lua b/tests/coderabbit/restore_spec.lua new file mode 100644 index 0000000..3835dfa --- /dev/null +++ b/tests/coderabbit/restore_spec.lua @@ -0,0 +1,97 @@ +local diagnostics = require("coderabbit.diagnostics") +local review = require("coderabbit.review") +local storage = require("coderabbit.storage") +local h = require("tests.helpers") +local test, eq = h.test, h.eq + +local test_dir = vim.fn.tempname() .. "/coderabbit_restore_test" +storage._set_base_dir(test_dir) + +local function cleanup() + vim.fn.delete(test_dir, "rf") + diagnostics.clear() +end + +local function make_findings(n) + local tmpdir = vim.fn.tempname() + vim.fn.mkdir(tmpdir, "p") + local findings = {} + for i = 1, n do + local filepath = tmpdir .. "/file" .. i .. ".ts" + vim.fn.writefile({ "// mock" }, filepath) + table.insert(findings, h.finding(filepath, i * 10, h.W, "finding " .. i)) + end + return findings +end + +-- ────────────────────────────────────────────────────────── +-- Tests +-- ────────────────────────────────────────────────────────── + +test("restore: warns when no saved reviews exist", function() + cleanup() + -- Should not error, just warn + review.restore(nil) +end) + +test("restore: warns when review ID not found", function() + cleanup() + storage.save(make_findings(1), h.context()) + review.restore(999) +end) + +test("restore: restores diagnostics from a specific review", function() + cleanup() + local findings = make_findings(2) + storage.save(findings, h.context()) + + review.restore(1) + + for _, finding in ipairs(findings) do + local bufnr = vim.fn.bufnr(finding.filepath) + local got = vim.diagnostic.get(bufnr, { namespace = diagnostics.ns }) + eq(#got, 1) + eq(got[1].message, finding.diagnostic.message) + end +end) + +test("restore: defaults to most recent review when no ID given", function() + cleanup() + local old_findings = make_findings(1) + storage.save(old_findings, h.context("old-branch")) + local new_findings = make_findings(2) + storage.save(new_findings, h.context("new-branch")) + + review.restore(nil) + + -- Should have diagnostics from the second review (2 findings) + local total = 0 + for _, d in ipairs(vim.diagnostic.get()) do + if d.source == "coderabbit" then + total = total + 1 + end + end + eq(total, 2) +end) + +test("restore: clears previous diagnostics before applying", function() + cleanup() + local first = make_findings(3) + storage.save(first, h.context()) + local second = make_findings(1) + storage.save(second, h.context()) + + review.restore(1) -- 3 findings + review.restore(2) -- 1 finding — should replace, not accumulate + + local total = 0 + for _, d in ipairs(vim.diagnostic.get()) do + if d.source == "coderabbit" then + total = total + 1 + end + end + eq(total, 1) +end) + +cleanup() +h.summary() From a768339613c81feb71fb0cd1d427ea60a4dbe2eb Mon Sep 17 00:00:00 2001 From: Sam Natale Date: Tue, 14 Apr 2026 12:52:39 +0100 Subject: [PATCH 2/5] fix restore not having diagnostics inline --- lua/coderabbit/review.lua | 16 +++++++++------- tests/coderabbit/restore_spec.lua | 10 ++++++++++ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/lua/coderabbit/review.lua b/lua/coderabbit/review.lua index 0dd5399..d98a3bd 100644 --- a/lua/coderabbit/review.lua +++ b/lua/coderabbit/review.lua @@ -247,14 +247,16 @@ function M.restore(id) end diagnostics.clear() - for _, finding in ipairs(review.findings) do - diagnostics.set(finding.filepath, { finding.diagnostic }) - end + vim.schedule(function() + for _, finding in ipairs(review.findings) do + diagnostics.set(finding.filepath, { finding.diagnostic }) + end - vim.notify( - string.format("CodeRabbit: Restored %d findings from review #%d", #review.findings, id), - vim.log.levels.INFO - ) + vim.notify( + string.format("CodeRabbit: Restored %d findings from review #%d", #review.findings, id), + vim.log.levels.INFO + ) + end) end function M.clear() diff --git a/tests/coderabbit/restore_spec.lua b/tests/coderabbit/restore_spec.lua index 3835dfa..b2b144c 100644 --- a/tests/coderabbit/restore_spec.lua +++ b/tests/coderabbit/restore_spec.lua @@ -7,6 +7,12 @@ local test, eq = h.test, h.eq local test_dir = vim.fn.tempname() .. "/coderabbit_restore_test" storage._set_base_dir(test_dir) +local function flush() + vim.wait(10, function() + return false + end) +end + local function cleanup() vim.fn.delete(test_dir, "rf") diagnostics.clear() @@ -46,6 +52,7 @@ test("restore: restores diagnostics from a specific review", function() storage.save(findings, h.context()) review.restore(1) + flush() for _, finding in ipairs(findings) do local bufnr = vim.fn.bufnr(finding.filepath) @@ -63,6 +70,7 @@ test("restore: defaults to most recent review when no ID given", function() storage.save(new_findings, h.context("new-branch")) review.restore(nil) + flush() -- Should have diagnostics from the second review (2 findings) local total = 0 @@ -82,7 +90,9 @@ test("restore: clears previous diagnostics before applying", function() storage.save(second, h.context()) review.restore(1) -- 3 findings + flush() review.restore(2) -- 1 finding — should replace, not accumulate + flush() local total = 0 for _, d in ipairs(vim.diagnostic.get()) do From 90af10cebe8087f80c70f2fca02ec750d457d1f7 Mon Sep 17 00:00:00 2001 From: Sam Natale Date: Tue, 14 Apr 2026 20:07:46 +0100 Subject: [PATCH 3/5] coderabbitai suggestions --- lua/coderabbit/review.lua | 5 +++-- plugin/coderabbit.lua | 9 ++++++++- tests/coderabbit/restore_spec.lua | 7 +++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/lua/coderabbit/review.lua b/lua/coderabbit/review.lua index d98a3bd..073b0dc 100644 --- a/lua/coderabbit/review.lua +++ b/lua/coderabbit/review.lua @@ -247,13 +247,14 @@ function M.restore(id) end diagnostics.clear() + local findings = type(review.findings) == "table" and review.findings or {} vim.schedule(function() - for _, finding in ipairs(review.findings) do + for _, finding in ipairs(findings) do diagnostics.set(finding.filepath, { finding.diagnostic }) end vim.notify( - string.format("CodeRabbit: Restored %d findings from review #%d", #review.findings, id), + string.format("CodeRabbit: Restored %d findings from review #%d", #findings, id), vim.log.levels.INFO ) end) diff --git a/plugin/coderabbit.lua b/plugin/coderabbit.lua index fb3f91d..6541779 100644 --- a/plugin/coderabbit.lua +++ b/plugin/coderabbit.lua @@ -50,7 +50,14 @@ end, { vim.api.nvim_create_user_command("CodeRabbitRestore", function(args) ensure_setup() - local id = args.fargs[1] and tonumber(args.fargs[1]) or nil + local id = nil + if args.fargs[1] then + id = tonumber(args.fargs[1]) + if not id then + vim.notify("CodeRabbitRestore: invalid review ID: " .. args.fargs[1], vim.log.levels.ERROR) + return + end + end require("coderabbit").restore(id) end, { nargs = "?", diff --git a/tests/coderabbit/restore_spec.lua b/tests/coderabbit/restore_spec.lua index b2b144c..4081562 100644 --- a/tests/coderabbit/restore_spec.lua +++ b/tests/coderabbit/restore_spec.lua @@ -14,13 +14,20 @@ local function flush() end local function cleanup() + for _, dir in ipairs(finding_tmpdirs) do + vim.fn.delete(dir, "rf") + end + finding_tmpdirs = {} vim.fn.delete(test_dir, "rf") diagnostics.clear() end +local finding_tmpdirs = {} + local function make_findings(n) local tmpdir = vim.fn.tempname() vim.fn.mkdir(tmpdir, "p") + table.insert(finding_tmpdirs, tmpdir) local findings = {} for i = 1, n do local filepath = tmpdir .. "/file" .. i .. ".ts" From eb071dd8f21e370cd144017c1665f36cf4a71336 Mon Sep 17 00:00:00 2001 From: Sam Natale Date: Tue, 14 Apr 2026 20:08:27 +0100 Subject: [PATCH 4/5] stylua --- lua/coderabbit/review.lua | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lua/coderabbit/review.lua b/lua/coderabbit/review.lua index 073b0dc..073d6bf 100644 --- a/lua/coderabbit/review.lua +++ b/lua/coderabbit/review.lua @@ -253,10 +253,7 @@ function M.restore(id) diagnostics.set(finding.filepath, { finding.diagnostic }) end - vim.notify( - string.format("CodeRabbit: Restored %d findings from review #%d", #findings, id), - vim.log.levels.INFO - ) + vim.notify(string.format("CodeRabbit: Restored %d findings from review #%d", #findings, id), vim.log.levels.INFO) end) end From b65ce93a9a935cda547342e53d7f0ca13f28c77e Mon Sep 17 00:00:00 2001 From: Sam Natale Date: Tue, 14 Apr 2026 20:10:02 +0100 Subject: [PATCH 5/5] luacheck --- tests/coderabbit/restore_spec.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/coderabbit/restore_spec.lua b/tests/coderabbit/restore_spec.lua index 4081562..d748c1c 100644 --- a/tests/coderabbit/restore_spec.lua +++ b/tests/coderabbit/restore_spec.lua @@ -13,6 +13,8 @@ local function flush() end) end +local finding_tmpdirs = {} + local function cleanup() for _, dir in ipairs(finding_tmpdirs) do vim.fn.delete(dir, "rf") @@ -22,8 +24,6 @@ local function cleanup() diagnostics.clear() end -local finding_tmpdirs = {} - local function make_findings(n) local tmpdir = vim.fn.tempname() vim.fn.mkdir(tmpdir, "p")