blob: 11dc707d74d2775e5a4fee16d83bf10a57f8998e [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},
Bram Moolenaar35fc61c2022-11-22 12:40:50 +0000194 {"WinResized", EVENT_WINRESIZED},
LemonBoy09371822022-04-08 15:18:45 +0100195 {"WinScrolled", EVENT_WINSCROLLED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100196 {"VimResized", EVENT_VIMRESIZED},
197 {"TextYankPost", EVENT_TEXTYANKPOST},
Bram Moolenaar100118c2020-12-11 19:30:34 +0100198 {"VimSuspend", EVENT_VIMSUSPEND},
199 {"VimResume", EVENT_VIMRESUME},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100200 {NULL, (event_T)0}
201};
202
203static AutoPat *first_autopat[NUM_EVENTS] =
204{
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 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
211};
212
213static AutoPat *last_autopat[NUM_EVENTS] =
214{
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 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
221};
222
kylo252ae6f1d82022-02-16 19:24:07 +0000223#define AUGROUP_DEFAULT (-1) // default autocmd group
224#define AUGROUP_ERROR (-2) // erroneous autocmd group
225#define AUGROUP_ALL (-3) // all autocmd groups
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100226
227/*
228 * struct used to keep status while executing autocommands for an event.
229 */
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100230struct AutoPatCmd_S
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100231{
232 AutoPat *curpat; // next AutoPat to examine
233 AutoCmd *nextcmd; // next AutoCmd to execute
234 int group; // group being used
235 char_u *fname; // fname to match with
236 char_u *sfname; // sfname to match with
237 char_u *tail; // tail of fname
238 event_T event; // current event
LemonBoyeca7c602022-04-14 15:39:43 +0100239 sctx_T script_ctx; // script context where it is defined
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100240 int arg_bufnr; // Initially equal to <abuf>, set to zero when
241 // buf is deleted.
LemonBoyeca7c602022-04-14 15:39:43 +0100242 AutoPatCmd_T *next; // chain of active apc-s for auto-invalidation
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100243};
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100244
LemonBoyeca7c602022-04-14 15:39:43 +0100245static AutoPatCmd_T *active_apc_list = NULL; // stack of active autocommands
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100246
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200247// Macro to loop over all the patterns for an autocmd event
248#define FOR_ALL_AUTOCMD_PATTERNS(event, ap) \
249 for ((ap) = first_autopat[(int)(event)]; (ap) != NULL; (ap) = (ap)->next)
250
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100251/*
252 * augroups stores a list of autocmd group names.
253 */
254static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
255#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
Bram Moolenaarc667da52019-11-30 20:52:27 +0100256// use get_deleted_augroup() to get this
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100257static char_u *deleted_augroup = NULL;
258
259/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100260 * The ID of the current group. Group 0 is the default one.
261 */
262static int current_augroup = AUGROUP_DEFAULT;
263
Bram Moolenaarc667da52019-11-30 20:52:27 +0100264static int au_need_clean = FALSE; // need to delete marked patterns
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100265
266static char_u *event_nr2name(event_T event);
267static int au_get_grouparg(char_u **argp);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200268static 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 +0100269static 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 +0100270static void auto_next_pat(AutoPatCmd_T *apc, int stop_at_last);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100271static int au_find_group(char_u *name);
272
273static event_T last_event;
274static int last_group;
Bram Moolenaarc667da52019-11-30 20:52:27 +0100275static int autocmd_blocked = 0; // block all autocmds
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100276
277 static char_u *
278get_deleted_augroup(void)
279{
280 if (deleted_augroup == NULL)
281 deleted_augroup = (char_u *)_("--Deleted--");
282 return deleted_augroup;
283}
284
285/*
286 * Show the autocommands for one AutoPat.
287 */
288 static void
289show_autocmd(AutoPat *ap, event_T event)
290{
291 AutoCmd *ac;
292
293 // Check for "got_int" (here and at various places below), which is set
294 // when "q" has been hit for the "--more--" prompt
295 if (got_int)
296 return;
297 if (ap->pat == NULL) // pattern has been removed
298 return;
299
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000300 // Make sure no info referenced by "ap" is cleared, e.g. when a timer
301 // clears an augroup. Jump to "theend" after this!
302 // "ap->pat" may be cleared anyway.
303 ++autocmd_busy;
304
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100305 msg_putchar('\n');
306 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000307 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100308 if (event != last_event || ap->group != last_group)
309 {
310 if (ap->group != AUGROUP_DEFAULT)
311 {
312 if (AUGROUP_NAME(ap->group) == NULL)
313 msg_puts_attr((char *)get_deleted_augroup(), HL_ATTR(HLF_E));
314 else
315 msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
316 msg_puts(" ");
317 }
318 msg_puts_attr((char *)event_nr2name(event), HL_ATTR(HLF_T));
319 last_event = event;
320 last_group = ap->group;
321 msg_putchar('\n');
322 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000323 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100324 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000325
326 if (ap->pat == NULL)
327 goto theend; // timer might have cleared the pattern or group
328
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100329 msg_col = 4;
330 msg_outtrans(ap->pat);
331
332 for (ac = ap->cmds; ac != NULL; ac = ac->next)
333 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100334 if (ac->cmd == NULL) // skip removed commands
335 continue;
336
337 if (msg_col >= 14)
338 msg_putchar('\n');
339 msg_col = 14;
340 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000341 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100342 msg_outtrans(ac->cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100343#ifdef FEAT_EVAL
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100344 if (p_verbose > 0)
345 last_set_msg(ac->script_ctx);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100346#endif
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100347 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000348 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100349 if (ac->next != NULL)
350 {
351 msg_putchar('\n');
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100352 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000353 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100354 }
355 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000356
357theend:
358 --autocmd_busy;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100359}
360
361/*
362 * Mark an autocommand pattern for deletion.
363 */
364 static void
365au_remove_pat(AutoPat *ap)
366{
367 VIM_CLEAR(ap->pat);
368 ap->buflocal_nr = -1;
369 au_need_clean = TRUE;
370}
371
372/*
373 * Mark all commands for a pattern for deletion.
374 */
375 static void
376au_remove_cmds(AutoPat *ap)
377{
378 AutoCmd *ac;
379
380 for (ac = ap->cmds; ac != NULL; ac = ac->next)
381 VIM_CLEAR(ac->cmd);
382 au_need_clean = TRUE;
383}
384
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200385// Delete one command from an autocmd pattern.
386static void au_del_cmd(AutoCmd *ac)
387{
388 VIM_CLEAR(ac->cmd);
389 au_need_clean = TRUE;
390}
391
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100392/*
393 * Cleanup autocommands and patterns that have been deleted.
394 * This is only done when not executing autocommands.
395 */
396 static void
397au_cleanup(void)
398{
399 AutoPat *ap, **prev_ap;
400 AutoCmd *ac, **prev_ac;
401 event_T event;
402
403 if (autocmd_busy || !au_need_clean)
404 return;
405
406 // loop over all events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100407 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100408 event = (event_T)((int)event + 1))
409 {
410 // loop over all autocommand patterns
411 prev_ap = &(first_autopat[(int)event]);
412 for (ap = *prev_ap; ap != NULL; ap = *prev_ap)
413 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200414 int has_cmd = FALSE;
415
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200416 // loop over all commands for this pattern
417 prev_ac = &(ap->cmds);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100418 for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
419 {
420 // remove the command if the pattern is to be deleted or when
421 // the command has been marked for deletion
422 if (ap->pat == NULL || ac->cmd == NULL)
423 {
424 *prev_ac = ac->next;
425 vim_free(ac->cmd);
426 vim_free(ac);
427 }
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200428 else
429 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200430 has_cmd = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100431 prev_ac = &(ac->next);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200432 }
433 }
434
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200435 if (ap->pat != NULL && !has_cmd)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200436 // Pattern was not marked for deletion, but all of its
437 // commands were. So mark the pattern for deletion.
438 au_remove_pat(ap);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100439
440 // remove the pattern if it has been marked for deletion
441 if (ap->pat == NULL)
442 {
443 if (ap->next == NULL)
444 {
445 if (prev_ap == &(first_autopat[(int)event]))
446 last_autopat[(int)event] = NULL;
447 else
448 // this depends on the "next" field being the first in
449 // the struct
450 last_autopat[(int)event] = (AutoPat *)prev_ap;
451 }
452 *prev_ap = ap->next;
453 vim_regfree(ap->reg_prog);
454 vim_free(ap);
455 }
456 else
457 prev_ap = &(ap->next);
458 }
459 }
460
461 au_need_clean = FALSE;
462}
463
464/*
465 * Called when buffer is freed, to remove/invalidate related buffer-local
466 * autocmds.
467 */
468 void
469aubuflocal_remove(buf_T *buf)
470{
LemonBoyeca7c602022-04-14 15:39:43 +0100471 AutoPat *ap;
472 event_T event;
473 AutoPatCmd_T *apc;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100474
475 // invalidate currently executing autocommands
476 for (apc = active_apc_list; apc; apc = apc->next)
477 if (buf->b_fnum == apc->arg_bufnr)
478 apc->arg_bufnr = 0;
479
480 // invalidate buflocals looping through events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100481 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100482 event = (event_T)((int)event + 1))
483 // loop over all autocommand patterns
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200484 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100485 if (ap->buflocal_nr == buf->b_fnum)
486 {
487 au_remove_pat(ap);
488 if (p_verbose >= 6)
489 {
490 verbose_enter();
491 smsg(_("auto-removing autocommand: %s <buffer=%d>"),
492 event_nr2name(event), buf->b_fnum);
493 verbose_leave();
494 }
495 }
496 au_cleanup();
497}
498
499/*
500 * Add an autocmd group name.
501 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
502 */
503 static int
504au_new_group(char_u *name)
505{
506 int i;
507
508 i = au_find_group(name);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100509 if (i != AUGROUP_ERROR)
510 return i;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100511
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100512 // the group doesn't exist yet, add it. First try using a free entry.
513 for (i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100514 if (AUGROUP_NAME(i) == NULL)
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100515 break;
516 if (i == augroups.ga_len && ga_grow(&augroups, 1) == FAIL)
517 return AUGROUP_ERROR;
518
519 AUGROUP_NAME(i) = vim_strsave(name);
520 if (AUGROUP_NAME(i) == NULL)
521 return AUGROUP_ERROR;
522 if (i == augroups.ga_len)
523 ++augroups.ga_len;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100524
525 return i;
526}
527
528 static void
529au_del_group(char_u *name)
530{
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100531 int i;
532 event_T event;
533 AutoPat *ap;
534 int in_use = FALSE;
535
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100536
537 i = au_find_group(name);
538 if (i == AUGROUP_ERROR) // the group doesn't exist
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100539 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100540 semsg(_(e_no_such_group_str), name);
541 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100542 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100543 if (i == current_augroup)
544 {
545 emsg(_(e_cannot_delete_current_group));
546 return;
547 }
548
549 for (event = (event_T)0; (int)event < NUM_EVENTS;
550 event = (event_T)((int)event + 1))
551 {
552 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
553 if (ap->group == i && ap->pat != NULL)
554 {
555 give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
556 in_use = TRUE;
557 event = NUM_EVENTS;
558 break;
559 }
560 }
561 vim_free(AUGROUP_NAME(i));
562 if (in_use)
563 AUGROUP_NAME(i) = get_deleted_augroup();
564 else
565 AUGROUP_NAME(i) = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100566}
567
568/*
569 * Find the ID of an autocmd group name.
570 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
571 */
572 static int
573au_find_group(char_u *name)
574{
575 int i;
576
577 for (i = 0; i < augroups.ga_len; ++i)
578 if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
579 && STRCMP(AUGROUP_NAME(i), name) == 0)
580 return i;
581 return AUGROUP_ERROR;
582}
583
584/*
585 * Return TRUE if augroup "name" exists.
586 */
587 int
588au_has_group(char_u *name)
589{
590 return au_find_group(name) != AUGROUP_ERROR;
591}
592
593/*
594 * ":augroup {name}".
595 */
596 void
597do_augroup(char_u *arg, int del_group)
598{
599 int i;
600
601 if (del_group)
602 {
603 if (*arg == NUL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000604 emsg(_(e_argument_required));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100605 else
606 au_del_group(arg);
607 }
608 else if (STRICMP(arg, "end") == 0) // ":aug end": back to group 0
609 current_augroup = AUGROUP_DEFAULT;
610 else if (*arg) // ":aug xxx": switch to group xxx
611 {
612 i = au_new_group(arg);
613 if (i != AUGROUP_ERROR)
614 current_augroup = i;
615 }
616 else // ":aug": list the group names
617 {
618 msg_start();
619 for (i = 0; i < augroups.ga_len; ++i)
620 {
621 if (AUGROUP_NAME(i) != NULL)
622 {
623 msg_puts((char *)AUGROUP_NAME(i));
624 msg_puts(" ");
625 }
626 }
627 msg_clr_eos();
628 msg_end();
629 }
630}
631
632#if defined(EXITFREE) || defined(PROTO)
633 void
634free_all_autocmds(void)
635{
636 int i;
637 char_u *s;
638
639 for (current_augroup = -1; current_augroup < augroups.ga_len;
640 ++current_augroup)
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200641 do_autocmd(NULL, (char_u *)"", TRUE);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100642
643 for (i = 0; i < augroups.ga_len; ++i)
644 {
645 s = ((char_u **)(augroups.ga_data))[i];
646 if (s != get_deleted_augroup())
647 vim_free(s);
648 }
649 ga_clear(&augroups);
650}
651#endif
652
653/*
654 * Return the event number for event name "start".
655 * Return NUM_EVENTS if the event name was not found.
656 * Return a pointer to the next event name in "end".
657 */
658 static event_T
659event_name2nr(char_u *start, char_u **end)
660{
661 char_u *p;
662 int i;
663 int len;
664
665 // the event name ends with end of line, '|', a blank or a comma
666 for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
667 ;
668 for (i = 0; event_names[i].name != NULL; ++i)
669 {
670 len = (int)STRLEN(event_names[i].name);
671 if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0)
672 break;
673 }
674 if (*p == ',')
675 ++p;
676 *end = p;
677 if (event_names[i].name == NULL)
678 return NUM_EVENTS;
679 return event_names[i].event;
680}
681
682/*
683 * Return the name for event "event".
684 */
685 static char_u *
686event_nr2name(event_T event)
687{
688 int i;
689
690 for (i = 0; event_names[i].name != NULL; ++i)
691 if (event_names[i].event == event)
692 return (char_u *)event_names[i].name;
693 return (char_u *)"Unknown";
694}
695
696/*
697 * Scan over the events. "*" stands for all events.
698 */
699 static char_u *
700find_end_event(
701 char_u *arg,
702 int have_group) // TRUE when group name was found
703{
704 char_u *pat;
705 char_u *p;
706
707 if (*arg == '*')
708 {
709 if (arg[1] && !VIM_ISWHITE(arg[1]))
710 {
Bram Moolenaar6d057012021-12-31 18:49:43 +0000711 semsg(_(e_illegal_character_after_star_str), arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100712 return NULL;
713 }
714 pat = arg + 1;
715 }
716 else
717 {
718 for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p)
719 {
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100720 if ((int)event_name2nr(pat, &p) >= NUM_EVENTS)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100721 {
722 if (have_group)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000723 semsg(_(e_no_such_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100724 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000725 semsg(_(e_no_such_group_or_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100726 return NULL;
727 }
728 }
729 }
730 return pat;
731}
732
733/*
734 * Return TRUE if "event" is included in 'eventignore'.
735 */
736 static int
737event_ignored(event_T event)
738{
739 char_u *p = p_ei;
740
741 while (*p != NUL)
742 {
743 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
744 return TRUE;
745 if (event_name2nr(p, &p) == event)
746 return TRUE;
747 }
748
749 return FALSE;
750}
751
752/*
753 * Return OK when the contents of p_ei is valid, FAIL otherwise.
754 */
755 int
756check_ei(void)
757{
758 char_u *p = p_ei;
759
760 while (*p)
761 {
762 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
763 {
764 p += 3;
765 if (*p == ',')
766 ++p;
767 }
768 else if (event_name2nr(p, &p) == NUM_EVENTS)
769 return FAIL;
770 }
771
772 return OK;
773}
774
775# if defined(FEAT_SYN_HL) || defined(PROTO)
776
777/*
778 * Add "what" to 'eventignore' to skip loading syntax highlighting for every
779 * buffer loaded into the window. "what" must start with a comma.
780 * Returns the old value of 'eventignore' in allocated memory.
781 */
782 char_u *
783au_event_disable(char *what)
784{
785 char_u *new_ei;
786 char_u *save_ei;
787
788 save_ei = vim_strsave(p_ei);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100789 if (save_ei == NULL)
790 return NULL;
791
792 new_ei = vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what));
793 if (new_ei == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100794 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100795 vim_free(save_ei);
796 return NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100797 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100798
799 if (*what == ',' && *p_ei == NUL)
800 STRCPY(new_ei, what + 1);
801 else
802 STRCAT(new_ei, what);
803 set_string_option_direct((char_u *)"ei", -1, new_ei,
804 OPT_FREE, SID_NONE);
805 vim_free(new_ei);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100806 return save_ei;
807}
808
809 void
810au_event_restore(char_u *old_ei)
811{
812 if (old_ei != NULL)
813 {
814 set_string_option_direct((char_u *)"ei", -1, old_ei,
815 OPT_FREE, SID_NONE);
816 vim_free(old_ei);
817 }
818}
Bram Moolenaarc667da52019-11-30 20:52:27 +0100819# endif // FEAT_SYN_HL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100820
821/*
822 * do_autocmd() -- implements the :autocmd command. Can be used in the
823 * following ways:
824 *
825 * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
826 * will be automatically executed for <event>
827 * when editing a file matching <pat>, in
828 * the current group.
829 * :autocmd <event> <pat> Show the autocommands associated with
830 * <event> and <pat>.
831 * :autocmd <event> Show the autocommands associated with
832 * <event>.
833 * :autocmd Show all autocommands.
834 * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
835 * <event> and <pat>, and add the command
836 * <cmd>, for the current group.
837 * :autocmd! <event> <pat> Remove all autocommands associated with
838 * <event> and <pat> for the current group.
839 * :autocmd! <event> Remove all autocommands associated with
840 * <event> for the current group.
841 * :autocmd! Remove ALL autocommands for the current
842 * group.
843 *
844 * Multiple events and patterns may be given separated by commas. Here are
845 * some examples:
846 * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
847 * :autocmd bufleave * set tw=79 nosmartindent ic infercase
848 *
849 * :autocmd * *.c show all autocommands for *.c files.
850 *
851 * Mostly a {group} argument can optionally appear before <event>.
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200852 * "eap" can be NULL.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100853 */
854 void
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200855do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100856{
857 char_u *arg = arg_in;
858 char_u *pat;
859 char_u *envpat = NULL;
860 char_u *cmd;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200861 int cmd_need_free = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100862 event_T event;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200863 char_u *tofree = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100864 int nested = FALSE;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200865 int once = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100866 int group;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200867 int i;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200868 int flags = 0;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100869
870 if (*arg == '|')
871 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000872 eap->nextcmd = arg + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100873 arg = (char_u *)"";
874 group = AUGROUP_ALL; // no argument, use all groups
875 }
876 else
877 {
878 /*
879 * Check for a legal group name. If not, use AUGROUP_ALL.
880 */
881 group = au_get_grouparg(&arg);
882 if (arg == NULL) // out of memory
883 return;
884 }
885
886 /*
887 * Scan over the events.
888 * If we find an illegal name, return here, don't do anything.
889 */
890 pat = find_end_event(arg, group != AUGROUP_ALL);
891 if (pat == NULL)
892 return;
893
894 pat = skipwhite(pat);
895 if (*pat == '|')
896 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000897 eap->nextcmd = pat + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100898 pat = (char_u *)"";
899 cmd = (char_u *)"";
900 }
901 else
902 {
903 /*
904 * Scan over the pattern. Put a NUL at the end.
905 */
906 cmd = pat;
907 while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
908 cmd++;
909 if (*cmd)
910 *cmd++ = NUL;
911
912 // Expand environment variables in the pattern. Set 'shellslash', we
913 // want forward slashes here.
914 if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
915 {
916#ifdef BACKSLASH_IN_FILENAME
917 int p_ssl_save = p_ssl;
918
919 p_ssl = TRUE;
920#endif
921 envpat = expand_env_save(pat);
922#ifdef BACKSLASH_IN_FILENAME
923 p_ssl = p_ssl_save;
924#endif
925 if (envpat != NULL)
926 pat = envpat;
927 }
928
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100929 cmd = skipwhite(cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200930 for (i = 0; i < 2; i++)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100931 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100932 if (*cmd == NUL)
933 continue;
934
935 // Check for "++once" flag.
936 if (STRNCMP(cmd, "++once", 6) == 0 && VIM_ISWHITE(cmd[6]))
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200937 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100938 if (once)
939 semsg(_(e_duplicate_argument_str), "++once");
940 once = TRUE;
941 cmd = skipwhite(cmd + 6);
942 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200943
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100944 // Check for "++nested" flag.
945 if ((STRNCMP(cmd, "++nested", 8) == 0 && VIM_ISWHITE(cmd[8])))
946 {
947 if (nested)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200948 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100949 semsg(_(e_duplicate_argument_str), "++nested");
950 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200951 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100952 nested = TRUE;
953 cmd = skipwhite(cmd + 8);
954 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200955
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100956 // Check for the old "nested" flag in legacy script.
957 if (STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
958 {
959 if (in_vim9script())
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200960 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100961 // If there ever is a :nested command this error should
962 // be removed and "nested" accepted as the start of the
963 // command.
964 emsg(_(e_invalid_command_nested_did_you_mean_plusplus_nested));
965 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200966 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100967 if (nested)
968 {
969 semsg(_(e_duplicate_argument_str), "nested");
970 return;
971 }
972 nested = TRUE;
973 cmd = skipwhite(cmd + 6);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200974 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100975 }
976
977 /*
978 * Find the start of the commands.
979 * Expand <sfile> in it.
980 */
981 if (*cmd != NUL)
982 {
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200983 if (eap != NULL)
984 // Read a {} block if it follows.
985 cmd = may_get_cmd_block(eap, cmd, &tofree, &flags);
986
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100987 cmd = expand_sfile(cmd);
988 if (cmd == NULL) // some error
989 return;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200990 cmd_need_free = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100991 }
992 }
993
994 /*
995 * Print header when showing autocommands.
996 */
997 if (!forceit && *cmd == NUL)
998 // Highlight title
999 msg_puts_title(_("\n--- Autocommands ---"));
1000
1001 /*
1002 * Loop over the events.
1003 */
1004 last_event = (event_T)-1; // for listing the event name
1005 last_group = AUGROUP_ERROR; // for listing the group name
1006 if (*arg == '*' || *arg == NUL || *arg == '|')
1007 {
Bram Moolenaarb6db1462021-12-24 19:24:47 +00001008 if (*cmd != NUL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001009 emsg(_(e_cannot_define_autocommands_for_all_events));
1010 else
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +01001011 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001012 event = (event_T)((int)event + 1))
1013 if (do_autocmd_event(event, pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001014 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001015 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001016 }
1017 else
1018 {
1019 while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
1020 if (do_autocmd_event(event_name2nr(arg, &arg), pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001021 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001022 break;
1023 }
1024
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001025 if (cmd_need_free)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001026 vim_free(cmd);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001027 vim_free(tofree);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001028 vim_free(envpat);
1029}
1030
1031/*
1032 * Find the group ID in a ":autocmd" or ":doautocmd" argument.
1033 * The "argp" argument is advanced to the following argument.
1034 *
1035 * Returns the group ID, AUGROUP_ERROR for error (out of memory).
1036 */
1037 static int
1038au_get_grouparg(char_u **argp)
1039{
1040 char_u *group_name;
1041 char_u *p;
1042 char_u *arg = *argp;
1043 int group = AUGROUP_ALL;
1044
1045 for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p)
1046 ;
1047 if (p > arg)
1048 {
Bram Moolenaardf44a272020-06-07 20:49:05 +02001049 group_name = vim_strnsave(arg, p - arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001050 if (group_name == NULL) // out of memory
1051 return AUGROUP_ERROR;
1052 group = au_find_group(group_name);
1053 if (group == AUGROUP_ERROR)
1054 group = AUGROUP_ALL; // no match, use all groups
1055 else
1056 *argp = skipwhite(p); // match, skip over group name
1057 vim_free(group_name);
1058 }
1059 return group;
1060}
1061
1062/*
1063 * do_autocmd() for one event.
1064 * If *pat == NUL do for all patterns.
1065 * If *cmd == NUL show entries.
1066 * If forceit == TRUE delete entries.
1067 * If group is not AUGROUP_ALL, only use this group.
1068 */
1069 static int
1070do_autocmd_event(
1071 event_T event,
1072 char_u *pat,
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001073 int once,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001074 int nested,
1075 char_u *cmd,
1076 int forceit,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001077 int group,
1078 int flags)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001079{
1080 AutoPat *ap;
1081 AutoPat **prev_ap;
1082 AutoCmd *ac;
1083 AutoCmd **prev_ac;
1084 int brace_level;
1085 char_u *endpat;
1086 int findgroup;
1087 int allgroups;
1088 int patlen;
1089 int is_buflocal;
1090 int buflocal_nr;
Bram Moolenaarc667da52019-11-30 20:52:27 +01001091 char_u buflocal_pat[25]; // for "<buffer=X>"
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001092
1093 if (group == AUGROUP_ALL)
1094 findgroup = current_augroup;
1095 else
1096 findgroup = group;
1097 allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
1098
1099 /*
1100 * Show or delete all patterns for an event.
1101 */
1102 if (*pat == NUL)
1103 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001104 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001105 {
1106 if (forceit) // delete the AutoPat, if it's in the current group
1107 {
1108 if (ap->group == findgroup)
1109 au_remove_pat(ap);
1110 }
1111 else if (group == AUGROUP_ALL || ap->group == group)
1112 show_autocmd(ap, event);
1113 }
1114 }
1115
1116 /*
1117 * Loop through all the specified patterns.
1118 */
1119 for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
1120 {
1121 /*
1122 * Find end of the pattern.
1123 * Watch out for a comma in braces, like "*.\{obj,o\}".
1124 */
1125 brace_level = 0;
1126 for (endpat = pat; *endpat && (*endpat != ',' || brace_level
1127 || (endpat > pat && endpat[-1] == '\\')); ++endpat)
1128 {
1129 if (*endpat == '{')
1130 brace_level++;
1131 else if (*endpat == '}')
1132 brace_level--;
1133 }
1134 if (pat == endpat) // ignore single comma
1135 continue;
1136 patlen = (int)(endpat - pat);
1137
1138 /*
1139 * detect special <buflocal[=X]> buffer-local patterns
1140 */
1141 is_buflocal = FALSE;
1142 buflocal_nr = 0;
1143
1144 if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
1145 && pat[patlen - 1] == '>')
1146 {
1147 // "<buffer...>": Error will be printed only for addition.
1148 // printing and removing will proceed silently.
1149 is_buflocal = TRUE;
1150 if (patlen == 8)
1151 // "<buffer>"
1152 buflocal_nr = curbuf->b_fnum;
1153 else if (patlen > 9 && pat[7] == '=')
1154 {
1155 if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0)
1156 // "<buffer=abuf>"
1157 buflocal_nr = autocmd_bufnr;
1158 else if (skipdigits(pat + 8) == pat + patlen - 1)
1159 // "<buffer=123>"
1160 buflocal_nr = atoi((char *)pat + 8);
1161 }
1162 }
1163
1164 if (is_buflocal)
1165 {
1166 // normalize pat into standard "<buffer>#N" form
1167 sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
1168 pat = buflocal_pat; // can modify pat and patlen
1169 patlen = (int)STRLEN(buflocal_pat); // but not endpat
1170 }
1171
1172 /*
1173 * Find AutoPat entries with this pattern. When adding a command it
1174 * always goes at or after the last one, so start at the end.
1175 */
1176 if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL)
1177 prev_ap = &last_autopat[(int)event];
1178 else
1179 prev_ap = &first_autopat[(int)event];
1180 while ((ap = *prev_ap) != NULL)
1181 {
1182 if (ap->pat != NULL)
1183 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001184 /*
1185 * Accept a pattern when:
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001186 * - a group was specified and it's that group, or a group was
1187 * not specified and it's the current group, or a group was
1188 * not specified and we are listing
1189 * - the length of the pattern matches
1190 * - the pattern matches.
1191 * For <buffer[=X]>, this condition works because we normalize
1192 * all buffer-local patterns.
1193 */
1194 if ((allgroups || ap->group == findgroup)
1195 && ap->patlen == patlen
1196 && STRNCMP(pat, ap->pat, patlen) == 0)
1197 {
1198 /*
1199 * Remove existing autocommands.
1200 * If adding any new autocmd's for this AutoPat, don't
1201 * delete the pattern from the autopat list, append to
1202 * this list.
1203 */
1204 if (forceit)
1205 {
1206 if (*cmd != NUL && ap->next == NULL)
1207 {
1208 au_remove_cmds(ap);
1209 break;
1210 }
1211 au_remove_pat(ap);
1212 }
1213
1214 /*
1215 * Show autocmd's for this autopat, or buflocals <buffer=X>
1216 */
1217 else if (*cmd == NUL)
1218 show_autocmd(ap, event);
1219
1220 /*
1221 * Add autocmd to this autopat, if it's the last one.
1222 */
1223 else if (ap->next == NULL)
1224 break;
1225 }
1226 }
1227 prev_ap = &ap->next;
1228 }
1229
1230 /*
1231 * Add a new command.
1232 */
1233 if (*cmd != NUL)
1234 {
1235 /*
1236 * If the pattern we want to add a command to does appear at the
1237 * end of the list (or not is not in the list at all), add the
1238 * pattern at the end of the list.
1239 */
1240 if (ap == NULL)
1241 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001242 // refuse to add buffer-local ap if buffer number is invalid
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001243 if (is_buflocal && (buflocal_nr == 0
1244 || buflist_findnr(buflocal_nr) == NULL))
1245 {
Bram Moolenaarf1474d82021-12-31 19:59:55 +00001246 semsg(_(e_buffer_nr_invalid_buffer_number), buflocal_nr);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001247 return FAIL;
1248 }
1249
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001250 ap = ALLOC_ONE(AutoPat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001251 if (ap == NULL)
1252 return FAIL;
1253 ap->pat = vim_strnsave(pat, patlen);
1254 ap->patlen = patlen;
1255 if (ap->pat == NULL)
1256 {
1257 vim_free(ap);
1258 return FAIL;
1259 }
1260
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001261#ifdef FEAT_EVAL
1262 // need to initialize last_mode for the first ModeChanged
1263 // autocmd
1264 if (event == EVENT_MODECHANGED && !has_modechanged())
LemonBoy2bf52dd2022-04-09 18:17:34 +01001265 get_mode(last_mode);
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001266#endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001267 // Initialize the fields checked by the WinScrolled and
1268 // WinResized trigger to prevent them from firing right after
1269 // the first autocmd is defined.
1270 if ((event == EVENT_WINSCROLLED || event == EVENT_WINRESIZED)
1271 && !(has_winscrolled() || has_winresized()))
LemonBoy09371822022-04-08 15:18:45 +01001272 {
Bram Moolenaar29967732022-11-20 12:11:45 +00001273 tabpage_T *save_curtab = curtab;
1274 tabpage_T *tp;
1275 FOR_ALL_TABPAGES(tp)
1276 {
1277 unuse_tabpage(curtab);
1278 use_tabpage(tp);
1279 snapshot_windows_scroll_size();
1280 }
1281 unuse_tabpage(curtab);
1282 use_tabpage(save_curtab);
LemonBoy09371822022-04-08 15:18:45 +01001283 }
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001284
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001285 if (is_buflocal)
1286 {
1287 ap->buflocal_nr = buflocal_nr;
1288 ap->reg_prog = NULL;
1289 }
1290 else
1291 {
1292 char_u *reg_pat;
1293
1294 ap->buflocal_nr = 0;
1295 reg_pat = file_pat_to_reg_pat(pat, endpat,
1296 &ap->allow_dirs, TRUE);
1297 if (reg_pat != NULL)
1298 ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
1299 vim_free(reg_pat);
1300 if (reg_pat == NULL || ap->reg_prog == NULL)
1301 {
1302 vim_free(ap->pat);
1303 vim_free(ap);
1304 return FAIL;
1305 }
1306 }
1307 ap->cmds = NULL;
1308 *prev_ap = ap;
1309 last_autopat[(int)event] = ap;
1310 ap->next = NULL;
1311 if (group == AUGROUP_ALL)
1312 ap->group = current_augroup;
1313 else
1314 ap->group = group;
1315 }
1316
1317 /*
1318 * Add the autocmd at the end of the AutoCmd list.
1319 */
1320 prev_ac = &(ap->cmds);
1321 while ((ac = *prev_ac) != NULL)
1322 prev_ac = &ac->next;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001323 ac = ALLOC_ONE(AutoCmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001324 if (ac == NULL)
1325 return FAIL;
1326 ac->cmd = vim_strsave(cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001327 ac->script_ctx = current_sctx;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001328 if (flags & UC_VIM9)
1329 ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001330#ifdef FEAT_EVAL
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001331 ac->script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001332#endif
1333 if (ac->cmd == NULL)
1334 {
1335 vim_free(ac);
1336 return FAIL;
1337 }
1338 ac->next = NULL;
1339 *prev_ac = ac;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001340 ac->once = once;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001341 ac->nested = nested;
1342 }
1343 }
1344
1345 au_cleanup(); // may really delete removed patterns/commands now
1346 return OK;
1347}
1348
1349/*
1350 * Implementation of ":doautocmd [group] event [fname]".
1351 * Return OK for success, FAIL for failure;
1352 */
1353 int
1354do_doautocmd(
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001355 char_u *arg_start,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001356 int do_msg, // give message for no matching autocmds?
1357 int *did_something)
1358{
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001359 char_u *arg = arg_start;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001360 char_u *fname;
1361 int nothing_done = TRUE;
1362 int group;
1363
1364 if (did_something != NULL)
1365 *did_something = FALSE;
1366
1367 /*
1368 * Check for a legal group name. If not, use AUGROUP_ALL.
1369 */
1370 group = au_get_grouparg(&arg);
1371 if (arg == NULL) // out of memory
1372 return FAIL;
1373
1374 if (*arg == '*')
1375 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001376 emsg(_(e_cant_execute_autocommands_for_all_events));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001377 return FAIL;
1378 }
1379
1380 /*
1381 * Scan over the events.
1382 * If we find an illegal name, return here, don't do anything.
1383 */
1384 fname = find_end_event(arg, group != AUGROUP_ALL);
1385 if (fname == NULL)
1386 return FAIL;
1387
1388 fname = skipwhite(fname);
1389
1390 /*
1391 * Loop over the events.
1392 */
1393 while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
1394 if (apply_autocmds_group(event_name2nr(arg, &arg),
1395 fname, NULL, TRUE, group, curbuf, NULL))
1396 nothing_done = FALSE;
1397
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001398 if (nothing_done && do_msg
1399#ifdef FEAT_EVAL
1400 && !aborting()
1401#endif
1402 )
1403 smsg(_("No matching autocommands: %s"), arg_start);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001404 if (did_something != NULL)
1405 *did_something = !nothing_done;
1406
1407#ifdef FEAT_EVAL
1408 return aborting() ? FAIL : OK;
1409#else
1410 return OK;
1411#endif
1412}
1413
1414/*
1415 * ":doautoall": execute autocommands for each loaded buffer.
1416 */
1417 void
1418ex_doautoall(exarg_T *eap)
1419{
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001420 int retval = OK;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001421 aco_save_T aco;
1422 buf_T *buf;
1423 bufref_T bufref;
1424 char_u *arg = eap->arg;
1425 int call_do_modelines = check_nomodeline(&arg);
1426 int did_aucmd;
1427
1428 /*
1429 * This is a bit tricky: For some commands curwin->w_buffer needs to be
1430 * equal to curbuf, but for some buffers there may not be a window.
1431 * So we change the buffer for the current window for a moment. This
1432 * gives problems when the autocommands make changes to the list of
1433 * buffers or windows...
1434 */
1435 FOR_ALL_BUFFERS(buf)
1436 {
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001437 // Only do loaded buffers and skip the current buffer, it's done last.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001438 if (buf->b_ml.ml_mfp == NULL || buf == curbuf)
1439 continue;
1440
1441 // find a window for this buffer and save some values
1442 aucmd_prepbuf(&aco, buf);
1443 set_bufref(&bufref, buf);
1444
1445 // execute the autocommands for this buffer
1446 retval = do_doautocmd(arg, FALSE, &did_aucmd);
1447
1448 if (call_do_modelines && did_aucmd)
1449 // Execute the modeline settings, but don't set window-local
1450 // options if we are using the current window for another
1451 // buffer.
1452 do_modelines(curwin == aucmd_win ? OPT_NOWIN : 0);
1453
1454 // restore the current window
1455 aucmd_restbuf(&aco);
1456
1457 // stop if there is some error or buffer was deleted
1458 if (retval == FAIL || !bufref_valid(&bufref))
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001459 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001460 retval = FAIL;
1461 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001462 }
1463 }
1464
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001465 // Execute autocommands for the current buffer last.
1466 if (retval == OK)
1467 {
1468 do_doautocmd(arg, FALSE, &did_aucmd);
1469 if (call_do_modelines && did_aucmd)
1470 do_modelines(0);
1471 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001472}
1473
1474/*
1475 * Check *argp for <nomodeline>. When it is present return FALSE, otherwise
1476 * return TRUE and advance *argp to after it.
1477 * Thus return TRUE when do_modelines() should be called.
1478 */
1479 int
1480check_nomodeline(char_u **argp)
1481{
1482 if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
1483 {
1484 *argp = skipwhite(*argp + 12);
1485 return FALSE;
1486 }
1487 return TRUE;
1488}
1489
1490/*
1491 * Prepare for executing autocommands for (hidden) buffer "buf".
1492 * Search for a visible window containing the current buffer. If there isn't
1493 * one then use "aucmd_win".
1494 * Set "curbuf" and "curwin" to match "buf".
1495 */
1496 void
1497aucmd_prepbuf(
1498 aco_save_T *aco, // structure to save values in
1499 buf_T *buf) // new curbuf
1500{
1501 win_T *win;
1502 int save_ea;
1503#ifdef FEAT_AUTOCHDIR
1504 int save_acd;
1505#endif
1506
1507 // Find a window that is for the new buffer
1508 if (buf == curbuf) // be quick when buf is curbuf
1509 win = curwin;
1510 else
1511 FOR_ALL_WINDOWS(win)
1512 if (win->w_buffer == buf)
1513 break;
1514
1515 // Allocate "aucmd_win" when needed. If this fails (out of memory) fall
1516 // back to using the current window.
1517 if (win == NULL && aucmd_win == NULL)
1518 {
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001519 aucmd_win = win_alloc_popup_win();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001520 if (aucmd_win == NULL)
1521 win = curwin;
1522 }
1523 if (win == NULL && aucmd_win_used)
1524 // Strange recursive autocommand, fall back to using the current
1525 // window. Expect a few side effects...
1526 win = curwin;
1527
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001528 aco->save_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001529 aco->save_curbuf = curbuf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001530 aco->save_prevwin_id = prevwin == NULL ? 0 : prevwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001531 if (win != NULL)
1532 {
1533 // There is a window for "buf" in the current tab page, make it the
1534 // curwin. This is preferred, it has the least side effects (esp. if
1535 // "buf" is curbuf).
1536 aco->use_aucmd_win = FALSE;
1537 curwin = win;
1538 }
1539 else
1540 {
1541 // There is no window for "buf", use "aucmd_win". To minimize the side
1542 // effects, insert it in the current tab page.
1543 // Anything related to a window (e.g., setting folds) may have
1544 // unexpected results.
1545 aco->use_aucmd_win = TRUE;
1546 aucmd_win_used = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001547
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001548 win_init_popup_win(aucmd_win, buf);
1549
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001550 aco->globaldir = globaldir;
1551 globaldir = NULL;
1552
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001553 // Split the current window, put the aucmd_win in the upper half.
1554 // We don't want the BufEnter or WinEnter autocommands.
1555 block_autocmds();
1556 make_snapshot(SNAP_AUCMD_IDX);
1557 save_ea = p_ea;
1558 p_ea = FALSE;
1559
1560#ifdef FEAT_AUTOCHDIR
1561 // Prevent chdir() call in win_enter_ext(), through do_autochdir().
1562 save_acd = p_acd;
1563 p_acd = FALSE;
1564#endif
1565
Bram Moolenaardff97e62022-01-24 20:00:55 +00001566 // no redrawing and don't set the window title
1567 ++RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001568 (void)win_split_ins(0, WSP_TOP, aucmd_win, 0);
Bram Moolenaardff97e62022-01-24 20:00:55 +00001569 --RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001570 (void)win_comp_pos(); // recompute window positions
1571 p_ea = save_ea;
1572#ifdef FEAT_AUTOCHDIR
1573 p_acd = save_acd;
1574#endif
1575 unblock_autocmds();
1576 curwin = aucmd_win;
1577 }
1578 curbuf = buf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001579 aco->new_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001580 set_bufref(&aco->new_curbuf, curbuf);
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001581
1582 // disable the Visual area, the position may be invalid in another buffer
1583 aco->save_VIsual_active = VIsual_active;
1584 VIsual_active = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001585}
1586
1587/*
1588 * Cleanup after executing autocommands for a (hidden) buffer.
1589 * Restore the window as it was (if possible).
1590 */
1591 void
1592aucmd_restbuf(
1593 aco_save_T *aco) // structure holding saved values
1594{
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001595 int dummy;
1596 win_T *save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001597
1598 if (aco->use_aucmd_win)
1599 {
1600 --curbuf->b_nwindows;
1601 // Find "aucmd_win", it can't be closed, but it may be in another tab
1602 // page. Do not trigger autocommands here.
1603 block_autocmds();
1604 if (curwin != aucmd_win)
1605 {
1606 tabpage_T *tp;
1607 win_T *wp;
1608
1609 FOR_ALL_TAB_WINDOWS(tp, wp)
1610 {
1611 if (wp == aucmd_win)
1612 {
1613 if (tp != curtab)
1614 goto_tabpage_tp(tp, TRUE, TRUE);
1615 win_goto(aucmd_win);
1616 goto win_found;
1617 }
1618 }
1619 }
1620win_found:
1621
1622 // Remove the window and frame from the tree of frames.
1623 (void)winframe_remove(curwin, &dummy, NULL);
1624 win_remove(curwin, NULL);
1625 aucmd_win_used = FALSE;
1626 last_status(FALSE); // may need to remove last status line
1627
1628 if (!valid_tabpage_win(curtab))
1629 // no valid window in current tabpage
1630 close_tabpage(curtab);
1631
1632 restore_snapshot(SNAP_AUCMD_IDX, FALSE);
1633 (void)win_comp_pos(); // recompute window positions
1634 unblock_autocmds();
1635
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001636 save_curwin = win_find_by_id(aco->save_curwin_id);
1637 if (save_curwin != NULL)
1638 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001639 else
1640 // Hmm, original window disappeared. Just use the first one.
1641 curwin = firstwin;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001642 curbuf = curwin->w_buffer;
1643#ifdef FEAT_JOB_CHANNEL
1644 // May need to restore insert mode for a prompt buffer.
1645 entering_window(curwin);
1646#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001647 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001648#ifdef FEAT_EVAL
1649 vars_clear(&aucmd_win->w_vars->dv_hashtab); // free all w: variables
1650 hash_init(&aucmd_win->w_vars->dv_hashtab); // re-use the hashtab
1651#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001652 vim_free(globaldir);
1653 globaldir = aco->globaldir;
1654
1655 // the buffer contents may have changed
1656 check_cursor();
1657 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1658 {
1659 curwin->w_topline = curbuf->b_ml.ml_line_count;
1660#ifdef FEAT_DIFF
1661 curwin->w_topfill = 0;
1662#endif
1663 }
1664#if defined(FEAT_GUI)
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001665 if (gui.in_use)
1666 {
1667 // Hide the scrollbars from the aucmd_win and update.
1668 gui_mch_enable_scrollbar(
1669 &aucmd_win->w_scrollbars[SBAR_LEFT], FALSE);
1670 gui_mch_enable_scrollbar(
1671 &aucmd_win->w_scrollbars[SBAR_RIGHT], FALSE);
1672 gui_may_update_scrollbars();
1673 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001674#endif
1675 }
1676 else
1677 {
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001678 // Restore curwin. Use the window ID, a window may have been closed
1679 // and the memory re-used for another one.
1680 save_curwin = win_find_by_id(aco->save_curwin_id);
1681 if (save_curwin != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001682 {
1683 // Restore the buffer which was previously edited by curwin, if
1684 // it was changed, we are still the same window and the buffer is
1685 // valid.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001686 if (curwin->w_id == aco->new_curwin_id
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001687 && curbuf != aco->new_curbuf.br_buf
1688 && bufref_valid(&aco->new_curbuf)
1689 && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
1690 {
1691# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
1692 if (curwin->w_s == &curbuf->b_s)
1693 curwin->w_s = &aco->new_curbuf.br_buf->b_s;
1694# endif
1695 --curbuf->b_nwindows;
1696 curbuf = aco->new_curbuf.br_buf;
1697 curwin->w_buffer = curbuf;
1698 ++curbuf->b_nwindows;
1699 }
1700
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001701 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001702 curbuf = curwin->w_buffer;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001703 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001704 // In case the autocommand moves the cursor to a position that
1705 // does not exist in curbuf.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001706 check_cursor();
1707 }
1708 }
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001709
1710 check_cursor(); // just in case lines got deleted
1711 VIsual_active = aco->save_VIsual_active;
1712 if (VIsual_active)
1713 check_pos(curbuf, &VIsual);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001714}
1715
1716static int autocmd_nested = FALSE;
1717
1718/*
1719 * Execute autocommands for "event" and file name "fname".
1720 * Return TRUE if some commands were executed.
1721 */
1722 int
1723apply_autocmds(
1724 event_T event,
1725 char_u *fname, // NULL or empty means use actual file name
1726 char_u *fname_io, // fname to use for <afile> on cmdline
1727 int force, // when TRUE, ignore autocmd_busy
1728 buf_T *buf) // buffer for <abuf>
1729{
1730 return apply_autocmds_group(event, fname, fname_io, force,
1731 AUGROUP_ALL, buf, NULL);
1732}
1733
1734/*
1735 * Like apply_autocmds(), but with extra "eap" argument. This takes care of
1736 * setting v:filearg.
1737 */
1738 int
1739apply_autocmds_exarg(
1740 event_T event,
1741 char_u *fname,
1742 char_u *fname_io,
1743 int force,
1744 buf_T *buf,
1745 exarg_T *eap)
1746{
1747 return apply_autocmds_group(event, fname, fname_io, force,
1748 AUGROUP_ALL, buf, eap);
1749}
1750
1751/*
1752 * Like apply_autocmds(), but handles the caller's retval. If the script
1753 * processing is being aborted or if retval is FAIL when inside a try
1754 * conditional, no autocommands are executed. If otherwise the autocommands
1755 * cause the script to be aborted, retval is set to FAIL.
1756 */
1757 int
1758apply_autocmds_retval(
1759 event_T event,
1760 char_u *fname, // NULL or empty means use actual file name
1761 char_u *fname_io, // fname to use for <afile> on cmdline
1762 int force, // when TRUE, ignore autocmd_busy
1763 buf_T *buf, // buffer for <abuf>
1764 int *retval) // pointer to caller's retval
1765{
1766 int did_cmd;
1767
1768#ifdef FEAT_EVAL
1769 if (should_abort(*retval))
1770 return FALSE;
1771#endif
1772
1773 did_cmd = apply_autocmds_group(event, fname, fname_io, force,
1774 AUGROUP_ALL, buf, NULL);
1775 if (did_cmd
1776#ifdef FEAT_EVAL
1777 && aborting()
1778#endif
1779 )
1780 *retval = FAIL;
1781 return did_cmd;
1782}
1783
1784/*
1785 * Return TRUE when there is a CursorHold autocommand defined.
1786 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001787 static int
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001788has_cursorhold(void)
1789{
Bram Moolenaar24959102022-05-07 20:01:16 +01001790 return (first_autopat[(int)(get_real_state() == MODE_NORMAL_BUSY
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001791 ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
1792}
1793
1794/*
1795 * Return TRUE if the CursorHold event can be triggered.
1796 */
1797 int
1798trigger_cursorhold(void)
1799{
1800 int state;
1801
1802 if (!did_cursorhold
1803 && has_cursorhold()
1804 && reg_recording == 0
1805 && typebuf.tb_len == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001806 && !ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001807 {
1808 state = get_real_state();
Bram Moolenaar24959102022-05-07 20:01:16 +01001809 if (state == MODE_NORMAL_BUSY || (state & MODE_INSERT) != 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001810 return TRUE;
1811 }
1812 return FALSE;
1813}
1814
1815/*
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001816 * Return TRUE when there is a WinResized autocommand defined.
1817 */
1818 int
1819has_winresized(void)
1820{
1821 return (first_autopat[(int)EVENT_WINRESIZED] != NULL);
1822}
1823
1824/*
LemonBoy09371822022-04-08 15:18:45 +01001825 * Return TRUE when there is a WinScrolled autocommand defined.
1826 */
1827 int
1828has_winscrolled(void)
1829{
1830 return (first_autopat[(int)EVENT_WINSCROLLED] != NULL);
1831}
1832
1833/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001834 * Return TRUE when there is a CursorMoved autocommand defined.
1835 */
1836 int
1837has_cursormoved(void)
1838{
1839 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1840}
1841
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001842/*
1843 * Return TRUE when there is a CursorMovedI autocommand defined.
1844 */
1845 int
1846has_cursormovedI(void)
1847{
1848 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1849}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001850
1851/*
1852 * Return TRUE when there is a TextChanged autocommand defined.
1853 */
1854 int
1855has_textchanged(void)
1856{
1857 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1858}
1859
1860/*
1861 * Return TRUE when there is a TextChangedI autocommand defined.
1862 */
1863 int
1864has_textchangedI(void)
1865{
1866 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
1867}
1868
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001869/*
1870 * Return TRUE when there is a TextChangedP autocommand defined.
1871 */
1872 int
1873has_textchangedP(void)
1874{
1875 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
1876}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001877
1878/*
1879 * Return TRUE when there is an InsertCharPre autocommand defined.
1880 */
1881 int
1882has_insertcharpre(void)
1883{
1884 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
1885}
1886
1887/*
1888 * Return TRUE when there is an CmdUndefined autocommand defined.
1889 */
1890 int
1891has_cmdundefined(void)
1892{
1893 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
1894}
1895
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001896#if defined(FEAT_EVAL) || defined(PROTO)
1897/*
1898 * Return TRUE when there is a TextYankPost autocommand defined.
1899 */
1900 int
1901has_textyankpost(void)
1902{
1903 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
1904}
1905#endif
1906
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001907#if defined(FEAT_EVAL) || defined(PROTO)
1908/*
1909 * Return TRUE when there is a CompleteChanged autocommand defined.
1910 */
1911 int
1912has_completechanged(void)
1913{
1914 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
1915}
1916#endif
1917
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02001918#if defined(FEAT_EVAL) || defined(PROTO)
1919/*
1920 * Return TRUE when there is a ModeChanged autocommand defined.
1921 */
1922 int
1923has_modechanged(void)
1924{
1925 return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
1926}
1927#endif
1928
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001929/*
1930 * Execute autocommands for "event" and file name "fname".
1931 * Return TRUE if some commands were executed.
1932 */
1933 static int
1934apply_autocmds_group(
1935 event_T event,
1936 char_u *fname, // NULL or empty means use actual file name
1937 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
1938 // use fname
1939 int force, // when TRUE, ignore autocmd_busy
1940 int group, // group ID, or AUGROUP_ALL
1941 buf_T *buf, // buffer for <abuf>
1942 exarg_T *eap UNUSED) // command arguments
1943{
1944 char_u *sfname = NULL; // short file name
1945 char_u *tail;
1946 int save_changed;
1947 buf_T *old_curbuf;
1948 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001949 char_u *save_autocmd_fname;
1950 int save_autocmd_fname_full;
1951 int save_autocmd_bufnr;
1952 char_u *save_autocmd_match;
1953 int save_autocmd_busy;
1954 int save_autocmd_nested;
1955 static int nesting = 0;
LemonBoyeca7c602022-04-14 15:39:43 +01001956 AutoPatCmd_T patcmd;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001957 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001958 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001959#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001960 funccal_entry_T funccal_entry;
1961 char_u *save_cmdarg;
1962 long save_cmdbang;
1963#endif
1964 static int filechangeshell_busy = FALSE;
1965#ifdef FEAT_PROFILE
1966 proftime_T wait_time;
1967#endif
1968 int did_save_redobuff = FALSE;
1969 save_redo_T save_redo;
1970 int save_KeyTyped = KeyTyped;
ichizokc3f91c02021-12-17 09:44:33 +00001971 int save_did_emsg;
Bram Moolenaare31ee862020-01-07 20:59:34 +01001972 ESTACK_CHECK_DECLARATION
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001973
1974 /*
1975 * Quickly return if there are no autocommands for this event or
1976 * autocommands are blocked.
1977 */
1978 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
1979 || autocmd_blocked > 0)
1980 goto BYPASS_AU;
1981
1982 /*
1983 * When autocommands are busy, new autocommands are only executed when
1984 * explicitly enabled with the "nested" flag.
1985 */
1986 if (autocmd_busy && !(force || autocmd_nested))
1987 goto BYPASS_AU;
1988
1989#ifdef FEAT_EVAL
1990 /*
1991 * Quickly return when immediately aborting on error, or when an interrupt
1992 * occurred or an exception was thrown but not caught.
1993 */
1994 if (aborting())
1995 goto BYPASS_AU;
1996#endif
1997
1998 /*
1999 * FileChangedShell never nests, because it can create an endless loop.
2000 */
2001 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
2002 || event == EVENT_FILECHANGEDSHELLPOST))
2003 goto BYPASS_AU;
2004
2005 /*
2006 * Ignore events in 'eventignore'.
2007 */
2008 if (event_ignored(event))
2009 goto BYPASS_AU;
2010
2011 /*
2012 * Allow nesting of autocommands, but restrict the depth, because it's
2013 * possible to create an endless loop.
2014 */
2015 if (nesting == 10)
2016 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00002017 emsg(_(e_autocommand_nesting_too_deep));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002018 goto BYPASS_AU;
2019 }
2020
2021 /*
2022 * Check if these autocommands are disabled. Used when doing ":all" or
2023 * ":ball".
2024 */
2025 if ( (autocmd_no_enter
2026 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
2027 || (autocmd_no_leave
2028 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
2029 goto BYPASS_AU;
2030
2031 /*
2032 * Save the autocmd_* variables and info about the current buffer.
2033 */
2034 save_autocmd_fname = autocmd_fname;
2035 save_autocmd_fname_full = autocmd_fname_full;
2036 save_autocmd_bufnr = autocmd_bufnr;
2037 save_autocmd_match = autocmd_match;
2038 save_autocmd_busy = autocmd_busy;
2039 save_autocmd_nested = autocmd_nested;
2040 save_changed = curbuf->b_changed;
2041 old_curbuf = curbuf;
2042
2043 /*
2044 * Set the file name to be used for <afile>.
2045 * Make a copy to avoid that changing a buffer name or directory makes it
2046 * invalid.
2047 */
2048 if (fname_io == NULL)
2049 {
2050 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002051 || event == EVENT_OPTIONSET
2052 || event == EVENT_MODECHANGED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002053 autocmd_fname = NULL;
2054 else if (fname != NULL && !ends_excmd(*fname))
2055 autocmd_fname = fname;
2056 else if (buf != NULL)
2057 autocmd_fname = buf->b_ffname;
2058 else
2059 autocmd_fname = NULL;
2060 }
2061 else
2062 autocmd_fname = fname_io;
2063 if (autocmd_fname != NULL)
2064 autocmd_fname = vim_strsave(autocmd_fname);
2065 autocmd_fname_full = FALSE; // call FullName_save() later
2066
2067 /*
2068 * Set the buffer number to be used for <abuf>.
2069 */
2070 if (buf == NULL)
2071 autocmd_bufnr = 0;
2072 else
2073 autocmd_bufnr = buf->b_fnum;
2074
2075 /*
2076 * When the file name is NULL or empty, use the file name of buffer "buf".
2077 * Always use the full path of the file name to match with, in case
2078 * "allow_dirs" is set.
2079 */
2080 if (fname == NULL || *fname == NUL)
2081 {
2082 if (buf == NULL)
2083 fname = NULL;
2084 else
2085 {
2086#ifdef FEAT_SYN_HL
2087 if (event == EVENT_SYNTAX)
2088 fname = buf->b_p_syn;
2089 else
2090#endif
2091 if (event == EVENT_FILETYPE)
2092 fname = buf->b_p_ft;
2093 else
2094 {
2095 if (buf->b_sfname != NULL)
2096 sfname = vim_strsave(buf->b_sfname);
2097 fname = buf->b_ffname;
2098 }
2099 }
2100 if (fname == NULL)
2101 fname = (char_u *)"";
2102 fname = vim_strsave(fname); // make a copy, so we can change it
2103 }
2104 else
2105 {
2106 sfname = vim_strsave(fname);
2107 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002108 // ColorScheme, QuickFixCmd*, DirChanged and similar.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002109 if (event == EVENT_FILETYPE
2110 || event == EVENT_SYNTAX
2111 || event == EVENT_CMDLINECHANGED
2112 || event == EVENT_CMDLINEENTER
2113 || event == EVENT_CMDLINELEAVE
2114 || event == EVENT_CMDWINENTER
2115 || event == EVENT_CMDWINLEAVE
2116 || event == EVENT_CMDUNDEFINED
2117 || event == EVENT_FUNCUNDEFINED
2118 || event == EVENT_REMOTEREPLY
2119 || event == EVENT_SPELLFILEMISSING
2120 || event == EVENT_QUICKFIXCMDPRE
2121 || event == EVENT_COLORSCHEME
2122 || event == EVENT_COLORSCHEMEPRE
2123 || event == EVENT_OPTIONSET
2124 || event == EVENT_QUICKFIXCMDPOST
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002125 || event == EVENT_DIRCHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002126 || event == EVENT_DIRCHANGEDPRE
naohiro ono23beefe2021-11-13 12:38:49 +00002127 || event == EVENT_MODECHANGED
zeertzjqc601d982022-10-10 13:46:15 +01002128 || event == EVENT_MENUPOPUP
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002129 || event == EVENT_USER
LemonBoy09371822022-04-08 15:18:45 +01002130 || event == EVENT_WINCLOSED
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00002131 || event == EVENT_WINRESIZED
LemonBoy09371822022-04-08 15:18:45 +01002132 || event == EVENT_WINSCROLLED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002133 {
2134 fname = vim_strsave(fname);
2135 autocmd_fname_full = TRUE; // don't expand it later
2136 }
2137 else
2138 fname = FullName_save(fname, FALSE);
2139 }
2140 if (fname == NULL) // out of memory
2141 {
2142 vim_free(sfname);
2143 retval = FALSE;
2144 goto BYPASS_AU;
2145 }
2146
2147#ifdef BACKSLASH_IN_FILENAME
2148 /*
2149 * Replace all backslashes with forward slashes. This makes the
2150 * autocommand patterns portable between Unix and MS-DOS.
2151 */
2152 if (sfname != NULL)
2153 forward_slash(sfname);
2154 forward_slash(fname);
2155#endif
2156
2157#ifdef VMS
2158 // remove version for correct match
2159 if (sfname != NULL)
2160 vms_remove_version(sfname);
2161 vms_remove_version(fname);
2162#endif
2163
2164 /*
2165 * Set the name to be used for <amatch>.
2166 */
2167 autocmd_match = fname;
2168
2169
2170 // Don't redraw while doing autocommands.
2171 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002172
2173 // name and lnum are filled in later
2174 estack_push(ETYPE_AUCMD, NULL, 0);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002175 ESTACK_CHECK_SETUP
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002176
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002177 save_current_sctx = current_sctx;
2178
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002179#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002180# ifdef FEAT_PROFILE
2181 if (do_profiling == PROF_YES)
2182 prof_child_enter(&wait_time); // doesn't count for the caller itself
2183# endif
2184
2185 // Don't use local function variables, if called from a function.
2186 save_funccal(&funccal_entry);
2187#endif
2188
2189 /*
2190 * When starting to execute autocommands, save the search patterns.
2191 */
2192 if (!autocmd_busy)
2193 {
2194 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002195 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002196 {
2197 saveRedobuff(&save_redo);
2198 did_save_redobuff = TRUE;
2199 }
2200 did_filetype = keep_filetype;
2201 }
2202
2203 /*
2204 * Note that we are applying autocmds. Some commands need to know.
2205 */
2206 autocmd_busy = TRUE;
2207 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2208 ++nesting; // see matching decrement below
2209
2210 // Remember that FileType was triggered. Used for did_filetype().
2211 if (event == EVENT_FILETYPE)
2212 did_filetype = TRUE;
2213
2214 tail = gettail(fname);
2215
2216 // Find first autocommand that matches
LemonBoyeca7c602022-04-14 15:39:43 +01002217 CLEAR_FIELD(patcmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002218 patcmd.curpat = first_autopat[(int)event];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002219 patcmd.group = group;
2220 patcmd.fname = fname;
2221 patcmd.sfname = sfname;
2222 patcmd.tail = tail;
2223 patcmd.event = event;
2224 patcmd.arg_bufnr = autocmd_bufnr;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002225 auto_next_pat(&patcmd, FALSE);
2226
2227 // found one, start executing the autocommands
2228 if (patcmd.curpat != NULL)
2229 {
2230 // add to active_apc_list
2231 patcmd.next = active_apc_list;
2232 active_apc_list = &patcmd;
2233
2234#ifdef FEAT_EVAL
2235 // set v:cmdarg (only when there is a matching pattern)
2236 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2237 if (eap != NULL)
2238 {
2239 save_cmdarg = set_cmdarg(eap, NULL);
2240 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2241 }
2242 else
2243 save_cmdarg = NULL; // avoid gcc warning
2244#endif
2245 retval = TRUE;
2246 // mark the last pattern, to avoid an endless loop when more patterns
2247 // are added when executing autocommands
2248 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2249 ap->last = FALSE;
2250 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002251
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002252 // Make sure cursor and topline are valid. The first time the current
2253 // values are saved, restored by reset_lnums(). When nested only the
2254 // values are corrected when needed.
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002255 if (nesting == 1)
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002256 check_lnums(TRUE);
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002257 else
2258 check_lnums_nested(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002259
ichizokc3f91c02021-12-17 09:44:33 +00002260 save_did_emsg = did_emsg;
2261
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002262 do_cmdline(NULL, getnextac, (void *)&patcmd,
2263 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002264
ichizokc3f91c02021-12-17 09:44:33 +00002265 did_emsg += save_did_emsg;
2266
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002267 if (nesting == 1)
2268 // restore cursor and topline, unless they were changed
2269 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002270
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002271#ifdef FEAT_EVAL
2272 if (eap != NULL)
2273 {
2274 (void)set_cmdarg(NULL, save_cmdarg);
2275 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2276 }
2277#endif
2278 // delete from active_apc_list
2279 if (active_apc_list == &patcmd) // just in case
2280 active_apc_list = patcmd.next;
2281 }
2282
2283 --RedrawingDisabled;
2284 autocmd_busy = save_autocmd_busy;
2285 filechangeshell_busy = FALSE;
2286 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002287 vim_free(SOURCING_NAME);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002288 ESTACK_CHECK_NOW
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002289 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002290 vim_free(autocmd_fname);
2291 autocmd_fname = save_autocmd_fname;
2292 autocmd_fname_full = save_autocmd_fname_full;
2293 autocmd_bufnr = save_autocmd_bufnr;
2294 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002295 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002296#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002297 restore_funccal();
2298# ifdef FEAT_PROFILE
2299 if (do_profiling == PROF_YES)
2300 prof_child_exit(&wait_time);
2301# endif
2302#endif
2303 KeyTyped = save_KeyTyped;
2304 vim_free(fname);
2305 vim_free(sfname);
2306 --nesting; // see matching increment above
2307
2308 /*
2309 * When stopping to execute autocommands, restore the search patterns and
2310 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2311 * free any windows in the au_pending_free_win list.
2312 */
2313 if (!autocmd_busy)
2314 {
2315 restore_search_patterns();
2316 if (did_save_redobuff)
2317 restoreRedobuff(&save_redo);
2318 did_filetype = FALSE;
2319 while (au_pending_free_buf != NULL)
2320 {
2321 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002322
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002323 vim_free(au_pending_free_buf);
2324 au_pending_free_buf = b;
2325 }
2326 while (au_pending_free_win != NULL)
2327 {
2328 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002329
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002330 vim_free(au_pending_free_win);
2331 au_pending_free_win = w;
2332 }
2333 }
2334
2335 /*
2336 * Some events don't set or reset the Changed flag.
2337 * Check if still in the same buffer!
2338 */
2339 if (curbuf == old_curbuf
2340 && (event == EVENT_BUFREADPOST
2341 || event == EVENT_BUFWRITEPOST
2342 || event == EVENT_FILEAPPENDPOST
2343 || event == EVENT_VIMLEAVE
2344 || event == EVENT_VIMLEAVEPRE))
2345 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002346 if (curbuf->b_changed != save_changed)
2347 need_maketitle = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002348 curbuf->b_changed = save_changed;
2349 }
2350
2351 au_cleanup(); // may really delete removed patterns/commands now
2352
2353BYPASS_AU:
2354 // When wiping out a buffer make sure all its buffer-local autocommands
2355 // are deleted.
2356 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2357 aubuflocal_remove(buf);
2358
2359 if (retval == OK && event == EVENT_FILETYPE)
2360 au_did_filetype = TRUE;
2361
2362 return retval;
2363}
2364
2365# ifdef FEAT_EVAL
2366static char_u *old_termresponse = NULL;
2367# endif
2368
2369/*
2370 * Block triggering autocommands until unblock_autocmd() is called.
2371 * Can be used recursively, so long as it's symmetric.
2372 */
2373 void
2374block_autocmds(void)
2375{
2376# ifdef FEAT_EVAL
2377 // Remember the value of v:termresponse.
2378 if (autocmd_blocked == 0)
2379 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
2380# endif
2381 ++autocmd_blocked;
2382}
2383
2384 void
2385unblock_autocmds(void)
2386{
2387 --autocmd_blocked;
2388
2389# ifdef FEAT_EVAL
2390 // When v:termresponse was set while autocommands were blocked, trigger
2391 // the autocommands now. Esp. useful when executing a shell command
2392 // during startup (vimdiff).
2393 if (autocmd_blocked == 0
2394 && get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2395 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2396# endif
2397}
2398
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002399 int
2400is_autocmd_blocked(void)
2401{
2402 return autocmd_blocked != 0;
2403}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002404
2405/*
2406 * Find next autocommand pattern that matches.
2407 */
2408 static void
2409auto_next_pat(
LemonBoyeca7c602022-04-14 15:39:43 +01002410 AutoPatCmd_T *apc,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002411 int stop_at_last) // stop when 'last' flag is set
2412{
2413 AutoPat *ap;
2414 AutoCmd *cp;
2415 char_u *name;
2416 char *s;
LemonBoyeca7c602022-04-14 15:39:43 +01002417 estack_T *entry;
2418 char_u *namep;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002419
LemonBoyeca7c602022-04-14 15:39:43 +01002420 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
2421
2422 // Clear the exestack entry for this ETYPE_AUCMD entry.
2423 VIM_CLEAR(entry->es_name);
2424 entry->es_info.aucmd = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002425
2426 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2427 {
2428 apc->curpat = NULL;
2429
2430 // Only use a pattern when it has not been removed, has commands and
2431 // the group matches. For buffer-local autocommands only check the
2432 // buffer number.
2433 if (ap->pat != NULL && ap->cmds != NULL
2434 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2435 {
2436 // execution-condition
2437 if (ap->buflocal_nr == 0
2438 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2439 apc->sfname, apc->tail, ap->allow_dirs))
2440 : ap->buflocal_nr == apc->arg_bufnr)
2441 {
2442 name = event_nr2name(apc->event);
2443 s = _("%s Autocommands for \"%s\"");
LemonBoyeca7c602022-04-14 15:39:43 +01002444 namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1);
2445 if (namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002446 {
LemonBoyeca7c602022-04-14 15:39:43 +01002447 sprintf((char *)namep, s, (char *)name, (char *)ap->pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002448 if (p_verbose >= 8)
2449 {
2450 verbose_enter();
LemonBoyeca7c602022-04-14 15:39:43 +01002451 smsg(_("Executing %s"), namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002452 verbose_leave();
2453 }
2454 }
2455
LemonBoyeca7c602022-04-14 15:39:43 +01002456 // Update the exestack entry for this autocmd.
2457 entry->es_name = namep;
2458 entry->es_info.aucmd = apc;
2459
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002460 apc->curpat = ap;
2461 apc->nextcmd = ap->cmds;
2462 // mark last command
2463 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2464 cp->last = FALSE;
2465 cp->last = TRUE;
2466 }
2467 line_breakcheck();
2468 if (apc->curpat != NULL) // found a match
2469 break;
2470 }
2471 if (stop_at_last && ap->last)
2472 break;
2473 }
2474}
2475
2476/*
LemonBoyeca7c602022-04-14 15:39:43 +01002477 * Get the script context where autocommand "acp" is defined.
2478 */
2479 sctx_T *
2480acp_script_ctx(AutoPatCmd_T *acp)
2481{
2482 return &acp->script_ctx;
2483}
2484
2485/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002486 * Get next autocommand command.
2487 * Called by do_cmdline() to get the next line for ":if".
2488 * Returns allocated string, or NULL for end of autocommands.
2489 */
2490 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002491getnextac(
2492 int c UNUSED,
2493 void *cookie,
2494 int indent UNUSED,
2495 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002496{
LemonBoyeca7c602022-04-14 15:39:43 +01002497 AutoPatCmd_T *acp = (AutoPatCmd_T *)cookie;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002498 char_u *retval;
2499 AutoCmd *ac;
2500
2501 // Can be called again after returning the last line.
2502 if (acp->curpat == NULL)
2503 return NULL;
2504
2505 // repeat until we find an autocommand to execute
2506 for (;;)
2507 {
2508 // skip removed commands
2509 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2510 if (acp->nextcmd->last)
2511 acp->nextcmd = NULL;
2512 else
2513 acp->nextcmd = acp->nextcmd->next;
2514
2515 if (acp->nextcmd != NULL)
2516 break;
2517
2518 // at end of commands, find next pattern that matches
2519 if (acp->curpat->last)
2520 acp->curpat = NULL;
2521 else
2522 acp->curpat = acp->curpat->next;
2523 if (acp->curpat != NULL)
2524 auto_next_pat(acp, TRUE);
2525 if (acp->curpat == NULL)
2526 return NULL;
2527 }
2528
2529 ac = acp->nextcmd;
2530
2531 if (p_verbose >= 9)
2532 {
2533 verbose_enter_scroll();
2534 smsg(_("autocommand %s"), ac->cmd);
2535 msg_puts("\n"); // don't overwrite this either
2536 verbose_leave_scroll();
2537 }
2538 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002539 // Remove one-shot ("once") autocmd in anticipation of its execution.
2540 if (ac->once)
2541 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002542 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002543 current_sctx = ac->script_ctx;
LemonBoyeca7c602022-04-14 15:39:43 +01002544 acp->script_ctx = current_sctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002545 if (ac->last)
2546 acp->nextcmd = NULL;
2547 else
2548 acp->nextcmd = ac->next;
2549 return retval;
2550}
2551
2552/*
2553 * Return TRUE if there is a matching autocommand for "fname".
2554 * To account for buffer-local autocommands, function needs to know
2555 * in which buffer the file will be opened.
2556 */
2557 int
2558has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2559{
2560 AutoPat *ap;
2561 char_u *fname;
2562 char_u *tail = gettail(sfname);
2563 int retval = FALSE;
2564
2565 fname = FullName_save(sfname, FALSE);
2566 if (fname == NULL)
2567 return FALSE;
2568
2569#ifdef BACKSLASH_IN_FILENAME
2570 /*
2571 * Replace all backslashes with forward slashes. This makes the
2572 * autocommand patterns portable between Unix and MS-DOS.
2573 */
2574 sfname = vim_strsave(sfname);
2575 if (sfname != NULL)
2576 forward_slash(sfname);
2577 forward_slash(fname);
2578#endif
2579
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002580 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002581 if (ap->pat != NULL && ap->cmds != NULL
2582 && (ap->buflocal_nr == 0
2583 ? match_file_pat(NULL, &ap->reg_prog,
2584 fname, sfname, tail, ap->allow_dirs)
2585 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2586 ))
2587 {
2588 retval = TRUE;
2589 break;
2590 }
2591
2592 vim_free(fname);
2593#ifdef BACKSLASH_IN_FILENAME
2594 vim_free(sfname);
2595#endif
2596
2597 return retval;
2598}
2599
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002600/*
2601 * Function given to ExpandGeneric() to obtain the list of autocommand group
2602 * names.
2603 */
2604 char_u *
2605get_augroup_name(expand_T *xp UNUSED, int idx)
2606{
2607 if (idx == augroups.ga_len) // add "END" add the end
2608 return (char_u *)"END";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002609 if (idx < 0 || idx >= augroups.ga_len) // end of list
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002610 return NULL;
2611 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2612 // skip deleted entries
2613 return (char_u *)"";
2614 return AUGROUP_NAME(idx); // return a name
2615}
2616
2617static int include_groups = FALSE;
2618
2619 char_u *
2620set_context_in_autocmd(
2621 expand_T *xp,
2622 char_u *arg,
2623 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2624{
2625 char_u *p;
2626 int group;
2627
2628 // check for a group name, skip it if present
2629 include_groups = FALSE;
2630 p = arg;
2631 group = au_get_grouparg(&arg);
2632 if (group == AUGROUP_ERROR)
2633 return NULL;
2634 // If there only is a group name that's what we expand.
2635 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2636 {
2637 arg = p;
2638 group = AUGROUP_ALL;
2639 }
2640
2641 // skip over event name
2642 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2643 if (*p == ',')
2644 arg = p + 1;
2645 if (*p == NUL)
2646 {
2647 if (group == AUGROUP_ALL)
2648 include_groups = TRUE;
2649 xp->xp_context = EXPAND_EVENTS; // expand event name
2650 xp->xp_pattern = arg;
2651 return NULL;
2652 }
2653
2654 // skip over pattern
2655 arg = skipwhite(p);
2656 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2657 arg++;
2658 if (*arg)
2659 return arg; // expand (next) command
2660
2661 if (doautocmd)
2662 xp->xp_context = EXPAND_FILES; // expand file names
2663 else
2664 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2665 return NULL;
2666}
2667
2668/*
2669 * Function given to ExpandGeneric() to obtain the list of event names.
2670 */
2671 char_u *
2672get_event_name(expand_T *xp UNUSED, int idx)
2673{
2674 if (idx < augroups.ga_len) // First list group names, if wanted
2675 {
2676 if (!include_groups || AUGROUP_NAME(idx) == NULL
2677 || AUGROUP_NAME(idx) == get_deleted_augroup())
2678 return (char_u *)""; // skip deleted entries
2679 return AUGROUP_NAME(idx); // return a name
2680 }
2681 return (char_u *)event_names[idx - augroups.ga_len].name;
2682}
2683
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002684
2685#if defined(FEAT_EVAL) || defined(PROTO)
2686/*
2687 * Return TRUE if autocmd is supported.
2688 */
2689 int
2690autocmd_supported(char_u *name)
2691{
2692 char_u *p;
2693
2694 return (event_name2nr(name, &p) != NUM_EVENTS);
2695}
2696
2697/*
2698 * Return TRUE if an autocommand is defined for a group, event and
2699 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2700 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2701 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2702 * Used for:
2703 * exists("#Group") or
2704 * exists("#Group#Event") or
2705 * exists("#Group#Event#pat") or
2706 * exists("#Event") or
2707 * exists("#Event#pat")
2708 */
2709 int
2710au_exists(char_u *arg)
2711{
2712 char_u *arg_save;
2713 char_u *pattern = NULL;
2714 char_u *event_name;
2715 char_u *p;
2716 event_T event;
2717 AutoPat *ap;
2718 buf_T *buflocal_buf = NULL;
2719 int group;
2720 int retval = FALSE;
2721
2722 // Make a copy so that we can change the '#' chars to a NUL.
2723 arg_save = vim_strsave(arg);
2724 if (arg_save == NULL)
2725 return FALSE;
2726 p = vim_strchr(arg_save, '#');
2727 if (p != NULL)
2728 *p++ = NUL;
2729
2730 // First, look for an autocmd group name
2731 group = au_find_group(arg_save);
2732 if (group == AUGROUP_ERROR)
2733 {
2734 // Didn't match a group name, assume the first argument is an event.
2735 group = AUGROUP_ALL;
2736 event_name = arg_save;
2737 }
2738 else
2739 {
2740 if (p == NULL)
2741 {
2742 // "Group": group name is present and it's recognized
2743 retval = TRUE;
2744 goto theend;
2745 }
2746
2747 // Must be "Group#Event" or "Group#Event#pat".
2748 event_name = p;
2749 p = vim_strchr(event_name, '#');
2750 if (p != NULL)
2751 *p++ = NUL; // "Group#Event#pat"
2752 }
2753
2754 pattern = p; // "pattern" is NULL when there is no pattern
2755
2756 // find the index (enum) for the event name
2757 event = event_name2nr(event_name, &p);
2758
2759 // return FALSE if the event name is not recognized
2760 if (event == NUM_EVENTS)
2761 goto theend;
2762
2763 // Find the first autocommand for this event.
2764 // If there isn't any, return FALSE;
2765 // If there is one and no pattern given, return TRUE;
2766 ap = first_autopat[(int)event];
2767 if (ap == NULL)
2768 goto theend;
2769
2770 // if pattern is "<buffer>", special handling is needed which uses curbuf
2771 // for pattern "<buffer=N>, fnamecmp() will work fine
2772 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2773 buflocal_buf = curbuf;
2774
2775 // Check if there is an autocommand with the given pattern.
2776 for ( ; ap != NULL; ap = ap->next)
2777 // only use a pattern when it has not been removed and has commands.
2778 // For buffer-local autocommands, fnamecmp() works fine.
2779 if (ap->pat != NULL && ap->cmds != NULL
2780 && (group == AUGROUP_ALL || ap->group == group)
2781 && (pattern == NULL
2782 || (buflocal_buf == NULL
2783 ? fnamecmp(ap->pat, pattern) == 0
2784 : ap->buflocal_nr == buflocal_buf->b_fnum)))
2785 {
2786 retval = TRUE;
2787 break;
2788 }
2789
2790theend:
2791 vim_free(arg_save);
2792 return retval;
2793}
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002794
2795/*
2796 * autocmd_add() and autocmd_delete() functions
2797 */
2798 static void
2799autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
2800{
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002801 list_T *aucmd_list;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002802 listitem_T *li;
2803 dict_T *event_dict;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002804 dictitem_T *di;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002805 char_u *event_name = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002806 list_T *event_list;
2807 listitem_T *eli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002808 event_T event;
2809 char_u *group_name = NULL;
2810 int group;
2811 char_u *pat = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002812 list_T *pat_list;
2813 listitem_T *pli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002814 char_u *cmd = NULL;
2815 char_u *end;
2816 int once;
2817 int nested;
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01002818 int replace; // replace the cmd for a group/event
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002819 int retval = VVAL_TRUE;
2820 int save_augroup = current_augroup;
2821
2822 rettv->v_type = VAR_BOOL;
2823 rettv->vval.v_number = VVAL_FALSE;
2824
2825 if (check_for_list_arg(argvars, 0) == FAIL)
2826 return;
2827
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002828 aucmd_list = argvars[0].vval.v_list;
2829 if (aucmd_list == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002830 return;
2831
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002832 FOR_ALL_LIST_ITEMS(aucmd_list, li)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002833 {
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002834 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002835 VIM_CLEAR(cmd);
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002836 event_name = NULL;
2837 event_list = NULL;
2838 pat = NULL;
2839 pat_list = NULL;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002840
2841 if (li->li_tv.v_type != VAR_DICT)
2842 continue;
2843
2844 event_dict = li->li_tv.vval.v_dict;
2845 if (event_dict == NULL)
2846 continue;
2847
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002848 di = dict_find(event_dict, (char_u *)"event", -1);
2849 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002850 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002851 if (di->di_tv.v_type == VAR_STRING)
2852 {
2853 event_name = di->di_tv.vval.v_string;
2854 if (event_name == NULL)
2855 {
2856 emsg(_(e_string_required));
2857 continue;
2858 }
2859 }
2860 else if (di->di_tv.v_type == VAR_LIST)
2861 {
2862 event_list = di->di_tv.vval.v_list;
2863 if (event_list == NULL)
2864 {
2865 emsg(_(e_list_required));
2866 continue;
2867 }
2868 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002869 else
2870 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002871 emsg(_(e_string_or_list_expected));
2872 continue;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002873 }
2874 }
2875
Bram Moolenaard61efa52022-07-23 09:52:04 +01002876 group_name = dict_get_string(event_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002877 if (group_name == NULL || *group_name == NUL)
2878 // if the autocmd group name is not specified, then use the current
2879 // autocmd group
2880 group = current_augroup;
2881 else
2882 {
2883 group = au_find_group(group_name);
2884 if (group == AUGROUP_ERROR)
2885 {
2886 if (delete)
2887 {
2888 semsg(_(e_no_such_group_str), group_name);
2889 retval = VVAL_FALSE;
2890 break;
2891 }
2892 // group is not found, create it now
2893 group = au_new_group(group_name);
2894 if (group == AUGROUP_ERROR)
2895 {
2896 semsg(_(e_no_such_group_str), group_name);
2897 retval = VVAL_FALSE;
2898 break;
2899 }
2900
2901 current_augroup = group;
2902 }
2903 }
2904
2905 // if a buffer number is specified, then generate a pattern of the form
2906 // "<buffer=n>. Otherwise, use the pattern supplied by the user.
2907 if (dict_has_key(event_dict, "bufnr"))
2908 {
2909 varnumber_T bnum;
2910
Bram Moolenaard61efa52022-07-23 09:52:04 +01002911 bnum = dict_get_number_def(event_dict, "bufnr", -1);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002912 if (bnum == -1)
2913 continue;
2914
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002915 vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
2916 pat = IObuff;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002917 }
2918 else
2919 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002920 di = dict_find(event_dict, (char_u *)"pattern", -1);
2921 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002922 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002923 if (di->di_tv.v_type == VAR_STRING)
2924 {
2925 pat = di->di_tv.vval.v_string;
2926 if (pat == NULL)
2927 {
2928 emsg(_(e_string_required));
2929 continue;
2930 }
2931 }
2932 else if (di->di_tv.v_type == VAR_LIST)
2933 {
2934 pat_list = di->di_tv.vval.v_list;
2935 if (pat_list == NULL)
2936 {
2937 emsg(_(e_list_required));
2938 continue;
2939 }
2940 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002941 else
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002942 {
2943 emsg(_(e_string_or_list_expected));
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002944 continue;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002945 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002946 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002947 else if (delete)
2948 pat = (char_u *)"";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002949 }
2950
Bram Moolenaard61efa52022-07-23 09:52:04 +01002951 once = dict_get_bool(event_dict, "once", FALSE);
2952 nested = dict_get_bool(event_dict, "nested", FALSE);
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01002953 // if 'replace' is true, then remove all the commands associated with
2954 // this autocmd event/group and add the new command.
Bram Moolenaard61efa52022-07-23 09:52:04 +01002955 replace = dict_get_bool(event_dict, "replace", FALSE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002956
Bram Moolenaard61efa52022-07-23 09:52:04 +01002957 cmd = dict_get_string(event_dict, "cmd", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002958 if (cmd == NULL)
2959 {
2960 if (delete)
2961 cmd = vim_strsave((char_u *)"");
2962 else
2963 continue;
2964 }
2965
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002966 if (delete && (event_name == NULL
2967 || (event_name[0] == '*' && event_name[1] == NUL)))
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002968 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002969 // if the event name is not specified or '*', delete all the events
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002970 for (event = (event_T)0; (int)event < NUM_EVENTS;
2971 event = (event_T)((int)event + 1))
2972 {
2973 if (do_autocmd_event(event, pat, once, nested, cmd, delete,
2974 group, 0) == FAIL)
2975 {
2976 retval = VVAL_FALSE;
2977 break;
2978 }
2979 }
2980 }
2981 else
2982 {
Bram Moolenaar968443e2022-05-27 21:16:34 +01002983 char_u *p = NULL;
2984
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002985 eli = NULL;
2986 end = NULL;
2987 while (TRUE)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002988 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002989 if (event_list != NULL)
2990 {
2991 if (eli == NULL)
2992 eli = event_list->lv_first;
2993 else
2994 eli = eli->li_next;
2995 if (eli == NULL)
2996 break;
2997 if (eli->li_tv.v_type != VAR_STRING
Bram Moolenaar882476a2022-06-01 16:02:38 +01002998 || (p = eli->li_tv.vval.v_string) == NULL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002999 {
3000 emsg(_(e_string_required));
Bram Moolenaar882476a2022-06-01 16:02:38 +01003001 break;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003002 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003003 }
3004 else
3005 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003006 if (p == NULL)
3007 p = event_name;
3008 if (p == NULL || *p == NUL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003009 break;
3010 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003011
3012 event = event_name2nr(p, &end);
3013 if (event == NUM_EVENTS || *end != NUL)
3014 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003015 // this also catches something following a valid event name
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003016 semsg(_(e_no_such_event_str), p);
3017 retval = VVAL_FALSE;
3018 break;
3019 }
3020 if (pat != NULL)
3021 {
3022 if (do_autocmd_event(event, pat, once, nested, cmd,
3023 delete | replace, group, 0) == FAIL)
3024 {
3025 retval = VVAL_FALSE;
3026 break;
3027 }
3028 }
3029 else if (pat_list != NULL)
3030 {
3031 FOR_ALL_LIST_ITEMS(pat_list, pli)
3032 {
3033 if (pli->li_tv.v_type != VAR_STRING
3034 || pli->li_tv.vval.v_string == NULL)
3035 {
3036 emsg(_(e_string_required));
3037 continue;
3038 }
3039 if (do_autocmd_event(event,
3040 pli->li_tv.vval.v_string, once, nested,
3041 cmd, delete | replace, group, 0) ==
3042 FAIL)
3043 {
3044 retval = VVAL_FALSE;
3045 break;
3046 }
3047 }
3048 if (retval == VVAL_FALSE)
3049 break;
3050 }
3051 if (event_name != NULL)
3052 p = end;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003053 }
3054 }
3055
3056 // if only the autocmd group name is specified for delete and the
3057 // autocmd event, pattern and cmd are not specified, then delete the
3058 // autocmd group.
3059 if (delete && group_name != NULL &&
3060 (event_name == NULL || event_name[0] == NUL)
3061 && (pat == NULL || pat[0] == NUL)
3062 && (cmd == NULL || cmd[0] == NUL))
3063 au_del_group(group_name);
3064 }
3065
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003066 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003067 VIM_CLEAR(cmd);
3068
3069 current_augroup = save_augroup;
3070 rettv->vval.v_number = retval;
3071}
3072
3073/*
3074 * autocmd_add() function
3075 */
3076 void
3077f_autocmd_add(typval_T *argvars, typval_T *rettv)
3078{
3079 autocmd_add_or_delete(argvars, rettv, FALSE);
3080}
3081
3082/*
3083 * autocmd_delete() function
3084 */
3085 void
3086f_autocmd_delete(typval_T *argvars, typval_T *rettv)
3087{
3088 autocmd_add_or_delete(argvars, rettv, TRUE);
3089}
3090
3091/*
3092 * autocmd_get() function
3093 * Returns a List of autocmds.
3094 */
3095 void
3096f_autocmd_get(typval_T *argvars, typval_T *rettv)
3097{
3098 event_T event_arg = NUM_EVENTS;
3099 event_T event;
3100 AutoPat *ap;
3101 AutoCmd *ac;
3102 list_T *event_list;
3103 dict_T *event_dict;
3104 char_u *event_name = NULL;
3105 char_u *pat = NULL;
3106 char_u *name = NULL;
3107 int group = AUGROUP_ALL;
3108
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01003109 if (rettv_list_alloc(rettv) == FAIL)
3110 return;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003111 if (check_for_opt_dict_arg(argvars, 0) == FAIL)
3112 return;
3113
3114 if (argvars[0].v_type == VAR_DICT)
3115 {
3116 // return only the autocmds in the specified group
3117 if (dict_has_key(argvars[0].vval.v_dict, "group"))
3118 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003119 name = dict_get_string(argvars[0].vval.v_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003120 if (name == NULL)
3121 return;
3122
3123 if (*name == NUL)
3124 group = AUGROUP_DEFAULT;
3125 else
3126 {
3127 group = au_find_group(name);
3128 if (group == AUGROUP_ERROR)
3129 {
3130 semsg(_(e_no_such_group_str), name);
3131 vim_free(name);
3132 return;
3133 }
3134 }
3135 vim_free(name);
3136 }
3137
3138 // return only the autocmds for the specified event
3139 if (dict_has_key(argvars[0].vval.v_dict, "event"))
3140 {
3141 int i;
3142
Bram Moolenaard61efa52022-07-23 09:52:04 +01003143 name = dict_get_string(argvars[0].vval.v_dict, "event", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003144 if (name == NULL)
3145 return;
3146
3147 if (name[0] == '*' && name[1] == NUL)
3148 event_arg = NUM_EVENTS;
3149 else
3150 {
3151 for (i = 0; event_names[i].name != NULL; i++)
3152 if (STRICMP(event_names[i].name, name) == 0)
3153 break;
3154 if (event_names[i].name == NULL)
3155 {
3156 semsg(_(e_no_such_event_str), name);
3157 vim_free(name);
3158 return;
3159 }
3160 event_arg = event_names[i].event;
3161 }
3162 vim_free(name);
3163 }
3164
3165 // return only the autocmds for the specified pattern
3166 if (dict_has_key(argvars[0].vval.v_dict, "pattern"))
3167 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003168 pat = dict_get_string(argvars[0].vval.v_dict, "pattern", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003169 if (pat == NULL)
3170 return;
3171 }
3172 }
3173
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003174 event_list = rettv->vval.v_list;
3175
3176 // iterate through all the autocmd events
3177 for (event = (event_T)0; (int)event < NUM_EVENTS;
3178 event = (event_T)((int)event + 1))
3179 {
3180 if (event_arg != NUM_EVENTS && event != event_arg)
3181 continue;
3182
3183 event_name = event_nr2name(event);
3184
3185 // iterate through all the patterns for this autocmd event
3186 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
3187 {
3188 char_u *group_name;
3189
3190 if (group != AUGROUP_ALL && group != ap->group)
3191 continue;
3192
3193 if (pat != NULL && STRCMP(pat, ap->pat) != 0)
3194 continue;
3195
3196 group_name = get_augroup_name(NULL, ap->group);
3197
3198 // iterate through all the commands for this pattern and add one
3199 // item for each cmd.
3200 for (ac = ap->cmds; ac != NULL; ac = ac->next)
3201 {
3202 event_dict = dict_alloc();
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003203 if (event_dict == NULL
3204 || list_append_dict(event_list, event_dict) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003205 return;
3206
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003207 if (dict_add_string(event_dict, "event", event_name) == FAIL
3208 || dict_add_string(event_dict, "group",
3209 group_name == NULL ? (char_u *)""
3210 : group_name) == FAIL
3211 || (ap->buflocal_nr != 0
3212 && (dict_add_number(event_dict, "bufnr",
3213 ap->buflocal_nr) == FAIL))
3214 || dict_add_string(event_dict, "pattern",
3215 ap->pat) == FAIL
3216 || dict_add_string(event_dict, "cmd", ac->cmd) == FAIL
3217 || dict_add_bool(event_dict, "once", ac->once) == FAIL
3218 || dict_add_bool(event_dict, "nested",
3219 ac->nested) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003220 return;
3221 }
3222 }
3223 }
3224
3225 vim_free(pat);
3226}
3227
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01003228#endif