changeset 16636:0daf9eca3541 v8.1.1320

patch 8.1.1320: it is not possible to track changes to a buffer commit https://github.com/vim/vim/commit/6d2399bd1053b367e13cc2b8991d3ff0bf724c7c Author: Bram Moolenaar <Bram@vim.org> Date: Sat May 11 19:14:16 2019 +0200 patch 8.1.1320: it is not possible to track changes to a buffer Problem: It is not possible to track changes to a buffer. Solution: Add listener_add() and listener_remove(). No docs or tests yet.
author Bram Moolenaar <Bram@vim.org>
date Sat, 11 May 2019 19:15:05 +0200
parents 3f496519cc23
children 511e89374819
files src/change.c src/proto/change.pro src/structs.h src/version.c
diffstat 4 files changed, 151 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/change.c
+++ b/src/change.c
@@ -151,6 +151,134 @@ changed_internal(void)
 #endif
 }
 
+#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 (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();
+    if (dict == NULL)
+	return;
+    dict_add_number(dict, "lnum", (varnumber_T)lnum);
+    dict_add_number(dict, "end", (varnumber_T)lnume);
+    dict_add_number(dict, "added", (varnumber_T)xtra);
+    dict_add_number(dict, "col", (varnumber_T)col);
+
+    list_append_dict(recorded_changes, dict);
+}
+
+/*
+ * listener_add() function
+ */
+    void
+f_listener_add(typval_T *argvars, typval_T *rettv)
+{
+    char_u	*callback;
+    partial_T	*partial;
+    listener_T	*lnr;
+
+    callback = get_callback(&argvars[0], &partial);
+    if (callback == NULL)
+	return;
+
+    lnr = (listener_T *)alloc_clear((sizeof(listener_T)));
+    if (lnr == NULL)
+    {
+	free_callback(callback, partial);
+	return;
+    }
+    lnr->lr_next = curbuf->b_listener;
+    curbuf->b_listener = lnr;
+
+    if (partial == NULL)
+	lnr->lr_callback = vim_strsave(callback);
+    else
+	lnr->lr_callback = callback;  // pointer into the partial
+    lnr->lr_partial = partial;
+
+    lnr->lr_id = ++next_listener_id;
+    rettv->vval.v_number = lnr->lr_id;
+}
+
+/*
+ * listener_remove() function
+ */
+    void
+f_listener_remove(typval_T *argvars, typval_T *rettv UNUSED)
+{
+    listener_T	*lnr;
+    listener_T	*next;
+    listener_T	*prev = NULL;
+    int		id = tv_get_number(argvars);
+    buf_T	*buf = curbuf;
+
+    for (lnr = buf->b_listener; lnr != NULL; lnr = next)
+    {
+	next = lnr->lr_next;
+	if (lnr->lr_id == id)
+	{
+	    if (prev != NULL)
+		prev->lr_next = lnr->lr_next;
+	    else
+		buf->b_listener = lnr->lr_next;
+	    free_callback(lnr->lr_callback, lnr->lr_partial);
+	    vim_free(lnr);
+	}
+	prev = lnr;
+    }
+}
+
+/*
+ * Called when a sequence of changes is done: invoke listeners added with
+ * listener_add().
+ */
+    void
+invoke_listeners(void)
+{
+    listener_T	*lnr;
+    typval_T	rettv;
+    int		dummy;
+    typval_T	argv[2];
+
+    if (recorded_changes == NULL)  // nothing changed
+	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)
+    {
+	call_func(lnr->lr_callback, -1, &rettv,
+		   1, argv, NULL, 0L, 0L, &dummy, TRUE, lnr->lr_partial, NULL);
+	clear_tv(&rettv);
+    }
+
+    list_unref(recorded_changes);
+    recorded_changes = NULL;
+}
+#endif
+
 /*
  * Common code for when a change was made.
  * See changed_lines() for the arguments.
@@ -175,6 +303,9 @@ changed_common(
     // mark the buffer as modified
     changed();
 
+#ifdef FEAT_EVAL
+    may_record_change(lnum, col, lnume, xtra);
+#endif
 #ifdef FEAT_DIFF
     if (curwin->w_p_diff && diff_internal())
 	curtab->tp_diff_update = TRUE;
--- a/src/proto/change.pro
+++ b/src/proto/change.pro
@@ -2,6 +2,9 @@
 void change_warning(int col);
 void changed(void);
 void changed_internal(void);
+void f_listener_add(typval_T *argvars, typval_T *rettv);
+void f_listener_remove(typval_T *argvars, typval_T *rettv);
+void invoke_listeners(void);
 void changed_bytes(linenr_T lnum, colnr_T col);
 void inserted_bytes(linenr_T lnum, colnr_T col, int added);
 void appended_lines(linenr_T lnum, long count);
--- a/src/structs.h
+++ b/src/structs.h
@@ -1873,6 +1873,19 @@ typedef struct
 #endif
 } jobopt_T;
 
+#ifdef FEAT_EVAL
+/*
+ * Structure used for listeners added with listener_add().
+ */
+typedef struct listener_S listener_T;
+struct listener_S
+{
+    listener_T	*lr_next;
+    int		lr_id;
+    char_u	*lr_callback;
+    partial_T	*lr_partial;
+};
+#endif
 
 /* structure used for explicit stack while garbage collecting hash tables */
 typedef struct ht_stack_S
@@ -2424,6 +2437,8 @@ struct file_buffer
 #ifdef FEAT_EVAL
     dictitem_T	b_bufvar;	/* variable for "b:" Dictionary */
     dict_T	*b_vars;	/* internal variables, local to buffer */
+
+    listener_T	*b_listener;
 #endif
 #ifdef FEAT_TEXT_PROP
     int		b_has_textprop;	// TRUE when text props were added
--- a/src/version.c
+++ b/src/version.c
@@ -768,6 +768,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1320,
+/**/
     1319,
 /**/
     1318,