comparison 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
comparison
equal deleted inserted replaced
29815:9941dc321348 29816:bbe62ea78aac
275 } 275 }
276 } 276 }
277 } 277 }
278 #endif 278 #endif
279 279
280 #ifdef FEAT_PROP_POPUP 280 #if defined(FEAT_PROP_POPUP) || defined(PROTO)
281 static textprop_T *current_text_props = NULL;
282 static buf_T *current_buf = NULL;
283
284 /* 281 /*
285 * Function passed to qsort() to sort text properties. 282 * Take care of padding, right-align and truncation of virtual text after a
286 * Return 1 if "s1" has priority over "s2", -1 if the other way around, zero if 283 * line.
287 * both have the same priority. 284 * if "n_attr" is not NULL then "n_extra" and "p_extra" are adjusted for any
285 * padding, right-align and truncation. Otherwise only the size is computed.
286 * When "n_attr" is NULL returns the number of screen cells used.
287 * Otherwise returns TRUE when drawing continues on the next line.
288 */ 288 */
289 static int 289 int
290 text_prop_compare(const void *s1, const void *s2) 290 text_prop_position(
291 win_T *wp,
292 textprop_T *tp,
293 int vcol, // current screen column
294 int *n_extra, // nr of bytes for virtual text
295 char_u **p_extra, // virtual text
296 int *n_attr, // attribute cells, NULL if not used
297 int *n_attr_skip) // cells to skip attr, NULL if not used
291 { 298 {
292 int idx1, idx2; 299 int right = (tp->tp_flags & TP_FLAG_ALIGN_RIGHT);
293 textprop_T *tp1, *tp2; 300 int below = (tp->tp_flags & TP_FLAG_ALIGN_BELOW);
294 proptype_T *pt1, *pt2; 301 int wrap = (tp->tp_flags & TP_FLAG_WRAP);
295 colnr_T col1, col2; 302 int padding = tp->tp_col == MAXCOL && tp->tp_len > 1
296 303 ? tp->tp_len - 1 : 0;
297 idx1 = *(int *)s1; 304 int col_with_padding = vcol + (below ? 0 : padding);
298 idx2 = *(int *)s2; 305 int room = wp->w_width - col_with_padding;
299 tp1 = &current_text_props[idx1]; 306 int added = room;
300 tp2 = &current_text_props[idx2]; 307 int n_used = *n_extra;
301 col1 = tp1->tp_col; 308 char_u *l = NULL;
302 col2 = tp2->tp_col; 309 int strsize = vim_strsize(*p_extra);
303 if (col1 == MAXCOL && col2 == MAXCOL) 310 int cells = wrap ? strsize
311 : textprop_size_after_trunc(wp, below, added, *p_extra, &n_used);
312
313 if (wrap || right || below || padding > 0 || n_used < *n_extra)
304 { 314 {
305 int flags1 = 0; 315 // Right-align: fill with spaces
306 int flags2 = 0; 316 if (right)
307 317 added -= cells;
308 // both props add text are after the line, order on 0: after (default), 318 if (added < 0
309 // 1: right, 2: below (comes last) 319 || !(right || below)
310 if (tp1->tp_flags & TP_FLAG_ALIGN_RIGHT) 320 || (below
311 flags1 = 1; 321 ? (col_with_padding == 0 || !wp->w_p_wrap)
312 if (tp1->tp_flags & TP_FLAG_ALIGN_BELOW) 322 : (n_used < *n_extra)))
313 flags1 = 2; 323 {
314 if (tp2->tp_flags & TP_FLAG_ALIGN_RIGHT) 324 if (right && (wrap || room < PROP_TEXT_MIN_CELLS))
315 flags2 = 1; 325 {
316 if (tp2->tp_flags & TP_FLAG_ALIGN_BELOW) 326 // right-align on next line instead of wrapping if possible
317 flags2 = 2; 327 added = wp->w_width - strsize + room;
318 if (flags1 != flags2) 328 if (added < 0)
319 return flags1 < flags2 ? 1 : -1; 329 added = 0;
330 else
331 n_used = *n_extra;
332 }
333 else
334 added = 0;
335 }
336
337 // With 'nowrap' add one to show the "extends" character if needed (it
338 // doesn't show if the text just fits).
339 if (!wp->w_p_wrap
340 && n_used < *n_extra
341 && wp->w_lcs_chars.ext != NUL
342 && wp->w_p_list)
343 ++n_used;
344
345 // add 1 for NUL, 2 for when '…' is used
346 if (n_attr != NULL)
347 l = alloc(n_used + added + padding + 3);
348 if (n_attr == NULL || l != NULL)
349 {
350 int off = 0;
351
352 if (n_attr != NULL)
353 {
354 vim_memset(l, ' ', added);
355 off += added;
356 if (padding > 0)
357 {
358 vim_memset(l + off, ' ', padding);
359 off += padding;
360 }
361 vim_strncpy(l + off, *p_extra, n_used);
362 off += n_used;
363 }
364 else
365 {
366 off = added + padding + n_used;
367 cells += added + padding;
368 }
369 if (n_attr != NULL)
370 {
371 if (n_used < *n_extra && wp->w_p_wrap)
372 {
373 char_u *lp = l + off - 1;
374
375 if (has_mbyte)
376 {
377 // change last character to '…'
378 lp -= (*mb_head_off)(l, lp);
379 STRCPY(lp, "…");
380 n_used = lp - l + 3 - padding;
381 }
382 else
383 // change last character to '>'
384 *lp = '>';
385 }
386 *p_extra = l;
387 *n_extra = n_used + added + padding;
388 *n_attr = mb_charlen(*p_extra);
389 *n_attr_skip = added + padding;
390 }
391 }
320 } 392 }
321 393
322 // property that inserts text has priority over one that doesn't 394 if (n_attr == NULL)
323 if ((tp1->tp_id < 0) != (tp2->tp_id < 0)) 395 return cells;
324 return tp1->tp_id < 0 ? 1 : -1; 396 return (below && col_with_padding > win_col_off(wp) && !wp->w_p_wrap);
325
326 // check highest priority, defined by the type
327 pt1 = text_prop_type_by_id(current_buf, tp1->tp_type);
328 pt2 = text_prop_type_by_id(current_buf, tp2->tp_type);
329 if (pt1 != pt2)
330 {
331 if (pt1 == NULL)
332 return -1;
333 if (pt2 == NULL)
334 return 1;
335 if (pt1->pt_priority != pt2->pt_priority)
336 return pt1->pt_priority > pt2->pt_priority ? 1 : -1;
337 }
338
339 // same priority, one that starts first wins
340 if (col1 != col2)
341 return col1 < col2 ? 1 : -1;
342
343 // for a property with text the id can be used as tie breaker
344 if (tp1->tp_id < 0)
345 return tp1->tp_id > tp2->tp_id ? 1 : -1;
346
347 return 0;
348 } 397 }
349 #endif 398 #endif
350 399
351 /* 400 /*
352 * Called when finished with the line: draw the screen line and handle any 401 * Called when finished with the line: draw the screen line and handle any
1217 mch_memmove(text_props, prop_start, 1266 mch_memmove(text_props, prop_start,
1218 text_prop_count * sizeof(textprop_T)); 1267 text_prop_count * sizeof(textprop_T));
1219 1268
1220 // Allocate an array for the indexes. 1269 // Allocate an array for the indexes.
1221 text_prop_idxs = ALLOC_MULT(int, text_prop_count); 1270 text_prop_idxs = ALLOC_MULT(int, text_prop_count);
1271 if (text_prop_idxs == NULL)
1272 VIM_CLEAR(text_props);
1273
1222 area_highlighting = TRUE; 1274 area_highlighting = TRUE;
1223 extra_check = TRUE; 1275 extra_check = TRUE;
1224 } 1276 }
1225 } 1277 }
1226 #endif 1278 #endif
1607 // Check if any active property ends. 1659 // Check if any active property ends.
1608 for (pi = 0; pi < text_props_active; ++pi) 1660 for (pi = 0; pi < text_props_active; ++pi)
1609 { 1661 {
1610 int tpi = text_prop_idxs[pi]; 1662 int tpi = text_prop_idxs[pi];
1611 1663
1612 if (bcol >= text_props[tpi].tp_col - 1 1664 if (text_props[tpi].tp_col != MAXCOL
1613 + text_props[tpi].tp_len) 1665 && bcol >= text_props[tpi].tp_col - 1
1666 + text_props[tpi].tp_len)
1614 { 1667 {
1615 if (pi + 1 < text_props_active) 1668 if (pi + 1 < text_props_active)
1616 mch_memmove(text_prop_idxs + pi, 1669 mch_memmove(text_prop_idxs + pi,
1617 text_prop_idxs + pi + 1, 1670 text_prop_idxs + pi + 1,
1618 sizeof(int) 1671 sizeof(int)
1672 int other_tpi = -1; 1725 int other_tpi = -1;
1673 1726
1674 // Sort the properties on priority and/or starting last. 1727 // Sort the properties on priority and/or starting last.
1675 // Then combine the attributes, highest priority last. 1728 // Then combine the attributes, highest priority last.
1676 text_prop_follows = FALSE; 1729 text_prop_follows = FALSE;
1677 current_text_props = text_props; 1730 sort_text_props(wp->w_buffer, text_props,
1678 current_buf = wp->w_buffer; 1731 text_prop_idxs, text_props_active);
1679 qsort((void *)text_prop_idxs, (size_t)text_props_active,
1680 sizeof(int), text_prop_compare);
1681 1732
1682 for (pi = 0; pi < text_props_active; ++pi) 1733 for (pi = 0; pi < text_props_active; ++pi)
1683 { 1734 {
1684 int tpi = text_prop_idxs[pi]; 1735 int tpi = text_prop_idxs[pi];
1685 proptype_T *pt = text_prop_type_by_id( 1736 proptype_T *pt = text_prop_type_by_id(
1702 } 1753 }
1703 if (text_prop_id < 0 && used_tpi >= 0 1754 if (text_prop_id < 0 && used_tpi >= 0
1704 && -text_prop_id 1755 && -text_prop_id
1705 <= wp->w_buffer->b_textprop_text.ga_len) 1756 <= wp->w_buffer->b_textprop_text.ga_len)
1706 { 1757 {
1707 char_u *p = ((char_u **)wp->w_buffer 1758 textprop_T *tp = &text_props[used_tpi];
1759 char_u *p = ((char_u **)wp->w_buffer
1708 ->b_textprop_text.ga_data)[ 1760 ->b_textprop_text.ga_data)[
1709 -text_prop_id - 1]; 1761 -text_prop_id - 1];
1710 1762
1711 // reset the ID in the copy to avoid it being used 1763 // reset the ID in the copy to avoid it being used
1712 // again 1764 // again
1713 text_props[used_tpi].tp_id = -MAXCOL; 1765 tp->tp_id = -MAXCOL;
1714 1766
1715 if (p != NULL) 1767 if (p != NULL)
1716 { 1768 {
1717 int right = (text_props[used_tpi].tp_flags 1769 int right = (tp->tp_flags
1718 & TP_FLAG_ALIGN_RIGHT); 1770 & TP_FLAG_ALIGN_RIGHT);
1719 int below = (text_props[used_tpi].tp_flags 1771 int below = (tp->tp_flags
1720 & TP_FLAG_ALIGN_BELOW); 1772 & TP_FLAG_ALIGN_BELOW);
1721 int wrap = (text_props[used_tpi].tp_flags 1773 int wrap = (tp->tp_flags & TP_FLAG_WRAP);
1722 & TP_FLAG_WRAP); 1774 int padding = tp->tp_col == MAXCOL
1723 1775 && tp->tp_len > 1
1776 ? tp->tp_len - 1 : 0;
1777
1778 // Insert virtual text before the current
1779 // character, or add after the end of the line.
1724 wlv.p_extra = p; 1780 wlv.p_extra = p;
1725 wlv.c_extra = NUL; 1781 wlv.c_extra = NUL;
1726 wlv.c_final = NUL; 1782 wlv.c_final = NUL;
1727 wlv.n_extra = (int)STRLEN(p); 1783 wlv.n_extra = (int)STRLEN(p);
1728 extra_for_textprop = TRUE; 1784 extra_for_textprop = TRUE;
1744 } 1800 }
1745 #endif 1801 #endif
1746 // Keep in sync with where 1802 // Keep in sync with where
1747 // textprop_size_after_trunc() is called in 1803 // textprop_size_after_trunc() is called in
1748 // win_lbr_chartabsize(). 1804 // win_lbr_chartabsize().
1749 if ((right || below || !wrap) && wp->w_width > 2) 1805 if ((right || below || !wrap || padding > 0)
1806 && wp->w_width > 2)
1750 { 1807 {
1751 int added = wp->w_width - wlv.col; 1808 char_u *prev_p_extra = wlv.p_extra;
1752 int n_used = wlv.n_extra; 1809 int start_line;
1753 char_u *l; 1810
1754 int strsize = wrap 1811 // Take care of padding, right-align and
1755 ? vim_strsize(wlv.p_extra) 1812 // truncation.
1756 : textprop_size_after_trunc(wp, 1813 // Shared with win_lbr_chartabsize(), must do
1757 below, added, wlv.p_extra, &n_used); 1814 // exactly the same.
1758 1815 start_line = text_prop_position(wp, tp,
1759 if (wrap || right || below 1816 wlv.col,
1760 || n_used < wlv.n_extra) 1817 &wlv.n_extra, &wlv.p_extra,
1818 &n_attr, &n_attr_skip);
1819 if (wlv.p_extra != prev_p_extra)
1761 { 1820 {
1762 // Right-align: fill with spaces 1821 // wlv.p_extra was allocated
1763 if (right) 1822 vim_free(p_extra_free2);
1764 added -= strsize; 1823 p_extra_free2 = wlv.p_extra;
1765 if (added < 0
1766 || (below
1767 ? wlv.col == 0 || !wp->w_p_wrap
1768 : n_used < wlv.n_extra))
1769 added = 0;
1770
1771 // With 'nowrap' add one to show the
1772 // "extends" character if needed (it
1773 // doesn't show it the text just fits).
1774 if (!wp->w_p_wrap
1775 && n_used < wlv.n_extra
1776 && wp->w_lcs_chars.ext != NUL
1777 && wp->w_p_list)
1778 ++n_used;
1779
1780 // add 1 for NUL, 2 for when '…' is used
1781 l = alloc(n_used + added + 3);
1782 if (l != NULL)
1783 {
1784 vim_memset(l, ' ', added);
1785 vim_strncpy(l + added, wlv.p_extra,
1786 n_used);
1787 if (n_used < wlv.n_extra
1788 && wp->w_p_wrap)
1789 {
1790 char_u *lp = l + added + n_used - 1;
1791
1792 if (has_mbyte)
1793 {
1794 // change last character to '…'
1795 lp -= (*mb_head_off)(l, lp);
1796 STRCPY(lp, "…");
1797 n_used = lp - l + 3;
1798 }
1799 else
1800 // change last character to '>'
1801 *lp = '>';
1802 }
1803 vim_free(p_extra_free2);
1804 wlv.p_extra = p_extra_free2 = l;
1805 wlv.n_extra = n_used + added;
1806 n_attr_skip = added;
1807 n_attr = mb_charlen(wlv.p_extra);
1808 }
1809 } 1824 }
1810 1825
1811 // When 'wrap' is off then for "below" we need 1826 // When 'wrap' is off then for "below" we need
1812 // to start a new line explictly. 1827 // to start a new line explictly.
1813 if (below && wlv.col > win_col_off(wp) 1828 if (start_line)
1814 && !wp->w_p_wrap)
1815 { 1829 {
1816 draw_screen_line(wp, &wlv); 1830 draw_screen_line(wp, &wlv);
1817 1831
1818 // When line got too long for screen break 1832 // When line got too long for screen break
1819 // here. 1833 // here.