aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/autocmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/autocmd.c')
-rw-r--r--src/nvim/autocmd.c153
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)