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.c974
1 files changed, 381 insertions, 593 deletions
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 1d09e8f6c3..7a65f11e80 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -15,16 +15,12 @@
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
-#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/eval/vars.h"
-#include "nvim/event/defs.h"
-#include "nvim/event/loop.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
-#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
@@ -34,7 +30,6 @@
#include "nvim/highlight_defs.h"
#include "nvim/insexpand.h"
#include "nvim/lua/executor.h"
-#include "nvim/main.h"
#include "nvim/map.h"
#include "nvim/memline_defs.h"
#include "nvim/memory.h"
@@ -69,41 +64,13 @@
// - Groups start with augroup_
// - Events start with event_
+// The autocommands are stored in a contiguous vector for each event.
//
-// The autocommands are stored in a list for each event.
-// Autocommands for the same pattern, that are consecutive, are joined
-// together, to avoid having to match the pattern too often.
-// The result is an array of Autopat lists, which point to AutoCmd lists:
-//
-// last_autopat[0] -----------------------------+
-// V
-// first_autopat[0] --> Autopat.next --> Autopat.next --> NULL
-// Autopat.cmds Autopat.cmds
-// | |
-// V V
-// AutoCmd.next AutoCmd.next
-// | |
-// V V
-// AutoCmd.next NULL
-// |
-// V
-// NULL
-//
-// last_autopat[1] --------+
-// V
-// first_autopat[1] --> Autopat.next --> NULL
-// Autopat.cmds
-// |
-// V
-// AutoCmd.next
-// |
-// V
-// NULL
-// etc.
-//
-// The order of AutoCmds is important, this is the order in which they were
-// defined and will have to be executed.
+// The order of AutoCmds is important, this is the order in which they
+// were defined and will have to be executed.
//
+// To avoid having to match the pattern too often, patterns are reference
+// counted and reused for consecutive autocommands.
// Code for automatic commands.
static AutoPatCmd *active_apc_list = NULL; // stack of active autocommands
@@ -120,7 +87,7 @@ static int current_augroup = AUGROUP_DEFAULT;
// Whether we need to delete marked patterns.
// While deleting autocmds, they aren't actually remover, just marked.
-static int au_need_clean = false;
+static bool au_need_clean = false;
static int autocmd_blocked = 0; // block all autocmds
@@ -129,20 +96,16 @@ static bool autocmd_include_groups = false;
static char *old_termresponse = NULL;
-/// Iterates over all the AutoPats for a particular event
-#define FOR_ALL_AUPATS_IN_EVENT(event, ap) \
- for (AutoPat *ap = first_autopat[event]; ap != NULL; ap = ap->next) // NOLINT
-
// Map of autocmd group names and ids.
// name -> ID
// ID -> name
static Map(String, int) map_augroup_name_to_id = MAP_INIT;
static Map(int, String) map_augroup_id_to_name = MAP_INIT;
-static void augroup_map_del(int id, char *name)
+static void augroup_map_del(int id, const char *name)
{
if (name != NULL) {
- String key = map_key(String, int)(&map_augroup_name_to_id, cstr_as_string(name));
+ String key = map_key(String, int)(&map_augroup_name_to_id, cstr_as_string((char *)name));
map_del(String, int)(&map_augroup_name_to_id, key);
api_free_string(key);
}
@@ -161,204 +124,191 @@ static inline const char *get_deleted_augroup(void) FUNC_ATTR_ALWAYS_INLINE
return deleted_augroup;
}
-// Show the autocommands for one AutoPat.
-static void aupat_show(AutoPat *ap, event_T event, int previous_group)
+static void au_show_for_all_events(int group, const char *pat)
{
- // Check for "got_int" (here and at various places below), which is set
- // when "q" has been hit for the "--more--" prompt
- if (got_int) {
- return;
+ FOR_ALL_AUEVENTS(event) {
+ au_show_for_event(group, event, pat);
}
+}
- // pattern has been removed
- if (ap->pat == NULL) {
+static void au_show_for_event(int group, event_T event, const char *pat)
+{
+ AutoCmdVec *const acs = &autocmds[(int)event];
+ // Return early if there are no autocmds for this event
+ if (kv_size(*acs) == 0) {
return;
}
- char *name = augroup_name(ap->group);
+ char buflocal_pat[BUFLOCAL_PAT_LEN]; // for "<buffer=X>"
+ int patlen;
+ if (*pat != NUL) {
+ patlen = (int)aucmd_pattern_length(pat);
- msg_putchar('\n');
- if (got_int) {
- return;
- }
- // When switching groups, we need to show the new group information.
- if (ap->group != previous_group) {
- // show the group name, if it's not the default group
- if (ap->group != AUGROUP_DEFAULT) {
- if (name == NULL) {
- msg_puts_attr(get_deleted_augroup(), HL_ATTR(HLF_E));
- } else {
- msg_puts_attr(name, HL_ATTR(HLF_T));
- }
- msg_puts(" ");
+ // detect special <buffer[=X]> buffer-local patterns
+ if (aupat_is_buflocal(pat, patlen)) {
+ // normalize pat into standard "<buffer>#N" form
+ aupat_normalize_buflocal_pat(buflocal_pat, pat, patlen, aupat_get_buflocal_nr(pat, patlen));
+ pat = buflocal_pat;
+ patlen = (int)strlen(buflocal_pat);
}
- // show the event name
- msg_puts_attr(event_nr2name(event), HL_ATTR(HLF_T));
- msg_putchar('\n');
- if (got_int) {
+
+ if (patlen == 0) {
return;
}
+ assert(*pat != NUL);
+ } else {
+ pat = NULL;
+ patlen = 0;
}
- msg_col = 4;
- msg_outtrans(ap->pat);
-
- for (AutoCmd *ac = ap->cmds; ac != NULL; ac = ac->next) {
- // skip removed commands
- if (aucmd_exec_is_deleted(ac->exec)) {
- continue;
- }
+ // Loop through all the specified patterns.
+ while (true) {
+ AutoPat *last_ap = NULL;
+ int last_group = AUGROUP_ERROR;
+ const char *last_group_name = NULL;
- if (msg_col >= 14) {
- msg_putchar('\n');
- }
- msg_col = 14;
- if (got_int) {
- return;
- }
+ for (size_t i = 0; i < kv_size(*acs); i++) {
+ AutoCmd *const ac = &kv_A(*acs, i);
- char *exec_to_string = aucmd_exec_to_string(ac, ac->exec);
- if (ac->desc != NULL) {
- size_t msglen = 100;
- char *msg = xmallocz(msglen);
- if (ac->exec.type == CALLABLE_CB) {
- msg_puts_attr(exec_to_string, HL_ATTR(HLF_8));
- snprintf(msg, msglen, " [%s]", ac->desc);
- } else {
- snprintf(msg, msglen, "%s [%s]", exec_to_string, ac->desc);
+ // Skip deleted autocommands.
+ if (ac->pat == NULL) {
+ continue;
}
- msg_outtrans(msg);
- XFREE_CLEAR(msg);
- } else if (ac->exec.type == CALLABLE_CB) {
- msg_puts_attr(exec_to_string, HL_ATTR(HLF_8));
- } else {
- msg_outtrans(exec_to_string);
- }
- XFREE_CLEAR(exec_to_string);
- if (p_verbose > 0) {
- last_set_msg(ac->script_ctx);
- }
- if (got_int) {
- return;
- }
- if (ac->next != NULL) {
- msg_putchar('\n');
- if (got_int) {
- return;
+
+ // Accept a pattern when:
+ // - a group was specified and it's that group
+ // - the length of the pattern matches
+ // - the pattern matches.
+ // For <buffer[=X]>, this condition works because we normalize
+ // all buffer-local patterns.
+ if ((group != AUGROUP_ALL && ac->pat->group != group)
+ || (pat != NULL
+ && (ac->pat->patlen != patlen || strncmp(pat, ac->pat->pat, (size_t)patlen) != 0))) {
+ continue;
}
- }
- }
-}
-static void au_show_for_all_events(int group, char *pat)
-{
- FOR_ALL_AUEVENTS(event) {
- au_show_for_event(group, event, pat);
- }
-}
+ // Show event name and group only if one of them changed.
+ if (ac->pat->group != last_group) {
+ last_group = ac->pat->group;
+ last_group_name = augroup_name(ac->pat->group);
-static void au_show_for_event(int group, event_T event, char *pat)
-{
- // Return early if there are no autocmds for this event
- if (au_event_is_empty(event)) {
- return;
- }
+ if (got_int) {
+ return;
+ }
- // always need to show group information before the first pattern for the event
- int previous_group = AUGROUP_ERROR;
+ msg_putchar('\n');
+ if (got_int) {
+ return;
+ }
- if (*pat == NUL) {
- FOR_ALL_AUPATS_IN_EVENT(event, ap) {
- if (group == AUGROUP_ALL || ap->group == group) {
- aupat_show(ap, event, previous_group);
- previous_group = ap->group;
+ // When switching groups, we need to show the new group information.
+ // show the group name, if it's not the default group
+ if (ac->pat->group != AUGROUP_DEFAULT) {
+ if (last_group_name == NULL) {
+ msg_puts_attr(get_deleted_augroup(), HL_ATTR(HLF_E));
+ } else {
+ msg_puts_attr(last_group_name, HL_ATTR(HLF_T));
+ }
+ msg_puts(" ");
+ }
+ // show the event name
+ msg_puts_attr(event_nr2name(event), HL_ATTR(HLF_T));
}
- }
- return;
- }
- char buflocal_pat[BUFLOCAL_PAT_LEN]; // for "<buffer=X>"
- // Loop through all the specified patterns.
- int patlen = (int)aucmd_pattern_length(pat);
- while (patlen) {
- // detect special <buffer[=X]> buffer-local patterns
- if (aupat_is_buflocal(pat, patlen)) {
- // normalize pat into standard "<buffer>#N" form
- aupat_normalize_buflocal_pat(buflocal_pat, pat, patlen, aupat_get_buflocal_nr(pat, patlen));
- pat = buflocal_pat;
- patlen = (int)strlen(buflocal_pat);
- }
+ // Show pattern only if it changed.
+ if (last_ap != ac->pat) {
+ last_ap = ac->pat;
- assert(*pat != NUL);
+ msg_putchar('\n');
+ if (got_int) {
+ return;
+ }
- // Find AutoPat entries with this pattern.
- // always goes at or after the last one, so start at the end.
- FOR_ALL_AUPATS_IN_EVENT(event, ap) {
- if (ap->pat != NULL) {
- // Accept a pattern when:
- // - a group was specified and it's that group
- // - the length of the pattern matches
- // - the pattern matches.
- // For <buffer[=X]>, this condition works because we normalize
- // all buffer-local patterns.
- if ((group == AUGROUP_ALL || ap->group == group)
- && ap->patlen == patlen
- && strncmp(pat, ap->pat, (size_t)patlen) == 0) {
- // Show autocmd's for this autopat, or buflocals <buffer=X>
- aupat_show(ap, event, previous_group);
- previous_group = ap->group;
+ msg_col = 4;
+ msg_outtrans(ac->pat->pat);
+ }
+
+ if (got_int) {
+ return;
+ }
+
+ if (msg_col >= 14) {
+ msg_putchar('\n');
+ }
+ msg_col = 14;
+ if (got_int) {
+ return;
+ }
+
+ char *exec_to_string = aucmd_exec_to_string(ac, ac->exec);
+ if (ac->desc != NULL) {
+ size_t msglen = 100;
+ char *msg = xmallocz(msglen);
+ if (ac->exec.type == CALLABLE_CB) {
+ msg_puts_attr(exec_to_string, HL_ATTR(HLF_8));
+ snprintf(msg, msglen, " [%s]", ac->desc);
+ } else {
+ snprintf(msg, msglen, "%s [%s]", exec_to_string, ac->desc);
}
+ msg_outtrans(msg);
+ XFREE_CLEAR(msg);
+ } else if (ac->exec.type == CALLABLE_CB) {
+ msg_puts_attr(exec_to_string, HL_ATTR(HLF_8));
+ } else {
+ msg_outtrans(exec_to_string);
+ }
+ XFREE_CLEAR(exec_to_string);
+ if (p_verbose > 0) {
+ last_set_msg(ac->script_ctx);
+ }
+
+ if (got_int) {
+ return;
}
}
- pat = aucmd_next_pattern(pat, (size_t)patlen);
- patlen = (int)aucmd_pattern_length(pat);
+ // If a pattern is provided, find next pattern. Otherwise exit after single iteration.
+ if (pat != NULL) {
+ pat = aucmd_next_pattern(pat, (size_t)patlen);
+ patlen = (int)aucmd_pattern_length(pat);
+ if (patlen == 0) {
+ break;
+ }
+ } else {
+ break;
+ }
}
}
-// Mark an autocommand handler for deletion.
-static void aupat_del(AutoPat *ap)
-{
- XFREE_CLEAR(ap->pat);
- ap->buflocal_nr = -1;
- au_need_clean = true;
-}
-
-void aupat_del_for_event_and_group(event_T event, int group)
+// Delete autocommand.
+static void aucmd_del(AutoCmd *ac)
{
- FOR_ALL_AUPATS_IN_EVENT(event, ap) {
- if (ap->group == group) {
- aupat_del(ap);
- }
+ if (ac->pat != NULL && --ac->pat->refcount == 0) {
+ XFREE_CLEAR(ac->pat->pat);
+ vim_regfree(ac->pat->reg_prog);
+ xfree(ac->pat);
}
+ ac->pat = NULL;
+ aucmd_exec_free(&ac->exec);
+ XFREE_CLEAR(ac->desc);
- au_cleanup();
+ au_need_clean = true;
}
-// Mark all commands for a pattern for deletion.
-static void aupat_remove_cmds(AutoPat *ap)
+void aucmd_del_for_event_and_group(event_T event, int group)
{
- for (AutoCmd *ac = ap->cmds; ac != NULL; ac = ac->next) {
- aucmd_exec_free(&ac->exec);
-
- if (ac->desc != NULL) {
- XFREE_CLEAR(ac->desc);
+ AutoCmdVec *const acs = &autocmds[(int)event];
+ for (size_t i = 0; i < kv_size(*acs); i++) {
+ AutoCmd *const ac = &kv_A(*acs, i);
+ if (ac->pat->group == group) {
+ aucmd_del(ac);
}
}
- au_need_clean = true;
-}
-// Delete one command from an autocmd pattern.
-static void aucmd_del(AutoCmd *ac)
-{
- aucmd_exec_free(&ac->exec);
- if (ac->desc != NULL) {
- XFREE_CLEAR(ac->desc);
- }
- au_need_clean = true;
+ au_cleanup();
}
-/// Cleanup autocommands and patterns that have been deleted.
+/// Cleanup autocommands that have been deleted.
/// This is only done when not executing autocommands.
static void au_cleanup(void)
{
@@ -368,72 +318,39 @@ static void au_cleanup(void)
// Loop over all events.
FOR_ALL_AUEVENTS(event) {
- // Loop over all autocommand patterns.
- AutoPat **prev_ap = &(first_autopat[(int)event]);
- for (AutoPat *ap = *prev_ap; ap != NULL; ap = *prev_ap) {
- bool has_cmd = false;
-
- // Loop over all commands for this pattern.
- AutoCmd **prev_ac = &(ap->cmds);
- for (AutoCmd *ac = *prev_ac; ac != NULL; ac = *prev_ac) {
- // Remove the command if the pattern is to be deleted or when
- // the command has been marked for deletion.
- if (ap->pat == NULL || aucmd_exec_is_deleted(ac->exec)) {
- *prev_ac = ac->next;
- aucmd_exec_free(&ac->exec);
- if (ac->desc != NULL) {
- XFREE_CLEAR(ac->desc);
- }
-
- xfree(ac);
- } else {
- has_cmd = true;
- prev_ac = &(ac->next);
- }
+ // Loop over all autocommands.
+ AutoCmdVec *const acs = &autocmds[(int)event];
+ size_t nsize = 0;
+ for (size_t i = 0; i < kv_size(*acs); i++) {
+ AutoCmd *const ac = &kv_A(*acs, i);
+ if (nsize != i) {
+ kv_A(*acs, nsize) = *ac;
}
-
- if (ap->pat != NULL && !has_cmd) {
- // Pattern was not marked for deletion, but all of its commands were.
- // So mark the pattern for deletion.
- aupat_del(ap);
- }
-
- // Remove the pattern if it has been marked for deletion.
- if (ap->pat == NULL) {
- if (ap->next == NULL) {
- if (prev_ap == &(first_autopat[(int)event])) {
- last_autopat[(int)event] = NULL;
- } else {
- // this depends on the "next" field being the first in
- // the struct
- last_autopat[(int)event] = (AutoPat *)prev_ap;
- }
- }
- *prev_ap = ap->next;
- vim_regfree(ap->reg_prog);
- xfree(ap);
- } else {
- prev_ap = &(ap->next);
+ if (ac->pat != NULL) {
+ nsize++;
}
}
+ if (nsize == 0) {
+ kv_destroy(*acs);
+ } else {
+ acs->size = nsize;
+ }
}
au_need_clean = false;
}
-// Get the first AutoPat for a particular event.
-AutoPat *au_get_autopat_for_event(event_T event)
+AutoCmdVec *au_get_autocmds_for_event(event_T event)
FUNC_ATTR_PURE
{
- return first_autopat[(int)event];
+ return &autocmds[(int)event];
}
-// Called when buffer is freed, to remove/invalidate related buffer-local
-// autocmds.
+// Called when buffer is freed, to remove/invalidate related buffer-local autocmds.
void aubuflocal_remove(buf_T *buf)
{
// invalidate currently executing autocommands
- for (AutoPatCmd *apc = active_apc_list; apc; apc = apc->next) {
+ for (AutoPatCmd *apc = active_apc_list; apc != NULL; apc = apc->next) {
if (buf->b_fnum == apc->arg_bufnr) {
apc->arg_bufnr = 0;
}
@@ -441,16 +358,19 @@ void aubuflocal_remove(buf_T *buf)
// invalidate buflocals looping through events
FOR_ALL_AUEVENTS(event) {
- FOR_ALL_AUPATS_IN_EVENT(event, ap) {
- if (ap->buflocal_nr == buf->b_fnum) {
- aupat_del(ap);
-
- if (p_verbose >= 6) {
- verbose_enter();
- smsg(_("auto-removing autocommand: %s <buffer=%d>"),
- event_nr2name(event), buf->b_fnum);
- verbose_leave();
- }
+ AutoCmdVec *const acs = &autocmds[(int)event];
+ for (size_t i = 0; i < kv_size(*acs); i++) {
+ AutoCmd *const ac = &kv_A(*acs, i);
+ if (ac->pat == NULL || ac->pat->buflocal_nr != buf->b_fnum) {
+ continue;
+ }
+
+ aucmd_del(ac);
+
+ if (p_verbose >= 6) {
+ verbose_enter();
+ smsg(_("auto-removing autocommand: %s <buffer=%d>"), event_nr2name(event), buf->b_fnum);
+ verbose_leave();
}
}
}
@@ -459,7 +379,7 @@ void aubuflocal_remove(buf_T *buf)
// Add an autocmd group name or return existing group matching name.
// Return its ID.
-int augroup_add(char *name)
+int augroup_add(const char *name)
{
assert(STRICMP(name, "end") != 0);
@@ -495,20 +415,21 @@ int augroup_add(char *name)
/// `--- DELETED ---` groups around)
void augroup_del(char *name, bool stupid_legacy_mode)
{
- int i = augroup_find(name);
- if (i == AUGROUP_ERROR) { // the group doesn't exist
+ int group = augroup_find(name);
+ if (group == AUGROUP_ERROR) { // the group doesn't exist
semsg(_("E367: No such group: \"%s\""), name);
return;
- }
- if (i == current_augroup) {
+ } else if (group == current_augroup) {
emsg(_("E936: Cannot delete the current group"));
return;
}
if (stupid_legacy_mode) {
FOR_ALL_AUEVENTS(event) {
- FOR_ALL_AUPATS_IN_EVENT(event, ap) {
- if (ap->group == i && ap->pat != NULL) {
+ AutoCmdVec *const acs = &autocmds[(int)event];
+ for (size_t i = 0; i < kv_size(*acs); i++) {
+ AutoPat *const ap = kv_A(*acs, i).pat;
+ if (ap != NULL && ap->group == group) {
give_warning(_("W19: Deleting augroup that is still in use"), true);
map_put(String, int)(&map_augroup_name_to_id, cstr_as_string(name), AUGROUP_DELETED);
augroup_map_del(ap->group, NULL);
@@ -518,16 +439,18 @@ void augroup_del(char *name, bool stupid_legacy_mode)
}
} else {
FOR_ALL_AUEVENTS(event) {
- FOR_ALL_AUPATS_IN_EVENT(event, ap) {
- if (ap->group == i) {
- aupat_del(ap);
+ AutoCmdVec *const acs = &autocmds[(int)event];
+ for (size_t i = 0; i < kv_size(*acs); i++) {
+ AutoCmd *const ac = &kv_A(*acs, i);
+ if (ac->pat != NULL && ac->pat->group == group) {
+ aucmd_del(ac);
}
}
}
}
// Remove the group because it's not currently in use.
- augroup_map_del(i, name);
+ augroup_map_del(group, name);
au_cleanup();
}
@@ -636,14 +559,14 @@ void do_augroup(char *arg, int del_group)
void free_all_autocmds(void)
{
FOR_ALL_AUEVENTS(event) {
- FOR_ALL_AUPATS_IN_EVENT(event, ap) {
- aupat_del(ap);
+ AutoCmdVec *const acs = &autocmds[(int)event];
+ for (size_t i = 0; i < kv_size(*acs); i++) {
+ aucmd_del(&kv_A(*acs, i));
}
+ kv_destroy(*acs);
+ au_need_clean = false;
}
- au_need_clean = true;
- au_cleanup();
-
// Delete the augroup_map, including free the data
String name;
int id;
@@ -727,8 +650,7 @@ static bool event_ignored(event_T event)
while (*p != NUL) {
if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ',')) {
return true;
- }
- if (event_name2nr(p, &p) == event) {
+ } else if (event_name2nr(p, &p) == event) {
return true;
}
}
@@ -900,7 +822,7 @@ void do_autocmd(exarg_T *eap, char *arg_in, int forceit)
}
}
- bool is_showing = !forceit && *cmd == NUL;
+ const bool is_showing = !forceit && *cmd == NUL;
// Print header when showing autocommands.
if (is_showing) {
@@ -925,8 +847,7 @@ void do_autocmd(exarg_T *eap, char *arg_in, int forceit)
while (*arg && *arg != '|' && !ascii_iswhite(*arg)) {
event_T event = event_name2nr(arg, &arg);
assert(event < NUM_EVENTS);
- if (do_autocmd_event(event, pat, once, nested, cmd, forceit, group)
- == FAIL) {
+ if (do_autocmd_event(event, pat, once, nested, cmd, forceit, group) == FAIL) {
break;
}
}
@@ -939,11 +860,10 @@ void do_autocmd(exarg_T *eap, char *arg_in, int forceit)
xfree(envpat);
}
-void do_all_autocmd_events(char *pat, bool once, int nested, char *cmd, bool del, int group)
+void do_all_autocmd_events(const char *pat, bool once, int nested, char *cmd, bool del, int group)
{
FOR_ALL_AUEVENTS(event) {
- if (do_autocmd_event(event, pat, once, nested, cmd, del, group)
- == FAIL) {
+ if (do_autocmd_event(event, pat, once, nested, cmd, del, group) == FAIL) {
return;
}
}
@@ -956,30 +876,21 @@ void do_all_autocmd_events(char *pat, bool once, int nested, char *cmd, bool del
// 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, char *pat, bool once, int nested, char *cmd, bool del,
+int do_autocmd_event(event_T event, const char *pat, bool once, int nested, 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
assert(*pat != NUL || del);
- AutoPat *ap;
- AutoPat **prev_ap;
- int findgroup;
- int buflocal_nr;
char buflocal_pat[BUFLOCAL_PAT_LEN]; // for "<buffer=X>"
bool is_adding_cmd = *cmd != NUL;
-
- if (group == AUGROUP_ALL) {
- findgroup = current_augroup;
- } else {
- findgroup = group;
- }
+ const int findgroup = group == AUGROUP_ALL ? current_augroup : group;
// Delete all aupat for an event.
if (*pat == NUL && del) {
- aupat_del_for_event_and_group(event, findgroup);
+ aucmd_del_for_event_and_group(event, findgroup);
return OK;
}
@@ -988,9 +899,8 @@ int do_autocmd_event(event_T event, char *pat, bool once, int nested, char *cmd,
while (patlen) {
// detect special <buffer[=X]> buffer-local patterns
int is_buflocal = aupat_is_buflocal(pat, patlen);
-
if (is_buflocal) {
- buflocal_nr = aupat_get_buflocal_nr(pat, patlen);
+ const int buflocal_nr = aupat_get_buflocal_nr(pat, patlen);
// normalize pat into standard "<buffer>#N" form
aupat_normalize_buflocal_pat(buflocal_pat, pat, patlen, buflocal_nr);
@@ -1002,31 +912,25 @@ int do_autocmd_event(event_T event, char *pat, bool once, int nested, char *cmd,
if (del) {
assert(*pat != NUL);
- // Find AutoPat entries with this pattern.
- prev_ap = &first_autopat[(int)event];
- while ((ap = *prev_ap) != NULL) {
- if (ap->pat != NULL) {
- // Accept a pattern when:
- // - a group was specified and it's that group
- // - the length of the pattern matches
- // - the pattern matches.
- // For <buffer[=X]>, this condition works because we normalize
- // all buffer-local patterns.
- if (ap->group == findgroup
- && ap->patlen == patlen
- && strncmp(pat, ap->pat, (size_t)patlen) == 0) {
- // Remove existing autocommands.
- // If adding any new autocmd's for this AutoPat, don't
- // delete the pattern from the autopat list, append to
- // this list.
- if (is_adding_cmd && ap->next == NULL) {
- aupat_remove_cmds(ap);
- break;
- }
- aupat_del(ap);
- }
+ // Find existing autocommands with this pattern.
+ AutoCmdVec *const acs = &autocmds[(int)event];
+ for (size_t i = 0; i < kv_size(*acs); i++) {
+ AutoCmd *const ac = &kv_A(*acs, i);
+ AutoPat *const ap = ac->pat;
+ // Accept a pattern when:
+ // - a group was specified and it's that group
+ // - the length of the pattern matches
+ // - the pattern matches.
+ // For <buffer[=X]>, this condition works because we normalize
+ // all buffer-local patterns.
+ if (ap != NULL && ap->group == findgroup && ap->patlen == patlen
+ && strncmp(pat, ap->pat, (size_t)patlen) == 0) {
+ // Remove existing autocommands.
+ // If adding any new autocmd's for this AutoPat, don't
+ // delete the pattern from the autopat list, append to
+ // this list.
+ aucmd_del(ac);
}
- prev_ap = &ap->next;
}
}
@@ -1045,32 +949,23 @@ int do_autocmd_event(event_T event, char *pat, bool once, int nested, char *cmd,
return OK;
}
-int autocmd_register(int64_t id, event_T event, char *pat, int patlen, int group, bool once,
+int autocmd_register(int64_t id, event_T event, const char *pat, int patlen, int group, bool once,
bool nested, char *desc, AucmdExecutable aucmd)
{
// 0 is not a valid group.
assert(group != 0);
- AutoPat *ap;
- AutoPat **prev_ap;
- AutoCmd *ac;
- int findgroup;
- char buflocal_pat[BUFLOCAL_PAT_LEN]; // for "<buffer=X>"
-
if (patlen > (int)strlen(pat)) {
return FAIL;
}
- if (group == AUGROUP_ALL) {
- findgroup = current_augroup;
- } else {
- findgroup = group;
- }
+ const int findgroup = group == AUGROUP_ALL ? current_augroup : group;
// detect special <buffer[=X]> buffer-local patterns
- int is_buflocal = aupat_is_buflocal(pat, patlen);
+ const int is_buflocal = aupat_is_buflocal(pat, patlen);
int buflocal_nr = 0;
+ char buflocal_pat[BUFLOCAL_PAT_LEN]; // for "<buffer=X>"
if (is_buflocal) {
buflocal_nr = aupat_get_buflocal_nr(pat, patlen);
@@ -1081,67 +976,52 @@ int autocmd_register(int64_t id, event_T event, char *pat, int patlen, int group
patlen = (int)strlen(buflocal_pat);
}
- // always goes at or after the last one, so start at the end.
- if (last_autopat[(int)event] != NULL) {
- prev_ap = &last_autopat[(int)event];
- } else {
- prev_ap = &first_autopat[(int)event];
- }
-
- while ((ap = *prev_ap) != NULL) {
- if (ap->pat != NULL) {
- // Accept a pattern when:
- // - a group was specified and it's that group
- // - the length of the pattern matches
- // - the pattern matches.
- // For <buffer[=X]>, this condition works because we normalize
- // all buffer-local patterns.
- if (ap->group == findgroup
- && ap->patlen == patlen
- && strncmp(pat, ap->pat, (size_t)patlen) == 0) {
- if (ap->next == NULL) {
- // Add autocmd to this autopat, if it's the last one.
- break;
- }
- }
+ // Try to reuse pattern from the last existing autocommand.
+ AutoPat *ap = NULL;
+ AutoCmdVec *const acs = &autocmds[(int)event];
+ for (ptrdiff_t i = (ptrdiff_t)kv_size(*acs) - 1; i >= 0; i--) {
+ ap = kv_A(*acs, i).pat;
+ if (ap == NULL) {
+ continue; // Skip deleted autocommands.
}
- prev_ap = &ap->next;
+ // Set result back to NULL if the last pattern doesn't match.
+ if (ap->group != findgroup || ap->patlen != patlen
+ || strncmp(pat, ap->pat, (size_t)patlen) != 0) {
+ ap = NULL;
+ }
+ break;
}
- // If the pattern we want to add a command to does appear at the
- // end of the list (or not is not in the list at all), add the
- // pattern at the end of the list.
+ // No matching pattern found, allocate a new one.
if (ap == NULL) {
// refuse to add buffer-local ap if buffer number is invalid
- if (is_buflocal
- && (buflocal_nr == 0 || buflist_findnr(buflocal_nr) == NULL)) {
+ if (is_buflocal && (buflocal_nr == 0 || buflist_findnr(buflocal_nr) == NULL)) {
semsg(_("E680: <buffer=%d>: invalid buffer number "), buflocal_nr);
return FAIL;
}
ap = xmalloc(sizeof(AutoPat));
- ap->pat = xstrnsave(pat, (size_t)patlen);
- ap->patlen = patlen;
if (is_buflocal) {
ap->buflocal_nr = buflocal_nr;
ap->reg_prog = NULL;
} else {
- char *reg_pat;
-
ap->buflocal_nr = 0;
- reg_pat = file_pat_to_reg_pat(pat, pat + patlen, &ap->allow_dirs, true);
+ char *reg_pat = file_pat_to_reg_pat(pat, pat + patlen, &ap->allow_dirs, true);
if (reg_pat != NULL) {
ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
}
xfree(reg_pat);
if (reg_pat == NULL || ap->reg_prog == NULL) {
- xfree(ap->pat);
xfree(ap);
return FAIL;
}
}
+ ap->refcount = 0;
+ ap->pat = xstrnsave(pat, (size_t)patlen);
+ ap->patlen = patlen;
+
// need to initialize last_mode for the first ModeChanged autocmd
if (event == EVENT_MODECHANGED && !has_event(EVENT_MODECHANGED)) {
get_mode(last_mode);
@@ -1168,53 +1048,34 @@ int autocmd_register(int64_t id, event_T event, char *pat, int patlen, int group
use_tabpage(save_curtab);
}
- ap->cmds = NULL;
- *prev_ap = ap;
- last_autopat[(int)event] = ap;
- ap->next = NULL;
- if (group == AUGROUP_ALL) {
- ap->group = current_augroup;
- } else {
- ap->group = group;
- }
- }
-
- // Add the autocmd at the end of the AutoCmd list.
- AutoCmd **prev_ac = &(ap->cmds);
- while ((ac = *prev_ac) != NULL) {
- prev_ac = &ac->next;
+ ap->group = group == AUGROUP_ALL ? current_augroup : group;
}
- ac = xmalloc(sizeof(AutoCmd));
- *prev_ac = ac;
+ ap->refcount++;
+ // Add the autocmd at the end of the AutoCmd vector.
+ AutoCmd *ac = kv_pushp(autocmds[(int)event]);
+ ac->pat = ap;
ac->id = id;
ac->exec = aucmd_exec_copy(aucmd);
ac->script_ctx = current_sctx;
ac->script_ctx.sc_lnum += SOURCING_LNUM;
nlua_set_sctx(&ac->script_ctx);
- ac->next = NULL;
ac->once = once;
ac->nested = nested;
- ac->desc = NULL;
-
- // TODO(tjdevries): What to do about :autocmd and where/how to show lua stuffs there.
- // perhaps: <lua>DESCRIPTION or similar
- if (desc != NULL) {
- ac->desc = xstrdup(desc);
- }
+ ac->desc = desc == NULL ? NULL : xstrdup(desc);
return OK;
}
-size_t aucmd_pattern_length(char *pat)
+size_t aucmd_pattern_length(const char *pat)
FUNC_ATTR_PURE
{
if (*pat == NUL) {
return 0;
}
- char *endpat;
+ const char *endpat;
for (; *pat; pat = endpat + 1) {
// Find end of the pattern.
@@ -1225,8 +1086,7 @@ size_t aucmd_pattern_length(char *pat)
continue;
}
int brace_level = 0;
- for (; *endpat && (*endpat != ',' || brace_level || endpat[-1] == '\\');
- endpat++) {
+ for (; *endpat && (*endpat != ',' || brace_level || endpat[-1] == '\\'); endpat++) {
if (*endpat == '{') {
brace_level++;
} else if (*endpat == '}') {
@@ -1240,14 +1100,13 @@ size_t aucmd_pattern_length(char *pat)
return strlen(pat);
}
-char *aucmd_next_pattern(char *pat, size_t patlen)
+const char *aucmd_next_pattern(const char *pat, size_t patlen)
FUNC_ATTR_PURE
{
pat = pat + patlen;
if (*pat == ',') {
pat = pat + 1;
}
-
return pat;
}
@@ -1637,8 +1496,7 @@ bool apply_autocmds_retval(event_T event, char *fname, char *fname_io, bool forc
return false;
}
- bool did_cmd = apply_autocmds_group(event, fname, fname_io, force,
- AUGROUP_ALL, buf, NULL, NULL);
+ bool did_cmd = apply_autocmds_group(event, fname, fname_io, force, AUGROUP_ALL, buf, NULL, NULL);
if (did_cmd && aborting()) {
*retval = FAIL;
}
@@ -1650,7 +1508,7 @@ bool apply_autocmds_retval(event_T event, char *fname, char *fname_io, bool forc
/// @param event the autocommand to check
bool has_event(event_T event) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- return first_autopat[event] != NULL;
+ return kv_size(autocmds[(int)event]) != 0;
}
/// Return true when there is a CursorHold/CursorHoldI autocommand defined for
@@ -1658,7 +1516,6 @@ bool has_event(event_T event) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
bool has_cursorhold(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return has_event((get_real_state() == MODE_NORMAL_BUSY ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI));
- // return first_autopat[] != NULL;
}
/// Return true if the CursorHold/CursorHoldI event can be triggered.
@@ -1692,7 +1549,6 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
char *sfname = NULL; // short file name
bool retval = false;
static int nesting = 0;
- AutoPat *ap;
char *save_cmdarg;
long save_cmdbang;
static int filechangeshell_busy = false;
@@ -1703,8 +1559,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
// Quickly return if there are no autocommands for this event or
// autocommands are blocked.
- if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
- || is_autocmd_blocked()) {
+ if (event == NUM_EVENTS || kv_size(autocmds[(int)event]) == 0 || is_autocmd_blocked()) {
goto BYPASS_AU;
}
@@ -1722,8 +1577,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
// FileChangedShell never nests, because it can create an endless loop.
if (filechangeshell_busy
- && (event == EVENT_FILECHANGEDSHELL
- || event == EVENT_FILECHANGEDSHELLPOST)) {
+ && (event == EVENT_FILECHANGEDSHELL || event == EVENT_FILECHANGEDSHELLPOST)) {
goto BYPASS_AU;
}
@@ -1742,8 +1596,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
// Check if these autocommands are disabled. Used when doing ":all" or
// ":ball".
if ((autocmd_no_enter && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
- || (autocmd_no_leave
- && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE))) {
+ || (autocmd_no_leave && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE))) {
goto BYPASS_AU;
}
@@ -1779,11 +1632,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
}
// Set the buffer number to be used for <abuf>.
- if (buf == NULL) {
- autocmd_bufnr = 0;
- } else {
- autocmd_bufnr = buf->b_fnum;
- }
+ autocmd_bufnr = buf == NULL ? 0 : buf->b_fnum;
// When the file name is NULL or empty, use the file name of buffer "buf".
// Always use the full path of the file name to match with, in case
@@ -1886,18 +1735,24 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
// Find first autocommand that matches
AutoPatCmd patcmd = {
- .curpat = first_autopat[(int)event],
- .group = group,
+ // aucmd_next will set lastpat back to NULL if there are no more autocommands left to run
+ .lastpat = NULL,
+ // current autocommand index
+ .auidx = 0,
+ // save vector size, to avoid an endless loop when more patterns
+ // are added when executing autocommands
+ .ausize = kv_size(autocmds[(int)event]),
.fname = fname,
.sfname = sfname,
.tail = tail,
+ .group = group,
.event = event,
.arg_bufnr = autocmd_bufnr,
};
- auto_next_pat(&patcmd, false);
+ aucmd_next(&patcmd);
- // found one, start executing the autocommands
- if (patcmd.curpat != NULL) {
+ // Found first autocommand, start executing them
+ if (patcmd.lastpat != NULL) {
// add to active_apc_list
patcmd.next = active_apc_list;
active_apc_list = &patcmd;
@@ -1914,12 +1769,6 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
save_cmdarg = NULL; // avoid gcc warning
}
retval = true;
- // mark the last pattern, to avoid an endless loop when more patterns
- // are added when executing autocommands
- for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next) {
- ap->last = false;
- }
- ap->last = true;
// Make sure cursor and topline are valid. The first time the current
// values are saved, restored by reset_lnums(). When nested only the
@@ -1931,8 +1780,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
}
// Execute the autocmd. The `getnextac` callback handles iteration.
- do_cmdline(NULL, getnextac, (void *)&patcmd,
- DOCMD_NOWAIT | DOCMD_VERBOSE | DOCMD_REPEAT);
+ do_cmdline(NULL, getnextac, (void *)&patcmd, DOCMD_NOWAIT | DOCMD_VERBOSE | DOCMD_REPEAT);
if (nesting == 1) {
// restore cursor and topline, unless they were changed
@@ -2038,8 +1886,7 @@ void unblock_autocmds(void)
// When v:termresponse was set while autocommands were blocked, trigger
// the autocommands now. Esp. useful when executing a shell command
// during startup (nvim -d).
- if (!is_autocmd_blocked()
- && get_vim_var_str(VV_TERMRESPONSE) != old_termresponse) {
+ if (!is_autocmd_blocked() && get_vim_var_str(VV_TERMRESPONSE) != old_termresponse) {
apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, false, curbuf);
}
}
@@ -2050,77 +1897,70 @@ bool is_autocmd_blocked(void)
return autocmd_blocked != 0;
}
-/// Find next autocommand pattern that matches.
-/// stop when 'last' flag is set
-void auto_next_pat(AutoPatCmd *apc, int stop_at_last)
+/// Find next matching autocommand.
+/// If next autocommand was not found, sets lastpat to NULL and cmdidx to SIZE_MAX on apc.
+static void aucmd_next(AutoPatCmd *apc)
{
- AutoPat *ap;
- AutoCmd *cp;
- char *s;
-
estack_T *const entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
- // Clear the exestack entry for this ETYPE_AUCMD entry.
- XFREE_CLEAR(entry->es_name);
- entry->es_info.aucmd = NULL;
-
- for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next) {
- apc->curpat = NULL;
+ AutoCmdVec *const acs = &autocmds[(int)apc->event];
+ assert(apc->ausize <= kv_size(*acs));
+ for (size_t i = apc->auidx; i < apc->ausize && !got_int; i++) {
+ AutoCmd *const ac = &kv_A(*acs, i);
+ AutoPat *const ap = ac->pat;
- // Only use a pattern when it has not been removed, has commands and
- // the group matches. For buffer-local autocommands only check the
- // buffer number.
- if (ap->pat != NULL && ap->cmds != NULL
- && (apc->group == AUGROUP_ALL || apc->group == ap->group)) {
- // execution-condition
+ // Skip deleted autocommands.
+ if (ap == NULL) {
+ continue;
+ }
+ // Skip matching if pattern didn't change.
+ if (ap != apc->lastpat) {
+ // Skip autocommands that don't match the group.
+ if (apc->group != AUGROUP_ALL && apc->group != ap->group) {
+ continue;
+ }
+ // Skip autocommands that don't match the pattern or buffer number.
if (ap->buflocal_nr == 0
- ? match_file_pat(NULL,
- &ap->reg_prog,
- apc->fname,
- apc->sfname,
- apc->tail,
- ap->allow_dirs)
- : ap->buflocal_nr == apc->arg_bufnr) {
- const char *const name = event_nr2name(apc->event);
- s = _("%s Autocommands for \"%s\"");
-
- const size_t sourcing_name_len
- = (strlen(s) + strlen(name) + (size_t)ap->patlen + 1);
-
- char *const namep = xmalloc(sourcing_name_len);
- snprintf(namep, sourcing_name_len, s, name, ap->pat);
- if (p_verbose >= 8) {
- verbose_enter();
- smsg(_("Executing %s"), namep);
- verbose_leave();
- }
+ ? !match_file_pat(NULL, &ap->reg_prog, apc->fname, apc->sfname, apc->tail, ap->allow_dirs)
+ : ap->buflocal_nr != apc->arg_bufnr) {
+ continue;
+ }
- // Update the exestack entry for this autocmd.
- entry->es_name = namep;
- entry->es_info.aucmd = apc;
+ const char *const name = event_nr2name(apc->event);
+ const char *const s = _("%s Autocommands for \"%s\"");
- apc->curpat = ap;
- apc->nextcmd = ap->cmds;
- // mark last command
- for (cp = ap->cmds; cp->next != NULL; cp = cp->next) {
- cp->last = false;
- }
- cp->last = true;
+ const size_t sourcing_name_len = strlen(s) + strlen(name) + (size_t)ap->patlen + 1;
+ char *const namep = xmalloc(sourcing_name_len);
+ snprintf(namep, sourcing_name_len, s, name, ap->pat);
+ if (p_verbose >= 8) {
+ verbose_enter();
+ smsg(_("Executing %s"), namep);
+ verbose_leave();
}
- line_breakcheck();
- if (apc->curpat != NULL) { // found a match
- break;
- }
- }
- if (stop_at_last && ap->last) {
- break;
+
+ // Update the exestack entry for this autocmd.
+ XFREE_CLEAR(entry->es_name);
+ entry->es_name = namep;
+ entry->es_info.aucmd = apc;
}
+
+ apc->lastpat = ap;
+ apc->auidx = i;
+
+ line_breakcheck();
+ return;
}
+
+ // Clear the exestack entry for this ETYPE_AUCMD entry.
+ XFREE_CLEAR(entry->es_name);
+ entry->es_info.aucmd = NULL;
+
+ apc->lastpat = NULL;
+ apc->auidx = SIZE_MAX;
}
static bool call_autocmd_callback(const AutoCmd *ac, const AutoPatCmd *apc)
{
- bool ret = false;
Callback callback = ac->exec.callable.cb;
if (callback.type == kCallbackLua) {
Dictionary data = ARRAY_DICT_INIT;
@@ -2134,7 +1974,7 @@ static bool call_autocmd_callback(const AutoCmd *ac, const AutoPatCmd *apc)
PUT(data, "data", copy_object(*apc->data, NULL));
}
- int group = apc->curpat->group;
+ int group = ac->pat->group;
switch (group) {
case AUGROUP_ERROR:
abort(); // unreachable
@@ -2152,18 +1992,19 @@ static bool call_autocmd_callback(const AutoCmd *ac, const AutoPatCmd *apc)
ADD_C(args, DICTIONARY_OBJ(data));
Object result = nlua_call_ref(callback.data.luaref, NULL, args, true, NULL);
+ bool ret = false;
if (result.type == kObjectTypeBoolean) {
ret = result.data.boolean;
}
api_free_dictionary(data);
api_free_object(result);
+ return ret;
} else {
typval_T argsin = TV_INITIAL_VALUE;
typval_T rettv = TV_INITIAL_VALUE;
callback_call(&callback, 0, &argsin, &rettv);
+ return false;
}
-
- return ret;
}
/// Get next autocommand command.
@@ -2176,45 +2017,16 @@ char *getnextac(int c, void *cookie, int indent, bool do_concat)
(void)indent;
(void)do_concat;
- AutoPatCmd *acp = (AutoPatCmd *)cookie;
- char *retval;
+ AutoPatCmd *const apc = (AutoPatCmd *)cookie;
+ AutoCmdVec *const acs = &autocmds[(int)apc->event];
- // Can be called again after returning the last line.
- if (acp->curpat == NULL) {
+ aucmd_next(apc);
+ if (apc->lastpat == NULL) {
return NULL;
}
- // repeat until we find an autocommand to execute
- while (true) {
- // skip removed commands
- while (acp->nextcmd != NULL
- && aucmd_exec_is_deleted(acp->nextcmd->exec)) {
- if (acp->nextcmd->last) {
- acp->nextcmd = NULL;
- } else {
- acp->nextcmd = acp->nextcmd->next;
- }
- }
-
- if (acp->nextcmd != NULL) {
- break;
- }
-
- // at end of commands, find next pattern that matches
- if (acp->curpat->last) {
- acp->curpat = NULL;
- } else {
- acp->curpat = acp->curpat->next;
- }
- if (acp->curpat != NULL) {
- auto_next_pat(acp, true);
- }
- if (acp->curpat == NULL) {
- return NULL;
- }
- }
-
- AutoCmd *ac = acp->nextcmd;
+ assert(apc->auidx < kv_size(*acs));
+ AutoCmd *const ac = &kv_A(*acs, apc->auidx);
bool oneshot = ac->once;
if (p_verbose >= 9) {
@@ -2230,10 +2042,12 @@ char *getnextac(int c, void *cookie, int indent, bool do_concat)
// lua code, so that it works properly
autocmd_nested = ac->nested;
current_sctx = ac->script_ctx;
- acp->script_ctx = current_sctx;
+ apc->script_ctx = current_sctx;
+ char *retval;
if (ac->exec.type == CALLABLE_CB) {
- if (call_autocmd_callback(ac, acp)) {
+ // 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;
}
@@ -2248,19 +2062,20 @@ char *getnextac(int c, void *cookie, int indent, bool do_concat)
// 2. make where we call do_cmdline for autocmds not have to return anything,
// and instead we loop over all the matches and just execute one-by-one.
// However, my expectation would be that could be expensive.
- retval = xstrdup("");
+ retval = xcalloc(1, 1);
} else {
retval = xstrdup(ac->exec.callable.cmd);
}
// Remove one-shot ("once") autocmd in anticipation of its execution.
if (oneshot) {
- aucmd_del(ac);
+ aucmd_del(&kv_A(*acs, apc->auidx));
}
- if (ac->last) {
- acp->nextcmd = NULL;
+
+ if (apc->auidx < apc->ausize) {
+ apc->auidx++;
} else {
- acp->nextcmd = ac->next;
+ apc->auidx = SIZE_MAX;
}
return retval;
@@ -2292,15 +2107,12 @@ bool has_autocmd(event_T event, char *sfname, buf_T *buf)
forward_slash(fname);
#endif
- for (AutoPat *ap = first_autopat[(int)event]; ap != NULL; ap = ap->next) {
- if (ap->pat != NULL && ap->cmds != NULL
+ AutoCmdVec *const acs = &autocmds[(int)event];
+ for (size_t i = 0; i < kv_size(*acs); i++) {
+ AutoPat *const ap = kv_A(*acs, i).pat;
+ if (ap != NULL
&& (ap->buflocal_nr == 0
- ? match_file_pat(NULL,
- &ap->reg_prog,
- fname,
- sfname,
- tail,
- ap->allow_dirs)
+ ? match_file_pat(NULL, &ap->reg_prog, fname, sfname, tail, ap->allow_dirs)
: buf != NULL && ap->buflocal_nr == buf->b_fnum)) {
retval = true;
break;
@@ -2315,13 +2127,10 @@ bool has_autocmd(event_T event, char *sfname, buf_T *buf)
return retval;
}
-// Function given to ExpandGeneric() to obtain the list of autocommand group
-// names.
+// Function given to ExpandGeneric() to obtain the list of autocommand group names.
char *expand_get_augroup_name(expand_T *xp, int idx)
{
- // Required for ExpandGeneric
- (void)xp;
-
+ (void)xp; // Required for ExpandGeneric
return augroup_name(idx + 1);
}
@@ -2374,8 +2183,7 @@ char *set_context_in_autocmd(expand_T *xp, char *arg, int doautocmd)
// Function given to ExpandGeneric() to obtain the list of event names.
char *expand_get_event_name(expand_T *xp, int idx)
{
- // xp is a required parameter to be used with ExpandGeneric
- (void)xp;
+ (void)xp; // xp is a required parameter to be used with ExpandGeneric
// List group names
char *name = augroup_name(idx + 1);
@@ -2416,7 +2224,8 @@ bool autocmd_supported(const char *const event)
/// exists("#Event#pat")
///
/// @param arg autocommand string
-bool au_exists(const char *const arg) FUNC_ATTR_WARN_UNUSED_RESULT
+bool au_exists(const char *const arg)
+ FUNC_ATTR_WARN_UNUSED_RESULT
{
buf_T *buflocal_buf = NULL;
bool retval = false;
@@ -2463,8 +2272,8 @@ bool au_exists(const char *const arg) FUNC_ATTR_WARN_UNUSED_RESULT
// Find the first autocommand for this event.
// If there isn't any, return false;
// If there is one and no pattern given, return true;
- AutoPat *ap = first_autopat[(int)event];
- if (ap == NULL) {
+ AutoCmdVec *const acs = &autocmds[(int)event];
+ if (kv_size(*acs) == 0) {
goto theend;
}
@@ -2475,11 +2284,11 @@ bool au_exists(const char *const arg) FUNC_ATTR_WARN_UNUSED_RESULT
}
// Check if there is an autocommand with the given pattern.
- for (; ap != NULL; ap = ap->next) {
- // only use a pattern when it has not been removed and has commands.
+ for (size_t i = 0; i < kv_size(*acs); i++) {
+ AutoPat *const ap = kv_A(*acs, i).pat;
+ // Only use a pattern when it has not been removed.
// For buffer-local autocommands, path_fnamecmp() works fine.
- if (ap->pat != NULL && ap->cmds != NULL
- && (group == AUGROUP_ALL || ap->group == group)
+ if ((group == AUGROUP_ALL || ap->group == group)
&& (pattern == NULL
|| (buflocal_buf == NULL
? path_fnamecmp(ap->pat, pattern) == 0
@@ -2495,15 +2304,13 @@ theend:
}
// Checks if a pattern is buflocal
-bool aupat_is_buflocal(char *pat, int patlen)
+bool aupat_is_buflocal(const char *pat, int patlen)
FUNC_ATTR_PURE
{
- return patlen >= 8
- && strncmp(pat, "<buffer", 7) == 0
- && (pat)[patlen - 1] == '>';
+ return patlen >= 8 && strncmp(pat, "<buffer", 7) == 0 && (pat)[patlen - 1] == '>';
}
-int aupat_get_buflocal_nr(char *pat, int patlen)
+int aupat_get_buflocal_nr(const char *pat, int patlen)
{
assert(aupat_is_buflocal(pat, patlen));
@@ -2528,7 +2335,7 @@ int aupat_get_buflocal_nr(char *pat, int patlen)
}
// normalize buffer pattern
-void aupat_normalize_buflocal_pat(char *dest, char *pat, int patlen, int buflocal_nr)
+void aupat_normalize_buflocal_pat(char *dest, const char *pat, int patlen, int buflocal_nr)
{
assert(aupat_is_buflocal(pat, patlen));
@@ -2537,13 +2344,10 @@ void aupat_normalize_buflocal_pat(char *dest, char *pat, int patlen, int bufloca
}
// normalize pat into standard "<buffer>#N" form
- snprintf(dest,
- BUFLOCAL_PAT_LEN,
- "<buffer=%d>",
- buflocal_nr);
+ snprintf(dest, BUFLOCAL_PAT_LEN, "<buffer=%d>", buflocal_nr);
}
-int autocmd_delete_event(int group, event_T event, char *pat)
+int autocmd_delete_event(int group, event_T event, const char *pat)
FUNC_ATTR_NONNULL_ALL
{
return do_autocmd_event(event, pat, false, false, "", true, group);
@@ -2559,12 +2363,12 @@ bool autocmd_delete_id(int64_t id)
// Note that since multiple AutoCmd objects can have the same ID, we need to do a full scan.
FOR_ALL_AUEVENTS(event) {
- FOR_ALL_AUPATS_IN_EVENT(event, ap) { // -V756
- for (AutoCmd *ac = ap->cmds; ac != NULL; ac = ac->next) {
- if (ac->id == id) {
- aucmd_del(ac);
- success = true;
- }
+ AutoCmdVec *const acs = &autocmds[(int)event];
+ for (size_t i = 0; i < kv_size(*acs); i++) {
+ AutoCmd *const ac = &kv_A(*acs, i);
+ if (ac->id == id) {
+ aucmd_del(ac);
+ success = true;
}
}
}
@@ -2627,25 +2431,10 @@ AucmdExecutable aucmd_exec_copy(AucmdExecutable src)
abort();
}
-bool aucmd_exec_is_deleted(AucmdExecutable acc)
- FUNC_ATTR_PURE
-{
- switch (acc.type) {
- case CALLABLE_EX:
- return acc.callable.cmd == NULL;
- case CALLABLE_CB:
- return acc.callable.cb.type == kCallbackNone;
- case CALLABLE_NONE:
- return true;
- }
-
- abort();
-}
-
bool au_event_is_empty(event_T event)
FUNC_ATTR_PURE
{
- return first_autopat[event] == NULL;
+ return kv_size(autocmds[(int)event]) == 0;
}
// Arg Parsing Functions
@@ -2734,8 +2523,7 @@ void do_autocmd_uienter(uint64_t chanid, bool attached)
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);
+ apply_autocmds(attached ? EVENT_UIENTER : EVENT_UILEAVE, NULL, NULL, false, curbuf);
restore_v_event(dict, &save_v_event);
recursive = false;