]> granicus.if.org Git - vim/commitdiff
patch 8.1.1335: listener callback is called after inserting text v8.1.1335
authorBram Moolenaar <Bram@vim.org>
Thu, 16 May 2019 20:11:47 +0000 (22:11 +0200)
committerBram Moolenaar <Bram@vim.org>
Thu, 16 May 2019 20:11:47 +0000 (22:11 +0200)
Problem:    Listener callback is called after inserting text.
Solution:   Flush the changes before inserting or deleting a line.  Store
            changes per buffer.

src/change.c
src/memline.c
src/proto/change.pro
src/structs.h
src/testdir/test_listener.vim
src/version.c

index 86f1ffcfb4869c78db25a974ba77461afd24810f..f1c3cc4d02fe899d10e6dc36135bde32029ceefa 100644 (file)
@@ -152,32 +152,30 @@ changed_internal(void)
 }
 
 #ifdef FEAT_EVAL
-static list_T *recorded_changes = NULL;
 static long next_listener_id = 0;
 
 /*
- * Record a change for listeners added with listener_add().
+ * 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 void
-may_record_change(
-    linenr_T   lnum,
-    colnr_T    col,
-    linenr_T   lnume,
-    long       xtra)
+    static int
+check_recorded_changes(
+       buf_T           *buf,
+       linenr_T        lnum,
+       colnr_T         col,
+       linenr_T        lnume,
+       long            xtra,
+       int             merge)
 {
-    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)
+    if (buf->b_recorded_changes != NULL && xtra != 0)
     {
        listitem_T *li;
        linenr_T    nr;
 
-       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)
        {
            nr = (linenr_T)dict_get_number(
                                      li->li_tv.vval.v_dict, (char_u *)"lnum");
@@ -187,35 +185,64 @@ may_record_change(
                        && 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,
+                   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;
+                       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;
                }
-
-               // the current change is going to make the line number in the
-               // older change invalid, flush now
-               invoke_listeners(curbuf);
-               break;
            }
        }
     }
+    return FALSE;
+}
 
-    if (recorded_changes == NULL)
+/*
+ * 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)
     {
-       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
 
index 812d10dc1a27f6a218879b4c6f36fc3a59a6abd9..006a8b52df1f8806bf0f6a8460e94eab2d37d40a 100644 (file)
@@ -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--;
 
index f0f390b05b3dc051da8e15e10f3ac12ff7cceffd..79306e30616ec86a27145147f862a168343a0cf3 100644 (file)
@@ -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);
index 36bdf9a53ee7ffba632672c5c3155e501bbe4f83..afd3e02813942cbf5c640b2ce09a7c69360bdd6e 100644 (file)
@@ -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
index d5d633274e3f3f920b4ec34e402887aab29373f7..66c3d514398fafdf9c968eb3f1de247f69f3e13f 100644 (file)
@@ -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
index ad16cd5706aa2ab4abbbcbb3b38ac370cbbc3493..b085052baa117010604440d78fb33fbe541559e6 100644 (file)
@@ -767,6 +767,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1335,
 /**/
     1334,
 /**/