blob: 8a7e6072a94f9c1dea2345d4e06cb6db45825bb9 [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},
Colin Kennedye5f22802024-03-26 18:20:16 +0100163 {"SessionWritePost",EVENT_SESSIONWRITEPOST},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100164 {"ShellCmdPost", EVENT_SHELLCMDPOST},
165 {"ShellFilterPost", EVENT_SHELLFILTERPOST},
Bram Moolenaarbe5ee862020-06-10 20:56:58 +0200166 {"SigUSR1", EVENT_SIGUSR1},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100167 {"SourceCmd", EVENT_SOURCECMD},
168 {"SourcePre", EVENT_SOURCEPRE},
169 {"SourcePost", EVENT_SOURCEPOST},
170 {"SpellFileMissing",EVENT_SPELLFILEMISSING},
171 {"StdinReadPost", EVENT_STDINREADPOST},
172 {"StdinReadPre", EVENT_STDINREADPRE},
173 {"SwapExists", EVENT_SWAPEXISTS},
174 {"Syntax", EVENT_SYNTAX},
175 {"TabNew", EVENT_TABNEW},
176 {"TabClosed", EVENT_TABCLOSED},
177 {"TabEnter", EVENT_TABENTER},
178 {"TabLeave", EVENT_TABLEAVE},
179 {"TermChanged", EVENT_TERMCHANGED},
180 {"TerminalOpen", EVENT_TERMINALOPEN},
Bram Moolenaar28ed4df2019-10-26 16:21:40 +0200181 {"TerminalWinOpen", EVENT_TERMINALWINOPEN},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100182 {"TermResponse", EVENT_TERMRESPONSE},
Danek Duvalld7d56032024-01-14 20:19:59 +0100183 {"TermResponseAll", EVENT_TERMRESPONSEALL},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100184 {"TextChanged", EVENT_TEXTCHANGED},
185 {"TextChangedI", EVENT_TEXTCHANGEDI},
186 {"TextChangedP", EVENT_TEXTCHANGEDP},
Shougo Matsushita4ccaedf2022-10-15 11:48:00 +0100187 {"TextChangedT", EVENT_TEXTCHANGEDT},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100188 {"User", EVENT_USER},
189 {"VimEnter", EVENT_VIMENTER},
190 {"VimLeave", EVENT_VIMLEAVE},
191 {"VimLeavePre", EVENT_VIMLEAVEPRE},
Sergey Vlasov1f47db72024-01-25 23:07:00 +0100192 {"WinNewPre", EVENT_WINNEWPRE},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100193 {"WinNew", EVENT_WINNEW},
naohiro ono23beefe2021-11-13 12:38:49 +0000194 {"WinClosed", EVENT_WINCLOSED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100195 {"WinEnter", EVENT_WINENTER},
196 {"WinLeave", EVENT_WINLEAVE},
Bram Moolenaar35fc61c2022-11-22 12:40:50 +0000197 {"WinResized", EVENT_WINRESIZED},
LemonBoy09371822022-04-08 15:18:45 +0100198 {"WinScrolled", EVENT_WINSCROLLED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100199 {"VimResized", EVENT_VIMRESIZED},
200 {"TextYankPost", EVENT_TEXTYANKPOST},
Bram Moolenaar100118c2020-12-11 19:30:34 +0100201 {"VimSuspend", EVENT_VIMSUSPEND},
202 {"VimResume", EVENT_VIMRESUME},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100203 {NULL, (event_T)0}
204};
205
206static AutoPat *first_autopat[NUM_EVENTS] =
207{
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 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
214};
215
216static AutoPat *last_autopat[NUM_EVENTS] =
217{
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 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
224};
225
kylo252ae6f1d82022-02-16 19:24:07 +0000226#define AUGROUP_DEFAULT (-1) // default autocmd group
227#define AUGROUP_ERROR (-2) // erroneous autocmd group
228#define AUGROUP_ALL (-3) // all autocmd groups
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100229
230/*
231 * struct used to keep status while executing autocommands for an event.
232 */
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100233struct AutoPatCmd_S
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100234{
235 AutoPat *curpat; // next AutoPat to examine
236 AutoCmd *nextcmd; // next AutoCmd to execute
237 int group; // group being used
238 char_u *fname; // fname to match with
239 char_u *sfname; // sfname to match with
240 char_u *tail; // tail of fname
241 event_T event; // current event
LemonBoyeca7c602022-04-14 15:39:43 +0100242 sctx_T script_ctx; // script context where it is defined
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100243 int arg_bufnr; // Initially equal to <abuf>, set to zero when
244 // buf is deleted.
LemonBoyeca7c602022-04-14 15:39:43 +0100245 AutoPatCmd_T *next; // chain of active apc-s for auto-invalidation
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100246};
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100247
LemonBoyeca7c602022-04-14 15:39:43 +0100248static AutoPatCmd_T *active_apc_list = NULL; // stack of active autocommands
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100249
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200250// Macro to loop over all the patterns for an autocmd event
251#define FOR_ALL_AUTOCMD_PATTERNS(event, ap) \
252 for ((ap) = first_autopat[(int)(event)]; (ap) != NULL; (ap) = (ap)->next)
253
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100254/*
255 * augroups stores a list of autocmd group names.
256 */
257static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
258#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
Bram Moolenaarc667da52019-11-30 20:52:27 +0100259// use get_deleted_augroup() to get this
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100260static char_u *deleted_augroup = NULL;
261
262/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100263 * The ID of the current group. Group 0 is the default one.
264 */
265static int current_augroup = AUGROUP_DEFAULT;
266
Bram Moolenaarc667da52019-11-30 20:52:27 +0100267static int au_need_clean = FALSE; // need to delete marked patterns
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100268
269static char_u *event_nr2name(event_T event);
270static int au_get_grouparg(char_u **argp);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200271static 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 +0100272static 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 +0100273static void auto_next_pat(AutoPatCmd_T *apc, int stop_at_last);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100274static int au_find_group(char_u *name);
275
276static event_T last_event;
277static int last_group;
Bram Moolenaarc667da52019-11-30 20:52:27 +0100278static int autocmd_blocked = 0; // block all autocmds
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100279
280 static char_u *
281get_deleted_augroup(void)
282{
283 if (deleted_augroup == NULL)
284 deleted_augroup = (char_u *)_("--Deleted--");
285 return deleted_augroup;
286}
287
288/*
289 * Show the autocommands for one AutoPat.
290 */
291 static void
292show_autocmd(AutoPat *ap, event_T event)
293{
294 AutoCmd *ac;
295
296 // Check for "got_int" (here and at various places below), which is set
297 // when "q" has been hit for the "--more--" prompt
298 if (got_int)
299 return;
300 if (ap->pat == NULL) // pattern has been removed
301 return;
302
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000303 // Make sure no info referenced by "ap" is cleared, e.g. when a timer
304 // clears an augroup. Jump to "theend" after this!
305 // "ap->pat" may be cleared anyway.
306 ++autocmd_busy;
307
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100308 msg_putchar('\n');
309 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000310 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100311 if (event != last_event || ap->group != last_group)
312 {
313 if (ap->group != AUGROUP_DEFAULT)
314 {
315 if (AUGROUP_NAME(ap->group) == NULL)
316 msg_puts_attr((char *)get_deleted_augroup(), HL_ATTR(HLF_E));
317 else
318 msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
319 msg_puts(" ");
320 }
321 msg_puts_attr((char *)event_nr2name(event), HL_ATTR(HLF_T));
322 last_event = event;
323 last_group = ap->group;
324 msg_putchar('\n');
325 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000326 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100327 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000328
329 if (ap->pat == NULL)
330 goto theend; // timer might have cleared the pattern or group
331
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100332 msg_col = 4;
333 msg_outtrans(ap->pat);
334
335 for (ac = ap->cmds; ac != NULL; ac = ac->next)
336 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100337 if (ac->cmd == NULL) // skip removed commands
338 continue;
339
340 if (msg_col >= 14)
341 msg_putchar('\n');
342 msg_col = 14;
343 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000344 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100345 msg_outtrans(ac->cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100346#ifdef FEAT_EVAL
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100347 if (p_verbose > 0)
348 last_set_msg(ac->script_ctx);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100349#endif
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100350 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000351 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100352 if (ac->next != NULL)
353 {
354 msg_putchar('\n');
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100355 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000356 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100357 }
358 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000359
360theend:
361 --autocmd_busy;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100362}
363
364/*
365 * Mark an autocommand pattern for deletion.
366 */
367 static void
368au_remove_pat(AutoPat *ap)
369{
370 VIM_CLEAR(ap->pat);
371 ap->buflocal_nr = -1;
372 au_need_clean = TRUE;
373}
374
375/*
376 * Mark all commands for a pattern for deletion.
377 */
378 static void
379au_remove_cmds(AutoPat *ap)
380{
381 AutoCmd *ac;
382
383 for (ac = ap->cmds; ac != NULL; ac = ac->next)
384 VIM_CLEAR(ac->cmd);
385 au_need_clean = TRUE;
386}
387
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200388// Delete one command from an autocmd pattern.
389static void au_del_cmd(AutoCmd *ac)
390{
391 VIM_CLEAR(ac->cmd);
392 au_need_clean = TRUE;
393}
394
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100395/*
396 * Cleanup autocommands and patterns that have been deleted.
397 * This is only done when not executing autocommands.
398 */
399 static void
400au_cleanup(void)
401{
402 AutoPat *ap, **prev_ap;
403 AutoCmd *ac, **prev_ac;
404 event_T event;
405
406 if (autocmd_busy || !au_need_clean)
407 return;
408
409 // loop over all events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100410 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100411 event = (event_T)((int)event + 1))
412 {
413 // loop over all autocommand patterns
414 prev_ap = &(first_autopat[(int)event]);
415 for (ap = *prev_ap; ap != NULL; ap = *prev_ap)
416 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200417 int has_cmd = FALSE;
418
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200419 // loop over all commands for this pattern
420 prev_ac = &(ap->cmds);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100421 for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
422 {
423 // remove the command if the pattern is to be deleted or when
424 // the command has been marked for deletion
425 if (ap->pat == NULL || ac->cmd == NULL)
426 {
427 *prev_ac = ac->next;
428 vim_free(ac->cmd);
429 vim_free(ac);
430 }
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200431 else
432 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200433 has_cmd = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100434 prev_ac = &(ac->next);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200435 }
436 }
437
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200438 if (ap->pat != NULL && !has_cmd)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200439 // Pattern was not marked for deletion, but all of its
440 // commands were. So mark the pattern for deletion.
441 au_remove_pat(ap);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100442
443 // remove the pattern if it has been marked for deletion
444 if (ap->pat == NULL)
445 {
446 if (ap->next == NULL)
447 {
448 if (prev_ap == &(first_autopat[(int)event]))
449 last_autopat[(int)event] = NULL;
450 else
451 // this depends on the "next" field being the first in
452 // the struct
453 last_autopat[(int)event] = (AutoPat *)prev_ap;
454 }
455 *prev_ap = ap->next;
456 vim_regfree(ap->reg_prog);
457 vim_free(ap);
458 }
459 else
460 prev_ap = &(ap->next);
461 }
462 }
463
464 au_need_clean = FALSE;
465}
466
467/*
468 * Called when buffer is freed, to remove/invalidate related buffer-local
469 * autocmds.
470 */
471 void
472aubuflocal_remove(buf_T *buf)
473{
LemonBoyeca7c602022-04-14 15:39:43 +0100474 AutoPat *ap;
475 event_T event;
476 AutoPatCmd_T *apc;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100477
478 // invalidate currently executing autocommands
479 for (apc = active_apc_list; apc; apc = apc->next)
480 if (buf->b_fnum == apc->arg_bufnr)
481 apc->arg_bufnr = 0;
482
483 // invalidate buflocals looping through events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100484 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100485 event = (event_T)((int)event + 1))
486 // loop over all autocommand patterns
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200487 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100488 if (ap->buflocal_nr == buf->b_fnum)
489 {
490 au_remove_pat(ap);
491 if (p_verbose >= 6)
492 {
493 verbose_enter();
494 smsg(_("auto-removing autocommand: %s <buffer=%d>"),
495 event_nr2name(event), buf->b_fnum);
496 verbose_leave();
497 }
498 }
499 au_cleanup();
500}
501
502/*
503 * Add an autocmd group name.
504 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
505 */
506 static int
507au_new_group(char_u *name)
508{
509 int i;
510
511 i = au_find_group(name);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100512 if (i != AUGROUP_ERROR)
513 return i;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100514
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100515 // the group doesn't exist yet, add it. First try using a free entry.
516 for (i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100517 if (AUGROUP_NAME(i) == NULL)
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100518 break;
519 if (i == augroups.ga_len && ga_grow(&augroups, 1) == FAIL)
520 return AUGROUP_ERROR;
521
522 AUGROUP_NAME(i) = vim_strsave(name);
523 if (AUGROUP_NAME(i) == NULL)
524 return AUGROUP_ERROR;
525 if (i == augroups.ga_len)
526 ++augroups.ga_len;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100527
528 return i;
529}
530
531 static void
532au_del_group(char_u *name)
533{
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100534 int i;
535 event_T event;
536 AutoPat *ap;
537 int in_use = FALSE;
538
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100539
540 i = au_find_group(name);
541 if (i == AUGROUP_ERROR) // the group doesn't exist
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100542 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100543 semsg(_(e_no_such_group_str), name);
544 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100545 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100546 if (i == current_augroup)
547 {
548 emsg(_(e_cannot_delete_current_group));
549 return;
550 }
551
552 for (event = (event_T)0; (int)event < NUM_EVENTS;
553 event = (event_T)((int)event + 1))
554 {
555 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
556 if (ap->group == i && ap->pat != NULL)
557 {
558 give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
559 in_use = TRUE;
560 event = NUM_EVENTS;
561 break;
562 }
563 }
564 vim_free(AUGROUP_NAME(i));
565 if (in_use)
566 AUGROUP_NAME(i) = get_deleted_augroup();
567 else
568 AUGROUP_NAME(i) = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100569}
570
571/*
572 * Find the ID of an autocmd group name.
573 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
574 */
575 static int
576au_find_group(char_u *name)
577{
578 int i;
579
580 for (i = 0; i < augroups.ga_len; ++i)
581 if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
582 && STRCMP(AUGROUP_NAME(i), name) == 0)
583 return i;
584 return AUGROUP_ERROR;
585}
586
587/*
588 * Return TRUE if augroup "name" exists.
589 */
590 int
591au_has_group(char_u *name)
592{
593 return au_find_group(name) != AUGROUP_ERROR;
594}
595
596/*
597 * ":augroup {name}".
598 */
599 void
600do_augroup(char_u *arg, int del_group)
601{
602 int i;
603
604 if (del_group)
605 {
606 if (*arg == NUL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000607 emsg(_(e_argument_required));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100608 else
609 au_del_group(arg);
610 }
611 else if (STRICMP(arg, "end") == 0) // ":aug end": back to group 0
612 current_augroup = AUGROUP_DEFAULT;
613 else if (*arg) // ":aug xxx": switch to group xxx
614 {
615 i = au_new_group(arg);
616 if (i != AUGROUP_ERROR)
617 current_augroup = i;
618 }
619 else // ":aug": list the group names
620 {
621 msg_start();
622 for (i = 0; i < augroups.ga_len; ++i)
623 {
624 if (AUGROUP_NAME(i) != NULL)
625 {
626 msg_puts((char *)AUGROUP_NAME(i));
627 msg_puts(" ");
628 }
629 }
630 msg_clr_eos();
631 msg_end();
632 }
633}
634
Bram Moolenaare76062c2022-11-28 18:51:43 +0000635 void
636autocmd_init(void)
637{
638 CLEAR_FIELD(aucmd_win);
639}
640
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100641#if defined(EXITFREE) || defined(PROTO)
642 void
643free_all_autocmds(void)
644{
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100645 char_u *s;
646
647 for (current_augroup = -1; current_augroup < augroups.ga_len;
648 ++current_augroup)
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200649 do_autocmd(NULL, (char_u *)"", TRUE);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100650
Bram Moolenaare76062c2022-11-28 18:51:43 +0000651 for (int i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100652 {
653 s = ((char_u **)(augroups.ga_data))[i];
654 if (s != get_deleted_augroup())
655 vim_free(s);
656 }
657 ga_clear(&augroups);
Bram Moolenaare76062c2022-11-28 18:51:43 +0000658
Bram Moolenaar84497cd2022-11-28 20:34:52 +0000659 // aucmd_win[] is freed in win_free_all()
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100660}
661#endif
662
663/*
Bram Moolenaare76062c2022-11-28 18:51:43 +0000664 * Return TRUE if "win" is an active entry in aucmd_win[].
665 */
666 int
667is_aucmd_win(win_T *win)
668{
669 for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
670 if (aucmd_win[i].auc_win_used && aucmd_win[i].auc_win == win)
671 return TRUE;
672 return FALSE;
673}
674
675/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100676 * Return the event number for event name "start".
677 * Return NUM_EVENTS if the event name was not found.
678 * Return a pointer to the next event name in "end".
679 */
680 static event_T
681event_name2nr(char_u *start, char_u **end)
682{
683 char_u *p;
684 int i;
685 int len;
686
687 // the event name ends with end of line, '|', a blank or a comma
688 for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
689 ;
690 for (i = 0; event_names[i].name != NULL; ++i)
691 {
692 len = (int)STRLEN(event_names[i].name);
693 if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0)
694 break;
695 }
696 if (*p == ',')
697 ++p;
698 *end = p;
699 if (event_names[i].name == NULL)
700 return NUM_EVENTS;
701 return event_names[i].event;
702}
703
704/*
705 * Return the name for event "event".
706 */
707 static char_u *
708event_nr2name(event_T event)
709{
710 int i;
711
712 for (i = 0; event_names[i].name != NULL; ++i)
713 if (event_names[i].event == event)
714 return (char_u *)event_names[i].name;
715 return (char_u *)"Unknown";
716}
717
718/*
719 * Scan over the events. "*" stands for all events.
720 */
721 static char_u *
722find_end_event(
723 char_u *arg,
724 int have_group) // TRUE when group name was found
725{
726 char_u *pat;
727 char_u *p;
728
729 if (*arg == '*')
730 {
731 if (arg[1] && !VIM_ISWHITE(arg[1]))
732 {
Bram Moolenaar6d057012021-12-31 18:49:43 +0000733 semsg(_(e_illegal_character_after_star_str), arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100734 return NULL;
735 }
736 pat = arg + 1;
737 }
738 else
739 {
740 for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p)
741 {
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100742 if ((int)event_name2nr(pat, &p) >= NUM_EVENTS)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100743 {
744 if (have_group)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000745 semsg(_(e_no_such_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100746 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000747 semsg(_(e_no_such_group_or_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100748 return NULL;
749 }
750 }
751 }
752 return pat;
753}
754
755/*
756 * Return TRUE if "event" is included in 'eventignore'.
757 */
758 static int
759event_ignored(event_T event)
760{
761 char_u *p = p_ei;
762
763 while (*p != NUL)
764 {
765 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
766 return TRUE;
767 if (event_name2nr(p, &p) == event)
768 return TRUE;
769 }
770
771 return FALSE;
772}
773
774/*
775 * Return OK when the contents of p_ei is valid, FAIL otherwise.
776 */
777 int
778check_ei(void)
779{
780 char_u *p = p_ei;
781
782 while (*p)
783 {
784 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
785 {
786 p += 3;
787 if (*p == ',')
788 ++p;
789 }
790 else if (event_name2nr(p, &p) == NUM_EVENTS)
791 return FAIL;
792 }
793
794 return OK;
795}
796
797# if defined(FEAT_SYN_HL) || defined(PROTO)
798
799/*
800 * Add "what" to 'eventignore' to skip loading syntax highlighting for every
801 * buffer loaded into the window. "what" must start with a comma.
802 * Returns the old value of 'eventignore' in allocated memory.
803 */
804 char_u *
805au_event_disable(char *what)
806{
807 char_u *new_ei;
808 char_u *save_ei;
809
810 save_ei = vim_strsave(p_ei);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100811 if (save_ei == NULL)
812 return NULL;
813
814 new_ei = vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what));
815 if (new_ei == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100816 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100817 vim_free(save_ei);
818 return NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100819 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100820
821 if (*what == ',' && *p_ei == NUL)
822 STRCPY(new_ei, what + 1);
823 else
824 STRCAT(new_ei, what);
825 set_string_option_direct((char_u *)"ei", -1, new_ei,
826 OPT_FREE, SID_NONE);
827 vim_free(new_ei);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100828 return save_ei;
829}
830
831 void
832au_event_restore(char_u *old_ei)
833{
834 if (old_ei != NULL)
835 {
836 set_string_option_direct((char_u *)"ei", -1, old_ei,
837 OPT_FREE, SID_NONE);
838 vim_free(old_ei);
839 }
840}
Bram Moolenaarc667da52019-11-30 20:52:27 +0100841# endif // FEAT_SYN_HL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100842
843/*
844 * do_autocmd() -- implements the :autocmd command. Can be used in the
845 * following ways:
846 *
847 * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
848 * will be automatically executed for <event>
849 * when editing a file matching <pat>, in
850 * the current group.
851 * :autocmd <event> <pat> Show the autocommands associated with
852 * <event> and <pat>.
853 * :autocmd <event> Show the autocommands associated with
854 * <event>.
855 * :autocmd Show all autocommands.
856 * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
857 * <event> and <pat>, and add the command
858 * <cmd>, for the current group.
859 * :autocmd! <event> <pat> Remove all autocommands associated with
860 * <event> and <pat> for the current group.
861 * :autocmd! <event> Remove all autocommands associated with
862 * <event> for the current group.
863 * :autocmd! Remove ALL autocommands for the current
864 * group.
865 *
866 * Multiple events and patterns may be given separated by commas. Here are
867 * some examples:
868 * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
869 * :autocmd bufleave * set tw=79 nosmartindent ic infercase
870 *
871 * :autocmd * *.c show all autocommands for *.c files.
872 *
873 * Mostly a {group} argument can optionally appear before <event>.
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200874 * "eap" can be NULL.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100875 */
876 void
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200877do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100878{
879 char_u *arg = arg_in;
880 char_u *pat;
881 char_u *envpat = NULL;
882 char_u *cmd;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200883 int cmd_need_free = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100884 event_T event;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200885 char_u *tofree = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100886 int nested = FALSE;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200887 int once = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100888 int group;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200889 int i;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200890 int flags = 0;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100891
892 if (*arg == '|')
893 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000894 eap->nextcmd = arg + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100895 arg = (char_u *)"";
896 group = AUGROUP_ALL; // no argument, use all groups
897 }
898 else
899 {
900 /*
901 * Check for a legal group name. If not, use AUGROUP_ALL.
902 */
903 group = au_get_grouparg(&arg);
904 if (arg == NULL) // out of memory
905 return;
906 }
907
908 /*
909 * Scan over the events.
910 * If we find an illegal name, return here, don't do anything.
911 */
912 pat = find_end_event(arg, group != AUGROUP_ALL);
913 if (pat == NULL)
914 return;
915
916 pat = skipwhite(pat);
917 if (*pat == '|')
918 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000919 eap->nextcmd = pat + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100920 pat = (char_u *)"";
921 cmd = (char_u *)"";
922 }
923 else
924 {
925 /*
926 * Scan over the pattern. Put a NUL at the end.
927 */
928 cmd = pat;
929 while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
930 cmd++;
931 if (*cmd)
932 *cmd++ = NUL;
933
934 // Expand environment variables in the pattern. Set 'shellslash', we
935 // want forward slashes here.
936 if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
937 {
938#ifdef BACKSLASH_IN_FILENAME
939 int p_ssl_save = p_ssl;
940
941 p_ssl = TRUE;
942#endif
943 envpat = expand_env_save(pat);
944#ifdef BACKSLASH_IN_FILENAME
945 p_ssl = p_ssl_save;
946#endif
947 if (envpat != NULL)
948 pat = envpat;
949 }
950
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100951 cmd = skipwhite(cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200952 for (i = 0; i < 2; i++)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100953 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100954 if (*cmd == NUL)
955 continue;
956
957 // Check for "++once" flag.
958 if (STRNCMP(cmd, "++once", 6) == 0 && VIM_ISWHITE(cmd[6]))
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200959 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100960 if (once)
961 semsg(_(e_duplicate_argument_str), "++once");
962 once = TRUE;
963 cmd = skipwhite(cmd + 6);
964 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200965
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100966 // Check for "++nested" flag.
967 if ((STRNCMP(cmd, "++nested", 8) == 0 && VIM_ISWHITE(cmd[8])))
968 {
969 if (nested)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200970 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100971 semsg(_(e_duplicate_argument_str), "++nested");
972 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200973 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100974 nested = TRUE;
975 cmd = skipwhite(cmd + 8);
976 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200977
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100978 // Check for the old "nested" flag in legacy script.
979 if (STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
980 {
981 if (in_vim9script())
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200982 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100983 // If there ever is a :nested command this error should
984 // be removed and "nested" accepted as the start of the
985 // command.
986 emsg(_(e_invalid_command_nested_did_you_mean_plusplus_nested));
987 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200988 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100989 if (nested)
990 {
991 semsg(_(e_duplicate_argument_str), "nested");
992 return;
993 }
994 nested = TRUE;
995 cmd = skipwhite(cmd + 6);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200996 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100997 }
998
999 /*
1000 * Find the start of the commands.
1001 * Expand <sfile> in it.
1002 */
1003 if (*cmd != NUL)
1004 {
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001005 if (eap != NULL)
1006 // Read a {} block if it follows.
1007 cmd = may_get_cmd_block(eap, cmd, &tofree, &flags);
1008
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001009 cmd = expand_sfile(cmd);
1010 if (cmd == NULL) // some error
1011 return;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001012 cmd_need_free = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001013 }
1014 }
1015
1016 /*
1017 * Print header when showing autocommands.
1018 */
1019 if (!forceit && *cmd == NUL)
1020 // Highlight title
1021 msg_puts_title(_("\n--- Autocommands ---"));
1022
1023 /*
1024 * Loop over the events.
1025 */
1026 last_event = (event_T)-1; // for listing the event name
1027 last_group = AUGROUP_ERROR; // for listing the group name
1028 if (*arg == '*' || *arg == NUL || *arg == '|')
1029 {
Bram Moolenaarb6db1462021-12-24 19:24:47 +00001030 if (*cmd != NUL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001031 emsg(_(e_cannot_define_autocommands_for_all_events));
1032 else
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +01001033 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001034 event = (event_T)((int)event + 1))
1035 if (do_autocmd_event(event, pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001036 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001037 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001038 }
1039 else
1040 {
1041 while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
1042 if (do_autocmd_event(event_name2nr(arg, &arg), pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001043 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001044 break;
1045 }
1046
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001047 if (cmd_need_free)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001048 vim_free(cmd);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001049 vim_free(tofree);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001050 vim_free(envpat);
1051}
1052
1053/*
1054 * Find the group ID in a ":autocmd" or ":doautocmd" argument.
1055 * The "argp" argument is advanced to the following argument.
1056 *
1057 * Returns the group ID, AUGROUP_ERROR for error (out of memory).
1058 */
1059 static int
1060au_get_grouparg(char_u **argp)
1061{
1062 char_u *group_name;
1063 char_u *p;
1064 char_u *arg = *argp;
1065 int group = AUGROUP_ALL;
1066
1067 for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p)
1068 ;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001069 if (p <= arg)
1070 return AUGROUP_ALL;
1071
1072 group_name = vim_strnsave(arg, p - arg);
1073 if (group_name == NULL) // out of memory
1074 return AUGROUP_ERROR;
1075 group = au_find_group(group_name);
1076 if (group == AUGROUP_ERROR)
1077 group = AUGROUP_ALL; // no match, use all groups
1078 else
1079 *argp = skipwhite(p); // match, skip over group name
1080 vim_free(group_name);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001081 return group;
1082}
1083
1084/*
1085 * do_autocmd() for one event.
1086 * If *pat == NUL do for all patterns.
1087 * If *cmd == NUL show entries.
1088 * If forceit == TRUE delete entries.
1089 * If group is not AUGROUP_ALL, only use this group.
1090 */
1091 static int
1092do_autocmd_event(
1093 event_T event,
1094 char_u *pat,
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001095 int once,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001096 int nested,
1097 char_u *cmd,
1098 int forceit,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001099 int group,
1100 int flags)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001101{
1102 AutoPat *ap;
1103 AutoPat **prev_ap;
1104 AutoCmd *ac;
1105 AutoCmd **prev_ac;
1106 int brace_level;
1107 char_u *endpat;
1108 int findgroup;
1109 int allgroups;
1110 int patlen;
1111 int is_buflocal;
1112 int buflocal_nr;
Bram Moolenaarc667da52019-11-30 20:52:27 +01001113 char_u buflocal_pat[25]; // for "<buffer=X>"
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001114
1115 if (group == AUGROUP_ALL)
1116 findgroup = current_augroup;
1117 else
1118 findgroup = group;
1119 allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
1120
1121 /*
1122 * Show or delete all patterns for an event.
1123 */
1124 if (*pat == NUL)
1125 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001126 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001127 {
1128 if (forceit) // delete the AutoPat, if it's in the current group
1129 {
1130 if (ap->group == findgroup)
1131 au_remove_pat(ap);
1132 }
1133 else if (group == AUGROUP_ALL || ap->group == group)
1134 show_autocmd(ap, event);
1135 }
1136 }
1137
1138 /*
1139 * Loop through all the specified patterns.
1140 */
1141 for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
1142 {
1143 /*
1144 * Find end of the pattern.
1145 * Watch out for a comma in braces, like "*.\{obj,o\}".
1146 */
1147 brace_level = 0;
1148 for (endpat = pat; *endpat && (*endpat != ',' || brace_level
1149 || (endpat > pat && endpat[-1] == '\\')); ++endpat)
1150 {
1151 if (*endpat == '{')
1152 brace_level++;
1153 else if (*endpat == '}')
1154 brace_level--;
1155 }
1156 if (pat == endpat) // ignore single comma
1157 continue;
1158 patlen = (int)(endpat - pat);
1159
1160 /*
1161 * detect special <buflocal[=X]> buffer-local patterns
1162 */
1163 is_buflocal = FALSE;
1164 buflocal_nr = 0;
1165
1166 if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
1167 && pat[patlen - 1] == '>')
1168 {
1169 // "<buffer...>": Error will be printed only for addition.
1170 // printing and removing will proceed silently.
1171 is_buflocal = TRUE;
1172 if (patlen == 8)
1173 // "<buffer>"
1174 buflocal_nr = curbuf->b_fnum;
1175 else if (patlen > 9 && pat[7] == '=')
1176 {
1177 if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0)
1178 // "<buffer=abuf>"
1179 buflocal_nr = autocmd_bufnr;
1180 else if (skipdigits(pat + 8) == pat + patlen - 1)
1181 // "<buffer=123>"
1182 buflocal_nr = atoi((char *)pat + 8);
1183 }
1184 }
1185
1186 if (is_buflocal)
1187 {
1188 // normalize pat into standard "<buffer>#N" form
1189 sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
1190 pat = buflocal_pat; // can modify pat and patlen
1191 patlen = (int)STRLEN(buflocal_pat); // but not endpat
1192 }
1193
1194 /*
1195 * Find AutoPat entries with this pattern. When adding a command it
1196 * always goes at or after the last one, so start at the end.
1197 */
1198 if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL)
1199 prev_ap = &last_autopat[(int)event];
1200 else
1201 prev_ap = &first_autopat[(int)event];
1202 while ((ap = *prev_ap) != NULL)
1203 {
1204 if (ap->pat != NULL)
1205 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001206 /*
1207 * Accept a pattern when:
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001208 * - a group was specified and it's that group, or a group was
1209 * not specified and it's the current group, or a group was
1210 * not specified and we are listing
1211 * - the length of the pattern matches
1212 * - the pattern matches.
1213 * For <buffer[=X]>, this condition works because we normalize
1214 * all buffer-local patterns.
1215 */
1216 if ((allgroups || ap->group == findgroup)
1217 && ap->patlen == patlen
1218 && STRNCMP(pat, ap->pat, patlen) == 0)
1219 {
1220 /*
1221 * Remove existing autocommands.
1222 * If adding any new autocmd's for this AutoPat, don't
1223 * delete the pattern from the autopat list, append to
1224 * this list.
1225 */
1226 if (forceit)
1227 {
1228 if (*cmd != NUL && ap->next == NULL)
1229 {
1230 au_remove_cmds(ap);
1231 break;
1232 }
1233 au_remove_pat(ap);
1234 }
1235
1236 /*
1237 * Show autocmd's for this autopat, or buflocals <buffer=X>
1238 */
1239 else if (*cmd == NUL)
1240 show_autocmd(ap, event);
1241
1242 /*
1243 * Add autocmd to this autopat, if it's the last one.
1244 */
1245 else if (ap->next == NULL)
1246 break;
1247 }
1248 }
1249 prev_ap = &ap->next;
1250 }
1251
1252 /*
1253 * Add a new command.
1254 */
1255 if (*cmd != NUL)
1256 {
1257 /*
1258 * If the pattern we want to add a command to does appear at the
1259 * end of the list (or not is not in the list at all), add the
1260 * pattern at the end of the list.
1261 */
1262 if (ap == NULL)
1263 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001264 // refuse to add buffer-local ap if buffer number is invalid
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001265 if (is_buflocal && (buflocal_nr == 0
1266 || buflist_findnr(buflocal_nr) == NULL))
1267 {
Bram Moolenaarf1474d82021-12-31 19:59:55 +00001268 semsg(_(e_buffer_nr_invalid_buffer_number), buflocal_nr);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001269 return FAIL;
1270 }
1271
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001272 ap = ALLOC_ONE(AutoPat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001273 if (ap == NULL)
1274 return FAIL;
1275 ap->pat = vim_strnsave(pat, patlen);
1276 ap->patlen = patlen;
1277 if (ap->pat == NULL)
1278 {
1279 vim_free(ap);
1280 return FAIL;
1281 }
1282
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001283#ifdef FEAT_EVAL
1284 // need to initialize last_mode for the first ModeChanged
1285 // autocmd
1286 if (event == EVENT_MODECHANGED && !has_modechanged())
LemonBoy2bf52dd2022-04-09 18:17:34 +01001287 get_mode(last_mode);
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001288#endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001289 // Initialize the fields checked by the WinScrolled and
1290 // WinResized trigger to prevent them from firing right after
1291 // the first autocmd is defined.
1292 if ((event == EVENT_WINSCROLLED || event == EVENT_WINRESIZED)
1293 && !(has_winscrolled() || has_winresized()))
LemonBoy09371822022-04-08 15:18:45 +01001294 {
Bram Moolenaar29967732022-11-20 12:11:45 +00001295 tabpage_T *save_curtab = curtab;
1296 tabpage_T *tp;
1297 FOR_ALL_TABPAGES(tp)
1298 {
1299 unuse_tabpage(curtab);
1300 use_tabpage(tp);
1301 snapshot_windows_scroll_size();
1302 }
1303 unuse_tabpage(curtab);
1304 use_tabpage(save_curtab);
LemonBoy09371822022-04-08 15:18:45 +01001305 }
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001306
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001307 if (is_buflocal)
1308 {
1309 ap->buflocal_nr = buflocal_nr;
1310 ap->reg_prog = NULL;
1311 }
1312 else
1313 {
1314 char_u *reg_pat;
1315
1316 ap->buflocal_nr = 0;
1317 reg_pat = file_pat_to_reg_pat(pat, endpat,
1318 &ap->allow_dirs, TRUE);
1319 if (reg_pat != NULL)
1320 ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
1321 vim_free(reg_pat);
1322 if (reg_pat == NULL || ap->reg_prog == NULL)
1323 {
1324 vim_free(ap->pat);
1325 vim_free(ap);
1326 return FAIL;
1327 }
1328 }
1329 ap->cmds = NULL;
1330 *prev_ap = ap;
1331 last_autopat[(int)event] = ap;
1332 ap->next = NULL;
1333 if (group == AUGROUP_ALL)
1334 ap->group = current_augroup;
1335 else
1336 ap->group = group;
1337 }
1338
1339 /*
1340 * Add the autocmd at the end of the AutoCmd list.
1341 */
1342 prev_ac = &(ap->cmds);
1343 while ((ac = *prev_ac) != NULL)
1344 prev_ac = &ac->next;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001345 ac = ALLOC_ONE(AutoCmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001346 if (ac == NULL)
1347 return FAIL;
1348 ac->cmd = vim_strsave(cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001349 ac->script_ctx = current_sctx;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001350 if (flags & UC_VIM9)
1351 ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001352#ifdef FEAT_EVAL
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001353 ac->script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001354#endif
1355 if (ac->cmd == NULL)
1356 {
1357 vim_free(ac);
1358 return FAIL;
1359 }
1360 ac->next = NULL;
1361 *prev_ac = ac;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001362 ac->once = once;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001363 ac->nested = nested;
1364 }
1365 }
1366
1367 au_cleanup(); // may really delete removed patterns/commands now
1368 return OK;
1369}
1370
1371/*
1372 * Implementation of ":doautocmd [group] event [fname]".
1373 * Return OK for success, FAIL for failure;
1374 */
1375 int
1376do_doautocmd(
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001377 char_u *arg_start,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001378 int do_msg, // give message for no matching autocmds?
1379 int *did_something)
1380{
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001381 char_u *arg = arg_start;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001382 char_u *fname;
1383 int nothing_done = TRUE;
1384 int group;
1385
1386 if (did_something != NULL)
1387 *did_something = FALSE;
1388
1389 /*
1390 * Check for a legal group name. If not, use AUGROUP_ALL.
1391 */
1392 group = au_get_grouparg(&arg);
1393 if (arg == NULL) // out of memory
1394 return FAIL;
1395
1396 if (*arg == '*')
1397 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001398 emsg(_(e_cant_execute_autocommands_for_all_events));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001399 return FAIL;
1400 }
1401
1402 /*
1403 * Scan over the events.
1404 * If we find an illegal name, return here, don't do anything.
1405 */
1406 fname = find_end_event(arg, group != AUGROUP_ALL);
1407 if (fname == NULL)
1408 return FAIL;
1409
1410 fname = skipwhite(fname);
1411
1412 /*
1413 * Loop over the events.
1414 */
1415 while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
1416 if (apply_autocmds_group(event_name2nr(arg, &arg),
1417 fname, NULL, TRUE, group, curbuf, NULL))
1418 nothing_done = FALSE;
1419
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001420 if (nothing_done && do_msg
1421#ifdef FEAT_EVAL
1422 && !aborting()
1423#endif
1424 )
1425 smsg(_("No matching autocommands: %s"), arg_start);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001426 if (did_something != NULL)
1427 *did_something = !nothing_done;
1428
1429#ifdef FEAT_EVAL
1430 return aborting() ? FAIL : OK;
1431#else
1432 return OK;
1433#endif
1434}
1435
1436/*
1437 * ":doautoall": execute autocommands for each loaded buffer.
1438 */
1439 void
1440ex_doautoall(exarg_T *eap)
1441{
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001442 int retval = OK;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001443 aco_save_T aco;
1444 buf_T *buf;
1445 bufref_T bufref;
1446 char_u *arg = eap->arg;
1447 int call_do_modelines = check_nomodeline(&arg);
1448 int did_aucmd;
1449
1450 /*
1451 * This is a bit tricky: For some commands curwin->w_buffer needs to be
1452 * equal to curbuf, but for some buffers there may not be a window.
1453 * So we change the buffer for the current window for a moment. This
1454 * gives problems when the autocommands make changes to the list of
1455 * buffers or windows...
1456 */
1457 FOR_ALL_BUFFERS(buf)
1458 {
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001459 // Only do loaded buffers and skip the current buffer, it's done last.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001460 if (buf->b_ml.ml_mfp == NULL || buf == curbuf)
1461 continue;
1462
Bram Moolenaare76062c2022-11-28 18:51:43 +00001463 // Find a window for this buffer and save some values.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001464 aucmd_prepbuf(&aco, buf);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001465 if (curbuf != buf)
1466 {
1467 // Failed to find a window for this buffer. Better not execute
1468 // autocommands then.
1469 retval = FAIL;
1470 break;
1471 }
1472
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001473 set_bufref(&bufref, buf);
1474
1475 // execute the autocommands for this buffer
1476 retval = do_doautocmd(arg, FALSE, &did_aucmd);
1477
1478 if (call_do_modelines && did_aucmd)
1479 // Execute the modeline settings, but don't set window-local
1480 // options if we are using the current window for another
1481 // buffer.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001482 do_modelines(is_aucmd_win(curwin) ? OPT_NOWIN : 0);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001483
1484 // restore the current window
1485 aucmd_restbuf(&aco);
1486
1487 // stop if there is some error or buffer was deleted
1488 if (retval == FAIL || !bufref_valid(&bufref))
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001489 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001490 retval = FAIL;
1491 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001492 }
1493 }
1494
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001495 // Execute autocommands for the current buffer last.
1496 if (retval == OK)
1497 {
1498 do_doautocmd(arg, FALSE, &did_aucmd);
1499 if (call_do_modelines && did_aucmd)
1500 do_modelines(0);
1501 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001502}
1503
1504/*
1505 * Check *argp for <nomodeline>. When it is present return FALSE, otherwise
1506 * return TRUE and advance *argp to after it.
1507 * Thus return TRUE when do_modelines() should be called.
1508 */
1509 int
1510check_nomodeline(char_u **argp)
1511{
1512 if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
1513 {
1514 *argp = skipwhite(*argp + 12);
1515 return FALSE;
1516 }
1517 return TRUE;
1518}
1519
1520/*
1521 * Prepare for executing autocommands for (hidden) buffer "buf".
1522 * Search for a visible window containing the current buffer. If there isn't
Bram Moolenaare76062c2022-11-28 18:51:43 +00001523 * one then use an entry in "aucmd_win[]".
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001524 * Set "curbuf" and "curwin" to match "buf".
Bram Moolenaare76062c2022-11-28 18:51:43 +00001525 * When this fails "curbuf" is not equal "buf".
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001526 */
1527 void
1528aucmd_prepbuf(
1529 aco_save_T *aco, // structure to save values in
1530 buf_T *buf) // new curbuf
1531{
1532 win_T *win;
1533 int save_ea;
1534#ifdef FEAT_AUTOCHDIR
1535 int save_acd;
1536#endif
1537
1538 // Find a window that is for the new buffer
1539 if (buf == curbuf) // be quick when buf is curbuf
1540 win = curwin;
1541 else
1542 FOR_ALL_WINDOWS(win)
1543 if (win->w_buffer == buf)
1544 break;
1545
Bram Moolenaare76062c2022-11-28 18:51:43 +00001546 // Allocate a window when needed.
1547 win_T *auc_win = NULL;
1548 int auc_idx = AUCMD_WIN_COUNT;
1549 if (win == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001550 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001551 for (auc_idx = 0; auc_idx < AUCMD_WIN_COUNT; ++auc_idx)
1552 if (!aucmd_win[auc_idx].auc_win_used)
1553 {
Bram Moolenaar84497cd2022-11-28 20:34:52 +00001554 if (aucmd_win[auc_idx].auc_win == NULL)
1555 aucmd_win[auc_idx].auc_win = win_alloc_popup_win();
1556 auc_win = aucmd_win[auc_idx].auc_win;
Bram Moolenaare76062c2022-11-28 18:51:43 +00001557 if (auc_win != NULL)
Bram Moolenaare76062c2022-11-28 18:51:43 +00001558 aucmd_win[auc_idx].auc_win_used = TRUE;
Bram Moolenaare76062c2022-11-28 18:51:43 +00001559 break;
1560 }
1561
1562 // If this fails (out of memory or using all AUCMD_WIN_COUNT
1563 // entries) then we can't reliable execute the autocmd, return with
1564 // "curbuf" unequal "buf".
1565 if (auc_win == NULL)
1566 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001567 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001568
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001569 aco->save_curwin_id = curwin->w_id;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001570 aco->save_prevwin_id = prevwin == NULL ? 0 : prevwin->w_id;
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001571 aco->save_State = State;
zeertzjqf2678472024-01-17 21:22:59 +01001572#ifdef FEAT_JOB_CHANNEL
1573 if (bt_prompt(curbuf))
1574 aco->save_prompt_insert = curbuf->b_prompt_insert;
1575#endif
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001576
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001577 if (win != NULL)
1578 {
1579 // There is a window for "buf" in the current tab page, make it the
1580 // curwin. This is preferred, it has the least side effects (esp. if
1581 // "buf" is curbuf).
Bram Moolenaare76062c2022-11-28 18:51:43 +00001582 aco->use_aucmd_win_idx = -1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001583 curwin = win;
1584 }
1585 else
1586 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001587 // There is no window for "buf", use "auc_win". To minimize the side
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001588 // effects, insert it in the current tab page.
1589 // Anything related to a window (e.g., setting folds) may have
1590 // unexpected results.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001591 aco->use_aucmd_win_idx = auc_idx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001592
Bram Moolenaare76062c2022-11-28 18:51:43 +00001593 win_init_popup_win(auc_win, buf);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001594
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001595 aco->globaldir = globaldir;
1596 globaldir = NULL;
1597
Bram Moolenaare76062c2022-11-28 18:51:43 +00001598 // Split the current window, put the auc_win in the upper half.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001599 // We don't want the BufEnter or WinEnter autocommands.
1600 block_autocmds();
1601 make_snapshot(SNAP_AUCMD_IDX);
1602 save_ea = p_ea;
1603 p_ea = FALSE;
1604
1605#ifdef FEAT_AUTOCHDIR
1606 // Prevent chdir() call in win_enter_ext(), through do_autochdir().
1607 save_acd = p_acd;
1608 p_acd = FALSE;
1609#endif
1610
Sean Dewar704966c2024-02-20 22:00:33 +01001611 (void)win_split_ins(0, WSP_TOP | WSP_FORCE_ROOM, auc_win, 0, NULL);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001612 (void)win_comp_pos(); // recompute window positions
1613 p_ea = save_ea;
1614#ifdef FEAT_AUTOCHDIR
1615 p_acd = save_acd;
1616#endif
1617 unblock_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001618 curwin = auc_win;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001619 }
1620 curbuf = buf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001621 aco->new_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001622 set_bufref(&aco->new_curbuf, curbuf);
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001623
1624 // disable the Visual area, the position may be invalid in another buffer
1625 aco->save_VIsual_active = VIsual_active;
1626 VIsual_active = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001627}
1628
1629/*
1630 * Cleanup after executing autocommands for a (hidden) buffer.
1631 * Restore the window as it was (if possible).
1632 */
1633 void
1634aucmd_restbuf(
1635 aco_save_T *aco) // structure holding saved values
1636{
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001637 int dummy;
1638 win_T *save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001639
Bram Moolenaare76062c2022-11-28 18:51:43 +00001640 if (aco->use_aucmd_win_idx >= 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001641 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001642 win_T *awp = aucmd_win[aco->use_aucmd_win_idx].auc_win;
1643
Bram Moolenaare76062c2022-11-28 18:51:43 +00001644 // Find "awp", it can't be closed, but it may be in another tab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001645 // page. Do not trigger autocommands here.
1646 block_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001647 if (curwin != awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001648 {
1649 tabpage_T *tp;
1650 win_T *wp;
1651
1652 FOR_ALL_TAB_WINDOWS(tp, wp)
1653 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001654 if (wp == awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001655 {
1656 if (tp != curtab)
1657 goto_tabpage_tp(tp, TRUE, TRUE);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001658 win_goto(awp);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001659 goto win_found;
1660 }
1661 }
1662 }
1663win_found:
zeertzjq46bdae02023-09-24 23:16:08 +02001664 --curbuf->b_nwindows;
orbitalcde8de02023-04-02 22:05:13 +01001665#ifdef FEAT_JOB_CHANNEL
zeertzjqa40c0bc2023-05-27 14:10:08 +01001666 int save_stop_insert_mode = stop_insert_mode;
orbitalcde8de02023-04-02 22:05:13 +01001667 // May need to stop Insert mode if we were in a prompt buffer.
1668 leaving_window(curwin);
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001669 // Do not stop Insert mode when already in Insert mode before.
1670 if (aco->save_State & MODE_INSERT)
zeertzjqa40c0bc2023-05-27 14:10:08 +01001671 stop_insert_mode = save_stop_insert_mode;
orbitalcde8de02023-04-02 22:05:13 +01001672#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001673 // Remove the window and frame from the tree of frames.
Sean Dewar704966c2024-02-20 22:00:33 +01001674 (void)winframe_remove(curwin, &dummy, NULL, NULL);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001675 win_remove(curwin, NULL);
Bram Moolenaar84497cd2022-11-28 20:34:52 +00001676
1677 // The window is marked as not used, but it is not freed, it can be
1678 // used again.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001679 aucmd_win[aco->use_aucmd_win_idx].auc_win_used = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001680 last_status(FALSE); // may need to remove last status line
1681
1682 if (!valid_tabpage_win(curtab))
1683 // no valid window in current tabpage
1684 close_tabpage(curtab);
1685
1686 restore_snapshot(SNAP_AUCMD_IDX, FALSE);
1687 (void)win_comp_pos(); // recompute window positions
1688 unblock_autocmds();
1689
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001690 save_curwin = win_find_by_id(aco->save_curwin_id);
1691 if (save_curwin != NULL)
1692 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001693 else
1694 // Hmm, original window disappeared. Just use the first one.
1695 curwin = firstwin;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001696 curbuf = curwin->w_buffer;
1697#ifdef FEAT_JOB_CHANNEL
1698 // May need to restore insert mode for a prompt buffer.
1699 entering_window(curwin);
zeertzjqf2678472024-01-17 21:22:59 +01001700 if (bt_prompt(curbuf))
1701 curbuf->b_prompt_insert = aco->save_prompt_insert;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001702#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001703 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001704#ifdef FEAT_EVAL
Bram Moolenaare76062c2022-11-28 18:51:43 +00001705 vars_clear(&awp->w_vars->dv_hashtab); // free all w: variables
1706 hash_init(&awp->w_vars->dv_hashtab); // re-use the hashtab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001707#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001708 vim_free(globaldir);
1709 globaldir = aco->globaldir;
1710
1711 // the buffer contents may have changed
zeertzjq49f05242023-02-04 10:58:34 +00001712 VIsual_active = aco->save_VIsual_active;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001713 check_cursor();
1714 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1715 {
1716 curwin->w_topline = curbuf->b_ml.ml_line_count;
1717#ifdef FEAT_DIFF
1718 curwin->w_topfill = 0;
1719#endif
1720 }
1721#if defined(FEAT_GUI)
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001722 if (gui.in_use)
1723 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001724 // Hide the scrollbars from the "awp" and update.
1725 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_LEFT], FALSE);
1726 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_RIGHT], FALSE);
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001727 gui_may_update_scrollbars();
1728 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001729#endif
1730 }
1731 else
1732 {
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001733 // Restore curwin. Use the window ID, a window may have been closed
1734 // and the memory re-used for another one.
1735 save_curwin = win_find_by_id(aco->save_curwin_id);
1736 if (save_curwin != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001737 {
1738 // Restore the buffer which was previously edited by curwin, if
1739 // it was changed, we are still the same window and the buffer is
1740 // valid.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001741 if (curwin->w_id == aco->new_curwin_id
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001742 && curbuf != aco->new_curbuf.br_buf
1743 && bufref_valid(&aco->new_curbuf)
1744 && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
1745 {
1746# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
1747 if (curwin->w_s == &curbuf->b_s)
1748 curwin->w_s = &aco->new_curbuf.br_buf->b_s;
1749# endif
1750 --curbuf->b_nwindows;
1751 curbuf = aco->new_curbuf.br_buf;
1752 curwin->w_buffer = curbuf;
1753 ++curbuf->b_nwindows;
1754 }
1755
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001756 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001757 curbuf = curwin->w_buffer;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001758 prevwin = win_find_by_id(aco->save_prevwin_id);
zeertzjq49f05242023-02-04 10:58:34 +00001759
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001760 // In case the autocommand moves the cursor to a position that
1761 // does not exist in curbuf.
zeertzjq49f05242023-02-04 10:58:34 +00001762 VIsual_active = aco->save_VIsual_active;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001763 check_cursor();
1764 }
1765 }
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001766
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001767 VIsual_active = aco->save_VIsual_active;
zeertzjq49f05242023-02-04 10:58:34 +00001768 check_cursor(); // just in case lines got deleted
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001769 if (VIsual_active)
1770 check_pos(curbuf, &VIsual);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001771}
1772
1773static int autocmd_nested = FALSE;
1774
1775/*
1776 * Execute autocommands for "event" and file name "fname".
1777 * Return TRUE if some commands were executed.
1778 */
1779 int
1780apply_autocmds(
1781 event_T event,
1782 char_u *fname, // NULL or empty means use actual file name
1783 char_u *fname_io, // fname to use for <afile> on cmdline
1784 int force, // when TRUE, ignore autocmd_busy
1785 buf_T *buf) // buffer for <abuf>
1786{
1787 return apply_autocmds_group(event, fname, fname_io, force,
1788 AUGROUP_ALL, buf, NULL);
1789}
1790
1791/*
1792 * Like apply_autocmds(), but with extra "eap" argument. This takes care of
1793 * setting v:filearg.
1794 */
1795 int
1796apply_autocmds_exarg(
1797 event_T event,
1798 char_u *fname,
1799 char_u *fname_io,
1800 int force,
1801 buf_T *buf,
1802 exarg_T *eap)
1803{
1804 return apply_autocmds_group(event, fname, fname_io, force,
1805 AUGROUP_ALL, buf, eap);
1806}
1807
1808/*
1809 * Like apply_autocmds(), but handles the caller's retval. If the script
1810 * processing is being aborted or if retval is FAIL when inside a try
1811 * conditional, no autocommands are executed. If otherwise the autocommands
1812 * cause the script to be aborted, retval is set to FAIL.
1813 */
1814 int
1815apply_autocmds_retval(
1816 event_T event,
1817 char_u *fname, // NULL or empty means use actual file name
1818 char_u *fname_io, // fname to use for <afile> on cmdline
1819 int force, // when TRUE, ignore autocmd_busy
1820 buf_T *buf, // buffer for <abuf>
1821 int *retval) // pointer to caller's retval
1822{
1823 int did_cmd;
1824
1825#ifdef FEAT_EVAL
1826 if (should_abort(*retval))
1827 return FALSE;
1828#endif
1829
1830 did_cmd = apply_autocmds_group(event, fname, fname_io, force,
1831 AUGROUP_ALL, buf, NULL);
1832 if (did_cmd
1833#ifdef FEAT_EVAL
1834 && aborting()
1835#endif
1836 )
1837 *retval = FAIL;
1838 return did_cmd;
1839}
1840
1841/*
1842 * Return TRUE when there is a CursorHold autocommand defined.
1843 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001844 static int
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001845has_cursorhold(void)
1846{
Bram Moolenaar24959102022-05-07 20:01:16 +01001847 return (first_autopat[(int)(get_real_state() == MODE_NORMAL_BUSY
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001848 ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
1849}
1850
1851/*
1852 * Return TRUE if the CursorHold event can be triggered.
1853 */
1854 int
1855trigger_cursorhold(void)
1856{
1857 int state;
1858
1859 if (!did_cursorhold
1860 && has_cursorhold()
1861 && reg_recording == 0
1862 && typebuf.tb_len == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001863 && !ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001864 {
1865 state = get_real_state();
Bram Moolenaar24959102022-05-07 20:01:16 +01001866 if (state == MODE_NORMAL_BUSY || (state & MODE_INSERT) != 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001867 return TRUE;
1868 }
1869 return FALSE;
1870}
1871
1872/*
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001873 * Return TRUE when there is a WinResized autocommand defined.
1874 */
1875 int
1876has_winresized(void)
1877{
1878 return (first_autopat[(int)EVENT_WINRESIZED] != NULL);
1879}
1880
1881/*
LemonBoy09371822022-04-08 15:18:45 +01001882 * Return TRUE when there is a WinScrolled autocommand defined.
1883 */
1884 int
1885has_winscrolled(void)
1886{
1887 return (first_autopat[(int)EVENT_WINSCROLLED] != NULL);
1888}
1889
1890/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001891 * Return TRUE when there is a CursorMoved autocommand defined.
1892 */
1893 int
1894has_cursormoved(void)
1895{
1896 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1897}
1898
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001899/*
1900 * Return TRUE when there is a CursorMovedI autocommand defined.
1901 */
1902 int
1903has_cursormovedI(void)
1904{
1905 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1906}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001907
1908/*
1909 * Return TRUE when there is a TextChanged autocommand defined.
1910 */
1911 int
1912has_textchanged(void)
1913{
1914 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1915}
1916
1917/*
1918 * Return TRUE when there is a TextChangedI autocommand defined.
1919 */
1920 int
1921has_textchangedI(void)
1922{
1923 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
1924}
1925
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001926/*
1927 * Return TRUE when there is a TextChangedP autocommand defined.
1928 */
1929 int
1930has_textchangedP(void)
1931{
1932 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
1933}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001934
1935/*
1936 * Return TRUE when there is an InsertCharPre autocommand defined.
1937 */
1938 int
1939has_insertcharpre(void)
1940{
1941 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
1942}
1943
1944/*
1945 * Return TRUE when there is an CmdUndefined autocommand defined.
1946 */
1947 int
1948has_cmdundefined(void)
1949{
1950 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
1951}
1952
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001953#if defined(FEAT_EVAL) || defined(PROTO)
1954/*
1955 * Return TRUE when there is a TextYankPost autocommand defined.
1956 */
1957 int
1958has_textyankpost(void)
1959{
1960 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
1961}
1962#endif
1963
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001964#if defined(FEAT_EVAL) || defined(PROTO)
1965/*
1966 * Return TRUE when there is a CompleteChanged autocommand defined.
1967 */
1968 int
1969has_completechanged(void)
1970{
1971 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
1972}
1973#endif
1974
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02001975#if defined(FEAT_EVAL) || defined(PROTO)
1976/*
1977 * Return TRUE when there is a ModeChanged autocommand defined.
1978 */
1979 int
1980has_modechanged(void)
1981{
1982 return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
1983}
1984#endif
1985
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001986/*
1987 * Execute autocommands for "event" and file name "fname".
1988 * Return TRUE if some commands were executed.
1989 */
1990 static int
1991apply_autocmds_group(
1992 event_T event,
1993 char_u *fname, // NULL or empty means use actual file name
1994 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
1995 // use fname
1996 int force, // when TRUE, ignore autocmd_busy
1997 int group, // group ID, or AUGROUP_ALL
1998 buf_T *buf, // buffer for <abuf>
1999 exarg_T *eap UNUSED) // command arguments
2000{
2001 char_u *sfname = NULL; // short file name
2002 char_u *tail;
2003 int save_changed;
2004 buf_T *old_curbuf;
2005 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002006 char_u *save_autocmd_fname;
2007 int save_autocmd_fname_full;
2008 int save_autocmd_bufnr;
2009 char_u *save_autocmd_match;
2010 int save_autocmd_busy;
2011 int save_autocmd_nested;
2012 static int nesting = 0;
LemonBoyeca7c602022-04-14 15:39:43 +01002013 AutoPatCmd_T patcmd;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002014 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002015 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002016#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002017 funccal_entry_T funccal_entry;
2018 char_u *save_cmdarg;
2019 long save_cmdbang;
2020#endif
2021 static int filechangeshell_busy = FALSE;
2022#ifdef FEAT_PROFILE
2023 proftime_T wait_time;
2024#endif
2025 int did_save_redobuff = FALSE;
2026 save_redo_T save_redo;
2027 int save_KeyTyped = KeyTyped;
ichizok7e5fe382023-04-15 13:17:50 +01002028 ESTACK_CHECK_DECLARATION;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002029
2030 /*
2031 * Quickly return if there are no autocommands for this event or
2032 * autocommands are blocked.
2033 */
2034 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
2035 || autocmd_blocked > 0)
2036 goto BYPASS_AU;
2037
2038 /*
2039 * When autocommands are busy, new autocommands are only executed when
2040 * explicitly enabled with the "nested" flag.
2041 */
2042 if (autocmd_busy && !(force || autocmd_nested))
2043 goto BYPASS_AU;
2044
2045#ifdef FEAT_EVAL
2046 /*
2047 * Quickly return when immediately aborting on error, or when an interrupt
2048 * occurred or an exception was thrown but not caught.
2049 */
2050 if (aborting())
2051 goto BYPASS_AU;
2052#endif
2053
2054 /*
2055 * FileChangedShell never nests, because it can create an endless loop.
2056 */
2057 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
2058 || event == EVENT_FILECHANGEDSHELLPOST))
2059 goto BYPASS_AU;
2060
2061 /*
2062 * Ignore events in 'eventignore'.
2063 */
2064 if (event_ignored(event))
2065 goto BYPASS_AU;
2066
2067 /*
2068 * Allow nesting of autocommands, but restrict the depth, because it's
2069 * possible to create an endless loop.
2070 */
2071 if (nesting == 10)
2072 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00002073 emsg(_(e_autocommand_nesting_too_deep));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002074 goto BYPASS_AU;
2075 }
2076
2077 /*
2078 * Check if these autocommands are disabled. Used when doing ":all" or
2079 * ":ball".
2080 */
2081 if ( (autocmd_no_enter
2082 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
2083 || (autocmd_no_leave
2084 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
2085 goto BYPASS_AU;
2086
Bram Moolenaarbb393d82022-12-09 12:21:50 +00002087 if (event == EVENT_CMDLINECHANGED)
2088 ++aucmd_cmdline_changed_count;
2089
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002090 /*
2091 * Save the autocmd_* variables and info about the current buffer.
2092 */
2093 save_autocmd_fname = autocmd_fname;
2094 save_autocmd_fname_full = autocmd_fname_full;
2095 save_autocmd_bufnr = autocmd_bufnr;
2096 save_autocmd_match = autocmd_match;
2097 save_autocmd_busy = autocmd_busy;
2098 save_autocmd_nested = autocmd_nested;
2099 save_changed = curbuf->b_changed;
2100 old_curbuf = curbuf;
2101
2102 /*
2103 * Set the file name to be used for <afile>.
2104 * Make a copy to avoid that changing a buffer name or directory makes it
2105 * invalid.
2106 */
2107 if (fname_io == NULL)
2108 {
2109 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
Bram Moolenaarbb393d82022-12-09 12:21:50 +00002110 || event == EVENT_OPTIONSET
Danek Duvalld7d56032024-01-14 20:19:59 +01002111 || event == EVENT_MODECHANGED
2112 || event == EVENT_TERMRESPONSEALL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002113 autocmd_fname = NULL;
2114 else if (fname != NULL && !ends_excmd(*fname))
2115 autocmd_fname = fname;
2116 else if (buf != NULL)
2117 autocmd_fname = buf->b_ffname;
2118 else
2119 autocmd_fname = NULL;
2120 }
2121 else
2122 autocmd_fname = fname_io;
2123 if (autocmd_fname != NULL)
2124 autocmd_fname = vim_strsave(autocmd_fname);
2125 autocmd_fname_full = FALSE; // call FullName_save() later
2126
2127 /*
2128 * Set the buffer number to be used for <abuf>.
2129 */
2130 if (buf == NULL)
2131 autocmd_bufnr = 0;
2132 else
2133 autocmd_bufnr = buf->b_fnum;
2134
2135 /*
2136 * When the file name is NULL or empty, use the file name of buffer "buf".
2137 * Always use the full path of the file name to match with, in case
2138 * "allow_dirs" is set.
2139 */
2140 if (fname == NULL || *fname == NUL)
2141 {
2142 if (buf == NULL)
2143 fname = NULL;
2144 else
2145 {
2146#ifdef FEAT_SYN_HL
2147 if (event == EVENT_SYNTAX)
2148 fname = buf->b_p_syn;
2149 else
2150#endif
2151 if (event == EVENT_FILETYPE)
2152 fname = buf->b_p_ft;
2153 else
2154 {
2155 if (buf->b_sfname != NULL)
2156 sfname = vim_strsave(buf->b_sfname);
2157 fname = buf->b_ffname;
2158 }
2159 }
2160 if (fname == NULL)
2161 fname = (char_u *)"";
2162 fname = vim_strsave(fname); // make a copy, so we can change it
2163 }
2164 else
2165 {
2166 sfname = vim_strsave(fname);
2167 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002168 // ColorScheme, QuickFixCmd*, DirChanged and similar.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002169 if (event == EVENT_FILETYPE
2170 || event == EVENT_SYNTAX
2171 || event == EVENT_CMDLINECHANGED
2172 || event == EVENT_CMDLINEENTER
2173 || event == EVENT_CMDLINELEAVE
2174 || event == EVENT_CMDWINENTER
2175 || event == EVENT_CMDWINLEAVE
2176 || event == EVENT_CMDUNDEFINED
2177 || event == EVENT_FUNCUNDEFINED
2178 || event == EVENT_REMOTEREPLY
2179 || event == EVENT_SPELLFILEMISSING
2180 || event == EVENT_QUICKFIXCMDPRE
2181 || event == EVENT_COLORSCHEME
2182 || event == EVENT_COLORSCHEMEPRE
2183 || event == EVENT_OPTIONSET
2184 || event == EVENT_QUICKFIXCMDPOST
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002185 || event == EVENT_DIRCHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002186 || event == EVENT_DIRCHANGEDPRE
naohiro ono23beefe2021-11-13 12:38:49 +00002187 || event == EVENT_MODECHANGED
zeertzjqc601d982022-10-10 13:46:15 +01002188 || event == EVENT_MENUPOPUP
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002189 || event == EVENT_USER
LemonBoy09371822022-04-08 15:18:45 +01002190 || event == EVENT_WINCLOSED
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00002191 || event == EVENT_WINRESIZED
Danek Duvalld7d56032024-01-14 20:19:59 +01002192 || event == EVENT_WINSCROLLED
2193 || event == EVENT_TERMRESPONSEALL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002194 {
2195 fname = vim_strsave(fname);
2196 autocmd_fname_full = TRUE; // don't expand it later
2197 }
2198 else
2199 fname = FullName_save(fname, FALSE);
2200 }
2201 if (fname == NULL) // out of memory
2202 {
2203 vim_free(sfname);
2204 retval = FALSE;
2205 goto BYPASS_AU;
2206 }
2207
2208#ifdef BACKSLASH_IN_FILENAME
2209 /*
2210 * Replace all backslashes with forward slashes. This makes the
2211 * autocommand patterns portable between Unix and MS-DOS.
2212 */
2213 if (sfname != NULL)
2214 forward_slash(sfname);
2215 forward_slash(fname);
2216#endif
2217
2218#ifdef VMS
2219 // remove version for correct match
2220 if (sfname != NULL)
2221 vms_remove_version(sfname);
2222 vms_remove_version(fname);
2223#endif
2224
2225 /*
2226 * Set the name to be used for <amatch>.
2227 */
2228 autocmd_match = fname;
2229
2230
2231 // Don't redraw while doing autocommands.
2232 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002233
2234 // name and lnum are filled in later
2235 estack_push(ETYPE_AUCMD, NULL, 0);
ichizok7e5fe382023-04-15 13:17:50 +01002236 ESTACK_CHECK_SETUP;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002237
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002238 save_current_sctx = current_sctx;
2239
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002240#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002241# ifdef FEAT_PROFILE
2242 if (do_profiling == PROF_YES)
2243 prof_child_enter(&wait_time); // doesn't count for the caller itself
2244# endif
2245
2246 // Don't use local function variables, if called from a function.
2247 save_funccal(&funccal_entry);
2248#endif
2249
2250 /*
2251 * When starting to execute autocommands, save the search patterns.
2252 */
2253 if (!autocmd_busy)
2254 {
2255 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002256 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002257 {
2258 saveRedobuff(&save_redo);
2259 did_save_redobuff = TRUE;
2260 }
2261 did_filetype = keep_filetype;
2262 }
2263
2264 /*
2265 * Note that we are applying autocmds. Some commands need to know.
2266 */
2267 autocmd_busy = TRUE;
2268 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2269 ++nesting; // see matching decrement below
2270
2271 // Remember that FileType was triggered. Used for did_filetype().
2272 if (event == EVENT_FILETYPE)
2273 did_filetype = TRUE;
2274
2275 tail = gettail(fname);
2276
2277 // Find first autocommand that matches
LemonBoyeca7c602022-04-14 15:39:43 +01002278 CLEAR_FIELD(patcmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002279 patcmd.curpat = first_autopat[(int)event];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002280 patcmd.group = group;
2281 patcmd.fname = fname;
2282 patcmd.sfname = sfname;
2283 patcmd.tail = tail;
2284 patcmd.event = event;
2285 patcmd.arg_bufnr = autocmd_bufnr;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002286 auto_next_pat(&patcmd, FALSE);
2287
2288 // found one, start executing the autocommands
2289 if (patcmd.curpat != NULL)
2290 {
2291 // add to active_apc_list
2292 patcmd.next = active_apc_list;
2293 active_apc_list = &patcmd;
2294
2295#ifdef FEAT_EVAL
2296 // set v:cmdarg (only when there is a matching pattern)
2297 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2298 if (eap != NULL)
2299 {
2300 save_cmdarg = set_cmdarg(eap, NULL);
2301 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2302 }
2303 else
2304 save_cmdarg = NULL; // avoid gcc warning
2305#endif
2306 retval = TRUE;
2307 // mark the last pattern, to avoid an endless loop when more patterns
2308 // are added when executing autocommands
2309 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2310 ap->last = FALSE;
2311 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002312
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002313 // Make sure cursor and topline are valid. The first time the current
2314 // values are saved, restored by reset_lnums(). When nested only the
2315 // values are corrected when needed.
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002316 if (nesting == 1)
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002317 check_lnums(TRUE);
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002318 else
2319 check_lnums_nested(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002320
Christian Brabandt590aae32023-06-25 22:34:22 +01002321 int save_did_emsg = did_emsg;
2322 int save_ex_pressedreturn = get_pressedreturn();
ichizokc3f91c02021-12-17 09:44:33 +00002323
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002324 do_cmdline(NULL, getnextac, (void *)&patcmd,
2325 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002326
ichizokc3f91c02021-12-17 09:44:33 +00002327 did_emsg += save_did_emsg;
Christian Brabandt590aae32023-06-25 22:34:22 +01002328 set_pressedreturn(save_ex_pressedreturn);
ichizokc3f91c02021-12-17 09:44:33 +00002329
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002330 if (nesting == 1)
2331 // restore cursor and topline, unless they were changed
2332 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002333
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002334#ifdef FEAT_EVAL
2335 if (eap != NULL)
2336 {
2337 (void)set_cmdarg(NULL, save_cmdarg);
2338 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2339 }
2340#endif
2341 // delete from active_apc_list
2342 if (active_apc_list == &patcmd) // just in case
2343 active_apc_list = patcmd.next;
2344 }
2345
Bram Moolenaar79cdf022023-05-20 14:07:00 +01002346 if (RedrawingDisabled > 0)
2347 --RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002348 autocmd_busy = save_autocmd_busy;
2349 filechangeshell_busy = FALSE;
2350 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002351 vim_free(SOURCING_NAME);
ichizok7e5fe382023-04-15 13:17:50 +01002352 ESTACK_CHECK_NOW;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002353 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002354 vim_free(autocmd_fname);
2355 autocmd_fname = save_autocmd_fname;
2356 autocmd_fname_full = save_autocmd_fname_full;
2357 autocmd_bufnr = save_autocmd_bufnr;
2358 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002359 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002360#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002361 restore_funccal();
2362# ifdef FEAT_PROFILE
2363 if (do_profiling == PROF_YES)
2364 prof_child_exit(&wait_time);
2365# endif
2366#endif
2367 KeyTyped = save_KeyTyped;
2368 vim_free(fname);
2369 vim_free(sfname);
2370 --nesting; // see matching increment above
2371
2372 /*
2373 * When stopping to execute autocommands, restore the search patterns and
2374 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2375 * free any windows in the au_pending_free_win list.
2376 */
2377 if (!autocmd_busy)
2378 {
2379 restore_search_patterns();
2380 if (did_save_redobuff)
2381 restoreRedobuff(&save_redo);
2382 did_filetype = FALSE;
2383 while (au_pending_free_buf != NULL)
2384 {
2385 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002386
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002387 vim_free(au_pending_free_buf);
2388 au_pending_free_buf = b;
2389 }
2390 while (au_pending_free_win != NULL)
2391 {
2392 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002393
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002394 vim_free(au_pending_free_win);
2395 au_pending_free_win = w;
2396 }
2397 }
2398
2399 /*
2400 * Some events don't set or reset the Changed flag.
2401 * Check if still in the same buffer!
2402 */
2403 if (curbuf == old_curbuf
2404 && (event == EVENT_BUFREADPOST
2405 || event == EVENT_BUFWRITEPOST
2406 || event == EVENT_FILEAPPENDPOST
2407 || event == EVENT_VIMLEAVE
2408 || event == EVENT_VIMLEAVEPRE))
2409 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002410 if (curbuf->b_changed != save_changed)
2411 need_maketitle = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002412 curbuf->b_changed = save_changed;
2413 }
2414
2415 au_cleanup(); // may really delete removed patterns/commands now
2416
2417BYPASS_AU:
2418 // When wiping out a buffer make sure all its buffer-local autocommands
2419 // are deleted.
2420 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2421 aubuflocal_remove(buf);
2422
2423 if (retval == OK && event == EVENT_FILETYPE)
2424 au_did_filetype = TRUE;
2425
2426 return retval;
2427}
2428
2429# ifdef FEAT_EVAL
2430static char_u *old_termresponse = NULL;
Danek Duvalld7d56032024-01-14 20:19:59 +01002431static char_u *old_termu7resp = NULL;
2432static char_u *old_termblinkresp = NULL;
2433static char_u *old_termrbgresp = NULL;
2434static char_u *old_termrfgresp = NULL;
2435static char_u *old_termstyleresp = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002436# endif
2437
2438/*
2439 * Block triggering autocommands until unblock_autocmd() is called.
2440 * Can be used recursively, so long as it's symmetric.
2441 */
2442 void
2443block_autocmds(void)
2444{
2445# ifdef FEAT_EVAL
2446 // Remember the value of v:termresponse.
2447 if (autocmd_blocked == 0)
Danek Duvalld7d56032024-01-14 20:19:59 +01002448 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002449 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
Danek Duvalld7d56032024-01-14 20:19:59 +01002450 old_termu7resp = get_vim_var_str(VV_TERMU7RESP);
2451 old_termblinkresp = get_vim_var_str(VV_TERMBLINKRESP);
2452 old_termrbgresp = get_vim_var_str(VV_TERMRBGRESP);
2453 old_termrfgresp = get_vim_var_str(VV_TERMRFGRESP);
2454 old_termstyleresp = get_vim_var_str(VV_TERMSTYLERESP);
2455 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002456# endif
2457 ++autocmd_blocked;
2458}
2459
2460 void
2461unblock_autocmds(void)
2462{
2463 --autocmd_blocked;
2464
2465# ifdef FEAT_EVAL
Danek Duvalld7d56032024-01-14 20:19:59 +01002466 // When v:termresponse, etc, were set while autocommands were blocked,
2467 // trigger the autocommands now. Esp. useful when executing a shell
2468 // command during startup (vimdiff).
2469 if (autocmd_blocked == 0)
2470 {
2471 if (get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2472 {
2473 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2474 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"version", NULL, FALSE, curbuf);
2475 }
2476 if (get_vim_var_str(VV_TERMU7RESP) != old_termu7resp)
2477 {
2478 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"ambiguouswidth", NULL, FALSE, curbuf);
2479 }
2480 if (get_vim_var_str(VV_TERMBLINKRESP) != old_termblinkresp)
2481 {
2482 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"cursorblink", NULL, FALSE, curbuf);
2483 }
2484 if (get_vim_var_str(VV_TERMRBGRESP) != old_termrbgresp)
2485 {
2486 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"background", NULL, FALSE, curbuf);
2487 }
2488 if (get_vim_var_str(VV_TERMRFGRESP) != old_termrfgresp)
2489 {
2490 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"foreground", NULL, FALSE, curbuf);
2491 }
2492 if (get_vim_var_str(VV_TERMSTYLERESP) != old_termstyleresp)
2493 {
2494 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"cursorshape", NULL, FALSE, curbuf);
2495 }
2496 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002497# endif
2498}
2499
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002500 int
2501is_autocmd_blocked(void)
2502{
2503 return autocmd_blocked != 0;
2504}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002505
2506/*
2507 * Find next autocommand pattern that matches.
2508 */
2509 static void
2510auto_next_pat(
LemonBoyeca7c602022-04-14 15:39:43 +01002511 AutoPatCmd_T *apc,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002512 int stop_at_last) // stop when 'last' flag is set
2513{
2514 AutoPat *ap;
2515 AutoCmd *cp;
2516 char_u *name;
2517 char *s;
LemonBoyeca7c602022-04-14 15:39:43 +01002518 estack_T *entry;
2519 char_u *namep;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002520
LemonBoyeca7c602022-04-14 15:39:43 +01002521 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
2522
2523 // Clear the exestack entry for this ETYPE_AUCMD entry.
2524 VIM_CLEAR(entry->es_name);
2525 entry->es_info.aucmd = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002526
2527 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2528 {
2529 apc->curpat = NULL;
2530
2531 // Only use a pattern when it has not been removed, has commands and
2532 // the group matches. For buffer-local autocommands only check the
2533 // buffer number.
2534 if (ap->pat != NULL && ap->cmds != NULL
2535 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2536 {
2537 // execution-condition
2538 if (ap->buflocal_nr == 0
2539 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2540 apc->sfname, apc->tail, ap->allow_dirs))
2541 : ap->buflocal_nr == apc->arg_bufnr)
2542 {
2543 name = event_nr2name(apc->event);
2544 s = _("%s Autocommands for \"%s\"");
LemonBoyeca7c602022-04-14 15:39:43 +01002545 namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1);
2546 if (namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002547 {
LemonBoyeca7c602022-04-14 15:39:43 +01002548 sprintf((char *)namep, s, (char *)name, (char *)ap->pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002549 if (p_verbose >= 8)
2550 {
2551 verbose_enter();
LemonBoyeca7c602022-04-14 15:39:43 +01002552 smsg(_("Executing %s"), namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002553 verbose_leave();
2554 }
2555 }
2556
LemonBoyeca7c602022-04-14 15:39:43 +01002557 // Update the exestack entry for this autocmd.
2558 entry->es_name = namep;
2559 entry->es_info.aucmd = apc;
2560
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002561 apc->curpat = ap;
2562 apc->nextcmd = ap->cmds;
2563 // mark last command
2564 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2565 cp->last = FALSE;
2566 cp->last = TRUE;
2567 }
2568 line_breakcheck();
2569 if (apc->curpat != NULL) // found a match
2570 break;
2571 }
2572 if (stop_at_last && ap->last)
2573 break;
2574 }
2575}
2576
Dominique Pellee764d1b2023-03-12 21:20:59 +00002577#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002578/*
LemonBoyeca7c602022-04-14 15:39:43 +01002579 * Get the script context where autocommand "acp" is defined.
2580 */
2581 sctx_T *
2582acp_script_ctx(AutoPatCmd_T *acp)
2583{
2584 return &acp->script_ctx;
2585}
Dominique Pellee764d1b2023-03-12 21:20:59 +00002586#endif
LemonBoyeca7c602022-04-14 15:39:43 +01002587
2588/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002589 * Get next autocommand command.
2590 * Called by do_cmdline() to get the next line for ":if".
2591 * Returns allocated string, or NULL for end of autocommands.
2592 */
2593 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002594getnextac(
2595 int c UNUSED,
2596 void *cookie,
2597 int indent UNUSED,
2598 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002599{
LemonBoyeca7c602022-04-14 15:39:43 +01002600 AutoPatCmd_T *acp = (AutoPatCmd_T *)cookie;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002601 char_u *retval;
2602 AutoCmd *ac;
2603
2604 // Can be called again after returning the last line.
2605 if (acp->curpat == NULL)
2606 return NULL;
2607
2608 // repeat until we find an autocommand to execute
2609 for (;;)
2610 {
2611 // skip removed commands
2612 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2613 if (acp->nextcmd->last)
2614 acp->nextcmd = NULL;
2615 else
2616 acp->nextcmd = acp->nextcmd->next;
2617
2618 if (acp->nextcmd != NULL)
2619 break;
2620
2621 // at end of commands, find next pattern that matches
2622 if (acp->curpat->last)
2623 acp->curpat = NULL;
2624 else
2625 acp->curpat = acp->curpat->next;
2626 if (acp->curpat != NULL)
2627 auto_next_pat(acp, TRUE);
2628 if (acp->curpat == NULL)
2629 return NULL;
2630 }
2631
2632 ac = acp->nextcmd;
2633
2634 if (p_verbose >= 9)
2635 {
2636 verbose_enter_scroll();
2637 smsg(_("autocommand %s"), ac->cmd);
2638 msg_puts("\n"); // don't overwrite this either
2639 verbose_leave_scroll();
2640 }
2641 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002642 // Remove one-shot ("once") autocmd in anticipation of its execution.
2643 if (ac->once)
2644 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002645 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002646 current_sctx = ac->script_ctx;
LemonBoyeca7c602022-04-14 15:39:43 +01002647 acp->script_ctx = current_sctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002648 if (ac->last)
2649 acp->nextcmd = NULL;
2650 else
2651 acp->nextcmd = ac->next;
2652 return retval;
2653}
2654
2655/*
2656 * Return TRUE if there is a matching autocommand for "fname".
2657 * To account for buffer-local autocommands, function needs to know
2658 * in which buffer the file will be opened.
2659 */
2660 int
2661has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2662{
2663 AutoPat *ap;
2664 char_u *fname;
2665 char_u *tail = gettail(sfname);
2666 int retval = FALSE;
2667
2668 fname = FullName_save(sfname, FALSE);
2669 if (fname == NULL)
2670 return FALSE;
2671
2672#ifdef BACKSLASH_IN_FILENAME
2673 /*
2674 * Replace all backslashes with forward slashes. This makes the
2675 * autocommand patterns portable between Unix and MS-DOS.
2676 */
2677 sfname = vim_strsave(sfname);
2678 if (sfname != NULL)
2679 forward_slash(sfname);
2680 forward_slash(fname);
2681#endif
2682
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002683 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002684 if (ap->pat != NULL && ap->cmds != NULL
2685 && (ap->buflocal_nr == 0
2686 ? match_file_pat(NULL, &ap->reg_prog,
2687 fname, sfname, tail, ap->allow_dirs)
2688 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2689 ))
2690 {
2691 retval = TRUE;
2692 break;
2693 }
2694
2695 vim_free(fname);
2696#ifdef BACKSLASH_IN_FILENAME
2697 vim_free(sfname);
2698#endif
2699
2700 return retval;
2701}
2702
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002703/*
2704 * Function given to ExpandGeneric() to obtain the list of autocommand group
2705 * names.
2706 */
2707 char_u *
2708get_augroup_name(expand_T *xp UNUSED, int idx)
2709{
2710 if (idx == augroups.ga_len) // add "END" add the end
2711 return (char_u *)"END";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002712 if (idx < 0 || idx >= augroups.ga_len) // end of list
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002713 return NULL;
2714 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2715 // skip deleted entries
2716 return (char_u *)"";
2717 return AUGROUP_NAME(idx); // return a name
2718}
2719
2720static int include_groups = FALSE;
2721
2722 char_u *
2723set_context_in_autocmd(
2724 expand_T *xp,
2725 char_u *arg,
2726 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2727{
2728 char_u *p;
2729 int group;
2730
2731 // check for a group name, skip it if present
2732 include_groups = FALSE;
2733 p = arg;
2734 group = au_get_grouparg(&arg);
2735 if (group == AUGROUP_ERROR)
2736 return NULL;
2737 // If there only is a group name that's what we expand.
2738 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2739 {
2740 arg = p;
2741 group = AUGROUP_ALL;
2742 }
2743
2744 // skip over event name
2745 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2746 if (*p == ',')
2747 arg = p + 1;
2748 if (*p == NUL)
2749 {
2750 if (group == AUGROUP_ALL)
2751 include_groups = TRUE;
2752 xp->xp_context = EXPAND_EVENTS; // expand event name
2753 xp->xp_pattern = arg;
2754 return NULL;
2755 }
2756
2757 // skip over pattern
2758 arg = skipwhite(p);
2759 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2760 arg++;
2761 if (*arg)
2762 return arg; // expand (next) command
2763
2764 if (doautocmd)
2765 xp->xp_context = EXPAND_FILES; // expand file names
2766 else
2767 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2768 return NULL;
2769}
2770
2771/*
2772 * Function given to ExpandGeneric() to obtain the list of event names.
2773 */
2774 char_u *
2775get_event_name(expand_T *xp UNUSED, int idx)
2776{
2777 if (idx < augroups.ga_len) // First list group names, if wanted
2778 {
2779 if (!include_groups || AUGROUP_NAME(idx) == NULL
2780 || AUGROUP_NAME(idx) == get_deleted_augroup())
2781 return (char_u *)""; // skip deleted entries
2782 return AUGROUP_NAME(idx); // return a name
2783 }
2784 return (char_u *)event_names[idx - augroups.ga_len].name;
2785}
2786
Yee Cheng Chin900894b2023-09-29 20:42:32 +02002787/*
2788 * Function given to ExpandGeneric() to obtain the list of event names. Don't
2789 * include groups.
2790 */
2791 char_u *
2792get_event_name_no_group(expand_T *xp UNUSED, int idx)
2793{
2794 return (char_u *)event_names[idx].name;
2795}
2796
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002797
2798#if defined(FEAT_EVAL) || defined(PROTO)
2799/*
2800 * Return TRUE if autocmd is supported.
2801 */
2802 int
2803autocmd_supported(char_u *name)
2804{
2805 char_u *p;
2806
2807 return (event_name2nr(name, &p) != NUM_EVENTS);
2808}
2809
2810/*
2811 * Return TRUE if an autocommand is defined for a group, event and
2812 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2813 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2814 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2815 * Used for:
2816 * exists("#Group") or
2817 * exists("#Group#Event") or
2818 * exists("#Group#Event#pat") or
2819 * exists("#Event") or
2820 * exists("#Event#pat")
2821 */
2822 int
2823au_exists(char_u *arg)
2824{
2825 char_u *arg_save;
2826 char_u *pattern = NULL;
2827 char_u *event_name;
2828 char_u *p;
2829 event_T event;
2830 AutoPat *ap;
2831 buf_T *buflocal_buf = NULL;
2832 int group;
2833 int retval = FALSE;
2834
2835 // Make a copy so that we can change the '#' chars to a NUL.
2836 arg_save = vim_strsave(arg);
2837 if (arg_save == NULL)
2838 return FALSE;
2839 p = vim_strchr(arg_save, '#');
2840 if (p != NULL)
2841 *p++ = NUL;
2842
2843 // First, look for an autocmd group name
2844 group = au_find_group(arg_save);
2845 if (group == AUGROUP_ERROR)
2846 {
2847 // Didn't match a group name, assume the first argument is an event.
2848 group = AUGROUP_ALL;
2849 event_name = arg_save;
2850 }
2851 else
2852 {
2853 if (p == NULL)
2854 {
2855 // "Group": group name is present and it's recognized
2856 retval = TRUE;
2857 goto theend;
2858 }
2859
2860 // Must be "Group#Event" or "Group#Event#pat".
2861 event_name = p;
2862 p = vim_strchr(event_name, '#');
2863 if (p != NULL)
2864 *p++ = NUL; // "Group#Event#pat"
2865 }
2866
2867 pattern = p; // "pattern" is NULL when there is no pattern
2868
2869 // find the index (enum) for the event name
2870 event = event_name2nr(event_name, &p);
2871
2872 // return FALSE if the event name is not recognized
2873 if (event == NUM_EVENTS)
2874 goto theend;
2875
2876 // Find the first autocommand for this event.
2877 // If there isn't any, return FALSE;
2878 // If there is one and no pattern given, return TRUE;
2879 ap = first_autopat[(int)event];
2880 if (ap == NULL)
2881 goto theend;
2882
2883 // if pattern is "<buffer>", special handling is needed which uses curbuf
2884 // for pattern "<buffer=N>, fnamecmp() will work fine
2885 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2886 buflocal_buf = curbuf;
2887
2888 // Check if there is an autocommand with the given pattern.
2889 for ( ; ap != NULL; ap = ap->next)
2890 // only use a pattern when it has not been removed and has commands.
2891 // For buffer-local autocommands, fnamecmp() works fine.
2892 if (ap->pat != NULL && ap->cmds != NULL
2893 && (group == AUGROUP_ALL || ap->group == group)
2894 && (pattern == NULL
2895 || (buflocal_buf == NULL
2896 ? fnamecmp(ap->pat, pattern) == 0
2897 : ap->buflocal_nr == buflocal_buf->b_fnum)))
2898 {
2899 retval = TRUE;
2900 break;
2901 }
2902
2903theend:
2904 vim_free(arg_save);
2905 return retval;
2906}
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002907
2908/*
2909 * autocmd_add() and autocmd_delete() functions
2910 */
2911 static void
2912autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
2913{
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002914 list_T *aucmd_list;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002915 listitem_T *li;
2916 dict_T *event_dict;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002917 dictitem_T *di;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002918 char_u *event_name = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002919 list_T *event_list;
2920 listitem_T *eli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002921 event_T event;
2922 char_u *group_name = NULL;
2923 int group;
2924 char_u *pat = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002925 list_T *pat_list;
2926 listitem_T *pli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002927 char_u *cmd = NULL;
2928 char_u *end;
2929 int once;
2930 int nested;
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01002931 int replace; // replace the cmd for a group/event
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002932 int retval = VVAL_TRUE;
2933 int save_augroup = current_augroup;
2934
2935 rettv->v_type = VAR_BOOL;
2936 rettv->vval.v_number = VVAL_FALSE;
2937
2938 if (check_for_list_arg(argvars, 0) == FAIL)
2939 return;
2940
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002941 aucmd_list = argvars[0].vval.v_list;
2942 if (aucmd_list == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002943 return;
2944
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002945 FOR_ALL_LIST_ITEMS(aucmd_list, li)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002946 {
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002947 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002948 VIM_CLEAR(cmd);
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002949 event_name = NULL;
2950 event_list = NULL;
2951 pat = NULL;
2952 pat_list = NULL;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002953
2954 if (li->li_tv.v_type != VAR_DICT)
2955 continue;
2956
2957 event_dict = li->li_tv.vval.v_dict;
2958 if (event_dict == NULL)
2959 continue;
2960
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002961 di = dict_find(event_dict, (char_u *)"event", -1);
2962 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002963 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002964 if (di->di_tv.v_type == VAR_STRING)
2965 {
2966 event_name = di->di_tv.vval.v_string;
2967 if (event_name == NULL)
2968 {
2969 emsg(_(e_string_required));
2970 continue;
2971 }
2972 }
2973 else if (di->di_tv.v_type == VAR_LIST)
2974 {
2975 event_list = di->di_tv.vval.v_list;
2976 if (event_list == NULL)
2977 {
2978 emsg(_(e_list_required));
2979 continue;
2980 }
2981 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002982 else
2983 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002984 emsg(_(e_string_or_list_expected));
2985 continue;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002986 }
2987 }
2988
Bram Moolenaard61efa52022-07-23 09:52:04 +01002989 group_name = dict_get_string(event_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002990 if (group_name == NULL || *group_name == NUL)
2991 // if the autocmd group name is not specified, then use the current
2992 // autocmd group
2993 group = current_augroup;
2994 else
2995 {
2996 group = au_find_group(group_name);
2997 if (group == AUGROUP_ERROR)
2998 {
2999 if (delete)
3000 {
3001 semsg(_(e_no_such_group_str), group_name);
3002 retval = VVAL_FALSE;
3003 break;
3004 }
3005 // group is not found, create it now
3006 group = au_new_group(group_name);
3007 if (group == AUGROUP_ERROR)
3008 {
3009 semsg(_(e_no_such_group_str), group_name);
3010 retval = VVAL_FALSE;
3011 break;
3012 }
3013
3014 current_augroup = group;
3015 }
3016 }
3017
3018 // if a buffer number is specified, then generate a pattern of the form
3019 // "<buffer=n>. Otherwise, use the pattern supplied by the user.
3020 if (dict_has_key(event_dict, "bufnr"))
3021 {
3022 varnumber_T bnum;
3023
Bram Moolenaard61efa52022-07-23 09:52:04 +01003024 bnum = dict_get_number_def(event_dict, "bufnr", -1);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003025 if (bnum == -1)
3026 continue;
3027
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003028 vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
3029 pat = IObuff;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003030 }
3031 else
3032 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003033 di = dict_find(event_dict, (char_u *)"pattern", -1);
3034 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003035 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003036 if (di->di_tv.v_type == VAR_STRING)
3037 {
3038 pat = di->di_tv.vval.v_string;
3039 if (pat == NULL)
3040 {
3041 emsg(_(e_string_required));
3042 continue;
3043 }
3044 }
3045 else if (di->di_tv.v_type == VAR_LIST)
3046 {
3047 pat_list = di->di_tv.vval.v_list;
3048 if (pat_list == NULL)
3049 {
3050 emsg(_(e_list_required));
3051 continue;
3052 }
3053 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003054 else
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003055 {
3056 emsg(_(e_string_or_list_expected));
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003057 continue;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003058 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003059 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003060 else if (delete)
3061 pat = (char_u *)"";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003062 }
3063
Bram Moolenaard61efa52022-07-23 09:52:04 +01003064 once = dict_get_bool(event_dict, "once", FALSE);
3065 nested = dict_get_bool(event_dict, "nested", FALSE);
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01003066 // if 'replace' is true, then remove all the commands associated with
3067 // this autocmd event/group and add the new command.
Bram Moolenaard61efa52022-07-23 09:52:04 +01003068 replace = dict_get_bool(event_dict, "replace", FALSE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003069
Bram Moolenaard61efa52022-07-23 09:52:04 +01003070 cmd = dict_get_string(event_dict, "cmd", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003071 if (cmd == NULL)
3072 {
3073 if (delete)
3074 cmd = vim_strsave((char_u *)"");
3075 else
3076 continue;
3077 }
3078
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003079 if (delete && (event_name == NULL
3080 || (event_name[0] == '*' && event_name[1] == NUL)))
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003081 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003082 // if the event name is not specified or '*', delete all the events
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003083 for (event = (event_T)0; (int)event < NUM_EVENTS;
3084 event = (event_T)((int)event + 1))
3085 {
3086 if (do_autocmd_event(event, pat, once, nested, cmd, delete,
3087 group, 0) == FAIL)
3088 {
3089 retval = VVAL_FALSE;
3090 break;
3091 }
3092 }
3093 }
3094 else
3095 {
Bram Moolenaar968443e2022-05-27 21:16:34 +01003096 char_u *p = NULL;
3097
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003098 eli = NULL;
3099 end = NULL;
3100 while (TRUE)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003101 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003102 if (event_list != NULL)
3103 {
3104 if (eli == NULL)
3105 eli = event_list->lv_first;
3106 else
3107 eli = eli->li_next;
3108 if (eli == NULL)
3109 break;
3110 if (eli->li_tv.v_type != VAR_STRING
Bram Moolenaar882476a2022-06-01 16:02:38 +01003111 || (p = eli->li_tv.vval.v_string) == NULL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003112 {
3113 emsg(_(e_string_required));
Bram Moolenaar882476a2022-06-01 16:02:38 +01003114 break;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003115 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003116 }
3117 else
3118 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003119 if (p == NULL)
3120 p = event_name;
3121 if (p == NULL || *p == NUL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003122 break;
3123 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003124
3125 event = event_name2nr(p, &end);
3126 if (event == NUM_EVENTS || *end != NUL)
3127 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003128 // this also catches something following a valid event name
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003129 semsg(_(e_no_such_event_str), p);
3130 retval = VVAL_FALSE;
3131 break;
3132 }
3133 if (pat != NULL)
3134 {
3135 if (do_autocmd_event(event, pat, once, nested, cmd,
3136 delete | replace, group, 0) == FAIL)
3137 {
3138 retval = VVAL_FALSE;
3139 break;
3140 }
3141 }
3142 else if (pat_list != NULL)
3143 {
3144 FOR_ALL_LIST_ITEMS(pat_list, pli)
3145 {
3146 if (pli->li_tv.v_type != VAR_STRING
3147 || pli->li_tv.vval.v_string == NULL)
3148 {
3149 emsg(_(e_string_required));
3150 continue;
3151 }
3152 if (do_autocmd_event(event,
3153 pli->li_tv.vval.v_string, once, nested,
3154 cmd, delete | replace, group, 0) ==
3155 FAIL)
3156 {
3157 retval = VVAL_FALSE;
3158 break;
3159 }
3160 }
3161 if (retval == VVAL_FALSE)
3162 break;
3163 }
3164 if (event_name != NULL)
3165 p = end;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003166 }
3167 }
3168
3169 // if only the autocmd group name is specified for delete and the
3170 // autocmd event, pattern and cmd are not specified, then delete the
3171 // autocmd group.
3172 if (delete && group_name != NULL &&
3173 (event_name == NULL || event_name[0] == NUL)
3174 && (pat == NULL || pat[0] == NUL)
3175 && (cmd == NULL || cmd[0] == NUL))
3176 au_del_group(group_name);
3177 }
3178
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003179 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003180 VIM_CLEAR(cmd);
3181
3182 current_augroup = save_augroup;
3183 rettv->vval.v_number = retval;
3184}
3185
3186/*
3187 * autocmd_add() function
3188 */
3189 void
3190f_autocmd_add(typval_T *argvars, typval_T *rettv)
3191{
3192 autocmd_add_or_delete(argvars, rettv, FALSE);
3193}
3194
3195/*
3196 * autocmd_delete() function
3197 */
3198 void
3199f_autocmd_delete(typval_T *argvars, typval_T *rettv)
3200{
3201 autocmd_add_or_delete(argvars, rettv, TRUE);
3202}
3203
3204/*
3205 * autocmd_get() function
3206 * Returns a List of autocmds.
3207 */
3208 void
3209f_autocmd_get(typval_T *argvars, typval_T *rettv)
3210{
3211 event_T event_arg = NUM_EVENTS;
3212 event_T event;
3213 AutoPat *ap;
3214 AutoCmd *ac;
3215 list_T *event_list;
3216 dict_T *event_dict;
3217 char_u *event_name = NULL;
3218 char_u *pat = NULL;
3219 char_u *name = NULL;
3220 int group = AUGROUP_ALL;
3221
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01003222 if (rettv_list_alloc(rettv) == FAIL)
3223 return;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003224 if (check_for_opt_dict_arg(argvars, 0) == FAIL)
3225 return;
3226
3227 if (argvars[0].v_type == VAR_DICT)
3228 {
3229 // return only the autocmds in the specified group
3230 if (dict_has_key(argvars[0].vval.v_dict, "group"))
3231 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003232 name = dict_get_string(argvars[0].vval.v_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003233 if (name == NULL)
3234 return;
3235
3236 if (*name == NUL)
3237 group = AUGROUP_DEFAULT;
3238 else
3239 {
3240 group = au_find_group(name);
3241 if (group == AUGROUP_ERROR)
3242 {
3243 semsg(_(e_no_such_group_str), name);
3244 vim_free(name);
3245 return;
3246 }
3247 }
3248 vim_free(name);
3249 }
3250
3251 // return only the autocmds for the specified event
3252 if (dict_has_key(argvars[0].vval.v_dict, "event"))
3253 {
3254 int i;
3255
Bram Moolenaard61efa52022-07-23 09:52:04 +01003256 name = dict_get_string(argvars[0].vval.v_dict, "event", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003257 if (name == NULL)
3258 return;
3259
3260 if (name[0] == '*' && name[1] == NUL)
3261 event_arg = NUM_EVENTS;
3262 else
3263 {
3264 for (i = 0; event_names[i].name != NULL; i++)
3265 if (STRICMP(event_names[i].name, name) == 0)
3266 break;
3267 if (event_names[i].name == NULL)
3268 {
3269 semsg(_(e_no_such_event_str), name);
3270 vim_free(name);
3271 return;
3272 }
3273 event_arg = event_names[i].event;
3274 }
3275 vim_free(name);
3276 }
3277
3278 // return only the autocmds for the specified pattern
3279 if (dict_has_key(argvars[0].vval.v_dict, "pattern"))
3280 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003281 pat = dict_get_string(argvars[0].vval.v_dict, "pattern", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003282 if (pat == NULL)
3283 return;
3284 }
3285 }
3286
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003287 event_list = rettv->vval.v_list;
3288
3289 // iterate through all the autocmd events
3290 for (event = (event_T)0; (int)event < NUM_EVENTS;
3291 event = (event_T)((int)event + 1))
3292 {
3293 if (event_arg != NUM_EVENTS && event != event_arg)
3294 continue;
3295
3296 event_name = event_nr2name(event);
3297
3298 // iterate through all the patterns for this autocmd event
3299 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
3300 {
3301 char_u *group_name;
3302
3303 if (group != AUGROUP_ALL && group != ap->group)
3304 continue;
3305
3306 if (pat != NULL && STRCMP(pat, ap->pat) != 0)
3307 continue;
3308
3309 group_name = get_augroup_name(NULL, ap->group);
3310
3311 // iterate through all the commands for this pattern and add one
3312 // item for each cmd.
3313 for (ac = ap->cmds; ac != NULL; ac = ac->next)
3314 {
3315 event_dict = dict_alloc();
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003316 if (event_dict == NULL
3317 || list_append_dict(event_list, event_dict) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003318 return;
3319
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003320 if (dict_add_string(event_dict, "event", event_name) == FAIL
3321 || dict_add_string(event_dict, "group",
3322 group_name == NULL ? (char_u *)""
3323 : group_name) == FAIL
3324 || (ap->buflocal_nr != 0
3325 && (dict_add_number(event_dict, "bufnr",
3326 ap->buflocal_nr) == FAIL))
3327 || dict_add_string(event_dict, "pattern",
3328 ap->pat) == FAIL
3329 || dict_add_string(event_dict, "cmd", ac->cmd) == FAIL
3330 || dict_add_bool(event_dict, "once", ac->once) == FAIL
3331 || dict_add_bool(event_dict, "nested",
3332 ac->nested) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003333 return;
3334 }
3335 }
3336 }
3337
3338 vim_free(pat);
3339}
3340
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01003341#endif