blob: 2e3026217a8f13e4e16d2c2ba99d92c259f576a0 [file] [log] [blame]
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * autocmd.c: Autocommand related functions
12 */
13
14#include "vim.h"
15
16/*
17 * The autocommands are stored in a list for each event.
18 * Autocommands for the same pattern, that are consecutive, are joined
19 * together, to avoid having to match the pattern too often.
20 * The result is an array of Autopat lists, which point to AutoCmd lists:
21 *
22 * last_autopat[0] -----------------------------+
23 * V
24 * first_autopat[0] --> Autopat.next --> Autopat.next --> NULL
25 * Autopat.cmds Autopat.cmds
26 * | |
27 * V V
28 * AutoCmd.next AutoCmd.next
29 * | |
30 * V V
31 * AutoCmd.next NULL
32 * |
33 * V
34 * NULL
35 *
36 * last_autopat[1] --------+
37 * V
38 * first_autopat[1] --> Autopat.next --> NULL
39 * Autopat.cmds
40 * |
41 * V
42 * AutoCmd.next
43 * |
44 * V
45 * NULL
46 * etc.
47 *
48 * The order of AutoCmds is important, this is the order in which they were
49 * defined and will have to be executed.
50 */
51typedef struct AutoCmd
52{
53 char_u *cmd; // The command to be executed (NULL
54 // when command has been removed).
Bram Moolenaareb93f3f2019-04-04 15:04:56 +020055 char once; // "One shot": removed after execution
Bram Moolenaar3e460fd2019-01-26 16:21:07 +010056 char nested; // If autocommands nest here.
57 char last; // last command in list
LemonBoyeca7c602022-04-14 15:39:43 +010058 sctx_T script_ctx; // script context where it is defined
Bram Moolenaar3e460fd2019-01-26 16:21:07 +010059 struct AutoCmd *next; // next AutoCmd in list
60} AutoCmd;
61
62typedef struct AutoPat
63{
64 struct AutoPat *next; // Next AutoPat in AutoPat list; MUST
65 // be the first entry.
66 char_u *pat; // pattern as typed (NULL when pattern
67 // has been removed)
68 regprog_T *reg_prog; // compiled regprog for pattern
69 AutoCmd *cmds; // list of commands to do
70 int group; // group ID
71 int patlen; // strlen() of pat
72 int buflocal_nr; // !=0 for buffer-local AutoPat
73 char allow_dirs; // Pattern may match whole path
74 char last; // last pattern for apply_autocmds()
75} AutoPat;
76
77static struct event_name
78{
79 char *name; // event name
80 event_T event; // event number
81} event_names[] =
82{
83 {"BufAdd", EVENT_BUFADD},
84 {"BufCreate", EVENT_BUFADD},
85 {"BufDelete", EVENT_BUFDELETE},
86 {"BufEnter", EVENT_BUFENTER},
87 {"BufFilePost", EVENT_BUFFILEPOST},
88 {"BufFilePre", EVENT_BUFFILEPRE},
89 {"BufHidden", EVENT_BUFHIDDEN},
90 {"BufLeave", EVENT_BUFLEAVE},
91 {"BufNew", EVENT_BUFNEW},
92 {"BufNewFile", EVENT_BUFNEWFILE},
93 {"BufRead", EVENT_BUFREADPOST},
94 {"BufReadCmd", EVENT_BUFREADCMD},
95 {"BufReadPost", EVENT_BUFREADPOST},
96 {"BufReadPre", EVENT_BUFREADPRE},
97 {"BufUnload", EVENT_BUFUNLOAD},
98 {"BufWinEnter", EVENT_BUFWINENTER},
99 {"BufWinLeave", EVENT_BUFWINLEAVE},
100 {"BufWipeout", EVENT_BUFWIPEOUT},
101 {"BufWrite", EVENT_BUFWRITEPRE},
102 {"BufWritePost", EVENT_BUFWRITEPOST},
103 {"BufWritePre", EVENT_BUFWRITEPRE},
104 {"BufWriteCmd", EVENT_BUFWRITECMD},
105 {"CmdlineChanged", EVENT_CMDLINECHANGED},
106 {"CmdlineEnter", EVENT_CMDLINEENTER},
107 {"CmdlineLeave", EVENT_CMDLINELEAVE},
108 {"CmdwinEnter", EVENT_CMDWINENTER},
109 {"CmdwinLeave", EVENT_CMDWINLEAVE},
110 {"CmdUndefined", EVENT_CMDUNDEFINED},
111 {"ColorScheme", EVENT_COLORSCHEME},
112 {"ColorSchemePre", EVENT_COLORSCHEMEPRE},
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200113 {"CompleteChanged", EVENT_COMPLETECHANGED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100114 {"CompleteDone", EVENT_COMPLETEDONE},
Bram Moolenaar3f169ce2020-01-26 22:43:31 +0100115 {"CompleteDonePre", EVENT_COMPLETEDONEPRE},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100116 {"CursorHold", EVENT_CURSORHOLD},
117 {"CursorHoldI", EVENT_CURSORHOLDI},
118 {"CursorMoved", EVENT_CURSORMOVED},
119 {"CursorMovedI", EVENT_CURSORMOVEDI},
120 {"DiffUpdated", EVENT_DIFFUPDATED},
121 {"DirChanged", EVENT_DIRCHANGED},
Bram Moolenaar28e8f732022-02-09 12:58:20 +0000122 {"DirChangedPre", EVENT_DIRCHANGEDPRE},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100123 {"EncodingChanged", EVENT_ENCODINGCHANGED},
124 {"ExitPre", EVENT_EXITPRE},
125 {"FileEncoding", EVENT_ENCODINGCHANGED},
126 {"FileAppendPost", EVENT_FILEAPPENDPOST},
127 {"FileAppendPre", EVENT_FILEAPPENDPRE},
128 {"FileAppendCmd", EVENT_FILEAPPENDCMD},
129 {"FileChangedShell",EVENT_FILECHANGEDSHELL},
130 {"FileChangedShellPost",EVENT_FILECHANGEDSHELLPOST},
131 {"FileChangedRO", EVENT_FILECHANGEDRO},
132 {"FileReadPost", EVENT_FILEREADPOST},
133 {"FileReadPre", EVENT_FILEREADPRE},
134 {"FileReadCmd", EVENT_FILEREADCMD},
135 {"FileType", EVENT_FILETYPE},
136 {"FileWritePost", EVENT_FILEWRITEPOST},
137 {"FileWritePre", EVENT_FILEWRITEPRE},
138 {"FileWriteCmd", EVENT_FILEWRITECMD},
139 {"FilterReadPost", EVENT_FILTERREADPOST},
140 {"FilterReadPre", EVENT_FILTERREADPRE},
141 {"FilterWritePost", EVENT_FILTERWRITEPOST},
142 {"FilterWritePre", EVENT_FILTERWRITEPRE},
143 {"FocusGained", EVENT_FOCUSGAINED},
144 {"FocusLost", EVENT_FOCUSLOST},
145 {"FuncUndefined", EVENT_FUNCUNDEFINED},
146 {"GUIEnter", EVENT_GUIENTER},
147 {"GUIFailed", EVENT_GUIFAILED},
148 {"InsertChange", EVENT_INSERTCHANGE},
149 {"InsertEnter", EVENT_INSERTENTER},
150 {"InsertLeave", EVENT_INSERTLEAVE},
Bram Moolenaarb53e13a2020-10-21 12:19:53 +0200151 {"InsertLeavePre", EVENT_INSERTLEAVEPRE},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100152 {"InsertCharPre", EVENT_INSERTCHARPRE},
153 {"MenuPopup", EVENT_MENUPOPUP},
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +0200154 {"ModeChanged", EVENT_MODECHANGED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100155 {"OptionSet", EVENT_OPTIONSET},
156 {"QuickFixCmdPost", EVENT_QUICKFIXCMDPOST},
157 {"QuickFixCmdPre", EVENT_QUICKFIXCMDPRE},
158 {"QuitPre", EVENT_QUITPRE},
159 {"RemoteReply", EVENT_REMOTEREPLY},
Bram Moolenaar8aeec402019-09-15 23:02:04 +0200160 {"SafeState", EVENT_SAFESTATE},
Bram Moolenaar69198cb2019-09-16 21:58:13 +0200161 {"SafeStateAgain", EVENT_SAFESTATEAGAIN},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100162 {"SessionLoadPost", EVENT_SESSIONLOADPOST},
163 {"ShellCmdPost", EVENT_SHELLCMDPOST},
164 {"ShellFilterPost", EVENT_SHELLFILTERPOST},
Bram Moolenaarbe5ee862020-06-10 20:56:58 +0200165 {"SigUSR1", EVENT_SIGUSR1},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100166 {"SourceCmd", EVENT_SOURCECMD},
167 {"SourcePre", EVENT_SOURCEPRE},
168 {"SourcePost", EVENT_SOURCEPOST},
169 {"SpellFileMissing",EVENT_SPELLFILEMISSING},
170 {"StdinReadPost", EVENT_STDINREADPOST},
171 {"StdinReadPre", EVENT_STDINREADPRE},
172 {"SwapExists", EVENT_SWAPEXISTS},
173 {"Syntax", EVENT_SYNTAX},
174 {"TabNew", EVENT_TABNEW},
175 {"TabClosed", EVENT_TABCLOSED},
176 {"TabEnter", EVENT_TABENTER},
177 {"TabLeave", EVENT_TABLEAVE},
178 {"TermChanged", EVENT_TERMCHANGED},
179 {"TerminalOpen", EVENT_TERMINALOPEN},
Bram Moolenaar28ed4df2019-10-26 16:21:40 +0200180 {"TerminalWinOpen", EVENT_TERMINALWINOPEN},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100181 {"TermResponse", EVENT_TERMRESPONSE},
182 {"TextChanged", EVENT_TEXTCHANGED},
183 {"TextChangedI", EVENT_TEXTCHANGEDI},
184 {"TextChangedP", EVENT_TEXTCHANGEDP},
185 {"User", EVENT_USER},
186 {"VimEnter", EVENT_VIMENTER},
187 {"VimLeave", EVENT_VIMLEAVE},
188 {"VimLeavePre", EVENT_VIMLEAVEPRE},
189 {"WinNew", EVENT_WINNEW},
naohiro ono23beefe2021-11-13 12:38:49 +0000190 {"WinClosed", EVENT_WINCLOSED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100191 {"WinEnter", EVENT_WINENTER},
192 {"WinLeave", EVENT_WINLEAVE},
LemonBoy09371822022-04-08 15:18:45 +0100193 {"WinScrolled", EVENT_WINSCROLLED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100194 {"VimResized", EVENT_VIMRESIZED},
195 {"TextYankPost", EVENT_TEXTYANKPOST},
Bram Moolenaar100118c2020-12-11 19:30:34 +0100196 {"VimSuspend", EVENT_VIMSUSPEND},
197 {"VimResume", EVENT_VIMRESUME},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100198 {NULL, (event_T)0}
199};
200
201static AutoPat *first_autopat[NUM_EVENTS] =
202{
203 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
204 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
205 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
206 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
207 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
208 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
209};
210
211static AutoPat *last_autopat[NUM_EVENTS] =
212{
213 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
214 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
215 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
216 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
217 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
218 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
219};
220
kylo252ae6f1d82022-02-16 19:24:07 +0000221#define AUGROUP_DEFAULT (-1) // default autocmd group
222#define AUGROUP_ERROR (-2) // erroneous autocmd group
223#define AUGROUP_ALL (-3) // all autocmd groups
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100224
225/*
226 * struct used to keep status while executing autocommands for an event.
227 */
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100228struct AutoPatCmd_S
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100229{
230 AutoPat *curpat; // next AutoPat to examine
231 AutoCmd *nextcmd; // next AutoCmd to execute
232 int group; // group being used
233 char_u *fname; // fname to match with
234 char_u *sfname; // sfname to match with
235 char_u *tail; // tail of fname
236 event_T event; // current event
LemonBoyeca7c602022-04-14 15:39:43 +0100237 sctx_T script_ctx; // script context where it is defined
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100238 int arg_bufnr; // Initially equal to <abuf>, set to zero when
239 // buf is deleted.
LemonBoyeca7c602022-04-14 15:39:43 +0100240 AutoPatCmd_T *next; // chain of active apc-s for auto-invalidation
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100241};
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100242
LemonBoyeca7c602022-04-14 15:39:43 +0100243static AutoPatCmd_T *active_apc_list = NULL; // stack of active autocommands
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100244
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200245// Macro to loop over all the patterns for an autocmd event
246#define FOR_ALL_AUTOCMD_PATTERNS(event, ap) \
247 for ((ap) = first_autopat[(int)(event)]; (ap) != NULL; (ap) = (ap)->next)
248
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100249/*
250 * augroups stores a list of autocmd group names.
251 */
252static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
253#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
Bram Moolenaarc667da52019-11-30 20:52:27 +0100254// use get_deleted_augroup() to get this
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100255static char_u *deleted_augroup = NULL;
256
257/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100258 * The ID of the current group. Group 0 is the default one.
259 */
260static int current_augroup = AUGROUP_DEFAULT;
261
Bram Moolenaarc667da52019-11-30 20:52:27 +0100262static int au_need_clean = FALSE; // need to delete marked patterns
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100263
264static char_u *event_nr2name(event_T event);
265static int au_get_grouparg(char_u **argp);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200266static int do_autocmd_event(event_T event, char_u *pat, int once, int nested, char_u *cmd, int forceit, int group, int flags);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100267static int apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, int force, int group, buf_T *buf, exarg_T *eap);
LemonBoyeca7c602022-04-14 15:39:43 +0100268static void auto_next_pat(AutoPatCmd_T *apc, int stop_at_last);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100269static int au_find_group(char_u *name);
270
271static event_T last_event;
272static int last_group;
Bram Moolenaarc667da52019-11-30 20:52:27 +0100273static int autocmd_blocked = 0; // block all autocmds
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100274
275 static char_u *
276get_deleted_augroup(void)
277{
278 if (deleted_augroup == NULL)
279 deleted_augroup = (char_u *)_("--Deleted--");
280 return deleted_augroup;
281}
282
283/*
284 * Show the autocommands for one AutoPat.
285 */
286 static void
287show_autocmd(AutoPat *ap, event_T event)
288{
289 AutoCmd *ac;
290
291 // Check for "got_int" (here and at various places below), which is set
292 // when "q" has been hit for the "--more--" prompt
293 if (got_int)
294 return;
295 if (ap->pat == NULL) // pattern has been removed
296 return;
297
298 msg_putchar('\n');
299 if (got_int)
300 return;
301 if (event != last_event || ap->group != last_group)
302 {
303 if (ap->group != AUGROUP_DEFAULT)
304 {
305 if (AUGROUP_NAME(ap->group) == NULL)
306 msg_puts_attr((char *)get_deleted_augroup(), HL_ATTR(HLF_E));
307 else
308 msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
309 msg_puts(" ");
310 }
311 msg_puts_attr((char *)event_nr2name(event), HL_ATTR(HLF_T));
312 last_event = event;
313 last_group = ap->group;
314 msg_putchar('\n');
315 if (got_int)
316 return;
317 }
318 msg_col = 4;
319 msg_outtrans(ap->pat);
320
321 for (ac = ap->cmds; ac != NULL; ac = ac->next)
322 {
323 if (ac->cmd != NULL) // skip removed commands
324 {
325 if (msg_col >= 14)
326 msg_putchar('\n');
327 msg_col = 14;
328 if (got_int)
329 return;
330 msg_outtrans(ac->cmd);
331#ifdef FEAT_EVAL
332 if (p_verbose > 0)
333 last_set_msg(ac->script_ctx);
334#endif
335 if (got_int)
336 return;
337 if (ac->next != NULL)
338 {
339 msg_putchar('\n');
340 if (got_int)
341 return;
342 }
343 }
344 }
345}
346
347/*
348 * Mark an autocommand pattern for deletion.
349 */
350 static void
351au_remove_pat(AutoPat *ap)
352{
353 VIM_CLEAR(ap->pat);
354 ap->buflocal_nr = -1;
355 au_need_clean = TRUE;
356}
357
358/*
359 * Mark all commands for a pattern for deletion.
360 */
361 static void
362au_remove_cmds(AutoPat *ap)
363{
364 AutoCmd *ac;
365
366 for (ac = ap->cmds; ac != NULL; ac = ac->next)
367 VIM_CLEAR(ac->cmd);
368 au_need_clean = TRUE;
369}
370
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200371// Delete one command from an autocmd pattern.
372static void au_del_cmd(AutoCmd *ac)
373{
374 VIM_CLEAR(ac->cmd);
375 au_need_clean = TRUE;
376}
377
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100378/*
379 * Cleanup autocommands and patterns that have been deleted.
380 * This is only done when not executing autocommands.
381 */
382 static void
383au_cleanup(void)
384{
385 AutoPat *ap, **prev_ap;
386 AutoCmd *ac, **prev_ac;
387 event_T event;
388
389 if (autocmd_busy || !au_need_clean)
390 return;
391
392 // loop over all events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100393 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100394 event = (event_T)((int)event + 1))
395 {
396 // loop over all autocommand patterns
397 prev_ap = &(first_autopat[(int)event]);
398 for (ap = *prev_ap; ap != NULL; ap = *prev_ap)
399 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200400 int has_cmd = FALSE;
401
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200402 // loop over all commands for this pattern
403 prev_ac = &(ap->cmds);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100404 for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
405 {
406 // remove the command if the pattern is to be deleted or when
407 // the command has been marked for deletion
408 if (ap->pat == NULL || ac->cmd == NULL)
409 {
410 *prev_ac = ac->next;
411 vim_free(ac->cmd);
412 vim_free(ac);
413 }
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200414 else
415 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200416 has_cmd = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100417 prev_ac = &(ac->next);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200418 }
419 }
420
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200421 if (ap->pat != NULL && !has_cmd)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200422 // Pattern was not marked for deletion, but all of its
423 // commands were. So mark the pattern for deletion.
424 au_remove_pat(ap);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100425
426 // remove the pattern if it has been marked for deletion
427 if (ap->pat == NULL)
428 {
429 if (ap->next == NULL)
430 {
431 if (prev_ap == &(first_autopat[(int)event]))
432 last_autopat[(int)event] = NULL;
433 else
434 // this depends on the "next" field being the first in
435 // the struct
436 last_autopat[(int)event] = (AutoPat *)prev_ap;
437 }
438 *prev_ap = ap->next;
439 vim_regfree(ap->reg_prog);
440 vim_free(ap);
441 }
442 else
443 prev_ap = &(ap->next);
444 }
445 }
446
447 au_need_clean = FALSE;
448}
449
450/*
451 * Called when buffer is freed, to remove/invalidate related buffer-local
452 * autocmds.
453 */
454 void
455aubuflocal_remove(buf_T *buf)
456{
LemonBoyeca7c602022-04-14 15:39:43 +0100457 AutoPat *ap;
458 event_T event;
459 AutoPatCmd_T *apc;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100460
461 // invalidate currently executing autocommands
462 for (apc = active_apc_list; apc; apc = apc->next)
463 if (buf->b_fnum == apc->arg_bufnr)
464 apc->arg_bufnr = 0;
465
466 // invalidate buflocals looping through events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100467 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100468 event = (event_T)((int)event + 1))
469 // loop over all autocommand patterns
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200470 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100471 if (ap->buflocal_nr == buf->b_fnum)
472 {
473 au_remove_pat(ap);
474 if (p_verbose >= 6)
475 {
476 verbose_enter();
477 smsg(_("auto-removing autocommand: %s <buffer=%d>"),
478 event_nr2name(event), buf->b_fnum);
479 verbose_leave();
480 }
481 }
482 au_cleanup();
483}
484
485/*
486 * Add an autocmd group name.
487 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
488 */
489 static int
490au_new_group(char_u *name)
491{
492 int i;
493
494 i = au_find_group(name);
495 if (i == AUGROUP_ERROR) // the group doesn't exist yet, add it
496 {
497 // First try using a free entry.
498 for (i = 0; i < augroups.ga_len; ++i)
499 if (AUGROUP_NAME(i) == NULL)
500 break;
501 if (i == augroups.ga_len && ga_grow(&augroups, 1) == FAIL)
502 return AUGROUP_ERROR;
503
504 AUGROUP_NAME(i) = vim_strsave(name);
505 if (AUGROUP_NAME(i) == NULL)
506 return AUGROUP_ERROR;
507 if (i == augroups.ga_len)
508 ++augroups.ga_len;
509 }
510
511 return i;
512}
513
514 static void
515au_del_group(char_u *name)
516{
517 int i;
518
519 i = au_find_group(name);
520 if (i == AUGROUP_ERROR) // the group doesn't exist
Bram Moolenaarf1474d82021-12-31 19:59:55 +0000521 semsg(_(e_no_such_group_str), name);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100522 else if (i == current_augroup)
Bram Moolenaarf1474d82021-12-31 19:59:55 +0000523 emsg(_(e_cannot_delete_current_group));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100524 else
525 {
526 event_T event;
527 AutoPat *ap;
528 int in_use = FALSE;
529
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100530 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100531 event = (event_T)((int)event + 1))
532 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200533 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100534 if (ap->group == i && ap->pat != NULL)
535 {
536 give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
537 in_use = TRUE;
538 event = NUM_EVENTS;
539 break;
540 }
541 }
542 vim_free(AUGROUP_NAME(i));
543 if (in_use)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100544 AUGROUP_NAME(i) = get_deleted_augroup();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100545 else
546 AUGROUP_NAME(i) = NULL;
547 }
548}
549
550/*
551 * Find the ID of an autocmd group name.
552 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
553 */
554 static int
555au_find_group(char_u *name)
556{
557 int i;
558
559 for (i = 0; i < augroups.ga_len; ++i)
560 if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
561 && STRCMP(AUGROUP_NAME(i), name) == 0)
562 return i;
563 return AUGROUP_ERROR;
564}
565
566/*
567 * Return TRUE if augroup "name" exists.
568 */
569 int
570au_has_group(char_u *name)
571{
572 return au_find_group(name) != AUGROUP_ERROR;
573}
574
575/*
576 * ":augroup {name}".
577 */
578 void
579do_augroup(char_u *arg, int del_group)
580{
581 int i;
582
583 if (del_group)
584 {
585 if (*arg == NUL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000586 emsg(_(e_argument_required));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100587 else
588 au_del_group(arg);
589 }
590 else if (STRICMP(arg, "end") == 0) // ":aug end": back to group 0
591 current_augroup = AUGROUP_DEFAULT;
592 else if (*arg) // ":aug xxx": switch to group xxx
593 {
594 i = au_new_group(arg);
595 if (i != AUGROUP_ERROR)
596 current_augroup = i;
597 }
598 else // ":aug": list the group names
599 {
600 msg_start();
601 for (i = 0; i < augroups.ga_len; ++i)
602 {
603 if (AUGROUP_NAME(i) != NULL)
604 {
605 msg_puts((char *)AUGROUP_NAME(i));
606 msg_puts(" ");
607 }
608 }
609 msg_clr_eos();
610 msg_end();
611 }
612}
613
614#if defined(EXITFREE) || defined(PROTO)
615 void
616free_all_autocmds(void)
617{
618 int i;
619 char_u *s;
620
621 for (current_augroup = -1; current_augroup < augroups.ga_len;
622 ++current_augroup)
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200623 do_autocmd(NULL, (char_u *)"", TRUE);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100624
625 for (i = 0; i < augroups.ga_len; ++i)
626 {
627 s = ((char_u **)(augroups.ga_data))[i];
628 if (s != get_deleted_augroup())
629 vim_free(s);
630 }
631 ga_clear(&augroups);
632}
633#endif
634
635/*
636 * Return the event number for event name "start".
637 * Return NUM_EVENTS if the event name was not found.
638 * Return a pointer to the next event name in "end".
639 */
640 static event_T
641event_name2nr(char_u *start, char_u **end)
642{
643 char_u *p;
644 int i;
645 int len;
646
647 // the event name ends with end of line, '|', a blank or a comma
648 for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
649 ;
650 for (i = 0; event_names[i].name != NULL; ++i)
651 {
652 len = (int)STRLEN(event_names[i].name);
653 if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0)
654 break;
655 }
656 if (*p == ',')
657 ++p;
658 *end = p;
659 if (event_names[i].name == NULL)
660 return NUM_EVENTS;
661 return event_names[i].event;
662}
663
664/*
665 * Return the name for event "event".
666 */
667 static char_u *
668event_nr2name(event_T event)
669{
670 int i;
671
672 for (i = 0; event_names[i].name != NULL; ++i)
673 if (event_names[i].event == event)
674 return (char_u *)event_names[i].name;
675 return (char_u *)"Unknown";
676}
677
678/*
679 * Scan over the events. "*" stands for all events.
680 */
681 static char_u *
682find_end_event(
683 char_u *arg,
684 int have_group) // TRUE when group name was found
685{
686 char_u *pat;
687 char_u *p;
688
689 if (*arg == '*')
690 {
691 if (arg[1] && !VIM_ISWHITE(arg[1]))
692 {
Bram Moolenaar6d057012021-12-31 18:49:43 +0000693 semsg(_(e_illegal_character_after_star_str), arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100694 return NULL;
695 }
696 pat = arg + 1;
697 }
698 else
699 {
700 for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p)
701 {
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100702 if ((int)event_name2nr(pat, &p) >= NUM_EVENTS)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100703 {
704 if (have_group)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000705 semsg(_(e_no_such_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100706 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000707 semsg(_(e_no_such_group_or_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100708 return NULL;
709 }
710 }
711 }
712 return pat;
713}
714
715/*
716 * Return TRUE if "event" is included in 'eventignore'.
717 */
718 static int
719event_ignored(event_T event)
720{
721 char_u *p = p_ei;
722
723 while (*p != NUL)
724 {
725 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
726 return TRUE;
727 if (event_name2nr(p, &p) == event)
728 return TRUE;
729 }
730
731 return FALSE;
732}
733
734/*
735 * Return OK when the contents of p_ei is valid, FAIL otherwise.
736 */
737 int
738check_ei(void)
739{
740 char_u *p = p_ei;
741
742 while (*p)
743 {
744 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
745 {
746 p += 3;
747 if (*p == ',')
748 ++p;
749 }
750 else if (event_name2nr(p, &p) == NUM_EVENTS)
751 return FAIL;
752 }
753
754 return OK;
755}
756
757# if defined(FEAT_SYN_HL) || defined(PROTO)
758
759/*
760 * Add "what" to 'eventignore' to skip loading syntax highlighting for every
761 * buffer loaded into the window. "what" must start with a comma.
762 * Returns the old value of 'eventignore' in allocated memory.
763 */
764 char_u *
765au_event_disable(char *what)
766{
767 char_u *new_ei;
768 char_u *save_ei;
769
770 save_ei = vim_strsave(p_ei);
771 if (save_ei != NULL)
772 {
Bram Moolenaardf44a272020-06-07 20:49:05 +0200773 new_ei = vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100774 if (new_ei != NULL)
775 {
776 if (*what == ',' && *p_ei == NUL)
777 STRCPY(new_ei, what + 1);
778 else
779 STRCAT(new_ei, what);
780 set_string_option_direct((char_u *)"ei", -1, new_ei,
781 OPT_FREE, SID_NONE);
782 vim_free(new_ei);
783 }
784 }
785 return save_ei;
786}
787
788 void
789au_event_restore(char_u *old_ei)
790{
791 if (old_ei != NULL)
792 {
793 set_string_option_direct((char_u *)"ei", -1, old_ei,
794 OPT_FREE, SID_NONE);
795 vim_free(old_ei);
796 }
797}
Bram Moolenaarc667da52019-11-30 20:52:27 +0100798# endif // FEAT_SYN_HL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100799
800/*
801 * do_autocmd() -- implements the :autocmd command. Can be used in the
802 * following ways:
803 *
804 * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
805 * will be automatically executed for <event>
806 * when editing a file matching <pat>, in
807 * the current group.
808 * :autocmd <event> <pat> Show the autocommands associated with
809 * <event> and <pat>.
810 * :autocmd <event> Show the autocommands associated with
811 * <event>.
812 * :autocmd Show all autocommands.
813 * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
814 * <event> and <pat>, and add the command
815 * <cmd>, for the current group.
816 * :autocmd! <event> <pat> Remove all autocommands associated with
817 * <event> and <pat> for the current group.
818 * :autocmd! <event> Remove all autocommands associated with
819 * <event> for the current group.
820 * :autocmd! Remove ALL autocommands for the current
821 * group.
822 *
823 * Multiple events and patterns may be given separated by commas. Here are
824 * some examples:
825 * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
826 * :autocmd bufleave * set tw=79 nosmartindent ic infercase
827 *
828 * :autocmd * *.c show all autocommands for *.c files.
829 *
830 * Mostly a {group} argument can optionally appear before <event>.
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200831 * "eap" can be NULL.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100832 */
833 void
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200834do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100835{
836 char_u *arg = arg_in;
837 char_u *pat;
838 char_u *envpat = NULL;
839 char_u *cmd;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200840 int cmd_need_free = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100841 event_T event;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200842 char_u *tofree = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100843 int nested = FALSE;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200844 int once = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100845 int group;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200846 int i;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200847 int flags = 0;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100848
849 if (*arg == '|')
850 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000851 eap->nextcmd = arg + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100852 arg = (char_u *)"";
853 group = AUGROUP_ALL; // no argument, use all groups
854 }
855 else
856 {
857 /*
858 * Check for a legal group name. If not, use AUGROUP_ALL.
859 */
860 group = au_get_grouparg(&arg);
861 if (arg == NULL) // out of memory
862 return;
863 }
864
865 /*
866 * Scan over the events.
867 * If we find an illegal name, return here, don't do anything.
868 */
869 pat = find_end_event(arg, group != AUGROUP_ALL);
870 if (pat == NULL)
871 return;
872
873 pat = skipwhite(pat);
874 if (*pat == '|')
875 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000876 eap->nextcmd = pat + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100877 pat = (char_u *)"";
878 cmd = (char_u *)"";
879 }
880 else
881 {
882 /*
883 * Scan over the pattern. Put a NUL at the end.
884 */
885 cmd = pat;
886 while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
887 cmd++;
888 if (*cmd)
889 *cmd++ = NUL;
890
891 // Expand environment variables in the pattern. Set 'shellslash', we
892 // want forward slashes here.
893 if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
894 {
895#ifdef BACKSLASH_IN_FILENAME
896 int p_ssl_save = p_ssl;
897
898 p_ssl = TRUE;
899#endif
900 envpat = expand_env_save(pat);
901#ifdef BACKSLASH_IN_FILENAME
902 p_ssl = p_ssl_save;
903#endif
904 if (envpat != NULL)
905 pat = envpat;
906 }
907
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100908 cmd = skipwhite(cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200909 for (i = 0; i < 2; i++)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100910 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200911 if (*cmd != NUL)
912 {
913 // Check for "++once" flag.
914 if (STRNCMP(cmd, "++once", 6) == 0 && VIM_ISWHITE(cmd[6]))
915 {
916 if (once)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000917 semsg(_(e_duplicate_argument_str), "++once");
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200918 once = TRUE;
919 cmd = skipwhite(cmd + 6);
920 }
921
922 // Check for "++nested" flag.
923 if ((STRNCMP(cmd, "++nested", 8) == 0 && VIM_ISWHITE(cmd[8])))
924 {
925 if (nested)
Bram Moolenaarf0775142022-03-04 20:10:38 +0000926 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000927 semsg(_(e_duplicate_argument_str), "++nested");
Bram Moolenaarf0775142022-03-04 20:10:38 +0000928 return;
929 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200930 nested = TRUE;
931 cmd = skipwhite(cmd + 8);
932 }
933
Bram Moolenaarf0775142022-03-04 20:10:38 +0000934 // Check for the old "nested" flag in legacy script.
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200935 if (STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
936 {
Bram Moolenaarf0775142022-03-04 20:10:38 +0000937 if (in_vim9script())
938 {
939 // If there ever is a :nested command this error should
940 // be removed and "nested" accepted as the start of the
941 // command.
942 emsg(_(e_invalid_command_nested_did_you_mean_plusplus_nested));
943 return;
944 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200945 if (nested)
Bram Moolenaarf0775142022-03-04 20:10:38 +0000946 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000947 semsg(_(e_duplicate_argument_str), "nested");
Bram Moolenaarf0775142022-03-04 20:10:38 +0000948 return;
949 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200950 nested = TRUE;
951 cmd = skipwhite(cmd + 6);
952 }
953 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100954 }
955
956 /*
957 * Find the start of the commands.
958 * Expand <sfile> in it.
959 */
960 if (*cmd != NUL)
961 {
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200962 if (eap != NULL)
963 // Read a {} block if it follows.
964 cmd = may_get_cmd_block(eap, cmd, &tofree, &flags);
965
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100966 cmd = expand_sfile(cmd);
967 if (cmd == NULL) // some error
968 return;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200969 cmd_need_free = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100970 }
971 }
972
973 /*
974 * Print header when showing autocommands.
975 */
976 if (!forceit && *cmd == NUL)
977 // Highlight title
978 msg_puts_title(_("\n--- Autocommands ---"));
979
980 /*
981 * Loop over the events.
982 */
983 last_event = (event_T)-1; // for listing the event name
984 last_group = AUGROUP_ERROR; // for listing the group name
985 if (*arg == '*' || *arg == NUL || *arg == '|')
986 {
Bram Moolenaarb6db1462021-12-24 19:24:47 +0000987 if (*cmd != NUL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +0100988 emsg(_(e_cannot_define_autocommands_for_all_events));
989 else
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100990 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar9a046fd2021-01-28 13:47:59 +0100991 event = (event_T)((int)event + 1))
992 if (do_autocmd_event(event, pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200993 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +0100994 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100995 }
996 else
997 {
998 while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
999 if (do_autocmd_event(event_name2nr(arg, &arg), pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001000 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001001 break;
1002 }
1003
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001004 if (cmd_need_free)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001005 vim_free(cmd);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001006 vim_free(tofree);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001007 vim_free(envpat);
1008}
1009
1010/*
1011 * Find the group ID in a ":autocmd" or ":doautocmd" argument.
1012 * The "argp" argument is advanced to the following argument.
1013 *
1014 * Returns the group ID, AUGROUP_ERROR for error (out of memory).
1015 */
1016 static int
1017au_get_grouparg(char_u **argp)
1018{
1019 char_u *group_name;
1020 char_u *p;
1021 char_u *arg = *argp;
1022 int group = AUGROUP_ALL;
1023
1024 for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p)
1025 ;
1026 if (p > arg)
1027 {
Bram Moolenaardf44a272020-06-07 20:49:05 +02001028 group_name = vim_strnsave(arg, p - arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001029 if (group_name == NULL) // out of memory
1030 return AUGROUP_ERROR;
1031 group = au_find_group(group_name);
1032 if (group == AUGROUP_ERROR)
1033 group = AUGROUP_ALL; // no match, use all groups
1034 else
1035 *argp = skipwhite(p); // match, skip over group name
1036 vim_free(group_name);
1037 }
1038 return group;
1039}
1040
1041/*
1042 * do_autocmd() for one event.
1043 * If *pat == NUL do for all patterns.
1044 * If *cmd == NUL show entries.
1045 * If forceit == TRUE delete entries.
1046 * If group is not AUGROUP_ALL, only use this group.
1047 */
1048 static int
1049do_autocmd_event(
1050 event_T event,
1051 char_u *pat,
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001052 int once,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001053 int nested,
1054 char_u *cmd,
1055 int forceit,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001056 int group,
1057 int flags)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001058{
1059 AutoPat *ap;
1060 AutoPat **prev_ap;
1061 AutoCmd *ac;
1062 AutoCmd **prev_ac;
1063 int brace_level;
1064 char_u *endpat;
1065 int findgroup;
1066 int allgroups;
1067 int patlen;
1068 int is_buflocal;
1069 int buflocal_nr;
Bram Moolenaarc667da52019-11-30 20:52:27 +01001070 char_u buflocal_pat[25]; // for "<buffer=X>"
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001071
1072 if (group == AUGROUP_ALL)
1073 findgroup = current_augroup;
1074 else
1075 findgroup = group;
1076 allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
1077
1078 /*
1079 * Show or delete all patterns for an event.
1080 */
1081 if (*pat == NUL)
1082 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001083 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001084 {
1085 if (forceit) // delete the AutoPat, if it's in the current group
1086 {
1087 if (ap->group == findgroup)
1088 au_remove_pat(ap);
1089 }
1090 else if (group == AUGROUP_ALL || ap->group == group)
1091 show_autocmd(ap, event);
1092 }
1093 }
1094
1095 /*
1096 * Loop through all the specified patterns.
1097 */
1098 for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
1099 {
1100 /*
1101 * Find end of the pattern.
1102 * Watch out for a comma in braces, like "*.\{obj,o\}".
1103 */
1104 brace_level = 0;
1105 for (endpat = pat; *endpat && (*endpat != ',' || brace_level
1106 || (endpat > pat && endpat[-1] == '\\')); ++endpat)
1107 {
1108 if (*endpat == '{')
1109 brace_level++;
1110 else if (*endpat == '}')
1111 brace_level--;
1112 }
1113 if (pat == endpat) // ignore single comma
1114 continue;
1115 patlen = (int)(endpat - pat);
1116
1117 /*
1118 * detect special <buflocal[=X]> buffer-local patterns
1119 */
1120 is_buflocal = FALSE;
1121 buflocal_nr = 0;
1122
1123 if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
1124 && pat[patlen - 1] == '>')
1125 {
1126 // "<buffer...>": Error will be printed only for addition.
1127 // printing and removing will proceed silently.
1128 is_buflocal = TRUE;
1129 if (patlen == 8)
1130 // "<buffer>"
1131 buflocal_nr = curbuf->b_fnum;
1132 else if (patlen > 9 && pat[7] == '=')
1133 {
1134 if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0)
1135 // "<buffer=abuf>"
1136 buflocal_nr = autocmd_bufnr;
1137 else if (skipdigits(pat + 8) == pat + patlen - 1)
1138 // "<buffer=123>"
1139 buflocal_nr = atoi((char *)pat + 8);
1140 }
1141 }
1142
1143 if (is_buflocal)
1144 {
1145 // normalize pat into standard "<buffer>#N" form
1146 sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
1147 pat = buflocal_pat; // can modify pat and patlen
1148 patlen = (int)STRLEN(buflocal_pat); // but not endpat
1149 }
1150
1151 /*
1152 * Find AutoPat entries with this pattern. When adding a command it
1153 * always goes at or after the last one, so start at the end.
1154 */
1155 if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL)
1156 prev_ap = &last_autopat[(int)event];
1157 else
1158 prev_ap = &first_autopat[(int)event];
1159 while ((ap = *prev_ap) != NULL)
1160 {
1161 if (ap->pat != NULL)
1162 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001163 /*
1164 * Accept a pattern when:
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001165 * - a group was specified and it's that group, or a group was
1166 * not specified and it's the current group, or a group was
1167 * not specified and we are listing
1168 * - the length of the pattern matches
1169 * - the pattern matches.
1170 * For <buffer[=X]>, this condition works because we normalize
1171 * all buffer-local patterns.
1172 */
1173 if ((allgroups || ap->group == findgroup)
1174 && ap->patlen == patlen
1175 && STRNCMP(pat, ap->pat, patlen) == 0)
1176 {
1177 /*
1178 * Remove existing autocommands.
1179 * If adding any new autocmd's for this AutoPat, don't
1180 * delete the pattern from the autopat list, append to
1181 * this list.
1182 */
1183 if (forceit)
1184 {
1185 if (*cmd != NUL && ap->next == NULL)
1186 {
1187 au_remove_cmds(ap);
1188 break;
1189 }
1190 au_remove_pat(ap);
1191 }
1192
1193 /*
1194 * Show autocmd's for this autopat, or buflocals <buffer=X>
1195 */
1196 else if (*cmd == NUL)
1197 show_autocmd(ap, event);
1198
1199 /*
1200 * Add autocmd to this autopat, if it's the last one.
1201 */
1202 else if (ap->next == NULL)
1203 break;
1204 }
1205 }
1206 prev_ap = &ap->next;
1207 }
1208
1209 /*
1210 * Add a new command.
1211 */
1212 if (*cmd != NUL)
1213 {
1214 /*
1215 * If the pattern we want to add a command to does appear at the
1216 * end of the list (or not is not in the list at all), add the
1217 * pattern at the end of the list.
1218 */
1219 if (ap == NULL)
1220 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001221 // refuse to add buffer-local ap if buffer number is invalid
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001222 if (is_buflocal && (buflocal_nr == 0
1223 || buflist_findnr(buflocal_nr) == NULL))
1224 {
Bram Moolenaarf1474d82021-12-31 19:59:55 +00001225 semsg(_(e_buffer_nr_invalid_buffer_number), buflocal_nr);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001226 return FAIL;
1227 }
1228
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001229 ap = ALLOC_ONE(AutoPat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001230 if (ap == NULL)
1231 return FAIL;
1232 ap->pat = vim_strnsave(pat, patlen);
1233 ap->patlen = patlen;
1234 if (ap->pat == NULL)
1235 {
1236 vim_free(ap);
1237 return FAIL;
1238 }
1239
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001240#ifdef FEAT_EVAL
1241 // need to initialize last_mode for the first ModeChanged
1242 // autocmd
1243 if (event == EVENT_MODECHANGED && !has_modechanged())
LemonBoy2bf52dd2022-04-09 18:17:34 +01001244 get_mode(last_mode);
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001245#endif
LemonBoy09371822022-04-08 15:18:45 +01001246 // Initialize the fields checked by the WinScrolled trigger to
1247 // stop it from firing right after the first autocmd is defined.
1248 if (event == EVENT_WINSCROLLED && !has_winscrolled())
1249 {
1250 curwin->w_last_topline = curwin->w_topline;
1251 curwin->w_last_leftcol = curwin->w_leftcol;
zeertzjq670ab032022-08-28 19:16:15 +01001252 curwin->w_last_skipcol = curwin->w_skipcol;
LemonBoy09371822022-04-08 15:18:45 +01001253 curwin->w_last_width = curwin->w_width;
1254 curwin->w_last_height = curwin->w_height;
1255 }
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001256
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001257 if (is_buflocal)
1258 {
1259 ap->buflocal_nr = buflocal_nr;
1260 ap->reg_prog = NULL;
1261 }
1262 else
1263 {
1264 char_u *reg_pat;
1265
1266 ap->buflocal_nr = 0;
1267 reg_pat = file_pat_to_reg_pat(pat, endpat,
1268 &ap->allow_dirs, TRUE);
1269 if (reg_pat != NULL)
1270 ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
1271 vim_free(reg_pat);
1272 if (reg_pat == NULL || ap->reg_prog == NULL)
1273 {
1274 vim_free(ap->pat);
1275 vim_free(ap);
1276 return FAIL;
1277 }
1278 }
1279 ap->cmds = NULL;
1280 *prev_ap = ap;
1281 last_autopat[(int)event] = ap;
1282 ap->next = NULL;
1283 if (group == AUGROUP_ALL)
1284 ap->group = current_augroup;
1285 else
1286 ap->group = group;
1287 }
1288
1289 /*
1290 * Add the autocmd at the end of the AutoCmd list.
1291 */
1292 prev_ac = &(ap->cmds);
1293 while ((ac = *prev_ac) != NULL)
1294 prev_ac = &ac->next;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001295 ac = ALLOC_ONE(AutoCmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001296 if (ac == NULL)
1297 return FAIL;
1298 ac->cmd = vim_strsave(cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001299 ac->script_ctx = current_sctx;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001300 if (flags & UC_VIM9)
1301 ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001302#ifdef FEAT_EVAL
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001303 ac->script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001304#endif
1305 if (ac->cmd == NULL)
1306 {
1307 vim_free(ac);
1308 return FAIL;
1309 }
1310 ac->next = NULL;
1311 *prev_ac = ac;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001312 ac->once = once;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001313 ac->nested = nested;
1314 }
1315 }
1316
1317 au_cleanup(); // may really delete removed patterns/commands now
1318 return OK;
1319}
1320
1321/*
1322 * Implementation of ":doautocmd [group] event [fname]".
1323 * Return OK for success, FAIL for failure;
1324 */
1325 int
1326do_doautocmd(
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001327 char_u *arg_start,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001328 int do_msg, // give message for no matching autocmds?
1329 int *did_something)
1330{
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001331 char_u *arg = arg_start;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001332 char_u *fname;
1333 int nothing_done = TRUE;
1334 int group;
1335
1336 if (did_something != NULL)
1337 *did_something = FALSE;
1338
1339 /*
1340 * Check for a legal group name. If not, use AUGROUP_ALL.
1341 */
1342 group = au_get_grouparg(&arg);
1343 if (arg == NULL) // out of memory
1344 return FAIL;
1345
1346 if (*arg == '*')
1347 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001348 emsg(_(e_cant_execute_autocommands_for_all_events));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001349 return FAIL;
1350 }
1351
1352 /*
1353 * Scan over the events.
1354 * If we find an illegal name, return here, don't do anything.
1355 */
1356 fname = find_end_event(arg, group != AUGROUP_ALL);
1357 if (fname == NULL)
1358 return FAIL;
1359
1360 fname = skipwhite(fname);
1361
1362 /*
1363 * Loop over the events.
1364 */
1365 while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
1366 if (apply_autocmds_group(event_name2nr(arg, &arg),
1367 fname, NULL, TRUE, group, curbuf, NULL))
1368 nothing_done = FALSE;
1369
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001370 if (nothing_done && do_msg
1371#ifdef FEAT_EVAL
1372 && !aborting()
1373#endif
1374 )
1375 smsg(_("No matching autocommands: %s"), arg_start);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001376 if (did_something != NULL)
1377 *did_something = !nothing_done;
1378
1379#ifdef FEAT_EVAL
1380 return aborting() ? FAIL : OK;
1381#else
1382 return OK;
1383#endif
1384}
1385
1386/*
1387 * ":doautoall": execute autocommands for each loaded buffer.
1388 */
1389 void
1390ex_doautoall(exarg_T *eap)
1391{
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001392 int retval = OK;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001393 aco_save_T aco;
1394 buf_T *buf;
1395 bufref_T bufref;
1396 char_u *arg = eap->arg;
1397 int call_do_modelines = check_nomodeline(&arg);
1398 int did_aucmd;
1399
1400 /*
1401 * This is a bit tricky: For some commands curwin->w_buffer needs to be
1402 * equal to curbuf, but for some buffers there may not be a window.
1403 * So we change the buffer for the current window for a moment. This
1404 * gives problems when the autocommands make changes to the list of
1405 * buffers or windows...
1406 */
1407 FOR_ALL_BUFFERS(buf)
1408 {
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001409 // Only do loaded buffers and skip the current buffer, it's done last.
1410 if (buf->b_ml.ml_mfp != NULL && buf != curbuf)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001411 {
1412 // find a window for this buffer and save some values
1413 aucmd_prepbuf(&aco, buf);
1414 set_bufref(&bufref, buf);
1415
1416 // execute the autocommands for this buffer
1417 retval = do_doautocmd(arg, FALSE, &did_aucmd);
1418
1419 if (call_do_modelines && did_aucmd)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001420 // Execute the modeline settings, but don't set window-local
1421 // options if we are using the current window for another
1422 // buffer.
1423 do_modelines(curwin == aucmd_win ? OPT_NOWIN : 0);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001424
1425 // restore the current window
1426 aucmd_restbuf(&aco);
1427
1428 // stop if there is some error or buffer was deleted
1429 if (retval == FAIL || !bufref_valid(&bufref))
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001430 {
1431 retval = FAIL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001432 break;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001433 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001434 }
1435 }
1436
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001437 // Execute autocommands for the current buffer last.
1438 if (retval == OK)
1439 {
1440 do_doautocmd(arg, FALSE, &did_aucmd);
1441 if (call_do_modelines && did_aucmd)
1442 do_modelines(0);
1443 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001444}
1445
1446/*
1447 * Check *argp for <nomodeline>. When it is present return FALSE, otherwise
1448 * return TRUE and advance *argp to after it.
1449 * Thus return TRUE when do_modelines() should be called.
1450 */
1451 int
1452check_nomodeline(char_u **argp)
1453{
1454 if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
1455 {
1456 *argp = skipwhite(*argp + 12);
1457 return FALSE;
1458 }
1459 return TRUE;
1460}
1461
1462/*
1463 * Prepare for executing autocommands for (hidden) buffer "buf".
1464 * Search for a visible window containing the current buffer. If there isn't
1465 * one then use "aucmd_win".
1466 * Set "curbuf" and "curwin" to match "buf".
1467 */
1468 void
1469aucmd_prepbuf(
1470 aco_save_T *aco, // structure to save values in
1471 buf_T *buf) // new curbuf
1472{
1473 win_T *win;
1474 int save_ea;
1475#ifdef FEAT_AUTOCHDIR
1476 int save_acd;
1477#endif
1478
1479 // Find a window that is for the new buffer
1480 if (buf == curbuf) // be quick when buf is curbuf
1481 win = curwin;
1482 else
1483 FOR_ALL_WINDOWS(win)
1484 if (win->w_buffer == buf)
1485 break;
1486
1487 // Allocate "aucmd_win" when needed. If this fails (out of memory) fall
1488 // back to using the current window.
1489 if (win == NULL && aucmd_win == NULL)
1490 {
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001491 aucmd_win = win_alloc_popup_win();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001492 if (aucmd_win == NULL)
1493 win = curwin;
1494 }
1495 if (win == NULL && aucmd_win_used)
1496 // Strange recursive autocommand, fall back to using the current
1497 // window. Expect a few side effects...
1498 win = curwin;
1499
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001500 aco->save_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001501 aco->save_curbuf = curbuf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001502 aco->save_prevwin_id = prevwin == NULL ? 0 : prevwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001503 if (win != NULL)
1504 {
1505 // There is a window for "buf" in the current tab page, make it the
1506 // curwin. This is preferred, it has the least side effects (esp. if
1507 // "buf" is curbuf).
1508 aco->use_aucmd_win = FALSE;
1509 curwin = win;
1510 }
1511 else
1512 {
1513 // There is no window for "buf", use "aucmd_win". To minimize the side
1514 // effects, insert it in the current tab page.
1515 // Anything related to a window (e.g., setting folds) may have
1516 // unexpected results.
1517 aco->use_aucmd_win = TRUE;
1518 aucmd_win_used = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001519
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001520 win_init_popup_win(aucmd_win, buf);
1521
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001522 aco->globaldir = globaldir;
1523 globaldir = NULL;
1524
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001525 // Split the current window, put the aucmd_win in the upper half.
1526 // We don't want the BufEnter or WinEnter autocommands.
1527 block_autocmds();
1528 make_snapshot(SNAP_AUCMD_IDX);
1529 save_ea = p_ea;
1530 p_ea = FALSE;
1531
1532#ifdef FEAT_AUTOCHDIR
1533 // Prevent chdir() call in win_enter_ext(), through do_autochdir().
1534 save_acd = p_acd;
1535 p_acd = FALSE;
1536#endif
1537
Bram Moolenaardff97e62022-01-24 20:00:55 +00001538 // no redrawing and don't set the window title
1539 ++RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001540 (void)win_split_ins(0, WSP_TOP, aucmd_win, 0);
Bram Moolenaardff97e62022-01-24 20:00:55 +00001541 --RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001542 (void)win_comp_pos(); // recompute window positions
1543 p_ea = save_ea;
1544#ifdef FEAT_AUTOCHDIR
1545 p_acd = save_acd;
1546#endif
1547 unblock_autocmds();
1548 curwin = aucmd_win;
1549 }
1550 curbuf = buf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001551 aco->new_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001552 set_bufref(&aco->new_curbuf, curbuf);
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001553
1554 // disable the Visual area, the position may be invalid in another buffer
1555 aco->save_VIsual_active = VIsual_active;
1556 VIsual_active = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001557}
1558
1559/*
1560 * Cleanup after executing autocommands for a (hidden) buffer.
1561 * Restore the window as it was (if possible).
1562 */
1563 void
1564aucmd_restbuf(
1565 aco_save_T *aco) // structure holding saved values
1566{
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001567 int dummy;
1568 win_T *save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001569
1570 if (aco->use_aucmd_win)
1571 {
1572 --curbuf->b_nwindows;
1573 // Find "aucmd_win", it can't be closed, but it may be in another tab
1574 // page. Do not trigger autocommands here.
1575 block_autocmds();
1576 if (curwin != aucmd_win)
1577 {
1578 tabpage_T *tp;
1579 win_T *wp;
1580
1581 FOR_ALL_TAB_WINDOWS(tp, wp)
1582 {
1583 if (wp == aucmd_win)
1584 {
1585 if (tp != curtab)
1586 goto_tabpage_tp(tp, TRUE, TRUE);
1587 win_goto(aucmd_win);
1588 goto win_found;
1589 }
1590 }
1591 }
1592win_found:
1593
1594 // Remove the window and frame from the tree of frames.
1595 (void)winframe_remove(curwin, &dummy, NULL);
1596 win_remove(curwin, NULL);
1597 aucmd_win_used = FALSE;
1598 last_status(FALSE); // may need to remove last status line
1599
1600 if (!valid_tabpage_win(curtab))
1601 // no valid window in current tabpage
1602 close_tabpage(curtab);
1603
1604 restore_snapshot(SNAP_AUCMD_IDX, FALSE);
1605 (void)win_comp_pos(); // recompute window positions
1606 unblock_autocmds();
1607
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001608 save_curwin = win_find_by_id(aco->save_curwin_id);
1609 if (save_curwin != NULL)
1610 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001611 else
1612 // Hmm, original window disappeared. Just use the first one.
1613 curwin = firstwin;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001614 curbuf = curwin->w_buffer;
1615#ifdef FEAT_JOB_CHANNEL
1616 // May need to restore insert mode for a prompt buffer.
1617 entering_window(curwin);
1618#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001619 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001620#ifdef FEAT_EVAL
1621 vars_clear(&aucmd_win->w_vars->dv_hashtab); // free all w: variables
1622 hash_init(&aucmd_win->w_vars->dv_hashtab); // re-use the hashtab
1623#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001624 vim_free(globaldir);
1625 globaldir = aco->globaldir;
1626
1627 // the buffer contents may have changed
1628 check_cursor();
1629 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1630 {
1631 curwin->w_topline = curbuf->b_ml.ml_line_count;
1632#ifdef FEAT_DIFF
1633 curwin->w_topfill = 0;
1634#endif
1635 }
1636#if defined(FEAT_GUI)
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001637 if (gui.in_use)
1638 {
1639 // Hide the scrollbars from the aucmd_win and update.
1640 gui_mch_enable_scrollbar(
1641 &aucmd_win->w_scrollbars[SBAR_LEFT], FALSE);
1642 gui_mch_enable_scrollbar(
1643 &aucmd_win->w_scrollbars[SBAR_RIGHT], FALSE);
1644 gui_may_update_scrollbars();
1645 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001646#endif
1647 }
1648 else
1649 {
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001650 // Restore curwin. Use the window ID, a window may have been closed
1651 // and the memory re-used for another one.
1652 save_curwin = win_find_by_id(aco->save_curwin_id);
1653 if (save_curwin != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001654 {
1655 // Restore the buffer which was previously edited by curwin, if
1656 // it was changed, we are still the same window and the buffer is
1657 // valid.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001658 if (curwin->w_id == aco->new_curwin_id
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001659 && curbuf != aco->new_curbuf.br_buf
1660 && bufref_valid(&aco->new_curbuf)
1661 && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
1662 {
1663# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
1664 if (curwin->w_s == &curbuf->b_s)
1665 curwin->w_s = &aco->new_curbuf.br_buf->b_s;
1666# endif
1667 --curbuf->b_nwindows;
1668 curbuf = aco->new_curbuf.br_buf;
1669 curwin->w_buffer = curbuf;
1670 ++curbuf->b_nwindows;
1671 }
1672
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001673 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001674 curbuf = curwin->w_buffer;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001675 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001676 // In case the autocommand moves the cursor to a position that
1677 // does not exist in curbuf.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001678 check_cursor();
1679 }
1680 }
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001681
1682 check_cursor(); // just in case lines got deleted
1683 VIsual_active = aco->save_VIsual_active;
1684 if (VIsual_active)
1685 check_pos(curbuf, &VIsual);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001686}
1687
1688static int autocmd_nested = FALSE;
1689
1690/*
1691 * Execute autocommands for "event" and file name "fname".
1692 * Return TRUE if some commands were executed.
1693 */
1694 int
1695apply_autocmds(
1696 event_T event,
1697 char_u *fname, // NULL or empty means use actual file name
1698 char_u *fname_io, // fname to use for <afile> on cmdline
1699 int force, // when TRUE, ignore autocmd_busy
1700 buf_T *buf) // buffer for <abuf>
1701{
1702 return apply_autocmds_group(event, fname, fname_io, force,
1703 AUGROUP_ALL, buf, NULL);
1704}
1705
1706/*
1707 * Like apply_autocmds(), but with extra "eap" argument. This takes care of
1708 * setting v:filearg.
1709 */
1710 int
1711apply_autocmds_exarg(
1712 event_T event,
1713 char_u *fname,
1714 char_u *fname_io,
1715 int force,
1716 buf_T *buf,
1717 exarg_T *eap)
1718{
1719 return apply_autocmds_group(event, fname, fname_io, force,
1720 AUGROUP_ALL, buf, eap);
1721}
1722
1723/*
1724 * Like apply_autocmds(), but handles the caller's retval. If the script
1725 * processing is being aborted or if retval is FAIL when inside a try
1726 * conditional, no autocommands are executed. If otherwise the autocommands
1727 * cause the script to be aborted, retval is set to FAIL.
1728 */
1729 int
1730apply_autocmds_retval(
1731 event_T event,
1732 char_u *fname, // NULL or empty means use actual file name
1733 char_u *fname_io, // fname to use for <afile> on cmdline
1734 int force, // when TRUE, ignore autocmd_busy
1735 buf_T *buf, // buffer for <abuf>
1736 int *retval) // pointer to caller's retval
1737{
1738 int did_cmd;
1739
1740#ifdef FEAT_EVAL
1741 if (should_abort(*retval))
1742 return FALSE;
1743#endif
1744
1745 did_cmd = apply_autocmds_group(event, fname, fname_io, force,
1746 AUGROUP_ALL, buf, NULL);
1747 if (did_cmd
1748#ifdef FEAT_EVAL
1749 && aborting()
1750#endif
1751 )
1752 *retval = FAIL;
1753 return did_cmd;
1754}
1755
1756/*
1757 * Return TRUE when there is a CursorHold autocommand defined.
1758 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001759 static int
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001760has_cursorhold(void)
1761{
Bram Moolenaar24959102022-05-07 20:01:16 +01001762 return (first_autopat[(int)(get_real_state() == MODE_NORMAL_BUSY
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001763 ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
1764}
1765
1766/*
1767 * Return TRUE if the CursorHold event can be triggered.
1768 */
1769 int
1770trigger_cursorhold(void)
1771{
1772 int state;
1773
1774 if (!did_cursorhold
1775 && has_cursorhold()
1776 && reg_recording == 0
1777 && typebuf.tb_len == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001778 && !ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001779 {
1780 state = get_real_state();
Bram Moolenaar24959102022-05-07 20:01:16 +01001781 if (state == MODE_NORMAL_BUSY || (state & MODE_INSERT) != 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001782 return TRUE;
1783 }
1784 return FALSE;
1785}
1786
1787/*
LemonBoy09371822022-04-08 15:18:45 +01001788 * Return TRUE when there is a WinScrolled autocommand defined.
1789 */
1790 int
1791has_winscrolled(void)
1792{
1793 return (first_autopat[(int)EVENT_WINSCROLLED] != NULL);
1794}
1795
1796/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001797 * Return TRUE when there is a CursorMoved autocommand defined.
1798 */
1799 int
1800has_cursormoved(void)
1801{
1802 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1803}
1804
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001805/*
1806 * Return TRUE when there is a CursorMovedI autocommand defined.
1807 */
1808 int
1809has_cursormovedI(void)
1810{
1811 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1812}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001813
1814/*
1815 * Return TRUE when there is a TextChanged autocommand defined.
1816 */
1817 int
1818has_textchanged(void)
1819{
1820 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1821}
1822
1823/*
1824 * Return TRUE when there is a TextChangedI autocommand defined.
1825 */
1826 int
1827has_textchangedI(void)
1828{
1829 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
1830}
1831
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001832/*
1833 * Return TRUE when there is a TextChangedP autocommand defined.
1834 */
1835 int
1836has_textchangedP(void)
1837{
1838 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
1839}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001840
1841/*
1842 * Return TRUE when there is an InsertCharPre autocommand defined.
1843 */
1844 int
1845has_insertcharpre(void)
1846{
1847 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
1848}
1849
1850/*
1851 * Return TRUE when there is an CmdUndefined autocommand defined.
1852 */
1853 int
1854has_cmdundefined(void)
1855{
1856 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
1857}
1858
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001859#if defined(FEAT_EVAL) || defined(PROTO)
1860/*
1861 * Return TRUE when there is a TextYankPost autocommand defined.
1862 */
1863 int
1864has_textyankpost(void)
1865{
1866 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
1867}
1868#endif
1869
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001870#if defined(FEAT_EVAL) || defined(PROTO)
1871/*
1872 * Return TRUE when there is a CompleteChanged autocommand defined.
1873 */
1874 int
1875has_completechanged(void)
1876{
1877 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
1878}
1879#endif
1880
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02001881#if defined(FEAT_EVAL) || defined(PROTO)
1882/*
1883 * Return TRUE when there is a ModeChanged autocommand defined.
1884 */
1885 int
1886has_modechanged(void)
1887{
1888 return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
1889}
1890#endif
1891
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001892/*
1893 * Execute autocommands for "event" and file name "fname".
1894 * Return TRUE if some commands were executed.
1895 */
1896 static int
1897apply_autocmds_group(
1898 event_T event,
1899 char_u *fname, // NULL or empty means use actual file name
1900 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
1901 // use fname
1902 int force, // when TRUE, ignore autocmd_busy
1903 int group, // group ID, or AUGROUP_ALL
1904 buf_T *buf, // buffer for <abuf>
1905 exarg_T *eap UNUSED) // command arguments
1906{
1907 char_u *sfname = NULL; // short file name
1908 char_u *tail;
1909 int save_changed;
1910 buf_T *old_curbuf;
1911 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001912 char_u *save_autocmd_fname;
1913 int save_autocmd_fname_full;
1914 int save_autocmd_bufnr;
1915 char_u *save_autocmd_match;
1916 int save_autocmd_busy;
1917 int save_autocmd_nested;
1918 static int nesting = 0;
LemonBoyeca7c602022-04-14 15:39:43 +01001919 AutoPatCmd_T patcmd;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001920 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001921 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001922#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001923 funccal_entry_T funccal_entry;
1924 char_u *save_cmdarg;
1925 long save_cmdbang;
1926#endif
1927 static int filechangeshell_busy = FALSE;
1928#ifdef FEAT_PROFILE
1929 proftime_T wait_time;
1930#endif
1931 int did_save_redobuff = FALSE;
1932 save_redo_T save_redo;
1933 int save_KeyTyped = KeyTyped;
ichizokc3f91c02021-12-17 09:44:33 +00001934 int save_did_emsg;
Bram Moolenaare31ee862020-01-07 20:59:34 +01001935 ESTACK_CHECK_DECLARATION
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001936
1937 /*
1938 * Quickly return if there are no autocommands for this event or
1939 * autocommands are blocked.
1940 */
1941 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
1942 || autocmd_blocked > 0)
1943 goto BYPASS_AU;
1944
1945 /*
1946 * When autocommands are busy, new autocommands are only executed when
1947 * explicitly enabled with the "nested" flag.
1948 */
1949 if (autocmd_busy && !(force || autocmd_nested))
1950 goto BYPASS_AU;
1951
1952#ifdef FEAT_EVAL
1953 /*
1954 * Quickly return when immediately aborting on error, or when an interrupt
1955 * occurred or an exception was thrown but not caught.
1956 */
1957 if (aborting())
1958 goto BYPASS_AU;
1959#endif
1960
1961 /*
1962 * FileChangedShell never nests, because it can create an endless loop.
1963 */
1964 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
1965 || event == EVENT_FILECHANGEDSHELLPOST))
1966 goto BYPASS_AU;
1967
1968 /*
1969 * Ignore events in 'eventignore'.
1970 */
1971 if (event_ignored(event))
1972 goto BYPASS_AU;
1973
1974 /*
1975 * Allow nesting of autocommands, but restrict the depth, because it's
1976 * possible to create an endless loop.
1977 */
1978 if (nesting == 10)
1979 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001980 emsg(_(e_autocommand_nesting_too_deep));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001981 goto BYPASS_AU;
1982 }
1983
1984 /*
1985 * Check if these autocommands are disabled. Used when doing ":all" or
1986 * ":ball".
1987 */
1988 if ( (autocmd_no_enter
1989 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
1990 || (autocmd_no_leave
1991 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
1992 goto BYPASS_AU;
1993
1994 /*
1995 * Save the autocmd_* variables and info about the current buffer.
1996 */
1997 save_autocmd_fname = autocmd_fname;
1998 save_autocmd_fname_full = autocmd_fname_full;
1999 save_autocmd_bufnr = autocmd_bufnr;
2000 save_autocmd_match = autocmd_match;
2001 save_autocmd_busy = autocmd_busy;
2002 save_autocmd_nested = autocmd_nested;
2003 save_changed = curbuf->b_changed;
2004 old_curbuf = curbuf;
2005
2006 /*
2007 * Set the file name to be used for <afile>.
2008 * Make a copy to avoid that changing a buffer name or directory makes it
2009 * invalid.
2010 */
2011 if (fname_io == NULL)
2012 {
2013 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002014 || event == EVENT_OPTIONSET
2015 || event == EVENT_MODECHANGED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002016 autocmd_fname = NULL;
2017 else if (fname != NULL && !ends_excmd(*fname))
2018 autocmd_fname = fname;
2019 else if (buf != NULL)
2020 autocmd_fname = buf->b_ffname;
2021 else
2022 autocmd_fname = NULL;
2023 }
2024 else
2025 autocmd_fname = fname_io;
2026 if (autocmd_fname != NULL)
2027 autocmd_fname = vim_strsave(autocmd_fname);
2028 autocmd_fname_full = FALSE; // call FullName_save() later
2029
2030 /*
2031 * Set the buffer number to be used for <abuf>.
2032 */
2033 if (buf == NULL)
2034 autocmd_bufnr = 0;
2035 else
2036 autocmd_bufnr = buf->b_fnum;
2037
2038 /*
2039 * When the file name is NULL or empty, use the file name of buffer "buf".
2040 * Always use the full path of the file name to match with, in case
2041 * "allow_dirs" is set.
2042 */
2043 if (fname == NULL || *fname == NUL)
2044 {
2045 if (buf == NULL)
2046 fname = NULL;
2047 else
2048 {
2049#ifdef FEAT_SYN_HL
2050 if (event == EVENT_SYNTAX)
2051 fname = buf->b_p_syn;
2052 else
2053#endif
2054 if (event == EVENT_FILETYPE)
2055 fname = buf->b_p_ft;
2056 else
2057 {
2058 if (buf->b_sfname != NULL)
2059 sfname = vim_strsave(buf->b_sfname);
2060 fname = buf->b_ffname;
2061 }
2062 }
2063 if (fname == NULL)
2064 fname = (char_u *)"";
2065 fname = vim_strsave(fname); // make a copy, so we can change it
2066 }
2067 else
2068 {
2069 sfname = vim_strsave(fname);
2070 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002071 // ColorScheme, QuickFixCmd*, DirChanged and similar.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002072 if (event == EVENT_FILETYPE
2073 || event == EVENT_SYNTAX
2074 || event == EVENT_CMDLINECHANGED
2075 || event == EVENT_CMDLINEENTER
2076 || event == EVENT_CMDLINELEAVE
2077 || event == EVENT_CMDWINENTER
2078 || event == EVENT_CMDWINLEAVE
2079 || event == EVENT_CMDUNDEFINED
2080 || event == EVENT_FUNCUNDEFINED
2081 || event == EVENT_REMOTEREPLY
2082 || event == EVENT_SPELLFILEMISSING
2083 || event == EVENT_QUICKFIXCMDPRE
2084 || event == EVENT_COLORSCHEME
2085 || event == EVENT_COLORSCHEMEPRE
2086 || event == EVENT_OPTIONSET
2087 || event == EVENT_QUICKFIXCMDPOST
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002088 || event == EVENT_DIRCHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002089 || event == EVENT_DIRCHANGEDPRE
naohiro ono23beefe2021-11-13 12:38:49 +00002090 || event == EVENT_MODECHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002091 || event == EVENT_USER
LemonBoy09371822022-04-08 15:18:45 +01002092 || event == EVENT_WINCLOSED
2093 || event == EVENT_WINSCROLLED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002094 {
2095 fname = vim_strsave(fname);
2096 autocmd_fname_full = TRUE; // don't expand it later
2097 }
2098 else
2099 fname = FullName_save(fname, FALSE);
2100 }
2101 if (fname == NULL) // out of memory
2102 {
2103 vim_free(sfname);
2104 retval = FALSE;
2105 goto BYPASS_AU;
2106 }
2107
2108#ifdef BACKSLASH_IN_FILENAME
2109 /*
2110 * Replace all backslashes with forward slashes. This makes the
2111 * autocommand patterns portable between Unix and MS-DOS.
2112 */
2113 if (sfname != NULL)
2114 forward_slash(sfname);
2115 forward_slash(fname);
2116#endif
2117
2118#ifdef VMS
2119 // remove version for correct match
2120 if (sfname != NULL)
2121 vms_remove_version(sfname);
2122 vms_remove_version(fname);
2123#endif
2124
2125 /*
2126 * Set the name to be used for <amatch>.
2127 */
2128 autocmd_match = fname;
2129
2130
2131 // Don't redraw while doing autocommands.
2132 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002133
2134 // name and lnum are filled in later
2135 estack_push(ETYPE_AUCMD, NULL, 0);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002136 ESTACK_CHECK_SETUP
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002137
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002138 save_current_sctx = current_sctx;
2139
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002140#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002141# ifdef FEAT_PROFILE
2142 if (do_profiling == PROF_YES)
2143 prof_child_enter(&wait_time); // doesn't count for the caller itself
2144# endif
2145
2146 // Don't use local function variables, if called from a function.
2147 save_funccal(&funccal_entry);
2148#endif
2149
2150 /*
2151 * When starting to execute autocommands, save the search patterns.
2152 */
2153 if (!autocmd_busy)
2154 {
2155 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002156 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002157 {
2158 saveRedobuff(&save_redo);
2159 did_save_redobuff = TRUE;
2160 }
2161 did_filetype = keep_filetype;
2162 }
2163
2164 /*
2165 * Note that we are applying autocmds. Some commands need to know.
2166 */
2167 autocmd_busy = TRUE;
2168 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2169 ++nesting; // see matching decrement below
2170
2171 // Remember that FileType was triggered. Used for did_filetype().
2172 if (event == EVENT_FILETYPE)
2173 did_filetype = TRUE;
2174
2175 tail = gettail(fname);
2176
2177 // Find first autocommand that matches
LemonBoyeca7c602022-04-14 15:39:43 +01002178 CLEAR_FIELD(patcmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002179 patcmd.curpat = first_autopat[(int)event];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002180 patcmd.group = group;
2181 patcmd.fname = fname;
2182 patcmd.sfname = sfname;
2183 patcmd.tail = tail;
2184 patcmd.event = event;
2185 patcmd.arg_bufnr = autocmd_bufnr;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002186 auto_next_pat(&patcmd, FALSE);
2187
2188 // found one, start executing the autocommands
2189 if (patcmd.curpat != NULL)
2190 {
2191 // add to active_apc_list
2192 patcmd.next = active_apc_list;
2193 active_apc_list = &patcmd;
2194
2195#ifdef FEAT_EVAL
2196 // set v:cmdarg (only when there is a matching pattern)
2197 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2198 if (eap != NULL)
2199 {
2200 save_cmdarg = set_cmdarg(eap, NULL);
2201 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2202 }
2203 else
2204 save_cmdarg = NULL; // avoid gcc warning
2205#endif
2206 retval = TRUE;
2207 // mark the last pattern, to avoid an endless loop when more patterns
2208 // are added when executing autocommands
2209 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2210 ap->last = FALSE;
2211 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002212
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002213 // Make sure cursor and topline are valid. The first time the current
2214 // values are saved, restored by reset_lnums(). When nested only the
2215 // values are corrected when needed.
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002216 if (nesting == 1)
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002217 check_lnums(TRUE);
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002218 else
2219 check_lnums_nested(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002220
ichizokc3f91c02021-12-17 09:44:33 +00002221 save_did_emsg = did_emsg;
2222
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002223 do_cmdline(NULL, getnextac, (void *)&patcmd,
2224 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002225
ichizokc3f91c02021-12-17 09:44:33 +00002226 did_emsg += save_did_emsg;
2227
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002228 if (nesting == 1)
2229 // restore cursor and topline, unless they were changed
2230 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002231
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002232#ifdef FEAT_EVAL
2233 if (eap != NULL)
2234 {
2235 (void)set_cmdarg(NULL, save_cmdarg);
2236 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2237 }
2238#endif
2239 // delete from active_apc_list
2240 if (active_apc_list == &patcmd) // just in case
2241 active_apc_list = patcmd.next;
2242 }
2243
2244 --RedrawingDisabled;
2245 autocmd_busy = save_autocmd_busy;
2246 filechangeshell_busy = FALSE;
2247 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002248 vim_free(SOURCING_NAME);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002249 ESTACK_CHECK_NOW
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002250 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002251 vim_free(autocmd_fname);
2252 autocmd_fname = save_autocmd_fname;
2253 autocmd_fname_full = save_autocmd_fname_full;
2254 autocmd_bufnr = save_autocmd_bufnr;
2255 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002256 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002257#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002258 restore_funccal();
2259# ifdef FEAT_PROFILE
2260 if (do_profiling == PROF_YES)
2261 prof_child_exit(&wait_time);
2262# endif
2263#endif
2264 KeyTyped = save_KeyTyped;
2265 vim_free(fname);
2266 vim_free(sfname);
2267 --nesting; // see matching increment above
2268
2269 /*
2270 * When stopping to execute autocommands, restore the search patterns and
2271 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2272 * free any windows in the au_pending_free_win list.
2273 */
2274 if (!autocmd_busy)
2275 {
2276 restore_search_patterns();
2277 if (did_save_redobuff)
2278 restoreRedobuff(&save_redo);
2279 did_filetype = FALSE;
2280 while (au_pending_free_buf != NULL)
2281 {
2282 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002283
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002284 vim_free(au_pending_free_buf);
2285 au_pending_free_buf = b;
2286 }
2287 while (au_pending_free_win != NULL)
2288 {
2289 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002290
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002291 vim_free(au_pending_free_win);
2292 au_pending_free_win = w;
2293 }
2294 }
2295
2296 /*
2297 * Some events don't set or reset the Changed flag.
2298 * Check if still in the same buffer!
2299 */
2300 if (curbuf == old_curbuf
2301 && (event == EVENT_BUFREADPOST
2302 || event == EVENT_BUFWRITEPOST
2303 || event == EVENT_FILEAPPENDPOST
2304 || event == EVENT_VIMLEAVE
2305 || event == EVENT_VIMLEAVEPRE))
2306 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002307 if (curbuf->b_changed != save_changed)
2308 need_maketitle = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002309 curbuf->b_changed = save_changed;
2310 }
2311
2312 au_cleanup(); // may really delete removed patterns/commands now
2313
2314BYPASS_AU:
2315 // When wiping out a buffer make sure all its buffer-local autocommands
2316 // are deleted.
2317 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2318 aubuflocal_remove(buf);
2319
2320 if (retval == OK && event == EVENT_FILETYPE)
2321 au_did_filetype = TRUE;
2322
2323 return retval;
2324}
2325
2326# ifdef FEAT_EVAL
2327static char_u *old_termresponse = NULL;
2328# endif
2329
2330/*
2331 * Block triggering autocommands until unblock_autocmd() is called.
2332 * Can be used recursively, so long as it's symmetric.
2333 */
2334 void
2335block_autocmds(void)
2336{
2337# ifdef FEAT_EVAL
2338 // Remember the value of v:termresponse.
2339 if (autocmd_blocked == 0)
2340 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
2341# endif
2342 ++autocmd_blocked;
2343}
2344
2345 void
2346unblock_autocmds(void)
2347{
2348 --autocmd_blocked;
2349
2350# ifdef FEAT_EVAL
2351 // When v:termresponse was set while autocommands were blocked, trigger
2352 // the autocommands now. Esp. useful when executing a shell command
2353 // during startup (vimdiff).
2354 if (autocmd_blocked == 0
2355 && get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2356 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2357# endif
2358}
2359
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002360 int
2361is_autocmd_blocked(void)
2362{
2363 return autocmd_blocked != 0;
2364}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002365
2366/*
2367 * Find next autocommand pattern that matches.
2368 */
2369 static void
2370auto_next_pat(
LemonBoyeca7c602022-04-14 15:39:43 +01002371 AutoPatCmd_T *apc,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002372 int stop_at_last) // stop when 'last' flag is set
2373{
2374 AutoPat *ap;
2375 AutoCmd *cp;
2376 char_u *name;
2377 char *s;
LemonBoyeca7c602022-04-14 15:39:43 +01002378 estack_T *entry;
2379 char_u *namep;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002380
LemonBoyeca7c602022-04-14 15:39:43 +01002381 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
2382
2383 // Clear the exestack entry for this ETYPE_AUCMD entry.
2384 VIM_CLEAR(entry->es_name);
2385 entry->es_info.aucmd = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002386
2387 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2388 {
2389 apc->curpat = NULL;
2390
2391 // Only use a pattern when it has not been removed, has commands and
2392 // the group matches. For buffer-local autocommands only check the
2393 // buffer number.
2394 if (ap->pat != NULL && ap->cmds != NULL
2395 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2396 {
2397 // execution-condition
2398 if (ap->buflocal_nr == 0
2399 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2400 apc->sfname, apc->tail, ap->allow_dirs))
2401 : ap->buflocal_nr == apc->arg_bufnr)
2402 {
2403 name = event_nr2name(apc->event);
2404 s = _("%s Autocommands for \"%s\"");
LemonBoyeca7c602022-04-14 15:39:43 +01002405 namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1);
2406 if (namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002407 {
LemonBoyeca7c602022-04-14 15:39:43 +01002408 sprintf((char *)namep, s, (char *)name, (char *)ap->pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002409 if (p_verbose >= 8)
2410 {
2411 verbose_enter();
LemonBoyeca7c602022-04-14 15:39:43 +01002412 smsg(_("Executing %s"), namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002413 verbose_leave();
2414 }
2415 }
2416
LemonBoyeca7c602022-04-14 15:39:43 +01002417 // Update the exestack entry for this autocmd.
2418 entry->es_name = namep;
2419 entry->es_info.aucmd = apc;
2420
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002421 apc->curpat = ap;
2422 apc->nextcmd = ap->cmds;
2423 // mark last command
2424 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2425 cp->last = FALSE;
2426 cp->last = TRUE;
2427 }
2428 line_breakcheck();
2429 if (apc->curpat != NULL) // found a match
2430 break;
2431 }
2432 if (stop_at_last && ap->last)
2433 break;
2434 }
2435}
2436
2437/*
LemonBoyeca7c602022-04-14 15:39:43 +01002438 * Get the script context where autocommand "acp" is defined.
2439 */
2440 sctx_T *
2441acp_script_ctx(AutoPatCmd_T *acp)
2442{
2443 return &acp->script_ctx;
2444}
2445
2446/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002447 * Get next autocommand command.
2448 * Called by do_cmdline() to get the next line for ":if".
2449 * Returns allocated string, or NULL for end of autocommands.
2450 */
2451 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002452getnextac(
2453 int c UNUSED,
2454 void *cookie,
2455 int indent UNUSED,
2456 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002457{
LemonBoyeca7c602022-04-14 15:39:43 +01002458 AutoPatCmd_T *acp = (AutoPatCmd_T *)cookie;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002459 char_u *retval;
2460 AutoCmd *ac;
2461
2462 // Can be called again after returning the last line.
2463 if (acp->curpat == NULL)
2464 return NULL;
2465
2466 // repeat until we find an autocommand to execute
2467 for (;;)
2468 {
2469 // skip removed commands
2470 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2471 if (acp->nextcmd->last)
2472 acp->nextcmd = NULL;
2473 else
2474 acp->nextcmd = acp->nextcmd->next;
2475
2476 if (acp->nextcmd != NULL)
2477 break;
2478
2479 // at end of commands, find next pattern that matches
2480 if (acp->curpat->last)
2481 acp->curpat = NULL;
2482 else
2483 acp->curpat = acp->curpat->next;
2484 if (acp->curpat != NULL)
2485 auto_next_pat(acp, TRUE);
2486 if (acp->curpat == NULL)
2487 return NULL;
2488 }
2489
2490 ac = acp->nextcmd;
2491
2492 if (p_verbose >= 9)
2493 {
2494 verbose_enter_scroll();
2495 smsg(_("autocommand %s"), ac->cmd);
2496 msg_puts("\n"); // don't overwrite this either
2497 verbose_leave_scroll();
2498 }
2499 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002500 // Remove one-shot ("once") autocmd in anticipation of its execution.
2501 if (ac->once)
2502 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002503 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002504 current_sctx = ac->script_ctx;
LemonBoyeca7c602022-04-14 15:39:43 +01002505 acp->script_ctx = current_sctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002506 if (ac->last)
2507 acp->nextcmd = NULL;
2508 else
2509 acp->nextcmd = ac->next;
2510 return retval;
2511}
2512
2513/*
2514 * Return TRUE if there is a matching autocommand for "fname".
2515 * To account for buffer-local autocommands, function needs to know
2516 * in which buffer the file will be opened.
2517 */
2518 int
2519has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2520{
2521 AutoPat *ap;
2522 char_u *fname;
2523 char_u *tail = gettail(sfname);
2524 int retval = FALSE;
2525
2526 fname = FullName_save(sfname, FALSE);
2527 if (fname == NULL)
2528 return FALSE;
2529
2530#ifdef BACKSLASH_IN_FILENAME
2531 /*
2532 * Replace all backslashes with forward slashes. This makes the
2533 * autocommand patterns portable between Unix and MS-DOS.
2534 */
2535 sfname = vim_strsave(sfname);
2536 if (sfname != NULL)
2537 forward_slash(sfname);
2538 forward_slash(fname);
2539#endif
2540
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002541 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002542 if (ap->pat != NULL && ap->cmds != NULL
2543 && (ap->buflocal_nr == 0
2544 ? match_file_pat(NULL, &ap->reg_prog,
2545 fname, sfname, tail, ap->allow_dirs)
2546 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2547 ))
2548 {
2549 retval = TRUE;
2550 break;
2551 }
2552
2553 vim_free(fname);
2554#ifdef BACKSLASH_IN_FILENAME
2555 vim_free(sfname);
2556#endif
2557
2558 return retval;
2559}
2560
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002561/*
2562 * Function given to ExpandGeneric() to obtain the list of autocommand group
2563 * names.
2564 */
2565 char_u *
2566get_augroup_name(expand_T *xp UNUSED, int idx)
2567{
2568 if (idx == augroups.ga_len) // add "END" add the end
2569 return (char_u *)"END";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002570 if (idx < 0 || idx >= augroups.ga_len) // end of list
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002571 return NULL;
2572 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2573 // skip deleted entries
2574 return (char_u *)"";
2575 return AUGROUP_NAME(idx); // return a name
2576}
2577
2578static int include_groups = FALSE;
2579
2580 char_u *
2581set_context_in_autocmd(
2582 expand_T *xp,
2583 char_u *arg,
2584 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2585{
2586 char_u *p;
2587 int group;
2588
2589 // check for a group name, skip it if present
2590 include_groups = FALSE;
2591 p = arg;
2592 group = au_get_grouparg(&arg);
2593 if (group == AUGROUP_ERROR)
2594 return NULL;
2595 // If there only is a group name that's what we expand.
2596 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2597 {
2598 arg = p;
2599 group = AUGROUP_ALL;
2600 }
2601
2602 // skip over event name
2603 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2604 if (*p == ',')
2605 arg = p + 1;
2606 if (*p == NUL)
2607 {
2608 if (group == AUGROUP_ALL)
2609 include_groups = TRUE;
2610 xp->xp_context = EXPAND_EVENTS; // expand event name
2611 xp->xp_pattern = arg;
2612 return NULL;
2613 }
2614
2615 // skip over pattern
2616 arg = skipwhite(p);
2617 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2618 arg++;
2619 if (*arg)
2620 return arg; // expand (next) command
2621
2622 if (doautocmd)
2623 xp->xp_context = EXPAND_FILES; // expand file names
2624 else
2625 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2626 return NULL;
2627}
2628
2629/*
2630 * Function given to ExpandGeneric() to obtain the list of event names.
2631 */
2632 char_u *
2633get_event_name(expand_T *xp UNUSED, int idx)
2634{
2635 if (idx < augroups.ga_len) // First list group names, if wanted
2636 {
2637 if (!include_groups || AUGROUP_NAME(idx) == NULL
2638 || AUGROUP_NAME(idx) == get_deleted_augroup())
2639 return (char_u *)""; // skip deleted entries
2640 return AUGROUP_NAME(idx); // return a name
2641 }
2642 return (char_u *)event_names[idx - augroups.ga_len].name;
2643}
2644
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002645
2646#if defined(FEAT_EVAL) || defined(PROTO)
2647/*
2648 * Return TRUE if autocmd is supported.
2649 */
2650 int
2651autocmd_supported(char_u *name)
2652{
2653 char_u *p;
2654
2655 return (event_name2nr(name, &p) != NUM_EVENTS);
2656}
2657
2658/*
2659 * Return TRUE if an autocommand is defined for a group, event and
2660 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2661 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2662 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2663 * Used for:
2664 * exists("#Group") or
2665 * exists("#Group#Event") or
2666 * exists("#Group#Event#pat") or
2667 * exists("#Event") or
2668 * exists("#Event#pat")
2669 */
2670 int
2671au_exists(char_u *arg)
2672{
2673 char_u *arg_save;
2674 char_u *pattern = NULL;
2675 char_u *event_name;
2676 char_u *p;
2677 event_T event;
2678 AutoPat *ap;
2679 buf_T *buflocal_buf = NULL;
2680 int group;
2681 int retval = FALSE;
2682
2683 // Make a copy so that we can change the '#' chars to a NUL.
2684 arg_save = vim_strsave(arg);
2685 if (arg_save == NULL)
2686 return FALSE;
2687 p = vim_strchr(arg_save, '#');
2688 if (p != NULL)
2689 *p++ = NUL;
2690
2691 // First, look for an autocmd group name
2692 group = au_find_group(arg_save);
2693 if (group == AUGROUP_ERROR)
2694 {
2695 // Didn't match a group name, assume the first argument is an event.
2696 group = AUGROUP_ALL;
2697 event_name = arg_save;
2698 }
2699 else
2700 {
2701 if (p == NULL)
2702 {
2703 // "Group": group name is present and it's recognized
2704 retval = TRUE;
2705 goto theend;
2706 }
2707
2708 // Must be "Group#Event" or "Group#Event#pat".
2709 event_name = p;
2710 p = vim_strchr(event_name, '#');
2711 if (p != NULL)
2712 *p++ = NUL; // "Group#Event#pat"
2713 }
2714
2715 pattern = p; // "pattern" is NULL when there is no pattern
2716
2717 // find the index (enum) for the event name
2718 event = event_name2nr(event_name, &p);
2719
2720 // return FALSE if the event name is not recognized
2721 if (event == NUM_EVENTS)
2722 goto theend;
2723
2724 // Find the first autocommand for this event.
2725 // If there isn't any, return FALSE;
2726 // If there is one and no pattern given, return TRUE;
2727 ap = first_autopat[(int)event];
2728 if (ap == NULL)
2729 goto theend;
2730
2731 // if pattern is "<buffer>", special handling is needed which uses curbuf
2732 // for pattern "<buffer=N>, fnamecmp() will work fine
2733 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2734 buflocal_buf = curbuf;
2735
2736 // Check if there is an autocommand with the given pattern.
2737 for ( ; ap != NULL; ap = ap->next)
2738 // only use a pattern when it has not been removed and has commands.
2739 // For buffer-local autocommands, fnamecmp() works fine.
2740 if (ap->pat != NULL && ap->cmds != NULL
2741 && (group == AUGROUP_ALL || ap->group == group)
2742 && (pattern == NULL
2743 || (buflocal_buf == NULL
2744 ? fnamecmp(ap->pat, pattern) == 0
2745 : ap->buflocal_nr == buflocal_buf->b_fnum)))
2746 {
2747 retval = TRUE;
2748 break;
2749 }
2750
2751theend:
2752 vim_free(arg_save);
2753 return retval;
2754}
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002755
2756/*
2757 * autocmd_add() and autocmd_delete() functions
2758 */
2759 static void
2760autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
2761{
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002762 list_T *aucmd_list;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002763 listitem_T *li;
2764 dict_T *event_dict;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002765 dictitem_T *di;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002766 char_u *event_name = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002767 list_T *event_list;
2768 listitem_T *eli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002769 event_T event;
2770 char_u *group_name = NULL;
2771 int group;
2772 char_u *pat = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002773 list_T *pat_list;
2774 listitem_T *pli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002775 char_u *cmd = NULL;
2776 char_u *end;
2777 int once;
2778 int nested;
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01002779 int replace; // replace the cmd for a group/event
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002780 int retval = VVAL_TRUE;
2781 int save_augroup = current_augroup;
2782
2783 rettv->v_type = VAR_BOOL;
2784 rettv->vval.v_number = VVAL_FALSE;
2785
2786 if (check_for_list_arg(argvars, 0) == FAIL)
2787 return;
2788
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002789 aucmd_list = argvars[0].vval.v_list;
2790 if (aucmd_list == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002791 return;
2792
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002793 FOR_ALL_LIST_ITEMS(aucmd_list, li)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002794 {
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002795 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002796 VIM_CLEAR(cmd);
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002797 event_name = NULL;
2798 event_list = NULL;
2799 pat = NULL;
2800 pat_list = NULL;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002801
2802 if (li->li_tv.v_type != VAR_DICT)
2803 continue;
2804
2805 event_dict = li->li_tv.vval.v_dict;
2806 if (event_dict == NULL)
2807 continue;
2808
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002809 di = dict_find(event_dict, (char_u *)"event", -1);
2810 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002811 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002812 if (di->di_tv.v_type == VAR_STRING)
2813 {
2814 event_name = di->di_tv.vval.v_string;
2815 if (event_name == NULL)
2816 {
2817 emsg(_(e_string_required));
2818 continue;
2819 }
2820 }
2821 else if (di->di_tv.v_type == VAR_LIST)
2822 {
2823 event_list = di->di_tv.vval.v_list;
2824 if (event_list == NULL)
2825 {
2826 emsg(_(e_list_required));
2827 continue;
2828 }
2829 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002830 else
2831 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002832 emsg(_(e_string_or_list_expected));
2833 continue;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002834 }
2835 }
2836
Bram Moolenaard61efa52022-07-23 09:52:04 +01002837 group_name = dict_get_string(event_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002838 if (group_name == NULL || *group_name == NUL)
2839 // if the autocmd group name is not specified, then use the current
2840 // autocmd group
2841 group = current_augroup;
2842 else
2843 {
2844 group = au_find_group(group_name);
2845 if (group == AUGROUP_ERROR)
2846 {
2847 if (delete)
2848 {
2849 semsg(_(e_no_such_group_str), group_name);
2850 retval = VVAL_FALSE;
2851 break;
2852 }
2853 // group is not found, create it now
2854 group = au_new_group(group_name);
2855 if (group == AUGROUP_ERROR)
2856 {
2857 semsg(_(e_no_such_group_str), group_name);
2858 retval = VVAL_FALSE;
2859 break;
2860 }
2861
2862 current_augroup = group;
2863 }
2864 }
2865
2866 // if a buffer number is specified, then generate a pattern of the form
2867 // "<buffer=n>. Otherwise, use the pattern supplied by the user.
2868 if (dict_has_key(event_dict, "bufnr"))
2869 {
2870 varnumber_T bnum;
2871
Bram Moolenaard61efa52022-07-23 09:52:04 +01002872 bnum = dict_get_number_def(event_dict, "bufnr", -1);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002873 if (bnum == -1)
2874 continue;
2875
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002876 vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
2877 pat = IObuff;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002878 }
2879 else
2880 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002881 di = dict_find(event_dict, (char_u *)"pattern", -1);
2882 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002883 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002884 if (di->di_tv.v_type == VAR_STRING)
2885 {
2886 pat = di->di_tv.vval.v_string;
2887 if (pat == NULL)
2888 {
2889 emsg(_(e_string_required));
2890 continue;
2891 }
2892 }
2893 else if (di->di_tv.v_type == VAR_LIST)
2894 {
2895 pat_list = di->di_tv.vval.v_list;
2896 if (pat_list == NULL)
2897 {
2898 emsg(_(e_list_required));
2899 continue;
2900 }
2901 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002902 else
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002903 {
2904 emsg(_(e_string_or_list_expected));
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002905 continue;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002906 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002907 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002908 else if (delete)
2909 pat = (char_u *)"";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002910 }
2911
Bram Moolenaard61efa52022-07-23 09:52:04 +01002912 once = dict_get_bool(event_dict, "once", FALSE);
2913 nested = dict_get_bool(event_dict, "nested", FALSE);
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01002914 // if 'replace' is true, then remove all the commands associated with
2915 // this autocmd event/group and add the new command.
Bram Moolenaard61efa52022-07-23 09:52:04 +01002916 replace = dict_get_bool(event_dict, "replace", FALSE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002917
Bram Moolenaard61efa52022-07-23 09:52:04 +01002918 cmd = dict_get_string(event_dict, "cmd", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002919 if (cmd == NULL)
2920 {
2921 if (delete)
2922 cmd = vim_strsave((char_u *)"");
2923 else
2924 continue;
2925 }
2926
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002927 if (delete && (event_name == NULL
2928 || (event_name[0] == '*' && event_name[1] == NUL)))
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002929 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002930 // if the event name is not specified or '*', delete all the events
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002931 for (event = (event_T)0; (int)event < NUM_EVENTS;
2932 event = (event_T)((int)event + 1))
2933 {
2934 if (do_autocmd_event(event, pat, once, nested, cmd, delete,
2935 group, 0) == FAIL)
2936 {
2937 retval = VVAL_FALSE;
2938 break;
2939 }
2940 }
2941 }
2942 else
2943 {
Bram Moolenaar968443e2022-05-27 21:16:34 +01002944 char_u *p = NULL;
2945
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002946 eli = NULL;
2947 end = NULL;
2948 while (TRUE)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002949 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002950 if (event_list != NULL)
2951 {
2952 if (eli == NULL)
2953 eli = event_list->lv_first;
2954 else
2955 eli = eli->li_next;
2956 if (eli == NULL)
2957 break;
2958 if (eli->li_tv.v_type != VAR_STRING
Bram Moolenaar882476a2022-06-01 16:02:38 +01002959 || (p = eli->li_tv.vval.v_string) == NULL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002960 {
2961 emsg(_(e_string_required));
Bram Moolenaar882476a2022-06-01 16:02:38 +01002962 break;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002963 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002964 }
2965 else
2966 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01002967 if (p == NULL)
2968 p = event_name;
2969 if (p == NULL || *p == NUL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002970 break;
2971 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002972
2973 event = event_name2nr(p, &end);
2974 if (event == NUM_EVENTS || *end != NUL)
2975 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01002976 // this also catches something following a valid event name
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002977 semsg(_(e_no_such_event_str), p);
2978 retval = VVAL_FALSE;
2979 break;
2980 }
2981 if (pat != NULL)
2982 {
2983 if (do_autocmd_event(event, pat, once, nested, cmd,
2984 delete | replace, group, 0) == FAIL)
2985 {
2986 retval = VVAL_FALSE;
2987 break;
2988 }
2989 }
2990 else if (pat_list != NULL)
2991 {
2992 FOR_ALL_LIST_ITEMS(pat_list, pli)
2993 {
2994 if (pli->li_tv.v_type != VAR_STRING
2995 || pli->li_tv.vval.v_string == NULL)
2996 {
2997 emsg(_(e_string_required));
2998 continue;
2999 }
3000 if (do_autocmd_event(event,
3001 pli->li_tv.vval.v_string, once, nested,
3002 cmd, delete | replace, group, 0) ==
3003 FAIL)
3004 {
3005 retval = VVAL_FALSE;
3006 break;
3007 }
3008 }
3009 if (retval == VVAL_FALSE)
3010 break;
3011 }
3012 if (event_name != NULL)
3013 p = end;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003014 }
3015 }
3016
3017 // if only the autocmd group name is specified for delete and the
3018 // autocmd event, pattern and cmd are not specified, then delete the
3019 // autocmd group.
3020 if (delete && group_name != NULL &&
3021 (event_name == NULL || event_name[0] == NUL)
3022 && (pat == NULL || pat[0] == NUL)
3023 && (cmd == NULL || cmd[0] == NUL))
3024 au_del_group(group_name);
3025 }
3026
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003027 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003028 VIM_CLEAR(cmd);
3029
3030 current_augroup = save_augroup;
3031 rettv->vval.v_number = retval;
3032}
3033
3034/*
3035 * autocmd_add() function
3036 */
3037 void
3038f_autocmd_add(typval_T *argvars, typval_T *rettv)
3039{
3040 autocmd_add_or_delete(argvars, rettv, FALSE);
3041}
3042
3043/*
3044 * autocmd_delete() function
3045 */
3046 void
3047f_autocmd_delete(typval_T *argvars, typval_T *rettv)
3048{
3049 autocmd_add_or_delete(argvars, rettv, TRUE);
3050}
3051
3052/*
3053 * autocmd_get() function
3054 * Returns a List of autocmds.
3055 */
3056 void
3057f_autocmd_get(typval_T *argvars, typval_T *rettv)
3058{
3059 event_T event_arg = NUM_EVENTS;
3060 event_T event;
3061 AutoPat *ap;
3062 AutoCmd *ac;
3063 list_T *event_list;
3064 dict_T *event_dict;
3065 char_u *event_name = NULL;
3066 char_u *pat = NULL;
3067 char_u *name = NULL;
3068 int group = AUGROUP_ALL;
3069
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01003070 if (rettv_list_alloc(rettv) == FAIL)
3071 return;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003072 if (check_for_opt_dict_arg(argvars, 0) == FAIL)
3073 return;
3074
3075 if (argvars[0].v_type == VAR_DICT)
3076 {
3077 // return only the autocmds in the specified group
3078 if (dict_has_key(argvars[0].vval.v_dict, "group"))
3079 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003080 name = dict_get_string(argvars[0].vval.v_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003081 if (name == NULL)
3082 return;
3083
3084 if (*name == NUL)
3085 group = AUGROUP_DEFAULT;
3086 else
3087 {
3088 group = au_find_group(name);
3089 if (group == AUGROUP_ERROR)
3090 {
3091 semsg(_(e_no_such_group_str), name);
3092 vim_free(name);
3093 return;
3094 }
3095 }
3096 vim_free(name);
3097 }
3098
3099 // return only the autocmds for the specified event
3100 if (dict_has_key(argvars[0].vval.v_dict, "event"))
3101 {
3102 int i;
3103
Bram Moolenaard61efa52022-07-23 09:52:04 +01003104 name = dict_get_string(argvars[0].vval.v_dict, "event", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003105 if (name == NULL)
3106 return;
3107
3108 if (name[0] == '*' && name[1] == NUL)
3109 event_arg = NUM_EVENTS;
3110 else
3111 {
3112 for (i = 0; event_names[i].name != NULL; i++)
3113 if (STRICMP(event_names[i].name, name) == 0)
3114 break;
3115 if (event_names[i].name == NULL)
3116 {
3117 semsg(_(e_no_such_event_str), name);
3118 vim_free(name);
3119 return;
3120 }
3121 event_arg = event_names[i].event;
3122 }
3123 vim_free(name);
3124 }
3125
3126 // return only the autocmds for the specified pattern
3127 if (dict_has_key(argvars[0].vval.v_dict, "pattern"))
3128 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003129 pat = dict_get_string(argvars[0].vval.v_dict, "pattern", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003130 if (pat == NULL)
3131 return;
3132 }
3133 }
3134
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003135 event_list = rettv->vval.v_list;
3136
3137 // iterate through all the autocmd events
3138 for (event = (event_T)0; (int)event < NUM_EVENTS;
3139 event = (event_T)((int)event + 1))
3140 {
3141 if (event_arg != NUM_EVENTS && event != event_arg)
3142 continue;
3143
3144 event_name = event_nr2name(event);
3145
3146 // iterate through all the patterns for this autocmd event
3147 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
3148 {
3149 char_u *group_name;
3150
3151 if (group != AUGROUP_ALL && group != ap->group)
3152 continue;
3153
3154 if (pat != NULL && STRCMP(pat, ap->pat) != 0)
3155 continue;
3156
3157 group_name = get_augroup_name(NULL, ap->group);
3158
3159 // iterate through all the commands for this pattern and add one
3160 // item for each cmd.
3161 for (ac = ap->cmds; ac != NULL; ac = ac->next)
3162 {
3163 event_dict = dict_alloc();
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003164 if (event_dict == NULL
3165 || list_append_dict(event_list, event_dict) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003166 return;
3167
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003168 if (dict_add_string(event_dict, "event", event_name) == FAIL
3169 || dict_add_string(event_dict, "group",
3170 group_name == NULL ? (char_u *)""
3171 : group_name) == FAIL
3172 || (ap->buflocal_nr != 0
3173 && (dict_add_number(event_dict, "bufnr",
3174 ap->buflocal_nr) == FAIL))
3175 || dict_add_string(event_dict, "pattern",
3176 ap->pat) == FAIL
3177 || dict_add_string(event_dict, "cmd", ac->cmd) == FAIL
3178 || dict_add_bool(event_dict, "once", ac->once) == FAIL
3179 || dict_add_bool(event_dict, "nested",
3180 ac->nested) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003181 return;
3182 }
3183 }
3184 }
3185
3186 vim_free(pat);
3187}
3188
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01003189#endif