diff options
-rw-r--r-- | src/nvim/drawline.c | 119 | ||||
-rw-r--r-- | test/functional/ui/decorations_spec.lua | 51 |
2 files changed, 124 insertions, 46 deletions
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 679f92c3b8..884abb6b88 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -930,28 +930,22 @@ static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t if (wlv->skip_cells > 0) { int virt_text_width = (int)mb_string2cells(wlv->p_extra); if (virt_text_width > wlv->skip_cells) { - int cells_to_skip = wlv->skip_cells; + int skip_cells_remaining = wlv->skip_cells; // Skip cells in the text. - while (cells_to_skip > 0) { + while (skip_cells_remaining > 0) { + int cells = utf_ptr2cells(wlv->p_extra); + if (cells > skip_cells_remaining) { + break; + } int c_len = utfc_ptr2len(wlv->p_extra); - cells_to_skip -= utf_ptr2cells(wlv->p_extra); + skip_cells_remaining -= cells; wlv->p_extra += c_len; wlv->n_extra -= c_len; wlv->n_attr--; } - // If a double-width char doesn't fit, pad with space. - if (cells_to_skip < 0) { - int pad_len = -cells_to_skip; - char *padded = get_extra_buf((size_t)(wlv->n_extra + pad_len) + 1); - memset(padded, ' ', (size_t)pad_len); - xmemcpyz(padded + pad_len, wlv->p_extra, (size_t)wlv->n_extra); - wlv->p_extra = padded; - wlv->n_extra += pad_len; - wlv->n_attr += pad_len; - } // Skipped cells needed to be accounted for in vcol. - wlv->skipped_cells += wlv->skip_cells; - wlv->skip_cells = 0; + wlv->skipped_cells += wlv->skip_cells - skip_cells_remaining; + wlv->skip_cells = skip_cells_remaining; } else { // The whole text is left of the window, drop // it and advance to the next one. @@ -1076,6 +1070,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s bool in_multispace = false; // in multiple consecutive spaces int multispace_pos = 0; // position in lcs-multispace string + int n_extra_next = 0; // n_extra to use after current extra chars + int extra_attr_next = -1; // extra_attr to use after current extra chars + bool search_attr_from_match = false; // if search_attr is from :match bool has_decor = false; // this buffer has decoration @@ -1950,7 +1947,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s if (wlv.col >= grid->cols - 1 && schar_cells(mb_schar) == 2) { mb_c = '>'; mb_l = 1; - (void)mb_l; mb_schar = schar_from_ascii(mb_c); multi_attr = win_hl_attr(wp, HLF_AT); @@ -1963,30 +1959,58 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s wlv.n_extra -= mb_l; wlv.p_extra += mb_l; } - } - // 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; - saved_search_attr = 0; - } - if (area_attr == 0 && *ptr != NUL) { - area_attr = saved_area_attr; - saved_area_attr = 0; - } - if (decor_attr == 0) { - decor_attr = saved_decor_attr; - saved_decor_attr = 0; + // If a double-width char doesn't fit at the left side display a '<'. + if (wlv.filler_todo <= 0 && wlv.skip_cells > 0 && mb_l > 1) { + if (wlv.n_extra > 0) { + n_extra_next = wlv.n_extra; + extra_attr_next = wlv.extra_attr; + } + wlv.n_extra = 1; + wlv.sc_extra = schar_from_ascii(MB_FILLER_CHAR); + wlv.sc_final = NUL; + mb_schar = schar_from_ascii(' '); + mb_c = ' '; + mb_l = 1; + (void)mb_l; + wlv.n_attr++; + wlv.extra_attr = win_hl_attr(wp, HLF_AT); } + } - if (wlv.extra_for_extmark) { - // wlv.extra_attr should be used at this position but not - // any further. + if (wlv.n_extra <= 0) { + // Only restore search_attr and area_attr when there is no "n_extra" to show. + if (n_extra_next <= 0) { + if (search_attr == 0) { + search_attr = saved_search_attr; + saved_search_attr = 0; + } + if (area_attr == 0 && *ptr != NUL) { + area_attr = saved_area_attr; + saved_area_attr = 0; + } + if (decor_attr == 0) { + decor_attr = saved_decor_attr; + saved_decor_attr = 0; + } + if (wlv.extra_for_extmark) { + // wlv.extra_attr should be used at this position but not any further. + wlv.reset_extra_attr = true; + extra_attr_next = -1; + } + wlv.extra_for_extmark = false; + } else { + assert(wlv.sc_extra != NUL || wlv.sc_final != NUL); + assert(wlv.p_extra != NULL); + wlv.sc_extra = NUL; + wlv.sc_final = NUL; + wlv.n_extra = n_extra_next; + n_extra_next = 0; + // wlv.extra_attr should be used at this position, but extra_attr_next + // should be used after that. wlv.reset_extra_attr = true; + assert(extra_attr_next >= 0); } - wlv.extra_for_extmark = false; } } else if (wlv.filler_todo > 0) { // Wait with reading text until filler lines are done. Still need to @@ -2563,14 +2587,19 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s wp->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL; } - // Don't override visual selection highlighting. + // Use "wlv.extra_attr", but don't override visual selection highlighting. if (wlv.n_attr > 0 && !search_attr_from_match) { wlv.char_attr = hl_combine_attr(wlv.char_attr, wlv.extra_attr); 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; + if (extra_attr_next >= 0) { + wlv.extra_attr = extra_attr_next; + extra_attr_next = -1; + } else { + 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; + } } } @@ -2584,14 +2613,20 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s && wlv.skip_cells <= 0 && mb_schar != NUL) { lcs_prec_todo = NUL; - // TODO(zeertzjq): handle the n_extra > 0 case - if (schar_cells(mb_schar) > 1 && wlv.n_extra == 0) { + if (schar_cells(mb_schar) > 1) { // Double-width character being overwritten by the "precedes" // character, need to fill up half the character. wlv.sc_extra = schar_from_ascii(MB_FILLER_CHAR); wlv.sc_final = NUL; + if (wlv.n_extra > 0) { + assert(wlv.p_extra != NULL); + n_extra_next = wlv.n_extra; + extra_attr_next = wlv.extra_attr; + wlv.n_attr = MAX(wlv.n_attr + 1, 2); + } else { + wlv.n_attr = 2; + } wlv.n_extra = 1; - wlv.n_attr = 2; wlv.extra_attr = win_hl_attr(wp, HLF_AT); } mb_schar = wp->w_p_lcs_chars.prec; diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 971d126fa7..99e6b5f78f 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -3368,17 +3368,24 @@ describe('decorations: inline virtual text', function() command("set nowrap") api.nvim_buf_set_extmark(0, ns, 0, 2, { virt_text = { { string.rep('X', 55), 'Special' } }, virt_text_pos = 'inline' }) feed('$') - screen:expect{grid=[[ + screen:expect([[ {10:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}cdefgh^i| {1:~ }| | - ]]} + ]]) + command('set list listchars+=precedes:!') + screen:expect([[ + {1:!}{10:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}cdefgh^i| + {1:~ }| + | + ]]) end) it('draws correctly with no wrap and multibyte virtual text', function() insert('12345678') command('set nowrap') api.nvim_buf_set_extmark(0, ns, 0, 2, { + hl_mode = 'replace', virt_text = { { 'α口β̳γ̲=', 'Special' }, { '❤️', 'Special' } }, virt_text_pos = 'inline', }) @@ -3405,9 +3412,33 @@ describe('decorations: inline virtual text', function() {1:~ }| | ]]) + feed('V') + screen:expect([[ + {10:口β̳γ̲=❤️}{7:34567}^8 | + {1:~ }| + {8:-- VISUAL LINE --} | + ]]) + command('set list listchars+=precedes:!') + screen:expect([[ + {1:!<}{10:β̳γ̲=❤️}{7:34567}^8 | + {1:~ }| + {8:-- VISUAL LINE --} | + ]]) feed('zl') screen:expect([[ - {10: β̳γ̲=❤️}34567^8 | + {1:!}{10:β̳γ̲=❤️}{7:34567}^8 | + {1:~ }| + {8:-- VISUAL LINE --} | + ]]) + command('set nolist') + screen:expect([[ + {1:<}{10:β̳γ̲=❤️}{7:34567}^8 | + {1:~ }| + {8:-- VISUAL LINE --} | + ]]) + feed('<Esc>') + screen:expect([[ + {1:<}{10:β̳γ̲=❤️}34567^8 | {1:~ }| | ]]) @@ -3435,9 +3466,21 @@ describe('decorations: inline virtual text', function() {1:~ }| | ]]) + command('set list') + screen:expect([[ + {1:!<}34567^8 | + {1:~ }| + | + ]]) feed('zl') screen:expect([[ - {10: }34567^8 | + {1:!}34567^8 | + {1:~ }| + | + ]]) + command('set nolist') + screen:expect([[ + {1:<}34567^8 | {1:~ }| | ]]) |