blob: 8b8f109ea097613dda7600ab8ebd670d4db5d7bf [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
77static struct event_name
78{
79 char *name; // event name
80 event_T event; // event number
81} event_names[] =
82{
83 {"BufAdd", EVENT_BUFADD},
84 {"BufCreate", EVENT_BUFADD},
85 {"BufDelete", EVENT_BUFDELETE},
86 {"BufEnter", EVENT_BUFENTER},
87 {"BufFilePost", EVENT_BUFFILEPOST},
88 {"BufFilePre", EVENT_BUFFILEPRE},
89 {"BufHidden", EVENT_BUFHIDDEN},
90 {"BufLeave", EVENT_BUFLEAVE},
91 {"BufNew", EVENT_BUFNEW},
92 {"BufNewFile", EVENT_BUFNEWFILE},
93 {"BufRead", EVENT_BUFREADPOST},
94 {"BufReadCmd", EVENT_BUFREADCMD},
95 {"BufReadPost", EVENT_BUFREADPOST},
96 {"BufReadPre", EVENT_BUFREADPRE},
97 {"BufUnload", EVENT_BUFUNLOAD},
98 {"BufWinEnter", EVENT_BUFWINENTER},
99 {"BufWinLeave", EVENT_BUFWINLEAVE},
100 {"BufWipeout", EVENT_BUFWIPEOUT},
101 {"BufWrite", EVENT_BUFWRITEPRE},
102 {"BufWritePost", EVENT_BUFWRITEPOST},
103 {"BufWritePre", EVENT_BUFWRITEPRE},
104 {"BufWriteCmd", EVENT_BUFWRITECMD},
105 {"CmdlineChanged", EVENT_CMDLINECHANGED},
106 {"CmdlineEnter", EVENT_CMDLINEENTER},
107 {"CmdlineLeave", EVENT_CMDLINELEAVE},
108 {"CmdwinEnter", EVENT_CMDWINENTER},
109 {"CmdwinLeave", EVENT_CMDWINLEAVE},
110 {"CmdUndefined", EVENT_CMDUNDEFINED},
111 {"ColorScheme", EVENT_COLORSCHEME},
112 {"ColorSchemePre", EVENT_COLORSCHEMEPRE},
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200113 {"CompleteChanged", EVENT_COMPLETECHANGED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100114 {"CompleteDone", EVENT_COMPLETEDONE},
Bram Moolenaar3f169ce2020-01-26 22:43:31 +0100115 {"CompleteDonePre", EVENT_COMPLETEDONEPRE},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100116 {"CursorHold", EVENT_CURSORHOLD},
117 {"CursorHoldI", EVENT_CURSORHOLDI},
118 {"CursorMoved", EVENT_CURSORMOVED},
119 {"CursorMovedI", EVENT_CURSORMOVEDI},
120 {"DiffUpdated", EVENT_DIFFUPDATED},
121 {"DirChanged", EVENT_DIRCHANGED},
Bram Moolenaar28e8f732022-02-09 12:58:20 +0000122 {"DirChangedPre", EVENT_DIRCHANGEDPRE},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100123 {"EncodingChanged", EVENT_ENCODINGCHANGED},
124 {"ExitPre", EVENT_EXITPRE},
125 {"FileEncoding", EVENT_ENCODINGCHANGED},
126 {"FileAppendPost", EVENT_FILEAPPENDPOST},
127 {"FileAppendPre", EVENT_FILEAPPENDPRE},
128 {"FileAppendCmd", EVENT_FILEAPPENDCMD},
129 {"FileChangedShell",EVENT_FILECHANGEDSHELL},
130 {"FileChangedShellPost",EVENT_FILECHANGEDSHELLPOST},
131 {"FileChangedRO", EVENT_FILECHANGEDRO},
132 {"FileReadPost", EVENT_FILEREADPOST},
133 {"FileReadPre", EVENT_FILEREADPRE},
134 {"FileReadCmd", EVENT_FILEREADCMD},
135 {"FileType", EVENT_FILETYPE},
136 {"FileWritePost", EVENT_FILEWRITEPOST},
137 {"FileWritePre", EVENT_FILEWRITEPRE},
138 {"FileWriteCmd", EVENT_FILEWRITECMD},
139 {"FilterReadPost", EVENT_FILTERREADPOST},
140 {"FilterReadPre", EVENT_FILTERREADPRE},
141 {"FilterWritePost", EVENT_FILTERWRITEPOST},
142 {"FilterWritePre", EVENT_FILTERWRITEPRE},
143 {"FocusGained", EVENT_FOCUSGAINED},
144 {"FocusLost", EVENT_FOCUSLOST},
145 {"FuncUndefined", EVENT_FUNCUNDEFINED},
146 {"GUIEnter", EVENT_GUIENTER},
147 {"GUIFailed", EVENT_GUIFAILED},
148 {"InsertChange", EVENT_INSERTCHANGE},
149 {"InsertEnter", EVENT_INSERTENTER},
150 {"InsertLeave", EVENT_INSERTLEAVE},
Bram Moolenaarb53e13a2020-10-21 12:19:53 +0200151 {"InsertLeavePre", EVENT_INSERTLEAVEPRE},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100152 {"InsertCharPre", EVENT_INSERTCHARPRE},
153 {"MenuPopup", EVENT_MENUPOPUP},
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +0200154 {"ModeChanged", EVENT_MODECHANGED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100155 {"OptionSet", EVENT_OPTIONSET},
156 {"QuickFixCmdPost", EVENT_QUICKFIXCMDPOST},
157 {"QuickFixCmdPre", EVENT_QUICKFIXCMDPRE},
158 {"QuitPre", EVENT_QUITPRE},
159 {"RemoteReply", EVENT_REMOTEREPLY},
Bram Moolenaar8aeec402019-09-15 23:02:04 +0200160 {"SafeState", EVENT_SAFESTATE},
Bram Moolenaar69198cb2019-09-16 21:58:13 +0200161 {"SafeStateAgain", EVENT_SAFESTATEAGAIN},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100162 {"SessionLoadPost", EVENT_SESSIONLOADPOST},
163 {"ShellCmdPost", EVENT_SHELLCMDPOST},
164 {"ShellFilterPost", EVENT_SHELLFILTERPOST},
Bram Moolenaarbe5ee862020-06-10 20:56:58 +0200165 {"SigUSR1", EVENT_SIGUSR1},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100166 {"SourceCmd", EVENT_SOURCECMD},
167 {"SourcePre", EVENT_SOURCEPRE},
168 {"SourcePost", EVENT_SOURCEPOST},
169 {"SpellFileMissing",EVENT_SPELLFILEMISSING},
170 {"StdinReadPost", EVENT_STDINREADPOST},
171 {"StdinReadPre", EVENT_STDINREADPRE},
172 {"SwapExists", EVENT_SWAPEXISTS},
173 {"Syntax", EVENT_SYNTAX},
174 {"TabNew", EVENT_TABNEW},
175 {"TabClosed", EVENT_TABCLOSED},
176 {"TabEnter", EVENT_TABENTER},
177 {"TabLeave", EVENT_TABLEAVE},
178 {"TermChanged", EVENT_TERMCHANGED},
179 {"TerminalOpen", EVENT_TERMINALOPEN},
Bram Moolenaar28ed4df2019-10-26 16:21:40 +0200180 {"TerminalWinOpen", EVENT_TERMINALWINOPEN},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100181 {"TermResponse", EVENT_TERMRESPONSE},
Danek Duvalld7d56032024-01-14 20:19:59 +0100182 {"TermResponseAll", EVENT_TERMRESPONSEALL},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100183 {"TextChanged", EVENT_TEXTCHANGED},
184 {"TextChangedI", EVENT_TEXTCHANGEDI},
185 {"TextChangedP", EVENT_TEXTCHANGEDP},
Shougo Matsushita4ccaedf2022-10-15 11:48:00 +0100186 {"TextChangedT", EVENT_TEXTCHANGEDT},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100187 {"User", EVENT_USER},
188 {"VimEnter", EVENT_VIMENTER},
189 {"VimLeave", EVENT_VIMLEAVE},
190 {"VimLeavePre", EVENT_VIMLEAVEPRE},
191 {"WinNew", EVENT_WINNEW},
naohiro ono23beefe2021-11-13 12:38:49 +0000192 {"WinClosed", EVENT_WINCLOSED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100193 {"WinEnter", EVENT_WINENTER},
194 {"WinLeave", EVENT_WINLEAVE},
Bram Moolenaar35fc61c2022-11-22 12:40:50 +0000195 {"WinResized", EVENT_WINRESIZED},
LemonBoy09371822022-04-08 15:18:45 +0100196 {"WinScrolled", EVENT_WINSCROLLED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100197 {"VimResized", EVENT_VIMRESIZED},
198 {"TextYankPost", EVENT_TEXTYANKPOST},
Bram Moolenaar100118c2020-12-11 19:30:34 +0100199 {"VimSuspend", EVENT_VIMSUSPEND},
200 {"VimResume", EVENT_VIMRESUME},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100201 {NULL, (event_T)0}
202};
203
204static AutoPat *first_autopat[NUM_EVENTS] =
205{
206 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
207 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
208 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
209 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
210 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
211 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
212};
213
214static AutoPat *last_autopat[NUM_EVENTS] =
215{
216 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};
223
kylo252ae6f1d82022-02-16 19:24:07 +0000224#define AUGROUP_DEFAULT (-1) // default autocmd group
225#define AUGROUP_ERROR (-2) // erroneous autocmd group
226#define AUGROUP_ALL (-3) // all autocmd groups
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100227
228/*
229 * struct used to keep status while executing autocommands for an event.
230 */
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100231struct AutoPatCmd_S
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100232{
233 AutoPat *curpat; // next AutoPat to examine
234 AutoCmd *nextcmd; // next AutoCmd to execute
235 int group; // group being used
236 char_u *fname; // fname to match with
237 char_u *sfname; // sfname to match with
238 char_u *tail; // tail of fname
239 event_T event; // current event
LemonBoyeca7c602022-04-14 15:39:43 +0100240 sctx_T script_ctx; // script context where it is defined
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100241 int arg_bufnr; // Initially equal to <abuf>, set to zero when
242 // buf is deleted.
LemonBoyeca7c602022-04-14 15:39:43 +0100243 AutoPatCmd_T *next; // chain of active apc-s for auto-invalidation
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100244};
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100245
LemonBoyeca7c602022-04-14 15:39:43 +0100246static AutoPatCmd_T *active_apc_list = NULL; // stack of active autocommands
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100247
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200248// Macro to loop over all the patterns for an autocmd event
249#define FOR_ALL_AUTOCMD_PATTERNS(event, ap) \
250 for ((ap) = first_autopat[(int)(event)]; (ap) != NULL; (ap) = (ap)->next)
251
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100252/*
253 * augroups stores a list of autocmd group names.
254 */
255static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
256#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
Bram Moolenaarc667da52019-11-30 20:52:27 +0100257// use get_deleted_augroup() to get this
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100258static char_u *deleted_augroup = NULL;
259
260/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100261 * The ID of the current group. Group 0 is the default one.
262 */
263static int current_augroup = AUGROUP_DEFAULT;
264
Bram Moolenaarc667da52019-11-30 20:52:27 +0100265static int au_need_clean = FALSE; // need to delete marked patterns
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100266
267static char_u *event_nr2name(event_T event);
268static int au_get_grouparg(char_u **argp);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200269static 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 +0100270static 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 +0100271static void auto_next_pat(AutoPatCmd_T *apc, int stop_at_last);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100272static int au_find_group(char_u *name);
273
274static event_T last_event;
275static int last_group;
Bram Moolenaarc667da52019-11-30 20:52:27 +0100276static int autocmd_blocked = 0; // block all autocmds
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100277
278 static char_u *
279get_deleted_augroup(void)
280{
281 if (deleted_augroup == NULL)
282 deleted_augroup = (char_u *)_("--Deleted--");
283 return deleted_augroup;
284}
285
286/*
287 * Show the autocommands for one AutoPat.
288 */
289 static void
290show_autocmd(AutoPat *ap, event_T event)
291{
292 AutoCmd *ac;
293
294 // Check for "got_int" (here and at various places below), which is set
295 // when "q" has been hit for the "--more--" prompt
296 if (got_int)
297 return;
298 if (ap->pat == NULL) // pattern has been removed
299 return;
300
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000301 // Make sure no info referenced by "ap" is cleared, e.g. when a timer
302 // clears an augroup. Jump to "theend" after this!
303 // "ap->pat" may be cleared anyway.
304 ++autocmd_busy;
305
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100306 msg_putchar('\n');
307 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000308 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100309 if (event != last_event || ap->group != last_group)
310 {
311 if (ap->group != AUGROUP_DEFAULT)
312 {
313 if (AUGROUP_NAME(ap->group) == NULL)
314 msg_puts_attr((char *)get_deleted_augroup(), HL_ATTR(HLF_E));
315 else
316 msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
317 msg_puts(" ");
318 }
319 msg_puts_attr((char *)event_nr2name(event), HL_ATTR(HLF_T));
320 last_event = event;
321 last_group = ap->group;
322 msg_putchar('\n');
323 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000324 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100325 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000326
327 if (ap->pat == NULL)
328 goto theend; // timer might have cleared the pattern or group
329
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100330 msg_col = 4;
331 msg_outtrans(ap->pat);
332
333 for (ac = ap->cmds; ac != NULL; ac = ac->next)
334 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100335 if (ac->cmd == NULL) // skip removed commands
336 continue;
337
338 if (msg_col >= 14)
339 msg_putchar('\n');
340 msg_col = 14;
341 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000342 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100343 msg_outtrans(ac->cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100344#ifdef FEAT_EVAL
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100345 if (p_verbose > 0)
346 last_set_msg(ac->script_ctx);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100347#endif
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100348 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000349 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100350 if (ac->next != NULL)
351 {
352 msg_putchar('\n');
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100353 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000354 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100355 }
356 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000357
358theend:
359 --autocmd_busy;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100360}
361
362/*
363 * Mark an autocommand pattern for deletion.
364 */
365 static void
366au_remove_pat(AutoPat *ap)
367{
368 VIM_CLEAR(ap->pat);
369 ap->buflocal_nr = -1;
370 au_need_clean = TRUE;
371}
372
373/*
374 * Mark all commands for a pattern for deletion.
375 */
376 static void
377au_remove_cmds(AutoPat *ap)
378{
379 AutoCmd *ac;
380
381 for (ac = ap->cmds; ac != NULL; ac = ac->next)
382 VIM_CLEAR(ac->cmd);
383 au_need_clean = TRUE;
384}
385
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200386// Delete one command from an autocmd pattern.
387static void au_del_cmd(AutoCmd *ac)
388{
389 VIM_CLEAR(ac->cmd);
390 au_need_clean = TRUE;
391}
392
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100393/*
394 * Cleanup autocommands and patterns that have been deleted.
395 * This is only done when not executing autocommands.
396 */
397 static void
398au_cleanup(void)
399{
400 AutoPat *ap, **prev_ap;
401 AutoCmd *ac, **prev_ac;
402 event_T event;
403
404 if (autocmd_busy || !au_need_clean)
405 return;
406
407 // loop over all events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100408 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100409 event = (event_T)((int)event + 1))
410 {
411 // loop over all autocommand patterns
412 prev_ap = &(first_autopat[(int)event]);
413 for (ap = *prev_ap; ap != NULL; ap = *prev_ap)
414 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200415 int has_cmd = FALSE;
416
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200417 // loop over all commands for this pattern
418 prev_ac = &(ap->cmds);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100419 for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
420 {
421 // remove the command if the pattern is to be deleted or when
422 // the command has been marked for deletion
423 if (ap->pat == NULL || ac->cmd == NULL)
424 {
425 *prev_ac = ac->next;
426 vim_free(ac->cmd);
427 vim_free(ac);
428 }
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200429 else
430 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200431 has_cmd = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100432 prev_ac = &(ac->next);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200433 }
434 }
435
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200436 if (ap->pat != NULL && !has_cmd)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200437 // Pattern was not marked for deletion, but all of its
438 // commands were. So mark the pattern for deletion.
439 au_remove_pat(ap);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100440
441 // remove the pattern if it has been marked for deletion
442 if (ap->pat == NULL)
443 {
444 if (ap->next == NULL)
445 {
446 if (prev_ap == &(first_autopat[(int)event]))
447 last_autopat[(int)event] = NULL;
448 else
449 // this depends on the "next" field being the first in
450 // the struct
451 last_autopat[(int)event] = (AutoPat *)prev_ap;
452 }
453 *prev_ap = ap->next;
454 vim_regfree(ap->reg_prog);
455 vim_free(ap);
456 }
457 else
458 prev_ap = &(ap->next);
459 }
460 }
461
462 au_need_clean = FALSE;
463}
464
465/*
466 * Called when buffer is freed, to remove/invalidate related buffer-local
467 * autocmds.
468 */
469 void
470aubuflocal_remove(buf_T *buf)
471{
LemonBoyeca7c602022-04-14 15:39:43 +0100472 AutoPat *ap;
473 event_T event;
474 AutoPatCmd_T *apc;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100475
476 // invalidate currently executing autocommands
477 for (apc = active_apc_list; apc; apc = apc->next)
478 if (buf->b_fnum == apc->arg_bufnr)
479 apc->arg_bufnr = 0;
480
481 // invalidate buflocals looping through events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100482 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100483 event = (event_T)((int)event + 1))
484 // loop over all autocommand patterns
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200485 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100486 if (ap->buflocal_nr == buf->b_fnum)
487 {
488 au_remove_pat(ap);
489 if (p_verbose >= 6)
490 {
491 verbose_enter();
492 smsg(_("auto-removing autocommand: %s <buffer=%d>"),
493 event_nr2name(event), buf->b_fnum);
494 verbose_leave();
495 }
496 }
497 au_cleanup();
498}
499
500/*
501 * Add an autocmd group name.
502 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
503 */
504 static int
505au_new_group(char_u *name)
506{
507 int i;
508
509 i = au_find_group(name);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100510 if (i != AUGROUP_ERROR)
511 return i;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100512
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100513 // the group doesn't exist yet, add it. First try using a free entry.
514 for (i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100515 if (AUGROUP_NAME(i) == NULL)
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100516 break;
517 if (i == augroups.ga_len && ga_grow(&augroups, 1) == FAIL)
518 return AUGROUP_ERROR;
519
520 AUGROUP_NAME(i) = vim_strsave(name);
521 if (AUGROUP_NAME(i) == NULL)
522 return AUGROUP_ERROR;
523 if (i == augroups.ga_len)
524 ++augroups.ga_len;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100525
526 return i;
527}
528
529 static void
530au_del_group(char_u *name)
531{
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100532 int i;
533 event_T event;
534 AutoPat *ap;
535 int in_use = FALSE;
536
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100537
538 i = au_find_group(name);
539 if (i == AUGROUP_ERROR) // the group doesn't exist
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100540 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100541 semsg(_(e_no_such_group_str), name);
542 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100543 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100544 if (i == current_augroup)
545 {
546 emsg(_(e_cannot_delete_current_group));
547 return;
548 }
549
550 for (event = (event_T)0; (int)event < NUM_EVENTS;
551 event = (event_T)((int)event + 1))
552 {
553 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
554 if (ap->group == i && ap->pat != NULL)
555 {
556 give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
557 in_use = TRUE;
558 event = NUM_EVENTS;
559 break;
560 }
561 }
562 vim_free(AUGROUP_NAME(i));
563 if (in_use)
564 AUGROUP_NAME(i) = get_deleted_augroup();
565 else
566 AUGROUP_NAME(i) = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100567}
568
569/*
570 * Find the ID of an autocmd group name.
571 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
572 */
573 static int
574au_find_group(char_u *name)
575{
576 int i;
577
578 for (i = 0; i < augroups.ga_len; ++i)
579 if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
580 && STRCMP(AUGROUP_NAME(i), name) == 0)
581 return i;
582 return AUGROUP_ERROR;
583}
584
585/*
586 * Return TRUE if augroup "name" exists.
587 */
588 int
589au_has_group(char_u *name)
590{
591 return au_find_group(name) != AUGROUP_ERROR;
592}
593
594/*
595 * ":augroup {name}".
596 */
597 void
598do_augroup(char_u *arg, int del_group)
599{
600 int i;
601
602 if (del_group)
603 {
604 if (*arg == NUL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000605 emsg(_(e_argument_required));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100606 else
607 au_del_group(arg);
608 }
609 else if (STRICMP(arg, "end") == 0) // ":aug end": back to group 0
610 current_augroup = AUGROUP_DEFAULT;
611 else if (*arg) // ":aug xxx": switch to group xxx
612 {
613 i = au_new_group(arg);
614 if (i != AUGROUP_ERROR)
615 current_augroup = i;
616 }
617 else // ":aug": list the group names
618 {
619 msg_start();
620 for (i = 0; i < augroups.ga_len; ++i)
621 {
622 if (AUGROUP_NAME(i) != NULL)
623 {
624 msg_puts((char *)AUGROUP_NAME(i));
625 msg_puts(" ");
626 }
627 }
628 msg_clr_eos();
629 msg_end();
630 }
631}
632
Bram Moolenaare76062c2022-11-28 18:51:43 +0000633 void
634autocmd_init(void)
635{
636 CLEAR_FIELD(aucmd_win);
637}
638
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100639#if defined(EXITFREE) || defined(PROTO)
640 void
641free_all_autocmds(void)
642{
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100643 char_u *s;
644
645 for (current_augroup = -1; current_augroup < augroups.ga_len;
646 ++current_augroup)
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200647 do_autocmd(NULL, (char_u *)"", TRUE);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100648
Bram Moolenaare76062c2022-11-28 18:51:43 +0000649 for (int i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100650 {
651 s = ((char_u **)(augroups.ga_data))[i];
652 if (s != get_deleted_augroup())
653 vim_free(s);
654 }
655 ga_clear(&augroups);
Bram Moolenaare76062c2022-11-28 18:51:43 +0000656
Bram Moolenaar84497cd2022-11-28 20:34:52 +0000657 // aucmd_win[] is freed in win_free_all()
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100658}
659#endif
660
661/*
Bram Moolenaare76062c2022-11-28 18:51:43 +0000662 * Return TRUE if "win" is an active entry in aucmd_win[].
663 */
664 int
665is_aucmd_win(win_T *win)
666{
667 for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
668 if (aucmd_win[i].auc_win_used && aucmd_win[i].auc_win == win)
669 return TRUE;
670 return FALSE;
671}
672
673/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100674 * Return the event number for event name "start".
675 * Return NUM_EVENTS if the event name was not found.
676 * Return a pointer to the next event name in "end".
677 */
678 static event_T
679event_name2nr(char_u *start, char_u **end)
680{
681 char_u *p;
682 int i;
683 int len;
684
685 // the event name ends with end of line, '|', a blank or a comma
686 for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
687 ;
688 for (i = 0; event_names[i].name != NULL; ++i)
689 {
690 len = (int)STRLEN(event_names[i].name);
691 if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0)
692 break;
693 }
694 if (*p == ',')
695 ++p;
696 *end = p;
697 if (event_names[i].name == NULL)
698 return NUM_EVENTS;
699 return event_names[i].event;
700}
701
702/*
703 * Return the name for event "event".
704 */
705 static char_u *
706event_nr2name(event_T event)
707{
708 int i;
709
710 for (i = 0; event_names[i].name != NULL; ++i)
711 if (event_names[i].event == event)
712 return (char_u *)event_names[i].name;
713 return (char_u *)"Unknown";
714}
715
716/*
717 * Scan over the events. "*" stands for all events.
718 */
719 static char_u *
720find_end_event(
721 char_u *arg,
722 int have_group) // TRUE when group name was found
723{
724 char_u *pat;
725 char_u *p;
726
727 if (*arg == '*')
728 {
729 if (arg[1] && !VIM_ISWHITE(arg[1]))
730 {
Bram Moolenaar6d057012021-12-31 18:49:43 +0000731 semsg(_(e_illegal_character_after_star_str), arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100732 return NULL;
733 }
734 pat = arg + 1;
735 }
736 else
737 {
738 for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p)
739 {
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100740 if ((int)event_name2nr(pat, &p) >= NUM_EVENTS)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100741 {
742 if (have_group)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000743 semsg(_(e_no_such_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100744 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000745 semsg(_(e_no_such_group_or_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100746 return NULL;
747 }
748 }
749 }
750 return pat;
751}
752
753/*
754 * Return TRUE if "event" is included in 'eventignore'.
755 */
756 static int
757event_ignored(event_T event)
758{
759 char_u *p = p_ei;
760
761 while (*p != NUL)
762 {
763 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
764 return TRUE;
765 if (event_name2nr(p, &p) == event)
766 return TRUE;
767 }
768
769 return FALSE;
770}
771
772/*
773 * Return OK when the contents of p_ei is valid, FAIL otherwise.
774 */
775 int
776check_ei(void)
777{
778 char_u *p = p_ei;
779
780 while (*p)
781 {
782 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
783 {
784 p += 3;
785 if (*p == ',')
786 ++p;
787 }
788 else if (event_name2nr(p, &p) == NUM_EVENTS)
789 return FAIL;
790 }
791
792 return OK;
793}
794
795# if defined(FEAT_SYN_HL) || defined(PROTO)
796
797/*
798 * Add "what" to 'eventignore' to skip loading syntax highlighting for every
799 * buffer loaded into the window. "what" must start with a comma.
800 * Returns the old value of 'eventignore' in allocated memory.
801 */
802 char_u *
803au_event_disable(char *what)
804{
805 char_u *new_ei;
806 char_u *save_ei;
807
808 save_ei = vim_strsave(p_ei);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100809 if (save_ei == NULL)
810 return NULL;
811
812 new_ei = vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what));
813 if (new_ei == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100814 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100815 vim_free(save_ei);
816 return NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100817 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100818
819 if (*what == ',' && *p_ei == NUL)
820 STRCPY(new_ei, what + 1);
821 else
822 STRCAT(new_ei, what);
823 set_string_option_direct((char_u *)"ei", -1, new_ei,
824 OPT_FREE, SID_NONE);
825 vim_free(new_ei);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100826 return save_ei;
827}
828
829 void
830au_event_restore(char_u *old_ei)
831{
832 if (old_ei != NULL)
833 {
834 set_string_option_direct((char_u *)"ei", -1, old_ei,
835 OPT_FREE, SID_NONE);
836 vim_free(old_ei);
837 }
838}
Bram Moolenaarc667da52019-11-30 20:52:27 +0100839# endif // FEAT_SYN_HL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100840
841/*
842 * do_autocmd() -- implements the :autocmd command. Can be used in the
843 * following ways:
844 *
845 * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
846 * will be automatically executed for <event>
847 * when editing a file matching <pat>, in
848 * the current group.
849 * :autocmd <event> <pat> Show the autocommands associated with
850 * <event> and <pat>.
851 * :autocmd <event> Show the autocommands associated with
852 * <event>.
853 * :autocmd Show all autocommands.
854 * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
855 * <event> and <pat>, and add the command
856 * <cmd>, for the current group.
857 * :autocmd! <event> <pat> Remove all autocommands associated with
858 * <event> and <pat> for the current group.
859 * :autocmd! <event> Remove all autocommands associated with
860 * <event> for the current group.
861 * :autocmd! Remove ALL autocommands for the current
862 * group.
863 *
864 * Multiple events and patterns may be given separated by commas. Here are
865 * some examples:
866 * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
867 * :autocmd bufleave * set tw=79 nosmartindent ic infercase
868 *
869 * :autocmd * *.c show all autocommands for *.c files.
870 *
871 * Mostly a {group} argument can optionally appear before <event>.
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200872 * "eap" can be NULL.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100873 */
874 void
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200875do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100876{
877 char_u *arg = arg_in;
878 char_u *pat;
879 char_u *envpat = NULL;
880 char_u *cmd;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200881 int cmd_need_free = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100882 event_T event;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200883 char_u *tofree = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100884 int nested = FALSE;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200885 int once = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100886 int group;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200887 int i;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200888 int flags = 0;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100889
890 if (*arg == '|')
891 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000892 eap->nextcmd = arg + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100893 arg = (char_u *)"";
894 group = AUGROUP_ALL; // no argument, use all groups
895 }
896 else
897 {
898 /*
899 * Check for a legal group name. If not, use AUGROUP_ALL.
900 */
901 group = au_get_grouparg(&arg);
902 if (arg == NULL) // out of memory
903 return;
904 }
905
906 /*
907 * Scan over the events.
908 * If we find an illegal name, return here, don't do anything.
909 */
910 pat = find_end_event(arg, group != AUGROUP_ALL);
911 if (pat == NULL)
912 return;
913
914 pat = skipwhite(pat);
915 if (*pat == '|')
916 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000917 eap->nextcmd = pat + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100918 pat = (char_u *)"";
919 cmd = (char_u *)"";
920 }
921 else
922 {
923 /*
924 * Scan over the pattern. Put a NUL at the end.
925 */
926 cmd = pat;
927 while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
928 cmd++;
929 if (*cmd)
930 *cmd++ = NUL;
931
932 // Expand environment variables in the pattern. Set 'shellslash', we
933 // want forward slashes here.
934 if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
935 {
936#ifdef BACKSLASH_IN_FILENAME
937 int p_ssl_save = p_ssl;
938
939 p_ssl = TRUE;
940#endif
941 envpat = expand_env_save(pat);
942#ifdef BACKSLASH_IN_FILENAME
943 p_ssl = p_ssl_save;
944#endif
945 if (envpat != NULL)
946 pat = envpat;
947 }
948
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100949 cmd = skipwhite(cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200950 for (i = 0; i < 2; i++)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100951 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100952 if (*cmd == NUL)
953 continue;
954
955 // Check for "++once" flag.
956 if (STRNCMP(cmd, "++once", 6) == 0 && VIM_ISWHITE(cmd[6]))
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200957 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100958 if (once)
959 semsg(_(e_duplicate_argument_str), "++once");
960 once = TRUE;
961 cmd = skipwhite(cmd + 6);
962 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200963
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100964 // Check for "++nested" flag.
965 if ((STRNCMP(cmd, "++nested", 8) == 0 && VIM_ISWHITE(cmd[8])))
966 {
967 if (nested)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200968 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100969 semsg(_(e_duplicate_argument_str), "++nested");
970 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200971 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100972 nested = TRUE;
973 cmd = skipwhite(cmd + 8);
974 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200975
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100976 // Check for the old "nested" flag in legacy script.
977 if (STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
978 {
979 if (in_vim9script())
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200980 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100981 // If there ever is a :nested command this error should
982 // be removed and "nested" accepted as the start of the
983 // command.
984 emsg(_(e_invalid_command_nested_did_you_mean_plusplus_nested));
985 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200986 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100987 if (nested)
988 {
989 semsg(_(e_duplicate_argument_str), "nested");
990 return;
991 }
992 nested = TRUE;
993 cmd = skipwhite(cmd + 6);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200994 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100995 }
996
997 /*
998 * Find the start of the commands.
999 * Expand <sfile> in it.
1000 */
1001 if (*cmd != NUL)
1002 {
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001003 if (eap != NULL)
1004 // Read a {} block if it follows.
1005 cmd = may_get_cmd_block(eap, cmd, &tofree, &flags);
1006
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001007 cmd = expand_sfile(cmd);
1008 if (cmd == NULL) // some error
1009 return;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001010 cmd_need_free = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001011 }
1012 }
1013
1014 /*
1015 * Print header when showing autocommands.
1016 */
1017 if (!forceit && *cmd == NUL)
1018 // Highlight title
1019 msg_puts_title(_("\n--- Autocommands ---"));
1020
1021 /*
1022 * Loop over the events.
1023 */
1024 last_event = (event_T)-1; // for listing the event name
1025 last_group = AUGROUP_ERROR; // for listing the group name
1026 if (*arg == '*' || *arg == NUL || *arg == '|')
1027 {
Bram Moolenaarb6db1462021-12-24 19:24:47 +00001028 if (*cmd != NUL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001029 emsg(_(e_cannot_define_autocommands_for_all_events));
1030 else
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +01001031 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001032 event = (event_T)((int)event + 1))
1033 if (do_autocmd_event(event, pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001034 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001035 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001036 }
1037 else
1038 {
1039 while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
1040 if (do_autocmd_event(event_name2nr(arg, &arg), pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001041 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001042 break;
1043 }
1044
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001045 if (cmd_need_free)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001046 vim_free(cmd);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001047 vim_free(tofree);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001048 vim_free(envpat);
1049}
1050
1051/*
1052 * Find the group ID in a ":autocmd" or ":doautocmd" argument.
1053 * The "argp" argument is advanced to the following argument.
1054 *
1055 * Returns the group ID, AUGROUP_ERROR for error (out of memory).
1056 */
1057 static int
1058au_get_grouparg(char_u **argp)
1059{
1060 char_u *group_name;
1061 char_u *p;
1062 char_u *arg = *argp;
1063 int group = AUGROUP_ALL;
1064
1065 for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p)
1066 ;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001067 if (p <= arg)
1068 return AUGROUP_ALL;
1069
1070 group_name = vim_strnsave(arg, p - arg);
1071 if (group_name == NULL) // out of memory
1072 return AUGROUP_ERROR;
1073 group = au_find_group(group_name);
1074 if (group == AUGROUP_ERROR)
1075 group = AUGROUP_ALL; // no match, use all groups
1076 else
1077 *argp = skipwhite(p); // match, skip over group name
1078 vim_free(group_name);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001079 return group;
1080}
1081
1082/*
1083 * do_autocmd() for one event.
1084 * If *pat == NUL do for all patterns.
1085 * If *cmd == NUL show entries.
1086 * If forceit == TRUE delete entries.
1087 * If group is not AUGROUP_ALL, only use this group.
1088 */
1089 static int
1090do_autocmd_event(
1091 event_T event,
1092 char_u *pat,
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001093 int once,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001094 int nested,
1095 char_u *cmd,
1096 int forceit,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001097 int group,
1098 int flags)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001099{
1100 AutoPat *ap;
1101 AutoPat **prev_ap;
1102 AutoCmd *ac;
1103 AutoCmd **prev_ac;
1104 int brace_level;
1105 char_u *endpat;
1106 int findgroup;
1107 int allgroups;
1108 int patlen;
1109 int is_buflocal;
1110 int buflocal_nr;
Bram Moolenaarc667da52019-11-30 20:52:27 +01001111 char_u buflocal_pat[25]; // for "<buffer=X>"
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001112
1113 if (group == AUGROUP_ALL)
1114 findgroup = current_augroup;
1115 else
1116 findgroup = group;
1117 allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
1118
1119 /*
1120 * Show or delete all patterns for an event.
1121 */
1122 if (*pat == NUL)
1123 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001124 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001125 {
1126 if (forceit) // delete the AutoPat, if it's in the current group
1127 {
1128 if (ap->group == findgroup)
1129 au_remove_pat(ap);
1130 }
1131 else if (group == AUGROUP_ALL || ap->group == group)
1132 show_autocmd(ap, event);
1133 }
1134 }
1135
1136 /*
1137 * Loop through all the specified patterns.
1138 */
1139 for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
1140 {
1141 /*
1142 * Find end of the pattern.
1143 * Watch out for a comma in braces, like "*.\{obj,o\}".
1144 */
1145 brace_level = 0;
1146 for (endpat = pat; *endpat && (*endpat != ',' || brace_level
1147 || (endpat > pat && endpat[-1] == '\\')); ++endpat)
1148 {
1149 if (*endpat == '{')
1150 brace_level++;
1151 else if (*endpat == '}')
1152 brace_level--;
1153 }
1154 if (pat == endpat) // ignore single comma
1155 continue;
1156 patlen = (int)(endpat - pat);
1157
1158 /*
1159 * detect special <buflocal[=X]> buffer-local patterns
1160 */
1161 is_buflocal = FALSE;
1162 buflocal_nr = 0;
1163
1164 if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
1165 && pat[patlen - 1] == '>')
1166 {
1167 // "<buffer...>": Error will be printed only for addition.
1168 // printing and removing will proceed silently.
1169 is_buflocal = TRUE;
1170 if (patlen == 8)
1171 // "<buffer>"
1172 buflocal_nr = curbuf->b_fnum;
1173 else if (patlen > 9 && pat[7] == '=')
1174 {
1175 if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0)
1176 // "<buffer=abuf>"
1177 buflocal_nr = autocmd_bufnr;
1178 else if (skipdigits(pat + 8) == pat + patlen - 1)
1179 // "<buffer=123>"
1180 buflocal_nr = atoi((char *)pat + 8);
1181 }
1182 }
1183
1184 if (is_buflocal)
1185 {
1186 // normalize pat into standard "<buffer>#N" form
1187 sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
1188 pat = buflocal_pat; // can modify pat and patlen
1189 patlen = (int)STRLEN(buflocal_pat); // but not endpat
1190 }
1191
1192 /*
1193 * Find AutoPat entries with this pattern. When adding a command it
1194 * always goes at or after the last one, so start at the end.
1195 */
1196 if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL)
1197 prev_ap = &last_autopat[(int)event];
1198 else
1199 prev_ap = &first_autopat[(int)event];
1200 while ((ap = *prev_ap) != NULL)
1201 {
1202 if (ap->pat != NULL)
1203 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001204 /*
1205 * Accept a pattern when:
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001206 * - a group was specified and it's that group, or a group was
1207 * not specified and it's the current group, or a group was
1208 * not specified and we are listing
1209 * - the length of the pattern matches
1210 * - the pattern matches.
1211 * For <buffer[=X]>, this condition works because we normalize
1212 * all buffer-local patterns.
1213 */
1214 if ((allgroups || ap->group == findgroup)
1215 && ap->patlen == patlen
1216 && STRNCMP(pat, ap->pat, patlen) == 0)
1217 {
1218 /*
1219 * Remove existing autocommands.
1220 * If adding any new autocmd's for this AutoPat, don't
1221 * delete the pattern from the autopat list, append to
1222 * this list.
1223 */
1224 if (forceit)
1225 {
1226 if (*cmd != NUL && ap->next == NULL)
1227 {
1228 au_remove_cmds(ap);
1229 break;
1230 }
1231 au_remove_pat(ap);
1232 }
1233
1234 /*
1235 * Show autocmd's for this autopat, or buflocals <buffer=X>
1236 */
1237 else if (*cmd == NUL)
1238 show_autocmd(ap, event);
1239
1240 /*
1241 * Add autocmd to this autopat, if it's the last one.
1242 */
1243 else if (ap->next == NULL)
1244 break;
1245 }
1246 }
1247 prev_ap = &ap->next;
1248 }
1249
1250 /*
1251 * Add a new command.
1252 */
1253 if (*cmd != NUL)
1254 {
1255 /*
1256 * If the pattern we want to add a command to does appear at the
1257 * end of the list (or not is not in the list at all), add the
1258 * pattern at the end of the list.
1259 */
1260 if (ap == NULL)
1261 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001262 // refuse to add buffer-local ap if buffer number is invalid
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001263 if (is_buflocal && (buflocal_nr == 0
1264 || buflist_findnr(buflocal_nr) == NULL))
1265 {
Bram Moolenaarf1474d82021-12-31 19:59:55 +00001266 semsg(_(e_buffer_nr_invalid_buffer_number), buflocal_nr);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001267 return FAIL;
1268 }
1269
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001270 ap = ALLOC_ONE(AutoPat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001271 if (ap == NULL)
1272 return FAIL;
1273 ap->pat = vim_strnsave(pat, patlen);
1274 ap->patlen = patlen;
1275 if (ap->pat == NULL)
1276 {
1277 vim_free(ap);
1278 return FAIL;
1279 }
1280
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001281#ifdef FEAT_EVAL
1282 // need to initialize last_mode for the first ModeChanged
1283 // autocmd
1284 if (event == EVENT_MODECHANGED && !has_modechanged())
LemonBoy2bf52dd2022-04-09 18:17:34 +01001285 get_mode(last_mode);
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001286#endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001287 // Initialize the fields checked by the WinScrolled and
1288 // WinResized trigger to prevent them from firing right after
1289 // the first autocmd is defined.
1290 if ((event == EVENT_WINSCROLLED || event == EVENT_WINRESIZED)
1291 && !(has_winscrolled() || has_winresized()))
LemonBoy09371822022-04-08 15:18:45 +01001292 {
Bram Moolenaar29967732022-11-20 12:11:45 +00001293 tabpage_T *save_curtab = curtab;
1294 tabpage_T *tp;
1295 FOR_ALL_TABPAGES(tp)
1296 {
1297 unuse_tabpage(curtab);
1298 use_tabpage(tp);
1299 snapshot_windows_scroll_size();
1300 }
1301 unuse_tabpage(curtab);
1302 use_tabpage(save_curtab);
LemonBoy09371822022-04-08 15:18:45 +01001303 }
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001304
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001305 if (is_buflocal)
1306 {
1307 ap->buflocal_nr = buflocal_nr;
1308 ap->reg_prog = NULL;
1309 }
1310 else
1311 {
1312 char_u *reg_pat;
1313
1314 ap->buflocal_nr = 0;
1315 reg_pat = file_pat_to_reg_pat(pat, endpat,
1316 &ap->allow_dirs, TRUE);
1317 if (reg_pat != NULL)
1318 ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
1319 vim_free(reg_pat);
1320 if (reg_pat == NULL || ap->reg_prog == NULL)
1321 {
1322 vim_free(ap->pat);
1323 vim_free(ap);
1324 return FAIL;
1325 }
1326 }
1327 ap->cmds = NULL;
1328 *prev_ap = ap;
1329 last_autopat[(int)event] = ap;
1330 ap->next = NULL;
1331 if (group == AUGROUP_ALL)
1332 ap->group = current_augroup;
1333 else
1334 ap->group = group;
1335 }
1336
1337 /*
1338 * Add the autocmd at the end of the AutoCmd list.
1339 */
1340 prev_ac = &(ap->cmds);
1341 while ((ac = *prev_ac) != NULL)
1342 prev_ac = &ac->next;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001343 ac = ALLOC_ONE(AutoCmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001344 if (ac == NULL)
1345 return FAIL;
1346 ac->cmd = vim_strsave(cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001347 ac->script_ctx = current_sctx;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001348 if (flags & UC_VIM9)
1349 ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001350#ifdef FEAT_EVAL
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001351 ac->script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001352#endif
1353 if (ac->cmd == NULL)
1354 {
1355 vim_free(ac);
1356 return FAIL;
1357 }
1358 ac->next = NULL;
1359 *prev_ac = ac;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001360 ac->once = once;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001361 ac->nested = nested;
1362 }
1363 }
1364
1365 au_cleanup(); // may really delete removed patterns/commands now
1366 return OK;
1367}
1368
1369/*
1370 * Implementation of ":doautocmd [group] event [fname]".
1371 * Return OK for success, FAIL for failure;
1372 */
1373 int
1374do_doautocmd(
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001375 char_u *arg_start,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001376 int do_msg, // give message for no matching autocmds?
1377 int *did_something)
1378{
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001379 char_u *arg = arg_start;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001380 char_u *fname;
1381 int nothing_done = TRUE;
1382 int group;
1383
1384 if (did_something != NULL)
1385 *did_something = FALSE;
1386
1387 /*
1388 * Check for a legal group name. If not, use AUGROUP_ALL.
1389 */
1390 group = au_get_grouparg(&arg);
1391 if (arg == NULL) // out of memory
1392 return FAIL;
1393
1394 if (*arg == '*')
1395 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001396 emsg(_(e_cant_execute_autocommands_for_all_events));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001397 return FAIL;
1398 }
1399
1400 /*
1401 * Scan over the events.
1402 * If we find an illegal name, return here, don't do anything.
1403 */
1404 fname = find_end_event(arg, group != AUGROUP_ALL);
1405 if (fname == NULL)
1406 return FAIL;
1407
1408 fname = skipwhite(fname);
1409
1410 /*
1411 * Loop over the events.
1412 */
1413 while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
1414 if (apply_autocmds_group(event_name2nr(arg, &arg),
1415 fname, NULL, TRUE, group, curbuf, NULL))
1416 nothing_done = FALSE;
1417
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001418 if (nothing_done && do_msg
1419#ifdef FEAT_EVAL
1420 && !aborting()
1421#endif
1422 )
1423 smsg(_("No matching autocommands: %s"), arg_start);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001424 if (did_something != NULL)
1425 *did_something = !nothing_done;
1426
1427#ifdef FEAT_EVAL
1428 return aborting() ? FAIL : OK;
1429#else
1430 return OK;
1431#endif
1432}
1433
1434/*
1435 * ":doautoall": execute autocommands for each loaded buffer.
1436 */
1437 void
1438ex_doautoall(exarg_T *eap)
1439{
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001440 int retval = OK;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001441 aco_save_T aco;
1442 buf_T *buf;
1443 bufref_T bufref;
1444 char_u *arg = eap->arg;
1445 int call_do_modelines = check_nomodeline(&arg);
1446 int did_aucmd;
1447
1448 /*
1449 * This is a bit tricky: For some commands curwin->w_buffer needs to be
1450 * equal to curbuf, but for some buffers there may not be a window.
1451 * So we change the buffer for the current window for a moment. This
1452 * gives problems when the autocommands make changes to the list of
1453 * buffers or windows...
1454 */
1455 FOR_ALL_BUFFERS(buf)
1456 {
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001457 // Only do loaded buffers and skip the current buffer, it's done last.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001458 if (buf->b_ml.ml_mfp == NULL || buf == curbuf)
1459 continue;
1460
Bram Moolenaare76062c2022-11-28 18:51:43 +00001461 // Find a window for this buffer and save some values.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001462 aucmd_prepbuf(&aco, buf);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001463 if (curbuf != buf)
1464 {
1465 // Failed to find a window for this buffer. Better not execute
1466 // autocommands then.
1467 retval = FAIL;
1468 break;
1469 }
1470
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001471 set_bufref(&bufref, buf);
1472
1473 // execute the autocommands for this buffer
1474 retval = do_doautocmd(arg, FALSE, &did_aucmd);
1475
1476 if (call_do_modelines && did_aucmd)
1477 // Execute the modeline settings, but don't set window-local
1478 // options if we are using the current window for another
1479 // buffer.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001480 do_modelines(is_aucmd_win(curwin) ? OPT_NOWIN : 0);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001481
1482 // restore the current window
1483 aucmd_restbuf(&aco);
1484
1485 // stop if there is some error or buffer was deleted
1486 if (retval == FAIL || !bufref_valid(&bufref))
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001487 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001488 retval = FAIL;
1489 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001490 }
1491 }
1492
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001493 // Execute autocommands for the current buffer last.
1494 if (retval == OK)
1495 {
1496 do_doautocmd(arg, FALSE, &did_aucmd);
1497 if (call_do_modelines && did_aucmd)
1498 do_modelines(0);
1499 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001500}
1501
1502/*
1503 * Check *argp for <nomodeline>. When it is present return FALSE, otherwise
1504 * return TRUE and advance *argp to after it.
1505 * Thus return TRUE when do_modelines() should be called.
1506 */
1507 int
1508check_nomodeline(char_u **argp)
1509{
1510 if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
1511 {
1512 *argp = skipwhite(*argp + 12);
1513 return FALSE;
1514 }
1515 return TRUE;
1516}
1517
1518/*
1519 * Prepare for executing autocommands for (hidden) buffer "buf".
1520 * Search for a visible window containing the current buffer. If there isn't
Bram Moolenaare76062c2022-11-28 18:51:43 +00001521 * one then use an entry in "aucmd_win[]".
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001522 * Set "curbuf" and "curwin" to match "buf".
Bram Moolenaare76062c2022-11-28 18:51:43 +00001523 * When this fails "curbuf" is not equal "buf".
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001524 */
1525 void
1526aucmd_prepbuf(
1527 aco_save_T *aco, // structure to save values in
1528 buf_T *buf) // new curbuf
1529{
1530 win_T *win;
1531 int save_ea;
1532#ifdef FEAT_AUTOCHDIR
1533 int save_acd;
1534#endif
1535
1536 // Find a window that is for the new buffer
1537 if (buf == curbuf) // be quick when buf is curbuf
1538 win = curwin;
1539 else
1540 FOR_ALL_WINDOWS(win)
1541 if (win->w_buffer == buf)
1542 break;
1543
Bram Moolenaare76062c2022-11-28 18:51:43 +00001544 // Allocate a window when needed.
1545 win_T *auc_win = NULL;
1546 int auc_idx = AUCMD_WIN_COUNT;
1547 if (win == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001548 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001549 for (auc_idx = 0; auc_idx < AUCMD_WIN_COUNT; ++auc_idx)
1550 if (!aucmd_win[auc_idx].auc_win_used)
1551 {
Bram Moolenaar84497cd2022-11-28 20:34:52 +00001552 if (aucmd_win[auc_idx].auc_win == NULL)
1553 aucmd_win[auc_idx].auc_win = win_alloc_popup_win();
1554 auc_win = aucmd_win[auc_idx].auc_win;
Bram Moolenaare76062c2022-11-28 18:51:43 +00001555 if (auc_win != NULL)
Bram Moolenaare76062c2022-11-28 18:51:43 +00001556 aucmd_win[auc_idx].auc_win_used = TRUE;
Bram Moolenaare76062c2022-11-28 18:51:43 +00001557 break;
1558 }
1559
1560 // If this fails (out of memory or using all AUCMD_WIN_COUNT
1561 // entries) then we can't reliable execute the autocmd, return with
1562 // "curbuf" unequal "buf".
1563 if (auc_win == NULL)
1564 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001565 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001566
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001567 aco->save_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001568 aco->save_curbuf = curbuf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001569 aco->save_prevwin_id = prevwin == NULL ? 0 : prevwin->w_id;
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001570 aco->save_State = State;
1571
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001572 if (win != NULL)
1573 {
1574 // There is a window for "buf" in the current tab page, make it the
1575 // curwin. This is preferred, it has the least side effects (esp. if
1576 // "buf" is curbuf).
Bram Moolenaare76062c2022-11-28 18:51:43 +00001577 aco->use_aucmd_win_idx = -1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001578 curwin = win;
1579 }
1580 else
1581 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001582 // There is no window for "buf", use "auc_win". To minimize the side
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001583 // effects, insert it in the current tab page.
1584 // Anything related to a window (e.g., setting folds) may have
1585 // unexpected results.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001586 aco->use_aucmd_win_idx = auc_idx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001587
Bram Moolenaare76062c2022-11-28 18:51:43 +00001588 win_init_popup_win(auc_win, buf);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001589
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001590 aco->globaldir = globaldir;
1591 globaldir = NULL;
1592
Bram Moolenaare76062c2022-11-28 18:51:43 +00001593 // Split the current window, put the auc_win in the upper half.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001594 // We don't want the BufEnter or WinEnter autocommands.
1595 block_autocmds();
1596 make_snapshot(SNAP_AUCMD_IDX);
1597 save_ea = p_ea;
1598 p_ea = FALSE;
1599
1600#ifdef FEAT_AUTOCHDIR
1601 // Prevent chdir() call in win_enter_ext(), through do_autochdir().
1602 save_acd = p_acd;
1603 p_acd = FALSE;
1604#endif
1605
Bram Moolenaare76062c2022-11-28 18:51:43 +00001606 (void)win_split_ins(0, WSP_TOP, auc_win, 0);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001607 (void)win_comp_pos(); // recompute window positions
1608 p_ea = save_ea;
1609#ifdef FEAT_AUTOCHDIR
1610 p_acd = save_acd;
1611#endif
1612 unblock_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001613 curwin = auc_win;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001614 }
1615 curbuf = buf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001616 aco->new_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001617 set_bufref(&aco->new_curbuf, curbuf);
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001618
1619 // disable the Visual area, the position may be invalid in another buffer
1620 aco->save_VIsual_active = VIsual_active;
1621 VIsual_active = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001622}
1623
1624/*
1625 * Cleanup after executing autocommands for a (hidden) buffer.
1626 * Restore the window as it was (if possible).
1627 */
1628 void
1629aucmd_restbuf(
1630 aco_save_T *aco) // structure holding saved values
1631{
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001632 int dummy;
1633 win_T *save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001634
Bram Moolenaare76062c2022-11-28 18:51:43 +00001635 if (aco->use_aucmd_win_idx >= 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001636 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001637 win_T *awp = aucmd_win[aco->use_aucmd_win_idx].auc_win;
1638
Bram Moolenaare76062c2022-11-28 18:51:43 +00001639 // Find "awp", it can't be closed, but it may be in another tab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001640 // page. Do not trigger autocommands here.
1641 block_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001642 if (curwin != awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001643 {
1644 tabpage_T *tp;
1645 win_T *wp;
1646
1647 FOR_ALL_TAB_WINDOWS(tp, wp)
1648 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001649 if (wp == awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001650 {
1651 if (tp != curtab)
1652 goto_tabpage_tp(tp, TRUE, TRUE);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001653 win_goto(awp);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001654 goto win_found;
1655 }
1656 }
1657 }
1658win_found:
zeertzjq46bdae02023-09-24 23:16:08 +02001659 --curbuf->b_nwindows;
orbitalcde8de02023-04-02 22:05:13 +01001660#ifdef FEAT_JOB_CHANNEL
zeertzjqa40c0bc2023-05-27 14:10:08 +01001661 int save_stop_insert_mode = stop_insert_mode;
orbitalcde8de02023-04-02 22:05:13 +01001662 // May need to stop Insert mode if we were in a prompt buffer.
1663 leaving_window(curwin);
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001664 // Do not stop Insert mode when already in Insert mode before.
1665 if (aco->save_State & MODE_INSERT)
zeertzjqa40c0bc2023-05-27 14:10:08 +01001666 stop_insert_mode = save_stop_insert_mode;
orbitalcde8de02023-04-02 22:05:13 +01001667#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001668 // Remove the window and frame from the tree of frames.
1669 (void)winframe_remove(curwin, &dummy, NULL);
1670 win_remove(curwin, NULL);
Bram Moolenaar84497cd2022-11-28 20:34:52 +00001671
1672 // The window is marked as not used, but it is not freed, it can be
1673 // used again.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001674 aucmd_win[aco->use_aucmd_win_idx].auc_win_used = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001675 last_status(FALSE); // may need to remove last status line
1676
1677 if (!valid_tabpage_win(curtab))
1678 // no valid window in current tabpage
1679 close_tabpage(curtab);
1680
1681 restore_snapshot(SNAP_AUCMD_IDX, FALSE);
1682 (void)win_comp_pos(); // recompute window positions
1683 unblock_autocmds();
1684
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001685 save_curwin = win_find_by_id(aco->save_curwin_id);
1686 if (save_curwin != NULL)
1687 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001688 else
1689 // Hmm, original window disappeared. Just use the first one.
1690 curwin = firstwin;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001691 curbuf = curwin->w_buffer;
1692#ifdef FEAT_JOB_CHANNEL
1693 // May need to restore insert mode for a prompt buffer.
1694 entering_window(curwin);
1695#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001696 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001697#ifdef FEAT_EVAL
Bram Moolenaare76062c2022-11-28 18:51:43 +00001698 vars_clear(&awp->w_vars->dv_hashtab); // free all w: variables
1699 hash_init(&awp->w_vars->dv_hashtab); // re-use the hashtab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001700#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001701 vim_free(globaldir);
1702 globaldir = aco->globaldir;
1703
1704 // the buffer contents may have changed
zeertzjq49f05242023-02-04 10:58:34 +00001705 VIsual_active = aco->save_VIsual_active;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001706 check_cursor();
1707 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1708 {
1709 curwin->w_topline = curbuf->b_ml.ml_line_count;
1710#ifdef FEAT_DIFF
1711 curwin->w_topfill = 0;
1712#endif
1713 }
1714#if defined(FEAT_GUI)
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001715 if (gui.in_use)
1716 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001717 // Hide the scrollbars from the "awp" and update.
1718 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_LEFT], FALSE);
1719 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_RIGHT], FALSE);
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001720 gui_may_update_scrollbars();
1721 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001722#endif
1723 }
1724 else
1725 {
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001726 // Restore curwin. Use the window ID, a window may have been closed
1727 // and the memory re-used for another one.
1728 save_curwin = win_find_by_id(aco->save_curwin_id);
1729 if (save_curwin != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001730 {
1731 // Restore the buffer which was previously edited by curwin, if
1732 // it was changed, we are still the same window and the buffer is
1733 // valid.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001734 if (curwin->w_id == aco->new_curwin_id
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001735 && curbuf != aco->new_curbuf.br_buf
1736 && bufref_valid(&aco->new_curbuf)
1737 && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
1738 {
1739# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
1740 if (curwin->w_s == &curbuf->b_s)
1741 curwin->w_s = &aco->new_curbuf.br_buf->b_s;
1742# endif
1743 --curbuf->b_nwindows;
1744 curbuf = aco->new_curbuf.br_buf;
1745 curwin->w_buffer = curbuf;
1746 ++curbuf->b_nwindows;
1747 }
1748
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001749 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001750 curbuf = curwin->w_buffer;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001751 prevwin = win_find_by_id(aco->save_prevwin_id);
zeertzjq49f05242023-02-04 10:58:34 +00001752
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001753 // In case the autocommand moves the cursor to a position that
1754 // does not exist in curbuf.
zeertzjq49f05242023-02-04 10:58:34 +00001755 VIsual_active = aco->save_VIsual_active;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001756 check_cursor();
1757 }
1758 }
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001759
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001760 VIsual_active = aco->save_VIsual_active;
zeertzjq49f05242023-02-04 10:58:34 +00001761 check_cursor(); // just in case lines got deleted
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001762 if (VIsual_active)
1763 check_pos(curbuf, &VIsual);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001764}
1765
1766static int autocmd_nested = FALSE;
1767
1768/*
1769 * Execute autocommands for "event" and file name "fname".
1770 * Return TRUE if some commands were executed.
1771 */
1772 int
1773apply_autocmds(
1774 event_T event,
1775 char_u *fname, // NULL or empty means use actual file name
1776 char_u *fname_io, // fname to use for <afile> on cmdline
1777 int force, // when TRUE, ignore autocmd_busy
1778 buf_T *buf) // buffer for <abuf>
1779{
1780 return apply_autocmds_group(event, fname, fname_io, force,
1781 AUGROUP_ALL, buf, NULL);
1782}
1783
1784/*
1785 * Like apply_autocmds(), but with extra "eap" argument. This takes care of
1786 * setting v:filearg.
1787 */
1788 int
1789apply_autocmds_exarg(
1790 event_T event,
1791 char_u *fname,
1792 char_u *fname_io,
1793 int force,
1794 buf_T *buf,
1795 exarg_T *eap)
1796{
1797 return apply_autocmds_group(event, fname, fname_io, force,
1798 AUGROUP_ALL, buf, eap);
1799}
1800
1801/*
1802 * Like apply_autocmds(), but handles the caller's retval. If the script
1803 * processing is being aborted or if retval is FAIL when inside a try
1804 * conditional, no autocommands are executed. If otherwise the autocommands
1805 * cause the script to be aborted, retval is set to FAIL.
1806 */
1807 int
1808apply_autocmds_retval(
1809 event_T event,
1810 char_u *fname, // NULL or empty means use actual file name
1811 char_u *fname_io, // fname to use for <afile> on cmdline
1812 int force, // when TRUE, ignore autocmd_busy
1813 buf_T *buf, // buffer for <abuf>
1814 int *retval) // pointer to caller's retval
1815{
1816 int did_cmd;
1817
1818#ifdef FEAT_EVAL
1819 if (should_abort(*retval))
1820 return FALSE;
1821#endif
1822
1823 did_cmd = apply_autocmds_group(event, fname, fname_io, force,
1824 AUGROUP_ALL, buf, NULL);
1825 if (did_cmd
1826#ifdef FEAT_EVAL
1827 && aborting()
1828#endif
1829 )
1830 *retval = FAIL;
1831 return did_cmd;
1832}
1833
1834/*
1835 * Return TRUE when there is a CursorHold autocommand defined.
1836 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001837 static int
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001838has_cursorhold(void)
1839{
Bram Moolenaar24959102022-05-07 20:01:16 +01001840 return (first_autopat[(int)(get_real_state() == MODE_NORMAL_BUSY
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001841 ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
1842}
1843
1844/*
1845 * Return TRUE if the CursorHold event can be triggered.
1846 */
1847 int
1848trigger_cursorhold(void)
1849{
1850 int state;
1851
1852 if (!did_cursorhold
1853 && has_cursorhold()
1854 && reg_recording == 0
1855 && typebuf.tb_len == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001856 && !ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001857 {
1858 state = get_real_state();
Bram Moolenaar24959102022-05-07 20:01:16 +01001859 if (state == MODE_NORMAL_BUSY || (state & MODE_INSERT) != 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001860 return TRUE;
1861 }
1862 return FALSE;
1863}
1864
1865/*
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001866 * Return TRUE when there is a WinResized autocommand defined.
1867 */
1868 int
1869has_winresized(void)
1870{
1871 return (first_autopat[(int)EVENT_WINRESIZED] != NULL);
1872}
1873
1874/*
LemonBoy09371822022-04-08 15:18:45 +01001875 * Return TRUE when there is a WinScrolled autocommand defined.
1876 */
1877 int
1878has_winscrolled(void)
1879{
1880 return (first_autopat[(int)EVENT_WINSCROLLED] != NULL);
1881}
1882
1883/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001884 * Return TRUE when there is a CursorMoved autocommand defined.
1885 */
1886 int
1887has_cursormoved(void)
1888{
1889 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1890}
1891
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001892/*
1893 * Return TRUE when there is a CursorMovedI autocommand defined.
1894 */
1895 int
1896has_cursormovedI(void)
1897{
1898 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1899}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001900
1901/*
1902 * Return TRUE when there is a TextChanged autocommand defined.
1903 */
1904 int
1905has_textchanged(void)
1906{
1907 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1908}
1909
1910/*
1911 * Return TRUE when there is a TextChangedI autocommand defined.
1912 */
1913 int
1914has_textchangedI(void)
1915{
1916 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
1917}
1918
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001919/*
1920 * Return TRUE when there is a TextChangedP autocommand defined.
1921 */
1922 int
1923has_textchangedP(void)
1924{
1925 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
1926}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001927
1928/*
1929 * Return TRUE when there is an InsertCharPre autocommand defined.
1930 */
1931 int
1932has_insertcharpre(void)
1933{
1934 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
1935}
1936
1937/*
1938 * Return TRUE when there is an CmdUndefined autocommand defined.
1939 */
1940 int
1941has_cmdundefined(void)
1942{
1943 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
1944}
1945
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001946#if defined(FEAT_EVAL) || defined(PROTO)
1947/*
1948 * Return TRUE when there is a TextYankPost autocommand defined.
1949 */
1950 int
1951has_textyankpost(void)
1952{
1953 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
1954}
1955#endif
1956
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001957#if defined(FEAT_EVAL) || defined(PROTO)
1958/*
1959 * Return TRUE when there is a CompleteChanged autocommand defined.
1960 */
1961 int
1962has_completechanged(void)
1963{
1964 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
1965}
1966#endif
1967
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02001968#if defined(FEAT_EVAL) || defined(PROTO)
1969/*
1970 * Return TRUE when there is a ModeChanged autocommand defined.
1971 */
1972 int
1973has_modechanged(void)
1974{
1975 return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
1976}
1977#endif
1978
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001979/*
1980 * Execute autocommands for "event" and file name "fname".
1981 * Return TRUE if some commands were executed.
1982 */
1983 static int
1984apply_autocmds_group(
1985 event_T event,
1986 char_u *fname, // NULL or empty means use actual file name
1987 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
1988 // use fname
1989 int force, // when TRUE, ignore autocmd_busy
1990 int group, // group ID, or AUGROUP_ALL
1991 buf_T *buf, // buffer for <abuf>
1992 exarg_T *eap UNUSED) // command arguments
1993{
1994 char_u *sfname = NULL; // short file name
1995 char_u *tail;
1996 int save_changed;
1997 buf_T *old_curbuf;
1998 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001999 char_u *save_autocmd_fname;
2000 int save_autocmd_fname_full;
2001 int save_autocmd_bufnr;
2002 char_u *save_autocmd_match;
2003 int save_autocmd_busy;
2004 int save_autocmd_nested;
2005 static int nesting = 0;
LemonBoyeca7c602022-04-14 15:39:43 +01002006 AutoPatCmd_T patcmd;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002007 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002008 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002009#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002010 funccal_entry_T funccal_entry;
2011 char_u *save_cmdarg;
2012 long save_cmdbang;
2013#endif
2014 static int filechangeshell_busy = FALSE;
2015#ifdef FEAT_PROFILE
2016 proftime_T wait_time;
2017#endif
2018 int did_save_redobuff = FALSE;
2019 save_redo_T save_redo;
2020 int save_KeyTyped = KeyTyped;
ichizok7e5fe382023-04-15 13:17:50 +01002021 ESTACK_CHECK_DECLARATION;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002022
2023 /*
2024 * Quickly return if there are no autocommands for this event or
2025 * autocommands are blocked.
2026 */
2027 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
2028 || autocmd_blocked > 0)
2029 goto BYPASS_AU;
2030
2031 /*
2032 * When autocommands are busy, new autocommands are only executed when
2033 * explicitly enabled with the "nested" flag.
2034 */
2035 if (autocmd_busy && !(force || autocmd_nested))
2036 goto BYPASS_AU;
2037
2038#ifdef FEAT_EVAL
2039 /*
2040 * Quickly return when immediately aborting on error, or when an interrupt
2041 * occurred or an exception was thrown but not caught.
2042 */
2043 if (aborting())
2044 goto BYPASS_AU;
2045#endif
2046
2047 /*
2048 * FileChangedShell never nests, because it can create an endless loop.
2049 */
2050 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
2051 || event == EVENT_FILECHANGEDSHELLPOST))
2052 goto BYPASS_AU;
2053
2054 /*
2055 * Ignore events in 'eventignore'.
2056 */
2057 if (event_ignored(event))
2058 goto BYPASS_AU;
2059
2060 /*
2061 * Allow nesting of autocommands, but restrict the depth, because it's
2062 * possible to create an endless loop.
2063 */
2064 if (nesting == 10)
2065 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00002066 emsg(_(e_autocommand_nesting_too_deep));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002067 goto BYPASS_AU;
2068 }
2069
2070 /*
2071 * Check if these autocommands are disabled. Used when doing ":all" or
2072 * ":ball".
2073 */
2074 if ( (autocmd_no_enter
2075 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
2076 || (autocmd_no_leave
2077 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
2078 goto BYPASS_AU;
2079
Bram Moolenaarbb393d82022-12-09 12:21:50 +00002080 if (event == EVENT_CMDLINECHANGED)
2081 ++aucmd_cmdline_changed_count;
2082
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002083 /*
2084 * Save the autocmd_* variables and info about the current buffer.
2085 */
2086 save_autocmd_fname = autocmd_fname;
2087 save_autocmd_fname_full = autocmd_fname_full;
2088 save_autocmd_bufnr = autocmd_bufnr;
2089 save_autocmd_match = autocmd_match;
2090 save_autocmd_busy = autocmd_busy;
2091 save_autocmd_nested = autocmd_nested;
2092 save_changed = curbuf->b_changed;
2093 old_curbuf = curbuf;
2094
2095 /*
2096 * Set the file name to be used for <afile>.
2097 * Make a copy to avoid that changing a buffer name or directory makes it
2098 * invalid.
2099 */
2100 if (fname_io == NULL)
2101 {
2102 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
Bram Moolenaarbb393d82022-12-09 12:21:50 +00002103 || event == EVENT_OPTIONSET
Danek Duvalld7d56032024-01-14 20:19:59 +01002104 || event == EVENT_MODECHANGED
2105 || event == EVENT_TERMRESPONSEALL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002106 autocmd_fname = NULL;
2107 else if (fname != NULL && !ends_excmd(*fname))
2108 autocmd_fname = fname;
2109 else if (buf != NULL)
2110 autocmd_fname = buf->b_ffname;
2111 else
2112 autocmd_fname = NULL;
2113 }
2114 else
2115 autocmd_fname = fname_io;
2116 if (autocmd_fname != NULL)
2117 autocmd_fname = vim_strsave(autocmd_fname);
2118 autocmd_fname_full = FALSE; // call FullName_save() later
2119
2120 /*
2121 * Set the buffer number to be used for <abuf>.
2122 */
2123 if (buf == NULL)
2124 autocmd_bufnr = 0;
2125 else
2126 autocmd_bufnr = buf->b_fnum;
2127
2128 /*
2129 * When the file name is NULL or empty, use the file name of buffer "buf".
2130 * Always use the full path of the file name to match with, in case
2131 * "allow_dirs" is set.
2132 */
2133 if (fname == NULL || *fname == NUL)
2134 {
2135 if (buf == NULL)
2136 fname = NULL;
2137 else
2138 {
2139#ifdef FEAT_SYN_HL
2140 if (event == EVENT_SYNTAX)
2141 fname = buf->b_p_syn;
2142 else
2143#endif
2144 if (event == EVENT_FILETYPE)
2145 fname = buf->b_p_ft;
2146 else
2147 {
2148 if (buf->b_sfname != NULL)
2149 sfname = vim_strsave(buf->b_sfname);
2150 fname = buf->b_ffname;
2151 }
2152 }
2153 if (fname == NULL)
2154 fname = (char_u *)"";
2155 fname = vim_strsave(fname); // make a copy, so we can change it
2156 }
2157 else
2158 {
2159 sfname = vim_strsave(fname);
2160 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002161 // ColorScheme, QuickFixCmd*, DirChanged and similar.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002162 if (event == EVENT_FILETYPE
2163 || event == EVENT_SYNTAX
2164 || event == EVENT_CMDLINECHANGED
2165 || event == EVENT_CMDLINEENTER
2166 || event == EVENT_CMDLINELEAVE
2167 || event == EVENT_CMDWINENTER
2168 || event == EVENT_CMDWINLEAVE
2169 || event == EVENT_CMDUNDEFINED
2170 || event == EVENT_FUNCUNDEFINED
2171 || event == EVENT_REMOTEREPLY
2172 || event == EVENT_SPELLFILEMISSING
2173 || event == EVENT_QUICKFIXCMDPRE
2174 || event == EVENT_COLORSCHEME
2175 || event == EVENT_COLORSCHEMEPRE
2176 || event == EVENT_OPTIONSET
2177 || event == EVENT_QUICKFIXCMDPOST
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002178 || event == EVENT_DIRCHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002179 || event == EVENT_DIRCHANGEDPRE
naohiro ono23beefe2021-11-13 12:38:49 +00002180 || event == EVENT_MODECHANGED
zeertzjqc601d982022-10-10 13:46:15 +01002181 || event == EVENT_MENUPOPUP
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002182 || event == EVENT_USER
LemonBoy09371822022-04-08 15:18:45 +01002183 || event == EVENT_WINCLOSED
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00002184 || event == EVENT_WINRESIZED
Danek Duvalld7d56032024-01-14 20:19:59 +01002185 || event == EVENT_WINSCROLLED
2186 || event == EVENT_TERMRESPONSEALL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002187 {
2188 fname = vim_strsave(fname);
2189 autocmd_fname_full = TRUE; // don't expand it later
2190 }
2191 else
2192 fname = FullName_save(fname, FALSE);
2193 }
2194 if (fname == NULL) // out of memory
2195 {
2196 vim_free(sfname);
2197 retval = FALSE;
2198 goto BYPASS_AU;
2199 }
2200
2201#ifdef BACKSLASH_IN_FILENAME
2202 /*
2203 * Replace all backslashes with forward slashes. This makes the
2204 * autocommand patterns portable between Unix and MS-DOS.
2205 */
2206 if (sfname != NULL)
2207 forward_slash(sfname);
2208 forward_slash(fname);
2209#endif
2210
2211#ifdef VMS
2212 // remove version for correct match
2213 if (sfname != NULL)
2214 vms_remove_version(sfname);
2215 vms_remove_version(fname);
2216#endif
2217
2218 /*
2219 * Set the name to be used for <amatch>.
2220 */
2221 autocmd_match = fname;
2222
2223
2224 // Don't redraw while doing autocommands.
2225 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002226
2227 // name and lnum are filled in later
2228 estack_push(ETYPE_AUCMD, NULL, 0);
ichizok7e5fe382023-04-15 13:17:50 +01002229 ESTACK_CHECK_SETUP;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002230
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002231 save_current_sctx = current_sctx;
2232
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002233#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002234# ifdef FEAT_PROFILE
2235 if (do_profiling == PROF_YES)
2236 prof_child_enter(&wait_time); // doesn't count for the caller itself
2237# endif
2238
2239 // Don't use local function variables, if called from a function.
2240 save_funccal(&funccal_entry);
2241#endif
2242
2243 /*
2244 * When starting to execute autocommands, save the search patterns.
2245 */
2246 if (!autocmd_busy)
2247 {
2248 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002249 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002250 {
2251 saveRedobuff(&save_redo);
2252 did_save_redobuff = TRUE;
2253 }
2254 did_filetype = keep_filetype;
2255 }
2256
2257 /*
2258 * Note that we are applying autocmds. Some commands need to know.
2259 */
2260 autocmd_busy = TRUE;
2261 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2262 ++nesting; // see matching decrement below
2263
2264 // Remember that FileType was triggered. Used for did_filetype().
2265 if (event == EVENT_FILETYPE)
2266 did_filetype = TRUE;
2267
2268 tail = gettail(fname);
2269
2270 // Find first autocommand that matches
LemonBoyeca7c602022-04-14 15:39:43 +01002271 CLEAR_FIELD(patcmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002272 patcmd.curpat = first_autopat[(int)event];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002273 patcmd.group = group;
2274 patcmd.fname = fname;
2275 patcmd.sfname = sfname;
2276 patcmd.tail = tail;
2277 patcmd.event = event;
2278 patcmd.arg_bufnr = autocmd_bufnr;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002279 auto_next_pat(&patcmd, FALSE);
2280
2281 // found one, start executing the autocommands
2282 if (patcmd.curpat != NULL)
2283 {
2284 // add to active_apc_list
2285 patcmd.next = active_apc_list;
2286 active_apc_list = &patcmd;
2287
2288#ifdef FEAT_EVAL
2289 // set v:cmdarg (only when there is a matching pattern)
2290 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2291 if (eap != NULL)
2292 {
2293 save_cmdarg = set_cmdarg(eap, NULL);
2294 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2295 }
2296 else
2297 save_cmdarg = NULL; // avoid gcc warning
2298#endif
2299 retval = TRUE;
2300 // mark the last pattern, to avoid an endless loop when more patterns
2301 // are added when executing autocommands
2302 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2303 ap->last = FALSE;
2304 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002305
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002306 // Make sure cursor and topline are valid. The first time the current
2307 // values are saved, restored by reset_lnums(). When nested only the
2308 // values are corrected when needed.
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002309 if (nesting == 1)
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002310 check_lnums(TRUE);
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002311 else
2312 check_lnums_nested(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002313
Christian Brabandt590aae32023-06-25 22:34:22 +01002314 int save_did_emsg = did_emsg;
2315 int save_ex_pressedreturn = get_pressedreturn();
ichizokc3f91c02021-12-17 09:44:33 +00002316
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002317 do_cmdline(NULL, getnextac, (void *)&patcmd,
2318 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002319
ichizokc3f91c02021-12-17 09:44:33 +00002320 did_emsg += save_did_emsg;
Christian Brabandt590aae32023-06-25 22:34:22 +01002321 set_pressedreturn(save_ex_pressedreturn);
ichizokc3f91c02021-12-17 09:44:33 +00002322
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002323 if (nesting == 1)
2324 // restore cursor and topline, unless they were changed
2325 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002326
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002327#ifdef FEAT_EVAL
2328 if (eap != NULL)
2329 {
2330 (void)set_cmdarg(NULL, save_cmdarg);
2331 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2332 }
2333#endif
2334 // delete from active_apc_list
2335 if (active_apc_list == &patcmd) // just in case
2336 active_apc_list = patcmd.next;
2337 }
2338
Bram Moolenaar79cdf022023-05-20 14:07:00 +01002339 if (RedrawingDisabled > 0)
2340 --RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002341 autocmd_busy = save_autocmd_busy;
2342 filechangeshell_busy = FALSE;
2343 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002344 vim_free(SOURCING_NAME);
ichizok7e5fe382023-04-15 13:17:50 +01002345 ESTACK_CHECK_NOW;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002346 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002347 vim_free(autocmd_fname);
2348 autocmd_fname = save_autocmd_fname;
2349 autocmd_fname_full = save_autocmd_fname_full;
2350 autocmd_bufnr = save_autocmd_bufnr;
2351 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002352 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002353#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002354 restore_funccal();
2355# ifdef FEAT_PROFILE
2356 if (do_profiling == PROF_YES)
2357 prof_child_exit(&wait_time);
2358# endif
2359#endif
2360 KeyTyped = save_KeyTyped;
2361 vim_free(fname);
2362 vim_free(sfname);
2363 --nesting; // see matching increment above
2364
2365 /*
2366 * When stopping to execute autocommands, restore the search patterns and
2367 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2368 * free any windows in the au_pending_free_win list.
2369 */
2370 if (!autocmd_busy)
2371 {
2372 restore_search_patterns();
2373 if (did_save_redobuff)
2374 restoreRedobuff(&save_redo);
2375 did_filetype = FALSE;
2376 while (au_pending_free_buf != NULL)
2377 {
2378 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002379
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002380 vim_free(au_pending_free_buf);
2381 au_pending_free_buf = b;
2382 }
2383 while (au_pending_free_win != NULL)
2384 {
2385 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002386
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002387 vim_free(au_pending_free_win);
2388 au_pending_free_win = w;
2389 }
2390 }
2391
2392 /*
2393 * Some events don't set or reset the Changed flag.
2394 * Check if still in the same buffer!
2395 */
2396 if (curbuf == old_curbuf
2397 && (event == EVENT_BUFREADPOST
2398 || event == EVENT_BUFWRITEPOST
2399 || event == EVENT_FILEAPPENDPOST
2400 || event == EVENT_VIMLEAVE
2401 || event == EVENT_VIMLEAVEPRE))
2402 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002403 if (curbuf->b_changed != save_changed)
2404 need_maketitle = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002405 curbuf->b_changed = save_changed;
2406 }
2407
2408 au_cleanup(); // may really delete removed patterns/commands now
2409
2410BYPASS_AU:
2411 // When wiping out a buffer make sure all its buffer-local autocommands
2412 // are deleted.
2413 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2414 aubuflocal_remove(buf);
2415
2416 if (retval == OK && event == EVENT_FILETYPE)
2417 au_did_filetype = TRUE;
2418
2419 return retval;
2420}
2421
2422# ifdef FEAT_EVAL
2423static char_u *old_termresponse = NULL;
Danek Duvalld7d56032024-01-14 20:19:59 +01002424static char_u *old_termu7resp = NULL;
2425static char_u *old_termblinkresp = NULL;
2426static char_u *old_termrbgresp = NULL;
2427static char_u *old_termrfgresp = NULL;
2428static char_u *old_termstyleresp = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002429# endif
2430
2431/*
2432 * Block triggering autocommands until unblock_autocmd() is called.
2433 * Can be used recursively, so long as it's symmetric.
2434 */
2435 void
2436block_autocmds(void)
2437{
2438# ifdef FEAT_EVAL
2439 // Remember the value of v:termresponse.
2440 if (autocmd_blocked == 0)
Danek Duvalld7d56032024-01-14 20:19:59 +01002441 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002442 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
Danek Duvalld7d56032024-01-14 20:19:59 +01002443 old_termu7resp = get_vim_var_str(VV_TERMU7RESP);
2444 old_termblinkresp = get_vim_var_str(VV_TERMBLINKRESP);
2445 old_termrbgresp = get_vim_var_str(VV_TERMRBGRESP);
2446 old_termrfgresp = get_vim_var_str(VV_TERMRFGRESP);
2447 old_termstyleresp = get_vim_var_str(VV_TERMSTYLERESP);
2448 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002449# endif
2450 ++autocmd_blocked;
2451}
2452
2453 void
2454unblock_autocmds(void)
2455{
2456 --autocmd_blocked;
2457
2458# ifdef FEAT_EVAL
Danek Duvalld7d56032024-01-14 20:19:59 +01002459 // When v:termresponse, etc, were set while autocommands were blocked,
2460 // trigger the autocommands now. Esp. useful when executing a shell
2461 // command during startup (vimdiff).
2462 if (autocmd_blocked == 0)
2463 {
2464 if (get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2465 {
2466 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2467 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"version", NULL, FALSE, curbuf);
2468 }
2469 if (get_vim_var_str(VV_TERMU7RESP) != old_termu7resp)
2470 {
2471 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"ambiguouswidth", NULL, FALSE, curbuf);
2472 }
2473 if (get_vim_var_str(VV_TERMBLINKRESP) != old_termblinkresp)
2474 {
2475 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"cursorblink", NULL, FALSE, curbuf);
2476 }
2477 if (get_vim_var_str(VV_TERMRBGRESP) != old_termrbgresp)
2478 {
2479 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"background", NULL, FALSE, curbuf);
2480 }
2481 if (get_vim_var_str(VV_TERMRFGRESP) != old_termrfgresp)
2482 {
2483 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"foreground", NULL, FALSE, curbuf);
2484 }
2485 if (get_vim_var_str(VV_TERMSTYLERESP) != old_termstyleresp)
2486 {
2487 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"cursorshape", NULL, FALSE, curbuf);
2488 }
2489 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002490# endif
2491}
2492
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002493 int
2494is_autocmd_blocked(void)
2495{
2496 return autocmd_blocked != 0;
2497}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002498
2499/*
2500 * Find next autocommand pattern that matches.
2501 */
2502 static void
2503auto_next_pat(
LemonBoyeca7c602022-04-14 15:39:43 +01002504 AutoPatCmd_T *apc,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002505 int stop_at_last) // stop when 'last' flag is set
2506{
2507 AutoPat *ap;
2508 AutoCmd *cp;
2509 char_u *name;
2510 char *s;
LemonBoyeca7c602022-04-14 15:39:43 +01002511 estack_T *entry;
2512 char_u *namep;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002513
LemonBoyeca7c602022-04-14 15:39:43 +01002514 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
2515
2516 // Clear the exestack entry for this ETYPE_AUCMD entry.
2517 VIM_CLEAR(entry->es_name);
2518 entry->es_info.aucmd = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002519
2520 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2521 {
2522 apc->curpat = NULL;
2523
2524 // Only use a pattern when it has not been removed, has commands and
2525 // the group matches. For buffer-local autocommands only check the
2526 // buffer number.
2527 if (ap->pat != NULL && ap->cmds != NULL
2528 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2529 {
2530 // execution-condition
2531 if (ap->buflocal_nr == 0
2532 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2533 apc->sfname, apc->tail, ap->allow_dirs))
2534 : ap->buflocal_nr == apc->arg_bufnr)
2535 {
2536 name = event_nr2name(apc->event);
2537 s = _("%s Autocommands for \"%s\"");
LemonBoyeca7c602022-04-14 15:39:43 +01002538 namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1);
2539 if (namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002540 {
LemonBoyeca7c602022-04-14 15:39:43 +01002541 sprintf((char *)namep, s, (char *)name, (char *)ap->pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002542 if (p_verbose >= 8)
2543 {
2544 verbose_enter();
LemonBoyeca7c602022-04-14 15:39:43 +01002545 smsg(_("Executing %s"), namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002546 verbose_leave();
2547 }
2548 }
2549
LemonBoyeca7c602022-04-14 15:39:43 +01002550 // Update the exestack entry for this autocmd.
2551 entry->es_name = namep;
2552 entry->es_info.aucmd = apc;
2553
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002554 apc->curpat = ap;
2555 apc->nextcmd = ap->cmds;
2556 // mark last command
2557 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2558 cp->last = FALSE;
2559 cp->last = TRUE;
2560 }
2561 line_breakcheck();
2562 if (apc->curpat != NULL) // found a match
2563 break;
2564 }
2565 if (stop_at_last && ap->last)
2566 break;
2567 }
2568}
2569
Dominique Pellee764d1b2023-03-12 21:20:59 +00002570#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002571/*
LemonBoyeca7c602022-04-14 15:39:43 +01002572 * Get the script context where autocommand "acp" is defined.
2573 */
2574 sctx_T *
2575acp_script_ctx(AutoPatCmd_T *acp)
2576{
2577 return &acp->script_ctx;
2578}
Dominique Pellee764d1b2023-03-12 21:20:59 +00002579#endif
LemonBoyeca7c602022-04-14 15:39:43 +01002580
2581/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002582 * Get next autocommand command.
2583 * Called by do_cmdline() to get the next line for ":if".
2584 * Returns allocated string, or NULL for end of autocommands.
2585 */
2586 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002587getnextac(
2588 int c UNUSED,
2589 void *cookie,
2590 int indent UNUSED,
2591 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002592{
LemonBoyeca7c602022-04-14 15:39:43 +01002593 AutoPatCmd_T *acp = (AutoPatCmd_T *)cookie;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002594 char_u *retval;
2595 AutoCmd *ac;
2596
2597 // Can be called again after returning the last line.
2598 if (acp->curpat == NULL)
2599 return NULL;
2600
2601 // repeat until we find an autocommand to execute
2602 for (;;)
2603 {
2604 // skip removed commands
2605 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2606 if (acp->nextcmd->last)
2607 acp->nextcmd = NULL;
2608 else
2609 acp->nextcmd = acp->nextcmd->next;
2610
2611 if (acp->nextcmd != NULL)
2612 break;
2613
2614 // at end of commands, find next pattern that matches
2615 if (acp->curpat->last)
2616 acp->curpat = NULL;
2617 else
2618 acp->curpat = acp->curpat->next;
2619 if (acp->curpat != NULL)
2620 auto_next_pat(acp, TRUE);
2621 if (acp->curpat == NULL)
2622 return NULL;
2623 }
2624
2625 ac = acp->nextcmd;
2626
2627 if (p_verbose >= 9)
2628 {
2629 verbose_enter_scroll();
2630 smsg(_("autocommand %s"), ac->cmd);
2631 msg_puts("\n"); // don't overwrite this either
2632 verbose_leave_scroll();
2633 }
2634 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002635 // Remove one-shot ("once") autocmd in anticipation of its execution.
2636 if (ac->once)
2637 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002638 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002639 current_sctx = ac->script_ctx;
LemonBoyeca7c602022-04-14 15:39:43 +01002640 acp->script_ctx = current_sctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002641 if (ac->last)
2642 acp->nextcmd = NULL;
2643 else
2644 acp->nextcmd = ac->next;
2645 return retval;
2646}
2647
2648/*
2649 * Return TRUE if there is a matching autocommand for "fname".
2650 * To account for buffer-local autocommands, function needs to know
2651 * in which buffer the file will be opened.
2652 */
2653 int
2654has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2655{
2656 AutoPat *ap;
2657 char_u *fname;
2658 char_u *tail = gettail(sfname);
2659 int retval = FALSE;
2660
2661 fname = FullName_save(sfname, FALSE);
2662 if (fname == NULL)
2663 return FALSE;
2664
2665#ifdef BACKSLASH_IN_FILENAME
2666 /*
2667 * Replace all backslashes with forward slashes. This makes the
2668 * autocommand patterns portable between Unix and MS-DOS.
2669 */
2670 sfname = vim_strsave(sfname);
2671 if (sfname != NULL)
2672 forward_slash(sfname);
2673 forward_slash(fname);
2674#endif
2675
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002676 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002677 if (ap->pat != NULL && ap->cmds != NULL
2678 && (ap->buflocal_nr == 0
2679 ? match_file_pat(NULL, &ap->reg_prog,
2680 fname, sfname, tail, ap->allow_dirs)
2681 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2682 ))
2683 {
2684 retval = TRUE;
2685 break;
2686 }
2687
2688 vim_free(fname);
2689#ifdef BACKSLASH_IN_FILENAME
2690 vim_free(sfname);
2691#endif
2692
2693 return retval;
2694}
2695
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002696/*
2697 * Function given to ExpandGeneric() to obtain the list of autocommand group
2698 * names.
2699 */
2700 char_u *
2701get_augroup_name(expand_T *xp UNUSED, int idx)
2702{
2703 if (idx == augroups.ga_len) // add "END" add the end
2704 return (char_u *)"END";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002705 if (idx < 0 || idx >= augroups.ga_len) // end of list
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002706 return NULL;
2707 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2708 // skip deleted entries
2709 return (char_u *)"";
2710 return AUGROUP_NAME(idx); // return a name
2711}
2712
2713static int include_groups = FALSE;
2714
2715 char_u *
2716set_context_in_autocmd(
2717 expand_T *xp,
2718 char_u *arg,
2719 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2720{
2721 char_u *p;
2722 int group;
2723
2724 // check for a group name, skip it if present
2725 include_groups = FALSE;
2726 p = arg;
2727 group = au_get_grouparg(&arg);
2728 if (group == AUGROUP_ERROR)
2729 return NULL;
2730 // If there only is a group name that's what we expand.
2731 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2732 {
2733 arg = p;
2734 group = AUGROUP_ALL;
2735 }
2736
2737 // skip over event name
2738 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2739 if (*p == ',')
2740 arg = p + 1;
2741 if (*p == NUL)
2742 {
2743 if (group == AUGROUP_ALL)
2744 include_groups = TRUE;
2745 xp->xp_context = EXPAND_EVENTS; // expand event name
2746 xp->xp_pattern = arg;
2747 return NULL;
2748 }
2749
2750 // skip over pattern
2751 arg = skipwhite(p);
2752 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2753 arg++;
2754 if (*arg)
2755 return arg; // expand (next) command
2756
2757 if (doautocmd)
2758 xp->xp_context = EXPAND_FILES; // expand file names
2759 else
2760 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2761 return NULL;
2762}
2763
2764/*
2765 * Function given to ExpandGeneric() to obtain the list of event names.
2766 */
2767 char_u *
2768get_event_name(expand_T *xp UNUSED, int idx)
2769{
2770 if (idx < augroups.ga_len) // First list group names, if wanted
2771 {
2772 if (!include_groups || AUGROUP_NAME(idx) == NULL
2773 || AUGROUP_NAME(idx) == get_deleted_augroup())
2774 return (char_u *)""; // skip deleted entries
2775 return AUGROUP_NAME(idx); // return a name
2776 }
2777 return (char_u *)event_names[idx - augroups.ga_len].name;
2778}
2779
Yee Cheng Chin900894b2023-09-29 20:42:32 +02002780/*
2781 * Function given to ExpandGeneric() to obtain the list of event names. Don't
2782 * include groups.
2783 */
2784 char_u *
2785get_event_name_no_group(expand_T *xp UNUSED, int idx)
2786{
2787 return (char_u *)event_names[idx].name;
2788}
2789
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002790
2791#if defined(FEAT_EVAL) || defined(PROTO)
2792/*
2793 * Return TRUE if autocmd is supported.
2794 */
2795 int
2796autocmd_supported(char_u *name)
2797{
2798 char_u *p;
2799
2800 return (event_name2nr(name, &p) != NUM_EVENTS);
2801}
2802
2803/*
2804 * Return TRUE if an autocommand is defined for a group, event and
2805 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2806 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2807 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2808 * Used for:
2809 * exists("#Group") or
2810 * exists("#Group#Event") or
2811 * exists("#Group#Event#pat") or
2812 * exists("#Event") or
2813 * exists("#Event#pat")
2814 */
2815 int
2816au_exists(char_u *arg)
2817{
2818 char_u *arg_save;
2819 char_u *pattern = NULL;
2820 char_u *event_name;
2821 char_u *p;
2822 event_T event;
2823 AutoPat *ap;
2824 buf_T *buflocal_buf = NULL;
2825 int group;
2826 int retval = FALSE;
2827
2828 // Make a copy so that we can change the '#' chars to a NUL.
2829 arg_save = vim_strsave(arg);
2830 if (arg_save == NULL)
2831 return FALSE;
2832 p = vim_strchr(arg_save, '#');
2833 if (p != NULL)
2834 *p++ = NUL;
2835
2836 // First, look for an autocmd group name
2837 group = au_find_group(arg_save);
2838 if (group == AUGROUP_ERROR)
2839 {
2840 // Didn't match a group name, assume the first argument is an event.
2841 group = AUGROUP_ALL;
2842 event_name = arg_save;
2843 }
2844 else
2845 {
2846 if (p == NULL)
2847 {
2848 // "Group": group name is present and it's recognized
2849 retval = TRUE;
2850 goto theend;
2851 }
2852
2853 // Must be "Group#Event" or "Group#Event#pat".
2854 event_name = p;
2855 p = vim_strchr(event_name, '#');
2856 if (p != NULL)
2857 *p++ = NUL; // "Group#Event#pat"
2858 }
2859
2860 pattern = p; // "pattern" is NULL when there is no pattern
2861
2862 // find the index (enum) for the event name
2863 event = event_name2nr(event_name, &p);
2864
2865 // return FALSE if the event name is not recognized
2866 if (event == NUM_EVENTS)
2867 goto theend;
2868
2869 // Find the first autocommand for this event.
2870 // If there isn't any, return FALSE;
2871 // If there is one and no pattern given, return TRUE;
2872 ap = first_autopat[(int)event];
2873 if (ap == NULL)
2874 goto theend;
2875
2876 // if pattern is "<buffer>", special handling is needed which uses curbuf
2877 // for pattern "<buffer=N>, fnamecmp() will work fine
2878 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2879 buflocal_buf = curbuf;
2880
2881 // Check if there is an autocommand with the given pattern.
2882 for ( ; ap != NULL; ap = ap->next)
2883 // only use a pattern when it has not been removed and has commands.
2884 // For buffer-local autocommands, fnamecmp() works fine.
2885 if (ap->pat != NULL && ap->cmds != NULL
2886 && (group == AUGROUP_ALL || ap->group == group)
2887 && (pattern == NULL
2888 || (buflocal_buf == NULL
2889 ? fnamecmp(ap->pat, pattern) == 0
2890 : ap->buflocal_nr == buflocal_buf->b_fnum)))
2891 {
2892 retval = TRUE;
2893 break;
2894 }
2895
2896theend:
2897 vim_free(arg_save);
2898 return retval;
2899}
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002900
2901/*
2902 * autocmd_add() and autocmd_delete() functions
2903 */
2904 static void
2905autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
2906{
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002907 list_T *aucmd_list;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002908 listitem_T *li;
2909 dict_T *event_dict;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002910 dictitem_T *di;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002911 char_u *event_name = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002912 list_T *event_list;
2913 listitem_T *eli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002914 event_T event;
2915 char_u *group_name = NULL;
2916 int group;
2917 char_u *pat = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002918 list_T *pat_list;
2919 listitem_T *pli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002920 char_u *cmd = NULL;
2921 char_u *end;
2922 int once;
2923 int nested;
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01002924 int replace; // replace the cmd for a group/event
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002925 int retval = VVAL_TRUE;
2926 int save_augroup = current_augroup;
2927
2928 rettv->v_type = VAR_BOOL;
2929 rettv->vval.v_number = VVAL_FALSE;
2930
2931 if (check_for_list_arg(argvars, 0) == FAIL)
2932 return;
2933
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002934 aucmd_list = argvars[0].vval.v_list;
2935 if (aucmd_list == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002936 return;
2937
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002938 FOR_ALL_LIST_ITEMS(aucmd_list, li)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002939 {
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002940 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002941 VIM_CLEAR(cmd);
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002942 event_name = NULL;
2943 event_list = NULL;
2944 pat = NULL;
2945 pat_list = NULL;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002946
2947 if (li->li_tv.v_type != VAR_DICT)
2948 continue;
2949
2950 event_dict = li->li_tv.vval.v_dict;
2951 if (event_dict == NULL)
2952 continue;
2953
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002954 di = dict_find(event_dict, (char_u *)"event", -1);
2955 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002956 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002957 if (di->di_tv.v_type == VAR_STRING)
2958 {
2959 event_name = di->di_tv.vval.v_string;
2960 if (event_name == NULL)
2961 {
2962 emsg(_(e_string_required));
2963 continue;
2964 }
2965 }
2966 else if (di->di_tv.v_type == VAR_LIST)
2967 {
2968 event_list = di->di_tv.vval.v_list;
2969 if (event_list == NULL)
2970 {
2971 emsg(_(e_list_required));
2972 continue;
2973 }
2974 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002975 else
2976 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002977 emsg(_(e_string_or_list_expected));
2978 continue;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002979 }
2980 }
2981
Bram Moolenaard61efa52022-07-23 09:52:04 +01002982 group_name = dict_get_string(event_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002983 if (group_name == NULL || *group_name == NUL)
2984 // if the autocmd group name is not specified, then use the current
2985 // autocmd group
2986 group = current_augroup;
2987 else
2988 {
2989 group = au_find_group(group_name);
2990 if (group == AUGROUP_ERROR)
2991 {
2992 if (delete)
2993 {
2994 semsg(_(e_no_such_group_str), group_name);
2995 retval = VVAL_FALSE;
2996 break;
2997 }
2998 // group is not found, create it now
2999 group = au_new_group(group_name);
3000 if (group == AUGROUP_ERROR)
3001 {
3002 semsg(_(e_no_such_group_str), group_name);
3003 retval = VVAL_FALSE;
3004 break;
3005 }
3006
3007 current_augroup = group;
3008 }
3009 }
3010
3011 // if a buffer number is specified, then generate a pattern of the form
3012 // "<buffer=n>. Otherwise, use the pattern supplied by the user.
3013 if (dict_has_key(event_dict, "bufnr"))
3014 {
3015 varnumber_T bnum;
3016
Bram Moolenaard61efa52022-07-23 09:52:04 +01003017 bnum = dict_get_number_def(event_dict, "bufnr", -1);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003018 if (bnum == -1)
3019 continue;
3020
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003021 vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
3022 pat = IObuff;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003023 }
3024 else
3025 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003026 di = dict_find(event_dict, (char_u *)"pattern", -1);
3027 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003028 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003029 if (di->di_tv.v_type == VAR_STRING)
3030 {
3031 pat = di->di_tv.vval.v_string;
3032 if (pat == NULL)
3033 {
3034 emsg(_(e_string_required));
3035 continue;
3036 }
3037 }
3038 else if (di->di_tv.v_type == VAR_LIST)
3039 {
3040 pat_list = di->di_tv.vval.v_list;
3041 if (pat_list == NULL)
3042 {
3043 emsg(_(e_list_required));
3044 continue;
3045 }
3046 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003047 else
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003048 {
3049 emsg(_(e_string_or_list_expected));
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003050 continue;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003051 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003052 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003053 else if (delete)
3054 pat = (char_u *)"";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003055 }
3056
Bram Moolenaard61efa52022-07-23 09:52:04 +01003057 once = dict_get_bool(event_dict, "once", FALSE);
3058 nested = dict_get_bool(event_dict, "nested", FALSE);
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01003059 // if 'replace' is true, then remove all the commands associated with
3060 // this autocmd event/group and add the new command.
Bram Moolenaard61efa52022-07-23 09:52:04 +01003061 replace = dict_get_bool(event_dict, "replace", FALSE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003062
Bram Moolenaard61efa52022-07-23 09:52:04 +01003063 cmd = dict_get_string(event_dict, "cmd", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003064 if (cmd == NULL)
3065 {
3066 if (delete)
3067 cmd = vim_strsave((char_u *)"");
3068 else
3069 continue;
3070 }
3071
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003072 if (delete && (event_name == NULL
3073 || (event_name[0] == '*' && event_name[1] == NUL)))
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003074 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003075 // if the event name is not specified or '*', delete all the events
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003076 for (event = (event_T)0; (int)event < NUM_EVENTS;
3077 event = (event_T)((int)event + 1))
3078 {
3079 if (do_autocmd_event(event, pat, once, nested, cmd, delete,
3080 group, 0) == FAIL)
3081 {
3082 retval = VVAL_FALSE;
3083 break;
3084 }
3085 }
3086 }
3087 else
3088 {
Bram Moolenaar968443e2022-05-27 21:16:34 +01003089 char_u *p = NULL;
3090
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003091 eli = NULL;
3092 end = NULL;
3093 while (TRUE)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003094 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003095 if (event_list != NULL)
3096 {
3097 if (eli == NULL)
3098 eli = event_list->lv_first;
3099 else
3100 eli = eli->li_next;
3101 if (eli == NULL)
3102 break;
3103 if (eli->li_tv.v_type != VAR_STRING
Bram Moolenaar882476a2022-06-01 16:02:38 +01003104 || (p = eli->li_tv.vval.v_string) == NULL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003105 {
3106 emsg(_(e_string_required));
Bram Moolenaar882476a2022-06-01 16:02:38 +01003107 break;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003108 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003109 }
3110 else
3111 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003112 if (p == NULL)
3113 p = event_name;
3114 if (p == NULL || *p == NUL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003115 break;
3116 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003117
3118 event = event_name2nr(p, &end);
3119 if (event == NUM_EVENTS || *end != NUL)
3120 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003121 // this also catches something following a valid event name
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003122 semsg(_(e_no_such_event_str), p);
3123 retval = VVAL_FALSE;
3124 break;
3125 }
3126 if (pat != NULL)
3127 {
3128 if (do_autocmd_event(event, pat, once, nested, cmd,
3129 delete | replace, group, 0) == FAIL)
3130 {
3131 retval = VVAL_FALSE;
3132 break;
3133 }
3134 }
3135 else if (pat_list != NULL)
3136 {
3137 FOR_ALL_LIST_ITEMS(pat_list, pli)
3138 {
3139 if (pli->li_tv.v_type != VAR_STRING
3140 || pli->li_tv.vval.v_string == NULL)
3141 {
3142 emsg(_(e_string_required));
3143 continue;
3144 }
3145 if (do_autocmd_event(event,
3146 pli->li_tv.vval.v_string, once, nested,
3147 cmd, delete | replace, group, 0) ==
3148 FAIL)
3149 {
3150 retval = VVAL_FALSE;
3151 break;
3152 }
3153 }
3154 if (retval == VVAL_FALSE)
3155 break;
3156 }
3157 if (event_name != NULL)
3158 p = end;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003159 }
3160 }
3161
3162 // if only the autocmd group name is specified for delete and the
3163 // autocmd event, pattern and cmd are not specified, then delete the
3164 // autocmd group.
3165 if (delete && group_name != NULL &&
3166 (event_name == NULL || event_name[0] == NUL)
3167 && (pat == NULL || pat[0] == NUL)
3168 && (cmd == NULL || cmd[0] == NUL))
3169 au_del_group(group_name);
3170 }
3171
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003172 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003173 VIM_CLEAR(cmd);
3174
3175 current_augroup = save_augroup;
3176 rettv->vval.v_number = retval;
3177}
3178
3179/*
3180 * autocmd_add() function
3181 */
3182 void
3183f_autocmd_add(typval_T *argvars, typval_T *rettv)
3184{
3185 autocmd_add_or_delete(argvars, rettv, FALSE);
3186}
3187
3188/*
3189 * autocmd_delete() function
3190 */
3191 void
3192f_autocmd_delete(typval_T *argvars, typval_T *rettv)
3193{
3194 autocmd_add_or_delete(argvars, rettv, TRUE);
3195}
3196
3197/*
3198 * autocmd_get() function
3199 * Returns a List of autocmds.
3200 */
3201 void
3202f_autocmd_get(typval_T *argvars, typval_T *rettv)
3203{
3204 event_T event_arg = NUM_EVENTS;
3205 event_T event;
3206 AutoPat *ap;
3207 AutoCmd *ac;
3208 list_T *event_list;
3209 dict_T *event_dict;
3210 char_u *event_name = NULL;
3211 char_u *pat = NULL;
3212 char_u *name = NULL;
3213 int group = AUGROUP_ALL;
3214
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01003215 if (rettv_list_alloc(rettv) == FAIL)
3216 return;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003217 if (check_for_opt_dict_arg(argvars, 0) == FAIL)
3218 return;
3219
3220 if (argvars[0].v_type == VAR_DICT)
3221 {
3222 // return only the autocmds in the specified group
3223 if (dict_has_key(argvars[0].vval.v_dict, "group"))
3224 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003225 name = dict_get_string(argvars[0].vval.v_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003226 if (name == NULL)
3227 return;
3228
3229 if (*name == NUL)
3230 group = AUGROUP_DEFAULT;
3231 else
3232 {
3233 group = au_find_group(name);
3234 if (group == AUGROUP_ERROR)
3235 {
3236 semsg(_(e_no_such_group_str), name);
3237 vim_free(name);
3238 return;
3239 }
3240 }
3241 vim_free(name);
3242 }
3243
3244 // return only the autocmds for the specified event
3245 if (dict_has_key(argvars[0].vval.v_dict, "event"))
3246 {
3247 int i;
3248
Bram Moolenaard61efa52022-07-23 09:52:04 +01003249 name = dict_get_string(argvars[0].vval.v_dict, "event", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003250 if (name == NULL)
3251 return;
3252
3253 if (name[0] == '*' && name[1] == NUL)
3254 event_arg = NUM_EVENTS;
3255 else
3256 {
3257 for (i = 0; event_names[i].name != NULL; i++)
3258 if (STRICMP(event_names[i].name, name) == 0)
3259 break;
3260 if (event_names[i].name == NULL)
3261 {
3262 semsg(_(e_no_such_event_str), name);
3263 vim_free(name);
3264 return;
3265 }
3266 event_arg = event_names[i].event;
3267 }
3268 vim_free(name);
3269 }
3270
3271 // return only the autocmds for the specified pattern
3272 if (dict_has_key(argvars[0].vval.v_dict, "pattern"))
3273 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003274 pat = dict_get_string(argvars[0].vval.v_dict, "pattern", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003275 if (pat == NULL)
3276 return;
3277 }
3278 }
3279
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003280 event_list = rettv->vval.v_list;
3281
3282 // iterate through all the autocmd events
3283 for (event = (event_T)0; (int)event < NUM_EVENTS;
3284 event = (event_T)((int)event + 1))
3285 {
3286 if (event_arg != NUM_EVENTS && event != event_arg)
3287 continue;
3288
3289 event_name = event_nr2name(event);
3290
3291 // iterate through all the patterns for this autocmd event
3292 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
3293 {
3294 char_u *group_name;
3295
3296 if (group != AUGROUP_ALL && group != ap->group)
3297 continue;
3298
3299 if (pat != NULL && STRCMP(pat, ap->pat) != 0)
3300 continue;
3301
3302 group_name = get_augroup_name(NULL, ap->group);
3303
3304 // iterate through all the commands for this pattern and add one
3305 // item for each cmd.
3306 for (ac = ap->cmds; ac != NULL; ac = ac->next)
3307 {
3308 event_dict = dict_alloc();
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003309 if (event_dict == NULL
3310 || list_append_dict(event_list, event_dict) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003311 return;
3312
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003313 if (dict_add_string(event_dict, "event", event_name) == FAIL
3314 || dict_add_string(event_dict, "group",
3315 group_name == NULL ? (char_u *)""
3316 : group_name) == FAIL
3317 || (ap->buflocal_nr != 0
3318 && (dict_add_number(event_dict, "bufnr",
3319 ap->buflocal_nr) == FAIL))
3320 || dict_add_string(event_dict, "pattern",
3321 ap->pat) == FAIL
3322 || dict_add_string(event_dict, "cmd", ac->cmd) == FAIL
3323 || dict_add_bool(event_dict, "once", ac->once) == FAIL
3324 || dict_add_bool(event_dict, "nested",
3325 ac->nested) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003326 return;
3327 }
3328 }
3329 }
3330
3331 vim_free(pat);
3332}
3333
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01003334#endif