From 938437499247c9b56c1d2813d068fb8eaaa517b3 Mon Sep 17 00:00:00 2001 From: Jorg Jenni Date: Thu, 24 Sep 2026 10:21:37 +0100 Subject: [PATCH 1/6] fix: handle line codes with an empty line number GitLab leaves the old or new number of a line code empty when the line has no counterpart on the other side (e.g. "__44"). parse_line_code returned nil for both numbers in that case and get_line_numbers_for_range crashed with "attempt to perform arithmetic on local 'new_end_line' (a nil value)" while setting diagnostics in the reviewer. Parse empty numbers as nil and fall back to a single-line range. --- lua/gitlab/actions/common.lua | 4 +-- lua/gitlab/indicators/common.lua | 15 +++++------ tests/spec/line_code_spec.lua | 45 ++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 tests/spec/line_code_spec.lua diff --git a/lua/gitlab/actions/common.lua b/lua/gitlab/actions/common.lua index 03ca63a0..9d78fe1e 100644 --- a/lua/gitlab/actions/common.lua +++ b/lua/gitlab/actions/common.lua @@ -262,10 +262,10 @@ M.get_line_numbers_for_range = function(old_line, new_line, start_line_code, end 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) if old_line ~= nil and old_start_line ~= 0 then - local range = old_end_line - old_start_line + local range = (old_start_line and old_end_line) and (old_end_line - old_start_line) or 0 return (old_line - range), old_line, false elseif new_line ~= nil then - local range = new_end_line - new_start_line + local range = (new_start_line and new_end_line) and (new_end_line - new_start_line) or 0 -- Force start_line to be greater than 0 -- TODO: use `math.max(new_line - range, 1)` instead local start_line = (new_line - range > 0) and (new_line - range) or 1 diff --git a/lua/gitlab/indicators/common.lua b/lua/gitlab/indicators/common.lua index 5324a463..4844b2d7 100644 --- a/lua/gitlab/indicators/common.lua +++ b/lua/gitlab/indicators/common.lua @@ -53,15 +53,14 @@ M.filter_placeable_discussions = function() end ---Parse old and new line from a line code like "3f454a98e586d1aa0d322e19afd5e67e08f2d3c8_10_44". ----@param line_code string A SHA hash of the file name and line numbers before and after change ----@return integer The line number before the change ----@return integer The line number after the change +---GitLab leaves a number empty when the line has no counterpart, e.g. "3f454a98...__44". +---@param line_code? string A SHA hash of the file name and line numbers before and after change +---@return integer? The line number before the change +---@return integer? The line number after the change M.parse_line_code = function(line_code) - local line_code_regex = "%w+_(%d+)_(%d+)" - local old_line, new_line = line_code:match(line_code_regex) - old_line = tonumber(old_line) --[[@as integer]] - new_line = tonumber(new_line) --[[@as integer]] - return old_line, new_line + local line_code_regex = "%w+_(%d*)_(%d*)" + local old_line, new_line = (line_code or ""):match(line_code_regex) + return tonumber(old_line), tonumber(new_line) end ---Return true if discussion/draft belongs to the old file, otherwise false. diff --git a/tests/spec/line_code_spec.lua b/tests/spec/line_code_spec.lua new file mode 100644 index 00000000..a5827024 --- /dev/null +++ b/tests/spec/line_code_spec.lua @@ -0,0 +1,45 @@ +local indicators_common = require("gitlab.indicators.common") +local actions_common = require("gitlab.actions.common") + +local sha = "3f454a98e586d1aa0d322e19afd5e67e08f2d3c8" + +describe("indicators/common.parse_line_code", function() + it("Parses both line numbers", function() + assert.are.same({ 10, 44 }, { indicators_common.parse_line_code(sha .. "_10_44") }) + end) + + it("Returns nil for an empty old line number", function() + assert.are.same({ nil, 44 }, { indicators_common.parse_line_code(sha .. "__44") }) + end) + + it("Returns nil for an empty new line number", function() + assert.are.same({ 10, nil }, { indicators_common.parse_line_code(sha .. "_10_") }) + end) + + it("Returns nil for a missing line code", function() + assert.are.same({ nil, nil }, { indicators_common.parse_line_code(nil) }) + end) +end) + +describe("actions/common.get_line_numbers_for_range", function() + it("Computes a range on the new SHA", function() + assert.are.same( + { 40, 44, true }, + { actions_common.get_line_numbers_for_range(nil, 44, sha .. "_1_40", sha .. "_1_44") } + ) + end) + + it("Falls back to a single line when a line code has an empty number", function() + assert.are.same( + { 44, 44, true }, + { actions_common.get_line_numbers_for_range(nil, 44, sha .. "__40", sha .. "_1_") } + ) + end) + + it("Falls back to a single line on the old SHA when a line code has an empty number", function() + assert.are.same( + { 12, 12, false }, + { actions_common.get_line_numbers_for_range(12, nil, sha .. "_10_", sha .. "__5") } + ) + end) +end) From 6e275293076497cd6c16de8c9964a40c8a742012 Mon Sep 17 00:00:00 2001 From: Jorg Jenni Date: Sat, 26 Sep 2026 14:16:50 +0100 Subject: [PATCH 2/6] Revert "fix: handle line codes with an empty line number" This reverts commit 938437499247c9b56c1d2813d068fb8eaaa517b3. --- lua/gitlab/actions/common.lua | 4 +-- lua/gitlab/indicators/common.lua | 15 ++++++----- tests/spec/line_code_spec.lua | 45 -------------------------------- 3 files changed, 10 insertions(+), 54 deletions(-) delete mode 100644 tests/spec/line_code_spec.lua diff --git a/lua/gitlab/actions/common.lua b/lua/gitlab/actions/common.lua index 9d78fe1e..03ca63a0 100644 --- a/lua/gitlab/actions/common.lua +++ b/lua/gitlab/actions/common.lua @@ -262,10 +262,10 @@ M.get_line_numbers_for_range = function(old_line, new_line, start_line_code, end 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) if old_line ~= nil and old_start_line ~= 0 then - local range = (old_start_line and old_end_line) and (old_end_line - old_start_line) or 0 + local range = old_end_line - old_start_line return (old_line - range), old_line, false elseif new_line ~= nil then - local range = (new_start_line and new_end_line) and (new_end_line - new_start_line) or 0 + local range = new_end_line - new_start_line -- Force start_line to be greater than 0 -- TODO: use `math.max(new_line - range, 1)` instead local start_line = (new_line - range > 0) and (new_line - range) or 1 diff --git a/lua/gitlab/indicators/common.lua b/lua/gitlab/indicators/common.lua index 4844b2d7..5324a463 100644 --- a/lua/gitlab/indicators/common.lua +++ b/lua/gitlab/indicators/common.lua @@ -53,14 +53,15 @@ M.filter_placeable_discussions = function() end ---Parse old and new line from a line code like "3f454a98e586d1aa0d322e19afd5e67e08f2d3c8_10_44". ----GitLab leaves a number empty when the line has no counterpart, e.g. "3f454a98...__44". ----@param line_code? string A SHA hash of the file name and line numbers before and after change ----@return integer? The line number before the change ----@return integer? The line number after the change +---@param line_code string A SHA hash of the file name and line numbers before and after change +---@return integer The line number before the change +---@return integer The line number after the change M.parse_line_code = function(line_code) - local line_code_regex = "%w+_(%d*)_(%d*)" - local old_line, new_line = (line_code or ""):match(line_code_regex) - return tonumber(old_line), tonumber(new_line) + local line_code_regex = "%w+_(%d+)_(%d+)" + local old_line, new_line = line_code:match(line_code_regex) + old_line = tonumber(old_line) --[[@as integer]] + new_line = tonumber(new_line) --[[@as integer]] + return old_line, new_line end ---Return true if discussion/draft belongs to the old file, otherwise false. diff --git a/tests/spec/line_code_spec.lua b/tests/spec/line_code_spec.lua deleted file mode 100644 index a5827024..00000000 --- a/tests/spec/line_code_spec.lua +++ /dev/null @@ -1,45 +0,0 @@ -local indicators_common = require("gitlab.indicators.common") -local actions_common = require("gitlab.actions.common") - -local sha = "3f454a98e586d1aa0d322e19afd5e67e08f2d3c8" - -describe("indicators/common.parse_line_code", function() - it("Parses both line numbers", function() - assert.are.same({ 10, 44 }, { indicators_common.parse_line_code(sha .. "_10_44") }) - end) - - it("Returns nil for an empty old line number", function() - assert.are.same({ nil, 44 }, { indicators_common.parse_line_code(sha .. "__44") }) - end) - - it("Returns nil for an empty new line number", function() - assert.are.same({ 10, nil }, { indicators_common.parse_line_code(sha .. "_10_") }) - end) - - it("Returns nil for a missing line code", function() - assert.are.same({ nil, nil }, { indicators_common.parse_line_code(nil) }) - end) -end) - -describe("actions/common.get_line_numbers_for_range", function() - it("Computes a range on the new SHA", function() - assert.are.same( - { 40, 44, true }, - { actions_common.get_line_numbers_for_range(nil, 44, sha .. "_1_40", sha .. "_1_44") } - ) - end) - - it("Falls back to a single line when a line code has an empty number", function() - assert.are.same( - { 44, 44, true }, - { actions_common.get_line_numbers_for_range(nil, 44, sha .. "__40", sha .. "_1_") } - ) - end) - - it("Falls back to a single line on the old SHA when a line code has an empty number", function() - assert.are.same( - { 12, 12, false }, - { actions_common.get_line_numbers_for_range(12, nil, sha .. "_10_", sha .. "__5") } - ) - end) -end) From 0544813acafec8437d3c4f704cb016255b86feb2 Mon Sep 17 00:00:00 2001 From: Jorg Jenni Date: Sat, 26 Sep 2026 14:16:50 +0100 Subject: [PATCH 3/6] fix: handle empty line codes in ranged discussions GitLab can return an empty line_code for both ends of a discussion's line_range. parse_line_code then returns nil for both numbers and get_line_numbers_for_range crashed with "attempt to perform arithmetic on local 'new_end_line' (a nil value)" while setting diagnostics in the reviewer. Fall back to a single-line range when a line code can't be parsed, and don't throw on a nil line code. --- lua/gitlab/actions/common.lua | 4 ++-- lua/gitlab/indicators/common.lua | 13 ++++++------ tests/spec/line_code_spec.lua | 35 ++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 9 deletions(-) create mode 100644 tests/spec/line_code_spec.lua diff --git a/lua/gitlab/actions/common.lua b/lua/gitlab/actions/common.lua index 03ca63a0..9d78fe1e 100644 --- a/lua/gitlab/actions/common.lua +++ b/lua/gitlab/actions/common.lua @@ -262,10 +262,10 @@ M.get_line_numbers_for_range = function(old_line, new_line, start_line_code, end 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) if old_line ~= nil and old_start_line ~= 0 then - local range = old_end_line - old_start_line + local range = (old_start_line and old_end_line) and (old_end_line - old_start_line) or 0 return (old_line - range), old_line, false elseif new_line ~= nil then - local range = new_end_line - new_start_line + local range = (new_start_line and new_end_line) and (new_end_line - new_start_line) or 0 -- Force start_line to be greater than 0 -- TODO: use `math.max(new_line - range, 1)` instead local start_line = (new_line - range > 0) and (new_line - range) or 1 diff --git a/lua/gitlab/indicators/common.lua b/lua/gitlab/indicators/common.lua index 5324a463..cbe5a11a 100644 --- a/lua/gitlab/indicators/common.lua +++ b/lua/gitlab/indicators/common.lua @@ -53,15 +53,14 @@ M.filter_placeable_discussions = function() end ---Parse old and new line from a line code like "3f454a98e586d1aa0d322e19afd5e67e08f2d3c8_10_44". ----@param line_code string A SHA hash of the file name and line numbers before and after change ----@return integer The line number before the change ----@return integer The line number after the change +---GitLab can return an empty line code for a ranged discussion. +---@param line_code? string A SHA hash of the file name and line numbers before and after change +---@return integer? The line number before the change +---@return integer? The line number after the change M.parse_line_code = function(line_code) local line_code_regex = "%w+_(%d+)_(%d+)" - local old_line, new_line = line_code:match(line_code_regex) - old_line = tonumber(old_line) --[[@as integer]] - new_line = tonumber(new_line) --[[@as integer]] - return old_line, new_line + local old_line, new_line = (line_code or ""):match(line_code_regex) + return tonumber(old_line), tonumber(new_line) end ---Return true if discussion/draft belongs to the old file, otherwise false. diff --git a/tests/spec/line_code_spec.lua b/tests/spec/line_code_spec.lua new file mode 100644 index 00000000..a6bbbf07 --- /dev/null +++ b/tests/spec/line_code_spec.lua @@ -0,0 +1,35 @@ +local indicators_common = require("gitlab.indicators.common") +local actions_common = require("gitlab.actions.common") + +local sha = "3f454a98e586d1aa0d322e19afd5e67e08f2d3c8" + +describe("indicators/common.parse_line_code", function() + it("Parses both line numbers", function() + assert.are.same({ 10, 44 }, { indicators_common.parse_line_code(sha .. "_10_44") }) + end) + + it("Returns nil for an empty line code", function() + assert.are.same({ nil, nil }, { indicators_common.parse_line_code("") }) + end) + + it("Returns nil for a missing line code", function() + assert.are.same({ nil, nil }, { indicators_common.parse_line_code(nil) }) + end) +end) + +describe("actions/common.get_line_numbers_for_range", function() + it("Computes a range on the new SHA", function() + assert.are.same( + { 40, 44, true }, + { actions_common.get_line_numbers_for_range(nil, 44, sha .. "_1_40", sha .. "_1_44") } + ) + end) + + it("Falls back to a single line on the new SHA when the line codes are empty", function() + assert.are.same({ 26, 26, true }, { actions_common.get_line_numbers_for_range(nil, 26, "", "") }) + end) + + it("Falls back to a single line on the old SHA when the line codes are empty", function() + assert.are.same({ 12, 12, false }, { actions_common.get_line_numbers_for_range(12, nil, "", "") }) + end) +end) From 088ab14a33ebbc95df12798b724ae22e820f4880 Mon Sep 17 00:00:00 2001 From: Jorg Jenni Date: Mon, 5 Oct 2026 16:10:19 +0100 Subject: [PATCH 4/6] test: split line code specs by module Move the parse_line_code and get_line_numbers_for_range specs into per-module spec files, nested under the module and function they test. --- tests/spec/actions_common_spec.lua | 22 +++++++++++++++++ tests/spec/indicators_common_spec.lua | 19 +++++++++++++++ tests/spec/line_code_spec.lua | 35 --------------------------- 3 files changed, 41 insertions(+), 35 deletions(-) create mode 100644 tests/spec/actions_common_spec.lua create mode 100644 tests/spec/indicators_common_spec.lua delete mode 100644 tests/spec/line_code_spec.lua diff --git a/tests/spec/actions_common_spec.lua b/tests/spec/actions_common_spec.lua new file mode 100644 index 00000000..93e7a0c4 --- /dev/null +++ b/tests/spec/actions_common_spec.lua @@ -0,0 +1,22 @@ +local actions_common = require("gitlab.actions.common") + +local sha = "3f454a98e586d1aa0d322e19afd5e67e08f2d3c8" + +describe("gitlab/actions/common.lua", function() + describe("get_line_numbers_for_range", function() + it("Computes a range on the new SHA", function() + assert.are.same( + { 40, 44, true }, + { actions_common.get_line_numbers_for_range(nil, 44, sha .. "_1_40", sha .. "_1_44") } + ) + end) + + it("Falls back to a single line on the new SHA when the line codes are empty", function() + assert.are.same({ 26, 26, true }, { actions_common.get_line_numbers_for_range(nil, 26, "", "") }) + end) + + it("Falls back to a single line on the old SHA when the line codes are empty", function() + assert.are.same({ 12, 12, false }, { actions_common.get_line_numbers_for_range(12, nil, "", "") }) + end) + end) +end) diff --git a/tests/spec/indicators_common_spec.lua b/tests/spec/indicators_common_spec.lua new file mode 100644 index 00000000..3ed5e3f0 --- /dev/null +++ b/tests/spec/indicators_common_spec.lua @@ -0,0 +1,19 @@ +local indicators_common = require("gitlab.indicators.common") + +local sha = "3f454a98e586d1aa0d322e19afd5e67e08f2d3c8" + +describe("gitlab/indicators/common.lua", function() + describe("parse_line_code", function() + it("Parses both line numbers", function() + assert.are.same({ 10, 44 }, { indicators_common.parse_line_code(sha .. "_10_44") }) + end) + + it("Returns nil for an empty line code", function() + assert.are.same({ nil, nil }, { indicators_common.parse_line_code("") }) + end) + + it("Returns nil for a missing line code", function() + assert.are.same({ nil, nil }, { indicators_common.parse_line_code(nil) }) + end) + end) +end) diff --git a/tests/spec/line_code_spec.lua b/tests/spec/line_code_spec.lua deleted file mode 100644 index a6bbbf07..00000000 --- a/tests/spec/line_code_spec.lua +++ /dev/null @@ -1,35 +0,0 @@ -local indicators_common = require("gitlab.indicators.common") -local actions_common = require("gitlab.actions.common") - -local sha = "3f454a98e586d1aa0d322e19afd5e67e08f2d3c8" - -describe("indicators/common.parse_line_code", function() - it("Parses both line numbers", function() - assert.are.same({ 10, 44 }, { indicators_common.parse_line_code(sha .. "_10_44") }) - end) - - it("Returns nil for an empty line code", function() - assert.are.same({ nil, nil }, { indicators_common.parse_line_code("") }) - end) - - it("Returns nil for a missing line code", function() - assert.are.same({ nil, nil }, { indicators_common.parse_line_code(nil) }) - end) -end) - -describe("actions/common.get_line_numbers_for_range", function() - it("Computes a range on the new SHA", function() - assert.are.same( - { 40, 44, true }, - { actions_common.get_line_numbers_for_range(nil, 44, sha .. "_1_40", sha .. "_1_44") } - ) - end) - - it("Falls back to a single line on the new SHA when the line codes are empty", function() - assert.are.same({ 26, 26, true }, { actions_common.get_line_numbers_for_range(nil, 26, "", "") }) - end) - - it("Falls back to a single line on the old SHA when the line codes are empty", function() - assert.are.same({ 12, 12, false }, { actions_common.get_line_numbers_for_range(12, nil, "", "") }) - end) -end) From 4735216dcedd28ee7ee4840448c5562532a2bf79 Mon Sep 17 00:00:00 2001 From: Jorg Jenni Date: Mon, 5 Oct 2026 16:31:28 +0100 Subject: [PATCH 5/6] fix: use range line numbers when line codes are empty When GitLab returns empty line codes for a ranged discussion, get_line_numbers_for_range collapsed the range to a single line. Fall back to the range's old_line/new_line instead. These are 0 on the side a line doesn't exist on, so a range spanning deleted and added lines without line codes still falls back to a single line. get_line_numbers_for_range now takes the whole line range instead of the two line codes. --- lua/gitlab/actions/common.lua | 39 +++++++++---- lua/gitlab/indicators/diagnostics.lua | 3 +- tests/spec/actions_common_spec.lua | 81 +++++++++++++++++++++++---- 3 files changed, 100 insertions(+), 23 deletions(-) diff --git a/lua/gitlab/actions/common.lua b/lua/gitlab/actions/common.lua index 9d78fe1e..bb622f15 100644 --- a/lua/gitlab/actions/common.lua +++ b/lua/gitlab/actions/common.lua @@ -248,24 +248,42 @@ 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 number of lines between the start and end of a range on one side of the diff. +---Line codes are preferred because they hold both line numbers, whereas the range's +---`old_line`/`new_line` are 0 on the side a line doesn't exist on. +---@param start_from_code? integer The start line parsed from the line code +---@param end_from_code? integer The end line parsed from the line code +---@param start_line? integer The start line from the range +---@param end_line? integer The end line from the range +---@return integer +local function get_range_length(start_from_code, end_from_code, start_line, end_line) + if start_from_code and end_from_code then + return end_from_code - start_from_code + end + if (start_line or 0) > 0 and (end_line or 0) > 0 then + return end_line - start_line + end + return 0 +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 ----either the `new_line` or `old_line`. +---The range is calculated from the line codes, or from the range's line numbers if GitLab +---returns empty line codes, 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 line_range LineRange The range of the note ---@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, line_range) + local start, end_ = line_range.start, line_range["end"] + 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) if old_line ~= nil and old_start_line ~= 0 then - local range = (old_start_line and old_end_line) and (old_end_line - old_start_line) or 0 + local range = get_range_length(old_start_line, old_end_line, start.old_line, end_.old_line) return (old_line - range), old_line, false elseif new_line ~= nil then - local range = (new_start_line and new_end_line) and (new_end_line - new_start_line) or 0 + local range = get_range_length(new_start_line, new_end_line, start.new_line, end_.new_line) -- Force start_line to be greater than 0 -- TODO: use `math.max(new_line - range, 1)` instead local start_line = (new_line - range > 0) and (new_line - range) or 1 @@ -285,8 +303,7 @@ 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 ) return line_number, is_new_sha else diff --git a/lua/gitlab/indicators/diagnostics.lua b/lua/gitlab/indicators/diagnostics.lua index d4d6b810..02faa98d 100644 --- a/lua/gitlab/indicators/diagnostics.lua +++ b/lua/gitlab/indicators/diagnostics.lua @@ -79,8 +79,7 @@ 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 ) return create_diagnostic({ diff --git a/tests/spec/actions_common_spec.lua b/tests/spec/actions_common_spec.lua index 93e7a0c4..16711179 100644 --- a/tests/spec/actions_common_spec.lua +++ b/tests/spec/actions_common_spec.lua @@ -2,21 +2,82 @@ local actions_common = require("gitlab.actions.common") local sha = "3f454a98e586d1aa0d322e19afd5e67e08f2d3c8" +local function line_code(old_line, new_line) + return sha .. "_" .. old_line .. "_" .. new_line +end + +local function position(type, old_line, new_line, code) + return { type = type, old_line = old_line, new_line = new_line, line_code = code or "" } +end + describe("gitlab/actions/common.lua", function() describe("get_line_numbers_for_range", function() - it("Computes a range on the new SHA", function() - assert.are.same( - { 40, 44, true }, - { actions_common.get_line_numbers_for_range(nil, 44, sha .. "_1_40", sha .. "_1_44") } - ) - end) + describe("with line codes", function() + it("Computes a range on the new SHA", function() + local range = { + start = position("new", 0, 40, line_code(1, 40)), + ["end"] = position("new", 0, 44, line_code(1, 44)), + } + assert.are.same({ 40, 44, true }, { actions_common.get_line_numbers_for_range(nil, 44, range) }) + end) + + it("Computes a range on the old SHA", function() + local range = { + start = position("old", 10, 0, line_code(10, 20)), + ["end"] = position("old", 12, 0, line_code(12, 20)), + } + assert.are.same({ 10, 12, false }, { actions_common.get_line_numbers_for_range(12, nil, range) }) + end) - it("Falls back to a single line on the new SHA when the line codes are empty", function() - assert.are.same({ 26, 26, true }, { actions_common.get_line_numbers_for_range(nil, 26, "", "") }) + it("Computes a range spanning deleted and added lines", function() + local range = { + start = position("old", 5, 0, line_code(5, 38)), + ["end"] = position("new", 0, 44, line_code(7, 44)), + } + assert.are.same({ 38, 44, true }, { actions_common.get_line_numbers_for_range(nil, 44, range) }) + end) end) - it("Falls back to a single line on the old SHA when the line codes are empty", function() - assert.are.same({ 12, 12, false }, { actions_common.get_line_numbers_for_range(12, nil, "", "") }) + describe("without line codes", function() + it("Computes a range on the new SHA from the range's new lines", function() + local range = { + start = position("new", 0, 24), + ["end"] = position("new", 0, 26), + } + assert.are.same({ 24, 26, true }, { actions_common.get_line_numbers_for_range(nil, 26, range) }) + end) + + it("Computes a range on the old SHA from the range's old lines", function() + local range = { + start = position("old", 10, 0), + ["end"] = position("old", 12, 0), + } + assert.are.same({ 10, 12, false }, { actions_common.get_line_numbers_for_range(12, nil, range) }) + end) + + it("Computes a range from the range's lines when only one line code is empty", function() + local range = { + start = position("new", 0, 24, line_code(1, 24)), + ["end"] = position("new", 0, 26), + } + assert.are.same({ 24, 26, true }, { actions_common.get_line_numbers_for_range(nil, 26, range) }) + end) + + it("Falls back to a single line for a range spanning deleted and added lines", function() + local range = { + start = position("old", 5, 0), + ["end"] = position("new", 0, 44), + } + assert.are.same({ 44, 44, true }, { actions_common.get_line_numbers_for_range(nil, 44, range) }) + end) + + it("Falls back to a single line when the range's lines are missing", function() + local range = { + start = position("old", 5, nil), + ["end"] = position("new", nil, 44), + } + assert.are.same({ 44, 44, true }, { actions_common.get_line_numbers_for_range(nil, 44, range) }) + end) end) end) end) From f813eed1b2c6a2a937fa9977f20bb1e1b4a1ce04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Bortl=C3=ADk?= Date: Tue, 13 Oct 2026 23:15:55 +0200 Subject: [PATCH 6/6] style: apply stylua --- lua/gitlab/actions/common.lua | 7 ++----- lua/gitlab/indicators/diagnostics.lua | 7 ++----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/lua/gitlab/actions/common.lua b/lua/gitlab/actions/common.lua index bb622f15..7dfbd4fd 100644 --- a/lua/gitlab/actions/common.lua +++ b/lua/gitlab/actions/common.lua @@ -300,11 +300,8 @@ end ---@return boolean is_new_sha True if line number refers to NEW SHA M.get_line_number_from_node = function(root_node) if root_node.range then - local line_number, _, is_new_sha = M.get_line_numbers_for_range( - root_node.old_line, - root_node.new_line, - root_node.range - ) + local line_number, _, is_new_sha = + M.get_line_numbers_for_range(root_node.old_line, root_node.new_line, root_node.range) return line_number, is_new_sha else return M.get_line_number(root_node.id) diff --git a/lua/gitlab/indicators/diagnostics.lua b/lua/gitlab/indicators/diagnostics.lua index 02faa98d..57c91c3d 100644 --- a/lua/gitlab/indicators/diagnostics.lua +++ b/lua/gitlab/indicators/diagnostics.lua @@ -76,11 +76,8 @@ local create_multiline_diagnostic = function(d_or_n) error("Parsing multi-line comment but note does not contain line range") end - local start_line, end_line, _ = actions_common.get_line_numbers_for_range( - first_note.position.old_line, - first_note.position.new_line, - line_range - ) + local start_line, end_line, _ = + actions_common.get_line_numbers_for_range(first_note.position.old_line, first_note.position.new_line, line_range) return create_diagnostic({ lnum = start_line - 1,