Patch 9.0.0121
Problem:    Cannot put virtual text after or below a line.
Solution:   Add "text_align" and "text_wrap" arguments.
Files:      runtime/doc textprop.txt, src/textprop.c, src/structs.h,
            src/drawline.c, src/charset.c, src/testdir/test_textprop.vim,
            src/testdir/dumps/Test_prop_with_text_after_1.dump


*** ../vim-9.0.0120/runtime/doc/textprop.txt    2022-07-25 18:13:33.046580756 
+0100
--- runtime/doc/textprop.txt    2022-07-31 15:03:34.516688562 +0100
***************
*** 141,147 ****
                                then "id" must not be present and will be set
                                automatically to a negative number; otherwise
                                zero is used
!                  text         text to be displayed at {col}
                   type         name of the text property type
                All fields except "type" are optional.
  
--- 141,160 ----
                                then "id" must not be present and will be set
                                automatically to a negative number; otherwise
                                zero is used
!                  text         text to be displayed before {col}, or after the
!                               line if {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
!                                  below   in the next screen line
!                               When omitted "after" is used.
!                  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.
  
***************
*** 162,177 ****
                added to. When not found, the global property types are used.
                If not found an error is given.
                                                        *virtual-text*
!               When "text" is used this text will be displayed at the start
!               location of the text property.  The text of the buffer line
!               will be shifted to make room.  This is called "virtual text".
                The text will be displayed but it is not part of the actual
                buffer line, the cursor cannot be placed on it.  A mouse click
                in the text will move the cursor to the first character after
!               the text.
                A negative "id" will be chosen and is returned.  Once a
                property with "text" has been added for a buffer then using a
!               negative "id" for any other property will give an error.
  
                Can also be used as a |method|: >
                        GetLnum()->prop_add(col, props)
--- 175,200 ----
                added to. When not found, the global property types are used.
                If not found an error is given.
                                                        *virtual-text*
!               When "text" is used and the column is non-zero then this text
!               will be displayed at the start location of the text property
!               after the text.  The text of the buffer line will be shifted
!               to make room.  This is called "virtual text".
!               When the column is zero the virtual text will appear after the
!               buffer text.  The "text_align" and "text_wrap" arguments
!               determine how it is displayed.
                The text will be displayed but it is not part of the actual
                buffer line, the cursor cannot be placed on it.  A mouse click
                in the text will move the cursor to the first character after
!               the text, or the last character of the line.
                A negative "id" will be chosen and is returned.  Once a
                property with "text" has been added for a buffer then using a
!               negative "id" for any other property will give an error:
!               *E1293*
!               Make sure to use a highlight that makes clear to the user that
!               this is virtual text, otherwise it will be very confusing that
!               the text cannot be edited.
!               To separate the virtual text from the buffer text prepend
!               and/or append spaces to the "text" field.
  
                Can also be used as a |method|: >
                        GetLnum()->prop_add(col, props)
*** ../vim-9.0.0120/src/textprop.c      2022-07-30 22:17:14.812697205 +0100
--- src/textprop.c      2022-07-31 16:53:29.130942164 +0100
***************
*** 163,173 ****
  
      start_lnum = tv_get_number(&argvars[0]);
      start_col = tv_get_number(&argvars[1]);
-     if (start_col < 1)
-     {
-       semsg(_(e_invalid_column_number_nr), (long)start_col);
-       return;
-     }
      if (argvars[2].v_type != VAR_DICT)
      {
        emsg(_(e_dictionary_required));
--- 163,168 ----
***************
*** 190,195 ****
--- 185,191 ----
        char_u          *type_name,
        int             id,
        char_u          *text_arg,
+       int             text_flags,
        linenr_T        start_lnum,
        linenr_T        end_lnum,
        colnr_T         start_col,
***************
*** 256,262 ****
            col = start_col;
        else
            col = 1;
!       if (col - 1 > (colnr_T)textlen)
        {
            semsg(_(e_invalid_column_number_nr), (long)start_col);
            goto theend;
--- 252,258 ----
            col = start_col;
        else
            col = 1;
!       if (col - 1 > (colnr_T)textlen && !(col == 0 && text_arg != NULL))
        {
            semsg(_(e_invalid_column_number_nr), (long)start_col);
            goto theend;
***************
*** 271,276 ****
--- 267,279 ----
        if (length < 0)
            length = 0;         // zero-width property
  
+       if (text_arg != NULL)
+       {
+           length = 1;         // text is placed on one character
+           if (col == 0)
+               col = MAXCOL;   // after the line
+       }
+ 
        // Allocate the new line with space for the new property.
        newtext = alloc(buf->b_ml.ml_line_len + sizeof(textprop_T));
        if (newtext == NULL)
***************
*** 296,303 ****
        tmp_prop.tp_len = length;
        tmp_prop.tp_id = id;
        tmp_prop.tp_type = type->pt_id;
!       tmp_prop.tp_flags = (lnum > start_lnum ? TP_FLAG_CONT_PREV : 0)
!                         | (lnum < end_lnum ? TP_FLAG_CONT_NEXT : 0);
        mch_memmove(newprops + i * sizeof(textprop_T), &tmp_prop,
                                                           sizeof(textprop_T));
  
--- 299,307 ----
        tmp_prop.tp_len = length;
        tmp_prop.tp_id = id;
        tmp_prop.tp_type = type->pt_id;
!       tmp_prop.tp_flags = text_flags
!                           | (lnum > start_lnum ? TP_FLAG_CONT_PREV : 0)
!                           | (lnum < end_lnum ? TP_FLAG_CONT_NEXT : 0);
        mch_memmove(newprops + i * sizeof(textprop_T), &tmp_prop,
                                                           sizeof(textprop_T));
  
***************
*** 390,396 ****
            emsg(_(e_invalid_argument));
            return;
        }
!       if (prop_add_one(buf, type_name, id, NULL, start_lnum, end_lnum,
                                                start_col, end_col) == FAIL)
            return;
      }
--- 394,400 ----
            emsg(_(e_invalid_argument));
            return;
        }
!       if (prop_add_one(buf, type_name, id, NULL, 0, start_lnum, end_lnum,
                                                start_col, end_col) == FAIL)
            return;
      }
***************
*** 428,433 ****
--- 432,438 ----
      buf_T     *buf = default_buf;
      int               id = 0;
      char_u    *text = NULL;
+     int               flags = 0;
  
      if (dict == NULL || !dict_has_key(dict, "type"))
      {
***************
*** 483,488 ****
--- 488,532 ----
            goto theend;
        // use a default length of 1 to make multiple props show up
        end_col = start_col + 1;
+ 
+       if (dict_has_key(dict, "text_align"))
+       {
+           char_u *p = dict_get_string(dict, "text_align", FALSE);
+ 
+           if (p == NULL)
+               goto theend;
+           if (STRCMP(p, "right") == 0)
+               flags |= TP_FLAG_ALIGN_RIGHT;
+           else if (STRCMP(p, "below") == 0)
+               flags |= TP_FLAG_ALIGN_BELOW;
+           else if (STRCMP(p, "after") != 0)
+           {
+               semsg(_(e_invalid_value_for_argument_str_str), "text_align", p);
+               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)
+               flags |= TP_FLAG_WRAP;
+           else if (STRCMP(p, "truncate") != 0)
+           {
+               semsg(_(e_invalid_value_for_argument_str_str), "text_wrap", p);
+               goto theend;
+           }
+       }
+     }
+ 
+     // Column must be 1 or more for a normal text property; when "text" is
+     // present zero means it goes after the line.
+     if (start_col < (text == NULL ? 1 : 0))
+     {
+       semsg(_(e_invalid_column_number_nr), (long)start_col);
+       goto theend;
      }
  
      if (dict_arg != NULL && get_bufnr_from_arg(dict_arg, &buf) == FAIL)
***************
*** 501,507 ****
      // correctly set.
      buf->b_has_textprop = TRUE;  // this is never reset
  
!     prop_add_one(buf, type_name, id, text,
                                    start_lnum, end_lnum, start_col, end_col);
      text = NULL;
  
--- 545,551 ----
      // correctly set.
      buf->b_has_textprop = TRUE;  // this is never reset
  
!     prop_add_one(buf, type_name, id, text, flags,
                                    start_lnum, end_lnum, start_col, end_col);
      text = NULL;
  
***************
*** 1738,1759 ****
   */
      static adjustres_T
  adjust_prop(
!       textprop_T *prop,
!       colnr_T col,
!       int added,
!       int flags)
! {
!     proptype_T        *pt = text_prop_type_by_id(curbuf, prop->tp_type);
!     int               start_incl = (pt != NULL
!                                   && (pt->pt_flags & PT_FLAG_INS_START_INCL))
                                || (flags & APC_SUBSTITUTE)
                                || (prop->tp_flags & TP_FLAG_CONT_PREV);
!     int               end_incl = (pt != NULL
!                                     && (pt->pt_flags & PT_FLAG_INS_END_INCL))
                                || (prop->tp_flags & TP_FLAG_CONT_NEXT);
!     // Do not drop zero-width props if they later can increase in size.
!     int               droppable = !(start_incl || end_incl);
!     adjustres_T res = {TRUE, FALSE};
  
      if (added > 0)
      {
--- 1782,1813 ----
   */
      static adjustres_T
  adjust_prop(
!       textprop_T  *prop,
!       colnr_T     col,
!       int         added,
!       int         flags)
! {
!     proptype_T        *pt;
!     int               start_incl;
!     int               end_incl;
!     int               droppable;
!     adjustres_T res = {TRUE, FALSE};
! 
!     // prop after end of the line doesn't move
!     if (prop->tp_col == MAXCOL)
!     {
!       res.dirty = FALSE;
!       return res;
!     }
! 
!     pt = text_prop_type_by_id(curbuf, prop->tp_type);
!     start_incl = (pt != NULL && (pt->pt_flags & PT_FLAG_INS_START_INCL))
                                || (flags & APC_SUBSTITUTE)
                                || (prop->tp_flags & TP_FLAG_CONT_PREV);
!     end_incl = (pt != NULL && (pt->pt_flags & PT_FLAG_INS_END_INCL))
                                || (prop->tp_flags & TP_FLAG_CONT_NEXT);
!     // do not drop zero-width props if they later can increase in size
!     droppable = !(start_incl || end_incl);
  
      if (added > 0)
      {
*** ../vim-9.0.0120/src/structs.h       2022-07-26 11:20:45.116283126 +0100
--- src/structs.h       2022-07-31 15:09:39.916075004 +0100
***************
*** 808,814 ****
  
  #define TP_FLAG_CONT_NEXT     0x1     // property continues in next line
  #define TP_FLAG_CONT_PREV     0x2     // property was continued from prev line
! #define TP_VIRTUAL            0x4     // virtual text, uses tp_id
  
  /*
   * Structure defining a property type.
--- 808,820 ----
  
  #define TP_FLAG_CONT_NEXT     0x1     // property continues in next line
  #define TP_FLAG_CONT_PREV     0x2     // property was continued from prev line
! 
! // without these text is placed after the end of the line
! #define TP_FLAG_ALIGN_RIGHT   0x10    // virtual text is right-aligned
! #define TP_FLAG_ALIGN_BELOW   0x20    // virtual text on next screen line
! 
! #define TP_FLAG_WRAP          0x40    // virtual text wraps - when missing
!                                       // text is truncated
  
  /*
   * Structure defining a property type.
***************
*** 4575,4580 ****
--- 4581,4588 ----
      textprop_T        *cts_text_props;        // text props (allocated)
      char      cts_has_prop_with_text; // TRUE if if a property inserts text
      int         cts_cur_text_width;     // width of current inserted text
+     int               cts_with_trailing;      // include size of trailing 
props with
+                                       // last character
  #endif
      int               cts_vcol;           // virtual column at current 
position
  } chartabsize_T;
*** ../vim-9.0.0120/src/drawline.c      2022-07-30 21:33:18.196195445 +0100
--- src/drawline.c      2022-07-31 17:04:48.962791895 +0100
***************
*** 208,220 ****
  text_prop_compare(const void *s1, const void *s2)
  {
      int  idx1, idx2;
      proptype_T  *pt1, *pt2;
      colnr_T col1, col2;
  
      idx1 = *(int *)s1;
      idx2 = *(int *)s2;
!     pt1 = text_prop_type_by_id(current_buf, current_text_props[idx1].tp_type);
!     pt2 = text_prop_type_by_id(current_buf, current_text_props[idx2].tp_type);
      if (pt1 == pt2)
        return 0;
      if (pt1 == NULL)
--- 208,223 ----
  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 = &current_text_props[idx1];
!     tp2 = &current_text_props[idx2];
!     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)
        return 0;
      if (pt1 == NULL)
***************
*** 223,230 ****
        return 1;
      if (pt1->pt_priority != pt2->pt_priority)
        return pt1->pt_priority > pt2->pt_priority ? 1 : -1;
!     col1 = current_text_props[idx1].tp_col;
!     col2 = current_text_props[idx2].tp_col;
      return col1 == col2 ? 0 : col1 > col2 ? 1 : -1;
  }
  #endif
--- 226,250 ----
        return 1;
      if (pt1->pt_priority != pt2->pt_priority)
        return pt1->pt_priority > pt2->pt_priority ? 1 : -1;
!     col1 = tp1->tp_col;
!     col2 = tp2->tp_col;
!     if (col1 == MAXCOL && col2 == MAXCOL)
!     {
!       int flags1 = 0;
!       int flags2 = 0;
! 
!       // order on 0: after, 1: right, 2: below
!       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;
!     }
      return col1 == col2 ? 0 : col1 > col2 ? 1 : -1;
  }
  #endif
***************
*** 281,290 ****
      int               saved_c_final = 0;
      int               saved_char_attr = 0;
  
!     int               n_attr = 0;             // chars with special attr
!     int               saved_attr2 = 0;        // char_attr saved for n_attr
!     int               n_attr3 = 0;            // chars with overruling 
special attr
!     int               saved_attr3 = 0;        // char_attr saved for n_attr3
  
      int               n_skip = 0;             // nr of chars to skip for 
'nowrap'
  
--- 301,311 ----
      int               saved_c_final = 0;
      int               saved_char_attr = 0;
  
!     int               n_attr = 0;         // chars with special attr
!     int               n_attr_skip = 0;    // chars to skip before using 
extra_attr
!     int               saved_attr2 = 0;    // char_attr saved for n_attr
!     int               n_attr3 = 0;        // chars with overruling special 
attr
!     int               saved_attr3 = 0;    // char_attr saved for n_attr3
  
      int               n_skip = 0;             // nr of chars to skip for 
'nowrap'
  
***************
*** 328,333 ****
--- 349,355 ----
      int               text_prop_attr = 0;
      int               text_prop_id = 0;       // active property ID
      int               text_prop_combine = FALSE;
+     int               text_prop_follows = FALSE;  // another text prop to 
display
  #endif
  #ifdef FEAT_SPELL
      int               has_spell = FALSE;      // this buffer has spell 
checking
***************
*** 1472,1478 ****
  # endif
                // Add any text property that starts in this column.
                while (text_prop_next < text_prop_count
!                          && bcol >= text_props[text_prop_next].tp_col - 1)
                {
                    if (bcol <= text_props[text_prop_next].tp_col - 1
                                           + text_props[text_prop_next].tp_len)
--- 1494,1502 ----
  # endif
                // Add any text property that starts in this column.
                while (text_prop_next < text_prop_count
!                          && (text_props[text_prop_next].tp_col == MAXCOL
!                             ? *ptr == NUL
!                             : bcol >= text_props[text_prop_next].tp_col - 1))
                {
                    if (bcol <= text_props[text_prop_next].tp_col - 1
                                           + text_props[text_prop_next].tp_len)
***************
*** 1484,1496 ****
                text_prop_combine = FALSE;
                text_prop_type = NULL;
                text_prop_id = 0;
!               if (text_props_active > 0)
                {
                    int used_tpi = -1;
                    int used_attr = 0;
  
                    // Sort the properties on priority and/or starting last.
                    // Then combine the attributes, highest priority last.
                    current_text_props = text_props;
                    current_buf = wp->w_buffer;
                    qsort((void *)text_prop_idxs, (size_t)text_props_active,
--- 1508,1522 ----
                text_prop_combine = FALSE;
                text_prop_type = NULL;
                text_prop_id = 0;
!               if (text_props_active > 0 && n_extra == 0)
                {
                    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_follows = FALSE;
                    current_text_props = text_props;
                    current_buf = wp->w_buffer;
                    qsort((void *)text_prop_idxs, (size_t)text_props_active,
***************
*** 1511,1520 ****
                                   hl_combine_attr(text_prop_attr, used_attr);
                            text_prop_combine = pt->pt_flags & PT_FLAG_COMBINE;
                            text_prop_id = text_props[tpi].tp_id;
                            used_tpi = tpi;
                        }
                    }
!                   if (n_extra == 0 && text_prop_id < 0 && used_tpi >= 0
                            && -text_prop_id
                                      <= wp->w_buffer->b_textprop_text.ga_len)
                    {
--- 1537,1547 ----
                                   hl_combine_attr(text_prop_attr, used_attr);
                            text_prop_combine = pt->pt_flags & PT_FLAG_COMBINE;
                            text_prop_id = text_props[tpi].tp_id;
+                           other_tpi = used_tpi;
                            used_tpi = tpi;
                        }
                    }
!                   if (text_prop_id < 0 && used_tpi >= 0
                            && -text_prop_id
                                      <= wp->w_buffer->b_textprop_text.ga_len)
                    {
***************
*** 1523,1528 ****
--- 1550,1560 ----
                                                           -text_prop_id - 1];
                        if (p != NULL)
                        {
+                           int     right = (text_props[used_tpi].tp_flags
+                                                       & TP_FLAG_ALIGN_RIGHT);
+                           int     below = (text_props[used_tpi].tp_flags
+                                                       & TP_FLAG_ALIGN_BELOW);
+ 
                            p_extra = p;
                            c_extra = NUL;
                            c_final = NUL;
***************
*** 1530,1535 ****
--- 1562,1594 ----
                            extra_attr = used_attr;
                            n_attr = n_extra;
                            text_prop_attr = 0;
+                           if (*ptr == NUL)
+                               // don't combine char attr after EOL
+                               text_prop_combine = FALSE;
+ 
+                           // TODO: truncation if it doesn't fit
+                           if (right || below)
+                           {
+                               int     added = wp->w_width - col;
+                               char_u  *l;
+ 
+                               // Right-align: fill with spaces
+                               // TODO: count screen columns
+                               if (right)
+                                   added -= n_extra;
+                               if (added < 0 || (below && col == 0))
+                                   added = 0;
+                               l = alloc(n_extra + added + 1);
+                               if (l != NULL)
+                               {
+                                   vim_memset(l, ' ', added);
+                                   STRCPY(l + added, p);
+                                   vim_free(p_extra_free);
+                                   p_extra = p_extra_free = l;
+                                   n_extra += added;
+                                   n_attr_skip = added;
+                               }
+                           }
  
                            // If the cursor is on or after this position,
                            // move it forward.
***************
*** 1541,1546 ****
--- 1600,1609 ----
                        // reset the ID in the copy to avoid it being used
                        // again
                        text_props[used_tpi].tp_id = -MAXCOL;
+ 
+                       // If another text prop follows the condition below at
+                       // the last window column must know.
+                       text_prop_follows = other_tpi != -1;
                    }
                }
            }
***************
*** 2641,2648 ****
        }
  #endif
  
!       // Don't override visual selection highlighting.
!       if (n_attr > 0
                && draw_state == WL_LINE
                && !attr_pri)
        {
--- 2704,2712 ----
        }
  #endif
  
!       // Use "extra_attr", but don't override visual selection highlighting.
!       // Don't use "extra_attr" until n_attr_skip is zero.
!       if (n_attr_skip == 0 && n_attr > 0
                && draw_state == WL_LINE
                && !attr_pri)
        {
***************
*** 3188,3195 ****
            char_attr = saved_attr3;
  
        // restore attributes after last 'listchars' or 'number' char
!       if (n_attr > 0 && draw_state == WL_LINE && --n_attr == 0)
            char_attr = saved_attr2;
  
        // At end of screen line and there is more to come: Display the line
        // so far.  If there is no more to display it is caught above.
--- 3252,3262 ----
            char_attr = saved_attr3;
  
        // restore attributes after last 'listchars' or 'number' char
!       if (n_attr > 0 && draw_state == WL_LINE
!                                         && n_attr_skip == 0 && --n_attr == 0)
            char_attr = saved_attr2;
+       if (n_attr_skip > 0)
+           --n_attr_skip;
  
        // At end of screen line and there is more to come: Display the line
        // so far.  If there is no more to display it is caught above.
***************
*** 3203,3208 ****
--- 3270,3278 ----
  #ifdef FEAT_DIFF
                    || filler_todo > 0
  #endif
+ #ifdef FEAT_PROP_POPUP
+                   || text_prop_follows
+ #endif
                    || (wp->w_p_list && wp->w_lcs_chars.eol != NUL
                                                && p_extra != at_end_str)
                    || (n_extra != 0 && (c_extra != NUL || *p_extra != NUL)))
***************
*** 3223,3229 ****
            // '$' and highlighting until last column, break here.
            if ((!wp->w_p_wrap
  #ifdef FEAT_DIFF
!                   && filler_todo <= 0
  #endif
                    ) || lcs_eol_one == -1)
                break;
--- 3293,3302 ----
            // '$' and highlighting until last column, break here.
            if ((!wp->w_p_wrap
  #ifdef FEAT_DIFF
!                       && filler_todo <= 0
! #endif
! #ifdef FEAT_PROP_POPUP
!                       && !text_prop_follows
  #endif
                    ) || lcs_eol_one == -1)
                break;
***************
*** 3251,3256 ****
--- 3324,3332 ----
  #ifdef FEAT_DIFF
                     && filler_todo <= 0
  #endif
+ #ifdef FEAT_PROP_POPUP
+                    && !text_prop_follows
+ #endif
                     && wp->w_width == Columns)
            {
                // Remember that the line wraps, used for modeless copy.
*** ../vim-9.0.0120/src/charset.c       2022-07-26 16:03:35.182058189 +0100
--- src/charset.c       2022-07-31 15:20:27.563111344 +0100
***************
*** 771,776 ****
--- 771,777 ----
      chartabsize_T cts;
  
      init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
+     cts.cts_with_trailing = len = MAXCOL;
      for ( ; *cts.cts_ptr != NUL && (len == MAXCOL || cts.cts_ptr < line + 
len);
                                                      MB_PTR_ADV(cts.cts_ptr))
        cts.cts_vcol += win_lbr_chartabsize(&cts, NULL);
***************
*** 1089,1103 ****
            textprop_T *tp = cts->cts_text_props + i;
  
            if (tp->tp_id < 0
!                    && tp->tp_col - 1 >= col && tp->tp_col - 1 < col + size
!                    && -tp->tp_id <= wp->w_buffer->b_textprop_text.ga_len)
            {
                char_u *p = ((char_u **)wp->w_buffer->b_textprop_text.ga_data)[
                                                               -tp->tp_id - 1];
                // TODO: count screen cells
!               cts->cts_cur_text_width = (int)STRLEN(p);
!               size += cts->cts_cur_text_width;
!               break;
            }
            if (tp->tp_col - 1 > col)
                break;
--- 1090,1113 ----
            textprop_T *tp = cts->cts_text_props + i;
  
            if (tp->tp_id < 0
!                   && ((tp->tp_col - 1 >= col && tp->tp_col - 1 < col + size
!                       && -tp->tp_id <= wp->w_buffer->b_textprop_text.ga_len)
!                   || (tp->tp_col == MAXCOL && (s[0] == NUL || s[1] == NUL)
!                                                  && cts->cts_with_trailing)))
            {
                char_u *p = ((char_u **)wp->w_buffer->b_textprop_text.ga_data)[
                                                               -tp->tp_id - 1];
+               int len = (int)STRLEN(p);
+ 
                // TODO: count screen cells
!               if (tp->tp_col == MAXCOL)
!               {
!                   // TODO: truncating
!                   if (tp->tp_flags & TP_FLAG_ALIGN_BELOW)
!                       len += wp->w_width - (vcol + size) % wp->w_width;
!               }
!               cts->cts_cur_text_width += len;
!               size += len;
            }
            if (tp->tp_col - 1 > col)
                break;
*** ../vim-9.0.0120/src/testdir/test_textprop.vim       2022-07-30 
22:17:14.812697205 +0100
--- src/testdir/test_textprop.vim       2022-07-31 16:25:28.438652874 +0100
***************
*** 2213,2218 ****
--- 2213,2238 ----
    call delete('XscriptPropsWithText')
  endfunc
  
+ func Test_props_with_text_after()
+   CheckRunVimInTerminal
+ 
+   let lines =<< trim END
+       call setline(1, 'some text here and other text there')
+       call prop_type_add('rightprop', #{highlight: 'ErrorMsg'})
+       call prop_type_add('afterprop', #{highlight: 'Search'})
+       call prop_type_add('belowprop', #{highlight: 'DiffAdd'})
+       call prop_add(1, 0, #{type: 'rightprop', text: ' RIGHT ', text_align: 
'right'})
+       call prop_add(1, 0, #{type: 'afterprop', text: ' AFTER ', text_align: 
'after'})
+       call prop_add(1, 0, #{type: 'belowprop', text: ' BELOW ', text_align: 
'below'})
+   END
+   call writefile(lines, 'XscriptPropsWithTextAfter')
+   let buf = RunVimInTerminal('-S XscriptPropsWithTextAfter', #{rows: 6, cols: 
60})
+   call VerifyScreenDump(buf, 'Test_prop_with_text_after_1', {})
+ 
+   call StopVimInTerminal(buf)
+   call delete('XscriptPropsWithTextAfter')
+ endfunc
+ 
  func Test_removed_prop_with_text_cleans_up_array()
    new
    call setline(1, 'some text here')
*** ../vim-9.0.0120/src/testdir/dumps/Test_prop_with_text_after_1.dump  
2022-07-31 17:09:26.662643392 +0100
--- src/testdir/dumps/Test_prop_with_text_after_1.dump  2022-07-31 
16:18:04.207226915 +0100
***************
*** 0 ****
--- 1,6 ----
+ >s+0&#ffffff0|o|m|e| |t|e|x|t| |h|e|r|e| |a|n|d| |o|t|h|e|r| |t|e|x|t| 
|t|h|e|r|e| +0&#ffff4012|A|F|T|E|R| | +0&#ffffff0@10| 
+0#ffffff16#e000002|R|I|G|H|T| 
+ | +0#0000000#5fd7ff255|B|E|L|O|W| | +0&#ffffff0@52
+ |~+0#4040ff13&| @58
+ |~| @58
+ |~| @58
+ | +0#0000000&@41|1|,|1| @10|A|l@1| 
*** ../vim-9.0.0120/src/version.c       2022-07-31 12:26:01.424141509 +0100
--- src/version.c       2022-07-31 12:40:12.789911850 +0100
***************
*** 737,738 ****
--- 737,740 ----
  {   /* Add new patch number below this line */
+ /**/
+     121,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
209. Your house stinks because you haven't cleaned it in a week.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20220731163641.CBE6E1C0C11%40moolenaar.net.

Raspunde prin e-mail lui