Patch 8.1.1335
Problem: Listener callback is called after inserting text.
Solution: Flush the changes before inserting or deleting a line. Store
changes per buffer.
Files: src/change.c, src/proto/change.pro, src/memline.c,
src/structs.h, src/testdir/test_listener.vim
*** ../vim-8.1.1334/src/change.c 2019-05-15 22:45:33.956067651 +0200
--- src/change.c 2019-05-16 21:44:23.932078618 +0200
***************
*** 152,183 ****
}
#ifdef FEAT_EVAL
- static list_T *recorded_changes = NULL;
static long next_listener_id = 0;
/*
! * Record a change for listeners added with listener_add().
*/
! static void
! may_record_change(
! linenr_T lnum,
! colnr_T col,
! linenr_T lnume,
! long xtra)
{
! dict_T *dict;
!
! if (curbuf->b_listener == NULL)
! return;
!
! // If the new change is going to change the line numbers in already listed
! // changes, then flush.
! if (recorded_changes != NULL && xtra != 0)
{
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");
--- 152,181 ----
}
#ifdef FEAT_EVAL
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");
***************
*** 187,221 ****
&& 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
return;
! ++recorded_changes->lv_refcount;
! recorded_changes->lv_lock = VAR_FIXED;
}
dict = dict_alloc();
--- 185,248 ----
&& 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(
! linenr_T lnum,
! colnr_T col,
! linenr_T lnume,
! long xtra)
! {
! dict_T *dict;
!
! if (curbuf->b_listener == NULL)
! return;
!
! // If the new change is going to change the line numbers in already listed
! // changes, then flush.
! if (check_recorded_changes(curbuf, lnum, col, lnume, xtra, TRUE))
! return;
!
! if (curbuf->b_recorded_changes == NULL)
{
! curbuf->b_recorded_changes = list_alloc();
! if (curbuf->b_recorded_changes == NULL) // out of memory
return;
! ++curbuf->b_recorded_changes->lv_refcount;
! curbuf->b_recorded_changes->lv_lock = VAR_FIXED;
}
dict = dict_alloc();
***************
*** 226,232 ****
dict_add_number(dict, "added", (varnumber_T)xtra);
dict_add_number(dict, "col", (varnumber_T)col + 1);
! list_append_dict(recorded_changes, dict);
}
/*
--- 253,259 ----
dict_add_number(dict, "added", (varnumber_T)xtra);
dict_add_number(dict, "col", (varnumber_T)col + 1);
! list_append_dict(curbuf->b_recorded_changes, dict);
}
/*
***************
*** 317,322 ****
--- 344,359 ----
}
/*
+ * 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,338 ****
linenr_T end = 0;
linenr_T added = 0;
! if (recorded_changes == NULL // nothing changed
|| buf->b_listener == NULL) // no listeners
return;
--- 369,375 ----
linenr_T end = 0;
linenr_T added = 0;
! if (buf->b_recorded_changes == NULL // nothing changed
|| buf->b_listener == NULL) // no listeners
return;
***************
*** 340,346 ****
argv[0].vval.v_number = buf->b_fnum; // a:bufnr
! for (li = recorded_changes->lv_first; li != NULL; li = li->li_next)
{
varnumber_T lnum;
--- 377,383 ----
argv[0].vval.v_number = buf->b_fnum; // a:bufnr
! for (li = buf->b_recorded_changes->lv_first; li != NULL; li = li->li_next)
{
varnumber_T lnum;
***************
*** 360,366 ****
argv[3].vval.v_number = added;
argv[4].v_type = VAR_LIST;
! argv[4].vval.v_list = recorded_changes;
++textlock;
for (lnr = buf->b_listener; lnr != NULL; lnr = lnr->lr_next)
--- 397,403 ----
argv[3].vval.v_number = added;
argv[4].v_type = VAR_LIST;
! argv[4].vval.v_list = buf->b_recorded_changes;
++textlock;
for (lnr = buf->b_listener; lnr != NULL; lnr = lnr->lr_next)
***************
*** 371,378 ****
}
--textlock;
! list_unref(recorded_changes);
! recorded_changes = NULL;
}
#endif
--- 408,415 ----
}
--textlock;
! list_unref(buf->b_recorded_changes);
! buf->b_recorded_changes = NULL;
}
#endif
*** ../vim-8.1.1334/src/proto/change.pro 2019-05-14 21:20:32.597441034
+0200
--- src/proto/change.pro 2019-05-16 21:43:11.104455404 +0200
***************
*** 5,10 ****
--- 5,11 ----
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);
*** ../vim-8.1.1334/src/memline.c 2019-05-11 17:03:55.170019762 +0200
--- src/memline.c 2019-05-16 22:10:02.903781871 +0200
***************
*** 2790,2795 ****
--- 2790,2801 ----
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,3531 ****
--- 3532,3542 ----
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--;
*** ../vim-8.1.1334/src/structs.h 2019-05-11 19:14:11.585314006 +0200
--- src/structs.h 2019-05-16 21:35:50.642762505 +0200
***************
*** 2439,2444 ****
--- 2439,2445 ----
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
*** ../vim-8.1.1334/src/testdir/test_listener.vim 2019-05-14
21:20:32.597441034 +0200
--- src/testdir/test_listener.vim 2019-05-16 22:09:20.164017981 +0200
***************
*** 1,6 ****
" tests for listener_add() and listener_remove()
! func s:StoreList(l)
let s:list = a:l
endfunc
--- 1,8 ----
" tests for listener_add() and listener_remove()
! func s:StoreList(s, l)
! let s:start = a:s
! let s:text = getline(a:s)
let s:list = a:l
endfunc
***************
*** 17,23 ****
new
call setline(1, ['one', 'two'])
let s:list = []
! let id = listener_add({b, s, e, a, l -> s:StoreList(l)})
call setline(1, 'one one')
call listener_flush()
call assert_equal([{'lnum': 1, 'end': 2, 'col': 1, 'added': 0}], s:list)
--- 19,25 ----
new
call setline(1, ['one', 'two'])
let s:list = []
! 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,73 ****
--- 68,77 ----
" 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,84 ****
--- 81,112 ----
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
*** ../vim-8.1.1334/src/version.c 2019-05-16 20:29:40.799834279 +0200
--- src/version.c 2019-05-16 22:10:40.187576723 +0200
***************
*** 769,770 ****
--- 769,772 ----
{ /* Add new patch number below this line */
+ /**/
+ 1335,
/**/
--
Hacker: Someone skilled in computer programming (good guy).
Cracker: A hacker that uses his skills to crack software (bad guy).
/// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language -- http://www.Zimbu.org ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/vim_dev/201905162012.x4GKCGCk010430%40masaka.moolenaar.net.
For more options, visit https://groups.google.com/d/optout.