diff options
| author | Josh Rahm <joshuarahm@gmail.com> | 2023-11-30 20:35:25 +0000 |
|---|---|---|
| committer | Josh Rahm <joshuarahm@gmail.com> | 2023-11-30 20:35:25 +0000 |
| commit | 1b7b916b7631ddf73c38e3a0070d64e4636cb2f3 (patch) | |
| tree | cd08258054db80bb9a11b1061bb091c70b76926a /src/nvim/autocmd.c | |
| parent | eaa89c11d0f8aefbb512de769c6c82f61a8baca3 (diff) | |
| parent | 4a8bf24ac690004aedf5540fa440e788459e5e34 (diff) | |
| download | rneovim-aucmd_textputpost.tar.gz rneovim-aucmd_textputpost.tar.bz2 rneovim-aucmd_textputpost.zip | |
Merge remote-tracking branch 'upstream/master' into aucmd_textputpostaucmd_textputpost
Diffstat (limited to 'src/nvim/autocmd.c')
| -rw-r--r-- | src/nvim/autocmd.c | 1210 |
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); |