]> granicus.if.org Git - vim/commitdiff
patch 9.0.0247: cannot add padding to virtual text without highlight v9.0.0247
authorBram Moolenaar <Bram@vim.org>
Tue, 23 Aug 2022 17:39:37 +0000 (18:39 +0100)
committerBram Moolenaar <Bram@vim.org>
Tue, 23 Aug 2022 17:39:37 +0000 (18:39 +0100)
Problem:    Cannot add padding to virtual text without highlight.
Solution:   Add the "text_padding_left" argument. (issue #10906)

15 files changed:
runtime/doc/textprop.txt
src/charset.c
src/drawline.c
src/errors.h
src/proto/drawline.pro
src/proto/textprop.pro
src/structs.h
src/testdir/dumps/Test_prop_right_align_twice_2.dump
src/testdir/dumps/Test_prop_text_with_padding_1.dump [new file with mode: 0644]
src/testdir/dumps/Test_prop_text_with_padding_2.dump [new file with mode: 0644]
src/testdir/dumps/Test_prop_text_with_padding_3.dump [new file with mode: 0644]
src/testdir/dumps/Test_prop_with_text_after_wraps_1.dump
src/testdir/test_textprop.vim
src/textprop.c
src/version.c

index 9a148455e0ff1e45bc89c7faf432212fb6b47e0a..846915f787e9a81a0995f3cc5827a992fe0572ca 100644 (file)
@@ -126,6 +126,7 @@ prop_add({lnum}, {col}, {props})
                If {col} is invalid an error is given. *E964*
 
                {props} is a dictionary with these fields:
+                  type         name of the text property type
                   length       length of text in bytes, can only be used
                                for a property that does not continue in
                                another line; can be zero
@@ -142,9 +143,10 @@ prop_add({lnum}, {col}, {props})
                                automatically to a negative number; otherwise
                                zero is used
                   text         text to be displayed before {col}, or after the
-                               line if {col} is zero
+                               line if {col} is zero; prepend and/or append
+                               spaces for padding with highlighting
                                                        *E1294*
-                  text_align   when "text" is present and {col} is zero
+                  text_align   when "text" is present and {col} is zero;
                                specifies where to display the text:
                                   after   after the end of the line
                                   right   right aligned in the window (unless
@@ -152,14 +154,20 @@ prop_add({lnum}, {col}, {props})
                                           line)
                                   below   in the next screen line
                                When omitted "after" is used.  Only one
-                               "right" property can fit in earch line.
+                               "right" property can fit in each line, if
+                               there are two ore more these will go in a
+                               separate line (still right aligned).
+                  text_padding_left                            *E1296*
+                               used when "text" is present and {col} is zero;
+                               padding between the end of the text line
+                               (leftmost column for "below") and the virtual
+                               text, not highlighted
                   text_wrap    when "text" is present and {col} is zero,
                                specifies what happens if the text doesn't
                                fit:
                                   wrap      wrap the text to the next line
                                   truncate  truncate the text to make it fit
                                When omitted "truncate" is used.
-                  type         name of the text property type
                All fields except "type" are optional.
 
                It is an error when both "length" and "end_lnum" or "end_col"
index 32539bf7acaba6614af728fcf94a9b72dc162803..299de54779eb7a0446d9b271e0fc498af18df0a5 100644 (file)
@@ -957,26 +957,26 @@ init_chartabsize_arg(
 #ifdef FEAT_PROP_POPUP
     if (lnum > 0)
     {
-       char_u *prop_start;
+       char_u  *prop_start;
+       int     count;
 
-       cts->cts_text_prop_count = get_text_props(wp->w_buffer, lnum,
-                                                         &prop_start, FALSE);
-       if (cts->cts_text_prop_count > 0)
+       count = get_text_props(wp->w_buffer, lnum, &prop_start, FALSE);
+       cts->cts_text_prop_count = count;
+       if (count > 0)
        {
            // Make a copy of the properties, so that they are properly
-           // aligned.
-           cts->cts_text_props = ALLOC_MULT(textprop_T,
-                                                   cts->cts_text_prop_count);
+           // aligned.  Make it twice as long for the sorting below.
+           cts->cts_text_props = ALLOC_MULT(textprop_T, count * 2);
            if (cts->cts_text_props == NULL)
                cts->cts_text_prop_count = 0;
            else
            {
-               int i;
+               int     i;
 
-               mch_memmove(cts->cts_text_props, prop_start,
-                              cts->cts_text_prop_count * sizeof(textprop_T));
-               for (i = 0; i < cts->cts_text_prop_count; ++i)
-                   if (cts->cts_text_props[i].tp_id < 0)
+               mch_memmove(cts->cts_text_props + count, prop_start,
+                                                  count * sizeof(textprop_T));
+               for (i = 0; i < count; ++i)
+                   if (cts->cts_text_props[i + count].tp_id < 0)
                    {
                        cts->cts_has_prop_with_text = TRUE;
                        break;
@@ -987,6 +987,27 @@ init_chartabsize_arg(
                    VIM_CLEAR(cts->cts_text_props);
                    cts->cts_text_prop_count = 0;
                }
+               else
+               {
+                   int     *text_prop_idxs;
+
+                   // Need to sort the array to get any truncation right.
+                   // Do the sorting in the second part of the array, then
+                   // move the sorted props to the first part of the array.
+                   text_prop_idxs = ALLOC_MULT(int, count);
+                   if (text_prop_idxs != NULL)
+                   {
+                       for (i = 0; i < count; ++i)
+                           text_prop_idxs[i] = i + count;
+                       sort_text_props(curbuf, cts->cts_text_props,
+                                                       text_prop_idxs, count);
+                       // Here we want the reverse order.
+                       for (i = 0; i < count; ++i)
+                           cts->cts_text_props[count - i - 1] =
+                                       cts->cts_text_props[text_prop_idxs[i]];
+                       vim_free(text_prop_idxs);
+                   }
+               }
            }
        }
     }
@@ -1159,6 +1180,11 @@ win_lbr_chartabsize(
        int         col = (int)(s - line);
        garray_T    *gap = &wp->w_buffer->b_textprop_text;
 
+       // The "$" for 'list' mode will go between the EOL and
+       // the text prop, account for that.
+       if (wp->w_p_list && wp->w_lcs_chars.eol != NUL)
+           ++vcol;
+
        for (i = 0; i < cts->cts_text_prop_count; ++i)
        {
            textprop_T *tp = cts->cts_text_props + i;
@@ -1176,46 +1202,21 @@ win_lbr_chartabsize(
 
                if (p != NULL)
                {
-                   int cells = vim_strsize(p);
+                   int cells;
 
                    if (tp->tp_col == MAXCOL)
                    {
-                       int below = (tp->tp_flags & TP_FLAG_ALIGN_BELOW);
-                       int right = (tp->tp_flags & TP_FLAG_ALIGN_RIGHT);
-                       int wrap = (tp->tp_flags & TP_FLAG_WRAP);
-                       int len = (int)STRLEN(p);
-                       int n_used = len;
-
-                       // The "$" for 'list' mode will go between the EOL and
-                       // the text prop, account for that.
-                       if (wp->w_p_list && wp->w_lcs_chars.eol != NUL)
-                           ++vcol;
-
-                       // Keep in sync with where textprop_size_after_trunc()
-                       // is called in win_line().
-                       if (!wrap)
-                       {
-                           added = wp->w_width - (vcol + size) % wp->w_width;
-                           cells = textprop_size_after_trunc(wp,
-                                                    below, added, p, &n_used);
-                       }
-                       if (below)
-                           cells += wp->w_width - (vcol + size) % wp->w_width;
-                       else if (right)
-                       {
-                           len = wp->w_width - vcol % wp->w_width;
-                           if (len > cells + size)
-                               // add the padding for right-alignment
-                               cells = len - size;
-                           else if (len == 0)
-                               // padding to right-align in the next line
-                               cells += cells > wp->w_width ? 0
-                                                         :wp->w_width - cells;
-                       }
+                       int n_extra = (int)STRLEN(p);
+
+                       cells = text_prop_position(wp, tp,
+                                           (vcol + size) % wp->w_width,
+                                                    &n_extra, &p, NULL, NULL);
 #ifdef FEAT_LINEBREAK
                        no_sbr = TRUE;  // don't use 'showbreak' now
 #endif
                    }
+                   else
+                       cells = vim_strsize(p);
                    cts->cts_cur_text_width += cells;
                    cts->cts_start_incl = tp->tp_flags & TP_FLAG_START_INCL;
                    size += cells;
@@ -1231,6 +1232,8 @@ win_lbr_chartabsize(
            if (tp->tp_col != MAXCOL && tp->tp_col - 1 > col)
                break;
        }
+       if (wp->w_p_list && wp->w_lcs_chars.eol != NUL)
+           --vcol;
     }
 # endif
 
index b29f5b19aefd79f47b3557a64c73de70a71091fe..039a8c94d7d16e3535a641e00a7979c08531d45a 100644 (file)
@@ -277,74 +277,123 @@ get_sign_display_info(
 }
 #endif
 
-#ifdef FEAT_PROP_POPUP
-static textprop_T      *current_text_props = NULL;
-static buf_T           *current_buf = NULL;
-
+#if defined(FEAT_PROP_POPUP) || defined(PROTO)
 /*
- * Function passed to qsort() to sort text properties.
- * Return 1 if "s1" has priority over "s2", -1 if the other way around, zero if
- * both have the same priority.
+ * Take care of padding, right-align and truncation of virtual text after a
+ * line.
+ * if "n_attr" is not NULL then "n_extra" and "p_extra" are adjusted for any
+ * padding, right-align and truncation.  Otherwise only the size is computed.
+ * When "n_attr" is NULL returns the number of screen cells used.
+ * Otherwise returns TRUE when drawing continues on the next line.
  */
-    static int
-text_prop_compare(const void *s1, const void *s2)
+    int
+text_prop_position(
+       win_T       *wp,
+       textprop_T  *tp,
+       int         vcol,           // current screen column
+       int         *n_extra,       // nr of bytes for virtual text
+       char_u      **p_extra,      // virtual text
+       int         *n_attr,        // attribute cells, NULL if not used
+       int         *n_attr_skip)   // cells to skip attr, NULL if not used
 {
-    int  idx1, idx2;
-    textprop_T *tp1, *tp2;
-    proptype_T  *pt1, *pt2;
-    colnr_T col1, col2;
-
-    idx1 = *(int *)s1;
-    idx2 = *(int *)s2;
-    tp1 = &current_text_props[idx1];
-    tp2 = &current_text_props[idx2];
-    col1 = tp1->tp_col;
-    col2 = tp2->tp_col;
-    if (col1 == MAXCOL && col2 == MAXCOL)
+    int            right = (tp->tp_flags & TP_FLAG_ALIGN_RIGHT);
+    int            below = (tp->tp_flags & TP_FLAG_ALIGN_BELOW);
+    int            wrap = (tp->tp_flags & TP_FLAG_WRAP);
+    int            padding = tp->tp_col == MAXCOL && tp->tp_len > 1
+                                 ? tp->tp_len - 1 : 0;
+    int            col_with_padding = vcol + (below ? 0 : padding);
+    int            room = wp->w_width - col_with_padding;
+    int            added = room;
+    int            n_used = *n_extra;
+    char_u  *l = NULL;
+    int            strsize = vim_strsize(*p_extra);
+    int            cells = wrap ? strsize
+             : textprop_size_after_trunc(wp, below, added, *p_extra, &n_used);
+
+    if (wrap || right || below || padding > 0 || n_used < *n_extra)
     {
-       int flags1 = 0;
-       int flags2 = 0;
-
-       // both props add text are after the line, order on 0: after (default),
-       // 1: right, 2: below (comes last)
-       if (tp1->tp_flags & TP_FLAG_ALIGN_RIGHT)
-           flags1 = 1;
-       if (tp1->tp_flags & TP_FLAG_ALIGN_BELOW)
-           flags1 = 2;
-       if (tp2->tp_flags & TP_FLAG_ALIGN_RIGHT)
-           flags2 = 1;
-       if (tp2->tp_flags & TP_FLAG_ALIGN_BELOW)
-           flags2 = 2;
-       if (flags1 != flags2)
-           return flags1 < flags2 ? 1 : -1;
-    }
-
-    // property that inserts text has priority over one that doesn't
-    if ((tp1->tp_id < 0) != (tp2->tp_id < 0))
-       return tp1->tp_id < 0 ? 1 : -1;
+       // Right-align: fill with spaces
+       if (right)
+           added -= cells;
+       if (added < 0
+               || !(right || below)
+               || (below
+                   ? (col_with_padding == 0 || !wp->w_p_wrap)
+                   : (n_used < *n_extra)))
+       {
+           if (right && (wrap || room < PROP_TEXT_MIN_CELLS))
+           {
+               // right-align on next line instead of wrapping if possible
+               added = wp->w_width - strsize + room;
+               if (added < 0)
+                   added = 0;
+               else
+                   n_used = *n_extra;
+           }
+           else
+               added = 0;
+       }
 
-    // check highest priority, defined by the type
-    pt1 = text_prop_type_by_id(current_buf, tp1->tp_type);
-    pt2 = text_prop_type_by_id(current_buf, tp2->tp_type);
-    if (pt1 != pt2)
-    {
-       if (pt1 == NULL)
-           return -1;
-       if (pt2 == NULL)
-           return 1;
-       if (pt1->pt_priority != pt2->pt_priority)
-           return pt1->pt_priority > pt2->pt_priority ? 1 : -1;
-    }
+       // With 'nowrap' add one to show the "extends" character if needed (it
+       // doesn't show if the text just fits).
+       if (!wp->w_p_wrap
+               && n_used < *n_extra
+               && wp->w_lcs_chars.ext != NUL
+               && wp->w_p_list)
+           ++n_used;
+
+       // add 1 for NUL, 2 for when '…' is used
+       if (n_attr != NULL)
+           l = alloc(n_used + added + padding + 3);
+       if (n_attr == NULL || l != NULL)
+       {
+           int off = 0;
 
-    // same priority, one that starts first wins
-    if (col1 != col2)
-       return col1 < col2 ? 1 : -1;
+           if (n_attr != NULL)
+           {
+               vim_memset(l, ' ', added);
+               off += added;
+               if (padding > 0)
+               {
+                   vim_memset(l + off, ' ', padding);
+                   off += padding;
+               }
+               vim_strncpy(l + off, *p_extra, n_used);
+               off += n_used;
+           }
+           else
+           {
+               off = added + padding + n_used;
+               cells += added + padding;
+           }
+           if (n_attr != NULL)
+           {
+               if (n_used < *n_extra && wp->w_p_wrap)
+               {
+                   char_u *lp = l + off - 1;
 
-    // for a property with text the id can be used as tie breaker
-    if (tp1->tp_id < 0)
-       return tp1->tp_id > tp2->tp_id ? 1 : -1;
+                   if (has_mbyte)
+                   {
+                       // change last character to '…'
+                       lp -= (*mb_head_off)(l, lp);
+                       STRCPY(lp, "…");
+                       n_used = lp - l + 3 - padding;
+                   }
+                   else
+                       // change last character to '>'
+                       *lp = '>';
+               }
+               *p_extra = l;
+               *n_extra = n_used + added + padding;
+               *n_attr = mb_charlen(*p_extra);
+               *n_attr_skip = added + padding;
+           }
+       }
+    }
 
-    return 0;
+    if (n_attr == NULL)
+       return cells;
+    return (below && col_with_padding > win_col_off(wp) && !wp->w_p_wrap);
 }
 #endif
 
@@ -1219,6 +1268,9 @@ win_line(
 
            // Allocate an array for the indexes.
            text_prop_idxs = ALLOC_MULT(int, text_prop_count);
+           if (text_prop_idxs == NULL)
+               VIM_CLEAR(text_props);
+
            area_highlighting = TRUE;
            extra_check = TRUE;
        }
@@ -1609,8 +1661,9 @@ win_line(
                {
                    int tpi = text_prop_idxs[pi];
 
-                   if (bcol >= text_props[tpi].tp_col - 1
-                                                 + text_props[tpi].tp_len)
+                   if (text_props[tpi].tp_col != MAXCOL
+                           && bcol >= text_props[tpi].tp_col - 1
+                                                     + text_props[tpi].tp_len)
                    {
                        if (pi + 1 < text_props_active)
                            mch_memmove(text_prop_idxs + pi,
@@ -1674,10 +1727,8 @@ win_line(
                    // Sort the properties on priority and/or starting last.
                    // Then combine the attributes, highest priority last.
                    text_prop_follows = FALSE;
-                   current_text_props = text_props;
-                   current_buf = wp->w_buffer;
-                   qsort((void *)text_prop_idxs, (size_t)text_props_active,
-                                              sizeof(int), text_prop_compare);
+                   sort_text_props(wp->w_buffer, text_props,
+                                           text_prop_idxs, text_props_active);
 
                    for (pi = 0; pi < text_props_active; ++pi)
                    {
@@ -1704,23 +1755,28 @@ win_line(
                            && -text_prop_id
                                      <= wp->w_buffer->b_textprop_text.ga_len)
                    {
-                       char_u *p = ((char_u **)wp->w_buffer
+                       textprop_T  *tp = &text_props[used_tpi];
+                       char_u      *p = ((char_u **)wp->w_buffer
                                                   ->b_textprop_text.ga_data)[
                                                           -text_prop_id - 1];
 
                        // reset the ID in the copy to avoid it being used
                        // again
-                       text_props[used_tpi].tp_id = -MAXCOL;
+                       tp->tp_id = -MAXCOL;
 
                        if (p != NULL)
                        {
-                           int     right = (text_props[used_tpi].tp_flags
+                           int     right = (tp->tp_flags
                                                        & TP_FLAG_ALIGN_RIGHT);
-                           int     below = (text_props[used_tpi].tp_flags
+                           int     below = (tp->tp_flags
                                                        & TP_FLAG_ALIGN_BELOW);
-                           int     wrap = (text_props[used_tpi].tp_flags
-                                                       & TP_FLAG_WRAP);
+                           int     wrap = (tp->tp_flags & TP_FLAG_WRAP);
+                           int     padding = tp->tp_col == MAXCOL
+                                                && tp->tp_len > 1
+                                                         ? tp->tp_len - 1 : 0;
 
+                           // Insert virtual text before the current
+                           // character, or add after the end of the line.
                            wlv.p_extra = p;
                            wlv.c_extra = NUL;
                            wlv.c_final = NUL;
@@ -1746,72 +1802,30 @@ win_line(
                            // Keep in sync with where
                            // textprop_size_after_trunc() is called in
                            // win_lbr_chartabsize().
-                           if ((right || below || !wrap) && wp->w_width > 2)
+                           if ((right || below || !wrap || padding > 0)
+                                                           && wp->w_width > 2)
                            {
-                               int     added = wp->w_width - wlv.col;
-                               int     n_used = wlv.n_extra;
-                               char_u  *l;
-                               int     strsize = wrap
-                                         ? vim_strsize(wlv.p_extra)
-                                         : textprop_size_after_trunc(wp,
-                                          below, added, wlv.p_extra, &n_used);
-
-                               if (wrap || right || below
-                                                      || n_used < wlv.n_extra)
+                               char_u  *prev_p_extra = wlv.p_extra;
+                               int     start_line;
+
+                               // Take care of padding, right-align and
+                               // truncation.
+                               // Shared with win_lbr_chartabsize(), must do
+                               // exactly the same.
+                               start_line = text_prop_position(wp, tp,
+                                                   wlv.col,
+                                                   &wlv.n_extra, &wlv.p_extra,
+                                                   &n_attr, &n_attr_skip);
+                               if (wlv.p_extra != prev_p_extra)
                                {
-                                   // Right-align: fill with spaces
-                                   if (right)
-                                       added -= strsize;
-                                   if (added < 0
-                                           || (below
-                                               ? wlv.col == 0 || !wp->w_p_wrap
-                                               : n_used < wlv.n_extra))
-                                       added = 0;
-
-                                   // With 'nowrap' add one to show the
-                                   // "extends" character if needed (it
-                                   // doesn't show it the text just fits).
-                                   if (!wp->w_p_wrap
-                                           && n_used < wlv.n_extra
-                                           && wp->w_lcs_chars.ext != NUL
-                                           && wp->w_p_list)
-                                       ++n_used;
-
-                                   // add 1 for NUL, 2 for when '…' is used
-                                   l = alloc(n_used + added + 3);
-                                   if (l != NULL)
-                                   {
-                                       vim_memset(l, ' ', added);
-                                       vim_strncpy(l + added, wlv.p_extra,
-                                                                      n_used);
-                                       if (n_used < wlv.n_extra
-                                                              && wp->w_p_wrap)
-                                       {
-                                           char_u *lp = l + added + n_used - 1;
-
-                                           if (has_mbyte)
-                                           {
-                                               // change last character to '…'
-                                               lp -= (*mb_head_off)(l, lp);
-                                               STRCPY(lp, "…");
-                                               n_used = lp - l + 3;
-                                           }
-                                           else
-                                               // change last character to '>'
-                                               *lp = '>';
-                                       }
-                                       vim_free(p_extra_free2);
-                                       wlv.p_extra = p_extra_free2 = l;
-                                       wlv.n_extra = n_used + added;
-                                       n_attr_skip = added;
-                                       n_attr = mb_charlen(wlv.p_extra);
-                                   }
+                                   // wlv.p_extra was allocated
+                                   vim_free(p_extra_free2);
+                                   p_extra_free2 = wlv.p_extra;
                                }
 
                                // When 'wrap' is off then for "below" we need
                                // to start a new line explictly.
-                               if (below && wlv.col > win_col_off(wp)
-                                                             && !wp->w_p_wrap)
+                               if (start_line)
                                {
                                    draw_screen_line(wp, &wlv);
 
index 716ec32d8eb791996531057602265844d8eb0ce4..99682e42472f404260bf885f4f8aaaed84308bda 100644 (file)
@@ -1218,6 +1218,8 @@ EXTERN char e_pattern_not_found_str[]
        INIT(= N_("E486: Pattern not found: %s"));
 EXTERN char e_argument_must_be_positive[]
        INIT(= N_("E487: Argument must be positive"));
+EXTERN char e_argument_must_be_positive_str[]
+       INIT(= N_("E487: Argument must be positive: %s"));
 EXTERN char e_trailing_characters[]
        INIT(= N_("E488: Trailing characters"));
 EXTERN char e_trailing_characters_str[]
@@ -3319,4 +3321,6 @@ EXTERN char e_can_only_use_text_align_when_column_is_zero[]
 #ifdef FEAT_PROP_POPUP
 EXTERN char e_cannot_specify_both_type_and_types[]
        INIT(= N_("E1295: Cannot specify both 'type' and 'types'"));
+EXTERN char e_can_only_use_left_padding_when_column_is_zero[]
+       INIT(= N_("E1296: Can only use left padding when column is zero"));
 #endif
index 99163acf8ea2eb10414813217f9d1fe1707b8519..781cdf15d194bc9f458c82efc0c92a7e904bfa65 100644 (file)
@@ -1,3 +1,4 @@
 /* drawline.c */
+int text_prop_position(win_T *wp, textprop_T *tp, int vcol, int *n_extra, char_u **p_extra, int *n_attr, int *n_attr_skip);
 int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int nochange, int number_only);
 /* vim: set ft=c : */
index c72b0ef260611c9f027334ec9e68fa88ac90d0c2..2b239673d08e7eead3d972b6a169bf7a7bfde1c1 100644 (file)
@@ -6,6 +6,7 @@ int prop_add_common(linenr_T start_lnum, colnr_T start_col, dict_T *dict, buf_T
 int get_text_props(buf_T *buf, linenr_T lnum, char_u **props, int will_change);
 int prop_count_below(buf_T *buf, linenr_T lnum);
 int count_props(linenr_T lnum, int only_starting, int last_line);
+void sort_text_props(buf_T *buf, textprop_T *props, int *idxs, int count);
 int find_visible_prop(win_T *wp, int type_id, int id, textprop_T *prop, linenr_T *found_lnum);
 void add_text_props(linenr_T lnum, textprop_T *text_props, int text_prop_count);
 proptype_T *text_prop_type_by_id(buf_T *buf, int id);
index 39c5ef2fc757a85dba92d4e721d4a5794429f26e..5b1a5cc937d8ac2067b48dcbf1eabd6b5945d50f 100644 (file)
@@ -800,7 +800,8 @@ typedef struct memline
 typedef struct textprop_S
 {
     colnr_T    tp_col;         // start column (one based, in bytes)
-    colnr_T    tp_len;         // length in bytes
+    colnr_T    tp_len;         // length in bytes, when tp_id is negative used
+                               // for left padding plus one
     int                tp_id;          // identifier
     int                tp_type;        // property type
     int                tp_flags;       // TP_FLAG_ values
index dd41c86cf5175ea014df85bef205c3c8a474159c..c4cd617f080e3fd33c0c256f5e49d8c97b40bb6c 100644 (file)
@@ -1,8 +1,8 @@
-|s+0&#ffffff0|o|m|e| |m|o|r|e| |t|e|x|t|s|o|m|e| |t|e|x|t| |s|o|m|e| |t|e|x|t| |s|o|m|e| |t|e|x|t| |s|o|m|e| |t|e|x|t| +0&#ffd7ff255|n|o|t|h|i|n|g| |h|e|r|e|S+0#ffffff16#e000002|o|m|e| |e|r@1|o
-|r| +0#0000000#ffffff0@60|A+0#ffffff16#e000002|n|o|t|h|e|r| |e|r@1|o|r
+|s+0&#ffffff0|o|m|e| |m|o|r|e| |t|e|x|t|s|o|m|e| |t|e|x|t| |s|o|m|e| |t|e|x|t| |s|o|m|e| |t|e|x|t| |s|o|m|e| |t|e|x|t| +0&#ffd7ff255|n|o|t|h|i|n|g| |h|e|r|e| +0&#ffffff0@8
+@65|S+0#ffffff16#e000002|o|m|e| |e|r@1|o|r
+| +0#0000000#ffffff0@61|A+0#ffffff16#e000002|n|o|t|h|e|r| |e|r@1|o|r
 |l+0#0000000#ffffff0|i|n|e| |t|w>o| @66
 |~+0#4040ff13&| @73
 |~| @73
 |~| @73
-|~| @73
 | +0#0000000&@56|2|,|8| @10|A|l@1| 
diff --git a/src/testdir/dumps/Test_prop_text_with_padding_1.dump b/src/testdir/dumps/Test_prop_text_with_padding_1.dump
new file mode 100644 (file)
index 0000000..1fde5e9
--- /dev/null
@@ -0,0 +1,8 @@
+>S+0&#ffffff0|o|m|e| |t|e|x|t| |t|o| |a|d@1| |v|i|r|t|u|a|l| |t|e|x|t| |t|o|.| @2|a+0&#ffd7ff255|f|t|e|r| +0&#ffffff0@5|r+0&#ffd7ff255|i|g|h|t| |a|l|i|g|n|e|d
+| +0&#ffffff0@3|b+0&#ffd7ff255|e|l|o|w| |t|h|e| |l|i|n|e| +0&#ffffff0@41
+|s|e|c|o|n|d| |l|i|n|e| @48
+|A|n|o|t|h|e|r| |l|i|n|e| |w|i|t|h| |s|o|m|e| |t|e|x|t| |t|o| |m|a|k|e| |t|h|e| |w|r|a|p|.| @5|r+0&#ffd7ff255|i|g|h|t|m|o|s|t
+|~+0#4040ff13#ffffff0| @58
+|~| @58
+|~| @58
+| +0#0000000&@41|1|,|1| @10|A|l@1| 
diff --git a/src/testdir/dumps/Test_prop_text_with_padding_2.dump b/src/testdir/dumps/Test_prop_text_with_padding_2.dump
new file mode 100644 (file)
index 0000000..abedaae
--- /dev/null
@@ -0,0 +1,8 @@
+|x+0&#ffffff0@9|S|o|m|e| |t|e|x|t| |t|o| |a|d@1| |v|i|r|t|u|a|l| |t|e|x|t| |t|o|.| @2|a+0&#ffd7ff255|f|t|e|r| +0&#ffffff0@4|r+0&#ffd7ff255|i|g|…
+| +0&#ffffff0@3|b+0&#ffd7ff255|e|l|o|w| |t|h|e| |l|i|n|e| +0&#ffffff0@41
+|s|e|c|o|n|d| |l|i|n|e| @48
+>x|A|n|o|t|h|e|r| |l|i|n|e| |w|i|t|h| |s|o|m|e| |t|e|x|t| |t|o| |m|a|k|e| |t|h|e| |w|r|a|p|.| @13
+@51|r+0&#ffd7ff255|i|g|h|t|m|o|s|t
+|~+0#4040ff13#ffffff0| @58
+|~| @58
+| +0#0000000&@41|3|,|1| @10|A|l@1| 
diff --git a/src/testdir/dumps/Test_prop_text_with_padding_3.dump b/src/testdir/dumps/Test_prop_text_with_padding_3.dump
new file mode 100644 (file)
index 0000000..2911162
--- /dev/null
@@ -0,0 +1,8 @@
+>x+0&#ffffff0@10|S|o|m|e| |t|e|x|t| |t|o| |a|d@1| |v|i|r|t|u|a|l| |t|e|x|t| |t|o|.| @2|a+0&#ffd7ff255|f|t|e|r| +0&#ffffff0@7
+@47|r+0&#ffd7ff255|i|g|h|t| |a|l|i|g|n|e|d
+| +0&#ffffff0@3|b+0&#ffd7ff255|e|l|o|w| |t|h|e| |l|i|n|e| +0&#ffffff0@41
+|s|e|c|o|n|d| |l|i|n|e| @48
+|x|A|n|o|t|h|e|r| |l|i|n|e| |w|i|t|h| |s|o|m|e| |t|e|x|t| |t|o| |m|a|k|e| |t|h|e| |w|r|a|p|.| @13
+@51|r+0&#ffd7ff255|i|g|h|t|m|o|s|t
+|~+0#4040ff13#ffffff0| @58
+| +0#0000000&@41|1|,|1| @10|A|l@1| 
index 674ea84c50b5c22cbd8736f3376d558d880a3911..7642f8fb57dc317a16d8934335ae9dc790a109e9 100644 (file)
@@ -1,8 +1,8 @@
 |o+0&#ffffff0|n|e| |t|w|o| |t|h|r|e@1| |f|o|u|r| |f|i|v|e| |s|i|x| |s|e|v|e|n| +0&#ffff4012|O|N|E| |a|n|d| |T|W|O| |a|n|d| |T|H|R|E@1| |a|n|d| 
 |F|O|U|R| |a|n|d| |F|I|V|E| +0&#ffffff0@46
-|o|n|e| |t|w|o| |t|h|r|e@1| |f|o|u|r| |f|i|v|e| |s|i|x| |s|e|v|e|n| +0&#ffff4012|o|n|e| |A|N|D| |t|w|o| |A|N|D| |t|h|r|e@1| |A|N|D| 
-|f|o|u|r| |A|N|D| |f|i|v|e| +0&#ffffff0@46
 |o|n|e| |t|w|o| |t|h|r|e@1| |f|o|u|r| |f|i|v|e| |s|i|x| |s|e|v|e|n| @26
+@20| +0&#ffff4012|o|n|e| |A|N|D| |t|w|o| |A|N|D| |t|h|r|e@1| |A|N|D| |f|o|u|r| |A|N|D| |f|i|v|e
+|o+0&#ffffff0|n|e| |t|w|o| |t|h|r|e@1| |f|o|u|r| |f|i|v|e| |s|i|x| |s|e|v|e|n| @26
 | +0&#ffff4012|o|n|e| |A|N|D| |t|w|o| |A|N|D| |t|h|r|e@1| |A|N|D| |f|o|u|r| |A|N|D| |f|i|v|e| |l|e|t|s| |w|r|a|p| |a|f|t|e|r| |s|o|m
 |e| |m|o|r|e| |t|e|x|t| +0&#ffffff0@48
 |c|u|r|s|o|r| >h|e|r|e| @48
index a7646209e8e7736187a273f84996d1403a84b6b9..0c7e91d0958b1681bbcea2d9baa509346a9e4992 100644 (file)
@@ -3041,4 +3041,54 @@ func Test_insert_text_list_mode()
   call delete('XscriptPropsListMode')
 endfunc
 
+func Test_insert_text_with_padding()
+  CheckRunVimInTerminal
+
+  let lines =<< trim END
+      vim9script
+      setline(1, ['Some text to add virtual text to.',
+                  'second line',
+                  'Another line with some text to make the wrap.'])
+      prop_type_add('theprop', {highlight: 'DiffChange'})
+      prop_add(1, 0, {
+          type: 'theprop',
+          text: 'after',
+          text_align: 'after',
+          text_padding_left: 3,
+      })
+      prop_add(1, 0, {
+          type: 'theprop',
+          text: 'right aligned',
+          text_align: 'right',
+          text_padding_left: 5,
+      })
+      prop_add(1, 0, {
+          type: 'theprop',
+          text: 'below the line',
+          text_align: 'below',
+          text_padding_left: 4,
+      })
+      prop_add(3, 0, {
+          type: 'theprop',
+          text: 'rightmost',
+          text_align: 'right',
+          text_padding_left: 6,
+          text_wrap: 'wrap',
+      })
+  END
+  call writefile(lines, 'XscriptPropsPadded')
+  let buf = RunVimInTerminal('-S XscriptPropsPadded', #{rows: 8, cols: 60})
+  call VerifyScreenDump(buf, 'Test_prop_text_with_padding_1', {})
+
+  call term_sendkeys(buf, "ggixxxxxxxxxx\<Esc>")
+  call term_sendkeys(buf, "3Gix\<Esc>")
+  call VerifyScreenDump(buf, 'Test_prop_text_with_padding_2', {})
+
+  call term_sendkeys(buf, "ggix\<Esc>")
+  call VerifyScreenDump(buf, 'Test_prop_text_with_padding_3', {})
+
+  call StopVimInTerminal(buf)
+  call delete('XscriptPropsPadded')
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
index 2fb8a531223776a1dbdfd65cd2088501f01224e8..31e1be3bbf03eebec4fbda3e6f474d804499b5c5 100644 (file)
@@ -171,6 +171,7 @@ prop_add_one(
        char_u          *type_name,
        int             id,
        char_u          *text_arg,
+       int             text_padding_left,
        int             text_flags,
        linenr_T        start_lnum,
        linenr_T        end_lnum,
@@ -264,7 +265,10 @@ prop_add_one(
        {
            length = 1;         // text is placed on one character
            if (col == 0)
+           {
                col = MAXCOL;   // after the line
+               length += text_padding_left;
+           }
        }
 
        // Allocate the new line with space for the new property.
@@ -390,7 +394,7 @@ f_prop_add_list(typval_T *argvars, typval_T *rettv UNUSED)
            emsg(_(e_invalid_argument));
            return;
        }
-       if (prop_add_one(buf, type_name, id, NULL, 0, start_lnum, end_lnum,
+       if (prop_add_one(buf, type_name, id, NULL, 0, 0, start_lnum, end_lnum,
                                                start_col, end_col) == FAIL)
            return;
     }
@@ -428,6 +432,7 @@ prop_add_common(
     buf_T      *buf = default_buf;
     int                id = 0;
     char_u     *text = NULL;
+    int                text_padding_left = 0;
     int                flags = 0;
 
     if (dict == NULL || !dict_has_key(dict, "type"))
@@ -507,9 +512,20 @@ prop_add_common(
            }
        }
 
+       if (dict_has_key(dict, "text_padding_left"))
+       {
+           text_padding_left = dict_get_number(dict, "text_padding_left");
+           if (text_padding_left < 0)
+           {
+               semsg(_(e_argument_must_be_positive_str), "text_padding_left");
+               goto theend;
+           }
+       }
+
        if (dict_has_key(dict, "text_wrap"))
        {
            char_u *p = dict_get_string(dict, "text_wrap", FALSE);
+
            if (p == NULL)
                goto theend;
            if (STRCMP(p, "wrap") == 0)
@@ -529,6 +545,11 @@ prop_add_common(
        semsg(_(e_invalid_column_number_nr), (long)start_col);
        goto theend;
     }
+    if (start_col > 0 && text_padding_left > 0)
+    {
+       emsg(_(e_can_only_use_left_padding_when_column_is_zero));
+       goto theend;
+    }
 
     if (dict_arg != NULL && get_bufnr_from_arg(dict_arg, &buf) == FAIL)
        goto theend;
@@ -546,7 +567,7 @@ prop_add_common(
     // correctly set.
     buf->b_has_textprop = TRUE;  // this is never reset
 
-    prop_add_one(buf, type_name, id, text, flags,
+    prop_add_one(buf, type_name, id, text, text_padding_left, flags,
                                    start_lnum, end_lnum, start_col, end_col);
     text = NULL;
 
@@ -655,6 +676,91 @@ count_props(linenr_T lnum, int only_starting, int last_line)
     return result;
 }
 
+static textprop_T      *text_prop_compare_props;
+static buf_T           *text_prop_compare_buf;
+
+/*
+ * Function passed to qsort() to sort text properties.
+ * Return 1 if "s1" has priority over "s2", -1 if the other way around, zero if
+ * both have the same priority.
+ */
+    static int
+text_prop_compare(const void *s1, const void *s2)
+{
+    int  idx1, idx2;
+    textprop_T *tp1, *tp2;
+    proptype_T  *pt1, *pt2;
+    colnr_T col1, col2;
+
+    idx1 = *(int *)s1;
+    idx2 = *(int *)s2;
+    tp1 = &text_prop_compare_props[idx1];
+    tp2 = &text_prop_compare_props[idx2];
+    col1 = tp1->tp_col;
+    col2 = tp2->tp_col;
+    if (col1 == MAXCOL && col2 == MAXCOL)
+    {
+       int flags1 = 0;
+       int flags2 = 0;
+
+       // both props add text are after the line, order on 0: after (default),
+       // 1: right, 2: below (comes last)
+       if (tp1->tp_flags & TP_FLAG_ALIGN_RIGHT)
+           flags1 = 1;
+       if (tp1->tp_flags & TP_FLAG_ALIGN_BELOW)
+           flags1 = 2;
+       if (tp2->tp_flags & TP_FLAG_ALIGN_RIGHT)
+           flags2 = 1;
+       if (tp2->tp_flags & TP_FLAG_ALIGN_BELOW)
+           flags2 = 2;
+       if (flags1 != flags2)
+           return flags1 < flags2 ? 1 : -1;
+    }
+
+    // property that inserts text has priority over one that doesn't
+    if ((tp1->tp_id < 0) != (tp2->tp_id < 0))
+       return tp1->tp_id < 0 ? 1 : -1;
+
+    // check highest priority, defined by the type
+    pt1 = text_prop_type_by_id(text_prop_compare_buf, tp1->tp_type);
+    pt2 = text_prop_type_by_id(text_prop_compare_buf, tp2->tp_type);
+    if (pt1 != pt2)
+    {
+       if (pt1 == NULL)
+           return -1;
+       if (pt2 == NULL)
+           return 1;
+       if (pt1->pt_priority != pt2->pt_priority)
+           return pt1->pt_priority > pt2->pt_priority ? 1 : -1;
+    }
+
+    // same priority, one that starts first wins
+    if (col1 != col2)
+       return col1 < col2 ? 1 : -1;
+
+    // for a property with text the id can be used as tie breaker
+    if (tp1->tp_id < 0)
+       return tp1->tp_id > tp2->tp_id ? 1 : -1;
+
+    return 0;
+}
+
+/*
+ * Sort "count" text properties using an array if indexes "idxs" into the list
+ * of text props "props" for buffer "buf".
+ */
+    void
+sort_text_props(
+       buf_T       *buf,
+       textprop_T  *props,
+       int         *idxs,
+       int         count)
+{
+    text_prop_compare_buf = buf;
+    text_prop_compare_props = props;
+    qsort((void *)idxs, (size_t)count, sizeof(int), text_prop_compare);
+}
+
 /*
  * Find text property "type_id" in the visible lines of window "wp".
  * Match "id" when it is > 0.
index ec1302df04549c7c8c9f9c2ca226ef493d52c79a..66ef53ca9f71e4b3ea14210f995634ca5ae930aa 100644 (file)
@@ -731,6 +731,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    247,
 /**/
     246,
 /**/