diff options
author | Magnus Groß <magnus.gross@rwth-aachen.de> | 2021-11-17 17:55:49 +0100 |
---|---|---|
committer | Magnus Groß <magnus.gross@rwth-aachen.de> | 2021-11-18 14:23:33 +0100 |
commit | 1fb101afe4f28ada83721c4ac260de46d23504ee (patch) | |
tree | a4aa3a2f24cd51f6b53fea0b15400bce0bf06f7f | |
parent | fdfd1eda434b70b02b4cb804546c97ef8ff09049 (diff) | |
download | rneovim-1fb101afe4f28ada83721c4ac260de46d23504ee.tar.gz rneovim-1fb101afe4f28ada83721c4ac260de46d23504ee.tar.bz2 rneovim-1fb101afe4f28ada83721c4ac260de46d23504ee.zip |
vim-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.
https://github.com/vim/vim/commit/3075a45592fe76f2febb6321632a23e352efe949
In the vim codebase there is no occurrence of get_vim_var_dict(VV_EVENT)
after the above patch, so in order to hold the same invariant in the
neovim codebase we needed to replace more occurrences than the related
vim patch.
-rw-r--r-- | src/nvim/aucmd.c | 6 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 6 | ||||
-rw-r--r-- | src/nvim/channel.c | 6 | ||||
-rw-r--r-- | src/nvim/edit.c | 5 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 12 | ||||
-rw-r--r-- | src/nvim/file_search.c | 5 | ||||
-rw-r--r-- | src/nvim/misc1.c | 31 | ||||
-rw-r--r-- | src/nvim/ops.c | 5 | ||||
-rw-r--r-- | src/nvim/terminal.c | 5 | ||||
-rw-r--r-- | src/nvim/testdir/test_edit.vim | 6 |
10 files changed, 67 insertions, 20 deletions
diff --git a/src/nvim/aucmd.c b/src/nvim/aucmd.c index af519dcba9..a236b47027 100644 --- a/src/nvim/aucmd.c +++ b/src/nvim/aucmd.c @@ -8,6 +8,7 @@ #include "nvim/ex_getln.h" #include "nvim/fileio.h" #include "nvim/main.h" +#include "nvim/misc1.h" #include "nvim/os/os.h" #include "nvim/ui.h" #include "nvim/vim.h" @@ -25,13 +26,14 @@ void do_autocmd_uienter(uint64_t chanid, bool attached) } recursive = true; - dict_T *dict = get_vim_var_dict(VV_EVENT); + save_v_event_T save_v_event; + dict_T *dict = get_v_event(&save_v_event); assert(chanid < VARNUMBER_MAX); tv_dict_add_nr(dict, S_LEN("chan"), (varnumber_T)chanid); tv_dict_set_keys_readonly(dict); apply_autocmds(attached ? EVENT_UIENTER : EVENT_UILEAVE, NULL, NULL, false, curbuf); - tv_dict_clear(dict); + restore_v_event(dict, &save_v_event); recursive = false; } diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index bd9c5efa44..3d7b03d921 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -1116,6 +1116,12 @@ typedef struct { pos_T w_cursor_corr; // corrected cursor position } pos_save_T; +// Struct passed to get_v_event() and restore_v_event(). +typedef struct { + bool sve_did_save; + hashtab_T sve_hashtab; +} save_v_event_T; + /// Indices into vimmenu_T->strings[] and vimmenu_T->noremap[] for each mode /// \addtogroup MENU_INDEX /// @{ diff --git a/src/nvim/channel.c b/src/nvim/channel.c index 9662f6205f..a662f3a951 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -10,6 +10,7 @@ #include "nvim/event/socket.h" #include "nvim/fileio.h" #include "nvim/lua/executor.h" +#include "nvim/misc1.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/msgpack_rpc/server.h" #include "nvim/os/shell.h" @@ -821,7 +822,8 @@ static void set_info_event(void **argv) Channel *chan = argv[0]; event_T event = (event_T)(ptrdiff_t)argv[1]; - dict_T *dict = get_vim_var_dict(VV_EVENT); + save_v_event_T save_v_event; + dict_T *dict = get_v_event(&save_v_event); Dictionary info = channel_info(chan->id); typval_T retval; (void)object_to_vim(DICTIONARY_OBJ(info), &retval, NULL); @@ -829,7 +831,7 @@ static void set_info_event(void **argv) apply_autocmds(event, NULL, NULL, false, curbuf); - tv_dict_clear(dict); + restore_v_event(dict, &save_v_event); api_free_dictionary(info); channel_decref(chan); } diff --git a/src/nvim/edit.c b/src/nvim/edit.c index a6b7461c59..cccb33b792 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -2719,12 +2719,13 @@ static bool pum_enough_matches(void) static void trigger_complete_changed_event(int cur) { static bool recursive = false; + save_v_event_T save_v_event; if (recursive) { return; } - dict_T *v_event = get_vim_var_dict(VV_EVENT); + dict_T *v_event = get_v_event(&save_v_event); if (cur < 0) { tv_dict_add_dict(v_event, S_LEN("completed_item"), tv_dict_alloc()); } else { @@ -2740,7 +2741,7 @@ static void trigger_complete_changed_event(int cur) textlock--; recursive = false; - tv_dict_clear(v_event); + restore_v_event(v_event, &save_v_event); } /// Show the popup menu for the list of matches. diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 09f379d0b9..2823cb7567 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -880,7 +880,8 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) TryState tstate; Error err = ERROR_INIT; bool tl_ret = true; - dict_T *dict = get_vim_var_dict(VV_EVENT); + save_v_event_T save_v_event; + dict_T *dict = get_v_event(&save_v_event); char firstcbuf[2]; firstcbuf[0] = (char)(firstc > 0 ? firstc : '-'); firstcbuf[1] = 0; @@ -894,7 +895,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) apply_autocmds(EVENT_CMDLINEENTER, (char_u *)firstcbuf, (char_u *)firstcbuf, false, curbuf); - tv_dict_clear(dict); + restore_v_event(dict, &save_v_event); tl_ret = try_leave(&tstate, &err); @@ -925,7 +926,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) if (tv_dict_get_number(dict, "abort") != 0) { s->gotesc = 1; } - tv_dict_clear(dict); + restore_v_event(dict, &save_v_event); } cmdmsg_rl = false; @@ -2290,7 +2291,8 @@ static int command_line_changed(CommandLineState *s) if (has_event(EVENT_CMDLINECHANGED)) { TryState tstate; Error err = ERROR_INIT; - dict_T *dict = get_vim_var_dict(VV_EVENT); + save_v_event_T save_v_event; + dict_T *dict = get_v_event(&save_v_event); char firstcbuf[2]; firstcbuf[0] = (char)(s->firstc > 0 ? s->firstc : '-'); @@ -2304,7 +2306,7 @@ static int command_line_changed(CommandLineState *s) apply_autocmds(EVENT_CMDLINECHANGED, (char_u *)firstcbuf, (char_u *)firstcbuf, false, curbuf); - tv_dict_clear(dict); + restore_v_event(dict, &save_v_event); bool tl_ret = try_leave(&tstate, &err); if (!tl_ret && ERROR_SET(&err)) { diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index 36257fefb3..fe991963a0 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1603,7 +1603,8 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause) recursive = true; - dict_T *dict = get_vim_var_dict(VV_EVENT); + save_v_event_T save_v_event; + dict_T *dict = get_v_event(&save_v_event); char buf[8]; switch (scope) { @@ -1648,7 +1649,7 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause) apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, (char_u *)new_dir, false, curbuf); - tv_dict_clear(dict); + restore_v_event(dict, &save_v_event); recursive = false; } diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index b50d8682eb..9900a68fac 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -1060,6 +1060,31 @@ void add_time(char_u *buf, size_t buflen, time_t tt) } } +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) +{ + tv_dict_free_contents(v_event); + if (sve->sve_did_save) { + v_event->dv_hashtab = sve->sve_hashtab; + } else { + hash_init(&v_event->dv_hashtab); + } +} + /// Fires a ModeChanged autocmd. void trigger_modechanged(void) { @@ -1073,7 +1098,8 @@ void trigger_modechanged(void) return; } - dict_T *v_event = get_vim_var_dict(VV_EVENT); + save_v_event_T save_v_event; + dict_T *v_event = get_v_event(&save_v_event); tv_dict_add_str(v_event, S_LEN("new_mode"), mode); tv_dict_add_str(v_event, S_LEN("old_mode"), last_mode); @@ -1086,6 +1112,5 @@ void trigger_modechanged(void) last_mode = mode; xfree(pat); - tv_dict_free_contents(v_event); - hash_init(&v_event->dv_hashtab); + restore_v_event(v_event, &save_v_event); } diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 7d7db2a8a6..c2555add50 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2792,8 +2792,9 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) recursive = true; + save_v_event_T save_v_event; // Set the v:event dictionary with information about the yank. - dict_T *dict = get_vim_var_dict(VV_EVENT); + dict_T *dict = get_v_event(&save_v_event); // The yanked text contents. list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size); @@ -2830,7 +2831,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) textlock++; apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf); textlock--; - tv_dict_clear(dict); + restore_v_event(dict, &save_v_event); recursive = false; } diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 30f6ae6f34..83ade74db1 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -324,10 +324,11 @@ void terminal_close(Terminal *term, int status) } if (buf && !is_autocmd_blocked()) { - dict_T *dict = get_vim_var_dict(VV_EVENT); + save_v_event_T save_v_event; + dict_T *dict = get_v_event(&save_v_event); tv_dict_add_nr(dict, S_LEN("status"), status); apply_autocmds(EVENT_TERMCLOSE, NULL, NULL, false, buf); - tv_dict_clear(dict); + restore_v_event(dict, &save_v_event); } } diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim index 95eb5a0c8b..7a7f4cb036 100644 --- a/src/nvim/testdir/test_edit.vim +++ b/src/nvim/testdir/test_edit.vim @@ -1721,4 +1721,10 @@ func Test_mode_changes() unlet! g:i_to_any endfunc +func Test_recursive_ModeChanged() + au! ModeChanged * norm 0u + sil! norm + au! +endfunc + " vim: shiftwidth=2 sts=2 expandtab |