blob: 999ee890cbdc7f43e2fa468218f8bd67304dcafd [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},
Shougo Matsushita4ccaedf2022-10-15 11:48:00 +0100185 {"TextChangedT", EVENT_TEXTCHANGEDT},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100186 {"User", EVENT_USER},
187 {"VimEnter", EVENT_VIMENTER},
188 {"VimLeave", EVENT_VIMLEAVE},
189 {"VimLeavePre", EVENT_VIMLEAVEPRE},
190 {"WinNew", EVENT_WINNEW},
naohiro ono23beefe2021-11-13 12:38:49 +0000191 {"WinClosed", EVENT_WINCLOSED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100192 {"WinEnter", EVENT_WINENTER},
193 {"WinLeave", EVENT_WINLEAVE},
LemonBoy09371822022-04-08 15:18:45 +0100194 {"WinScrolled", EVENT_WINSCROLLED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100195 {"VimResized", EVENT_VIMRESIZED},
196 {"TextYankPost", EVENT_TEXTYANKPOST},
Bram Moolenaar100118c2020-12-11 19:30:34 +0100197 {"VimSuspend", EVENT_VIMSUSPEND},
198 {"VimResume", EVENT_VIMRESUME},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100199 {NULL, (event_T)0}
200};
201
202static AutoPat *first_autopat[NUM_EVENTS] =
203{
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 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
210};
211
212static AutoPat *last_autopat[NUM_EVENTS] =
213{
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 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
220};
221
kylo252ae6f1d82022-02-16 19:24:07 +0000222#define AUGROUP_DEFAULT (-1) // default autocmd group
223#define AUGROUP_ERROR (-2) // erroneous autocmd group
224#define AUGROUP_ALL (-3) // all autocmd groups
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100225
226/*
227 * struct used to keep status while executing autocommands for an event.
228 */
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100229struct AutoPatCmd_S
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100230{
231 AutoPat *curpat; // next AutoPat to examine
232 AutoCmd *nextcmd; // next AutoCmd to execute
233 int group; // group being used
234 char_u *fname; // fname to match with
235 char_u *sfname; // sfname to match with
236 char_u *tail; // tail of fname
237 event_T event; // current event
LemonBoyeca7c602022-04-14 15:39:43 +0100238 sctx_T script_ctx; // script context where it is defined
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100239 int arg_bufnr; // Initially equal to <abuf>, set to zero when
240 // buf is deleted.
LemonBoyeca7c602022-04-14 15:39:43 +0100241 AutoPatCmd_T *next; // chain of active apc-s for auto-invalidation
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100242};
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100243
LemonBoyeca7c602022-04-14 15:39:43 +0100244static AutoPatCmd_T *active_apc_list = NULL; // stack of active autocommands
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100245
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200246// Macro to loop over all the patterns for an autocmd event
247#define FOR_ALL_AUTOCMD_PATTERNS(event, ap) \
248 for ((ap) = first_autopat[(int)(event)]; (ap) != NULL; (ap) = (ap)->next)
249
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100250/*
251 * augroups stores a list of autocmd group names.
252 */
253static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
254#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
Bram Moolenaarc667da52019-11-30 20:52:27 +0100255// use get_deleted_augroup() to get this
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100256static char_u *deleted_augroup = NULL;
257
258/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100259 * The ID of the current group. Group 0 is the default one.
260 */
261static int current_augroup = AUGROUP_DEFAULT;
262
Bram Moolenaarc667da52019-11-30 20:52:27 +0100263static int au_need_clean = FALSE; // need to delete marked patterns
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100264
265static char_u *event_nr2name(event_T event);
266static int au_get_grouparg(char_u **argp);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200267static 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 +0100268static 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 +0100269static void auto_next_pat(AutoPatCmd_T *apc, int stop_at_last);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100270static int au_find_group(char_u *name);
271
272static event_T last_event;
273static int last_group;
Bram Moolenaarc667da52019-11-30 20:52:27 +0100274static int autocmd_blocked = 0; // block all autocmds
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100275
276 static char_u *
277get_deleted_augroup(void)
278{
279 if (deleted_augroup == NULL)
280 deleted_augroup = (char_u *)_("--Deleted--");
281 return deleted_augroup;
282}
283
284/*
285 * Show the autocommands for one AutoPat.
286 */
287 static void
288show_autocmd(AutoPat *ap, event_T event)
289{
290 AutoCmd *ac;
291
292 // Check for "got_int" (here and at various places below), which is set
293 // when "q" has been hit for the "--more--" prompt
294 if (got_int)
295 return;
296 if (ap->pat == NULL) // pattern has been removed
297 return;
298
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000299 // Make sure no info referenced by "ap" is cleared, e.g. when a timer
300 // clears an augroup. Jump to "theend" after this!
301 // "ap->pat" may be cleared anyway.
302 ++autocmd_busy;
303
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100304 msg_putchar('\n');
305 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000306 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100307 if (event != last_event || ap->group != last_group)
308 {
309 if (ap->group != AUGROUP_DEFAULT)
310 {
311 if (AUGROUP_NAME(ap->group) == NULL)
312 msg_puts_attr((char *)get_deleted_augroup(), HL_ATTR(HLF_E));
313 else
314 msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
315 msg_puts(" ");
316 }
317 msg_puts_attr((char *)event_nr2name(event), HL_ATTR(HLF_T));
318 last_event = event;
319 last_group = ap->group;
320 msg_putchar('\n');
321 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000322 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100323 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000324
325 if (ap->pat == NULL)
326 goto theend; // timer might have cleared the pattern or group
327
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100328 msg_col = 4;
329 msg_outtrans(ap->pat);
330
331 for (ac = ap->cmds; ac != NULL; ac = ac->next)
332 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100333 if (ac->cmd == NULL) // skip removed commands
334 continue;
335
336 if (msg_col >= 14)
337 msg_putchar('\n');
338 msg_col = 14;
339 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000340 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100341 msg_outtrans(ac->cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100342#ifdef FEAT_EVAL
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100343 if (p_verbose > 0)
344 last_set_msg(ac->script_ctx);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100345#endif
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100346 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000347 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100348 if (ac->next != NULL)
349 {
350 msg_putchar('\n');
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100351 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000352 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100353 }
354 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000355
356theend:
357 --autocmd_busy;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100358}
359
360/*
361 * Mark an autocommand pattern for deletion.
362 */
363 static void
364au_remove_pat(AutoPat *ap)
365{
366 VIM_CLEAR(ap->pat);
367 ap->buflocal_nr = -1;
368 au_need_clean = TRUE;
369}
370
371/*
372 * Mark all commands for a pattern for deletion.
373 */
374 static void
375au_remove_cmds(AutoPat *ap)
376{
377 AutoCmd *ac;
378
379 for (ac = ap->cmds; ac != NULL; ac = ac->next)
380 VIM_CLEAR(ac->cmd);
381 au_need_clean = TRUE;
382}
383
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200384// Delete one command from an autocmd pattern.
385static void au_del_cmd(AutoCmd *ac)
386{
387 VIM_CLEAR(ac->cmd);
388 au_need_clean = TRUE;
389}
390
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100391/*
392 * Cleanup autocommands and patterns that have been deleted.
393 * This is only done when not executing autocommands.
394 */
395 static void
396au_cleanup(void)
397{
398 AutoPat *ap, **prev_ap;
399 AutoCmd *ac, **prev_ac;
400 event_T event;
401
402 if (autocmd_busy || !au_need_clean)
403 return;
404
405 // loop over all events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100406 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100407 event = (event_T)((int)event + 1))
408 {
409 // loop over all autocommand patterns
410 prev_ap = &(first_autopat[(int)event]);
411 for (ap = *prev_ap; ap != NULL; ap = *prev_ap)
412 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200413 int has_cmd = FALSE;
414
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200415 // loop over all commands for this pattern
416 prev_ac = &(ap->cmds);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100417 for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
418 {
419 // remove the command if the pattern is to be deleted or when
420 // the command has been marked for deletion
421 if (ap->pat == NULL || ac->cmd == NULL)
422 {
423 *prev_ac = ac->next;
424 vim_free(ac->cmd);
425 vim_free(ac);
426 }
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200427 else
428 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200429 has_cmd = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100430 prev_ac = &(ac->next);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200431 }
432 }
433
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200434 if (ap->pat != NULL && !has_cmd)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200435 // Pattern was not marked for deletion, but all of its
436 // commands were. So mark the pattern for deletion.
437 au_remove_pat(ap);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100438
439 // remove the pattern if it has been marked for deletion
440 if (ap->pat == NULL)
441 {
442 if (ap->next == NULL)
443 {
444 if (prev_ap == &(first_autopat[(int)event]))
445 last_autopat[(int)event] = NULL;
446 else
447 // this depends on the "next" field being the first in
448 // the struct
449 last_autopat[(int)event] = (AutoPat *)prev_ap;
450 }
451 *prev_ap = ap->next;
452 vim_regfree(ap->reg_prog);
453 vim_free(ap);
454 }
455 else
456 prev_ap = &(ap->next);
457 }
458 }
459
460 au_need_clean = FALSE;
461}
462
463/*
464 * Called when buffer is freed, to remove/invalidate related buffer-local
465 * autocmds.
466 */
467 void
468aubuflocal_remove(buf_T *buf)
469{
LemonBoyeca7c602022-04-14 15:39:43 +0100470 AutoPat *ap;
471 event_T event;
472 AutoPatCmd_T *apc;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100473
474 // invalidate currently executing autocommands
475 for (apc = active_apc_list; apc; apc = apc->next)
476 if (buf->b_fnum == apc->arg_bufnr)
477 apc->arg_bufnr = 0;
478
479 // invalidate buflocals looping through events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100480 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100481 event = (event_T)((int)event + 1))
482 // loop over all autocommand patterns
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200483 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100484 if (ap->buflocal_nr == buf->b_fnum)
485 {
486 au_remove_pat(ap);
487 if (p_verbose >= 6)
488 {
489 verbose_enter();
490 smsg(_("auto-removing autocommand: %s <buffer=%d>"),
491 event_nr2name(event), buf->b_fnum);
492 verbose_leave();
493 }
494 }
495 au_cleanup();
496}
497
498/*
499 * Add an autocmd group name.
500 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
501 */
502 static int
503au_new_group(char_u *name)
504{
505 int i;
506
507 i = au_find_group(name);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100508 if (i != AUGROUP_ERROR)
509 return i;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100510
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100511 // the group doesn't exist yet, add it. First try using a free entry.
512 for (i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100513 if (AUGROUP_NAME(i) == NULL)
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100514 break;
515 if (i == augroups.ga_len && ga_grow(&augroups, 1) == FAIL)
516 return AUGROUP_ERROR;
517
518 AUGROUP_NAME(i) = vim_strsave(name);
519 if (AUGROUP_NAME(i) == NULL)
520 return AUGROUP_ERROR;
521 if (i == augroups.ga_len)
522 ++augroups.ga_len;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100523
524 return i;
525}
526
527 static void
528au_del_group(char_u *name)
529{
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100530 int i;
531 event_T event;
532 AutoPat *ap;
533 int in_use = FALSE;
534
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100535
536 i = au_find_group(name);
537 if (i == AUGROUP_ERROR) // the group doesn't exist
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100538 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100539 semsg(_(e_no_such_group_str), name);
540 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100541 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100542 if (i == current_augroup)
543 {
544 emsg(_(e_cannot_delete_current_group));
545 return;
546 }
547
548 for (event = (event_T)0; (int)event < NUM_EVENTS;
549 event = (event_T)((int)event + 1))
550 {
551 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
552 if (ap->group == i && ap->pat != NULL)
553 {
554 give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
555 in_use = TRUE;
556 event = NUM_EVENTS;
557 break;
558 }
559 }
560 vim_free(AUGROUP_NAME(i));
561 if (in_use)
562 AUGROUP_NAME(i) = get_deleted_augroup();
563 else
564 AUGROUP_NAME(i) = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100565}
566
567/*
568 * Find the ID of an autocmd group name.
569 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
570 */
571 static int
572au_find_group(char_u *name)
573{
574 int i;
575
576 for (i = 0; i < augroups.ga_len; ++i)
577 if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
578 && STRCMP(AUGROUP_NAME(i), name) == 0)
579 return i;
580 return AUGROUP_ERROR;
581}
582
583/*
584 * Return TRUE if augroup "name" exists.
585 */
586 int
587au_has_group(char_u *name)
588{
589 return au_find_group(name) != AUGROUP_ERROR;
590}
591
592/*
593 * ":augroup {name}".
594 */
595 void
596do_augroup(char_u *arg, int del_group)
597{
598 int i;
599
600 if (del_group)
601 {
602 if (*arg == NUL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000603 emsg(_(e_argument_required));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100604 else
605 au_del_group(arg);
606 }
607 else if (STRICMP(arg, "end") == 0) // ":aug end": back to group 0
608 current_augroup = AUGROUP_DEFAULT;
609 else if (*arg) // ":aug xxx": switch to group xxx
610 {
611 i = au_new_group(arg);
612 if (i != AUGROUP_ERROR)
613 current_augroup = i;
614 }
615 else // ":aug": list the group names
616 {
617 msg_start();
618 for (i = 0; i < augroups.ga_len; ++i)
619 {
620 if (AUGROUP_NAME(i) != NULL)
621 {
622 msg_puts((char *)AUGROUP_NAME(i));
623 msg_puts(" ");
624 }
625 }
626 msg_clr_eos();
627 msg_end();
628 }
629}
630
631#if defined(EXITFREE) || defined(PROTO)
632 void
633free_all_autocmds(void)
634{
635 int i;
636 char_u *s;
637
638 for (current_augroup = -1; current_augroup < augroups.ga_len;
639 ++current_augroup)
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200640 do_autocmd(NULL, (char_u *)"", TRUE);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100641
642 for (i = 0; i < augroups.ga_len; ++i)
643 {
644 s = ((char_u **)(augroups.ga_data))[i];
645 if (s != get_deleted_augroup())
646 vim_free(s);
647 }
648 ga_clear(&augroups);
649}
650#endif
651
652/*
653 * Return the event number for event name "start".
654 * Return NUM_EVENTS if the event name was not found.
655 * Return a pointer to the next event name in "end".
656 */
657 static event_T
658event_name2nr(char_u *start, char_u **end)
659{
660 char_u *p;
661 int i;
662 int len;
663
664 // the event name ends with end of line, '|', a blank or a comma
665 for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
666 ;
667 for (i = 0; event_names[i].name != NULL; ++i)
668 {
669 len = (int)STRLEN(event_names[i].name);
670 if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0)
671 break;
672 }
673 if (*p == ',')
674 ++p;
675 *end = p;
676 if (event_names[i].name == NULL)
677 return NUM_EVENTS;
678 return event_names[i].event;
679}
680
681/*
682 * Return the name for event "event".
683 */
684 static char_u *
685event_nr2name(event_T event)
686{
687 int i;
688
689 for (i = 0; event_names[i].name != NULL; ++i)
690 if (event_names[i].event == event)
691 return (char_u *)event_names[i].name;
692 return (char_u *)"Unknown";
693}
694
695/*
696 * Scan over the events. "*" stands for all events.
697 */
698 static char_u *
699find_end_event(
700 char_u *arg,
701 int have_group) // TRUE when group name was found
702{
703 char_u *pat;
704 char_u *p;
705
706 if (*arg == '*')
707 {
708 if (arg[1] && !VIM_ISWHITE(arg[1]))
709 {
Bram Moolenaar6d057012021-12-31 18:49:43 +0000710 semsg(_(e_illegal_character_after_star_str), arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100711 return NULL;
712 }
713 pat = arg + 1;
714 }
715 else
716 {
717 for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p)
718 {
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100719 if ((int)event_name2nr(pat, &p) >= NUM_EVENTS)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100720 {
721 if (have_group)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000722 semsg(_(e_no_such_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100723 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000724 semsg(_(e_no_such_group_or_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100725 return NULL;
726 }
727 }
728 }
729 return pat;
730}
731
732/*
733 * Return TRUE if "event" is included in 'eventignore'.
734 */
735 static int
736event_ignored(event_T event)
737{
738 char_u *p = p_ei;
739
740 while (*p != NUL)
741 {
742 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
743 return TRUE;
744 if (event_name2nr(p, &p) == event)
745 return TRUE;
746 }
747
748 return FALSE;
749}
750
751/*
752 * Return OK when the contents of p_ei is valid, FAIL otherwise.
753 */
754 int
755check_ei(void)
756{
757 char_u *p = p_ei;
758
759 while (*p)
760 {
761 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
762 {
763 p += 3;
764 if (*p == ',')
765 ++p;
766 }
767 else if (event_name2nr(p, &p) == NUM_EVENTS)
768 return FAIL;
769 }
770
771 return OK;
772}
773
774# if defined(FEAT_SYN_HL) || defined(PROTO)
775
776/*
777 * Add "what" to 'eventignore' to skip loading syntax highlighting for every
778 * buffer loaded into the window. "what" must start with a comma.
779 * Returns the old value of 'eventignore' in allocated memory.
780 */
781 char_u *
782au_event_disable(char *what)
783{
784 char_u *new_ei;
785 char_u *save_ei;
786
787 save_ei = vim_strsave(p_ei);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100788 if (save_ei == NULL)
789 return NULL;
790
791 new_ei = vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what));
792 if (new_ei == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100793 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100794 vim_free(save_ei);
795 return NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100796 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100797
798 if (*what == ',' && *p_ei == NUL)
799 STRCPY(new_ei, what + 1);
800 else
801 STRCAT(new_ei, what);
802 set_string_option_direct((char_u *)"ei", -1, new_ei,
803 OPT_FREE, SID_NONE);
804 vim_free(new_ei);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100805 return save_ei;
806}
807
808 void
809au_event_restore(char_u *old_ei)
810{
811 if (old_ei != NULL)
812 {
813 set_string_option_direct((char_u *)"ei", -1, old_ei,
814 OPT_FREE, SID_NONE);
815 vim_free(old_ei);
816 }
817}
Bram Moolenaarc667da52019-11-30 20:52:27 +0100818# endif // FEAT_SYN_HL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100819
820/*
821 * do_autocmd() -- implements the :autocmd command. Can be used in the
822 * following ways:
823 *
824 * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
825 * will be automatically executed for <event>
826 * when editing a file matching <pat>, in
827 * the current group.
828 * :autocmd <event> <pat> Show the autocommands associated with
829 * <event> and <pat>.
830 * :autocmd <event> Show the autocommands associated with
831 * <event>.
832 * :autocmd Show all autocommands.
833 * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
834 * <event> and <pat>, and add the command
835 * <cmd>, for the current group.
836 * :autocmd! <event> <pat> Remove all autocommands associated with
837 * <event> and <pat> for the current group.
838 * :autocmd! <event> Remove all autocommands associated with
839 * <event> for the current group.
840 * :autocmd! Remove ALL autocommands for the current
841 * group.
842 *
843 * Multiple events and patterns may be given separated by commas. Here are
844 * some examples:
845 * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
846 * :autocmd bufleave * set tw=79 nosmartindent ic infercase
847 *
848 * :autocmd * *.c show all autocommands for *.c files.
849 *
850 * Mostly a {group} argument can optionally appear before <event>.
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200851 * "eap" can be NULL.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100852 */
853 void
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200854do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100855{
856 char_u *arg = arg_in;
857 char_u *pat;
858 char_u *envpat = NULL;
859 char_u *cmd;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200860 int cmd_need_free = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100861 event_T event;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200862 char_u *tofree = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100863 int nested = FALSE;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200864 int once = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100865 int group;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200866 int i;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200867 int flags = 0;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100868
869 if (*arg == '|')
870 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000871 eap->nextcmd = arg + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100872 arg = (char_u *)"";
873 group = AUGROUP_ALL; // no argument, use all groups
874 }
875 else
876 {
877 /*
878 * Check for a legal group name. If not, use AUGROUP_ALL.
879 */
880 group = au_get_grouparg(&arg);
881 if (arg == NULL) // out of memory
882 return;
883 }
884
885 /*
886 * Scan over the events.
887 * If we find an illegal name, return here, don't do anything.
888 */
889 pat = find_end_event(arg, group != AUGROUP_ALL);
890 if (pat == NULL)
891 return;
892
893 pat = skipwhite(pat);
894 if (*pat == '|')
895 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000896 eap->nextcmd = pat + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100897 pat = (char_u *)"";
898 cmd = (char_u *)"";
899 }
900 else
901 {
902 /*
903 * Scan over the pattern. Put a NUL at the end.
904 */
905 cmd = pat;
906 while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
907 cmd++;
908 if (*cmd)
909 *cmd++ = NUL;
910
911 // Expand environment variables in the pattern. Set 'shellslash', we
912 // want forward slashes here.
913 if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
914 {
915#ifdef BACKSLASH_IN_FILENAME
916 int p_ssl_save = p_ssl;
917
918 p_ssl = TRUE;
919#endif
920 envpat = expand_env_save(pat);
921#ifdef BACKSLASH_IN_FILENAME
922 p_ssl = p_ssl_save;
923#endif
924 if (envpat != NULL)
925 pat = envpat;
926 }
927
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100928 cmd = skipwhite(cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200929 for (i = 0; i < 2; i++)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100930 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100931 if (*cmd == NUL)
932 continue;
933
934 // Check for "++once" flag.
935 if (STRNCMP(cmd, "++once", 6) == 0 && VIM_ISWHITE(cmd[6]))
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200936 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100937 if (once)
938 semsg(_(e_duplicate_argument_str), "++once");
939 once = TRUE;
940 cmd = skipwhite(cmd + 6);
941 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200942
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100943 // Check for "++nested" flag.
944 if ((STRNCMP(cmd, "++nested", 8) == 0 && VIM_ISWHITE(cmd[8])))
945 {
946 if (nested)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200947 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100948 semsg(_(e_duplicate_argument_str), "++nested");
949 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200950 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100951 nested = TRUE;
952 cmd = skipwhite(cmd + 8);
953 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200954
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100955 // Check for the old "nested" flag in legacy script.
956 if (STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
957 {
958 if (in_vim9script())
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200959 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100960 // If there ever is a :nested command this error should
961 // be removed and "nested" accepted as the start of the
962 // command.
963 emsg(_(e_invalid_command_nested_did_you_mean_plusplus_nested));
964 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200965 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100966 if (nested)
967 {
968 semsg(_(e_duplicate_argument_str), "nested");
969 return;
970 }
971 nested = TRUE;
972 cmd = skipwhite(cmd + 6);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200973 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100974 }
975
976 /*
977 * Find the start of the commands.
978 * Expand <sfile> in it.
979 */
980 if (*cmd != NUL)
981 {
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200982 if (eap != NULL)
983 // Read a {} block if it follows.
984 cmd = may_get_cmd_block(eap, cmd, &tofree, &flags);
985
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100986 cmd = expand_sfile(cmd);
987 if (cmd == NULL) // some error
988 return;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200989 cmd_need_free = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100990 }
991 }
992
993 /*
994 * Print header when showing autocommands.
995 */
996 if (!forceit && *cmd == NUL)
997 // Highlight title
998 msg_puts_title(_("\n--- Autocommands ---"));
999
1000 /*
1001 * Loop over the events.
1002 */
1003 last_event = (event_T)-1; // for listing the event name
1004 last_group = AUGROUP_ERROR; // for listing the group name
1005 if (*arg == '*' || *arg == NUL || *arg == '|')
1006 {
Bram Moolenaarb6db1462021-12-24 19:24:47 +00001007 if (*cmd != NUL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001008 emsg(_(e_cannot_define_autocommands_for_all_events));
1009 else
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +01001010 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001011 event = (event_T)((int)event + 1))
1012 if (do_autocmd_event(event, pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001013 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001014 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001015 }
1016 else
1017 {
1018 while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
1019 if (do_autocmd_event(event_name2nr(arg, &arg), pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001020 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001021 break;
1022 }
1023
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001024 if (cmd_need_free)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001025 vim_free(cmd);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001026 vim_free(tofree);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001027 vim_free(envpat);
1028}
1029
1030/*
1031 * Find the group ID in a ":autocmd" or ":doautocmd" argument.
1032 * The "argp" argument is advanced to the following argument.
1033 *
1034 * Returns the group ID, AUGROUP_ERROR for error (out of memory).
1035 */
1036 static int
1037au_get_grouparg(char_u **argp)
1038{
1039 char_u *group_name;
1040 char_u *p;
1041 char_u *arg = *argp;
1042 int group = AUGROUP_ALL;
1043
1044 for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p)
1045 ;
1046 if (p > arg)
1047 {
Bram Moolenaardf44a272020-06-07 20:49:05 +02001048 group_name = vim_strnsave(arg, p - arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001049 if (group_name == NULL) // out of memory
1050 return AUGROUP_ERROR;
1051 group = au_find_group(group_name);
1052 if (group == AUGROUP_ERROR)
1053 group = AUGROUP_ALL; // no match, use all groups
1054 else
1055 *argp = skipwhite(p); // match, skip over group name
1056 vim_free(group_name);
1057 }
1058 return group;
1059}
1060
1061/*
1062 * do_autocmd() for one event.
1063 * If *pat == NUL do for all patterns.
1064 * If *cmd == NUL show entries.
1065 * If forceit == TRUE delete entries.
1066 * If group is not AUGROUP_ALL, only use this group.
1067 */
1068 static int
1069do_autocmd_event(
1070 event_T event,
1071 char_u *pat,
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001072 int once,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001073 int nested,
1074 char_u *cmd,
1075 int forceit,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001076 int group,
1077 int flags)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001078{
1079 AutoPat *ap;
1080 AutoPat **prev_ap;
1081 AutoCmd *ac;
1082 AutoCmd **prev_ac;
1083 int brace_level;
1084 char_u *endpat;
1085 int findgroup;
1086 int allgroups;
1087 int patlen;
1088 int is_buflocal;
1089 int buflocal_nr;
Bram Moolenaarc667da52019-11-30 20:52:27 +01001090 char_u buflocal_pat[25]; // for "<buffer=X>"
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001091
1092 if (group == AUGROUP_ALL)
1093 findgroup = current_augroup;
1094 else
1095 findgroup = group;
1096 allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
1097
1098 /*
1099 * Show or delete all patterns for an event.
1100 */
1101 if (*pat == NUL)
1102 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001103 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001104 {
1105 if (forceit) // delete the AutoPat, if it's in the current group
1106 {
1107 if (ap->group == findgroup)
1108 au_remove_pat(ap);
1109 }
1110 else if (group == AUGROUP_ALL || ap->group == group)
1111 show_autocmd(ap, event);
1112 }
1113 }
1114
1115 /*
1116 * Loop through all the specified patterns.
1117 */
1118 for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
1119 {
1120 /*
1121 * Find end of the pattern.
1122 * Watch out for a comma in braces, like "*.\{obj,o\}".
1123 */
1124 brace_level = 0;
1125 for (endpat = pat; *endpat && (*endpat != ',' || brace_level
1126 || (endpat > pat && endpat[-1] == '\\')); ++endpat)
1127 {
1128 if (*endpat == '{')
1129 brace_level++;
1130 else if (*endpat == '}')
1131 brace_level--;
1132 }
1133 if (pat == endpat) // ignore single comma
1134 continue;
1135 patlen = (int)(endpat - pat);
1136
1137 /*
1138 * detect special <buflocal[=X]> buffer-local patterns
1139 */
1140 is_buflocal = FALSE;
1141 buflocal_nr = 0;
1142
1143 if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
1144 && pat[patlen - 1] == '>')
1145 {
1146 // "<buffer...>": Error will be printed only for addition.
1147 // printing and removing will proceed silently.
1148 is_buflocal = TRUE;
1149 if (patlen == 8)
1150 // "<buffer>"
1151 buflocal_nr = curbuf->b_fnum;
1152 else if (patlen > 9 && pat[7] == '=')
1153 {
1154 if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0)
1155 // "<buffer=abuf>"
1156 buflocal_nr = autocmd_bufnr;
1157 else if (skipdigits(pat + 8) == pat + patlen - 1)
1158 // "<buffer=123>"
1159 buflocal_nr = atoi((char *)pat + 8);
1160 }
1161 }
1162
1163 if (is_buflocal)
1164 {
1165 // normalize pat into standard "<buffer>#N" form
1166 sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
1167 pat = buflocal_pat; // can modify pat and patlen
1168 patlen = (int)STRLEN(buflocal_pat); // but not endpat
1169 }
1170
1171 /*
1172 * Find AutoPat entries with this pattern. When adding a command it
1173 * always goes at or after the last one, so start at the end.
1174 */
1175 if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL)
1176 prev_ap = &last_autopat[(int)event];
1177 else
1178 prev_ap = &first_autopat[(int)event];
1179 while ((ap = *prev_ap) != NULL)
1180 {
1181 if (ap->pat != NULL)
1182 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001183 /*
1184 * Accept a pattern when:
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001185 * - a group was specified and it's that group, or a group was
1186 * not specified and it's the current group, or a group was
1187 * not specified and we are listing
1188 * - the length of the pattern matches
1189 * - the pattern matches.
1190 * For <buffer[=X]>, this condition works because we normalize
1191 * all buffer-local patterns.
1192 */
1193 if ((allgroups || ap->group == findgroup)
1194 && ap->patlen == patlen
1195 && STRNCMP(pat, ap->pat, patlen) == 0)
1196 {
1197 /*
1198 * Remove existing autocommands.
1199 * If adding any new autocmd's for this AutoPat, don't
1200 * delete the pattern from the autopat list, append to
1201 * this list.
1202 */
1203 if (forceit)
1204 {
1205 if (*cmd != NUL && ap->next == NULL)
1206 {
1207 au_remove_cmds(ap);
1208 break;
1209 }
1210 au_remove_pat(ap);
1211 }
1212
1213 /*
1214 * Show autocmd's for this autopat, or buflocals <buffer=X>
1215 */
1216 else if (*cmd == NUL)
1217 show_autocmd(ap, event);
1218
1219 /*
1220 * Add autocmd to this autopat, if it's the last one.
1221 */
1222 else if (ap->next == NULL)
1223 break;
1224 }
1225 }
1226 prev_ap = &ap->next;
1227 }
1228
1229 /*
1230 * Add a new command.
1231 */
1232 if (*cmd != NUL)
1233 {
1234 /*
1235 * If the pattern we want to add a command to does appear at the
1236 * end of the list (or not is not in the list at all), add the
1237 * pattern at the end of the list.
1238 */
1239 if (ap == NULL)
1240 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001241 // refuse to add buffer-local ap if buffer number is invalid
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001242 if (is_buflocal && (buflocal_nr == 0
1243 || buflist_findnr(buflocal_nr) == NULL))
1244 {
Bram Moolenaarf1474d82021-12-31 19:59:55 +00001245 semsg(_(e_buffer_nr_invalid_buffer_number), buflocal_nr);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001246 return FAIL;
1247 }
1248
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001249 ap = ALLOC_ONE(AutoPat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001250 if (ap == NULL)
1251 return FAIL;
1252 ap->pat = vim_strnsave(pat, patlen);
1253 ap->patlen = patlen;
1254 if (ap->pat == NULL)
1255 {
1256 vim_free(ap);
1257 return FAIL;
1258 }
1259
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001260#ifdef FEAT_EVAL
1261 // need to initialize last_mode for the first ModeChanged
1262 // autocmd
1263 if (event == EVENT_MODECHANGED && !has_modechanged())
LemonBoy2bf52dd2022-04-09 18:17:34 +01001264 get_mode(last_mode);
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001265#endif
LemonBoy09371822022-04-08 15:18:45 +01001266 // Initialize the fields checked by the WinScrolled trigger to
Bram Moolenaar29967732022-11-20 12:11:45 +00001267 // prevent it from firing right after the first autocmd is
1268 // defined.
LemonBoy09371822022-04-08 15:18:45 +01001269 if (event == EVENT_WINSCROLLED && !has_winscrolled())
1270 {
Bram Moolenaar29967732022-11-20 12:11:45 +00001271 tabpage_T *save_curtab = curtab;
1272 tabpage_T *tp;
1273 FOR_ALL_TABPAGES(tp)
1274 {
1275 unuse_tabpage(curtab);
1276 use_tabpage(tp);
1277 snapshot_windows_scroll_size();
1278 }
1279 unuse_tabpage(curtab);
1280 use_tabpage(save_curtab);
LemonBoy09371822022-04-08 15:18:45 +01001281 }
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001282
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001283 if (is_buflocal)
1284 {
1285 ap->buflocal_nr = buflocal_nr;
1286 ap->reg_prog = NULL;
1287 }
1288 else
1289 {
1290 char_u *reg_pat;
1291
1292 ap->buflocal_nr = 0;
1293 reg_pat = file_pat_to_reg_pat(pat, endpat,
1294 &ap->allow_dirs, TRUE);
1295 if (reg_pat != NULL)
1296 ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
1297 vim_free(reg_pat);
1298 if (reg_pat == NULL || ap->reg_prog == NULL)
1299 {
1300 vim_free(ap->pat);
1301 vim_free(ap);
1302 return FAIL;
1303 }
1304 }
1305 ap->cmds = NULL;
1306 *prev_ap = ap;
1307 last_autopat[(int)event] = ap;
1308 ap->next = NULL;
1309 if (group == AUGROUP_ALL)
1310 ap->group = current_augroup;
1311 else
1312 ap->group = group;
1313 }
1314
1315 /*
1316 * Add the autocmd at the end of the AutoCmd list.
1317 */
1318 prev_ac = &(ap->cmds);
1319 while ((ac = *prev_ac) != NULL)
1320 prev_ac = &ac->next;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001321 ac = ALLOC_ONE(AutoCmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001322 if (ac == NULL)
1323 return FAIL;
1324 ac->cmd = vim_strsave(cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001325 ac->script_ctx = current_sctx;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001326 if (flags & UC_VIM9)
1327 ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001328#ifdef FEAT_EVAL
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001329 ac->script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001330#endif
1331 if (ac->cmd == NULL)
1332 {
1333 vim_free(ac);
1334 return FAIL;
1335 }
1336 ac->next = NULL;
1337 *prev_ac = ac;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001338 ac->once = once;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001339 ac->nested = nested;
1340 }
1341 }
1342
1343 au_cleanup(); // may really delete removed patterns/commands now
1344 return OK;
1345}
1346
1347/*
1348 * Implementation of ":doautocmd [group] event [fname]".
1349 * Return OK for success, FAIL for failure;
1350 */
1351 int
1352do_doautocmd(
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001353 char_u *arg_start,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001354 int do_msg, // give message for no matching autocmds?
1355 int *did_something)
1356{
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001357 char_u *arg = arg_start;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001358 char_u *fname;
1359 int nothing_done = TRUE;
1360 int group;
1361
1362 if (did_something != NULL)
1363 *did_something = FALSE;
1364
1365 /*
1366 * Check for a legal group name. If not, use AUGROUP_ALL.
1367 */
1368 group = au_get_grouparg(&arg);
1369 if (arg == NULL) // out of memory
1370 return FAIL;
1371
1372 if (*arg == '*')
1373 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001374 emsg(_(e_cant_execute_autocommands_for_all_events));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001375 return FAIL;
1376 }
1377
1378 /*
1379 * Scan over the events.
1380 * If we find an illegal name, return here, don't do anything.
1381 */
1382 fname = find_end_event(arg, group != AUGROUP_ALL);
1383 if (fname == NULL)
1384 return FAIL;
1385
1386 fname = skipwhite(fname);
1387
1388 /*
1389 * Loop over the events.
1390 */
1391 while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
1392 if (apply_autocmds_group(event_name2nr(arg, &arg),
1393 fname, NULL, TRUE, group, curbuf, NULL))
1394 nothing_done = FALSE;
1395
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001396 if (nothing_done && do_msg
1397#ifdef FEAT_EVAL
1398 && !aborting()
1399#endif
1400 )
1401 smsg(_("No matching autocommands: %s"), arg_start);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001402 if (did_something != NULL)
1403 *did_something = !nothing_done;
1404
1405#ifdef FEAT_EVAL
1406 return aborting() ? FAIL : OK;
1407#else
1408 return OK;
1409#endif
1410}
1411
1412/*
1413 * ":doautoall": execute autocommands for each loaded buffer.
1414 */
1415 void
1416ex_doautoall(exarg_T *eap)
1417{
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001418 int retval = OK;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001419 aco_save_T aco;
1420 buf_T *buf;
1421 bufref_T bufref;
1422 char_u *arg = eap->arg;
1423 int call_do_modelines = check_nomodeline(&arg);
1424 int did_aucmd;
1425
1426 /*
1427 * This is a bit tricky: For some commands curwin->w_buffer needs to be
1428 * equal to curbuf, but for some buffers there may not be a window.
1429 * So we change the buffer for the current window for a moment. This
1430 * gives problems when the autocommands make changes to the list of
1431 * buffers or windows...
1432 */
1433 FOR_ALL_BUFFERS(buf)
1434 {
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001435 // Only do loaded buffers and skip the current buffer, it's done last.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001436 if (buf->b_ml.ml_mfp == NULL || buf == curbuf)
1437 continue;
1438
1439 // find a window for this buffer and save some values
1440 aucmd_prepbuf(&aco, buf);
1441 set_bufref(&bufref, buf);
1442
1443 // execute the autocommands for this buffer
1444 retval = do_doautocmd(arg, FALSE, &did_aucmd);
1445
1446 if (call_do_modelines && did_aucmd)
1447 // Execute the modeline settings, but don't set window-local
1448 // options if we are using the current window for another
1449 // buffer.
1450 do_modelines(curwin == aucmd_win ? OPT_NOWIN : 0);
1451
1452 // restore the current window
1453 aucmd_restbuf(&aco);
1454
1455 // stop if there is some error or buffer was deleted
1456 if (retval == FAIL || !bufref_valid(&bufref))
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001457 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001458 retval = FAIL;
1459 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001460 }
1461 }
1462
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001463 // Execute autocommands for the current buffer last.
1464 if (retval == OK)
1465 {
1466 do_doautocmd(arg, FALSE, &did_aucmd);
1467 if (call_do_modelines && did_aucmd)
1468 do_modelines(0);
1469 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001470}
1471
1472/*
1473 * Check *argp for <nomodeline>. When it is present return FALSE, otherwise
1474 * return TRUE and advance *argp to after it.
1475 * Thus return TRUE when do_modelines() should be called.
1476 */
1477 int
1478check_nomodeline(char_u **argp)
1479{
1480 if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
1481 {
1482 *argp = skipwhite(*argp + 12);
1483 return FALSE;
1484 }
1485 return TRUE;
1486}
1487
1488/*
1489 * Prepare for executing autocommands for (hidden) buffer "buf".
1490 * Search for a visible window containing the current buffer. If there isn't
1491 * one then use "aucmd_win".
1492 * Set "curbuf" and "curwin" to match "buf".
1493 */
1494 void
1495aucmd_prepbuf(
1496 aco_save_T *aco, // structure to save values in
1497 buf_T *buf) // new curbuf
1498{
1499 win_T *win;
1500 int save_ea;
1501#ifdef FEAT_AUTOCHDIR
1502 int save_acd;
1503#endif
1504
1505 // Find a window that is for the new buffer
1506 if (buf == curbuf) // be quick when buf is curbuf
1507 win = curwin;
1508 else
1509 FOR_ALL_WINDOWS(win)
1510 if (win->w_buffer == buf)
1511 break;
1512
1513 // Allocate "aucmd_win" when needed. If this fails (out of memory) fall
1514 // back to using the current window.
1515 if (win == NULL && aucmd_win == NULL)
1516 {
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001517 aucmd_win = win_alloc_popup_win();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001518 if (aucmd_win == NULL)
1519 win = curwin;
1520 }
1521 if (win == NULL && aucmd_win_used)
1522 // Strange recursive autocommand, fall back to using the current
1523 // window. Expect a few side effects...
1524 win = curwin;
1525
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001526 aco->save_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001527 aco->save_curbuf = curbuf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001528 aco->save_prevwin_id = prevwin == NULL ? 0 : prevwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001529 if (win != NULL)
1530 {
1531 // There is a window for "buf" in the current tab page, make it the
1532 // curwin. This is preferred, it has the least side effects (esp. if
1533 // "buf" is curbuf).
1534 aco->use_aucmd_win = FALSE;
1535 curwin = win;
1536 }
1537 else
1538 {
1539 // There is no window for "buf", use "aucmd_win". To minimize the side
1540 // effects, insert it in the current tab page.
1541 // Anything related to a window (e.g., setting folds) may have
1542 // unexpected results.
1543 aco->use_aucmd_win = TRUE;
1544 aucmd_win_used = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001545
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001546 win_init_popup_win(aucmd_win, buf);
1547
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001548 aco->globaldir = globaldir;
1549 globaldir = NULL;
1550
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001551 // Split the current window, put the aucmd_win in the upper half.
1552 // We don't want the BufEnter or WinEnter autocommands.
1553 block_autocmds();
1554 make_snapshot(SNAP_AUCMD_IDX);
1555 save_ea = p_ea;
1556 p_ea = FALSE;
1557
1558#ifdef FEAT_AUTOCHDIR
1559 // Prevent chdir() call in win_enter_ext(), through do_autochdir().
1560 save_acd = p_acd;
1561 p_acd = FALSE;
1562#endif
1563
Bram Moolenaardff97e62022-01-24 20:00:55 +00001564 // no redrawing and don't set the window title
1565 ++RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001566 (void)win_split_ins(0, WSP_TOP, aucmd_win, 0);
Bram Moolenaardff97e62022-01-24 20:00:55 +00001567 --RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001568 (void)win_comp_pos(); // recompute window positions
1569 p_ea = save_ea;
1570#ifdef FEAT_AUTOCHDIR
1571 p_acd = save_acd;
1572#endif
1573 unblock_autocmds();
1574 curwin = aucmd_win;
1575 }
1576 curbuf = buf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001577 aco->new_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001578 set_bufref(&aco->new_curbuf, curbuf);
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001579
1580 // disable the Visual area, the position may be invalid in another buffer
1581 aco->save_VIsual_active = VIsual_active;
1582 VIsual_active = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001583}
1584
1585/*
1586 * Cleanup after executing autocommands for a (hidden) buffer.
1587 * Restore the window as it was (if possible).
1588 */
1589 void
1590aucmd_restbuf(
1591 aco_save_T *aco) // structure holding saved values
1592{
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001593 int dummy;
1594 win_T *save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001595
1596 if (aco->use_aucmd_win)
1597 {
1598 --curbuf->b_nwindows;
1599 // Find "aucmd_win", it can't be closed, but it may be in another tab
1600 // page. Do not trigger autocommands here.
1601 block_autocmds();
1602 if (curwin != aucmd_win)
1603 {
1604 tabpage_T *tp;
1605 win_T *wp;
1606
1607 FOR_ALL_TAB_WINDOWS(tp, wp)
1608 {
1609 if (wp == aucmd_win)
1610 {
1611 if (tp != curtab)
1612 goto_tabpage_tp(tp, TRUE, TRUE);
1613 win_goto(aucmd_win);
1614 goto win_found;
1615 }
1616 }
1617 }
1618win_found:
1619
1620 // Remove the window and frame from the tree of frames.
1621 (void)winframe_remove(curwin, &dummy, NULL);
1622 win_remove(curwin, NULL);
1623 aucmd_win_used = FALSE;
1624 last_status(FALSE); // may need to remove last status line
1625
1626 if (!valid_tabpage_win(curtab))
1627 // no valid window in current tabpage
1628 close_tabpage(curtab);
1629
1630 restore_snapshot(SNAP_AUCMD_IDX, FALSE);
1631 (void)win_comp_pos(); // recompute window positions
1632 unblock_autocmds();
1633
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001634 save_curwin = win_find_by_id(aco->save_curwin_id);
1635 if (save_curwin != NULL)
1636 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001637 else
1638 // Hmm, original window disappeared. Just use the first one.
1639 curwin = firstwin;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001640 curbuf = curwin->w_buffer;
1641#ifdef FEAT_JOB_CHANNEL
1642 // May need to restore insert mode for a prompt buffer.
1643 entering_window(curwin);
1644#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001645 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001646#ifdef FEAT_EVAL
1647 vars_clear(&aucmd_win->w_vars->dv_hashtab); // free all w: variables
1648 hash_init(&aucmd_win->w_vars->dv_hashtab); // re-use the hashtab
1649#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001650 vim_free(globaldir);
1651 globaldir = aco->globaldir;
1652
1653 // the buffer contents may have changed
1654 check_cursor();
1655 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1656 {
1657 curwin->w_topline = curbuf->b_ml.ml_line_count;
1658#ifdef FEAT_DIFF
1659 curwin->w_topfill = 0;
1660#endif
1661 }
1662#if defined(FEAT_GUI)
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001663 if (gui.in_use)
1664 {
1665 // Hide the scrollbars from the aucmd_win and update.
1666 gui_mch_enable_scrollbar(
1667 &aucmd_win->w_scrollbars[SBAR_LEFT], FALSE);
1668 gui_mch_enable_scrollbar(
1669 &aucmd_win->w_scrollbars[SBAR_RIGHT], FALSE);
1670 gui_may_update_scrollbars();
1671 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001672#endif
1673 }
1674 else
1675 {
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001676 // Restore curwin. Use the window ID, a window may have been closed
1677 // and the memory re-used for another one.
1678 save_curwin = win_find_by_id(aco->save_curwin_id);
1679 if (save_curwin != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001680 {
1681 // Restore the buffer which was previously edited by curwin, if
1682 // it was changed, we are still the same window and the buffer is
1683 // valid.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001684 if (curwin->w_id == aco->new_curwin_id
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001685 && curbuf != aco->new_curbuf.br_buf
1686 && bufref_valid(&aco->new_curbuf)
1687 && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
1688 {
1689# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
1690 if (curwin->w_s == &curbuf->b_s)
1691 curwin->w_s = &aco->new_curbuf.br_buf->b_s;
1692# endif
1693 --curbuf->b_nwindows;
1694 curbuf = aco->new_curbuf.br_buf;
1695 curwin->w_buffer = curbuf;
1696 ++curbuf->b_nwindows;
1697 }
1698
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001699 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001700 curbuf = curwin->w_buffer;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001701 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001702 // In case the autocommand moves the cursor to a position that
1703 // does not exist in curbuf.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001704 check_cursor();
1705 }
1706 }
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001707
1708 check_cursor(); // just in case lines got deleted
1709 VIsual_active = aco->save_VIsual_active;
1710 if (VIsual_active)
1711 check_pos(curbuf, &VIsual);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001712}
1713
1714static int autocmd_nested = FALSE;
1715
1716/*
1717 * Execute autocommands for "event" and file name "fname".
1718 * Return TRUE if some commands were executed.
1719 */
1720 int
1721apply_autocmds(
1722 event_T event,
1723 char_u *fname, // NULL or empty means use actual file name
1724 char_u *fname_io, // fname to use for <afile> on cmdline
1725 int force, // when TRUE, ignore autocmd_busy
1726 buf_T *buf) // buffer for <abuf>
1727{
1728 return apply_autocmds_group(event, fname, fname_io, force,
1729 AUGROUP_ALL, buf, NULL);
1730}
1731
1732/*
1733 * Like apply_autocmds(), but with extra "eap" argument. This takes care of
1734 * setting v:filearg.
1735 */
1736 int
1737apply_autocmds_exarg(
1738 event_T event,
1739 char_u *fname,
1740 char_u *fname_io,
1741 int force,
1742 buf_T *buf,
1743 exarg_T *eap)
1744{
1745 return apply_autocmds_group(event, fname, fname_io, force,
1746 AUGROUP_ALL, buf, eap);
1747}
1748
1749/*
1750 * Like apply_autocmds(), but handles the caller's retval. If the script
1751 * processing is being aborted or if retval is FAIL when inside a try
1752 * conditional, no autocommands are executed. If otherwise the autocommands
1753 * cause the script to be aborted, retval is set to FAIL.
1754 */
1755 int
1756apply_autocmds_retval(
1757 event_T event,
1758 char_u *fname, // NULL or empty means use actual file name
1759 char_u *fname_io, // fname to use for <afile> on cmdline
1760 int force, // when TRUE, ignore autocmd_busy
1761 buf_T *buf, // buffer for <abuf>
1762 int *retval) // pointer to caller's retval
1763{
1764 int did_cmd;
1765
1766#ifdef FEAT_EVAL
1767 if (should_abort(*retval))
1768 return FALSE;
1769#endif
1770
1771 did_cmd = apply_autocmds_group(event, fname, fname_io, force,
1772 AUGROUP_ALL, buf, NULL);
1773 if (did_cmd
1774#ifdef FEAT_EVAL
1775 && aborting()
1776#endif
1777 )
1778 *retval = FAIL;
1779 return did_cmd;
1780}
1781
1782/*
1783 * Return TRUE when there is a CursorHold autocommand defined.
1784 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001785 static int
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001786has_cursorhold(void)
1787{
Bram Moolenaar24959102022-05-07 20:01:16 +01001788 return (first_autopat[(int)(get_real_state() == MODE_NORMAL_BUSY
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001789 ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
1790}
1791
1792/*
1793 * Return TRUE if the CursorHold event can be triggered.
1794 */
1795 int
1796trigger_cursorhold(void)
1797{
1798 int state;
1799
1800 if (!did_cursorhold
1801 && has_cursorhold()
1802 && reg_recording == 0
1803 && typebuf.tb_len == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001804 && !ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001805 {
1806 state = get_real_state();
Bram Moolenaar24959102022-05-07 20:01:16 +01001807 if (state == MODE_NORMAL_BUSY || (state & MODE_INSERT) != 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001808 return TRUE;
1809 }
1810 return FALSE;
1811}
1812
1813/*
LemonBoy09371822022-04-08 15:18:45 +01001814 * Return TRUE when there is a WinScrolled autocommand defined.
1815 */
1816 int
1817has_winscrolled(void)
1818{
1819 return (first_autopat[(int)EVENT_WINSCROLLED] != NULL);
1820}
1821
1822/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001823 * Return TRUE when there is a CursorMoved autocommand defined.
1824 */
1825 int
1826has_cursormoved(void)
1827{
1828 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1829}
1830
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001831/*
1832 * Return TRUE when there is a CursorMovedI autocommand defined.
1833 */
1834 int
1835has_cursormovedI(void)
1836{
1837 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1838}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001839
1840/*
1841 * Return TRUE when there is a TextChanged autocommand defined.
1842 */
1843 int
1844has_textchanged(void)
1845{
1846 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1847}
1848
1849/*
1850 * Return TRUE when there is a TextChangedI autocommand defined.
1851 */
1852 int
1853has_textchangedI(void)
1854{
1855 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
1856}
1857
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001858/*
1859 * Return TRUE when there is a TextChangedP autocommand defined.
1860 */
1861 int
1862has_textchangedP(void)
1863{
1864 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
1865}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001866
1867/*
1868 * Return TRUE when there is an InsertCharPre autocommand defined.
1869 */
1870 int
1871has_insertcharpre(void)
1872{
1873 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
1874}
1875
1876/*
1877 * Return TRUE when there is an CmdUndefined autocommand defined.
1878 */
1879 int
1880has_cmdundefined(void)
1881{
1882 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
1883}
1884
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001885#if defined(FEAT_EVAL) || defined(PROTO)
1886/*
1887 * Return TRUE when there is a TextYankPost autocommand defined.
1888 */
1889 int
1890has_textyankpost(void)
1891{
1892 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
1893}
1894#endif
1895
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001896#if defined(FEAT_EVAL) || defined(PROTO)
1897/*
1898 * Return TRUE when there is a CompleteChanged autocommand defined.
1899 */
1900 int
1901has_completechanged(void)
1902{
1903 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
1904}
1905#endif
1906
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02001907#if defined(FEAT_EVAL) || defined(PROTO)
1908/*
1909 * Return TRUE when there is a ModeChanged autocommand defined.
1910 */
1911 int
1912has_modechanged(void)
1913{
1914 return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
1915}
1916#endif
1917
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001918/*
1919 * Execute autocommands for "event" and file name "fname".
1920 * Return TRUE if some commands were executed.
1921 */
1922 static int
1923apply_autocmds_group(
1924 event_T event,
1925 char_u *fname, // NULL or empty means use actual file name
1926 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
1927 // use fname
1928 int force, // when TRUE, ignore autocmd_busy
1929 int group, // group ID, or AUGROUP_ALL
1930 buf_T *buf, // buffer for <abuf>
1931 exarg_T *eap UNUSED) // command arguments
1932{
1933 char_u *sfname = NULL; // short file name
1934 char_u *tail;
1935 int save_changed;
1936 buf_T *old_curbuf;
1937 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001938 char_u *save_autocmd_fname;
1939 int save_autocmd_fname_full;
1940 int save_autocmd_bufnr;
1941 char_u *save_autocmd_match;
1942 int save_autocmd_busy;
1943 int save_autocmd_nested;
1944 static int nesting = 0;
LemonBoyeca7c602022-04-14 15:39:43 +01001945 AutoPatCmd_T patcmd;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001946 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001947 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001948#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001949 funccal_entry_T funccal_entry;
1950 char_u *save_cmdarg;
1951 long save_cmdbang;
1952#endif
1953 static int filechangeshell_busy = FALSE;
1954#ifdef FEAT_PROFILE
1955 proftime_T wait_time;
1956#endif
1957 int did_save_redobuff = FALSE;
1958 save_redo_T save_redo;
1959 int save_KeyTyped = KeyTyped;
ichizokc3f91c02021-12-17 09:44:33 +00001960 int save_did_emsg;
Bram Moolenaare31ee862020-01-07 20:59:34 +01001961 ESTACK_CHECK_DECLARATION
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001962
1963 /*
1964 * Quickly return if there are no autocommands for this event or
1965 * autocommands are blocked.
1966 */
1967 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
1968 || autocmd_blocked > 0)
1969 goto BYPASS_AU;
1970
1971 /*
1972 * When autocommands are busy, new autocommands are only executed when
1973 * explicitly enabled with the "nested" flag.
1974 */
1975 if (autocmd_busy && !(force || autocmd_nested))
1976 goto BYPASS_AU;
1977
1978#ifdef FEAT_EVAL
1979 /*
1980 * Quickly return when immediately aborting on error, or when an interrupt
1981 * occurred or an exception was thrown but not caught.
1982 */
1983 if (aborting())
1984 goto BYPASS_AU;
1985#endif
1986
1987 /*
1988 * FileChangedShell never nests, because it can create an endless loop.
1989 */
1990 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
1991 || event == EVENT_FILECHANGEDSHELLPOST))
1992 goto BYPASS_AU;
1993
1994 /*
1995 * Ignore events in 'eventignore'.
1996 */
1997 if (event_ignored(event))
1998 goto BYPASS_AU;
1999
2000 /*
2001 * Allow nesting of autocommands, but restrict the depth, because it's
2002 * possible to create an endless loop.
2003 */
2004 if (nesting == 10)
2005 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00002006 emsg(_(e_autocommand_nesting_too_deep));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002007 goto BYPASS_AU;
2008 }
2009
2010 /*
2011 * Check if these autocommands are disabled. Used when doing ":all" or
2012 * ":ball".
2013 */
2014 if ( (autocmd_no_enter
2015 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
2016 || (autocmd_no_leave
2017 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
2018 goto BYPASS_AU;
2019
2020 /*
2021 * Save the autocmd_* variables and info about the current buffer.
2022 */
2023 save_autocmd_fname = autocmd_fname;
2024 save_autocmd_fname_full = autocmd_fname_full;
2025 save_autocmd_bufnr = autocmd_bufnr;
2026 save_autocmd_match = autocmd_match;
2027 save_autocmd_busy = autocmd_busy;
2028 save_autocmd_nested = autocmd_nested;
2029 save_changed = curbuf->b_changed;
2030 old_curbuf = curbuf;
2031
2032 /*
2033 * Set the file name to be used for <afile>.
2034 * Make a copy to avoid that changing a buffer name or directory makes it
2035 * invalid.
2036 */
2037 if (fname_io == NULL)
2038 {
2039 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002040 || event == EVENT_OPTIONSET
2041 || event == EVENT_MODECHANGED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002042 autocmd_fname = NULL;
2043 else if (fname != NULL && !ends_excmd(*fname))
2044 autocmd_fname = fname;
2045 else if (buf != NULL)
2046 autocmd_fname = buf->b_ffname;
2047 else
2048 autocmd_fname = NULL;
2049 }
2050 else
2051 autocmd_fname = fname_io;
2052 if (autocmd_fname != NULL)
2053 autocmd_fname = vim_strsave(autocmd_fname);
2054 autocmd_fname_full = FALSE; // call FullName_save() later
2055
2056 /*
2057 * Set the buffer number to be used for <abuf>.
2058 */
2059 if (buf == NULL)
2060 autocmd_bufnr = 0;
2061 else
2062 autocmd_bufnr = buf->b_fnum;
2063
2064 /*
2065 * When the file name is NULL or empty, use the file name of buffer "buf".
2066 * Always use the full path of the file name to match with, in case
2067 * "allow_dirs" is set.
2068 */
2069 if (fname == NULL || *fname == NUL)
2070 {
2071 if (buf == NULL)
2072 fname = NULL;
2073 else
2074 {
2075#ifdef FEAT_SYN_HL
2076 if (event == EVENT_SYNTAX)
2077 fname = buf->b_p_syn;
2078 else
2079#endif
2080 if (event == EVENT_FILETYPE)
2081 fname = buf->b_p_ft;
2082 else
2083 {
2084 if (buf->b_sfname != NULL)
2085 sfname = vim_strsave(buf->b_sfname);
2086 fname = buf->b_ffname;
2087 }
2088 }
2089 if (fname == NULL)
2090 fname = (char_u *)"";
2091 fname = vim_strsave(fname); // make a copy, so we can change it
2092 }
2093 else
2094 {
2095 sfname = vim_strsave(fname);
2096 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002097 // ColorScheme, QuickFixCmd*, DirChanged and similar.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002098 if (event == EVENT_FILETYPE
2099 || event == EVENT_SYNTAX
2100 || event == EVENT_CMDLINECHANGED
2101 || event == EVENT_CMDLINEENTER
2102 || event == EVENT_CMDLINELEAVE
2103 || event == EVENT_CMDWINENTER
2104 || event == EVENT_CMDWINLEAVE
2105 || event == EVENT_CMDUNDEFINED
2106 || event == EVENT_FUNCUNDEFINED
2107 || event == EVENT_REMOTEREPLY
2108 || event == EVENT_SPELLFILEMISSING
2109 || event == EVENT_QUICKFIXCMDPRE
2110 || event == EVENT_COLORSCHEME
2111 || event == EVENT_COLORSCHEMEPRE
2112 || event == EVENT_OPTIONSET
2113 || event == EVENT_QUICKFIXCMDPOST
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002114 || event == EVENT_DIRCHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002115 || event == EVENT_DIRCHANGEDPRE
naohiro ono23beefe2021-11-13 12:38:49 +00002116 || event == EVENT_MODECHANGED
zeertzjqc601d982022-10-10 13:46:15 +01002117 || event == EVENT_MENUPOPUP
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002118 || event == EVENT_USER
LemonBoy09371822022-04-08 15:18:45 +01002119 || event == EVENT_WINCLOSED
2120 || event == EVENT_WINSCROLLED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002121 {
2122 fname = vim_strsave(fname);
2123 autocmd_fname_full = TRUE; // don't expand it later
2124 }
2125 else
2126 fname = FullName_save(fname, FALSE);
2127 }
2128 if (fname == NULL) // out of memory
2129 {
2130 vim_free(sfname);
2131 retval = FALSE;
2132 goto BYPASS_AU;
2133 }
2134
2135#ifdef BACKSLASH_IN_FILENAME
2136 /*
2137 * Replace all backslashes with forward slashes. This makes the
2138 * autocommand patterns portable between Unix and MS-DOS.
2139 */
2140 if (sfname != NULL)
2141 forward_slash(sfname);
2142 forward_slash(fname);
2143#endif
2144
2145#ifdef VMS
2146 // remove version for correct match
2147 if (sfname != NULL)
2148 vms_remove_version(sfname);
2149 vms_remove_version(fname);
2150#endif
2151
2152 /*
2153 * Set the name to be used for <amatch>.
2154 */
2155 autocmd_match = fname;
2156
2157
2158 // Don't redraw while doing autocommands.
2159 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002160
2161 // name and lnum are filled in later
2162 estack_push(ETYPE_AUCMD, NULL, 0);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002163 ESTACK_CHECK_SETUP
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002164
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002165 save_current_sctx = current_sctx;
2166
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002167#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002168# ifdef FEAT_PROFILE
2169 if (do_profiling == PROF_YES)
2170 prof_child_enter(&wait_time); // doesn't count for the caller itself
2171# endif
2172
2173 // Don't use local function variables, if called from a function.
2174 save_funccal(&funccal_entry);
2175#endif
2176
2177 /*
2178 * When starting to execute autocommands, save the search patterns.
2179 */
2180 if (!autocmd_busy)
2181 {
2182 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002183 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002184 {
2185 saveRedobuff(&save_redo);
2186 did_save_redobuff = TRUE;
2187 }
2188 did_filetype = keep_filetype;
2189 }
2190
2191 /*
2192 * Note that we are applying autocmds. Some commands need to know.
2193 */
2194 autocmd_busy = TRUE;
2195 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2196 ++nesting; // see matching decrement below
2197
2198 // Remember that FileType was triggered. Used for did_filetype().
2199 if (event == EVENT_FILETYPE)
2200 did_filetype = TRUE;
2201
2202 tail = gettail(fname);
2203
2204 // Find first autocommand that matches
LemonBoyeca7c602022-04-14 15:39:43 +01002205 CLEAR_FIELD(patcmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002206 patcmd.curpat = first_autopat[(int)event];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002207 patcmd.group = group;
2208 patcmd.fname = fname;
2209 patcmd.sfname = sfname;
2210 patcmd.tail = tail;
2211 patcmd.event = event;
2212 patcmd.arg_bufnr = autocmd_bufnr;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002213 auto_next_pat(&patcmd, FALSE);
2214
2215 // found one, start executing the autocommands
2216 if (patcmd.curpat != NULL)
2217 {
2218 // add to active_apc_list
2219 patcmd.next = active_apc_list;
2220 active_apc_list = &patcmd;
2221
2222#ifdef FEAT_EVAL
2223 // set v:cmdarg (only when there is a matching pattern)
2224 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2225 if (eap != NULL)
2226 {
2227 save_cmdarg = set_cmdarg(eap, NULL);
2228 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2229 }
2230 else
2231 save_cmdarg = NULL; // avoid gcc warning
2232#endif
2233 retval = TRUE;
2234 // mark the last pattern, to avoid an endless loop when more patterns
2235 // are added when executing autocommands
2236 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2237 ap->last = FALSE;
2238 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002239
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002240 // Make sure cursor and topline are valid. The first time the current
2241 // values are saved, restored by reset_lnums(). When nested only the
2242 // values are corrected when needed.
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002243 if (nesting == 1)
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002244 check_lnums(TRUE);
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002245 else
2246 check_lnums_nested(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002247
ichizokc3f91c02021-12-17 09:44:33 +00002248 save_did_emsg = did_emsg;
2249
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002250 do_cmdline(NULL, getnextac, (void *)&patcmd,
2251 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002252
ichizokc3f91c02021-12-17 09:44:33 +00002253 did_emsg += save_did_emsg;
2254
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002255 if (nesting == 1)
2256 // restore cursor and topline, unless they were changed
2257 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002258
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002259#ifdef FEAT_EVAL
2260 if (eap != NULL)
2261 {
2262 (void)set_cmdarg(NULL, save_cmdarg);
2263 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2264 }
2265#endif
2266 // delete from active_apc_list
2267 if (active_apc_list == &patcmd) // just in case
2268 active_apc_list = patcmd.next;
2269 }
2270
2271 --RedrawingDisabled;
2272 autocmd_busy = save_autocmd_busy;
2273 filechangeshell_busy = FALSE;
2274 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002275 vim_free(SOURCING_NAME);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002276 ESTACK_CHECK_NOW
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002277 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002278 vim_free(autocmd_fname);
2279 autocmd_fname = save_autocmd_fname;
2280 autocmd_fname_full = save_autocmd_fname_full;
2281 autocmd_bufnr = save_autocmd_bufnr;
2282 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002283 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002284#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002285 restore_funccal();
2286# ifdef FEAT_PROFILE
2287 if (do_profiling == PROF_YES)
2288 prof_child_exit(&wait_time);
2289# endif
2290#endif
2291 KeyTyped = save_KeyTyped;
2292 vim_free(fname);
2293 vim_free(sfname);
2294 --nesting; // see matching increment above
2295
2296 /*
2297 * When stopping to execute autocommands, restore the search patterns and
2298 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2299 * free any windows in the au_pending_free_win list.
2300 */
2301 if (!autocmd_busy)
2302 {
2303 restore_search_patterns();
2304 if (did_save_redobuff)
2305 restoreRedobuff(&save_redo);
2306 did_filetype = FALSE;
2307 while (au_pending_free_buf != NULL)
2308 {
2309 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002310
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002311 vim_free(au_pending_free_buf);
2312 au_pending_free_buf = b;
2313 }
2314 while (au_pending_free_win != NULL)
2315 {
2316 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002317
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002318 vim_free(au_pending_free_win);
2319 au_pending_free_win = w;
2320 }
2321 }
2322
2323 /*
2324 * Some events don't set or reset the Changed flag.
2325 * Check if still in the same buffer!
2326 */
2327 if (curbuf == old_curbuf
2328 && (event == EVENT_BUFREADPOST
2329 || event == EVENT_BUFWRITEPOST
2330 || event == EVENT_FILEAPPENDPOST
2331 || event == EVENT_VIMLEAVE
2332 || event == EVENT_VIMLEAVEPRE))
2333 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002334 if (curbuf->b_changed != save_changed)
2335 need_maketitle = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002336 curbuf->b_changed = save_changed;
2337 }
2338
2339 au_cleanup(); // may really delete removed patterns/commands now
2340
2341BYPASS_AU:
2342 // When wiping out a buffer make sure all its buffer-local autocommands
2343 // are deleted.
2344 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2345 aubuflocal_remove(buf);
2346
2347 if (retval == OK && event == EVENT_FILETYPE)
2348 au_did_filetype = TRUE;
2349
2350 return retval;
2351}
2352
2353# ifdef FEAT_EVAL
2354static char_u *old_termresponse = NULL;
2355# endif
2356
2357/*
2358 * Block triggering autocommands until unblock_autocmd() is called.
2359 * Can be used recursively, so long as it's symmetric.
2360 */
2361 void
2362block_autocmds(void)
2363{
2364# ifdef FEAT_EVAL
2365 // Remember the value of v:termresponse.
2366 if (autocmd_blocked == 0)
2367 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
2368# endif
2369 ++autocmd_blocked;
2370}
2371
2372 void
2373unblock_autocmds(void)
2374{
2375 --autocmd_blocked;
2376
2377# ifdef FEAT_EVAL
2378 // When v:termresponse was set while autocommands were blocked, trigger
2379 // the autocommands now. Esp. useful when executing a shell command
2380 // during startup (vimdiff).
2381 if (autocmd_blocked == 0
2382 && get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2383 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2384# endif
2385}
2386
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002387 int
2388is_autocmd_blocked(void)
2389{
2390 return autocmd_blocked != 0;
2391}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002392
2393/*
2394 * Find next autocommand pattern that matches.
2395 */
2396 static void
2397auto_next_pat(
LemonBoyeca7c602022-04-14 15:39:43 +01002398 AutoPatCmd_T *apc,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002399 int stop_at_last) // stop when 'last' flag is set
2400{
2401 AutoPat *ap;
2402 AutoCmd *cp;
2403 char_u *name;
2404 char *s;
LemonBoyeca7c602022-04-14 15:39:43 +01002405 estack_T *entry;
2406 char_u *namep;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002407
LemonBoyeca7c602022-04-14 15:39:43 +01002408 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
2409
2410 // Clear the exestack entry for this ETYPE_AUCMD entry.
2411 VIM_CLEAR(entry->es_name);
2412 entry->es_info.aucmd = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002413
2414 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2415 {
2416 apc->curpat = NULL;
2417
2418 // Only use a pattern when it has not been removed, has commands and
2419 // the group matches. For buffer-local autocommands only check the
2420 // buffer number.
2421 if (ap->pat != NULL && ap->cmds != NULL
2422 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2423 {
2424 // execution-condition
2425 if (ap->buflocal_nr == 0
2426 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2427 apc->sfname, apc->tail, ap->allow_dirs))
2428 : ap->buflocal_nr == apc->arg_bufnr)
2429 {
2430 name = event_nr2name(apc->event);
2431 s = _("%s Autocommands for \"%s\"");
LemonBoyeca7c602022-04-14 15:39:43 +01002432 namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1);
2433 if (namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002434 {
LemonBoyeca7c602022-04-14 15:39:43 +01002435 sprintf((char *)namep, s, (char *)name, (char *)ap->pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002436 if (p_verbose >= 8)
2437 {
2438 verbose_enter();
LemonBoyeca7c602022-04-14 15:39:43 +01002439 smsg(_("Executing %s"), namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002440 verbose_leave();
2441 }
2442 }
2443
LemonBoyeca7c602022-04-14 15:39:43 +01002444 // Update the exestack entry for this autocmd.
2445 entry->es_name = namep;
2446 entry->es_info.aucmd = apc;
2447
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002448 apc->curpat = ap;
2449 apc->nextcmd = ap->cmds;
2450 // mark last command
2451 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2452 cp->last = FALSE;
2453 cp->last = TRUE;
2454 }
2455 line_breakcheck();
2456 if (apc->curpat != NULL) // found a match
2457 break;
2458 }
2459 if (stop_at_last && ap->last)
2460 break;
2461 }
2462}
2463
2464/*
LemonBoyeca7c602022-04-14 15:39:43 +01002465 * Get the script context where autocommand "acp" is defined.
2466 */
2467 sctx_T *
2468acp_script_ctx(AutoPatCmd_T *acp)
2469{
2470 return &acp->script_ctx;
2471}
2472
2473/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002474 * Get next autocommand command.
2475 * Called by do_cmdline() to get the next line for ":if".
2476 * Returns allocated string, or NULL for end of autocommands.
2477 */
2478 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002479getnextac(
2480 int c UNUSED,
2481 void *cookie,
2482 int indent UNUSED,
2483 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002484{
LemonBoyeca7c602022-04-14 15:39:43 +01002485 AutoPatCmd_T *acp = (AutoPatCmd_T *)cookie;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002486 char_u *retval;
2487 AutoCmd *ac;
2488
2489 // Can be called again after returning the last line.
2490 if (acp->curpat == NULL)
2491 return NULL;
2492
2493 // repeat until we find an autocommand to execute
2494 for (;;)
2495 {
2496 // skip removed commands
2497 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2498 if (acp->nextcmd->last)
2499 acp->nextcmd = NULL;
2500 else
2501 acp->nextcmd = acp->nextcmd->next;
2502
2503 if (acp->nextcmd != NULL)
2504 break;
2505
2506 // at end of commands, find next pattern that matches
2507 if (acp->curpat->last)
2508 acp->curpat = NULL;
2509 else
2510 acp->curpat = acp->curpat->next;
2511 if (acp->curpat != NULL)
2512 auto_next_pat(acp, TRUE);
2513 if (acp->curpat == NULL)
2514 return NULL;
2515 }
2516
2517 ac = acp->nextcmd;
2518
2519 if (p_verbose >= 9)
2520 {
2521 verbose_enter_scroll();
2522 smsg(_("autocommand %s"), ac->cmd);
2523 msg_puts("\n"); // don't overwrite this either
2524 verbose_leave_scroll();
2525 }
2526 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002527 // Remove one-shot ("once") autocmd in anticipation of its execution.
2528 if (ac->once)
2529 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002530 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002531 current_sctx = ac->script_ctx;
LemonBoyeca7c602022-04-14 15:39:43 +01002532 acp->script_ctx = current_sctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002533 if (ac->last)
2534 acp->nextcmd = NULL;
2535 else
2536 acp->nextcmd = ac->next;
2537 return retval;
2538}
2539
2540/*
2541 * Return TRUE if there is a matching autocommand for "fname".
2542 * To account for buffer-local autocommands, function needs to know
2543 * in which buffer the file will be opened.
2544 */
2545 int
2546has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2547{
2548 AutoPat *ap;
2549 char_u *fname;
2550 char_u *tail = gettail(sfname);
2551 int retval = FALSE;
2552
2553 fname = FullName_save(sfname, FALSE);
2554 if (fname == NULL)
2555 return FALSE;
2556
2557#ifdef BACKSLASH_IN_FILENAME
2558 /*
2559 * Replace all backslashes with forward slashes. This makes the
2560 * autocommand patterns portable between Unix and MS-DOS.
2561 */
2562 sfname = vim_strsave(sfname);
2563 if (sfname != NULL)
2564 forward_slash(sfname);
2565 forward_slash(fname);
2566#endif
2567
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002568 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002569 if (ap->pat != NULL && ap->cmds != NULL
2570 && (ap->buflocal_nr == 0
2571 ? match_file_pat(NULL, &ap->reg_prog,
2572 fname, sfname, tail, ap->allow_dirs)
2573 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2574 ))
2575 {
2576 retval = TRUE;
2577 break;
2578 }
2579
2580 vim_free(fname);
2581#ifdef BACKSLASH_IN_FILENAME
2582 vim_free(sfname);
2583#endif
2584
2585 return retval;
2586}
2587
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002588/*
2589 * Function given to ExpandGeneric() to obtain the list of autocommand group
2590 * names.
2591 */
2592 char_u *
2593get_augroup_name(expand_T *xp UNUSED, int idx)
2594{
2595 if (idx == augroups.ga_len) // add "END" add the end
2596 return (char_u *)"END";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002597 if (idx < 0 || idx >= augroups.ga_len) // end of list
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002598 return NULL;
2599 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2600 // skip deleted entries
2601 return (char_u *)"";
2602 return AUGROUP_NAME(idx); // return a name
2603}
2604
2605static int include_groups = FALSE;
2606
2607 char_u *
2608set_context_in_autocmd(
2609 expand_T *xp,
2610 char_u *arg,
2611 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2612{
2613 char_u *p;
2614 int group;
2615
2616 // check for a group name, skip it if present
2617 include_groups = FALSE;
2618 p = arg;
2619 group = au_get_grouparg(&arg);
2620 if (group == AUGROUP_ERROR)
2621 return NULL;
2622 // If there only is a group name that's what we expand.
2623 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2624 {
2625 arg = p;
2626 group = AUGROUP_ALL;
2627 }
2628
2629 // skip over event name
2630 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2631 if (*p == ',')
2632 arg = p + 1;
2633 if (*p == NUL)
2634 {
2635 if (group == AUGROUP_ALL)
2636 include_groups = TRUE;
2637 xp->xp_context = EXPAND_EVENTS; // expand event name
2638 xp->xp_pattern = arg;
2639 return NULL;
2640 }
2641
2642 // skip over pattern
2643 arg = skipwhite(p);
2644 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2645 arg++;
2646 if (*arg)
2647 return arg; // expand (next) command
2648
2649 if (doautocmd)
2650 xp->xp_context = EXPAND_FILES; // expand file names
2651 else
2652 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2653 return NULL;
2654}
2655
2656/*
2657 * Function given to ExpandGeneric() to obtain the list of event names.
2658 */
2659 char_u *
2660get_event_name(expand_T *xp UNUSED, int idx)
2661{
2662 if (idx < augroups.ga_len) // First list group names, if wanted
2663 {
2664 if (!include_groups || AUGROUP_NAME(idx) == NULL
2665 || AUGROUP_NAME(idx) == get_deleted_augroup())
2666 return (char_u *)""; // skip deleted entries
2667 return AUGROUP_NAME(idx); // return a name
2668 }
2669 return (char_u *)event_names[idx - augroups.ga_len].name;
2670}
2671
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002672
2673#if defined(FEAT_EVAL) || defined(PROTO)
2674/*
2675 * Return TRUE if autocmd is supported.
2676 */
2677 int
2678autocmd_supported(char_u *name)
2679{
2680 char_u *p;
2681
2682 return (event_name2nr(name, &p) != NUM_EVENTS);
2683}
2684
2685/*
2686 * Return TRUE if an autocommand is defined for a group, event and
2687 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2688 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2689 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2690 * Used for:
2691 * exists("#Group") or
2692 * exists("#Group#Event") or
2693 * exists("#Group#Event#pat") or
2694 * exists("#Event") or
2695 * exists("#Event#pat")
2696 */
2697 int
2698au_exists(char_u *arg)
2699{
2700 char_u *arg_save;
2701 char_u *pattern = NULL;
2702 char_u *event_name;
2703 char_u *p;
2704 event_T event;
2705 AutoPat *ap;
2706 buf_T *buflocal_buf = NULL;
2707 int group;
2708 int retval = FALSE;
2709
2710 // Make a copy so that we can change the '#' chars to a NUL.
2711 arg_save = vim_strsave(arg);
2712 if (arg_save == NULL)
2713 return FALSE;
2714 p = vim_strchr(arg_save, '#');
2715 if (p != NULL)
2716 *p++ = NUL;
2717
2718 // First, look for an autocmd group name
2719 group = au_find_group(arg_save);
2720 if (group == AUGROUP_ERROR)
2721 {
2722 // Didn't match a group name, assume the first argument is an event.
2723 group = AUGROUP_ALL;
2724 event_name = arg_save;
2725 }
2726 else
2727 {
2728 if (p == NULL)
2729 {
2730 // "Group": group name is present and it's recognized
2731 retval = TRUE;
2732 goto theend;
2733 }
2734
2735 // Must be "Group#Event" or "Group#Event#pat".
2736 event_name = p;
2737 p = vim_strchr(event_name, '#');
2738 if (p != NULL)
2739 *p++ = NUL; // "Group#Event#pat"
2740 }
2741
2742 pattern = p; // "pattern" is NULL when there is no pattern
2743
2744 // find the index (enum) for the event name
2745 event = event_name2nr(event_name, &p);
2746
2747 // return FALSE if the event name is not recognized
2748 if (event == NUM_EVENTS)
2749 goto theend;
2750
2751 // Find the first autocommand for this event.
2752 // If there isn't any, return FALSE;
2753 // If there is one and no pattern given, return TRUE;
2754 ap = first_autopat[(int)event];
2755 if (ap == NULL)
2756 goto theend;
2757
2758 // if pattern is "<buffer>", special handling is needed which uses curbuf
2759 // for pattern "<buffer=N>, fnamecmp() will work fine
2760 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2761 buflocal_buf = curbuf;
2762
2763 // Check if there is an autocommand with the given pattern.
2764 for ( ; ap != NULL; ap = ap->next)
2765 // only use a pattern when it has not been removed and has commands.
2766 // For buffer-local autocommands, fnamecmp() works fine.
2767 if (ap->pat != NULL && ap->cmds != NULL
2768 && (group == AUGROUP_ALL || ap->group == group)
2769 && (pattern == NULL
2770 || (buflocal_buf == NULL
2771 ? fnamecmp(ap->pat, pattern) == 0
2772 : ap->buflocal_nr == buflocal_buf->b_fnum)))
2773 {
2774 retval = TRUE;
2775 break;
2776 }
2777
2778theend:
2779 vim_free(arg_save);
2780 return retval;
2781}
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002782
2783/*
2784 * autocmd_add() and autocmd_delete() functions
2785 */
2786 static void
2787autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
2788{
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002789 list_T *aucmd_list;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002790 listitem_T *li;
2791 dict_T *event_dict;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002792 dictitem_T *di;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002793 char_u *event_name = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002794 list_T *event_list;
2795 listitem_T *eli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002796 event_T event;
2797 char_u *group_name = NULL;
2798 int group;
2799 char_u *pat = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002800 list_T *pat_list;
2801 listitem_T *pli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002802 char_u *cmd = NULL;
2803 char_u *end;
2804 int once;
2805 int nested;
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01002806 int replace; // replace the cmd for a group/event
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002807 int retval = VVAL_TRUE;
2808 int save_augroup = current_augroup;
2809
2810 rettv->v_type = VAR_BOOL;
2811 rettv->vval.v_number = VVAL_FALSE;
2812
2813 if (check_for_list_arg(argvars, 0) == FAIL)
2814 return;
2815
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002816 aucmd_list = argvars[0].vval.v_list;
2817 if (aucmd_list == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002818 return;
2819
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002820 FOR_ALL_LIST_ITEMS(aucmd_list, li)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002821 {
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002822 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002823 VIM_CLEAR(cmd);
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002824 event_name = NULL;
2825 event_list = NULL;
2826 pat = NULL;
2827 pat_list = NULL;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002828
2829 if (li->li_tv.v_type != VAR_DICT)
2830 continue;
2831
2832 event_dict = li->li_tv.vval.v_dict;
2833 if (event_dict == NULL)
2834 continue;
2835
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002836 di = dict_find(event_dict, (char_u *)"event", -1);
2837 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002838 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002839 if (di->di_tv.v_type == VAR_STRING)
2840 {
2841 event_name = di->di_tv.vval.v_string;
2842 if (event_name == NULL)
2843 {
2844 emsg(_(e_string_required));
2845 continue;
2846 }
2847 }
2848 else if (di->di_tv.v_type == VAR_LIST)
2849 {
2850 event_list = di->di_tv.vval.v_list;
2851 if (event_list == NULL)
2852 {
2853 emsg(_(e_list_required));
2854 continue;
2855 }
2856 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002857 else
2858 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002859 emsg(_(e_string_or_list_expected));
2860 continue;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002861 }
2862 }
2863
Bram Moolenaard61efa52022-07-23 09:52:04 +01002864 group_name = dict_get_string(event_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002865 if (group_name == NULL || *group_name == NUL)
2866 // if the autocmd group name is not specified, then use the current
2867 // autocmd group
2868 group = current_augroup;
2869 else
2870 {
2871 group = au_find_group(group_name);
2872 if (group == AUGROUP_ERROR)
2873 {
2874 if (delete)
2875 {
2876 semsg(_(e_no_such_group_str), group_name);
2877 retval = VVAL_FALSE;
2878 break;
2879 }
2880 // group is not found, create it now
2881 group = au_new_group(group_name);
2882 if (group == AUGROUP_ERROR)
2883 {
2884 semsg(_(e_no_such_group_str), group_name);
2885 retval = VVAL_FALSE;
2886 break;
2887 }
2888
2889 current_augroup = group;
2890 }
2891 }
2892
2893 // if a buffer number is specified, then generate a pattern of the form
2894 // "<buffer=n>. Otherwise, use the pattern supplied by the user.
2895 if (dict_has_key(event_dict, "bufnr"))
2896 {
2897 varnumber_T bnum;
2898
Bram Moolenaard61efa52022-07-23 09:52:04 +01002899 bnum = dict_get_number_def(event_dict, "bufnr", -1);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002900 if (bnum == -1)
2901 continue;
2902
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002903 vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
2904 pat = IObuff;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002905 }
2906 else
2907 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002908 di = dict_find(event_dict, (char_u *)"pattern", -1);
2909 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002910 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002911 if (di->di_tv.v_type == VAR_STRING)
2912 {
2913 pat = di->di_tv.vval.v_string;
2914 if (pat == NULL)
2915 {
2916 emsg(_(e_string_required));
2917 continue;
2918 }
2919 }
2920 else if (di->di_tv.v_type == VAR_LIST)
2921 {
2922 pat_list = di->di_tv.vval.v_list;
2923 if (pat_list == NULL)
2924 {
2925 emsg(_(e_list_required));
2926 continue;
2927 }
2928 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002929 else
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002930 {
2931 emsg(_(e_string_or_list_expected));
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002932 continue;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002933 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002934 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002935 else if (delete)
2936 pat = (char_u *)"";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002937 }
2938
Bram Moolenaard61efa52022-07-23 09:52:04 +01002939 once = dict_get_bool(event_dict, "once", FALSE);
2940 nested = dict_get_bool(event_dict, "nested", FALSE);
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01002941 // if 'replace' is true, then remove all the commands associated with
2942 // this autocmd event/group and add the new command.
Bram Moolenaard61efa52022-07-23 09:52:04 +01002943 replace = dict_get_bool(event_dict, "replace", FALSE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002944
Bram Moolenaard61efa52022-07-23 09:52:04 +01002945 cmd = dict_get_string(event_dict, "cmd", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002946 if (cmd == NULL)
2947 {
2948 if (delete)
2949 cmd = vim_strsave((char_u *)"");
2950 else
2951 continue;
2952 }
2953
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002954 if (delete && (event_name == NULL
2955 || (event_name[0] == '*' && event_name[1] == NUL)))
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002956 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002957 // if the event name is not specified or '*', delete all the events
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002958 for (event = (event_T)0; (int)event < NUM_EVENTS;
2959 event = (event_T)((int)event + 1))
2960 {
2961 if (do_autocmd_event(event, pat, once, nested, cmd, delete,
2962 group, 0) == FAIL)
2963 {
2964 retval = VVAL_FALSE;
2965 break;
2966 }
2967 }
2968 }
2969 else
2970 {
Bram Moolenaar968443e2022-05-27 21:16:34 +01002971 char_u *p = NULL;
2972
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002973 eli = NULL;
2974 end = NULL;
2975 while (TRUE)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002976 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002977 if (event_list != NULL)
2978 {
2979 if (eli == NULL)
2980 eli = event_list->lv_first;
2981 else
2982 eli = eli->li_next;
2983 if (eli == NULL)
2984 break;
2985 if (eli->li_tv.v_type != VAR_STRING
Bram Moolenaar882476a2022-06-01 16:02:38 +01002986 || (p = eli->li_tv.vval.v_string) == NULL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002987 {
2988 emsg(_(e_string_required));
Bram Moolenaar882476a2022-06-01 16:02:38 +01002989 break;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002990 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002991 }
2992 else
2993 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01002994 if (p == NULL)
2995 p = event_name;
2996 if (p == NULL || *p == NUL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002997 break;
2998 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002999
3000 event = event_name2nr(p, &end);
3001 if (event == NUM_EVENTS || *end != NUL)
3002 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003003 // this also catches something following a valid event name
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003004 semsg(_(e_no_such_event_str), p);
3005 retval = VVAL_FALSE;
3006 break;
3007 }
3008 if (pat != NULL)
3009 {
3010 if (do_autocmd_event(event, pat, once, nested, cmd,
3011 delete | replace, group, 0) == FAIL)
3012 {
3013 retval = VVAL_FALSE;
3014 break;
3015 }
3016 }
3017 else if (pat_list != NULL)
3018 {
3019 FOR_ALL_LIST_ITEMS(pat_list, pli)
3020 {
3021 if (pli->li_tv.v_type != VAR_STRING
3022 || pli->li_tv.vval.v_string == NULL)
3023 {
3024 emsg(_(e_string_required));
3025 continue;
3026 }
3027 if (do_autocmd_event(event,
3028 pli->li_tv.vval.v_string, once, nested,
3029 cmd, delete | replace, group, 0) ==
3030 FAIL)
3031 {
3032 retval = VVAL_FALSE;
3033 break;
3034 }
3035 }
3036 if (retval == VVAL_FALSE)
3037 break;
3038 }
3039 if (event_name != NULL)
3040 p = end;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003041 }
3042 }
3043
3044 // if only the autocmd group name is specified for delete and the
3045 // autocmd event, pattern and cmd are not specified, then delete the
3046 // autocmd group.
3047 if (delete && group_name != NULL &&
3048 (event_name == NULL || event_name[0] == NUL)
3049 && (pat == NULL || pat[0] == NUL)
3050 && (cmd == NULL || cmd[0] == NUL))
3051 au_del_group(group_name);
3052 }
3053
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003054 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003055 VIM_CLEAR(cmd);
3056
3057 current_augroup = save_augroup;
3058 rettv->vval.v_number = retval;
3059}
3060
3061/*
3062 * autocmd_add() function
3063 */
3064 void
3065f_autocmd_add(typval_T *argvars, typval_T *rettv)
3066{
3067 autocmd_add_or_delete(argvars, rettv, FALSE);
3068}
3069
3070/*
3071 * autocmd_delete() function
3072 */
3073 void
3074f_autocmd_delete(typval_T *argvars, typval_T *rettv)
3075{
3076 autocmd_add_or_delete(argvars, rettv, TRUE);
3077}
3078
3079/*
3080 * autocmd_get() function
3081 * Returns a List of autocmds.
3082 */
3083 void
3084f_autocmd_get(typval_T *argvars, typval_T *rettv)
3085{
3086 event_T event_arg = NUM_EVENTS;
3087 event_T event;
3088 AutoPat *ap;
3089 AutoCmd *ac;
3090 list_T *event_list;
3091 dict_T *event_dict;
3092 char_u *event_name = NULL;
3093 char_u *pat = NULL;
3094 char_u *name = NULL;
3095 int group = AUGROUP_ALL;
3096
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01003097 if (rettv_list_alloc(rettv) == FAIL)
3098 return;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003099 if (check_for_opt_dict_arg(argvars, 0) == FAIL)
3100 return;
3101
3102 if (argvars[0].v_type == VAR_DICT)
3103 {
3104 // return only the autocmds in the specified group
3105 if (dict_has_key(argvars[0].vval.v_dict, "group"))
3106 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003107 name = dict_get_string(argvars[0].vval.v_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003108 if (name == NULL)
3109 return;
3110
3111 if (*name == NUL)
3112 group = AUGROUP_DEFAULT;
3113 else
3114 {
3115 group = au_find_group(name);
3116 if (group == AUGROUP_ERROR)
3117 {
3118 semsg(_(e_no_such_group_str), name);
3119 vim_free(name);
3120 return;
3121 }
3122 }
3123 vim_free(name);
3124 }
3125
3126 // return only the autocmds for the specified event
3127 if (dict_has_key(argvars[0].vval.v_dict, "event"))
3128 {
3129 int i;
3130
Bram Moolenaard61efa52022-07-23 09:52:04 +01003131 name = dict_get_string(argvars[0].vval.v_dict, "event", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003132 if (name == NULL)
3133 return;
3134
3135 if (name[0] == '*' && name[1] == NUL)
3136 event_arg = NUM_EVENTS;
3137 else
3138 {
3139 for (i = 0; event_names[i].name != NULL; i++)
3140 if (STRICMP(event_names[i].name, name) == 0)
3141 break;
3142 if (event_names[i].name == NULL)
3143 {
3144 semsg(_(e_no_such_event_str), name);
3145 vim_free(name);
3146 return;
3147 }
3148 event_arg = event_names[i].event;
3149 }
3150 vim_free(name);
3151 }
3152
3153 // return only the autocmds for the specified pattern
3154 if (dict_has_key(argvars[0].vval.v_dict, "pattern"))
3155 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003156 pat = dict_get_string(argvars[0].vval.v_dict, "pattern", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003157 if (pat == NULL)
3158 return;
3159 }
3160 }
3161
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003162 event_list = rettv->vval.v_list;
3163
3164 // iterate through all the autocmd events
3165 for (event = (event_T)0; (int)event < NUM_EVENTS;
3166 event = (event_T)((int)event + 1))
3167 {
3168 if (event_arg != NUM_EVENTS && event != event_arg)
3169 continue;
3170
3171 event_name = event_nr2name(event);
3172
3173 // iterate through all the patterns for this autocmd event
3174 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
3175 {
3176 char_u *group_name;
3177
3178 if (group != AUGROUP_ALL && group != ap->group)
3179 continue;
3180
3181 if (pat != NULL && STRCMP(pat, ap->pat) != 0)
3182 continue;
3183
3184 group_name = get_augroup_name(NULL, ap->group);
3185
3186 // iterate through all the commands for this pattern and add one
3187 // item for each cmd.
3188 for (ac = ap->cmds; ac != NULL; ac = ac->next)
3189 {
3190 event_dict = dict_alloc();
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003191 if (event_dict == NULL
3192 || list_append_dict(event_list, event_dict) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003193 return;
3194
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003195 if (dict_add_string(event_dict, "event", event_name) == FAIL
3196 || dict_add_string(event_dict, "group",
3197 group_name == NULL ? (char_u *)""
3198 : group_name) == FAIL
3199 || (ap->buflocal_nr != 0
3200 && (dict_add_number(event_dict, "bufnr",
3201 ap->buflocal_nr) == FAIL))
3202 || dict_add_string(event_dict, "pattern",
3203 ap->pat) == FAIL
3204 || dict_add_string(event_dict, "cmd", ac->cmd) == FAIL
3205 || dict_add_bool(event_dict, "once", ac->once) == FAIL
3206 || dict_add_bool(event_dict, "nested",
3207 ac->nested) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003208 return;
3209 }
3210 }
3211 }
3212
3213 vim_free(pat);
3214}
3215
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01003216#endif