blob: 392168269a71a5612b1b2ea26cd285c1989a5d6f [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"),
113 KEYVALUE_ENTRY(EVENT_CMDUNDEFINED, "CmdUndefined"),
114 KEYVALUE_ENTRY(EVENT_CMDWINENTER, "CmdwinEnter"),
115 KEYVALUE_ENTRY(EVENT_CMDWINLEAVE, "CmdwinLeave"),
116 KEYVALUE_ENTRY(EVENT_COLORSCHEME, "ColorScheme"),
117 KEYVALUE_ENTRY(EVENT_COLORSCHEMEPRE, "ColorSchemePre"),
118 KEYVALUE_ENTRY(EVENT_COMPLETECHANGED, "CompleteChanged"),
119 KEYVALUE_ENTRY(EVENT_COMPLETEDONE, "CompleteDone"),
120 KEYVALUE_ENTRY(EVENT_COMPLETEDONEPRE, "CompleteDonePre"),
Luuk van Baalb7147f82025-02-08 18:52:39 +0100121 KEYVALUE_ENTRY(-EVENT_CURSORHOLD, "CursorHold"),
122 KEYVALUE_ENTRY(-EVENT_CURSORHOLDI, "CursorHoldI"),
123 KEYVALUE_ENTRY(-EVENT_CURSORMOVED, "CursorMoved"),
124 KEYVALUE_ENTRY(-EVENT_CURSORMOVEDC, "CursorMovedC"),
125 KEYVALUE_ENTRY(-EVENT_CURSORMOVEDI, "CursorMovedI"),
John Marriott78d742a2024-04-02 20:26:01 +0200126 KEYVALUE_ENTRY(EVENT_DIFFUPDATED, "DiffUpdated"),
127 KEYVALUE_ENTRY(EVENT_DIRCHANGED, "DirChanged"),
128 KEYVALUE_ENTRY(EVENT_DIRCHANGEDPRE, "DirChangedPre"),
129 KEYVALUE_ENTRY(EVENT_ENCODINGCHANGED, "EncodingChanged"),
130 KEYVALUE_ENTRY(EVENT_EXITPRE, "ExitPre"),
Luuk van Baalb7147f82025-02-08 18:52:39 +0100131 KEYVALUE_ENTRY(-EVENT_FILEAPPENDCMD, "FileAppendCmd"),
132 KEYVALUE_ENTRY(-EVENT_FILEAPPENDPOST, "FileAppendPost"),
133 KEYVALUE_ENTRY(-EVENT_FILEAPPENDPRE, "FileAppendPre"),
134 KEYVALUE_ENTRY(-EVENT_FILECHANGEDRO, "FileChangedRO"),
135 KEYVALUE_ENTRY(-EVENT_FILECHANGEDSHELL, "FileChangedShell"),
136 KEYVALUE_ENTRY(-EVENT_FILECHANGEDSHELLPOST, "FileChangedShellPost"),
John Marriott78d742a2024-04-02 20:26:01 +0200137 KEYVALUE_ENTRY(EVENT_ENCODINGCHANGED, "FileEncoding"),
Luuk van Baalb7147f82025-02-08 18:52:39 +0100138 KEYVALUE_ENTRY(-EVENT_FILEREADCMD, "FileReadCmd"),
139 KEYVALUE_ENTRY(-EVENT_FILEREADPOST, "FileReadPost"),
140 KEYVALUE_ENTRY(-EVENT_FILEREADPRE, "FileReadPre"),
141 KEYVALUE_ENTRY(-EVENT_FILETYPE, "FileType"),
142 KEYVALUE_ENTRY(-EVENT_FILEWRITECMD, "FileWriteCmd"),
143 KEYVALUE_ENTRY(-EVENT_FILEWRITEPOST, "FileWritePost"),
144 KEYVALUE_ENTRY(-EVENT_FILEWRITEPRE, "FileWritePre"),
145 KEYVALUE_ENTRY(-EVENT_FILTERREADPOST, "FilterReadPost"),
146 KEYVALUE_ENTRY(-EVENT_FILTERREADPRE, "FilterReadPre"),
147 KEYVALUE_ENTRY(-EVENT_FILTERWRITEPOST, "FilterWritePost"),
148 KEYVALUE_ENTRY(-EVENT_FILTERWRITEPRE, "FilterWritePre"),
John Marriott78d742a2024-04-02 20:26:01 +0200149 KEYVALUE_ENTRY(EVENT_FOCUSGAINED, "FocusGained"),
150 KEYVALUE_ENTRY(EVENT_FOCUSLOST, "FocusLost"),
151 KEYVALUE_ENTRY(EVENT_FUNCUNDEFINED, "FuncUndefined"),
152 KEYVALUE_ENTRY(EVENT_GUIENTER, "GUIEnter"),
153 KEYVALUE_ENTRY(EVENT_GUIFAILED, "GUIFailed"),
Luuk van Baalb7147f82025-02-08 18:52:39 +0100154 KEYVALUE_ENTRY(-EVENT_INSERTCHANGE, "InsertChange"),
155 KEYVALUE_ENTRY(-EVENT_INSERTCHARPRE, "InsertCharPre"),
156 KEYVALUE_ENTRY(-EVENT_INSERTENTER, "InsertEnter"),
157 KEYVALUE_ENTRY(-EVENT_INSERTLEAVE, "InsertLeave"),
158 KEYVALUE_ENTRY(-EVENT_INSERTLEAVEPRE, "InsertLeavePre"),
Shougo Matsushita83678842024-07-11 22:05:12 +0200159 KEYVALUE_ENTRY(EVENT_KEYINPUTPRE, "KeyInputPre"),
John Marriott78d742a2024-04-02 20:26:01 +0200160 KEYVALUE_ENTRY(EVENT_MENUPOPUP, "MenuPopup"),
161 KEYVALUE_ENTRY(EVENT_MODECHANGED, "ModeChanged"),
162 KEYVALUE_ENTRY(EVENT_OPTIONSET, "OptionSet"),
163 KEYVALUE_ENTRY(EVENT_QUICKFIXCMDPOST, "QuickFixCmdPost"),
164 KEYVALUE_ENTRY(EVENT_QUICKFIXCMDPRE, "QuickFixCmdPre"),
165 KEYVALUE_ENTRY(EVENT_QUITPRE, "QuitPre"),
166 KEYVALUE_ENTRY(EVENT_REMOTEREPLY, "RemoteReply"),
167 KEYVALUE_ENTRY(EVENT_SAFESTATE, "SafeState"),
168 KEYVALUE_ENTRY(EVENT_SAFESTATEAGAIN, "SafeStateAgain"),
169 KEYVALUE_ENTRY(EVENT_SESSIONLOADPOST, "SessionLoadPost"),
170 KEYVALUE_ENTRY(EVENT_SESSIONWRITEPOST, "SessionWritePost"),
171 KEYVALUE_ENTRY(EVENT_SHELLCMDPOST, "ShellCmdPost"),
Luuk van Baalb7147f82025-02-08 18:52:39 +0100172 KEYVALUE_ENTRY(-EVENT_SHELLFILTERPOST, "ShellFilterPost"),
John Marriott78d742a2024-04-02 20:26:01 +0200173 KEYVALUE_ENTRY(EVENT_SIGUSR1, "SigUSR1"),
174 KEYVALUE_ENTRY(EVENT_SOURCECMD, "SourceCmd"),
175 KEYVALUE_ENTRY(EVENT_SOURCEPOST, "SourcePost"),
176 KEYVALUE_ENTRY(EVENT_SOURCEPRE, "SourcePre"),
177 KEYVALUE_ENTRY(EVENT_SPELLFILEMISSING, "SpellFileMissing"),
178 KEYVALUE_ENTRY(EVENT_STDINREADPOST, "StdinReadPost"),
179 KEYVALUE_ENTRY(EVENT_STDINREADPRE, "StdinReadPre"),
180 KEYVALUE_ENTRY(EVENT_SWAPEXISTS, "SwapExists"),
181 KEYVALUE_ENTRY(EVENT_SYNTAX, "Syntax"),
182 KEYVALUE_ENTRY(EVENT_TABCLOSED, "TabClosed"),
Jim Zhou5606ca52025-03-13 21:58:25 +0100183 KEYVALUE_ENTRY(EVENT_TABCLOSEDPRE, "TabClosedPre"),
John Marriott78d742a2024-04-02 20:26:01 +0200184 KEYVALUE_ENTRY(EVENT_TABENTER, "TabEnter"),
185 KEYVALUE_ENTRY(EVENT_TABLEAVE, "TabLeave"),
186 KEYVALUE_ENTRY(EVENT_TABNEW, "TabNew"),
187 KEYVALUE_ENTRY(EVENT_TERMCHANGED, "TermChanged"),
188 KEYVALUE_ENTRY(EVENT_TERMINALOPEN, "TerminalOpen"),
189 KEYVALUE_ENTRY(EVENT_TERMINALWINOPEN, "TerminalWinOpen"),
190 KEYVALUE_ENTRY(EVENT_TERMRESPONSE, "TermResponse"),
191 KEYVALUE_ENTRY(EVENT_TERMRESPONSEALL, "TermResponseAll"),
Luuk van Baalb7147f82025-02-08 18:52:39 +0100192 KEYVALUE_ENTRY(-EVENT_TEXTCHANGED, "TextChanged"),
193 KEYVALUE_ENTRY(-EVENT_TEXTCHANGEDI, "TextChangedI"),
194 KEYVALUE_ENTRY(-EVENT_TEXTCHANGEDP, "TextChangedP"),
195 KEYVALUE_ENTRY(-EVENT_TEXTCHANGEDT, "TextChangedT"),
196 KEYVALUE_ENTRY(-EVENT_TEXTYANKPOST, "TextYankPost"),
John Marriott78d742a2024-04-02 20:26:01 +0200197 KEYVALUE_ENTRY(EVENT_USER, "User"),
198 KEYVALUE_ENTRY(EVENT_VIMENTER, "VimEnter"),
199 KEYVALUE_ENTRY(EVENT_VIMLEAVE, "VimLeave"),
200 KEYVALUE_ENTRY(EVENT_VIMLEAVEPRE, "VimLeavePre"),
201 KEYVALUE_ENTRY(EVENT_VIMRESIZED, "VimResized"),
202 KEYVALUE_ENTRY(EVENT_VIMRESUME, "VimResume"),
203 KEYVALUE_ENTRY(EVENT_VIMSUSPEND, "VimSuspend"),
Luuk van Baalb7147f82025-02-08 18:52:39 +0100204 KEYVALUE_ENTRY(-EVENT_WINCLOSED, "WinClosed"),
205 KEYVALUE_ENTRY(-EVENT_WINENTER, "WinEnter"),
206 KEYVALUE_ENTRY(-EVENT_WINLEAVE, "WinLeave"),
John Marriott78d742a2024-04-02 20:26:01 +0200207 KEYVALUE_ENTRY(EVENT_WINNEW, "WinNew"),
208 KEYVALUE_ENTRY(EVENT_WINNEWPRE, "WinNewPre"),
Luuk van Baalb7147f82025-02-08 18:52:39 +0100209 KEYVALUE_ENTRY(-EVENT_WINRESIZED, "WinResized"),
210 KEYVALUE_ENTRY(-EVENT_WINSCROLLED, "WinScrolled"),
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100211};
212
Luuk van Baalb7147f82025-02-08 18:52:39 +0100213static AutoPat *first_autopat[NUM_EVENTS] = { NULL };
214static AutoPat *last_autopat[NUM_EVENTS] = { NULL };
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100215
kylo252ae6f1d82022-02-16 19:24:07 +0000216#define AUGROUP_DEFAULT (-1) // default autocmd group
217#define AUGROUP_ERROR (-2) // erroneous autocmd group
218#define AUGROUP_ALL (-3) // all autocmd groups
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100219
220/*
221 * struct used to keep status while executing autocommands for an event.
222 */
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100223struct AutoPatCmd_S
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100224{
225 AutoPat *curpat; // next AutoPat to examine
226 AutoCmd *nextcmd; // next AutoCmd to execute
227 int group; // group being used
228 char_u *fname; // fname to match with
229 char_u *sfname; // sfname to match with
230 char_u *tail; // tail of fname
231 event_T event; // current event
LemonBoyeca7c602022-04-14 15:39:43 +0100232 sctx_T script_ctx; // script context where it is defined
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100233 int arg_bufnr; // Initially equal to <abuf>, set to zero when
234 // buf is deleted.
LemonBoyeca7c602022-04-14 15:39:43 +0100235 AutoPatCmd_T *next; // chain of active apc-s for auto-invalidation
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100236};
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100237
LemonBoyeca7c602022-04-14 15:39:43 +0100238static AutoPatCmd_T *active_apc_list = NULL; // stack of active autocommands
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100239
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200240// Macro to loop over all the patterns for an autocmd event
241#define FOR_ALL_AUTOCMD_PATTERNS(event, ap) \
242 for ((ap) = first_autopat[(int)(event)]; (ap) != NULL; (ap) = (ap)->next)
243
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100244/*
245 * augroups stores a list of autocmd group names.
246 */
247static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
248#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
Bram Moolenaarc667da52019-11-30 20:52:27 +0100249// use get_deleted_augroup() to get this
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100250static char_u *deleted_augroup = NULL;
251
252/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100253 * The ID of the current group. Group 0 is the default one.
254 */
255static int current_augroup = AUGROUP_DEFAULT;
256
Bram Moolenaarc667da52019-11-30 20:52:27 +0100257static int au_need_clean = FALSE; // need to delete marked patterns
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100258
John Marriott78d742a2024-04-02 20:26:01 +0200259static event_T event_name2nr(char_u *start, char_u **end);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100260static char_u *event_nr2name(event_T event);
261static int au_get_grouparg(char_u **argp);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200262static 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 +0100263static 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 +0100264static void auto_next_pat(AutoPatCmd_T *apc, int stop_at_last);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100265static int au_find_group(char_u *name);
266
267static event_T last_event;
268static int last_group;
Bram Moolenaarc667da52019-11-30 20:52:27 +0100269static int autocmd_blocked = 0; // block all autocmds
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100270
271 static char_u *
272get_deleted_augroup(void)
273{
274 if (deleted_augroup == NULL)
275 deleted_augroup = (char_u *)_("--Deleted--");
276 return deleted_augroup;
277}
278
279/*
280 * Show the autocommands for one AutoPat.
281 */
282 static void
283show_autocmd(AutoPat *ap, event_T event)
284{
285 AutoCmd *ac;
286
287 // Check for "got_int" (here and at various places below), which is set
288 // when "q" has been hit for the "--more--" prompt
289 if (got_int)
290 return;
291 if (ap->pat == NULL) // pattern has been removed
292 return;
293
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000294 // Make sure no info referenced by "ap" is cleared, e.g. when a timer
295 // clears an augroup. Jump to "theend" after this!
296 // "ap->pat" may be cleared anyway.
297 ++autocmd_busy;
298
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100299 msg_putchar('\n');
300 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000301 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100302 if (event != last_event || ap->group != last_group)
303 {
304 if (ap->group != AUGROUP_DEFAULT)
305 {
306 if (AUGROUP_NAME(ap->group) == NULL)
307 msg_puts_attr((char *)get_deleted_augroup(), HL_ATTR(HLF_E));
308 else
309 msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
310 msg_puts(" ");
311 }
312 msg_puts_attr((char *)event_nr2name(event), HL_ATTR(HLF_T));
313 last_event = event;
314 last_group = ap->group;
315 msg_putchar('\n');
316 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000317 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100318 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000319
320 if (ap->pat == NULL)
321 goto theend; // timer might have cleared the pattern or group
322
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100323 msg_col = 4;
324 msg_outtrans(ap->pat);
325
326 for (ac = ap->cmds; ac != NULL; ac = ac->next)
327 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100328 if (ac->cmd == NULL) // skip removed commands
329 continue;
330
331 if (msg_col >= 14)
332 msg_putchar('\n');
333 msg_col = 14;
334 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000335 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100336 msg_outtrans(ac->cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100337#ifdef FEAT_EVAL
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100338 if (p_verbose > 0)
339 last_set_msg(ac->script_ctx);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100340#endif
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100341 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000342 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100343 if (ac->next != NULL)
344 {
345 msg_putchar('\n');
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100346 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000347 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100348 }
349 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000350
351theend:
352 --autocmd_busy;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100353}
354
355/*
356 * Mark an autocommand pattern for deletion.
357 */
358 static void
359au_remove_pat(AutoPat *ap)
360{
361 VIM_CLEAR(ap->pat);
362 ap->buflocal_nr = -1;
363 au_need_clean = TRUE;
364}
365
366/*
367 * Mark all commands for a pattern for deletion.
368 */
369 static void
370au_remove_cmds(AutoPat *ap)
371{
372 AutoCmd *ac;
373
374 for (ac = ap->cmds; ac != NULL; ac = ac->next)
375 VIM_CLEAR(ac->cmd);
376 au_need_clean = TRUE;
377}
378
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200379// Delete one command from an autocmd pattern.
380static void au_del_cmd(AutoCmd *ac)
381{
382 VIM_CLEAR(ac->cmd);
383 au_need_clean = TRUE;
384}
385
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100386/*
387 * Cleanup autocommands and patterns that have been deleted.
388 * This is only done when not executing autocommands.
389 */
390 static void
391au_cleanup(void)
392{
393 AutoPat *ap, **prev_ap;
394 AutoCmd *ac, **prev_ac;
395 event_T event;
396
397 if (autocmd_busy || !au_need_clean)
398 return;
399
400 // loop over all events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100401 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100402 event = (event_T)((int)event + 1))
403 {
404 // loop over all autocommand patterns
405 prev_ap = &(first_autopat[(int)event]);
406 for (ap = *prev_ap; ap != NULL; ap = *prev_ap)
407 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200408 int has_cmd = FALSE;
409
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200410 // loop over all commands for this pattern
411 prev_ac = &(ap->cmds);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100412 for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
413 {
414 // remove the command if the pattern is to be deleted or when
415 // the command has been marked for deletion
416 if (ap->pat == NULL || ac->cmd == NULL)
417 {
418 *prev_ac = ac->next;
419 vim_free(ac->cmd);
420 vim_free(ac);
421 }
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200422 else
423 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200424 has_cmd = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100425 prev_ac = &(ac->next);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200426 }
427 }
428
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200429 if (ap->pat != NULL && !has_cmd)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200430 // Pattern was not marked for deletion, but all of its
431 // commands were. So mark the pattern for deletion.
432 au_remove_pat(ap);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100433
434 // remove the pattern if it has been marked for deletion
435 if (ap->pat == NULL)
436 {
437 if (ap->next == NULL)
438 {
439 if (prev_ap == &(first_autopat[(int)event]))
440 last_autopat[(int)event] = NULL;
441 else
442 // this depends on the "next" field being the first in
443 // the struct
444 last_autopat[(int)event] = (AutoPat *)prev_ap;
445 }
446 *prev_ap = ap->next;
447 vim_regfree(ap->reg_prog);
448 vim_free(ap);
449 }
450 else
451 prev_ap = &(ap->next);
452 }
453 }
454
455 au_need_clean = FALSE;
456}
457
458/*
459 * Called when buffer is freed, to remove/invalidate related buffer-local
460 * autocmds.
461 */
462 void
463aubuflocal_remove(buf_T *buf)
464{
LemonBoyeca7c602022-04-14 15:39:43 +0100465 AutoPat *ap;
466 event_T event;
467 AutoPatCmd_T *apc;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100468
469 // invalidate currently executing autocommands
470 for (apc = active_apc_list; apc; apc = apc->next)
471 if (buf->b_fnum == apc->arg_bufnr)
472 apc->arg_bufnr = 0;
473
474 // invalidate buflocals looping through events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100475 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100476 event = (event_T)((int)event + 1))
477 // loop over all autocommand patterns
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200478 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100479 if (ap->buflocal_nr == buf->b_fnum)
480 {
481 au_remove_pat(ap);
482 if (p_verbose >= 6)
483 {
484 verbose_enter();
485 smsg(_("auto-removing autocommand: %s <buffer=%d>"),
486 event_nr2name(event), buf->b_fnum);
487 verbose_leave();
488 }
489 }
490 au_cleanup();
491}
492
493/*
494 * Add an autocmd group name.
495 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
496 */
497 static int
498au_new_group(char_u *name)
499{
500 int i;
501
502 i = au_find_group(name);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100503 if (i != AUGROUP_ERROR)
504 return i;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100505
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100506 // the group doesn't exist yet, add it. First try using a free entry.
507 for (i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100508 if (AUGROUP_NAME(i) == NULL)
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100509 break;
510 if (i == augroups.ga_len && ga_grow(&augroups, 1) == FAIL)
511 return AUGROUP_ERROR;
512
513 AUGROUP_NAME(i) = vim_strsave(name);
514 if (AUGROUP_NAME(i) == NULL)
515 return AUGROUP_ERROR;
516 if (i == augroups.ga_len)
517 ++augroups.ga_len;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100518
519 return i;
520}
521
522 static void
523au_del_group(char_u *name)
524{
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100525 int i;
526 event_T event;
527 AutoPat *ap;
528 int in_use = FALSE;
529
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100530
531 i = au_find_group(name);
532 if (i == AUGROUP_ERROR) // the group doesn't exist
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100533 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100534 semsg(_(e_no_such_group_str), name);
535 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100536 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100537 if (i == current_augroup)
538 {
539 emsg(_(e_cannot_delete_current_group));
540 return;
541 }
542
543 for (event = (event_T)0; (int)event < NUM_EVENTS;
544 event = (event_T)((int)event + 1))
545 {
546 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
547 if (ap->group == i && ap->pat != NULL)
548 {
549 give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
550 in_use = TRUE;
551 event = NUM_EVENTS;
552 break;
553 }
554 }
555 vim_free(AUGROUP_NAME(i));
556 if (in_use)
557 AUGROUP_NAME(i) = get_deleted_augroup();
558 else
559 AUGROUP_NAME(i) = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100560}
561
562/*
563 * Find the ID of an autocmd group name.
564 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
565 */
566 static int
567au_find_group(char_u *name)
568{
569 int i;
570
571 for (i = 0; i < augroups.ga_len; ++i)
572 if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
573 && STRCMP(AUGROUP_NAME(i), name) == 0)
574 return i;
575 return AUGROUP_ERROR;
576}
577
578/*
579 * Return TRUE if augroup "name" exists.
580 */
581 int
582au_has_group(char_u *name)
583{
584 return au_find_group(name) != AUGROUP_ERROR;
585}
586
587/*
588 * ":augroup {name}".
589 */
590 void
591do_augroup(char_u *arg, int del_group)
592{
593 int i;
594
595 if (del_group)
596 {
597 if (*arg == NUL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000598 emsg(_(e_argument_required));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100599 else
600 au_del_group(arg);
601 }
602 else if (STRICMP(arg, "end") == 0) // ":aug end": back to group 0
603 current_augroup = AUGROUP_DEFAULT;
604 else if (*arg) // ":aug xxx": switch to group xxx
605 {
606 i = au_new_group(arg);
607 if (i != AUGROUP_ERROR)
608 current_augroup = i;
609 }
610 else // ":aug": list the group names
611 {
612 msg_start();
613 for (i = 0; i < augroups.ga_len; ++i)
614 {
615 if (AUGROUP_NAME(i) != NULL)
616 {
617 msg_puts((char *)AUGROUP_NAME(i));
618 msg_puts(" ");
619 }
620 }
621 msg_clr_eos();
622 msg_end();
623 }
624}
625
Bram Moolenaare76062c2022-11-28 18:51:43 +0000626 void
627autocmd_init(void)
628{
629 CLEAR_FIELD(aucmd_win);
630}
631
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100632#if defined(EXITFREE) || defined(PROTO)
633 void
634free_all_autocmds(void)
635{
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100636 char_u *s;
637
638 for (current_augroup = -1; current_augroup < augroups.ga_len;
639 ++current_augroup)
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200640 do_autocmd(NULL, (char_u *)"", TRUE);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100641
Bram Moolenaare76062c2022-11-28 18:51:43 +0000642 for (int i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100643 {
644 s = ((char_u **)(augroups.ga_data))[i];
645 if (s != get_deleted_augroup())
646 vim_free(s);
647 }
648 ga_clear(&augroups);
Bram Moolenaare76062c2022-11-28 18:51:43 +0000649
Bram Moolenaar84497cd2022-11-28 20:34:52 +0000650 // aucmd_win[] is freed in win_free_all()
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100651}
652#endif
653
654/*
Bram Moolenaare76062c2022-11-28 18:51:43 +0000655 * Return TRUE if "win" is an active entry in aucmd_win[].
656 */
657 int
658is_aucmd_win(win_T *win)
659{
660 for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
661 if (aucmd_win[i].auc_win_used && aucmd_win[i].auc_win == win)
662 return TRUE;
663 return FALSE;
664}
665
666/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100667 * Return the event number for event name "start".
668 * Return NUM_EVENTS if the event name was not found.
669 * Return a pointer to the next event name in "end".
670 */
671 static event_T
672event_name2nr(char_u *start, char_u **end)
673{
674 char_u *p;
John Marriott78d742a2024-04-02 20:26:01 +0200675 keyvalue_T target;
676 keyvalue_T *entry;
677 static keyvalue_T *bufnewfile = &event_tab[BUFNEWFILE_INDEX];
678 static keyvalue_T *bufread = &event_tab[BUFREAD_INDEX];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100679
680 // the event name ends with end of line, '|', a blank or a comma
681 for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
682 ;
John Marriott78d742a2024-04-02 20:26:01 +0200683
684 target.key = 0;
John Marriott8d4477e2024-11-02 15:59:01 +0100685 target.value.string = start;
686 target.value.length = (size_t)(p - start);
John Marriott78d742a2024-04-02 20:26:01 +0200687
688 // special cases:
689 // BufNewFile and BufRead are searched for ALOT (especially at startup)
690 // so we check for them first.
691 if (cmp_keyvalue_value_ni(&target, bufnewfile) == 0)
692 entry = bufnewfile;
Luuk van Baalb7147f82025-02-08 18:52:39 +0100693 else if (cmp_keyvalue_value_ni(&target, bufread) == 0)
John Marriott78d742a2024-04-02 20:26:01 +0200694 entry = bufread;
695 else
Luuk van Baalb7147f82025-02-08 18:52:39 +0100696 entry = (keyvalue_T *)bsearch(&target, &event_tab, NUM_EVENTS,
697 sizeof(event_tab[0]), cmp_keyvalue_value_ni);
John Marriott78d742a2024-04-02 20:26:01 +0200698
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100699 if (*p == ',')
700 ++p;
701 *end = p;
John Marriott78d742a2024-04-02 20:26:01 +0200702
Luuk van Baalb7147f82025-02-08 18:52:39 +0100703 return (entry == NULL) ? NUM_EVENTS : (event_T)abs(entry->key);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100704}
705
706/*
707 * Return the name for event "event".
708 */
709 static char_u *
710event_nr2name(event_T event)
711{
712 int i;
John Marriott78d742a2024-04-02 20:26:01 +0200713#define CACHE_SIZE 12
714 static int cache_tab[CACHE_SIZE];
715 static int cache_last_index = -1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100716
John Marriott78d742a2024-04-02 20:26:01 +0200717 if (cache_last_index < 0)
718 {
Luuk van Baalb7147f82025-02-08 18:52:39 +0100719 for (i = 0; i < CACHE_SIZE; ++i)
John Marriott78d742a2024-04-02 20:26:01 +0200720 cache_tab[i] = -1;
Luuk van Baalb7147f82025-02-08 18:52:39 +0100721 cache_last_index = CACHE_SIZE - 1;
John Marriott78d742a2024-04-02 20:26:01 +0200722 }
723
724 // first look in the cache
725 // the cache is circular. to search it we start at the most recent entry
726 // and go backwards wrapping around when we get to index 0.
727 for (i = cache_last_index; cache_tab[i] >= 0; )
728 {
Luuk van Baalb7147f82025-02-08 18:52:39 +0100729 if ((event_T)abs(event_tab[cache_tab[i]].key) == event)
John Marriott8d4477e2024-11-02 15:59:01 +0100730 return event_tab[cache_tab[i]].value.string;
John Marriott78d742a2024-04-02 20:26:01 +0200731
732 if (i == 0)
Luuk van Baalb7147f82025-02-08 18:52:39 +0100733 i = CACHE_SIZE - 1;
John Marriott78d742a2024-04-02 20:26:01 +0200734 else
735 --i;
736
737 // are we back at the start?
738 if (i == cache_last_index)
739 break;
740 }
741
742 // look in the event table itself
Luuk van Baalb7147f82025-02-08 18:52:39 +0100743 for (i = 0; i < NUM_EVENTS; ++i)
John Marriott78d742a2024-04-02 20:26:01 +0200744 {
Luuk van Baalb7147f82025-02-08 18:52:39 +0100745 if ((event_T)abs(event_tab[i].key) == event)
John Marriott78d742a2024-04-02 20:26:01 +0200746 {
747 // store the found entry in the next position in the cache,
748 // wrapping around when we get to the maximum index.
Luuk van Baalb7147f82025-02-08 18:52:39 +0100749 if (cache_last_index == CACHE_SIZE - 1)
John Marriott78d742a2024-04-02 20:26:01 +0200750 cache_last_index = 0;
751 else
752 ++cache_last_index;
753 cache_tab[cache_last_index] = i;
754 break;
755 }
756 }
757
Luuk van Baalb7147f82025-02-08 18:52:39 +0100758 return (i == NUM_EVENTS) ? (char_u *)"Unknown" :
759 event_tab[i].value.string;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100760}
761
762/*
763 * Scan over the events. "*" stands for all events.
764 */
765 static char_u *
766find_end_event(
767 char_u *arg,
768 int have_group) // TRUE when group name was found
769{
770 char_u *pat;
771 char_u *p;
772
773 if (*arg == '*')
774 {
775 if (arg[1] && !VIM_ISWHITE(arg[1]))
776 {
Bram Moolenaar6d057012021-12-31 18:49:43 +0000777 semsg(_(e_illegal_character_after_star_str), arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100778 return NULL;
779 }
780 pat = arg + 1;
781 }
782 else
783 {
784 for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p)
785 {
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100786 if ((int)event_name2nr(pat, &p) >= NUM_EVENTS)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100787 {
788 if (have_group)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000789 semsg(_(e_no_such_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100790 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000791 semsg(_(e_no_such_group_or_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100792 return NULL;
793 }
794 }
795 }
796 return pat;
797}
798
799/*
Luuk van Baalb7147f82025-02-08 18:52:39 +0100800 * Return TRUE if "event" is included in 'eventignore(win)'.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100801 */
Luuk van Baalb7147f82025-02-08 18:52:39 +0100802 int
803event_ignored(event_T event, char_u *ei)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100804{
Luuk van Baalb7147f82025-02-08 18:52:39 +0100805 while (*ei != NUL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100806 {
Luuk van Baalb7147f82025-02-08 18:52:39 +0100807 if (STRNICMP(ei, "all", 3) == 0 && (ei[3] == NUL || ei[3] == ',')
808 && (ei == p_ei || (event_tab[event].key <= 0)))
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100809 return TRUE;
Luuk van Baalb7147f82025-02-08 18:52:39 +0100810 if (event_name2nr(ei, &ei) == event)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100811 return TRUE;
812 }
813
814 return FALSE;
815}
816
817/*
Luuk van Baalb7147f82025-02-08 18:52:39 +0100818 * Return OK when the contents of 'eventignore' or 'eventignorewin' is valid,
819 * FAIL otherwise.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100820 */
821 int
Luuk van Baalb7147f82025-02-08 18:52:39 +0100822check_ei(char_u *ei)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100823{
Luuk van Baalb7147f82025-02-08 18:52:39 +0100824 int win = ei != p_ei;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100825
Luuk van Baalb7147f82025-02-08 18:52:39 +0100826 while (*ei)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100827 {
Luuk van Baalb7147f82025-02-08 18:52:39 +0100828 if (STRNICMP(ei, "all", 3) == 0 && (ei[3] == NUL || ei[3] == ','))
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100829 {
Luuk van Baalb7147f82025-02-08 18:52:39 +0100830 ei += 3;
831 if (*ei == ',')
832 ++ei;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100833 }
Luuk van Baalb7147f82025-02-08 18:52:39 +0100834 else
835 {
836 event_T event = event_name2nr(ei, &ei);
837 if (event == NUM_EVENTS || (win && event_tab[event].key > 0))
838 return FAIL;
839 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100840 }
841
842 return OK;
843}
844
845# if defined(FEAT_SYN_HL) || defined(PROTO)
846
847/*
848 * Add "what" to 'eventignore' to skip loading syntax highlighting for every
849 * buffer loaded into the window. "what" must start with a comma.
850 * Returns the old value of 'eventignore' in allocated memory.
851 */
852 char_u *
853au_event_disable(char *what)
854{
855 char_u *new_ei;
856 char_u *save_ei;
John Marriott78d742a2024-04-02 20:26:01 +0200857 size_t p_ei_len;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100858
John Marriott78d742a2024-04-02 20:26:01 +0200859 p_ei_len = STRLEN(p_ei);
860 save_ei = vim_strnsave(p_ei, p_ei_len);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100861 if (save_ei == NULL)
862 return NULL;
863
John Marriott78d742a2024-04-02 20:26:01 +0200864 new_ei = vim_strnsave(p_ei, p_ei_len + STRLEN(what));
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100865 if (new_ei == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100866 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100867 vim_free(save_ei);
868 return NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100869 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100870
871 if (*what == ',' && *p_ei == NUL)
872 STRCPY(new_ei, what + 1);
873 else
zeertzjq969e11a2025-03-10 21:15:19 +0100874 STRCPY(new_ei + p_ei_len, what);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100875 set_string_option_direct((char_u *)"ei", -1, new_ei,
876 OPT_FREE, SID_NONE);
877 vim_free(new_ei);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100878 return save_ei;
879}
880
881 void
882au_event_restore(char_u *old_ei)
883{
884 if (old_ei != NULL)
885 {
886 set_string_option_direct((char_u *)"ei", -1, old_ei,
887 OPT_FREE, SID_NONE);
888 vim_free(old_ei);
889 }
890}
Bram Moolenaarc667da52019-11-30 20:52:27 +0100891# endif // FEAT_SYN_HL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100892
893/*
894 * do_autocmd() -- implements the :autocmd command. Can be used in the
895 * following ways:
896 *
897 * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
898 * will be automatically executed for <event>
899 * when editing a file matching <pat>, in
900 * the current group.
901 * :autocmd <event> <pat> Show the autocommands associated with
902 * <event> and <pat>.
903 * :autocmd <event> Show the autocommands associated with
904 * <event>.
905 * :autocmd Show all autocommands.
906 * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
907 * <event> and <pat>, and add the command
908 * <cmd>, for the current group.
909 * :autocmd! <event> <pat> Remove all autocommands associated with
910 * <event> and <pat> for the current group.
911 * :autocmd! <event> Remove all autocommands associated with
912 * <event> for the current group.
913 * :autocmd! Remove ALL autocommands for the current
914 * group.
915 *
916 * Multiple events and patterns may be given separated by commas. Here are
917 * some examples:
918 * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
919 * :autocmd bufleave * set tw=79 nosmartindent ic infercase
920 *
921 * :autocmd * *.c show all autocommands for *.c files.
922 *
923 * Mostly a {group} argument can optionally appear before <event>.
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200924 * "eap" can be NULL.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100925 */
926 void
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200927do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100928{
929 char_u *arg = arg_in;
930 char_u *pat;
931 char_u *envpat = NULL;
932 char_u *cmd;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200933 int cmd_need_free = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100934 event_T event;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200935 char_u *tofree = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100936 int nested = FALSE;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200937 int once = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100938 int group;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200939 int i;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200940 int flags = 0;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100941
942 if (*arg == '|')
943 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000944 eap->nextcmd = arg + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100945 arg = (char_u *)"";
946 group = AUGROUP_ALL; // no argument, use all groups
947 }
948 else
949 {
950 /*
951 * Check for a legal group name. If not, use AUGROUP_ALL.
952 */
953 group = au_get_grouparg(&arg);
954 if (arg == NULL) // out of memory
955 return;
956 }
957
958 /*
959 * Scan over the events.
960 * If we find an illegal name, return here, don't do anything.
961 */
962 pat = find_end_event(arg, group != AUGROUP_ALL);
963 if (pat == NULL)
964 return;
965
966 pat = skipwhite(pat);
967 if (*pat == '|')
968 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000969 eap->nextcmd = pat + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100970 pat = (char_u *)"";
971 cmd = (char_u *)"";
972 }
973 else
974 {
975 /*
976 * Scan over the pattern. Put a NUL at the end.
977 */
978 cmd = pat;
979 while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
980 cmd++;
981 if (*cmd)
982 *cmd++ = NUL;
983
984 // Expand environment variables in the pattern. Set 'shellslash', we
985 // want forward slashes here.
986 if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
987 {
988#ifdef BACKSLASH_IN_FILENAME
989 int p_ssl_save = p_ssl;
990
991 p_ssl = TRUE;
992#endif
993 envpat = expand_env_save(pat);
994#ifdef BACKSLASH_IN_FILENAME
995 p_ssl = p_ssl_save;
996#endif
997 if (envpat != NULL)
998 pat = envpat;
999 }
1000
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001001 cmd = skipwhite(cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001002 for (i = 0; i < 2; i++)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001003 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001004 if (*cmd == NUL)
1005 continue;
1006
1007 // Check for "++once" flag.
1008 if (STRNCMP(cmd, "++once", 6) == 0 && VIM_ISWHITE(cmd[6]))
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001009 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001010 if (once)
1011 semsg(_(e_duplicate_argument_str), "++once");
1012 once = TRUE;
1013 cmd = skipwhite(cmd + 6);
1014 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001015
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001016 // Check for "++nested" flag.
1017 if ((STRNCMP(cmd, "++nested", 8) == 0 && VIM_ISWHITE(cmd[8])))
1018 {
1019 if (nested)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001020 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001021 semsg(_(e_duplicate_argument_str), "++nested");
1022 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001023 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001024 nested = TRUE;
1025 cmd = skipwhite(cmd + 8);
1026 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001027
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001028 // Check for the old "nested" flag in legacy script.
1029 if (STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
1030 {
1031 if (in_vim9script())
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001032 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001033 // If there ever is a :nested command this error should
1034 // be removed and "nested" accepted as the start of the
1035 // command.
1036 emsg(_(e_invalid_command_nested_did_you_mean_plusplus_nested));
1037 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001038 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001039 if (nested)
1040 {
1041 semsg(_(e_duplicate_argument_str), "nested");
1042 return;
1043 }
1044 nested = TRUE;
1045 cmd = skipwhite(cmd + 6);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001046 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001047 }
1048
1049 /*
1050 * Find the start of the commands.
1051 * Expand <sfile> in it.
1052 */
1053 if (*cmd != NUL)
1054 {
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001055 if (eap != NULL)
1056 // Read a {} block if it follows.
1057 cmd = may_get_cmd_block(eap, cmd, &tofree, &flags);
1058
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001059 cmd = expand_sfile(cmd);
1060 if (cmd == NULL) // some error
1061 return;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001062 cmd_need_free = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001063 }
1064 }
1065
1066 /*
1067 * Print header when showing autocommands.
1068 */
1069 if (!forceit && *cmd == NUL)
1070 // Highlight title
1071 msg_puts_title(_("\n--- Autocommands ---"));
1072
1073 /*
1074 * Loop over the events.
1075 */
1076 last_event = (event_T)-1; // for listing the event name
1077 last_group = AUGROUP_ERROR; // for listing the group name
1078 if (*arg == '*' || *arg == NUL || *arg == '|')
1079 {
Bram Moolenaarb6db1462021-12-24 19:24:47 +00001080 if (*cmd != NUL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001081 emsg(_(e_cannot_define_autocommands_for_all_events));
1082 else
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +01001083 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001084 event = (event_T)((int)event + 1))
1085 if (do_autocmd_event(event, pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001086 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001087 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001088 }
1089 else
1090 {
1091 while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
1092 if (do_autocmd_event(event_name2nr(arg, &arg), pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001093 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001094 break;
1095 }
1096
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001097 if (cmd_need_free)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001098 vim_free(cmd);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001099 vim_free(tofree);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001100 vim_free(envpat);
1101}
1102
1103/*
1104 * Find the group ID in a ":autocmd" or ":doautocmd" argument.
1105 * The "argp" argument is advanced to the following argument.
1106 *
1107 * Returns the group ID, AUGROUP_ERROR for error (out of memory).
1108 */
1109 static int
1110au_get_grouparg(char_u **argp)
1111{
1112 char_u *group_name;
1113 char_u *p;
1114 char_u *arg = *argp;
1115 int group = AUGROUP_ALL;
1116
1117 for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p)
1118 ;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001119 if (p <= arg)
1120 return AUGROUP_ALL;
1121
1122 group_name = vim_strnsave(arg, p - arg);
1123 if (group_name == NULL) // out of memory
1124 return AUGROUP_ERROR;
1125 group = au_find_group(group_name);
1126 if (group == AUGROUP_ERROR)
1127 group = AUGROUP_ALL; // no match, use all groups
1128 else
1129 *argp = skipwhite(p); // match, skip over group name
1130 vim_free(group_name);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001131 return group;
1132}
1133
1134/*
1135 * do_autocmd() for one event.
1136 * If *pat == NUL do for all patterns.
1137 * If *cmd == NUL show entries.
1138 * If forceit == TRUE delete entries.
1139 * If group is not AUGROUP_ALL, only use this group.
1140 */
1141 static int
1142do_autocmd_event(
1143 event_T event,
1144 char_u *pat,
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001145 int once,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001146 int nested,
1147 char_u *cmd,
1148 int forceit,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001149 int group,
1150 int flags)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001151{
1152 AutoPat *ap;
1153 AutoPat **prev_ap;
1154 AutoCmd *ac;
1155 AutoCmd **prev_ac;
1156 int brace_level;
1157 char_u *endpat;
1158 int findgroup;
1159 int allgroups;
1160 int patlen;
1161 int is_buflocal;
1162 int buflocal_nr;
Bram Moolenaarc667da52019-11-30 20:52:27 +01001163 char_u buflocal_pat[25]; // for "<buffer=X>"
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001164
1165 if (group == AUGROUP_ALL)
1166 findgroup = current_augroup;
1167 else
1168 findgroup = group;
1169 allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
1170
1171 /*
1172 * Show or delete all patterns for an event.
1173 */
1174 if (*pat == NUL)
1175 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001176 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001177 {
1178 if (forceit) // delete the AutoPat, if it's in the current group
1179 {
1180 if (ap->group == findgroup)
1181 au_remove_pat(ap);
1182 }
1183 else if (group == AUGROUP_ALL || ap->group == group)
1184 show_autocmd(ap, event);
1185 }
1186 }
1187
1188 /*
1189 * Loop through all the specified patterns.
1190 */
1191 for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
1192 {
1193 /*
1194 * Find end of the pattern.
1195 * Watch out for a comma in braces, like "*.\{obj,o\}".
1196 */
1197 brace_level = 0;
1198 for (endpat = pat; *endpat && (*endpat != ',' || brace_level
1199 || (endpat > pat && endpat[-1] == '\\')); ++endpat)
1200 {
1201 if (*endpat == '{')
1202 brace_level++;
1203 else if (*endpat == '}')
1204 brace_level--;
1205 }
1206 if (pat == endpat) // ignore single comma
1207 continue;
1208 patlen = (int)(endpat - pat);
1209
1210 /*
1211 * detect special <buflocal[=X]> buffer-local patterns
1212 */
1213 is_buflocal = FALSE;
1214 buflocal_nr = 0;
1215
1216 if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
1217 && pat[patlen - 1] == '>')
1218 {
1219 // "<buffer...>": Error will be printed only for addition.
1220 // printing and removing will proceed silently.
1221 is_buflocal = TRUE;
1222 if (patlen == 8)
1223 // "<buffer>"
1224 buflocal_nr = curbuf->b_fnum;
1225 else if (patlen > 9 && pat[7] == '=')
1226 {
1227 if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0)
1228 // "<buffer=abuf>"
1229 buflocal_nr = autocmd_bufnr;
1230 else if (skipdigits(pat + 8) == pat + patlen - 1)
1231 // "<buffer=123>"
1232 buflocal_nr = atoi((char *)pat + 8);
1233 }
1234 }
1235
1236 if (is_buflocal)
1237 {
1238 // normalize pat into standard "<buffer>#N" form
1239 sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
1240 pat = buflocal_pat; // can modify pat and patlen
1241 patlen = (int)STRLEN(buflocal_pat); // but not endpat
1242 }
1243
1244 /*
1245 * Find AutoPat entries with this pattern. When adding a command it
1246 * always goes at or after the last one, so start at the end.
1247 */
1248 if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL)
1249 prev_ap = &last_autopat[(int)event];
1250 else
1251 prev_ap = &first_autopat[(int)event];
1252 while ((ap = *prev_ap) != NULL)
1253 {
1254 if (ap->pat != NULL)
1255 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001256 /*
1257 * Accept a pattern when:
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001258 * - a group was specified and it's that group, or a group was
1259 * not specified and it's the current group, or a group was
1260 * not specified and we are listing
1261 * - the length of the pattern matches
1262 * - the pattern matches.
1263 * For <buffer[=X]>, this condition works because we normalize
1264 * all buffer-local patterns.
1265 */
1266 if ((allgroups || ap->group == findgroup)
1267 && ap->patlen == patlen
1268 && STRNCMP(pat, ap->pat, patlen) == 0)
1269 {
1270 /*
1271 * Remove existing autocommands.
1272 * If adding any new autocmd's for this AutoPat, don't
1273 * delete the pattern from the autopat list, append to
1274 * this list.
1275 */
1276 if (forceit)
1277 {
1278 if (*cmd != NUL && ap->next == NULL)
1279 {
1280 au_remove_cmds(ap);
1281 break;
1282 }
1283 au_remove_pat(ap);
1284 }
1285
1286 /*
1287 * Show autocmd's for this autopat, or buflocals <buffer=X>
1288 */
1289 else if (*cmd == NUL)
1290 show_autocmd(ap, event);
1291
1292 /*
1293 * Add autocmd to this autopat, if it's the last one.
1294 */
1295 else if (ap->next == NULL)
1296 break;
1297 }
1298 }
1299 prev_ap = &ap->next;
1300 }
1301
1302 /*
1303 * Add a new command.
1304 */
1305 if (*cmd != NUL)
1306 {
1307 /*
1308 * If the pattern we want to add a command to does appear at the
1309 * end of the list (or not is not in the list at all), add the
1310 * pattern at the end of the list.
1311 */
1312 if (ap == NULL)
1313 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001314 // refuse to add buffer-local ap if buffer number is invalid
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001315 if (is_buflocal && (buflocal_nr == 0
1316 || buflist_findnr(buflocal_nr) == NULL))
1317 {
Bram Moolenaarf1474d82021-12-31 19:59:55 +00001318 semsg(_(e_buffer_nr_invalid_buffer_number), buflocal_nr);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001319 return FAIL;
1320 }
1321
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001322 ap = ALLOC_ONE(AutoPat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001323 if (ap == NULL)
1324 return FAIL;
1325 ap->pat = vim_strnsave(pat, patlen);
1326 ap->patlen = patlen;
1327 if (ap->pat == NULL)
1328 {
1329 vim_free(ap);
1330 return FAIL;
1331 }
1332
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001333#ifdef FEAT_EVAL
1334 // need to initialize last_mode for the first ModeChanged
1335 // autocmd
1336 if (event == EVENT_MODECHANGED && !has_modechanged())
LemonBoy2bf52dd2022-04-09 18:17:34 +01001337 get_mode(last_mode);
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001338#endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001339 // Initialize the fields checked by the WinScrolled and
1340 // WinResized trigger to prevent them from firing right after
1341 // the first autocmd is defined.
1342 if ((event == EVENT_WINSCROLLED || event == EVENT_WINRESIZED)
1343 && !(has_winscrolled() || has_winresized()))
LemonBoy09371822022-04-08 15:18:45 +01001344 {
Bram Moolenaar29967732022-11-20 12:11:45 +00001345 tabpage_T *save_curtab = curtab;
1346 tabpage_T *tp;
1347 FOR_ALL_TABPAGES(tp)
1348 {
1349 unuse_tabpage(curtab);
1350 use_tabpage(tp);
1351 snapshot_windows_scroll_size();
1352 }
1353 unuse_tabpage(curtab);
1354 use_tabpage(save_curtab);
LemonBoy09371822022-04-08 15:18:45 +01001355 }
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001356
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001357 if (is_buflocal)
1358 {
1359 ap->buflocal_nr = buflocal_nr;
1360 ap->reg_prog = NULL;
1361 }
1362 else
1363 {
1364 char_u *reg_pat;
1365
1366 ap->buflocal_nr = 0;
1367 reg_pat = file_pat_to_reg_pat(pat, endpat,
1368 &ap->allow_dirs, TRUE);
1369 if (reg_pat != NULL)
1370 ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
1371 vim_free(reg_pat);
1372 if (reg_pat == NULL || ap->reg_prog == NULL)
1373 {
1374 vim_free(ap->pat);
1375 vim_free(ap);
1376 return FAIL;
1377 }
1378 }
1379 ap->cmds = NULL;
1380 *prev_ap = ap;
1381 last_autopat[(int)event] = ap;
1382 ap->next = NULL;
1383 if (group == AUGROUP_ALL)
1384 ap->group = current_augroup;
1385 else
1386 ap->group = group;
1387 }
1388
1389 /*
1390 * Add the autocmd at the end of the AutoCmd list.
1391 */
1392 prev_ac = &(ap->cmds);
1393 while ((ac = *prev_ac) != NULL)
1394 prev_ac = &ac->next;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001395 ac = ALLOC_ONE(AutoCmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001396 if (ac == NULL)
1397 return FAIL;
1398 ac->cmd = vim_strsave(cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001399 ac->script_ctx = current_sctx;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001400 if (flags & UC_VIM9)
1401 ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001402#ifdef FEAT_EVAL
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001403 ac->script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001404#endif
1405 if (ac->cmd == NULL)
1406 {
1407 vim_free(ac);
1408 return FAIL;
1409 }
1410 ac->next = NULL;
1411 *prev_ac = ac;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001412 ac->once = once;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001413 ac->nested = nested;
1414 }
1415 }
1416
1417 au_cleanup(); // may really delete removed patterns/commands now
1418 return OK;
1419}
1420
1421/*
1422 * Implementation of ":doautocmd [group] event [fname]".
1423 * Return OK for success, FAIL for failure;
1424 */
1425 int
1426do_doautocmd(
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001427 char_u *arg_start,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001428 int do_msg, // give message for no matching autocmds?
1429 int *did_something)
1430{
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001431 char_u *arg = arg_start;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001432 char_u *fname;
1433 int nothing_done = TRUE;
1434 int group;
1435
1436 if (did_something != NULL)
1437 *did_something = FALSE;
1438
1439 /*
1440 * Check for a legal group name. If not, use AUGROUP_ALL.
1441 */
1442 group = au_get_grouparg(&arg);
1443 if (arg == NULL) // out of memory
1444 return FAIL;
1445
1446 if (*arg == '*')
1447 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001448 emsg(_(e_cant_execute_autocommands_for_all_events));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001449 return FAIL;
1450 }
1451
1452 /*
1453 * Scan over the events.
1454 * If we find an illegal name, return here, don't do anything.
1455 */
1456 fname = find_end_event(arg, group != AUGROUP_ALL);
1457 if (fname == NULL)
1458 return FAIL;
1459
1460 fname = skipwhite(fname);
1461
1462 /*
1463 * Loop over the events.
1464 */
1465 while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
1466 if (apply_autocmds_group(event_name2nr(arg, &arg),
1467 fname, NULL, TRUE, group, curbuf, NULL))
1468 nothing_done = FALSE;
1469
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001470 if (nothing_done && do_msg
1471#ifdef FEAT_EVAL
1472 && !aborting()
1473#endif
1474 )
1475 smsg(_("No matching autocommands: %s"), arg_start);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001476 if (did_something != NULL)
1477 *did_something = !nothing_done;
1478
1479#ifdef FEAT_EVAL
1480 return aborting() ? FAIL : OK;
1481#else
1482 return OK;
1483#endif
1484}
1485
1486/*
1487 * ":doautoall": execute autocommands for each loaded buffer.
1488 */
1489 void
1490ex_doautoall(exarg_T *eap)
1491{
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001492 int retval = OK;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001493 aco_save_T aco;
1494 buf_T *buf;
1495 bufref_T bufref;
1496 char_u *arg = eap->arg;
1497 int call_do_modelines = check_nomodeline(&arg);
1498 int did_aucmd;
1499
1500 /*
1501 * This is a bit tricky: For some commands curwin->w_buffer needs to be
1502 * equal to curbuf, but for some buffers there may not be a window.
1503 * So we change the buffer for the current window for a moment. This
1504 * gives problems when the autocommands make changes to the list of
1505 * buffers or windows...
1506 */
1507 FOR_ALL_BUFFERS(buf)
1508 {
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001509 // Only do loaded buffers and skip the current buffer, it's done last.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001510 if (buf->b_ml.ml_mfp == NULL || buf == curbuf)
1511 continue;
1512
Bram Moolenaare76062c2022-11-28 18:51:43 +00001513 // Find a window for this buffer and save some values.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001514 aucmd_prepbuf(&aco, buf);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001515 if (curbuf != buf)
1516 {
1517 // Failed to find a window for this buffer. Better not execute
1518 // autocommands then.
1519 retval = FAIL;
1520 break;
1521 }
1522
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001523 set_bufref(&bufref, buf);
1524
1525 // execute the autocommands for this buffer
1526 retval = do_doautocmd(arg, FALSE, &did_aucmd);
1527
1528 if (call_do_modelines && did_aucmd)
1529 // Execute the modeline settings, but don't set window-local
1530 // options if we are using the current window for another
1531 // buffer.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001532 do_modelines(is_aucmd_win(curwin) ? OPT_NOWIN : 0);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001533
1534 // restore the current window
1535 aucmd_restbuf(&aco);
1536
1537 // stop if there is some error or buffer was deleted
1538 if (retval == FAIL || !bufref_valid(&bufref))
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001539 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001540 retval = FAIL;
1541 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001542 }
1543 }
1544
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001545 // Execute autocommands for the current buffer last.
1546 if (retval == OK)
1547 {
1548 do_doautocmd(arg, FALSE, &did_aucmd);
1549 if (call_do_modelines && did_aucmd)
1550 do_modelines(0);
1551 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001552}
1553
1554/*
1555 * Check *argp for <nomodeline>. When it is present return FALSE, otherwise
1556 * return TRUE and advance *argp to after it.
1557 * Thus return TRUE when do_modelines() should be called.
1558 */
1559 int
1560check_nomodeline(char_u **argp)
1561{
1562 if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
1563 {
1564 *argp = skipwhite(*argp + 12);
1565 return FALSE;
1566 }
1567 return TRUE;
1568}
1569
1570/*
1571 * Prepare for executing autocommands for (hidden) buffer "buf".
1572 * Search for a visible window containing the current buffer. If there isn't
Bram Moolenaare76062c2022-11-28 18:51:43 +00001573 * one then use an entry in "aucmd_win[]".
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001574 * Set "curbuf" and "curwin" to match "buf".
Bram Moolenaare76062c2022-11-28 18:51:43 +00001575 * When this fails "curbuf" is not equal "buf".
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001576 */
1577 void
1578aucmd_prepbuf(
1579 aco_save_T *aco, // structure to save values in
1580 buf_T *buf) // new curbuf
1581{
1582 win_T *win;
1583 int save_ea;
1584#ifdef FEAT_AUTOCHDIR
1585 int save_acd;
1586#endif
1587
1588 // Find a window that is for the new buffer
1589 if (buf == curbuf) // be quick when buf is curbuf
1590 win = curwin;
1591 else
1592 FOR_ALL_WINDOWS(win)
1593 if (win->w_buffer == buf)
1594 break;
1595
Bram Moolenaare76062c2022-11-28 18:51:43 +00001596 // Allocate a window when needed.
1597 win_T *auc_win = NULL;
1598 int auc_idx = AUCMD_WIN_COUNT;
1599 if (win == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001600 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001601 for (auc_idx = 0; auc_idx < AUCMD_WIN_COUNT; ++auc_idx)
1602 if (!aucmd_win[auc_idx].auc_win_used)
1603 {
Bram Moolenaar84497cd2022-11-28 20:34:52 +00001604 if (aucmd_win[auc_idx].auc_win == NULL)
1605 aucmd_win[auc_idx].auc_win = win_alloc_popup_win();
1606 auc_win = aucmd_win[auc_idx].auc_win;
Bram Moolenaare76062c2022-11-28 18:51:43 +00001607 if (auc_win != NULL)
Bram Moolenaare76062c2022-11-28 18:51:43 +00001608 aucmd_win[auc_idx].auc_win_used = TRUE;
Bram Moolenaare76062c2022-11-28 18:51:43 +00001609 break;
1610 }
1611
1612 // If this fails (out of memory or using all AUCMD_WIN_COUNT
1613 // entries) then we can't reliable execute the autocmd, return with
1614 // "curbuf" unequal "buf".
1615 if (auc_win == NULL)
1616 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001617 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001618
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001619 aco->save_curwin_id = curwin->w_id;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001620 aco->save_prevwin_id = prevwin == NULL ? 0 : prevwin->w_id;
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001621 aco->save_State = State;
zeertzjqf2678472024-01-17 21:22:59 +01001622#ifdef FEAT_JOB_CHANNEL
1623 if (bt_prompt(curbuf))
1624 aco->save_prompt_insert = curbuf->b_prompt_insert;
1625#endif
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001626
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001627 if (win != NULL)
1628 {
1629 // There is a window for "buf" in the current tab page, make it the
1630 // curwin. This is preferred, it has the least side effects (esp. if
1631 // "buf" is curbuf).
Bram Moolenaare76062c2022-11-28 18:51:43 +00001632 aco->use_aucmd_win_idx = -1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001633 curwin = win;
1634 }
1635 else
1636 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001637 // There is no window for "buf", use "auc_win". To minimize the side
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001638 // effects, insert it in the current tab page.
1639 // Anything related to a window (e.g., setting folds) may have
1640 // unexpected results.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001641 aco->use_aucmd_win_idx = auc_idx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001642
Bram Moolenaare76062c2022-11-28 18:51:43 +00001643 win_init_popup_win(auc_win, buf);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001644
zeertzjq9d956ee2024-04-07 18:16:10 +02001645 // Make sure tp_localdir and globaldir are NULL to avoid a
1646 // chdir() in win_enter_ext().
1647 // win_init_popup_win() has already set w_localdir to NULL.
1648 aco->tp_localdir = curtab->tp_localdir;
1649 curtab->tp_localdir = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001650 aco->globaldir = globaldir;
1651 globaldir = NULL;
1652
Bram Moolenaare76062c2022-11-28 18:51:43 +00001653 // Split the current window, put the auc_win in the upper half.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001654 // We don't want the BufEnter or WinEnter autocommands.
1655 block_autocmds();
1656 make_snapshot(SNAP_AUCMD_IDX);
1657 save_ea = p_ea;
1658 p_ea = FALSE;
1659
1660#ifdef FEAT_AUTOCHDIR
1661 // Prevent chdir() call in win_enter_ext(), through do_autochdir().
1662 save_acd = p_acd;
1663 p_acd = FALSE;
1664#endif
1665
Sean Dewar704966c2024-02-20 22:00:33 +01001666 (void)win_split_ins(0, WSP_TOP | WSP_FORCE_ROOM, auc_win, 0, NULL);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001667 (void)win_comp_pos(); // recompute window positions
1668 p_ea = save_ea;
1669#ifdef FEAT_AUTOCHDIR
1670 p_acd = save_acd;
1671#endif
1672 unblock_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001673 curwin = auc_win;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001674 }
1675 curbuf = buf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001676 aco->new_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001677 set_bufref(&aco->new_curbuf, curbuf);
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001678
1679 // disable the Visual area, the position may be invalid in another buffer
1680 aco->save_VIsual_active = VIsual_active;
1681 VIsual_active = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001682}
1683
1684/*
1685 * Cleanup after executing autocommands for a (hidden) buffer.
1686 * Restore the window as it was (if possible).
1687 */
1688 void
1689aucmd_restbuf(
1690 aco_save_T *aco) // structure holding saved values
1691{
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001692 int dummy;
1693 win_T *save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001694
Bram Moolenaare76062c2022-11-28 18:51:43 +00001695 if (aco->use_aucmd_win_idx >= 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001696 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001697 win_T *awp = aucmd_win[aco->use_aucmd_win_idx].auc_win;
1698
Bram Moolenaare76062c2022-11-28 18:51:43 +00001699 // Find "awp", it can't be closed, but it may be in another tab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001700 // page. Do not trigger autocommands here.
1701 block_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001702 if (curwin != awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001703 {
1704 tabpage_T *tp;
1705 win_T *wp;
1706
1707 FOR_ALL_TAB_WINDOWS(tp, wp)
1708 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001709 if (wp == awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001710 {
1711 if (tp != curtab)
1712 goto_tabpage_tp(tp, TRUE, TRUE);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001713 win_goto(awp);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001714 goto win_found;
1715 }
1716 }
1717 }
1718win_found:
zeertzjq46bdae02023-09-24 23:16:08 +02001719 --curbuf->b_nwindows;
orbitalcde8de02023-04-02 22:05:13 +01001720#ifdef FEAT_JOB_CHANNEL
zeertzjqa40c0bc2023-05-27 14:10:08 +01001721 int save_stop_insert_mode = stop_insert_mode;
orbitalcde8de02023-04-02 22:05:13 +01001722 // May need to stop Insert mode if we were in a prompt buffer.
1723 leaving_window(curwin);
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001724 // Do not stop Insert mode when already in Insert mode before.
1725 if (aco->save_State & MODE_INSERT)
zeertzjqa40c0bc2023-05-27 14:10:08 +01001726 stop_insert_mode = save_stop_insert_mode;
orbitalcde8de02023-04-02 22:05:13 +01001727#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001728 // Remove the window and frame from the tree of frames.
Sean Dewar704966c2024-02-20 22:00:33 +01001729 (void)winframe_remove(curwin, &dummy, NULL, NULL);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001730 win_remove(curwin, NULL);
Bram Moolenaar84497cd2022-11-28 20:34:52 +00001731
1732 // The window is marked as not used, but it is not freed, it can be
1733 // used again.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001734 aucmd_win[aco->use_aucmd_win_idx].auc_win_used = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001735 last_status(FALSE); // may need to remove last status line
1736
1737 if (!valid_tabpage_win(curtab))
1738 // no valid window in current tabpage
1739 close_tabpage(curtab);
1740
1741 restore_snapshot(SNAP_AUCMD_IDX, FALSE);
1742 (void)win_comp_pos(); // recompute window positions
1743 unblock_autocmds();
1744
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001745 save_curwin = win_find_by_id(aco->save_curwin_id);
1746 if (save_curwin != NULL)
1747 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001748 else
1749 // Hmm, original window disappeared. Just use the first one.
1750 curwin = firstwin;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001751 curbuf = curwin->w_buffer;
1752#ifdef FEAT_JOB_CHANNEL
1753 // May need to restore insert mode for a prompt buffer.
1754 entering_window(curwin);
zeertzjqf2678472024-01-17 21:22:59 +01001755 if (bt_prompt(curbuf))
1756 curbuf->b_prompt_insert = aco->save_prompt_insert;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001757#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001758 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001759#ifdef FEAT_EVAL
Bram Moolenaare76062c2022-11-28 18:51:43 +00001760 vars_clear(&awp->w_vars->dv_hashtab); // free all w: variables
1761 hash_init(&awp->w_vars->dv_hashtab); // re-use the hashtab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001762#endif
zeertzjq9d956ee2024-04-07 18:16:10 +02001763 // If :lcd has been used in the autocommand window, correct current
1764 // directory before restoring tp_localdir and globaldir.
1765 if (awp->w_localdir != NULL)
1766 win_fix_current_dir();
1767 vim_free(curtab->tp_localdir);
1768 curtab->tp_localdir = aco->tp_localdir;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001769 vim_free(globaldir);
1770 globaldir = aco->globaldir;
1771
1772 // the buffer contents may have changed
zeertzjq49f05242023-02-04 10:58:34 +00001773 VIsual_active = aco->save_VIsual_active;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001774 check_cursor();
1775 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1776 {
1777 curwin->w_topline = curbuf->b_ml.ml_line_count;
1778#ifdef FEAT_DIFF
1779 curwin->w_topfill = 0;
1780#endif
1781 }
1782#if defined(FEAT_GUI)
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001783 if (gui.in_use)
1784 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001785 // Hide the scrollbars from the "awp" and update.
1786 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_LEFT], FALSE);
1787 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_RIGHT], FALSE);
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001788 gui_may_update_scrollbars();
1789 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001790#endif
1791 }
1792 else
1793 {
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001794 // Restore curwin. Use the window ID, a window may have been closed
1795 // and the memory re-used for another one.
1796 save_curwin = win_find_by_id(aco->save_curwin_id);
1797 if (save_curwin != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001798 {
1799 // Restore the buffer which was previously edited by curwin, if
1800 // it was changed, we are still the same window and the buffer is
1801 // valid.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001802 if (curwin->w_id == aco->new_curwin_id
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001803 && curbuf != aco->new_curbuf.br_buf
1804 && bufref_valid(&aco->new_curbuf)
1805 && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
1806 {
1807# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
1808 if (curwin->w_s == &curbuf->b_s)
1809 curwin->w_s = &aco->new_curbuf.br_buf->b_s;
1810# endif
1811 --curbuf->b_nwindows;
1812 curbuf = aco->new_curbuf.br_buf;
1813 curwin->w_buffer = curbuf;
1814 ++curbuf->b_nwindows;
1815 }
1816
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001817 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001818 curbuf = curwin->w_buffer;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001819 prevwin = win_find_by_id(aco->save_prevwin_id);
zeertzjq49f05242023-02-04 10:58:34 +00001820
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001821 // In case the autocommand moves the cursor to a position that
1822 // does not exist in curbuf.
zeertzjq49f05242023-02-04 10:58:34 +00001823 VIsual_active = aco->save_VIsual_active;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001824 check_cursor();
1825 }
1826 }
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001827
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001828 VIsual_active = aco->save_VIsual_active;
zeertzjq49f05242023-02-04 10:58:34 +00001829 check_cursor(); // just in case lines got deleted
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001830 if (VIsual_active)
1831 check_pos(curbuf, &VIsual);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001832}
1833
1834static int autocmd_nested = FALSE;
1835
1836/*
1837 * Execute autocommands for "event" and file name "fname".
1838 * Return TRUE if some commands were executed.
1839 */
1840 int
1841apply_autocmds(
1842 event_T event,
1843 char_u *fname, // NULL or empty means use actual file name
1844 char_u *fname_io, // fname to use for <afile> on cmdline
1845 int force, // when TRUE, ignore autocmd_busy
1846 buf_T *buf) // buffer for <abuf>
1847{
1848 return apply_autocmds_group(event, fname, fname_io, force,
1849 AUGROUP_ALL, buf, NULL);
1850}
1851
1852/*
1853 * Like apply_autocmds(), but with extra "eap" argument. This takes care of
1854 * setting v:filearg.
1855 */
1856 int
1857apply_autocmds_exarg(
1858 event_T event,
1859 char_u *fname,
1860 char_u *fname_io,
1861 int force,
1862 buf_T *buf,
1863 exarg_T *eap)
1864{
1865 return apply_autocmds_group(event, fname, fname_io, force,
1866 AUGROUP_ALL, buf, eap);
1867}
1868
1869/*
1870 * Like apply_autocmds(), but handles the caller's retval. If the script
1871 * processing is being aborted or if retval is FAIL when inside a try
1872 * conditional, no autocommands are executed. If otherwise the autocommands
1873 * cause the script to be aborted, retval is set to FAIL.
1874 */
1875 int
1876apply_autocmds_retval(
1877 event_T event,
1878 char_u *fname, // NULL or empty means use actual file name
1879 char_u *fname_io, // fname to use for <afile> on cmdline
1880 int force, // when TRUE, ignore autocmd_busy
1881 buf_T *buf, // buffer for <abuf>
1882 int *retval) // pointer to caller's retval
1883{
1884 int did_cmd;
1885
1886#ifdef FEAT_EVAL
1887 if (should_abort(*retval))
1888 return FALSE;
1889#endif
1890
1891 did_cmd = apply_autocmds_group(event, fname, fname_io, force,
1892 AUGROUP_ALL, buf, NULL);
1893 if (did_cmd
1894#ifdef FEAT_EVAL
1895 && aborting()
1896#endif
1897 )
1898 *retval = FAIL;
1899 return did_cmd;
1900}
1901
1902/*
1903 * Return TRUE when there is a CursorHold autocommand defined.
1904 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001905 static int
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001906has_cursorhold(void)
1907{
Bram Moolenaar24959102022-05-07 20:01:16 +01001908 return (first_autopat[(int)(get_real_state() == MODE_NORMAL_BUSY
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001909 ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
1910}
1911
1912/*
1913 * Return TRUE if the CursorHold event can be triggered.
1914 */
1915 int
1916trigger_cursorhold(void)
1917{
1918 int state;
1919
1920 if (!did_cursorhold
1921 && has_cursorhold()
1922 && reg_recording == 0
1923 && typebuf.tb_len == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001924 && !ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001925 {
1926 state = get_real_state();
Bram Moolenaar24959102022-05-07 20:01:16 +01001927 if (state == MODE_NORMAL_BUSY || (state & MODE_INSERT) != 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001928 return TRUE;
1929 }
1930 return FALSE;
1931}
1932
1933/*
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001934 * Return TRUE when there is a WinResized autocommand defined.
1935 */
1936 int
1937has_winresized(void)
1938{
1939 return (first_autopat[(int)EVENT_WINRESIZED] != NULL);
1940}
1941
1942/*
LemonBoy09371822022-04-08 15:18:45 +01001943 * Return TRUE when there is a WinScrolled autocommand defined.
1944 */
1945 int
1946has_winscrolled(void)
1947{
1948 return (first_autopat[(int)EVENT_WINSCROLLED] != NULL);
1949}
1950
1951/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001952 * Return TRUE when there is a CursorMoved autocommand defined.
1953 */
1954 int
1955has_cursormoved(void)
1956{
1957 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1958}
1959
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001960/*
1961 * Return TRUE when there is a CursorMovedI autocommand defined.
1962 */
1963 int
1964has_cursormovedI(void)
1965{
1966 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1967}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001968
1969/*
1970 * Return TRUE when there is a TextChanged autocommand defined.
1971 */
1972 int
1973has_textchanged(void)
1974{
1975 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1976}
1977
1978/*
1979 * Return TRUE when there is a TextChangedI autocommand defined.
1980 */
1981 int
1982has_textchangedI(void)
1983{
1984 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
1985}
1986
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001987/*
1988 * Return TRUE when there is a TextChangedP autocommand defined.
1989 */
1990 int
1991has_textchangedP(void)
1992{
1993 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
1994}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001995
1996/*
1997 * Return TRUE when there is an InsertCharPre autocommand defined.
1998 */
1999 int
2000has_insertcharpre(void)
2001{
2002 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
2003}
2004
2005/*
Shougo Matsushita83678842024-07-11 22:05:12 +02002006 * Return TRUE when there is an KeyInputPre autocommand defined.
2007 */
2008 int
2009has_keyinputpre(void)
2010{
2011 return (first_autopat[(int)EVENT_KEYINPUTPRE] != NULL);
2012}
2013
2014/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002015 * Return TRUE when there is an CmdUndefined autocommand defined.
2016 */
2017 int
2018has_cmdundefined(void)
2019{
2020 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
2021}
2022
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002023#if defined(FEAT_EVAL) || defined(PROTO)
2024/*
2025 * Return TRUE when there is a TextYankPost autocommand defined.
2026 */
2027 int
2028has_textyankpost(void)
2029{
2030 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
2031}
2032#endif
2033
Bram Moolenaard7f246c2019-04-08 18:15:41 +02002034#if defined(FEAT_EVAL) || defined(PROTO)
2035/*
2036 * Return TRUE when there is a CompleteChanged autocommand defined.
2037 */
2038 int
2039has_completechanged(void)
2040{
2041 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
2042}
2043#endif
2044
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002045#if defined(FEAT_EVAL) || defined(PROTO)
2046/*
2047 * Return TRUE when there is a ModeChanged autocommand defined.
2048 */
2049 int
2050has_modechanged(void)
2051{
2052 return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
2053}
2054#endif
2055
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002056/*
2057 * Execute autocommands for "event" and file name "fname".
2058 * Return TRUE if some commands were executed.
2059 */
2060 static int
2061apply_autocmds_group(
2062 event_T event,
2063 char_u *fname, // NULL or empty means use actual file name
2064 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
2065 // use fname
2066 int force, // when TRUE, ignore autocmd_busy
2067 int group, // group ID, or AUGROUP_ALL
2068 buf_T *buf, // buffer for <abuf>
2069 exarg_T *eap UNUSED) // command arguments
2070{
2071 char_u *sfname = NULL; // short file name
2072 char_u *tail;
2073 int save_changed;
2074 buf_T *old_curbuf;
2075 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002076 char_u *save_autocmd_fname;
2077 int save_autocmd_fname_full;
2078 int save_autocmd_bufnr;
2079 char_u *save_autocmd_match;
2080 int save_autocmd_busy;
2081 int save_autocmd_nested;
2082 static int nesting = 0;
LemonBoyeca7c602022-04-14 15:39:43 +01002083 AutoPatCmd_T patcmd;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002084 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002085 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002086#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002087 funccal_entry_T funccal_entry;
2088 char_u *save_cmdarg;
2089 long save_cmdbang;
2090#endif
2091 static int filechangeshell_busy = FALSE;
2092#ifdef FEAT_PROFILE
2093 proftime_T wait_time;
2094#endif
2095 int did_save_redobuff = FALSE;
2096 save_redo_T save_redo;
2097 int save_KeyTyped = KeyTyped;
ichizok7e5fe382023-04-15 13:17:50 +01002098 ESTACK_CHECK_DECLARATION;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002099
2100 /*
2101 * Quickly return if there are no autocommands for this event or
2102 * autocommands are blocked.
2103 */
2104 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
2105 || autocmd_blocked > 0)
2106 goto BYPASS_AU;
2107
2108 /*
2109 * When autocommands are busy, new autocommands are only executed when
2110 * explicitly enabled with the "nested" flag.
2111 */
2112 if (autocmd_busy && !(force || autocmd_nested))
2113 goto BYPASS_AU;
2114
2115#ifdef FEAT_EVAL
2116 /*
2117 * Quickly return when immediately aborting on error, or when an interrupt
2118 * occurred or an exception was thrown but not caught.
2119 */
2120 if (aborting())
2121 goto BYPASS_AU;
2122#endif
2123
2124 /*
2125 * FileChangedShell never nests, because it can create an endless loop.
2126 */
2127 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
2128 || event == EVENT_FILECHANGEDSHELLPOST))
2129 goto BYPASS_AU;
2130
2131 /*
2132 * Ignore events in 'eventignore'.
2133 */
Luuk van Baalb7147f82025-02-08 18:52:39 +01002134 if (event_ignored(event, p_ei))
2135 goto BYPASS_AU;
2136
2137 wininfo_T *wip;
2138 int win_ignore = FALSE;
2139 // If event is allowed in 'eventignorewin', check if curwin or all windows
2140 // into "buf" are ignoring the event.
2141 if (buf == curbuf && event_tab[event].key <= 0)
2142 win_ignore = event_ignored(event, curwin->w_p_eiw);
2143 else if (buf != NULL && event_tab[event].key <= 0)
2144 FOR_ALL_BUF_WININFO(buf, wip)
2145 if (wip->wi_win != NULL && wip->wi_win->w_buffer == buf)
2146 win_ignore = event_ignored(event, wip->wi_win->w_p_eiw);
2147 if (win_ignore)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002148 goto BYPASS_AU;
2149
2150 /*
2151 * Allow nesting of autocommands, but restrict the depth, because it's
2152 * possible to create an endless loop.
2153 */
2154 if (nesting == 10)
2155 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00002156 emsg(_(e_autocommand_nesting_too_deep));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002157 goto BYPASS_AU;
2158 }
2159
2160 /*
2161 * Check if these autocommands are disabled. Used when doing ":all" or
2162 * ":ball".
2163 */
2164 if ( (autocmd_no_enter
2165 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
2166 || (autocmd_no_leave
2167 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
2168 goto BYPASS_AU;
2169
Bram Moolenaarbb393d82022-12-09 12:21:50 +00002170 if (event == EVENT_CMDLINECHANGED)
2171 ++aucmd_cmdline_changed_count;
2172
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002173 /*
2174 * Save the autocmd_* variables and info about the current buffer.
2175 */
2176 save_autocmd_fname = autocmd_fname;
2177 save_autocmd_fname_full = autocmd_fname_full;
2178 save_autocmd_bufnr = autocmd_bufnr;
2179 save_autocmd_match = autocmd_match;
2180 save_autocmd_busy = autocmd_busy;
2181 save_autocmd_nested = autocmd_nested;
2182 save_changed = curbuf->b_changed;
2183 old_curbuf = curbuf;
2184
2185 /*
2186 * Set the file name to be used for <afile>.
2187 * Make a copy to avoid that changing a buffer name or directory makes it
2188 * invalid.
2189 */
2190 if (fname_io == NULL)
2191 {
2192 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
Bram Moolenaarbb393d82022-12-09 12:21:50 +00002193 || event == EVENT_OPTIONSET
Danek Duvalld7d56032024-01-14 20:19:59 +01002194 || event == EVENT_MODECHANGED
2195 || event == EVENT_TERMRESPONSEALL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002196 autocmd_fname = NULL;
2197 else if (fname != NULL && !ends_excmd(*fname))
2198 autocmd_fname = fname;
2199 else if (buf != NULL)
2200 autocmd_fname = buf->b_ffname;
2201 else
2202 autocmd_fname = NULL;
2203 }
2204 else
2205 autocmd_fname = fname_io;
2206 if (autocmd_fname != NULL)
2207 autocmd_fname = vim_strsave(autocmd_fname);
2208 autocmd_fname_full = FALSE; // call FullName_save() later
2209
2210 /*
2211 * Set the buffer number to be used for <abuf>.
2212 */
2213 if (buf == NULL)
2214 autocmd_bufnr = 0;
2215 else
2216 autocmd_bufnr = buf->b_fnum;
2217
2218 /*
2219 * When the file name is NULL or empty, use the file name of buffer "buf".
2220 * Always use the full path of the file name to match with, in case
2221 * "allow_dirs" is set.
2222 */
2223 if (fname == NULL || *fname == NUL)
2224 {
2225 if (buf == NULL)
2226 fname = NULL;
2227 else
2228 {
2229#ifdef FEAT_SYN_HL
2230 if (event == EVENT_SYNTAX)
2231 fname = buf->b_p_syn;
2232 else
2233#endif
2234 if (event == EVENT_FILETYPE)
2235 fname = buf->b_p_ft;
2236 else
2237 {
2238 if (buf->b_sfname != NULL)
2239 sfname = vim_strsave(buf->b_sfname);
2240 fname = buf->b_ffname;
2241 }
2242 }
2243 if (fname == NULL)
2244 fname = (char_u *)"";
2245 fname = vim_strsave(fname); // make a copy, so we can change it
2246 }
2247 else
2248 {
2249 sfname = vim_strsave(fname);
2250 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002251 // ColorScheme, QuickFixCmd*, DirChanged and similar.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002252 if (event == EVENT_FILETYPE
2253 || event == EVENT_SYNTAX
2254 || event == EVENT_CMDLINECHANGED
2255 || event == EVENT_CMDLINEENTER
2256 || event == EVENT_CMDLINELEAVE
Shougo Matsushitad0952142024-06-20 22:05:16 +02002257 || event == EVENT_CURSORMOVEDC
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002258 || event == EVENT_CMDWINENTER
2259 || event == EVENT_CMDWINLEAVE
2260 || event == EVENT_CMDUNDEFINED
2261 || event == EVENT_FUNCUNDEFINED
Shougo Matsushita83678842024-07-11 22:05:12 +02002262 || event == EVENT_KEYINPUTPRE
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002263 || event == EVENT_REMOTEREPLY
2264 || event == EVENT_SPELLFILEMISSING
2265 || event == EVENT_QUICKFIXCMDPRE
2266 || event == EVENT_COLORSCHEME
2267 || event == EVENT_COLORSCHEMEPRE
2268 || event == EVENT_OPTIONSET
2269 || event == EVENT_QUICKFIXCMDPOST
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002270 || event == EVENT_DIRCHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002271 || event == EVENT_DIRCHANGEDPRE
naohiro ono23beefe2021-11-13 12:38:49 +00002272 || event == EVENT_MODECHANGED
zeertzjqc601d982022-10-10 13:46:15 +01002273 || event == EVENT_MENUPOPUP
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002274 || event == EVENT_USER
LemonBoy09371822022-04-08 15:18:45 +01002275 || event == EVENT_WINCLOSED
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00002276 || event == EVENT_WINRESIZED
Danek Duvalld7d56032024-01-14 20:19:59 +01002277 || event == EVENT_WINSCROLLED
2278 || event == EVENT_TERMRESPONSEALL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002279 {
2280 fname = vim_strsave(fname);
2281 autocmd_fname_full = TRUE; // don't expand it later
2282 }
2283 else
2284 fname = FullName_save(fname, FALSE);
2285 }
2286 if (fname == NULL) // out of memory
2287 {
2288 vim_free(sfname);
2289 retval = FALSE;
2290 goto BYPASS_AU;
2291 }
2292
2293#ifdef BACKSLASH_IN_FILENAME
2294 /*
2295 * Replace all backslashes with forward slashes. This makes the
2296 * autocommand patterns portable between Unix and MS-DOS.
2297 */
2298 if (sfname != NULL)
2299 forward_slash(sfname);
2300 forward_slash(fname);
2301#endif
2302
2303#ifdef VMS
2304 // remove version for correct match
2305 if (sfname != NULL)
2306 vms_remove_version(sfname);
2307 vms_remove_version(fname);
2308#endif
2309
2310 /*
2311 * Set the name to be used for <amatch>.
2312 */
2313 autocmd_match = fname;
2314
2315
2316 // Don't redraw while doing autocommands.
2317 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002318
2319 // name and lnum are filled in later
2320 estack_push(ETYPE_AUCMD, NULL, 0);
ichizok7e5fe382023-04-15 13:17:50 +01002321 ESTACK_CHECK_SETUP;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002322
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002323 save_current_sctx = current_sctx;
2324
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002325#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002326# ifdef FEAT_PROFILE
2327 if (do_profiling == PROF_YES)
2328 prof_child_enter(&wait_time); // doesn't count for the caller itself
2329# endif
2330
2331 // Don't use local function variables, if called from a function.
2332 save_funccal(&funccal_entry);
2333#endif
2334
2335 /*
2336 * When starting to execute autocommands, save the search patterns.
2337 */
2338 if (!autocmd_busy)
2339 {
2340 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002341 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002342 {
2343 saveRedobuff(&save_redo);
2344 did_save_redobuff = TRUE;
2345 }
zeertzjq5bf6c212024-03-31 18:41:27 +02002346 curbuf->b_did_filetype = curbuf->b_keep_filetype;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002347 }
2348
2349 /*
2350 * Note that we are applying autocmds. Some commands need to know.
2351 */
2352 autocmd_busy = TRUE;
2353 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2354 ++nesting; // see matching decrement below
2355
2356 // Remember that FileType was triggered. Used for did_filetype().
2357 if (event == EVENT_FILETYPE)
zeertzjq5bf6c212024-03-31 18:41:27 +02002358 curbuf->b_did_filetype = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002359
2360 tail = gettail(fname);
2361
2362 // Find first autocommand that matches
LemonBoyeca7c602022-04-14 15:39:43 +01002363 CLEAR_FIELD(patcmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002364 patcmd.curpat = first_autopat[(int)event];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002365 patcmd.group = group;
2366 patcmd.fname = fname;
2367 patcmd.sfname = sfname;
2368 patcmd.tail = tail;
2369 patcmd.event = event;
2370 patcmd.arg_bufnr = autocmd_bufnr;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002371 auto_next_pat(&patcmd, FALSE);
2372
2373 // found one, start executing the autocommands
2374 if (patcmd.curpat != NULL)
2375 {
2376 // add to active_apc_list
2377 patcmd.next = active_apc_list;
2378 active_apc_list = &patcmd;
2379
2380#ifdef FEAT_EVAL
2381 // set v:cmdarg (only when there is a matching pattern)
2382 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2383 if (eap != NULL)
2384 {
2385 save_cmdarg = set_cmdarg(eap, NULL);
2386 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2387 }
2388 else
2389 save_cmdarg = NULL; // avoid gcc warning
2390#endif
2391 retval = TRUE;
2392 // mark the last pattern, to avoid an endless loop when more patterns
2393 // are added when executing autocommands
2394 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2395 ap->last = FALSE;
2396 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002397
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002398 // Make sure cursor and topline are valid. The first time the current
2399 // values are saved, restored by reset_lnums(). When nested only the
2400 // values are corrected when needed.
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002401 if (nesting == 1)
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002402 check_lnums(TRUE);
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002403 else
2404 check_lnums_nested(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002405
Christian Brabandt590aae32023-06-25 22:34:22 +01002406 int save_did_emsg = did_emsg;
2407 int save_ex_pressedreturn = get_pressedreturn();
ichizokc3f91c02021-12-17 09:44:33 +00002408
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002409 do_cmdline(NULL, getnextac, (void *)&patcmd,
2410 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002411
ichizokc3f91c02021-12-17 09:44:33 +00002412 did_emsg += save_did_emsg;
Christian Brabandt590aae32023-06-25 22:34:22 +01002413 set_pressedreturn(save_ex_pressedreturn);
ichizokc3f91c02021-12-17 09:44:33 +00002414
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002415 if (nesting == 1)
2416 // restore cursor and topline, unless they were changed
2417 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002418
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002419#ifdef FEAT_EVAL
2420 if (eap != NULL)
2421 {
2422 (void)set_cmdarg(NULL, save_cmdarg);
2423 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2424 }
2425#endif
2426 // delete from active_apc_list
2427 if (active_apc_list == &patcmd) // just in case
2428 active_apc_list = patcmd.next;
2429 }
2430
Bram Moolenaar79cdf022023-05-20 14:07:00 +01002431 if (RedrawingDisabled > 0)
2432 --RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002433 autocmd_busy = save_autocmd_busy;
2434 filechangeshell_busy = FALSE;
2435 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002436 vim_free(SOURCING_NAME);
ichizok7e5fe382023-04-15 13:17:50 +01002437 ESTACK_CHECK_NOW;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002438 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002439 vim_free(autocmd_fname);
2440 autocmd_fname = save_autocmd_fname;
2441 autocmd_fname_full = save_autocmd_fname_full;
2442 autocmd_bufnr = save_autocmd_bufnr;
2443 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002444 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002445#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002446 restore_funccal();
2447# ifdef FEAT_PROFILE
2448 if (do_profiling == PROF_YES)
2449 prof_child_exit(&wait_time);
2450# endif
2451#endif
2452 KeyTyped = save_KeyTyped;
2453 vim_free(fname);
2454 vim_free(sfname);
2455 --nesting; // see matching increment above
2456
2457 /*
2458 * When stopping to execute autocommands, restore the search patterns and
2459 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2460 * free any windows in the au_pending_free_win list.
2461 */
2462 if (!autocmd_busy)
2463 {
2464 restore_search_patterns();
2465 if (did_save_redobuff)
2466 restoreRedobuff(&save_redo);
zeertzjq5bf6c212024-03-31 18:41:27 +02002467 curbuf->b_did_filetype = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002468 while (au_pending_free_buf != NULL)
2469 {
2470 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002471
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002472 vim_free(au_pending_free_buf);
2473 au_pending_free_buf = b;
2474 }
2475 while (au_pending_free_win != NULL)
2476 {
2477 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002478
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002479 vim_free(au_pending_free_win);
2480 au_pending_free_win = w;
2481 }
2482 }
2483
2484 /*
2485 * Some events don't set or reset the Changed flag.
2486 * Check if still in the same buffer!
2487 */
2488 if (curbuf == old_curbuf
2489 && (event == EVENT_BUFREADPOST
2490 || event == EVENT_BUFWRITEPOST
2491 || event == EVENT_FILEAPPENDPOST
2492 || event == EVENT_VIMLEAVE
2493 || event == EVENT_VIMLEAVEPRE))
2494 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002495 if (curbuf->b_changed != save_changed)
2496 need_maketitle = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002497 curbuf->b_changed = save_changed;
2498 }
2499
2500 au_cleanup(); // may really delete removed patterns/commands now
2501
2502BYPASS_AU:
2503 // When wiping out a buffer make sure all its buffer-local autocommands
2504 // are deleted.
2505 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2506 aubuflocal_remove(buf);
2507
2508 if (retval == OK && event == EVENT_FILETYPE)
zeertzjq5bf6c212024-03-31 18:41:27 +02002509 curbuf->b_au_did_filetype = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002510
2511 return retval;
2512}
2513
2514# ifdef FEAT_EVAL
2515static char_u *old_termresponse = NULL;
Danek Duvalld7d56032024-01-14 20:19:59 +01002516static char_u *old_termu7resp = NULL;
2517static char_u *old_termblinkresp = NULL;
2518static char_u *old_termrbgresp = NULL;
2519static char_u *old_termrfgresp = NULL;
2520static char_u *old_termstyleresp = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002521# endif
2522
2523/*
2524 * Block triggering autocommands until unblock_autocmd() is called.
2525 * Can be used recursively, so long as it's symmetric.
2526 */
2527 void
2528block_autocmds(void)
2529{
2530# ifdef FEAT_EVAL
2531 // Remember the value of v:termresponse.
2532 if (autocmd_blocked == 0)
Danek Duvalld7d56032024-01-14 20:19:59 +01002533 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002534 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
Danek Duvalld7d56032024-01-14 20:19:59 +01002535 old_termu7resp = get_vim_var_str(VV_TERMU7RESP);
2536 old_termblinkresp = get_vim_var_str(VV_TERMBLINKRESP);
2537 old_termrbgresp = get_vim_var_str(VV_TERMRBGRESP);
2538 old_termrfgresp = get_vim_var_str(VV_TERMRFGRESP);
2539 old_termstyleresp = get_vim_var_str(VV_TERMSTYLERESP);
2540 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002541# endif
2542 ++autocmd_blocked;
2543}
2544
2545 void
2546unblock_autocmds(void)
2547{
2548 --autocmd_blocked;
2549
2550# ifdef FEAT_EVAL
Danek Duvalld7d56032024-01-14 20:19:59 +01002551 // When v:termresponse, etc, were set while autocommands were blocked,
2552 // trigger the autocommands now. Esp. useful when executing a shell
2553 // command during startup (vimdiff).
2554 if (autocmd_blocked == 0)
2555 {
2556 if (get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2557 {
2558 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2559 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"version", NULL, FALSE, curbuf);
2560 }
2561 if (get_vim_var_str(VV_TERMU7RESP) != old_termu7resp)
2562 {
2563 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"ambiguouswidth", NULL, FALSE, curbuf);
2564 }
2565 if (get_vim_var_str(VV_TERMBLINKRESP) != old_termblinkresp)
2566 {
2567 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"cursorblink", NULL, FALSE, curbuf);
2568 }
2569 if (get_vim_var_str(VV_TERMRBGRESP) != old_termrbgresp)
2570 {
2571 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"background", NULL, FALSE, curbuf);
2572 }
2573 if (get_vim_var_str(VV_TERMRFGRESP) != old_termrfgresp)
2574 {
2575 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"foreground", NULL, FALSE, curbuf);
2576 }
2577 if (get_vim_var_str(VV_TERMSTYLERESP) != old_termstyleresp)
2578 {
2579 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"cursorshape", NULL, FALSE, curbuf);
2580 }
2581 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002582# endif
2583}
2584
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002585 int
2586is_autocmd_blocked(void)
2587{
2588 return autocmd_blocked != 0;
2589}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002590
2591/*
2592 * Find next autocommand pattern that matches.
2593 */
2594 static void
2595auto_next_pat(
LemonBoyeca7c602022-04-14 15:39:43 +01002596 AutoPatCmd_T *apc,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002597 int stop_at_last) // stop when 'last' flag is set
2598{
2599 AutoPat *ap;
2600 AutoCmd *cp;
2601 char_u *name;
2602 char *s;
LemonBoyeca7c602022-04-14 15:39:43 +01002603 estack_T *entry;
2604 char_u *namep;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002605
LemonBoyeca7c602022-04-14 15:39:43 +01002606 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
2607
2608 // Clear the exestack entry for this ETYPE_AUCMD entry.
2609 VIM_CLEAR(entry->es_name);
2610 entry->es_info.aucmd = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002611
2612 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2613 {
2614 apc->curpat = NULL;
2615
2616 // Only use a pattern when it has not been removed, has commands and
2617 // the group matches. For buffer-local autocommands only check the
2618 // buffer number.
2619 if (ap->pat != NULL && ap->cmds != NULL
2620 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2621 {
2622 // execution-condition
2623 if (ap->buflocal_nr == 0
2624 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2625 apc->sfname, apc->tail, ap->allow_dirs))
2626 : ap->buflocal_nr == apc->arg_bufnr)
2627 {
2628 name = event_nr2name(apc->event);
2629 s = _("%s Autocommands for \"%s\"");
LemonBoyeca7c602022-04-14 15:39:43 +01002630 namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1);
2631 if (namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002632 {
LemonBoyeca7c602022-04-14 15:39:43 +01002633 sprintf((char *)namep, s, (char *)name, (char *)ap->pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002634 if (p_verbose >= 8)
2635 {
2636 verbose_enter();
LemonBoyeca7c602022-04-14 15:39:43 +01002637 smsg(_("Executing %s"), namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002638 verbose_leave();
2639 }
2640 }
2641
LemonBoyeca7c602022-04-14 15:39:43 +01002642 // Update the exestack entry for this autocmd.
2643 entry->es_name = namep;
2644 entry->es_info.aucmd = apc;
2645
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002646 apc->curpat = ap;
2647 apc->nextcmd = ap->cmds;
2648 // mark last command
2649 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2650 cp->last = FALSE;
2651 cp->last = TRUE;
2652 }
2653 line_breakcheck();
2654 if (apc->curpat != NULL) // found a match
2655 break;
2656 }
2657 if (stop_at_last && ap->last)
2658 break;
2659 }
2660}
2661
Dominique Pellee764d1b2023-03-12 21:20:59 +00002662#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002663/*
LemonBoyeca7c602022-04-14 15:39:43 +01002664 * Get the script context where autocommand "acp" is defined.
2665 */
2666 sctx_T *
2667acp_script_ctx(AutoPatCmd_T *acp)
2668{
2669 return &acp->script_ctx;
2670}
Dominique Pellee764d1b2023-03-12 21:20:59 +00002671#endif
LemonBoyeca7c602022-04-14 15:39:43 +01002672
2673/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002674 * Get next autocommand command.
2675 * Called by do_cmdline() to get the next line for ":if".
2676 * Returns allocated string, or NULL for end of autocommands.
2677 */
2678 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002679getnextac(
2680 int c UNUSED,
2681 void *cookie,
2682 int indent UNUSED,
2683 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002684{
LemonBoyeca7c602022-04-14 15:39:43 +01002685 AutoPatCmd_T *acp = (AutoPatCmd_T *)cookie;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002686 char_u *retval;
2687 AutoCmd *ac;
2688
2689 // Can be called again after returning the last line.
2690 if (acp->curpat == NULL)
2691 return NULL;
2692
2693 // repeat until we find an autocommand to execute
2694 for (;;)
2695 {
2696 // skip removed commands
2697 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2698 if (acp->nextcmd->last)
2699 acp->nextcmd = NULL;
2700 else
2701 acp->nextcmd = acp->nextcmd->next;
2702
2703 if (acp->nextcmd != NULL)
2704 break;
2705
2706 // at end of commands, find next pattern that matches
2707 if (acp->curpat->last)
2708 acp->curpat = NULL;
2709 else
2710 acp->curpat = acp->curpat->next;
2711 if (acp->curpat != NULL)
2712 auto_next_pat(acp, TRUE);
2713 if (acp->curpat == NULL)
2714 return NULL;
2715 }
2716
2717 ac = acp->nextcmd;
2718
2719 if (p_verbose >= 9)
2720 {
2721 verbose_enter_scroll();
2722 smsg(_("autocommand %s"), ac->cmd);
2723 msg_puts("\n"); // don't overwrite this either
2724 verbose_leave_scroll();
2725 }
2726 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002727 // Remove one-shot ("once") autocmd in anticipation of its execution.
2728 if (ac->once)
2729 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002730 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002731 current_sctx = ac->script_ctx;
LemonBoyeca7c602022-04-14 15:39:43 +01002732 acp->script_ctx = current_sctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002733 if (ac->last)
2734 acp->nextcmd = NULL;
2735 else
2736 acp->nextcmd = ac->next;
2737 return retval;
2738}
2739
2740/*
2741 * Return TRUE if there is a matching autocommand for "fname".
2742 * To account for buffer-local autocommands, function needs to know
2743 * in which buffer the file will be opened.
2744 */
2745 int
2746has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2747{
2748 AutoPat *ap;
2749 char_u *fname;
2750 char_u *tail = gettail(sfname);
2751 int retval = FALSE;
2752
2753 fname = FullName_save(sfname, FALSE);
2754 if (fname == NULL)
2755 return FALSE;
2756
2757#ifdef BACKSLASH_IN_FILENAME
2758 /*
2759 * Replace all backslashes with forward slashes. This makes the
2760 * autocommand patterns portable between Unix and MS-DOS.
2761 */
2762 sfname = vim_strsave(sfname);
2763 if (sfname != NULL)
2764 forward_slash(sfname);
2765 forward_slash(fname);
2766#endif
2767
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002768 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002769 if (ap->pat != NULL && ap->cmds != NULL
2770 && (ap->buflocal_nr == 0
2771 ? match_file_pat(NULL, &ap->reg_prog,
2772 fname, sfname, tail, ap->allow_dirs)
2773 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2774 ))
2775 {
2776 retval = TRUE;
2777 break;
2778 }
2779
2780 vim_free(fname);
2781#ifdef BACKSLASH_IN_FILENAME
2782 vim_free(sfname);
2783#endif
2784
2785 return retval;
2786}
2787
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002788/*
2789 * Function given to ExpandGeneric() to obtain the list of autocommand group
2790 * names.
2791 */
2792 char_u *
2793get_augroup_name(expand_T *xp UNUSED, int idx)
2794{
2795 if (idx == augroups.ga_len) // add "END" add the end
2796 return (char_u *)"END";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002797 if (idx < 0 || idx >= augroups.ga_len) // end of list
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002798 return NULL;
2799 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2800 // skip deleted entries
2801 return (char_u *)"";
2802 return AUGROUP_NAME(idx); // return a name
2803}
2804
2805static int include_groups = FALSE;
2806
2807 char_u *
2808set_context_in_autocmd(
2809 expand_T *xp,
2810 char_u *arg,
2811 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2812{
2813 char_u *p;
2814 int group;
2815
2816 // check for a group name, skip it if present
2817 include_groups = FALSE;
2818 p = arg;
2819 group = au_get_grouparg(&arg);
2820 if (group == AUGROUP_ERROR)
2821 return NULL;
2822 // If there only is a group name that's what we expand.
2823 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2824 {
2825 arg = p;
2826 group = AUGROUP_ALL;
2827 }
2828
2829 // skip over event name
2830 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2831 if (*p == ',')
2832 arg = p + 1;
2833 if (*p == NUL)
2834 {
2835 if (group == AUGROUP_ALL)
2836 include_groups = TRUE;
2837 xp->xp_context = EXPAND_EVENTS; // expand event name
2838 xp->xp_pattern = arg;
2839 return NULL;
2840 }
2841
2842 // skip over pattern
2843 arg = skipwhite(p);
2844 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2845 arg++;
2846 if (*arg)
2847 return arg; // expand (next) command
2848
2849 if (doautocmd)
2850 xp->xp_context = EXPAND_FILES; // expand file names
2851 else
2852 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2853 return NULL;
2854}
2855
2856/*
2857 * Function given to ExpandGeneric() to obtain the list of event names.
2858 */
2859 char_u *
2860get_event_name(expand_T *xp UNUSED, int idx)
2861{
John Marriott78d742a2024-04-02 20:26:01 +02002862 int i;
2863
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002864 if (idx < augroups.ga_len) // First list group names, if wanted
2865 {
2866 if (!include_groups || AUGROUP_NAME(idx) == NULL
2867 || AUGROUP_NAME(idx) == get_deleted_augroup())
2868 return (char_u *)""; // skip deleted entries
2869 return AUGROUP_NAME(idx); // return a name
2870 }
John Marriott78d742a2024-04-02 20:26:01 +02002871
2872 i = idx - augroups.ga_len;
Luuk van Baalb7147f82025-02-08 18:52:39 +01002873 if (i < 0 || i >= NUM_EVENTS)
John Marriott78d742a2024-04-02 20:26:01 +02002874 return NULL;
2875
John Marriott8d4477e2024-11-02 15:59:01 +01002876 return event_tab[i].value.string;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002877}
2878
Yee Cheng Chin900894b2023-09-29 20:42:32 +02002879/*
2880 * Function given to ExpandGeneric() to obtain the list of event names. Don't
2881 * include groups.
2882 */
2883 char_u *
Luuk van Baalb7147f82025-02-08 18:52:39 +01002884get_event_name_no_group(expand_T *xp UNUSED, int idx, int win)
Yee Cheng Chin900894b2023-09-29 20:42:32 +02002885{
Luuk van Baalb7147f82025-02-08 18:52:39 +01002886 if (idx < 0 || idx >= NUM_EVENTS)
John Marriott78d742a2024-04-02 20:26:01 +02002887 return NULL;
2888
Luuk van Baalb7147f82025-02-08 18:52:39 +01002889 if (!win)
2890 return event_tab[idx].value.string;
2891
2892 // Need to check subset of allowed values for 'eventignorewin'.
2893 int j = 0;
2894 for (int i = 0; i < NUM_EVENTS; ++i)
2895 {
2896 j += event_tab[i].key <= 0;
2897 if (j == idx + 1)
2898 return event_tab[i].value.string;
2899 }
2900
2901 return NULL;
Yee Cheng Chin900894b2023-09-29 20:42:32 +02002902}
2903
Jim Zhou5606ca52025-03-13 21:58:25 +01002904/*
2905 * Return TRUE when there is a TabClosedPre autocommand defined.
2906 */
2907 int
2908has_tabclosedpre(void)
2909{
2910 return (first_autopat[(int)EVENT_TABCLOSEDPRE] != NULL);
2911}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002912
2913#if defined(FEAT_EVAL) || defined(PROTO)
2914/*
2915 * Return TRUE if autocmd is supported.
2916 */
2917 int
2918autocmd_supported(char_u *name)
2919{
2920 char_u *p;
2921
2922 return (event_name2nr(name, &p) != NUM_EVENTS);
2923}
2924
2925/*
2926 * Return TRUE if an autocommand is defined for a group, event and
2927 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2928 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2929 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2930 * Used for:
2931 * exists("#Group") or
2932 * exists("#Group#Event") or
2933 * exists("#Group#Event#pat") or
2934 * exists("#Event") or
2935 * exists("#Event#pat")
2936 */
2937 int
2938au_exists(char_u *arg)
2939{
2940 char_u *arg_save;
2941 char_u *pattern = NULL;
2942 char_u *event_name;
2943 char_u *p;
2944 event_T event;
2945 AutoPat *ap;
2946 buf_T *buflocal_buf = NULL;
2947 int group;
2948 int retval = FALSE;
2949
2950 // Make a copy so that we can change the '#' chars to a NUL.
2951 arg_save = vim_strsave(arg);
2952 if (arg_save == NULL)
2953 return FALSE;
2954 p = vim_strchr(arg_save, '#');
2955 if (p != NULL)
2956 *p++ = NUL;
2957
2958 // First, look for an autocmd group name
2959 group = au_find_group(arg_save);
2960 if (group == AUGROUP_ERROR)
2961 {
2962 // Didn't match a group name, assume the first argument is an event.
2963 group = AUGROUP_ALL;
2964 event_name = arg_save;
2965 }
2966 else
2967 {
2968 if (p == NULL)
2969 {
2970 // "Group": group name is present and it's recognized
2971 retval = TRUE;
2972 goto theend;
2973 }
2974
2975 // Must be "Group#Event" or "Group#Event#pat".
2976 event_name = p;
2977 p = vim_strchr(event_name, '#');
2978 if (p != NULL)
2979 *p++ = NUL; // "Group#Event#pat"
2980 }
2981
2982 pattern = p; // "pattern" is NULL when there is no pattern
2983
2984 // find the index (enum) for the event name
2985 event = event_name2nr(event_name, &p);
2986
2987 // return FALSE if the event name is not recognized
2988 if (event == NUM_EVENTS)
2989 goto theend;
2990
2991 // Find the first autocommand for this event.
2992 // If there isn't any, return FALSE;
2993 // If there is one and no pattern given, return TRUE;
2994 ap = first_autopat[(int)event];
2995 if (ap == NULL)
2996 goto theend;
2997
2998 // if pattern is "<buffer>", special handling is needed which uses curbuf
2999 // for pattern "<buffer=N>, fnamecmp() will work fine
3000 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
3001 buflocal_buf = curbuf;
3002
3003 // Check if there is an autocommand with the given pattern.
3004 for ( ; ap != NULL; ap = ap->next)
3005 // only use a pattern when it has not been removed and has commands.
3006 // For buffer-local autocommands, fnamecmp() works fine.
3007 if (ap->pat != NULL && ap->cmds != NULL
3008 && (group == AUGROUP_ALL || ap->group == group)
3009 && (pattern == NULL
3010 || (buflocal_buf == NULL
3011 ? fnamecmp(ap->pat, pattern) == 0
3012 : ap->buflocal_nr == buflocal_buf->b_fnum)))
3013 {
3014 retval = TRUE;
3015 break;
3016 }
3017
3018theend:
3019 vim_free(arg_save);
3020 return retval;
3021}
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003022
3023/*
3024 * autocmd_add() and autocmd_delete() functions
3025 */
3026 static void
3027autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
3028{
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003029 list_T *aucmd_list;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003030 listitem_T *li;
3031 dict_T *event_dict;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003032 dictitem_T *di;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003033 char_u *event_name = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003034 list_T *event_list;
3035 listitem_T *eli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003036 event_T event;
3037 char_u *group_name = NULL;
3038 int group;
3039 char_u *pat = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003040 list_T *pat_list;
3041 listitem_T *pli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003042 char_u *cmd = NULL;
3043 char_u *end;
3044 int once;
3045 int nested;
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01003046 int replace; // replace the cmd for a group/event
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003047 int retval = VVAL_TRUE;
3048 int save_augroup = current_augroup;
3049
3050 rettv->v_type = VAR_BOOL;
3051 rettv->vval.v_number = VVAL_FALSE;
3052
3053 if (check_for_list_arg(argvars, 0) == FAIL)
3054 return;
3055
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003056 aucmd_list = argvars[0].vval.v_list;
3057 if (aucmd_list == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003058 return;
3059
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003060 FOR_ALL_LIST_ITEMS(aucmd_list, li)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003061 {
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003062 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003063 VIM_CLEAR(cmd);
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003064 event_name = NULL;
3065 event_list = NULL;
3066 pat = NULL;
3067 pat_list = NULL;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003068
3069 if (li->li_tv.v_type != VAR_DICT)
3070 continue;
3071
3072 event_dict = li->li_tv.vval.v_dict;
3073 if (event_dict == NULL)
3074 continue;
3075
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003076 di = dict_find(event_dict, (char_u *)"event", -1);
3077 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003078 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003079 if (di->di_tv.v_type == VAR_STRING)
3080 {
3081 event_name = di->di_tv.vval.v_string;
3082 if (event_name == NULL)
3083 {
3084 emsg(_(e_string_required));
3085 continue;
3086 }
3087 }
3088 else if (di->di_tv.v_type == VAR_LIST)
3089 {
3090 event_list = di->di_tv.vval.v_list;
3091 if (event_list == NULL)
3092 {
3093 emsg(_(e_list_required));
3094 continue;
3095 }
3096 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003097 else
3098 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003099 emsg(_(e_string_or_list_expected));
3100 continue;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003101 }
3102 }
3103
Bram Moolenaard61efa52022-07-23 09:52:04 +01003104 group_name = dict_get_string(event_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003105 if (group_name == NULL || *group_name == NUL)
3106 // if the autocmd group name is not specified, then use the current
3107 // autocmd group
3108 group = current_augroup;
3109 else
3110 {
3111 group = au_find_group(group_name);
3112 if (group == AUGROUP_ERROR)
3113 {
3114 if (delete)
3115 {
3116 semsg(_(e_no_such_group_str), group_name);
3117 retval = VVAL_FALSE;
3118 break;
3119 }
3120 // group is not found, create it now
3121 group = au_new_group(group_name);
3122 if (group == AUGROUP_ERROR)
3123 {
3124 semsg(_(e_no_such_group_str), group_name);
3125 retval = VVAL_FALSE;
3126 break;
3127 }
3128
3129 current_augroup = group;
3130 }
3131 }
3132
3133 // if a buffer number is specified, then generate a pattern of the form
3134 // "<buffer=n>. Otherwise, use the pattern supplied by the user.
3135 if (dict_has_key(event_dict, "bufnr"))
3136 {
3137 varnumber_T bnum;
3138
Bram Moolenaard61efa52022-07-23 09:52:04 +01003139 bnum = dict_get_number_def(event_dict, "bufnr", -1);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003140 if (bnum == -1)
3141 continue;
3142
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003143 vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
3144 pat = IObuff;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003145 }
3146 else
3147 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003148 di = dict_find(event_dict, (char_u *)"pattern", -1);
3149 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003150 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003151 if (di->di_tv.v_type == VAR_STRING)
3152 {
3153 pat = di->di_tv.vval.v_string;
3154 if (pat == NULL)
3155 {
3156 emsg(_(e_string_required));
3157 continue;
3158 }
3159 }
3160 else if (di->di_tv.v_type == VAR_LIST)
3161 {
3162 pat_list = di->di_tv.vval.v_list;
3163 if (pat_list == NULL)
3164 {
3165 emsg(_(e_list_required));
3166 continue;
3167 }
3168 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003169 else
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003170 {
3171 emsg(_(e_string_or_list_expected));
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003172 continue;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003173 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003174 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003175 else if (delete)
3176 pat = (char_u *)"";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003177 }
3178
Bram Moolenaard61efa52022-07-23 09:52:04 +01003179 once = dict_get_bool(event_dict, "once", FALSE);
3180 nested = dict_get_bool(event_dict, "nested", FALSE);
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01003181 // if 'replace' is true, then remove all the commands associated with
3182 // this autocmd event/group and add the new command.
Bram Moolenaard61efa52022-07-23 09:52:04 +01003183 replace = dict_get_bool(event_dict, "replace", FALSE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003184
Bram Moolenaard61efa52022-07-23 09:52:04 +01003185 cmd = dict_get_string(event_dict, "cmd", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003186 if (cmd == NULL)
3187 {
3188 if (delete)
3189 cmd = vim_strsave((char_u *)"");
3190 else
3191 continue;
3192 }
3193
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003194 if (delete && (event_name == NULL
3195 || (event_name[0] == '*' && event_name[1] == NUL)))
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003196 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003197 // if the event name is not specified or '*', delete all the events
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003198 for (event = (event_T)0; (int)event < NUM_EVENTS;
3199 event = (event_T)((int)event + 1))
3200 {
3201 if (do_autocmd_event(event, pat, once, nested, cmd, delete,
3202 group, 0) == FAIL)
3203 {
3204 retval = VVAL_FALSE;
3205 break;
3206 }
3207 }
3208 }
3209 else
3210 {
Bram Moolenaar968443e2022-05-27 21:16:34 +01003211 char_u *p = NULL;
3212
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003213 eli = NULL;
3214 end = NULL;
3215 while (TRUE)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003216 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003217 if (event_list != NULL)
3218 {
3219 if (eli == NULL)
3220 eli = event_list->lv_first;
3221 else
3222 eli = eli->li_next;
3223 if (eli == NULL)
3224 break;
3225 if (eli->li_tv.v_type != VAR_STRING
Bram Moolenaar882476a2022-06-01 16:02:38 +01003226 || (p = eli->li_tv.vval.v_string) == NULL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003227 {
3228 emsg(_(e_string_required));
Bram Moolenaar882476a2022-06-01 16:02:38 +01003229 break;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003230 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003231 }
3232 else
3233 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003234 if (p == NULL)
3235 p = event_name;
3236 if (p == NULL || *p == NUL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003237 break;
3238 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003239
3240 event = event_name2nr(p, &end);
3241 if (event == NUM_EVENTS || *end != NUL)
3242 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003243 // this also catches something following a valid event name
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003244 semsg(_(e_no_such_event_str), p);
3245 retval = VVAL_FALSE;
3246 break;
3247 }
3248 if (pat != NULL)
3249 {
3250 if (do_autocmd_event(event, pat, once, nested, cmd,
3251 delete | replace, group, 0) == FAIL)
3252 {
3253 retval = VVAL_FALSE;
3254 break;
3255 }
3256 }
3257 else if (pat_list != NULL)
3258 {
3259 FOR_ALL_LIST_ITEMS(pat_list, pli)
3260 {
3261 if (pli->li_tv.v_type != VAR_STRING
3262 || pli->li_tv.vval.v_string == NULL)
3263 {
3264 emsg(_(e_string_required));
3265 continue;
3266 }
3267 if (do_autocmd_event(event,
3268 pli->li_tv.vval.v_string, once, nested,
3269 cmd, delete | replace, group, 0) ==
3270 FAIL)
3271 {
3272 retval = VVAL_FALSE;
3273 break;
3274 }
3275 }
3276 if (retval == VVAL_FALSE)
3277 break;
3278 }
3279 if (event_name != NULL)
3280 p = end;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003281 }
3282 }
3283
3284 // if only the autocmd group name is specified for delete and the
3285 // autocmd event, pattern and cmd are not specified, then delete the
3286 // autocmd group.
3287 if (delete && group_name != NULL &&
3288 (event_name == NULL || event_name[0] == NUL)
3289 && (pat == NULL || pat[0] == NUL)
3290 && (cmd == NULL || cmd[0] == NUL))
3291 au_del_group(group_name);
3292 }
3293
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003294 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003295 VIM_CLEAR(cmd);
3296
3297 current_augroup = save_augroup;
3298 rettv->vval.v_number = retval;
3299}
3300
3301/*
3302 * autocmd_add() function
3303 */
3304 void
3305f_autocmd_add(typval_T *argvars, typval_T *rettv)
3306{
3307 autocmd_add_or_delete(argvars, rettv, FALSE);
3308}
3309
3310/*
3311 * autocmd_delete() function
3312 */
3313 void
3314f_autocmd_delete(typval_T *argvars, typval_T *rettv)
3315{
3316 autocmd_add_or_delete(argvars, rettv, TRUE);
3317}
3318
3319/*
3320 * autocmd_get() function
3321 * Returns a List of autocmds.
3322 */
3323 void
3324f_autocmd_get(typval_T *argvars, typval_T *rettv)
3325{
3326 event_T event_arg = NUM_EVENTS;
3327 event_T event;
3328 AutoPat *ap;
3329 AutoCmd *ac;
3330 list_T *event_list;
3331 dict_T *event_dict;
3332 char_u *event_name = NULL;
3333 char_u *pat = NULL;
3334 char_u *name = NULL;
3335 int group = AUGROUP_ALL;
3336
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01003337 if (rettv_list_alloc(rettv) == FAIL)
3338 return;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003339 if (check_for_opt_dict_arg(argvars, 0) == FAIL)
3340 return;
3341
3342 if (argvars[0].v_type == VAR_DICT)
3343 {
3344 // return only the autocmds in the specified group
3345 if (dict_has_key(argvars[0].vval.v_dict, "group"))
3346 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003347 name = dict_get_string(argvars[0].vval.v_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003348 if (name == NULL)
3349 return;
3350
3351 if (*name == NUL)
3352 group = AUGROUP_DEFAULT;
3353 else
3354 {
3355 group = au_find_group(name);
3356 if (group == AUGROUP_ERROR)
3357 {
3358 semsg(_(e_no_such_group_str), name);
3359 vim_free(name);
3360 return;
3361 }
3362 }
3363 vim_free(name);
3364 }
3365
3366 // return only the autocmds for the specified event
3367 if (dict_has_key(argvars[0].vval.v_dict, "event"))
3368 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003369 name = dict_get_string(argvars[0].vval.v_dict, "event", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003370 if (name == NULL)
3371 return;
3372
3373 if (name[0] == '*' && name[1] == NUL)
3374 event_arg = NUM_EVENTS;
3375 else
3376 {
John Marriott78d742a2024-04-02 20:26:01 +02003377 keyvalue_T target;
3378 keyvalue_T *entry;
3379
3380 target.key = 0;
John Marriott8d4477e2024-11-02 15:59:01 +01003381 target.value.string = name;
3382 target.value.length = STRLEN(target.value.string);
3383 entry = (keyvalue_T *)bsearch(&target, &event_tab,
Luuk van Baalb7147f82025-02-08 18:52:39 +01003384 NUM_EVENTS, sizeof(event_tab[0]), cmp_keyvalue_value_ni);
John Marriott78d742a2024-04-02 20:26:01 +02003385 if (entry == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003386 {
3387 semsg(_(e_no_such_event_str), name);
3388 vim_free(name);
3389 return;
3390 }
Luuk van Baalb7147f82025-02-08 18:52:39 +01003391 event_arg = (event_T)abs(entry->key);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003392 }
3393 vim_free(name);
3394 }
3395
3396 // return only the autocmds for the specified pattern
3397 if (dict_has_key(argvars[0].vval.v_dict, "pattern"))
3398 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003399 pat = dict_get_string(argvars[0].vval.v_dict, "pattern", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003400 if (pat == NULL)
3401 return;
3402 }
3403 }
3404
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003405 event_list = rettv->vval.v_list;
3406
3407 // iterate through all the autocmd events
3408 for (event = (event_T)0; (int)event < NUM_EVENTS;
3409 event = (event_T)((int)event + 1))
3410 {
3411 if (event_arg != NUM_EVENTS && event != event_arg)
3412 continue;
3413
3414 event_name = event_nr2name(event);
3415
3416 // iterate through all the patterns for this autocmd event
3417 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
3418 {
3419 char_u *group_name;
3420
zeertzjq2d1d5c62024-06-09 16:44:33 +02003421 if (ap->pat == NULL) // pattern has been removed
3422 continue;
3423
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003424 if (group != AUGROUP_ALL && group != ap->group)
3425 continue;
3426
3427 if (pat != NULL && STRCMP(pat, ap->pat) != 0)
3428 continue;
3429
3430 group_name = get_augroup_name(NULL, ap->group);
3431
3432 // iterate through all the commands for this pattern and add one
3433 // item for each cmd.
3434 for (ac = ap->cmds; ac != NULL; ac = ac->next)
3435 {
3436 event_dict = dict_alloc();
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003437 if (event_dict == NULL
3438 || list_append_dict(event_list, event_dict) == FAIL)
Christian Brabandt29269a72024-04-16 22:44:31 +02003439 {
3440 vim_free(pat);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003441 return;
Christian Brabandt29269a72024-04-16 22:44:31 +02003442 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003443
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003444 if (dict_add_string(event_dict, "event", event_name) == FAIL
3445 || dict_add_string(event_dict, "group",
3446 group_name == NULL ? (char_u *)""
3447 : group_name) == FAIL
3448 || (ap->buflocal_nr != 0
3449 && (dict_add_number(event_dict, "bufnr",
3450 ap->buflocal_nr) == FAIL))
3451 || dict_add_string(event_dict, "pattern",
3452 ap->pat) == FAIL
3453 || dict_add_string(event_dict, "cmd", ac->cmd) == FAIL
3454 || dict_add_bool(event_dict, "once", ac->once) == FAIL
3455 || dict_add_bool(event_dict, "nested",
3456 ac->nested) == FAIL)
Christian Brabandt29269a72024-04-16 22:44:31 +02003457 {
3458 vim_free(pat);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003459 return;
Christian Brabandt29269a72024-04-16 22:44:31 +02003460 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003461 }
3462 }
3463 }
3464
3465 vim_free(pat);
3466}
3467
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01003468#endif