blob: 7eb08c85b953b3fac3d735e9fc22fd3c4365db6b [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},
193 {"VimResized", EVENT_VIMRESIZED},
194 {"TextYankPost", EVENT_TEXTYANKPOST},
Bram Moolenaar100118c2020-12-11 19:30:34 +0100195 {"VimSuspend", EVENT_VIMSUSPEND},
196 {"VimResume", EVENT_VIMRESUME},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100197 {NULL, (event_T)0}
198};
199
200static AutoPat *first_autopat[NUM_EVENTS] =
201{
202 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
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};
209
210static AutoPat *last_autopat[NUM_EVENTS] =
211{
212 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
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};
219
kylo252ae6f1d82022-02-16 19:24:07 +0000220#define AUGROUP_DEFAULT (-1) // default autocmd group
221#define AUGROUP_ERROR (-2) // erroneous autocmd group
222#define AUGROUP_ALL (-3) // all autocmd groups
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100223
224/*
225 * struct used to keep status while executing autocommands for an event.
226 */
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100227struct AutoPatCmd_S
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100228{
229 AutoPat *curpat; // next AutoPat to examine
230 AutoCmd *nextcmd; // next AutoCmd to execute
231 int group; // group being used
232 char_u *fname; // fname to match with
233 char_u *sfname; // sfname to match with
234 char_u *tail; // tail of fname
235 event_T event; // current event
236 int arg_bufnr; // Initially equal to <abuf>, set to zero when
237 // buf is deleted.
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100238 AutoPatCmd *next; // chain of active apc-s for auto-invalidation
239};
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100240
Bram Moolenaarc667da52019-11-30 20:52:27 +0100241static AutoPatCmd *active_apc_list = NULL; // stack of active autocommands
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100242
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200243// Macro to loop over all the patterns for an autocmd event
244#define FOR_ALL_AUTOCMD_PATTERNS(event, ap) \
245 for ((ap) = first_autopat[(int)(event)]; (ap) != NULL; (ap) = (ap)->next)
246
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100247/*
248 * augroups stores a list of autocmd group names.
249 */
250static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
251#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
Bram Moolenaarc667da52019-11-30 20:52:27 +0100252// use get_deleted_augroup() to get this
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100253static char_u *deleted_augroup = NULL;
254
255/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100256 * The ID of the current group. Group 0 is the default one.
257 */
258static int current_augroup = AUGROUP_DEFAULT;
259
Bram Moolenaarc667da52019-11-30 20:52:27 +0100260static int au_need_clean = FALSE; // need to delete marked patterns
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100261
262static char_u *event_nr2name(event_T event);
263static int au_get_grouparg(char_u **argp);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200264static 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 +0100265static int apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, int force, int group, buf_T *buf, exarg_T *eap);
266static void auto_next_pat(AutoPatCmd *apc, int stop_at_last);
267static int au_find_group(char_u *name);
268
269static event_T last_event;
270static int last_group;
Bram Moolenaarc667da52019-11-30 20:52:27 +0100271static int autocmd_blocked = 0; // block all autocmds
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100272
273 static char_u *
274get_deleted_augroup(void)
275{
276 if (deleted_augroup == NULL)
277 deleted_augroup = (char_u *)_("--Deleted--");
278 return deleted_augroup;
279}
280
281/*
282 * Show the autocommands for one AutoPat.
283 */
284 static void
285show_autocmd(AutoPat *ap, event_T event)
286{
287 AutoCmd *ac;
288
289 // Check for "got_int" (here and at various places below), which is set
290 // when "q" has been hit for the "--more--" prompt
291 if (got_int)
292 return;
293 if (ap->pat == NULL) // pattern has been removed
294 return;
295
296 msg_putchar('\n');
297 if (got_int)
298 return;
299 if (event != last_event || ap->group != last_group)
300 {
301 if (ap->group != AUGROUP_DEFAULT)
302 {
303 if (AUGROUP_NAME(ap->group) == NULL)
304 msg_puts_attr((char *)get_deleted_augroup(), HL_ATTR(HLF_E));
305 else
306 msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
307 msg_puts(" ");
308 }
309 msg_puts_attr((char *)event_nr2name(event), HL_ATTR(HLF_T));
310 last_event = event;
311 last_group = ap->group;
312 msg_putchar('\n');
313 if (got_int)
314 return;
315 }
316 msg_col = 4;
317 msg_outtrans(ap->pat);
318
319 for (ac = ap->cmds; ac != NULL; ac = ac->next)
320 {
321 if (ac->cmd != NULL) // skip removed commands
322 {
323 if (msg_col >= 14)
324 msg_putchar('\n');
325 msg_col = 14;
326 if (got_int)
327 return;
328 msg_outtrans(ac->cmd);
329#ifdef FEAT_EVAL
330 if (p_verbose > 0)
331 last_set_msg(ac->script_ctx);
332#endif
333 if (got_int)
334 return;
335 if (ac->next != NULL)
336 {
337 msg_putchar('\n');
338 if (got_int)
339 return;
340 }
341 }
342 }
343}
344
345/*
346 * Mark an autocommand pattern for deletion.
347 */
348 static void
349au_remove_pat(AutoPat *ap)
350{
351 VIM_CLEAR(ap->pat);
352 ap->buflocal_nr = -1;
353 au_need_clean = TRUE;
354}
355
356/*
357 * Mark all commands for a pattern for deletion.
358 */
359 static void
360au_remove_cmds(AutoPat *ap)
361{
362 AutoCmd *ac;
363
364 for (ac = ap->cmds; ac != NULL; ac = ac->next)
365 VIM_CLEAR(ac->cmd);
366 au_need_clean = TRUE;
367}
368
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200369// Delete one command from an autocmd pattern.
370static void au_del_cmd(AutoCmd *ac)
371{
372 VIM_CLEAR(ac->cmd);
373 au_need_clean = TRUE;
374}
375
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100376/*
377 * Cleanup autocommands and patterns that have been deleted.
378 * This is only done when not executing autocommands.
379 */
380 static void
381au_cleanup(void)
382{
383 AutoPat *ap, **prev_ap;
384 AutoCmd *ac, **prev_ac;
385 event_T event;
386
387 if (autocmd_busy || !au_need_clean)
388 return;
389
390 // loop over all events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100391 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100392 event = (event_T)((int)event + 1))
393 {
394 // loop over all autocommand patterns
395 prev_ap = &(first_autopat[(int)event]);
396 for (ap = *prev_ap; ap != NULL; ap = *prev_ap)
397 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200398 int has_cmd = FALSE;
399
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200400 // loop over all commands for this pattern
401 prev_ac = &(ap->cmds);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100402 for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
403 {
404 // remove the command if the pattern is to be deleted or when
405 // the command has been marked for deletion
406 if (ap->pat == NULL || ac->cmd == NULL)
407 {
408 *prev_ac = ac->next;
409 vim_free(ac->cmd);
410 vim_free(ac);
411 }
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200412 else
413 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200414 has_cmd = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100415 prev_ac = &(ac->next);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200416 }
417 }
418
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200419 if (ap->pat != NULL && !has_cmd)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200420 // Pattern was not marked for deletion, but all of its
421 // commands were. So mark the pattern for deletion.
422 au_remove_pat(ap);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100423
424 // remove the pattern if it has been marked for deletion
425 if (ap->pat == NULL)
426 {
427 if (ap->next == NULL)
428 {
429 if (prev_ap == &(first_autopat[(int)event]))
430 last_autopat[(int)event] = NULL;
431 else
432 // this depends on the "next" field being the first in
433 // the struct
434 last_autopat[(int)event] = (AutoPat *)prev_ap;
435 }
436 *prev_ap = ap->next;
437 vim_regfree(ap->reg_prog);
438 vim_free(ap);
439 }
440 else
441 prev_ap = &(ap->next);
442 }
443 }
444
445 au_need_clean = FALSE;
446}
447
448/*
449 * Called when buffer is freed, to remove/invalidate related buffer-local
450 * autocmds.
451 */
452 void
453aubuflocal_remove(buf_T *buf)
454{
455 AutoPat *ap;
456 event_T event;
457 AutoPatCmd *apc;
458
459 // invalidate currently executing autocommands
460 for (apc = active_apc_list; apc; apc = apc->next)
461 if (buf->b_fnum == apc->arg_bufnr)
462 apc->arg_bufnr = 0;
463
464 // invalidate buflocals looping through events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100465 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100466 event = (event_T)((int)event + 1))
467 // loop over all autocommand patterns
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200468 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100469 if (ap->buflocal_nr == buf->b_fnum)
470 {
471 au_remove_pat(ap);
472 if (p_verbose >= 6)
473 {
474 verbose_enter();
475 smsg(_("auto-removing autocommand: %s <buffer=%d>"),
476 event_nr2name(event), buf->b_fnum);
477 verbose_leave();
478 }
479 }
480 au_cleanup();
481}
482
483/*
484 * Add an autocmd group name.
485 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
486 */
487 static int
488au_new_group(char_u *name)
489{
490 int i;
491
492 i = au_find_group(name);
493 if (i == AUGROUP_ERROR) // the group doesn't exist yet, add it
494 {
495 // First try using a free entry.
496 for (i = 0; i < augroups.ga_len; ++i)
497 if (AUGROUP_NAME(i) == NULL)
498 break;
499 if (i == augroups.ga_len && ga_grow(&augroups, 1) == FAIL)
500 return AUGROUP_ERROR;
501
502 AUGROUP_NAME(i) = vim_strsave(name);
503 if (AUGROUP_NAME(i) == NULL)
504 return AUGROUP_ERROR;
505 if (i == augroups.ga_len)
506 ++augroups.ga_len;
507 }
508
509 return i;
510}
511
512 static void
513au_del_group(char_u *name)
514{
515 int i;
516
517 i = au_find_group(name);
518 if (i == AUGROUP_ERROR) // the group doesn't exist
Bram Moolenaarf1474d82021-12-31 19:59:55 +0000519 semsg(_(e_no_such_group_str), name);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100520 else if (i == current_augroup)
Bram Moolenaarf1474d82021-12-31 19:59:55 +0000521 emsg(_(e_cannot_delete_current_group));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100522 else
523 {
524 event_T event;
525 AutoPat *ap;
526 int in_use = FALSE;
527
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100528 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100529 event = (event_T)((int)event + 1))
530 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200531 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100532 if (ap->group == i && ap->pat != NULL)
533 {
534 give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
535 in_use = TRUE;
536 event = NUM_EVENTS;
537 break;
538 }
539 }
540 vim_free(AUGROUP_NAME(i));
541 if (in_use)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100542 AUGROUP_NAME(i) = get_deleted_augroup();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100543 else
544 AUGROUP_NAME(i) = NULL;
545 }
546}
547
548/*
549 * Find the ID of an autocmd group name.
550 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
551 */
552 static int
553au_find_group(char_u *name)
554{
555 int i;
556
557 for (i = 0; i < augroups.ga_len; ++i)
558 if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
559 && STRCMP(AUGROUP_NAME(i), name) == 0)
560 return i;
561 return AUGROUP_ERROR;
562}
563
564/*
565 * Return TRUE if augroup "name" exists.
566 */
567 int
568au_has_group(char_u *name)
569{
570 return au_find_group(name) != AUGROUP_ERROR;
571}
572
573/*
574 * ":augroup {name}".
575 */
576 void
577do_augroup(char_u *arg, int del_group)
578{
579 int i;
580
581 if (del_group)
582 {
583 if (*arg == NUL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000584 emsg(_(e_argument_required));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100585 else
586 au_del_group(arg);
587 }
588 else if (STRICMP(arg, "end") == 0) // ":aug end": back to group 0
589 current_augroup = AUGROUP_DEFAULT;
590 else if (*arg) // ":aug xxx": switch to group xxx
591 {
592 i = au_new_group(arg);
593 if (i != AUGROUP_ERROR)
594 current_augroup = i;
595 }
596 else // ":aug": list the group names
597 {
598 msg_start();
599 for (i = 0; i < augroups.ga_len; ++i)
600 {
601 if (AUGROUP_NAME(i) != NULL)
602 {
603 msg_puts((char *)AUGROUP_NAME(i));
604 msg_puts(" ");
605 }
606 }
607 msg_clr_eos();
608 msg_end();
609 }
610}
611
612#if defined(EXITFREE) || defined(PROTO)
613 void
614free_all_autocmds(void)
615{
616 int i;
617 char_u *s;
618
619 for (current_augroup = -1; current_augroup < augroups.ga_len;
620 ++current_augroup)
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200621 do_autocmd(NULL, (char_u *)"", TRUE);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100622
623 for (i = 0; i < augroups.ga_len; ++i)
624 {
625 s = ((char_u **)(augroups.ga_data))[i];
626 if (s != get_deleted_augroup())
627 vim_free(s);
628 }
629 ga_clear(&augroups);
630}
631#endif
632
633/*
634 * Return the event number for event name "start".
635 * Return NUM_EVENTS if the event name was not found.
636 * Return a pointer to the next event name in "end".
637 */
638 static event_T
639event_name2nr(char_u *start, char_u **end)
640{
641 char_u *p;
642 int i;
643 int len;
644
645 // the event name ends with end of line, '|', a blank or a comma
646 for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
647 ;
648 for (i = 0; event_names[i].name != NULL; ++i)
649 {
650 len = (int)STRLEN(event_names[i].name);
651 if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0)
652 break;
653 }
654 if (*p == ',')
655 ++p;
656 *end = p;
657 if (event_names[i].name == NULL)
658 return NUM_EVENTS;
659 return event_names[i].event;
660}
661
662/*
663 * Return the name for event "event".
664 */
665 static char_u *
666event_nr2name(event_T event)
667{
668 int i;
669
670 for (i = 0; event_names[i].name != NULL; ++i)
671 if (event_names[i].event == event)
672 return (char_u *)event_names[i].name;
673 return (char_u *)"Unknown";
674}
675
676/*
677 * Scan over the events. "*" stands for all events.
678 */
679 static char_u *
680find_end_event(
681 char_u *arg,
682 int have_group) // TRUE when group name was found
683{
684 char_u *pat;
685 char_u *p;
686
687 if (*arg == '*')
688 {
689 if (arg[1] && !VIM_ISWHITE(arg[1]))
690 {
Bram Moolenaar6d057012021-12-31 18:49:43 +0000691 semsg(_(e_illegal_character_after_star_str), arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100692 return NULL;
693 }
694 pat = arg + 1;
695 }
696 else
697 {
698 for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p)
699 {
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100700 if ((int)event_name2nr(pat, &p) >= NUM_EVENTS)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100701 {
702 if (have_group)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000703 semsg(_(e_no_such_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100704 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000705 semsg(_(e_no_such_group_or_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100706 return NULL;
707 }
708 }
709 }
710 return pat;
711}
712
713/*
714 * Return TRUE if "event" is included in 'eventignore'.
715 */
716 static int
717event_ignored(event_T event)
718{
719 char_u *p = p_ei;
720
721 while (*p != NUL)
722 {
723 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
724 return TRUE;
725 if (event_name2nr(p, &p) == event)
726 return TRUE;
727 }
728
729 return FALSE;
730}
731
732/*
733 * Return OK when the contents of p_ei is valid, FAIL otherwise.
734 */
735 int
736check_ei(void)
737{
738 char_u *p = p_ei;
739
740 while (*p)
741 {
742 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
743 {
744 p += 3;
745 if (*p == ',')
746 ++p;
747 }
748 else if (event_name2nr(p, &p) == NUM_EVENTS)
749 return FAIL;
750 }
751
752 return OK;
753}
754
755# if defined(FEAT_SYN_HL) || defined(PROTO)
756
757/*
758 * Add "what" to 'eventignore' to skip loading syntax highlighting for every
759 * buffer loaded into the window. "what" must start with a comma.
760 * Returns the old value of 'eventignore' in allocated memory.
761 */
762 char_u *
763au_event_disable(char *what)
764{
765 char_u *new_ei;
766 char_u *save_ei;
767
768 save_ei = vim_strsave(p_ei);
769 if (save_ei != NULL)
770 {
Bram Moolenaardf44a272020-06-07 20:49:05 +0200771 new_ei = vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100772 if (new_ei != NULL)
773 {
774 if (*what == ',' && *p_ei == NUL)
775 STRCPY(new_ei, what + 1);
776 else
777 STRCAT(new_ei, what);
778 set_string_option_direct((char_u *)"ei", -1, new_ei,
779 OPT_FREE, SID_NONE);
780 vim_free(new_ei);
781 }
782 }
783 return save_ei;
784}
785
786 void
787au_event_restore(char_u *old_ei)
788{
789 if (old_ei != NULL)
790 {
791 set_string_option_direct((char_u *)"ei", -1, old_ei,
792 OPT_FREE, SID_NONE);
793 vim_free(old_ei);
794 }
795}
Bram Moolenaarc667da52019-11-30 20:52:27 +0100796# endif // FEAT_SYN_HL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100797
798/*
799 * do_autocmd() -- implements the :autocmd command. Can be used in the
800 * following ways:
801 *
802 * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
803 * will be automatically executed for <event>
804 * when editing a file matching <pat>, in
805 * the current group.
806 * :autocmd <event> <pat> Show the autocommands associated with
807 * <event> and <pat>.
808 * :autocmd <event> Show the autocommands associated with
809 * <event>.
810 * :autocmd Show all autocommands.
811 * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
812 * <event> and <pat>, and add the command
813 * <cmd>, for the current group.
814 * :autocmd! <event> <pat> Remove all autocommands associated with
815 * <event> and <pat> for the current group.
816 * :autocmd! <event> Remove all autocommands associated with
817 * <event> for the current group.
818 * :autocmd! Remove ALL autocommands for the current
819 * group.
820 *
821 * Multiple events and patterns may be given separated by commas. Here are
822 * some examples:
823 * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
824 * :autocmd bufleave * set tw=79 nosmartindent ic infercase
825 *
826 * :autocmd * *.c show all autocommands for *.c files.
827 *
828 * Mostly a {group} argument can optionally appear before <event>.
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200829 * "eap" can be NULL.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100830 */
831 void
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200832do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100833{
834 char_u *arg = arg_in;
835 char_u *pat;
836 char_u *envpat = NULL;
837 char_u *cmd;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200838 int cmd_need_free = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100839 event_T event;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200840 char_u *tofree = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100841 int nested = FALSE;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200842 int once = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100843 int group;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200844 int i;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200845 int flags = 0;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100846
847 if (*arg == '|')
848 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000849 eap->nextcmd = arg + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100850 arg = (char_u *)"";
851 group = AUGROUP_ALL; // no argument, use all groups
852 }
853 else
854 {
855 /*
856 * Check for a legal group name. If not, use AUGROUP_ALL.
857 */
858 group = au_get_grouparg(&arg);
859 if (arg == NULL) // out of memory
860 return;
861 }
862
863 /*
864 * Scan over the events.
865 * If we find an illegal name, return here, don't do anything.
866 */
867 pat = find_end_event(arg, group != AUGROUP_ALL);
868 if (pat == NULL)
869 return;
870
871 pat = skipwhite(pat);
872 if (*pat == '|')
873 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000874 eap->nextcmd = pat + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100875 pat = (char_u *)"";
876 cmd = (char_u *)"";
877 }
878 else
879 {
880 /*
881 * Scan over the pattern. Put a NUL at the end.
882 */
883 cmd = pat;
884 while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
885 cmd++;
886 if (*cmd)
887 *cmd++ = NUL;
888
889 // Expand environment variables in the pattern. Set 'shellslash', we
890 // want forward slashes here.
891 if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
892 {
893#ifdef BACKSLASH_IN_FILENAME
894 int p_ssl_save = p_ssl;
895
896 p_ssl = TRUE;
897#endif
898 envpat = expand_env_save(pat);
899#ifdef BACKSLASH_IN_FILENAME
900 p_ssl = p_ssl_save;
901#endif
902 if (envpat != NULL)
903 pat = envpat;
904 }
905
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100906 cmd = skipwhite(cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200907 for (i = 0; i < 2; i++)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100908 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200909 if (*cmd != NUL)
910 {
911 // Check for "++once" flag.
912 if (STRNCMP(cmd, "++once", 6) == 0 && VIM_ISWHITE(cmd[6]))
913 {
914 if (once)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000915 semsg(_(e_duplicate_argument_str), "++once");
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200916 once = TRUE;
917 cmd = skipwhite(cmd + 6);
918 }
919
920 // Check for "++nested" flag.
921 if ((STRNCMP(cmd, "++nested", 8) == 0 && VIM_ISWHITE(cmd[8])))
922 {
923 if (nested)
Bram Moolenaarf0775142022-03-04 20:10:38 +0000924 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000925 semsg(_(e_duplicate_argument_str), "++nested");
Bram Moolenaarf0775142022-03-04 20:10:38 +0000926 return;
927 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200928 nested = TRUE;
929 cmd = skipwhite(cmd + 8);
930 }
931
Bram Moolenaarf0775142022-03-04 20:10:38 +0000932 // Check for the old "nested" flag in legacy script.
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200933 if (STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
934 {
Bram Moolenaarf0775142022-03-04 20:10:38 +0000935 if (in_vim9script())
936 {
937 // If there ever is a :nested command this error should
938 // be removed and "nested" accepted as the start of the
939 // command.
940 emsg(_(e_invalid_command_nested_did_you_mean_plusplus_nested));
941 return;
942 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200943 if (nested)
Bram Moolenaarf0775142022-03-04 20:10:38 +0000944 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000945 semsg(_(e_duplicate_argument_str), "nested");
Bram Moolenaarf0775142022-03-04 20:10:38 +0000946 return;
947 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200948 nested = TRUE;
949 cmd = skipwhite(cmd + 6);
950 }
951 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100952 }
953
954 /*
955 * Find the start of the commands.
956 * Expand <sfile> in it.
957 */
958 if (*cmd != NUL)
959 {
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200960 if (eap != NULL)
961 // Read a {} block if it follows.
962 cmd = may_get_cmd_block(eap, cmd, &tofree, &flags);
963
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100964 cmd = expand_sfile(cmd);
965 if (cmd == NULL) // some error
966 return;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200967 cmd_need_free = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100968 }
969 }
970
971 /*
972 * Print header when showing autocommands.
973 */
974 if (!forceit && *cmd == NUL)
975 // Highlight title
976 msg_puts_title(_("\n--- Autocommands ---"));
977
978 /*
979 * Loop over the events.
980 */
981 last_event = (event_T)-1; // for listing the event name
982 last_group = AUGROUP_ERROR; // for listing the group name
983 if (*arg == '*' || *arg == NUL || *arg == '|')
984 {
Bram Moolenaarb6db1462021-12-24 19:24:47 +0000985 if (*cmd != NUL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +0100986 emsg(_(e_cannot_define_autocommands_for_all_events));
987 else
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100988 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar9a046fd2021-01-28 13:47:59 +0100989 event = (event_T)((int)event + 1))
990 if (do_autocmd_event(event, pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200991 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +0100992 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100993 }
994 else
995 {
996 while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
997 if (do_autocmd_event(event_name2nr(arg, &arg), pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200998 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100999 break;
1000 }
1001
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001002 if (cmd_need_free)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001003 vim_free(cmd);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001004 vim_free(tofree);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001005 vim_free(envpat);
1006}
1007
1008/*
1009 * Find the group ID in a ":autocmd" or ":doautocmd" argument.
1010 * The "argp" argument is advanced to the following argument.
1011 *
1012 * Returns the group ID, AUGROUP_ERROR for error (out of memory).
1013 */
1014 static int
1015au_get_grouparg(char_u **argp)
1016{
1017 char_u *group_name;
1018 char_u *p;
1019 char_u *arg = *argp;
1020 int group = AUGROUP_ALL;
1021
1022 for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p)
1023 ;
1024 if (p > arg)
1025 {
Bram Moolenaardf44a272020-06-07 20:49:05 +02001026 group_name = vim_strnsave(arg, p - arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001027 if (group_name == NULL) // out of memory
1028 return AUGROUP_ERROR;
1029 group = au_find_group(group_name);
1030 if (group == AUGROUP_ERROR)
1031 group = AUGROUP_ALL; // no match, use all groups
1032 else
1033 *argp = skipwhite(p); // match, skip over group name
1034 vim_free(group_name);
1035 }
1036 return group;
1037}
1038
1039/*
1040 * do_autocmd() for one event.
1041 * If *pat == NUL do for all patterns.
1042 * If *cmd == NUL show entries.
1043 * If forceit == TRUE delete entries.
1044 * If group is not AUGROUP_ALL, only use this group.
1045 */
1046 static int
1047do_autocmd_event(
1048 event_T event,
1049 char_u *pat,
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001050 int once,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001051 int nested,
1052 char_u *cmd,
1053 int forceit,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001054 int group,
1055 int flags)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001056{
1057 AutoPat *ap;
1058 AutoPat **prev_ap;
1059 AutoCmd *ac;
1060 AutoCmd **prev_ac;
1061 int brace_level;
1062 char_u *endpat;
1063 int findgroup;
1064 int allgroups;
1065 int patlen;
1066 int is_buflocal;
1067 int buflocal_nr;
Bram Moolenaarc667da52019-11-30 20:52:27 +01001068 char_u buflocal_pat[25]; // for "<buffer=X>"
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001069
1070 if (group == AUGROUP_ALL)
1071 findgroup = current_augroup;
1072 else
1073 findgroup = group;
1074 allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
1075
1076 /*
1077 * Show or delete all patterns for an event.
1078 */
1079 if (*pat == NUL)
1080 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001081 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001082 {
1083 if (forceit) // delete the AutoPat, if it's in the current group
1084 {
1085 if (ap->group == findgroup)
1086 au_remove_pat(ap);
1087 }
1088 else if (group == AUGROUP_ALL || ap->group == group)
1089 show_autocmd(ap, event);
1090 }
1091 }
1092
1093 /*
1094 * Loop through all the specified patterns.
1095 */
1096 for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
1097 {
1098 /*
1099 * Find end of the pattern.
1100 * Watch out for a comma in braces, like "*.\{obj,o\}".
1101 */
1102 brace_level = 0;
1103 for (endpat = pat; *endpat && (*endpat != ',' || brace_level
1104 || (endpat > pat && endpat[-1] == '\\')); ++endpat)
1105 {
1106 if (*endpat == '{')
1107 brace_level++;
1108 else if (*endpat == '}')
1109 brace_level--;
1110 }
1111 if (pat == endpat) // ignore single comma
1112 continue;
1113 patlen = (int)(endpat - pat);
1114
1115 /*
1116 * detect special <buflocal[=X]> buffer-local patterns
1117 */
1118 is_buflocal = FALSE;
1119 buflocal_nr = 0;
1120
1121 if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
1122 && pat[patlen - 1] == '>')
1123 {
1124 // "<buffer...>": Error will be printed only for addition.
1125 // printing and removing will proceed silently.
1126 is_buflocal = TRUE;
1127 if (patlen == 8)
1128 // "<buffer>"
1129 buflocal_nr = curbuf->b_fnum;
1130 else if (patlen > 9 && pat[7] == '=')
1131 {
1132 if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0)
1133 // "<buffer=abuf>"
1134 buflocal_nr = autocmd_bufnr;
1135 else if (skipdigits(pat + 8) == pat + patlen - 1)
1136 // "<buffer=123>"
1137 buflocal_nr = atoi((char *)pat + 8);
1138 }
1139 }
1140
1141 if (is_buflocal)
1142 {
1143 // normalize pat into standard "<buffer>#N" form
1144 sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
1145 pat = buflocal_pat; // can modify pat and patlen
1146 patlen = (int)STRLEN(buflocal_pat); // but not endpat
1147 }
1148
1149 /*
1150 * Find AutoPat entries with this pattern. When adding a command it
1151 * always goes at or after the last one, so start at the end.
1152 */
1153 if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL)
1154 prev_ap = &last_autopat[(int)event];
1155 else
1156 prev_ap = &first_autopat[(int)event];
1157 while ((ap = *prev_ap) != NULL)
1158 {
1159 if (ap->pat != NULL)
1160 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001161 /*
1162 * Accept a pattern when:
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001163 * - a group was specified and it's that group, or a group was
1164 * not specified and it's the current group, or a group was
1165 * not specified and we are listing
1166 * - the length of the pattern matches
1167 * - the pattern matches.
1168 * For <buffer[=X]>, this condition works because we normalize
1169 * all buffer-local patterns.
1170 */
1171 if ((allgroups || ap->group == findgroup)
1172 && ap->patlen == patlen
1173 && STRNCMP(pat, ap->pat, patlen) == 0)
1174 {
1175 /*
1176 * Remove existing autocommands.
1177 * If adding any new autocmd's for this AutoPat, don't
1178 * delete the pattern from the autopat list, append to
1179 * this list.
1180 */
1181 if (forceit)
1182 {
1183 if (*cmd != NUL && ap->next == NULL)
1184 {
1185 au_remove_cmds(ap);
1186 break;
1187 }
1188 au_remove_pat(ap);
1189 }
1190
1191 /*
1192 * Show autocmd's for this autopat, or buflocals <buffer=X>
1193 */
1194 else if (*cmd == NUL)
1195 show_autocmd(ap, event);
1196
1197 /*
1198 * Add autocmd to this autopat, if it's the last one.
1199 */
1200 else if (ap->next == NULL)
1201 break;
1202 }
1203 }
1204 prev_ap = &ap->next;
1205 }
1206
1207 /*
1208 * Add a new command.
1209 */
1210 if (*cmd != NUL)
1211 {
1212 /*
1213 * If the pattern we want to add a command to does appear at the
1214 * end of the list (or not is not in the list at all), add the
1215 * pattern at the end of the list.
1216 */
1217 if (ap == NULL)
1218 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001219 // refuse to add buffer-local ap if buffer number is invalid
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001220 if (is_buflocal && (buflocal_nr == 0
1221 || buflist_findnr(buflocal_nr) == NULL))
1222 {
Bram Moolenaarf1474d82021-12-31 19:59:55 +00001223 semsg(_(e_buffer_nr_invalid_buffer_number), buflocal_nr);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001224 return FAIL;
1225 }
1226
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001227 ap = ALLOC_ONE(AutoPat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001228 if (ap == NULL)
1229 return FAIL;
1230 ap->pat = vim_strnsave(pat, patlen);
1231 ap->patlen = patlen;
1232 if (ap->pat == NULL)
1233 {
1234 vim_free(ap);
1235 return FAIL;
1236 }
1237
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001238#ifdef FEAT_EVAL
1239 // need to initialize last_mode for the first ModeChanged
1240 // autocmd
1241 if (event == EVENT_MODECHANGED && !has_modechanged())
1242 {
1243 typval_T rettv;
1244 typval_T tv[2];
1245
1246 tv[0].v_type = VAR_NUMBER;
1247 tv[0].vval.v_number = 1;
1248 tv[1].v_type = VAR_UNKNOWN;
1249 f_mode(tv, &rettv);
1250 STRCPY(last_mode, rettv.vval.v_string);
1251 vim_free(rettv.vval.v_string);
1252 }
1253#endif
1254
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/*
1786 * Return TRUE when there is a CursorMoved autocommand defined.
1787 */
1788 int
1789has_cursormoved(void)
1790{
1791 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1792}
1793
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001794/*
1795 * Return TRUE when there is a CursorMovedI autocommand defined.
1796 */
1797 int
1798has_cursormovedI(void)
1799{
1800 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1801}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001802
1803/*
1804 * Return TRUE when there is a TextChanged autocommand defined.
1805 */
1806 int
1807has_textchanged(void)
1808{
1809 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1810}
1811
1812/*
1813 * Return TRUE when there is a TextChangedI autocommand defined.
1814 */
1815 int
1816has_textchangedI(void)
1817{
1818 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
1819}
1820
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001821/*
1822 * Return TRUE when there is a TextChangedP autocommand defined.
1823 */
1824 int
1825has_textchangedP(void)
1826{
1827 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
1828}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001829
1830/*
1831 * Return TRUE when there is an InsertCharPre autocommand defined.
1832 */
1833 int
1834has_insertcharpre(void)
1835{
1836 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
1837}
1838
1839/*
1840 * Return TRUE when there is an CmdUndefined autocommand defined.
1841 */
1842 int
1843has_cmdundefined(void)
1844{
1845 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
1846}
1847
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001848#if defined(FEAT_EVAL) || defined(PROTO)
1849/*
1850 * Return TRUE when there is a TextYankPost autocommand defined.
1851 */
1852 int
1853has_textyankpost(void)
1854{
1855 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
1856}
1857#endif
1858
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001859#if defined(FEAT_EVAL) || defined(PROTO)
1860/*
1861 * Return TRUE when there is a CompleteChanged autocommand defined.
1862 */
1863 int
1864has_completechanged(void)
1865{
1866 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
1867}
1868#endif
1869
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02001870#if defined(FEAT_EVAL) || defined(PROTO)
1871/*
1872 * Return TRUE when there is a ModeChanged autocommand defined.
1873 */
1874 int
1875has_modechanged(void)
1876{
1877 return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
1878}
1879#endif
1880
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001881/*
1882 * Execute autocommands for "event" and file name "fname".
1883 * Return TRUE if some commands were executed.
1884 */
1885 static int
1886apply_autocmds_group(
1887 event_T event,
1888 char_u *fname, // NULL or empty means use actual file name
1889 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
1890 // use fname
1891 int force, // when TRUE, ignore autocmd_busy
1892 int group, // group ID, or AUGROUP_ALL
1893 buf_T *buf, // buffer for <abuf>
1894 exarg_T *eap UNUSED) // command arguments
1895{
1896 char_u *sfname = NULL; // short file name
1897 char_u *tail;
1898 int save_changed;
1899 buf_T *old_curbuf;
1900 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001901 char_u *save_autocmd_fname;
1902 int save_autocmd_fname_full;
1903 int save_autocmd_bufnr;
1904 char_u *save_autocmd_match;
1905 int save_autocmd_busy;
1906 int save_autocmd_nested;
1907 static int nesting = 0;
1908 AutoPatCmd patcmd;
1909 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001910 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001911#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001912 funccal_entry_T funccal_entry;
1913 char_u *save_cmdarg;
1914 long save_cmdbang;
1915#endif
1916 static int filechangeshell_busy = FALSE;
1917#ifdef FEAT_PROFILE
1918 proftime_T wait_time;
1919#endif
1920 int did_save_redobuff = FALSE;
1921 save_redo_T save_redo;
1922 int save_KeyTyped = KeyTyped;
ichizokc3f91c02021-12-17 09:44:33 +00001923 int save_did_emsg;
Bram Moolenaare31ee862020-01-07 20:59:34 +01001924 ESTACK_CHECK_DECLARATION
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001925
1926 /*
1927 * Quickly return if there are no autocommands for this event or
1928 * autocommands are blocked.
1929 */
1930 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
1931 || autocmd_blocked > 0)
1932 goto BYPASS_AU;
1933
1934 /*
1935 * When autocommands are busy, new autocommands are only executed when
1936 * explicitly enabled with the "nested" flag.
1937 */
1938 if (autocmd_busy && !(force || autocmd_nested))
1939 goto BYPASS_AU;
1940
1941#ifdef FEAT_EVAL
1942 /*
1943 * Quickly return when immediately aborting on error, or when an interrupt
1944 * occurred or an exception was thrown but not caught.
1945 */
1946 if (aborting())
1947 goto BYPASS_AU;
1948#endif
1949
1950 /*
1951 * FileChangedShell never nests, because it can create an endless loop.
1952 */
1953 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
1954 || event == EVENT_FILECHANGEDSHELLPOST))
1955 goto BYPASS_AU;
1956
1957 /*
1958 * Ignore events in 'eventignore'.
1959 */
1960 if (event_ignored(event))
1961 goto BYPASS_AU;
1962
1963 /*
1964 * Allow nesting of autocommands, but restrict the depth, because it's
1965 * possible to create an endless loop.
1966 */
1967 if (nesting == 10)
1968 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001969 emsg(_(e_autocommand_nesting_too_deep));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001970 goto BYPASS_AU;
1971 }
1972
1973 /*
1974 * Check if these autocommands are disabled. Used when doing ":all" or
1975 * ":ball".
1976 */
1977 if ( (autocmd_no_enter
1978 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
1979 || (autocmd_no_leave
1980 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
1981 goto BYPASS_AU;
1982
1983 /*
1984 * Save the autocmd_* variables and info about the current buffer.
1985 */
1986 save_autocmd_fname = autocmd_fname;
1987 save_autocmd_fname_full = autocmd_fname_full;
1988 save_autocmd_bufnr = autocmd_bufnr;
1989 save_autocmd_match = autocmd_match;
1990 save_autocmd_busy = autocmd_busy;
1991 save_autocmd_nested = autocmd_nested;
1992 save_changed = curbuf->b_changed;
1993 old_curbuf = curbuf;
1994
1995 /*
1996 * Set the file name to be used for <afile>.
1997 * Make a copy to avoid that changing a buffer name or directory makes it
1998 * invalid.
1999 */
2000 if (fname_io == NULL)
2001 {
2002 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002003 || event == EVENT_OPTIONSET
2004 || event == EVENT_MODECHANGED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002005 autocmd_fname = NULL;
2006 else if (fname != NULL && !ends_excmd(*fname))
2007 autocmd_fname = fname;
2008 else if (buf != NULL)
2009 autocmd_fname = buf->b_ffname;
2010 else
2011 autocmd_fname = NULL;
2012 }
2013 else
2014 autocmd_fname = fname_io;
2015 if (autocmd_fname != NULL)
2016 autocmd_fname = vim_strsave(autocmd_fname);
2017 autocmd_fname_full = FALSE; // call FullName_save() later
2018
2019 /*
2020 * Set the buffer number to be used for <abuf>.
2021 */
2022 if (buf == NULL)
2023 autocmd_bufnr = 0;
2024 else
2025 autocmd_bufnr = buf->b_fnum;
2026
2027 /*
2028 * When the file name is NULL or empty, use the file name of buffer "buf".
2029 * Always use the full path of the file name to match with, in case
2030 * "allow_dirs" is set.
2031 */
2032 if (fname == NULL || *fname == NUL)
2033 {
2034 if (buf == NULL)
2035 fname = NULL;
2036 else
2037 {
2038#ifdef FEAT_SYN_HL
2039 if (event == EVENT_SYNTAX)
2040 fname = buf->b_p_syn;
2041 else
2042#endif
2043 if (event == EVENT_FILETYPE)
2044 fname = buf->b_p_ft;
2045 else
2046 {
2047 if (buf->b_sfname != NULL)
2048 sfname = vim_strsave(buf->b_sfname);
2049 fname = buf->b_ffname;
2050 }
2051 }
2052 if (fname == NULL)
2053 fname = (char_u *)"";
2054 fname = vim_strsave(fname); // make a copy, so we can change it
2055 }
2056 else
2057 {
2058 sfname = vim_strsave(fname);
2059 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002060 // ColorScheme, QuickFixCmd*, DirChanged and similar.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002061 if (event == EVENT_FILETYPE
2062 || event == EVENT_SYNTAX
2063 || event == EVENT_CMDLINECHANGED
2064 || event == EVENT_CMDLINEENTER
2065 || event == EVENT_CMDLINELEAVE
2066 || event == EVENT_CMDWINENTER
2067 || event == EVENT_CMDWINLEAVE
2068 || event == EVENT_CMDUNDEFINED
2069 || event == EVENT_FUNCUNDEFINED
2070 || event == EVENT_REMOTEREPLY
2071 || event == EVENT_SPELLFILEMISSING
2072 || event == EVENT_QUICKFIXCMDPRE
2073 || event == EVENT_COLORSCHEME
2074 || event == EVENT_COLORSCHEMEPRE
2075 || event == EVENT_OPTIONSET
2076 || event == EVENT_QUICKFIXCMDPOST
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002077 || event == EVENT_DIRCHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002078 || event == EVENT_DIRCHANGEDPRE
naohiro ono23beefe2021-11-13 12:38:49 +00002079 || event == EVENT_MODECHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002080 || event == EVENT_USER
naohiro ono23beefe2021-11-13 12:38:49 +00002081 || event == EVENT_WINCLOSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002082 {
2083 fname = vim_strsave(fname);
2084 autocmd_fname_full = TRUE; // don't expand it later
2085 }
2086 else
2087 fname = FullName_save(fname, FALSE);
2088 }
2089 if (fname == NULL) // out of memory
2090 {
2091 vim_free(sfname);
2092 retval = FALSE;
2093 goto BYPASS_AU;
2094 }
2095
2096#ifdef BACKSLASH_IN_FILENAME
2097 /*
2098 * Replace all backslashes with forward slashes. This makes the
2099 * autocommand patterns portable between Unix and MS-DOS.
2100 */
2101 if (sfname != NULL)
2102 forward_slash(sfname);
2103 forward_slash(fname);
2104#endif
2105
2106#ifdef VMS
2107 // remove version for correct match
2108 if (sfname != NULL)
2109 vms_remove_version(sfname);
2110 vms_remove_version(fname);
2111#endif
2112
2113 /*
2114 * Set the name to be used for <amatch>.
2115 */
2116 autocmd_match = fname;
2117
2118
2119 // Don't redraw while doing autocommands.
2120 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002121
2122 // name and lnum are filled in later
2123 estack_push(ETYPE_AUCMD, NULL, 0);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002124 ESTACK_CHECK_SETUP
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002125
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002126 save_current_sctx = current_sctx;
2127
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002128#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002129# ifdef FEAT_PROFILE
2130 if (do_profiling == PROF_YES)
2131 prof_child_enter(&wait_time); // doesn't count for the caller itself
2132# endif
2133
2134 // Don't use local function variables, if called from a function.
2135 save_funccal(&funccal_entry);
2136#endif
2137
2138 /*
2139 * When starting to execute autocommands, save the search patterns.
2140 */
2141 if (!autocmd_busy)
2142 {
2143 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002144 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002145 {
2146 saveRedobuff(&save_redo);
2147 did_save_redobuff = TRUE;
2148 }
2149 did_filetype = keep_filetype;
2150 }
2151
2152 /*
2153 * Note that we are applying autocmds. Some commands need to know.
2154 */
2155 autocmd_busy = TRUE;
2156 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2157 ++nesting; // see matching decrement below
2158
2159 // Remember that FileType was triggered. Used for did_filetype().
2160 if (event == EVENT_FILETYPE)
2161 did_filetype = TRUE;
2162
2163 tail = gettail(fname);
2164
2165 // Find first autocommand that matches
2166 patcmd.curpat = first_autopat[(int)event];
2167 patcmd.nextcmd = NULL;
2168 patcmd.group = group;
2169 patcmd.fname = fname;
2170 patcmd.sfname = sfname;
2171 patcmd.tail = tail;
2172 patcmd.event = event;
2173 patcmd.arg_bufnr = autocmd_bufnr;
2174 patcmd.next = NULL;
2175 auto_next_pat(&patcmd, FALSE);
2176
2177 // found one, start executing the autocommands
2178 if (patcmd.curpat != NULL)
2179 {
2180 // add to active_apc_list
2181 patcmd.next = active_apc_list;
2182 active_apc_list = &patcmd;
2183
2184#ifdef FEAT_EVAL
2185 // set v:cmdarg (only when there is a matching pattern)
2186 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2187 if (eap != NULL)
2188 {
2189 save_cmdarg = set_cmdarg(eap, NULL);
2190 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2191 }
2192 else
2193 save_cmdarg = NULL; // avoid gcc warning
2194#endif
2195 retval = TRUE;
2196 // mark the last pattern, to avoid an endless loop when more patterns
2197 // are added when executing autocommands
2198 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2199 ap->last = FALSE;
2200 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002201
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002202 if (nesting == 1)
2203 // make sure cursor and topline are valid
2204 check_lnums(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002205
ichizokc3f91c02021-12-17 09:44:33 +00002206 save_did_emsg = did_emsg;
2207
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002208 do_cmdline(NULL, getnextac, (void *)&patcmd,
2209 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002210
ichizokc3f91c02021-12-17 09:44:33 +00002211 did_emsg += save_did_emsg;
2212
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002213 if (nesting == 1)
2214 // restore cursor and topline, unless they were changed
2215 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002216
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002217#ifdef FEAT_EVAL
2218 if (eap != NULL)
2219 {
2220 (void)set_cmdarg(NULL, save_cmdarg);
2221 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2222 }
2223#endif
2224 // delete from active_apc_list
2225 if (active_apc_list == &patcmd) // just in case
2226 active_apc_list = patcmd.next;
2227 }
2228
2229 --RedrawingDisabled;
2230 autocmd_busy = save_autocmd_busy;
2231 filechangeshell_busy = FALSE;
2232 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002233 vim_free(SOURCING_NAME);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002234 ESTACK_CHECK_NOW
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002235 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002236 vim_free(autocmd_fname);
2237 autocmd_fname = save_autocmd_fname;
2238 autocmd_fname_full = save_autocmd_fname_full;
2239 autocmd_bufnr = save_autocmd_bufnr;
2240 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002241 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002242#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002243 restore_funccal();
2244# ifdef FEAT_PROFILE
2245 if (do_profiling == PROF_YES)
2246 prof_child_exit(&wait_time);
2247# endif
2248#endif
2249 KeyTyped = save_KeyTyped;
2250 vim_free(fname);
2251 vim_free(sfname);
2252 --nesting; // see matching increment above
2253
2254 /*
2255 * When stopping to execute autocommands, restore the search patterns and
2256 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2257 * free any windows in the au_pending_free_win list.
2258 */
2259 if (!autocmd_busy)
2260 {
2261 restore_search_patterns();
2262 if (did_save_redobuff)
2263 restoreRedobuff(&save_redo);
2264 did_filetype = FALSE;
2265 while (au_pending_free_buf != NULL)
2266 {
2267 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002268
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002269 vim_free(au_pending_free_buf);
2270 au_pending_free_buf = b;
2271 }
2272 while (au_pending_free_win != NULL)
2273 {
2274 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002275
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002276 vim_free(au_pending_free_win);
2277 au_pending_free_win = w;
2278 }
2279 }
2280
2281 /*
2282 * Some events don't set or reset the Changed flag.
2283 * Check if still in the same buffer!
2284 */
2285 if (curbuf == old_curbuf
2286 && (event == EVENT_BUFREADPOST
2287 || event == EVENT_BUFWRITEPOST
2288 || event == EVENT_FILEAPPENDPOST
2289 || event == EVENT_VIMLEAVE
2290 || event == EVENT_VIMLEAVEPRE))
2291 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002292 if (curbuf->b_changed != save_changed)
2293 need_maketitle = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002294 curbuf->b_changed = save_changed;
2295 }
2296
2297 au_cleanup(); // may really delete removed patterns/commands now
2298
2299BYPASS_AU:
2300 // When wiping out a buffer make sure all its buffer-local autocommands
2301 // are deleted.
2302 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2303 aubuflocal_remove(buf);
2304
2305 if (retval == OK && event == EVENT_FILETYPE)
2306 au_did_filetype = TRUE;
2307
2308 return retval;
2309}
2310
2311# ifdef FEAT_EVAL
2312static char_u *old_termresponse = NULL;
2313# endif
2314
2315/*
2316 * Block triggering autocommands until unblock_autocmd() is called.
2317 * Can be used recursively, so long as it's symmetric.
2318 */
2319 void
2320block_autocmds(void)
2321{
2322# ifdef FEAT_EVAL
2323 // Remember the value of v:termresponse.
2324 if (autocmd_blocked == 0)
2325 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
2326# endif
2327 ++autocmd_blocked;
2328}
2329
2330 void
2331unblock_autocmds(void)
2332{
2333 --autocmd_blocked;
2334
2335# ifdef FEAT_EVAL
2336 // When v:termresponse was set while autocommands were blocked, trigger
2337 // the autocommands now. Esp. useful when executing a shell command
2338 // during startup (vimdiff).
2339 if (autocmd_blocked == 0
2340 && get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2341 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2342# endif
2343}
2344
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002345 int
2346is_autocmd_blocked(void)
2347{
2348 return autocmd_blocked != 0;
2349}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002350
2351/*
2352 * Find next autocommand pattern that matches.
2353 */
2354 static void
2355auto_next_pat(
2356 AutoPatCmd *apc,
2357 int stop_at_last) // stop when 'last' flag is set
2358{
2359 AutoPat *ap;
2360 AutoCmd *cp;
2361 char_u *name;
2362 char *s;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002363 char_u **sourcing_namep = &SOURCING_NAME;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002364
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002365 VIM_CLEAR(*sourcing_namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002366
2367 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2368 {
2369 apc->curpat = NULL;
2370
2371 // Only use a pattern when it has not been removed, has commands and
2372 // the group matches. For buffer-local autocommands only check the
2373 // buffer number.
2374 if (ap->pat != NULL && ap->cmds != NULL
2375 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2376 {
2377 // execution-condition
2378 if (ap->buflocal_nr == 0
2379 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2380 apc->sfname, apc->tail, ap->allow_dirs))
2381 : ap->buflocal_nr == apc->arg_bufnr)
2382 {
2383 name = event_nr2name(apc->event);
2384 s = _("%s Autocommands for \"%s\"");
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002385 *sourcing_namep = alloc(STRLEN(s)
Bram Moolenaar964b3742019-05-24 18:54:09 +02002386 + STRLEN(name) + ap->patlen + 1);
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002387 if (*sourcing_namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002388 {
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002389 sprintf((char *)*sourcing_namep, s,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002390 (char *)name, (char *)ap->pat);
2391 if (p_verbose >= 8)
2392 {
2393 verbose_enter();
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002394 smsg(_("Executing %s"), *sourcing_namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002395 verbose_leave();
2396 }
2397 }
2398
2399 apc->curpat = ap;
2400 apc->nextcmd = ap->cmds;
2401 // mark last command
2402 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2403 cp->last = FALSE;
2404 cp->last = TRUE;
2405 }
2406 line_breakcheck();
2407 if (apc->curpat != NULL) // found a match
2408 break;
2409 }
2410 if (stop_at_last && ap->last)
2411 break;
2412 }
2413}
2414
2415/*
2416 * Get next autocommand command.
2417 * Called by do_cmdline() to get the next line for ":if".
2418 * Returns allocated string, or NULL for end of autocommands.
2419 */
2420 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002421getnextac(
2422 int c UNUSED,
2423 void *cookie,
2424 int indent UNUSED,
2425 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002426{
2427 AutoPatCmd *acp = (AutoPatCmd *)cookie;
2428 char_u *retval;
2429 AutoCmd *ac;
2430
2431 // Can be called again after returning the last line.
2432 if (acp->curpat == NULL)
2433 return NULL;
2434
2435 // repeat until we find an autocommand to execute
2436 for (;;)
2437 {
2438 // skip removed commands
2439 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2440 if (acp->nextcmd->last)
2441 acp->nextcmd = NULL;
2442 else
2443 acp->nextcmd = acp->nextcmd->next;
2444
2445 if (acp->nextcmd != NULL)
2446 break;
2447
2448 // at end of commands, find next pattern that matches
2449 if (acp->curpat->last)
2450 acp->curpat = NULL;
2451 else
2452 acp->curpat = acp->curpat->next;
2453 if (acp->curpat != NULL)
2454 auto_next_pat(acp, TRUE);
2455 if (acp->curpat == NULL)
2456 return NULL;
2457 }
2458
2459 ac = acp->nextcmd;
2460
2461 if (p_verbose >= 9)
2462 {
2463 verbose_enter_scroll();
2464 smsg(_("autocommand %s"), ac->cmd);
2465 msg_puts("\n"); // don't overwrite this either
2466 verbose_leave_scroll();
2467 }
2468 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002469 // Remove one-shot ("once") autocmd in anticipation of its execution.
2470 if (ac->once)
2471 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002472 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002473 current_sctx = ac->script_ctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002474 if (ac->last)
2475 acp->nextcmd = NULL;
2476 else
2477 acp->nextcmd = ac->next;
2478 return retval;
2479}
2480
2481/*
2482 * Return TRUE if there is a matching autocommand for "fname".
2483 * To account for buffer-local autocommands, function needs to know
2484 * in which buffer the file will be opened.
2485 */
2486 int
2487has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2488{
2489 AutoPat *ap;
2490 char_u *fname;
2491 char_u *tail = gettail(sfname);
2492 int retval = FALSE;
2493
2494 fname = FullName_save(sfname, FALSE);
2495 if (fname == NULL)
2496 return FALSE;
2497
2498#ifdef BACKSLASH_IN_FILENAME
2499 /*
2500 * Replace all backslashes with forward slashes. This makes the
2501 * autocommand patterns portable between Unix and MS-DOS.
2502 */
2503 sfname = vim_strsave(sfname);
2504 if (sfname != NULL)
2505 forward_slash(sfname);
2506 forward_slash(fname);
2507#endif
2508
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002509 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002510 if (ap->pat != NULL && ap->cmds != NULL
2511 && (ap->buflocal_nr == 0
2512 ? match_file_pat(NULL, &ap->reg_prog,
2513 fname, sfname, tail, ap->allow_dirs)
2514 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2515 ))
2516 {
2517 retval = TRUE;
2518 break;
2519 }
2520
2521 vim_free(fname);
2522#ifdef BACKSLASH_IN_FILENAME
2523 vim_free(sfname);
2524#endif
2525
2526 return retval;
2527}
2528
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002529/*
2530 * Function given to ExpandGeneric() to obtain the list of autocommand group
2531 * names.
2532 */
2533 char_u *
2534get_augroup_name(expand_T *xp UNUSED, int idx)
2535{
2536 if (idx == augroups.ga_len) // add "END" add the end
2537 return (char_u *)"END";
2538 if (idx >= augroups.ga_len) // end of list
2539 return NULL;
2540 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2541 // skip deleted entries
2542 return (char_u *)"";
2543 return AUGROUP_NAME(idx); // return a name
2544}
2545
2546static int include_groups = FALSE;
2547
2548 char_u *
2549set_context_in_autocmd(
2550 expand_T *xp,
2551 char_u *arg,
2552 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2553{
2554 char_u *p;
2555 int group;
2556
2557 // check for a group name, skip it if present
2558 include_groups = FALSE;
2559 p = arg;
2560 group = au_get_grouparg(&arg);
2561 if (group == AUGROUP_ERROR)
2562 return NULL;
2563 // If there only is a group name that's what we expand.
2564 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2565 {
2566 arg = p;
2567 group = AUGROUP_ALL;
2568 }
2569
2570 // skip over event name
2571 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2572 if (*p == ',')
2573 arg = p + 1;
2574 if (*p == NUL)
2575 {
2576 if (group == AUGROUP_ALL)
2577 include_groups = TRUE;
2578 xp->xp_context = EXPAND_EVENTS; // expand event name
2579 xp->xp_pattern = arg;
2580 return NULL;
2581 }
2582
2583 // skip over pattern
2584 arg = skipwhite(p);
2585 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2586 arg++;
2587 if (*arg)
2588 return arg; // expand (next) command
2589
2590 if (doautocmd)
2591 xp->xp_context = EXPAND_FILES; // expand file names
2592 else
2593 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2594 return NULL;
2595}
2596
2597/*
2598 * Function given to ExpandGeneric() to obtain the list of event names.
2599 */
2600 char_u *
2601get_event_name(expand_T *xp UNUSED, int idx)
2602{
2603 if (idx < augroups.ga_len) // First list group names, if wanted
2604 {
2605 if (!include_groups || AUGROUP_NAME(idx) == NULL
2606 || AUGROUP_NAME(idx) == get_deleted_augroup())
2607 return (char_u *)""; // skip deleted entries
2608 return AUGROUP_NAME(idx); // return a name
2609 }
2610 return (char_u *)event_names[idx - augroups.ga_len].name;
2611}
2612
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002613
2614#if defined(FEAT_EVAL) || defined(PROTO)
2615/*
2616 * Return TRUE if autocmd is supported.
2617 */
2618 int
2619autocmd_supported(char_u *name)
2620{
2621 char_u *p;
2622
2623 return (event_name2nr(name, &p) != NUM_EVENTS);
2624}
2625
2626/*
2627 * Return TRUE if an autocommand is defined for a group, event and
2628 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2629 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2630 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2631 * Used for:
2632 * exists("#Group") or
2633 * exists("#Group#Event") or
2634 * exists("#Group#Event#pat") or
2635 * exists("#Event") or
2636 * exists("#Event#pat")
2637 */
2638 int
2639au_exists(char_u *arg)
2640{
2641 char_u *arg_save;
2642 char_u *pattern = NULL;
2643 char_u *event_name;
2644 char_u *p;
2645 event_T event;
2646 AutoPat *ap;
2647 buf_T *buflocal_buf = NULL;
2648 int group;
2649 int retval = FALSE;
2650
2651 // Make a copy so that we can change the '#' chars to a NUL.
2652 arg_save = vim_strsave(arg);
2653 if (arg_save == NULL)
2654 return FALSE;
2655 p = vim_strchr(arg_save, '#');
2656 if (p != NULL)
2657 *p++ = NUL;
2658
2659 // First, look for an autocmd group name
2660 group = au_find_group(arg_save);
2661 if (group == AUGROUP_ERROR)
2662 {
2663 // Didn't match a group name, assume the first argument is an event.
2664 group = AUGROUP_ALL;
2665 event_name = arg_save;
2666 }
2667 else
2668 {
2669 if (p == NULL)
2670 {
2671 // "Group": group name is present and it's recognized
2672 retval = TRUE;
2673 goto theend;
2674 }
2675
2676 // Must be "Group#Event" or "Group#Event#pat".
2677 event_name = p;
2678 p = vim_strchr(event_name, '#');
2679 if (p != NULL)
2680 *p++ = NUL; // "Group#Event#pat"
2681 }
2682
2683 pattern = p; // "pattern" is NULL when there is no pattern
2684
2685 // find the index (enum) for the event name
2686 event = event_name2nr(event_name, &p);
2687
2688 // return FALSE if the event name is not recognized
2689 if (event == NUM_EVENTS)
2690 goto theend;
2691
2692 // Find the first autocommand for this event.
2693 // If there isn't any, return FALSE;
2694 // If there is one and no pattern given, return TRUE;
2695 ap = first_autopat[(int)event];
2696 if (ap == NULL)
2697 goto theend;
2698
2699 // if pattern is "<buffer>", special handling is needed which uses curbuf
2700 // for pattern "<buffer=N>, fnamecmp() will work fine
2701 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2702 buflocal_buf = curbuf;
2703
2704 // Check if there is an autocommand with the given pattern.
2705 for ( ; ap != NULL; ap = ap->next)
2706 // only use a pattern when it has not been removed and has commands.
2707 // For buffer-local autocommands, fnamecmp() works fine.
2708 if (ap->pat != NULL && ap->cmds != NULL
2709 && (group == AUGROUP_ALL || ap->group == group)
2710 && (pattern == NULL
2711 || (buflocal_buf == NULL
2712 ? fnamecmp(ap->pat, pattern) == 0
2713 : ap->buflocal_nr == buflocal_buf->b_fnum)))
2714 {
2715 retval = TRUE;
2716 break;
2717 }
2718
2719theend:
2720 vim_free(arg_save);
2721 return retval;
2722}
2723#endif