From 35e50d79c630b05f67a840ebe21b4043ba9a6066 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 15 Sep 2023 20:30:50 +0800 Subject: [PATCH] fix(extmarks): overlay virt_text position after 'showbreak' (#25175) Also make virt_text_hide work properly. --- src/nvim/decoration.c | 31 +++++++++++--- src/nvim/decoration.h | 1 - src/nvim/drawline.c | 36 ++++++++++++---- src/nvim/spell.c | 2 +- test/functional/ui/decorations_spec.lua | 55 ++++++++++++++++++++++++- 5 files changed, 106 insertions(+), 19 deletions(-) diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index 0181cc8983..f4ca31040a 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -268,6 +268,28 @@ static void decor_add(DecorState *state, int start_row, int start_col, int end_r kv_A(state->active, index) = range; } +/// Initialize the draw_col of a newly-added non-inline virtual text item. +static void decor_init_draw_col(int win_col, bool hidden, DecorRange *item) +{ + if (win_col < 0) { + item->draw_col = win_col; + } else if (item->decor.virt_text_pos == kVTOverlay) { + item->draw_col = (item->decor.virt_text_hide && hidden) ? INT_MIN : win_col; + } else { + item->draw_col = -1; + } +} + +void decor_recheck_draw_col(int win_col, bool hidden, DecorState *state) +{ + for (size_t i = 0; i < kv_size(state->active); i++) { + DecorRange *item = &kv_A(state->active, i); + if (item->draw_col == -3) { + decor_init_draw_col(win_col, hidden, item); + } + } +} + int decor_redraw_col(win_T *wp, int col, int win_col, bool hidden, DecorState *state) { buf_T *buf = wp->w_buffer; @@ -349,12 +371,9 @@ next_mark: spell = item.decor.spell; } if (item.start_row == state->row && item.start_col <= col - && decor_virt_pos(&item.decor) && item.draw_col == -1) { - if (item.decor.virt_text_pos == kVTOverlay) { - item.draw_col = (item.decor.virt_text_hide && hidden) ? INT_MIN : win_col; - } else if (win_col < 0 && item.decor.virt_text_pos != kVTInline) { - item.draw_col = win_col; - } + && decor_virt_pos(&item.decor) && item.draw_col == -1 + && item.decor.virt_text_pos != kVTInline) { + decor_init_draw_col(win_col, hidden, &item); } if (keep) { kv_A(state->active, j++) = item; diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index dfd26294f3..0f191aa870 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -84,7 +84,6 @@ typedef struct { bool virt_text_owned; /// Screen column to draw the virtual text. /// When -1, the virtual text may be drawn after deciding where. - /// When -2, the virtual text should be drawn at the start of the screen line. /// When -3, the virtual text should be drawn on the next screen line. /// When INT_MIN, the virtual text should no longer be drawn. int draw_col; diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index cd1a5df8de..e1550e0ece 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -282,10 +282,6 @@ static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int } else if (item->decor.virt_text_pos == kVTWinCol) { item->draw_col = MAX(item->decor.col + col_off, 0); } - } else if (item->draw_col == -2) { - item->draw_col = col_off; - } else if (item->draw_col == -3) { - item->draw_col = item->decor.virt_text_pos == kVTOverlay ? -2 : -1; } if (item->draw_col < 0) { continue; @@ -1145,6 +1141,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl bool saved_search_attr_from_match = false; int win_col_offset = 0; // offset for window columns + bool area_active = false; // whether in Visual selection, for virtual text + bool decor_need_recheck = false; // call decor_recheck_draw_col() at next char char buf_fold[FOLD_TEXT_LEN]; // Hold value returned by get_foldtext @@ -1786,9 +1784,27 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl } if (has_decor && wlv.n_extra == 0) { - bool selected = (area_highlighting - && ((wlv.vcol >= wlv.fromcol && wlv.vcol < wlv.tocol) - || (noinvcur && wlv.vcol == wp->w_virtcol))); + // Duplicate the Visual area check after this block, + // but don't check inside p_extra here. + if (wlv.vcol == wlv.fromcol + || (wlv.vcol + 1 == wlv.fromcol + && (wlv.n_extra == 0 && utf_ptr2cells(ptr) > 1)) + || (vcol_prev == fromcol_prev + && vcol_prev < wlv.vcol + && wlv.vcol < wlv.tocol)) { + area_active = true; + } else if (area_active + && (wlv.vcol == wlv.tocol + || (noinvcur && wlv.vcol == wp->w_virtcol))) { + area_active = false; + } + + bool selected = (area_active || (area_highlighting && noinvcur + && wlv.vcol == wp->w_virtcol)); + if (decor_need_recheck) { + decor_recheck_draw_col(wlv.off, selected, &decor_state); + decor_need_recheck = false; + } extmark_attr = decor_redraw_col(wp, (colnr_T)v, wlv.off, selected, &decor_state); if (!has_fold && wp->w_buffer->b_virt_text_inline > 0) { @@ -1822,10 +1838,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl && vcol_prev < wlv.vcol // not at margin && wlv.vcol < wlv.tocol)) { *area_attr_p = vi_attr; // start highlighting + area_active = true; } else if (*area_attr_p != 0 && (wlv.vcol == wlv.tocol || (noinvcur && wlv.vcol == wp->w_virtcol))) { *area_attr_p = 0; // stop highlighting + area_active = false; } if (!has_fold && wlv.n_extra == 0) { @@ -3087,9 +3105,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // At the end of screen line: might need to peek for decorations just after // this position. if (!has_fold && wp->w_p_wrap && wlv.n_extra == 0) { - // FIXME: virt_text_hide doesn't work for overlay virt_text at the next char - // as it's not easy to check if the next char is inside Visual selection. decor_redraw_col(wp, (int)(ptr - line), -3, false, &decor_state); + // Check position/hiding of virtual text again on next screen line. + decor_need_recheck = true; } else if (has_fold || !wp->w_p_wrap) { // Without wrapping, we might need to display right_align and win_col // virt_text for the entire text line. diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 72e21a9130..38e045a08b 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -1280,7 +1280,7 @@ static TriState decor_spell_nav_col(win_T *wp, linenr_T lnum, linenr_T *decor_ln decor_redraw_line(wp, lnum - 1, &decor_state); *decor_lnum = lnum; } - decor_redraw_col(wp, col, col, false, &decor_state); + decor_redraw_col(wp, col, 0, false, &decor_state); return decor_state.spell; } diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 85093566a5..daa4b4bdb3 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -869,7 +869,7 @@ describe('extmark decorations', function() insert(('ab'):rep(100)) for i = 0, 9 do meths.buf_set_extmark(0, ns, 0, 42 + i, { virt_text={{tostring(i), 'ErrorMsg'}}, virt_text_pos='overlay'}) - meths.buf_set_extmark(0, ns, 0, 91 + i, { virt_text={{tostring(i), 'ErrorMsg'}}, virt_text_pos='overlay', virt_text_hide = true}) + meths.buf_set_extmark(0, ns, 0, 91 + i, { virt_text={{tostring(i), 'ErrorMsg'}}, virt_text_pos='overlay', virt_text_hide=true}) end screen:expect{grid=[[ ababababababababababababababababababababab{4:01234567}| @@ -880,7 +880,58 @@ describe('extmark decorations', function() | ]]} - command('set number') + command('set showbreak=++') + screen:expect{grid=[[ + ababababababababababababababababababababab{4:01234567}| + {1:++}{4:89}abababababababababababababababababababa{4:0123456}| + {1:++}{4:789}babababababababababababababababababababababab| + {1:++}abababababababababababababababababababababababab| + {1:++}ababa^b | + | + ]]} + + feed('2gkvg0') + screen:expect{grid=[[ + ababababababababababababababababababababab{4:01234567}| + {1:++}{4:89}abababababababababababababababababababa{4:0123456}| + {1:++}^a{18:babab}ababababababababababababababababababababab| + {1:++}abababababababababababababababababababababababab| + {1:++}ababab | + {24:-- VISUAL --} | + ]]} + + feed('o') + screen:expect{grid=[[ + ababababababababababababababababababababab{4:01234567}| + {1:++}{4:89}abababababababababababababababababababa{4:0123456}| + {1:++}{18:ababa}^bababababababababababababababababababababab| + {1:++}abababababababababababababababababababababababab| + {1:++}ababab | + {24:-- VISUAL --} | + ]]} + + feed('gk') + screen:expect{grid=[[ + ababababababababababababababababababababab{4:01234567}| + {1:++}{4:89}aba^b{18:ababababababababababababababababababababab}| + {1:++}{18:a}{4:89}babababababababababababababababababababababab| + {1:++}abababababababababababababababababababababababab| + {1:++}ababab | + {24:-- VISUAL --} | + ]]} + + feed('o') + screen:expect{grid=[[ + ababababababababababababababababababababab{4:01234567}| + {1:++}{4:89}aba{18:bababababababababababababababababababababab}| + {1:++}^a{4:89}babababababababababababababababababababababab| + {1:++}abababababababababababababababababababababababab| + {1:++}ababab | + {24:-- VISUAL --} | + ]]} + + feed('$') + command('set number showbreak=') screen:expect{grid=[[ {2: 1 }ababababababababababababababababababababab{4:0123}| {2: }{4:456789}abababababababababababababababababababa{4:0}|