Mercurial > vim
view src/testdir/test_listchars.vim @ 34686:83875247fbc0 v9.1.0224
patch 9.1.0224: cursor may move too many lines over "right" & "below" virt text
Commit: https://github.com/vim/vim/commit/515f734e687f28f7199b2a8042197624d9f3ec15
Author: Dylan Thacker-Smith <dylan.ah.smith@gmail.com>
Date: Thu Mar 28 12:01:14 2024 +0100
patch 9.1.0224: cursor may move too many lines over "right" & "below" virt text
Problem: If a line has "right" & "below" virtual text properties,
where the "below" property may be stored first due to lack of
ordering between them, then the line height is calculated to
be 1 more and causes the cursor to far over the line.
Solution: Remove some unnecessary setting of a
`next_right_goes_below = TRUE` flag for "below" and "above"
text properties. (Dylan Thacker-Smith)
I modified a regression test I recently added to cover this case,
leveraging the fact that "after", "right" & "below" text properties are
being stored in the reverse of the order they are added in. The
previous version of this regression test was crafted to workaround this
issue so it can be addressed by this separate patch.
closes: #14317
Signed-off-by: Dylan Thacker-Smith <dylan.ah.smith@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Thu, 28 Mar 2024 12:15:03 +0100 |
parents | e7d64a3fffe4 |
children |
line wrap: on
line source
" Tests for 'listchars' display with 'list' and :list source check.vim source view_util.vim source screendump.vim func Check_listchars(expected, end_lnum, end_scol = -1, leftcol = 0) if a:leftcol > 0 let save_wrap = &wrap set nowrap call cursor(1, 1) exe 'normal! ' .. a:leftcol .. 'zl' endif redraw! for i in range(1, a:end_lnum) if a:leftcol > 0 let col = virtcol2col(0, i, a:leftcol) let col += getline(i)->strpart(col - 1, 1, v:true)->len() call cursor(i, col) redraw call assert_equal(a:leftcol, winsaveview().leftcol) else call cursor(i, 1) end let end_scol = a:end_scol < 0 ? '$'->virtcol() - a:leftcol : a:end_scol call assert_equal([a:expected[i - 1]->strcharpart(a:leftcol)], \ ScreenLines(i, end_scol)) endfor if a:leftcol > 0 let &wrap = save_wrap endif endfunc func Test_listchars() enew! set ff=unix set list set listchars+=tab:>-,space:.,trail:< call append(0, [ \ ' aa ', \ ' bb ', \ ' cccc ', \ 'dd ee ', \ ' ' \ ]) let expected = [ \ '>-------aa>-----$', \ '..bb>---<<$', \ '...cccc><$', \ 'dd........ee<<>-$', \ '<$' \ ] call Check_listchars(expected, 5) call Check_listchars(expected, 4, -1, 5) set listchars-=trail:< let expected = [ \ '>-------aa>-----$', \ '..bb>---..$', \ '...cccc>.$', \ 'dd........ee..>-$', \ '.$' \ ] call Check_listchars(expected, 5) call Check_listchars(expected, 4, -1, 5) " tab with 3rd character. set listchars-=tab:>- set listchars+=tab:<=>,trail:- let expected = [ \ '<======>aa<====>$', \ '..bb<==>--$', \ '...cccc>-$', \ 'dd........ee--<>$', \ '-$' \ ] call Check_listchars(expected, 5) call Check_listchars(expected, 4, -1, 5) " tab with 3rd character and linebreak set set listchars-=tab:<=> set listchars+=tab:<·> set linebreak let expected = [ \ '<······>aa<····>$', \ '..bb<··>--$', \ '...cccc>-$', \ 'dd........ee--<>$', \ '-$' \ ] call Check_listchars(expected, 5) set nolinebreak set listchars-=tab:<·> set listchars+=tab:<=> set listchars-=trail:- let expected = [ \ '<======>aa<====>$', \ '..bb<==>..$', \ '...cccc>.$', \ 'dd........ee..<>$', \ '.$' \ ] call Check_listchars(expected, 5) call Check_listchars(expected, 4, -1, 5) set listchars-=tab:<=> set listchars+=tab:>- set listchars+=trail:< set nolist normal ggdG call append(0, [ \ ' fff ', \ ' gg ', \ ' h ', \ 'iii ', \ ]) let l = split(execute("%list"), "\n") call assert_equal([ \ '..fff>--<<$', \ '>-------gg>-----$', \ '.....h>-$', \ 'iii<<<<><<$', \ '$'], l) " Test lead and trail normal ggdG set listchars& set listchars+=lead:>,trail:<,space:x set list call append(0, [ \ ' ffff ', \ ' gg', \ 'h ', \ ' ', \ ' 0 0 ', \ ]) let expected = [ \ '>>>>ffff<<<<$', \ '>>>>>>>>>>gg$', \ 'h<<<<<<<<<<<$', \ '<<<<<<<<<<<<$', \ '>>>>0xx0<<<<$', \ '$' \ ] call Check_listchars(expected, 6) call Check_listchars(expected, 5, -1, 6) call assert_equal(expected, split(execute("%list"), "\n")) " Test multispace normal ggdG set listchars& set listchars+=multispace:yYzZ set list call append(0, [ \ ' ffff ', \ ' i i gg', \ ' h ', \ ' j ', \ ' 0 0 ', \ ]) let expected = [ \ 'yYzZffffyYzZ$', \ 'yYi iyYzZygg$', \ ' hyYzZyYzZyY$', \ 'yYzZyYzZyYj $', \ 'yYzZ0yY0yYzZ$', \ '$' \ ] call Check_listchars(expected, 6) call Check_listchars(expected, 5, -1, 6) call assert_equal(expected, split(execute("%list"), "\n")) " Test leadmultispace + multispace normal ggdG set listchars=eol:$,multispace:yYzZ,nbsp:S set listchars+=leadmultispace:.-+* set list call append(0, [ \ ' ffff ', \ ' i i gg', \ ' h ', \ ' j ', \ ' 0 0 ', \ ]) let expected = [ \ '.-+*ffffyYzZ$', \ '.-i iSyYzZgg$', \ ' hyYzZyYzZyY$', \ '.-+*.-+*.-j $', \ '.-+*0yY0yYzZ$', \ '$' \ ] call assert_equal('eol:$,multispace:yYzZ,nbsp:S,leadmultispace:.-+*', &listchars) call Check_listchars(expected, 6) call Check_listchars(expected, 5, -1, 1) call Check_listchars(expected, 5, -1, 2) call Check_listchars(expected, 5, -1, 3) call Check_listchars(expected, 5, -1, 6) call assert_equal(expected, split(execute("%list"), "\n")) " Test leadmultispace without multispace normal ggdG set listchars-=multispace:yYzZ set listchars+=space:+,trail:>,eol:$ set list call append(0, [ \ ' ffff ', \ ' i i gg', \ ' h ', \ ' j ', \ ' 0 0 ', \ ]) let expected = [ \ '.-+*ffff>>>>$', \ '.-i+i+++++gg$', \ '+h>>>>>>>>>>$', \ '.-+*.-+*.-j>$', \ '.-+*0++0>>>>$', \ '$' \ ] call assert_equal('eol:$,nbsp:S,leadmultispace:.-+*,space:+,trail:>,eol:$', &listchars) call Check_listchars(expected, 6) call Check_listchars(expected, 5, -1, 1) call Check_listchars(expected, 5, -1, 2) call Check_listchars(expected, 5, -1, 3) call Check_listchars(expected, 5, -1, 6) call assert_equal(expected, split(execute("%list"), "\n")) " Test leadmultispace only normal ggdG set listchars& set listchars=leadmultispace:.-+* set list call append(0, [ \ ' ffff ', \ ' i i gg', \ ' h ', \ ' j ', \ ' 0 0 ', \ ]) let expected = [ \ '.-+*ffff ', \ '.-i i gg', \ ' h ', \ '.-+*.-+*.-j ', \ '.-+*0 0 ', \ ' ' \ ] call assert_equal('leadmultispace:.-+*', &listchars) call Check_listchars(expected, 5, 12) call assert_equal(expected, split(execute("%list"), "\n")) " Changing the value of 'ambiwidth' twice shouldn't cause double-free when " "leadmultispace" is specified. set ambiwidth=double set ambiwidth& " Test leadmultispace and lead and space normal ggdG set listchars& set listchars+=lead:<,space:- set listchars+=leadmultispace:.-+* set list call append(0, [ \ ' ffff ', \ ' i i gg', \ ' h ', \ ' j ', \ ' 0 0 ', \ ]) let expected = [ \ '.-+*ffff----$', \ '.-i-i-----gg$', \ '<h----------$', \ '.-+*.-+*.-j-$', \ '.-+*0--0----$', \ '$' \ ] call assert_equal('eol:$,lead:<,space:-,leadmultispace:.-+*', &listchars) call Check_listchars(expected, 6) call Check_listchars(expected, 5, -1, 1) call Check_listchars(expected, 5, -1, 2) call Check_listchars(expected, 5, -1, 3) call Check_listchars(expected, 5, -1, 6) call assert_equal(expected, split(execute("%list"), "\n")) " the last occurrence of 'multispace:' is used set listchars& set listchars+=multispace:yYzZ set listchars+=space:x,multispace:XyY let expected = [ \ 'XyYXffffXyYX$', \ 'XyixiXyYXygg$', \ 'xhXyYXyYXyYX$', \ 'XyYXyYXyYXjx$', \ 'XyYX0Xy0XyYX$', \ '$' \ ] call assert_equal('eol:$,multispace:yYzZ,space:x,multispace:XyY', &listchars) call Check_listchars(expected, 6) call Check_listchars(expected, 5, -1, 6) call assert_equal(expected, split(execute("%list"), "\n")) set listchars+=lead:>,trail:< let expected = [ \ '>>>>ffff<<<<$', \ '>>ixiXyYXygg$', \ '>h<<<<<<<<<<$', \ '>>>>>>>>>>j<$', \ '>>>>0Xy0<<<<$', \ '$' \ ] call Check_listchars(expected, 6) call Check_listchars(expected, 5, -1, 6) call assert_equal(expected, split(execute("%list"), "\n")) " removing 'multispace:' set listchars-=multispace:XyY set listchars-=multispace:yYzZ let expected = [ \ '>>>>ffff<<<<$', \ '>>ixixxxxxgg$', \ '>h<<<<<<<<<<$', \ '>>>>>>>>>>j<$', \ '>>>>0xx0<<<<$', \ '$' \ ] call Check_listchars(expected, 6) call Check_listchars(expected, 5, -1, 6) call assert_equal(expected, split(execute("%list"), "\n")) " test nbsp normal ggdG set listchars=nbsp:X,trail:Y set list " Non-breaking space let nbsp = nr2char(0xa0) call append(0, [ ">" .. nbsp .. "<" ]) let expected = '>X< ' call Check_listchars([expected], 1) set listchars=nbsp:X call Check_listchars([expected], 1) " test extends normal ggdG set listchars=extends:Z set nowrap set nolist call append(0, [ repeat('A', &columns + 1) ]) let expected = repeat('A', &columns) call Check_listchars([expected], 1, &columns) set list let expected = expected[:-2] . 'Z' call Check_listchars([expected], 1, &columns) enew! set listchars& ff& endfunc " Test that unicode listchars characters get properly inserted func Test_listchars_unicode() enew! let oldencoding=&encoding set encoding=utf-8 set ff=unix set listchars=eol:⇔,space:␣,multispace:≡≢≣,nbsp:≠,tab:←↔→ set list let nbsp = nr2char(0xa0) call append(0, [" a\tb c" .. nbsp .. "d "]) let expected = ['≡≢≣≡≢≣≡≢a←↔↔↔↔↔→b␣c≠d≡≢⇔'] call Check_listchars(expected, 1) call Check_listchars(expected, 1, -1, 3) call Check_listchars(expected, 1, -1, 13) set listchars=eol:\\u21d4,space:\\u2423,multispace:≡\\u2262\\U00002263,nbsp:\\U00002260,tab:←↔\\u2192 call Check_listchars(expected, 1) call Check_listchars(expected, 1, -1, 3) call Check_listchars(expected, 1, -1, 13) set listchars+=lead:⇨,trail:⇦ let expected = ['⇨⇨⇨⇨⇨⇨⇨⇨a←↔↔↔↔↔→b␣c≠d⇦⇦⇔'] call Check_listchars(expected, 1) call Check_listchars(expected, 1, -1, 3) call Check_listchars(expected, 1, -1, 13) let &encoding=oldencoding enew! set listchars& ff& endfunction func Test_listchars_invalid() enew! set ff=unix set listchars& set list set ambiwidth=double " No colon call assert_fails('set listchars=x', 'E474:') call assert_fails('set listchars=x', 'E474:') call assert_fails('set listchars=multispace', 'E474:') call assert_fails('set listchars=leadmultispace', 'E474:') " Too short call assert_fails('set listchars=space:', 'E1511:') call assert_fails('set listchars=tab:x', 'E1511:') call assert_fails('set listchars=multispace:', 'E1511:') call assert_fails('set listchars=leadmultispace:', 'E1511:') " One occurrence too short call assert_fails('set listchars=space:x,space:', 'E1511:') call assert_fails('set listchars=space:,space:x', 'E1511:') call assert_fails('set listchars=tab:xx,tab:x', 'E1511:') call assert_fails('set listchars=tab:x,tab:xx', 'E1511:') call assert_fails('set listchars=multispace:,multispace:x', 'E1511:') call assert_fails('set listchars=multispace:x,multispace:', 'E1511:') call assert_fails('set listchars=leadmultispace:,leadmultispace:x', 'E1511:') call assert_fails('set listchars=leadmultispace:x,leadmultispace:', 'E1511:') " Too long call assert_fails('set listchars=space:xx', 'E1511:') call assert_fails('set listchars=tab:xxxx', 'E1511:') " Has double-width character call assert_fails('set listchars=space:·', 'E1512:') call assert_fails('set listchars=tab:·x', 'E1512:') call assert_fails('set listchars=tab:x·', 'E1512:') call assert_fails('set listchars=tab:xx·', 'E1512:') call assert_fails('set listchars=multispace:·', 'E1512:') call assert_fails('set listchars=multispace:xxx·', 'E1512:') call assert_fails('set listchars=leadmultispace:·', 'E1512:') call assert_fails('set listchars=leadmultispace:xxx·', 'E1512:') " Has control character call assert_fails("set listchars=space:\x01", 'E1512:') call assert_fails("set listchars=tab:\x01x", 'E1512:') call assert_fails("set listchars=tab:x\x01", 'E1512:') call assert_fails("set listchars=tab:xx\x01", 'E1512:') call assert_fails("set listchars=multispace:\x01", 'E1512:') call assert_fails("set listchars=multispace:xxx\x01", 'E1512:') call assert_fails('set listchars=space:\\x01', 'E1512:') call assert_fails('set listchars=tab:\\x01x', 'E1512:') call assert_fails('set listchars=tab:x\\x01', 'E1512:') call assert_fails('set listchars=tab:xx\\x01', 'E1512:') call assert_fails('set listchars=multispace:\\x01', 'E1512:') call assert_fails('set listchars=multispace:xxx\\x01', 'E1512:') call assert_fails("set listchars=leadmultispace:\x01", 'E1512:') call assert_fails('set listchars=leadmultispace:\\x01', 'E1512:') call assert_fails("set listchars=leadmultispace:xxx\x01", 'E1512:') call assert_fails('set listchars=leadmultispace:xxx\\x01', 'E1512:') enew! set ambiwidth& listchars& ff& endfunction " Tests that space characters following composing character won't get replaced " by listchars. func Test_listchars_composing() enew! let oldencoding=&encoding set encoding=utf-8 set ff=unix set list set listchars=eol:$,space:_,nbsp:= let nbsp1 = nr2char(0xa0) let nbsp2 = nr2char(0x202f) call append(0, [ \ " \u3099\t \u309A" .. nbsp1 .. nbsp1 .. "\u0302" .. nbsp2 .. nbsp2 .. "\u0302", \ ]) let expected = [ \ "_ \u3099^I \u309A=" .. nbsp1 .. "\u0302=" .. nbsp2 .. "\u0302$" \ ] call Check_listchars(expected, 1) let &encoding=oldencoding enew! set listchars& ff& endfunction " Check for the value of the 'listchars' option func s:CheckListCharsValue(expected) call assert_equal(a:expected, &listchars) call assert_equal(a:expected, getwinvar(0, '&listchars')) endfunc " Test for using a window local value for 'listchars' func Test_listchars_window_local() %bw! set list listchars& new " set a local value for 'listchars' setlocal listchars=tab:+-,eol:# call s:CheckListCharsValue('tab:+-,eol:#') " When local value is reset, global value should be used setlocal listchars= call s:CheckListCharsValue('eol:$') " Use 'setlocal <' to copy global value setlocal listchars=space:.,extends:> setlocal listchars< call s:CheckListCharsValue('eol:$') " Use 'set <' to copy global value setlocal listchars=space:.,extends:> set listchars< call s:CheckListCharsValue('eol:$') " Changing global setting should not change the local setting setlocal listchars=space:.,extends:> setglobal listchars=tab:+-,eol:# call s:CheckListCharsValue('space:.,extends:>') " when split opening a new window, local value should be copied split call s:CheckListCharsValue('space:.,extends:>') " clearing local value in one window should not change the other window set listchars& call s:CheckListCharsValue('eol:$') close call s:CheckListCharsValue('space:.,extends:>') " use different values for 'listchars' items in two different windows call setline(1, ["\t one two "]) setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:# split setlocal listchars=tab:[.],lead:#,space:_,trail:.,eol:& split set listchars=tab:+-+,lead:^,space:>,trail:<,eol:% call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$'))) close call assert_equal(['[......]##one__two..&'], ScreenLines(1, virtcol('$'))) close call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$'))) " changing the global setting should not change the local value setglobal listchars=tab:[.],lead:#,space:_,trail:.,eol:& call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$'))) set listchars< call assert_equal(['[......]##one__two..&'], ScreenLines(1, virtcol('$'))) " Using setglobal in a window with local setting should not affect the " window. But should impact other windows using the global setting. enew! | only call setline(1, ["\t one two "]) set listchars=tab:[.],lead:#,space:_,trail:.,eol:& split setlocal listchars=tab:+-+,lead:^,space:>,trail:<,eol:% split setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:# setglobal listchars=tab:{.},lead:-,space:=,trail:#,eol:$ call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$'))) close call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$'))) close call assert_equal(['{......}--one==two##$'], ScreenLines(1, virtcol('$'))) " Setting the global setting to the default value should not impact a window " using a local setting. split setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:# setglobal listchars&vim call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$'))) close call assert_equal(['^I one two $'], ScreenLines(1, virtcol('$'))) " Setting the local setting to the default value should not impact a window " using a global setting. set listchars=tab:{.},lead:-,space:=,trail:#,eol:$ split setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:# call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$'))) setlocal listchars&vim call assert_equal(['^I one two $'], ScreenLines(1, virtcol('$'))) close call assert_equal(['{......}--one==two##$'], ScreenLines(1, virtcol('$'))) " Using set in a window with a local setting should change it to use the " global setting and also impact other windows using the global setting. split setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:# call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$'))) set listchars=tab:+-+,lead:^,space:>,trail:<,eol:% call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$'))) close call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$'))) " Setting invalid value for a local setting should not impact the local and " global settings. split setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:# let cmd = 'setlocal listchars=tab:{.},lead:-,space:=,trail:#,eol:$,x' call assert_fails(cmd, 'E474:') call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$'))) close call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$'))) " Setting invalid value for a global setting should not impact the local and " global settings. split setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:# let cmd = 'setglobal listchars=tab:{.},lead:-,space:=,trail:#,eol:$,x' call assert_fails(cmd, 'E474:') call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$'))) close call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$'))) " Closing window with local lcs-multispace should not cause a memory leak. setlocal listchars=multispace:---+ split call s:CheckListCharsValue('multispace:---+') close %bw! set list& listchars& endfunc func Test_listchars_foldcolumn() CheckScreendump let lines =<< trim END call setline(1, ['aaa', '', 'a', 'aaaaaa']) vsplit vsplit windo set signcolumn=yes foldcolumn=1 winminwidth=0 nowrap list listchars=extends:>,precedes:< END call writefile(lines, 'XTest_listchars', 'D') let buf = RunVimInTerminal('-S XTest_listchars', {'rows': 10, 'cols': 60}) call term_sendkeys(buf, "13\<C-W>>") call VerifyScreenDump(buf, 'Test_listchars_01', {}) call term_sendkeys(buf, "\<C-W>>") call VerifyScreenDump(buf, 'Test_listchars_02', {}) call term_sendkeys(buf, "\<C-W>>") call VerifyScreenDump(buf, 'Test_listchars_03', {}) call term_sendkeys(buf, "\<C-W>>") call VerifyScreenDump(buf, 'Test_listchars_04', {}) call term_sendkeys(buf, "\<C-W>>") call VerifyScreenDump(buf, 'Test_listchars_05', {}) call term_sendkeys(buf, "\<C-W>h") call term_sendkeys(buf, ":set nowrap foldcolumn=4\<CR>") call term_sendkeys(buf, "15\<C-W><") call VerifyScreenDump(buf, 'Test_listchars_06', {}) call term_sendkeys(buf, "4\<C-W><") call VerifyScreenDump(buf, 'Test_listchars_07', {}) " clean up call StopVimInTerminal(buf) endfunc " vim: shiftwidth=2 sts=2 expandtab