blob: 94f9c1fba466520f5454146342fc028d0636daa2 [file] [log] [blame]
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * autocmd.c: Autocommand related functions
12 */
13
14#include "vim.h"
15
16/*
17 * The autocommands are stored in a list for each event.
18 * Autocommands for the same pattern, that are consecutive, are joined
19 * together, to avoid having to match the pattern too often.
20 * The result is an array of Autopat lists, which point to AutoCmd lists:
21 *
22 * last_autopat[0] -----------------------------+
23 * V
24 * first_autopat[0] --> Autopat.next --> Autopat.next --> NULL
25 * Autopat.cmds Autopat.cmds
26 * | |
27 * V V
28 * AutoCmd.next AutoCmd.next
29 * | |
30 * V V
31 * AutoCmd.next NULL
32 * |
33 * V
34 * NULL
35 *
36 * last_autopat[1] --------+
37 * V
38 * first_autopat[1] --> Autopat.next --> NULL
39 * Autopat.cmds
40 * |
41 * V
42 * AutoCmd.next
43 * |
44 * V
45 * NULL
46 * etc.
47 *
48 * The order of AutoCmds is important, this is the order in which they were
49 * defined and will have to be executed.
50 */
51typedef struct AutoCmd
52{
53 char_u *cmd; // The command to be executed (NULL
54 // when command has been removed).
Bram Moolenaareb93f3f2019-04-04 15:04:56 +020055 char once; // "One shot": removed after execution
Bram Moolenaar3e460fd2019-01-26 16:21:07 +010056 char nested; // If autocommands nest here.
57 char last; // last command in list
LemonBoyeca7c602022-04-14 15:39:43 +010058 sctx_T script_ctx; // script context where it is defined
Bram Moolenaar3e460fd2019-01-26 16:21:07 +010059 struct AutoCmd *next; // next AutoCmd in list
60} AutoCmd;
61
62typedef struct AutoPat
63{
64 struct AutoPat *next; // Next AutoPat in AutoPat list; MUST
65 // be the first entry.
66 char_u *pat; // pattern as typed (NULL when pattern
67 // has been removed)
68 regprog_T *reg_prog; // compiled regprog for pattern
69 AutoCmd *cmds; // list of commands to do
70 int group; // group ID
71 int patlen; // strlen() of pat
72 int buflocal_nr; // !=0 for buffer-local AutoPat
73 char allow_dirs; // Pattern may match whole path
74 char last; // last pattern for apply_autocmds()
75} AutoPat;
76
John Marriott78d742a2024-04-02 20:26:01 +020077//
78// special cases:
79// BufNewFile and BufRead are searched for ALOT (especially at startup)
80// so we pre-determine their index into the event_tab[] table for fast access.
81// Keep these values in sync with event_tab[]!
82#define BUFNEWFILE_INDEX 9
83#define BUFREAD_INDEX 10
84
Luuk van Baalb7147f82025-02-08 18:52:39 +010085// Must be sorted by the 'value' field because it is used by bsearch()!
86// Events with positive keys aren't allowed in 'eventignorewin'.
87static keyvalue_T event_tab[NUM_EVENTS] = {
88 KEYVALUE_ENTRY(-EVENT_BUFADD, "BufAdd"),
89 KEYVALUE_ENTRY(-EVENT_BUFADD, "BufCreate"),
90 KEYVALUE_ENTRY(-EVENT_BUFDELETE, "BufDelete"),
91 KEYVALUE_ENTRY(-EVENT_BUFENTER, "BufEnter"),
92 KEYVALUE_ENTRY(-EVENT_BUFFILEPOST, "BufFilePost"),
93 KEYVALUE_ENTRY(-EVENT_BUFFILEPRE, "BufFilePre"),
94 KEYVALUE_ENTRY(-EVENT_BUFHIDDEN, "BufHidden"),
95 KEYVALUE_ENTRY(-EVENT_BUFLEAVE, "BufLeave"),
96 KEYVALUE_ENTRY(-EVENT_BUFNEW, "BufNew"),
97 KEYVALUE_ENTRY(-EVENT_BUFNEWFILE, "BufNewFile"), // BUFNEWFILE_INDEX
98 KEYVALUE_ENTRY(-EVENT_BUFREADPOST, "BufRead"), // BUFREAD_INDEX
99 KEYVALUE_ENTRY(-EVENT_BUFREADCMD, "BufReadCmd"),
100 KEYVALUE_ENTRY(-EVENT_BUFREADPOST, "BufReadPost"),
101 KEYVALUE_ENTRY(-EVENT_BUFREADPRE, "BufReadPre"),
102 KEYVALUE_ENTRY(-EVENT_BUFUNLOAD, "BufUnload"),
103 KEYVALUE_ENTRY(-EVENT_BUFWINENTER, "BufWinEnter"),
104 KEYVALUE_ENTRY(-EVENT_BUFWINLEAVE, "BufWinLeave"),
105 KEYVALUE_ENTRY(-EVENT_BUFWIPEOUT, "BufWipeout"),
106 KEYVALUE_ENTRY(-EVENT_BUFWRITEPRE, "BufWrite"),
107 KEYVALUE_ENTRY(-EVENT_BUFWRITECMD, "BufWriteCmd"),
108 KEYVALUE_ENTRY(-EVENT_BUFWRITEPOST, "BufWritePost"),
109 KEYVALUE_ENTRY(-EVENT_BUFWRITEPRE, "BufWritePre"),
John Marriott78d742a2024-04-02 20:26:01 +0200110 KEYVALUE_ENTRY(EVENT_CMDLINECHANGED, "CmdlineChanged"),
111 KEYVALUE_ENTRY(EVENT_CMDLINEENTER, "CmdlineEnter"),
112 KEYVALUE_ENTRY(EVENT_CMDLINELEAVE, "CmdlineLeave"),
Girish Palya92f68e22025-04-21 11:12:41 +0200113 KEYVALUE_ENTRY(EVENT_CMDLINELEAVEPRE, "CmdlineLeavePre"),
John Marriott78d742a2024-04-02 20:26:01 +0200114 KEYVALUE_ENTRY(EVENT_CMDUNDEFINED, "CmdUndefined"),
115 KEYVALUE_ENTRY(EVENT_CMDWINENTER, "CmdwinEnter"),
116 KEYVALUE_ENTRY(EVENT_CMDWINLEAVE, "CmdwinLeave"),
117 KEYVALUE_ENTRY(EVENT_COLORSCHEME, "ColorScheme"),
118 KEYVALUE_ENTRY(EVENT_COLORSCHEMEPRE, "ColorSchemePre"),
119 KEYVALUE_ENTRY(EVENT_COMPLETECHANGED, "CompleteChanged"),
120 KEYVALUE_ENTRY(EVENT_COMPLETEDONE, "CompleteDone"),
121 KEYVALUE_ENTRY(EVENT_COMPLETEDONEPRE, "CompleteDonePre"),
Luuk van Baalb7147f82025-02-08 18:52:39 +0100122 KEYVALUE_ENTRY(-EVENT_CURSORHOLD, "CursorHold"),
123 KEYVALUE_ENTRY(-EVENT_CURSORHOLDI, "CursorHoldI"),
124 KEYVALUE_ENTRY(-EVENT_CURSORMOVED, "CursorMoved"),
125 KEYVALUE_ENTRY(-EVENT_CURSORMOVEDC, "CursorMovedC"),
126 KEYVALUE_ENTRY(-EVENT_CURSORMOVEDI, "CursorMovedI"),
John Marriott78d742a2024-04-02 20:26:01 +0200127 KEYVALUE_ENTRY(EVENT_DIFFUPDATED, "DiffUpdated"),
128 KEYVALUE_ENTRY(EVENT_DIRCHANGED, "DirChanged"),
129 KEYVALUE_ENTRY(EVENT_DIRCHANGEDPRE, "DirChangedPre"),
130 KEYVALUE_ENTRY(EVENT_ENCODINGCHANGED, "EncodingChanged"),
131 KEYVALUE_ENTRY(EVENT_EXITPRE, "ExitPre"),
Luuk van Baalb7147f82025-02-08 18:52:39 +0100132 KEYVALUE_ENTRY(-EVENT_FILEAPPENDCMD, "FileAppendCmd"),
133 KEYVALUE_ENTRY(-EVENT_FILEAPPENDPOST, "FileAppendPost"),
134 KEYVALUE_ENTRY(-EVENT_FILEAPPENDPRE, "FileAppendPre"),
135 KEYVALUE_ENTRY(-EVENT_FILECHANGEDRO, "FileChangedRO"),
136 KEYVALUE_ENTRY(-EVENT_FILECHANGEDSHELL, "FileChangedShell"),
137 KEYVALUE_ENTRY(-EVENT_FILECHANGEDSHELLPOST, "FileChangedShellPost"),
John Marriott78d742a2024-04-02 20:26:01 +0200138 KEYVALUE_ENTRY(EVENT_ENCODINGCHANGED, "FileEncoding"),
Luuk van Baalb7147f82025-02-08 18:52:39 +0100139 KEYVALUE_ENTRY(-EVENT_FILEREADCMD, "FileReadCmd"),
140 KEYVALUE_ENTRY(-EVENT_FILEREADPOST, "FileReadPost"),
141 KEYVALUE_ENTRY(-EVENT_FILEREADPRE, "FileReadPre"),
142 KEYVALUE_ENTRY(-EVENT_FILETYPE, "FileType"),
143 KEYVALUE_ENTRY(-EVENT_FILEWRITECMD, "FileWriteCmd"),
144 KEYVALUE_ENTRY(-EVENT_FILEWRITEPOST, "FileWritePost"),
145 KEYVALUE_ENTRY(-EVENT_FILEWRITEPRE, "FileWritePre"),
146 KEYVALUE_ENTRY(-EVENT_FILTERREADPOST, "FilterReadPost"),
147 KEYVALUE_ENTRY(-EVENT_FILTERREADPRE, "FilterReadPre"),
148 KEYVALUE_ENTRY(-EVENT_FILTERWRITEPOST, "FilterWritePost"),
149 KEYVALUE_ENTRY(-EVENT_FILTERWRITEPRE, "FilterWritePre"),
John Marriott78d742a2024-04-02 20:26:01 +0200150 KEYVALUE_ENTRY(EVENT_FOCUSGAINED, "FocusGained"),
151 KEYVALUE_ENTRY(EVENT_FOCUSLOST, "FocusLost"),
152 KEYVALUE_ENTRY(EVENT_FUNCUNDEFINED, "FuncUndefined"),
153 KEYVALUE_ENTRY(EVENT_GUIENTER, "GUIEnter"),
154 KEYVALUE_ENTRY(EVENT_GUIFAILED, "GUIFailed"),
Luuk van Baalb7147f82025-02-08 18:52:39 +0100155 KEYVALUE_ENTRY(-EVENT_INSERTCHANGE, "InsertChange"),
156 KEYVALUE_ENTRY(-EVENT_INSERTCHARPRE, "InsertCharPre"),
157 KEYVALUE_ENTRY(-EVENT_INSERTENTER, "InsertEnter"),
158 KEYVALUE_ENTRY(-EVENT_INSERTLEAVE, "InsertLeave"),
159 KEYVALUE_ENTRY(-EVENT_INSERTLEAVEPRE, "InsertLeavePre"),
Shougo Matsushita83678842024-07-11 22:05:12 +0200160 KEYVALUE_ENTRY(EVENT_KEYINPUTPRE, "KeyInputPre"),
John Marriott78d742a2024-04-02 20:26:01 +0200161 KEYVALUE_ENTRY(EVENT_MENUPOPUP, "MenuPopup"),
162 KEYVALUE_ENTRY(EVENT_MODECHANGED, "ModeChanged"),
163 KEYVALUE_ENTRY(EVENT_OPTIONSET, "OptionSet"),
164 KEYVALUE_ENTRY(EVENT_QUICKFIXCMDPOST, "QuickFixCmdPost"),
165 KEYVALUE_ENTRY(EVENT_QUICKFIXCMDPRE, "QuickFixCmdPre"),
166 KEYVALUE_ENTRY(EVENT_QUITPRE, "QuitPre"),
167 KEYVALUE_ENTRY(EVENT_REMOTEREPLY, "RemoteReply"),
168 KEYVALUE_ENTRY(EVENT_SAFESTATE, "SafeState"),
169 KEYVALUE_ENTRY(EVENT_SAFESTATEAGAIN, "SafeStateAgain"),
170 KEYVALUE_ENTRY(EVENT_SESSIONLOADPOST, "SessionLoadPost"),
171 KEYVALUE_ENTRY(EVENT_SESSIONWRITEPOST, "SessionWritePost"),
172 KEYVALUE_ENTRY(EVENT_SHELLCMDPOST, "ShellCmdPost"),
Luuk van Baalb7147f82025-02-08 18:52:39 +0100173 KEYVALUE_ENTRY(-EVENT_SHELLFILTERPOST, "ShellFilterPost"),
John Marriott78d742a2024-04-02 20:26:01 +0200174 KEYVALUE_ENTRY(EVENT_SIGUSR1, "SigUSR1"),
175 KEYVALUE_ENTRY(EVENT_SOURCECMD, "SourceCmd"),
176 KEYVALUE_ENTRY(EVENT_SOURCEPOST, "SourcePost"),
177 KEYVALUE_ENTRY(EVENT_SOURCEPRE, "SourcePre"),
178 KEYVALUE_ENTRY(EVENT_SPELLFILEMISSING, "SpellFileMissing"),
179 KEYVALUE_ENTRY(EVENT_STDINREADPOST, "StdinReadPost"),
180 KEYVALUE_ENTRY(EVENT_STDINREADPRE, "StdinReadPre"),
181 KEYVALUE_ENTRY(EVENT_SWAPEXISTS, "SwapExists"),
182 KEYVALUE_ENTRY(EVENT_SYNTAX, "Syntax"),
183 KEYVALUE_ENTRY(EVENT_TABCLOSED, "TabClosed"),
Jim Zhou5606ca52025-03-13 21:58:25 +0100184 KEYVALUE_ENTRY(EVENT_TABCLOSEDPRE, "TabClosedPre"),
John Marriott78d742a2024-04-02 20:26:01 +0200185 KEYVALUE_ENTRY(EVENT_TABENTER, "TabEnter"),
186 KEYVALUE_ENTRY(EVENT_TABLEAVE, "TabLeave"),
187 KEYVALUE_ENTRY(EVENT_TABNEW, "TabNew"),
188 KEYVALUE_ENTRY(EVENT_TERMCHANGED, "TermChanged"),
189 KEYVALUE_ENTRY(EVENT_TERMINALOPEN, "TerminalOpen"),
190 KEYVALUE_ENTRY(EVENT_TERMINALWINOPEN, "TerminalWinOpen"),
191 KEYVALUE_ENTRY(EVENT_TERMRESPONSE, "TermResponse"),
192 KEYVALUE_ENTRY(EVENT_TERMRESPONSEALL, "TermResponseAll"),
Luuk van Baalb7147f82025-02-08 18:52:39 +0100193 KEYVALUE_ENTRY(-EVENT_TEXTCHANGED, "TextChanged"),
194 KEYVALUE_ENTRY(-EVENT_TEXTCHANGEDI, "TextChangedI"),
195 KEYVALUE_ENTRY(-EVENT_TEXTCHANGEDP, "TextChangedP"),
196 KEYVALUE_ENTRY(-EVENT_TEXTCHANGEDT, "TextChangedT"),
197 KEYVALUE_ENTRY(-EVENT_TEXTYANKPOST, "TextYankPost"),
John Marriott78d742a2024-04-02 20:26:01 +0200198 KEYVALUE_ENTRY(EVENT_USER, "User"),
199 KEYVALUE_ENTRY(EVENT_VIMENTER, "VimEnter"),
200 KEYVALUE_ENTRY(EVENT_VIMLEAVE, "VimLeave"),
201 KEYVALUE_ENTRY(EVENT_VIMLEAVEPRE, "VimLeavePre"),
202 KEYVALUE_ENTRY(EVENT_VIMRESIZED, "VimResized"),
203 KEYVALUE_ENTRY(EVENT_VIMRESUME, "VimResume"),
204 KEYVALUE_ENTRY(EVENT_VIMSUSPEND, "VimSuspend"),
Luuk van Baalb7147f82025-02-08 18:52:39 +0100205 KEYVALUE_ENTRY(-EVENT_WINCLOSED, "WinClosed"),
206 KEYVALUE_ENTRY(-EVENT_WINENTER, "WinEnter"),
207 KEYVALUE_ENTRY(-EVENT_WINLEAVE, "WinLeave"),
John Marriott78d742a2024-04-02 20:26:01 +0200208 KEYVALUE_ENTRY(EVENT_WINNEW, "WinNew"),
209 KEYVALUE_ENTRY(EVENT_WINNEWPRE, "WinNewPre"),
Luuk van Baalb7147f82025-02-08 18:52:39 +0100210 KEYVALUE_ENTRY(-EVENT_WINRESIZED, "WinResized"),
211 KEYVALUE_ENTRY(-EVENT_WINSCROLLED, "WinScrolled"),
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100212};
213
Luuk van Baalb7147f82025-02-08 18:52:39 +0100214static AutoPat *first_autopat[NUM_EVENTS] = { NULL };
215static AutoPat *last_autopat[NUM_EVENTS] = { NULL };
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100216
kylo252ae6f1d82022-02-16 19:24:07 +0000217#define AUGROUP_DEFAULT (-1) // default autocmd group
218#define AUGROUP_ERROR (-2) // erroneous autocmd group
219#define AUGROUP_ALL (-3) // all autocmd groups
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100220
221/*
222 * struct used to keep status while executing autocommands for an event.
223 */
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100224struct AutoPatCmd_S
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100225{
226 AutoPat *curpat; // next AutoPat to examine
227 AutoCmd *nextcmd; // next AutoCmd to execute
228 int group; // group being used
229 char_u *fname; // fname to match with
230 char_u *sfname; // sfname to match with
231 char_u *tail; // tail of fname
232 event_T event; // current event
LemonBoyeca7c602022-04-14 15:39:43 +0100233 sctx_T script_ctx; // script context where it is defined
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100234 int arg_bufnr; // Initially equal to <abuf>, set to zero when
235 // buf is deleted.
LemonBoyeca7c602022-04-14 15:39:43 +0100236 AutoPatCmd_T *next; // chain of active apc-s for auto-invalidation
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100237};
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100238
LemonBoyeca7c602022-04-14 15:39:43 +0100239static AutoPatCmd_T *active_apc_list = NULL; // stack of active autocommands
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100240
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200241// Macro to loop over all the patterns for an autocmd event
242#define FOR_ALL_AUTOCMD_PATTERNS(event, ap) \
243 for ((ap) = first_autopat[(int)(event)]; (ap) != NULL; (ap) = (ap)->next)
244
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100245/*
246 * augroups stores a list of autocmd group names.
247 */
248static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
249#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
Bram Moolenaarc667da52019-11-30 20:52:27 +0100250// use get_deleted_augroup() to get this
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100251static char_u *deleted_augroup = NULL;
252
253/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100254 * The ID of the current group. Group 0 is the default one.
255 */
256static int current_augroup = AUGROUP_DEFAULT;
257
Bram Moolenaarc667da52019-11-30 20:52:27 +0100258static int au_need_clean = FALSE; // need to delete marked patterns
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100259
John Marriott78d742a2024-04-02 20:26:01 +0200260static event_T event_name2nr(char_u *start, char_u **end);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100261static char_u *event_nr2name(event_T event);
262static int au_get_grouparg(char_u **argp);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200263static int do_autocmd_event(event_T event, char_u *pat, int once, int nested, char_u *cmd, int forceit, int group, int flags);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100264static int apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, int force, int group, buf_T *buf, exarg_T *eap);
LemonBoyeca7c602022-04-14 15:39:43 +0100265static void auto_next_pat(AutoPatCmd_T *apc, int stop_at_last);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100266static int au_find_group(char_u *name);
267
268static event_T last_event;
269static int last_group;
Bram Moolenaarc667da52019-11-30 20:52:27 +0100270static int autocmd_blocked = 0; // block all autocmds
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100271
272 static char_u *
273get_deleted_augroup(void)
274{
275 if (deleted_augroup == NULL)
276 deleted_augroup = (char_u *)_("--Deleted--");
277 return deleted_augroup;
278}
279
280/*
281 * Show the autocommands for one AutoPat.
282 */
283 static void
284show_autocmd(AutoPat *ap, event_T event)
285{
286 AutoCmd *ac;
287
288 // Check for "got_int" (here and at various places below), which is set
289 // when "q" has been hit for the "--more--" prompt
290 if (got_int)
291 return;
292 if (ap->pat == NULL) // pattern has been removed
293 return;
294
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000295 // Make sure no info referenced by "ap" is cleared, e.g. when a timer
296 // clears an augroup. Jump to "theend" after this!
297 // "ap->pat" may be cleared anyway.
298 ++autocmd_busy;
299
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100300 msg_putchar('\n');
301 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000302 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100303 if (event != last_event || ap->group != last_group)
304 {
305 if (ap->group != AUGROUP_DEFAULT)
306 {
307 if (AUGROUP_NAME(ap->group) == NULL)
308 msg_puts_attr((char *)get_deleted_augroup(), HL_ATTR(HLF_E));
309 else
310 msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
311 msg_puts(" ");
312 }
313 msg_puts_attr((char *)event_nr2name(event), HL_ATTR(HLF_T));
314 last_event = event;
315 last_group = ap->group;
316 msg_putchar('\n');
317 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000318 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100319 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000320
321 if (ap->pat == NULL)
322 goto theend; // timer might have cleared the pattern or group
323
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100324 msg_col = 4;
325 msg_outtrans(ap->pat);
326
327 for (ac = ap->cmds; ac != NULL; ac = ac->next)
328 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100329 if (ac->cmd == NULL) // skip removed commands
330 continue;
331
332 if (msg_col >= 14)
333 msg_putchar('\n');
334 msg_col = 14;
335 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000336 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100337 msg_outtrans(ac->cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100338#ifdef FEAT_EVAL
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100339 if (p_verbose > 0)
340 last_set_msg(ac->script_ctx);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100341#endif
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100342 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000343 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100344 if (ac->next != NULL)
345 {
346 msg_putchar('\n');
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100347 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000348 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100349 }
350 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000351
352theend:
353 --autocmd_busy;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100354}
355
356/*
357 * Mark an autocommand pattern for deletion.
358 */
359 static void
360au_remove_pat(AutoPat *ap)
361{
362 VIM_CLEAR(ap->pat);
363 ap->buflocal_nr = -1;
364 au_need_clean = TRUE;
365}
366
367/*
368 * Mark all commands for a pattern for deletion.
369 */
370 static void
371au_remove_cmds(AutoPat *ap)
372{
373 AutoCmd *ac;
374
375 for (ac = ap->cmds; ac != NULL; ac = ac->next)
376 VIM_CLEAR(ac->cmd);
377 au_need_clean = TRUE;
378}
379
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200380// Delete one command from an autocmd pattern.
381static void au_del_cmd(AutoCmd *ac)
382{
383 VIM_CLEAR(ac->cmd);
384 au_need_clean = TRUE;
385}
386
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100387/*
388 * Cleanup autocommands and patterns that have been deleted.
389 * This is only done when not executing autocommands.
390 */
391 static void
392au_cleanup(void)
393{
394 AutoPat *ap, **prev_ap;
395 AutoCmd *ac, **prev_ac;
396 event_T event;
397
398 if (autocmd_busy || !au_need_clean)
399 return;
400
401 // loop over all events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100402 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100403 event = (event_T)((int)event + 1))
404 {
405 // loop over all autocommand patterns
406 prev_ap = &(first_autopat[(int)event]);
407 for (ap = *prev_ap; ap != NULL; ap = *prev_ap)
408 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200409 int has_cmd = FALSE;
410
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200411 // loop over all commands for this pattern
412 prev_ac = &(ap->cmds);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100413 for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
414 {
415 // remove the command if the pattern is to be deleted or when
416 // the command has been marked for deletion
417 if (ap->pat == NULL || ac->cmd == NULL)
418 {
419 *prev_ac = ac->next;
420 vim_free(ac->cmd);
421 vim_free(ac);
422 }
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200423 else
424 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200425 has_cmd = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100426 prev_ac = &(ac->next);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200427 }
428 }
429
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200430 if (ap->pat != NULL && !has_cmd)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200431 // Pattern was not marked for deletion, but all of its
432 // commands were. So mark the pattern for deletion.
433 au_remove_pat(ap);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100434
435 // remove the pattern if it has been marked for deletion
436 if (ap->pat == NULL)
437 {
438 if (ap->next == NULL)
439 {
440 if (prev_ap == &(first_autopat[(int)event]))
441 last_autopat[(int)event] = NULL;
442 else
443 // this depends on the "next" field being the first in
444 // the struct
445 last_autopat[(int)event] = (AutoPat *)prev_ap;
446 }
447 *prev_ap = ap->next;
448 vim_regfree(ap->reg_prog);
449 vim_free(ap);
450 }
451 else
452 prev_ap = &(ap->next);
453 }
454 }
455
456 au_need_clean = FALSE;
457}
458
459/*
460 * Called when buffer is freed, to remove/invalidate related buffer-local
461 * autocmds.
462 */
463 void
464aubuflocal_remove(buf_T *buf)
465{
LemonBoyeca7c602022-04-14 15:39:43 +0100466 AutoPat *ap;
467 event_T event;
468 AutoPatCmd_T *apc;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100469
470 // invalidate currently executing autocommands
471 for (apc = active_apc_list; apc; apc = apc->next)
472 if (buf->b_fnum == apc->arg_bufnr)
473 apc->arg_bufnr = 0;
474
475 // invalidate buflocals looping through events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100476 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100477 event = (event_T)((int)event + 1))
478 // loop over all autocommand patterns
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200479 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100480 if (ap->buflocal_nr == buf->b_fnum)
481 {
482 au_remove_pat(ap);
483 if (p_verbose >= 6)
484 {
485 verbose_enter();
486 smsg(_("auto-removing autocommand: %s <buffer=%d>"),
487 event_nr2name(event), buf->b_fnum);
488 verbose_leave();
489 }
490 }
491 au_cleanup();
492}
493
494/*
495 * Add an autocmd group name.
496 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
497 */
498 static int
499au_new_group(char_u *name)
500{
501 int i;
502
503 i = au_find_group(name);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100504 if (i != AUGROUP_ERROR)
505 return i;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100506
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100507 // the group doesn't exist yet, add it. First try using a free entry.
508 for (i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100509 if (AUGROUP_NAME(i) == NULL)
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100510 break;
511 if (i == augroups.ga_len && ga_grow(&augroups, 1) == FAIL)
512 return AUGROUP_ERROR;
513
514 AUGROUP_NAME(i) = vim_strsave(name);
515 if (AUGROUP_NAME(i) == NULL)
516 return AUGROUP_ERROR;
517 if (i == augroups.ga_len)
518 ++augroups.ga_len;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100519
520 return i;
521}
522
523 static void
524au_del_group(char_u *name)
525{
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100526 int i;
527 event_T event;
528 AutoPat *ap;
529 int in_use = FALSE;
530
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100531
532 i = au_find_group(name);
533 if (i == AUGROUP_ERROR) // the group doesn't exist
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100534 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100535 semsg(_(e_no_such_group_str), name);
536 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100537 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100538 if (i == current_augroup)
539 {
540 emsg(_(e_cannot_delete_current_group));
541 return;
542 }
543
544 for (event = (event_T)0; (int)event < NUM_EVENTS;
545 event = (event_T)((int)event + 1))
546 {
547 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
548 if (ap->group == i && ap->pat != NULL)
549 {
550 give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
551 in_use = TRUE;
552 event = NUM_EVENTS;
553 break;
554 }
555 }
556 vim_free(AUGROUP_NAME(i));
557 if (in_use)
558 AUGROUP_NAME(i) = get_deleted_augroup();
559 else
560 AUGROUP_NAME(i) = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100561}
562
563/*
564 * Find the ID of an autocmd group name.
565 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
566 */
567 static int
568au_find_group(char_u *name)
569{
570 int i;
571
572 for (i = 0; i < augroups.ga_len; ++i)
573 if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
574 && STRCMP(AUGROUP_NAME(i), name) == 0)
575 return i;
576 return AUGROUP_ERROR;
577}
578
579/*
580 * Return TRUE if augroup "name" exists.
581 */
582 int
583au_has_group(char_u *name)
584{
585 return au_find_group(name) != AUGROUP_ERROR;
586}
587
588/*
589 * ":augroup {name}".
590 */
591 void
592do_augroup(char_u *arg, int del_group)
593{
594 int i;
595
596 if (del_group)
597 {
598 if (*arg == NUL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000599 emsg(_(e_argument_required));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100600 else
601 au_del_group(arg);
602 }
603 else if (STRICMP(arg, "end") == 0) // ":aug end": back to group 0
604 current_augroup = AUGROUP_DEFAULT;
605 else if (*arg) // ":aug xxx": switch to group xxx
606 {
607 i = au_new_group(arg);
608 if (i != AUGROUP_ERROR)
609 current_augroup = i;
610 }
611 else // ":aug": list the group names
612 {
613 msg_start();
614 for (i = 0; i < augroups.ga_len; ++i)
615 {
616 if (AUGROUP_NAME(i) != NULL)
617 {
618 msg_puts((char *)AUGROUP_NAME(i));
619 msg_puts(" ");
620 }
621 }
622 msg_clr_eos();
623 msg_end();
624 }
625}
626
Bram Moolenaare76062c2022-11-28 18:51:43 +0000627 void
628autocmd_init(void)
629{
630 CLEAR_FIELD(aucmd_win);
631}
632
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100633#if defined(EXITFREE) || defined(PROTO)
634 void
635free_all_autocmds(void)
636{
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100637 char_u *s;
638
639 for (current_augroup = -1; current_augroup < augroups.ga_len;
640 ++current_augroup)
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200641 do_autocmd(NULL, (char_u *)"", TRUE);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100642
Bram Moolenaare76062c2022-11-28 18:51:43 +0000643 for (int i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100644 {
645 s = ((char_u **)(augroups.ga_data))[i];
646 if (s != get_deleted_augroup())
647 vim_free(s);
648 }
649 ga_clear(&augroups);
Bram Moolenaare76062c2022-11-28 18:51:43 +0000650
Bram Moolenaar84497cd2022-11-28 20:34:52 +0000651 // aucmd_win[] is freed in win_free_all()
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100652}
653#endif
654
655/*
Bram Moolenaare76062c2022-11-28 18:51:43 +0000656 * Return TRUE if "win" is an active entry in aucmd_win[].
657 */
658 int
659is_aucmd_win(win_T *win)
660{
661 for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
662 if (aucmd_win[i].auc_win_used && aucmd_win[i].auc_win == win)
663 return TRUE;
664 return FALSE;
665}
666
667/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100668 * Return the event number for event name "start".
669 * Return NUM_EVENTS if the event name was not found.
670 * Return a pointer to the next event name in "end".
671 */
672 static event_T
673event_name2nr(char_u *start, char_u **end)
674{
675 char_u *p;
John Marriott78d742a2024-04-02 20:26:01 +0200676 keyvalue_T target;
677 keyvalue_T *entry;
678 static keyvalue_T *bufnewfile = &event_tab[BUFNEWFILE_INDEX];
679 static keyvalue_T *bufread = &event_tab[BUFREAD_INDEX];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100680
681 // the event name ends with end of line, '|', a blank or a comma
682 for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
683 ;
John Marriott78d742a2024-04-02 20:26:01 +0200684
685 target.key = 0;
John Marriott8d4477e2024-11-02 15:59:01 +0100686 target.value.string = start;
687 target.value.length = (size_t)(p - start);
John Marriott78d742a2024-04-02 20:26:01 +0200688
689 // special cases:
690 // BufNewFile and BufRead are searched for ALOT (especially at startup)
691 // so we check for them first.
692 if (cmp_keyvalue_value_ni(&target, bufnewfile) == 0)
693 entry = bufnewfile;
Luuk van Baalb7147f82025-02-08 18:52:39 +0100694 else if (cmp_keyvalue_value_ni(&target, bufread) == 0)
John Marriott78d742a2024-04-02 20:26:01 +0200695 entry = bufread;
696 else
Luuk van Baalb7147f82025-02-08 18:52:39 +0100697 entry = (keyvalue_T *)bsearch(&target, &event_tab, NUM_EVENTS,
698 sizeof(event_tab[0]), cmp_keyvalue_value_ni);
John Marriott78d742a2024-04-02 20:26:01 +0200699
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100700 if (*p == ',')
701 ++p;
702 *end = p;
John Marriott78d742a2024-04-02 20:26:01 +0200703
Luuk van Baalb7147f82025-02-08 18:52:39 +0100704 return (entry == NULL) ? NUM_EVENTS : (event_T)abs(entry->key);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100705}
706
707/*
708 * Return the name for event "event".
709 */
710 static char_u *
711event_nr2name(event_T event)
712{
713 int i;
John Marriott78d742a2024-04-02 20:26:01 +0200714#define CACHE_SIZE 12
715 static int cache_tab[CACHE_SIZE];
716 static int cache_last_index = -1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100717
John Marriott78d742a2024-04-02 20:26:01 +0200718 if (cache_last_index < 0)
719 {
Luuk van Baalb7147f82025-02-08 18:52:39 +0100720 for (i = 0; i < CACHE_SIZE; ++i)
John Marriott78d742a2024-04-02 20:26:01 +0200721 cache_tab[i] = -1;
Luuk van Baalb7147f82025-02-08 18:52:39 +0100722 cache_last_index = CACHE_SIZE - 1;
John Marriott78d742a2024-04-02 20:26:01 +0200723 }
724
725 // first look in the cache
726 // the cache is circular. to search it we start at the most recent entry
727 // and go backwards wrapping around when we get to index 0.
728 for (i = cache_last_index; cache_tab[i] >= 0; )
729 {
Luuk van Baalb7147f82025-02-08 18:52:39 +0100730 if ((event_T)abs(event_tab[cache_tab[i]].key) == event)
John Marriott8d4477e2024-11-02 15:59:01 +0100731 return event_tab[cache_tab[i]].value.string;
John Marriott78d742a2024-04-02 20:26:01 +0200732
733 if (i == 0)
Luuk van Baalb7147f82025-02-08 18:52:39 +0100734 i = CACHE_SIZE - 1;
John Marriott78d742a2024-04-02 20:26:01 +0200735 else
736 --i;
737
738 // are we back at the start?
739 if (i == cache_last_index)
740 break;
741 }
742
743 // look in the event table itself
Luuk van Baalb7147f82025-02-08 18:52:39 +0100744 for (i = 0; i < NUM_EVENTS; ++i)
John Marriott78d742a2024-04-02 20:26:01 +0200745 {
Luuk van Baalb7147f82025-02-08 18:52:39 +0100746 if ((event_T)abs(event_tab[i].key) == event)
John Marriott78d742a2024-04-02 20:26:01 +0200747 {
748 // store the found entry in the next position in the cache,
749 // wrapping around when we get to the maximum index.
Luuk van Baalb7147f82025-02-08 18:52:39 +0100750 if (cache_last_index == CACHE_SIZE - 1)
John Marriott78d742a2024-04-02 20:26:01 +0200751 cache_last_index = 0;
752 else
753 ++cache_last_index;
754 cache_tab[cache_last_index] = i;
755 break;
756 }
757 }
758
Luuk van Baalb7147f82025-02-08 18:52:39 +0100759 return (i == NUM_EVENTS) ? (char_u *)"Unknown" :
760 event_tab[i].value.string;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100761}
762
763/*
764 * Scan over the events. "*" stands for all events.
765 */
766 static char_u *
767find_end_event(
768 char_u *arg,
769 int have_group) // TRUE when group name was found
770{
771 char_u *pat;
772 char_u *p;
773
774 if (*arg == '*')
775 {
776 if (arg[1] && !VIM_ISWHITE(arg[1]))
777 {
Bram Moolenaar6d057012021-12-31 18:49:43 +0000778 semsg(_(e_illegal_character_after_star_str), arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100779 return NULL;
780 }
781 pat = arg + 1;
782 }
783 else
784 {
785 for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p)
786 {
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100787 if ((int)event_name2nr(pat, &p) >= NUM_EVENTS)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100788 {
789 if (have_group)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000790 semsg(_(e_no_such_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100791 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000792 semsg(_(e_no_such_group_or_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100793 return NULL;
794 }
795 }
796 }
797 return pat;
798}
799
800/*
Luuk van Baalb7147f82025-02-08 18:52:39 +0100801 * Return TRUE if "event" is included in 'eventignore(win)'.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100802 */
Luuk van Baalb7147f82025-02-08 18:52:39 +0100803 int
804event_ignored(event_T event, char_u *ei)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100805{
Luuk van Baal8cc6d8b2025-05-31 12:10:31 +0200806 int ignored = FALSE;
Luuk van Baalb7147f82025-02-08 18:52:39 +0100807 while (*ei != NUL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100808 {
Luuk van Baal8cc6d8b2025-05-31 12:10:31 +0200809 int unignore = *ei == '-';
810 ei += unignore;
811 if (STRNICMP(ei, "all", 3) == 0 && (ei[3] == NUL || ei[3] == ','))
812 {
813 ignored = ei == p_ei || (event_tab[event].key <= 0);
814 ei += 3 + (ei[3] == ',');
815 }
816 else if (event_name2nr(ei, &ei) == event)
817 {
818 if (unignore)
819 return FALSE;
820 else
821 ignored = TRUE;
822 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100823 }
824
Luuk van Baal8cc6d8b2025-05-31 12:10:31 +0200825 return ignored;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100826}
827
828/*
Luuk van Baalb7147f82025-02-08 18:52:39 +0100829 * Return OK when the contents of 'eventignore' or 'eventignorewin' is valid,
830 * FAIL otherwise.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100831 */
832 int
Luuk van Baalb7147f82025-02-08 18:52:39 +0100833check_ei(char_u *ei)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100834{
Luuk van Baalb7147f82025-02-08 18:52:39 +0100835 int win = ei != p_ei;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100836
Luuk van Baalb7147f82025-02-08 18:52:39 +0100837 while (*ei)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100838 {
Luuk van Baalb7147f82025-02-08 18:52:39 +0100839 if (STRNICMP(ei, "all", 3) == 0 && (ei[3] == NUL || ei[3] == ','))
Luuk van Baal8cc6d8b2025-05-31 12:10:31 +0200840 ei += 3 + (ei[3] == ',');
Luuk van Baalb7147f82025-02-08 18:52:39 +0100841 else
842 {
Luuk van Baal8cc6d8b2025-05-31 12:10:31 +0200843 ei += (*ei == '-');
Luuk van Baalb7147f82025-02-08 18:52:39 +0100844 event_T event = event_name2nr(ei, &ei);
845 if (event == NUM_EVENTS || (win && event_tab[event].key > 0))
846 return FAIL;
847 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100848 }
849
850 return OK;
851}
852
853# if defined(FEAT_SYN_HL) || defined(PROTO)
854
855/*
856 * Add "what" to 'eventignore' to skip loading syntax highlighting for every
857 * buffer loaded into the window. "what" must start with a comma.
858 * Returns the old value of 'eventignore' in allocated memory.
859 */
860 char_u *
861au_event_disable(char *what)
862{
863 char_u *new_ei;
864 char_u *save_ei;
John Marriott78d742a2024-04-02 20:26:01 +0200865 size_t p_ei_len;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100866
John Marriott78d742a2024-04-02 20:26:01 +0200867 p_ei_len = STRLEN(p_ei);
868 save_ei = vim_strnsave(p_ei, p_ei_len);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100869 if (save_ei == NULL)
870 return NULL;
871
John Marriott78d742a2024-04-02 20:26:01 +0200872 new_ei = vim_strnsave(p_ei, p_ei_len + STRLEN(what));
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100873 if (new_ei == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100874 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100875 vim_free(save_ei);
876 return NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100877 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100878
879 if (*what == ',' && *p_ei == NUL)
880 STRCPY(new_ei, what + 1);
881 else
zeertzjq969e11a2025-03-10 21:15:19 +0100882 STRCPY(new_ei + p_ei_len, what);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100883 set_string_option_direct((char_u *)"ei", -1, new_ei,
884 OPT_FREE, SID_NONE);
885 vim_free(new_ei);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100886 return save_ei;
887}
888
889 void
890au_event_restore(char_u *old_ei)
891{
892 if (old_ei != NULL)
893 {
894 set_string_option_direct((char_u *)"ei", -1, old_ei,
895 OPT_FREE, SID_NONE);
896 vim_free(old_ei);
897 }
898}
Bram Moolenaarc667da52019-11-30 20:52:27 +0100899# endif // FEAT_SYN_HL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100900
901/*
902 * do_autocmd() -- implements the :autocmd command. Can be used in the
903 * following ways:
904 *
905 * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
906 * will be automatically executed for <event>
907 * when editing a file matching <pat>, in
908 * the current group.
909 * :autocmd <event> <pat> Show the autocommands associated with
910 * <event> and <pat>.
911 * :autocmd <event> Show the autocommands associated with
912 * <event>.
913 * :autocmd Show all autocommands.
914 * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
915 * <event> and <pat>, and add the command
916 * <cmd>, for the current group.
917 * :autocmd! <event> <pat> Remove all autocommands associated with
918 * <event> and <pat> for the current group.
919 * :autocmd! <event> Remove all autocommands associated with
920 * <event> for the current group.
921 * :autocmd! Remove ALL autocommands for the current
922 * group.
923 *
924 * Multiple events and patterns may be given separated by commas. Here are
925 * some examples:
926 * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
927 * :autocmd bufleave * set tw=79 nosmartindent ic infercase
928 *
929 * :autocmd * *.c show all autocommands for *.c files.
930 *
931 * Mostly a {group} argument can optionally appear before <event>.
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200932 * "eap" can be NULL.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100933 */
934 void
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200935do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100936{
937 char_u *arg = arg_in;
938 char_u *pat;
939 char_u *envpat = NULL;
940 char_u *cmd;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200941 int cmd_need_free = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100942 event_T event;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200943 char_u *tofree = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100944 int nested = FALSE;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200945 int once = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100946 int group;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200947 int i;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200948 int flags = 0;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100949
950 if (*arg == '|')
951 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000952 eap->nextcmd = arg + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100953 arg = (char_u *)"";
954 group = AUGROUP_ALL; // no argument, use all groups
955 }
956 else
957 {
958 /*
959 * Check for a legal group name. If not, use AUGROUP_ALL.
960 */
961 group = au_get_grouparg(&arg);
962 if (arg == NULL) // out of memory
963 return;
964 }
965
966 /*
967 * Scan over the events.
968 * If we find an illegal name, return here, don't do anything.
969 */
970 pat = find_end_event(arg, group != AUGROUP_ALL);
971 if (pat == NULL)
972 return;
973
974 pat = skipwhite(pat);
975 if (*pat == '|')
976 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000977 eap->nextcmd = pat + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100978 pat = (char_u *)"";
979 cmd = (char_u *)"";
980 }
981 else
982 {
983 /*
984 * Scan over the pattern. Put a NUL at the end.
985 */
986 cmd = pat;
987 while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
988 cmd++;
989 if (*cmd)
990 *cmd++ = NUL;
991
992 // Expand environment variables in the pattern. Set 'shellslash', we
993 // want forward slashes here.
994 if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
995 {
996#ifdef BACKSLASH_IN_FILENAME
997 int p_ssl_save = p_ssl;
998
999 p_ssl = TRUE;
1000#endif
1001 envpat = expand_env_save(pat);
1002#ifdef BACKSLASH_IN_FILENAME
1003 p_ssl = p_ssl_save;
1004#endif
1005 if (envpat != NULL)
1006 pat = envpat;
1007 }
1008
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001009 cmd = skipwhite(cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001010 for (i = 0; i < 2; i++)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001011 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001012 if (*cmd == NUL)
1013 continue;
1014
1015 // Check for "++once" flag.
1016 if (STRNCMP(cmd, "++once", 6) == 0 && VIM_ISWHITE(cmd[6]))
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001017 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001018 if (once)
1019 semsg(_(e_duplicate_argument_str), "++once");
1020 once = TRUE;
1021 cmd = skipwhite(cmd + 6);
1022 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001023
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001024 // Check for "++nested" flag.
1025 if ((STRNCMP(cmd, "++nested", 8) == 0 && VIM_ISWHITE(cmd[8])))
1026 {
1027 if (nested)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001028 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001029 semsg(_(e_duplicate_argument_str), "++nested");
1030 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001031 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001032 nested = TRUE;
1033 cmd = skipwhite(cmd + 8);
1034 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001035
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001036 // Check for the old "nested" flag in legacy script.
1037 if (STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
1038 {
1039 if (in_vim9script())
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001040 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001041 // If there ever is a :nested command this error should
1042 // be removed and "nested" accepted as the start of the
1043 // command.
1044 emsg(_(e_invalid_command_nested_did_you_mean_plusplus_nested));
1045 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001046 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001047 if (nested)
1048 {
1049 semsg(_(e_duplicate_argument_str), "nested");
1050 return;
1051 }
1052 nested = TRUE;
1053 cmd = skipwhite(cmd + 6);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001054 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001055 }
1056
1057 /*
1058 * Find the start of the commands.
1059 * Expand <sfile> in it.
1060 */
1061 if (*cmd != NUL)
1062 {
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001063 if (eap != NULL)
1064 // Read a {} block if it follows.
1065 cmd = may_get_cmd_block(eap, cmd, &tofree, &flags);
1066
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001067 cmd = expand_sfile(cmd);
1068 if (cmd == NULL) // some error
1069 return;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001070 cmd_need_free = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001071 }
1072 }
1073
1074 /*
1075 * Print header when showing autocommands.
1076 */
1077 if (!forceit && *cmd == NUL)
1078 // Highlight title
1079 msg_puts_title(_("\n--- Autocommands ---"));
1080
1081 /*
1082 * Loop over the events.
1083 */
1084 last_event = (event_T)-1; // for listing the event name
1085 last_group = AUGROUP_ERROR; // for listing the group name
1086 if (*arg == '*' || *arg == NUL || *arg == '|')
1087 {
Bram Moolenaarb6db1462021-12-24 19:24:47 +00001088 if (*cmd != NUL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001089 emsg(_(e_cannot_define_autocommands_for_all_events));
1090 else
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +01001091 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001092 event = (event_T)((int)event + 1))
1093 if (do_autocmd_event(event, pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001094 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001095 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001096 }
1097 else
1098 {
1099 while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
1100 if (do_autocmd_event(event_name2nr(arg, &arg), pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001101 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001102 break;
1103 }
1104
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001105 if (cmd_need_free)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001106 vim_free(cmd);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001107 vim_free(tofree);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001108 vim_free(envpat);
1109}
1110
1111/*
1112 * Find the group ID in a ":autocmd" or ":doautocmd" argument.
1113 * The "argp" argument is advanced to the following argument.
1114 *
1115 * Returns the group ID, AUGROUP_ERROR for error (out of memory).
1116 */
1117 static int
1118au_get_grouparg(char_u **argp)
1119{
1120 char_u *group_name;
1121 char_u *p;
1122 char_u *arg = *argp;
1123 int group = AUGROUP_ALL;
1124
1125 for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p)
1126 ;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001127 if (p <= arg)
1128 return AUGROUP_ALL;
1129
1130 group_name = vim_strnsave(arg, p - arg);
1131 if (group_name == NULL) // out of memory
1132 return AUGROUP_ERROR;
1133 group = au_find_group(group_name);
1134 if (group == AUGROUP_ERROR)
1135 group = AUGROUP_ALL; // no match, use all groups
1136 else
1137 *argp = skipwhite(p); // match, skip over group name
1138 vim_free(group_name);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001139 return group;
1140}
1141
1142/*
1143 * do_autocmd() for one event.
1144 * If *pat == NUL do for all patterns.
1145 * If *cmd == NUL show entries.
1146 * If forceit == TRUE delete entries.
1147 * If group is not AUGROUP_ALL, only use this group.
1148 */
1149 static int
1150do_autocmd_event(
1151 event_T event,
1152 char_u *pat,
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001153 int once,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001154 int nested,
1155 char_u *cmd,
1156 int forceit,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001157 int group,
1158 int flags)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001159{
1160 AutoPat *ap;
1161 AutoPat **prev_ap;
1162 AutoCmd *ac;
1163 AutoCmd **prev_ac;
1164 int brace_level;
1165 char_u *endpat;
1166 int findgroup;
1167 int allgroups;
1168 int patlen;
1169 int is_buflocal;
1170 int buflocal_nr;
Bram Moolenaarc667da52019-11-30 20:52:27 +01001171 char_u buflocal_pat[25]; // for "<buffer=X>"
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001172
1173 if (group == AUGROUP_ALL)
1174 findgroup = current_augroup;
1175 else
1176 findgroup = group;
1177 allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
1178
1179 /*
1180 * Show or delete all patterns for an event.
1181 */
1182 if (*pat == NUL)
1183 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001184 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001185 {
1186 if (forceit) // delete the AutoPat, if it's in the current group
1187 {
1188 if (ap->group == findgroup)
1189 au_remove_pat(ap);
1190 }
1191 else if (group == AUGROUP_ALL || ap->group == group)
1192 show_autocmd(ap, event);
1193 }
1194 }
1195
1196 /*
1197 * Loop through all the specified patterns.
1198 */
1199 for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
1200 {
1201 /*
1202 * Find end of the pattern.
1203 * Watch out for a comma in braces, like "*.\{obj,o\}".
1204 */
1205 brace_level = 0;
1206 for (endpat = pat; *endpat && (*endpat != ',' || brace_level
1207 || (endpat > pat && endpat[-1] == '\\')); ++endpat)
1208 {
1209 if (*endpat == '{')
1210 brace_level++;
1211 else if (*endpat == '}')
1212 brace_level--;
1213 }
1214 if (pat == endpat) // ignore single comma
1215 continue;
1216 patlen = (int)(endpat - pat);
1217
1218 /*
1219 * detect special <buflocal[=X]> buffer-local patterns
1220 */
1221 is_buflocal = FALSE;
1222 buflocal_nr = 0;
1223
1224 if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
1225 && pat[patlen - 1] == '>')
1226 {
1227 // "<buffer...>": Error will be printed only for addition.
1228 // printing and removing will proceed silently.
1229 is_buflocal = TRUE;
1230 if (patlen == 8)
1231 // "<buffer>"
1232 buflocal_nr = curbuf->b_fnum;
1233 else if (patlen > 9 && pat[7] == '=')
1234 {
1235 if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0)
1236 // "<buffer=abuf>"
1237 buflocal_nr = autocmd_bufnr;
1238 else if (skipdigits(pat + 8) == pat + patlen - 1)
1239 // "<buffer=123>"
1240 buflocal_nr = atoi((char *)pat + 8);
1241 }
1242 }
1243
1244 if (is_buflocal)
1245 {
1246 // normalize pat into standard "<buffer>#N" form
1247 sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
1248 pat = buflocal_pat; // can modify pat and patlen
1249 patlen = (int)STRLEN(buflocal_pat); // but not endpat
1250 }
1251
1252 /*
1253 * Find AutoPat entries with this pattern. When adding a command it
1254 * always goes at or after the last one, so start at the end.
1255 */
1256 if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL)
1257 prev_ap = &last_autopat[(int)event];
1258 else
1259 prev_ap = &first_autopat[(int)event];
1260 while ((ap = *prev_ap) != NULL)
1261 {
1262 if (ap->pat != NULL)
1263 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001264 /*
1265 * Accept a pattern when:
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001266 * - a group was specified and it's that group, or a group was
1267 * not specified and it's the current group, or a group was
1268 * not specified and we are listing
1269 * - the length of the pattern matches
1270 * - the pattern matches.
1271 * For <buffer[=X]>, this condition works because we normalize
1272 * all buffer-local patterns.
1273 */
1274 if ((allgroups || ap->group == findgroup)
1275 && ap->patlen == patlen
1276 && STRNCMP(pat, ap->pat, patlen) == 0)
1277 {
1278 /*
1279 * Remove existing autocommands.
1280 * If adding any new autocmd's for this AutoPat, don't
1281 * delete the pattern from the autopat list, append to
1282 * this list.
1283 */
1284 if (forceit)
1285 {
1286 if (*cmd != NUL && ap->next == NULL)
1287 {
1288 au_remove_cmds(ap);
1289 break;
1290 }
1291 au_remove_pat(ap);
1292 }
1293
1294 /*
1295 * Show autocmd's for this autopat, or buflocals <buffer=X>
1296 */
1297 else if (*cmd == NUL)
1298 show_autocmd(ap, event);
1299
1300 /*
1301 * Add autocmd to this autopat, if it's the last one.
1302 */
1303 else if (ap->next == NULL)
1304 break;
1305 }
1306 }
1307 prev_ap = &ap->next;
1308 }
1309
1310 /*
1311 * Add a new command.
1312 */
1313 if (*cmd != NUL)
1314 {
1315 /*
1316 * If the pattern we want to add a command to does appear at the
1317 * end of the list (or not is not in the list at all), add the
1318 * pattern at the end of the list.
1319 */
1320 if (ap == NULL)
1321 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001322 // refuse to add buffer-local ap if buffer number is invalid
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001323 if (is_buflocal && (buflocal_nr == 0
1324 || buflist_findnr(buflocal_nr) == NULL))
1325 {
Bram Moolenaarf1474d82021-12-31 19:59:55 +00001326 semsg(_(e_buffer_nr_invalid_buffer_number), buflocal_nr);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001327 return FAIL;
1328 }
1329
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001330 ap = ALLOC_ONE(AutoPat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001331 if (ap == NULL)
1332 return FAIL;
1333 ap->pat = vim_strnsave(pat, patlen);
1334 ap->patlen = patlen;
1335 if (ap->pat == NULL)
1336 {
1337 vim_free(ap);
1338 return FAIL;
1339 }
1340
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001341#ifdef FEAT_EVAL
1342 // need to initialize last_mode for the first ModeChanged
1343 // autocmd
1344 if (event == EVENT_MODECHANGED && !has_modechanged())
LemonBoy2bf52dd2022-04-09 18:17:34 +01001345 get_mode(last_mode);
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001346#endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001347 // Initialize the fields checked by the WinScrolled and
1348 // WinResized trigger to prevent them from firing right after
1349 // the first autocmd is defined.
1350 if ((event == EVENT_WINSCROLLED || event == EVENT_WINRESIZED)
1351 && !(has_winscrolled() || has_winresized()))
LemonBoy09371822022-04-08 15:18:45 +01001352 {
Bram Moolenaar29967732022-11-20 12:11:45 +00001353 tabpage_T *save_curtab = curtab;
1354 tabpage_T *tp;
1355 FOR_ALL_TABPAGES(tp)
1356 {
1357 unuse_tabpage(curtab);
1358 use_tabpage(tp);
1359 snapshot_windows_scroll_size();
1360 }
1361 unuse_tabpage(curtab);
1362 use_tabpage(save_curtab);
LemonBoy09371822022-04-08 15:18:45 +01001363 }
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001364
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001365 if (is_buflocal)
1366 {
1367 ap->buflocal_nr = buflocal_nr;
1368 ap->reg_prog = NULL;
1369 }
1370 else
1371 {
1372 char_u *reg_pat;
1373
1374 ap->buflocal_nr = 0;
1375 reg_pat = file_pat_to_reg_pat(pat, endpat,
1376 &ap->allow_dirs, TRUE);
1377 if (reg_pat != NULL)
1378 ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
1379 vim_free(reg_pat);
1380 if (reg_pat == NULL || ap->reg_prog == NULL)
1381 {
1382 vim_free(ap->pat);
1383 vim_free(ap);
1384 return FAIL;
1385 }
1386 }
1387 ap->cmds = NULL;
1388 *prev_ap = ap;
1389 last_autopat[(int)event] = ap;
1390 ap->next = NULL;
1391 if (group == AUGROUP_ALL)
1392 ap->group = current_augroup;
1393 else
1394 ap->group = group;
1395 }
1396
1397 /*
1398 * Add the autocmd at the end of the AutoCmd list.
1399 */
1400 prev_ac = &(ap->cmds);
1401 while ((ac = *prev_ac) != NULL)
1402 prev_ac = &ac->next;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001403 ac = ALLOC_ONE(AutoCmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001404 if (ac == NULL)
1405 return FAIL;
1406 ac->cmd = vim_strsave(cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001407 ac->script_ctx = current_sctx;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001408 if (flags & UC_VIM9)
1409 ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001410#ifdef FEAT_EVAL
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001411 ac->script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001412#endif
1413 if (ac->cmd == NULL)
1414 {
1415 vim_free(ac);
1416 return FAIL;
1417 }
1418 ac->next = NULL;
1419 *prev_ac = ac;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001420 ac->once = once;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001421 ac->nested = nested;
1422 }
1423 }
1424
1425 au_cleanup(); // may really delete removed patterns/commands now
1426 return OK;
1427}
1428
1429/*
1430 * Implementation of ":doautocmd [group] event [fname]".
1431 * Return OK for success, FAIL for failure;
1432 */
1433 int
1434do_doautocmd(
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001435 char_u *arg_start,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001436 int do_msg, // give message for no matching autocmds?
1437 int *did_something)
1438{
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001439 char_u *arg = arg_start;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001440 char_u *fname;
1441 int nothing_done = TRUE;
1442 int group;
1443
1444 if (did_something != NULL)
1445 *did_something = FALSE;
1446
1447 /*
1448 * Check for a legal group name. If not, use AUGROUP_ALL.
1449 */
1450 group = au_get_grouparg(&arg);
1451 if (arg == NULL) // out of memory
1452 return FAIL;
1453
1454 if (*arg == '*')
1455 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001456 emsg(_(e_cant_execute_autocommands_for_all_events));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001457 return FAIL;
1458 }
1459
1460 /*
1461 * Scan over the events.
1462 * If we find an illegal name, return here, don't do anything.
1463 */
1464 fname = find_end_event(arg, group != AUGROUP_ALL);
1465 if (fname == NULL)
1466 return FAIL;
1467
1468 fname = skipwhite(fname);
1469
1470 /*
1471 * Loop over the events.
1472 */
1473 while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
1474 if (apply_autocmds_group(event_name2nr(arg, &arg),
1475 fname, NULL, TRUE, group, curbuf, NULL))
1476 nothing_done = FALSE;
1477
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001478 if (nothing_done && do_msg
1479#ifdef FEAT_EVAL
1480 && !aborting()
1481#endif
1482 )
1483 smsg(_("No matching autocommands: %s"), arg_start);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001484 if (did_something != NULL)
1485 *did_something = !nothing_done;
1486
1487#ifdef FEAT_EVAL
1488 return aborting() ? FAIL : OK;
1489#else
1490 return OK;
1491#endif
1492}
1493
1494/*
1495 * ":doautoall": execute autocommands for each loaded buffer.
1496 */
1497 void
1498ex_doautoall(exarg_T *eap)
1499{
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001500 int retval = OK;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001501 aco_save_T aco;
1502 buf_T *buf;
1503 bufref_T bufref;
1504 char_u *arg = eap->arg;
1505 int call_do_modelines = check_nomodeline(&arg);
1506 int did_aucmd;
1507
1508 /*
1509 * This is a bit tricky: For some commands curwin->w_buffer needs to be
1510 * equal to curbuf, but for some buffers there may not be a window.
1511 * So we change the buffer for the current window for a moment. This
1512 * gives problems when the autocommands make changes to the list of
1513 * buffers or windows...
1514 */
1515 FOR_ALL_BUFFERS(buf)
1516 {
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001517 // Only do loaded buffers and skip the current buffer, it's done last.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001518 if (buf->b_ml.ml_mfp == NULL || buf == curbuf)
1519 continue;
1520
Bram Moolenaare76062c2022-11-28 18:51:43 +00001521 // Find a window for this buffer and save some values.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001522 aucmd_prepbuf(&aco, buf);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001523 if (curbuf != buf)
1524 {
1525 // Failed to find a window for this buffer. Better not execute
1526 // autocommands then.
1527 retval = FAIL;
1528 break;
1529 }
1530
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001531 set_bufref(&bufref, buf);
1532
1533 // execute the autocommands for this buffer
1534 retval = do_doautocmd(arg, FALSE, &did_aucmd);
1535
1536 if (call_do_modelines && did_aucmd)
1537 // Execute the modeline settings, but don't set window-local
1538 // options if we are using the current window for another
1539 // buffer.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001540 do_modelines(is_aucmd_win(curwin) ? OPT_NOWIN : 0);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001541
1542 // restore the current window
1543 aucmd_restbuf(&aco);
1544
1545 // stop if there is some error or buffer was deleted
1546 if (retval == FAIL || !bufref_valid(&bufref))
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001547 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001548 retval = FAIL;
1549 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001550 }
1551 }
1552
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001553 // Execute autocommands for the current buffer last.
1554 if (retval == OK)
1555 {
1556 do_doautocmd(arg, FALSE, &did_aucmd);
1557 if (call_do_modelines && did_aucmd)
1558 do_modelines(0);
1559 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001560}
1561
1562/*
1563 * Check *argp for <nomodeline>. When it is present return FALSE, otherwise
1564 * return TRUE and advance *argp to after it.
1565 * Thus return TRUE when do_modelines() should be called.
1566 */
1567 int
1568check_nomodeline(char_u **argp)
1569{
1570 if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
1571 {
1572 *argp = skipwhite(*argp + 12);
1573 return FALSE;
1574 }
1575 return TRUE;
1576}
1577
1578/*
1579 * Prepare for executing autocommands for (hidden) buffer "buf".
1580 * Search for a visible window containing the current buffer. If there isn't
Bram Moolenaare76062c2022-11-28 18:51:43 +00001581 * one then use an entry in "aucmd_win[]".
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001582 * Set "curbuf" and "curwin" to match "buf".
Bram Moolenaare76062c2022-11-28 18:51:43 +00001583 * When this fails "curbuf" is not equal "buf".
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001584 */
1585 void
1586aucmd_prepbuf(
1587 aco_save_T *aco, // structure to save values in
1588 buf_T *buf) // new curbuf
1589{
1590 win_T *win;
1591 int save_ea;
1592#ifdef FEAT_AUTOCHDIR
1593 int save_acd;
1594#endif
zeertzjq5717ee32025-05-25 16:59:50 +02001595 int same_buffer = buf == curbuf;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001596
1597 // Find a window that is for the new buffer
zeertzjq5717ee32025-05-25 16:59:50 +02001598 if (same_buffer) // be quick when buf is curbuf
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001599 win = curwin;
1600 else
1601 FOR_ALL_WINDOWS(win)
1602 if (win->w_buffer == buf)
1603 break;
1604
Bram Moolenaare76062c2022-11-28 18:51:43 +00001605 // Allocate a window when needed.
1606 win_T *auc_win = NULL;
1607 int auc_idx = AUCMD_WIN_COUNT;
1608 if (win == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001609 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001610 for (auc_idx = 0; auc_idx < AUCMD_WIN_COUNT; ++auc_idx)
1611 if (!aucmd_win[auc_idx].auc_win_used)
1612 {
Bram Moolenaar84497cd2022-11-28 20:34:52 +00001613 if (aucmd_win[auc_idx].auc_win == NULL)
1614 aucmd_win[auc_idx].auc_win = win_alloc_popup_win();
1615 auc_win = aucmd_win[auc_idx].auc_win;
Bram Moolenaare76062c2022-11-28 18:51:43 +00001616 if (auc_win != NULL)
Bram Moolenaare76062c2022-11-28 18:51:43 +00001617 aucmd_win[auc_idx].auc_win_used = TRUE;
Bram Moolenaare76062c2022-11-28 18:51:43 +00001618 break;
1619 }
1620
1621 // If this fails (out of memory or using all AUCMD_WIN_COUNT
1622 // entries) then we can't reliable execute the autocmd, return with
1623 // "curbuf" unequal "buf".
1624 if (auc_win == NULL)
1625 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001626 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001627
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001628 aco->save_curwin_id = curwin->w_id;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001629 aco->save_prevwin_id = prevwin == NULL ? 0 : prevwin->w_id;
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001630 aco->save_State = State;
zeertzjqf2678472024-01-17 21:22:59 +01001631#ifdef FEAT_JOB_CHANNEL
1632 if (bt_prompt(curbuf))
1633 aco->save_prompt_insert = curbuf->b_prompt_insert;
1634#endif
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001635
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001636 if (win != NULL)
1637 {
1638 // There is a window for "buf" in the current tab page, make it the
1639 // curwin. This is preferred, it has the least side effects (esp. if
1640 // "buf" is curbuf).
Bram Moolenaare76062c2022-11-28 18:51:43 +00001641 aco->use_aucmd_win_idx = -1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001642 curwin = win;
1643 }
1644 else
1645 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001646 // There is no window for "buf", use "auc_win". To minimize the side
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001647 // effects, insert it in the current tab page.
1648 // Anything related to a window (e.g., setting folds) may have
1649 // unexpected results.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001650 aco->use_aucmd_win_idx = auc_idx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001651
Bram Moolenaare76062c2022-11-28 18:51:43 +00001652 win_init_popup_win(auc_win, buf);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001653
zeertzjq9d956ee2024-04-07 18:16:10 +02001654 // Make sure tp_localdir and globaldir are NULL to avoid a
1655 // chdir() in win_enter_ext().
1656 // win_init_popup_win() has already set w_localdir to NULL.
1657 aco->tp_localdir = curtab->tp_localdir;
1658 curtab->tp_localdir = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001659 aco->globaldir = globaldir;
1660 globaldir = NULL;
1661
Bram Moolenaare76062c2022-11-28 18:51:43 +00001662 // Split the current window, put the auc_win in the upper half.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001663 // We don't want the BufEnter or WinEnter autocommands.
1664 block_autocmds();
1665 make_snapshot(SNAP_AUCMD_IDX);
1666 save_ea = p_ea;
1667 p_ea = FALSE;
1668
1669#ifdef FEAT_AUTOCHDIR
1670 // Prevent chdir() call in win_enter_ext(), through do_autochdir().
1671 save_acd = p_acd;
1672 p_acd = FALSE;
1673#endif
1674
Sean Dewar704966c2024-02-20 22:00:33 +01001675 (void)win_split_ins(0, WSP_TOP | WSP_FORCE_ROOM, auc_win, 0, NULL);
Hirohito Higashie5c96e32025-06-16 19:39:24 +02001676 win_comp_pos(); // recompute window positions
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001677 p_ea = save_ea;
1678#ifdef FEAT_AUTOCHDIR
1679 p_acd = save_acd;
1680#endif
1681 unblock_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001682 curwin = auc_win;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001683 }
1684 curbuf = buf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001685 aco->new_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001686 set_bufref(&aco->new_curbuf, curbuf);
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001687
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001688 aco->save_VIsual_active = VIsual_active;
zeertzjq5717ee32025-05-25 16:59:50 +02001689 if (!same_buffer)
1690 // disable the Visual area, position may be invalid in another buffer
1691 VIsual_active = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001692}
1693
1694/*
1695 * Cleanup after executing autocommands for a (hidden) buffer.
1696 * Restore the window as it was (if possible).
1697 */
1698 void
1699aucmd_restbuf(
1700 aco_save_T *aco) // structure holding saved values
1701{
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001702 int dummy;
1703 win_T *save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001704
Bram Moolenaare76062c2022-11-28 18:51:43 +00001705 if (aco->use_aucmd_win_idx >= 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001706 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001707 win_T *awp = aucmd_win[aco->use_aucmd_win_idx].auc_win;
1708
Bram Moolenaare76062c2022-11-28 18:51:43 +00001709 // Find "awp", it can't be closed, but it may be in another tab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001710 // page. Do not trigger autocommands here.
1711 block_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001712 if (curwin != awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001713 {
1714 tabpage_T *tp;
1715 win_T *wp;
1716
1717 FOR_ALL_TAB_WINDOWS(tp, wp)
1718 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001719 if (wp == awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001720 {
1721 if (tp != curtab)
1722 goto_tabpage_tp(tp, TRUE, TRUE);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001723 win_goto(awp);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001724 goto win_found;
1725 }
1726 }
1727 }
1728win_found:
zeertzjq46bdae02023-09-24 23:16:08 +02001729 --curbuf->b_nwindows;
orbitalcde8de02023-04-02 22:05:13 +01001730#ifdef FEAT_JOB_CHANNEL
zeertzjqa40c0bc2023-05-27 14:10:08 +01001731 int save_stop_insert_mode = stop_insert_mode;
orbitalcde8de02023-04-02 22:05:13 +01001732 // May need to stop Insert mode if we were in a prompt buffer.
1733 leaving_window(curwin);
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001734 // Do not stop Insert mode when already in Insert mode before.
1735 if (aco->save_State & MODE_INSERT)
zeertzjqa40c0bc2023-05-27 14:10:08 +01001736 stop_insert_mode = save_stop_insert_mode;
orbitalcde8de02023-04-02 22:05:13 +01001737#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001738 // Remove the window and frame from the tree of frames.
Sean Dewar704966c2024-02-20 22:00:33 +01001739 (void)winframe_remove(curwin, &dummy, NULL, NULL);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001740 win_remove(curwin, NULL);
Bram Moolenaar84497cd2022-11-28 20:34:52 +00001741
1742 // The window is marked as not used, but it is not freed, it can be
1743 // used again.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001744 aucmd_win[aco->use_aucmd_win_idx].auc_win_used = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001745 last_status(FALSE); // may need to remove last status line
1746
1747 if (!valid_tabpage_win(curtab))
1748 // no valid window in current tabpage
1749 close_tabpage(curtab);
1750
1751 restore_snapshot(SNAP_AUCMD_IDX, FALSE);
Hirohito Higashie5c96e32025-06-16 19:39:24 +02001752 win_comp_pos(); // recompute window positions
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001753 unblock_autocmds();
1754
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001755 save_curwin = win_find_by_id(aco->save_curwin_id);
1756 if (save_curwin != NULL)
1757 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001758 else
1759 // Hmm, original window disappeared. Just use the first one.
1760 curwin = firstwin;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001761 curbuf = curwin->w_buffer;
1762#ifdef FEAT_JOB_CHANNEL
1763 // May need to restore insert mode for a prompt buffer.
1764 entering_window(curwin);
zeertzjqf2678472024-01-17 21:22:59 +01001765 if (bt_prompt(curbuf))
1766 curbuf->b_prompt_insert = aco->save_prompt_insert;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001767#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001768 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001769#ifdef FEAT_EVAL
Bram Moolenaare76062c2022-11-28 18:51:43 +00001770 vars_clear(&awp->w_vars->dv_hashtab); // free all w: variables
1771 hash_init(&awp->w_vars->dv_hashtab); // re-use the hashtab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001772#endif
zeertzjq9d956ee2024-04-07 18:16:10 +02001773 // If :lcd has been used in the autocommand window, correct current
1774 // directory before restoring tp_localdir and globaldir.
1775 if (awp->w_localdir != NULL)
1776 win_fix_current_dir();
1777 vim_free(curtab->tp_localdir);
1778 curtab->tp_localdir = aco->tp_localdir;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001779 vim_free(globaldir);
1780 globaldir = aco->globaldir;
1781
1782 // the buffer contents may have changed
zeertzjq49f05242023-02-04 10:58:34 +00001783 VIsual_active = aco->save_VIsual_active;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001784 check_cursor();
1785 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1786 {
1787 curwin->w_topline = curbuf->b_ml.ml_line_count;
1788#ifdef FEAT_DIFF
1789 curwin->w_topfill = 0;
1790#endif
1791 }
1792#if defined(FEAT_GUI)
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001793 if (gui.in_use)
1794 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001795 // Hide the scrollbars from the "awp" and update.
1796 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_LEFT], FALSE);
1797 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_RIGHT], FALSE);
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001798 gui_may_update_scrollbars();
1799 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001800#endif
1801 }
1802 else
1803 {
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001804 // Restore curwin. Use the window ID, a window may have been closed
1805 // and the memory re-used for another one.
1806 save_curwin = win_find_by_id(aco->save_curwin_id);
1807 if (save_curwin != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001808 {
1809 // Restore the buffer which was previously edited by curwin, if
1810 // it was changed, we are still the same window and the buffer is
1811 // valid.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001812 if (curwin->w_id == aco->new_curwin_id
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001813 && curbuf != aco->new_curbuf.br_buf
1814 && bufref_valid(&aco->new_curbuf)
1815 && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
1816 {
1817# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
1818 if (curwin->w_s == &curbuf->b_s)
1819 curwin->w_s = &aco->new_curbuf.br_buf->b_s;
1820# endif
1821 --curbuf->b_nwindows;
1822 curbuf = aco->new_curbuf.br_buf;
1823 curwin->w_buffer = curbuf;
1824 ++curbuf->b_nwindows;
1825 }
1826
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001827 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001828 curbuf = curwin->w_buffer;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001829 prevwin = win_find_by_id(aco->save_prevwin_id);
zeertzjq49f05242023-02-04 10:58:34 +00001830
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001831 // In case the autocommand moves the cursor to a position that
1832 // does not exist in curbuf.
zeertzjq49f05242023-02-04 10:58:34 +00001833 VIsual_active = aco->save_VIsual_active;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001834 check_cursor();
1835 }
1836 }
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001837
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001838 VIsual_active = aco->save_VIsual_active;
zeertzjq49f05242023-02-04 10:58:34 +00001839 check_cursor(); // just in case lines got deleted
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001840 if (VIsual_active)
1841 check_pos(curbuf, &VIsual);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001842}
1843
1844static int autocmd_nested = FALSE;
1845
1846/*
1847 * Execute autocommands for "event" and file name "fname".
1848 * Return TRUE if some commands were executed.
1849 */
1850 int
1851apply_autocmds(
1852 event_T event,
1853 char_u *fname, // NULL or empty means use actual file name
1854 char_u *fname_io, // fname to use for <afile> on cmdline
1855 int force, // when TRUE, ignore autocmd_busy
1856 buf_T *buf) // buffer for <abuf>
1857{
1858 return apply_autocmds_group(event, fname, fname_io, force,
1859 AUGROUP_ALL, buf, NULL);
1860}
1861
1862/*
1863 * Like apply_autocmds(), but with extra "eap" argument. This takes care of
1864 * setting v:filearg.
1865 */
1866 int
1867apply_autocmds_exarg(
1868 event_T event,
1869 char_u *fname,
1870 char_u *fname_io,
1871 int force,
1872 buf_T *buf,
1873 exarg_T *eap)
1874{
1875 return apply_autocmds_group(event, fname, fname_io, force,
1876 AUGROUP_ALL, buf, eap);
1877}
1878
1879/*
1880 * Like apply_autocmds(), but handles the caller's retval. If the script
1881 * processing is being aborted or if retval is FAIL when inside a try
1882 * conditional, no autocommands are executed. If otherwise the autocommands
1883 * cause the script to be aborted, retval is set to FAIL.
1884 */
1885 int
1886apply_autocmds_retval(
1887 event_T event,
1888 char_u *fname, // NULL or empty means use actual file name
1889 char_u *fname_io, // fname to use for <afile> on cmdline
1890 int force, // when TRUE, ignore autocmd_busy
1891 buf_T *buf, // buffer for <abuf>
1892 int *retval) // pointer to caller's retval
1893{
1894 int did_cmd;
1895
1896#ifdef FEAT_EVAL
1897 if (should_abort(*retval))
1898 return FALSE;
1899#endif
1900
1901 did_cmd = apply_autocmds_group(event, fname, fname_io, force,
1902 AUGROUP_ALL, buf, NULL);
1903 if (did_cmd
1904#ifdef FEAT_EVAL
1905 && aborting()
1906#endif
1907 )
1908 *retval = FAIL;
1909 return did_cmd;
1910}
1911
1912/*
1913 * Return TRUE when there is a CursorHold autocommand defined.
1914 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001915 static int
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001916has_cursorhold(void)
1917{
Bram Moolenaar24959102022-05-07 20:01:16 +01001918 return (first_autopat[(int)(get_real_state() == MODE_NORMAL_BUSY
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001919 ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
1920}
1921
1922/*
1923 * Return TRUE if the CursorHold event can be triggered.
1924 */
1925 int
1926trigger_cursorhold(void)
1927{
1928 int state;
1929
1930 if (!did_cursorhold
1931 && has_cursorhold()
1932 && reg_recording == 0
1933 && typebuf.tb_len == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001934 && !ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001935 {
1936 state = get_real_state();
Bram Moolenaar24959102022-05-07 20:01:16 +01001937 if (state == MODE_NORMAL_BUSY || (state & MODE_INSERT) != 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001938 return TRUE;
1939 }
1940 return FALSE;
1941}
1942
1943/*
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001944 * Return TRUE when there is a WinResized autocommand defined.
1945 */
1946 int
1947has_winresized(void)
1948{
1949 return (first_autopat[(int)EVENT_WINRESIZED] != NULL);
1950}
1951
1952/*
LemonBoy09371822022-04-08 15:18:45 +01001953 * Return TRUE when there is a WinScrolled autocommand defined.
1954 */
1955 int
1956has_winscrolled(void)
1957{
1958 return (first_autopat[(int)EVENT_WINSCROLLED] != NULL);
1959}
1960
1961/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001962 * Return TRUE when there is a CursorMoved autocommand defined.
1963 */
1964 int
1965has_cursormoved(void)
1966{
1967 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1968}
1969
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001970/*
1971 * Return TRUE when there is a CursorMovedI autocommand defined.
1972 */
1973 int
1974has_cursormovedI(void)
1975{
1976 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1977}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001978
1979/*
1980 * Return TRUE when there is a TextChanged autocommand defined.
1981 */
1982 int
1983has_textchanged(void)
1984{
1985 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1986}
1987
1988/*
1989 * Return TRUE when there is a TextChangedI autocommand defined.
1990 */
1991 int
1992has_textchangedI(void)
1993{
1994 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
1995}
1996
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001997/*
1998 * Return TRUE when there is a TextChangedP autocommand defined.
1999 */
2000 int
2001has_textchangedP(void)
2002{
2003 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
2004}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002005
2006/*
2007 * Return TRUE when there is an InsertCharPre autocommand defined.
2008 */
2009 int
2010has_insertcharpre(void)
2011{
2012 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
2013}
2014
2015/*
Shougo Matsushita83678842024-07-11 22:05:12 +02002016 * Return TRUE when there is an KeyInputPre autocommand defined.
2017 */
2018 int
2019has_keyinputpre(void)
2020{
2021 return (first_autopat[(int)EVENT_KEYINPUTPRE] != NULL);
2022}
2023
2024/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002025 * Return TRUE when there is an CmdUndefined autocommand defined.
2026 */
2027 int
2028has_cmdundefined(void)
2029{
2030 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
2031}
2032
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002033#if defined(FEAT_EVAL) || defined(PROTO)
2034/*
2035 * Return TRUE when there is a TextYankPost autocommand defined.
2036 */
2037 int
2038has_textyankpost(void)
2039{
2040 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
2041}
2042#endif
2043
Bram Moolenaard7f246c2019-04-08 18:15:41 +02002044#if defined(FEAT_EVAL) || defined(PROTO)
2045/*
2046 * Return TRUE when there is a CompleteChanged autocommand defined.
2047 */
2048 int
2049has_completechanged(void)
2050{
2051 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
2052}
2053#endif
2054
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002055#if defined(FEAT_EVAL) || defined(PROTO)
2056/*
2057 * Return TRUE when there is a ModeChanged autocommand defined.
2058 */
2059 int
2060has_modechanged(void)
2061{
2062 return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
2063}
2064#endif
2065
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002066/*
2067 * Execute autocommands for "event" and file name "fname".
2068 * Return TRUE if some commands were executed.
2069 */
2070 static int
2071apply_autocmds_group(
2072 event_T event,
2073 char_u *fname, // NULL or empty means use actual file name
2074 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
2075 // use fname
2076 int force, // when TRUE, ignore autocmd_busy
2077 int group, // group ID, or AUGROUP_ALL
2078 buf_T *buf, // buffer for <abuf>
2079 exarg_T *eap UNUSED) // command arguments
2080{
2081 char_u *sfname = NULL; // short file name
2082 char_u *tail;
2083 int save_changed;
2084 buf_T *old_curbuf;
2085 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002086 char_u *save_autocmd_fname;
2087 int save_autocmd_fname_full;
2088 int save_autocmd_bufnr;
2089 char_u *save_autocmd_match;
2090 int save_autocmd_busy;
2091 int save_autocmd_nested;
2092 static int nesting = 0;
LemonBoyeca7c602022-04-14 15:39:43 +01002093 AutoPatCmd_T patcmd;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002094 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002095 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002096#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002097 funccal_entry_T funccal_entry;
2098 char_u *save_cmdarg;
2099 long save_cmdbang;
2100#endif
2101 static int filechangeshell_busy = FALSE;
2102#ifdef FEAT_PROFILE
2103 proftime_T wait_time;
2104#endif
2105 int did_save_redobuff = FALSE;
2106 save_redo_T save_redo;
2107 int save_KeyTyped = KeyTyped;
ichizok7e5fe382023-04-15 13:17:50 +01002108 ESTACK_CHECK_DECLARATION;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002109
2110 /*
2111 * Quickly return if there are no autocommands for this event or
2112 * autocommands are blocked.
2113 */
2114 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
2115 || autocmd_blocked > 0)
2116 goto BYPASS_AU;
2117
2118 /*
2119 * When autocommands are busy, new autocommands are only executed when
2120 * explicitly enabled with the "nested" flag.
2121 */
2122 if (autocmd_busy && !(force || autocmd_nested))
2123 goto BYPASS_AU;
2124
2125#ifdef FEAT_EVAL
2126 /*
2127 * Quickly return when immediately aborting on error, or when an interrupt
2128 * occurred or an exception was thrown but not caught.
2129 */
2130 if (aborting())
2131 goto BYPASS_AU;
2132#endif
2133
2134 /*
2135 * FileChangedShell never nests, because it can create an endless loop.
2136 */
2137 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
2138 || event == EVENT_FILECHANGEDSHELLPOST))
2139 goto BYPASS_AU;
2140
2141 /*
2142 * Ignore events in 'eventignore'.
2143 */
Luuk van Baalb7147f82025-02-08 18:52:39 +01002144 if (event_ignored(event, p_ei))
2145 goto BYPASS_AU;
2146
Luuk van Baalb7147f82025-02-08 18:52:39 +01002147 int win_ignore = FALSE;
2148 // If event is allowed in 'eventignorewin', check if curwin or all windows
2149 // into "buf" are ignoring the event.
2150 if (buf == curbuf && event_tab[event].key <= 0)
2151 win_ignore = event_ignored(event, curwin->w_p_eiw);
Sean Deward4110e02025-05-11 13:45:21 +02002152 else if (buf != NULL && event_tab[event].key <= 0 && buf->b_nwindows > 0)
2153 {
2154 tabpage_T *tp;
2155 win_T *wp;
2156
2157 win_ignore = TRUE;
2158 FOR_ALL_TAB_WINDOWS(tp, wp)
2159 if (wp->w_buffer == buf && !event_ignored(event, wp->w_p_eiw))
2160 {
2161 win_ignore = FALSE;
2162 break;
2163 }
2164 }
Luuk van Baalb7147f82025-02-08 18:52:39 +01002165 if (win_ignore)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002166 goto BYPASS_AU;
2167
2168 /*
2169 * Allow nesting of autocommands, but restrict the depth, because it's
2170 * possible to create an endless loop.
2171 */
2172 if (nesting == 10)
2173 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00002174 emsg(_(e_autocommand_nesting_too_deep));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002175 goto BYPASS_AU;
2176 }
2177
2178 /*
2179 * Check if these autocommands are disabled. Used when doing ":all" or
2180 * ":ball".
2181 */
2182 if ( (autocmd_no_enter
2183 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
2184 || (autocmd_no_leave
2185 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
2186 goto BYPASS_AU;
2187
Bram Moolenaarbb393d82022-12-09 12:21:50 +00002188 if (event == EVENT_CMDLINECHANGED)
2189 ++aucmd_cmdline_changed_count;
2190
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002191 /*
2192 * Save the autocmd_* variables and info about the current buffer.
2193 */
2194 save_autocmd_fname = autocmd_fname;
2195 save_autocmd_fname_full = autocmd_fname_full;
2196 save_autocmd_bufnr = autocmd_bufnr;
2197 save_autocmd_match = autocmd_match;
2198 save_autocmd_busy = autocmd_busy;
2199 save_autocmd_nested = autocmd_nested;
2200 save_changed = curbuf->b_changed;
2201 old_curbuf = curbuf;
2202
2203 /*
2204 * Set the file name to be used for <afile>.
2205 * Make a copy to avoid that changing a buffer name or directory makes it
2206 * invalid.
2207 */
2208 if (fname_io == NULL)
2209 {
2210 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
Bram Moolenaarbb393d82022-12-09 12:21:50 +00002211 || event == EVENT_OPTIONSET
Danek Duvalld7d56032024-01-14 20:19:59 +01002212 || event == EVENT_MODECHANGED
2213 || event == EVENT_TERMRESPONSEALL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002214 autocmd_fname = NULL;
2215 else if (fname != NULL && !ends_excmd(*fname))
2216 autocmd_fname = fname;
2217 else if (buf != NULL)
2218 autocmd_fname = buf->b_ffname;
2219 else
2220 autocmd_fname = NULL;
2221 }
2222 else
2223 autocmd_fname = fname_io;
2224 if (autocmd_fname != NULL)
2225 autocmd_fname = vim_strsave(autocmd_fname);
2226 autocmd_fname_full = FALSE; // call FullName_save() later
2227
2228 /*
2229 * Set the buffer number to be used for <abuf>.
2230 */
2231 if (buf == NULL)
2232 autocmd_bufnr = 0;
2233 else
2234 autocmd_bufnr = buf->b_fnum;
2235
2236 /*
2237 * When the file name is NULL or empty, use the file name of buffer "buf".
2238 * Always use the full path of the file name to match with, in case
2239 * "allow_dirs" is set.
2240 */
2241 if (fname == NULL || *fname == NUL)
2242 {
2243 if (buf == NULL)
2244 fname = NULL;
2245 else
2246 {
2247#ifdef FEAT_SYN_HL
2248 if (event == EVENT_SYNTAX)
2249 fname = buf->b_p_syn;
2250 else
2251#endif
2252 if (event == EVENT_FILETYPE)
2253 fname = buf->b_p_ft;
2254 else
2255 {
2256 if (buf->b_sfname != NULL)
2257 sfname = vim_strsave(buf->b_sfname);
2258 fname = buf->b_ffname;
2259 }
2260 }
2261 if (fname == NULL)
2262 fname = (char_u *)"";
2263 fname = vim_strsave(fname); // make a copy, so we can change it
2264 }
2265 else
2266 {
2267 sfname = vim_strsave(fname);
2268 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002269 // ColorScheme, QuickFixCmd*, DirChanged and similar.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002270 if (event == EVENT_FILETYPE
2271 || event == EVENT_SYNTAX
2272 || event == EVENT_CMDLINECHANGED
2273 || event == EVENT_CMDLINEENTER
Girish Palya92f68e22025-04-21 11:12:41 +02002274 || event == EVENT_CMDLINELEAVEPRE
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002275 || event == EVENT_CMDLINELEAVE
Shougo Matsushitad0952142024-06-20 22:05:16 +02002276 || event == EVENT_CURSORMOVEDC
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002277 || event == EVENT_CMDWINENTER
2278 || event == EVENT_CMDWINLEAVE
2279 || event == EVENT_CMDUNDEFINED
2280 || event == EVENT_FUNCUNDEFINED
Shougo Matsushita83678842024-07-11 22:05:12 +02002281 || event == EVENT_KEYINPUTPRE
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002282 || event == EVENT_REMOTEREPLY
2283 || event == EVENT_SPELLFILEMISSING
2284 || event == EVENT_QUICKFIXCMDPRE
2285 || event == EVENT_COLORSCHEME
2286 || event == EVENT_COLORSCHEMEPRE
2287 || event == EVENT_OPTIONSET
2288 || event == EVENT_QUICKFIXCMDPOST
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002289 || event == EVENT_DIRCHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002290 || event == EVENT_DIRCHANGEDPRE
naohiro ono23beefe2021-11-13 12:38:49 +00002291 || event == EVENT_MODECHANGED
zeertzjqc601d982022-10-10 13:46:15 +01002292 || event == EVENT_MENUPOPUP
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002293 || event == EVENT_USER
LemonBoy09371822022-04-08 15:18:45 +01002294 || event == EVENT_WINCLOSED
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00002295 || event == EVENT_WINRESIZED
Danek Duvalld7d56032024-01-14 20:19:59 +01002296 || event == EVENT_WINSCROLLED
2297 || event == EVENT_TERMRESPONSEALL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002298 {
2299 fname = vim_strsave(fname);
2300 autocmd_fname_full = TRUE; // don't expand it later
2301 }
2302 else
2303 fname = FullName_save(fname, FALSE);
2304 }
2305 if (fname == NULL) // out of memory
2306 {
2307 vim_free(sfname);
2308 retval = FALSE;
2309 goto BYPASS_AU;
2310 }
2311
2312#ifdef BACKSLASH_IN_FILENAME
2313 /*
2314 * Replace all backslashes with forward slashes. This makes the
2315 * autocommand patterns portable between Unix and MS-DOS.
2316 */
2317 if (sfname != NULL)
2318 forward_slash(sfname);
2319 forward_slash(fname);
2320#endif
2321
2322#ifdef VMS
2323 // remove version for correct match
2324 if (sfname != NULL)
2325 vms_remove_version(sfname);
2326 vms_remove_version(fname);
2327#endif
2328
2329 /*
2330 * Set the name to be used for <amatch>.
2331 */
2332 autocmd_match = fname;
2333
2334
2335 // Don't redraw while doing autocommands.
2336 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002337
2338 // name and lnum are filled in later
2339 estack_push(ETYPE_AUCMD, NULL, 0);
ichizok7e5fe382023-04-15 13:17:50 +01002340 ESTACK_CHECK_SETUP;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002341
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002342 save_current_sctx = current_sctx;
2343
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002344#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002345# ifdef FEAT_PROFILE
2346 if (do_profiling == PROF_YES)
2347 prof_child_enter(&wait_time); // doesn't count for the caller itself
2348# endif
2349
2350 // Don't use local function variables, if called from a function.
2351 save_funccal(&funccal_entry);
2352#endif
2353
2354 /*
2355 * When starting to execute autocommands, save the search patterns.
2356 */
2357 if (!autocmd_busy)
2358 {
2359 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002360 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002361 {
2362 saveRedobuff(&save_redo);
2363 did_save_redobuff = TRUE;
2364 }
zeertzjq5bf6c212024-03-31 18:41:27 +02002365 curbuf->b_did_filetype = curbuf->b_keep_filetype;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002366 }
2367
2368 /*
2369 * Note that we are applying autocmds. Some commands need to know.
2370 */
2371 autocmd_busy = TRUE;
2372 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2373 ++nesting; // see matching decrement below
2374
2375 // Remember that FileType was triggered. Used for did_filetype().
2376 if (event == EVENT_FILETYPE)
zeertzjq5bf6c212024-03-31 18:41:27 +02002377 curbuf->b_did_filetype = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002378
2379 tail = gettail(fname);
2380
2381 // Find first autocommand that matches
LemonBoyeca7c602022-04-14 15:39:43 +01002382 CLEAR_FIELD(patcmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002383 patcmd.curpat = first_autopat[(int)event];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002384 patcmd.group = group;
2385 patcmd.fname = fname;
2386 patcmd.sfname = sfname;
2387 patcmd.tail = tail;
2388 patcmd.event = event;
2389 patcmd.arg_bufnr = autocmd_bufnr;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002390 auto_next_pat(&patcmd, FALSE);
2391
2392 // found one, start executing the autocommands
2393 if (patcmd.curpat != NULL)
2394 {
2395 // add to active_apc_list
2396 patcmd.next = active_apc_list;
2397 active_apc_list = &patcmd;
2398
2399#ifdef FEAT_EVAL
2400 // set v:cmdarg (only when there is a matching pattern)
2401 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2402 if (eap != NULL)
2403 {
2404 save_cmdarg = set_cmdarg(eap, NULL);
2405 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2406 }
2407 else
2408 save_cmdarg = NULL; // avoid gcc warning
2409#endif
2410 retval = TRUE;
2411 // mark the last pattern, to avoid an endless loop when more patterns
2412 // are added when executing autocommands
2413 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2414 ap->last = FALSE;
2415 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002416
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002417 // Make sure cursor and topline are valid. The first time the current
2418 // values are saved, restored by reset_lnums(). When nested only the
2419 // values are corrected when needed.
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002420 if (nesting == 1)
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002421 check_lnums(TRUE);
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002422 else
2423 check_lnums_nested(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002424
Christian Brabandt590aae32023-06-25 22:34:22 +01002425 int save_did_emsg = did_emsg;
2426 int save_ex_pressedreturn = get_pressedreturn();
ichizokc3f91c02021-12-17 09:44:33 +00002427
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002428 do_cmdline(NULL, getnextac, (void *)&patcmd,
2429 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002430
ichizokc3f91c02021-12-17 09:44:33 +00002431 did_emsg += save_did_emsg;
Christian Brabandt590aae32023-06-25 22:34:22 +01002432 set_pressedreturn(save_ex_pressedreturn);
ichizokc3f91c02021-12-17 09:44:33 +00002433
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002434 if (nesting == 1)
2435 // restore cursor and topline, unless they were changed
2436 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002437
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002438#ifdef FEAT_EVAL
2439 if (eap != NULL)
2440 {
2441 (void)set_cmdarg(NULL, save_cmdarg);
2442 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2443 }
2444#endif
2445 // delete from active_apc_list
2446 if (active_apc_list == &patcmd) // just in case
2447 active_apc_list = patcmd.next;
2448 }
2449
Bram Moolenaar79cdf022023-05-20 14:07:00 +01002450 if (RedrawingDisabled > 0)
2451 --RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002452 autocmd_busy = save_autocmd_busy;
2453 filechangeshell_busy = FALSE;
2454 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002455 vim_free(SOURCING_NAME);
ichizok7e5fe382023-04-15 13:17:50 +01002456 ESTACK_CHECK_NOW;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002457 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002458 vim_free(autocmd_fname);
2459 autocmd_fname = save_autocmd_fname;
2460 autocmd_fname_full = save_autocmd_fname_full;
2461 autocmd_bufnr = save_autocmd_bufnr;
2462 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002463 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002464#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002465 restore_funccal();
2466# ifdef FEAT_PROFILE
2467 if (do_profiling == PROF_YES)
2468 prof_child_exit(&wait_time);
2469# endif
2470#endif
2471 KeyTyped = save_KeyTyped;
2472 vim_free(fname);
2473 vim_free(sfname);
2474 --nesting; // see matching increment above
2475
2476 /*
2477 * When stopping to execute autocommands, restore the search patterns and
2478 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2479 * free any windows in the au_pending_free_win list.
2480 */
2481 if (!autocmd_busy)
2482 {
2483 restore_search_patterns();
2484 if (did_save_redobuff)
2485 restoreRedobuff(&save_redo);
zeertzjq5bf6c212024-03-31 18:41:27 +02002486 curbuf->b_did_filetype = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002487 while (au_pending_free_buf != NULL)
2488 {
2489 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002490
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002491 vim_free(au_pending_free_buf);
2492 au_pending_free_buf = b;
2493 }
2494 while (au_pending_free_win != NULL)
2495 {
2496 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002497
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002498 vim_free(au_pending_free_win);
2499 au_pending_free_win = w;
2500 }
2501 }
2502
2503 /*
2504 * Some events don't set or reset the Changed flag.
2505 * Check if still in the same buffer!
2506 */
2507 if (curbuf == old_curbuf
2508 && (event == EVENT_BUFREADPOST
2509 || event == EVENT_BUFWRITEPOST
2510 || event == EVENT_FILEAPPENDPOST
2511 || event == EVENT_VIMLEAVE
2512 || event == EVENT_VIMLEAVEPRE))
2513 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002514 if (curbuf->b_changed != save_changed)
2515 need_maketitle = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002516 curbuf->b_changed = save_changed;
2517 }
2518
2519 au_cleanup(); // may really delete removed patterns/commands now
2520
2521BYPASS_AU:
2522 // When wiping out a buffer make sure all its buffer-local autocommands
2523 // are deleted.
2524 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2525 aubuflocal_remove(buf);
2526
2527 if (retval == OK && event == EVENT_FILETYPE)
zeertzjq5bf6c212024-03-31 18:41:27 +02002528 curbuf->b_au_did_filetype = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002529
2530 return retval;
2531}
2532
2533# ifdef FEAT_EVAL
2534static char_u *old_termresponse = NULL;
Danek Duvalld7d56032024-01-14 20:19:59 +01002535static char_u *old_termu7resp = NULL;
2536static char_u *old_termblinkresp = NULL;
2537static char_u *old_termrbgresp = NULL;
2538static char_u *old_termrfgresp = NULL;
2539static char_u *old_termstyleresp = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002540# endif
2541
2542/*
2543 * Block triggering autocommands until unblock_autocmd() is called.
2544 * Can be used recursively, so long as it's symmetric.
2545 */
2546 void
2547block_autocmds(void)
2548{
2549# ifdef FEAT_EVAL
2550 // Remember the value of v:termresponse.
2551 if (autocmd_blocked == 0)
Danek Duvalld7d56032024-01-14 20:19:59 +01002552 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002553 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
Danek Duvalld7d56032024-01-14 20:19:59 +01002554 old_termu7resp = get_vim_var_str(VV_TERMU7RESP);
2555 old_termblinkresp = get_vim_var_str(VV_TERMBLINKRESP);
2556 old_termrbgresp = get_vim_var_str(VV_TERMRBGRESP);
2557 old_termrfgresp = get_vim_var_str(VV_TERMRFGRESP);
2558 old_termstyleresp = get_vim_var_str(VV_TERMSTYLERESP);
2559 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002560# endif
2561 ++autocmd_blocked;
2562}
2563
2564 void
2565unblock_autocmds(void)
2566{
2567 --autocmd_blocked;
2568
2569# ifdef FEAT_EVAL
Danek Duvalld7d56032024-01-14 20:19:59 +01002570 // When v:termresponse, etc, were set while autocommands were blocked,
2571 // trigger the autocommands now. Esp. useful when executing a shell
2572 // command during startup (vimdiff).
2573 if (autocmd_blocked == 0)
2574 {
2575 if (get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2576 {
2577 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2578 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"version", NULL, FALSE, curbuf);
2579 }
2580 if (get_vim_var_str(VV_TERMU7RESP) != old_termu7resp)
2581 {
2582 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"ambiguouswidth", NULL, FALSE, curbuf);
2583 }
2584 if (get_vim_var_str(VV_TERMBLINKRESP) != old_termblinkresp)
2585 {
2586 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"cursorblink", NULL, FALSE, curbuf);
2587 }
2588 if (get_vim_var_str(VV_TERMRBGRESP) != old_termrbgresp)
2589 {
2590 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"background", NULL, FALSE, curbuf);
2591 }
2592 if (get_vim_var_str(VV_TERMRFGRESP) != old_termrfgresp)
2593 {
2594 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"foreground", NULL, FALSE, curbuf);
2595 }
2596 if (get_vim_var_str(VV_TERMSTYLERESP) != old_termstyleresp)
2597 {
2598 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"cursorshape", NULL, FALSE, curbuf);
2599 }
2600 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002601# endif
2602}
2603
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002604 int
2605is_autocmd_blocked(void)
2606{
2607 return autocmd_blocked != 0;
2608}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002609
2610/*
2611 * Find next autocommand pattern that matches.
2612 */
2613 static void
2614auto_next_pat(
LemonBoyeca7c602022-04-14 15:39:43 +01002615 AutoPatCmd_T *apc,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002616 int stop_at_last) // stop when 'last' flag is set
2617{
2618 AutoPat *ap;
2619 AutoCmd *cp;
2620 char_u *name;
2621 char *s;
LemonBoyeca7c602022-04-14 15:39:43 +01002622 estack_T *entry;
2623 char_u *namep;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002624
LemonBoyeca7c602022-04-14 15:39:43 +01002625 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
2626
2627 // Clear the exestack entry for this ETYPE_AUCMD entry.
2628 VIM_CLEAR(entry->es_name);
2629 entry->es_info.aucmd = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002630
2631 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2632 {
2633 apc->curpat = NULL;
2634
2635 // Only use a pattern when it has not been removed, has commands and
2636 // the group matches. For buffer-local autocommands only check the
2637 // buffer number.
2638 if (ap->pat != NULL && ap->cmds != NULL
2639 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2640 {
2641 // execution-condition
2642 if (ap->buflocal_nr == 0
2643 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2644 apc->sfname, apc->tail, ap->allow_dirs))
2645 : ap->buflocal_nr == apc->arg_bufnr)
2646 {
2647 name = event_nr2name(apc->event);
2648 s = _("%s Autocommands for \"%s\"");
LemonBoyeca7c602022-04-14 15:39:43 +01002649 namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1);
2650 if (namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002651 {
LemonBoyeca7c602022-04-14 15:39:43 +01002652 sprintf((char *)namep, s, (char *)name, (char *)ap->pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002653 if (p_verbose >= 8)
2654 {
2655 verbose_enter();
LemonBoyeca7c602022-04-14 15:39:43 +01002656 smsg(_("Executing %s"), namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002657 verbose_leave();
2658 }
2659 }
2660
LemonBoyeca7c602022-04-14 15:39:43 +01002661 // Update the exestack entry for this autocmd.
2662 entry->es_name = namep;
2663 entry->es_info.aucmd = apc;
2664
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002665 apc->curpat = ap;
2666 apc->nextcmd = ap->cmds;
2667 // mark last command
2668 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2669 cp->last = FALSE;
2670 cp->last = TRUE;
2671 }
2672 line_breakcheck();
2673 if (apc->curpat != NULL) // found a match
2674 break;
2675 }
2676 if (stop_at_last && ap->last)
2677 break;
2678 }
2679}
2680
Dominique Pellee764d1b2023-03-12 21:20:59 +00002681#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002682/*
LemonBoyeca7c602022-04-14 15:39:43 +01002683 * Get the script context where autocommand "acp" is defined.
2684 */
2685 sctx_T *
2686acp_script_ctx(AutoPatCmd_T *acp)
2687{
2688 return &acp->script_ctx;
2689}
Dominique Pellee764d1b2023-03-12 21:20:59 +00002690#endif
LemonBoyeca7c602022-04-14 15:39:43 +01002691
2692/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002693 * Get next autocommand command.
2694 * Called by do_cmdline() to get the next line for ":if".
2695 * Returns allocated string, or NULL for end of autocommands.
2696 */
2697 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002698getnextac(
2699 int c UNUSED,
2700 void *cookie,
2701 int indent UNUSED,
2702 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002703{
LemonBoyeca7c602022-04-14 15:39:43 +01002704 AutoPatCmd_T *acp = (AutoPatCmd_T *)cookie;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002705 char_u *retval;
2706 AutoCmd *ac;
2707
2708 // Can be called again after returning the last line.
2709 if (acp->curpat == NULL)
2710 return NULL;
2711
2712 // repeat until we find an autocommand to execute
2713 for (;;)
2714 {
2715 // skip removed commands
2716 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2717 if (acp->nextcmd->last)
2718 acp->nextcmd = NULL;
2719 else
2720 acp->nextcmd = acp->nextcmd->next;
2721
2722 if (acp->nextcmd != NULL)
2723 break;
2724
2725 // at end of commands, find next pattern that matches
2726 if (acp->curpat->last)
2727 acp->curpat = NULL;
2728 else
2729 acp->curpat = acp->curpat->next;
2730 if (acp->curpat != NULL)
2731 auto_next_pat(acp, TRUE);
2732 if (acp->curpat == NULL)
2733 return NULL;
2734 }
2735
2736 ac = acp->nextcmd;
2737
2738 if (p_verbose >= 9)
2739 {
2740 verbose_enter_scroll();
2741 smsg(_("autocommand %s"), ac->cmd);
2742 msg_puts("\n"); // don't overwrite this either
2743 verbose_leave_scroll();
2744 }
2745 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002746 // Remove one-shot ("once") autocmd in anticipation of its execution.
2747 if (ac->once)
2748 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002749 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002750 current_sctx = ac->script_ctx;
LemonBoyeca7c602022-04-14 15:39:43 +01002751 acp->script_ctx = current_sctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002752 if (ac->last)
2753 acp->nextcmd = NULL;
2754 else
2755 acp->nextcmd = ac->next;
2756 return retval;
2757}
2758
2759/*
2760 * Return TRUE if there is a matching autocommand for "fname".
2761 * To account for buffer-local autocommands, function needs to know
2762 * in which buffer the file will be opened.
2763 */
2764 int
2765has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2766{
2767 AutoPat *ap;
2768 char_u *fname;
2769 char_u *tail = gettail(sfname);
2770 int retval = FALSE;
2771
2772 fname = FullName_save(sfname, FALSE);
2773 if (fname == NULL)
2774 return FALSE;
2775
2776#ifdef BACKSLASH_IN_FILENAME
2777 /*
2778 * Replace all backslashes with forward slashes. This makes the
2779 * autocommand patterns portable between Unix and MS-DOS.
2780 */
2781 sfname = vim_strsave(sfname);
2782 if (sfname != NULL)
2783 forward_slash(sfname);
2784 forward_slash(fname);
2785#endif
2786
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002787 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002788 if (ap->pat != NULL && ap->cmds != NULL
2789 && (ap->buflocal_nr == 0
2790 ? match_file_pat(NULL, &ap->reg_prog,
2791 fname, sfname, tail, ap->allow_dirs)
2792 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2793 ))
2794 {
2795 retval = TRUE;
2796 break;
2797 }
2798
2799 vim_free(fname);
2800#ifdef BACKSLASH_IN_FILENAME
2801 vim_free(sfname);
2802#endif
2803
2804 return retval;
2805}
2806
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002807/*
2808 * Function given to ExpandGeneric() to obtain the list of autocommand group
2809 * names.
2810 */
2811 char_u *
2812get_augroup_name(expand_T *xp UNUSED, int idx)
2813{
2814 if (idx == augroups.ga_len) // add "END" add the end
2815 return (char_u *)"END";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002816 if (idx < 0 || idx >= augroups.ga_len) // end of list
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002817 return NULL;
2818 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2819 // skip deleted entries
2820 return (char_u *)"";
2821 return AUGROUP_NAME(idx); // return a name
2822}
2823
2824static int include_groups = FALSE;
2825
2826 char_u *
2827set_context_in_autocmd(
2828 expand_T *xp,
2829 char_u *arg,
2830 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2831{
2832 char_u *p;
2833 int group;
2834
2835 // check for a group name, skip it if present
2836 include_groups = FALSE;
2837 p = arg;
2838 group = au_get_grouparg(&arg);
2839 if (group == AUGROUP_ERROR)
2840 return NULL;
2841 // If there only is a group name that's what we expand.
2842 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2843 {
2844 arg = p;
2845 group = AUGROUP_ALL;
2846 }
2847
2848 // skip over event name
2849 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2850 if (*p == ',')
2851 arg = p + 1;
2852 if (*p == NUL)
2853 {
2854 if (group == AUGROUP_ALL)
2855 include_groups = TRUE;
2856 xp->xp_context = EXPAND_EVENTS; // expand event name
2857 xp->xp_pattern = arg;
2858 return NULL;
2859 }
2860
2861 // skip over pattern
2862 arg = skipwhite(p);
2863 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2864 arg++;
2865 if (*arg)
2866 return arg; // expand (next) command
2867
2868 if (doautocmd)
2869 xp->xp_context = EXPAND_FILES; // expand file names
2870 else
2871 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2872 return NULL;
2873}
2874
2875/*
2876 * Function given to ExpandGeneric() to obtain the list of event names.
2877 */
2878 char_u *
2879get_event_name(expand_T *xp UNUSED, int idx)
2880{
John Marriott78d742a2024-04-02 20:26:01 +02002881 int i;
2882
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002883 if (idx < augroups.ga_len) // First list group names, if wanted
2884 {
2885 if (!include_groups || AUGROUP_NAME(idx) == NULL
2886 || AUGROUP_NAME(idx) == get_deleted_augroup())
2887 return (char_u *)""; // skip deleted entries
2888 return AUGROUP_NAME(idx); // return a name
2889 }
John Marriott78d742a2024-04-02 20:26:01 +02002890
2891 i = idx - augroups.ga_len;
Luuk van Baalb7147f82025-02-08 18:52:39 +01002892 if (i < 0 || i >= NUM_EVENTS)
John Marriott78d742a2024-04-02 20:26:01 +02002893 return NULL;
2894
John Marriott8d4477e2024-11-02 15:59:01 +01002895 return event_tab[i].value.string;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002896}
2897
Yee Cheng Chin900894b2023-09-29 20:42:32 +02002898/*
2899 * Function given to ExpandGeneric() to obtain the list of event names. Don't
2900 * include groups.
2901 */
2902 char_u *
Luuk van Baalb7147f82025-02-08 18:52:39 +01002903get_event_name_no_group(expand_T *xp UNUSED, int idx, int win)
Yee Cheng Chin900894b2023-09-29 20:42:32 +02002904{
Luuk van Baalb7147f82025-02-08 18:52:39 +01002905 if (idx < 0 || idx >= NUM_EVENTS)
John Marriott78d742a2024-04-02 20:26:01 +02002906 return NULL;
2907
Luuk van Baalb7147f82025-02-08 18:52:39 +01002908 if (!win)
2909 return event_tab[idx].value.string;
2910
2911 // Need to check subset of allowed values for 'eventignorewin'.
2912 int j = 0;
2913 for (int i = 0; i < NUM_EVENTS; ++i)
2914 {
2915 j += event_tab[i].key <= 0;
2916 if (j == idx + 1)
2917 return event_tab[i].value.string;
2918 }
2919
2920 return NULL;
Yee Cheng Chin900894b2023-09-29 20:42:32 +02002921}
2922
Jim Zhou5606ca52025-03-13 21:58:25 +01002923/*
2924 * Return TRUE when there is a TabClosedPre autocommand defined.
2925 */
2926 int
2927has_tabclosedpre(void)
2928{
2929 return (first_autopat[(int)EVENT_TABCLOSEDPRE] != NULL);
2930}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002931
2932#if defined(FEAT_EVAL) || defined(PROTO)
2933/*
2934 * Return TRUE if autocmd is supported.
2935 */
2936 int
2937autocmd_supported(char_u *name)
2938{
2939 char_u *p;
2940
2941 return (event_name2nr(name, &p) != NUM_EVENTS);
2942}
2943
2944/*
2945 * Return TRUE if an autocommand is defined for a group, event and
2946 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2947 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2948 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2949 * Used for:
2950 * exists("#Group") or
2951 * exists("#Group#Event") or
2952 * exists("#Group#Event#pat") or
2953 * exists("#Event") or
2954 * exists("#Event#pat")
2955 */
2956 int
2957au_exists(char_u *arg)
2958{
2959 char_u *arg_save;
2960 char_u *pattern = NULL;
2961 char_u *event_name;
2962 char_u *p;
2963 event_T event;
2964 AutoPat *ap;
2965 buf_T *buflocal_buf = NULL;
2966 int group;
2967 int retval = FALSE;
2968
2969 // Make a copy so that we can change the '#' chars to a NUL.
2970 arg_save = vim_strsave(arg);
2971 if (arg_save == NULL)
2972 return FALSE;
2973 p = vim_strchr(arg_save, '#');
2974 if (p != NULL)
2975 *p++ = NUL;
2976
2977 // First, look for an autocmd group name
2978 group = au_find_group(arg_save);
2979 if (group == AUGROUP_ERROR)
2980 {
2981 // Didn't match a group name, assume the first argument is an event.
2982 group = AUGROUP_ALL;
2983 event_name = arg_save;
2984 }
2985 else
2986 {
2987 if (p == NULL)
2988 {
2989 // "Group": group name is present and it's recognized
2990 retval = TRUE;
2991 goto theend;
2992 }
2993
2994 // Must be "Group#Event" or "Group#Event#pat".
2995 event_name = p;
2996 p = vim_strchr(event_name, '#');
2997 if (p != NULL)
2998 *p++ = NUL; // "Group#Event#pat"
2999 }
3000
3001 pattern = p; // "pattern" is NULL when there is no pattern
3002
3003 // find the index (enum) for the event name
3004 event = event_name2nr(event_name, &p);
3005
3006 // return FALSE if the event name is not recognized
3007 if (event == NUM_EVENTS)
3008 goto theend;
3009
3010 // Find the first autocommand for this event.
3011 // If there isn't any, return FALSE;
3012 // If there is one and no pattern given, return TRUE;
3013 ap = first_autopat[(int)event];
3014 if (ap == NULL)
3015 goto theend;
3016
3017 // if pattern is "<buffer>", special handling is needed which uses curbuf
3018 // for pattern "<buffer=N>, fnamecmp() will work fine
3019 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
3020 buflocal_buf = curbuf;
3021
3022 // Check if there is an autocommand with the given pattern.
3023 for ( ; ap != NULL; ap = ap->next)
3024 // only use a pattern when it has not been removed and has commands.
3025 // For buffer-local autocommands, fnamecmp() works fine.
3026 if (ap->pat != NULL && ap->cmds != NULL
3027 && (group == AUGROUP_ALL || ap->group == group)
3028 && (pattern == NULL
3029 || (buflocal_buf == NULL
3030 ? fnamecmp(ap->pat, pattern) == 0
3031 : ap->buflocal_nr == buflocal_buf->b_fnum)))
3032 {
3033 retval = TRUE;
3034 break;
3035 }
3036
3037theend:
3038 vim_free(arg_save);
3039 return retval;
3040}
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003041
3042/*
3043 * autocmd_add() and autocmd_delete() functions
3044 */
3045 static void
3046autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
3047{
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003048 list_T *aucmd_list;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003049 listitem_T *li;
3050 dict_T *event_dict;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003051 dictitem_T *di;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003052 char_u *event_name = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003053 list_T *event_list;
3054 listitem_T *eli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003055 event_T event;
3056 char_u *group_name = NULL;
3057 int group;
3058 char_u *pat = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003059 list_T *pat_list;
3060 listitem_T *pli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003061 char_u *cmd = NULL;
3062 char_u *end;
3063 int once;
3064 int nested;
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01003065 int replace; // replace the cmd for a group/event
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003066 int retval = VVAL_TRUE;
3067 int save_augroup = current_augroup;
3068
3069 rettv->v_type = VAR_BOOL;
3070 rettv->vval.v_number = VVAL_FALSE;
3071
3072 if (check_for_list_arg(argvars, 0) == FAIL)
3073 return;
3074
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003075 aucmd_list = argvars[0].vval.v_list;
3076 if (aucmd_list == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003077 return;
3078
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003079 FOR_ALL_LIST_ITEMS(aucmd_list, li)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003080 {
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003081 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003082 VIM_CLEAR(cmd);
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003083 event_name = NULL;
3084 event_list = NULL;
3085 pat = NULL;
3086 pat_list = NULL;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003087
3088 if (li->li_tv.v_type != VAR_DICT)
3089 continue;
3090
3091 event_dict = li->li_tv.vval.v_dict;
3092 if (event_dict == NULL)
3093 continue;
3094
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003095 di = dict_find(event_dict, (char_u *)"event", -1);
3096 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003097 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003098 if (di->di_tv.v_type == VAR_STRING)
3099 {
3100 event_name = di->di_tv.vval.v_string;
3101 if (event_name == NULL)
3102 {
3103 emsg(_(e_string_required));
3104 continue;
3105 }
3106 }
3107 else if (di->di_tv.v_type == VAR_LIST)
3108 {
3109 event_list = di->di_tv.vval.v_list;
3110 if (event_list == NULL)
3111 {
3112 emsg(_(e_list_required));
3113 continue;
3114 }
3115 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003116 else
3117 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003118 emsg(_(e_string_or_list_expected));
3119 continue;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003120 }
3121 }
3122
Bram Moolenaard61efa52022-07-23 09:52:04 +01003123 group_name = dict_get_string(event_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003124 if (group_name == NULL || *group_name == NUL)
3125 // if the autocmd group name is not specified, then use the current
3126 // autocmd group
3127 group = current_augroup;
3128 else
3129 {
3130 group = au_find_group(group_name);
3131 if (group == AUGROUP_ERROR)
3132 {
3133 if (delete)
3134 {
3135 semsg(_(e_no_such_group_str), group_name);
3136 retval = VVAL_FALSE;
3137 break;
3138 }
3139 // group is not found, create it now
3140 group = au_new_group(group_name);
3141 if (group == AUGROUP_ERROR)
3142 {
3143 semsg(_(e_no_such_group_str), group_name);
3144 retval = VVAL_FALSE;
3145 break;
3146 }
3147
3148 current_augroup = group;
3149 }
3150 }
3151
3152 // if a buffer number is specified, then generate a pattern of the form
3153 // "<buffer=n>. Otherwise, use the pattern supplied by the user.
3154 if (dict_has_key(event_dict, "bufnr"))
3155 {
3156 varnumber_T bnum;
3157
Bram Moolenaard61efa52022-07-23 09:52:04 +01003158 bnum = dict_get_number_def(event_dict, "bufnr", -1);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003159 if (bnum == -1)
3160 continue;
3161
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003162 vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
3163 pat = IObuff;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003164 }
3165 else
3166 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003167 di = dict_find(event_dict, (char_u *)"pattern", -1);
3168 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003169 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003170 if (di->di_tv.v_type == VAR_STRING)
3171 {
3172 pat = di->di_tv.vval.v_string;
3173 if (pat == NULL)
3174 {
3175 emsg(_(e_string_required));
3176 continue;
3177 }
3178 }
3179 else if (di->di_tv.v_type == VAR_LIST)
3180 {
3181 pat_list = di->di_tv.vval.v_list;
3182 if (pat_list == NULL)
3183 {
3184 emsg(_(e_list_required));
3185 continue;
3186 }
3187 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003188 else
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003189 {
3190 emsg(_(e_string_or_list_expected));
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003191 continue;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003192 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003193 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003194 else if (delete)
3195 pat = (char_u *)"";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003196 }
3197
Bram Moolenaard61efa52022-07-23 09:52:04 +01003198 once = dict_get_bool(event_dict, "once", FALSE);
3199 nested = dict_get_bool(event_dict, "nested", FALSE);
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01003200 // if 'replace' is true, then remove all the commands associated with
3201 // this autocmd event/group and add the new command.
Bram Moolenaard61efa52022-07-23 09:52:04 +01003202 replace = dict_get_bool(event_dict, "replace", FALSE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003203
Bram Moolenaard61efa52022-07-23 09:52:04 +01003204 cmd = dict_get_string(event_dict, "cmd", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003205 if (cmd == NULL)
3206 {
3207 if (delete)
3208 cmd = vim_strsave((char_u *)"");
3209 else
3210 continue;
3211 }
3212
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003213 if (delete && (event_name == NULL
3214 || (event_name[0] == '*' && event_name[1] == NUL)))
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003215 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003216 // if the event name is not specified or '*', delete all the events
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003217 for (event = (event_T)0; (int)event < NUM_EVENTS;
3218 event = (event_T)((int)event + 1))
3219 {
3220 if (do_autocmd_event(event, pat, once, nested, cmd, delete,
3221 group, 0) == FAIL)
3222 {
3223 retval = VVAL_FALSE;
3224 break;
3225 }
3226 }
3227 }
3228 else
3229 {
Bram Moolenaar968443e2022-05-27 21:16:34 +01003230 char_u *p = NULL;
3231
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003232 eli = NULL;
3233 end = NULL;
3234 while (TRUE)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003235 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003236 if (event_list != NULL)
3237 {
3238 if (eli == NULL)
3239 eli = event_list->lv_first;
3240 else
3241 eli = eli->li_next;
3242 if (eli == NULL)
3243 break;
3244 if (eli->li_tv.v_type != VAR_STRING
Bram Moolenaar882476a2022-06-01 16:02:38 +01003245 || (p = eli->li_tv.vval.v_string) == NULL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003246 {
3247 emsg(_(e_string_required));
Bram Moolenaar882476a2022-06-01 16:02:38 +01003248 break;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003249 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003250 }
3251 else
3252 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003253 if (p == NULL)
3254 p = event_name;
3255 if (p == NULL || *p == NUL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003256 break;
3257 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003258
3259 event = event_name2nr(p, &end);
3260 if (event == NUM_EVENTS || *end != NUL)
3261 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003262 // this also catches something following a valid event name
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003263 semsg(_(e_no_such_event_str), p);
3264 retval = VVAL_FALSE;
3265 break;
3266 }
3267 if (pat != NULL)
3268 {
3269 if (do_autocmd_event(event, pat, once, nested, cmd,
3270 delete | replace, group, 0) == FAIL)
3271 {
3272 retval = VVAL_FALSE;
3273 break;
3274 }
3275 }
3276 else if (pat_list != NULL)
3277 {
3278 FOR_ALL_LIST_ITEMS(pat_list, pli)
3279 {
3280 if (pli->li_tv.v_type != VAR_STRING
3281 || pli->li_tv.vval.v_string == NULL)
3282 {
3283 emsg(_(e_string_required));
3284 continue;
3285 }
3286 if (do_autocmd_event(event,
3287 pli->li_tv.vval.v_string, once, nested,
3288 cmd, delete | replace, group, 0) ==
3289 FAIL)
3290 {
3291 retval = VVAL_FALSE;
3292 break;
3293 }
3294 }
3295 if (retval == VVAL_FALSE)
3296 break;
3297 }
3298 if (event_name != NULL)
3299 p = end;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003300 }
3301 }
3302
3303 // if only the autocmd group name is specified for delete and the
3304 // autocmd event, pattern and cmd are not specified, then delete the
3305 // autocmd group.
3306 if (delete && group_name != NULL &&
3307 (event_name == NULL || event_name[0] == NUL)
3308 && (pat == NULL || pat[0] == NUL)
3309 && (cmd == NULL || cmd[0] == NUL))
3310 au_del_group(group_name);
3311 }
3312
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003313 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003314 VIM_CLEAR(cmd);
3315
3316 current_augroup = save_augroup;
3317 rettv->vval.v_number = retval;
3318}
3319
3320/*
3321 * autocmd_add() function
3322 */
3323 void
3324f_autocmd_add(typval_T *argvars, typval_T *rettv)
3325{
3326 autocmd_add_or_delete(argvars, rettv, FALSE);
3327}
3328
3329/*
3330 * autocmd_delete() function
3331 */
3332 void
3333f_autocmd_delete(typval_T *argvars, typval_T *rettv)
3334{
3335 autocmd_add_or_delete(argvars, rettv, TRUE);
3336}
3337
3338/*
3339 * autocmd_get() function
3340 * Returns a List of autocmds.
3341 */
3342 void
3343f_autocmd_get(typval_T *argvars, typval_T *rettv)
3344{
3345 event_T event_arg = NUM_EVENTS;
3346 event_T event;
3347 AutoPat *ap;
3348 AutoCmd *ac;
3349 list_T *event_list;
3350 dict_T *event_dict;
3351 char_u *event_name = NULL;
3352 char_u *pat = NULL;
3353 char_u *name = NULL;
3354 int group = AUGROUP_ALL;
3355
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01003356 if (rettv_list_alloc(rettv) == FAIL)
3357 return;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003358 if (check_for_opt_dict_arg(argvars, 0) == FAIL)
3359 return;
3360
3361 if (argvars[0].v_type == VAR_DICT)
3362 {
3363 // return only the autocmds in the specified group
3364 if (dict_has_key(argvars[0].vval.v_dict, "group"))
3365 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003366 name = dict_get_string(argvars[0].vval.v_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003367 if (name == NULL)
3368 return;
3369
3370 if (*name == NUL)
3371 group = AUGROUP_DEFAULT;
3372 else
3373 {
3374 group = au_find_group(name);
3375 if (group == AUGROUP_ERROR)
3376 {
3377 semsg(_(e_no_such_group_str), name);
3378 vim_free(name);
3379 return;
3380 }
3381 }
3382 vim_free(name);
3383 }
3384
3385 // return only the autocmds for the specified event
3386 if (dict_has_key(argvars[0].vval.v_dict, "event"))
3387 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003388 name = dict_get_string(argvars[0].vval.v_dict, "event", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003389 if (name == NULL)
3390 return;
3391
3392 if (name[0] == '*' && name[1] == NUL)
3393 event_arg = NUM_EVENTS;
3394 else
3395 {
John Marriott78d742a2024-04-02 20:26:01 +02003396 keyvalue_T target;
3397 keyvalue_T *entry;
3398
3399 target.key = 0;
John Marriott8d4477e2024-11-02 15:59:01 +01003400 target.value.string = name;
3401 target.value.length = STRLEN(target.value.string);
3402 entry = (keyvalue_T *)bsearch(&target, &event_tab,
Luuk van Baalb7147f82025-02-08 18:52:39 +01003403 NUM_EVENTS, sizeof(event_tab[0]), cmp_keyvalue_value_ni);
John Marriott78d742a2024-04-02 20:26:01 +02003404 if (entry == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003405 {
3406 semsg(_(e_no_such_event_str), name);
3407 vim_free(name);
3408 return;
3409 }
Luuk van Baalb7147f82025-02-08 18:52:39 +01003410 event_arg = (event_T)abs(entry->key);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003411 }
3412 vim_free(name);
3413 }
3414
3415 // return only the autocmds for the specified pattern
3416 if (dict_has_key(argvars[0].vval.v_dict, "pattern"))
3417 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003418 pat = dict_get_string(argvars[0].vval.v_dict, "pattern", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003419 if (pat == NULL)
3420 return;
3421 }
3422 }
3423
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003424 event_list = rettv->vval.v_list;
3425
3426 // iterate through all the autocmd events
3427 for (event = (event_T)0; (int)event < NUM_EVENTS;
3428 event = (event_T)((int)event + 1))
3429 {
3430 if (event_arg != NUM_EVENTS && event != event_arg)
3431 continue;
3432
3433 event_name = event_nr2name(event);
3434
3435 // iterate through all the patterns for this autocmd event
3436 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
3437 {
3438 char_u *group_name;
3439
zeertzjq2d1d5c62024-06-09 16:44:33 +02003440 if (ap->pat == NULL) // pattern has been removed
3441 continue;
3442
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003443 if (group != AUGROUP_ALL && group != ap->group)
3444 continue;
3445
3446 if (pat != NULL && STRCMP(pat, ap->pat) != 0)
3447 continue;
3448
3449 group_name = get_augroup_name(NULL, ap->group);
3450
3451 // iterate through all the commands for this pattern and add one
3452 // item for each cmd.
3453 for (ac = ap->cmds; ac != NULL; ac = ac->next)
3454 {
3455 event_dict = dict_alloc();
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003456 if (event_dict == NULL
3457 || list_append_dict(event_list, event_dict) == FAIL)
Christian Brabandt29269a72024-04-16 22:44:31 +02003458 {
3459 vim_free(pat);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003460 return;
Christian Brabandt29269a72024-04-16 22:44:31 +02003461 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003462
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003463 if (dict_add_string(event_dict, "event", event_name) == FAIL
3464 || dict_add_string(event_dict, "group",
3465 group_name == NULL ? (char_u *)""
3466 : group_name) == FAIL
3467 || (ap->buflocal_nr != 0
3468 && (dict_add_number(event_dict, "bufnr",
3469 ap->buflocal_nr) == FAIL))
3470 || dict_add_string(event_dict, "pattern",
3471 ap->pat) == FAIL
3472 || dict_add_string(event_dict, "cmd", ac->cmd) == FAIL
3473 || dict_add_bool(event_dict, "once", ac->once) == FAIL
3474 || dict_add_bool(event_dict, "nested",
3475 ac->nested) == FAIL)
Christian Brabandt29269a72024-04-16 22:44:31 +02003476 {
3477 vim_free(pat);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003478 return;
Christian Brabandt29269a72024-04-16 22:44:31 +02003479 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003480 }
3481 }
3482 }
3483
3484 vim_free(pat);
3485}
3486
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01003487#endif