forked from aniani/vim
patch 8.1.0654: when deleting a line text property flags are not adjusted
Problem: When deleting a line text property flags are not adjusted. Solution: Adjust text property flags in preceding and following lines.
This commit is contained in:
150
src/memline.c
150
src/memline.c
@@ -3214,6 +3214,104 @@ ml_replace_len(linenr_T lnum, char_u *line_arg, colnr_T len_arg, int copy)
|
||||
return OK;
|
||||
}
|
||||
|
||||
#ifdef FEAT_TEXT_PROP
|
||||
/*
|
||||
* Adjust text properties in line "lnum" for a deleted line.
|
||||
* When "above" is true this is the line above the deleted line.
|
||||
* "del_props" are the properties of the deleted line.
|
||||
*/
|
||||
static void
|
||||
adjust_text_props_for_delete(
|
||||
buf_T *buf,
|
||||
linenr_T lnum,
|
||||
char_u *del_props,
|
||||
int del_props_len,
|
||||
int above)
|
||||
{
|
||||
int did_get_line = FALSE;
|
||||
int done_del;
|
||||
int done_this;
|
||||
textprop_T prop_del;
|
||||
textprop_T prop_this;
|
||||
bhdr_T *hp;
|
||||
DATA_BL *dp;
|
||||
int idx;
|
||||
int line_start;
|
||||
long line_size;
|
||||
int this_props_len;
|
||||
char_u *text;
|
||||
size_t textlen;
|
||||
int found;
|
||||
|
||||
for (done_del = 0; done_del < del_props_len; done_del += sizeof(textprop_T))
|
||||
{
|
||||
mch_memmove(&prop_del, del_props + done_del, sizeof(textprop_T));
|
||||
if ((above && (prop_del.tp_flags & TP_FLAG_CONT_PREV)
|
||||
&& !(prop_del.tp_flags & TP_FLAG_CONT_NEXT))
|
||||
|| (!above && (prop_del.tp_flags & TP_FLAG_CONT_NEXT)
|
||||
&& !(prop_del.tp_flags & TP_FLAG_CONT_PREV)))
|
||||
{
|
||||
if (!did_get_line)
|
||||
{
|
||||
did_get_line = TRUE;
|
||||
if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL)
|
||||
return;
|
||||
|
||||
dp = (DATA_BL *)(hp->bh_data);
|
||||
idx = lnum - buf->b_ml.ml_locked_low;
|
||||
line_start = ((dp->db_index[idx]) & DB_INDEX_MASK);
|
||||
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;
|
||||
text = (char_u *)dp + line_start;
|
||||
textlen = STRLEN(text) + 1;
|
||||
if ((long)textlen >= line_size)
|
||||
{
|
||||
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 - textlen;
|
||||
}
|
||||
|
||||
found = FALSE;
|
||||
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
|
||||
&& 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");
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
if (above)
|
||||
internal_error("text property above deleted line not found");
|
||||
else
|
||||
internal_error("text property below deleted line not found");
|
||||
}
|
||||
|
||||
buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Delete line "lnum" in the current buffer.
|
||||
* When "message" is TRUE may give a "No lines in buffer" message.
|
||||
@@ -3245,6 +3343,11 @@ ml_delete_int(buf_T *buf, linenr_T lnum, int message)
|
||||
int line_start;
|
||||
long line_size;
|
||||
int i;
|
||||
int ret = FAIL;
|
||||
#ifdef FEAT_TEXT_PROP
|
||||
char_u *textprop_save = NULL;
|
||||
int textprop_save_len;
|
||||
#endif
|
||||
|
||||
if (lnum < 1 || lnum > buf->b_ml.ml_line_count)
|
||||
return FAIL;
|
||||
@@ -3272,9 +3375,9 @@ ml_delete_int(buf_T *buf, linenr_T lnum, int message)
|
||||
}
|
||||
|
||||
/*
|
||||
* find the data block containing the line
|
||||
* This also fills the stack with the blocks from the root to the data block
|
||||
* This also releases any locked block.
|
||||
* Find the data block containing the line.
|
||||
* This also fills the stack with the blocks from the root to the data block.
|
||||
* This also releases any locked block..
|
||||
*/
|
||||
mfp = buf->b_ml.ml_mfp;
|
||||
if (mfp == NULL)
|
||||
@@ -3301,6 +3404,21 @@ ml_delete_int(buf_T *buf, linenr_T lnum, int message)
|
||||
if (netbeans_active())
|
||||
netbeans_removed(buf, lnum, 0, (long)line_size);
|
||||
#endif
|
||||
#ifdef FEAT_TEXT_PROP
|
||||
// If there are text properties, make a copy, so that we can update
|
||||
// properties in preceding and following lines.
|
||||
if (buf->b_has_textprop)
|
||||
{
|
||||
size_t textlen = STRLEN((char_u *)dp + line_start) + 1;
|
||||
|
||||
if ((long)textlen < line_size)
|
||||
{
|
||||
textprop_save_len = line_size - textlen;
|
||||
textprop_save = vim_memsave((char_u *)dp + line_start + textlen,
|
||||
textprop_save_len);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* special case: If there is only one line in the data block it becomes empty.
|
||||
@@ -3322,13 +3440,13 @@ ml_delete_int(buf_T *buf, linenr_T lnum, int message)
|
||||
ip = &(buf->b_ml.ml_stack[stack_idx]);
|
||||
idx = ip->ip_index;
|
||||
if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
|
||||
return FAIL;
|
||||
goto theend;
|
||||
pp = (PTR_BL *)(hp->bh_data); /* must be pointer block */
|
||||
if (pp->pb_id != PTR_ID)
|
||||
{
|
||||
IEMSG(_("E317: pointer block id wrong 4"));
|
||||
mf_put(mfp, hp, FALSE, FALSE);
|
||||
return FAIL;
|
||||
goto theend;
|
||||
}
|
||||
count = --(pp->pb_count);
|
||||
if (count == 0) /* the pointer block becomes empty! */
|
||||
@@ -3384,11 +3502,25 @@ ml_delete_int(buf_T *buf, linenr_T lnum, int message)
|
||||
#ifdef FEAT_BYTEOFF
|
||||
ml_updatechunk(buf, lnum, line_size, ML_CHNK_DELLINE);
|
||||
#endif
|
||||
return OK;
|
||||
ret = OK;
|
||||
|
||||
theend:
|
||||
#ifdef FEAT_TEXT_PROP
|
||||
if (textprop_save != NULL)
|
||||
{
|
||||
// Adjust text properties in the line above and below.
|
||||
if (lnum > 1)
|
||||
adjust_text_props_for_delete(buf, lnum - 1, textprop_save, textprop_save_len, TRUE);
|
||||
if (lnum <= buf->b_ml.ml_line_count)
|
||||
adjust_text_props_for_delete(buf, lnum, textprop_save, textprop_save_len, FALSE);
|
||||
}
|
||||
vim_free(textprop_save);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* set the B_MARKED flag for line 'lnum'
|
||||
* set the DB_MARKED flag for line 'lnum'
|
||||
*/
|
||||
void
|
||||
ml_setmarked(linenr_T lnum)
|
||||
@@ -3417,7 +3549,7 @@ ml_setmarked(linenr_T lnum)
|
||||
}
|
||||
|
||||
/*
|
||||
* find the first line with its B_MARKED flag set
|
||||
* find the first line with its DB_MARKED flag set
|
||||
*/
|
||||
linenr_T
|
||||
ml_firstmarked(void)
|
||||
@@ -3650,7 +3782,7 @@ ml_new_ptr(memfile_T *mfp)
|
||||
}
|
||||
|
||||
/*
|
||||
* lookup line 'lnum' in a memline
|
||||
* Lookup line 'lnum' in a memline.
|
||||
*
|
||||
* action: if ML_DELETE or ML_INSERT the line count is updated while searching
|
||||
* if ML_FLUSH only flush a locked block
|
||||
|
14
src/misc2.c
14
src/misc2.c
@@ -1350,6 +1350,20 @@ vim_strnsave(char_u *string, int len)
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy "p[len]" into allocated memory, ignoring NUL characters.
|
||||
* Returns NULL when out of memory.
|
||||
*/
|
||||
char_u *
|
||||
vim_memsave(char_u *p, int len)
|
||||
{
|
||||
char_u *ret = alloc((unsigned)len);
|
||||
|
||||
if (ret != NULL)
|
||||
mch_memmove(ret, p, (size_t)len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Same as vim_strsave(), but any characters found in esc_chars are preceded
|
||||
* by a backslash.
|
||||
|
@@ -24,7 +24,7 @@ int alloc_does_fail(long_u size);
|
||||
char_u *alloc(unsigned size);
|
||||
char_u *alloc_id(unsigned size, alloc_id_T id);
|
||||
char_u *alloc_clear(unsigned size);
|
||||
char_u * alloc_clear_id(unsigned size, alloc_id_T id);
|
||||
char_u *alloc_clear_id(unsigned size, alloc_id_T id);
|
||||
char_u *alloc_check(unsigned size);
|
||||
char_u *lalloc_clear(long_u size, int message);
|
||||
char_u *lalloc(long_u size, int message);
|
||||
@@ -34,6 +34,7 @@ void do_outofmem_msg(long_u size);
|
||||
void free_all_mem(void);
|
||||
char_u *vim_strsave(char_u *string);
|
||||
char_u *vim_strnsave(char_u *string, int len);
|
||||
char_u *vim_memsave(char_u *p, int len);
|
||||
char_u *vim_strsave_escaped(char_u *string, char_u *esc_chars);
|
||||
char_u *vim_strsave_escaped_ext(char_u *string, char_u *esc_chars, int cc, int bsl);
|
||||
int csh_like_shell(void);
|
||||
|
@@ -197,6 +197,16 @@ func Test_prop_clear_buf()
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
" Setup a three line prop in lines 2 - 4.
|
||||
" Add short props in line 1 and 5.
|
||||
func Setup_three_line_prop()
|
||||
new
|
||||
call setline(1, ['one', 'twotwo', 'three', 'fourfour', 'five'])
|
||||
call prop_add(1, 2, {'length': 1, 'type': 'comment'})
|
||||
call prop_add(2, 4, {'end_lnum': 4, 'end_col': 5, 'type': 'comment'})
|
||||
call prop_add(5, 2, {'length': 1, 'type': 'comment'})
|
||||
endfunc
|
||||
|
||||
func Test_prop_multiline()
|
||||
call prop_type_add('comment', {'highlight': 'Directory'})
|
||||
new
|
||||
@@ -223,6 +233,30 @@ func Test_prop_multiline()
|
||||
call prop_clear(1, 3)
|
||||
|
||||
bwipe!
|
||||
|
||||
" Test deleting the first line with a prop.
|
||||
call Setup_three_line_prop()
|
||||
let expect2 = {'col': 4, 'length': 4, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0}
|
||||
call assert_equal([expect2], prop_list(2))
|
||||
2del
|
||||
let expect_short = {'col': 2, 'length': 1, 'type': 'comment', 'start': 1, 'end': 1, 'id': 0}
|
||||
call assert_equal([expect_short], prop_list(1))
|
||||
let expect2 = {'col': 1, 'length': 6, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0}
|
||||
call assert_equal([expect2], prop_list(2))
|
||||
bwipe!
|
||||
|
||||
" Test deleting the last line with a prop.
|
||||
call Setup_three_line_prop()
|
||||
let expect3 = {'col': 1, 'length': 6, 'type': 'comment', 'start': 0, 'end': 0, 'id': 0}
|
||||
call assert_equal([expect3], prop_list(3))
|
||||
let expect4 = {'col': 1, 'length': 5, 'type': 'comment', 'start': 0, 'end': 1, 'id': 0}
|
||||
call assert_equal([expect4], prop_list(4))
|
||||
4del
|
||||
let expect3 = {'col': 1, 'length': 6, 'type': 'comment', 'start': 0, 'end': 1, 'id': 0}
|
||||
call assert_equal([expect3], prop_list(3))
|
||||
call assert_equal([expect_short], prop_list(4))
|
||||
bwipe!
|
||||
|
||||
call prop_type_delete('comment')
|
||||
endfunc
|
||||
|
||||
|
@@ -799,6 +799,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
654,
|
||||
/**/
|
||||
653,
|
||||
/**/
|
||||
|
Reference in New Issue
Block a user