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()