# HG changeset patch # User Bram Moolenaar # Date 1557594905 -7200 # Node ID 0daf9eca3541633834e100af8038b455f278d6cf # Parent 3f496519cc23ec4aaa3e18349cd983e6f170c2ad 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 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. diff --git a/src/change.c b/src/change.c --- 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 --- 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 --- 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 --- 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,