Patch 9.0.0067

21 views
Skip to first unread message

Bram Moolenaar

unread,
Jul 25, 2022, 1:14:34 PM7/25/22
to vim...@googlegroups.com

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 <wchar.h> // 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 <wchar.h> // 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+0&#5fd7ff255|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 -- Br...@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 ///

Bram Moolenaar

unread,
Jul 25, 2022, 1:44:01 PM7/25/22
to vim...@googlegroups.com

I wrote:

> Patch 9.0.0067
> Problem: Cannot show virtual text.
> Solution: Initial changes for virtual text support, using text properties.

I worked on this while I was offline. It adds just the basic things,
inserting text at a certain text column. I haven't checked what works
and what doesn't, at least it shows up and line wrapping seems to work
OK.

You can try it out a bit, mainly to check the way that the virtual text
is added. Commands that rely on the screen column may not work
correctly. You can mention that, but there are various other things to
work on. It will take a while before all of this works.

Please do check that the refactoring of various "chartabsize" functions
doesn't cause problems, when the new feature isn't used everything
should work as before.

--
hundred-and-one symptoms of being an internet addict:
119. You are reading a book and look for the scroll bar to get to
the next page.

John Marriott

unread,
Jul 25, 2022, 4:08:54 PM7/25/22
to vim...@googlegroups.com

On 26-July-2022 03:14, Bram Moolenaar wrote:
> 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
>
>
After this patch, mingw64 (gcc 12.1.0) gives this warning:
<snip>
gcc -c -I. -Iproto -DWIN32 -DWINVER=0x0603 -D_WIN32_WINNT=0x0603
-DHAVE_PATHDEF -DFEAT_NORMAL -DHAVE_STDINT_H -D__USE_MINGW_ANSI_STDIO
-pipe -march=native -Wall -O3 -fomit-frame-pointer -freg-struct-return
-fpie -fPIE -DFEAT_GUI_MSWIN -DFEAT_CLIPBOARD drawline.c -o
gobjnative/drawline.o
drawline.c:858:16: warning: 'charsize' may be used uninitialized
[-Wmaybe-uninitialized]
  858 |             if (( (*mb_ptr2cells)(ptr) >= charsize || *ptr ==
TAB) && col == 0)
      |                ^
drawline.c:822:25: note: 'charsize' was declared here
  822 |         int             charsize;
      |                         ^~~~~~~~
</snip>

The attached patch tries to fix it.

Cheers
John
drawline.c.9.0.0067.patch

Bram Moolenaar

unread,
Jul 25, 2022, 4:16:23 PM7/25/22
to vim...@googlegroups.com, John Marriott

John Marriott wrote:

> On 26-July-2022 03:14, Bram Moolenaar wrote:
> > Patch 9.0.0067
> > Problem: Cannot show virtual text.
> > Solution: Initial changes for virtual text support, using text properti=
Thanks, I'll include it.

--
Would you care for a drink? I mean, if it were, like,
disabled and you had to look after it?
Reply all
Reply to author
Forward
0 new messages