mirror of
https://github.com/vim/vim.git
synced 2025-07-26 11:04:33 -04:00
patch 8.2.0845: text properties crossing lines not handled correctly
Problem: Text properties crossing lines not handled correctly. Solution: When joining lines merge text properties if possible. (Axel Forsman, closes #5839, closes #5683)
This commit is contained in:
parent
a9d4b84d97
commit
87be9be1db
@ -3420,7 +3420,6 @@ adjust_text_props_for_delete(
|
||||
int done_del;
|
||||
int done_this;
|
||||
textprop_T prop_del;
|
||||
textprop_T prop_this;
|
||||
bhdr_T *hp;
|
||||
DATA_BL *dp;
|
||||
int idx;
|
||||
@ -3451,7 +3450,8 @@ adjust_text_props_for_delete(
|
||||
if (idx == 0) // first line in block, text at the end
|
||||
line_size = dp->db_txt_end - line_start;
|
||||
else
|
||||
line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK) - line_start;
|
||||
line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK)
|
||||
- line_start;
|
||||
text = (char_u *)dp + line_start;
|
||||
textlen = STRLEN(text) + 1;
|
||||
if ((long)textlen >= line_size)
|
||||
@ -3466,24 +3466,24 @@ adjust_text_props_for_delete(
|
||||
}
|
||||
|
||||
found = FALSE;
|
||||
for (done_this = 0; done_this < this_props_len; done_this += sizeof(textprop_T))
|
||||
for (done_this = 0; done_this < this_props_len;
|
||||
done_this += sizeof(textprop_T))
|
||||
{
|
||||
mch_memmove(&prop_this, text + textlen + done_del, sizeof(textprop_T));
|
||||
if (prop_del.tp_id == prop_this.tp_id
|
||||
int flag = above ? TP_FLAG_CONT_NEXT
|
||||
: TP_FLAG_CONT_PREV;
|
||||
textprop_T prop_this;
|
||||
|
||||
mch_memmove(&prop_this, text + textlen + done_del,
|
||||
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)
|
||||
{
|
||||
int flag = above ? TP_FLAG_CONT_NEXT : TP_FLAG_CONT_PREV;
|
||||
|
||||
found = TRUE;
|
||||
if (prop_this.tp_flags & flag)
|
||||
{
|
||||
prop_this.tp_flags &= ~flag;
|
||||
mch_memmove(text + textlen + done_del, &prop_this, sizeof(textprop_T));
|
||||
}
|
||||
else if (above)
|
||||
internal_error("text property above deleted line does not continue");
|
||||
else
|
||||
internal_error("text property below deleted line does not continue");
|
||||
prop_this.tp_flags &= ~flag;
|
||||
mch_memmove(text + textlen + done_del, &prop_this,
|
||||
sizeof(textprop_T));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
|
51
src/ops.c
51
src/ops.c
@ -1887,6 +1887,7 @@ do_join(
|
||||
char_u *curr_start = NULL;
|
||||
char_u *cend;
|
||||
char_u *newp;
|
||||
size_t newp_len;
|
||||
char_u *spaces; // number of spaces inserted before a line
|
||||
int endcurr1 = NUL;
|
||||
int endcurr2 = NUL;
|
||||
@ -1900,8 +1901,8 @@ do_join(
|
||||
&& has_format_option(FO_REMOVE_COMS);
|
||||
int prev_was_comment;
|
||||
#ifdef FEAT_PROP_POPUP
|
||||
textprop_T **prop_lines = NULL;
|
||||
int *prop_lengths = NULL;
|
||||
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),
|
||||
@ -1932,6 +1933,9 @@ 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);
|
||||
#endif
|
||||
if (t == 0 && setmark && !cmdmod.lockmarks)
|
||||
{
|
||||
// Set the '[ mark.
|
||||
@ -2014,7 +2018,11 @@ do_join(
|
||||
col = sumsize - currsize - spaces[count - 1];
|
||||
|
||||
// allocate the space for the new line
|
||||
newp = alloc(sumsize + 1);
|
||||
newp_len = sumsize + 1;
|
||||
#ifdef FEAT_PROP_POPUP
|
||||
newp_len += propcount * sizeof(textprop_T);
|
||||
#endif
|
||||
newp = alloc(newp_len);
|
||||
if (newp == NULL)
|
||||
{
|
||||
ret = FAIL;
|
||||
@ -2023,20 +2031,6 @@ do_join(
|
||||
cend = newp + sumsize;
|
||||
*cend = 0;
|
||||
|
||||
#ifdef FEAT_PROP_POPUP
|
||||
// We need to move properties of the lines that are going to be deleted to
|
||||
// the new long one.
|
||||
if (curbuf->b_has_textprop && !text_prop_frozen)
|
||||
{
|
||||
// Allocate an array to copy the text properties of joined lines into.
|
||||
// And another array to store the number of properties in each line.
|
||||
prop_lines = ALLOC_CLEAR_MULT(textprop_T *, count - 1);
|
||||
prop_lengths = ALLOC_CLEAR_MULT(int, count - 1);
|
||||
if (prop_lengths == NULL)
|
||||
VIM_CLEAR(prop_lines);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Move affected lines to the new long one.
|
||||
* This loops backwards over the joined lines, including the original line.
|
||||
@ -2045,12 +2039,16 @@ do_join(
|
||||
* column. This is not Vi compatible, but Vi deletes the marks, thus that
|
||||
* should not really be a problem.
|
||||
*/
|
||||
#ifdef FEAT_PROP_POPUP
|
||||
props_remaining = propcount;
|
||||
#endif
|
||||
for (t = count - 1; ; --t)
|
||||
{
|
||||
int spaces_removed;
|
||||
|
||||
cend -= currsize;
|
||||
mch_memmove(cend, curr, (size_t)currsize);
|
||||
|
||||
if (spaces[t] > 0)
|
||||
{
|
||||
cend -= spaces[t];
|
||||
@ -2063,15 +2061,14 @@ do_join(
|
||||
|
||||
mark_col_adjust(curwin->w_cursor.lnum + t, (colnr_T)0, (linenr_T)-t,
|
||||
(long)(cend - newp - spaces_removed), spaces_removed);
|
||||
if (t == 0)
|
||||
break;
|
||||
#ifdef FEAT_PROP_POPUP
|
||||
if (prop_lines != NULL)
|
||||
adjust_props_for_join(curwin->w_cursor.lnum + t,
|
||||
prop_lines + t - 1, prop_lengths + t - 1,
|
||||
(long)(cend - newp - spaces_removed), spaces_removed);
|
||||
prepend_joined_props(newp + sumsize + 1, propcount, &props_remaining,
|
||||
curwin->w_cursor.lnum + t, t == count - 1,
|
||||
(long)(cend - newp), spaces_removed);
|
||||
#endif
|
||||
|
||||
if (t == 0)
|
||||
break;
|
||||
curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t - 1));
|
||||
if (remove_comments)
|
||||
curr += comments[t - 1];
|
||||
@ -2080,13 +2077,7 @@ do_join(
|
||||
currsize = (int)STRLEN(curr);
|
||||
}
|
||||
|
||||
#ifdef FEAT_PROP_POPUP
|
||||
if (prop_lines != NULL)
|
||||
join_prop_lines(curwin->w_cursor.lnum, newp,
|
||||
prop_lines, prop_lengths, count);
|
||||
else
|
||||
#endif
|
||||
ml_replace(curwin->w_cursor.lnum, newp, FALSE);
|
||||
ml_replace_len(curwin->w_cursor.lnum, newp, newp_len, TRUE, FALSE);
|
||||
|
||||
if (setmark && !cmdmod.lockmarks)
|
||||
{
|
||||
|
@ -3,6 +3,7 @@ int find_prop_type_id(char_u *name, buf_T *buf);
|
||||
void f_prop_add(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);
|
||||
proptype_T *text_prop_type_by_id(buf_T *buf, int id);
|
||||
void f_prop_clear(typval_T *argvars, typval_T *rettv);
|
||||
@ -18,6 +19,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);
|
||||
void adjust_props_for_join(linenr_T lnum, textprop_T **prop_line, int *prop_length, long col, int removed);
|
||||
void join_prop_lines(linenr_T lnum, char_u *newp, textprop_T **prop_lines, int *prop_lengths, int count);
|
||||
void prepend_joined_props(char_u *new_props, int propcount, int *props_remaining, linenr_T lnum, int add_all, long col, int removed);
|
||||
/* vim: set ft=c : */
|
||||
|
@ -2,7 +2,7 @@
|
||||
| +0#af5f00255&@1|2| |N+0#0000000#ffff4012|u|m|b|é|r| |1+0#4040ff13&|2|3| +0#0000000&|ä|n|d| |t|h|œ|n| |4+0#4040ff13&|¾|7|.+0#0000000&| +0&#ffffff0@46
|
||||
| +8#af5f00255&@1|3| >-+8#0000000#ffff4012|x+8&#ffffff0|a+8#4040ff13&@1|x+8#0000000&|-@1|x+8#4040ff13&|b@1|x+8#0000000&|-@1|x|c+8#4040ff13&@1|x|-+8#0000000&@1|x+8#4040ff13&|d@1|x|-+8#0000000&@1| @45
|
||||
| +0#af5f00255&@1|4| |/+0#40ff4011&@1| |c|o|m+0#0000000#e0e0e08@1|e|n+0#40ff4011#ffffff0|t| |w+0&#e0e0e08|i|t|h| |e+8&&|r@1|o|r| +0&#ffffff0|i|n| |i|t| +0#0000000&@43
|
||||
| +0#af5f00255&@1|5| |f+0#0000000&|i|r|s|t| |l+0&#ffff4012|i|n|e| @1|s|e|c|o|n|d| +0&#ffffff0|l|i|n|e| @1|t|h|i|r|d| |l|i|n|e| |f|o|u|r|t|h| |l+0&#ffff4012|i|n|e| +0&#ffffff0@23
|
||||
| +0#af5f00255&@1|5| |f+0#0000000&|i|r|s|t| |l+0&#ffff4012|i|n|e| @1|s|e|c|o|n|d| +0&#ffffff0|l|i|n|e| @1|t|h|i|r|d| |l|i|n|e| +0&#ffff4012|f+0&#ffffff0|o|u|r|t|h| |l+0&#ffff4012|i|n|e| +0&#ffffff0@23
|
||||
|~+0#4040ff13&| @73
|
||||
|~| @73
|
||||
| +0#0000000&@56|3|,|1| @10|A|l@1|
|
||||
|
@ -460,9 +460,11 @@ func Test_prop_open_line()
|
||||
call assert_equal('nex xtwoxx', getline(2))
|
||||
let exp_first = [deepcopy(expected[0])]
|
||||
let exp_first[0].length = 1
|
||||
let exp_first[0].end = 0
|
||||
call assert_equal(exp_first, prop_list(1))
|
||||
let expected[0].col = 1
|
||||
let expected[0].length = 2
|
||||
let expected[0].start = 0
|
||||
let expected[1].col -= 2
|
||||
call assert_equal(expected, prop_list(2))
|
||||
call DeletePropTypes()
|
||||
@ -575,11 +577,13 @@ func Test_prop_substitute()
|
||||
\ copy(expected_props[3]),
|
||||
\ ]
|
||||
let expected_props[0].length = 5
|
||||
let expected_props[0].end = 0
|
||||
unlet expected_props[3]
|
||||
unlet expected_props[2]
|
||||
call assert_equal(expected_props, prop_list(1))
|
||||
|
||||
let new_props[0].length = 6
|
||||
let new_props[0].start = 0
|
||||
let new_props[1].col = 1
|
||||
let new_props[1].length = 1
|
||||
let new_props[2].col = 3
|
||||
@ -1228,4 +1232,25 @@ func Test_prop_func_invalid_args()
|
||||
call assert_fails("call prop_type_list([])", 'E715:')
|
||||
endfunc
|
||||
|
||||
func Test_split_join()
|
||||
new
|
||||
call prop_type_add('test', {'highlight': 'ErrorMsg'})
|
||||
call setline(1, 'just some text')
|
||||
call prop_add(1, 6, {'length': 4, 'type': 'test'})
|
||||
|
||||
" Split in middle of "some"
|
||||
execute "normal! 8|i\<CR>"
|
||||
call assert_equal([{'id': 0, 'col': 6, 'end': 0, 'type': 'test', 'length': 2, 'start': 1}],
|
||||
\ prop_list(1))
|
||||
call assert_equal([{'id': 0, 'col': 1, 'end': 1, 'type': 'test', 'length': 2, 'start': 0}],
|
||||
\ prop_list(2))
|
||||
|
||||
" Join the two lines back together
|
||||
normal! 1GJ
|
||||
call assert_equal([{'id': 0, 'col': 6, 'end': 1, 'type': 'test', 'length': 5, 'start': 1}], prop_list(1))
|
||||
|
||||
bwipe!
|
||||
call prop_type_delete('test')
|
||||
endfunc
|
||||
|
||||
" vim: shiftwidth=2 sts=2 expandtab
|
||||
|
352
src/textprop.c
352
src/textprop.c
@ -380,6 +380,30 @@ get_text_props(buf_T *buf, linenr_T lnum, char_u **props, int will_change)
|
||||
return (int)(proplen / sizeof(textprop_T));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of text properties on line "lnum" in the current buffer.
|
||||
* When "only_starting" is true only text properties starting in this line will
|
||||
* be considered.
|
||||
*/
|
||||
int
|
||||
count_props(linenr_T lnum, int only_starting)
|
||||
{
|
||||
char_u *props;
|
||||
int proplen = get_text_props(curbuf, lnum, &props, 0);
|
||||
int result = proplen;
|
||||
int i;
|
||||
textprop_T prop;
|
||||
|
||||
if (only_starting)
|
||||
for (i = 0; i < proplen; ++i)
|
||||
{
|
||||
mch_memmove(&prop, props + i * sizeof(prop), sizeof(prop));
|
||||
if (prop.tp_flags & TP_FLAG_CONT_PREV)
|
||||
--result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find text property "type_id" in the visible lines of window "wp".
|
||||
* Match "id" when it is > 0.
|
||||
@ -564,15 +588,15 @@ f_prop_find(typval_T *argvars, typval_T *rettv)
|
||||
dict_T *dict;
|
||||
buf_T *buf = curbuf;
|
||||
dictitem_T *di;
|
||||
int lnum_start;
|
||||
int start_pos_has_prop = 0;
|
||||
int seen_end = 0;
|
||||
int id = -1;
|
||||
int type_id = -1;
|
||||
int skipstart = 0;
|
||||
int lnum = -1;
|
||||
int col = -1;
|
||||
int dir = 1; // 1 = forward, -1 = backward
|
||||
int lnum_start;
|
||||
int start_pos_has_prop = 0;
|
||||
int seen_end = 0;
|
||||
int id = -1;
|
||||
int type_id = -1;
|
||||
int skipstart = 0;
|
||||
int lnum = -1;
|
||||
int col = -1;
|
||||
int dir = 1; // 1 = forward, -1 = backward
|
||||
|
||||
if (argvars[0].v_type != VAR_DICT || argvars[0].vval.v_dict == NULL)
|
||||
{
|
||||
@ -652,7 +676,7 @@ f_prop_find(typval_T *argvars, typval_T *rettv)
|
||||
char_u *text = ml_get_buf(buf, lnum, FALSE);
|
||||
size_t textlen = STRLEN(text) + 1;
|
||||
int count = (int)((buf->b_ml.ml_line_len - textlen)
|
||||
/ sizeof(textprop_T));
|
||||
/ sizeof(textprop_T));
|
||||
int i;
|
||||
textprop_T prop;
|
||||
int prop_start;
|
||||
@ -856,8 +880,8 @@ f_prop_remove(typval_T *argvars, typval_T *rettv)
|
||||
len = STRLEN(text) + 1;
|
||||
if ((size_t)buf->b_ml.ml_line_len > len)
|
||||
{
|
||||
static textprop_T textprop; // static because of alignment
|
||||
unsigned idx;
|
||||
static textprop_T textprop; // static because of alignment
|
||||
unsigned idx;
|
||||
|
||||
for (idx = 0; idx < (buf->b_ml.ml_line_len - len)
|
||||
/ sizeof(textprop_T); ++idx)
|
||||
@ -1212,6 +1236,77 @@ clear_buf_prop_types(buf_T *buf)
|
||||
buf->b_proptypes = NULL;
|
||||
}
|
||||
|
||||
// Struct used to return two values from adjust_prop().
|
||||
typedef struct
|
||||
{
|
||||
int dirty; // if the property was changed
|
||||
int can_drop; // whether after this change, the prop may be removed
|
||||
} adjustres_T;
|
||||
|
||||
/*
|
||||
* Adjust the property for "added" bytes (can be negative) inserted at "col".
|
||||
*
|
||||
* Note that "col" is zero-based, while tp_col is one-based.
|
||||
* Only for the current buffer.
|
||||
* "flags" can have:
|
||||
* APC_SUBSTITUTE: Text is replaced, not inserted.
|
||||
*/
|
||||
static adjustres_T
|
||||
adjust_prop(
|
||||
textprop_T *prop,
|
||||
colnr_T col,
|
||||
int added,
|
||||
int flags)
|
||||
{
|
||||
proptype_T *pt = text_prop_type_by_id(curbuf, prop->tp_type);
|
||||
int start_incl = (pt != NULL
|
||||
&& (pt->pt_flags & PT_FLAG_INS_START_INCL))
|
||||
|| (flags & APC_SUBSTITUTE);
|
||||
int end_incl = (pt != NULL
|
||||
&& (pt->pt_flags & PT_FLAG_INS_END_INCL));
|
||||
// Do not drop zero-width props if they later can increase in
|
||||
// size.
|
||||
int droppable = !(start_incl || end_incl);
|
||||
adjustres_T res = {TRUE, FALSE};
|
||||
|
||||
if (added > 0)
|
||||
{
|
||||
if (col + 1 <= prop->tp_col
|
||||
- (start_incl || (prop->tp_len == 0 && end_incl)))
|
||||
// Change is entirely before the text property: Only shift
|
||||
prop->tp_col += added;
|
||||
else if (col + 1 < prop->tp_col + prop->tp_len + end_incl)
|
||||
// Insertion was inside text property
|
||||
prop->tp_len += added;
|
||||
}
|
||||
else if (prop->tp_col > col + 1)
|
||||
{
|
||||
if (prop->tp_col + added < col + 1)
|
||||
{
|
||||
prop->tp_len += (prop->tp_col - 1 - col) + added;
|
||||
prop->tp_col = col + 1;
|
||||
if (prop->tp_len <= 0)
|
||||
{
|
||||
prop->tp_len = 0;
|
||||
res.can_drop = droppable;
|
||||
}
|
||||
}
|
||||
else
|
||||
prop->tp_col += added;
|
||||
}
|
||||
else if (prop->tp_len > 0 && prop->tp_col + prop->tp_len > col)
|
||||
{
|
||||
int after = col - added - (prop->tp_col - 1 + prop->tp_len);
|
||||
|
||||
prop->tp_len += after > 0 ? added + after : added;
|
||||
res.can_drop = prop->tp_len <= 0 && droppable;
|
||||
}
|
||||
else
|
||||
res.dirty = FALSE;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust the columns of text properties in line "lnum" after position "col" to
|
||||
* shift by "bytes_added" (can be negative).
|
||||
@ -1232,7 +1327,6 @@ adjust_prop_columns(
|
||||
{
|
||||
int proplen;
|
||||
char_u *props;
|
||||
proptype_T *pt;
|
||||
int dirty = FALSE;
|
||||
int ri, wi;
|
||||
size_t textlen;
|
||||
@ -1249,78 +1343,19 @@ adjust_prop_columns(
|
||||
for (ri = 0; ri < proplen; ++ri)
|
||||
{
|
||||
textprop_T prop;
|
||||
int start_incl, end_incl;
|
||||
int can_drop;
|
||||
adjustres_T res;
|
||||
|
||||
mch_memmove(&prop, props + ri * sizeof(textprop_T), sizeof(textprop_T));
|
||||
pt = text_prop_type_by_id(curbuf, prop.tp_type);
|
||||
start_incl = (pt != NULL && (pt->pt_flags & PT_FLAG_INS_START_INCL))
|
||||
|| (flags & APC_SUBSTITUTE);
|
||||
end_incl = (pt != NULL && (pt->pt_flags & PT_FLAG_INS_END_INCL));
|
||||
// Do not drop zero-width props if they later can increase in size
|
||||
can_drop = !(start_incl || end_incl);
|
||||
|
||||
if (bytes_added > 0)
|
||||
mch_memmove(&prop, props + ri * sizeof(prop), sizeof(prop));
|
||||
res = adjust_prop(&prop, col, bytes_added, flags);
|
||||
if (res.dirty)
|
||||
{
|
||||
if (col + 1 <= prop.tp_col
|
||||
- (start_incl || (prop.tp_len == 0 && end_incl)))
|
||||
{
|
||||
// Change is entirely before the text property: Only shift
|
||||
prop.tp_col += bytes_added;
|
||||
// Save for undo if requested and not done yet.
|
||||
if ((flags & APC_SAVE_FOR_UNDO) && !dirty)
|
||||
u_savesub(lnum);
|
||||
dirty = TRUE;
|
||||
}
|
||||
else if (col + 1 < prop.tp_col + prop.tp_len + end_incl)
|
||||
{
|
||||
// Insertion was inside text property
|
||||
prop.tp_len += bytes_added;
|
||||
// Save for undo if requested and not done yet.
|
||||
if ((flags & APC_SAVE_FOR_UNDO) && !dirty)
|
||||
u_savesub(lnum);
|
||||
dirty = TRUE;
|
||||
}
|
||||
}
|
||||
else if (prop.tp_col > col + 1)
|
||||
{
|
||||
int len_changed = FALSE;
|
||||
|
||||
if (prop.tp_col + bytes_added < col + 1)
|
||||
{
|
||||
prop.tp_len += (prop.tp_col - 1 - col) + bytes_added;
|
||||
prop.tp_col = col + 1;
|
||||
len_changed = TRUE;
|
||||
}
|
||||
else
|
||||
prop.tp_col += bytes_added;
|
||||
// Save for undo if requested and not done yet.
|
||||
if ((flags & APC_SAVE_FOR_UNDO) && !dirty)
|
||||
u_savesub(lnum);
|
||||
dirty = TRUE;
|
||||
if (len_changed && prop.tp_len <= 0)
|
||||
{
|
||||
prop.tp_len = 0;
|
||||
if (can_drop)
|
||||
continue; // drop this text property
|
||||
}
|
||||
}
|
||||
else if (prop.tp_len > 0 && prop.tp_col + prop.tp_len > col)
|
||||
{
|
||||
int after = col - bytes_added - (prop.tp_col - 1 + prop.tp_len);
|
||||
|
||||
if (after > 0)
|
||||
prop.tp_len += bytes_added + after;
|
||||
else
|
||||
prop.tp_len += bytes_added;
|
||||
// Save for undo if requested and not done yet.
|
||||
if ((flags & APC_SAVE_FOR_UNDO) && !dirty)
|
||||
u_savesub(lnum);
|
||||
dirty = TRUE;
|
||||
if (prop.tp_len <= 0 && can_drop)
|
||||
continue; // drop this text property
|
||||
}
|
||||
|
||||
if (res.can_drop)
|
||||
continue; // Drop this text property
|
||||
mch_memmove(props + wi * sizeof(textprop_T), &prop, sizeof(textprop_T));
|
||||
++wi;
|
||||
}
|
||||
@ -1372,26 +1407,38 @@ adjust_props_for_split(
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
textprop_T prop;
|
||||
textprop_T *p;
|
||||
proptype_T *pt;
|
||||
int start_incl, end_incl;
|
||||
int cont_prev, cont_next;
|
||||
|
||||
// copy the prop to an aligned structure
|
||||
mch_memmove(&prop, props + i * sizeof(textprop_T), sizeof(textprop_T));
|
||||
|
||||
if (prop.tp_col < kept && ga_grow(&prevprop, 1) == OK)
|
||||
pt = text_prop_type_by_id(curbuf, prop.tp_type);
|
||||
start_incl = (pt != NULL && (pt->pt_flags & PT_FLAG_INS_START_INCL));
|
||||
end_incl = (pt != NULL && (pt->pt_flags & PT_FLAG_INS_END_INCL));
|
||||
cont_prev = prop.tp_col + !start_incl <= kept;
|
||||
cont_next = skipped <= prop.tp_col + prop.tp_len - !end_incl;
|
||||
|
||||
if (cont_prev && ga_grow(&prevprop, 1) == OK)
|
||||
{
|
||||
p = ((textprop_T *)prevprop.ga_data) + prevprop.ga_len;
|
||||
textprop_T *p = ((textprop_T *)prevprop.ga_data) + prevprop.ga_len;
|
||||
|
||||
*p = prop;
|
||||
++prevprop.ga_len;
|
||||
if (p->tp_col + p->tp_len >= kept)
|
||||
p->tp_len = kept - p->tp_col;
|
||||
++prevprop.ga_len;
|
||||
if (cont_next)
|
||||
p->tp_flags |= TP_FLAG_CONT_NEXT;
|
||||
}
|
||||
|
||||
// Only add the property to the next line if the length is bigger than
|
||||
// zero.
|
||||
if (prop.tp_col + prop.tp_len > skipped && ga_grow(&nextprop, 1) == OK)
|
||||
if (cont_next && ga_grow(&nextprop, 1) == OK)
|
||||
{
|
||||
p = ((textprop_T *)nextprop.ga_data) + nextprop.ga_len;
|
||||
textprop_T *p = ((textprop_T *)nextprop.ga_data) + nextprop.ga_len;
|
||||
*p = prop;
|
||||
++nextprop.ga_len;
|
||||
if (p->tp_col > skipped)
|
||||
p->tp_col -= skipped - 1;
|
||||
else
|
||||
@ -1399,7 +1446,8 @@ adjust_props_for_split(
|
||||
p->tp_len -= skipped - p->tp_col;
|
||||
p->tp_col = 1;
|
||||
}
|
||||
++nextprop.ga_len;
|
||||
if (cont_prev)
|
||||
p->tp_flags |= TP_FLAG_CONT_PREV;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1412,111 +1460,63 @@ adjust_props_for_split(
|
||||
}
|
||||
|
||||
/*
|
||||
* Line "lnum" has been joined and will end up at column "col" in the new line.
|
||||
* "removed" bytes have been removed from the start of the line, properties
|
||||
* there are to be discarded.
|
||||
* Move the adjusted text properties to an allocated string, store it in
|
||||
* "prop_line" and adjust the columns.
|
||||
* Prepend properties of joined line "lnum" to "new_props".
|
||||
*/
|
||||
void
|
||||
adjust_props_for_join(
|
||||
prepend_joined_props(
|
||||
char_u *new_props,
|
||||
int propcount,
|
||||
int *props_remaining,
|
||||
linenr_T lnum,
|
||||
textprop_T **prop_line,
|
||||
int *prop_length,
|
||||
int add_all,
|
||||
long col,
|
||||
int removed)
|
||||
{
|
||||
int proplen;
|
||||
char_u *props;
|
||||
int ri;
|
||||
int wi = 0;
|
||||
char_u *props;
|
||||
int proplen = get_text_props(curbuf, lnum, &props, FALSE);
|
||||
int i;
|
||||
|
||||
proplen = get_text_props(curbuf, lnum, &props, FALSE);
|
||||
if (proplen > 0)
|
||||
for (i = proplen; i-- > 0; )
|
||||
{
|
||||
*prop_line = ALLOC_MULT(textprop_T, proplen);
|
||||
if (*prop_line != NULL)
|
||||
{
|
||||
for (ri = 0; ri < proplen; ++ri)
|
||||
{
|
||||
textprop_T *cp = *prop_line + wi;
|
||||
textprop_T prop;
|
||||
int end;
|
||||
|
||||
mch_memmove(cp, props + ri * sizeof(textprop_T),
|
||||
sizeof(textprop_T));
|
||||
if (cp->tp_col + cp->tp_len > removed)
|
||||
mch_memmove(&prop, props + i * sizeof(prop), sizeof(prop));
|
||||
end = !(prop.tp_flags & TP_FLAG_CONT_NEXT);
|
||||
|
||||
adjust_prop(&prop, 0, -removed, 0); // Remove leading spaces
|
||||
adjust_prop(&prop, -1, col, 0); // Make line start at its final colum
|
||||
|
||||
if (add_all || end)
|
||||
mch_memmove(new_props + --(*props_remaining) * sizeof(prop),
|
||||
&prop, sizeof(prop));
|
||||
else
|
||||
{
|
||||
int j;
|
||||
int found = FALSE;
|
||||
|
||||
// Search for continuing prop.
|
||||
for (j = *props_remaining; j < propcount; ++j)
|
||||
{
|
||||
textprop_T op;
|
||||
|
||||
mch_memmove(&op, new_props + j * sizeof(op), sizeof(op));
|
||||
if ((op.tp_flags & TP_FLAG_CONT_PREV)
|
||||
&& op.tp_id == prop.tp_id && op.tp_type == prop.tp_type)
|
||||
{
|
||||
if (cp->tp_col > removed)
|
||||
cp->tp_col += col;
|
||||
else
|
||||
{
|
||||
// property was partly deleted, make it shorter
|
||||
cp->tp_len -= removed - cp->tp_col;
|
||||
cp->tp_col = col;
|
||||
}
|
||||
++wi;
|
||||
found = TRUE;
|
||||
op.tp_len += op.tp_col - prop.tp_col;
|
||||
op.tp_col = prop.tp_col;
|
||||
// Start/end is taken care of when deleting joined lines
|
||||
op.tp_flags = prop.tp_flags;
|
||||
mch_memmove(new_props + j * sizeof(op), &op, sizeof(op));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
internal_error("text property above joined line not found");
|
||||
}
|
||||
*prop_length = wi;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* After joining lines: concatenate the text and the properties of all joined
|
||||
* lines into one line and replace the line.
|
||||
*/
|
||||
void
|
||||
join_prop_lines(
|
||||
linenr_T lnum,
|
||||
char_u *newp,
|
||||
textprop_T **prop_lines,
|
||||
int *prop_lengths,
|
||||
int count)
|
||||
{
|
||||
size_t proplen = 0;
|
||||
size_t oldproplen;
|
||||
char_u *props;
|
||||
int i;
|
||||
size_t len;
|
||||
char_u *line;
|
||||
size_t l;
|
||||
|
||||
for (i = 0; i < count - 1; ++i)
|
||||
proplen += prop_lengths[i];
|
||||
if (proplen == 0)
|
||||
{
|
||||
ml_replace(lnum, newp, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
// get existing properties of the joined line
|
||||
oldproplen = get_text_props(curbuf, lnum, &props, FALSE);
|
||||
|
||||
len = STRLEN(newp) + 1;
|
||||
line = alloc(len + (oldproplen + proplen) * sizeof(textprop_T));
|
||||
if (line == NULL)
|
||||
return;
|
||||
mch_memmove(line, newp, len);
|
||||
if (oldproplen > 0)
|
||||
{
|
||||
l = oldproplen * sizeof(textprop_T);
|
||||
mch_memmove(line + len, props, l);
|
||||
len += l;
|
||||
}
|
||||
|
||||
for (i = 0; i < count - 1; ++i)
|
||||
if (prop_lines[i] != NULL)
|
||||
{
|
||||
l = prop_lengths[i] * sizeof(textprop_T);
|
||||
mch_memmove(line + len, prop_lines[i], l);
|
||||
len += l;
|
||||
vim_free(prop_lines[i]);
|
||||
}
|
||||
|
||||
ml_replace_len(lnum, line, (colnr_T)len, TRUE, FALSE);
|
||||
vim_free(newp);
|
||||
vim_free(prop_lines);
|
||||
vim_free(prop_lengths);
|
||||
}
|
||||
|
||||
#endif // FEAT_PROP_POPUP
|
||||
|
@ -746,6 +746,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
845,
|
||||
/**/
|
||||
844,
|
||||
/**/
|
||||
|
Loading…
x
Reference in New Issue
Block a user