mirror of
https://github.com/neovim/neovim.git
synced 2025-10-17 07:16:09 +00:00

We do not need an enum to keep track of what place in win_line() we currently are at. We already have a variable which keeps track where in the code we currently are (and thus what part of the line we are currently rendering), it is called the _program counter_. When we need non-linear or self-referential control-flow anyway for a laugh, we have a mechanism for that, it is called _function calls_. Do not "save" and "restore" the wlv->n_extra state every time the columns are to be drawn. This sort of thing needs to go away. Instead of setting the n_extra variables and then going to the outer while loop, the text in the columns can be rendered by just simply putting the text into the cells of the screen line, right away. Even in nvim this can be tricky sometimes, luckily we can use function calls to abstract this logic, which means that this handy data structure called the _call stack_ is handling saving away state temporarily, and restoring it back when we need it again. Lastly, but not least, as we now have direct control how signs are rendered, these can be stored as schar_T[2] and be directly put on screen as such.
517 lines
25 KiB
Lua
517 lines
25 KiB
Lua
local helpers = require('test.functional.helpers')(after_each)
|
|
local Screen = require('test.functional.ui.screen')
|
|
local clear, feed, command = helpers.clear, helpers.feed, helpers.command
|
|
local source = helpers.source
|
|
local meths = helpers.meths
|
|
|
|
describe('Signs', function()
|
|
local screen
|
|
|
|
before_each(function()
|
|
clear()
|
|
screen = Screen.new()
|
|
screen:attach()
|
|
screen:set_default_attr_ids( {
|
|
[0] = {bold=true, foreground=255},
|
|
[1] = {background = Screen.colors.Yellow},
|
|
[2] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.Grey},
|
|
[3] = {background = Screen.colors.Gray90},
|
|
[4] = {bold = true, reverse = true},
|
|
[5] = {reverse = true},
|
|
[6] = {foreground = Screen.colors.Brown},
|
|
[7] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey},
|
|
[8] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
|
|
[9] = {bold = true, foreground = Screen.colors.Magenta},
|
|
[10] = {foreground = Screen.colors.Blue1},
|
|
[11] = {bold = true, foreground = Screen.colors.SeaGreen4},
|
|
} )
|
|
end)
|
|
|
|
describe(':sign place', function()
|
|
it('allows signs with combining characters', function()
|
|
feed('ia<cr>b<cr><esc>')
|
|
command('sign define piet1 text=𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄ texthl=Search')
|
|
command('sign define piet2 text=𠜎̀́̂̃̄̅ texthl=Search')
|
|
command('sign place 1 line=1 name=piet1 buffer=1')
|
|
command('sign place 2 line=2 name=piet2 buffer=1')
|
|
screen:expect([[
|
|
{1:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}a |
|
|
{1:𠜎̀́̂̃̄̅}b |
|
|
{2: }^ |
|
|
{0:~ }|*10
|
|
|
|
|
]])
|
|
end)
|
|
|
|
it('shadows previously placed signs', function()
|
|
feed('ia<cr>b<cr>c<cr><esc>')
|
|
command('sign define piet text=>> texthl=Search')
|
|
command('sign define pietx text=>! texthl=Search')
|
|
command('sign place 1 line=1 name=piet buffer=1')
|
|
command('sign place 2 line=3 name=piet buffer=1')
|
|
command('sign place 3 line=1 name=pietx buffer=1')
|
|
screen:expect([[
|
|
{1:>!}a |
|
|
{2: }b |
|
|
{1:>>}c |
|
|
{2: }^ |
|
|
{0:~ }|*9
|
|
|
|
|
]])
|
|
end)
|
|
|
|
it('allows signs with no text', function()
|
|
feed('ia<cr>b<cr><esc>')
|
|
command('sign define piet1 text= texthl=Search')
|
|
command('sign place 1 line=1 name=piet1 buffer=1')
|
|
screen:expect([[
|
|
a |
|
|
b |
|
|
^ |
|
|
{0:~ }|*10
|
|
|
|
|
]])
|
|
end)
|
|
|
|
it('can be called right after :split', function()
|
|
feed('ia<cr>b<cr>c<cr><esc>gg')
|
|
-- This used to cause a crash due to :sign using a special redraw
|
|
-- (not updating nvim's specific highlight data structures)
|
|
-- without proper redraw first, as split just flags for redraw later.
|
|
source([[
|
|
set cursorline
|
|
sign define piet text=>> texthl=Search
|
|
split
|
|
sign place 3 line=2 name=piet buffer=1
|
|
]])
|
|
screen:expect([[
|
|
{2: }{3:^a }|
|
|
{1:>>}b |
|
|
{2: }c |
|
|
{2: } |
|
|
{0:~ }|*2
|
|
{4:[No Name] [+] }|
|
|
{2: }{3:a }|
|
|
{1:>>}b |
|
|
{2: }c |
|
|
{2: } |
|
|
{0:~ }|
|
|
{5:[No Name] [+] }|
|
|
|
|
|
]])
|
|
end)
|
|
|
|
it('can combine text, linehl and numhl', function()
|
|
feed('ia<cr>b<cr>c<cr><esc>')
|
|
command('set number')
|
|
command('sign define piet text=>> texthl=Search')
|
|
command('sign define pietx linehl=ErrorMsg')
|
|
command('sign define pietxx numhl=Folded')
|
|
command('sign place 1 line=1 name=piet buffer=1')
|
|
command('sign place 2 line=2 name=pietx buffer=1')
|
|
command('sign place 3 line=3 name=pietxx buffer=1')
|
|
command('sign place 4 line=4 name=piet buffer=1')
|
|
command('sign place 5 line=4 name=pietx buffer=1')
|
|
command('sign place 6 line=4 name=pietxx buffer=1')
|
|
screen:expect([[
|
|
{1:>>}{6: 1 }a |
|
|
{2: }{6: 2 }{8:b }|
|
|
{2: }{7: 3 }c |
|
|
{1:>>}{7: 4 }{8:^ }|
|
|
{0:~ }|*9
|
|
|
|
|
]])
|
|
-- Check that 'statuscolumn' correctly applies numhl
|
|
command('set statuscolumn=%s%=%l\\ ')
|
|
screen:expect_unchanged()
|
|
end)
|
|
|
|
it('highlights the cursorline sign with culhl', function()
|
|
feed('ia<cr>b<cr>c<esc>')
|
|
command('sign define piet text=>> texthl=Search culhl=ErrorMsg')
|
|
command('sign place 1 line=1 name=piet buffer=1')
|
|
command('sign place 2 line=2 name=piet buffer=1')
|
|
command('sign place 3 line=3 name=piet buffer=1')
|
|
command('set cursorline')
|
|
screen:expect([[
|
|
{1:>>}a |
|
|
{1:>>}b |
|
|
{8:>>}{3:^c }|
|
|
{0:~ }|*10
|
|
|
|
|
]])
|
|
feed('k')
|
|
screen:expect([[
|
|
{1:>>}a |
|
|
{8:>>}{3:^b }|
|
|
{1:>>}c |
|
|
{0:~ }|*10
|
|
|
|
|
]])
|
|
command('set nocursorline')
|
|
screen:expect([[
|
|
{1:>>}a |
|
|
{1:>>}^b |
|
|
{1:>>}c |
|
|
{0:~ }|*10
|
|
|
|
|
]])
|
|
command('set cursorline cursorlineopt=line')
|
|
screen:expect([[
|
|
{1:>>}a |
|
|
{1:>>}{3:^b }|
|
|
{1:>>}c |
|
|
{0:~ }|*10
|
|
|
|
|
]])
|
|
command('set cursorlineopt=number')
|
|
command('hi! link SignColumn IncSearch')
|
|
feed('Go<esc>2G')
|
|
screen:expect([[
|
|
{1:>>}a |
|
|
{8:>>}^b |
|
|
{1:>>}c |
|
|
{5: } |
|
|
{0:~ }|*9
|
|
|
|
|
]])
|
|
-- Check that 'statuscolumn' cursorline/signcolumn highlights are the same (#21726)
|
|
command('set statuscolumn=%s')
|
|
screen:expect_unchanged()
|
|
end)
|
|
|
|
it('multiple signs #9295', function()
|
|
feed('ia<cr>b<cr>c<cr><esc>')
|
|
command('set number')
|
|
command('set signcolumn=yes:2')
|
|
command('sign define pietSearch text=>> texthl=Search')
|
|
command('sign define pietError text=XX texthl=Error')
|
|
command('sign define pietWarn text=WW texthl=Warning')
|
|
command('sign place 1 line=1 name=pietSearch buffer=1')
|
|
command('sign place 2 line=1 name=pietError buffer=1')
|
|
-- Line 2 helps checking that signs in the same line are ordered by Id.
|
|
command('sign place 4 line=2 name=pietSearch buffer=1')
|
|
command('sign place 3 line=2 name=pietError buffer=1')
|
|
-- Line 3 checks that with a limit over the maximum number
|
|
-- of signs, the ones with the highest Ids are being picked,
|
|
-- and presented by their sorted Id order.
|
|
command('sign place 6 line=3 name=pietSearch buffer=1')
|
|
command('sign place 7 line=3 name=pietWarn buffer=1')
|
|
command('sign place 5 line=3 name=pietError buffer=1')
|
|
screen:expect([[
|
|
{1:>>}{8:XX}{6: 1 }a |
|
|
{8:XX}{1:>>}{6: 2 }b |
|
|
{1:>>}WW{6: 3 }c |
|
|
{2: }{6: 4 }^ |
|
|
{0:~ }|*9
|
|
|
|
|
]])
|
|
-- With the default setting, we get the sign with the top id.
|
|
command('set signcolumn=yes:1')
|
|
screen:expect([[
|
|
{8:XX}{6: 1 }a |
|
|
{1:>>}{6: 2 }b |
|
|
WW{6: 3 }c |
|
|
{2: }{6: 4 }^ |
|
|
{0:~ }|*9
|
|
|
|
|
]])
|
|
-- "auto:3" accommodates all the signs we defined so far.
|
|
command('set signcolumn=auto:3')
|
|
local s3 = [[
|
|
{1:>>}{8:XX}{2: }{6: 1 }a |
|
|
{8:XX}{1:>>}{2: }{6: 2 }b |
|
|
{8:XX}{1:>>}WW{6: 3 }c |
|
|
{2: }{6: 4 }^ |
|
|
{0:~ }|*9
|
|
|
|
|
]]
|
|
screen:expect(s3)
|
|
-- Check "yes:9".
|
|
command('set signcolumn=yes:9')
|
|
screen:expect([[
|
|
{1:>>}{8:XX}{2: }{6: 1 }a |
|
|
{8:XX}{1:>>}{2: }{6: 2 }b |
|
|
{8:XX}{1:>>}WW{2: }{6: 3 }c |
|
|
{2: }{6: 4 }^ |
|
|
{0:~ }|*9
|
|
|
|
|
]])
|
|
-- Check "auto:N" larger than the maximum number of signs defined in
|
|
-- a single line (same result as "auto:3").
|
|
command('set signcolumn=auto:4')
|
|
screen:expect(s3)
|
|
-- line deletion deletes signs.
|
|
command('3move1')
|
|
command('2d')
|
|
screen:expect([[
|
|
{1:>>}{8:XX}{6: 1 }a |
|
|
{8:XX}{1:>>}{6: 2 }^b |
|
|
{2: }{6: 3 } |
|
|
{0:~ }|*10
|
|
|
|
|
]])
|
|
-- character deletion does not delete signs.
|
|
feed('x')
|
|
screen:expect([[
|
|
{1:>>}{8:XX}{6: 1 }a |
|
|
{8:XX}{1:>>}{6: 2 }^ |
|
|
{2: }{6: 3 } |
|
|
{0:~ }|*10
|
|
|
|
|
]])
|
|
end)
|
|
|
|
it('auto-resize sign column with minimum size (#13783)', function()
|
|
feed('ia<cr>b<cr>c<cr><esc>')
|
|
command('set number')
|
|
-- sign column should always accommodate at the minimum size
|
|
command('set signcolumn=auto:1-3')
|
|
screen:expect([[
|
|
{2: }{6: 1 }a |
|
|
{2: }{6: 2 }b |
|
|
{2: }{6: 3 }c |
|
|
{2: }{6: 4 }^ |
|
|
{0:~ }|*9
|
|
|
|
|
]])
|
|
-- should support up to 8 signs at minimum
|
|
command('set signcolumn=auto:8-9')
|
|
screen:expect([[
|
|
{2: }{6: 1 }a |
|
|
{2: }{6: 2 }b |
|
|
{2: }{6: 3 }c |
|
|
{2: }{6: 4 }^ |
|
|
{0:~ }|*9
|
|
|
|
|
]])
|
|
-- should keep the same sign size when signs are not exceeding
|
|
-- the minimum
|
|
command('set signcolumn=auto:2-5')
|
|
command('sign define pietSearch text=>> texthl=Search')
|
|
command('sign place 1 line=1 name=pietSearch buffer=1')
|
|
screen:expect([[
|
|
{1:>>}{2: }{6: 1 }a |
|
|
{2: }{6: 2 }b |
|
|
{2: }{6: 3 }c |
|
|
{2: }{6: 4 }^ |
|
|
{0:~ }|*9
|
|
|
|
|
]])
|
|
-- should resize itself when signs are exceeding minimum but
|
|
-- not over the maximum
|
|
command('sign place 2 line=1 name=pietSearch buffer=1')
|
|
command('sign place 3 line=1 name=pietSearch buffer=1')
|
|
command('sign place 4 line=1 name=pietSearch buffer=1')
|
|
screen:expect([[
|
|
{1:>>>>>>>>}{6: 1 }a |
|
|
{2: }{6: 2 }b |
|
|
{2: }{6: 3 }c |
|
|
{2: }{6: 4 }^ |
|
|
{0:~ }|*9
|
|
|
|
|
]])
|
|
-- should not increase size because sign with existing id is moved
|
|
command('sign place 4 line=1 name=pietSearch buffer=1')
|
|
screen:expect_unchanged()
|
|
command('sign unplace 4')
|
|
screen:expect([[
|
|
{1:>>>>>>}{6: 1 }a |
|
|
{2: }{6: 2 }b |
|
|
{2: }{6: 3 }c |
|
|
{2: }{6: 4 }^ |
|
|
{0:~ }|*9
|
|
|
|
|
]])
|
|
command('sign place 4 line=1 name=pietSearch buffer=1')
|
|
-- should keep the column at maximum size when signs are
|
|
-- exceeding the maximum
|
|
command('sign place 5 line=1 name=pietSearch buffer=1')
|
|
command('sign place 6 line=1 name=pietSearch buffer=1')
|
|
command('sign place 7 line=1 name=pietSearch buffer=1')
|
|
command('sign place 8 line=1 name=pietSearch buffer=1')
|
|
screen:expect([[
|
|
{1:>>>>>>>>>>}{6: 1 }a |
|
|
{2: }{6: 2 }b |
|
|
{2: }{6: 3 }c |
|
|
{2: }{6: 4 }^ |
|
|
{0:~ }|*9
|
|
|
|
|
]])
|
|
end)
|
|
|
|
it('ignores signs with no icon and text when calculating the signcolumn width', function()
|
|
feed('ia<cr>b<cr>c<cr><esc>')
|
|
command('set number')
|
|
command('set signcolumn=auto:2')
|
|
command('sign define pietSearch text=>> texthl=Search')
|
|
command('sign define pietError text= texthl=Error')
|
|
command('sign place 2 line=1 name=pietError buffer=1')
|
|
-- no signcolumn with only empty sign
|
|
screen:expect([[
|
|
{6: 1 }a |
|
|
{6: 2 }b |
|
|
{6: 3 }c |
|
|
{6: 4 }^ |
|
|
{0:~ }|*9
|
|
|
|
|
]])
|
|
-- single column with 1 sign with text and one sign without
|
|
command('sign place 1 line=1 name=pietSearch buffer=1')
|
|
screen:expect([[
|
|
{1:>>}{6: 1 }a |
|
|
{2: }{6: 2 }b |
|
|
{2: }{6: 3 }c |
|
|
{2: }{6: 4 }^ |
|
|
{0:~ }|*9
|
|
|
|
|
]])
|
|
end)
|
|
|
|
it('signcolumn=number', function()
|
|
feed('ia<cr>b<cr>c<cr><esc>')
|
|
command('set number signcolumn=number')
|
|
command('sign define pietSearch text=>> texthl=Search numhl=Error')
|
|
command('sign define pietError text= texthl=Search numhl=Error')
|
|
command('sign place 1 line=1 name=pietSearch buffer=1')
|
|
command('sign place 2 line=2 name=pietError buffer=1')
|
|
-- line number should be drawn if sign has no text
|
|
-- no signcolumn, line number for "a" is Search, for "b" is Error, for "c" is LineNr
|
|
screen:expect([[
|
|
{1: >> }a |
|
|
{8: 2 }b |
|
|
{6: 3 }c |
|
|
{6: 4 }^ |
|
|
{0:~ }|*9
|
|
|
|
|
]])
|
|
-- number column on wrapped part of a line should be empty
|
|
feed('gg100aa<Esc>')
|
|
screen:expect([[
|
|
{1: >> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
|
|
{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
|
|
{8: }aa^a |
|
|
{8: 2 }b |
|
|
{6: 3 }c |
|
|
{6: 4 } |
|
|
{0:~ }|*7
|
|
|
|
|
]])
|
|
meths.buf_set_extmark(0, meths.create_namespace('test'), 0, 0, {
|
|
virt_lines = { { { 'VIRT LINES' } } },
|
|
virt_lines_above = true,
|
|
})
|
|
feed('<C-Y>')
|
|
-- number column on virtual lines should be empty
|
|
screen:expect([[
|
|
{6: }VIRT LINES |
|
|
{1: >> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
|
|
{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
|
|
{8: }aa^a |
|
|
{8: 2 }b |
|
|
{6: 3 }c |
|
|
{6: 4 } |
|
|
{0:~ }|*6
|
|
|
|
|
]])
|
|
end)
|
|
|
|
it('can have 32bit sign IDs', function()
|
|
command('sign define piet text=>> texthl=Search')
|
|
command('sign place 100000 line=1 name=piet buffer=1')
|
|
feed(':sign place<cr>')
|
|
screen:expect([[
|
|
{1:>>} |
|
|
{0:~ }|*6
|
|
{4: }|
|
|
:sign place |
|
|
{9:--- Signs ---} |
|
|
{10:Signs for [NULL]:} |
|
|
line=1 id=100000 name=piet priority=10 |
|
|
|
|
|
{11:Press ENTER or type command to continue}^ |
|
|
]])
|
|
|
|
feed('<cr>')
|
|
screen:expect([[
|
|
{1:>>}^ |
|
|
{0:~ }|*12
|
|
|
|
|
]])
|
|
end)
|
|
end)
|
|
|
|
it('signcolumn width is updated when removing all signs after deleting lines', function()
|
|
meths.buf_set_lines(0, 0, 1, true, {'a', 'b', 'c', 'd', 'e'})
|
|
command('sign define piet text=>>')
|
|
command('sign place 10001 line=1 name=piet')
|
|
command('sign place 10002 line=5 name=piet')
|
|
command('2delete')
|
|
command('sign unplace 10001')
|
|
screen:expect([[
|
|
{2: }a |
|
|
{2: }^c |
|
|
{2: }d |
|
|
>>e |
|
|
{0:~ }|*9
|
|
|
|
|
]])
|
|
command('sign unplace 10002')
|
|
screen:expect([[
|
|
a |
|
|
^c |
|
|
d |
|
|
e |
|
|
{0:~ }|*9
|
|
|
|
|
]])
|
|
end)
|
|
|
|
it('signcolumn width is updated when removing all signs after inserting lines', function()
|
|
meths.buf_set_lines(0, 0, 1, true, {'a', 'b', 'c', 'd', 'e'})
|
|
command('sign define piet text=>>')
|
|
command('sign place 10001 line=1 name=piet')
|
|
command('sign place 10002 line=5 name=piet')
|
|
command('copy .')
|
|
command('sign unplace 10001')
|
|
screen:expect([[
|
|
{2: }a |
|
|
{2: }^a |
|
|
{2: }b |
|
|
{2: }c |
|
|
{2: }d |
|
|
>>e |
|
|
{0:~ }|*7
|
|
|
|
|
]])
|
|
command('sign unplace 10002')
|
|
screen:expect([[
|
|
a |
|
|
^a |
|
|
b |
|
|
c |
|
|
d |
|
|
e |
|
|
{0:~ }|*7
|
|
|
|
|
]])
|
|
end)
|
|
|
|
it('numhl highlight is applied when signcolumn=no', function()
|
|
screen:try_resize(screen._width, 4)
|
|
command([[
|
|
set nu scl=no
|
|
call setline(1, ['line1', 'line2', 'line3'])
|
|
call nvim_buf_set_extmark(0, nvim_create_namespace('test'), 0, 0, {'number_hl_group':'Error'})
|
|
call sign_define('foo', { 'text':'F', 'numhl':'Error' })
|
|
call sign_place(0, '', 'foo', bufnr(''), { 'lnum':2 })
|
|
]])
|
|
screen:expect([[
|
|
{8: 1 }^line1 |
|
|
{8: 2 }line2 |
|
|
{6: 3 }line3 |
|
|
|
|
|
]])
|
|
end)
|
|
end)
|