This MR adds support for in-line comments in the review pane. This allows you to view comments (as diagnostics) directly in the Neovim buffers that you are reviewing. You can then jump to them directly in the discussion tree if you want to reply, edit, and so forth.
244 lines
8.0 KiB
Lua
244 lines
8.0 KiB
Lua
-- This Module contains all of the reviewer code for diffview
|
|
local u = require("gitlab.utils")
|
|
local state = require("gitlab.state")
|
|
local async_ok, async = pcall(require, "diffview.async")
|
|
local diffview_lib = require("diffview.lib")
|
|
|
|
local M = {
|
|
bufnr = nil,
|
|
tabnr = nil,
|
|
}
|
|
|
|
M.open = function()
|
|
vim.api.nvim_command(string.format("DiffviewOpen %s", state.INFO.target_branch))
|
|
M.tabnr = vim.api.nvim_get_current_tabpage()
|
|
local group = vim.api.nvim_create_augroup("gitlab.diffview.autocommand.close", {})
|
|
vim.api.nvim_create_autocmd("User", {
|
|
pattern = { "DiffviewViewClosed" },
|
|
group = group,
|
|
callback = function()
|
|
--Check if our diffview tab was closed
|
|
if vim.api.nvim_tabpage_is_valid(M.tabnr) then
|
|
M.tabnr = nil
|
|
end
|
|
end,
|
|
})
|
|
end
|
|
|
|
M.jump = function(file_name, new_line, old_line)
|
|
if M.tabnr == nil then
|
|
u.notify("Can't jump to Diffvew. Is it open?", vim.log.levels.ERROR)
|
|
return
|
|
end
|
|
vim.api.nvim_set_current_tabpage(M.tabnr)
|
|
vim.cmd("DiffviewFocusFiles")
|
|
local view = diffview_lib.get_current_view()
|
|
if view == nil then
|
|
u.notify("Could not find Diffview view", vim.log.levels.ERROR)
|
|
return
|
|
end
|
|
local files = view.panel:ordered_file_list()
|
|
local layout = view.cur_layout
|
|
for _, file in ipairs(files) do
|
|
if file.path == file_name then
|
|
if not async_ok then
|
|
u.notify("Could not load Diffview async", vim.log.levels.ERROR)
|
|
return
|
|
end
|
|
async.await(view:set_file(file))
|
|
if new_line ~= nil then
|
|
layout.b:focus()
|
|
vim.api.nvim_win_set_cursor(0, { tonumber(new_line), 0 })
|
|
elseif old_line ~= nil then
|
|
layout.a:focus()
|
|
vim.api.nvim_win_set_cursor(0, { tonumber(old_line), 0 })
|
|
end
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
---Get the location of a line within the diffview. If range is specified, then also the location
|
|
---of the lines in range.
|
|
---@param range LineRange | nil Line range to get location for
|
|
---@return ReviewerInfo | nil nil is returned only if error was encountered
|
|
M.get_location = function(range)
|
|
if M.tabnr == nil then
|
|
u.notify("Diffview reviewer must be initialized first")
|
|
return
|
|
end
|
|
local bufnr = vim.api.nvim_get_current_buf()
|
|
local current_line = vim.api.nvim_win_get_cursor(0)[1]
|
|
|
|
-- check if we are in the diffview tab
|
|
local tabnr = vim.api.nvim_get_current_tabpage()
|
|
if tabnr ~= M.tabnr then
|
|
u.notify("Line location can only be determined within reviewer window")
|
|
return
|
|
end
|
|
|
|
-- check if we are in the diffview buffer
|
|
local view = diffview_lib.get_current_view()
|
|
if view == nil then
|
|
u.notify("Could not find Diffview view", vim.log.levels.ERROR)
|
|
return
|
|
end
|
|
local layout = view.cur_layout
|
|
local result = {}
|
|
local type
|
|
local is_new
|
|
if layout.a.file.bufnr == bufnr then
|
|
result.file_name = layout.a.file.path
|
|
result.old_line = current_line
|
|
type = "old"
|
|
is_new = false
|
|
elseif layout.b.file.bufnr == bufnr then
|
|
result.file_name = layout.b.file.path
|
|
result.new_line = current_line
|
|
type = "new"
|
|
is_new = true
|
|
else
|
|
u.notify("Line location can only be determined within reviewer window")
|
|
return
|
|
end
|
|
|
|
local hunks = u.parse_hunk_headers(result.file_name, state.INFO.target_branch)
|
|
if hunks == nil then
|
|
u.notify("Could not parse hunks", vim.log.levels.ERROR)
|
|
return
|
|
end
|
|
|
|
local current_line_info
|
|
if is_new then
|
|
current_line_info = u.get_lines_from_hunks(hunks, result.new_line, is_new)
|
|
else
|
|
current_line_info = u.get_lines_from_hunks(hunks, result.old_line, is_new)
|
|
end
|
|
|
|
-- If single line comment is outside of changed lines then we need to specify both new line and old line
|
|
-- otherwise the API returns error.
|
|
-- https://docs.gitlab.com/ee/api/discussions.html#create-a-new-thread-in-the-merge-request-diff
|
|
if not current_line_info.in_hunk then
|
|
result.old_line = current_line_info.old_line
|
|
result.new_line = current_line_info.new_line
|
|
end
|
|
|
|
if range == nil then
|
|
return result
|
|
end
|
|
|
|
result.range_info = { start = {}, ["end"] = {} }
|
|
if current_line == range.start_line then
|
|
result.range_info.start.old_line = current_line_info.old_line
|
|
result.range_info.start.new_line = current_line_info.new_line
|
|
result.range_info.start.type = type
|
|
else
|
|
local start_line_info = u.get_lines_from_hunks(hunks, range.start_line, is_new)
|
|
result.range_info.start.old_line = start_line_info.old_line
|
|
result.range_info.start.new_line = start_line_info.new_line
|
|
result.range_info.start.type = type
|
|
end
|
|
|
|
if current_line == range.end_line then
|
|
result.range_info["end"].old_line = current_line_info.old_line
|
|
result.range_info["end"].new_line = current_line_info.new_line
|
|
result.range_info["end"].type = type
|
|
else
|
|
local end_line_info = u.get_lines_from_hunks(hunks, range.end_line, is_new)
|
|
result.range_info["end"].old_line = end_line_info.old_line
|
|
result.range_info["end"].new_line = end_line_info.new_line
|
|
result.range_info["end"].type = type
|
|
end
|
|
|
|
return result
|
|
end
|
|
|
|
---Return content between start_line and end_line
|
|
---@param start_line integer
|
|
---@param end_line integer
|
|
---@return string[]
|
|
M.get_lines = function(start_line, end_line)
|
|
return vim.api.nvim_buf_get_lines(0, start_line - 1, end_line, false)
|
|
end
|
|
|
|
---Get currently shown file
|
|
M.get_current_file = function()
|
|
local view = diffview_lib.get_current_view()
|
|
if not view then
|
|
return
|
|
end
|
|
return view.panel.cur_file.path
|
|
end
|
|
|
|
---Place a sign in currently reviewed file. Use new line for identifing lines after changes, old
|
|
---line for identifing lines before changes and both if line was not changed.
|
|
---@param signs table table of signs. See :h sign_placelist
|
|
---@param type string "new" if diagnostic should be in file after changes else "old"
|
|
M.place_sign = function(signs, type)
|
|
local view = diffview_lib.get_current_view()
|
|
if not view then
|
|
return
|
|
end
|
|
if type == "new" then
|
|
for _, sign in ipairs(signs) do
|
|
sign.buffer = view.cur_layout.b.file.bufnr
|
|
end
|
|
elseif type == "old" then
|
|
for _, sign in ipairs(signs) do
|
|
sign.buffer = view.cur_layout.a.file.bufnr
|
|
end
|
|
end
|
|
vim.fn.sign_placelist(signs)
|
|
end
|
|
|
|
---Set diagnostics in currently reviewed file.
|
|
---@param namespace integer namespace for diagnostics
|
|
---@param diagnostics table see :h vim.diagnostic.set
|
|
---@param type string "new" if diagnostic should be in file after changes else "old"
|
|
---@param opts table? see :h vim.diagnostic.set
|
|
M.set_diagnostics = function(namespace, diagnostics, type, opts)
|
|
local view = diffview_lib.get_current_view()
|
|
if not view then
|
|
return
|
|
end
|
|
if type == "new" and view.cur_layout.b.file.bufnr then
|
|
vim.diagnostic.set(namespace, view.cur_layout.b.file.bufnr, diagnostics, opts)
|
|
elseif type == "old" and view.cur_layout.a.file.bufnr then
|
|
vim.diagnostic.set(namespace, view.cur_layout.a.file.bufnr, diagnostics, opts)
|
|
else
|
|
vim.notify("Unknown diagnostic type", vim.log.levels.ERROR)
|
|
end
|
|
end
|
|
|
|
---Diffview exposes events which can be used to setup autocommands.
|
|
---@param callback fun(opts: table) - for more information about opts see callback in :h nvim_create_autocmd
|
|
M.set_callback_for_file_changed = function(callback)
|
|
local group = vim.api.nvim_create_augroup("gitlab.diffview.autocommand.file_changed", {})
|
|
vim.api.nvim_create_autocmd("User", {
|
|
pattern = { "DiffviewDiffBufWinEnter", "DiffviewViewEnter" },
|
|
group = group,
|
|
callback = function(...)
|
|
if M.tabnr == vim.api.nvim_get_current_tabpage() then
|
|
callback(...)
|
|
end
|
|
end,
|
|
})
|
|
end
|
|
|
|
---Diffview exposes events which can be used to setup autocommands.
|
|
---@param callback fun(opts: table) - for more information about opts see callback in :h nvim_create_autocmd
|
|
M.set_callback_for_reviewer_leave = function(callback)
|
|
local group = vim.api.nvim_create_augroup("gitlab.diffview.autocommand.leave", {})
|
|
vim.api.nvim_create_autocmd("User", {
|
|
pattern = { "DiffviewViewLeave", "DiffviewViewClosed" },
|
|
group = group,
|
|
callback = function(...)
|
|
if M.tabnr == vim.api.nvim_get_current_tabpage() then
|
|
callback(...)
|
|
end
|
|
end,
|
|
})
|
|
end
|
|
|
|
return M
|