diff options
Diffstat (limited to 'src/nvim/autocmd.c')
-rw-r--r-- | src/nvim/autocmd.c | 153 |
1 files changed, 63 insertions, 90 deletions
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index c08ef7a4c1..96da4695de 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -30,8 +30,8 @@ #include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/grid.h" +#include "nvim/grid_defs.h" #include "nvim/hashtab.h" -#include "nvim/highlight.h" #include "nvim/highlight_defs.h" #include "nvim/insexpand.h" #include "nvim/lua/executor.h" @@ -42,7 +42,6 @@ #include "nvim/option.h" #include "nvim/option_defs.h" #include "nvim/option_vars.h" -#include "nvim/optionstr.h" #include "nvim/os/input.h" #include "nvim/os/os.h" #include "nvim/os/os_defs.h" @@ -75,7 +74,6 @@ static const char e_autocommand_nesting_too_deep[] // Naming Conventions: // - general autocmd behavior start with au_ // - AutoCmd start with aucmd_ -// - Autocmd.exec stat with aucmd_exec // - AutoPat start with aupat_ // - Groups start with augroup_ // - Events start with event_ @@ -255,24 +253,24 @@ static void au_show_for_event(int group, event_T event, const char *pat) return; } - char *exec_to_string = aucmd_exec_to_string(ac, ac->exec); + char *handler_str = aucmd_handler_to_string(ac); if (ac->desc != NULL) { size_t msglen = 100; char *msg = xmallocz(msglen); - if (ac->exec.type == CALLABLE_CB) { - msg_puts_hl(exec_to_string, HLF_8, false); - snprintf(msg, msglen, " [%s]", ac->desc); + if (ac->handler_cmd) { + snprintf(msg, msglen, "%s [%s]", handler_str, ac->desc); } else { - snprintf(msg, msglen, "%s [%s]", exec_to_string, ac->desc); + msg_puts_hl(handler_str, HLF_8, false); + snprintf(msg, msglen, " [%s]", ac->desc); } msg_outtrans(msg, 0, false); XFREE_CLEAR(msg); - } else if (ac->exec.type == CALLABLE_CB) { - msg_puts_hl(exec_to_string, HLF_8, false); + } else if (ac->handler_cmd) { + msg_outtrans(handler_str, 0, false); } else { - msg_outtrans(exec_to_string, 0, false); + msg_puts_hl(handler_str, HLF_8, false); } - XFREE_CLEAR(exec_to_string); + XFREE_CLEAR(handler_str); if (p_verbose > 0) { last_set_msg(ac->script_ctx); } @@ -304,7 +302,11 @@ static void aucmd_del(AutoCmd *ac) xfree(ac->pat); } ac->pat = NULL; - aucmd_exec_free(&ac->exec); + if (ac->handler_cmd) { + XFREE_CLEAR(ac->handler_cmd); + } else { + callback_free(&ac->handler_fn); + } XFREE_CLEAR(ac->desc); au_need_clean = true; @@ -900,8 +902,8 @@ void do_all_autocmd_events(const char *pat, bool once, int nested, char *cmd, bo // If *cmd == NUL: show entries. // If forceit == true: delete entries. // If group is not AUGROUP_ALL: only use this group. -int do_autocmd_event(event_T event, const char *pat, bool once, int nested, char *cmd, bool del, - int group) +int do_autocmd_event(event_T event, const char *pat, bool once, int nested, const char *cmd, + bool del, int group) FUNC_ATTR_NONNULL_ALL { // Cannot be used to show all patterns. See au_show_for_event or au_show_for_all_events @@ -959,10 +961,8 @@ int do_autocmd_event(event_T event, const char *pat, bool once, int nested, char } if (is_adding_cmd) { - AucmdExecutable exec = AUCMD_EXECUTABLE_INIT; - exec.type = CALLABLE_EX; - exec.callable.cmd = cmd; - autocmd_register(0, event, pat, patlen, group, once, nested, NULL, exec); + Callback handler_fn = CALLBACK_INIT; + autocmd_register(0, event, pat, patlen, group, once, nested, NULL, cmd, &handler_fn); } pat = aucmd_next_pattern(pat, (size_t)patlen); @@ -973,8 +973,13 @@ int do_autocmd_event(event_T event, const char *pat, bool once, int nested, char return OK; } +/// Registers an autocmd. The handler may be a Ex command or callback function, decided by +/// the `handler_cmd` or `handler_fn` args. +/// +/// @param handler_cmd Handler Ex command, or NULL if handler is a function (`handler_fn`). +/// @param handler_fn Handler function, ignored if `handler_cmd` is not NULL. int autocmd_register(int64_t id, event_T event, const char *pat, int patlen, int group, bool once, - bool nested, char *desc, AucmdExecutable aucmd) + bool nested, char *desc, const char *handler_cmd, Callback *handler_fn) { // 0 is not a valid group. assert(group != 0); @@ -1082,7 +1087,12 @@ int autocmd_register(int64_t id, event_T event, const char *pat, int patlen, int AutoCmd *ac = kv_pushp(autocmds[(int)event]); ac->pat = ap; ac->id = id; - ac->exec = aucmd_exec_copy(aucmd); + if (handler_cmd) { + ac->handler_cmd = xstrdup(handler_cmd); + } else { + ac->handler_cmd = NULL; + callback_copy(&ac->handler_fn, handler_fn); + } ac->script_ctx = current_sctx; ac->script_ctx.sc_lnum += SOURCING_LNUM; nlua_set_sctx(&ac->script_ctx); @@ -1666,7 +1676,9 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force } else { autocmd_fname = fname_io; } + char *afile_orig = NULL; ///< Unexpanded <afile> if (autocmd_fname != NULL) { + afile_orig = xstrdup(autocmd_fname); // Allocate MAXPATHL for when eval_vars() resolves the fullpath. autocmd_fname = xstrnsave(autocmd_fname, MAXPATHL); } @@ -1798,6 +1810,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force // save vector size, to avoid an endless loop when more patterns // are added when executing autocommands .ausize = kv_size(autocmds[(int)event]), + .afile_orig = afile_orig, .fname = fname, .sfname = sfname, .tail = tail, @@ -1865,6 +1878,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force autocmd_nested = save_autocmd_nested; xfree(SOURCING_NAME); estack_pop(); + xfree(afile_orig); xfree(autocmd_fname); autocmd_fname = save_autocmd_fname; autocmd_fname_full = save_autocmd_fname_full; @@ -2022,15 +2036,16 @@ static void aucmd_next(AutoPatCmd *apc) apc->auidx = SIZE_MAX; } -static bool call_autocmd_callback(const AutoCmd *ac, const AutoPatCmd *apc) +/// Executes an autocmd callback function (as opposed to an Ex command). +static bool au_callback(const AutoCmd *ac, const AutoPatCmd *apc) { - Callback callback = ac->exec.callable.cb; + Callback callback = ac->handler_fn; if (callback.type == kCallbackLua) { MAXSIZE_TEMP_DICT(data, 7); PUT_C(data, "id", INTEGER_OBJ(ac->id)); PUT_C(data, "event", CSTR_AS_OBJ(event_nr2name(apc->event))); + PUT_C(data, "file", CSTR_AS_OBJ(apc->afile_orig)); PUT_C(data, "match", CSTR_AS_OBJ(autocmd_match)); - PUT_C(data, "file", CSTR_AS_OBJ(autocmd_fname)); PUT_C(data, "buf", INTEGER_OBJ(autocmd_bufnr)); if (apc->data) { @@ -2089,10 +2104,10 @@ char *getnextac(int c, void *cookie, int indent, bool do_concat) if (p_verbose >= 9) { verbose_enter_scroll(); - char *exec_to_string = aucmd_exec_to_string(ac, ac->exec); - smsg(0, _("autocommand %s"), exec_to_string); + char *handler_str = aucmd_handler_to_string(ac); + smsg(0, _("autocommand %s"), handler_str); msg_puts("\n"); // don't overwrite this either - XFREE_CLEAR(exec_to_string); + XFREE_CLEAR(handler_str); verbose_leave_scroll(); } @@ -2103,16 +2118,22 @@ char *getnextac(int c, void *cookie, int indent, bool do_concat) apc->script_ctx = current_sctx; char *retval; - if (ac->exec.type == CALLABLE_CB) { - // Can potentially reallocate kvec_t data and invalidate the ac pointer - if (call_autocmd_callback(ac, apc)) { - // If an autocommand callback returns true, delete the autocommand - oneshot = true; - } - - // TODO(tjdevries): - // - // Major Hack Alert: + if (ac->handler_cmd) { + retval = xstrdup(ac->handler_cmd); + } else { + AutoCmd ac_copy = *ac; + // Mark oneshot handler as "removed" now, to prevent recursion by e.g. `:doautocmd`. #25526 + ac->pat = oneshot ? NULL : ac->pat; + // May reallocate `acs` kvec_t data and invalidate the `ac` pointer. + bool rv = au_callback(&ac_copy, apc); + if (oneshot) { + // Restore `pat`. Use `acs` because `ac` may have been invalidated by the callback. + kv_A(*acs, apc->auidx).pat = ac_copy.pat; + } + // If an autocommand callback returns true, delete the autocommand + oneshot = oneshot || rv; + + // HACK(tjdevries): // We just return "not-null" and continue going. // This would be a good candidate for a refactor. You would need to refactor: // 1. do_cmdline to accept something besides a string @@ -2121,8 +2142,6 @@ char *getnextac(int c, void *cookie, int indent, bool do_concat) // and instead we loop over all the matches and just execute one-by-one. // However, my expectation would be that could be expensive. retval = xcalloc(1, 1); - } else { - retval = xstrdup(ac->exec.callable.cmd); } // Remove one-shot ("once") autocmd in anticipation of its execution. @@ -2441,60 +2460,14 @@ bool autocmd_delete_id(int64_t id) return success; } -// =========================================================================== -// AucmdExecutable Functions -// =========================================================================== - -/// Generate a string description for the command/callback of an autocmd -char *aucmd_exec_to_string(AutoCmd *ac, AucmdExecutable acc) +/// Gets an (allocated) string representation of an autocmd command/callback. +char *aucmd_handler_to_string(AutoCmd *ac) FUNC_ATTR_PURE { - switch (acc.type) { - case CALLABLE_EX: - return xstrdup(acc.callable.cmd); - case CALLABLE_CB: - return callback_to_string(&acc.callable.cb, NULL); - case CALLABLE_NONE: - return "This is not possible"; + if (ac->handler_cmd) { + return xstrdup(ac->handler_cmd); } - - abort(); -} - -void aucmd_exec_free(AucmdExecutable *acc) -{ - switch (acc->type) { - case CALLABLE_EX: - XFREE_CLEAR(acc->callable.cmd); - break; - case CALLABLE_CB: - callback_free(&acc->callable.cb); - break; - case CALLABLE_NONE: - return; - } - - acc->type = CALLABLE_NONE; -} - -AucmdExecutable aucmd_exec_copy(AucmdExecutable src) -{ - AucmdExecutable dest = AUCMD_EXECUTABLE_INIT; - - switch (src.type) { - case CALLABLE_EX: - dest.type = CALLABLE_EX; - dest.callable.cmd = xstrdup(src.callable.cmd); - return dest; - case CALLABLE_CB: - dest.type = CALLABLE_CB; - callback_copy(&dest.callable.cb, &src.callable.cb); - return dest; - case CALLABLE_NONE: - return dest; - } - - abort(); + return callback_to_string(&ac->handler_fn, NULL); } bool au_event_is_empty(event_T event) |