blob: a0065decba8da79a096daa7054ba69e261b4d194 [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())
1243 {
1244 typval_T rettv;
1245 typval_T tv[2];
1246
1247 tv[0].v_type = VAR_NUMBER;
1248 tv[0].vval.v_number = 1;
1249 tv[1].v_type = VAR_UNKNOWN;
1250 f_mode(tv, &rettv);
1251 STRCPY(last_mode, rettv.vval.v_string);
1252 vim_free(rettv.vval.v_string);
1253 }
1254#endif
LemonBoy09371822022-04-08 15:18:45 +01001255 // Initialize the fields checked by the WinScrolled trigger to
1256 // stop it from firing right after the first autocmd is defined.
1257 if (event == EVENT_WINSCROLLED && !has_winscrolled())
1258 {
1259 curwin->w_last_topline = curwin->w_topline;
1260 curwin->w_last_leftcol = curwin->w_leftcol;
1261 curwin->w_last_width = curwin->w_width;
1262 curwin->w_last_height = curwin->w_height;
1263 }
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001264
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001265 if (is_buflocal)
1266 {
1267 ap->buflocal_nr = buflocal_nr;
1268 ap->reg_prog = NULL;
1269 }
1270 else
1271 {
1272 char_u *reg_pat;
1273
1274 ap->buflocal_nr = 0;
1275 reg_pat = file_pat_to_reg_pat(pat, endpat,
1276 &ap->allow_dirs, TRUE);
1277 if (reg_pat != NULL)
1278 ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
1279 vim_free(reg_pat);
1280 if (reg_pat == NULL || ap->reg_prog == NULL)
1281 {
1282 vim_free(ap->pat);
1283 vim_free(ap);
1284 return FAIL;
1285 }
1286 }
1287 ap->cmds = NULL;
1288 *prev_ap = ap;
1289 last_autopat[(int)event] = ap;
1290 ap->next = NULL;
1291 if (group == AUGROUP_ALL)
1292 ap->group = current_augroup;
1293 else
1294 ap->group = group;
1295 }
1296
1297 /*
1298 * Add the autocmd at the end of the AutoCmd list.
1299 */
1300 prev_ac = &(ap->cmds);
1301 while ((ac = *prev_ac) != NULL)
1302 prev_ac = &ac->next;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001303 ac = ALLOC_ONE(AutoCmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001304 if (ac == NULL)
1305 return FAIL;
1306 ac->cmd = vim_strsave(cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001307 ac->script_ctx = current_sctx;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001308 if (flags & UC_VIM9)
1309 ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001310#ifdef FEAT_EVAL
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001311 ac->script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001312#endif
1313 if (ac->cmd == NULL)
1314 {
1315 vim_free(ac);
1316 return FAIL;
1317 }
1318 ac->next = NULL;
1319 *prev_ac = ac;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001320 ac->once = once;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001321 ac->nested = nested;
1322 }
1323 }
1324
1325 au_cleanup(); // may really delete removed patterns/commands now
1326 return OK;
1327}
1328
1329/*
1330 * Implementation of ":doautocmd [group] event [fname]".
1331 * Return OK for success, FAIL for failure;
1332 */
1333 int
1334do_doautocmd(
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001335 char_u *arg_start,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001336 int do_msg, // give message for no matching autocmds?
1337 int *did_something)
1338{
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001339 char_u *arg = arg_start;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001340 char_u *fname;
1341 int nothing_done = TRUE;
1342 int group;
1343
1344 if (did_something != NULL)
1345 *did_something = FALSE;
1346
1347 /*
1348 * Check for a legal group name. If not, use AUGROUP_ALL.
1349 */
1350 group = au_get_grouparg(&arg);
1351 if (arg == NULL) // out of memory
1352 return FAIL;
1353
1354 if (*arg == '*')
1355 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001356 emsg(_(e_cant_execute_autocommands_for_all_events));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001357 return FAIL;
1358 }
1359
1360 /*
1361 * Scan over the events.
1362 * If we find an illegal name, return here, don't do anything.
1363 */
1364 fname = find_end_event(arg, group != AUGROUP_ALL);
1365 if (fname == NULL)
1366 return FAIL;
1367
1368 fname = skipwhite(fname);
1369
1370 /*
1371 * Loop over the events.
1372 */
1373 while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
1374 if (apply_autocmds_group(event_name2nr(arg, &arg),
1375 fname, NULL, TRUE, group, curbuf, NULL))
1376 nothing_done = FALSE;
1377
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001378 if (nothing_done && do_msg
1379#ifdef FEAT_EVAL
1380 && !aborting()
1381#endif
1382 )
1383 smsg(_("No matching autocommands: %s"), arg_start);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001384 if (did_something != NULL)
1385 *did_something = !nothing_done;
1386
1387#ifdef FEAT_EVAL
1388 return aborting() ? FAIL : OK;
1389#else
1390 return OK;
1391#endif
1392}
1393
1394/*
1395 * ":doautoall": execute autocommands for each loaded buffer.
1396 */
1397 void
1398ex_doautoall(exarg_T *eap)
1399{
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001400 int retval = OK;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001401 aco_save_T aco;
1402 buf_T *buf;
1403 bufref_T bufref;
1404 char_u *arg = eap->arg;
1405 int call_do_modelines = check_nomodeline(&arg);
1406 int did_aucmd;
1407
1408 /*
1409 * This is a bit tricky: For some commands curwin->w_buffer needs to be
1410 * equal to curbuf, but for some buffers there may not be a window.
1411 * So we change the buffer for the current window for a moment. This
1412 * gives problems when the autocommands make changes to the list of
1413 * buffers or windows...
1414 */
1415 FOR_ALL_BUFFERS(buf)
1416 {
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001417 // Only do loaded buffers and skip the current buffer, it's done last.
1418 if (buf->b_ml.ml_mfp != NULL && buf != curbuf)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001419 {
1420 // find a window for this buffer and save some values
1421 aucmd_prepbuf(&aco, buf);
1422 set_bufref(&bufref, buf);
1423
1424 // execute the autocommands for this buffer
1425 retval = do_doautocmd(arg, FALSE, &did_aucmd);
1426
1427 if (call_do_modelines && did_aucmd)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001428 // Execute the modeline settings, but don't set window-local
1429 // options if we are using the current window for another
1430 // buffer.
1431 do_modelines(curwin == aucmd_win ? OPT_NOWIN : 0);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001432
1433 // restore the current window
1434 aucmd_restbuf(&aco);
1435
1436 // stop if there is some error or buffer was deleted
1437 if (retval == FAIL || !bufref_valid(&bufref))
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001438 {
1439 retval = FAIL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001440 break;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001441 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001442 }
1443 }
1444
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001445 // Execute autocommands for the current buffer last.
1446 if (retval == OK)
1447 {
1448 do_doautocmd(arg, FALSE, &did_aucmd);
1449 if (call_do_modelines && did_aucmd)
1450 do_modelines(0);
1451 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001452}
1453
1454/*
1455 * Check *argp for <nomodeline>. When it is present return FALSE, otherwise
1456 * return TRUE and advance *argp to after it.
1457 * Thus return TRUE when do_modelines() should be called.
1458 */
1459 int
1460check_nomodeline(char_u **argp)
1461{
1462 if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
1463 {
1464 *argp = skipwhite(*argp + 12);
1465 return FALSE;
1466 }
1467 return TRUE;
1468}
1469
1470/*
1471 * Prepare for executing autocommands for (hidden) buffer "buf".
1472 * Search for a visible window containing the current buffer. If there isn't
1473 * one then use "aucmd_win".
1474 * Set "curbuf" and "curwin" to match "buf".
1475 */
1476 void
1477aucmd_prepbuf(
1478 aco_save_T *aco, // structure to save values in
1479 buf_T *buf) // new curbuf
1480{
1481 win_T *win;
1482 int save_ea;
1483#ifdef FEAT_AUTOCHDIR
1484 int save_acd;
1485#endif
1486
1487 // Find a window that is for the new buffer
1488 if (buf == curbuf) // be quick when buf is curbuf
1489 win = curwin;
1490 else
1491 FOR_ALL_WINDOWS(win)
1492 if (win->w_buffer == buf)
1493 break;
1494
1495 // Allocate "aucmd_win" when needed. If this fails (out of memory) fall
1496 // back to using the current window.
1497 if (win == NULL && aucmd_win == NULL)
1498 {
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001499 aucmd_win = win_alloc_popup_win();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001500 if (aucmd_win == NULL)
1501 win = curwin;
1502 }
1503 if (win == NULL && aucmd_win_used)
1504 // Strange recursive autocommand, fall back to using the current
1505 // window. Expect a few side effects...
1506 win = curwin;
1507
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001508 aco->save_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001509 aco->save_curbuf = curbuf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001510 aco->save_prevwin_id = prevwin == NULL ? 0 : prevwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001511 if (win != NULL)
1512 {
1513 // There is a window for "buf" in the current tab page, make it the
1514 // curwin. This is preferred, it has the least side effects (esp. if
1515 // "buf" is curbuf).
1516 aco->use_aucmd_win = FALSE;
1517 curwin = win;
1518 }
1519 else
1520 {
1521 // There is no window for "buf", use "aucmd_win". To minimize the side
1522 // effects, insert it in the current tab page.
1523 // Anything related to a window (e.g., setting folds) may have
1524 // unexpected results.
1525 aco->use_aucmd_win = TRUE;
1526 aucmd_win_used = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001527
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001528 win_init_popup_win(aucmd_win, buf);
1529
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001530 aco->globaldir = globaldir;
1531 globaldir = NULL;
1532
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001533 // Split the current window, put the aucmd_win in the upper half.
1534 // We don't want the BufEnter or WinEnter autocommands.
1535 block_autocmds();
1536 make_snapshot(SNAP_AUCMD_IDX);
1537 save_ea = p_ea;
1538 p_ea = FALSE;
1539
1540#ifdef FEAT_AUTOCHDIR
1541 // Prevent chdir() call in win_enter_ext(), through do_autochdir().
1542 save_acd = p_acd;
1543 p_acd = FALSE;
1544#endif
1545
Bram Moolenaardff97e62022-01-24 20:00:55 +00001546 // no redrawing and don't set the window title
1547 ++RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001548 (void)win_split_ins(0, WSP_TOP, aucmd_win, 0);
Bram Moolenaardff97e62022-01-24 20:00:55 +00001549 --RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001550 (void)win_comp_pos(); // recompute window positions
1551 p_ea = save_ea;
1552#ifdef FEAT_AUTOCHDIR
1553 p_acd = save_acd;
1554#endif
1555 unblock_autocmds();
1556 curwin = aucmd_win;
1557 }
1558 curbuf = buf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001559 aco->new_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001560 set_bufref(&aco->new_curbuf, curbuf);
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001561
1562 // disable the Visual area, the position may be invalid in another buffer
1563 aco->save_VIsual_active = VIsual_active;
1564 VIsual_active = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001565}
1566
1567/*
1568 * Cleanup after executing autocommands for a (hidden) buffer.
1569 * Restore the window as it was (if possible).
1570 */
1571 void
1572aucmd_restbuf(
1573 aco_save_T *aco) // structure holding saved values
1574{
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001575 int dummy;
1576 win_T *save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001577
1578 if (aco->use_aucmd_win)
1579 {
1580 --curbuf->b_nwindows;
1581 // Find "aucmd_win", it can't be closed, but it may be in another tab
1582 // page. Do not trigger autocommands here.
1583 block_autocmds();
1584 if (curwin != aucmd_win)
1585 {
1586 tabpage_T *tp;
1587 win_T *wp;
1588
1589 FOR_ALL_TAB_WINDOWS(tp, wp)
1590 {
1591 if (wp == aucmd_win)
1592 {
1593 if (tp != curtab)
1594 goto_tabpage_tp(tp, TRUE, TRUE);
1595 win_goto(aucmd_win);
1596 goto win_found;
1597 }
1598 }
1599 }
1600win_found:
1601
1602 // Remove the window and frame from the tree of frames.
1603 (void)winframe_remove(curwin, &dummy, NULL);
1604 win_remove(curwin, NULL);
1605 aucmd_win_used = FALSE;
1606 last_status(FALSE); // may need to remove last status line
1607
1608 if (!valid_tabpage_win(curtab))
1609 // no valid window in current tabpage
1610 close_tabpage(curtab);
1611
1612 restore_snapshot(SNAP_AUCMD_IDX, FALSE);
1613 (void)win_comp_pos(); // recompute window positions
1614 unblock_autocmds();
1615
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001616 save_curwin = win_find_by_id(aco->save_curwin_id);
1617 if (save_curwin != NULL)
1618 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001619 else
1620 // Hmm, original window disappeared. Just use the first one.
1621 curwin = firstwin;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001622 curbuf = curwin->w_buffer;
1623#ifdef FEAT_JOB_CHANNEL
1624 // May need to restore insert mode for a prompt buffer.
1625 entering_window(curwin);
1626#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001627 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001628#ifdef FEAT_EVAL
1629 vars_clear(&aucmd_win->w_vars->dv_hashtab); // free all w: variables
1630 hash_init(&aucmd_win->w_vars->dv_hashtab); // re-use the hashtab
1631#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001632 vim_free(globaldir);
1633 globaldir = aco->globaldir;
1634
1635 // the buffer contents may have changed
1636 check_cursor();
1637 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1638 {
1639 curwin->w_topline = curbuf->b_ml.ml_line_count;
1640#ifdef FEAT_DIFF
1641 curwin->w_topfill = 0;
1642#endif
1643 }
1644#if defined(FEAT_GUI)
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001645 if (gui.in_use)
1646 {
1647 // Hide the scrollbars from the aucmd_win and update.
1648 gui_mch_enable_scrollbar(
1649 &aucmd_win->w_scrollbars[SBAR_LEFT], FALSE);
1650 gui_mch_enable_scrollbar(
1651 &aucmd_win->w_scrollbars[SBAR_RIGHT], FALSE);
1652 gui_may_update_scrollbars();
1653 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001654#endif
1655 }
1656 else
1657 {
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001658 // Restore curwin. Use the window ID, a window may have been closed
1659 // and the memory re-used for another one.
1660 save_curwin = win_find_by_id(aco->save_curwin_id);
1661 if (save_curwin != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001662 {
1663 // Restore the buffer which was previously edited by curwin, if
1664 // it was changed, we are still the same window and the buffer is
1665 // valid.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001666 if (curwin->w_id == aco->new_curwin_id
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001667 && curbuf != aco->new_curbuf.br_buf
1668 && bufref_valid(&aco->new_curbuf)
1669 && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
1670 {
1671# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
1672 if (curwin->w_s == &curbuf->b_s)
1673 curwin->w_s = &aco->new_curbuf.br_buf->b_s;
1674# endif
1675 --curbuf->b_nwindows;
1676 curbuf = aco->new_curbuf.br_buf;
1677 curwin->w_buffer = curbuf;
1678 ++curbuf->b_nwindows;
1679 }
1680
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001681 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001682 curbuf = curwin->w_buffer;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001683 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001684 // In case the autocommand moves the cursor to a position that
1685 // does not exist in curbuf.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001686 check_cursor();
1687 }
1688 }
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001689
1690 check_cursor(); // just in case lines got deleted
1691 VIsual_active = aco->save_VIsual_active;
1692 if (VIsual_active)
1693 check_pos(curbuf, &VIsual);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001694}
1695
1696static int autocmd_nested = FALSE;
1697
1698/*
1699 * Execute autocommands for "event" and file name "fname".
1700 * Return TRUE if some commands were executed.
1701 */
1702 int
1703apply_autocmds(
1704 event_T event,
1705 char_u *fname, // NULL or empty means use actual file name
1706 char_u *fname_io, // fname to use for <afile> on cmdline
1707 int force, // when TRUE, ignore autocmd_busy
1708 buf_T *buf) // buffer for <abuf>
1709{
1710 return apply_autocmds_group(event, fname, fname_io, force,
1711 AUGROUP_ALL, buf, NULL);
1712}
1713
1714/*
1715 * Like apply_autocmds(), but with extra "eap" argument. This takes care of
1716 * setting v:filearg.
1717 */
1718 int
1719apply_autocmds_exarg(
1720 event_T event,
1721 char_u *fname,
1722 char_u *fname_io,
1723 int force,
1724 buf_T *buf,
1725 exarg_T *eap)
1726{
1727 return apply_autocmds_group(event, fname, fname_io, force,
1728 AUGROUP_ALL, buf, eap);
1729}
1730
1731/*
1732 * Like apply_autocmds(), but handles the caller's retval. If the script
1733 * processing is being aborted or if retval is FAIL when inside a try
1734 * conditional, no autocommands are executed. If otherwise the autocommands
1735 * cause the script to be aborted, retval is set to FAIL.
1736 */
1737 int
1738apply_autocmds_retval(
1739 event_T event,
1740 char_u *fname, // NULL or empty means use actual file name
1741 char_u *fname_io, // fname to use for <afile> on cmdline
1742 int force, // when TRUE, ignore autocmd_busy
1743 buf_T *buf, // buffer for <abuf>
1744 int *retval) // pointer to caller's retval
1745{
1746 int did_cmd;
1747
1748#ifdef FEAT_EVAL
1749 if (should_abort(*retval))
1750 return FALSE;
1751#endif
1752
1753 did_cmd = apply_autocmds_group(event, fname, fname_io, force,
1754 AUGROUP_ALL, buf, NULL);
1755 if (did_cmd
1756#ifdef FEAT_EVAL
1757 && aborting()
1758#endif
1759 )
1760 *retval = FAIL;
1761 return did_cmd;
1762}
1763
1764/*
1765 * Return TRUE when there is a CursorHold autocommand defined.
1766 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001767 static int
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001768has_cursorhold(void)
1769{
1770 return (first_autopat[(int)(get_real_state() == NORMAL_BUSY
1771 ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
1772}
1773
1774/*
1775 * Return TRUE if the CursorHold event can be triggered.
1776 */
1777 int
1778trigger_cursorhold(void)
1779{
1780 int state;
1781
1782 if (!did_cursorhold
1783 && has_cursorhold()
1784 && reg_recording == 0
1785 && typebuf.tb_len == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001786 && !ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001787 {
1788 state = get_real_state();
1789 if (state == NORMAL_BUSY || (state & INSERT) != 0)
1790 return TRUE;
1791 }
1792 return FALSE;
1793}
1794
1795/*
LemonBoy09371822022-04-08 15:18:45 +01001796 * Return TRUE when there is a WinScrolled autocommand defined.
1797 */
1798 int
1799has_winscrolled(void)
1800{
1801 return (first_autopat[(int)EVENT_WINSCROLLED] != NULL);
1802}
1803
1804/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001805 * Return TRUE when there is a CursorMoved autocommand defined.
1806 */
1807 int
1808has_cursormoved(void)
1809{
1810 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1811}
1812
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001813/*
1814 * Return TRUE when there is a CursorMovedI autocommand defined.
1815 */
1816 int
1817has_cursormovedI(void)
1818{
1819 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1820}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001821
1822/*
1823 * Return TRUE when there is a TextChanged autocommand defined.
1824 */
1825 int
1826has_textchanged(void)
1827{
1828 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1829}
1830
1831/*
1832 * Return TRUE when there is a TextChangedI autocommand defined.
1833 */
1834 int
1835has_textchangedI(void)
1836{
1837 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
1838}
1839
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001840/*
1841 * Return TRUE when there is a TextChangedP autocommand defined.
1842 */
1843 int
1844has_textchangedP(void)
1845{
1846 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
1847}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001848
1849/*
1850 * Return TRUE when there is an InsertCharPre autocommand defined.
1851 */
1852 int
1853has_insertcharpre(void)
1854{
1855 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
1856}
1857
1858/*
1859 * Return TRUE when there is an CmdUndefined autocommand defined.
1860 */
1861 int
1862has_cmdundefined(void)
1863{
1864 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
1865}
1866
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001867#if defined(FEAT_EVAL) || defined(PROTO)
1868/*
1869 * Return TRUE when there is a TextYankPost autocommand defined.
1870 */
1871 int
1872has_textyankpost(void)
1873{
1874 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
1875}
1876#endif
1877
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001878#if defined(FEAT_EVAL) || defined(PROTO)
1879/*
1880 * Return TRUE when there is a CompleteChanged autocommand defined.
1881 */
1882 int
1883has_completechanged(void)
1884{
1885 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
1886}
1887#endif
1888
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02001889#if defined(FEAT_EVAL) || defined(PROTO)
1890/*
1891 * Return TRUE when there is a ModeChanged autocommand defined.
1892 */
1893 int
1894has_modechanged(void)
1895{
1896 return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
1897}
1898#endif
1899
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001900/*
1901 * Execute autocommands for "event" and file name "fname".
1902 * Return TRUE if some commands were executed.
1903 */
1904 static int
1905apply_autocmds_group(
1906 event_T event,
1907 char_u *fname, // NULL or empty means use actual file name
1908 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
1909 // use fname
1910 int force, // when TRUE, ignore autocmd_busy
1911 int group, // group ID, or AUGROUP_ALL
1912 buf_T *buf, // buffer for <abuf>
1913 exarg_T *eap UNUSED) // command arguments
1914{
1915 char_u *sfname = NULL; // short file name
1916 char_u *tail;
1917 int save_changed;
1918 buf_T *old_curbuf;
1919 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001920 char_u *save_autocmd_fname;
1921 int save_autocmd_fname_full;
1922 int save_autocmd_bufnr;
1923 char_u *save_autocmd_match;
1924 int save_autocmd_busy;
1925 int save_autocmd_nested;
1926 static int nesting = 0;
1927 AutoPatCmd patcmd;
1928 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001929 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001930#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001931 funccal_entry_T funccal_entry;
1932 char_u *save_cmdarg;
1933 long save_cmdbang;
1934#endif
1935 static int filechangeshell_busy = FALSE;
1936#ifdef FEAT_PROFILE
1937 proftime_T wait_time;
1938#endif
1939 int did_save_redobuff = FALSE;
1940 save_redo_T save_redo;
1941 int save_KeyTyped = KeyTyped;
ichizokc3f91c02021-12-17 09:44:33 +00001942 int save_did_emsg;
Bram Moolenaare31ee862020-01-07 20:59:34 +01001943 ESTACK_CHECK_DECLARATION
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001944
1945 /*
1946 * Quickly return if there are no autocommands for this event or
1947 * autocommands are blocked.
1948 */
1949 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
1950 || autocmd_blocked > 0)
1951 goto BYPASS_AU;
1952
1953 /*
1954 * When autocommands are busy, new autocommands are only executed when
1955 * explicitly enabled with the "nested" flag.
1956 */
1957 if (autocmd_busy && !(force || autocmd_nested))
1958 goto BYPASS_AU;
1959
1960#ifdef FEAT_EVAL
1961 /*
1962 * Quickly return when immediately aborting on error, or when an interrupt
1963 * occurred or an exception was thrown but not caught.
1964 */
1965 if (aborting())
1966 goto BYPASS_AU;
1967#endif
1968
1969 /*
1970 * FileChangedShell never nests, because it can create an endless loop.
1971 */
1972 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
1973 || event == EVENT_FILECHANGEDSHELLPOST))
1974 goto BYPASS_AU;
1975
1976 /*
1977 * Ignore events in 'eventignore'.
1978 */
1979 if (event_ignored(event))
1980 goto BYPASS_AU;
1981
1982 /*
1983 * Allow nesting of autocommands, but restrict the depth, because it's
1984 * possible to create an endless loop.
1985 */
1986 if (nesting == 10)
1987 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001988 emsg(_(e_autocommand_nesting_too_deep));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001989 goto BYPASS_AU;
1990 }
1991
1992 /*
1993 * Check if these autocommands are disabled. Used when doing ":all" or
1994 * ":ball".
1995 */
1996 if ( (autocmd_no_enter
1997 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
1998 || (autocmd_no_leave
1999 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
2000 goto BYPASS_AU;
2001
2002 /*
2003 * Save the autocmd_* variables and info about the current buffer.
2004 */
2005 save_autocmd_fname = autocmd_fname;
2006 save_autocmd_fname_full = autocmd_fname_full;
2007 save_autocmd_bufnr = autocmd_bufnr;
2008 save_autocmd_match = autocmd_match;
2009 save_autocmd_busy = autocmd_busy;
2010 save_autocmd_nested = autocmd_nested;
2011 save_changed = curbuf->b_changed;
2012 old_curbuf = curbuf;
2013
2014 /*
2015 * Set the file name to be used for <afile>.
2016 * Make a copy to avoid that changing a buffer name or directory makes it
2017 * invalid.
2018 */
2019 if (fname_io == NULL)
2020 {
2021 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002022 || event == EVENT_OPTIONSET
2023 || event == EVENT_MODECHANGED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002024 autocmd_fname = NULL;
2025 else if (fname != NULL && !ends_excmd(*fname))
2026 autocmd_fname = fname;
2027 else if (buf != NULL)
2028 autocmd_fname = buf->b_ffname;
2029 else
2030 autocmd_fname = NULL;
2031 }
2032 else
2033 autocmd_fname = fname_io;
2034 if (autocmd_fname != NULL)
2035 autocmd_fname = vim_strsave(autocmd_fname);
2036 autocmd_fname_full = FALSE; // call FullName_save() later
2037
2038 /*
2039 * Set the buffer number to be used for <abuf>.
2040 */
2041 if (buf == NULL)
2042 autocmd_bufnr = 0;
2043 else
2044 autocmd_bufnr = buf->b_fnum;
2045
2046 /*
2047 * When the file name is NULL or empty, use the file name of buffer "buf".
2048 * Always use the full path of the file name to match with, in case
2049 * "allow_dirs" is set.
2050 */
2051 if (fname == NULL || *fname == NUL)
2052 {
2053 if (buf == NULL)
2054 fname = NULL;
2055 else
2056 {
2057#ifdef FEAT_SYN_HL
2058 if (event == EVENT_SYNTAX)
2059 fname = buf->b_p_syn;
2060 else
2061#endif
2062 if (event == EVENT_FILETYPE)
2063 fname = buf->b_p_ft;
2064 else
2065 {
2066 if (buf->b_sfname != NULL)
2067 sfname = vim_strsave(buf->b_sfname);
2068 fname = buf->b_ffname;
2069 }
2070 }
2071 if (fname == NULL)
2072 fname = (char_u *)"";
2073 fname = vim_strsave(fname); // make a copy, so we can change it
2074 }
2075 else
2076 {
2077 sfname = vim_strsave(fname);
2078 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002079 // ColorScheme, QuickFixCmd*, DirChanged and similar.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002080 if (event == EVENT_FILETYPE
2081 || event == EVENT_SYNTAX
2082 || event == EVENT_CMDLINECHANGED
2083 || event == EVENT_CMDLINEENTER
2084 || event == EVENT_CMDLINELEAVE
2085 || event == EVENT_CMDWINENTER
2086 || event == EVENT_CMDWINLEAVE
2087 || event == EVENT_CMDUNDEFINED
2088 || event == EVENT_FUNCUNDEFINED
2089 || event == EVENT_REMOTEREPLY
2090 || event == EVENT_SPELLFILEMISSING
2091 || event == EVENT_QUICKFIXCMDPRE
2092 || event == EVENT_COLORSCHEME
2093 || event == EVENT_COLORSCHEMEPRE
2094 || event == EVENT_OPTIONSET
2095 || event == EVENT_QUICKFIXCMDPOST
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002096 || event == EVENT_DIRCHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002097 || event == EVENT_DIRCHANGEDPRE
naohiro ono23beefe2021-11-13 12:38:49 +00002098 || event == EVENT_MODECHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002099 || event == EVENT_USER
LemonBoy09371822022-04-08 15:18:45 +01002100 || event == EVENT_WINCLOSED
2101 || event == EVENT_WINSCROLLED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002102 {
2103 fname = vim_strsave(fname);
2104 autocmd_fname_full = TRUE; // don't expand it later
2105 }
2106 else
2107 fname = FullName_save(fname, FALSE);
2108 }
2109 if (fname == NULL) // out of memory
2110 {
2111 vim_free(sfname);
2112 retval = FALSE;
2113 goto BYPASS_AU;
2114 }
2115
2116#ifdef BACKSLASH_IN_FILENAME
2117 /*
2118 * Replace all backslashes with forward slashes. This makes the
2119 * autocommand patterns portable between Unix and MS-DOS.
2120 */
2121 if (sfname != NULL)
2122 forward_slash(sfname);
2123 forward_slash(fname);
2124#endif
2125
2126#ifdef VMS
2127 // remove version for correct match
2128 if (sfname != NULL)
2129 vms_remove_version(sfname);
2130 vms_remove_version(fname);
2131#endif
2132
2133 /*
2134 * Set the name to be used for <amatch>.
2135 */
2136 autocmd_match = fname;
2137
2138
2139 // Don't redraw while doing autocommands.
2140 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002141
2142 // name and lnum are filled in later
2143 estack_push(ETYPE_AUCMD, NULL, 0);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002144 ESTACK_CHECK_SETUP
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002145
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002146 save_current_sctx = current_sctx;
2147
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002148#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002149# ifdef FEAT_PROFILE
2150 if (do_profiling == PROF_YES)
2151 prof_child_enter(&wait_time); // doesn't count for the caller itself
2152# endif
2153
2154 // Don't use local function variables, if called from a function.
2155 save_funccal(&funccal_entry);
2156#endif
2157
2158 /*
2159 * When starting to execute autocommands, save the search patterns.
2160 */
2161 if (!autocmd_busy)
2162 {
2163 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002164 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002165 {
2166 saveRedobuff(&save_redo);
2167 did_save_redobuff = TRUE;
2168 }
2169 did_filetype = keep_filetype;
2170 }
2171
2172 /*
2173 * Note that we are applying autocmds. Some commands need to know.
2174 */
2175 autocmd_busy = TRUE;
2176 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2177 ++nesting; // see matching decrement below
2178
2179 // Remember that FileType was triggered. Used for did_filetype().
2180 if (event == EVENT_FILETYPE)
2181 did_filetype = TRUE;
2182
2183 tail = gettail(fname);
2184
2185 // Find first autocommand that matches
2186 patcmd.curpat = first_autopat[(int)event];
2187 patcmd.nextcmd = NULL;
2188 patcmd.group = group;
2189 patcmd.fname = fname;
2190 patcmd.sfname = sfname;
2191 patcmd.tail = tail;
2192 patcmd.event = event;
2193 patcmd.arg_bufnr = autocmd_bufnr;
2194 patcmd.next = NULL;
2195 auto_next_pat(&patcmd, FALSE);
2196
2197 // found one, start executing the autocommands
2198 if (patcmd.curpat != NULL)
2199 {
2200 // add to active_apc_list
2201 patcmd.next = active_apc_list;
2202 active_apc_list = &patcmd;
2203
2204#ifdef FEAT_EVAL
2205 // set v:cmdarg (only when there is a matching pattern)
2206 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2207 if (eap != NULL)
2208 {
2209 save_cmdarg = set_cmdarg(eap, NULL);
2210 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2211 }
2212 else
2213 save_cmdarg = NULL; // avoid gcc warning
2214#endif
2215 retval = TRUE;
2216 // mark the last pattern, to avoid an endless loop when more patterns
2217 // are added when executing autocommands
2218 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2219 ap->last = FALSE;
2220 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002221
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002222 if (nesting == 1)
2223 // make sure cursor and topline are valid
2224 check_lnums(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002225
ichizokc3f91c02021-12-17 09:44:33 +00002226 save_did_emsg = did_emsg;
2227
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002228 do_cmdline(NULL, getnextac, (void *)&patcmd,
2229 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002230
ichizokc3f91c02021-12-17 09:44:33 +00002231 did_emsg += save_did_emsg;
2232
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002233 if (nesting == 1)
2234 // restore cursor and topline, unless they were changed
2235 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002236
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002237#ifdef FEAT_EVAL
2238 if (eap != NULL)
2239 {
2240 (void)set_cmdarg(NULL, save_cmdarg);
2241 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2242 }
2243#endif
2244 // delete from active_apc_list
2245 if (active_apc_list == &patcmd) // just in case
2246 active_apc_list = patcmd.next;
2247 }
2248
2249 --RedrawingDisabled;
2250 autocmd_busy = save_autocmd_busy;
2251 filechangeshell_busy = FALSE;
2252 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002253 vim_free(SOURCING_NAME);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002254 ESTACK_CHECK_NOW
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002255 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002256 vim_free(autocmd_fname);
2257 autocmd_fname = save_autocmd_fname;
2258 autocmd_fname_full = save_autocmd_fname_full;
2259 autocmd_bufnr = save_autocmd_bufnr;
2260 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002261 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002262#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002263 restore_funccal();
2264# ifdef FEAT_PROFILE
2265 if (do_profiling == PROF_YES)
2266 prof_child_exit(&wait_time);
2267# endif
2268#endif
2269 KeyTyped = save_KeyTyped;
2270 vim_free(fname);
2271 vim_free(sfname);
2272 --nesting; // see matching increment above
2273
2274 /*
2275 * When stopping to execute autocommands, restore the search patterns and
2276 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2277 * free any windows in the au_pending_free_win list.
2278 */
2279 if (!autocmd_busy)
2280 {
2281 restore_search_patterns();
2282 if (did_save_redobuff)
2283 restoreRedobuff(&save_redo);
2284 did_filetype = FALSE;
2285 while (au_pending_free_buf != NULL)
2286 {
2287 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002288
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002289 vim_free(au_pending_free_buf);
2290 au_pending_free_buf = b;
2291 }
2292 while (au_pending_free_win != NULL)
2293 {
2294 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002295
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002296 vim_free(au_pending_free_win);
2297 au_pending_free_win = w;
2298 }
2299 }
2300
2301 /*
2302 * Some events don't set or reset the Changed flag.
2303 * Check if still in the same buffer!
2304 */
2305 if (curbuf == old_curbuf
2306 && (event == EVENT_BUFREADPOST
2307 || event == EVENT_BUFWRITEPOST
2308 || event == EVENT_FILEAPPENDPOST
2309 || event == EVENT_VIMLEAVE
2310 || event == EVENT_VIMLEAVEPRE))
2311 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002312 if (curbuf->b_changed != save_changed)
2313 need_maketitle = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002314 curbuf->b_changed = save_changed;
2315 }
2316
2317 au_cleanup(); // may really delete removed patterns/commands now
2318
2319BYPASS_AU:
2320 // When wiping out a buffer make sure all its buffer-local autocommands
2321 // are deleted.
2322 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2323 aubuflocal_remove(buf);
2324
2325 if (retval == OK && event == EVENT_FILETYPE)
2326 au_did_filetype = TRUE;
2327
2328 return retval;
2329}
2330
2331# ifdef FEAT_EVAL
2332static char_u *old_termresponse = NULL;
2333# endif
2334
2335/*
2336 * Block triggering autocommands until unblock_autocmd() is called.
2337 * Can be used recursively, so long as it's symmetric.
2338 */
2339 void
2340block_autocmds(void)
2341{
2342# ifdef FEAT_EVAL
2343 // Remember the value of v:termresponse.
2344 if (autocmd_blocked == 0)
2345 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
2346# endif
2347 ++autocmd_blocked;
2348}
2349
2350 void
2351unblock_autocmds(void)
2352{
2353 --autocmd_blocked;
2354
2355# ifdef FEAT_EVAL
2356 // When v:termresponse was set while autocommands were blocked, trigger
2357 // the autocommands now. Esp. useful when executing a shell command
2358 // during startup (vimdiff).
2359 if (autocmd_blocked == 0
2360 && get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2361 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2362# endif
2363}
2364
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002365 int
2366is_autocmd_blocked(void)
2367{
2368 return autocmd_blocked != 0;
2369}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002370
2371/*
2372 * Find next autocommand pattern that matches.
2373 */
2374 static void
2375auto_next_pat(
2376 AutoPatCmd *apc,
2377 int stop_at_last) // stop when 'last' flag is set
2378{
2379 AutoPat *ap;
2380 AutoCmd *cp;
2381 char_u *name;
2382 char *s;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002383 char_u **sourcing_namep = &SOURCING_NAME;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002384
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002385 VIM_CLEAR(*sourcing_namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002386
2387 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2388 {
2389 apc->curpat = NULL;
2390
2391 // Only use a pattern when it has not been removed, has commands and
2392 // the group matches. For buffer-local autocommands only check the
2393 // buffer number.
2394 if (ap->pat != NULL && ap->cmds != NULL
2395 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2396 {
2397 // execution-condition
2398 if (ap->buflocal_nr == 0
2399 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2400 apc->sfname, apc->tail, ap->allow_dirs))
2401 : ap->buflocal_nr == apc->arg_bufnr)
2402 {
2403 name = event_nr2name(apc->event);
2404 s = _("%s Autocommands for \"%s\"");
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002405 *sourcing_namep = alloc(STRLEN(s)
Bram Moolenaar964b3742019-05-24 18:54:09 +02002406 + STRLEN(name) + ap->patlen + 1);
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002407 if (*sourcing_namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002408 {
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002409 sprintf((char *)*sourcing_namep, s,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002410 (char *)name, (char *)ap->pat);
2411 if (p_verbose >= 8)
2412 {
2413 verbose_enter();
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002414 smsg(_("Executing %s"), *sourcing_namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002415 verbose_leave();
2416 }
2417 }
2418
2419 apc->curpat = ap;
2420 apc->nextcmd = ap->cmds;
2421 // mark last command
2422 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2423 cp->last = FALSE;
2424 cp->last = TRUE;
2425 }
2426 line_breakcheck();
2427 if (apc->curpat != NULL) // found a match
2428 break;
2429 }
2430 if (stop_at_last && ap->last)
2431 break;
2432 }
2433}
2434
2435/*
2436 * Get next autocommand command.
2437 * Called by do_cmdline() to get the next line for ":if".
2438 * Returns allocated string, or NULL for end of autocommands.
2439 */
2440 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002441getnextac(
2442 int c UNUSED,
2443 void *cookie,
2444 int indent UNUSED,
2445 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002446{
2447 AutoPatCmd *acp = (AutoPatCmd *)cookie;
2448 char_u *retval;
2449 AutoCmd *ac;
2450
2451 // Can be called again after returning the last line.
2452 if (acp->curpat == NULL)
2453 return NULL;
2454
2455 // repeat until we find an autocommand to execute
2456 for (;;)
2457 {
2458 // skip removed commands
2459 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2460 if (acp->nextcmd->last)
2461 acp->nextcmd = NULL;
2462 else
2463 acp->nextcmd = acp->nextcmd->next;
2464
2465 if (acp->nextcmd != NULL)
2466 break;
2467
2468 // at end of commands, find next pattern that matches
2469 if (acp->curpat->last)
2470 acp->curpat = NULL;
2471 else
2472 acp->curpat = acp->curpat->next;
2473 if (acp->curpat != NULL)
2474 auto_next_pat(acp, TRUE);
2475 if (acp->curpat == NULL)
2476 return NULL;
2477 }
2478
2479 ac = acp->nextcmd;
2480
2481 if (p_verbose >= 9)
2482 {
2483 verbose_enter_scroll();
2484 smsg(_("autocommand %s"), ac->cmd);
2485 msg_puts("\n"); // don't overwrite this either
2486 verbose_leave_scroll();
2487 }
2488 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002489 // Remove one-shot ("once") autocmd in anticipation of its execution.
2490 if (ac->once)
2491 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002492 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002493 current_sctx = ac->script_ctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002494 if (ac->last)
2495 acp->nextcmd = NULL;
2496 else
2497 acp->nextcmd = ac->next;
2498 return retval;
2499}
2500
2501/*
2502 * Return TRUE if there is a matching autocommand for "fname".
2503 * To account for buffer-local autocommands, function needs to know
2504 * in which buffer the file will be opened.
2505 */
2506 int
2507has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2508{
2509 AutoPat *ap;
2510 char_u *fname;
2511 char_u *tail = gettail(sfname);
2512 int retval = FALSE;
2513
2514 fname = FullName_save(sfname, FALSE);
2515 if (fname == NULL)
2516 return FALSE;
2517
2518#ifdef BACKSLASH_IN_FILENAME
2519 /*
2520 * Replace all backslashes with forward slashes. This makes the
2521 * autocommand patterns portable between Unix and MS-DOS.
2522 */
2523 sfname = vim_strsave(sfname);
2524 if (sfname != NULL)
2525 forward_slash(sfname);
2526 forward_slash(fname);
2527#endif
2528
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002529 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002530 if (ap->pat != NULL && ap->cmds != NULL
2531 && (ap->buflocal_nr == 0
2532 ? match_file_pat(NULL, &ap->reg_prog,
2533 fname, sfname, tail, ap->allow_dirs)
2534 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2535 ))
2536 {
2537 retval = TRUE;
2538 break;
2539 }
2540
2541 vim_free(fname);
2542#ifdef BACKSLASH_IN_FILENAME
2543 vim_free(sfname);
2544#endif
2545
2546 return retval;
2547}
2548
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002549/*
2550 * Function given to ExpandGeneric() to obtain the list of autocommand group
2551 * names.
2552 */
2553 char_u *
2554get_augroup_name(expand_T *xp UNUSED, int idx)
2555{
2556 if (idx == augroups.ga_len) // add "END" add the end
2557 return (char_u *)"END";
2558 if (idx >= augroups.ga_len) // end of list
2559 return NULL;
2560 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2561 // skip deleted entries
2562 return (char_u *)"";
2563 return AUGROUP_NAME(idx); // return a name
2564}
2565
2566static int include_groups = FALSE;
2567
2568 char_u *
2569set_context_in_autocmd(
2570 expand_T *xp,
2571 char_u *arg,
2572 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2573{
2574 char_u *p;
2575 int group;
2576
2577 // check for a group name, skip it if present
2578 include_groups = FALSE;
2579 p = arg;
2580 group = au_get_grouparg(&arg);
2581 if (group == AUGROUP_ERROR)
2582 return NULL;
2583 // If there only is a group name that's what we expand.
2584 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2585 {
2586 arg = p;
2587 group = AUGROUP_ALL;
2588 }
2589
2590 // skip over event name
2591 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2592 if (*p == ',')
2593 arg = p + 1;
2594 if (*p == NUL)
2595 {
2596 if (group == AUGROUP_ALL)
2597 include_groups = TRUE;
2598 xp->xp_context = EXPAND_EVENTS; // expand event name
2599 xp->xp_pattern = arg;
2600 return NULL;
2601 }
2602
2603 // skip over pattern
2604 arg = skipwhite(p);
2605 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2606 arg++;
2607 if (*arg)
2608 return arg; // expand (next) command
2609
2610 if (doautocmd)
2611 xp->xp_context = EXPAND_FILES; // expand file names
2612 else
2613 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2614 return NULL;
2615}
2616
2617/*
2618 * Function given to ExpandGeneric() to obtain the list of event names.
2619 */
2620 char_u *
2621get_event_name(expand_T *xp UNUSED, int idx)
2622{
2623 if (idx < augroups.ga_len) // First list group names, if wanted
2624 {
2625 if (!include_groups || AUGROUP_NAME(idx) == NULL
2626 || AUGROUP_NAME(idx) == get_deleted_augroup())
2627 return (char_u *)""; // skip deleted entries
2628 return AUGROUP_NAME(idx); // return a name
2629 }
2630 return (char_u *)event_names[idx - augroups.ga_len].name;
2631}
2632
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002633
2634#if defined(FEAT_EVAL) || defined(PROTO)
2635/*
2636 * Return TRUE if autocmd is supported.
2637 */
2638 int
2639autocmd_supported(char_u *name)
2640{
2641 char_u *p;
2642
2643 return (event_name2nr(name, &p) != NUM_EVENTS);
2644}
2645
2646/*
2647 * Return TRUE if an autocommand is defined for a group, event and
2648 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2649 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2650 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2651 * Used for:
2652 * exists("#Group") or
2653 * exists("#Group#Event") or
2654 * exists("#Group#Event#pat") or
2655 * exists("#Event") or
2656 * exists("#Event#pat")
2657 */
2658 int
2659au_exists(char_u *arg)
2660{
2661 char_u *arg_save;
2662 char_u *pattern = NULL;
2663 char_u *event_name;
2664 char_u *p;
2665 event_T event;
2666 AutoPat *ap;
2667 buf_T *buflocal_buf = NULL;
2668 int group;
2669 int retval = FALSE;
2670
2671 // Make a copy so that we can change the '#' chars to a NUL.
2672 arg_save = vim_strsave(arg);
2673 if (arg_save == NULL)
2674 return FALSE;
2675 p = vim_strchr(arg_save, '#');
2676 if (p != NULL)
2677 *p++ = NUL;
2678
2679 // First, look for an autocmd group name
2680 group = au_find_group(arg_save);
2681 if (group == AUGROUP_ERROR)
2682 {
2683 // Didn't match a group name, assume the first argument is an event.
2684 group = AUGROUP_ALL;
2685 event_name = arg_save;
2686 }
2687 else
2688 {
2689 if (p == NULL)
2690 {
2691 // "Group": group name is present and it's recognized
2692 retval = TRUE;
2693 goto theend;
2694 }
2695
2696 // Must be "Group#Event" or "Group#Event#pat".
2697 event_name = p;
2698 p = vim_strchr(event_name, '#');
2699 if (p != NULL)
2700 *p++ = NUL; // "Group#Event#pat"
2701 }
2702
2703 pattern = p; // "pattern" is NULL when there is no pattern
2704
2705 // find the index (enum) for the event name
2706 event = event_name2nr(event_name, &p);
2707
2708 // return FALSE if the event name is not recognized
2709 if (event == NUM_EVENTS)
2710 goto theend;
2711
2712 // Find the first autocommand for this event.
2713 // If there isn't any, return FALSE;
2714 // If there is one and no pattern given, return TRUE;
2715 ap = first_autopat[(int)event];
2716 if (ap == NULL)
2717 goto theend;
2718
2719 // if pattern is "<buffer>", special handling is needed which uses curbuf
2720 // for pattern "<buffer=N>, fnamecmp() will work fine
2721 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2722 buflocal_buf = curbuf;
2723
2724 // Check if there is an autocommand with the given pattern.
2725 for ( ; ap != NULL; ap = ap->next)
2726 // only use a pattern when it has not been removed and has commands.
2727 // For buffer-local autocommands, fnamecmp() works fine.
2728 if (ap->pat != NULL && ap->cmds != NULL
2729 && (group == AUGROUP_ALL || ap->group == group)
2730 && (pattern == NULL
2731 || (buflocal_buf == NULL
2732 ? fnamecmp(ap->pat, pattern) == 0
2733 : ap->buflocal_nr == buflocal_buf->b_fnum)))
2734 {
2735 retval = TRUE;
2736 break;
2737 }
2738
2739theend:
2740 vim_free(arg_save);
2741 return retval;
2742}
2743#endif