blob: 8e43b34473339c0c357f8955f67bca315e3e39c8 [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},
Danek Duvalld7d56032024-01-14 20:19:59 +0100182 {"TermResponseAll", EVENT_TERMRESPONSEALL},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100183 {"TextChanged", EVENT_TEXTCHANGED},
184 {"TextChangedI", EVENT_TEXTCHANGEDI},
185 {"TextChangedP", EVENT_TEXTCHANGEDP},
Shougo Matsushita4ccaedf2022-10-15 11:48:00 +0100186 {"TextChangedT", EVENT_TEXTCHANGEDT},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100187 {"User", EVENT_USER},
188 {"VimEnter", EVENT_VIMENTER},
189 {"VimLeave", EVENT_VIMLEAVE},
190 {"VimLeavePre", EVENT_VIMLEAVEPRE},
Sergey Vlasov1f47db72024-01-25 23:07:00 +0100191 {"WinNewPre", EVENT_WINNEWPRE},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100192 {"WinNew", EVENT_WINNEW},
naohiro ono23beefe2021-11-13 12:38:49 +0000193 {"WinClosed", EVENT_WINCLOSED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100194 {"WinEnter", EVENT_WINENTER},
195 {"WinLeave", EVENT_WINLEAVE},
Bram Moolenaar35fc61c2022-11-22 12:40:50 +0000196 {"WinResized", EVENT_WINRESIZED},
LemonBoy09371822022-04-08 15:18:45 +0100197 {"WinScrolled", EVENT_WINSCROLLED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100198 {"VimResized", EVENT_VIMRESIZED},
199 {"TextYankPost", EVENT_TEXTYANKPOST},
Bram Moolenaar100118c2020-12-11 19:30:34 +0100200 {"VimSuspend", EVENT_VIMSUSPEND},
201 {"VimResume", EVENT_VIMRESUME},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100202 {NULL, (event_T)0}
203};
204
205static AutoPat *first_autopat[NUM_EVENTS] =
206{
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 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
212 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
213};
214
215static AutoPat *last_autopat[NUM_EVENTS] =
216{
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 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
222 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
223};
224
kylo252ae6f1d82022-02-16 19:24:07 +0000225#define AUGROUP_DEFAULT (-1) // default autocmd group
226#define AUGROUP_ERROR (-2) // erroneous autocmd group
227#define AUGROUP_ALL (-3) // all autocmd groups
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100228
229/*
230 * struct used to keep status while executing autocommands for an event.
231 */
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100232struct AutoPatCmd_S
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100233{
234 AutoPat *curpat; // next AutoPat to examine
235 AutoCmd *nextcmd; // next AutoCmd to execute
236 int group; // group being used
237 char_u *fname; // fname to match with
238 char_u *sfname; // sfname to match with
239 char_u *tail; // tail of fname
240 event_T event; // current event
LemonBoyeca7c602022-04-14 15:39:43 +0100241 sctx_T script_ctx; // script context where it is defined
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100242 int arg_bufnr; // Initially equal to <abuf>, set to zero when
243 // buf is deleted.
LemonBoyeca7c602022-04-14 15:39:43 +0100244 AutoPatCmd_T *next; // chain of active apc-s for auto-invalidation
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100245};
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100246
LemonBoyeca7c602022-04-14 15:39:43 +0100247static AutoPatCmd_T *active_apc_list = NULL; // stack of active autocommands
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100248
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200249// Macro to loop over all the patterns for an autocmd event
250#define FOR_ALL_AUTOCMD_PATTERNS(event, ap) \
251 for ((ap) = first_autopat[(int)(event)]; (ap) != NULL; (ap) = (ap)->next)
252
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100253/*
254 * augroups stores a list of autocmd group names.
255 */
256static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
257#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
Bram Moolenaarc667da52019-11-30 20:52:27 +0100258// use get_deleted_augroup() to get this
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100259static char_u *deleted_augroup = NULL;
260
261/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100262 * The ID of the current group. Group 0 is the default one.
263 */
264static int current_augroup = AUGROUP_DEFAULT;
265
Bram Moolenaarc667da52019-11-30 20:52:27 +0100266static int au_need_clean = FALSE; // need to delete marked patterns
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100267
268static char_u *event_nr2name(event_T event);
269static int au_get_grouparg(char_u **argp);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200270static 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 +0100271static 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 +0100272static void auto_next_pat(AutoPatCmd_T *apc, int stop_at_last);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100273static int au_find_group(char_u *name);
274
275static event_T last_event;
276static int last_group;
Bram Moolenaarc667da52019-11-30 20:52:27 +0100277static int autocmd_blocked = 0; // block all autocmds
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100278
279 static char_u *
280get_deleted_augroup(void)
281{
282 if (deleted_augroup == NULL)
283 deleted_augroup = (char_u *)_("--Deleted--");
284 return deleted_augroup;
285}
286
287/*
288 * Show the autocommands for one AutoPat.
289 */
290 static void
291show_autocmd(AutoPat *ap, event_T event)
292{
293 AutoCmd *ac;
294
295 // Check for "got_int" (here and at various places below), which is set
296 // when "q" has been hit for the "--more--" prompt
297 if (got_int)
298 return;
299 if (ap->pat == NULL) // pattern has been removed
300 return;
301
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000302 // Make sure no info referenced by "ap" is cleared, e.g. when a timer
303 // clears an augroup. Jump to "theend" after this!
304 // "ap->pat" may be cleared anyway.
305 ++autocmd_busy;
306
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100307 msg_putchar('\n');
308 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000309 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100310 if (event != last_event || ap->group != last_group)
311 {
312 if (ap->group != AUGROUP_DEFAULT)
313 {
314 if (AUGROUP_NAME(ap->group) == NULL)
315 msg_puts_attr((char *)get_deleted_augroup(), HL_ATTR(HLF_E));
316 else
317 msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
318 msg_puts(" ");
319 }
320 msg_puts_attr((char *)event_nr2name(event), HL_ATTR(HLF_T));
321 last_event = event;
322 last_group = ap->group;
323 msg_putchar('\n');
324 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000325 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100326 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000327
328 if (ap->pat == NULL)
329 goto theend; // timer might have cleared the pattern or group
330
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100331 msg_col = 4;
332 msg_outtrans(ap->pat);
333
334 for (ac = ap->cmds; ac != NULL; ac = ac->next)
335 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100336 if (ac->cmd == NULL) // skip removed commands
337 continue;
338
339 if (msg_col >= 14)
340 msg_putchar('\n');
341 msg_col = 14;
342 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000343 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100344 msg_outtrans(ac->cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100345#ifdef FEAT_EVAL
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100346 if (p_verbose > 0)
347 last_set_msg(ac->script_ctx);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100348#endif
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100349 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000350 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100351 if (ac->next != NULL)
352 {
353 msg_putchar('\n');
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100354 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000355 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100356 }
357 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000358
359theend:
360 --autocmd_busy;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100361}
362
363/*
364 * Mark an autocommand pattern for deletion.
365 */
366 static void
367au_remove_pat(AutoPat *ap)
368{
369 VIM_CLEAR(ap->pat);
370 ap->buflocal_nr = -1;
371 au_need_clean = TRUE;
372}
373
374/*
375 * Mark all commands for a pattern for deletion.
376 */
377 static void
378au_remove_cmds(AutoPat *ap)
379{
380 AutoCmd *ac;
381
382 for (ac = ap->cmds; ac != NULL; ac = ac->next)
383 VIM_CLEAR(ac->cmd);
384 au_need_clean = TRUE;
385}
386
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200387// Delete one command from an autocmd pattern.
388static void au_del_cmd(AutoCmd *ac)
389{
390 VIM_CLEAR(ac->cmd);
391 au_need_clean = TRUE;
392}
393
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100394/*
395 * Cleanup autocommands and patterns that have been deleted.
396 * This is only done when not executing autocommands.
397 */
398 static void
399au_cleanup(void)
400{
401 AutoPat *ap, **prev_ap;
402 AutoCmd *ac, **prev_ac;
403 event_T event;
404
405 if (autocmd_busy || !au_need_clean)
406 return;
407
408 // loop over all events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100409 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100410 event = (event_T)((int)event + 1))
411 {
412 // loop over all autocommand patterns
413 prev_ap = &(first_autopat[(int)event]);
414 for (ap = *prev_ap; ap != NULL; ap = *prev_ap)
415 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200416 int has_cmd = FALSE;
417
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200418 // loop over all commands for this pattern
419 prev_ac = &(ap->cmds);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100420 for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
421 {
422 // remove the command if the pattern is to be deleted or when
423 // the command has been marked for deletion
424 if (ap->pat == NULL || ac->cmd == NULL)
425 {
426 *prev_ac = ac->next;
427 vim_free(ac->cmd);
428 vim_free(ac);
429 }
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200430 else
431 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200432 has_cmd = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100433 prev_ac = &(ac->next);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200434 }
435 }
436
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200437 if (ap->pat != NULL && !has_cmd)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200438 // Pattern was not marked for deletion, but all of its
439 // commands were. So mark the pattern for deletion.
440 au_remove_pat(ap);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100441
442 // remove the pattern if it has been marked for deletion
443 if (ap->pat == NULL)
444 {
445 if (ap->next == NULL)
446 {
447 if (prev_ap == &(first_autopat[(int)event]))
448 last_autopat[(int)event] = NULL;
449 else
450 // this depends on the "next" field being the first in
451 // the struct
452 last_autopat[(int)event] = (AutoPat *)prev_ap;
453 }
454 *prev_ap = ap->next;
455 vim_regfree(ap->reg_prog);
456 vim_free(ap);
457 }
458 else
459 prev_ap = &(ap->next);
460 }
461 }
462
463 au_need_clean = FALSE;
464}
465
466/*
467 * Called when buffer is freed, to remove/invalidate related buffer-local
468 * autocmds.
469 */
470 void
471aubuflocal_remove(buf_T *buf)
472{
LemonBoyeca7c602022-04-14 15:39:43 +0100473 AutoPat *ap;
474 event_T event;
475 AutoPatCmd_T *apc;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100476
477 // invalidate currently executing autocommands
478 for (apc = active_apc_list; apc; apc = apc->next)
479 if (buf->b_fnum == apc->arg_bufnr)
480 apc->arg_bufnr = 0;
481
482 // invalidate buflocals looping through events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100483 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100484 event = (event_T)((int)event + 1))
485 // loop over all autocommand patterns
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200486 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100487 if (ap->buflocal_nr == buf->b_fnum)
488 {
489 au_remove_pat(ap);
490 if (p_verbose >= 6)
491 {
492 verbose_enter();
493 smsg(_("auto-removing autocommand: %s <buffer=%d>"),
494 event_nr2name(event), buf->b_fnum);
495 verbose_leave();
496 }
497 }
498 au_cleanup();
499}
500
501/*
502 * Add an autocmd group name.
503 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
504 */
505 static int
506au_new_group(char_u *name)
507{
508 int i;
509
510 i = au_find_group(name);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100511 if (i != AUGROUP_ERROR)
512 return i;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100513
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100514 // the group doesn't exist yet, add it. First try using a free entry.
515 for (i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100516 if (AUGROUP_NAME(i) == NULL)
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100517 break;
518 if (i == augroups.ga_len && ga_grow(&augroups, 1) == FAIL)
519 return AUGROUP_ERROR;
520
521 AUGROUP_NAME(i) = vim_strsave(name);
522 if (AUGROUP_NAME(i) == NULL)
523 return AUGROUP_ERROR;
524 if (i == augroups.ga_len)
525 ++augroups.ga_len;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100526
527 return i;
528}
529
530 static void
531au_del_group(char_u *name)
532{
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100533 int i;
534 event_T event;
535 AutoPat *ap;
536 int in_use = FALSE;
537
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100538
539 i = au_find_group(name);
540 if (i == AUGROUP_ERROR) // the group doesn't exist
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100541 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100542 semsg(_(e_no_such_group_str), name);
543 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100544 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100545 if (i == current_augroup)
546 {
547 emsg(_(e_cannot_delete_current_group));
548 return;
549 }
550
551 for (event = (event_T)0; (int)event < NUM_EVENTS;
552 event = (event_T)((int)event + 1))
553 {
554 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
555 if (ap->group == i && ap->pat != NULL)
556 {
557 give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
558 in_use = TRUE;
559 event = NUM_EVENTS;
560 break;
561 }
562 }
563 vim_free(AUGROUP_NAME(i));
564 if (in_use)
565 AUGROUP_NAME(i) = get_deleted_augroup();
566 else
567 AUGROUP_NAME(i) = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100568}
569
570/*
571 * Find the ID of an autocmd group name.
572 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
573 */
574 static int
575au_find_group(char_u *name)
576{
577 int i;
578
579 for (i = 0; i < augroups.ga_len; ++i)
580 if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
581 && STRCMP(AUGROUP_NAME(i), name) == 0)
582 return i;
583 return AUGROUP_ERROR;
584}
585
586/*
587 * Return TRUE if augroup "name" exists.
588 */
589 int
590au_has_group(char_u *name)
591{
592 return au_find_group(name) != AUGROUP_ERROR;
593}
594
595/*
596 * ":augroup {name}".
597 */
598 void
599do_augroup(char_u *arg, int del_group)
600{
601 int i;
602
603 if (del_group)
604 {
605 if (*arg == NUL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000606 emsg(_(e_argument_required));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100607 else
608 au_del_group(arg);
609 }
610 else if (STRICMP(arg, "end") == 0) // ":aug end": back to group 0
611 current_augroup = AUGROUP_DEFAULT;
612 else if (*arg) // ":aug xxx": switch to group xxx
613 {
614 i = au_new_group(arg);
615 if (i != AUGROUP_ERROR)
616 current_augroup = i;
617 }
618 else // ":aug": list the group names
619 {
620 msg_start();
621 for (i = 0; i < augroups.ga_len; ++i)
622 {
623 if (AUGROUP_NAME(i) != NULL)
624 {
625 msg_puts((char *)AUGROUP_NAME(i));
626 msg_puts(" ");
627 }
628 }
629 msg_clr_eos();
630 msg_end();
631 }
632}
633
Bram Moolenaare76062c2022-11-28 18:51:43 +0000634 void
635autocmd_init(void)
636{
637 CLEAR_FIELD(aucmd_win);
638}
639
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100640#if defined(EXITFREE) || defined(PROTO)
641 void
642free_all_autocmds(void)
643{
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100644 char_u *s;
645
646 for (current_augroup = -1; current_augroup < augroups.ga_len;
647 ++current_augroup)
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200648 do_autocmd(NULL, (char_u *)"", TRUE);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100649
Bram Moolenaare76062c2022-11-28 18:51:43 +0000650 for (int i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100651 {
652 s = ((char_u **)(augroups.ga_data))[i];
653 if (s != get_deleted_augroup())
654 vim_free(s);
655 }
656 ga_clear(&augroups);
Bram Moolenaare76062c2022-11-28 18:51:43 +0000657
Bram Moolenaar84497cd2022-11-28 20:34:52 +0000658 // aucmd_win[] is freed in win_free_all()
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100659}
660#endif
661
662/*
Bram Moolenaare76062c2022-11-28 18:51:43 +0000663 * Return TRUE if "win" is an active entry in aucmd_win[].
664 */
665 int
666is_aucmd_win(win_T *win)
667{
668 for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
669 if (aucmd_win[i].auc_win_used && aucmd_win[i].auc_win == win)
670 return TRUE;
671 return FALSE;
672}
673
674/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100675 * Return the event number for event name "start".
676 * Return NUM_EVENTS if the event name was not found.
677 * Return a pointer to the next event name in "end".
678 */
679 static event_T
680event_name2nr(char_u *start, char_u **end)
681{
682 char_u *p;
683 int i;
684 int len;
685
686 // the event name ends with end of line, '|', a blank or a comma
687 for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
688 ;
689 for (i = 0; event_names[i].name != NULL; ++i)
690 {
691 len = (int)STRLEN(event_names[i].name);
692 if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0)
693 break;
694 }
695 if (*p == ',')
696 ++p;
697 *end = p;
698 if (event_names[i].name == NULL)
699 return NUM_EVENTS;
700 return event_names[i].event;
701}
702
703/*
704 * Return the name for event "event".
705 */
706 static char_u *
707event_nr2name(event_T event)
708{
709 int i;
710
711 for (i = 0; event_names[i].name != NULL; ++i)
712 if (event_names[i].event == event)
713 return (char_u *)event_names[i].name;
714 return (char_u *)"Unknown";
715}
716
717/*
718 * Scan over the events. "*" stands for all events.
719 */
720 static char_u *
721find_end_event(
722 char_u *arg,
723 int have_group) // TRUE when group name was found
724{
725 char_u *pat;
726 char_u *p;
727
728 if (*arg == '*')
729 {
730 if (arg[1] && !VIM_ISWHITE(arg[1]))
731 {
Bram Moolenaar6d057012021-12-31 18:49:43 +0000732 semsg(_(e_illegal_character_after_star_str), arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100733 return NULL;
734 }
735 pat = arg + 1;
736 }
737 else
738 {
739 for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p)
740 {
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100741 if ((int)event_name2nr(pat, &p) >= NUM_EVENTS)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100742 {
743 if (have_group)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000744 semsg(_(e_no_such_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100745 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000746 semsg(_(e_no_such_group_or_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100747 return NULL;
748 }
749 }
750 }
751 return pat;
752}
753
754/*
755 * Return TRUE if "event" is included in 'eventignore'.
756 */
757 static int
758event_ignored(event_T event)
759{
760 char_u *p = p_ei;
761
762 while (*p != NUL)
763 {
764 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
765 return TRUE;
766 if (event_name2nr(p, &p) == event)
767 return TRUE;
768 }
769
770 return FALSE;
771}
772
773/*
774 * Return OK when the contents of p_ei is valid, FAIL otherwise.
775 */
776 int
777check_ei(void)
778{
779 char_u *p = p_ei;
780
781 while (*p)
782 {
783 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
784 {
785 p += 3;
786 if (*p == ',')
787 ++p;
788 }
789 else if (event_name2nr(p, &p) == NUM_EVENTS)
790 return FAIL;
791 }
792
793 return OK;
794}
795
796# if defined(FEAT_SYN_HL) || defined(PROTO)
797
798/*
799 * Add "what" to 'eventignore' to skip loading syntax highlighting for every
800 * buffer loaded into the window. "what" must start with a comma.
801 * Returns the old value of 'eventignore' in allocated memory.
802 */
803 char_u *
804au_event_disable(char *what)
805{
806 char_u *new_ei;
807 char_u *save_ei;
808
809 save_ei = vim_strsave(p_ei);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100810 if (save_ei == NULL)
811 return NULL;
812
813 new_ei = vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what));
814 if (new_ei == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100815 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100816 vim_free(save_ei);
817 return NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100818 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100819
820 if (*what == ',' && *p_ei == NUL)
821 STRCPY(new_ei, what + 1);
822 else
823 STRCAT(new_ei, what);
824 set_string_option_direct((char_u *)"ei", -1, new_ei,
825 OPT_FREE, SID_NONE);
826 vim_free(new_ei);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100827 return save_ei;
828}
829
830 void
831au_event_restore(char_u *old_ei)
832{
833 if (old_ei != NULL)
834 {
835 set_string_option_direct((char_u *)"ei", -1, old_ei,
836 OPT_FREE, SID_NONE);
837 vim_free(old_ei);
838 }
839}
Bram Moolenaarc667da52019-11-30 20:52:27 +0100840# endif // FEAT_SYN_HL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100841
842/*
843 * do_autocmd() -- implements the :autocmd command. Can be used in the
844 * following ways:
845 *
846 * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
847 * will be automatically executed for <event>
848 * when editing a file matching <pat>, in
849 * the current group.
850 * :autocmd <event> <pat> Show the autocommands associated with
851 * <event> and <pat>.
852 * :autocmd <event> Show the autocommands associated with
853 * <event>.
854 * :autocmd Show all autocommands.
855 * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
856 * <event> and <pat>, and add the command
857 * <cmd>, for the current group.
858 * :autocmd! <event> <pat> Remove all autocommands associated with
859 * <event> and <pat> for the current group.
860 * :autocmd! <event> Remove all autocommands associated with
861 * <event> for the current group.
862 * :autocmd! Remove ALL autocommands for the current
863 * group.
864 *
865 * Multiple events and patterns may be given separated by commas. Here are
866 * some examples:
867 * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
868 * :autocmd bufleave * set tw=79 nosmartindent ic infercase
869 *
870 * :autocmd * *.c show all autocommands for *.c files.
871 *
872 * Mostly a {group} argument can optionally appear before <event>.
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200873 * "eap" can be NULL.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100874 */
875 void
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200876do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100877{
878 char_u *arg = arg_in;
879 char_u *pat;
880 char_u *envpat = NULL;
881 char_u *cmd;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200882 int cmd_need_free = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100883 event_T event;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200884 char_u *tofree = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100885 int nested = FALSE;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200886 int once = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100887 int group;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200888 int i;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200889 int flags = 0;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100890
891 if (*arg == '|')
892 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000893 eap->nextcmd = arg + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100894 arg = (char_u *)"";
895 group = AUGROUP_ALL; // no argument, use all groups
896 }
897 else
898 {
899 /*
900 * Check for a legal group name. If not, use AUGROUP_ALL.
901 */
902 group = au_get_grouparg(&arg);
903 if (arg == NULL) // out of memory
904 return;
905 }
906
907 /*
908 * Scan over the events.
909 * If we find an illegal name, return here, don't do anything.
910 */
911 pat = find_end_event(arg, group != AUGROUP_ALL);
912 if (pat == NULL)
913 return;
914
915 pat = skipwhite(pat);
916 if (*pat == '|')
917 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000918 eap->nextcmd = pat + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100919 pat = (char_u *)"";
920 cmd = (char_u *)"";
921 }
922 else
923 {
924 /*
925 * Scan over the pattern. Put a NUL at the end.
926 */
927 cmd = pat;
928 while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
929 cmd++;
930 if (*cmd)
931 *cmd++ = NUL;
932
933 // Expand environment variables in the pattern. Set 'shellslash', we
934 // want forward slashes here.
935 if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
936 {
937#ifdef BACKSLASH_IN_FILENAME
938 int p_ssl_save = p_ssl;
939
940 p_ssl = TRUE;
941#endif
942 envpat = expand_env_save(pat);
943#ifdef BACKSLASH_IN_FILENAME
944 p_ssl = p_ssl_save;
945#endif
946 if (envpat != NULL)
947 pat = envpat;
948 }
949
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100950 cmd = skipwhite(cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200951 for (i = 0; i < 2; i++)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100952 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100953 if (*cmd == NUL)
954 continue;
955
956 // Check for "++once" flag.
957 if (STRNCMP(cmd, "++once", 6) == 0 && VIM_ISWHITE(cmd[6]))
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200958 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100959 if (once)
960 semsg(_(e_duplicate_argument_str), "++once");
961 once = TRUE;
962 cmd = skipwhite(cmd + 6);
963 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200964
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100965 // Check for "++nested" flag.
966 if ((STRNCMP(cmd, "++nested", 8) == 0 && VIM_ISWHITE(cmd[8])))
967 {
968 if (nested)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200969 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100970 semsg(_(e_duplicate_argument_str), "++nested");
971 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200972 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100973 nested = TRUE;
974 cmd = skipwhite(cmd + 8);
975 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200976
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100977 // Check for the old "nested" flag in legacy script.
978 if (STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
979 {
980 if (in_vim9script())
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200981 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100982 // If there ever is a :nested command this error should
983 // be removed and "nested" accepted as the start of the
984 // command.
985 emsg(_(e_invalid_command_nested_did_you_mean_plusplus_nested));
986 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200987 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100988 if (nested)
989 {
990 semsg(_(e_duplicate_argument_str), "nested");
991 return;
992 }
993 nested = TRUE;
994 cmd = skipwhite(cmd + 6);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200995 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100996 }
997
998 /*
999 * Find the start of the commands.
1000 * Expand <sfile> in it.
1001 */
1002 if (*cmd != NUL)
1003 {
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001004 if (eap != NULL)
1005 // Read a {} block if it follows.
1006 cmd = may_get_cmd_block(eap, cmd, &tofree, &flags);
1007
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001008 cmd = expand_sfile(cmd);
1009 if (cmd == NULL) // some error
1010 return;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001011 cmd_need_free = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001012 }
1013 }
1014
1015 /*
1016 * Print header when showing autocommands.
1017 */
1018 if (!forceit && *cmd == NUL)
1019 // Highlight title
1020 msg_puts_title(_("\n--- Autocommands ---"));
1021
1022 /*
1023 * Loop over the events.
1024 */
1025 last_event = (event_T)-1; // for listing the event name
1026 last_group = AUGROUP_ERROR; // for listing the group name
1027 if (*arg == '*' || *arg == NUL || *arg == '|')
1028 {
Bram Moolenaarb6db1462021-12-24 19:24:47 +00001029 if (*cmd != NUL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001030 emsg(_(e_cannot_define_autocommands_for_all_events));
1031 else
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +01001032 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001033 event = (event_T)((int)event + 1))
1034 if (do_autocmd_event(event, pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001035 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001036 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001037 }
1038 else
1039 {
1040 while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
1041 if (do_autocmd_event(event_name2nr(arg, &arg), pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001042 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001043 break;
1044 }
1045
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001046 if (cmd_need_free)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001047 vim_free(cmd);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001048 vim_free(tofree);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001049 vim_free(envpat);
1050}
1051
1052/*
1053 * Find the group ID in a ":autocmd" or ":doautocmd" argument.
1054 * The "argp" argument is advanced to the following argument.
1055 *
1056 * Returns the group ID, AUGROUP_ERROR for error (out of memory).
1057 */
1058 static int
1059au_get_grouparg(char_u **argp)
1060{
1061 char_u *group_name;
1062 char_u *p;
1063 char_u *arg = *argp;
1064 int group = AUGROUP_ALL;
1065
1066 for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p)
1067 ;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001068 if (p <= arg)
1069 return AUGROUP_ALL;
1070
1071 group_name = vim_strnsave(arg, p - arg);
1072 if (group_name == NULL) // out of memory
1073 return AUGROUP_ERROR;
1074 group = au_find_group(group_name);
1075 if (group == AUGROUP_ERROR)
1076 group = AUGROUP_ALL; // no match, use all groups
1077 else
1078 *argp = skipwhite(p); // match, skip over group name
1079 vim_free(group_name);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001080 return group;
1081}
1082
1083/*
1084 * do_autocmd() for one event.
1085 * If *pat == NUL do for all patterns.
1086 * If *cmd == NUL show entries.
1087 * If forceit == TRUE delete entries.
1088 * If group is not AUGROUP_ALL, only use this group.
1089 */
1090 static int
1091do_autocmd_event(
1092 event_T event,
1093 char_u *pat,
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001094 int once,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001095 int nested,
1096 char_u *cmd,
1097 int forceit,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001098 int group,
1099 int flags)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001100{
1101 AutoPat *ap;
1102 AutoPat **prev_ap;
1103 AutoCmd *ac;
1104 AutoCmd **prev_ac;
1105 int brace_level;
1106 char_u *endpat;
1107 int findgroup;
1108 int allgroups;
1109 int patlen;
1110 int is_buflocal;
1111 int buflocal_nr;
Bram Moolenaarc667da52019-11-30 20:52:27 +01001112 char_u buflocal_pat[25]; // for "<buffer=X>"
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001113
1114 if (group == AUGROUP_ALL)
1115 findgroup = current_augroup;
1116 else
1117 findgroup = group;
1118 allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
1119
1120 /*
1121 * Show or delete all patterns for an event.
1122 */
1123 if (*pat == NUL)
1124 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001125 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001126 {
1127 if (forceit) // delete the AutoPat, if it's in the current group
1128 {
1129 if (ap->group == findgroup)
1130 au_remove_pat(ap);
1131 }
1132 else if (group == AUGROUP_ALL || ap->group == group)
1133 show_autocmd(ap, event);
1134 }
1135 }
1136
1137 /*
1138 * Loop through all the specified patterns.
1139 */
1140 for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
1141 {
1142 /*
1143 * Find end of the pattern.
1144 * Watch out for a comma in braces, like "*.\{obj,o\}".
1145 */
1146 brace_level = 0;
1147 for (endpat = pat; *endpat && (*endpat != ',' || brace_level
1148 || (endpat > pat && endpat[-1] == '\\')); ++endpat)
1149 {
1150 if (*endpat == '{')
1151 brace_level++;
1152 else if (*endpat == '}')
1153 brace_level--;
1154 }
1155 if (pat == endpat) // ignore single comma
1156 continue;
1157 patlen = (int)(endpat - pat);
1158
1159 /*
1160 * detect special <buflocal[=X]> buffer-local patterns
1161 */
1162 is_buflocal = FALSE;
1163 buflocal_nr = 0;
1164
1165 if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
1166 && pat[patlen - 1] == '>')
1167 {
1168 // "<buffer...>": Error will be printed only for addition.
1169 // printing and removing will proceed silently.
1170 is_buflocal = TRUE;
1171 if (patlen == 8)
1172 // "<buffer>"
1173 buflocal_nr = curbuf->b_fnum;
1174 else if (patlen > 9 && pat[7] == '=')
1175 {
1176 if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0)
1177 // "<buffer=abuf>"
1178 buflocal_nr = autocmd_bufnr;
1179 else if (skipdigits(pat + 8) == pat + patlen - 1)
1180 // "<buffer=123>"
1181 buflocal_nr = atoi((char *)pat + 8);
1182 }
1183 }
1184
1185 if (is_buflocal)
1186 {
1187 // normalize pat into standard "<buffer>#N" form
1188 sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
1189 pat = buflocal_pat; // can modify pat and patlen
1190 patlen = (int)STRLEN(buflocal_pat); // but not endpat
1191 }
1192
1193 /*
1194 * Find AutoPat entries with this pattern. When adding a command it
1195 * always goes at or after the last one, so start at the end.
1196 */
1197 if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL)
1198 prev_ap = &last_autopat[(int)event];
1199 else
1200 prev_ap = &first_autopat[(int)event];
1201 while ((ap = *prev_ap) != NULL)
1202 {
1203 if (ap->pat != NULL)
1204 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001205 /*
1206 * Accept a pattern when:
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001207 * - a group was specified and it's that group, or a group was
1208 * not specified and it's the current group, or a group was
1209 * not specified and we are listing
1210 * - the length of the pattern matches
1211 * - the pattern matches.
1212 * For <buffer[=X]>, this condition works because we normalize
1213 * all buffer-local patterns.
1214 */
1215 if ((allgroups || ap->group == findgroup)
1216 && ap->patlen == patlen
1217 && STRNCMP(pat, ap->pat, patlen) == 0)
1218 {
1219 /*
1220 * Remove existing autocommands.
1221 * If adding any new autocmd's for this AutoPat, don't
1222 * delete the pattern from the autopat list, append to
1223 * this list.
1224 */
1225 if (forceit)
1226 {
1227 if (*cmd != NUL && ap->next == NULL)
1228 {
1229 au_remove_cmds(ap);
1230 break;
1231 }
1232 au_remove_pat(ap);
1233 }
1234
1235 /*
1236 * Show autocmd's for this autopat, or buflocals <buffer=X>
1237 */
1238 else if (*cmd == NUL)
1239 show_autocmd(ap, event);
1240
1241 /*
1242 * Add autocmd to this autopat, if it's the last one.
1243 */
1244 else if (ap->next == NULL)
1245 break;
1246 }
1247 }
1248 prev_ap = &ap->next;
1249 }
1250
1251 /*
1252 * Add a new command.
1253 */
1254 if (*cmd != NUL)
1255 {
1256 /*
1257 * If the pattern we want to add a command to does appear at the
1258 * end of the list (or not is not in the list at all), add the
1259 * pattern at the end of the list.
1260 */
1261 if (ap == NULL)
1262 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001263 // refuse to add buffer-local ap if buffer number is invalid
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001264 if (is_buflocal && (buflocal_nr == 0
1265 || buflist_findnr(buflocal_nr) == NULL))
1266 {
Bram Moolenaarf1474d82021-12-31 19:59:55 +00001267 semsg(_(e_buffer_nr_invalid_buffer_number), buflocal_nr);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001268 return FAIL;
1269 }
1270
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001271 ap = ALLOC_ONE(AutoPat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001272 if (ap == NULL)
1273 return FAIL;
1274 ap->pat = vim_strnsave(pat, patlen);
1275 ap->patlen = patlen;
1276 if (ap->pat == NULL)
1277 {
1278 vim_free(ap);
1279 return FAIL;
1280 }
1281
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001282#ifdef FEAT_EVAL
1283 // need to initialize last_mode for the first ModeChanged
1284 // autocmd
1285 if (event == EVENT_MODECHANGED && !has_modechanged())
LemonBoy2bf52dd2022-04-09 18:17:34 +01001286 get_mode(last_mode);
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001287#endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001288 // Initialize the fields checked by the WinScrolled and
1289 // WinResized trigger to prevent them from firing right after
1290 // the first autocmd is defined.
1291 if ((event == EVENT_WINSCROLLED || event == EVENT_WINRESIZED)
1292 && !(has_winscrolled() || has_winresized()))
LemonBoy09371822022-04-08 15:18:45 +01001293 {
Bram Moolenaar29967732022-11-20 12:11:45 +00001294 tabpage_T *save_curtab = curtab;
1295 tabpage_T *tp;
1296 FOR_ALL_TABPAGES(tp)
1297 {
1298 unuse_tabpage(curtab);
1299 use_tabpage(tp);
1300 snapshot_windows_scroll_size();
1301 }
1302 unuse_tabpage(curtab);
1303 use_tabpage(save_curtab);
LemonBoy09371822022-04-08 15:18:45 +01001304 }
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001305
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001306 if (is_buflocal)
1307 {
1308 ap->buflocal_nr = buflocal_nr;
1309 ap->reg_prog = NULL;
1310 }
1311 else
1312 {
1313 char_u *reg_pat;
1314
1315 ap->buflocal_nr = 0;
1316 reg_pat = file_pat_to_reg_pat(pat, endpat,
1317 &ap->allow_dirs, TRUE);
1318 if (reg_pat != NULL)
1319 ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
1320 vim_free(reg_pat);
1321 if (reg_pat == NULL || ap->reg_prog == NULL)
1322 {
1323 vim_free(ap->pat);
1324 vim_free(ap);
1325 return FAIL;
1326 }
1327 }
1328 ap->cmds = NULL;
1329 *prev_ap = ap;
1330 last_autopat[(int)event] = ap;
1331 ap->next = NULL;
1332 if (group == AUGROUP_ALL)
1333 ap->group = current_augroup;
1334 else
1335 ap->group = group;
1336 }
1337
1338 /*
1339 * Add the autocmd at the end of the AutoCmd list.
1340 */
1341 prev_ac = &(ap->cmds);
1342 while ((ac = *prev_ac) != NULL)
1343 prev_ac = &ac->next;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001344 ac = ALLOC_ONE(AutoCmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001345 if (ac == NULL)
1346 return FAIL;
1347 ac->cmd = vim_strsave(cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001348 ac->script_ctx = current_sctx;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001349 if (flags & UC_VIM9)
1350 ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001351#ifdef FEAT_EVAL
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001352 ac->script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001353#endif
1354 if (ac->cmd == NULL)
1355 {
1356 vim_free(ac);
1357 return FAIL;
1358 }
1359 ac->next = NULL;
1360 *prev_ac = ac;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001361 ac->once = once;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001362 ac->nested = nested;
1363 }
1364 }
1365
1366 au_cleanup(); // may really delete removed patterns/commands now
1367 return OK;
1368}
1369
1370/*
1371 * Implementation of ":doautocmd [group] event [fname]".
1372 * Return OK for success, FAIL for failure;
1373 */
1374 int
1375do_doautocmd(
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001376 char_u *arg_start,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001377 int do_msg, // give message for no matching autocmds?
1378 int *did_something)
1379{
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001380 char_u *arg = arg_start;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001381 char_u *fname;
1382 int nothing_done = TRUE;
1383 int group;
1384
1385 if (did_something != NULL)
1386 *did_something = FALSE;
1387
1388 /*
1389 * Check for a legal group name. If not, use AUGROUP_ALL.
1390 */
1391 group = au_get_grouparg(&arg);
1392 if (arg == NULL) // out of memory
1393 return FAIL;
1394
1395 if (*arg == '*')
1396 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001397 emsg(_(e_cant_execute_autocommands_for_all_events));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001398 return FAIL;
1399 }
1400
1401 /*
1402 * Scan over the events.
1403 * If we find an illegal name, return here, don't do anything.
1404 */
1405 fname = find_end_event(arg, group != AUGROUP_ALL);
1406 if (fname == NULL)
1407 return FAIL;
1408
1409 fname = skipwhite(fname);
1410
1411 /*
1412 * Loop over the events.
1413 */
1414 while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
1415 if (apply_autocmds_group(event_name2nr(arg, &arg),
1416 fname, NULL, TRUE, group, curbuf, NULL))
1417 nothing_done = FALSE;
1418
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001419 if (nothing_done && do_msg
1420#ifdef FEAT_EVAL
1421 && !aborting()
1422#endif
1423 )
1424 smsg(_("No matching autocommands: %s"), arg_start);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001425 if (did_something != NULL)
1426 *did_something = !nothing_done;
1427
1428#ifdef FEAT_EVAL
1429 return aborting() ? FAIL : OK;
1430#else
1431 return OK;
1432#endif
1433}
1434
1435/*
1436 * ":doautoall": execute autocommands for each loaded buffer.
1437 */
1438 void
1439ex_doautoall(exarg_T *eap)
1440{
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001441 int retval = OK;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001442 aco_save_T aco;
1443 buf_T *buf;
1444 bufref_T bufref;
1445 char_u *arg = eap->arg;
1446 int call_do_modelines = check_nomodeline(&arg);
1447 int did_aucmd;
1448
1449 /*
1450 * This is a bit tricky: For some commands curwin->w_buffer needs to be
1451 * equal to curbuf, but for some buffers there may not be a window.
1452 * So we change the buffer for the current window for a moment. This
1453 * gives problems when the autocommands make changes to the list of
1454 * buffers or windows...
1455 */
1456 FOR_ALL_BUFFERS(buf)
1457 {
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001458 // Only do loaded buffers and skip the current buffer, it's done last.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001459 if (buf->b_ml.ml_mfp == NULL || buf == curbuf)
1460 continue;
1461
Bram Moolenaare76062c2022-11-28 18:51:43 +00001462 // Find a window for this buffer and save some values.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001463 aucmd_prepbuf(&aco, buf);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001464 if (curbuf != buf)
1465 {
1466 // Failed to find a window for this buffer. Better not execute
1467 // autocommands then.
1468 retval = FAIL;
1469 break;
1470 }
1471
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001472 set_bufref(&bufref, buf);
1473
1474 // execute the autocommands for this buffer
1475 retval = do_doautocmd(arg, FALSE, &did_aucmd);
1476
1477 if (call_do_modelines && did_aucmd)
1478 // Execute the modeline settings, but don't set window-local
1479 // options if we are using the current window for another
1480 // buffer.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001481 do_modelines(is_aucmd_win(curwin) ? OPT_NOWIN : 0);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001482
1483 // restore the current window
1484 aucmd_restbuf(&aco);
1485
1486 // stop if there is some error or buffer was deleted
1487 if (retval == FAIL || !bufref_valid(&bufref))
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001488 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001489 retval = FAIL;
1490 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001491 }
1492 }
1493
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001494 // Execute autocommands for the current buffer last.
1495 if (retval == OK)
1496 {
1497 do_doautocmd(arg, FALSE, &did_aucmd);
1498 if (call_do_modelines && did_aucmd)
1499 do_modelines(0);
1500 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001501}
1502
1503/*
1504 * Check *argp for <nomodeline>. When it is present return FALSE, otherwise
1505 * return TRUE and advance *argp to after it.
1506 * Thus return TRUE when do_modelines() should be called.
1507 */
1508 int
1509check_nomodeline(char_u **argp)
1510{
1511 if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
1512 {
1513 *argp = skipwhite(*argp + 12);
1514 return FALSE;
1515 }
1516 return TRUE;
1517}
1518
1519/*
1520 * Prepare for executing autocommands for (hidden) buffer "buf".
1521 * Search for a visible window containing the current buffer. If there isn't
Bram Moolenaare76062c2022-11-28 18:51:43 +00001522 * one then use an entry in "aucmd_win[]".
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001523 * Set "curbuf" and "curwin" to match "buf".
Bram Moolenaare76062c2022-11-28 18:51:43 +00001524 * When this fails "curbuf" is not equal "buf".
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001525 */
1526 void
1527aucmd_prepbuf(
1528 aco_save_T *aco, // structure to save values in
1529 buf_T *buf) // new curbuf
1530{
1531 win_T *win;
1532 int save_ea;
1533#ifdef FEAT_AUTOCHDIR
1534 int save_acd;
1535#endif
1536
1537 // Find a window that is for the new buffer
1538 if (buf == curbuf) // be quick when buf is curbuf
1539 win = curwin;
1540 else
1541 FOR_ALL_WINDOWS(win)
1542 if (win->w_buffer == buf)
1543 break;
1544
Bram Moolenaare76062c2022-11-28 18:51:43 +00001545 // Allocate a window when needed.
1546 win_T *auc_win = NULL;
1547 int auc_idx = AUCMD_WIN_COUNT;
1548 if (win == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001549 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001550 for (auc_idx = 0; auc_idx < AUCMD_WIN_COUNT; ++auc_idx)
1551 if (!aucmd_win[auc_idx].auc_win_used)
1552 {
Bram Moolenaar84497cd2022-11-28 20:34:52 +00001553 if (aucmd_win[auc_idx].auc_win == NULL)
1554 aucmd_win[auc_idx].auc_win = win_alloc_popup_win();
1555 auc_win = aucmd_win[auc_idx].auc_win;
Bram Moolenaare76062c2022-11-28 18:51:43 +00001556 if (auc_win != NULL)
Bram Moolenaare76062c2022-11-28 18:51:43 +00001557 aucmd_win[auc_idx].auc_win_used = TRUE;
Bram Moolenaare76062c2022-11-28 18:51:43 +00001558 break;
1559 }
1560
1561 // If this fails (out of memory or using all AUCMD_WIN_COUNT
1562 // entries) then we can't reliable execute the autocmd, return with
1563 // "curbuf" unequal "buf".
1564 if (auc_win == NULL)
1565 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001566 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001567
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001568 aco->save_curwin_id = curwin->w_id;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001569 aco->save_prevwin_id = prevwin == NULL ? 0 : prevwin->w_id;
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001570 aco->save_State = State;
zeertzjqf2678472024-01-17 21:22:59 +01001571#ifdef FEAT_JOB_CHANNEL
1572 if (bt_prompt(curbuf))
1573 aco->save_prompt_insert = curbuf->b_prompt_insert;
1574#endif
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001575
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001576 if (win != NULL)
1577 {
1578 // There is a window for "buf" in the current tab page, make it the
1579 // curwin. This is preferred, it has the least side effects (esp. if
1580 // "buf" is curbuf).
Bram Moolenaare76062c2022-11-28 18:51:43 +00001581 aco->use_aucmd_win_idx = -1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001582 curwin = win;
1583 }
1584 else
1585 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001586 // There is no window for "buf", use "auc_win". To minimize the side
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001587 // effects, insert it in the current tab page.
1588 // Anything related to a window (e.g., setting folds) may have
1589 // unexpected results.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001590 aco->use_aucmd_win_idx = auc_idx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001591
Bram Moolenaare76062c2022-11-28 18:51:43 +00001592 win_init_popup_win(auc_win, buf);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001593
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001594 aco->globaldir = globaldir;
1595 globaldir = NULL;
1596
Bram Moolenaare76062c2022-11-28 18:51:43 +00001597 // Split the current window, put the auc_win in the upper half.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001598 // We don't want the BufEnter or WinEnter autocommands.
1599 block_autocmds();
1600 make_snapshot(SNAP_AUCMD_IDX);
1601 save_ea = p_ea;
1602 p_ea = FALSE;
1603
1604#ifdef FEAT_AUTOCHDIR
1605 // Prevent chdir() call in win_enter_ext(), through do_autochdir().
1606 save_acd = p_acd;
1607 p_acd = FALSE;
1608#endif
1609
Sean Dewar704966c2024-02-20 22:00:33 +01001610 (void)win_split_ins(0, WSP_TOP | WSP_FORCE_ROOM, auc_win, 0, NULL);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001611 (void)win_comp_pos(); // recompute window positions
1612 p_ea = save_ea;
1613#ifdef FEAT_AUTOCHDIR
1614 p_acd = save_acd;
1615#endif
1616 unblock_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001617 curwin = auc_win;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001618 }
1619 curbuf = buf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001620 aco->new_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001621 set_bufref(&aco->new_curbuf, curbuf);
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001622
1623 // disable the Visual area, the position may be invalid in another buffer
1624 aco->save_VIsual_active = VIsual_active;
1625 VIsual_active = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001626}
1627
1628/*
1629 * Cleanup after executing autocommands for a (hidden) buffer.
1630 * Restore the window as it was (if possible).
1631 */
1632 void
1633aucmd_restbuf(
1634 aco_save_T *aco) // structure holding saved values
1635{
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001636 int dummy;
1637 win_T *save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001638
Bram Moolenaare76062c2022-11-28 18:51:43 +00001639 if (aco->use_aucmd_win_idx >= 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001640 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001641 win_T *awp = aucmd_win[aco->use_aucmd_win_idx].auc_win;
1642
Bram Moolenaare76062c2022-11-28 18:51:43 +00001643 // Find "awp", it can't be closed, but it may be in another tab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001644 // page. Do not trigger autocommands here.
1645 block_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001646 if (curwin != awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001647 {
1648 tabpage_T *tp;
1649 win_T *wp;
1650
1651 FOR_ALL_TAB_WINDOWS(tp, wp)
1652 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001653 if (wp == awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001654 {
1655 if (tp != curtab)
1656 goto_tabpage_tp(tp, TRUE, TRUE);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001657 win_goto(awp);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001658 goto win_found;
1659 }
1660 }
1661 }
1662win_found:
zeertzjq46bdae02023-09-24 23:16:08 +02001663 --curbuf->b_nwindows;
orbitalcde8de02023-04-02 22:05:13 +01001664#ifdef FEAT_JOB_CHANNEL
zeertzjqa40c0bc2023-05-27 14:10:08 +01001665 int save_stop_insert_mode = stop_insert_mode;
orbitalcde8de02023-04-02 22:05:13 +01001666 // May need to stop Insert mode if we were in a prompt buffer.
1667 leaving_window(curwin);
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001668 // Do not stop Insert mode when already in Insert mode before.
1669 if (aco->save_State & MODE_INSERT)
zeertzjqa40c0bc2023-05-27 14:10:08 +01001670 stop_insert_mode = save_stop_insert_mode;
orbitalcde8de02023-04-02 22:05:13 +01001671#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001672 // Remove the window and frame from the tree of frames.
Sean Dewar704966c2024-02-20 22:00:33 +01001673 (void)winframe_remove(curwin, &dummy, NULL, NULL);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001674 win_remove(curwin, NULL);
Bram Moolenaar84497cd2022-11-28 20:34:52 +00001675
1676 // The window is marked as not used, but it is not freed, it can be
1677 // used again.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001678 aucmd_win[aco->use_aucmd_win_idx].auc_win_used = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001679 last_status(FALSE); // may need to remove last status line
1680
1681 if (!valid_tabpage_win(curtab))
1682 // no valid window in current tabpage
1683 close_tabpage(curtab);
1684
1685 restore_snapshot(SNAP_AUCMD_IDX, FALSE);
1686 (void)win_comp_pos(); // recompute window positions
1687 unblock_autocmds();
1688
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001689 save_curwin = win_find_by_id(aco->save_curwin_id);
1690 if (save_curwin != NULL)
1691 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001692 else
1693 // Hmm, original window disappeared. Just use the first one.
1694 curwin = firstwin;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001695 curbuf = curwin->w_buffer;
1696#ifdef FEAT_JOB_CHANNEL
1697 // May need to restore insert mode for a prompt buffer.
1698 entering_window(curwin);
zeertzjqf2678472024-01-17 21:22:59 +01001699 if (bt_prompt(curbuf))
1700 curbuf->b_prompt_insert = aco->save_prompt_insert;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001701#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001702 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001703#ifdef FEAT_EVAL
Bram Moolenaare76062c2022-11-28 18:51:43 +00001704 vars_clear(&awp->w_vars->dv_hashtab); // free all w: variables
1705 hash_init(&awp->w_vars->dv_hashtab); // re-use the hashtab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001706#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001707 vim_free(globaldir);
1708 globaldir = aco->globaldir;
1709
1710 // the buffer contents may have changed
zeertzjq49f05242023-02-04 10:58:34 +00001711 VIsual_active = aco->save_VIsual_active;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001712 check_cursor();
1713 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1714 {
1715 curwin->w_topline = curbuf->b_ml.ml_line_count;
1716#ifdef FEAT_DIFF
1717 curwin->w_topfill = 0;
1718#endif
1719 }
1720#if defined(FEAT_GUI)
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001721 if (gui.in_use)
1722 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001723 // Hide the scrollbars from the "awp" and update.
1724 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_LEFT], FALSE);
1725 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_RIGHT], FALSE);
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001726 gui_may_update_scrollbars();
1727 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001728#endif
1729 }
1730 else
1731 {
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001732 // Restore curwin. Use the window ID, a window may have been closed
1733 // and the memory re-used for another one.
1734 save_curwin = win_find_by_id(aco->save_curwin_id);
1735 if (save_curwin != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001736 {
1737 // Restore the buffer which was previously edited by curwin, if
1738 // it was changed, we are still the same window and the buffer is
1739 // valid.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001740 if (curwin->w_id == aco->new_curwin_id
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001741 && curbuf != aco->new_curbuf.br_buf
1742 && bufref_valid(&aco->new_curbuf)
1743 && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
1744 {
1745# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
1746 if (curwin->w_s == &curbuf->b_s)
1747 curwin->w_s = &aco->new_curbuf.br_buf->b_s;
1748# endif
1749 --curbuf->b_nwindows;
1750 curbuf = aco->new_curbuf.br_buf;
1751 curwin->w_buffer = curbuf;
1752 ++curbuf->b_nwindows;
1753 }
1754
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001755 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001756 curbuf = curwin->w_buffer;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001757 prevwin = win_find_by_id(aco->save_prevwin_id);
zeertzjq49f05242023-02-04 10:58:34 +00001758
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001759 // In case the autocommand moves the cursor to a position that
1760 // does not exist in curbuf.
zeertzjq49f05242023-02-04 10:58:34 +00001761 VIsual_active = aco->save_VIsual_active;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001762 check_cursor();
1763 }
1764 }
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001765
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001766 VIsual_active = aco->save_VIsual_active;
zeertzjq49f05242023-02-04 10:58:34 +00001767 check_cursor(); // just in case lines got deleted
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001768 if (VIsual_active)
1769 check_pos(curbuf, &VIsual);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001770}
1771
1772static int autocmd_nested = FALSE;
1773
1774/*
1775 * Execute autocommands for "event" and file name "fname".
1776 * Return TRUE if some commands were executed.
1777 */
1778 int
1779apply_autocmds(
1780 event_T event,
1781 char_u *fname, // NULL or empty means use actual file name
1782 char_u *fname_io, // fname to use for <afile> on cmdline
1783 int force, // when TRUE, ignore autocmd_busy
1784 buf_T *buf) // buffer for <abuf>
1785{
1786 return apply_autocmds_group(event, fname, fname_io, force,
1787 AUGROUP_ALL, buf, NULL);
1788}
1789
1790/*
1791 * Like apply_autocmds(), but with extra "eap" argument. This takes care of
1792 * setting v:filearg.
1793 */
1794 int
1795apply_autocmds_exarg(
1796 event_T event,
1797 char_u *fname,
1798 char_u *fname_io,
1799 int force,
1800 buf_T *buf,
1801 exarg_T *eap)
1802{
1803 return apply_autocmds_group(event, fname, fname_io, force,
1804 AUGROUP_ALL, buf, eap);
1805}
1806
1807/*
1808 * Like apply_autocmds(), but handles the caller's retval. If the script
1809 * processing is being aborted or if retval is FAIL when inside a try
1810 * conditional, no autocommands are executed. If otherwise the autocommands
1811 * cause the script to be aborted, retval is set to FAIL.
1812 */
1813 int
1814apply_autocmds_retval(
1815 event_T event,
1816 char_u *fname, // NULL or empty means use actual file name
1817 char_u *fname_io, // fname to use for <afile> on cmdline
1818 int force, // when TRUE, ignore autocmd_busy
1819 buf_T *buf, // buffer for <abuf>
1820 int *retval) // pointer to caller's retval
1821{
1822 int did_cmd;
1823
1824#ifdef FEAT_EVAL
1825 if (should_abort(*retval))
1826 return FALSE;
1827#endif
1828
1829 did_cmd = apply_autocmds_group(event, fname, fname_io, force,
1830 AUGROUP_ALL, buf, NULL);
1831 if (did_cmd
1832#ifdef FEAT_EVAL
1833 && aborting()
1834#endif
1835 )
1836 *retval = FAIL;
1837 return did_cmd;
1838}
1839
1840/*
1841 * Return TRUE when there is a CursorHold autocommand defined.
1842 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001843 static int
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001844has_cursorhold(void)
1845{
Bram Moolenaar24959102022-05-07 20:01:16 +01001846 return (first_autopat[(int)(get_real_state() == MODE_NORMAL_BUSY
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001847 ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
1848}
1849
1850/*
1851 * Return TRUE if the CursorHold event can be triggered.
1852 */
1853 int
1854trigger_cursorhold(void)
1855{
1856 int state;
1857
1858 if (!did_cursorhold
1859 && has_cursorhold()
1860 && reg_recording == 0
1861 && typebuf.tb_len == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001862 && !ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001863 {
1864 state = get_real_state();
Bram Moolenaar24959102022-05-07 20:01:16 +01001865 if (state == MODE_NORMAL_BUSY || (state & MODE_INSERT) != 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001866 return TRUE;
1867 }
1868 return FALSE;
1869}
1870
1871/*
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001872 * Return TRUE when there is a WinResized autocommand defined.
1873 */
1874 int
1875has_winresized(void)
1876{
1877 return (first_autopat[(int)EVENT_WINRESIZED] != NULL);
1878}
1879
1880/*
LemonBoy09371822022-04-08 15:18:45 +01001881 * Return TRUE when there is a WinScrolled autocommand defined.
1882 */
1883 int
1884has_winscrolled(void)
1885{
1886 return (first_autopat[(int)EVENT_WINSCROLLED] != NULL);
1887}
1888
1889/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001890 * Return TRUE when there is a CursorMoved autocommand defined.
1891 */
1892 int
1893has_cursormoved(void)
1894{
1895 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1896}
1897
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001898/*
1899 * Return TRUE when there is a CursorMovedI autocommand defined.
1900 */
1901 int
1902has_cursormovedI(void)
1903{
1904 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1905}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001906
1907/*
1908 * Return TRUE when there is a TextChanged autocommand defined.
1909 */
1910 int
1911has_textchanged(void)
1912{
1913 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1914}
1915
1916/*
1917 * Return TRUE when there is a TextChangedI autocommand defined.
1918 */
1919 int
1920has_textchangedI(void)
1921{
1922 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
1923}
1924
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001925/*
1926 * Return TRUE when there is a TextChangedP autocommand defined.
1927 */
1928 int
1929has_textchangedP(void)
1930{
1931 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
1932}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001933
1934/*
1935 * Return TRUE when there is an InsertCharPre autocommand defined.
1936 */
1937 int
1938has_insertcharpre(void)
1939{
1940 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
1941}
1942
1943/*
1944 * Return TRUE when there is an CmdUndefined autocommand defined.
1945 */
1946 int
1947has_cmdundefined(void)
1948{
1949 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
1950}
1951
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001952#if defined(FEAT_EVAL) || defined(PROTO)
1953/*
1954 * Return TRUE when there is a TextYankPost autocommand defined.
1955 */
1956 int
1957has_textyankpost(void)
1958{
1959 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
1960}
1961#endif
1962
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001963#if defined(FEAT_EVAL) || defined(PROTO)
1964/*
1965 * Return TRUE when there is a CompleteChanged autocommand defined.
1966 */
1967 int
1968has_completechanged(void)
1969{
1970 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
1971}
1972#endif
1973
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02001974#if defined(FEAT_EVAL) || defined(PROTO)
1975/*
1976 * Return TRUE when there is a ModeChanged autocommand defined.
1977 */
1978 int
1979has_modechanged(void)
1980{
1981 return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
1982}
1983#endif
1984
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001985/*
1986 * Execute autocommands for "event" and file name "fname".
1987 * Return TRUE if some commands were executed.
1988 */
1989 static int
1990apply_autocmds_group(
1991 event_T event,
1992 char_u *fname, // NULL or empty means use actual file name
1993 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
1994 // use fname
1995 int force, // when TRUE, ignore autocmd_busy
1996 int group, // group ID, or AUGROUP_ALL
1997 buf_T *buf, // buffer for <abuf>
1998 exarg_T *eap UNUSED) // command arguments
1999{
2000 char_u *sfname = NULL; // short file name
2001 char_u *tail;
2002 int save_changed;
2003 buf_T *old_curbuf;
2004 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002005 char_u *save_autocmd_fname;
2006 int save_autocmd_fname_full;
2007 int save_autocmd_bufnr;
2008 char_u *save_autocmd_match;
2009 int save_autocmd_busy;
2010 int save_autocmd_nested;
2011 static int nesting = 0;
LemonBoyeca7c602022-04-14 15:39:43 +01002012 AutoPatCmd_T patcmd;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002013 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002014 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002015#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002016 funccal_entry_T funccal_entry;
2017 char_u *save_cmdarg;
2018 long save_cmdbang;
2019#endif
2020 static int filechangeshell_busy = FALSE;
2021#ifdef FEAT_PROFILE
2022 proftime_T wait_time;
2023#endif
2024 int did_save_redobuff = FALSE;
2025 save_redo_T save_redo;
2026 int save_KeyTyped = KeyTyped;
ichizok7e5fe382023-04-15 13:17:50 +01002027 ESTACK_CHECK_DECLARATION;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002028
2029 /*
2030 * Quickly return if there are no autocommands for this event or
2031 * autocommands are blocked.
2032 */
2033 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
2034 || autocmd_blocked > 0)
2035 goto BYPASS_AU;
2036
2037 /*
2038 * When autocommands are busy, new autocommands are only executed when
2039 * explicitly enabled with the "nested" flag.
2040 */
2041 if (autocmd_busy && !(force || autocmd_nested))
2042 goto BYPASS_AU;
2043
2044#ifdef FEAT_EVAL
2045 /*
2046 * Quickly return when immediately aborting on error, or when an interrupt
2047 * occurred or an exception was thrown but not caught.
2048 */
2049 if (aborting())
2050 goto BYPASS_AU;
2051#endif
2052
2053 /*
2054 * FileChangedShell never nests, because it can create an endless loop.
2055 */
2056 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
2057 || event == EVENT_FILECHANGEDSHELLPOST))
2058 goto BYPASS_AU;
2059
2060 /*
2061 * Ignore events in 'eventignore'.
2062 */
2063 if (event_ignored(event))
2064 goto BYPASS_AU;
2065
2066 /*
2067 * Allow nesting of autocommands, but restrict the depth, because it's
2068 * possible to create an endless loop.
2069 */
2070 if (nesting == 10)
2071 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00002072 emsg(_(e_autocommand_nesting_too_deep));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002073 goto BYPASS_AU;
2074 }
2075
2076 /*
2077 * Check if these autocommands are disabled. Used when doing ":all" or
2078 * ":ball".
2079 */
2080 if ( (autocmd_no_enter
2081 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
2082 || (autocmd_no_leave
2083 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
2084 goto BYPASS_AU;
2085
Bram Moolenaarbb393d82022-12-09 12:21:50 +00002086 if (event == EVENT_CMDLINECHANGED)
2087 ++aucmd_cmdline_changed_count;
2088
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002089 /*
2090 * Save the autocmd_* variables and info about the current buffer.
2091 */
2092 save_autocmd_fname = autocmd_fname;
2093 save_autocmd_fname_full = autocmd_fname_full;
2094 save_autocmd_bufnr = autocmd_bufnr;
2095 save_autocmd_match = autocmd_match;
2096 save_autocmd_busy = autocmd_busy;
2097 save_autocmd_nested = autocmd_nested;
2098 save_changed = curbuf->b_changed;
2099 old_curbuf = curbuf;
2100
2101 /*
2102 * Set the file name to be used for <afile>.
2103 * Make a copy to avoid that changing a buffer name or directory makes it
2104 * invalid.
2105 */
2106 if (fname_io == NULL)
2107 {
2108 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
Bram Moolenaarbb393d82022-12-09 12:21:50 +00002109 || event == EVENT_OPTIONSET
Danek Duvalld7d56032024-01-14 20:19:59 +01002110 || event == EVENT_MODECHANGED
2111 || event == EVENT_TERMRESPONSEALL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002112 autocmd_fname = NULL;
2113 else if (fname != NULL && !ends_excmd(*fname))
2114 autocmd_fname = fname;
2115 else if (buf != NULL)
2116 autocmd_fname = buf->b_ffname;
2117 else
2118 autocmd_fname = NULL;
2119 }
2120 else
2121 autocmd_fname = fname_io;
2122 if (autocmd_fname != NULL)
2123 autocmd_fname = vim_strsave(autocmd_fname);
2124 autocmd_fname_full = FALSE; // call FullName_save() later
2125
2126 /*
2127 * Set the buffer number to be used for <abuf>.
2128 */
2129 if (buf == NULL)
2130 autocmd_bufnr = 0;
2131 else
2132 autocmd_bufnr = buf->b_fnum;
2133
2134 /*
2135 * When the file name is NULL or empty, use the file name of buffer "buf".
2136 * Always use the full path of the file name to match with, in case
2137 * "allow_dirs" is set.
2138 */
2139 if (fname == NULL || *fname == NUL)
2140 {
2141 if (buf == NULL)
2142 fname = NULL;
2143 else
2144 {
2145#ifdef FEAT_SYN_HL
2146 if (event == EVENT_SYNTAX)
2147 fname = buf->b_p_syn;
2148 else
2149#endif
2150 if (event == EVENT_FILETYPE)
2151 fname = buf->b_p_ft;
2152 else
2153 {
2154 if (buf->b_sfname != NULL)
2155 sfname = vim_strsave(buf->b_sfname);
2156 fname = buf->b_ffname;
2157 }
2158 }
2159 if (fname == NULL)
2160 fname = (char_u *)"";
2161 fname = vim_strsave(fname); // make a copy, so we can change it
2162 }
2163 else
2164 {
2165 sfname = vim_strsave(fname);
2166 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002167 // ColorScheme, QuickFixCmd*, DirChanged and similar.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002168 if (event == EVENT_FILETYPE
2169 || event == EVENT_SYNTAX
2170 || event == EVENT_CMDLINECHANGED
2171 || event == EVENT_CMDLINEENTER
2172 || event == EVENT_CMDLINELEAVE
2173 || event == EVENT_CMDWINENTER
2174 || event == EVENT_CMDWINLEAVE
2175 || event == EVENT_CMDUNDEFINED
2176 || event == EVENT_FUNCUNDEFINED
2177 || event == EVENT_REMOTEREPLY
2178 || event == EVENT_SPELLFILEMISSING
2179 || event == EVENT_QUICKFIXCMDPRE
2180 || event == EVENT_COLORSCHEME
2181 || event == EVENT_COLORSCHEMEPRE
2182 || event == EVENT_OPTIONSET
2183 || event == EVENT_QUICKFIXCMDPOST
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002184 || event == EVENT_DIRCHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002185 || event == EVENT_DIRCHANGEDPRE
naohiro ono23beefe2021-11-13 12:38:49 +00002186 || event == EVENT_MODECHANGED
zeertzjqc601d982022-10-10 13:46:15 +01002187 || event == EVENT_MENUPOPUP
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002188 || event == EVENT_USER
LemonBoy09371822022-04-08 15:18:45 +01002189 || event == EVENT_WINCLOSED
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00002190 || event == EVENT_WINRESIZED
Danek Duvalld7d56032024-01-14 20:19:59 +01002191 || event == EVENT_WINSCROLLED
2192 || event == EVENT_TERMRESPONSEALL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002193 {
2194 fname = vim_strsave(fname);
2195 autocmd_fname_full = TRUE; // don't expand it later
2196 }
2197 else
2198 fname = FullName_save(fname, FALSE);
2199 }
2200 if (fname == NULL) // out of memory
2201 {
2202 vim_free(sfname);
2203 retval = FALSE;
2204 goto BYPASS_AU;
2205 }
2206
2207#ifdef BACKSLASH_IN_FILENAME
2208 /*
2209 * Replace all backslashes with forward slashes. This makes the
2210 * autocommand patterns portable between Unix and MS-DOS.
2211 */
2212 if (sfname != NULL)
2213 forward_slash(sfname);
2214 forward_slash(fname);
2215#endif
2216
2217#ifdef VMS
2218 // remove version for correct match
2219 if (sfname != NULL)
2220 vms_remove_version(sfname);
2221 vms_remove_version(fname);
2222#endif
2223
2224 /*
2225 * Set the name to be used for <amatch>.
2226 */
2227 autocmd_match = fname;
2228
2229
2230 // Don't redraw while doing autocommands.
2231 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002232
2233 // name and lnum are filled in later
2234 estack_push(ETYPE_AUCMD, NULL, 0);
ichizok7e5fe382023-04-15 13:17:50 +01002235 ESTACK_CHECK_SETUP;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002236
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002237 save_current_sctx = current_sctx;
2238
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002239#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002240# ifdef FEAT_PROFILE
2241 if (do_profiling == PROF_YES)
2242 prof_child_enter(&wait_time); // doesn't count for the caller itself
2243# endif
2244
2245 // Don't use local function variables, if called from a function.
2246 save_funccal(&funccal_entry);
2247#endif
2248
2249 /*
2250 * When starting to execute autocommands, save the search patterns.
2251 */
2252 if (!autocmd_busy)
2253 {
2254 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002255 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002256 {
2257 saveRedobuff(&save_redo);
2258 did_save_redobuff = TRUE;
2259 }
2260 did_filetype = keep_filetype;
2261 }
2262
2263 /*
2264 * Note that we are applying autocmds. Some commands need to know.
2265 */
2266 autocmd_busy = TRUE;
2267 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2268 ++nesting; // see matching decrement below
2269
2270 // Remember that FileType was triggered. Used for did_filetype().
2271 if (event == EVENT_FILETYPE)
2272 did_filetype = TRUE;
2273
2274 tail = gettail(fname);
2275
2276 // Find first autocommand that matches
LemonBoyeca7c602022-04-14 15:39:43 +01002277 CLEAR_FIELD(patcmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002278 patcmd.curpat = first_autopat[(int)event];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002279 patcmd.group = group;
2280 patcmd.fname = fname;
2281 patcmd.sfname = sfname;
2282 patcmd.tail = tail;
2283 patcmd.event = event;
2284 patcmd.arg_bufnr = autocmd_bufnr;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002285 auto_next_pat(&patcmd, FALSE);
2286
2287 // found one, start executing the autocommands
2288 if (patcmd.curpat != NULL)
2289 {
2290 // add to active_apc_list
2291 patcmd.next = active_apc_list;
2292 active_apc_list = &patcmd;
2293
2294#ifdef FEAT_EVAL
2295 // set v:cmdarg (only when there is a matching pattern)
2296 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2297 if (eap != NULL)
2298 {
2299 save_cmdarg = set_cmdarg(eap, NULL);
2300 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2301 }
2302 else
2303 save_cmdarg = NULL; // avoid gcc warning
2304#endif
2305 retval = TRUE;
2306 // mark the last pattern, to avoid an endless loop when more patterns
2307 // are added when executing autocommands
2308 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2309 ap->last = FALSE;
2310 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002311
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002312 // Make sure cursor and topline are valid. The first time the current
2313 // values are saved, restored by reset_lnums(). When nested only the
2314 // values are corrected when needed.
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002315 if (nesting == 1)
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002316 check_lnums(TRUE);
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002317 else
2318 check_lnums_nested(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002319
Christian Brabandt590aae32023-06-25 22:34:22 +01002320 int save_did_emsg = did_emsg;
2321 int save_ex_pressedreturn = get_pressedreturn();
ichizokc3f91c02021-12-17 09:44:33 +00002322
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002323 do_cmdline(NULL, getnextac, (void *)&patcmd,
2324 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002325
ichizokc3f91c02021-12-17 09:44:33 +00002326 did_emsg += save_did_emsg;
Christian Brabandt590aae32023-06-25 22:34:22 +01002327 set_pressedreturn(save_ex_pressedreturn);
ichizokc3f91c02021-12-17 09:44:33 +00002328
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002329 if (nesting == 1)
2330 // restore cursor and topline, unless they were changed
2331 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002332
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002333#ifdef FEAT_EVAL
2334 if (eap != NULL)
2335 {
2336 (void)set_cmdarg(NULL, save_cmdarg);
2337 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2338 }
2339#endif
2340 // delete from active_apc_list
2341 if (active_apc_list == &patcmd) // just in case
2342 active_apc_list = patcmd.next;
2343 }
2344
Bram Moolenaar79cdf022023-05-20 14:07:00 +01002345 if (RedrawingDisabled > 0)
2346 --RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002347 autocmd_busy = save_autocmd_busy;
2348 filechangeshell_busy = FALSE;
2349 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002350 vim_free(SOURCING_NAME);
ichizok7e5fe382023-04-15 13:17:50 +01002351 ESTACK_CHECK_NOW;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002352 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002353 vim_free(autocmd_fname);
2354 autocmd_fname = save_autocmd_fname;
2355 autocmd_fname_full = save_autocmd_fname_full;
2356 autocmd_bufnr = save_autocmd_bufnr;
2357 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002358 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002359#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002360 restore_funccal();
2361# ifdef FEAT_PROFILE
2362 if (do_profiling == PROF_YES)
2363 prof_child_exit(&wait_time);
2364# endif
2365#endif
2366 KeyTyped = save_KeyTyped;
2367 vim_free(fname);
2368 vim_free(sfname);
2369 --nesting; // see matching increment above
2370
2371 /*
2372 * When stopping to execute autocommands, restore the search patterns and
2373 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2374 * free any windows in the au_pending_free_win list.
2375 */
2376 if (!autocmd_busy)
2377 {
2378 restore_search_patterns();
2379 if (did_save_redobuff)
2380 restoreRedobuff(&save_redo);
2381 did_filetype = FALSE;
2382 while (au_pending_free_buf != NULL)
2383 {
2384 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002385
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002386 vim_free(au_pending_free_buf);
2387 au_pending_free_buf = b;
2388 }
2389 while (au_pending_free_win != NULL)
2390 {
2391 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002392
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002393 vim_free(au_pending_free_win);
2394 au_pending_free_win = w;
2395 }
2396 }
2397
2398 /*
2399 * Some events don't set or reset the Changed flag.
2400 * Check if still in the same buffer!
2401 */
2402 if (curbuf == old_curbuf
2403 && (event == EVENT_BUFREADPOST
2404 || event == EVENT_BUFWRITEPOST
2405 || event == EVENT_FILEAPPENDPOST
2406 || event == EVENT_VIMLEAVE
2407 || event == EVENT_VIMLEAVEPRE))
2408 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002409 if (curbuf->b_changed != save_changed)
2410 need_maketitle = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002411 curbuf->b_changed = save_changed;
2412 }
2413
2414 au_cleanup(); // may really delete removed patterns/commands now
2415
2416BYPASS_AU:
2417 // When wiping out a buffer make sure all its buffer-local autocommands
2418 // are deleted.
2419 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2420 aubuflocal_remove(buf);
2421
2422 if (retval == OK && event == EVENT_FILETYPE)
2423 au_did_filetype = TRUE;
2424
2425 return retval;
2426}
2427
2428# ifdef FEAT_EVAL
2429static char_u *old_termresponse = NULL;
Danek Duvalld7d56032024-01-14 20:19:59 +01002430static char_u *old_termu7resp = NULL;
2431static char_u *old_termblinkresp = NULL;
2432static char_u *old_termrbgresp = NULL;
2433static char_u *old_termrfgresp = NULL;
2434static char_u *old_termstyleresp = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002435# endif
2436
2437/*
2438 * Block triggering autocommands until unblock_autocmd() is called.
2439 * Can be used recursively, so long as it's symmetric.
2440 */
2441 void
2442block_autocmds(void)
2443{
2444# ifdef FEAT_EVAL
2445 // Remember the value of v:termresponse.
2446 if (autocmd_blocked == 0)
Danek Duvalld7d56032024-01-14 20:19:59 +01002447 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002448 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
Danek Duvalld7d56032024-01-14 20:19:59 +01002449 old_termu7resp = get_vim_var_str(VV_TERMU7RESP);
2450 old_termblinkresp = get_vim_var_str(VV_TERMBLINKRESP);
2451 old_termrbgresp = get_vim_var_str(VV_TERMRBGRESP);
2452 old_termrfgresp = get_vim_var_str(VV_TERMRFGRESP);
2453 old_termstyleresp = get_vim_var_str(VV_TERMSTYLERESP);
2454 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002455# endif
2456 ++autocmd_blocked;
2457}
2458
2459 void
2460unblock_autocmds(void)
2461{
2462 --autocmd_blocked;
2463
2464# ifdef FEAT_EVAL
Danek Duvalld7d56032024-01-14 20:19:59 +01002465 // When v:termresponse, etc, were set while autocommands were blocked,
2466 // trigger the autocommands now. Esp. useful when executing a shell
2467 // command during startup (vimdiff).
2468 if (autocmd_blocked == 0)
2469 {
2470 if (get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2471 {
2472 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2473 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"version", NULL, FALSE, curbuf);
2474 }
2475 if (get_vim_var_str(VV_TERMU7RESP) != old_termu7resp)
2476 {
2477 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"ambiguouswidth", NULL, FALSE, curbuf);
2478 }
2479 if (get_vim_var_str(VV_TERMBLINKRESP) != old_termblinkresp)
2480 {
2481 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"cursorblink", NULL, FALSE, curbuf);
2482 }
2483 if (get_vim_var_str(VV_TERMRBGRESP) != old_termrbgresp)
2484 {
2485 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"background", NULL, FALSE, curbuf);
2486 }
2487 if (get_vim_var_str(VV_TERMRFGRESP) != old_termrfgresp)
2488 {
2489 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"foreground", NULL, FALSE, curbuf);
2490 }
2491 if (get_vim_var_str(VV_TERMSTYLERESP) != old_termstyleresp)
2492 {
2493 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"cursorshape", NULL, FALSE, curbuf);
2494 }
2495 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002496# endif
2497}
2498
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002499 int
2500is_autocmd_blocked(void)
2501{
2502 return autocmd_blocked != 0;
2503}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002504
2505/*
2506 * Find next autocommand pattern that matches.
2507 */
2508 static void
2509auto_next_pat(
LemonBoyeca7c602022-04-14 15:39:43 +01002510 AutoPatCmd_T *apc,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002511 int stop_at_last) // stop when 'last' flag is set
2512{
2513 AutoPat *ap;
2514 AutoCmd *cp;
2515 char_u *name;
2516 char *s;
LemonBoyeca7c602022-04-14 15:39:43 +01002517 estack_T *entry;
2518 char_u *namep;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002519
LemonBoyeca7c602022-04-14 15:39:43 +01002520 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
2521
2522 // Clear the exestack entry for this ETYPE_AUCMD entry.
2523 VIM_CLEAR(entry->es_name);
2524 entry->es_info.aucmd = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002525
2526 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2527 {
2528 apc->curpat = NULL;
2529
2530 // Only use a pattern when it has not been removed, has commands and
2531 // the group matches. For buffer-local autocommands only check the
2532 // buffer number.
2533 if (ap->pat != NULL && ap->cmds != NULL
2534 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2535 {
2536 // execution-condition
2537 if (ap->buflocal_nr == 0
2538 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2539 apc->sfname, apc->tail, ap->allow_dirs))
2540 : ap->buflocal_nr == apc->arg_bufnr)
2541 {
2542 name = event_nr2name(apc->event);
2543 s = _("%s Autocommands for \"%s\"");
LemonBoyeca7c602022-04-14 15:39:43 +01002544 namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1);
2545 if (namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002546 {
LemonBoyeca7c602022-04-14 15:39:43 +01002547 sprintf((char *)namep, s, (char *)name, (char *)ap->pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002548 if (p_verbose >= 8)
2549 {
2550 verbose_enter();
LemonBoyeca7c602022-04-14 15:39:43 +01002551 smsg(_("Executing %s"), namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002552 verbose_leave();
2553 }
2554 }
2555
LemonBoyeca7c602022-04-14 15:39:43 +01002556 // Update the exestack entry for this autocmd.
2557 entry->es_name = namep;
2558 entry->es_info.aucmd = apc;
2559
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002560 apc->curpat = ap;
2561 apc->nextcmd = ap->cmds;
2562 // mark last command
2563 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2564 cp->last = FALSE;
2565 cp->last = TRUE;
2566 }
2567 line_breakcheck();
2568 if (apc->curpat != NULL) // found a match
2569 break;
2570 }
2571 if (stop_at_last && ap->last)
2572 break;
2573 }
2574}
2575
Dominique Pellee764d1b2023-03-12 21:20:59 +00002576#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002577/*
LemonBoyeca7c602022-04-14 15:39:43 +01002578 * Get the script context where autocommand "acp" is defined.
2579 */
2580 sctx_T *
2581acp_script_ctx(AutoPatCmd_T *acp)
2582{
2583 return &acp->script_ctx;
2584}
Dominique Pellee764d1b2023-03-12 21:20:59 +00002585#endif
LemonBoyeca7c602022-04-14 15:39:43 +01002586
2587/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002588 * Get next autocommand command.
2589 * Called by do_cmdline() to get the next line for ":if".
2590 * Returns allocated string, or NULL for end of autocommands.
2591 */
2592 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002593getnextac(
2594 int c UNUSED,
2595 void *cookie,
2596 int indent UNUSED,
2597 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002598{
LemonBoyeca7c602022-04-14 15:39:43 +01002599 AutoPatCmd_T *acp = (AutoPatCmd_T *)cookie;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002600 char_u *retval;
2601 AutoCmd *ac;
2602
2603 // Can be called again after returning the last line.
2604 if (acp->curpat == NULL)
2605 return NULL;
2606
2607 // repeat until we find an autocommand to execute
2608 for (;;)
2609 {
2610 // skip removed commands
2611 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2612 if (acp->nextcmd->last)
2613 acp->nextcmd = NULL;
2614 else
2615 acp->nextcmd = acp->nextcmd->next;
2616
2617 if (acp->nextcmd != NULL)
2618 break;
2619
2620 // at end of commands, find next pattern that matches
2621 if (acp->curpat->last)
2622 acp->curpat = NULL;
2623 else
2624 acp->curpat = acp->curpat->next;
2625 if (acp->curpat != NULL)
2626 auto_next_pat(acp, TRUE);
2627 if (acp->curpat == NULL)
2628 return NULL;
2629 }
2630
2631 ac = acp->nextcmd;
2632
2633 if (p_verbose >= 9)
2634 {
2635 verbose_enter_scroll();
2636 smsg(_("autocommand %s"), ac->cmd);
2637 msg_puts("\n"); // don't overwrite this either
2638 verbose_leave_scroll();
2639 }
2640 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002641 // Remove one-shot ("once") autocmd in anticipation of its execution.
2642 if (ac->once)
2643 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002644 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002645 current_sctx = ac->script_ctx;
LemonBoyeca7c602022-04-14 15:39:43 +01002646 acp->script_ctx = current_sctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002647 if (ac->last)
2648 acp->nextcmd = NULL;
2649 else
2650 acp->nextcmd = ac->next;
2651 return retval;
2652}
2653
2654/*
2655 * Return TRUE if there is a matching autocommand for "fname".
2656 * To account for buffer-local autocommands, function needs to know
2657 * in which buffer the file will be opened.
2658 */
2659 int
2660has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2661{
2662 AutoPat *ap;
2663 char_u *fname;
2664 char_u *tail = gettail(sfname);
2665 int retval = FALSE;
2666
2667 fname = FullName_save(sfname, FALSE);
2668 if (fname == NULL)
2669 return FALSE;
2670
2671#ifdef BACKSLASH_IN_FILENAME
2672 /*
2673 * Replace all backslashes with forward slashes. This makes the
2674 * autocommand patterns portable between Unix and MS-DOS.
2675 */
2676 sfname = vim_strsave(sfname);
2677 if (sfname != NULL)
2678 forward_slash(sfname);
2679 forward_slash(fname);
2680#endif
2681
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002682 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002683 if (ap->pat != NULL && ap->cmds != NULL
2684 && (ap->buflocal_nr == 0
2685 ? match_file_pat(NULL, &ap->reg_prog,
2686 fname, sfname, tail, ap->allow_dirs)
2687 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2688 ))
2689 {
2690 retval = TRUE;
2691 break;
2692 }
2693
2694 vim_free(fname);
2695#ifdef BACKSLASH_IN_FILENAME
2696 vim_free(sfname);
2697#endif
2698
2699 return retval;
2700}
2701
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002702/*
2703 * Function given to ExpandGeneric() to obtain the list of autocommand group
2704 * names.
2705 */
2706 char_u *
2707get_augroup_name(expand_T *xp UNUSED, int idx)
2708{
2709 if (idx == augroups.ga_len) // add "END" add the end
2710 return (char_u *)"END";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002711 if (idx < 0 || idx >= augroups.ga_len) // end of list
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002712 return NULL;
2713 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2714 // skip deleted entries
2715 return (char_u *)"";
2716 return AUGROUP_NAME(idx); // return a name
2717}
2718
2719static int include_groups = FALSE;
2720
2721 char_u *
2722set_context_in_autocmd(
2723 expand_T *xp,
2724 char_u *arg,
2725 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2726{
2727 char_u *p;
2728 int group;
2729
2730 // check for a group name, skip it if present
2731 include_groups = FALSE;
2732 p = arg;
2733 group = au_get_grouparg(&arg);
2734 if (group == AUGROUP_ERROR)
2735 return NULL;
2736 // If there only is a group name that's what we expand.
2737 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2738 {
2739 arg = p;
2740 group = AUGROUP_ALL;
2741 }
2742
2743 // skip over event name
2744 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2745 if (*p == ',')
2746 arg = p + 1;
2747 if (*p == NUL)
2748 {
2749 if (group == AUGROUP_ALL)
2750 include_groups = TRUE;
2751 xp->xp_context = EXPAND_EVENTS; // expand event name
2752 xp->xp_pattern = arg;
2753 return NULL;
2754 }
2755
2756 // skip over pattern
2757 arg = skipwhite(p);
2758 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2759 arg++;
2760 if (*arg)
2761 return arg; // expand (next) command
2762
2763 if (doautocmd)
2764 xp->xp_context = EXPAND_FILES; // expand file names
2765 else
2766 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2767 return NULL;
2768}
2769
2770/*
2771 * Function given to ExpandGeneric() to obtain the list of event names.
2772 */
2773 char_u *
2774get_event_name(expand_T *xp UNUSED, int idx)
2775{
2776 if (idx < augroups.ga_len) // First list group names, if wanted
2777 {
2778 if (!include_groups || AUGROUP_NAME(idx) == NULL
2779 || AUGROUP_NAME(idx) == get_deleted_augroup())
2780 return (char_u *)""; // skip deleted entries
2781 return AUGROUP_NAME(idx); // return a name
2782 }
2783 return (char_u *)event_names[idx - augroups.ga_len].name;
2784}
2785
Yee Cheng Chin900894b2023-09-29 20:42:32 +02002786/*
2787 * Function given to ExpandGeneric() to obtain the list of event names. Don't
2788 * include groups.
2789 */
2790 char_u *
2791get_event_name_no_group(expand_T *xp UNUSED, int idx)
2792{
2793 return (char_u *)event_names[idx].name;
2794}
2795
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002796
2797#if defined(FEAT_EVAL) || defined(PROTO)
2798/*
2799 * Return TRUE if autocmd is supported.
2800 */
2801 int
2802autocmd_supported(char_u *name)
2803{
2804 char_u *p;
2805
2806 return (event_name2nr(name, &p) != NUM_EVENTS);
2807}
2808
2809/*
2810 * Return TRUE if an autocommand is defined for a group, event and
2811 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2812 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2813 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2814 * Used for:
2815 * exists("#Group") or
2816 * exists("#Group#Event") or
2817 * exists("#Group#Event#pat") or
2818 * exists("#Event") or
2819 * exists("#Event#pat")
2820 */
2821 int
2822au_exists(char_u *arg)
2823{
2824 char_u *arg_save;
2825 char_u *pattern = NULL;
2826 char_u *event_name;
2827 char_u *p;
2828 event_T event;
2829 AutoPat *ap;
2830 buf_T *buflocal_buf = NULL;
2831 int group;
2832 int retval = FALSE;
2833
2834 // Make a copy so that we can change the '#' chars to a NUL.
2835 arg_save = vim_strsave(arg);
2836 if (arg_save == NULL)
2837 return FALSE;
2838 p = vim_strchr(arg_save, '#');
2839 if (p != NULL)
2840 *p++ = NUL;
2841
2842 // First, look for an autocmd group name
2843 group = au_find_group(arg_save);
2844 if (group == AUGROUP_ERROR)
2845 {
2846 // Didn't match a group name, assume the first argument is an event.
2847 group = AUGROUP_ALL;
2848 event_name = arg_save;
2849 }
2850 else
2851 {
2852 if (p == NULL)
2853 {
2854 // "Group": group name is present and it's recognized
2855 retval = TRUE;
2856 goto theend;
2857 }
2858
2859 // Must be "Group#Event" or "Group#Event#pat".
2860 event_name = p;
2861 p = vim_strchr(event_name, '#');
2862 if (p != NULL)
2863 *p++ = NUL; // "Group#Event#pat"
2864 }
2865
2866 pattern = p; // "pattern" is NULL when there is no pattern
2867
2868 // find the index (enum) for the event name
2869 event = event_name2nr(event_name, &p);
2870
2871 // return FALSE if the event name is not recognized
2872 if (event == NUM_EVENTS)
2873 goto theend;
2874
2875 // Find the first autocommand for this event.
2876 // If there isn't any, return FALSE;
2877 // If there is one and no pattern given, return TRUE;
2878 ap = first_autopat[(int)event];
2879 if (ap == NULL)
2880 goto theend;
2881
2882 // if pattern is "<buffer>", special handling is needed which uses curbuf
2883 // for pattern "<buffer=N>, fnamecmp() will work fine
2884 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2885 buflocal_buf = curbuf;
2886
2887 // Check if there is an autocommand with the given pattern.
2888 for ( ; ap != NULL; ap = ap->next)
2889 // only use a pattern when it has not been removed and has commands.
2890 // For buffer-local autocommands, fnamecmp() works fine.
2891 if (ap->pat != NULL && ap->cmds != NULL
2892 && (group == AUGROUP_ALL || ap->group == group)
2893 && (pattern == NULL
2894 || (buflocal_buf == NULL
2895 ? fnamecmp(ap->pat, pattern) == 0
2896 : ap->buflocal_nr == buflocal_buf->b_fnum)))
2897 {
2898 retval = TRUE;
2899 break;
2900 }
2901
2902theend:
2903 vim_free(arg_save);
2904 return retval;
2905}
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002906
2907/*
2908 * autocmd_add() and autocmd_delete() functions
2909 */
2910 static void
2911autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
2912{
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002913 list_T *aucmd_list;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002914 listitem_T *li;
2915 dict_T *event_dict;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002916 dictitem_T *di;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002917 char_u *event_name = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002918 list_T *event_list;
2919 listitem_T *eli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002920 event_T event;
2921 char_u *group_name = NULL;
2922 int group;
2923 char_u *pat = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002924 list_T *pat_list;
2925 listitem_T *pli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002926 char_u *cmd = NULL;
2927 char_u *end;
2928 int once;
2929 int nested;
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01002930 int replace; // replace the cmd for a group/event
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002931 int retval = VVAL_TRUE;
2932 int save_augroup = current_augroup;
2933
2934 rettv->v_type = VAR_BOOL;
2935 rettv->vval.v_number = VVAL_FALSE;
2936
2937 if (check_for_list_arg(argvars, 0) == FAIL)
2938 return;
2939
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002940 aucmd_list = argvars[0].vval.v_list;
2941 if (aucmd_list == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002942 return;
2943
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002944 FOR_ALL_LIST_ITEMS(aucmd_list, li)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002945 {
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002946 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002947 VIM_CLEAR(cmd);
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002948 event_name = NULL;
2949 event_list = NULL;
2950 pat = NULL;
2951 pat_list = NULL;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002952
2953 if (li->li_tv.v_type != VAR_DICT)
2954 continue;
2955
2956 event_dict = li->li_tv.vval.v_dict;
2957 if (event_dict == NULL)
2958 continue;
2959
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002960 di = dict_find(event_dict, (char_u *)"event", -1);
2961 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002962 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002963 if (di->di_tv.v_type == VAR_STRING)
2964 {
2965 event_name = di->di_tv.vval.v_string;
2966 if (event_name == NULL)
2967 {
2968 emsg(_(e_string_required));
2969 continue;
2970 }
2971 }
2972 else if (di->di_tv.v_type == VAR_LIST)
2973 {
2974 event_list = di->di_tv.vval.v_list;
2975 if (event_list == NULL)
2976 {
2977 emsg(_(e_list_required));
2978 continue;
2979 }
2980 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002981 else
2982 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002983 emsg(_(e_string_or_list_expected));
2984 continue;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002985 }
2986 }
2987
Bram Moolenaard61efa52022-07-23 09:52:04 +01002988 group_name = dict_get_string(event_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002989 if (group_name == NULL || *group_name == NUL)
2990 // if the autocmd group name is not specified, then use the current
2991 // autocmd group
2992 group = current_augroup;
2993 else
2994 {
2995 group = au_find_group(group_name);
2996 if (group == AUGROUP_ERROR)
2997 {
2998 if (delete)
2999 {
3000 semsg(_(e_no_such_group_str), group_name);
3001 retval = VVAL_FALSE;
3002 break;
3003 }
3004 // group is not found, create it now
3005 group = au_new_group(group_name);
3006 if (group == AUGROUP_ERROR)
3007 {
3008 semsg(_(e_no_such_group_str), group_name);
3009 retval = VVAL_FALSE;
3010 break;
3011 }
3012
3013 current_augroup = group;
3014 }
3015 }
3016
3017 // if a buffer number is specified, then generate a pattern of the form
3018 // "<buffer=n>. Otherwise, use the pattern supplied by the user.
3019 if (dict_has_key(event_dict, "bufnr"))
3020 {
3021 varnumber_T bnum;
3022
Bram Moolenaard61efa52022-07-23 09:52:04 +01003023 bnum = dict_get_number_def(event_dict, "bufnr", -1);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003024 if (bnum == -1)
3025 continue;
3026
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003027 vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
3028 pat = IObuff;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003029 }
3030 else
3031 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003032 di = dict_find(event_dict, (char_u *)"pattern", -1);
3033 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003034 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003035 if (di->di_tv.v_type == VAR_STRING)
3036 {
3037 pat = di->di_tv.vval.v_string;
3038 if (pat == NULL)
3039 {
3040 emsg(_(e_string_required));
3041 continue;
3042 }
3043 }
3044 else if (di->di_tv.v_type == VAR_LIST)
3045 {
3046 pat_list = di->di_tv.vval.v_list;
3047 if (pat_list == NULL)
3048 {
3049 emsg(_(e_list_required));
3050 continue;
3051 }
3052 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003053 else
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003054 {
3055 emsg(_(e_string_or_list_expected));
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003056 continue;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003057 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003058 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003059 else if (delete)
3060 pat = (char_u *)"";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003061 }
3062
Bram Moolenaard61efa52022-07-23 09:52:04 +01003063 once = dict_get_bool(event_dict, "once", FALSE);
3064 nested = dict_get_bool(event_dict, "nested", FALSE);
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01003065 // if 'replace' is true, then remove all the commands associated with
3066 // this autocmd event/group and add the new command.
Bram Moolenaard61efa52022-07-23 09:52:04 +01003067 replace = dict_get_bool(event_dict, "replace", FALSE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003068
Bram Moolenaard61efa52022-07-23 09:52:04 +01003069 cmd = dict_get_string(event_dict, "cmd", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003070 if (cmd == NULL)
3071 {
3072 if (delete)
3073 cmd = vim_strsave((char_u *)"");
3074 else
3075 continue;
3076 }
3077
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003078 if (delete && (event_name == NULL
3079 || (event_name[0] == '*' && event_name[1] == NUL)))
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003080 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003081 // if the event name is not specified or '*', delete all the events
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003082 for (event = (event_T)0; (int)event < NUM_EVENTS;
3083 event = (event_T)((int)event + 1))
3084 {
3085 if (do_autocmd_event(event, pat, once, nested, cmd, delete,
3086 group, 0) == FAIL)
3087 {
3088 retval = VVAL_FALSE;
3089 break;
3090 }
3091 }
3092 }
3093 else
3094 {
Bram Moolenaar968443e2022-05-27 21:16:34 +01003095 char_u *p = NULL;
3096
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003097 eli = NULL;
3098 end = NULL;
3099 while (TRUE)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003100 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003101 if (event_list != NULL)
3102 {
3103 if (eli == NULL)
3104 eli = event_list->lv_first;
3105 else
3106 eli = eli->li_next;
3107 if (eli == NULL)
3108 break;
3109 if (eli->li_tv.v_type != VAR_STRING
Bram Moolenaar882476a2022-06-01 16:02:38 +01003110 || (p = eli->li_tv.vval.v_string) == NULL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003111 {
3112 emsg(_(e_string_required));
Bram Moolenaar882476a2022-06-01 16:02:38 +01003113 break;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003114 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003115 }
3116 else
3117 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003118 if (p == NULL)
3119 p = event_name;
3120 if (p == NULL || *p == NUL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003121 break;
3122 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003123
3124 event = event_name2nr(p, &end);
3125 if (event == NUM_EVENTS || *end != NUL)
3126 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003127 // this also catches something following a valid event name
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003128 semsg(_(e_no_such_event_str), p);
3129 retval = VVAL_FALSE;
3130 break;
3131 }
3132 if (pat != NULL)
3133 {
3134 if (do_autocmd_event(event, pat, once, nested, cmd,
3135 delete | replace, group, 0) == FAIL)
3136 {
3137 retval = VVAL_FALSE;
3138 break;
3139 }
3140 }
3141 else if (pat_list != NULL)
3142 {
3143 FOR_ALL_LIST_ITEMS(pat_list, pli)
3144 {
3145 if (pli->li_tv.v_type != VAR_STRING
3146 || pli->li_tv.vval.v_string == NULL)
3147 {
3148 emsg(_(e_string_required));
3149 continue;
3150 }
3151 if (do_autocmd_event(event,
3152 pli->li_tv.vval.v_string, once, nested,
3153 cmd, delete | replace, group, 0) ==
3154 FAIL)
3155 {
3156 retval = VVAL_FALSE;
3157 break;
3158 }
3159 }
3160 if (retval == VVAL_FALSE)
3161 break;
3162 }
3163 if (event_name != NULL)
3164 p = end;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003165 }
3166 }
3167
3168 // if only the autocmd group name is specified for delete and the
3169 // autocmd event, pattern and cmd are not specified, then delete the
3170 // autocmd group.
3171 if (delete && group_name != NULL &&
3172 (event_name == NULL || event_name[0] == NUL)
3173 && (pat == NULL || pat[0] == NUL)
3174 && (cmd == NULL || cmd[0] == NUL))
3175 au_del_group(group_name);
3176 }
3177
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003178 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003179 VIM_CLEAR(cmd);
3180
3181 current_augroup = save_augroup;
3182 rettv->vval.v_number = retval;
3183}
3184
3185/*
3186 * autocmd_add() function
3187 */
3188 void
3189f_autocmd_add(typval_T *argvars, typval_T *rettv)
3190{
3191 autocmd_add_or_delete(argvars, rettv, FALSE);
3192}
3193
3194/*
3195 * autocmd_delete() function
3196 */
3197 void
3198f_autocmd_delete(typval_T *argvars, typval_T *rettv)
3199{
3200 autocmd_add_or_delete(argvars, rettv, TRUE);
3201}
3202
3203/*
3204 * autocmd_get() function
3205 * Returns a List of autocmds.
3206 */
3207 void
3208f_autocmd_get(typval_T *argvars, typval_T *rettv)
3209{
3210 event_T event_arg = NUM_EVENTS;
3211 event_T event;
3212 AutoPat *ap;
3213 AutoCmd *ac;
3214 list_T *event_list;
3215 dict_T *event_dict;
3216 char_u *event_name = NULL;
3217 char_u *pat = NULL;
3218 char_u *name = NULL;
3219 int group = AUGROUP_ALL;
3220
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01003221 if (rettv_list_alloc(rettv) == FAIL)
3222 return;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003223 if (check_for_opt_dict_arg(argvars, 0) == FAIL)
3224 return;
3225
3226 if (argvars[0].v_type == VAR_DICT)
3227 {
3228 // return only the autocmds in the specified group
3229 if (dict_has_key(argvars[0].vval.v_dict, "group"))
3230 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003231 name = dict_get_string(argvars[0].vval.v_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003232 if (name == NULL)
3233 return;
3234
3235 if (*name == NUL)
3236 group = AUGROUP_DEFAULT;
3237 else
3238 {
3239 group = au_find_group(name);
3240 if (group == AUGROUP_ERROR)
3241 {
3242 semsg(_(e_no_such_group_str), name);
3243 vim_free(name);
3244 return;
3245 }
3246 }
3247 vim_free(name);
3248 }
3249
3250 // return only the autocmds for the specified event
3251 if (dict_has_key(argvars[0].vval.v_dict, "event"))
3252 {
3253 int i;
3254
Bram Moolenaard61efa52022-07-23 09:52:04 +01003255 name = dict_get_string(argvars[0].vval.v_dict, "event", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003256 if (name == NULL)
3257 return;
3258
3259 if (name[0] == '*' && name[1] == NUL)
3260 event_arg = NUM_EVENTS;
3261 else
3262 {
3263 for (i = 0; event_names[i].name != NULL; i++)
3264 if (STRICMP(event_names[i].name, name) == 0)
3265 break;
3266 if (event_names[i].name == NULL)
3267 {
3268 semsg(_(e_no_such_event_str), name);
3269 vim_free(name);
3270 return;
3271 }
3272 event_arg = event_names[i].event;
3273 }
3274 vim_free(name);
3275 }
3276
3277 // return only the autocmds for the specified pattern
3278 if (dict_has_key(argvars[0].vval.v_dict, "pattern"))
3279 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003280 pat = dict_get_string(argvars[0].vval.v_dict, "pattern", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003281 if (pat == NULL)
3282 return;
3283 }
3284 }
3285
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003286 event_list = rettv->vval.v_list;
3287
3288 // iterate through all the autocmd events
3289 for (event = (event_T)0; (int)event < NUM_EVENTS;
3290 event = (event_T)((int)event + 1))
3291 {
3292 if (event_arg != NUM_EVENTS && event != event_arg)
3293 continue;
3294
3295 event_name = event_nr2name(event);
3296
3297 // iterate through all the patterns for this autocmd event
3298 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
3299 {
3300 char_u *group_name;
3301
3302 if (group != AUGROUP_ALL && group != ap->group)
3303 continue;
3304
3305 if (pat != NULL && STRCMP(pat, ap->pat) != 0)
3306 continue;
3307
3308 group_name = get_augroup_name(NULL, ap->group);
3309
3310 // iterate through all the commands for this pattern and add one
3311 // item for each cmd.
3312 for (ac = ap->cmds; ac != NULL; ac = ac->next)
3313 {
3314 event_dict = dict_alloc();
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003315 if (event_dict == NULL
3316 || list_append_dict(event_list, event_dict) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003317 return;
3318
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003319 if (dict_add_string(event_dict, "event", event_name) == FAIL
3320 || dict_add_string(event_dict, "group",
3321 group_name == NULL ? (char_u *)""
3322 : group_name) == FAIL
3323 || (ap->buflocal_nr != 0
3324 && (dict_add_number(event_dict, "bufnr",
3325 ap->buflocal_nr) == FAIL))
3326 || dict_add_string(event_dict, "pattern",
3327 ap->pat) == FAIL
3328 || dict_add_string(event_dict, "cmd", ac->cmd) == FAIL
3329 || dict_add_bool(event_dict, "once", ac->once) == FAIL
3330 || dict_add_bool(event_dict, "nested",
3331 ac->nested) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003332 return;
3333 }
3334 }
3335 }
3336
3337 vim_free(pat);
3338}
3339
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01003340#endif