Problem: Cannot show virtual text.
Solution: Initial changes for virtual text support, using text properties.
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
+ 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.
"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)
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],
{
// Not past end of the file.
lbuf = ml_get_buf(wp->w_buffer, lnum, FALSE);
- if (col <= win_linetabsize(wp, lbuf, (colnr_T)MAXCOL))
+ if (col <= win_linetabsize(wp, lnum, lbuf, (colnr_T)MAXCOL))
{
// Not past end of line.
if (getword)
#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 int win_nolbr_chartabsize(chartabsize_T *cts, int *headp);
static unsigned nr2hex(unsigned c);
static int chartab_initialized = FALSE;
#endif
/*
- * Return the number of characters the string 's' will take on the screen,
+ * 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)
}
/*
- * Like linetabsize(), but starting at column "startcol".
+ * Like linetabsize(), but "s" starts 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
+ chartabsize_T cts;
- while (*s != NUL)
- col += lbr_chartabsize_adv(line, &s, col);
- return (int)col;
+ 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, char_u *line, colnr_T len)
+win_linetabsize(win_T *wp, linenr_T lnum, char_u *line, colnr_T len)
{
- colnr_T col = 0;
- char_u *s;
+ chartabsize_T cts;
- 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;
+ 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;
}
/*
}
/*
- * like chartabsize(), but also check for line breaks on the screen
+ * 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(
- char_u *line UNUSED, // start of the line
- unsigned char *s,
- colnr_T col)
+lbr_chartabsize(chartabsize_T *cts)
{
-#ifdef FEAT_LINEBREAK
- if (!curwin->w_p_lbr && *get_showbreak_value(curwin) == NUL
- && !curwin->w_p_bri)
+#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(curwin, s, col, NULL);
- RET_WIN_BUF_CHARTABSIZE(curwin, curbuf, s, col)
-#ifdef FEAT_LINEBREAK
+ 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(curwin, line == NULL ? s : line, s, col, NULL);
+ return win_lbr_chartabsize(cts, NULL);
#endif
}
* Call lbr_chartabsize() and advance the pointer.
*/
int
-lbr_chartabsize_adv(
- char_u *line, // start of the line
- char_u **s,
- colnr_T col)
+lbr_chartabsize_adv(chartabsize_T *cts)
{
int retval;
- retval = lbr_chartabsize(line, *s, col);
- MB_PTR_ADV(*s);
+ 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'
*/
int
win_lbr_chartabsize(
- win_T *wp,
- char_u *line UNUSED, // start of the line
- char_u *s,
- colnr_T col,
- int *headp UNUSED)
+ 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; // col + screen size of tab
+ colnr_T col_adj = 0; // vcol + screen size of tab
colnr_T colmax;
int added;
int mb_added = 0;
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' and 'breakindent': return quickly.
+ * No 'linebreak', 'showbreak', 'breakindent' and text properties that
+ * insert text: return quickly.
*/
- if (!wp->w_p_lbr && !wp->w_p_bri && *get_showbreak_value(wp) == NUL)
+ 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(wp, s, col, headp);
- RET_WIN_BUF_CHARTABSIZE(wp, wp->w_buffer, s, col)
+ return win_nolbr_chartabsize(cts, headp);
+ RET_WIN_BUF_CHARTABSIZE(wp, wp->w_buffer, s, vcol)
}
-#ifdef FEAT_LINEBREAK
+#if defined(FEAT_LINEBREAK) || defined(FEAT_PROP_POPUP)
/*
- * First get normal size, without 'linebreak'
+ * First get the normal size, without 'linebreak' or text properties
*/
- size = win_chartabsize(wp, s, col);
+ 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;
* non-blank after a blank.
*/
numberextra = win_col_off(wp);
- col2 = col;
+ col2 = vcol;
colmax = (colnr_T)(wp->w_width - numberextra - col_adj);
- if (col >= colmax)
+ if (vcol >= colmax)
{
colmax += col_adj;
n = colmax + win_col_off2(wp);
if (n > 0)
- colmax += (((col - colmax) / n) + 1) * n - col_adj;
+ colmax += (((vcol - colmax) / n) + 1) * n - col_adj;
}
for (;;)
if (!(c != NUL
&& (VIM_ISBREAK(c)
|| (!VIM_ISBREAK(c)
- && (col2 == col || !VIM_ISBREAK((int)*ps))))))
+ && (col2 == vcol || !VIM_ISBREAK((int)*ps))))))
break;
col2 += win_chartabsize(wp, s, col2);
if (col2 >= colmax) // doesn't fit
{
- size = colmax - col + col_adj;
+ size = colmax - vcol + col_adj;
break;
}
}
}
else if (has_mbyte && size == 2 && MB_BYTE2LEN(*s) > 1
- && wp->w_p_wrap && in_win_border(wp, col))
+ && wp->w_p_wrap && in_win_border(wp, vcol))
{
++size; // Count the ">" in the last column.
mb_added = 1;
*/
added = 0;
sbr = c == NUL ? empty_option : get_showbreak_value(wp);
- if ((*sbr != NUL || wp->w_p_bri) && wp->w_p_wrap && col != 0)
+ 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;
- col += numberextra + mb_added;
- if (col >= (colnr_T)wp->w_width)
+ vcol += numberextra + mb_added;
+ if (vcol >= (colnr_T)wp->w_width)
{
- col -= wp->w_width;
+ vcol -= wp->w_width;
numberextra = wp->w_width - (numberextra - win_col_off2(wp));
- if (col >= numberextra && numberextra > 0)
- col %= numberextra;
+ if (vcol >= numberextra && numberextra > 0)
+ vcol %= numberextra;
if (*sbr != NUL)
{
sbrlen = (colnr_T)MB_CHARLEN(sbr);
- if (col >= sbrlen)
- col -= sbrlen;
+ if (vcol >= sbrlen)
+ vcol -= sbrlen;
}
- if (col >= numberextra && numberextra > 0)
- col = col % numberextra;
- else if (col > 0 && numberextra > 0)
- col += numberwidth - win_col_off2(wp);
+ 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 (col == 0 || col + size + sbrlen > (colnr_T)wp->w_width)
+ if (vcol == 0 || vcol + size + sbrlen > (colnr_T)wp->w_width)
{
added = 0;
if (*sbr != NUL)
{
// 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;
+ int prev_width = vcol
+ ? ((colnr_T)wp->w_width - (sbrlen + vcol)) : 0;
if (width <= 0)
width = (colnr_T)1;
added += get_breakindent_win(wp, line);
size += added;
- if (col != 0)
+ 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 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.
+ * 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(
- win_T *wp,
- char_u *s,
- colnr_T col,
- int *headp)
+ 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))
#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);
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
- * use a simple loop.
+ * 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 (;;)
{
- // A tab gets expanded, depending on the current column
+ // A tab gets expanded, depending on the current column.
+ // Other things also take up space.
head = 0;
- incr = win_lbr_chartabsize(wp, line, ptr, vcol, &head);
+ incr = win_lbr_chartabsize(&cts, &head);
// make sure we don't go past the end of the line
- if (*ptr == NUL)
+ if (*cts.cts_ptr == NUL)
{
incr = 1; // NUL at end of line only takes one column
break;
}
- if (posptr != NULL && ptr >= posptr) // character at pos->col
+ if (posptr != NULL && cts.cts_ptr >= posptr)
+ // character at pos->col
break;
- vcol += incr;
- MB_PTR_ADV(ptr);
+ 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
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
v = wp->w_leftcol;
if (v > 0 && !number_only)
{
- char_u *prev_ptr = ptr;
+ char_u *prev_ptr = ptr;
+ chartabsize_T cts;
+ int charsize;
- while (vcol < v && *ptr != NUL)
+ init_chartabsize_arg(&cts, wp, lnum, vcol, line, ptr);
+ while (cts.cts_vcol < v && *cts.cts_ptr != NUL)
{
- c = win_lbr_chartabsize(wp, line, ptr, (colnr_T)vcol, NULL);
- vcol += c;
- prev_ptr = ptr;
- MB_PTR_ADV(ptr);
+ 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
// that character but skip the first few screen characters.
if (vcol > v)
{
- vcol -= c;
+ 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) >= c || *ptr == TAB) && col == 0)
+ if (( (*mb_ptr2cells)(ptr) >= charsize || *ptr == TAB) && col == 0)
n_skip = v - vcol;
}
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;
proptype_T *pt = text_prop_type_by_id(
wp->w_buffer, text_props[tpi].tp_type);
- if (pt != NULL && pt->pt_hl_id > 0)
+ if (pt != NULL && pt->pt_hl_id > 0
+ && text_props[tpi].tp_id != -MAXCOL)
{
- int pt_attr = syn_id2attr(pt->pt_hl_id);
-
+ used_attr = syn_id2attr(pt->pt_hl_id);
text_prop_type = pt;
text_prop_attr =
- hl_combine_attr(text_prop_attr, pt_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;
}
}
}
int mb_off = has_mbyte ? (*mb_head_off)(line, ptr - 1)
: 0;
char_u *p = ptr - (mb_off + 1);
+ chartabsize_T cts;
- // TODO: is passing p for start of the line OK?
- n_extra = win_lbr_chartabsize(wp, line, p, (colnr_T)vcol,
- NULL) - 1;
+ 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.
if (!wp->w_p_list)
c = ' ';
}
+ clear_chartabsize_arg(&cts);
}
#endif
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
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(NULL, (char_u *)"\t", vcol);
- if (vcol + i > want_vcol)
+ i = lbr_chartabsize(&cts);
+ if (cts.cts_vcol + i > want_vcol)
break;
if (*ptr != TAB)
{
}
++fpos.col;
++ptr;
- vcol += i;
+ cts.cts_vcol += i;
}
+ vcol = cts.cts_vcol;
+ clear_chartabsize_arg(&cts);
if (change_col >= 0)
{
- int repl_off = 0;
- char_u *line = ptr;
+ int repl_off = 0;
// Skip over the spaces we need.
- while (vcol < want_vcol && *ptr == ' ')
+ init_chartabsize_arg(&cts, curwin, 0, vcol, ptr, ptr);
+ while (cts.cts_vcol < want_vcol && *cts.cts_ptr == ' ')
{
- vcol += lbr_chartabsize(line, ptr, vcol);
- ++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.
int
ins_copychar(linenr_T lnum)
{
- int c;
- int temp;
- char_u *ptr, *prev_ptr;
- char_u *line;
+ int c;
+ char_u *ptr, *prev_ptr;
+ char_u *line;
+ chartabsize_T cts;
if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count)
{
// 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)
+ 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 = ptr;
- temp += lbr_chartabsize_adv(line, &ptr, (colnr_T)temp);
+ prev_ptr = cts.cts_ptr;
+ cts.cts_vcol += lbr_chartabsize_adv(&cts);
}
- if ((colnr_T)temp > curwin->w_virtcol)
+ 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)
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
{"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)},
+ 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,
&& (c = inchar(typebuf.tb_buf + typebuf.tb_off
+ typebuf.tb_len, 3, 25L)) == 0)
{
- colnr_T col = 0, vcol;
+ colnr_T col = 0;
char_u *ptr;
if (mode_displayed)
{
if (did_ai)
{
+ chartabsize_T cts;
+
/*
* We are expecting to truncate the trailing
* white-space, so find the last non-white
* character -- webb
*/
- col = vcol = curwin->w_wcol = 0;
+ curwin->w_wcol = 0;
ptr = ml_get_curline();
- while (col < curwin->w_cursor.col)
+ init_chartabsize_arg(&cts, curwin,
+ curwin->w_cursor.lnum, 0, ptr, ptr);
+ while (cts.cts_ptr < ptr + curwin->w_cursor.col)
{
- if (!VIM_ISWHITE(ptr[col]))
- curwin->w_wcol = vcol;
- vcol += lbr_chartabsize(ptr, ptr + col,
- vcol);
+ if (!VIM_ISWHITE(*cts.cts_ptr))
+ curwin->w_wcol = cts.cts_vcol;
+ cts.cts_vcol += lbr_chartabsize(&cts);
if (has_mbyte)
- col += (*mb_ptr2len)(ptr + col);
+ cts.cts_ptr +=
+ (*mb_ptr2len)(cts.cts_ptr);
else
- ++col;
+ ++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;
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.
- vcol = last_vcol = 0;
- new_cursor_col = -1;
+ last_vcol = 0;
ptr = ml_get_curline();
- while (vcol <= (int)curwin->w_virtcol)
+ init_chartabsize_arg(&cts, curwin, 0, 0, ptr, ptr);
+ while (cts.cts_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)
+ last_vcol = cts.cts_vcol;
+ if (cts.cts_vcol > 0)
+ MB_PTR_ADV(cts.cts_ptr);
+ if (*cts.cts_ptr == NUL)
break;
- vcol += lbr_chartabsize(ptr, ptr + new_cursor_col, (colnr_T)vcol);
+ 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.
amount = 2;
else
{
- char_u *line = that;
+ char_u *line = that;
+ chartabsize_T cts;
- amount = 0;
- while (*that && col)
+ init_chartabsize_arg(&cts, curwin, pos->lnum, 0, line, line);
+ while (*cts.cts_ptr != NUL && col > 0)
{
- amount += lbr_chartabsize_adv(line, &that, (colnr_T)amount);
+ 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):
}
firsttry = amount;
- while (VIM_ISWHITE(*that))
+ init_chartabsize_arg(&cts, curwin, (colnr_T)(that - line),
+ amount, line, that);
+ while (VIM_ISWHITE(*cts.cts_ptr))
{
- amount += lbr_chartabsize(line, that, (colnr_T)amount);
- ++that;
+ 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
{
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 (*that
- && (!VIM_ISWHITE(*that)
+ while (*cts.cts_ptr
+ && (!VIM_ISWHITE(*cts.cts_ptr)
|| quotecount
|| parencount)
- && (!((*that == '(' || *that == '[')
+ && (!((*cts.cts_ptr == '('
+ || *cts.cts_ptr == '[')
&& !quotecount
&& !parencount
&& vi_lisp)))
{
- if (*that == '"')
+ if (*cts.cts_ptr == '"')
quotecount = !quotecount;
- if ((*that == '(' || *that == '[')
+ if ((*cts.cts_ptr == '(' || *cts.cts_ptr == '[')
&& !quotecount)
++parencount;
- if ((*that == ')' || *that == ']')
+ if ((*cts.cts_ptr == ')' || *cts.cts_ptr == ']')
&& !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);
+ 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(*that))
+ while (VIM_ISWHITE(*cts.cts_ptr))
{
- amount += lbr_chartabsize(
- line, that, (colnr_T)amount);
- that++;
+ 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;
}
s = ml_get_buf(wp->w_buffer, lnum, FALSE);
if (*s == NUL) // empty line
return 1;
- col = win_linetabsize(wp, s, (colnr_T)MAXCOL);
+ 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
plines_win_col(win_T *wp, linenr_T lnum, long column)
{
long col;
- char_u *s;
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
if (wp->w_width == 0)
return lines + 1;
- line = s = ml_get_buf(wp->w_buffer, lnum, FALSE);
+ line = ml_get_buf(wp->w_buffer, lnum, FALSE);
- col = 0;
- while (*s != NUL && --column >= 0)
+ init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
+ while (*cts.cts_ptr != NUL && --column >= 0)
{
- col += win_lbr_chartabsize(wp, line, s, (colnr_T)col, NULL);
- MB_PTR_ADV(s);
+ cts.cts_vcol += win_lbr_chartabsize(&cts, NULL);
+ MB_PTR_ADV(cts.cts_ptr);
}
/*
- * 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 *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.
*/
- if (*s == TAB && (State & MODE_NORMAL)
+ 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(wp, line, s, (colnr_T)col, NULL) - 1;
+ col += win_lbr_chartabsize(&cts, NULL) - 1;
+ clear_chartabsize_arg(&cts);
/*
* Add column offset for 'number', 'relativenumber', 'foldcolumn', etc.
{
colnr_T wcol = wcol_arg;
int idx;
- char_u *ptr;
char_u *line;
colnr_T col = 0;
int csize = 0;
else
{
int width = curwin->w_width - win_col_off(curwin);
+ chartabsize_T cts;
if (finetune
&& curwin->w_p_wrap
}
}
- ptr = line;
- while (col <= wcol && *ptr != NUL)
+ 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(curwin, line, ptr, col, &head);
- MB_PTR_ADV(ptr);
+ csize = win_lbr_chartabsize(&cts, &head);
+ MB_PTR_ADV(cts.cts_ptr);
#else
- csize = lbr_chartabsize_adv(line, &ptr, col);
+ csize = lbr_chartabsize_adv(&cts);
#endif
- col += csize;
+ cts.cts_vcol += csize;
}
- idx = (int)(ptr - line);
+ 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
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;
+ char_u *line;
+ chartabsize_T cts;
- line = ptr = ml_get_buf(wp->w_buffer, lnum, FALSE);
- while (count < vcol && *ptr != NUL)
+ // 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)
{
- count += win_lbr_chartabsize(wp, line, ptr, count, NULL);
- MB_PTR_ADV(ptr);
+ cts.cts_vcol += win_lbr_chartabsize(&cts, NULL);
+ MB_PTR_ADV(cts.cts_ptr);
}
- return (int)(ptr - line);
+ clear_chartabsize_arg(&cts);
+
+ return (int)(cts.cts_ptr - line);
}
#endif
if (!left)
{
- int tabs = 0, spaces = 0;
+ int tabs = 0, spaces = 0;
+ chartabsize_T cts;
/*
* 1. Get start vcol
else
++bd.textstart;
}
- for ( ; VIM_ISWHITE(*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); )
{
- // TODO: is passing bd.textstart for start of the line OK?
- incr = lbr_chartabsize_adv(bd.textstart, &bd.textstart, bd.start_vcol);
+ incr = lbr_chartabsize_adv(&cts);
total += incr;
- bd.start_vcol += 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
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
// The character's column is in "bd.start_vcol".
non_white_col = bd.start_vcol;
- while (VIM_ISWHITE(*non_white))
+ 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(bd.textstart, &non_white, non_white_col);
- non_white_col += incr;
+ 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.
// column number.
if (bd.startspaces)
verbatim_copy_width -= bd.start_char_vcols;
- while (verbatim_copy_width < destination_col)
+ init_chartabsize_arg(&cts, curwin, 0, verbatim_copy_width,
+ bd.textstart, verbatim_copy_end);
+ while (cts.cts_vcol < 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)
+ incr = lbr_chartabsize(&cts);
+ if (cts.cts_vcol + incr > destination_col)
break;
- verbatim_copy_width += incr;
- MB_PTR_ADV(verbatim_copy_end);
+ 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
* 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)
char_u *line;
char_u *prev_pstart;
char_u *prev_pend;
+ chartabsize_T cts;
#ifdef FEAT_LINEBREAK
int lbr_saved = curwin->w_p_lbr;
bdp->start_char_vcols = 0;
line = ml_get(lnum);
- pstart = line;
prev_pstart = line;
- while (bdp->start_vcol < oap->start_vcol && *pstart)
+ 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(line, pstart, bdp->start_vcol);
- bdp->start_vcol += incr;
- if (VIM_ISWHITE(*pstart))
+ incr = lbr_chartabsize(&cts);
+ cts.cts_vcol += incr;
+ if (VIM_ISWHITE(*cts.cts_ptr))
{
bdp->pre_whitesp += incr;
bdp->pre_whitesp_c++;
bdp->pre_whitesp = 0;
bdp->pre_whitesp_c = 0;
}
- prev_pstart = pstart;
- MB_PTR_ADV(pstart);
+ 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
{
}
else
{
+ init_chartabsize_arg(&cts, curwin, lnum, bdp->end_vcol,
+ line, pend);
prev_pend = pend;
- while (bdp->end_vcol <= oap->end_vcol && *pend != NUL)
+ 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 = pend;
- incr = lbr_chartabsize_adv(line, &pend, bdp->end_vcol);
- bdp->end_vcol += incr;
+ // 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
// "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),
+ len = win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum, FALSE),
(colnr_T)MAXCOL);
wp->w_width = w_width;
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 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);
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 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);
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 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);
rex.line = reg_getline(rex.lnum);
rex.input = rex.line + col;
- cols = win_linetabsize(wp, 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;
}
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))
+ rex.reg_firstlnum + rex.lnum,
+ rex.line,
+ (colnr_T)(rex.input - rex.line)) + 1, scan))
status = RA_NOMATCH;
break;
}
if (!result)
result = nfa_re_num_cmp(t->state->val, op,
- (long_u)win_linetabsize(wp, rex.line, col) + 1);
+ (long_u)win_linetabsize(wp,
+ rex.reg_firstlnum + rex.lnum,
+ rex.line, col) + 1);
if (result)
{
add_here = TRUE;
bd.textcol = 0;
for (i = 0; i < y_size; ++i)
{
- int spaces = 0;
- char shortline;
+ int spaces = 0;
+ char shortline;
+ chartabsize_T cts;
bd.startspaces = 0;
bd.endspaces = 0;
// 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; )
+ 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(oldp, &ptr, vcol);
- vcol += incr;
+ 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) ;
// 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(NULL, &y_array[i][j], 0);
+ {
+ spaces -= lbr_chartabsize(&cts);
+ ++cts.cts_ptr;
+ cts.cts_vcol = 0;
+ }
+ clear_chartabsize_arg(&cts);
if (spaces < 0)
spaces = 0;
}
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
+#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.
#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)
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;
--- /dev/null
+|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|
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
* prop_add({lnum}, {col}, {props})
*/
void
-f_prop_add(typval_T *argvars, typval_T *rettv UNUSED)
+f_prop_add(typval_T *argvars, typval_T *rettv)
{
linenr_T start_lnum;
colnr_T start_col;
return;
}
- prop_add_common(start_lnum, start_col, argvars[2].vval.v_dict,
- curbuf, &argvars[2]);
+ 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'.
+ * 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,
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)
- return FAIL;
+ goto theend;
if (start_lnum < 1 || start_lnum > buf->b_ml.ml_line_count)
{
semsg(_(e_invalid_line_number_nr), (long)start_lnum);
- return FAIL;
+ goto theend;
}
if (end_lnum < start_lnum || end_lnum > buf->b_ml.ml_line_count)
{
semsg(_(e_invalid_line_number_nr), (long)end_lnum);
- return FAIL;
+ goto theend;
}
if (buf->b_ml.ml_mfp == NULL)
{
emsg(_(e_cannot_add_text_property_to_unloaded_buffer));
- return FAIL;
+ 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)
if (col - 1 > (colnr_T)textlen)
{
semsg(_(e_invalid_column_number_nr), (long)start_col);
- return FAIL;
+ goto theend;
}
if (lnum == end_lnum)
// 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;
+ goto theend;
// Copy the text, including terminating NUL.
mch_memmove(newtext, buf->b_ml.ml_line_ptr, textlen);
}
changed_lines_buf(buf, start_lnum, end_lnum + 1, 0);
- return OK;
+ res = OK;
+
+theend:
+ vim_free(text);
+ return res;
}
/*
emsg(_(e_invalid_argument));
return;
}
- if (prop_add_one(buf, type_name, id, start_lnum, end_lnum,
+ if (prop_add_one(buf, type_name, id, NULL, start_lnum, end_lnum,
start_col, end_col) == FAIL)
return;
}
redraw_buf_later(buf, VALID);
}
+/*
+ * 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.
*/
- void
+ int
prop_add_common(
linenr_T start_lnum,
colnr_T start_col,
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));
- return;
+ goto theend;
}
type_name = dict_get_string(dict, "type", FALSE);
if (end_lnum < start_lnum)
{
semsg(_(e_invalid_value_for_argument_str), "end_lnum");
- return;
+ goto theend;
}
}
else
if (length < 0 || end_lnum > start_lnum)
{
semsg(_(e_invalid_value_for_argument_str), "length");
- return;
+ goto theend;
}
end_col = start_col + length;
}
if (end_col <= 0)
{
semsg(_(e_invalid_value_for_argument_str), "end_col");
- return;
+ goto theend;
}
}
else if (start_lnum == end_lnum)
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)
- return;
+ 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, start_lnum, end_lnum, start_col, end_col);
+ 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;
}
/*
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)))
+ && (prop_ids == NULL
+ || prop_type_or_id_in_list(prop_ids, prop_ids_len,
+ prop.tp_id)))
{
dict_T *d = dict_alloc();
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 67,
/**/
66,
/**/