blob: c376f20a231469768309831c4ee41cdd1405ff1a [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},
185 {"User", EVENT_USER},
186 {"VimEnter", EVENT_VIMENTER},
187 {"VimLeave", EVENT_VIMLEAVE},
188 {"VimLeavePre", EVENT_VIMLEAVEPRE},
189 {"WinNew", EVENT_WINNEW},
naohiro ono23beefe2021-11-13 12:38:49 +0000190 {"WinClosed", EVENT_WINCLOSED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100191 {"WinEnter", EVENT_WINENTER},
192 {"WinLeave", EVENT_WINLEAVE},
LemonBoy09371822022-04-08 15:18:45 +0100193 {"WinScrolled", EVENT_WINSCROLLED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100194 {"VimResized", EVENT_VIMRESIZED},
195 {"TextYankPost", EVENT_TEXTYANKPOST},
Bram Moolenaar100118c2020-12-11 19:30:34 +0100196 {"VimSuspend", EVENT_VIMSUSPEND},
197 {"VimResume", EVENT_VIMRESUME},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100198 {NULL, (event_T)0}
199};
200
201static AutoPat *first_autopat[NUM_EVENTS] =
202{
203 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
204 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
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};
210
211static AutoPat *last_autopat[NUM_EVENTS] =
212{
213 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
214 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
215 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
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};
220
kylo252ae6f1d82022-02-16 19:24:07 +0000221#define AUGROUP_DEFAULT (-1) // default autocmd group
222#define AUGROUP_ERROR (-2) // erroneous autocmd group
223#define AUGROUP_ALL (-3) // all autocmd groups
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100224
225/*
226 * struct used to keep status while executing autocommands for an event.
227 */
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100228struct AutoPatCmd_S
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100229{
230 AutoPat *curpat; // next AutoPat to examine
231 AutoCmd *nextcmd; // next AutoCmd to execute
232 int group; // group being used
233 char_u *fname; // fname to match with
234 char_u *sfname; // sfname to match with
235 char_u *tail; // tail of fname
236 event_T event; // current event
LemonBoyeca7c602022-04-14 15:39:43 +0100237 sctx_T script_ctx; // script context where it is defined
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100238 int arg_bufnr; // Initially equal to <abuf>, set to zero when
239 // buf is deleted.
LemonBoyeca7c602022-04-14 15:39:43 +0100240 AutoPatCmd_T *next; // chain of active apc-s for auto-invalidation
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100241};
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100242
LemonBoyeca7c602022-04-14 15:39:43 +0100243static AutoPatCmd_T *active_apc_list = NULL; // stack of active autocommands
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100244
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200245// Macro to loop over all the patterns for an autocmd event
246#define FOR_ALL_AUTOCMD_PATTERNS(event, ap) \
247 for ((ap) = first_autopat[(int)(event)]; (ap) != NULL; (ap) = (ap)->next)
248
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100249/*
250 * augroups stores a list of autocmd group names.
251 */
252static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
253#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
Bram Moolenaarc667da52019-11-30 20:52:27 +0100254// use get_deleted_augroup() to get this
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100255static char_u *deleted_augroup = NULL;
256
257/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100258 * The ID of the current group. Group 0 is the default one.
259 */
260static int current_augroup = AUGROUP_DEFAULT;
261
Bram Moolenaarc667da52019-11-30 20:52:27 +0100262static int au_need_clean = FALSE; // need to delete marked patterns
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100263
264static char_u *event_nr2name(event_T event);
265static int au_get_grouparg(char_u **argp);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200266static 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 +0100267static 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 +0100268static void auto_next_pat(AutoPatCmd_T *apc, int stop_at_last);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100269static int au_find_group(char_u *name);
270
271static event_T last_event;
272static int last_group;
Bram Moolenaarc667da52019-11-30 20:52:27 +0100273static int autocmd_blocked = 0; // block all autocmds
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100274
275 static char_u *
276get_deleted_augroup(void)
277{
278 if (deleted_augroup == NULL)
279 deleted_augroup = (char_u *)_("--Deleted--");
280 return deleted_augroup;
281}
282
283/*
284 * Show the autocommands for one AutoPat.
285 */
286 static void
287show_autocmd(AutoPat *ap, event_T event)
288{
289 AutoCmd *ac;
290
291 // Check for "got_int" (here and at various places below), which is set
292 // when "q" has been hit for the "--more--" prompt
293 if (got_int)
294 return;
295 if (ap->pat == NULL) // pattern has been removed
296 return;
297
298 msg_putchar('\n');
299 if (got_int)
300 return;
301 if (event != last_event || ap->group != last_group)
302 {
303 if (ap->group != AUGROUP_DEFAULT)
304 {
305 if (AUGROUP_NAME(ap->group) == NULL)
306 msg_puts_attr((char *)get_deleted_augroup(), HL_ATTR(HLF_E));
307 else
308 msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
309 msg_puts(" ");
310 }
311 msg_puts_attr((char *)event_nr2name(event), HL_ATTR(HLF_T));
312 last_event = event;
313 last_group = ap->group;
314 msg_putchar('\n');
315 if (got_int)
316 return;
317 }
318 msg_col = 4;
319 msg_outtrans(ap->pat);
320
321 for (ac = ap->cmds; ac != NULL; ac = ac->next)
322 {
323 if (ac->cmd != NULL) // skip removed commands
324 {
325 if (msg_col >= 14)
326 msg_putchar('\n');
327 msg_col = 14;
328 if (got_int)
329 return;
330 msg_outtrans(ac->cmd);
331#ifdef FEAT_EVAL
332 if (p_verbose > 0)
333 last_set_msg(ac->script_ctx);
334#endif
335 if (got_int)
336 return;
337 if (ac->next != NULL)
338 {
339 msg_putchar('\n');
340 if (got_int)
341 return;
342 }
343 }
344 }
345}
346
347/*
348 * Mark an autocommand pattern for deletion.
349 */
350 static void
351au_remove_pat(AutoPat *ap)
352{
353 VIM_CLEAR(ap->pat);
354 ap->buflocal_nr = -1;
355 au_need_clean = TRUE;
356}
357
358/*
359 * Mark all commands for a pattern for deletion.
360 */
361 static void
362au_remove_cmds(AutoPat *ap)
363{
364 AutoCmd *ac;
365
366 for (ac = ap->cmds; ac != NULL; ac = ac->next)
367 VIM_CLEAR(ac->cmd);
368 au_need_clean = TRUE;
369}
370
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200371// Delete one command from an autocmd pattern.
372static void au_del_cmd(AutoCmd *ac)
373{
374 VIM_CLEAR(ac->cmd);
375 au_need_clean = TRUE;
376}
377
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100378/*
379 * Cleanup autocommands and patterns that have been deleted.
380 * This is only done when not executing autocommands.
381 */
382 static void
383au_cleanup(void)
384{
385 AutoPat *ap, **prev_ap;
386 AutoCmd *ac, **prev_ac;
387 event_T event;
388
389 if (autocmd_busy || !au_need_clean)
390 return;
391
392 // loop over all events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100393 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100394 event = (event_T)((int)event + 1))
395 {
396 // loop over all autocommand patterns
397 prev_ap = &(first_autopat[(int)event]);
398 for (ap = *prev_ap; ap != NULL; ap = *prev_ap)
399 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200400 int has_cmd = FALSE;
401
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200402 // loop over all commands for this pattern
403 prev_ac = &(ap->cmds);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100404 for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
405 {
406 // remove the command if the pattern is to be deleted or when
407 // the command has been marked for deletion
408 if (ap->pat == NULL || ac->cmd == NULL)
409 {
410 *prev_ac = ac->next;
411 vim_free(ac->cmd);
412 vim_free(ac);
413 }
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200414 else
415 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200416 has_cmd = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100417 prev_ac = &(ac->next);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200418 }
419 }
420
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200421 if (ap->pat != NULL && !has_cmd)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200422 // Pattern was not marked for deletion, but all of its
423 // commands were. So mark the pattern for deletion.
424 au_remove_pat(ap);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100425
426 // remove the pattern if it has been marked for deletion
427 if (ap->pat == NULL)
428 {
429 if (ap->next == NULL)
430 {
431 if (prev_ap == &(first_autopat[(int)event]))
432 last_autopat[(int)event] = NULL;
433 else
434 // this depends on the "next" field being the first in
435 // the struct
436 last_autopat[(int)event] = (AutoPat *)prev_ap;
437 }
438 *prev_ap = ap->next;
439 vim_regfree(ap->reg_prog);
440 vim_free(ap);
441 }
442 else
443 prev_ap = &(ap->next);
444 }
445 }
446
447 au_need_clean = FALSE;
448}
449
450/*
451 * Called when buffer is freed, to remove/invalidate related buffer-local
452 * autocmds.
453 */
454 void
455aubuflocal_remove(buf_T *buf)
456{
LemonBoyeca7c602022-04-14 15:39:43 +0100457 AutoPat *ap;
458 event_T event;
459 AutoPatCmd_T *apc;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100460
461 // invalidate currently executing autocommands
462 for (apc = active_apc_list; apc; apc = apc->next)
463 if (buf->b_fnum == apc->arg_bufnr)
464 apc->arg_bufnr = 0;
465
466 // invalidate buflocals looping through events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100467 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100468 event = (event_T)((int)event + 1))
469 // loop over all autocommand patterns
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200470 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100471 if (ap->buflocal_nr == buf->b_fnum)
472 {
473 au_remove_pat(ap);
474 if (p_verbose >= 6)
475 {
476 verbose_enter();
477 smsg(_("auto-removing autocommand: %s <buffer=%d>"),
478 event_nr2name(event), buf->b_fnum);
479 verbose_leave();
480 }
481 }
482 au_cleanup();
483}
484
485/*
486 * Add an autocmd group name.
487 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
488 */
489 static int
490au_new_group(char_u *name)
491{
492 int i;
493
494 i = au_find_group(name);
495 if (i == AUGROUP_ERROR) // the group doesn't exist yet, add it
496 {
497 // First try using a free entry.
498 for (i = 0; i < augroups.ga_len; ++i)
499 if (AUGROUP_NAME(i) == NULL)
500 break;
501 if (i == augroups.ga_len && ga_grow(&augroups, 1) == FAIL)
502 return AUGROUP_ERROR;
503
504 AUGROUP_NAME(i) = vim_strsave(name);
505 if (AUGROUP_NAME(i) == NULL)
506 return AUGROUP_ERROR;
507 if (i == augroups.ga_len)
508 ++augroups.ga_len;
509 }
510
511 return i;
512}
513
514 static void
515au_del_group(char_u *name)
516{
517 int i;
518
519 i = au_find_group(name);
520 if (i == AUGROUP_ERROR) // the group doesn't exist
Bram Moolenaarf1474d82021-12-31 19:59:55 +0000521 semsg(_(e_no_such_group_str), name);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100522 else if (i == current_augroup)
Bram Moolenaarf1474d82021-12-31 19:59:55 +0000523 emsg(_(e_cannot_delete_current_group));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100524 else
525 {
526 event_T event;
527 AutoPat *ap;
528 int in_use = FALSE;
529
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100530 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100531 event = (event_T)((int)event + 1))
532 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200533 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100534 if (ap->group == i && ap->pat != NULL)
535 {
536 give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
537 in_use = TRUE;
538 event = NUM_EVENTS;
539 break;
540 }
541 }
542 vim_free(AUGROUP_NAME(i));
543 if (in_use)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100544 AUGROUP_NAME(i) = get_deleted_augroup();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100545 else
546 AUGROUP_NAME(i) = NULL;
547 }
548}
549
550/*
551 * Find the ID of an autocmd group name.
552 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
553 */
554 static int
555au_find_group(char_u *name)
556{
557 int i;
558
559 for (i = 0; i < augroups.ga_len; ++i)
560 if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
561 && STRCMP(AUGROUP_NAME(i), name) == 0)
562 return i;
563 return AUGROUP_ERROR;
564}
565
566/*
567 * Return TRUE if augroup "name" exists.
568 */
569 int
570au_has_group(char_u *name)
571{
572 return au_find_group(name) != AUGROUP_ERROR;
573}
574
575/*
576 * ":augroup {name}".
577 */
578 void
579do_augroup(char_u *arg, int del_group)
580{
581 int i;
582
583 if (del_group)
584 {
585 if (*arg == NUL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000586 emsg(_(e_argument_required));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100587 else
588 au_del_group(arg);
589 }
590 else if (STRICMP(arg, "end") == 0) // ":aug end": back to group 0
591 current_augroup = AUGROUP_DEFAULT;
592 else if (*arg) // ":aug xxx": switch to group xxx
593 {
594 i = au_new_group(arg);
595 if (i != AUGROUP_ERROR)
596 current_augroup = i;
597 }
598 else // ":aug": list the group names
599 {
600 msg_start();
601 for (i = 0; i < augroups.ga_len; ++i)
602 {
603 if (AUGROUP_NAME(i) != NULL)
604 {
605 msg_puts((char *)AUGROUP_NAME(i));
606 msg_puts(" ");
607 }
608 }
609 msg_clr_eos();
610 msg_end();
611 }
612}
613
614#if defined(EXITFREE) || defined(PROTO)
615 void
616free_all_autocmds(void)
617{
618 int i;
619 char_u *s;
620
621 for (current_augroup = -1; current_augroup < augroups.ga_len;
622 ++current_augroup)
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200623 do_autocmd(NULL, (char_u *)"", TRUE);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100624
625 for (i = 0; i < augroups.ga_len; ++i)
626 {
627 s = ((char_u **)(augroups.ga_data))[i];
628 if (s != get_deleted_augroup())
629 vim_free(s);
630 }
631 ga_clear(&augroups);
632}
633#endif
634
635/*
636 * Return the event number for event name "start".
637 * Return NUM_EVENTS if the event name was not found.
638 * Return a pointer to the next event name in "end".
639 */
640 static event_T
641event_name2nr(char_u *start, char_u **end)
642{
643 char_u *p;
644 int i;
645 int len;
646
647 // the event name ends with end of line, '|', a blank or a comma
648 for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
649 ;
650 for (i = 0; event_names[i].name != NULL; ++i)
651 {
652 len = (int)STRLEN(event_names[i].name);
653 if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0)
654 break;
655 }
656 if (*p == ',')
657 ++p;
658 *end = p;
659 if (event_names[i].name == NULL)
660 return NUM_EVENTS;
661 return event_names[i].event;
662}
663
664/*
665 * Return the name for event "event".
666 */
667 static char_u *
668event_nr2name(event_T event)
669{
670 int i;
671
672 for (i = 0; event_names[i].name != NULL; ++i)
673 if (event_names[i].event == event)
674 return (char_u *)event_names[i].name;
675 return (char_u *)"Unknown";
676}
677
678/*
679 * Scan over the events. "*" stands for all events.
680 */
681 static char_u *
682find_end_event(
683 char_u *arg,
684 int have_group) // TRUE when group name was found
685{
686 char_u *pat;
687 char_u *p;
688
689 if (*arg == '*')
690 {
691 if (arg[1] && !VIM_ISWHITE(arg[1]))
692 {
Bram Moolenaar6d057012021-12-31 18:49:43 +0000693 semsg(_(e_illegal_character_after_star_str), arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100694 return NULL;
695 }
696 pat = arg + 1;
697 }
698 else
699 {
700 for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p)
701 {
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100702 if ((int)event_name2nr(pat, &p) >= NUM_EVENTS)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100703 {
704 if (have_group)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000705 semsg(_(e_no_such_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100706 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000707 semsg(_(e_no_such_group_or_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100708 return NULL;
709 }
710 }
711 }
712 return pat;
713}
714
715/*
716 * Return TRUE if "event" is included in 'eventignore'.
717 */
718 static int
719event_ignored(event_T event)
720{
721 char_u *p = p_ei;
722
723 while (*p != NUL)
724 {
725 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
726 return TRUE;
727 if (event_name2nr(p, &p) == event)
728 return TRUE;
729 }
730
731 return FALSE;
732}
733
734/*
735 * Return OK when the contents of p_ei is valid, FAIL otherwise.
736 */
737 int
738check_ei(void)
739{
740 char_u *p = p_ei;
741
742 while (*p)
743 {
744 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
745 {
746 p += 3;
747 if (*p == ',')
748 ++p;
749 }
750 else if (event_name2nr(p, &p) == NUM_EVENTS)
751 return FAIL;
752 }
753
754 return OK;
755}
756
757# if defined(FEAT_SYN_HL) || defined(PROTO)
758
759/*
760 * Add "what" to 'eventignore' to skip loading syntax highlighting for every
761 * buffer loaded into the window. "what" must start with a comma.
762 * Returns the old value of 'eventignore' in allocated memory.
763 */
764 char_u *
765au_event_disable(char *what)
766{
767 char_u *new_ei;
768 char_u *save_ei;
769
770 save_ei = vim_strsave(p_ei);
771 if (save_ei != NULL)
772 {
Bram Moolenaardf44a272020-06-07 20:49:05 +0200773 new_ei = vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100774 if (new_ei != NULL)
775 {
776 if (*what == ',' && *p_ei == NUL)
777 STRCPY(new_ei, what + 1);
778 else
779 STRCAT(new_ei, what);
780 set_string_option_direct((char_u *)"ei", -1, new_ei,
781 OPT_FREE, SID_NONE);
782 vim_free(new_ei);
783 }
784 }
785 return save_ei;
786}
787
788 void
789au_event_restore(char_u *old_ei)
790{
791 if (old_ei != NULL)
792 {
793 set_string_option_direct((char_u *)"ei", -1, old_ei,
794 OPT_FREE, SID_NONE);
795 vim_free(old_ei);
796 }
797}
Bram Moolenaarc667da52019-11-30 20:52:27 +0100798# endif // FEAT_SYN_HL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100799
800/*
801 * do_autocmd() -- implements the :autocmd command. Can be used in the
802 * following ways:
803 *
804 * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
805 * will be automatically executed for <event>
806 * when editing a file matching <pat>, in
807 * the current group.
808 * :autocmd <event> <pat> Show the autocommands associated with
809 * <event> and <pat>.
810 * :autocmd <event> Show the autocommands associated with
811 * <event>.
812 * :autocmd Show all autocommands.
813 * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
814 * <event> and <pat>, and add the command
815 * <cmd>, for the current group.
816 * :autocmd! <event> <pat> Remove all autocommands associated with
817 * <event> and <pat> for the current group.
818 * :autocmd! <event> Remove all autocommands associated with
819 * <event> for the current group.
820 * :autocmd! Remove ALL autocommands for the current
821 * group.
822 *
823 * Multiple events and patterns may be given separated by commas. Here are
824 * some examples:
825 * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
826 * :autocmd bufleave * set tw=79 nosmartindent ic infercase
827 *
828 * :autocmd * *.c show all autocommands for *.c files.
829 *
830 * Mostly a {group} argument can optionally appear before <event>.
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200831 * "eap" can be NULL.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100832 */
833 void
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200834do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100835{
836 char_u *arg = arg_in;
837 char_u *pat;
838 char_u *envpat = NULL;
839 char_u *cmd;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200840 int cmd_need_free = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100841 event_T event;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200842 char_u *tofree = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100843 int nested = FALSE;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200844 int once = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100845 int group;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200846 int i;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200847 int flags = 0;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100848
849 if (*arg == '|')
850 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000851 eap->nextcmd = arg + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100852 arg = (char_u *)"";
853 group = AUGROUP_ALL; // no argument, use all groups
854 }
855 else
856 {
857 /*
858 * Check for a legal group name. If not, use AUGROUP_ALL.
859 */
860 group = au_get_grouparg(&arg);
861 if (arg == NULL) // out of memory
862 return;
863 }
864
865 /*
866 * Scan over the events.
867 * If we find an illegal name, return here, don't do anything.
868 */
869 pat = find_end_event(arg, group != AUGROUP_ALL);
870 if (pat == NULL)
871 return;
872
873 pat = skipwhite(pat);
874 if (*pat == '|')
875 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000876 eap->nextcmd = pat + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100877 pat = (char_u *)"";
878 cmd = (char_u *)"";
879 }
880 else
881 {
882 /*
883 * Scan over the pattern. Put a NUL at the end.
884 */
885 cmd = pat;
886 while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
887 cmd++;
888 if (*cmd)
889 *cmd++ = NUL;
890
891 // Expand environment variables in the pattern. Set 'shellslash', we
892 // want forward slashes here.
893 if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
894 {
895#ifdef BACKSLASH_IN_FILENAME
896 int p_ssl_save = p_ssl;
897
898 p_ssl = TRUE;
899#endif
900 envpat = expand_env_save(pat);
901#ifdef BACKSLASH_IN_FILENAME
902 p_ssl = p_ssl_save;
903#endif
904 if (envpat != NULL)
905 pat = envpat;
906 }
907
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100908 cmd = skipwhite(cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200909 for (i = 0; i < 2; i++)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100910 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200911 if (*cmd != NUL)
912 {
913 // Check for "++once" flag.
914 if (STRNCMP(cmd, "++once", 6) == 0 && VIM_ISWHITE(cmd[6]))
915 {
916 if (once)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000917 semsg(_(e_duplicate_argument_str), "++once");
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200918 once = TRUE;
919 cmd = skipwhite(cmd + 6);
920 }
921
922 // Check for "++nested" flag.
923 if ((STRNCMP(cmd, "++nested", 8) == 0 && VIM_ISWHITE(cmd[8])))
924 {
925 if (nested)
Bram Moolenaarf0775142022-03-04 20:10:38 +0000926 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000927 semsg(_(e_duplicate_argument_str), "++nested");
Bram Moolenaarf0775142022-03-04 20:10:38 +0000928 return;
929 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200930 nested = TRUE;
931 cmd = skipwhite(cmd + 8);
932 }
933
Bram Moolenaarf0775142022-03-04 20:10:38 +0000934 // Check for the old "nested" flag in legacy script.
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200935 if (STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
936 {
Bram Moolenaarf0775142022-03-04 20:10:38 +0000937 if (in_vim9script())
938 {
939 // If there ever is a :nested command this error should
940 // be removed and "nested" accepted as the start of the
941 // command.
942 emsg(_(e_invalid_command_nested_did_you_mean_plusplus_nested));
943 return;
944 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200945 if (nested)
Bram Moolenaarf0775142022-03-04 20:10:38 +0000946 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000947 semsg(_(e_duplicate_argument_str), "nested");
Bram Moolenaarf0775142022-03-04 20:10:38 +0000948 return;
949 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200950 nested = TRUE;
951 cmd = skipwhite(cmd + 6);
952 }
953 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100954 }
955
956 /*
957 * Find the start of the commands.
958 * Expand <sfile> in it.
959 */
960 if (*cmd != NUL)
961 {
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200962 if (eap != NULL)
963 // Read a {} block if it follows.
964 cmd = may_get_cmd_block(eap, cmd, &tofree, &flags);
965
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100966 cmd = expand_sfile(cmd);
967 if (cmd == NULL) // some error
968 return;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200969 cmd_need_free = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100970 }
971 }
972
973 /*
974 * Print header when showing autocommands.
975 */
976 if (!forceit && *cmd == NUL)
977 // Highlight title
978 msg_puts_title(_("\n--- Autocommands ---"));
979
980 /*
981 * Loop over the events.
982 */
983 last_event = (event_T)-1; // for listing the event name
984 last_group = AUGROUP_ERROR; // for listing the group name
985 if (*arg == '*' || *arg == NUL || *arg == '|')
986 {
Bram Moolenaarb6db1462021-12-24 19:24:47 +0000987 if (*cmd != NUL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +0100988 emsg(_(e_cannot_define_autocommands_for_all_events));
989 else
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100990 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar9a046fd2021-01-28 13:47:59 +0100991 event = (event_T)((int)event + 1))
992 if (do_autocmd_event(event, pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200993 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +0100994 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100995 }
996 else
997 {
998 while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
999 if (do_autocmd_event(event_name2nr(arg, &arg), pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001000 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001001 break;
1002 }
1003
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001004 if (cmd_need_free)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001005 vim_free(cmd);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001006 vim_free(tofree);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001007 vim_free(envpat);
1008}
1009
1010/*
1011 * Find the group ID in a ":autocmd" or ":doautocmd" argument.
1012 * The "argp" argument is advanced to the following argument.
1013 *
1014 * Returns the group ID, AUGROUP_ERROR for error (out of memory).
1015 */
1016 static int
1017au_get_grouparg(char_u **argp)
1018{
1019 char_u *group_name;
1020 char_u *p;
1021 char_u *arg = *argp;
1022 int group = AUGROUP_ALL;
1023
1024 for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p)
1025 ;
1026 if (p > arg)
1027 {
Bram Moolenaardf44a272020-06-07 20:49:05 +02001028 group_name = vim_strnsave(arg, p - arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001029 if (group_name == NULL) // out of memory
1030 return AUGROUP_ERROR;
1031 group = au_find_group(group_name);
1032 if (group == AUGROUP_ERROR)
1033 group = AUGROUP_ALL; // no match, use all groups
1034 else
1035 *argp = skipwhite(p); // match, skip over group name
1036 vim_free(group_name);
1037 }
1038 return group;
1039}
1040
1041/*
1042 * do_autocmd() for one event.
1043 * If *pat == NUL do for all patterns.
1044 * If *cmd == NUL show entries.
1045 * If forceit == TRUE delete entries.
1046 * If group is not AUGROUP_ALL, only use this group.
1047 */
1048 static int
1049do_autocmd_event(
1050 event_T event,
1051 char_u *pat,
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001052 int once,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001053 int nested,
1054 char_u *cmd,
1055 int forceit,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001056 int group,
1057 int flags)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001058{
1059 AutoPat *ap;
1060 AutoPat **prev_ap;
1061 AutoCmd *ac;
1062 AutoCmd **prev_ac;
1063 int brace_level;
1064 char_u *endpat;
1065 int findgroup;
1066 int allgroups;
1067 int patlen;
1068 int is_buflocal;
1069 int buflocal_nr;
Bram Moolenaarc667da52019-11-30 20:52:27 +01001070 char_u buflocal_pat[25]; // for "<buffer=X>"
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001071
1072 if (group == AUGROUP_ALL)
1073 findgroup = current_augroup;
1074 else
1075 findgroup = group;
1076 allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
1077
1078 /*
1079 * Show or delete all patterns for an event.
1080 */
1081 if (*pat == NUL)
1082 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001083 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001084 {
1085 if (forceit) // delete the AutoPat, if it's in the current group
1086 {
1087 if (ap->group == findgroup)
1088 au_remove_pat(ap);
1089 }
1090 else if (group == AUGROUP_ALL || ap->group == group)
1091 show_autocmd(ap, event);
1092 }
1093 }
1094
1095 /*
1096 * Loop through all the specified patterns.
1097 */
1098 for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
1099 {
1100 /*
1101 * Find end of the pattern.
1102 * Watch out for a comma in braces, like "*.\{obj,o\}".
1103 */
1104 brace_level = 0;
1105 for (endpat = pat; *endpat && (*endpat != ',' || brace_level
1106 || (endpat > pat && endpat[-1] == '\\')); ++endpat)
1107 {
1108 if (*endpat == '{')
1109 brace_level++;
1110 else if (*endpat == '}')
1111 brace_level--;
1112 }
1113 if (pat == endpat) // ignore single comma
1114 continue;
1115 patlen = (int)(endpat - pat);
1116
1117 /*
1118 * detect special <buflocal[=X]> buffer-local patterns
1119 */
1120 is_buflocal = FALSE;
1121 buflocal_nr = 0;
1122
1123 if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
1124 && pat[patlen - 1] == '>')
1125 {
1126 // "<buffer...>": Error will be printed only for addition.
1127 // printing and removing will proceed silently.
1128 is_buflocal = TRUE;
1129 if (patlen == 8)
1130 // "<buffer>"
1131 buflocal_nr = curbuf->b_fnum;
1132 else if (patlen > 9 && pat[7] == '=')
1133 {
1134 if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0)
1135 // "<buffer=abuf>"
1136 buflocal_nr = autocmd_bufnr;
1137 else if (skipdigits(pat + 8) == pat + patlen - 1)
1138 // "<buffer=123>"
1139 buflocal_nr = atoi((char *)pat + 8);
1140 }
1141 }
1142
1143 if (is_buflocal)
1144 {
1145 // normalize pat into standard "<buffer>#N" form
1146 sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
1147 pat = buflocal_pat; // can modify pat and patlen
1148 patlen = (int)STRLEN(buflocal_pat); // but not endpat
1149 }
1150
1151 /*
1152 * Find AutoPat entries with this pattern. When adding a command it
1153 * always goes at or after the last one, so start at the end.
1154 */
1155 if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL)
1156 prev_ap = &last_autopat[(int)event];
1157 else
1158 prev_ap = &first_autopat[(int)event];
1159 while ((ap = *prev_ap) != NULL)
1160 {
1161 if (ap->pat != NULL)
1162 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001163 /*
1164 * Accept a pattern when:
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001165 * - a group was specified and it's that group, or a group was
1166 * not specified and it's the current group, or a group was
1167 * not specified and we are listing
1168 * - the length of the pattern matches
1169 * - the pattern matches.
1170 * For <buffer[=X]>, this condition works because we normalize
1171 * all buffer-local patterns.
1172 */
1173 if ((allgroups || ap->group == findgroup)
1174 && ap->patlen == patlen
1175 && STRNCMP(pat, ap->pat, patlen) == 0)
1176 {
1177 /*
1178 * Remove existing autocommands.
1179 * If adding any new autocmd's for this AutoPat, don't
1180 * delete the pattern from the autopat list, append to
1181 * this list.
1182 */
1183 if (forceit)
1184 {
1185 if (*cmd != NUL && ap->next == NULL)
1186 {
1187 au_remove_cmds(ap);
1188 break;
1189 }
1190 au_remove_pat(ap);
1191 }
1192
1193 /*
1194 * Show autocmd's for this autopat, or buflocals <buffer=X>
1195 */
1196 else if (*cmd == NUL)
1197 show_autocmd(ap, event);
1198
1199 /*
1200 * Add autocmd to this autopat, if it's the last one.
1201 */
1202 else if (ap->next == NULL)
1203 break;
1204 }
1205 }
1206 prev_ap = &ap->next;
1207 }
1208
1209 /*
1210 * Add a new command.
1211 */
1212 if (*cmd != NUL)
1213 {
1214 /*
1215 * If the pattern we want to add a command to does appear at the
1216 * end of the list (or not is not in the list at all), add the
1217 * pattern at the end of the list.
1218 */
1219 if (ap == NULL)
1220 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001221 // refuse to add buffer-local ap if buffer number is invalid
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001222 if (is_buflocal && (buflocal_nr == 0
1223 || buflist_findnr(buflocal_nr) == NULL))
1224 {
Bram Moolenaarf1474d82021-12-31 19:59:55 +00001225 semsg(_(e_buffer_nr_invalid_buffer_number), buflocal_nr);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001226 return FAIL;
1227 }
1228
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001229 ap = ALLOC_ONE(AutoPat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001230 if (ap == NULL)
1231 return FAIL;
1232 ap->pat = vim_strnsave(pat, patlen);
1233 ap->patlen = patlen;
1234 if (ap->pat == NULL)
1235 {
1236 vim_free(ap);
1237 return FAIL;
1238 }
1239
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001240#ifdef FEAT_EVAL
1241 // need to initialize last_mode for the first ModeChanged
1242 // autocmd
1243 if (event == EVENT_MODECHANGED && !has_modechanged())
LemonBoy2bf52dd2022-04-09 18:17:34 +01001244 get_mode(last_mode);
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001245#endif
LemonBoy09371822022-04-08 15:18:45 +01001246 // Initialize the fields checked by the WinScrolled trigger to
1247 // stop it from firing right after the first autocmd is defined.
1248 if (event == EVENT_WINSCROLLED && !has_winscrolled())
1249 {
1250 curwin->w_last_topline = curwin->w_topline;
1251 curwin->w_last_leftcol = curwin->w_leftcol;
1252 curwin->w_last_width = curwin->w_width;
1253 curwin->w_last_height = curwin->w_height;
1254 }
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001255
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001256 if (is_buflocal)
1257 {
1258 ap->buflocal_nr = buflocal_nr;
1259 ap->reg_prog = NULL;
1260 }
1261 else
1262 {
1263 char_u *reg_pat;
1264
1265 ap->buflocal_nr = 0;
1266 reg_pat = file_pat_to_reg_pat(pat, endpat,
1267 &ap->allow_dirs, TRUE);
1268 if (reg_pat != NULL)
1269 ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
1270 vim_free(reg_pat);
1271 if (reg_pat == NULL || ap->reg_prog == NULL)
1272 {
1273 vim_free(ap->pat);
1274 vim_free(ap);
1275 return FAIL;
1276 }
1277 }
1278 ap->cmds = NULL;
1279 *prev_ap = ap;
1280 last_autopat[(int)event] = ap;
1281 ap->next = NULL;
1282 if (group == AUGROUP_ALL)
1283 ap->group = current_augroup;
1284 else
1285 ap->group = group;
1286 }
1287
1288 /*
1289 * Add the autocmd at the end of the AutoCmd list.
1290 */
1291 prev_ac = &(ap->cmds);
1292 while ((ac = *prev_ac) != NULL)
1293 prev_ac = &ac->next;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001294 ac = ALLOC_ONE(AutoCmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001295 if (ac == NULL)
1296 return FAIL;
1297 ac->cmd = vim_strsave(cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001298 ac->script_ctx = current_sctx;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001299 if (flags & UC_VIM9)
1300 ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001301#ifdef FEAT_EVAL
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001302 ac->script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001303#endif
1304 if (ac->cmd == NULL)
1305 {
1306 vim_free(ac);
1307 return FAIL;
1308 }
1309 ac->next = NULL;
1310 *prev_ac = ac;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001311 ac->once = once;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001312 ac->nested = nested;
1313 }
1314 }
1315
1316 au_cleanup(); // may really delete removed patterns/commands now
1317 return OK;
1318}
1319
1320/*
1321 * Implementation of ":doautocmd [group] event [fname]".
1322 * Return OK for success, FAIL for failure;
1323 */
1324 int
1325do_doautocmd(
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001326 char_u *arg_start,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001327 int do_msg, // give message for no matching autocmds?
1328 int *did_something)
1329{
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001330 char_u *arg = arg_start;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001331 char_u *fname;
1332 int nothing_done = TRUE;
1333 int group;
1334
1335 if (did_something != NULL)
1336 *did_something = FALSE;
1337
1338 /*
1339 * Check for a legal group name. If not, use AUGROUP_ALL.
1340 */
1341 group = au_get_grouparg(&arg);
1342 if (arg == NULL) // out of memory
1343 return FAIL;
1344
1345 if (*arg == '*')
1346 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001347 emsg(_(e_cant_execute_autocommands_for_all_events));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001348 return FAIL;
1349 }
1350
1351 /*
1352 * Scan over the events.
1353 * If we find an illegal name, return here, don't do anything.
1354 */
1355 fname = find_end_event(arg, group != AUGROUP_ALL);
1356 if (fname == NULL)
1357 return FAIL;
1358
1359 fname = skipwhite(fname);
1360
1361 /*
1362 * Loop over the events.
1363 */
1364 while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
1365 if (apply_autocmds_group(event_name2nr(arg, &arg),
1366 fname, NULL, TRUE, group, curbuf, NULL))
1367 nothing_done = FALSE;
1368
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001369 if (nothing_done && do_msg
1370#ifdef FEAT_EVAL
1371 && !aborting()
1372#endif
1373 )
1374 smsg(_("No matching autocommands: %s"), arg_start);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001375 if (did_something != NULL)
1376 *did_something = !nothing_done;
1377
1378#ifdef FEAT_EVAL
1379 return aborting() ? FAIL : OK;
1380#else
1381 return OK;
1382#endif
1383}
1384
1385/*
1386 * ":doautoall": execute autocommands for each loaded buffer.
1387 */
1388 void
1389ex_doautoall(exarg_T *eap)
1390{
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001391 int retval = OK;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001392 aco_save_T aco;
1393 buf_T *buf;
1394 bufref_T bufref;
1395 char_u *arg = eap->arg;
1396 int call_do_modelines = check_nomodeline(&arg);
1397 int did_aucmd;
1398
1399 /*
1400 * This is a bit tricky: For some commands curwin->w_buffer needs to be
1401 * equal to curbuf, but for some buffers there may not be a window.
1402 * So we change the buffer for the current window for a moment. This
1403 * gives problems when the autocommands make changes to the list of
1404 * buffers or windows...
1405 */
1406 FOR_ALL_BUFFERS(buf)
1407 {
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001408 // Only do loaded buffers and skip the current buffer, it's done last.
1409 if (buf->b_ml.ml_mfp != NULL && buf != curbuf)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001410 {
1411 // find a window for this buffer and save some values
1412 aucmd_prepbuf(&aco, buf);
1413 set_bufref(&bufref, buf);
1414
1415 // execute the autocommands for this buffer
1416 retval = do_doautocmd(arg, FALSE, &did_aucmd);
1417
1418 if (call_do_modelines && did_aucmd)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001419 // Execute the modeline settings, but don't set window-local
1420 // options if we are using the current window for another
1421 // buffer.
1422 do_modelines(curwin == aucmd_win ? OPT_NOWIN : 0);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001423
1424 // restore the current window
1425 aucmd_restbuf(&aco);
1426
1427 // stop if there is some error or buffer was deleted
1428 if (retval == FAIL || !bufref_valid(&bufref))
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001429 {
1430 retval = FAIL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001431 break;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001432 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001433 }
1434 }
1435
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001436 // Execute autocommands for the current buffer last.
1437 if (retval == OK)
1438 {
1439 do_doautocmd(arg, FALSE, &did_aucmd);
1440 if (call_do_modelines && did_aucmd)
1441 do_modelines(0);
1442 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001443}
1444
1445/*
1446 * Check *argp for <nomodeline>. When it is present return FALSE, otherwise
1447 * return TRUE and advance *argp to after it.
1448 * Thus return TRUE when do_modelines() should be called.
1449 */
1450 int
1451check_nomodeline(char_u **argp)
1452{
1453 if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
1454 {
1455 *argp = skipwhite(*argp + 12);
1456 return FALSE;
1457 }
1458 return TRUE;
1459}
1460
1461/*
1462 * Prepare for executing autocommands for (hidden) buffer "buf".
1463 * Search for a visible window containing the current buffer. If there isn't
1464 * one then use "aucmd_win".
1465 * Set "curbuf" and "curwin" to match "buf".
1466 */
1467 void
1468aucmd_prepbuf(
1469 aco_save_T *aco, // structure to save values in
1470 buf_T *buf) // new curbuf
1471{
1472 win_T *win;
1473 int save_ea;
1474#ifdef FEAT_AUTOCHDIR
1475 int save_acd;
1476#endif
1477
1478 // Find a window that is for the new buffer
1479 if (buf == curbuf) // be quick when buf is curbuf
1480 win = curwin;
1481 else
1482 FOR_ALL_WINDOWS(win)
1483 if (win->w_buffer == buf)
1484 break;
1485
1486 // Allocate "aucmd_win" when needed. If this fails (out of memory) fall
1487 // back to using the current window.
1488 if (win == NULL && aucmd_win == NULL)
1489 {
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001490 aucmd_win = win_alloc_popup_win();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001491 if (aucmd_win == NULL)
1492 win = curwin;
1493 }
1494 if (win == NULL && aucmd_win_used)
1495 // Strange recursive autocommand, fall back to using the current
1496 // window. Expect a few side effects...
1497 win = curwin;
1498
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001499 aco->save_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001500 aco->save_curbuf = curbuf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001501 aco->save_prevwin_id = prevwin == NULL ? 0 : prevwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001502 if (win != NULL)
1503 {
1504 // There is a window for "buf" in the current tab page, make it the
1505 // curwin. This is preferred, it has the least side effects (esp. if
1506 // "buf" is curbuf).
1507 aco->use_aucmd_win = FALSE;
1508 curwin = win;
1509 }
1510 else
1511 {
1512 // There is no window for "buf", use "aucmd_win". To minimize the side
1513 // effects, insert it in the current tab page.
1514 // Anything related to a window (e.g., setting folds) may have
1515 // unexpected results.
1516 aco->use_aucmd_win = TRUE;
1517 aucmd_win_used = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001518
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001519 win_init_popup_win(aucmd_win, buf);
1520
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001521 aco->globaldir = globaldir;
1522 globaldir = NULL;
1523
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001524 // Split the current window, put the aucmd_win in the upper half.
1525 // We don't want the BufEnter or WinEnter autocommands.
1526 block_autocmds();
1527 make_snapshot(SNAP_AUCMD_IDX);
1528 save_ea = p_ea;
1529 p_ea = FALSE;
1530
1531#ifdef FEAT_AUTOCHDIR
1532 // Prevent chdir() call in win_enter_ext(), through do_autochdir().
1533 save_acd = p_acd;
1534 p_acd = FALSE;
1535#endif
1536
Bram Moolenaardff97e62022-01-24 20:00:55 +00001537 // no redrawing and don't set the window title
1538 ++RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001539 (void)win_split_ins(0, WSP_TOP, aucmd_win, 0);
Bram Moolenaardff97e62022-01-24 20:00:55 +00001540 --RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001541 (void)win_comp_pos(); // recompute window positions
1542 p_ea = save_ea;
1543#ifdef FEAT_AUTOCHDIR
1544 p_acd = save_acd;
1545#endif
1546 unblock_autocmds();
1547 curwin = aucmd_win;
1548 }
1549 curbuf = buf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001550 aco->new_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001551 set_bufref(&aco->new_curbuf, curbuf);
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001552
1553 // disable the Visual area, the position may be invalid in another buffer
1554 aco->save_VIsual_active = VIsual_active;
1555 VIsual_active = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001556}
1557
1558/*
1559 * Cleanup after executing autocommands for a (hidden) buffer.
1560 * Restore the window as it was (if possible).
1561 */
1562 void
1563aucmd_restbuf(
1564 aco_save_T *aco) // structure holding saved values
1565{
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001566 int dummy;
1567 win_T *save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001568
1569 if (aco->use_aucmd_win)
1570 {
1571 --curbuf->b_nwindows;
1572 // Find "aucmd_win", it can't be closed, but it may be in another tab
1573 // page. Do not trigger autocommands here.
1574 block_autocmds();
1575 if (curwin != aucmd_win)
1576 {
1577 tabpage_T *tp;
1578 win_T *wp;
1579
1580 FOR_ALL_TAB_WINDOWS(tp, wp)
1581 {
1582 if (wp == aucmd_win)
1583 {
1584 if (tp != curtab)
1585 goto_tabpage_tp(tp, TRUE, TRUE);
1586 win_goto(aucmd_win);
1587 goto win_found;
1588 }
1589 }
1590 }
1591win_found:
1592
1593 // Remove the window and frame from the tree of frames.
1594 (void)winframe_remove(curwin, &dummy, NULL);
1595 win_remove(curwin, NULL);
1596 aucmd_win_used = FALSE;
1597 last_status(FALSE); // may need to remove last status line
1598
1599 if (!valid_tabpage_win(curtab))
1600 // no valid window in current tabpage
1601 close_tabpage(curtab);
1602
1603 restore_snapshot(SNAP_AUCMD_IDX, FALSE);
1604 (void)win_comp_pos(); // recompute window positions
1605 unblock_autocmds();
1606
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001607 save_curwin = win_find_by_id(aco->save_curwin_id);
1608 if (save_curwin != NULL)
1609 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001610 else
1611 // Hmm, original window disappeared. Just use the first one.
1612 curwin = firstwin;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001613 curbuf = curwin->w_buffer;
1614#ifdef FEAT_JOB_CHANNEL
1615 // May need to restore insert mode for a prompt buffer.
1616 entering_window(curwin);
1617#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001618 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001619#ifdef FEAT_EVAL
1620 vars_clear(&aucmd_win->w_vars->dv_hashtab); // free all w: variables
1621 hash_init(&aucmd_win->w_vars->dv_hashtab); // re-use the hashtab
1622#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001623 vim_free(globaldir);
1624 globaldir = aco->globaldir;
1625
1626 // the buffer contents may have changed
1627 check_cursor();
1628 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1629 {
1630 curwin->w_topline = curbuf->b_ml.ml_line_count;
1631#ifdef FEAT_DIFF
1632 curwin->w_topfill = 0;
1633#endif
1634 }
1635#if defined(FEAT_GUI)
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001636 if (gui.in_use)
1637 {
1638 // Hide the scrollbars from the aucmd_win and update.
1639 gui_mch_enable_scrollbar(
1640 &aucmd_win->w_scrollbars[SBAR_LEFT], FALSE);
1641 gui_mch_enable_scrollbar(
1642 &aucmd_win->w_scrollbars[SBAR_RIGHT], FALSE);
1643 gui_may_update_scrollbars();
1644 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001645#endif
1646 }
1647 else
1648 {
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001649 // Restore curwin. Use the window ID, a window may have been closed
1650 // and the memory re-used for another one.
1651 save_curwin = win_find_by_id(aco->save_curwin_id);
1652 if (save_curwin != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001653 {
1654 // Restore the buffer which was previously edited by curwin, if
1655 // it was changed, we are still the same window and the buffer is
1656 // valid.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001657 if (curwin->w_id == aco->new_curwin_id
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001658 && curbuf != aco->new_curbuf.br_buf
1659 && bufref_valid(&aco->new_curbuf)
1660 && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
1661 {
1662# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
1663 if (curwin->w_s == &curbuf->b_s)
1664 curwin->w_s = &aco->new_curbuf.br_buf->b_s;
1665# endif
1666 --curbuf->b_nwindows;
1667 curbuf = aco->new_curbuf.br_buf;
1668 curwin->w_buffer = curbuf;
1669 ++curbuf->b_nwindows;
1670 }
1671
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001672 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001673 curbuf = curwin->w_buffer;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001674 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001675 // In case the autocommand moves the cursor to a position that
1676 // does not exist in curbuf.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001677 check_cursor();
1678 }
1679 }
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001680
1681 check_cursor(); // just in case lines got deleted
1682 VIsual_active = aco->save_VIsual_active;
1683 if (VIsual_active)
1684 check_pos(curbuf, &VIsual);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001685}
1686
1687static int autocmd_nested = FALSE;
1688
1689/*
1690 * Execute autocommands for "event" and file name "fname".
1691 * Return TRUE if some commands were executed.
1692 */
1693 int
1694apply_autocmds(
1695 event_T event,
1696 char_u *fname, // NULL or empty means use actual file name
1697 char_u *fname_io, // fname to use for <afile> on cmdline
1698 int force, // when TRUE, ignore autocmd_busy
1699 buf_T *buf) // buffer for <abuf>
1700{
1701 return apply_autocmds_group(event, fname, fname_io, force,
1702 AUGROUP_ALL, buf, NULL);
1703}
1704
1705/*
1706 * Like apply_autocmds(), but with extra "eap" argument. This takes care of
1707 * setting v:filearg.
1708 */
1709 int
1710apply_autocmds_exarg(
1711 event_T event,
1712 char_u *fname,
1713 char_u *fname_io,
1714 int force,
1715 buf_T *buf,
1716 exarg_T *eap)
1717{
1718 return apply_autocmds_group(event, fname, fname_io, force,
1719 AUGROUP_ALL, buf, eap);
1720}
1721
1722/*
1723 * Like apply_autocmds(), but handles the caller's retval. If the script
1724 * processing is being aborted or if retval is FAIL when inside a try
1725 * conditional, no autocommands are executed. If otherwise the autocommands
1726 * cause the script to be aborted, retval is set to FAIL.
1727 */
1728 int
1729apply_autocmds_retval(
1730 event_T event,
1731 char_u *fname, // NULL or empty means use actual file name
1732 char_u *fname_io, // fname to use for <afile> on cmdline
1733 int force, // when TRUE, ignore autocmd_busy
1734 buf_T *buf, // buffer for <abuf>
1735 int *retval) // pointer to caller's retval
1736{
1737 int did_cmd;
1738
1739#ifdef FEAT_EVAL
1740 if (should_abort(*retval))
1741 return FALSE;
1742#endif
1743
1744 did_cmd = apply_autocmds_group(event, fname, fname_io, force,
1745 AUGROUP_ALL, buf, NULL);
1746 if (did_cmd
1747#ifdef FEAT_EVAL
1748 && aborting()
1749#endif
1750 )
1751 *retval = FAIL;
1752 return did_cmd;
1753}
1754
1755/*
1756 * Return TRUE when there is a CursorHold autocommand defined.
1757 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001758 static int
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001759has_cursorhold(void)
1760{
Bram Moolenaar24959102022-05-07 20:01:16 +01001761 return (first_autopat[(int)(get_real_state() == MODE_NORMAL_BUSY
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001762 ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
1763}
1764
1765/*
1766 * Return TRUE if the CursorHold event can be triggered.
1767 */
1768 int
1769trigger_cursorhold(void)
1770{
1771 int state;
1772
1773 if (!did_cursorhold
1774 && has_cursorhold()
1775 && reg_recording == 0
1776 && typebuf.tb_len == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001777 && !ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001778 {
1779 state = get_real_state();
Bram Moolenaar24959102022-05-07 20:01:16 +01001780 if (state == MODE_NORMAL_BUSY || (state & MODE_INSERT) != 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001781 return TRUE;
1782 }
1783 return FALSE;
1784}
1785
1786/*
LemonBoy09371822022-04-08 15:18:45 +01001787 * Return TRUE when there is a WinScrolled autocommand defined.
1788 */
1789 int
1790has_winscrolled(void)
1791{
1792 return (first_autopat[(int)EVENT_WINSCROLLED] != NULL);
1793}
1794
1795/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001796 * Return TRUE when there is a CursorMoved autocommand defined.
1797 */
1798 int
1799has_cursormoved(void)
1800{
1801 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1802}
1803
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001804/*
1805 * Return TRUE when there is a CursorMovedI autocommand defined.
1806 */
1807 int
1808has_cursormovedI(void)
1809{
1810 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1811}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001812
1813/*
1814 * Return TRUE when there is a TextChanged autocommand defined.
1815 */
1816 int
1817has_textchanged(void)
1818{
1819 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1820}
1821
1822/*
1823 * Return TRUE when there is a TextChangedI autocommand defined.
1824 */
1825 int
1826has_textchangedI(void)
1827{
1828 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
1829}
1830
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001831/*
1832 * Return TRUE when there is a TextChangedP autocommand defined.
1833 */
1834 int
1835has_textchangedP(void)
1836{
1837 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
1838}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001839
1840/*
1841 * Return TRUE when there is an InsertCharPre autocommand defined.
1842 */
1843 int
1844has_insertcharpre(void)
1845{
1846 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
1847}
1848
1849/*
1850 * Return TRUE when there is an CmdUndefined autocommand defined.
1851 */
1852 int
1853has_cmdundefined(void)
1854{
1855 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
1856}
1857
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001858#if defined(FEAT_EVAL) || defined(PROTO)
1859/*
1860 * Return TRUE when there is a TextYankPost autocommand defined.
1861 */
1862 int
1863has_textyankpost(void)
1864{
1865 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
1866}
1867#endif
1868
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001869#if defined(FEAT_EVAL) || defined(PROTO)
1870/*
1871 * Return TRUE when there is a CompleteChanged autocommand defined.
1872 */
1873 int
1874has_completechanged(void)
1875{
1876 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
1877}
1878#endif
1879
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02001880#if defined(FEAT_EVAL) || defined(PROTO)
1881/*
1882 * Return TRUE when there is a ModeChanged autocommand defined.
1883 */
1884 int
1885has_modechanged(void)
1886{
1887 return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
1888}
1889#endif
1890
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001891/*
1892 * Execute autocommands for "event" and file name "fname".
1893 * Return TRUE if some commands were executed.
1894 */
1895 static int
1896apply_autocmds_group(
1897 event_T event,
1898 char_u *fname, // NULL or empty means use actual file name
1899 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
1900 // use fname
1901 int force, // when TRUE, ignore autocmd_busy
1902 int group, // group ID, or AUGROUP_ALL
1903 buf_T *buf, // buffer for <abuf>
1904 exarg_T *eap UNUSED) // command arguments
1905{
1906 char_u *sfname = NULL; // short file name
1907 char_u *tail;
1908 int save_changed;
1909 buf_T *old_curbuf;
1910 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001911 char_u *save_autocmd_fname;
1912 int save_autocmd_fname_full;
1913 int save_autocmd_bufnr;
1914 char_u *save_autocmd_match;
1915 int save_autocmd_busy;
1916 int save_autocmd_nested;
1917 static int nesting = 0;
LemonBoyeca7c602022-04-14 15:39:43 +01001918 AutoPatCmd_T patcmd;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001919 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001920 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001921#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001922 funccal_entry_T funccal_entry;
1923 char_u *save_cmdarg;
1924 long save_cmdbang;
1925#endif
1926 static int filechangeshell_busy = FALSE;
1927#ifdef FEAT_PROFILE
1928 proftime_T wait_time;
1929#endif
1930 int did_save_redobuff = FALSE;
1931 save_redo_T save_redo;
1932 int save_KeyTyped = KeyTyped;
ichizokc3f91c02021-12-17 09:44:33 +00001933 int save_did_emsg;
Bram Moolenaare31ee862020-01-07 20:59:34 +01001934 ESTACK_CHECK_DECLARATION
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001935
1936 /*
1937 * Quickly return if there are no autocommands for this event or
1938 * autocommands are blocked.
1939 */
1940 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
1941 || autocmd_blocked > 0)
1942 goto BYPASS_AU;
1943
1944 /*
1945 * When autocommands are busy, new autocommands are only executed when
1946 * explicitly enabled with the "nested" flag.
1947 */
1948 if (autocmd_busy && !(force || autocmd_nested))
1949 goto BYPASS_AU;
1950
1951#ifdef FEAT_EVAL
1952 /*
1953 * Quickly return when immediately aborting on error, or when an interrupt
1954 * occurred or an exception was thrown but not caught.
1955 */
1956 if (aborting())
1957 goto BYPASS_AU;
1958#endif
1959
1960 /*
1961 * FileChangedShell never nests, because it can create an endless loop.
1962 */
1963 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
1964 || event == EVENT_FILECHANGEDSHELLPOST))
1965 goto BYPASS_AU;
1966
1967 /*
1968 * Ignore events in 'eventignore'.
1969 */
1970 if (event_ignored(event))
1971 goto BYPASS_AU;
1972
1973 /*
1974 * Allow nesting of autocommands, but restrict the depth, because it's
1975 * possible to create an endless loop.
1976 */
1977 if (nesting == 10)
1978 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001979 emsg(_(e_autocommand_nesting_too_deep));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001980 goto BYPASS_AU;
1981 }
1982
1983 /*
1984 * Check if these autocommands are disabled. Used when doing ":all" or
1985 * ":ball".
1986 */
1987 if ( (autocmd_no_enter
1988 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
1989 || (autocmd_no_leave
1990 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
1991 goto BYPASS_AU;
1992
1993 /*
1994 * Save the autocmd_* variables and info about the current buffer.
1995 */
1996 save_autocmd_fname = autocmd_fname;
1997 save_autocmd_fname_full = autocmd_fname_full;
1998 save_autocmd_bufnr = autocmd_bufnr;
1999 save_autocmd_match = autocmd_match;
2000 save_autocmd_busy = autocmd_busy;
2001 save_autocmd_nested = autocmd_nested;
2002 save_changed = curbuf->b_changed;
2003 old_curbuf = curbuf;
2004
2005 /*
2006 * Set the file name to be used for <afile>.
2007 * Make a copy to avoid that changing a buffer name or directory makes it
2008 * invalid.
2009 */
2010 if (fname_io == NULL)
2011 {
2012 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002013 || event == EVENT_OPTIONSET
2014 || event == EVENT_MODECHANGED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002015 autocmd_fname = NULL;
2016 else if (fname != NULL && !ends_excmd(*fname))
2017 autocmd_fname = fname;
2018 else if (buf != NULL)
2019 autocmd_fname = buf->b_ffname;
2020 else
2021 autocmd_fname = NULL;
2022 }
2023 else
2024 autocmd_fname = fname_io;
2025 if (autocmd_fname != NULL)
2026 autocmd_fname = vim_strsave(autocmd_fname);
2027 autocmd_fname_full = FALSE; // call FullName_save() later
2028
2029 /*
2030 * Set the buffer number to be used for <abuf>.
2031 */
2032 if (buf == NULL)
2033 autocmd_bufnr = 0;
2034 else
2035 autocmd_bufnr = buf->b_fnum;
2036
2037 /*
2038 * When the file name is NULL or empty, use the file name of buffer "buf".
2039 * Always use the full path of the file name to match with, in case
2040 * "allow_dirs" is set.
2041 */
2042 if (fname == NULL || *fname == NUL)
2043 {
2044 if (buf == NULL)
2045 fname = NULL;
2046 else
2047 {
2048#ifdef FEAT_SYN_HL
2049 if (event == EVENT_SYNTAX)
2050 fname = buf->b_p_syn;
2051 else
2052#endif
2053 if (event == EVENT_FILETYPE)
2054 fname = buf->b_p_ft;
2055 else
2056 {
2057 if (buf->b_sfname != NULL)
2058 sfname = vim_strsave(buf->b_sfname);
2059 fname = buf->b_ffname;
2060 }
2061 }
2062 if (fname == NULL)
2063 fname = (char_u *)"";
2064 fname = vim_strsave(fname); // make a copy, so we can change it
2065 }
2066 else
2067 {
2068 sfname = vim_strsave(fname);
2069 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002070 // ColorScheme, QuickFixCmd*, DirChanged and similar.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002071 if (event == EVENT_FILETYPE
2072 || event == EVENT_SYNTAX
2073 || event == EVENT_CMDLINECHANGED
2074 || event == EVENT_CMDLINEENTER
2075 || event == EVENT_CMDLINELEAVE
2076 || event == EVENT_CMDWINENTER
2077 || event == EVENT_CMDWINLEAVE
2078 || event == EVENT_CMDUNDEFINED
2079 || event == EVENT_FUNCUNDEFINED
2080 || event == EVENT_REMOTEREPLY
2081 || event == EVENT_SPELLFILEMISSING
2082 || event == EVENT_QUICKFIXCMDPRE
2083 || event == EVENT_COLORSCHEME
2084 || event == EVENT_COLORSCHEMEPRE
2085 || event == EVENT_OPTIONSET
2086 || event == EVENT_QUICKFIXCMDPOST
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002087 || event == EVENT_DIRCHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002088 || event == EVENT_DIRCHANGEDPRE
naohiro ono23beefe2021-11-13 12:38:49 +00002089 || event == EVENT_MODECHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002090 || event == EVENT_USER
LemonBoy09371822022-04-08 15:18:45 +01002091 || event == EVENT_WINCLOSED
2092 || event == EVENT_WINSCROLLED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002093 {
2094 fname = vim_strsave(fname);
2095 autocmd_fname_full = TRUE; // don't expand it later
2096 }
2097 else
2098 fname = FullName_save(fname, FALSE);
2099 }
2100 if (fname == NULL) // out of memory
2101 {
2102 vim_free(sfname);
2103 retval = FALSE;
2104 goto BYPASS_AU;
2105 }
2106
2107#ifdef BACKSLASH_IN_FILENAME
2108 /*
2109 * Replace all backslashes with forward slashes. This makes the
2110 * autocommand patterns portable between Unix and MS-DOS.
2111 */
2112 if (sfname != NULL)
2113 forward_slash(sfname);
2114 forward_slash(fname);
2115#endif
2116
2117#ifdef VMS
2118 // remove version for correct match
2119 if (sfname != NULL)
2120 vms_remove_version(sfname);
2121 vms_remove_version(fname);
2122#endif
2123
2124 /*
2125 * Set the name to be used for <amatch>.
2126 */
2127 autocmd_match = fname;
2128
2129
2130 // Don't redraw while doing autocommands.
2131 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002132
2133 // name and lnum are filled in later
2134 estack_push(ETYPE_AUCMD, NULL, 0);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002135 ESTACK_CHECK_SETUP
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002136
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002137 save_current_sctx = current_sctx;
2138
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002139#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002140# ifdef FEAT_PROFILE
2141 if (do_profiling == PROF_YES)
2142 prof_child_enter(&wait_time); // doesn't count for the caller itself
2143# endif
2144
2145 // Don't use local function variables, if called from a function.
2146 save_funccal(&funccal_entry);
2147#endif
2148
2149 /*
2150 * When starting to execute autocommands, save the search patterns.
2151 */
2152 if (!autocmd_busy)
2153 {
2154 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002155 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002156 {
2157 saveRedobuff(&save_redo);
2158 did_save_redobuff = TRUE;
2159 }
2160 did_filetype = keep_filetype;
2161 }
2162
2163 /*
2164 * Note that we are applying autocmds. Some commands need to know.
2165 */
2166 autocmd_busy = TRUE;
2167 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2168 ++nesting; // see matching decrement below
2169
2170 // Remember that FileType was triggered. Used for did_filetype().
2171 if (event == EVENT_FILETYPE)
2172 did_filetype = TRUE;
2173
2174 tail = gettail(fname);
2175
2176 // Find first autocommand that matches
LemonBoyeca7c602022-04-14 15:39:43 +01002177 CLEAR_FIELD(patcmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002178 patcmd.curpat = first_autopat[(int)event];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002179 patcmd.group = group;
2180 patcmd.fname = fname;
2181 patcmd.sfname = sfname;
2182 patcmd.tail = tail;
2183 patcmd.event = event;
2184 patcmd.arg_bufnr = autocmd_bufnr;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002185 auto_next_pat(&patcmd, FALSE);
2186
2187 // found one, start executing the autocommands
2188 if (patcmd.curpat != NULL)
2189 {
2190 // add to active_apc_list
2191 patcmd.next = active_apc_list;
2192 active_apc_list = &patcmd;
2193
2194#ifdef FEAT_EVAL
2195 // set v:cmdarg (only when there is a matching pattern)
2196 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2197 if (eap != NULL)
2198 {
2199 save_cmdarg = set_cmdarg(eap, NULL);
2200 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2201 }
2202 else
2203 save_cmdarg = NULL; // avoid gcc warning
2204#endif
2205 retval = TRUE;
2206 // mark the last pattern, to avoid an endless loop when more patterns
2207 // are added when executing autocommands
2208 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2209 ap->last = FALSE;
2210 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002211
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002212 // Make sure cursor and topline are valid. The first time the current
2213 // values are saved, restored by reset_lnums(). When nested only the
2214 // values are corrected when needed.
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002215 if (nesting == 1)
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002216 check_lnums(TRUE);
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002217 else
2218 check_lnums_nested(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002219
ichizokc3f91c02021-12-17 09:44:33 +00002220 save_did_emsg = did_emsg;
2221
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002222 do_cmdline(NULL, getnextac, (void *)&patcmd,
2223 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002224
ichizokc3f91c02021-12-17 09:44:33 +00002225 did_emsg += save_did_emsg;
2226
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002227 if (nesting == 1)
2228 // restore cursor and topline, unless they were changed
2229 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002230
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002231#ifdef FEAT_EVAL
2232 if (eap != NULL)
2233 {
2234 (void)set_cmdarg(NULL, save_cmdarg);
2235 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2236 }
2237#endif
2238 // delete from active_apc_list
2239 if (active_apc_list == &patcmd) // just in case
2240 active_apc_list = patcmd.next;
2241 }
2242
2243 --RedrawingDisabled;
2244 autocmd_busy = save_autocmd_busy;
2245 filechangeshell_busy = FALSE;
2246 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002247 vim_free(SOURCING_NAME);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002248 ESTACK_CHECK_NOW
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002249 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002250 vim_free(autocmd_fname);
2251 autocmd_fname = save_autocmd_fname;
2252 autocmd_fname_full = save_autocmd_fname_full;
2253 autocmd_bufnr = save_autocmd_bufnr;
2254 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002255 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002256#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002257 restore_funccal();
2258# ifdef FEAT_PROFILE
2259 if (do_profiling == PROF_YES)
2260 prof_child_exit(&wait_time);
2261# endif
2262#endif
2263 KeyTyped = save_KeyTyped;
2264 vim_free(fname);
2265 vim_free(sfname);
2266 --nesting; // see matching increment above
2267
2268 /*
2269 * When stopping to execute autocommands, restore the search patterns and
2270 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2271 * free any windows in the au_pending_free_win list.
2272 */
2273 if (!autocmd_busy)
2274 {
2275 restore_search_patterns();
2276 if (did_save_redobuff)
2277 restoreRedobuff(&save_redo);
2278 did_filetype = FALSE;
2279 while (au_pending_free_buf != NULL)
2280 {
2281 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002282
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002283 vim_free(au_pending_free_buf);
2284 au_pending_free_buf = b;
2285 }
2286 while (au_pending_free_win != NULL)
2287 {
2288 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002289
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002290 vim_free(au_pending_free_win);
2291 au_pending_free_win = w;
2292 }
2293 }
2294
2295 /*
2296 * Some events don't set or reset the Changed flag.
2297 * Check if still in the same buffer!
2298 */
2299 if (curbuf == old_curbuf
2300 && (event == EVENT_BUFREADPOST
2301 || event == EVENT_BUFWRITEPOST
2302 || event == EVENT_FILEAPPENDPOST
2303 || event == EVENT_VIMLEAVE
2304 || event == EVENT_VIMLEAVEPRE))
2305 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002306 if (curbuf->b_changed != save_changed)
2307 need_maketitle = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002308 curbuf->b_changed = save_changed;
2309 }
2310
2311 au_cleanup(); // may really delete removed patterns/commands now
2312
2313BYPASS_AU:
2314 // When wiping out a buffer make sure all its buffer-local autocommands
2315 // are deleted.
2316 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2317 aubuflocal_remove(buf);
2318
2319 if (retval == OK && event == EVENT_FILETYPE)
2320 au_did_filetype = TRUE;
2321
2322 return retval;
2323}
2324
2325# ifdef FEAT_EVAL
2326static char_u *old_termresponse = NULL;
2327# endif
2328
2329/*
2330 * Block triggering autocommands until unblock_autocmd() is called.
2331 * Can be used recursively, so long as it's symmetric.
2332 */
2333 void
2334block_autocmds(void)
2335{
2336# ifdef FEAT_EVAL
2337 // Remember the value of v:termresponse.
2338 if (autocmd_blocked == 0)
2339 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
2340# endif
2341 ++autocmd_blocked;
2342}
2343
2344 void
2345unblock_autocmds(void)
2346{
2347 --autocmd_blocked;
2348
2349# ifdef FEAT_EVAL
2350 // When v:termresponse was set while autocommands were blocked, trigger
2351 // the autocommands now. Esp. useful when executing a shell command
2352 // during startup (vimdiff).
2353 if (autocmd_blocked == 0
2354 && get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2355 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2356# endif
2357}
2358
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002359 int
2360is_autocmd_blocked(void)
2361{
2362 return autocmd_blocked != 0;
2363}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002364
2365/*
2366 * Find next autocommand pattern that matches.
2367 */
2368 static void
2369auto_next_pat(
LemonBoyeca7c602022-04-14 15:39:43 +01002370 AutoPatCmd_T *apc,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002371 int stop_at_last) // stop when 'last' flag is set
2372{
2373 AutoPat *ap;
2374 AutoCmd *cp;
2375 char_u *name;
2376 char *s;
LemonBoyeca7c602022-04-14 15:39:43 +01002377 estack_T *entry;
2378 char_u *namep;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002379
LemonBoyeca7c602022-04-14 15:39:43 +01002380 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
2381
2382 // Clear the exestack entry for this ETYPE_AUCMD entry.
2383 VIM_CLEAR(entry->es_name);
2384 entry->es_info.aucmd = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002385
2386 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2387 {
2388 apc->curpat = NULL;
2389
2390 // Only use a pattern when it has not been removed, has commands and
2391 // the group matches. For buffer-local autocommands only check the
2392 // buffer number.
2393 if (ap->pat != NULL && ap->cmds != NULL
2394 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2395 {
2396 // execution-condition
2397 if (ap->buflocal_nr == 0
2398 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2399 apc->sfname, apc->tail, ap->allow_dirs))
2400 : ap->buflocal_nr == apc->arg_bufnr)
2401 {
2402 name = event_nr2name(apc->event);
2403 s = _("%s Autocommands for \"%s\"");
LemonBoyeca7c602022-04-14 15:39:43 +01002404 namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1);
2405 if (namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002406 {
LemonBoyeca7c602022-04-14 15:39:43 +01002407 sprintf((char *)namep, s, (char *)name, (char *)ap->pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002408 if (p_verbose >= 8)
2409 {
2410 verbose_enter();
LemonBoyeca7c602022-04-14 15:39:43 +01002411 smsg(_("Executing %s"), namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002412 verbose_leave();
2413 }
2414 }
2415
LemonBoyeca7c602022-04-14 15:39:43 +01002416 // Update the exestack entry for this autocmd.
2417 entry->es_name = namep;
2418 entry->es_info.aucmd = apc;
2419
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002420 apc->curpat = ap;
2421 apc->nextcmd = ap->cmds;
2422 // mark last command
2423 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2424 cp->last = FALSE;
2425 cp->last = TRUE;
2426 }
2427 line_breakcheck();
2428 if (apc->curpat != NULL) // found a match
2429 break;
2430 }
2431 if (stop_at_last && ap->last)
2432 break;
2433 }
2434}
2435
2436/*
LemonBoyeca7c602022-04-14 15:39:43 +01002437 * Get the script context where autocommand "acp" is defined.
2438 */
2439 sctx_T *
2440acp_script_ctx(AutoPatCmd_T *acp)
2441{
2442 return &acp->script_ctx;
2443}
2444
2445/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002446 * Get next autocommand command.
2447 * Called by do_cmdline() to get the next line for ":if".
2448 * Returns allocated string, or NULL for end of autocommands.
2449 */
2450 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002451getnextac(
2452 int c UNUSED,
2453 void *cookie,
2454 int indent UNUSED,
2455 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002456{
LemonBoyeca7c602022-04-14 15:39:43 +01002457 AutoPatCmd_T *acp = (AutoPatCmd_T *)cookie;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002458 char_u *retval;
2459 AutoCmd *ac;
2460
2461 // Can be called again after returning the last line.
2462 if (acp->curpat == NULL)
2463 return NULL;
2464
2465 // repeat until we find an autocommand to execute
2466 for (;;)
2467 {
2468 // skip removed commands
2469 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2470 if (acp->nextcmd->last)
2471 acp->nextcmd = NULL;
2472 else
2473 acp->nextcmd = acp->nextcmd->next;
2474
2475 if (acp->nextcmd != NULL)
2476 break;
2477
2478 // at end of commands, find next pattern that matches
2479 if (acp->curpat->last)
2480 acp->curpat = NULL;
2481 else
2482 acp->curpat = acp->curpat->next;
2483 if (acp->curpat != NULL)
2484 auto_next_pat(acp, TRUE);
2485 if (acp->curpat == NULL)
2486 return NULL;
2487 }
2488
2489 ac = acp->nextcmd;
2490
2491 if (p_verbose >= 9)
2492 {
2493 verbose_enter_scroll();
2494 smsg(_("autocommand %s"), ac->cmd);
2495 msg_puts("\n"); // don't overwrite this either
2496 verbose_leave_scroll();
2497 }
2498 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002499 // Remove one-shot ("once") autocmd in anticipation of its execution.
2500 if (ac->once)
2501 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002502 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002503 current_sctx = ac->script_ctx;
LemonBoyeca7c602022-04-14 15:39:43 +01002504 acp->script_ctx = current_sctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002505 if (ac->last)
2506 acp->nextcmd = NULL;
2507 else
2508 acp->nextcmd = ac->next;
2509 return retval;
2510}
2511
2512/*
2513 * Return TRUE if there is a matching autocommand for "fname".
2514 * To account for buffer-local autocommands, function needs to know
2515 * in which buffer the file will be opened.
2516 */
2517 int
2518has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2519{
2520 AutoPat *ap;
2521 char_u *fname;
2522 char_u *tail = gettail(sfname);
2523 int retval = FALSE;
2524
2525 fname = FullName_save(sfname, FALSE);
2526 if (fname == NULL)
2527 return FALSE;
2528
2529#ifdef BACKSLASH_IN_FILENAME
2530 /*
2531 * Replace all backslashes with forward slashes. This makes the
2532 * autocommand patterns portable between Unix and MS-DOS.
2533 */
2534 sfname = vim_strsave(sfname);
2535 if (sfname != NULL)
2536 forward_slash(sfname);
2537 forward_slash(fname);
2538#endif
2539
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002540 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002541 if (ap->pat != NULL && ap->cmds != NULL
2542 && (ap->buflocal_nr == 0
2543 ? match_file_pat(NULL, &ap->reg_prog,
2544 fname, sfname, tail, ap->allow_dirs)
2545 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2546 ))
2547 {
2548 retval = TRUE;
2549 break;
2550 }
2551
2552 vim_free(fname);
2553#ifdef BACKSLASH_IN_FILENAME
2554 vim_free(sfname);
2555#endif
2556
2557 return retval;
2558}
2559
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002560/*
2561 * Function given to ExpandGeneric() to obtain the list of autocommand group
2562 * names.
2563 */
2564 char_u *
2565get_augroup_name(expand_T *xp UNUSED, int idx)
2566{
2567 if (idx == augroups.ga_len) // add "END" add the end
2568 return (char_u *)"END";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002569 if (idx < 0 || idx >= augroups.ga_len) // end of list
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002570 return NULL;
2571 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2572 // skip deleted entries
2573 return (char_u *)"";
2574 return AUGROUP_NAME(idx); // return a name
2575}
2576
2577static int include_groups = FALSE;
2578
2579 char_u *
2580set_context_in_autocmd(
2581 expand_T *xp,
2582 char_u *arg,
2583 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2584{
2585 char_u *p;
2586 int group;
2587
2588 // check for a group name, skip it if present
2589 include_groups = FALSE;
2590 p = arg;
2591 group = au_get_grouparg(&arg);
2592 if (group == AUGROUP_ERROR)
2593 return NULL;
2594 // If there only is a group name that's what we expand.
2595 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2596 {
2597 arg = p;
2598 group = AUGROUP_ALL;
2599 }
2600
2601 // skip over event name
2602 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2603 if (*p == ',')
2604 arg = p + 1;
2605 if (*p == NUL)
2606 {
2607 if (group == AUGROUP_ALL)
2608 include_groups = TRUE;
2609 xp->xp_context = EXPAND_EVENTS; // expand event name
2610 xp->xp_pattern = arg;
2611 return NULL;
2612 }
2613
2614 // skip over pattern
2615 arg = skipwhite(p);
2616 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2617 arg++;
2618 if (*arg)
2619 return arg; // expand (next) command
2620
2621 if (doautocmd)
2622 xp->xp_context = EXPAND_FILES; // expand file names
2623 else
2624 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2625 return NULL;
2626}
2627
2628/*
2629 * Function given to ExpandGeneric() to obtain the list of event names.
2630 */
2631 char_u *
2632get_event_name(expand_T *xp UNUSED, int idx)
2633{
2634 if (idx < augroups.ga_len) // First list group names, if wanted
2635 {
2636 if (!include_groups || AUGROUP_NAME(idx) == NULL
2637 || AUGROUP_NAME(idx) == get_deleted_augroup())
2638 return (char_u *)""; // skip deleted entries
2639 return AUGROUP_NAME(idx); // return a name
2640 }
2641 return (char_u *)event_names[idx - augroups.ga_len].name;
2642}
2643
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002644
2645#if defined(FEAT_EVAL) || defined(PROTO)
2646/*
2647 * Return TRUE if autocmd is supported.
2648 */
2649 int
2650autocmd_supported(char_u *name)
2651{
2652 char_u *p;
2653
2654 return (event_name2nr(name, &p) != NUM_EVENTS);
2655}
2656
2657/*
2658 * Return TRUE if an autocommand is defined for a group, event and
2659 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2660 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2661 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2662 * Used for:
2663 * exists("#Group") or
2664 * exists("#Group#Event") or
2665 * exists("#Group#Event#pat") or
2666 * exists("#Event") or
2667 * exists("#Event#pat")
2668 */
2669 int
2670au_exists(char_u *arg)
2671{
2672 char_u *arg_save;
2673 char_u *pattern = NULL;
2674 char_u *event_name;
2675 char_u *p;
2676 event_T event;
2677 AutoPat *ap;
2678 buf_T *buflocal_buf = NULL;
2679 int group;
2680 int retval = FALSE;
2681
2682 // Make a copy so that we can change the '#' chars to a NUL.
2683 arg_save = vim_strsave(arg);
2684 if (arg_save == NULL)
2685 return FALSE;
2686 p = vim_strchr(arg_save, '#');
2687 if (p != NULL)
2688 *p++ = NUL;
2689
2690 // First, look for an autocmd group name
2691 group = au_find_group(arg_save);
2692 if (group == AUGROUP_ERROR)
2693 {
2694 // Didn't match a group name, assume the first argument is an event.
2695 group = AUGROUP_ALL;
2696 event_name = arg_save;
2697 }
2698 else
2699 {
2700 if (p == NULL)
2701 {
2702 // "Group": group name is present and it's recognized
2703 retval = TRUE;
2704 goto theend;
2705 }
2706
2707 // Must be "Group#Event" or "Group#Event#pat".
2708 event_name = p;
2709 p = vim_strchr(event_name, '#');
2710 if (p != NULL)
2711 *p++ = NUL; // "Group#Event#pat"
2712 }
2713
2714 pattern = p; // "pattern" is NULL when there is no pattern
2715
2716 // find the index (enum) for the event name
2717 event = event_name2nr(event_name, &p);
2718
2719 // return FALSE if the event name is not recognized
2720 if (event == NUM_EVENTS)
2721 goto theend;
2722
2723 // Find the first autocommand for this event.
2724 // If there isn't any, return FALSE;
2725 // If there is one and no pattern given, return TRUE;
2726 ap = first_autopat[(int)event];
2727 if (ap == NULL)
2728 goto theend;
2729
2730 // if pattern is "<buffer>", special handling is needed which uses curbuf
2731 // for pattern "<buffer=N>, fnamecmp() will work fine
2732 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2733 buflocal_buf = curbuf;
2734
2735 // Check if there is an autocommand with the given pattern.
2736 for ( ; ap != NULL; ap = ap->next)
2737 // only use a pattern when it has not been removed and has commands.
2738 // For buffer-local autocommands, fnamecmp() works fine.
2739 if (ap->pat != NULL && ap->cmds != NULL
2740 && (group == AUGROUP_ALL || ap->group == group)
2741 && (pattern == NULL
2742 || (buflocal_buf == NULL
2743 ? fnamecmp(ap->pat, pattern) == 0
2744 : ap->buflocal_nr == buflocal_buf->b_fnum)))
2745 {
2746 retval = TRUE;
2747 break;
2748 }
2749
2750theend:
2751 vim_free(arg_save);
2752 return retval;
2753}
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002754
2755/*
2756 * autocmd_add() and autocmd_delete() functions
2757 */
2758 static void
2759autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
2760{
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002761 list_T *aucmd_list;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002762 listitem_T *li;
2763 dict_T *event_dict;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002764 dictitem_T *di;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002765 char_u *event_name = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002766 list_T *event_list;
2767 listitem_T *eli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002768 event_T event;
2769 char_u *group_name = NULL;
2770 int group;
2771 char_u *pat = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002772 list_T *pat_list;
2773 listitem_T *pli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002774 char_u *cmd = NULL;
2775 char_u *end;
2776 int once;
2777 int nested;
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01002778 int replace; // replace the cmd for a group/event
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002779 int retval = VVAL_TRUE;
2780 int save_augroup = current_augroup;
2781
2782 rettv->v_type = VAR_BOOL;
2783 rettv->vval.v_number = VVAL_FALSE;
2784
2785 if (check_for_list_arg(argvars, 0) == FAIL)
2786 return;
2787
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002788 aucmd_list = argvars[0].vval.v_list;
2789 if (aucmd_list == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002790 return;
2791
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002792 FOR_ALL_LIST_ITEMS(aucmd_list, li)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002793 {
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002794 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002795 VIM_CLEAR(cmd);
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002796 event_name = NULL;
2797 event_list = NULL;
2798 pat = NULL;
2799 pat_list = NULL;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002800
2801 if (li->li_tv.v_type != VAR_DICT)
2802 continue;
2803
2804 event_dict = li->li_tv.vval.v_dict;
2805 if (event_dict == NULL)
2806 continue;
2807
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002808 di = dict_find(event_dict, (char_u *)"event", -1);
2809 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002810 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002811 if (di->di_tv.v_type == VAR_STRING)
2812 {
2813 event_name = di->di_tv.vval.v_string;
2814 if (event_name == NULL)
2815 {
2816 emsg(_(e_string_required));
2817 continue;
2818 }
2819 }
2820 else if (di->di_tv.v_type == VAR_LIST)
2821 {
2822 event_list = di->di_tv.vval.v_list;
2823 if (event_list == NULL)
2824 {
2825 emsg(_(e_list_required));
2826 continue;
2827 }
2828 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002829 else
2830 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002831 emsg(_(e_string_or_list_expected));
2832 continue;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002833 }
2834 }
2835
2836 group_name = dict_get_string(event_dict, (char_u *)"group", TRUE);
2837 if (group_name == NULL || *group_name == NUL)
2838 // if the autocmd group name is not specified, then use the current
2839 // autocmd group
2840 group = current_augroup;
2841 else
2842 {
2843 group = au_find_group(group_name);
2844 if (group == AUGROUP_ERROR)
2845 {
2846 if (delete)
2847 {
2848 semsg(_(e_no_such_group_str), group_name);
2849 retval = VVAL_FALSE;
2850 break;
2851 }
2852 // group is not found, create it now
2853 group = au_new_group(group_name);
2854 if (group == AUGROUP_ERROR)
2855 {
2856 semsg(_(e_no_such_group_str), group_name);
2857 retval = VVAL_FALSE;
2858 break;
2859 }
2860
2861 current_augroup = group;
2862 }
2863 }
2864
2865 // if a buffer number is specified, then generate a pattern of the form
2866 // "<buffer=n>. Otherwise, use the pattern supplied by the user.
2867 if (dict_has_key(event_dict, "bufnr"))
2868 {
2869 varnumber_T bnum;
2870
2871 bnum = dict_get_number_def(event_dict, (char_u *)"bufnr", -1);
2872 if (bnum == -1)
2873 continue;
2874
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002875 vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
2876 pat = IObuff;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002877 }
2878 else
2879 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002880 di = dict_find(event_dict, (char_u *)"pattern", -1);
2881 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002882 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002883 if (di->di_tv.v_type == VAR_STRING)
2884 {
2885 pat = di->di_tv.vval.v_string;
2886 if (pat == NULL)
2887 {
2888 emsg(_(e_string_required));
2889 continue;
2890 }
2891 }
2892 else if (di->di_tv.v_type == VAR_LIST)
2893 {
2894 pat_list = di->di_tv.vval.v_list;
2895 if (pat_list == NULL)
2896 {
2897 emsg(_(e_list_required));
2898 continue;
2899 }
2900 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002901 else
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002902 {
2903 emsg(_(e_string_or_list_expected));
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002904 continue;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002905 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002906 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002907 else if (delete)
2908 pat = (char_u *)"";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002909 }
2910
2911 once = dict_get_bool(event_dict, (char_u *)"once", FALSE);
2912 nested = dict_get_bool(event_dict, (char_u *)"nested", FALSE);
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01002913 // if 'replace' is true, then remove all the commands associated with
2914 // this autocmd event/group and add the new command.
2915 replace = dict_get_bool(event_dict, (char_u *)"replace", FALSE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002916
2917 cmd = dict_get_string(event_dict, (char_u *)"cmd", TRUE);
2918 if (cmd == NULL)
2919 {
2920 if (delete)
2921 cmd = vim_strsave((char_u *)"");
2922 else
2923 continue;
2924 }
2925
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002926 if (delete && (event_name == NULL
2927 || (event_name[0] == '*' && event_name[1] == NUL)))
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002928 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002929 // if the event name is not specified or '*', delete all the events
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002930 for (event = (event_T)0; (int)event < NUM_EVENTS;
2931 event = (event_T)((int)event + 1))
2932 {
2933 if (do_autocmd_event(event, pat, once, nested, cmd, delete,
2934 group, 0) == FAIL)
2935 {
2936 retval = VVAL_FALSE;
2937 break;
2938 }
2939 }
2940 }
2941 else
2942 {
Bram Moolenaar968443e2022-05-27 21:16:34 +01002943 char_u *p = NULL;
2944
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002945 eli = NULL;
2946 end = NULL;
2947 while (TRUE)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002948 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002949 if (event_list != NULL)
2950 {
2951 if (eli == NULL)
2952 eli = event_list->lv_first;
2953 else
2954 eli = eli->li_next;
2955 if (eli == NULL)
2956 break;
2957 if (eli->li_tv.v_type != VAR_STRING
Bram Moolenaar882476a2022-06-01 16:02:38 +01002958 || (p = eli->li_tv.vval.v_string) == NULL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002959 {
2960 emsg(_(e_string_required));
Bram Moolenaar882476a2022-06-01 16:02:38 +01002961 break;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002962 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002963 }
2964 else
2965 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01002966 if (p == NULL)
2967 p = event_name;
2968 if (p == NULL || *p == NUL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002969 break;
2970 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002971
2972 event = event_name2nr(p, &end);
2973 if (event == NUM_EVENTS || *end != NUL)
2974 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01002975 // this also catches something following a valid event name
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002976 semsg(_(e_no_such_event_str), p);
2977 retval = VVAL_FALSE;
2978 break;
2979 }
2980 if (pat != NULL)
2981 {
2982 if (do_autocmd_event(event, pat, once, nested, cmd,
2983 delete | replace, group, 0) == FAIL)
2984 {
2985 retval = VVAL_FALSE;
2986 break;
2987 }
2988 }
2989 else if (pat_list != NULL)
2990 {
2991 FOR_ALL_LIST_ITEMS(pat_list, pli)
2992 {
2993 if (pli->li_tv.v_type != VAR_STRING
2994 || pli->li_tv.vval.v_string == NULL)
2995 {
2996 emsg(_(e_string_required));
2997 continue;
2998 }
2999 if (do_autocmd_event(event,
3000 pli->li_tv.vval.v_string, once, nested,
3001 cmd, delete | replace, group, 0) ==
3002 FAIL)
3003 {
3004 retval = VVAL_FALSE;
3005 break;
3006 }
3007 }
3008 if (retval == VVAL_FALSE)
3009 break;
3010 }
3011 if (event_name != NULL)
3012 p = end;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003013 }
3014 }
3015
3016 // if only the autocmd group name is specified for delete and the
3017 // autocmd event, pattern and cmd are not specified, then delete the
3018 // autocmd group.
3019 if (delete && group_name != NULL &&
3020 (event_name == NULL || event_name[0] == NUL)
3021 && (pat == NULL || pat[0] == NUL)
3022 && (cmd == NULL || cmd[0] == NUL))
3023 au_del_group(group_name);
3024 }
3025
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003026 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003027 VIM_CLEAR(cmd);
3028
3029 current_augroup = save_augroup;
3030 rettv->vval.v_number = retval;
3031}
3032
3033/*
3034 * autocmd_add() function
3035 */
3036 void
3037f_autocmd_add(typval_T *argvars, typval_T *rettv)
3038{
3039 autocmd_add_or_delete(argvars, rettv, FALSE);
3040}
3041
3042/*
3043 * autocmd_delete() function
3044 */
3045 void
3046f_autocmd_delete(typval_T *argvars, typval_T *rettv)
3047{
3048 autocmd_add_or_delete(argvars, rettv, TRUE);
3049}
3050
3051/*
3052 * autocmd_get() function
3053 * Returns a List of autocmds.
3054 */
3055 void
3056f_autocmd_get(typval_T *argvars, typval_T *rettv)
3057{
3058 event_T event_arg = NUM_EVENTS;
3059 event_T event;
3060 AutoPat *ap;
3061 AutoCmd *ac;
3062 list_T *event_list;
3063 dict_T *event_dict;
3064 char_u *event_name = NULL;
3065 char_u *pat = NULL;
3066 char_u *name = NULL;
3067 int group = AUGROUP_ALL;
3068
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01003069 if (rettv_list_alloc(rettv) == FAIL)
3070 return;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003071 if (check_for_opt_dict_arg(argvars, 0) == FAIL)
3072 return;
3073
3074 if (argvars[0].v_type == VAR_DICT)
3075 {
3076 // return only the autocmds in the specified group
3077 if (dict_has_key(argvars[0].vval.v_dict, "group"))
3078 {
3079 name = dict_get_string(argvars[0].vval.v_dict,
3080 (char_u *)"group", TRUE);
3081 if (name == NULL)
3082 return;
3083
3084 if (*name == NUL)
3085 group = AUGROUP_DEFAULT;
3086 else
3087 {
3088 group = au_find_group(name);
3089 if (group == AUGROUP_ERROR)
3090 {
3091 semsg(_(e_no_such_group_str), name);
3092 vim_free(name);
3093 return;
3094 }
3095 }
3096 vim_free(name);
3097 }
3098
3099 // return only the autocmds for the specified event
3100 if (dict_has_key(argvars[0].vval.v_dict, "event"))
3101 {
3102 int i;
3103
3104 name = dict_get_string(argvars[0].vval.v_dict,
3105 (char_u *)"event", TRUE);
3106 if (name == NULL)
3107 return;
3108
3109 if (name[0] == '*' && name[1] == NUL)
3110 event_arg = NUM_EVENTS;
3111 else
3112 {
3113 for (i = 0; event_names[i].name != NULL; i++)
3114 if (STRICMP(event_names[i].name, name) == 0)
3115 break;
3116 if (event_names[i].name == NULL)
3117 {
3118 semsg(_(e_no_such_event_str), name);
3119 vim_free(name);
3120 return;
3121 }
3122 event_arg = event_names[i].event;
3123 }
3124 vim_free(name);
3125 }
3126
3127 // return only the autocmds for the specified pattern
3128 if (dict_has_key(argvars[0].vval.v_dict, "pattern"))
3129 {
3130 pat = dict_get_string(argvars[0].vval.v_dict,
3131 (char_u *)"pattern", TRUE);
3132 if (pat == NULL)
3133 return;
3134 }
3135 }
3136
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003137 event_list = rettv->vval.v_list;
3138
3139 // iterate through all the autocmd events
3140 for (event = (event_T)0; (int)event < NUM_EVENTS;
3141 event = (event_T)((int)event + 1))
3142 {
3143 if (event_arg != NUM_EVENTS && event != event_arg)
3144 continue;
3145
3146 event_name = event_nr2name(event);
3147
3148 // iterate through all the patterns for this autocmd event
3149 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
3150 {
3151 char_u *group_name;
3152
3153 if (group != AUGROUP_ALL && group != ap->group)
3154 continue;
3155
3156 if (pat != NULL && STRCMP(pat, ap->pat) != 0)
3157 continue;
3158
3159 group_name = get_augroup_name(NULL, ap->group);
3160
3161 // iterate through all the commands for this pattern and add one
3162 // item for each cmd.
3163 for (ac = ap->cmds; ac != NULL; ac = ac->next)
3164 {
3165 event_dict = dict_alloc();
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003166 if (event_dict == NULL
3167 || list_append_dict(event_list, event_dict) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003168 return;
3169
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003170 if (dict_add_string(event_dict, "event", event_name) == FAIL
3171 || dict_add_string(event_dict, "group",
3172 group_name == NULL ? (char_u *)""
3173 : group_name) == FAIL
3174 || (ap->buflocal_nr != 0
3175 && (dict_add_number(event_dict, "bufnr",
3176 ap->buflocal_nr) == FAIL))
3177 || dict_add_string(event_dict, "pattern",
3178 ap->pat) == FAIL
3179 || dict_add_string(event_dict, "cmd", ac->cmd) == FAIL
3180 || dict_add_bool(event_dict, "once", ac->once) == FAIL
3181 || dict_add_bool(event_dict, "nested",
3182 ac->nested) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003183 return;
3184 }
3185 }
3186 }
3187
3188 vim_free(pat);
3189}
3190
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01003191#endif