aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/autocmd.c
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2023-11-29 21:52:58 +0000
committerJosh Rahm <joshuarahm@gmail.com>2023-11-29 21:52:58 +0000
commit931bffbda3668ddc609fc1da8f9eb576b170aa52 (patch)
treed8c1843a95da5ea0bb4acc09f7e37843d9995c86 /src/nvim/autocmd.c
parent142d9041391780ac15b89886a54015fdc5c73995 (diff)
parent4a8bf24ac690004aedf5540fa440e788459e5e34 (diff)
downloadrneovim-userreg.tar.gz
rneovim-userreg.tar.bz2
rneovim-userreg.zip
Merge remote-tracking branch 'upstream/master' into userreguserreg
Diffstat (limited to 'src/nvim/autocmd.c')
-rw-r--r--src/nvim/autocmd.c1210
1 files changed, 524 insertions, 686 deletions
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 01ebdfdafe..74a1dbdbc3 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -1,45 +1,43 @@
-// This is an open source non-commercial project. Dear PVS-Studio, please check
-// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-
// autocmd.c: Autocommand related functions
#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <time.h>
+#include "klib/kvec.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/ascii.h"
+#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand_defs.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/event/multiqueue.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/func_attr.h"
#include "nvim/getchar.h"
#include "nvim/gettext.h"
+#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/hashtab.h"
-#include "nvim/highlight_defs.h"
+#include "nvim/highlight.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/map_defs.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/option_defs.h"
+#include "nvim/option.h"
+#include "nvim/option_vars.h"
#include "nvim/optionstr.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
@@ -48,20 +46,24 @@
#include "nvim/profile.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/state.h"
#include "nvim/strings.h"
+#include "nvim/types_defs.h"
#include "nvim/ui.h"
#include "nvim/ui_compositor.h"
-#include "nvim/vim.h"
+#include "nvim/vim_defs.h"
#include "nvim/window.h"
+#include "nvim/winfloat.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "auevents_name_map.generated.h"
# include "autocmd.c.generated.h"
#endif
+static const char e_autocommand_nesting_too_deep[]
+ = N_("E218: Autocommand nesting too deep");
+
// Naming Conventions:
// - general autocmd behavior start with au_
// - AutoCmd start with aucmd_
@@ -70,41 +72,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
@@ -121,7 +95,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
@@ -130,27 +104,22 @@ 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));
- map_del(String, int)(&map_augroup_name_to_id, key);
+ String key;
+ map_del(String, int)(&map_augroup_name_to_id, cstr_as_string((char *)name), &key);
api_free_string(key);
}
if (id > 0) {
- String mapped = map_get(int, String)(&map_augroup_id_to_name, id);
+ String mapped = map_del(int, String)(&map_augroup_id_to_name, id, NULL);
api_free_string(mapped);
- map_del(int, String)(&map_augroup_id_to_name, id);
}
}
@@ -162,204 +131,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 = (char *)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 = (char *)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, 0);
+ }
+
+ 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, 0);
+ 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, 0);
+ }
+ 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 != NULL && 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)
{
@@ -369,72 +325,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;
}
@@ -442,16 +365,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(0, _("auto-removing autocommand: %s <buffer=%d>"), event_nr2name(event), buf->b_fnum);
+ verbose_leave();
}
}
}
@@ -460,7 +386,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);
@@ -496,20 +422,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);
@@ -519,16 +446,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();
}
@@ -637,28 +566,25 @@ 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;
- map_foreach(&map_augroup_name_to_id, name, id, {
- (void)id;
+ map_foreach_key(&map_augroup_name_to_id, name, {
api_free_string(name);
})
- map_destroy(String, int)(&map_augroup_name_to_id);
+ map_destroy(String, &map_augroup_name_to_id);
- map_foreach(&map_augroup_id_to_name, id, name, {
- (void)id;
+ map_foreach_value(&map_augroup_id_to_name, name, {
api_free_string(name);
})
- map_destroy(int, String)(&map_augroup_id_to_name);
+ map_destroy(int, &map_augroup_id_to_name);
// aucmd_win[] is freed in win_free_all()
}
@@ -675,9 +601,9 @@ bool is_aucmd_win(win_T *win)
return false;
}
-// Return the event number for event name "start".
-// Return NUM_EVENTS if the event name was not found.
-// Return a pointer to the next event name in "end".
+/// Return the event number for event name "start".
+/// Return NUM_EVENTS if the event name was not found.
+/// Return a pointer to the next event name in "end".
event_T event_name2nr(const char *start, char **end)
{
const char *p;
@@ -701,6 +627,18 @@ event_T event_name2nr(const char *start, char **end)
return event_names[i].event;
}
+/// Return the event number for event name "str".
+/// Return NUM_EVENTS if the event name was not found.
+event_T event_name2nr_str(String str)
+{
+ for (int i = 0; event_names[i].name != NULL; i++) {
+ if (str.size == event_names[i].len && STRNICMP(str.data, event_names[i].name, str.size) == 0) {
+ return event_names[i].event;
+ }
+ }
+ return NUM_EVENTS;
+}
+
/// Return the name for event
///
/// @param[in] event Event to return name for.
@@ -728,8 +666,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;
}
}
@@ -901,7 +838,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) {
@@ -926,8 +863,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;
}
}
@@ -940,11 +876,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 delete, 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, delete, group)
- == FAIL) {
+ if (do_autocmd_event(event, pat, once, nested, cmd, del, group) == FAIL) {
return;
}
}
@@ -957,30 +892,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 delete,
+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 || delete);
+ 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 && delete) {
- aupat_del_for_event_and_group(event, findgroup);
+ if (*pat == NUL && del) {
+ aucmd_del_for_event_and_group(event, findgroup);
return OK;
}
@@ -989,9 +915,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);
@@ -1000,34 +925,28 @@ int do_autocmd_event(event_T event, char *pat, bool once, int nested, char *cmd,
patlen = (int)strlen(buflocal_pat);
}
- if (delete) {
+ 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;
}
}
@@ -1046,32 +965,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);
@@ -1082,67 +992,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.
+ }
+ // 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;
}
- prev_ap = &ap->next;
+ 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 = xmemdupz(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);
@@ -1151,7 +1046,8 @@ int autocmd_register(int64_t id, event_T event, char *pat, int patlen, int group
// If the event is CursorMoved, update the last cursor position
// position to avoid immediately triggering the autocommand
if (event == EVENT_CURSORMOVED && !has_event(EVENT_CURSORMOVED)) {
- curwin->w_last_cursormoved = curwin->w_cursor;
+ last_cursormoved_win = curwin;
+ last_cursormoved = curwin->w_cursor;
}
// Initialize the fields checked by the WinScrolled and
@@ -1169,53 +1065,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.
@@ -1226,8 +1103,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 == '}') {
@@ -1241,14 +1117,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;
}
@@ -1291,7 +1166,7 @@ int do_doautocmd(char *arg_start, bool do_msg, bool *did_something)
}
if (nothing_done && do_msg && !aborting()) {
- smsg(_("No matching autocommands: %s"), arg_start);
+ smsg(0, _("No matching autocommands: %s"), arg_start);
}
if (did_something != NULL) {
*did_something = !nothing_done;
@@ -1422,6 +1297,8 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf)
aco->save_curwin_handle = curwin->handle;
aco->save_curbuf = curbuf;
aco->save_prevwin_handle = prevwin == NULL ? 0 : prevwin->handle;
+ aco->save_State = State;
+
if (win != NULL) {
// There is a window for "buf" in the current tab page, make it the
// curwin. This is preferred, it has the least side effects (esp. if
@@ -1448,7 +1325,7 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf)
block_autocmds(); // We don't want BufEnter/WinEnter autocommands.
if (need_append) {
win_append(lastwin, auc_win);
- pmap_put(handle_T)(&window_handles, auc_win->handle, auc_win);
+ pmap_put(int)(&window_handles, auc_win->handle, auc_win);
win_config_float(auc_win, auc_win->w_float_config);
}
// Prevent chdir() call in win_enter_ext(), through do_autochdir()
@@ -1480,7 +1357,6 @@ void aucmd_restbuf(aco_save_T *aco)
if (aco->use_aucmd_win_idx >= 0) {
win_T *awp = aucmd_win[aco->use_aucmd_win_idx].auc_win;
- curbuf->b_nwindows--;
// Find "awp", it can't be closed, but it may be in another tab page.
// Do not trigger autocommands here.
block_autocmds();
@@ -1496,9 +1372,17 @@ void aucmd_restbuf(aco_save_T *aco)
}
}
win_found:
-
+ curbuf->b_nwindows--;
+ const bool save_stop_insert_mode = stop_insert_mode;
+ // May need to stop Insert mode if we were in a prompt buffer.
+ leaving_window(curwin);
+ // Do not stop Insert mode when already in Insert mode before.
+ if (aco->save_State & MODE_INSERT) {
+ stop_insert_mode = save_stop_insert_mode;
+ }
+ // Remove the window.
win_remove(curwin, NULL);
- pmap_del(handle_T)(&window_handles, curwin->handle);
+ pmap_del(int)(&window_handles, curwin->handle, NULL);
if (curwin->w_grid_alloc.chars != NULL) {
ui_comp_remove_grid(&curwin->w_grid_alloc);
ui_call_win_hide(curwin->w_grid_alloc.handle);
@@ -1535,6 +1419,7 @@ win_found:
globaldir = aco->globaldir;
// the buffer contents may have changed
+ VIsual_active = aco->save_VIsual_active;
check_cursor();
if (curwin->w_topline > curbuf->b_ml.ml_line_count) {
curwin->w_topline = curbuf->b_ml.ml_line_count;
@@ -1563,14 +1448,16 @@ win_found:
curwin = save_curwin;
curbuf = curwin->w_buffer;
prevwin = win_find_by_handle(aco->save_prevwin_handle);
+
// In case the autocommand moves the cursor to a position that does not
// exist in curbuf
+ VIsual_active = aco->save_VIsual_active;
check_cursor();
}
}
- check_cursor(); // just in case lines got deleted
VIsual_active = aco->save_VIsual_active;
+ check_cursor(); // just in case lines got deleted
if (VIsual_active) {
check_pos(curbuf, &VIsual);
}
@@ -1627,8 +1514,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;
}
@@ -1640,7 +1526,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
@@ -1648,7 +1534,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.
@@ -1682,9 +1567,8 @@ 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;
+ varnumber_T save_cmdbang;
static int filechangeshell_busy = false;
proftime_T wait_time;
bool did_save_redobuff = false;
@@ -1693,8 +1577,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;
}
@@ -1712,8 +1595,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;
}
@@ -1725,20 +1607,20 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
// Allow nesting of autocommands, but restrict the depth, because it's
// possible to create an endless loop.
if (nesting == 10) {
- emsg(_("E218: autocommand nesting too deep"));
+ emsg(_(e_autocommand_nesting_too_deep));
goto BYPASS_AU;
}
// 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;
}
// Save the autocmd_* variables and info about the current buffer.
char *save_autocmd_fname = autocmd_fname;
+ bool save_autocmd_fname_full = autocmd_fname_full;
int save_autocmd_bufnr = autocmd_bufnr;
char *save_autocmd_match = autocmd_match;
int save_autocmd_busy = autocmd_busy;
@@ -1767,13 +1649,10 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
// Allocate MAXPATHL for when eval_vars() resolves the fullpath.
autocmd_fname = xstrnsave(autocmd_fname, MAXPATHL);
}
+ autocmd_fname_full = false; // call FullName_save() later
// 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
@@ -1814,6 +1693,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
|| event == EVENT_USER || event == EVENT_WINCLOSED
|| event == EVENT_WINRESIZED || event == EVENT_WINSCROLLED) {
fname = xstrdup(fname);
+ autocmd_fname_full = true; // don't expand it later
} else {
fname = FullName_save(fname, false);
}
@@ -1876,18 +1756,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;
@@ -1896,20 +1782,14 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
patcmd.data = data;
// set v:cmdarg (only when there is a matching pattern)
- save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
+ save_cmdbang = get_vim_var_nr(VV_CMDBANG);
if (eap != NULL) {
save_cmdarg = set_cmdarg(eap, NULL);
- set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
+ set_vim_var_nr(VV_CMDBANG, eap->forceit);
} else {
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
@@ -1920,9 +1800,14 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
check_lnums_nested(true);
}
+ const int save_did_emsg = did_emsg;
+ const bool save_ex_pressedreturn = get_pressedreturn();
+
// Execute the autocmd. The `getnextac` callback handles iteration.
- do_cmdline(NULL, getnextac, (void *)&patcmd,
- DOCMD_NOWAIT | DOCMD_VERBOSE | DOCMD_REPEAT);
+ do_cmdline(NULL, getnextac, &patcmd, DOCMD_NOWAIT | DOCMD_VERBOSE | DOCMD_REPEAT);
+
+ did_emsg += save_did_emsg;
+ set_pressedreturn(save_ex_pressedreturn);
if (nesting == 1) {
// restore cursor and topline, unless they were changed
@@ -1947,6 +1832,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
estack_pop();
xfree(autocmd_fname);
autocmd_fname = save_autocmd_fname;
+ autocmd_fname_full = save_autocmd_fname_full;
autocmd_bufnr = save_autocmd_bufnr;
autocmd_match = save_autocmd_match;
current_sctx = save_current_sctx;
@@ -2028,8 +1914,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);
}
}
@@ -2040,91 +1925,84 @@ 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;
+ 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;
- for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next) {
- apc->curpat = NULL;
-
- // 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(0, _("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;
PUT(data, "id", INTEGER_OBJ(ac->id));
PUT(data, "event", CSTR_TO_OBJ(event_nr2name(apc->event)));
- PUT(data, "match", CSTR_TO_OBJ((char *)autocmd_match));
- PUT(data, "file", CSTR_TO_OBJ((char *)autocmd_fname));
+ PUT(data, "match", CSTR_TO_OBJ(autocmd_match));
+ PUT(data, "file", CSTR_TO_OBJ(autocmd_fname));
PUT(data, "buf", INTEGER_OBJ(autocmd_bufnr));
if (apc->data) {
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
@@ -2142,18 +2020,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.
@@ -2166,51 +2045,23 @@ 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
- for (;;) {
- // 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);
+ assert(ac->pat != NULL);
bool oneshot = ac->once;
if (p_verbose >= 9) {
verbose_enter_scroll();
char *exec_to_string = aucmd_exec_to_string(ac, ac->exec);
- smsg(_("autocommand %s"), exec_to_string);
+ smsg(0, _("autocommand %s"), exec_to_string);
msg_puts("\n"); // don't overwrite this either
XFREE_CLEAR(exec_to_string);
verbose_leave_scroll();
@@ -2220,10 +2071,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;
}
@@ -2238,19 +2091,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;
@@ -2282,15 +2136,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;
@@ -2305,13 +2156,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);
}
@@ -2364,8 +2212,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);
@@ -2382,6 +2229,13 @@ char *expand_get_event_name(expand_T *xp, int idx)
return event_names[idx - next_augroup_id].name;
}
+/// Function given to ExpandGeneric() to obtain the list of event names. Don't
+/// include groups.
+char *get_event_name_no_group(expand_T *xp FUNC_ATTR_UNUSED, int idx)
+{
+ return event_names[idx].name;
+}
+
/// Check whether given autocommand is supported
///
/// @param[in] event Event to check.
@@ -2406,7 +2260,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;
@@ -2453,8 +2308,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;
}
@@ -2465,10 +2320,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
+ if (ap != NULL
&& (group == AUGROUP_ALL || ap->group == group)
&& (pattern == NULL
|| (buflocal_buf == NULL
@@ -2485,17 +2341,15 @@ 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((char *)pat, patlen));
+ assert(aupat_is_buflocal(pat, patlen));
// "<buffer>"
if (patlen == 8) {
@@ -2518,7 +2372,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));
@@ -2527,13 +2381,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);
@@ -2549,12 +2400,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;
}
}
}
@@ -2617,25 +2468,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
@@ -2682,7 +2518,7 @@ static int arg_augroup_get(char **argp)
return AUGROUP_ALL;
}
- char *group_name = xstrnsave(arg, (size_t)(p - arg));
+ char *group_name = xmemdupz(arg, (size_t)(p - arg));
int group = augroup_find(group_name);
if (group == AUGROUP_ERROR) {
group = AUGROUP_ALL; // no match, use all groups
@@ -2709,11 +2545,38 @@ static bool arg_autocmd_flag_get(bool *flag, char **cmd_ptr, char *pattern, int
return false;
}
+/// When kFalse: VimSuspend should be triggered next.
+/// When kTrue: VimResume should be triggered next.
+/// When kNone: Currently triggering VimSuspend or VimResume.
+static TriState pending_vimresume = kFalse;
+
+static void vimresume_event(void **argv)
+{
+ apply_autocmds(EVENT_VIMRESUME, NULL, NULL, false, NULL);
+ pending_vimresume = kFalse;
+}
+
+/// Trigger VimSuspend or VimResume autocommand.
+void may_trigger_vim_suspend_resume(bool suspend)
+{
+ if (suspend && pending_vimresume == kFalse) {
+ pending_vimresume = kNone;
+ apply_autocmds(EVENT_VIMSUSPEND, NULL, NULL, false, NULL);
+ pending_vimresume = kTrue;
+ } else if (!suspend && pending_vimresume == kTrue) {
+ pending_vimresume = kNone;
+ multiqueue_put(main_loop.events, vimresume_event, 0);
+ }
+}
+
// UI Enter
void do_autocmd_uienter(uint64_t chanid, bool attached)
{
static bool recursive = false;
+ if (starting == NO_SCREEN) {
+ return; // user config hasn't been sourced yet
+ }
if (recursive) {
return; // disallow recursion
}
@@ -2724,8 +2587,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;
@@ -2736,15 +2598,13 @@ void do_autocmd_uienter(uint64_t chanid, bool attached)
void do_autocmd_focusgained(bool gained)
{
static bool recursive = false;
- static Timestamp last_time = (time_t)0;
- bool need_redraw = false;
+ static Timestamp last_time = 0;
if (recursive) {
return; // disallow recursion
}
recursive = true;
- need_redraw |= apply_autocmds((gained ? EVENT_FOCUSGAINED : EVENT_FOCUSLOST),
- NULL, NULL, false, curbuf);
+ apply_autocmds((gained ? EVENT_FOCUSGAINED : EVENT_FOCUSLOST), NULL, NULL, false, curbuf);
// When activated: Check if any file was modified outside of Vim.
// Only do this when not done within the last two seconds as:
@@ -2752,60 +2612,38 @@ void do_autocmd_focusgained(bool gained)
// has a granularity of 2 seconds.
// 2. We could get multiple notifications in a row.
if (gained && last_time + (Timestamp)2000 < os_now()) {
- need_redraw = check_timestamps(true);
+ check_timestamps(true);
last_time = os_now();
}
- if (need_redraw) {
- // Something was executed, make sure the cursor is put back where it
- // belongs.
- need_wait_return = false;
-
- if (State & MODE_CMDLINE) {
- redrawcmdline();
- } else if ((State & MODE_NORMAL) || (State & MODE_INSERT)) {
- if (must_redraw != 0) {
- update_screen();
- }
+ recursive = false;
+}
- setcursor();
- }
+void do_filetype_autocmd(buf_T *buf, bool force)
+{
+ static int ft_recursive = 0;
- ui_flush();
+ if (ft_recursive > 0 && !force) {
+ return; // disallow recursion
}
- if (need_maketitle) {
- maketitle();
- }
+ char **varp = &buf->b_p_ft;
+ int secure_save = secure;
- recursive = false;
-}
+ // Reset the secure flag, since the value of 'filetype' has
+ // been checked to be safe.
+ secure = 0;
-static void define_autocmd(event_T event, char *pat, char *group, bool once, bool nested, char *cmd)
-{
- AucmdExecutable exec = AUCMD_EXECUTABLE_INIT;
- exec.type = CALLABLE_EX;
- exec.callable.cmd = cmd; // autocmd_register() makes a copy
- int group_id = augroup_add(group);
- autocmd_register(0, event, pat, (int)strlen(pat), group_id, once, nested, NULL, exec);
-}
-
-/// initialization of default autocmds
-void init_default_autocmds(void)
-{
- // open terminals when opening files that start with term://
-#define PROTO "term://"
- define_autocmd(EVENT_BUFREADCMD, PROTO "*", "nvim_terminal", false, true,
- "if !exists('b:term_title')|call termopen("
- // Capture the command string
- "matchstr(expand(\"<amatch>\"), "
- "'\\c\\m" PROTO "\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), "
- // capture the working directory
- "{'cwd': expand(get(matchlist(expand(\"<amatch>\"), "
- "'\\c\\m" PROTO "\\(.\\{-}\\)//'), 1, ''))})"
- "|endif");
-#undef PROTO
- // limit syntax synchronization in the command window
- define_autocmd(EVENT_CMDWINENTER, "[:>]", "nvim_cmdwin", false, false,
- "syntax sync minlines=1 maxlines=1");
+ ft_recursive++;
+ did_filetype = true;
+ // Only pass true for "force" when it is true or
+ // used recursively, to avoid endless recurrence.
+ apply_autocmds(EVENT_FILETYPE, buf->b_p_ft, buf->b_fname, force || ft_recursive == 1, buf);
+ ft_recursive--;
+
+ // Just in case the old "buf" is now invalid
+ if (varp != &(buf->b_p_ft)) {
+ varp = NULL;
+ }
+ secure = secure_save;
}