diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index 265bc11661..0181cc8983 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -348,18 +348,12 @@ next_mark: if (active && item.decor.spell != kNone) { spell = item.decor.spell; } - if (item.start_row == state->row && decor_virt_pos(&item.decor) - && item.draw_col != INT_MIN) { - if (item.start_col <= col) { - if (item.decor.virt_text_pos == kVTOverlay && item.draw_col == -1) { - item.draw_col = (item.decor.virt_text_hide && hidden) ? INT_MIN : win_col; - } else if (item.draw_col == -3) { - item.draw_col = -1; - } - } else if (wp->w_p_wrap - && (item.decor.virt_text_pos == kVTRightAlign - || item.decor.virt_text_pos == kVTWinCol)) { - item.draw_col = -3; + 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; } } if (keep) { diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index 3d16aa803e..dfd26294f3 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -84,7 +84,8 @@ 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 -3, the virtual text should be drawn on a later screen line. + /// 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; uint64_t ns_id; diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index d0e31b41c3..b22699231d 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -282,6 +282,10 @@ 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; @@ -3080,10 +3084,16 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl if (has_decor && (wp->w_p_rl ? (wlv.col < 0) : (wlv.col >= grid->cols))) { // At the end of screen line: might need to peek for decorations just after - // this position. Without wrapping, we might need to display win_col overlays - // from the entire text line. - colnr_T nextpos = wp->w_p_wrap ? (colnr_T)(ptr - line) : (colnr_T)strlen(line); - decor_redraw_col(wp, nextpos, wlv.off, true, &decor_state); + // this position. + if (wp->w_p_wrap) { + // 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); + } else { + // Without wrapping, we might need to display right_align and win_col + // virt_text for the entire text line. + decor_redraw_col(wp, MAXCOL, -1, true, &decor_state); + } } // At end of screen line and there is more to come: Display the line diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 4262f7ce77..8a94eaef1b 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -859,6 +859,43 @@ describe('extmark decorations', function() ]]} end) + it('overlay virtual text works with wrapped lines #25158', function() + screen:try_resize(50, 6) + 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}) + end + screen:expect{grid=[[ + ababababababababababababababababababababab{4:01234567}| + {4:89}abababababababababababababababababababa{4:012345678}| + {4:9}babababababababababababababababababababababababab| + ababababababababababababababababababababababababa^b| + {1:~ }| + | + ]]} + + command('set number') + screen:expect{grid=[[ + {2: 1 }ababababababababababababababababababababab{4:0123}| + {2: }{4:456789}abababababababababababababababababababa{4:0}| + {2: }{4:123456789}babababababababababababababababababab| + {2: }ababababababababababababababababababababababab| + {2: }abababababababa^b | + | + ]]} + + command('set cpoptions+=n') + screen:expect{grid=[[ + {2: 1 }ababababababababababababababababababababab{4:0123}| + {4:456789}abababababababababababababababababababa{4:01234}| + {4:56789}babababababababababababababababababababababab| + ababababababababababababababababababababababababab| + aba^b | + | + ]]} + end) + it('virt_text_hide hides overlay virtual text when extmark is off-screen', function() screen:try_resize(50, 3) command('set nowrap') @@ -1028,12 +1065,12 @@ describe('extmark decorations', function() it('can have virtual text of right_align and fixed win_col position', function() insert(example_text) feed 'gg' - meths.buf_set_extmark(0, ns, 1, 0, { virt_text={{'Very', 'ErrorMsg'}}, virt_text_win_col=31, hl_mode='blend'}) + meths.buf_set_extmark(0, ns, 1, 0, { virt_text={{'Very', 'ErrorMsg'}}, virt_text_win_col=31, hl_mode='blend'}) meths.buf_set_extmark(0, ns, 1, 0, { virt_text={{'VERY', 'ErrorMsg'}}, virt_text_pos='right_align', hl_mode='blend'}) - meths.buf_set_extmark(0, ns, 2, 10, { virt_text={{'Much', 'ErrorMsg'}}, virt_text_win_col=31, hl_mode='blend'}) + meths.buf_set_extmark(0, ns, 2, 10, { virt_text={{'Much', 'ErrorMsg'}}, virt_text_win_col=31, hl_mode='blend'}) meths.buf_set_extmark(0, ns, 2, 10, { virt_text={{'MUCH', 'ErrorMsg'}}, virt_text_pos='right_align', hl_mode='blend'}) - meths.buf_set_extmark(0, ns, 3, 15, { virt_text={{'Error', 'ErrorMsg'}}, virt_text_win_col=31, hl_mode='blend'}) - meths.buf_set_extmark(0, ns, 3, 15, { virt_text={{'ERROR', 'ErrorMsg'}}, virt_text_pos='right_align', hl_mode='blend'}) + meths.buf_set_extmark(0, ns, 3, 14, { virt_text={{'Error', 'ErrorMsg'}}, virt_text_win_col=31, hl_mode='blend'}) + meths.buf_set_extmark(0, ns, 3, 14, { virt_text={{'ERROR', 'ErrorMsg'}}, virt_text_pos='right_align', hl_mode='blend'}) meths.buf_set_extmark(0, ns, 7, 21, { virt_text={{'-', 'NonText'}}, virt_text_win_col=4, hl_mode='blend'}) meths.buf_set_extmark(0, ns, 7, 21, { virt_text={{'-', 'NonText'}}, virt_text_pos='right_align', hl_mode='blend'}) -- empty virt_text should not change anything @@ -1191,40 +1228,40 @@ describe('extmark decorations', function() | ]]} - command 'set cpoptions-=n nonumber nowrap' + command 'set cpoptions-=n nowrap' screen:expect{grid=[[ - for _,item in ipairs(items) do | - local text, hl_id_cell, cou{4:Very} unpack(ite{4:VERY}| - if | - hl_id_cell ~= nil then {4:Much} {4:MUCH}| - --^ -- -- -- -- -- -- --{4:Error}- -- -- h{4:ERROR}| - end | - for _ = 1, (count or 1) do | - local cell = line[colpos] | - {1:-} cell.text = text {1:-}| - cell.hl_id = hl_id | - colpos = colpos+1 | - end | - end | + {2: 1 }for _,item in ipairs(items) do | + {2: 2 } local text, hl_id_cell, cou{4:Very} unpack{4:VERY}| + {2: 3 } if | + {2: 4 }hl_id_cell ~= nil then {4:Much} {4:MUCH}| + {2: 5 } --^ -- -- -- -- -- -- --{4:Error}- -- {4:ERROR}| + {2: 6 } end | + {2: 7 } for _ = 1, (count or 1) do | + {2: 8 } local cell = line[colpos] | + {2: 9 } {1:-} cell.text = text {1:-}| + {2: 10 } cell.hl_id = hl_id | + {2: 11 } colpos = colpos+1 | + {2: 12 } end | + {2: 13 }end | {1:~ }| | ]]} - feed '8zl' + feed '12zl' screen:expect{grid=[[ - em in ipairs(items) do | - l text, hl_id_cell, count = unp{4:Very}item) {4:VERY}| - | - ll ~= nil then {4:Much} {4:MUCH}| - --^ -- -- -- -- -- -- -- -- -- -{4:Error}hl_id = h{4:ERROR}| - | - _ = 1, (count or 1) do | - local cell = line[colpos] | - cell{1:-}text = text {1:-}| - cell.hl_id = hl_id | - colpos = colpos+1 | - | - | + {2: 1 }n ipairs(items) do | + {2: 2 }xt, hl_id_cell, count = unpack({4:Very}) {4:VERY}| + {2: 3 } | + {2: 4 }= nil then {4:Much} {4:MUCH}| + {2: 5 }^- -- -- -- -- -- -- -- -- -- --{4:Error}d = h{4:ERROR}| + {2: 6 } | + {2: 7 }1, (count or 1) do | + {2: 8 }l cell = line[colpos] | + {2: 9 }.tex{1:-} = text {1:-}| + {2: 10 }.hl_id = hl_id | + {2: 11 }os = colpos+1 | + {2: 12 } | + {2: 13 } | {1:~ }| | ]]} @@ -1243,7 +1280,7 @@ describe('extmark decorations', function() -- 1. With 'wrap' it is never shown. -- 2. With 'nowrap' it is shown only if the extmark is hidden before leftcol. meths.buf_set_extmark(0, ns, 0, 0, { virt_text = {{'AA', 'Underlined'}}, hl_mode = 'combine', virt_text_pos = 'overlay' }) - meths.buf_set_extmark(0, ns, 0, 1, { virt_text = {{'BB', 'Underlined'}}, hl_mode = 'combine', virt_text_win_col = 10 }) + meths.buf_set_extmark(0, ns, 0, 5, { virt_text = {{'BB', 'Underlined'}}, hl_mode = 'combine', virt_text_win_col = 10 }) meths.buf_set_extmark(0, ns, 0, 2, { virt_text = {{'CC', 'Underlined'}}, hl_mode = 'combine', virt_text_pos = 'right_align' }) screen:expect{grid=[[ {29:AA}{33:- 2 lin}{29:BB}{33:: 11111·····························}{29:CC}|