]> granicus.if.org Git - vim/commitdiff
patch 8.2.3609: internal error when ModeChanged is triggered recursively v8.2.3609
authorBram Moolenaar <Bram@vim.org>
Wed, 17 Nov 2021 15:51:52 +0000 (15:51 +0000)
committerBram Moolenaar <Bram@vim.org>
Wed, 17 Nov 2021 15:51:52 +0000 (15:51 +0000)
Problem:    Internal error when ModeChanged is triggered when v:event is
            already in use.
Solution:   Save and restore v:event if needed.

src/insexpand.c
src/misc1.c
src/proto/misc1.pro
src/register.c
src/structs.h
src/testdir/test_edit.vim
src/version.c

index c993d96701a17c86e5d2b9553f9ad323f8e04128..e9b48616df23fdc68d5919ffa50d19ebdbdbc728 100644 (file)
@@ -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
 
index 58f515dffb332fcc552feb4f744c84a0e3875dc6..e35ba98f432b2181a0c5ab69bf4e07be6e5ab308 100644 (file)
@@ -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
 }
index 7d4c41faadc8c9a89ada320c04cffc9149048e74..db60a6dc2e9916e0767958387d37c02adc9bd098 100644 (file)
@@ -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 : */
index 129c80df810ae4aaddaf2d3236a7cab217c76d92..268c839f749a3795692ef23a63787406958d6b76 100644 (file)
@@ -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 *reg)
     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
 
index 8ffebf31264ca0dec27f735db13b511aff8e0921..7bd1ff775d1337bc2140f8e354d2b613e5b5afe1 100644 (file)
@@ -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;
index 0abc47a91d320ab2d9cf567265429306cee10ea3..957f248fc737db5ec09859c292fa53db5b721532 100644 (file)
@@ -2034,6 +2034,12 @@ func Test_mode_changes()
   unlet! g:i_to_any
 endfunc
 
+func Test_recursive_ModeChanged()
+  au! ModeChanged * norm 0u
+  sil! norm \16
+  au!
+endfunc
+
 " Test toggling of input method. See :help i_CTRL-^
 func Test_edit_CTRL_hat()
   CheckFeature xim
index e5146bc03a9223224c871f0df335692b5c612804..55928db1963c12cef27bf8159810736383175d9a 100644 (file)
@@ -757,6 +757,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    3609,
 /**/
     3608,
 /**/