Skip to content

Commit fb75cb9

Browse files
authored
fix(ui): correct mark name highlight offset by one (#25)
adjusted name_start calculation so the first character of mark names is highlighted properly added tests to cover single and multi-digit indices
1 parent 37fd8f6 commit fb75cb9

File tree

2 files changed

+148
-1
lines changed

2 files changed

+148
-1
lines changed

lua/marksman/ui.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ local function create_minimal_mark_line(name, mark, index, line_idx)
172172
})
173173

174174
-- Name highlight
175-
local name_start = 5 + #tostring(index)
175+
local name_start = 4 + #tostring(index)
176176
local name_end = name_start + math.min(20, #name)
177177
table.insert(highlights, {
178178
line = line_idx,

tests/marksman_spec.lua

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,4 +528,151 @@ describe("marksman.nvim", function()
528528
assert.is_not_nil(filtered["helper_util"])
529529
end)
530530
end)
531+
532+
describe("UI highlighting", function()
533+
local ui
534+
535+
before_each(function()
536+
-- Load the UI module fresh for each test
537+
package.loaded["marksman.ui"] = nil
538+
ui = require("marksman.ui")
539+
ui.setup({ silent = true })
540+
end)
541+
542+
describe("create_minimal_mark_line highlighting", function()
543+
it("highlights the first character of the mark name correctly", function()
544+
-- This test verifies that the first character of the bookmark name
545+
-- gets highlighted properly ISSUE: #23
546+
547+
-- Mock mark data
548+
local mark = {
549+
file = "/path/to/nvim/lua/plugins/nvim-lspconfig.lua",
550+
line = 42,
551+
col = 1,
552+
text = "local function setup()",
553+
}
554+
555+
local name = "fn_config"
556+
local index = 1
557+
local line_idx = 5
558+
559+
-- Get the private function via debug (for testing)
560+
local create_minimal_mark_line
561+
for k, v in pairs(debug.getregistry()) do
562+
if type(v) == "table" then
563+
for fname, func in pairs(v) do
564+
if fname == "create_minimal_mark_line" and type(func) == "function" then
565+
create_minimal_mark_line = func
566+
break
567+
end
568+
end
569+
end
570+
end
571+
572+
-- If we can't access the private function directly, we'll simulate the logic
573+
local filepath = "nvim/lua/plugins/nvim-lspconfig.lua"
574+
local line = string.format(" [%d] %-20s %s", index, name:sub(1, 20), filepath)
575+
576+
local expected_name_start = 4 + #tostring(index)
577+
local actual_f_position = line:find("f")
578+
579+
assert.equals(
580+
expected_name_start + 1,
581+
actual_f_position,
582+
"The first character 'f' should be at position " .. (expected_name_start + 1) .. " (1-indexed)"
583+
)
584+
585+
local buggy_name_start = 5 + #tostring(index) -- This is 6, which is wrong
586+
local correct_name_start = 4 + #tostring(index) -- This is 5, which is correct
587+
588+
assert.equals(5, correct_name_start, "Correct name start should be 5 for index=1")
589+
assert.equals(6, buggy_name_start, "Buggy calculation gives 6 for index=1")
590+
assert.equals(expected_name_start, correct_name_start, "Correct calculation matches expected position")
591+
end)
592+
593+
it("highlights correctly for double-digit indices", function()
594+
-- Test with index=10 to ensure the fix works for multi-digit indices too
595+
local name = "fn_config"
596+
local index = 10
597+
local expected_name_start = 4 + #tostring(index) -- Should be 6 for index=10
598+
assert.equals(6, expected_name_start, "Name should start at position 6 for index=10")
599+
600+
-- Create the line to verify
601+
local filepath = "test.lua"
602+
local line = string.format(" [%d] %-20s %s", index, name:sub(1, 20), filepath)
603+
local actual_f_position = line:find("f")
604+
605+
-- Find returns 1-indexed, so we expect position 7 (which is 0-indexed position 6)
606+
assert.equals(
607+
expected_name_start + 1,
608+
actual_f_position,
609+
"The 'f' should be at 1-indexed position " .. (expected_name_start + 1)
610+
)
611+
end)
612+
613+
it("applies highlight to the entire mark name", function()
614+
local name = "fn_config"
615+
local index = 1
616+
617+
local name_start = 4 + #tostring(index) -- Correct calculation
618+
local name_end = name_start + math.min(20, #name)
619+
620+
-- For "fn_config" (9 chars), the highlight should cover positions 5-13 (0-indexed)
621+
assert.equals(5, name_start, "Name should start at position 5")
622+
assert.equals(14, name_end, "Name should end at position 14 (5 + 9)")
623+
624+
-- Verify the full name is covered
625+
local expected_length = #name
626+
local actual_length = name_end - name_start
627+
assert.equals(expected_length, actual_length, "Highlight should cover all characters of the name")
628+
end)
629+
630+
it("handles name truncation correctly", function()
631+
-- Test with a name longer than 20 characters
632+
local long_name = "very_long_function_name_that_exceeds_twenty_chars"
633+
local index = 1
634+
635+
local name_start = 4 + #tostring(index)
636+
local name_end = name_start + math.min(20, #long_name)
637+
638+
-- Should truncate to 20 characters
639+
assert.equals(5, name_start)
640+
assert.equals(25, name_end, "Should highlight exactly 20 characters (5 + 20)")
641+
end)
642+
end)
643+
644+
describe("regression test for the reported bug", function()
645+
it("does not skip the first character when highlighting bookmark names", function()
646+
-- "[1] fn_config" where the 'f' was not highlighted
647+
648+
local name = "fn_config"
649+
local index = 1
650+
651+
-- Build the line as the code does
652+
local line = string.format(" [%d] %-20s %s", index, name:sub(1, 20), "somefile.lua")
653+
654+
-- Calculate highlight position using CORRECT formula
655+
local correct_name_start = 4 + #tostring(index)
656+
657+
-- Extract the substring that should be highlighted
658+
local highlighted_part = line:sub(correct_name_start + 1, correct_name_start + #name)
659+
660+
-- The highlighted part should start with 'f', not 'n'
661+
assert.equals(
662+
"fn_config",
663+
highlighted_part:sub(1, #name),
664+
"Highlighted text should start with 'f' (the first character of fn_config)"
665+
)
666+
667+
-- Verify that the buggy calculation would miss the 'f'
668+
local buggy_name_start = 5 + #tostring(index)
669+
local buggy_highlighted_part = line:sub(buggy_name_start + 1, buggy_name_start + #name)
670+
assert.equals(
671+
"n_config ",
672+
buggy_highlighted_part:sub(1, 9),
673+
"Buggy calculation would start with 'n' (missing the 'f')"
674+
)
675+
end)
676+
end)
677+
end)
531678
end)

0 commit comments

Comments
 (0)