From fe183101662ee955ac0805aa910b6f222fbb85ac Mon Sep 17 00:00:00 2001 From: Hektor Misplon Date: Mon, 5 Oct 2026 19:07:27 +0200 Subject: [PATCH] fix: resolve draft note line ranges without line codes --- lua/gitlab/actions/common.lua | 27 ++++++++++++------ lua/gitlab/indicators/diagnostics.lua | 4 +-- tests/spec/common_line_range_spec.lua | 40 +++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 10 deletions(-) create mode 100644 tests/spec/common_line_range_spec.lua diff --git a/lua/gitlab/actions/common.lua b/lua/gitlab/actions/common.lua index 03ca63a0..29fadfb9 100644 --- a/lua/gitlab/actions/common.lua +++ b/lua/gitlab/actions/common.lua @@ -248,19 +248,30 @@ M.get_line_number = function(id) return ((is_new_sha and first_note.position.new_line or first_note.position.old_line) or 1), is_new_sha end +---Return the OLD and NEW line numbers for one end of a note range. The `draft_notes` API +---leaves `line_code` empty inside `line_range`, so fall back to the line numbers that the +---range end carries itself. +---@param range_end PositionInfo One end of a `line_range` +---@return integer? old_line +---@return integer? new_line +local function parse_range_end(range_end) + local old_line, new_line = indicators_common.parse_line_code(range_end.line_code or "") + return old_line or range_end.old_line, new_line or range_end.new_line +end + ---Return start and end line numbers for the note range, and whether comment is on "NEW SHA". ----The range is calculated from the line codes but the position itself is based on +---The range is calculated from the range ends but the position itself is based on ---either the `new_line` or `old_line`. ---@param old_line? integer The line number in the OLD version ---@param new_line? integer The line number in the NEW version ----@param start_line_code string The line code for the start of the range ----@param end_line_code string The line code for the end of the range +---@param range_start PositionInfo The start of the range +---@param range_end PositionInfo The end of the range ---@return integer start_line ---@return integer end_line ---@return boolean is_new_sha True if line range refers to NEW SHA -M.get_line_numbers_for_range = function(old_line, new_line, start_line_code, end_line_code) - local old_start_line, new_start_line = indicators_common.parse_line_code(start_line_code) - local old_end_line, new_end_line = indicators_common.parse_line_code(end_line_code) +M.get_line_numbers_for_range = function(old_line, new_line, range_start, range_end) + local old_start_line, new_start_line = parse_range_end(range_start) + local old_end_line, new_end_line = parse_range_end(range_end) if old_line ~= nil and old_start_line ~= 0 then local range = old_end_line - old_start_line return (old_line - range), old_line, false @@ -285,8 +296,8 @@ M.get_line_number_from_node = function(root_node) local line_number, _, is_new_sha = M.get_line_numbers_for_range( root_node.old_line, root_node.new_line, - root_node.range.start.line_code, - root_node.range["end"].line_code + root_node.range.start, + root_node.range["end"] ) return line_number, is_new_sha else diff --git a/lua/gitlab/indicators/diagnostics.lua b/lua/gitlab/indicators/diagnostics.lua index d4d6b810..1fc0aadd 100644 --- a/lua/gitlab/indicators/diagnostics.lua +++ b/lua/gitlab/indicators/diagnostics.lua @@ -79,8 +79,8 @@ local create_multiline_diagnostic = function(d_or_n) local start_line, end_line, _ = actions_common.get_line_numbers_for_range( first_note.position.old_line, first_note.position.new_line, - line_range.start.line_code, - line_range["end"].line_code + line_range.start, + line_range["end"] ) return create_diagnostic({ diff --git a/tests/spec/common_line_range_spec.lua b/tests/spec/common_line_range_spec.lua new file mode 100644 index 00000000..89699569 --- /dev/null +++ b/tests/spec/common_line_range_spec.lua @@ -0,0 +1,40 @@ +-- The `draft_notes` API leaves `line_code` empty inside `line_range`, so resolving the +-- range from line codes alone makes a draft note's multi-line comment fail with +-- "attempt to perform arithmetic on local 'new_end_line' (a nil value)". + +local common = require("gitlab.actions.common") + +describe("actions/common.get_line_numbers_for_range", function() + it("Resolves a NEW SHA range from its line codes", function() + local start_line, end_line, is_new_sha = common.get_line_numbers_for_range( + nil, + 500, + { line_code = "a0185ee7c1543c5d1e2eee6bd336fcbf59504f38_482_495", type = "new" }, + { line_code = "a0185ee7c1543c5d1e2eee6bd336fcbf59504f38_487_500", type = "new" } + ) + + assert.are.same({ 495, 500, true }, { start_line, end_line, is_new_sha }) + end) + + it("Resolves an OLD SHA range from its line codes", function() + local start_line, end_line, is_new_sha = common.get_line_numbers_for_range( + 487, + nil, + { line_code = "a0185ee7c1543c5d1e2eee6bd336fcbf59504f38_482_495", type = "old" }, + { line_code = "a0185ee7c1543c5d1e2eee6bd336fcbf59504f38_487_500", type = "old" } + ) + + assert.are.same({ 482, 487, false }, { start_line, end_line, is_new_sha }) + end) + + it("Falls back to the line numbers a draft note's range carries itself", function() + local start_line, end_line, is_new_sha = common.get_line_numbers_for_range( + nil, + 500, + { line_code = "", new_line = 495, type = "new" }, + { line_code = "", new_line = 500, type = "new" } + ) + + assert.are.same({ 495, 500, true }, { start_line, end_line, is_new_sha }) + end) +end)