Patch 9.0.0438
Problem:    Cannot put virtual text above a line.
Solution:   Add the "above" value for "text_align".
Files:      runtime/doc/textprop.txt, src/textprop.c, src/structs.h,
            src/charset.c, src/proto/charset.pro, src/drawline.c,
            src/move.c, src/misc2.c, src/testdir/test_textprop.vim,
            src/testdir/dumps/Test_prop_with_text_above_1.dump


*** ../vim-9.0.0437/runtime/doc/textprop.txt    2022-08-23 18:39:14.756797669 
+0100
--- runtime/doc/textprop.txt    2022-09-10 15:37:36.718018982 +0100
***************
*** 153,158 ****
--- 153,159 ----
                                           the text wraps to the next screen
                                           line)
                                   below   in the next screen line
+                                  above   just above the line
                                When omitted "after" is used.  Only one
                                "right" property can fit in each line, if
                                there are two ore more these will go in a
*** ../vim-9.0.0437/src/textprop.c      2022-09-01 12:22:19.747659165 +0100
--- src/textprop.c      2022-09-10 15:50:46.276035379 +0100
***************
*** 497,502 ****
--- 497,504 ----
            }
            if (STRCMP(p, "right") == 0)
                flags |= TP_FLAG_ALIGN_RIGHT;
+           else if (STRCMP(p, "above") == 0)
+               flags |= TP_FLAG_ALIGN_ABOVE;
            else if (STRCMP(p, "below") == 0)
                flags |= TP_FLAG_ALIGN_BELOW;
            else if (STRCMP(p, "after") != 0)
***************
*** 673,678 ****
--- 675,695 ----
  static textprop_T     *text_prop_compare_props;
  static buf_T          *text_prop_compare_buf;
  
+ /* Score for sorting on position of the text property: 0: above,
+  * 1: after (default), 2: right, 3: below (comes last)
+  */
+     static int
+ text_prop_order(int flags)
+ {
+     if (flags & TP_FLAG_ALIGN_ABOVE)
+       return 0;
+     if (flags & TP_FLAG_ALIGN_RIGHT)
+       return 2;
+     if (flags & TP_FLAG_ALIGN_BELOW)
+       return 3;
+     return 1;
+ }
+ 
  /*
   * Function passed to qsort() to sort text properties.
   * Return 1 if "s1" has priority over "s2", -1 if the other way around, zero 
if
***************
*** 694,714 ****
      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
--- 711,723 ----
      col2 = tp2->tp_col;
      if (col1 == MAXCOL && col2 == MAXCOL)
      {
!       int order1 = text_prop_order(tp1->tp_flags);
!       int order2 = text_prop_order(tp2->tp_flags);
  
!       // both props add text before or after the line, sort on order where it
!       // is added
!       if (order1 != order2)
!           return order1 < order2 ? 1 : -1;
      }
  
      // property that inserts text has priority over one that doesn't
*** ../vim-9.0.0437/src/structs.h       2022-09-07 20:01:13.323141838 +0100
--- src/structs.h       2022-09-10 17:56:47.601964368 +0100
***************
*** 813,824 ****
  #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
! #define TP_FLAG_START_INCL    0x80    // "start_incl" copied from proptype
  
  #define PROP_TEXT_MIN_CELLS   4       // minimun number of cells to use for
                                        // the text, even when truncating
--- 813,825 ----
  #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   0x010   // virtual text is right-aligned
! #define TP_FLAG_ALIGN_ABOVE   0x020   // virtual text above the line
! #define TP_FLAG_ALIGN_BELOW   0x040   // virtual text on next screen line
  
! #define TP_FLAG_WRAP          0x080   // virtual text wraps - when missing
                                        // text is truncated
! #define TP_FLAG_START_INCL    0x100   // "start_incl" copied from proptype
  
  #define PROP_TEXT_MIN_CELLS   4       // minimun number of cells to use for
                                        // the text, even when truncating
***************
*** 3678,3683 ****
--- 3679,3689 ----
                                    // more than one screen line or when
                                    // w_leftcol is non-zero
  
+ #ifdef FEAT_PROP_POPUP
+     colnr_T   w_virtcol_first_char;   // offset for w_virtcol when there are
+                                       // virtual text properties above the
+                                       // line
+ #endif
      /*
       * w_wrow and w_wcol specify the cursor position in the window.
       * This is related to positions in the window, not in the display or
***************
*** 4607,4612 ****
--- 4613,4619 ----
      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_first_char;               // width text props above the 
line
      int               cts_with_trailing;      // include size of trailing 
props with
                                        // last character
      int               cts_start_incl;         // prop has true "start_incl" 
arg
*** ../vim-9.0.0437/src/charset.c       2022-09-07 21:46:48.733219019 +0100
--- src/charset.c       2022-09-10 19:36:37.333121913 +0100
***************
*** 1069,1108 ****
      return retval;
  }
  
- #if defined(FEAT_PROP_POPUP) || defined(PROTO)
- /*
-  * Return the cell size of virtual text after truncation.
-  */
-     int
- textprop_size_after_trunc(
-       win_T   *wp,
-       int     below,
-       int     added,
-       char_u  *text,
-       int     *n_used_ptr)
- {
-     int       space = below ? wp->w_width : added;
-     int len = (int)STRLEN(text);
-     int strsize = 0;
-     int n_used;
- 
-     // if the remaining size is to small wrap
-     // anyway and use the next line
-     if (space < PROP_TEXT_MIN_CELLS)
-       space += wp->w_width;
-     for (n_used = 0; n_used < len; n_used += (*mb_ptr2len)(text + n_used))
-     {
-       int clen = ptr2cells(text + n_used);
- 
-       if (strsize + clen > space)
-           break;
-       strsize += clen;
-     }
-     *n_used_ptr = n_used;
-     return strsize;
- }
- #endif
- 
  /*
   * Return the screen size of the character indicated by "cts".
   * "cts->cts_cur_text_width" is set to the extra size for a text property that
--- 1069,1074 ----
***************
*** 1142,1147 ****
--- 1108,1114 ----
  
  #if defined(FEAT_PROP_POPUP)
      cts->cts_cur_text_width = 0;
+     cts->cts_first_char = 0;
  #endif
  
  #if defined(FEAT_LINEBREAK) || defined(FEAT_PROP_POPUP)
***************
*** 1194,1202 ****
            if (tp->tp_id < 0
                    && ((tp->tp_col - 1 >= col
                                             && tp->tp_col - 1 < col + charlen)
!                      || (tp->tp_col == MAXCOL && (s[0] == NUL || s[1] == NUL)
!                                                  && cts->cts_with_trailing))
!                   && -tp->tp_id - 1 < gap->ga_len)
            {
                char_u *p = ((char_u **)gap->ga_data)[-tp->tp_id - 1];
  
--- 1161,1172 ----
            if (tp->tp_id < 0
                    && ((tp->tp_col - 1 >= col
                                             && tp->tp_col - 1 < col + charlen)
!                      || (tp->tp_col == MAXCOL
!                          && ((tp->tp_flags & TP_FLAG_ALIGN_ABOVE)
!                               ? col == 0
!                               : (s[0] == NUL || s[1] == NUL)
!                                                 && cts->cts_with_trailing)))
!                   && tp->tp_id - 1 < gap->ga_len)
            {
                char_u *p = ((char_u **)gap->ga_data)[-tp->tp_id - 1];
  
***************
*** 1218,1223 ****
--- 1188,1195 ----
                    else
                        cells = vim_strsize(p);
                    cts->cts_cur_text_width += cells;
+                   if (tp->tp_flags & TP_FLAG_ALIGN_ABOVE)
+                       cts->cts_first_char += cells;
                    cts->cts_start_incl = tp->tp_flags & TP_FLAG_START_INCL;
                    size += cells;
                    if (*s == TAB)
***************
*** 1564,1569 ****
--- 1536,1546 ----
  #endif
                break;
            }
+ #ifdef FEAT_PROP_POPUP
+           if (cursor == &wp->w_virtcol && cts.cts_ptr == cts.cts_line)
+               // do not count the virtual text above for w_curswant
+               wp->w_virtcol_first_char = cts.cts_first_char;
+ #endif
  
            if (posptr != NULL && cts.cts_ptr >= posptr)
                // character at pos->col
*** ../vim-9.0.0437/src/proto/charset.pro       2022-08-09 18:24:58.594059458 
+0100
--- src/proto/charset.pro       2022-09-10 16:44:42.692151302 +0100
***************
*** 34,40 ****
  void clear_chartabsize_arg(chartabsize_T *cts);
  int lbr_chartabsize(chartabsize_T *cts);
  int lbr_chartabsize_adv(chartabsize_T *cts);
- int textprop_size_after_trunc(win_T *wp, int below, int added, char_u *text, 
int *n_used_ptr);
  int win_lbr_chartabsize(chartabsize_T *cts, int *headp);
  void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T 
*end);
  colnr_T getvcol_nolist(pos_T *posp);
--- 34,39 ----
*** ../vim-9.0.0437/src/drawline.c      2022-09-02 13:26:03.848450830 +0100
--- src/drawline.c      2022-09-10 18:25:24.360978117 +0100
***************
*** 279,284 ****
--- 279,316 ----
  
  #if defined(FEAT_PROP_POPUP) || defined(PROTO)
  /*
+  * Return the cell size of virtual text after truncation.
+  */
+     static int
+ textprop_size_after_trunc(
+       win_T   *wp,
+       int     flags,      // TP_FLAG_ALIGN_*
+       int     added,
+       char_u  *text,
+       int     *n_used_ptr)
+ {
+     int       space = (flags & (TP_FLAG_ALIGN_BELOW | TP_FLAG_ALIGN_ABOVE))
+                                                        ? wp->w_width : added;
+     int len = (int)STRLEN(text);
+     int strsize = 0;
+     int n_used;
+ 
+     // if the remaining size is to small wrap anyway and use the next line
+     if (space < PROP_TEXT_MIN_CELLS)
+       space += wp->w_width;
+     for (n_used = 0; n_used < len; n_used += (*mb_ptr2len)(text + n_used))
+     {
+       int clen = ptr2cells(text + n_used);
+ 
+       if (strsize + clen > space)
+           break;
+       strsize += clen;
+     }
+     *n_used_ptr = n_used;
+     return strsize;
+ }
+ 
+ /*
   * 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
***************
*** 297,302 ****
--- 329,335 ----
        int         *n_attr_skip)   // cells to skip attr, NULL if not used
  {
      int           right = (tp->tp_flags & TP_FLAG_ALIGN_RIGHT);
+     int           above = (tp->tp_flags & TP_FLAG_ALIGN_ABOVE);
      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
***************
*** 304,339 ****
      int           col_with_padding = vcol + (below ? 0 : padding);
      int           col_off = 0;
      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)
      {
!       // 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
!               col_off = win_col_off(wp) + win_col_off2(wp);
!               added = wp->w_width - col_off - strsize + room;
!               if (added < 0)
!                   added = 0;
                else
!                   n_used = *n_extra;
            }
-           else
-               added = 0;
        }
  
        // With 'nowrap' add one to show the "extends" character if needed (it
--- 337,381 ----
      int           col_with_padding = vcol + (below ? 0 : padding);
      int           col_off = 0;
      int           room = wp->w_width - col_with_padding;
!     int           before = room;      // spaces before the text
!     int           after = 0;          // spaces after the text
      int           n_used = *n_extra;
      char_u  *l = NULL;
      int           strsize = vim_strsize(*p_extra);
!     int           cells = wrap ? strsize : textprop_size_after_trunc(wp,
!                                     tp->tp_flags, before, *p_extra, &n_used);
  
!     if (wrap || right || above || below || padding > 0 || n_used < *n_extra)
      {
!       if (above)
!       {
!           before = 0;
!           after = wp->w_width - cells;
!       }
!       else
!       {
!           // Right-align: fill with before
!           if (right)
!               before -= cells;
!           if (before < 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
!                   col_off = win_col_off(wp) + win_col_off2(wp);
!                   before = wp->w_width - col_off - strsize + room;
!                   if (before < 0)
!                       before = 0;
!                   else
!                       n_used = *n_extra;
!               }
                else
!                   before = 0;
            }
        }
  
        // With 'nowrap' add one to show the "extends" character if needed (it
***************
*** 346,360 ****
  
        // 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;
  
            if (n_attr != NULL)
            {
!               vim_memset(l, ' ', added);
!               off += added;
                if (padding > 0)
                {
                    vim_memset(l + off, ' ', padding);
--- 388,402 ----
  
        // add 1 for NUL, 2 for when '…' is used
        if (n_attr != NULL)
!           l = alloc(n_used + before + after + padding + 3);
        if (n_attr == NULL || l != NULL)
        {
            int off = 0;
  
            if (n_attr != NULL)
            {
!               vim_memset(l, ' ', before);
!               off += before;
                if (padding > 0)
                {
                    vim_memset(l + off, ' ', padding);
***************
*** 365,372 ****
            }
            else
            {
!               off = added + padding + n_used;
!               cells += added + padding;
            }
            if (n_attr != NULL)
            {
--- 407,414 ----
            }
            else
            {
!               off = before + after + padding + n_used;
!               cells += before + after + padding;
            }
            if (n_attr != NULL)
            {
***************
*** 385,394 ****
                        // 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 + col_off;
            }
        }
      }
--- 427,442 ----
                        // change last character to '>'
                        *lp = '>';
                }
+               else if (after > 0)
+               {
+                   vim_memset(l + off, ' ', after);
+                   l[off + after] = NUL;
+               }
+ 
                *p_extra = l;
!               *n_extra = n_used + before + after + padding;
                *n_attr = mb_charlen(*p_extra);
!               *n_attr_skip = before + padding + col_off;
            }
        }
      }
***************
*** 1694,1704 ****
                // text prop can show.
                while (text_prop_next < text_prop_count
                           && (text_props[text_prop_next].tp_col == MAXCOL
!                             ? (*ptr == NUL
                                  && (wp->w_p_wrap
                                      || wlv.row == startrow
                                      || (text_props[text_prop_next].tp_flags
                                                       & TP_FLAG_ALIGN_BELOW)))
                              : bcol >= text_props[text_prop_next].tp_col - 1))
                {
                    if (text_props[text_prop_next].tp_col == MAXCOL
--- 1742,1755 ----
                // text prop can show.
                while (text_prop_next < text_prop_count
                           && (text_props[text_prop_next].tp_col == MAXCOL
!                             ? ((*ptr == NUL
                                  && (wp->w_p_wrap
                                      || wlv.row == startrow
                                      || (text_props[text_prop_next].tp_flags
                                                       & TP_FLAG_ALIGN_BELOW)))
+                              || (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
***************
*** 1773,1778 ****
--- 1824,1831 ----
                        {
                            int     right = (tp->tp_flags
                                                        & TP_FLAG_ALIGN_RIGHT);
+                           int     above = (tp->tp_flags
+                                                       & TP_FLAG_ALIGN_ABOVE);
                            int     below = (tp->tp_flags
                                                        & TP_FLAG_ALIGN_BELOW);
                            int     wrap = (tp->tp_flags & TP_FLAG_WRAP);
***************
*** 1797,1814 ****
                                // don't combine char attr after EOL
                                text_prop_flags &= ~PT_FLAG_COMBINE;
  #ifdef FEAT_LINEBREAK
!                           if (below || right || !wrap)
                            {
                                // no 'showbreak' before "below" text property
!                               // or after "right" text property
                                need_showbreak = FALSE;
                                dont_use_showbreak = TRUE;
                            }
  #endif
!                           // Keep in sync with where
!                           // textprop_size_after_trunc() is called in
!                           // win_lbr_chartabsize().
!                           if ((right || below || !wrap || padding > 0)
                                                            && wp->w_width > 2)
                            {
                                char_u  *prev_p_extra = wlv.p_extra;
--- 1850,1864 ----
                                // don't combine char attr after EOL
                                text_prop_flags &= ~PT_FLAG_COMBINE;
  #ifdef FEAT_LINEBREAK
!                           if (above || below || right || !wrap)
                            {
                                // no 'showbreak' before "below" text property
!                               // or after "above" or "right" text property
                                need_showbreak = FALSE;
                                dont_use_showbreak = TRUE;
                            }
  #endif
!                           if ((right || above || below || !wrap || padding > 
0)
                                                            && wp->w_width > 2)
                            {
                                char_u  *prev_p_extra = wlv.p_extra;
*** ../vim-9.0.0437/src/move.c  2022-08-14 14:16:07.995582211 +0100
--- src/move.c  2022-09-10 19:46:34.851494080 +0100
***************
*** 476,482 ****
      if (curwin->w_set_curswant)
      {
        validate_virtcol();
!       curwin->w_curswant = curwin->w_virtcol;
        curwin->w_set_curswant = FALSE;
      }
  }
--- 476,486 ----
      if (curwin->w_set_curswant)
      {
        validate_virtcol();
!       curwin->w_curswant = curwin->w_virtcol
! #ifdef FEAT_PROP_POPUP
!                               - curwin->w_virtcol_first_char
! #endif
!                               ;
        curwin->w_set_curswant = FALSE;
      }
  }
***************
*** 835,840 ****
--- 839,847 ----
      check_cursor_moved(wp);
      if (!(wp->w_valid & VALID_VIRTCOL))
      {
+ #ifdef FEAT_PROP_POPUP
+       wp->w_virtcol_first_char = 0;
+ #endif
        getvvcol(wp, &wp->w_cursor, NULL, &(wp->w_virtcol), NULL);
  #ifdef FEAT_SYN_HL
        redraw_for_cursorcolumn(wp);
***************
*** 982,987 ****
--- 989,999 ----
      if (!(curwin->w_valid & VALID_CROW))
        curs_rows(curwin);
  
+ #ifdef FEAT_PROP_POPUP
+     // will be set by getvvcol() but not reset
+     curwin->w_virtcol_first_char = 0;
+ #endif
+ 
      /*
       * Compute the number of virtual columns.
       */
*** ../vim-9.0.0437/src/misc2.c 2022-08-25 16:02:09.677816456 +0100
--- src/misc2.c 2022-09-10 18:39:37.746726450 +0100
***************
*** 85,91 ****
  }
  
  /*
!  * Try to advance the Cursor to the specified screen column.
   * If virtual editing: fine tune the cursor position.
   * Note that all virtual positions off the end of a line should share
   * a curwin->w_cursor.col value (n.b. this is equal to STRLEN(line)),
--- 85,91 ----
  }
  
  /*
!  * Try to advance the Cursor to the specified screen column "wantcol".
   * If virtual editing: fine tune the cursor position.
   * Note that all virtual positions off the end of a line should share
   * a curwin->w_cursor.col value (n.b. this is equal to STRLEN(line)),
***************
*** 94,122 ****
   * return OK if desired column is reached, FAIL if not
   */
      int
! coladvance(colnr_T wcol)
  {
!     int rc = getvpos(&curwin->w_cursor, wcol);
  
!     if (wcol == MAXCOL || rc == FAIL)
        curwin->w_valid &= ~VALID_VIRTCOL;
      else if (*ml_get_cursor() != TAB)
      {
        // Virtcol is valid when not on a TAB
        curwin->w_valid |= VALID_VIRTCOL;
!       curwin->w_virtcol = wcol;
      }
      return rc;
  }
  
  /*
!  * Return in "pos" the position of the cursor advanced to screen column 
"wcol".
   * return OK if desired column is reached, FAIL if not
   */
      int
! getvpos(pos_T *pos, colnr_T wcol)
  {
!     return coladvance2(pos, FALSE, virtual_active(), wcol);
  }
  
      static int
--- 94,123 ----
   * return OK if desired column is reached, FAIL if not
   */
      int
! coladvance(colnr_T wantcol)
  {
!     int rc = getvpos(&curwin->w_cursor, wantcol);
  
!     if (wantcol == MAXCOL || rc == FAIL)
        curwin->w_valid &= ~VALID_VIRTCOL;
      else if (*ml_get_cursor() != TAB)
      {
        // Virtcol is valid when not on a TAB
        curwin->w_valid |= VALID_VIRTCOL;
!       curwin->w_virtcol = wantcol;
      }
      return rc;
  }
  
  /*
!  * Return in "pos" the position of the cursor advanced to screen column
!  * "wantcol".
   * return OK if desired column is reached, FAIL if not
   */
      int
! getvpos(pos_T *pos, colnr_T wantcol)
  {
!     return coladvance2(pos, FALSE, virtual_active(), wantcol);
  }
  
      static int
***************
*** 156,163 ****
      }
      else
      {
!       int width = curwin->w_width - win_col_off(curwin);
!       chartabsize_T cts;
  
        if (finetune
                && curwin->w_p_wrap
--- 157,164 ----
      }
      else
      {
!       int             width = curwin->w_width - win_col_off(curwin);
!       chartabsize_T   cts;
  
        if (finetune
                && curwin->w_p_wrap
***************
*** 183,188 ****
--- 184,192 ----
        init_chartabsize_arg(&cts, curwin, pos->lnum, 0, line, line);
        while (cts.cts_vcol <= wcol && *cts.cts_ptr != NUL)
        {
+ #ifdef FEAT_PROP_POPUP
+           int at_start = cts.cts_ptr == cts.cts_line;
+ #endif
            // Count a tab for what it's worth (if list mode not on)
  #ifdef FEAT_LINEBREAK
            csize = win_lbr_chartabsize(&cts, &head);
***************
*** 191,196 ****
--- 195,205 ----
            csize = lbr_chartabsize_adv(&cts);
  #endif
            cts.cts_vcol += csize;
+ #ifdef FEAT_PROP_POPUP
+           if (at_start)
+               // do not count the columns for virtual text above
+               cts.cts_vcol -= cts.cts_first_char;
+ #endif
        }
        col = cts.cts_vcol;
        idx = (int)(cts.cts_ptr - line);
***************
*** 2400,2406 ****
  
  /*
   * Change directory to "new_dir".  Search 'cdpath' for relative directory
!  * names, otherwise just mch_chdir().
   */
      int
  vim_chdir(char_u *new_dir)
--- 2409,2415 ----
  
  /*
   * Change directory to "new_dir".  Search 'cdpath' for relative directory
!  * names.
   */
      int
  vim_chdir(char_u *new_dir)
*** ../vim-9.0.0437/src/testdir/test_textprop.vim       2022-09-02 
13:26:03.852450821 +0100
--- src/testdir/test_textprop.vim       2022-09-10 19:51:20.202816212 +0100
***************
*** 2848,2853 ****
--- 2848,2873 ----
    call delete('XscriptPropsBelowNowrap')
  endfunc
  
+ func Test_props_with_text_above()
+   CheckRunVimInTerminal
+ 
+   let lines =<< trim END
+       call setline(1, ['one two', 'three four', 'five six'])
+       call prop_type_add('above1', #{highlight: 'Search'})
+       call prop_type_add('above2', #{highlight: 'DiffChange'})
+       call prop_add(1, 0, #{type: 'above1', text: 'first thing above', 
text_align: 'above'})
+       call prop_add(1, 0, #{type: 'above2', text: 'second thing above', 
text_align: 'above'})
+       call prop_add(3, 0, #{type: 'above1', text: 'another thing', 
text_align: 'above'})
+ 
+       normal gglllj
+   END
+   call writefile(lines, 'XscriptPropsWithTextAbove', 'D')
+   let buf = RunVimInTerminal('-S XscriptPropsWithTextAbove', #{rows: 9, cols: 
60})
+   call VerifyScreenDump(buf, 'Test_prop_with_text_above_1', {})
+ 
+   call StopVimInTerminal(buf)
+ endfunc
+ 
  func Test_props_with_text_override()
    CheckRunVimInTerminal
  
*** ../vim-9.0.0437/src/testdir/dumps/Test_prop_with_text_above_1.dump  
2022-09-10 19:59:58.013552456 +0100
--- src/testdir/dumps/Test_prop_with_text_above_1.dump  2022-09-10 
19:51:33.050786376 +0100
***************
*** 0 ****
--- 1,9 ----
+ |f+0&#ffff4012|i|r|s|t| |t|h|i|n|g| |a|b|o|v|e| @42
+ |s+0&#ffd7ff255|e|c|o|n|d| |t|h|i|n|g| |a|b|o|v|e| @41
+ |o+0&#ffffff0|n|e| |t|w|o| @52
+ |t|h|r>e@1| |f|o|u|r| @49
+ |a+0&#ffff4012|n|o|t|h|e|r| |t|h|i|n|g| @46
+ |f+0&#ffffff0|i|v|e| |s|i|x| @51
+ |~+0#4040ff13&| @58
+ |~| @58
+ | +0#0000000&@41|2|,|4| @10|A|l@1| 
*** ../vim-9.0.0437/src/version.c       2022-09-10 13:51:18.117956356 +0100
--- src/version.c       2022-09-10 19:53:46.754470082 +0100
***************
*** 705,706 ****
--- 705,708 ----
  {   /* Add new patch number below this line */
+ /**/
+     438,
  /**/

-- 
You have the right to remain silent. Anything you say will be
misquoted, then used against you.

 /// 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/20220910190131.656491C0CF3%40moolenaar.net.

Raspunde prin e-mail lui