feat(marks): virtual lines support horizontal scrolling (#32497)

Add a new field `virt_lines_overflow` that enables horizontal scrolling
for virtual lines when set to "scroll".
This commit is contained in:
zeertzjq
2025-02-20 21:47:12 +08:00
committed by GitHub
parent 574ea6a191
commit 51cf84daf9
12 changed files with 274 additions and 46 deletions

View File

@ -2651,6 +2651,12 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {opts})
• virt_lines_above: place virtual lines above instead. • virt_lines_above: place virtual lines above instead.
• virt_lines_leftcol: Place virtual lines in the leftmost • virt_lines_leftcol: Place virtual lines in the leftmost
column of the window, bypassing sign and number columns. column of the window, bypassing sign and number columns.
• virt_lines_overflow: controls how to handle virtual lines
wider than the window. Currently takes the one of the
following values:
• "trunc": truncate virtual lines on the right (default).
• "scroll": virtual lines can scroll horizontally with
'nowrap', otherwise the same as "trunc".
• ephemeral : for use with |nvim_set_decoration_provider()| • ephemeral : for use with |nvim_set_decoration_provider()|
callbacks. The mark will only be used for the current callbacks. The mark will only be used for the current
redraw cycle, and not be permantently stored in the redraw cycle, and not be permantently stored in the

View File

@ -202,9 +202,12 @@ API
• |nvim_echo()| `err` field to print error messages and `chunks` accepts • |nvim_echo()| `err` field to print error messages and `chunks` accepts
highlight group IDs. highlight group IDs.
• |nvim_open_win()| `relative` field can be set to "laststatus" and "tabline". • |nvim_open_win()| `relative` field can be set to "laststatus" and "tabline".
• |nvim_buf_set_extmark()| `hl_group` field can be an array of layered groups • |nvim_buf_set_extmark()| new field `virt_lines_overflow` accepts value `scroll` to
enable horizontal scrolling for virtual lines with 'nowrap'.
right aligned text that truncates before covering up buffer text.
• |nvim_buf_set_extmark()| `hl_group` field can be an array of layered groups.
• |vim.hl.range()| now has a optional `timeout` field which allows for a timed highlight • |vim.hl.range()| now has a optional `timeout` field which allows for a timed highlight
• |nvim_buf_set_extmark()| virt_text_pos accepts `eol_right_align` to • |nvim_buf_set_extmark()| `virt_text_pos` field accepts value `eol_right_align` to
allow for right aligned text that truncates before covering up buffer text. allow for right aligned text that truncates before covering up buffer text.
DEFAULTS DEFAULTS

View File

@ -641,7 +641,11 @@ function vim.api.nvim_buf_line_count(buffer) end
--- - virt_lines_leftcol: Place virtual lines in the leftmost --- - virt_lines_leftcol: Place virtual lines in the leftmost
--- column of the window, bypassing --- column of the window, bypassing
--- sign and number columns. --- sign and number columns.
--- --- - virt_lines_overflow: controls how to handle virtual lines wider
--- than the window. Currently takes the one of the following values:
--- - "trunc": truncate virtual lines on the right (default).
--- - "scroll": virtual lines can scroll horizontally with 'nowrap',
--- otherwise the same as "trunc".
--- - ephemeral : for use with `nvim_set_decoration_provider()` --- - ephemeral : for use with `nvim_set_decoration_provider()`
--- callbacks. The mark will only be used for the current --- callbacks. The mark will only be used for the current
--- redraw cycle, and not be permantently stored in the --- redraw cycle, and not be permantently stored in the

View File

@ -258,6 +258,7 @@ error('Cannot require a meta file')
--- @field virt_lines? any[] --- @field virt_lines? any[]
--- @field virt_lines_above? boolean --- @field virt_lines_above? boolean
--- @field virt_lines_leftcol? boolean --- @field virt_lines_leftcol? boolean
--- @field virt_lines_overflow? string
--- @field strict? boolean --- @field strict? boolean
--- @field sign_text? string --- @field sign_text? string
--- @field sign_hl_group? integer|string --- @field sign_hl_group? integer|string

View File

@ -448,7 +448,11 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// - virt_lines_leftcol: Place virtual lines in the leftmost /// - virt_lines_leftcol: Place virtual lines in the leftmost
/// column of the window, bypassing /// column of the window, bypassing
/// sign and number columns. /// sign and number columns.
/// /// - virt_lines_overflow: controls how to handle virtual lines wider
/// than the window. Currently takes the one of the following values:
/// - "trunc": truncate virtual lines on the right (default).
/// - "scroll": virtual lines can scroll horizontally with 'nowrap',
/// otherwise the same as "trunc".
/// - ephemeral : for use with |nvim_set_decoration_provider()| /// - ephemeral : for use with |nvim_set_decoration_provider()|
/// callbacks. The mark will only be used for the current /// callbacks. The mark will only be used for the current
/// redraw cycle, and not be permantently stored in the /// redraw cycle, and not be permantently stored in the
@ -669,7 +673,17 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
} }
} }
bool virt_lines_leftcol = opts->virt_lines_leftcol; int virt_lines_flags = opts->virt_lines_leftcol ? kVLLeftcol : 0;
if (HAS_KEY(opts, set_extmark, virt_lines_overflow)) {
String str = opts->virt_lines_overflow;
if (strequal("scroll", str.data)) {
virt_lines_flags |= kVLScroll;
} else if (!strequal("trunc", str.data)) {
VALIDATE_S(false, "virt_lines_overflow", str.data, {
goto error;
});
}
}
if (HAS_KEY(opts, set_extmark, virt_lines)) { if (HAS_KEY(opts, set_extmark, virt_lines)) {
Array a = opts->virt_lines; Array a = opts->virt_lines;
@ -679,7 +693,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
}); });
int dummig; int dummig;
VirtText jtem = parse_virt_text(a.items[j].data.array, err, &dummig); VirtText jtem = parse_virt_text(a.items[j].data.array, err, &dummig);
kv_push(virt_lines.data.virt_lines, ((struct virt_line){ jtem, virt_lines_leftcol })); kv_push(virt_lines.data.virt_lines, ((struct virt_line){ jtem, virt_lines_flags }));
if (ERROR_SET(err)) { if (ERROR_SET(err)) {
goto error; goto error;
} }

View File

@ -45,6 +45,7 @@ typedef struct {
Array virt_lines; Array virt_lines;
Boolean virt_lines_above; Boolean virt_lines_above;
Boolean virt_lines_leftcol; Boolean virt_lines_leftcol;
String virt_lines_overflow;
Boolean strict; Boolean strict;
String sign_text; String sign_text;
HLGroupID sign_hl_group; HLGroupID sign_hl_group;

View File

@ -64,7 +64,7 @@
#define NIL ((Object)OBJECT_INIT) #define NIL ((Object)OBJECT_INIT)
#define NULL_STRING ((String)STRING_INIT) #define NULL_STRING ((String)STRING_INIT)
#define HAS_KEY(d, typ, key) (((d)->is_set__##typ##_ & (1 << KEYSET_OPTIDX_##typ##__##key)) != 0) #define HAS_KEY(d, typ, key) (((d)->is_set__##typ##_ & (1ULL << KEYSET_OPTIDX_##typ##__##key)) != 0)
#define GET_BOOL_OR_TRUE(d, typ, key) (HAS_KEY(d, typ, key) ? (d)->key : true) #define GET_BOOL_OR_TRUE(d, typ, key) (HAS_KEY(d, typ, key) ? (d)->key : true)
@ -75,7 +75,7 @@
kv_push_c(dict, ((KeyValuePair) { .key = cstr_as_string(k), .value = v })) kv_push_c(dict, ((KeyValuePair) { .key = cstr_as_string(k), .value = v }))
#define PUT_KEY(d, typ, key, v) \ #define PUT_KEY(d, typ, key, v) \
do { (d).is_set__##typ##_ |= (1 << KEYSET_OPTIDX_##typ##__##key); (d).key = v; } while (0) do { (d).is_set__##typ##_ |= (1ULL << KEYSET_OPTIDX_##typ##__##key); (d).key = v; } while (0)
#define ADD(array, item) \ #define ADD(array, item) \
kv_push(array, item) kv_push(array, item)

View File

@ -1169,15 +1169,17 @@ void decor_to_dict_legacy(Dict *dict, DecorInline decor, bool hl_name, Arena *ar
if (virt_lines) { if (virt_lines) {
Array all_chunks = arena_array(arena, kv_size(virt_lines->data.virt_lines)); Array all_chunks = arena_array(arena, kv_size(virt_lines->data.virt_lines));
bool virt_lines_leftcol = false; int virt_lines_flags = 0;
for (size_t i = 0; i < kv_size(virt_lines->data.virt_lines); i++) { for (size_t i = 0; i < kv_size(virt_lines->data.virt_lines); i++) {
virt_lines_leftcol = kv_A(virt_lines->data.virt_lines, i).left_col; virt_lines_flags = kv_A(virt_lines->data.virt_lines, i).flags;
Array chunks = virt_text_to_array(kv_A(virt_lines->data.virt_lines, i).line, hl_name, arena); Array chunks = virt_text_to_array(kv_A(virt_lines->data.virt_lines, i).line, hl_name, arena);
ADD(all_chunks, ARRAY_OBJ(chunks)); ADD(all_chunks, ARRAY_OBJ(chunks));
} }
PUT_C(*dict, "virt_lines", ARRAY_OBJ(all_chunks)); PUT_C(*dict, "virt_lines", ARRAY_OBJ(all_chunks));
PUT_C(*dict, "virt_lines_above", BOOLEAN_OBJ(virt_lines->flags & kVTLinesAbove)); PUT_C(*dict, "virt_lines_above", BOOLEAN_OBJ(virt_lines->flags & kVTLinesAbove));
PUT_C(*dict, "virt_lines_leftcol", BOOLEAN_OBJ(virt_lines_leftcol)); PUT_C(*dict, "virt_lines_leftcol", BOOLEAN_OBJ(virt_lines_flags & kVLLeftcol));
PUT_C(*dict, "virt_lines_overflow",
CSTR_AS_OBJ(virt_lines_flags & kVLScroll ? "scroll" : "trunc"));
priority = virt_lines->priority; priority = virt_lines->priority;
} }

View File

@ -26,7 +26,14 @@ typedef enum {
kVPosWinCol, kVPosWinCol,
} VirtTextPos; } VirtTextPos;
typedef kvec_t(struct virt_line { VirtText line; bool left_col; }) VirtLines; /// Flags for virtual lines
enum {
kVLLeftcol = 1, ///< Start at left window edge, ignoring number column, etc.
kVLScroll = 2, ///< Can scroll horizontally with 'nowrap'
// kVLWrap = 4,
};
typedef kvec_t(struct virt_line { VirtText line; int flags; }) VirtLines;
typedef uint16_t DecorPriority; typedef uint16_t DecorPriority;
#define DECOR_PRIORITY_BASE 0x1000 #define DECOR_PRIORITY_BASE 0x1000

View File

@ -231,7 +231,8 @@ static int line_putchar(buf_T *buf, const char **pp, schar_T *dest, int maxcells
} }
if (*p == TAB) { if (*p == TAB) {
cells = MIN(tabstop_padding(vcol, buf->b_p_ts, buf->b_p_vts_array), maxcells); cells = tabstop_padding(vcol, buf->b_p_ts, buf->b_p_vts_array);
cells = MIN(cells, maxcells);
} }
// When overwriting the left half of a double-width char, clear the right half. // When overwriting the left half of a double-width char, clear the right half.
@ -345,7 +346,7 @@ static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int
if (vt) { if (vt) {
int vcol = item->draw_col - col_off; int vcol = item->draw_col - col_off;
int col = draw_virt_text_item(buf, item->draw_col, vt->data.virt_text, int col = draw_virt_text_item(buf, item->draw_col, vt->data.virt_text,
vt->hl_mode, max_col, vcol); vt->hl_mode, max_col, vcol, 0);
if (do_eol && ((vt->pos == kVPosEndOfLine) || (vt->pos == kVPosEndOfLineRightAlign))) { if (do_eol && ((vt->pos == kVPosEndOfLine) || (vt->pos == kVPosEndOfLineRightAlign))) {
state->eol_col = col + 1; state->eol_col = col + 1;
} }
@ -358,32 +359,45 @@ static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int
} }
static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode, int max_col, static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode, int max_col,
int vcol) int vcol, int skip_cells)
{ {
const char *p = ""; const char *virt_str = "";
int virt_attr = 0; int virt_attr = 0;
size_t virt_pos = 0; size_t virt_pos = 0;
while (col < max_col) { while (col < max_col) {
if (!*p) { if (skip_cells >= 0 && *virt_str == NUL) {
if (virt_pos >= kv_size(vt)) { if (virt_pos >= kv_size(vt)) {
break; break;
} }
virt_attr = 0; virt_attr = 0;
p = next_virt_text_chunk(vt, &virt_pos, &virt_attr); virt_str = next_virt_text_chunk(vt, &virt_pos, &virt_attr);
if (p == NULL) { if (virt_str == NULL) {
break; break;
} }
} }
if (*p == NUL) { // Skip cells in the text.
while (skip_cells > 0 && *virt_str != NUL) {
int c_len = utfc_ptr2len(virt_str);
int cells = *virt_str == TAB
? tabstop_padding(vcol, buf->b_p_ts, buf->b_p_vts_array)
: utf_ptr2cells(virt_str);
skip_cells -= cells;
vcol += cells;
virt_str += c_len;
}
// If a double-width char or TAB doesn't fit, pad with spaces.
const char *draw_str = skip_cells < 0 ? " " : virt_str;
if (*draw_str == NUL) {
continue; continue;
} }
assert(skip_cells <= 0);
int attr; int attr;
bool through = false; bool through = false;
if (hl_mode == kHlModeCombine) { if (hl_mode == kHlModeCombine) {
attr = hl_combine_attr(linebuf_attr[col], virt_attr); attr = hl_combine_attr(linebuf_attr[col], virt_attr);
} else if (hl_mode == kHlModeBlend) { } else if (hl_mode == kHlModeBlend) {
through = (*p == ' '); through = (*draw_str == ' ');
attr = hl_blend_attrs(linebuf_attr[col], virt_attr, &through); attr = hl_blend_attrs(linebuf_attr[col], virt_attr, &through);
} else { } else {
attr = virt_attr; attr = virt_attr;
@ -397,13 +411,18 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode,
// Clear the right half as well for the assertion in line_putchar(). // Clear the right half as well for the assertion in line_putchar().
linebuf_char[col] = schar_from_ascii(' '); linebuf_char[col] = schar_from_ascii(' ');
} }
int cells = line_putchar(buf, &p, through ? dummy : &linebuf_char[col], int cells = line_putchar(buf, &draw_str, through ? dummy : &linebuf_char[col],
maxcells, vcol); maxcells, vcol);
for (int c = 0; c < cells; c++) { for (int c = 0; c < cells; c++) {
linebuf_attr[col] = attr; linebuf_attr[col] = attr;
col++; col++;
} }
vcol += cells; if (skip_cells < 0) {
skip_cells++;
} else {
vcol += cells;
virt_str = draw_str;
}
} }
return col; return col;
} }
@ -1587,8 +1606,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
const bool may_have_inline_virt const bool may_have_inline_virt
= !has_foldtext && buf_meta_total(wp->w_buffer, kMTMetaInline) > 0; = !has_foldtext && buf_meta_total(wp->w_buffer, kMTMetaInline) > 0;
int virt_line_index; int virt_line_index = -1;
int virt_line_offset = -1; int virt_line_flags = 0;
// Repeat for the whole displayed line. // Repeat for the whole displayed line.
while (true) { while (true) {
int has_match_conc = 0; ///< match wants to conceal int has_match_conc = 0; ///< match wants to conceal
@ -1616,11 +1635,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
if (index > 0) { if (index > 0) {
virt_line_index = (int)kv_size(virt_lines) - index; virt_line_index = (int)kv_size(virt_lines) - index;
assert(virt_line_index >= 0); assert(virt_line_index >= 0);
virt_line_offset = kv_A(virt_lines, virt_line_index).left_col ? 0 : win_col_off(wp); virt_line_flags = kv_A(virt_lines, virt_line_index).flags;
} }
} }
if (virt_line_offset == 0) { if (virt_line_index >= 0 && (virt_line_flags & kVLLeftcol)) {
// skip columns // skip columns
} else if (statuscol.draw) { } else if (statuscol.draw) {
// Draw 'statuscolumn' if it is set. // Draw 'statuscolumn' if it is set.
@ -2715,7 +2734,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
} }
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, 0);
} }
draw_virt_text(wp, buf, win_col_offset, &wlv.col, wlv.row); draw_virt_text(wp, buf, win_col_offset, &wlv.col, wlv.row);
// Set increasing virtual columns in grid->vcols[] to set correct curswant // Set increasing virtual columns in grid->vcols[] to set correct curswant
@ -2923,7 +2942,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
end_check: end_check:
// At end of screen line and there is more to come: Display the line // At end of screen line and there is more to come: Display the line
// so far. If there is no more to display it is caught above. // so far. If there is no more to display it is caught above.
if (wlv.col >= grid->cols && (!has_foldtext || virt_line_offset >= 0) if (wlv.col >= grid->cols && (!has_foldtext || virt_line_index >= 0)
&& (wlv.col <= leftcols_width && (wlv.col <= leftcols_width
|| *ptr != NUL || *ptr != NUL
|| wlv.filler_todo > 0 || wlv.filler_todo > 0
@ -2956,9 +2975,14 @@ end_check:
} }
} }
if (virt_line_offset >= 0) { if (virt_line_index >= 0) {
draw_virt_text_item(buf, virt_line_offset, kv_A(virt_lines, virt_line_index).line, draw_virt_text_item(buf,
kHlModeReplace, grid->cols, 0); virt_line_flags & kVLLeftcol ? 0 : win_col_offset,
kv_A(virt_lines, virt_line_index).line,
kHlModeReplace,
grid->cols,
0,
virt_line_flags & kVLScroll ? wp->w_leftcol : 0);
} else if (wlv.filler_todo <= 0) { } else if (wlv.filler_todo <= 0) {
draw_virt_text(wp, buf, win_col_offset, &draw_col, wlv.row); draw_virt_text(wp, buf, win_col_offset, &draw_col, wlv.row);
} }
@ -3008,7 +3032,8 @@ end_check:
statuscol.draw = false; // don't draw status column if "n" is in 'cpo' statuscol.draw = false; // don't draw status column if "n" is in 'cpo'
} }
wlv.filler_todo--; wlv.filler_todo--;
virt_line_offset = -1; virt_line_index = -1;
virt_line_flags = 0;
// When the filler lines are actually below the last line of the // When the filler lines are actually below the last line of the
// file, don't draw the line itself, break here. // file, don't draw the line itself, break here.
if (wlv.filler_todo == 0 && (wp->w_botfill || end_fill)) { if (wlv.filler_todo == 0 && (wp->w_botfill || end_fill)) {

View File

@ -123,6 +123,10 @@ describe('API/extmarks', function()
pcall_err(set_extmark, ns, marks[2], 0, 0, { hl_mode = 0 }) pcall_err(set_extmark, ns, marks[2], 0, 0, { hl_mode = 0 })
) )
eq("Invalid 'hl_mode': 'foo'", pcall_err(set_extmark, ns, marks[2], 0, 0, { hl_mode = 'foo' })) eq("Invalid 'hl_mode': 'foo'", pcall_err(set_extmark, ns, marks[2], 0, 0, { hl_mode = 'foo' }))
eq(
"Invalid 'virt_lines_overflow': 'foo'",
pcall_err(set_extmark, ns, marks[2], 0, 0, { virt_lines_overflow = 'foo' })
)
eq( eq(
"Invalid 'id': expected Integer, got Array", "Invalid 'id': expected Integer, got Array",
pcall_err(set_extmark, ns, {}, 0, 0, { end_col = 1, end_row = 1 }) pcall_err(set_extmark, ns, {}, 0, 0, { end_col = 1, end_row = 1 })
@ -1576,11 +1580,6 @@ describe('API/extmarks', function()
virt_text_hide = true, virt_text_hide = true,
virt_text_pos = 'right_align', virt_text_pos = 'right_align',
}) })
set_extmark(ns, marks[2], 0, 0, {
priority = 0,
virt_text = { { '', 'Macro' }, { '', { 'Type', 'Search' } }, { '' } },
virt_text_win_col = 1,
})
eq({ eq({
0, 0,
0, 0,
@ -1607,12 +1606,20 @@ describe('API/extmarks', function()
}, },
virt_lines_above = true, virt_lines_above = true,
virt_lines_leftcol = true, virt_lines_leftcol = true,
virt_lines_overflow = 'trunc',
virt_text = { { 'text', 'Macro' }, { '???' }, { 'stack', { 'Type', 'Search' } } }, virt_text = { { 'text', 'Macro' }, { '???' }, { 'stack', { 'Type', 'Search' } } },
virt_text_repeat_linebreak = false, virt_text_repeat_linebreak = false,
virt_text_hide = true, virt_text_hide = true,
virt_text_pos = 'right_align', virt_text_pos = 'right_align',
}, },
}, get_extmark_by_id(ns, marks[1], { details = true })) }, get_extmark_by_id(ns, marks[1], { details = true }))
set_extmark(ns, marks[2], 0, 0, {
priority = 0,
virt_text = { { '', 'Macro' }, { '', { 'Type', 'Search' } }, { '' } },
virt_text_repeat_linebreak = true,
virt_text_win_col = 1,
})
eq({ eq({
0, 0,
0, 0,
@ -1621,13 +1628,35 @@ describe('API/extmarks', function()
right_gravity = true, right_gravity = true,
priority = 0, priority = 0,
virt_text = { { '', 'Macro' }, { '', { 'Type', 'Search' } }, { '' } }, virt_text = { { '', 'Macro' }, { '', { 'Type', 'Search' } }, { '' } },
virt_text_repeat_linebreak = false, virt_text_repeat_linebreak = true,
virt_text_hide = false, virt_text_hide = false,
virt_text_pos = 'win_col', virt_text_pos = 'win_col',
virt_text_win_col = 1, virt_text_win_col = 1,
}, },
}, get_extmark_by_id(ns, marks[2], { details = true })) }, get_extmark_by_id(ns, marks[2], { details = true }))
set_extmark(ns, marks[3], 0, 0, { cursorline_hl_group = 'Statement' })
set_extmark(ns, marks[3], 0, 0, {
priority = 0,
ui_watched = true,
virt_lines = { { { '', 'Macro' }, { '' }, { '', '' } } },
virt_lines_overflow = 'scroll',
})
eq({
0,
0,
{
ns_id = 1,
right_gravity = true,
ui_watched = true,
priority = 0,
virt_lines = { { { '', 'Macro' }, { '' }, { '', '' } } },
virt_lines_above = false,
virt_lines_leftcol = false,
virt_lines_overflow = 'scroll',
},
}, get_extmark_by_id(ns, marks[3], { details = true }))
set_extmark(ns, marks[4], 0, 0, { cursorline_hl_group = 'Statement' })
eq({ eq({
0, 0,
0, 0,
@ -1637,8 +1666,9 @@ describe('API/extmarks', function()
priority = 4096, priority = 4096,
right_gravity = true, right_gravity = true,
}, },
}, get_extmark_by_id(ns, marks[3], { details = true })) }, get_extmark_by_id(ns, marks[4], { details = true }))
set_extmark(ns, marks[4], 0, 0, {
set_extmark(ns, marks[5], 0, 0, {
end_col = 1, end_col = 1,
conceal = 'a', conceal = 'a',
spell = true, spell = true,
@ -1655,8 +1685,9 @@ describe('API/extmarks', function()
right_gravity = true, right_gravity = true,
spell = true, spell = true,
}, },
}, get_extmark_by_id(ns, marks[4], { details = true })) }, get_extmark_by_id(ns, marks[5], { details = true }))
set_extmark(ns, marks[5], 0, 0, {
set_extmark(ns, marks[6], 0, 0, {
end_col = 1, end_col = 1,
spell = false, spell = false,
}) })
@ -1671,7 +1702,8 @@ describe('API/extmarks', function()
right_gravity = true, right_gravity = true,
spell = false, spell = false,
}, },
}, get_extmark_by_id(ns, marks[5], { details = true })) }, get_extmark_by_id(ns, marks[6], { details = true }))
api.nvim_buf_clear_namespace(0, ns, 0, -1) api.nvim_buf_clear_namespace(0, ns, 0, -1)
-- legacy sign mark includes sign name -- legacy sign mark includes sign name
command('sign define sign1 text=s1 texthl=Title linehl=LineNR numhl=Normal culhl=CursorLine') command('sign define sign1 text=s1 texthl=Title linehl=LineNR numhl=Normal culhl=CursorLine')

View File

@ -4949,7 +4949,6 @@ if (h->n_buckets < new_n_buckets) { // expand
]]} ]]}
end) end)
it('works with hard TABs', function() it('works with hard TABs', function()
insert(example_text2) insert(example_text2)
feed 'gg' feed 'gg'
@ -5020,6 +5019,140 @@ if (h->n_buckets < new_n_buckets) { // expand
]]} ]]}
end) end)
it('scrolls horizontally with virt_lines_overflow = "scroll" #31000', function()
command('set nowrap signcolumn=yes')
insert('abcdefghijklmnopqrstuvwxyz')
api.nvim_buf_set_extmark(0, ns, 0, 0, {
virt_lines = {
{ { '12αβ̳γ̲口=', 'Special' }, { '345678', 'Special' } },
{ { '123\t45\t678', 'NonText' } },
},
virt_lines_overflow = 'scroll',
})
screen:expect([[
{7: }abcdefghijklmnopqrstuvwxy^z |
{7: }{16:12αβ̳γ̲口=❤345678} |
{7: }{1:123 45 678} |
{1:~ }|*8
|
]])
feed('zl')
screen:expect([[
{7: }bcdefghijklmnopqrstuvwxy^z |
{7: }{16:2αβ̳γ̲口=❤345678} |
{7: }{1:23 45 678} |
{1:~ }|*8
|
]])
feed('zl')
screen:expect([[
{7: }cdefghijklmnopqrstuvwxy^z |
{7: }{16:αβ̳γ̲口=❤345678} |
{7: }{1:3 45 678} |
{1:~ }|*8
|
]])
feed('zl')
screen:expect([[
{7: }defghijklmnopqrstuvwxy^z |
{7: }{16:β̳γ̲口=❤345678} |
{7: }{1: 45 678} |
{1:~ }|*8
|
]])
feed('zl')
screen:expect([[
{7: }efghijklmnopqrstuvwxy^z |
{7: }{16:γ̲口=❤345678} |
{7: }{1: 45 678} |
{1:~ }|*8
|
]])
feed('zl')
screen:expect([[
{7: }fghijklmnopqrstuvwxy^z |
{7: }{16:口=❤345678} |
{7: }{1: 45 678} |
{1:~ }|*8
|
]])
feed('zl')
screen:expect([[
{7: }ghijklmnopqrstuvwxy^z |
{7: }{16: =❤345678} |
{7: }{1: 45 678} |
{1:~ }|*8
|
]])
feed('zl')
screen:expect([[
{7: }hijklmnopqrstuvwxy^z |
{7: }{16:=❤345678} |
{7: }{1: 45 678} |
{1:~ }|*8
|
]])
feed('zl')
screen:expect([[
{7: }ijklmnopqrstuvwxy^z |
{7: }{16:❤345678} |
{7: }{1:45 678} |
{1:~ }|*8
|
]])
feed('zl')
screen:expect([[
{7: }jklmnopqrstuvwxy^z |
{7: }{16: 345678} |
{7: }{1:5 678} |
{1:~ }|*8
|
]])
feed('zl')
screen:expect([[
{7: }klmnopqrstuvwxy^z |
{7: }{16:345678} |
{7: }{1: 678} |
{1:~ }|*8
|
]])
feed('zl')
screen:expect([[
{7: }lmnopqrstuvwxy^z |
{7: }{16:45678} |
{7: }{1: 678} |
{1:~ }|*8
|
]])
feed('zl')
screen:expect([[
{7: }mnopqrstuvwxy^z |
{7: }{16:5678} |
{7: }{1: 678} |
{1:~ }|*8
|
]])
api.nvim_buf_set_extmark(0, ns, 0, 1, {
virt_lines = { { { '123\t45\t67', 'NonText' } } },
virt_lines_leftcol = true,
virt_lines_overflow = 'trunc',
})
api.nvim_buf_set_extmark(0, ns, 0, 2, {
virt_lines = { { { '123\t45\t6', 'NonText' } } },
virt_lines_leftcol = false,
virt_lines_overflow = 'trunc',
})
screen:expect([[
{7: }mnopqrstuvwxy^z |
{7: }{16:5678} |
{7: }{1: 678} |
{1:123 45 67} |
{7: }{1:123 45 6} |
{1:~ }|*6
|
]])
end)
it('does not show twice if end_row or end_col is specified #18622', function() it('does not show twice if end_row or end_col is specified #18622', function()
screen:try_resize(50, 8) screen:try_resize(50, 8)
insert([[ insert([[