]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0896: textprop: wrong Tab size in a line with virtual text above it v9.2.0896
authorHirohito Higashi <h.east.727@gmail.com>
Sun, 2 Aug 2026 17:20:29 +0000 (17:20 +0000)
committerChristian Brabandt <cb@256bit.org>
Sun, 2 Aug 2026 17:20:29 +0000 (17:20 +0000)
Problem:  In a line with virtual text above it a Tab does not have the right
          size, depending on the width of the window.
Solution: Do not count the columns of the virtual text for the size of the
          Tab, neither when drawing nor when computing the column.

fixes:  #12232
closes: #20901

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Hirohito Higashi <h.east.727@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
src/charset.c
src/drawline.c
src/structs.h
src/testdir/test_textprop.vim
src/version.c

index bf7c59620f1bee79c21d1139a7a8b6c7b94cb78c..c47180e2ce0fea592c4a70399926c1905b1e1a04 100644 (file)
@@ -1285,11 +1285,18 @@ win_lbr_chartabsize(
 
 #if defined(FEAT_LINEBREAK) || defined(FEAT_PROP_POPUP)
     int has_lcs_eol = wp->w_p_list && wp->w_lcs_chars.eol != NUL;
+    // Virtual text above the line is on its own screen line, it does not count
+    // for the size of a Tab.
+    colnr_T tab_vcol = vcol;
+
+# ifdef FEAT_PROP_POPUP
+    tab_vcol -= cts->cts_above_width;
+# endif
 
     /*
      * First get the normal size, without 'linebreak' or text properties
      */
-    size = win_chartabsize(wp, s, vcol);
+    size = win_chartabsize(wp, s, tab_vcol);
 # ifdef FEAT_LINEBREAK
     if (*s == NUL)
     {
@@ -1367,7 +1374,8 @@ win_lbr_chartabsize(
                    {
                        // tab size changes because of the inserted text
                        size -= tab_size;
-                       tab_size = win_chartabsize(wp, s, vcol + size);
+                       tab_size = win_chartabsize(wp, s,
+                                       vcol + size - cts->cts_above_width);
                        size += tab_size;
                    }
 #  endif
@@ -1549,6 +1557,10 @@ win_lbr_chartabsize(
        *tailp = size - size_before_lbr;
 
 #  ifdef FEAT_PROP_POPUP
+    if (cts->cts_first_char > 0)
+       // Remember the width for the size of a Tab later in the line.  Use
+       // assignment, this may be called more than once for a character.
+       cts->cts_above_width = cts->cts_first_char;
     size += cts->cts_first_char;
 #  endif
 # endif
index a2679e14679249e1e849b47f30c628334fa7e255..c799291ba6b063c7c67f375a2342b0af2cb72a9d 100644 (file)
@@ -3334,7 +3334,9 @@ win_line(
                if (c == TAB && (!wp->w_p_list || wp->w_lcs_chars.tab1))
                {
                    int     tab_len = 0;
-                   long    vcol_adjusted = wlv.vcol; // removed showbreak len
+                   // Virtual text and 'showbreak' do not count for the size
+                   // of a Tab.
+                   long    vcol_adjusted = wlv.vcol - wlv.vcol_off_tp;
                    int     lcs_tab1 = wp->w_lcs_chars.tab1;
                    int     lcs_tab2 = wp->w_lcs_chars.tab2;
                    int     lcs_tab3 = wp->w_lcs_chars.tab3;
@@ -3353,7 +3355,7 @@ win_line(
                    // only adjust the tab_len, when at the first column
                    // after the showbreak value was drawn
                    if (*sbr != NUL && wlv.vcol == wlv.vcol_sbr && wp->w_p_wrap)
-                       vcol_adjusted = wlv.vcol - MB_CHARLEN(sbr);
+                       vcol_adjusted -= MB_CHARLEN(sbr);
 #endif
                    // tab amount depends on current column
 #ifdef FEAT_VARTABS
index d9b7b6b0a587e5f411cfebd4ff626a320c394f20..38037c9d5ea8a01377cef7a259a05a9f580d29c8 100644 (file)
@@ -5359,6 +5359,8 @@ typedef struct {
     int                cts_cur_text_width;     // width of current inserted text
     int                cts_prop_lines;         // nr of properties above or below
     int                cts_first_char;         // width text props above the line
+    int                cts_above_width;        // width of text props above the line,
+                                       // kept for the whole line
     int                cts_with_trailing;      // include size of trailing props with
                                        // last character
     int                cts_start_incl;         // prop has true "start_incl" arg
index a566a47af8d6d09c7b2121f1363ca65fef940442..a89ed55154151af45a3857c4e7f4947d74d5208d 100644 (file)
@@ -3782,6 +3782,48 @@ func Test_prop_above_with_indent()
   call prop_type_delete('indented')
 endfunc
 
+" A Tab in the line is not affected by virtual text above it.
+func Test_prop_above_with_tab()
+  " Use a width that is not a multiple of 'tabstop', otherwise counting the
+  " virtual text for the size of a Tab happens to give the right result.
+  call NewWindow(10, 45)
+  setlocal tabstop=8
+  call setline(1, ["\tX"])
+  call prop_type_add('above', #{highlight: 'Search'})
+
+  " Get the column of the "X" without and with the virtual text.
+  redraw
+  let col_without = 0
+  for col in range(1, winwidth(0))
+    if screenstring(1, col) == 'X'
+      let col_without = col
+      break
+    endif
+  endfor
+  call assert_equal(9, col_without)
+
+  call prop_add(1, 0, #{type: 'above', text: 'text above', text_align: 'above'})
+  redraw
+  let col_with = 0
+  for col in range(1, winwidth(0))
+    if screenstring(2, col) == 'X'
+      let col_with = col
+      break
+    endif
+  endfor
+  call assert_equal(col_without, col_with)
+
+  " The cursor is placed on the character, also with a second Tab.
+  call setline(1, ["\t\tX"])
+  redraw
+  normal! 0fX
+  call assert_equal('X', screenstring(winline(), wincol()))
+
+  only!
+  bwipe!
+  call prop_type_delete('above')
+endfunc
+
 func Test_prop_above_with_number()
   CheckScreendump
   CheckRunVimInTerminal
index d1b8ad9311406ae3e98738469a6261b742b8f99c..30b9a310bed69ea1193bb5039bcb3248b5a1b553 100644 (file)
@@ -758,6 +758,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    896,
 /**/
     895,
 /**/