Skip to content

Commit d167c50

Browse files
barrettruthchrisbra
authored andcommitted
patch 9.2.0707: completion: popup misplaced when text before it is concealed
Problem: When the cursor line has concealed text before the start of the completion, the insert-mode completion popup is drawn at the wrong screen column and the cursor no longer lines up with the completed text. Solution: Record the concealed width before the cursor on its screen line in a new `win_T` field while `win_line()` draws it, subtract it in `pum_display()` to place the menu over the visible text, and redraw the cursor line so `win_line()` corrects the cursor too. closes: #20539 Signed-off-by: Barrett Ruth <br@barrettruth.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
1 parent 4ed08ee commit d167c50

10 files changed

Lines changed: 168 additions & 0 deletions

src/drawline.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3813,6 +3813,10 @@ win_line(
38133813
else
38143814
# endif
38153815
wp->w_wcol = wlv.col - wlv.boguscols;
3816+
// Screen cells concealed before the cursor on this screen line, so
3817+
// pum_display() can line the menu up with the visible text;
3818+
// "skip_cells" is the concealed cell at the cursor not yet counted.
3819+
wp->w_wcol_conceal_off = wlv.vcol_off_co + skip_cells;
38163820
if (wlv.vcol + skip_cells < wp->w_virtcol)
38173821
// Cursor beyond end of the line with 'virtualedit'.
38183822
wp->w_wcol += wp->w_virtcol - wlv.vcol - skip_cells;

src/insexpand.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1896,6 +1896,15 @@ ins_compl_show_pum(void)
18961896
pum_display(compl_match_array, compl_match_arraysize, cur);
18971897
curwin->w_cursor.col = col;
18981898

1899+
#ifdef FEAT_CONCEAL
1900+
// The cursor was temporarily moved to "compl_col" above to position the
1901+
// menu, so the screen update left w_wcol conceal-corrected for that column
1902+
// rather than for the real cursor. Redraw the cursor line so the caret is
1903+
// positioned correctly when the cursor line has concealed text.
1904+
if (curwin->w_p_cole > 0 && conceal_cursor_line(curwin))
1905+
redrawWinline(curwin, curwin->w_cursor.lnum);
1906+
#endif
1907+
18991908
// After adding leader, set the current match to shown match.
19001909
if (compl_started && compl_curr_match != compl_shown_match)
19011910
compl_curr_match = compl_shown_match;

src/popupmenu.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,17 @@ pum_display(
361361
{
362362
// w_wcol includes virtual text "above"
363363
int wcol = curwin->w_wcol % curwin->w_width;
364+
#ifdef FEAT_CONCEAL
365+
// w_wcol does not account for text concealed before the cursor;
366+
// shift by the offset win_line() recorded for the cursor line so the
367+
// menu lines up with the visible text.
368+
if (curwin->w_p_cole > 0 && conceal_cursor_line(curwin))
369+
{
370+
wcol -= curwin->w_wcol_conceal_off;
371+
if (wcol < 0)
372+
wcol = 0;
373+
}
374+
#endif
364375
#ifdef FEAT_RIGHTLEFT
365376
if (pum_rl)
366377
cursor_col = curwin->w_wincol + curwin->w_width - wcol - 1;

src/structs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4367,6 +4367,10 @@ struct window_S
43674367
* buffer, thus w_wrow is relative to w_winrow.
43684368
*/
43694369
int w_wrow, w_wcol; // cursor position in window
4370+
#ifdef FEAT_CONCEAL
4371+
int w_wcol_conceal_off; // screen cells concealed before w_wcol on
4372+
// the cursor's screen line, set by win_line()
4373+
#endif
43704374

43714375
/*
43724376
* Info about the lines currently in the window is remembered to avoid

src/testdir/dumps/Test_pum_position_with_concealed_match.dump

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/testdir/dumps/Test_pum_position_with_concealed_rl.dump

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/testdir/dumps/Test_pum_position_with_concealed_text.dump

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/testdir/dumps/Test_pum_position_with_concealed_wrap.dump

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/testdir/test_ins_complete.vim

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -870,6 +870,104 @@ func Test_pum_stopped_by_timer()
870870
call StopVimInTerminal(buf)
871871
endfunc
872872

873+
" The completion popup menu must line up with the start of the completed text
874+
" on screen, also when there is concealed text before it on the line.
875+
func Test_pum_position_with_concealed_text()
876+
CheckScreendump
877+
878+
let lines =<< trim END
879+
call setline(1, ['CONCEALED foobar', 'CONCEALED foo'])
880+
syntax match Hidden /CONCEALED / conceal
881+
setlocal conceallevel=3 concealcursor=nvic
882+
set completeopt=menu,menuone
883+
END
884+
885+
call writefile(lines, 'Xpumconceal', 'D')
886+
let buf = RunVimInTerminal('-S Xpumconceal', #{rows: 10})
887+
call TermWait(buf, 50)
888+
call term_sendkeys(buf, "2GA")
889+
call TermWait(buf, 50)
890+
call term_sendkeys(buf, "\<C-X>\<C-N>")
891+
call VerifyScreenDump(buf, 'Test_pum_position_with_concealed_text', {})
892+
893+
call term_sendkeys(buf, "\<Esc>")
894+
call StopVimInTerminal(buf)
895+
endfunc
896+
897+
" Same alignment when the concealed text comes from a match and is shown as a
898+
" replacement character with 'conceallevel' 2.
899+
func Test_pum_position_with_concealed_match()
900+
CheckScreendump
901+
902+
let lines =<< trim END
903+
call setline(1, ['XXX foobar', 'XXX foo'])
904+
call matchadd('Conceal', 'XXX ', 10, -1, {'conceal': '+'})
905+
setlocal conceallevel=2 concealcursor=nvic
906+
set completeopt=menu,menuone
907+
END
908+
909+
call writefile(lines, 'Xpumconcealmatch', 'D')
910+
let buf = RunVimInTerminal('-S Xpumconcealmatch', #{rows: 10})
911+
call TermWait(buf, 50)
912+
call term_sendkeys(buf, "2GA")
913+
call TermWait(buf, 50)
914+
call term_sendkeys(buf, "\<C-X>\<C-N>")
915+
call VerifyScreenDump(buf, 'Test_pum_position_with_concealed_match', {})
916+
917+
call term_sendkeys(buf, "\<Esc>")
918+
call StopVimInTerminal(buf)
919+
endfunc
920+
921+
" The menu lines up with the visible text in a 'rightleft' window too, where
922+
" the cursor screen column is mirrored.
923+
func Test_pum_position_with_concealed_rl()
924+
CheckScreendump
925+
CheckFeature rightleft
926+
927+
let lines =<< trim END
928+
set rightleft
929+
call setline(1, ['CONCEALED foobar', 'CONCEALED foo'])
930+
syntax match Hidden /CONCEALED / conceal
931+
setlocal conceallevel=3 concealcursor=nvic
932+
set completeopt=menu,menuone
933+
END
934+
935+
call writefile(lines, 'Xpumconcealrl', 'D')
936+
let buf = RunVimInTerminal('-S Xpumconcealrl', #{rows: 10})
937+
call TermWait(buf, 50)
938+
call term_sendkeys(buf, "2GA")
939+
call TermWait(buf, 50)
940+
call term_sendkeys(buf, "\<C-X>\<C-N>")
941+
call VerifyScreenDump(buf, 'Test_pum_position_with_concealed_rl', {})
942+
943+
call term_sendkeys(buf, "\<Esc>")
944+
call StopVimInTerminal(buf)
945+
endfunc
946+
947+
" The recorded offset is per screen line, so the menu also lines up when the
948+
" concealed text and the completion are on a wrapped continuation line.
949+
func Test_pum_position_with_concealed_wrap()
950+
CheckScreendump
951+
952+
let lines =<< trim END
953+
call setline(1, ['foobar', 'aaaaaaaaaaaaaaaaaaaa CONCEALED foo'])
954+
syntax match Hidden /CONCEALED / conceal
955+
setlocal conceallevel=3 concealcursor=nvic
956+
set completeopt=menu,menuone
957+
END
958+
959+
call writefile(lines, 'Xpumconcealwrap', 'D')
960+
let buf = RunVimInTerminal('-S Xpumconcealwrap', #{rows: 10, cols: 20})
961+
call TermWait(buf, 50)
962+
call term_sendkeys(buf, "2GA")
963+
call TermWait(buf, 50)
964+
call term_sendkeys(buf, "\<C-X>\<C-N>")
965+
call VerifyScreenDump(buf, 'Test_pum_position_with_concealed_wrap', {})
966+
967+
call term_sendkeys(buf, "\<Esc>")
968+
call StopVimInTerminal(buf)
969+
endfunc
970+
873971
func Test_complete_stopinsert_startinsert()
874972
nnoremap <F2> <Cmd>startinsert<CR>
875973
inoremap <F2> <Cmd>stopinsert<CR>

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,8 @@ static char *(features[]) =
759759

760760
static int included_patches[] =
761761
{ /* Add new patch number below this line */
762+
/**/
763+
707,
762764
/**/
763765
706,
764766
/**/

0 commit comments

Comments
 (0)