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
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Run `:checkhealth coderabbit` to verify everything is wired up.
| `:CodeRabbitReview [type]` | Run a review. Defaults to `all`, or pass `committed`/`uncommitted` |
| `:CodeRabbitStop` | Cancel a running review |
| `:CodeRabbitClear` | Clear diagnostics |
| `:CodeRabbitShow [id]` | View results in a split. Defaults to the latest review |
| `:CodeRabbitShow [id]` | View results (float or buffer). Defaults to the latest review |
| `:CodeRabbitHistory` | Browse past reviews |

For your statusline:
Expand Down Expand Up @@ -73,6 +73,14 @@ require("coderabbit").setup({
signs = true,
underline = true,
},
show = {
layout = "float", -- "float" or "buffer"
float = {
width = 0.6,
height = 0.7,
border = "rounded",
},
},
on_review_complete = nil,
})
```
20 changes: 18 additions & 2 deletions doc/coderabbit.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ All options are optional. Defaults: >lua
signs = true,
underline = true,
},
show = {
layout = "float",
float = {
width = 0.6,
height = 0.7,
border = "rounded",
},
},
on_review_complete = nil,
})
<
Expand All @@ -72,6 +80,13 @@ diagnostics.virtual_text Show inline virtual text.
diagnostics.signs Show sign column indicators.
diagnostics.underline Underline diagnostic ranges.

show.layout `"float"` or `"buffer"`. Default: `"float"`.
`"float"` opens a centered floating window.
`"buffer"` replaces the current buffer (oil.nvim style).
show.float.width Fraction of editor width (0-1). Default: `0.6`.
show.float.height Fraction of editor height (0-1). Default: `0.7`.
show.float.border Border style for the floating window. Default: `"rounded"`.

on_review_complete Callback receiving the findings table when a review
finishes.

Expand All @@ -88,8 +103,9 @@ COMMANDS *coderabbit-commands*
Clear all CodeRabbit diagnostics.

:CodeRabbitShow [id] *:CodeRabbitShow*
Open review results in a vertical split. Pass an `id` from
`:CodeRabbitHistory` to view a saved review. Press `q` to close.
Open review results. Display mode is controlled by `show.layout`.
Pass an `id` from `:CodeRabbitHistory` to view a saved review.
Press `q` to close.

:CodeRabbitHistory *:CodeRabbitHistory*
Browse saved reviews via |vim.ui.select|.
Expand Down
8 changes: 8 additions & 0 deletions lua/coderabbit/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ M.defaults = {
signs = true,
underline = true,
},
show = {
layout = "float",
float = {
width = 0.6,
height = 0.7,
border = "rounded",
},
},
on_review_complete = nil,
}

Expand Down
59 changes: 46 additions & 13 deletions lua/coderabbit/show.lua
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,45 @@ local function line_label(diag)
return nil
end

local function set_win_opts(winid)
vim.api.nvim_set_option_value("number", false, { win = winid })
vim.api.nvim_set_option_value("relativenumber", false, { win = winid })
vim.api.nvim_set_option_value("signcolumn", "no", { win = winid })
vim.api.nvim_set_option_value("wrap", true, { win = winid })
vim.api.nvim_set_option_value("linebreak", true, { win = winid })
vim.api.nvim_set_option_value("spell", false, { win = winid })
vim.api.nvim_set_option_value("conceallevel", 2, { win = winid })
end

local function open_float(buf, float_cfg)
local width = math.floor(vim.o.columns * float_cfg.width)
local height = math.floor(vim.o.lines * float_cfg.height)
local row = math.floor((vim.o.lines - height) / 2)
local col = math.floor((vim.o.columns - width) / 2)
vim.api.nvim_open_win(buf, true, {
relative = "editor",
width = width,
height = height,
row = row,
col = col,
style = "minimal",
border = float_cfg.border,
title = " CodeRabbit Review ",
title_pos = "center",
})
end

local function open_window(buf)
local cfg = require("coderabbit.config").get().show
local layout = cfg.layout

if layout == "buffer" then
vim.api.nvim_win_set_buf(0, buf)
else
open_float(buf, cfg.float)
end
end

--- Render findings and context into an array of markdown lines.
--- @param findings table[] Array of { diagnostic, filepath }
--- @param context table|nil Review context metadata
Expand Down Expand Up @@ -172,35 +211,29 @@ function M.open(id)
if winid ~= -1 then
vim.api.nvim_set_current_win(winid)
else
vim.cmd("vsplit")
vim.api.nvim_win_set_buf(0, buf_id)
open_window(buf_id)
Comment thread
smnatale marked this conversation as resolved.
set_win_opts(0)
end
vim.api.nvim_set_option_value("modifiable", true, { buf = buf_id })
vim.api.nvim_buf_set_lines(buf_id, 0, -1, false, content)
vim.api.nvim_set_option_value("modifiable", false, { buf = buf_id })
return
end

local layout = require("coderabbit.config").get().show.layout

buf_id = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(buf_id, "coderabbit://review")
vim.api.nvim_set_option_value("buftype", "nofile", { buf = buf_id })
vim.api.nvim_set_option_value("bufhidden", "wipe", { buf = buf_id })
vim.api.nvim_set_option_value("bufhidden", layout == "buffer" and "hide" or "wipe", { buf = buf_id })
vim.api.nvim_set_option_value("swapfile", false, { buf = buf_id })
vim.api.nvim_set_option_value("filetype", "markdown", { buf = buf_id })

vim.api.nvim_buf_set_lines(buf_id, 0, -1, false, content)
vim.api.nvim_set_option_value("modifiable", false, { buf = buf_id })

vim.cmd("vsplit")
vim.api.nvim_win_set_buf(0, buf_id)

vim.api.nvim_set_option_value("number", false, { win = 0 })
vim.api.nvim_set_option_value("relativenumber", false, { win = 0 })
vim.api.nvim_set_option_value("signcolumn", "no", { win = 0 })
vim.api.nvim_set_option_value("wrap", true, { win = 0 })
vim.api.nvim_set_option_value("linebreak", true, { win = 0 })
vim.api.nvim_set_option_value("spell", false, { win = 0 })
vim.api.nvim_set_option_value("conceallevel", 2, { win = 0 })
open_window(buf_id)
set_win_opts(0)

vim.keymap.set("n", "q", function()
M.close()
Expand Down
100 changes: 100 additions & 0 deletions tests/coderabbit/show_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -259,4 +259,104 @@ test("is_open: false when no buffer, true when visible", function()
end)
end)

-- ──────────────────────────────────────────────────────────
-- Tests: layout modes
-- ──────────────────────────────────────────────────────────

local config = require("coderabbit.config")

local function with_layout(layout, fn)
local prev = config.get().show.layout
config.get().show.layout = layout
local ok, err = pcall(fn)
config.get().show.layout = prev
if not ok then
error(err, 2)
end
end

test("layout float: opens floating window", function()
with_layout("float", function()
with_review(one_finding, one_ctx, false, function()
show.open()
local bufnr = show._get_buf_id()
assert(bufnr and vim.api.nvim_buf_is_valid(bufnr), "expected valid buffer")
local winid = vim.fn.bufwinid(bufnr)
assert(winid ~= -1, "expected buffer in a window")
local win_cfg = vim.api.nvim_win_get_config(winid)
eq(win_cfg.relative, "editor")
assert(win_cfg.width > 0, "expected positive width")
assert(win_cfg.height > 0, "expected positive height")
end)
end)
end)

test("layout float: close removes floating window", function()
with_layout("float", function()
with_review(one_finding, one_ctx, false, function()
show.open()
eq(show.is_open(), true)
show.close()
eq(show.is_open(), false)
eq(show._get_buf_id(), nil)
end)
end)
end)

test("layout buffer: opens in current window (no split or float)", function()
with_layout("buffer", function()
with_review(one_finding, one_ctx, false, function()
local win_before = vim.api.nvim_get_current_win()
local win_count_before = #vim.api.nvim_list_wins()
show.open()
local bufnr = show._get_buf_id()
assert(bufnr and vim.api.nvim_buf_is_valid(bufnr), "expected valid buffer")
eq(vim.api.nvim_get_current_win(), win_before)
eq(#vim.api.nvim_list_wins(), win_count_before)
local win_cfg = vim.api.nvim_win_get_config(vim.fn.bufwinid(bufnr))
eq(win_cfg.relative, "")
end)
end)
end)

test("layout buffer: bufhidden is hide", function()
with_layout("buffer", function()
with_review(one_finding, one_ctx, false, function()
show.open()
local bufnr = show._get_buf_id()
eq(vim.api.nvim_get_option_value("bufhidden", { buf = bufnr }), "hide")
end)
end)
end)

test("layout float: bufhidden is wipe", function()
with_layout("float", function()
with_review(one_finding, one_ctx, false, function()
show.open()
local bufnr = show._get_buf_id()
eq(vim.api.nvim_get_option_value("bufhidden", { buf = bufnr }), "wipe")
end)
end)
end)

test("layout float: q keymap closes float", function()
with_layout("float", function()
with_review(one_finding, { cwd = CWD }, false, function()
show.open()
local keymaps = vim.api.nvim_buf_get_keymap(show._get_buf_id(), "n")
local found = false
for _, km in ipairs(keymaps) do
if km.lhs == "q" then
found = true
end
end
assert(found, "expected q keymap")
end)
end)
end)

test("default layout is float", function()
eq(config.defaults.show.layout, "float")
end)

h.summary()
Loading