Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
8 changes: 8 additions & 0 deletions doc/coderabbit.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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|.

Expand All @@ -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.

Expand Down
4 changes: 4 additions & 0 deletions lua/coderabbit/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
28 changes: 28 additions & 0 deletions lua/coderabbit/review.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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()
local findings = type(review.findings) == "table" and review.findings or {}
vim.schedule(function()
for _, finding in ipairs(findings) do
diagnostics.set(finding.filepath, { finding.diagnostic })
end
Comment thread
coderabbitai[bot] marked this conversation as resolved.

vim.notify(string.format("CodeRabbit: Restored %d findings from review #%d", #findings, id), vim.log.levels.INFO)
end)
end

function M.clear()
diagnostics.clear()
state.findings = {}
Expand Down
19 changes: 19 additions & 0 deletions plugin/coderabbit.lua
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,25 @@ 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 = 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 = "?",
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()
Expand Down
114 changes: 114 additions & 0 deletions tests/coderabbit/restore_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
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 flush()
vim.wait(10, function()
return false
end)
end

local finding_tmpdirs = {}

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 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"
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)
flush()

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)
flush()

-- 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
flush()
review.restore(2) -- 1 finding — should replace, not accumulate
flush()

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()
Loading