patch 9.0.1301: virtual text below empty line not displayed v9.0.1301
authorBram Moolenaar <Bram@vim.org>
Sat, 11 Feb 2023 13:49:01 +0000 (13:49 +0000)
committerBram Moolenaar <Bram@vim.org>
Sat, 11 Feb 2023 13:49:01 +0000 (13:49 +0000)
Problem:    Virtual text below empty line not displayed.
Solution:   Adjust flags and computations. (closes #11959)

src/charset.c
src/drawline.c
src/testdir/dumps/Test_prop_above_below_empty_1.dump [new file with mode: 0644]
src/testdir/test_textprop.vim
src/version.c

index 9609299717ecbfd14b6a487c152896af6e4a199d..ebec2cf40175b25a7a266c30eface7873f7c28e1 100644 (file)
@@ -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)
index b4208a833c7025fd658581636c1a483973d683ee..4328c06c53a1a96982bc4cc4864e13dd8ec1d349 100644 (file)
@@ -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;
 
-                   // 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 the properties on priority and/or starting last.
+                   // Then combine the attributes, highest priority last.
                    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 (file)
index 0000000..f470016
--- /dev/null
@@ -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| 
index e00bad6d34f2ee8ea81d9d5e4524327abca182c0..b628c8215d848de93d0cc8cab9490a3a48739cc2 100644 (file)
@@ -2779,6 +2779,28 @@ func Test_prop_with_text_below_after_empty()
   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
 
index f28cfd8e237be77dc9f5b10d5a1be20577504845..524abf792ff9c5b0a738a9071d8981c4905aa24b 100644 (file)
@@ -695,6 +695,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1301,
 /**/
     1300,
 /**/