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 /src | |
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.
Diffstat (limited to 'src')
-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 |