This MR makes the plugin throw the correct error when someone tries to open up the reviewer without any changes
258 lines
8.4 KiB
Lua
258 lines
8.4 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()
|
|
local diff_refs = state.INFO.diff_refs
|
|
if diff_refs == nil then
|
|
u.notify("Gitlab did not provide diff refs required to review this MR", vim.log.levels.ERROR)
|
|
return
|
|
end
|
|
|
|
if diff_refs.base_sha == "" or diff_refs.head_sha == "" then
|
|
u.notify("Merge request contains no changes", vim.log.levels.ERROR)
|
|
return
|
|
end
|
|
|
|
vim.api.nvim_command(string.format("DiffviewOpen %s..%s", diff_refs.base_sha, diff_refs.head_sha))
|
|
M.tabnr = vim.api.nvim_get_current_tabpage()
|
|
|
|
if state.INFO.has_conflicts then
|
|
u.notify("This merge request has conflicts!", vim.log.levels.WARN)
|
|
end
|
|
|
|
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)
|
|
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
|