To: vim_dev@googlegroups.com Subject: Patch 9.0.0067 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.0067 Problem: Cannot show virtual text. Solution: Initial changes for virtual text support, using text properties. Files: runtime/doc/textprop.txt, src/beval.c, src/charset.c, src/drawline.c, src/edit.c, src/errors.h, src/evalfunc.c, src/getchar.c, src/indent.c, src/misc1.c, src/misc2.c, src/mouse.c, src/ops.c, src/popupwin.c, src/proto/charset.pro, src/proto/textprop.pro, src/regexp.c, src/regexp_bt.c, src/regexp_nfa.c, src/register.c, src/structs.h, src/textprop.c, src/testdir/test_textprop.vim, src/testdir/dumps/Test_prop_inserts_text.dump *** ../vim-9.0.0066/runtime/doc/textprop.txt 2022-06-28 11:21:06.000000000 +0100 --- runtime/doc/textprop.txt 2022-07-25 15:42:47.103101003 +0100 *************** *** 137,143 **** bufnr buffer to add the property to; when omitted the current buffer is used id user defined ID for the property; must be a ! number; when omitted zero is used type name of the text property type All fields except "type" are optional. --- 137,147 ---- bufnr buffer to add the property to; when omitted the current buffer is used id user defined ID for the property; must be a ! number, should be positive; when using "text" ! then "id" must not be present and will be set ! automatically to a negative number; otherwise ! zero is used ! text text to be displayed at {col} type name of the text property type All fields except "type" are optional. *************** *** 157,162 **** --- 161,177 ---- "type" will first be looked up in the buffer the property is added to. When not found, the global property types are used. If not found an error is given. + *virtual-text* + When "text" is used this text will be displayed at the start + location of the text property. The text of the buffer line + will be shifted to make room. This is called "virtual text". + The text will be displayed but it is not part of the actual + buffer line, the cursor cannot be placed on it. A mouse click + in the text will move the cursor to the first character after + the text. + A negative "id" will be chosen and is returned. Once a + property with "text" has been added for a buffer then using a + negative "id" for any other property will give an error. Can also be used as a |method|: > GetLnum()->prop_add(col, props) *************** *** 181,186 **** --- 196,204 ---- two items {end-lnum} and {end-col} specify the position just after the text. + It is not possible to add a text property with a "text" field + here. + Example: call prop_add_list(#{type: 'MyProp', id: 2}, \ [[1, 4, 1, 7], *** ../vim-9.0.0066/src/beval.c 2022-05-27 17:18:23.000000000 +0100 --- src/beval.c 2022-07-25 15:42:47.103101003 +0100 *************** *** 47,53 **** { // Not past end of the file. lbuf = ml_get_buf(wp->w_buffer, lnum, FALSE); ! if (col <= win_linetabsize(wp, lbuf, (colnr_T)MAXCOL)) { // Not past end of line. if (getword) --- 47,53 ---- { // Not past end of the file. lbuf = ml_get_buf(wp->w_buffer, lnum, FALSE); ! if (col <= win_linetabsize(wp, lnum, lbuf, (colnr_T)MAXCOL)) { // Not past end of line. if (getword) *** ../vim-9.0.0066/src/charset.c 2022-05-22 19:57:32.000000000 +0100 --- src/charset.c 2022-07-25 16:54:09.730345955 +0100 *************** *** 12,19 **** #if defined(HAVE_WCHAR_H) # include // for towupper() and towlower() #endif - static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp); static unsigned nr2hex(unsigned c); static int chartab_initialized = FALSE; --- 12,19 ---- #if defined(HAVE_WCHAR_H) # include // for towupper() and towlower() #endif + static int win_nolbr_chartabsize(chartabsize_T *cts, int *headp); static unsigned nr2hex(unsigned c); static int chartab_initialized = FALSE; *************** *** 737,744 **** #endif /* ! * Return the number of characters the string 's' will take on the screen, * taking into account the size of a tab. */ int linetabsize(char_u *s) --- 737,745 ---- #endif /* ! * Return the number of characters the string "s" will take on the screen, * taking into account the size of a tab. + * Does not handle text properties, since "s" is not a buffer line. */ int linetabsize(char_u *s) *************** *** 747,778 **** } /* ! * Like linetabsize(), but starting at column "startcol". */ int linetabsize_col(int startcol, char_u *s) { ! colnr_T col = startcol; ! char_u *line = s; // pointer to start of line, for breakindent ! while (*s != NUL) ! col += lbr_chartabsize_adv(line, &s, col); ! return (int)col; } /* * Like linetabsize(), but for a given window instead of the current one. */ int ! win_linetabsize(win_T *wp, char_u *line, colnr_T len) { ! colnr_T col = 0; ! char_u *s; ! for (s = line; *s != NUL && (len == MAXCOL || s < line + len); ! MB_PTR_ADV(s)) ! col += win_lbr_chartabsize(wp, line, s, col, NULL); ! return (int)col; } /* --- 748,781 ---- } /* ! * Like linetabsize(), but "s" starts at column "startcol". */ int linetabsize_col(int startcol, char_u *s) { ! chartabsize_T cts; ! init_chartabsize_arg(&cts, curwin, 0, startcol, s, s); ! while (*cts.cts_ptr != NUL) ! cts.cts_vcol += lbr_chartabsize_adv(&cts); ! clear_chartabsize_arg(&cts); ! return (int)cts.cts_vcol; } /* * Like linetabsize(), but for a given window instead of the current one. */ int ! win_linetabsize(win_T *wp, linenr_T lnum, char_u *line, colnr_T len) { ! chartabsize_T cts; ! init_chartabsize_arg(&cts, wp, lnum, 0, line, line); ! for ( ; *cts.cts_ptr != NUL && (len == MAXCOL || cts.cts_ptr < line + len); ! MB_PTR_ADV(cts.cts_ptr)) ! cts.cts_vcol += win_lbr_chartabsize(&cts, NULL); ! clear_chartabsize_arg(&cts); ! return (int)cts.cts_vcol; } /* *************** *** 893,917 **** } /* ! * like chartabsize(), but also check for line breaks on the screen */ int ! lbr_chartabsize( ! char_u *line UNUSED, // start of the line ! unsigned char *s, ! colnr_T col) { ! #ifdef FEAT_LINEBREAK ! if (!curwin->w_p_lbr && *get_showbreak_value(curwin) == NUL ! && !curwin->w_p_bri) { #endif if (curwin->w_p_wrap) ! return win_nolbr_chartabsize(curwin, s, col, NULL); ! RET_WIN_BUF_CHARTABSIZE(curwin, curbuf, s, col) ! #ifdef FEAT_LINEBREAK } ! return win_lbr_chartabsize(curwin, line == NULL ? s : line, s, col, NULL); #endif } --- 896,996 ---- } /* ! * Prepare the structure passed to chartabsize functions. ! * "line" is the start of the line, "ptr" is the first relevant character. ! * When "lnum" is zero do not use text properties that insert text. ! */ ! void ! init_chartabsize_arg( ! chartabsize_T *cts, ! win_T *wp, ! linenr_T lnum, ! colnr_T col, ! char_u *line, ! char_u *ptr) ! { ! cts->cts_win = wp; ! cts->cts_lnum = lnum; ! cts->cts_vcol = col; ! cts->cts_line = line; ! cts->cts_ptr = ptr; ! #ifdef FEAT_PROP_POPUP ! cts->cts_text_prop_count = 0; ! cts->cts_has_prop_with_text = FALSE; ! cts->cts_cur_text_width = 0; ! 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; ! } ! if (!cts->cts_has_prop_with_text) ! { ! // won't use the text properties, free them ! vim_free(cts->cts_text_props); ! cts->cts_text_prop_count = 0; ! } ! } ! } ! } ! #endif ! } ! ! /* ! * Free any allocated item in "cts". ! */ ! void ! clear_chartabsize_arg(chartabsize_T *cts) ! { ! if (cts->cts_text_prop_count > 0) ! vim_free(cts->cts_text_props); ! } ! ! /* ! * Like chartabsize(), but also check for line breaks on the screen and text ! * properties that insert text. */ int ! lbr_chartabsize(chartabsize_T *cts) { ! #if defined(FEAT_LINEBREAK) || defined(FEAT_PROP_POPUP) ! if (1 ! # ifdef FEAT_LINEBREAK ! && !curwin->w_p_lbr && *get_showbreak_value(curwin) == NUL ! && !curwin->w_p_bri ! # endif ! # ifdef FEAT_PROP_POPUP ! && !cts->cts_has_prop_with_text ! #endif ! ) { #endif if (curwin->w_p_wrap) ! return win_nolbr_chartabsize(cts, NULL); ! RET_WIN_BUF_CHARTABSIZE(curwin, curbuf, cts->cts_ptr, cts->cts_vcol) ! #if defined(FEAT_LINEBREAK) || defined(FEAT_PROP_POPUP) } ! return win_lbr_chartabsize(cts, NULL); #endif } *************** *** 919,937 **** * Call lbr_chartabsize() and advance the pointer. */ int ! lbr_chartabsize_adv( ! char_u *line, // start of the line ! char_u **s, ! colnr_T col) { int retval; ! retval = lbr_chartabsize(line, *s, col); ! MB_PTR_ADV(*s); return retval; } /* * This function is used very often, keep it fast!!!! * * If "headp" not NULL, set *headp to the size of what we for 'showbreak' --- 998,1016 ---- * Call lbr_chartabsize() and advance the pointer. */ int ! lbr_chartabsize_adv(chartabsize_T *cts) { int retval; ! retval = lbr_chartabsize(cts); ! MB_PTR_ADV(cts->cts_ptr); return retval; } /* + * Return the screen size of the character indicated by "cts". + * "cts->cts_cur_text_width" is set to the extra size for a text property that + * inserts text. * This function is used very often, keep it fast!!!! * * If "headp" not NULL, set *headp to the size of what we for 'showbreak' *************** *** 940,956 **** */ int win_lbr_chartabsize( ! win_T *wp, ! char_u *line UNUSED, // start of the line ! char_u *s, ! colnr_T col, ! int *headp UNUSED) { #ifdef FEAT_LINEBREAK int c; int size; colnr_T col2; ! colnr_T col_adj = 0; // col + screen size of tab colnr_T colmax; int added; int mb_added = 0; --- 1019,1036 ---- */ int win_lbr_chartabsize( ! chartabsize_T *cts, ! int *headp UNUSED) { + win_T *wp = cts->cts_win; + char_u *line = cts->cts_line; // start of the line + char_u *s = cts->cts_ptr; + colnr_T vcol = cts->cts_vcol; #ifdef FEAT_LINEBREAK int c; int size; colnr_T col2; ! colnr_T col_adj = 0; // vcol + screen size of tab colnr_T colmax; int added; int mb_added = 0; *************** *** 959,981 **** int tab_corr = (*s == TAB); int n; char_u *sbr; /* ! * No 'linebreak', 'showbreak' and 'breakindent': return quickly. */ ! if (!wp->w_p_lbr && !wp->w_p_bri && *get_showbreak_value(wp) == NUL) #endif { if (wp->w_p_wrap) ! return win_nolbr_chartabsize(wp, s, col, headp); ! RET_WIN_BUF_CHARTABSIZE(wp, wp->w_buffer, s, col) } ! #ifdef FEAT_LINEBREAK /* ! * First get normal size, without 'linebreak' */ ! size = win_chartabsize(wp, s, col); c = *s; if (tab_corr) col_adj = size - 1; --- 1039,1104 ---- int tab_corr = (*s == TAB); int n; char_u *sbr; + #endif + + #if defined(FEAT_PROP_POPUP) + cts->cts_cur_text_width = 0; + #endif + #if defined(FEAT_LINEBREAK) || defined(FEAT_PROP_POPUP) /* ! * No 'linebreak', 'showbreak', 'breakindent' and text properties that ! * insert text: return quickly. */ ! if (1 ! # ifdef FEAT_LINEBREAK ! && !wp->w_p_lbr && !wp->w_p_bri && *get_showbreak_value(wp) == NUL ! # endif ! # ifdef FEAT_PROP_POPUP ! && !cts->cts_has_prop_with_text ! # endif ! ) #endif { if (wp->w_p_wrap) ! return win_nolbr_chartabsize(cts, headp); ! RET_WIN_BUF_CHARTABSIZE(wp, wp->w_buffer, s, vcol) } ! #if defined(FEAT_LINEBREAK) || defined(FEAT_PROP_POPUP) /* ! * First get the normal size, without 'linebreak' or text properties */ ! size = win_chartabsize(wp, s, vcol); ! ! # ifdef FEAT_PROP_POPUP ! if (cts->cts_has_prop_with_text) ! { ! int i; ! int col = (int)(s - line); ! ! for (i = 0; i < cts->cts_text_prop_count; ++i) ! { ! textprop_T *tp = cts->cts_text_props + i; ! ! if (tp->tp_id < 0 ! && tp->tp_col - 1 >= col && tp->tp_col - 1 < col + size ! && -tp->tp_id <= wp->w_buffer->b_textprop_text.ga_len) ! { ! char_u *p = ((char_u **)wp->w_buffer->b_textprop_text.ga_data)[ ! -tp->tp_id - 1]; ! // TODO: count screen cells ! cts->cts_cur_text_width = STRLEN(p); ! size += cts->cts_cur_text_width; ! break; ! } ! if (tp->tp_col - 1 > col) ! break; ! } ! } ! # endif ! ! # ifdef FEAT_LINEBREAK c = *s; if (tab_corr) col_adj = size - 1; *************** *** 995,1008 **** * non-blank after a blank. */ numberextra = win_col_off(wp); ! col2 = col; colmax = (colnr_T)(wp->w_width - numberextra - col_adj); ! if (col >= colmax) { colmax += col_adj; n = colmax + win_col_off2(wp); if (n > 0) ! colmax += (((col - colmax) / n) + 1) * n - col_adj; } for (;;) --- 1118,1131 ---- * non-blank after a blank. */ numberextra = win_col_off(wp); ! col2 = vcol; colmax = (colnr_T)(wp->w_width - numberextra - col_adj); ! if (vcol >= colmax) { colmax += col_adj; n = colmax + win_col_off2(wp); if (n > 0) ! colmax += (((vcol - colmax) / n) + 1) * n - col_adj; } for (;;) *************** *** 1013,1031 **** if (!(c != NUL && (VIM_ISBREAK(c) || (!VIM_ISBREAK(c) ! && (col2 == col || !VIM_ISBREAK((int)*ps)))))) break; col2 += win_chartabsize(wp, s, col2); if (col2 >= colmax) // doesn't fit { ! size = colmax - col + col_adj; break; } } } else if (has_mbyte && size == 2 && MB_BYTE2LEN(*s) > 1 ! && wp->w_p_wrap && in_win_border(wp, col)) { ++size; // Count the ">" in the last column. mb_added = 1; --- 1136,1154 ---- if (!(c != NUL && (VIM_ISBREAK(c) || (!VIM_ISBREAK(c) ! && (col2 == vcol || !VIM_ISBREAK((int)*ps)))))) break; col2 += win_chartabsize(wp, s, col2); if (col2 >= colmax) // doesn't fit { ! size = colmax - vcol + col_adj; break; } } } else if (has_mbyte && size == 2 && MB_BYTE2LEN(*s) > 1 ! && wp->w_p_wrap && in_win_border(wp, vcol)) { ++size; // Count the ">" in the last column. mb_added = 1; *************** *** 1039,1071 **** */ added = 0; sbr = c == NUL ? empty_option : get_showbreak_value(wp); ! if ((*sbr != NUL || wp->w_p_bri) && wp->w_p_wrap && col != 0) { colnr_T sbrlen = 0; int numberwidth = win_col_off(wp); numberextra = numberwidth; ! col += numberextra + mb_added; ! if (col >= (colnr_T)wp->w_width) { ! col -= wp->w_width; numberextra = wp->w_width - (numberextra - win_col_off2(wp)); ! if (col >= numberextra && numberextra > 0) ! col %= numberextra; if (*sbr != NUL) { sbrlen = (colnr_T)MB_CHARLEN(sbr); ! if (col >= sbrlen) ! col -= sbrlen; } ! if (col >= numberextra && numberextra > 0) ! col = col % numberextra; ! else if (col > 0 && numberextra > 0) ! col += numberwidth - win_col_off2(wp); numberwidth -= win_col_off2(wp); } ! if (col == 0 || col + size + sbrlen > (colnr_T)wp->w_width) { added = 0; if (*sbr != NUL) --- 1162,1194 ---- */ added = 0; sbr = c == NUL ? empty_option : get_showbreak_value(wp); ! if ((*sbr != NUL || wp->w_p_bri) && wp->w_p_wrap && vcol != 0) { colnr_T sbrlen = 0; int numberwidth = win_col_off(wp); numberextra = numberwidth; ! vcol += numberextra + mb_added; ! if (vcol >= (colnr_T)wp->w_width) { ! vcol -= wp->w_width; numberextra = wp->w_width - (numberextra - win_col_off2(wp)); ! if (vcol >= numberextra && numberextra > 0) ! vcol %= numberextra; if (*sbr != NUL) { sbrlen = (colnr_T)MB_CHARLEN(sbr); ! if (vcol >= sbrlen) ! vcol -= sbrlen; } ! if (vcol >= numberextra && numberextra > 0) ! vcol = vcol % numberextra; ! else if (vcol > 0 && numberextra > 0) ! vcol += numberwidth - win_col_off2(wp); numberwidth -= win_col_off2(wp); } ! if (vcol == 0 || vcol + size + sbrlen > (colnr_T)wp->w_width) { added = 0; if (*sbr != NUL) *************** *** 1074,1081 **** { // calculate effective window width int width = (colnr_T)wp->w_width - sbrlen - numberwidth; ! int prev_width = col ! ? ((colnr_T)wp->w_width - (sbrlen + col)) : 0; if (width <= 0) width = (colnr_T)1; --- 1197,1204 ---- { // calculate effective window width int width = (colnr_T)wp->w_width - sbrlen - numberwidth; ! int prev_width = vcol ! ? ((colnr_T)wp->w_width - (sbrlen + vcol)) : 0; if (width <= 0) width = (colnr_T)1; *************** *** 1091,1118 **** added += get_breakindent_win(wp, line); size += added; ! if (col != 0) added = 0; } } if (headp != NULL) *headp = added + mb_added; return size; #endif } /* ! * Like win_lbr_chartabsize(), except that we know 'linebreak' is off and ! * 'wrap' is on. This means we need to check for a double-byte character that ! * doesn't fit at the end of the screen line. */ static int win_nolbr_chartabsize( ! win_T *wp, ! char_u *s, ! colnr_T col, ! int *headp) { int n; if (*s == TAB && (!wp->w_p_list || wp->w_lcs_chars.tab1)) --- 1214,1245 ---- added += get_breakindent_win(wp, line); size += added; ! if (vcol != 0) added = 0; } } if (headp != NULL) *headp = added + mb_added; return size; + # endif #endif } /* ! * Like win_lbr_chartabsize(), except that we know 'linebreak' is off, 'wrap' ! * is on and there are no properties that insert text. This means we need to ! * check for a double-byte character that doesn't fit at the end of the screen ! * line. ! * Only uses "cts_win", "cts_ptr" and "cts_vcol" from "cts". */ static int win_nolbr_chartabsize( ! chartabsize_T *cts, ! int *headp) { + win_T *wp = cts->cts_win; + char_u *s = cts->cts_ptr; + colnr_T col = cts->cts_vcol; int n; if (*s == TAB && (!wp->w_p_list || wp->w_lcs_chars.tab1)) *************** *** 1187,1192 **** --- 1314,1320 ---- #endif int ts = wp->w_buffer->b_p_ts; int c; + chartabsize_T cts; vcol = 0; line = ptr = ml_get_buf(wp->w_buffer, pos->lnum, FALSE); *************** *** 1209,1224 **** posptr -= (*mb_head_off)(line, posptr); } /* * This function is used very often, do some speed optimizations. * When 'list', 'linebreak', 'showbreak' and 'breakindent' are not set ! * use a simple loop. * Also use this when 'list' is set but tabs take their normal size. */ if ((!wp->w_p_list || wp->w_lcs_chars.tab1 != NUL) #ifdef FEAT_LINEBREAK && !wp->w_p_lbr && *get_showbreak_value(wp) == NUL && !wp->w_p_bri #endif ) { for (;;) --- 1337,1357 ---- posptr -= (*mb_head_off)(line, posptr); } + init_chartabsize_arg(&cts, wp, pos->lnum, 0, line, line); + /* * This function is used very often, do some speed optimizations. * When 'list', 'linebreak', 'showbreak' and 'breakindent' are not set ! * and there are no text properties with "text" use a simple loop. * Also use this when 'list' is set but tabs take their normal size. */ if ((!wp->w_p_list || wp->w_lcs_chars.tab1 != NUL) #ifdef FEAT_LINEBREAK && !wp->w_p_lbr && *get_showbreak_value(wp) == NUL && !wp->w_p_bri #endif + #ifdef FEAT_PROP_POPUP + && !cts.cts_has_prop_with_text + #endif ) { for (;;) *************** *** 1274,1302 **** { for (;;) { ! // A tab gets expanded, depending on the current column head = 0; ! incr = win_lbr_chartabsize(wp, line, ptr, vcol, &head); // make sure we don't go past the end of the line ! if (*ptr == NUL) { incr = 1; // NUL at end of line only takes one column break; } ! if (posptr != NULL && ptr >= posptr) // character at pos->col break; ! vcol += incr; ! MB_PTR_ADV(ptr); } } if (start != NULL) *start = vcol + head; if (end != NULL) *end = vcol + incr - 1; if (cursor != NULL) { if (*ptr == TAB && (State & MODE_NORMAL) && !wp->w_p_list --- 1407,1445 ---- { for (;;) { ! // A tab gets expanded, depending on the current column. ! // Other things also take up space. head = 0; ! incr = win_lbr_chartabsize(&cts, &head); // make sure we don't go past the end of the line ! if (*cts.cts_ptr == NUL) { incr = 1; // NUL at end of line only takes one column break; } ! if (posptr != NULL && cts.cts_ptr >= posptr) ! // character at pos->col break; ! cts.cts_vcol += incr; ! MB_PTR_ADV(cts.cts_ptr); } + vcol = cts.cts_vcol; + ptr = cts.cts_ptr; } + clear_chartabsize_arg(&cts); + if (start != NULL) *start = vcol + head; if (end != NULL) *end = vcol + incr - 1; if (cursor != NULL) { + #ifdef FEAT_PROP_POPUP + // cursor is after inserted text + vcol += cts.cts_cur_text_width; + #endif if (*ptr == TAB && (State & MODE_NORMAL) && !wp->w_p_list *** ../vim-9.0.0066/src/drawline.c 2022-07-09 04:56:12.522528981 +0100 --- src/drawline.c 2022-07-25 16:10:32.731049859 +0100 *************** *** 326,331 **** --- 326,332 ---- int text_props_active = 0; proptype_T *text_prop_type = NULL; int text_prop_attr = 0; + int text_prop_id = 0; // active property ID int text_prop_combine = FALSE; #endif #ifdef FEAT_SPELL *************** *** 816,830 **** v = wp->w_leftcol; if (v > 0 && !number_only) { ! char_u *prev_ptr = ptr; ! ! while (vcol < v && *ptr != NUL) ! { ! c = win_lbr_chartabsize(wp, line, ptr, (colnr_T)vcol, NULL); ! vcol += c; ! prev_ptr = ptr; ! MB_PTR_ADV(ptr); ! } // When: // - 'cuc' is set, or --- 817,837 ---- v = wp->w_leftcol; if (v > 0 && !number_only) { ! char_u *prev_ptr = ptr; ! chartabsize_T cts; ! int charsize; ! ! init_chartabsize_arg(&cts, wp, lnum, vcol, line, ptr); ! while (cts.cts_vcol < v && *cts.cts_ptr != NUL) ! { ! charsize = win_lbr_chartabsize(&cts, NULL); ! cts.cts_vcol += charsize; ! prev_ptr = cts.cts_ptr; ! MB_PTR_ADV(cts.cts_ptr); ! } ! vcol = cts.cts_vcol; ! ptr = cts.cts_ptr; ! clear_chartabsize_arg(&cts); // When: // - 'cuc' is set, or *************** *** 844,854 **** // that character but skip the first few screen characters. if (vcol > v) { ! vcol -= c; ptr = prev_ptr; // If the character fits on the screen, don't need to skip it. // Except for a TAB. ! if (( (*mb_ptr2cells)(ptr) >= c || *ptr == TAB) && col == 0) n_skip = v - vcol; } --- 851,861 ---- // that character but skip the first few screen characters. if (vcol > v) { ! vcol -= charsize; ptr = prev_ptr; // If the character fits on the screen, don't need to skip it. // Except for a TAB. ! if (( (*mb_ptr2cells)(ptr) >= charsize || *ptr == TAB) && col == 0) n_skip = v - vcol; } *************** *** 1476,1483 **** --- 1483,1494 ---- text_prop_attr = 0; text_prop_combine = FALSE; text_prop_type = NULL; + text_prop_id = 0; if (text_props_active > 0) { + int used_tpi; + int used_attr = 0; + // Sort the properties on priority and/or starting last. // Then combine the attributes, highest priority last. current_text_props = text_props; *************** *** 1491,1505 **** proptype_T *pt = text_prop_type_by_id( wp->w_buffer, text_props[tpi].tp_type); ! if (pt != NULL && pt->pt_hl_id > 0) { ! int pt_attr = syn_id2attr(pt->pt_hl_id); ! text_prop_type = pt; text_prop_attr = ! hl_combine_attr(text_prop_attr, pt_attr); text_prop_combine = pt->pt_flags & PT_FLAG_COMBINE; } } } } --- 1502,1544 ---- proptype_T *pt = text_prop_type_by_id( wp->w_buffer, text_props[tpi].tp_type); ! if (pt != NULL && pt->pt_hl_id > 0 ! && text_props[tpi].tp_id != -MAXCOL) { ! used_attr = syn_id2attr(pt->pt_hl_id); text_prop_type = pt; text_prop_attr = ! hl_combine_attr(text_prop_attr, used_attr); text_prop_combine = pt->pt_flags & PT_FLAG_COMBINE; + text_prop_id = text_props[tpi].tp_id; + used_tpi = tpi; + } + } + if (n_extra == 0 && text_prop_id < 0 + && -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]; + if (p != NULL) + { + p_extra = p; + n_extra = STRLEN(p); + extra_attr = used_attr; + n_attr = n_extra; + text_prop_attr = 0; + + // If the cursor is on or after this position, + // move it forward. + if (wp == curwin + && lnum == curwin->w_cursor.lnum + && curwin->w_cursor.col >= vcol) + curwin->w_cursor.col += n_extra; } + // reset the ID in the copy to avoid it being used + // again + text_props[used_tpi].tp_id = -MAXCOL; } } } *************** *** 2025,2034 **** int mb_off = has_mbyte ? (*mb_head_off)(line, ptr - 1) : 0; char_u *p = ptr - (mb_off + 1); ! // TODO: is passing p for start of the line OK? ! n_extra = win_lbr_chartabsize(wp, line, p, (colnr_T)vcol, ! NULL) - 1; // We have just drawn the showbreak value, no need to add // space for it again. --- 2064,2073 ---- int mb_off = has_mbyte ? (*mb_head_off)(line, ptr - 1) : 0; char_u *p = ptr - (mb_off + 1); + chartabsize_T cts; ! init_chartabsize_arg(&cts, wp, lnum, vcol, line, p); ! n_extra = win_lbr_chartabsize(&cts, NULL) - 1; // We have just drawn the showbreak value, no need to add // space for it again. *************** *** 2069,2074 **** --- 2108,2114 ---- if (!wp->w_p_list) c = ' '; } + clear_chartabsize_arg(&cts); } #endif *** ../vim-9.0.0066/src/edit.c 2022-06-30 22:13:56.204846337 +0100 --- src/edit.c 2022-07-25 16:17:12.986522771 +0100 *************** *** 4905,4910 **** --- 4905,4912 ---- colnr_T want_vcol, vcol; int change_col = -1; int save_list = curwin->w_p_list; + char_u *tab = (char_u *)"\t"; + chartabsize_T cts; /* * Get the current line. For MODE_VREPLACE state, don't make real *************** *** 4950,4961 **** getvcol(curwin, &fpos, &vcol, NULL, NULL); getvcol(curwin, cursor, &want_vcol, NULL, NULL); // Use as many TABs as possible. Beware of 'breakindent', 'showbreak' // and 'linebreak' adding extra virtual columns. while (VIM_ISWHITE(*ptr)) { ! i = lbr_chartabsize(NULL, (char_u *)"\t", vcol); ! if (vcol + i > want_vcol) break; if (*ptr != TAB) { --- 4952,4965 ---- getvcol(curwin, &fpos, &vcol, NULL, NULL); getvcol(curwin, cursor, &want_vcol, NULL, NULL); + init_chartabsize_arg(&cts, curwin, 0, vcol, tab, tab); + // Use as many TABs as possible. Beware of 'breakindent', 'showbreak' // and 'linebreak' adding extra virtual columns. while (VIM_ISWHITE(*ptr)) { ! i = lbr_chartabsize(&cts); ! if (cts.cts_vcol + i > want_vcol) break; if (*ptr != TAB) { *************** *** 4970,4990 **** } ++fpos.col; ++ptr; ! vcol += i; } if (change_col >= 0) { ! int repl_off = 0; ! char_u *line = ptr; // Skip over the spaces we need. ! while (vcol < want_vcol && *ptr == ' ') { ! vcol += lbr_chartabsize(line, ptr, vcol); ! ++ptr; ++repl_off; } if (vcol > want_vcol) { // Must have a char with 'showbreak' just before it. --- 4974,5000 ---- } ++fpos.col; ++ptr; ! cts.cts_vcol += i; } + vcol = cts.cts_vcol; + clear_chartabsize_arg(&cts); if (change_col >= 0) { ! int repl_off = 0; // Skip over the spaces we need. ! init_chartabsize_arg(&cts, curwin, 0, vcol, ptr, ptr); ! while (cts.cts_vcol < want_vcol && *cts.cts_ptr == ' ') { ! cts.cts_vcol += lbr_chartabsize(&cts); ! ++cts.cts_ptr; ++repl_off; } + ptr = cts.cts_ptr; + vcol = cts.cts_vcol; + clear_chartabsize_arg(&cts); + if (vcol > want_vcol) { // Must have a char with 'showbreak' just before it. *************** *** 5220,5229 **** int ins_copychar(linenr_T lnum) { ! int c; ! int temp; ! char_u *ptr, *prev_ptr; ! char_u *line; if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) { --- 5230,5239 ---- int ins_copychar(linenr_T lnum) { ! int c; ! char_u *ptr, *prev_ptr; ! char_u *line; ! chartabsize_T cts; if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) { *************** *** 5233,5248 **** // try to advance to the cursor column validate_virtcol(); ! temp = 0; ! line = ptr = ml_get(lnum); ! prev_ptr = ptr; ! while ((colnr_T)temp < curwin->w_virtcol && *ptr != NUL) { ! prev_ptr = ptr; ! temp += lbr_chartabsize_adv(line, &ptr, (colnr_T)temp); } ! if ((colnr_T)temp > curwin->w_virtcol) ptr = prev_ptr; c = (*mb_ptr2char)(ptr); if (c == NUL) --- 5243,5261 ---- // try to advance to the cursor column validate_virtcol(); ! line = ml_get(lnum); ! prev_ptr = line; ! init_chartabsize_arg(&cts, curwin, lnum, 0, line, line); ! while (cts.cts_vcol < curwin->w_virtcol && *cts.cts_ptr != NUL) { ! prev_ptr = cts.cts_ptr; ! cts.cts_vcol += lbr_chartabsize_adv(&cts); } ! if (cts.cts_vcol > curwin->w_virtcol) ptr = prev_ptr; + else + ptr = cts.cts_ptr; + clear_chartabsize_arg(&cts); c = (*mb_ptr2char)(ptr); if (c == NUL) *** ../vim-9.0.0066/src/errors.h 2022-07-24 20:07:57.656416981 +0100 --- src/errors.h 2022-07-25 15:42:47.107100992 +0100 *************** *** 3310,3312 **** --- 3310,3316 ---- EXTERN char e_cmdline_window_already_open[] INIT(= N_("E1292: Command-line window is already open")); #endif + #ifdef FEAT_PROP_POPUP + EXTERN char e_cannot_use_negative_id_after_adding_textprop_with_text[] + INIT(= N_("E1291: Cannot use a negative id after adding a textprop with text")); + #endif *** ../vim-9.0.0066/src/evalfunc.c 2022-07-23 09:52:00.333814262 +0100 --- src/evalfunc.c 2022-07-25 15:42:47.107100992 +0100 *************** *** 2218,2224 **** {"prompt_setprompt", 2, 2, FEARG_1, arg2_buffer_string, ret_void, JOB_FUNC(f_prompt_setprompt)}, {"prop_add", 3, 3, FEARG_1, arg3_number_number_dict, ! ret_void, PROP_FUNC(f_prop_add)}, {"prop_add_list", 2, 2, FEARG_1, arg2_dict_any_list_any, ret_void, PROP_FUNC(f_prop_add_list)}, {"prop_clear", 1, 3, FEARG_1, arg3_number_number_dict, --- 2218,2224 ---- {"prompt_setprompt", 2, 2, FEARG_1, arg2_buffer_string, ret_void, JOB_FUNC(f_prompt_setprompt)}, {"prop_add", 3, 3, FEARG_1, arg3_number_number_dict, ! ret_number, PROP_FUNC(f_prop_add)}, {"prop_add_list", 2, 2, FEARG_1, arg2_dict_any_list_any, ret_void, PROP_FUNC(f_prop_add_list)}, {"prop_clear", 1, 3, FEARG_1, arg3_number_number_dict, *** ../vim-9.0.0066/src/getchar.c 2022-07-23 06:24:56.405106035 +0100 --- src/getchar.c 2022-07-25 16:51:55.042379009 +0100 *************** *** 3210,3216 **** && (c = inchar(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len, 3, 25L)) == 0) { ! colnr_T col = 0, vcol; char_u *ptr; if (mode_displayed) --- 3210,3216 ---- && (c = inchar(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len, 3, 25L)) == 0) { ! colnr_T col = 0; char_u *ptr; if (mode_displayed) *************** *** 3242,3265 **** { if (did_ai) { /* * We are expecting to truncate the trailing * white-space, so find the last non-white * character -- webb */ ! col = vcol = curwin->w_wcol = 0; ptr = ml_get_curline(); ! while (col < curwin->w_cursor.col) { ! if (!VIM_ISWHITE(ptr[col])) ! curwin->w_wcol = vcol; ! vcol += lbr_chartabsize(ptr, ptr + col, ! vcol); if (has_mbyte) ! col += (*mb_ptr2len)(ptr + col); else ! ++col; } curwin->w_wrow = curwin->w_cline_row + curwin->w_wcol / curwin->w_width; curwin->w_wcol %= curwin->w_width; --- 3242,3271 ---- { if (did_ai) { + chartabsize_T cts; + /* * We are expecting to truncate the trailing * white-space, so find the last non-white * character -- webb */ ! curwin->w_wcol = 0; ptr = ml_get_curline(); ! init_chartabsize_arg(&cts, curwin, ! curwin->w_cursor.lnum, 0, ptr, ptr); ! while (cts.cts_ptr < ptr + curwin->w_cursor.col) { ! if (!VIM_ISWHITE(*cts.cts_ptr)) ! curwin->w_wcol = cts.cts_vcol; ! cts.cts_vcol += lbr_chartabsize(&cts); if (has_mbyte) ! cts.cts_ptr += ! (*mb_ptr2len)(cts.cts_ptr); else ! ++cts.cts_ptr; } + clear_chartabsize_arg(&cts); + curwin->w_wrow = curwin->w_cline_row + curwin->w_wcol / curwin->w_width; curwin->w_wcol %= curwin->w_width; *** ../vim-9.0.0066/src/indent.c 2022-07-01 13:15:31.556075437 +0100 --- src/indent.c 2022-07-25 17:49:44.798632658 +0100 *************** *** 1350,1375 **** new_cursor_col = curwin->w_cursor.col; else { // Compute the screen column where the cursor should be. vcol = get_indent() - vcol; curwin->w_virtcol = (colnr_T)((vcol < 0) ? 0 : vcol); // Advance the cursor until we reach the right screen column. ! vcol = last_vcol = 0; ! new_cursor_col = -1; ptr = ml_get_curline(); ! while (vcol <= (int)curwin->w_virtcol) { ! last_vcol = vcol; ! if (has_mbyte && new_cursor_col >= 0) ! new_cursor_col += (*mb_ptr2len)(ptr + new_cursor_col); ! else ! ++new_cursor_col; ! if (ptr[new_cursor_col] == NUL) break; ! vcol += lbr_chartabsize(ptr, ptr + new_cursor_col, (colnr_T)vcol); } vcol = last_vcol; // May need to insert spaces to be able to position the cursor on // the right screen column. --- 1350,1377 ---- new_cursor_col = curwin->w_cursor.col; else { + chartabsize_T cts; + // Compute the screen column where the cursor should be. vcol = get_indent() - vcol; curwin->w_virtcol = (colnr_T)((vcol < 0) ? 0 : vcol); // Advance the cursor until we reach the right screen column. ! last_vcol = 0; ptr = ml_get_curline(); ! init_chartabsize_arg(&cts, curwin, 0, 0, ptr, ptr); ! while (cts.cts_vcol <= (int)curwin->w_virtcol) { ! last_vcol = cts.cts_vcol; ! if (cts.cts_vcol > 0) ! MB_PTR_ADV(cts.cts_ptr); ! if (*cts.cts_ptr == NUL) break; ! cts.cts_vcol += lbr_chartabsize(&cts); } vcol = last_vcol; + new_cursor_col = cts.cts_ptr - cts.cts_line; + clear_chartabsize_arg(&cts); // May need to insert spaces to be able to position the cursor on // the right screen column. *************** *** 2064,2077 **** amount = 2; else { ! char_u *line = that; ! amount = 0; ! while (*that && col) { ! amount += lbr_chartabsize_adv(line, &that, (colnr_T)amount); col--; } // Some keywords require "body" indenting rules (the // non-standard-lisp ones are Scheme special forms): --- 2066,2083 ---- amount = 2; else { ! char_u *line = that; ! chartabsize_T cts; ! init_chartabsize_arg(&cts, curwin, pos->lnum, 0, line, line); ! while (*cts.cts_ptr != NUL && col > 0) { ! cts.cts_vcol += lbr_chartabsize_adv(&cts); col--; } + amount = cts.cts_vcol; + that = cts.cts_ptr; + clear_chartabsize_arg(&cts); // Some keywords require "body" indenting rules (the // non-standard-lisp ones are Scheme special forms): *************** *** 2091,2101 **** } firsttry = amount; ! while (VIM_ISWHITE(*that)) { ! amount += lbr_chartabsize(line, that, (colnr_T)amount); ! ++that; } if (*that && *that != ';') // not a comment line { --- 2097,2112 ---- } firsttry = amount; ! init_chartabsize_arg(&cts, curwin, (colnr_T)(that - line), ! amount, line, that); ! while (VIM_ISWHITE(*cts.cts_ptr)) { ! cts.cts_vcol += lbr_chartabsize(&cts); ! ++cts.cts_ptr; } + that = cts.cts_ptr; + amount = cts.cts_vcol; + clear_chartabsize_arg(&cts); if (*that && *that != ';') // not a comment line { *************** *** 2107,2148 **** parencount = 0; quotecount = 0; if (vi_lisp || (*that != '"' && *that != '\'' && *that != '#' && (*that < '0' || *that > '9'))) { ! while (*that ! && (!VIM_ISWHITE(*that) || quotecount || parencount) ! && (!((*that == '(' || *that == '[') && !quotecount && !parencount && vi_lisp))) { ! if (*that == '"') quotecount = !quotecount; ! if ((*that == '(' || *that == '[') && !quotecount) ++parencount; ! if ((*that == ')' || *that == ']') && !quotecount) --parencount; ! if (*that == '\\' && *(that+1) != NUL) ! amount += lbr_chartabsize_adv( ! line, &that, (colnr_T)amount); ! amount += lbr_chartabsize_adv( ! line, &that, (colnr_T)amount); } } ! while (VIM_ISWHITE(*that)) { ! amount += lbr_chartabsize( ! line, that, (colnr_T)amount); ! that++; } if (!*that || *that == ';') amount = firsttry; } --- 2118,2164 ---- parencount = 0; quotecount = 0; + init_chartabsize_arg(&cts, curwin, + (colnr_T)(that - line), amount, line, that); if (vi_lisp || (*that != '"' && *that != '\'' && *that != '#' && (*that < '0' || *that > '9'))) { ! while (*cts.cts_ptr ! && (!VIM_ISWHITE(*cts.cts_ptr) || quotecount || parencount) ! && (!((*cts.cts_ptr == '(' ! || *cts.cts_ptr == '[') && !quotecount && !parencount && vi_lisp))) { ! if (*cts.cts_ptr == '"') quotecount = !quotecount; ! if ((*cts.cts_ptr == '(' || *cts.cts_ptr == '[') && !quotecount) ++parencount; ! if ((*cts.cts_ptr == ')' || *cts.cts_ptr == ']') && !quotecount) --parencount; ! if (*cts.cts_ptr == '\\' ! && *(cts.cts_ptr+1) != NUL) ! cts.cts_vcol += lbr_chartabsize_adv(&cts); ! cts.cts_vcol += lbr_chartabsize_adv(&cts); } } ! while (VIM_ISWHITE(*cts.cts_ptr)) { ! cts.cts_vcol += lbr_chartabsize(&cts); ! ++cts.cts_ptr; } + that = cts.cts_ptr; + amount = cts.cts_vcol; + clear_chartabsize_arg(&cts); + if (!*that || *that == ';') amount = firsttry; } *** ../vim-9.0.0066/src/misc1.c 2022-07-02 17:36:27.332515941 +0100 --- src/misc1.c 2022-07-25 16:31:28.675129729 +0100 *************** *** 397,403 **** s = ml_get_buf(wp->w_buffer, lnum, FALSE); if (*s == NUL) // empty line return 1; ! col = win_linetabsize(wp, s, (colnr_T)MAXCOL); /* * If list mode is on, then the '$' at the end of the line may take up one --- 397,403 ---- s = ml_get_buf(wp->w_buffer, lnum, FALSE); if (*s == NUL) // empty line return 1; ! col = win_linetabsize(wp, lnum, s, (colnr_T)MAXCOL); /* * If list mode is on, then the '$' at the end of the line may take up one *************** *** 427,436 **** plines_win_col(win_T *wp, linenr_T lnum, long column) { long col; - char_u *s; int lines = 0; int width; char_u *line; #ifdef FEAT_DIFF // Check for filler lines above this buffer line. When folded the result --- 427,436 ---- plines_win_col(win_T *wp, linenr_T lnum, long column) { long col; int lines = 0; int width; char_u *line; + chartabsize_T cts; #ifdef FEAT_DIFF // Check for filler lines above this buffer line. When folded the result *************** *** 444,468 **** if (wp->w_width == 0) return lines + 1; ! line = s = ml_get_buf(wp->w_buffer, lnum, FALSE); ! col = 0; ! while (*s != NUL && --column >= 0) { ! col += win_lbr_chartabsize(wp, line, s, (colnr_T)col, NULL); ! MB_PTR_ADV(s); } /* ! * If *s is a TAB, and the TAB is not displayed as ^I, and we're not in ! * MODE_INSERT state, then col must be adjusted so that it represents the ! * last screen position of the TAB. This only fixes an error when the TAB ! * wraps from one screen line to the next (when 'columns' is not a multiple ! * of 'ts') -- webb. */ ! if (*s == TAB && (State & MODE_NORMAL) && (!wp->w_p_list || wp->w_lcs_chars.tab1)) ! col += win_lbr_chartabsize(wp, line, s, (colnr_T)col, NULL) - 1; /* * Add column offset for 'number', 'relativenumber', 'foldcolumn', etc. --- 444,470 ---- if (wp->w_width == 0) return lines + 1; ! line = ml_get_buf(wp->w_buffer, lnum, FALSE); ! init_chartabsize_arg(&cts, wp, lnum, 0, line, line); ! while (*cts.cts_ptr != NUL && --column >= 0) { ! cts.cts_vcol += win_lbr_chartabsize(&cts, NULL); ! MB_PTR_ADV(cts.cts_ptr); } /* ! * If *cts.cts_ptr is a TAB, and the TAB is not displayed as ^I, and we're ! * not in MODE_INSERT state, then col must be adjusted so that it ! * represents the last screen position of the TAB. This only fixes an ! * error when the TAB wraps from one screen line to the next (when ! * 'columns' is not a multiple of 'ts') -- webb. */ ! col = cts.cts_vcol; ! if (*cts.cts_ptr == TAB && (State & MODE_NORMAL) && (!wp->w_p_list || wp->w_lcs_chars.tab1)) ! col += win_lbr_chartabsize(&cts, NULL) - 1; ! clear_chartabsize_arg(&cts); /* * Add column offset for 'number', 'relativenumber', 'foldcolumn', etc. *** ../vim-9.0.0066/src/misc2.c 2022-05-16 19:32:27.000000000 +0100 --- src/misc2.c 2022-07-25 16:52:25.602379723 +0100 *************** *** 128,134 **** { colnr_T wcol = wcol_arg; int idx; - char_u *ptr; char_u *line; colnr_T col = 0; int csize = 0; --- 128,133 ---- *************** *** 158,163 **** --- 157,163 ---- else { int width = curwin->w_width - win_col_off(curwin); + chartabsize_T cts; if (finetune && curwin->w_p_wrap *************** *** 180,198 **** } } ! ptr = line; ! while (col <= wcol && *ptr != NUL) { // Count a tab for what it's worth (if list mode not on) #ifdef FEAT_LINEBREAK ! csize = win_lbr_chartabsize(curwin, line, ptr, col, &head); ! MB_PTR_ADV(ptr); #else ! csize = lbr_chartabsize_adv(line, &ptr, col); #endif ! col += csize; } ! idx = (int)(ptr - line); /* * Handle all the special cases. The virtual_active() check * is needed to ensure that a virtual position off the end of --- 180,201 ---- } } ! init_chartabsize_arg(&cts, curwin, pos->lnum, 0, line, line); ! while (cts.cts_vcol <= wcol && *cts.cts_ptr != NUL) { // Count a tab for what it's worth (if list mode not on) #ifdef FEAT_LINEBREAK ! csize = win_lbr_chartabsize(&cts, &head); ! MB_PTR_ADV(cts.cts_ptr); #else ! csize = lbr_chartabsize_adv(&cts); #endif ! cts.cts_vcol += csize; } ! col = cts.cts_vcol; ! idx = (int)(cts.cts_ptr - line); ! clear_chartabsize_arg(&cts); ! /* * Handle all the special cases. The virtual_active() check * is needed to ensure that a virtual position off the end of *** ../vim-9.0.0066/src/mouse.c 2022-07-09 04:56:12.522528981 +0100 --- src/mouse.c 2022-07-25 15:42:47.107100992 +0100 *************** *** 3101,3118 **** int vcol2col(win_T *wp, linenr_T lnum, int vcol) { ! // try to advance to the specified column ! int count = 0; ! char_u *ptr; ! char_u *line; ! line = ptr = ml_get_buf(wp->w_buffer, lnum, FALSE); ! while (count < vcol && *ptr != NUL) { ! count += win_lbr_chartabsize(wp, line, ptr, count, NULL); ! MB_PTR_ADV(ptr); } ! return (int)(ptr - line); } #endif --- 3101,3120 ---- int vcol2col(win_T *wp, linenr_T lnum, int vcol) { ! char_u *line; ! chartabsize_T cts; ! // try to advance to the specified column ! line = ml_get_buf(wp->w_buffer, lnum, FALSE); ! init_chartabsize_arg(&cts, wp, lnum, 0, line, line); ! while (cts.cts_vcol < vcol && *cts.cts_ptr != NUL) { ! cts.cts_vcol += win_lbr_chartabsize(&cts, NULL); ! MB_PTR_ADV(cts.cts_ptr); } ! clear_chartabsize_arg(&cts); ! ! return (int)(cts.cts_ptr - line); } #endif *** ../vim-9.0.0066/src/ops.c 2022-06-30 22:13:56.204846337 +0100 --- src/ops.c 2022-07-25 17:25:27.982274054 +0100 *************** *** 307,313 **** if (!left) { ! int tabs = 0, spaces = 0; /* * 1. Get start vcol --- 307,314 ---- if (!left) { ! int tabs = 0, spaces = 0; ! chartabsize_T cts; /* * 1. Get start vcol *************** *** 332,344 **** else ++bd.textstart; } ! for ( ; VIM_ISWHITE(*bd.textstart); ) { ! // TODO: is passing bd.textstart for start of the line OK? ! incr = lbr_chartabsize_adv(bd.textstart, &bd.textstart, bd.start_vcol); total += incr; ! bd.start_vcol += incr; } // OK, now total=all the VWS reqd, and textstart points at the 1st // non-ws char in the block. #ifdef FEAT_VARTABS --- 333,352 ---- else ++bd.textstart; } ! ! // TODO: is passing bd.textstart for start of the line OK? ! init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum, ! bd.start_vcol, bd.textstart, bd.textstart); ! for ( ; VIM_ISWHITE(*cts.cts_ptr); ) { ! incr = lbr_chartabsize_adv(&cts); total += incr; ! cts.cts_vcol += incr; } + bd.textstart = cts.cts_ptr; + bd.start_vcol = cts.cts_vcol; + clear_chartabsize_arg(&cts); + // OK, now total=all the VWS reqd, and textstart points at the 1st // non-ws char in the block. #ifdef FEAT_VARTABS *************** *** 381,386 **** --- 389,395 ---- size_t shift_amount; char_u *non_white = bd.textstart; colnr_T non_white_col; + chartabsize_T cts; /* * Firstly, let's find the first non-whitespace character that is *************** *** 399,409 **** // The character's column is in "bd.start_vcol". non_white_col = bd.start_vcol; ! while (VIM_ISWHITE(*non_white)) ! { ! incr = lbr_chartabsize_adv(bd.textstart, &non_white, non_white_col); ! non_white_col += incr; ! } block_space_width = non_white_col - oap->start_vcol; // We will shift by "total" or "block_space_width", whichever is less. --- 408,423 ---- // The character's column is in "bd.start_vcol". non_white_col = bd.start_vcol; ! init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum, ! non_white_col, bd.textstart, non_white); ! while (VIM_ISWHITE(*cts.cts_ptr)) ! { ! incr = lbr_chartabsize_adv(&cts); ! cts.cts_vcol += incr; ! } ! non_white_col = cts.cts_vcol; ! non_white = cts.cts_ptr; ! clear_chartabsize_arg(&cts); block_space_width = non_white_col - oap->start_vcol; // We will shift by "total" or "block_space_width", whichever is less. *************** *** 423,440 **** // column number. if (bd.startspaces) verbatim_copy_width -= bd.start_char_vcols; ! while (verbatim_copy_width < destination_col) { ! char_u *line = verbatim_copy_end; ! ! // TODO: is passing verbatim_copy_end for start of the line OK? ! incr = lbr_chartabsize(line, verbatim_copy_end, ! verbatim_copy_width); ! if (verbatim_copy_width + incr > destination_col) break; ! verbatim_copy_width += incr; ! MB_PTR_ADV(verbatim_copy_end); } // If "destination_col" is different from the width of the initial // part of the line that will be copied, it means we encountered a tab --- 437,455 ---- // column number. if (bd.startspaces) verbatim_copy_width -= bd.start_char_vcols; ! init_chartabsize_arg(&cts, curwin, 0, verbatim_copy_width, ! bd.textstart, verbatim_copy_end); ! while (cts.cts_vcol < destination_col) { ! incr = lbr_chartabsize(&cts); ! if (cts.cts_vcol + incr > destination_col) break; ! cts.cts_vcol += incr; ! MB_PTR_ADV(cts.cts_ptr); } + verbatim_copy_width = cts.cts_vcol; + verbatim_copy_end = cts.cts_ptr; + clear_chartabsize_arg(&cts); // If "destination_col" is different from the width of the initial // part of the line that will be copied, it means we encountered a tab *************** *** 703,710 **** * Put deleted text into register 1 and shift number registers if the * delete contains a line break, or when using a specific operator (Vi * compatible) - * Use the register name from before adjust_clip_reg() may have - * changed it. */ if (oap->motion_type == MLINE || oap->line_count > 1 || oap->use_reg_one) --- 718,723 ---- *************** *** 2213,2218 **** --- 2226,2232 ---- char_u *line; char_u *prev_pstart; char_u *prev_pend; + chartabsize_T cts; #ifdef FEAT_LINEBREAK int lbr_saved = curwin->w_p_lbr; *************** *** 2232,2245 **** bdp->start_char_vcols = 0; line = ml_get(lnum); - pstart = line; prev_pstart = line; ! while (bdp->start_vcol < oap->start_vcol && *pstart) { // Count a tab for what it's worth (if list mode not on) ! incr = lbr_chartabsize(line, pstart, bdp->start_vcol); ! bdp->start_vcol += incr; ! if (VIM_ISWHITE(*pstart)) { bdp->pre_whitesp += incr; bdp->pre_whitesp_c++; --- 2246,2259 ---- bdp->start_char_vcols = 0; line = ml_get(lnum); prev_pstart = line; ! init_chartabsize_arg(&cts, curwin, lnum, bdp->start_vcol, line, line); ! while (cts.cts_vcol < oap->start_vcol && *cts.cts_ptr != NUL) { // Count a tab for what it's worth (if list mode not on) ! incr = lbr_chartabsize(&cts); ! cts.cts_vcol += incr; ! if (VIM_ISWHITE(*cts.cts_ptr)) { bdp->pre_whitesp += incr; bdp->pre_whitesp_c++; *************** *** 2249,2257 **** bdp->pre_whitesp = 0; bdp->pre_whitesp_c = 0; } ! prev_pstart = pstart; ! MB_PTR_ADV(pstart); } bdp->start_char_vcols = incr; if (bdp->start_vcol < oap->start_vcol) // line too short { --- 2263,2275 ---- bdp->pre_whitesp = 0; bdp->pre_whitesp_c = 0; } ! prev_pstart = cts.cts_ptr; ! MB_PTR_ADV(cts.cts_ptr); } + bdp->start_vcol = cts.cts_vcol; + pstart = cts.cts_ptr; + clear_chartabsize_arg(&cts); + bdp->start_char_vcols = incr; if (bdp->start_vcol < oap->start_vcol) // line too short { *************** *** 2295,2308 **** } else { prev_pend = pend; ! while (bdp->end_vcol <= oap->end_vcol && *pend != NUL) { ! // Count a tab for what it's worth (if list mode not on) ! prev_pend = pend; ! incr = lbr_chartabsize_adv(line, &pend, bdp->end_vcol); ! bdp->end_vcol += incr; ! } if (bdp->end_vcol <= oap->end_vcol && (!is_del || oap->op_type == OP_APPEND --- 2313,2332 ---- } else { + init_chartabsize_arg(&cts, curwin, lnum, bdp->end_vcol, + line, pend); prev_pend = pend; ! while (cts.cts_vcol <= oap->end_vcol && *cts.cts_ptr != NUL) { ! // count a tab for what it's worth (if list mode not on) ! prev_pend = cts.cts_ptr; ! incr = lbr_chartabsize_adv(&cts); ! cts.cts_vcol += incr; ! } ! bdp->end_vcol = cts.cts_vcol; ! pend = cts.cts_ptr; ! clear_chartabsize_arg(&cts); ! if (bdp->end_vcol <= oap->end_vcol && (!is_del || oap->op_type == OP_APPEND *** ../vim-9.0.0066/src/popupwin.c 2022-07-23 09:52:00.337814264 +0100 --- src/popupwin.c 2022-07-25 15:42:47.111100981 +0100 *************** *** 1371,1377 **** // "margin_width" is added to "len" where it matters. if (wp->w_width < maxwidth) wp->w_width = maxwidth; ! len = win_linetabsize(wp, ml_get_buf(wp->w_buffer, lnum, FALSE), (colnr_T)MAXCOL); wp->w_width = w_width; --- 1371,1377 ---- // "margin_width" is added to "len" where it matters. if (wp->w_width < maxwidth) wp->w_width = maxwidth; ! len = win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum, FALSE), (colnr_T)MAXCOL); wp->w_width = w_width; *** ../vim-9.0.0066/src/proto/charset.pro 2022-06-27 23:14:58.000000000 +0100 --- src/proto/charset.pro 2022-07-25 16:47:56.031024543 +0100 *************** *** 17,23 **** int chartabsize(char_u *p, colnr_T col); int linetabsize(char_u *s); int linetabsize_col(int startcol, char_u *s); ! int win_linetabsize(win_T *wp, char_u *line, colnr_T len); int vim_isIDc(int c); int vim_isNormalIDc(int c); int vim_iswordc(int c); --- 17,23 ---- int chartabsize(char_u *p, colnr_T col); int linetabsize(char_u *s); int linetabsize_col(int startcol, char_u *s); ! int win_linetabsize(win_T *wp, linenr_T lnum, char_u *line, colnr_T len); int vim_isIDc(int c); int vim_isNormalIDc(int c); int vim_iswordc(int c); *************** *** 28,36 **** int vim_isfilec_or_wc(int c); int vim_isprintc(int c); int vim_isprintc_strict(int c); ! int lbr_chartabsize(char_u *line, unsigned char *s, colnr_T col); ! int lbr_chartabsize_adv(char_u *line, char_u **s, colnr_T col); ! int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *headp); void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *end); colnr_T getvcol_nolist(pos_T *posp); void getvvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *end); --- 28,38 ---- int vim_isfilec_or_wc(int c); int vim_isprintc(int c); int vim_isprintc_strict(int c); ! void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum, colnr_T col, char_u *line, char_u *ptr); ! void clear_chartabsize_arg(chartabsize_T *cts); ! int lbr_chartabsize(chartabsize_T *cts); ! int lbr_chartabsize_adv(chartabsize_T *cts); ! int win_lbr_chartabsize(chartabsize_T *cts, int *headp); void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *end); colnr_T getvcol_nolist(pos_T *posp); void getvvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *end); *** ../vim-9.0.0066/src/proto/textprop.pro 2022-06-27 23:15:26.000000000 +0100 --- src/proto/textprop.pro 2022-07-25 16:48:05.562990867 +0100 *************** *** 2,8 **** int find_prop_type_id(char_u *name, buf_T *buf); void f_prop_add(typval_T *argvars, typval_T *rettv); void f_prop_add_list(typval_T *argvars, typval_T *rettv); ! void prop_add_common(linenr_T start_lnum, colnr_T start_col, dict_T *dict, buf_T *default_buf, typval_T *dict_arg); int get_text_props(buf_T *buf, linenr_T lnum, char_u **props, int will_change); int count_props(linenr_T lnum, int only_starting); int find_visible_prop(win_T *wp, int type_id, int id, textprop_T *prop, linenr_T *found_lnum); --- 2,8 ---- int find_prop_type_id(char_u *name, buf_T *buf); void f_prop_add(typval_T *argvars, typval_T *rettv); void f_prop_add_list(typval_T *argvars, typval_T *rettv); ! int prop_add_common(linenr_T start_lnum, colnr_T start_col, dict_T *dict, buf_T *default_buf, typval_T *dict_arg); int get_text_props(buf_T *buf, linenr_T lnum, char_u **props, int will_change); int count_props(linenr_T lnum, int only_starting); int find_visible_prop(win_T *wp, int type_id, int id, textprop_T *prop, linenr_T *found_lnum); *** ../vim-9.0.0066/src/regexp.c 2022-07-07 22:20:28.441941352 +0100 --- src/regexp.c 2022-07-25 15:42:47.111100981 +0100 *************** *** 1303,1309 **** rex.line = reg_getline(rex.lnum); rex.input = rex.line + col; ! cols = win_linetabsize(wp, rex.line, col); if (cols < start || cols > end - (*p_sel == 'e')) return FALSE; } --- 1303,1309 ---- rex.line = reg_getline(rex.lnum); rex.input = rex.line + col; ! cols = win_linetabsize(wp, rex.reg_firstlnum + rex.lnum, rex.line, col); if (cols < start || cols > end - (*p_sel == 'e')) return FALSE; } *** ../vim-9.0.0066/src/regexp_bt.c 2022-06-20 11:20:30.000000000 +0100 --- src/regexp_bt.c 2022-07-25 15:48:54.537761933 +0100 *************** *** 3441,3447 **** case RE_VCOL: if (!re_num_cmp((long_u)win_linetabsize( rex.reg_win == NULL ? curwin : rex.reg_win, ! rex.line, (colnr_T)(rex.input - rex.line)) + 1, scan)) status = RA_NOMATCH; break; --- 3441,3449 ---- case RE_VCOL: if (!re_num_cmp((long_u)win_linetabsize( rex.reg_win == NULL ? curwin : rex.reg_win, ! rex.reg_firstlnum + rex.lnum, ! rex.line, ! (colnr_T)(rex.input - rex.line)) + 1, scan)) status = RA_NOMATCH; break; *** ../vim-9.0.0066/src/regexp_nfa.c 2022-06-20 11:20:50.000000000 +0100 --- src/regexp_nfa.c 2022-07-25 15:49:04.693808384 +0100 *************** *** 6775,6781 **** } if (!result) result = nfa_re_num_cmp(t->state->val, op, ! (long_u)win_linetabsize(wp, rex.line, col) + 1); if (result) { add_here = TRUE; --- 6775,6783 ---- } if (!result) result = nfa_re_num_cmp(t->state->val, op, ! (long_u)win_linetabsize(wp, ! rex.reg_firstlnum + rex.lnum, ! rex.line, col) + 1); if (result) { add_here = TRUE; *** ../vim-9.0.0066/src/register.c 2022-06-30 12:30:13.823485781 +0100 --- src/register.c 2022-07-25 17:06:18.621161796 +0100 *************** *** 1820,1827 **** bd.textcol = 0; for (i = 0; i < y_size; ++i) { ! int spaces = 0; ! char shortline; bd.startspaces = 0; bd.endspaces = 0; --- 1820,1828 ---- bd.textcol = 0; for (i = 0; i < y_size; ++i) { ! int spaces = 0; ! char shortline; ! chartabsize_T cts; bd.startspaces = 0; bd.endspaces = 0; *************** *** 1839,1851 **** // get the old line and advance to the position to insert at oldp = ml_get_curline(); oldlen = (int)STRLEN(oldp); ! for (ptr = oldp; vcol < col && *ptr; ) { // Count a tab for what it's worth (if list mode not on) ! incr = lbr_chartabsize_adv(oldp, &ptr, vcol); ! vcol += incr; } bd.textcol = (colnr_T)(ptr - oldp); shortline = (vcol < col) || (vcol == col && !*ptr) ; --- 1840,1858 ---- // get the old line and advance to the position to insert at oldp = ml_get_curline(); oldlen = (int)STRLEN(oldp); ! init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum, 0, ! oldp, oldp); ! ! while (cts.cts_vcol < col && *cts.cts_ptr != NUL) { // Count a tab for what it's worth (if list mode not on) ! incr = lbr_chartabsize_adv(&cts); ! cts.cts_vcol += incr; } + vcol = cts.cts_vcol; + ptr = cts.cts_ptr; bd.textcol = (colnr_T)(ptr - oldp); + clear_chartabsize_arg(&cts); shortline = (vcol < col) || (vcol == col && !*ptr) ; *************** *** 1876,1883 **** // calculate number of spaces required to fill right side of // block spaces = y_width + 1; for (j = 0; j < yanklen; j++) ! spaces -= lbr_chartabsize(NULL, &y_array[i][j], 0); if (spaces < 0) spaces = 0; } --- 1883,1897 ---- // calculate number of spaces required to fill right side of // block spaces = y_width + 1; + init_chartabsize_arg(&cts, curwin, 0, 0, + y_array[i], y_array[i]); for (j = 0; j < yanklen; j++) ! { ! spaces -= lbr_chartabsize(&cts); ! ++cts.cts_ptr; ! cts.cts_vcol = 0; ! } ! clear_chartabsize_arg(&cts); if (spaces < 0) spaces = 0; } *** ../vim-9.0.0066/src/structs.h 2022-07-04 17:34:06.382292138 +0100 --- src/structs.h 2022-07-25 15:42:47.111100981 +0100 *************** *** 806,813 **** int tp_flags; // TP_FLAG_ values } textprop_T; ! #define TP_FLAG_CONT_NEXT 1 // property continues in next line ! #define TP_FLAG_CONT_PREV 2 // property was continued from prev line /* * Structure defining a property type. --- 806,814 ---- int tp_flags; // TP_FLAG_ values } textprop_T; ! #define TP_FLAG_CONT_NEXT 0x1 // property continues in next line ! #define TP_FLAG_CONT_PREV 0x2 // property was continued from prev line ! #define TP_VIRTUAL 0x4 // virtual text, uses tp_id /* * Structure defining a property type. *************** *** 3074,3079 **** --- 3075,3081 ---- #ifdef FEAT_PROP_POPUP int b_has_textprop; // TRUE when text props were added hashtab_T *b_proptypes; // text property types local to buffer + garray_T b_textprop_text; // stores text for props, index by (-id - 1) #endif #if defined(FEAT_BEVAL) && defined(FEAT_EVAL) *************** *** 4560,4562 **** --- 4562,4579 ---- char_u *str; int score; } fuzmatch_str_T; + + // Argument for lbr_chartabsize(). + typedef struct { + win_T *cts_win; + linenr_T cts_lnum; // zero when not using text properties + char_u *cts_line; // start of the line + char_u *cts_ptr; // current position in line + #ifdef FEAT_PROP_POPUP + int cts_text_prop_count; // number of text props + textprop_T *cts_text_props; // text props (allocated) or NULL + char cts_has_prop_with_text; // TRUE if if a property inserts text + int cts_cur_text_width; // width of current inserted text + #endif + int cts_vcol; // virtual column at current position + } chartabsize_T; *** ../vim-9.0.0066/src/textprop.c 2022-07-23 09:52:00.341814264 +0100 --- src/textprop.c 2022-07-25 16:53:22.590367803 +0100 *************** *** 150,156 **** * prop_add({lnum}, {col}, {props}) */ void ! f_prop_add(typval_T *argvars, typval_T *rettv UNUSED) { linenr_T start_lnum; colnr_T start_col; --- 150,156 ---- * prop_add({lnum}, {col}, {props}) */ void ! f_prop_add(typval_T *argvars, typval_T *rettv) { linenr_T start_lnum; colnr_T start_col; *************** *** 174,193 **** return; } ! prop_add_common(start_lnum, start_col, argvars[2].vval.v_dict, ! curbuf, &argvars[2]); } /* * Attach a text property 'type_name' to the text starting * at [start_lnum, start_col] and ending at [end_lnum, end_col] in ! * the buffer 'buf' and assign identifier 'id'. */ static int prop_add_one( buf_T *buf, char_u *type_name, int id, linenr_T start_lnum, linenr_T end_lnum, colnr_T start_col, --- 174,195 ---- return; } ! rettv->vval.v_number = prop_add_common(start_lnum, start_col, ! argvars[2].vval.v_dict, curbuf, &argvars[2]); } /* * Attach a text property 'type_name' to the text starting * at [start_lnum, start_col] and ending at [end_lnum, end_col] in ! * the buffer "buf" and assign identifier "id". ! * When "text" is not NULL add it to buf->b_textprop_text[-id - 1]. */ static int prop_add_one( buf_T *buf, char_u *type_name, int id, + char_u *text_arg, linenr_T start_lnum, linenr_T end_lnum, colnr_T start_col, *************** *** 202,227 **** char_u *newtext; int i; textprop_T tmp_prop; type = lookup_prop_type(type_name, buf); if (type == NULL) ! return FAIL; if (start_lnum < 1 || start_lnum > buf->b_ml.ml_line_count) { semsg(_(e_invalid_line_number_nr), (long)start_lnum); ! return FAIL; } if (end_lnum < start_lnum || end_lnum > buf->b_ml.ml_line_count) { semsg(_(e_invalid_line_number_nr), (long)end_lnum); ! return FAIL; } if (buf->b_ml.ml_mfp == NULL) { emsg(_(e_cannot_add_text_property_to_unloaded_buffer)); ! return FAIL; } for (lnum = start_lnum; lnum <= end_lnum; ++lnum) --- 204,246 ---- char_u *newtext; int i; textprop_T tmp_prop; + char_u *text = text_arg; + int res = FAIL; type = lookup_prop_type(type_name, buf); if (type == NULL) ! goto theend; if (start_lnum < 1 || start_lnum > buf->b_ml.ml_line_count) { semsg(_(e_invalid_line_number_nr), (long)start_lnum); ! goto theend; } if (end_lnum < start_lnum || end_lnum > buf->b_ml.ml_line_count) { semsg(_(e_invalid_line_number_nr), (long)end_lnum); ! goto theend; } if (buf->b_ml.ml_mfp == NULL) { emsg(_(e_cannot_add_text_property_to_unloaded_buffer)); ! goto theend; ! } ! ! if (text != NULL) ! { ! garray_T *gap = &buf->b_textprop_text; ! ! // double check we got the right ID ! if (-id - 1 != gap->ga_len) ! iemsg("text prop ID mismatch"); ! if (gap->ga_growsize == 0) ! ga_init2(gap, sizeof(char *), 50); ! if (ga_grow(gap, 1) == FAIL) ! goto theend; ! ((char_u **)gap->ga_data)[gap->ga_len++] = text; ! text = NULL; } for (lnum = start_lnum; lnum <= end_lnum; ++lnum) *************** *** 240,246 **** if (col - 1 > (colnr_T)textlen) { semsg(_(e_invalid_column_number_nr), (long)start_col); ! return FAIL; } if (lnum == end_lnum) --- 259,265 ---- if (col - 1 > (colnr_T)textlen) { semsg(_(e_invalid_column_number_nr), (long)start_col); ! goto theend; } if (lnum == end_lnum) *************** *** 255,261 **** // Allocate the new line with space for the new property. newtext = alloc(buf->b_ml.ml_line_len + sizeof(textprop_T)); if (newtext == NULL) ! return FAIL; // Copy the text, including terminating NUL. mch_memmove(newtext, buf->b_ml.ml_line_ptr, textlen); --- 274,280 ---- // Allocate the new line with space for the new property. newtext = alloc(buf->b_ml.ml_line_len + sizeof(textprop_T)); if (newtext == NULL) ! goto theend; // Copy the text, including terminating NUL. mch_memmove(newtext, buf->b_ml.ml_line_ptr, textlen); *************** *** 295,301 **** } changed_lines_buf(buf, start_lnum, end_lnum + 1, 0); ! return OK; } /* --- 314,324 ---- } changed_lines_buf(buf, start_lnum, end_lnum + 1, 0); ! res = OK; ! ! theend: ! vim_free(text); ! return res; } /* *************** *** 367,373 **** emsg(_(e_invalid_argument)); return; } ! if (prop_add_one(buf, type_name, id, start_lnum, end_lnum, start_col, end_col) == FAIL) return; } --- 390,396 ---- emsg(_(e_invalid_argument)); return; } ! if (prop_add_one(buf, type_name, id, NULL, start_lnum, end_lnum, start_col, end_col) == FAIL) return; } *************** *** 376,386 **** } /* * Shared between prop_add() and popup_create(). * "dict_arg" is the function argument of a dict containing "bufnr". * it is NULL for popup_create(). */ ! void prop_add_common( linenr_T start_lnum, colnr_T start_col, --- 399,420 ---- } /* + * Get the next ID to use for a textprop with text in buffer "buf". + */ + static int + get_textprop_id(buf_T *buf) + { + // TODO: recycle deleted entries + return -(buf->b_textprop_text.ga_len + 1); + } + + /* * Shared between prop_add() and popup_create(). * "dict_arg" is the function argument of a dict containing "bufnr". * it is NULL for popup_create(). + * Returns the "id" used for "text" or zero. */ ! int prop_add_common( linenr_T start_lnum, colnr_T start_col, *************** *** 393,403 **** char_u *type_name; buf_T *buf = default_buf; int id = 0; if (dict == NULL || !dict_has_key(dict, "type")) { emsg(_(e_missing_property_type_name)); ! return; } type_name = dict_get_string(dict, "type", FALSE); --- 427,438 ---- char_u *type_name; buf_T *buf = default_buf; int id = 0; + char_u *text = NULL; if (dict == NULL || !dict_has_key(dict, "type")) { emsg(_(e_missing_property_type_name)); ! goto theend; } type_name = dict_get_string(dict, "type", FALSE); *************** *** 407,413 **** if (end_lnum < start_lnum) { semsg(_(e_invalid_value_for_argument_str), "end_lnum"); ! return; } } else --- 442,448 ---- if (end_lnum < start_lnum) { semsg(_(e_invalid_value_for_argument_str), "end_lnum"); ! goto theend; } } else *************** *** 420,426 **** if (length < 0 || end_lnum > start_lnum) { semsg(_(e_invalid_value_for_argument_str), "length"); ! return; } end_col = start_col + length; } --- 455,461 ---- if (length < 0 || end_lnum > start_lnum) { semsg(_(e_invalid_value_for_argument_str), "length"); ! goto theend; } end_col = start_col + length; } *************** *** 430,436 **** if (end_col <= 0) { semsg(_(e_invalid_value_for_argument_str), "end_col"); ! return; } } else if (start_lnum == end_lnum) --- 465,471 ---- if (end_col <= 0) { semsg(_(e_invalid_value_for_argument_str), "end_col"); ! goto theend; } } else if (start_lnum == end_lnum) *************** *** 441,457 **** if (dict_has_key(dict, "id")) id = dict_get_number(dict, "id"); if (dict_arg != NULL && get_bufnr_from_arg(dict_arg, &buf) == FAIL) ! return; // This must be done _before_ we add the property because property changes // trigger buffer (memline) reorganisation, which needs this flag to be // correctly set. buf->b_has_textprop = TRUE; // this is never reset ! prop_add_one(buf, type_name, id, start_lnum, end_lnum, start_col, end_col); redraw_buf_later(buf, VALID); } /* --- 476,515 ---- if (dict_has_key(dict, "id")) id = dict_get_number(dict, "id"); + if (dict_has_key(dict, "text")) + { + text = dict_get_string(dict, "text", TRUE); + if (text == NULL) + goto theend; + // use a default length of 1 to make multiple props show up + end_col = start_col + 1; + } + if (dict_arg != NULL && get_bufnr_from_arg(dict_arg, &buf) == FAIL) ! goto theend; ! ! if (id < 0 && buf->b_textprop_text.ga_len > 0) ! { ! emsg(_(e_cannot_use_negative_id_after_adding_textprop_with_text)); ! goto theend; ! } ! if (text != NULL) ! id = get_textprop_id(buf); // This must be done _before_ we add the property because property changes // trigger buffer (memline) reorganisation, which needs this flag to be // correctly set. buf->b_has_textprop = TRUE; // this is never reset ! prop_add_one(buf, type_name, id, text, ! start_lnum, end_lnum, start_col, end_col); ! text = NULL; redraw_buf_later(buf, VALID); + + theend: + vim_free(text); + return id; } /* *************** *** 954,962 **** if ((prop_types == NULL || prop_type_or_id_in_list(prop_types, prop_types_len, prop.tp_type)) ! && (prop_ids == NULL || ! prop_type_or_id_in_list(prop_ids, prop_ids_len, ! prop.tp_id))) { dict_T *d = dict_alloc(); --- 1012,1020 ---- if ((prop_types == NULL || prop_type_or_id_in_list(prop_types, prop_types_len, prop.tp_type)) ! && (prop_ids == NULL ! || prop_type_or_id_in_list(prop_ids, prop_ids_len, ! prop.tp_id))) { dict_T *d = dict_alloc(); *** ../vim-9.0.0066/src/testdir/test_textprop.vim 2022-05-24 21:22:09.000000000 +0100 --- src/testdir/test_textprop.vim 2022-07-25 18:09:17.168061181 +0100 *************** *** 2187,2190 **** --- 2187,2213 ---- bwipe! endfunc + func Test_prop_inserts_text() + CheckRunVimInTerminal + + " Just a basic check for now + let lines =<< trim END + call setline(1, 'insert some text here and other text there and some more text after wrapping') + call prop_type_add('someprop', #{highlight: 'ErrorMsg'}) + call prop_type_add('otherprop', #{highlight: 'Search'}) + call prop_type_add('moreprop', #{highlight: 'DiffAdd'}) + call prop_add(1, 18, #{type: 'someprop', text: 'SOME '}) + call prop_add(1, 38, #{type: 'otherprop', text: 'OTHER '}) + call prop_add(1, 69, #{type: 'moreprop', text: 'MORE '}) + redraw + normal $ + END + call writefile(lines, 'XscriptPropsWithText') + let buf = RunVimInTerminal('-S XscriptPropsWithText', #{rows: 6, cols: 60}) + call VerifyScreenDump(buf, 'Test_prop_inserts_text', {}) + + call StopVimInTerminal(buf) + call delete('XscriptPropsWithText') + endfunc + " vim: shiftwidth=2 sts=2 expandtab *** ../vim-9.0.0066/src/testdir/dumps/Test_prop_inserts_text.dump 2022-07-25 18:11:47.815151136 +0100 --- src/testdir/dumps/Test_prop_inserts_text.dump 2022-07-25 18:06:47.773105469 +0100 *************** *** 0 **** --- 1,6 ---- + |i+0&#ffffff0|n|s|e|r|t| |s|o|m|e| |t|e|x|t| |S+0#ffffff16#e000002|O|M|E| |h+0#0000000#ffffff0|e|r|e| |a|n|d| |o|t|h|e|r| |t|e|x|t| |O+0&#ffff4012|T|H|E|R| |t+0&#ffffff0|h|e|r|e| |a|n|d| |s|o + |m|e| |m|o|r|e| |t|e|x|t| |a|f|t|e|r| |M+0fd7ff255|O|R|E| |w+0&#ffffff0|r|a|p@1|i|n|g> @27 + |~+0#4040ff13&| @58 + |~| @58 + |~| @58 + | +0#0000000&@41|1|,|7@1|-|9|3| @6|A|l@1| *** ../vim-9.0.0066/src/version.c 2022-07-25 12:28:05.844483996 +0100 --- src/version.c 2022-07-25 18:05:59.213482921 +0100 *************** *** 737,738 **** --- 737,740 ---- { /* Add new patch number below this line */ + /**/ + 67, /**/ -- "A clear conscience is usually the sign of a bad memory." -- Steven Wright /// 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 ///