Skip to content

Commit 4c4f4b5

Browse files
Fix: Off by 1 Error in Old Sha (#202)
Fixes an issue in the old sha where the index when iterating through the lines of the hunk in the old SHA was off by one
1 parent 7a3e761 commit 4c4f4b5

File tree

3 files changed

+67
-54
lines changed

3 files changed

+67
-54
lines changed

lua/gitlab/hunks/init.lua

Lines changed: 46 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -35,28 +35,31 @@ end
3535
---@param linnr number
3636
---@param hunk Hunk
3737
---@param all_diff_output table
38+
---@return boolean
3839
local line_was_removed = function(linnr, hunk, all_diff_output)
3940
for matching_line_index, line in ipairs(all_diff_output) do
4041
local found_hunk = M.parse_possible_hunk_headers(line)
4142
if found_hunk ~= nil and vim.deep_equal(found_hunk, hunk) then
4243
-- We found a matching hunk, now we need to iterate over the lines from the raw diff output
4344
-- at that hunk until we reach the line we are looking for. When the indexes match we check
4445
-- to see if that line is deleted or not.
45-
for hunk_line_index = found_hunk.old_line, hunk.old_line + hunk.old_range - 1, 1 do
46+
for hunk_line_index = found_hunk.old_line, hunk.old_line + hunk.old_range, 1 do
4647
local line_content = all_diff_output[matching_line_index + 1]
4748
if hunk_line_index == linnr then
4849
if string.match(line_content, "^%-") then
49-
return "deleted"
50+
return true
5051
end
5152
end
5253
end
5354
end
5455
end
56+
return false
5557
end
5658

5759
---@param linnr number
5860
---@param hunk Hunk
5961
---@param all_diff_output table
62+
---@return boolean
6063
local line_was_added = function(linnr, hunk, all_diff_output)
6164
for matching_line_index, line in ipairs(all_diff_output) do
6265
local found_hunk = M.parse_possible_hunk_headers(line)
@@ -71,13 +74,14 @@ local line_was_added = function(linnr, hunk, all_diff_output)
7174
local line_content = all_diff_output[hunk_line_index]
7275
if (found_hunk.new_line + i) == linnr then
7376
if string.match(line_content, "^%+") then
74-
return "added"
77+
return true
7578
end
7679
end
7780
i = i + 1
7881
end
7982
end
8083
end
84+
return false
8185
end
8286

8387
---Parse git diff hunks.
@@ -173,63 +177,58 @@ local count_changes = function(lines)
173177
return total
174178
end
175179

180+
---@param new_line number|nil
181+
---@param hunks Hunk[]
182+
---@param all_diff_output table
183+
---@return string|nil
184+
local function get_modification_type_from_new_sha(new_line, hunks, all_diff_output)
185+
if new_line == nil then
186+
return nil
187+
end
188+
return List.new(hunks):find(function(hunk)
189+
local new_line_end = hunk.new_line + hunk.new_range
190+
local in_new_range = new_line >= hunk.new_line and new_line <= new_line_end
191+
local is_range_zero = hunk.new_range == 0 and hunk.old_range == 0
192+
return in_new_range and (is_range_zero or line_was_added(new_line, hunk, all_diff_output))
193+
end) and "added" or "bad_file_unmodified"
194+
end
195+
196+
---@param old_line number|nil
197+
---@param new_line number|nil
198+
---@param hunks Hunk[]
199+
---@param all_diff_output table
200+
---@return string|nil
201+
local function get_modification_type_from_old_sha(old_line, new_line, hunks, all_diff_output)
202+
if old_line == nil then
203+
return nil
204+
end
205+
206+
return List.new(hunks):find(function(hunk)
207+
local old_line_end = hunk.old_line + hunk.old_range
208+
local new_line_end = hunk.new_line + hunk.new_range
209+
local in_old_range = old_line >= hunk.old_line and old_line <= old_line_end
210+
local in_new_range = old_line >= hunk.new_line and new_line <= new_line_end
211+
return (in_old_range or in_new_range) and line_was_removed(old_line, hunk, all_diff_output)
212+
end) and "deleted" or "unmodified"
213+
end
214+
176215
---Returns whether the comment is on a deleted line, added line, or unmodified line.
177216
---This is in order to build the payload for Gitlab correctly by setting the old line and new line.
178217
---@param old_line number|nil
179218
---@param new_line number|nil
180219
---@param current_file string
220+
---@param is_current_sha boolean
181221
---@return string|nil
182-
function M.get_modification_type(old_line, new_line, current_file)
222+
function M.get_modification_type(old_line, new_line, current_file, is_current_sha)
183223
local hunk_and_diff_data = parse_hunks_and_diff(current_file, state.INFO.target_branch)
184224
if hunk_and_diff_data.hunks == nil then
185225
return
186226
end
187227

188228
local hunks = hunk_and_diff_data.hunks
189229
local all_diff_output = hunk_and_diff_data.all_diff_output
190-
191-
local is_current_sha = require("gitlab.reviewer").is_current_sha()
192-
193-
for _, hunk in ipairs(hunks) do
194-
local old_line_end = hunk.old_line + hunk.old_range
195-
local new_line_end = hunk.new_line + hunk.new_range
196-
197-
if is_current_sha then
198-
-- If it is a single line change and neither hunk has a range, then it's added
199-
if new_line >= hunk.new_line and new_line <= new_line_end then
200-
if hunk.new_range == 0 and hunk.old_range == 0 then
201-
return "added"
202-
end
203-
-- If leaving a comment on the new window, we may be commenting on an added line
204-
-- or on an unmodified line. To tell, we have to check whether the line itself is
205-
-- prefixed with "+" and only return "added" if it is.
206-
if line_was_added(new_line, hunk, all_diff_output) then
207-
return "added"
208-
end
209-
end
210-
else
211-
-- It's a deletion if it's in the range of the hunks and the new
212-
-- range is zero, since that is only a deletion hunk, or if we find
213-
-- a match in another hunk with a range, and the corresponding line is prefixed
214-
-- with a "-" only. If it is, then it's a deletion.
215-
if old_line >= hunk.old_line and old_line <= old_line_end and hunk.old_range == 0 then
216-
return "deleted"
217-
end
218-
if
219-
(old_line >= hunk.old_line and old_line <= old_line_end)
220-
or (old_line >= hunk.new_line and new_line <= new_line_end)
221-
then
222-
if line_was_removed(old_line, hunk, all_diff_output) then
223-
return "deleted"
224-
end
225-
end
226-
end
227-
end
228-
229-
-- If we can't find the line, this means the user is either trying to leave
230-
-- a comment on an unchanged line in the new or old file SHA. This is only
231-
-- allowed in the old file
232-
return is_current_sha and "bad_file_unmodified" or "unmodified"
230+
return is_current_sha and get_modification_type_from_new_sha(new_line, hunks, all_diff_output)
231+
or get_modification_type_from_old_sha(old_line, new_line, hunks, all_diff_output)
233232
end
234233

235234
---Returns the matching line number of a line in the new/old version of the file compared to the current SHA.

lua/gitlab/reviewer/init.lua

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,18 +167,20 @@ M.get_reviewer_data = function()
167167

168168
local new_line = vim.api.nvim_win_get_cursor(new_win)[1]
169169
local old_line = vim.api.nvim_win_get_cursor(old_win)[1]
170-
local modification_type = hunks.get_modification_type(old_line, new_line, current_file)
170+
171+
local is_current_sha = M.is_current_sha()
172+
local modification_type = hunks.get_modification_type(old_line, new_line, current_file, is_current_sha)
171173
if modification_type == nil then
172-
u.notify("Error parsing hunks for modification type", vim.log.levels.ERROR)
174+
u.notify("Error getting modification type", vim.log.levels.ERROR)
173175
return
174176
end
175177

176178
if modification_type == "bad_file_unmodified" then
177179
u.notify("Comments on unmodified lines will be placed in the old file", vim.log.levels.WARN)
178180
end
179181

180-
local current_bufnr = M.is_current_sha() and layout.b.file.bufnr or layout.a.file.bufnr
181-
local opposite_bufnr = M.is_current_sha() and layout.a.file.bufnr or layout.b.file.bufnr
182+
local current_bufnr = is_current_sha and layout.b.file.bufnr or layout.a.file.bufnr
183+
local opposite_bufnr = is_current_sha and layout.a.file.bufnr or layout.b.file.bufnr
182184
local old_sha_win_id = u.get_window_id_by_buffer_id(layout.a.file.bufnr)
183185
local new_sha_win_id = u.get_window_id_by_buffer_id(layout.b.file.bufnr)
184186

lua/gitlab/reviewer/location.lua

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,8 @@ function Location:set_start_range(visual_range)
141141
end
142142

143143
local reviewer = require("gitlab.reviewer")
144-
local win_id = reviewer.is_current_sha() and self.reviewer_data.new_sha_win_id or self.reviewer_data.old_sha_win_id
144+
local is_current_sha = reviewer.is_current_sha()
145+
local win_id = is_current_sha and self.reviewer_data.new_sha_win_id or self.reviewer_data.old_sha_win_id
145146
if win_id == nil then
146147
u.notify("Error getting window number of SHA for start range", vim.log.levels.ERROR)
147148
return
@@ -163,7 +164,11 @@ function Location:set_start_range(visual_range)
163164
return
164165
end
165166

166-
local modification_type = hunks.get_modification_type(old_line, new_line, current_file)
167+
local modification_type = hunks.get_modification_type(old_line, new_line, current_file, is_current_sha)
168+
if modification_type == nil then
169+
u.notify("Error getting modification type for start of range", vim.log.levels.ERROR)
170+
return
171+
end
167172

168173
self.location_data.line_range.start = {
169174
new_line = modification_type ~= "deleted" and new_line or nil,
@@ -199,7 +204,14 @@ function Location:set_end_range(visual_range)
199204
return
200205
end
201206

202-
local modification_type = hunks.get_modification_type(old_line, new_line, current_file)
207+
local reviewer = require("gitlab.reviewer")
208+
local is_current_sha = reviewer.is_current_sha()
209+
local modification_type = hunks.get_modification_type(old_line, new_line, current_file, is_current_sha)
210+
if modification_type == nil then
211+
u.notify("Error getting modification type for end of range", vim.log.levels.ERROR)
212+
return
213+
end
214+
203215
self.location_data.line_range["end"] = {
204216
new_line = modification_type ~= "deleted" and new_line or nil,
205217
old_line = modification_type ~= "added" and old_line or nil,

0 commit comments

Comments
 (0)