feat(extmarks): add virt_text_repeat_linebreak flag (#26625)

Problem:  Unable to predict which byte-offset to place virtual text to
          make it repeat visually in the wrapped part of a line.
Solution: Add a flag to nvim_buf_set_extmark() that causes virtual
          text to repeat in wrapped lines.
This commit is contained in:
luukvbaal 2023-12-26 00:16:03 +01:00 committed by GitHub
parent 0a598c13b1
commit bbd5c6363c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 55 additions and 12 deletions

View File

@ -2696,6 +2696,8 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts})
text is selected or hidden because of scrolling with
'nowrap' or 'smoothscroll'. Currently only affects
"overlay" virt_text.
• virt_text_repeat_linebreak : repeat the virtual text on
wrapped lines.
• hl_mode : control how highlights are combined with the
highlights of the text. Currently only affects virt_text
highlights, but might affect `hl_group` in later versions.

View File

@ -339,11 +339,10 @@ The following changes to existing APIs or features add new behavior.
In addition, |nvim_buf_get_extmarks()| has gained an "overlap" option to
return such ranges even if they started before the specified position.
• Extmarks can opt-out of precise undo tracking using the new "undo_restore"
flag to |nvim_buf_set_extmark()|
• Extmarks can be automatically hidden or removed using the new "invalidate"
flag to |nvim_buf_set_extmark()|
• The following flags were added to |nvim_buf_set_extmark()|:
- "undo_restore": opt-out extmarks of precise undo tracking.
- "invalidate": automatically hide or delete extmarks.
- "virt_text_repeat_linebreak": repeat virtual text on wrapped lines.
• LSP hover and signature help now use Treesitter for highlighting of Markdown
content.

View File

@ -519,6 +519,8 @@ function vim.api.nvim_buf_line_count(buffer) end
--- text is selected or hidden because of scrolling with
--- 'nowrap' or 'smoothscroll'. Currently only affects
--- "overlay" virt_text.
--- • virt_text_repeat_linebreak : repeat the virtual text on
--- wrapped lines.
--- • hl_mode : control how highlights are combined with the
--- highlights of the text. Currently only affects virt_text
--- highlights, but might affect `hl_group` in later versions.

View File

@ -251,6 +251,7 @@ error('Cannot require a meta file')
--- @field virt_text_pos? string
--- @field virt_text_win_col? integer
--- @field virt_text_hide? boolean
--- @field virt_text_repeat_linebreak? boolean
--- @field hl_eol? boolean
--- @field hl_mode? string
--- @field invalidate? boolean

View File

@ -391,6 +391,8 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// text is selected or hidden because of
/// scrolling with 'nowrap' or 'smoothscroll'.
/// Currently only affects "overlay" virt_text.
/// - virt_text_repeat_linebreak : repeat the virtual text on
/// wrapped lines.
/// - hl_mode : control how highlights are combined with the
/// highlights of the text. Currently only affects
/// virt_text highlights, but might affect `hl_group`
@ -613,7 +615,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
}
hl.flags |= opts->hl_eol ? kSHHlEol : 0;
virt_text.flags |= opts->virt_text_hide ? kVTHide : 0;
virt_text.flags |= ((opts->virt_text_hide ? kVTHide : 0)
| (opts->virt_text_repeat_linebreak ? kVTRepeatLinebreak : 0));
if (HAS_KEY(opts, set_extmark, hl_mode)) {
String str = opts->hl_mode;

View File

@ -33,6 +33,7 @@ typedef struct {
String virt_text_pos;
Integer virt_text_win_col;
Boolean virt_text_hide;
Boolean virt_text_repeat_linebreak;
Boolean hl_eol;
String hl_mode;
Boolean invalidate;

View File

@ -1061,11 +1061,11 @@ void decor_to_dict_legacy(Dictionary *dict, DecorInline decor, bool hl_name)
Array chunks = virt_text_to_array(virt_text->data.virt_text, hl_name);
PUT(*dict, "virt_text", ARRAY_OBJ(chunks));
PUT(*dict, "virt_text_hide", BOOLEAN_OBJ(virt_text->flags & kVTHide));
PUT(*dict, "virt_text_repeat_linebreak", BOOLEAN_OBJ(virt_text->flags & kVTRepeatLinebreak));
if (virt_text->pos == kVPosWinCol) {
PUT(*dict, "virt_text_win_col", INTEGER_OBJ(virt_text->col));
}
PUT(*dict, "virt_text_pos",
CSTR_TO_OBJ(virt_text_pos_str[virt_text->pos]));
PUT(*dict, "virt_text_pos", CSTR_TO_OBJ(virt_text_pos_str[virt_text->pos]));
priority = virt_text->priority;
}

View File

@ -77,6 +77,7 @@ enum {
kVTIsLines = 1,
kVTHide = 2,
kVTLinesAbove = 4,
kVTRepeatLinebreak = 8,
};
typedef struct DecorVirtText DecorVirtText;

View File

@ -286,10 +286,12 @@ static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int
int vcol = item->draw_col - col_off;
col = draw_virt_text_item(buf, item->draw_col, vt->data.virt_text,
vt->hl_mode, max_col, vcol);
if (vt->pos == kVPosEndOfLine && do_eol) {
state->eol_col = col + 1;
}
}
item->draw_col = INT_MIN; // deactivate
if (vt && vt->pos == kVPosEndOfLine && do_eol) {
state->eol_col = col + 1;
if (!vt || !(vt->flags & kVTRepeatLinebreak)) {
item->draw_col = INT_MIN; // deactivate
}
*end_col = MAX(*end_col, col);
@ -1588,7 +1590,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
&& wlv.vcol >= wp->w_virtcol)
|| number_only)
&& wlv.filler_todo <= 0) {
draw_virt_text(wp, buf, win_col_offset, &wlv.col, wlv.row);
if (!number_only) {
draw_virt_text(wp, buf, win_col_offset, &wlv.col, wlv.row);
}
// don't clear anything after wlv.col
win_put_linebuf(wp, wlv.row, 0, wlv.col, wlv.col, bg_attr, false);
// Pretend we have finished updating the window. Except when

View File

@ -1549,6 +1549,7 @@ describe('API/extmarks', function()
virt_lines_above = true,
virt_lines_leftcol = true,
virt_text = { { "text", "Macro" }, { "???" }, { "stack", { "Type", "Search" } } },
virt_text_repeat_linebreak = false,
virt_text_hide = true,
virt_text_pos = "right_align",
} }, get_extmark_by_id(ns, marks[1], { details = true }))
@ -1557,6 +1558,7 @@ describe('API/extmarks', function()
right_gravity = true,
priority = 0,
virt_text = { { "", "Macro" }, { "", { "Type", "Search" } }, { "" } },
virt_text_repeat_linebreak = false,
virt_text_hide = false,
virt_text_pos = "win_col",
virt_text_win_col = 1,

View File

@ -644,6 +644,7 @@ describe('Buffer highlighting', function()
virt_text = s1,
-- other details
right_gravity = true,
virt_text_repeat_linebreak = false,
virt_text_pos = 'eol',
virt_text_hide = false,
}}}, get_extmarks(id1, {0,0}, {0, -1}, {details=true}))
@ -656,6 +657,7 @@ describe('Buffer highlighting', function()
virt_text = s2,
-- other details
right_gravity = true,
virt_text_repeat_linebreak = false,
virt_text_pos = 'eol',
virt_text_hide = false,
}}}, get_extmarks(id1, {lastline,0}, {lastline, -1}, {details=true}))

View File

@ -2094,6 +2094,32 @@ describe('extmark decorations', function()
|
]]}
end)
it('virt_text_repeat_linebreak repeats virtual text on wrapped lines', function()
screen:try_resize(40, 5)
meths.set_option_value('breakindent', true, {})
insert(example_text)
meths.buf_set_extmark(0, ns, 1, 0, { virt_text = {{'', 'NonText'}}, virt_text_pos = 'overlay', virt_text_repeat_linebreak = true })
meths.buf_set_extmark(0, ns, 1, 3, { virt_text = {{'', 'NonText'}}, virt_text_pos = 'overlay', virt_text_repeat_linebreak = true })
command('norm gg')
screen:expect{grid=[[
^for _,item in ipairs(items) do |
{1:} {1:}local text, hl_id_cell, count = unpa|
{1:} {1:}ck(item) |
if hl_id_cell ~= nil then |
|
]]}
meths.buf_clear_namespace(0, ns, 0, -1)
meths.buf_set_extmark(0, ns, 1, 0, { virt_text = {{'', 'NonText'}}, virt_text_repeat_linebreak = true, virt_text_win_col = 0 })
meths.buf_set_extmark(0, ns, 1, 0, { virt_text = {{'', 'NonText'}}, virt_text_repeat_linebreak = true, virt_text_win_col = 2 })
screen:expect{grid=[[
^for _,item in ipairs(items) do |
{1:} {1:} local text, hl_id_cell, count = unpa|
{1:} {1:} ck(item) |
if hl_id_cell ~= nil then |
|
]]}
end)
end)
describe('decorations: inline virtual text', function()