# HG changeset patch # User Bram Moolenaar # Date 1637164805 -3600 # Node ID f2392648af3ecef777a0780c11b9aacab0c4af7e # Parent fa84cd94d8b6b40e0c7fbc1677d4fddd4002caa5 patch 8.2.3609: internal error when ModeChanged is triggered recursively Commit: https://github.com/vim/vim/commit/3075a45592fe76f2febb6321632a23e352efe949 Author: Bram Moolenaar Date: Wed Nov 17 15:51:52 2021 +0000 patch 8.2.3609: internal error when ModeChanged is triggered recursively Problem: Internal error when ModeChanged is triggered when v:event is already in use. Solution: Save and restore v:event if needed. diff --git a/src/insexpand.c b/src/insexpand.c --- a/src/insexpand.c +++ b/src/insexpand.c @@ -962,7 +962,7 @@ pum_enough_matches(void) return (i >= 2); } -#ifdef FEAT_EVAL +#if defined(FEAT_EVAL) || defined(PROTO) /* * Allocate Dict for the completed item. * { word, abbr, menu, kind, info } @@ -993,17 +993,18 @@ trigger_complete_changed_event(int cur) dict_T *v_event; dict_T *item; static int recursive = FALSE; + save_v_event_T save_v_event; if (recursive) return; - v_event = get_vim_var_dict(VV_EVENT); if (cur < 0) item = dict_alloc(); else item = ins_compl_dict_alloc(compl_curr_match); if (item == NULL) return; + v_event = get_v_event(&save_v_event); dict_add_dict(v_event, "completed_item", item); pum_set_event_info(v_event); dict_set_items_ro(v_event); @@ -1014,8 +1015,7 @@ trigger_complete_changed_event(int cur) textwinlock--; recursive = FALSE; - dict_free_contents(v_event); - hash_init(&v_event->dv_hashtab); + restore_v_event(v_event, &save_v_event); } #endif diff --git a/src/misc1.c b/src/misc1.c --- a/src/misc1.c +++ b/src/misc1.c @@ -2654,18 +2654,52 @@ path_with_url(char_u *fname) return path_is_url(p); } +#if defined(FEAT_EVAL) || defined(PROTO) +/* + * Return the dictionary of v:event. + * Save and clear the value in case it already has items. + */ + dict_T * +get_v_event(save_v_event_T *sve) +{ + dict_T *v_event = get_vim_var_dict(VV_EVENT); + + if (v_event->dv_hashtab.ht_used > 0) + { + // recursive use of v:event, save, make empty and restore later + sve->sve_did_save = TRUE; + sve->sve_hashtab = v_event->dv_hashtab; + hash_init(&v_event->dv_hashtab); + } + else + sve->sve_did_save = FALSE; + return v_event; +} + + void +restore_v_event(dict_T *v_event, save_v_event_T *sve) +{ + dict_free_contents(v_event); + if (sve->sve_did_save) + v_event->dv_hashtab = sve->sve_hashtab; + else + hash_init(&v_event->dv_hashtab); +} +#endif + /* * Fires a ModeChanged autocmd */ void trigger_modechanged() { -#if defined(FEAT_EVAL) || defined(PROTO) +#ifdef FEAT_EVAL dict_T *v_event; typval_T rettv; typval_T tv[2]; char_u *pat_pre; char_u *pat; + save_v_event_T save_v_event; if (!has_modechanged()) return; @@ -2680,7 +2714,7 @@ trigger_modechanged() return; } - v_event = get_vim_var_dict(VV_EVENT); + v_event = get_v_event(&save_v_event); (void)dict_add_string(v_event, "new_mode", rettv.vval.v_string); (void)dict_add_string(v_event, "old_mode", last_mode); dict_set_items_ro(v_event); @@ -2694,8 +2728,7 @@ trigger_modechanged() STRCPY(last_mode, rettv.vval.v_string); vim_free(pat); - dict_free_contents(v_event); - hash_init(&v_event->dv_hashtab); + restore_v_event(v_event, &save_v_event); vim_free(rettv.vval.v_string); #endif } diff --git a/src/proto/misc1.pro b/src/proto/misc1.pro --- a/src/proto/misc1.pro +++ b/src/proto/misc1.pro @@ -47,5 +47,7 @@ int goto_im(void); char_u *get_isolated_shell_name(void); int path_is_url(char_u *p); int path_with_url(char_u *fname); +dict_T *get_v_event(save_v_event_T *sve); +void restore_v_event(dict_T *v_event, save_v_event_T *sve); void trigger_modechanged(void); /* vim: set ft=c : */ diff --git a/src/register.c b/src/register.c --- a/src/register.c +++ b/src/register.c @@ -991,17 +991,18 @@ shift_delete_registers() void yank_do_autocmd(oparg_T *oap, yankreg_T *reg) { - static int recursive = FALSE; - dict_T *v_event; - list_T *list; - int n; - char_u buf[NUMBUFLEN + 2]; - long reglen = 0; + static int recursive = FALSE; + dict_T *v_event; + list_T *list; + int n; + char_u buf[NUMBUFLEN + 2]; + long reglen = 0; + save_v_event_T save_v_event; if (recursive) return; - v_event = get_vim_var_dict(VV_EVENT); + v_event = get_v_event(&save_v_event); list = list_alloc(); if (list == NULL) @@ -1045,8 +1046,7 @@ yank_do_autocmd(oparg_T *oap, yankreg_T recursive = FALSE; // Empty the dictionary, v:event is still valid - dict_free_contents(v_event); - hash_init(&v_event->dv_hashtab); + restore_v_event(v_event, &save_v_event); } #endif diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -4465,3 +4465,8 @@ typedef struct { #define WHERE_INIT {NULL, 0, 0} +// Struct passed to get_v_event() and restore_v_event(). +typedef struct { + int sve_did_save; + hashtab_T sve_hashtab; +} save_v_event_T; diff --git a/src/testdir/test_edit.vim b/src/testdir/test_edit.vim --- a/src/testdir/test_edit.vim +++ b/src/testdir/test_edit.vim @@ -2034,6 +2034,12 @@ func Test_mode_changes() unlet! g:i_to_any endfunc +func Test_recursive_ModeChanged() + au! ModeChanged * norm 0u + sil! norm  + au! +endfunc + " Test toggling of input method. See :help i_CTRL-^ func Test_edit_CTRL_hat() CheckFeature xim diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 3609, +/**/ 3608, /**/ 3607,