mirror of
https://github.com/vim/vim.git
synced 2025-09-23 03:43:49 -04:00
patch 8.1.1335: listener callback is called after inserting text
Problem: Listener callback is called after inserting text. Solution: Flush the changes before inserting or deleting a line. Store changes per buffer.
This commit is contained in:
135
src/change.c
135
src/change.c
@@ -152,11 +152,72 @@ changed_internal(void)
|
||||
}
|
||||
|
||||
#ifdef FEAT_EVAL
|
||||
static list_T *recorded_changes = NULL;
|
||||
static long next_listener_id = 0;
|
||||
|
||||
/*
|
||||
* Check if the change at "lnum" / "col" is above or overlaps with an existing
|
||||
* changed. If above then flush changes and invoke listeners.
|
||||
* If "merge" is TRUE do the merge.
|
||||
* Returns TRUE if the change was merged.
|
||||
*/
|
||||
static int
|
||||
check_recorded_changes(
|
||||
buf_T *buf,
|
||||
linenr_T lnum,
|
||||
colnr_T col,
|
||||
linenr_T lnume,
|
||||
long xtra,
|
||||
int merge)
|
||||
{
|
||||
if (buf->b_recorded_changes != NULL && xtra != 0)
|
||||
{
|
||||
listitem_T *li;
|
||||
linenr_T nr;
|
||||
|
||||
for (li = buf->b_recorded_changes->lv_first; li != NULL;
|
||||
li = li->li_next)
|
||||
{
|
||||
nr = (linenr_T)dict_get_number(
|
||||
li->li_tv.vval.v_dict, (char_u *)"lnum");
|
||||
if (nr >= lnum || nr > lnume)
|
||||
{
|
||||
if (li->li_next == NULL && lnum == nr
|
||||
&& col + 1 == (colnr_T)dict_get_number(
|
||||
li->li_tv.vval.v_dict, (char_u *)"col"))
|
||||
{
|
||||
if (merge)
|
||||
{
|
||||
dictitem_T *di;
|
||||
|
||||
// Same start point and nothing is following, entries
|
||||
// can be merged.
|
||||
di = dict_find(li->li_tv.vval.v_dict,
|
||||
(char_u *)"end", -1);
|
||||
nr = tv_get_number(&di->di_tv);
|
||||
if (lnume > nr)
|
||||
di->di_tv.vval.v_number = lnume;
|
||||
di = dict_find(li->li_tv.vval.v_dict,
|
||||
(char_u *)"added", -1);
|
||||
di->di_tv.vval.v_number += xtra;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// the current change is going to make the line number in
|
||||
// the older change invalid, flush now
|
||||
invoke_listeners(curbuf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Record a change for listeners added with listener_add().
|
||||
* Always for the current buffer.
|
||||
*/
|
||||
static void
|
||||
may_record_change(
|
||||
@@ -172,50 +233,16 @@ may_record_change(
|
||||
|
||||
// If the new change is going to change the line numbers in already listed
|
||||
// changes, then flush.
|
||||
if (recorded_changes != NULL && xtra != 0)
|
||||
if (check_recorded_changes(curbuf, lnum, col, lnume, xtra, TRUE))
|
||||
return;
|
||||
|
||||
if (curbuf->b_recorded_changes == NULL)
|
||||
{
|
||||
listitem_T *li;
|
||||
linenr_T nr;
|
||||
|
||||
for (li = recorded_changes->lv_first; li != NULL; li = li->li_next)
|
||||
{
|
||||
nr = (linenr_T)dict_get_number(
|
||||
li->li_tv.vval.v_dict, (char_u *)"lnum");
|
||||
if (nr >= lnum || nr > lnume)
|
||||
{
|
||||
if (li->li_next == NULL && lnum == nr
|
||||
&& col + 1 == (colnr_T)dict_get_number(
|
||||
li->li_tv.vval.v_dict, (char_u *)"col"))
|
||||
{
|
||||
dictitem_T *di;
|
||||
|
||||
// Same start point and nothing is following, entries can
|
||||
// be merged.
|
||||
di = dict_find(li->li_tv.vval.v_dict, (char_u *)"end", -1);
|
||||
nr = tv_get_number(&di->di_tv);
|
||||
if (lnume > nr)
|
||||
di->di_tv.vval.v_number = lnume;
|
||||
di = dict_find(li->li_tv.vval.v_dict,
|
||||
(char_u *)"added", -1);
|
||||
di->di_tv.vval.v_number += xtra;
|
||||
return;
|
||||
}
|
||||
|
||||
// the current change is going to make the line number in the
|
||||
// older change invalid, flush now
|
||||
invoke_listeners(curbuf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (recorded_changes == NULL)
|
||||
{
|
||||
recorded_changes = list_alloc();
|
||||
if (recorded_changes == NULL) // out of memory
|
||||
curbuf->b_recorded_changes = list_alloc();
|
||||
if (curbuf->b_recorded_changes == NULL) // out of memory
|
||||
return;
|
||||
++recorded_changes->lv_refcount;
|
||||
recorded_changes->lv_lock = VAR_FIXED;
|
||||
++curbuf->b_recorded_changes->lv_refcount;
|
||||
curbuf->b_recorded_changes->lv_lock = VAR_FIXED;
|
||||
}
|
||||
|
||||
dict = dict_alloc();
|
||||
@@ -226,7 +253,7 @@ may_record_change(
|
||||
dict_add_number(dict, "added", (varnumber_T)xtra);
|
||||
dict_add_number(dict, "col", (varnumber_T)col + 1);
|
||||
|
||||
list_append_dict(recorded_changes, dict);
|
||||
list_append_dict(curbuf->b_recorded_changes, dict);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -316,6 +343,16 @@ f_listener_remove(typval_T *argvars, typval_T *rettv UNUSED)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called before inserting a line above "lnum"/"lnum3" or deleting line "lnum"
|
||||
* to "lnume".
|
||||
*/
|
||||
void
|
||||
may_invoke_listeners(buf_T *buf, linenr_T lnum, linenr_T lnume, int added)
|
||||
{
|
||||
check_recorded_changes(buf, lnum, 0, lnume, added, FALSE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when a sequence of changes is done: invoke listeners added with
|
||||
* listener_add().
|
||||
@@ -332,7 +369,7 @@ invoke_listeners(buf_T *buf)
|
||||
linenr_T end = 0;
|
||||
linenr_T added = 0;
|
||||
|
||||
if (recorded_changes == NULL // nothing changed
|
||||
if (buf->b_recorded_changes == NULL // nothing changed
|
||||
|| buf->b_listener == NULL) // no listeners
|
||||
return;
|
||||
|
||||
@@ -340,7 +377,7 @@ invoke_listeners(buf_T *buf)
|
||||
argv[0].vval.v_number = buf->b_fnum; // a:bufnr
|
||||
|
||||
|
||||
for (li = recorded_changes->lv_first; li != NULL; li = li->li_next)
|
||||
for (li = buf->b_recorded_changes->lv_first; li != NULL; li = li->li_next)
|
||||
{
|
||||
varnumber_T lnum;
|
||||
|
||||
@@ -360,7 +397,7 @@ invoke_listeners(buf_T *buf)
|
||||
argv[3].vval.v_number = added;
|
||||
|
||||
argv[4].v_type = VAR_LIST;
|
||||
argv[4].vval.v_list = recorded_changes;
|
||||
argv[4].vval.v_list = buf->b_recorded_changes;
|
||||
++textlock;
|
||||
|
||||
for (lnr = buf->b_listener; lnr != NULL; lnr = lnr->lr_next)
|
||||
@@ -371,8 +408,8 @@ invoke_listeners(buf_T *buf)
|
||||
}
|
||||
|
||||
--textlock;
|
||||
list_unref(recorded_changes);
|
||||
recorded_changes = NULL;
|
||||
list_unref(buf->b_recorded_changes);
|
||||
buf->b_recorded_changes = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@@ -2790,6 +2790,12 @@ ml_append_int(
|
||||
if (len == 0)
|
||||
len = (colnr_T)STRLEN(line) + 1; // space needed for the text
|
||||
|
||||
#ifdef FEAT_EVAL
|
||||
// When inserting above recorded changes: flush the changes before changing
|
||||
// the text.
|
||||
may_invoke_listeners(buf, lnum + 1, lnum + 1, 1);
|
||||
#endif
|
||||
|
||||
#ifdef FEAT_TEXT_PROP
|
||||
if (curbuf->b_has_textprop && lnum > 0)
|
||||
// Add text properties that continue from the previous line.
|
||||
@@ -3526,6 +3532,11 @@ ml_delete_int(buf_T *buf, linenr_T lnum, int message)
|
||||
if (lnum < 1 || lnum > buf->b_ml.ml_line_count)
|
||||
return FAIL;
|
||||
|
||||
#ifdef FEAT_EVAL
|
||||
// When inserting above recorded changes: flush the changes before changing
|
||||
// the text.
|
||||
may_invoke_listeners(buf, lnum, lnum + 1, -1);
|
||||
#endif
|
||||
if (lowest_marked && lowest_marked > lnum)
|
||||
lowest_marked--;
|
||||
|
||||
|
@@ -5,6 +5,7 @@ void changed_internal(void);
|
||||
void f_listener_add(typval_T *argvars, typval_T *rettv);
|
||||
void f_listener_flush(typval_T *argvars, typval_T *rettv);
|
||||
void f_listener_remove(typval_T *argvars, typval_T *rettv);
|
||||
void may_invoke_listeners(buf_T *buf, linenr_T lnum, linenr_T lnume, int added);
|
||||
void invoke_listeners(buf_T *buf);
|
||||
void changed_bytes(linenr_T lnum, colnr_T col);
|
||||
void inserted_bytes(linenr_T lnum, colnr_T col, int added);
|
||||
|
@@ -2439,6 +2439,7 @@ struct file_buffer
|
||||
dict_T *b_vars; /* internal variables, local to buffer */
|
||||
|
||||
listener_T *b_listener;
|
||||
list_T *b_recorded_changes;
|
||||
#endif
|
||||
#ifdef FEAT_TEXT_PROP
|
||||
int b_has_textprop; // TRUE when text props were added
|
||||
|
@@ -1,6 +1,8 @@
|
||||
" tests for listener_add() and listener_remove()
|
||||
|
||||
func s:StoreList(l)
|
||||
func s:StoreList(s, l)
|
||||
let s:start = a:s
|
||||
let s:text = getline(a:s)
|
||||
let s:list = a:l
|
||||
endfunc
|
||||
|
||||
@@ -17,7 +19,7 @@ func Test_listening()
|
||||
new
|
||||
call setline(1, ['one', 'two'])
|
||||
let s:list = []
|
||||
let id = listener_add({b, s, e, a, l -> s:StoreList(l)})
|
||||
let id = listener_add({b, s, e, a, l -> s:StoreList(s, l)})
|
||||
call setline(1, 'one one')
|
||||
call listener_flush()
|
||||
call assert_equal([{'lnum': 1, 'end': 2, 'col': 1, 'added': 0}], s:list)
|
||||
@@ -66,8 +68,10 @@ func Test_listening()
|
||||
" an insert just above a previous change that was the last one gets merged
|
||||
call setline(1, ['one one', 'two'])
|
||||
call listener_flush()
|
||||
let s:list = []
|
||||
call setline(2, 'something')
|
||||
call append(1, 'two two')
|
||||
call assert_equal([], s:list)
|
||||
call listener_flush()
|
||||
call assert_equal([{'lnum': 2, 'end': 3, 'col': 1, 'added': 1}], s:list)
|
||||
|
||||
@@ -77,8 +81,32 @@ func Test_listening()
|
||||
call setline(2, 'something')
|
||||
call append(0, 'two two')
|
||||
call assert_equal([{'lnum': 2, 'end': 3, 'col': 1, 'added': 0}], s:list)
|
||||
call assert_equal('something', s:text)
|
||||
call listener_flush()
|
||||
call assert_equal([{'lnum': 1, 'end': 1, 'col': 1, 'added': 1}], s:list)
|
||||
call assert_equal('two two', s:text)
|
||||
|
||||
" a delete at a previous change that was the last one gets merged
|
||||
call setline(1, ['one one', 'two'])
|
||||
call listener_flush()
|
||||
let s:list = []
|
||||
call setline(2, 'something')
|
||||
2del
|
||||
call assert_equal([], s:list)
|
||||
call listener_flush()
|
||||
call assert_equal([{'lnum': 2, 'end': 3, 'col': 1, 'added': -1}], s:list)
|
||||
|
||||
" a delete above a previous change causes a flush
|
||||
call setline(1, ['one one', 'two'])
|
||||
call listener_flush()
|
||||
call setline(2, 'another')
|
||||
1del
|
||||
call assert_equal([{'lnum': 2, 'end': 3, 'col': 1, 'added': 0}], s:list)
|
||||
call assert_equal(2, s:start)
|
||||
call assert_equal('another', s:text)
|
||||
call listener_flush()
|
||||
call assert_equal([{'lnum': 1, 'end': 2, 'col': 1, 'added': -1}], s:list)
|
||||
call assert_equal('another', s:text)
|
||||
|
||||
" the "o" command first adds an empty line and then changes it
|
||||
%del
|
||||
|
@@ -767,6 +767,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1335,
|
||||
/**/
|
||||
1334,
|
||||
/**/
|
||||
|
Reference in New Issue
Block a user