diff src/drawline.c @ 29816:bbe62ea78aac v9.0.0247

patch 9.0.0247: cannot add padding to virtual text without highlight Commit: https://github.com/vim/vim/commit/f396ce83eebf6c61596184231d39ce4d41eeac04 Author: Bram Moolenaar <Bram@vim.org> Date: Tue Aug 23 18:39:37 2022 +0100 patch 9.0.0247: cannot add padding to virtual text without highlight Problem: Cannot add padding to virtual text without highlight. Solution: Add the "text_padding_left" argument. (issue https://github.com/vim/vim/issues/10906)
author Bram Moolenaar <Bram@vim.org>
date Tue, 23 Aug 2022 19:45:05 +0200
parents 3035901eceb7
children e6e0f1c39edb
line wrap: on
line diff
--- a/src/drawline.c
+++ b/src/drawline.c
@@ -277,74 +277,123 @@ get_sign_display_info(
 }
 #endif
 
-#ifdef FEAT_PROP_POPUP
-static textprop_T	*current_text_props = NULL;
-static buf_T		*current_buf = NULL;
-
+#if defined(FEAT_PROP_POPUP) || defined(PROTO)
 /*
- * Function passed to qsort() to sort text properties.
- * Return 1 if "s1" has priority over "s2", -1 if the other way around, zero if
- * both have the same priority.
+ * 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
+ * padding, right-align and truncation.  Otherwise only the size is computed.
+ * When "n_attr" is NULL returns the number of screen cells used.
+ * Otherwise returns TRUE when drawing continues on the next line.
  */
-    static int
-text_prop_compare(const void *s1, const void *s2)
+    int
+text_prop_position(
+	win_T	    *wp,
+	textprop_T  *tp,
+	int	    vcol,	    // current screen column
+	int	    *n_extra,	    // nr of bytes for virtual text
+	char_u	    **p_extra,	    // virtual text
+	int	    *n_attr,	    // attribute cells, NULL if not used
+	int	    *n_attr_skip)   // cells to skip attr, NULL if not used
 {
-    int  idx1, idx2;
-    textprop_T	*tp1, *tp2;
-    proptype_T  *pt1, *pt2;
-    colnr_T col1, col2;
+    int	    right = (tp->tp_flags & TP_FLAG_ALIGN_RIGHT);
+    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
+				  ? tp->tp_len - 1 : 0;
+    int	    col_with_padding = vcol + (below ? 0 : padding);
+    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);
 
-    idx1 = *(int *)s1;
-    idx2 = *(int *)s2;
-    tp1 = &current_text_props[idx1];
-    tp2 = &current_text_props[idx2];
-    col1 = tp1->tp_col;
-    col2 = tp2->tp_col;
-    if (col1 == MAXCOL && col2 == MAXCOL)
+    if (wrap || right || below || padding > 0 || n_used < *n_extra)
     {
-	int flags1 = 0;
-	int flags2 = 0;
+	// 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
+		added = wp->w_width - 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
+	// doesn't show if the text just fits).
+	if (!wp->w_p_wrap
+		&& n_used < *n_extra
+		&& wp->w_lcs_chars.ext != NUL
+		&& wp->w_p_list)
+	    ++n_used;
+
+	// 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;
 
-	// 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;
+	    if (n_attr != NULL)
+	    {
+		vim_memset(l, ' ', added);
+		off += added;
+		if (padding > 0)
+		{
+		    vim_memset(l + off, ' ', padding);
+		    off += padding;
+		}
+		vim_strncpy(l + off, *p_extra, n_used);
+		off += n_used;
+	    }
+	    else
+	    {
+		off = added + padding + n_used;
+		cells += added + padding;
+	    }
+	    if (n_attr != NULL)
+	    {
+		if (n_used < *n_extra && wp->w_p_wrap)
+		{
+		    char_u *lp = l + off - 1;
+
+		    if (has_mbyte)
+		    {
+			// change last character to '…'
+			lp -= (*mb_head_off)(l, lp);
+			STRCPY(lp, "…");
+			n_used = lp - l + 3 - padding;
+		    }
+		    else
+			// 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;
+	    }
+	}
     }
 
-    // property that inserts text has priority over one that doesn't
-    if ((tp1->tp_id < 0) != (tp2->tp_id < 0))
-	return tp1->tp_id < 0 ? 1 : -1;
-
-    // check highest priority, defined by the type
-    pt1 = text_prop_type_by_id(current_buf, tp1->tp_type);
-    pt2 = text_prop_type_by_id(current_buf, tp2->tp_type);
-    if (pt1 != pt2)
-    {
-	if (pt1 == NULL)
-	    return -1;
-	if (pt2 == NULL)
-	    return 1;
-	if (pt1->pt_priority != pt2->pt_priority)
-	    return pt1->pt_priority > pt2->pt_priority ? 1 : -1;
-    }
-
-    // same priority, one that starts first wins
-    if (col1 != col2)
-	return col1 < col2 ? 1 : -1;
-
-    // for a property with text the id can be used as tie breaker
-    if (tp1->tp_id < 0)
-	return tp1->tp_id > tp2->tp_id ? 1 : -1;
-
-    return 0;
+    if (n_attr == NULL)
+	return cells;
+    return (below && col_with_padding > win_col_off(wp) && !wp->w_p_wrap);
 }
 #endif
 
@@ -1219,6 +1268,9 @@ win_line(
 
 	    // Allocate an array for the indexes.
 	    text_prop_idxs = ALLOC_MULT(int, text_prop_count);
+	    if (text_prop_idxs == NULL)
+		VIM_CLEAR(text_props);
+
 	    area_highlighting = TRUE;
 	    extra_check = TRUE;
 	}
@@ -1609,8 +1661,9 @@ win_line(
 		{
 		    int tpi = text_prop_idxs[pi];
 
-		    if (bcol >= text_props[tpi].tp_col - 1
-						  + text_props[tpi].tp_len)
+		    if (text_props[tpi].tp_col != MAXCOL
+			    && bcol >= text_props[tpi].tp_col - 1
+						      + text_props[tpi].tp_len)
 		    {
 			if (pi + 1 < text_props_active)
 			    mch_memmove(text_prop_idxs + pi,
@@ -1674,10 +1727,8 @@ win_line(
 		    // Sort the properties on priority and/or starting last.
 		    // Then combine the attributes, highest priority last.
 		    text_prop_follows = FALSE;
-		    current_text_props = text_props;
-		    current_buf = wp->w_buffer;
-		    qsort((void *)text_prop_idxs, (size_t)text_props_active,
-					       sizeof(int), text_prop_compare);
+		    sort_text_props(wp->w_buffer, text_props,
+					    text_prop_idxs, text_props_active);
 
 		    for (pi = 0; pi < text_props_active; ++pi)
 		    {
@@ -1704,23 +1755,28 @@ win_line(
 			    && -text_prop_id
 				      <= wp->w_buffer->b_textprop_text.ga_len)
 		    {
-			char_u *p = ((char_u **)wp->w_buffer
+			textprop_T  *tp = &text_props[used_tpi];
+			char_u	    *p = ((char_u **)wp->w_buffer
 						   ->b_textprop_text.ga_data)[
 							   -text_prop_id - 1];
 
 			// reset the ID in the copy to avoid it being used
 			// again
-			text_props[used_tpi].tp_id = -MAXCOL;
+			tp->tp_id = -MAXCOL;
 
 			if (p != NULL)
 			{
-			    int	    right = (text_props[used_tpi].tp_flags
+			    int	    right = (tp->tp_flags
 							& TP_FLAG_ALIGN_RIGHT);
-			    int	    below = (text_props[used_tpi].tp_flags
+			    int	    below = (tp->tp_flags
 							& TP_FLAG_ALIGN_BELOW);
-			    int	    wrap = (text_props[used_tpi].tp_flags
-							& TP_FLAG_WRAP);
+			    int	    wrap = (tp->tp_flags & TP_FLAG_WRAP);
+			    int	    padding = tp->tp_col == MAXCOL
+						 && tp->tp_len > 1
+							  ? tp->tp_len - 1 : 0;
 
+			    // Insert virtual text before the current
+			    // character, or add after the end of the line.
 			    wlv.p_extra = p;
 			    wlv.c_extra = NUL;
 			    wlv.c_final = NUL;
@@ -1746,72 +1802,30 @@ win_line(
 			    // Keep in sync with where
 			    // textprop_size_after_trunc() is called in
 			    // win_lbr_chartabsize().
-			    if ((right || below || !wrap) && wp->w_width > 2)
+			    if ((right || below || !wrap || padding > 0)
+							    && wp->w_width > 2)
 			    {
-				int	added = wp->w_width - wlv.col;
-				int	n_used = wlv.n_extra;
-				char_u	*l;
-				int	strsize = wrap
-					  ? vim_strsize(wlv.p_extra)
-					  : textprop_size_after_trunc(wp,
-					   below, added, wlv.p_extra, &n_used);
-
-				if (wrap || right || below
-						       || n_used < wlv.n_extra)
-				{
-				    // Right-align: fill with spaces
-				    if (right)
-					added -= strsize;
-				    if (added < 0
-					    || (below
-						? wlv.col == 0 || !wp->w_p_wrap
-						: n_used < wlv.n_extra))
-					added = 0;
+				char_u	*prev_p_extra = wlv.p_extra;
+				int	start_line;
 
-				    // With 'nowrap' add one to show the
-				    // "extends" character if needed (it
-				    // doesn't show it the text just fits).
-				    if (!wp->w_p_wrap
-					    && n_used < wlv.n_extra
-					    && wp->w_lcs_chars.ext != NUL
-					    && wp->w_p_list)
-					++n_used;
-
-				    // add 1 for NUL, 2 for when '…' is used
-				    l = alloc(n_used + added + 3);
-				    if (l != NULL)
-				    {
-					vim_memset(l, ' ', added);
-					vim_strncpy(l + added, wlv.p_extra,
-								       n_used);
-					if (n_used < wlv.n_extra
-							       && wp->w_p_wrap)
-					{
-					    char_u *lp = l + added + n_used - 1;
-
-					    if (has_mbyte)
-					    {
-						// change last character to '…'
-						lp -= (*mb_head_off)(l, lp);
-						STRCPY(lp, "…");
-						n_used = lp - l + 3;
-					    }
-					    else
-						// change last character to '>'
-						*lp = '>';
-					}
-					vim_free(p_extra_free2);
-					wlv.p_extra = p_extra_free2 = l;
-					wlv.n_extra = n_used + added;
-					n_attr_skip = added;
-					n_attr = mb_charlen(wlv.p_extra);
-				    }
+				// Take care of padding, right-align and
+				// truncation.
+				// Shared with win_lbr_chartabsize(), must do
+				// exactly the same.
+				start_line = text_prop_position(wp, tp,
+						    wlv.col,
+						    &wlv.n_extra, &wlv.p_extra,
+						    &n_attr, &n_attr_skip);
+				if (wlv.p_extra != prev_p_extra)
+				{
+				    // wlv.p_extra was allocated
+				    vim_free(p_extra_free2);
+				    p_extra_free2 = wlv.p_extra;
 				}
 
 				// When 'wrap' is off then for "below" we need
 				// to start a new line explictly.
-				if (below && wlv.col > win_col_off(wp)
-							      && !wp->w_p_wrap)
+				if (start_line)
 				{
 				    draw_screen_line(wp, &wlv);