summaryrefslogtreecommitdiffstats
path: root/src/change.c
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2019-05-14 21:20:36 +0200
committerBram Moolenaar <Bram@vim.org>2019-05-14 21:20:36 +0200
commitfe1ade0a78a70a4c7ddaebb6964497f037f4997a (patch)
tree2a19d938a6a5cc48aa729feb4caebd09c8a5a0d1 /src/change.c
parentfb222df28d5158516104a21cba7141a6240f4817 (diff)
patch 8.1.1332: cannot flush listeners without redrawing, mix of changesv8.1.1332
Problem: Cannot flush change listeners without also redrawing. The line numbers in the list of changes may become invalid. Solution: Add listener_flush(). Invoke listeners before adding a change that makes line numbers invalid.
Diffstat (limited to 'src/change.c')
-rw-r--r--src/change.c104
1 files changed, 96 insertions, 8 deletions
diff --git a/src/change.c b/src/change.c
index 27ea9ac8fb..9b71596ec7 100644
--- a/src/change.c
+++ b/src/change.c
@@ -169,6 +169,46 @@ may_record_change(
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");
+ 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();
@@ -231,6 +271,23 @@ f_listener_add(typval_T *argvars, typval_T *rettv)
}
/*
+ * listener_flush() function
+ */
+ void
+f_listener_flush(typval_T *argvars, typval_T *rettv UNUSED)
+{
+ buf_T *buf = curbuf;
+
+ if (argvars[0].v_type != VAR_UNKNOWN)
+ {
+ buf = get_buf_arg(&argvars[0]);
+ if (buf == NULL)
+ return;
+ }
+ invoke_listeners(buf);
+}
+
+/*
* listener_remove() function
*/
void
@@ -264,25 +321,56 @@ f_listener_remove(typval_T *argvars, typval_T *rettv UNUSED)
* listener_add().
*/
void
-invoke_listeners(void)
+invoke_listeners(buf_T *buf)
{
listener_T *lnr;
typval_T rettv;
int dummy;
- typval_T argv[2];
-
- if (recorded_changes == NULL) // nothing changed
+ typval_T argv[6];
+ listitem_T *li;
+ linenr_T start = MAXLNUM;
+ linenr_T end = 0;
+ linenr_T added = 0;
+
+ if (recorded_changes == NULL // nothing changed
+ || buf->b_listener == NULL) // no listeners
return;
- argv[0].v_type = VAR_LIST;
- argv[0].vval.v_list = recorded_changes;
- for (lnr = curbuf->b_listener; lnr != NULL; lnr = lnr->lr_next)
+ argv[0].v_type = VAR_NUMBER;
+ 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;
+
+ lnum = dict_get_number(li->li_tv.vval.v_dict, (char_u *)"lnum");
+ if (start > lnum)
+ start = lnum;
+ lnum = dict_get_number(li->li_tv.vval.v_dict, (char_u *)"end");
+ if (lnum > end)
+ end = lnum;
+ added = dict_get_number(li->li_tv.vval.v_dict, (char_u *)"added");
+ }
+ argv[1].v_type = VAR_NUMBER;
+ argv[1].vval.v_number = start;
+ argv[2].v_type = VAR_NUMBER;
+ argv[2].vval.v_number = end;
+ argv[3].v_type = VAR_NUMBER;
+ 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)
{
call_func(lnr->lr_callback, -1, &rettv,
- 1, argv, NULL, 0L, 0L, &dummy, TRUE, lnr->lr_partial, NULL);
+ 5, argv, NULL, 0L, 0L, &dummy, TRUE, lnr->lr_partial, NULL);
clear_tv(&rettv);
}
+ --textlock;
list_unref(recorded_changes);
recorded_changes = NULL;
}