blob: 3a081281333c9fbac987a7e55c71655c866b7fa2 [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
Bram Moolenaare76062c2022-11-28 18:51:43 +0000632 void
633autocmd_init(void)
634{
635 CLEAR_FIELD(aucmd_win);
636}
637
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100638#if defined(EXITFREE) || defined(PROTO)
639 void
640free_all_autocmds(void)
641{
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100642 char_u *s;
643
644 for (current_augroup = -1; current_augroup < augroups.ga_len;
645 ++current_augroup)
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200646 do_autocmd(NULL, (char_u *)"", TRUE);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100647
Bram Moolenaare76062c2022-11-28 18:51:43 +0000648 for (int i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100649 {
650 s = ((char_u **)(augroups.ga_data))[i];
651 if (s != get_deleted_augroup())
652 vim_free(s);
653 }
654 ga_clear(&augroups);
Bram Moolenaare76062c2022-11-28 18:51:43 +0000655
656 for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
657 if (aucmd_win[i].auc_win_used)
658 {
659 aucmd_win[i].auc_win_used = FALSE;
660 win_remove(aucmd_win[i].auc_win, NULL);
661 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100662}
663#endif
664
665/*
Bram Moolenaare76062c2022-11-28 18:51:43 +0000666 * Return TRUE if "win" is an active entry in aucmd_win[].
667 */
668 int
669is_aucmd_win(win_T *win)
670{
671 for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
672 if (aucmd_win[i].auc_win_used && aucmd_win[i].auc_win == win)
673 return TRUE;
674 return FALSE;
675}
676
677/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100678 * Return the event number for event name "start".
679 * Return NUM_EVENTS if the event name was not found.
680 * Return a pointer to the next event name in "end".
681 */
682 static event_T
683event_name2nr(char_u *start, char_u **end)
684{
685 char_u *p;
686 int i;
687 int len;
688
689 // the event name ends with end of line, '|', a blank or a comma
690 for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
691 ;
692 for (i = 0; event_names[i].name != NULL; ++i)
693 {
694 len = (int)STRLEN(event_names[i].name);
695 if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0)
696 break;
697 }
698 if (*p == ',')
699 ++p;
700 *end = p;
701 if (event_names[i].name == NULL)
702 return NUM_EVENTS;
703 return event_names[i].event;
704}
705
706/*
707 * Return the name for event "event".
708 */
709 static char_u *
710event_nr2name(event_T event)
711{
712 int i;
713
714 for (i = 0; event_names[i].name != NULL; ++i)
715 if (event_names[i].event == event)
716 return (char_u *)event_names[i].name;
717 return (char_u *)"Unknown";
718}
719
720/*
721 * Scan over the events. "*" stands for all events.
722 */
723 static char_u *
724find_end_event(
725 char_u *arg,
726 int have_group) // TRUE when group name was found
727{
728 char_u *pat;
729 char_u *p;
730
731 if (*arg == '*')
732 {
733 if (arg[1] && !VIM_ISWHITE(arg[1]))
734 {
Bram Moolenaar6d057012021-12-31 18:49:43 +0000735 semsg(_(e_illegal_character_after_star_str), arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100736 return NULL;
737 }
738 pat = arg + 1;
739 }
740 else
741 {
742 for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p)
743 {
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100744 if ((int)event_name2nr(pat, &p) >= NUM_EVENTS)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100745 {
746 if (have_group)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000747 semsg(_(e_no_such_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100748 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000749 semsg(_(e_no_such_group_or_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100750 return NULL;
751 }
752 }
753 }
754 return pat;
755}
756
757/*
758 * Return TRUE if "event" is included in 'eventignore'.
759 */
760 static int
761event_ignored(event_T event)
762{
763 char_u *p = p_ei;
764
765 while (*p != NUL)
766 {
767 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
768 return TRUE;
769 if (event_name2nr(p, &p) == event)
770 return TRUE;
771 }
772
773 return FALSE;
774}
775
776/*
777 * Return OK when the contents of p_ei is valid, FAIL otherwise.
778 */
779 int
780check_ei(void)
781{
782 char_u *p = p_ei;
783
784 while (*p)
785 {
786 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
787 {
788 p += 3;
789 if (*p == ',')
790 ++p;
791 }
792 else if (event_name2nr(p, &p) == NUM_EVENTS)
793 return FAIL;
794 }
795
796 return OK;
797}
798
799# if defined(FEAT_SYN_HL) || defined(PROTO)
800
801/*
802 * Add "what" to 'eventignore' to skip loading syntax highlighting for every
803 * buffer loaded into the window. "what" must start with a comma.
804 * Returns the old value of 'eventignore' in allocated memory.
805 */
806 char_u *
807au_event_disable(char *what)
808{
809 char_u *new_ei;
810 char_u *save_ei;
811
812 save_ei = vim_strsave(p_ei);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100813 if (save_ei == NULL)
814 return NULL;
815
816 new_ei = vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what));
817 if (new_ei == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100818 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100819 vim_free(save_ei);
820 return NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100821 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100822
823 if (*what == ',' && *p_ei == NUL)
824 STRCPY(new_ei, what + 1);
825 else
826 STRCAT(new_ei, what);
827 set_string_option_direct((char_u *)"ei", -1, new_ei,
828 OPT_FREE, SID_NONE);
829 vim_free(new_ei);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100830 return save_ei;
831}
832
833 void
834au_event_restore(char_u *old_ei)
835{
836 if (old_ei != NULL)
837 {
838 set_string_option_direct((char_u *)"ei", -1, old_ei,
839 OPT_FREE, SID_NONE);
840 vim_free(old_ei);
841 }
842}
Bram Moolenaarc667da52019-11-30 20:52:27 +0100843# endif // FEAT_SYN_HL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100844
845/*
846 * do_autocmd() -- implements the :autocmd command. Can be used in the
847 * following ways:
848 *
849 * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
850 * will be automatically executed for <event>
851 * when editing a file matching <pat>, in
852 * the current group.
853 * :autocmd <event> <pat> Show the autocommands associated with
854 * <event> and <pat>.
855 * :autocmd <event> Show the autocommands associated with
856 * <event>.
857 * :autocmd Show all autocommands.
858 * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
859 * <event> and <pat>, and add the command
860 * <cmd>, for the current group.
861 * :autocmd! <event> <pat> Remove all autocommands associated with
862 * <event> and <pat> for the current group.
863 * :autocmd! <event> Remove all autocommands associated with
864 * <event> for the current group.
865 * :autocmd! Remove ALL autocommands for the current
866 * group.
867 *
868 * Multiple events and patterns may be given separated by commas. Here are
869 * some examples:
870 * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
871 * :autocmd bufleave * set tw=79 nosmartindent ic infercase
872 *
873 * :autocmd * *.c show all autocommands for *.c files.
874 *
875 * Mostly a {group} argument can optionally appear before <event>.
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200876 * "eap" can be NULL.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100877 */
878 void
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200879do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100880{
881 char_u *arg = arg_in;
882 char_u *pat;
883 char_u *envpat = NULL;
884 char_u *cmd;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200885 int cmd_need_free = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100886 event_T event;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200887 char_u *tofree = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100888 int nested = FALSE;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200889 int once = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100890 int group;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200891 int i;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200892 int flags = 0;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100893
894 if (*arg == '|')
895 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000896 eap->nextcmd = arg + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100897 arg = (char_u *)"";
898 group = AUGROUP_ALL; // no argument, use all groups
899 }
900 else
901 {
902 /*
903 * Check for a legal group name. If not, use AUGROUP_ALL.
904 */
905 group = au_get_grouparg(&arg);
906 if (arg == NULL) // out of memory
907 return;
908 }
909
910 /*
911 * Scan over the events.
912 * If we find an illegal name, return here, don't do anything.
913 */
914 pat = find_end_event(arg, group != AUGROUP_ALL);
915 if (pat == NULL)
916 return;
917
918 pat = skipwhite(pat);
919 if (*pat == '|')
920 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000921 eap->nextcmd = pat + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100922 pat = (char_u *)"";
923 cmd = (char_u *)"";
924 }
925 else
926 {
927 /*
928 * Scan over the pattern. Put a NUL at the end.
929 */
930 cmd = pat;
931 while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
932 cmd++;
933 if (*cmd)
934 *cmd++ = NUL;
935
936 // Expand environment variables in the pattern. Set 'shellslash', we
937 // want forward slashes here.
938 if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
939 {
940#ifdef BACKSLASH_IN_FILENAME
941 int p_ssl_save = p_ssl;
942
943 p_ssl = TRUE;
944#endif
945 envpat = expand_env_save(pat);
946#ifdef BACKSLASH_IN_FILENAME
947 p_ssl = p_ssl_save;
948#endif
949 if (envpat != NULL)
950 pat = envpat;
951 }
952
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100953 cmd = skipwhite(cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200954 for (i = 0; i < 2; i++)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100955 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100956 if (*cmd == NUL)
957 continue;
958
959 // Check for "++once" flag.
960 if (STRNCMP(cmd, "++once", 6) == 0 && VIM_ISWHITE(cmd[6]))
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200961 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100962 if (once)
963 semsg(_(e_duplicate_argument_str), "++once");
964 once = TRUE;
965 cmd = skipwhite(cmd + 6);
966 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200967
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100968 // Check for "++nested" flag.
969 if ((STRNCMP(cmd, "++nested", 8) == 0 && VIM_ISWHITE(cmd[8])))
970 {
971 if (nested)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200972 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100973 semsg(_(e_duplicate_argument_str), "++nested");
974 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200975 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100976 nested = TRUE;
977 cmd = skipwhite(cmd + 8);
978 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200979
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100980 // Check for the old "nested" flag in legacy script.
981 if (STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
982 {
983 if (in_vim9script())
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200984 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100985 // If there ever is a :nested command this error should
986 // be removed and "nested" accepted as the start of the
987 // command.
988 emsg(_(e_invalid_command_nested_did_you_mean_plusplus_nested));
989 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200990 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100991 if (nested)
992 {
993 semsg(_(e_duplicate_argument_str), "nested");
994 return;
995 }
996 nested = TRUE;
997 cmd = skipwhite(cmd + 6);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200998 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100999 }
1000
1001 /*
1002 * Find the start of the commands.
1003 * Expand <sfile> in it.
1004 */
1005 if (*cmd != NUL)
1006 {
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001007 if (eap != NULL)
1008 // Read a {} block if it follows.
1009 cmd = may_get_cmd_block(eap, cmd, &tofree, &flags);
1010
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001011 cmd = expand_sfile(cmd);
1012 if (cmd == NULL) // some error
1013 return;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001014 cmd_need_free = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001015 }
1016 }
1017
1018 /*
1019 * Print header when showing autocommands.
1020 */
1021 if (!forceit && *cmd == NUL)
1022 // Highlight title
1023 msg_puts_title(_("\n--- Autocommands ---"));
1024
1025 /*
1026 * Loop over the events.
1027 */
1028 last_event = (event_T)-1; // for listing the event name
1029 last_group = AUGROUP_ERROR; // for listing the group name
1030 if (*arg == '*' || *arg == NUL || *arg == '|')
1031 {
Bram Moolenaarb6db1462021-12-24 19:24:47 +00001032 if (*cmd != NUL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001033 emsg(_(e_cannot_define_autocommands_for_all_events));
1034 else
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +01001035 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001036 event = (event_T)((int)event + 1))
1037 if (do_autocmd_event(event, pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001038 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001039 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001040 }
1041 else
1042 {
1043 while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
1044 if (do_autocmd_event(event_name2nr(arg, &arg), pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001045 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001046 break;
1047 }
1048
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001049 if (cmd_need_free)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001050 vim_free(cmd);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001051 vim_free(tofree);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001052 vim_free(envpat);
1053}
1054
1055/*
1056 * Find the group ID in a ":autocmd" or ":doautocmd" argument.
1057 * The "argp" argument is advanced to the following argument.
1058 *
1059 * Returns the group ID, AUGROUP_ERROR for error (out of memory).
1060 */
1061 static int
1062au_get_grouparg(char_u **argp)
1063{
1064 char_u *group_name;
1065 char_u *p;
1066 char_u *arg = *argp;
1067 int group = AUGROUP_ALL;
1068
1069 for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p)
1070 ;
1071 if (p > arg)
1072 {
Bram Moolenaardf44a272020-06-07 20:49:05 +02001073 group_name = vim_strnsave(arg, p - arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001074 if (group_name == NULL) // out of memory
1075 return AUGROUP_ERROR;
1076 group = au_find_group(group_name);
1077 if (group == AUGROUP_ERROR)
1078 group = AUGROUP_ALL; // no match, use all groups
1079 else
1080 *argp = skipwhite(p); // match, skip over group name
1081 vim_free(group_name);
1082 }
1083 return group;
1084}
1085
1086/*
1087 * do_autocmd() for one event.
1088 * If *pat == NUL do for all patterns.
1089 * If *cmd == NUL show entries.
1090 * If forceit == TRUE delete entries.
1091 * If group is not AUGROUP_ALL, only use this group.
1092 */
1093 static int
1094do_autocmd_event(
1095 event_T event,
1096 char_u *pat,
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001097 int once,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001098 int nested,
1099 char_u *cmd,
1100 int forceit,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001101 int group,
1102 int flags)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001103{
1104 AutoPat *ap;
1105 AutoPat **prev_ap;
1106 AutoCmd *ac;
1107 AutoCmd **prev_ac;
1108 int brace_level;
1109 char_u *endpat;
1110 int findgroup;
1111 int allgroups;
1112 int patlen;
1113 int is_buflocal;
1114 int buflocal_nr;
Bram Moolenaarc667da52019-11-30 20:52:27 +01001115 char_u buflocal_pat[25]; // for "<buffer=X>"
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001116
1117 if (group == AUGROUP_ALL)
1118 findgroup = current_augroup;
1119 else
1120 findgroup = group;
1121 allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
1122
1123 /*
1124 * Show or delete all patterns for an event.
1125 */
1126 if (*pat == NUL)
1127 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001128 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001129 {
1130 if (forceit) // delete the AutoPat, if it's in the current group
1131 {
1132 if (ap->group == findgroup)
1133 au_remove_pat(ap);
1134 }
1135 else if (group == AUGROUP_ALL || ap->group == group)
1136 show_autocmd(ap, event);
1137 }
1138 }
1139
1140 /*
1141 * Loop through all the specified patterns.
1142 */
1143 for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
1144 {
1145 /*
1146 * Find end of the pattern.
1147 * Watch out for a comma in braces, like "*.\{obj,o\}".
1148 */
1149 brace_level = 0;
1150 for (endpat = pat; *endpat && (*endpat != ',' || brace_level
1151 || (endpat > pat && endpat[-1] == '\\')); ++endpat)
1152 {
1153 if (*endpat == '{')
1154 brace_level++;
1155 else if (*endpat == '}')
1156 brace_level--;
1157 }
1158 if (pat == endpat) // ignore single comma
1159 continue;
1160 patlen = (int)(endpat - pat);
1161
1162 /*
1163 * detect special <buflocal[=X]> buffer-local patterns
1164 */
1165 is_buflocal = FALSE;
1166 buflocal_nr = 0;
1167
1168 if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
1169 && pat[patlen - 1] == '>')
1170 {
1171 // "<buffer...>": Error will be printed only for addition.
1172 // printing and removing will proceed silently.
1173 is_buflocal = TRUE;
1174 if (patlen == 8)
1175 // "<buffer>"
1176 buflocal_nr = curbuf->b_fnum;
1177 else if (patlen > 9 && pat[7] == '=')
1178 {
1179 if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0)
1180 // "<buffer=abuf>"
1181 buflocal_nr = autocmd_bufnr;
1182 else if (skipdigits(pat + 8) == pat + patlen - 1)
1183 // "<buffer=123>"
1184 buflocal_nr = atoi((char *)pat + 8);
1185 }
1186 }
1187
1188 if (is_buflocal)
1189 {
1190 // normalize pat into standard "<buffer>#N" form
1191 sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
1192 pat = buflocal_pat; // can modify pat and patlen
1193 patlen = (int)STRLEN(buflocal_pat); // but not endpat
1194 }
1195
1196 /*
1197 * Find AutoPat entries with this pattern. When adding a command it
1198 * always goes at or after the last one, so start at the end.
1199 */
1200 if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL)
1201 prev_ap = &last_autopat[(int)event];
1202 else
1203 prev_ap = &first_autopat[(int)event];
1204 while ((ap = *prev_ap) != NULL)
1205 {
1206 if (ap->pat != NULL)
1207 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001208 /*
1209 * Accept a pattern when:
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001210 * - a group was specified and it's that group, or a group was
1211 * not specified and it's the current group, or a group was
1212 * not specified and we are listing
1213 * - the length of the pattern matches
1214 * - the pattern matches.
1215 * For <buffer[=X]>, this condition works because we normalize
1216 * all buffer-local patterns.
1217 */
1218 if ((allgroups || ap->group == findgroup)
1219 && ap->patlen == patlen
1220 && STRNCMP(pat, ap->pat, patlen) == 0)
1221 {
1222 /*
1223 * Remove existing autocommands.
1224 * If adding any new autocmd's for this AutoPat, don't
1225 * delete the pattern from the autopat list, append to
1226 * this list.
1227 */
1228 if (forceit)
1229 {
1230 if (*cmd != NUL && ap->next == NULL)
1231 {
1232 au_remove_cmds(ap);
1233 break;
1234 }
1235 au_remove_pat(ap);
1236 }
1237
1238 /*
1239 * Show autocmd's for this autopat, or buflocals <buffer=X>
1240 */
1241 else if (*cmd == NUL)
1242 show_autocmd(ap, event);
1243
1244 /*
1245 * Add autocmd to this autopat, if it's the last one.
1246 */
1247 else if (ap->next == NULL)
1248 break;
1249 }
1250 }
1251 prev_ap = &ap->next;
1252 }
1253
1254 /*
1255 * Add a new command.
1256 */
1257 if (*cmd != NUL)
1258 {
1259 /*
1260 * If the pattern we want to add a command to does appear at the
1261 * end of the list (or not is not in the list at all), add the
1262 * pattern at the end of the list.
1263 */
1264 if (ap == NULL)
1265 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001266 // refuse to add buffer-local ap if buffer number is invalid
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001267 if (is_buflocal && (buflocal_nr == 0
1268 || buflist_findnr(buflocal_nr) == NULL))
1269 {
Bram Moolenaarf1474d82021-12-31 19:59:55 +00001270 semsg(_(e_buffer_nr_invalid_buffer_number), buflocal_nr);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001271 return FAIL;
1272 }
1273
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001274 ap = ALLOC_ONE(AutoPat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001275 if (ap == NULL)
1276 return FAIL;
1277 ap->pat = vim_strnsave(pat, patlen);
1278 ap->patlen = patlen;
1279 if (ap->pat == NULL)
1280 {
1281 vim_free(ap);
1282 return FAIL;
1283 }
1284
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001285#ifdef FEAT_EVAL
1286 // need to initialize last_mode for the first ModeChanged
1287 // autocmd
1288 if (event == EVENT_MODECHANGED && !has_modechanged())
LemonBoy2bf52dd2022-04-09 18:17:34 +01001289 get_mode(last_mode);
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001290#endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001291 // Initialize the fields checked by the WinScrolled and
1292 // WinResized trigger to prevent them from firing right after
1293 // the first autocmd is defined.
1294 if ((event == EVENT_WINSCROLLED || event == EVENT_WINRESIZED)
1295 && !(has_winscrolled() || has_winresized()))
LemonBoy09371822022-04-08 15:18:45 +01001296 {
Bram Moolenaar29967732022-11-20 12:11:45 +00001297 tabpage_T *save_curtab = curtab;
1298 tabpage_T *tp;
1299 FOR_ALL_TABPAGES(tp)
1300 {
1301 unuse_tabpage(curtab);
1302 use_tabpage(tp);
1303 snapshot_windows_scroll_size();
1304 }
1305 unuse_tabpage(curtab);
1306 use_tabpage(save_curtab);
LemonBoy09371822022-04-08 15:18:45 +01001307 }
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001308
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001309 if (is_buflocal)
1310 {
1311 ap->buflocal_nr = buflocal_nr;
1312 ap->reg_prog = NULL;
1313 }
1314 else
1315 {
1316 char_u *reg_pat;
1317
1318 ap->buflocal_nr = 0;
1319 reg_pat = file_pat_to_reg_pat(pat, endpat,
1320 &ap->allow_dirs, TRUE);
1321 if (reg_pat != NULL)
1322 ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
1323 vim_free(reg_pat);
1324 if (reg_pat == NULL || ap->reg_prog == NULL)
1325 {
1326 vim_free(ap->pat);
1327 vim_free(ap);
1328 return FAIL;
1329 }
1330 }
1331 ap->cmds = NULL;
1332 *prev_ap = ap;
1333 last_autopat[(int)event] = ap;
1334 ap->next = NULL;
1335 if (group == AUGROUP_ALL)
1336 ap->group = current_augroup;
1337 else
1338 ap->group = group;
1339 }
1340
1341 /*
1342 * Add the autocmd at the end of the AutoCmd list.
1343 */
1344 prev_ac = &(ap->cmds);
1345 while ((ac = *prev_ac) != NULL)
1346 prev_ac = &ac->next;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001347 ac = ALLOC_ONE(AutoCmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001348 if (ac == NULL)
1349 return FAIL;
1350 ac->cmd = vim_strsave(cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001351 ac->script_ctx = current_sctx;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001352 if (flags & UC_VIM9)
1353 ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001354#ifdef FEAT_EVAL
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001355 ac->script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001356#endif
1357 if (ac->cmd == NULL)
1358 {
1359 vim_free(ac);
1360 return FAIL;
1361 }
1362 ac->next = NULL;
1363 *prev_ac = ac;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001364 ac->once = once;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001365 ac->nested = nested;
1366 }
1367 }
1368
1369 au_cleanup(); // may really delete removed patterns/commands now
1370 return OK;
1371}
1372
1373/*
1374 * Implementation of ":doautocmd [group] event [fname]".
1375 * Return OK for success, FAIL for failure;
1376 */
1377 int
1378do_doautocmd(
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001379 char_u *arg_start,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001380 int do_msg, // give message for no matching autocmds?
1381 int *did_something)
1382{
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001383 char_u *arg = arg_start;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001384 char_u *fname;
1385 int nothing_done = TRUE;
1386 int group;
1387
1388 if (did_something != NULL)
1389 *did_something = FALSE;
1390
1391 /*
1392 * Check for a legal group name. If not, use AUGROUP_ALL.
1393 */
1394 group = au_get_grouparg(&arg);
1395 if (arg == NULL) // out of memory
1396 return FAIL;
1397
1398 if (*arg == '*')
1399 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001400 emsg(_(e_cant_execute_autocommands_for_all_events));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001401 return FAIL;
1402 }
1403
1404 /*
1405 * Scan over the events.
1406 * If we find an illegal name, return here, don't do anything.
1407 */
1408 fname = find_end_event(arg, group != AUGROUP_ALL);
1409 if (fname == NULL)
1410 return FAIL;
1411
1412 fname = skipwhite(fname);
1413
1414 /*
1415 * Loop over the events.
1416 */
1417 while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
1418 if (apply_autocmds_group(event_name2nr(arg, &arg),
1419 fname, NULL, TRUE, group, curbuf, NULL))
1420 nothing_done = FALSE;
1421
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001422 if (nothing_done && do_msg
1423#ifdef FEAT_EVAL
1424 && !aborting()
1425#endif
1426 )
1427 smsg(_("No matching autocommands: %s"), arg_start);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001428 if (did_something != NULL)
1429 *did_something = !nothing_done;
1430
1431#ifdef FEAT_EVAL
1432 return aborting() ? FAIL : OK;
1433#else
1434 return OK;
1435#endif
1436}
1437
1438/*
1439 * ":doautoall": execute autocommands for each loaded buffer.
1440 */
1441 void
1442ex_doautoall(exarg_T *eap)
1443{
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001444 int retval = OK;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001445 aco_save_T aco;
1446 buf_T *buf;
1447 bufref_T bufref;
1448 char_u *arg = eap->arg;
1449 int call_do_modelines = check_nomodeline(&arg);
1450 int did_aucmd;
1451
1452 /*
1453 * This is a bit tricky: For some commands curwin->w_buffer needs to be
1454 * equal to curbuf, but for some buffers there may not be a window.
1455 * So we change the buffer for the current window for a moment. This
1456 * gives problems when the autocommands make changes to the list of
1457 * buffers or windows...
1458 */
1459 FOR_ALL_BUFFERS(buf)
1460 {
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001461 // Only do loaded buffers and skip the current buffer, it's done last.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001462 if (buf->b_ml.ml_mfp == NULL || buf == curbuf)
1463 continue;
1464
Bram Moolenaare76062c2022-11-28 18:51:43 +00001465 // Find a window for this buffer and save some values.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001466 aucmd_prepbuf(&aco, buf);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001467 if (curbuf != buf)
1468 {
1469 // Failed to find a window for this buffer. Better not execute
1470 // autocommands then.
1471 retval = FAIL;
1472 break;
1473 }
1474
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001475 set_bufref(&bufref, buf);
1476
1477 // execute the autocommands for this buffer
1478 retval = do_doautocmd(arg, FALSE, &did_aucmd);
1479
1480 if (call_do_modelines && did_aucmd)
1481 // Execute the modeline settings, but don't set window-local
1482 // options if we are using the current window for another
1483 // buffer.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001484 do_modelines(is_aucmd_win(curwin) ? OPT_NOWIN : 0);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001485
1486 // restore the current window
1487 aucmd_restbuf(&aco);
1488
1489 // stop if there is some error or buffer was deleted
1490 if (retval == FAIL || !bufref_valid(&bufref))
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001491 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001492 retval = FAIL;
1493 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001494 }
1495 }
1496
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001497 // Execute autocommands for the current buffer last.
1498 if (retval == OK)
1499 {
1500 do_doautocmd(arg, FALSE, &did_aucmd);
1501 if (call_do_modelines && did_aucmd)
1502 do_modelines(0);
1503 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001504}
1505
1506/*
1507 * Check *argp for <nomodeline>. When it is present return FALSE, otherwise
1508 * return TRUE and advance *argp to after it.
1509 * Thus return TRUE when do_modelines() should be called.
1510 */
1511 int
1512check_nomodeline(char_u **argp)
1513{
1514 if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
1515 {
1516 *argp = skipwhite(*argp + 12);
1517 return FALSE;
1518 }
1519 return TRUE;
1520}
1521
1522/*
1523 * Prepare for executing autocommands for (hidden) buffer "buf".
1524 * Search for a visible window containing the current buffer. If there isn't
Bram Moolenaare76062c2022-11-28 18:51:43 +00001525 * one then use an entry in "aucmd_win[]".
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001526 * Set "curbuf" and "curwin" to match "buf".
Bram Moolenaare76062c2022-11-28 18:51:43 +00001527 * When this fails "curbuf" is not equal "buf".
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001528 */
1529 void
1530aucmd_prepbuf(
1531 aco_save_T *aco, // structure to save values in
1532 buf_T *buf) // new curbuf
1533{
1534 win_T *win;
1535 int save_ea;
1536#ifdef FEAT_AUTOCHDIR
1537 int save_acd;
1538#endif
1539
1540 // Find a window that is for the new buffer
1541 if (buf == curbuf) // be quick when buf is curbuf
1542 win = curwin;
1543 else
1544 FOR_ALL_WINDOWS(win)
1545 if (win->w_buffer == buf)
1546 break;
1547
Bram Moolenaare76062c2022-11-28 18:51:43 +00001548 // Allocate a window when needed.
1549 win_T *auc_win = NULL;
1550 int auc_idx = AUCMD_WIN_COUNT;
1551 if (win == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001552 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001553 for (auc_idx = 0; auc_idx < AUCMD_WIN_COUNT; ++auc_idx)
1554 if (!aucmd_win[auc_idx].auc_win_used)
1555 {
1556 auc_win = win_alloc_popup_win();
1557 if (auc_win != NULL)
1558 {
1559 aucmd_win[auc_idx].auc_win = auc_win;
1560 aucmd_win[auc_idx].auc_win_used = TRUE;
1561 }
1562 break;
1563 }
1564
1565 // If this fails (out of memory or using all AUCMD_WIN_COUNT
1566 // entries) then we can't reliable execute the autocmd, return with
1567 // "curbuf" unequal "buf".
1568 if (auc_win == NULL)
1569 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001570 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001571
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001572 aco->save_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001573 aco->save_curbuf = curbuf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001574 aco->save_prevwin_id = prevwin == NULL ? 0 : prevwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001575 if (win != NULL)
1576 {
1577 // There is a window for "buf" in the current tab page, make it the
1578 // curwin. This is preferred, it has the least side effects (esp. if
1579 // "buf" is curbuf).
Bram Moolenaare76062c2022-11-28 18:51:43 +00001580 aco->use_aucmd_win_idx = -1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001581 curwin = win;
1582 }
1583 else
1584 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001585 // There is no window for "buf", use "auc_win". To minimize the side
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001586 // effects, insert it in the current tab page.
1587 // Anything related to a window (e.g., setting folds) may have
1588 // unexpected results.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001589 aco->use_aucmd_win_idx = auc_idx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001590
Bram Moolenaare76062c2022-11-28 18:51:43 +00001591 win_init_popup_win(auc_win, buf);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001592
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001593 aco->globaldir = globaldir;
1594 globaldir = NULL;
1595
Bram Moolenaare76062c2022-11-28 18:51:43 +00001596 // Split the current window, put the auc_win in the upper half.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001597 // We don't want the BufEnter or WinEnter autocommands.
1598 block_autocmds();
1599 make_snapshot(SNAP_AUCMD_IDX);
1600 save_ea = p_ea;
1601 p_ea = FALSE;
1602
1603#ifdef FEAT_AUTOCHDIR
1604 // Prevent chdir() call in win_enter_ext(), through do_autochdir().
1605 save_acd = p_acd;
1606 p_acd = FALSE;
1607#endif
1608
Bram Moolenaardff97e62022-01-24 20:00:55 +00001609 // no redrawing and don't set the window title
1610 ++RedrawingDisabled;
Bram Moolenaare76062c2022-11-28 18:51:43 +00001611 (void)win_split_ins(0, WSP_TOP, auc_win, 0);
Bram Moolenaardff97e62022-01-24 20:00:55 +00001612 --RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001613 (void)win_comp_pos(); // recompute window positions
1614 p_ea = save_ea;
1615#ifdef FEAT_AUTOCHDIR
1616 p_acd = save_acd;
1617#endif
1618 unblock_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001619 curwin = auc_win;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001620 }
1621 curbuf = buf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001622 aco->new_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001623 set_bufref(&aco->new_curbuf, curbuf);
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001624
1625 // disable the Visual area, the position may be invalid in another buffer
1626 aco->save_VIsual_active = VIsual_active;
1627 VIsual_active = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001628}
1629
1630/*
1631 * Cleanup after executing autocommands for a (hidden) buffer.
1632 * Restore the window as it was (if possible).
1633 */
1634 void
1635aucmd_restbuf(
1636 aco_save_T *aco) // structure holding saved values
1637{
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001638 int dummy;
1639 win_T *save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001640
Bram Moolenaare76062c2022-11-28 18:51:43 +00001641 if (aco->use_aucmd_win_idx >= 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001642 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001643 win_T *awp = aucmd_win[aco->use_aucmd_win_idx].auc_win;
1644
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001645 --curbuf->b_nwindows;
Bram Moolenaare76062c2022-11-28 18:51:43 +00001646 // Find "awp", it can't be closed, but it may be in another tab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001647 // page. Do not trigger autocommands here.
1648 block_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001649 if (curwin != awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001650 {
1651 tabpage_T *tp;
1652 win_T *wp;
1653
1654 FOR_ALL_TAB_WINDOWS(tp, wp)
1655 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001656 if (wp == awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001657 {
1658 if (tp != curtab)
1659 goto_tabpage_tp(tp, TRUE, TRUE);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001660 win_goto(awp);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001661 goto win_found;
1662 }
1663 }
1664 }
1665win_found:
1666
1667 // Remove the window and frame from the tree of frames.
1668 (void)winframe_remove(curwin, &dummy, NULL);
1669 win_remove(curwin, NULL);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001670 aucmd_win[aco->use_aucmd_win_idx].auc_win_used = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001671 last_status(FALSE); // may need to remove last status line
1672
1673 if (!valid_tabpage_win(curtab))
1674 // no valid window in current tabpage
1675 close_tabpage(curtab);
1676
1677 restore_snapshot(SNAP_AUCMD_IDX, FALSE);
1678 (void)win_comp_pos(); // recompute window positions
1679 unblock_autocmds();
1680
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001681 save_curwin = win_find_by_id(aco->save_curwin_id);
1682 if (save_curwin != NULL)
1683 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001684 else
1685 // Hmm, original window disappeared. Just use the first one.
1686 curwin = firstwin;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001687 curbuf = curwin->w_buffer;
1688#ifdef FEAT_JOB_CHANNEL
1689 // May need to restore insert mode for a prompt buffer.
1690 entering_window(curwin);
1691#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001692 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001693#ifdef FEAT_EVAL
Bram Moolenaare76062c2022-11-28 18:51:43 +00001694 vars_clear(&awp->w_vars->dv_hashtab); // free all w: variables
1695 hash_init(&awp->w_vars->dv_hashtab); // re-use the hashtab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001696#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001697 vim_free(globaldir);
1698 globaldir = aco->globaldir;
1699
1700 // the buffer contents may have changed
1701 check_cursor();
1702 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1703 {
1704 curwin->w_topline = curbuf->b_ml.ml_line_count;
1705#ifdef FEAT_DIFF
1706 curwin->w_topfill = 0;
1707#endif
1708 }
1709#if defined(FEAT_GUI)
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001710 if (gui.in_use)
1711 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001712 // Hide the scrollbars from the "awp" and update.
1713 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_LEFT], FALSE);
1714 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_RIGHT], FALSE);
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001715 gui_may_update_scrollbars();
1716 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001717#endif
1718 }
1719 else
1720 {
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001721 // Restore curwin. Use the window ID, a window may have been closed
1722 // and the memory re-used for another one.
1723 save_curwin = win_find_by_id(aco->save_curwin_id);
1724 if (save_curwin != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001725 {
1726 // Restore the buffer which was previously edited by curwin, if
1727 // it was changed, we are still the same window and the buffer is
1728 // valid.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001729 if (curwin->w_id == aco->new_curwin_id
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001730 && curbuf != aco->new_curbuf.br_buf
1731 && bufref_valid(&aco->new_curbuf)
1732 && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
1733 {
1734# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
1735 if (curwin->w_s == &curbuf->b_s)
1736 curwin->w_s = &aco->new_curbuf.br_buf->b_s;
1737# endif
1738 --curbuf->b_nwindows;
1739 curbuf = aco->new_curbuf.br_buf;
1740 curwin->w_buffer = curbuf;
1741 ++curbuf->b_nwindows;
1742 }
1743
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001744 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001745 curbuf = curwin->w_buffer;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001746 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001747 // In case the autocommand moves the cursor to a position that
1748 // does not exist in curbuf.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001749 check_cursor();
1750 }
1751 }
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001752
1753 check_cursor(); // just in case lines got deleted
1754 VIsual_active = aco->save_VIsual_active;
1755 if (VIsual_active)
1756 check_pos(curbuf, &VIsual);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001757}
1758
1759static int autocmd_nested = FALSE;
1760
1761/*
1762 * Execute autocommands for "event" and file name "fname".
1763 * Return TRUE if some commands were executed.
1764 */
1765 int
1766apply_autocmds(
1767 event_T event,
1768 char_u *fname, // NULL or empty means use actual file name
1769 char_u *fname_io, // fname to use for <afile> on cmdline
1770 int force, // when TRUE, ignore autocmd_busy
1771 buf_T *buf) // buffer for <abuf>
1772{
1773 return apply_autocmds_group(event, fname, fname_io, force,
1774 AUGROUP_ALL, buf, NULL);
1775}
1776
1777/*
1778 * Like apply_autocmds(), but with extra "eap" argument. This takes care of
1779 * setting v:filearg.
1780 */
1781 int
1782apply_autocmds_exarg(
1783 event_T event,
1784 char_u *fname,
1785 char_u *fname_io,
1786 int force,
1787 buf_T *buf,
1788 exarg_T *eap)
1789{
1790 return apply_autocmds_group(event, fname, fname_io, force,
1791 AUGROUP_ALL, buf, eap);
1792}
1793
1794/*
1795 * Like apply_autocmds(), but handles the caller's retval. If the script
1796 * processing is being aborted or if retval is FAIL when inside a try
1797 * conditional, no autocommands are executed. If otherwise the autocommands
1798 * cause the script to be aborted, retval is set to FAIL.
1799 */
1800 int
1801apply_autocmds_retval(
1802 event_T event,
1803 char_u *fname, // NULL or empty means use actual file name
1804 char_u *fname_io, // fname to use for <afile> on cmdline
1805 int force, // when TRUE, ignore autocmd_busy
1806 buf_T *buf, // buffer for <abuf>
1807 int *retval) // pointer to caller's retval
1808{
1809 int did_cmd;
1810
1811#ifdef FEAT_EVAL
1812 if (should_abort(*retval))
1813 return FALSE;
1814#endif
1815
1816 did_cmd = apply_autocmds_group(event, fname, fname_io, force,
1817 AUGROUP_ALL, buf, NULL);
1818 if (did_cmd
1819#ifdef FEAT_EVAL
1820 && aborting()
1821#endif
1822 )
1823 *retval = FAIL;
1824 return did_cmd;
1825}
1826
1827/*
1828 * Return TRUE when there is a CursorHold autocommand defined.
1829 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001830 static int
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001831has_cursorhold(void)
1832{
Bram Moolenaar24959102022-05-07 20:01:16 +01001833 return (first_autopat[(int)(get_real_state() == MODE_NORMAL_BUSY
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001834 ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
1835}
1836
1837/*
1838 * Return TRUE if the CursorHold event can be triggered.
1839 */
1840 int
1841trigger_cursorhold(void)
1842{
1843 int state;
1844
1845 if (!did_cursorhold
1846 && has_cursorhold()
1847 && reg_recording == 0
1848 && typebuf.tb_len == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001849 && !ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001850 {
1851 state = get_real_state();
Bram Moolenaar24959102022-05-07 20:01:16 +01001852 if (state == MODE_NORMAL_BUSY || (state & MODE_INSERT) != 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001853 return TRUE;
1854 }
1855 return FALSE;
1856}
1857
1858/*
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001859 * Return TRUE when there is a WinResized autocommand defined.
1860 */
1861 int
1862has_winresized(void)
1863{
1864 return (first_autopat[(int)EVENT_WINRESIZED] != NULL);
1865}
1866
1867/*
LemonBoy09371822022-04-08 15:18:45 +01001868 * Return TRUE when there is a WinScrolled autocommand defined.
1869 */
1870 int
1871has_winscrolled(void)
1872{
1873 return (first_autopat[(int)EVENT_WINSCROLLED] != NULL);
1874}
1875
1876/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001877 * Return TRUE when there is a CursorMoved autocommand defined.
1878 */
1879 int
1880has_cursormoved(void)
1881{
1882 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1883}
1884
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001885/*
1886 * Return TRUE when there is a CursorMovedI autocommand defined.
1887 */
1888 int
1889has_cursormovedI(void)
1890{
1891 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1892}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001893
1894/*
1895 * Return TRUE when there is a TextChanged autocommand defined.
1896 */
1897 int
1898has_textchanged(void)
1899{
1900 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1901}
1902
1903/*
1904 * Return TRUE when there is a TextChangedI autocommand defined.
1905 */
1906 int
1907has_textchangedI(void)
1908{
1909 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
1910}
1911
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001912/*
1913 * Return TRUE when there is a TextChangedP autocommand defined.
1914 */
1915 int
1916has_textchangedP(void)
1917{
1918 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
1919}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001920
1921/*
1922 * Return TRUE when there is an InsertCharPre autocommand defined.
1923 */
1924 int
1925has_insertcharpre(void)
1926{
1927 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
1928}
1929
1930/*
1931 * Return TRUE when there is an CmdUndefined autocommand defined.
1932 */
1933 int
1934has_cmdundefined(void)
1935{
1936 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
1937}
1938
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001939#if defined(FEAT_EVAL) || defined(PROTO)
1940/*
1941 * Return TRUE when there is a TextYankPost autocommand defined.
1942 */
1943 int
1944has_textyankpost(void)
1945{
1946 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
1947}
1948#endif
1949
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001950#if defined(FEAT_EVAL) || defined(PROTO)
1951/*
1952 * Return TRUE when there is a CompleteChanged autocommand defined.
1953 */
1954 int
1955has_completechanged(void)
1956{
1957 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
1958}
1959#endif
1960
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02001961#if defined(FEAT_EVAL) || defined(PROTO)
1962/*
1963 * Return TRUE when there is a ModeChanged autocommand defined.
1964 */
1965 int
1966has_modechanged(void)
1967{
1968 return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
1969}
1970#endif
1971
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001972/*
1973 * Execute autocommands for "event" and file name "fname".
1974 * Return TRUE if some commands were executed.
1975 */
1976 static int
1977apply_autocmds_group(
1978 event_T event,
1979 char_u *fname, // NULL or empty means use actual file name
1980 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
1981 // use fname
1982 int force, // when TRUE, ignore autocmd_busy
1983 int group, // group ID, or AUGROUP_ALL
1984 buf_T *buf, // buffer for <abuf>
1985 exarg_T *eap UNUSED) // command arguments
1986{
1987 char_u *sfname = NULL; // short file name
1988 char_u *tail;
1989 int save_changed;
1990 buf_T *old_curbuf;
1991 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001992 char_u *save_autocmd_fname;
1993 int save_autocmd_fname_full;
1994 int save_autocmd_bufnr;
1995 char_u *save_autocmd_match;
1996 int save_autocmd_busy;
1997 int save_autocmd_nested;
1998 static int nesting = 0;
LemonBoyeca7c602022-04-14 15:39:43 +01001999 AutoPatCmd_T patcmd;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002000 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002001 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002002#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002003 funccal_entry_T funccal_entry;
2004 char_u *save_cmdarg;
2005 long save_cmdbang;
2006#endif
2007 static int filechangeshell_busy = FALSE;
2008#ifdef FEAT_PROFILE
2009 proftime_T wait_time;
2010#endif
2011 int did_save_redobuff = FALSE;
2012 save_redo_T save_redo;
2013 int save_KeyTyped = KeyTyped;
ichizokc3f91c02021-12-17 09:44:33 +00002014 int save_did_emsg;
Bram Moolenaare31ee862020-01-07 20:59:34 +01002015 ESTACK_CHECK_DECLARATION
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002016
2017 /*
2018 * Quickly return if there are no autocommands for this event or
2019 * autocommands are blocked.
2020 */
2021 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
2022 || autocmd_blocked > 0)
2023 goto BYPASS_AU;
2024
2025 /*
2026 * When autocommands are busy, new autocommands are only executed when
2027 * explicitly enabled with the "nested" flag.
2028 */
2029 if (autocmd_busy && !(force || autocmd_nested))
2030 goto BYPASS_AU;
2031
2032#ifdef FEAT_EVAL
2033 /*
2034 * Quickly return when immediately aborting on error, or when an interrupt
2035 * occurred or an exception was thrown but not caught.
2036 */
2037 if (aborting())
2038 goto BYPASS_AU;
2039#endif
2040
2041 /*
2042 * FileChangedShell never nests, because it can create an endless loop.
2043 */
2044 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
2045 || event == EVENT_FILECHANGEDSHELLPOST))
2046 goto BYPASS_AU;
2047
2048 /*
2049 * Ignore events in 'eventignore'.
2050 */
2051 if (event_ignored(event))
2052 goto BYPASS_AU;
2053
2054 /*
2055 * Allow nesting of autocommands, but restrict the depth, because it's
2056 * possible to create an endless loop.
2057 */
2058 if (nesting == 10)
2059 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00002060 emsg(_(e_autocommand_nesting_too_deep));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002061 goto BYPASS_AU;
2062 }
2063
2064 /*
2065 * Check if these autocommands are disabled. Used when doing ":all" or
2066 * ":ball".
2067 */
2068 if ( (autocmd_no_enter
2069 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
2070 || (autocmd_no_leave
2071 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
2072 goto BYPASS_AU;
2073
2074 /*
2075 * Save the autocmd_* variables and info about the current buffer.
2076 */
2077 save_autocmd_fname = autocmd_fname;
2078 save_autocmd_fname_full = autocmd_fname_full;
2079 save_autocmd_bufnr = autocmd_bufnr;
2080 save_autocmd_match = autocmd_match;
2081 save_autocmd_busy = autocmd_busy;
2082 save_autocmd_nested = autocmd_nested;
2083 save_changed = curbuf->b_changed;
2084 old_curbuf = curbuf;
2085
2086 /*
2087 * Set the file name to be used for <afile>.
2088 * Make a copy to avoid that changing a buffer name or directory makes it
2089 * invalid.
2090 */
2091 if (fname_io == NULL)
2092 {
2093 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002094 || event == EVENT_OPTIONSET
2095 || event == EVENT_MODECHANGED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002096 autocmd_fname = NULL;
2097 else if (fname != NULL && !ends_excmd(*fname))
2098 autocmd_fname = fname;
2099 else if (buf != NULL)
2100 autocmd_fname = buf->b_ffname;
2101 else
2102 autocmd_fname = NULL;
2103 }
2104 else
2105 autocmd_fname = fname_io;
2106 if (autocmd_fname != NULL)
2107 autocmd_fname = vim_strsave(autocmd_fname);
2108 autocmd_fname_full = FALSE; // call FullName_save() later
2109
2110 /*
2111 * Set the buffer number to be used for <abuf>.
2112 */
2113 if (buf == NULL)
2114 autocmd_bufnr = 0;
2115 else
2116 autocmd_bufnr = buf->b_fnum;
2117
2118 /*
2119 * When the file name is NULL or empty, use the file name of buffer "buf".
2120 * Always use the full path of the file name to match with, in case
2121 * "allow_dirs" is set.
2122 */
2123 if (fname == NULL || *fname == NUL)
2124 {
2125 if (buf == NULL)
2126 fname = NULL;
2127 else
2128 {
2129#ifdef FEAT_SYN_HL
2130 if (event == EVENT_SYNTAX)
2131 fname = buf->b_p_syn;
2132 else
2133#endif
2134 if (event == EVENT_FILETYPE)
2135 fname = buf->b_p_ft;
2136 else
2137 {
2138 if (buf->b_sfname != NULL)
2139 sfname = vim_strsave(buf->b_sfname);
2140 fname = buf->b_ffname;
2141 }
2142 }
2143 if (fname == NULL)
2144 fname = (char_u *)"";
2145 fname = vim_strsave(fname); // make a copy, so we can change it
2146 }
2147 else
2148 {
2149 sfname = vim_strsave(fname);
2150 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002151 // ColorScheme, QuickFixCmd*, DirChanged and similar.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002152 if (event == EVENT_FILETYPE
2153 || event == EVENT_SYNTAX
2154 || event == EVENT_CMDLINECHANGED
2155 || event == EVENT_CMDLINEENTER
2156 || event == EVENT_CMDLINELEAVE
2157 || event == EVENT_CMDWINENTER
2158 || event == EVENT_CMDWINLEAVE
2159 || event == EVENT_CMDUNDEFINED
2160 || event == EVENT_FUNCUNDEFINED
2161 || event == EVENT_REMOTEREPLY
2162 || event == EVENT_SPELLFILEMISSING
2163 || event == EVENT_QUICKFIXCMDPRE
2164 || event == EVENT_COLORSCHEME
2165 || event == EVENT_COLORSCHEMEPRE
2166 || event == EVENT_OPTIONSET
2167 || event == EVENT_QUICKFIXCMDPOST
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002168 || event == EVENT_DIRCHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002169 || event == EVENT_DIRCHANGEDPRE
naohiro ono23beefe2021-11-13 12:38:49 +00002170 || event == EVENT_MODECHANGED
zeertzjqc601d982022-10-10 13:46:15 +01002171 || event == EVENT_MENUPOPUP
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002172 || event == EVENT_USER
LemonBoy09371822022-04-08 15:18:45 +01002173 || event == EVENT_WINCLOSED
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00002174 || event == EVENT_WINRESIZED
LemonBoy09371822022-04-08 15:18:45 +01002175 || event == EVENT_WINSCROLLED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002176 {
2177 fname = vim_strsave(fname);
2178 autocmd_fname_full = TRUE; // don't expand it later
2179 }
2180 else
2181 fname = FullName_save(fname, FALSE);
2182 }
2183 if (fname == NULL) // out of memory
2184 {
2185 vim_free(sfname);
2186 retval = FALSE;
2187 goto BYPASS_AU;
2188 }
2189
2190#ifdef BACKSLASH_IN_FILENAME
2191 /*
2192 * Replace all backslashes with forward slashes. This makes the
2193 * autocommand patterns portable between Unix and MS-DOS.
2194 */
2195 if (sfname != NULL)
2196 forward_slash(sfname);
2197 forward_slash(fname);
2198#endif
2199
2200#ifdef VMS
2201 // remove version for correct match
2202 if (sfname != NULL)
2203 vms_remove_version(sfname);
2204 vms_remove_version(fname);
2205#endif
2206
2207 /*
2208 * Set the name to be used for <amatch>.
2209 */
2210 autocmd_match = fname;
2211
2212
2213 // Don't redraw while doing autocommands.
2214 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002215
2216 // name and lnum are filled in later
2217 estack_push(ETYPE_AUCMD, NULL, 0);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002218 ESTACK_CHECK_SETUP
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002219
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002220 save_current_sctx = current_sctx;
2221
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002222#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002223# ifdef FEAT_PROFILE
2224 if (do_profiling == PROF_YES)
2225 prof_child_enter(&wait_time); // doesn't count for the caller itself
2226# endif
2227
2228 // Don't use local function variables, if called from a function.
2229 save_funccal(&funccal_entry);
2230#endif
2231
2232 /*
2233 * When starting to execute autocommands, save the search patterns.
2234 */
2235 if (!autocmd_busy)
2236 {
2237 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002238 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002239 {
2240 saveRedobuff(&save_redo);
2241 did_save_redobuff = TRUE;
2242 }
2243 did_filetype = keep_filetype;
2244 }
2245
2246 /*
2247 * Note that we are applying autocmds. Some commands need to know.
2248 */
2249 autocmd_busy = TRUE;
2250 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2251 ++nesting; // see matching decrement below
2252
2253 // Remember that FileType was triggered. Used for did_filetype().
2254 if (event == EVENT_FILETYPE)
2255 did_filetype = TRUE;
2256
2257 tail = gettail(fname);
2258
2259 // Find first autocommand that matches
LemonBoyeca7c602022-04-14 15:39:43 +01002260 CLEAR_FIELD(patcmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002261 patcmd.curpat = first_autopat[(int)event];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002262 patcmd.group = group;
2263 patcmd.fname = fname;
2264 patcmd.sfname = sfname;
2265 patcmd.tail = tail;
2266 patcmd.event = event;
2267 patcmd.arg_bufnr = autocmd_bufnr;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002268 auto_next_pat(&patcmd, FALSE);
2269
2270 // found one, start executing the autocommands
2271 if (patcmd.curpat != NULL)
2272 {
2273 // add to active_apc_list
2274 patcmd.next = active_apc_list;
2275 active_apc_list = &patcmd;
2276
2277#ifdef FEAT_EVAL
2278 // set v:cmdarg (only when there is a matching pattern)
2279 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2280 if (eap != NULL)
2281 {
2282 save_cmdarg = set_cmdarg(eap, NULL);
2283 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2284 }
2285 else
2286 save_cmdarg = NULL; // avoid gcc warning
2287#endif
2288 retval = TRUE;
2289 // mark the last pattern, to avoid an endless loop when more patterns
2290 // are added when executing autocommands
2291 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2292 ap->last = FALSE;
2293 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002294
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002295 // Make sure cursor and topline are valid. The first time the current
2296 // values are saved, restored by reset_lnums(). When nested only the
2297 // values are corrected when needed.
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002298 if (nesting == 1)
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002299 check_lnums(TRUE);
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002300 else
2301 check_lnums_nested(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002302
ichizokc3f91c02021-12-17 09:44:33 +00002303 save_did_emsg = did_emsg;
2304
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002305 do_cmdline(NULL, getnextac, (void *)&patcmd,
2306 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002307
ichizokc3f91c02021-12-17 09:44:33 +00002308 did_emsg += save_did_emsg;
2309
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002310 if (nesting == 1)
2311 // restore cursor and topline, unless they were changed
2312 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002313
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002314#ifdef FEAT_EVAL
2315 if (eap != NULL)
2316 {
2317 (void)set_cmdarg(NULL, save_cmdarg);
2318 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2319 }
2320#endif
2321 // delete from active_apc_list
2322 if (active_apc_list == &patcmd) // just in case
2323 active_apc_list = patcmd.next;
2324 }
2325
2326 --RedrawingDisabled;
2327 autocmd_busy = save_autocmd_busy;
2328 filechangeshell_busy = FALSE;
2329 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002330 vim_free(SOURCING_NAME);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002331 ESTACK_CHECK_NOW
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002332 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002333 vim_free(autocmd_fname);
2334 autocmd_fname = save_autocmd_fname;
2335 autocmd_fname_full = save_autocmd_fname_full;
2336 autocmd_bufnr = save_autocmd_bufnr;
2337 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002338 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002339#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002340 restore_funccal();
2341# ifdef FEAT_PROFILE
2342 if (do_profiling == PROF_YES)
2343 prof_child_exit(&wait_time);
2344# endif
2345#endif
2346 KeyTyped = save_KeyTyped;
2347 vim_free(fname);
2348 vim_free(sfname);
2349 --nesting; // see matching increment above
2350
2351 /*
2352 * When stopping to execute autocommands, restore the search patterns and
2353 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2354 * free any windows in the au_pending_free_win list.
2355 */
2356 if (!autocmd_busy)
2357 {
2358 restore_search_patterns();
2359 if (did_save_redobuff)
2360 restoreRedobuff(&save_redo);
2361 did_filetype = FALSE;
2362 while (au_pending_free_buf != NULL)
2363 {
2364 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002365
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002366 vim_free(au_pending_free_buf);
2367 au_pending_free_buf = b;
2368 }
2369 while (au_pending_free_win != NULL)
2370 {
2371 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002372
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002373 vim_free(au_pending_free_win);
2374 au_pending_free_win = w;
2375 }
2376 }
2377
2378 /*
2379 * Some events don't set or reset the Changed flag.
2380 * Check if still in the same buffer!
2381 */
2382 if (curbuf == old_curbuf
2383 && (event == EVENT_BUFREADPOST
2384 || event == EVENT_BUFWRITEPOST
2385 || event == EVENT_FILEAPPENDPOST
2386 || event == EVENT_VIMLEAVE
2387 || event == EVENT_VIMLEAVEPRE))
2388 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002389 if (curbuf->b_changed != save_changed)
2390 need_maketitle = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002391 curbuf->b_changed = save_changed;
2392 }
2393
2394 au_cleanup(); // may really delete removed patterns/commands now
2395
2396BYPASS_AU:
2397 // When wiping out a buffer make sure all its buffer-local autocommands
2398 // are deleted.
2399 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2400 aubuflocal_remove(buf);
2401
2402 if (retval == OK && event == EVENT_FILETYPE)
2403 au_did_filetype = TRUE;
2404
2405 return retval;
2406}
2407
2408# ifdef FEAT_EVAL
2409static char_u *old_termresponse = NULL;
2410# endif
2411
2412/*
2413 * Block triggering autocommands until unblock_autocmd() is called.
2414 * Can be used recursively, so long as it's symmetric.
2415 */
2416 void
2417block_autocmds(void)
2418{
2419# ifdef FEAT_EVAL
2420 // Remember the value of v:termresponse.
2421 if (autocmd_blocked == 0)
2422 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
2423# endif
2424 ++autocmd_blocked;
2425}
2426
2427 void
2428unblock_autocmds(void)
2429{
2430 --autocmd_blocked;
2431
2432# ifdef FEAT_EVAL
2433 // When v:termresponse was set while autocommands were blocked, trigger
2434 // the autocommands now. Esp. useful when executing a shell command
2435 // during startup (vimdiff).
2436 if (autocmd_blocked == 0
2437 && get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2438 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2439# endif
2440}
2441
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002442 int
2443is_autocmd_blocked(void)
2444{
2445 return autocmd_blocked != 0;
2446}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002447
2448/*
2449 * Find next autocommand pattern that matches.
2450 */
2451 static void
2452auto_next_pat(
LemonBoyeca7c602022-04-14 15:39:43 +01002453 AutoPatCmd_T *apc,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002454 int stop_at_last) // stop when 'last' flag is set
2455{
2456 AutoPat *ap;
2457 AutoCmd *cp;
2458 char_u *name;
2459 char *s;
LemonBoyeca7c602022-04-14 15:39:43 +01002460 estack_T *entry;
2461 char_u *namep;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002462
LemonBoyeca7c602022-04-14 15:39:43 +01002463 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
2464
2465 // Clear the exestack entry for this ETYPE_AUCMD entry.
2466 VIM_CLEAR(entry->es_name);
2467 entry->es_info.aucmd = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002468
2469 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2470 {
2471 apc->curpat = NULL;
2472
2473 // Only use a pattern when it has not been removed, has commands and
2474 // the group matches. For buffer-local autocommands only check the
2475 // buffer number.
2476 if (ap->pat != NULL && ap->cmds != NULL
2477 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2478 {
2479 // execution-condition
2480 if (ap->buflocal_nr == 0
2481 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2482 apc->sfname, apc->tail, ap->allow_dirs))
2483 : ap->buflocal_nr == apc->arg_bufnr)
2484 {
2485 name = event_nr2name(apc->event);
2486 s = _("%s Autocommands for \"%s\"");
LemonBoyeca7c602022-04-14 15:39:43 +01002487 namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1);
2488 if (namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002489 {
LemonBoyeca7c602022-04-14 15:39:43 +01002490 sprintf((char *)namep, s, (char *)name, (char *)ap->pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002491 if (p_verbose >= 8)
2492 {
2493 verbose_enter();
LemonBoyeca7c602022-04-14 15:39:43 +01002494 smsg(_("Executing %s"), namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002495 verbose_leave();
2496 }
2497 }
2498
LemonBoyeca7c602022-04-14 15:39:43 +01002499 // Update the exestack entry for this autocmd.
2500 entry->es_name = namep;
2501 entry->es_info.aucmd = apc;
2502
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002503 apc->curpat = ap;
2504 apc->nextcmd = ap->cmds;
2505 // mark last command
2506 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2507 cp->last = FALSE;
2508 cp->last = TRUE;
2509 }
2510 line_breakcheck();
2511 if (apc->curpat != NULL) // found a match
2512 break;
2513 }
2514 if (stop_at_last && ap->last)
2515 break;
2516 }
2517}
2518
2519/*
LemonBoyeca7c602022-04-14 15:39:43 +01002520 * Get the script context where autocommand "acp" is defined.
2521 */
2522 sctx_T *
2523acp_script_ctx(AutoPatCmd_T *acp)
2524{
2525 return &acp->script_ctx;
2526}
2527
2528/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002529 * Get next autocommand command.
2530 * Called by do_cmdline() to get the next line for ":if".
2531 * Returns allocated string, or NULL for end of autocommands.
2532 */
2533 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002534getnextac(
2535 int c UNUSED,
2536 void *cookie,
2537 int indent UNUSED,
2538 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002539{
LemonBoyeca7c602022-04-14 15:39:43 +01002540 AutoPatCmd_T *acp = (AutoPatCmd_T *)cookie;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002541 char_u *retval;
2542 AutoCmd *ac;
2543
2544 // Can be called again after returning the last line.
2545 if (acp->curpat == NULL)
2546 return NULL;
2547
2548 // repeat until we find an autocommand to execute
2549 for (;;)
2550 {
2551 // skip removed commands
2552 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2553 if (acp->nextcmd->last)
2554 acp->nextcmd = NULL;
2555 else
2556 acp->nextcmd = acp->nextcmd->next;
2557
2558 if (acp->nextcmd != NULL)
2559 break;
2560
2561 // at end of commands, find next pattern that matches
2562 if (acp->curpat->last)
2563 acp->curpat = NULL;
2564 else
2565 acp->curpat = acp->curpat->next;
2566 if (acp->curpat != NULL)
2567 auto_next_pat(acp, TRUE);
2568 if (acp->curpat == NULL)
2569 return NULL;
2570 }
2571
2572 ac = acp->nextcmd;
2573
2574 if (p_verbose >= 9)
2575 {
2576 verbose_enter_scroll();
2577 smsg(_("autocommand %s"), ac->cmd);
2578 msg_puts("\n"); // don't overwrite this either
2579 verbose_leave_scroll();
2580 }
2581 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002582 // Remove one-shot ("once") autocmd in anticipation of its execution.
2583 if (ac->once)
2584 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002585 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002586 current_sctx = ac->script_ctx;
LemonBoyeca7c602022-04-14 15:39:43 +01002587 acp->script_ctx = current_sctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002588 if (ac->last)
2589 acp->nextcmd = NULL;
2590 else
2591 acp->nextcmd = ac->next;
2592 return retval;
2593}
2594
2595/*
2596 * Return TRUE if there is a matching autocommand for "fname".
2597 * To account for buffer-local autocommands, function needs to know
2598 * in which buffer the file will be opened.
2599 */
2600 int
2601has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2602{
2603 AutoPat *ap;
2604 char_u *fname;
2605 char_u *tail = gettail(sfname);
2606 int retval = FALSE;
2607
2608 fname = FullName_save(sfname, FALSE);
2609 if (fname == NULL)
2610 return FALSE;
2611
2612#ifdef BACKSLASH_IN_FILENAME
2613 /*
2614 * Replace all backslashes with forward slashes. This makes the
2615 * autocommand patterns portable between Unix and MS-DOS.
2616 */
2617 sfname = vim_strsave(sfname);
2618 if (sfname != NULL)
2619 forward_slash(sfname);
2620 forward_slash(fname);
2621#endif
2622
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002623 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002624 if (ap->pat != NULL && ap->cmds != NULL
2625 && (ap->buflocal_nr == 0
2626 ? match_file_pat(NULL, &ap->reg_prog,
2627 fname, sfname, tail, ap->allow_dirs)
2628 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2629 ))
2630 {
2631 retval = TRUE;
2632 break;
2633 }
2634
2635 vim_free(fname);
2636#ifdef BACKSLASH_IN_FILENAME
2637 vim_free(sfname);
2638#endif
2639
2640 return retval;
2641}
2642
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002643/*
2644 * Function given to ExpandGeneric() to obtain the list of autocommand group
2645 * names.
2646 */
2647 char_u *
2648get_augroup_name(expand_T *xp UNUSED, int idx)
2649{
2650 if (idx == augroups.ga_len) // add "END" add the end
2651 return (char_u *)"END";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002652 if (idx < 0 || idx >= augroups.ga_len) // end of list
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002653 return NULL;
2654 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2655 // skip deleted entries
2656 return (char_u *)"";
2657 return AUGROUP_NAME(idx); // return a name
2658}
2659
2660static int include_groups = FALSE;
2661
2662 char_u *
2663set_context_in_autocmd(
2664 expand_T *xp,
2665 char_u *arg,
2666 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2667{
2668 char_u *p;
2669 int group;
2670
2671 // check for a group name, skip it if present
2672 include_groups = FALSE;
2673 p = arg;
2674 group = au_get_grouparg(&arg);
2675 if (group == AUGROUP_ERROR)
2676 return NULL;
2677 // If there only is a group name that's what we expand.
2678 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2679 {
2680 arg = p;
2681 group = AUGROUP_ALL;
2682 }
2683
2684 // skip over event name
2685 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2686 if (*p == ',')
2687 arg = p + 1;
2688 if (*p == NUL)
2689 {
2690 if (group == AUGROUP_ALL)
2691 include_groups = TRUE;
2692 xp->xp_context = EXPAND_EVENTS; // expand event name
2693 xp->xp_pattern = arg;
2694 return NULL;
2695 }
2696
2697 // skip over pattern
2698 arg = skipwhite(p);
2699 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2700 arg++;
2701 if (*arg)
2702 return arg; // expand (next) command
2703
2704 if (doautocmd)
2705 xp->xp_context = EXPAND_FILES; // expand file names
2706 else
2707 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2708 return NULL;
2709}
2710
2711/*
2712 * Function given to ExpandGeneric() to obtain the list of event names.
2713 */
2714 char_u *
2715get_event_name(expand_T *xp UNUSED, int idx)
2716{
2717 if (idx < augroups.ga_len) // First list group names, if wanted
2718 {
2719 if (!include_groups || AUGROUP_NAME(idx) == NULL
2720 || AUGROUP_NAME(idx) == get_deleted_augroup())
2721 return (char_u *)""; // skip deleted entries
2722 return AUGROUP_NAME(idx); // return a name
2723 }
2724 return (char_u *)event_names[idx - augroups.ga_len].name;
2725}
2726
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002727
2728#if defined(FEAT_EVAL) || defined(PROTO)
2729/*
2730 * Return TRUE if autocmd is supported.
2731 */
2732 int
2733autocmd_supported(char_u *name)
2734{
2735 char_u *p;
2736
2737 return (event_name2nr(name, &p) != NUM_EVENTS);
2738}
2739
2740/*
2741 * Return TRUE if an autocommand is defined for a group, event and
2742 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2743 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2744 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2745 * Used for:
2746 * exists("#Group") or
2747 * exists("#Group#Event") or
2748 * exists("#Group#Event#pat") or
2749 * exists("#Event") or
2750 * exists("#Event#pat")
2751 */
2752 int
2753au_exists(char_u *arg)
2754{
2755 char_u *arg_save;
2756 char_u *pattern = NULL;
2757 char_u *event_name;
2758 char_u *p;
2759 event_T event;
2760 AutoPat *ap;
2761 buf_T *buflocal_buf = NULL;
2762 int group;
2763 int retval = FALSE;
2764
2765 // Make a copy so that we can change the '#' chars to a NUL.
2766 arg_save = vim_strsave(arg);
2767 if (arg_save == NULL)
2768 return FALSE;
2769 p = vim_strchr(arg_save, '#');
2770 if (p != NULL)
2771 *p++ = NUL;
2772
2773 // First, look for an autocmd group name
2774 group = au_find_group(arg_save);
2775 if (group == AUGROUP_ERROR)
2776 {
2777 // Didn't match a group name, assume the first argument is an event.
2778 group = AUGROUP_ALL;
2779 event_name = arg_save;
2780 }
2781 else
2782 {
2783 if (p == NULL)
2784 {
2785 // "Group": group name is present and it's recognized
2786 retval = TRUE;
2787 goto theend;
2788 }
2789
2790 // Must be "Group#Event" or "Group#Event#pat".
2791 event_name = p;
2792 p = vim_strchr(event_name, '#');
2793 if (p != NULL)
2794 *p++ = NUL; // "Group#Event#pat"
2795 }
2796
2797 pattern = p; // "pattern" is NULL when there is no pattern
2798
2799 // find the index (enum) for the event name
2800 event = event_name2nr(event_name, &p);
2801
2802 // return FALSE if the event name is not recognized
2803 if (event == NUM_EVENTS)
2804 goto theend;
2805
2806 // Find the first autocommand for this event.
2807 // If there isn't any, return FALSE;
2808 // If there is one and no pattern given, return TRUE;
2809 ap = first_autopat[(int)event];
2810 if (ap == NULL)
2811 goto theend;
2812
2813 // if pattern is "<buffer>", special handling is needed which uses curbuf
2814 // for pattern "<buffer=N>, fnamecmp() will work fine
2815 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2816 buflocal_buf = curbuf;
2817
2818 // Check if there is an autocommand with the given pattern.
2819 for ( ; ap != NULL; ap = ap->next)
2820 // only use a pattern when it has not been removed and has commands.
2821 // For buffer-local autocommands, fnamecmp() works fine.
2822 if (ap->pat != NULL && ap->cmds != NULL
2823 && (group == AUGROUP_ALL || ap->group == group)
2824 && (pattern == NULL
2825 || (buflocal_buf == NULL
2826 ? fnamecmp(ap->pat, pattern) == 0
2827 : ap->buflocal_nr == buflocal_buf->b_fnum)))
2828 {
2829 retval = TRUE;
2830 break;
2831 }
2832
2833theend:
2834 vim_free(arg_save);
2835 return retval;
2836}
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002837
2838/*
2839 * autocmd_add() and autocmd_delete() functions
2840 */
2841 static void
2842autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
2843{
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002844 list_T *aucmd_list;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002845 listitem_T *li;
2846 dict_T *event_dict;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002847 dictitem_T *di;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002848 char_u *event_name = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002849 list_T *event_list;
2850 listitem_T *eli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002851 event_T event;
2852 char_u *group_name = NULL;
2853 int group;
2854 char_u *pat = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002855 list_T *pat_list;
2856 listitem_T *pli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002857 char_u *cmd = NULL;
2858 char_u *end;
2859 int once;
2860 int nested;
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01002861 int replace; // replace the cmd for a group/event
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002862 int retval = VVAL_TRUE;
2863 int save_augroup = current_augroup;
2864
2865 rettv->v_type = VAR_BOOL;
2866 rettv->vval.v_number = VVAL_FALSE;
2867
2868 if (check_for_list_arg(argvars, 0) == FAIL)
2869 return;
2870
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002871 aucmd_list = argvars[0].vval.v_list;
2872 if (aucmd_list == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002873 return;
2874
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002875 FOR_ALL_LIST_ITEMS(aucmd_list, li)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002876 {
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002877 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002878 VIM_CLEAR(cmd);
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002879 event_name = NULL;
2880 event_list = NULL;
2881 pat = NULL;
2882 pat_list = NULL;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002883
2884 if (li->li_tv.v_type != VAR_DICT)
2885 continue;
2886
2887 event_dict = li->li_tv.vval.v_dict;
2888 if (event_dict == NULL)
2889 continue;
2890
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002891 di = dict_find(event_dict, (char_u *)"event", -1);
2892 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002893 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002894 if (di->di_tv.v_type == VAR_STRING)
2895 {
2896 event_name = di->di_tv.vval.v_string;
2897 if (event_name == NULL)
2898 {
2899 emsg(_(e_string_required));
2900 continue;
2901 }
2902 }
2903 else if (di->di_tv.v_type == VAR_LIST)
2904 {
2905 event_list = di->di_tv.vval.v_list;
2906 if (event_list == NULL)
2907 {
2908 emsg(_(e_list_required));
2909 continue;
2910 }
2911 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002912 else
2913 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002914 emsg(_(e_string_or_list_expected));
2915 continue;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002916 }
2917 }
2918
Bram Moolenaard61efa52022-07-23 09:52:04 +01002919 group_name = dict_get_string(event_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002920 if (group_name == NULL || *group_name == NUL)
2921 // if the autocmd group name is not specified, then use the current
2922 // autocmd group
2923 group = current_augroup;
2924 else
2925 {
2926 group = au_find_group(group_name);
2927 if (group == AUGROUP_ERROR)
2928 {
2929 if (delete)
2930 {
2931 semsg(_(e_no_such_group_str), group_name);
2932 retval = VVAL_FALSE;
2933 break;
2934 }
2935 // group is not found, create it now
2936 group = au_new_group(group_name);
2937 if (group == AUGROUP_ERROR)
2938 {
2939 semsg(_(e_no_such_group_str), group_name);
2940 retval = VVAL_FALSE;
2941 break;
2942 }
2943
2944 current_augroup = group;
2945 }
2946 }
2947
2948 // if a buffer number is specified, then generate a pattern of the form
2949 // "<buffer=n>. Otherwise, use the pattern supplied by the user.
2950 if (dict_has_key(event_dict, "bufnr"))
2951 {
2952 varnumber_T bnum;
2953
Bram Moolenaard61efa52022-07-23 09:52:04 +01002954 bnum = dict_get_number_def(event_dict, "bufnr", -1);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002955 if (bnum == -1)
2956 continue;
2957
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002958 vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
2959 pat = IObuff;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002960 }
2961 else
2962 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002963 di = dict_find(event_dict, (char_u *)"pattern", -1);
2964 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002965 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002966 if (di->di_tv.v_type == VAR_STRING)
2967 {
2968 pat = di->di_tv.vval.v_string;
2969 if (pat == NULL)
2970 {
2971 emsg(_(e_string_required));
2972 continue;
2973 }
2974 }
2975 else if (di->di_tv.v_type == VAR_LIST)
2976 {
2977 pat_list = di->di_tv.vval.v_list;
2978 if (pat_list == NULL)
2979 {
2980 emsg(_(e_list_required));
2981 continue;
2982 }
2983 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002984 else
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002985 {
2986 emsg(_(e_string_or_list_expected));
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002987 continue;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002988 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002989 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002990 else if (delete)
2991 pat = (char_u *)"";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002992 }
2993
Bram Moolenaard61efa52022-07-23 09:52:04 +01002994 once = dict_get_bool(event_dict, "once", FALSE);
2995 nested = dict_get_bool(event_dict, "nested", FALSE);
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01002996 // if 'replace' is true, then remove all the commands associated with
2997 // this autocmd event/group and add the new command.
Bram Moolenaard61efa52022-07-23 09:52:04 +01002998 replace = dict_get_bool(event_dict, "replace", FALSE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002999
Bram Moolenaard61efa52022-07-23 09:52:04 +01003000 cmd = dict_get_string(event_dict, "cmd", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003001 if (cmd == NULL)
3002 {
3003 if (delete)
3004 cmd = vim_strsave((char_u *)"");
3005 else
3006 continue;
3007 }
3008
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003009 if (delete && (event_name == NULL
3010 || (event_name[0] == '*' && event_name[1] == NUL)))
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003011 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003012 // if the event name is not specified or '*', delete all the events
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003013 for (event = (event_T)0; (int)event < NUM_EVENTS;
3014 event = (event_T)((int)event + 1))
3015 {
3016 if (do_autocmd_event(event, pat, once, nested, cmd, delete,
3017 group, 0) == FAIL)
3018 {
3019 retval = VVAL_FALSE;
3020 break;
3021 }
3022 }
3023 }
3024 else
3025 {
Bram Moolenaar968443e2022-05-27 21:16:34 +01003026 char_u *p = NULL;
3027
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003028 eli = NULL;
3029 end = NULL;
3030 while (TRUE)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003031 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003032 if (event_list != NULL)
3033 {
3034 if (eli == NULL)
3035 eli = event_list->lv_first;
3036 else
3037 eli = eli->li_next;
3038 if (eli == NULL)
3039 break;
3040 if (eli->li_tv.v_type != VAR_STRING
Bram Moolenaar882476a2022-06-01 16:02:38 +01003041 || (p = eli->li_tv.vval.v_string) == NULL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003042 {
3043 emsg(_(e_string_required));
Bram Moolenaar882476a2022-06-01 16:02:38 +01003044 break;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003045 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003046 }
3047 else
3048 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003049 if (p == NULL)
3050 p = event_name;
3051 if (p == NULL || *p == NUL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003052 break;
3053 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003054
3055 event = event_name2nr(p, &end);
3056 if (event == NUM_EVENTS || *end != NUL)
3057 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003058 // this also catches something following a valid event name
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003059 semsg(_(e_no_such_event_str), p);
3060 retval = VVAL_FALSE;
3061 break;
3062 }
3063 if (pat != NULL)
3064 {
3065 if (do_autocmd_event(event, pat, once, nested, cmd,
3066 delete | replace, group, 0) == FAIL)
3067 {
3068 retval = VVAL_FALSE;
3069 break;
3070 }
3071 }
3072 else if (pat_list != NULL)
3073 {
3074 FOR_ALL_LIST_ITEMS(pat_list, pli)
3075 {
3076 if (pli->li_tv.v_type != VAR_STRING
3077 || pli->li_tv.vval.v_string == NULL)
3078 {
3079 emsg(_(e_string_required));
3080 continue;
3081 }
3082 if (do_autocmd_event(event,
3083 pli->li_tv.vval.v_string, once, nested,
3084 cmd, delete | replace, group, 0) ==
3085 FAIL)
3086 {
3087 retval = VVAL_FALSE;
3088 break;
3089 }
3090 }
3091 if (retval == VVAL_FALSE)
3092 break;
3093 }
3094 if (event_name != NULL)
3095 p = end;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003096 }
3097 }
3098
3099 // if only the autocmd group name is specified for delete and the
3100 // autocmd event, pattern and cmd are not specified, then delete the
3101 // autocmd group.
3102 if (delete && group_name != NULL &&
3103 (event_name == NULL || event_name[0] == NUL)
3104 && (pat == NULL || pat[0] == NUL)
3105 && (cmd == NULL || cmd[0] == NUL))
3106 au_del_group(group_name);
3107 }
3108
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003109 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003110 VIM_CLEAR(cmd);
3111
3112 current_augroup = save_augroup;
3113 rettv->vval.v_number = retval;
3114}
3115
3116/*
3117 * autocmd_add() function
3118 */
3119 void
3120f_autocmd_add(typval_T *argvars, typval_T *rettv)
3121{
3122 autocmd_add_or_delete(argvars, rettv, FALSE);
3123}
3124
3125/*
3126 * autocmd_delete() function
3127 */
3128 void
3129f_autocmd_delete(typval_T *argvars, typval_T *rettv)
3130{
3131 autocmd_add_or_delete(argvars, rettv, TRUE);
3132}
3133
3134/*
3135 * autocmd_get() function
3136 * Returns a List of autocmds.
3137 */
3138 void
3139f_autocmd_get(typval_T *argvars, typval_T *rettv)
3140{
3141 event_T event_arg = NUM_EVENTS;
3142 event_T event;
3143 AutoPat *ap;
3144 AutoCmd *ac;
3145 list_T *event_list;
3146 dict_T *event_dict;
3147 char_u *event_name = NULL;
3148 char_u *pat = NULL;
3149 char_u *name = NULL;
3150 int group = AUGROUP_ALL;
3151
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01003152 if (rettv_list_alloc(rettv) == FAIL)
3153 return;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003154 if (check_for_opt_dict_arg(argvars, 0) == FAIL)
3155 return;
3156
3157 if (argvars[0].v_type == VAR_DICT)
3158 {
3159 // return only the autocmds in the specified group
3160 if (dict_has_key(argvars[0].vval.v_dict, "group"))
3161 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003162 name = dict_get_string(argvars[0].vval.v_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003163 if (name == NULL)
3164 return;
3165
3166 if (*name == NUL)
3167 group = AUGROUP_DEFAULT;
3168 else
3169 {
3170 group = au_find_group(name);
3171 if (group == AUGROUP_ERROR)
3172 {
3173 semsg(_(e_no_such_group_str), name);
3174 vim_free(name);
3175 return;
3176 }
3177 }
3178 vim_free(name);
3179 }
3180
3181 // return only the autocmds for the specified event
3182 if (dict_has_key(argvars[0].vval.v_dict, "event"))
3183 {
3184 int i;
3185
Bram Moolenaard61efa52022-07-23 09:52:04 +01003186 name = dict_get_string(argvars[0].vval.v_dict, "event", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003187 if (name == NULL)
3188 return;
3189
3190 if (name[0] == '*' && name[1] == NUL)
3191 event_arg = NUM_EVENTS;
3192 else
3193 {
3194 for (i = 0; event_names[i].name != NULL; i++)
3195 if (STRICMP(event_names[i].name, name) == 0)
3196 break;
3197 if (event_names[i].name == NULL)
3198 {
3199 semsg(_(e_no_such_event_str), name);
3200 vim_free(name);
3201 return;
3202 }
3203 event_arg = event_names[i].event;
3204 }
3205 vim_free(name);
3206 }
3207
3208 // return only the autocmds for the specified pattern
3209 if (dict_has_key(argvars[0].vval.v_dict, "pattern"))
3210 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003211 pat = dict_get_string(argvars[0].vval.v_dict, "pattern", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003212 if (pat == NULL)
3213 return;
3214 }
3215 }
3216
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003217 event_list = rettv->vval.v_list;
3218
3219 // iterate through all the autocmd events
3220 for (event = (event_T)0; (int)event < NUM_EVENTS;
3221 event = (event_T)((int)event + 1))
3222 {
3223 if (event_arg != NUM_EVENTS && event != event_arg)
3224 continue;
3225
3226 event_name = event_nr2name(event);
3227
3228 // iterate through all the patterns for this autocmd event
3229 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
3230 {
3231 char_u *group_name;
3232
3233 if (group != AUGROUP_ALL && group != ap->group)
3234 continue;
3235
3236 if (pat != NULL && STRCMP(pat, ap->pat) != 0)
3237 continue;
3238
3239 group_name = get_augroup_name(NULL, ap->group);
3240
3241 // iterate through all the commands for this pattern and add one
3242 // item for each cmd.
3243 for (ac = ap->cmds; ac != NULL; ac = ac->next)
3244 {
3245 event_dict = dict_alloc();
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003246 if (event_dict == NULL
3247 || list_append_dict(event_list, event_dict) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003248 return;
3249
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003250 if (dict_add_string(event_dict, "event", event_name) == FAIL
3251 || dict_add_string(event_dict, "group",
3252 group_name == NULL ? (char_u *)""
3253 : group_name) == FAIL
3254 || (ap->buflocal_nr != 0
3255 && (dict_add_number(event_dict, "bufnr",
3256 ap->buflocal_nr) == FAIL))
3257 || dict_add_string(event_dict, "pattern",
3258 ap->pat) == FAIL
3259 || dict_add_string(event_dict, "cmd", ac->cmd) == FAIL
3260 || dict_add_bool(event_dict, "once", ac->once) == FAIL
3261 || dict_add_bool(event_dict, "nested",
3262 ac->nested) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003263 return;
3264 }
3265 }
3266 }
3267
3268 vim_free(pat);
3269}
3270
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01003271#endif