refactor(drawline): integrate terminal hl with eol loop (#27893)

There is no test for using 'cursorline' in Normal mode in a terminal
buffer, so add a test and fix 'cursorcolumn' remaining when entering
Terminal mode.

Also move synIDattr() tests to ui/highlight_spec.lua.
This commit is contained in:
zeertzjq 2024-03-17 17:24:03 +08:00 committed by GitHub
parent c52dfb6e84
commit 54db75e995
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 181 additions and 129 deletions

View File

@ -2576,7 +2576,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
&& wp->w_virtcol < grid->cols * (ptrdiff_t)(wlv.row - startrow + 1) + start_col && wp->w_virtcol < grid->cols * (ptrdiff_t)(wlv.row - startrow + 1) + start_col
&& lnum != wp->w_cursor.lnum) && lnum != wp->w_cursor.lnum)
|| wlv.color_cols || wlv.line_attr_lowprio || wlv.line_attr || wlv.color_cols || wlv.line_attr_lowprio || wlv.line_attr
|| wlv.diff_hlf != 0)) { || wlv.diff_hlf != 0 || wp->w_buffer->terminal)) {
int rightmost_vcol = get_rightmost_vcol(wp, wlv.color_cols); int rightmost_vcol = get_rightmost_vcol(wp, wlv.color_cols);
const int cuc_attr = win_hl_attr(wp, HLF_CUC); const int cuc_attr = win_hl_attr(wp, HLF_CUC);
const int mc_attr = win_hl_attr(wp, HLF_MC); const int mc_attr = win_hl_attr(wp, HLF_MC);
@ -2590,7 +2590,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
: 0; : 0;
const int base_attr = hl_combine_attr(wlv.line_attr_lowprio, diff_attr); const int base_attr = hl_combine_attr(wlv.line_attr_lowprio, diff_attr);
if (base_attr || wlv.line_attr) { if (base_attr || wlv.line_attr || wp->w_buffer->terminal) {
rightmost_vcol = INT_MAX; rightmost_vcol = INT_MAX;
} }
@ -2609,6 +2609,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
col_attr = hl_combine_attr(col_attr, mc_attr); col_attr = hl_combine_attr(col_attr, mc_attr);
} }
if (wp->w_buffer->terminal && wlv.vcol < TERM_ATTRS_MAX) {
col_attr = hl_combine_attr(col_attr, term_attrs[wlv.vcol]);
}
col_attr = hl_combine_attr(col_attr, wlv.line_attr); col_attr = hl_combine_attr(col_attr, wlv.line_attr);
linebuf_attr[wlv.off] = col_attr; linebuf_attr[wlv.off] = col_attr;
@ -2622,19 +2626,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
} }
} }
// TODO(bfredl): integrate with the common beyond-the-end-loop
if (wp->w_buffer->terminal) {
// terminal buffers may need to highlight beyond the end of the logical line
while (wlv.col >= 0 && wlv.col < grid->cols) {
linebuf_char[wlv.off] = schar_from_ascii(' ');
linebuf_attr[wlv.off] = wlv.vcol >= TERM_ATTRS_MAX ? 0 : term_attrs[wlv.vcol];
linebuf_vcol[wlv.off] = wlv.vcol;
wlv.off++;
wlv.vcol++;
wlv.col++;
}
}
if (kv_size(fold_vt) > 0) { if (kv_size(fold_vt) > 0) {
draw_virt_text_item(buf, win_col_offset, fold_vt, kHlModeCombine, grid->cols, 0); draw_virt_text_item(buf, win_col_offset, fold_vt, kHlModeCombine, grid->cols, 0);
} }

View File

@ -543,6 +543,9 @@ bool terminal_enter(void)
} else { } else {
curwin->w_p_cul = false; curwin->w_p_cul = false;
} }
if (curwin->w_p_cuc) {
redraw_later(curwin, UPD_SOME_VALID);
}
curwin->w_p_cuc = false; curwin->w_p_cuc = false;
curwin->w_p_so = 0; curwin->w_p_so = 0;
curwin->w_p_siso = 0; curwin->w_p_siso = 0;

View File

@ -54,7 +54,7 @@ describe(':terminal buffer', function()
eq({ 0, 'both' }, eval('[&l:cursorline, &l:cursorlineopt]')) eq({ 0, 'both' }, eval('[&l:cursorline, &l:cursorlineopt]'))
end) end)
it('terminal-mode disables cursorline when cursorlineopt is only set to "line', function() it('terminal-mode disables cursorline when cursorlineopt is only set to "line"', function()
feed([[<C-\><C-N>]]) feed([[<C-\><C-N>]])
command('setlocal cursorline cursorlineopt=line') command('setlocal cursorline cursorlineopt=line')
feed('i') feed('i')

View File

@ -5,7 +5,6 @@ local feed, clear = helpers.feed, helpers.clear
local api = helpers.api local api = helpers.api
local testprg, command = helpers.testprg, helpers.command local testprg, command = helpers.testprg, helpers.command
local nvim_prog_abs = helpers.nvim_prog_abs local nvim_prog_abs = helpers.nvim_prog_abs
local eq, eval = helpers.eq, helpers.eval
local fn = helpers.fn local fn = helpers.fn
local nvim_set = helpers.nvim_set local nvim_set = helpers.nvim_set
local is_os = helpers.is_os local is_os = helpers.is_os
@ -191,6 +190,65 @@ it(':terminal highlight has lower precedence than editor #9964', function()
]]) ]])
end) end)
it('CursorLine and CursorColumn work in :terminal buffer in Normal mode', function()
clear()
local screen = Screen.new(50, 7)
screen:set_default_attr_ids({
[1] = { background = Screen.colors.Grey90 }, -- CursorLine, CursorColumn
[2] = { reverse = true }, -- TermCursor
[3] = { bold = true }, -- ModeMsg
})
screen:attach()
command(("enew | call termopen(['%s'])"):format(testprg('tty-test')))
screen:expect([[
^tty ready |
|*6
]])
thelpers.feed_data((' foobar'):rep(30))
screen:expect([[
^tty ready |
foobar foobar foobar foobar foobar foobar foobar |
foobar foobar foobar foobar foobar foobar foobar f|
oobar foobar foobar foobar foobar foobar foobar fo|
obar foobar foobar foobar foobar foobar foobar foo|
bar foobar |
|
]])
command('set cursorline cursorcolumn')
feed('j10w')
screen:expect([[
tty ready {1: } |
foobar foobar{1: }foobar foobar foobar foobar foobar |
{1:foobar foobar ^foobar foobar foobar foobar foobar f}|
oobar foobar f{1:o}obar foobar foobar foobar foobar fo|
obar foobar fo{1:o}bar foobar foobar foobar foobar foo|
bar foobar {1: } |
|
]])
-- Entering terminal mode disables 'cursorline' and 'cursorcolumn'.
feed('i')
screen:expect([[
tty ready |
foobar foobar foobar foobar foobar foobar foobar |
foobar foobar foobar foobar foobar foobar foobar f|
oobar foobar foobar foobar foobar foobar foobar fo|
obar foobar foobar foobar foobar foobar foobar foo|
bar foobar{2: } |
{3:-- TERMINAL --} |
]])
-- Leaving terminal mode restores old values.
feed([[<C-\><C-N>]])
screen:expect([[
tty ready{1: } |
foobar f{1:o}obar foobar foobar foobar foobar foobar |
foobar fo{1:o}bar foobar foobar foobar foobar foobar f|
oobar foo{1:b}ar foobar foobar foobar foobar foobar fo|
obar foob{1:a}r foobar foobar foobar foobar foobar foo|
{1:bar fooba^r }|
|
]])
end)
describe(':terminal highlight forwarding', function() describe(':terminal highlight forwarding', function()
local screen local screen
@ -276,115 +334,3 @@ describe(':terminal highlight with custom palette', function()
]]) ]])
end) end)
end) end)
describe('synIDattr()', function()
local screen
before_each(function()
clear()
screen = Screen.new(50, 7)
command('highlight Normal ctermfg=252 guifg=#ff0000 guibg=Black')
-- Salmon #fa8072 Maroon #800000
command(
'highlight Keyword ctermfg=79 guifg=Salmon guisp=Maroon cterm=strikethrough gui=strikethrough'
)
end)
it('returns cterm-color if RGB-capable UI is _not_ attached', function()
eq('252', eval('synIDattr(hlID("Normal"), "fg")'))
eq('252', eval('synIDattr(hlID("Normal"), "fg#")'))
eq('', eval('synIDattr(hlID("Normal"), "bg")'))
eq('', eval('synIDattr(hlID("Normal"), "bg#")'))
eq('79', eval('synIDattr(hlID("Keyword"), "fg")'))
eq('79', eval('synIDattr(hlID("Keyword"), "fg#")'))
eq('', eval('synIDattr(hlID("Keyword"), "sp")'))
eq('', eval('synIDattr(hlID("Keyword"), "sp#")'))
end)
it('returns gui-color if "gui" arg is passed', function()
eq('Black', eval('synIDattr(hlID("Normal"), "bg", "gui")'))
eq('Maroon', eval('synIDattr(hlID("Keyword"), "sp", "gui")'))
end)
it('returns gui-color if RGB-capable UI is attached', function()
screen:attach({ rgb = true })
eq('#ff0000', eval('synIDattr(hlID("Normal"), "fg")'))
eq('Black', eval('synIDattr(hlID("Normal"), "bg")'))
eq('Salmon', eval('synIDattr(hlID("Keyword"), "fg")'))
eq('Maroon', eval('synIDattr(hlID("Keyword"), "sp")'))
end)
it('returns #RRGGBB value for fg#/bg#/sp#', function()
screen:attach({ rgb = true })
eq('#ff0000', eval('synIDattr(hlID("Normal"), "fg#")'))
eq('#000000', eval('synIDattr(hlID("Normal"), "bg#")'))
eq('#fa8072', eval('synIDattr(hlID("Keyword"), "fg#")'))
eq('#800000', eval('synIDattr(hlID("Keyword"), "sp#")'))
end)
it('returns color number if non-GUI', function()
screen:attach({ rgb = false })
eq('252', eval('synIDattr(hlID("Normal"), "fg")'))
eq('79', eval('synIDattr(hlID("Keyword"), "fg")'))
end)
it('returns "1" if group has given highlight attribute', function()
local hl_attrs = {
'underline',
'undercurl',
'underdouble',
'underdotted',
'underdashed',
'strikethrough',
}
for _, hl_attr in ipairs(hl_attrs) do
local context = 'using ' .. hl_attr .. ' attr'
command('highlight Keyword cterm=' .. hl_attr .. ' gui=' .. hl_attr)
eq('', eval('synIDattr(hlID("Normal"), "' .. hl_attr .. '")'), context)
eq('1', eval('synIDattr(hlID("Keyword"), "' .. hl_attr .. '")'), context)
eq('1', eval('synIDattr(hlID("Keyword"), "' .. hl_attr .. '", "gui")'), context)
end
end)
end)
describe('fg/bg special colors', function()
local screen
before_each(function()
clear()
screen = Screen.new(50, 7)
command('highlight Normal ctermfg=145 ctermbg=16 guifg=#ff0000 guibg=Black')
command('highlight Visual ctermfg=bg ctermbg=fg guifg=bg guibg=fg guisp=bg')
end)
it('resolve to "Normal" values', function()
eq(eval('synIDattr(hlID("Normal"), "bg")'), eval('synIDattr(hlID("Visual"), "fg")'))
eq(eval('synIDattr(hlID("Normal"), "bg#")'), eval('synIDattr(hlID("Visual"), "fg#")'))
eq(eval('synIDattr(hlID("Normal"), "fg")'), eval('synIDattr(hlID("Visual"), "bg")'))
eq(eval('synIDattr(hlID("Normal"), "fg#")'), eval('synIDattr(hlID("Visual"), "bg#")'))
eq('bg', eval('synIDattr(hlID("Visual"), "fg", "gui")'))
eq('bg', eval('synIDattr(hlID("Visual"), "fg#", "gui")'))
eq('fg', eval('synIDattr(hlID("Visual"), "bg", "gui")'))
eq('fg', eval('synIDattr(hlID("Visual"), "bg#", "gui")'))
eq('bg', eval('synIDattr(hlID("Visual"), "sp", "gui")'))
eq('bg', eval('synIDattr(hlID("Visual"), "sp#", "gui")'))
end)
it('resolve to "Normal" values in RGB-capable UI', function()
screen:attach({ rgb = true })
eq('bg', eval('synIDattr(hlID("Visual"), "fg")'))
eq(eval('synIDattr(hlID("Normal"), "bg#")'), eval('synIDattr(hlID("Visual"), "fg#")'))
eq('fg', eval('synIDattr(hlID("Visual"), "bg")'))
eq(eval('synIDattr(hlID("Normal"), "fg#")'), eval('synIDattr(hlID("Visual"), "bg#")'))
eq('bg', eval('synIDattr(hlID("Visual"), "sp")'))
eq(eval('synIDattr(hlID("Normal"), "bg#")'), eval('synIDattr(hlID("Visual"), "sp#")'))
end)
it('resolve after the "Normal" group is modified', function()
screen:attach({ rgb = true })
local new_guibg = '#282c34'
local new_guifg = '#abb2bf'
command('highlight Normal guifg=' .. new_guifg .. ' guibg=' .. new_guibg)
eq(new_guibg, eval('synIDattr(hlID("Visual"), "fg#")'))
eq(new_guifg, eval('synIDattr(hlID("Visual"), "bg#")'))
eq(new_guibg, eval('synIDattr(hlID("Visual"), "sp#")'))
end)
end)

View File

@ -2537,3 +2537,115 @@ describe('highlight namespaces', function()
} }
end) end)
end) end)
describe('synIDattr()', function()
local screen
before_each(function()
clear()
screen = Screen.new(50, 7)
command('highlight Normal ctermfg=252 guifg=#ff0000 guibg=Black')
-- Salmon #fa8072 Maroon #800000
command(
'highlight Keyword ctermfg=79 guifg=Salmon guisp=Maroon cterm=strikethrough gui=strikethrough'
)
end)
it('returns cterm-color if RGB-capable UI is _not_ attached', function()
eq('252', eval('synIDattr(hlID("Normal"), "fg")'))
eq('252', eval('synIDattr(hlID("Normal"), "fg#")'))
eq('', eval('synIDattr(hlID("Normal"), "bg")'))
eq('', eval('synIDattr(hlID("Normal"), "bg#")'))
eq('79', eval('synIDattr(hlID("Keyword"), "fg")'))
eq('79', eval('synIDattr(hlID("Keyword"), "fg#")'))
eq('', eval('synIDattr(hlID("Keyword"), "sp")'))
eq('', eval('synIDattr(hlID("Keyword"), "sp#")'))
end)
it('returns gui-color if "gui" arg is passed', function()
eq('Black', eval('synIDattr(hlID("Normal"), "bg", "gui")'))
eq('Maroon', eval('synIDattr(hlID("Keyword"), "sp", "gui")'))
end)
it('returns gui-color if RGB-capable UI is attached', function()
screen:attach({ rgb = true })
eq('#ff0000', eval('synIDattr(hlID("Normal"), "fg")'))
eq('Black', eval('synIDattr(hlID("Normal"), "bg")'))
eq('Salmon', eval('synIDattr(hlID("Keyword"), "fg")'))
eq('Maroon', eval('synIDattr(hlID("Keyword"), "sp")'))
end)
it('returns #RRGGBB value for fg#/bg#/sp#', function()
screen:attach({ rgb = true })
eq('#ff0000', eval('synIDattr(hlID("Normal"), "fg#")'))
eq('#000000', eval('synIDattr(hlID("Normal"), "bg#")'))
eq('#fa8072', eval('synIDattr(hlID("Keyword"), "fg#")'))
eq('#800000', eval('synIDattr(hlID("Keyword"), "sp#")'))
end)
it('returns color number if non-GUI', function()
screen:attach({ rgb = false })
eq('252', eval('synIDattr(hlID("Normal"), "fg")'))
eq('79', eval('synIDattr(hlID("Keyword"), "fg")'))
end)
it('returns "1" if group has given highlight attribute', function()
local hl_attrs = {
'underline',
'undercurl',
'underdouble',
'underdotted',
'underdashed',
'strikethrough',
}
for _, hl_attr in ipairs(hl_attrs) do
local context = 'using ' .. hl_attr .. ' attr'
command('highlight Keyword cterm=' .. hl_attr .. ' gui=' .. hl_attr)
eq('', eval('synIDattr(hlID("Normal"), "' .. hl_attr .. '")'), context)
eq('1', eval('synIDattr(hlID("Keyword"), "' .. hl_attr .. '")'), context)
eq('1', eval('synIDattr(hlID("Keyword"), "' .. hl_attr .. '", "gui")'), context)
end
end)
end)
describe('fg/bg special colors', function()
local screen
before_each(function()
clear()
screen = Screen.new(50, 7)
command('highlight Normal ctermfg=145 ctermbg=16 guifg=#ff0000 guibg=Black')
command('highlight Visual ctermfg=bg ctermbg=fg guifg=bg guibg=fg guisp=bg')
end)
it('resolve to "Normal" values', function()
eq(eval('synIDattr(hlID("Normal"), "bg")'), eval('synIDattr(hlID("Visual"), "fg")'))
eq(eval('synIDattr(hlID("Normal"), "bg#")'), eval('synIDattr(hlID("Visual"), "fg#")'))
eq(eval('synIDattr(hlID("Normal"), "fg")'), eval('synIDattr(hlID("Visual"), "bg")'))
eq(eval('synIDattr(hlID("Normal"), "fg#")'), eval('synIDattr(hlID("Visual"), "bg#")'))
eq('bg', eval('synIDattr(hlID("Visual"), "fg", "gui")'))
eq('bg', eval('synIDattr(hlID("Visual"), "fg#", "gui")'))
eq('fg', eval('synIDattr(hlID("Visual"), "bg", "gui")'))
eq('fg', eval('synIDattr(hlID("Visual"), "bg#", "gui")'))
eq('bg', eval('synIDattr(hlID("Visual"), "sp", "gui")'))
eq('bg', eval('synIDattr(hlID("Visual"), "sp#", "gui")'))
end)
it('resolve to "Normal" values in RGB-capable UI', function()
screen:attach({ rgb = true })
eq('bg', eval('synIDattr(hlID("Visual"), "fg")'))
eq(eval('synIDattr(hlID("Normal"), "bg#")'), eval('synIDattr(hlID("Visual"), "fg#")'))
eq('fg', eval('synIDattr(hlID("Visual"), "bg")'))
eq(eval('synIDattr(hlID("Normal"), "fg#")'), eval('synIDattr(hlID("Visual"), "bg#")'))
eq('bg', eval('synIDattr(hlID("Visual"), "sp")'))
eq(eval('synIDattr(hlID("Normal"), "bg#")'), eval('synIDattr(hlID("Visual"), "sp#")'))
end)
it('resolve after the "Normal" group is modified', function()
screen:attach({ rgb = true })
local new_guibg = '#282c34'
local new_guifg = '#abb2bf'
command('highlight Normal guifg=' .. new_guifg .. ' guibg=' .. new_guibg)
eq(new_guibg, eval('synIDattr(hlID("Visual"), "fg#")'))
eq(new_guifg, eval('synIDattr(hlID("Visual"), "bg#")'))
eq(new_guibg, eval('synIDattr(hlID("Visual"), "sp#")'))
end)
end)