blob: a78e78b02437301363aef77a4764cdfde309c69a [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},
182 {"TextChanged", EVENT_TEXTCHANGED},
183 {"TextChangedI", EVENT_TEXTCHANGEDI},
184 {"TextChangedP", EVENT_TEXTCHANGEDP},
Shougo Matsushita4ccaedf2022-10-15 11:48:00 +0100185 {"TextChangedT", EVENT_TEXTCHANGEDT},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100186 {"User", EVENT_USER},
187 {"VimEnter", EVENT_VIMENTER},
188 {"VimLeave", EVENT_VIMLEAVE},
189 {"VimLeavePre", EVENT_VIMLEAVEPRE},
190 {"WinNew", EVENT_WINNEW},
naohiro ono23beefe2021-11-13 12:38:49 +0000191 {"WinClosed", EVENT_WINCLOSED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100192 {"WinEnter", EVENT_WINENTER},
193 {"WinLeave", EVENT_WINLEAVE},
Bram Moolenaar35fc61c2022-11-22 12:40:50 +0000194 {"WinResized", EVENT_WINRESIZED},
LemonBoy09371822022-04-08 15:18:45 +0100195 {"WinScrolled", EVENT_WINSCROLLED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100196 {"VimResized", EVENT_VIMRESIZED},
197 {"TextYankPost", EVENT_TEXTYANKPOST},
Bram Moolenaar100118c2020-12-11 19:30:34 +0100198 {"VimSuspend", EVENT_VIMSUSPEND},
199 {"VimResume", EVENT_VIMRESUME},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100200 {NULL, (event_T)0}
201};
202
203static AutoPat *first_autopat[NUM_EVENTS] =
204{
205 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
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};
212
213static AutoPat *last_autopat[NUM_EVENTS] =
214{
215 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
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};
222
kylo252ae6f1d82022-02-16 19:24:07 +0000223#define AUGROUP_DEFAULT (-1) // default autocmd group
224#define AUGROUP_ERROR (-2) // erroneous autocmd group
225#define AUGROUP_ALL (-3) // all autocmd groups
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100226
227/*
228 * struct used to keep status while executing autocommands for an event.
229 */
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100230struct AutoPatCmd_S
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100231{
232 AutoPat *curpat; // next AutoPat to examine
233 AutoCmd *nextcmd; // next AutoCmd to execute
234 int group; // group being used
235 char_u *fname; // fname to match with
236 char_u *sfname; // sfname to match with
237 char_u *tail; // tail of fname
238 event_T event; // current event
LemonBoyeca7c602022-04-14 15:39:43 +0100239 sctx_T script_ctx; // script context where it is defined
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100240 int arg_bufnr; // Initially equal to <abuf>, set to zero when
241 // buf is deleted.
LemonBoyeca7c602022-04-14 15:39:43 +0100242 AutoPatCmd_T *next; // chain of active apc-s for auto-invalidation
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100243};
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100244
LemonBoyeca7c602022-04-14 15:39:43 +0100245static AutoPatCmd_T *active_apc_list = NULL; // stack of active autocommands
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100246
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200247// Macro to loop over all the patterns for an autocmd event
248#define FOR_ALL_AUTOCMD_PATTERNS(event, ap) \
249 for ((ap) = first_autopat[(int)(event)]; (ap) != NULL; (ap) = (ap)->next)
250
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100251/*
252 * augroups stores a list of autocmd group names.
253 */
254static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
255#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
Bram Moolenaarc667da52019-11-30 20:52:27 +0100256// use get_deleted_augroup() to get this
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100257static char_u *deleted_augroup = NULL;
258
259/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100260 * The ID of the current group. Group 0 is the default one.
261 */
262static int current_augroup = AUGROUP_DEFAULT;
263
Bram Moolenaarc667da52019-11-30 20:52:27 +0100264static int au_need_clean = FALSE; // need to delete marked patterns
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100265
266static char_u *event_nr2name(event_T event);
267static int au_get_grouparg(char_u **argp);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200268static 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 +0100269static 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 +0100270static void auto_next_pat(AutoPatCmd_T *apc, int stop_at_last);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100271static int au_find_group(char_u *name);
272
273static event_T last_event;
274static int last_group;
Bram Moolenaarc667da52019-11-30 20:52:27 +0100275static int autocmd_blocked = 0; // block all autocmds
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100276
277 static char_u *
278get_deleted_augroup(void)
279{
280 if (deleted_augroup == NULL)
281 deleted_augroup = (char_u *)_("--Deleted--");
282 return deleted_augroup;
283}
284
285/*
286 * Show the autocommands for one AutoPat.
287 */
288 static void
289show_autocmd(AutoPat *ap, event_T event)
290{
291 AutoCmd *ac;
292
293 // Check for "got_int" (here and at various places below), which is set
294 // when "q" has been hit for the "--more--" prompt
295 if (got_int)
296 return;
297 if (ap->pat == NULL) // pattern has been removed
298 return;
299
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000300 // Make sure no info referenced by "ap" is cleared, e.g. when a timer
301 // clears an augroup. Jump to "theend" after this!
302 // "ap->pat" may be cleared anyway.
303 ++autocmd_busy;
304
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100305 msg_putchar('\n');
306 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000307 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100308 if (event != last_event || ap->group != last_group)
309 {
310 if (ap->group != AUGROUP_DEFAULT)
311 {
312 if (AUGROUP_NAME(ap->group) == NULL)
313 msg_puts_attr((char *)get_deleted_augroup(), HL_ATTR(HLF_E));
314 else
315 msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
316 msg_puts(" ");
317 }
318 msg_puts_attr((char *)event_nr2name(event), HL_ATTR(HLF_T));
319 last_event = event;
320 last_group = ap->group;
321 msg_putchar('\n');
322 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000323 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100324 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000325
326 if (ap->pat == NULL)
327 goto theend; // timer might have cleared the pattern or group
328
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100329 msg_col = 4;
330 msg_outtrans(ap->pat);
331
332 for (ac = ap->cmds; ac != NULL; ac = ac->next)
333 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100334 if (ac->cmd == NULL) // skip removed commands
335 continue;
336
337 if (msg_col >= 14)
338 msg_putchar('\n');
339 msg_col = 14;
340 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000341 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100342 msg_outtrans(ac->cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100343#ifdef FEAT_EVAL
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100344 if (p_verbose > 0)
345 last_set_msg(ac->script_ctx);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100346#endif
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100347 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000348 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100349 if (ac->next != NULL)
350 {
351 msg_putchar('\n');
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100352 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000353 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100354 }
355 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000356
357theend:
358 --autocmd_busy;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100359}
360
361/*
362 * Mark an autocommand pattern for deletion.
363 */
364 static void
365au_remove_pat(AutoPat *ap)
366{
367 VIM_CLEAR(ap->pat);
368 ap->buflocal_nr = -1;
369 au_need_clean = TRUE;
370}
371
372/*
373 * Mark all commands for a pattern for deletion.
374 */
375 static void
376au_remove_cmds(AutoPat *ap)
377{
378 AutoCmd *ac;
379
380 for (ac = ap->cmds; ac != NULL; ac = ac->next)
381 VIM_CLEAR(ac->cmd);
382 au_need_clean = TRUE;
383}
384
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200385// Delete one command from an autocmd pattern.
386static void au_del_cmd(AutoCmd *ac)
387{
388 VIM_CLEAR(ac->cmd);
389 au_need_clean = TRUE;
390}
391
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100392/*
393 * Cleanup autocommands and patterns that have been deleted.
394 * This is only done when not executing autocommands.
395 */
396 static void
397au_cleanup(void)
398{
399 AutoPat *ap, **prev_ap;
400 AutoCmd *ac, **prev_ac;
401 event_T event;
402
403 if (autocmd_busy || !au_need_clean)
404 return;
405
406 // loop over all events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100407 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100408 event = (event_T)((int)event + 1))
409 {
410 // loop over all autocommand patterns
411 prev_ap = &(first_autopat[(int)event]);
412 for (ap = *prev_ap; ap != NULL; ap = *prev_ap)
413 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200414 int has_cmd = FALSE;
415
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200416 // loop over all commands for this pattern
417 prev_ac = &(ap->cmds);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100418 for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
419 {
420 // remove the command if the pattern is to be deleted or when
421 // the command has been marked for deletion
422 if (ap->pat == NULL || ac->cmd == NULL)
423 {
424 *prev_ac = ac->next;
425 vim_free(ac->cmd);
426 vim_free(ac);
427 }
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200428 else
429 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200430 has_cmd = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100431 prev_ac = &(ac->next);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200432 }
433 }
434
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200435 if (ap->pat != NULL && !has_cmd)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200436 // Pattern was not marked for deletion, but all of its
437 // commands were. So mark the pattern for deletion.
438 au_remove_pat(ap);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100439
440 // remove the pattern if it has been marked for deletion
441 if (ap->pat == NULL)
442 {
443 if (ap->next == NULL)
444 {
445 if (prev_ap == &(first_autopat[(int)event]))
446 last_autopat[(int)event] = NULL;
447 else
448 // this depends on the "next" field being the first in
449 // the struct
450 last_autopat[(int)event] = (AutoPat *)prev_ap;
451 }
452 *prev_ap = ap->next;
453 vim_regfree(ap->reg_prog);
454 vim_free(ap);
455 }
456 else
457 prev_ap = &(ap->next);
458 }
459 }
460
461 au_need_clean = FALSE;
462}
463
464/*
465 * Called when buffer is freed, to remove/invalidate related buffer-local
466 * autocmds.
467 */
468 void
469aubuflocal_remove(buf_T *buf)
470{
LemonBoyeca7c602022-04-14 15:39:43 +0100471 AutoPat *ap;
472 event_T event;
473 AutoPatCmd_T *apc;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100474
475 // invalidate currently executing autocommands
476 for (apc = active_apc_list; apc; apc = apc->next)
477 if (buf->b_fnum == apc->arg_bufnr)
478 apc->arg_bufnr = 0;
479
480 // invalidate buflocals looping through events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100481 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100482 event = (event_T)((int)event + 1))
483 // loop over all autocommand patterns
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200484 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100485 if (ap->buflocal_nr == buf->b_fnum)
486 {
487 au_remove_pat(ap);
488 if (p_verbose >= 6)
489 {
490 verbose_enter();
491 smsg(_("auto-removing autocommand: %s <buffer=%d>"),
492 event_nr2name(event), buf->b_fnum);
493 verbose_leave();
494 }
495 }
496 au_cleanup();
497}
498
499/*
500 * Add an autocmd group name.
501 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
502 */
503 static int
504au_new_group(char_u *name)
505{
506 int i;
507
508 i = au_find_group(name);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100509 if (i != AUGROUP_ERROR)
510 return i;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100511
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100512 // the group doesn't exist yet, add it. First try using a free entry.
513 for (i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100514 if (AUGROUP_NAME(i) == NULL)
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100515 break;
516 if (i == augroups.ga_len && ga_grow(&augroups, 1) == FAIL)
517 return AUGROUP_ERROR;
518
519 AUGROUP_NAME(i) = vim_strsave(name);
520 if (AUGROUP_NAME(i) == NULL)
521 return AUGROUP_ERROR;
522 if (i == augroups.ga_len)
523 ++augroups.ga_len;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100524
525 return i;
526}
527
528 static void
529au_del_group(char_u *name)
530{
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100531 int i;
532 event_T event;
533 AutoPat *ap;
534 int in_use = FALSE;
535
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100536
537 i = au_find_group(name);
538 if (i == AUGROUP_ERROR) // the group doesn't exist
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100539 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100540 semsg(_(e_no_such_group_str), name);
541 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100542 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100543 if (i == current_augroup)
544 {
545 emsg(_(e_cannot_delete_current_group));
546 return;
547 }
548
549 for (event = (event_T)0; (int)event < NUM_EVENTS;
550 event = (event_T)((int)event + 1))
551 {
552 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
553 if (ap->group == i && ap->pat != NULL)
554 {
555 give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
556 in_use = TRUE;
557 event = NUM_EVENTS;
558 break;
559 }
560 }
561 vim_free(AUGROUP_NAME(i));
562 if (in_use)
563 AUGROUP_NAME(i) = get_deleted_augroup();
564 else
565 AUGROUP_NAME(i) = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100566}
567
568/*
569 * Find the ID of an autocmd group name.
570 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
571 */
572 static int
573au_find_group(char_u *name)
574{
575 int i;
576
577 for (i = 0; i < augroups.ga_len; ++i)
578 if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
579 && STRCMP(AUGROUP_NAME(i), name) == 0)
580 return i;
581 return AUGROUP_ERROR;
582}
583
584/*
585 * Return TRUE if augroup "name" exists.
586 */
587 int
588au_has_group(char_u *name)
589{
590 return au_find_group(name) != AUGROUP_ERROR;
591}
592
593/*
594 * ":augroup {name}".
595 */
596 void
597do_augroup(char_u *arg, int del_group)
598{
599 int i;
600
601 if (del_group)
602 {
603 if (*arg == NUL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000604 emsg(_(e_argument_required));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100605 else
606 au_del_group(arg);
607 }
608 else if (STRICMP(arg, "end") == 0) // ":aug end": back to group 0
609 current_augroup = AUGROUP_DEFAULT;
610 else if (*arg) // ":aug xxx": switch to group xxx
611 {
612 i = au_new_group(arg);
613 if (i != AUGROUP_ERROR)
614 current_augroup = i;
615 }
616 else // ":aug": list the group names
617 {
618 msg_start();
619 for (i = 0; i < augroups.ga_len; ++i)
620 {
621 if (AUGROUP_NAME(i) != NULL)
622 {
623 msg_puts((char *)AUGROUP_NAME(i));
624 msg_puts(" ");
625 }
626 }
627 msg_clr_eos();
628 msg_end();
629 }
630}
631
Bram Moolenaare76062c2022-11-28 18:51:43 +0000632 void
633autocmd_init(void)
634{
635 CLEAR_FIELD(aucmd_win);
636}
637
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100638#if defined(EXITFREE) || defined(PROTO)
639 void
640free_all_autocmds(void)
641{
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100642 char_u *s;
643
644 for (current_augroup = -1; current_augroup < augroups.ga_len;
645 ++current_augroup)
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200646 do_autocmd(NULL, (char_u *)"", TRUE);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100647
Bram Moolenaare76062c2022-11-28 18:51:43 +0000648 for (int i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100649 {
650 s = ((char_u **)(augroups.ga_data))[i];
651 if (s != get_deleted_augroup())
652 vim_free(s);
653 }
654 ga_clear(&augroups);
Bram Moolenaare76062c2022-11-28 18:51:43 +0000655
Bram Moolenaar84497cd2022-11-28 20:34:52 +0000656 // aucmd_win[] is freed in win_free_all()
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100657}
658#endif
659
660/*
Bram Moolenaare76062c2022-11-28 18:51:43 +0000661 * Return TRUE if "win" is an active entry in aucmd_win[].
662 */
663 int
664is_aucmd_win(win_T *win)
665{
666 for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
667 if (aucmd_win[i].auc_win_used && aucmd_win[i].auc_win == win)
668 return TRUE;
669 return FALSE;
670}
671
672/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100673 * Return the event number for event name "start".
674 * Return NUM_EVENTS if the event name was not found.
675 * Return a pointer to the next event name in "end".
676 */
677 static event_T
678event_name2nr(char_u *start, char_u **end)
679{
680 char_u *p;
681 int i;
682 int len;
683
684 // the event name ends with end of line, '|', a blank or a comma
685 for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
686 ;
687 for (i = 0; event_names[i].name != NULL; ++i)
688 {
689 len = (int)STRLEN(event_names[i].name);
690 if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0)
691 break;
692 }
693 if (*p == ',')
694 ++p;
695 *end = p;
696 if (event_names[i].name == NULL)
697 return NUM_EVENTS;
698 return event_names[i].event;
699}
700
701/*
702 * Return the name for event "event".
703 */
704 static char_u *
705event_nr2name(event_T event)
706{
707 int i;
708
709 for (i = 0; event_names[i].name != NULL; ++i)
710 if (event_names[i].event == event)
711 return (char_u *)event_names[i].name;
712 return (char_u *)"Unknown";
713}
714
715/*
716 * Scan over the events. "*" stands for all events.
717 */
718 static char_u *
719find_end_event(
720 char_u *arg,
721 int have_group) // TRUE when group name was found
722{
723 char_u *pat;
724 char_u *p;
725
726 if (*arg == '*')
727 {
728 if (arg[1] && !VIM_ISWHITE(arg[1]))
729 {
Bram Moolenaar6d057012021-12-31 18:49:43 +0000730 semsg(_(e_illegal_character_after_star_str), arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100731 return NULL;
732 }
733 pat = arg + 1;
734 }
735 else
736 {
737 for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p)
738 {
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100739 if ((int)event_name2nr(pat, &p) >= NUM_EVENTS)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100740 {
741 if (have_group)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000742 semsg(_(e_no_such_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100743 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000744 semsg(_(e_no_such_group_or_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100745 return NULL;
746 }
747 }
748 }
749 return pat;
750}
751
752/*
753 * Return TRUE if "event" is included in 'eventignore'.
754 */
755 static int
756event_ignored(event_T event)
757{
758 char_u *p = p_ei;
759
760 while (*p != NUL)
761 {
762 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
763 return TRUE;
764 if (event_name2nr(p, &p) == event)
765 return TRUE;
766 }
767
768 return FALSE;
769}
770
771/*
772 * Return OK when the contents of p_ei is valid, FAIL otherwise.
773 */
774 int
775check_ei(void)
776{
777 char_u *p = p_ei;
778
779 while (*p)
780 {
781 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
782 {
783 p += 3;
784 if (*p == ',')
785 ++p;
786 }
787 else if (event_name2nr(p, &p) == NUM_EVENTS)
788 return FAIL;
789 }
790
791 return OK;
792}
793
794# if defined(FEAT_SYN_HL) || defined(PROTO)
795
796/*
797 * Add "what" to 'eventignore' to skip loading syntax highlighting for every
798 * buffer loaded into the window. "what" must start with a comma.
799 * Returns the old value of 'eventignore' in allocated memory.
800 */
801 char_u *
802au_event_disable(char *what)
803{
804 char_u *new_ei;
805 char_u *save_ei;
806
807 save_ei = vim_strsave(p_ei);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100808 if (save_ei == NULL)
809 return NULL;
810
811 new_ei = vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what));
812 if (new_ei == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100813 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100814 vim_free(save_ei);
815 return NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100816 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100817
818 if (*what == ',' && *p_ei == NUL)
819 STRCPY(new_ei, what + 1);
820 else
821 STRCAT(new_ei, what);
822 set_string_option_direct((char_u *)"ei", -1, new_ei,
823 OPT_FREE, SID_NONE);
824 vim_free(new_ei);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100825 return save_ei;
826}
827
828 void
829au_event_restore(char_u *old_ei)
830{
831 if (old_ei != NULL)
832 {
833 set_string_option_direct((char_u *)"ei", -1, old_ei,
834 OPT_FREE, SID_NONE);
835 vim_free(old_ei);
836 }
837}
Bram Moolenaarc667da52019-11-30 20:52:27 +0100838# endif // FEAT_SYN_HL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100839
840/*
841 * do_autocmd() -- implements the :autocmd command. Can be used in the
842 * following ways:
843 *
844 * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
845 * will be automatically executed for <event>
846 * when editing a file matching <pat>, in
847 * the current group.
848 * :autocmd <event> <pat> Show the autocommands associated with
849 * <event> and <pat>.
850 * :autocmd <event> Show the autocommands associated with
851 * <event>.
852 * :autocmd Show all autocommands.
853 * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
854 * <event> and <pat>, and add the command
855 * <cmd>, for the current group.
856 * :autocmd! <event> <pat> Remove all autocommands associated with
857 * <event> and <pat> for the current group.
858 * :autocmd! <event> Remove all autocommands associated with
859 * <event> for the current group.
860 * :autocmd! Remove ALL autocommands for the current
861 * group.
862 *
863 * Multiple events and patterns may be given separated by commas. Here are
864 * some examples:
865 * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
866 * :autocmd bufleave * set tw=79 nosmartindent ic infercase
867 *
868 * :autocmd * *.c show all autocommands for *.c files.
869 *
870 * Mostly a {group} argument can optionally appear before <event>.
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200871 * "eap" can be NULL.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100872 */
873 void
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200874do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100875{
876 char_u *arg = arg_in;
877 char_u *pat;
878 char_u *envpat = NULL;
879 char_u *cmd;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200880 int cmd_need_free = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100881 event_T event;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200882 char_u *tofree = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100883 int nested = FALSE;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200884 int once = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100885 int group;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200886 int i;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200887 int flags = 0;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100888
889 if (*arg == '|')
890 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000891 eap->nextcmd = arg + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100892 arg = (char_u *)"";
893 group = AUGROUP_ALL; // no argument, use all groups
894 }
895 else
896 {
897 /*
898 * Check for a legal group name. If not, use AUGROUP_ALL.
899 */
900 group = au_get_grouparg(&arg);
901 if (arg == NULL) // out of memory
902 return;
903 }
904
905 /*
906 * Scan over the events.
907 * If we find an illegal name, return here, don't do anything.
908 */
909 pat = find_end_event(arg, group != AUGROUP_ALL);
910 if (pat == NULL)
911 return;
912
913 pat = skipwhite(pat);
914 if (*pat == '|')
915 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000916 eap->nextcmd = pat + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100917 pat = (char_u *)"";
918 cmd = (char_u *)"";
919 }
920 else
921 {
922 /*
923 * Scan over the pattern. Put a NUL at the end.
924 */
925 cmd = pat;
926 while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
927 cmd++;
928 if (*cmd)
929 *cmd++ = NUL;
930
931 // Expand environment variables in the pattern. Set 'shellslash', we
932 // want forward slashes here.
933 if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
934 {
935#ifdef BACKSLASH_IN_FILENAME
936 int p_ssl_save = p_ssl;
937
938 p_ssl = TRUE;
939#endif
940 envpat = expand_env_save(pat);
941#ifdef BACKSLASH_IN_FILENAME
942 p_ssl = p_ssl_save;
943#endif
944 if (envpat != NULL)
945 pat = envpat;
946 }
947
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100948 cmd = skipwhite(cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200949 for (i = 0; i < 2; i++)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100950 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100951 if (*cmd == NUL)
952 continue;
953
954 // Check for "++once" flag.
955 if (STRNCMP(cmd, "++once", 6) == 0 && VIM_ISWHITE(cmd[6]))
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200956 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100957 if (once)
958 semsg(_(e_duplicate_argument_str), "++once");
959 once = TRUE;
960 cmd = skipwhite(cmd + 6);
961 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200962
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100963 // Check for "++nested" flag.
964 if ((STRNCMP(cmd, "++nested", 8) == 0 && VIM_ISWHITE(cmd[8])))
965 {
966 if (nested)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200967 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100968 semsg(_(e_duplicate_argument_str), "++nested");
969 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200970 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100971 nested = TRUE;
972 cmd = skipwhite(cmd + 8);
973 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200974
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100975 // Check for the old "nested" flag in legacy script.
976 if (STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
977 {
978 if (in_vim9script())
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200979 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100980 // If there ever is a :nested command this error should
981 // be removed and "nested" accepted as the start of the
982 // command.
983 emsg(_(e_invalid_command_nested_did_you_mean_plusplus_nested));
984 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200985 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100986 if (nested)
987 {
988 semsg(_(e_duplicate_argument_str), "nested");
989 return;
990 }
991 nested = TRUE;
992 cmd = skipwhite(cmd + 6);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200993 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100994 }
995
996 /*
997 * Find the start of the commands.
998 * Expand <sfile> in it.
999 */
1000 if (*cmd != NUL)
1001 {
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001002 if (eap != NULL)
1003 // Read a {} block if it follows.
1004 cmd = may_get_cmd_block(eap, cmd, &tofree, &flags);
1005
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001006 cmd = expand_sfile(cmd);
1007 if (cmd == NULL) // some error
1008 return;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001009 cmd_need_free = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001010 }
1011 }
1012
1013 /*
1014 * Print header when showing autocommands.
1015 */
1016 if (!forceit && *cmd == NUL)
1017 // Highlight title
1018 msg_puts_title(_("\n--- Autocommands ---"));
1019
1020 /*
1021 * Loop over the events.
1022 */
1023 last_event = (event_T)-1; // for listing the event name
1024 last_group = AUGROUP_ERROR; // for listing the group name
1025 if (*arg == '*' || *arg == NUL || *arg == '|')
1026 {
Bram Moolenaarb6db1462021-12-24 19:24:47 +00001027 if (*cmd != NUL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001028 emsg(_(e_cannot_define_autocommands_for_all_events));
1029 else
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +01001030 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001031 event = (event_T)((int)event + 1))
1032 if (do_autocmd_event(event, pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001033 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001034 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001035 }
1036 else
1037 {
1038 while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
1039 if (do_autocmd_event(event_name2nr(arg, &arg), pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001040 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001041 break;
1042 }
1043
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001044 if (cmd_need_free)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001045 vim_free(cmd);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001046 vim_free(tofree);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001047 vim_free(envpat);
1048}
1049
1050/*
1051 * Find the group ID in a ":autocmd" or ":doautocmd" argument.
1052 * The "argp" argument is advanced to the following argument.
1053 *
1054 * Returns the group ID, AUGROUP_ERROR for error (out of memory).
1055 */
1056 static int
1057au_get_grouparg(char_u **argp)
1058{
1059 char_u *group_name;
1060 char_u *p;
1061 char_u *arg = *argp;
1062 int group = AUGROUP_ALL;
1063
1064 for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p)
1065 ;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001066 if (p <= arg)
1067 return AUGROUP_ALL;
1068
1069 group_name = vim_strnsave(arg, p - arg);
1070 if (group_name == NULL) // out of memory
1071 return AUGROUP_ERROR;
1072 group = au_find_group(group_name);
1073 if (group == AUGROUP_ERROR)
1074 group = AUGROUP_ALL; // no match, use all groups
1075 else
1076 *argp = skipwhite(p); // match, skip over group name
1077 vim_free(group_name);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001078 return group;
1079}
1080
1081/*
1082 * do_autocmd() for one event.
1083 * If *pat == NUL do for all patterns.
1084 * If *cmd == NUL show entries.
1085 * If forceit == TRUE delete entries.
1086 * If group is not AUGROUP_ALL, only use this group.
1087 */
1088 static int
1089do_autocmd_event(
1090 event_T event,
1091 char_u *pat,
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001092 int once,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001093 int nested,
1094 char_u *cmd,
1095 int forceit,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001096 int group,
1097 int flags)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001098{
1099 AutoPat *ap;
1100 AutoPat **prev_ap;
1101 AutoCmd *ac;
1102 AutoCmd **prev_ac;
1103 int brace_level;
1104 char_u *endpat;
1105 int findgroup;
1106 int allgroups;
1107 int patlen;
1108 int is_buflocal;
1109 int buflocal_nr;
Bram Moolenaarc667da52019-11-30 20:52:27 +01001110 char_u buflocal_pat[25]; // for "<buffer=X>"
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001111
1112 if (group == AUGROUP_ALL)
1113 findgroup = current_augroup;
1114 else
1115 findgroup = group;
1116 allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
1117
1118 /*
1119 * Show or delete all patterns for an event.
1120 */
1121 if (*pat == NUL)
1122 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001123 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001124 {
1125 if (forceit) // delete the AutoPat, if it's in the current group
1126 {
1127 if (ap->group == findgroup)
1128 au_remove_pat(ap);
1129 }
1130 else if (group == AUGROUP_ALL || ap->group == group)
1131 show_autocmd(ap, event);
1132 }
1133 }
1134
1135 /*
1136 * Loop through all the specified patterns.
1137 */
1138 for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
1139 {
1140 /*
1141 * Find end of the pattern.
1142 * Watch out for a comma in braces, like "*.\{obj,o\}".
1143 */
1144 brace_level = 0;
1145 for (endpat = pat; *endpat && (*endpat != ',' || brace_level
1146 || (endpat > pat && endpat[-1] == '\\')); ++endpat)
1147 {
1148 if (*endpat == '{')
1149 brace_level++;
1150 else if (*endpat == '}')
1151 brace_level--;
1152 }
1153 if (pat == endpat) // ignore single comma
1154 continue;
1155 patlen = (int)(endpat - pat);
1156
1157 /*
1158 * detect special <buflocal[=X]> buffer-local patterns
1159 */
1160 is_buflocal = FALSE;
1161 buflocal_nr = 0;
1162
1163 if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
1164 && pat[patlen - 1] == '>')
1165 {
1166 // "<buffer...>": Error will be printed only for addition.
1167 // printing and removing will proceed silently.
1168 is_buflocal = TRUE;
1169 if (patlen == 8)
1170 // "<buffer>"
1171 buflocal_nr = curbuf->b_fnum;
1172 else if (patlen > 9 && pat[7] == '=')
1173 {
1174 if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0)
1175 // "<buffer=abuf>"
1176 buflocal_nr = autocmd_bufnr;
1177 else if (skipdigits(pat + 8) == pat + patlen - 1)
1178 // "<buffer=123>"
1179 buflocal_nr = atoi((char *)pat + 8);
1180 }
1181 }
1182
1183 if (is_buflocal)
1184 {
1185 // normalize pat into standard "<buffer>#N" form
1186 sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
1187 pat = buflocal_pat; // can modify pat and patlen
1188 patlen = (int)STRLEN(buflocal_pat); // but not endpat
1189 }
1190
1191 /*
1192 * Find AutoPat entries with this pattern. When adding a command it
1193 * always goes at or after the last one, so start at the end.
1194 */
1195 if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL)
1196 prev_ap = &last_autopat[(int)event];
1197 else
1198 prev_ap = &first_autopat[(int)event];
1199 while ((ap = *prev_ap) != NULL)
1200 {
1201 if (ap->pat != NULL)
1202 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001203 /*
1204 * Accept a pattern when:
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001205 * - a group was specified and it's that group, or a group was
1206 * not specified and it's the current group, or a group was
1207 * not specified and we are listing
1208 * - the length of the pattern matches
1209 * - the pattern matches.
1210 * For <buffer[=X]>, this condition works because we normalize
1211 * all buffer-local patterns.
1212 */
1213 if ((allgroups || ap->group == findgroup)
1214 && ap->patlen == patlen
1215 && STRNCMP(pat, ap->pat, patlen) == 0)
1216 {
1217 /*
1218 * Remove existing autocommands.
1219 * If adding any new autocmd's for this AutoPat, don't
1220 * delete the pattern from the autopat list, append to
1221 * this list.
1222 */
1223 if (forceit)
1224 {
1225 if (*cmd != NUL && ap->next == NULL)
1226 {
1227 au_remove_cmds(ap);
1228 break;
1229 }
1230 au_remove_pat(ap);
1231 }
1232
1233 /*
1234 * Show autocmd's for this autopat, or buflocals <buffer=X>
1235 */
1236 else if (*cmd == NUL)
1237 show_autocmd(ap, event);
1238
1239 /*
1240 * Add autocmd to this autopat, if it's the last one.
1241 */
1242 else if (ap->next == NULL)
1243 break;
1244 }
1245 }
1246 prev_ap = &ap->next;
1247 }
1248
1249 /*
1250 * Add a new command.
1251 */
1252 if (*cmd != NUL)
1253 {
1254 /*
1255 * If the pattern we want to add a command to does appear at the
1256 * end of the list (or not is not in the list at all), add the
1257 * pattern at the end of the list.
1258 */
1259 if (ap == NULL)
1260 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001261 // refuse to add buffer-local ap if buffer number is invalid
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001262 if (is_buflocal && (buflocal_nr == 0
1263 || buflist_findnr(buflocal_nr) == NULL))
1264 {
Bram Moolenaarf1474d82021-12-31 19:59:55 +00001265 semsg(_(e_buffer_nr_invalid_buffer_number), buflocal_nr);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001266 return FAIL;
1267 }
1268
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001269 ap = ALLOC_ONE(AutoPat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001270 if (ap == NULL)
1271 return FAIL;
1272 ap->pat = vim_strnsave(pat, patlen);
1273 ap->patlen = patlen;
1274 if (ap->pat == NULL)
1275 {
1276 vim_free(ap);
1277 return FAIL;
1278 }
1279
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001280#ifdef FEAT_EVAL
1281 // need to initialize last_mode for the first ModeChanged
1282 // autocmd
1283 if (event == EVENT_MODECHANGED && !has_modechanged())
LemonBoy2bf52dd2022-04-09 18:17:34 +01001284 get_mode(last_mode);
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001285#endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001286 // Initialize the fields checked by the WinScrolled and
1287 // WinResized trigger to prevent them from firing right after
1288 // the first autocmd is defined.
1289 if ((event == EVENT_WINSCROLLED || event == EVENT_WINRESIZED)
1290 && !(has_winscrolled() || has_winresized()))
LemonBoy09371822022-04-08 15:18:45 +01001291 {
Bram Moolenaar29967732022-11-20 12:11:45 +00001292 tabpage_T *save_curtab = curtab;
1293 tabpage_T *tp;
1294 FOR_ALL_TABPAGES(tp)
1295 {
1296 unuse_tabpage(curtab);
1297 use_tabpage(tp);
1298 snapshot_windows_scroll_size();
1299 }
1300 unuse_tabpage(curtab);
1301 use_tabpage(save_curtab);
LemonBoy09371822022-04-08 15:18:45 +01001302 }
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001303
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001304 if (is_buflocal)
1305 {
1306 ap->buflocal_nr = buflocal_nr;
1307 ap->reg_prog = NULL;
1308 }
1309 else
1310 {
1311 char_u *reg_pat;
1312
1313 ap->buflocal_nr = 0;
1314 reg_pat = file_pat_to_reg_pat(pat, endpat,
1315 &ap->allow_dirs, TRUE);
1316 if (reg_pat != NULL)
1317 ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
1318 vim_free(reg_pat);
1319 if (reg_pat == NULL || ap->reg_prog == NULL)
1320 {
1321 vim_free(ap->pat);
1322 vim_free(ap);
1323 return FAIL;
1324 }
1325 }
1326 ap->cmds = NULL;
1327 *prev_ap = ap;
1328 last_autopat[(int)event] = ap;
1329 ap->next = NULL;
1330 if (group == AUGROUP_ALL)
1331 ap->group = current_augroup;
1332 else
1333 ap->group = group;
1334 }
1335
1336 /*
1337 * Add the autocmd at the end of the AutoCmd list.
1338 */
1339 prev_ac = &(ap->cmds);
1340 while ((ac = *prev_ac) != NULL)
1341 prev_ac = &ac->next;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001342 ac = ALLOC_ONE(AutoCmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001343 if (ac == NULL)
1344 return FAIL;
1345 ac->cmd = vim_strsave(cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001346 ac->script_ctx = current_sctx;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001347 if (flags & UC_VIM9)
1348 ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001349#ifdef FEAT_EVAL
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001350 ac->script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001351#endif
1352 if (ac->cmd == NULL)
1353 {
1354 vim_free(ac);
1355 return FAIL;
1356 }
1357 ac->next = NULL;
1358 *prev_ac = ac;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001359 ac->once = once;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001360 ac->nested = nested;
1361 }
1362 }
1363
1364 au_cleanup(); // may really delete removed patterns/commands now
1365 return OK;
1366}
1367
1368/*
1369 * Implementation of ":doautocmd [group] event [fname]".
1370 * Return OK for success, FAIL for failure;
1371 */
1372 int
1373do_doautocmd(
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001374 char_u *arg_start,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001375 int do_msg, // give message for no matching autocmds?
1376 int *did_something)
1377{
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001378 char_u *arg = arg_start;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001379 char_u *fname;
1380 int nothing_done = TRUE;
1381 int group;
1382
1383 if (did_something != NULL)
1384 *did_something = FALSE;
1385
1386 /*
1387 * Check for a legal group name. If not, use AUGROUP_ALL.
1388 */
1389 group = au_get_grouparg(&arg);
1390 if (arg == NULL) // out of memory
1391 return FAIL;
1392
1393 if (*arg == '*')
1394 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001395 emsg(_(e_cant_execute_autocommands_for_all_events));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001396 return FAIL;
1397 }
1398
1399 /*
1400 * Scan over the events.
1401 * If we find an illegal name, return here, don't do anything.
1402 */
1403 fname = find_end_event(arg, group != AUGROUP_ALL);
1404 if (fname == NULL)
1405 return FAIL;
1406
1407 fname = skipwhite(fname);
1408
1409 /*
1410 * Loop over the events.
1411 */
1412 while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
1413 if (apply_autocmds_group(event_name2nr(arg, &arg),
1414 fname, NULL, TRUE, group, curbuf, NULL))
1415 nothing_done = FALSE;
1416
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001417 if (nothing_done && do_msg
1418#ifdef FEAT_EVAL
1419 && !aborting()
1420#endif
1421 )
1422 smsg(_("No matching autocommands: %s"), arg_start);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001423 if (did_something != NULL)
1424 *did_something = !nothing_done;
1425
1426#ifdef FEAT_EVAL
1427 return aborting() ? FAIL : OK;
1428#else
1429 return OK;
1430#endif
1431}
1432
1433/*
1434 * ":doautoall": execute autocommands for each loaded buffer.
1435 */
1436 void
1437ex_doautoall(exarg_T *eap)
1438{
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001439 int retval = OK;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001440 aco_save_T aco;
1441 buf_T *buf;
1442 bufref_T bufref;
1443 char_u *arg = eap->arg;
1444 int call_do_modelines = check_nomodeline(&arg);
1445 int did_aucmd;
1446
1447 /*
1448 * This is a bit tricky: For some commands curwin->w_buffer needs to be
1449 * equal to curbuf, but for some buffers there may not be a window.
1450 * So we change the buffer for the current window for a moment. This
1451 * gives problems when the autocommands make changes to the list of
1452 * buffers or windows...
1453 */
1454 FOR_ALL_BUFFERS(buf)
1455 {
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001456 // Only do loaded buffers and skip the current buffer, it's done last.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001457 if (buf->b_ml.ml_mfp == NULL || buf == curbuf)
1458 continue;
1459
Bram Moolenaare76062c2022-11-28 18:51:43 +00001460 // Find a window for this buffer and save some values.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001461 aucmd_prepbuf(&aco, buf);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001462 if (curbuf != buf)
1463 {
1464 // Failed to find a window for this buffer. Better not execute
1465 // autocommands then.
1466 retval = FAIL;
1467 break;
1468 }
1469
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001470 set_bufref(&bufref, buf);
1471
1472 // execute the autocommands for this buffer
1473 retval = do_doautocmd(arg, FALSE, &did_aucmd);
1474
1475 if (call_do_modelines && did_aucmd)
1476 // Execute the modeline settings, but don't set window-local
1477 // options if we are using the current window for another
1478 // buffer.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001479 do_modelines(is_aucmd_win(curwin) ? OPT_NOWIN : 0);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001480
1481 // restore the current window
1482 aucmd_restbuf(&aco);
1483
1484 // stop if there is some error or buffer was deleted
1485 if (retval == FAIL || !bufref_valid(&bufref))
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001486 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001487 retval = FAIL;
1488 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001489 }
1490 }
1491
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001492 // Execute autocommands for the current buffer last.
1493 if (retval == OK)
1494 {
1495 do_doautocmd(arg, FALSE, &did_aucmd);
1496 if (call_do_modelines && did_aucmd)
1497 do_modelines(0);
1498 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001499}
1500
1501/*
1502 * Check *argp for <nomodeline>. When it is present return FALSE, otherwise
1503 * return TRUE and advance *argp to after it.
1504 * Thus return TRUE when do_modelines() should be called.
1505 */
1506 int
1507check_nomodeline(char_u **argp)
1508{
1509 if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
1510 {
1511 *argp = skipwhite(*argp + 12);
1512 return FALSE;
1513 }
1514 return TRUE;
1515}
1516
1517/*
1518 * Prepare for executing autocommands for (hidden) buffer "buf".
1519 * Search for a visible window containing the current buffer. If there isn't
Bram Moolenaare76062c2022-11-28 18:51:43 +00001520 * one then use an entry in "aucmd_win[]".
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001521 * Set "curbuf" and "curwin" to match "buf".
Bram Moolenaare76062c2022-11-28 18:51:43 +00001522 * When this fails "curbuf" is not equal "buf".
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001523 */
1524 void
1525aucmd_prepbuf(
1526 aco_save_T *aco, // structure to save values in
1527 buf_T *buf) // new curbuf
1528{
1529 win_T *win;
1530 int save_ea;
1531#ifdef FEAT_AUTOCHDIR
1532 int save_acd;
1533#endif
1534
1535 // Find a window that is for the new buffer
1536 if (buf == curbuf) // be quick when buf is curbuf
1537 win = curwin;
1538 else
1539 FOR_ALL_WINDOWS(win)
1540 if (win->w_buffer == buf)
1541 break;
1542
Bram Moolenaare76062c2022-11-28 18:51:43 +00001543 // Allocate a window when needed.
1544 win_T *auc_win = NULL;
1545 int auc_idx = AUCMD_WIN_COUNT;
1546 if (win == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001547 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001548 for (auc_idx = 0; auc_idx < AUCMD_WIN_COUNT; ++auc_idx)
1549 if (!aucmd_win[auc_idx].auc_win_used)
1550 {
Bram Moolenaar84497cd2022-11-28 20:34:52 +00001551 if (aucmd_win[auc_idx].auc_win == NULL)
1552 aucmd_win[auc_idx].auc_win = win_alloc_popup_win();
1553 auc_win = aucmd_win[auc_idx].auc_win;
Bram Moolenaare76062c2022-11-28 18:51:43 +00001554 if (auc_win != NULL)
Bram Moolenaare76062c2022-11-28 18:51:43 +00001555 aucmd_win[auc_idx].auc_win_used = TRUE;
Bram Moolenaare76062c2022-11-28 18:51:43 +00001556 break;
1557 }
1558
1559 // If this fails (out of memory or using all AUCMD_WIN_COUNT
1560 // entries) then we can't reliable execute the autocmd, return with
1561 // "curbuf" unequal "buf".
1562 if (auc_win == NULL)
1563 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001564 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001565
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001566 aco->save_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001567 aco->save_curbuf = curbuf;
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;
1570
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001571 if (win != NULL)
1572 {
1573 // There is a window for "buf" in the current tab page, make it the
1574 // curwin. This is preferred, it has the least side effects (esp. if
1575 // "buf" is curbuf).
Bram Moolenaare76062c2022-11-28 18:51:43 +00001576 aco->use_aucmd_win_idx = -1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001577 curwin = win;
1578 }
1579 else
1580 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001581 // There is no window for "buf", use "auc_win". To minimize the side
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001582 // effects, insert it in the current tab page.
1583 // Anything related to a window (e.g., setting folds) may have
1584 // unexpected results.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001585 aco->use_aucmd_win_idx = auc_idx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001586
Bram Moolenaare76062c2022-11-28 18:51:43 +00001587 win_init_popup_win(auc_win, buf);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001588
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001589 aco->globaldir = globaldir;
1590 globaldir = NULL;
1591
Bram Moolenaare76062c2022-11-28 18:51:43 +00001592 // Split the current window, put the auc_win in the upper half.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001593 // We don't want the BufEnter or WinEnter autocommands.
1594 block_autocmds();
1595 make_snapshot(SNAP_AUCMD_IDX);
1596 save_ea = p_ea;
1597 p_ea = FALSE;
1598
1599#ifdef FEAT_AUTOCHDIR
1600 // Prevent chdir() call in win_enter_ext(), through do_autochdir().
1601 save_acd = p_acd;
1602 p_acd = FALSE;
1603#endif
1604
Bram Moolenaare76062c2022-11-28 18:51:43 +00001605 (void)win_split_ins(0, WSP_TOP, auc_win, 0);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001606 (void)win_comp_pos(); // recompute window positions
1607 p_ea = save_ea;
1608#ifdef FEAT_AUTOCHDIR
1609 p_acd = save_acd;
1610#endif
1611 unblock_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001612 curwin = auc_win;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001613 }
1614 curbuf = buf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001615 aco->new_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001616 set_bufref(&aco->new_curbuf, curbuf);
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001617
1618 // disable the Visual area, the position may be invalid in another buffer
1619 aco->save_VIsual_active = VIsual_active;
1620 VIsual_active = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001621}
1622
1623/*
1624 * Cleanup after executing autocommands for a (hidden) buffer.
1625 * Restore the window as it was (if possible).
1626 */
1627 void
1628aucmd_restbuf(
1629 aco_save_T *aco) // structure holding saved values
1630{
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001631 int dummy;
1632 win_T *save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001633
Bram Moolenaare76062c2022-11-28 18:51:43 +00001634 if (aco->use_aucmd_win_idx >= 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001635 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001636 win_T *awp = aucmd_win[aco->use_aucmd_win_idx].auc_win;
1637
Bram Moolenaare76062c2022-11-28 18:51:43 +00001638 // Find "awp", it can't be closed, but it may be in another tab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001639 // page. Do not trigger autocommands here.
1640 block_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001641 if (curwin != awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001642 {
1643 tabpage_T *tp;
1644 win_T *wp;
1645
1646 FOR_ALL_TAB_WINDOWS(tp, wp)
1647 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001648 if (wp == awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001649 {
1650 if (tp != curtab)
1651 goto_tabpage_tp(tp, TRUE, TRUE);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001652 win_goto(awp);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001653 goto win_found;
1654 }
1655 }
1656 }
1657win_found:
zeertzjq46bdae02023-09-24 23:16:08 +02001658 --curbuf->b_nwindows;
orbitalcde8de02023-04-02 22:05:13 +01001659#ifdef FEAT_JOB_CHANNEL
zeertzjqa40c0bc2023-05-27 14:10:08 +01001660 int save_stop_insert_mode = stop_insert_mode;
orbitalcde8de02023-04-02 22:05:13 +01001661 // May need to stop Insert mode if we were in a prompt buffer.
1662 leaving_window(curwin);
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001663 // Do not stop Insert mode when already in Insert mode before.
1664 if (aco->save_State & MODE_INSERT)
zeertzjqa40c0bc2023-05-27 14:10:08 +01001665 stop_insert_mode = save_stop_insert_mode;
orbitalcde8de02023-04-02 22:05:13 +01001666#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001667 // Remove the window and frame from the tree of frames.
1668 (void)winframe_remove(curwin, &dummy, NULL);
1669 win_remove(curwin, NULL);
Bram Moolenaar84497cd2022-11-28 20:34:52 +00001670
1671 // The window is marked as not used, but it is not freed, it can be
1672 // used again.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001673 aucmd_win[aco->use_aucmd_win_idx].auc_win_used = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001674 last_status(FALSE); // may need to remove last status line
1675
1676 if (!valid_tabpage_win(curtab))
1677 // no valid window in current tabpage
1678 close_tabpage(curtab);
1679
1680 restore_snapshot(SNAP_AUCMD_IDX, FALSE);
1681 (void)win_comp_pos(); // recompute window positions
1682 unblock_autocmds();
1683
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001684 save_curwin = win_find_by_id(aco->save_curwin_id);
1685 if (save_curwin != NULL)
1686 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001687 else
1688 // Hmm, original window disappeared. Just use the first one.
1689 curwin = firstwin;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001690 curbuf = curwin->w_buffer;
1691#ifdef FEAT_JOB_CHANNEL
1692 // May need to restore insert mode for a prompt buffer.
1693 entering_window(curwin);
1694#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001695 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001696#ifdef FEAT_EVAL
Bram Moolenaare76062c2022-11-28 18:51:43 +00001697 vars_clear(&awp->w_vars->dv_hashtab); // free all w: variables
1698 hash_init(&awp->w_vars->dv_hashtab); // re-use the hashtab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001699#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001700 vim_free(globaldir);
1701 globaldir = aco->globaldir;
1702
1703 // the buffer contents may have changed
zeertzjq49f05242023-02-04 10:58:34 +00001704 VIsual_active = aco->save_VIsual_active;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001705 check_cursor();
1706 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1707 {
1708 curwin->w_topline = curbuf->b_ml.ml_line_count;
1709#ifdef FEAT_DIFF
1710 curwin->w_topfill = 0;
1711#endif
1712 }
1713#if defined(FEAT_GUI)
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001714 if (gui.in_use)
1715 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001716 // Hide the scrollbars from the "awp" and update.
1717 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_LEFT], FALSE);
1718 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_RIGHT], FALSE);
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001719 gui_may_update_scrollbars();
1720 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001721#endif
1722 }
1723 else
1724 {
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001725 // Restore curwin. Use the window ID, a window may have been closed
1726 // and the memory re-used for another one.
1727 save_curwin = win_find_by_id(aco->save_curwin_id);
1728 if (save_curwin != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001729 {
1730 // Restore the buffer which was previously edited by curwin, if
1731 // it was changed, we are still the same window and the buffer is
1732 // valid.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001733 if (curwin->w_id == aco->new_curwin_id
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001734 && curbuf != aco->new_curbuf.br_buf
1735 && bufref_valid(&aco->new_curbuf)
1736 && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
1737 {
1738# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
1739 if (curwin->w_s == &curbuf->b_s)
1740 curwin->w_s = &aco->new_curbuf.br_buf->b_s;
1741# endif
1742 --curbuf->b_nwindows;
1743 curbuf = aco->new_curbuf.br_buf;
1744 curwin->w_buffer = curbuf;
1745 ++curbuf->b_nwindows;
1746 }
1747
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001748 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001749 curbuf = curwin->w_buffer;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001750 prevwin = win_find_by_id(aco->save_prevwin_id);
zeertzjq49f05242023-02-04 10:58:34 +00001751
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001752 // In case the autocommand moves the cursor to a position that
1753 // does not exist in curbuf.
zeertzjq49f05242023-02-04 10:58:34 +00001754 VIsual_active = aco->save_VIsual_active;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001755 check_cursor();
1756 }
1757 }
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001758
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001759 VIsual_active = aco->save_VIsual_active;
zeertzjq49f05242023-02-04 10:58:34 +00001760 check_cursor(); // just in case lines got deleted
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001761 if (VIsual_active)
1762 check_pos(curbuf, &VIsual);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001763}
1764
1765static int autocmd_nested = FALSE;
1766
1767/*
1768 * Execute autocommands for "event" and file name "fname".
1769 * Return TRUE if some commands were executed.
1770 */
1771 int
1772apply_autocmds(
1773 event_T event,
1774 char_u *fname, // NULL or empty means use actual file name
1775 char_u *fname_io, // fname to use for <afile> on cmdline
1776 int force, // when TRUE, ignore autocmd_busy
1777 buf_T *buf) // buffer for <abuf>
1778{
1779 return apply_autocmds_group(event, fname, fname_io, force,
1780 AUGROUP_ALL, buf, NULL);
1781}
1782
1783/*
1784 * Like apply_autocmds(), but with extra "eap" argument. This takes care of
1785 * setting v:filearg.
1786 */
1787 int
1788apply_autocmds_exarg(
1789 event_T event,
1790 char_u *fname,
1791 char_u *fname_io,
1792 int force,
1793 buf_T *buf,
1794 exarg_T *eap)
1795{
1796 return apply_autocmds_group(event, fname, fname_io, force,
1797 AUGROUP_ALL, buf, eap);
1798}
1799
1800/*
1801 * Like apply_autocmds(), but handles the caller's retval. If the script
1802 * processing is being aborted or if retval is FAIL when inside a try
1803 * conditional, no autocommands are executed. If otherwise the autocommands
1804 * cause the script to be aborted, retval is set to FAIL.
1805 */
1806 int
1807apply_autocmds_retval(
1808 event_T event,
1809 char_u *fname, // NULL or empty means use actual file name
1810 char_u *fname_io, // fname to use for <afile> on cmdline
1811 int force, // when TRUE, ignore autocmd_busy
1812 buf_T *buf, // buffer for <abuf>
1813 int *retval) // pointer to caller's retval
1814{
1815 int did_cmd;
1816
1817#ifdef FEAT_EVAL
1818 if (should_abort(*retval))
1819 return FALSE;
1820#endif
1821
1822 did_cmd = apply_autocmds_group(event, fname, fname_io, force,
1823 AUGROUP_ALL, buf, NULL);
1824 if (did_cmd
1825#ifdef FEAT_EVAL
1826 && aborting()
1827#endif
1828 )
1829 *retval = FAIL;
1830 return did_cmd;
1831}
1832
1833/*
1834 * Return TRUE when there is a CursorHold autocommand defined.
1835 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001836 static int
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001837has_cursorhold(void)
1838{
Bram Moolenaar24959102022-05-07 20:01:16 +01001839 return (first_autopat[(int)(get_real_state() == MODE_NORMAL_BUSY
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001840 ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
1841}
1842
1843/*
1844 * Return TRUE if the CursorHold event can be triggered.
1845 */
1846 int
1847trigger_cursorhold(void)
1848{
1849 int state;
1850
1851 if (!did_cursorhold
1852 && has_cursorhold()
1853 && reg_recording == 0
1854 && typebuf.tb_len == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001855 && !ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001856 {
1857 state = get_real_state();
Bram Moolenaar24959102022-05-07 20:01:16 +01001858 if (state == MODE_NORMAL_BUSY || (state & MODE_INSERT) != 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001859 return TRUE;
1860 }
1861 return FALSE;
1862}
1863
1864/*
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001865 * Return TRUE when there is a WinResized autocommand defined.
1866 */
1867 int
1868has_winresized(void)
1869{
1870 return (first_autopat[(int)EVENT_WINRESIZED] != NULL);
1871}
1872
1873/*
LemonBoy09371822022-04-08 15:18:45 +01001874 * Return TRUE when there is a WinScrolled autocommand defined.
1875 */
1876 int
1877has_winscrolled(void)
1878{
1879 return (first_autopat[(int)EVENT_WINSCROLLED] != NULL);
1880}
1881
1882/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001883 * Return TRUE when there is a CursorMoved autocommand defined.
1884 */
1885 int
1886has_cursormoved(void)
1887{
1888 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1889}
1890
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001891/*
1892 * Return TRUE when there is a CursorMovedI autocommand defined.
1893 */
1894 int
1895has_cursormovedI(void)
1896{
1897 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1898}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001899
1900/*
1901 * Return TRUE when there is a TextChanged autocommand defined.
1902 */
1903 int
1904has_textchanged(void)
1905{
1906 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1907}
1908
1909/*
1910 * Return TRUE when there is a TextChangedI autocommand defined.
1911 */
1912 int
1913has_textchangedI(void)
1914{
1915 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
1916}
1917
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001918/*
1919 * Return TRUE when there is a TextChangedP autocommand defined.
1920 */
1921 int
1922has_textchangedP(void)
1923{
1924 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
1925}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001926
1927/*
1928 * Return TRUE when there is an InsertCharPre autocommand defined.
1929 */
1930 int
1931has_insertcharpre(void)
1932{
1933 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
1934}
1935
1936/*
1937 * Return TRUE when there is an CmdUndefined autocommand defined.
1938 */
1939 int
1940has_cmdundefined(void)
1941{
1942 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
1943}
1944
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001945#if defined(FEAT_EVAL) || defined(PROTO)
1946/*
1947 * Return TRUE when there is a TextYankPost autocommand defined.
1948 */
1949 int
1950has_textyankpost(void)
1951{
1952 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
1953}
1954#endif
1955
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001956#if defined(FEAT_EVAL) || defined(PROTO)
1957/*
1958 * Return TRUE when there is a CompleteChanged autocommand defined.
1959 */
1960 int
1961has_completechanged(void)
1962{
1963 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
1964}
1965#endif
1966
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02001967#if defined(FEAT_EVAL) || defined(PROTO)
1968/*
1969 * Return TRUE when there is a ModeChanged autocommand defined.
1970 */
1971 int
1972has_modechanged(void)
1973{
1974 return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
1975}
1976#endif
1977
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001978/*
1979 * Execute autocommands for "event" and file name "fname".
1980 * Return TRUE if some commands were executed.
1981 */
1982 static int
1983apply_autocmds_group(
1984 event_T event,
1985 char_u *fname, // NULL or empty means use actual file name
1986 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
1987 // use fname
1988 int force, // when TRUE, ignore autocmd_busy
1989 int group, // group ID, or AUGROUP_ALL
1990 buf_T *buf, // buffer for <abuf>
1991 exarg_T *eap UNUSED) // command arguments
1992{
1993 char_u *sfname = NULL; // short file name
1994 char_u *tail;
1995 int save_changed;
1996 buf_T *old_curbuf;
1997 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001998 char_u *save_autocmd_fname;
1999 int save_autocmd_fname_full;
2000 int save_autocmd_bufnr;
2001 char_u *save_autocmd_match;
2002 int save_autocmd_busy;
2003 int save_autocmd_nested;
2004 static int nesting = 0;
LemonBoyeca7c602022-04-14 15:39:43 +01002005 AutoPatCmd_T patcmd;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002006 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002007 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002008#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002009 funccal_entry_T funccal_entry;
2010 char_u *save_cmdarg;
2011 long save_cmdbang;
2012#endif
2013 static int filechangeshell_busy = FALSE;
2014#ifdef FEAT_PROFILE
2015 proftime_T wait_time;
2016#endif
2017 int did_save_redobuff = FALSE;
2018 save_redo_T save_redo;
2019 int save_KeyTyped = KeyTyped;
ichizok7e5fe382023-04-15 13:17:50 +01002020 ESTACK_CHECK_DECLARATION;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002021
2022 /*
2023 * Quickly return if there are no autocommands for this event or
2024 * autocommands are blocked.
2025 */
2026 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
2027 || autocmd_blocked > 0)
2028 goto BYPASS_AU;
2029
2030 /*
2031 * When autocommands are busy, new autocommands are only executed when
2032 * explicitly enabled with the "nested" flag.
2033 */
2034 if (autocmd_busy && !(force || autocmd_nested))
2035 goto BYPASS_AU;
2036
2037#ifdef FEAT_EVAL
2038 /*
2039 * Quickly return when immediately aborting on error, or when an interrupt
2040 * occurred or an exception was thrown but not caught.
2041 */
2042 if (aborting())
2043 goto BYPASS_AU;
2044#endif
2045
2046 /*
2047 * FileChangedShell never nests, because it can create an endless loop.
2048 */
2049 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
2050 || event == EVENT_FILECHANGEDSHELLPOST))
2051 goto BYPASS_AU;
2052
2053 /*
2054 * Ignore events in 'eventignore'.
2055 */
2056 if (event_ignored(event))
2057 goto BYPASS_AU;
2058
2059 /*
2060 * Allow nesting of autocommands, but restrict the depth, because it's
2061 * possible to create an endless loop.
2062 */
2063 if (nesting == 10)
2064 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00002065 emsg(_(e_autocommand_nesting_too_deep));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002066 goto BYPASS_AU;
2067 }
2068
2069 /*
2070 * Check if these autocommands are disabled. Used when doing ":all" or
2071 * ":ball".
2072 */
2073 if ( (autocmd_no_enter
2074 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
2075 || (autocmd_no_leave
2076 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
2077 goto BYPASS_AU;
2078
Bram Moolenaarbb393d82022-12-09 12:21:50 +00002079 if (event == EVENT_CMDLINECHANGED)
2080 ++aucmd_cmdline_changed_count;
2081
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002082 /*
2083 * Save the autocmd_* variables and info about the current buffer.
2084 */
2085 save_autocmd_fname = autocmd_fname;
2086 save_autocmd_fname_full = autocmd_fname_full;
2087 save_autocmd_bufnr = autocmd_bufnr;
2088 save_autocmd_match = autocmd_match;
2089 save_autocmd_busy = autocmd_busy;
2090 save_autocmd_nested = autocmd_nested;
2091 save_changed = curbuf->b_changed;
2092 old_curbuf = curbuf;
2093
2094 /*
2095 * Set the file name to be used for <afile>.
2096 * Make a copy to avoid that changing a buffer name or directory makes it
2097 * invalid.
2098 */
2099 if (fname_io == NULL)
2100 {
2101 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
Bram Moolenaarbb393d82022-12-09 12:21:50 +00002102 || event == EVENT_OPTIONSET
2103 || event == EVENT_MODECHANGED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002104 autocmd_fname = NULL;
2105 else if (fname != NULL && !ends_excmd(*fname))
2106 autocmd_fname = fname;
2107 else if (buf != NULL)
2108 autocmd_fname = buf->b_ffname;
2109 else
2110 autocmd_fname = NULL;
2111 }
2112 else
2113 autocmd_fname = fname_io;
2114 if (autocmd_fname != NULL)
2115 autocmd_fname = vim_strsave(autocmd_fname);
2116 autocmd_fname_full = FALSE; // call FullName_save() later
2117
2118 /*
2119 * Set the buffer number to be used for <abuf>.
2120 */
2121 if (buf == NULL)
2122 autocmd_bufnr = 0;
2123 else
2124 autocmd_bufnr = buf->b_fnum;
2125
2126 /*
2127 * When the file name is NULL or empty, use the file name of buffer "buf".
2128 * Always use the full path of the file name to match with, in case
2129 * "allow_dirs" is set.
2130 */
2131 if (fname == NULL || *fname == NUL)
2132 {
2133 if (buf == NULL)
2134 fname = NULL;
2135 else
2136 {
2137#ifdef FEAT_SYN_HL
2138 if (event == EVENT_SYNTAX)
2139 fname = buf->b_p_syn;
2140 else
2141#endif
2142 if (event == EVENT_FILETYPE)
2143 fname = buf->b_p_ft;
2144 else
2145 {
2146 if (buf->b_sfname != NULL)
2147 sfname = vim_strsave(buf->b_sfname);
2148 fname = buf->b_ffname;
2149 }
2150 }
2151 if (fname == NULL)
2152 fname = (char_u *)"";
2153 fname = vim_strsave(fname); // make a copy, so we can change it
2154 }
2155 else
2156 {
2157 sfname = vim_strsave(fname);
2158 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002159 // ColorScheme, QuickFixCmd*, DirChanged and similar.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002160 if (event == EVENT_FILETYPE
2161 || event == EVENT_SYNTAX
2162 || event == EVENT_CMDLINECHANGED
2163 || event == EVENT_CMDLINEENTER
2164 || event == EVENT_CMDLINELEAVE
2165 || event == EVENT_CMDWINENTER
2166 || event == EVENT_CMDWINLEAVE
2167 || event == EVENT_CMDUNDEFINED
2168 || event == EVENT_FUNCUNDEFINED
2169 || event == EVENT_REMOTEREPLY
2170 || event == EVENT_SPELLFILEMISSING
2171 || event == EVENT_QUICKFIXCMDPRE
2172 || event == EVENT_COLORSCHEME
2173 || event == EVENT_COLORSCHEMEPRE
2174 || event == EVENT_OPTIONSET
2175 || event == EVENT_QUICKFIXCMDPOST
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002176 || event == EVENT_DIRCHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002177 || event == EVENT_DIRCHANGEDPRE
naohiro ono23beefe2021-11-13 12:38:49 +00002178 || event == EVENT_MODECHANGED
zeertzjqc601d982022-10-10 13:46:15 +01002179 || event == EVENT_MENUPOPUP
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002180 || event == EVENT_USER
LemonBoy09371822022-04-08 15:18:45 +01002181 || event == EVENT_WINCLOSED
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00002182 || event == EVENT_WINRESIZED
LemonBoy09371822022-04-08 15:18:45 +01002183 || event == EVENT_WINSCROLLED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002184 {
2185 fname = vim_strsave(fname);
2186 autocmd_fname_full = TRUE; // don't expand it later
2187 }
2188 else
2189 fname = FullName_save(fname, FALSE);
2190 }
2191 if (fname == NULL) // out of memory
2192 {
2193 vim_free(sfname);
2194 retval = FALSE;
2195 goto BYPASS_AU;
2196 }
2197
2198#ifdef BACKSLASH_IN_FILENAME
2199 /*
2200 * Replace all backslashes with forward slashes. This makes the
2201 * autocommand patterns portable between Unix and MS-DOS.
2202 */
2203 if (sfname != NULL)
2204 forward_slash(sfname);
2205 forward_slash(fname);
2206#endif
2207
2208#ifdef VMS
2209 // remove version for correct match
2210 if (sfname != NULL)
2211 vms_remove_version(sfname);
2212 vms_remove_version(fname);
2213#endif
2214
2215 /*
2216 * Set the name to be used for <amatch>.
2217 */
2218 autocmd_match = fname;
2219
2220
2221 // Don't redraw while doing autocommands.
2222 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002223
2224 // name and lnum are filled in later
2225 estack_push(ETYPE_AUCMD, NULL, 0);
ichizok7e5fe382023-04-15 13:17:50 +01002226 ESTACK_CHECK_SETUP;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002227
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002228 save_current_sctx = current_sctx;
2229
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002230#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002231# ifdef FEAT_PROFILE
2232 if (do_profiling == PROF_YES)
2233 prof_child_enter(&wait_time); // doesn't count for the caller itself
2234# endif
2235
2236 // Don't use local function variables, if called from a function.
2237 save_funccal(&funccal_entry);
2238#endif
2239
2240 /*
2241 * When starting to execute autocommands, save the search patterns.
2242 */
2243 if (!autocmd_busy)
2244 {
2245 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002246 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002247 {
2248 saveRedobuff(&save_redo);
2249 did_save_redobuff = TRUE;
2250 }
2251 did_filetype = keep_filetype;
2252 }
2253
2254 /*
2255 * Note that we are applying autocmds. Some commands need to know.
2256 */
2257 autocmd_busy = TRUE;
2258 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2259 ++nesting; // see matching decrement below
2260
2261 // Remember that FileType was triggered. Used for did_filetype().
2262 if (event == EVENT_FILETYPE)
2263 did_filetype = TRUE;
2264
2265 tail = gettail(fname);
2266
2267 // Find first autocommand that matches
LemonBoyeca7c602022-04-14 15:39:43 +01002268 CLEAR_FIELD(patcmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002269 patcmd.curpat = first_autopat[(int)event];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002270 patcmd.group = group;
2271 patcmd.fname = fname;
2272 patcmd.sfname = sfname;
2273 patcmd.tail = tail;
2274 patcmd.event = event;
2275 patcmd.arg_bufnr = autocmd_bufnr;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002276 auto_next_pat(&patcmd, FALSE);
2277
2278 // found one, start executing the autocommands
2279 if (patcmd.curpat != NULL)
2280 {
2281 // add to active_apc_list
2282 patcmd.next = active_apc_list;
2283 active_apc_list = &patcmd;
2284
2285#ifdef FEAT_EVAL
2286 // set v:cmdarg (only when there is a matching pattern)
2287 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2288 if (eap != NULL)
2289 {
2290 save_cmdarg = set_cmdarg(eap, NULL);
2291 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2292 }
2293 else
2294 save_cmdarg = NULL; // avoid gcc warning
2295#endif
2296 retval = TRUE;
2297 // mark the last pattern, to avoid an endless loop when more patterns
2298 // are added when executing autocommands
2299 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2300 ap->last = FALSE;
2301 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002302
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002303 // Make sure cursor and topline are valid. The first time the current
2304 // values are saved, restored by reset_lnums(). When nested only the
2305 // values are corrected when needed.
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002306 if (nesting == 1)
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002307 check_lnums(TRUE);
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002308 else
2309 check_lnums_nested(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002310
Christian Brabandt590aae32023-06-25 22:34:22 +01002311 int save_did_emsg = did_emsg;
2312 int save_ex_pressedreturn = get_pressedreturn();
ichizokc3f91c02021-12-17 09:44:33 +00002313
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002314 do_cmdline(NULL, getnextac, (void *)&patcmd,
2315 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002316
ichizokc3f91c02021-12-17 09:44:33 +00002317 did_emsg += save_did_emsg;
Christian Brabandt590aae32023-06-25 22:34:22 +01002318 set_pressedreturn(save_ex_pressedreturn);
ichizokc3f91c02021-12-17 09:44:33 +00002319
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002320 if (nesting == 1)
2321 // restore cursor and topline, unless they were changed
2322 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002323
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002324#ifdef FEAT_EVAL
2325 if (eap != NULL)
2326 {
2327 (void)set_cmdarg(NULL, save_cmdarg);
2328 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2329 }
2330#endif
2331 // delete from active_apc_list
2332 if (active_apc_list == &patcmd) // just in case
2333 active_apc_list = patcmd.next;
2334 }
2335
Bram Moolenaar79cdf022023-05-20 14:07:00 +01002336 if (RedrawingDisabled > 0)
2337 --RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002338 autocmd_busy = save_autocmd_busy;
2339 filechangeshell_busy = FALSE;
2340 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002341 vim_free(SOURCING_NAME);
ichizok7e5fe382023-04-15 13:17:50 +01002342 ESTACK_CHECK_NOW;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002343 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002344 vim_free(autocmd_fname);
2345 autocmd_fname = save_autocmd_fname;
2346 autocmd_fname_full = save_autocmd_fname_full;
2347 autocmd_bufnr = save_autocmd_bufnr;
2348 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002349 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002350#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002351 restore_funccal();
2352# ifdef FEAT_PROFILE
2353 if (do_profiling == PROF_YES)
2354 prof_child_exit(&wait_time);
2355# endif
2356#endif
2357 KeyTyped = save_KeyTyped;
2358 vim_free(fname);
2359 vim_free(sfname);
2360 --nesting; // see matching increment above
2361
2362 /*
2363 * When stopping to execute autocommands, restore the search patterns and
2364 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2365 * free any windows in the au_pending_free_win list.
2366 */
2367 if (!autocmd_busy)
2368 {
2369 restore_search_patterns();
2370 if (did_save_redobuff)
2371 restoreRedobuff(&save_redo);
2372 did_filetype = FALSE;
2373 while (au_pending_free_buf != NULL)
2374 {
2375 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002376
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002377 vim_free(au_pending_free_buf);
2378 au_pending_free_buf = b;
2379 }
2380 while (au_pending_free_win != NULL)
2381 {
2382 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002383
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002384 vim_free(au_pending_free_win);
2385 au_pending_free_win = w;
2386 }
2387 }
2388
2389 /*
2390 * Some events don't set or reset the Changed flag.
2391 * Check if still in the same buffer!
2392 */
2393 if (curbuf == old_curbuf
2394 && (event == EVENT_BUFREADPOST
2395 || event == EVENT_BUFWRITEPOST
2396 || event == EVENT_FILEAPPENDPOST
2397 || event == EVENT_VIMLEAVE
2398 || event == EVENT_VIMLEAVEPRE))
2399 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002400 if (curbuf->b_changed != save_changed)
2401 need_maketitle = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002402 curbuf->b_changed = save_changed;
2403 }
2404
2405 au_cleanup(); // may really delete removed patterns/commands now
2406
2407BYPASS_AU:
2408 // When wiping out a buffer make sure all its buffer-local autocommands
2409 // are deleted.
2410 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2411 aubuflocal_remove(buf);
2412
2413 if (retval == OK && event == EVENT_FILETYPE)
2414 au_did_filetype = TRUE;
2415
2416 return retval;
2417}
2418
2419# ifdef FEAT_EVAL
2420static char_u *old_termresponse = NULL;
2421# endif
2422
2423/*
2424 * Block triggering autocommands until unblock_autocmd() is called.
2425 * Can be used recursively, so long as it's symmetric.
2426 */
2427 void
2428block_autocmds(void)
2429{
2430# ifdef FEAT_EVAL
2431 // Remember the value of v:termresponse.
2432 if (autocmd_blocked == 0)
2433 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
2434# endif
2435 ++autocmd_blocked;
2436}
2437
2438 void
2439unblock_autocmds(void)
2440{
2441 --autocmd_blocked;
2442
2443# ifdef FEAT_EVAL
2444 // When v:termresponse was set while autocommands were blocked, trigger
2445 // the autocommands now. Esp. useful when executing a shell command
2446 // during startup (vimdiff).
2447 if (autocmd_blocked == 0
2448 && get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2449 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2450# endif
2451}
2452
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002453 int
2454is_autocmd_blocked(void)
2455{
2456 return autocmd_blocked != 0;
2457}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002458
2459/*
2460 * Find next autocommand pattern that matches.
2461 */
2462 static void
2463auto_next_pat(
LemonBoyeca7c602022-04-14 15:39:43 +01002464 AutoPatCmd_T *apc,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002465 int stop_at_last) // stop when 'last' flag is set
2466{
2467 AutoPat *ap;
2468 AutoCmd *cp;
2469 char_u *name;
2470 char *s;
LemonBoyeca7c602022-04-14 15:39:43 +01002471 estack_T *entry;
2472 char_u *namep;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002473
LemonBoyeca7c602022-04-14 15:39:43 +01002474 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
2475
2476 // Clear the exestack entry for this ETYPE_AUCMD entry.
2477 VIM_CLEAR(entry->es_name);
2478 entry->es_info.aucmd = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002479
2480 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2481 {
2482 apc->curpat = NULL;
2483
2484 // Only use a pattern when it has not been removed, has commands and
2485 // the group matches. For buffer-local autocommands only check the
2486 // buffer number.
2487 if (ap->pat != NULL && ap->cmds != NULL
2488 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2489 {
2490 // execution-condition
2491 if (ap->buflocal_nr == 0
2492 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2493 apc->sfname, apc->tail, ap->allow_dirs))
2494 : ap->buflocal_nr == apc->arg_bufnr)
2495 {
2496 name = event_nr2name(apc->event);
2497 s = _("%s Autocommands for \"%s\"");
LemonBoyeca7c602022-04-14 15:39:43 +01002498 namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1);
2499 if (namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002500 {
LemonBoyeca7c602022-04-14 15:39:43 +01002501 sprintf((char *)namep, s, (char *)name, (char *)ap->pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002502 if (p_verbose >= 8)
2503 {
2504 verbose_enter();
LemonBoyeca7c602022-04-14 15:39:43 +01002505 smsg(_("Executing %s"), namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002506 verbose_leave();
2507 }
2508 }
2509
LemonBoyeca7c602022-04-14 15:39:43 +01002510 // Update the exestack entry for this autocmd.
2511 entry->es_name = namep;
2512 entry->es_info.aucmd = apc;
2513
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002514 apc->curpat = ap;
2515 apc->nextcmd = ap->cmds;
2516 // mark last command
2517 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2518 cp->last = FALSE;
2519 cp->last = TRUE;
2520 }
2521 line_breakcheck();
2522 if (apc->curpat != NULL) // found a match
2523 break;
2524 }
2525 if (stop_at_last && ap->last)
2526 break;
2527 }
2528}
2529
Dominique Pellee764d1b2023-03-12 21:20:59 +00002530#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002531/*
LemonBoyeca7c602022-04-14 15:39:43 +01002532 * Get the script context where autocommand "acp" is defined.
2533 */
2534 sctx_T *
2535acp_script_ctx(AutoPatCmd_T *acp)
2536{
2537 return &acp->script_ctx;
2538}
Dominique Pellee764d1b2023-03-12 21:20:59 +00002539#endif
LemonBoyeca7c602022-04-14 15:39:43 +01002540
2541/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002542 * Get next autocommand command.
2543 * Called by do_cmdline() to get the next line for ":if".
2544 * Returns allocated string, or NULL for end of autocommands.
2545 */
2546 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002547getnextac(
2548 int c UNUSED,
2549 void *cookie,
2550 int indent UNUSED,
2551 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002552{
LemonBoyeca7c602022-04-14 15:39:43 +01002553 AutoPatCmd_T *acp = (AutoPatCmd_T *)cookie;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002554 char_u *retval;
2555 AutoCmd *ac;
2556
2557 // Can be called again after returning the last line.
2558 if (acp->curpat == NULL)
2559 return NULL;
2560
2561 // repeat until we find an autocommand to execute
2562 for (;;)
2563 {
2564 // skip removed commands
2565 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2566 if (acp->nextcmd->last)
2567 acp->nextcmd = NULL;
2568 else
2569 acp->nextcmd = acp->nextcmd->next;
2570
2571 if (acp->nextcmd != NULL)
2572 break;
2573
2574 // at end of commands, find next pattern that matches
2575 if (acp->curpat->last)
2576 acp->curpat = NULL;
2577 else
2578 acp->curpat = acp->curpat->next;
2579 if (acp->curpat != NULL)
2580 auto_next_pat(acp, TRUE);
2581 if (acp->curpat == NULL)
2582 return NULL;
2583 }
2584
2585 ac = acp->nextcmd;
2586
2587 if (p_verbose >= 9)
2588 {
2589 verbose_enter_scroll();
2590 smsg(_("autocommand %s"), ac->cmd);
2591 msg_puts("\n"); // don't overwrite this either
2592 verbose_leave_scroll();
2593 }
2594 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002595 // Remove one-shot ("once") autocmd in anticipation of its execution.
2596 if (ac->once)
2597 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002598 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002599 current_sctx = ac->script_ctx;
LemonBoyeca7c602022-04-14 15:39:43 +01002600 acp->script_ctx = current_sctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002601 if (ac->last)
2602 acp->nextcmd = NULL;
2603 else
2604 acp->nextcmd = ac->next;
2605 return retval;
2606}
2607
2608/*
2609 * Return TRUE if there is a matching autocommand for "fname".
2610 * To account for buffer-local autocommands, function needs to know
2611 * in which buffer the file will be opened.
2612 */
2613 int
2614has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2615{
2616 AutoPat *ap;
2617 char_u *fname;
2618 char_u *tail = gettail(sfname);
2619 int retval = FALSE;
2620
2621 fname = FullName_save(sfname, FALSE);
2622 if (fname == NULL)
2623 return FALSE;
2624
2625#ifdef BACKSLASH_IN_FILENAME
2626 /*
2627 * Replace all backslashes with forward slashes. This makes the
2628 * autocommand patterns portable between Unix and MS-DOS.
2629 */
2630 sfname = vim_strsave(sfname);
2631 if (sfname != NULL)
2632 forward_slash(sfname);
2633 forward_slash(fname);
2634#endif
2635
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002636 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002637 if (ap->pat != NULL && ap->cmds != NULL
2638 && (ap->buflocal_nr == 0
2639 ? match_file_pat(NULL, &ap->reg_prog,
2640 fname, sfname, tail, ap->allow_dirs)
2641 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2642 ))
2643 {
2644 retval = TRUE;
2645 break;
2646 }
2647
2648 vim_free(fname);
2649#ifdef BACKSLASH_IN_FILENAME
2650 vim_free(sfname);
2651#endif
2652
2653 return retval;
2654}
2655
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002656/*
2657 * Function given to ExpandGeneric() to obtain the list of autocommand group
2658 * names.
2659 */
2660 char_u *
2661get_augroup_name(expand_T *xp UNUSED, int idx)
2662{
2663 if (idx == augroups.ga_len) // add "END" add the end
2664 return (char_u *)"END";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002665 if (idx < 0 || idx >= augroups.ga_len) // end of list
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002666 return NULL;
2667 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2668 // skip deleted entries
2669 return (char_u *)"";
2670 return AUGROUP_NAME(idx); // return a name
2671}
2672
2673static int include_groups = FALSE;
2674
2675 char_u *
2676set_context_in_autocmd(
2677 expand_T *xp,
2678 char_u *arg,
2679 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2680{
2681 char_u *p;
2682 int group;
2683
2684 // check for a group name, skip it if present
2685 include_groups = FALSE;
2686 p = arg;
2687 group = au_get_grouparg(&arg);
2688 if (group == AUGROUP_ERROR)
2689 return NULL;
2690 // If there only is a group name that's what we expand.
2691 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2692 {
2693 arg = p;
2694 group = AUGROUP_ALL;
2695 }
2696
2697 // skip over event name
2698 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2699 if (*p == ',')
2700 arg = p + 1;
2701 if (*p == NUL)
2702 {
2703 if (group == AUGROUP_ALL)
2704 include_groups = TRUE;
2705 xp->xp_context = EXPAND_EVENTS; // expand event name
2706 xp->xp_pattern = arg;
2707 return NULL;
2708 }
2709
2710 // skip over pattern
2711 arg = skipwhite(p);
2712 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2713 arg++;
2714 if (*arg)
2715 return arg; // expand (next) command
2716
2717 if (doautocmd)
2718 xp->xp_context = EXPAND_FILES; // expand file names
2719 else
2720 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2721 return NULL;
2722}
2723
2724/*
2725 * Function given to ExpandGeneric() to obtain the list of event names.
2726 */
2727 char_u *
2728get_event_name(expand_T *xp UNUSED, int idx)
2729{
2730 if (idx < augroups.ga_len) // First list group names, if wanted
2731 {
2732 if (!include_groups || AUGROUP_NAME(idx) == NULL
2733 || AUGROUP_NAME(idx) == get_deleted_augroup())
2734 return (char_u *)""; // skip deleted entries
2735 return AUGROUP_NAME(idx); // return a name
2736 }
2737 return (char_u *)event_names[idx - augroups.ga_len].name;
2738}
2739
Yee Cheng Chin900894b2023-09-29 20:42:32 +02002740/*
2741 * Function given to ExpandGeneric() to obtain the list of event names. Don't
2742 * include groups.
2743 */
2744 char_u *
2745get_event_name_no_group(expand_T *xp UNUSED, int idx)
2746{
2747 return (char_u *)event_names[idx].name;
2748}
2749
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002750
2751#if defined(FEAT_EVAL) || defined(PROTO)
2752/*
2753 * Return TRUE if autocmd is supported.
2754 */
2755 int
2756autocmd_supported(char_u *name)
2757{
2758 char_u *p;
2759
2760 return (event_name2nr(name, &p) != NUM_EVENTS);
2761}
2762
2763/*
2764 * Return TRUE if an autocommand is defined for a group, event and
2765 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2766 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2767 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2768 * Used for:
2769 * exists("#Group") or
2770 * exists("#Group#Event") or
2771 * exists("#Group#Event#pat") or
2772 * exists("#Event") or
2773 * exists("#Event#pat")
2774 */
2775 int
2776au_exists(char_u *arg)
2777{
2778 char_u *arg_save;
2779 char_u *pattern = NULL;
2780 char_u *event_name;
2781 char_u *p;
2782 event_T event;
2783 AutoPat *ap;
2784 buf_T *buflocal_buf = NULL;
2785 int group;
2786 int retval = FALSE;
2787
2788 // Make a copy so that we can change the '#' chars to a NUL.
2789 arg_save = vim_strsave(arg);
2790 if (arg_save == NULL)
2791 return FALSE;
2792 p = vim_strchr(arg_save, '#');
2793 if (p != NULL)
2794 *p++ = NUL;
2795
2796 // First, look for an autocmd group name
2797 group = au_find_group(arg_save);
2798 if (group == AUGROUP_ERROR)
2799 {
2800 // Didn't match a group name, assume the first argument is an event.
2801 group = AUGROUP_ALL;
2802 event_name = arg_save;
2803 }
2804 else
2805 {
2806 if (p == NULL)
2807 {
2808 // "Group": group name is present and it's recognized
2809 retval = TRUE;
2810 goto theend;
2811 }
2812
2813 // Must be "Group#Event" or "Group#Event#pat".
2814 event_name = p;
2815 p = vim_strchr(event_name, '#');
2816 if (p != NULL)
2817 *p++ = NUL; // "Group#Event#pat"
2818 }
2819
2820 pattern = p; // "pattern" is NULL when there is no pattern
2821
2822 // find the index (enum) for the event name
2823 event = event_name2nr(event_name, &p);
2824
2825 // return FALSE if the event name is not recognized
2826 if (event == NUM_EVENTS)
2827 goto theend;
2828
2829 // Find the first autocommand for this event.
2830 // If there isn't any, return FALSE;
2831 // If there is one and no pattern given, return TRUE;
2832 ap = first_autopat[(int)event];
2833 if (ap == NULL)
2834 goto theend;
2835
2836 // if pattern is "<buffer>", special handling is needed which uses curbuf
2837 // for pattern "<buffer=N>, fnamecmp() will work fine
2838 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2839 buflocal_buf = curbuf;
2840
2841 // Check if there is an autocommand with the given pattern.
2842 for ( ; ap != NULL; ap = ap->next)
2843 // only use a pattern when it has not been removed and has commands.
2844 // For buffer-local autocommands, fnamecmp() works fine.
2845 if (ap->pat != NULL && ap->cmds != NULL
2846 && (group == AUGROUP_ALL || ap->group == group)
2847 && (pattern == NULL
2848 || (buflocal_buf == NULL
2849 ? fnamecmp(ap->pat, pattern) == 0
2850 : ap->buflocal_nr == buflocal_buf->b_fnum)))
2851 {
2852 retval = TRUE;
2853 break;
2854 }
2855
2856theend:
2857 vim_free(arg_save);
2858 return retval;
2859}
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002860
2861/*
2862 * autocmd_add() and autocmd_delete() functions
2863 */
2864 static void
2865autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
2866{
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002867 list_T *aucmd_list;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002868 listitem_T *li;
2869 dict_T *event_dict;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002870 dictitem_T *di;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002871 char_u *event_name = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002872 list_T *event_list;
2873 listitem_T *eli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002874 event_T event;
2875 char_u *group_name = NULL;
2876 int group;
2877 char_u *pat = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002878 list_T *pat_list;
2879 listitem_T *pli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002880 char_u *cmd = NULL;
2881 char_u *end;
2882 int once;
2883 int nested;
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01002884 int replace; // replace the cmd for a group/event
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002885 int retval = VVAL_TRUE;
2886 int save_augroup = current_augroup;
2887
2888 rettv->v_type = VAR_BOOL;
2889 rettv->vval.v_number = VVAL_FALSE;
2890
2891 if (check_for_list_arg(argvars, 0) == FAIL)
2892 return;
2893
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002894 aucmd_list = argvars[0].vval.v_list;
2895 if (aucmd_list == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002896 return;
2897
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002898 FOR_ALL_LIST_ITEMS(aucmd_list, li)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002899 {
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002900 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002901 VIM_CLEAR(cmd);
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002902 event_name = NULL;
2903 event_list = NULL;
2904 pat = NULL;
2905 pat_list = NULL;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002906
2907 if (li->li_tv.v_type != VAR_DICT)
2908 continue;
2909
2910 event_dict = li->li_tv.vval.v_dict;
2911 if (event_dict == NULL)
2912 continue;
2913
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002914 di = dict_find(event_dict, (char_u *)"event", -1);
2915 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002916 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002917 if (di->di_tv.v_type == VAR_STRING)
2918 {
2919 event_name = di->di_tv.vval.v_string;
2920 if (event_name == NULL)
2921 {
2922 emsg(_(e_string_required));
2923 continue;
2924 }
2925 }
2926 else if (di->di_tv.v_type == VAR_LIST)
2927 {
2928 event_list = di->di_tv.vval.v_list;
2929 if (event_list == NULL)
2930 {
2931 emsg(_(e_list_required));
2932 continue;
2933 }
2934 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002935 else
2936 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002937 emsg(_(e_string_or_list_expected));
2938 continue;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002939 }
2940 }
2941
Bram Moolenaard61efa52022-07-23 09:52:04 +01002942 group_name = dict_get_string(event_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002943 if (group_name == NULL || *group_name == NUL)
2944 // if the autocmd group name is not specified, then use the current
2945 // autocmd group
2946 group = current_augroup;
2947 else
2948 {
2949 group = au_find_group(group_name);
2950 if (group == AUGROUP_ERROR)
2951 {
2952 if (delete)
2953 {
2954 semsg(_(e_no_such_group_str), group_name);
2955 retval = VVAL_FALSE;
2956 break;
2957 }
2958 // group is not found, create it now
2959 group = au_new_group(group_name);
2960 if (group == AUGROUP_ERROR)
2961 {
2962 semsg(_(e_no_such_group_str), group_name);
2963 retval = VVAL_FALSE;
2964 break;
2965 }
2966
2967 current_augroup = group;
2968 }
2969 }
2970
2971 // if a buffer number is specified, then generate a pattern of the form
2972 // "<buffer=n>. Otherwise, use the pattern supplied by the user.
2973 if (dict_has_key(event_dict, "bufnr"))
2974 {
2975 varnumber_T bnum;
2976
Bram Moolenaard61efa52022-07-23 09:52:04 +01002977 bnum = dict_get_number_def(event_dict, "bufnr", -1);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002978 if (bnum == -1)
2979 continue;
2980
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002981 vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
2982 pat = IObuff;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002983 }
2984 else
2985 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002986 di = dict_find(event_dict, (char_u *)"pattern", -1);
2987 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002988 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002989 if (di->di_tv.v_type == VAR_STRING)
2990 {
2991 pat = di->di_tv.vval.v_string;
2992 if (pat == NULL)
2993 {
2994 emsg(_(e_string_required));
2995 continue;
2996 }
2997 }
2998 else if (di->di_tv.v_type == VAR_LIST)
2999 {
3000 pat_list = di->di_tv.vval.v_list;
3001 if (pat_list == NULL)
3002 {
3003 emsg(_(e_list_required));
3004 continue;
3005 }
3006 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003007 else
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003008 {
3009 emsg(_(e_string_or_list_expected));
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003010 continue;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003011 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003012 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003013 else if (delete)
3014 pat = (char_u *)"";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003015 }
3016
Bram Moolenaard61efa52022-07-23 09:52:04 +01003017 once = dict_get_bool(event_dict, "once", FALSE);
3018 nested = dict_get_bool(event_dict, "nested", FALSE);
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01003019 // if 'replace' is true, then remove all the commands associated with
3020 // this autocmd event/group and add the new command.
Bram Moolenaard61efa52022-07-23 09:52:04 +01003021 replace = dict_get_bool(event_dict, "replace", FALSE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003022
Bram Moolenaard61efa52022-07-23 09:52:04 +01003023 cmd = dict_get_string(event_dict, "cmd", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003024 if (cmd == NULL)
3025 {
3026 if (delete)
3027 cmd = vim_strsave((char_u *)"");
3028 else
3029 continue;
3030 }
3031
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003032 if (delete && (event_name == NULL
3033 || (event_name[0] == '*' && event_name[1] == NUL)))
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003034 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003035 // if the event name is not specified or '*', delete all the events
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003036 for (event = (event_T)0; (int)event < NUM_EVENTS;
3037 event = (event_T)((int)event + 1))
3038 {
3039 if (do_autocmd_event(event, pat, once, nested, cmd, delete,
3040 group, 0) == FAIL)
3041 {
3042 retval = VVAL_FALSE;
3043 break;
3044 }
3045 }
3046 }
3047 else
3048 {
Bram Moolenaar968443e2022-05-27 21:16:34 +01003049 char_u *p = NULL;
3050
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003051 eli = NULL;
3052 end = NULL;
3053 while (TRUE)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003054 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003055 if (event_list != NULL)
3056 {
3057 if (eli == NULL)
3058 eli = event_list->lv_first;
3059 else
3060 eli = eli->li_next;
3061 if (eli == NULL)
3062 break;
3063 if (eli->li_tv.v_type != VAR_STRING
Bram Moolenaar882476a2022-06-01 16:02:38 +01003064 || (p = eli->li_tv.vval.v_string) == NULL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003065 {
3066 emsg(_(e_string_required));
Bram Moolenaar882476a2022-06-01 16:02:38 +01003067 break;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003068 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003069 }
3070 else
3071 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003072 if (p == NULL)
3073 p = event_name;
3074 if (p == NULL || *p == NUL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003075 break;
3076 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003077
3078 event = event_name2nr(p, &end);
3079 if (event == NUM_EVENTS || *end != NUL)
3080 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003081 // this also catches something following a valid event name
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003082 semsg(_(e_no_such_event_str), p);
3083 retval = VVAL_FALSE;
3084 break;
3085 }
3086 if (pat != NULL)
3087 {
3088 if (do_autocmd_event(event, pat, once, nested, cmd,
3089 delete | replace, group, 0) == FAIL)
3090 {
3091 retval = VVAL_FALSE;
3092 break;
3093 }
3094 }
3095 else if (pat_list != NULL)
3096 {
3097 FOR_ALL_LIST_ITEMS(pat_list, pli)
3098 {
3099 if (pli->li_tv.v_type != VAR_STRING
3100 || pli->li_tv.vval.v_string == NULL)
3101 {
3102 emsg(_(e_string_required));
3103 continue;
3104 }
3105 if (do_autocmd_event(event,
3106 pli->li_tv.vval.v_string, once, nested,
3107 cmd, delete | replace, group, 0) ==
3108 FAIL)
3109 {
3110 retval = VVAL_FALSE;
3111 break;
3112 }
3113 }
3114 if (retval == VVAL_FALSE)
3115 break;
3116 }
3117 if (event_name != NULL)
3118 p = end;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003119 }
3120 }
3121
3122 // if only the autocmd group name is specified for delete and the
3123 // autocmd event, pattern and cmd are not specified, then delete the
3124 // autocmd group.
3125 if (delete && group_name != NULL &&
3126 (event_name == NULL || event_name[0] == NUL)
3127 && (pat == NULL || pat[0] == NUL)
3128 && (cmd == NULL || cmd[0] == NUL))
3129 au_del_group(group_name);
3130 }
3131
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003132 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003133 VIM_CLEAR(cmd);
3134
3135 current_augroup = save_augroup;
3136 rettv->vval.v_number = retval;
3137}
3138
3139/*
3140 * autocmd_add() function
3141 */
3142 void
3143f_autocmd_add(typval_T *argvars, typval_T *rettv)
3144{
3145 autocmd_add_or_delete(argvars, rettv, FALSE);
3146}
3147
3148/*
3149 * autocmd_delete() function
3150 */
3151 void
3152f_autocmd_delete(typval_T *argvars, typval_T *rettv)
3153{
3154 autocmd_add_or_delete(argvars, rettv, TRUE);
3155}
3156
3157/*
3158 * autocmd_get() function
3159 * Returns a List of autocmds.
3160 */
3161 void
3162f_autocmd_get(typval_T *argvars, typval_T *rettv)
3163{
3164 event_T event_arg = NUM_EVENTS;
3165 event_T event;
3166 AutoPat *ap;
3167 AutoCmd *ac;
3168 list_T *event_list;
3169 dict_T *event_dict;
3170 char_u *event_name = NULL;
3171 char_u *pat = NULL;
3172 char_u *name = NULL;
3173 int group = AUGROUP_ALL;
3174
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01003175 if (rettv_list_alloc(rettv) == FAIL)
3176 return;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003177 if (check_for_opt_dict_arg(argvars, 0) == FAIL)
3178 return;
3179
3180 if (argvars[0].v_type == VAR_DICT)
3181 {
3182 // return only the autocmds in the specified group
3183 if (dict_has_key(argvars[0].vval.v_dict, "group"))
3184 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003185 name = dict_get_string(argvars[0].vval.v_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003186 if (name == NULL)
3187 return;
3188
3189 if (*name == NUL)
3190 group = AUGROUP_DEFAULT;
3191 else
3192 {
3193 group = au_find_group(name);
3194 if (group == AUGROUP_ERROR)
3195 {
3196 semsg(_(e_no_such_group_str), name);
3197 vim_free(name);
3198 return;
3199 }
3200 }
3201 vim_free(name);
3202 }
3203
3204 // return only the autocmds for the specified event
3205 if (dict_has_key(argvars[0].vval.v_dict, "event"))
3206 {
3207 int i;
3208
Bram Moolenaard61efa52022-07-23 09:52:04 +01003209 name = dict_get_string(argvars[0].vval.v_dict, "event", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003210 if (name == NULL)
3211 return;
3212
3213 if (name[0] == '*' && name[1] == NUL)
3214 event_arg = NUM_EVENTS;
3215 else
3216 {
3217 for (i = 0; event_names[i].name != NULL; i++)
3218 if (STRICMP(event_names[i].name, name) == 0)
3219 break;
3220 if (event_names[i].name == NULL)
3221 {
3222 semsg(_(e_no_such_event_str), name);
3223 vim_free(name);
3224 return;
3225 }
3226 event_arg = event_names[i].event;
3227 }
3228 vim_free(name);
3229 }
3230
3231 // return only the autocmds for the specified pattern
3232 if (dict_has_key(argvars[0].vval.v_dict, "pattern"))
3233 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003234 pat = dict_get_string(argvars[0].vval.v_dict, "pattern", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003235 if (pat == NULL)
3236 return;
3237 }
3238 }
3239
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003240 event_list = rettv->vval.v_list;
3241
3242 // iterate through all the autocmd events
3243 for (event = (event_T)0; (int)event < NUM_EVENTS;
3244 event = (event_T)((int)event + 1))
3245 {
3246 if (event_arg != NUM_EVENTS && event != event_arg)
3247 continue;
3248
3249 event_name = event_nr2name(event);
3250
3251 // iterate through all the patterns for this autocmd event
3252 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
3253 {
3254 char_u *group_name;
3255
3256 if (group != AUGROUP_ALL && group != ap->group)
3257 continue;
3258
3259 if (pat != NULL && STRCMP(pat, ap->pat) != 0)
3260 continue;
3261
3262 group_name = get_augroup_name(NULL, ap->group);
3263
3264 // iterate through all the commands for this pattern and add one
3265 // item for each cmd.
3266 for (ac = ap->cmds; ac != NULL; ac = ac->next)
3267 {
3268 event_dict = dict_alloc();
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003269 if (event_dict == NULL
3270 || list_append_dict(event_list, event_dict) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003271 return;
3272
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003273 if (dict_add_string(event_dict, "event", event_name) == FAIL
3274 || dict_add_string(event_dict, "group",
3275 group_name == NULL ? (char_u *)""
3276 : group_name) == FAIL
3277 || (ap->buflocal_nr != 0
3278 && (dict_add_number(event_dict, "bufnr",
3279 ap->buflocal_nr) == FAIL))
3280 || dict_add_string(event_dict, "pattern",
3281 ap->pat) == FAIL
3282 || dict_add_string(event_dict, "cmd", ac->cmd) == FAIL
3283 || dict_add_bool(event_dict, "once", ac->once) == FAIL
3284 || dict_add_bool(event_dict, "nested",
3285 ac->nested) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003286 return;
3287 }
3288 }
3289 }
3290
3291 vim_free(pat);
3292}
3293
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01003294#endif