Files
neovim/test/functional/ui/syntax_conceal_spec.lua
bfredl e61228a214 fix(tests): needing two calls to setup a screen is cringe
Before calling "attach" a screen object is just a dummy container for
(row, col) values whose purpose is to be sent as part of the "attach"
function call anyway.

Just create the screen in an attached state directly. Keep the complete
(row, col, options) config together. It is still completely valid to
later detach and re-attach as needed, including to another session.
2024-11-14 12:40:57 +01:00

860 lines
35 KiB
Lua

local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
local clear, feed, command = n.clear, n.feed, n.command
local eq = t.eq
local insert = n.insert
local poke_eventloop = n.poke_eventloop
local exec = n.exec
describe('Screen', function()
local screen
before_each(function()
clear()
screen = Screen.new(nil, 10)
screen:set_default_attr_ids({
[0] = { bold = true, foreground = Screen.colors.Blue },
[1] = { foreground = Screen.colors.LightGrey, background = Screen.colors.DarkGray },
[2] = { bold = true, reverse = true },
[3] = { reverse = true },
[4] = { bold = true },
[5] = { background = Screen.colors.Yellow },
[6] = { foreground = Screen.colors.Black, background = Screen.colors.LightGrey },
})
end)
describe('match and conceal', function()
before_each(function()
command('let &conceallevel=1')
end)
describe('multiple', function()
before_each(function()
insert([[
&&
&&
&&
&&
&&
&&
]])
command("syn match dAmpersand '[&][&]' conceal cchar=∧")
end)
it('double characters.', function()
screen:expect([[
{1:∧} |*6
^ |
{0:~ }|*2
|
]])
end)
it('double characters and move the cursor one line up.', function()
feed('k')
screen:expect([[
{1:∧} |*5
^&& |
|
{0:~ }|*2
|
]])
end)
it('double characters and move the cursor to the beginning of the file.', function()
feed('gg')
screen:expect([[
^&& |
{1:∧} |*5
|
{0:~ }|*2
|
]])
end)
it('double characters and move the cursor to the second line in the file.', function()
feed('ggj')
screen:expect([[
{1:∧} |
^&& |
{1:∧} |*4
|
{0:~ }|*2
|
]])
end)
it(
'double characters and then move the cursor to the beginning of the file and back to the end of the file.',
function()
feed('ggG')
screen:expect([[
{1:∧} |*6
^ |
{0:~ }|*2
|
]])
end
)
end) -- multiple
it('keyword instances in initially in the document.', function()
feed('2ilambda<cr><ESC>')
command('let &conceallevel=1')
command('syn keyword kLambda lambda conceal cchar=λ')
screen:expect([[
{1:λ} |*2
^ |
{0:~ }|*6
|
]])
end) -- Keyword
describe('regions in the document', function()
before_each(function()
feed('2')
insert('<r> a region of text </r>\n')
command('let &conceallevel=1')
end)
it('initially and conceal it.', function()
command("syn region rText start='<r>' end='</r>' conceal cchar=R")
screen:expect([[
{1:R} |*2
^ |
{0:~ }|*6
|
]])
end)
it('initially and conceal its start tag and end tag.', function()
-- concealends has a known bug (todo.txt) where the first match won't
-- be replaced with cchar.
command("syn region rText matchgroup=rMatch start='<r>' end='</r>' concealends cchar=-")
screen:expect([[
{1: } a region of text {1:-} |*2
^ |
{0:~ }|*6
|
]])
end)
it("that are nested and conceal the nested region's start and end tags.", function()
command(
"syn region rText contains=rText matchgroup=rMatch start='<r>' end='</r>' concealends cchar=-"
)
insert('<r> A region with <r> a nested <r> nested region.</r> </r> </r>\n')
screen:expect([[
{1: } a region of text {1:-} |*2
{1: } A region with {1: } a nested {1: } nested region.{1:-} |
{1:-} {1:-} |
^ |
{0:~ }|*4
|
]])
end)
end) -- regions in the document
describe('a region of text', function()
before_each(function()
command('syntax conceal on')
feed('2')
insert('<r> a region of text </r>\n')
command("syn region rText start='<r>' end='</r>' cchar=-")
end)
it('and turn on implicit concealing', function()
screen:expect([[
{1:-} |*2
^ |
{0:~ }|*6
|
]])
end)
it('and then turn on, then off, and then back on implicit concealing.', function()
command('syntax conceal off')
feed('2')
insert('<i> italian text </i>\n')
command("syn region iText start='<i>' end='</i>' cchar=*")
screen:expect([[
{1:-} |*2
<i> italian text </i> |*2
^ |
{0:~ }|*4
|
]])
command('syntax conceal on')
command("syn region iText start='<i>' end='</i>' cchar=*")
screen:expect([[
{1:-} |*2
{1:*} |*2
^ |
{0:~ }|*4
|
]])
end)
end) -- a region of text (implicit concealing)
it('cursor position is correct when entering Insert mode with cocu=ni #13916', function()
insert([[foobarfoobarfoobar]])
-- move to end of line
feed('$')
command('set concealcursor=ni')
command('syn match Foo /foobar/ conceal cchar=&')
screen:expect([[
{1:&&&}^ |
{0:~ }|*8
|
]])
feed('i')
-- cursor should stay in place, not jump to column 16
screen:expect([[
{1:&&&}^ |
{0:~ }|*8
{4:-- INSERT --} |
]])
end)
end) -- match and conceal
describe('let the conceal level be', function()
before_each(function()
insert('// No Conceal\n')
insert('"Conceal without a cchar"\n')
insert('+ With cchar\n\n')
command("syn match noConceal '^//.*$'")
command('syn match concealNoCchar \'".\\{-}"$\' conceal')
command("syn match concealWCchar '^+.\\{-}$' conceal cchar=C")
end)
it('0. No concealing.', function()
command('let &conceallevel=0')
screen:expect([[
// No Conceal |
"Conceal without a cchar" |
+ With cchar |
|
^ |
{0:~ }|*4
|
]])
end)
it('1. Conceal using cchar or reference listchars.', function()
command('let &conceallevel=1')
screen:expect([[
// No Conceal |
{1: } |
{1:C} |
|
^ |
{0:~ }|*4
|
]])
end)
it('2. Hidden unless cchar is set.', function()
command('let &conceallevel=2')
screen:expect([[
// No Conceal |
|
{1:C} |
|
^ |
{0:~ }|*4
|
]])
end)
it('3. Hide all concealed text.', function()
command('let &conceallevel=3')
screen:expect([[
// No Conceal |
|*3
^ |
{0:~ }|*4
|
]])
end)
end) -- conceallevel
describe('cursor movement', function()
before_each(function()
command('syn keyword concealy barf conceal cchar=b')
command('set cole=2')
feed('5Ofoo barf bar barf eggs<esc>')
screen:expect([[
foo {1:b} bar {1:b} eggs |*4
foo barf bar barf egg^s |
|
{0:~ }|*3
|
]])
end)
it('between windows', function()
feed('k')
command('split')
screen:expect([[
foo {1:b} bar {1:b} eggs |
foo barf bar barf egg^s |
foo {1:b} bar {1:b} eggs |
|
{2:[No Name] [+] }|
foo {1:b} bar {1:b} eggs |*3
{3:[No Name] [+] }|
|
]])
feed('<c-w>w')
screen:expect([[
foo {1:b} bar {1:b} eggs |*3
|
{3:[No Name] [+] }|
foo {1:b} bar {1:b} eggs |
foo barf bar barf egg^s |
foo {1:b} bar {1:b} eggs |
{2:[No Name] [+] }|
|
]])
end)
it('in insert mode', function()
feed('i')
screen:expect([[
foo {1:b} bar {1:b} eggs |*4
foo barf bar barf egg^s |
|
{0:~ }|*3
{4:-- INSERT --} |
]])
feed('<up>')
screen:expect([[
foo {1:b} bar {1:b} eggs |*3
foo barf bar barf egg^s |
foo {1:b} bar {1:b} eggs |
|
{0:~ }|*3
{4:-- INSERT --} |
]])
end)
it('between modes cocu=iv', function()
command('set cocu=iv')
feed('gg')
screen:expect([[
^foo barf bar barf eggs |
foo {1:b} bar {1:b} eggs |*4
|
{0:~ }|*3
|
]])
feed('i')
screen:expect([[
^foo {1:b} bar {1:b} eggs |
foo {1:b} bar {1:b} eggs |*4
|
{0:~ }|*3
{4:-- INSERT --} |
]])
feed('<esc>')
screen:expect([[
^foo barf bar barf eggs |
foo {1:b} bar {1:b} eggs |*4
|
{0:~ }|*3
|
]])
feed('v')
screen:expect([[
^foo {1:b} bar {1:b} eggs |
foo {1:b} bar {1:b} eggs |*4
|
{0:~ }|*3
{4:-- VISUAL --} |
]])
feed('<esc>')
screen:expect([[
^foo barf bar barf eggs |
foo {1:b} bar {1:b} eggs |*4
|
{0:~ }|*3
|
]])
end)
it('between modes cocu=n', function()
command('set cocu=n')
feed('gg')
screen:expect([[
^foo {1:b} bar {1:b} eggs |
foo {1:b} bar {1:b} eggs |*4
|
{0:~ }|*3
|
]])
feed('i')
screen:expect([[
^foo barf bar barf eggs |
foo {1:b} bar {1:b} eggs |*4
|
{0:~ }|*3
{4:-- INSERT --} |
]])
feed('<esc>')
screen:expect([[
^foo {1:b} bar {1:b} eggs |
foo {1:b} bar {1:b} eggs |*4
|
{0:~ }|*3
|
]])
feed('v')
screen:expect([[
^foo barf bar barf eggs |
foo {1:b} bar {1:b} eggs |*4
|
{0:~ }|*3
{4:-- VISUAL --} |
]])
feed('<esc>')
screen:expect([[
^foo {1:b} bar {1:b} eggs |
foo {1:b} bar {1:b} eggs |*4
|
{0:~ }|*3
|
]])
feed('r')
screen:expect_unchanged()
feed('m')
screen:expect([[
^moo {1:b} bar {1:b} eggs |
foo {1:b} bar {1:b} eggs |*4
|
{0:~ }|*3
|
]])
end)
it('and open line', function()
feed('o')
screen:expect([[
foo {1:b} bar {1:b} eggs |*5
^ |
|
{0:~ }|*2
{4:-- INSERT --} |
]])
end)
it('and open line cocu=i', function()
command('set cocu=i')
feed('o')
screen:expect([[
foo {1:b} bar {1:b} eggs |*5
^ |
|
{0:~ }|*2
{4:-- INSERT --} |
]])
end)
describe('with incsearch', function()
before_each(function()
command('set incsearch hlsearch')
feed('2GA x<esc>3GA xy<esc>gg')
screen:expect([[
^foo barf bar barf eggs |
foo {1:b} bar {1:b} eggs x |
foo {1:b} bar {1:b} eggs xy |
foo {1:b} bar {1:b} eggs |*2
|
{0:~ }|*3
|
]])
end)
it('cocu=', function()
feed('/')
screen:expect([[
foo barf bar barf eggs |
foo {1:b} bar {1:b} eggs x |
foo {1:b} bar {1:b} eggs xy |
foo {1:b} bar {1:b} eggs |*2
|
{0:~ }|*3
/^ |
]])
feed('x')
screen:expect([[
foo {1:b} bar {1:b} eggs |
foo barf bar barf eggs {3:x} |
foo {1:b} bar {1:b} eggs {5:x}y |
foo {1:b} bar {1:b} eggs |*2
|
{0:~ }|*3
/x^ |
]])
feed('y')
screen:expect([[
foo {1:b} bar {1:b} eggs |
foo {1:b} bar {1:b} eggs x |
foo barf bar barf eggs {3:xy} |
foo {1:b} bar {1:b} eggs |*2
|
{0:~ }|*3
/xy^ |
]])
feed('<c-w>')
screen:expect([[
foo barf bar barf eggs |
foo {1:b} bar {1:b} eggs x |
foo {1:b} bar {1:b} eggs xy |
foo {1:b} bar {1:b} eggs |*2
|
{0:~ }|*3
/^ |
]])
end)
it('cocu=c', function()
command('set cocu=c')
feed('/')
-- NB: we don't do this redraw. Probably best to still skip it,
-- to avoid annoying distraction from the cmdline
screen:expect([[
foo barf bar barf eggs |
foo {1:b} bar {1:b} eggs x |
foo {1:b} bar {1:b} eggs xy |
foo {1:b} bar {1:b} eggs |*2
|
{0:~ }|*3
/^ |
]])
feed('x')
screen:expect([[
foo {1:b} bar {1:b} eggs |
foo {1:b} bar {1:b} eggs {3:x} |
foo {1:b} bar {1:b} eggs {5:x}y |
foo {1:b} bar {1:b} eggs |*2
|
{0:~ }|*3
/x^ |
]])
feed('y')
screen:expect([[
foo {1:b} bar {1:b} eggs |
foo {1:b} bar {1:b} eggs x |
foo {1:b} bar {1:b} eggs {3:xy} |
foo {1:b} bar {1:b} eggs |*2
|
{0:~ }|*3
/xy^ |
]])
feed('<c-w>')
screen:expect([[
foo {1:b} bar {1:b} eggs |
foo {1:b} bar {1:b} eggs x |
foo {1:b} bar {1:b} eggs xy |
foo {1:b} bar {1:b} eggs |*2
|
{0:~ }|*3
/^ |
]])
feed('<esc>')
screen:expect([[
^foo barf bar barf eggs |
foo {1:b} bar {1:b} eggs x |
foo {1:b} bar {1:b} eggs xy |
foo {1:b} bar {1:b} eggs |*2
|
{0:~ }|*3
|
]])
end)
it('cocu=n', function()
command('set cocu=n')
screen:expect([[
^foo {1:b} bar {1:b} eggs |
foo {1:b} bar {1:b} eggs x |
foo {1:b} bar {1:b} eggs xy |
foo {1:b} bar {1:b} eggs |*2
|
{0:~ }|*3
|
]])
feed('/')
-- NB: we don't do this redraw. Probably best to still skip it,
-- to avoid annoying distraction from the cmdline
screen:expect([[
foo {1:b} bar {1:b} eggs |
foo {1:b} bar {1:b} eggs x |
foo {1:b} bar {1:b} eggs xy |
foo {1:b} bar {1:b} eggs |*2
|
{0:~ }|*3
/^ |
]])
feed('x')
screen:expect([[
foo {1:b} bar {1:b} eggs |
foo barf bar barf eggs {3:x} |
foo {1:b} bar {1:b} eggs {5:x}y |
foo {1:b} bar {1:b} eggs |*2
|
{0:~ }|*3
/x^ |
]])
feed('<c-w>')
screen:expect([[
foo barf bar barf eggs |
foo {1:b} bar {1:b} eggs x |
foo {1:b} bar {1:b} eggs xy |
foo {1:b} bar {1:b} eggs |*2
|
{0:~ }|*3
/^ |
]])
feed('<esc>')
screen:expect([[
^foo {1:b} bar {1:b} eggs |
foo {1:b} bar {1:b} eggs x |
foo {1:b} bar {1:b} eggs xy |
foo {1:b} bar {1:b} eggs |*2
|
{0:~ }|*3
|
]])
end)
end)
it('redraws properly with concealcursor in visual mode', function()
command('set concealcursor=v conceallevel=2')
feed('10Ofoo barf bar barf eggs<esc>')
feed(':3<cr>o a<Esc>ggV')
screen:expect {
grid = [[
^f{6:oo }{1:b}{6: bar }{1:b}{6: eggs} |
foo {1:b} bar {1:b} eggs |*2
a |
foo {1:b} bar {1:b} eggs |*5
{4:-- VISUAL LINE --} |
]],
}
feed(string.rep('j', 15))
screen:expect {
grid = [[
{6:foo }{1:b}{6: bar }{1:b}{6: eggs} |*8
^f{6:oo }{1:b}{6: bar }{1:b}{6: eggs} |
{4:-- VISUAL LINE --} |
]],
}
feed(string.rep('k', 15))
screen:expect {
grid = [[
^f{6:oo }{1:b}{6: bar }{1:b}{6: eggs} |
foo {1:b} bar {1:b} eggs |*2
a |
foo {1:b} bar {1:b} eggs |*5
{4:-- VISUAL LINE --} |
]],
}
end)
end)
it('redraws not too much with conceallevel=1', function()
command('set conceallevel=1')
command('set redrawdebug+=nodelta')
insert([[
aaa
bbb
ccc
]])
screen:expect {
grid = [[
aaa |
bbb |
ccc |
^ |
{0:~ }|*5
|
]],
}
-- XXX: hack to get notifications, and check only a single line is
-- updated. Could use next_msg() also.
local orig_handle_grid_line = screen._handle_grid_line
local grid_lines = {}
function screen._handle_grid_line(self, grid, row, col, items)
table.insert(grid_lines, { row, col, items })
orig_handle_grid_line(self, grid, row, col, items)
end
feed('k')
screen:expect {
grid = [[
aaa |
bbb |
^ccc |
|
{0:~ }|*5
|
]],
}
eq({ { 2, 0, { { 'c', 0, 3 }, { ' ', 0, 50 } } }, { 3, 0, { { ' ', 0, 53 } } } }, grid_lines)
end)
it('K_EVENT should not cause extra redraws with concealcursor #13196', function()
command('set conceallevel=1')
command('set concealcursor=nv')
command('set redrawdebug+=nodelta')
insert([[
aaa
bbb
ccc
]])
screen:expect {
grid = [[
aaa |
bbb |
ccc |
^ |
{0:~ }|*5
|
]],
}
-- XXX: hack to get notifications, and check only a single line is
-- updated. Could use next_msg() also.
local orig_handle_grid_line = screen._handle_grid_line
local grid_lines = {}
function screen._handle_grid_line(self, grid, row, col, items)
table.insert(grid_lines, { row, col, items })
orig_handle_grid_line(self, grid, row, col, items)
end
feed('k')
screen:expect {
grid = [[
aaa |
bbb |
^ccc |
|
{0:~ }|*5
|
]],
}
eq({ { 2, 0, { { 'c', 0, 3 }, { ' ', 0, 50 } } } }, grid_lines)
grid_lines = {}
poke_eventloop() -- causes K_EVENT key
screen:expect_unchanged()
eq({}, grid_lines) -- no redraw was done
end)
describe('concealed line has the correct cursor column', function()
-- oldtest: Test_cursor_column_in_concealed_line_after_window_scroll()
it('after window scroll', function()
insert([[
3split
let m = matchadd('Conceal', '=')
setl conceallevel=2 concealcursor=nc
normal gg
"==expr==]])
feed('gg')
command('file Xcolesearch')
command('set nomodified')
command('so')
screen:expect {
grid = [[
^3split |
let m matchadd('Conceal', '') |
setl conceallevel2 concealcursornc |
{2:Xcolesearch }|
3split |
let m = matchadd('Conceal', '=') |
setl conceallevel=2 concealcursor=nc |
normal gg |
{3:Xcolesearch }|
|
]],
}
-- Jump to something that is beyond the bottom of the window,
-- so there's a scroll down.
feed('/expr<CR>')
-- Are the concealed parts of the current line really hidden?
-- Is the window's cursor column properly updated for hidden
-- parts of the current line?
screen:expect {
grid = [[
setl conceallevel2 concealcursornc |
normal gg |
"{5:^expr} |
{2:Xcolesearch }|
3split |
let m = matchadd('Conceal', '=') |
setl conceallevel=2 concealcursor=nc |
normal gg |
{3:Xcolesearch }|
/expr |
]],
}
end)
-- oldtest: Test_cursor_column_in_concealed_line_after_leftcol_change()
it('after leftcol change', function()
exec([[
0put = 'ab' .. repeat('-', &columns) .. 'c'
call matchadd('Conceal', '-')
set nowrap ss=0 cole=3 cocu=n
]])
-- Go to the end of the line (3 columns beyond the end of the screen).
-- Horizontal scroll would center the cursor in the screen line, but conceal
-- makes it go to screen column 1.
feed('$')
-- Are the concealed parts of the current line really hidden?
-- Is the window's cursor column properly updated for conceal?
screen:expect {
grid = [[
^c |
|
{0:~ }|*7
|
]],
}
end)
end)
end)