-
-
Notifications
You must be signed in to change notification settings - Fork 1
feat: populate quickfix list #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
61f89d8
quickfix populate feature
smnatale 7c75712
coderabbit suggestions
smnatale a027e36
fix: apply CodeRabbit auto-fixes
coderabbitai[bot] df180cb
fix: apply CodeRabbit auto-fixes
coderabbitai[bot] 54230c8
stylua
smnatale 231ea67
Merge branch 'main' into feat/quickfix-populate
smnatale File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,6 +30,9 @@ M.defaults = { | |
| border = "rounded", | ||
| }, | ||
| }, | ||
| quickfix = { | ||
| auto = false, | ||
| }, | ||
| on_review_complete = nil, | ||
| } | ||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| local M = {} | ||
|
|
||
| local severity_types = { | ||
| [vim.diagnostic.severity.ERROR] = "E", | ||
| [vim.diagnostic.severity.WARN] = "W", | ||
| } | ||
|
|
||
| --- Map vim.diagnostic.severity to quickfix type character. | ||
| --- @param severity number | ||
| --- @return string "E", "W", or "I" | ||
| function M.severity_to_type(severity) | ||
| return severity_types[severity] or "I" | ||
| end | ||
|
|
||
| --- Convert findings to quickfix items (pure function, no side effects). | ||
| --- @param findings table[] Array of { diagnostic, filepath } | ||
| --- @return table[] Array of { filename, lnum, col, text, type } for setqflist() | ||
| function M.findings_to_qf_items(findings) | ||
| local items = {} | ||
| for _, f in ipairs(findings) do | ||
| local d = f.diagnostic | ||
| local raw = d.user_data and d.user_data.severity_raw | ||
| local prefix = raw and ("[" .. raw .. "] ") or "" | ||
| local first_line = d.message:match("^([^\n]*)") or d.message | ||
| table.insert(items, { | ||
| filename = f.filepath, | ||
| lnum = d.lnum + 1, | ||
| col = d.col + 1, | ||
| text = prefix .. first_line, | ||
| type = M.severity_to_type(d.severity), | ||
| }) | ||
| end | ||
| return items | ||
| end | ||
|
|
||
| --- Populate the quickfix list from findings and open the window. | ||
| --- @param findings table[] Array of { diagnostic, filepath } | ||
| --- @param opts table|nil { title = string } | ||
| function M.set(findings, opts) | ||
| opts = opts or {} | ||
| local items = M.findings_to_qf_items(findings) | ||
| vim.fn.setqflist({}, "r", { | ||
| title = opts.title or "CodeRabbit Review", | ||
| items = items, | ||
| }) | ||
| vim.cmd("copen") | ||
| end | ||
|
|
||
| --- Populate quickfix from current review or a saved review by ID. | ||
| --- @param id number|nil Review ID (nil = current in-memory findings) | ||
| function M.populate(id) | ||
| local findings, title | ||
|
|
||
| local review = require("coderabbit.review") | ||
|
|
||
| if id then | ||
| local entry = review.get_review(id) | ||
| if not entry then | ||
| vim.notify("CodeRabbit: Review #" .. id .. " not found", vim.log.levels.WARN) | ||
| return | ||
| end | ||
| findings = type(entry.findings) == "table" and entry.findings or {} | ||
| title = "CodeRabbit Review #" .. id | ||
| else | ||
| findings = review.get_results() | ||
| if #findings == 0 and not review.get_context() then | ||
| vim.notify("CodeRabbit: No review results. Run :CodeRabbitReview first", vim.log.levels.WARN) | ||
| return | ||
| end | ||
| title = "CodeRabbit Review" | ||
| end | ||
|
|
||
| M.set(findings, { title = title }) | ||
| end | ||
|
|
||
| return M |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,186 @@ | ||
| local quickfix = require("coderabbit.quickfix") | ||
| local h = require("tests.helpers") | ||
| local test, eq = h.test, h.eq | ||
| local E, W, I = h.E, h.W, h.I | ||
|
|
||
| -- ────────────────────────────────────────────────────────── | ||
| -- Tests: severity_to_type | ||
| -- ────────────────────────────────────────────────────────── | ||
|
|
||
| test("severity_to_type: ERROR -> E", function() | ||
| eq(quickfix.severity_to_type(E), "E") | ||
| end) | ||
|
|
||
| test("severity_to_type: WARN -> W", function() | ||
| eq(quickfix.severity_to_type(W), "W") | ||
| end) | ||
|
|
||
| test("severity_to_type: INFO -> I", function() | ||
| eq(quickfix.severity_to_type(I), "I") | ||
| end) | ||
|
|
||
| test("severity_to_type: HINT -> I (fallback)", function() | ||
| eq(quickfix.severity_to_type(vim.diagnostic.severity.HINT), "I") | ||
| end) | ||
|
|
||
| -- ────────────────────────────────────────────────────────── | ||
| -- Tests: findings_to_qf_items | ||
| -- ────────────────────────────────────────────────────────── | ||
|
|
||
| test("findings_to_qf_items: empty findings returns empty", function() | ||
| local items = quickfix.findings_to_qf_items({}) | ||
| eq(#items, 0) | ||
| end) | ||
|
|
||
| test("findings_to_qf_items: single finding produces correct entry", function() | ||
| local findings = { h.finding("/tmp/repo/foo.lua", 41, E, "null check", {}) } | ||
| local items = quickfix.findings_to_qf_items(findings) | ||
| eq(#items, 1) | ||
| eq(items[1].filename, "/tmp/repo/foo.lua") | ||
| eq(items[1].lnum, 42) -- 0-indexed -> 1-indexed | ||
| eq(items[1].col, 1) -- col 0 -> 1 | ||
| eq(items[1].type, "E") | ||
| end) | ||
|
|
||
| test("findings_to_qf_items: lnum 0 becomes 1", function() | ||
| local findings = { h.finding("/tmp/repo/a.lua", 0, I, "file-level issue") } | ||
| local items = quickfix.findings_to_qf_items(findings) | ||
| eq(items[1].lnum, 1) | ||
| end) | ||
|
|
||
| test("findings_to_qf_items: severity maps correctly", function() | ||
| local findings = { | ||
| h.finding("/tmp/repo/a.lua", 0, E, "error"), | ||
| h.finding("/tmp/repo/b.lua", 0, W, "warn"), | ||
| h.finding("/tmp/repo/c.lua", 0, I, "info"), | ||
| } | ||
| local items = quickfix.findings_to_qf_items(findings) | ||
| eq(items[1].type, "E") | ||
| eq(items[2].type, "W") | ||
| eq(items[3].type, "I") | ||
| end) | ||
|
|
||
| test("findings_to_qf_items: text includes severity_raw prefix", function() | ||
| local findings = { h.finding("/tmp/repo/a.lua", 10, W, "missing import") } | ||
| local items = quickfix.findings_to_qf_items(findings) | ||
| -- helpers.finding sets severity_raw = "minor" by default | ||
| eq(items[1].text, "[minor] missing import") | ||
| end) | ||
|
|
||
| test("findings_to_qf_items: multi-line message uses first line only", function() | ||
| local findings = { h.finding("/tmp/repo/a.lua", 5, E, "first line\nsecond line\nthird") } | ||
| local items = quickfix.findings_to_qf_items(findings) | ||
| eq(items[1].text, "[minor] first line") | ||
| end) | ||
|
|
||
| test("findings_to_qf_items: missing severity_raw omits prefix", function() | ||
| local findings = { | ||
| { | ||
| filepath = "/tmp/repo/a.lua", | ||
| diagnostic = { | ||
| lnum = 0, | ||
| col = 0, | ||
| severity = E, | ||
| message = "bare finding", | ||
| source = "coderabbit", | ||
| }, | ||
| }, | ||
| } | ||
| local items = quickfix.findings_to_qf_items(findings) | ||
| eq(items[1].text, "bare finding") | ||
| end) | ||
|
|
||
| test("findings_to_qf_items: multiple findings produce correct count", function() | ||
| local findings = { | ||
| h.finding("/tmp/repo/a.lua", 1, E, "one"), | ||
| h.finding("/tmp/repo/b.lua", 2, W, "two"), | ||
| h.finding("/tmp/repo/c.lua", 3, I, "three"), | ||
| } | ||
| local items = quickfix.findings_to_qf_items(findings) | ||
| eq(#items, 3) | ||
| end) | ||
|
|
||
| -- ────────────────────────────────────────────────────────── | ||
| -- Tests: populate | ||
| -- ────────────────────────────────────────────────────────── | ||
|
|
||
| local storage = require("coderabbit.storage") | ||
| local populate_test_dir = vim.fn.tempname() .. "/coderabbit_populate_test" | ||
| storage._set_base_dir(populate_test_dir) | ||
|
|
||
| -- Save a review so storage.load(1) returns it. | ||
| local saved_findings = { | ||
| h.finding("/tmp/repo/a.lua", 10, E, "error here"), | ||
| h.finding("/tmp/repo/b.lua", 20, W, "warning here"), | ||
| } | ||
| storage.save(saved_findings, h.context()) | ||
|
|
||
| test("populate: valid id populates quickfix from stored review", function() | ||
| quickfix.populate(1) | ||
| vim.cmd("cclose") | ||
| local qf = vim.fn.getqflist({ title = 1, items = 1 }) | ||
| eq(qf.title, "CodeRabbit Review #1") | ||
| eq(#qf.items, 2) | ||
| end) | ||
|
|
||
| test("populate: invalid id does not error and leaves quickfix unchanged", function() | ||
| -- Set a known state first | ||
| quickfix.set({ h.finding("/tmp/repo/x.lua", 0, I, "baseline") }, { title = "Baseline" }) | ||
| vim.cmd("cclose") | ||
| -- Call with non-existent id | ||
| quickfix.populate(999) | ||
| local qf = vim.fn.getqflist({ title = 1, items = 1 }) | ||
| -- Should remain unchanged (populate returns early with a warning) | ||
| eq(qf.title, "Baseline") | ||
| eq(#qf.items, 1) | ||
| end) | ||
|
|
||
| test("populate: nil id with no review context warns and leaves quickfix unchanged", function() | ||
| -- Set a known state first | ||
| quickfix.set({ h.finding("/tmp/repo/x.lua", 0, I, "baseline") }, { title = "Baseline" }) | ||
| vim.cmd("cclose") | ||
| -- Clear review state so get_results() returns {} and get_context() returns nil | ||
| require("coderabbit.review").clear() | ||
| quickfix.populate(nil) | ||
| local qf = vim.fn.getqflist({ title = 1, items = 1 }) | ||
| -- Should remain unchanged (populate returns early with a warning) | ||
| eq(qf.title, "Baseline") | ||
| eq(#qf.items, 1) | ||
| end) | ||
|
|
||
| -- Clean up temp dir | ||
| vim.fn.delete(populate_test_dir, "rf") | ||
|
|
||
| -- ────────────────────────────────────────────────────────── | ||
| -- Tests: set | ||
| -- ────────────────────────────────────────────────────────── | ||
|
|
||
| test("set: populates quickfix list with items", function() | ||
| local findings = { | ||
| h.finding("/tmp/repo/a.lua", 10, E, "error here"), | ||
| h.finding("/tmp/repo/b.lua", 20, W, "warning here"), | ||
| } | ||
| quickfix.set(findings, { title = "Test Review" }) | ||
| vim.cmd("cclose") | ||
| local qf = vim.fn.getqflist({ title = 1, items = 1 }) | ||
| eq(qf.title, "Test Review") | ||
| eq(#qf.items, 2) | ||
| end) | ||
|
|
||
| test("set: empty findings clears quickfix list", function() | ||
| quickfix.set({ h.finding("/tmp/repo/a.lua", 0, E, "x") }) | ||
| quickfix.set({}) | ||
| vim.cmd("cclose") | ||
| local qf = vim.fn.getqflist({ items = 1 }) | ||
| eq(#qf.items, 0) | ||
| end) | ||
|
|
||
| test("set: replaces existing quickfix content", function() | ||
| quickfix.set({ h.finding("/tmp/repo/a.lua", 0, E, "first") }) | ||
| quickfix.set({ h.finding("/tmp/repo/b.lua", 1, W, "second") }) | ||
| vim.cmd("cclose") | ||
| local qf = vim.fn.getqflist({ items = 1 }) | ||
| eq(#qf.items, 1) | ||
| end) | ||
|
|
||
| h.summary() | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.