# HG changeset patch # User Bram Moolenaar # Date 1676124004 -3600 # Node ID 96d6d31dd66b70e1fb27b5f3d811620011e76ef2 # Parent 65c19095296fa80eea549abfcf669b3e20f879c4 patch 9.0.1301: virtual text below empty line not displayed Commit: https://github.com/vim/vim/commit/9d9a20ee8799bafe9caac616fef11b7a26db6a8d Author: Bram Moolenaar Date: Sat Feb 11 13:49:01 2023 +0000 patch 9.0.1301: virtual text below empty line not displayed Problem: Virtual text below empty line not displayed. Solution: Adjust flags and computations. (closes https://github.com/vim/vim/issues/11959) diff --git a/src/charset.c b/src/charset.c --- a/src/charset.c +++ b/src/charset.c @@ -1152,6 +1152,8 @@ win_lbr_chartabsize( * First get the normal size, without 'linebreak' or text properties */ size = win_chartabsize(wp, s, vcol); + if (*s == NUL) + size = 0; // NUL is not displayed # ifdef FEAT_PROP_POPUP if (cts->cts_has_prop_with_text) diff --git a/src/drawline.c b/src/drawline.c --- a/src/drawline.c +++ b/src/drawline.c @@ -1085,6 +1085,7 @@ win_line( int save_did_emsg; #endif #ifdef FEAT_PROP_POPUP + int did_line = FALSE; // set to TRUE when line text done int text_prop_count; int text_prop_next = 0; // next text property to use textprop_T *text_props = NULL; @@ -1914,17 +1915,20 @@ win_line( // Check if any active property ends. for (pi = 0; pi < text_props_active; ++pi) { - int tpi = text_prop_idxs[pi]; - - if (text_props[tpi].tp_col != MAXCOL - && bcol >= text_props[tpi].tp_col - 1 - + text_props[tpi].tp_len) + int tpi = text_prop_idxs[pi]; + textprop_T *tp = &text_props[tpi]; + + // An inline property ends when after the start column plus + // length. An "above" property ends when used and n_extra + // is zero. + if ((tp->tp_col != MAXCOL + && bcol >= tp->tp_col - 1 + tp->tp_len)) { if (pi + 1 < text_props_active) mch_memmove(text_prop_idxs + pi, text_prop_idxs + pi + 1, sizeof(int) - * (text_props_active - (pi + 1))); + * (text_props_active - (pi + 1))); --text_props_active; --pi; # ifdef FEAT_LINEBREAK @@ -1940,6 +1944,9 @@ win_line( // not on the next char yet, don't start another prop --bcol; # endif + int display_text_first = FALSE; + int active_before = text_props_active; + // Add any text property that starts in this column. // With 'nowrap' and not in the first screen line only "below" // text prop can show. @@ -1950,16 +1957,24 @@ win_line( || wlv.row == startrow || (text_props[text_prop_next].tp_flags & TP_FLAG_ALIGN_BELOW))) - || (bcol == 0 && - (text_props[text_prop_next].tp_flags + || (bcol == 0 + && (text_props[text_prop_next].tp_flags & TP_FLAG_ALIGN_ABOVE))) : bcol >= text_props[text_prop_next].tp_col - 1)) { if (text_props[text_prop_next].tp_col == MAXCOL - && *ptr == NUL && wp->w_p_list && lcs_eol_one > 0) + && *ptr == NUL + && ((wp->w_p_list && lcs_eol_one > 0) + || (ptr == line + && !did_line + && (text_props[text_prop_next].tp_flags + & TP_FLAG_ALIGN_BELOW)))) { - // first display the '$' after the line + // first display the '$' after the line or display an + // empty line text_prop_follows = TRUE; + if (text_props_active == active_before) + display_text_first = TRUE; break; } if (text_props[text_prop_next].tp_col == MAXCOL @@ -1978,16 +1993,18 @@ win_line( text_prop_id = 0; reset_extra_attr = FALSE; } - if (text_props_active > 0 && wlv.n_extra == 0) + if (text_props_active > 0 && wlv.n_extra == 0 + && !display_text_first) { int used_tpi = -1; int used_attr = 0; int other_tpi = -1; + text_prop_above = FALSE; + text_prop_follows = FALSE; + // Sort the properties on priority and/or starting last. // Then combine the attributes, highest priority last. - text_prop_above = FALSE; - text_prop_follows = FALSE; sort_text_props(wp->w_buffer, text_props, text_prop_idxs, text_props_active); @@ -2159,8 +2176,8 @@ win_line( // must wrap anyway. text_prop_above = above; text_prop_follows |= other_tpi != -1 - && (wp->w_p_wrap - || (text_props[other_tpi].tp_flags + && (wp->w_p_wrap + || (text_props[other_tpi].tp_flags & (TP_FLAG_ALIGN_BELOW | TP_FLAG_ALIGN_RIGHT))); if (bail_out) @@ -2495,6 +2512,12 @@ win_line( #ifdef FEAT_LINEBREAK c0 = *ptr; #endif +#ifdef FEAT_PROP_POPUP + if (c == NUL) + // text is finished, may display a "below" virtual text + did_line = TRUE; +#endif + if (has_mbyte) { mb_c = c; @@ -3576,20 +3599,31 @@ win_line( // At end of the text line. if (c == NUL) { - draw_screen_line(wp, &wlv); - - // Update w_cline_height and w_cline_folded if the cursor line was - // updated (saves a call to plines() later). - if (wp == curwin && lnum == curwin->w_cursor.lnum) +#ifdef FEAT_PROP_POPUP + if (text_prop_follows) + { + // Put the pointer back to the NUL. + --ptr; + c = ' '; + } + else +#endif { - curwin->w_cline_row = startrow; - curwin->w_cline_height = wlv.row - startrow; + draw_screen_line(wp, &wlv); + + // Update w_cline_height and w_cline_folded if the cursor line + // was updated (saves a call to plines() later). + if (wp == curwin && lnum == curwin->w_cursor.lnum) + { + curwin->w_cline_row = startrow; + curwin->w_cline_height = wlv.row - startrow; #ifdef FEAT_FOLDING - curwin->w_cline_folded = FALSE; + curwin->w_cline_folded = FALSE; #endif - curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW); + curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW); + } + break; } - break; } // Show "extends" character from 'listchars' if beyond the line end and diff --git a/src/testdir/dumps/Test_prop_above_below_empty_1.dump b/src/testdir/dumps/Test_prop_above_below_empty_1.dump new file mode 100644 --- /dev/null +++ b/src/testdir/dumps/Test_prop_above_below_empty_1.dump @@ -0,0 +1,16 @@ +| +0#af5f00255#ffffff0@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52 +| +0#af5f00255&@1|1| |1+0#0000000&@7| @47 +| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52 +| +0#af5f00255&@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52 +| +0#af5f00255&@1|2| | +0#0000000&@55 +| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52 +| +0#af5f00255&@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52 +| +0#af5f00255&@1|3| |3+0#0000000&@8| @46 +| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52 +| +0#af5f00255&@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52 +| +0#af5f00255&@1|4| | +0#0000000&@55 +| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52 +| +0#af5f00255&@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52 +| +0#af5f00255&@1|5| >5+0#0000000&@10| @44 +| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52 +@42|5|,|1|-|5|7| @7|A|l@1| diff --git a/src/testdir/test_textprop.vim b/src/testdir/test_textprop.vim --- a/src/testdir/test_textprop.vim +++ b/src/testdir/test_textprop.vim @@ -2779,6 +2779,28 @@ func Test_prop_with_text_below_after_emp call StopVimInTerminal(buf) endfunc +func Test_prop_with_text_above_below_empty() + CheckRunVimInTerminal + + let lines =<< trim END + setlocal number + call setline(1, ['11111111', '', '333333333', '', '55555555555']) + + let vt = 'test' + call prop_type_add(vt, {'highlight': 'ToDo'}) + for ln in range(1, line('$')) + call prop_add(ln, 0, {'type': vt, 'text': '---', 'text_align': 'above'}) + call prop_add(ln, 0, {'type': vt, 'text': '+++', 'text_align': 'below'}) + endfor + normal G + END + call writefile(lines, 'XscriptPropAboveBelowEmpty', 'D') + let buf = RunVimInTerminal('-S XscriptPropAboveBelowEmpty', #{rows: 16, cols: 60}) + call VerifyScreenDump(buf, 'Test_prop_above_below_empty_1', {}) + + call StopVimInTerminal(buf) +endfunc + func Test_prop_with_text_below_after_match() CheckRunVimInTerminal diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -696,6 +696,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1301, +/**/ 1300, /**/ 1299,