To: vim_dev@googlegroups.com Subject: Patch 9.0.0247 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.0247 Problem: Cannot add padding to virtual text without highlight. Solution: Add the "text_padding_left" argument. (issue #10906) Files: runtime/doc/textprop.txt, src/textprop.c, src/proto/textprop.pro, src/errors.h, src/structs.h, src/charset.c, src/drawline.c, src/proto/drawline.pro, src/testdir/test_textprop.vim, src/testdir/dumps/Test_prop_with_text_after_wraps_1.dump, src/testdir/dumps/Test_prop_right_align_twice_2.dump, src/testdir/dumps/Test_prop_text_with_padding_1.dump, src/testdir/dumps/Test_prop_text_with_padding_2.dump, src/testdir/dumps/Test_prop_text_with_padding_3.dump *** ../vim-9.0.0246/runtime/doc/textprop.txt 2022-08-20 20:54:44.258968044 +0100 --- runtime/doc/textprop.txt 2022-08-22 21:14:07.824389656 +0100 *************** *** 126,131 **** --- 126,132 ---- If {col} is invalid an error is given. *E964* {props} is a dictionary with these fields: + type name of the text property type length length of text in bytes, can only be used for a property that does not continue in another line; can be zero *************** *** 142,150 **** automatically to a negative number; otherwise zero is used text text to be displayed before {col}, or after the ! line if {col} is zero *E1294* ! text_align when "text" is present and {col} is zero specifies where to display the text: after after the end of the line right right aligned in the window (unless --- 143,152 ---- automatically to a negative number; otherwise zero is used text text to be displayed before {col}, or after the ! line if {col} is zero; prepend and/or append ! spaces for padding with highlighting *E1294* ! text_align when "text" is present and {col} is zero; specifies where to display the text: after after the end of the line right right aligned in the window (unless *************** *** 152,165 **** line) below in the next screen line When omitted "after" is used. Only one ! "right" property can fit in earch line. text_wrap when "text" is present and {col} is zero, specifies what happens if the text doesn't fit: wrap wrap the text to the next line truncate truncate the text to make it fit When omitted "truncate" is used. - type name of the text property type All fields except "type" are optional. It is an error when both "length" and "end_lnum" or "end_col" --- 154,173 ---- line) below in the next screen line When omitted "after" is used. Only one ! "right" property can fit in each line, if ! there are two ore more these will go in a ! separate line (still right aligned). ! text_padding_left *E1296* ! used when "text" is present and {col} is zero; ! padding between the end of the text line ! (leftmost column for "below") and the virtual ! text, not highlighted text_wrap when "text" is present and {col} is zero, specifies what happens if the text doesn't fit: wrap wrap the text to the next line truncate truncate the text to make it fit When omitted "truncate" is used. All fields except "type" are optional. It is an error when both "length" and "end_lnum" or "end_col" *** ../vim-9.0.0246/src/textprop.c 2022-08-20 20:54:44.258968044 +0100 --- src/textprop.c 2022-08-23 12:29:43.304717804 +0100 *************** *** 171,176 **** --- 171,177 ---- char_u *type_name, int id, char_u *text_arg, + int text_padding_left, int text_flags, linenr_T start_lnum, linenr_T end_lnum, *************** *** 264,270 **** --- 265,274 ---- { length = 1; // text is placed on one character if (col == 0) + { col = MAXCOL; // after the line + length += text_padding_left; + } } // Allocate the new line with space for the new property. *************** *** 390,396 **** emsg(_(e_invalid_argument)); return; } ! if (prop_add_one(buf, type_name, id, NULL, 0, start_lnum, end_lnum, start_col, end_col) == FAIL) return; } --- 394,400 ---- emsg(_(e_invalid_argument)); return; } ! if (prop_add_one(buf, type_name, id, NULL, 0, 0, start_lnum, end_lnum, start_col, end_col) == FAIL) return; } *************** *** 428,433 **** --- 432,438 ---- buf_T *buf = default_buf; int id = 0; char_u *text = NULL; + int text_padding_left = 0; int flags = 0; if (dict == NULL || !dict_has_key(dict, "type")) *************** *** 507,515 **** --- 512,531 ---- } } + if (dict_has_key(dict, "text_padding_left")) + { + text_padding_left = dict_get_number(dict, "text_padding_left"); + if (text_padding_left < 0) + { + semsg(_(e_argument_must_be_positive_str), "text_padding_left"); + goto theend; + } + } + if (dict_has_key(dict, "text_wrap")) { char_u *p = dict_get_string(dict, "text_wrap", FALSE); + if (p == NULL) goto theend; if (STRCMP(p, "wrap") == 0) *************** *** 529,534 **** --- 545,555 ---- semsg(_(e_invalid_column_number_nr), (long)start_col); goto theend; } + if (start_col > 0 && text_padding_left > 0) + { + emsg(_(e_can_only_use_left_padding_when_column_is_zero)); + goto theend; + } if (dict_arg != NULL && get_bufnr_from_arg(dict_arg, &buf) == FAIL) goto theend; *************** *** 546,552 **** // correctly set. buf->b_has_textprop = TRUE; // this is never reset ! prop_add_one(buf, type_name, id, text, flags, start_lnum, end_lnum, start_col, end_col); text = NULL; --- 567,573 ---- // correctly set. buf->b_has_textprop = TRUE; // this is never reset ! prop_add_one(buf, type_name, id, text, text_padding_left, flags, start_lnum, end_lnum, start_col, end_col); text = NULL; *************** *** 655,660 **** --- 676,766 ---- return result; } + static textprop_T *text_prop_compare_props; + static buf_T *text_prop_compare_buf; + + /* + * 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. + */ + static int + text_prop_compare(const void *s1, const void *s2) + { + int idx1, idx2; + textprop_T *tp1, *tp2; + proptype_T *pt1, *pt2; + colnr_T col1, col2; + + idx1 = *(int *)s1; + idx2 = *(int *)s2; + tp1 = &text_prop_compare_props[idx1]; + tp2 = &text_prop_compare_props[idx2]; + col1 = tp1->tp_col; + col2 = tp2->tp_col; + if (col1 == MAXCOL && col2 == MAXCOL) + { + int flags1 = 0; + int flags2 = 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; + } + + // 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(text_prop_compare_buf, tp1->tp_type); + pt2 = text_prop_type_by_id(text_prop_compare_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; + } + + /* + * Sort "count" text properties using an array if indexes "idxs" into the list + * of text props "props" for buffer "buf". + */ + void + sort_text_props( + buf_T *buf, + textprop_T *props, + int *idxs, + int count) + { + text_prop_compare_buf = buf; + text_prop_compare_props = props; + qsort((void *)idxs, (size_t)count, sizeof(int), text_prop_compare); + } + /* * Find text property "type_id" in the visible lines of window "wp". * Match "id" when it is > 0. *** ../vim-9.0.0246/src/proto/textprop.pro 2022-08-06 13:47:16.212982332 +0100 --- src/proto/textprop.pro 2022-08-23 12:40:39.964112745 +0100 *************** *** 6,11 **** --- 6,12 ---- int get_text_props(buf_T *buf, linenr_T lnum, char_u **props, int will_change); int prop_count_below(buf_T *buf, linenr_T lnum); int count_props(linenr_T lnum, int only_starting, int last_line); + void sort_text_props(buf_T *buf, textprop_T *props, int *idxs, int count); int find_visible_prop(win_T *wp, int type_id, int id, textprop_T *prop, linenr_T *found_lnum); void add_text_props(linenr_T lnum, textprop_T *text_props, int text_prop_count); proptype_T *text_prop_type_by_id(buf_T *buf, int id); *** ../vim-9.0.0246/src/errors.h 2022-08-20 20:54:44.258968044 +0100 --- src/errors.h 2022-08-22 21:13:51.692394349 +0100 *************** *** 1218,1223 **** --- 1218,1225 ---- INIT(= N_("E486: Pattern not found: %s")); EXTERN char e_argument_must_be_positive[] INIT(= N_("E487: Argument must be positive")); + EXTERN char e_argument_must_be_positive_str[] + INIT(= N_("E487: Argument must be positive: %s")); EXTERN char e_trailing_characters[] INIT(= N_("E488: Trailing characters")); EXTERN char e_trailing_characters_str[] *************** *** 3319,3322 **** --- 3321,3326 ---- #ifdef FEAT_PROP_POPUP EXTERN char e_cannot_specify_both_type_and_types[] INIT(= N_("E1295: Cannot specify both 'type' and 'types'")); + EXTERN char e_can_only_use_left_padding_when_column_is_zero[] + INIT(= N_("E1296: Can only use left padding when column is zero")); #endif *** ../vim-9.0.0246/src/structs.h 2022-08-16 20:23:57.402232730 +0100 --- src/structs.h 2022-08-22 21:10:53.256429108 +0100 *************** *** 800,806 **** typedef struct textprop_S { colnr_T tp_col; // start column (one based, in bytes) ! colnr_T tp_len; // length in bytes int tp_id; // identifier int tp_type; // property type int tp_flags; // TP_FLAG_ values --- 800,807 ---- typedef struct textprop_S { colnr_T tp_col; // start column (one based, in bytes) ! colnr_T tp_len; // length in bytes, when tp_id is negative used ! // for left padding plus one int tp_id; // identifier int tp_type; // property type int tp_flags; // TP_FLAG_ values *** ../vim-9.0.0246/src/charset.c 2022-08-14 19:36:51.503751154 +0100 --- src/charset.c 2022-08-23 17:23:34.958193063 +0100 *************** *** 957,982 **** #ifdef FEAT_PROP_POPUP if (lnum > 0) { ! char_u *prop_start; ! cts->cts_text_prop_count = get_text_props(wp->w_buffer, lnum, ! &prop_start, FALSE); ! if (cts->cts_text_prop_count > 0) { // Make a copy of the properties, so that they are properly ! // aligned. ! cts->cts_text_props = ALLOC_MULT(textprop_T, ! cts->cts_text_prop_count); if (cts->cts_text_props == NULL) cts->cts_text_prop_count = 0; else { ! int i; ! mch_memmove(cts->cts_text_props, prop_start, ! cts->cts_text_prop_count * sizeof(textprop_T)); ! for (i = 0; i < cts->cts_text_prop_count; ++i) ! if (cts->cts_text_props[i].tp_id < 0) { cts->cts_has_prop_with_text = TRUE; break; --- 957,982 ---- #ifdef FEAT_PROP_POPUP if (lnum > 0) { ! char_u *prop_start; ! int count; ! count = get_text_props(wp->w_buffer, lnum, &prop_start, FALSE); ! cts->cts_text_prop_count = count; ! if (count > 0) { // Make a copy of the properties, so that they are properly ! // aligned. Make it twice as long for the sorting below. ! cts->cts_text_props = ALLOC_MULT(textprop_T, count * 2); if (cts->cts_text_props == NULL) cts->cts_text_prop_count = 0; else { ! int i; ! mch_memmove(cts->cts_text_props + count, prop_start, ! count * sizeof(textprop_T)); ! for (i = 0; i < count; ++i) ! if (cts->cts_text_props[i + count].tp_id < 0) { cts->cts_has_prop_with_text = TRUE; break; *************** *** 987,992 **** --- 987,1013 ---- VIM_CLEAR(cts->cts_text_props); cts->cts_text_prop_count = 0; } + else + { + int *text_prop_idxs; + + // Need to sort the array to get any truncation right. + // Do the sorting in the second part of the array, then + // move the sorted props to the first part of the array. + text_prop_idxs = ALLOC_MULT(int, count); + if (text_prop_idxs != NULL) + { + for (i = 0; i < count; ++i) + text_prop_idxs[i] = i + count; + sort_text_props(curbuf, cts->cts_text_props, + text_prop_idxs, count); + // Here we want the reverse order. + for (i = 0; i < count; ++i) + cts->cts_text_props[count - i - 1] = + cts->cts_text_props[text_prop_idxs[i]]; + vim_free(text_prop_idxs); + } + } } } } *************** *** 1159,1164 **** --- 1180,1190 ---- int col = (int)(s - line); garray_T *gap = &wp->w_buffer->b_textprop_text; + // The "$" for 'list' mode will go between the EOL and + // the text prop, account for that. + if (wp->w_p_list && wp->w_lcs_chars.eol != NUL) + ++vcol; + for (i = 0; i < cts->cts_text_prop_count; ++i) { textprop_T *tp = cts->cts_text_props + i; *************** *** 1176,1221 **** if (p != NULL) { ! int cells = vim_strsize(p); if (tp->tp_col == MAXCOL) { ! int below = (tp->tp_flags & TP_FLAG_ALIGN_BELOW); ! int right = (tp->tp_flags & TP_FLAG_ALIGN_RIGHT); ! int wrap = (tp->tp_flags & TP_FLAG_WRAP); ! int len = (int)STRLEN(p); ! int n_used = len; ! ! // The "$" for 'list' mode will go between the EOL and ! // the text prop, account for that. ! if (wp->w_p_list && wp->w_lcs_chars.eol != NUL) ! ++vcol; ! ! // Keep in sync with where textprop_size_after_trunc() ! // is called in win_line(). ! if (!wrap) ! { ! added = wp->w_width - (vcol + size) % wp->w_width; ! cells = textprop_size_after_trunc(wp, ! below, added, p, &n_used); ! } ! if (below) ! cells += wp->w_width - (vcol + size) % wp->w_width; ! else if (right) ! { ! len = wp->w_width - vcol % wp->w_width; ! if (len > cells + size) ! // add the padding for right-alignment ! cells = len - size; ! else if (len == 0) ! // padding to right-align in the next line ! cells += cells > wp->w_width ? 0 ! :wp->w_width - cells; ! } #ifdef FEAT_LINEBREAK no_sbr = TRUE; // don't use 'showbreak' now #endif } cts->cts_cur_text_width += cells; cts->cts_start_incl = tp->tp_flags & TP_FLAG_START_INCL; size += cells; --- 1202,1222 ---- if (p != NULL) { ! int cells; if (tp->tp_col == MAXCOL) { ! int n_extra = (int)STRLEN(p); ! ! cells = text_prop_position(wp, tp, ! (vcol + size) % wp->w_width, ! &n_extra, &p, NULL, NULL); #ifdef FEAT_LINEBREAK no_sbr = TRUE; // don't use 'showbreak' now #endif } + else + cells = vim_strsize(p); cts->cts_cur_text_width += cells; cts->cts_start_incl = tp->tp_flags & TP_FLAG_START_INCL; size += cells; *************** *** 1231,1236 **** --- 1232,1239 ---- if (tp->tp_col != MAXCOL && tp->tp_col - 1 > col) break; } + if (wp->w_p_list && wp->w_lcs_chars.eol != NUL) + --vcol; } # endif *** ../vim-9.0.0246/src/drawline.c 2022-08-22 13:00:13.250390870 +0100 --- src/drawline.c 2022-08-23 18:11:34.424946815 +0100 *************** *** 277,350 **** } #endif ! #ifdef FEAT_PROP_POPUP ! static textprop_T *current_text_props = NULL; ! static buf_T *current_buf = NULL; ! /* ! * 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. */ ! static int ! text_prop_compare(const void *s1, const void *s2) { ! int idx1, idx2; ! textprop_T *tp1, *tp2; ! proptype_T *pt1, *pt2; ! colnr_T col1, col2; ! ! idx1 = *(int *)s1; ! idx2 = *(int *)s2; ! tp1 = ¤t_text_props[idx1]; ! tp2 = ¤t_text_props[idx2]; ! col1 = tp1->tp_col; ! col2 = tp2->tp_col; ! if (col1 == MAXCOL && col2 == MAXCOL) { ! int flags1 = 0; ! int flags2 = 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; ! } ! // 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; } #endif --- 277,399 ---- } #endif ! #if defined(FEAT_PROP_POPUP) || defined(PROTO) /* ! * 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. */ ! 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 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); ! ! if (wrap || right || below || padding > 0 || n_used < *n_extra) { ! // 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; ! ! 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; ! } ! } } ! if (n_attr == NULL) ! return cells; ! return (below && col_with_padding > win_col_off(wp) && !wp->w_p_wrap); } #endif *************** *** 1219,1224 **** --- 1268,1276 ---- // 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,1616 **** { int tpi = text_prop_idxs[pi]; ! if (bcol >= text_props[tpi].tp_col - 1 ! + text_props[tpi].tp_len) { if (pi + 1 < text_props_active) mch_memmove(text_prop_idxs + pi, --- 1661,1669 ---- { int tpi = text_prop_idxs[pi]; ! 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,1683 **** // 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); for (pi = 0; pi < text_props_active; ++pi) { --- 1727,1734 ---- // Sort the properties on priority and/or starting last. // Then combine the attributes, highest priority last. text_prop_follows = FALSE; ! sort_text_props(wp->w_buffer, text_props, ! text_prop_idxs, text_props_active); for (pi = 0; pi < text_props_active; ++pi) { *************** *** 1704,1726 **** && -text_prop_id <= wp->w_buffer->b_textprop_text.ga_len) { ! 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; if (p != NULL) { ! int right = (text_props[used_tpi].tp_flags & TP_FLAG_ALIGN_RIGHT); ! int below = (text_props[used_tpi].tp_flags & TP_FLAG_ALIGN_BELOW); ! int wrap = (text_props[used_tpi].tp_flags ! & TP_FLAG_WRAP); wlv.p_extra = p; wlv.c_extra = NUL; wlv.c_final = NUL; --- 1755,1782 ---- && -text_prop_id <= wp->w_buffer->b_textprop_text.ga_len) { ! 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 ! tp->tp_id = -MAXCOL; if (p != NULL) { ! 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; + // 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,1817 **** // Keep in sync with where // textprop_size_after_trunc() is called in // win_lbr_chartabsize(). ! if ((right || below || !wrap) && 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; ! ! // 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); ! } } // 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) { draw_screen_line(wp, &wlv); --- 1802,1831 ---- // Keep in sync with where // textprop_size_after_trunc() is called in // win_lbr_chartabsize(). ! if ((right || below || !wrap || padding > 0) ! && wp->w_width > 2) { ! char_u *prev_p_extra = wlv.p_extra; ! int start_line; ! // 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 (start_line) { draw_screen_line(wp, &wlv); *** ../vim-9.0.0246/src/proto/drawline.pro 2022-06-27 23:15:01.000000000 +0100 --- src/proto/drawline.pro 2022-08-23 16:14:06.876388454 +0100 *************** *** 1,3 **** --- 1,4 ---- /* drawline.c */ + int text_prop_position(win_T *wp, textprop_T *tp, int vcol, int *n_extra, char_u **p_extra, int *n_attr, int *n_attr_skip); int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int nochange, int number_only); /* vim: set ft=c : */ *** ../vim-9.0.0246/src/testdir/test_textprop.vim 2022-08-20 20:54:44.262968041 +0100 --- src/testdir/test_textprop.vim 2022-08-23 18:32:05.445873249 +0100 *************** *** 3041,3044 **** --- 3041,3094 ---- call delete('XscriptPropsListMode') endfunc + func Test_insert_text_with_padding() + CheckRunVimInTerminal + + let lines =<< trim END + vim9script + setline(1, ['Some text to add virtual text to.', + 'second line', + 'Another line with some text to make the wrap.']) + prop_type_add('theprop', {highlight: 'DiffChange'}) + prop_add(1, 0, { + type: 'theprop', + text: 'after', + text_align: 'after', + text_padding_left: 3, + }) + prop_add(1, 0, { + type: 'theprop', + text: 'right aligned', + text_align: 'right', + text_padding_left: 5, + }) + prop_add(1, 0, { + type: 'theprop', + text: 'below the line', + text_align: 'below', + text_padding_left: 4, + }) + prop_add(3, 0, { + type: 'theprop', + text: 'rightmost', + text_align: 'right', + text_padding_left: 6, + text_wrap: 'wrap', + }) + END + call writefile(lines, 'XscriptPropsPadded') + let buf = RunVimInTerminal('-S XscriptPropsPadded', #{rows: 8, cols: 60}) + call VerifyScreenDump(buf, 'Test_prop_text_with_padding_1', {}) + + call term_sendkeys(buf, "ggixxxxxxxxxx\") + call term_sendkeys(buf, "3Gix\") + call VerifyScreenDump(buf, 'Test_prop_text_with_padding_2', {}) + + call term_sendkeys(buf, "ggix\") + call VerifyScreenDump(buf, 'Test_prop_text_with_padding_3', {}) + + call StopVimInTerminal(buf) + call delete('XscriptPropsPadded') + endfunc + " vim: shiftwidth=2 sts=2 expandtab *** ../vim-9.0.0246/src/testdir/dumps/Test_prop_with_text_after_wraps_1.dump 2022-08-04 15:03:16.257142097 +0100 --- src/testdir/dumps/Test_prop_with_text_after_wraps_1.dump 2022-08-23 17:54:31.813745056 +0100 *************** *** 1,8 **** |o+0&#ffffff0|n|e| |t|w|o| |t|h|r|e@1| |f|o|u|r| |f|i|v|e| |s|i|x| |s|e|v|e|n| +0&#ffff4012|O|N|E| |a|n|d| |T|W|O| |a|n|d| |T|H|R|E@1| |a|n|d| |F|O|U|R| |a|n|d| |F|I|V|E| +0&#ffffff0@46 - |o|n|e| |t|w|o| |t|h|r|e@1| |f|o|u|r| |f|i|v|e| |s|i|x| |s|e|v|e|n| +0&#ffff4012|o|n|e| |A|N|D| |t|w|o| |A|N|D| |t|h|r|e@1| |A|N|D| - |f|o|u|r| |A|N|D| |f|i|v|e| +0&#ffffff0@46 |o|n|e| |t|w|o| |t|h|r|e@1| |f|o|u|r| |f|i|v|e| |s|i|x| |s|e|v|e|n| @26 | +0&#ffff4012|o|n|e| |A|N|D| |t|w|o| |A|N|D| |t|h|r|e@1| |A|N|D| |f|o|u|r| |A|N|D| |f|i|v|e| |l|e|t|s| |w|r|a|p| |a|f|t|e|r| |s|o|m |e| |m|o|r|e| |t|e|x|t| +0&#ffffff0@48 |c|u|r|s|o|r| >h|e|r|e| @48 --- 1,8 ---- |o+0&#ffffff0|n|e| |t|w|o| |t|h|r|e@1| |f|o|u|r| |f|i|v|e| |s|i|x| |s|e|v|e|n| +0&#ffff4012|O|N|E| |a|n|d| |T|W|O| |a|n|d| |T|H|R|E@1| |a|n|d| |F|O|U|R| |a|n|d| |F|I|V|E| +0&#ffffff0@46 |o|n|e| |t|w|o| |t|h|r|e@1| |f|o|u|r| |f|i|v|e| |s|i|x| |s|e|v|e|n| @26 + @20| +0&#ffff4012|o|n|e| |A|N|D| |t|w|o| |A|N|D| |t|h|r|e@1| |A|N|D| |f|o|u|r| |A|N|D| |f|i|v|e + |o+0&#ffffff0|n|e| |t|w|o| |t|h|r|e@1| |f|o|u|r| |f|i|v|e| |s|i|x| |s|e|v|e|n| @26 | +0&#ffff4012|o|n|e| |A|N|D| |t|w|o| |A|N|D| |t|h|r|e@1| |A|N|D| |f|o|u|r| |A|N|D| |f|i|v|e| |l|e|t|s| |w|r|a|p| |a|f|t|e|r| |s|o|m |e| |m|o|r|e| |t|e|x|t| +0&#ffffff0@48 |c|u|r|s|o|r| >h|e|r|e| @48 *** ../vim-9.0.0246/src/testdir/dumps/Test_prop_right_align_twice_2.dump 2022-08-13 16:40:51.736717691 +0100 --- src/testdir/dumps/Test_prop_right_align_twice_2.dump 2022-08-23 17:59:55.377678988 +0100 *************** *** 1,8 **** ! |s+0&#ffffff0|o|m|e| |m|o|r|e| |t|e|x|t|s|o|m|e| |t|e|x|t| |s|o|m|e| |t|e|x|t| |s|o|m|e| |t|e|x|t| |s|o|m|e| |t|e|x|t| +0&#ffd7ff255|n|o|t|h|i|n|g| |h|e|r|e|S+0#ffffff16#e000002|o|m|e| |e|r@1|o ! |r| +0#0000000#ffffff0@60|A+0#ffffff16#e000002|n|o|t|h|e|r| |e|r@1|o|r |l+0#0000000#ffffff0|i|n|e| |t|w>o| @66 |~+0#4040ff13&| @73 |~| @73 |~| @73 - |~| @73 | +0#0000000&@56|2|,|8| @10|A|l@1| --- 1,8 ---- ! |s+0&#ffffff0|o|m|e| |m|o|r|e| |t|e|x|t|s|o|m|e| |t|e|x|t| |s|o|m|e| |t|e|x|t| |s|o|m|e| |t|e|x|t| |s|o|m|e| |t|e|x|t| +0&#ffd7ff255|n|o|t|h|i|n|g| |h|e|r|e| +0&#ffffff0@8 ! @65|S+0#ffffff16#e000002|o|m|e| |e|r@1|o|r ! | +0#0000000#ffffff0@61|A+0#ffffff16#e000002|n|o|t|h|e|r| |e|r@1|o|r |l+0#0000000#ffffff0|i|n|e| |t|w>o| @66 |~+0#4040ff13&| @73 |~| @73 |~| @73 | +0#0000000&@56|2|,|8| @10|A|l@1| *** ../vim-9.0.0246/src/testdir/dumps/Test_prop_text_with_padding_1.dump 2022-08-23 18:35:13.113377891 +0100 --- src/testdir/dumps/Test_prop_text_with_padding_1.dump 2022-08-23 18:32:12.917852535 +0100 *************** *** 0 **** --- 1,8 ---- + >S+0&#ffffff0|o|m|e| |t|e|x|t| |t|o| |a|d@1| |v|i|r|t|u|a|l| |t|e|x|t| |t|o|.| @2|a+0&#ffd7ff255|f|t|e|r| +0&#ffffff0@5|r+0&#ffd7ff255|i|g|h|t| |a|l|i|g|n|e|d + | +0&#ffffff0@3|b+0&#ffd7ff255|e|l|o|w| |t|h|e| |l|i|n|e| +0&#ffffff0@41 + |s|e|c|o|n|d| |l|i|n|e| @48 + |A|n|o|t|h|e|r| |l|i|n|e| |w|i|t|h| |s|o|m|e| |t|e|x|t| |t|o| |m|a|k|e| |t|h|e| |w|r|a|p|.| @5|r+0&#ffd7ff255|i|g|h|t|m|o|s|t + |~+0#4040ff13#ffffff0| @58 + |~| @58 + |~| @58 + | +0#0000000&@41|1|,|1| @10|A|l@1| *** ../vim-9.0.0246/src/testdir/dumps/Test_prop_text_with_padding_2.dump 2022-08-23 18:35:13.117377881 +0100 --- src/testdir/dumps/Test_prop_text_with_padding_2.dump 2022-08-23 18:32:14.073849343 +0100 *************** *** 0 **** --- 1,8 ---- + |x+0&#ffffff0@9|S|o|m|e| |t|e|x|t| |t|o| |a|d@1| |v|i|r|t|u|a|l| |t|e|x|t| |t|o|.| @2|a+0&#ffd7ff255|f|t|e|r| +0&#ffffff0@4|r+0&#ffd7ff255|i|g|… + | +0&#ffffff0@3|b+0&#ffd7ff255|e|l|o|w| |t|h|e| |l|i|n|e| +0&#ffffff0@41 + |s|e|c|o|n|d| |l|i|n|e| @48 + >x|A|n|o|t|h|e|r| |l|i|n|e| |w|i|t|h| |s|o|m|e| |t|e|x|t| |t|o| |m|a|k|e| |t|h|e| |w|r|a|p|.| @13 + @51|r+0&#ffd7ff255|i|g|h|t|m|o|s|t + |~+0#4040ff13#ffffff0| @58 + |~| @58 + | +0#0000000&@41|3|,|1| @10|A|l@1| *** ../vim-9.0.0246/src/testdir/dumps/Test_prop_text_with_padding_3.dump 2022-08-23 18:35:13.121377869 +0100 --- src/testdir/dumps/Test_prop_text_with_padding_3.dump 2022-08-23 18:32:15.229846149 +0100 *************** *** 0 **** --- 1,8 ---- + >x+0&#ffffff0@10|S|o|m|e| |t|e|x|t| |t|o| |a|d@1| |v|i|r|t|u|a|l| |t|e|x|t| |t|o|.| @2|a+0&#ffd7ff255|f|t|e|r| +0&#ffffff0@7 + @47|r+0&#ffd7ff255|i|g|h|t| |a|l|i|g|n|e|d + | +0&#ffffff0@3|b+0&#ffd7ff255|e|l|o|w| |t|h|e| |l|i|n|e| +0&#ffffff0@41 + |s|e|c|o|n|d| |l|i|n|e| @48 + |x|A|n|o|t|h|e|r| |l|i|n|e| |w|i|t|h| |s|o|m|e| |t|e|x|t| |t|o| |m|a|k|e| |t|h|e| |w|r|a|p|.| @13 + @51|r+0&#ffd7ff255|i|g|h|t|m|o|s|t + |~+0#4040ff13#ffffff0| @58 + | +0#0000000&@41|1|,|1| @10|A|l@1| *** ../vim-9.0.0246/src/version.c 2022-08-22 16:35:41.698700428 +0100 --- src/version.c 2022-08-22 21:04:56.048359742 +0100 *************** *** 733,734 **** --- 733,736 ---- { /* Add new patch number below this line */ + /**/ + 247, /**/ -- An indication you must be a manager: You believe you never have any problems in your life, just "issues" and "improvement opportunities". /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///