blob: 80ce8ca6c8e055436c18503697fa0d7a4b9f9a33 [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 Moolenaarcbcd9cb2020-11-07 16:58:59 +01001568 aco->save_prevwin_id = prevwin == NULL ? 0 : prevwin->w_id;
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001569 aco->save_State = State;
zeertzjqf2678472024-01-17 21:22:59 +01001570#ifdef FEAT_JOB_CHANNEL
1571 if (bt_prompt(curbuf))
1572 aco->save_prompt_insert = curbuf->b_prompt_insert;
1573#endif
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001574
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001575 if (win != NULL)
1576 {
1577 // There is a window for "buf" in the current tab page, make it the
1578 // curwin. This is preferred, it has the least side effects (esp. if
1579 // "buf" is curbuf).
Bram Moolenaare76062c2022-11-28 18:51:43 +00001580 aco->use_aucmd_win_idx = -1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001581 curwin = win;
1582 }
1583 else
1584 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001585 // There is no window for "buf", use "auc_win". To minimize the side
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001586 // effects, insert it in the current tab page.
1587 // Anything related to a window (e.g., setting folds) may have
1588 // unexpected results.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001589 aco->use_aucmd_win_idx = auc_idx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001590
Bram Moolenaare76062c2022-11-28 18:51:43 +00001591 win_init_popup_win(auc_win, buf);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001592
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001593 aco->globaldir = globaldir;
1594 globaldir = NULL;
1595
Bram Moolenaare76062c2022-11-28 18:51:43 +00001596 // Split the current window, put the auc_win in the upper half.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001597 // We don't want the BufEnter or WinEnter autocommands.
1598 block_autocmds();
1599 make_snapshot(SNAP_AUCMD_IDX);
1600 save_ea = p_ea;
1601 p_ea = FALSE;
1602
1603#ifdef FEAT_AUTOCHDIR
1604 // Prevent chdir() call in win_enter_ext(), through do_autochdir().
1605 save_acd = p_acd;
1606 p_acd = FALSE;
1607#endif
1608
Bram Moolenaare76062c2022-11-28 18:51:43 +00001609 (void)win_split_ins(0, WSP_TOP, auc_win, 0);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001610 (void)win_comp_pos(); // recompute window positions
1611 p_ea = save_ea;
1612#ifdef FEAT_AUTOCHDIR
1613 p_acd = save_acd;
1614#endif
1615 unblock_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001616 curwin = auc_win;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001617 }
1618 curbuf = buf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001619 aco->new_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001620 set_bufref(&aco->new_curbuf, curbuf);
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001621
1622 // disable the Visual area, the position may be invalid in another buffer
1623 aco->save_VIsual_active = VIsual_active;
1624 VIsual_active = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001625}
1626
1627/*
1628 * Cleanup after executing autocommands for a (hidden) buffer.
1629 * Restore the window as it was (if possible).
1630 */
1631 void
1632aucmd_restbuf(
1633 aco_save_T *aco) // structure holding saved values
1634{
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001635 int dummy;
1636 win_T *save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001637
Bram Moolenaare76062c2022-11-28 18:51:43 +00001638 if (aco->use_aucmd_win_idx >= 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001639 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001640 win_T *awp = aucmd_win[aco->use_aucmd_win_idx].auc_win;
1641
Bram Moolenaare76062c2022-11-28 18:51:43 +00001642 // Find "awp", it can't be closed, but it may be in another tab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001643 // page. Do not trigger autocommands here.
1644 block_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001645 if (curwin != awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001646 {
1647 tabpage_T *tp;
1648 win_T *wp;
1649
1650 FOR_ALL_TAB_WINDOWS(tp, wp)
1651 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001652 if (wp == awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001653 {
1654 if (tp != curtab)
1655 goto_tabpage_tp(tp, TRUE, TRUE);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001656 win_goto(awp);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001657 goto win_found;
1658 }
1659 }
1660 }
1661win_found:
zeertzjq46bdae02023-09-24 23:16:08 +02001662 --curbuf->b_nwindows;
orbitalcde8de02023-04-02 22:05:13 +01001663#ifdef FEAT_JOB_CHANNEL
zeertzjqa40c0bc2023-05-27 14:10:08 +01001664 int save_stop_insert_mode = stop_insert_mode;
orbitalcde8de02023-04-02 22:05:13 +01001665 // May need to stop Insert mode if we were in a prompt buffer.
1666 leaving_window(curwin);
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001667 // Do not stop Insert mode when already in Insert mode before.
1668 if (aco->save_State & MODE_INSERT)
zeertzjqa40c0bc2023-05-27 14:10:08 +01001669 stop_insert_mode = save_stop_insert_mode;
orbitalcde8de02023-04-02 22:05:13 +01001670#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001671 // Remove the window and frame from the tree of frames.
1672 (void)winframe_remove(curwin, &dummy, NULL);
1673 win_remove(curwin, NULL);
Bram Moolenaar84497cd2022-11-28 20:34:52 +00001674
1675 // The window is marked as not used, but it is not freed, it can be
1676 // used again.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001677 aucmd_win[aco->use_aucmd_win_idx].auc_win_used = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001678 last_status(FALSE); // may need to remove last status line
1679
1680 if (!valid_tabpage_win(curtab))
1681 // no valid window in current tabpage
1682 close_tabpage(curtab);
1683
1684 restore_snapshot(SNAP_AUCMD_IDX, FALSE);
1685 (void)win_comp_pos(); // recompute window positions
1686 unblock_autocmds();
1687
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001688 save_curwin = win_find_by_id(aco->save_curwin_id);
1689 if (save_curwin != NULL)
1690 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001691 else
1692 // Hmm, original window disappeared. Just use the first one.
1693 curwin = firstwin;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001694 curbuf = curwin->w_buffer;
1695#ifdef FEAT_JOB_CHANNEL
1696 // May need to restore insert mode for a prompt buffer.
1697 entering_window(curwin);
zeertzjqf2678472024-01-17 21:22:59 +01001698 if (bt_prompt(curbuf))
1699 curbuf->b_prompt_insert = aco->save_prompt_insert;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001700#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001701 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001702#ifdef FEAT_EVAL
Bram Moolenaare76062c2022-11-28 18:51:43 +00001703 vars_clear(&awp->w_vars->dv_hashtab); // free all w: variables
1704 hash_init(&awp->w_vars->dv_hashtab); // re-use the hashtab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001705#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001706 vim_free(globaldir);
1707 globaldir = aco->globaldir;
1708
1709 // the buffer contents may have changed
zeertzjq49f05242023-02-04 10:58:34 +00001710 VIsual_active = aco->save_VIsual_active;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001711 check_cursor();
1712 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1713 {
1714 curwin->w_topline = curbuf->b_ml.ml_line_count;
1715#ifdef FEAT_DIFF
1716 curwin->w_topfill = 0;
1717#endif
1718 }
1719#if defined(FEAT_GUI)
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001720 if (gui.in_use)
1721 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001722 // Hide the scrollbars from the "awp" and update.
1723 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_LEFT], FALSE);
1724 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_RIGHT], FALSE);
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001725 gui_may_update_scrollbars();
1726 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001727#endif
1728 }
1729 else
1730 {
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001731 // Restore curwin. Use the window ID, a window may have been closed
1732 // and the memory re-used for another one.
1733 save_curwin = win_find_by_id(aco->save_curwin_id);
1734 if (save_curwin != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001735 {
1736 // Restore the buffer which was previously edited by curwin, if
1737 // it was changed, we are still the same window and the buffer is
1738 // valid.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001739 if (curwin->w_id == aco->new_curwin_id
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001740 && curbuf != aco->new_curbuf.br_buf
1741 && bufref_valid(&aco->new_curbuf)
1742 && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
1743 {
1744# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
1745 if (curwin->w_s == &curbuf->b_s)
1746 curwin->w_s = &aco->new_curbuf.br_buf->b_s;
1747# endif
1748 --curbuf->b_nwindows;
1749 curbuf = aco->new_curbuf.br_buf;
1750 curwin->w_buffer = curbuf;
1751 ++curbuf->b_nwindows;
1752 }
1753
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001754 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001755 curbuf = curwin->w_buffer;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001756 prevwin = win_find_by_id(aco->save_prevwin_id);
zeertzjq49f05242023-02-04 10:58:34 +00001757
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001758 // In case the autocommand moves the cursor to a position that
1759 // does not exist in curbuf.
zeertzjq49f05242023-02-04 10:58:34 +00001760 VIsual_active = aco->save_VIsual_active;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001761 check_cursor();
1762 }
1763 }
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001764
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001765 VIsual_active = aco->save_VIsual_active;
zeertzjq49f05242023-02-04 10:58:34 +00001766 check_cursor(); // just in case lines got deleted
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001767 if (VIsual_active)
1768 check_pos(curbuf, &VIsual);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001769}
1770
1771static int autocmd_nested = FALSE;
1772
1773/*
1774 * Execute autocommands for "event" and file name "fname".
1775 * Return TRUE if some commands were executed.
1776 */
1777 int
1778apply_autocmds(
1779 event_T event,
1780 char_u *fname, // NULL or empty means use actual file name
1781 char_u *fname_io, // fname to use for <afile> on cmdline
1782 int force, // when TRUE, ignore autocmd_busy
1783 buf_T *buf) // buffer for <abuf>
1784{
1785 return apply_autocmds_group(event, fname, fname_io, force,
1786 AUGROUP_ALL, buf, NULL);
1787}
1788
1789/*
1790 * Like apply_autocmds(), but with extra "eap" argument. This takes care of
1791 * setting v:filearg.
1792 */
1793 int
1794apply_autocmds_exarg(
1795 event_T event,
1796 char_u *fname,
1797 char_u *fname_io,
1798 int force,
1799 buf_T *buf,
1800 exarg_T *eap)
1801{
1802 return apply_autocmds_group(event, fname, fname_io, force,
1803 AUGROUP_ALL, buf, eap);
1804}
1805
1806/*
1807 * Like apply_autocmds(), but handles the caller's retval. If the script
1808 * processing is being aborted or if retval is FAIL when inside a try
1809 * conditional, no autocommands are executed. If otherwise the autocommands
1810 * cause the script to be aborted, retval is set to FAIL.
1811 */
1812 int
1813apply_autocmds_retval(
1814 event_T event,
1815 char_u *fname, // NULL or empty means use actual file name
1816 char_u *fname_io, // fname to use for <afile> on cmdline
1817 int force, // when TRUE, ignore autocmd_busy
1818 buf_T *buf, // buffer for <abuf>
1819 int *retval) // pointer to caller's retval
1820{
1821 int did_cmd;
1822
1823#ifdef FEAT_EVAL
1824 if (should_abort(*retval))
1825 return FALSE;
1826#endif
1827
1828 did_cmd = apply_autocmds_group(event, fname, fname_io, force,
1829 AUGROUP_ALL, buf, NULL);
1830 if (did_cmd
1831#ifdef FEAT_EVAL
1832 && aborting()
1833#endif
1834 )
1835 *retval = FAIL;
1836 return did_cmd;
1837}
1838
1839/*
1840 * Return TRUE when there is a CursorHold autocommand defined.
1841 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001842 static int
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001843has_cursorhold(void)
1844{
Bram Moolenaar24959102022-05-07 20:01:16 +01001845 return (first_autopat[(int)(get_real_state() == MODE_NORMAL_BUSY
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001846 ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
1847}
1848
1849/*
1850 * Return TRUE if the CursorHold event can be triggered.
1851 */
1852 int
1853trigger_cursorhold(void)
1854{
1855 int state;
1856
1857 if (!did_cursorhold
1858 && has_cursorhold()
1859 && reg_recording == 0
1860 && typebuf.tb_len == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001861 && !ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001862 {
1863 state = get_real_state();
Bram Moolenaar24959102022-05-07 20:01:16 +01001864 if (state == MODE_NORMAL_BUSY || (state & MODE_INSERT) != 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001865 return TRUE;
1866 }
1867 return FALSE;
1868}
1869
1870/*
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001871 * Return TRUE when there is a WinResized autocommand defined.
1872 */
1873 int
1874has_winresized(void)
1875{
1876 return (first_autopat[(int)EVENT_WINRESIZED] != NULL);
1877}
1878
1879/*
LemonBoy09371822022-04-08 15:18:45 +01001880 * Return TRUE when there is a WinScrolled autocommand defined.
1881 */
1882 int
1883has_winscrolled(void)
1884{
1885 return (first_autopat[(int)EVENT_WINSCROLLED] != NULL);
1886}
1887
1888/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001889 * Return TRUE when there is a CursorMoved autocommand defined.
1890 */
1891 int
1892has_cursormoved(void)
1893{
1894 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1895}
1896
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001897/*
1898 * Return TRUE when there is a CursorMovedI autocommand defined.
1899 */
1900 int
1901has_cursormovedI(void)
1902{
1903 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1904}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001905
1906/*
1907 * Return TRUE when there is a TextChanged autocommand defined.
1908 */
1909 int
1910has_textchanged(void)
1911{
1912 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1913}
1914
1915/*
1916 * Return TRUE when there is a TextChangedI autocommand defined.
1917 */
1918 int
1919has_textchangedI(void)
1920{
1921 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
1922}
1923
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001924/*
1925 * Return TRUE when there is a TextChangedP autocommand defined.
1926 */
1927 int
1928has_textchangedP(void)
1929{
1930 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
1931}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001932
1933/*
1934 * Return TRUE when there is an InsertCharPre autocommand defined.
1935 */
1936 int
1937has_insertcharpre(void)
1938{
1939 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
1940}
1941
1942/*
1943 * Return TRUE when there is an CmdUndefined autocommand defined.
1944 */
1945 int
1946has_cmdundefined(void)
1947{
1948 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
1949}
1950
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001951#if defined(FEAT_EVAL) || defined(PROTO)
1952/*
1953 * Return TRUE when there is a TextYankPost autocommand defined.
1954 */
1955 int
1956has_textyankpost(void)
1957{
1958 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
1959}
1960#endif
1961
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001962#if defined(FEAT_EVAL) || defined(PROTO)
1963/*
1964 * Return TRUE when there is a CompleteChanged autocommand defined.
1965 */
1966 int
1967has_completechanged(void)
1968{
1969 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
1970}
1971#endif
1972
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02001973#if defined(FEAT_EVAL) || defined(PROTO)
1974/*
1975 * Return TRUE when there is a ModeChanged autocommand defined.
1976 */
1977 int
1978has_modechanged(void)
1979{
1980 return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
1981}
1982#endif
1983
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001984/*
1985 * Execute autocommands for "event" and file name "fname".
1986 * Return TRUE if some commands were executed.
1987 */
1988 static int
1989apply_autocmds_group(
1990 event_T event,
1991 char_u *fname, // NULL or empty means use actual file name
1992 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
1993 // use fname
1994 int force, // when TRUE, ignore autocmd_busy
1995 int group, // group ID, or AUGROUP_ALL
1996 buf_T *buf, // buffer for <abuf>
1997 exarg_T *eap UNUSED) // command arguments
1998{
1999 char_u *sfname = NULL; // short file name
2000 char_u *tail;
2001 int save_changed;
2002 buf_T *old_curbuf;
2003 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002004 char_u *save_autocmd_fname;
2005 int save_autocmd_fname_full;
2006 int save_autocmd_bufnr;
2007 char_u *save_autocmd_match;
2008 int save_autocmd_busy;
2009 int save_autocmd_nested;
2010 static int nesting = 0;
LemonBoyeca7c602022-04-14 15:39:43 +01002011 AutoPatCmd_T patcmd;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002012 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002013 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002014#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002015 funccal_entry_T funccal_entry;
2016 char_u *save_cmdarg;
2017 long save_cmdbang;
2018#endif
2019 static int filechangeshell_busy = FALSE;
2020#ifdef FEAT_PROFILE
2021 proftime_T wait_time;
2022#endif
2023 int did_save_redobuff = FALSE;
2024 save_redo_T save_redo;
2025 int save_KeyTyped = KeyTyped;
ichizok7e5fe382023-04-15 13:17:50 +01002026 ESTACK_CHECK_DECLARATION;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002027
2028 /*
2029 * Quickly return if there are no autocommands for this event or
2030 * autocommands are blocked.
2031 */
2032 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
2033 || autocmd_blocked > 0)
2034 goto BYPASS_AU;
2035
2036 /*
2037 * When autocommands are busy, new autocommands are only executed when
2038 * explicitly enabled with the "nested" flag.
2039 */
2040 if (autocmd_busy && !(force || autocmd_nested))
2041 goto BYPASS_AU;
2042
2043#ifdef FEAT_EVAL
2044 /*
2045 * Quickly return when immediately aborting on error, or when an interrupt
2046 * occurred or an exception was thrown but not caught.
2047 */
2048 if (aborting())
2049 goto BYPASS_AU;
2050#endif
2051
2052 /*
2053 * FileChangedShell never nests, because it can create an endless loop.
2054 */
2055 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
2056 || event == EVENT_FILECHANGEDSHELLPOST))
2057 goto BYPASS_AU;
2058
2059 /*
2060 * Ignore events in 'eventignore'.
2061 */
2062 if (event_ignored(event))
2063 goto BYPASS_AU;
2064
2065 /*
2066 * Allow nesting of autocommands, but restrict the depth, because it's
2067 * possible to create an endless loop.
2068 */
2069 if (nesting == 10)
2070 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00002071 emsg(_(e_autocommand_nesting_too_deep));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002072 goto BYPASS_AU;
2073 }
2074
2075 /*
2076 * Check if these autocommands are disabled. Used when doing ":all" or
2077 * ":ball".
2078 */
2079 if ( (autocmd_no_enter
2080 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
2081 || (autocmd_no_leave
2082 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
2083 goto BYPASS_AU;
2084
Bram Moolenaarbb393d82022-12-09 12:21:50 +00002085 if (event == EVENT_CMDLINECHANGED)
2086 ++aucmd_cmdline_changed_count;
2087
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002088 /*
2089 * Save the autocmd_* variables and info about the current buffer.
2090 */
2091 save_autocmd_fname = autocmd_fname;
2092 save_autocmd_fname_full = autocmd_fname_full;
2093 save_autocmd_bufnr = autocmd_bufnr;
2094 save_autocmd_match = autocmd_match;
2095 save_autocmd_busy = autocmd_busy;
2096 save_autocmd_nested = autocmd_nested;
2097 save_changed = curbuf->b_changed;
2098 old_curbuf = curbuf;
2099
2100 /*
2101 * Set the file name to be used for <afile>.
2102 * Make a copy to avoid that changing a buffer name or directory makes it
2103 * invalid.
2104 */
2105 if (fname_io == NULL)
2106 {
2107 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
Bram Moolenaarbb393d82022-12-09 12:21:50 +00002108 || event == EVENT_OPTIONSET
Danek Duvalld7d56032024-01-14 20:19:59 +01002109 || event == EVENT_MODECHANGED
2110 || event == EVENT_TERMRESPONSEALL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002111 autocmd_fname = NULL;
2112 else if (fname != NULL && !ends_excmd(*fname))
2113 autocmd_fname = fname;
2114 else if (buf != NULL)
2115 autocmd_fname = buf->b_ffname;
2116 else
2117 autocmd_fname = NULL;
2118 }
2119 else
2120 autocmd_fname = fname_io;
2121 if (autocmd_fname != NULL)
2122 autocmd_fname = vim_strsave(autocmd_fname);
2123 autocmd_fname_full = FALSE; // call FullName_save() later
2124
2125 /*
2126 * Set the buffer number to be used for <abuf>.
2127 */
2128 if (buf == NULL)
2129 autocmd_bufnr = 0;
2130 else
2131 autocmd_bufnr = buf->b_fnum;
2132
2133 /*
2134 * When the file name is NULL or empty, use the file name of buffer "buf".
2135 * Always use the full path of the file name to match with, in case
2136 * "allow_dirs" is set.
2137 */
2138 if (fname == NULL || *fname == NUL)
2139 {
2140 if (buf == NULL)
2141 fname = NULL;
2142 else
2143 {
2144#ifdef FEAT_SYN_HL
2145 if (event == EVENT_SYNTAX)
2146 fname = buf->b_p_syn;
2147 else
2148#endif
2149 if (event == EVENT_FILETYPE)
2150 fname = buf->b_p_ft;
2151 else
2152 {
2153 if (buf->b_sfname != NULL)
2154 sfname = vim_strsave(buf->b_sfname);
2155 fname = buf->b_ffname;
2156 }
2157 }
2158 if (fname == NULL)
2159 fname = (char_u *)"";
2160 fname = vim_strsave(fname); // make a copy, so we can change it
2161 }
2162 else
2163 {
2164 sfname = vim_strsave(fname);
2165 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002166 // ColorScheme, QuickFixCmd*, DirChanged and similar.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002167 if (event == EVENT_FILETYPE
2168 || event == EVENT_SYNTAX
2169 || event == EVENT_CMDLINECHANGED
2170 || event == EVENT_CMDLINEENTER
2171 || event == EVENT_CMDLINELEAVE
2172 || event == EVENT_CMDWINENTER
2173 || event == EVENT_CMDWINLEAVE
2174 || event == EVENT_CMDUNDEFINED
2175 || event == EVENT_FUNCUNDEFINED
2176 || event == EVENT_REMOTEREPLY
2177 || event == EVENT_SPELLFILEMISSING
2178 || event == EVENT_QUICKFIXCMDPRE
2179 || event == EVENT_COLORSCHEME
2180 || event == EVENT_COLORSCHEMEPRE
2181 || event == EVENT_OPTIONSET
2182 || event == EVENT_QUICKFIXCMDPOST
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002183 || event == EVENT_DIRCHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002184 || event == EVENT_DIRCHANGEDPRE
naohiro ono23beefe2021-11-13 12:38:49 +00002185 || event == EVENT_MODECHANGED
zeertzjqc601d982022-10-10 13:46:15 +01002186 || event == EVENT_MENUPOPUP
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002187 || event == EVENT_USER
LemonBoy09371822022-04-08 15:18:45 +01002188 || event == EVENT_WINCLOSED
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00002189 || event == EVENT_WINRESIZED
Danek Duvalld7d56032024-01-14 20:19:59 +01002190 || event == EVENT_WINSCROLLED
2191 || event == EVENT_TERMRESPONSEALL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002192 {
2193 fname = vim_strsave(fname);
2194 autocmd_fname_full = TRUE; // don't expand it later
2195 }
2196 else
2197 fname = FullName_save(fname, FALSE);
2198 }
2199 if (fname == NULL) // out of memory
2200 {
2201 vim_free(sfname);
2202 retval = FALSE;
2203 goto BYPASS_AU;
2204 }
2205
2206#ifdef BACKSLASH_IN_FILENAME
2207 /*
2208 * Replace all backslashes with forward slashes. This makes the
2209 * autocommand patterns portable between Unix and MS-DOS.
2210 */
2211 if (sfname != NULL)
2212 forward_slash(sfname);
2213 forward_slash(fname);
2214#endif
2215
2216#ifdef VMS
2217 // remove version for correct match
2218 if (sfname != NULL)
2219 vms_remove_version(sfname);
2220 vms_remove_version(fname);
2221#endif
2222
2223 /*
2224 * Set the name to be used for <amatch>.
2225 */
2226 autocmd_match = fname;
2227
2228
2229 // Don't redraw while doing autocommands.
2230 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002231
2232 // name and lnum are filled in later
2233 estack_push(ETYPE_AUCMD, NULL, 0);
ichizok7e5fe382023-04-15 13:17:50 +01002234 ESTACK_CHECK_SETUP;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002235
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002236 save_current_sctx = current_sctx;
2237
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002238#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002239# ifdef FEAT_PROFILE
2240 if (do_profiling == PROF_YES)
2241 prof_child_enter(&wait_time); // doesn't count for the caller itself
2242# endif
2243
2244 // Don't use local function variables, if called from a function.
2245 save_funccal(&funccal_entry);
2246#endif
2247
2248 /*
2249 * When starting to execute autocommands, save the search patterns.
2250 */
2251 if (!autocmd_busy)
2252 {
2253 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002254 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002255 {
2256 saveRedobuff(&save_redo);
2257 did_save_redobuff = TRUE;
2258 }
2259 did_filetype = keep_filetype;
2260 }
2261
2262 /*
2263 * Note that we are applying autocmds. Some commands need to know.
2264 */
2265 autocmd_busy = TRUE;
2266 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2267 ++nesting; // see matching decrement below
2268
2269 // Remember that FileType was triggered. Used for did_filetype().
2270 if (event == EVENT_FILETYPE)
2271 did_filetype = TRUE;
2272
2273 tail = gettail(fname);
2274
2275 // Find first autocommand that matches
LemonBoyeca7c602022-04-14 15:39:43 +01002276 CLEAR_FIELD(patcmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002277 patcmd.curpat = first_autopat[(int)event];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002278 patcmd.group = group;
2279 patcmd.fname = fname;
2280 patcmd.sfname = sfname;
2281 patcmd.tail = tail;
2282 patcmd.event = event;
2283 patcmd.arg_bufnr = autocmd_bufnr;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002284 auto_next_pat(&patcmd, FALSE);
2285
2286 // found one, start executing the autocommands
2287 if (patcmd.curpat != NULL)
2288 {
2289 // add to active_apc_list
2290 patcmd.next = active_apc_list;
2291 active_apc_list = &patcmd;
2292
2293#ifdef FEAT_EVAL
2294 // set v:cmdarg (only when there is a matching pattern)
2295 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2296 if (eap != NULL)
2297 {
2298 save_cmdarg = set_cmdarg(eap, NULL);
2299 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2300 }
2301 else
2302 save_cmdarg = NULL; // avoid gcc warning
2303#endif
2304 retval = TRUE;
2305 // mark the last pattern, to avoid an endless loop when more patterns
2306 // are added when executing autocommands
2307 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2308 ap->last = FALSE;
2309 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002310
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002311 // Make sure cursor and topline are valid. The first time the current
2312 // values are saved, restored by reset_lnums(). When nested only the
2313 // values are corrected when needed.
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002314 if (nesting == 1)
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002315 check_lnums(TRUE);
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002316 else
2317 check_lnums_nested(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002318
Christian Brabandt590aae32023-06-25 22:34:22 +01002319 int save_did_emsg = did_emsg;
2320 int save_ex_pressedreturn = get_pressedreturn();
ichizokc3f91c02021-12-17 09:44:33 +00002321
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002322 do_cmdline(NULL, getnextac, (void *)&patcmd,
2323 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002324
ichizokc3f91c02021-12-17 09:44:33 +00002325 did_emsg += save_did_emsg;
Christian Brabandt590aae32023-06-25 22:34:22 +01002326 set_pressedreturn(save_ex_pressedreturn);
ichizokc3f91c02021-12-17 09:44:33 +00002327
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002328 if (nesting == 1)
2329 // restore cursor and topline, unless they were changed
2330 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002331
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002332#ifdef FEAT_EVAL
2333 if (eap != NULL)
2334 {
2335 (void)set_cmdarg(NULL, save_cmdarg);
2336 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2337 }
2338#endif
2339 // delete from active_apc_list
2340 if (active_apc_list == &patcmd) // just in case
2341 active_apc_list = patcmd.next;
2342 }
2343
Bram Moolenaar79cdf022023-05-20 14:07:00 +01002344 if (RedrawingDisabled > 0)
2345 --RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002346 autocmd_busy = save_autocmd_busy;
2347 filechangeshell_busy = FALSE;
2348 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002349 vim_free(SOURCING_NAME);
ichizok7e5fe382023-04-15 13:17:50 +01002350 ESTACK_CHECK_NOW;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002351 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002352 vim_free(autocmd_fname);
2353 autocmd_fname = save_autocmd_fname;
2354 autocmd_fname_full = save_autocmd_fname_full;
2355 autocmd_bufnr = save_autocmd_bufnr;
2356 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002357 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002358#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002359 restore_funccal();
2360# ifdef FEAT_PROFILE
2361 if (do_profiling == PROF_YES)
2362 prof_child_exit(&wait_time);
2363# endif
2364#endif
2365 KeyTyped = save_KeyTyped;
2366 vim_free(fname);
2367 vim_free(sfname);
2368 --nesting; // see matching increment above
2369
2370 /*
2371 * When stopping to execute autocommands, restore the search patterns and
2372 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2373 * free any windows in the au_pending_free_win list.
2374 */
2375 if (!autocmd_busy)
2376 {
2377 restore_search_patterns();
2378 if (did_save_redobuff)
2379 restoreRedobuff(&save_redo);
2380 did_filetype = FALSE;
2381 while (au_pending_free_buf != NULL)
2382 {
2383 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002384
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002385 vim_free(au_pending_free_buf);
2386 au_pending_free_buf = b;
2387 }
2388 while (au_pending_free_win != NULL)
2389 {
2390 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002391
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002392 vim_free(au_pending_free_win);
2393 au_pending_free_win = w;
2394 }
2395 }
2396
2397 /*
2398 * Some events don't set or reset the Changed flag.
2399 * Check if still in the same buffer!
2400 */
2401 if (curbuf == old_curbuf
2402 && (event == EVENT_BUFREADPOST
2403 || event == EVENT_BUFWRITEPOST
2404 || event == EVENT_FILEAPPENDPOST
2405 || event == EVENT_VIMLEAVE
2406 || event == EVENT_VIMLEAVEPRE))
2407 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002408 if (curbuf->b_changed != save_changed)
2409 need_maketitle = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002410 curbuf->b_changed = save_changed;
2411 }
2412
2413 au_cleanup(); // may really delete removed patterns/commands now
2414
2415BYPASS_AU:
2416 // When wiping out a buffer make sure all its buffer-local autocommands
2417 // are deleted.
2418 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2419 aubuflocal_remove(buf);
2420
2421 if (retval == OK && event == EVENT_FILETYPE)
2422 au_did_filetype = TRUE;
2423
2424 return retval;
2425}
2426
2427# ifdef FEAT_EVAL
2428static char_u *old_termresponse = NULL;
Danek Duvalld7d56032024-01-14 20:19:59 +01002429static char_u *old_termu7resp = NULL;
2430static char_u *old_termblinkresp = NULL;
2431static char_u *old_termrbgresp = NULL;
2432static char_u *old_termrfgresp = NULL;
2433static char_u *old_termstyleresp = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002434# endif
2435
2436/*
2437 * Block triggering autocommands until unblock_autocmd() is called.
2438 * Can be used recursively, so long as it's symmetric.
2439 */
2440 void
2441block_autocmds(void)
2442{
2443# ifdef FEAT_EVAL
2444 // Remember the value of v:termresponse.
2445 if (autocmd_blocked == 0)
Danek Duvalld7d56032024-01-14 20:19:59 +01002446 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002447 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
Danek Duvalld7d56032024-01-14 20:19:59 +01002448 old_termu7resp = get_vim_var_str(VV_TERMU7RESP);
2449 old_termblinkresp = get_vim_var_str(VV_TERMBLINKRESP);
2450 old_termrbgresp = get_vim_var_str(VV_TERMRBGRESP);
2451 old_termrfgresp = get_vim_var_str(VV_TERMRFGRESP);
2452 old_termstyleresp = get_vim_var_str(VV_TERMSTYLERESP);
2453 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002454# endif
2455 ++autocmd_blocked;
2456}
2457
2458 void
2459unblock_autocmds(void)
2460{
2461 --autocmd_blocked;
2462
2463# ifdef FEAT_EVAL
Danek Duvalld7d56032024-01-14 20:19:59 +01002464 // When v:termresponse, etc, were set while autocommands were blocked,
2465 // trigger the autocommands now. Esp. useful when executing a shell
2466 // command during startup (vimdiff).
2467 if (autocmd_blocked == 0)
2468 {
2469 if (get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2470 {
2471 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2472 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"version", NULL, FALSE, curbuf);
2473 }
2474 if (get_vim_var_str(VV_TERMU7RESP) != old_termu7resp)
2475 {
2476 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"ambiguouswidth", NULL, FALSE, curbuf);
2477 }
2478 if (get_vim_var_str(VV_TERMBLINKRESP) != old_termblinkresp)
2479 {
2480 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"cursorblink", NULL, FALSE, curbuf);
2481 }
2482 if (get_vim_var_str(VV_TERMRBGRESP) != old_termrbgresp)
2483 {
2484 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"background", NULL, FALSE, curbuf);
2485 }
2486 if (get_vim_var_str(VV_TERMRFGRESP) != old_termrfgresp)
2487 {
2488 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"foreground", NULL, FALSE, curbuf);
2489 }
2490 if (get_vim_var_str(VV_TERMSTYLERESP) != old_termstyleresp)
2491 {
2492 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"cursorshape", NULL, FALSE, curbuf);
2493 }
2494 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002495# endif
2496}
2497
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002498 int
2499is_autocmd_blocked(void)
2500{
2501 return autocmd_blocked != 0;
2502}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002503
2504/*
2505 * Find next autocommand pattern that matches.
2506 */
2507 static void
2508auto_next_pat(
LemonBoyeca7c602022-04-14 15:39:43 +01002509 AutoPatCmd_T *apc,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002510 int stop_at_last) // stop when 'last' flag is set
2511{
2512 AutoPat *ap;
2513 AutoCmd *cp;
2514 char_u *name;
2515 char *s;
LemonBoyeca7c602022-04-14 15:39:43 +01002516 estack_T *entry;
2517 char_u *namep;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002518
LemonBoyeca7c602022-04-14 15:39:43 +01002519 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
2520
2521 // Clear the exestack entry for this ETYPE_AUCMD entry.
2522 VIM_CLEAR(entry->es_name);
2523 entry->es_info.aucmd = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002524
2525 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2526 {
2527 apc->curpat = NULL;
2528
2529 // Only use a pattern when it has not been removed, has commands and
2530 // the group matches. For buffer-local autocommands only check the
2531 // buffer number.
2532 if (ap->pat != NULL && ap->cmds != NULL
2533 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2534 {
2535 // execution-condition
2536 if (ap->buflocal_nr == 0
2537 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2538 apc->sfname, apc->tail, ap->allow_dirs))
2539 : ap->buflocal_nr == apc->arg_bufnr)
2540 {
2541 name = event_nr2name(apc->event);
2542 s = _("%s Autocommands for \"%s\"");
LemonBoyeca7c602022-04-14 15:39:43 +01002543 namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1);
2544 if (namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002545 {
LemonBoyeca7c602022-04-14 15:39:43 +01002546 sprintf((char *)namep, s, (char *)name, (char *)ap->pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002547 if (p_verbose >= 8)
2548 {
2549 verbose_enter();
LemonBoyeca7c602022-04-14 15:39:43 +01002550 smsg(_("Executing %s"), namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002551 verbose_leave();
2552 }
2553 }
2554
LemonBoyeca7c602022-04-14 15:39:43 +01002555 // Update the exestack entry for this autocmd.
2556 entry->es_name = namep;
2557 entry->es_info.aucmd = apc;
2558
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002559 apc->curpat = ap;
2560 apc->nextcmd = ap->cmds;
2561 // mark last command
2562 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2563 cp->last = FALSE;
2564 cp->last = TRUE;
2565 }
2566 line_breakcheck();
2567 if (apc->curpat != NULL) // found a match
2568 break;
2569 }
2570 if (stop_at_last && ap->last)
2571 break;
2572 }
2573}
2574
Dominique Pellee764d1b2023-03-12 21:20:59 +00002575#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002576/*
LemonBoyeca7c602022-04-14 15:39:43 +01002577 * Get the script context where autocommand "acp" is defined.
2578 */
2579 sctx_T *
2580acp_script_ctx(AutoPatCmd_T *acp)
2581{
2582 return &acp->script_ctx;
2583}
Dominique Pellee764d1b2023-03-12 21:20:59 +00002584#endif
LemonBoyeca7c602022-04-14 15:39:43 +01002585
2586/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002587 * Get next autocommand command.
2588 * Called by do_cmdline() to get the next line for ":if".
2589 * Returns allocated string, or NULL for end of autocommands.
2590 */
2591 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002592getnextac(
2593 int c UNUSED,
2594 void *cookie,
2595 int indent UNUSED,
2596 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002597{
LemonBoyeca7c602022-04-14 15:39:43 +01002598 AutoPatCmd_T *acp = (AutoPatCmd_T *)cookie;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002599 char_u *retval;
2600 AutoCmd *ac;
2601
2602 // Can be called again after returning the last line.
2603 if (acp->curpat == NULL)
2604 return NULL;
2605
2606 // repeat until we find an autocommand to execute
2607 for (;;)
2608 {
2609 // skip removed commands
2610 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2611 if (acp->nextcmd->last)
2612 acp->nextcmd = NULL;
2613 else
2614 acp->nextcmd = acp->nextcmd->next;
2615
2616 if (acp->nextcmd != NULL)
2617 break;
2618
2619 // at end of commands, find next pattern that matches
2620 if (acp->curpat->last)
2621 acp->curpat = NULL;
2622 else
2623 acp->curpat = acp->curpat->next;
2624 if (acp->curpat != NULL)
2625 auto_next_pat(acp, TRUE);
2626 if (acp->curpat == NULL)
2627 return NULL;
2628 }
2629
2630 ac = acp->nextcmd;
2631
2632 if (p_verbose >= 9)
2633 {
2634 verbose_enter_scroll();
2635 smsg(_("autocommand %s"), ac->cmd);
2636 msg_puts("\n"); // don't overwrite this either
2637 verbose_leave_scroll();
2638 }
2639 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002640 // Remove one-shot ("once") autocmd in anticipation of its execution.
2641 if (ac->once)
2642 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002643 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002644 current_sctx = ac->script_ctx;
LemonBoyeca7c602022-04-14 15:39:43 +01002645 acp->script_ctx = current_sctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002646 if (ac->last)
2647 acp->nextcmd = NULL;
2648 else
2649 acp->nextcmd = ac->next;
2650 return retval;
2651}
2652
2653/*
2654 * Return TRUE if there is a matching autocommand for "fname".
2655 * To account for buffer-local autocommands, function needs to know
2656 * in which buffer the file will be opened.
2657 */
2658 int
2659has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2660{
2661 AutoPat *ap;
2662 char_u *fname;
2663 char_u *tail = gettail(sfname);
2664 int retval = FALSE;
2665
2666 fname = FullName_save(sfname, FALSE);
2667 if (fname == NULL)
2668 return FALSE;
2669
2670#ifdef BACKSLASH_IN_FILENAME
2671 /*
2672 * Replace all backslashes with forward slashes. This makes the
2673 * autocommand patterns portable between Unix and MS-DOS.
2674 */
2675 sfname = vim_strsave(sfname);
2676 if (sfname != NULL)
2677 forward_slash(sfname);
2678 forward_slash(fname);
2679#endif
2680
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002681 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002682 if (ap->pat != NULL && ap->cmds != NULL
2683 && (ap->buflocal_nr == 0
2684 ? match_file_pat(NULL, &ap->reg_prog,
2685 fname, sfname, tail, ap->allow_dirs)
2686 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2687 ))
2688 {
2689 retval = TRUE;
2690 break;
2691 }
2692
2693 vim_free(fname);
2694#ifdef BACKSLASH_IN_FILENAME
2695 vim_free(sfname);
2696#endif
2697
2698 return retval;
2699}
2700
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002701/*
2702 * Function given to ExpandGeneric() to obtain the list of autocommand group
2703 * names.
2704 */
2705 char_u *
2706get_augroup_name(expand_T *xp UNUSED, int idx)
2707{
2708 if (idx == augroups.ga_len) // add "END" add the end
2709 return (char_u *)"END";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002710 if (idx < 0 || idx >= augroups.ga_len) // end of list
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002711 return NULL;
2712 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2713 // skip deleted entries
2714 return (char_u *)"";
2715 return AUGROUP_NAME(idx); // return a name
2716}
2717
2718static int include_groups = FALSE;
2719
2720 char_u *
2721set_context_in_autocmd(
2722 expand_T *xp,
2723 char_u *arg,
2724 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2725{
2726 char_u *p;
2727 int group;
2728
2729 // check for a group name, skip it if present
2730 include_groups = FALSE;
2731 p = arg;
2732 group = au_get_grouparg(&arg);
2733 if (group == AUGROUP_ERROR)
2734 return NULL;
2735 // If there only is a group name that's what we expand.
2736 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2737 {
2738 arg = p;
2739 group = AUGROUP_ALL;
2740 }
2741
2742 // skip over event name
2743 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2744 if (*p == ',')
2745 arg = p + 1;
2746 if (*p == NUL)
2747 {
2748 if (group == AUGROUP_ALL)
2749 include_groups = TRUE;
2750 xp->xp_context = EXPAND_EVENTS; // expand event name
2751 xp->xp_pattern = arg;
2752 return NULL;
2753 }
2754
2755 // skip over pattern
2756 arg = skipwhite(p);
2757 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2758 arg++;
2759 if (*arg)
2760 return arg; // expand (next) command
2761
2762 if (doautocmd)
2763 xp->xp_context = EXPAND_FILES; // expand file names
2764 else
2765 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2766 return NULL;
2767}
2768
2769/*
2770 * Function given to ExpandGeneric() to obtain the list of event names.
2771 */
2772 char_u *
2773get_event_name(expand_T *xp UNUSED, int idx)
2774{
2775 if (idx < augroups.ga_len) // First list group names, if wanted
2776 {
2777 if (!include_groups || AUGROUP_NAME(idx) == NULL
2778 || AUGROUP_NAME(idx) == get_deleted_augroup())
2779 return (char_u *)""; // skip deleted entries
2780 return AUGROUP_NAME(idx); // return a name
2781 }
2782 return (char_u *)event_names[idx - augroups.ga_len].name;
2783}
2784
Yee Cheng Chin900894b2023-09-29 20:42:32 +02002785/*
2786 * Function given to ExpandGeneric() to obtain the list of event names. Don't
2787 * include groups.
2788 */
2789 char_u *
2790get_event_name_no_group(expand_T *xp UNUSED, int idx)
2791{
2792 return (char_u *)event_names[idx].name;
2793}
2794
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002795
2796#if defined(FEAT_EVAL) || defined(PROTO)
2797/*
2798 * Return TRUE if autocmd is supported.
2799 */
2800 int
2801autocmd_supported(char_u *name)
2802{
2803 char_u *p;
2804
2805 return (event_name2nr(name, &p) != NUM_EVENTS);
2806}
2807
2808/*
2809 * Return TRUE if an autocommand is defined for a group, event and
2810 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2811 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2812 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2813 * Used for:
2814 * exists("#Group") or
2815 * exists("#Group#Event") or
2816 * exists("#Group#Event#pat") or
2817 * exists("#Event") or
2818 * exists("#Event#pat")
2819 */
2820 int
2821au_exists(char_u *arg)
2822{
2823 char_u *arg_save;
2824 char_u *pattern = NULL;
2825 char_u *event_name;
2826 char_u *p;
2827 event_T event;
2828 AutoPat *ap;
2829 buf_T *buflocal_buf = NULL;
2830 int group;
2831 int retval = FALSE;
2832
2833 // Make a copy so that we can change the '#' chars to a NUL.
2834 arg_save = vim_strsave(arg);
2835 if (arg_save == NULL)
2836 return FALSE;
2837 p = vim_strchr(arg_save, '#');
2838 if (p != NULL)
2839 *p++ = NUL;
2840
2841 // First, look for an autocmd group name
2842 group = au_find_group(arg_save);
2843 if (group == AUGROUP_ERROR)
2844 {
2845 // Didn't match a group name, assume the first argument is an event.
2846 group = AUGROUP_ALL;
2847 event_name = arg_save;
2848 }
2849 else
2850 {
2851 if (p == NULL)
2852 {
2853 // "Group": group name is present and it's recognized
2854 retval = TRUE;
2855 goto theend;
2856 }
2857
2858 // Must be "Group#Event" or "Group#Event#pat".
2859 event_name = p;
2860 p = vim_strchr(event_name, '#');
2861 if (p != NULL)
2862 *p++ = NUL; // "Group#Event#pat"
2863 }
2864
2865 pattern = p; // "pattern" is NULL when there is no pattern
2866
2867 // find the index (enum) for the event name
2868 event = event_name2nr(event_name, &p);
2869
2870 // return FALSE if the event name is not recognized
2871 if (event == NUM_EVENTS)
2872 goto theend;
2873
2874 // Find the first autocommand for this event.
2875 // If there isn't any, return FALSE;
2876 // If there is one and no pattern given, return TRUE;
2877 ap = first_autopat[(int)event];
2878 if (ap == NULL)
2879 goto theend;
2880
2881 // if pattern is "<buffer>", special handling is needed which uses curbuf
2882 // for pattern "<buffer=N>, fnamecmp() will work fine
2883 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2884 buflocal_buf = curbuf;
2885
2886 // Check if there is an autocommand with the given pattern.
2887 for ( ; ap != NULL; ap = ap->next)
2888 // only use a pattern when it has not been removed and has commands.
2889 // For buffer-local autocommands, fnamecmp() works fine.
2890 if (ap->pat != NULL && ap->cmds != NULL
2891 && (group == AUGROUP_ALL || ap->group == group)
2892 && (pattern == NULL
2893 || (buflocal_buf == NULL
2894 ? fnamecmp(ap->pat, pattern) == 0
2895 : ap->buflocal_nr == buflocal_buf->b_fnum)))
2896 {
2897 retval = TRUE;
2898 break;
2899 }
2900
2901theend:
2902 vim_free(arg_save);
2903 return retval;
2904}
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002905
2906/*
2907 * autocmd_add() and autocmd_delete() functions
2908 */
2909 static void
2910autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
2911{
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002912 list_T *aucmd_list;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002913 listitem_T *li;
2914 dict_T *event_dict;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002915 dictitem_T *di;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002916 char_u *event_name = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002917 list_T *event_list;
2918 listitem_T *eli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002919 event_T event;
2920 char_u *group_name = NULL;
2921 int group;
2922 char_u *pat = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002923 list_T *pat_list;
2924 listitem_T *pli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002925 char_u *cmd = NULL;
2926 char_u *end;
2927 int once;
2928 int nested;
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01002929 int replace; // replace the cmd for a group/event
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002930 int retval = VVAL_TRUE;
2931 int save_augroup = current_augroup;
2932
2933 rettv->v_type = VAR_BOOL;
2934 rettv->vval.v_number = VVAL_FALSE;
2935
2936 if (check_for_list_arg(argvars, 0) == FAIL)
2937 return;
2938
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002939 aucmd_list = argvars[0].vval.v_list;
2940 if (aucmd_list == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002941 return;
2942
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002943 FOR_ALL_LIST_ITEMS(aucmd_list, li)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002944 {
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002945 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002946 VIM_CLEAR(cmd);
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002947 event_name = NULL;
2948 event_list = NULL;
2949 pat = NULL;
2950 pat_list = NULL;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002951
2952 if (li->li_tv.v_type != VAR_DICT)
2953 continue;
2954
2955 event_dict = li->li_tv.vval.v_dict;
2956 if (event_dict == NULL)
2957 continue;
2958
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002959 di = dict_find(event_dict, (char_u *)"event", -1);
2960 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002961 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002962 if (di->di_tv.v_type == VAR_STRING)
2963 {
2964 event_name = di->di_tv.vval.v_string;
2965 if (event_name == NULL)
2966 {
2967 emsg(_(e_string_required));
2968 continue;
2969 }
2970 }
2971 else if (di->di_tv.v_type == VAR_LIST)
2972 {
2973 event_list = di->di_tv.vval.v_list;
2974 if (event_list == NULL)
2975 {
2976 emsg(_(e_list_required));
2977 continue;
2978 }
2979 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002980 else
2981 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002982 emsg(_(e_string_or_list_expected));
2983 continue;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002984 }
2985 }
2986
Bram Moolenaard61efa52022-07-23 09:52:04 +01002987 group_name = dict_get_string(event_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002988 if (group_name == NULL || *group_name == NUL)
2989 // if the autocmd group name is not specified, then use the current
2990 // autocmd group
2991 group = current_augroup;
2992 else
2993 {
2994 group = au_find_group(group_name);
2995 if (group == AUGROUP_ERROR)
2996 {
2997 if (delete)
2998 {
2999 semsg(_(e_no_such_group_str), group_name);
3000 retval = VVAL_FALSE;
3001 break;
3002 }
3003 // group is not found, create it now
3004 group = au_new_group(group_name);
3005 if (group == AUGROUP_ERROR)
3006 {
3007 semsg(_(e_no_such_group_str), group_name);
3008 retval = VVAL_FALSE;
3009 break;
3010 }
3011
3012 current_augroup = group;
3013 }
3014 }
3015
3016 // if a buffer number is specified, then generate a pattern of the form
3017 // "<buffer=n>. Otherwise, use the pattern supplied by the user.
3018 if (dict_has_key(event_dict, "bufnr"))
3019 {
3020 varnumber_T bnum;
3021
Bram Moolenaard61efa52022-07-23 09:52:04 +01003022 bnum = dict_get_number_def(event_dict, "bufnr", -1);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003023 if (bnum == -1)
3024 continue;
3025
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003026 vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
3027 pat = IObuff;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003028 }
3029 else
3030 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003031 di = dict_find(event_dict, (char_u *)"pattern", -1);
3032 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003033 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003034 if (di->di_tv.v_type == VAR_STRING)
3035 {
3036 pat = di->di_tv.vval.v_string;
3037 if (pat == NULL)
3038 {
3039 emsg(_(e_string_required));
3040 continue;
3041 }
3042 }
3043 else if (di->di_tv.v_type == VAR_LIST)
3044 {
3045 pat_list = di->di_tv.vval.v_list;
3046 if (pat_list == NULL)
3047 {
3048 emsg(_(e_list_required));
3049 continue;
3050 }
3051 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003052 else
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003053 {
3054 emsg(_(e_string_or_list_expected));
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003055 continue;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003056 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003057 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003058 else if (delete)
3059 pat = (char_u *)"";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003060 }
3061
Bram Moolenaard61efa52022-07-23 09:52:04 +01003062 once = dict_get_bool(event_dict, "once", FALSE);
3063 nested = dict_get_bool(event_dict, "nested", FALSE);
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01003064 // if 'replace' is true, then remove all the commands associated with
3065 // this autocmd event/group and add the new command.
Bram Moolenaard61efa52022-07-23 09:52:04 +01003066 replace = dict_get_bool(event_dict, "replace", FALSE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003067
Bram Moolenaard61efa52022-07-23 09:52:04 +01003068 cmd = dict_get_string(event_dict, "cmd", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003069 if (cmd == NULL)
3070 {
3071 if (delete)
3072 cmd = vim_strsave((char_u *)"");
3073 else
3074 continue;
3075 }
3076
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003077 if (delete && (event_name == NULL
3078 || (event_name[0] == '*' && event_name[1] == NUL)))
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003079 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003080 // if the event name is not specified or '*', delete all the events
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003081 for (event = (event_T)0; (int)event < NUM_EVENTS;
3082 event = (event_T)((int)event + 1))
3083 {
3084 if (do_autocmd_event(event, pat, once, nested, cmd, delete,
3085 group, 0) == FAIL)
3086 {
3087 retval = VVAL_FALSE;
3088 break;
3089 }
3090 }
3091 }
3092 else
3093 {
Bram Moolenaar968443e2022-05-27 21:16:34 +01003094 char_u *p = NULL;
3095
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003096 eli = NULL;
3097 end = NULL;
3098 while (TRUE)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003099 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003100 if (event_list != NULL)
3101 {
3102 if (eli == NULL)
3103 eli = event_list->lv_first;
3104 else
3105 eli = eli->li_next;
3106 if (eli == NULL)
3107 break;
3108 if (eli->li_tv.v_type != VAR_STRING
Bram Moolenaar882476a2022-06-01 16:02:38 +01003109 || (p = eli->li_tv.vval.v_string) == NULL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003110 {
3111 emsg(_(e_string_required));
Bram Moolenaar882476a2022-06-01 16:02:38 +01003112 break;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003113 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003114 }
3115 else
3116 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003117 if (p == NULL)
3118 p = event_name;
3119 if (p == NULL || *p == NUL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003120 break;
3121 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003122
3123 event = event_name2nr(p, &end);
3124 if (event == NUM_EVENTS || *end != NUL)
3125 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003126 // this also catches something following a valid event name
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003127 semsg(_(e_no_such_event_str), p);
3128 retval = VVAL_FALSE;
3129 break;
3130 }
3131 if (pat != NULL)
3132 {
3133 if (do_autocmd_event(event, pat, once, nested, cmd,
3134 delete | replace, group, 0) == FAIL)
3135 {
3136 retval = VVAL_FALSE;
3137 break;
3138 }
3139 }
3140 else if (pat_list != NULL)
3141 {
3142 FOR_ALL_LIST_ITEMS(pat_list, pli)
3143 {
3144 if (pli->li_tv.v_type != VAR_STRING
3145 || pli->li_tv.vval.v_string == NULL)
3146 {
3147 emsg(_(e_string_required));
3148 continue;
3149 }
3150 if (do_autocmd_event(event,
3151 pli->li_tv.vval.v_string, once, nested,
3152 cmd, delete | replace, group, 0) ==
3153 FAIL)
3154 {
3155 retval = VVAL_FALSE;
3156 break;
3157 }
3158 }
3159 if (retval == VVAL_FALSE)
3160 break;
3161 }
3162 if (event_name != NULL)
3163 p = end;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003164 }
3165 }
3166
3167 // if only the autocmd group name is specified for delete and the
3168 // autocmd event, pattern and cmd are not specified, then delete the
3169 // autocmd group.
3170 if (delete && group_name != NULL &&
3171 (event_name == NULL || event_name[0] == NUL)
3172 && (pat == NULL || pat[0] == NUL)
3173 && (cmd == NULL || cmd[0] == NUL))
3174 au_del_group(group_name);
3175 }
3176
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003177 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003178 VIM_CLEAR(cmd);
3179
3180 current_augroup = save_augroup;
3181 rettv->vval.v_number = retval;
3182}
3183
3184/*
3185 * autocmd_add() function
3186 */
3187 void
3188f_autocmd_add(typval_T *argvars, typval_T *rettv)
3189{
3190 autocmd_add_or_delete(argvars, rettv, FALSE);
3191}
3192
3193/*
3194 * autocmd_delete() function
3195 */
3196 void
3197f_autocmd_delete(typval_T *argvars, typval_T *rettv)
3198{
3199 autocmd_add_or_delete(argvars, rettv, TRUE);
3200}
3201
3202/*
3203 * autocmd_get() function
3204 * Returns a List of autocmds.
3205 */
3206 void
3207f_autocmd_get(typval_T *argvars, typval_T *rettv)
3208{
3209 event_T event_arg = NUM_EVENTS;
3210 event_T event;
3211 AutoPat *ap;
3212 AutoCmd *ac;
3213 list_T *event_list;
3214 dict_T *event_dict;
3215 char_u *event_name = NULL;
3216 char_u *pat = NULL;
3217 char_u *name = NULL;
3218 int group = AUGROUP_ALL;
3219
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01003220 if (rettv_list_alloc(rettv) == FAIL)
3221 return;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003222 if (check_for_opt_dict_arg(argvars, 0) == FAIL)
3223 return;
3224
3225 if (argvars[0].v_type == VAR_DICT)
3226 {
3227 // return only the autocmds in the specified group
3228 if (dict_has_key(argvars[0].vval.v_dict, "group"))
3229 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003230 name = dict_get_string(argvars[0].vval.v_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003231 if (name == NULL)
3232 return;
3233
3234 if (*name == NUL)
3235 group = AUGROUP_DEFAULT;
3236 else
3237 {
3238 group = au_find_group(name);
3239 if (group == AUGROUP_ERROR)
3240 {
3241 semsg(_(e_no_such_group_str), name);
3242 vim_free(name);
3243 return;
3244 }
3245 }
3246 vim_free(name);
3247 }
3248
3249 // return only the autocmds for the specified event
3250 if (dict_has_key(argvars[0].vval.v_dict, "event"))
3251 {
3252 int i;
3253
Bram Moolenaard61efa52022-07-23 09:52:04 +01003254 name = dict_get_string(argvars[0].vval.v_dict, "event", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003255 if (name == NULL)
3256 return;
3257
3258 if (name[0] == '*' && name[1] == NUL)
3259 event_arg = NUM_EVENTS;
3260 else
3261 {
3262 for (i = 0; event_names[i].name != NULL; i++)
3263 if (STRICMP(event_names[i].name, name) == 0)
3264 break;
3265 if (event_names[i].name == NULL)
3266 {
3267 semsg(_(e_no_such_event_str), name);
3268 vim_free(name);
3269 return;
3270 }
3271 event_arg = event_names[i].event;
3272 }
3273 vim_free(name);
3274 }
3275
3276 // return only the autocmds for the specified pattern
3277 if (dict_has_key(argvars[0].vval.v_dict, "pattern"))
3278 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003279 pat = dict_get_string(argvars[0].vval.v_dict, "pattern", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003280 if (pat == NULL)
3281 return;
3282 }
3283 }
3284
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003285 event_list = rettv->vval.v_list;
3286
3287 // iterate through all the autocmd events
3288 for (event = (event_T)0; (int)event < NUM_EVENTS;
3289 event = (event_T)((int)event + 1))
3290 {
3291 if (event_arg != NUM_EVENTS && event != event_arg)
3292 continue;
3293
3294 event_name = event_nr2name(event);
3295
3296 // iterate through all the patterns for this autocmd event
3297 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
3298 {
3299 char_u *group_name;
3300
3301 if (group != AUGROUP_ALL && group != ap->group)
3302 continue;
3303
3304 if (pat != NULL && STRCMP(pat, ap->pat) != 0)
3305 continue;
3306
3307 group_name = get_augroup_name(NULL, ap->group);
3308
3309 // iterate through all the commands for this pattern and add one
3310 // item for each cmd.
3311 for (ac = ap->cmds; ac != NULL; ac = ac->next)
3312 {
3313 event_dict = dict_alloc();
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003314 if (event_dict == NULL
3315 || list_append_dict(event_list, event_dict) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003316 return;
3317
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003318 if (dict_add_string(event_dict, "event", event_name) == FAIL
3319 || dict_add_string(event_dict, "group",
3320 group_name == NULL ? (char_u *)""
3321 : group_name) == FAIL
3322 || (ap->buflocal_nr != 0
3323 && (dict_add_number(event_dict, "bufnr",
3324 ap->buflocal_nr) == FAIL))
3325 || dict_add_string(event_dict, "pattern",
3326 ap->pat) == FAIL
3327 || dict_add_string(event_dict, "cmd", ac->cmd) == FAIL
3328 || dict_add_bool(event_dict, "once", ac->once) == FAIL
3329 || dict_add_bool(event_dict, "nested",
3330 ac->nested) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003331 return;
3332 }
3333 }
3334 }
3335
3336 vim_free(pat);
3337}
3338
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01003339#endif