Skip to content

Commit eba6788

Browse files
committed
fix(ui): tone down stack preview syntax highlighting
1 parent 129b6f7 commit eba6788

2 files changed

Lines changed: 186 additions & 6 deletions

File tree

lua/peekstack/ui/stack_view.lua

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,22 @@ local M = {}
88
local NS = vim.api.nvim_create_namespace("PeekstackStackView")
99
local TS_HL_PRIORITY = 150
1010
local PREVIEW_BASE_HL_PRIORITY = 10
11+
local PREVIEW_LINE_MARKER = ""
12+
13+
---@type table<string, boolean>
14+
local PREVIEW_ALLOWED_CAPTURE_PREFIX = {
15+
keyword = true,
16+
string = true,
17+
number = true,
18+
boolean = true,
19+
constant = true,
20+
["function"] = true,
21+
method = true,
22+
constructor = true,
23+
type = true,
24+
comment = true,
25+
character = true,
26+
}
1127

1228
---@type table<string, vim.treesitter.Query|false>
1329
local TS_HIGHLIGHT_QUERY_CACHE = {}
@@ -297,6 +313,10 @@ local function capture_hl_group(capture_name, lang)
297313
if type(capture_name) ~= "string" or capture_name == "" then
298314
return nil
299315
end
316+
local capture_prefix = capture_name:match("^[^%.]+") or capture_name
317+
if not PREVIEW_ALLOWED_CAPTURE_PREFIX[capture_prefix] then
318+
return nil
319+
end
300320
if lang and lang ~= "" then
301321
local lang_group = string.format("@%s.%s", capture_name, lang)
302322
if highlight_exists(lang_group) then
@@ -376,8 +396,9 @@ end
376396
---@param source_bufnr integer?
377397
---@param line integer
378398
---@param max_width integer
399+
---@param preview_prefix string
379400
---@return PeekstackStackViewPreviewLine?
380-
local function get_preview_line(source_bufnr, line, max_width)
401+
local function get_preview_line(source_bufnr, line, max_width, preview_prefix)
381402
if not source_bufnr or not vim.api.nvim_buf_is_valid(source_bufnr) then
382403
return nil
383404
end
@@ -397,20 +418,21 @@ local function get_preview_line(source_bufnr, line, max_width)
397418
end
398419

399420
local text = source_text:sub(source_col_start + 1, source_col_end)
400-
local available = math.max(10, max_width - 4)
421+
local prefix_display_width = vim.fn.strdisplaywidth(preview_prefix)
422+
local available = math.max(10, max_width - prefix_display_width)
401423
if vim.fn.strdisplaywidth(text) > available then
402424
local keep_chars = math.max(available - 3, 0)
403425
local kept = vim.fn.strcharpart(text, 0, keep_chars)
404426
text = kept .. "..."
405427
source_col_end = source_col_start + #kept
406428
end
407429
return {
408-
line = " " .. text,
430+
line = preview_prefix .. text,
409431
source_bufnr = source_bufnr,
410432
source_line = line,
411433
source_col_start = source_col_start,
412434
source_col_end = source_col_end,
413-
preview_col_start = 4,
435+
preview_col_start = #preview_prefix,
414436
}
415437
end
416438

@@ -539,7 +561,8 @@ local function render(s)
539561
and popup.location.range.start
540562
and popup.location.range.start.line
541563
if source_line then
542-
local preview = get_preview_line(popup.source_bufnr or popup.bufnr, source_line, win_width)
564+
local preview_prefix = string.rep(" ", vim.fn.strdisplaywidth(prefix)) .. PREVIEW_LINE_MARKER
565+
local preview = get_preview_line(popup.source_bufnr or popup.bufnr, source_line, win_width, preview_prefix)
543566
if preview then
544567
table.insert(lines, preview.line)
545568
local preview_line_nr = #lines

tests/stack_view_spec.lua

Lines changed: 158 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,53 @@ describe("peekstack.ui.stack_view", function()
378378
vim.fn.delete(tmpfile)
379379
end)
380380

381+
it("aligns preview marker with stack item prefix width", function()
382+
local source_bufnr = vim.api.nvim_create_buf(false, true)
383+
vim.api.nvim_buf_set_lines(source_bufnr, 0, -1, false, { "local value = 42" })
384+
385+
local root_winid = vim.api.nvim_get_current_win()
386+
local s = stack.current_stack(root_winid)
387+
s.popups = {
388+
{
389+
id = 1,
390+
title = "Alpha",
391+
location = {
392+
uri = "file:///tmp/alpha.lua",
393+
range = { start = { line = 0, character = 0 }, ["end"] = { line = 0, character = 0 } },
394+
provider = "test",
395+
},
396+
pinned = false,
397+
source_bufnr = source_bufnr,
398+
bufnr = source_bufnr,
399+
},
400+
}
401+
s.focused_id = 1
402+
403+
stack_view.open()
404+
local state = stack_view._get_state()
405+
stack_view._render(state)
406+
407+
local lines = vim.api.nvim_buf_get_lines(state.bufnr, 0, -1, false)
408+
local entry_line = lines[2]
409+
local preview_line = lines[3]
410+
assert.is_not_nil(entry_line)
411+
assert.is_not_nil(preview_line)
412+
413+
local label_pos = entry_line:find("Alpha", 1, true)
414+
local marker_pos = preview_line:find("", 1, true)
415+
assert.is_not_nil(label_pos)
416+
assert.is_not_nil(marker_pos)
417+
418+
local entry_prefix = entry_line:sub(1, label_pos - 1)
419+
local preview_indent = preview_line:sub(1, marker_pos - 1)
420+
assert.equals(vim.fn.strdisplaywidth(entry_prefix), vim.fn.strdisplaywidth(preview_indent))
421+
assert.is_true(preview_line:find("│ local value = 42", 1, true) ~= nil)
422+
423+
if vim.api.nvim_buf_is_valid(source_bufnr) then
424+
vim.api.nvim_buf_delete(source_bufnr, { force = true })
425+
end
426+
end)
427+
381428
it("does not crash when source buffer is invalid for preview", function()
382429
local root_winid = vim.api.nvim_get_current_win()
383430
local s = stack.current_stack(root_winid)
@@ -525,6 +572,116 @@ describe("peekstack.ui.stack_view", function()
525572
end
526573
end)
527574

575+
it("skips noisy treesitter captures for preview lines", function()
576+
local tmpfile = vim.fn.tempname() .. ".lua"
577+
vim.fn.writefile({ "local value = 42" }, tmpfile)
578+
579+
local original_get_parser = vim.treesitter.get_parser
580+
local original_query_get = vim.treesitter.query.get
581+
local ok, err = pcall(function()
582+
vim.api.nvim_set_hl(0, "@operator.peekstack_test_ts_skip", { link = "Operator" })
583+
vim.api.nvim_set_hl(0, "@keyword.peekstack_test_ts_skip", { link = "Keyword" })
584+
585+
local fake_keyword_node = {
586+
range = function()
587+
return 0, 0, 0, 5
588+
end,
589+
}
590+
local fake_operator_node = {
591+
range = function()
592+
return 0, 12, 0, 13
593+
end,
594+
}
595+
local fake_root = {
596+
range = function()
597+
return 0, 0, 0, 20
598+
end,
599+
}
600+
local fake_tree = {
601+
root = function()
602+
return fake_root
603+
end,
604+
lang = function()
605+
return "peekstack_test_ts_skip"
606+
end,
607+
}
608+
local fake_query = {
609+
captures = { "operator", "keyword" },
610+
}
611+
fake_query.iter_captures = function(_self, _root, _bufnr, _start_row, _end_row)
612+
local step = 0
613+
return function()
614+
step = step + 1
615+
if step == 1 then
616+
return 1, fake_operator_node
617+
end
618+
if step == 2 then
619+
return 2, fake_keyword_node
620+
end
621+
return nil
622+
end
623+
end
624+
625+
vim.treesitter.get_parser = function(_bufnr)
626+
return {
627+
parse = function() end,
628+
trees = function()
629+
return { fake_tree }
630+
end,
631+
}
632+
end
633+
vim.treesitter.query.get = function(_lang, _query_name)
634+
return fake_query
635+
end
636+
637+
local loc = helpers.make_location({
638+
uri = vim.uri_from_fname(tmpfile),
639+
range = { start = { line = 0, character = 0 }, ["end"] = { line = 0, character = 0 } },
640+
})
641+
local model = stack.push(loc)
642+
assert.is_not_nil(model)
643+
644+
stack_view.open()
645+
local state = stack_view._get_state()
646+
stack_view._render(state)
647+
648+
local lines = vim.api.nvim_buf_get_lines(state.bufnr, 0, -1, false)
649+
local preview_line_nr = nil
650+
for idx, line in ipairs(lines) do
651+
if line:find("local value = 42", 1, true) then
652+
preview_line_nr = idx
653+
break
654+
end
655+
end
656+
assert.is_not_nil(preview_line_nr)
657+
658+
local ns = vim.api.nvim_create_namespace("PeekstackStackView")
659+
local extmarks = vim.api.nvim_buf_get_extmarks(state.bufnr, ns, 0, -1, { details = true })
660+
local has_keyword = false
661+
local has_operator = false
662+
for _, mark in ipairs(extmarks) do
663+
local row = mark[2]
664+
local details = mark[4] or {}
665+
if row == preview_line_nr - 1 and details.hl_group == "@keyword.peekstack_test_ts_skip" then
666+
has_keyword = true
667+
end
668+
if row == preview_line_nr - 1 and details.hl_group == "@operator.peekstack_test_ts_skip" then
669+
has_operator = true
670+
end
671+
end
672+
673+
assert.is_true(has_keyword, "keyword capture should be applied")
674+
assert.is_false(has_operator, "operator capture should be skipped for preview")
675+
end)
676+
677+
vim.treesitter.get_parser = original_get_parser
678+
vim.treesitter.query.get = original_query_get
679+
vim.fn.delete(tmpfile)
680+
if not ok then
681+
error(err)
682+
end
683+
end)
684+
528685
it("falls back to default preview highlight when treesitter parser fails", function()
529686
local tmpfile = vim.fn.tempname() .. ".lua"
530687
vim.fn.writefile({ "local x = 42" }, tmpfile)
@@ -648,7 +805,7 @@ describe("peekstack.ui.stack_view", function()
648805
local lines = vim.api.nvim_buf_get_lines(state.bufnr, 0, -1, false)
649806
local preview_line_nr = nil
650807
for idx, line in ipairs(lines) do
651-
if line:find("^ a", 1, false) then
808+
if line:find("a", 1, true) then
652809
preview_line_nr = idx
653810
break
654811
end

0 commit comments

Comments
 (0)