blob: e60eac794d0b203c49439f80f752cb9cb2d65c86 [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
85// must be sorted by the 'value' field because it is used by bsearch()!
86static keyvalue_T event_tab[] = {
87 KEYVALUE_ENTRY(EVENT_BUFADD, "BufAdd"),
88 KEYVALUE_ENTRY(EVENT_BUFADD, "BufCreate"),
89 KEYVALUE_ENTRY(EVENT_BUFDELETE, "BufDelete"),
90 KEYVALUE_ENTRY(EVENT_BUFENTER, "BufEnter"),
91 KEYVALUE_ENTRY(EVENT_BUFFILEPOST, "BufFilePost"),
92 KEYVALUE_ENTRY(EVENT_BUFFILEPRE, "BufFilePre"),
93 KEYVALUE_ENTRY(EVENT_BUFHIDDEN, "BufHidden"),
94 KEYVALUE_ENTRY(EVENT_BUFLEAVE, "BufLeave"),
95 KEYVALUE_ENTRY(EVENT_BUFNEW, "BufNew"),
96 KEYVALUE_ENTRY(EVENT_BUFNEWFILE, "BufNewFile"), // BUFNEWFILE_INDEX
97 KEYVALUE_ENTRY(EVENT_BUFREADPOST, "BufRead"), // BUFREAD_INDEX
98 KEYVALUE_ENTRY(EVENT_BUFREADCMD, "BufReadCmd"),
99 KEYVALUE_ENTRY(EVENT_BUFREADPOST, "BufReadPost"),
100 KEYVALUE_ENTRY(EVENT_BUFREADPRE, "BufReadPre"),
101 KEYVALUE_ENTRY(EVENT_BUFUNLOAD, "BufUnload"),
102 KEYVALUE_ENTRY(EVENT_BUFWINENTER, "BufWinEnter"),
103 KEYVALUE_ENTRY(EVENT_BUFWINLEAVE, "BufWinLeave"),
104 KEYVALUE_ENTRY(EVENT_BUFWIPEOUT, "BufWipeout"),
105 KEYVALUE_ENTRY(EVENT_BUFWRITEPRE, "BufWrite"),
106 KEYVALUE_ENTRY(EVENT_BUFWRITECMD, "BufWriteCmd"),
107 KEYVALUE_ENTRY(EVENT_BUFWRITEPOST, "BufWritePost"),
108 KEYVALUE_ENTRY(EVENT_BUFWRITEPRE, "BufWritePre"),
109 KEYVALUE_ENTRY(EVENT_CMDLINECHANGED, "CmdlineChanged"),
110 KEYVALUE_ENTRY(EVENT_CMDLINEENTER, "CmdlineEnter"),
111 KEYVALUE_ENTRY(EVENT_CMDLINELEAVE, "CmdlineLeave"),
112 KEYVALUE_ENTRY(EVENT_CMDUNDEFINED, "CmdUndefined"),
113 KEYVALUE_ENTRY(EVENT_CMDWINENTER, "CmdwinEnter"),
114 KEYVALUE_ENTRY(EVENT_CMDWINLEAVE, "CmdwinLeave"),
115 KEYVALUE_ENTRY(EVENT_COLORSCHEME, "ColorScheme"),
116 KEYVALUE_ENTRY(EVENT_COLORSCHEMEPRE, "ColorSchemePre"),
117 KEYVALUE_ENTRY(EVENT_COMPLETECHANGED, "CompleteChanged"),
118 KEYVALUE_ENTRY(EVENT_COMPLETEDONE, "CompleteDone"),
119 KEYVALUE_ENTRY(EVENT_COMPLETEDONEPRE, "CompleteDonePre"),
120 KEYVALUE_ENTRY(EVENT_CURSORHOLD, "CursorHold"),
121 KEYVALUE_ENTRY(EVENT_CURSORHOLDI, "CursorHoldI"),
122 KEYVALUE_ENTRY(EVENT_CURSORMOVED, "CursorMoved"),
Shougo Matsushitad0952142024-06-20 22:05:16 +0200123 KEYVALUE_ENTRY(EVENT_CURSORMOVEDC, "CursorMovedC"),
John Marriott78d742a2024-04-02 20:26:01 +0200124 KEYVALUE_ENTRY(EVENT_CURSORMOVEDI, "CursorMovedI"),
125 KEYVALUE_ENTRY(EVENT_DIFFUPDATED, "DiffUpdated"),
126 KEYVALUE_ENTRY(EVENT_DIRCHANGED, "DirChanged"),
127 KEYVALUE_ENTRY(EVENT_DIRCHANGEDPRE, "DirChangedPre"),
128 KEYVALUE_ENTRY(EVENT_ENCODINGCHANGED, "EncodingChanged"),
129 KEYVALUE_ENTRY(EVENT_EXITPRE, "ExitPre"),
130 KEYVALUE_ENTRY(EVENT_FILEAPPENDCMD, "FileAppendCmd"),
131 KEYVALUE_ENTRY(EVENT_FILEAPPENDPOST, "FileAppendPost"),
132 KEYVALUE_ENTRY(EVENT_FILEAPPENDPRE, "FileAppendPre"),
133 KEYVALUE_ENTRY(EVENT_FILECHANGEDRO, "FileChangedRO"),
134 KEYVALUE_ENTRY(EVENT_FILECHANGEDSHELL, "FileChangedShell"),
135 KEYVALUE_ENTRY(EVENT_FILECHANGEDSHELLPOST, "FileChangedShellPost"),
136 KEYVALUE_ENTRY(EVENT_ENCODINGCHANGED, "FileEncoding"),
137 KEYVALUE_ENTRY(EVENT_FILEREADCMD, "FileReadCmd"),
138 KEYVALUE_ENTRY(EVENT_FILEREADPOST, "FileReadPost"),
139 KEYVALUE_ENTRY(EVENT_FILEREADPRE, "FileReadPre"),
140 KEYVALUE_ENTRY(EVENT_FILETYPE, "FileType"),
141 KEYVALUE_ENTRY(EVENT_FILEWRITECMD, "FileWriteCmd"),
142 KEYVALUE_ENTRY(EVENT_FILEWRITEPOST, "FileWritePost"),
143 KEYVALUE_ENTRY(EVENT_FILEWRITEPRE, "FileWritePre"),
144 KEYVALUE_ENTRY(EVENT_FILTERREADPOST, "FilterReadPost"),
145 KEYVALUE_ENTRY(EVENT_FILTERREADPRE, "FilterReadPre"),
146 KEYVALUE_ENTRY(EVENT_FILTERWRITEPOST, "FilterWritePost"),
147 KEYVALUE_ENTRY(EVENT_FILTERWRITEPRE, "FilterWritePre"),
148 KEYVALUE_ENTRY(EVENT_FOCUSGAINED, "FocusGained"),
149 KEYVALUE_ENTRY(EVENT_FOCUSLOST, "FocusLost"),
150 KEYVALUE_ENTRY(EVENT_FUNCUNDEFINED, "FuncUndefined"),
151 KEYVALUE_ENTRY(EVENT_GUIENTER, "GUIEnter"),
152 KEYVALUE_ENTRY(EVENT_GUIFAILED, "GUIFailed"),
153 KEYVALUE_ENTRY(EVENT_INSERTCHANGE, "InsertChange"),
154 KEYVALUE_ENTRY(EVENT_INSERTCHARPRE, "InsertCharPre"),
155 KEYVALUE_ENTRY(EVENT_INSERTENTER, "InsertEnter"),
156 KEYVALUE_ENTRY(EVENT_INSERTLEAVE, "InsertLeave"),
157 KEYVALUE_ENTRY(EVENT_INSERTLEAVEPRE, "InsertLeavePre"),
158 KEYVALUE_ENTRY(EVENT_MENUPOPUP, "MenuPopup"),
159 KEYVALUE_ENTRY(EVENT_MODECHANGED, "ModeChanged"),
160 KEYVALUE_ENTRY(EVENT_OPTIONSET, "OptionSet"),
161 KEYVALUE_ENTRY(EVENT_QUICKFIXCMDPOST, "QuickFixCmdPost"),
162 KEYVALUE_ENTRY(EVENT_QUICKFIXCMDPRE, "QuickFixCmdPre"),
163 KEYVALUE_ENTRY(EVENT_QUITPRE, "QuitPre"),
164 KEYVALUE_ENTRY(EVENT_REMOTEREPLY, "RemoteReply"),
165 KEYVALUE_ENTRY(EVENT_SAFESTATE, "SafeState"),
166 KEYVALUE_ENTRY(EVENT_SAFESTATEAGAIN, "SafeStateAgain"),
167 KEYVALUE_ENTRY(EVENT_SESSIONLOADPOST, "SessionLoadPost"),
168 KEYVALUE_ENTRY(EVENT_SESSIONWRITEPOST, "SessionWritePost"),
169 KEYVALUE_ENTRY(EVENT_SHELLCMDPOST, "ShellCmdPost"),
170 KEYVALUE_ENTRY(EVENT_SHELLFILTERPOST, "ShellFilterPost"),
171 KEYVALUE_ENTRY(EVENT_SIGUSR1, "SigUSR1"),
172 KEYVALUE_ENTRY(EVENT_SOURCECMD, "SourceCmd"),
173 KEYVALUE_ENTRY(EVENT_SOURCEPOST, "SourcePost"),
174 KEYVALUE_ENTRY(EVENT_SOURCEPRE, "SourcePre"),
175 KEYVALUE_ENTRY(EVENT_SPELLFILEMISSING, "SpellFileMissing"),
176 KEYVALUE_ENTRY(EVENT_STDINREADPOST, "StdinReadPost"),
177 KEYVALUE_ENTRY(EVENT_STDINREADPRE, "StdinReadPre"),
178 KEYVALUE_ENTRY(EVENT_SWAPEXISTS, "SwapExists"),
179 KEYVALUE_ENTRY(EVENT_SYNTAX, "Syntax"),
180 KEYVALUE_ENTRY(EVENT_TABCLOSED, "TabClosed"),
181 KEYVALUE_ENTRY(EVENT_TABENTER, "TabEnter"),
182 KEYVALUE_ENTRY(EVENT_TABLEAVE, "TabLeave"),
183 KEYVALUE_ENTRY(EVENT_TABNEW, "TabNew"),
184 KEYVALUE_ENTRY(EVENT_TERMCHANGED, "TermChanged"),
185 KEYVALUE_ENTRY(EVENT_TERMINALOPEN, "TerminalOpen"),
186 KEYVALUE_ENTRY(EVENT_TERMINALWINOPEN, "TerminalWinOpen"),
187 KEYVALUE_ENTRY(EVENT_TERMRESPONSE, "TermResponse"),
188 KEYVALUE_ENTRY(EVENT_TERMRESPONSEALL, "TermResponseAll"),
189 KEYVALUE_ENTRY(EVENT_TEXTCHANGED, "TextChanged"),
190 KEYVALUE_ENTRY(EVENT_TEXTCHANGEDI, "TextChangedI"),
191 KEYVALUE_ENTRY(EVENT_TEXTCHANGEDP, "TextChangedP"),
192 KEYVALUE_ENTRY(EVENT_TEXTCHANGEDT, "TextChangedT"),
193 KEYVALUE_ENTRY(EVENT_TEXTYANKPOST, "TextYankPost"),
194 KEYVALUE_ENTRY(EVENT_USER, "User"),
195 KEYVALUE_ENTRY(EVENT_VIMENTER, "VimEnter"),
196 KEYVALUE_ENTRY(EVENT_VIMLEAVE, "VimLeave"),
197 KEYVALUE_ENTRY(EVENT_VIMLEAVEPRE, "VimLeavePre"),
198 KEYVALUE_ENTRY(EVENT_VIMRESIZED, "VimResized"),
199 KEYVALUE_ENTRY(EVENT_VIMRESUME, "VimResume"),
200 KEYVALUE_ENTRY(EVENT_VIMSUSPEND, "VimSuspend"),
201 KEYVALUE_ENTRY(EVENT_WINCLOSED, "WinClosed"),
202 KEYVALUE_ENTRY(EVENT_WINENTER, "WinEnter"),
203 KEYVALUE_ENTRY(EVENT_WINLEAVE, "WinLeave"),
204 KEYVALUE_ENTRY(EVENT_WINNEW, "WinNew"),
205 KEYVALUE_ENTRY(EVENT_WINNEWPRE, "WinNewPre"),
206 KEYVALUE_ENTRY(EVENT_WINRESIZED, "WinResized"),
207 KEYVALUE_ENTRY(EVENT_WINSCROLLED, "WinScrolled")
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100208};
209
John Marriott78d742a2024-04-02 20:26:01 +0200210static AutoPat *first_autopat[NUM_EVENTS] = {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100211 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
212 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
213 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
214 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
215 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
John Marriott78d742a2024-04-02 20:26:01 +0200216 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
217 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
218 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
219 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
220 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
221 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
222 NULL, NULL, NULL, NULL, NULL, NULL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100223};
224
John Marriott78d742a2024-04-02 20:26:01 +0200225static AutoPat *last_autopat[NUM_EVENTS] = {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100226 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
227 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
228 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
229 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
230 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
John Marriott78d742a2024-04-02 20:26:01 +0200231 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
232 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
233 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
234 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
235 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
236 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
237 NULL, NULL, NULL, NULL, NULL, NULL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100238};
239
kylo252ae6f1d82022-02-16 19:24:07 +0000240#define AUGROUP_DEFAULT (-1) // default autocmd group
241#define AUGROUP_ERROR (-2) // erroneous autocmd group
242#define AUGROUP_ALL (-3) // all autocmd groups
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100243
244/*
245 * struct used to keep status while executing autocommands for an event.
246 */
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100247struct AutoPatCmd_S
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100248{
249 AutoPat *curpat; // next AutoPat to examine
250 AutoCmd *nextcmd; // next AutoCmd to execute
251 int group; // group being used
252 char_u *fname; // fname to match with
253 char_u *sfname; // sfname to match with
254 char_u *tail; // tail of fname
255 event_T event; // current event
LemonBoyeca7c602022-04-14 15:39:43 +0100256 sctx_T script_ctx; // script context where it is defined
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100257 int arg_bufnr; // Initially equal to <abuf>, set to zero when
258 // buf is deleted.
LemonBoyeca7c602022-04-14 15:39:43 +0100259 AutoPatCmd_T *next; // chain of active apc-s for auto-invalidation
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100260};
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100261
LemonBoyeca7c602022-04-14 15:39:43 +0100262static AutoPatCmd_T *active_apc_list = NULL; // stack of active autocommands
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100263
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200264// Macro to loop over all the patterns for an autocmd event
265#define FOR_ALL_AUTOCMD_PATTERNS(event, ap) \
266 for ((ap) = first_autopat[(int)(event)]; (ap) != NULL; (ap) = (ap)->next)
267
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100268/*
269 * augroups stores a list of autocmd group names.
270 */
271static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
272#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
Bram Moolenaarc667da52019-11-30 20:52:27 +0100273// use get_deleted_augroup() to get this
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100274static char_u *deleted_augroup = NULL;
275
276/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100277 * The ID of the current group. Group 0 is the default one.
278 */
279static int current_augroup = AUGROUP_DEFAULT;
280
Bram Moolenaarc667da52019-11-30 20:52:27 +0100281static int au_need_clean = FALSE; // need to delete marked patterns
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100282
John Marriott78d742a2024-04-02 20:26:01 +0200283static event_T event_name2nr(char_u *start, char_u **end);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100284static char_u *event_nr2name(event_T event);
285static int au_get_grouparg(char_u **argp);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200286static 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 +0100287static 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 +0100288static void auto_next_pat(AutoPatCmd_T *apc, int stop_at_last);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100289static int au_find_group(char_u *name);
290
291static event_T last_event;
292static int last_group;
Bram Moolenaarc667da52019-11-30 20:52:27 +0100293static int autocmd_blocked = 0; // block all autocmds
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100294
295 static char_u *
296get_deleted_augroup(void)
297{
298 if (deleted_augroup == NULL)
299 deleted_augroup = (char_u *)_("--Deleted--");
300 return deleted_augroup;
301}
302
303/*
304 * Show the autocommands for one AutoPat.
305 */
306 static void
307show_autocmd(AutoPat *ap, event_T event)
308{
309 AutoCmd *ac;
310
311 // Check for "got_int" (here and at various places below), which is set
312 // when "q" has been hit for the "--more--" prompt
313 if (got_int)
314 return;
315 if (ap->pat == NULL) // pattern has been removed
316 return;
317
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000318 // Make sure no info referenced by "ap" is cleared, e.g. when a timer
319 // clears an augroup. Jump to "theend" after this!
320 // "ap->pat" may be cleared anyway.
321 ++autocmd_busy;
322
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100323 msg_putchar('\n');
324 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000325 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100326 if (event != last_event || ap->group != last_group)
327 {
328 if (ap->group != AUGROUP_DEFAULT)
329 {
330 if (AUGROUP_NAME(ap->group) == NULL)
331 msg_puts_attr((char *)get_deleted_augroup(), HL_ATTR(HLF_E));
332 else
333 msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
334 msg_puts(" ");
335 }
336 msg_puts_attr((char *)event_nr2name(event), HL_ATTR(HLF_T));
337 last_event = event;
338 last_group = ap->group;
339 msg_putchar('\n');
340 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000341 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100342 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000343
344 if (ap->pat == NULL)
345 goto theend; // timer might have cleared the pattern or group
346
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100347 msg_col = 4;
348 msg_outtrans(ap->pat);
349
350 for (ac = ap->cmds; ac != NULL; ac = ac->next)
351 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100352 if (ac->cmd == NULL) // skip removed commands
353 continue;
354
355 if (msg_col >= 14)
356 msg_putchar('\n');
357 msg_col = 14;
358 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000359 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100360 msg_outtrans(ac->cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100361#ifdef FEAT_EVAL
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100362 if (p_verbose > 0)
363 last_set_msg(ac->script_ctx);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100364#endif
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100365 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000366 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100367 if (ac->next != NULL)
368 {
369 msg_putchar('\n');
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100370 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000371 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100372 }
373 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000374
375theend:
376 --autocmd_busy;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100377}
378
379/*
380 * Mark an autocommand pattern for deletion.
381 */
382 static void
383au_remove_pat(AutoPat *ap)
384{
385 VIM_CLEAR(ap->pat);
386 ap->buflocal_nr = -1;
387 au_need_clean = TRUE;
388}
389
390/*
391 * Mark all commands for a pattern for deletion.
392 */
393 static void
394au_remove_cmds(AutoPat *ap)
395{
396 AutoCmd *ac;
397
398 for (ac = ap->cmds; ac != NULL; ac = ac->next)
399 VIM_CLEAR(ac->cmd);
400 au_need_clean = TRUE;
401}
402
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200403// Delete one command from an autocmd pattern.
404static void au_del_cmd(AutoCmd *ac)
405{
406 VIM_CLEAR(ac->cmd);
407 au_need_clean = TRUE;
408}
409
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100410/*
411 * Cleanup autocommands and patterns that have been deleted.
412 * This is only done when not executing autocommands.
413 */
414 static void
415au_cleanup(void)
416{
417 AutoPat *ap, **prev_ap;
418 AutoCmd *ac, **prev_ac;
419 event_T event;
420
421 if (autocmd_busy || !au_need_clean)
422 return;
423
424 // loop over all events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100425 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100426 event = (event_T)((int)event + 1))
427 {
428 // loop over all autocommand patterns
429 prev_ap = &(first_autopat[(int)event]);
430 for (ap = *prev_ap; ap != NULL; ap = *prev_ap)
431 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200432 int has_cmd = FALSE;
433
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200434 // loop over all commands for this pattern
435 prev_ac = &(ap->cmds);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100436 for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
437 {
438 // remove the command if the pattern is to be deleted or when
439 // the command has been marked for deletion
440 if (ap->pat == NULL || ac->cmd == NULL)
441 {
442 *prev_ac = ac->next;
443 vim_free(ac->cmd);
444 vim_free(ac);
445 }
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200446 else
447 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200448 has_cmd = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100449 prev_ac = &(ac->next);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200450 }
451 }
452
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200453 if (ap->pat != NULL && !has_cmd)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200454 // Pattern was not marked for deletion, but all of its
455 // commands were. So mark the pattern for deletion.
456 au_remove_pat(ap);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100457
458 // remove the pattern if it has been marked for deletion
459 if (ap->pat == NULL)
460 {
461 if (ap->next == NULL)
462 {
463 if (prev_ap == &(first_autopat[(int)event]))
464 last_autopat[(int)event] = NULL;
465 else
466 // this depends on the "next" field being the first in
467 // the struct
468 last_autopat[(int)event] = (AutoPat *)prev_ap;
469 }
470 *prev_ap = ap->next;
471 vim_regfree(ap->reg_prog);
472 vim_free(ap);
473 }
474 else
475 prev_ap = &(ap->next);
476 }
477 }
478
479 au_need_clean = FALSE;
480}
481
482/*
483 * Called when buffer is freed, to remove/invalidate related buffer-local
484 * autocmds.
485 */
486 void
487aubuflocal_remove(buf_T *buf)
488{
LemonBoyeca7c602022-04-14 15:39:43 +0100489 AutoPat *ap;
490 event_T event;
491 AutoPatCmd_T *apc;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100492
493 // invalidate currently executing autocommands
494 for (apc = active_apc_list; apc; apc = apc->next)
495 if (buf->b_fnum == apc->arg_bufnr)
496 apc->arg_bufnr = 0;
497
498 // invalidate buflocals looping through events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100499 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100500 event = (event_T)((int)event + 1))
501 // loop over all autocommand patterns
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200502 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100503 if (ap->buflocal_nr == buf->b_fnum)
504 {
505 au_remove_pat(ap);
506 if (p_verbose >= 6)
507 {
508 verbose_enter();
509 smsg(_("auto-removing autocommand: %s <buffer=%d>"),
510 event_nr2name(event), buf->b_fnum);
511 verbose_leave();
512 }
513 }
514 au_cleanup();
515}
516
517/*
518 * Add an autocmd group name.
519 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
520 */
521 static int
522au_new_group(char_u *name)
523{
524 int i;
525
526 i = au_find_group(name);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100527 if (i != AUGROUP_ERROR)
528 return i;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100529
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100530 // the group doesn't exist yet, add it. First try using a free entry.
531 for (i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100532 if (AUGROUP_NAME(i) == NULL)
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100533 break;
534 if (i == augroups.ga_len && ga_grow(&augroups, 1) == FAIL)
535 return AUGROUP_ERROR;
536
537 AUGROUP_NAME(i) = vim_strsave(name);
538 if (AUGROUP_NAME(i) == NULL)
539 return AUGROUP_ERROR;
540 if (i == augroups.ga_len)
541 ++augroups.ga_len;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100542
543 return i;
544}
545
546 static void
547au_del_group(char_u *name)
548{
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100549 int i;
550 event_T event;
551 AutoPat *ap;
552 int in_use = FALSE;
553
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100554
555 i = au_find_group(name);
556 if (i == AUGROUP_ERROR) // the group doesn't exist
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100557 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100558 semsg(_(e_no_such_group_str), name);
559 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100560 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100561 if (i == current_augroup)
562 {
563 emsg(_(e_cannot_delete_current_group));
564 return;
565 }
566
567 for (event = (event_T)0; (int)event < NUM_EVENTS;
568 event = (event_T)((int)event + 1))
569 {
570 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
571 if (ap->group == i && ap->pat != NULL)
572 {
573 give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
574 in_use = TRUE;
575 event = NUM_EVENTS;
576 break;
577 }
578 }
579 vim_free(AUGROUP_NAME(i));
580 if (in_use)
581 AUGROUP_NAME(i) = get_deleted_augroup();
582 else
583 AUGROUP_NAME(i) = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100584}
585
586/*
587 * Find the ID of an autocmd group name.
588 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
589 */
590 static int
591au_find_group(char_u *name)
592{
593 int i;
594
595 for (i = 0; i < augroups.ga_len; ++i)
596 if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
597 && STRCMP(AUGROUP_NAME(i), name) == 0)
598 return i;
599 return AUGROUP_ERROR;
600}
601
602/*
603 * Return TRUE if augroup "name" exists.
604 */
605 int
606au_has_group(char_u *name)
607{
608 return au_find_group(name) != AUGROUP_ERROR;
609}
610
611/*
612 * ":augroup {name}".
613 */
614 void
615do_augroup(char_u *arg, int del_group)
616{
617 int i;
618
619 if (del_group)
620 {
621 if (*arg == NUL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000622 emsg(_(e_argument_required));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100623 else
624 au_del_group(arg);
625 }
626 else if (STRICMP(arg, "end") == 0) // ":aug end": back to group 0
627 current_augroup = AUGROUP_DEFAULT;
628 else if (*arg) // ":aug xxx": switch to group xxx
629 {
630 i = au_new_group(arg);
631 if (i != AUGROUP_ERROR)
632 current_augroup = i;
633 }
634 else // ":aug": list the group names
635 {
636 msg_start();
637 for (i = 0; i < augroups.ga_len; ++i)
638 {
639 if (AUGROUP_NAME(i) != NULL)
640 {
641 msg_puts((char *)AUGROUP_NAME(i));
642 msg_puts(" ");
643 }
644 }
645 msg_clr_eos();
646 msg_end();
647 }
648}
649
Bram Moolenaare76062c2022-11-28 18:51:43 +0000650 void
651autocmd_init(void)
652{
653 CLEAR_FIELD(aucmd_win);
654}
655
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100656#if defined(EXITFREE) || defined(PROTO)
657 void
658free_all_autocmds(void)
659{
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100660 char_u *s;
661
662 for (current_augroup = -1; current_augroup < augroups.ga_len;
663 ++current_augroup)
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200664 do_autocmd(NULL, (char_u *)"", TRUE);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100665
Bram Moolenaare76062c2022-11-28 18:51:43 +0000666 for (int i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100667 {
668 s = ((char_u **)(augroups.ga_data))[i];
669 if (s != get_deleted_augroup())
670 vim_free(s);
671 }
672 ga_clear(&augroups);
Bram Moolenaare76062c2022-11-28 18:51:43 +0000673
Bram Moolenaar84497cd2022-11-28 20:34:52 +0000674 // aucmd_win[] is freed in win_free_all()
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100675}
676#endif
677
678/*
Bram Moolenaare76062c2022-11-28 18:51:43 +0000679 * Return TRUE if "win" is an active entry in aucmd_win[].
680 */
681 int
682is_aucmd_win(win_T *win)
683{
684 for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
685 if (aucmd_win[i].auc_win_used && aucmd_win[i].auc_win == win)
686 return TRUE;
687 return FALSE;
688}
689
690/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100691 * Return the event number for event name "start".
692 * Return NUM_EVENTS if the event name was not found.
693 * Return a pointer to the next event name in "end".
694 */
695 static event_T
696event_name2nr(char_u *start, char_u **end)
697{
698 char_u *p;
John Marriott78d742a2024-04-02 20:26:01 +0200699 keyvalue_T target;
700 keyvalue_T *entry;
701 static keyvalue_T *bufnewfile = &event_tab[BUFNEWFILE_INDEX];
702 static keyvalue_T *bufread = &event_tab[BUFREAD_INDEX];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100703
704 // the event name ends with end of line, '|', a blank or a comma
705 for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
706 ;
John Marriott78d742a2024-04-02 20:26:01 +0200707
708 target.key = 0;
709 target.value = (char *)start;
710 target.length = (size_t)(p - start);
711
712 // special cases:
713 // BufNewFile and BufRead are searched for ALOT (especially at startup)
714 // so we check for them first.
715 if (cmp_keyvalue_value_ni(&target, bufnewfile) == 0)
716 entry = bufnewfile;
717 else
718 if (cmp_keyvalue_value_ni(&target, bufread) == 0)
719 entry = bufread;
720 else
721 entry = (keyvalue_T *)bsearch(&target, &event_tab, ARRAY_LENGTH(event_tab), sizeof(event_tab[0]), cmp_keyvalue_value_ni);
722
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100723 if (*p == ',')
724 ++p;
725 *end = p;
John Marriott78d742a2024-04-02 20:26:01 +0200726
727 return (entry == NULL) ? NUM_EVENTS : (event_T)entry->key;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100728}
729
730/*
731 * Return the name for event "event".
732 */
733 static char_u *
734event_nr2name(event_T event)
735{
736 int i;
John Marriott78d742a2024-04-02 20:26:01 +0200737#define CACHE_SIZE 12
738 static int cache_tab[CACHE_SIZE];
739 static int cache_last_index = -1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100740
John Marriott78d742a2024-04-02 20:26:01 +0200741 if (cache_last_index < 0)
742 {
743 for (i = 0; i < (int)ARRAY_LENGTH(cache_tab); ++i)
744 cache_tab[i] = -1;
745 cache_last_index = ARRAY_LENGTH(cache_tab) - 1;
746 }
747
748 // first look in the cache
749 // the cache is circular. to search it we start at the most recent entry
750 // and go backwards wrapping around when we get to index 0.
751 for (i = cache_last_index; cache_tab[i] >= 0; )
752 {
753 if ((event_T)event_tab[cache_tab[i]].key == event)
754 return (char_u *)event_tab[cache_tab[i]].value;
755
756 if (i == 0)
757 i = ARRAY_LENGTH(cache_tab) - 1;
758 else
759 --i;
760
761 // are we back at the start?
762 if (i == cache_last_index)
763 break;
764 }
765
766 // look in the event table itself
767 for (i = 0; i < (int)ARRAY_LENGTH(event_tab); ++i)
768 {
769 if ((event_T)event_tab[i].key == event)
770 {
771 // store the found entry in the next position in the cache,
772 // wrapping around when we get to the maximum index.
773 if (cache_last_index == ARRAY_LENGTH(cache_tab) - 1)
774 cache_last_index = 0;
775 else
776 ++cache_last_index;
777 cache_tab[cache_last_index] = i;
778 break;
779 }
780 }
781
782 return (i == (int)ARRAY_LENGTH(event_tab)) ? (char_u *)"Unknown" : (char_u *)event_tab[i].value;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100783}
784
785/*
786 * Scan over the events. "*" stands for all events.
787 */
788 static char_u *
789find_end_event(
790 char_u *arg,
791 int have_group) // TRUE when group name was found
792{
793 char_u *pat;
794 char_u *p;
795
796 if (*arg == '*')
797 {
798 if (arg[1] && !VIM_ISWHITE(arg[1]))
799 {
Bram Moolenaar6d057012021-12-31 18:49:43 +0000800 semsg(_(e_illegal_character_after_star_str), arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100801 return NULL;
802 }
803 pat = arg + 1;
804 }
805 else
806 {
807 for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p)
808 {
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100809 if ((int)event_name2nr(pat, &p) >= NUM_EVENTS)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100810 {
811 if (have_group)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000812 semsg(_(e_no_such_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100813 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000814 semsg(_(e_no_such_group_or_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100815 return NULL;
816 }
817 }
818 }
819 return pat;
820}
821
822/*
823 * Return TRUE if "event" is included in 'eventignore'.
824 */
825 static int
826event_ignored(event_T event)
827{
828 char_u *p = p_ei;
829
830 while (*p != NUL)
831 {
832 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
833 return TRUE;
834 if (event_name2nr(p, &p) == event)
835 return TRUE;
836 }
837
838 return FALSE;
839}
840
841/*
842 * Return OK when the contents of p_ei is valid, FAIL otherwise.
843 */
844 int
845check_ei(void)
846{
847 char_u *p = p_ei;
848
849 while (*p)
850 {
851 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
852 {
853 p += 3;
854 if (*p == ',')
855 ++p;
856 }
857 else if (event_name2nr(p, &p) == NUM_EVENTS)
858 return FAIL;
859 }
860
861 return OK;
862}
863
864# if defined(FEAT_SYN_HL) || defined(PROTO)
865
866/*
867 * Add "what" to 'eventignore' to skip loading syntax highlighting for every
868 * buffer loaded into the window. "what" must start with a comma.
869 * Returns the old value of 'eventignore' in allocated memory.
870 */
871 char_u *
872au_event_disable(char *what)
873{
874 char_u *new_ei;
875 char_u *save_ei;
John Marriott78d742a2024-04-02 20:26:01 +0200876 size_t p_ei_len;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100877
John Marriott78d742a2024-04-02 20:26:01 +0200878 p_ei_len = STRLEN(p_ei);
879 save_ei = vim_strnsave(p_ei, p_ei_len);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100880 if (save_ei == NULL)
881 return NULL;
882
John Marriott78d742a2024-04-02 20:26:01 +0200883 new_ei = vim_strnsave(p_ei, p_ei_len + STRLEN(what));
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100884 if (new_ei == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100885 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100886 vim_free(save_ei);
887 return NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100888 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100889
890 if (*what == ',' && *p_ei == NUL)
891 STRCPY(new_ei, what + 1);
892 else
893 STRCAT(new_ei, what);
894 set_string_option_direct((char_u *)"ei", -1, new_ei,
895 OPT_FREE, SID_NONE);
896 vim_free(new_ei);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100897 return save_ei;
898}
899
900 void
901au_event_restore(char_u *old_ei)
902{
903 if (old_ei != NULL)
904 {
905 set_string_option_direct((char_u *)"ei", -1, old_ei,
906 OPT_FREE, SID_NONE);
907 vim_free(old_ei);
908 }
909}
Bram Moolenaarc667da52019-11-30 20:52:27 +0100910# endif // FEAT_SYN_HL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100911
912/*
913 * do_autocmd() -- implements the :autocmd command. Can be used in the
914 * following ways:
915 *
916 * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
917 * will be automatically executed for <event>
918 * when editing a file matching <pat>, in
919 * the current group.
920 * :autocmd <event> <pat> Show the autocommands associated with
921 * <event> and <pat>.
922 * :autocmd <event> Show the autocommands associated with
923 * <event>.
924 * :autocmd Show all autocommands.
925 * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
926 * <event> and <pat>, and add the command
927 * <cmd>, for the current group.
928 * :autocmd! <event> <pat> Remove all autocommands associated with
929 * <event> and <pat> for the current group.
930 * :autocmd! <event> Remove all autocommands associated with
931 * <event> for the current group.
932 * :autocmd! Remove ALL autocommands for the current
933 * group.
934 *
935 * Multiple events and patterns may be given separated by commas. Here are
936 * some examples:
937 * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
938 * :autocmd bufleave * set tw=79 nosmartindent ic infercase
939 *
940 * :autocmd * *.c show all autocommands for *.c files.
941 *
942 * Mostly a {group} argument can optionally appear before <event>.
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200943 * "eap" can be NULL.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100944 */
945 void
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200946do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100947{
948 char_u *arg = arg_in;
949 char_u *pat;
950 char_u *envpat = NULL;
951 char_u *cmd;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200952 int cmd_need_free = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100953 event_T event;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200954 char_u *tofree = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100955 int nested = FALSE;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200956 int once = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100957 int group;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200958 int i;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200959 int flags = 0;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100960
961 if (*arg == '|')
962 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000963 eap->nextcmd = arg + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100964 arg = (char_u *)"";
965 group = AUGROUP_ALL; // no argument, use all groups
966 }
967 else
968 {
969 /*
970 * Check for a legal group name. If not, use AUGROUP_ALL.
971 */
972 group = au_get_grouparg(&arg);
973 if (arg == NULL) // out of memory
974 return;
975 }
976
977 /*
978 * Scan over the events.
979 * If we find an illegal name, return here, don't do anything.
980 */
981 pat = find_end_event(arg, group != AUGROUP_ALL);
982 if (pat == NULL)
983 return;
984
985 pat = skipwhite(pat);
986 if (*pat == '|')
987 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000988 eap->nextcmd = pat + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100989 pat = (char_u *)"";
990 cmd = (char_u *)"";
991 }
992 else
993 {
994 /*
995 * Scan over the pattern. Put a NUL at the end.
996 */
997 cmd = pat;
998 while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
999 cmd++;
1000 if (*cmd)
1001 *cmd++ = NUL;
1002
1003 // Expand environment variables in the pattern. Set 'shellslash', we
1004 // want forward slashes here.
1005 if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
1006 {
1007#ifdef BACKSLASH_IN_FILENAME
1008 int p_ssl_save = p_ssl;
1009
1010 p_ssl = TRUE;
1011#endif
1012 envpat = expand_env_save(pat);
1013#ifdef BACKSLASH_IN_FILENAME
1014 p_ssl = p_ssl_save;
1015#endif
1016 if (envpat != NULL)
1017 pat = envpat;
1018 }
1019
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001020 cmd = skipwhite(cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001021 for (i = 0; i < 2; i++)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001022 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001023 if (*cmd == NUL)
1024 continue;
1025
1026 // Check for "++once" flag.
1027 if (STRNCMP(cmd, "++once", 6) == 0 && VIM_ISWHITE(cmd[6]))
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001028 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001029 if (once)
1030 semsg(_(e_duplicate_argument_str), "++once");
1031 once = TRUE;
1032 cmd = skipwhite(cmd + 6);
1033 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001034
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001035 // Check for "++nested" flag.
1036 if ((STRNCMP(cmd, "++nested", 8) == 0 && VIM_ISWHITE(cmd[8])))
1037 {
1038 if (nested)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001039 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001040 semsg(_(e_duplicate_argument_str), "++nested");
1041 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001042 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001043 nested = TRUE;
1044 cmd = skipwhite(cmd + 8);
1045 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001046
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001047 // Check for the old "nested" flag in legacy script.
1048 if (STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
1049 {
1050 if (in_vim9script())
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001051 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001052 // If there ever is a :nested command this error should
1053 // be removed and "nested" accepted as the start of the
1054 // command.
1055 emsg(_(e_invalid_command_nested_did_you_mean_plusplus_nested));
1056 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001057 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001058 if (nested)
1059 {
1060 semsg(_(e_duplicate_argument_str), "nested");
1061 return;
1062 }
1063 nested = TRUE;
1064 cmd = skipwhite(cmd + 6);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001065 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001066 }
1067
1068 /*
1069 * Find the start of the commands.
1070 * Expand <sfile> in it.
1071 */
1072 if (*cmd != NUL)
1073 {
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001074 if (eap != NULL)
1075 // Read a {} block if it follows.
1076 cmd = may_get_cmd_block(eap, cmd, &tofree, &flags);
1077
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001078 cmd = expand_sfile(cmd);
1079 if (cmd == NULL) // some error
1080 return;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001081 cmd_need_free = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001082 }
1083 }
1084
1085 /*
1086 * Print header when showing autocommands.
1087 */
1088 if (!forceit && *cmd == NUL)
1089 // Highlight title
1090 msg_puts_title(_("\n--- Autocommands ---"));
1091
1092 /*
1093 * Loop over the events.
1094 */
1095 last_event = (event_T)-1; // for listing the event name
1096 last_group = AUGROUP_ERROR; // for listing the group name
1097 if (*arg == '*' || *arg == NUL || *arg == '|')
1098 {
Bram Moolenaarb6db1462021-12-24 19:24:47 +00001099 if (*cmd != NUL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001100 emsg(_(e_cannot_define_autocommands_for_all_events));
1101 else
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +01001102 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001103 event = (event_T)((int)event + 1))
1104 if (do_autocmd_event(event, pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001105 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001106 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001107 }
1108 else
1109 {
1110 while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
1111 if (do_autocmd_event(event_name2nr(arg, &arg), pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001112 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001113 break;
1114 }
1115
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001116 if (cmd_need_free)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001117 vim_free(cmd);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001118 vim_free(tofree);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001119 vim_free(envpat);
1120}
1121
1122/*
1123 * Find the group ID in a ":autocmd" or ":doautocmd" argument.
1124 * The "argp" argument is advanced to the following argument.
1125 *
1126 * Returns the group ID, AUGROUP_ERROR for error (out of memory).
1127 */
1128 static int
1129au_get_grouparg(char_u **argp)
1130{
1131 char_u *group_name;
1132 char_u *p;
1133 char_u *arg = *argp;
1134 int group = AUGROUP_ALL;
1135
1136 for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p)
1137 ;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001138 if (p <= arg)
1139 return AUGROUP_ALL;
1140
1141 group_name = vim_strnsave(arg, p - arg);
1142 if (group_name == NULL) // out of memory
1143 return AUGROUP_ERROR;
1144 group = au_find_group(group_name);
1145 if (group == AUGROUP_ERROR)
1146 group = AUGROUP_ALL; // no match, use all groups
1147 else
1148 *argp = skipwhite(p); // match, skip over group name
1149 vim_free(group_name);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001150 return group;
1151}
1152
1153/*
1154 * do_autocmd() for one event.
1155 * If *pat == NUL do for all patterns.
1156 * If *cmd == NUL show entries.
1157 * If forceit == TRUE delete entries.
1158 * If group is not AUGROUP_ALL, only use this group.
1159 */
1160 static int
1161do_autocmd_event(
1162 event_T event,
1163 char_u *pat,
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001164 int once,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001165 int nested,
1166 char_u *cmd,
1167 int forceit,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001168 int group,
1169 int flags)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001170{
1171 AutoPat *ap;
1172 AutoPat **prev_ap;
1173 AutoCmd *ac;
1174 AutoCmd **prev_ac;
1175 int brace_level;
1176 char_u *endpat;
1177 int findgroup;
1178 int allgroups;
1179 int patlen;
1180 int is_buflocal;
1181 int buflocal_nr;
Bram Moolenaarc667da52019-11-30 20:52:27 +01001182 char_u buflocal_pat[25]; // for "<buffer=X>"
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001183
1184 if (group == AUGROUP_ALL)
1185 findgroup = current_augroup;
1186 else
1187 findgroup = group;
1188 allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
1189
1190 /*
1191 * Show or delete all patterns for an event.
1192 */
1193 if (*pat == NUL)
1194 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001195 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001196 {
1197 if (forceit) // delete the AutoPat, if it's in the current group
1198 {
1199 if (ap->group == findgroup)
1200 au_remove_pat(ap);
1201 }
1202 else if (group == AUGROUP_ALL || ap->group == group)
1203 show_autocmd(ap, event);
1204 }
1205 }
1206
1207 /*
1208 * Loop through all the specified patterns.
1209 */
1210 for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
1211 {
1212 /*
1213 * Find end of the pattern.
1214 * Watch out for a comma in braces, like "*.\{obj,o\}".
1215 */
1216 brace_level = 0;
1217 for (endpat = pat; *endpat && (*endpat != ',' || brace_level
1218 || (endpat > pat && endpat[-1] == '\\')); ++endpat)
1219 {
1220 if (*endpat == '{')
1221 brace_level++;
1222 else if (*endpat == '}')
1223 brace_level--;
1224 }
1225 if (pat == endpat) // ignore single comma
1226 continue;
1227 patlen = (int)(endpat - pat);
1228
1229 /*
1230 * detect special <buflocal[=X]> buffer-local patterns
1231 */
1232 is_buflocal = FALSE;
1233 buflocal_nr = 0;
1234
1235 if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
1236 && pat[patlen - 1] == '>')
1237 {
1238 // "<buffer...>": Error will be printed only for addition.
1239 // printing and removing will proceed silently.
1240 is_buflocal = TRUE;
1241 if (patlen == 8)
1242 // "<buffer>"
1243 buflocal_nr = curbuf->b_fnum;
1244 else if (patlen > 9 && pat[7] == '=')
1245 {
1246 if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0)
1247 // "<buffer=abuf>"
1248 buflocal_nr = autocmd_bufnr;
1249 else if (skipdigits(pat + 8) == pat + patlen - 1)
1250 // "<buffer=123>"
1251 buflocal_nr = atoi((char *)pat + 8);
1252 }
1253 }
1254
1255 if (is_buflocal)
1256 {
1257 // normalize pat into standard "<buffer>#N" form
1258 sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
1259 pat = buflocal_pat; // can modify pat and patlen
1260 patlen = (int)STRLEN(buflocal_pat); // but not endpat
1261 }
1262
1263 /*
1264 * Find AutoPat entries with this pattern. When adding a command it
1265 * always goes at or after the last one, so start at the end.
1266 */
1267 if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL)
1268 prev_ap = &last_autopat[(int)event];
1269 else
1270 prev_ap = &first_autopat[(int)event];
1271 while ((ap = *prev_ap) != NULL)
1272 {
1273 if (ap->pat != NULL)
1274 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001275 /*
1276 * Accept a pattern when:
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001277 * - a group was specified and it's that group, or a group was
1278 * not specified and it's the current group, or a group was
1279 * not specified and we are listing
1280 * - the length of the pattern matches
1281 * - the pattern matches.
1282 * For <buffer[=X]>, this condition works because we normalize
1283 * all buffer-local patterns.
1284 */
1285 if ((allgroups || ap->group == findgroup)
1286 && ap->patlen == patlen
1287 && STRNCMP(pat, ap->pat, patlen) == 0)
1288 {
1289 /*
1290 * Remove existing autocommands.
1291 * If adding any new autocmd's for this AutoPat, don't
1292 * delete the pattern from the autopat list, append to
1293 * this list.
1294 */
1295 if (forceit)
1296 {
1297 if (*cmd != NUL && ap->next == NULL)
1298 {
1299 au_remove_cmds(ap);
1300 break;
1301 }
1302 au_remove_pat(ap);
1303 }
1304
1305 /*
1306 * Show autocmd's for this autopat, or buflocals <buffer=X>
1307 */
1308 else if (*cmd == NUL)
1309 show_autocmd(ap, event);
1310
1311 /*
1312 * Add autocmd to this autopat, if it's the last one.
1313 */
1314 else if (ap->next == NULL)
1315 break;
1316 }
1317 }
1318 prev_ap = &ap->next;
1319 }
1320
1321 /*
1322 * Add a new command.
1323 */
1324 if (*cmd != NUL)
1325 {
1326 /*
1327 * If the pattern we want to add a command to does appear at the
1328 * end of the list (or not is not in the list at all), add the
1329 * pattern at the end of the list.
1330 */
1331 if (ap == NULL)
1332 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001333 // refuse to add buffer-local ap if buffer number is invalid
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001334 if (is_buflocal && (buflocal_nr == 0
1335 || buflist_findnr(buflocal_nr) == NULL))
1336 {
Bram Moolenaarf1474d82021-12-31 19:59:55 +00001337 semsg(_(e_buffer_nr_invalid_buffer_number), buflocal_nr);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001338 return FAIL;
1339 }
1340
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001341 ap = ALLOC_ONE(AutoPat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001342 if (ap == NULL)
1343 return FAIL;
1344 ap->pat = vim_strnsave(pat, patlen);
1345 ap->patlen = patlen;
1346 if (ap->pat == NULL)
1347 {
1348 vim_free(ap);
1349 return FAIL;
1350 }
1351
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001352#ifdef FEAT_EVAL
1353 // need to initialize last_mode for the first ModeChanged
1354 // autocmd
1355 if (event == EVENT_MODECHANGED && !has_modechanged())
LemonBoy2bf52dd2022-04-09 18:17:34 +01001356 get_mode(last_mode);
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001357#endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001358 // Initialize the fields checked by the WinScrolled and
1359 // WinResized trigger to prevent them from firing right after
1360 // the first autocmd is defined.
1361 if ((event == EVENT_WINSCROLLED || event == EVENT_WINRESIZED)
1362 && !(has_winscrolled() || has_winresized()))
LemonBoy09371822022-04-08 15:18:45 +01001363 {
Bram Moolenaar29967732022-11-20 12:11:45 +00001364 tabpage_T *save_curtab = curtab;
1365 tabpage_T *tp;
1366 FOR_ALL_TABPAGES(tp)
1367 {
1368 unuse_tabpage(curtab);
1369 use_tabpage(tp);
1370 snapshot_windows_scroll_size();
1371 }
1372 unuse_tabpage(curtab);
1373 use_tabpage(save_curtab);
LemonBoy09371822022-04-08 15:18:45 +01001374 }
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001375
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001376 if (is_buflocal)
1377 {
1378 ap->buflocal_nr = buflocal_nr;
1379 ap->reg_prog = NULL;
1380 }
1381 else
1382 {
1383 char_u *reg_pat;
1384
1385 ap->buflocal_nr = 0;
1386 reg_pat = file_pat_to_reg_pat(pat, endpat,
1387 &ap->allow_dirs, TRUE);
1388 if (reg_pat != NULL)
1389 ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
1390 vim_free(reg_pat);
1391 if (reg_pat == NULL || ap->reg_prog == NULL)
1392 {
1393 vim_free(ap->pat);
1394 vim_free(ap);
1395 return FAIL;
1396 }
1397 }
1398 ap->cmds = NULL;
1399 *prev_ap = ap;
1400 last_autopat[(int)event] = ap;
1401 ap->next = NULL;
1402 if (group == AUGROUP_ALL)
1403 ap->group = current_augroup;
1404 else
1405 ap->group = group;
1406 }
1407
1408 /*
1409 * Add the autocmd at the end of the AutoCmd list.
1410 */
1411 prev_ac = &(ap->cmds);
1412 while ((ac = *prev_ac) != NULL)
1413 prev_ac = &ac->next;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001414 ac = ALLOC_ONE(AutoCmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001415 if (ac == NULL)
1416 return FAIL;
1417 ac->cmd = vim_strsave(cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001418 ac->script_ctx = current_sctx;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001419 if (flags & UC_VIM9)
1420 ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001421#ifdef FEAT_EVAL
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001422 ac->script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001423#endif
1424 if (ac->cmd == NULL)
1425 {
1426 vim_free(ac);
1427 return FAIL;
1428 }
1429 ac->next = NULL;
1430 *prev_ac = ac;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001431 ac->once = once;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001432 ac->nested = nested;
1433 }
1434 }
1435
1436 au_cleanup(); // may really delete removed patterns/commands now
1437 return OK;
1438}
1439
1440/*
1441 * Implementation of ":doautocmd [group] event [fname]".
1442 * Return OK for success, FAIL for failure;
1443 */
1444 int
1445do_doautocmd(
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001446 char_u *arg_start,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001447 int do_msg, // give message for no matching autocmds?
1448 int *did_something)
1449{
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001450 char_u *arg = arg_start;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001451 char_u *fname;
1452 int nothing_done = TRUE;
1453 int group;
1454
1455 if (did_something != NULL)
1456 *did_something = FALSE;
1457
1458 /*
1459 * Check for a legal group name. If not, use AUGROUP_ALL.
1460 */
1461 group = au_get_grouparg(&arg);
1462 if (arg == NULL) // out of memory
1463 return FAIL;
1464
1465 if (*arg == '*')
1466 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001467 emsg(_(e_cant_execute_autocommands_for_all_events));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001468 return FAIL;
1469 }
1470
1471 /*
1472 * Scan over the events.
1473 * If we find an illegal name, return here, don't do anything.
1474 */
1475 fname = find_end_event(arg, group != AUGROUP_ALL);
1476 if (fname == NULL)
1477 return FAIL;
1478
1479 fname = skipwhite(fname);
1480
1481 /*
1482 * Loop over the events.
1483 */
1484 while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
1485 if (apply_autocmds_group(event_name2nr(arg, &arg),
1486 fname, NULL, TRUE, group, curbuf, NULL))
1487 nothing_done = FALSE;
1488
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001489 if (nothing_done && do_msg
1490#ifdef FEAT_EVAL
1491 && !aborting()
1492#endif
1493 )
1494 smsg(_("No matching autocommands: %s"), arg_start);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001495 if (did_something != NULL)
1496 *did_something = !nothing_done;
1497
1498#ifdef FEAT_EVAL
1499 return aborting() ? FAIL : OK;
1500#else
1501 return OK;
1502#endif
1503}
1504
1505/*
1506 * ":doautoall": execute autocommands for each loaded buffer.
1507 */
1508 void
1509ex_doautoall(exarg_T *eap)
1510{
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001511 int retval = OK;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001512 aco_save_T aco;
1513 buf_T *buf;
1514 bufref_T bufref;
1515 char_u *arg = eap->arg;
1516 int call_do_modelines = check_nomodeline(&arg);
1517 int did_aucmd;
1518
1519 /*
1520 * This is a bit tricky: For some commands curwin->w_buffer needs to be
1521 * equal to curbuf, but for some buffers there may not be a window.
1522 * So we change the buffer for the current window for a moment. This
1523 * gives problems when the autocommands make changes to the list of
1524 * buffers or windows...
1525 */
1526 FOR_ALL_BUFFERS(buf)
1527 {
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001528 // Only do loaded buffers and skip the current buffer, it's done last.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001529 if (buf->b_ml.ml_mfp == NULL || buf == curbuf)
1530 continue;
1531
Bram Moolenaare76062c2022-11-28 18:51:43 +00001532 // Find a window for this buffer and save some values.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001533 aucmd_prepbuf(&aco, buf);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001534 if (curbuf != buf)
1535 {
1536 // Failed to find a window for this buffer. Better not execute
1537 // autocommands then.
1538 retval = FAIL;
1539 break;
1540 }
1541
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001542 set_bufref(&bufref, buf);
1543
1544 // execute the autocommands for this buffer
1545 retval = do_doautocmd(arg, FALSE, &did_aucmd);
1546
1547 if (call_do_modelines && did_aucmd)
1548 // Execute the modeline settings, but don't set window-local
1549 // options if we are using the current window for another
1550 // buffer.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001551 do_modelines(is_aucmd_win(curwin) ? OPT_NOWIN : 0);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001552
1553 // restore the current window
1554 aucmd_restbuf(&aco);
1555
1556 // stop if there is some error or buffer was deleted
1557 if (retval == FAIL || !bufref_valid(&bufref))
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001558 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001559 retval = FAIL;
1560 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001561 }
1562 }
1563
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001564 // Execute autocommands for the current buffer last.
1565 if (retval == OK)
1566 {
1567 do_doautocmd(arg, FALSE, &did_aucmd);
1568 if (call_do_modelines && did_aucmd)
1569 do_modelines(0);
1570 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001571}
1572
1573/*
1574 * Check *argp for <nomodeline>. When it is present return FALSE, otherwise
1575 * return TRUE and advance *argp to after it.
1576 * Thus return TRUE when do_modelines() should be called.
1577 */
1578 int
1579check_nomodeline(char_u **argp)
1580{
1581 if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
1582 {
1583 *argp = skipwhite(*argp + 12);
1584 return FALSE;
1585 }
1586 return TRUE;
1587}
1588
1589/*
1590 * Prepare for executing autocommands for (hidden) buffer "buf".
1591 * Search for a visible window containing the current buffer. If there isn't
Bram Moolenaare76062c2022-11-28 18:51:43 +00001592 * one then use an entry in "aucmd_win[]".
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001593 * Set "curbuf" and "curwin" to match "buf".
Bram Moolenaare76062c2022-11-28 18:51:43 +00001594 * When this fails "curbuf" is not equal "buf".
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001595 */
1596 void
1597aucmd_prepbuf(
1598 aco_save_T *aco, // structure to save values in
1599 buf_T *buf) // new curbuf
1600{
1601 win_T *win;
1602 int save_ea;
1603#ifdef FEAT_AUTOCHDIR
1604 int save_acd;
1605#endif
1606
1607 // Find a window that is for the new buffer
1608 if (buf == curbuf) // be quick when buf is curbuf
1609 win = curwin;
1610 else
1611 FOR_ALL_WINDOWS(win)
1612 if (win->w_buffer == buf)
1613 break;
1614
Bram Moolenaare76062c2022-11-28 18:51:43 +00001615 // Allocate a window when needed.
1616 win_T *auc_win = NULL;
1617 int auc_idx = AUCMD_WIN_COUNT;
1618 if (win == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001619 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001620 for (auc_idx = 0; auc_idx < AUCMD_WIN_COUNT; ++auc_idx)
1621 if (!aucmd_win[auc_idx].auc_win_used)
1622 {
Bram Moolenaar84497cd2022-11-28 20:34:52 +00001623 if (aucmd_win[auc_idx].auc_win == NULL)
1624 aucmd_win[auc_idx].auc_win = win_alloc_popup_win();
1625 auc_win = aucmd_win[auc_idx].auc_win;
Bram Moolenaare76062c2022-11-28 18:51:43 +00001626 if (auc_win != NULL)
Bram Moolenaare76062c2022-11-28 18:51:43 +00001627 aucmd_win[auc_idx].auc_win_used = TRUE;
Bram Moolenaare76062c2022-11-28 18:51:43 +00001628 break;
1629 }
1630
1631 // If this fails (out of memory or using all AUCMD_WIN_COUNT
1632 // entries) then we can't reliable execute the autocmd, return with
1633 // "curbuf" unequal "buf".
1634 if (auc_win == NULL)
1635 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001636 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001637
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001638 aco->save_curwin_id = curwin->w_id;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001639 aco->save_prevwin_id = prevwin == NULL ? 0 : prevwin->w_id;
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001640 aco->save_State = State;
zeertzjqf2678472024-01-17 21:22:59 +01001641#ifdef FEAT_JOB_CHANNEL
1642 if (bt_prompt(curbuf))
1643 aco->save_prompt_insert = curbuf->b_prompt_insert;
1644#endif
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001645
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001646 if (win != NULL)
1647 {
1648 // There is a window for "buf" in the current tab page, make it the
1649 // curwin. This is preferred, it has the least side effects (esp. if
1650 // "buf" is curbuf).
Bram Moolenaare76062c2022-11-28 18:51:43 +00001651 aco->use_aucmd_win_idx = -1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001652 curwin = win;
1653 }
1654 else
1655 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001656 // There is no window for "buf", use "auc_win". To minimize the side
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001657 // effects, insert it in the current tab page.
1658 // Anything related to a window (e.g., setting folds) may have
1659 // unexpected results.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001660 aco->use_aucmd_win_idx = auc_idx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001661
Bram Moolenaare76062c2022-11-28 18:51:43 +00001662 win_init_popup_win(auc_win, buf);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001663
zeertzjq9d956ee2024-04-07 18:16:10 +02001664 // Make sure tp_localdir and globaldir are NULL to avoid a
1665 // chdir() in win_enter_ext().
1666 // win_init_popup_win() has already set w_localdir to NULL.
1667 aco->tp_localdir = curtab->tp_localdir;
1668 curtab->tp_localdir = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001669 aco->globaldir = globaldir;
1670 globaldir = NULL;
1671
Bram Moolenaare76062c2022-11-28 18:51:43 +00001672 // Split the current window, put the auc_win in the upper half.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001673 // We don't want the BufEnter or WinEnter autocommands.
1674 block_autocmds();
1675 make_snapshot(SNAP_AUCMD_IDX);
1676 save_ea = p_ea;
1677 p_ea = FALSE;
1678
1679#ifdef FEAT_AUTOCHDIR
1680 // Prevent chdir() call in win_enter_ext(), through do_autochdir().
1681 save_acd = p_acd;
1682 p_acd = FALSE;
1683#endif
1684
Sean Dewar704966c2024-02-20 22:00:33 +01001685 (void)win_split_ins(0, WSP_TOP | WSP_FORCE_ROOM, auc_win, 0, NULL);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001686 (void)win_comp_pos(); // recompute window positions
1687 p_ea = save_ea;
1688#ifdef FEAT_AUTOCHDIR
1689 p_acd = save_acd;
1690#endif
1691 unblock_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001692 curwin = auc_win;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001693 }
1694 curbuf = buf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001695 aco->new_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001696 set_bufref(&aco->new_curbuf, curbuf);
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001697
1698 // disable the Visual area, the position may be invalid in another buffer
1699 aco->save_VIsual_active = VIsual_active;
1700 VIsual_active = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001701}
1702
1703/*
1704 * Cleanup after executing autocommands for a (hidden) buffer.
1705 * Restore the window as it was (if possible).
1706 */
1707 void
1708aucmd_restbuf(
1709 aco_save_T *aco) // structure holding saved values
1710{
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001711 int dummy;
1712 win_T *save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001713
Bram Moolenaare76062c2022-11-28 18:51:43 +00001714 if (aco->use_aucmd_win_idx >= 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001715 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001716 win_T *awp = aucmd_win[aco->use_aucmd_win_idx].auc_win;
1717
Bram Moolenaare76062c2022-11-28 18:51:43 +00001718 // Find "awp", it can't be closed, but it may be in another tab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001719 // page. Do not trigger autocommands here.
1720 block_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001721 if (curwin != awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001722 {
1723 tabpage_T *tp;
1724 win_T *wp;
1725
1726 FOR_ALL_TAB_WINDOWS(tp, wp)
1727 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001728 if (wp == awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001729 {
1730 if (tp != curtab)
1731 goto_tabpage_tp(tp, TRUE, TRUE);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001732 win_goto(awp);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001733 goto win_found;
1734 }
1735 }
1736 }
1737win_found:
zeertzjq46bdae02023-09-24 23:16:08 +02001738 --curbuf->b_nwindows;
orbitalcde8de02023-04-02 22:05:13 +01001739#ifdef FEAT_JOB_CHANNEL
zeertzjqa40c0bc2023-05-27 14:10:08 +01001740 int save_stop_insert_mode = stop_insert_mode;
orbitalcde8de02023-04-02 22:05:13 +01001741 // May need to stop Insert mode if we were in a prompt buffer.
1742 leaving_window(curwin);
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001743 // Do not stop Insert mode when already in Insert mode before.
1744 if (aco->save_State & MODE_INSERT)
zeertzjqa40c0bc2023-05-27 14:10:08 +01001745 stop_insert_mode = save_stop_insert_mode;
orbitalcde8de02023-04-02 22:05:13 +01001746#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001747 // Remove the window and frame from the tree of frames.
Sean Dewar704966c2024-02-20 22:00:33 +01001748 (void)winframe_remove(curwin, &dummy, NULL, NULL);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001749 win_remove(curwin, NULL);
Bram Moolenaar84497cd2022-11-28 20:34:52 +00001750
1751 // The window is marked as not used, but it is not freed, it can be
1752 // used again.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001753 aucmd_win[aco->use_aucmd_win_idx].auc_win_used = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001754 last_status(FALSE); // may need to remove last status line
1755
1756 if (!valid_tabpage_win(curtab))
1757 // no valid window in current tabpage
1758 close_tabpage(curtab);
1759
1760 restore_snapshot(SNAP_AUCMD_IDX, FALSE);
1761 (void)win_comp_pos(); // recompute window positions
1762 unblock_autocmds();
1763
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001764 save_curwin = win_find_by_id(aco->save_curwin_id);
1765 if (save_curwin != NULL)
1766 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001767 else
1768 // Hmm, original window disappeared. Just use the first one.
1769 curwin = firstwin;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001770 curbuf = curwin->w_buffer;
1771#ifdef FEAT_JOB_CHANNEL
1772 // May need to restore insert mode for a prompt buffer.
1773 entering_window(curwin);
zeertzjqf2678472024-01-17 21:22:59 +01001774 if (bt_prompt(curbuf))
1775 curbuf->b_prompt_insert = aco->save_prompt_insert;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001776#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001777 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001778#ifdef FEAT_EVAL
Bram Moolenaare76062c2022-11-28 18:51:43 +00001779 vars_clear(&awp->w_vars->dv_hashtab); // free all w: variables
1780 hash_init(&awp->w_vars->dv_hashtab); // re-use the hashtab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001781#endif
zeertzjq9d956ee2024-04-07 18:16:10 +02001782 // If :lcd has been used in the autocommand window, correct current
1783 // directory before restoring tp_localdir and globaldir.
1784 if (awp->w_localdir != NULL)
1785 win_fix_current_dir();
1786 vim_free(curtab->tp_localdir);
1787 curtab->tp_localdir = aco->tp_localdir;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001788 vim_free(globaldir);
1789 globaldir = aco->globaldir;
1790
1791 // the buffer contents may have changed
zeertzjq49f05242023-02-04 10:58:34 +00001792 VIsual_active = aco->save_VIsual_active;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001793 check_cursor();
1794 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1795 {
1796 curwin->w_topline = curbuf->b_ml.ml_line_count;
1797#ifdef FEAT_DIFF
1798 curwin->w_topfill = 0;
1799#endif
1800 }
1801#if defined(FEAT_GUI)
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001802 if (gui.in_use)
1803 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001804 // Hide the scrollbars from the "awp" and update.
1805 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_LEFT], FALSE);
1806 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_RIGHT], FALSE);
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001807 gui_may_update_scrollbars();
1808 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001809#endif
1810 }
1811 else
1812 {
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001813 // Restore curwin. Use the window ID, a window may have been closed
1814 // and the memory re-used for another one.
1815 save_curwin = win_find_by_id(aco->save_curwin_id);
1816 if (save_curwin != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001817 {
1818 // Restore the buffer which was previously edited by curwin, if
1819 // it was changed, we are still the same window and the buffer is
1820 // valid.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001821 if (curwin->w_id == aco->new_curwin_id
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001822 && curbuf != aco->new_curbuf.br_buf
1823 && bufref_valid(&aco->new_curbuf)
1824 && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
1825 {
1826# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
1827 if (curwin->w_s == &curbuf->b_s)
1828 curwin->w_s = &aco->new_curbuf.br_buf->b_s;
1829# endif
1830 --curbuf->b_nwindows;
1831 curbuf = aco->new_curbuf.br_buf;
1832 curwin->w_buffer = curbuf;
1833 ++curbuf->b_nwindows;
1834 }
1835
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001836 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001837 curbuf = curwin->w_buffer;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001838 prevwin = win_find_by_id(aco->save_prevwin_id);
zeertzjq49f05242023-02-04 10:58:34 +00001839
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001840 // In case the autocommand moves the cursor to a position that
1841 // does not exist in curbuf.
zeertzjq49f05242023-02-04 10:58:34 +00001842 VIsual_active = aco->save_VIsual_active;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001843 check_cursor();
1844 }
1845 }
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001846
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001847 VIsual_active = aco->save_VIsual_active;
zeertzjq49f05242023-02-04 10:58:34 +00001848 check_cursor(); // just in case lines got deleted
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001849 if (VIsual_active)
1850 check_pos(curbuf, &VIsual);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001851}
1852
1853static int autocmd_nested = FALSE;
1854
1855/*
1856 * Execute autocommands for "event" and file name "fname".
1857 * Return TRUE if some commands were executed.
1858 */
1859 int
1860apply_autocmds(
1861 event_T event,
1862 char_u *fname, // NULL or empty means use actual file name
1863 char_u *fname_io, // fname to use for <afile> on cmdline
1864 int force, // when TRUE, ignore autocmd_busy
1865 buf_T *buf) // buffer for <abuf>
1866{
1867 return apply_autocmds_group(event, fname, fname_io, force,
1868 AUGROUP_ALL, buf, NULL);
1869}
1870
1871/*
1872 * Like apply_autocmds(), but with extra "eap" argument. This takes care of
1873 * setting v:filearg.
1874 */
1875 int
1876apply_autocmds_exarg(
1877 event_T event,
1878 char_u *fname,
1879 char_u *fname_io,
1880 int force,
1881 buf_T *buf,
1882 exarg_T *eap)
1883{
1884 return apply_autocmds_group(event, fname, fname_io, force,
1885 AUGROUP_ALL, buf, eap);
1886}
1887
1888/*
1889 * Like apply_autocmds(), but handles the caller's retval. If the script
1890 * processing is being aborted or if retval is FAIL when inside a try
1891 * conditional, no autocommands are executed. If otherwise the autocommands
1892 * cause the script to be aborted, retval is set to FAIL.
1893 */
1894 int
1895apply_autocmds_retval(
1896 event_T event,
1897 char_u *fname, // NULL or empty means use actual file name
1898 char_u *fname_io, // fname to use for <afile> on cmdline
1899 int force, // when TRUE, ignore autocmd_busy
1900 buf_T *buf, // buffer for <abuf>
1901 int *retval) // pointer to caller's retval
1902{
1903 int did_cmd;
1904
1905#ifdef FEAT_EVAL
1906 if (should_abort(*retval))
1907 return FALSE;
1908#endif
1909
1910 did_cmd = apply_autocmds_group(event, fname, fname_io, force,
1911 AUGROUP_ALL, buf, NULL);
1912 if (did_cmd
1913#ifdef FEAT_EVAL
1914 && aborting()
1915#endif
1916 )
1917 *retval = FAIL;
1918 return did_cmd;
1919}
1920
1921/*
1922 * Return TRUE when there is a CursorHold autocommand defined.
1923 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001924 static int
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001925has_cursorhold(void)
1926{
Bram Moolenaar24959102022-05-07 20:01:16 +01001927 return (first_autopat[(int)(get_real_state() == MODE_NORMAL_BUSY
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001928 ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
1929}
1930
1931/*
1932 * Return TRUE if the CursorHold event can be triggered.
1933 */
1934 int
1935trigger_cursorhold(void)
1936{
1937 int state;
1938
1939 if (!did_cursorhold
1940 && has_cursorhold()
1941 && reg_recording == 0
1942 && typebuf.tb_len == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001943 && !ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001944 {
1945 state = get_real_state();
Bram Moolenaar24959102022-05-07 20:01:16 +01001946 if (state == MODE_NORMAL_BUSY || (state & MODE_INSERT) != 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001947 return TRUE;
1948 }
1949 return FALSE;
1950}
1951
1952/*
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001953 * Return TRUE when there is a WinResized autocommand defined.
1954 */
1955 int
1956has_winresized(void)
1957{
1958 return (first_autopat[(int)EVENT_WINRESIZED] != NULL);
1959}
1960
1961/*
LemonBoy09371822022-04-08 15:18:45 +01001962 * Return TRUE when there is a WinScrolled autocommand defined.
1963 */
1964 int
1965has_winscrolled(void)
1966{
1967 return (first_autopat[(int)EVENT_WINSCROLLED] != NULL);
1968}
1969
1970/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001971 * Return TRUE when there is a CursorMoved autocommand defined.
1972 */
1973 int
1974has_cursormoved(void)
1975{
1976 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1977}
1978
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001979/*
1980 * Return TRUE when there is a CursorMovedI autocommand defined.
1981 */
1982 int
1983has_cursormovedI(void)
1984{
1985 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1986}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001987
1988/*
1989 * Return TRUE when there is a TextChanged autocommand defined.
1990 */
1991 int
1992has_textchanged(void)
1993{
1994 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1995}
1996
1997/*
1998 * Return TRUE when there is a TextChangedI autocommand defined.
1999 */
2000 int
2001has_textchangedI(void)
2002{
2003 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
2004}
2005
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002006/*
2007 * Return TRUE when there is a TextChangedP autocommand defined.
2008 */
2009 int
2010has_textchangedP(void)
2011{
2012 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
2013}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002014
2015/*
2016 * Return TRUE when there is an InsertCharPre autocommand defined.
2017 */
2018 int
2019has_insertcharpre(void)
2020{
2021 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
2022}
2023
2024/*
2025 * Return TRUE when there is an CmdUndefined autocommand defined.
2026 */
2027 int
2028has_cmdundefined(void)
2029{
2030 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
2031}
2032
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002033#if defined(FEAT_EVAL) || defined(PROTO)
2034/*
2035 * Return TRUE when there is a TextYankPost autocommand defined.
2036 */
2037 int
2038has_textyankpost(void)
2039{
2040 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
2041}
2042#endif
2043
Bram Moolenaard7f246c2019-04-08 18:15:41 +02002044#if defined(FEAT_EVAL) || defined(PROTO)
2045/*
2046 * Return TRUE when there is a CompleteChanged autocommand defined.
2047 */
2048 int
2049has_completechanged(void)
2050{
2051 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
2052}
2053#endif
2054
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002055#if defined(FEAT_EVAL) || defined(PROTO)
2056/*
2057 * Return TRUE when there is a ModeChanged autocommand defined.
2058 */
2059 int
2060has_modechanged(void)
2061{
2062 return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
2063}
2064#endif
2065
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002066/*
2067 * Execute autocommands for "event" and file name "fname".
2068 * Return TRUE if some commands were executed.
2069 */
2070 static int
2071apply_autocmds_group(
2072 event_T event,
2073 char_u *fname, // NULL or empty means use actual file name
2074 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
2075 // use fname
2076 int force, // when TRUE, ignore autocmd_busy
2077 int group, // group ID, or AUGROUP_ALL
2078 buf_T *buf, // buffer for <abuf>
2079 exarg_T *eap UNUSED) // command arguments
2080{
2081 char_u *sfname = NULL; // short file name
2082 char_u *tail;
2083 int save_changed;
2084 buf_T *old_curbuf;
2085 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002086 char_u *save_autocmd_fname;
2087 int save_autocmd_fname_full;
2088 int save_autocmd_bufnr;
2089 char_u *save_autocmd_match;
2090 int save_autocmd_busy;
2091 int save_autocmd_nested;
2092 static int nesting = 0;
LemonBoyeca7c602022-04-14 15:39:43 +01002093 AutoPatCmd_T patcmd;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002094 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002095 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002096#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002097 funccal_entry_T funccal_entry;
2098 char_u *save_cmdarg;
2099 long save_cmdbang;
2100#endif
2101 static int filechangeshell_busy = FALSE;
2102#ifdef FEAT_PROFILE
2103 proftime_T wait_time;
2104#endif
2105 int did_save_redobuff = FALSE;
2106 save_redo_T save_redo;
2107 int save_KeyTyped = KeyTyped;
ichizok7e5fe382023-04-15 13:17:50 +01002108 ESTACK_CHECK_DECLARATION;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002109
2110 /*
2111 * Quickly return if there are no autocommands for this event or
2112 * autocommands are blocked.
2113 */
2114 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
2115 || autocmd_blocked > 0)
2116 goto BYPASS_AU;
2117
2118 /*
2119 * When autocommands are busy, new autocommands are only executed when
2120 * explicitly enabled with the "nested" flag.
2121 */
2122 if (autocmd_busy && !(force || autocmd_nested))
2123 goto BYPASS_AU;
2124
2125#ifdef FEAT_EVAL
2126 /*
2127 * Quickly return when immediately aborting on error, or when an interrupt
2128 * occurred or an exception was thrown but not caught.
2129 */
2130 if (aborting())
2131 goto BYPASS_AU;
2132#endif
2133
2134 /*
2135 * FileChangedShell never nests, because it can create an endless loop.
2136 */
2137 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
2138 || event == EVENT_FILECHANGEDSHELLPOST))
2139 goto BYPASS_AU;
2140
2141 /*
2142 * Ignore events in 'eventignore'.
2143 */
2144 if (event_ignored(event))
2145 goto BYPASS_AU;
2146
2147 /*
2148 * Allow nesting of autocommands, but restrict the depth, because it's
2149 * possible to create an endless loop.
2150 */
2151 if (nesting == 10)
2152 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00002153 emsg(_(e_autocommand_nesting_too_deep));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002154 goto BYPASS_AU;
2155 }
2156
2157 /*
2158 * Check if these autocommands are disabled. Used when doing ":all" or
2159 * ":ball".
2160 */
2161 if ( (autocmd_no_enter
2162 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
2163 || (autocmd_no_leave
2164 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
2165 goto BYPASS_AU;
2166
Bram Moolenaarbb393d82022-12-09 12:21:50 +00002167 if (event == EVENT_CMDLINECHANGED)
2168 ++aucmd_cmdline_changed_count;
2169
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002170 /*
2171 * Save the autocmd_* variables and info about the current buffer.
2172 */
2173 save_autocmd_fname = autocmd_fname;
2174 save_autocmd_fname_full = autocmd_fname_full;
2175 save_autocmd_bufnr = autocmd_bufnr;
2176 save_autocmd_match = autocmd_match;
2177 save_autocmd_busy = autocmd_busy;
2178 save_autocmd_nested = autocmd_nested;
2179 save_changed = curbuf->b_changed;
2180 old_curbuf = curbuf;
2181
2182 /*
2183 * Set the file name to be used for <afile>.
2184 * Make a copy to avoid that changing a buffer name or directory makes it
2185 * invalid.
2186 */
2187 if (fname_io == NULL)
2188 {
2189 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
Bram Moolenaarbb393d82022-12-09 12:21:50 +00002190 || event == EVENT_OPTIONSET
Danek Duvalld7d56032024-01-14 20:19:59 +01002191 || event == EVENT_MODECHANGED
2192 || event == EVENT_TERMRESPONSEALL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002193 autocmd_fname = NULL;
2194 else if (fname != NULL && !ends_excmd(*fname))
2195 autocmd_fname = fname;
2196 else if (buf != NULL)
2197 autocmd_fname = buf->b_ffname;
2198 else
2199 autocmd_fname = NULL;
2200 }
2201 else
2202 autocmd_fname = fname_io;
2203 if (autocmd_fname != NULL)
2204 autocmd_fname = vim_strsave(autocmd_fname);
2205 autocmd_fname_full = FALSE; // call FullName_save() later
2206
2207 /*
2208 * Set the buffer number to be used for <abuf>.
2209 */
2210 if (buf == NULL)
2211 autocmd_bufnr = 0;
2212 else
2213 autocmd_bufnr = buf->b_fnum;
2214
2215 /*
2216 * When the file name is NULL or empty, use the file name of buffer "buf".
2217 * Always use the full path of the file name to match with, in case
2218 * "allow_dirs" is set.
2219 */
2220 if (fname == NULL || *fname == NUL)
2221 {
2222 if (buf == NULL)
2223 fname = NULL;
2224 else
2225 {
2226#ifdef FEAT_SYN_HL
2227 if (event == EVENT_SYNTAX)
2228 fname = buf->b_p_syn;
2229 else
2230#endif
2231 if (event == EVENT_FILETYPE)
2232 fname = buf->b_p_ft;
2233 else
2234 {
2235 if (buf->b_sfname != NULL)
2236 sfname = vim_strsave(buf->b_sfname);
2237 fname = buf->b_ffname;
2238 }
2239 }
2240 if (fname == NULL)
2241 fname = (char_u *)"";
2242 fname = vim_strsave(fname); // make a copy, so we can change it
2243 }
2244 else
2245 {
2246 sfname = vim_strsave(fname);
2247 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002248 // ColorScheme, QuickFixCmd*, DirChanged and similar.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002249 if (event == EVENT_FILETYPE
2250 || event == EVENT_SYNTAX
2251 || event == EVENT_CMDLINECHANGED
2252 || event == EVENT_CMDLINEENTER
2253 || event == EVENT_CMDLINELEAVE
Shougo Matsushitad0952142024-06-20 22:05:16 +02002254 || event == EVENT_CURSORMOVEDC
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002255 || event == EVENT_CMDWINENTER
2256 || event == EVENT_CMDWINLEAVE
2257 || event == EVENT_CMDUNDEFINED
2258 || event == EVENT_FUNCUNDEFINED
2259 || event == EVENT_REMOTEREPLY
2260 || event == EVENT_SPELLFILEMISSING
2261 || event == EVENT_QUICKFIXCMDPRE
2262 || event == EVENT_COLORSCHEME
2263 || event == EVENT_COLORSCHEMEPRE
2264 || event == EVENT_OPTIONSET
2265 || event == EVENT_QUICKFIXCMDPOST
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002266 || event == EVENT_DIRCHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002267 || event == EVENT_DIRCHANGEDPRE
naohiro ono23beefe2021-11-13 12:38:49 +00002268 || event == EVENT_MODECHANGED
zeertzjqc601d982022-10-10 13:46:15 +01002269 || event == EVENT_MENUPOPUP
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002270 || event == EVENT_USER
LemonBoy09371822022-04-08 15:18:45 +01002271 || event == EVENT_WINCLOSED
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00002272 || event == EVENT_WINRESIZED
Danek Duvalld7d56032024-01-14 20:19:59 +01002273 || event == EVENT_WINSCROLLED
2274 || event == EVENT_TERMRESPONSEALL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002275 {
2276 fname = vim_strsave(fname);
2277 autocmd_fname_full = TRUE; // don't expand it later
2278 }
2279 else
2280 fname = FullName_save(fname, FALSE);
2281 }
2282 if (fname == NULL) // out of memory
2283 {
2284 vim_free(sfname);
2285 retval = FALSE;
2286 goto BYPASS_AU;
2287 }
2288
2289#ifdef BACKSLASH_IN_FILENAME
2290 /*
2291 * Replace all backslashes with forward slashes. This makes the
2292 * autocommand patterns portable between Unix and MS-DOS.
2293 */
2294 if (sfname != NULL)
2295 forward_slash(sfname);
2296 forward_slash(fname);
2297#endif
2298
2299#ifdef VMS
2300 // remove version for correct match
2301 if (sfname != NULL)
2302 vms_remove_version(sfname);
2303 vms_remove_version(fname);
2304#endif
2305
2306 /*
2307 * Set the name to be used for <amatch>.
2308 */
2309 autocmd_match = fname;
2310
2311
2312 // Don't redraw while doing autocommands.
2313 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002314
2315 // name and lnum are filled in later
2316 estack_push(ETYPE_AUCMD, NULL, 0);
ichizok7e5fe382023-04-15 13:17:50 +01002317 ESTACK_CHECK_SETUP;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002318
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002319 save_current_sctx = current_sctx;
2320
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002321#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002322# ifdef FEAT_PROFILE
2323 if (do_profiling == PROF_YES)
2324 prof_child_enter(&wait_time); // doesn't count for the caller itself
2325# endif
2326
2327 // Don't use local function variables, if called from a function.
2328 save_funccal(&funccal_entry);
2329#endif
2330
2331 /*
2332 * When starting to execute autocommands, save the search patterns.
2333 */
2334 if (!autocmd_busy)
2335 {
2336 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002337 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002338 {
2339 saveRedobuff(&save_redo);
2340 did_save_redobuff = TRUE;
2341 }
zeertzjq5bf6c212024-03-31 18:41:27 +02002342 curbuf->b_did_filetype = curbuf->b_keep_filetype;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002343 }
2344
2345 /*
2346 * Note that we are applying autocmds. Some commands need to know.
2347 */
2348 autocmd_busy = TRUE;
2349 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2350 ++nesting; // see matching decrement below
2351
2352 // Remember that FileType was triggered. Used for did_filetype().
2353 if (event == EVENT_FILETYPE)
zeertzjq5bf6c212024-03-31 18:41:27 +02002354 curbuf->b_did_filetype = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002355
2356 tail = gettail(fname);
2357
2358 // Find first autocommand that matches
LemonBoyeca7c602022-04-14 15:39:43 +01002359 CLEAR_FIELD(patcmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002360 patcmd.curpat = first_autopat[(int)event];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002361 patcmd.group = group;
2362 patcmd.fname = fname;
2363 patcmd.sfname = sfname;
2364 patcmd.tail = tail;
2365 patcmd.event = event;
2366 patcmd.arg_bufnr = autocmd_bufnr;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002367 auto_next_pat(&patcmd, FALSE);
2368
2369 // found one, start executing the autocommands
2370 if (patcmd.curpat != NULL)
2371 {
2372 // add to active_apc_list
2373 patcmd.next = active_apc_list;
2374 active_apc_list = &patcmd;
2375
2376#ifdef FEAT_EVAL
2377 // set v:cmdarg (only when there is a matching pattern)
2378 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2379 if (eap != NULL)
2380 {
2381 save_cmdarg = set_cmdarg(eap, NULL);
2382 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2383 }
2384 else
2385 save_cmdarg = NULL; // avoid gcc warning
2386#endif
2387 retval = TRUE;
2388 // mark the last pattern, to avoid an endless loop when more patterns
2389 // are added when executing autocommands
2390 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2391 ap->last = FALSE;
2392 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002393
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002394 // Make sure cursor and topline are valid. The first time the current
2395 // values are saved, restored by reset_lnums(). When nested only the
2396 // values are corrected when needed.
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002397 if (nesting == 1)
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002398 check_lnums(TRUE);
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002399 else
2400 check_lnums_nested(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002401
Christian Brabandt590aae32023-06-25 22:34:22 +01002402 int save_did_emsg = did_emsg;
2403 int save_ex_pressedreturn = get_pressedreturn();
ichizokc3f91c02021-12-17 09:44:33 +00002404
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002405 do_cmdline(NULL, getnextac, (void *)&patcmd,
2406 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002407
ichizokc3f91c02021-12-17 09:44:33 +00002408 did_emsg += save_did_emsg;
Christian Brabandt590aae32023-06-25 22:34:22 +01002409 set_pressedreturn(save_ex_pressedreturn);
ichizokc3f91c02021-12-17 09:44:33 +00002410
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002411 if (nesting == 1)
2412 // restore cursor and topline, unless they were changed
2413 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002414
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002415#ifdef FEAT_EVAL
2416 if (eap != NULL)
2417 {
2418 (void)set_cmdarg(NULL, save_cmdarg);
2419 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2420 }
2421#endif
2422 // delete from active_apc_list
2423 if (active_apc_list == &patcmd) // just in case
2424 active_apc_list = patcmd.next;
2425 }
2426
Bram Moolenaar79cdf022023-05-20 14:07:00 +01002427 if (RedrawingDisabled > 0)
2428 --RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002429 autocmd_busy = save_autocmd_busy;
2430 filechangeshell_busy = FALSE;
2431 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002432 vim_free(SOURCING_NAME);
ichizok7e5fe382023-04-15 13:17:50 +01002433 ESTACK_CHECK_NOW;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002434 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002435 vim_free(autocmd_fname);
2436 autocmd_fname = save_autocmd_fname;
2437 autocmd_fname_full = save_autocmd_fname_full;
2438 autocmd_bufnr = save_autocmd_bufnr;
2439 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002440 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002441#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002442 restore_funccal();
2443# ifdef FEAT_PROFILE
2444 if (do_profiling == PROF_YES)
2445 prof_child_exit(&wait_time);
2446# endif
2447#endif
2448 KeyTyped = save_KeyTyped;
2449 vim_free(fname);
2450 vim_free(sfname);
2451 --nesting; // see matching increment above
2452
2453 /*
2454 * When stopping to execute autocommands, restore the search patterns and
2455 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2456 * free any windows in the au_pending_free_win list.
2457 */
2458 if (!autocmd_busy)
2459 {
2460 restore_search_patterns();
2461 if (did_save_redobuff)
2462 restoreRedobuff(&save_redo);
zeertzjq5bf6c212024-03-31 18:41:27 +02002463 curbuf->b_did_filetype = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002464 while (au_pending_free_buf != NULL)
2465 {
2466 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002467
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002468 vim_free(au_pending_free_buf);
2469 au_pending_free_buf = b;
2470 }
2471 while (au_pending_free_win != NULL)
2472 {
2473 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002474
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002475 vim_free(au_pending_free_win);
2476 au_pending_free_win = w;
2477 }
2478 }
2479
2480 /*
2481 * Some events don't set or reset the Changed flag.
2482 * Check if still in the same buffer!
2483 */
2484 if (curbuf == old_curbuf
2485 && (event == EVENT_BUFREADPOST
2486 || event == EVENT_BUFWRITEPOST
2487 || event == EVENT_FILEAPPENDPOST
2488 || event == EVENT_VIMLEAVE
2489 || event == EVENT_VIMLEAVEPRE))
2490 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002491 if (curbuf->b_changed != save_changed)
2492 need_maketitle = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002493 curbuf->b_changed = save_changed;
2494 }
2495
2496 au_cleanup(); // may really delete removed patterns/commands now
2497
2498BYPASS_AU:
2499 // When wiping out a buffer make sure all its buffer-local autocommands
2500 // are deleted.
2501 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2502 aubuflocal_remove(buf);
2503
2504 if (retval == OK && event == EVENT_FILETYPE)
zeertzjq5bf6c212024-03-31 18:41:27 +02002505 curbuf->b_au_did_filetype = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002506
2507 return retval;
2508}
2509
2510# ifdef FEAT_EVAL
2511static char_u *old_termresponse = NULL;
Danek Duvalld7d56032024-01-14 20:19:59 +01002512static char_u *old_termu7resp = NULL;
2513static char_u *old_termblinkresp = NULL;
2514static char_u *old_termrbgresp = NULL;
2515static char_u *old_termrfgresp = NULL;
2516static char_u *old_termstyleresp = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002517# endif
2518
2519/*
2520 * Block triggering autocommands until unblock_autocmd() is called.
2521 * Can be used recursively, so long as it's symmetric.
2522 */
2523 void
2524block_autocmds(void)
2525{
2526# ifdef FEAT_EVAL
2527 // Remember the value of v:termresponse.
2528 if (autocmd_blocked == 0)
Danek Duvalld7d56032024-01-14 20:19:59 +01002529 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002530 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
Danek Duvalld7d56032024-01-14 20:19:59 +01002531 old_termu7resp = get_vim_var_str(VV_TERMU7RESP);
2532 old_termblinkresp = get_vim_var_str(VV_TERMBLINKRESP);
2533 old_termrbgresp = get_vim_var_str(VV_TERMRBGRESP);
2534 old_termrfgresp = get_vim_var_str(VV_TERMRFGRESP);
2535 old_termstyleresp = get_vim_var_str(VV_TERMSTYLERESP);
2536 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002537# endif
2538 ++autocmd_blocked;
2539}
2540
2541 void
2542unblock_autocmds(void)
2543{
2544 --autocmd_blocked;
2545
2546# ifdef FEAT_EVAL
Danek Duvalld7d56032024-01-14 20:19:59 +01002547 // When v:termresponse, etc, were set while autocommands were blocked,
2548 // trigger the autocommands now. Esp. useful when executing a shell
2549 // command during startup (vimdiff).
2550 if (autocmd_blocked == 0)
2551 {
2552 if (get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2553 {
2554 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2555 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"version", NULL, FALSE, curbuf);
2556 }
2557 if (get_vim_var_str(VV_TERMU7RESP) != old_termu7resp)
2558 {
2559 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"ambiguouswidth", NULL, FALSE, curbuf);
2560 }
2561 if (get_vim_var_str(VV_TERMBLINKRESP) != old_termblinkresp)
2562 {
2563 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"cursorblink", NULL, FALSE, curbuf);
2564 }
2565 if (get_vim_var_str(VV_TERMRBGRESP) != old_termrbgresp)
2566 {
2567 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"background", NULL, FALSE, curbuf);
2568 }
2569 if (get_vim_var_str(VV_TERMRFGRESP) != old_termrfgresp)
2570 {
2571 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"foreground", NULL, FALSE, curbuf);
2572 }
2573 if (get_vim_var_str(VV_TERMSTYLERESP) != old_termstyleresp)
2574 {
2575 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"cursorshape", NULL, FALSE, curbuf);
2576 }
2577 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002578# endif
2579}
2580
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002581 int
2582is_autocmd_blocked(void)
2583{
2584 return autocmd_blocked != 0;
2585}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002586
2587/*
2588 * Find next autocommand pattern that matches.
2589 */
2590 static void
2591auto_next_pat(
LemonBoyeca7c602022-04-14 15:39:43 +01002592 AutoPatCmd_T *apc,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002593 int stop_at_last) // stop when 'last' flag is set
2594{
2595 AutoPat *ap;
2596 AutoCmd *cp;
2597 char_u *name;
2598 char *s;
LemonBoyeca7c602022-04-14 15:39:43 +01002599 estack_T *entry;
2600 char_u *namep;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002601
LemonBoyeca7c602022-04-14 15:39:43 +01002602 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
2603
2604 // Clear the exestack entry for this ETYPE_AUCMD entry.
2605 VIM_CLEAR(entry->es_name);
2606 entry->es_info.aucmd = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002607
2608 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2609 {
2610 apc->curpat = NULL;
2611
2612 // Only use a pattern when it has not been removed, has commands and
2613 // the group matches. For buffer-local autocommands only check the
2614 // buffer number.
2615 if (ap->pat != NULL && ap->cmds != NULL
2616 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2617 {
2618 // execution-condition
2619 if (ap->buflocal_nr == 0
2620 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2621 apc->sfname, apc->tail, ap->allow_dirs))
2622 : ap->buflocal_nr == apc->arg_bufnr)
2623 {
2624 name = event_nr2name(apc->event);
2625 s = _("%s Autocommands for \"%s\"");
LemonBoyeca7c602022-04-14 15:39:43 +01002626 namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1);
2627 if (namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002628 {
LemonBoyeca7c602022-04-14 15:39:43 +01002629 sprintf((char *)namep, s, (char *)name, (char *)ap->pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002630 if (p_verbose >= 8)
2631 {
2632 verbose_enter();
LemonBoyeca7c602022-04-14 15:39:43 +01002633 smsg(_("Executing %s"), namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002634 verbose_leave();
2635 }
2636 }
2637
LemonBoyeca7c602022-04-14 15:39:43 +01002638 // Update the exestack entry for this autocmd.
2639 entry->es_name = namep;
2640 entry->es_info.aucmd = apc;
2641
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002642 apc->curpat = ap;
2643 apc->nextcmd = ap->cmds;
2644 // mark last command
2645 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2646 cp->last = FALSE;
2647 cp->last = TRUE;
2648 }
2649 line_breakcheck();
2650 if (apc->curpat != NULL) // found a match
2651 break;
2652 }
2653 if (stop_at_last && ap->last)
2654 break;
2655 }
2656}
2657
Dominique Pellee764d1b2023-03-12 21:20:59 +00002658#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002659/*
LemonBoyeca7c602022-04-14 15:39:43 +01002660 * Get the script context where autocommand "acp" is defined.
2661 */
2662 sctx_T *
2663acp_script_ctx(AutoPatCmd_T *acp)
2664{
2665 return &acp->script_ctx;
2666}
Dominique Pellee764d1b2023-03-12 21:20:59 +00002667#endif
LemonBoyeca7c602022-04-14 15:39:43 +01002668
2669/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002670 * Get next autocommand command.
2671 * Called by do_cmdline() to get the next line for ":if".
2672 * Returns allocated string, or NULL for end of autocommands.
2673 */
2674 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002675getnextac(
2676 int c UNUSED,
2677 void *cookie,
2678 int indent UNUSED,
2679 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002680{
LemonBoyeca7c602022-04-14 15:39:43 +01002681 AutoPatCmd_T *acp = (AutoPatCmd_T *)cookie;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002682 char_u *retval;
2683 AutoCmd *ac;
2684
2685 // Can be called again after returning the last line.
2686 if (acp->curpat == NULL)
2687 return NULL;
2688
2689 // repeat until we find an autocommand to execute
2690 for (;;)
2691 {
2692 // skip removed commands
2693 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2694 if (acp->nextcmd->last)
2695 acp->nextcmd = NULL;
2696 else
2697 acp->nextcmd = acp->nextcmd->next;
2698
2699 if (acp->nextcmd != NULL)
2700 break;
2701
2702 // at end of commands, find next pattern that matches
2703 if (acp->curpat->last)
2704 acp->curpat = NULL;
2705 else
2706 acp->curpat = acp->curpat->next;
2707 if (acp->curpat != NULL)
2708 auto_next_pat(acp, TRUE);
2709 if (acp->curpat == NULL)
2710 return NULL;
2711 }
2712
2713 ac = acp->nextcmd;
2714
2715 if (p_verbose >= 9)
2716 {
2717 verbose_enter_scroll();
2718 smsg(_("autocommand %s"), ac->cmd);
2719 msg_puts("\n"); // don't overwrite this either
2720 verbose_leave_scroll();
2721 }
2722 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002723 // Remove one-shot ("once") autocmd in anticipation of its execution.
2724 if (ac->once)
2725 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002726 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002727 current_sctx = ac->script_ctx;
LemonBoyeca7c602022-04-14 15:39:43 +01002728 acp->script_ctx = current_sctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002729 if (ac->last)
2730 acp->nextcmd = NULL;
2731 else
2732 acp->nextcmd = ac->next;
2733 return retval;
2734}
2735
2736/*
2737 * Return TRUE if there is a matching autocommand for "fname".
2738 * To account for buffer-local autocommands, function needs to know
2739 * in which buffer the file will be opened.
2740 */
2741 int
2742has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2743{
2744 AutoPat *ap;
2745 char_u *fname;
2746 char_u *tail = gettail(sfname);
2747 int retval = FALSE;
2748
2749 fname = FullName_save(sfname, FALSE);
2750 if (fname == NULL)
2751 return FALSE;
2752
2753#ifdef BACKSLASH_IN_FILENAME
2754 /*
2755 * Replace all backslashes with forward slashes. This makes the
2756 * autocommand patterns portable between Unix and MS-DOS.
2757 */
2758 sfname = vim_strsave(sfname);
2759 if (sfname != NULL)
2760 forward_slash(sfname);
2761 forward_slash(fname);
2762#endif
2763
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002764 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002765 if (ap->pat != NULL && ap->cmds != NULL
2766 && (ap->buflocal_nr == 0
2767 ? match_file_pat(NULL, &ap->reg_prog,
2768 fname, sfname, tail, ap->allow_dirs)
2769 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2770 ))
2771 {
2772 retval = TRUE;
2773 break;
2774 }
2775
2776 vim_free(fname);
2777#ifdef BACKSLASH_IN_FILENAME
2778 vim_free(sfname);
2779#endif
2780
2781 return retval;
2782}
2783
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002784/*
2785 * Function given to ExpandGeneric() to obtain the list of autocommand group
2786 * names.
2787 */
2788 char_u *
2789get_augroup_name(expand_T *xp UNUSED, int idx)
2790{
2791 if (idx == augroups.ga_len) // add "END" add the end
2792 return (char_u *)"END";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002793 if (idx < 0 || idx >= augroups.ga_len) // end of list
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002794 return NULL;
2795 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2796 // skip deleted entries
2797 return (char_u *)"";
2798 return AUGROUP_NAME(idx); // return a name
2799}
2800
2801static int include_groups = FALSE;
2802
2803 char_u *
2804set_context_in_autocmd(
2805 expand_T *xp,
2806 char_u *arg,
2807 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2808{
2809 char_u *p;
2810 int group;
2811
2812 // check for a group name, skip it if present
2813 include_groups = FALSE;
2814 p = arg;
2815 group = au_get_grouparg(&arg);
2816 if (group == AUGROUP_ERROR)
2817 return NULL;
2818 // If there only is a group name that's what we expand.
2819 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2820 {
2821 arg = p;
2822 group = AUGROUP_ALL;
2823 }
2824
2825 // skip over event name
2826 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2827 if (*p == ',')
2828 arg = p + 1;
2829 if (*p == NUL)
2830 {
2831 if (group == AUGROUP_ALL)
2832 include_groups = TRUE;
2833 xp->xp_context = EXPAND_EVENTS; // expand event name
2834 xp->xp_pattern = arg;
2835 return NULL;
2836 }
2837
2838 // skip over pattern
2839 arg = skipwhite(p);
2840 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2841 arg++;
2842 if (*arg)
2843 return arg; // expand (next) command
2844
2845 if (doautocmd)
2846 xp->xp_context = EXPAND_FILES; // expand file names
2847 else
2848 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2849 return NULL;
2850}
2851
2852/*
2853 * Function given to ExpandGeneric() to obtain the list of event names.
2854 */
2855 char_u *
2856get_event_name(expand_T *xp UNUSED, int idx)
2857{
John Marriott78d742a2024-04-02 20:26:01 +02002858 int i;
2859
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002860 if (idx < augroups.ga_len) // First list group names, if wanted
2861 {
2862 if (!include_groups || AUGROUP_NAME(idx) == NULL
2863 || AUGROUP_NAME(idx) == get_deleted_augroup())
2864 return (char_u *)""; // skip deleted entries
2865 return AUGROUP_NAME(idx); // return a name
2866 }
John Marriott78d742a2024-04-02 20:26:01 +02002867
2868 i = idx - augroups.ga_len;
2869 if (i < 0 || i >= (int)ARRAY_LENGTH(event_tab))
2870 return NULL;
2871
2872 return (char_u *)event_tab[i].value;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002873}
2874
Yee Cheng Chin900894b2023-09-29 20:42:32 +02002875/*
2876 * Function given to ExpandGeneric() to obtain the list of event names. Don't
2877 * include groups.
2878 */
2879 char_u *
2880get_event_name_no_group(expand_T *xp UNUSED, int idx)
2881{
John Marriott78d742a2024-04-02 20:26:01 +02002882 if (idx < 0 || idx >= (int)ARRAY_LENGTH(event_tab))
2883 return NULL;
2884
2885 return (char_u *)event_tab[idx].value;
Yee Cheng Chin900894b2023-09-29 20:42:32 +02002886}
2887
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002888
2889#if defined(FEAT_EVAL) || defined(PROTO)
2890/*
2891 * Return TRUE if autocmd is supported.
2892 */
2893 int
2894autocmd_supported(char_u *name)
2895{
2896 char_u *p;
2897
2898 return (event_name2nr(name, &p) != NUM_EVENTS);
2899}
2900
2901/*
2902 * Return TRUE if an autocommand is defined for a group, event and
2903 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2904 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2905 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2906 * Used for:
2907 * exists("#Group") or
2908 * exists("#Group#Event") or
2909 * exists("#Group#Event#pat") or
2910 * exists("#Event") or
2911 * exists("#Event#pat")
2912 */
2913 int
2914au_exists(char_u *arg)
2915{
2916 char_u *arg_save;
2917 char_u *pattern = NULL;
2918 char_u *event_name;
2919 char_u *p;
2920 event_T event;
2921 AutoPat *ap;
2922 buf_T *buflocal_buf = NULL;
2923 int group;
2924 int retval = FALSE;
2925
2926 // Make a copy so that we can change the '#' chars to a NUL.
2927 arg_save = vim_strsave(arg);
2928 if (arg_save == NULL)
2929 return FALSE;
2930 p = vim_strchr(arg_save, '#');
2931 if (p != NULL)
2932 *p++ = NUL;
2933
2934 // First, look for an autocmd group name
2935 group = au_find_group(arg_save);
2936 if (group == AUGROUP_ERROR)
2937 {
2938 // Didn't match a group name, assume the first argument is an event.
2939 group = AUGROUP_ALL;
2940 event_name = arg_save;
2941 }
2942 else
2943 {
2944 if (p == NULL)
2945 {
2946 // "Group": group name is present and it's recognized
2947 retval = TRUE;
2948 goto theend;
2949 }
2950
2951 // Must be "Group#Event" or "Group#Event#pat".
2952 event_name = p;
2953 p = vim_strchr(event_name, '#');
2954 if (p != NULL)
2955 *p++ = NUL; // "Group#Event#pat"
2956 }
2957
2958 pattern = p; // "pattern" is NULL when there is no pattern
2959
2960 // find the index (enum) for the event name
2961 event = event_name2nr(event_name, &p);
2962
2963 // return FALSE if the event name is not recognized
2964 if (event == NUM_EVENTS)
2965 goto theend;
2966
2967 // Find the first autocommand for this event.
2968 // If there isn't any, return FALSE;
2969 // If there is one and no pattern given, return TRUE;
2970 ap = first_autopat[(int)event];
2971 if (ap == NULL)
2972 goto theend;
2973
2974 // if pattern is "<buffer>", special handling is needed which uses curbuf
2975 // for pattern "<buffer=N>, fnamecmp() will work fine
2976 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2977 buflocal_buf = curbuf;
2978
2979 // Check if there is an autocommand with the given pattern.
2980 for ( ; ap != NULL; ap = ap->next)
2981 // only use a pattern when it has not been removed and has commands.
2982 // For buffer-local autocommands, fnamecmp() works fine.
2983 if (ap->pat != NULL && ap->cmds != NULL
2984 && (group == AUGROUP_ALL || ap->group == group)
2985 && (pattern == NULL
2986 || (buflocal_buf == NULL
2987 ? fnamecmp(ap->pat, pattern) == 0
2988 : ap->buflocal_nr == buflocal_buf->b_fnum)))
2989 {
2990 retval = TRUE;
2991 break;
2992 }
2993
2994theend:
2995 vim_free(arg_save);
2996 return retval;
2997}
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002998
2999/*
3000 * autocmd_add() and autocmd_delete() functions
3001 */
3002 static void
3003autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
3004{
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003005 list_T *aucmd_list;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003006 listitem_T *li;
3007 dict_T *event_dict;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003008 dictitem_T *di;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003009 char_u *event_name = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003010 list_T *event_list;
3011 listitem_T *eli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003012 event_T event;
3013 char_u *group_name = NULL;
3014 int group;
3015 char_u *pat = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003016 list_T *pat_list;
3017 listitem_T *pli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003018 char_u *cmd = NULL;
3019 char_u *end;
3020 int once;
3021 int nested;
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01003022 int replace; // replace the cmd for a group/event
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003023 int retval = VVAL_TRUE;
3024 int save_augroup = current_augroup;
3025
3026 rettv->v_type = VAR_BOOL;
3027 rettv->vval.v_number = VVAL_FALSE;
3028
3029 if (check_for_list_arg(argvars, 0) == FAIL)
3030 return;
3031
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003032 aucmd_list = argvars[0].vval.v_list;
3033 if (aucmd_list == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003034 return;
3035
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003036 FOR_ALL_LIST_ITEMS(aucmd_list, li)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003037 {
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003038 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003039 VIM_CLEAR(cmd);
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003040 event_name = NULL;
3041 event_list = NULL;
3042 pat = NULL;
3043 pat_list = NULL;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003044
3045 if (li->li_tv.v_type != VAR_DICT)
3046 continue;
3047
3048 event_dict = li->li_tv.vval.v_dict;
3049 if (event_dict == NULL)
3050 continue;
3051
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003052 di = dict_find(event_dict, (char_u *)"event", -1);
3053 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003054 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003055 if (di->di_tv.v_type == VAR_STRING)
3056 {
3057 event_name = di->di_tv.vval.v_string;
3058 if (event_name == NULL)
3059 {
3060 emsg(_(e_string_required));
3061 continue;
3062 }
3063 }
3064 else if (di->di_tv.v_type == VAR_LIST)
3065 {
3066 event_list = di->di_tv.vval.v_list;
3067 if (event_list == NULL)
3068 {
3069 emsg(_(e_list_required));
3070 continue;
3071 }
3072 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003073 else
3074 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003075 emsg(_(e_string_or_list_expected));
3076 continue;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003077 }
3078 }
3079
Bram Moolenaard61efa52022-07-23 09:52:04 +01003080 group_name = dict_get_string(event_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003081 if (group_name == NULL || *group_name == NUL)
3082 // if the autocmd group name is not specified, then use the current
3083 // autocmd group
3084 group = current_augroup;
3085 else
3086 {
3087 group = au_find_group(group_name);
3088 if (group == AUGROUP_ERROR)
3089 {
3090 if (delete)
3091 {
3092 semsg(_(e_no_such_group_str), group_name);
3093 retval = VVAL_FALSE;
3094 break;
3095 }
3096 // group is not found, create it now
3097 group = au_new_group(group_name);
3098 if (group == AUGROUP_ERROR)
3099 {
3100 semsg(_(e_no_such_group_str), group_name);
3101 retval = VVAL_FALSE;
3102 break;
3103 }
3104
3105 current_augroup = group;
3106 }
3107 }
3108
3109 // if a buffer number is specified, then generate a pattern of the form
3110 // "<buffer=n>. Otherwise, use the pattern supplied by the user.
3111 if (dict_has_key(event_dict, "bufnr"))
3112 {
3113 varnumber_T bnum;
3114
Bram Moolenaard61efa52022-07-23 09:52:04 +01003115 bnum = dict_get_number_def(event_dict, "bufnr", -1);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003116 if (bnum == -1)
3117 continue;
3118
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003119 vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
3120 pat = IObuff;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003121 }
3122 else
3123 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003124 di = dict_find(event_dict, (char_u *)"pattern", -1);
3125 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003126 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003127 if (di->di_tv.v_type == VAR_STRING)
3128 {
3129 pat = di->di_tv.vval.v_string;
3130 if (pat == NULL)
3131 {
3132 emsg(_(e_string_required));
3133 continue;
3134 }
3135 }
3136 else if (di->di_tv.v_type == VAR_LIST)
3137 {
3138 pat_list = di->di_tv.vval.v_list;
3139 if (pat_list == NULL)
3140 {
3141 emsg(_(e_list_required));
3142 continue;
3143 }
3144 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003145 else
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003146 {
3147 emsg(_(e_string_or_list_expected));
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003148 continue;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003149 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003150 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003151 else if (delete)
3152 pat = (char_u *)"";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003153 }
3154
Bram Moolenaard61efa52022-07-23 09:52:04 +01003155 once = dict_get_bool(event_dict, "once", FALSE);
3156 nested = dict_get_bool(event_dict, "nested", FALSE);
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01003157 // if 'replace' is true, then remove all the commands associated with
3158 // this autocmd event/group and add the new command.
Bram Moolenaard61efa52022-07-23 09:52:04 +01003159 replace = dict_get_bool(event_dict, "replace", FALSE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003160
Bram Moolenaard61efa52022-07-23 09:52:04 +01003161 cmd = dict_get_string(event_dict, "cmd", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003162 if (cmd == NULL)
3163 {
3164 if (delete)
3165 cmd = vim_strsave((char_u *)"");
3166 else
3167 continue;
3168 }
3169
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003170 if (delete && (event_name == NULL
3171 || (event_name[0] == '*' && event_name[1] == NUL)))
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003172 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003173 // if the event name is not specified or '*', delete all the events
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003174 for (event = (event_T)0; (int)event < NUM_EVENTS;
3175 event = (event_T)((int)event + 1))
3176 {
3177 if (do_autocmd_event(event, pat, once, nested, cmd, delete,
3178 group, 0) == FAIL)
3179 {
3180 retval = VVAL_FALSE;
3181 break;
3182 }
3183 }
3184 }
3185 else
3186 {
Bram Moolenaar968443e2022-05-27 21:16:34 +01003187 char_u *p = NULL;
3188
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003189 eli = NULL;
3190 end = NULL;
3191 while (TRUE)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003192 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003193 if (event_list != NULL)
3194 {
3195 if (eli == NULL)
3196 eli = event_list->lv_first;
3197 else
3198 eli = eli->li_next;
3199 if (eli == NULL)
3200 break;
3201 if (eli->li_tv.v_type != VAR_STRING
Bram Moolenaar882476a2022-06-01 16:02:38 +01003202 || (p = eli->li_tv.vval.v_string) == NULL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003203 {
3204 emsg(_(e_string_required));
Bram Moolenaar882476a2022-06-01 16:02:38 +01003205 break;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003206 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003207 }
3208 else
3209 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003210 if (p == NULL)
3211 p = event_name;
3212 if (p == NULL || *p == NUL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003213 break;
3214 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003215
3216 event = event_name2nr(p, &end);
3217 if (event == NUM_EVENTS || *end != NUL)
3218 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003219 // this also catches something following a valid event name
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003220 semsg(_(e_no_such_event_str), p);
3221 retval = VVAL_FALSE;
3222 break;
3223 }
3224 if (pat != NULL)
3225 {
3226 if (do_autocmd_event(event, pat, once, nested, cmd,
3227 delete | replace, group, 0) == FAIL)
3228 {
3229 retval = VVAL_FALSE;
3230 break;
3231 }
3232 }
3233 else if (pat_list != NULL)
3234 {
3235 FOR_ALL_LIST_ITEMS(pat_list, pli)
3236 {
3237 if (pli->li_tv.v_type != VAR_STRING
3238 || pli->li_tv.vval.v_string == NULL)
3239 {
3240 emsg(_(e_string_required));
3241 continue;
3242 }
3243 if (do_autocmd_event(event,
3244 pli->li_tv.vval.v_string, once, nested,
3245 cmd, delete | replace, group, 0) ==
3246 FAIL)
3247 {
3248 retval = VVAL_FALSE;
3249 break;
3250 }
3251 }
3252 if (retval == VVAL_FALSE)
3253 break;
3254 }
3255 if (event_name != NULL)
3256 p = end;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003257 }
3258 }
3259
3260 // if only the autocmd group name is specified for delete and the
3261 // autocmd event, pattern and cmd are not specified, then delete the
3262 // autocmd group.
3263 if (delete && group_name != NULL &&
3264 (event_name == NULL || event_name[0] == NUL)
3265 && (pat == NULL || pat[0] == NUL)
3266 && (cmd == NULL || cmd[0] == NUL))
3267 au_del_group(group_name);
3268 }
3269
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003270 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003271 VIM_CLEAR(cmd);
3272
3273 current_augroup = save_augroup;
3274 rettv->vval.v_number = retval;
3275}
3276
3277/*
3278 * autocmd_add() function
3279 */
3280 void
3281f_autocmd_add(typval_T *argvars, typval_T *rettv)
3282{
3283 autocmd_add_or_delete(argvars, rettv, FALSE);
3284}
3285
3286/*
3287 * autocmd_delete() function
3288 */
3289 void
3290f_autocmd_delete(typval_T *argvars, typval_T *rettv)
3291{
3292 autocmd_add_or_delete(argvars, rettv, TRUE);
3293}
3294
3295/*
3296 * autocmd_get() function
3297 * Returns a List of autocmds.
3298 */
3299 void
3300f_autocmd_get(typval_T *argvars, typval_T *rettv)
3301{
3302 event_T event_arg = NUM_EVENTS;
3303 event_T event;
3304 AutoPat *ap;
3305 AutoCmd *ac;
3306 list_T *event_list;
3307 dict_T *event_dict;
3308 char_u *event_name = NULL;
3309 char_u *pat = NULL;
3310 char_u *name = NULL;
3311 int group = AUGROUP_ALL;
3312
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01003313 if (rettv_list_alloc(rettv) == FAIL)
3314 return;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003315 if (check_for_opt_dict_arg(argvars, 0) == FAIL)
3316 return;
3317
3318 if (argvars[0].v_type == VAR_DICT)
3319 {
3320 // return only the autocmds in the specified group
3321 if (dict_has_key(argvars[0].vval.v_dict, "group"))
3322 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003323 name = dict_get_string(argvars[0].vval.v_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003324 if (name == NULL)
3325 return;
3326
3327 if (*name == NUL)
3328 group = AUGROUP_DEFAULT;
3329 else
3330 {
3331 group = au_find_group(name);
3332 if (group == AUGROUP_ERROR)
3333 {
3334 semsg(_(e_no_such_group_str), name);
3335 vim_free(name);
3336 return;
3337 }
3338 }
3339 vim_free(name);
3340 }
3341
3342 // return only the autocmds for the specified event
3343 if (dict_has_key(argvars[0].vval.v_dict, "event"))
3344 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003345 name = dict_get_string(argvars[0].vval.v_dict, "event", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003346 if (name == NULL)
3347 return;
3348
3349 if (name[0] == '*' && name[1] == NUL)
3350 event_arg = NUM_EVENTS;
3351 else
3352 {
John Marriott78d742a2024-04-02 20:26:01 +02003353 keyvalue_T target;
3354 keyvalue_T *entry;
3355
3356 target.key = 0;
3357 target.value = (char *)name;
3358 target.length = (int)STRLEN(target.value);
3359 entry = (keyvalue_T *)bsearch(&target, &event_tab, ARRAY_LENGTH(event_tab), sizeof(event_tab[0]), cmp_keyvalue_value_ni);
3360 if (entry == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003361 {
3362 semsg(_(e_no_such_event_str), name);
3363 vim_free(name);
3364 return;
3365 }
John Marriott78d742a2024-04-02 20:26:01 +02003366 event_arg = (event_T)entry->key;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003367 }
3368 vim_free(name);
3369 }
3370
3371 // return only the autocmds for the specified pattern
3372 if (dict_has_key(argvars[0].vval.v_dict, "pattern"))
3373 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003374 pat = dict_get_string(argvars[0].vval.v_dict, "pattern", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003375 if (pat == NULL)
3376 return;
3377 }
3378 }
3379
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003380 event_list = rettv->vval.v_list;
3381
3382 // iterate through all the autocmd events
3383 for (event = (event_T)0; (int)event < NUM_EVENTS;
3384 event = (event_T)((int)event + 1))
3385 {
3386 if (event_arg != NUM_EVENTS && event != event_arg)
3387 continue;
3388
3389 event_name = event_nr2name(event);
3390
3391 // iterate through all the patterns for this autocmd event
3392 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
3393 {
3394 char_u *group_name;
3395
zeertzjq2d1d5c62024-06-09 16:44:33 +02003396 if (ap->pat == NULL) // pattern has been removed
3397 continue;
3398
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003399 if (group != AUGROUP_ALL && group != ap->group)
3400 continue;
3401
3402 if (pat != NULL && STRCMP(pat, ap->pat) != 0)
3403 continue;
3404
3405 group_name = get_augroup_name(NULL, ap->group);
3406
3407 // iterate through all the commands for this pattern and add one
3408 // item for each cmd.
3409 for (ac = ap->cmds; ac != NULL; ac = ac->next)
3410 {
3411 event_dict = dict_alloc();
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003412 if (event_dict == NULL
3413 || list_append_dict(event_list, event_dict) == FAIL)
Christian Brabandt29269a72024-04-16 22:44:31 +02003414 {
3415 vim_free(pat);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003416 return;
Christian Brabandt29269a72024-04-16 22:44:31 +02003417 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003418
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003419 if (dict_add_string(event_dict, "event", event_name) == FAIL
3420 || dict_add_string(event_dict, "group",
3421 group_name == NULL ? (char_u *)""
3422 : group_name) == FAIL
3423 || (ap->buflocal_nr != 0
3424 && (dict_add_number(event_dict, "bufnr",
3425 ap->buflocal_nr) == FAIL))
3426 || dict_add_string(event_dict, "pattern",
3427 ap->pat) == FAIL
3428 || dict_add_string(event_dict, "cmd", ac->cmd) == FAIL
3429 || dict_add_bool(event_dict, "once", ac->once) == FAIL
3430 || dict_add_bool(event_dict, "nested",
3431 ac->nested) == FAIL)
Christian Brabandt29269a72024-04-16 22:44:31 +02003432 {
3433 vim_free(pat);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003434 return;
Christian Brabandt29269a72024-04-16 22:44:31 +02003435 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003436 }
3437 }
3438 }
3439
3440 vim_free(pat);
3441}
3442
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01003443#endif