summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2019-05-11 19:14:16 +0200
committerBram Moolenaar <Bram@vim.org>2019-05-11 19:14:16 +0200
commit6d2399bd1053b367e13cc2b8991d3ff0bf724c7c (patch)
treed80ead8330e09e75221c0269235e8a69092634c6
parent6ed8819822994512c160006bd1204aa11ae3c494 (diff)
patch 8.1.1320: it is not possible to track changes to a bufferv8.1.1320
Problem: It is not possible to track changes to a buffer. Solution: Add listener_add() and listener_remove(). No docs or tests yet.
-rw-r--r--src/change.c131
-rw-r--r--src/proto/change.pro3
-rw-r--r--src/structs.h15
-rw-r--r--src/version.c2
4 files changed, 151 insertions, 0 deletions
diff --git a/src/change.c b/src/change.c
index 750634b26e..06d20f4ac2 100644
--- 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;
diff --git a/src/proto/change.pro b/src/proto/change.pro
index 34733f502a..4e8a1e64c7 100644
--- 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);
diff --git a/src/structs.h b/src/structs.h
index 16e5ce3da8..36bdf9a53e 100644
--- 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
diff --git a/src/version.c b/src/version.c
index 7bcb8c8735..1829fa338a 100644
--- 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,