blob: 3b384f5f96a1a60056b370f91bf717d36f3c37c5 [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
Bram Moolenaar3e460fd2019-01-26 16:21:07 +010058 sctx_T script_ctx; // script context where 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
237 int arg_bufnr; // Initially equal to <abuf>, set to zero when
238 // buf is deleted.
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100239 AutoPatCmd *next; // chain of active apc-s for auto-invalidation
240};
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100241
Bram Moolenaarc667da52019-11-30 20:52:27 +0100242static AutoPatCmd *active_apc_list = NULL; // stack of active autocommands
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100243
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200244// Macro to loop over all the patterns for an autocmd event
245#define FOR_ALL_AUTOCMD_PATTERNS(event, ap) \
246 for ((ap) = first_autopat[(int)(event)]; (ap) != NULL; (ap) = (ap)->next)
247
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100248/*
249 * augroups stores a list of autocmd group names.
250 */
251static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
252#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
Bram Moolenaarc667da52019-11-30 20:52:27 +0100253// use get_deleted_augroup() to get this
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100254static char_u *deleted_augroup = NULL;
255
256/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100257 * The ID of the current group. Group 0 is the default one.
258 */
259static int current_augroup = AUGROUP_DEFAULT;
260
Bram Moolenaarc667da52019-11-30 20:52:27 +0100261static int au_need_clean = FALSE; // need to delete marked patterns
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100262
263static char_u *event_nr2name(event_T event);
264static int au_get_grouparg(char_u **argp);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200265static 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 +0100266static int apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, int force, int group, buf_T *buf, exarg_T *eap);
267static void auto_next_pat(AutoPatCmd *apc, int stop_at_last);
268static int au_find_group(char_u *name);
269
270static event_T last_event;
271static int last_group;
Bram Moolenaarc667da52019-11-30 20:52:27 +0100272static int autocmd_blocked = 0; // block all autocmds
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100273
274 static char_u *
275get_deleted_augroup(void)
276{
277 if (deleted_augroup == NULL)
278 deleted_augroup = (char_u *)_("--Deleted--");
279 return deleted_augroup;
280}
281
282/*
283 * Show the autocommands for one AutoPat.
284 */
285 static void
286show_autocmd(AutoPat *ap, event_T event)
287{
288 AutoCmd *ac;
289
290 // Check for "got_int" (here and at various places below), which is set
291 // when "q" has been hit for the "--more--" prompt
292 if (got_int)
293 return;
294 if (ap->pat == NULL) // pattern has been removed
295 return;
296
297 msg_putchar('\n');
298 if (got_int)
299 return;
300 if (event != last_event || ap->group != last_group)
301 {
302 if (ap->group != AUGROUP_DEFAULT)
303 {
304 if (AUGROUP_NAME(ap->group) == NULL)
305 msg_puts_attr((char *)get_deleted_augroup(), HL_ATTR(HLF_E));
306 else
307 msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
308 msg_puts(" ");
309 }
310 msg_puts_attr((char *)event_nr2name(event), HL_ATTR(HLF_T));
311 last_event = event;
312 last_group = ap->group;
313 msg_putchar('\n');
314 if (got_int)
315 return;
316 }
317 msg_col = 4;
318 msg_outtrans(ap->pat);
319
320 for (ac = ap->cmds; ac != NULL; ac = ac->next)
321 {
322 if (ac->cmd != NULL) // skip removed commands
323 {
324 if (msg_col >= 14)
325 msg_putchar('\n');
326 msg_col = 14;
327 if (got_int)
328 return;
329 msg_outtrans(ac->cmd);
330#ifdef FEAT_EVAL
331 if (p_verbose > 0)
332 last_set_msg(ac->script_ctx);
333#endif
334 if (got_int)
335 return;
336 if (ac->next != NULL)
337 {
338 msg_putchar('\n');
339 if (got_int)
340 return;
341 }
342 }
343 }
344}
345
346/*
347 * Mark an autocommand pattern for deletion.
348 */
349 static void
350au_remove_pat(AutoPat *ap)
351{
352 VIM_CLEAR(ap->pat);
353 ap->buflocal_nr = -1;
354 au_need_clean = TRUE;
355}
356
357/*
358 * Mark all commands for a pattern for deletion.
359 */
360 static void
361au_remove_cmds(AutoPat *ap)
362{
363 AutoCmd *ac;
364
365 for (ac = ap->cmds; ac != NULL; ac = ac->next)
366 VIM_CLEAR(ac->cmd);
367 au_need_clean = TRUE;
368}
369
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200370// Delete one command from an autocmd pattern.
371static void au_del_cmd(AutoCmd *ac)
372{
373 VIM_CLEAR(ac->cmd);
374 au_need_clean = TRUE;
375}
376
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100377/*
378 * Cleanup autocommands and patterns that have been deleted.
379 * This is only done when not executing autocommands.
380 */
381 static void
382au_cleanup(void)
383{
384 AutoPat *ap, **prev_ap;
385 AutoCmd *ac, **prev_ac;
386 event_T event;
387
388 if (autocmd_busy || !au_need_clean)
389 return;
390
391 // loop over all events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100392 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100393 event = (event_T)((int)event + 1))
394 {
395 // loop over all autocommand patterns
396 prev_ap = &(first_autopat[(int)event]);
397 for (ap = *prev_ap; ap != NULL; ap = *prev_ap)
398 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200399 int has_cmd = FALSE;
400
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200401 // loop over all commands for this pattern
402 prev_ac = &(ap->cmds);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100403 for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
404 {
405 // remove the command if the pattern is to be deleted or when
406 // the command has been marked for deletion
407 if (ap->pat == NULL || ac->cmd == NULL)
408 {
409 *prev_ac = ac->next;
410 vim_free(ac->cmd);
411 vim_free(ac);
412 }
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200413 else
414 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200415 has_cmd = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100416 prev_ac = &(ac->next);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200417 }
418 }
419
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200420 if (ap->pat != NULL && !has_cmd)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200421 // Pattern was not marked for deletion, but all of its
422 // commands were. So mark the pattern for deletion.
423 au_remove_pat(ap);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100424
425 // remove the pattern if it has been marked for deletion
426 if (ap->pat == NULL)
427 {
428 if (ap->next == NULL)
429 {
430 if (prev_ap == &(first_autopat[(int)event]))
431 last_autopat[(int)event] = NULL;
432 else
433 // this depends on the "next" field being the first in
434 // the struct
435 last_autopat[(int)event] = (AutoPat *)prev_ap;
436 }
437 *prev_ap = ap->next;
438 vim_regfree(ap->reg_prog);
439 vim_free(ap);
440 }
441 else
442 prev_ap = &(ap->next);
443 }
444 }
445
446 au_need_clean = FALSE;
447}
448
449/*
450 * Called when buffer is freed, to remove/invalidate related buffer-local
451 * autocmds.
452 */
453 void
454aubuflocal_remove(buf_T *buf)
455{
456 AutoPat *ap;
457 event_T event;
458 AutoPatCmd *apc;
459
460 // invalidate currently executing autocommands
461 for (apc = active_apc_list; apc; apc = apc->next)
462 if (buf->b_fnum == apc->arg_bufnr)
463 apc->arg_bufnr = 0;
464
465 // invalidate buflocals looping through events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100466 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100467 event = (event_T)((int)event + 1))
468 // loop over all autocommand patterns
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200469 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100470 if (ap->buflocal_nr == buf->b_fnum)
471 {
472 au_remove_pat(ap);
473 if (p_verbose >= 6)
474 {
475 verbose_enter();
476 smsg(_("auto-removing autocommand: %s <buffer=%d>"),
477 event_nr2name(event), buf->b_fnum);
478 verbose_leave();
479 }
480 }
481 au_cleanup();
482}
483
484/*
485 * Add an autocmd group name.
486 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
487 */
488 static int
489au_new_group(char_u *name)
490{
491 int i;
492
493 i = au_find_group(name);
494 if (i == AUGROUP_ERROR) // the group doesn't exist yet, add it
495 {
496 // First try using a free entry.
497 for (i = 0; i < augroups.ga_len; ++i)
498 if (AUGROUP_NAME(i) == NULL)
499 break;
500 if (i == augroups.ga_len && ga_grow(&augroups, 1) == FAIL)
501 return AUGROUP_ERROR;
502
503 AUGROUP_NAME(i) = vim_strsave(name);
504 if (AUGROUP_NAME(i) == NULL)
505 return AUGROUP_ERROR;
506 if (i == augroups.ga_len)
507 ++augroups.ga_len;
508 }
509
510 return i;
511}
512
513 static void
514au_del_group(char_u *name)
515{
516 int i;
517
518 i = au_find_group(name);
519 if (i == AUGROUP_ERROR) // the group doesn't exist
Bram Moolenaarf1474d82021-12-31 19:59:55 +0000520 semsg(_(e_no_such_group_str), name);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100521 else if (i == current_augroup)
Bram Moolenaarf1474d82021-12-31 19:59:55 +0000522 emsg(_(e_cannot_delete_current_group));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100523 else
524 {
525 event_T event;
526 AutoPat *ap;
527 int in_use = FALSE;
528
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100529 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100530 event = (event_T)((int)event + 1))
531 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200532 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100533 if (ap->group == i && ap->pat != NULL)
534 {
535 give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
536 in_use = TRUE;
537 event = NUM_EVENTS;
538 break;
539 }
540 }
541 vim_free(AUGROUP_NAME(i));
542 if (in_use)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100543 AUGROUP_NAME(i) = get_deleted_augroup();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100544 else
545 AUGROUP_NAME(i) = NULL;
546 }
547}
548
549/*
550 * Find the ID of an autocmd group name.
551 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
552 */
553 static int
554au_find_group(char_u *name)
555{
556 int i;
557
558 for (i = 0; i < augroups.ga_len; ++i)
559 if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
560 && STRCMP(AUGROUP_NAME(i), name) == 0)
561 return i;
562 return AUGROUP_ERROR;
563}
564
565/*
566 * Return TRUE if augroup "name" exists.
567 */
568 int
569au_has_group(char_u *name)
570{
571 return au_find_group(name) != AUGROUP_ERROR;
572}
573
574/*
575 * ":augroup {name}".
576 */
577 void
578do_augroup(char_u *arg, int del_group)
579{
580 int i;
581
582 if (del_group)
583 {
584 if (*arg == NUL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000585 emsg(_(e_argument_required));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100586 else
587 au_del_group(arg);
588 }
589 else if (STRICMP(arg, "end") == 0) // ":aug end": back to group 0
590 current_augroup = AUGROUP_DEFAULT;
591 else if (*arg) // ":aug xxx": switch to group xxx
592 {
593 i = au_new_group(arg);
594 if (i != AUGROUP_ERROR)
595 current_augroup = i;
596 }
597 else // ":aug": list the group names
598 {
599 msg_start();
600 for (i = 0; i < augroups.ga_len; ++i)
601 {
602 if (AUGROUP_NAME(i) != NULL)
603 {
604 msg_puts((char *)AUGROUP_NAME(i));
605 msg_puts(" ");
606 }
607 }
608 msg_clr_eos();
609 msg_end();
610 }
611}
612
613#if defined(EXITFREE) || defined(PROTO)
614 void
615free_all_autocmds(void)
616{
617 int i;
618 char_u *s;
619
620 for (current_augroup = -1; current_augroup < augroups.ga_len;
621 ++current_augroup)
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200622 do_autocmd(NULL, (char_u *)"", TRUE);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100623
624 for (i = 0; i < augroups.ga_len; ++i)
625 {
626 s = ((char_u **)(augroups.ga_data))[i];
627 if (s != get_deleted_augroup())
628 vim_free(s);
629 }
630 ga_clear(&augroups);
631}
632#endif
633
634/*
635 * Return the event number for event name "start".
636 * Return NUM_EVENTS if the event name was not found.
637 * Return a pointer to the next event name in "end".
638 */
639 static event_T
640event_name2nr(char_u *start, char_u **end)
641{
642 char_u *p;
643 int i;
644 int len;
645
646 // the event name ends with end of line, '|', a blank or a comma
647 for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
648 ;
649 for (i = 0; event_names[i].name != NULL; ++i)
650 {
651 len = (int)STRLEN(event_names[i].name);
652 if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0)
653 break;
654 }
655 if (*p == ',')
656 ++p;
657 *end = p;
658 if (event_names[i].name == NULL)
659 return NUM_EVENTS;
660 return event_names[i].event;
661}
662
663/*
664 * Return the name for event "event".
665 */
666 static char_u *
667event_nr2name(event_T event)
668{
669 int i;
670
671 for (i = 0; event_names[i].name != NULL; ++i)
672 if (event_names[i].event == event)
673 return (char_u *)event_names[i].name;
674 return (char_u *)"Unknown";
675}
676
677/*
678 * Scan over the events. "*" stands for all events.
679 */
680 static char_u *
681find_end_event(
682 char_u *arg,
683 int have_group) // TRUE when group name was found
684{
685 char_u *pat;
686 char_u *p;
687
688 if (*arg == '*')
689 {
690 if (arg[1] && !VIM_ISWHITE(arg[1]))
691 {
Bram Moolenaar6d057012021-12-31 18:49:43 +0000692 semsg(_(e_illegal_character_after_star_str), arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100693 return NULL;
694 }
695 pat = arg + 1;
696 }
697 else
698 {
699 for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p)
700 {
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100701 if ((int)event_name2nr(pat, &p) >= NUM_EVENTS)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100702 {
703 if (have_group)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000704 semsg(_(e_no_such_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100705 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000706 semsg(_(e_no_such_group_or_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100707 return NULL;
708 }
709 }
710 }
711 return pat;
712}
713
714/*
715 * Return TRUE if "event" is included in 'eventignore'.
716 */
717 static int
718event_ignored(event_T event)
719{
720 char_u *p = p_ei;
721
722 while (*p != NUL)
723 {
724 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
725 return TRUE;
726 if (event_name2nr(p, &p) == event)
727 return TRUE;
728 }
729
730 return FALSE;
731}
732
733/*
734 * Return OK when the contents of p_ei is valid, FAIL otherwise.
735 */
736 int
737check_ei(void)
738{
739 char_u *p = p_ei;
740
741 while (*p)
742 {
743 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
744 {
745 p += 3;
746 if (*p == ',')
747 ++p;
748 }
749 else if (event_name2nr(p, &p) == NUM_EVENTS)
750 return FAIL;
751 }
752
753 return OK;
754}
755
756# if defined(FEAT_SYN_HL) || defined(PROTO)
757
758/*
759 * Add "what" to 'eventignore' to skip loading syntax highlighting for every
760 * buffer loaded into the window. "what" must start with a comma.
761 * Returns the old value of 'eventignore' in allocated memory.
762 */
763 char_u *
764au_event_disable(char *what)
765{
766 char_u *new_ei;
767 char_u *save_ei;
768
769 save_ei = vim_strsave(p_ei);
770 if (save_ei != NULL)
771 {
Bram Moolenaardf44a272020-06-07 20:49:05 +0200772 new_ei = vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100773 if (new_ei != NULL)
774 {
775 if (*what == ',' && *p_ei == NUL)
776 STRCPY(new_ei, what + 1);
777 else
778 STRCAT(new_ei, what);
779 set_string_option_direct((char_u *)"ei", -1, new_ei,
780 OPT_FREE, SID_NONE);
781 vim_free(new_ei);
782 }
783 }
784 return save_ei;
785}
786
787 void
788au_event_restore(char_u *old_ei)
789{
790 if (old_ei != NULL)
791 {
792 set_string_option_direct((char_u *)"ei", -1, old_ei,
793 OPT_FREE, SID_NONE);
794 vim_free(old_ei);
795 }
796}
Bram Moolenaarc667da52019-11-30 20:52:27 +0100797# endif // FEAT_SYN_HL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100798
799/*
800 * do_autocmd() -- implements the :autocmd command. Can be used in the
801 * following ways:
802 *
803 * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
804 * will be automatically executed for <event>
805 * when editing a file matching <pat>, in
806 * the current group.
807 * :autocmd <event> <pat> Show the autocommands associated with
808 * <event> and <pat>.
809 * :autocmd <event> Show the autocommands associated with
810 * <event>.
811 * :autocmd Show all autocommands.
812 * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
813 * <event> and <pat>, and add the command
814 * <cmd>, for the current group.
815 * :autocmd! <event> <pat> Remove all autocommands associated with
816 * <event> and <pat> for the current group.
817 * :autocmd! <event> Remove all autocommands associated with
818 * <event> for the current group.
819 * :autocmd! Remove ALL autocommands for the current
820 * group.
821 *
822 * Multiple events and patterns may be given separated by commas. Here are
823 * some examples:
824 * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
825 * :autocmd bufleave * set tw=79 nosmartindent ic infercase
826 *
827 * :autocmd * *.c show all autocommands for *.c files.
828 *
829 * Mostly a {group} argument can optionally appear before <event>.
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200830 * "eap" can be NULL.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100831 */
832 void
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200833do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100834{
835 char_u *arg = arg_in;
836 char_u *pat;
837 char_u *envpat = NULL;
838 char_u *cmd;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200839 int cmd_need_free = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100840 event_T event;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200841 char_u *tofree = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100842 int nested = FALSE;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200843 int once = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100844 int group;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200845 int i;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200846 int flags = 0;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100847
848 if (*arg == '|')
849 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000850 eap->nextcmd = arg + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100851 arg = (char_u *)"";
852 group = AUGROUP_ALL; // no argument, use all groups
853 }
854 else
855 {
856 /*
857 * Check for a legal group name. If not, use AUGROUP_ALL.
858 */
859 group = au_get_grouparg(&arg);
860 if (arg == NULL) // out of memory
861 return;
862 }
863
864 /*
865 * Scan over the events.
866 * If we find an illegal name, return here, don't do anything.
867 */
868 pat = find_end_event(arg, group != AUGROUP_ALL);
869 if (pat == NULL)
870 return;
871
872 pat = skipwhite(pat);
873 if (*pat == '|')
874 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000875 eap->nextcmd = pat + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100876 pat = (char_u *)"";
877 cmd = (char_u *)"";
878 }
879 else
880 {
881 /*
882 * Scan over the pattern. Put a NUL at the end.
883 */
884 cmd = pat;
885 while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
886 cmd++;
887 if (*cmd)
888 *cmd++ = NUL;
889
890 // Expand environment variables in the pattern. Set 'shellslash', we
891 // want forward slashes here.
892 if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
893 {
894#ifdef BACKSLASH_IN_FILENAME
895 int p_ssl_save = p_ssl;
896
897 p_ssl = TRUE;
898#endif
899 envpat = expand_env_save(pat);
900#ifdef BACKSLASH_IN_FILENAME
901 p_ssl = p_ssl_save;
902#endif
903 if (envpat != NULL)
904 pat = envpat;
905 }
906
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100907 cmd = skipwhite(cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200908 for (i = 0; i < 2; i++)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100909 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200910 if (*cmd != NUL)
911 {
912 // Check for "++once" flag.
913 if (STRNCMP(cmd, "++once", 6) == 0 && VIM_ISWHITE(cmd[6]))
914 {
915 if (once)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000916 semsg(_(e_duplicate_argument_str), "++once");
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200917 once = TRUE;
918 cmd = skipwhite(cmd + 6);
919 }
920
921 // Check for "++nested" flag.
922 if ((STRNCMP(cmd, "++nested", 8) == 0 && VIM_ISWHITE(cmd[8])))
923 {
924 if (nested)
Bram Moolenaarf0775142022-03-04 20:10:38 +0000925 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000926 semsg(_(e_duplicate_argument_str), "++nested");
Bram Moolenaarf0775142022-03-04 20:10:38 +0000927 return;
928 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200929 nested = TRUE;
930 cmd = skipwhite(cmd + 8);
931 }
932
Bram Moolenaarf0775142022-03-04 20:10:38 +0000933 // Check for the old "nested" flag in legacy script.
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200934 if (STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
935 {
Bram Moolenaarf0775142022-03-04 20:10:38 +0000936 if (in_vim9script())
937 {
938 // If there ever is a :nested command this error should
939 // be removed and "nested" accepted as the start of the
940 // command.
941 emsg(_(e_invalid_command_nested_did_you_mean_plusplus_nested));
942 return;
943 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200944 if (nested)
Bram Moolenaarf0775142022-03-04 20:10:38 +0000945 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000946 semsg(_(e_duplicate_argument_str), "nested");
Bram Moolenaarf0775142022-03-04 20:10:38 +0000947 return;
948 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200949 nested = TRUE;
950 cmd = skipwhite(cmd + 6);
951 }
952 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100953 }
954
955 /*
956 * Find the start of the commands.
957 * Expand <sfile> in it.
958 */
959 if (*cmd != NUL)
960 {
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200961 if (eap != NULL)
962 // Read a {} block if it follows.
963 cmd = may_get_cmd_block(eap, cmd, &tofree, &flags);
964
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100965 cmd = expand_sfile(cmd);
966 if (cmd == NULL) // some error
967 return;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200968 cmd_need_free = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100969 }
970 }
971
972 /*
973 * Print header when showing autocommands.
974 */
975 if (!forceit && *cmd == NUL)
976 // Highlight title
977 msg_puts_title(_("\n--- Autocommands ---"));
978
979 /*
980 * Loop over the events.
981 */
982 last_event = (event_T)-1; // for listing the event name
983 last_group = AUGROUP_ERROR; // for listing the group name
984 if (*arg == '*' || *arg == NUL || *arg == '|')
985 {
Bram Moolenaarb6db1462021-12-24 19:24:47 +0000986 if (*cmd != NUL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +0100987 emsg(_(e_cannot_define_autocommands_for_all_events));
988 else
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100989 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar9a046fd2021-01-28 13:47:59 +0100990 event = (event_T)((int)event + 1))
991 if (do_autocmd_event(event, pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200992 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +0100993 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100994 }
995 else
996 {
997 while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
998 if (do_autocmd_event(event_name2nr(arg, &arg), pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200999 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001000 break;
1001 }
1002
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001003 if (cmd_need_free)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001004 vim_free(cmd);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001005 vim_free(tofree);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001006 vim_free(envpat);
1007}
1008
1009/*
1010 * Find the group ID in a ":autocmd" or ":doautocmd" argument.
1011 * The "argp" argument is advanced to the following argument.
1012 *
1013 * Returns the group ID, AUGROUP_ERROR for error (out of memory).
1014 */
1015 static int
1016au_get_grouparg(char_u **argp)
1017{
1018 char_u *group_name;
1019 char_u *p;
1020 char_u *arg = *argp;
1021 int group = AUGROUP_ALL;
1022
1023 for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p)
1024 ;
1025 if (p > arg)
1026 {
Bram Moolenaardf44a272020-06-07 20:49:05 +02001027 group_name = vim_strnsave(arg, p - arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001028 if (group_name == NULL) // out of memory
1029 return AUGROUP_ERROR;
1030 group = au_find_group(group_name);
1031 if (group == AUGROUP_ERROR)
1032 group = AUGROUP_ALL; // no match, use all groups
1033 else
1034 *argp = skipwhite(p); // match, skip over group name
1035 vim_free(group_name);
1036 }
1037 return group;
1038}
1039
1040/*
1041 * do_autocmd() for one event.
1042 * If *pat == NUL do for all patterns.
1043 * If *cmd == NUL show entries.
1044 * If forceit == TRUE delete entries.
1045 * If group is not AUGROUP_ALL, only use this group.
1046 */
1047 static int
1048do_autocmd_event(
1049 event_T event,
1050 char_u *pat,
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001051 int once,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001052 int nested,
1053 char_u *cmd,
1054 int forceit,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001055 int group,
1056 int flags)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001057{
1058 AutoPat *ap;
1059 AutoPat **prev_ap;
1060 AutoCmd *ac;
1061 AutoCmd **prev_ac;
1062 int brace_level;
1063 char_u *endpat;
1064 int findgroup;
1065 int allgroups;
1066 int patlen;
1067 int is_buflocal;
1068 int buflocal_nr;
Bram Moolenaarc667da52019-11-30 20:52:27 +01001069 char_u buflocal_pat[25]; // for "<buffer=X>"
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001070
1071 if (group == AUGROUP_ALL)
1072 findgroup = current_augroup;
1073 else
1074 findgroup = group;
1075 allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
1076
1077 /*
1078 * Show or delete all patterns for an event.
1079 */
1080 if (*pat == NUL)
1081 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001082 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001083 {
1084 if (forceit) // delete the AutoPat, if it's in the current group
1085 {
1086 if (ap->group == findgroup)
1087 au_remove_pat(ap);
1088 }
1089 else if (group == AUGROUP_ALL || ap->group == group)
1090 show_autocmd(ap, event);
1091 }
1092 }
1093
1094 /*
1095 * Loop through all the specified patterns.
1096 */
1097 for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
1098 {
1099 /*
1100 * Find end of the pattern.
1101 * Watch out for a comma in braces, like "*.\{obj,o\}".
1102 */
1103 brace_level = 0;
1104 for (endpat = pat; *endpat && (*endpat != ',' || brace_level
1105 || (endpat > pat && endpat[-1] == '\\')); ++endpat)
1106 {
1107 if (*endpat == '{')
1108 brace_level++;
1109 else if (*endpat == '}')
1110 brace_level--;
1111 }
1112 if (pat == endpat) // ignore single comma
1113 continue;
1114 patlen = (int)(endpat - pat);
1115
1116 /*
1117 * detect special <buflocal[=X]> buffer-local patterns
1118 */
1119 is_buflocal = FALSE;
1120 buflocal_nr = 0;
1121
1122 if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
1123 && pat[patlen - 1] == '>')
1124 {
1125 // "<buffer...>": Error will be printed only for addition.
1126 // printing and removing will proceed silently.
1127 is_buflocal = TRUE;
1128 if (patlen == 8)
1129 // "<buffer>"
1130 buflocal_nr = curbuf->b_fnum;
1131 else if (patlen > 9 && pat[7] == '=')
1132 {
1133 if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0)
1134 // "<buffer=abuf>"
1135 buflocal_nr = autocmd_bufnr;
1136 else if (skipdigits(pat + 8) == pat + patlen - 1)
1137 // "<buffer=123>"
1138 buflocal_nr = atoi((char *)pat + 8);
1139 }
1140 }
1141
1142 if (is_buflocal)
1143 {
1144 // normalize pat into standard "<buffer>#N" form
1145 sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
1146 pat = buflocal_pat; // can modify pat and patlen
1147 patlen = (int)STRLEN(buflocal_pat); // but not endpat
1148 }
1149
1150 /*
1151 * Find AutoPat entries with this pattern. When adding a command it
1152 * always goes at or after the last one, so start at the end.
1153 */
1154 if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL)
1155 prev_ap = &last_autopat[(int)event];
1156 else
1157 prev_ap = &first_autopat[(int)event];
1158 while ((ap = *prev_ap) != NULL)
1159 {
1160 if (ap->pat != NULL)
1161 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001162 /*
1163 * Accept a pattern when:
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001164 * - a group was specified and it's that group, or a group was
1165 * not specified and it's the current group, or a group was
1166 * not specified and we are listing
1167 * - the length of the pattern matches
1168 * - the pattern matches.
1169 * For <buffer[=X]>, this condition works because we normalize
1170 * all buffer-local patterns.
1171 */
1172 if ((allgroups || ap->group == findgroup)
1173 && ap->patlen == patlen
1174 && STRNCMP(pat, ap->pat, patlen) == 0)
1175 {
1176 /*
1177 * Remove existing autocommands.
1178 * If adding any new autocmd's for this AutoPat, don't
1179 * delete the pattern from the autopat list, append to
1180 * this list.
1181 */
1182 if (forceit)
1183 {
1184 if (*cmd != NUL && ap->next == NULL)
1185 {
1186 au_remove_cmds(ap);
1187 break;
1188 }
1189 au_remove_pat(ap);
1190 }
1191
1192 /*
1193 * Show autocmd's for this autopat, or buflocals <buffer=X>
1194 */
1195 else if (*cmd == NUL)
1196 show_autocmd(ap, event);
1197
1198 /*
1199 * Add autocmd to this autopat, if it's the last one.
1200 */
1201 else if (ap->next == NULL)
1202 break;
1203 }
1204 }
1205 prev_ap = &ap->next;
1206 }
1207
1208 /*
1209 * Add a new command.
1210 */
1211 if (*cmd != NUL)
1212 {
1213 /*
1214 * If the pattern we want to add a command to does appear at the
1215 * end of the list (or not is not in the list at all), add the
1216 * pattern at the end of the list.
1217 */
1218 if (ap == NULL)
1219 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001220 // refuse to add buffer-local ap if buffer number is invalid
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001221 if (is_buflocal && (buflocal_nr == 0
1222 || buflist_findnr(buflocal_nr) == NULL))
1223 {
Bram Moolenaarf1474d82021-12-31 19:59:55 +00001224 semsg(_(e_buffer_nr_invalid_buffer_number), buflocal_nr);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001225 return FAIL;
1226 }
1227
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001228 ap = ALLOC_ONE(AutoPat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001229 if (ap == NULL)
1230 return FAIL;
1231 ap->pat = vim_strnsave(pat, patlen);
1232 ap->patlen = patlen;
1233 if (ap->pat == NULL)
1234 {
1235 vim_free(ap);
1236 return FAIL;
1237 }
1238
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001239#ifdef FEAT_EVAL
1240 // need to initialize last_mode for the first ModeChanged
1241 // autocmd
1242 if (event == EVENT_MODECHANGED && !has_modechanged())
LemonBoy2bf52dd2022-04-09 18:17:34 +01001243 get_mode(last_mode);
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001244#endif
LemonBoy09371822022-04-08 15:18:45 +01001245 // Initialize the fields checked by the WinScrolled trigger to
1246 // stop it from firing right after the first autocmd is defined.
1247 if (event == EVENT_WINSCROLLED && !has_winscrolled())
1248 {
1249 curwin->w_last_topline = curwin->w_topline;
1250 curwin->w_last_leftcol = curwin->w_leftcol;
1251 curwin->w_last_width = curwin->w_width;
1252 curwin->w_last_height = curwin->w_height;
1253 }
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001254
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001255 if (is_buflocal)
1256 {
1257 ap->buflocal_nr = buflocal_nr;
1258 ap->reg_prog = NULL;
1259 }
1260 else
1261 {
1262 char_u *reg_pat;
1263
1264 ap->buflocal_nr = 0;
1265 reg_pat = file_pat_to_reg_pat(pat, endpat,
1266 &ap->allow_dirs, TRUE);
1267 if (reg_pat != NULL)
1268 ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
1269 vim_free(reg_pat);
1270 if (reg_pat == NULL || ap->reg_prog == NULL)
1271 {
1272 vim_free(ap->pat);
1273 vim_free(ap);
1274 return FAIL;
1275 }
1276 }
1277 ap->cmds = NULL;
1278 *prev_ap = ap;
1279 last_autopat[(int)event] = ap;
1280 ap->next = NULL;
1281 if (group == AUGROUP_ALL)
1282 ap->group = current_augroup;
1283 else
1284 ap->group = group;
1285 }
1286
1287 /*
1288 * Add the autocmd at the end of the AutoCmd list.
1289 */
1290 prev_ac = &(ap->cmds);
1291 while ((ac = *prev_ac) != NULL)
1292 prev_ac = &ac->next;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001293 ac = ALLOC_ONE(AutoCmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001294 if (ac == NULL)
1295 return FAIL;
1296 ac->cmd = vim_strsave(cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001297 ac->script_ctx = current_sctx;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001298 if (flags & UC_VIM9)
1299 ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001300#ifdef FEAT_EVAL
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001301 ac->script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001302#endif
1303 if (ac->cmd == NULL)
1304 {
1305 vim_free(ac);
1306 return FAIL;
1307 }
1308 ac->next = NULL;
1309 *prev_ac = ac;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001310 ac->once = once;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001311 ac->nested = nested;
1312 }
1313 }
1314
1315 au_cleanup(); // may really delete removed patterns/commands now
1316 return OK;
1317}
1318
1319/*
1320 * Implementation of ":doautocmd [group] event [fname]".
1321 * Return OK for success, FAIL for failure;
1322 */
1323 int
1324do_doautocmd(
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001325 char_u *arg_start,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001326 int do_msg, // give message for no matching autocmds?
1327 int *did_something)
1328{
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001329 char_u *arg = arg_start;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001330 char_u *fname;
1331 int nothing_done = TRUE;
1332 int group;
1333
1334 if (did_something != NULL)
1335 *did_something = FALSE;
1336
1337 /*
1338 * Check for a legal group name. If not, use AUGROUP_ALL.
1339 */
1340 group = au_get_grouparg(&arg);
1341 if (arg == NULL) // out of memory
1342 return FAIL;
1343
1344 if (*arg == '*')
1345 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001346 emsg(_(e_cant_execute_autocommands_for_all_events));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001347 return FAIL;
1348 }
1349
1350 /*
1351 * Scan over the events.
1352 * If we find an illegal name, return here, don't do anything.
1353 */
1354 fname = find_end_event(arg, group != AUGROUP_ALL);
1355 if (fname == NULL)
1356 return FAIL;
1357
1358 fname = skipwhite(fname);
1359
1360 /*
1361 * Loop over the events.
1362 */
1363 while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
1364 if (apply_autocmds_group(event_name2nr(arg, &arg),
1365 fname, NULL, TRUE, group, curbuf, NULL))
1366 nothing_done = FALSE;
1367
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001368 if (nothing_done && do_msg
1369#ifdef FEAT_EVAL
1370 && !aborting()
1371#endif
1372 )
1373 smsg(_("No matching autocommands: %s"), arg_start);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001374 if (did_something != NULL)
1375 *did_something = !nothing_done;
1376
1377#ifdef FEAT_EVAL
1378 return aborting() ? FAIL : OK;
1379#else
1380 return OK;
1381#endif
1382}
1383
1384/*
1385 * ":doautoall": execute autocommands for each loaded buffer.
1386 */
1387 void
1388ex_doautoall(exarg_T *eap)
1389{
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001390 int retval = OK;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001391 aco_save_T aco;
1392 buf_T *buf;
1393 bufref_T bufref;
1394 char_u *arg = eap->arg;
1395 int call_do_modelines = check_nomodeline(&arg);
1396 int did_aucmd;
1397
1398 /*
1399 * This is a bit tricky: For some commands curwin->w_buffer needs to be
1400 * equal to curbuf, but for some buffers there may not be a window.
1401 * So we change the buffer for the current window for a moment. This
1402 * gives problems when the autocommands make changes to the list of
1403 * buffers or windows...
1404 */
1405 FOR_ALL_BUFFERS(buf)
1406 {
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001407 // Only do loaded buffers and skip the current buffer, it's done last.
1408 if (buf->b_ml.ml_mfp != NULL && buf != curbuf)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001409 {
1410 // find a window for this buffer and save some values
1411 aucmd_prepbuf(&aco, buf);
1412 set_bufref(&bufref, buf);
1413
1414 // execute the autocommands for this buffer
1415 retval = do_doautocmd(arg, FALSE, &did_aucmd);
1416
1417 if (call_do_modelines && did_aucmd)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001418 // Execute the modeline settings, but don't set window-local
1419 // options if we are using the current window for another
1420 // buffer.
1421 do_modelines(curwin == aucmd_win ? OPT_NOWIN : 0);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001422
1423 // restore the current window
1424 aucmd_restbuf(&aco);
1425
1426 // stop if there is some error or buffer was deleted
1427 if (retval == FAIL || !bufref_valid(&bufref))
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001428 {
1429 retval = FAIL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001430 break;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001431 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001432 }
1433 }
1434
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001435 // Execute autocommands for the current buffer last.
1436 if (retval == OK)
1437 {
1438 do_doautocmd(arg, FALSE, &did_aucmd);
1439 if (call_do_modelines && did_aucmd)
1440 do_modelines(0);
1441 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001442}
1443
1444/*
1445 * Check *argp for <nomodeline>. When it is present return FALSE, otherwise
1446 * return TRUE and advance *argp to after it.
1447 * Thus return TRUE when do_modelines() should be called.
1448 */
1449 int
1450check_nomodeline(char_u **argp)
1451{
1452 if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
1453 {
1454 *argp = skipwhite(*argp + 12);
1455 return FALSE;
1456 }
1457 return TRUE;
1458}
1459
1460/*
1461 * Prepare for executing autocommands for (hidden) buffer "buf".
1462 * Search for a visible window containing the current buffer. If there isn't
1463 * one then use "aucmd_win".
1464 * Set "curbuf" and "curwin" to match "buf".
1465 */
1466 void
1467aucmd_prepbuf(
1468 aco_save_T *aco, // structure to save values in
1469 buf_T *buf) // new curbuf
1470{
1471 win_T *win;
1472 int save_ea;
1473#ifdef FEAT_AUTOCHDIR
1474 int save_acd;
1475#endif
1476
1477 // Find a window that is for the new buffer
1478 if (buf == curbuf) // be quick when buf is curbuf
1479 win = curwin;
1480 else
1481 FOR_ALL_WINDOWS(win)
1482 if (win->w_buffer == buf)
1483 break;
1484
1485 // Allocate "aucmd_win" when needed. If this fails (out of memory) fall
1486 // back to using the current window.
1487 if (win == NULL && aucmd_win == NULL)
1488 {
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001489 aucmd_win = win_alloc_popup_win();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001490 if (aucmd_win == NULL)
1491 win = curwin;
1492 }
1493 if (win == NULL && aucmd_win_used)
1494 // Strange recursive autocommand, fall back to using the current
1495 // window. Expect a few side effects...
1496 win = curwin;
1497
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001498 aco->save_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001499 aco->save_curbuf = curbuf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001500 aco->save_prevwin_id = prevwin == NULL ? 0 : prevwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001501 if (win != NULL)
1502 {
1503 // There is a window for "buf" in the current tab page, make it the
1504 // curwin. This is preferred, it has the least side effects (esp. if
1505 // "buf" is curbuf).
1506 aco->use_aucmd_win = FALSE;
1507 curwin = win;
1508 }
1509 else
1510 {
1511 // There is no window for "buf", use "aucmd_win". To minimize the side
1512 // effects, insert it in the current tab page.
1513 // Anything related to a window (e.g., setting folds) may have
1514 // unexpected results.
1515 aco->use_aucmd_win = TRUE;
1516 aucmd_win_used = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001517
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001518 win_init_popup_win(aucmd_win, buf);
1519
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001520 aco->globaldir = globaldir;
1521 globaldir = NULL;
1522
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001523 // Split the current window, put the aucmd_win in the upper half.
1524 // We don't want the BufEnter or WinEnter autocommands.
1525 block_autocmds();
1526 make_snapshot(SNAP_AUCMD_IDX);
1527 save_ea = p_ea;
1528 p_ea = FALSE;
1529
1530#ifdef FEAT_AUTOCHDIR
1531 // Prevent chdir() call in win_enter_ext(), through do_autochdir().
1532 save_acd = p_acd;
1533 p_acd = FALSE;
1534#endif
1535
Bram Moolenaardff97e62022-01-24 20:00:55 +00001536 // no redrawing and don't set the window title
1537 ++RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001538 (void)win_split_ins(0, WSP_TOP, aucmd_win, 0);
Bram Moolenaardff97e62022-01-24 20:00:55 +00001539 --RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001540 (void)win_comp_pos(); // recompute window positions
1541 p_ea = save_ea;
1542#ifdef FEAT_AUTOCHDIR
1543 p_acd = save_acd;
1544#endif
1545 unblock_autocmds();
1546 curwin = aucmd_win;
1547 }
1548 curbuf = buf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001549 aco->new_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001550 set_bufref(&aco->new_curbuf, curbuf);
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001551
1552 // disable the Visual area, the position may be invalid in another buffer
1553 aco->save_VIsual_active = VIsual_active;
1554 VIsual_active = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001555}
1556
1557/*
1558 * Cleanup after executing autocommands for a (hidden) buffer.
1559 * Restore the window as it was (if possible).
1560 */
1561 void
1562aucmd_restbuf(
1563 aco_save_T *aco) // structure holding saved values
1564{
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001565 int dummy;
1566 win_T *save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001567
1568 if (aco->use_aucmd_win)
1569 {
1570 --curbuf->b_nwindows;
1571 // Find "aucmd_win", it can't be closed, but it may be in another tab
1572 // page. Do not trigger autocommands here.
1573 block_autocmds();
1574 if (curwin != aucmd_win)
1575 {
1576 tabpage_T *tp;
1577 win_T *wp;
1578
1579 FOR_ALL_TAB_WINDOWS(tp, wp)
1580 {
1581 if (wp == aucmd_win)
1582 {
1583 if (tp != curtab)
1584 goto_tabpage_tp(tp, TRUE, TRUE);
1585 win_goto(aucmd_win);
1586 goto win_found;
1587 }
1588 }
1589 }
1590win_found:
1591
1592 // Remove the window and frame from the tree of frames.
1593 (void)winframe_remove(curwin, &dummy, NULL);
1594 win_remove(curwin, NULL);
1595 aucmd_win_used = FALSE;
1596 last_status(FALSE); // may need to remove last status line
1597
1598 if (!valid_tabpage_win(curtab))
1599 // no valid window in current tabpage
1600 close_tabpage(curtab);
1601
1602 restore_snapshot(SNAP_AUCMD_IDX, FALSE);
1603 (void)win_comp_pos(); // recompute window positions
1604 unblock_autocmds();
1605
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001606 save_curwin = win_find_by_id(aco->save_curwin_id);
1607 if (save_curwin != NULL)
1608 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001609 else
1610 // Hmm, original window disappeared. Just use the first one.
1611 curwin = firstwin;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001612 curbuf = curwin->w_buffer;
1613#ifdef FEAT_JOB_CHANNEL
1614 // May need to restore insert mode for a prompt buffer.
1615 entering_window(curwin);
1616#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001617 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001618#ifdef FEAT_EVAL
1619 vars_clear(&aucmd_win->w_vars->dv_hashtab); // free all w: variables
1620 hash_init(&aucmd_win->w_vars->dv_hashtab); // re-use the hashtab
1621#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001622 vim_free(globaldir);
1623 globaldir = aco->globaldir;
1624
1625 // the buffer contents may have changed
1626 check_cursor();
1627 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1628 {
1629 curwin->w_topline = curbuf->b_ml.ml_line_count;
1630#ifdef FEAT_DIFF
1631 curwin->w_topfill = 0;
1632#endif
1633 }
1634#if defined(FEAT_GUI)
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001635 if (gui.in_use)
1636 {
1637 // Hide the scrollbars from the aucmd_win and update.
1638 gui_mch_enable_scrollbar(
1639 &aucmd_win->w_scrollbars[SBAR_LEFT], FALSE);
1640 gui_mch_enable_scrollbar(
1641 &aucmd_win->w_scrollbars[SBAR_RIGHT], FALSE);
1642 gui_may_update_scrollbars();
1643 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001644#endif
1645 }
1646 else
1647 {
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001648 // Restore curwin. Use the window ID, a window may have been closed
1649 // and the memory re-used for another one.
1650 save_curwin = win_find_by_id(aco->save_curwin_id);
1651 if (save_curwin != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001652 {
1653 // Restore the buffer which was previously edited by curwin, if
1654 // it was changed, we are still the same window and the buffer is
1655 // valid.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001656 if (curwin->w_id == aco->new_curwin_id
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001657 && curbuf != aco->new_curbuf.br_buf
1658 && bufref_valid(&aco->new_curbuf)
1659 && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
1660 {
1661# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
1662 if (curwin->w_s == &curbuf->b_s)
1663 curwin->w_s = &aco->new_curbuf.br_buf->b_s;
1664# endif
1665 --curbuf->b_nwindows;
1666 curbuf = aco->new_curbuf.br_buf;
1667 curwin->w_buffer = curbuf;
1668 ++curbuf->b_nwindows;
1669 }
1670
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001671 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001672 curbuf = curwin->w_buffer;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001673 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001674 // In case the autocommand moves the cursor to a position that
1675 // does not exist in curbuf.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001676 check_cursor();
1677 }
1678 }
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001679
1680 check_cursor(); // just in case lines got deleted
1681 VIsual_active = aco->save_VIsual_active;
1682 if (VIsual_active)
1683 check_pos(curbuf, &VIsual);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001684}
1685
1686static int autocmd_nested = FALSE;
1687
1688/*
1689 * Execute autocommands for "event" and file name "fname".
1690 * Return TRUE if some commands were executed.
1691 */
1692 int
1693apply_autocmds(
1694 event_T event,
1695 char_u *fname, // NULL or empty means use actual file name
1696 char_u *fname_io, // fname to use for <afile> on cmdline
1697 int force, // when TRUE, ignore autocmd_busy
1698 buf_T *buf) // buffer for <abuf>
1699{
1700 return apply_autocmds_group(event, fname, fname_io, force,
1701 AUGROUP_ALL, buf, NULL);
1702}
1703
1704/*
1705 * Like apply_autocmds(), but with extra "eap" argument. This takes care of
1706 * setting v:filearg.
1707 */
1708 int
1709apply_autocmds_exarg(
1710 event_T event,
1711 char_u *fname,
1712 char_u *fname_io,
1713 int force,
1714 buf_T *buf,
1715 exarg_T *eap)
1716{
1717 return apply_autocmds_group(event, fname, fname_io, force,
1718 AUGROUP_ALL, buf, eap);
1719}
1720
1721/*
1722 * Like apply_autocmds(), but handles the caller's retval. If the script
1723 * processing is being aborted or if retval is FAIL when inside a try
1724 * conditional, no autocommands are executed. If otherwise the autocommands
1725 * cause the script to be aborted, retval is set to FAIL.
1726 */
1727 int
1728apply_autocmds_retval(
1729 event_T event,
1730 char_u *fname, // NULL or empty means use actual file name
1731 char_u *fname_io, // fname to use for <afile> on cmdline
1732 int force, // when TRUE, ignore autocmd_busy
1733 buf_T *buf, // buffer for <abuf>
1734 int *retval) // pointer to caller's retval
1735{
1736 int did_cmd;
1737
1738#ifdef FEAT_EVAL
1739 if (should_abort(*retval))
1740 return FALSE;
1741#endif
1742
1743 did_cmd = apply_autocmds_group(event, fname, fname_io, force,
1744 AUGROUP_ALL, buf, NULL);
1745 if (did_cmd
1746#ifdef FEAT_EVAL
1747 && aborting()
1748#endif
1749 )
1750 *retval = FAIL;
1751 return did_cmd;
1752}
1753
1754/*
1755 * Return TRUE when there is a CursorHold autocommand defined.
1756 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001757 static int
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001758has_cursorhold(void)
1759{
1760 return (first_autopat[(int)(get_real_state() == NORMAL_BUSY
1761 ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
1762}
1763
1764/*
1765 * Return TRUE if the CursorHold event can be triggered.
1766 */
1767 int
1768trigger_cursorhold(void)
1769{
1770 int state;
1771
1772 if (!did_cursorhold
1773 && has_cursorhold()
1774 && reg_recording == 0
1775 && typebuf.tb_len == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001776 && !ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001777 {
1778 state = get_real_state();
1779 if (state == NORMAL_BUSY || (state & INSERT) != 0)
1780 return TRUE;
1781 }
1782 return FALSE;
1783}
1784
1785/*
LemonBoy09371822022-04-08 15:18:45 +01001786 * Return TRUE when there is a WinScrolled autocommand defined.
1787 */
1788 int
1789has_winscrolled(void)
1790{
1791 return (first_autopat[(int)EVENT_WINSCROLLED] != NULL);
1792}
1793
1794/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001795 * Return TRUE when there is a CursorMoved autocommand defined.
1796 */
1797 int
1798has_cursormoved(void)
1799{
1800 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1801}
1802
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001803/*
1804 * Return TRUE when there is a CursorMovedI autocommand defined.
1805 */
1806 int
1807has_cursormovedI(void)
1808{
1809 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1810}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001811
1812/*
1813 * Return TRUE when there is a TextChanged autocommand defined.
1814 */
1815 int
1816has_textchanged(void)
1817{
1818 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1819}
1820
1821/*
1822 * Return TRUE when there is a TextChangedI autocommand defined.
1823 */
1824 int
1825has_textchangedI(void)
1826{
1827 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
1828}
1829
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001830/*
1831 * Return TRUE when there is a TextChangedP autocommand defined.
1832 */
1833 int
1834has_textchangedP(void)
1835{
1836 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
1837}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001838
1839/*
1840 * Return TRUE when there is an InsertCharPre autocommand defined.
1841 */
1842 int
1843has_insertcharpre(void)
1844{
1845 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
1846}
1847
1848/*
1849 * Return TRUE when there is an CmdUndefined autocommand defined.
1850 */
1851 int
1852has_cmdundefined(void)
1853{
1854 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
1855}
1856
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001857#if defined(FEAT_EVAL) || defined(PROTO)
1858/*
1859 * Return TRUE when there is a TextYankPost autocommand defined.
1860 */
1861 int
1862has_textyankpost(void)
1863{
1864 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
1865}
1866#endif
1867
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001868#if defined(FEAT_EVAL) || defined(PROTO)
1869/*
1870 * Return TRUE when there is a CompleteChanged autocommand defined.
1871 */
1872 int
1873has_completechanged(void)
1874{
1875 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
1876}
1877#endif
1878
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02001879#if defined(FEAT_EVAL) || defined(PROTO)
1880/*
1881 * Return TRUE when there is a ModeChanged autocommand defined.
1882 */
1883 int
1884has_modechanged(void)
1885{
1886 return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
1887}
1888#endif
1889
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001890/*
1891 * Execute autocommands for "event" and file name "fname".
1892 * Return TRUE if some commands were executed.
1893 */
1894 static int
1895apply_autocmds_group(
1896 event_T event,
1897 char_u *fname, // NULL or empty means use actual file name
1898 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
1899 // use fname
1900 int force, // when TRUE, ignore autocmd_busy
1901 int group, // group ID, or AUGROUP_ALL
1902 buf_T *buf, // buffer for <abuf>
1903 exarg_T *eap UNUSED) // command arguments
1904{
1905 char_u *sfname = NULL; // short file name
1906 char_u *tail;
1907 int save_changed;
1908 buf_T *old_curbuf;
1909 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001910 char_u *save_autocmd_fname;
1911 int save_autocmd_fname_full;
1912 int save_autocmd_bufnr;
1913 char_u *save_autocmd_match;
1914 int save_autocmd_busy;
1915 int save_autocmd_nested;
1916 static int nesting = 0;
1917 AutoPatCmd patcmd;
1918 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001919 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001920#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001921 funccal_entry_T funccal_entry;
1922 char_u *save_cmdarg;
1923 long save_cmdbang;
1924#endif
1925 static int filechangeshell_busy = FALSE;
1926#ifdef FEAT_PROFILE
1927 proftime_T wait_time;
1928#endif
1929 int did_save_redobuff = FALSE;
1930 save_redo_T save_redo;
1931 int save_KeyTyped = KeyTyped;
ichizokc3f91c02021-12-17 09:44:33 +00001932 int save_did_emsg;
Bram Moolenaare31ee862020-01-07 20:59:34 +01001933 ESTACK_CHECK_DECLARATION
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001934
1935 /*
1936 * Quickly return if there are no autocommands for this event or
1937 * autocommands are blocked.
1938 */
1939 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
1940 || autocmd_blocked > 0)
1941 goto BYPASS_AU;
1942
1943 /*
1944 * When autocommands are busy, new autocommands are only executed when
1945 * explicitly enabled with the "nested" flag.
1946 */
1947 if (autocmd_busy && !(force || autocmd_nested))
1948 goto BYPASS_AU;
1949
1950#ifdef FEAT_EVAL
1951 /*
1952 * Quickly return when immediately aborting on error, or when an interrupt
1953 * occurred or an exception was thrown but not caught.
1954 */
1955 if (aborting())
1956 goto BYPASS_AU;
1957#endif
1958
1959 /*
1960 * FileChangedShell never nests, because it can create an endless loop.
1961 */
1962 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
1963 || event == EVENT_FILECHANGEDSHELLPOST))
1964 goto BYPASS_AU;
1965
1966 /*
1967 * Ignore events in 'eventignore'.
1968 */
1969 if (event_ignored(event))
1970 goto BYPASS_AU;
1971
1972 /*
1973 * Allow nesting of autocommands, but restrict the depth, because it's
1974 * possible to create an endless loop.
1975 */
1976 if (nesting == 10)
1977 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001978 emsg(_(e_autocommand_nesting_too_deep));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001979 goto BYPASS_AU;
1980 }
1981
1982 /*
1983 * Check if these autocommands are disabled. Used when doing ":all" or
1984 * ":ball".
1985 */
1986 if ( (autocmd_no_enter
1987 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
1988 || (autocmd_no_leave
1989 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
1990 goto BYPASS_AU;
1991
1992 /*
1993 * Save the autocmd_* variables and info about the current buffer.
1994 */
1995 save_autocmd_fname = autocmd_fname;
1996 save_autocmd_fname_full = autocmd_fname_full;
1997 save_autocmd_bufnr = autocmd_bufnr;
1998 save_autocmd_match = autocmd_match;
1999 save_autocmd_busy = autocmd_busy;
2000 save_autocmd_nested = autocmd_nested;
2001 save_changed = curbuf->b_changed;
2002 old_curbuf = curbuf;
2003
2004 /*
2005 * Set the file name to be used for <afile>.
2006 * Make a copy to avoid that changing a buffer name or directory makes it
2007 * invalid.
2008 */
2009 if (fname_io == NULL)
2010 {
2011 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002012 || event == EVENT_OPTIONSET
2013 || event == EVENT_MODECHANGED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002014 autocmd_fname = NULL;
2015 else if (fname != NULL && !ends_excmd(*fname))
2016 autocmd_fname = fname;
2017 else if (buf != NULL)
2018 autocmd_fname = buf->b_ffname;
2019 else
2020 autocmd_fname = NULL;
2021 }
2022 else
2023 autocmd_fname = fname_io;
2024 if (autocmd_fname != NULL)
2025 autocmd_fname = vim_strsave(autocmd_fname);
2026 autocmd_fname_full = FALSE; // call FullName_save() later
2027
2028 /*
2029 * Set the buffer number to be used for <abuf>.
2030 */
2031 if (buf == NULL)
2032 autocmd_bufnr = 0;
2033 else
2034 autocmd_bufnr = buf->b_fnum;
2035
2036 /*
2037 * When the file name is NULL or empty, use the file name of buffer "buf".
2038 * Always use the full path of the file name to match with, in case
2039 * "allow_dirs" is set.
2040 */
2041 if (fname == NULL || *fname == NUL)
2042 {
2043 if (buf == NULL)
2044 fname = NULL;
2045 else
2046 {
2047#ifdef FEAT_SYN_HL
2048 if (event == EVENT_SYNTAX)
2049 fname = buf->b_p_syn;
2050 else
2051#endif
2052 if (event == EVENT_FILETYPE)
2053 fname = buf->b_p_ft;
2054 else
2055 {
2056 if (buf->b_sfname != NULL)
2057 sfname = vim_strsave(buf->b_sfname);
2058 fname = buf->b_ffname;
2059 }
2060 }
2061 if (fname == NULL)
2062 fname = (char_u *)"";
2063 fname = vim_strsave(fname); // make a copy, so we can change it
2064 }
2065 else
2066 {
2067 sfname = vim_strsave(fname);
2068 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002069 // ColorScheme, QuickFixCmd*, DirChanged and similar.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002070 if (event == EVENT_FILETYPE
2071 || event == EVENT_SYNTAX
2072 || event == EVENT_CMDLINECHANGED
2073 || event == EVENT_CMDLINEENTER
2074 || event == EVENT_CMDLINELEAVE
2075 || event == EVENT_CMDWINENTER
2076 || event == EVENT_CMDWINLEAVE
2077 || event == EVENT_CMDUNDEFINED
2078 || event == EVENT_FUNCUNDEFINED
2079 || event == EVENT_REMOTEREPLY
2080 || event == EVENT_SPELLFILEMISSING
2081 || event == EVENT_QUICKFIXCMDPRE
2082 || event == EVENT_COLORSCHEME
2083 || event == EVENT_COLORSCHEMEPRE
2084 || event == EVENT_OPTIONSET
2085 || event == EVENT_QUICKFIXCMDPOST
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002086 || event == EVENT_DIRCHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002087 || event == EVENT_DIRCHANGEDPRE
naohiro ono23beefe2021-11-13 12:38:49 +00002088 || event == EVENT_MODECHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002089 || event == EVENT_USER
LemonBoy09371822022-04-08 15:18:45 +01002090 || event == EVENT_WINCLOSED
2091 || event == EVENT_WINSCROLLED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002092 {
2093 fname = vim_strsave(fname);
2094 autocmd_fname_full = TRUE; // don't expand it later
2095 }
2096 else
2097 fname = FullName_save(fname, FALSE);
2098 }
2099 if (fname == NULL) // out of memory
2100 {
2101 vim_free(sfname);
2102 retval = FALSE;
2103 goto BYPASS_AU;
2104 }
2105
2106#ifdef BACKSLASH_IN_FILENAME
2107 /*
2108 * Replace all backslashes with forward slashes. This makes the
2109 * autocommand patterns portable between Unix and MS-DOS.
2110 */
2111 if (sfname != NULL)
2112 forward_slash(sfname);
2113 forward_slash(fname);
2114#endif
2115
2116#ifdef VMS
2117 // remove version for correct match
2118 if (sfname != NULL)
2119 vms_remove_version(sfname);
2120 vms_remove_version(fname);
2121#endif
2122
2123 /*
2124 * Set the name to be used for <amatch>.
2125 */
2126 autocmd_match = fname;
2127
2128
2129 // Don't redraw while doing autocommands.
2130 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002131
2132 // name and lnum are filled in later
2133 estack_push(ETYPE_AUCMD, NULL, 0);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002134 ESTACK_CHECK_SETUP
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002135
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002136 save_current_sctx = current_sctx;
2137
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002138#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002139# ifdef FEAT_PROFILE
2140 if (do_profiling == PROF_YES)
2141 prof_child_enter(&wait_time); // doesn't count for the caller itself
2142# endif
2143
2144 // Don't use local function variables, if called from a function.
2145 save_funccal(&funccal_entry);
2146#endif
2147
2148 /*
2149 * When starting to execute autocommands, save the search patterns.
2150 */
2151 if (!autocmd_busy)
2152 {
2153 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002154 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002155 {
2156 saveRedobuff(&save_redo);
2157 did_save_redobuff = TRUE;
2158 }
2159 did_filetype = keep_filetype;
2160 }
2161
2162 /*
2163 * Note that we are applying autocmds. Some commands need to know.
2164 */
2165 autocmd_busy = TRUE;
2166 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2167 ++nesting; // see matching decrement below
2168
2169 // Remember that FileType was triggered. Used for did_filetype().
2170 if (event == EVENT_FILETYPE)
2171 did_filetype = TRUE;
2172
2173 tail = gettail(fname);
2174
2175 // Find first autocommand that matches
2176 patcmd.curpat = first_autopat[(int)event];
2177 patcmd.nextcmd = NULL;
2178 patcmd.group = group;
2179 patcmd.fname = fname;
2180 patcmd.sfname = sfname;
2181 patcmd.tail = tail;
2182 patcmd.event = event;
2183 patcmd.arg_bufnr = autocmd_bufnr;
2184 patcmd.next = NULL;
2185 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(
2366 AutoPatCmd *apc,
2367 int stop_at_last) // stop when 'last' flag is set
2368{
2369 AutoPat *ap;
2370 AutoCmd *cp;
2371 char_u *name;
2372 char *s;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002373 char_u **sourcing_namep = &SOURCING_NAME;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002374
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002375 VIM_CLEAR(*sourcing_namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002376
2377 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2378 {
2379 apc->curpat = NULL;
2380
2381 // Only use a pattern when it has not been removed, has commands and
2382 // the group matches. For buffer-local autocommands only check the
2383 // buffer number.
2384 if (ap->pat != NULL && ap->cmds != NULL
2385 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2386 {
2387 // execution-condition
2388 if (ap->buflocal_nr == 0
2389 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2390 apc->sfname, apc->tail, ap->allow_dirs))
2391 : ap->buflocal_nr == apc->arg_bufnr)
2392 {
2393 name = event_nr2name(apc->event);
2394 s = _("%s Autocommands for \"%s\"");
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002395 *sourcing_namep = alloc(STRLEN(s)
Bram Moolenaar964b3742019-05-24 18:54:09 +02002396 + STRLEN(name) + ap->patlen + 1);
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002397 if (*sourcing_namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002398 {
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002399 sprintf((char *)*sourcing_namep, s,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002400 (char *)name, (char *)ap->pat);
2401 if (p_verbose >= 8)
2402 {
2403 verbose_enter();
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002404 smsg(_("Executing %s"), *sourcing_namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002405 verbose_leave();
2406 }
2407 }
2408
2409 apc->curpat = ap;
2410 apc->nextcmd = ap->cmds;
2411 // mark last command
2412 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2413 cp->last = FALSE;
2414 cp->last = TRUE;
2415 }
2416 line_breakcheck();
2417 if (apc->curpat != NULL) // found a match
2418 break;
2419 }
2420 if (stop_at_last && ap->last)
2421 break;
2422 }
2423}
2424
2425/*
2426 * Get next autocommand command.
2427 * Called by do_cmdline() to get the next line for ":if".
2428 * Returns allocated string, or NULL for end of autocommands.
2429 */
2430 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002431getnextac(
2432 int c UNUSED,
2433 void *cookie,
2434 int indent UNUSED,
2435 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002436{
2437 AutoPatCmd *acp = (AutoPatCmd *)cookie;
2438 char_u *retval;
2439 AutoCmd *ac;
2440
2441 // Can be called again after returning the last line.
2442 if (acp->curpat == NULL)
2443 return NULL;
2444
2445 // repeat until we find an autocommand to execute
2446 for (;;)
2447 {
2448 // skip removed commands
2449 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2450 if (acp->nextcmd->last)
2451 acp->nextcmd = NULL;
2452 else
2453 acp->nextcmd = acp->nextcmd->next;
2454
2455 if (acp->nextcmd != NULL)
2456 break;
2457
2458 // at end of commands, find next pattern that matches
2459 if (acp->curpat->last)
2460 acp->curpat = NULL;
2461 else
2462 acp->curpat = acp->curpat->next;
2463 if (acp->curpat != NULL)
2464 auto_next_pat(acp, TRUE);
2465 if (acp->curpat == NULL)
2466 return NULL;
2467 }
2468
2469 ac = acp->nextcmd;
2470
2471 if (p_verbose >= 9)
2472 {
2473 verbose_enter_scroll();
2474 smsg(_("autocommand %s"), ac->cmd);
2475 msg_puts("\n"); // don't overwrite this either
2476 verbose_leave_scroll();
2477 }
2478 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002479 // Remove one-shot ("once") autocmd in anticipation of its execution.
2480 if (ac->once)
2481 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002482 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002483 current_sctx = ac->script_ctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002484 if (ac->last)
2485 acp->nextcmd = NULL;
2486 else
2487 acp->nextcmd = ac->next;
2488 return retval;
2489}
2490
2491/*
2492 * Return TRUE if there is a matching autocommand for "fname".
2493 * To account for buffer-local autocommands, function needs to know
2494 * in which buffer the file will be opened.
2495 */
2496 int
2497has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2498{
2499 AutoPat *ap;
2500 char_u *fname;
2501 char_u *tail = gettail(sfname);
2502 int retval = FALSE;
2503
2504 fname = FullName_save(sfname, FALSE);
2505 if (fname == NULL)
2506 return FALSE;
2507
2508#ifdef BACKSLASH_IN_FILENAME
2509 /*
2510 * Replace all backslashes with forward slashes. This makes the
2511 * autocommand patterns portable between Unix and MS-DOS.
2512 */
2513 sfname = vim_strsave(sfname);
2514 if (sfname != NULL)
2515 forward_slash(sfname);
2516 forward_slash(fname);
2517#endif
2518
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002519 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002520 if (ap->pat != NULL && ap->cmds != NULL
2521 && (ap->buflocal_nr == 0
2522 ? match_file_pat(NULL, &ap->reg_prog,
2523 fname, sfname, tail, ap->allow_dirs)
2524 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2525 ))
2526 {
2527 retval = TRUE;
2528 break;
2529 }
2530
2531 vim_free(fname);
2532#ifdef BACKSLASH_IN_FILENAME
2533 vim_free(sfname);
2534#endif
2535
2536 return retval;
2537}
2538
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002539/*
2540 * Function given to ExpandGeneric() to obtain the list of autocommand group
2541 * names.
2542 */
2543 char_u *
2544get_augroup_name(expand_T *xp UNUSED, int idx)
2545{
2546 if (idx == augroups.ga_len) // add "END" add the end
2547 return (char_u *)"END";
2548 if (idx >= augroups.ga_len) // end of list
2549 return NULL;
2550 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2551 // skip deleted entries
2552 return (char_u *)"";
2553 return AUGROUP_NAME(idx); // return a name
2554}
2555
2556static int include_groups = FALSE;
2557
2558 char_u *
2559set_context_in_autocmd(
2560 expand_T *xp,
2561 char_u *arg,
2562 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2563{
2564 char_u *p;
2565 int group;
2566
2567 // check for a group name, skip it if present
2568 include_groups = FALSE;
2569 p = arg;
2570 group = au_get_grouparg(&arg);
2571 if (group == AUGROUP_ERROR)
2572 return NULL;
2573 // If there only is a group name that's what we expand.
2574 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2575 {
2576 arg = p;
2577 group = AUGROUP_ALL;
2578 }
2579
2580 // skip over event name
2581 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2582 if (*p == ',')
2583 arg = p + 1;
2584 if (*p == NUL)
2585 {
2586 if (group == AUGROUP_ALL)
2587 include_groups = TRUE;
2588 xp->xp_context = EXPAND_EVENTS; // expand event name
2589 xp->xp_pattern = arg;
2590 return NULL;
2591 }
2592
2593 // skip over pattern
2594 arg = skipwhite(p);
2595 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2596 arg++;
2597 if (*arg)
2598 return arg; // expand (next) command
2599
2600 if (doautocmd)
2601 xp->xp_context = EXPAND_FILES; // expand file names
2602 else
2603 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2604 return NULL;
2605}
2606
2607/*
2608 * Function given to ExpandGeneric() to obtain the list of event names.
2609 */
2610 char_u *
2611get_event_name(expand_T *xp UNUSED, int idx)
2612{
2613 if (idx < augroups.ga_len) // First list group names, if wanted
2614 {
2615 if (!include_groups || AUGROUP_NAME(idx) == NULL
2616 || AUGROUP_NAME(idx) == get_deleted_augroup())
2617 return (char_u *)""; // skip deleted entries
2618 return AUGROUP_NAME(idx); // return a name
2619 }
2620 return (char_u *)event_names[idx - augroups.ga_len].name;
2621}
2622
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002623
2624#if defined(FEAT_EVAL) || defined(PROTO)
2625/*
2626 * Return TRUE if autocmd is supported.
2627 */
2628 int
2629autocmd_supported(char_u *name)
2630{
2631 char_u *p;
2632
2633 return (event_name2nr(name, &p) != NUM_EVENTS);
2634}
2635
2636/*
2637 * Return TRUE if an autocommand is defined for a group, event and
2638 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2639 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2640 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2641 * Used for:
2642 * exists("#Group") or
2643 * exists("#Group#Event") or
2644 * exists("#Group#Event#pat") or
2645 * exists("#Event") or
2646 * exists("#Event#pat")
2647 */
2648 int
2649au_exists(char_u *arg)
2650{
2651 char_u *arg_save;
2652 char_u *pattern = NULL;
2653 char_u *event_name;
2654 char_u *p;
2655 event_T event;
2656 AutoPat *ap;
2657 buf_T *buflocal_buf = NULL;
2658 int group;
2659 int retval = FALSE;
2660
2661 // Make a copy so that we can change the '#' chars to a NUL.
2662 arg_save = vim_strsave(arg);
2663 if (arg_save == NULL)
2664 return FALSE;
2665 p = vim_strchr(arg_save, '#');
2666 if (p != NULL)
2667 *p++ = NUL;
2668
2669 // First, look for an autocmd group name
2670 group = au_find_group(arg_save);
2671 if (group == AUGROUP_ERROR)
2672 {
2673 // Didn't match a group name, assume the first argument is an event.
2674 group = AUGROUP_ALL;
2675 event_name = arg_save;
2676 }
2677 else
2678 {
2679 if (p == NULL)
2680 {
2681 // "Group": group name is present and it's recognized
2682 retval = TRUE;
2683 goto theend;
2684 }
2685
2686 // Must be "Group#Event" or "Group#Event#pat".
2687 event_name = p;
2688 p = vim_strchr(event_name, '#');
2689 if (p != NULL)
2690 *p++ = NUL; // "Group#Event#pat"
2691 }
2692
2693 pattern = p; // "pattern" is NULL when there is no pattern
2694
2695 // find the index (enum) for the event name
2696 event = event_name2nr(event_name, &p);
2697
2698 // return FALSE if the event name is not recognized
2699 if (event == NUM_EVENTS)
2700 goto theend;
2701
2702 // Find the first autocommand for this event.
2703 // If there isn't any, return FALSE;
2704 // If there is one and no pattern given, return TRUE;
2705 ap = first_autopat[(int)event];
2706 if (ap == NULL)
2707 goto theend;
2708
2709 // if pattern is "<buffer>", special handling is needed which uses curbuf
2710 // for pattern "<buffer=N>, fnamecmp() will work fine
2711 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2712 buflocal_buf = curbuf;
2713
2714 // Check if there is an autocommand with the given pattern.
2715 for ( ; ap != NULL; ap = ap->next)
2716 // only use a pattern when it has not been removed and has commands.
2717 // For buffer-local autocommands, fnamecmp() works fine.
2718 if (ap->pat != NULL && ap->cmds != NULL
2719 && (group == AUGROUP_ALL || ap->group == group)
2720 && (pattern == NULL
2721 || (buflocal_buf == NULL
2722 ? fnamecmp(ap->pat, pattern) == 0
2723 : ap->buflocal_nr == buflocal_buf->b_fnum)))
2724 {
2725 retval = TRUE;
2726 break;
2727 }
2728
2729theend:
2730 vim_free(arg_save);
2731 return retval;
2732}
2733#endif