Files
gitlab.nvim/lua/gitlab/reviewer/location.lua
Harrison (Harry) Cramer 9f898aa1a8 Bug Fixes (#470)
* fix: Restore buffer local settings outside reviewer (#446)
* fix: do not show healthcheck alert for warnings (#468)
* feat: Add MR URL to the summary details (#467)
* fix: make cycling reviewed files faster (#474)
* feat(pipeline): display trigger jobs for a pipeline in the pipelines popup  (#465)
* fix: Jumping to renamed files (#484)

---------

Co-authored-by: Jakub F. Bortlík <jakub.bortlik@proton.me>
Co-authored-by: Ashish Alex <ashish.alex10@gmail.com>
2025-03-01 13:28:02 -05:00

228 lines
7.4 KiB
Lua
Executable File

local u = require("gitlab.utils")
local hunks = require("gitlab.hunks")
local state = require("gitlab.state")
---@class Location
---@field location_data LocationData
---@field reviewer_data DiffviewInfo
---@field run function
---@field build_location_data function
---@field visual_range table
---@class ReviewerLineInfo
---@field old_line integer|nil
---@field new_line integer|nil
---@field type "new"|"old"
---@class ReviewerRangeInfo
---@field start ReviewerLineInfo
---@field end ReviewerLineInfo
local Location = {}
Location.__index = Location
---The new() function returns nil when the location cannot be created due to missing
---reviewer data.
---@return Location | nil
function Location.new()
local current_win = vim.api.nvim_get_current_win()
local reviewer_data = require("gitlab.reviewer").get_reviewer_data(current_win)
if reviewer_data == nil then
return nil
end
local location = {}
local instance = setmetatable(location, Location)
instance.reviewer_data = reviewer_data
instance.base_sha = state.INFO.diff_refs.base_sha
instance.head_sha = state.INFO.diff_refs.head_sha
instance:build_location_data()
return instance
end
---Takes in information about the current changes, such as the file name, modification type of the diff, and the line numbers
---and builds the appropriate payload when creating a comment.
function Location:build_location_data()
---@type DiffviewInfo
local reviewer_data = self.reviewer_data
local start_line, end_line = u.get_visual_selection_boundaries()
---@type LineRange
self.visual_range = { start_line = start_line, end_line = end_line }
---@type LocationData
self.location_data = {
old_line = nil,
new_line = nil,
line_range = nil,
}
-- Comment on new line: Include only new_line in payload.
-- Comment on deleted line: Include only old_line in payload.
-- The line was not found in any hunks, send both lines.
if reviewer_data.modification_type == "added" then
self.location_data.old_line = nil
self.location_data.new_line = reviewer_data.new_line_from_buf
elseif reviewer_data.modification_type == "deleted" then
self.location_data.old_line = reviewer_data.old_line_from_buf
self.location_data.new_line = nil
elseif
reviewer_data.modification_type == "unmodified" or reviewer_data.modification_type == "bad_file_unmodified"
then
self.location_data.old_line = reviewer_data.old_line_from_buf
self.location_data.new_line = reviewer_data.new_line_from_buf
end
if end_line > start_line then
self.location_data.line_range = {
start = {},
["end"] = {},
}
else
return
end
self:set_start_range()
self:set_end_range()
-- Ranged comments should always use the end of the range.
-- Otherwise they will not highlight the full comment in Gitlab.
self.location_data.old_line = self.location_data.line_range["end"].old_line
self.location_data.new_line = self.location_data.line_range["end"].new_line
end
-- Helper methods 🤝
-- Returns the matching line from the new SHA.
-- For instance, line 12 in the new SHA may be scroll-linked
-- to line 10 in the old SHA.
---@param line number
---@return number|nil
function Location:get_line_number_from_new_sha(line)
if self.reviewer_data.new_sha_focused then
return line
end
-- Otherwise we want to get the matching line in the opposite buffer
return hunks.calculate_matching_line_new(
self.base_sha,
self.head_sha,
self.reviewer_data.file_name,
self.reviewer_data.old_file_name,
line
)
end
-- Returns the matching line from the old SHA.
-- For instance, line 12 in the new SHA may be scroll-linked
-- to line 10 in the old SHA.
---@param line number
---@return number|nil
function Location:get_line_number_from_old_sha(line)
if not self.reviewer_data.new_sha_focused then
return line
end
-- Otherwise we want to get the matching line in the opposite buffer
return hunks.calculate_matching_line_new(
self.head_sha,
self.base_sha,
self.reviewer_data.file_name,
self.reviewer_data.old_file_name,
line
)
end
-- Returns the current line number from whatever SHA (new or old)
-- the reviewer is focused in.
---@return number|nil
function Location:get_current_line()
if self.reviewer_data.current_win_id == nil then
return
end
local current_line = vim.api.nvim_win_get_cursor(self.reviewer_data.current_win_id)[1]
return current_line
end
-- Given a modification type, a visual selection range, and the hunk data, sets the start range
-- information to the location_data for the Gitlab payload
function Location:set_start_range()
local current_file = require("gitlab.reviewer").get_current_file_path()
if current_file == nil then
u.notify("Error getting current file from Diffview", vim.log.levels.ERROR)
return
end
if self.reviewer_data.current_win_id == nil then
u.notify("Error getting window number of SHA for start range", vim.log.levels.ERROR)
return
end
local current_line = self:get_current_line()
if current_line == nil then
u.notify("Error getting current line for start range", vim.log.levels.ERROR)
return
end
local new_line = self:get_line_number_from_new_sha(self.visual_range.start_line)
local old_line = self:get_line_number_from_old_sha(self.visual_range.start_line)
if
(new_line == nil and self.reviewer_data.modification_type ~= "deleted")
or (old_line == nil and self.reviewer_data.modification_type ~= "added")
then
u.notify("Error getting new or old line for start range", vim.log.levels.ERROR)
return
end
local modification_type = hunks.get_modification_type(old_line, new_line, self.reviewer_data.new_sha_focused)
if modification_type == nil then
u.notify("Error getting modification type for start of range", vim.log.levels.ERROR)
return
end
self.location_data.line_range.start = {
new_line = modification_type ~= "deleted" and new_line or nil,
old_line = modification_type ~= "added" and old_line or nil,
type = modification_type == "added" and "new" or "old",
}
end
-- Given a modification type, a visual selection range, and the hunk data, sets the end range
-- information to the location_data for the Gitlab payload
function Location:set_end_range()
local current_file = require("gitlab.reviewer").get_current_file_path()
if current_file == nil then
u.notify("Error getting current file from Diffview", vim.log.levels.ERROR)
return
end
local current_line = self:get_current_line()
if current_line == nil then
u.notify("Error getting current line for end range", vim.log.levels.ERROR)
return
end
local new_line = self:get_line_number_from_new_sha(self.visual_range.end_line)
local old_line = self:get_line_number_from_old_sha(self.visual_range.end_line)
if
(new_line == nil and self.reviewer_data.modification_type ~= "deleted")
or (old_line == nil and self.reviewer_data.modification_type ~= "added")
then
u.notify("Error getting new or old line for end range", vim.log.levels.ERROR)
return
end
local modification_type = hunks.get_modification_type(old_line, new_line, self.reviewer_data.new_sha_focused)
if modification_type == nil then
u.notify("Error getting modification type for end of range", vim.log.levels.ERROR)
return
end
self.location_data.line_range["end"] = {
new_line = modification_type ~= "deleted" and new_line or nil,
old_line = modification_type ~= "added" and old_line or nil,
type = modification_type == "added" and "new" or "old",
}
end
return Location