feat(extmark): virt_lines can highlight until EOL #41289

This commit is contained in:
Goldpigg
2026-08-18 20:35:22 +08:00
committed by GitHub
parent 1eae512285
commit ab82c2c6b0
7 changed files with 101 additions and 7 deletions

View File

@@ -3356,7 +3356,9 @@ nvim_buf_set_extmark({buf}, {ns_id}, {line}, {col}, {opts})
'wrap' and 'linebreak' options do not take effect, so the
number of extra screen lines will match the list-size (but
see `virt_lines_overflow`). By default lines are placed
below the marked line.
below the marked line. Special case: If the last
`text_chunk` is empty, the corresponding `hl` will extend
to the end of the line.
• virt_lines_above: Place virtual lines above instead.
• virt_lines_leftcol: Place virtual lines in the leftmost
column of the window, bypassing sign and number columns.

View File

@@ -228,6 +228,8 @@ API
• |nvim_buf_set_extmark()| `virt_lines_overflow` accepts "wrap" to enable
wrapping onto extra rows and "auto" which enables horizontal scrolling when
'nowrap' is set and wrapping when 'wrap' is set.
• |nvim_buf_set_extmark()| `virt_lines` can extend the last `hl` in each `virt_line`
till the end of the line.
• |nvim_set_option_value()| accepts a new `operation` field to modify the
existing option value.
• |nvim_set_option_value()| returns the new option value.

View File

@@ -671,7 +671,8 @@ function vim.api.nvim_buf_line_count(buf) end
--- display of the text, except 'tabstop' is used for hard tabs. The 'wrap' and
--- 'linebreak' options do not take effect, so the number of extra screen lines will
--- match the list-size (but see `virt_lines_overflow`). By default lines are placed
--- below the marked line.
--- below the marked line. Special case: If the last `text_chunk` is empty,
--- the corresponding `hl` will extend to the end of the line.
--- - virt_lines_above: Place virtual lines above instead.
--- - virt_lines_leftcol: Place virtual lines in the leftmost column of the window,
--- bypassing sign and number columns.

View File

@@ -247,6 +247,12 @@ function Completor:show(hint)
col = col + skip - 1
local virt_lines = { unpack(lines, 2) }
for i, s in ipairs(virt_lines) do
if #s == 1 and s[1][1] == '' then
virt_lines[i] = {}
end
end
api.nvim_buf_set_extmark(self.bufnr, namespace, row, col, {
virt_text = virt_text,
virt_lines = virt_lines,

View File

@@ -457,7 +457,8 @@ ArrayOf(DictAs(get_extmark_item)) nvim_buf_get_extmarks(Buffer buf, Integer ns_i
/// display of the text, except 'tabstop' is used for hard tabs. The 'wrap' and
/// 'linebreak' options do not take effect, so the number of extra screen lines will
/// match the list-size (but see `virt_lines_overflow`). By default lines are placed
/// below the marked line.
/// below the marked line. Special case: If the last `text_chunk` is empty,
/// the corresponding `hl` will extend to the end of the line.
/// - virt_lines_above: Place virtual lines above instead.
/// - virt_lines_leftcol: Place virtual lines in the leftmost column of the window,
/// bypassing sign and number columns.

View File

@@ -361,7 +361,7 @@ static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int
if (vt) {
int vcol = item->draw_col - col_off;
int col = draw_virt_text_item(buf, item->draw_col, vt->data.virt_text,
vt->hl_mode, max_col, vcol, 0);
vt->hl_mode, max_col, vcol, 0, false);
if (do_eol && ((vt->pos == kVPosEndOfLine) || (vt->pos == kVPosEndOfLineRightAlign))) {
state->eol_col = col + 1;
}
@@ -373,14 +373,20 @@ static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int
}
}
/// @param eol_hl Extend highlight to EOL.
static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode, int max_col,
int vcol, int skip_cells)
int vcol, int skip_cells, bool eol_hl)
{
const char *virt_str = "";
int virt_attr = 0;
size_t virt_pos = 0;
eol_hl &= kv_size(vt) && *kv_A(vt, kv_size(vt) - 1).text == NUL;
while (col < max_col) {
// extending last highlight till the end of line
if (eol_hl && *virt_str == NUL && virt_pos == kv_size(vt)) {
virt_str = " ";
}
if (skip_cells >= 0 && *virt_str == NUL) {
if (virt_pos >= kv_size(vt)) {
break;
@@ -2983,7 +2989,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
}
if (kv_size(fold_vt) > 0) {
draw_virt_text_item(buf, win_col_offset, fold_vt, kHlModeCombine, view_width, 0, 0);
draw_virt_text_item(buf, win_col_offset, fold_vt, kHlModeCombine, view_width, 0, 0, false);
}
draw_virt_text(wp, buf, win_col_offset, &wlv.col, wlv.row);
// Set increasing virtual columns in grid->vcols[] to set correct curswant
@@ -3261,7 +3267,7 @@ end_check:
kHlModeReplace,
view_width,
0,
virt_line_skip_cells);
virt_line_skip_cells, true);
} else if (wlv.filler_todo <= 0) {
draw_virt_text(wp, buf, win_col_offset, &draw_col, wlv.row);
}

View File

@@ -7276,6 +7276,82 @@ if (h->n_buckets < new_n_buckets) { // expand
]])
end)
it('highlight till eol with virt_lines_overflow=trunc', function()
insert('line1')
api.nvim_buf_set_extmark(0, ns, 0, 0, {
virt_lines = { { { 'VIRT LINE1', 'String' }, { '', 'Visual' } }, { { 'VIRT LINE2', 'String' } } },
})
screen:expect([[
line^1 |
{26:VIRT LINE1}{17: }|
{26:VIRT LINE2} |
{1:~ }|*8
|
]])
end)
it('highlight till eol with virt_lines_overflow=wrap', function()
insert('line1')
api.nvim_buf_set_extmark(0, ns, 0, 0, {
virt_lines = { { { 'VIRT LINE', 'String' }, { string.rep('-', 50), 'Visual' }, { '', 'Visual' } } },
virt_lines_overflow = 'wrap',
})
screen:expect([[
line^1 |
{26:VIRT LINE}{17:-----------------------------------------}|
{17:--------- }|
{1:~ }|*8
|
]])
end)
it('highlight till eol with virt_lines_overflow=scroll', function()
command('set nowrap')
insert('abcdefghijklmnopqrstuvwxyz')
api.nvim_buf_set_extmark(0, ns, 0, 0, {
virt_lines = { { { 'VIRT LINE', 'String' }, { '', 'Visual' } } },
virt_lines_overflow = 'scroll',
})
screen:expect([[
abcdefghijklmnopqrstuvwxy^z |
{26:VIRT LINE}{17: }|
{1:~ }|*9
|
]])
feed('zl')
screen:expect([[
bcdefghijklmnopqrstuvwxy^z |
{26:IRT LINE}{17: }|
{1:~ }|*9
|
]])
feed('zl')
screen:expect([[
cdefghijklmnopqrstuvwxy^z |
{26:RT LINE}{17: }|
{1:~ }|*9
|
]])
feed('6zl')
screen:expect([[
ijklmnopqrstuvwxy^z |
{26:E}{17: }|
{1:~ }|*9
|
]])
feed('zl')
screen:expect([[
jklmnopqrstuvwxy^z |
{17: }|
{1:~ }|*9
|
]])
end)
it('does not show twice if end_row or end_col is specified #18622', function()
screen:try_resize(50, 8)
insert([[