blob: 14cd4af592c1cb5b3c6a011ebed2981f55cc9da5 [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},
122 {"EncodingChanged", EVENT_ENCODINGCHANGED},
123 {"ExitPre", EVENT_EXITPRE},
124 {"FileEncoding", EVENT_ENCODINGCHANGED},
125 {"FileAppendPost", EVENT_FILEAPPENDPOST},
126 {"FileAppendPre", EVENT_FILEAPPENDPRE},
127 {"FileAppendCmd", EVENT_FILEAPPENDCMD},
128 {"FileChangedShell",EVENT_FILECHANGEDSHELL},
129 {"FileChangedShellPost",EVENT_FILECHANGEDSHELLPOST},
130 {"FileChangedRO", EVENT_FILECHANGEDRO},
131 {"FileReadPost", EVENT_FILEREADPOST},
132 {"FileReadPre", EVENT_FILEREADPRE},
133 {"FileReadCmd", EVENT_FILEREADCMD},
134 {"FileType", EVENT_FILETYPE},
135 {"FileWritePost", EVENT_FILEWRITEPOST},
136 {"FileWritePre", EVENT_FILEWRITEPRE},
137 {"FileWriteCmd", EVENT_FILEWRITECMD},
138 {"FilterReadPost", EVENT_FILTERREADPOST},
139 {"FilterReadPre", EVENT_FILTERREADPRE},
140 {"FilterWritePost", EVENT_FILTERWRITEPOST},
141 {"FilterWritePre", EVENT_FILTERWRITEPRE},
142 {"FocusGained", EVENT_FOCUSGAINED},
143 {"FocusLost", EVENT_FOCUSLOST},
144 {"FuncUndefined", EVENT_FUNCUNDEFINED},
145 {"GUIEnter", EVENT_GUIENTER},
146 {"GUIFailed", EVENT_GUIFAILED},
147 {"InsertChange", EVENT_INSERTCHANGE},
148 {"InsertEnter", EVENT_INSERTENTER},
149 {"InsertLeave", EVENT_INSERTLEAVE},
Bram Moolenaarb53e13a2020-10-21 12:19:53 +0200150 {"InsertLeavePre", EVENT_INSERTLEAVEPRE},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100151 {"InsertCharPre", EVENT_INSERTCHARPRE},
152 {"MenuPopup", EVENT_MENUPOPUP},
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +0200153 {"ModeChanged", EVENT_MODECHANGED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100154 {"OptionSet", EVENT_OPTIONSET},
155 {"QuickFixCmdPost", EVENT_QUICKFIXCMDPOST},
156 {"QuickFixCmdPre", EVENT_QUICKFIXCMDPRE},
157 {"QuitPre", EVENT_QUITPRE},
158 {"RemoteReply", EVENT_REMOTEREPLY},
Bram Moolenaar8aeec402019-09-15 23:02:04 +0200159 {"SafeState", EVENT_SAFESTATE},
Bram Moolenaar69198cb2019-09-16 21:58:13 +0200160 {"SafeStateAgain", EVENT_SAFESTATEAGAIN},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100161 {"SessionLoadPost", EVENT_SESSIONLOADPOST},
162 {"ShellCmdPost", EVENT_SHELLCMDPOST},
163 {"ShellFilterPost", EVENT_SHELLFILTERPOST},
Bram Moolenaarbe5ee862020-06-10 20:56:58 +0200164 {"SigUSR1", EVENT_SIGUSR1},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100165 {"SourceCmd", EVENT_SOURCECMD},
166 {"SourcePre", EVENT_SOURCEPRE},
167 {"SourcePost", EVENT_SOURCEPOST},
168 {"SpellFileMissing",EVENT_SPELLFILEMISSING},
169 {"StdinReadPost", EVENT_STDINREADPOST},
170 {"StdinReadPre", EVENT_STDINREADPRE},
171 {"SwapExists", EVENT_SWAPEXISTS},
172 {"Syntax", EVENT_SYNTAX},
173 {"TabNew", EVENT_TABNEW},
174 {"TabClosed", EVENT_TABCLOSED},
175 {"TabEnter", EVENT_TABENTER},
176 {"TabLeave", EVENT_TABLEAVE},
177 {"TermChanged", EVENT_TERMCHANGED},
178 {"TerminalOpen", EVENT_TERMINALOPEN},
Bram Moolenaar28ed4df2019-10-26 16:21:40 +0200179 {"TerminalWinOpen", EVENT_TERMINALWINOPEN},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100180 {"TermResponse", EVENT_TERMRESPONSE},
181 {"TextChanged", EVENT_TEXTCHANGED},
182 {"TextChangedI", EVENT_TEXTCHANGEDI},
183 {"TextChangedP", EVENT_TEXTCHANGEDP},
184 {"User", EVENT_USER},
185 {"VimEnter", EVENT_VIMENTER},
186 {"VimLeave", EVENT_VIMLEAVE},
187 {"VimLeavePre", EVENT_VIMLEAVEPRE},
188 {"WinNew", EVENT_WINNEW},
naohiro ono23beefe2021-11-13 12:38:49 +0000189 {"WinClosed", EVENT_WINCLOSED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100190 {"WinEnter", EVENT_WINENTER},
191 {"WinLeave", EVENT_WINLEAVE},
192 {"VimResized", EVENT_VIMRESIZED},
193 {"TextYankPost", EVENT_TEXTYANKPOST},
Bram Moolenaar100118c2020-12-11 19:30:34 +0100194 {"VimSuspend", EVENT_VIMSUSPEND},
195 {"VimResume", EVENT_VIMRESUME},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100196 {NULL, (event_T)0}
197};
198
199static AutoPat *first_autopat[NUM_EVENTS] =
200{
201 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
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};
208
209static AutoPat *last_autopat[NUM_EVENTS] =
210{
211 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
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};
218
219#define AUGROUP_DEFAULT -1 // default autocmd group
220#define AUGROUP_ERROR -2 // erroneous autocmd group
221#define AUGROUP_ALL -3 // all autocmd groups
222
223/*
224 * struct used to keep status while executing autocommands for an event.
225 */
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100226struct AutoPatCmd_S
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100227{
228 AutoPat *curpat; // next AutoPat to examine
229 AutoCmd *nextcmd; // next AutoCmd to execute
230 int group; // group being used
231 char_u *fname; // fname to match with
232 char_u *sfname; // sfname to match with
233 char_u *tail; // tail of fname
234 event_T event; // current event
235 int arg_bufnr; // Initially equal to <abuf>, set to zero when
236 // buf is deleted.
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100237 AutoPatCmd *next; // chain of active apc-s for auto-invalidation
238};
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100239
Bram Moolenaarc667da52019-11-30 20:52:27 +0100240static AutoPatCmd *active_apc_list = NULL; // stack of active autocommands
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100241
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200242// Macro to loop over all the patterns for an autocmd event
243#define FOR_ALL_AUTOCMD_PATTERNS(event, ap) \
244 for ((ap) = first_autopat[(int)(event)]; (ap) != NULL; (ap) = (ap)->next)
245
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100246/*
247 * augroups stores a list of autocmd group names.
248 */
249static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
250#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
Bram Moolenaarc667da52019-11-30 20:52:27 +0100251// use get_deleted_augroup() to get this
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100252static char_u *deleted_augroup = NULL;
253
254/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100255 * The ID of the current group. Group 0 is the default one.
256 */
257static int current_augroup = AUGROUP_DEFAULT;
258
Bram Moolenaarc667da52019-11-30 20:52:27 +0100259static int au_need_clean = FALSE; // need to delete marked patterns
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100260
261static char_u *event_nr2name(event_T event);
262static int au_get_grouparg(char_u **argp);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200263static 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 +0100264static int apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, int force, int group, buf_T *buf, exarg_T *eap);
265static void auto_next_pat(AutoPatCmd *apc, int stop_at_last);
266static int au_find_group(char_u *name);
267
268static event_T last_event;
269static int last_group;
Bram Moolenaarc667da52019-11-30 20:52:27 +0100270static int autocmd_blocked = 0; // block all autocmds
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100271
272 static char_u *
273get_deleted_augroup(void)
274{
275 if (deleted_augroup == NULL)
276 deleted_augroup = (char_u *)_("--Deleted--");
277 return deleted_augroup;
278}
279
280/*
281 * Show the autocommands for one AutoPat.
282 */
283 static void
284show_autocmd(AutoPat *ap, event_T event)
285{
286 AutoCmd *ac;
287
288 // Check for "got_int" (here and at various places below), which is set
289 // when "q" has been hit for the "--more--" prompt
290 if (got_int)
291 return;
292 if (ap->pat == NULL) // pattern has been removed
293 return;
294
295 msg_putchar('\n');
296 if (got_int)
297 return;
298 if (event != last_event || ap->group != last_group)
299 {
300 if (ap->group != AUGROUP_DEFAULT)
301 {
302 if (AUGROUP_NAME(ap->group) == NULL)
303 msg_puts_attr((char *)get_deleted_augroup(), HL_ATTR(HLF_E));
304 else
305 msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
306 msg_puts(" ");
307 }
308 msg_puts_attr((char *)event_nr2name(event), HL_ATTR(HLF_T));
309 last_event = event;
310 last_group = ap->group;
311 msg_putchar('\n');
312 if (got_int)
313 return;
314 }
315 msg_col = 4;
316 msg_outtrans(ap->pat);
317
318 for (ac = ap->cmds; ac != NULL; ac = ac->next)
319 {
320 if (ac->cmd != NULL) // skip removed commands
321 {
322 if (msg_col >= 14)
323 msg_putchar('\n');
324 msg_col = 14;
325 if (got_int)
326 return;
327 msg_outtrans(ac->cmd);
328#ifdef FEAT_EVAL
329 if (p_verbose > 0)
330 last_set_msg(ac->script_ctx);
331#endif
332 if (got_int)
333 return;
334 if (ac->next != NULL)
335 {
336 msg_putchar('\n');
337 if (got_int)
338 return;
339 }
340 }
341 }
342}
343
344/*
345 * Mark an autocommand pattern for deletion.
346 */
347 static void
348au_remove_pat(AutoPat *ap)
349{
350 VIM_CLEAR(ap->pat);
351 ap->buflocal_nr = -1;
352 au_need_clean = TRUE;
353}
354
355/*
356 * Mark all commands for a pattern for deletion.
357 */
358 static void
359au_remove_cmds(AutoPat *ap)
360{
361 AutoCmd *ac;
362
363 for (ac = ap->cmds; ac != NULL; ac = ac->next)
364 VIM_CLEAR(ac->cmd);
365 au_need_clean = TRUE;
366}
367
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200368// Delete one command from an autocmd pattern.
369static void au_del_cmd(AutoCmd *ac)
370{
371 VIM_CLEAR(ac->cmd);
372 au_need_clean = TRUE;
373}
374
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100375/*
376 * Cleanup autocommands and patterns that have been deleted.
377 * This is only done when not executing autocommands.
378 */
379 static void
380au_cleanup(void)
381{
382 AutoPat *ap, **prev_ap;
383 AutoCmd *ac, **prev_ac;
384 event_T event;
385
386 if (autocmd_busy || !au_need_clean)
387 return;
388
389 // loop over all events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100390 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100391 event = (event_T)((int)event + 1))
392 {
393 // loop over all autocommand patterns
394 prev_ap = &(first_autopat[(int)event]);
395 for (ap = *prev_ap; ap != NULL; ap = *prev_ap)
396 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200397 int has_cmd = FALSE;
398
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200399 // loop over all commands for this pattern
400 prev_ac = &(ap->cmds);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100401 for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
402 {
403 // remove the command if the pattern is to be deleted or when
404 // the command has been marked for deletion
405 if (ap->pat == NULL || ac->cmd == NULL)
406 {
407 *prev_ac = ac->next;
408 vim_free(ac->cmd);
409 vim_free(ac);
410 }
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200411 else
412 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200413 has_cmd = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100414 prev_ac = &(ac->next);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200415 }
416 }
417
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200418 if (ap->pat != NULL && !has_cmd)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200419 // Pattern was not marked for deletion, but all of its
420 // commands were. So mark the pattern for deletion.
421 au_remove_pat(ap);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100422
423 // remove the pattern if it has been marked for deletion
424 if (ap->pat == NULL)
425 {
426 if (ap->next == NULL)
427 {
428 if (prev_ap == &(first_autopat[(int)event]))
429 last_autopat[(int)event] = NULL;
430 else
431 // this depends on the "next" field being the first in
432 // the struct
433 last_autopat[(int)event] = (AutoPat *)prev_ap;
434 }
435 *prev_ap = ap->next;
436 vim_regfree(ap->reg_prog);
437 vim_free(ap);
438 }
439 else
440 prev_ap = &(ap->next);
441 }
442 }
443
444 au_need_clean = FALSE;
445}
446
447/*
448 * Called when buffer is freed, to remove/invalidate related buffer-local
449 * autocmds.
450 */
451 void
452aubuflocal_remove(buf_T *buf)
453{
454 AutoPat *ap;
455 event_T event;
456 AutoPatCmd *apc;
457
458 // invalidate currently executing autocommands
459 for (apc = active_apc_list; apc; apc = apc->next)
460 if (buf->b_fnum == apc->arg_bufnr)
461 apc->arg_bufnr = 0;
462
463 // invalidate buflocals looping through events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100464 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100465 event = (event_T)((int)event + 1))
466 // loop over all autocommand patterns
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200467 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100468 if (ap->buflocal_nr == buf->b_fnum)
469 {
470 au_remove_pat(ap);
471 if (p_verbose >= 6)
472 {
473 verbose_enter();
474 smsg(_("auto-removing autocommand: %s <buffer=%d>"),
475 event_nr2name(event), buf->b_fnum);
476 verbose_leave();
477 }
478 }
479 au_cleanup();
480}
481
482/*
483 * Add an autocmd group name.
484 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
485 */
486 static int
487au_new_group(char_u *name)
488{
489 int i;
490
491 i = au_find_group(name);
492 if (i == AUGROUP_ERROR) // the group doesn't exist yet, add it
493 {
494 // First try using a free entry.
495 for (i = 0; i < augroups.ga_len; ++i)
496 if (AUGROUP_NAME(i) == NULL)
497 break;
498 if (i == augroups.ga_len && ga_grow(&augroups, 1) == FAIL)
499 return AUGROUP_ERROR;
500
501 AUGROUP_NAME(i) = vim_strsave(name);
502 if (AUGROUP_NAME(i) == NULL)
503 return AUGROUP_ERROR;
504 if (i == augroups.ga_len)
505 ++augroups.ga_len;
506 }
507
508 return i;
509}
510
511 static void
512au_del_group(char_u *name)
513{
514 int i;
515
516 i = au_find_group(name);
517 if (i == AUGROUP_ERROR) // the group doesn't exist
518 semsg(_("E367: No such group: \"%s\""), name);
519 else if (i == current_augroup)
520 emsg(_("E936: Cannot delete the current group"));
521 else
522 {
523 event_T event;
524 AutoPat *ap;
525 int in_use = FALSE;
526
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100527 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100528 event = (event_T)((int)event + 1))
529 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200530 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100531 if (ap->group == i && ap->pat != NULL)
532 {
533 give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
534 in_use = TRUE;
535 event = NUM_EVENTS;
536 break;
537 }
538 }
539 vim_free(AUGROUP_NAME(i));
540 if (in_use)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100541 AUGROUP_NAME(i) = get_deleted_augroup();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100542 else
543 AUGROUP_NAME(i) = NULL;
544 }
545}
546
547/*
548 * Find the ID of an autocmd group name.
549 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
550 */
551 static int
552au_find_group(char_u *name)
553{
554 int i;
555
556 for (i = 0; i < augroups.ga_len; ++i)
557 if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
558 && STRCMP(AUGROUP_NAME(i), name) == 0)
559 return i;
560 return AUGROUP_ERROR;
561}
562
563/*
564 * Return TRUE if augroup "name" exists.
565 */
566 int
567au_has_group(char_u *name)
568{
569 return au_find_group(name) != AUGROUP_ERROR;
570}
571
572/*
573 * ":augroup {name}".
574 */
575 void
576do_augroup(char_u *arg, int del_group)
577{
578 int i;
579
580 if (del_group)
581 {
582 if (*arg == NUL)
583 emsg(_(e_argreq));
584 else
585 au_del_group(arg);
586 }
587 else if (STRICMP(arg, "end") == 0) // ":aug end": back to group 0
588 current_augroup = AUGROUP_DEFAULT;
589 else if (*arg) // ":aug xxx": switch to group xxx
590 {
591 i = au_new_group(arg);
592 if (i != AUGROUP_ERROR)
593 current_augroup = i;
594 }
595 else // ":aug": list the group names
596 {
597 msg_start();
598 for (i = 0; i < augroups.ga_len; ++i)
599 {
600 if (AUGROUP_NAME(i) != NULL)
601 {
602 msg_puts((char *)AUGROUP_NAME(i));
603 msg_puts(" ");
604 }
605 }
606 msg_clr_eos();
607 msg_end();
608 }
609}
610
611#if defined(EXITFREE) || defined(PROTO)
612 void
613free_all_autocmds(void)
614{
615 int i;
616 char_u *s;
617
618 for (current_augroup = -1; current_augroup < augroups.ga_len;
619 ++current_augroup)
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200620 do_autocmd(NULL, (char_u *)"", TRUE);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100621
622 for (i = 0; i < augroups.ga_len; ++i)
623 {
624 s = ((char_u **)(augroups.ga_data))[i];
625 if (s != get_deleted_augroup())
626 vim_free(s);
627 }
628 ga_clear(&augroups);
629}
630#endif
631
632/*
633 * Return the event number for event name "start".
634 * Return NUM_EVENTS if the event name was not found.
635 * Return a pointer to the next event name in "end".
636 */
637 static event_T
638event_name2nr(char_u *start, char_u **end)
639{
640 char_u *p;
641 int i;
642 int len;
643
644 // the event name ends with end of line, '|', a blank or a comma
645 for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
646 ;
647 for (i = 0; event_names[i].name != NULL; ++i)
648 {
649 len = (int)STRLEN(event_names[i].name);
650 if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0)
651 break;
652 }
653 if (*p == ',')
654 ++p;
655 *end = p;
656 if (event_names[i].name == NULL)
657 return NUM_EVENTS;
658 return event_names[i].event;
659}
660
661/*
662 * Return the name for event "event".
663 */
664 static char_u *
665event_nr2name(event_T event)
666{
667 int i;
668
669 for (i = 0; event_names[i].name != NULL; ++i)
670 if (event_names[i].event == event)
671 return (char_u *)event_names[i].name;
672 return (char_u *)"Unknown";
673}
674
675/*
676 * Scan over the events. "*" stands for all events.
677 */
678 static char_u *
679find_end_event(
680 char_u *arg,
681 int have_group) // TRUE when group name was found
682{
683 char_u *pat;
684 char_u *p;
685
686 if (*arg == '*')
687 {
688 if (arg[1] && !VIM_ISWHITE(arg[1]))
689 {
690 semsg(_("E215: Illegal character after *: %s"), arg);
691 return NULL;
692 }
693 pat = arg + 1;
694 }
695 else
696 {
697 for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p)
698 {
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100699 if ((int)event_name2nr(pat, &p) >= NUM_EVENTS)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100700 {
701 if (have_group)
702 semsg(_("E216: No such event: %s"), pat);
703 else
704 semsg(_("E216: No such group or event: %s"), pat);
705 return NULL;
706 }
707 }
708 }
709 return pat;
710}
711
712/*
713 * Return TRUE if "event" is included in 'eventignore'.
714 */
715 static int
716event_ignored(event_T event)
717{
718 char_u *p = p_ei;
719
720 while (*p != NUL)
721 {
722 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
723 return TRUE;
724 if (event_name2nr(p, &p) == event)
725 return TRUE;
726 }
727
728 return FALSE;
729}
730
731/*
732 * Return OK when the contents of p_ei is valid, FAIL otherwise.
733 */
734 int
735check_ei(void)
736{
737 char_u *p = p_ei;
738
739 while (*p)
740 {
741 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
742 {
743 p += 3;
744 if (*p == ',')
745 ++p;
746 }
747 else if (event_name2nr(p, &p) == NUM_EVENTS)
748 return FAIL;
749 }
750
751 return OK;
752}
753
754# if defined(FEAT_SYN_HL) || defined(PROTO)
755
756/*
757 * Add "what" to 'eventignore' to skip loading syntax highlighting for every
758 * buffer loaded into the window. "what" must start with a comma.
759 * Returns the old value of 'eventignore' in allocated memory.
760 */
761 char_u *
762au_event_disable(char *what)
763{
764 char_u *new_ei;
765 char_u *save_ei;
766
767 save_ei = vim_strsave(p_ei);
768 if (save_ei != NULL)
769 {
Bram Moolenaardf44a272020-06-07 20:49:05 +0200770 new_ei = vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100771 if (new_ei != NULL)
772 {
773 if (*what == ',' && *p_ei == NUL)
774 STRCPY(new_ei, what + 1);
775 else
776 STRCAT(new_ei, what);
777 set_string_option_direct((char_u *)"ei", -1, new_ei,
778 OPT_FREE, SID_NONE);
779 vim_free(new_ei);
780 }
781 }
782 return save_ei;
783}
784
785 void
786au_event_restore(char_u *old_ei)
787{
788 if (old_ei != NULL)
789 {
790 set_string_option_direct((char_u *)"ei", -1, old_ei,
791 OPT_FREE, SID_NONE);
792 vim_free(old_ei);
793 }
794}
Bram Moolenaarc667da52019-11-30 20:52:27 +0100795# endif // FEAT_SYN_HL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100796
797/*
798 * do_autocmd() -- implements the :autocmd command. Can be used in the
799 * following ways:
800 *
801 * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
802 * will be automatically executed for <event>
803 * when editing a file matching <pat>, in
804 * the current group.
805 * :autocmd <event> <pat> Show the autocommands associated with
806 * <event> and <pat>.
807 * :autocmd <event> Show the autocommands associated with
808 * <event>.
809 * :autocmd Show all autocommands.
810 * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
811 * <event> and <pat>, and add the command
812 * <cmd>, for the current group.
813 * :autocmd! <event> <pat> Remove all autocommands associated with
814 * <event> and <pat> for the current group.
815 * :autocmd! <event> Remove all autocommands associated with
816 * <event> for the current group.
817 * :autocmd! Remove ALL autocommands for the current
818 * group.
819 *
820 * Multiple events and patterns may be given separated by commas. Here are
821 * some examples:
822 * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
823 * :autocmd bufleave * set tw=79 nosmartindent ic infercase
824 *
825 * :autocmd * *.c show all autocommands for *.c files.
826 *
827 * Mostly a {group} argument can optionally appear before <event>.
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200828 * "eap" can be NULL.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100829 */
830 void
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200831do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100832{
833 char_u *arg = arg_in;
834 char_u *pat;
835 char_u *envpat = NULL;
836 char_u *cmd;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200837 int cmd_need_free = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100838 event_T event;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200839 char_u *tofree = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100840 int nested = FALSE;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200841 int once = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100842 int group;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200843 int i;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200844 int flags = 0;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100845
846 if (*arg == '|')
847 {
848 arg = (char_u *)"";
849 group = AUGROUP_ALL; // no argument, use all groups
850 }
851 else
852 {
853 /*
854 * Check for a legal group name. If not, use AUGROUP_ALL.
855 */
856 group = au_get_grouparg(&arg);
857 if (arg == NULL) // out of memory
858 return;
859 }
860
861 /*
862 * Scan over the events.
863 * If we find an illegal name, return here, don't do anything.
864 */
865 pat = find_end_event(arg, group != AUGROUP_ALL);
866 if (pat == NULL)
867 return;
868
869 pat = skipwhite(pat);
870 if (*pat == '|')
871 {
872 pat = (char_u *)"";
873 cmd = (char_u *)"";
874 }
875 else
876 {
877 /*
878 * Scan over the pattern. Put a NUL at the end.
879 */
880 cmd = pat;
881 while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
882 cmd++;
883 if (*cmd)
884 *cmd++ = NUL;
885
886 // Expand environment variables in the pattern. Set 'shellslash', we
887 // want forward slashes here.
888 if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
889 {
890#ifdef BACKSLASH_IN_FILENAME
891 int p_ssl_save = p_ssl;
892
893 p_ssl = TRUE;
894#endif
895 envpat = expand_env_save(pat);
896#ifdef BACKSLASH_IN_FILENAME
897 p_ssl = p_ssl_save;
898#endif
899 if (envpat != NULL)
900 pat = envpat;
901 }
902
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100903 cmd = skipwhite(cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200904 for (i = 0; i < 2; i++)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100905 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200906 if (*cmd != NUL)
907 {
908 // Check for "++once" flag.
909 if (STRNCMP(cmd, "++once", 6) == 0 && VIM_ISWHITE(cmd[6]))
910 {
911 if (once)
912 semsg(_(e_duparg2), "++once");
913 once = TRUE;
914 cmd = skipwhite(cmd + 6);
915 }
916
917 // Check for "++nested" flag.
918 if ((STRNCMP(cmd, "++nested", 8) == 0 && VIM_ISWHITE(cmd[8])))
919 {
920 if (nested)
921 semsg(_(e_duparg2), "++nested");
922 nested = TRUE;
923 cmd = skipwhite(cmd + 8);
924 }
925
926 // Check for the old "nested" flag.
927 if (STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
928 {
929 if (nested)
930 semsg(_(e_duparg2), "nested");
931 nested = TRUE;
932 cmd = skipwhite(cmd + 6);
933 }
934 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100935 }
936
937 /*
938 * Find the start of the commands.
939 * Expand <sfile> in it.
940 */
941 if (*cmd != NUL)
942 {
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200943 if (eap != NULL)
944 // Read a {} block if it follows.
945 cmd = may_get_cmd_block(eap, cmd, &tofree, &flags);
946
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100947 cmd = expand_sfile(cmd);
948 if (cmd == NULL) // some error
949 return;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200950 cmd_need_free = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100951 }
952 }
953
954 /*
955 * Print header when showing autocommands.
956 */
957 if (!forceit && *cmd == NUL)
958 // Highlight title
959 msg_puts_title(_("\n--- Autocommands ---"));
960
961 /*
962 * Loop over the events.
963 */
964 last_event = (event_T)-1; // for listing the event name
965 last_group = AUGROUP_ERROR; // for listing the group name
966 if (*arg == '*' || *arg == NUL || *arg == '|')
967 {
Bram Moolenaar9a046fd2021-01-28 13:47:59 +0100968 if (!forceit && *cmd != NUL)
969 emsg(_(e_cannot_define_autocommands_for_all_events));
970 else
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100971 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar9a046fd2021-01-28 13:47:59 +0100972 event = (event_T)((int)event + 1))
973 if (do_autocmd_event(event, pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200974 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +0100975 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100976 }
977 else
978 {
979 while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
980 if (do_autocmd_event(event_name2nr(arg, &arg), pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200981 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100982 break;
983 }
984
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200985 if (cmd_need_free)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100986 vim_free(cmd);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200987 vim_free(tofree);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100988 vim_free(envpat);
989}
990
991/*
992 * Find the group ID in a ":autocmd" or ":doautocmd" argument.
993 * The "argp" argument is advanced to the following argument.
994 *
995 * Returns the group ID, AUGROUP_ERROR for error (out of memory).
996 */
997 static int
998au_get_grouparg(char_u **argp)
999{
1000 char_u *group_name;
1001 char_u *p;
1002 char_u *arg = *argp;
1003 int group = AUGROUP_ALL;
1004
1005 for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p)
1006 ;
1007 if (p > arg)
1008 {
Bram Moolenaardf44a272020-06-07 20:49:05 +02001009 group_name = vim_strnsave(arg, p - arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001010 if (group_name == NULL) // out of memory
1011 return AUGROUP_ERROR;
1012 group = au_find_group(group_name);
1013 if (group == AUGROUP_ERROR)
1014 group = AUGROUP_ALL; // no match, use all groups
1015 else
1016 *argp = skipwhite(p); // match, skip over group name
1017 vim_free(group_name);
1018 }
1019 return group;
1020}
1021
1022/*
1023 * do_autocmd() for one event.
1024 * If *pat == NUL do for all patterns.
1025 * If *cmd == NUL show entries.
1026 * If forceit == TRUE delete entries.
1027 * If group is not AUGROUP_ALL, only use this group.
1028 */
1029 static int
1030do_autocmd_event(
1031 event_T event,
1032 char_u *pat,
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001033 int once,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001034 int nested,
1035 char_u *cmd,
1036 int forceit,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001037 int group,
1038 int flags)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001039{
1040 AutoPat *ap;
1041 AutoPat **prev_ap;
1042 AutoCmd *ac;
1043 AutoCmd **prev_ac;
1044 int brace_level;
1045 char_u *endpat;
1046 int findgroup;
1047 int allgroups;
1048 int patlen;
1049 int is_buflocal;
1050 int buflocal_nr;
Bram Moolenaarc667da52019-11-30 20:52:27 +01001051 char_u buflocal_pat[25]; // for "<buffer=X>"
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001052
1053 if (group == AUGROUP_ALL)
1054 findgroup = current_augroup;
1055 else
1056 findgroup = group;
1057 allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
1058
1059 /*
1060 * Show or delete all patterns for an event.
1061 */
1062 if (*pat == NUL)
1063 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001064 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001065 {
1066 if (forceit) // delete the AutoPat, if it's in the current group
1067 {
1068 if (ap->group == findgroup)
1069 au_remove_pat(ap);
1070 }
1071 else if (group == AUGROUP_ALL || ap->group == group)
1072 show_autocmd(ap, event);
1073 }
1074 }
1075
1076 /*
1077 * Loop through all the specified patterns.
1078 */
1079 for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
1080 {
1081 /*
1082 * Find end of the pattern.
1083 * Watch out for a comma in braces, like "*.\{obj,o\}".
1084 */
1085 brace_level = 0;
1086 for (endpat = pat; *endpat && (*endpat != ',' || brace_level
1087 || (endpat > pat && endpat[-1] == '\\')); ++endpat)
1088 {
1089 if (*endpat == '{')
1090 brace_level++;
1091 else if (*endpat == '}')
1092 brace_level--;
1093 }
1094 if (pat == endpat) // ignore single comma
1095 continue;
1096 patlen = (int)(endpat - pat);
1097
1098 /*
1099 * detect special <buflocal[=X]> buffer-local patterns
1100 */
1101 is_buflocal = FALSE;
1102 buflocal_nr = 0;
1103
1104 if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
1105 && pat[patlen - 1] == '>')
1106 {
1107 // "<buffer...>": Error will be printed only for addition.
1108 // printing and removing will proceed silently.
1109 is_buflocal = TRUE;
1110 if (patlen == 8)
1111 // "<buffer>"
1112 buflocal_nr = curbuf->b_fnum;
1113 else if (patlen > 9 && pat[7] == '=')
1114 {
1115 if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0)
1116 // "<buffer=abuf>"
1117 buflocal_nr = autocmd_bufnr;
1118 else if (skipdigits(pat + 8) == pat + patlen - 1)
1119 // "<buffer=123>"
1120 buflocal_nr = atoi((char *)pat + 8);
1121 }
1122 }
1123
1124 if (is_buflocal)
1125 {
1126 // normalize pat into standard "<buffer>#N" form
1127 sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
1128 pat = buflocal_pat; // can modify pat and patlen
1129 patlen = (int)STRLEN(buflocal_pat); // but not endpat
1130 }
1131
1132 /*
1133 * Find AutoPat entries with this pattern. When adding a command it
1134 * always goes at or after the last one, so start at the end.
1135 */
1136 if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL)
1137 prev_ap = &last_autopat[(int)event];
1138 else
1139 prev_ap = &first_autopat[(int)event];
1140 while ((ap = *prev_ap) != NULL)
1141 {
1142 if (ap->pat != NULL)
1143 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001144 /*
1145 * Accept a pattern when:
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001146 * - a group was specified and it's that group, or a group was
1147 * not specified and it's the current group, or a group was
1148 * not specified and we are listing
1149 * - the length of the pattern matches
1150 * - the pattern matches.
1151 * For <buffer[=X]>, this condition works because we normalize
1152 * all buffer-local patterns.
1153 */
1154 if ((allgroups || ap->group == findgroup)
1155 && ap->patlen == patlen
1156 && STRNCMP(pat, ap->pat, patlen) == 0)
1157 {
1158 /*
1159 * Remove existing autocommands.
1160 * If adding any new autocmd's for this AutoPat, don't
1161 * delete the pattern from the autopat list, append to
1162 * this list.
1163 */
1164 if (forceit)
1165 {
1166 if (*cmd != NUL && ap->next == NULL)
1167 {
1168 au_remove_cmds(ap);
1169 break;
1170 }
1171 au_remove_pat(ap);
1172 }
1173
1174 /*
1175 * Show autocmd's for this autopat, or buflocals <buffer=X>
1176 */
1177 else if (*cmd == NUL)
1178 show_autocmd(ap, event);
1179
1180 /*
1181 * Add autocmd to this autopat, if it's the last one.
1182 */
1183 else if (ap->next == NULL)
1184 break;
1185 }
1186 }
1187 prev_ap = &ap->next;
1188 }
1189
1190 /*
1191 * Add a new command.
1192 */
1193 if (*cmd != NUL)
1194 {
1195 /*
1196 * If the pattern we want to add a command to does appear at the
1197 * end of the list (or not is not in the list at all), add the
1198 * pattern at the end of the list.
1199 */
1200 if (ap == NULL)
1201 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001202 // refuse to add buffer-local ap if buffer number is invalid
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001203 if (is_buflocal && (buflocal_nr == 0
1204 || buflist_findnr(buflocal_nr) == NULL))
1205 {
1206 semsg(_("E680: <buffer=%d>: invalid buffer number "),
1207 buflocal_nr);
1208 return FAIL;
1209 }
1210
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001211 ap = ALLOC_ONE(AutoPat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001212 if (ap == NULL)
1213 return FAIL;
1214 ap->pat = vim_strnsave(pat, patlen);
1215 ap->patlen = patlen;
1216 if (ap->pat == NULL)
1217 {
1218 vim_free(ap);
1219 return FAIL;
1220 }
1221
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001222#ifdef FEAT_EVAL
1223 // need to initialize last_mode for the first ModeChanged
1224 // autocmd
1225 if (event == EVENT_MODECHANGED && !has_modechanged())
1226 {
1227 typval_T rettv;
1228 typval_T tv[2];
1229
1230 tv[0].v_type = VAR_NUMBER;
1231 tv[0].vval.v_number = 1;
1232 tv[1].v_type = VAR_UNKNOWN;
1233 f_mode(tv, &rettv);
1234 STRCPY(last_mode, rettv.vval.v_string);
1235 vim_free(rettv.vval.v_string);
1236 }
1237#endif
1238
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001239 if (is_buflocal)
1240 {
1241 ap->buflocal_nr = buflocal_nr;
1242 ap->reg_prog = NULL;
1243 }
1244 else
1245 {
1246 char_u *reg_pat;
1247
1248 ap->buflocal_nr = 0;
1249 reg_pat = file_pat_to_reg_pat(pat, endpat,
1250 &ap->allow_dirs, TRUE);
1251 if (reg_pat != NULL)
1252 ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
1253 vim_free(reg_pat);
1254 if (reg_pat == NULL || ap->reg_prog == NULL)
1255 {
1256 vim_free(ap->pat);
1257 vim_free(ap);
1258 return FAIL;
1259 }
1260 }
1261 ap->cmds = NULL;
1262 *prev_ap = ap;
1263 last_autopat[(int)event] = ap;
1264 ap->next = NULL;
1265 if (group == AUGROUP_ALL)
1266 ap->group = current_augroup;
1267 else
1268 ap->group = group;
1269 }
1270
1271 /*
1272 * Add the autocmd at the end of the AutoCmd list.
1273 */
1274 prev_ac = &(ap->cmds);
1275 while ((ac = *prev_ac) != NULL)
1276 prev_ac = &ac->next;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001277 ac = ALLOC_ONE(AutoCmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001278 if (ac == NULL)
1279 return FAIL;
1280 ac->cmd = vim_strsave(cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001281 ac->script_ctx = current_sctx;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001282 if (flags & UC_VIM9)
1283 ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001284#ifdef FEAT_EVAL
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001285 ac->script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001286#endif
1287 if (ac->cmd == NULL)
1288 {
1289 vim_free(ac);
1290 return FAIL;
1291 }
1292 ac->next = NULL;
1293 *prev_ac = ac;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001294 ac->once = once;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001295 ac->nested = nested;
1296 }
1297 }
1298
1299 au_cleanup(); // may really delete removed patterns/commands now
1300 return OK;
1301}
1302
1303/*
1304 * Implementation of ":doautocmd [group] event [fname]".
1305 * Return OK for success, FAIL for failure;
1306 */
1307 int
1308do_doautocmd(
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001309 char_u *arg_start,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001310 int do_msg, // give message for no matching autocmds?
1311 int *did_something)
1312{
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001313 char_u *arg = arg_start;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001314 char_u *fname;
1315 int nothing_done = TRUE;
1316 int group;
1317
1318 if (did_something != NULL)
1319 *did_something = FALSE;
1320
1321 /*
1322 * Check for a legal group name. If not, use AUGROUP_ALL.
1323 */
1324 group = au_get_grouparg(&arg);
1325 if (arg == NULL) // out of memory
1326 return FAIL;
1327
1328 if (*arg == '*')
1329 {
1330 emsg(_("E217: Can't execute autocommands for ALL events"));
1331 return FAIL;
1332 }
1333
1334 /*
1335 * Scan over the events.
1336 * If we find an illegal name, return here, don't do anything.
1337 */
1338 fname = find_end_event(arg, group != AUGROUP_ALL);
1339 if (fname == NULL)
1340 return FAIL;
1341
1342 fname = skipwhite(fname);
1343
1344 /*
1345 * Loop over the events.
1346 */
1347 while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
1348 if (apply_autocmds_group(event_name2nr(arg, &arg),
1349 fname, NULL, TRUE, group, curbuf, NULL))
1350 nothing_done = FALSE;
1351
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001352 if (nothing_done && do_msg
1353#ifdef FEAT_EVAL
1354 && !aborting()
1355#endif
1356 )
1357 smsg(_("No matching autocommands: %s"), arg_start);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001358 if (did_something != NULL)
1359 *did_something = !nothing_done;
1360
1361#ifdef FEAT_EVAL
1362 return aborting() ? FAIL : OK;
1363#else
1364 return OK;
1365#endif
1366}
1367
1368/*
1369 * ":doautoall": execute autocommands for each loaded buffer.
1370 */
1371 void
1372ex_doautoall(exarg_T *eap)
1373{
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001374 int retval = OK;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001375 aco_save_T aco;
1376 buf_T *buf;
1377 bufref_T bufref;
1378 char_u *arg = eap->arg;
1379 int call_do_modelines = check_nomodeline(&arg);
1380 int did_aucmd;
1381
1382 /*
1383 * This is a bit tricky: For some commands curwin->w_buffer needs to be
1384 * equal to curbuf, but for some buffers there may not be a window.
1385 * So we change the buffer for the current window for a moment. This
1386 * gives problems when the autocommands make changes to the list of
1387 * buffers or windows...
1388 */
1389 FOR_ALL_BUFFERS(buf)
1390 {
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001391 // Only do loaded buffers and skip the current buffer, it's done last.
1392 if (buf->b_ml.ml_mfp != NULL && buf != curbuf)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001393 {
1394 // find a window for this buffer and save some values
1395 aucmd_prepbuf(&aco, buf);
1396 set_bufref(&bufref, buf);
1397
1398 // execute the autocommands for this buffer
1399 retval = do_doautocmd(arg, FALSE, &did_aucmd);
1400
1401 if (call_do_modelines && did_aucmd)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001402 // Execute the modeline settings, but don't set window-local
1403 // options if we are using the current window for another
1404 // buffer.
1405 do_modelines(curwin == aucmd_win ? OPT_NOWIN : 0);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001406
1407 // restore the current window
1408 aucmd_restbuf(&aco);
1409
1410 // stop if there is some error or buffer was deleted
1411 if (retval == FAIL || !bufref_valid(&bufref))
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001412 {
1413 retval = FAIL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001414 break;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001415 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001416 }
1417 }
1418
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001419 // Execute autocommands for the current buffer last.
1420 if (retval == OK)
1421 {
1422 do_doautocmd(arg, FALSE, &did_aucmd);
1423 if (call_do_modelines && did_aucmd)
1424 do_modelines(0);
1425 }
1426
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001427 check_cursor(); // just in case lines got deleted
1428}
1429
1430/*
1431 * Check *argp for <nomodeline>. When it is present return FALSE, otherwise
1432 * return TRUE and advance *argp to after it.
1433 * Thus return TRUE when do_modelines() should be called.
1434 */
1435 int
1436check_nomodeline(char_u **argp)
1437{
1438 if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
1439 {
1440 *argp = skipwhite(*argp + 12);
1441 return FALSE;
1442 }
1443 return TRUE;
1444}
1445
1446/*
1447 * Prepare for executing autocommands for (hidden) buffer "buf".
1448 * Search for a visible window containing the current buffer. If there isn't
1449 * one then use "aucmd_win".
1450 * Set "curbuf" and "curwin" to match "buf".
1451 */
1452 void
1453aucmd_prepbuf(
1454 aco_save_T *aco, // structure to save values in
1455 buf_T *buf) // new curbuf
1456{
1457 win_T *win;
1458 int save_ea;
1459#ifdef FEAT_AUTOCHDIR
1460 int save_acd;
1461#endif
1462
1463 // Find a window that is for the new buffer
1464 if (buf == curbuf) // be quick when buf is curbuf
1465 win = curwin;
1466 else
1467 FOR_ALL_WINDOWS(win)
1468 if (win->w_buffer == buf)
1469 break;
1470
1471 // Allocate "aucmd_win" when needed. If this fails (out of memory) fall
1472 // back to using the current window.
1473 if (win == NULL && aucmd_win == NULL)
1474 {
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001475 aucmd_win = win_alloc_popup_win();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001476 if (aucmd_win == NULL)
1477 win = curwin;
1478 }
1479 if (win == NULL && aucmd_win_used)
1480 // Strange recursive autocommand, fall back to using the current
1481 // window. Expect a few side effects...
1482 win = curwin;
1483
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001484 aco->save_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001485 aco->save_curbuf = curbuf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001486 aco->save_prevwin_id = prevwin == NULL ? 0 : prevwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001487 if (win != NULL)
1488 {
1489 // There is a window for "buf" in the current tab page, make it the
1490 // curwin. This is preferred, it has the least side effects (esp. if
1491 // "buf" is curbuf).
1492 aco->use_aucmd_win = FALSE;
1493 curwin = win;
1494 }
1495 else
1496 {
1497 // There is no window for "buf", use "aucmd_win". To minimize the side
1498 // effects, insert it in the current tab page.
1499 // Anything related to a window (e.g., setting folds) may have
1500 // unexpected results.
1501 aco->use_aucmd_win = TRUE;
1502 aucmd_win_used = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001503
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001504 win_init_popup_win(aucmd_win, buf);
1505
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001506 aco->globaldir = globaldir;
1507 globaldir = NULL;
1508
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001509 // Split the current window, put the aucmd_win in the upper half.
1510 // We don't want the BufEnter or WinEnter autocommands.
1511 block_autocmds();
1512 make_snapshot(SNAP_AUCMD_IDX);
1513 save_ea = p_ea;
1514 p_ea = FALSE;
1515
1516#ifdef FEAT_AUTOCHDIR
1517 // Prevent chdir() call in win_enter_ext(), through do_autochdir().
1518 save_acd = p_acd;
1519 p_acd = FALSE;
1520#endif
1521
1522 (void)win_split_ins(0, WSP_TOP, aucmd_win, 0);
1523 (void)win_comp_pos(); // recompute window positions
1524 p_ea = save_ea;
1525#ifdef FEAT_AUTOCHDIR
1526 p_acd = save_acd;
1527#endif
1528 unblock_autocmds();
1529 curwin = aucmd_win;
1530 }
1531 curbuf = buf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001532 aco->new_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001533 set_bufref(&aco->new_curbuf, curbuf);
1534}
1535
1536/*
1537 * Cleanup after executing autocommands for a (hidden) buffer.
1538 * Restore the window as it was (if possible).
1539 */
1540 void
1541aucmd_restbuf(
1542 aco_save_T *aco) // structure holding saved values
1543{
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001544 int dummy;
1545 win_T *save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001546
1547 if (aco->use_aucmd_win)
1548 {
1549 --curbuf->b_nwindows;
1550 // Find "aucmd_win", it can't be closed, but it may be in another tab
1551 // page. Do not trigger autocommands here.
1552 block_autocmds();
1553 if (curwin != aucmd_win)
1554 {
1555 tabpage_T *tp;
1556 win_T *wp;
1557
1558 FOR_ALL_TAB_WINDOWS(tp, wp)
1559 {
1560 if (wp == aucmd_win)
1561 {
1562 if (tp != curtab)
1563 goto_tabpage_tp(tp, TRUE, TRUE);
1564 win_goto(aucmd_win);
1565 goto win_found;
1566 }
1567 }
1568 }
1569win_found:
1570
1571 // Remove the window and frame from the tree of frames.
1572 (void)winframe_remove(curwin, &dummy, NULL);
1573 win_remove(curwin, NULL);
1574 aucmd_win_used = FALSE;
1575 last_status(FALSE); // may need to remove last status line
1576
1577 if (!valid_tabpage_win(curtab))
1578 // no valid window in current tabpage
1579 close_tabpage(curtab);
1580
1581 restore_snapshot(SNAP_AUCMD_IDX, FALSE);
1582 (void)win_comp_pos(); // recompute window positions
1583 unblock_autocmds();
1584
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001585 save_curwin = win_find_by_id(aco->save_curwin_id);
1586 if (save_curwin != NULL)
1587 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001588 else
1589 // Hmm, original window disappeared. Just use the first one.
1590 curwin = firstwin;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001591 curbuf = curwin->w_buffer;
1592#ifdef FEAT_JOB_CHANNEL
1593 // May need to restore insert mode for a prompt buffer.
1594 entering_window(curwin);
1595#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001596 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001597#ifdef FEAT_EVAL
1598 vars_clear(&aucmd_win->w_vars->dv_hashtab); // free all w: variables
1599 hash_init(&aucmd_win->w_vars->dv_hashtab); // re-use the hashtab
1600#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001601 vim_free(globaldir);
1602 globaldir = aco->globaldir;
1603
1604 // the buffer contents may have changed
1605 check_cursor();
1606 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1607 {
1608 curwin->w_topline = curbuf->b_ml.ml_line_count;
1609#ifdef FEAT_DIFF
1610 curwin->w_topfill = 0;
1611#endif
1612 }
1613#if defined(FEAT_GUI)
1614 // Hide the scrollbars from the aucmd_win and update.
1615 gui_mch_enable_scrollbar(&aucmd_win->w_scrollbars[SBAR_LEFT], FALSE);
1616 gui_mch_enable_scrollbar(&aucmd_win->w_scrollbars[SBAR_RIGHT], FALSE);
1617 gui_may_update_scrollbars();
1618#endif
1619 }
1620 else
1621 {
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001622 // Restore curwin. Use the window ID, a window may have been closed
1623 // and the memory re-used for another one.
1624 save_curwin = win_find_by_id(aco->save_curwin_id);
1625 if (save_curwin != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001626 {
1627 // Restore the buffer which was previously edited by curwin, if
1628 // it was changed, we are still the same window and the buffer is
1629 // valid.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001630 if (curwin->w_id == aco->new_curwin_id
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001631 && curbuf != aco->new_curbuf.br_buf
1632 && bufref_valid(&aco->new_curbuf)
1633 && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
1634 {
1635# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
1636 if (curwin->w_s == &curbuf->b_s)
1637 curwin->w_s = &aco->new_curbuf.br_buf->b_s;
1638# endif
1639 --curbuf->b_nwindows;
1640 curbuf = aco->new_curbuf.br_buf;
1641 curwin->w_buffer = curbuf;
1642 ++curbuf->b_nwindows;
1643 }
1644
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001645 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001646 curbuf = curwin->w_buffer;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001647 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001648 // In case the autocommand moves the cursor to a position that
1649 // does not exist in curbuf.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001650 check_cursor();
1651 }
1652 }
1653}
1654
1655static int autocmd_nested = FALSE;
1656
1657/*
1658 * Execute autocommands for "event" and file name "fname".
1659 * Return TRUE if some commands were executed.
1660 */
1661 int
1662apply_autocmds(
1663 event_T event,
1664 char_u *fname, // NULL or empty means use actual file name
1665 char_u *fname_io, // fname to use for <afile> on cmdline
1666 int force, // when TRUE, ignore autocmd_busy
1667 buf_T *buf) // buffer for <abuf>
1668{
1669 return apply_autocmds_group(event, fname, fname_io, force,
1670 AUGROUP_ALL, buf, NULL);
1671}
1672
1673/*
1674 * Like apply_autocmds(), but with extra "eap" argument. This takes care of
1675 * setting v:filearg.
1676 */
1677 int
1678apply_autocmds_exarg(
1679 event_T event,
1680 char_u *fname,
1681 char_u *fname_io,
1682 int force,
1683 buf_T *buf,
1684 exarg_T *eap)
1685{
1686 return apply_autocmds_group(event, fname, fname_io, force,
1687 AUGROUP_ALL, buf, eap);
1688}
1689
1690/*
1691 * Like apply_autocmds(), but handles the caller's retval. If the script
1692 * processing is being aborted or if retval is FAIL when inside a try
1693 * conditional, no autocommands are executed. If otherwise the autocommands
1694 * cause the script to be aborted, retval is set to FAIL.
1695 */
1696 int
1697apply_autocmds_retval(
1698 event_T event,
1699 char_u *fname, // NULL or empty means use actual file name
1700 char_u *fname_io, // fname to use for <afile> on cmdline
1701 int force, // when TRUE, ignore autocmd_busy
1702 buf_T *buf, // buffer for <abuf>
1703 int *retval) // pointer to caller's retval
1704{
1705 int did_cmd;
1706
1707#ifdef FEAT_EVAL
1708 if (should_abort(*retval))
1709 return FALSE;
1710#endif
1711
1712 did_cmd = apply_autocmds_group(event, fname, fname_io, force,
1713 AUGROUP_ALL, buf, NULL);
1714 if (did_cmd
1715#ifdef FEAT_EVAL
1716 && aborting()
1717#endif
1718 )
1719 *retval = FAIL;
1720 return did_cmd;
1721}
1722
1723/*
1724 * Return TRUE when there is a CursorHold autocommand defined.
1725 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001726 static int
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001727has_cursorhold(void)
1728{
1729 return (first_autopat[(int)(get_real_state() == NORMAL_BUSY
1730 ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
1731}
1732
1733/*
1734 * Return TRUE if the CursorHold event can be triggered.
1735 */
1736 int
1737trigger_cursorhold(void)
1738{
1739 int state;
1740
1741 if (!did_cursorhold
1742 && has_cursorhold()
1743 && reg_recording == 0
1744 && typebuf.tb_len == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001745 && !ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001746 {
1747 state = get_real_state();
1748 if (state == NORMAL_BUSY || (state & INSERT) != 0)
1749 return TRUE;
1750 }
1751 return FALSE;
1752}
1753
1754/*
1755 * Return TRUE when there is a CursorMoved autocommand defined.
1756 */
1757 int
1758has_cursormoved(void)
1759{
1760 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1761}
1762
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001763/*
1764 * Return TRUE when there is a CursorMovedI autocommand defined.
1765 */
1766 int
1767has_cursormovedI(void)
1768{
1769 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1770}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001771
1772/*
1773 * Return TRUE when there is a TextChanged autocommand defined.
1774 */
1775 int
1776has_textchanged(void)
1777{
1778 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1779}
1780
1781/*
1782 * Return TRUE when there is a TextChangedI autocommand defined.
1783 */
1784 int
1785has_textchangedI(void)
1786{
1787 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
1788}
1789
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001790/*
1791 * Return TRUE when there is a TextChangedP autocommand defined.
1792 */
1793 int
1794has_textchangedP(void)
1795{
1796 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
1797}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001798
1799/*
1800 * Return TRUE when there is an InsertCharPre autocommand defined.
1801 */
1802 int
1803has_insertcharpre(void)
1804{
1805 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
1806}
1807
1808/*
1809 * Return TRUE when there is an CmdUndefined autocommand defined.
1810 */
1811 int
1812has_cmdundefined(void)
1813{
1814 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
1815}
1816
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001817#if defined(FEAT_EVAL) || defined(PROTO)
1818/*
1819 * Return TRUE when there is a TextYankPost autocommand defined.
1820 */
1821 int
1822has_textyankpost(void)
1823{
1824 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
1825}
1826#endif
1827
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001828#if defined(FEAT_EVAL) || defined(PROTO)
1829/*
1830 * Return TRUE when there is a CompleteChanged autocommand defined.
1831 */
1832 int
1833has_completechanged(void)
1834{
1835 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
1836}
1837#endif
1838
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02001839#if defined(FEAT_EVAL) || defined(PROTO)
1840/*
1841 * Return TRUE when there is a ModeChanged autocommand defined.
1842 */
1843 int
1844has_modechanged(void)
1845{
1846 return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
1847}
1848#endif
1849
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001850/*
1851 * Execute autocommands for "event" and file name "fname".
1852 * Return TRUE if some commands were executed.
1853 */
1854 static int
1855apply_autocmds_group(
1856 event_T event,
1857 char_u *fname, // NULL or empty means use actual file name
1858 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
1859 // use fname
1860 int force, // when TRUE, ignore autocmd_busy
1861 int group, // group ID, or AUGROUP_ALL
1862 buf_T *buf, // buffer for <abuf>
1863 exarg_T *eap UNUSED) // command arguments
1864{
1865 char_u *sfname = NULL; // short file name
1866 char_u *tail;
1867 int save_changed;
1868 buf_T *old_curbuf;
1869 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001870 char_u *save_autocmd_fname;
1871 int save_autocmd_fname_full;
1872 int save_autocmd_bufnr;
1873 char_u *save_autocmd_match;
1874 int save_autocmd_busy;
1875 int save_autocmd_nested;
1876 static int nesting = 0;
1877 AutoPatCmd patcmd;
1878 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001879 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001880#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001881 funccal_entry_T funccal_entry;
1882 char_u *save_cmdarg;
1883 long save_cmdbang;
1884#endif
1885 static int filechangeshell_busy = FALSE;
1886#ifdef FEAT_PROFILE
1887 proftime_T wait_time;
1888#endif
1889 int did_save_redobuff = FALSE;
1890 save_redo_T save_redo;
1891 int save_KeyTyped = KeyTyped;
Bram Moolenaare31ee862020-01-07 20:59:34 +01001892 ESTACK_CHECK_DECLARATION
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001893
1894 /*
1895 * Quickly return if there are no autocommands for this event or
1896 * autocommands are blocked.
1897 */
1898 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
1899 || autocmd_blocked > 0)
1900 goto BYPASS_AU;
1901
1902 /*
1903 * When autocommands are busy, new autocommands are only executed when
1904 * explicitly enabled with the "nested" flag.
1905 */
1906 if (autocmd_busy && !(force || autocmd_nested))
1907 goto BYPASS_AU;
1908
1909#ifdef FEAT_EVAL
1910 /*
1911 * Quickly return when immediately aborting on error, or when an interrupt
1912 * occurred or an exception was thrown but not caught.
1913 */
1914 if (aborting())
1915 goto BYPASS_AU;
1916#endif
1917
1918 /*
1919 * FileChangedShell never nests, because it can create an endless loop.
1920 */
1921 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
1922 || event == EVENT_FILECHANGEDSHELLPOST))
1923 goto BYPASS_AU;
1924
1925 /*
1926 * Ignore events in 'eventignore'.
1927 */
1928 if (event_ignored(event))
1929 goto BYPASS_AU;
1930
1931 /*
1932 * Allow nesting of autocommands, but restrict the depth, because it's
1933 * possible to create an endless loop.
1934 */
1935 if (nesting == 10)
1936 {
1937 emsg(_("E218: autocommand nesting too deep"));
1938 goto BYPASS_AU;
1939 }
1940
1941 /*
1942 * Check if these autocommands are disabled. Used when doing ":all" or
1943 * ":ball".
1944 */
1945 if ( (autocmd_no_enter
1946 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
1947 || (autocmd_no_leave
1948 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
1949 goto BYPASS_AU;
1950
1951 /*
1952 * Save the autocmd_* variables and info about the current buffer.
1953 */
1954 save_autocmd_fname = autocmd_fname;
1955 save_autocmd_fname_full = autocmd_fname_full;
1956 save_autocmd_bufnr = autocmd_bufnr;
1957 save_autocmd_match = autocmd_match;
1958 save_autocmd_busy = autocmd_busy;
1959 save_autocmd_nested = autocmd_nested;
1960 save_changed = curbuf->b_changed;
1961 old_curbuf = curbuf;
1962
1963 /*
1964 * Set the file name to be used for <afile>.
1965 * Make a copy to avoid that changing a buffer name or directory makes it
1966 * invalid.
1967 */
1968 if (fname_io == NULL)
1969 {
1970 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02001971 || event == EVENT_OPTIONSET
1972 || event == EVENT_MODECHANGED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001973 autocmd_fname = NULL;
1974 else if (fname != NULL && !ends_excmd(*fname))
1975 autocmd_fname = fname;
1976 else if (buf != NULL)
1977 autocmd_fname = buf->b_ffname;
1978 else
1979 autocmd_fname = NULL;
1980 }
1981 else
1982 autocmd_fname = fname_io;
1983 if (autocmd_fname != NULL)
1984 autocmd_fname = vim_strsave(autocmd_fname);
1985 autocmd_fname_full = FALSE; // call FullName_save() later
1986
1987 /*
1988 * Set the buffer number to be used for <abuf>.
1989 */
1990 if (buf == NULL)
1991 autocmd_bufnr = 0;
1992 else
1993 autocmd_bufnr = buf->b_fnum;
1994
1995 /*
1996 * When the file name is NULL or empty, use the file name of buffer "buf".
1997 * Always use the full path of the file name to match with, in case
1998 * "allow_dirs" is set.
1999 */
2000 if (fname == NULL || *fname == NUL)
2001 {
2002 if (buf == NULL)
2003 fname = NULL;
2004 else
2005 {
2006#ifdef FEAT_SYN_HL
2007 if (event == EVENT_SYNTAX)
2008 fname = buf->b_p_syn;
2009 else
2010#endif
2011 if (event == EVENT_FILETYPE)
2012 fname = buf->b_p_ft;
2013 else
2014 {
2015 if (buf->b_sfname != NULL)
2016 sfname = vim_strsave(buf->b_sfname);
2017 fname = buf->b_ffname;
2018 }
2019 }
2020 if (fname == NULL)
2021 fname = (char_u *)"";
2022 fname = vim_strsave(fname); // make a copy, so we can change it
2023 }
2024 else
2025 {
2026 sfname = vim_strsave(fname);
2027 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
2028 // ColorScheme, QuickFixCmd* or DirChanged
2029 if (event == EVENT_FILETYPE
2030 || event == EVENT_SYNTAX
2031 || event == EVENT_CMDLINECHANGED
2032 || event == EVENT_CMDLINEENTER
2033 || event == EVENT_CMDLINELEAVE
2034 || event == EVENT_CMDWINENTER
2035 || event == EVENT_CMDWINLEAVE
2036 || event == EVENT_CMDUNDEFINED
2037 || event == EVENT_FUNCUNDEFINED
2038 || event == EVENT_REMOTEREPLY
2039 || event == EVENT_SPELLFILEMISSING
2040 || event == EVENT_QUICKFIXCMDPRE
2041 || event == EVENT_COLORSCHEME
2042 || event == EVENT_COLORSCHEMEPRE
2043 || event == EVENT_OPTIONSET
2044 || event == EVENT_QUICKFIXCMDPOST
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002045 || event == EVENT_DIRCHANGED
naohiro ono23beefe2021-11-13 12:38:49 +00002046 || event == EVENT_MODECHANGED
2047 || event == EVENT_WINCLOSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002048 {
2049 fname = vim_strsave(fname);
2050 autocmd_fname_full = TRUE; // don't expand it later
2051 }
2052 else
2053 fname = FullName_save(fname, FALSE);
2054 }
2055 if (fname == NULL) // out of memory
2056 {
2057 vim_free(sfname);
2058 retval = FALSE;
2059 goto BYPASS_AU;
2060 }
2061
2062#ifdef BACKSLASH_IN_FILENAME
2063 /*
2064 * Replace all backslashes with forward slashes. This makes the
2065 * autocommand patterns portable between Unix and MS-DOS.
2066 */
2067 if (sfname != NULL)
2068 forward_slash(sfname);
2069 forward_slash(fname);
2070#endif
2071
2072#ifdef VMS
2073 // remove version for correct match
2074 if (sfname != NULL)
2075 vms_remove_version(sfname);
2076 vms_remove_version(fname);
2077#endif
2078
2079 /*
2080 * Set the name to be used for <amatch>.
2081 */
2082 autocmd_match = fname;
2083
2084
2085 // Don't redraw while doing autocommands.
2086 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002087
2088 // name and lnum are filled in later
2089 estack_push(ETYPE_AUCMD, NULL, 0);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002090 ESTACK_CHECK_SETUP
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002091
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002092 save_current_sctx = current_sctx;
2093
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002094#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002095# ifdef FEAT_PROFILE
2096 if (do_profiling == PROF_YES)
2097 prof_child_enter(&wait_time); // doesn't count for the caller itself
2098# endif
2099
2100 // Don't use local function variables, if called from a function.
2101 save_funccal(&funccal_entry);
2102#endif
2103
2104 /*
2105 * When starting to execute autocommands, save the search patterns.
2106 */
2107 if (!autocmd_busy)
2108 {
2109 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002110 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002111 {
2112 saveRedobuff(&save_redo);
2113 did_save_redobuff = TRUE;
2114 }
2115 did_filetype = keep_filetype;
2116 }
2117
2118 /*
2119 * Note that we are applying autocmds. Some commands need to know.
2120 */
2121 autocmd_busy = TRUE;
2122 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2123 ++nesting; // see matching decrement below
2124
2125 // Remember that FileType was triggered. Used for did_filetype().
2126 if (event == EVENT_FILETYPE)
2127 did_filetype = TRUE;
2128
2129 tail = gettail(fname);
2130
2131 // Find first autocommand that matches
2132 patcmd.curpat = first_autopat[(int)event];
2133 patcmd.nextcmd = NULL;
2134 patcmd.group = group;
2135 patcmd.fname = fname;
2136 patcmd.sfname = sfname;
2137 patcmd.tail = tail;
2138 patcmd.event = event;
2139 patcmd.arg_bufnr = autocmd_bufnr;
2140 patcmd.next = NULL;
2141 auto_next_pat(&patcmd, FALSE);
2142
2143 // found one, start executing the autocommands
2144 if (patcmd.curpat != NULL)
2145 {
2146 // add to active_apc_list
2147 patcmd.next = active_apc_list;
2148 active_apc_list = &patcmd;
2149
2150#ifdef FEAT_EVAL
2151 // set v:cmdarg (only when there is a matching pattern)
2152 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2153 if (eap != NULL)
2154 {
2155 save_cmdarg = set_cmdarg(eap, NULL);
2156 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2157 }
2158 else
2159 save_cmdarg = NULL; // avoid gcc warning
2160#endif
2161 retval = TRUE;
2162 // mark the last pattern, to avoid an endless loop when more patterns
2163 // are added when executing autocommands
2164 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2165 ap->last = FALSE;
2166 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002167
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002168 if (nesting == 1)
2169 // make sure cursor and topline are valid
2170 check_lnums(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002171
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002172 do_cmdline(NULL, getnextac, (void *)&patcmd,
2173 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002174
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002175 if (nesting == 1)
2176 // restore cursor and topline, unless they were changed
2177 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002178
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002179#ifdef FEAT_EVAL
2180 if (eap != NULL)
2181 {
2182 (void)set_cmdarg(NULL, save_cmdarg);
2183 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2184 }
2185#endif
2186 // delete from active_apc_list
2187 if (active_apc_list == &patcmd) // just in case
2188 active_apc_list = patcmd.next;
2189 }
2190
2191 --RedrawingDisabled;
2192 autocmd_busy = save_autocmd_busy;
2193 filechangeshell_busy = FALSE;
2194 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002195 vim_free(SOURCING_NAME);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002196 ESTACK_CHECK_NOW
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002197 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002198 vim_free(autocmd_fname);
2199 autocmd_fname = save_autocmd_fname;
2200 autocmd_fname_full = save_autocmd_fname_full;
2201 autocmd_bufnr = save_autocmd_bufnr;
2202 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002203 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002204#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002205 restore_funccal();
2206# ifdef FEAT_PROFILE
2207 if (do_profiling == PROF_YES)
2208 prof_child_exit(&wait_time);
2209# endif
2210#endif
2211 KeyTyped = save_KeyTyped;
2212 vim_free(fname);
2213 vim_free(sfname);
2214 --nesting; // see matching increment above
2215
2216 /*
2217 * When stopping to execute autocommands, restore the search patterns and
2218 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2219 * free any windows in the au_pending_free_win list.
2220 */
2221 if (!autocmd_busy)
2222 {
2223 restore_search_patterns();
2224 if (did_save_redobuff)
2225 restoreRedobuff(&save_redo);
2226 did_filetype = FALSE;
2227 while (au_pending_free_buf != NULL)
2228 {
2229 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002230
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002231 vim_free(au_pending_free_buf);
2232 au_pending_free_buf = b;
2233 }
2234 while (au_pending_free_win != NULL)
2235 {
2236 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002237
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002238 vim_free(au_pending_free_win);
2239 au_pending_free_win = w;
2240 }
2241 }
2242
2243 /*
2244 * Some events don't set or reset the Changed flag.
2245 * Check if still in the same buffer!
2246 */
2247 if (curbuf == old_curbuf
2248 && (event == EVENT_BUFREADPOST
2249 || event == EVENT_BUFWRITEPOST
2250 || event == EVENT_FILEAPPENDPOST
2251 || event == EVENT_VIMLEAVE
2252 || event == EVENT_VIMLEAVEPRE))
2253 {
2254#ifdef FEAT_TITLE
2255 if (curbuf->b_changed != save_changed)
2256 need_maketitle = TRUE;
2257#endif
2258 curbuf->b_changed = save_changed;
2259 }
2260
2261 au_cleanup(); // may really delete removed patterns/commands now
2262
2263BYPASS_AU:
2264 // When wiping out a buffer make sure all its buffer-local autocommands
2265 // are deleted.
2266 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2267 aubuflocal_remove(buf);
2268
2269 if (retval == OK && event == EVENT_FILETYPE)
2270 au_did_filetype = TRUE;
2271
2272 return retval;
2273}
2274
2275# ifdef FEAT_EVAL
2276static char_u *old_termresponse = NULL;
2277# endif
2278
2279/*
2280 * Block triggering autocommands until unblock_autocmd() is called.
2281 * Can be used recursively, so long as it's symmetric.
2282 */
2283 void
2284block_autocmds(void)
2285{
2286# ifdef FEAT_EVAL
2287 // Remember the value of v:termresponse.
2288 if (autocmd_blocked == 0)
2289 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
2290# endif
2291 ++autocmd_blocked;
2292}
2293
2294 void
2295unblock_autocmds(void)
2296{
2297 --autocmd_blocked;
2298
2299# ifdef FEAT_EVAL
2300 // When v:termresponse was set while autocommands were blocked, trigger
2301 // the autocommands now. Esp. useful when executing a shell command
2302 // during startup (vimdiff).
2303 if (autocmd_blocked == 0
2304 && get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2305 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2306# endif
2307}
2308
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002309 int
2310is_autocmd_blocked(void)
2311{
2312 return autocmd_blocked != 0;
2313}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002314
2315/*
2316 * Find next autocommand pattern that matches.
2317 */
2318 static void
2319auto_next_pat(
2320 AutoPatCmd *apc,
2321 int stop_at_last) // stop when 'last' flag is set
2322{
2323 AutoPat *ap;
2324 AutoCmd *cp;
2325 char_u *name;
2326 char *s;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002327 char_u **sourcing_namep = &SOURCING_NAME;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002328
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002329 VIM_CLEAR(*sourcing_namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002330
2331 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2332 {
2333 apc->curpat = NULL;
2334
2335 // Only use a pattern when it has not been removed, has commands and
2336 // the group matches. For buffer-local autocommands only check the
2337 // buffer number.
2338 if (ap->pat != NULL && ap->cmds != NULL
2339 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2340 {
2341 // execution-condition
2342 if (ap->buflocal_nr == 0
2343 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2344 apc->sfname, apc->tail, ap->allow_dirs))
2345 : ap->buflocal_nr == apc->arg_bufnr)
2346 {
2347 name = event_nr2name(apc->event);
2348 s = _("%s Autocommands for \"%s\"");
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002349 *sourcing_namep = alloc(STRLEN(s)
Bram Moolenaar964b3742019-05-24 18:54:09 +02002350 + STRLEN(name) + ap->patlen + 1);
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002351 if (*sourcing_namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002352 {
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002353 sprintf((char *)*sourcing_namep, s,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002354 (char *)name, (char *)ap->pat);
2355 if (p_verbose >= 8)
2356 {
2357 verbose_enter();
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002358 smsg(_("Executing %s"), *sourcing_namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002359 verbose_leave();
2360 }
2361 }
2362
2363 apc->curpat = ap;
2364 apc->nextcmd = ap->cmds;
2365 // mark last command
2366 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2367 cp->last = FALSE;
2368 cp->last = TRUE;
2369 }
2370 line_breakcheck();
2371 if (apc->curpat != NULL) // found a match
2372 break;
2373 }
2374 if (stop_at_last && ap->last)
2375 break;
2376 }
2377}
2378
2379/*
2380 * Get next autocommand command.
2381 * Called by do_cmdline() to get the next line for ":if".
2382 * Returns allocated string, or NULL for end of autocommands.
2383 */
2384 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002385getnextac(
2386 int c UNUSED,
2387 void *cookie,
2388 int indent UNUSED,
2389 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002390{
2391 AutoPatCmd *acp = (AutoPatCmd *)cookie;
2392 char_u *retval;
2393 AutoCmd *ac;
2394
2395 // Can be called again after returning the last line.
2396 if (acp->curpat == NULL)
2397 return NULL;
2398
2399 // repeat until we find an autocommand to execute
2400 for (;;)
2401 {
2402 // skip removed commands
2403 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2404 if (acp->nextcmd->last)
2405 acp->nextcmd = NULL;
2406 else
2407 acp->nextcmd = acp->nextcmd->next;
2408
2409 if (acp->nextcmd != NULL)
2410 break;
2411
2412 // at end of commands, find next pattern that matches
2413 if (acp->curpat->last)
2414 acp->curpat = NULL;
2415 else
2416 acp->curpat = acp->curpat->next;
2417 if (acp->curpat != NULL)
2418 auto_next_pat(acp, TRUE);
2419 if (acp->curpat == NULL)
2420 return NULL;
2421 }
2422
2423 ac = acp->nextcmd;
2424
2425 if (p_verbose >= 9)
2426 {
2427 verbose_enter_scroll();
2428 smsg(_("autocommand %s"), ac->cmd);
2429 msg_puts("\n"); // don't overwrite this either
2430 verbose_leave_scroll();
2431 }
2432 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002433 // Remove one-shot ("once") autocmd in anticipation of its execution.
2434 if (ac->once)
2435 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002436 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002437 current_sctx = ac->script_ctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002438 if (ac->last)
2439 acp->nextcmd = NULL;
2440 else
2441 acp->nextcmd = ac->next;
2442 return retval;
2443}
2444
2445/*
2446 * Return TRUE if there is a matching autocommand for "fname".
2447 * To account for buffer-local autocommands, function needs to know
2448 * in which buffer the file will be opened.
2449 */
2450 int
2451has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2452{
2453 AutoPat *ap;
2454 char_u *fname;
2455 char_u *tail = gettail(sfname);
2456 int retval = FALSE;
2457
2458 fname = FullName_save(sfname, FALSE);
2459 if (fname == NULL)
2460 return FALSE;
2461
2462#ifdef BACKSLASH_IN_FILENAME
2463 /*
2464 * Replace all backslashes with forward slashes. This makes the
2465 * autocommand patterns portable between Unix and MS-DOS.
2466 */
2467 sfname = vim_strsave(sfname);
2468 if (sfname != NULL)
2469 forward_slash(sfname);
2470 forward_slash(fname);
2471#endif
2472
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002473 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002474 if (ap->pat != NULL && ap->cmds != NULL
2475 && (ap->buflocal_nr == 0
2476 ? match_file_pat(NULL, &ap->reg_prog,
2477 fname, sfname, tail, ap->allow_dirs)
2478 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2479 ))
2480 {
2481 retval = TRUE;
2482 break;
2483 }
2484
2485 vim_free(fname);
2486#ifdef BACKSLASH_IN_FILENAME
2487 vim_free(sfname);
2488#endif
2489
2490 return retval;
2491}
2492
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002493/*
2494 * Function given to ExpandGeneric() to obtain the list of autocommand group
2495 * names.
2496 */
2497 char_u *
2498get_augroup_name(expand_T *xp UNUSED, int idx)
2499{
2500 if (idx == augroups.ga_len) // add "END" add the end
2501 return (char_u *)"END";
2502 if (idx >= augroups.ga_len) // end of list
2503 return NULL;
2504 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2505 // skip deleted entries
2506 return (char_u *)"";
2507 return AUGROUP_NAME(idx); // return a name
2508}
2509
2510static int include_groups = FALSE;
2511
2512 char_u *
2513set_context_in_autocmd(
2514 expand_T *xp,
2515 char_u *arg,
2516 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2517{
2518 char_u *p;
2519 int group;
2520
2521 // check for a group name, skip it if present
2522 include_groups = FALSE;
2523 p = arg;
2524 group = au_get_grouparg(&arg);
2525 if (group == AUGROUP_ERROR)
2526 return NULL;
2527 // If there only is a group name that's what we expand.
2528 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2529 {
2530 arg = p;
2531 group = AUGROUP_ALL;
2532 }
2533
2534 // skip over event name
2535 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2536 if (*p == ',')
2537 arg = p + 1;
2538 if (*p == NUL)
2539 {
2540 if (group == AUGROUP_ALL)
2541 include_groups = TRUE;
2542 xp->xp_context = EXPAND_EVENTS; // expand event name
2543 xp->xp_pattern = arg;
2544 return NULL;
2545 }
2546
2547 // skip over pattern
2548 arg = skipwhite(p);
2549 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2550 arg++;
2551 if (*arg)
2552 return arg; // expand (next) command
2553
2554 if (doautocmd)
2555 xp->xp_context = EXPAND_FILES; // expand file names
2556 else
2557 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2558 return NULL;
2559}
2560
2561/*
2562 * Function given to ExpandGeneric() to obtain the list of event names.
2563 */
2564 char_u *
2565get_event_name(expand_T *xp UNUSED, int idx)
2566{
2567 if (idx < augroups.ga_len) // First list group names, if wanted
2568 {
2569 if (!include_groups || AUGROUP_NAME(idx) == NULL
2570 || AUGROUP_NAME(idx) == get_deleted_augroup())
2571 return (char_u *)""; // skip deleted entries
2572 return AUGROUP_NAME(idx); // return a name
2573 }
2574 return (char_u *)event_names[idx - augroups.ga_len].name;
2575}
2576
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002577
2578#if defined(FEAT_EVAL) || defined(PROTO)
2579/*
2580 * Return TRUE if autocmd is supported.
2581 */
2582 int
2583autocmd_supported(char_u *name)
2584{
2585 char_u *p;
2586
2587 return (event_name2nr(name, &p) != NUM_EVENTS);
2588}
2589
2590/*
2591 * Return TRUE if an autocommand is defined for a group, event and
2592 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2593 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2594 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2595 * Used for:
2596 * exists("#Group") or
2597 * exists("#Group#Event") or
2598 * exists("#Group#Event#pat") or
2599 * exists("#Event") or
2600 * exists("#Event#pat")
2601 */
2602 int
2603au_exists(char_u *arg)
2604{
2605 char_u *arg_save;
2606 char_u *pattern = NULL;
2607 char_u *event_name;
2608 char_u *p;
2609 event_T event;
2610 AutoPat *ap;
2611 buf_T *buflocal_buf = NULL;
2612 int group;
2613 int retval = FALSE;
2614
2615 // Make a copy so that we can change the '#' chars to a NUL.
2616 arg_save = vim_strsave(arg);
2617 if (arg_save == NULL)
2618 return FALSE;
2619 p = vim_strchr(arg_save, '#');
2620 if (p != NULL)
2621 *p++ = NUL;
2622
2623 // First, look for an autocmd group name
2624 group = au_find_group(arg_save);
2625 if (group == AUGROUP_ERROR)
2626 {
2627 // Didn't match a group name, assume the first argument is an event.
2628 group = AUGROUP_ALL;
2629 event_name = arg_save;
2630 }
2631 else
2632 {
2633 if (p == NULL)
2634 {
2635 // "Group": group name is present and it's recognized
2636 retval = TRUE;
2637 goto theend;
2638 }
2639
2640 // Must be "Group#Event" or "Group#Event#pat".
2641 event_name = p;
2642 p = vim_strchr(event_name, '#');
2643 if (p != NULL)
2644 *p++ = NUL; // "Group#Event#pat"
2645 }
2646
2647 pattern = p; // "pattern" is NULL when there is no pattern
2648
2649 // find the index (enum) for the event name
2650 event = event_name2nr(event_name, &p);
2651
2652 // return FALSE if the event name is not recognized
2653 if (event == NUM_EVENTS)
2654 goto theend;
2655
2656 // Find the first autocommand for this event.
2657 // If there isn't any, return FALSE;
2658 // If there is one and no pattern given, return TRUE;
2659 ap = first_autopat[(int)event];
2660 if (ap == NULL)
2661 goto theend;
2662
2663 // if pattern is "<buffer>", special handling is needed which uses curbuf
2664 // for pattern "<buffer=N>, fnamecmp() will work fine
2665 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2666 buflocal_buf = curbuf;
2667
2668 // Check if there is an autocommand with the given pattern.
2669 for ( ; ap != NULL; ap = ap->next)
2670 // only use a pattern when it has not been removed and has commands.
2671 // For buffer-local autocommands, fnamecmp() works fine.
2672 if (ap->pat != NULL && ap->cmds != NULL
2673 && (group == AUGROUP_ALL || ap->group == group)
2674 && (pattern == NULL
2675 || (buflocal_buf == NULL
2676 ? fnamecmp(ap->pat, pattern) == 0
2677 : ap->buflocal_nr == buflocal_buf->b_fnum)))
2678 {
2679 retval = TRUE;
2680 break;
2681 }
2682
2683theend:
2684 vim_free(arg_save);
2685 return retval;
2686}
2687#endif