@@ -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