Commit: patch 9.2.0901: textprop: wrong cursor line with truncated virtual text

Christian Brabandt <[email protected]> Sun, 2 Aug 2026 21:30:04 +0200
Newsgroups gmane.editors.vim.devel
Message-ID <[email protected]>
patch 9.2.0901: textprop: wrong cursor line with truncated virtual text

Commit: https://github.com/vim/vim/commit/1050666c96c68abe3ef738359312e85c76607901
Author: Hirohito Higashi <[email protected]>
Date:   Sun Aug 2 19:25:12 2026 +0000

    patch 9.2.0901: textprop: wrong cursor line with truncated virtual text
    
    Problem:  The cursor is displayed in the wrong line when virtual text below
              an empty line is truncated.
    Solution: Do not count an extra column for an empty line that has virtual
              text below it, its width already includes filling up the line.
              (Hirohito Higashi).
    
    fixes:  #12493
    closes: #20917
    
    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/runtime/doc/todo.txt b/runtime/doc/todo.txt
index a277a260f..a8f9dff8e 100644
--- a/runtime/doc/todo.txt
+++ b/runtime/doc/todo.txt
@@ -48,7 +48,6 @@ without all the help files.
 Virtual text problems:
 -  Virtual text aligned "above": Wrong indentation when using tabs  (Issue
    #12232)
--  truncated Virtual text below an empty line causes display error #12493
 
 Errors when running tests with valgrind:
 - test_gui.vim:
diff --git a/src/charset.c b/src/charset.c
index c47180e2c..1c45688fe 100644
--- a/src/charset.c
+++ b/src/charset.c
@@ -921,9 +921,10 @@ win_linetabsize_cts(chartabsize_T *cts, colnr_T len)
 	int head = 0;
 	(void)win_lbr_chartabsize(cts, &head, NULL);
 	vcol += cts->cts_cur_text_width + head;
-	// when properties are above or below the empty line must also be
-	// counted
-	if (cts->cts_ptr == cts->cts_line && cts->cts_prop_lines > 0)
+	// When properties are above the empty line must also be counted.  For
+	// a property below the width already includes filling up the line.
+	if (cts->cts_ptr == cts->cts_line && cts->cts_prop_lines > 0
+							 && !cts->cts_has_below)
 	    ++vcol;
 	cts->cts_vcol = vcol > MAXCOL ? MAXCOL : (int)vcol;
     }
@@ -1260,6 +1261,7 @@ win_lbr_chartabsize(
 
 #if defined(FEAT_PROP_POPUP)
     cts->cts_cur_text_width = 0;
+    cts->cts_has_below = false;
     cts->cts_first_char = 0;
 #endif
 
@@ -1381,8 +1383,12 @@ win_lbr_chartabsize(
 #  endif
 		    if (tp->tp_col == MAXCOL && (tp->tp_flags
 				& (TP_FLAG_ALIGN_ABOVE | TP_FLAG_ALIGN_BELOW)))
+		    {
 			// count extra line for property above/below
 			++cts->cts_prop_lines;
+			if (tp->tp_flags & TP_FLAG_ALIGN_BELOW)
+			    cts->cts_has_below = true;
+		    }
 		}
 	    }
 	    if (tp->tp_col != MAXCOL && tp->tp_col - 1 > col)
diff --git a/src/structs.h b/src/structs.h
index b1ec3fa78..fdba4ee83 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -5361,6 +5361,8 @@ typedef struct {
     char	cts_has_prop_with_text;	// TRUE if a property inserts text
     int		cts_cur_text_width;	// width of current inserted text
     int		cts_prop_lines;		// nr of properties above or below
+    bool	cts_has_below;		// true if a text property below was
+					// counted, its width fills up the line
     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
diff --git a/src/testdir/test_textprop.vim b/src/testdir/test_textprop.vim
index a89ed5515..86437354b 100644
--- a/src/testdir/test_textprop.vim
+++ b/src/testdir/test_textprop.vim
@@ -3229,6 +3229,27 @@ func Test_prop_with_text_above_below_empty()
   call StopVimInTerminal(buf)
 endfunc
 
+func Test_prop_with_text_below_empty_truncated()
+  " Use a fixed size, the virtual text must be wider than the text area.
+  call NewWindow(12, 40)
+  setlocal number
+  call setline(1, ['11111', '', '33333', '', '55555'])
+
+  call prop_type_add('belowprop', #{highlight: 'Directory'})
+  for ln in range(1, 5)
+    call prop_add(ln, 0, #{type: 'belowprop',
+	  \ text: repeat('+', winwidth(0)), text_align: 'below'})
+  endfor
+  normal! G
+  redraw
+
+  " Every line takes two screen lines: the line and the virtual text below it.
+  call assert_equal(9, winline())
+
+  call prop_type_delete('belowprop')
+  bwipe!
+endfunc
+
 func Test_prop_multiple_lines_above()
   CheckScreendump
   CheckRunVimInTerminal
diff --git a/src/version.c b/src/version.c
index ac814a01e..dcea5c2ab 100644
--- a/src/version.c
+++ b/src/version.c
@@ -763,6 +763,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    901,
 /**/
     900,
 /**/

-- 
-- 
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/E1wqbsq-009eDj-Qq%40256bit.org.