From 90333b24c3582cb017d823583d4896c8bbb8edb8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 4 Feb 2023 19:35:31 +0800 Subject: vim-patch:9.0.1277: cursor may move with autocmd in Visual mode (#22116) Problem: Cursor may move with autocmd in Visual mode. Solution: Restore "VIsual_active" before calling check_cursor(). (closes vim/vim#11939) https://github.com/vim/vim/commit/49f0524fb575bb1cf4881e472afab7d37c579440 --- src/nvim/autocmd.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 01ebdfdafe..897c9533e5 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1535,6 +1535,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 +1564,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); } -- cgit From 257765d9e0f20dc060f496ed400b16203e44dc9c Mon Sep 17 00:00:00 2001 From: bfredl Date: Thu, 9 Feb 2023 20:55:35 +0100 Subject: refactor(ui): don't reimplement redrawing in focus gained handling These are just ordinary boring events now. Modes already redraw events themselves. --- src/nvim/autocmd.c | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 897c9533e5..0485fbcdb0 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -2740,14 +2740,12 @@ void do_autocmd_focusgained(bool gained) { static bool recursive = false; static Timestamp last_time = (time_t)0; - bool need_redraw = false; 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: @@ -2755,32 +2753,10 @@ 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(); - } - - setcursor(); - } - - ui_flush(); - } - - if (need_maketitle) { - maketitle(); - } - recursive = false; } -- cgit From 4be6c6cf0ddf5e31d4103cb5df06651ba6f4897b Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sat, 11 Feb 2023 11:05:57 +0100 Subject: refactor: replace char_u with char (#21901) refactor: replace char_u with char Work on https://github.com/neovim/neovim/issues/459 --- src/nvim/autocmd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 0485fbcdb0..239a07da1f 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -221,7 +221,7 @@ static void aupat_show(AutoPat *ap, event_T event, int previous_group) char *exec_to_string = aucmd_exec_to_string(ac, ac->exec); if (ac->desc != NULL) { size_t msglen = 100; - char *msg = (char *)xmallocz(msglen); + 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); @@ -286,7 +286,7 @@ static void au_show_for_event(int group, event_T event, char *pat) if (aupat_is_buflocal(pat, patlen)) { // normalize pat into standard "#N" form aupat_normalize_buflocal_pat(buflocal_pat, pat, patlen, aupat_get_buflocal_nr(pat, patlen)); - pat = (char *)buflocal_pat; + pat = buflocal_pat; patlen = (int)strlen(buflocal_pat); } @@ -2119,8 +2119,8 @@ static bool call_autocmd_callback(const AutoCmd *ac, const AutoPatCmd *apc) 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) { -- cgit From c5b34fa55483d84d1de32937ffff0b7cf1aeba78 Mon Sep 17 00:00:00 2001 From: glacambre Date: Sat, 11 Feb 2023 09:45:11 +0100 Subject: refactor: move init_default_autocmds to lua The original motivation for this change came from developping https://github.com/neovim/neovim/pull/22159, which will require adding more autocommand creation to Neovim's startup sequence. This change requires lightly editing a test that expected no autocommand to have been created from lua. --- src/nvim/autocmd.c | 29 ----------------------------- 1 file changed, 29 deletions(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 0485fbcdb0..4cc75fa9a6 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -2759,32 +2759,3 @@ void do_autocmd_focusgained(bool gained) recursive = false; } - -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(\"\"), " - "'\\c\\m" PROTO "\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), " - // capture the working directory - "{'cwd': expand(get(matchlist(expand(\"\"), " - "'\\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"); -} -- cgit From d6ecead36406233cc56353dd05f3380f0497630f Mon Sep 17 00:00:00 2001 From: bfredl Date: Tue, 14 Mar 2023 11:49:46 +0100 Subject: refactor(screen): screen.c delenda est drawscreen.c vs screen.c makes absolutely no sense. The screen exists only to draw upon it, therefore helper functions are distributed randomly between screen.c and the file that does the redrawing. In addition screen.c does a lot of drawing on the screen. It made more sense for vim/vim as our grid.c is their screen.c Not sure if we want to dump all the code for option chars into optionstr.c, so keep these in a optionchar.c for now. --- src/nvim/autocmd.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 2f1317b05d..c66ee4286e 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -48,7 +48,6 @@ #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" -- cgit From 8b7fb668e440f7793564b764bc9a691e3f45382a Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 29 Mar 2023 19:54:12 +0100 Subject: fix(filetype): avoid recursive FileType autocmds (#22813) --- src/nvim/autocmd.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index c66ee4286e..b488dd9f8f 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -2758,3 +2758,32 @@ void do_autocmd_focusgained(bool gained) recursive = false; } + +void do_filetype_autocmd(buf_T *buf, bool force) +{ + static bool recursive = false; + + if (recursive && !force) { + return; // disallow recursion + } + + char **varp = &buf->b_p_ft; + int secure_save = secure; + + // Reset the secure flag, since the value of 'filetype' has + // been checked to be safe. + secure = 0; + + recursive = true; + 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, buf); + recursive = false; + + // Just in case the old "buf" is now invalid + if (varp != &(buf->b_p_ft)) { + varp = NULL; + } + secure = secure_save; +} -- cgit From e5fa2e3a5d0f120c0b957b4643ea63a807ba377b Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 30 Mar 2023 11:35:13 +0100 Subject: fix(autocmd): handle recursion for force set (#22820) --- src/nvim/autocmd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index b488dd9f8f..2e83260a40 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -2761,9 +2761,9 @@ void do_autocmd_focusgained(bool gained) void do_filetype_autocmd(buf_T *buf, bool force) { - static bool recursive = false; + static int ft_recursive = 0; - if (recursive && !force) { + if (ft_recursive > 0 && !force) { return; // disallow recursion } @@ -2774,12 +2774,12 @@ void do_filetype_autocmd(buf_T *buf, bool force) // been checked to be safe. secure = 0; - recursive = true; + 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, buf); - recursive = false; + ft_recursive--; // Just in case the old "buf" is now invalid if (varp != &(buf->b_p_ft)) { -- cgit From 5bf2f4b3c29fdab72044ddce74f06cb45fe9401c Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 30 Mar 2023 17:24:50 +0100 Subject: fix(filetype): make recursive work...again (#22826) --- src/nvim/autocmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 2e83260a40..578542adfe 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -2778,7 +2778,7 @@ void do_filetype_autocmd(buf_T *buf, bool force) 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, buf); + 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 -- cgit From 10baf89712724b4b95f7c641f2012f051737003c Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 3 Apr 2023 08:36:14 +0800 Subject: vim-patch:9.0.1439: start Insert mode when accessing a hidden prompt buffer (#22867) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Start Insert mode when accessing a hidden prompt buffer. Solution: Call leaving_window() in aucmd_restbuf(). (Thorben Tröbst, closes vim/vim#12148, closes vim/vim#12147) https://github.com/vim/vim/commit/cde8de034524d00aba4ff4142e658baff511e12d Cherry-pick test_prompt_buffer.vim changes from patch 9.0.0631. Co-authored-by: orbital --- src/nvim/autocmd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 578542adfe..9b4cb336bc 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1495,7 +1495,9 @@ void aucmd_restbuf(aco_save_T *aco) } } win_found: - + // May need to stop Insert mode if we were in a prompt buffer. + leaving_window(curwin); + // Remove the window. win_remove(curwin, NULL); pmap_del(handle_T)(&window_handles, curwin->handle); if (curwin->w_grid_alloc.chars != NULL) { -- cgit From 7190dba017e3aac0409c73ff1c954d18858cb3c9 Mon Sep 17 00:00:00 2001 From: ii14 <59243201+ii14@users.noreply.github.com> Date: Thu, 6 Apr 2023 22:39:50 +0200 Subject: refactor: remove use of reserved c++ keywords libnvim couldn't be easily used in C++ due to the use of reserved keywords. Additionally, add explicit casts to *alloc function calls used in inline functions, as C++ doesn't allow implicit casts from void pointers. --- src/nvim/autocmd.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 9b4cb336bc..41ba4f506d 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -939,10 +939,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(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) + if (do_autocmd_event(event, pat, once, nested, cmd, del, group) == FAIL) { return; } @@ -956,12 +956,12 @@ 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, 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; @@ -978,7 +978,7 @@ int do_autocmd_event(event_T event, char *pat, bool once, int nested, char *cmd, } // Delete all aupat for an event. - if (*pat == NUL && delete) { + if (*pat == NUL && del) { aupat_del_for_event_and_group(event, findgroup); return OK; } @@ -999,7 +999,7 @@ 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. -- cgit From 04933b1ea968f958d2541dd65fd33ebb503caac3 Mon Sep 17 00:00:00 2001 From: ii14 <59243201+ii14@users.noreply.github.com> Date: Fri, 7 Apr 2023 21:08:16 +0200 Subject: refactor: remove redundant casts --- src/nvim/autocmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 41ba4f506d..f1ce919942 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -2499,7 +2499,7 @@ bool aupat_is_buflocal(char *pat, int patlen) int aupat_get_buflocal_nr(char *pat, int patlen) { - assert(aupat_is_buflocal((char *)pat, patlen)); + assert(aupat_is_buflocal(pat, patlen)); // "" if (patlen == 8) { -- cgit From d52cc668c736ef6ca7ee3655a7eb7fe6475afadc Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 10 Apr 2023 07:33:26 +0800 Subject: vim-patch:9.0.1443: ending Insert mode when accessing a hidden prompt buffer (#22984) Problem: Ending Insert mode when accessing a hidden prompt buffer. Solution: Don't stop Insert mode when it was active before. (closes vim/vim#12237) https://github.com/vim/vim/commit/05a627c3d4e42a18f76c14828d68ee4747118211 Co-authored-by: Bram Moolenaar --- src/nvim/autocmd.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index f1ce919942..726344a42b 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1421,6 +1421,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 @@ -1497,6 +1499,10 @@ void aucmd_restbuf(aco_save_T *aco) win_found: // 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 = false; + } // Remove the window. win_remove(curwin, NULL); pmap_del(handle_T)(&window_handles, curwin->handle); -- cgit From 3b0df1780e2c8526bda5dead18ee7cc45925caba Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Wed, 26 Apr 2023 23:23:44 +0200 Subject: refactor: uncrustify Notable changes: replace all infinite loops to `while(true)` and remove `int` from `unsigned int`. --- src/nvim/autocmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 726344a42b..1d09e8f6c3 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -2185,7 +2185,7 @@ char *getnextac(int c, void *cookie, int indent, bool do_concat) } // repeat until we find an autocommand to execute - for (;;) { + while (true) { // skip removed commands while (acp->nextcmd != NULL && aucmd_exec_is_deleted(acp->nextcmd->exec)) { -- cgit From 1cb60405548e79f1ec63921540e1c3ebb3ddcc01 Mon Sep 17 00:00:00 2001 From: ii14 <59243201+ii14@users.noreply.github.com> Date: Thu, 27 Apr 2023 19:25:08 +0200 Subject: perf(events): store autocommands in flat vectors (#23256) Instead of nested linked lists, store autocommands in a flat, contiguous kvec_t, with one kvec_t per event type. Previously patterns were stored in each node of the outer linked list, so they can be matched only once on repeating patterns. They are now reference counted and referenced in each autocommand, and matching is skipped if the pattern repeats. Speeds up creation and deletion, execution is not affected. Co-authored-by: ii14 --- src/nvim/autocmd.c | 974 +++++++++++++++++++++-------------------------------- 1 file changed, 381 insertions(+), 593 deletions(-) (limited to 'src/nvim/autocmd.c') 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 "" + 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-local patterns + if (aupat_is_buflocal(pat, patlen)) { + // normalize pat into standard "#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 , 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 "" - // Loop through all the specified patterns. - int patlen = (int)aucmd_pattern_length(pat); - while (patlen) { - // detect special buffer-local patterns - if (aupat_is_buflocal(pat, patlen)) { - // normalize pat into standard "#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 , 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 - 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 "), - 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 "), 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 "" 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-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 "#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 , 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 , 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 "" - 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-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 "" 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 , 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: : 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: 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 . - 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, "'; + return patlen >= 8 && strncmp(pat, "'; } -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 "#N" form - snprintf(dest, - BUFLOCAL_PAT_LEN, - "", - buflocal_nr); + snprintf(dest, BUFLOCAL_PAT_LEN, "", 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; -- cgit From 774a32e5fe732a43b229ab25e24dffa36ac29aa4 Mon Sep 17 00:00:00 2001 From: ii14 Date: Thu, 27 Apr 2023 22:03:17 +0200 Subject: fix(events): null dereference in autocmd functions --- src/nvim/autocmd.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 7a65f11e80..b5109b4b21 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -300,7 +300,7 @@ void aucmd_del_for_event_and_group(event_T event, int group) 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) { + if (ac->pat != NULL && ac->pat->group == group) { aucmd_del(ac); } } @@ -2027,6 +2027,7 @@ char *getnextac(int c, void *cookie, int indent, bool do_concat) 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) { @@ -2288,7 +2289,8 @@ bool au_exists(const char *const arg) 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 ((group == AUGROUP_ALL || ap->group == group) + if (ap != NULL + && (group == AUGROUP_ALL || ap->group == group) && (pattern == NULL || (buflocal_buf == NULL ? path_fnamecmp(ap->pat, pattern) == 0 -- cgit From 88cfb49bee3c9102082c7010acb92244e4ad1348 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 5 May 2023 07:14:39 +0800 Subject: vim-patch:8.2.4890: inconsistent capitalization in error messages Problem: Inconsistent capitalization in error messages. Solution: Make capitalization consistent. (Doug Kearns) https://github.com/vim/vim/commit/cf030578b26460643dca4a40e7f2e3bc19c749aa Co-authored-by: Bram Moolenaar --- src/nvim/autocmd.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index b5109b4b21..2d5d8e262b 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -56,6 +56,9 @@ # 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_ @@ -1589,7 +1592,7 @@ 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; } -- cgit From e2fdd53d8c015913e8be4ff708fc3488558c8906 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 14 May 2023 18:45:56 +0200 Subject: refactor(map): avoid duplicated khash_t types for values This reduces the total number of khash_t instantiations from 22 to 8. Make the khash internal functions take the size of values as a runtime parameter. This is abstracted with typesafe Map containers which are still specialized for both key, value type. Introduce `Set(key)` type for when there is no value. Refactor shada.c to use Map/Set instead of khash directly. This requires `map_ref` operation to be more flexible. Return pointers to both key and value, plus an indicator for new_item. As a bonus, `map_key` is now redundant. Instead of Map(cstr_t, FileMarks), use a pointer map as the FileMarks struct is humongous. Make `event_strings` actually work like an intern pool instead of wtf it was doing before. --- src/nvim/autocmd.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 2d5d8e262b..17a3fd33f1 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -108,14 +108,13 @@ static Map(int, String) map_augroup_id_to_name = MAP_INIT; 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((char *)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); } } @@ -543,7 +542,7 @@ void do_augroup(char *arg, int del_group) String name; int value; - map_foreach(&map_augroup_name_to_id, name, value, { + map_foreach(int, &map_augroup_name_to_id, name, value, { if (value > 0) { msg_puts(name.data); } else { @@ -572,18 +571,15 @@ void free_all_autocmds(void) // 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(String, &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() } @@ -1311,7 +1307,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() @@ -1367,7 +1363,7 @@ win_found: } // 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); -- cgit From b46f61ac050e6eb530ceb4d4b510b612596427c5 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 27 May 2023 22:15:22 +0800 Subject: vim-patch:9.0.1582: :stopinsert may not work in a popup close handler (#23785) Problem: :stopinsert may not work in a popup close handler. (Ben Jackson) Solution: Restore stop_insert_mode when appropriate. (closes vim/vim#12452, closes vim/vim#12434) https://github.com/vim/vim/commit/a40c0bcc83c32da02869f59b10538d6327df61c5 --- src/nvim/autocmd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 17a3fd33f1..53cca7baa1 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1355,11 +1355,13 @@ void aucmd_restbuf(aco_save_T *aco) } } win_found: + ; + 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 = false; + stop_insert_mode = save_stop_insert_mode; } // Remove the window. win_remove(curwin, NULL); -- cgit From 971049f3189d4769db5e9896cd19b555719b3d09 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 7 Jun 2023 09:26:46 +0800 Subject: revert: "refactor: eliminate `autocmd_fname_full` global" This reverts commit 82cd0be2eaf71c0476e15c66ba3e83c76896d407. --- src/nvim/autocmd.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 53cca7baa1..564a7b4f87 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1603,6 +1603,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force // 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; @@ -1631,6 +1632,7 @@ 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 . autocmd_bufnr = buf == NULL ? 0 : buf->b_fnum; @@ -1806,6 +1808,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; -- cgit From dd24ea819507e3a5da04df55df7dda5240e5b57f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 7 Jun 2023 09:29:12 +0800 Subject: fix(events): don't expand non-file as file name --- src/nvim/autocmd.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 564a7b4f87..427bce0e80 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1676,6 +1676,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); } -- cgit From 7966020f70255e04bc7a8015a170307c4d5341a8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 26 Jun 2023 07:17:45 +0800 Subject: vim-patch:8.2.3833: error from term_start() not caught by try/catch Problem: Error from term_start() not caught by try/catch. Solution: save and restore did_emsg when applying autocommands. (Ozaki Kiichi, closes vim/vim#9361) https://github.com/vim/vim/commit/c3f91c0648f4b04a6a9ceb4ccec45ea767a63796 Co-authored-by: ichizok --- src/nvim/autocmd.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 427bce0e80..f3eb5d410d 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1783,8 +1783,12 @@ 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; + // 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; if (nesting == 1) { // restore cursor and topline, unless they were changed -- cgit From dc3ee122dc132b5baf11ea0083604927eb6c7443 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 26 Jun 2023 07:14:43 +0800 Subject: vim-patch:9.0.1665: empty CmdlineEnter autocommand causes errors in Ex mode Problem: Empty CmdlineEnter autocommand causes errors in Ex mode. Solution: Save and restore ex_pressedreturn. (Christian Brabandt, closes # 12581, closes vim/vim#12578) https://github.com/vim/vim/commit/590aae35575cbd74d80c41d87fc647f2812aad70 Co-authored-by: Christian Brabandt --- src/nvim/autocmd.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index f3eb5d410d..36f0183fd8 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1784,11 +1784,13 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force } 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, &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 -- cgit From fcf3519c65a2d6736de437f686e788684a6c8564 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 17 Apr 2023 22:18:58 +0200 Subject: refactor: remove long long is 32-bits even on 64-bit windows which makes the type suboptimal for a codebase meant to be cross-platform. --- src/nvim/autocmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 36f0183fd8..4aa2ec56a4 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1551,7 +1551,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force bool retval = false; static int nesting = 0; char *save_cmdarg; - long save_cmdbang; + varnumber_T save_cmdbang; static int filechangeshell_busy = false; proftime_T wait_time; bool did_save_redobuff = false; -- cgit From a8cfdf43bc6226e32679ec59769ea3e48ca26193 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 23 Jul 2023 07:16:41 +0800 Subject: fix(events): trigger VimResume on next UI request (#24426) --- src/nvim/autocmd.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 4aa2ec56a4..a8c5d00383 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -46,6 +46,7 @@ #include "nvim/search.h" #include "nvim/state.h" #include "nvim/strings.h" +#include "nvim/types.h" #include "nvim/ui.h" #include "nvim/ui_compositor.h" #include "nvim/vim.h" @@ -2521,6 +2522,30 @@ 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 triggerd 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) { -- cgit From 58a1ef8e6a93c615379f6fbe7234697bcdc42b3e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 12 Aug 2023 09:50:17 +0800 Subject: fix(events): avoid unnecessary CursorMoved (#24675) Problem: Temporarily changing current window in a script causes CursorMoved to be triggerd. Solution: Don't trigger CursorMoved if neither curwin nor cursor changed between two checks. --- src/nvim/autocmd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index a8c5d00383..c43a59cbb3 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1030,7 +1030,8 @@ int autocmd_register(int64_t id, event_T event, const char *pat, int patlen, int // 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 -- cgit From 5970157e1d22fd5e05ae5d3bd949f807fb7a744c Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 17 May 2023 16:08:06 +0200 Subject: refactor(map): enhanced implementation, Clean Code™, etc etc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This involves two redesigns of the map.c implementations: 1. Change of macro style and code organization The old khash.h and map.c implementation used huge #define blocks with a lot of backslash line continuations. This instead uses the "implementation file" .c.h pattern. Such a file is meant to be included multiple times, with different macros set prior to inclusion as parameters. we already use this pattern e.g. for eval/typval_encode.c.h to implement different typval encoders reusing a similar structure. We can structure this code into two parts. one that only depends on key type and is enough to implement sets, and one which depends on both key and value to implement maps (as a wrapper around sets, with an added value[] array) 2. Separate the main hash buckets from the key / value arrays Change the hack buckets to only contain an index into separate key / value arrays This is a common pattern in modern, state of the art hashmap implementations. Even though this leads to one more allocated array, it is this often is a net reduction of memory consumption. Consider key+value consuming at least 12 bytes per pair. On average, we will have twice as many buckets per item. Thus old implementation: 2*12 = 24 bytes per item New implementation 1*12 + 2*4 = 20 bytes per item And the difference gets bigger with larger items. One might think we have pulled a fast one here, as wouldn't the average size of the new key/value arrays be 1.5 slots per items due to amortized grows? But remember, these arrays are fully dense, and thus the accessed memory, measured in _cache lines_, the unit which actually matters, will be the fully used memory but just rounded up to the nearest cache line boundary. This has some other interesting properties, such as an insert-only set/map will be fully ordered by insert only. Preserving this ordering in face of deletions is more tricky tho. As we currently don't use ordered maps, the "delete" operation maintains compactness of the item arrays in the simplest way by breaking the ordering. It would be possible to implement an order-preserving delete although at some cost, like allowing the items array to become non-dense until the next rehash. Finally, in face of these two major changes, all code used in khash.h has been integrated into map.c and friends. Given the heavy edits it makes no sense to "layer" the code into a vendored and a wrapper part. Rather, the layered cake follows the specialization depth: code shared for all maps, code specialized to a key type (and its equivalence relation), and finally code specialized to value+key type. --- src/nvim/autocmd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index c43a59cbb3..5f94fbb014 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -543,7 +543,7 @@ void do_augroup(char *arg, int del_group) String name; int value; - map_foreach(int, &map_augroup_name_to_id, name, value, { + map_foreach(&map_augroup_name_to_id, name, value, { if (value > 0) { msg_puts(name.data); } else { @@ -577,7 +577,7 @@ void free_all_autocmds(void) }) map_destroy(String, &map_augroup_name_to_id); - map_foreach_value(String, &map_augroup_id_to_name, name, { + map_foreach_value(&map_augroup_id_to_name, name, { api_free_string(name); }) map_destroy(int, &map_augroup_id_to_name); -- cgit From a6e74c1f0a2bbf03f5b99c167b549018f4c8fb0d Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Thu, 14 Sep 2023 06:05:27 +0200 Subject: docs: fix typos and other small fixes (#25005) Co-authored-by: nuid64 Co-authored-by: Mike Smith <10135646+mikesmithgh@users.noreply.github.com> Co-authored-by: XTY Co-authored-by: Empa Co-authored-by: kyu08 <49891479+kyu08@users.noreply.github.com> --- src/nvim/autocmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 5f94fbb014..3fa20f4e48 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -2524,7 +2524,7 @@ static bool arg_autocmd_flag_get(bool *flag, char **cmd_ptr, char *pattern, int } /// When kFalse: VimSuspend should be triggered next. -/// When kTrue: VimResume should be triggerd next. +/// When kTrue: VimResume should be triggered next. /// When kNone: Currently triggering VimSuspend or VimResume. static TriState pending_vimresume = kFalse; -- cgit From c88bb658ce6fb12cca3e5324d8a15d1859d095cd Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 23 Sep 2023 12:41:09 +0800 Subject: vim-patch:8.2.4609: :unhide does not check for failing to close a window (#25317) Problem: :unhide does not check for failing to close a window. Solution: When closing a window fails continue with the next one. Do not try closing the autocmd window. (closes vim/vim#9984) https://github.com/vim/vim/commit/6f2465d336a9d4afe392db4084ef7e9db17e67c1 Co-authored-by: Bram Moolenaar --- src/nvim/autocmd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 3fa20f4e48..ff0c2f063f 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1341,7 +1341,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(); @@ -1357,7 +1356,7 @@ 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); -- cgit From f91cd31d7d9d70006e0000592637d5d997eab52c Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 27 Sep 2023 21:46:39 +0200 Subject: refactor(messages): fold msg_outtrans_attr into msg_outtrans problem: there are too many different functions in message.c solution: fold some of the functions into themselves --- src/nvim/autocmd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index ff0c2f063f..f2408a9457 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -228,7 +228,7 @@ static void au_show_for_event(int group, event_T event, const char *pat) } msg_col = 4; - msg_outtrans(ac->pat->pat); + msg_outtrans(ac->pat->pat, 0); } if (got_int) { @@ -253,12 +253,12 @@ static void au_show_for_event(int group, event_T event, const char *pat) } else { snprintf(msg, msglen, "%s [%s]", exec_to_string, ac->desc); } - msg_outtrans(msg); + 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); + msg_outtrans(exec_to_string, 0); } XFREE_CLEAR(exec_to_string); if (p_verbose > 0) { -- cgit From bc13bc154aa574e0bb58a50f2e0ca4570efa57c3 Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 29 Sep 2023 16:10:54 +0200 Subject: refactor(message): smsg_attr -> smsg --- src/nvim/autocmd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index f2408a9457..8c2df6f3b5 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -372,7 +372,7 @@ void aubuflocal_remove(buf_T *buf) if (p_verbose >= 6) { verbose_enter(); - smsg(_("auto-removing autocommand: %s "), event_nr2name(event), buf->b_fnum); + smsg(0, _("auto-removing autocommand: %s "), event_nr2name(event), buf->b_fnum); verbose_leave(); } } @@ -1150,7 +1150,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; @@ -1946,7 +1946,7 @@ static void aucmd_next(AutoPatCmd *apc) snprintf(namep, sourcing_name_len, s, name, ap->pat); if (p_verbose >= 8) { verbose_enter(); - smsg(_("Executing %s"), namep); + smsg(0, _("Executing %s"), namep); verbose_leave(); } @@ -2045,7 +2045,7 @@ char *getnextac(int c, void *cookie, int indent, bool do_concat) if (p_verbose >= 9) { verbose_enter_scroll(); char *exec_to_string = aucmd_exec_to_string(ac, ac->exec); - smsg(_("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(); -- cgit From dc6d0d2daf69e2fdadda81feb97906dbc962a239 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 30 Sep 2023 14:41:34 +0800 Subject: refactor: reorganize option header files (#25437) - Move vimoption_T to option.h - option_defs.h is for option-related types - option_vars.h corresponds to Vim's option.h - option_defs.h and option_vars.h don't include each other --- src/nvim/autocmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 8c2df6f3b5..657760914f 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -34,7 +34,7 @@ #include "nvim/memline_defs.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/option_defs.h" +#include "nvim/option.h" #include "nvim/optionstr.h" #include "nvim/os/input.h" #include "nvim/os/os.h" -- cgit From f06af5e66981095f3244f67d1587ce7e9853eb4c Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 30 Sep 2023 08:13:58 +0800 Subject: vim-patch:9.0.1958: cannot complete option values Problem: cannot complete option values Solution: Add completion functions for several options Add cmdline tab-completion for setting string options Add tab-completion for setting string options on the cmdline using `:set=` (along with `:set+=` and `:set-=`). The existing tab completion for setting options currently only works when nothing is typed yet, and it only fills in with the existing value, e.g. when the user does `:set diffopt=` it will be completed to `set diffopt=internal,filler,closeoff` and nothing else. This isn't too useful as a user usually wants auto-complete to suggest all the possible values, such as 'iblank', or 'algorithm:patience'. For set= and set+=, this adds a new optional callback function for each option that can be invoked when doing completion. This allows for each option to have control over how completion works. For example, in 'diffopt', it will suggest the default enumeration, but if `algorithm:` is selected, it will further suggest different algorithm types like 'meyers' and 'patience'. When using set=, the existing option value will be filled in as the first choice to preserve the existing behavior. When using set+= this won't happen as it doesn't make sense. For flag list options (e.g. 'mouse' and 'guioptions'), completion will take into account existing typed values (and in the case of set+=, the existing option value) to make sure it doesn't suggest duplicates. For set-=, there is a new `ExpandSettingSubtract` function which will handle flag list and comma-separated options smartly, by only suggesting values that currently exist in the option. Note that Vim has some existing code that adds special handling for 'filetype', 'syntax', and misc dir options like 'backupdir'. This change preserves them as they already work, instead of converting to the new callback API for each option. closes: vim/vim#13182 https://github.com/vim/vim/commit/900894b09a95398dfc75599e9f0aa2ea25723384 Co-authored-by: Yee Cheng Chin --- src/nvim/autocmd.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 657760914f..a40f7d8c26 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -2213,6 +2213,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. -- cgit From 8e932480f61d6101bf8bea1abc07ed93826221fd Mon Sep 17 00:00:00 2001 From: dundargoc Date: Fri, 29 Sep 2023 14:58:48 +0200 Subject: refactor: the long goodbye long is 32 bits on windows, while it is 64 bits on other architectures. This makes the type suboptimal for a codebase meant to be cross-platform. Replace it with more appropriate integer types. --- src/nvim/autocmd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index a40f7d8c26..9f7c599164 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1766,10 +1766,10 @@ 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 } -- cgit From 139e6f68f937b9efcadf2709ee1c83213d3266fa Mon Sep 17 00:00:00 2001 From: ii14 <59243201+ii14@users.noreply.github.com> Date: Mon, 9 Oct 2023 11:50:44 +0200 Subject: fix(autocmd): API functions accept garbage after event name #25523 "VimEnter foo" was accepted as a valid event name for "VimEnter". Events delimited with commas, eg. "VimEnter,BufRead", were also accepted, even though only the first event was actually parsed. Co-authored-by: ii14 --- src/nvim/autocmd.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 9f7c599164..2537269c5c 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -597,9 +597,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; @@ -623,6 +623,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. -- cgit From d7359a87425dc38efda4f74bd580bae9946abe31 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 1 Nov 2023 12:16:37 +0800 Subject: fix(startup): trigger UIEnter for the correct channel (#25860) --- src/nvim/autocmd.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 2537269c5c..bdb3983ab3 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -2570,6 +2570,9 @@ 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 } -- cgit From cd63a9addd6e1114c3524fa041ece560550cfe7b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 10 Nov 2023 08:39:21 +0800 Subject: refactor: change some xstrndup() and xstrnsave() to xmemdupz() (#25959) When the given length is exactly the number of bytes to copy, xmemdupz() makes the intention clearer. --- src/nvim/autocmd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index bdb3983ab3..1a83dd30c0 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1031,7 +1031,7 @@ int autocmd_register(int64_t id, event_T event, const char *pat, int patlen, int } ap->refcount = 0; - ap->pat = xstrnsave(pat, (size_t)patlen); + ap->pat = xmemdupz(pat, (size_t)patlen); ap->patlen = patlen; // need to initialize last_mode for the first ModeChanged autocmd @@ -2514,7 +2514,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 -- cgit From 8e58d37f2e15ac8540377148e55ed08a039aadb6 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sat, 11 Nov 2023 11:20:08 +0100 Subject: refactor: remove redundant casts --- src/nvim/autocmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 1a83dd30c0..b58b5784b4 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -2594,7 +2594,7 @@ 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; + static Timestamp last_time = 0; if (recursive) { return; // disallow recursion -- cgit From 353a4be7e84fdc101318215bdcc8a7e780d737fe Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sun, 12 Nov 2023 13:13:58 +0100 Subject: build: remove PVS We already have an extensive suite of static analysis tools we use, which causes a fair bit of redundancy as we get duplicate warnings. PVS is also prone to give false warnings which creates a lot of work to identify and disable. --- src/nvim/autocmd.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index b58b5784b4..80573696d4 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1,6 +1,3 @@ -// 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 -- cgit From 326d46f690b383846f136f2a25523cffe2882f27 Mon Sep 17 00:00:00 2001 From: Raphael Date: Thu, 16 Nov 2023 09:54:47 +0800 Subject: refactor: move some functions to winfloat.c (#26020) --- src/nvim/autocmd.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 80573696d4..65e902ed4b 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -48,6 +48,7 @@ #include "nvim/ui_compositor.h" #include "nvim/vim.h" #include "nvim/window.h" +#include "nvim/winfloat.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "auevents_name_map.generated.h" -- cgit From 34509bbea3e8c6a8033911aea645b1b5579f7d1a Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sun, 26 Nov 2023 15:34:29 +0100 Subject: build: sync IWYU and clint to ignore the same headers (#26228) Also fix headers for autocmd.c. --- src/nvim/autocmd.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 65e902ed4b..80c9ad9465 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -4,34 +4,36 @@ #include #include #include -#include #include "nvim/api/private/helpers.h" #include "nvim/ascii.h" #include "nvim/autocmd.h" #include "nvim/buffer.h" #include "nvim/charset.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/cursor.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" #include "nvim/eval/vars.h" +#include "nvim/event/multiqueue.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/fileio.h" -#include "nvim/garray.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/insexpand.h" #include "nvim/lua/executor.h" +#include "nvim/main.h" #include "nvim/map.h" -#include "nvim/memline_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option.h" +#include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/os/input.h" #include "nvim/os/os.h" -- cgit From 6343d414369de1f3b259e51438cd4f666d82d3d2 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 27 Nov 2023 11:17:04 +0800 Subject: refactor: move autocmd types to autocmd_defs.h (#26239) --- src/nvim/autocmd.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 80c9ad9465..1fd22f7060 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1,10 +1,13 @@ // autocmd.c: Autocommand related functions #include +#include +#include #include #include #include +#include "klib/kvec.h" #include "nvim/api/private/helpers.h" #include "nvim/ascii.h" #include "nvim/autocmd.h" -- cgit From 8b428ca8b79ebb7b36c3e403ff3bcb6924a635a6 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 27 Nov 2023 16:00:21 +0100 Subject: build(IWYU): fix includes for func_attr.h --- src/nvim/autocmd.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 1fd22f7060..ac88b19fb9 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -23,6 +23,7 @@ #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/fileio.h" +#include "nvim/func_attr.h" #include "nvim/getchar.h" #include "nvim/gettext.h" #include "nvim/globals.h" -- cgit From 6c14ae6bfaf51415b555e9a6b85d1d280976358d Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 27 Nov 2023 20:27:32 +0100 Subject: refactor: rename types.h to types_defs.h --- src/nvim/autocmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index ac88b19fb9..b6c853bd66 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -49,7 +49,7 @@ #include "nvim/search.h" #include "nvim/state.h" #include "nvim/strings.h" -#include "nvim/types.h" +#include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/ui_compositor.h" #include "nvim/vim.h" -- cgit From 79b6ff28ad1204fbb4199b9092f5c578d88cb28e Mon Sep 17 00:00:00 2001 From: dundargoc Date: Tue, 28 Nov 2023 20:31:00 +0100 Subject: refactor: fix headers with IWYU --- src/nvim/autocmd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index b6c853bd66..585fac1228 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -9,7 +9,7 @@ #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" @@ -33,7 +33,7 @@ #include "nvim/insexpand.h" #include "nvim/lua/executor.h" #include "nvim/main.h" -#include "nvim/map.h" +#include "nvim/map_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option.h" @@ -52,7 +52,7 @@ #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" -- cgit From a6cba103cebce535279db197f9efeb34e9d1171f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 29 Nov 2023 20:32:40 +0800 Subject: refactor: move some constants out of vim_defs.h (#26298) --- src/nvim/autocmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/autocmd.c') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 585fac1228..74a1dbdbc3 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -29,7 +29,7 @@ #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" -- cgit