diff options
author | Björn Linse <bjorn.linse@gmail.com> | 2021-03-10 13:28:07 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-03-10 13:28:07 +0100 |
commit | 7844ca2e0501e1427cd18ece99f0baf63497df72 (patch) | |
tree | 0e7a049ace119b311d6f4238e4b7ab8cec96ed11 | |
parent | 0869cbd55c29ee02a2aeecc0fde3d19f09d5002e (diff) | |
parent | 425bc438ae1ae6bb207dcc56186be952049f9149 (diff) | |
download | rneovim-7844ca2e0501e1427cd18ece99f0baf63497df72.tar.gz rneovim-7844ca2e0501e1427cd18ece99f0baf63497df72.tar.bz2 rneovim-7844ca2e0501e1427cd18ece99f0baf63497df72.zip |
Merge pull request #14065 from bfredl/overlaymode
more virt_text display options
-rw-r--r-- | src/nvim/api/buffer.c | 79 | ||||
-rw-r--r-- | src/nvim/decoration.c | 9 | ||||
-rw-r--r-- | src/nvim/decoration.h | 15 | ||||
-rw-r--r-- | src/nvim/screen.c | 42 | ||||
-rw-r--r-- | test/functional/ui/decorations_spec.lua | 243 |
5 files changed, 290 insertions, 98 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 2c2e8a024f..66c4454f7b 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -1430,6 +1430,18 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, /// - "eol": right after eol character (default) /// - "overlay": display over the specified column, without /// shifting the underlying text. +/// - virt_text_hide : hide the virtual text when the background +/// text is selected or hidden due to +/// horizontal scroll 'nowrap' +/// - 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. +/// - "replace": only show the virt_text color. This is the +/// default +/// - "combine": combine with background text color +/// - "blend": blend with background text color. +/// /// - ephemeral : for use with |nvim_set_decoration_provider| /// callbacks. The mark will only be used for the current /// redraw cycle, and not be permantently stored in the @@ -1477,11 +1489,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, bool ephemeral = false; uint64_t id = 0; - int line2 = -1, hl_id = 0; - DecorPriority priority = DECOR_PRIORITY_BASE; + int line2 = -1; + Decoration decor = DECORATION_INIT; colnr_T col2 = -1; - VirtText virt_text = KV_INITIAL_VALUE; - VirtTextPos virt_text_pos = kVTEndOfLine; + bool right_gravity = true; bool end_right_gravity = false; bool end_gravity_set = false; @@ -1528,12 +1539,12 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, switch (v->type) { case kObjectTypeString: hl_group = v->data.string; - hl_id = syn_check_group( + decor.hl_id = syn_check_group( (char_u *)(hl_group.data), (int)hl_group.size); break; case kObjectTypeInteger: - hl_id = (int)v->data.integer; + decor.hl_id = (int)v->data.integer; break; default: api_set_error(err, kErrorTypeValidation, @@ -1546,7 +1557,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, "virt_text is not an Array"); goto error; } - virt_text = parse_virt_text(v->data.array, err); + decor.virt_text = parse_virt_text(v->data.array, err); if (ERROR_SET(err)) { goto error; } @@ -1558,9 +1569,33 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, } String str = v->data.string; if (strequal("eol", str.data)) { - virt_text_pos = kVTEndOfLine; + decor.virt_text_pos = kVTEndOfLine; } else if (strequal("overlay", str.data)) { - virt_text_pos = kVTOverlay; + decor.virt_text_pos = kVTOverlay; + } else { + api_set_error(err, kErrorTypeValidation, + "virt_text_pos: invalid value"); + goto error; + } + } else if (strequal("virt_text_hide", k.data)) { + decor.virt_text_hide = api_object_to_bool(*v, + "virt_text_hide", false, err); + if (ERROR_SET(err)) { + goto error; + } + } else if (strequal("hl_mode", k.data)) { + if (v->type != kObjectTypeString) { + api_set_error(err, kErrorTypeValidation, + "hl_mode is not a String"); + goto error; + } + String str = v->data.string; + if (strequal("replace", str.data)) { + decor.hl_mode = kHlModeReplace; + } else if (strequal("combine", str.data)) { + decor.hl_mode = kHlModeCombine; + } else if (strequal("blend", str.data)) { + decor.hl_mode = kHlModeBlend; } else { api_set_error(err, kErrorTypeValidation, "virt_text_pos: invalid value"); @@ -1583,7 +1618,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, "priority is not a valid value"); goto error; } - priority = (DecorPriority)v->data.integer; + decor.priority = (DecorPriority)v->data.integer; } else if (strequal("right_gravity", k.data)) { if (v->type != kObjectTypeBoolean) { api_set_error(err, kErrorTypeValidation, @@ -1631,23 +1666,23 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, col2 = 0; } - Decoration *decor = NULL, tmp = { 0 }; + Decoration *d = NULL; - if (kv_size(virt_text) || priority != DECOR_PRIORITY_BASE) { + if (ephemeral) { + d = &decor; + } else if (kv_size(decor.virt_text) + || decor.priority != DECOR_PRIORITY_BASE) { // TODO(bfredl): this is a bit sketchy. eventually we should // have predefined decorations for both marks/ephemerals - decor = ephemeral ? &tmp : xcalloc(1, sizeof(*decor)); - decor->hl_id = hl_id; - decor->virt_text = virt_text; - decor->priority = priority; - decor->virt_text_pos = virt_text_pos; - } else if (hl_id) { - decor = decor_hl(hl_id); + d = xcalloc(1, sizeof(*d)); + *d = decor; + } else if (decor.hl_id) { + d = decor_hl(decor.hl_id); } // TODO(bfredl): synergize these two branches even more if (ephemeral && decor_state.buf == buf) { - decor_add_ephemeral((int)line, (int)col, line2, col2, decor, 0); + decor_add_ephemeral((int)line, (int)col, line2, col2, &decor, 0); } else { if (ephemeral) { api_set_error(err, kErrorTypeException, "not yet implemented"); @@ -1655,14 +1690,14 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, } id = extmark_set(buf, (uint64_t)ns_id, id, (int)line, (colnr_T)col, - line2, col2, decor, right_gravity, + line2, col2, d, right_gravity, end_right_gravity, kExtmarkNoUndo); } return (Integer)id; error: - clear_virttext(&virt_text); + clear_virttext(&decor.virt_text); return 0; } diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index a1289f202a..9a20b06660 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -230,7 +230,7 @@ static void decor_add(DecorState *state, int start_row, int start_col, HlRange range = { start_row, start_col, end_row, end_col, attr_id, MAX(priority, decor->priority), kv_size(decor->virt_text) ? &decor->virt_text : NULL, - decor->virt_text_pos, + decor->virt_text_pos, decor->virt_text_hide, decor->hl_mode, kv_size(decor->virt_text) && owned, -1 }; kv_pushp(state->active); @@ -245,7 +245,8 @@ static void decor_add(DecorState *state, int start_row, int start_col, kv_A(state->active, index) = range; } -int decor_redraw_col(buf_T *buf, int col, int virt_col, DecorState *state) +int decor_redraw_col(buf_T *buf, int col, int virt_col, bool hidden, + DecorState *state) { if (col <= state->col_until) { return state->current; @@ -324,7 +325,7 @@ next_mark: } if ((item.start_row == state->row && item.start_col <= col) && item.virt_text && item.virt_col == -1) { - item.virt_col = virt_col; + item.virt_col = (item.virt_text_hide && hidden) ? -2 : virt_col; } if (keep) { kv_A(state->active, j++) = item; @@ -345,7 +346,7 @@ void decor_redraw_end(DecorState *state) VirtText *decor_redraw_virt_text(buf_T *buf, DecorState *state) { - decor_redraw_col(buf, MAXCOL, MAXCOL, state); + decor_redraw_col(buf, MAXCOL, MAXCOL, false, state); for (size_t i = 0; i < kv_size(state->active); i++) { HlRange item = kv_A(state->active, i); if (item.start_row == state->row && item.virt_text diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index 47bd9abbc3..264e8a4a82 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -23,15 +23,26 @@ typedef enum { kVTOverlay, } VirtTextPos; +typedef enum { + kHlModeUnknown, + kHlModeReplace, + kHlModeCombine, + kHlModeBlend, +} HlMode; + struct Decoration { int hl_id; // highlight group VirtText virt_text; VirtTextPos virt_text_pos; + bool virt_text_hide; + HlMode hl_mode; // TODO(bfredl): style, signs, etc DecorPriority priority; bool shared; // shared decoration, don't free }; +#define DECORATION_INIT { 0, KV_INITIAL_VALUE, kVTEndOfLine, false, \ + kHlModeUnknown, DECOR_PRIORITY_BASE, false } typedef struct { int start_row; @@ -39,9 +50,13 @@ typedef struct { int end_row; int end_col; int attr_id; + // TODO(bfredl): embed decoration instead, perhaps using an arena + // for ephemerals? DecorPriority priority; VirtText *virt_text; VirtTextPos virt_text_pos; + bool virt_text_hide; + HlMode hl_mode; bool virt_text_owned; int virt_col; } HlRange; diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 47f1cb6423..aa3a7ae7ed 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2096,6 +2096,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, char_u buf_fold[FOLD_TEXT_LEN + 1]; // Hold value returned by get_foldtext + bool area_active = false; + /* draw_state: items that are drawn in sequence: */ #define WL_START 0 /* nothing done yet */ # define WL_CMDLINE WL_START + 1 /* cmdline window column */ @@ -2850,6 +2852,12 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, if (draw_state == WL_LINE - 1 && n_extra == 0) { sign_idx = 0; draw_state = WL_LINE; + + if (has_decor && row == startrow + filler_lines) { + // hide virt_text on text hidden by 'nowrap' + decor_redraw_col(wp->w_buffer, vcol, off, true, &decor_state); + } + if (saved_n_extra) { /* Continue item from end of wrapped line. */ n_extra = saved_n_extra; @@ -2934,10 +2942,14 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, && vcol_prev < vcol // not at margin && vcol < tocol)) { area_attr = attr; // start highlighting + if (area_highlighting) { + area_active = true; + } } else if (area_attr != 0 && (vcol == tocol || (noinvcur && (colnr_T)vcol == wp->w_virtcol))) { area_attr = 0; // stop highlighting + area_active = false; } if (!n_extra) { @@ -3397,9 +3409,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, char_attr = hl_combine_attr(spell_attr, char_attr); } + if (wp->w_buffer->terminal) { + char_attr = hl_combine_attr(term_attrs[vcol], char_attr); + } + if (has_decor && v > 0) { + bool selected = (area_active || (area_highlighting && noinvcur + && (colnr_T)vcol == wp->w_virtcol)); int extmark_attr = decor_redraw_col(wp->w_buffer, (colnr_T)v-1, off, - &decor_state); + selected, &decor_state); if (extmark_attr != 0) { if (!attr_pri) { char_attr = hl_combine_attr(char_attr, extmark_attr); @@ -3409,10 +3427,6 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } } - if (wp->w_buffer->terminal) { - char_attr = hl_combine_attr(term_attrs[vcol], char_attr); - } - // Found last space before word: check for line break. if (wp->w_p_lbr && c0 == c && vim_isbreak(c) && !vim_isbreak((int)(*ptr))) { @@ -4355,10 +4369,22 @@ void draw_virt_text(buf_T *buf, int *end_col, int max_col) virt_pos++; continue; } - int cells = line_putchar(&s, &linebuf_char[col], 2, false); - linebuf_attr[col++] = virt_attr; + int attr; + bool through = false; + if (item->hl_mode == kHlModeCombine) { + attr = hl_combine_attr(linebuf_attr[col], virt_attr); + } else if (item->hl_mode == kHlModeBlend) { + through = (*s.p == ' '); + attr = hl_blend_attrs(linebuf_attr[col], virt_attr, &through); + } else { + attr = virt_attr; + } + schar_T dummy[2]; + int cells = line_putchar(&s, through ? dummy : &linebuf_char[col], + max_col-col, false); + linebuf_attr[col++] = attr; if (cells > 1) { - linebuf_attr[col++] = virt_attr; + linebuf_attr[col++] = attr; } } *end_col = MAX(*end_col, col); diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 7a87521a6b..6cf549909a 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -8,6 +8,7 @@ local exec_lua = helpers.exec_lua local exec = helpers.exec local expect_events = helpers.expect_events local meths = helpers.meths +local command = helpers.command describe('decorations providers', function() local screen @@ -314,11 +315,30 @@ describe('extmark decorations', function() [2] = {foreground = Screen.colors.Brown}; [3] = {bold = true, foreground = Screen.colors.SeaGreen}; [4] = {background = Screen.colors.Red1, foreground = Screen.colors.Gray100}; + [5] = {foreground = Screen.colors.Brown, bold = true}; + [6] = {foreground = Screen.colors.DarkCyan}; + [7] = {foreground = Screen.colors.Grey0, background = tonumber('0xff4c4c')}; + [8] = {foreground = tonumber('0x180606'), background = tonumber('0xff4c4c')}; + [9] = {foreground = tonumber('0xe40c0c'), background = tonumber('0xff4c4c'), bold = true}; + [10] = {foreground = tonumber('0xb20000'), background = tonumber('0xff4c4c')}; + [11] = {blend = 30, background = Screen.colors.Red1}; + [12] = {foreground = Screen.colors.Brown, blend = 30, background = Screen.colors.Red1, bold = true}; + [13] = {foreground = Screen.colors.Fuchsia}; + [14] = {background = Screen.colors.Red1, foreground = Screen.colors.Black}; + [15] = {background = Screen.colors.Red1, foreground = tonumber('0xb20000')}; + [16] = {blend = 30, background = Screen.colors.Red1, foreground = Screen.colors.Magenta1}; + [17] = {bold = true, foreground = Screen.colors.Brown, background = Screen.colors.LightGrey}; + [18] = {background = Screen.colors.LightGrey}; + [19] = {foreground = Screen.colors.Cyan4, background = Screen.colors.LightGrey}; + [20] = {foreground = tonumber('0x180606'), background = tonumber('0xf13f3f')}; + [21] = {foreground = Screen.colors.Gray0, background = tonumber('0xf13f3f')}; + [22] = {foreground = tonumber('0xb20000'), background = tonumber('0xf13f3f')}; + [23] = {foreground = Screen.colors.Magenta1, background = Screen.colors.LightGrey}; + [24] = {bold = true}; } end) - it('can have virtual text of overlay style', function() - insert [[ + local example_text = [[ for _,item in ipairs(items) do local text, hl_id_cell, count = unpack(item) if hl_id_cell ~= nil then @@ -331,69 +351,164 @@ for _,item in ipairs(items) do colpos = colpos+1 end end]] - feed 'gg' - local ns = meths.create_namespace 'test' - for i = 1,9 do - meths.buf_set_extmark(0, ns, i, 0, { virt_text={{'|', 'LineNr'}}, virt_text_pos='overlay'}) - if i == 3 or (i >= 6 and i <= 9) then - meths.buf_set_extmark(0, ns, i, 4, { virt_text={{'|', 'NonText'}}, virt_text_pos='overlay'}) + it('can have virtual text of overlay position', function() + insert(example_text) + feed 'gg' + + local ns = meths.create_namespace 'test' + for i = 1,9 do + meths.buf_set_extmark(0, ns, i, 0, { virt_text={{'|', 'LineNr'}}, virt_text_pos='overlay'}) + if i == 3 or (i >= 6 and i <= 9) then + meths.buf_set_extmark(0, ns, i, 4, { virt_text={{'|', 'NonText'}}, virt_text_pos='overlay'}) + end end - end - meths.buf_set_extmark(0, ns, 9, 10, { virt_text={{'foo'}, {'bar', 'MoreMsg'}, {'!!', 'ErrorMsg'}}, virt_text_pos='overlay'}) - - -- can "float" beyond end of line - meths.buf_set_extmark(0, ns, 5, 28, { virt_text={{'loopy', 'ErrorMsg'}}, virt_text_pos='overlay'}) - -- bound check: right edge of window - meths.buf_set_extmark(0, ns, 2, 26, { virt_text={{'bork bork bork ' }, {'bork bork bork', 'ErrorMsg'}}, virt_text_pos='overlay'}) - - screen:expect{grid=[[ - ^for _,item in ipairs(items) do | - {2:|} local text, hl_id_cell, count = unpack(item) | - {2:|} if hl_id_cell ~= nil tbork bork bork {4:bork bork}| - {2:|} {1:|} hl_id = hl_id_cell | - {2:|} end | - {2:|} for _ = 1, (count or 1) {4:loopy} | - {2:|} {1:|} local cell = line[colpos] | - {2:|} {1:|} cell.text = text | - {2:|} {1:|} cell.hl_id = hl_id | - {2:|} {1:|} cofoo{3:bar}{4:!!}olpos+1 | - end | - end | - {1:~ }| - {1:~ }| - | - ]]} - - - -- handles broken lines - screen:try_resize(22, 25) - screen:expect{grid=[[ - ^for _,item in ipairs(i| - tems) do | - {2:|} local text, hl_id_| - cell, count = unpack(i| - tem) | - {2:|} if hl_id_cell ~= n| - il tbork bork bork {4:bor}| - {2:|} {1:|} hl_id = hl_id_| - cell | - {2:|} end | - {2:|} for _ = 1, (count | - or 1) {4:loopy} | - {2:|} {1:|} local cell = l| - ine[colpos] | - {2:|} {1:|} cell.text = te| - xt | - {2:|} {1:|} cell.hl_id = h| - l_id | - {2:|} {1:|} cofoo{3:bar}{4:!!}olpo| - s+1 | - end | - end | - {1:~ }| - {1:~ }| - | - ]]} + meths.buf_set_extmark(0, ns, 9, 10, { virt_text={{'foo'}, {'bar', 'MoreMsg'}, {'!!', 'ErrorMsg'}}, virt_text_pos='overlay'}) + + -- can "float" beyond end of line + meths.buf_set_extmark(0, ns, 5, 28, { virt_text={{'loopy', 'ErrorMsg'}}, virt_text_pos='overlay'}) + -- bound check: right edge of window + meths.buf_set_extmark(0, ns, 2, 26, { virt_text={{'bork bork bork ' }, {'bork bork bork', 'ErrorMsg'}}, virt_text_pos='overlay'}) + + screen:expect{grid=[[ + ^for _,item in ipairs(items) do | + {2:|} local text, hl_id_cell, count = unpack(item) | + {2:|} if hl_id_cell ~= nil tbork bork bork {4:bork bork}| + {2:|} {1:|} hl_id = hl_id_cell | + {2:|} end | + {2:|} for _ = 1, (count or 1) {4:loopy} | + {2:|} {1:|} local cell = line[colpos] | + {2:|} {1:|} cell.text = text | + {2:|} {1:|} cell.hl_id = hl_id | + {2:|} {1:|} cofoo{3:bar}{4:!!}olpos+1 | + end | + end | + {1:~ }| + {1:~ }| + | + ]]} + + + -- handles broken lines + screen:try_resize(22, 25) + screen:expect{grid=[[ + ^for _,item in ipairs(i| + tems) do | + {2:|} local text, hl_id_| + cell, count = unpack(i| + tem) | + {2:|} if hl_id_cell ~= n| + il tbork bork bork {4:bor}| + {2:|} {1:|} hl_id = hl_id_| + cell | + {2:|} end | + {2:|} for _ = 1, (count | + or 1) {4:loopy} | + {2:|} {1:|} local cell = l| + ine[colpos] | + {2:|} {1:|} cell.text = te| + xt | + {2:|} {1:|} cell.hl_id = h| + l_id | + {2:|} {1:|} cofoo{3:bar}{4:!!}olpo| + s+1 | + end | + end | + {1:~ }| + {1:~ }| + | + ]]} + end) + + it('can have virtual text of overlay position and styling', function() + insert(example_text) + feed 'gg' + local ns = meths.create_namespace 'test' + + command 'set ft=lua' + command 'syntax on' + + screen:expect{grid=[[ + {5:^for} _,item {5:in} {6:ipairs}(items) {5:do} | + {5:local} text, hl_id_cell, count = unpack(item) | + {5:if} hl_id_cell ~= {13:nil} {5:then} | + hl_id = hl_id_cell | + {5:end} | + {5:for} _ = {13:1}, (count {5:or} {13:1}) {5:do} | + {5:local} cell = line[colpos] | + cell.text = text | + cell.hl_id = hl_id | + colpos = colpos+{13:1} | + {5:end} | + {5:end} | + {1:~ }| + {1:~ }| + | + ]]} + + command 'hi Blendy guibg=Red blend=30' + meths.buf_set_extmark(0, ns, 1, 5, { virt_text={{'blendy text - here', 'Blendy'}}, virt_text_pos='overlay', hl_mode='blend'}) + meths.buf_set_extmark(0, ns, 2, 5, { virt_text={{'combining color', 'Blendy'}}, virt_text_pos='overlay', hl_mode='combine'}) + meths.buf_set_extmark(0, ns, 3, 5, { virt_text={{'replacing color', 'Blendy'}}, virt_text_pos='overlay', hl_mode='replace'}) + + meths.buf_set_extmark(0, ns, 4, 5, { virt_text={{'blendy text - here', 'Blendy'}}, virt_text_pos='overlay', hl_mode='blend', virt_text_hide=true}) + meths.buf_set_extmark(0, ns, 5, 5, { virt_text={{'combining color', 'Blendy'}}, virt_text_pos='overlay', hl_mode='combine', virt_text_hide=true}) + meths.buf_set_extmark(0, ns, 6, 5, { virt_text={{'replacing color', 'Blendy'}}, virt_text_pos='overlay', hl_mode='replace', virt_text_hide=true}) + + screen:expect{grid=[[ + {5:^for} _,item {5:in} {6:ipairs}(items) {5:do} | + {5:l}{8:blen}{7:dy}{10:e}{7:text}{10:h}{7:-}{10:_}{7:here}ell, count = unpack(item) | + {5:i}{12:c}{11:ombining color} {13:nil} {5:then} | + {11:replacing color}d_cell | + {5:e}{8:bl}{14:endy}{15:i}{14:text}{15:o}{14:-}{15:o}{14:h}{7:ere} | + {5:f}{12:co}{11:mbini}{16:n}{11:g color}t {5:or} {13:1}) {5:do} | + {11:replacing color} line[colpos] | + cell.text = text | + cell.hl_id = hl_id | + colpos = colpos+{13:1} | + {5:end} | + {5:end} | + {1:~ }| + {1:~ }| + | + ]]} + + feed 'V5G' + screen:expect{grid=[[ + {17:for}{18: _,item }{17:in}{18: }{19:ipairs}{18:(items) }{17:do} | + {18: }{17:l}{20:blen}{21:dy}{22:e}{21:text}{22:h}{21:-}{22:_}{21:here}{18:ell, count = unpack(item)} | + {18: }{17:i}{12:c}{11:ombining color}{18: }{23:nil}{18: }{17:then} | + {18: }{11:replacing color}{18:d_cell} | + {18: }{5:^e}{17:nd} | + {5:f}{12:co}{11:mbini}{16:n}{11:g color}t {5:or} {13:1}) {5:do} | + {11:replacing color} line[colpos] | + cell.text = text | + cell.hl_id = hl_id | + colpos = colpos+{13:1} | + {5:end} | + {5:end} | + {1:~ }| + {1:~ }| + {24:-- VISUAL LINE --} | + ]]} + + feed 'jj' + screen:expect{grid=[[ + {17:for}{18: _,item }{17:in}{18: }{19:ipairs}{18:(items) }{17:do} | + {18: }{17:l}{20:blen}{21:dy}{22:e}{21:text}{22:h}{21:-}{22:_}{21:here}{18:ell, count = unpack(item)} | + {18: }{17:i}{12:c}{11:ombining color}{18: }{23:nil}{18: }{17:then} | + {18: }{11:replacing color}{18:d_cell} | + {18: }{17:end} | + {18: }{17:for}{18: _ = }{23:1}{18:, (count }{17:or}{18: }{23:1}{18:) }{17:do} | + {18: }^ {18: }{17:local}{18: cell = line[colpos]} | + cell.text = text | + cell.hl_id = hl_id | + colpos = colpos+{13:1} | + {5:end} | + {5:end} | + {1:~ }| + {1:~ }| + {24:-- VISUAL LINE --} | + ]]} end) end) |