mirror of
https://github.com/macvim-dev/macvim.git
synced 2026-05-28 00:21:57 +02:00
patch 9.2.0320: several bugs with text properties
Problem: several bugs with text properties Solution: Fix the bugs, rework the text properties work related: #19685 fixes: #19680 fixes: #19681 fixes: #12568 fixes: #19256 closes: #19869 Co-Authored-By: Paul Ollis <paul@cleversheep.org> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Hirohito Higashi <h.east.727@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
c79edc0df9
commit
ff41e9d853
@@ -1,4 +1,4 @@
|
||||
*textprop.txt* For Vim version 9.2. Last change: 2026 Apr 06
|
||||
*textprop.txt* For Vim version 9.2. Last change: 2026 Apr 07
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -511,7 +511,9 @@ will move accordingly.
|
||||
|
||||
When text is deleted and a text property no longer includes any text, it is
|
||||
deleted. However, a text property that was defined as zero-width will remain,
|
||||
unless the whole line is deleted.
|
||||
unless the whole line is deleted. When lines are joined by a multi-line
|
||||
substitute command, virtual text properties on the deleted lines are moved to
|
||||
the resulting joined line.
|
||||
*E275*
|
||||
When a buffer is unloaded, all the text properties are gone. There is no way
|
||||
to store the properties in a file. You can only re-create them. When a
|
||||
|
||||
@@ -52625,6 +52625,8 @@ Changed~
|
||||
- |json_decode()| is stricter: keywords must be lowercase, lone surrogates are
|
||||
now invalid
|
||||
- |js_decode()| rejects lone surrogates
|
||||
- virtual text properties on lines deleted by a multi-line substitute
|
||||
are moved to the resulting joined line instead of being dropped.
|
||||
|
||||
*added-9.3*
|
||||
Added ~
|
||||
|
||||
@@ -1115,9 +1115,6 @@ free_buffer_stuff(
|
||||
#endif
|
||||
#ifdef FEAT_NETBEANS_INTG
|
||||
netbeans_file_killed(buf);
|
||||
#endif
|
||||
#ifdef FEAT_PROP_POPUP
|
||||
ga_clear_strings(&buf->b_textprop_text);
|
||||
#endif
|
||||
map_clear_mode(buf, MAP_ALL_MODES, TRUE, FALSE); // clear local mappings
|
||||
map_clear_mode(buf, MAP_ALL_MODES, TRUE, TRUE); // clear local abbrevs
|
||||
|
||||
+1
-1
@@ -2570,7 +2570,7 @@ truncate_line(int fixpos)
|
||||
* Saves the lines for undo first if "undo" is TRUE.
|
||||
*/
|
||||
void
|
||||
del_lines(long nlines, int undo)
|
||||
del_lines(long nlines, int undo)
|
||||
{
|
||||
long n;
|
||||
linenr_T first = curwin->w_cursor.lnum;
|
||||
|
||||
+19
-3
@@ -1140,6 +1140,22 @@ init_chartabsize_arg(
|
||||
cts->cts_text_props[text_prop_idxs[i]];
|
||||
vim_free(text_prop_idxs);
|
||||
}
|
||||
|
||||
// Convert tp_text_offset to tp_text pointer.
|
||||
char_u *count_ptr = prop_start - PROP_COUNT_SIZE;
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
textprop_T *tp = &cts->cts_text_props[i];
|
||||
|
||||
if (tp->tp_id < 0 && tp->u.tp_text_offset > 0)
|
||||
{
|
||||
tp->u.tp_text = count_ptr + tp->u.tp_text_offset;
|
||||
tp->tp_flags |= TP_FLAG_VTEXT_PTR;
|
||||
}
|
||||
else
|
||||
tp->u.tp_text = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1294,7 +1310,7 @@ win_lbr_chartabsize(
|
||||
int charlen = *s == NUL ? 1 : mb_ptr2len(s);
|
||||
int i;
|
||||
int col = (int)(s - line);
|
||||
garray_T *gap = &wp->w_buffer->b_textprop_text;
|
||||
|
||||
|
||||
// The "$" for 'list' mode will go between the EOL and
|
||||
// the text prop, account for that.
|
||||
@@ -1318,9 +1334,9 @@ win_lbr_chartabsize(
|
||||
&& ((tp->tp_flags & TP_FLAG_ALIGN_ABOVE)
|
||||
? col == 0
|
||||
: s[0] == NUL && cts->cts_with_trailing)))
|
||||
&& -tp->tp_id - 1 < gap->ga_len)
|
||||
&& tp->u.tp_text != NULL)
|
||||
{
|
||||
char_u *p = ((char_u **)gap->ga_data)[-tp->tp_id - 1];
|
||||
char_u *p = tp->u.tp_text;
|
||||
|
||||
if (p != NULL)
|
||||
{
|
||||
|
||||
+24
-9
@@ -685,8 +685,7 @@ text_prop_position(
|
||||
int above = (tp->tp_flags & TP_FLAG_ALIGN_ABOVE);
|
||||
int below = (tp->tp_flags & TP_FLAG_ALIGN_BELOW);
|
||||
int wrap = tp->tp_col < MAXCOL || (tp->tp_flags & TP_FLAG_WRAP);
|
||||
int padding = tp->tp_col == MAXCOL && tp->tp_len > 1
|
||||
? tp->tp_len - 1 : 0;
|
||||
int padding = tp->tp_col == MAXCOL ? tp->tp_padleft : 0;
|
||||
int col_with_padding = scr_col + (below ? 0 : padding);
|
||||
int room = wp->w_width - col_with_padding;
|
||||
int before = room; // spaces before the text
|
||||
@@ -1705,9 +1704,29 @@ win_line(
|
||||
else
|
||||
text_props = ALLOC_MULT(textprop_T, text_prop_count);
|
||||
if (text_props != NULL)
|
||||
{
|
||||
mch_memmove(text_props, prop_start,
|
||||
text_prop_count * sizeof(textprop_T));
|
||||
|
||||
// Convert tp_text_offset to tp_text pointer for virtual
|
||||
// text properties. prop_start points into the memline
|
||||
// after the prop_count field.
|
||||
char_u *count_ptr = prop_start - PROP_COUNT_SIZE;
|
||||
|
||||
for (int i = 0; i < text_prop_count; ++i)
|
||||
{
|
||||
if (text_props[i].tp_id < 0
|
||||
&& text_props[i].u.tp_text_offset > 0)
|
||||
{
|
||||
text_props[i].u.tp_text =
|
||||
count_ptr + text_props[i].u.tp_text_offset;
|
||||
text_props[i].tp_flags |= TP_FLAG_VTEXT_PTR;
|
||||
}
|
||||
else
|
||||
text_props[i].u.tp_text = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate an array for the indexes.
|
||||
if (text_prop_count <= WIN_LINE_TEXT_PROP_STACK_LEN)
|
||||
text_prop_idxs = text_prop_idxs_buf;
|
||||
@@ -2301,13 +2320,10 @@ win_line(
|
||||
}
|
||||
}
|
||||
if (text_prop_id < 0 && used_tpi >= 0
|
||||
&& -text_prop_id
|
||||
<= wp->w_buffer->b_textprop_text.ga_len)
|
||||
&& text_props[used_tpi].u.tp_text != NULL)
|
||||
{
|
||||
textprop_T *tp = &text_props[used_tpi];
|
||||
char_u *p = ((char_u **)wp->w_buffer
|
||||
->b_textprop_text.ga_data)[
|
||||
-text_prop_id - 1];
|
||||
char_u *p = tp->u.tp_text;
|
||||
int above = (tp->tp_flags
|
||||
& TP_FLAG_ALIGN_ABOVE);
|
||||
int bail_out = FALSE;
|
||||
@@ -2325,8 +2341,7 @@ win_line(
|
||||
int wrap = tp->tp_col < MAXCOL
|
||||
|| (tp->tp_flags & TP_FLAG_WRAP);
|
||||
int padding = tp->tp_col == MAXCOL
|
||||
&& tp->tp_len > 1
|
||||
? tp->tp_len - 1 : 0;
|
||||
? tp->tp_padleft : 0;
|
||||
|
||||
// Insert virtual text before the current
|
||||
// character, or add after the end of the line.
|
||||
|
||||
+1
-2
@@ -3431,9 +3431,8 @@ EXTERN char e_internal_error_shortmess_too_long[]
|
||||
#ifdef FEAT_EVAL
|
||||
EXTERN char e_class_variable_str_not_found_in_class_str[]
|
||||
INIT(= N_("E1337: Class variable \"%s\" not found in class \"%s\""));
|
||||
// E1338 unused
|
||||
#endif
|
||||
// E1339 unused
|
||||
// E1338 and E1339 unused
|
||||
#ifdef FEAT_EVAL
|
||||
EXTERN char e_argument_already_declared_in_class_str[]
|
||||
INIT(= N_("E1340: Argument already declared in the class: %s"));
|
||||
|
||||
+27
-8
@@ -4895,15 +4895,27 @@ ex_substitute(exarg_T *eap)
|
||||
text_prop_count);
|
||||
if (text_props != NULL)
|
||||
{
|
||||
int pi;
|
||||
|
||||
mch_memmove(text_props, prop_start,
|
||||
text_prop_count * sizeof(textprop_T));
|
||||
// After joining the text prop columns will
|
||||
// increase.
|
||||
for (pi = 0; pi < text_prop_count; ++pi)
|
||||
text_props[pi].tp_col +=
|
||||
regmatch.startpos[0].col + sublen - 1;
|
||||
// Filter out virtual text and continuation
|
||||
// properties from deleted lines, convert
|
||||
// offsets to pointers, and adjust columns.
|
||||
int wi = 0;
|
||||
for (int pi = 0; pi < text_prop_count; ++pi)
|
||||
{
|
||||
// Skip virtual text and continuation
|
||||
// properties from the deleted line.
|
||||
if (text_props[pi].tp_id < 0
|
||||
|| (text_props[pi].tp_flags
|
||||
& TP_FLAG_CONT_PREV))
|
||||
continue;
|
||||
text_props[wi] = text_props[pi];
|
||||
text_props[wi].tp_col +=
|
||||
regmatch.startpos[0].col + sublen - 1;
|
||||
text_props[wi].u.tp_text = NULL;
|
||||
++wi;
|
||||
}
|
||||
text_prop_count = wi;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5142,7 +5154,14 @@ skip:
|
||||
break;
|
||||
for (i = 0; i < nmatch_tl; ++i)
|
||||
ml_delete(lnum);
|
||||
mark_adjust(lnum, lnum + nmatch_tl - 1,
|
||||
if (copycol > 0)
|
||||
mark_adjust(lnum, lnum + nmatch_tl - 1,
|
||||
(long)MAXLNUM, -nmatch_tl);
|
||||
else
|
||||
// The entire last matched line was consumed,
|
||||
// so the first line was effectively replaced
|
||||
// by lines below.
|
||||
mark_adjust(lnum - 1, lnum - 1,
|
||||
(long)MAXLNUM, -nmatch_tl);
|
||||
if (subflags.do_ask)
|
||||
deleted_lines(lnum, nmatch_tl);
|
||||
|
||||
+54
-22
@@ -2930,13 +2930,19 @@ add_text_props_for_append(
|
||||
{
|
||||
if (round == 2)
|
||||
{
|
||||
uint16_t pc;
|
||||
|
||||
if (new_prop_count == 0)
|
||||
return; // nothing to do
|
||||
new_len = *len + new_prop_count * sizeof(textprop_T);
|
||||
new_len = *len + (int)PROP_COUNT_SIZE
|
||||
+ new_prop_count * (int)sizeof(textprop_T);
|
||||
new_line = alloc(new_len);
|
||||
if (new_line == NULL)
|
||||
return;
|
||||
mch_memmove(new_line, *line, *len);
|
||||
// Write prop_count header.
|
||||
pc = (uint16_t)new_prop_count;
|
||||
mch_memmove(new_line + *len, &pc, PROP_COUNT_SIZE);
|
||||
new_prop_count = 0;
|
||||
}
|
||||
|
||||
@@ -2954,8 +2960,10 @@ add_text_props_for_append(
|
||||
prop.tp_flags |= TP_FLAG_CONT_PREV;
|
||||
prop.tp_col = 1;
|
||||
prop.tp_len = *len; // not exactly the right length
|
||||
mch_memmove(new_line + *len + new_prop_count
|
||||
* sizeof(textprop_T), &prop, sizeof(textprop_T));
|
||||
prop.u.tp_text_offset = 0;
|
||||
mch_memmove(new_line + *len + (int)PROP_COUNT_SIZE
|
||||
+ new_prop_count * sizeof(textprop_T),
|
||||
&prop, sizeof(textprop_T));
|
||||
}
|
||||
++new_prop_count;
|
||||
}
|
||||
@@ -3772,34 +3780,48 @@ adjust_text_props_for_delete(
|
||||
textlen = STRLEN(text) + 1;
|
||||
if ((long)textlen >= line_size)
|
||||
{
|
||||
// No properties on this line.
|
||||
if (above)
|
||||
internal_error("no text property above deleted line");
|
||||
else
|
||||
internal_error("no text property below deleted line");
|
||||
return;
|
||||
}
|
||||
this_props_len = line_size - (int)textlen;
|
||||
if ((long)textlen + (long)PROP_COUNT_SIZE > line_size)
|
||||
{
|
||||
internal_error("text property data too short");
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t pc;
|
||||
|
||||
mch_memmove(&pc, text + textlen, PROP_COUNT_SIZE);
|
||||
this_props_len = pc * (int)sizeof(textprop_T);
|
||||
}
|
||||
|
||||
found = FALSE;
|
||||
for (done_this = 0; done_this < this_props_len;
|
||||
done_this += sizeof(textprop_T))
|
||||
{
|
||||
int flag = above ? TP_FLAG_CONT_NEXT
|
||||
: TP_FLAG_CONT_PREV;
|
||||
textprop_T prop_this;
|
||||
char_u *props_start = text + textlen + PROP_COUNT_SIZE;
|
||||
|
||||
mch_memmove(&prop_this, text + textlen + done_this,
|
||||
sizeof(textprop_T));
|
||||
if ((prop_this.tp_flags & flag)
|
||||
&& prop_del.tp_id == prop_this.tp_id
|
||||
&& prop_del.tp_type == prop_this.tp_type)
|
||||
for (done_this = 0; done_this < this_props_len;
|
||||
done_this += sizeof(textprop_T))
|
||||
{
|
||||
found = TRUE;
|
||||
prop_this.tp_flags &= ~flag;
|
||||
mch_memmove(text + textlen + done_this, &prop_this,
|
||||
int flag = above ? TP_FLAG_CONT_NEXT
|
||||
: TP_FLAG_CONT_PREV;
|
||||
textprop_T prop_this;
|
||||
|
||||
mch_memmove(&prop_this, props_start + done_this,
|
||||
sizeof(textprop_T));
|
||||
break;
|
||||
if ((prop_this.tp_flags & flag)
|
||||
&& prop_del.tp_id == prop_this.tp_id
|
||||
&& prop_del.tp_type == prop_this.tp_type)
|
||||
{
|
||||
found = TRUE;
|
||||
prop_this.tp_flags &= ~flag;
|
||||
mch_memmove(props_start + done_this, &prop_this,
|
||||
sizeof(textprop_T));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
@@ -4003,13 +4025,23 @@ theend:
|
||||
#ifdef FEAT_PROP_POPUP
|
||||
if (textprop_save != NULL)
|
||||
{
|
||||
// textprop_save is [prop_count][textprop_T...][vtext...].
|
||||
// Skip prop_count header and pass only the textprop_T part.
|
||||
uint16_t pc;
|
||||
char_u *props_data;
|
||||
int props_bytes;
|
||||
|
||||
mch_memmove(&pc, textprop_save, PROP_COUNT_SIZE);
|
||||
props_data = textprop_save + PROP_COUNT_SIZE;
|
||||
props_bytes = pc * (int)sizeof(textprop_T);
|
||||
|
||||
// Adjust text properties in the line above and below.
|
||||
if (lnum > 1)
|
||||
adjust_text_props_for_delete(buf, lnum - 1, textprop_save,
|
||||
(int)textprop_len, TRUE);
|
||||
adjust_text_props_for_delete(buf, lnum - 1,
|
||||
props_data, props_bytes, TRUE);
|
||||
if (lnum <= buf->b_ml.ml_line_count)
|
||||
adjust_text_props_for_delete(buf, lnum, textprop_save,
|
||||
(int)textprop_len, FALSE);
|
||||
adjust_text_props_for_delete(buf, lnum,
|
||||
props_data, props_bytes, FALSE);
|
||||
}
|
||||
vim_free(textprop_save);
|
||||
#endif
|
||||
|
||||
@@ -2172,10 +2172,6 @@ do_join(
|
||||
int remove_comments = (use_formatoptions == TRUE)
|
||||
&& has_format_option(FO_REMOVE_COMS);
|
||||
int prev_was_comment;
|
||||
#ifdef FEAT_PROP_POPUP
|
||||
int propcount = 0; // number of props over all joined lines
|
||||
int props_remaining;
|
||||
#endif
|
||||
|
||||
if (save_undo && u_save((linenr_T)(curwin->w_cursor.lnum - 1),
|
||||
(linenr_T)(curwin->w_cursor.lnum + count)) == FAIL)
|
||||
@@ -2205,10 +2201,6 @@ do_join(
|
||||
for (t = 0; t < count; ++t)
|
||||
{
|
||||
curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t));
|
||||
#ifdef FEAT_PROP_POPUP
|
||||
propcount += count_props((linenr_T) (curwin->w_cursor.lnum + t),
|
||||
t > 0, t + 1 == count);
|
||||
#endif
|
||||
if (t == 0 && setmark && (cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0)
|
||||
{
|
||||
// Set the '[ mark.
|
||||
@@ -2295,9 +2287,6 @@ do_join(
|
||||
|
||||
// allocate the space for the new line
|
||||
newp_len = sumsize + 1;
|
||||
#ifdef FEAT_PROP_POPUP
|
||||
newp_len += propcount * sizeof(textprop_T);
|
||||
#endif
|
||||
newp = alloc(newp_len);
|
||||
if (newp == NULL)
|
||||
{
|
||||
@@ -2316,8 +2305,15 @@ do_join(
|
||||
* should not really be a problem.
|
||||
*/
|
||||
#ifdef FEAT_PROP_POPUP
|
||||
props_remaining = propcount;
|
||||
unpacked_memline_T um = um_open_at_no_props(
|
||||
curwin->w_buffer, curwin->w_cursor.lnum, 0);
|
||||
// um_open_at_no_props may have invalidated "curr".
|
||||
int curr_off = (int)(curr - curr_start);
|
||||
|
||||
curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + count - 1));
|
||||
curr += curr_off;
|
||||
#endif
|
||||
|
||||
for (t = count - 1; ; --t)
|
||||
{
|
||||
int spaces_removed;
|
||||
@@ -2338,9 +2334,8 @@ do_join(
|
||||
mark_col_adjust(curwin->w_cursor.lnum + t, (colnr_T)0, -t,
|
||||
(long)(cend - newp - spaces_removed), spaces_removed);
|
||||
#ifdef FEAT_PROP_POPUP
|
||||
prepend_joined_props(newp + sumsize + 1, propcount, &props_remaining,
|
||||
curwin->w_cursor.lnum + t, t == count - 1,
|
||||
(long)(cend - newp), spaces_removed);
|
||||
prepend_joined_props(&um, curwin->w_cursor.lnum + t,
|
||||
t == count - 1, (long)(cend - newp), spaces_removed);
|
||||
#endif
|
||||
if (t == 0)
|
||||
break;
|
||||
@@ -2352,6 +2347,16 @@ do_join(
|
||||
currsize = (int)STRLEN(curr);
|
||||
}
|
||||
|
||||
#ifdef FEAT_PROP_POPUP
|
||||
if (um.buf != NULL)
|
||||
{
|
||||
um_set_text(&um, newp);
|
||||
um_reverse_props(&um);
|
||||
um_close(&um);
|
||||
newp = NULL; // um_set_text took ownership
|
||||
}
|
||||
else
|
||||
#endif
|
||||
ml_replace_len(curwin->w_cursor.lnum, newp, (colnr_T)newp_len, TRUE, FALSE);
|
||||
|
||||
if (setmark && (cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0)
|
||||
|
||||
+3
-3
@@ -1318,9 +1318,9 @@ popup_adjust_position(win_T *wp)
|
||||
int screen_ecol;
|
||||
|
||||
// Popup window is positioned relative to a text property.
|
||||
if (find_visible_prop(prop_win,
|
||||
if (!find_visible_prop(prop_win,
|
||||
wp->w_popup_prop_type, wp->w_popup_prop_id,
|
||||
&prop, &prop_lnum) == FAIL)
|
||||
&prop, &prop_lnum))
|
||||
{
|
||||
// Text property is no longer visible, hide the popup.
|
||||
// Unhiding the popup is done in check_popup_unhidden().
|
||||
@@ -4307,7 +4307,7 @@ check_popup_unhidden(win_T *wp)
|
||||
if ((wp->w_popup_flags & POPF_HIDDEN_FORCE) == 0
|
||||
&& find_visible_prop(wp->w_popup_prop_win,
|
||||
wp->w_popup_prop_type, wp->w_popup_prop_id,
|
||||
&prop, &lnum) == OK)
|
||||
&prop, &lnum))
|
||||
{
|
||||
wp->w_popup_flags &= ~POPF_HIDDEN;
|
||||
wp->w_popup_prop_topline = 0; // force repositioning
|
||||
|
||||
+13
-3
@@ -1,4 +1,13 @@
|
||||
/* textprop.c */
|
||||
unpacked_memline_T um_open(buf_T *buf);
|
||||
bool um_goto_line(unpacked_memline_T *um, linenr_T lnum, int extra_props);
|
||||
unpacked_memline_T um_open_at(buf_T *buf, linenr_T lnum, int extra_props);
|
||||
bool um_set_text(unpacked_memline_T *um, char_u *text);
|
||||
void um_reverse_props(unpacked_memline_T *um);
|
||||
unpacked_memline_T um_open_at_no_props(buf_T *buf, linenr_T lnum, int prop_count);
|
||||
void um_delete_prop(unpacked_memline_T *um, int index);
|
||||
void um_close(unpacked_memline_T *um);
|
||||
void um_abort(unpacked_memline_T *um);
|
||||
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);
|
||||
@@ -7,10 +16,11 @@ int get_text_props(buf_T *buf, linenr_T lnum, char_u **props, int will_change);
|
||||
int prop_count_above_below(buf_T *buf, linenr_T lnum);
|
||||
int count_props(linenr_T lnum, int only_starting, int last_line);
|
||||
void sort_text_props(buf_T *buf, textprop_T *props, int *idxs, int count);
|
||||
int find_visible_prop(win_T *wp, int type_id, int id, textprop_T *prop, linenr_T *found_lnum);
|
||||
bool find_visible_prop(win_T *wp, int type_id, int id, textprop_T *prop, linenr_T *found_lnum);
|
||||
char_u *props_add_count_header(char_u *line, int line_len, int textlen, int *new_len);
|
||||
void add_text_props(linenr_T lnum, textprop_T *text_props, int text_prop_count);
|
||||
proptype_T *text_prop_type_by_id(buf_T *buf, int id);
|
||||
int text_prop_type_valid(buf_T *buf, textprop_T *prop);
|
||||
bool text_prop_type_valid(buf_T *buf, textprop_T *prop);
|
||||
void f_prop_clear(typval_T *argvars, typval_T *rettv);
|
||||
void f_prop_find(typval_T *argvars, typval_T *rettv);
|
||||
void f_prop_list(typval_T *argvars, typval_T *rettv);
|
||||
@@ -24,5 +34,5 @@ void clear_global_prop_types(void);
|
||||
void clear_buf_prop_types(buf_T *buf);
|
||||
int adjust_prop_columns(linenr_T lnum, colnr_T col, int bytes_added, int flags);
|
||||
void adjust_props_for_split(linenr_T lnum_props, linenr_T lnum_top, int kept, int deleted, int at_eol);
|
||||
void prepend_joined_props(char_u *new_props, int propcount, int *props_remaining, linenr_T lnum, int last_line, long col, int removed);
|
||||
void prepend_joined_props(unpacked_memline_T *um, linenr_T lnum, int last_line, long col, int removed);
|
||||
/* vim: set ft=c : */
|
||||
|
||||
+43
-3
@@ -893,13 +893,22 @@ typedef struct memline
|
||||
typedef struct textprop_S
|
||||
{
|
||||
colnr_T tp_col; // start column (one based, in bytes)
|
||||
colnr_T tp_len; // length in bytes, when tp_id is negative used
|
||||
// for left padding plus one
|
||||
colnr_T tp_len; // length in bytes; for virtual text props
|
||||
// this is STRLEN(vtext) (not including NUL)
|
||||
int tp_id; // identifier
|
||||
int tp_type; // property type
|
||||
int tp_flags; // TP_FLAG_ values
|
||||
int tp_padleft; // left padding between text line and virtual
|
||||
// text
|
||||
union // For virtual text props (tp_id < 0):
|
||||
{ // check TP_FLAG_VTEXT_PTR in tp_flags to
|
||||
// determine which member is active.
|
||||
colnr_T tp_text_offset; // offset to vtext string from the
|
||||
// prop_count position in the memline
|
||||
// (when TP_FLAG_VTEXT_PTR is NOT set)
|
||||
char_u *tp_text; // pointer to virtual text string
|
||||
// (when TP_FLAG_VTEXT_PTR IS set)
|
||||
} u;
|
||||
} textprop_T;
|
||||
|
||||
#define TP_FLAG_CONT_NEXT 0x1 // property continues in next line
|
||||
@@ -913,10 +922,42 @@ typedef struct textprop_S
|
||||
#define TP_FLAG_WRAP 0x080 // virtual text wraps - when missing
|
||||
// text is truncated
|
||||
#define TP_FLAG_START_INCL 0x100 // "start_incl" copied from proptype
|
||||
#define TP_FLAG_DELETED 0x200 // marked for deletion in
|
||||
// unpacked_memline_T
|
||||
#define TP_FLAG_VTEXT_PTR 0x400 // u.tp_text access is valid
|
||||
|
||||
#define PROP_COUNT_SIZE sizeof(uint16_t) // size of prop_count in memline
|
||||
|
||||
#define PROP_TEXT_MIN_CELLS 4 // minimum number of cells to use for
|
||||
// the text, even when truncating
|
||||
|
||||
/*
|
||||
* An unpacked form of a single memline with text properties.
|
||||
*
|
||||
* When packed in a memline, the format is:
|
||||
* [line text] [NUL] [textprop_T...] [vtext strings...]
|
||||
* Virtual text props use u.tp_text_offset (relative to the start of props
|
||||
* area). When unpacked, u.tp_text is a pointer: either into the memline
|
||||
* data (LOADED) or to separately allocated memory (DETACHED).
|
||||
*
|
||||
* States:
|
||||
* - LOADED: text and u.tp_text point into the memline. Read-only.
|
||||
* - DETACHED: text and u.tp_text are separately allocated. Writable.
|
||||
* - CLOSED: buf == NULL. Unusable (error recovery).
|
||||
*/
|
||||
typedef struct unpacked_memline_S
|
||||
{
|
||||
buf_T *buf; // the line's buffer (NULL = closed)
|
||||
linenr_T lnum; // line number (0 = no line loaded)
|
||||
bool detached; // true when text/vtext are allocated
|
||||
colnr_T text_size; // size of text including NUL
|
||||
char_u *text; // NUL-terminated text
|
||||
int prop_size; // number of allocated prop slots
|
||||
int prop_count; // number of properties
|
||||
textprop_T *props; // property array
|
||||
bool text_changed; // true if text was modified
|
||||
} unpacked_memline_T;
|
||||
|
||||
/*
|
||||
* Structure defining a property type.
|
||||
*/
|
||||
@@ -3550,7 +3591,6 @@ struct file_buffer
|
||||
int b_has_textprop; // TRUE when text props were added
|
||||
hashtab_T *b_proptypes; // text property types local to buffer
|
||||
proptype_T **b_proparray; // entries of b_proptypes sorted on tp_id
|
||||
garray_T b_textprop_text; // stores text for props, index by (-id - 1)
|
||||
#endif
|
||||
|
||||
#if defined(FEAT_BEVAL) && defined(FEAT_EVAL)
|
||||
|
||||
@@ -337,6 +337,7 @@ NEW_TESTS = \
|
||||
test_textformat \
|
||||
test_textobjects \
|
||||
test_textprop \
|
||||
test_textprop2 \
|
||||
test_timers \
|
||||
test_true_false \
|
||||
test_trycatch \
|
||||
@@ -601,6 +602,7 @@ NEW_TESTS_RES = \
|
||||
test_textformat.res \
|
||||
test_textobjects.res \
|
||||
test_textprop.res \
|
||||
test_textprop2.res \
|
||||
test_timers.res \
|
||||
test_true_false.res \
|
||||
test_trycatch.res \
|
||||
|
||||
+1
-1
@@ -9,4 +9,4 @@
|
||||
|~+0#4040ff13&| @58
|
||||
|~| @58
|
||||
|~| @58
|
||||
|:+0#0000000&|s|e|t| |s|i|g|n|c|o|l|u|m|n|=|y|e|s| |f|o|l|d|c|o|l|u|m|n|=|3| |c|u|r|s|o|r|l|i|n|3|,|5| @10|A|l@1|
|
||||
| +0#0000000&@41|3|,|5| @10|A|l@1|
|
||||
|
||||
+1
-1
@@ -9,4 +9,4 @@
|
||||
|~+0#4040ff13#ffffff0| @58
|
||||
|~| @58
|
||||
|~| @58
|
||||
|:+0#0000000&|s|e|t| |s|i|g|n|c|o|l|u|m|n|=|y|e|s| |f|o|l|d|c|o|l|u|m|n|=|3| @9|4|,|4| @10|A|l@1|
|
||||
| +0#0000000&@41|4|,|4| @10|A|l@1|
|
||||
|
||||
@@ -3565,7 +3565,7 @@ func Test_props_with_text_after_nowrap()
|
||||
let buf = RunVimInTerminal('-S XscriptPropsAfterNowrap', #{rows: 12, cols: 60})
|
||||
call VerifyScreenDump(buf, 'Test_prop_with_text_after_nowrap_1', {})
|
||||
|
||||
call term_sendkeys(buf, ":set signcolumn=yes foldcolumn=3 cursorline\<CR>")
|
||||
call term_sendkeys(buf, ":set signcolumn=yes foldcolumn=3 cursorline\<CR>\<C-L>")
|
||||
call VerifyScreenDump(buf, 'Test_prop_with_text_after_nowrap_2', {})
|
||||
|
||||
call term_sendkeys(buf, "j")
|
||||
@@ -3975,15 +3975,15 @@ func Test_removed_prop_with_text_cleans_up_array()
|
||||
call setline(1, 'some text here')
|
||||
call prop_type_add('some', #{highlight: 'ErrorMsg'})
|
||||
let id1 = prop_add(1, 5, #{type: 'some', text: "SOME"})
|
||||
call assert_equal(-1, id1)
|
||||
call assert_true(id1 < 0)
|
||||
let id2 = prop_add(1, 10, #{type: 'some', text: "HERE"})
|
||||
call assert_equal(-2, id2)
|
||||
call assert_true(id2 < id1)
|
||||
|
||||
" removing the props resets the index
|
||||
" IDs are not recycled after removal; new IDs keep decreasing.
|
||||
call prop_remove(#{id: id1})
|
||||
call prop_remove(#{id: id2})
|
||||
let id1 = prop_add(1, 5, #{type: 'some', text: "SOME"})
|
||||
call assert_equal(-1, id1)
|
||||
let id3 = prop_add(1, 5, #{type: 'some', text: "SOME"})
|
||||
call assert_true(id3 < id2)
|
||||
|
||||
call prop_type_delete('some')
|
||||
bwipe!
|
||||
@@ -4672,7 +4672,7 @@ func Test_error_when_using_negative_id()
|
||||
|
||||
" Negative id is always rejected. Before the fix, prop_add() with a negative
|
||||
" id succeeded when no virtual text existed, then prop_list() would dereference
|
||||
" a NULL pointer (b_textprop_text.ga_data) and crash.
|
||||
" a NULL pointer and crash.
|
||||
call assert_fails("call prop_add(1, 1, #{type: 'test1', length: 1, id: -1})", 'E1293:')
|
||||
call assert_equal([], prop_list(1))
|
||||
|
||||
|
||||
@@ -0,0 +1,431 @@
|
||||
" Additional tests for defining text property types and adding text properties
|
||||
" to the buffer.
|
||||
|
||||
CheckFeature textprop
|
||||
|
||||
source util/screendump.vim
|
||||
|
||||
" Find a property of a given type on a given line.
|
||||
func s:PropForType(lnum, type_name)
|
||||
for p in prop_list(a:lnum)
|
||||
if p['type'] == a:type_name
|
||||
return p
|
||||
endif
|
||||
endfor
|
||||
return {}
|
||||
endfunc
|
||||
|
||||
" Clean up property types and wipe buffer.
|
||||
func s:CleanupPropTypes(types)
|
||||
for name in a:types
|
||||
call prop_type_delete(name)
|
||||
endfor
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
" Set up buffer content and properties used by multiple tests.
|
||||
"
|
||||
" Properties:
|
||||
" type '1': line 2 col 2 -> line 4 col 9 (multiline highlight)
|
||||
" type '2': line 2 col 3 -> line 2 col 7 (single line highlight)
|
||||
" type '2': line 3 col 3 -> line 3 col 8 (single line highlight)
|
||||
" type '2': line 4 col 3 -> line 4 col 9 (single line highlight)
|
||||
" type '3': line 2 col 5 -> line 4 col 9 (multiline highlight)
|
||||
func s:Setup_multiline_props_1()
|
||||
new
|
||||
call setline(1, ['Line1', 'Line.2', 'Line..3', 'Line...4'])
|
||||
silent! call prop_type_delete('1')
|
||||
silent! call prop_type_delete('2')
|
||||
silent! call prop_type_delete('3')
|
||||
call prop_type_add('1', {'highlight': 'DiffAdd'})
|
||||
call prop_type_add('2', {'highlight': 'DiffChange'})
|
||||
call prop_type_add('3', {'highlight': 'DiffDelete'})
|
||||
call prop_add(2, 2, {'type': '1', 'id': 42, 'end_lnum': 4, 'end_col': 9})
|
||||
call prop_add(2, 3, {'type': '2', 'id': 42, 'end_lnum': 2, 'end_col': 7})
|
||||
call prop_add(3, 3, {'type': '2', 'id': 42, 'end_lnum': 3, 'end_col': 8})
|
||||
call prop_add(4, 3, {'type': '2', 'id': 42, 'end_lnum': 4, 'end_col': 9})
|
||||
call prop_add(2, 5, {'type': '3', 'id': 42, 'end_lnum': 4, 'end_col': 9})
|
||||
|
||||
" Sanity check.
|
||||
call assert_equal(4, line('$'))
|
||||
call assert_equal(0, len(prop_list(1)))
|
||||
call assert_equal(3, len(prop_list(2)))
|
||||
call assert_equal(3, len(prop_list(3)))
|
||||
call assert_equal(3, len(prop_list(4)))
|
||||
endfunc
|
||||
|
||||
" Set up buffer with a multiline property spanning line 1 col 4 -> line 3 col 4.
|
||||
func s:Setup_start_end_prop()
|
||||
new
|
||||
call setline(1, ['Line.1', 'Line..2', 'Line...3', 'Line....4'])
|
||||
silent! call prop_type_delete('1')
|
||||
call prop_type_add('1', {'highlight': 'DiffAdd'})
|
||||
call prop_add(1, 4, {'type': '1', 'id': 42, 'end_lnum': 3, 'end_col': 4})
|
||||
endfunc
|
||||
|
||||
" The substitute command should adjust marks when one or more whole lines are
|
||||
" deleted.
|
||||
func Test_subst_adjusts_marks()
|
||||
" Buffer: 4 lines with a single multiline property spanning all lines.
|
||||
" type '1': line 1 col 1 -> line 4 col 10
|
||||
func DoEditAndCheck(edit, expected_marks, expected_nlines) closure
|
||||
new
|
||||
call setline(1, ['Line.1', 'Line..2', 'Line...3', 'Line....4'])
|
||||
silent! call prop_type_delete('1')
|
||||
call prop_type_add('1', {'highlight': 'DiffAdd'})
|
||||
call prop_add(1, 1, {'type': '1', 'id': 42, 'end_lnum': 4, 'end_col': 10})
|
||||
call setpos("'a", [0, 1, 1])
|
||||
call setpos("'b", [0, 2, 1])
|
||||
call setpos("'c", [0, 3, 1])
|
||||
call setpos("'d", [0, 4, 1])
|
||||
set undolevels&
|
||||
let msg = printf('Edit command = "%s"', a:edit)
|
||||
|
||||
execute a:edit
|
||||
|
||||
call assert_equal(a:expected_nlines, line('$'), msg)
|
||||
call assert_equal(a:expected_marks[0], getpos("'a"), msg .. ', mark a')
|
||||
call assert_equal(a:expected_marks[1], getpos("'b"), msg .. ', mark b')
|
||||
call assert_equal(a:expected_marks[2], getpos("'c"), msg .. ', mark c')
|
||||
call assert_equal(a:expected_marks[3], getpos("'d"), msg .. ', mark d')
|
||||
|
||||
" Undo and verify original state is restored.
|
||||
:undo
|
||||
call assert_equal(4, line('$'), msg .. ', post-undo')
|
||||
call assert_equal('Line.1', getline(1), msg .. ', post-undo line 1')
|
||||
call assert_equal([0, 1, 1, 0], getpos("'a"), msg .. ', post-undo mark a')
|
||||
call assert_equal([0, 2, 1, 0], getpos("'b"), msg .. ', post-undo mark b')
|
||||
call assert_equal([0, 3, 1, 0], getpos("'c"), msg .. ', post-undo mark c')
|
||||
call assert_equal([0, 4, 1, 0], getpos("'d"), msg .. ', post-undo mark d')
|
||||
|
||||
call prop_type_delete('1')
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
" Delete line 1.
|
||||
let expected = [[0, 0, 0, 0], [0, 1, 1, 0], [0, 2, 1, 0], [0, 3, 1, 0]]
|
||||
for edit in [':1 substitute/Line.1\n//', ':1 delete', 'normal 1GVx']
|
||||
call DoEditAndCheck(edit, expected, 3)
|
||||
endfor
|
||||
return
|
||||
|
||||
" NOTE: The tests below are disabled in the original too (after 'return').
|
||||
" Delete line 2.
|
||||
let expected = [[0, 1, 1, 0], [0, 0, 0, 0], [0, 2, 1, 0], [0, 3, 1, 0]]
|
||||
for edit in [':2 substitute/Line..2\n//', ':1 substitute/\nLine..2//',
|
||||
\ '2: delete', 'normal 2GVx']
|
||||
call DoEditAndCheck(edit, expected, 3)
|
||||
endfor
|
||||
|
||||
" Delete line 4.
|
||||
let expected = [[0, 1, 1, 0], [0, 2, 1, 0], [0, 3, 1, 0], [0, 0, 0, 0]]
|
||||
for edit in [':3 substitute/\nLine....4//', '4: delete', 'normal 4GVx']
|
||||
call DoEditAndCheck(edit, expected, 3)
|
||||
endfor
|
||||
|
||||
" Delete lines 2-3.
|
||||
let expected = [[0, 1, 1, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 2, 1, 0]]
|
||||
for edit in [':2,3 substitute/Line.*[23]\n//',
|
||||
\ ':2,3 substitute/\%(Line[.]*[23]\n\)*',
|
||||
\ '2,3: delete', 'normal 2GVjx']
|
||||
call DoEditAndCheck(edit, expected, 2)
|
||||
endfor
|
||||
|
||||
" Delete lines 1-3.
|
||||
let expected = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 1, 1, 0]]
|
||||
for edit in [':1,$ substitute/Line.*[123]\n//',
|
||||
\ ':1,$ substitute/\%(Line[.]*[123]\n\)*',
|
||||
\ '1,3: delete', 'normal 1GVjjx']
|
||||
call DoEditAndCheck(edit, expected, 1)
|
||||
endfor
|
||||
|
||||
" Delete all lines.
|
||||
let expected = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
|
||||
for edit in [':1,$ substitute/Line.*[1234]\n//',
|
||||
\ ':1,$ substitute/\%(Line[.]*[1234]\n\)*//',
|
||||
\ '1,4: delete', 'normal 1GVjjjx']
|
||||
call DoEditAndCheck(edit, expected, 1)
|
||||
endfor
|
||||
|
||||
" Delete lines 3-4.
|
||||
let expected = [[0, 1, 1, 0], [0, 2, 1, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
|
||||
for edit in [':2,$ substitute/\n\%(Line.*[34]\n\?\)*//',
|
||||
\ '3,4: delete', 'normal 3GVjx']
|
||||
call DoEditAndCheck(edit, expected, 2)
|
||||
endfor
|
||||
|
||||
" Delete lines 2-4.
|
||||
let expected = [[0, 1, 1, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
|
||||
for edit in [':1,$ substitute/\n\%(Line.*[234]\n\?\)*//',
|
||||
\ '2,4: delete', 'normal 2GVjjx']
|
||||
call DoEditAndCheck(edit, expected, 1)
|
||||
endfor
|
||||
endfunc
|
||||
|
||||
" The substitute command should correctly drop floating, virtual
|
||||
" properties when lines are deleted.
|
||||
func Test_multiline_substitute_del_lines_drops_virt_text_props()
|
||||
" Helper to set up the buffer with virtual text properties.
|
||||
" When a:virt_k_col is 1, 'virt-k' is at line 1 col 1 (floating);
|
||||
" when 4, it is at line 1 col 4 (inline).
|
||||
func SetupVirtProps(virt_k_col)
|
||||
new
|
||||
call setline(1, ['Line.1', 'Line..2', 'Line...3', 'Line....4'])
|
||||
for s:t in ['1', '2', '3', '4', '7', '8']
|
||||
silent! call prop_type_delete(s:t)
|
||||
endfor
|
||||
call prop_type_add('1', {'highlight': 'DiffAdd'})
|
||||
call prop_type_add('2', {'highlight': 'DiffChange', 'end_incl': 1})
|
||||
call prop_type_add('3', {'highlight': 'DiffDelete'})
|
||||
call prop_type_add('4', {'highlight': 'DiffText'})
|
||||
call prop_type_add('7', {'highlight': 'WarningMsg'})
|
||||
call prop_type_add('8', {'highlight': 'Directory'})
|
||||
" Floating virtual text.
|
||||
call prop_add(1, 0, {'type': '1', 'text': 'virt-a', 'text_align': 'right'})
|
||||
call prop_add(1, 0, {'type': '2', 'text': 'virt-b', 'text_align': 'right'})
|
||||
call prop_add(2, 0, {'type': '3', 'text': 'virt-c', 'text_align': 'right'})
|
||||
call prop_add(2, 0, {'type': '4', 'text': 'virt-d', 'text_align': 'right'})
|
||||
call prop_add(3, 0, {'type': '4', 'text': 'virt-e', 'text_align': 'right'})
|
||||
call prop_add(4, 0, {'type': '3', 'text': 'virt-g', 'text_align': 'right'})
|
||||
call prop_add(4, 0, {'type': '7', 'text': 'virt-h', 'text_align': 'right'})
|
||||
" Inline virtual text.
|
||||
call prop_add(1, a:virt_k_col, {'type': '8', 'text': 'virt-k'})
|
||||
" Highlight property spanning lines 1-4.
|
||||
call prop_add(1, 1, {'type': '2', 'id': 42, 'end_lnum': 4, 'end_col': 4})
|
||||
call prop_add(4, 4, {'type': '3', 'id': 42, 'end_lnum': 4, 'end_col': 7})
|
||||
endfunc
|
||||
|
||||
" Join lines 1-2.
|
||||
call SetupVirtProps(1)
|
||||
1,2 substitute /e.1\nL/e.1 L/
|
||||
call assert_equal(3, line('$'))
|
||||
call assert_equal('Line.1 Line..2', getline(1))
|
||||
call assert_equal(4, len(prop_list(1)))
|
||||
call s:CleanupPropTypes(['1', '2', '3', '4', '7', '8'])
|
||||
|
||||
" Join lines 1-3.
|
||||
call SetupVirtProps(1)
|
||||
1,3 substitute /e.1\nLine..2\nL/e.1 L/
|
||||
call assert_equal(2, line('$'))
|
||||
call assert_equal('Line.1 Line...3', getline(1))
|
||||
" NOTE: Original PR expected value is 3
|
||||
call assert_equal(4, len(prop_list(1)))
|
||||
call s:CleanupPropTypes(['1', '2', '3', '4', '7', '8'])
|
||||
|
||||
" Join lines 1-4.
|
||||
call SetupVirtProps(1)
|
||||
1,4 substitute /e.1\nLine..2\nLine...3\nL/e.1 L/
|
||||
call assert_equal(1, line('$'))
|
||||
call assert_equal('Line.1 Line....4', getline(1))
|
||||
call assert_equal(5, len(prop_list(1)))
|
||||
call s:CleanupPropTypes(['1', '2', '3', '4', '7', '8'])
|
||||
|
||||
" Second variant: inline virtual text at col 4.
|
||||
call SetupVirtProps(4)
|
||||
1,2 substitute /e.1\nL/e.1 L/
|
||||
call assert_equal(3, line('$'))
|
||||
call assert_equal(4, len(prop_list(1)))
|
||||
call s:CleanupPropTypes(['1', '2', '3', '4', '7', '8'])
|
||||
endfunc
|
||||
|
||||
" Deletion of text starting a multiline property should adjust next line.
|
||||
func Test_text_deletion_of_start_to_eol_adjusts_multiline_property()
|
||||
" Partial delete: property is shortened but not removed.
|
||||
call s:Setup_start_end_prop()
|
||||
normal 1G03l2x
|
||||
call assert_equal('Lin1', getline(1))
|
||||
call assert_equal(1, len(prop_list(1)))
|
||||
call assert_equal(2, prop_list(1)[0]['length'])
|
||||
call prop_type_delete('1')
|
||||
bwipe!
|
||||
|
||||
" Full delete of start: property should be removed from line 1.
|
||||
for edit in ['normal 1G03ld$', 'normal 1G03l3x',
|
||||
\ 'normal 1G03lv x', '1 substitute /e.1//']
|
||||
call s:Setup_start_end_prop()
|
||||
execute edit
|
||||
let msg = printf('op="%s"', edit)
|
||||
call assert_equal([], prop_list(1), msg)
|
||||
call prop_type_delete('1')
|
||||
bwipe!
|
||||
endfor
|
||||
endfunc
|
||||
|
||||
" Deletion of text ending a multiline property should adjust previous line.
|
||||
func Test_text_deletion_of_end_to_sol_adjusts_multiline_property()
|
||||
" Partial delete: property end is adjusted but not removed.
|
||||
call s:Setup_start_end_prop()
|
||||
normal 3G02x
|
||||
call assert_equal('ne...3', getline(3))
|
||||
call assert_equal(1, len(prop_list(3)))
|
||||
call assert_equal(0, prop_list(3)[0]['start'])
|
||||
call prop_type_delete('1')
|
||||
bwipe!
|
||||
|
||||
" Full delete of ending portion: property should be removed from line 3.
|
||||
for edit in ['normal 3G03x', 'normal 3G0v x', '3 substitute /Lin//']
|
||||
call s:Setup_start_end_prop()
|
||||
execute edit
|
||||
let msg = printf('op="%s"', edit)
|
||||
call assert_equal([], prop_list(3), msg)
|
||||
call prop_type_delete('1')
|
||||
bwipe!
|
||||
endfor
|
||||
endfunc
|
||||
|
||||
" Inline text properties should be removed when surrounding text is removed.
|
||||
func Test_text_deletion_removes_inline_virtual_text()
|
||||
func SetupVirtText(start_incl, end_incl)
|
||||
new
|
||||
call setline(1, ['The line with properties....'])
|
||||
let opts = {'highlight': 'DiffChange'}
|
||||
if a:start_incl
|
||||
let opts['start_incl'] = 1
|
||||
endif
|
||||
if a:end_incl
|
||||
let opts['end_incl'] = 1
|
||||
endif
|
||||
silent! call prop_type_delete('2')
|
||||
call prop_type_add('2', opts)
|
||||
call prop_add(1, 7, {'type': '2', 'text': 'xxx'})
|
||||
endfunc
|
||||
|
||||
" Test all combinations of start_incl/end_incl.
|
||||
for [si, ei] in [[0, 0], [1, 0], [0, 1], [1, 1]]
|
||||
" Deletion of one char before virtual text: property stays.
|
||||
for edit in ['normal 1G05lx', '1 substitute /i//', 'normal 1G05lvx']
|
||||
call SetupVirtText(si, ei)
|
||||
execute edit
|
||||
let msg = printf('si=%d ei=%d op="%s"', si, ei, edit)
|
||||
call assert_equal(1, len(prop_list(1)), msg)
|
||||
call assert_equal(6, prop_list(1)[0]['col'], msg)
|
||||
call prop_type_delete('2')
|
||||
bwipe!
|
||||
endfor
|
||||
|
||||
" Deletion of one char after virtual text: property stays.
|
||||
for edit in ['normal 1G06lx', '1 substitute /n//', 'normal 1G06lvx']
|
||||
call SetupVirtText(si, ei)
|
||||
execute edit
|
||||
let msg = printf('si=%d ei=%d op="%s"', si, ei, edit)
|
||||
call assert_equal(1, len(prop_list(1)), msg)
|
||||
call assert_equal(7, prop_list(1)[0]['col'], msg)
|
||||
call prop_type_delete('2')
|
||||
bwipe!
|
||||
endfor
|
||||
|
||||
" Deletion of both chars around virtual text: property is removed.
|
||||
for edit in ['normal 1G05l2x', '1 substitute /in//', 'normal 1G05lv x']
|
||||
call SetupVirtText(si, ei)
|
||||
execute edit
|
||||
let msg = printf('si=%d ei=%d op="%s"', si, ei, edit)
|
||||
call assert_equal([], prop_list(1), msg)
|
||||
call prop_type_delete('2')
|
||||
bwipe!
|
||||
endfor
|
||||
endfor
|
||||
endfunc
|
||||
|
||||
" Removing a multiline property from the last line should fix the property
|
||||
" on the penultimate line.
|
||||
func Test_multiline_prop_partial_remove_last_using_remove()
|
||||
call s:Setup_multiline_props_1()
|
||||
|
||||
call prop_remove({'type': '3'}, 4)
|
||||
call assert_equal(1, s:PropForType(3, '3')['end'])
|
||||
|
||||
call s:CleanupPropTypes(['1', '2', '3'])
|
||||
endfunc
|
||||
|
||||
" Removing a multiline property from the penultimate line should fix the
|
||||
" properties on the previous and last lines.
|
||||
func Test_multiline_prop_partial_remove_penultimate_using_remove()
|
||||
call s:Setup_multiline_props_1()
|
||||
|
||||
call prop_remove({'type': '3'}, 3)
|
||||
call assert_equal(1, s:PropForType(2, '3')['end'])
|
||||
call assert_equal(1, s:PropForType(4, '3')['start'])
|
||||
|
||||
call s:CleanupPropTypes(['1', '2', '3'])
|
||||
endfunc
|
||||
|
||||
" Removing all properties from the first line should fix the properties
|
||||
" on the second line.
|
||||
func Test_multiline_prop_partial_remove_first_using_clear()
|
||||
call s:Setup_multiline_props_1()
|
||||
|
||||
call prop_clear(2)
|
||||
call assert_equal(1, s:PropForType(3, '3')['start'])
|
||||
call assert_equal(1, s:PropForType(3, '1')['start'])
|
||||
|
||||
call s:CleanupPropTypes(['1', '2', '3'])
|
||||
endfunc
|
||||
|
||||
" Removing all multiline properties from the last line should fix the
|
||||
" properties on the penultimate line.
|
||||
func Test_multiline_prop_partial_remove_last_using_clear()
|
||||
call s:Setup_multiline_props_1()
|
||||
|
||||
call prop_clear(4)
|
||||
call assert_equal(1, s:PropForType(3, '3')['end'])
|
||||
call assert_equal(1, s:PropForType(3, '1')['end'])
|
||||
|
||||
call s:CleanupPropTypes(['1', '2', '3'])
|
||||
endfunc
|
||||
|
||||
" Removing all multiline properties from the penultimate line should fix the
|
||||
" properties on the previous and last lines.
|
||||
func Test_multiline_prop_partial_remove_penultimate_using_clear()
|
||||
call s:Setup_multiline_props_1()
|
||||
|
||||
call prop_clear(3)
|
||||
call assert_equal(1, s:PropForType(2, '3')['end'])
|
||||
call assert_equal(1, s:PropForType(4, '3')['start'])
|
||||
call assert_equal(1, s:PropForType(2, '1')['end'])
|
||||
call assert_equal(1, s:PropForType(4, '1')['start'])
|
||||
|
||||
call s:CleanupPropTypes(['1', '2', '3'])
|
||||
endfunc
|
||||
|
||||
" Deleting the first line with multiline properties should fix the properties
|
||||
" on the second line.
|
||||
func Test_multiline_prop_delete_first_line()
|
||||
call s:Setup_multiline_props_1()
|
||||
|
||||
:2 delete
|
||||
call assert_equal(3, line('$'))
|
||||
call assert_equal(1, s:PropForType(2, '1')['start'])
|
||||
call assert_equal(1, s:PropForType(2, '3')['start'])
|
||||
|
||||
call s:CleanupPropTypes(['1', '2', '3'])
|
||||
endfunc
|
||||
|
||||
" Deleting the last line with multiline properties should fix the properties
|
||||
" on the penultimate line.
|
||||
func Test_multiline_prop_delete_last_line()
|
||||
call s:Setup_multiline_props_1()
|
||||
|
||||
:4 delete
|
||||
call assert_equal(3, line('$'))
|
||||
call assert_equal(1, s:PropForType(3, '1')['end'])
|
||||
call assert_equal(1, s:PropForType(3, '3')['end'])
|
||||
|
||||
call s:CleanupPropTypes(['1', '2', '3'])
|
||||
endfunc
|
||||
|
||||
" Deleting the penultimate line with multiline properties should keep
|
||||
" the properties spanning lines.
|
||||
func Test_multiline_prop_delete_penultimate_line()
|
||||
call s:Setup_multiline_props_1()
|
||||
|
||||
:3 delete
|
||||
call assert_equal(3, line('$'))
|
||||
call assert_equal(0, s:PropForType(2, '1')['end'])
|
||||
call assert_equal(0, s:PropForType(2, '3')['end'])
|
||||
call assert_equal(0, s:PropForType(3, '1')['start'])
|
||||
call assert_equal(0, s:PropForType(3, '3')['start'])
|
||||
|
||||
call s:CleanupPropTypes(['1', '2', '3'])
|
||||
endfunc
|
||||
|
||||
" vim: shiftwidth=2 sts=2 expandtab
|
||||
+1042
-301
File diff suppressed because it is too large
Load Diff
@@ -734,6 +734,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
320,
|
||||
/**/
|
||||
319,
|
||||
/**/
|
||||
|
||||
Reference in New Issue
Block a user