summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2021-11-17 15:51:52 +0000
committerBram Moolenaar <Bram@vim.org>2021-11-17 15:51:52 +0000
commit3075a45592fe76f2febb6321632a23e352efe949 (patch)
tree03456a8d38fac32baca2c5157b264f2c759e55c4
parent125ffd21f9601a90b845f1d50c24da0d3938bb59 (diff)
downloadvim-git-3075a45592fe76f2febb6321632a23e352efe949.tar.gz
patch 8.2.3609: internal error when ModeChanged is triggered recursivelyv8.2.3609
Problem: Internal error when ModeChanged is triggered when v:event is already in use. Solution: Save and restore v:event if needed.
-rw-r--r--src/insexpand.c8
-rw-r--r--src/misc1.c41
-rw-r--r--src/proto/misc1.pro2
-rw-r--r--src/register.c18
-rw-r--r--src/structs.h5
-rw-r--r--src/testdir/test_edit.vim6
-rw-r--r--src/version.c2
7 files changed, 65 insertions, 17 deletions
diff --git a/src/insexpand.c b/src/insexpand.c
index c993d9670..e9b48616d 100644
--- 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
index 58f515dff..e35ba98f4 100644
--- 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
index 7d4c41faa..db60a6dc2 100644
--- 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
index 129c80df8..268c839f7 100644
--- 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 *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
diff --git a/src/structs.h b/src/structs.h
index 8ffebf312..7bd1ff775 100644
--- 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
index 0abc47a91..957f248fc 100644
--- 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
index e5146bc03..55928db19 100644
--- 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,