blob: f0b573bf5d837a0251bd4c7bb41229454f90506a [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"),
183 KEYVALUE_ENTRY(EVENT_TABENTER, "TabEnter"),
184 KEYVALUE_ENTRY(EVENT_TABLEAVE, "TabLeave"),
185 KEYVALUE_ENTRY(EVENT_TABNEW, "TabNew"),
186 KEYVALUE_ENTRY(EVENT_TERMCHANGED, "TermChanged"),
187 KEYVALUE_ENTRY(EVENT_TERMINALOPEN, "TerminalOpen"),
188 KEYVALUE_ENTRY(EVENT_TERMINALWINOPEN, "TerminalWinOpen"),
189 KEYVALUE_ENTRY(EVENT_TERMRESPONSE, "TermResponse"),
190 KEYVALUE_ENTRY(EVENT_TERMRESPONSEALL, "TermResponseAll"),
Luuk van Baalb7147f82025-02-08 18:52:39 +0100191 KEYVALUE_ENTRY(-EVENT_TEXTCHANGED, "TextChanged"),
192 KEYVALUE_ENTRY(-EVENT_TEXTCHANGEDI, "TextChangedI"),
193 KEYVALUE_ENTRY(-EVENT_TEXTCHANGEDP, "TextChangedP"),
194 KEYVALUE_ENTRY(-EVENT_TEXTCHANGEDT, "TextChangedT"),
195 KEYVALUE_ENTRY(-EVENT_TEXTYANKPOST, "TextYankPost"),
John Marriott78d742a2024-04-02 20:26:01 +0200196 KEYVALUE_ENTRY(EVENT_USER, "User"),
197 KEYVALUE_ENTRY(EVENT_VIMENTER, "VimEnter"),
198 KEYVALUE_ENTRY(EVENT_VIMLEAVE, "VimLeave"),
199 KEYVALUE_ENTRY(EVENT_VIMLEAVEPRE, "VimLeavePre"),
200 KEYVALUE_ENTRY(EVENT_VIMRESIZED, "VimResized"),
201 KEYVALUE_ENTRY(EVENT_VIMRESUME, "VimResume"),
202 KEYVALUE_ENTRY(EVENT_VIMSUSPEND, "VimSuspend"),
Luuk van Baalb7147f82025-02-08 18:52:39 +0100203 KEYVALUE_ENTRY(-EVENT_WINCLOSED, "WinClosed"),
204 KEYVALUE_ENTRY(-EVENT_WINENTER, "WinEnter"),
205 KEYVALUE_ENTRY(-EVENT_WINLEAVE, "WinLeave"),
John Marriott78d742a2024-04-02 20:26:01 +0200206 KEYVALUE_ENTRY(EVENT_WINNEW, "WinNew"),
207 KEYVALUE_ENTRY(EVENT_WINNEWPRE, "WinNewPre"),
Luuk van Baalb7147f82025-02-08 18:52:39 +0100208 KEYVALUE_ENTRY(-EVENT_WINRESIZED, "WinResized"),
209 KEYVALUE_ENTRY(-EVENT_WINSCROLLED, "WinScrolled"),
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100210};
211
Luuk van Baalb7147f82025-02-08 18:52:39 +0100212static AutoPat *first_autopat[NUM_EVENTS] = { NULL };
213static AutoPat *last_autopat[NUM_EVENTS] = { NULL };
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100214
kylo252ae6f1d82022-02-16 19:24:07 +0000215#define AUGROUP_DEFAULT (-1) // default autocmd group
216#define AUGROUP_ERROR (-2) // erroneous autocmd group
217#define AUGROUP_ALL (-3) // all autocmd groups
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100218
219/*
220 * struct used to keep status while executing autocommands for an event.
221 */
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100222struct AutoPatCmd_S
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100223{
224 AutoPat *curpat; // next AutoPat to examine
225 AutoCmd *nextcmd; // next AutoCmd to execute
226 int group; // group being used
227 char_u *fname; // fname to match with
228 char_u *sfname; // sfname to match with
229 char_u *tail; // tail of fname
230 event_T event; // current event
LemonBoyeca7c602022-04-14 15:39:43 +0100231 sctx_T script_ctx; // script context where it is defined
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100232 int arg_bufnr; // Initially equal to <abuf>, set to zero when
233 // buf is deleted.
LemonBoyeca7c602022-04-14 15:39:43 +0100234 AutoPatCmd_T *next; // chain of active apc-s for auto-invalidation
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100235};
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100236
LemonBoyeca7c602022-04-14 15:39:43 +0100237static AutoPatCmd_T *active_apc_list = NULL; // stack of active autocommands
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100238
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200239// Macro to loop over all the patterns for an autocmd event
240#define FOR_ALL_AUTOCMD_PATTERNS(event, ap) \
241 for ((ap) = first_autopat[(int)(event)]; (ap) != NULL; (ap) = (ap)->next)
242
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100243/*
244 * augroups stores a list of autocmd group names.
245 */
246static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
247#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
Bram Moolenaarc667da52019-11-30 20:52:27 +0100248// use get_deleted_augroup() to get this
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100249static char_u *deleted_augroup = NULL;
250
251/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100252 * The ID of the current group. Group 0 is the default one.
253 */
254static int current_augroup = AUGROUP_DEFAULT;
255
Bram Moolenaarc667da52019-11-30 20:52:27 +0100256static int au_need_clean = FALSE; // need to delete marked patterns
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100257
John Marriott78d742a2024-04-02 20:26:01 +0200258static event_T event_name2nr(char_u *start, char_u **end);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100259static char_u *event_nr2name(event_T event);
260static int au_get_grouparg(char_u **argp);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200261static 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 +0100262static 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 +0100263static void auto_next_pat(AutoPatCmd_T *apc, int stop_at_last);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100264static int au_find_group(char_u *name);
265
266static event_T last_event;
267static int last_group;
Bram Moolenaarc667da52019-11-30 20:52:27 +0100268static int autocmd_blocked = 0; // block all autocmds
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100269
270 static char_u *
271get_deleted_augroup(void)
272{
273 if (deleted_augroup == NULL)
274 deleted_augroup = (char_u *)_("--Deleted--");
275 return deleted_augroup;
276}
277
278/*
279 * Show the autocommands for one AutoPat.
280 */
281 static void
282show_autocmd(AutoPat *ap, event_T event)
283{
284 AutoCmd *ac;
285
286 // Check for "got_int" (here and at various places below), which is set
287 // when "q" has been hit for the "--more--" prompt
288 if (got_int)
289 return;
290 if (ap->pat == NULL) // pattern has been removed
291 return;
292
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000293 // Make sure no info referenced by "ap" is cleared, e.g. when a timer
294 // clears an augroup. Jump to "theend" after this!
295 // "ap->pat" may be cleared anyway.
296 ++autocmd_busy;
297
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100298 msg_putchar('\n');
299 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000300 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100301 if (event != last_event || ap->group != last_group)
302 {
303 if (ap->group != AUGROUP_DEFAULT)
304 {
305 if (AUGROUP_NAME(ap->group) == NULL)
306 msg_puts_attr((char *)get_deleted_augroup(), HL_ATTR(HLF_E));
307 else
308 msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
309 msg_puts(" ");
310 }
311 msg_puts_attr((char *)event_nr2name(event), HL_ATTR(HLF_T));
312 last_event = event;
313 last_group = ap->group;
314 msg_putchar('\n');
315 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000316 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100317 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000318
319 if (ap->pat == NULL)
320 goto theend; // timer might have cleared the pattern or group
321
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100322 msg_col = 4;
323 msg_outtrans(ap->pat);
324
325 for (ac = ap->cmds; ac != NULL; ac = ac->next)
326 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100327 if (ac->cmd == NULL) // skip removed commands
328 continue;
329
330 if (msg_col >= 14)
331 msg_putchar('\n');
332 msg_col = 14;
333 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000334 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100335 msg_outtrans(ac->cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100336#ifdef FEAT_EVAL
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100337 if (p_verbose > 0)
338 last_set_msg(ac->script_ctx);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100339#endif
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100340 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000341 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100342 if (ac->next != NULL)
343 {
344 msg_putchar('\n');
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100345 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000346 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100347 }
348 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000349
350theend:
351 --autocmd_busy;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100352}
353
354/*
355 * Mark an autocommand pattern for deletion.
356 */
357 static void
358au_remove_pat(AutoPat *ap)
359{
360 VIM_CLEAR(ap->pat);
361 ap->buflocal_nr = -1;
362 au_need_clean = TRUE;
363}
364
365/*
366 * Mark all commands for a pattern for deletion.
367 */
368 static void
369au_remove_cmds(AutoPat *ap)
370{
371 AutoCmd *ac;
372
373 for (ac = ap->cmds; ac != NULL; ac = ac->next)
374 VIM_CLEAR(ac->cmd);
375 au_need_clean = TRUE;
376}
377
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200378// Delete one command from an autocmd pattern.
379static void au_del_cmd(AutoCmd *ac)
380{
381 VIM_CLEAR(ac->cmd);
382 au_need_clean = TRUE;
383}
384
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100385/*
386 * Cleanup autocommands and patterns that have been deleted.
387 * This is only done when not executing autocommands.
388 */
389 static void
390au_cleanup(void)
391{
392 AutoPat *ap, **prev_ap;
393 AutoCmd *ac, **prev_ac;
394 event_T event;
395
396 if (autocmd_busy || !au_need_clean)
397 return;
398
399 // loop over all events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100400 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100401 event = (event_T)((int)event + 1))
402 {
403 // loop over all autocommand patterns
404 prev_ap = &(first_autopat[(int)event]);
405 for (ap = *prev_ap; ap != NULL; ap = *prev_ap)
406 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200407 int has_cmd = FALSE;
408
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200409 // loop over all commands for this pattern
410 prev_ac = &(ap->cmds);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100411 for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
412 {
413 // remove the command if the pattern is to be deleted or when
414 // the command has been marked for deletion
415 if (ap->pat == NULL || ac->cmd == NULL)
416 {
417 *prev_ac = ac->next;
418 vim_free(ac->cmd);
419 vim_free(ac);
420 }
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200421 else
422 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200423 has_cmd = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100424 prev_ac = &(ac->next);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200425 }
426 }
427
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200428 if (ap->pat != NULL && !has_cmd)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200429 // Pattern was not marked for deletion, but all of its
430 // commands were. So mark the pattern for deletion.
431 au_remove_pat(ap);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100432
433 // remove the pattern if it has been marked for deletion
434 if (ap->pat == NULL)
435 {
436 if (ap->next == NULL)
437 {
438 if (prev_ap == &(first_autopat[(int)event]))
439 last_autopat[(int)event] = NULL;
440 else
441 // this depends on the "next" field being the first in
442 // the struct
443 last_autopat[(int)event] = (AutoPat *)prev_ap;
444 }
445 *prev_ap = ap->next;
446 vim_regfree(ap->reg_prog);
447 vim_free(ap);
448 }
449 else
450 prev_ap = &(ap->next);
451 }
452 }
453
454 au_need_clean = FALSE;
455}
456
457/*
458 * Called when buffer is freed, to remove/invalidate related buffer-local
459 * autocmds.
460 */
461 void
462aubuflocal_remove(buf_T *buf)
463{
LemonBoyeca7c602022-04-14 15:39:43 +0100464 AutoPat *ap;
465 event_T event;
466 AutoPatCmd_T *apc;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100467
468 // invalidate currently executing autocommands
469 for (apc = active_apc_list; apc; apc = apc->next)
470 if (buf->b_fnum == apc->arg_bufnr)
471 apc->arg_bufnr = 0;
472
473 // invalidate buflocals looping through events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100474 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100475 event = (event_T)((int)event + 1))
476 // loop over all autocommand patterns
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200477 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100478 if (ap->buflocal_nr == buf->b_fnum)
479 {
480 au_remove_pat(ap);
481 if (p_verbose >= 6)
482 {
483 verbose_enter();
484 smsg(_("auto-removing autocommand: %s <buffer=%d>"),
485 event_nr2name(event), buf->b_fnum);
486 verbose_leave();
487 }
488 }
489 au_cleanup();
490}
491
492/*
493 * Add an autocmd group name.
494 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
495 */
496 static int
497au_new_group(char_u *name)
498{
499 int i;
500
501 i = au_find_group(name);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100502 if (i != AUGROUP_ERROR)
503 return i;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100504
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100505 // the group doesn't exist yet, add it. First try using a free entry.
506 for (i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100507 if (AUGROUP_NAME(i) == NULL)
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100508 break;
509 if (i == augroups.ga_len && ga_grow(&augroups, 1) == FAIL)
510 return AUGROUP_ERROR;
511
512 AUGROUP_NAME(i) = vim_strsave(name);
513 if (AUGROUP_NAME(i) == NULL)
514 return AUGROUP_ERROR;
515 if (i == augroups.ga_len)
516 ++augroups.ga_len;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100517
518 return i;
519}
520
521 static void
522au_del_group(char_u *name)
523{
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100524 int i;
525 event_T event;
526 AutoPat *ap;
527 int in_use = FALSE;
528
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100529
530 i = au_find_group(name);
531 if (i == AUGROUP_ERROR) // the group doesn't exist
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100532 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100533 semsg(_(e_no_such_group_str), name);
534 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100535 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100536 if (i == current_augroup)
537 {
538 emsg(_(e_cannot_delete_current_group));
539 return;
540 }
541
542 for (event = (event_T)0; (int)event < NUM_EVENTS;
543 event = (event_T)((int)event + 1))
544 {
545 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
546 if (ap->group == i && ap->pat != NULL)
547 {
548 give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
549 in_use = TRUE;
550 event = NUM_EVENTS;
551 break;
552 }
553 }
554 vim_free(AUGROUP_NAME(i));
555 if (in_use)
556 AUGROUP_NAME(i) = get_deleted_augroup();
557 else
558 AUGROUP_NAME(i) = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100559}
560
561/*
562 * Find the ID of an autocmd group name.
563 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
564 */
565 static int
566au_find_group(char_u *name)
567{
568 int i;
569
570 for (i = 0; i < augroups.ga_len; ++i)
571 if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
572 && STRCMP(AUGROUP_NAME(i), name) == 0)
573 return i;
574 return AUGROUP_ERROR;
575}
576
577/*
578 * Return TRUE if augroup "name" exists.
579 */
580 int
581au_has_group(char_u *name)
582{
583 return au_find_group(name) != AUGROUP_ERROR;
584}
585
586/*
587 * ":augroup {name}".
588 */
589 void
590do_augroup(char_u *arg, int del_group)
591{
592 int i;
593
594 if (del_group)
595 {
596 if (*arg == NUL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000597 emsg(_(e_argument_required));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100598 else
599 au_del_group(arg);
600 }
601 else if (STRICMP(arg, "end") == 0) // ":aug end": back to group 0
602 current_augroup = AUGROUP_DEFAULT;
603 else if (*arg) // ":aug xxx": switch to group xxx
604 {
605 i = au_new_group(arg);
606 if (i != AUGROUP_ERROR)
607 current_augroup = i;
608 }
609 else // ":aug": list the group names
610 {
611 msg_start();
612 for (i = 0; i < augroups.ga_len; ++i)
613 {
614 if (AUGROUP_NAME(i) != NULL)
615 {
616 msg_puts((char *)AUGROUP_NAME(i));
617 msg_puts(" ");
618 }
619 }
620 msg_clr_eos();
621 msg_end();
622 }
623}
624
Bram Moolenaare76062c2022-11-28 18:51:43 +0000625 void
626autocmd_init(void)
627{
628 CLEAR_FIELD(aucmd_win);
629}
630
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100631#if defined(EXITFREE) || defined(PROTO)
632 void
633free_all_autocmds(void)
634{
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100635 char_u *s;
636
637 for (current_augroup = -1; current_augroup < augroups.ga_len;
638 ++current_augroup)
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200639 do_autocmd(NULL, (char_u *)"", TRUE);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100640
Bram Moolenaare76062c2022-11-28 18:51:43 +0000641 for (int i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100642 {
643 s = ((char_u **)(augroups.ga_data))[i];
644 if (s != get_deleted_augroup())
645 vim_free(s);
646 }
647 ga_clear(&augroups);
Bram Moolenaare76062c2022-11-28 18:51:43 +0000648
Bram Moolenaar84497cd2022-11-28 20:34:52 +0000649 // aucmd_win[] is freed in win_free_all()
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100650}
651#endif
652
653/*
Bram Moolenaare76062c2022-11-28 18:51:43 +0000654 * Return TRUE if "win" is an active entry in aucmd_win[].
655 */
656 int
657is_aucmd_win(win_T *win)
658{
659 for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
660 if (aucmd_win[i].auc_win_used && aucmd_win[i].auc_win == win)
661 return TRUE;
662 return FALSE;
663}
664
665/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100666 * Return the event number for event name "start".
667 * Return NUM_EVENTS if the event name was not found.
668 * Return a pointer to the next event name in "end".
669 */
670 static event_T
671event_name2nr(char_u *start, char_u **end)
672{
673 char_u *p;
John Marriott78d742a2024-04-02 20:26:01 +0200674 keyvalue_T target;
675 keyvalue_T *entry;
676 static keyvalue_T *bufnewfile = &event_tab[BUFNEWFILE_INDEX];
677 static keyvalue_T *bufread = &event_tab[BUFREAD_INDEX];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100678
679 // the event name ends with end of line, '|', a blank or a comma
680 for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
681 ;
John Marriott78d742a2024-04-02 20:26:01 +0200682
683 target.key = 0;
John Marriott8d4477e2024-11-02 15:59:01 +0100684 target.value.string = start;
685 target.value.length = (size_t)(p - start);
John Marriott78d742a2024-04-02 20:26:01 +0200686
687 // special cases:
688 // BufNewFile and BufRead are searched for ALOT (especially at startup)
689 // so we check for them first.
690 if (cmp_keyvalue_value_ni(&target, bufnewfile) == 0)
691 entry = bufnewfile;
Luuk van Baalb7147f82025-02-08 18:52:39 +0100692 else if (cmp_keyvalue_value_ni(&target, bufread) == 0)
John Marriott78d742a2024-04-02 20:26:01 +0200693 entry = bufread;
694 else
Luuk van Baalb7147f82025-02-08 18:52:39 +0100695 entry = (keyvalue_T *)bsearch(&target, &event_tab, NUM_EVENTS,
696 sizeof(event_tab[0]), cmp_keyvalue_value_ni);
John Marriott78d742a2024-04-02 20:26:01 +0200697
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100698 if (*p == ',')
699 ++p;
700 *end = p;
John Marriott78d742a2024-04-02 20:26:01 +0200701
Luuk van Baalb7147f82025-02-08 18:52:39 +0100702 return (entry == NULL) ? NUM_EVENTS : (event_T)abs(entry->key);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100703}
704
705/*
706 * Return the name for event "event".
707 */
708 static char_u *
709event_nr2name(event_T event)
710{
711 int i;
John Marriott78d742a2024-04-02 20:26:01 +0200712#define CACHE_SIZE 12
713 static int cache_tab[CACHE_SIZE];
714 static int cache_last_index = -1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100715
John Marriott78d742a2024-04-02 20:26:01 +0200716 if (cache_last_index < 0)
717 {
Luuk van Baalb7147f82025-02-08 18:52:39 +0100718 for (i = 0; i < CACHE_SIZE; ++i)
John Marriott78d742a2024-04-02 20:26:01 +0200719 cache_tab[i] = -1;
Luuk van Baalb7147f82025-02-08 18:52:39 +0100720 cache_last_index = CACHE_SIZE - 1;
John Marriott78d742a2024-04-02 20:26:01 +0200721 }
722
723 // first look in the cache
724 // the cache is circular. to search it we start at the most recent entry
725 // and go backwards wrapping around when we get to index 0.
726 for (i = cache_last_index; cache_tab[i] >= 0; )
727 {
Luuk van Baalb7147f82025-02-08 18:52:39 +0100728 if ((event_T)abs(event_tab[cache_tab[i]].key) == event)
John Marriott8d4477e2024-11-02 15:59:01 +0100729 return event_tab[cache_tab[i]].value.string;
John Marriott78d742a2024-04-02 20:26:01 +0200730
731 if (i == 0)
Luuk van Baalb7147f82025-02-08 18:52:39 +0100732 i = CACHE_SIZE - 1;
John Marriott78d742a2024-04-02 20:26:01 +0200733 else
734 --i;
735
736 // are we back at the start?
737 if (i == cache_last_index)
738 break;
739 }
740
741 // look in the event table itself
Luuk van Baalb7147f82025-02-08 18:52:39 +0100742 for (i = 0; i < NUM_EVENTS; ++i)
John Marriott78d742a2024-04-02 20:26:01 +0200743 {
Luuk van Baalb7147f82025-02-08 18:52:39 +0100744 if ((event_T)abs(event_tab[i].key) == event)
John Marriott78d742a2024-04-02 20:26:01 +0200745 {
746 // store the found entry in the next position in the cache,
747 // wrapping around when we get to the maximum index.
Luuk van Baalb7147f82025-02-08 18:52:39 +0100748 if (cache_last_index == CACHE_SIZE - 1)
John Marriott78d742a2024-04-02 20:26:01 +0200749 cache_last_index = 0;
750 else
751 ++cache_last_index;
752 cache_tab[cache_last_index] = i;
753 break;
754 }
755 }
756
Luuk van Baalb7147f82025-02-08 18:52:39 +0100757 return (i == NUM_EVENTS) ? (char_u *)"Unknown" :
758 event_tab[i].value.string;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100759}
760
761/*
762 * Scan over the events. "*" stands for all events.
763 */
764 static char_u *
765find_end_event(
766 char_u *arg,
767 int have_group) // TRUE when group name was found
768{
769 char_u *pat;
770 char_u *p;
771
772 if (*arg == '*')
773 {
774 if (arg[1] && !VIM_ISWHITE(arg[1]))
775 {
Bram Moolenaar6d057012021-12-31 18:49:43 +0000776 semsg(_(e_illegal_character_after_star_str), arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100777 return NULL;
778 }
779 pat = arg + 1;
780 }
781 else
782 {
783 for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p)
784 {
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100785 if ((int)event_name2nr(pat, &p) >= NUM_EVENTS)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100786 {
787 if (have_group)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000788 semsg(_(e_no_such_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100789 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000790 semsg(_(e_no_such_group_or_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100791 return NULL;
792 }
793 }
794 }
795 return pat;
796}
797
798/*
Luuk van Baalb7147f82025-02-08 18:52:39 +0100799 * Return TRUE if "event" is included in 'eventignore(win)'.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100800 */
Luuk van Baalb7147f82025-02-08 18:52:39 +0100801 int
802event_ignored(event_T event, char_u *ei)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100803{
Luuk van Baalb7147f82025-02-08 18:52:39 +0100804 while (*ei != NUL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100805 {
Luuk van Baalb7147f82025-02-08 18:52:39 +0100806 if (STRNICMP(ei, "all", 3) == 0 && (ei[3] == NUL || ei[3] == ',')
807 && (ei == p_ei || (event_tab[event].key <= 0)))
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100808 return TRUE;
Luuk van Baalb7147f82025-02-08 18:52:39 +0100809 if (event_name2nr(ei, &ei) == event)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100810 return TRUE;
811 }
812
813 return FALSE;
814}
815
816/*
Luuk van Baalb7147f82025-02-08 18:52:39 +0100817 * Return OK when the contents of 'eventignore' or 'eventignorewin' is valid,
818 * FAIL otherwise.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100819 */
820 int
Luuk van Baalb7147f82025-02-08 18:52:39 +0100821check_ei(char_u *ei)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100822{
Luuk van Baalb7147f82025-02-08 18:52:39 +0100823 int win = ei != p_ei;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100824
Luuk van Baalb7147f82025-02-08 18:52:39 +0100825 while (*ei)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100826 {
Luuk van Baalb7147f82025-02-08 18:52:39 +0100827 if (STRNICMP(ei, "all", 3) == 0 && (ei[3] == NUL || ei[3] == ','))
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100828 {
Luuk van Baalb7147f82025-02-08 18:52:39 +0100829 ei += 3;
830 if (*ei == ',')
831 ++ei;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100832 }
Luuk van Baalb7147f82025-02-08 18:52:39 +0100833 else
834 {
835 event_T event = event_name2nr(ei, &ei);
836 if (event == NUM_EVENTS || (win && event_tab[event].key > 0))
837 return FAIL;
838 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100839 }
840
841 return OK;
842}
843
844# if defined(FEAT_SYN_HL) || defined(PROTO)
845
846/*
847 * Add "what" to 'eventignore' to skip loading syntax highlighting for every
848 * buffer loaded into the window. "what" must start with a comma.
849 * Returns the old value of 'eventignore' in allocated memory.
850 */
851 char_u *
852au_event_disable(char *what)
853{
854 char_u *new_ei;
855 char_u *save_ei;
John Marriott78d742a2024-04-02 20:26:01 +0200856 size_t p_ei_len;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100857
John Marriott78d742a2024-04-02 20:26:01 +0200858 p_ei_len = STRLEN(p_ei);
859 save_ei = vim_strnsave(p_ei, p_ei_len);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100860 if (save_ei == NULL)
861 return NULL;
862
John Marriott78d742a2024-04-02 20:26:01 +0200863 new_ei = vim_strnsave(p_ei, p_ei_len + STRLEN(what));
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100864 if (new_ei == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100865 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100866 vim_free(save_ei);
867 return NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100868 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100869
870 if (*what == ',' && *p_ei == NUL)
871 STRCPY(new_ei, what + 1);
872 else
zeertzjq969e11a2025-03-10 21:15:19 +0100873 STRCPY(new_ei + p_ei_len, what);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100874 set_string_option_direct((char_u *)"ei", -1, new_ei,
875 OPT_FREE, SID_NONE);
876 vim_free(new_ei);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100877 return save_ei;
878}
879
880 void
881au_event_restore(char_u *old_ei)
882{
883 if (old_ei != NULL)
884 {
885 set_string_option_direct((char_u *)"ei", -1, old_ei,
886 OPT_FREE, SID_NONE);
887 vim_free(old_ei);
888 }
889}
Bram Moolenaarc667da52019-11-30 20:52:27 +0100890# endif // FEAT_SYN_HL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100891
892/*
893 * do_autocmd() -- implements the :autocmd command. Can be used in the
894 * following ways:
895 *
896 * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
897 * will be automatically executed for <event>
898 * when editing a file matching <pat>, in
899 * the current group.
900 * :autocmd <event> <pat> Show the autocommands associated with
901 * <event> and <pat>.
902 * :autocmd <event> Show the autocommands associated with
903 * <event>.
904 * :autocmd Show all autocommands.
905 * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
906 * <event> and <pat>, and add the command
907 * <cmd>, for the current group.
908 * :autocmd! <event> <pat> Remove all autocommands associated with
909 * <event> and <pat> for the current group.
910 * :autocmd! <event> Remove all autocommands associated with
911 * <event> for the current group.
912 * :autocmd! Remove ALL autocommands for the current
913 * group.
914 *
915 * Multiple events and patterns may be given separated by commas. Here are
916 * some examples:
917 * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
918 * :autocmd bufleave * set tw=79 nosmartindent ic infercase
919 *
920 * :autocmd * *.c show all autocommands for *.c files.
921 *
922 * Mostly a {group} argument can optionally appear before <event>.
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200923 * "eap" can be NULL.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100924 */
925 void
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200926do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100927{
928 char_u *arg = arg_in;
929 char_u *pat;
930 char_u *envpat = NULL;
931 char_u *cmd;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200932 int cmd_need_free = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100933 event_T event;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200934 char_u *tofree = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100935 int nested = FALSE;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200936 int once = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100937 int group;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200938 int i;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200939 int flags = 0;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100940
941 if (*arg == '|')
942 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000943 eap->nextcmd = arg + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100944 arg = (char_u *)"";
945 group = AUGROUP_ALL; // no argument, use all groups
946 }
947 else
948 {
949 /*
950 * Check for a legal group name. If not, use AUGROUP_ALL.
951 */
952 group = au_get_grouparg(&arg);
953 if (arg == NULL) // out of memory
954 return;
955 }
956
957 /*
958 * Scan over the events.
959 * If we find an illegal name, return here, don't do anything.
960 */
961 pat = find_end_event(arg, group != AUGROUP_ALL);
962 if (pat == NULL)
963 return;
964
965 pat = skipwhite(pat);
966 if (*pat == '|')
967 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000968 eap->nextcmd = pat + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100969 pat = (char_u *)"";
970 cmd = (char_u *)"";
971 }
972 else
973 {
974 /*
975 * Scan over the pattern. Put a NUL at the end.
976 */
977 cmd = pat;
978 while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
979 cmd++;
980 if (*cmd)
981 *cmd++ = NUL;
982
983 // Expand environment variables in the pattern. Set 'shellslash', we
984 // want forward slashes here.
985 if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
986 {
987#ifdef BACKSLASH_IN_FILENAME
988 int p_ssl_save = p_ssl;
989
990 p_ssl = TRUE;
991#endif
992 envpat = expand_env_save(pat);
993#ifdef BACKSLASH_IN_FILENAME
994 p_ssl = p_ssl_save;
995#endif
996 if (envpat != NULL)
997 pat = envpat;
998 }
999
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001000 cmd = skipwhite(cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001001 for (i = 0; i < 2; i++)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001002 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001003 if (*cmd == NUL)
1004 continue;
1005
1006 // Check for "++once" flag.
1007 if (STRNCMP(cmd, "++once", 6) == 0 && VIM_ISWHITE(cmd[6]))
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001008 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001009 if (once)
1010 semsg(_(e_duplicate_argument_str), "++once");
1011 once = TRUE;
1012 cmd = skipwhite(cmd + 6);
1013 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001014
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001015 // Check for "++nested" flag.
1016 if ((STRNCMP(cmd, "++nested", 8) == 0 && VIM_ISWHITE(cmd[8])))
1017 {
1018 if (nested)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001019 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001020 semsg(_(e_duplicate_argument_str), "++nested");
1021 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001022 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001023 nested = TRUE;
1024 cmd = skipwhite(cmd + 8);
1025 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001026
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001027 // Check for the old "nested" flag in legacy script.
1028 if (STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
1029 {
1030 if (in_vim9script())
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001031 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001032 // If there ever is a :nested command this error should
1033 // be removed and "nested" accepted as the start of the
1034 // command.
1035 emsg(_(e_invalid_command_nested_did_you_mean_plusplus_nested));
1036 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001037 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001038 if (nested)
1039 {
1040 semsg(_(e_duplicate_argument_str), "nested");
1041 return;
1042 }
1043 nested = TRUE;
1044 cmd = skipwhite(cmd + 6);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001045 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001046 }
1047
1048 /*
1049 * Find the start of the commands.
1050 * Expand <sfile> in it.
1051 */
1052 if (*cmd != NUL)
1053 {
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001054 if (eap != NULL)
1055 // Read a {} block if it follows.
1056 cmd = may_get_cmd_block(eap, cmd, &tofree, &flags);
1057
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001058 cmd = expand_sfile(cmd);
1059 if (cmd == NULL) // some error
1060 return;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001061 cmd_need_free = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001062 }
1063 }
1064
1065 /*
1066 * Print header when showing autocommands.
1067 */
1068 if (!forceit && *cmd == NUL)
1069 // Highlight title
1070 msg_puts_title(_("\n--- Autocommands ---"));
1071
1072 /*
1073 * Loop over the events.
1074 */
1075 last_event = (event_T)-1; // for listing the event name
1076 last_group = AUGROUP_ERROR; // for listing the group name
1077 if (*arg == '*' || *arg == NUL || *arg == '|')
1078 {
Bram Moolenaarb6db1462021-12-24 19:24:47 +00001079 if (*cmd != NUL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001080 emsg(_(e_cannot_define_autocommands_for_all_events));
1081 else
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +01001082 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001083 event = (event_T)((int)event + 1))
1084 if (do_autocmd_event(event, pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001085 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001086 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001087 }
1088 else
1089 {
1090 while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
1091 if (do_autocmd_event(event_name2nr(arg, &arg), pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001092 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001093 break;
1094 }
1095
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001096 if (cmd_need_free)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001097 vim_free(cmd);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001098 vim_free(tofree);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001099 vim_free(envpat);
1100}
1101
1102/*
1103 * Find the group ID in a ":autocmd" or ":doautocmd" argument.
1104 * The "argp" argument is advanced to the following argument.
1105 *
1106 * Returns the group ID, AUGROUP_ERROR for error (out of memory).
1107 */
1108 static int
1109au_get_grouparg(char_u **argp)
1110{
1111 char_u *group_name;
1112 char_u *p;
1113 char_u *arg = *argp;
1114 int group = AUGROUP_ALL;
1115
1116 for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p)
1117 ;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001118 if (p <= arg)
1119 return AUGROUP_ALL;
1120
1121 group_name = vim_strnsave(arg, p - arg);
1122 if (group_name == NULL) // out of memory
1123 return AUGROUP_ERROR;
1124 group = au_find_group(group_name);
1125 if (group == AUGROUP_ERROR)
1126 group = AUGROUP_ALL; // no match, use all groups
1127 else
1128 *argp = skipwhite(p); // match, skip over group name
1129 vim_free(group_name);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001130 return group;
1131}
1132
1133/*
1134 * do_autocmd() for one event.
1135 * If *pat == NUL do for all patterns.
1136 * If *cmd == NUL show entries.
1137 * If forceit == TRUE delete entries.
1138 * If group is not AUGROUP_ALL, only use this group.
1139 */
1140 static int
1141do_autocmd_event(
1142 event_T event,
1143 char_u *pat,
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001144 int once,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001145 int nested,
1146 char_u *cmd,
1147 int forceit,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001148 int group,
1149 int flags)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001150{
1151 AutoPat *ap;
1152 AutoPat **prev_ap;
1153 AutoCmd *ac;
1154 AutoCmd **prev_ac;
1155 int brace_level;
1156 char_u *endpat;
1157 int findgroup;
1158 int allgroups;
1159 int patlen;
1160 int is_buflocal;
1161 int buflocal_nr;
Bram Moolenaarc667da52019-11-30 20:52:27 +01001162 char_u buflocal_pat[25]; // for "<buffer=X>"
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001163
1164 if (group == AUGROUP_ALL)
1165 findgroup = current_augroup;
1166 else
1167 findgroup = group;
1168 allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
1169
1170 /*
1171 * Show or delete all patterns for an event.
1172 */
1173 if (*pat == NUL)
1174 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001175 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001176 {
1177 if (forceit) // delete the AutoPat, if it's in the current group
1178 {
1179 if (ap->group == findgroup)
1180 au_remove_pat(ap);
1181 }
1182 else if (group == AUGROUP_ALL || ap->group == group)
1183 show_autocmd(ap, event);
1184 }
1185 }
1186
1187 /*
1188 * Loop through all the specified patterns.
1189 */
1190 for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
1191 {
1192 /*
1193 * Find end of the pattern.
1194 * Watch out for a comma in braces, like "*.\{obj,o\}".
1195 */
1196 brace_level = 0;
1197 for (endpat = pat; *endpat && (*endpat != ',' || brace_level
1198 || (endpat > pat && endpat[-1] == '\\')); ++endpat)
1199 {
1200 if (*endpat == '{')
1201 brace_level++;
1202 else if (*endpat == '}')
1203 brace_level--;
1204 }
1205 if (pat == endpat) // ignore single comma
1206 continue;
1207 patlen = (int)(endpat - pat);
1208
1209 /*
1210 * detect special <buflocal[=X]> buffer-local patterns
1211 */
1212 is_buflocal = FALSE;
1213 buflocal_nr = 0;
1214
1215 if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
1216 && pat[patlen - 1] == '>')
1217 {
1218 // "<buffer...>": Error will be printed only for addition.
1219 // printing and removing will proceed silently.
1220 is_buflocal = TRUE;
1221 if (patlen == 8)
1222 // "<buffer>"
1223 buflocal_nr = curbuf->b_fnum;
1224 else if (patlen > 9 && pat[7] == '=')
1225 {
1226 if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0)
1227 // "<buffer=abuf>"
1228 buflocal_nr = autocmd_bufnr;
1229 else if (skipdigits(pat + 8) == pat + patlen - 1)
1230 // "<buffer=123>"
1231 buflocal_nr = atoi((char *)pat + 8);
1232 }
1233 }
1234
1235 if (is_buflocal)
1236 {
1237 // normalize pat into standard "<buffer>#N" form
1238 sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
1239 pat = buflocal_pat; // can modify pat and patlen
1240 patlen = (int)STRLEN(buflocal_pat); // but not endpat
1241 }
1242
1243 /*
1244 * Find AutoPat entries with this pattern. When adding a command it
1245 * always goes at or after the last one, so start at the end.
1246 */
1247 if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL)
1248 prev_ap = &last_autopat[(int)event];
1249 else
1250 prev_ap = &first_autopat[(int)event];
1251 while ((ap = *prev_ap) != NULL)
1252 {
1253 if (ap->pat != NULL)
1254 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001255 /*
1256 * Accept a pattern when:
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001257 * - a group was specified and it's that group, or a group was
1258 * not specified and it's the current group, or a group was
1259 * not specified and we are listing
1260 * - the length of the pattern matches
1261 * - the pattern matches.
1262 * For <buffer[=X]>, this condition works because we normalize
1263 * all buffer-local patterns.
1264 */
1265 if ((allgroups || ap->group == findgroup)
1266 && ap->patlen == patlen
1267 && STRNCMP(pat, ap->pat, patlen) == 0)
1268 {
1269 /*
1270 * Remove existing autocommands.
1271 * If adding any new autocmd's for this AutoPat, don't
1272 * delete the pattern from the autopat list, append to
1273 * this list.
1274 */
1275 if (forceit)
1276 {
1277 if (*cmd != NUL && ap->next == NULL)
1278 {
1279 au_remove_cmds(ap);
1280 break;
1281 }
1282 au_remove_pat(ap);
1283 }
1284
1285 /*
1286 * Show autocmd's for this autopat, or buflocals <buffer=X>
1287 */
1288 else if (*cmd == NUL)
1289 show_autocmd(ap, event);
1290
1291 /*
1292 * Add autocmd to this autopat, if it's the last one.
1293 */
1294 else if (ap->next == NULL)
1295 break;
1296 }
1297 }
1298 prev_ap = &ap->next;
1299 }
1300
1301 /*
1302 * Add a new command.
1303 */
1304 if (*cmd != NUL)
1305 {
1306 /*
1307 * If the pattern we want to add a command to does appear at the
1308 * end of the list (or not is not in the list at all), add the
1309 * pattern at the end of the list.
1310 */
1311 if (ap == NULL)
1312 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001313 // refuse to add buffer-local ap if buffer number is invalid
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001314 if (is_buflocal && (buflocal_nr == 0
1315 || buflist_findnr(buflocal_nr) == NULL))
1316 {
Bram Moolenaarf1474d82021-12-31 19:59:55 +00001317 semsg(_(e_buffer_nr_invalid_buffer_number), buflocal_nr);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001318 return FAIL;
1319 }
1320
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001321 ap = ALLOC_ONE(AutoPat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001322 if (ap == NULL)
1323 return FAIL;
1324 ap->pat = vim_strnsave(pat, patlen);
1325 ap->patlen = patlen;
1326 if (ap->pat == NULL)
1327 {
1328 vim_free(ap);
1329 return FAIL;
1330 }
1331
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001332#ifdef FEAT_EVAL
1333 // need to initialize last_mode for the first ModeChanged
1334 // autocmd
1335 if (event == EVENT_MODECHANGED && !has_modechanged())
LemonBoy2bf52dd2022-04-09 18:17:34 +01001336 get_mode(last_mode);
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001337#endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001338 // Initialize the fields checked by the WinScrolled and
1339 // WinResized trigger to prevent them from firing right after
1340 // the first autocmd is defined.
1341 if ((event == EVENT_WINSCROLLED || event == EVENT_WINRESIZED)
1342 && !(has_winscrolled() || has_winresized()))
LemonBoy09371822022-04-08 15:18:45 +01001343 {
Bram Moolenaar29967732022-11-20 12:11:45 +00001344 tabpage_T *save_curtab = curtab;
1345 tabpage_T *tp;
1346 FOR_ALL_TABPAGES(tp)
1347 {
1348 unuse_tabpage(curtab);
1349 use_tabpage(tp);
1350 snapshot_windows_scroll_size();
1351 }
1352 unuse_tabpage(curtab);
1353 use_tabpage(save_curtab);
LemonBoy09371822022-04-08 15:18:45 +01001354 }
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001355
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001356 if (is_buflocal)
1357 {
1358 ap->buflocal_nr = buflocal_nr;
1359 ap->reg_prog = NULL;
1360 }
1361 else
1362 {
1363 char_u *reg_pat;
1364
1365 ap->buflocal_nr = 0;
1366 reg_pat = file_pat_to_reg_pat(pat, endpat,
1367 &ap->allow_dirs, TRUE);
1368 if (reg_pat != NULL)
1369 ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
1370 vim_free(reg_pat);
1371 if (reg_pat == NULL || ap->reg_prog == NULL)
1372 {
1373 vim_free(ap->pat);
1374 vim_free(ap);
1375 return FAIL;
1376 }
1377 }
1378 ap->cmds = NULL;
1379 *prev_ap = ap;
1380 last_autopat[(int)event] = ap;
1381 ap->next = NULL;
1382 if (group == AUGROUP_ALL)
1383 ap->group = current_augroup;
1384 else
1385 ap->group = group;
1386 }
1387
1388 /*
1389 * Add the autocmd at the end of the AutoCmd list.
1390 */
1391 prev_ac = &(ap->cmds);
1392 while ((ac = *prev_ac) != NULL)
1393 prev_ac = &ac->next;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001394 ac = ALLOC_ONE(AutoCmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001395 if (ac == NULL)
1396 return FAIL;
1397 ac->cmd = vim_strsave(cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001398 ac->script_ctx = current_sctx;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001399 if (flags & UC_VIM9)
1400 ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001401#ifdef FEAT_EVAL
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001402 ac->script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001403#endif
1404 if (ac->cmd == NULL)
1405 {
1406 vim_free(ac);
1407 return FAIL;
1408 }
1409 ac->next = NULL;
1410 *prev_ac = ac;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001411 ac->once = once;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001412 ac->nested = nested;
1413 }
1414 }
1415
1416 au_cleanup(); // may really delete removed patterns/commands now
1417 return OK;
1418}
1419
1420/*
1421 * Implementation of ":doautocmd [group] event [fname]".
1422 * Return OK for success, FAIL for failure;
1423 */
1424 int
1425do_doautocmd(
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001426 char_u *arg_start,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001427 int do_msg, // give message for no matching autocmds?
1428 int *did_something)
1429{
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001430 char_u *arg = arg_start;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001431 char_u *fname;
1432 int nothing_done = TRUE;
1433 int group;
1434
1435 if (did_something != NULL)
1436 *did_something = FALSE;
1437
1438 /*
1439 * Check for a legal group name. If not, use AUGROUP_ALL.
1440 */
1441 group = au_get_grouparg(&arg);
1442 if (arg == NULL) // out of memory
1443 return FAIL;
1444
1445 if (*arg == '*')
1446 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001447 emsg(_(e_cant_execute_autocommands_for_all_events));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001448 return FAIL;
1449 }
1450
1451 /*
1452 * Scan over the events.
1453 * If we find an illegal name, return here, don't do anything.
1454 */
1455 fname = find_end_event(arg, group != AUGROUP_ALL);
1456 if (fname == NULL)
1457 return FAIL;
1458
1459 fname = skipwhite(fname);
1460
1461 /*
1462 * Loop over the events.
1463 */
1464 while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
1465 if (apply_autocmds_group(event_name2nr(arg, &arg),
1466 fname, NULL, TRUE, group, curbuf, NULL))
1467 nothing_done = FALSE;
1468
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001469 if (nothing_done && do_msg
1470#ifdef FEAT_EVAL
1471 && !aborting()
1472#endif
1473 )
1474 smsg(_("No matching autocommands: %s"), arg_start);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001475 if (did_something != NULL)
1476 *did_something = !nothing_done;
1477
1478#ifdef FEAT_EVAL
1479 return aborting() ? FAIL : OK;
1480#else
1481 return OK;
1482#endif
1483}
1484
1485/*
1486 * ":doautoall": execute autocommands for each loaded buffer.
1487 */
1488 void
1489ex_doautoall(exarg_T *eap)
1490{
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001491 int retval = OK;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001492 aco_save_T aco;
1493 buf_T *buf;
1494 bufref_T bufref;
1495 char_u *arg = eap->arg;
1496 int call_do_modelines = check_nomodeline(&arg);
1497 int did_aucmd;
1498
1499 /*
1500 * This is a bit tricky: For some commands curwin->w_buffer needs to be
1501 * equal to curbuf, but for some buffers there may not be a window.
1502 * So we change the buffer for the current window for a moment. This
1503 * gives problems when the autocommands make changes to the list of
1504 * buffers or windows...
1505 */
1506 FOR_ALL_BUFFERS(buf)
1507 {
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001508 // Only do loaded buffers and skip the current buffer, it's done last.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001509 if (buf->b_ml.ml_mfp == NULL || buf == curbuf)
1510 continue;
1511
Bram Moolenaare76062c2022-11-28 18:51:43 +00001512 // Find a window for this buffer and save some values.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001513 aucmd_prepbuf(&aco, buf);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001514 if (curbuf != buf)
1515 {
1516 // Failed to find a window for this buffer. Better not execute
1517 // autocommands then.
1518 retval = FAIL;
1519 break;
1520 }
1521
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001522 set_bufref(&bufref, buf);
1523
1524 // execute the autocommands for this buffer
1525 retval = do_doautocmd(arg, FALSE, &did_aucmd);
1526
1527 if (call_do_modelines && did_aucmd)
1528 // Execute the modeline settings, but don't set window-local
1529 // options if we are using the current window for another
1530 // buffer.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001531 do_modelines(is_aucmd_win(curwin) ? OPT_NOWIN : 0);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001532
1533 // restore the current window
1534 aucmd_restbuf(&aco);
1535
1536 // stop if there is some error or buffer was deleted
1537 if (retval == FAIL || !bufref_valid(&bufref))
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001538 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001539 retval = FAIL;
1540 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001541 }
1542 }
1543
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001544 // Execute autocommands for the current buffer last.
1545 if (retval == OK)
1546 {
1547 do_doautocmd(arg, FALSE, &did_aucmd);
1548 if (call_do_modelines && did_aucmd)
1549 do_modelines(0);
1550 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001551}
1552
1553/*
1554 * Check *argp for <nomodeline>. When it is present return FALSE, otherwise
1555 * return TRUE and advance *argp to after it.
1556 * Thus return TRUE when do_modelines() should be called.
1557 */
1558 int
1559check_nomodeline(char_u **argp)
1560{
1561 if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
1562 {
1563 *argp = skipwhite(*argp + 12);
1564 return FALSE;
1565 }
1566 return TRUE;
1567}
1568
1569/*
1570 * Prepare for executing autocommands for (hidden) buffer "buf".
1571 * Search for a visible window containing the current buffer. If there isn't
Bram Moolenaare76062c2022-11-28 18:51:43 +00001572 * one then use an entry in "aucmd_win[]".
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001573 * Set "curbuf" and "curwin" to match "buf".
Bram Moolenaare76062c2022-11-28 18:51:43 +00001574 * When this fails "curbuf" is not equal "buf".
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001575 */
1576 void
1577aucmd_prepbuf(
1578 aco_save_T *aco, // structure to save values in
1579 buf_T *buf) // new curbuf
1580{
1581 win_T *win;
1582 int save_ea;
1583#ifdef FEAT_AUTOCHDIR
1584 int save_acd;
1585#endif
1586
1587 // Find a window that is for the new buffer
1588 if (buf == curbuf) // be quick when buf is curbuf
1589 win = curwin;
1590 else
1591 FOR_ALL_WINDOWS(win)
1592 if (win->w_buffer == buf)
1593 break;
1594
Bram Moolenaare76062c2022-11-28 18:51:43 +00001595 // Allocate a window when needed.
1596 win_T *auc_win = NULL;
1597 int auc_idx = AUCMD_WIN_COUNT;
1598 if (win == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001599 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001600 for (auc_idx = 0; auc_idx < AUCMD_WIN_COUNT; ++auc_idx)
1601 if (!aucmd_win[auc_idx].auc_win_used)
1602 {
Bram Moolenaar84497cd2022-11-28 20:34:52 +00001603 if (aucmd_win[auc_idx].auc_win == NULL)
1604 aucmd_win[auc_idx].auc_win = win_alloc_popup_win();
1605 auc_win = aucmd_win[auc_idx].auc_win;
Bram Moolenaare76062c2022-11-28 18:51:43 +00001606 if (auc_win != NULL)
Bram Moolenaare76062c2022-11-28 18:51:43 +00001607 aucmd_win[auc_idx].auc_win_used = TRUE;
Bram Moolenaare76062c2022-11-28 18:51:43 +00001608 break;
1609 }
1610
1611 // If this fails (out of memory or using all AUCMD_WIN_COUNT
1612 // entries) then we can't reliable execute the autocmd, return with
1613 // "curbuf" unequal "buf".
1614 if (auc_win == NULL)
1615 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001616 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001617
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001618 aco->save_curwin_id = curwin->w_id;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001619 aco->save_prevwin_id = prevwin == NULL ? 0 : prevwin->w_id;
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001620 aco->save_State = State;
zeertzjqf2678472024-01-17 21:22:59 +01001621#ifdef FEAT_JOB_CHANNEL
1622 if (bt_prompt(curbuf))
1623 aco->save_prompt_insert = curbuf->b_prompt_insert;
1624#endif
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001625
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001626 if (win != NULL)
1627 {
1628 // There is a window for "buf" in the current tab page, make it the
1629 // curwin. This is preferred, it has the least side effects (esp. if
1630 // "buf" is curbuf).
Bram Moolenaare76062c2022-11-28 18:51:43 +00001631 aco->use_aucmd_win_idx = -1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001632 curwin = win;
1633 }
1634 else
1635 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001636 // There is no window for "buf", use "auc_win". To minimize the side
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001637 // effects, insert it in the current tab page.
1638 // Anything related to a window (e.g., setting folds) may have
1639 // unexpected results.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001640 aco->use_aucmd_win_idx = auc_idx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001641
Bram Moolenaare76062c2022-11-28 18:51:43 +00001642 win_init_popup_win(auc_win, buf);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001643
zeertzjq9d956ee2024-04-07 18:16:10 +02001644 // Make sure tp_localdir and globaldir are NULL to avoid a
1645 // chdir() in win_enter_ext().
1646 // win_init_popup_win() has already set w_localdir to NULL.
1647 aco->tp_localdir = curtab->tp_localdir;
1648 curtab->tp_localdir = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001649 aco->globaldir = globaldir;
1650 globaldir = NULL;
1651
Bram Moolenaare76062c2022-11-28 18:51:43 +00001652 // Split the current window, put the auc_win in the upper half.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001653 // We don't want the BufEnter or WinEnter autocommands.
1654 block_autocmds();
1655 make_snapshot(SNAP_AUCMD_IDX);
1656 save_ea = p_ea;
1657 p_ea = FALSE;
1658
1659#ifdef FEAT_AUTOCHDIR
1660 // Prevent chdir() call in win_enter_ext(), through do_autochdir().
1661 save_acd = p_acd;
1662 p_acd = FALSE;
1663#endif
1664
Sean Dewar704966c2024-02-20 22:00:33 +01001665 (void)win_split_ins(0, WSP_TOP | WSP_FORCE_ROOM, auc_win, 0, NULL);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001666 (void)win_comp_pos(); // recompute window positions
1667 p_ea = save_ea;
1668#ifdef FEAT_AUTOCHDIR
1669 p_acd = save_acd;
1670#endif
1671 unblock_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001672 curwin = auc_win;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001673 }
1674 curbuf = buf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001675 aco->new_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001676 set_bufref(&aco->new_curbuf, curbuf);
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001677
1678 // disable the Visual area, the position may be invalid in another buffer
1679 aco->save_VIsual_active = VIsual_active;
1680 VIsual_active = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001681}
1682
1683/*
1684 * Cleanup after executing autocommands for a (hidden) buffer.
1685 * Restore the window as it was (if possible).
1686 */
1687 void
1688aucmd_restbuf(
1689 aco_save_T *aco) // structure holding saved values
1690{
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001691 int dummy;
1692 win_T *save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001693
Bram Moolenaare76062c2022-11-28 18:51:43 +00001694 if (aco->use_aucmd_win_idx >= 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001695 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001696 win_T *awp = aucmd_win[aco->use_aucmd_win_idx].auc_win;
1697
Bram Moolenaare76062c2022-11-28 18:51:43 +00001698 // Find "awp", it can't be closed, but it may be in another tab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001699 // page. Do not trigger autocommands here.
1700 block_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001701 if (curwin != awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001702 {
1703 tabpage_T *tp;
1704 win_T *wp;
1705
1706 FOR_ALL_TAB_WINDOWS(tp, wp)
1707 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001708 if (wp == awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001709 {
1710 if (tp != curtab)
1711 goto_tabpage_tp(tp, TRUE, TRUE);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001712 win_goto(awp);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001713 goto win_found;
1714 }
1715 }
1716 }
1717win_found:
zeertzjq46bdae02023-09-24 23:16:08 +02001718 --curbuf->b_nwindows;
orbitalcde8de02023-04-02 22:05:13 +01001719#ifdef FEAT_JOB_CHANNEL
zeertzjqa40c0bc2023-05-27 14:10:08 +01001720 int save_stop_insert_mode = stop_insert_mode;
orbitalcde8de02023-04-02 22:05:13 +01001721 // May need to stop Insert mode if we were in a prompt buffer.
1722 leaving_window(curwin);
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001723 // Do not stop Insert mode when already in Insert mode before.
1724 if (aco->save_State & MODE_INSERT)
zeertzjqa40c0bc2023-05-27 14:10:08 +01001725 stop_insert_mode = save_stop_insert_mode;
orbitalcde8de02023-04-02 22:05:13 +01001726#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001727 // Remove the window and frame from the tree of frames.
Sean Dewar704966c2024-02-20 22:00:33 +01001728 (void)winframe_remove(curwin, &dummy, NULL, NULL);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001729 win_remove(curwin, NULL);
Bram Moolenaar84497cd2022-11-28 20:34:52 +00001730
1731 // The window is marked as not used, but it is not freed, it can be
1732 // used again.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001733 aucmd_win[aco->use_aucmd_win_idx].auc_win_used = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001734 last_status(FALSE); // may need to remove last status line
1735
1736 if (!valid_tabpage_win(curtab))
1737 // no valid window in current tabpage
1738 close_tabpage(curtab);
1739
1740 restore_snapshot(SNAP_AUCMD_IDX, FALSE);
1741 (void)win_comp_pos(); // recompute window positions
1742 unblock_autocmds();
1743
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001744 save_curwin = win_find_by_id(aco->save_curwin_id);
1745 if (save_curwin != NULL)
1746 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001747 else
1748 // Hmm, original window disappeared. Just use the first one.
1749 curwin = firstwin;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001750 curbuf = curwin->w_buffer;
1751#ifdef FEAT_JOB_CHANNEL
1752 // May need to restore insert mode for a prompt buffer.
1753 entering_window(curwin);
zeertzjqf2678472024-01-17 21:22:59 +01001754 if (bt_prompt(curbuf))
1755 curbuf->b_prompt_insert = aco->save_prompt_insert;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001756#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001757 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001758#ifdef FEAT_EVAL
Bram Moolenaare76062c2022-11-28 18:51:43 +00001759 vars_clear(&awp->w_vars->dv_hashtab); // free all w: variables
1760 hash_init(&awp->w_vars->dv_hashtab); // re-use the hashtab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001761#endif
zeertzjq9d956ee2024-04-07 18:16:10 +02001762 // If :lcd has been used in the autocommand window, correct current
1763 // directory before restoring tp_localdir and globaldir.
1764 if (awp->w_localdir != NULL)
1765 win_fix_current_dir();
1766 vim_free(curtab->tp_localdir);
1767 curtab->tp_localdir = aco->tp_localdir;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001768 vim_free(globaldir);
1769 globaldir = aco->globaldir;
1770
1771 // the buffer contents may have changed
zeertzjq49f05242023-02-04 10:58:34 +00001772 VIsual_active = aco->save_VIsual_active;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001773 check_cursor();
1774 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1775 {
1776 curwin->w_topline = curbuf->b_ml.ml_line_count;
1777#ifdef FEAT_DIFF
1778 curwin->w_topfill = 0;
1779#endif
1780 }
1781#if defined(FEAT_GUI)
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001782 if (gui.in_use)
1783 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001784 // Hide the scrollbars from the "awp" and update.
1785 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_LEFT], FALSE);
1786 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_RIGHT], FALSE);
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001787 gui_may_update_scrollbars();
1788 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001789#endif
1790 }
1791 else
1792 {
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001793 // Restore curwin. Use the window ID, a window may have been closed
1794 // and the memory re-used for another one.
1795 save_curwin = win_find_by_id(aco->save_curwin_id);
1796 if (save_curwin != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001797 {
1798 // Restore the buffer which was previously edited by curwin, if
1799 // it was changed, we are still the same window and the buffer is
1800 // valid.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001801 if (curwin->w_id == aco->new_curwin_id
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001802 && curbuf != aco->new_curbuf.br_buf
1803 && bufref_valid(&aco->new_curbuf)
1804 && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
1805 {
1806# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
1807 if (curwin->w_s == &curbuf->b_s)
1808 curwin->w_s = &aco->new_curbuf.br_buf->b_s;
1809# endif
1810 --curbuf->b_nwindows;
1811 curbuf = aco->new_curbuf.br_buf;
1812 curwin->w_buffer = curbuf;
1813 ++curbuf->b_nwindows;
1814 }
1815
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001816 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001817 curbuf = curwin->w_buffer;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001818 prevwin = win_find_by_id(aco->save_prevwin_id);
zeertzjq49f05242023-02-04 10:58:34 +00001819
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001820 // In case the autocommand moves the cursor to a position that
1821 // does not exist in curbuf.
zeertzjq49f05242023-02-04 10:58:34 +00001822 VIsual_active = aco->save_VIsual_active;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001823 check_cursor();
1824 }
1825 }
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001826
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001827 VIsual_active = aco->save_VIsual_active;
zeertzjq49f05242023-02-04 10:58:34 +00001828 check_cursor(); // just in case lines got deleted
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001829 if (VIsual_active)
1830 check_pos(curbuf, &VIsual);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001831}
1832
1833static int autocmd_nested = FALSE;
1834
1835/*
1836 * Execute autocommands for "event" and file name "fname".
1837 * Return TRUE if some commands were executed.
1838 */
1839 int
1840apply_autocmds(
1841 event_T event,
1842 char_u *fname, // NULL or empty means use actual file name
1843 char_u *fname_io, // fname to use for <afile> on cmdline
1844 int force, // when TRUE, ignore autocmd_busy
1845 buf_T *buf) // buffer for <abuf>
1846{
1847 return apply_autocmds_group(event, fname, fname_io, force,
1848 AUGROUP_ALL, buf, NULL);
1849}
1850
1851/*
1852 * Like apply_autocmds(), but with extra "eap" argument. This takes care of
1853 * setting v:filearg.
1854 */
1855 int
1856apply_autocmds_exarg(
1857 event_T event,
1858 char_u *fname,
1859 char_u *fname_io,
1860 int force,
1861 buf_T *buf,
1862 exarg_T *eap)
1863{
1864 return apply_autocmds_group(event, fname, fname_io, force,
1865 AUGROUP_ALL, buf, eap);
1866}
1867
1868/*
1869 * Like apply_autocmds(), but handles the caller's retval. If the script
1870 * processing is being aborted or if retval is FAIL when inside a try
1871 * conditional, no autocommands are executed. If otherwise the autocommands
1872 * cause the script to be aborted, retval is set to FAIL.
1873 */
1874 int
1875apply_autocmds_retval(
1876 event_T event,
1877 char_u *fname, // NULL or empty means use actual file name
1878 char_u *fname_io, // fname to use for <afile> on cmdline
1879 int force, // when TRUE, ignore autocmd_busy
1880 buf_T *buf, // buffer for <abuf>
1881 int *retval) // pointer to caller's retval
1882{
1883 int did_cmd;
1884
1885#ifdef FEAT_EVAL
1886 if (should_abort(*retval))
1887 return FALSE;
1888#endif
1889
1890 did_cmd = apply_autocmds_group(event, fname, fname_io, force,
1891 AUGROUP_ALL, buf, NULL);
1892 if (did_cmd
1893#ifdef FEAT_EVAL
1894 && aborting()
1895#endif
1896 )
1897 *retval = FAIL;
1898 return did_cmd;
1899}
1900
1901/*
1902 * Return TRUE when there is a CursorHold autocommand defined.
1903 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001904 static int
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001905has_cursorhold(void)
1906{
Bram Moolenaar24959102022-05-07 20:01:16 +01001907 return (first_autopat[(int)(get_real_state() == MODE_NORMAL_BUSY
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001908 ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
1909}
1910
1911/*
1912 * Return TRUE if the CursorHold event can be triggered.
1913 */
1914 int
1915trigger_cursorhold(void)
1916{
1917 int state;
1918
1919 if (!did_cursorhold
1920 && has_cursorhold()
1921 && reg_recording == 0
1922 && typebuf.tb_len == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001923 && !ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001924 {
1925 state = get_real_state();
Bram Moolenaar24959102022-05-07 20:01:16 +01001926 if (state == MODE_NORMAL_BUSY || (state & MODE_INSERT) != 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001927 return TRUE;
1928 }
1929 return FALSE;
1930}
1931
1932/*
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001933 * Return TRUE when there is a WinResized autocommand defined.
1934 */
1935 int
1936has_winresized(void)
1937{
1938 return (first_autopat[(int)EVENT_WINRESIZED] != NULL);
1939}
1940
1941/*
LemonBoy09371822022-04-08 15:18:45 +01001942 * Return TRUE when there is a WinScrolled autocommand defined.
1943 */
1944 int
1945has_winscrolled(void)
1946{
1947 return (first_autopat[(int)EVENT_WINSCROLLED] != NULL);
1948}
1949
1950/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001951 * Return TRUE when there is a CursorMoved autocommand defined.
1952 */
1953 int
1954has_cursormoved(void)
1955{
1956 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1957}
1958
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001959/*
1960 * Return TRUE when there is a CursorMovedI autocommand defined.
1961 */
1962 int
1963has_cursormovedI(void)
1964{
1965 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1966}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001967
1968/*
1969 * Return TRUE when there is a TextChanged autocommand defined.
1970 */
1971 int
1972has_textchanged(void)
1973{
1974 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1975}
1976
1977/*
1978 * Return TRUE when there is a TextChangedI autocommand defined.
1979 */
1980 int
1981has_textchangedI(void)
1982{
1983 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
1984}
1985
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001986/*
1987 * Return TRUE when there is a TextChangedP autocommand defined.
1988 */
1989 int
1990has_textchangedP(void)
1991{
1992 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
1993}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001994
1995/*
1996 * Return TRUE when there is an InsertCharPre autocommand defined.
1997 */
1998 int
1999has_insertcharpre(void)
2000{
2001 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
2002}
2003
2004/*
Shougo Matsushita83678842024-07-11 22:05:12 +02002005 * Return TRUE when there is an KeyInputPre autocommand defined.
2006 */
2007 int
2008has_keyinputpre(void)
2009{
2010 return (first_autopat[(int)EVENT_KEYINPUTPRE] != NULL);
2011}
2012
2013/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002014 * Return TRUE when there is an CmdUndefined autocommand defined.
2015 */
2016 int
2017has_cmdundefined(void)
2018{
2019 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
2020}
2021
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002022#if defined(FEAT_EVAL) || defined(PROTO)
2023/*
2024 * Return TRUE when there is a TextYankPost autocommand defined.
2025 */
2026 int
2027has_textyankpost(void)
2028{
2029 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
2030}
2031#endif
2032
Bram Moolenaard7f246c2019-04-08 18:15:41 +02002033#if defined(FEAT_EVAL) || defined(PROTO)
2034/*
2035 * Return TRUE when there is a CompleteChanged autocommand defined.
2036 */
2037 int
2038has_completechanged(void)
2039{
2040 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
2041}
2042#endif
2043
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002044#if defined(FEAT_EVAL) || defined(PROTO)
2045/*
2046 * Return TRUE when there is a ModeChanged autocommand defined.
2047 */
2048 int
2049has_modechanged(void)
2050{
2051 return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
2052}
2053#endif
2054
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002055/*
2056 * Execute autocommands for "event" and file name "fname".
2057 * Return TRUE if some commands were executed.
2058 */
2059 static int
2060apply_autocmds_group(
2061 event_T event,
2062 char_u *fname, // NULL or empty means use actual file name
2063 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
2064 // use fname
2065 int force, // when TRUE, ignore autocmd_busy
2066 int group, // group ID, or AUGROUP_ALL
2067 buf_T *buf, // buffer for <abuf>
2068 exarg_T *eap UNUSED) // command arguments
2069{
2070 char_u *sfname = NULL; // short file name
2071 char_u *tail;
2072 int save_changed;
2073 buf_T *old_curbuf;
2074 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002075 char_u *save_autocmd_fname;
2076 int save_autocmd_fname_full;
2077 int save_autocmd_bufnr;
2078 char_u *save_autocmd_match;
2079 int save_autocmd_busy;
2080 int save_autocmd_nested;
2081 static int nesting = 0;
LemonBoyeca7c602022-04-14 15:39:43 +01002082 AutoPatCmd_T patcmd;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002083 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002084 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002085#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002086 funccal_entry_T funccal_entry;
2087 char_u *save_cmdarg;
2088 long save_cmdbang;
2089#endif
2090 static int filechangeshell_busy = FALSE;
2091#ifdef FEAT_PROFILE
2092 proftime_T wait_time;
2093#endif
2094 int did_save_redobuff = FALSE;
2095 save_redo_T save_redo;
2096 int save_KeyTyped = KeyTyped;
ichizok7e5fe382023-04-15 13:17:50 +01002097 ESTACK_CHECK_DECLARATION;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002098
2099 /*
2100 * Quickly return if there are no autocommands for this event or
2101 * autocommands are blocked.
2102 */
2103 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
2104 || autocmd_blocked > 0)
2105 goto BYPASS_AU;
2106
2107 /*
2108 * When autocommands are busy, new autocommands are only executed when
2109 * explicitly enabled with the "nested" flag.
2110 */
2111 if (autocmd_busy && !(force || autocmd_nested))
2112 goto BYPASS_AU;
2113
2114#ifdef FEAT_EVAL
2115 /*
2116 * Quickly return when immediately aborting on error, or when an interrupt
2117 * occurred or an exception was thrown but not caught.
2118 */
2119 if (aborting())
2120 goto BYPASS_AU;
2121#endif
2122
2123 /*
2124 * FileChangedShell never nests, because it can create an endless loop.
2125 */
2126 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
2127 || event == EVENT_FILECHANGEDSHELLPOST))
2128 goto BYPASS_AU;
2129
2130 /*
2131 * Ignore events in 'eventignore'.
2132 */
Luuk van Baalb7147f82025-02-08 18:52:39 +01002133 if (event_ignored(event, p_ei))
2134 goto BYPASS_AU;
2135
2136 wininfo_T *wip;
2137 int win_ignore = FALSE;
2138 // If event is allowed in 'eventignorewin', check if curwin or all windows
2139 // into "buf" are ignoring the event.
2140 if (buf == curbuf && event_tab[event].key <= 0)
2141 win_ignore = event_ignored(event, curwin->w_p_eiw);
2142 else if (buf != NULL && event_tab[event].key <= 0)
2143 FOR_ALL_BUF_WININFO(buf, wip)
2144 if (wip->wi_win != NULL && wip->wi_win->w_buffer == buf)
2145 win_ignore = event_ignored(event, wip->wi_win->w_p_eiw);
2146 if (win_ignore)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002147 goto BYPASS_AU;
2148
2149 /*
2150 * Allow nesting of autocommands, but restrict the depth, because it's
2151 * possible to create an endless loop.
2152 */
2153 if (nesting == 10)
2154 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00002155 emsg(_(e_autocommand_nesting_too_deep));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002156 goto BYPASS_AU;
2157 }
2158
2159 /*
2160 * Check if these autocommands are disabled. Used when doing ":all" or
2161 * ":ball".
2162 */
2163 if ( (autocmd_no_enter
2164 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
2165 || (autocmd_no_leave
2166 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
2167 goto BYPASS_AU;
2168
Bram Moolenaarbb393d82022-12-09 12:21:50 +00002169 if (event == EVENT_CMDLINECHANGED)
2170 ++aucmd_cmdline_changed_count;
2171
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002172 /*
2173 * Save the autocmd_* variables and info about the current buffer.
2174 */
2175 save_autocmd_fname = autocmd_fname;
2176 save_autocmd_fname_full = autocmd_fname_full;
2177 save_autocmd_bufnr = autocmd_bufnr;
2178 save_autocmd_match = autocmd_match;
2179 save_autocmd_busy = autocmd_busy;
2180 save_autocmd_nested = autocmd_nested;
2181 save_changed = curbuf->b_changed;
2182 old_curbuf = curbuf;
2183
2184 /*
2185 * Set the file name to be used for <afile>.
2186 * Make a copy to avoid that changing a buffer name or directory makes it
2187 * invalid.
2188 */
2189 if (fname_io == NULL)
2190 {
2191 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
Bram Moolenaarbb393d82022-12-09 12:21:50 +00002192 || event == EVENT_OPTIONSET
Danek Duvalld7d56032024-01-14 20:19:59 +01002193 || event == EVENT_MODECHANGED
2194 || event == EVENT_TERMRESPONSEALL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002195 autocmd_fname = NULL;
2196 else if (fname != NULL && !ends_excmd(*fname))
2197 autocmd_fname = fname;
2198 else if (buf != NULL)
2199 autocmd_fname = buf->b_ffname;
2200 else
2201 autocmd_fname = NULL;
2202 }
2203 else
2204 autocmd_fname = fname_io;
2205 if (autocmd_fname != NULL)
2206 autocmd_fname = vim_strsave(autocmd_fname);
2207 autocmd_fname_full = FALSE; // call FullName_save() later
2208
2209 /*
2210 * Set the buffer number to be used for <abuf>.
2211 */
2212 if (buf == NULL)
2213 autocmd_bufnr = 0;
2214 else
2215 autocmd_bufnr = buf->b_fnum;
2216
2217 /*
2218 * When the file name is NULL or empty, use the file name of buffer "buf".
2219 * Always use the full path of the file name to match with, in case
2220 * "allow_dirs" is set.
2221 */
2222 if (fname == NULL || *fname == NUL)
2223 {
2224 if (buf == NULL)
2225 fname = NULL;
2226 else
2227 {
2228#ifdef FEAT_SYN_HL
2229 if (event == EVENT_SYNTAX)
2230 fname = buf->b_p_syn;
2231 else
2232#endif
2233 if (event == EVENT_FILETYPE)
2234 fname = buf->b_p_ft;
2235 else
2236 {
2237 if (buf->b_sfname != NULL)
2238 sfname = vim_strsave(buf->b_sfname);
2239 fname = buf->b_ffname;
2240 }
2241 }
2242 if (fname == NULL)
2243 fname = (char_u *)"";
2244 fname = vim_strsave(fname); // make a copy, so we can change it
2245 }
2246 else
2247 {
2248 sfname = vim_strsave(fname);
2249 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002250 // ColorScheme, QuickFixCmd*, DirChanged and similar.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002251 if (event == EVENT_FILETYPE
2252 || event == EVENT_SYNTAX
2253 || event == EVENT_CMDLINECHANGED
2254 || event == EVENT_CMDLINEENTER
2255 || event == EVENT_CMDLINELEAVE
Shougo Matsushitad0952142024-06-20 22:05:16 +02002256 || event == EVENT_CURSORMOVEDC
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002257 || event == EVENT_CMDWINENTER
2258 || event == EVENT_CMDWINLEAVE
2259 || event == EVENT_CMDUNDEFINED
2260 || event == EVENT_FUNCUNDEFINED
Shougo Matsushita83678842024-07-11 22:05:12 +02002261 || event == EVENT_KEYINPUTPRE
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002262 || event == EVENT_REMOTEREPLY
2263 || event == EVENT_SPELLFILEMISSING
2264 || event == EVENT_QUICKFIXCMDPRE
2265 || event == EVENT_COLORSCHEME
2266 || event == EVENT_COLORSCHEMEPRE
2267 || event == EVENT_OPTIONSET
2268 || event == EVENT_QUICKFIXCMDPOST
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002269 || event == EVENT_DIRCHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002270 || event == EVENT_DIRCHANGEDPRE
naohiro ono23beefe2021-11-13 12:38:49 +00002271 || event == EVENT_MODECHANGED
zeertzjqc601d982022-10-10 13:46:15 +01002272 || event == EVENT_MENUPOPUP
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002273 || event == EVENT_USER
LemonBoy09371822022-04-08 15:18:45 +01002274 || event == EVENT_WINCLOSED
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00002275 || event == EVENT_WINRESIZED
Danek Duvalld7d56032024-01-14 20:19:59 +01002276 || event == EVENT_WINSCROLLED
2277 || event == EVENT_TERMRESPONSEALL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002278 {
2279 fname = vim_strsave(fname);
2280 autocmd_fname_full = TRUE; // don't expand it later
2281 }
2282 else
2283 fname = FullName_save(fname, FALSE);
2284 }
2285 if (fname == NULL) // out of memory
2286 {
2287 vim_free(sfname);
2288 retval = FALSE;
2289 goto BYPASS_AU;
2290 }
2291
2292#ifdef BACKSLASH_IN_FILENAME
2293 /*
2294 * Replace all backslashes with forward slashes. This makes the
2295 * autocommand patterns portable between Unix and MS-DOS.
2296 */
2297 if (sfname != NULL)
2298 forward_slash(sfname);
2299 forward_slash(fname);
2300#endif
2301
2302#ifdef VMS
2303 // remove version for correct match
2304 if (sfname != NULL)
2305 vms_remove_version(sfname);
2306 vms_remove_version(fname);
2307#endif
2308
2309 /*
2310 * Set the name to be used for <amatch>.
2311 */
2312 autocmd_match = fname;
2313
2314
2315 // Don't redraw while doing autocommands.
2316 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002317
2318 // name and lnum are filled in later
2319 estack_push(ETYPE_AUCMD, NULL, 0);
ichizok7e5fe382023-04-15 13:17:50 +01002320 ESTACK_CHECK_SETUP;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002321
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002322 save_current_sctx = current_sctx;
2323
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002324#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002325# ifdef FEAT_PROFILE
2326 if (do_profiling == PROF_YES)
2327 prof_child_enter(&wait_time); // doesn't count for the caller itself
2328# endif
2329
2330 // Don't use local function variables, if called from a function.
2331 save_funccal(&funccal_entry);
2332#endif
2333
2334 /*
2335 * When starting to execute autocommands, save the search patterns.
2336 */
2337 if (!autocmd_busy)
2338 {
2339 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002340 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002341 {
2342 saveRedobuff(&save_redo);
2343 did_save_redobuff = TRUE;
2344 }
zeertzjq5bf6c212024-03-31 18:41:27 +02002345 curbuf->b_did_filetype = curbuf->b_keep_filetype;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002346 }
2347
2348 /*
2349 * Note that we are applying autocmds. Some commands need to know.
2350 */
2351 autocmd_busy = TRUE;
2352 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2353 ++nesting; // see matching decrement below
2354
2355 // Remember that FileType was triggered. Used for did_filetype().
2356 if (event == EVENT_FILETYPE)
zeertzjq5bf6c212024-03-31 18:41:27 +02002357 curbuf->b_did_filetype = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002358
2359 tail = gettail(fname);
2360
2361 // Find first autocommand that matches
LemonBoyeca7c602022-04-14 15:39:43 +01002362 CLEAR_FIELD(patcmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002363 patcmd.curpat = first_autopat[(int)event];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002364 patcmd.group = group;
2365 patcmd.fname = fname;
2366 patcmd.sfname = sfname;
2367 patcmd.tail = tail;
2368 patcmd.event = event;
2369 patcmd.arg_bufnr = autocmd_bufnr;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002370 auto_next_pat(&patcmd, FALSE);
2371
2372 // found one, start executing the autocommands
2373 if (patcmd.curpat != NULL)
2374 {
2375 // add to active_apc_list
2376 patcmd.next = active_apc_list;
2377 active_apc_list = &patcmd;
2378
2379#ifdef FEAT_EVAL
2380 // set v:cmdarg (only when there is a matching pattern)
2381 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2382 if (eap != NULL)
2383 {
2384 save_cmdarg = set_cmdarg(eap, NULL);
2385 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2386 }
2387 else
2388 save_cmdarg = NULL; // avoid gcc warning
2389#endif
2390 retval = TRUE;
2391 // mark the last pattern, to avoid an endless loop when more patterns
2392 // are added when executing autocommands
2393 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2394 ap->last = FALSE;
2395 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002396
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002397 // Make sure cursor and topline are valid. The first time the current
2398 // values are saved, restored by reset_lnums(). When nested only the
2399 // values are corrected when needed.
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002400 if (nesting == 1)
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002401 check_lnums(TRUE);
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002402 else
2403 check_lnums_nested(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002404
Christian Brabandt590aae32023-06-25 22:34:22 +01002405 int save_did_emsg = did_emsg;
2406 int save_ex_pressedreturn = get_pressedreturn();
ichizokc3f91c02021-12-17 09:44:33 +00002407
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002408 do_cmdline(NULL, getnextac, (void *)&patcmd,
2409 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002410
ichizokc3f91c02021-12-17 09:44:33 +00002411 did_emsg += save_did_emsg;
Christian Brabandt590aae32023-06-25 22:34:22 +01002412 set_pressedreturn(save_ex_pressedreturn);
ichizokc3f91c02021-12-17 09:44:33 +00002413
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002414 if (nesting == 1)
2415 // restore cursor and topline, unless they were changed
2416 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002417
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002418#ifdef FEAT_EVAL
2419 if (eap != NULL)
2420 {
2421 (void)set_cmdarg(NULL, save_cmdarg);
2422 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2423 }
2424#endif
2425 // delete from active_apc_list
2426 if (active_apc_list == &patcmd) // just in case
2427 active_apc_list = patcmd.next;
2428 }
2429
Bram Moolenaar79cdf022023-05-20 14:07:00 +01002430 if (RedrawingDisabled > 0)
2431 --RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002432 autocmd_busy = save_autocmd_busy;
2433 filechangeshell_busy = FALSE;
2434 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002435 vim_free(SOURCING_NAME);
ichizok7e5fe382023-04-15 13:17:50 +01002436 ESTACK_CHECK_NOW;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002437 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002438 vim_free(autocmd_fname);
2439 autocmd_fname = save_autocmd_fname;
2440 autocmd_fname_full = save_autocmd_fname_full;
2441 autocmd_bufnr = save_autocmd_bufnr;
2442 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002443 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002444#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002445 restore_funccal();
2446# ifdef FEAT_PROFILE
2447 if (do_profiling == PROF_YES)
2448 prof_child_exit(&wait_time);
2449# endif
2450#endif
2451 KeyTyped = save_KeyTyped;
2452 vim_free(fname);
2453 vim_free(sfname);
2454 --nesting; // see matching increment above
2455
2456 /*
2457 * When stopping to execute autocommands, restore the search patterns and
2458 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2459 * free any windows in the au_pending_free_win list.
2460 */
2461 if (!autocmd_busy)
2462 {
2463 restore_search_patterns();
2464 if (did_save_redobuff)
2465 restoreRedobuff(&save_redo);
zeertzjq5bf6c212024-03-31 18:41:27 +02002466 curbuf->b_did_filetype = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002467 while (au_pending_free_buf != NULL)
2468 {
2469 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002470
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002471 vim_free(au_pending_free_buf);
2472 au_pending_free_buf = b;
2473 }
2474 while (au_pending_free_win != NULL)
2475 {
2476 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002477
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002478 vim_free(au_pending_free_win);
2479 au_pending_free_win = w;
2480 }
2481 }
2482
2483 /*
2484 * Some events don't set or reset the Changed flag.
2485 * Check if still in the same buffer!
2486 */
2487 if (curbuf == old_curbuf
2488 && (event == EVENT_BUFREADPOST
2489 || event == EVENT_BUFWRITEPOST
2490 || event == EVENT_FILEAPPENDPOST
2491 || event == EVENT_VIMLEAVE
2492 || event == EVENT_VIMLEAVEPRE))
2493 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002494 if (curbuf->b_changed != save_changed)
2495 need_maketitle = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002496 curbuf->b_changed = save_changed;
2497 }
2498
2499 au_cleanup(); // may really delete removed patterns/commands now
2500
2501BYPASS_AU:
2502 // When wiping out a buffer make sure all its buffer-local autocommands
2503 // are deleted.
2504 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2505 aubuflocal_remove(buf);
2506
2507 if (retval == OK && event == EVENT_FILETYPE)
zeertzjq5bf6c212024-03-31 18:41:27 +02002508 curbuf->b_au_did_filetype = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002509
2510 return retval;
2511}
2512
2513# ifdef FEAT_EVAL
2514static char_u *old_termresponse = NULL;
Danek Duvalld7d56032024-01-14 20:19:59 +01002515static char_u *old_termu7resp = NULL;
2516static char_u *old_termblinkresp = NULL;
2517static char_u *old_termrbgresp = NULL;
2518static char_u *old_termrfgresp = NULL;
2519static char_u *old_termstyleresp = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002520# endif
2521
2522/*
2523 * Block triggering autocommands until unblock_autocmd() is called.
2524 * Can be used recursively, so long as it's symmetric.
2525 */
2526 void
2527block_autocmds(void)
2528{
2529# ifdef FEAT_EVAL
2530 // Remember the value of v:termresponse.
2531 if (autocmd_blocked == 0)
Danek Duvalld7d56032024-01-14 20:19:59 +01002532 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002533 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
Danek Duvalld7d56032024-01-14 20:19:59 +01002534 old_termu7resp = get_vim_var_str(VV_TERMU7RESP);
2535 old_termblinkresp = get_vim_var_str(VV_TERMBLINKRESP);
2536 old_termrbgresp = get_vim_var_str(VV_TERMRBGRESP);
2537 old_termrfgresp = get_vim_var_str(VV_TERMRFGRESP);
2538 old_termstyleresp = get_vim_var_str(VV_TERMSTYLERESP);
2539 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002540# endif
2541 ++autocmd_blocked;
2542}
2543
2544 void
2545unblock_autocmds(void)
2546{
2547 --autocmd_blocked;
2548
2549# ifdef FEAT_EVAL
Danek Duvalld7d56032024-01-14 20:19:59 +01002550 // When v:termresponse, etc, were set while autocommands were blocked,
2551 // trigger the autocommands now. Esp. useful when executing a shell
2552 // command during startup (vimdiff).
2553 if (autocmd_blocked == 0)
2554 {
2555 if (get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2556 {
2557 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2558 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"version", NULL, FALSE, curbuf);
2559 }
2560 if (get_vim_var_str(VV_TERMU7RESP) != old_termu7resp)
2561 {
2562 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"ambiguouswidth", NULL, FALSE, curbuf);
2563 }
2564 if (get_vim_var_str(VV_TERMBLINKRESP) != old_termblinkresp)
2565 {
2566 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"cursorblink", NULL, FALSE, curbuf);
2567 }
2568 if (get_vim_var_str(VV_TERMRBGRESP) != old_termrbgresp)
2569 {
2570 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"background", NULL, FALSE, curbuf);
2571 }
2572 if (get_vim_var_str(VV_TERMRFGRESP) != old_termrfgresp)
2573 {
2574 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"foreground", NULL, FALSE, curbuf);
2575 }
2576 if (get_vim_var_str(VV_TERMSTYLERESP) != old_termstyleresp)
2577 {
2578 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"cursorshape", NULL, FALSE, curbuf);
2579 }
2580 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002581# endif
2582}
2583
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002584 int
2585is_autocmd_blocked(void)
2586{
2587 return autocmd_blocked != 0;
2588}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002589
2590/*
2591 * Find next autocommand pattern that matches.
2592 */
2593 static void
2594auto_next_pat(
LemonBoyeca7c602022-04-14 15:39:43 +01002595 AutoPatCmd_T *apc,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002596 int stop_at_last) // stop when 'last' flag is set
2597{
2598 AutoPat *ap;
2599 AutoCmd *cp;
2600 char_u *name;
2601 char *s;
LemonBoyeca7c602022-04-14 15:39:43 +01002602 estack_T *entry;
2603 char_u *namep;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002604
LemonBoyeca7c602022-04-14 15:39:43 +01002605 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
2606
2607 // Clear the exestack entry for this ETYPE_AUCMD entry.
2608 VIM_CLEAR(entry->es_name);
2609 entry->es_info.aucmd = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002610
2611 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2612 {
2613 apc->curpat = NULL;
2614
2615 // Only use a pattern when it has not been removed, has commands and
2616 // the group matches. For buffer-local autocommands only check the
2617 // buffer number.
2618 if (ap->pat != NULL && ap->cmds != NULL
2619 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2620 {
2621 // execution-condition
2622 if (ap->buflocal_nr == 0
2623 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2624 apc->sfname, apc->tail, ap->allow_dirs))
2625 : ap->buflocal_nr == apc->arg_bufnr)
2626 {
2627 name = event_nr2name(apc->event);
2628 s = _("%s Autocommands for \"%s\"");
LemonBoyeca7c602022-04-14 15:39:43 +01002629 namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1);
2630 if (namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002631 {
LemonBoyeca7c602022-04-14 15:39:43 +01002632 sprintf((char *)namep, s, (char *)name, (char *)ap->pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002633 if (p_verbose >= 8)
2634 {
2635 verbose_enter();
LemonBoyeca7c602022-04-14 15:39:43 +01002636 smsg(_("Executing %s"), namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002637 verbose_leave();
2638 }
2639 }
2640
LemonBoyeca7c602022-04-14 15:39:43 +01002641 // Update the exestack entry for this autocmd.
2642 entry->es_name = namep;
2643 entry->es_info.aucmd = apc;
2644
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002645 apc->curpat = ap;
2646 apc->nextcmd = ap->cmds;
2647 // mark last command
2648 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2649 cp->last = FALSE;
2650 cp->last = TRUE;
2651 }
2652 line_breakcheck();
2653 if (apc->curpat != NULL) // found a match
2654 break;
2655 }
2656 if (stop_at_last && ap->last)
2657 break;
2658 }
2659}
2660
Dominique Pellee764d1b2023-03-12 21:20:59 +00002661#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002662/*
LemonBoyeca7c602022-04-14 15:39:43 +01002663 * Get the script context where autocommand "acp" is defined.
2664 */
2665 sctx_T *
2666acp_script_ctx(AutoPatCmd_T *acp)
2667{
2668 return &acp->script_ctx;
2669}
Dominique Pellee764d1b2023-03-12 21:20:59 +00002670#endif
LemonBoyeca7c602022-04-14 15:39:43 +01002671
2672/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002673 * Get next autocommand command.
2674 * Called by do_cmdline() to get the next line for ":if".
2675 * Returns allocated string, or NULL for end of autocommands.
2676 */
2677 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002678getnextac(
2679 int c UNUSED,
2680 void *cookie,
2681 int indent UNUSED,
2682 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002683{
LemonBoyeca7c602022-04-14 15:39:43 +01002684 AutoPatCmd_T *acp = (AutoPatCmd_T *)cookie;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002685 char_u *retval;
2686 AutoCmd *ac;
2687
2688 // Can be called again after returning the last line.
2689 if (acp->curpat == NULL)
2690 return NULL;
2691
2692 // repeat until we find an autocommand to execute
2693 for (;;)
2694 {
2695 // skip removed commands
2696 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2697 if (acp->nextcmd->last)
2698 acp->nextcmd = NULL;
2699 else
2700 acp->nextcmd = acp->nextcmd->next;
2701
2702 if (acp->nextcmd != NULL)
2703 break;
2704
2705 // at end of commands, find next pattern that matches
2706 if (acp->curpat->last)
2707 acp->curpat = NULL;
2708 else
2709 acp->curpat = acp->curpat->next;
2710 if (acp->curpat != NULL)
2711 auto_next_pat(acp, TRUE);
2712 if (acp->curpat == NULL)
2713 return NULL;
2714 }
2715
2716 ac = acp->nextcmd;
2717
2718 if (p_verbose >= 9)
2719 {
2720 verbose_enter_scroll();
2721 smsg(_("autocommand %s"), ac->cmd);
2722 msg_puts("\n"); // don't overwrite this either
2723 verbose_leave_scroll();
2724 }
2725 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002726 // Remove one-shot ("once") autocmd in anticipation of its execution.
2727 if (ac->once)
2728 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002729 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002730 current_sctx = ac->script_ctx;
LemonBoyeca7c602022-04-14 15:39:43 +01002731 acp->script_ctx = current_sctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002732 if (ac->last)
2733 acp->nextcmd = NULL;
2734 else
2735 acp->nextcmd = ac->next;
2736 return retval;
2737}
2738
2739/*
2740 * Return TRUE if there is a matching autocommand for "fname".
2741 * To account for buffer-local autocommands, function needs to know
2742 * in which buffer the file will be opened.
2743 */
2744 int
2745has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2746{
2747 AutoPat *ap;
2748 char_u *fname;
2749 char_u *tail = gettail(sfname);
2750 int retval = FALSE;
2751
2752 fname = FullName_save(sfname, FALSE);
2753 if (fname == NULL)
2754 return FALSE;
2755
2756#ifdef BACKSLASH_IN_FILENAME
2757 /*
2758 * Replace all backslashes with forward slashes. This makes the
2759 * autocommand patterns portable between Unix and MS-DOS.
2760 */
2761 sfname = vim_strsave(sfname);
2762 if (sfname != NULL)
2763 forward_slash(sfname);
2764 forward_slash(fname);
2765#endif
2766
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002767 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002768 if (ap->pat != NULL && ap->cmds != NULL
2769 && (ap->buflocal_nr == 0
2770 ? match_file_pat(NULL, &ap->reg_prog,
2771 fname, sfname, tail, ap->allow_dirs)
2772 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2773 ))
2774 {
2775 retval = TRUE;
2776 break;
2777 }
2778
2779 vim_free(fname);
2780#ifdef BACKSLASH_IN_FILENAME
2781 vim_free(sfname);
2782#endif
2783
2784 return retval;
2785}
2786
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002787/*
2788 * Function given to ExpandGeneric() to obtain the list of autocommand group
2789 * names.
2790 */
2791 char_u *
2792get_augroup_name(expand_T *xp UNUSED, int idx)
2793{
2794 if (idx == augroups.ga_len) // add "END" add the end
2795 return (char_u *)"END";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002796 if (idx < 0 || idx >= augroups.ga_len) // end of list
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002797 return NULL;
2798 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2799 // skip deleted entries
2800 return (char_u *)"";
2801 return AUGROUP_NAME(idx); // return a name
2802}
2803
2804static int include_groups = FALSE;
2805
2806 char_u *
2807set_context_in_autocmd(
2808 expand_T *xp,
2809 char_u *arg,
2810 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2811{
2812 char_u *p;
2813 int group;
2814
2815 // check for a group name, skip it if present
2816 include_groups = FALSE;
2817 p = arg;
2818 group = au_get_grouparg(&arg);
2819 if (group == AUGROUP_ERROR)
2820 return NULL;
2821 // If there only is a group name that's what we expand.
2822 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2823 {
2824 arg = p;
2825 group = AUGROUP_ALL;
2826 }
2827
2828 // skip over event name
2829 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2830 if (*p == ',')
2831 arg = p + 1;
2832 if (*p == NUL)
2833 {
2834 if (group == AUGROUP_ALL)
2835 include_groups = TRUE;
2836 xp->xp_context = EXPAND_EVENTS; // expand event name
2837 xp->xp_pattern = arg;
2838 return NULL;
2839 }
2840
2841 // skip over pattern
2842 arg = skipwhite(p);
2843 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2844 arg++;
2845 if (*arg)
2846 return arg; // expand (next) command
2847
2848 if (doautocmd)
2849 xp->xp_context = EXPAND_FILES; // expand file names
2850 else
2851 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2852 return NULL;
2853}
2854
2855/*
2856 * Function given to ExpandGeneric() to obtain the list of event names.
2857 */
2858 char_u *
2859get_event_name(expand_T *xp UNUSED, int idx)
2860{
John Marriott78d742a2024-04-02 20:26:01 +02002861 int i;
2862
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002863 if (idx < augroups.ga_len) // First list group names, if wanted
2864 {
2865 if (!include_groups || AUGROUP_NAME(idx) == NULL
2866 || AUGROUP_NAME(idx) == get_deleted_augroup())
2867 return (char_u *)""; // skip deleted entries
2868 return AUGROUP_NAME(idx); // return a name
2869 }
John Marriott78d742a2024-04-02 20:26:01 +02002870
2871 i = idx - augroups.ga_len;
Luuk van Baalb7147f82025-02-08 18:52:39 +01002872 if (i < 0 || i >= NUM_EVENTS)
John Marriott78d742a2024-04-02 20:26:01 +02002873 return NULL;
2874
John Marriott8d4477e2024-11-02 15:59:01 +01002875 return event_tab[i].value.string;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002876}
2877
Yee Cheng Chin900894b2023-09-29 20:42:32 +02002878/*
2879 * Function given to ExpandGeneric() to obtain the list of event names. Don't
2880 * include groups.
2881 */
2882 char_u *
Luuk van Baalb7147f82025-02-08 18:52:39 +01002883get_event_name_no_group(expand_T *xp UNUSED, int idx, int win)
Yee Cheng Chin900894b2023-09-29 20:42:32 +02002884{
Luuk van Baalb7147f82025-02-08 18:52:39 +01002885 if (idx < 0 || idx >= NUM_EVENTS)
John Marriott78d742a2024-04-02 20:26:01 +02002886 return NULL;
2887
Luuk van Baalb7147f82025-02-08 18:52:39 +01002888 if (!win)
2889 return event_tab[idx].value.string;
2890
2891 // Need to check subset of allowed values for 'eventignorewin'.
2892 int j = 0;
2893 for (int i = 0; i < NUM_EVENTS; ++i)
2894 {
2895 j += event_tab[i].key <= 0;
2896 if (j == idx + 1)
2897 return event_tab[i].value.string;
2898 }
2899
2900 return NULL;
Yee Cheng Chin900894b2023-09-29 20:42:32 +02002901}
2902
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002903
2904#if defined(FEAT_EVAL) || defined(PROTO)
2905/*
2906 * Return TRUE if autocmd is supported.
2907 */
2908 int
2909autocmd_supported(char_u *name)
2910{
2911 char_u *p;
2912
2913 return (event_name2nr(name, &p) != NUM_EVENTS);
2914}
2915
2916/*
2917 * Return TRUE if an autocommand is defined for a group, event and
2918 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2919 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2920 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2921 * Used for:
2922 * exists("#Group") or
2923 * exists("#Group#Event") or
2924 * exists("#Group#Event#pat") or
2925 * exists("#Event") or
2926 * exists("#Event#pat")
2927 */
2928 int
2929au_exists(char_u *arg)
2930{
2931 char_u *arg_save;
2932 char_u *pattern = NULL;
2933 char_u *event_name;
2934 char_u *p;
2935 event_T event;
2936 AutoPat *ap;
2937 buf_T *buflocal_buf = NULL;
2938 int group;
2939 int retval = FALSE;
2940
2941 // Make a copy so that we can change the '#' chars to a NUL.
2942 arg_save = vim_strsave(arg);
2943 if (arg_save == NULL)
2944 return FALSE;
2945 p = vim_strchr(arg_save, '#');
2946 if (p != NULL)
2947 *p++ = NUL;
2948
2949 // First, look for an autocmd group name
2950 group = au_find_group(arg_save);
2951 if (group == AUGROUP_ERROR)
2952 {
2953 // Didn't match a group name, assume the first argument is an event.
2954 group = AUGROUP_ALL;
2955 event_name = arg_save;
2956 }
2957 else
2958 {
2959 if (p == NULL)
2960 {
2961 // "Group": group name is present and it's recognized
2962 retval = TRUE;
2963 goto theend;
2964 }
2965
2966 // Must be "Group#Event" or "Group#Event#pat".
2967 event_name = p;
2968 p = vim_strchr(event_name, '#');
2969 if (p != NULL)
2970 *p++ = NUL; // "Group#Event#pat"
2971 }
2972
2973 pattern = p; // "pattern" is NULL when there is no pattern
2974
2975 // find the index (enum) for the event name
2976 event = event_name2nr(event_name, &p);
2977
2978 // return FALSE if the event name is not recognized
2979 if (event == NUM_EVENTS)
2980 goto theend;
2981
2982 // Find the first autocommand for this event.
2983 // If there isn't any, return FALSE;
2984 // If there is one and no pattern given, return TRUE;
2985 ap = first_autopat[(int)event];
2986 if (ap == NULL)
2987 goto theend;
2988
2989 // if pattern is "<buffer>", special handling is needed which uses curbuf
2990 // for pattern "<buffer=N>, fnamecmp() will work fine
2991 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2992 buflocal_buf = curbuf;
2993
2994 // Check if there is an autocommand with the given pattern.
2995 for ( ; ap != NULL; ap = ap->next)
2996 // only use a pattern when it has not been removed and has commands.
2997 // For buffer-local autocommands, fnamecmp() works fine.
2998 if (ap->pat != NULL && ap->cmds != NULL
2999 && (group == AUGROUP_ALL || ap->group == group)
3000 && (pattern == NULL
3001 || (buflocal_buf == NULL
3002 ? fnamecmp(ap->pat, pattern) == 0
3003 : ap->buflocal_nr == buflocal_buf->b_fnum)))
3004 {
3005 retval = TRUE;
3006 break;
3007 }
3008
3009theend:
3010 vim_free(arg_save);
3011 return retval;
3012}
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003013
3014/*
3015 * autocmd_add() and autocmd_delete() functions
3016 */
3017 static void
3018autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
3019{
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003020 list_T *aucmd_list;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003021 listitem_T *li;
3022 dict_T *event_dict;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003023 dictitem_T *di;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003024 char_u *event_name = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003025 list_T *event_list;
3026 listitem_T *eli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003027 event_T event;
3028 char_u *group_name = NULL;
3029 int group;
3030 char_u *pat = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003031 list_T *pat_list;
3032 listitem_T *pli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003033 char_u *cmd = NULL;
3034 char_u *end;
3035 int once;
3036 int nested;
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01003037 int replace; // replace the cmd for a group/event
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003038 int retval = VVAL_TRUE;
3039 int save_augroup = current_augroup;
3040
3041 rettv->v_type = VAR_BOOL;
3042 rettv->vval.v_number = VVAL_FALSE;
3043
3044 if (check_for_list_arg(argvars, 0) == FAIL)
3045 return;
3046
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003047 aucmd_list = argvars[0].vval.v_list;
3048 if (aucmd_list == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003049 return;
3050
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003051 FOR_ALL_LIST_ITEMS(aucmd_list, li)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003052 {
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003053 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003054 VIM_CLEAR(cmd);
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003055 event_name = NULL;
3056 event_list = NULL;
3057 pat = NULL;
3058 pat_list = NULL;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003059
3060 if (li->li_tv.v_type != VAR_DICT)
3061 continue;
3062
3063 event_dict = li->li_tv.vval.v_dict;
3064 if (event_dict == NULL)
3065 continue;
3066
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003067 di = dict_find(event_dict, (char_u *)"event", -1);
3068 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003069 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003070 if (di->di_tv.v_type == VAR_STRING)
3071 {
3072 event_name = di->di_tv.vval.v_string;
3073 if (event_name == NULL)
3074 {
3075 emsg(_(e_string_required));
3076 continue;
3077 }
3078 }
3079 else if (di->di_tv.v_type == VAR_LIST)
3080 {
3081 event_list = di->di_tv.vval.v_list;
3082 if (event_list == NULL)
3083 {
3084 emsg(_(e_list_required));
3085 continue;
3086 }
3087 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003088 else
3089 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003090 emsg(_(e_string_or_list_expected));
3091 continue;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003092 }
3093 }
3094
Bram Moolenaard61efa52022-07-23 09:52:04 +01003095 group_name = dict_get_string(event_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003096 if (group_name == NULL || *group_name == NUL)
3097 // if the autocmd group name is not specified, then use the current
3098 // autocmd group
3099 group = current_augroup;
3100 else
3101 {
3102 group = au_find_group(group_name);
3103 if (group == AUGROUP_ERROR)
3104 {
3105 if (delete)
3106 {
3107 semsg(_(e_no_such_group_str), group_name);
3108 retval = VVAL_FALSE;
3109 break;
3110 }
3111 // group is not found, create it now
3112 group = au_new_group(group_name);
3113 if (group == AUGROUP_ERROR)
3114 {
3115 semsg(_(e_no_such_group_str), group_name);
3116 retval = VVAL_FALSE;
3117 break;
3118 }
3119
3120 current_augroup = group;
3121 }
3122 }
3123
3124 // if a buffer number is specified, then generate a pattern of the form
3125 // "<buffer=n>. Otherwise, use the pattern supplied by the user.
3126 if (dict_has_key(event_dict, "bufnr"))
3127 {
3128 varnumber_T bnum;
3129
Bram Moolenaard61efa52022-07-23 09:52:04 +01003130 bnum = dict_get_number_def(event_dict, "bufnr", -1);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003131 if (bnum == -1)
3132 continue;
3133
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003134 vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
3135 pat = IObuff;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003136 }
3137 else
3138 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003139 di = dict_find(event_dict, (char_u *)"pattern", -1);
3140 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003141 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003142 if (di->di_tv.v_type == VAR_STRING)
3143 {
3144 pat = di->di_tv.vval.v_string;
3145 if (pat == NULL)
3146 {
3147 emsg(_(e_string_required));
3148 continue;
3149 }
3150 }
3151 else if (di->di_tv.v_type == VAR_LIST)
3152 {
3153 pat_list = di->di_tv.vval.v_list;
3154 if (pat_list == NULL)
3155 {
3156 emsg(_(e_list_required));
3157 continue;
3158 }
3159 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003160 else
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003161 {
3162 emsg(_(e_string_or_list_expected));
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003163 continue;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003164 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003165 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003166 else if (delete)
3167 pat = (char_u *)"";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003168 }
3169
Bram Moolenaard61efa52022-07-23 09:52:04 +01003170 once = dict_get_bool(event_dict, "once", FALSE);
3171 nested = dict_get_bool(event_dict, "nested", FALSE);
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01003172 // if 'replace' is true, then remove all the commands associated with
3173 // this autocmd event/group and add the new command.
Bram Moolenaard61efa52022-07-23 09:52:04 +01003174 replace = dict_get_bool(event_dict, "replace", FALSE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003175
Bram Moolenaard61efa52022-07-23 09:52:04 +01003176 cmd = dict_get_string(event_dict, "cmd", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003177 if (cmd == NULL)
3178 {
3179 if (delete)
3180 cmd = vim_strsave((char_u *)"");
3181 else
3182 continue;
3183 }
3184
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003185 if (delete && (event_name == NULL
3186 || (event_name[0] == '*' && event_name[1] == NUL)))
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003187 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003188 // if the event name is not specified or '*', delete all the events
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003189 for (event = (event_T)0; (int)event < NUM_EVENTS;
3190 event = (event_T)((int)event + 1))
3191 {
3192 if (do_autocmd_event(event, pat, once, nested, cmd, delete,
3193 group, 0) == FAIL)
3194 {
3195 retval = VVAL_FALSE;
3196 break;
3197 }
3198 }
3199 }
3200 else
3201 {
Bram Moolenaar968443e2022-05-27 21:16:34 +01003202 char_u *p = NULL;
3203
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003204 eli = NULL;
3205 end = NULL;
3206 while (TRUE)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003207 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003208 if (event_list != NULL)
3209 {
3210 if (eli == NULL)
3211 eli = event_list->lv_first;
3212 else
3213 eli = eli->li_next;
3214 if (eli == NULL)
3215 break;
3216 if (eli->li_tv.v_type != VAR_STRING
Bram Moolenaar882476a2022-06-01 16:02:38 +01003217 || (p = eli->li_tv.vval.v_string) == NULL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003218 {
3219 emsg(_(e_string_required));
Bram Moolenaar882476a2022-06-01 16:02:38 +01003220 break;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003221 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003222 }
3223 else
3224 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003225 if (p == NULL)
3226 p = event_name;
3227 if (p == NULL || *p == NUL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003228 break;
3229 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003230
3231 event = event_name2nr(p, &end);
3232 if (event == NUM_EVENTS || *end != NUL)
3233 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003234 // this also catches something following a valid event name
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003235 semsg(_(e_no_such_event_str), p);
3236 retval = VVAL_FALSE;
3237 break;
3238 }
3239 if (pat != NULL)
3240 {
3241 if (do_autocmd_event(event, pat, once, nested, cmd,
3242 delete | replace, group, 0) == FAIL)
3243 {
3244 retval = VVAL_FALSE;
3245 break;
3246 }
3247 }
3248 else if (pat_list != NULL)
3249 {
3250 FOR_ALL_LIST_ITEMS(pat_list, pli)
3251 {
3252 if (pli->li_tv.v_type != VAR_STRING
3253 || pli->li_tv.vval.v_string == NULL)
3254 {
3255 emsg(_(e_string_required));
3256 continue;
3257 }
3258 if (do_autocmd_event(event,
3259 pli->li_tv.vval.v_string, once, nested,
3260 cmd, delete | replace, group, 0) ==
3261 FAIL)
3262 {
3263 retval = VVAL_FALSE;
3264 break;
3265 }
3266 }
3267 if (retval == VVAL_FALSE)
3268 break;
3269 }
3270 if (event_name != NULL)
3271 p = end;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003272 }
3273 }
3274
3275 // if only the autocmd group name is specified for delete and the
3276 // autocmd event, pattern and cmd are not specified, then delete the
3277 // autocmd group.
3278 if (delete && group_name != NULL &&
3279 (event_name == NULL || event_name[0] == NUL)
3280 && (pat == NULL || pat[0] == NUL)
3281 && (cmd == NULL || cmd[0] == NUL))
3282 au_del_group(group_name);
3283 }
3284
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003285 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003286 VIM_CLEAR(cmd);
3287
3288 current_augroup = save_augroup;
3289 rettv->vval.v_number = retval;
3290}
3291
3292/*
3293 * autocmd_add() function
3294 */
3295 void
3296f_autocmd_add(typval_T *argvars, typval_T *rettv)
3297{
3298 autocmd_add_or_delete(argvars, rettv, FALSE);
3299}
3300
3301/*
3302 * autocmd_delete() function
3303 */
3304 void
3305f_autocmd_delete(typval_T *argvars, typval_T *rettv)
3306{
3307 autocmd_add_or_delete(argvars, rettv, TRUE);
3308}
3309
3310/*
3311 * autocmd_get() function
3312 * Returns a List of autocmds.
3313 */
3314 void
3315f_autocmd_get(typval_T *argvars, typval_T *rettv)
3316{
3317 event_T event_arg = NUM_EVENTS;
3318 event_T event;
3319 AutoPat *ap;
3320 AutoCmd *ac;
3321 list_T *event_list;
3322 dict_T *event_dict;
3323 char_u *event_name = NULL;
3324 char_u *pat = NULL;
3325 char_u *name = NULL;
3326 int group = AUGROUP_ALL;
3327
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01003328 if (rettv_list_alloc(rettv) == FAIL)
3329 return;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003330 if (check_for_opt_dict_arg(argvars, 0) == FAIL)
3331 return;
3332
3333 if (argvars[0].v_type == VAR_DICT)
3334 {
3335 // return only the autocmds in the specified group
3336 if (dict_has_key(argvars[0].vval.v_dict, "group"))
3337 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003338 name = dict_get_string(argvars[0].vval.v_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003339 if (name == NULL)
3340 return;
3341
3342 if (*name == NUL)
3343 group = AUGROUP_DEFAULT;
3344 else
3345 {
3346 group = au_find_group(name);
3347 if (group == AUGROUP_ERROR)
3348 {
3349 semsg(_(e_no_such_group_str), name);
3350 vim_free(name);
3351 return;
3352 }
3353 }
3354 vim_free(name);
3355 }
3356
3357 // return only the autocmds for the specified event
3358 if (dict_has_key(argvars[0].vval.v_dict, "event"))
3359 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003360 name = dict_get_string(argvars[0].vval.v_dict, "event", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003361 if (name == NULL)
3362 return;
3363
3364 if (name[0] == '*' && name[1] == NUL)
3365 event_arg = NUM_EVENTS;
3366 else
3367 {
John Marriott78d742a2024-04-02 20:26:01 +02003368 keyvalue_T target;
3369 keyvalue_T *entry;
3370
3371 target.key = 0;
John Marriott8d4477e2024-11-02 15:59:01 +01003372 target.value.string = name;
3373 target.value.length = STRLEN(target.value.string);
3374 entry = (keyvalue_T *)bsearch(&target, &event_tab,
Luuk van Baalb7147f82025-02-08 18:52:39 +01003375 NUM_EVENTS, sizeof(event_tab[0]), cmp_keyvalue_value_ni);
John Marriott78d742a2024-04-02 20:26:01 +02003376 if (entry == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003377 {
3378 semsg(_(e_no_such_event_str), name);
3379 vim_free(name);
3380 return;
3381 }
Luuk van Baalb7147f82025-02-08 18:52:39 +01003382 event_arg = (event_T)abs(entry->key);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003383 }
3384 vim_free(name);
3385 }
3386
3387 // return only the autocmds for the specified pattern
3388 if (dict_has_key(argvars[0].vval.v_dict, "pattern"))
3389 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003390 pat = dict_get_string(argvars[0].vval.v_dict, "pattern", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003391 if (pat == NULL)
3392 return;
3393 }
3394 }
3395
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003396 event_list = rettv->vval.v_list;
3397
3398 // iterate through all the autocmd events
3399 for (event = (event_T)0; (int)event < NUM_EVENTS;
3400 event = (event_T)((int)event + 1))
3401 {
3402 if (event_arg != NUM_EVENTS && event != event_arg)
3403 continue;
3404
3405 event_name = event_nr2name(event);
3406
3407 // iterate through all the patterns for this autocmd event
3408 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
3409 {
3410 char_u *group_name;
3411
zeertzjq2d1d5c62024-06-09 16:44:33 +02003412 if (ap->pat == NULL) // pattern has been removed
3413 continue;
3414
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003415 if (group != AUGROUP_ALL && group != ap->group)
3416 continue;
3417
3418 if (pat != NULL && STRCMP(pat, ap->pat) != 0)
3419 continue;
3420
3421 group_name = get_augroup_name(NULL, ap->group);
3422
3423 // iterate through all the commands for this pattern and add one
3424 // item for each cmd.
3425 for (ac = ap->cmds; ac != NULL; ac = ac->next)
3426 {
3427 event_dict = dict_alloc();
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003428 if (event_dict == NULL
3429 || list_append_dict(event_list, event_dict) == FAIL)
Christian Brabandt29269a72024-04-16 22:44:31 +02003430 {
3431 vim_free(pat);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003432 return;
Christian Brabandt29269a72024-04-16 22:44:31 +02003433 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003434
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003435 if (dict_add_string(event_dict, "event", event_name) == FAIL
3436 || dict_add_string(event_dict, "group",
3437 group_name == NULL ? (char_u *)""
3438 : group_name) == FAIL
3439 || (ap->buflocal_nr != 0
3440 && (dict_add_number(event_dict, "bufnr",
3441 ap->buflocal_nr) == FAIL))
3442 || dict_add_string(event_dict, "pattern",
3443 ap->pat) == FAIL
3444 || dict_add_string(event_dict, "cmd", ac->cmd) == FAIL
3445 || dict_add_bool(event_dict, "once", ac->once) == FAIL
3446 || dict_add_bool(event_dict, "nested",
3447 ac->nested) == FAIL)
Christian Brabandt29269a72024-04-16 22:44:31 +02003448 {
3449 vim_free(pat);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003450 return;
Christian Brabandt29269a72024-04-16 22:44:31 +02003451 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003452 }
3453 }
3454 }
3455
3456 vim_free(pat);
3457}
3458
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01003459#endif