blob: 4b30c1f5303b50633baea3d2d330e8f92757d4e1 [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 Moolenaar1e6bbfb2021-04-03 13:19:26 +02002212 if (nesting == 1)
2213 // make sure cursor and topline are valid
2214 check_lnums(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002215
ichizokc3f91c02021-12-17 09:44:33 +00002216 save_did_emsg = did_emsg;
2217
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002218 do_cmdline(NULL, getnextac, (void *)&patcmd,
2219 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002220
ichizokc3f91c02021-12-17 09:44:33 +00002221 did_emsg += save_did_emsg;
2222
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002223 if (nesting == 1)
2224 // restore cursor and topline, unless they were changed
2225 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002226
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002227#ifdef FEAT_EVAL
2228 if (eap != NULL)
2229 {
2230 (void)set_cmdarg(NULL, save_cmdarg);
2231 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2232 }
2233#endif
2234 // delete from active_apc_list
2235 if (active_apc_list == &patcmd) // just in case
2236 active_apc_list = patcmd.next;
2237 }
2238
2239 --RedrawingDisabled;
2240 autocmd_busy = save_autocmd_busy;
2241 filechangeshell_busy = FALSE;
2242 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002243 vim_free(SOURCING_NAME);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002244 ESTACK_CHECK_NOW
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002245 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002246 vim_free(autocmd_fname);
2247 autocmd_fname = save_autocmd_fname;
2248 autocmd_fname_full = save_autocmd_fname_full;
2249 autocmd_bufnr = save_autocmd_bufnr;
2250 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002251 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002252#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002253 restore_funccal();
2254# ifdef FEAT_PROFILE
2255 if (do_profiling == PROF_YES)
2256 prof_child_exit(&wait_time);
2257# endif
2258#endif
2259 KeyTyped = save_KeyTyped;
2260 vim_free(fname);
2261 vim_free(sfname);
2262 --nesting; // see matching increment above
2263
2264 /*
2265 * When stopping to execute autocommands, restore the search patterns and
2266 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2267 * free any windows in the au_pending_free_win list.
2268 */
2269 if (!autocmd_busy)
2270 {
2271 restore_search_patterns();
2272 if (did_save_redobuff)
2273 restoreRedobuff(&save_redo);
2274 did_filetype = FALSE;
2275 while (au_pending_free_buf != NULL)
2276 {
2277 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002278
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002279 vim_free(au_pending_free_buf);
2280 au_pending_free_buf = b;
2281 }
2282 while (au_pending_free_win != NULL)
2283 {
2284 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002285
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002286 vim_free(au_pending_free_win);
2287 au_pending_free_win = w;
2288 }
2289 }
2290
2291 /*
2292 * Some events don't set or reset the Changed flag.
2293 * Check if still in the same buffer!
2294 */
2295 if (curbuf == old_curbuf
2296 && (event == EVENT_BUFREADPOST
2297 || event == EVENT_BUFWRITEPOST
2298 || event == EVENT_FILEAPPENDPOST
2299 || event == EVENT_VIMLEAVE
2300 || event == EVENT_VIMLEAVEPRE))
2301 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002302 if (curbuf->b_changed != save_changed)
2303 need_maketitle = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002304 curbuf->b_changed = save_changed;
2305 }
2306
2307 au_cleanup(); // may really delete removed patterns/commands now
2308
2309BYPASS_AU:
2310 // When wiping out a buffer make sure all its buffer-local autocommands
2311 // are deleted.
2312 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2313 aubuflocal_remove(buf);
2314
2315 if (retval == OK && event == EVENT_FILETYPE)
2316 au_did_filetype = TRUE;
2317
2318 return retval;
2319}
2320
2321# ifdef FEAT_EVAL
2322static char_u *old_termresponse = NULL;
2323# endif
2324
2325/*
2326 * Block triggering autocommands until unblock_autocmd() is called.
2327 * Can be used recursively, so long as it's symmetric.
2328 */
2329 void
2330block_autocmds(void)
2331{
2332# ifdef FEAT_EVAL
2333 // Remember the value of v:termresponse.
2334 if (autocmd_blocked == 0)
2335 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
2336# endif
2337 ++autocmd_blocked;
2338}
2339
2340 void
2341unblock_autocmds(void)
2342{
2343 --autocmd_blocked;
2344
2345# ifdef FEAT_EVAL
2346 // When v:termresponse was set while autocommands were blocked, trigger
2347 // the autocommands now. Esp. useful when executing a shell command
2348 // during startup (vimdiff).
2349 if (autocmd_blocked == 0
2350 && get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2351 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2352# endif
2353}
2354
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002355 int
2356is_autocmd_blocked(void)
2357{
2358 return autocmd_blocked != 0;
2359}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002360
2361/*
2362 * Find next autocommand pattern that matches.
2363 */
2364 static void
2365auto_next_pat(
LemonBoyeca7c602022-04-14 15:39:43 +01002366 AutoPatCmd_T *apc,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002367 int stop_at_last) // stop when 'last' flag is set
2368{
2369 AutoPat *ap;
2370 AutoCmd *cp;
2371 char_u *name;
2372 char *s;
LemonBoyeca7c602022-04-14 15:39:43 +01002373 estack_T *entry;
2374 char_u *namep;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002375
LemonBoyeca7c602022-04-14 15:39:43 +01002376 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
2377
2378 // Clear the exestack entry for this ETYPE_AUCMD entry.
2379 VIM_CLEAR(entry->es_name);
2380 entry->es_info.aucmd = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002381
2382 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2383 {
2384 apc->curpat = NULL;
2385
2386 // Only use a pattern when it has not been removed, has commands and
2387 // the group matches. For buffer-local autocommands only check the
2388 // buffer number.
2389 if (ap->pat != NULL && ap->cmds != NULL
2390 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2391 {
2392 // execution-condition
2393 if (ap->buflocal_nr == 0
2394 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2395 apc->sfname, apc->tail, ap->allow_dirs))
2396 : ap->buflocal_nr == apc->arg_bufnr)
2397 {
2398 name = event_nr2name(apc->event);
2399 s = _("%s Autocommands for \"%s\"");
LemonBoyeca7c602022-04-14 15:39:43 +01002400 namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1);
2401 if (namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002402 {
LemonBoyeca7c602022-04-14 15:39:43 +01002403 sprintf((char *)namep, s, (char *)name, (char *)ap->pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002404 if (p_verbose >= 8)
2405 {
2406 verbose_enter();
LemonBoyeca7c602022-04-14 15:39:43 +01002407 smsg(_("Executing %s"), namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002408 verbose_leave();
2409 }
2410 }
2411
LemonBoyeca7c602022-04-14 15:39:43 +01002412 // Update the exestack entry for this autocmd.
2413 entry->es_name = namep;
2414 entry->es_info.aucmd = apc;
2415
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002416 apc->curpat = ap;
2417 apc->nextcmd = ap->cmds;
2418 // mark last command
2419 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2420 cp->last = FALSE;
2421 cp->last = TRUE;
2422 }
2423 line_breakcheck();
2424 if (apc->curpat != NULL) // found a match
2425 break;
2426 }
2427 if (stop_at_last && ap->last)
2428 break;
2429 }
2430}
2431
2432/*
LemonBoyeca7c602022-04-14 15:39:43 +01002433 * Get the script context where autocommand "acp" is defined.
2434 */
2435 sctx_T *
2436acp_script_ctx(AutoPatCmd_T *acp)
2437{
2438 return &acp->script_ctx;
2439}
2440
2441/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002442 * Get next autocommand command.
2443 * Called by do_cmdline() to get the next line for ":if".
2444 * Returns allocated string, or NULL for end of autocommands.
2445 */
2446 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002447getnextac(
2448 int c UNUSED,
2449 void *cookie,
2450 int indent UNUSED,
2451 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002452{
LemonBoyeca7c602022-04-14 15:39:43 +01002453 AutoPatCmd_T *acp = (AutoPatCmd_T *)cookie;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002454 char_u *retval;
2455 AutoCmd *ac;
2456
2457 // Can be called again after returning the last line.
2458 if (acp->curpat == NULL)
2459 return NULL;
2460
2461 // repeat until we find an autocommand to execute
2462 for (;;)
2463 {
2464 // skip removed commands
2465 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2466 if (acp->nextcmd->last)
2467 acp->nextcmd = NULL;
2468 else
2469 acp->nextcmd = acp->nextcmd->next;
2470
2471 if (acp->nextcmd != NULL)
2472 break;
2473
2474 // at end of commands, find next pattern that matches
2475 if (acp->curpat->last)
2476 acp->curpat = NULL;
2477 else
2478 acp->curpat = acp->curpat->next;
2479 if (acp->curpat != NULL)
2480 auto_next_pat(acp, TRUE);
2481 if (acp->curpat == NULL)
2482 return NULL;
2483 }
2484
2485 ac = acp->nextcmd;
2486
2487 if (p_verbose >= 9)
2488 {
2489 verbose_enter_scroll();
2490 smsg(_("autocommand %s"), ac->cmd);
2491 msg_puts("\n"); // don't overwrite this either
2492 verbose_leave_scroll();
2493 }
2494 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002495 // Remove one-shot ("once") autocmd in anticipation of its execution.
2496 if (ac->once)
2497 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002498 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002499 current_sctx = ac->script_ctx;
LemonBoyeca7c602022-04-14 15:39:43 +01002500 acp->script_ctx = current_sctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002501 if (ac->last)
2502 acp->nextcmd = NULL;
2503 else
2504 acp->nextcmd = ac->next;
2505 return retval;
2506}
2507
2508/*
2509 * Return TRUE if there is a matching autocommand for "fname".
2510 * To account for buffer-local autocommands, function needs to know
2511 * in which buffer the file will be opened.
2512 */
2513 int
2514has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2515{
2516 AutoPat *ap;
2517 char_u *fname;
2518 char_u *tail = gettail(sfname);
2519 int retval = FALSE;
2520
2521 fname = FullName_save(sfname, FALSE);
2522 if (fname == NULL)
2523 return FALSE;
2524
2525#ifdef BACKSLASH_IN_FILENAME
2526 /*
2527 * Replace all backslashes with forward slashes. This makes the
2528 * autocommand patterns portable between Unix and MS-DOS.
2529 */
2530 sfname = vim_strsave(sfname);
2531 if (sfname != NULL)
2532 forward_slash(sfname);
2533 forward_slash(fname);
2534#endif
2535
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002536 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002537 if (ap->pat != NULL && ap->cmds != NULL
2538 && (ap->buflocal_nr == 0
2539 ? match_file_pat(NULL, &ap->reg_prog,
2540 fname, sfname, tail, ap->allow_dirs)
2541 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2542 ))
2543 {
2544 retval = TRUE;
2545 break;
2546 }
2547
2548 vim_free(fname);
2549#ifdef BACKSLASH_IN_FILENAME
2550 vim_free(sfname);
2551#endif
2552
2553 return retval;
2554}
2555
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002556/*
2557 * Function given to ExpandGeneric() to obtain the list of autocommand group
2558 * names.
2559 */
2560 char_u *
2561get_augroup_name(expand_T *xp UNUSED, int idx)
2562{
2563 if (idx == augroups.ga_len) // add "END" add the end
2564 return (char_u *)"END";
2565 if (idx >= augroups.ga_len) // end of list
2566 return NULL;
2567 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2568 // skip deleted entries
2569 return (char_u *)"";
2570 return AUGROUP_NAME(idx); // return a name
2571}
2572
2573static int include_groups = FALSE;
2574
2575 char_u *
2576set_context_in_autocmd(
2577 expand_T *xp,
2578 char_u *arg,
2579 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2580{
2581 char_u *p;
2582 int group;
2583
2584 // check for a group name, skip it if present
2585 include_groups = FALSE;
2586 p = arg;
2587 group = au_get_grouparg(&arg);
2588 if (group == AUGROUP_ERROR)
2589 return NULL;
2590 // If there only is a group name that's what we expand.
2591 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2592 {
2593 arg = p;
2594 group = AUGROUP_ALL;
2595 }
2596
2597 // skip over event name
2598 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2599 if (*p == ',')
2600 arg = p + 1;
2601 if (*p == NUL)
2602 {
2603 if (group == AUGROUP_ALL)
2604 include_groups = TRUE;
2605 xp->xp_context = EXPAND_EVENTS; // expand event name
2606 xp->xp_pattern = arg;
2607 return NULL;
2608 }
2609
2610 // skip over pattern
2611 arg = skipwhite(p);
2612 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2613 arg++;
2614 if (*arg)
2615 return arg; // expand (next) command
2616
2617 if (doautocmd)
2618 xp->xp_context = EXPAND_FILES; // expand file names
2619 else
2620 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2621 return NULL;
2622}
2623
2624/*
2625 * Function given to ExpandGeneric() to obtain the list of event names.
2626 */
2627 char_u *
2628get_event_name(expand_T *xp UNUSED, int idx)
2629{
2630 if (idx < augroups.ga_len) // First list group names, if wanted
2631 {
2632 if (!include_groups || AUGROUP_NAME(idx) == NULL
2633 || AUGROUP_NAME(idx) == get_deleted_augroup())
2634 return (char_u *)""; // skip deleted entries
2635 return AUGROUP_NAME(idx); // return a name
2636 }
2637 return (char_u *)event_names[idx - augroups.ga_len].name;
2638}
2639
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002640
2641#if defined(FEAT_EVAL) || defined(PROTO)
2642/*
2643 * Return TRUE if autocmd is supported.
2644 */
2645 int
2646autocmd_supported(char_u *name)
2647{
2648 char_u *p;
2649
2650 return (event_name2nr(name, &p) != NUM_EVENTS);
2651}
2652
2653/*
2654 * Return TRUE if an autocommand is defined for a group, event and
2655 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2656 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2657 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2658 * Used for:
2659 * exists("#Group") or
2660 * exists("#Group#Event") or
2661 * exists("#Group#Event#pat") or
2662 * exists("#Event") or
2663 * exists("#Event#pat")
2664 */
2665 int
2666au_exists(char_u *arg)
2667{
2668 char_u *arg_save;
2669 char_u *pattern = NULL;
2670 char_u *event_name;
2671 char_u *p;
2672 event_T event;
2673 AutoPat *ap;
2674 buf_T *buflocal_buf = NULL;
2675 int group;
2676 int retval = FALSE;
2677
2678 // Make a copy so that we can change the '#' chars to a NUL.
2679 arg_save = vim_strsave(arg);
2680 if (arg_save == NULL)
2681 return FALSE;
2682 p = vim_strchr(arg_save, '#');
2683 if (p != NULL)
2684 *p++ = NUL;
2685
2686 // First, look for an autocmd group name
2687 group = au_find_group(arg_save);
2688 if (group == AUGROUP_ERROR)
2689 {
2690 // Didn't match a group name, assume the first argument is an event.
2691 group = AUGROUP_ALL;
2692 event_name = arg_save;
2693 }
2694 else
2695 {
2696 if (p == NULL)
2697 {
2698 // "Group": group name is present and it's recognized
2699 retval = TRUE;
2700 goto theend;
2701 }
2702
2703 // Must be "Group#Event" or "Group#Event#pat".
2704 event_name = p;
2705 p = vim_strchr(event_name, '#');
2706 if (p != NULL)
2707 *p++ = NUL; // "Group#Event#pat"
2708 }
2709
2710 pattern = p; // "pattern" is NULL when there is no pattern
2711
2712 // find the index (enum) for the event name
2713 event = event_name2nr(event_name, &p);
2714
2715 // return FALSE if the event name is not recognized
2716 if (event == NUM_EVENTS)
2717 goto theend;
2718
2719 // Find the first autocommand for this event.
2720 // If there isn't any, return FALSE;
2721 // If there is one and no pattern given, return TRUE;
2722 ap = first_autopat[(int)event];
2723 if (ap == NULL)
2724 goto theend;
2725
2726 // if pattern is "<buffer>", special handling is needed which uses curbuf
2727 // for pattern "<buffer=N>, fnamecmp() will work fine
2728 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2729 buflocal_buf = curbuf;
2730
2731 // Check if there is an autocommand with the given pattern.
2732 for ( ; ap != NULL; ap = ap->next)
2733 // only use a pattern when it has not been removed and has commands.
2734 // For buffer-local autocommands, fnamecmp() works fine.
2735 if (ap->pat != NULL && ap->cmds != NULL
2736 && (group == AUGROUP_ALL || ap->group == group)
2737 && (pattern == NULL
2738 || (buflocal_buf == NULL
2739 ? fnamecmp(ap->pat, pattern) == 0
2740 : ap->buflocal_nr == buflocal_buf->b_fnum)))
2741 {
2742 retval = TRUE;
2743 break;
2744 }
2745
2746theend:
2747 vim_free(arg_save);
2748 return retval;
2749}
2750#endif