From efa9b299a7cb68909e9bcd290e4d12bcb6d0bb03 Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Sun, 19 Mar 2023 16:31:08 +1100 Subject: feat(ui): inline virtual text vim-patch:9.0.0067: cannot show virtual text Problem: Cannot show virtual text. Solution: Initial changes for virtual text support, using text properties. https://github.com/vim/vim/commit/7f9969c559b51446632ac7e8f76cde07e7d0078d vim-patch:9.0.0116: virtual text not displayed if 'signcolumn' is "yes" Problem: Virtual text not displayed if 'signcolumn' is "yes". Solution: Set c_extra and c_final to NUL. https://github.com/vim/vim/commit/711483cd1381a4ed848d783ae0a6792d5b04447b Co-authored-by: bfredl --- src/nvim/api/extmark.c | 4 ++ src/nvim/decoration.h | 4 +- src/nvim/drawline.c | 49 +++++++++++++++++--- src/nvim/plines.c | 33 +++++++++++--- src/nvim/plines.h | 2 + test/functional/ui/decorations_spec.lua | 79 +++++++++++++++++++++++++++++++++ 6 files changed, 160 insertions(+), 11 deletions(-) diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index d6f0288f94..dd256a54f0 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -477,6 +477,8 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// - "overlay": display over the specified column, without /// shifting the underlying text. /// - "right_align": display right aligned in the window. +/// - "inline": display at the specified column, and +/// shift the buffer text to the right as needed /// - virt_text_win_col : position the virtual text at a fixed /// window column (starting from the first /// text column) @@ -695,6 +697,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer decor.virt_text_pos = kVTOverlay; } else if (strequal("right_align", str.data)) { decor.virt_text_pos = kVTRightAlign; + } else if (strequal("inline", str.data)) { + decor.virt_text_pos = kVTInline; } else { VALIDATE_S(false, "virt_text_pos", "", { goto error; diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index 92001d496d..95c9655742 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -23,9 +23,11 @@ typedef enum { kVTOverlay, kVTWinCol, kVTRightAlign, + kVTInline, } VirtTextPos; -EXTERN const char *const virt_text_pos_str[] INIT(= { "eol", "overlay", "win_col", "right_align" }); +EXTERN const char *const virt_text_pos_str[] INIT(= { "eol", "overlay", "win_col", "right_align", + "inline" }); typedef enum { kHlModeUnknown, diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 863d237062..012cce7b22 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -1030,6 +1030,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, int left_curline_col = 0; int right_curline_col = 0; + VirtText virt_inline = KV_INITIAL_VALUE; + size_t virt_inline_i = 0; + int match_conc = 0; ///< cchar for match functions bool on_last_col = false; int syntax_flags = 0; @@ -1706,7 +1709,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.n_extra = 0; } - if (wlv.draw_state == WL_LINE && (area_highlighting || has_spell)) { + int extmark_attr = 0; + if (wlv.draw_state == WL_LINE + && (area_highlighting || has_spell || (extra_check && !has_fold))) { // handle Visual or match highlighting in this line if (wlv.vcol == wlv.fromcol || (wlv.vcol + 1 == wlv.fromcol && wlv.n_extra == 0 @@ -1787,6 +1792,44 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.char_attr = 0; } } + + if (has_decor && v >= 0) { + bool selected = (area_active || (area_highlighting && noinvcur + && wlv.vcol == wp->w_virtcol)); + extmark_attr = decor_redraw_col(wp, (colnr_T)v, wlv.off, + selected, &decor_state); + + // we could already be inside an existing virt_line with multiple chunks + if (!(virt_inline_i < kv_size(virt_inline))) { + DecorState *state = &decor_state; + for (size_t i = 0; i < kv_size(state->active); i++) { + DecorRange *item = &kv_A(state->active, i); + if (!(item->start_row == state->row + && kv_size(item->decor.virt_text) + && item->decor.virt_text_pos == kVTInline)) { + continue; + } + if (item->win_col >= -1 && item->start_col <= v) { + virt_inline = item->decor.virt_text; + virt_inline_i = 0; + item->win_col = -2; + break; + } + } + } + + if (wlv.n_extra <= 0 && virt_inline_i < kv_size(virt_inline)) { + VirtTextChunk vtc = kv_A(virt_inline, virt_inline_i); + wlv.p_extra = vtc.text; + wlv.n_extra = (int)strlen(wlv.p_extra); + wlv.c_extra = NUL; + wlv.c_final = NUL; + wlv.extra_attr = vtc.hl_id ? syn_id2attr(vtc.hl_id) : 0; + n_attr = wlv.n_extra; + extmark_attr = 0; + virt_inline_i++; + } + } } // Get the next character to put on the screen. @@ -2019,10 +2062,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } if (has_decor && v > 0) { - bool selected = (area_active || (area_highlighting && noinvcur - && wlv.vcol == wp->w_virtcol)); - int extmark_attr = decor_redraw_col(wp, (colnr_T)v - 1, wlv.off, - selected, &decor_state); if (extmark_attr != 0) { if (!attr_pri) { wlv.char_attr = hl_combine_attr(wlv.char_attr, extmark_attr); diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 3e69e547cb..738057b696 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -295,17 +295,25 @@ unsigned linetabsize(win_T *wp, linenr_T lnum) /// Prepare the structure passed to chartabsize functions. /// /// "line" is the start of the line, "ptr" is the first relevant character. -/// When "lnum" is zero do not use text properties that insert text. -void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum FUNC_ATTR_UNUSED, - colnr_T col, char *line, char *ptr) +/// When "lnum" is zero do not use inline virtual text. +void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum, colnr_T col, char *line, + char *ptr) { cts->cts_win = wp; cts->cts_vcol = col; cts->cts_line = line; cts->cts_ptr = ptr; cts->cts_cur_text_width = 0; - // TODO(bfredl): actually lookup inline virtual text here cts->cts_has_virt_text = false; + cts->cts_row = lnum - 1; + + if (cts->cts_row >= 0) { + marktree_itr_get(wp->w_buffer->b_marktree, cts->cts_row, 0, cts->cts_iter); + mtkey_t mark = marktree_itr_current(cts->cts_iter); + if (mark.pos.row == cts->cts_row) { + cts->cts_has_virt_text = true; + } + } } /// Free any allocated item in "cts". @@ -383,7 +391,22 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) // First get normal size, without 'linebreak' or virtual text int size = win_chartabsize(wp, s, vcol); if (cts->cts_has_virt_text) { - // TODO(bfredl): inline virtual text + int col = (int)(s - line); + while (true) { + mtkey_t mark = marktree_itr_current(cts->cts_iter); + if (mark.pos.row != cts->cts_row || mark.pos.col > col) { + break; + } else if (mark.pos.col == col) { // TODO: or maybe unconditionally, what if byte-misaligned? + if (!mt_end(mark)) { + Decoration decor = get_decor(mark); + if (decor.virt_text_pos == kVTInline) { + cts->cts_cur_text_width = decor.virt_text_width; + size += cts->cts_cur_text_width; + } + } + } + marktree_itr_next(wp->w_buffer->b_marktree, cts->cts_iter); + } } int c = (uint8_t)(*s); diff --git a/src/nvim/plines.h b/src/nvim/plines.h index 808f6d284e..a15c234bba 100644 --- a/src/nvim/plines.h +++ b/src/nvim/plines.h @@ -11,9 +11,11 @@ typedef struct { win_T *cts_win; char *cts_line; // start of the line char *cts_ptr; // current position in line + int cts_row; bool cts_has_virt_text; // true if if a property inserts text int cts_cur_text_width; // width of current inserted text + MarkTreeIter cts_iter[1]; // TODO(bfredl): iterator in to the marktree for scanning virt text int cts_vcol; // virtual column at current position diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index ccf1810ee1..80dfebd33e 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -645,6 +645,7 @@ describe('extmark decorations', function() [25] = {background = Screen.colors.LightRed}; [26] = {background=Screen.colors.DarkGrey, foreground=Screen.colors.LightGrey}; [27] = {background = Screen.colors.Plum1}; + [28] = {foreground = Screen.colors.SlateBlue}; } ns = meths.create_namespace 'test' @@ -1166,6 +1167,84 @@ end]] screen:expect_unchanged(true) end) + it('can have virtual text of inline position', function() + insert(example_text) + feed 'gg' + screen:expect{grid=[[ + ^for _,item in ipairs(items) do | + local text, hl_id_cell, count = unpack(item) | + if hl_id_cell ~= nil then | + hl_id = hl_id_cell | + end | + for _ = 1, (count or 1) do | + local cell = line[colpos] | + cell.text = text | + cell.hl_id = hl_id | + colpos = colpos+1 | + end | + end | + {1:~ }| + {1:~ }| + | + ]]} + + meths.buf_set_extmark(0, ns, 1, 14, {virt_text={{': ', 'Special'}, {'string', 'Type'}}, virt_text_pos='inline'}) + screen:expect{grid=[[ + ^for _,item in ipairs(items) do | + local text{28:: }{3:string}, hl_id_cell, count = unpack| + (item) | + if hl_id_cell ~= nil then | + hl_id = hl_id_cell | + end | + for _ = 1, (count or 1) do | + local cell = line[colpos] | + cell.text = text | + cell.hl_id = hl_id | + colpos = colpos+1 | + end | + end | + {1:~ }| + | + ]]} + + screen:try_resize(55, 15) + screen:expect{grid=[[ + ^for _,item in ipairs(items) do | + local text{28:: }{3:string}, hl_id_cell, count = unpack(item| + ) | + if hl_id_cell ~= nil then | + hl_id = hl_id_cell | + end | + for _ = 1, (count or 1) do | + local cell = line[colpos] | + cell.text = text | + cell.hl_id = hl_id | + colpos = colpos+1 | + end | + end | + {1:~ }| + | + ]]} + + screen:try_resize(56, 15) + screen:expect{grid=[[ + ^for _,item in ipairs(items) do | + local text{28:: }{3:string}, hl_id_cell, count = unpack(item)| + if hl_id_cell ~= nil then | + hl_id = hl_id_cell | + end | + for _ = 1, (count or 1) do | + local cell = line[colpos] | + cell.text = text | + cell.hl_id = hl_id | + colpos = colpos+1 | + end | + end | + {1:~ }| + {1:~ }| + | + ]]} + end) end) describe('decorations: virtual lines', function() -- cgit From a38d7f99845d6ef566b2885737b892c660749d5e Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Sun, 19 Mar 2023 17:37:28 +1100 Subject: fix(ui): fix cursor position with multiple inline virtual text vim-patch9.0.0121: cannot put virtual text after or below a line Problem: Cannot put virtual text after or below a line. Solution: Add "text_align" and "text_wrap" arguments. https://github.com/vim/vim/commit/b7963df98f9dbbb824713acad2f47c9989fcf8f3 This only patches the fix, not the whole thing. --- src/nvim/drawline.c | 3 +-- src/nvim/plines.c | 7 ++++--- test/functional/ui/decorations_spec.lua | 27 +++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 012cce7b22..ed22f71239 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -1796,8 +1796,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, if (has_decor && v >= 0) { bool selected = (area_active || (area_highlighting && noinvcur && wlv.vcol == wp->w_virtcol)); - extmark_attr = decor_redraw_col(wp, (colnr_T)v, wlv.off, - selected, &decor_state); + extmark_attr = decor_redraw_col(wp, (colnr_T)v, wlv.off, selected, &decor_state); // we could already be inside an existing virt_line with multiple chunks if (!(virt_inline_i < kv_size(virt_inline))) { diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 738057b696..dd4955c352 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -396,12 +396,13 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) mtkey_t mark = marktree_itr_current(cts->cts_iter); if (mark.pos.row != cts->cts_row || mark.pos.col > col) { break; - } else if (mark.pos.col == col) { // TODO: or maybe unconditionally, what if byte-misaligned? + } else if (mark.pos.col + == col) { // TODO(bfredl): or maybe unconditionally, what if byte-misaligned? if (!mt_end(mark)) { Decoration decor = get_decor(mark); if (decor.virt_text_pos == kVTInline) { - cts->cts_cur_text_width = decor.virt_text_width; - size += cts->cts_cur_text_width; + cts->cts_cur_text_width += decor.virt_text_width; + size += decor.virt_text_width; } } } diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 80dfebd33e..d029a66301 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -1245,6 +1245,33 @@ end]] | ]]} end) + + it('cursor positions are correct with multiple inline virtual text', function() + insert('12345678') + meths.buf_set_extmark(0, ns, 0, 4, + { virt_text = { { ' virtual text ', 'Special' } }, virt_text_pos = 'inline' }) + meths.buf_set_extmark(0, ns, 0, 4, + { virt_text = { { ' virtual text ', 'Special' } }, virt_text_pos = 'inline' }) + feed '^' + feed '4l' + screen:expect { grid = [[ + 1234{28: virtual text virtual text }^5678 | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) end) describe('decorations: virtual lines', function() -- cgit From 0e1f3b5acf74e82ea778f3c0871a804a9ae89d4e Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Sun, 19 Mar 2023 18:32:44 +1100 Subject: vim-patch:9.0.0130: cursor position wrong when inserting around virtual text Problem: Cursor position wrong when inserting around virtual text. Solution: Update the cursor position properly. https://github.com/vim/vim/commit/1f4ee19eefecd8f70b7cbe8ee9db8ace6352e23e Co-authored-by: tom-anders <13141438+tom-anders@users.noreply.github.com> --- src/nvim/buffer_defs.h | 1 + src/nvim/decoration.c | 4 ++++ src/nvim/edit.c | 4 ++-- src/nvim/extmark.c | 3 +++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index f8b26d9d16..f3f98bbd17 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -808,6 +808,7 @@ struct file_buffer { MarkTree b_marktree[1]; Map(uint32_t, uint32_t) b_extmark_ns[1]; // extmark namespaces + size_t b_virt_text_inline; // number of inline virtual texts size_t b_virt_line_blocks; // number of virt_line blocks size_t b_signs; // number of sign extmarks size_t b_signs_with_text; // number of sign extmarks with text diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index 87e4441f32..ec11c20b3e 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -95,6 +95,10 @@ void decor_remove(buf_T *buf, int row, int row2, Decoration *decor) { decor_redraw(buf, row, row2, decor); if (decor) { + if (kv_size(decor->virt_text) && decor->virt_text_pos == kVTInline) { + assert(buf->b_virt_text_inline > 0); + buf->b_virt_text_inline--; + } if (kv_size(decor->virt_lines)) { assert(buf->b_virt_line_blocks > 0); buf->b_virt_line_blocks--; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index e3321a8b99..a25387f5a6 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -232,7 +232,7 @@ static void insert_enter(InsertState *s) stop_insert_mode = false; // need to position cursor again when on a TAB - if (gchar_cursor() == TAB) { + if (gchar_cursor() == TAB || curbuf->b_virt_text_inline > 0) { curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL); } @@ -3471,7 +3471,7 @@ static bool ins_esc(long *count, int cmdchar, bool nomove) State = MODE_NORMAL; may_trigger_modechanged(); // need to position cursor again when on a TAB - if (gchar_cursor() == TAB) { + if (gchar_cursor() == TAB || curbuf->b_virt_text_inline > 0) { curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL); } diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index acdc36f9c7..f4a6a02682 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -148,6 +148,9 @@ revised: } if (decor) { + if (kv_size(decor->virt_text) && decor->virt_text_pos == kVTInline) { + buf->b_virt_text_inline++; + } if (kv_size(decor->virt_lines)) { buf->b_virt_line_blocks++; } -- cgit From 389f5ca39d278173dc0446dc339f7ac1ff573329 Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Sun, 19 Mar 2023 18:50:45 +1100 Subject: fix(ui): adjust the cursor when inserting virtual text Credit to: Jesse Bakker https://github.com/neovim/neovim/pull/20130#issuecomment-1369652743 Co-authored-by: Jesse Bakker --- src/nvim/decoration.c | 3 +++ test/functional/ui/decorations_spec.lua | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index ec11c20b3e..ce1af290c1 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -83,6 +83,9 @@ void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor) if (decor && decor_virt_pos(*decor)) { redraw_buf_line_later(buf, row1 + 1, false); + if (decor->virt_text_pos == kVTInline) { + changed_line_display_buf(buf); + } } if (decor && kv_size(decor->virt_lines)) { diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index d029a66301..94267e35c4 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -1272,6 +1272,32 @@ end]] | ]]} end) + + it('adjusts cursor location correctly when inserting around inline virtual text', function() + insert('12345678') + feed '$' + meths.buf_set_extmark(0, ns, 0, 4, + { virt_text = { { ' virtual text ', 'Special' } }, virt_text_pos = 'inline' }) + + screen:expect { grid = [[ + 1234{28: virtual text }567^8 | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]] + } + end) end) describe('decorations: virtual lines', function() -- cgit From 0c7fa3bdcc3761cc851ea0ac37bf692b990044cc Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Sun, 19 Mar 2023 20:31:52 +1100 Subject: fix(ui): fix multi-byte characters highlight in virtual text This also fixes insert cursor position around virtual text vim-patch:9.0.0132: multi-byte characters in virtual text not handled correctly Problem: Multi-byte characters in virtual text not handled correctly. Solution: Count screen cells instead of bytes. https://github.com/vim/vim/commit/09ff4b54fb86a64390ba9c609853c6410ea6197c --- src/nvim/charset.c | 6 ++- src/nvim/drawline.c | 2 +- test/functional/ui/decorations_spec.lua | 84 ++++++++++++++++++++++++++++++--- 3 files changed, 82 insertions(+), 10 deletions(-) diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 8cae831881..48a38dd3d3 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1079,8 +1079,10 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en } if (cursor != NULL) { - // cursor is after inserted text - vcol += cts.cts_cur_text_width; + if ((State & MODE_INSERT) == 0) { + // cursor is after inserted text + vcol += cts.cts_cur_text_width; + } if ((*ptr == TAB) && (State & MODE_NORMAL) && !wp->w_p_list diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index ed22f71239..e42d912dbe 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -1824,7 +1824,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.c_extra = NUL; wlv.c_final = NUL; wlv.extra_attr = vtc.hl_id ? syn_id2attr(vtc.hl_id) : 0; - n_attr = wlv.n_extra; + n_attr = mb_charlen(vtc.text); extmark_attr = 0; virt_inline_i++; } diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 94267e35c4..6e4b19c856 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -1254,7 +1254,7 @@ end]] { virt_text = { { ' virtual text ', 'Special' } }, virt_text_pos = 'inline' }) feed '^' feed '4l' - screen:expect { grid = [[ + screen:expect { grid = [[ 1234{28: virtual text virtual text }^5678 | {1:~ }| {1:~ }| @@ -1274,12 +1274,12 @@ end]] end) it('adjusts cursor location correctly when inserting around inline virtual text', function() - insert('12345678') - feed '$' - meths.buf_set_extmark(0, ns, 0, 4, + insert('12345678') + feed '$' + meths.buf_set_extmark(0, ns, 0, 4, { virt_text = { { ' virtual text ', 'Special' } }, virt_text_pos = 'inline' }) - screen:expect { grid = [[ + screen:expect { grid = [[ 1234{28: virtual text }567^8 | {1:~ }| {1:~ }| @@ -1295,8 +1295,78 @@ end]] {1:~ }| {1:~ }| | - ]] - } + ]]} + end) + + it('has correct highlighting with multi-byte characters in inline virtual text', function() + insert('12345678') + meths.buf_set_extmark(0, ns, 0, 4, + { virt_text = { { 'múlti-byté chñröcters 修补', 'Special' } }, virt_text_pos = 'inline' }) + + screen:expect { grid = [[ + 1234{28:múlti-byté chñröcters 修补}567^8 | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + + it('has correct cursor position when inserting around virtual text', function() + insert('12345678') + meths.buf_set_extmark(0, ns, 0, 4, + { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) + feed '^' + feed '3l' + feed 'a' + screen:expect { grid = [[ + 1234{28:^virtual text}5678 | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {24:-- INSERT --} | + ]]} + feed '' + feed '^' + feed '4l' + feed 'i' + screen:expect { grid = [[ + 1234{28:^virtual text}5678 | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {24:-- INSERT --} | + ]]} end) end) -- cgit From 8eaf3c4f8c2ee4dc5a6e12bb809058ad263dbb65 Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Sun, 26 Mar 2023 00:10:28 +1100 Subject: vim-patch:9.0.0143: cursor positioned after virtual text in empty line Problem: Cursor positioned after virtual text in empty line. Solution: Keep cursor in the first column. (closes vim/vim#10786) https://github.com/vim/vim/commit/afd2aa79eda3fe69f2e7c87d0b9b4bca874f386a --- src/nvim/plines.c | 2 +- test/functional/ui/decorations_spec.lua | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/nvim/plines.c b/src/nvim/plines.c index dd4955c352..1c6ed4f4c1 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -390,7 +390,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) // First get normal size, without 'linebreak' or virtual text int size = win_chartabsize(wp, s, vcol); - if (cts->cts_has_virt_text) { + if (cts->cts_has_virt_text && *line != NUL) { int col = (int)(s - line); while (true) { mtkey_t mark = marktree_itr_current(cts->cts_iter); diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 6e4b19c856..a2b0138da3 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -1368,6 +1368,28 @@ end]] {24:-- INSERT --} | ]]} end) + + it('has correct cursor position with virtual text on an empty line', function() + meths.buf_set_extmark(0, ns, 0, 0, + { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) + screen:expect { grid = [[ + {28:^virtual text} | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) end) describe('decorations: virtual lines', function() -- cgit From 43c2eaada220d5e7d44fa9086655b00ee3e5fbb5 Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Sun, 26 Mar 2023 14:49:09 +1100 Subject: vim-patch:9.0.0179: cursor pos wrong with wrapping virtual text in empty line Problem: Cursor position wrong with wrapping virtual text in empty line. Solution: Adjust handling of an empty line. (closes vim/vim#10875) https://github.com/vim/vim/commit/49a90792d950c51608d0459ef8699fe921070718 Co-authored-by: Bram Moolenaar --- src/nvim/charset.c | 9 +++- src/nvim/plines.c | 42 ++++++++++++---- test/functional/ui/decorations_spec.lua | 86 +++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 12 deletions(-) diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 48a38dd3d3..3b48392d49 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -989,6 +989,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en } chartabsize_T cts; + bool on_NUL = false; init_chartabsize_arg(&cts, wp, pos->lnum, 0, line, line); // This function is used very often, do some speed optimizations. @@ -1054,6 +1055,10 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en if (*cts.cts_ptr == NUL) { // NUL at end of line only takes one column incr = 1; + if (cts.cts_cur_text_width > 0) { + incr = cts.cts_cur_text_width; + } + on_NUL = true; break; } @@ -1079,8 +1084,8 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en } if (cursor != NULL) { - if ((State & MODE_INSERT) == 0) { - // cursor is after inserted text + if ((State & MODE_INSERT) == 0 && !on_NUL) { + // cursor is after inserted text, unless on the NUL vcol += cts.cts_cur_text_width; } if ((*ptr == TAB) diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 1c6ed4f4c1..59c45d3615 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -102,12 +102,16 @@ int plines_win_nofold(win_T *wp, linenr_T lnum) char *s; unsigned col; int width; + chartabsize_T cts; s = ml_get_buf(wp->w_buffer, lnum, false); - if (*s == NUL) { // empty line - return 1; + init_chartabsize_arg(&cts, wp, lnum, 0, s, s); + if (*s == NUL && !cts.cts_has_virt_text) { + return 1; // be quick for an empty line } - col = win_linetabsize(wp, lnum, s, MAXCOL); + win_linetabsize_cts(&cts, (colnr_T)MAXCOL); + clear_chartabsize_arg(&cts); + col = (unsigned)cts.cts_vcol; // If list mode is on, then the '$' at the end of the line may take up one // extra column. @@ -262,6 +266,11 @@ int linetabsize_col(int startcol, char *s) while (*cts.cts_ptr != NUL) { cts.cts_vcol += lbr_chartabsize_adv(&cts); } + if (cts.cts_has_virt_text && cts.cts_ptr == cts.cts_line) { + // check for virtual text in an empty line + (void)lbr_chartabsize_adv(&cts); + cts.cts_vcol += cts.cts_cur_text_width; + } clear_chartabsize_arg(&cts); return cts.cts_vcol; } @@ -277,10 +286,7 @@ unsigned win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len) { chartabsize_T cts; init_chartabsize_arg(&cts, wp, lnum, 0, line, line); - for (; *cts.cts_ptr != NUL && (len == MAXCOL || cts.cts_ptr < line + len); - MB_PTR_ADV(cts.cts_ptr)) { - cts.cts_vcol += win_lbr_chartabsize(&cts, NULL); - } + win_linetabsize_cts(&cts, len); clear_chartabsize_arg(&cts); return (unsigned)cts.cts_vcol; } @@ -292,6 +298,20 @@ unsigned linetabsize(win_T *wp, linenr_T lnum) return win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum, false), (colnr_T)MAXCOL); } +void win_linetabsize_cts(chartabsize_T *cts, colnr_T len) +{ + for (; *cts->cts_ptr != NUL && (len == MAXCOL || cts->cts_ptr < cts->cts_line + len); + MB_PTR_ADV(cts->cts_ptr)) { + cts->cts_vcol += win_lbr_chartabsize(cts, NULL); + } + // check for a virtual text on an empty line + if (cts->cts_has_virt_text && *cts->cts_ptr == NUL + && cts->cts_ptr == cts->cts_line) { + (void)win_lbr_chartabsize(cts, NULL); + cts->cts_vcol += cts->cts_cur_text_width; + } +} + /// Prepare the structure passed to chartabsize functions. /// /// "line" is the start of the line, "ptr" is the first relevant character. @@ -390,14 +410,16 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) // First get normal size, without 'linebreak' or virtual text int size = win_chartabsize(wp, s, vcol); - if (cts->cts_has_virt_text && *line != NUL) { + if (cts->cts_has_virt_text) { + int charlen = *s == NUL ? 1 : utf_ptr2len(s); int col = (int)(s - line); while (true) { mtkey_t mark = marktree_itr_current(cts->cts_iter); if (mark.pos.row != cts->cts_row || mark.pos.col > col) { break; - } else if (mark.pos.col - == col) { // TODO(bfredl): or maybe unconditionally, what if byte-misaligned? + } else if (mark.pos.col >= col + && mark.pos.col < col + charlen) { // TODO(bfredl): or maybe unconditionally, what + // if byte-misaligned? if (!mt_end(mark)) { Decoration decor = get_decor(mark); if (decor.virt_text_pos == kVTInline) { diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index a2b0138da3..635666c941 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -1390,6 +1390,92 @@ end]] | ]]} end) + + it('text is drawn correctly when inserting a wrapping virtual text on an empty line', function() + feed('o') + insert([[aaaaaaa + +bbbbbbb]]) + meths.buf_set_extmark(0, ns, 0, 0, + { virt_text = { { string.rep('X', 51), 'Special' } }, virt_text_pos = 'inline' }) + meths.buf_set_extmark(0, ns, 2, 0, + { virt_text = { { string.rep('X', 50), 'Special' } }, virt_text_pos = 'inline' }) + feed('gg0') + screen:expect { grid = [[ + {28:^XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + {28:X} | + aaaaaaa | + {28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + bbbbbbb | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('j') + screen:expect { grid = [[ + {28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + {28:X} | + ^aaaaaaa | + {28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + bbbbbbb | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('j') + screen:expect { grid = [[ + {28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + {28:X} | + aaaaaaa | + {28:^XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + bbbbbbb | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('j') + screen:expect { grid = [[ + {28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + {28:X} | + aaaaaaa | + {28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + ^bbbbbbb | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) end) describe('decorations: virtual lines', function() -- cgit From dee3af2122d5072d351551ef023d2c177334b332 Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Sun, 26 Mar 2023 16:09:09 +1100 Subject: vim-patch:9.0.0178: cursor position wrong with virtual text before Tab Problem: Cursor position wrong with virtual text before Tab. Solution: Use the byte length, not the cell with, to compare the column. Correct tab size after text prop. (closes vim/vim#10866) https://github.com/vim/vim/commit/e428fa04a758cc87ea580c856a796e58e407504b Co-authored-by: Bram Moolenaar --- src/nvim/charset.c | 8 +-- src/nvim/plines.c | 7 +++ test/functional/ui/decorations_spec.lua | 105 ++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 4 deletions(-) diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 3b48392d49..24267ebd50 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1084,10 +1084,6 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en } if (cursor != NULL) { - if ((State & MODE_INSERT) == 0 && !on_NUL) { - // cursor is after inserted text, unless on the NUL - vcol += cts.cts_cur_text_width; - } if ((*ptr == TAB) && (State & MODE_NORMAL) && !wp->w_p_list @@ -1096,6 +1092,10 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en // cursor at end *cursor = vcol + incr - 1; } else { + if ((State & MODE_INSERT) == 0 && !on_NUL) { + // cursor is after inserted text, unless on the NUL + vcol += cts.cts_cur_text_width; + } // cursor at start *cursor = vcol + head; } diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 59c45d3615..2df9c23902 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -411,6 +411,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) // First get normal size, without 'linebreak' or virtual text int size = win_chartabsize(wp, s, vcol); if (cts->cts_has_virt_text) { + int tab_size = size; int charlen = *s == NUL ? 1 : utf_ptr2len(s); int col = (int)(s - line); while (true) { @@ -425,6 +426,12 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) if (decor.virt_text_pos == kVTInline) { cts->cts_cur_text_width += decor.virt_text_width; size += decor.virt_text_width; + if (*s == TAB) { + // tab size changes because of the inserted text + size -= tab_size; + tab_size = win_chartabsize(wp, s, vcol + size); + size += tab_size; + } } } } diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 635666c941..91284e3a2f 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -1476,6 +1476,111 @@ bbbbbbb]]) | ]]} end) + + it('cursor position is correct with virtual text attatched to hard tabs', function() + command('set noexpandtab') + feed('i') + feed('') + feed('') + feed('test') + feed('') + meths.buf_set_extmark(0, ns, 0, 1, + { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) + feed('0') + screen:expect { grid = [[ + ^ {28:virtual text} test | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('l') + screen:expect { grid = [[ + {28:virtual text} ^ test | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('l') + screen:expect { grid = [[ + {28:virtual text} ^test | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('l') + screen:expect { grid = [[ + {28:virtual text} t^est | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('l') + screen:expect { grid = [[ + {28:virtual text} te^st | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) end) describe('decorations: virtual lines', function() -- cgit From e12b5882af97126a9525a74a0955cc9e0f9114a9 Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Sun, 26 Mar 2023 17:40:07 +1100 Subject: vim-patch:9.0.0183: extra space after virtual text when 'linebreak' is set Problem: Extra space after virtual text when 'linebreak' is set. Solution: Do not count virtual text when getting linebreak value. (closes vim/vim#10884) https://github.com/vim/vim/commit/52de3a8d3943520bbd4e5e40a4c43fcc7182dac0 Co-authored-by: Bram Moolenaar --- src/nvim/drawline.c | 4 +++- test/functional/ui/decorations_spec.lua | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index e42d912dbe..725b047985 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -2165,7 +2165,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, chartabsize_T cts; init_chartabsize_arg(&cts, wp, lnum, wlv.vcol, line, p); + // do not want virtual text to be counted here + cts.cts_has_virt_text = false; wlv.n_extra = win_lbr_chartabsize(&cts, NULL) - 1; + clear_chartabsize_arg(&cts); // We have just drawn the showbreak value, no need to add // space for it again. @@ -2197,7 +2200,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, c = ' '; } } - clear_chartabsize_arg(&cts); } in_multispace = c == ' ' && ((ptr > line + 1 && ptr[-2] == ' ') || *ptr == ' '); diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 91284e3a2f..4ed9d5a044 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -1581,6 +1581,31 @@ bbbbbbb]]) | ]]} end) + + it('has correct cursor position with virtual text on an empty line', function() + command('set linebreak') + insert('one twoword') + feed('0') + meths.buf_set_extmark(0, ns, 0, 3, + { virt_text = { { ': virtual text', 'Special' } }, virt_text_pos = 'inline' }) + screen:expect { grid = [[ + ^one{28:: virtual text} twoword | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) end) describe('decorations: virtual lines', function() -- cgit From c5bf838f8aa51709f8d7ee81cf2b2a6479c77ad7 Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Mon, 27 Mar 2023 01:25:37 +1100 Subject: fix(ui): fix visual and search highlighting interfering with virtual text vim-patch:9.0.0193: search and match highlgith interfere with virtual text Problem: Search and match highlgith interfere with virtual text highlight. (Ben Jackson) Solution: Check for match highlight after text properties. Reset and restore search highlight when showing virtual text. (closes vim/vim#10892) https://github.com/vim/vim/commit/e38fc86180fd3f6b372648eea6adc3f623fea302 vim-patch:9.0.0452: Visual highlighting extends into virtual text prop Problem: Visual highlighting extends into virtual text prop. Solution: Do not highlight what isn't actually selected. Fix ordering of stored text props. https://github.com/vim/vim/commit/6eda17d881c9b2880ccb2a4d11951939a58f233d Co-authored-by: Bram Moolenaar --- src/nvim/drawline.c | 92 ++++++++++++++++++++------------- test/functional/ui/decorations_spec.lua | 92 ++++++++++++++++++++++++++++++++- 2 files changed, 146 insertions(+), 38 deletions(-) diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 725b047985..c789c42af4 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -978,7 +978,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, bool area_highlighting = false; // Visual or incsearch highlighting in this line int vi_attr = 0; // attributes for Visual and incsearch highlighting int area_attr = 0; // attributes desired by highlighting + int saved_area_attr = 0; // idem for area_attr int search_attr = 0; // attributes desired by 'hlsearch' + int saved_search_attr = 0; // search_attr to be used when n_extra + // goes to zero int vcol_save_attr = 0; // saved attr for 'cursorcolumn' int syntax_attr = 0; // attributes desired by syntax bool has_syntax = false; // this buffer has syntax highl. @@ -1729,6 +1732,50 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, area_active = false; } + if (has_decor && v >= 0) { + bool selected = (area_active || (area_highlighting && noinvcur + && wlv.vcol == wp->w_virtcol)); + extmark_attr = decor_redraw_col(wp, (colnr_T)v, wlv.off, + selected, &decor_state); + + // we could already be inside an existing virt_line with multiple chunks + if (!(virt_inline_i < kv_size(virt_inline))) { + DecorState *state = &decor_state; + for (size_t i = 0; i < kv_size(state->active); i++) { + DecorRange *item = &kv_A(state->active, i); + if (!(item->start_row == state->row + && kv_size(item->decor.virt_text) + && item->decor.virt_text_pos == kVTInline)) { + continue; + } + if (item->win_col >= -1 && item->start_col <= v) { + virt_inline = item->decor.virt_text; + virt_inline_i = 0; + item->win_col = -2; + break; + } + } + } + + if (wlv.n_extra <= 0 && virt_inline_i < kv_size(virt_inline)) { + VirtTextChunk vtc = kv_A(virt_inline, virt_inline_i); + wlv.p_extra = vtc.text; + wlv.n_extra = (int)strlen(wlv.p_extra); + wlv.c_extra = NUL; + wlv.c_final = NUL; + wlv.extra_attr = vtc.hl_id ? syn_id2attr(vtc.hl_id) : 0; + n_attr = mb_charlen(vtc.text); + // restore search_attr and area_attr when n_extra + // is down to zero + saved_search_attr = search_attr; + saved_area_attr = area_attr; + search_attr = 0; + area_attr = 0; + extmark_attr = 0; + virt_inline_i++; + } + } + if (!wlv.n_extra) { // Check for start/end of 'hlsearch' and other matches. // After end, check for start/end of next match. @@ -1792,43 +1839,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.char_attr = 0; } } - - if (has_decor && v >= 0) { - bool selected = (area_active || (area_highlighting && noinvcur - && wlv.vcol == wp->w_virtcol)); - extmark_attr = decor_redraw_col(wp, (colnr_T)v, wlv.off, selected, &decor_state); - - // we could already be inside an existing virt_line with multiple chunks - if (!(virt_inline_i < kv_size(virt_inline))) { - DecorState *state = &decor_state; - for (size_t i = 0; i < kv_size(state->active); i++) { - DecorRange *item = &kv_A(state->active, i); - if (!(item->start_row == state->row - && kv_size(item->decor.virt_text) - && item->decor.virt_text_pos == kVTInline)) { - continue; - } - if (item->win_col >= -1 && item->start_col <= v) { - virt_inline = item->decor.virt_text; - virt_inline_i = 0; - item->win_col = -2; - break; - } - } - } - - if (wlv.n_extra <= 0 && virt_inline_i < kv_size(virt_inline)) { - VirtTextChunk vtc = kv_A(virt_inline, virt_inline_i); - wlv.p_extra = vtc.text; - wlv.n_extra = (int)strlen(wlv.p_extra); - wlv.c_extra = NUL; - wlv.c_final = NUL; - wlv.extra_attr = vtc.hl_id ? syn_id2attr(vtc.hl_id) : 0; - n_attr = mb_charlen(vtc.text); - extmark_attr = 0; - virt_inline_i++; - } - } } // Get the next character to put on the screen. @@ -1890,6 +1900,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.p_extra++; } wlv.n_extra--; + if (wlv.n_extra <= 0) { + if (search_attr == 0) { + search_attr = saved_search_attr; + } + if (area_attr == 0 && *ptr != NUL) { + area_attr = saved_area_attr; + } + } } else if (foldinfo.fi_lines > 0) { // skip writing the buffer line itself c = NUL; diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 4ed9d5a044..fa492dfcac 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -646,6 +646,8 @@ describe('extmark decorations', function() [26] = {background=Screen.colors.DarkGrey, foreground=Screen.colors.LightGrey}; [27] = {background = Screen.colors.Plum1}; [28] = {foreground = Screen.colors.SlateBlue}; + [29] = {background = Screen.colors.Yellow1}; + [30] = {reverse = true}; } ns = meths.create_namespace 'test' @@ -1582,7 +1584,7 @@ bbbbbbb]]) ]]} end) - it('has correct cursor position with virtual text on an empty line', function() + it('cursor position is correct with virtual text on an empty line', function() command('set linebreak') insert('one twoword') feed('0') @@ -1606,6 +1608,94 @@ bbbbbbb]]) | ]]} end) + + it('search highlight is correct with virtual text attatched to', function() + insert('foo foo foo foo') + feed('0') + meths.buf_set_extmark(0, ns, 0, 8, + { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) + screen:expect { grid = [[ + ^foo foo {28:virtual text}foo foo | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('/foo') + screen:expect { grid = [[ + {29:foo} {30:foo} {28:virtual text}{29:foo} {29:foo} | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + /foo^ | + ]]} + end) + + it('visual select highlight is correct with virtual text attatched to', function() + insert('foo foo foo foo') + feed('0') + meths.buf_set_extmark(0, ns, 0, 8, + { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) + feed('8l') + screen:expect { grid = [[ + foo foo {28:virtual text}^foo foo | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('v') + feed('2h') + screen:expect { grid = [[ + foo fo^o{18: }{28:virtual text}{18:f}oo foo | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {24:-- VISUAL --} | + ]]} + end) end) describe('decorations: virtual lines', function() -- cgit From be273c3a23cf65665e843cfb13fd5319657cc5c2 Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Tue, 28 Mar 2023 01:23:21 +1100 Subject: vim-patch:9.0.0205: cursor in wrong position when inserting after virtual text Problem: Cursor in wrong position when inserting after virtual text. (Ben Jackson) Solution: Put the cursor after the virtual text, where the text will be inserted. (closes vim/vim#10914) https://github.com/vim/vim/commit/28c9f895716cfa8f1220bc41b72a534c0e10cabe Co-authored-by: Bram Moolenaar --- src/nvim/charset.c | 2 +- src/nvim/plines.c | 2 ++ src/nvim/plines.h | 1 + test/functional/ui/decorations_spec.lua | 47 ++++++++++++++++++++++++++++++++- 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 24267ebd50..2917bb31ee 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1092,7 +1092,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en // cursor at end *cursor = vcol + incr - 1; } else { - if ((State & MODE_INSERT) == 0 && !on_NUL) { + if (((State & MODE_INSERT) == 0 || !cts.cts_has_right_gravity) && !on_NUL) { // cursor is after inserted text, unless on the NUL vcol += cts.cts_cur_text_width; } diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 2df9c23902..3b4883d5c2 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -325,6 +325,7 @@ void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum, colnr_T cts->cts_ptr = ptr; cts->cts_cur_text_width = 0; cts->cts_has_virt_text = false; + cts->cts_has_right_gravity = true; cts->cts_row = lnum - 1; if (cts->cts_row >= 0) { @@ -425,6 +426,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) Decoration decor = get_decor(mark); if (decor.virt_text_pos == kVTInline) { cts->cts_cur_text_width += decor.virt_text_width; + cts->cts_has_right_gravity = mt_right(mark); size += decor.virt_text_width; if (*s == TAB) { // tab size changes because of the inserted text diff --git a/src/nvim/plines.h b/src/nvim/plines.h index a15c234bba..6e4768bc7d 100644 --- a/src/nvim/plines.h +++ b/src/nvim/plines.h @@ -14,6 +14,7 @@ typedef struct { int cts_row; bool cts_has_virt_text; // true if if a property inserts text + bool cts_has_right_gravity; int cts_cur_text_width; // width of current inserted text MarkTreeIter cts_iter[1]; // TODO(bfredl): iterator in to the marktree for scanning virt text diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index fa492dfcac..de3f7e7a5b 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -1652,11 +1652,12 @@ bbbbbbb]]) ]]} end) + feed('8l') it('visual select highlight is correct with virtual text attatched to', function() insert('foo foo foo foo') feed('0') meths.buf_set_extmark(0, ns, 0, 8, - { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) + { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) feed('8l') screen:expect { grid = [[ foo foo {28:virtual text}^foo foo | @@ -1696,6 +1697,50 @@ bbbbbbb]]) {24:-- VISUAL --} | ]]} end) + + it('cursor position is correct when inserting around a virtual text with right gravity set to false', function() + insert('foo foo foo foo') + meths.buf_set_extmark(0, ns, 0, 8, + { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline', right_gravity = false }) + feed('0') + feed('8l') + screen:expect { grid = [[ + foo foo {28:virtual text}^foo foo | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('i') + screen:expect { grid = [[ + foo foo {28:virtual text}^foo foo | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {24:-- INSERT --} | + ]]} + end) end) describe('decorations: virtual lines', function() -- cgit From 1936285d98f62a1357abf928b10c824cf9e3ff41 Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Mon, 3 Apr 2023 01:05:08 +1000 Subject: fix(ui): fixes incorrect rendering when virtual text is not visable and nowrap --- src/nvim/drawline.c | 8 ++++---- test/functional/ui/decorations_spec.lua | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index c789c42af4..c9ebcd8ed4 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -1743,12 +1743,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, DecorState *state = &decor_state; for (size_t i = 0; i < kv_size(state->active); i++) { DecorRange *item = &kv_A(state->active, i); - if (!(item->start_row == state->row - && kv_size(item->decor.virt_text) - && item->decor.virt_text_pos == kVTInline)) { + if (item->start_row != state->row + || !kv_size(item->decor.virt_text) + || item->decor.virt_text_pos != kVTInline) { continue; } - if (item->win_col >= -1 && item->start_col <= v) { + if (item->win_col >= -1 && item->start_col == v) { virt_inline = item->decor.virt_text; virt_inline_i = 0; item->win_col = -2; diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index de3f7e7a5b..68c0e5eaca 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -1741,6 +1741,33 @@ bbbbbbb]]) {24:-- INSERT --} | ]]} end) + + it('no wrap is rendered correctly with multiple virtual text, where one is hidden', function() + insert('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz') + command("set nowrap") + meths.buf_set_extmark(0, ns, 0, 50, + { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline', right_gravity = false }) + meths.buf_set_extmark(0, ns, 0, 2, + { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline', right_gravity = false }) + feed('$') + screen:expect { grid = [[ + opqrstuvwxyzabcdefghijklmnopqrstuvwx{28:virtual text}y^z| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) end) describe('decorations: virtual lines', function() -- cgit From 7423d3479d95e875e2d261d6e404ad19a631e530 Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Mon, 3 Apr 2023 01:43:06 +1000 Subject: vim-patch:9.0.0716: with 'nowrap' virtual text "after" does not scroll left Problem: With 'nowrap' virtual text "after" does not scroll left. Solution: Skip part of the virtual text that is left of the window. (closes vim/vim#11320) Fix going beyond the last column of the window. https://github.com/vim/vim/commit/cd105417a53fcf97c0935f3468201ef11516c9f1 Co-authored-by: Bram Moolenaar --- src/nvim/drawline.c | 30 +++++++++++++++++++++++++++--- test/functional/ui/decorations_spec.lua | 27 ++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index c9ebcd8ed4..0ad03f940a 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -966,7 +966,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, int n_attr3 = 0; // chars with overruling special attr int saved_attr3 = 0; // char_attr saved for n_attr3 - int n_skip = 0; // nr of chars to skip for 'nowrap' + int n_skip = 0; // nr of chars to skip for 'nowrap' or + // concealing + int skip_cells = 0; // nr of cells to skip for virtual text + // after the line, when w_skipcol is + // larger than the text length int fromcol_prev = -2; // start of inverting after cursor bool noinvcur = false; // don't invert the cursor @@ -1434,6 +1438,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } } + // If there the text doesn't reach to the desired column, need to skip + // "skip_cells" cells when virtual text follows. + if (!wp->w_p_wrap && v > wlv.vcol) { + skip_cells = (int)(v - wlv.vcol); + } + // Adjust for when the inverted text is before the screen, // and when the start of the inverted text is before the screen. if (wlv.tocol <= wlv.vcol) { @@ -1744,8 +1754,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, for (size_t i = 0; i < kv_size(state->active); i++) { DecorRange *item = &kv_A(state->active, i); if (item->start_row != state->row - || !kv_size(item->decor.virt_text) - || item->decor.virt_text_pos != kVTInline) { + || !kv_size(item->decor.virt_text) + || item->decor.virt_text_pos != kVTInline) { continue; } if (item->win_col >= -1 && item->start_col == v) { @@ -1773,6 +1783,20 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, area_attr = 0; extmark_attr = 0; virt_inline_i++; + // If the text didn't reach until the first window + // column we need to skip cells. + if (skip_cells > 0) { + if (wlv.n_extra > skip_cells) { + wlv.n_extra -= skip_cells; + wlv.p_extra += skip_cells; + skip_cells = 0; + } else { + // the whole text is left of the window, drop + // it and advance to the next one + skip_cells -= wlv.n_extra; + wlv.n_extra = 0; + } + } } } diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 68c0e5eaca..ba3f73c229 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -1742,7 +1742,7 @@ bbbbbbb]]) ]]} end) - it('no wrap is rendered correctly with multiple virtual text, where one is hidden', function() + it('draws correctly with no wrap multiple virtual text, where one is hidden', function() insert('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz') command("set nowrap") meths.buf_set_extmark(0, ns, 0, 50, @@ -1768,6 +1768,31 @@ bbbbbbb]]) | ]]} end) + + it('draws correctly with no wrap and a long virtual text', function() + insert('abcdefghi') + command("set nowrap") + meths.buf_set_extmark(0, ns, 0, 2, + { virt_text = { { string.rep('X', 55), 'Special' } }, virt_text_pos = 'inline', right_gravity = false }) + feed('$') + screen:expect { grid = [[ + {28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}cdefgh^i| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) end) describe('decorations: virtual lines', function() -- cgit From 5d7afb2e9f222c32dd18c9c2bca49cab8bf751bc Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Wed, 12 Apr 2023 18:21:46 +1000 Subject: fix(ui): fix tabs not being spaced properly after virtual text with no wrap also fixes incorrect skipping of multibyte characters --- src/nvim/drawline.c | 26 ++++++++++++++++++++------ src/nvim/mbyte.c | 18 ++++++++++++++++++ test/functional/ui/decorations_spec.lua | 25 +++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 6 deletions(-) diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 0ad03f940a..fddf1362a3 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -969,8 +969,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, int n_skip = 0; // nr of chars to skip for 'nowrap' or // concealing int skip_cells = 0; // nr of cells to skip for virtual text - // after the line, when w_skipcol is - // larger than the text length + int skipped_cells = 0; // nr of skipped virtual text cells int fromcol_prev = -2; // start of inverting after cursor bool noinvcur = false; // don't invert the cursor @@ -1786,14 +1785,22 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, // If the text didn't reach until the first window // column we need to skip cells. if (skip_cells > 0) { - if (wlv.n_extra > skip_cells) { - wlv.n_extra -= skip_cells; - wlv.p_extra += skip_cells; + int virt_text_len = n_attr; + if (virt_text_len > skip_cells) { + int len = mb_charlen2bytelen(wlv.p_extra, skip_cells); + wlv.n_extra -= len; + wlv.p_extra += len; + n_attr -= skip_cells; + // Skipped cells needed to be accounted for in vcol. + skipped_cells += skip_cells; skip_cells = 0; } else { // the whole text is left of the window, drop // it and advance to the next one - skip_cells -= wlv.n_extra; + skip_cells -= virt_text_len; + // Skipped cells needed to be accounted for in vcol. + skipped_cells += virt_text_len; + n_attr = 0; wlv.n_extra = 0; } } @@ -2969,6 +2976,13 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, n_skip--; } + // The skipped cells need to be accounted for in vcol. + if (wlv.draw_state > WL_STC + && skipped_cells > 0) { + wlv.vcol += skipped_cells; + skipped_cells = 0; + } + // Only advance the "wlv.vcol" when after the 'number' or // 'relativenumber' column. if (wlv.draw_state > WL_STC diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 7d61b918d2..66c26275f1 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -2024,6 +2024,24 @@ int mb_charlen(const char *str) return count; } +int mb_charlen2bytelen(const char *str, int charlen) +{ + const char *p = str; + int count = 0; + + if (p == NULL) { + return 0; + } + + for (int i = 0; *p != NUL && i < charlen; i++) { + int b = utfc_ptr2len(p); + p += b; + count += b; + } + + return count; +} + /// Like mb_charlen() but for a string with specified length. int mb_charlen_len(const char *str, int len) { diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index ba3f73c229..9be8c23ba4 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -1793,6 +1793,31 @@ bbbbbbb]]) | ]]} end) + + it('tabs are the correct length with no wrap following virtual text', function() + command('set nowrap') + feed('itesta') + meths.buf_set_extmark(0, ns, 0, 0, + { virt_text = { { string.rep('a', 55), 'Special' } }, virt_text_pos = 'inline' }) + feed('gg$') + screen:expect { grid = [[ + {28:aaaaaaaaaaaaaaaaaaaaaaaaa}test ^a | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) end) describe('decorations: virtual lines', function() -- cgit From ecf225df574a48a4bbddf4978972b2b97184e92c Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Mon, 3 Apr 2023 20:46:50 +1000 Subject: vim-patch:9.0.0944: 'cursorline' causes virtual text highlight to continue Problem: 'cursorline' causes virtual text highlight to continue. Solution: Save and restore line_attr. (closes vim/vim#11588) https://github.com/vim/vim/commit/6ac16f0c0fe923098b9df5ac430f1923045f16ea Co-authored-by: Bram Moolenaar --- src/nvim/drawline.c | 18 ++++++++++++++++++ test/functional/ui/decorations_spec.lua | 25 +++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index fddf1362a3..c063425f68 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -102,6 +102,7 @@ typedef struct { ///< when c_extra and c_final are NUL char *p_extra_free; ///< p_extra buffer that needs to be freed int extra_attr; ///< attributes for p_extra + ///< with win_attr if needed int c_extra; ///< extra chars, all the same int c_final; ///< final char, mandatory if set @@ -923,6 +924,7 @@ static void win_line_continue(winlinevars_T *wlv) if (wlv->saved_n_extra > 0) { // Continue item from end of wrapped line. wlv->n_extra = wlv->saved_n_extra; + wlv->saved_n_extra = 0; wlv->c_extra = wlv->saved_c_extra; wlv->c_final = wlv->saved_c_final; wlv->p_extra = wlv->saved_p_extra; @@ -985,6 +987,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, int search_attr = 0; // attributes desired by 'hlsearch' int saved_search_attr = 0; // search_attr to be used when n_extra // goes to zero + bool reset_extra_attr = false; + int vcol_save_attr = 0; // saved attr for 'cursorcolumn' int syntax_attr = 0; // attributes desired by syntax bool has_syntax = false; // this buffer has syntax highl. @@ -1766,6 +1770,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } } + if (wlv.n_extra == 0) { + reset_extra_attr = false; + } + if (wlv.n_extra <= 0 && virt_inline_i < kv_size(virt_inline)) { VirtTextChunk vtc = kv_A(virt_inline, virt_inline_i); wlv.p_extra = vtc.text; @@ -1931,6 +1939,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.p_extra++; } wlv.n_extra--; + + // Only restore search_attr and area_attr after "n_extra" in + // the next screen line is also done. if (wlv.n_extra <= 0) { if (search_attr == 0) { search_attr = saved_search_attr; @@ -1938,6 +1949,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, if (area_attr == 0 && *ptr != NUL) { area_attr = saved_area_attr; } + // wlv.extra_attr should be used at this position but not + // any further. + reset_extra_attr = true; } } else if (foldinfo.fi_lines > 0) { // skip writing the buffer line itself @@ -2589,6 +2603,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, // Don't override visual selection highlighting. if (n_attr > 0 && wlv.draw_state == WL_LINE && !search_attr_from_match) { wlv.char_attr = hl_combine_attr(wlv.char_attr, wlv.extra_attr); + if (reset_extra_attr) { + reset_extra_attr = false; + wlv.extra_attr = 0; + } } // Handle the case where we are in column 0 but not on the first diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 9be8c23ba4..77748eecb3 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -1818,6 +1818,31 @@ bbbbbbb]]) | ]]} end) + + it('highlighting does not extend when no wrap is enabled with a long virtual text', function() + insert('abcdef') + command("set nowrap") + meths.buf_set_extmark(0, ns, 0, 3, + { virt_text = { { string.rep('X', 50), 'Special' } }, virt_text_pos = 'inline', right_gravity = false }) + feed('$') + screen:expect { grid = [[ + {28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}de^f| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) end) describe('decorations: virtual lines', function() -- cgit From 332b70d2ed310084fe382affddbef3126c12751c Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Mon, 3 Apr 2023 23:12:56 +1000 Subject: fix(ui): fix incorrect highlighting when virtual text wraps with number --- src/nvim/drawline.c | 2 +- test/functional/ui/decorations_spec.lua | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index c063425f68..466fe8f8f1 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -1942,7 +1942,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, // Only restore search_attr and area_attr after "n_extra" in // the next screen line is also done. - if (wlv.n_extra <= 0) { + if (wlv.n_extra <= 0 && wlv.saved_n_extra <= 0) { if (search_attr == 0) { search_attr = saved_search_attr; } diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 77748eecb3..2d0f3e7a17 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -1843,6 +1843,33 @@ bbbbbbb]]) | ]]} end) + + it('highlighting is correct when virtual text wraps with number', function() + insert([[ + test + test]]) + command('set number') + meths.buf_set_extmark(0, ns, 0, 1, + { virt_text = { { string.rep('X', 55), 'Special' } }, virt_text_pos = 'inline', right_gravity = false }) + feed('gg0') + screen:expect { grid = [[ + {2: 1 }^t{28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + {2: }{28:XXXXXXXXXX}est | + {2: 2 }test | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) end) describe('decorations: virtual lines', function() -- cgit From 75f350aac6cd952341d7ce88e64b0d6ed2aa694d Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Wed, 5 Apr 2023 00:29:05 +1000 Subject: fix(ui): fix incorrect highlighting when virtual text next to match --- src/nvim/drawline.c | 8 +++-- test/functional/ui/decorations_spec.lua | 53 +++++++++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 466fe8f8f1..23b88413b1 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -102,7 +102,6 @@ typedef struct { ///< when c_extra and c_final are NUL char *p_extra_free; ///< p_extra buffer that needs to be freed int extra_attr; ///< attributes for p_extra - ///< with win_attr if needed int c_extra; ///< extra chars, all the same int c_final; ///< final char, mandatory if set @@ -1026,6 +1025,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, int prev_c1 = 0; // first composing char for prev_c bool search_attr_from_match = false; // if search_attr is from :match + bool saved_search_attr_from_match = false; // if search_attr is from :match bool has_decor = false; // this buffer has decoration int win_col_offset = 0; // offset for window columns @@ -1786,6 +1786,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, // is down to zero saved_search_attr = search_attr; saved_area_attr = area_attr; + saved_search_attr_from_match = search_attr_from_match; + search_attr_from_match = false; search_attr = 0; area_attr = 0; extmark_attr = 0; @@ -1815,7 +1817,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } } - if (!wlv.n_extra) { + if (wlv.n_extra == 0) { // Check for start/end of 'hlsearch' and other matches. // After end, check for start/end of next match. // When another match, have to check for start again. @@ -2606,6 +2608,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, if (reset_extra_attr) { reset_extra_attr = false; wlv.extra_attr = 0; + // search_attr_from_match can be restored now that the extra_attr has been applied + search_attr_from_match = saved_search_attr_from_match; } } diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 2d0f3e7a17..292743e21a 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -1746,9 +1746,9 @@ bbbbbbb]]) insert('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz') command("set nowrap") meths.buf_set_extmark(0, ns, 0, 50, - { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline', right_gravity = false }) + { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) meths.buf_set_extmark(0, ns, 0, 2, - { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline', right_gravity = false }) + { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) feed('$') screen:expect { grid = [[ opqrstuvwxyzabcdefghijklmnopqrstuvwx{28:virtual text}y^z| @@ -1773,7 +1773,7 @@ bbbbbbb]]) insert('abcdefghi') command("set nowrap") meths.buf_set_extmark(0, ns, 0, 2, - { virt_text = { { string.rep('X', 55), 'Special' } }, virt_text_pos = 'inline', right_gravity = false }) + { virt_text = { { string.rep('X', 55), 'Special' } }, virt_text_pos = 'inline' }) feed('$') screen:expect { grid = [[ {28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}cdefgh^i| @@ -1823,7 +1823,7 @@ bbbbbbb]]) insert('abcdef') command("set nowrap") meths.buf_set_extmark(0, ns, 0, 3, - { virt_text = { { string.rep('X', 50), 'Special' } }, virt_text_pos = 'inline', right_gravity = false }) + { virt_text = { { string.rep('X', 50), 'Special' } }, virt_text_pos = 'inline' }) feed('$') screen:expect { grid = [[ {28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}de^f| @@ -1850,7 +1850,7 @@ bbbbbbb]]) test]]) command('set number') meths.buf_set_extmark(0, ns, 0, 1, - { virt_text = { { string.rep('X', 55), 'Special' } }, virt_text_pos = 'inline', right_gravity = false }) + { virt_text = { { string.rep('X', 55), 'Special' } }, virt_text_pos = 'inline' }) feed('gg0') screen:expect { grid = [[ {2: 1 }^t{28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| @@ -1870,6 +1870,49 @@ bbbbbbb]]) | ]]} end) + + it('highlighting is correct when virtual text is proceeded with a match', function() + insert([[test]]) + meths.buf_set_extmark(0, ns, 0, 2, + { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) + feed('gg0') + command('match ErrorMsg /e/') + screen:expect { grid = [[ + ^t{4:e}{28:virtual text}st | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + command('match ErrorMsg /s/') + screen:expect { grid = [[ + ^te{28:virtual text}{4:s}t | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) end) describe('decorations: virtual lines', function() -- cgit From 5547b16c40f92fbc75ad1b9a7b3dd1f5ff50cbb2 Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Mon, 3 Apr 2023 22:13:15 +1000 Subject: vim-patch:9.0.1067: in diff mode virtual text is highlighted incorrectly Problem: In diff mode virtual text is highlighted incorrectly. (Rick Howe) Solution: Do not use diff attributes for virtual text. (closes vim/vim#11714) https://github.com/vim/vim/commit/d097af77797f030e0f29f9bbc298358a5addb2a5 Co-authored-by: Bram Moolenaar --- src/nvim/drawline.c | 38 ++++++++++++++++++++--------- test/functional/ui/decorations_spec.lua | 43 +++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 12 deletions(-) diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 23b88413b1..84f0447ca6 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -105,9 +105,12 @@ typedef struct { int c_extra; ///< extra chars, all the same int c_final; ///< final char, mandatory if set + bool extra_for_extmark; + // saved "extra" items for when draw_state becomes WL_LINE (again) int saved_n_extra; char *saved_p_extra; + bool saved_extra_for_extmark; int saved_c_extra; int saved_c_final; int saved_char_attr; @@ -909,6 +912,7 @@ static void win_line_start(win_T *wp, winlinevars_T *wlv, bool save_extra) wlv->draw_state = WL_START; wlv->saved_n_extra = wlv->n_extra; wlv->saved_p_extra = wlv->p_extra; + wlv->saved_extra_for_extmark = wlv->extra_for_extmark; wlv->saved_c_extra = wlv->c_extra; wlv->saved_c_final = wlv->c_final; wlv->saved_char_attr = wlv->char_attr; @@ -924,6 +928,7 @@ static void win_line_continue(winlinevars_T *wlv) // Continue item from end of wrapped line. wlv->n_extra = wlv->saved_n_extra; wlv->saved_n_extra = 0; + wlv->extra_for_extmark = wlv->saved_extra_for_extmark; wlv->c_extra = wlv->saved_c_extra; wlv->c_final = wlv->saved_c_final; wlv->p_extra = wlv->saved_p_extra; @@ -1770,7 +1775,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } } - if (wlv.n_extra == 0) { + if (wlv.n_extra == 0 || !wlv.extra_for_extmark) { reset_extra_attr = false; } @@ -1778,6 +1783,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, VirtTextChunk vtc = kv_A(virt_inline, virt_inline_i); wlv.p_extra = vtc.text; wlv.n_extra = (int)strlen(wlv.p_extra); + wlv.extra_for_extmark = true; wlv.c_extra = NUL; wlv.c_final = NUL; wlv.extra_attr = vtc.hl_id ? syn_id2attr(vtc.hl_id) : 0; @@ -1835,12 +1841,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } if (wlv.diff_hlf != (hlf_T)0) { + // When there is extra text (eg: virtual text) it gets the + // diff highlighting for the line, but not for changed text. if (wlv.diff_hlf == HLF_CHD && ptr - line >= change_start && wlv.n_extra == 0) { wlv.diff_hlf = HLF_TXD; // changed text } - if (wlv.diff_hlf == HLF_TXD && ptr - line > change_end - && wlv.n_extra == 0) { + if (wlv.diff_hlf == HLF_TXD && ((ptr - line > change_end && wlv.n_extra == 0) + || (wlv.n_extra > 0 && wlv.extra_for_extmark))) { wlv.diff_hlf = HLF_CHD; // changed line } wlv.line_attr = win_hl_attr(wp, (int)wlv.diff_hlf); @@ -1944,16 +1952,22 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, // Only restore search_attr and area_attr after "n_extra" in // the next screen line is also done. - if (wlv.n_extra <= 0 && wlv.saved_n_extra <= 0) { - if (search_attr == 0) { - search_attr = saved_search_attr; - } - if (area_attr == 0 && *ptr != NUL) { - area_attr = saved_area_attr; + if (wlv.n_extra <= 0) { + if (wlv.saved_n_extra <= 0) { + if (search_attr == 0) { + search_attr = saved_search_attr; + } + if (area_attr == 0 && *ptr != NUL) { + area_attr = saved_area_attr; + } + + if (wlv.extra_for_extmark) { + // wlv.extra_attr should be used at this position but not + // any further. + reset_extra_attr = true; + } } - // wlv.extra_attr should be used at this position but not - // any further. - reset_extra_attr = true; + wlv.extra_for_extmark = false; } } else if (foldinfo.fi_lines > 0) { // skip writing the buffer line itself diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 292743e21a..4579fad53f 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -648,6 +648,9 @@ describe('extmark decorations', function() [28] = {foreground = Screen.colors.SlateBlue}; [29] = {background = Screen.colors.Yellow1}; [30] = {reverse = true}; + [31] = {foreground = Screen.colors.SlateBlue, background = Screen.colors.LightMagenta}; + [32] = {bold = true, reverse = true}; + [33] = {background = Screen.colors.Red1, bold = true} } ns = meths.create_namespace 'test' @@ -1913,6 +1916,46 @@ bbbbbbb]]) | ]]} end) + + it('in diff mode virtual text is highlighted correct', function() + insert([[ + 9000 + 0009 + 0009 + 9000 + 0009 + ]]) + command("set diff") + meths.buf_set_extmark(0, ns, 0, 1, + { virt_text = { { 'test', 'Special' } }, virt_text_pos = 'inline', right_gravity = false }) + command("vnew") + insert([[ + 000 + 000 + 000 + 000 + 000 + ]]) + command("set diff") + feed('gg0') + screen:expect { grid = [[ + {27:^000 }│{33:9}{31:test}{27:000 }| + {27:000 }│{27:000}{33:9}{27: }| + {27:000 }│{27:000}{33:9}{27: }| + {27:000 }│{33:9}{27:000 }| + {27:000 }│{27:000}{33:9}{27: }| + │ | + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + {32:[No Name] [+] }{30:[No Name] [+] }| + | + ]]} + end) end) describe('decorations: virtual lines', function() -- cgit From 34d862942c3387eaa9d2bba963d931d384031b90 Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Wed, 19 Apr 2023 19:02:33 +1000 Subject: fix(ui): fix virtual text not displaying when two overlapping inlines (nowrap) --- src/nvim/drawline.c | 125 +++++++++++++++++--------------- test/functional/ui/decorations_spec.lua | 27 +++++++ 2 files changed, 92 insertions(+), 60 deletions(-) diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 84f0447ca6..abc27c1eda 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -1756,70 +1756,76 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, extmark_attr = decor_redraw_col(wp, (colnr_T)v, wlv.off, selected, &decor_state); - // we could already be inside an existing virt_line with multiple chunks - if (!(virt_inline_i < kv_size(virt_inline))) { - DecorState *state = &decor_state; - for (size_t i = 0; i < kv_size(state->active); i++) { - DecorRange *item = &kv_A(state->active, i); - if (item->start_row != state->row - || !kv_size(item->decor.virt_text) - || item->decor.virt_text_pos != kVTInline) { - continue; - } - if (item->win_col >= -1 && item->start_col == v) { - virt_inline = item->decor.virt_text; - virt_inline_i = 0; - item->win_col = -2; - break; + while (true) { + // we could already be inside an existing virt_line with multiple chunks + if (!(virt_inline_i < kv_size(virt_inline))) { + DecorState *state = &decor_state; + for (size_t i = 0; i < kv_size(state->active); i++) { + DecorRange *item = &kv_A(state->active, i); + if (item->start_row != state->row + || !kv_size(item->decor.virt_text) + || item->decor.virt_text_pos != kVTInline) { + continue; + } + if (item->win_col >= -1 && item->start_col == v) { + virt_inline = item->decor.virt_text; + virt_inline_i = 0; + item->win_col = -2; + break; + } } } - } - if (wlv.n_extra == 0 || !wlv.extra_for_extmark) { - reset_extra_attr = false; - } + if (wlv.n_extra == 0 || !wlv.extra_for_extmark) { + reset_extra_attr = false; + } - if (wlv.n_extra <= 0 && virt_inline_i < kv_size(virt_inline)) { - VirtTextChunk vtc = kv_A(virt_inline, virt_inline_i); - wlv.p_extra = vtc.text; - wlv.n_extra = (int)strlen(wlv.p_extra); - wlv.extra_for_extmark = true; - wlv.c_extra = NUL; - wlv.c_final = NUL; - wlv.extra_attr = vtc.hl_id ? syn_id2attr(vtc.hl_id) : 0; - n_attr = mb_charlen(vtc.text); - // restore search_attr and area_attr when n_extra - // is down to zero - saved_search_attr = search_attr; - saved_area_attr = area_attr; - saved_search_attr_from_match = search_attr_from_match; - search_attr_from_match = false; - search_attr = 0; - area_attr = 0; - extmark_attr = 0; - virt_inline_i++; - // If the text didn't reach until the first window - // column we need to skip cells. - if (skip_cells > 0) { - int virt_text_len = n_attr; - if (virt_text_len > skip_cells) { - int len = mb_charlen2bytelen(wlv.p_extra, skip_cells); - wlv.n_extra -= len; - wlv.p_extra += len; - n_attr -= skip_cells; - // Skipped cells needed to be accounted for in vcol. - skipped_cells += skip_cells; - skip_cells = 0; - } else { - // the whole text is left of the window, drop - // it and advance to the next one - skip_cells -= virt_text_len; - // Skipped cells needed to be accounted for in vcol. - skipped_cells += virt_text_len; - n_attr = 0; - wlv.n_extra = 0; + if (wlv.n_extra <= 0 && virt_inline_i < kv_size(virt_inline)) { + VirtTextChunk vtc = kv_A(virt_inline, virt_inline_i); + wlv.p_extra = vtc.text; + wlv.n_extra = (int)strlen(wlv.p_extra); + wlv.extra_for_extmark = true; + wlv.c_extra = NUL; + wlv.c_final = NUL; + wlv.extra_attr = vtc.hl_id ? syn_id2attr(vtc.hl_id) : 0; + n_attr = mb_charlen(vtc.text); + // restore search_attr and area_attr when n_extra + // is down to zero + saved_search_attr = search_attr; + saved_area_attr = area_attr; + saved_search_attr_from_match = search_attr_from_match; + search_attr_from_match = false; + search_attr = 0; + area_attr = 0; + extmark_attr = 0; + virt_inline_i++; + // If the text didn't reach until the first window + // column we need to skip cells. + if (skip_cells > 0) { + int virt_text_len = n_attr; + if (virt_text_len > skip_cells) { + int len = mb_charlen2bytelen(wlv.p_extra, skip_cells); + wlv.n_extra -= len; + wlv.p_extra += len; + n_attr -= skip_cells; + // Skipped cells needed to be accounted for in vcol. + skipped_cells += skip_cells; + skip_cells = 0; + } else { + // the whole text is left of the window, drop + // it and advance to the next one + skip_cells -= virt_text_len; + // Skipped cells needed to be accounted for in vcol. + skipped_cells += virt_text_len; + n_attr = 0; + wlv.n_extra = 0; + + // go to the start so the next virtual text chunk can be selected. + continue; + } } } + break; } } @@ -3013,8 +3019,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } // The skipped cells need to be accounted for in vcol. - if (wlv.draw_state > WL_STC - && skipped_cells > 0) { + if (wlv.draw_state > WL_STC && skipped_cells > 0) { wlv.vcol += skipped_cells; skipped_cells = 0; } diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 4579fad53f..68a5bb5f9a 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -1956,6 +1956,33 @@ bbbbbbb]]) | ]]} end) + + it('correctly draws when there are multiple overlapping virtual texts on the same line with nowrap', function() + command('set nowrap') + insert('a') + meths.buf_set_extmark(0, ns, 0, 0, + { virt_text = { { string.rep('a', 55), 'Special' } }, virt_text_pos = 'inline' }) + meths.buf_set_extmark(0, ns, 0, 0, + { virt_text = { { string.rep('b', 55), 'Special' } }, virt_text_pos = 'inline' }) + feed('$') + screen:expect { grid = [[ + {28:bbbbbbbbbbbbbbbbbbbbbbbbb}^a | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) end) describe('decorations: virtual lines', function() -- cgit From a37c99048359c27bbb7e1997c2ec318bb2cba787 Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Thu, 20 Apr 2023 02:15:49 +1000 Subject: fix(ui): fix overflowing nowrap virtual text not displaying if tab follows --- src/nvim/drawline.c | 1 + test/functional/ui/decorations_spec.lua | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index abc27c1eda..ad08fd0e10 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -1799,6 +1799,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, area_attr = 0; extmark_attr = 0; virt_inline_i++; + n_skip = 0; // If the text didn't reach until the first window // column we need to skip cells. if (skip_cells > 0) { diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 68a5bb5f9a..f67c4be419 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -1983,6 +1983,38 @@ bbbbbbb]]) | ]]} end) + + it('correctly draws when overflowing virtual text is followed by tab with no wrap', function() + command('set nowrap') + feed('itest') + meths.buf_set_extmark( + 0, + ns, + 0, + 0, + { virt_text = { { string.rep('a', 60), 'Special' } }, virt_text_pos = 'inline' } + ) + feed('0') + screen:expect({ + grid = [[ + {28:aaaaaaaaaaaaaaaaaaaaaa} ^ test | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], + }) + end) end) describe('decorations: virtual lines', function() -- cgit From 584319cb03df44374e110d0c4b1d9b84c2d24aeb Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Sat, 1 Apr 2023 15:04:49 +1100 Subject: vim-patch:9.0.0210: 'list' mode does not work properly with virtual text Problem: 'list' mode does not work properly with virtual text. Solution: Show the "$" at the right position. (closes vim/vim#10913) https://github.com/vim/vim/commit/c3a483fc3c65f649f9985bb88792a465ea18b0a2#diff-15009492c2b1d0a2629908b4618ad51c99d16746f238a0e6451dfe32355ed32fR1653 This commit only contains the seemingly refactored portion (not entirely sure what it does) Co-authored-by: Bram Moolenaar --- src/nvim/drawline.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index ad08fd0e10..9374464a93 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -2482,15 +2482,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, && wlv.line_attr == 0 && wlv.line_attr_lowprio == 0) { // In virtualedit, visual selections may extend beyond end of line - if (area_highlighting && virtual_active() - && wlv.tocol != MAXCOL && wlv.vcol < wlv.tocol) { - wlv.n_extra = 0; - } else { + if (!(area_highlighting && virtual_active() + && wlv.tocol != MAXCOL && wlv.vcol < wlv.tocol)) { wlv.p_extra = at_end_str; - wlv.n_extra = 1; - wlv.c_extra = NUL; - wlv.c_final = NUL; } + wlv.n_extra = 0; } if (wp->w_p_list && wp->w_p_lcs_chars.eol > 0) { c = wp->w_p_lcs_chars.eol; @@ -2867,7 +2863,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, && !has_fold && (*ptr != NUL || lcs_eol_one > 0 - || (wlv.n_extra && (wlv.c_extra != NUL || *wlv.p_extra != NUL)))) { + || (wlv.n_extra > 0 && (wlv.c_extra != NUL || *wlv.p_extra != NUL)))) { c = wp->w_p_lcs_chars.ext; wlv.char_attr = win_hl_attr(wp, HLF_AT); mb_c = c; -- cgit From 29da1a9cf0b44f2edcbff9e45af283f235360947 Mon Sep 17 00:00:00 2001 From: tom-anders <13141438+tom-anders@users.noreply.github.com> Date: Sat, 6 May 2023 15:14:00 +0200 Subject: docs: update api.txt and add inline virtual text to news.txt --- runtime/doc/api.txt | 2 ++ runtime/doc/news.txt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index e7cda59906..f399f1ed25 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -2608,6 +2608,8 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts}) • "overlay": display over the specified column, without shifting the underlying text. • "right_align": display right aligned in the window. + • "inline": display at the specified column, and shift the + buffer text to the right as needed • virt_text_win_col : position the virtual text at a fixed window column (starting from the first text column) diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 9f6ab2ddd0..5690dbf3bc 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -56,6 +56,8 @@ The following new APIs or features were added. • |Query:iter_matches()| now has the ability to set the maximum start depth for matches. +• Added inline virtual text support to |nvim_buf_set_extmark()|. + ============================================================================== CHANGED FEATURES *news-changed* -- cgit From a78fd18ed92d7474d10b5640dcf7a15728d2e03a Mon Sep 17 00:00:00 2001 From: bfredl Date: Tue, 9 May 2023 14:26:03 +0200 Subject: fix(extmark): fix cursor position with both left and right gravity inline text --- src/nvim/charset.c | 14 +++++------ src/nvim/drawline.c | 2 +- src/nvim/plines.c | 22 +++++++++-------- src/nvim/plines.h | 4 +-- test/functional/ui/decorations_spec.lua | 43 +++++++++++++++++++++++++++++++++ 5 files changed, 65 insertions(+), 20 deletions(-) diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 2917bb31ee..49890a460a 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1053,11 +1053,8 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en // make sure we don't go past the end of the line if (*cts.cts_ptr == NUL) { - // NUL at end of line only takes one column - incr = 1; - if (cts.cts_cur_text_width > 0) { - incr = cts.cts_cur_text_width; - } + // NUL at end of line only takes one column, unless there is virtual text + incr = MAX(1, cts.cts_cur_text_width_left + cts.cts_cur_text_width_right); on_NUL = true; break; } @@ -1092,9 +1089,12 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en // cursor at end *cursor = vcol + incr - 1; } else { - if (((State & MODE_INSERT) == 0 || !cts.cts_has_right_gravity) && !on_NUL) { + if (!on_NUL) { // cursor is after inserted text, unless on the NUL - vcol += cts.cts_cur_text_width; + vcol += cts.cts_cur_text_width_left; + if ((State & MODE_INSERT) == 0) { + vcol += cts.cts_cur_text_width_right; + } } // cursor at start *cursor = vcol + head; diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 9374464a93..17d23020fa 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -1757,7 +1757,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, selected, &decor_state); while (true) { - // we could already be inside an existing virt_line with multiple chunks + // we could already be inside an existing inline text with multiple chunks if (!(virt_inline_i < kv_size(virt_inline))) { DecorState *state = &decor_state; for (size_t i = 0; i < kv_size(state->active); i++) { diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 3b4883d5c2..25c745ae97 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -269,7 +269,7 @@ int linetabsize_col(int startcol, char *s) if (cts.cts_has_virt_text && cts.cts_ptr == cts.cts_line) { // check for virtual text in an empty line (void)lbr_chartabsize_adv(&cts); - cts.cts_vcol += cts.cts_cur_text_width; + cts.cts_vcol += cts.cts_cur_text_width_left + cts.cts_cur_text_width_right; } clear_chartabsize_arg(&cts); return cts.cts_vcol; @@ -308,7 +308,7 @@ void win_linetabsize_cts(chartabsize_T *cts, colnr_T len) if (cts->cts_has_virt_text && *cts->cts_ptr == NUL && cts->cts_ptr == cts->cts_line) { (void)win_lbr_chartabsize(cts, NULL); - cts->cts_vcol += cts->cts_cur_text_width; + cts->cts_vcol += cts->cts_cur_text_width_left + cts->cts_cur_text_width_right; } } @@ -323,9 +323,9 @@ void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum, colnr_T cts->cts_vcol = col; cts->cts_line = line; cts->cts_ptr = ptr; - cts->cts_cur_text_width = 0; + cts->cts_cur_text_width_left = 0; + cts->cts_cur_text_width_right = 0; cts->cts_has_virt_text = false; - cts->cts_has_right_gravity = true; cts->cts_row = lnum - 1; if (cts->cts_row >= 0) { @@ -398,7 +398,8 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) int mb_added = 0; int numberextra; - cts->cts_cur_text_width = 0; + cts->cts_cur_text_width_left = 0; + cts->cts_cur_text_width_right = 0; // No 'linebreak', 'showbreak' and 'breakindent': return quickly. if (!wp->w_p_lbr && !wp->w_p_bri && *get_showbreak_value(wp) == NUL @@ -419,14 +420,15 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) mtkey_t mark = marktree_itr_current(cts->cts_iter); if (mark.pos.row != cts->cts_row || mark.pos.col > col) { break; - } else if (mark.pos.col >= col - && mark.pos.col < col + charlen) { // TODO(bfredl): or maybe unconditionally, what - // if byte-misaligned? + } else if (mark.pos.col >= col && mark.pos.col < col + charlen) { if (!mt_end(mark)) { Decoration decor = get_decor(mark); if (decor.virt_text_pos == kVTInline) { - cts->cts_cur_text_width += decor.virt_text_width; - cts->cts_has_right_gravity = mt_right(mark); + if (mt_right(mark)) { + cts->cts_cur_text_width_right += decor.virt_text_width; + } else { + cts->cts_cur_text_width_left += decor.virt_text_width; + } size += decor.virt_text_width; if (*s == TAB) { // tab size changes because of the inserted text diff --git a/src/nvim/plines.h b/src/nvim/plines.h index 6e4768bc7d..fa55357193 100644 --- a/src/nvim/plines.h +++ b/src/nvim/plines.h @@ -14,8 +14,8 @@ typedef struct { int cts_row; bool cts_has_virt_text; // true if if a property inserts text - bool cts_has_right_gravity; - int cts_cur_text_width; // width of current inserted text + int cts_cur_text_width_left; // width of virtual text left of cursor + int cts_cur_text_width_right; // width of virtual text right of cursor MarkTreeIter cts_iter[1]; // TODO(bfredl): iterator in to the marktree for scanning virt text diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index f67c4be419..e8847258f4 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -1745,6 +1745,49 @@ bbbbbbb]]) ]]} end) + it('cursor position is correct when inserting around virtual texts with both left and right gravity ', function() + insert('foo foo foo foo') + meths.buf_set_extmark(0, ns, 0, 8, { virt_text = {{ '>>', 'Special' }}, virt_text_pos = 'inline', right_gravity = false }) + meths.buf_set_extmark(0, ns, 0, 8, { virt_text = {{ '<<', 'Special' }}, virt_text_pos = 'inline', right_gravity = true }) + feed('08l') + screen:expect{ grid = [[ + foo foo {28:>><<}^foo foo | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('i') + screen:expect { grid = [[ + foo foo {28:>>^<<}foo foo | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {24:-- INSERT --} | + ]]} + end) + it('draws correctly with no wrap multiple virtual text, where one is hidden', function() insert('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz') command("set nowrap") -- cgit From c5528e7fd852939a5719ecc35edde89b41275a15 Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 22 May 2023 12:15:41 +0200 Subject: fix(test): clean up inline virtual text tests a little --- test/functional/ui/decorations_spec.lua | 429 ++++++++++---------------------- 1 file changed, 137 insertions(+), 292 deletions(-) diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index e8847258f4..4418729859 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -611,6 +611,20 @@ describe('decorations providers', function() end) end) +local example_text = [[ +for _,item in ipairs(items) do + local text, hl_id_cell, count = unpack(item) + if hl_id_cell ~= nil then + hl_id = hl_id_cell + end + for _ = 1, (count or 1) do + local cell = line[colpos] + cell.text = text + cell.hl_id = hl_id + colpos = colpos+1 + end +end]] + describe('extmark decorations', function() local screen, ns before_each( function() @@ -645,31 +659,11 @@ describe('extmark decorations', function() [25] = {background = Screen.colors.LightRed}; [26] = {background=Screen.colors.DarkGrey, foreground=Screen.colors.LightGrey}; [27] = {background = Screen.colors.Plum1}; - [28] = {foreground = Screen.colors.SlateBlue}; - [29] = {background = Screen.colors.Yellow1}; - [30] = {reverse = true}; - [31] = {foreground = Screen.colors.SlateBlue, background = Screen.colors.LightMagenta}; - [32] = {bold = true, reverse = true}; - [33] = {background = Screen.colors.Red1, bold = true} } ns = meths.create_namespace 'test' end) - local example_text = [[ -for _,item in ipairs(items) do - local text, hl_id_cell, count = unpack(item) - if hl_id_cell ~= nil then - hl_id = hl_id_cell - end - for _ = 1, (count or 1) do - local cell = line[colpos] - cell.text = text - cell.hl_id = hl_id - colpos = colpos+1 - end -end]] - it('empty virtual text at eol should not break colorcolumn #17860', function() insert(example_text) feed('gg') @@ -1171,8 +1165,38 @@ end]] meths.buf_set_extmark(0, ns, 0, 3, { end_col = 6, hl_group = 'TestBold', priority = 20 }) screen:expect_unchanged(true) end) +end) + - it('can have virtual text of inline position', function() +describe('decorations: inline virtual text', function() + local screen, ns + before_each( function() + clear() + screen = Screen.new(50, 10) + screen:attach() + screen:set_default_attr_ids { + [1] = {bold=true, foreground=Screen.colors.Blue}; + [2] = {foreground = Screen.colors.Brown}; + [3] = {bold = true, foreground = Screen.colors.SeaGreen}; + [4] = {background = Screen.colors.Red1, foreground = Screen.colors.Gray100}; + [5] = {background = Screen.colors.Red1, bold = true}; + [6] = {foreground = Screen.colors.DarkCyan}; + [7] = {background = Screen.colors.LightGrey}; + [8] = {bold = true}; + [9] = {background = Screen.colors.Plum1}; + [10] = {foreground = Screen.colors.SlateBlue}; + [11] = {blend = 30, background = Screen.colors.Red1}; + [12] = {background = Screen.colors.Yellow1}; + [13] = {reverse = true}; + [14] = {foreground = Screen.colors.SlateBlue, background = Screen.colors.LightMagenta}; + [15] = {bold = true, reverse = true}; + } + + ns = meths.create_namespace 'test' + end) + + + it('works', function() insert(example_text) feed 'gg' screen:expect{grid=[[ @@ -1185,18 +1209,13 @@ end]] local cell = line[colpos] | cell.text = text | cell.hl_id = hl_id | - colpos = colpos+1 | - end | - end | - {1:~ }| - {1:~ }| | ]]} meths.buf_set_extmark(0, ns, 1, 14, {virt_text={{': ', 'Special'}, {'string', 'Type'}}, virt_text_pos='inline'}) screen:expect{grid=[[ ^for _,item in ipairs(items) do | - local text{28:: }{3:string}, hl_id_cell, count = unpack| + local text{10:: }{3:string}, hl_id_cell, count = unpack| (item) | if hl_id_cell ~= nil then | hl_id = hl_id_cell | @@ -1204,18 +1223,13 @@ end]] for _ = 1, (count or 1) do | local cell = line[colpos] | cell.text = text | - cell.hl_id = hl_id | - colpos = colpos+1 | - end | - end | - {1:~ }| | ]]} - screen:try_resize(55, 15) + screen:try_resize(55, 10) screen:expect{grid=[[ ^for _,item in ipairs(items) do | - local text{28:: }{3:string}, hl_id_cell, count = unpack(item| + local text{10:: }{3:string}, hl_id_cell, count = unpack(item| ) | if hl_id_cell ~= nil then | hl_id = hl_id_cell | @@ -1223,18 +1237,13 @@ end]] for _ = 1, (count or 1) do | local cell = line[colpos] | cell.text = text | - cell.hl_id = hl_id | - colpos = colpos+1 | - end | - end | - {1:~ }| | ]]} - screen:try_resize(56, 15) + screen:try_resize(56, 10) screen:expect{grid=[[ ^for _,item in ipairs(items) do | - local text{28:: }{3:string}, hl_id_cell, count = unpack(item)| + local text{10:: }{3:string}, hl_id_cell, count = unpack(item)| if hl_id_cell ~= nil then | hl_id = hl_id_cell | end | @@ -1242,11 +1251,6 @@ end]] local cell = line[colpos] | cell.text = text | cell.hl_id = hl_id | - colpos = colpos+1 | - end | - end | - {1:~ }| - {1:~ }| | ]]} end) @@ -1260,12 +1264,7 @@ end]] feed '^' feed '4l' screen:expect { grid = [[ - 1234{28: virtual text virtual text }^5678 | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + 1234{10: virtual text virtual text }^5678 | {1:~ }| {1:~ }| {1:~ }| @@ -1285,12 +1284,7 @@ end]] { virt_text = { { ' virtual text ', 'Special' } }, virt_text_pos = 'inline' }) screen:expect { grid = [[ - 1234{28: virtual text }567^8 | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + 1234{10: virtual text }567^8 | {1:~ }| {1:~ }| {1:~ }| @@ -1303,18 +1297,13 @@ end]] ]]} end) - it('has correct highlighting with multi-byte characters in inline virtual text', function() + it('has correct highlighting with multi-byte characters', function() insert('12345678') meths.buf_set_extmark(0, ns, 0, 4, { virt_text = { { 'múlti-byté chñröcters 修补', 'Special' } }, virt_text_pos = 'inline' }) screen:expect { grid = [[ - 1234{28:múlti-byté chñröcters 修补}567^8 | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + 1234{10:múlti-byté chñröcters 修补}567^8 | {1:~ }| {1:~ }| {1:~ }| @@ -1335,7 +1324,7 @@ end]] feed '3l' feed 'a' screen:expect { grid = [[ - 1234{28:^virtual text}5678 | + 1234{10:^virtual text}5678 | {1:~ }| {1:~ }| {1:~ }| @@ -1344,23 +1333,26 @@ end]] {1:~ }| {1:~ }| {1:~ }| + {8:-- INSERT --} | + ]]} + feed '' + screen:expect{grid=[[ + 123^4{10:virtual text}5678 | + {1:~ }| {1:~ }| {1:~ }| {1:~ }| {1:~ }| {1:~ }| - {24:-- INSERT --} | - ]]} - feed '' + {1:~ }| + {1:~ }| + | + ]]} feed '^' feed '4l' feed 'i' screen:expect { grid = [[ - 1234{28:^virtual text}5678 | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + 1234{10:^virtual text}5678 | {1:~ }| {1:~ }| {1:~ }| @@ -1369,8 +1361,7 @@ end]] {1:~ }| {1:~ }| {1:~ }| - {1:~ }| - {24:-- INSERT --} | + {8:-- INSERT --} | ]]} end) @@ -1378,12 +1369,7 @@ end]] meths.buf_set_extmark(0, ns, 0, 0, { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) screen:expect { grid = [[ - {28:^virtual text} | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + {10:^virtual text} | {1:~ }| {1:~ }| {1:~ }| @@ -1407,82 +1393,62 @@ bbbbbbb]]) { virt_text = { { string.rep('X', 50), 'Special' } }, virt_text_pos = 'inline' }) feed('gg0') screen:expect { grid = [[ - {28:^XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| - {28:X} | + {10:^XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + {10:X} | aaaaaaa | - {28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + {10:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| bbbbbbb | {1:~ }| {1:~ }| {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| {1:~ }| | ]]} feed('j') screen:expect { grid = [[ - {28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| - {28:X} | + {10:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + {10:X} | ^aaaaaaa | - {28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + {10:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| bbbbbbb | {1:~ }| {1:~ }| {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| {1:~ }| | ]]} feed('j') screen:expect { grid = [[ - {28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| - {28:X} | + {10:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + {10:X} | aaaaaaa | - {28:^XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + {10:^XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| bbbbbbb | {1:~ }| {1:~ }| {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| {1:~ }| | ]]} feed('j') screen:expect { grid = [[ - {28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| - {28:X} | + {10:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + {10:X} | aaaaaaa | - {28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + {10:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| ^bbbbbbb | {1:~ }| {1:~ }| {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| {1:~ }| | ]]} end) - it('cursor position is correct with virtual text attatched to hard tabs', function() + it('cursor position is correct with virtual text attached to hard tabs', function() command('set noexpandtab') feed('i') feed('') @@ -1493,12 +1459,7 @@ bbbbbbb]]) { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) feed('0') screen:expect { grid = [[ - ^ {28:virtual text} test | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + ^ {10:virtual text} test | {1:~ }| {1:~ }| {1:~ }| @@ -1512,12 +1473,7 @@ bbbbbbb]]) feed('l') screen:expect { grid = [[ - {28:virtual text} ^ test | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + {10:virtual text} ^ test | {1:~ }| {1:~ }| {1:~ }| @@ -1531,12 +1487,7 @@ bbbbbbb]]) feed('l') screen:expect { grid = [[ - {28:virtual text} ^test | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + {10:virtual text} ^test | {1:~ }| {1:~ }| {1:~ }| @@ -1550,12 +1501,7 @@ bbbbbbb]]) feed('l') screen:expect { grid = [[ - {28:virtual text} t^est | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + {10:virtual text} t^est | {1:~ }| {1:~ }| {1:~ }| @@ -1569,12 +1515,7 @@ bbbbbbb]]) feed('l') screen:expect { grid = [[ - {28:virtual text} te^st | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + {10:virtual text} te^st | {1:~ }| {1:~ }| {1:~ }| @@ -1594,12 +1535,7 @@ bbbbbbb]]) meths.buf_set_extmark(0, ns, 0, 3, { virt_text = { { ': virtual text', 'Special' } }, virt_text_pos = 'inline' }) screen:expect { grid = [[ - ^one{28:: virtual text} twoword | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + ^one{10:: virtual text} twoword | {1:~ }| {1:~ }| {1:~ }| @@ -1612,18 +1548,13 @@ bbbbbbb]]) ]]} end) - it('search highlight is correct with virtual text attatched to', function() + it('search highlight is correct', function() insert('foo foo foo foo') feed('0') meths.buf_set_extmark(0, ns, 0, 8, { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) screen:expect { grid = [[ - ^foo foo {28:virtual text}foo foo | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + ^foo foo {10:virtual text}foo foo | {1:~ }| {1:~ }| {1:~ }| @@ -1637,12 +1568,7 @@ bbbbbbb]]) feed('/foo') screen:expect { grid = [[ - {29:foo} {30:foo} {28:virtual text}{29:foo} {29:foo} | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + {12:foo} {13:foo} {10:virtual text}{12:foo} {12:foo} | {1:~ }| {1:~ }| {1:~ }| @@ -1655,20 +1581,14 @@ bbbbbbb]]) ]]} end) - feed('8l') - it('visual select highlight is correct with virtual text attatched to', function() + it('visual select highlight is correct', function() insert('foo foo foo foo') feed('0') meths.buf_set_extmark(0, ns, 0, 8, { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) feed('8l') screen:expect { grid = [[ - foo foo {28:virtual text}^foo foo | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + foo foo {10:virtual text}^foo foo | {1:~ }| {1:~ }| {1:~ }| @@ -1683,10 +1603,7 @@ bbbbbbb]]) feed('v') feed('2h') screen:expect { grid = [[ - foo fo^o{18: }{28:virtual text}{18:f}oo foo | - {1:~ }| - {1:~ }| - {1:~ }| + foo fo^o{7: }{10:virtual text}{7:f}oo foo | {1:~ }| {1:~ }| {1:~ }| @@ -1695,9 +1612,7 @@ bbbbbbb]]) {1:~ }| {1:~ }| {1:~ }| - {1:~ }| - {1:~ }| - {24:-- VISUAL --} | + {8:-- VISUAL --} | ]]} end) @@ -1708,12 +1623,7 @@ bbbbbbb]]) feed('0') feed('8l') screen:expect { grid = [[ - foo foo {28:virtual text}^foo foo | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + foo foo {10:virtual text}^foo foo | {1:~ }| {1:~ }| {1:~ }| @@ -1727,12 +1637,7 @@ bbbbbbb]]) feed('i') screen:expect { grid = [[ - foo foo {28:virtual text}^foo foo | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + foo foo {10:virtual text}^foo foo | {1:~ }| {1:~ }| {1:~ }| @@ -1741,7 +1646,7 @@ bbbbbbb]]) {1:~ }| {1:~ }| {1:~ }| - {24:-- INSERT --} | + {8:-- INSERT --} | ]]} end) @@ -1751,12 +1656,7 @@ bbbbbbb]]) meths.buf_set_extmark(0, ns, 0, 8, { virt_text = {{ '<<', 'Special' }}, virt_text_pos = 'inline', right_gravity = true }) feed('08l') screen:expect{ grid = [[ - foo foo {28:>><<}^foo foo | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + foo foo {10:>><<}^foo foo | {1:~ }| {1:~ }| {1:~ }| @@ -1770,10 +1670,7 @@ bbbbbbb]]) feed('i') screen:expect { grid = [[ - foo foo {28:>>^<<}foo foo | - {1:~ }| - {1:~ }| - {1:~ }| + foo foo {10:>>^<<}foo foo | {1:~ }| {1:~ }| {1:~ }| @@ -1782,9 +1679,7 @@ bbbbbbb]]) {1:~ }| {1:~ }| {1:~ }| - {1:~ }| - {1:~ }| - {24:-- INSERT --} | + {8:-- INSERT --} | ]]} end) @@ -1797,12 +1692,7 @@ bbbbbbb]]) { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) feed('$') screen:expect { grid = [[ - opqrstuvwxyzabcdefghijklmnopqrstuvwx{28:virtual text}y^z| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + opqrstuvwxyzabcdefghijklmnopqrstuvwx{10:virtual text}y^z| {1:~ }| {1:~ }| {1:~ }| @@ -1822,12 +1712,7 @@ bbbbbbb]]) { virt_text = { { string.rep('X', 55), 'Special' } }, virt_text_pos = 'inline' }) feed('$') screen:expect { grid = [[ - {28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}cdefgh^i| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + {10:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}cdefgh^i| {1:~ }| {1:~ }| {1:~ }| @@ -1847,12 +1732,7 @@ bbbbbbb]]) { virt_text = { { string.rep('a', 55), 'Special' } }, virt_text_pos = 'inline' }) feed('gg$') screen:expect { grid = [[ - {28:aaaaaaaaaaaaaaaaaaaaaaaaa}test ^a | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + {10:aaaaaaaaaaaaaaaaaaaaaaaaa}test ^a | {1:~ }| {1:~ }| {1:~ }| @@ -1872,12 +1752,7 @@ bbbbbbb]]) { virt_text = { { string.rep('X', 50), 'Special' } }, virt_text_pos = 'inline' }) feed('$') screen:expect { grid = [[ - {28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}de^f| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + {10:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}de^f| {1:~ }| {1:~ }| {1:~ }| @@ -1899,19 +1774,14 @@ bbbbbbb]]) { virt_text = { { string.rep('X', 55), 'Special' } }, virt_text_pos = 'inline' }) feed('gg0') screen:expect { grid = [[ - {2: 1 }^t{28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| - {2: }{28:XXXXXXXXXX}est | + {2: 1 }^t{10:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + {2: }{10:XXXXXXXXXX}est | {2: 2 }test | {1:~ }| {1:~ }| {1:~ }| {1:~ }| {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| {1:~ }| | ]]} @@ -1924,12 +1794,7 @@ bbbbbbb]]) feed('gg0') command('match ErrorMsg /e/') screen:expect { grid = [[ - ^t{4:e}{28:virtual text}st | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + ^t{4:e}{10:virtual text}st | {1:~ }| {1:~ }| {1:~ }| @@ -1942,12 +1807,7 @@ bbbbbbb]]) ]]} command('match ErrorMsg /s/') screen:expect { grid = [[ - ^te{28:virtual text}{4:s}t | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + ^te{10:virtual text}{4:s}t | {1:~ }| {1:~ }| {1:~ }| @@ -1960,7 +1820,7 @@ bbbbbbb]]) ]]} end) - it('in diff mode virtual text is highlighted correct', function() + it('in diff mode is highlighted correct', function() insert([[ 9000 0009 @@ -1982,20 +1842,15 @@ bbbbbbb]]) command("set diff") feed('gg0') screen:expect { grid = [[ - {27:^000 }│{33:9}{31:test}{27:000 }| - {27:000 }│{27:000}{33:9}{27: }| - {27:000 }│{27:000}{33:9}{27: }| - {27:000 }│{33:9}{27:000 }| - {27:000 }│{27:000}{33:9}{27: }| + {9:^000 }│{5:9}{14:test}{9:000 }| + {9:000 }│{9:000}{5:9}{9: }| + {9:000 }│{9:000}{5:9}{9: }| + {9:000 }│{5:9}{9:000 }| + {9:000 }│{9:000}{5:9}{9: }| │ | {1:~ }│{1:~ }| {1:~ }│{1:~ }| - {1:~ }│{1:~ }| - {1:~ }│{1:~ }| - {1:~ }│{1:~ }| - {1:~ }│{1:~ }| - {1:~ }│{1:~ }| - {32:[No Name] [+] }{30:[No Name] [+] }| + {15:[No Name] [+] }{13:[No Name] [+] }| | ]]} end) @@ -2009,12 +1864,7 @@ bbbbbbb]]) { virt_text = { { string.rep('b', 55), 'Special' } }, virt_text_pos = 'inline' }) feed('$') screen:expect { grid = [[ - {28:bbbbbbbbbbbbbbbbbbbbbbbbb}^a | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + {10:bbbbbbbbbbbbbbbbbbbbbbbbb}^a | {1:~ }| {1:~ }| {1:~ }| @@ -2040,12 +1890,7 @@ bbbbbbb]]) feed('0') screen:expect({ grid = [[ - {28:aaaaaaaaaaaaaaaaaaaaaa} ^ test | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + {10:aaaaaaaaaaaaaaaaaaaaaa} ^ test | {1:~ }| {1:~ }| {1:~ }| @@ -2081,7 +1926,7 @@ describe('decorations: virtual lines', function() ns = meths.create_namespace 'test' end) - local example_text = [[ + local example_text2 = [[ if (h->n_buckets < new_n_buckets) { // expand khkey_t *new_keys = (khkey_t *)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); h->keys = new_keys; @@ -2092,7 +1937,7 @@ if (h->n_buckets < new_n_buckets) { // expand }]] it('works with one line', function() - insert(example_text) + insert(example_text2) feed 'gg' meths.buf_set_extmark(0, ns, 1, 33, { virt_lines={ {{">> ", "NonText"}, {"krealloc", "Identifier"}, {": change the size of an allocation"}}}; @@ -2201,7 +2046,7 @@ if (h->n_buckets < new_n_buckets) { // expand end) it('works with text at the beginning of the buffer', function() - insert(example_text) + insert(example_text2) feed 'gg' screen:expect{grid=[[ @@ -2262,7 +2107,7 @@ if (h->n_buckets < new_n_buckets) { // expand end) it('works with text at the end of the buffer', function() - insert(example_text) + insert(example_text2) feed 'G' screen:expect{grid=[[ @@ -2381,7 +2226,7 @@ if (h->n_buckets < new_n_buckets) { // expand end) it('works beyond end of the buffer with virt_lines_above', function() - insert(example_text) + insert(example_text2) feed 'G' screen:expect{grid=[[ @@ -2652,7 +2497,7 @@ if (h->n_buckets < new_n_buckets) { // expand end) it('works with sign and numbercolumns', function() - insert(example_text) + insert(example_text2) feed 'gg' command 'set number signcolumn=yes' screen:expect{grid=[[ @@ -2718,7 +2563,7 @@ if (h->n_buckets < new_n_buckets) { // expand it('works with hard tabs', function() - insert(example_text) + insert(example_text2) feed 'gg' meths.buf_set_extmark(0, ns, 1, 0, { virt_lines={ {{">>", "NonText"}, {"\tvery\ttabby", "Identifier"}, {"text\twith\ttabs"}}}; @@ -2805,7 +2650,7 @@ describe('decorations: signs', function() meths.set_option_value('signcolumn', 'auto:9', {}) end) - local example_text = [[ + local example_test3 = [[ l1 l2 l3 @@ -2814,7 +2659,7 @@ l5 ]] it('can add a single sign (no end row)', function() - insert(example_text) + insert(example_test3) feed 'gg' meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S'}) @@ -2835,7 +2680,7 @@ l5 end) it('can add a single sign (with end row)', function() - insert(example_text) + insert(example_test3) feed 'gg' meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S', end_row=1}) @@ -2857,7 +2702,7 @@ l5 it('can add multiple signs (single extmark)', function() pending('TODO(lewis6991): Support ranged signs') - insert(example_text) + insert(example_test3) feed 'gg' meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S', end_row = 2}) @@ -2879,7 +2724,7 @@ l5 it('can add multiple signs (multiple extmarks)', function() pending('TODO(lewis6991): Support ranged signs') - insert(example_text) + insert(example_test3) feed'gg' meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S1'}) @@ -2901,7 +2746,7 @@ l5 end) it('can add multiple signs (multiple extmarks) 2', function() - insert(example_text) + insert(example_test3) feed 'gg' meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S1'}) @@ -2941,7 +2786,7 @@ l5 it('can add multiple signs (multiple extmarks) 3', function() pending('TODO(lewis6991): Support ranged signs') - insert(example_text) + insert(example_test3) feed 'gg' meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S1', end_row=2}) @@ -2962,7 +2807,7 @@ l5 end) it('can add multiple signs (multiple extmarks) 4', function() - insert(example_text) + insert(example_test3) feed 'gg' meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S1', end_row=0}) @@ -2983,7 +2828,7 @@ l5 end) it('works with old signs', function() - insert(example_text) + insert(example_test3) feed 'gg' helpers.command('sign define Oldsign text=x') @@ -3010,7 +2855,7 @@ l5 it('works with old signs (with range)', function() pending('TODO(lewis6991): Support ranged signs') - insert(example_text) + insert(example_test3) feed 'gg' helpers.command('sign define Oldsign text=x') @@ -3039,7 +2884,7 @@ l5 it('can add a ranged sign (with start out of view)', function() pending('TODO(lewis6991): Support ranged signs') - insert(example_text) + insert(example_test3) command 'set signcolumn=yes:2' feed 'gg' feed '2' @@ -3096,7 +2941,7 @@ l5 it('works with priority #19716', function() screen:try_resize(20, 3) - insert(example_text) + insert(example_test3) feed 'gg' helpers.command('sign define Oldsign text=O3') @@ -3126,7 +2971,7 @@ l5 it('does not set signcolumn for signs without text', function() screen:try_resize(20, 3) meths.set_option_value('signcolumn', 'auto', {}) - insert(example_text) + insert(example_test3) feed 'gg' meths.buf_set_extmark(0, ns, 0, -1, {number_hl_group='Error'}) screen:expect{grid=[[ -- cgit From 6eeb28845a930fbfe128dc3edc7969b0d9b2ed1c Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 22 May 2023 12:46:12 +0200 Subject: refactor(drawline): move inline text code to its own function --- src/nvim/drawline.c | 208 +++++++++++++++++++++++++++------------------------- src/nvim/plines.h | 1 - 2 files changed, 109 insertions(+), 100 deletions(-) diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 17d23020fa..f065cf5ef4 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -98,6 +98,7 @@ typedef struct { int char_attr; ///< attributes for next character int n_extra; ///< number of extra bytes + int n_attr; ///< chars with special attr char *p_extra; ///< string of extra chars, plus NUL, only used ///< when c_extra and c_final are NUL char *p_extra_free; ///< p_extra buffer that needs to be freed @@ -123,6 +124,14 @@ typedef struct { int filler_lines; ///< nr of filler lines to be drawn int filler_todo; ///< nr of filler lines still to do + 1 SignTextAttrs sattrs[SIGN_SHOW_MAX]; ///< sign attributes for the sign column + + VirtText virt_inline; + size_t virt_inline_i; + + bool reset_extra_attr; + + int skip_cells; // nr of cells to skip for virtual text + int skipped_cells; // nr of skipped virtual text cells } winlinevars_T; /// for line_putchar. Contains the state that needs to be remembered from @@ -846,6 +855,73 @@ static void apply_cursorline_highlight(win_T *wp, winlinevars_T *wlv) } } +static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t v, bool *do_save) +{ + while (true) { + // we could already be inside an existing inline text with multiple chunks + if (!(wlv->virt_inline_i < kv_size(wlv->virt_inline))) { + DecorState *state = &decor_state; + for (size_t i = 0; i < kv_size(state->active); i++) { + DecorRange *item = &kv_A(state->active, i); + if (item->start_row != state->row + || !kv_size(item->decor.virt_text) + || item->decor.virt_text_pos != kVTInline) { + continue; + } + if (item->win_col >= -1 && item->start_col == v) { + wlv->virt_inline = item->decor.virt_text; + wlv->virt_inline_i = 0; + item->win_col = -2; + break; + } + } + } + + if (wlv->n_extra == 0 || !wlv->extra_for_extmark) { + wlv->reset_extra_attr = false; + } + + if (wlv->n_extra <= 0 && wlv->virt_inline_i < kv_size(wlv->virt_inline)) { + VirtTextChunk vtc = kv_A(wlv->virt_inline, wlv->virt_inline_i); + wlv->p_extra = vtc.text; + wlv->n_extra = (int)strlen(wlv->p_extra); + wlv->extra_for_extmark = true; + wlv->c_extra = NUL; + wlv->c_final = NUL; + wlv->extra_attr = vtc.hl_id ? syn_id2attr(vtc.hl_id) : 0; + wlv->n_attr = mb_charlen(vtc.text); + wlv->virt_inline_i++; + *do_save = true; + // If the text didn't reach until the first window + // column we need to skip cells. + if (wlv->skip_cells > 0) { + int virt_text_len = wlv->n_attr; + if (virt_text_len > wlv->skip_cells) { + int len = mb_charlen2bytelen(wlv->p_extra, wlv->skip_cells); + wlv->n_extra -= len; + wlv->p_extra += len; + wlv->n_attr -= wlv->skip_cells; + // Skipped cells needed to be accounted for in vcol. + wlv->skipped_cells += wlv->skip_cells; + wlv->skip_cells = 0; + } else { + // the whole text is left of the window, drop + // it and advance to the next one + wlv->skip_cells -= virt_text_len; + // Skipped cells needed to be accounted for in vcol. + wlv->skipped_cells += virt_text_len; + wlv->n_attr = 0; + wlv->n_extra = 0; + + // go to the start so the next virtual text chunk can be selected. + continue; + } + } + } + break; + } +} + static bool check_mb_utf8(int *c, int *u8cc) { if (utf_char2len(*c) > 1) { @@ -967,15 +1043,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, // at end-of-line bool has_fold = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0; - int n_attr = 0; // chars with special attr int saved_attr2 = 0; // char_attr saved for n_attr int n_attr3 = 0; // chars with overruling special attr int saved_attr3 = 0; // char_attr saved for n_attr3 int n_skip = 0; // nr of chars to skip for 'nowrap' or // concealing - int skip_cells = 0; // nr of cells to skip for virtual text - int skipped_cells = 0; // nr of skipped virtual text cells int fromcol_prev = -2; // start of inverting after cursor bool noinvcur = false; // don't invert the cursor @@ -991,8 +1064,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, int search_attr = 0; // attributes desired by 'hlsearch' int saved_search_attr = 0; // search_attr to be used when n_extra // goes to zero - bool reset_extra_attr = false; - int vcol_save_attr = 0; // saved attr for 'cursorcolumn' int syntax_attr = 0; // attributes desired by syntax bool has_syntax = false; // this buffer has syntax highl. @@ -1045,9 +1116,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, int left_curline_col = 0; int right_curline_col = 0; - VirtText virt_inline = KV_INITIAL_VALUE; - size_t virt_inline_i = 0; - int match_conc = 0; ///< cchar for match functions bool on_last_col = false; int syntax_flags = 0; @@ -1449,7 +1517,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, // If there the text doesn't reach to the desired column, need to skip // "skip_cells" cells when virtual text follows. if (!wp->w_p_wrap && v > wlv.vcol) { - skip_cells = (int)(v - wlv.vcol); + wlv.skip_cells = (int)(v - wlv.vcol); } // Adjust for when the inverted text is before the screen, @@ -1756,77 +1824,19 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, extmark_attr = decor_redraw_col(wp, (colnr_T)v, wlv.off, selected, &decor_state); - while (true) { - // we could already be inside an existing inline text with multiple chunks - if (!(virt_inline_i < kv_size(virt_inline))) { - DecorState *state = &decor_state; - for (size_t i = 0; i < kv_size(state->active); i++) { - DecorRange *item = &kv_A(state->active, i); - if (item->start_row != state->row - || !kv_size(item->decor.virt_text) - || item->decor.virt_text_pos != kVTInline) { - continue; - } - if (item->win_col >= -1 && item->start_col == v) { - virt_inline = item->decor.virt_text; - virt_inline_i = 0; - item->win_col = -2; - break; - } - } - } - - if (wlv.n_extra == 0 || !wlv.extra_for_extmark) { - reset_extra_attr = false; - } - - if (wlv.n_extra <= 0 && virt_inline_i < kv_size(virt_inline)) { - VirtTextChunk vtc = kv_A(virt_inline, virt_inline_i); - wlv.p_extra = vtc.text; - wlv.n_extra = (int)strlen(wlv.p_extra); - wlv.extra_for_extmark = true; - wlv.c_extra = NUL; - wlv.c_final = NUL; - wlv.extra_attr = vtc.hl_id ? syn_id2attr(vtc.hl_id) : 0; - n_attr = mb_charlen(vtc.text); - // restore search_attr and area_attr when n_extra - // is down to zero - saved_search_attr = search_attr; - saved_area_attr = area_attr; - saved_search_attr_from_match = search_attr_from_match; - search_attr_from_match = false; - search_attr = 0; - area_attr = 0; - extmark_attr = 0; - virt_inline_i++; - n_skip = 0; - // If the text didn't reach until the first window - // column we need to skip cells. - if (skip_cells > 0) { - int virt_text_len = n_attr; - if (virt_text_len > skip_cells) { - int len = mb_charlen2bytelen(wlv.p_extra, skip_cells); - wlv.n_extra -= len; - wlv.p_extra += len; - n_attr -= skip_cells; - // Skipped cells needed to be accounted for in vcol. - skipped_cells += skip_cells; - skip_cells = 0; - } else { - // the whole text is left of the window, drop - // it and advance to the next one - skip_cells -= virt_text_len; - // Skipped cells needed to be accounted for in vcol. - skipped_cells += virt_text_len; - n_attr = 0; - wlv.n_extra = 0; - - // go to the start so the next virtual text chunk can be selected. - continue; - } - } - } - break; + bool do_save = false; + handle_inline_virtual_text(wp, &wlv, v, &do_save); + if (do_save) { + // restore search_attr and area_attr when n_extra is down to zero + // TODO(bfredl): this is ugly as fuck. look if we can do this some other way. + saved_search_attr = search_attr; + saved_area_attr = area_attr; + saved_search_attr_from_match = search_attr_from_match; + search_attr_from_match = false; + search_attr = 0; + area_attr = 0; + extmark_attr = 0; + n_skip = 0; } } @@ -1971,7 +1981,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, if (wlv.extra_for_extmark) { // wlv.extra_attr should be used at this position but not // any further. - reset_extra_attr = true; + wlv.reset_extra_attr = true; } } wlv.extra_for_extmark = false; @@ -2027,7 +2037,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.c_extra = NUL; wlv.c_final = NUL; if (area_attr == 0 && search_attr == 0) { - n_attr = wlv.n_extra + 1; + wlv.n_attr = wlv.n_extra + 1; wlv.extra_attr = win_hl_attr(wp, HLF_8); saved_attr2 = wlv.char_attr; // save current attr } @@ -2082,7 +2092,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.c_final = NUL; c = ' '; if (area_attr == 0 && search_attr == 0) { - n_attr = wlv.n_extra + 1; + wlv.n_attr = wlv.n_extra + 1; wlv.extra_attr = win_hl_attr(wp, HLF_AT); saved_attr2 = wlv.char_attr; // save current attr } @@ -2316,7 +2326,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } else { c = (c == ' ') ? wp->w_p_lcs_chars.space : wp->w_p_lcs_chars.nbsp; } - n_attr = 1; + wlv.n_attr = 1; wlv.extra_attr = win_hl_attr(wp, HLF_0); saved_attr2 = wlv.char_attr; // save current attr mb_c = c; @@ -2339,7 +2349,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, c = wp->w_p_lcs_chars.space; } - n_attr = 1; + wlv.n_attr = 1; wlv.extra_attr = win_hl_attr(wp, HLF_0); saved_attr2 = wlv.char_attr; // save current attr mb_c = c; @@ -2455,7 +2465,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.c_extra = wp->w_p_lcs_chars.tab2; } wlv.c_final = wp->w_p_lcs_chars.tab3; - n_attr = tab_len + 1; + wlv.n_attr = tab_len + 1; wlv.extra_attr = win_hl_attr(wp, HLF_0); saved_attr2 = wlv.char_attr; // save current attr mb_c = c; @@ -2496,7 +2506,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, lcs_eol_one = -1; ptr--; // put it back at the NUL wlv.extra_attr = win_hl_attr(wp, HLF_AT); - n_attr = 1; + wlv.n_attr = 1; mb_c = c; mb_utf8 = check_mb_utf8(&c, u8cc); } else if (c != NUL) { @@ -2525,7 +2535,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.n_extra = byte2cells(c) - 1; c = (uint8_t)(*wlv.p_extra++); } - n_attr = wlv.n_extra + 1; + wlv.n_attr = wlv.n_extra + 1; wlv.extra_attr = win_hl_attr(wp, HLF_8); saved_attr2 = wlv.char_attr; // save current attr mb_utf8 = false; // don't draw as UTF-8 @@ -2585,7 +2595,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } } wlv.n_extra = 0; - n_attr = 0; + wlv.n_attr = 0; } else if (n_skip == 0) { is_concealing = true; n_skip = 1; @@ -2620,10 +2630,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } // Don't override visual selection highlighting. - if (n_attr > 0 && wlv.draw_state == WL_LINE && !search_attr_from_match) { + if (wlv.n_attr > 0 && wlv.draw_state == WL_LINE && !search_attr_from_match) { wlv.char_attr = hl_combine_attr(wlv.char_attr, wlv.extra_attr); - if (reset_extra_attr) { - reset_extra_attr = false; + if (wlv.reset_extra_attr) { + wlv.reset_extra_attr = false; wlv.extra_attr = 0; // search_attr_from_match can be restored now that the extra_attr has been applied search_attr_from_match = saved_search_attr_from_match; @@ -2647,7 +2657,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.c_extra = MB_FILLER_CHAR; wlv.c_final = NUL; wlv.n_extra = 1; - n_attr = 2; + wlv.n_attr = 2; wlv.extra_attr = win_hl_attr(wp, HLF_AT); } mb_c = c; @@ -2983,7 +2993,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.boguscols += wlv.n_extra; } wlv.n_extra = 0; - n_attr = 0; + wlv.n_attr = 0; } if (utf_char2cells(mb_c) > 1) { @@ -3008,7 +3018,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, if (wlv.n_extra > 0) { wlv.vcol += wlv.n_extra; wlv.n_extra = 0; - n_attr = 0; + wlv.n_attr = 0; } } } else { @@ -3016,9 +3026,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } // The skipped cells need to be accounted for in vcol. - if (wlv.draw_state > WL_STC && skipped_cells > 0) { - wlv.vcol += skipped_cells; - skipped_cells = 0; + if (wlv.draw_state > WL_STC && wlv.skipped_cells > 0) { + wlv.vcol += wlv.skipped_cells; + wlv.skipped_cells = 0; } // Only advance the "wlv.vcol" when after the 'number' or @@ -3038,7 +3048,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } // restore attributes after last 'listchars' or 'number' char - if (n_attr > 0 && wlv.draw_state == WL_LINE && --n_attr == 0) { + if (wlv.n_attr > 0 && wlv.draw_state == WL_LINE && --wlv.n_attr == 0) { wlv.char_attr = saved_attr2; } diff --git a/src/nvim/plines.h b/src/nvim/plines.h index fa55357193..2ce7133705 100644 --- a/src/nvim/plines.h +++ b/src/nvim/plines.h @@ -17,7 +17,6 @@ typedef struct { int cts_cur_text_width_left; // width of virtual text left of cursor int cts_cur_text_width_right; // width of virtual text right of cursor MarkTreeIter cts_iter[1]; - // TODO(bfredl): iterator in to the marktree for scanning virt text int cts_vcol; // virtual column at current position } chartabsize_T; -- cgit