diff --git a/lua/coderabbit/config.lua b/lua/coderabbit/config.lua index bf00234..097d1ad 100644 --- a/lua/coderabbit/config.lua +++ b/lua/coderabbit/config.lua @@ -3,7 +3,7 @@ local M = {} M.defaults = { cli = { binary = "cr", - timeout = 120000, + timeout = 0, extra_args = {}, }, review = { diff --git a/lua/coderabbit/init.lua b/lua/coderabbit/init.lua index 61249b5..c422b25 100644 --- a/lua/coderabbit/init.lua +++ b/lua/coderabbit/init.lua @@ -20,4 +20,8 @@ function M.clear() require("coderabbit.review").clear() end +function M.status() + return require("coderabbit.review").status() +end + return M diff --git a/lua/coderabbit/review.lua b/lua/coderabbit/review.lua index 9879d5a..8fb81a4 100644 --- a/lua/coderabbit/review.lua +++ b/lua/coderabbit/review.lua @@ -5,12 +5,49 @@ local cli = require("coderabbit.cli") local parser = require("coderabbit.parser") local diagnostics = require("coderabbit.diagnostics") +local spinner_frames = { "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" } +local FRAME_MS = 80 + local state = { job_id = nil, findings = {}, cwd = nil, + start_time = nil, + fidget_handle = nil, + last_notify_time = nil, } +local function spinner() + local idx = math.floor(vim.uv.hrtime() / (1e6 * FRAME_MS)) % #spinner_frames + 1 + return spinner_frames[idx] +end + +local function fidget_start() + local ok, progress = pcall(require, "fidget.progress") + if not ok then + return nil + end + return progress.handle.create({ + title = "Reviewing", + message = "analyzing...", + lsp_client = { name = "coderabbit" }, + }) +end + +local function fidget_update(msg) + if state.fidget_handle then + state.fidget_handle.message = msg + end +end + +local function fidget_finish(msg) + if state.fidget_handle then + state.fidget_handle.message = msg + state.fidget_handle:finish() + state.fidget_handle = nil + end +end + function M.is_running() return state.job_id ~= nil end @@ -19,6 +56,16 @@ function M.get_results() return state.findings end +--- Return a short status string for statusline integration. +--- Returns nil when no review is running. +function M.status() + if not state.job_id then + return nil + end + local elapsed = os.time() - state.start_time + return string.format("%s CodeRabbit (%ds)", spinner(), elapsed) +end + function M.run(opts) opts = opts or {} @@ -43,6 +90,9 @@ function M.run(opts) vim.notify("CodeRabbit: Reviewing...") + state.start_time = os.time() + state.fidget_handle = fidget_start() + state.job_id = cli.review(opts, { on_line = function(line) local event = parser.parse_line(line) @@ -51,7 +101,16 @@ function M.run(opts) end if event.type == "status" then - vim.notify("CodeRabbit: " .. (event.status or event.phase or "working..."), vim.log.levels.INFO) + local msg = event.status or event.phase or "working..." + if state.fidget_handle then + fidget_update(msg) + else + local now = os.time() + if not state.last_notify_time or (now - state.last_notify_time) >= 20 then + state.last_notify_time = now + vim.notify("CodeRabbit: " .. msg, vim.log.levels.INFO) + end + end elseif event.type == "finding" then finding_count = finding_count + 1 local diag, filepath = parser.finding_to_diagnostic(event, state.cwd, cfg.diagnostics.severity_map) @@ -73,8 +132,11 @@ function M.run(opts) on_exit = function(code, stderr) state.job_id = nil + state.start_time = nil + state.last_notify_time = nil if code == -1 then + fidget_finish("timed out") vim.notify("CodeRabbit: Review timed out", vim.log.levels.ERROR) return end @@ -85,6 +147,7 @@ function M.run(opts) if msg:match("[Aa]uth") then msg = msg .. "\nRun: cr auth login" end + fidget_finish("failed") vim.notify("CodeRabbit: " .. msg, vim.log.levels.ERROR) return end @@ -92,7 +155,10 @@ function M.run(opts) if not got_error then local summary = string.format("CodeRabbit: Review complete. %d finding%s.", finding_count, finding_count == 1 and "" or "s") + fidget_finish(string.format("done — %d finding%s", finding_count, finding_count == 1 and "" or "s")) vim.notify(summary, vim.log.levels.INFO) + else + fidget_finish("done (with errors)") end if cfg.on_review_complete then @@ -104,6 +170,7 @@ end function M.stop() if state.job_id then + fidget_finish("cancelled") cli.cancel(state.job_id) state.job_id = nil vim.notify("CodeRabbit: Review cancelled", vim.log.levels.INFO)