patch 9.2.0311: redrawing logic with text properties can be improved

Commit: 
https://github.com/vim/vim/commit/c4921023b1234e6c31cc748c6ff13e1cdd7912b1
Author: Yasuhiro Matsumoto <[email protected]>
Date:   Mon Apr 6 13:19:04 2026 +0000

    patch 9.2.0311: redrawing logic with text properties can be improved
    
    Problem:  redrawing logic with text properties can be improved in
              win_line()
    Solution: Avoid repeated sorts, use stack storage for small
              properties, pre-compute whether trailing virtual text follows
              (Yasuhiro Matsumoto)
    
    closes: #19880
    
    Signed-off-by: Yasuhiro Matsumoto <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/src/drawline.c b/src/drawline.c
index e42044256..f3a3b1671 100644
--- a/src/drawline.c
+++ b/src/drawline.c
@@ -1213,13 +1213,19 @@ win_line(
     int                save_did_emsg;
 #endif
 #ifdef FEAT_PROP_POPUP
+# define WIN_LINE_TEXT_PROP_STACK_LEN 32
     int                did_line = FALSE;       // set to TRUE when line text 
done
     int                text_prop_count;
     int                last_textprop_text_idx = -1;
     int                text_prop_next = 0;     // next text property to use
+    textprop_T text_props_buf[WIN_LINE_TEXT_PROP_STACK_LEN];
+    int                text_prop_idxs_buf[WIN_LINE_TEXT_PROP_STACK_LEN];
+    char_u     text_prop_suffix_flags_buf[WIN_LINE_TEXT_PROP_STACK_LEN + 1];
     textprop_T *text_props = NULL;
     int                *text_prop_idxs = NULL;
+    char_u     *text_prop_suffix_flags = NULL;
     int                text_props_active = 0;
+    int                text_props_need_sort = FALSE;
     proptype_T  *text_prop_type = NULL;
     int                text_prop_attr = 0;
     int                text_prop_attr_comb = 0;  // text_prop_attr combined 
with
@@ -1694,15 +1700,40 @@ win_line(
     {
        // Make a copy of the properties, so that they are properly
        // aligned.
-       text_props = ALLOC_MULT(textprop_T, text_prop_count);
+       if (text_prop_count <= WIN_LINE_TEXT_PROP_STACK_LEN)
+           text_props = text_props_buf;
+       else
+           text_props = ALLOC_MULT(textprop_T, text_prop_count);
        if (text_props != NULL)
            mch_memmove(text_props, prop_start,
                                     text_prop_count * sizeof(textprop_T));
 
        // Allocate an array for the indexes.
-       text_prop_idxs = ALLOC_MULT(int, text_prop_count);
+       if (text_prop_count <= WIN_LINE_TEXT_PROP_STACK_LEN)
+           text_prop_idxs = text_prop_idxs_buf;
+       else
+           text_prop_idxs = ALLOC_MULT(int, text_prop_count);
        if (text_prop_idxs == NULL)
-           VIM_CLEAR(text_props);
+       {
+           if (text_props != text_props_buf)
+               VIM_CLEAR(text_props);
+           else
+               text_props = NULL;
+       }
+       if (text_prop_count <= WIN_LINE_TEXT_PROP_STACK_LEN)
+           text_prop_suffix_flags = text_prop_suffix_flags_buf;
+       else
+           text_prop_suffix_flags = ALLOC_MULT(char_u, text_prop_count + 1);
+       if (text_prop_suffix_flags == NULL)
+       {
+           if (text_prop_idxs != text_prop_idxs_buf)
+               vim_free(text_prop_idxs);
+           text_prop_idxs = NULL;
+           if (text_props != text_props_buf)
+               VIM_CLEAR(text_props);
+           else
+               text_props = NULL;
+       }
 
        if (text_props != NULL)
        {
@@ -1736,6 +1767,20 @@ win_line(
                    else
                        ++wlv.text_prop_above_count;
                }
+
+           text_prop_suffix_flags[text_prop_count] = 0;
+           for (int i = text_prop_count - 1; i >= 0; --i)
+           {
+               int flags = text_prop_suffix_flags[i + 1];
+
+               if (text_props[i].tp_col == MAXCOL)
+               {
+                   flags |= 1;
+                   if (text_props[i].tp_flags & TP_FLAG_ALIGN_BELOW)
+                       flags |= 2;
+               }
+               text_prop_suffix_flags[i] = flags;
+           }
        }
     }
 
@@ -1745,8 +1790,12 @@ win_line(
        wlv.row += wlv.text_prop_above_count;
        if (wlv.row >= endrow)
        {
-           vim_free(text_props);
-           vim_free(text_prop_idxs);
+           if (text_props != text_props_buf)
+               vim_free(text_props);
+           if (text_prop_idxs != text_prop_idxs_buf)
+               vim_free(text_prop_idxs);
+           if (text_prop_suffix_flags != text_prop_suffix_flags_buf)
+               vim_free(text_prop_suffix_flags);
            return wlv.row;
        }
        wlv.screen_row += wlv.text_prop_above_count;
@@ -2122,6 +2171,7 @@ win_line(
                                        sizeof(int)
                                             * (text_props_active - (pi + 1)));
                        --text_props_active;
+                       text_props_need_sort = TRUE;
                        --pi;
 # ifdef FEAT_LINEBREAK
                        // not exactly right but should work in most cases
@@ -2164,7 +2214,10 @@ win_line(
                    }
 
                    if (active)
+                   {
                        text_prop_idxs[text_props_active++] = text_prop_next;
+                       text_props_need_sort = TRUE;
+                   }
                    ++text_prop_next;
                }
 
@@ -2190,10 +2243,14 @@ win_line(
                    text_prop_above = FALSE;
                    text_prop_follows = FALSE;
 
-                   // Sort the properties on priority and/or starting last.
-                   // Then combine the attributes, highest priority last.
-                   sort_text_props(wp->w_buffer, text_props,
-                                           text_prop_idxs, text_props_active);
+                   if (text_props_need_sort)
+                   {
+                       // The active set only changes when a property starts
+                       // or ends, so avoid sorting again for every column.
+                       sort_text_props(wp->w_buffer, text_props,
+                                       text_prop_idxs, text_props_active);
+                       text_props_need_sort = FALSE;
+                   }
 
                    for (pi = 0; pi < text_props_active; ++pi)
                    {
@@ -2402,19 +2459,10 @@ win_line(
                    // Or when not wrapping and at the rightmost column.
 
                    int only_below_follows = !wp->w_p_wrap && wlv.col == 
wp->w_width - 1;
-                   // TODO: Store "after"/"right"/"below" text properties in 
order
-                   //       in the buffer so only `text_props[text_prop_count 
- 1]`
-                   //       needs to be checked for following "below" virtual 
text
-                   for (int i = text_prop_next; i < text_prop_count; ++i)
-                   {
-                       if (text_props[i].tp_col == MAXCOL
-                               && (!only_below_follows
-                                   || (text_props[i].tp_flags & 
TP_FLAG_ALIGN_BELOW)))
-                       {
-                           text_prop_follows = TRUE;
-                           break;
-                       }
-                   }
+                   int suffix_flags = text_prop_suffix_flags[text_prop_next];
+
+                   text_prop_follows = (suffix_flags
+                                       & (only_below_follows ? 2 : 1)) != 0;
                }
            }
 
@@ -4500,8 +4548,12 @@ win_line(
 
     }  // for every character in the line
 #ifdef FEAT_PROP_POPUP
-    vim_free(text_props);
-    vim_free(text_prop_idxs);
+    if (text_props != text_props_buf)
+       vim_free(text_props);
+    if (text_prop_idxs != text_prop_idxs_buf)
+       vim_free(text_prop_idxs);
+    if (text_prop_suffix_flags != text_prop_suffix_flags_buf)
+       vim_free(text_prop_suffix_flags);
     vim_free(p_extra_free2);
 #endif
 
diff --git a/src/version.c b/src/version.c
index ed4cf0ecf..57f688cfe 100644
--- a/src/version.c
+++ b/src/version.c
@@ -734,6 +734,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    311,
 /**/
     310,
 /**/

-- 
-- 
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/E1w9k1j-006syV-7W%40256bit.org.

Raspunde prin e-mail lui