blob: 8a0992c092e973ec27497ca588f271a7b98daea9 [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
1267 // stop it from firing right after the first autocmd is defined.
1268 if (event == EVENT_WINSCROLLED && !has_winscrolled())
1269 {
1270 curwin->w_last_topline = curwin->w_topline;
1271 curwin->w_last_leftcol = curwin->w_leftcol;
zeertzjq670ab032022-08-28 19:16:15 +01001272 curwin->w_last_skipcol = curwin->w_skipcol;
LemonBoy09371822022-04-08 15:18:45 +01001273 curwin->w_last_width = curwin->w_width;
1274 curwin->w_last_height = curwin->w_height;
1275 }
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001276
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001277 if (is_buflocal)
1278 {
1279 ap->buflocal_nr = buflocal_nr;
1280 ap->reg_prog = NULL;
1281 }
1282 else
1283 {
1284 char_u *reg_pat;
1285
1286 ap->buflocal_nr = 0;
1287 reg_pat = file_pat_to_reg_pat(pat, endpat,
1288 &ap->allow_dirs, TRUE);
1289 if (reg_pat != NULL)
1290 ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
1291 vim_free(reg_pat);
1292 if (reg_pat == NULL || ap->reg_prog == NULL)
1293 {
1294 vim_free(ap->pat);
1295 vim_free(ap);
1296 return FAIL;
1297 }
1298 }
1299 ap->cmds = NULL;
1300 *prev_ap = ap;
1301 last_autopat[(int)event] = ap;
1302 ap->next = NULL;
1303 if (group == AUGROUP_ALL)
1304 ap->group = current_augroup;
1305 else
1306 ap->group = group;
1307 }
1308
1309 /*
1310 * Add the autocmd at the end of the AutoCmd list.
1311 */
1312 prev_ac = &(ap->cmds);
1313 while ((ac = *prev_ac) != NULL)
1314 prev_ac = &ac->next;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001315 ac = ALLOC_ONE(AutoCmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001316 if (ac == NULL)
1317 return FAIL;
1318 ac->cmd = vim_strsave(cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001319 ac->script_ctx = current_sctx;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001320 if (flags & UC_VIM9)
1321 ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001322#ifdef FEAT_EVAL
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001323 ac->script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001324#endif
1325 if (ac->cmd == NULL)
1326 {
1327 vim_free(ac);
1328 return FAIL;
1329 }
1330 ac->next = NULL;
1331 *prev_ac = ac;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001332 ac->once = once;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001333 ac->nested = nested;
1334 }
1335 }
1336
1337 au_cleanup(); // may really delete removed patterns/commands now
1338 return OK;
1339}
1340
1341/*
1342 * Implementation of ":doautocmd [group] event [fname]".
1343 * Return OK for success, FAIL for failure;
1344 */
1345 int
1346do_doautocmd(
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001347 char_u *arg_start,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001348 int do_msg, // give message for no matching autocmds?
1349 int *did_something)
1350{
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001351 char_u *arg = arg_start;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001352 char_u *fname;
1353 int nothing_done = TRUE;
1354 int group;
1355
1356 if (did_something != NULL)
1357 *did_something = FALSE;
1358
1359 /*
1360 * Check for a legal group name. If not, use AUGROUP_ALL.
1361 */
1362 group = au_get_grouparg(&arg);
1363 if (arg == NULL) // out of memory
1364 return FAIL;
1365
1366 if (*arg == '*')
1367 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001368 emsg(_(e_cant_execute_autocommands_for_all_events));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001369 return FAIL;
1370 }
1371
1372 /*
1373 * Scan over the events.
1374 * If we find an illegal name, return here, don't do anything.
1375 */
1376 fname = find_end_event(arg, group != AUGROUP_ALL);
1377 if (fname == NULL)
1378 return FAIL;
1379
1380 fname = skipwhite(fname);
1381
1382 /*
1383 * Loop over the events.
1384 */
1385 while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
1386 if (apply_autocmds_group(event_name2nr(arg, &arg),
1387 fname, NULL, TRUE, group, curbuf, NULL))
1388 nothing_done = FALSE;
1389
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001390 if (nothing_done && do_msg
1391#ifdef FEAT_EVAL
1392 && !aborting()
1393#endif
1394 )
1395 smsg(_("No matching autocommands: %s"), arg_start);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001396 if (did_something != NULL)
1397 *did_something = !nothing_done;
1398
1399#ifdef FEAT_EVAL
1400 return aborting() ? FAIL : OK;
1401#else
1402 return OK;
1403#endif
1404}
1405
1406/*
1407 * ":doautoall": execute autocommands for each loaded buffer.
1408 */
1409 void
1410ex_doautoall(exarg_T *eap)
1411{
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001412 int retval = OK;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001413 aco_save_T aco;
1414 buf_T *buf;
1415 bufref_T bufref;
1416 char_u *arg = eap->arg;
1417 int call_do_modelines = check_nomodeline(&arg);
1418 int did_aucmd;
1419
1420 /*
1421 * This is a bit tricky: For some commands curwin->w_buffer needs to be
1422 * equal to curbuf, but for some buffers there may not be a window.
1423 * So we change the buffer for the current window for a moment. This
1424 * gives problems when the autocommands make changes to the list of
1425 * buffers or windows...
1426 */
1427 FOR_ALL_BUFFERS(buf)
1428 {
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001429 // Only do loaded buffers and skip the current buffer, it's done last.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001430 if (buf->b_ml.ml_mfp == NULL || buf == curbuf)
1431 continue;
1432
1433 // find a window for this buffer and save some values
1434 aucmd_prepbuf(&aco, buf);
1435 set_bufref(&bufref, buf);
1436
1437 // execute the autocommands for this buffer
1438 retval = do_doautocmd(arg, FALSE, &did_aucmd);
1439
1440 if (call_do_modelines && did_aucmd)
1441 // Execute the modeline settings, but don't set window-local
1442 // options if we are using the current window for another
1443 // buffer.
1444 do_modelines(curwin == aucmd_win ? OPT_NOWIN : 0);
1445
1446 // restore the current window
1447 aucmd_restbuf(&aco);
1448
1449 // stop if there is some error or buffer was deleted
1450 if (retval == FAIL || !bufref_valid(&bufref))
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001451 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001452 retval = FAIL;
1453 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001454 }
1455 }
1456
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001457 // Execute autocommands for the current buffer last.
1458 if (retval == OK)
1459 {
1460 do_doautocmd(arg, FALSE, &did_aucmd);
1461 if (call_do_modelines && did_aucmd)
1462 do_modelines(0);
1463 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001464}
1465
1466/*
1467 * Check *argp for <nomodeline>. When it is present return FALSE, otherwise
1468 * return TRUE and advance *argp to after it.
1469 * Thus return TRUE when do_modelines() should be called.
1470 */
1471 int
1472check_nomodeline(char_u **argp)
1473{
1474 if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
1475 {
1476 *argp = skipwhite(*argp + 12);
1477 return FALSE;
1478 }
1479 return TRUE;
1480}
1481
1482/*
1483 * Prepare for executing autocommands for (hidden) buffer "buf".
1484 * Search for a visible window containing the current buffer. If there isn't
1485 * one then use "aucmd_win".
1486 * Set "curbuf" and "curwin" to match "buf".
1487 */
1488 void
1489aucmd_prepbuf(
1490 aco_save_T *aco, // structure to save values in
1491 buf_T *buf) // new curbuf
1492{
1493 win_T *win;
1494 int save_ea;
1495#ifdef FEAT_AUTOCHDIR
1496 int save_acd;
1497#endif
1498
1499 // Find a window that is for the new buffer
1500 if (buf == curbuf) // be quick when buf is curbuf
1501 win = curwin;
1502 else
1503 FOR_ALL_WINDOWS(win)
1504 if (win->w_buffer == buf)
1505 break;
1506
1507 // Allocate "aucmd_win" when needed. If this fails (out of memory) fall
1508 // back to using the current window.
1509 if (win == NULL && aucmd_win == NULL)
1510 {
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001511 aucmd_win = win_alloc_popup_win();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001512 if (aucmd_win == NULL)
1513 win = curwin;
1514 }
1515 if (win == NULL && aucmd_win_used)
1516 // Strange recursive autocommand, fall back to using the current
1517 // window. Expect a few side effects...
1518 win = curwin;
1519
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001520 aco->save_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001521 aco->save_curbuf = curbuf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001522 aco->save_prevwin_id = prevwin == NULL ? 0 : prevwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001523 if (win != NULL)
1524 {
1525 // There is a window for "buf" in the current tab page, make it the
1526 // curwin. This is preferred, it has the least side effects (esp. if
1527 // "buf" is curbuf).
1528 aco->use_aucmd_win = FALSE;
1529 curwin = win;
1530 }
1531 else
1532 {
1533 // There is no window for "buf", use "aucmd_win". To minimize the side
1534 // effects, insert it in the current tab page.
1535 // Anything related to a window (e.g., setting folds) may have
1536 // unexpected results.
1537 aco->use_aucmd_win = TRUE;
1538 aucmd_win_used = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001539
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001540 win_init_popup_win(aucmd_win, buf);
1541
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001542 aco->globaldir = globaldir;
1543 globaldir = NULL;
1544
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001545 // Split the current window, put the aucmd_win in the upper half.
1546 // We don't want the BufEnter or WinEnter autocommands.
1547 block_autocmds();
1548 make_snapshot(SNAP_AUCMD_IDX);
1549 save_ea = p_ea;
1550 p_ea = FALSE;
1551
1552#ifdef FEAT_AUTOCHDIR
1553 // Prevent chdir() call in win_enter_ext(), through do_autochdir().
1554 save_acd = p_acd;
1555 p_acd = FALSE;
1556#endif
1557
Bram Moolenaardff97e62022-01-24 20:00:55 +00001558 // no redrawing and don't set the window title
1559 ++RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001560 (void)win_split_ins(0, WSP_TOP, aucmd_win, 0);
Bram Moolenaardff97e62022-01-24 20:00:55 +00001561 --RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001562 (void)win_comp_pos(); // recompute window positions
1563 p_ea = save_ea;
1564#ifdef FEAT_AUTOCHDIR
1565 p_acd = save_acd;
1566#endif
1567 unblock_autocmds();
1568 curwin = aucmd_win;
1569 }
1570 curbuf = buf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001571 aco->new_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001572 set_bufref(&aco->new_curbuf, curbuf);
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001573
1574 // disable the Visual area, the position may be invalid in another buffer
1575 aco->save_VIsual_active = VIsual_active;
1576 VIsual_active = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001577}
1578
1579/*
1580 * Cleanup after executing autocommands for a (hidden) buffer.
1581 * Restore the window as it was (if possible).
1582 */
1583 void
1584aucmd_restbuf(
1585 aco_save_T *aco) // structure holding saved values
1586{
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001587 int dummy;
1588 win_T *save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001589
1590 if (aco->use_aucmd_win)
1591 {
1592 --curbuf->b_nwindows;
1593 // Find "aucmd_win", it can't be closed, but it may be in another tab
1594 // page. Do not trigger autocommands here.
1595 block_autocmds();
1596 if (curwin != aucmd_win)
1597 {
1598 tabpage_T *tp;
1599 win_T *wp;
1600
1601 FOR_ALL_TAB_WINDOWS(tp, wp)
1602 {
1603 if (wp == aucmd_win)
1604 {
1605 if (tp != curtab)
1606 goto_tabpage_tp(tp, TRUE, TRUE);
1607 win_goto(aucmd_win);
1608 goto win_found;
1609 }
1610 }
1611 }
1612win_found:
1613
1614 // Remove the window and frame from the tree of frames.
1615 (void)winframe_remove(curwin, &dummy, NULL);
1616 win_remove(curwin, NULL);
1617 aucmd_win_used = FALSE;
1618 last_status(FALSE); // may need to remove last status line
1619
1620 if (!valid_tabpage_win(curtab))
1621 // no valid window in current tabpage
1622 close_tabpage(curtab);
1623
1624 restore_snapshot(SNAP_AUCMD_IDX, FALSE);
1625 (void)win_comp_pos(); // recompute window positions
1626 unblock_autocmds();
1627
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001628 save_curwin = win_find_by_id(aco->save_curwin_id);
1629 if (save_curwin != NULL)
1630 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001631 else
1632 // Hmm, original window disappeared. Just use the first one.
1633 curwin = firstwin;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001634 curbuf = curwin->w_buffer;
1635#ifdef FEAT_JOB_CHANNEL
1636 // May need to restore insert mode for a prompt buffer.
1637 entering_window(curwin);
1638#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001639 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001640#ifdef FEAT_EVAL
1641 vars_clear(&aucmd_win->w_vars->dv_hashtab); // free all w: variables
1642 hash_init(&aucmd_win->w_vars->dv_hashtab); // re-use the hashtab
1643#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001644 vim_free(globaldir);
1645 globaldir = aco->globaldir;
1646
1647 // the buffer contents may have changed
1648 check_cursor();
1649 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1650 {
1651 curwin->w_topline = curbuf->b_ml.ml_line_count;
1652#ifdef FEAT_DIFF
1653 curwin->w_topfill = 0;
1654#endif
1655 }
1656#if defined(FEAT_GUI)
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001657 if (gui.in_use)
1658 {
1659 // Hide the scrollbars from the aucmd_win and update.
1660 gui_mch_enable_scrollbar(
1661 &aucmd_win->w_scrollbars[SBAR_LEFT], FALSE);
1662 gui_mch_enable_scrollbar(
1663 &aucmd_win->w_scrollbars[SBAR_RIGHT], FALSE);
1664 gui_may_update_scrollbars();
1665 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001666#endif
1667 }
1668 else
1669 {
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001670 // Restore curwin. Use the window ID, a window may have been closed
1671 // and the memory re-used for another one.
1672 save_curwin = win_find_by_id(aco->save_curwin_id);
1673 if (save_curwin != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001674 {
1675 // Restore the buffer which was previously edited by curwin, if
1676 // it was changed, we are still the same window and the buffer is
1677 // valid.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001678 if (curwin->w_id == aco->new_curwin_id
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001679 && curbuf != aco->new_curbuf.br_buf
1680 && bufref_valid(&aco->new_curbuf)
1681 && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
1682 {
1683# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
1684 if (curwin->w_s == &curbuf->b_s)
1685 curwin->w_s = &aco->new_curbuf.br_buf->b_s;
1686# endif
1687 --curbuf->b_nwindows;
1688 curbuf = aco->new_curbuf.br_buf;
1689 curwin->w_buffer = curbuf;
1690 ++curbuf->b_nwindows;
1691 }
1692
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001693 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001694 curbuf = curwin->w_buffer;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001695 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001696 // In case the autocommand moves the cursor to a position that
1697 // does not exist in curbuf.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001698 check_cursor();
1699 }
1700 }
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001701
1702 check_cursor(); // just in case lines got deleted
1703 VIsual_active = aco->save_VIsual_active;
1704 if (VIsual_active)
1705 check_pos(curbuf, &VIsual);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001706}
1707
1708static int autocmd_nested = FALSE;
1709
1710/*
1711 * Execute autocommands for "event" and file name "fname".
1712 * Return TRUE if some commands were executed.
1713 */
1714 int
1715apply_autocmds(
1716 event_T event,
1717 char_u *fname, // NULL or empty means use actual file name
1718 char_u *fname_io, // fname to use for <afile> on cmdline
1719 int force, // when TRUE, ignore autocmd_busy
1720 buf_T *buf) // buffer for <abuf>
1721{
1722 return apply_autocmds_group(event, fname, fname_io, force,
1723 AUGROUP_ALL, buf, NULL);
1724}
1725
1726/*
1727 * Like apply_autocmds(), but with extra "eap" argument. This takes care of
1728 * setting v:filearg.
1729 */
1730 int
1731apply_autocmds_exarg(
1732 event_T event,
1733 char_u *fname,
1734 char_u *fname_io,
1735 int force,
1736 buf_T *buf,
1737 exarg_T *eap)
1738{
1739 return apply_autocmds_group(event, fname, fname_io, force,
1740 AUGROUP_ALL, buf, eap);
1741}
1742
1743/*
1744 * Like apply_autocmds(), but handles the caller's retval. If the script
1745 * processing is being aborted or if retval is FAIL when inside a try
1746 * conditional, no autocommands are executed. If otherwise the autocommands
1747 * cause the script to be aborted, retval is set to FAIL.
1748 */
1749 int
1750apply_autocmds_retval(
1751 event_T event,
1752 char_u *fname, // NULL or empty means use actual file name
1753 char_u *fname_io, // fname to use for <afile> on cmdline
1754 int force, // when TRUE, ignore autocmd_busy
1755 buf_T *buf, // buffer for <abuf>
1756 int *retval) // pointer to caller's retval
1757{
1758 int did_cmd;
1759
1760#ifdef FEAT_EVAL
1761 if (should_abort(*retval))
1762 return FALSE;
1763#endif
1764
1765 did_cmd = apply_autocmds_group(event, fname, fname_io, force,
1766 AUGROUP_ALL, buf, NULL);
1767 if (did_cmd
1768#ifdef FEAT_EVAL
1769 && aborting()
1770#endif
1771 )
1772 *retval = FAIL;
1773 return did_cmd;
1774}
1775
1776/*
1777 * Return TRUE when there is a CursorHold autocommand defined.
1778 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001779 static int
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001780has_cursorhold(void)
1781{
Bram Moolenaar24959102022-05-07 20:01:16 +01001782 return (first_autopat[(int)(get_real_state() == MODE_NORMAL_BUSY
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001783 ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
1784}
1785
1786/*
1787 * Return TRUE if the CursorHold event can be triggered.
1788 */
1789 int
1790trigger_cursorhold(void)
1791{
1792 int state;
1793
1794 if (!did_cursorhold
1795 && has_cursorhold()
1796 && reg_recording == 0
1797 && typebuf.tb_len == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001798 && !ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001799 {
1800 state = get_real_state();
Bram Moolenaar24959102022-05-07 20:01:16 +01001801 if (state == MODE_NORMAL_BUSY || (state & MODE_INSERT) != 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001802 return TRUE;
1803 }
1804 return FALSE;
1805}
1806
1807/*
LemonBoy09371822022-04-08 15:18:45 +01001808 * Return TRUE when there is a WinScrolled autocommand defined.
1809 */
1810 int
1811has_winscrolled(void)
1812{
1813 return (first_autopat[(int)EVENT_WINSCROLLED] != NULL);
1814}
1815
1816/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001817 * Return TRUE when there is a CursorMoved autocommand defined.
1818 */
1819 int
1820has_cursormoved(void)
1821{
1822 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1823}
1824
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001825/*
1826 * Return TRUE when there is a CursorMovedI autocommand defined.
1827 */
1828 int
1829has_cursormovedI(void)
1830{
1831 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1832}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001833
1834/*
1835 * Return TRUE when there is a TextChanged autocommand defined.
1836 */
1837 int
1838has_textchanged(void)
1839{
1840 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1841}
1842
1843/*
1844 * Return TRUE when there is a TextChangedI autocommand defined.
1845 */
1846 int
1847has_textchangedI(void)
1848{
1849 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
1850}
1851
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001852/*
1853 * Return TRUE when there is a TextChangedP autocommand defined.
1854 */
1855 int
1856has_textchangedP(void)
1857{
1858 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
1859}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001860
1861/*
1862 * Return TRUE when there is an InsertCharPre autocommand defined.
1863 */
1864 int
1865has_insertcharpre(void)
1866{
1867 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
1868}
1869
1870/*
1871 * Return TRUE when there is an CmdUndefined autocommand defined.
1872 */
1873 int
1874has_cmdundefined(void)
1875{
1876 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
1877}
1878
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001879#if defined(FEAT_EVAL) || defined(PROTO)
1880/*
1881 * Return TRUE when there is a TextYankPost autocommand defined.
1882 */
1883 int
1884has_textyankpost(void)
1885{
1886 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
1887}
1888#endif
1889
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001890#if defined(FEAT_EVAL) || defined(PROTO)
1891/*
1892 * Return TRUE when there is a CompleteChanged autocommand defined.
1893 */
1894 int
1895has_completechanged(void)
1896{
1897 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
1898}
1899#endif
1900
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02001901#if defined(FEAT_EVAL) || defined(PROTO)
1902/*
1903 * Return TRUE when there is a ModeChanged autocommand defined.
1904 */
1905 int
1906has_modechanged(void)
1907{
1908 return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
1909}
1910#endif
1911
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001912/*
1913 * Execute autocommands for "event" and file name "fname".
1914 * Return TRUE if some commands were executed.
1915 */
1916 static int
1917apply_autocmds_group(
1918 event_T event,
1919 char_u *fname, // NULL or empty means use actual file name
1920 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
1921 // use fname
1922 int force, // when TRUE, ignore autocmd_busy
1923 int group, // group ID, or AUGROUP_ALL
1924 buf_T *buf, // buffer for <abuf>
1925 exarg_T *eap UNUSED) // command arguments
1926{
1927 char_u *sfname = NULL; // short file name
1928 char_u *tail;
1929 int save_changed;
1930 buf_T *old_curbuf;
1931 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001932 char_u *save_autocmd_fname;
1933 int save_autocmd_fname_full;
1934 int save_autocmd_bufnr;
1935 char_u *save_autocmd_match;
1936 int save_autocmd_busy;
1937 int save_autocmd_nested;
1938 static int nesting = 0;
LemonBoyeca7c602022-04-14 15:39:43 +01001939 AutoPatCmd_T patcmd;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001940 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001941 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001942#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001943 funccal_entry_T funccal_entry;
1944 char_u *save_cmdarg;
1945 long save_cmdbang;
1946#endif
1947 static int filechangeshell_busy = FALSE;
1948#ifdef FEAT_PROFILE
1949 proftime_T wait_time;
1950#endif
1951 int did_save_redobuff = FALSE;
1952 save_redo_T save_redo;
1953 int save_KeyTyped = KeyTyped;
ichizokc3f91c02021-12-17 09:44:33 +00001954 int save_did_emsg;
Bram Moolenaare31ee862020-01-07 20:59:34 +01001955 ESTACK_CHECK_DECLARATION
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001956
1957 /*
1958 * Quickly return if there are no autocommands for this event or
1959 * autocommands are blocked.
1960 */
1961 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
1962 || autocmd_blocked > 0)
1963 goto BYPASS_AU;
1964
1965 /*
1966 * When autocommands are busy, new autocommands are only executed when
1967 * explicitly enabled with the "nested" flag.
1968 */
1969 if (autocmd_busy && !(force || autocmd_nested))
1970 goto BYPASS_AU;
1971
1972#ifdef FEAT_EVAL
1973 /*
1974 * Quickly return when immediately aborting on error, or when an interrupt
1975 * occurred or an exception was thrown but not caught.
1976 */
1977 if (aborting())
1978 goto BYPASS_AU;
1979#endif
1980
1981 /*
1982 * FileChangedShell never nests, because it can create an endless loop.
1983 */
1984 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
1985 || event == EVENT_FILECHANGEDSHELLPOST))
1986 goto BYPASS_AU;
1987
1988 /*
1989 * Ignore events in 'eventignore'.
1990 */
1991 if (event_ignored(event))
1992 goto BYPASS_AU;
1993
1994 /*
1995 * Allow nesting of autocommands, but restrict the depth, because it's
1996 * possible to create an endless loop.
1997 */
1998 if (nesting == 10)
1999 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00002000 emsg(_(e_autocommand_nesting_too_deep));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002001 goto BYPASS_AU;
2002 }
2003
2004 /*
2005 * Check if these autocommands are disabled. Used when doing ":all" or
2006 * ":ball".
2007 */
2008 if ( (autocmd_no_enter
2009 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
2010 || (autocmd_no_leave
2011 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
2012 goto BYPASS_AU;
2013
2014 /*
2015 * Save the autocmd_* variables and info about the current buffer.
2016 */
2017 save_autocmd_fname = autocmd_fname;
2018 save_autocmd_fname_full = autocmd_fname_full;
2019 save_autocmd_bufnr = autocmd_bufnr;
2020 save_autocmd_match = autocmd_match;
2021 save_autocmd_busy = autocmd_busy;
2022 save_autocmd_nested = autocmd_nested;
2023 save_changed = curbuf->b_changed;
2024 old_curbuf = curbuf;
2025
2026 /*
2027 * Set the file name to be used for <afile>.
2028 * Make a copy to avoid that changing a buffer name or directory makes it
2029 * invalid.
2030 */
2031 if (fname_io == NULL)
2032 {
2033 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002034 || event == EVENT_OPTIONSET
2035 || event == EVENT_MODECHANGED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002036 autocmd_fname = NULL;
2037 else if (fname != NULL && !ends_excmd(*fname))
2038 autocmd_fname = fname;
2039 else if (buf != NULL)
2040 autocmd_fname = buf->b_ffname;
2041 else
2042 autocmd_fname = NULL;
2043 }
2044 else
2045 autocmd_fname = fname_io;
2046 if (autocmd_fname != NULL)
2047 autocmd_fname = vim_strsave(autocmd_fname);
2048 autocmd_fname_full = FALSE; // call FullName_save() later
2049
2050 /*
2051 * Set the buffer number to be used for <abuf>.
2052 */
2053 if (buf == NULL)
2054 autocmd_bufnr = 0;
2055 else
2056 autocmd_bufnr = buf->b_fnum;
2057
2058 /*
2059 * When the file name is NULL or empty, use the file name of buffer "buf".
2060 * Always use the full path of the file name to match with, in case
2061 * "allow_dirs" is set.
2062 */
2063 if (fname == NULL || *fname == NUL)
2064 {
2065 if (buf == NULL)
2066 fname = NULL;
2067 else
2068 {
2069#ifdef FEAT_SYN_HL
2070 if (event == EVENT_SYNTAX)
2071 fname = buf->b_p_syn;
2072 else
2073#endif
2074 if (event == EVENT_FILETYPE)
2075 fname = buf->b_p_ft;
2076 else
2077 {
2078 if (buf->b_sfname != NULL)
2079 sfname = vim_strsave(buf->b_sfname);
2080 fname = buf->b_ffname;
2081 }
2082 }
2083 if (fname == NULL)
2084 fname = (char_u *)"";
2085 fname = vim_strsave(fname); // make a copy, so we can change it
2086 }
2087 else
2088 {
2089 sfname = vim_strsave(fname);
2090 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002091 // ColorScheme, QuickFixCmd*, DirChanged and similar.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002092 if (event == EVENT_FILETYPE
2093 || event == EVENT_SYNTAX
2094 || event == EVENT_CMDLINECHANGED
2095 || event == EVENT_CMDLINEENTER
2096 || event == EVENT_CMDLINELEAVE
2097 || event == EVENT_CMDWINENTER
2098 || event == EVENT_CMDWINLEAVE
2099 || event == EVENT_CMDUNDEFINED
2100 || event == EVENT_FUNCUNDEFINED
2101 || event == EVENT_REMOTEREPLY
2102 || event == EVENT_SPELLFILEMISSING
2103 || event == EVENT_QUICKFIXCMDPRE
2104 || event == EVENT_COLORSCHEME
2105 || event == EVENT_COLORSCHEMEPRE
2106 || event == EVENT_OPTIONSET
2107 || event == EVENT_QUICKFIXCMDPOST
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002108 || event == EVENT_DIRCHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002109 || event == EVENT_DIRCHANGEDPRE
naohiro ono23beefe2021-11-13 12:38:49 +00002110 || event == EVENT_MODECHANGED
zeertzjqc601d982022-10-10 13:46:15 +01002111 || event == EVENT_MENUPOPUP
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002112 || event == EVENT_USER
LemonBoy09371822022-04-08 15:18:45 +01002113 || event == EVENT_WINCLOSED
2114 || event == EVENT_WINSCROLLED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002115 {
2116 fname = vim_strsave(fname);
2117 autocmd_fname_full = TRUE; // don't expand it later
2118 }
2119 else
2120 fname = FullName_save(fname, FALSE);
2121 }
2122 if (fname == NULL) // out of memory
2123 {
2124 vim_free(sfname);
2125 retval = FALSE;
2126 goto BYPASS_AU;
2127 }
2128
2129#ifdef BACKSLASH_IN_FILENAME
2130 /*
2131 * Replace all backslashes with forward slashes. This makes the
2132 * autocommand patterns portable between Unix and MS-DOS.
2133 */
2134 if (sfname != NULL)
2135 forward_slash(sfname);
2136 forward_slash(fname);
2137#endif
2138
2139#ifdef VMS
2140 // remove version for correct match
2141 if (sfname != NULL)
2142 vms_remove_version(sfname);
2143 vms_remove_version(fname);
2144#endif
2145
2146 /*
2147 * Set the name to be used for <amatch>.
2148 */
2149 autocmd_match = fname;
2150
2151
2152 // Don't redraw while doing autocommands.
2153 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002154
2155 // name and lnum are filled in later
2156 estack_push(ETYPE_AUCMD, NULL, 0);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002157 ESTACK_CHECK_SETUP
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002158
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002159 save_current_sctx = current_sctx;
2160
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002161#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002162# ifdef FEAT_PROFILE
2163 if (do_profiling == PROF_YES)
2164 prof_child_enter(&wait_time); // doesn't count for the caller itself
2165# endif
2166
2167 // Don't use local function variables, if called from a function.
2168 save_funccal(&funccal_entry);
2169#endif
2170
2171 /*
2172 * When starting to execute autocommands, save the search patterns.
2173 */
2174 if (!autocmd_busy)
2175 {
2176 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002177 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002178 {
2179 saveRedobuff(&save_redo);
2180 did_save_redobuff = TRUE;
2181 }
2182 did_filetype = keep_filetype;
2183 }
2184
2185 /*
2186 * Note that we are applying autocmds. Some commands need to know.
2187 */
2188 autocmd_busy = TRUE;
2189 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2190 ++nesting; // see matching decrement below
2191
2192 // Remember that FileType was triggered. Used for did_filetype().
2193 if (event == EVENT_FILETYPE)
2194 did_filetype = TRUE;
2195
2196 tail = gettail(fname);
2197
2198 // Find first autocommand that matches
LemonBoyeca7c602022-04-14 15:39:43 +01002199 CLEAR_FIELD(patcmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002200 patcmd.curpat = first_autopat[(int)event];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002201 patcmd.group = group;
2202 patcmd.fname = fname;
2203 patcmd.sfname = sfname;
2204 patcmd.tail = tail;
2205 patcmd.event = event;
2206 patcmd.arg_bufnr = autocmd_bufnr;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002207 auto_next_pat(&patcmd, FALSE);
2208
2209 // found one, start executing the autocommands
2210 if (patcmd.curpat != NULL)
2211 {
2212 // add to active_apc_list
2213 patcmd.next = active_apc_list;
2214 active_apc_list = &patcmd;
2215
2216#ifdef FEAT_EVAL
2217 // set v:cmdarg (only when there is a matching pattern)
2218 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2219 if (eap != NULL)
2220 {
2221 save_cmdarg = set_cmdarg(eap, NULL);
2222 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2223 }
2224 else
2225 save_cmdarg = NULL; // avoid gcc warning
2226#endif
2227 retval = TRUE;
2228 // mark the last pattern, to avoid an endless loop when more patterns
2229 // are added when executing autocommands
2230 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2231 ap->last = FALSE;
2232 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002233
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002234 // Make sure cursor and topline are valid. The first time the current
2235 // values are saved, restored by reset_lnums(). When nested only the
2236 // values are corrected when needed.
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002237 if (nesting == 1)
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002238 check_lnums(TRUE);
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002239 else
2240 check_lnums_nested(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002241
ichizokc3f91c02021-12-17 09:44:33 +00002242 save_did_emsg = did_emsg;
2243
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002244 do_cmdline(NULL, getnextac, (void *)&patcmd,
2245 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002246
ichizokc3f91c02021-12-17 09:44:33 +00002247 did_emsg += save_did_emsg;
2248
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002249 if (nesting == 1)
2250 // restore cursor and topline, unless they were changed
2251 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002252
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002253#ifdef FEAT_EVAL
2254 if (eap != NULL)
2255 {
2256 (void)set_cmdarg(NULL, save_cmdarg);
2257 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2258 }
2259#endif
2260 // delete from active_apc_list
2261 if (active_apc_list == &patcmd) // just in case
2262 active_apc_list = patcmd.next;
2263 }
2264
2265 --RedrawingDisabled;
2266 autocmd_busy = save_autocmd_busy;
2267 filechangeshell_busy = FALSE;
2268 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002269 vim_free(SOURCING_NAME);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002270 ESTACK_CHECK_NOW
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002271 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002272 vim_free(autocmd_fname);
2273 autocmd_fname = save_autocmd_fname;
2274 autocmd_fname_full = save_autocmd_fname_full;
2275 autocmd_bufnr = save_autocmd_bufnr;
2276 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002277 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002278#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002279 restore_funccal();
2280# ifdef FEAT_PROFILE
2281 if (do_profiling == PROF_YES)
2282 prof_child_exit(&wait_time);
2283# endif
2284#endif
2285 KeyTyped = save_KeyTyped;
2286 vim_free(fname);
2287 vim_free(sfname);
2288 --nesting; // see matching increment above
2289
2290 /*
2291 * When stopping to execute autocommands, restore the search patterns and
2292 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2293 * free any windows in the au_pending_free_win list.
2294 */
2295 if (!autocmd_busy)
2296 {
2297 restore_search_patterns();
2298 if (did_save_redobuff)
2299 restoreRedobuff(&save_redo);
2300 did_filetype = FALSE;
2301 while (au_pending_free_buf != NULL)
2302 {
2303 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002304
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002305 vim_free(au_pending_free_buf);
2306 au_pending_free_buf = b;
2307 }
2308 while (au_pending_free_win != NULL)
2309 {
2310 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002311
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002312 vim_free(au_pending_free_win);
2313 au_pending_free_win = w;
2314 }
2315 }
2316
2317 /*
2318 * Some events don't set or reset the Changed flag.
2319 * Check if still in the same buffer!
2320 */
2321 if (curbuf == old_curbuf
2322 && (event == EVENT_BUFREADPOST
2323 || event == EVENT_BUFWRITEPOST
2324 || event == EVENT_FILEAPPENDPOST
2325 || event == EVENT_VIMLEAVE
2326 || event == EVENT_VIMLEAVEPRE))
2327 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002328 if (curbuf->b_changed != save_changed)
2329 need_maketitle = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002330 curbuf->b_changed = save_changed;
2331 }
2332
2333 au_cleanup(); // may really delete removed patterns/commands now
2334
2335BYPASS_AU:
2336 // When wiping out a buffer make sure all its buffer-local autocommands
2337 // are deleted.
2338 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2339 aubuflocal_remove(buf);
2340
2341 if (retval == OK && event == EVENT_FILETYPE)
2342 au_did_filetype = TRUE;
2343
2344 return retval;
2345}
2346
2347# ifdef FEAT_EVAL
2348static char_u *old_termresponse = NULL;
2349# endif
2350
2351/*
2352 * Block triggering autocommands until unblock_autocmd() is called.
2353 * Can be used recursively, so long as it's symmetric.
2354 */
2355 void
2356block_autocmds(void)
2357{
2358# ifdef FEAT_EVAL
2359 // Remember the value of v:termresponse.
2360 if (autocmd_blocked == 0)
2361 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
2362# endif
2363 ++autocmd_blocked;
2364}
2365
2366 void
2367unblock_autocmds(void)
2368{
2369 --autocmd_blocked;
2370
2371# ifdef FEAT_EVAL
2372 // When v:termresponse was set while autocommands were blocked, trigger
2373 // the autocommands now. Esp. useful when executing a shell command
2374 // during startup (vimdiff).
2375 if (autocmd_blocked == 0
2376 && get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2377 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2378# endif
2379}
2380
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002381 int
2382is_autocmd_blocked(void)
2383{
2384 return autocmd_blocked != 0;
2385}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002386
2387/*
2388 * Find next autocommand pattern that matches.
2389 */
2390 static void
2391auto_next_pat(
LemonBoyeca7c602022-04-14 15:39:43 +01002392 AutoPatCmd_T *apc,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002393 int stop_at_last) // stop when 'last' flag is set
2394{
2395 AutoPat *ap;
2396 AutoCmd *cp;
2397 char_u *name;
2398 char *s;
LemonBoyeca7c602022-04-14 15:39:43 +01002399 estack_T *entry;
2400 char_u *namep;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002401
LemonBoyeca7c602022-04-14 15:39:43 +01002402 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
2403
2404 // Clear the exestack entry for this ETYPE_AUCMD entry.
2405 VIM_CLEAR(entry->es_name);
2406 entry->es_info.aucmd = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002407
2408 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2409 {
2410 apc->curpat = NULL;
2411
2412 // Only use a pattern when it has not been removed, has commands and
2413 // the group matches. For buffer-local autocommands only check the
2414 // buffer number.
2415 if (ap->pat != NULL && ap->cmds != NULL
2416 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2417 {
2418 // execution-condition
2419 if (ap->buflocal_nr == 0
2420 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2421 apc->sfname, apc->tail, ap->allow_dirs))
2422 : ap->buflocal_nr == apc->arg_bufnr)
2423 {
2424 name = event_nr2name(apc->event);
2425 s = _("%s Autocommands for \"%s\"");
LemonBoyeca7c602022-04-14 15:39:43 +01002426 namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1);
2427 if (namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002428 {
LemonBoyeca7c602022-04-14 15:39:43 +01002429 sprintf((char *)namep, s, (char *)name, (char *)ap->pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002430 if (p_verbose >= 8)
2431 {
2432 verbose_enter();
LemonBoyeca7c602022-04-14 15:39:43 +01002433 smsg(_("Executing %s"), namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002434 verbose_leave();
2435 }
2436 }
2437
LemonBoyeca7c602022-04-14 15:39:43 +01002438 // Update the exestack entry for this autocmd.
2439 entry->es_name = namep;
2440 entry->es_info.aucmd = apc;
2441
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002442 apc->curpat = ap;
2443 apc->nextcmd = ap->cmds;
2444 // mark last command
2445 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2446 cp->last = FALSE;
2447 cp->last = TRUE;
2448 }
2449 line_breakcheck();
2450 if (apc->curpat != NULL) // found a match
2451 break;
2452 }
2453 if (stop_at_last && ap->last)
2454 break;
2455 }
2456}
2457
2458/*
LemonBoyeca7c602022-04-14 15:39:43 +01002459 * Get the script context where autocommand "acp" is defined.
2460 */
2461 sctx_T *
2462acp_script_ctx(AutoPatCmd_T *acp)
2463{
2464 return &acp->script_ctx;
2465}
2466
2467/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002468 * Get next autocommand command.
2469 * Called by do_cmdline() to get the next line for ":if".
2470 * Returns allocated string, or NULL for end of autocommands.
2471 */
2472 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002473getnextac(
2474 int c UNUSED,
2475 void *cookie,
2476 int indent UNUSED,
2477 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002478{
LemonBoyeca7c602022-04-14 15:39:43 +01002479 AutoPatCmd_T *acp = (AutoPatCmd_T *)cookie;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002480 char_u *retval;
2481 AutoCmd *ac;
2482
2483 // Can be called again after returning the last line.
2484 if (acp->curpat == NULL)
2485 return NULL;
2486
2487 // repeat until we find an autocommand to execute
2488 for (;;)
2489 {
2490 // skip removed commands
2491 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2492 if (acp->nextcmd->last)
2493 acp->nextcmd = NULL;
2494 else
2495 acp->nextcmd = acp->nextcmd->next;
2496
2497 if (acp->nextcmd != NULL)
2498 break;
2499
2500 // at end of commands, find next pattern that matches
2501 if (acp->curpat->last)
2502 acp->curpat = NULL;
2503 else
2504 acp->curpat = acp->curpat->next;
2505 if (acp->curpat != NULL)
2506 auto_next_pat(acp, TRUE);
2507 if (acp->curpat == NULL)
2508 return NULL;
2509 }
2510
2511 ac = acp->nextcmd;
2512
2513 if (p_verbose >= 9)
2514 {
2515 verbose_enter_scroll();
2516 smsg(_("autocommand %s"), ac->cmd);
2517 msg_puts("\n"); // don't overwrite this either
2518 verbose_leave_scroll();
2519 }
2520 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002521 // Remove one-shot ("once") autocmd in anticipation of its execution.
2522 if (ac->once)
2523 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002524 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002525 current_sctx = ac->script_ctx;
LemonBoyeca7c602022-04-14 15:39:43 +01002526 acp->script_ctx = current_sctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002527 if (ac->last)
2528 acp->nextcmd = NULL;
2529 else
2530 acp->nextcmd = ac->next;
2531 return retval;
2532}
2533
2534/*
2535 * Return TRUE if there is a matching autocommand for "fname".
2536 * To account for buffer-local autocommands, function needs to know
2537 * in which buffer the file will be opened.
2538 */
2539 int
2540has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2541{
2542 AutoPat *ap;
2543 char_u *fname;
2544 char_u *tail = gettail(sfname);
2545 int retval = FALSE;
2546
2547 fname = FullName_save(sfname, FALSE);
2548 if (fname == NULL)
2549 return FALSE;
2550
2551#ifdef BACKSLASH_IN_FILENAME
2552 /*
2553 * Replace all backslashes with forward slashes. This makes the
2554 * autocommand patterns portable between Unix and MS-DOS.
2555 */
2556 sfname = vim_strsave(sfname);
2557 if (sfname != NULL)
2558 forward_slash(sfname);
2559 forward_slash(fname);
2560#endif
2561
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002562 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002563 if (ap->pat != NULL && ap->cmds != NULL
2564 && (ap->buflocal_nr == 0
2565 ? match_file_pat(NULL, &ap->reg_prog,
2566 fname, sfname, tail, ap->allow_dirs)
2567 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2568 ))
2569 {
2570 retval = TRUE;
2571 break;
2572 }
2573
2574 vim_free(fname);
2575#ifdef BACKSLASH_IN_FILENAME
2576 vim_free(sfname);
2577#endif
2578
2579 return retval;
2580}
2581
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002582/*
2583 * Function given to ExpandGeneric() to obtain the list of autocommand group
2584 * names.
2585 */
2586 char_u *
2587get_augroup_name(expand_T *xp UNUSED, int idx)
2588{
2589 if (idx == augroups.ga_len) // add "END" add the end
2590 return (char_u *)"END";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002591 if (idx < 0 || idx >= augroups.ga_len) // end of list
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002592 return NULL;
2593 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2594 // skip deleted entries
2595 return (char_u *)"";
2596 return AUGROUP_NAME(idx); // return a name
2597}
2598
2599static int include_groups = FALSE;
2600
2601 char_u *
2602set_context_in_autocmd(
2603 expand_T *xp,
2604 char_u *arg,
2605 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2606{
2607 char_u *p;
2608 int group;
2609
2610 // check for a group name, skip it if present
2611 include_groups = FALSE;
2612 p = arg;
2613 group = au_get_grouparg(&arg);
2614 if (group == AUGROUP_ERROR)
2615 return NULL;
2616 // If there only is a group name that's what we expand.
2617 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2618 {
2619 arg = p;
2620 group = AUGROUP_ALL;
2621 }
2622
2623 // skip over event name
2624 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2625 if (*p == ',')
2626 arg = p + 1;
2627 if (*p == NUL)
2628 {
2629 if (group == AUGROUP_ALL)
2630 include_groups = TRUE;
2631 xp->xp_context = EXPAND_EVENTS; // expand event name
2632 xp->xp_pattern = arg;
2633 return NULL;
2634 }
2635
2636 // skip over pattern
2637 arg = skipwhite(p);
2638 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2639 arg++;
2640 if (*arg)
2641 return arg; // expand (next) command
2642
2643 if (doautocmd)
2644 xp->xp_context = EXPAND_FILES; // expand file names
2645 else
2646 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2647 return NULL;
2648}
2649
2650/*
2651 * Function given to ExpandGeneric() to obtain the list of event names.
2652 */
2653 char_u *
2654get_event_name(expand_T *xp UNUSED, int idx)
2655{
2656 if (idx < augroups.ga_len) // First list group names, if wanted
2657 {
2658 if (!include_groups || AUGROUP_NAME(idx) == NULL
2659 || AUGROUP_NAME(idx) == get_deleted_augroup())
2660 return (char_u *)""; // skip deleted entries
2661 return AUGROUP_NAME(idx); // return a name
2662 }
2663 return (char_u *)event_names[idx - augroups.ga_len].name;
2664}
2665
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002666
2667#if defined(FEAT_EVAL) || defined(PROTO)
2668/*
2669 * Return TRUE if autocmd is supported.
2670 */
2671 int
2672autocmd_supported(char_u *name)
2673{
2674 char_u *p;
2675
2676 return (event_name2nr(name, &p) != NUM_EVENTS);
2677}
2678
2679/*
2680 * Return TRUE if an autocommand is defined for a group, event and
2681 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2682 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2683 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2684 * Used for:
2685 * exists("#Group") or
2686 * exists("#Group#Event") or
2687 * exists("#Group#Event#pat") or
2688 * exists("#Event") or
2689 * exists("#Event#pat")
2690 */
2691 int
2692au_exists(char_u *arg)
2693{
2694 char_u *arg_save;
2695 char_u *pattern = NULL;
2696 char_u *event_name;
2697 char_u *p;
2698 event_T event;
2699 AutoPat *ap;
2700 buf_T *buflocal_buf = NULL;
2701 int group;
2702 int retval = FALSE;
2703
2704 // Make a copy so that we can change the '#' chars to a NUL.
2705 arg_save = vim_strsave(arg);
2706 if (arg_save == NULL)
2707 return FALSE;
2708 p = vim_strchr(arg_save, '#');
2709 if (p != NULL)
2710 *p++ = NUL;
2711
2712 // First, look for an autocmd group name
2713 group = au_find_group(arg_save);
2714 if (group == AUGROUP_ERROR)
2715 {
2716 // Didn't match a group name, assume the first argument is an event.
2717 group = AUGROUP_ALL;
2718 event_name = arg_save;
2719 }
2720 else
2721 {
2722 if (p == NULL)
2723 {
2724 // "Group": group name is present and it's recognized
2725 retval = TRUE;
2726 goto theend;
2727 }
2728
2729 // Must be "Group#Event" or "Group#Event#pat".
2730 event_name = p;
2731 p = vim_strchr(event_name, '#');
2732 if (p != NULL)
2733 *p++ = NUL; // "Group#Event#pat"
2734 }
2735
2736 pattern = p; // "pattern" is NULL when there is no pattern
2737
2738 // find the index (enum) for the event name
2739 event = event_name2nr(event_name, &p);
2740
2741 // return FALSE if the event name is not recognized
2742 if (event == NUM_EVENTS)
2743 goto theend;
2744
2745 // Find the first autocommand for this event.
2746 // If there isn't any, return FALSE;
2747 // If there is one and no pattern given, return TRUE;
2748 ap = first_autopat[(int)event];
2749 if (ap == NULL)
2750 goto theend;
2751
2752 // if pattern is "<buffer>", special handling is needed which uses curbuf
2753 // for pattern "<buffer=N>, fnamecmp() will work fine
2754 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2755 buflocal_buf = curbuf;
2756
2757 // Check if there is an autocommand with the given pattern.
2758 for ( ; ap != NULL; ap = ap->next)
2759 // only use a pattern when it has not been removed and has commands.
2760 // For buffer-local autocommands, fnamecmp() works fine.
2761 if (ap->pat != NULL && ap->cmds != NULL
2762 && (group == AUGROUP_ALL || ap->group == group)
2763 && (pattern == NULL
2764 || (buflocal_buf == NULL
2765 ? fnamecmp(ap->pat, pattern) == 0
2766 : ap->buflocal_nr == buflocal_buf->b_fnum)))
2767 {
2768 retval = TRUE;
2769 break;
2770 }
2771
2772theend:
2773 vim_free(arg_save);
2774 return retval;
2775}
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002776
2777/*
2778 * autocmd_add() and autocmd_delete() functions
2779 */
2780 static void
2781autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
2782{
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002783 list_T *aucmd_list;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002784 listitem_T *li;
2785 dict_T *event_dict;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002786 dictitem_T *di;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002787 char_u *event_name = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002788 list_T *event_list;
2789 listitem_T *eli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002790 event_T event;
2791 char_u *group_name = NULL;
2792 int group;
2793 char_u *pat = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002794 list_T *pat_list;
2795 listitem_T *pli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002796 char_u *cmd = NULL;
2797 char_u *end;
2798 int once;
2799 int nested;
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01002800 int replace; // replace the cmd for a group/event
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002801 int retval = VVAL_TRUE;
2802 int save_augroup = current_augroup;
2803
2804 rettv->v_type = VAR_BOOL;
2805 rettv->vval.v_number = VVAL_FALSE;
2806
2807 if (check_for_list_arg(argvars, 0) == FAIL)
2808 return;
2809
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002810 aucmd_list = argvars[0].vval.v_list;
2811 if (aucmd_list == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002812 return;
2813
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002814 FOR_ALL_LIST_ITEMS(aucmd_list, li)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002815 {
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002816 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002817 VIM_CLEAR(cmd);
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002818 event_name = NULL;
2819 event_list = NULL;
2820 pat = NULL;
2821 pat_list = NULL;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002822
2823 if (li->li_tv.v_type != VAR_DICT)
2824 continue;
2825
2826 event_dict = li->li_tv.vval.v_dict;
2827 if (event_dict == NULL)
2828 continue;
2829
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002830 di = dict_find(event_dict, (char_u *)"event", -1);
2831 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002832 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002833 if (di->di_tv.v_type == VAR_STRING)
2834 {
2835 event_name = di->di_tv.vval.v_string;
2836 if (event_name == NULL)
2837 {
2838 emsg(_(e_string_required));
2839 continue;
2840 }
2841 }
2842 else if (di->di_tv.v_type == VAR_LIST)
2843 {
2844 event_list = di->di_tv.vval.v_list;
2845 if (event_list == NULL)
2846 {
2847 emsg(_(e_list_required));
2848 continue;
2849 }
2850 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002851 else
2852 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002853 emsg(_(e_string_or_list_expected));
2854 continue;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002855 }
2856 }
2857
Bram Moolenaard61efa52022-07-23 09:52:04 +01002858 group_name = dict_get_string(event_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002859 if (group_name == NULL || *group_name == NUL)
2860 // if the autocmd group name is not specified, then use the current
2861 // autocmd group
2862 group = current_augroup;
2863 else
2864 {
2865 group = au_find_group(group_name);
2866 if (group == AUGROUP_ERROR)
2867 {
2868 if (delete)
2869 {
2870 semsg(_(e_no_such_group_str), group_name);
2871 retval = VVAL_FALSE;
2872 break;
2873 }
2874 // group is not found, create it now
2875 group = au_new_group(group_name);
2876 if (group == AUGROUP_ERROR)
2877 {
2878 semsg(_(e_no_such_group_str), group_name);
2879 retval = VVAL_FALSE;
2880 break;
2881 }
2882
2883 current_augroup = group;
2884 }
2885 }
2886
2887 // if a buffer number is specified, then generate a pattern of the form
2888 // "<buffer=n>. Otherwise, use the pattern supplied by the user.
2889 if (dict_has_key(event_dict, "bufnr"))
2890 {
2891 varnumber_T bnum;
2892
Bram Moolenaard61efa52022-07-23 09:52:04 +01002893 bnum = dict_get_number_def(event_dict, "bufnr", -1);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002894 if (bnum == -1)
2895 continue;
2896
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002897 vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
2898 pat = IObuff;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002899 }
2900 else
2901 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002902 di = dict_find(event_dict, (char_u *)"pattern", -1);
2903 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002904 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002905 if (di->di_tv.v_type == VAR_STRING)
2906 {
2907 pat = di->di_tv.vval.v_string;
2908 if (pat == NULL)
2909 {
2910 emsg(_(e_string_required));
2911 continue;
2912 }
2913 }
2914 else if (di->di_tv.v_type == VAR_LIST)
2915 {
2916 pat_list = di->di_tv.vval.v_list;
2917 if (pat_list == NULL)
2918 {
2919 emsg(_(e_list_required));
2920 continue;
2921 }
2922 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002923 else
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002924 {
2925 emsg(_(e_string_or_list_expected));
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002926 continue;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002927 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002928 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002929 else if (delete)
2930 pat = (char_u *)"";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002931 }
2932
Bram Moolenaard61efa52022-07-23 09:52:04 +01002933 once = dict_get_bool(event_dict, "once", FALSE);
2934 nested = dict_get_bool(event_dict, "nested", FALSE);
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01002935 // if 'replace' is true, then remove all the commands associated with
2936 // this autocmd event/group and add the new command.
Bram Moolenaard61efa52022-07-23 09:52:04 +01002937 replace = dict_get_bool(event_dict, "replace", FALSE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002938
Bram Moolenaard61efa52022-07-23 09:52:04 +01002939 cmd = dict_get_string(event_dict, "cmd", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002940 if (cmd == NULL)
2941 {
2942 if (delete)
2943 cmd = vim_strsave((char_u *)"");
2944 else
2945 continue;
2946 }
2947
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002948 if (delete && (event_name == NULL
2949 || (event_name[0] == '*' && event_name[1] == NUL)))
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002950 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002951 // if the event name is not specified or '*', delete all the events
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002952 for (event = (event_T)0; (int)event < NUM_EVENTS;
2953 event = (event_T)((int)event + 1))
2954 {
2955 if (do_autocmd_event(event, pat, once, nested, cmd, delete,
2956 group, 0) == FAIL)
2957 {
2958 retval = VVAL_FALSE;
2959 break;
2960 }
2961 }
2962 }
2963 else
2964 {
Bram Moolenaar968443e2022-05-27 21:16:34 +01002965 char_u *p = NULL;
2966
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002967 eli = NULL;
2968 end = NULL;
2969 while (TRUE)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002970 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002971 if (event_list != NULL)
2972 {
2973 if (eli == NULL)
2974 eli = event_list->lv_first;
2975 else
2976 eli = eli->li_next;
2977 if (eli == NULL)
2978 break;
2979 if (eli->li_tv.v_type != VAR_STRING
Bram Moolenaar882476a2022-06-01 16:02:38 +01002980 || (p = eli->li_tv.vval.v_string) == NULL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002981 {
2982 emsg(_(e_string_required));
Bram Moolenaar882476a2022-06-01 16:02:38 +01002983 break;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002984 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002985 }
2986 else
2987 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01002988 if (p == NULL)
2989 p = event_name;
2990 if (p == NULL || *p == NUL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002991 break;
2992 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002993
2994 event = event_name2nr(p, &end);
2995 if (event == NUM_EVENTS || *end != NUL)
2996 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01002997 // this also catches something following a valid event name
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002998 semsg(_(e_no_such_event_str), p);
2999 retval = VVAL_FALSE;
3000 break;
3001 }
3002 if (pat != NULL)
3003 {
3004 if (do_autocmd_event(event, pat, once, nested, cmd,
3005 delete | replace, group, 0) == FAIL)
3006 {
3007 retval = VVAL_FALSE;
3008 break;
3009 }
3010 }
3011 else if (pat_list != NULL)
3012 {
3013 FOR_ALL_LIST_ITEMS(pat_list, pli)
3014 {
3015 if (pli->li_tv.v_type != VAR_STRING
3016 || pli->li_tv.vval.v_string == NULL)
3017 {
3018 emsg(_(e_string_required));
3019 continue;
3020 }
3021 if (do_autocmd_event(event,
3022 pli->li_tv.vval.v_string, once, nested,
3023 cmd, delete | replace, group, 0) ==
3024 FAIL)
3025 {
3026 retval = VVAL_FALSE;
3027 break;
3028 }
3029 }
3030 if (retval == VVAL_FALSE)
3031 break;
3032 }
3033 if (event_name != NULL)
3034 p = end;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003035 }
3036 }
3037
3038 // if only the autocmd group name is specified for delete and the
3039 // autocmd event, pattern and cmd are not specified, then delete the
3040 // autocmd group.
3041 if (delete && group_name != NULL &&
3042 (event_name == NULL || event_name[0] == NUL)
3043 && (pat == NULL || pat[0] == NUL)
3044 && (cmd == NULL || cmd[0] == NUL))
3045 au_del_group(group_name);
3046 }
3047
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003048 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003049 VIM_CLEAR(cmd);
3050
3051 current_augroup = save_augroup;
3052 rettv->vval.v_number = retval;
3053}
3054
3055/*
3056 * autocmd_add() function
3057 */
3058 void
3059f_autocmd_add(typval_T *argvars, typval_T *rettv)
3060{
3061 autocmd_add_or_delete(argvars, rettv, FALSE);
3062}
3063
3064/*
3065 * autocmd_delete() function
3066 */
3067 void
3068f_autocmd_delete(typval_T *argvars, typval_T *rettv)
3069{
3070 autocmd_add_or_delete(argvars, rettv, TRUE);
3071}
3072
3073/*
3074 * autocmd_get() function
3075 * Returns a List of autocmds.
3076 */
3077 void
3078f_autocmd_get(typval_T *argvars, typval_T *rettv)
3079{
3080 event_T event_arg = NUM_EVENTS;
3081 event_T event;
3082 AutoPat *ap;
3083 AutoCmd *ac;
3084 list_T *event_list;
3085 dict_T *event_dict;
3086 char_u *event_name = NULL;
3087 char_u *pat = NULL;
3088 char_u *name = NULL;
3089 int group = AUGROUP_ALL;
3090
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01003091 if (rettv_list_alloc(rettv) == FAIL)
3092 return;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003093 if (check_for_opt_dict_arg(argvars, 0) == FAIL)
3094 return;
3095
3096 if (argvars[0].v_type == VAR_DICT)
3097 {
3098 // return only the autocmds in the specified group
3099 if (dict_has_key(argvars[0].vval.v_dict, "group"))
3100 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003101 name = dict_get_string(argvars[0].vval.v_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003102 if (name == NULL)
3103 return;
3104
3105 if (*name == NUL)
3106 group = AUGROUP_DEFAULT;
3107 else
3108 {
3109 group = au_find_group(name);
3110 if (group == AUGROUP_ERROR)
3111 {
3112 semsg(_(e_no_such_group_str), name);
3113 vim_free(name);
3114 return;
3115 }
3116 }
3117 vim_free(name);
3118 }
3119
3120 // return only the autocmds for the specified event
3121 if (dict_has_key(argvars[0].vval.v_dict, "event"))
3122 {
3123 int i;
3124
Bram Moolenaard61efa52022-07-23 09:52:04 +01003125 name = dict_get_string(argvars[0].vval.v_dict, "event", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003126 if (name == NULL)
3127 return;
3128
3129 if (name[0] == '*' && name[1] == NUL)
3130 event_arg = NUM_EVENTS;
3131 else
3132 {
3133 for (i = 0; event_names[i].name != NULL; i++)
3134 if (STRICMP(event_names[i].name, name) == 0)
3135 break;
3136 if (event_names[i].name == NULL)
3137 {
3138 semsg(_(e_no_such_event_str), name);
3139 vim_free(name);
3140 return;
3141 }
3142 event_arg = event_names[i].event;
3143 }
3144 vim_free(name);
3145 }
3146
3147 // return only the autocmds for the specified pattern
3148 if (dict_has_key(argvars[0].vval.v_dict, "pattern"))
3149 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003150 pat = dict_get_string(argvars[0].vval.v_dict, "pattern", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003151 if (pat == NULL)
3152 return;
3153 }
3154 }
3155
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003156 event_list = rettv->vval.v_list;
3157
3158 // iterate through all the autocmd events
3159 for (event = (event_T)0; (int)event < NUM_EVENTS;
3160 event = (event_T)((int)event + 1))
3161 {
3162 if (event_arg != NUM_EVENTS && event != event_arg)
3163 continue;
3164
3165 event_name = event_nr2name(event);
3166
3167 // iterate through all the patterns for this autocmd event
3168 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
3169 {
3170 char_u *group_name;
3171
3172 if (group != AUGROUP_ALL && group != ap->group)
3173 continue;
3174
3175 if (pat != NULL && STRCMP(pat, ap->pat) != 0)
3176 continue;
3177
3178 group_name = get_augroup_name(NULL, ap->group);
3179
3180 // iterate through all the commands for this pattern and add one
3181 // item for each cmd.
3182 for (ac = ap->cmds; ac != NULL; ac = ac->next)
3183 {
3184 event_dict = dict_alloc();
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003185 if (event_dict == NULL
3186 || list_append_dict(event_list, event_dict) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003187 return;
3188
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003189 if (dict_add_string(event_dict, "event", event_name) == FAIL
3190 || dict_add_string(event_dict, "group",
3191 group_name == NULL ? (char_u *)""
3192 : group_name) == FAIL
3193 || (ap->buflocal_nr != 0
3194 && (dict_add_number(event_dict, "bufnr",
3195 ap->buflocal_nr) == FAIL))
3196 || dict_add_string(event_dict, "pattern",
3197 ap->pat) == FAIL
3198 || dict_add_string(event_dict, "cmd", ac->cmd) == FAIL
3199 || dict_add_bool(event_dict, "once", ac->once) == FAIL
3200 || dict_add_bool(event_dict, "nested",
3201 ac->nested) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003202 return;
3203 }
3204 }
3205 }
3206
3207 vim_free(pat);
3208}
3209
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01003210#endif