feat(gh): when on a review comment, the gh_comment action will now do a reply instead of a top-level comment. Fixes #2410

This commit is contained in:
Folke Lemaitre 2025-11-02 18:09:08 +01:00
parent 2099572fe8
commit a4f2b9da2d
No known key found for this signature in database
GPG key ID: 9B52594D560070AB
5 changed files with 77 additions and 10 deletions

View file

@ -6,6 +6,7 @@ local M = {}
---@class snacks.gh.action.ctx
---@field items snacks.picker.gh.Item[]
---@field picker? snacks.Picker
---@field main? number
---@field action? snacks.picker.Action
---@class snacks.gh.cli.Action.ctx
@ -79,6 +80,7 @@ M.actions.gh_actions = {
if ctx.action and ctx.action.cmd then
return Snacks.picker.actions.jump(ctx.picker, item, ctx.action)
end
ctx.main = ctx.main or ctx.picker and ctx.picker.main or nil
local actions = M.get_actions(item)
actions.gh_actions = nil -- remove this action
actions.gh_perform_action = nil -- remove this action
@ -103,6 +105,7 @@ M.actions.gh_actions = {
if ctx.picker then
ctx.picker:focus()
end
ctx.main = ctx.main or picker and picker.main or nil
it.action.action(item, ctx)
picker:close()
end,
@ -233,6 +236,35 @@ M.actions.gh_yank = {
end,
}
M.actions.gh_comment = {
desc = "Comment on {type} #{number}",
icon = "",
action = function(item, ctx)
local win = ctx.main or vim.api.nvim_get_current_win()
local buf = vim.api.nvim_win_get_buf(win)
local action = vim.deepcopy(M.cli_actions.gh_comment)
if item.uri == vim.api.nvim_buf_get_name(buf) then
local lino = vim.api.nvim_win_get_cursor(win)[1]
---@type {line:number, id:number}[]
local comments = vim.b[buf].snacks_gh_comments or {}
for _, c in ipairs(comments) do
if c.line == lino then
action.title = "Reply to comment on {type} #{number}"
action.api = {
endpoint = "/repos/{repo}/pulls/{number}/comments",
input = {
in_reply_to = c.id,
},
}
break
end
end
end
M.run(item, action, ctx)
end,
}
---@type table<string, snacks.gh.cli.Action>
M.cli_actions = {
gh_comment = {
@ -570,7 +602,6 @@ function M._run(ctx, force)
Api.request(
cb,
Snacks.config.merge(ctx.opts.api or {}, {
input = ctx.input,
args = ctx.args,
on_error = function()
spinner:stop()
@ -655,10 +686,20 @@ function M.submit(ctx)
if body:find("%S") then
if edit == "body-file" then
vim.list_extend(ctx.args, { "--body-file", "-" })
ctx.input = body
if ctx.opts.api then
ctx.opts.api.input = ctx.opts.api.input or {}
ctx.opts.api.input.body = body
else
ctx.input = body
vim.list_extend(ctx.args, { "--body-file", "-" })
end
else
vim.list_extend(ctx.args, { "--" .. edit, body })
if ctx.opts.api then
ctx.opts.api.fields = ctx.opts.api.fields or {}
ctx.opts.api.fields[edit] = body
else
vim.list_extend(ctx.args, { "--" .. edit, body })
end
end
end

View file

@ -228,7 +228,7 @@ function M.request(cb, opts)
cb(proc, data and data:find("%S") and proc:json() or nil)
end, {
args = args,
input = opts.input,
input = opts.input and vim.json.encode(opts.input) or nil,
on_error = opts.on_error,
})
end
@ -417,6 +417,7 @@ function M.comments(item, cb)
comments(first: 50) {
nodes {
id
databaseId
body
path
diffHunk
@ -427,7 +428,7 @@ function M.comments(item, cb)
createdAt
subjectType
author { login }
replyTo { id }
replyTo { id databaseId }
reactionGroups {
content
users { totalCount }
@ -439,6 +440,7 @@ function M.comments(item, cb)
comments(first: 100) {
nodes {
id
databaseId
body
author { login }
authorAssociation

View file

@ -11,6 +11,7 @@ local extend = Snacks.picker.highlight.extend
---@field item snacks.picker.gh.Item
---@field opts snacks.gh.Config
---@field comment_skip table<string, boolean>
---@field is_review? boolean
---@param field string
local function time_prop(field)
@ -296,17 +297,24 @@ function M.render(buf, item, opts)
if #threads > 0 then
lines[#lines + 1] = { { "" } } -- empty line
lines[#lines + 1] = { { "---", "@punctuation.special.markdown" } }
lines[#lines + 1] = {} -- empty line
for _, thread in ipairs(threads) do
lines[#lines + 1] = {} -- empty line
local c = #lines
if thread.submitted then
---@cast thread snacks.gh.Review
ctx.is_review = true
vim.list_extend(lines, M.review(thread, 1, ctx))
else
---@cast thread snacks.gh.Comment
ctx.is_review = false
vim.list_extend(lines, M.comment(thread, 1, ctx))
end
if #lines > c then -- only add separator if there were comments added
lines[#lines + 1] = {} -- empty line
end
end
end
@ -316,7 +324,13 @@ function M.render(buf, item, opts)
vim.api.nvim_buf_clear_namespace(buf, ns, 0, -1)
local changed = #lines ~= #buf_lines
local comments = {} ---@type {line:number, id:number}[]
for l, line in ipairs(lines) do
for _, segment in ipairs(line) do
if segment.meta and segment.meta.reply then
comments[#comments + 1] = { line = l, id = segment.meta.reply }
end
end
local line_text, extmarks = Snacks.picker.highlight.to_text(line)
if line_text ~= buf_lines[l] then
vim.api.nvim_buf_set_lines(buf, l - 1, l, false, { line_text })
@ -339,6 +353,7 @@ function M.render(buf, item, opts)
end
end
end
vim.b[buf].snacks_gh_comments = comments
if #lines < #buf_lines then
vim.api.nvim_buf_set_lines(buf, #lines, -1, false, {})
end
@ -490,6 +505,14 @@ function M.comment(comment, level, ctx)
vim.list_extend(ret, M.comment(reply, level, ctx))
ctx.comment_skip[reply.id] = true
end
if ctx.is_review then
for _, line in ipairs(ret) do
local reply_id = comment.replyTo and comment.replyTo.databaseId or comment.databaseId
if reply_id then
line[#line + 1] = { "", meta = { reply = reply_id } }
end
end
end
return ret
end

View file

@ -30,7 +30,7 @@
---@field params? table<string, string|number|boolean> typed fields (--field)
---@field header? table<string, string|number|boolean>
---@field jq? string
---@field input? string
---@field input? any
---@field method? "GET" | "POST" | "PATCH" | "PUT" | "DELETE"
---@field paginate? boolean
---@field silent? boolean
@ -138,6 +138,7 @@
---@class snacks.gh.Comment
---@field id string
---@field databaseId number
---@field url string
---@field author { login: string }
---@field authorAssociation? string
@ -149,7 +150,7 @@
---@field createdAt string
---@field reactionGroups? snacks.gh.Reaction[]
---@field created? number
---@field replyTo? {id: string}
---@field replyTo? {id: string, databaseId: number}
---@field path? string
---@field diffHunk? string
---@field line? number

View file

@ -2,7 +2,7 @@ local M = {}
---@alias snacks.picker.format.resolve fun(max_width:number):snacks.picker.Highlight[]
---@alias snacks.picker.Extmark vim.api.keyset.set_extmark|{col:number, row?:number, field?:string}
---@alias snacks.picker.Text {[1]:string, [2]:(string|string[])?, virtual?:boolean, field?:string, resolve?:snacks.picker.format.resolve}
---@alias snacks.picker.Text {[1]:string, [2]:(string|string[])?, virtual?:boolean, field?:string, resolve?:snacks.picker.format.resolve, meta?:table<string, any>}
---@alias snacks.picker.Highlight snacks.picker.Text|snacks.picker.Extmark
---@alias snacks.picker.format fun(item:snacks.picker.Item, picker:snacks.Picker):snacks.picker.Highlight[]
---@alias snacks.picker.preview fun(ctx: snacks.picker.preview.ctx):boolean?