Commit: patch 9.2.0896: textprop: wrong Tab size in a line with virtual text above it

Christian Brabandt <[email protected]> Sun, 2 Aug 2026 19:45:10 +0200
Newsgroups gmane.editors.vim.devel
Message-ID <[email protected]>
patch 9.2.0896: textprop: wrong Tab size in a line with virtual text above it

Commit: https://github.com/vim/vim/commit/a556e21552e9978e670fd351c8fc263efffe47b1
Author: Hirohito Higashi <[email protected]>
Date:   Sun Aug 2 17:20:29 2026 +0000

    patch 9.2.0896: textprop: wrong Tab size in a line with virtual text above it
    
    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) <[email protected]>
    Signed-off-by: Hirohito Higashi <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/src/charset.c b/src/charset.c
index bf7c59620..c47180e2c 100644
--- a/src/charset.c
+++ b/src/charset.c
@@ -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
diff --git a/src/drawline.c b/src/drawline.c
index a2679e146..c799291ba 100644
--- a/src/drawline.c
+++ b/src/drawline.c
@@ -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
diff --git a/src/structs.h b/src/structs.h
index d9b7b6b0a..38037c9d5 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -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
diff --git a/src/testdir/test_textprop.vim b/src/testdir/test_textprop.vim
index a566a47af..a89ed5515 100644
--- a/src/testdir/test_textprop.vim
+++ b/src/testdir/test_textprop.vim
@@ -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, ["	X"])
+  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, ["		X"])
+  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
diff --git a/src/version.c b/src/version.c
index d1b8ad931..30b9a310b 100644
--- a/src/version.c
+++ b/src/version.c
@@ -758,6 +758,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    896,
 /**/
     895,
 /**/

-- 
-- 
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 visit https://groups.google.com/d/msgid/vim_dev/E1wqaFK-009WW5-E1%40256bit.org.