blob: 00f41bddd965f39d13e32b30eb6eb1861dc8ac3d [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
John Marriott78d742a2024-04-02 20:26:01 +020077//
78// special cases:
79// BufNewFile and BufRead are searched for ALOT (especially at startup)
80// so we pre-determine their index into the event_tab[] table for fast access.
81// Keep these values in sync with event_tab[]!
82#define BUFNEWFILE_INDEX 9
83#define BUFREAD_INDEX 10
84
85// must be sorted by the 'value' field because it is used by bsearch()!
86static keyvalue_T event_tab[] = {
87 KEYVALUE_ENTRY(EVENT_BUFADD, "BufAdd"),
88 KEYVALUE_ENTRY(EVENT_BUFADD, "BufCreate"),
89 KEYVALUE_ENTRY(EVENT_BUFDELETE, "BufDelete"),
90 KEYVALUE_ENTRY(EVENT_BUFENTER, "BufEnter"),
91 KEYVALUE_ENTRY(EVENT_BUFFILEPOST, "BufFilePost"),
92 KEYVALUE_ENTRY(EVENT_BUFFILEPRE, "BufFilePre"),
93 KEYVALUE_ENTRY(EVENT_BUFHIDDEN, "BufHidden"),
94 KEYVALUE_ENTRY(EVENT_BUFLEAVE, "BufLeave"),
95 KEYVALUE_ENTRY(EVENT_BUFNEW, "BufNew"),
96 KEYVALUE_ENTRY(EVENT_BUFNEWFILE, "BufNewFile"), // BUFNEWFILE_INDEX
97 KEYVALUE_ENTRY(EVENT_BUFREADPOST, "BufRead"), // BUFREAD_INDEX
98 KEYVALUE_ENTRY(EVENT_BUFREADCMD, "BufReadCmd"),
99 KEYVALUE_ENTRY(EVENT_BUFREADPOST, "BufReadPost"),
100 KEYVALUE_ENTRY(EVENT_BUFREADPRE, "BufReadPre"),
101 KEYVALUE_ENTRY(EVENT_BUFUNLOAD, "BufUnload"),
102 KEYVALUE_ENTRY(EVENT_BUFWINENTER, "BufWinEnter"),
103 KEYVALUE_ENTRY(EVENT_BUFWINLEAVE, "BufWinLeave"),
104 KEYVALUE_ENTRY(EVENT_BUFWIPEOUT, "BufWipeout"),
105 KEYVALUE_ENTRY(EVENT_BUFWRITEPRE, "BufWrite"),
106 KEYVALUE_ENTRY(EVENT_BUFWRITECMD, "BufWriteCmd"),
107 KEYVALUE_ENTRY(EVENT_BUFWRITEPOST, "BufWritePost"),
108 KEYVALUE_ENTRY(EVENT_BUFWRITEPRE, "BufWritePre"),
109 KEYVALUE_ENTRY(EVENT_CMDLINECHANGED, "CmdlineChanged"),
110 KEYVALUE_ENTRY(EVENT_CMDLINEENTER, "CmdlineEnter"),
111 KEYVALUE_ENTRY(EVENT_CMDLINELEAVE, "CmdlineLeave"),
112 KEYVALUE_ENTRY(EVENT_CMDUNDEFINED, "CmdUndefined"),
113 KEYVALUE_ENTRY(EVENT_CMDWINENTER, "CmdwinEnter"),
114 KEYVALUE_ENTRY(EVENT_CMDWINLEAVE, "CmdwinLeave"),
115 KEYVALUE_ENTRY(EVENT_COLORSCHEME, "ColorScheme"),
116 KEYVALUE_ENTRY(EVENT_COLORSCHEMEPRE, "ColorSchemePre"),
117 KEYVALUE_ENTRY(EVENT_COMPLETECHANGED, "CompleteChanged"),
118 KEYVALUE_ENTRY(EVENT_COMPLETEDONE, "CompleteDone"),
119 KEYVALUE_ENTRY(EVENT_COMPLETEDONEPRE, "CompleteDonePre"),
120 KEYVALUE_ENTRY(EVENT_CURSORHOLD, "CursorHold"),
121 KEYVALUE_ENTRY(EVENT_CURSORHOLDI, "CursorHoldI"),
122 KEYVALUE_ENTRY(EVENT_CURSORMOVED, "CursorMoved"),
Shougo Matsushitad0952142024-06-20 22:05:16 +0200123 KEYVALUE_ENTRY(EVENT_CURSORMOVEDC, "CursorMovedC"),
John Marriott78d742a2024-04-02 20:26:01 +0200124 KEYVALUE_ENTRY(EVENT_CURSORMOVEDI, "CursorMovedI"),
125 KEYVALUE_ENTRY(EVENT_DIFFUPDATED, "DiffUpdated"),
126 KEYVALUE_ENTRY(EVENT_DIRCHANGED, "DirChanged"),
127 KEYVALUE_ENTRY(EVENT_DIRCHANGEDPRE, "DirChangedPre"),
128 KEYVALUE_ENTRY(EVENT_ENCODINGCHANGED, "EncodingChanged"),
129 KEYVALUE_ENTRY(EVENT_EXITPRE, "ExitPre"),
130 KEYVALUE_ENTRY(EVENT_FILEAPPENDCMD, "FileAppendCmd"),
131 KEYVALUE_ENTRY(EVENT_FILEAPPENDPOST, "FileAppendPost"),
132 KEYVALUE_ENTRY(EVENT_FILEAPPENDPRE, "FileAppendPre"),
133 KEYVALUE_ENTRY(EVENT_FILECHANGEDRO, "FileChangedRO"),
134 KEYVALUE_ENTRY(EVENT_FILECHANGEDSHELL, "FileChangedShell"),
135 KEYVALUE_ENTRY(EVENT_FILECHANGEDSHELLPOST, "FileChangedShellPost"),
136 KEYVALUE_ENTRY(EVENT_ENCODINGCHANGED, "FileEncoding"),
137 KEYVALUE_ENTRY(EVENT_FILEREADCMD, "FileReadCmd"),
138 KEYVALUE_ENTRY(EVENT_FILEREADPOST, "FileReadPost"),
139 KEYVALUE_ENTRY(EVENT_FILEREADPRE, "FileReadPre"),
140 KEYVALUE_ENTRY(EVENT_FILETYPE, "FileType"),
141 KEYVALUE_ENTRY(EVENT_FILEWRITECMD, "FileWriteCmd"),
142 KEYVALUE_ENTRY(EVENT_FILEWRITEPOST, "FileWritePost"),
143 KEYVALUE_ENTRY(EVENT_FILEWRITEPRE, "FileWritePre"),
144 KEYVALUE_ENTRY(EVENT_FILTERREADPOST, "FilterReadPost"),
145 KEYVALUE_ENTRY(EVENT_FILTERREADPRE, "FilterReadPre"),
146 KEYVALUE_ENTRY(EVENT_FILTERWRITEPOST, "FilterWritePost"),
147 KEYVALUE_ENTRY(EVENT_FILTERWRITEPRE, "FilterWritePre"),
148 KEYVALUE_ENTRY(EVENT_FOCUSGAINED, "FocusGained"),
149 KEYVALUE_ENTRY(EVENT_FOCUSLOST, "FocusLost"),
150 KEYVALUE_ENTRY(EVENT_FUNCUNDEFINED, "FuncUndefined"),
151 KEYVALUE_ENTRY(EVENT_GUIENTER, "GUIEnter"),
152 KEYVALUE_ENTRY(EVENT_GUIFAILED, "GUIFailed"),
153 KEYVALUE_ENTRY(EVENT_INSERTCHANGE, "InsertChange"),
154 KEYVALUE_ENTRY(EVENT_INSERTCHARPRE, "InsertCharPre"),
155 KEYVALUE_ENTRY(EVENT_INSERTENTER, "InsertEnter"),
156 KEYVALUE_ENTRY(EVENT_INSERTLEAVE, "InsertLeave"),
157 KEYVALUE_ENTRY(EVENT_INSERTLEAVEPRE, "InsertLeavePre"),
Shougo Matsushita83678842024-07-11 22:05:12 +0200158 KEYVALUE_ENTRY(EVENT_KEYINPUTPRE, "KeyInputPre"),
John Marriott78d742a2024-04-02 20:26:01 +0200159 KEYVALUE_ENTRY(EVENT_MENUPOPUP, "MenuPopup"),
160 KEYVALUE_ENTRY(EVENT_MODECHANGED, "ModeChanged"),
161 KEYVALUE_ENTRY(EVENT_OPTIONSET, "OptionSet"),
162 KEYVALUE_ENTRY(EVENT_QUICKFIXCMDPOST, "QuickFixCmdPost"),
163 KEYVALUE_ENTRY(EVENT_QUICKFIXCMDPRE, "QuickFixCmdPre"),
164 KEYVALUE_ENTRY(EVENT_QUITPRE, "QuitPre"),
165 KEYVALUE_ENTRY(EVENT_REMOTEREPLY, "RemoteReply"),
166 KEYVALUE_ENTRY(EVENT_SAFESTATE, "SafeState"),
167 KEYVALUE_ENTRY(EVENT_SAFESTATEAGAIN, "SafeStateAgain"),
168 KEYVALUE_ENTRY(EVENT_SESSIONLOADPOST, "SessionLoadPost"),
169 KEYVALUE_ENTRY(EVENT_SESSIONWRITEPOST, "SessionWritePost"),
170 KEYVALUE_ENTRY(EVENT_SHELLCMDPOST, "ShellCmdPost"),
171 KEYVALUE_ENTRY(EVENT_SHELLFILTERPOST, "ShellFilterPost"),
172 KEYVALUE_ENTRY(EVENT_SIGUSR1, "SigUSR1"),
173 KEYVALUE_ENTRY(EVENT_SOURCECMD, "SourceCmd"),
174 KEYVALUE_ENTRY(EVENT_SOURCEPOST, "SourcePost"),
175 KEYVALUE_ENTRY(EVENT_SOURCEPRE, "SourcePre"),
176 KEYVALUE_ENTRY(EVENT_SPELLFILEMISSING, "SpellFileMissing"),
177 KEYVALUE_ENTRY(EVENT_STDINREADPOST, "StdinReadPost"),
178 KEYVALUE_ENTRY(EVENT_STDINREADPRE, "StdinReadPre"),
179 KEYVALUE_ENTRY(EVENT_SWAPEXISTS, "SwapExists"),
180 KEYVALUE_ENTRY(EVENT_SYNTAX, "Syntax"),
181 KEYVALUE_ENTRY(EVENT_TABCLOSED, "TabClosed"),
182 KEYVALUE_ENTRY(EVENT_TABENTER, "TabEnter"),
183 KEYVALUE_ENTRY(EVENT_TABLEAVE, "TabLeave"),
184 KEYVALUE_ENTRY(EVENT_TABNEW, "TabNew"),
185 KEYVALUE_ENTRY(EVENT_TERMCHANGED, "TermChanged"),
186 KEYVALUE_ENTRY(EVENT_TERMINALOPEN, "TerminalOpen"),
187 KEYVALUE_ENTRY(EVENT_TERMINALWINOPEN, "TerminalWinOpen"),
188 KEYVALUE_ENTRY(EVENT_TERMRESPONSE, "TermResponse"),
189 KEYVALUE_ENTRY(EVENT_TERMRESPONSEALL, "TermResponseAll"),
190 KEYVALUE_ENTRY(EVENT_TEXTCHANGED, "TextChanged"),
191 KEYVALUE_ENTRY(EVENT_TEXTCHANGEDI, "TextChangedI"),
192 KEYVALUE_ENTRY(EVENT_TEXTCHANGEDP, "TextChangedP"),
193 KEYVALUE_ENTRY(EVENT_TEXTCHANGEDT, "TextChangedT"),
194 KEYVALUE_ENTRY(EVENT_TEXTYANKPOST, "TextYankPost"),
195 KEYVALUE_ENTRY(EVENT_USER, "User"),
196 KEYVALUE_ENTRY(EVENT_VIMENTER, "VimEnter"),
197 KEYVALUE_ENTRY(EVENT_VIMLEAVE, "VimLeave"),
198 KEYVALUE_ENTRY(EVENT_VIMLEAVEPRE, "VimLeavePre"),
199 KEYVALUE_ENTRY(EVENT_VIMRESIZED, "VimResized"),
200 KEYVALUE_ENTRY(EVENT_VIMRESUME, "VimResume"),
201 KEYVALUE_ENTRY(EVENT_VIMSUSPEND, "VimSuspend"),
202 KEYVALUE_ENTRY(EVENT_WINCLOSED, "WinClosed"),
203 KEYVALUE_ENTRY(EVENT_WINENTER, "WinEnter"),
204 KEYVALUE_ENTRY(EVENT_WINLEAVE, "WinLeave"),
205 KEYVALUE_ENTRY(EVENT_WINNEW, "WinNew"),
206 KEYVALUE_ENTRY(EVENT_WINNEWPRE, "WinNewPre"),
207 KEYVALUE_ENTRY(EVENT_WINRESIZED, "WinResized"),
208 KEYVALUE_ENTRY(EVENT_WINSCROLLED, "WinScrolled")
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100209};
210
John Marriott78d742a2024-04-02 20:26:01 +0200211static AutoPat *first_autopat[NUM_EVENTS] = {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100212 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
213 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
214 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
215 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
216 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
John Marriott78d742a2024-04-02 20:26:01 +0200217 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
218 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
219 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
220 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
221 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
222 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
223 NULL, NULL, NULL, NULL, NULL, NULL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100224};
225
John Marriott78d742a2024-04-02 20:26:01 +0200226static AutoPat *last_autopat[NUM_EVENTS] = {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100227 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
228 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
229 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
230 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
231 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
John Marriott78d742a2024-04-02 20:26:01 +0200232 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
233 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
234 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
235 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
236 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
237 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
238 NULL, NULL, NULL, NULL, NULL, NULL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100239};
240
kylo252ae6f1d82022-02-16 19:24:07 +0000241#define AUGROUP_DEFAULT (-1) // default autocmd group
242#define AUGROUP_ERROR (-2) // erroneous autocmd group
243#define AUGROUP_ALL (-3) // all autocmd groups
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100244
245/*
246 * struct used to keep status while executing autocommands for an event.
247 */
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100248struct AutoPatCmd_S
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100249{
250 AutoPat *curpat; // next AutoPat to examine
251 AutoCmd *nextcmd; // next AutoCmd to execute
252 int group; // group being used
253 char_u *fname; // fname to match with
254 char_u *sfname; // sfname to match with
255 char_u *tail; // tail of fname
256 event_T event; // current event
LemonBoyeca7c602022-04-14 15:39:43 +0100257 sctx_T script_ctx; // script context where it is defined
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100258 int arg_bufnr; // Initially equal to <abuf>, set to zero when
259 // buf is deleted.
LemonBoyeca7c602022-04-14 15:39:43 +0100260 AutoPatCmd_T *next; // chain of active apc-s for auto-invalidation
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100261};
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100262
LemonBoyeca7c602022-04-14 15:39:43 +0100263static AutoPatCmd_T *active_apc_list = NULL; // stack of active autocommands
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100264
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200265// Macro to loop over all the patterns for an autocmd event
266#define FOR_ALL_AUTOCMD_PATTERNS(event, ap) \
267 for ((ap) = first_autopat[(int)(event)]; (ap) != NULL; (ap) = (ap)->next)
268
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100269/*
270 * augroups stores a list of autocmd group names.
271 */
272static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
273#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
Bram Moolenaarc667da52019-11-30 20:52:27 +0100274// use get_deleted_augroup() to get this
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100275static char_u *deleted_augroup = NULL;
276
277/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100278 * The ID of the current group. Group 0 is the default one.
279 */
280static int current_augroup = AUGROUP_DEFAULT;
281
Bram Moolenaarc667da52019-11-30 20:52:27 +0100282static int au_need_clean = FALSE; // need to delete marked patterns
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100283
John Marriott78d742a2024-04-02 20:26:01 +0200284static event_T event_name2nr(char_u *start, char_u **end);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100285static char_u *event_nr2name(event_T event);
286static int au_get_grouparg(char_u **argp);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200287static 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 +0100288static 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 +0100289static void auto_next_pat(AutoPatCmd_T *apc, int stop_at_last);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100290static int au_find_group(char_u *name);
291
292static event_T last_event;
293static int last_group;
Bram Moolenaarc667da52019-11-30 20:52:27 +0100294static int autocmd_blocked = 0; // block all autocmds
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100295
296 static char_u *
297get_deleted_augroup(void)
298{
299 if (deleted_augroup == NULL)
300 deleted_augroup = (char_u *)_("--Deleted--");
301 return deleted_augroup;
302}
303
304/*
305 * Show the autocommands for one AutoPat.
306 */
307 static void
308show_autocmd(AutoPat *ap, event_T event)
309{
310 AutoCmd *ac;
311
312 // Check for "got_int" (here and at various places below), which is set
313 // when "q" has been hit for the "--more--" prompt
314 if (got_int)
315 return;
316 if (ap->pat == NULL) // pattern has been removed
317 return;
318
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000319 // Make sure no info referenced by "ap" is cleared, e.g. when a timer
320 // clears an augroup. Jump to "theend" after this!
321 // "ap->pat" may be cleared anyway.
322 ++autocmd_busy;
323
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100324 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 if (event != last_event || ap->group != last_group)
328 {
329 if (ap->group != AUGROUP_DEFAULT)
330 {
331 if (AUGROUP_NAME(ap->group) == NULL)
332 msg_puts_attr((char *)get_deleted_augroup(), HL_ATTR(HLF_E));
333 else
334 msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
335 msg_puts(" ");
336 }
337 msg_puts_attr((char *)event_nr2name(event), HL_ATTR(HLF_T));
338 last_event = event;
339 last_group = ap->group;
340 msg_putchar('\n');
341 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000342 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100343 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000344
345 if (ap->pat == NULL)
346 goto theend; // timer might have cleared the pattern or group
347
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100348 msg_col = 4;
349 msg_outtrans(ap->pat);
350
351 for (ac = ap->cmds; ac != NULL; ac = ac->next)
352 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100353 if (ac->cmd == NULL) // skip removed commands
354 continue;
355
356 if (msg_col >= 14)
357 msg_putchar('\n');
358 msg_col = 14;
359 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000360 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100361 msg_outtrans(ac->cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100362#ifdef FEAT_EVAL
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100363 if (p_verbose > 0)
364 last_set_msg(ac->script_ctx);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100365#endif
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100366 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000367 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100368 if (ac->next != NULL)
369 {
370 msg_putchar('\n');
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100371 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000372 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100373 }
374 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000375
376theend:
377 --autocmd_busy;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100378}
379
380/*
381 * Mark an autocommand pattern for deletion.
382 */
383 static void
384au_remove_pat(AutoPat *ap)
385{
386 VIM_CLEAR(ap->pat);
387 ap->buflocal_nr = -1;
388 au_need_clean = TRUE;
389}
390
391/*
392 * Mark all commands for a pattern for deletion.
393 */
394 static void
395au_remove_cmds(AutoPat *ap)
396{
397 AutoCmd *ac;
398
399 for (ac = ap->cmds; ac != NULL; ac = ac->next)
400 VIM_CLEAR(ac->cmd);
401 au_need_clean = TRUE;
402}
403
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200404// Delete one command from an autocmd pattern.
405static void au_del_cmd(AutoCmd *ac)
406{
407 VIM_CLEAR(ac->cmd);
408 au_need_clean = TRUE;
409}
410
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100411/*
412 * Cleanup autocommands and patterns that have been deleted.
413 * This is only done when not executing autocommands.
414 */
415 static void
416au_cleanup(void)
417{
418 AutoPat *ap, **prev_ap;
419 AutoCmd *ac, **prev_ac;
420 event_T event;
421
422 if (autocmd_busy || !au_need_clean)
423 return;
424
425 // loop over all events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100426 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100427 event = (event_T)((int)event + 1))
428 {
429 // loop over all autocommand patterns
430 prev_ap = &(first_autopat[(int)event]);
431 for (ap = *prev_ap; ap != NULL; ap = *prev_ap)
432 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200433 int has_cmd = FALSE;
434
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200435 // loop over all commands for this pattern
436 prev_ac = &(ap->cmds);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100437 for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
438 {
439 // remove the command if the pattern is to be deleted or when
440 // the command has been marked for deletion
441 if (ap->pat == NULL || ac->cmd == NULL)
442 {
443 *prev_ac = ac->next;
444 vim_free(ac->cmd);
445 vim_free(ac);
446 }
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200447 else
448 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200449 has_cmd = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100450 prev_ac = &(ac->next);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200451 }
452 }
453
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200454 if (ap->pat != NULL && !has_cmd)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200455 // Pattern was not marked for deletion, but all of its
456 // commands were. So mark the pattern for deletion.
457 au_remove_pat(ap);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100458
459 // remove the pattern if it has been marked for deletion
460 if (ap->pat == NULL)
461 {
462 if (ap->next == NULL)
463 {
464 if (prev_ap == &(first_autopat[(int)event]))
465 last_autopat[(int)event] = NULL;
466 else
467 // this depends on the "next" field being the first in
468 // the struct
469 last_autopat[(int)event] = (AutoPat *)prev_ap;
470 }
471 *prev_ap = ap->next;
472 vim_regfree(ap->reg_prog);
473 vim_free(ap);
474 }
475 else
476 prev_ap = &(ap->next);
477 }
478 }
479
480 au_need_clean = FALSE;
481}
482
483/*
484 * Called when buffer is freed, to remove/invalidate related buffer-local
485 * autocmds.
486 */
487 void
488aubuflocal_remove(buf_T *buf)
489{
LemonBoyeca7c602022-04-14 15:39:43 +0100490 AutoPat *ap;
491 event_T event;
492 AutoPatCmd_T *apc;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100493
494 // invalidate currently executing autocommands
495 for (apc = active_apc_list; apc; apc = apc->next)
496 if (buf->b_fnum == apc->arg_bufnr)
497 apc->arg_bufnr = 0;
498
499 // invalidate buflocals looping through events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100500 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100501 event = (event_T)((int)event + 1))
502 // loop over all autocommand patterns
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200503 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100504 if (ap->buflocal_nr == buf->b_fnum)
505 {
506 au_remove_pat(ap);
507 if (p_verbose >= 6)
508 {
509 verbose_enter();
510 smsg(_("auto-removing autocommand: %s <buffer=%d>"),
511 event_nr2name(event), buf->b_fnum);
512 verbose_leave();
513 }
514 }
515 au_cleanup();
516}
517
518/*
519 * Add an autocmd group name.
520 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
521 */
522 static int
523au_new_group(char_u *name)
524{
525 int i;
526
527 i = au_find_group(name);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100528 if (i != AUGROUP_ERROR)
529 return i;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100530
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100531 // the group doesn't exist yet, add it. First try using a free entry.
532 for (i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100533 if (AUGROUP_NAME(i) == NULL)
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100534 break;
535 if (i == augroups.ga_len && ga_grow(&augroups, 1) == FAIL)
536 return AUGROUP_ERROR;
537
538 AUGROUP_NAME(i) = vim_strsave(name);
539 if (AUGROUP_NAME(i) == NULL)
540 return AUGROUP_ERROR;
541 if (i == augroups.ga_len)
542 ++augroups.ga_len;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100543
544 return i;
545}
546
547 static void
548au_del_group(char_u *name)
549{
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100550 int i;
551 event_T event;
552 AutoPat *ap;
553 int in_use = FALSE;
554
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100555
556 i = au_find_group(name);
557 if (i == AUGROUP_ERROR) // the group doesn't exist
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100558 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100559 semsg(_(e_no_such_group_str), name);
560 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100561 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100562 if (i == current_augroup)
563 {
564 emsg(_(e_cannot_delete_current_group));
565 return;
566 }
567
568 for (event = (event_T)0; (int)event < NUM_EVENTS;
569 event = (event_T)((int)event + 1))
570 {
571 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
572 if (ap->group == i && ap->pat != NULL)
573 {
574 give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
575 in_use = TRUE;
576 event = NUM_EVENTS;
577 break;
578 }
579 }
580 vim_free(AUGROUP_NAME(i));
581 if (in_use)
582 AUGROUP_NAME(i) = get_deleted_augroup();
583 else
584 AUGROUP_NAME(i) = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100585}
586
587/*
588 * Find the ID of an autocmd group name.
589 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
590 */
591 static int
592au_find_group(char_u *name)
593{
594 int i;
595
596 for (i = 0; i < augroups.ga_len; ++i)
597 if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
598 && STRCMP(AUGROUP_NAME(i), name) == 0)
599 return i;
600 return AUGROUP_ERROR;
601}
602
603/*
604 * Return TRUE if augroup "name" exists.
605 */
606 int
607au_has_group(char_u *name)
608{
609 return au_find_group(name) != AUGROUP_ERROR;
610}
611
612/*
613 * ":augroup {name}".
614 */
615 void
616do_augroup(char_u *arg, int del_group)
617{
618 int i;
619
620 if (del_group)
621 {
622 if (*arg == NUL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000623 emsg(_(e_argument_required));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100624 else
625 au_del_group(arg);
626 }
627 else if (STRICMP(arg, "end") == 0) // ":aug end": back to group 0
628 current_augroup = AUGROUP_DEFAULT;
629 else if (*arg) // ":aug xxx": switch to group xxx
630 {
631 i = au_new_group(arg);
632 if (i != AUGROUP_ERROR)
633 current_augroup = i;
634 }
635 else // ":aug": list the group names
636 {
637 msg_start();
638 for (i = 0; i < augroups.ga_len; ++i)
639 {
640 if (AUGROUP_NAME(i) != NULL)
641 {
642 msg_puts((char *)AUGROUP_NAME(i));
643 msg_puts(" ");
644 }
645 }
646 msg_clr_eos();
647 msg_end();
648 }
649}
650
Bram Moolenaare76062c2022-11-28 18:51:43 +0000651 void
652autocmd_init(void)
653{
654 CLEAR_FIELD(aucmd_win);
655}
656
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100657#if defined(EXITFREE) || defined(PROTO)
658 void
659free_all_autocmds(void)
660{
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100661 char_u *s;
662
663 for (current_augroup = -1; current_augroup < augroups.ga_len;
664 ++current_augroup)
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200665 do_autocmd(NULL, (char_u *)"", TRUE);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100666
Bram Moolenaare76062c2022-11-28 18:51:43 +0000667 for (int i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100668 {
669 s = ((char_u **)(augroups.ga_data))[i];
670 if (s != get_deleted_augroup())
671 vim_free(s);
672 }
673 ga_clear(&augroups);
Bram Moolenaare76062c2022-11-28 18:51:43 +0000674
Bram Moolenaar84497cd2022-11-28 20:34:52 +0000675 // aucmd_win[] is freed in win_free_all()
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100676}
677#endif
678
679/*
Bram Moolenaare76062c2022-11-28 18:51:43 +0000680 * Return TRUE if "win" is an active entry in aucmd_win[].
681 */
682 int
683is_aucmd_win(win_T *win)
684{
685 for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
686 if (aucmd_win[i].auc_win_used && aucmd_win[i].auc_win == win)
687 return TRUE;
688 return FALSE;
689}
690
691/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100692 * Return the event number for event name "start".
693 * Return NUM_EVENTS if the event name was not found.
694 * Return a pointer to the next event name in "end".
695 */
696 static event_T
697event_name2nr(char_u *start, char_u **end)
698{
699 char_u *p;
John Marriott78d742a2024-04-02 20:26:01 +0200700 keyvalue_T target;
701 keyvalue_T *entry;
702 static keyvalue_T *bufnewfile = &event_tab[BUFNEWFILE_INDEX];
703 static keyvalue_T *bufread = &event_tab[BUFREAD_INDEX];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100704
705 // the event name ends with end of line, '|', a blank or a comma
706 for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
707 ;
John Marriott78d742a2024-04-02 20:26:01 +0200708
709 target.key = 0;
710 target.value = (char *)start;
711 target.length = (size_t)(p - start);
712
713 // special cases:
714 // BufNewFile and BufRead are searched for ALOT (especially at startup)
715 // so we check for them first.
716 if (cmp_keyvalue_value_ni(&target, bufnewfile) == 0)
717 entry = bufnewfile;
718 else
719 if (cmp_keyvalue_value_ni(&target, bufread) == 0)
720 entry = bufread;
721 else
722 entry = (keyvalue_T *)bsearch(&target, &event_tab, ARRAY_LENGTH(event_tab), sizeof(event_tab[0]), cmp_keyvalue_value_ni);
723
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100724 if (*p == ',')
725 ++p;
726 *end = p;
John Marriott78d742a2024-04-02 20:26:01 +0200727
728 return (entry == NULL) ? NUM_EVENTS : (event_T)entry->key;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100729}
730
731/*
732 * Return the name for event "event".
733 */
734 static char_u *
735event_nr2name(event_T event)
736{
737 int i;
John Marriott78d742a2024-04-02 20:26:01 +0200738#define CACHE_SIZE 12
739 static int cache_tab[CACHE_SIZE];
740 static int cache_last_index = -1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100741
John Marriott78d742a2024-04-02 20:26:01 +0200742 if (cache_last_index < 0)
743 {
744 for (i = 0; i < (int)ARRAY_LENGTH(cache_tab); ++i)
745 cache_tab[i] = -1;
746 cache_last_index = ARRAY_LENGTH(cache_tab) - 1;
747 }
748
749 // first look in the cache
750 // the cache is circular. to search it we start at the most recent entry
751 // and go backwards wrapping around when we get to index 0.
752 for (i = cache_last_index; cache_tab[i] >= 0; )
753 {
754 if ((event_T)event_tab[cache_tab[i]].key == event)
755 return (char_u *)event_tab[cache_tab[i]].value;
756
757 if (i == 0)
758 i = ARRAY_LENGTH(cache_tab) - 1;
759 else
760 --i;
761
762 // are we back at the start?
763 if (i == cache_last_index)
764 break;
765 }
766
767 // look in the event table itself
768 for (i = 0; i < (int)ARRAY_LENGTH(event_tab); ++i)
769 {
770 if ((event_T)event_tab[i].key == event)
771 {
772 // store the found entry in the next position in the cache,
773 // wrapping around when we get to the maximum index.
774 if (cache_last_index == ARRAY_LENGTH(cache_tab) - 1)
775 cache_last_index = 0;
776 else
777 ++cache_last_index;
778 cache_tab[cache_last_index] = i;
779 break;
780 }
781 }
782
783 return (i == (int)ARRAY_LENGTH(event_tab)) ? (char_u *)"Unknown" : (char_u *)event_tab[i].value;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100784}
785
786/*
787 * Scan over the events. "*" stands for all events.
788 */
789 static char_u *
790find_end_event(
791 char_u *arg,
792 int have_group) // TRUE when group name was found
793{
794 char_u *pat;
795 char_u *p;
796
797 if (*arg == '*')
798 {
799 if (arg[1] && !VIM_ISWHITE(arg[1]))
800 {
Bram Moolenaar6d057012021-12-31 18:49:43 +0000801 semsg(_(e_illegal_character_after_star_str), arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100802 return NULL;
803 }
804 pat = arg + 1;
805 }
806 else
807 {
808 for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p)
809 {
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100810 if ((int)event_name2nr(pat, &p) >= NUM_EVENTS)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100811 {
812 if (have_group)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000813 semsg(_(e_no_such_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100814 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000815 semsg(_(e_no_such_group_or_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100816 return NULL;
817 }
818 }
819 }
820 return pat;
821}
822
823/*
824 * Return TRUE if "event" is included in 'eventignore'.
825 */
826 static int
827event_ignored(event_T event)
828{
829 char_u *p = p_ei;
830
831 while (*p != NUL)
832 {
833 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
834 return TRUE;
835 if (event_name2nr(p, &p) == event)
836 return TRUE;
837 }
838
839 return FALSE;
840}
841
842/*
843 * Return OK when the contents of p_ei is valid, FAIL otherwise.
844 */
845 int
846check_ei(void)
847{
848 char_u *p = p_ei;
849
850 while (*p)
851 {
852 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
853 {
854 p += 3;
855 if (*p == ',')
856 ++p;
857 }
858 else if (event_name2nr(p, &p) == NUM_EVENTS)
859 return FAIL;
860 }
861
862 return OK;
863}
864
865# if defined(FEAT_SYN_HL) || defined(PROTO)
866
867/*
868 * Add "what" to 'eventignore' to skip loading syntax highlighting for every
869 * buffer loaded into the window. "what" must start with a comma.
870 * Returns the old value of 'eventignore' in allocated memory.
871 */
872 char_u *
873au_event_disable(char *what)
874{
875 char_u *new_ei;
876 char_u *save_ei;
John Marriott78d742a2024-04-02 20:26:01 +0200877 size_t p_ei_len;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100878
John Marriott78d742a2024-04-02 20:26:01 +0200879 p_ei_len = STRLEN(p_ei);
880 save_ei = vim_strnsave(p_ei, p_ei_len);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100881 if (save_ei == NULL)
882 return NULL;
883
John Marriott78d742a2024-04-02 20:26:01 +0200884 new_ei = vim_strnsave(p_ei, p_ei_len + STRLEN(what));
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100885 if (new_ei == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100886 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100887 vim_free(save_ei);
888 return NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100889 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100890
891 if (*what == ',' && *p_ei == NUL)
892 STRCPY(new_ei, what + 1);
893 else
894 STRCAT(new_ei, what);
895 set_string_option_direct((char_u *)"ei", -1, new_ei,
896 OPT_FREE, SID_NONE);
897 vim_free(new_ei);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100898 return save_ei;
899}
900
901 void
902au_event_restore(char_u *old_ei)
903{
904 if (old_ei != NULL)
905 {
906 set_string_option_direct((char_u *)"ei", -1, old_ei,
907 OPT_FREE, SID_NONE);
908 vim_free(old_ei);
909 }
910}
Bram Moolenaarc667da52019-11-30 20:52:27 +0100911# endif // FEAT_SYN_HL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100912
913/*
914 * do_autocmd() -- implements the :autocmd command. Can be used in the
915 * following ways:
916 *
917 * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
918 * will be automatically executed for <event>
919 * when editing a file matching <pat>, in
920 * the current group.
921 * :autocmd <event> <pat> Show the autocommands associated with
922 * <event> and <pat>.
923 * :autocmd <event> Show the autocommands associated with
924 * <event>.
925 * :autocmd Show all autocommands.
926 * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
927 * <event> and <pat>, and add the command
928 * <cmd>, for the current group.
929 * :autocmd! <event> <pat> Remove all autocommands associated with
930 * <event> and <pat> for the current group.
931 * :autocmd! <event> Remove all autocommands associated with
932 * <event> for the current group.
933 * :autocmd! Remove ALL autocommands for the current
934 * group.
935 *
936 * Multiple events and patterns may be given separated by commas. Here are
937 * some examples:
938 * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
939 * :autocmd bufleave * set tw=79 nosmartindent ic infercase
940 *
941 * :autocmd * *.c show all autocommands for *.c files.
942 *
943 * Mostly a {group} argument can optionally appear before <event>.
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200944 * "eap" can be NULL.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100945 */
946 void
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200947do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100948{
949 char_u *arg = arg_in;
950 char_u *pat;
951 char_u *envpat = NULL;
952 char_u *cmd;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200953 int cmd_need_free = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100954 event_T event;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200955 char_u *tofree = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100956 int nested = FALSE;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200957 int once = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100958 int group;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200959 int i;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200960 int flags = 0;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100961
962 if (*arg == '|')
963 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000964 eap->nextcmd = arg + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100965 arg = (char_u *)"";
966 group = AUGROUP_ALL; // no argument, use all groups
967 }
968 else
969 {
970 /*
971 * Check for a legal group name. If not, use AUGROUP_ALL.
972 */
973 group = au_get_grouparg(&arg);
974 if (arg == NULL) // out of memory
975 return;
976 }
977
978 /*
979 * Scan over the events.
980 * If we find an illegal name, return here, don't do anything.
981 */
982 pat = find_end_event(arg, group != AUGROUP_ALL);
983 if (pat == NULL)
984 return;
985
986 pat = skipwhite(pat);
987 if (*pat == '|')
988 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000989 eap->nextcmd = pat + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100990 pat = (char_u *)"";
991 cmd = (char_u *)"";
992 }
993 else
994 {
995 /*
996 * Scan over the pattern. Put a NUL at the end.
997 */
998 cmd = pat;
999 while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
1000 cmd++;
1001 if (*cmd)
1002 *cmd++ = NUL;
1003
1004 // Expand environment variables in the pattern. Set 'shellslash', we
1005 // want forward slashes here.
1006 if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
1007 {
1008#ifdef BACKSLASH_IN_FILENAME
1009 int p_ssl_save = p_ssl;
1010
1011 p_ssl = TRUE;
1012#endif
1013 envpat = expand_env_save(pat);
1014#ifdef BACKSLASH_IN_FILENAME
1015 p_ssl = p_ssl_save;
1016#endif
1017 if (envpat != NULL)
1018 pat = envpat;
1019 }
1020
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001021 cmd = skipwhite(cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001022 for (i = 0; i < 2; i++)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001023 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001024 if (*cmd == NUL)
1025 continue;
1026
1027 // Check for "++once" flag.
1028 if (STRNCMP(cmd, "++once", 6) == 0 && VIM_ISWHITE(cmd[6]))
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001029 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001030 if (once)
1031 semsg(_(e_duplicate_argument_str), "++once");
1032 once = TRUE;
1033 cmd = skipwhite(cmd + 6);
1034 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001035
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001036 // Check for "++nested" flag.
1037 if ((STRNCMP(cmd, "++nested", 8) == 0 && VIM_ISWHITE(cmd[8])))
1038 {
1039 if (nested)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001040 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001041 semsg(_(e_duplicate_argument_str), "++nested");
1042 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001043 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001044 nested = TRUE;
1045 cmd = skipwhite(cmd + 8);
1046 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001047
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001048 // Check for the old "nested" flag in legacy script.
1049 if (STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
1050 {
1051 if (in_vim9script())
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001052 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001053 // If there ever is a :nested command this error should
1054 // be removed and "nested" accepted as the start of the
1055 // command.
1056 emsg(_(e_invalid_command_nested_did_you_mean_plusplus_nested));
1057 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001058 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001059 if (nested)
1060 {
1061 semsg(_(e_duplicate_argument_str), "nested");
1062 return;
1063 }
1064 nested = TRUE;
1065 cmd = skipwhite(cmd + 6);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001066 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001067 }
1068
1069 /*
1070 * Find the start of the commands.
1071 * Expand <sfile> in it.
1072 */
1073 if (*cmd != NUL)
1074 {
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001075 if (eap != NULL)
1076 // Read a {} block if it follows.
1077 cmd = may_get_cmd_block(eap, cmd, &tofree, &flags);
1078
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001079 cmd = expand_sfile(cmd);
1080 if (cmd == NULL) // some error
1081 return;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001082 cmd_need_free = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001083 }
1084 }
1085
1086 /*
1087 * Print header when showing autocommands.
1088 */
1089 if (!forceit && *cmd == NUL)
1090 // Highlight title
1091 msg_puts_title(_("\n--- Autocommands ---"));
1092
1093 /*
1094 * Loop over the events.
1095 */
1096 last_event = (event_T)-1; // for listing the event name
1097 last_group = AUGROUP_ERROR; // for listing the group name
1098 if (*arg == '*' || *arg == NUL || *arg == '|')
1099 {
Bram Moolenaarb6db1462021-12-24 19:24:47 +00001100 if (*cmd != NUL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001101 emsg(_(e_cannot_define_autocommands_for_all_events));
1102 else
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +01001103 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001104 event = (event_T)((int)event + 1))
1105 if (do_autocmd_event(event, pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001106 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001107 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001108 }
1109 else
1110 {
1111 while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
1112 if (do_autocmd_event(event_name2nr(arg, &arg), pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001113 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001114 break;
1115 }
1116
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001117 if (cmd_need_free)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001118 vim_free(cmd);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001119 vim_free(tofree);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001120 vim_free(envpat);
1121}
1122
1123/*
1124 * Find the group ID in a ":autocmd" or ":doautocmd" argument.
1125 * The "argp" argument is advanced to the following argument.
1126 *
1127 * Returns the group ID, AUGROUP_ERROR for error (out of memory).
1128 */
1129 static int
1130au_get_grouparg(char_u **argp)
1131{
1132 char_u *group_name;
1133 char_u *p;
1134 char_u *arg = *argp;
1135 int group = AUGROUP_ALL;
1136
1137 for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p)
1138 ;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001139 if (p <= arg)
1140 return AUGROUP_ALL;
1141
1142 group_name = vim_strnsave(arg, p - arg);
1143 if (group_name == NULL) // out of memory
1144 return AUGROUP_ERROR;
1145 group = au_find_group(group_name);
1146 if (group == AUGROUP_ERROR)
1147 group = AUGROUP_ALL; // no match, use all groups
1148 else
1149 *argp = skipwhite(p); // match, skip over group name
1150 vim_free(group_name);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001151 return group;
1152}
1153
1154/*
1155 * do_autocmd() for one event.
1156 * If *pat == NUL do for all patterns.
1157 * If *cmd == NUL show entries.
1158 * If forceit == TRUE delete entries.
1159 * If group is not AUGROUP_ALL, only use this group.
1160 */
1161 static int
1162do_autocmd_event(
1163 event_T event,
1164 char_u *pat,
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001165 int once,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001166 int nested,
1167 char_u *cmd,
1168 int forceit,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001169 int group,
1170 int flags)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001171{
1172 AutoPat *ap;
1173 AutoPat **prev_ap;
1174 AutoCmd *ac;
1175 AutoCmd **prev_ac;
1176 int brace_level;
1177 char_u *endpat;
1178 int findgroup;
1179 int allgroups;
1180 int patlen;
1181 int is_buflocal;
1182 int buflocal_nr;
Bram Moolenaarc667da52019-11-30 20:52:27 +01001183 char_u buflocal_pat[25]; // for "<buffer=X>"
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001184
1185 if (group == AUGROUP_ALL)
1186 findgroup = current_augroup;
1187 else
1188 findgroup = group;
1189 allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
1190
1191 /*
1192 * Show or delete all patterns for an event.
1193 */
1194 if (*pat == NUL)
1195 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001196 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001197 {
1198 if (forceit) // delete the AutoPat, if it's in the current group
1199 {
1200 if (ap->group == findgroup)
1201 au_remove_pat(ap);
1202 }
1203 else if (group == AUGROUP_ALL || ap->group == group)
1204 show_autocmd(ap, event);
1205 }
1206 }
1207
1208 /*
1209 * Loop through all the specified patterns.
1210 */
1211 for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
1212 {
1213 /*
1214 * Find end of the pattern.
1215 * Watch out for a comma in braces, like "*.\{obj,o\}".
1216 */
1217 brace_level = 0;
1218 for (endpat = pat; *endpat && (*endpat != ',' || brace_level
1219 || (endpat > pat && endpat[-1] == '\\')); ++endpat)
1220 {
1221 if (*endpat == '{')
1222 brace_level++;
1223 else if (*endpat == '}')
1224 brace_level--;
1225 }
1226 if (pat == endpat) // ignore single comma
1227 continue;
1228 patlen = (int)(endpat - pat);
1229
1230 /*
1231 * detect special <buflocal[=X]> buffer-local patterns
1232 */
1233 is_buflocal = FALSE;
1234 buflocal_nr = 0;
1235
1236 if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
1237 && pat[patlen - 1] == '>')
1238 {
1239 // "<buffer...>": Error will be printed only for addition.
1240 // printing and removing will proceed silently.
1241 is_buflocal = TRUE;
1242 if (patlen == 8)
1243 // "<buffer>"
1244 buflocal_nr = curbuf->b_fnum;
1245 else if (patlen > 9 && pat[7] == '=')
1246 {
1247 if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0)
1248 // "<buffer=abuf>"
1249 buflocal_nr = autocmd_bufnr;
1250 else if (skipdigits(pat + 8) == pat + patlen - 1)
1251 // "<buffer=123>"
1252 buflocal_nr = atoi((char *)pat + 8);
1253 }
1254 }
1255
1256 if (is_buflocal)
1257 {
1258 // normalize pat into standard "<buffer>#N" form
1259 sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
1260 pat = buflocal_pat; // can modify pat and patlen
1261 patlen = (int)STRLEN(buflocal_pat); // but not endpat
1262 }
1263
1264 /*
1265 * Find AutoPat entries with this pattern. When adding a command it
1266 * always goes at or after the last one, so start at the end.
1267 */
1268 if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL)
1269 prev_ap = &last_autopat[(int)event];
1270 else
1271 prev_ap = &first_autopat[(int)event];
1272 while ((ap = *prev_ap) != NULL)
1273 {
1274 if (ap->pat != NULL)
1275 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001276 /*
1277 * Accept a pattern when:
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001278 * - a group was specified and it's that group, or a group was
1279 * not specified and it's the current group, or a group was
1280 * not specified and we are listing
1281 * - the length of the pattern matches
1282 * - the pattern matches.
1283 * For <buffer[=X]>, this condition works because we normalize
1284 * all buffer-local patterns.
1285 */
1286 if ((allgroups || ap->group == findgroup)
1287 && ap->patlen == patlen
1288 && STRNCMP(pat, ap->pat, patlen) == 0)
1289 {
1290 /*
1291 * Remove existing autocommands.
1292 * If adding any new autocmd's for this AutoPat, don't
1293 * delete the pattern from the autopat list, append to
1294 * this list.
1295 */
1296 if (forceit)
1297 {
1298 if (*cmd != NUL && ap->next == NULL)
1299 {
1300 au_remove_cmds(ap);
1301 break;
1302 }
1303 au_remove_pat(ap);
1304 }
1305
1306 /*
1307 * Show autocmd's for this autopat, or buflocals <buffer=X>
1308 */
1309 else if (*cmd == NUL)
1310 show_autocmd(ap, event);
1311
1312 /*
1313 * Add autocmd to this autopat, if it's the last one.
1314 */
1315 else if (ap->next == NULL)
1316 break;
1317 }
1318 }
1319 prev_ap = &ap->next;
1320 }
1321
1322 /*
1323 * Add a new command.
1324 */
1325 if (*cmd != NUL)
1326 {
1327 /*
1328 * If the pattern we want to add a command to does appear at the
1329 * end of the list (or not is not in the list at all), add the
1330 * pattern at the end of the list.
1331 */
1332 if (ap == NULL)
1333 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001334 // refuse to add buffer-local ap if buffer number is invalid
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001335 if (is_buflocal && (buflocal_nr == 0
1336 || buflist_findnr(buflocal_nr) == NULL))
1337 {
Bram Moolenaarf1474d82021-12-31 19:59:55 +00001338 semsg(_(e_buffer_nr_invalid_buffer_number), buflocal_nr);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001339 return FAIL;
1340 }
1341
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001342 ap = ALLOC_ONE(AutoPat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001343 if (ap == NULL)
1344 return FAIL;
1345 ap->pat = vim_strnsave(pat, patlen);
1346 ap->patlen = patlen;
1347 if (ap->pat == NULL)
1348 {
1349 vim_free(ap);
1350 return FAIL;
1351 }
1352
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001353#ifdef FEAT_EVAL
1354 // need to initialize last_mode for the first ModeChanged
1355 // autocmd
1356 if (event == EVENT_MODECHANGED && !has_modechanged())
LemonBoy2bf52dd2022-04-09 18:17:34 +01001357 get_mode(last_mode);
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001358#endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001359 // Initialize the fields checked by the WinScrolled and
1360 // WinResized trigger to prevent them from firing right after
1361 // the first autocmd is defined.
1362 if ((event == EVENT_WINSCROLLED || event == EVENT_WINRESIZED)
1363 && !(has_winscrolled() || has_winresized()))
LemonBoy09371822022-04-08 15:18:45 +01001364 {
Bram Moolenaar29967732022-11-20 12:11:45 +00001365 tabpage_T *save_curtab = curtab;
1366 tabpage_T *tp;
1367 FOR_ALL_TABPAGES(tp)
1368 {
1369 unuse_tabpage(curtab);
1370 use_tabpage(tp);
1371 snapshot_windows_scroll_size();
1372 }
1373 unuse_tabpage(curtab);
1374 use_tabpage(save_curtab);
LemonBoy09371822022-04-08 15:18:45 +01001375 }
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001376
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001377 if (is_buflocal)
1378 {
1379 ap->buflocal_nr = buflocal_nr;
1380 ap->reg_prog = NULL;
1381 }
1382 else
1383 {
1384 char_u *reg_pat;
1385
1386 ap->buflocal_nr = 0;
1387 reg_pat = file_pat_to_reg_pat(pat, endpat,
1388 &ap->allow_dirs, TRUE);
1389 if (reg_pat != NULL)
1390 ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
1391 vim_free(reg_pat);
1392 if (reg_pat == NULL || ap->reg_prog == NULL)
1393 {
1394 vim_free(ap->pat);
1395 vim_free(ap);
1396 return FAIL;
1397 }
1398 }
1399 ap->cmds = NULL;
1400 *prev_ap = ap;
1401 last_autopat[(int)event] = ap;
1402 ap->next = NULL;
1403 if (group == AUGROUP_ALL)
1404 ap->group = current_augroup;
1405 else
1406 ap->group = group;
1407 }
1408
1409 /*
1410 * Add the autocmd at the end of the AutoCmd list.
1411 */
1412 prev_ac = &(ap->cmds);
1413 while ((ac = *prev_ac) != NULL)
1414 prev_ac = &ac->next;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001415 ac = ALLOC_ONE(AutoCmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001416 if (ac == NULL)
1417 return FAIL;
1418 ac->cmd = vim_strsave(cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001419 ac->script_ctx = current_sctx;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001420 if (flags & UC_VIM9)
1421 ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001422#ifdef FEAT_EVAL
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001423 ac->script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001424#endif
1425 if (ac->cmd == NULL)
1426 {
1427 vim_free(ac);
1428 return FAIL;
1429 }
1430 ac->next = NULL;
1431 *prev_ac = ac;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001432 ac->once = once;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001433 ac->nested = nested;
1434 }
1435 }
1436
1437 au_cleanup(); // may really delete removed patterns/commands now
1438 return OK;
1439}
1440
1441/*
1442 * Implementation of ":doautocmd [group] event [fname]".
1443 * Return OK for success, FAIL for failure;
1444 */
1445 int
1446do_doautocmd(
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001447 char_u *arg_start,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001448 int do_msg, // give message for no matching autocmds?
1449 int *did_something)
1450{
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001451 char_u *arg = arg_start;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001452 char_u *fname;
1453 int nothing_done = TRUE;
1454 int group;
1455
1456 if (did_something != NULL)
1457 *did_something = FALSE;
1458
1459 /*
1460 * Check for a legal group name. If not, use AUGROUP_ALL.
1461 */
1462 group = au_get_grouparg(&arg);
1463 if (arg == NULL) // out of memory
1464 return FAIL;
1465
1466 if (*arg == '*')
1467 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001468 emsg(_(e_cant_execute_autocommands_for_all_events));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001469 return FAIL;
1470 }
1471
1472 /*
1473 * Scan over the events.
1474 * If we find an illegal name, return here, don't do anything.
1475 */
1476 fname = find_end_event(arg, group != AUGROUP_ALL);
1477 if (fname == NULL)
1478 return FAIL;
1479
1480 fname = skipwhite(fname);
1481
1482 /*
1483 * Loop over the events.
1484 */
1485 while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
1486 if (apply_autocmds_group(event_name2nr(arg, &arg),
1487 fname, NULL, TRUE, group, curbuf, NULL))
1488 nothing_done = FALSE;
1489
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001490 if (nothing_done && do_msg
1491#ifdef FEAT_EVAL
1492 && !aborting()
1493#endif
1494 )
1495 smsg(_("No matching autocommands: %s"), arg_start);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001496 if (did_something != NULL)
1497 *did_something = !nothing_done;
1498
1499#ifdef FEAT_EVAL
1500 return aborting() ? FAIL : OK;
1501#else
1502 return OK;
1503#endif
1504}
1505
1506/*
1507 * ":doautoall": execute autocommands for each loaded buffer.
1508 */
1509 void
1510ex_doautoall(exarg_T *eap)
1511{
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001512 int retval = OK;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001513 aco_save_T aco;
1514 buf_T *buf;
1515 bufref_T bufref;
1516 char_u *arg = eap->arg;
1517 int call_do_modelines = check_nomodeline(&arg);
1518 int did_aucmd;
1519
1520 /*
1521 * This is a bit tricky: For some commands curwin->w_buffer needs to be
1522 * equal to curbuf, but for some buffers there may not be a window.
1523 * So we change the buffer for the current window for a moment. This
1524 * gives problems when the autocommands make changes to the list of
1525 * buffers or windows...
1526 */
1527 FOR_ALL_BUFFERS(buf)
1528 {
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001529 // Only do loaded buffers and skip the current buffer, it's done last.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001530 if (buf->b_ml.ml_mfp == NULL || buf == curbuf)
1531 continue;
1532
Bram Moolenaare76062c2022-11-28 18:51:43 +00001533 // Find a window for this buffer and save some values.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001534 aucmd_prepbuf(&aco, buf);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001535 if (curbuf != buf)
1536 {
1537 // Failed to find a window for this buffer. Better not execute
1538 // autocommands then.
1539 retval = FAIL;
1540 break;
1541 }
1542
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001543 set_bufref(&bufref, buf);
1544
1545 // execute the autocommands for this buffer
1546 retval = do_doautocmd(arg, FALSE, &did_aucmd);
1547
1548 if (call_do_modelines && did_aucmd)
1549 // Execute the modeline settings, but don't set window-local
1550 // options if we are using the current window for another
1551 // buffer.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001552 do_modelines(is_aucmd_win(curwin) ? OPT_NOWIN : 0);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001553
1554 // restore the current window
1555 aucmd_restbuf(&aco);
1556
1557 // stop if there is some error or buffer was deleted
1558 if (retval == FAIL || !bufref_valid(&bufref))
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001559 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001560 retval = FAIL;
1561 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001562 }
1563 }
1564
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001565 // Execute autocommands for the current buffer last.
1566 if (retval == OK)
1567 {
1568 do_doautocmd(arg, FALSE, &did_aucmd);
1569 if (call_do_modelines && did_aucmd)
1570 do_modelines(0);
1571 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001572}
1573
1574/*
1575 * Check *argp for <nomodeline>. When it is present return FALSE, otherwise
1576 * return TRUE and advance *argp to after it.
1577 * Thus return TRUE when do_modelines() should be called.
1578 */
1579 int
1580check_nomodeline(char_u **argp)
1581{
1582 if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
1583 {
1584 *argp = skipwhite(*argp + 12);
1585 return FALSE;
1586 }
1587 return TRUE;
1588}
1589
1590/*
1591 * Prepare for executing autocommands for (hidden) buffer "buf".
1592 * Search for a visible window containing the current buffer. If there isn't
Bram Moolenaare76062c2022-11-28 18:51:43 +00001593 * one then use an entry in "aucmd_win[]".
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001594 * Set "curbuf" and "curwin" to match "buf".
Bram Moolenaare76062c2022-11-28 18:51:43 +00001595 * When this fails "curbuf" is not equal "buf".
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001596 */
1597 void
1598aucmd_prepbuf(
1599 aco_save_T *aco, // structure to save values in
1600 buf_T *buf) // new curbuf
1601{
1602 win_T *win;
1603 int save_ea;
1604#ifdef FEAT_AUTOCHDIR
1605 int save_acd;
1606#endif
1607
1608 // Find a window that is for the new buffer
1609 if (buf == curbuf) // be quick when buf is curbuf
1610 win = curwin;
1611 else
1612 FOR_ALL_WINDOWS(win)
1613 if (win->w_buffer == buf)
1614 break;
1615
Bram Moolenaare76062c2022-11-28 18:51:43 +00001616 // Allocate a window when needed.
1617 win_T *auc_win = NULL;
1618 int auc_idx = AUCMD_WIN_COUNT;
1619 if (win == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001620 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001621 for (auc_idx = 0; auc_idx < AUCMD_WIN_COUNT; ++auc_idx)
1622 if (!aucmd_win[auc_idx].auc_win_used)
1623 {
Bram Moolenaar84497cd2022-11-28 20:34:52 +00001624 if (aucmd_win[auc_idx].auc_win == NULL)
1625 aucmd_win[auc_idx].auc_win = win_alloc_popup_win();
1626 auc_win = aucmd_win[auc_idx].auc_win;
Bram Moolenaare76062c2022-11-28 18:51:43 +00001627 if (auc_win != NULL)
Bram Moolenaare76062c2022-11-28 18:51:43 +00001628 aucmd_win[auc_idx].auc_win_used = TRUE;
Bram Moolenaare76062c2022-11-28 18:51:43 +00001629 break;
1630 }
1631
1632 // If this fails (out of memory or using all AUCMD_WIN_COUNT
1633 // entries) then we can't reliable execute the autocmd, return with
1634 // "curbuf" unequal "buf".
1635 if (auc_win == NULL)
1636 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001637 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001638
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001639 aco->save_curwin_id = curwin->w_id;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001640 aco->save_prevwin_id = prevwin == NULL ? 0 : prevwin->w_id;
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001641 aco->save_State = State;
zeertzjqf2678472024-01-17 21:22:59 +01001642#ifdef FEAT_JOB_CHANNEL
1643 if (bt_prompt(curbuf))
1644 aco->save_prompt_insert = curbuf->b_prompt_insert;
1645#endif
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001646
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001647 if (win != NULL)
1648 {
1649 // There is a window for "buf" in the current tab page, make it the
1650 // curwin. This is preferred, it has the least side effects (esp. if
1651 // "buf" is curbuf).
Bram Moolenaare76062c2022-11-28 18:51:43 +00001652 aco->use_aucmd_win_idx = -1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001653 curwin = win;
1654 }
1655 else
1656 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001657 // There is no window for "buf", use "auc_win". To minimize the side
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001658 // effects, insert it in the current tab page.
1659 // Anything related to a window (e.g., setting folds) may have
1660 // unexpected results.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001661 aco->use_aucmd_win_idx = auc_idx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001662
Bram Moolenaare76062c2022-11-28 18:51:43 +00001663 win_init_popup_win(auc_win, buf);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001664
zeertzjq9d956ee2024-04-07 18:16:10 +02001665 // Make sure tp_localdir and globaldir are NULL to avoid a
1666 // chdir() in win_enter_ext().
1667 // win_init_popup_win() has already set w_localdir to NULL.
1668 aco->tp_localdir = curtab->tp_localdir;
1669 curtab->tp_localdir = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001670 aco->globaldir = globaldir;
1671 globaldir = NULL;
1672
Bram Moolenaare76062c2022-11-28 18:51:43 +00001673 // Split the current window, put the auc_win in the upper half.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001674 // We don't want the BufEnter or WinEnter autocommands.
1675 block_autocmds();
1676 make_snapshot(SNAP_AUCMD_IDX);
1677 save_ea = p_ea;
1678 p_ea = FALSE;
1679
1680#ifdef FEAT_AUTOCHDIR
1681 // Prevent chdir() call in win_enter_ext(), through do_autochdir().
1682 save_acd = p_acd;
1683 p_acd = FALSE;
1684#endif
1685
Sean Dewar704966c2024-02-20 22:00:33 +01001686 (void)win_split_ins(0, WSP_TOP | WSP_FORCE_ROOM, auc_win, 0, NULL);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001687 (void)win_comp_pos(); // recompute window positions
1688 p_ea = save_ea;
1689#ifdef FEAT_AUTOCHDIR
1690 p_acd = save_acd;
1691#endif
1692 unblock_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001693 curwin = auc_win;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001694 }
1695 curbuf = buf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001696 aco->new_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001697 set_bufref(&aco->new_curbuf, curbuf);
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001698
1699 // disable the Visual area, the position may be invalid in another buffer
1700 aco->save_VIsual_active = VIsual_active;
1701 VIsual_active = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001702}
1703
1704/*
1705 * Cleanup after executing autocommands for a (hidden) buffer.
1706 * Restore the window as it was (if possible).
1707 */
1708 void
1709aucmd_restbuf(
1710 aco_save_T *aco) // structure holding saved values
1711{
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001712 int dummy;
1713 win_T *save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001714
Bram Moolenaare76062c2022-11-28 18:51:43 +00001715 if (aco->use_aucmd_win_idx >= 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001716 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001717 win_T *awp = aucmd_win[aco->use_aucmd_win_idx].auc_win;
1718
Bram Moolenaare76062c2022-11-28 18:51:43 +00001719 // Find "awp", it can't be closed, but it may be in another tab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001720 // page. Do not trigger autocommands here.
1721 block_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001722 if (curwin != awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001723 {
1724 tabpage_T *tp;
1725 win_T *wp;
1726
1727 FOR_ALL_TAB_WINDOWS(tp, wp)
1728 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001729 if (wp == awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001730 {
1731 if (tp != curtab)
1732 goto_tabpage_tp(tp, TRUE, TRUE);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001733 win_goto(awp);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001734 goto win_found;
1735 }
1736 }
1737 }
1738win_found:
zeertzjq46bdae02023-09-24 23:16:08 +02001739 --curbuf->b_nwindows;
orbitalcde8de02023-04-02 22:05:13 +01001740#ifdef FEAT_JOB_CHANNEL
zeertzjqa40c0bc2023-05-27 14:10:08 +01001741 int save_stop_insert_mode = stop_insert_mode;
orbitalcde8de02023-04-02 22:05:13 +01001742 // May need to stop Insert mode if we were in a prompt buffer.
1743 leaving_window(curwin);
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001744 // Do not stop Insert mode when already in Insert mode before.
1745 if (aco->save_State & MODE_INSERT)
zeertzjqa40c0bc2023-05-27 14:10:08 +01001746 stop_insert_mode = save_stop_insert_mode;
orbitalcde8de02023-04-02 22:05:13 +01001747#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001748 // Remove the window and frame from the tree of frames.
Sean Dewar704966c2024-02-20 22:00:33 +01001749 (void)winframe_remove(curwin, &dummy, NULL, NULL);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001750 win_remove(curwin, NULL);
Bram Moolenaar84497cd2022-11-28 20:34:52 +00001751
1752 // The window is marked as not used, but it is not freed, it can be
1753 // used again.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001754 aucmd_win[aco->use_aucmd_win_idx].auc_win_used = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001755 last_status(FALSE); // may need to remove last status line
1756
1757 if (!valid_tabpage_win(curtab))
1758 // no valid window in current tabpage
1759 close_tabpage(curtab);
1760
1761 restore_snapshot(SNAP_AUCMD_IDX, FALSE);
1762 (void)win_comp_pos(); // recompute window positions
1763 unblock_autocmds();
1764
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001765 save_curwin = win_find_by_id(aco->save_curwin_id);
1766 if (save_curwin != NULL)
1767 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001768 else
1769 // Hmm, original window disappeared. Just use the first one.
1770 curwin = firstwin;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001771 curbuf = curwin->w_buffer;
1772#ifdef FEAT_JOB_CHANNEL
1773 // May need to restore insert mode for a prompt buffer.
1774 entering_window(curwin);
zeertzjqf2678472024-01-17 21:22:59 +01001775 if (bt_prompt(curbuf))
1776 curbuf->b_prompt_insert = aco->save_prompt_insert;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001777#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001778 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001779#ifdef FEAT_EVAL
Bram Moolenaare76062c2022-11-28 18:51:43 +00001780 vars_clear(&awp->w_vars->dv_hashtab); // free all w: variables
1781 hash_init(&awp->w_vars->dv_hashtab); // re-use the hashtab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001782#endif
zeertzjq9d956ee2024-04-07 18:16:10 +02001783 // If :lcd has been used in the autocommand window, correct current
1784 // directory before restoring tp_localdir and globaldir.
1785 if (awp->w_localdir != NULL)
1786 win_fix_current_dir();
1787 vim_free(curtab->tp_localdir);
1788 curtab->tp_localdir = aco->tp_localdir;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001789 vim_free(globaldir);
1790 globaldir = aco->globaldir;
1791
1792 // the buffer contents may have changed
zeertzjq49f05242023-02-04 10:58:34 +00001793 VIsual_active = aco->save_VIsual_active;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001794 check_cursor();
1795 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1796 {
1797 curwin->w_topline = curbuf->b_ml.ml_line_count;
1798#ifdef FEAT_DIFF
1799 curwin->w_topfill = 0;
1800#endif
1801 }
1802#if defined(FEAT_GUI)
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001803 if (gui.in_use)
1804 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001805 // Hide the scrollbars from the "awp" and update.
1806 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_LEFT], FALSE);
1807 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_RIGHT], FALSE);
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001808 gui_may_update_scrollbars();
1809 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001810#endif
1811 }
1812 else
1813 {
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001814 // Restore curwin. Use the window ID, a window may have been closed
1815 // and the memory re-used for another one.
1816 save_curwin = win_find_by_id(aco->save_curwin_id);
1817 if (save_curwin != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001818 {
1819 // Restore the buffer which was previously edited by curwin, if
1820 // it was changed, we are still the same window and the buffer is
1821 // valid.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001822 if (curwin->w_id == aco->new_curwin_id
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001823 && curbuf != aco->new_curbuf.br_buf
1824 && bufref_valid(&aco->new_curbuf)
1825 && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
1826 {
1827# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
1828 if (curwin->w_s == &curbuf->b_s)
1829 curwin->w_s = &aco->new_curbuf.br_buf->b_s;
1830# endif
1831 --curbuf->b_nwindows;
1832 curbuf = aco->new_curbuf.br_buf;
1833 curwin->w_buffer = curbuf;
1834 ++curbuf->b_nwindows;
1835 }
1836
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001837 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001838 curbuf = curwin->w_buffer;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001839 prevwin = win_find_by_id(aco->save_prevwin_id);
zeertzjq49f05242023-02-04 10:58:34 +00001840
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001841 // In case the autocommand moves the cursor to a position that
1842 // does not exist in curbuf.
zeertzjq49f05242023-02-04 10:58:34 +00001843 VIsual_active = aco->save_VIsual_active;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001844 check_cursor();
1845 }
1846 }
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001847
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001848 VIsual_active = aco->save_VIsual_active;
zeertzjq49f05242023-02-04 10:58:34 +00001849 check_cursor(); // just in case lines got deleted
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001850 if (VIsual_active)
1851 check_pos(curbuf, &VIsual);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001852}
1853
1854static int autocmd_nested = FALSE;
1855
1856/*
1857 * Execute autocommands for "event" and file name "fname".
1858 * Return TRUE if some commands were executed.
1859 */
1860 int
1861apply_autocmds(
1862 event_T event,
1863 char_u *fname, // NULL or empty means use actual file name
1864 char_u *fname_io, // fname to use for <afile> on cmdline
1865 int force, // when TRUE, ignore autocmd_busy
1866 buf_T *buf) // buffer for <abuf>
1867{
1868 return apply_autocmds_group(event, fname, fname_io, force,
1869 AUGROUP_ALL, buf, NULL);
1870}
1871
1872/*
1873 * Like apply_autocmds(), but with extra "eap" argument. This takes care of
1874 * setting v:filearg.
1875 */
1876 int
1877apply_autocmds_exarg(
1878 event_T event,
1879 char_u *fname,
1880 char_u *fname_io,
1881 int force,
1882 buf_T *buf,
1883 exarg_T *eap)
1884{
1885 return apply_autocmds_group(event, fname, fname_io, force,
1886 AUGROUP_ALL, buf, eap);
1887}
1888
1889/*
1890 * Like apply_autocmds(), but handles the caller's retval. If the script
1891 * processing is being aborted or if retval is FAIL when inside a try
1892 * conditional, no autocommands are executed. If otherwise the autocommands
1893 * cause the script to be aborted, retval is set to FAIL.
1894 */
1895 int
1896apply_autocmds_retval(
1897 event_T event,
1898 char_u *fname, // NULL or empty means use actual file name
1899 char_u *fname_io, // fname to use for <afile> on cmdline
1900 int force, // when TRUE, ignore autocmd_busy
1901 buf_T *buf, // buffer for <abuf>
1902 int *retval) // pointer to caller's retval
1903{
1904 int did_cmd;
1905
1906#ifdef FEAT_EVAL
1907 if (should_abort(*retval))
1908 return FALSE;
1909#endif
1910
1911 did_cmd = apply_autocmds_group(event, fname, fname_io, force,
1912 AUGROUP_ALL, buf, NULL);
1913 if (did_cmd
1914#ifdef FEAT_EVAL
1915 && aborting()
1916#endif
1917 )
1918 *retval = FAIL;
1919 return did_cmd;
1920}
1921
1922/*
1923 * Return TRUE when there is a CursorHold autocommand defined.
1924 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001925 static int
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001926has_cursorhold(void)
1927{
Bram Moolenaar24959102022-05-07 20:01:16 +01001928 return (first_autopat[(int)(get_real_state() == MODE_NORMAL_BUSY
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001929 ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
1930}
1931
1932/*
1933 * Return TRUE if the CursorHold event can be triggered.
1934 */
1935 int
1936trigger_cursorhold(void)
1937{
1938 int state;
1939
1940 if (!did_cursorhold
1941 && has_cursorhold()
1942 && reg_recording == 0
1943 && typebuf.tb_len == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001944 && !ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001945 {
1946 state = get_real_state();
Bram Moolenaar24959102022-05-07 20:01:16 +01001947 if (state == MODE_NORMAL_BUSY || (state & MODE_INSERT) != 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001948 return TRUE;
1949 }
1950 return FALSE;
1951}
1952
1953/*
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001954 * Return TRUE when there is a WinResized autocommand defined.
1955 */
1956 int
1957has_winresized(void)
1958{
1959 return (first_autopat[(int)EVENT_WINRESIZED] != NULL);
1960}
1961
1962/*
LemonBoy09371822022-04-08 15:18:45 +01001963 * Return TRUE when there is a WinScrolled autocommand defined.
1964 */
1965 int
1966has_winscrolled(void)
1967{
1968 return (first_autopat[(int)EVENT_WINSCROLLED] != NULL);
1969}
1970
1971/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001972 * Return TRUE when there is a CursorMoved autocommand defined.
1973 */
1974 int
1975has_cursormoved(void)
1976{
1977 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1978}
1979
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001980/*
1981 * Return TRUE when there is a CursorMovedI autocommand defined.
1982 */
1983 int
1984has_cursormovedI(void)
1985{
1986 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1987}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001988
1989/*
1990 * Return TRUE when there is a TextChanged autocommand defined.
1991 */
1992 int
1993has_textchanged(void)
1994{
1995 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1996}
1997
1998/*
1999 * Return TRUE when there is a TextChangedI autocommand defined.
2000 */
2001 int
2002has_textchangedI(void)
2003{
2004 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
2005}
2006
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002007/*
2008 * Return TRUE when there is a TextChangedP autocommand defined.
2009 */
2010 int
2011has_textchangedP(void)
2012{
2013 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
2014}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002015
2016/*
2017 * Return TRUE when there is an InsertCharPre autocommand defined.
2018 */
2019 int
2020has_insertcharpre(void)
2021{
2022 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
2023}
2024
2025/*
Shougo Matsushita83678842024-07-11 22:05:12 +02002026 * Return TRUE when there is an KeyInputPre autocommand defined.
2027 */
2028 int
2029has_keyinputpre(void)
2030{
2031 return (first_autopat[(int)EVENT_KEYINPUTPRE] != NULL);
2032}
2033
2034/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002035 * Return TRUE when there is an CmdUndefined autocommand defined.
2036 */
2037 int
2038has_cmdundefined(void)
2039{
2040 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
2041}
2042
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002043#if defined(FEAT_EVAL) || defined(PROTO)
2044/*
2045 * Return TRUE when there is a TextYankPost autocommand defined.
2046 */
2047 int
2048has_textyankpost(void)
2049{
2050 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
2051}
2052#endif
2053
Bram Moolenaard7f246c2019-04-08 18:15:41 +02002054#if defined(FEAT_EVAL) || defined(PROTO)
2055/*
2056 * Return TRUE when there is a CompleteChanged autocommand defined.
2057 */
2058 int
2059has_completechanged(void)
2060{
2061 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
2062}
2063#endif
2064
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002065#if defined(FEAT_EVAL) || defined(PROTO)
2066/*
2067 * Return TRUE when there is a ModeChanged autocommand defined.
2068 */
2069 int
2070has_modechanged(void)
2071{
2072 return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
2073}
2074#endif
2075
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002076/*
2077 * Execute autocommands for "event" and file name "fname".
2078 * Return TRUE if some commands were executed.
2079 */
2080 static int
2081apply_autocmds_group(
2082 event_T event,
2083 char_u *fname, // NULL or empty means use actual file name
2084 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
2085 // use fname
2086 int force, // when TRUE, ignore autocmd_busy
2087 int group, // group ID, or AUGROUP_ALL
2088 buf_T *buf, // buffer for <abuf>
2089 exarg_T *eap UNUSED) // command arguments
2090{
2091 char_u *sfname = NULL; // short file name
2092 char_u *tail;
2093 int save_changed;
2094 buf_T *old_curbuf;
2095 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002096 char_u *save_autocmd_fname;
2097 int save_autocmd_fname_full;
2098 int save_autocmd_bufnr;
2099 char_u *save_autocmd_match;
2100 int save_autocmd_busy;
2101 int save_autocmd_nested;
2102 static int nesting = 0;
LemonBoyeca7c602022-04-14 15:39:43 +01002103 AutoPatCmd_T patcmd;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002104 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002105 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002106#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002107 funccal_entry_T funccal_entry;
2108 char_u *save_cmdarg;
2109 long save_cmdbang;
2110#endif
2111 static int filechangeshell_busy = FALSE;
2112#ifdef FEAT_PROFILE
2113 proftime_T wait_time;
2114#endif
2115 int did_save_redobuff = FALSE;
2116 save_redo_T save_redo;
2117 int save_KeyTyped = KeyTyped;
ichizok7e5fe382023-04-15 13:17:50 +01002118 ESTACK_CHECK_DECLARATION;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002119
2120 /*
2121 * Quickly return if there are no autocommands for this event or
2122 * autocommands are blocked.
2123 */
2124 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
2125 || autocmd_blocked > 0)
2126 goto BYPASS_AU;
2127
2128 /*
2129 * When autocommands are busy, new autocommands are only executed when
2130 * explicitly enabled with the "nested" flag.
2131 */
2132 if (autocmd_busy && !(force || autocmd_nested))
2133 goto BYPASS_AU;
2134
2135#ifdef FEAT_EVAL
2136 /*
2137 * Quickly return when immediately aborting on error, or when an interrupt
2138 * occurred or an exception was thrown but not caught.
2139 */
2140 if (aborting())
2141 goto BYPASS_AU;
2142#endif
2143
2144 /*
2145 * FileChangedShell never nests, because it can create an endless loop.
2146 */
2147 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
2148 || event == EVENT_FILECHANGEDSHELLPOST))
2149 goto BYPASS_AU;
2150
2151 /*
2152 * Ignore events in 'eventignore'.
2153 */
2154 if (event_ignored(event))
2155 goto BYPASS_AU;
2156
2157 /*
2158 * Allow nesting of autocommands, but restrict the depth, because it's
2159 * possible to create an endless loop.
2160 */
2161 if (nesting == 10)
2162 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00002163 emsg(_(e_autocommand_nesting_too_deep));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002164 goto BYPASS_AU;
2165 }
2166
2167 /*
2168 * Check if these autocommands are disabled. Used when doing ":all" or
2169 * ":ball".
2170 */
2171 if ( (autocmd_no_enter
2172 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
2173 || (autocmd_no_leave
2174 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
2175 goto BYPASS_AU;
2176
Bram Moolenaarbb393d82022-12-09 12:21:50 +00002177 if (event == EVENT_CMDLINECHANGED)
2178 ++aucmd_cmdline_changed_count;
2179
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002180 /*
2181 * Save the autocmd_* variables and info about the current buffer.
2182 */
2183 save_autocmd_fname = autocmd_fname;
2184 save_autocmd_fname_full = autocmd_fname_full;
2185 save_autocmd_bufnr = autocmd_bufnr;
2186 save_autocmd_match = autocmd_match;
2187 save_autocmd_busy = autocmd_busy;
2188 save_autocmd_nested = autocmd_nested;
2189 save_changed = curbuf->b_changed;
2190 old_curbuf = curbuf;
2191
2192 /*
2193 * Set the file name to be used for <afile>.
2194 * Make a copy to avoid that changing a buffer name or directory makes it
2195 * invalid.
2196 */
2197 if (fname_io == NULL)
2198 {
2199 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
Bram Moolenaarbb393d82022-12-09 12:21:50 +00002200 || event == EVENT_OPTIONSET
Danek Duvalld7d56032024-01-14 20:19:59 +01002201 || event == EVENT_MODECHANGED
2202 || event == EVENT_TERMRESPONSEALL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002203 autocmd_fname = NULL;
2204 else if (fname != NULL && !ends_excmd(*fname))
2205 autocmd_fname = fname;
2206 else if (buf != NULL)
2207 autocmd_fname = buf->b_ffname;
2208 else
2209 autocmd_fname = NULL;
2210 }
2211 else
2212 autocmd_fname = fname_io;
2213 if (autocmd_fname != NULL)
2214 autocmd_fname = vim_strsave(autocmd_fname);
2215 autocmd_fname_full = FALSE; // call FullName_save() later
2216
2217 /*
2218 * Set the buffer number to be used for <abuf>.
2219 */
2220 if (buf == NULL)
2221 autocmd_bufnr = 0;
2222 else
2223 autocmd_bufnr = buf->b_fnum;
2224
2225 /*
2226 * When the file name is NULL or empty, use the file name of buffer "buf".
2227 * Always use the full path of the file name to match with, in case
2228 * "allow_dirs" is set.
2229 */
2230 if (fname == NULL || *fname == NUL)
2231 {
2232 if (buf == NULL)
2233 fname = NULL;
2234 else
2235 {
2236#ifdef FEAT_SYN_HL
2237 if (event == EVENT_SYNTAX)
2238 fname = buf->b_p_syn;
2239 else
2240#endif
2241 if (event == EVENT_FILETYPE)
2242 fname = buf->b_p_ft;
2243 else
2244 {
2245 if (buf->b_sfname != NULL)
2246 sfname = vim_strsave(buf->b_sfname);
2247 fname = buf->b_ffname;
2248 }
2249 }
2250 if (fname == NULL)
2251 fname = (char_u *)"";
2252 fname = vim_strsave(fname); // make a copy, so we can change it
2253 }
2254 else
2255 {
2256 sfname = vim_strsave(fname);
2257 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002258 // ColorScheme, QuickFixCmd*, DirChanged and similar.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002259 if (event == EVENT_FILETYPE
2260 || event == EVENT_SYNTAX
2261 || event == EVENT_CMDLINECHANGED
2262 || event == EVENT_CMDLINEENTER
2263 || event == EVENT_CMDLINELEAVE
Shougo Matsushitad0952142024-06-20 22:05:16 +02002264 || event == EVENT_CURSORMOVEDC
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002265 || event == EVENT_CMDWINENTER
2266 || event == EVENT_CMDWINLEAVE
2267 || event == EVENT_CMDUNDEFINED
2268 || event == EVENT_FUNCUNDEFINED
Shougo Matsushita83678842024-07-11 22:05:12 +02002269 || event == EVENT_KEYINPUTPRE
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002270 || event == EVENT_REMOTEREPLY
2271 || event == EVENT_SPELLFILEMISSING
2272 || event == EVENT_QUICKFIXCMDPRE
2273 || event == EVENT_COLORSCHEME
2274 || event == EVENT_COLORSCHEMEPRE
2275 || event == EVENT_OPTIONSET
2276 || event == EVENT_QUICKFIXCMDPOST
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002277 || event == EVENT_DIRCHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002278 || event == EVENT_DIRCHANGEDPRE
naohiro ono23beefe2021-11-13 12:38:49 +00002279 || event == EVENT_MODECHANGED
zeertzjqc601d982022-10-10 13:46:15 +01002280 || event == EVENT_MENUPOPUP
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002281 || event == EVENT_USER
LemonBoy09371822022-04-08 15:18:45 +01002282 || event == EVENT_WINCLOSED
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00002283 || event == EVENT_WINRESIZED
Danek Duvalld7d56032024-01-14 20:19:59 +01002284 || event == EVENT_WINSCROLLED
2285 || event == EVENT_TERMRESPONSEALL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002286 {
2287 fname = vim_strsave(fname);
2288 autocmd_fname_full = TRUE; // don't expand it later
2289 }
2290 else
2291 fname = FullName_save(fname, FALSE);
2292 }
2293 if (fname == NULL) // out of memory
2294 {
2295 vim_free(sfname);
2296 retval = FALSE;
2297 goto BYPASS_AU;
2298 }
2299
2300#ifdef BACKSLASH_IN_FILENAME
2301 /*
2302 * Replace all backslashes with forward slashes. This makes the
2303 * autocommand patterns portable between Unix and MS-DOS.
2304 */
2305 if (sfname != NULL)
2306 forward_slash(sfname);
2307 forward_slash(fname);
2308#endif
2309
2310#ifdef VMS
2311 // remove version for correct match
2312 if (sfname != NULL)
2313 vms_remove_version(sfname);
2314 vms_remove_version(fname);
2315#endif
2316
2317 /*
2318 * Set the name to be used for <amatch>.
2319 */
2320 autocmd_match = fname;
2321
2322
2323 // Don't redraw while doing autocommands.
2324 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002325
2326 // name and lnum are filled in later
2327 estack_push(ETYPE_AUCMD, NULL, 0);
ichizok7e5fe382023-04-15 13:17:50 +01002328 ESTACK_CHECK_SETUP;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002329
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002330 save_current_sctx = current_sctx;
2331
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002332#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002333# ifdef FEAT_PROFILE
2334 if (do_profiling == PROF_YES)
2335 prof_child_enter(&wait_time); // doesn't count for the caller itself
2336# endif
2337
2338 // Don't use local function variables, if called from a function.
2339 save_funccal(&funccal_entry);
2340#endif
2341
2342 /*
2343 * When starting to execute autocommands, save the search patterns.
2344 */
2345 if (!autocmd_busy)
2346 {
2347 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002348 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002349 {
2350 saveRedobuff(&save_redo);
2351 did_save_redobuff = TRUE;
2352 }
zeertzjq5bf6c212024-03-31 18:41:27 +02002353 curbuf->b_did_filetype = curbuf->b_keep_filetype;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002354 }
2355
2356 /*
2357 * Note that we are applying autocmds. Some commands need to know.
2358 */
2359 autocmd_busy = TRUE;
2360 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2361 ++nesting; // see matching decrement below
2362
2363 // Remember that FileType was triggered. Used for did_filetype().
2364 if (event == EVENT_FILETYPE)
zeertzjq5bf6c212024-03-31 18:41:27 +02002365 curbuf->b_did_filetype = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002366
2367 tail = gettail(fname);
2368
2369 // Find first autocommand that matches
LemonBoyeca7c602022-04-14 15:39:43 +01002370 CLEAR_FIELD(patcmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002371 patcmd.curpat = first_autopat[(int)event];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002372 patcmd.group = group;
2373 patcmd.fname = fname;
2374 patcmd.sfname = sfname;
2375 patcmd.tail = tail;
2376 patcmd.event = event;
2377 patcmd.arg_bufnr = autocmd_bufnr;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002378 auto_next_pat(&patcmd, FALSE);
2379
2380 // found one, start executing the autocommands
2381 if (patcmd.curpat != NULL)
2382 {
2383 // add to active_apc_list
2384 patcmd.next = active_apc_list;
2385 active_apc_list = &patcmd;
2386
2387#ifdef FEAT_EVAL
2388 // set v:cmdarg (only when there is a matching pattern)
2389 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2390 if (eap != NULL)
2391 {
2392 save_cmdarg = set_cmdarg(eap, NULL);
2393 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2394 }
2395 else
2396 save_cmdarg = NULL; // avoid gcc warning
2397#endif
2398 retval = TRUE;
2399 // mark the last pattern, to avoid an endless loop when more patterns
2400 // are added when executing autocommands
2401 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2402 ap->last = FALSE;
2403 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002404
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002405 // Make sure cursor and topline are valid. The first time the current
2406 // values are saved, restored by reset_lnums(). When nested only the
2407 // values are corrected when needed.
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002408 if (nesting == 1)
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002409 check_lnums(TRUE);
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002410 else
2411 check_lnums_nested(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002412
Christian Brabandt590aae32023-06-25 22:34:22 +01002413 int save_did_emsg = did_emsg;
2414 int save_ex_pressedreturn = get_pressedreturn();
ichizokc3f91c02021-12-17 09:44:33 +00002415
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002416 do_cmdline(NULL, getnextac, (void *)&patcmd,
2417 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002418
ichizokc3f91c02021-12-17 09:44:33 +00002419 did_emsg += save_did_emsg;
Christian Brabandt590aae32023-06-25 22:34:22 +01002420 set_pressedreturn(save_ex_pressedreturn);
ichizokc3f91c02021-12-17 09:44:33 +00002421
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002422 if (nesting == 1)
2423 // restore cursor and topline, unless they were changed
2424 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002425
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002426#ifdef FEAT_EVAL
2427 if (eap != NULL)
2428 {
2429 (void)set_cmdarg(NULL, save_cmdarg);
2430 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2431 }
2432#endif
2433 // delete from active_apc_list
2434 if (active_apc_list == &patcmd) // just in case
2435 active_apc_list = patcmd.next;
2436 }
2437
Bram Moolenaar79cdf022023-05-20 14:07:00 +01002438 if (RedrawingDisabled > 0)
2439 --RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002440 autocmd_busy = save_autocmd_busy;
2441 filechangeshell_busy = FALSE;
2442 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002443 vim_free(SOURCING_NAME);
ichizok7e5fe382023-04-15 13:17:50 +01002444 ESTACK_CHECK_NOW;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002445 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002446 vim_free(autocmd_fname);
2447 autocmd_fname = save_autocmd_fname;
2448 autocmd_fname_full = save_autocmd_fname_full;
2449 autocmd_bufnr = save_autocmd_bufnr;
2450 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002451 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002452#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002453 restore_funccal();
2454# ifdef FEAT_PROFILE
2455 if (do_profiling == PROF_YES)
2456 prof_child_exit(&wait_time);
2457# endif
2458#endif
2459 KeyTyped = save_KeyTyped;
2460 vim_free(fname);
2461 vim_free(sfname);
2462 --nesting; // see matching increment above
2463
2464 /*
2465 * When stopping to execute autocommands, restore the search patterns and
2466 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2467 * free any windows in the au_pending_free_win list.
2468 */
2469 if (!autocmd_busy)
2470 {
2471 restore_search_patterns();
2472 if (did_save_redobuff)
2473 restoreRedobuff(&save_redo);
zeertzjq5bf6c212024-03-31 18:41:27 +02002474 curbuf->b_did_filetype = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002475 while (au_pending_free_buf != NULL)
2476 {
2477 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002478
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002479 vim_free(au_pending_free_buf);
2480 au_pending_free_buf = b;
2481 }
2482 while (au_pending_free_win != NULL)
2483 {
2484 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002485
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002486 vim_free(au_pending_free_win);
2487 au_pending_free_win = w;
2488 }
2489 }
2490
2491 /*
2492 * Some events don't set or reset the Changed flag.
2493 * Check if still in the same buffer!
2494 */
2495 if (curbuf == old_curbuf
2496 && (event == EVENT_BUFREADPOST
2497 || event == EVENT_BUFWRITEPOST
2498 || event == EVENT_FILEAPPENDPOST
2499 || event == EVENT_VIMLEAVE
2500 || event == EVENT_VIMLEAVEPRE))
2501 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002502 if (curbuf->b_changed != save_changed)
2503 need_maketitle = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002504 curbuf->b_changed = save_changed;
2505 }
2506
2507 au_cleanup(); // may really delete removed patterns/commands now
2508
2509BYPASS_AU:
2510 // When wiping out a buffer make sure all its buffer-local autocommands
2511 // are deleted.
2512 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2513 aubuflocal_remove(buf);
2514
2515 if (retval == OK && event == EVENT_FILETYPE)
zeertzjq5bf6c212024-03-31 18:41:27 +02002516 curbuf->b_au_did_filetype = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002517
2518 return retval;
2519}
2520
2521# ifdef FEAT_EVAL
2522static char_u *old_termresponse = NULL;
Danek Duvalld7d56032024-01-14 20:19:59 +01002523static char_u *old_termu7resp = NULL;
2524static char_u *old_termblinkresp = NULL;
2525static char_u *old_termrbgresp = NULL;
2526static char_u *old_termrfgresp = NULL;
2527static char_u *old_termstyleresp = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002528# endif
2529
2530/*
2531 * Block triggering autocommands until unblock_autocmd() is called.
2532 * Can be used recursively, so long as it's symmetric.
2533 */
2534 void
2535block_autocmds(void)
2536{
2537# ifdef FEAT_EVAL
2538 // Remember the value of v:termresponse.
2539 if (autocmd_blocked == 0)
Danek Duvalld7d56032024-01-14 20:19:59 +01002540 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002541 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
Danek Duvalld7d56032024-01-14 20:19:59 +01002542 old_termu7resp = get_vim_var_str(VV_TERMU7RESP);
2543 old_termblinkresp = get_vim_var_str(VV_TERMBLINKRESP);
2544 old_termrbgresp = get_vim_var_str(VV_TERMRBGRESP);
2545 old_termrfgresp = get_vim_var_str(VV_TERMRFGRESP);
2546 old_termstyleresp = get_vim_var_str(VV_TERMSTYLERESP);
2547 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002548# endif
2549 ++autocmd_blocked;
2550}
2551
2552 void
2553unblock_autocmds(void)
2554{
2555 --autocmd_blocked;
2556
2557# ifdef FEAT_EVAL
Danek Duvalld7d56032024-01-14 20:19:59 +01002558 // When v:termresponse, etc, were set while autocommands were blocked,
2559 // trigger the autocommands now. Esp. useful when executing a shell
2560 // command during startup (vimdiff).
2561 if (autocmd_blocked == 0)
2562 {
2563 if (get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2564 {
2565 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2566 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"version", NULL, FALSE, curbuf);
2567 }
2568 if (get_vim_var_str(VV_TERMU7RESP) != old_termu7resp)
2569 {
2570 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"ambiguouswidth", NULL, FALSE, curbuf);
2571 }
2572 if (get_vim_var_str(VV_TERMBLINKRESP) != old_termblinkresp)
2573 {
2574 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"cursorblink", NULL, FALSE, curbuf);
2575 }
2576 if (get_vim_var_str(VV_TERMRBGRESP) != old_termrbgresp)
2577 {
2578 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"background", NULL, FALSE, curbuf);
2579 }
2580 if (get_vim_var_str(VV_TERMRFGRESP) != old_termrfgresp)
2581 {
2582 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"foreground", NULL, FALSE, curbuf);
2583 }
2584 if (get_vim_var_str(VV_TERMSTYLERESP) != old_termstyleresp)
2585 {
2586 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"cursorshape", NULL, FALSE, curbuf);
2587 }
2588 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002589# endif
2590}
2591
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002592 int
2593is_autocmd_blocked(void)
2594{
2595 return autocmd_blocked != 0;
2596}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002597
2598/*
2599 * Find next autocommand pattern that matches.
2600 */
2601 static void
2602auto_next_pat(
LemonBoyeca7c602022-04-14 15:39:43 +01002603 AutoPatCmd_T *apc,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002604 int stop_at_last) // stop when 'last' flag is set
2605{
2606 AutoPat *ap;
2607 AutoCmd *cp;
2608 char_u *name;
2609 char *s;
LemonBoyeca7c602022-04-14 15:39:43 +01002610 estack_T *entry;
2611 char_u *namep;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002612
LemonBoyeca7c602022-04-14 15:39:43 +01002613 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
2614
2615 // Clear the exestack entry for this ETYPE_AUCMD entry.
2616 VIM_CLEAR(entry->es_name);
2617 entry->es_info.aucmd = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002618
2619 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2620 {
2621 apc->curpat = NULL;
2622
2623 // Only use a pattern when it has not been removed, has commands and
2624 // the group matches. For buffer-local autocommands only check the
2625 // buffer number.
2626 if (ap->pat != NULL && ap->cmds != NULL
2627 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2628 {
2629 // execution-condition
2630 if (ap->buflocal_nr == 0
2631 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2632 apc->sfname, apc->tail, ap->allow_dirs))
2633 : ap->buflocal_nr == apc->arg_bufnr)
2634 {
2635 name = event_nr2name(apc->event);
2636 s = _("%s Autocommands for \"%s\"");
LemonBoyeca7c602022-04-14 15:39:43 +01002637 namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1);
2638 if (namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002639 {
LemonBoyeca7c602022-04-14 15:39:43 +01002640 sprintf((char *)namep, s, (char *)name, (char *)ap->pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002641 if (p_verbose >= 8)
2642 {
2643 verbose_enter();
LemonBoyeca7c602022-04-14 15:39:43 +01002644 smsg(_("Executing %s"), namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002645 verbose_leave();
2646 }
2647 }
2648
LemonBoyeca7c602022-04-14 15:39:43 +01002649 // Update the exestack entry for this autocmd.
2650 entry->es_name = namep;
2651 entry->es_info.aucmd = apc;
2652
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002653 apc->curpat = ap;
2654 apc->nextcmd = ap->cmds;
2655 // mark last command
2656 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2657 cp->last = FALSE;
2658 cp->last = TRUE;
2659 }
2660 line_breakcheck();
2661 if (apc->curpat != NULL) // found a match
2662 break;
2663 }
2664 if (stop_at_last && ap->last)
2665 break;
2666 }
2667}
2668
Dominique Pellee764d1b2023-03-12 21:20:59 +00002669#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002670/*
LemonBoyeca7c602022-04-14 15:39:43 +01002671 * Get the script context where autocommand "acp" is defined.
2672 */
2673 sctx_T *
2674acp_script_ctx(AutoPatCmd_T *acp)
2675{
2676 return &acp->script_ctx;
2677}
Dominique Pellee764d1b2023-03-12 21:20:59 +00002678#endif
LemonBoyeca7c602022-04-14 15:39:43 +01002679
2680/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002681 * Get next autocommand command.
2682 * Called by do_cmdline() to get the next line for ":if".
2683 * Returns allocated string, or NULL for end of autocommands.
2684 */
2685 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002686getnextac(
2687 int c UNUSED,
2688 void *cookie,
2689 int indent UNUSED,
2690 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002691{
LemonBoyeca7c602022-04-14 15:39:43 +01002692 AutoPatCmd_T *acp = (AutoPatCmd_T *)cookie;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002693 char_u *retval;
2694 AutoCmd *ac;
2695
2696 // Can be called again after returning the last line.
2697 if (acp->curpat == NULL)
2698 return NULL;
2699
2700 // repeat until we find an autocommand to execute
2701 for (;;)
2702 {
2703 // skip removed commands
2704 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2705 if (acp->nextcmd->last)
2706 acp->nextcmd = NULL;
2707 else
2708 acp->nextcmd = acp->nextcmd->next;
2709
2710 if (acp->nextcmd != NULL)
2711 break;
2712
2713 // at end of commands, find next pattern that matches
2714 if (acp->curpat->last)
2715 acp->curpat = NULL;
2716 else
2717 acp->curpat = acp->curpat->next;
2718 if (acp->curpat != NULL)
2719 auto_next_pat(acp, TRUE);
2720 if (acp->curpat == NULL)
2721 return NULL;
2722 }
2723
2724 ac = acp->nextcmd;
2725
2726 if (p_verbose >= 9)
2727 {
2728 verbose_enter_scroll();
2729 smsg(_("autocommand %s"), ac->cmd);
2730 msg_puts("\n"); // don't overwrite this either
2731 verbose_leave_scroll();
2732 }
2733 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002734 // Remove one-shot ("once") autocmd in anticipation of its execution.
2735 if (ac->once)
2736 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002737 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002738 current_sctx = ac->script_ctx;
LemonBoyeca7c602022-04-14 15:39:43 +01002739 acp->script_ctx = current_sctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002740 if (ac->last)
2741 acp->nextcmd = NULL;
2742 else
2743 acp->nextcmd = ac->next;
2744 return retval;
2745}
2746
2747/*
2748 * Return TRUE if there is a matching autocommand for "fname".
2749 * To account for buffer-local autocommands, function needs to know
2750 * in which buffer the file will be opened.
2751 */
2752 int
2753has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2754{
2755 AutoPat *ap;
2756 char_u *fname;
2757 char_u *tail = gettail(sfname);
2758 int retval = FALSE;
2759
2760 fname = FullName_save(sfname, FALSE);
2761 if (fname == NULL)
2762 return FALSE;
2763
2764#ifdef BACKSLASH_IN_FILENAME
2765 /*
2766 * Replace all backslashes with forward slashes. This makes the
2767 * autocommand patterns portable between Unix and MS-DOS.
2768 */
2769 sfname = vim_strsave(sfname);
2770 if (sfname != NULL)
2771 forward_slash(sfname);
2772 forward_slash(fname);
2773#endif
2774
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002775 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002776 if (ap->pat != NULL && ap->cmds != NULL
2777 && (ap->buflocal_nr == 0
2778 ? match_file_pat(NULL, &ap->reg_prog,
2779 fname, sfname, tail, ap->allow_dirs)
2780 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2781 ))
2782 {
2783 retval = TRUE;
2784 break;
2785 }
2786
2787 vim_free(fname);
2788#ifdef BACKSLASH_IN_FILENAME
2789 vim_free(sfname);
2790#endif
2791
2792 return retval;
2793}
2794
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002795/*
2796 * Function given to ExpandGeneric() to obtain the list of autocommand group
2797 * names.
2798 */
2799 char_u *
2800get_augroup_name(expand_T *xp UNUSED, int idx)
2801{
2802 if (idx == augroups.ga_len) // add "END" add the end
2803 return (char_u *)"END";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002804 if (idx < 0 || idx >= augroups.ga_len) // end of list
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002805 return NULL;
2806 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2807 // skip deleted entries
2808 return (char_u *)"";
2809 return AUGROUP_NAME(idx); // return a name
2810}
2811
2812static int include_groups = FALSE;
2813
2814 char_u *
2815set_context_in_autocmd(
2816 expand_T *xp,
2817 char_u *arg,
2818 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2819{
2820 char_u *p;
2821 int group;
2822
2823 // check for a group name, skip it if present
2824 include_groups = FALSE;
2825 p = arg;
2826 group = au_get_grouparg(&arg);
2827 if (group == AUGROUP_ERROR)
2828 return NULL;
2829 // If there only is a group name that's what we expand.
2830 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2831 {
2832 arg = p;
2833 group = AUGROUP_ALL;
2834 }
2835
2836 // skip over event name
2837 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2838 if (*p == ',')
2839 arg = p + 1;
2840 if (*p == NUL)
2841 {
2842 if (group == AUGROUP_ALL)
2843 include_groups = TRUE;
2844 xp->xp_context = EXPAND_EVENTS; // expand event name
2845 xp->xp_pattern = arg;
2846 return NULL;
2847 }
2848
2849 // skip over pattern
2850 arg = skipwhite(p);
2851 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2852 arg++;
2853 if (*arg)
2854 return arg; // expand (next) command
2855
2856 if (doautocmd)
2857 xp->xp_context = EXPAND_FILES; // expand file names
2858 else
2859 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2860 return NULL;
2861}
2862
2863/*
2864 * Function given to ExpandGeneric() to obtain the list of event names.
2865 */
2866 char_u *
2867get_event_name(expand_T *xp UNUSED, int idx)
2868{
John Marriott78d742a2024-04-02 20:26:01 +02002869 int i;
2870
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002871 if (idx < augroups.ga_len) // First list group names, if wanted
2872 {
2873 if (!include_groups || AUGROUP_NAME(idx) == NULL
2874 || AUGROUP_NAME(idx) == get_deleted_augroup())
2875 return (char_u *)""; // skip deleted entries
2876 return AUGROUP_NAME(idx); // return a name
2877 }
John Marriott78d742a2024-04-02 20:26:01 +02002878
2879 i = idx - augroups.ga_len;
2880 if (i < 0 || i >= (int)ARRAY_LENGTH(event_tab))
2881 return NULL;
2882
2883 return (char_u *)event_tab[i].value;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002884}
2885
Yee Cheng Chin900894b2023-09-29 20:42:32 +02002886/*
2887 * Function given to ExpandGeneric() to obtain the list of event names. Don't
2888 * include groups.
2889 */
2890 char_u *
2891get_event_name_no_group(expand_T *xp UNUSED, int idx)
2892{
John Marriott78d742a2024-04-02 20:26:01 +02002893 if (idx < 0 || idx >= (int)ARRAY_LENGTH(event_tab))
2894 return NULL;
2895
2896 return (char_u *)event_tab[idx].value;
Yee Cheng Chin900894b2023-09-29 20:42:32 +02002897}
2898
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002899
2900#if defined(FEAT_EVAL) || defined(PROTO)
2901/*
2902 * Return TRUE if autocmd is supported.
2903 */
2904 int
2905autocmd_supported(char_u *name)
2906{
2907 char_u *p;
2908
2909 return (event_name2nr(name, &p) != NUM_EVENTS);
2910}
2911
2912/*
2913 * Return TRUE if an autocommand is defined for a group, event and
2914 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2915 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2916 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2917 * Used for:
2918 * exists("#Group") or
2919 * exists("#Group#Event") or
2920 * exists("#Group#Event#pat") or
2921 * exists("#Event") or
2922 * exists("#Event#pat")
2923 */
2924 int
2925au_exists(char_u *arg)
2926{
2927 char_u *arg_save;
2928 char_u *pattern = NULL;
2929 char_u *event_name;
2930 char_u *p;
2931 event_T event;
2932 AutoPat *ap;
2933 buf_T *buflocal_buf = NULL;
2934 int group;
2935 int retval = FALSE;
2936
2937 // Make a copy so that we can change the '#' chars to a NUL.
2938 arg_save = vim_strsave(arg);
2939 if (arg_save == NULL)
2940 return FALSE;
2941 p = vim_strchr(arg_save, '#');
2942 if (p != NULL)
2943 *p++ = NUL;
2944
2945 // First, look for an autocmd group name
2946 group = au_find_group(arg_save);
2947 if (group == AUGROUP_ERROR)
2948 {
2949 // Didn't match a group name, assume the first argument is an event.
2950 group = AUGROUP_ALL;
2951 event_name = arg_save;
2952 }
2953 else
2954 {
2955 if (p == NULL)
2956 {
2957 // "Group": group name is present and it's recognized
2958 retval = TRUE;
2959 goto theend;
2960 }
2961
2962 // Must be "Group#Event" or "Group#Event#pat".
2963 event_name = p;
2964 p = vim_strchr(event_name, '#');
2965 if (p != NULL)
2966 *p++ = NUL; // "Group#Event#pat"
2967 }
2968
2969 pattern = p; // "pattern" is NULL when there is no pattern
2970
2971 // find the index (enum) for the event name
2972 event = event_name2nr(event_name, &p);
2973
2974 // return FALSE if the event name is not recognized
2975 if (event == NUM_EVENTS)
2976 goto theend;
2977
2978 // Find the first autocommand for this event.
2979 // If there isn't any, return FALSE;
2980 // If there is one and no pattern given, return TRUE;
2981 ap = first_autopat[(int)event];
2982 if (ap == NULL)
2983 goto theend;
2984
2985 // if pattern is "<buffer>", special handling is needed which uses curbuf
2986 // for pattern "<buffer=N>, fnamecmp() will work fine
2987 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2988 buflocal_buf = curbuf;
2989
2990 // Check if there is an autocommand with the given pattern.
2991 for ( ; ap != NULL; ap = ap->next)
2992 // only use a pattern when it has not been removed and has commands.
2993 // For buffer-local autocommands, fnamecmp() works fine.
2994 if (ap->pat != NULL && ap->cmds != NULL
2995 && (group == AUGROUP_ALL || ap->group == group)
2996 && (pattern == NULL
2997 || (buflocal_buf == NULL
2998 ? fnamecmp(ap->pat, pattern) == 0
2999 : ap->buflocal_nr == buflocal_buf->b_fnum)))
3000 {
3001 retval = TRUE;
3002 break;
3003 }
3004
3005theend:
3006 vim_free(arg_save);
3007 return retval;
3008}
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003009
3010/*
3011 * autocmd_add() and autocmd_delete() functions
3012 */
3013 static void
3014autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
3015{
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003016 list_T *aucmd_list;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003017 listitem_T *li;
3018 dict_T *event_dict;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003019 dictitem_T *di;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003020 char_u *event_name = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003021 list_T *event_list;
3022 listitem_T *eli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003023 event_T event;
3024 char_u *group_name = NULL;
3025 int group;
3026 char_u *pat = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003027 list_T *pat_list;
3028 listitem_T *pli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003029 char_u *cmd = NULL;
3030 char_u *end;
3031 int once;
3032 int nested;
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01003033 int replace; // replace the cmd for a group/event
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003034 int retval = VVAL_TRUE;
3035 int save_augroup = current_augroup;
3036
3037 rettv->v_type = VAR_BOOL;
3038 rettv->vval.v_number = VVAL_FALSE;
3039
3040 if (check_for_list_arg(argvars, 0) == FAIL)
3041 return;
3042
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003043 aucmd_list = argvars[0].vval.v_list;
3044 if (aucmd_list == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003045 return;
3046
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003047 FOR_ALL_LIST_ITEMS(aucmd_list, li)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003048 {
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003049 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003050 VIM_CLEAR(cmd);
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003051 event_name = NULL;
3052 event_list = NULL;
3053 pat = NULL;
3054 pat_list = NULL;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003055
3056 if (li->li_tv.v_type != VAR_DICT)
3057 continue;
3058
3059 event_dict = li->li_tv.vval.v_dict;
3060 if (event_dict == NULL)
3061 continue;
3062
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003063 di = dict_find(event_dict, (char_u *)"event", -1);
3064 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003065 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003066 if (di->di_tv.v_type == VAR_STRING)
3067 {
3068 event_name = di->di_tv.vval.v_string;
3069 if (event_name == NULL)
3070 {
3071 emsg(_(e_string_required));
3072 continue;
3073 }
3074 }
3075 else if (di->di_tv.v_type == VAR_LIST)
3076 {
3077 event_list = di->di_tv.vval.v_list;
3078 if (event_list == NULL)
3079 {
3080 emsg(_(e_list_required));
3081 continue;
3082 }
3083 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003084 else
3085 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003086 emsg(_(e_string_or_list_expected));
3087 continue;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003088 }
3089 }
3090
Bram Moolenaard61efa52022-07-23 09:52:04 +01003091 group_name = dict_get_string(event_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003092 if (group_name == NULL || *group_name == NUL)
3093 // if the autocmd group name is not specified, then use the current
3094 // autocmd group
3095 group = current_augroup;
3096 else
3097 {
3098 group = au_find_group(group_name);
3099 if (group == AUGROUP_ERROR)
3100 {
3101 if (delete)
3102 {
3103 semsg(_(e_no_such_group_str), group_name);
3104 retval = VVAL_FALSE;
3105 break;
3106 }
3107 // group is not found, create it now
3108 group = au_new_group(group_name);
3109 if (group == AUGROUP_ERROR)
3110 {
3111 semsg(_(e_no_such_group_str), group_name);
3112 retval = VVAL_FALSE;
3113 break;
3114 }
3115
3116 current_augroup = group;
3117 }
3118 }
3119
3120 // if a buffer number is specified, then generate a pattern of the form
3121 // "<buffer=n>. Otherwise, use the pattern supplied by the user.
3122 if (dict_has_key(event_dict, "bufnr"))
3123 {
3124 varnumber_T bnum;
3125
Bram Moolenaard61efa52022-07-23 09:52:04 +01003126 bnum = dict_get_number_def(event_dict, "bufnr", -1);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003127 if (bnum == -1)
3128 continue;
3129
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003130 vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
3131 pat = IObuff;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003132 }
3133 else
3134 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003135 di = dict_find(event_dict, (char_u *)"pattern", -1);
3136 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003137 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003138 if (di->di_tv.v_type == VAR_STRING)
3139 {
3140 pat = di->di_tv.vval.v_string;
3141 if (pat == NULL)
3142 {
3143 emsg(_(e_string_required));
3144 continue;
3145 }
3146 }
3147 else if (di->di_tv.v_type == VAR_LIST)
3148 {
3149 pat_list = di->di_tv.vval.v_list;
3150 if (pat_list == NULL)
3151 {
3152 emsg(_(e_list_required));
3153 continue;
3154 }
3155 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003156 else
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003157 {
3158 emsg(_(e_string_or_list_expected));
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003159 continue;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003160 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003161 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003162 else if (delete)
3163 pat = (char_u *)"";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003164 }
3165
Bram Moolenaard61efa52022-07-23 09:52:04 +01003166 once = dict_get_bool(event_dict, "once", FALSE);
3167 nested = dict_get_bool(event_dict, "nested", FALSE);
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01003168 // if 'replace' is true, then remove all the commands associated with
3169 // this autocmd event/group and add the new command.
Bram Moolenaard61efa52022-07-23 09:52:04 +01003170 replace = dict_get_bool(event_dict, "replace", FALSE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003171
Bram Moolenaard61efa52022-07-23 09:52:04 +01003172 cmd = dict_get_string(event_dict, "cmd", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003173 if (cmd == NULL)
3174 {
3175 if (delete)
3176 cmd = vim_strsave((char_u *)"");
3177 else
3178 continue;
3179 }
3180
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003181 if (delete && (event_name == NULL
3182 || (event_name[0] == '*' && event_name[1] == NUL)))
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003183 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003184 // if the event name is not specified or '*', delete all the events
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003185 for (event = (event_T)0; (int)event < NUM_EVENTS;
3186 event = (event_T)((int)event + 1))
3187 {
3188 if (do_autocmd_event(event, pat, once, nested, cmd, delete,
3189 group, 0) == FAIL)
3190 {
3191 retval = VVAL_FALSE;
3192 break;
3193 }
3194 }
3195 }
3196 else
3197 {
Bram Moolenaar968443e2022-05-27 21:16:34 +01003198 char_u *p = NULL;
3199
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003200 eli = NULL;
3201 end = NULL;
3202 while (TRUE)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003203 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003204 if (event_list != NULL)
3205 {
3206 if (eli == NULL)
3207 eli = event_list->lv_first;
3208 else
3209 eli = eli->li_next;
3210 if (eli == NULL)
3211 break;
3212 if (eli->li_tv.v_type != VAR_STRING
Bram Moolenaar882476a2022-06-01 16:02:38 +01003213 || (p = eli->li_tv.vval.v_string) == NULL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003214 {
3215 emsg(_(e_string_required));
Bram Moolenaar882476a2022-06-01 16:02:38 +01003216 break;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003217 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003218 }
3219 else
3220 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003221 if (p == NULL)
3222 p = event_name;
3223 if (p == NULL || *p == NUL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003224 break;
3225 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003226
3227 event = event_name2nr(p, &end);
3228 if (event == NUM_EVENTS || *end != NUL)
3229 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003230 // this also catches something following a valid event name
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003231 semsg(_(e_no_such_event_str), p);
3232 retval = VVAL_FALSE;
3233 break;
3234 }
3235 if (pat != NULL)
3236 {
3237 if (do_autocmd_event(event, pat, once, nested, cmd,
3238 delete | replace, group, 0) == FAIL)
3239 {
3240 retval = VVAL_FALSE;
3241 break;
3242 }
3243 }
3244 else if (pat_list != NULL)
3245 {
3246 FOR_ALL_LIST_ITEMS(pat_list, pli)
3247 {
3248 if (pli->li_tv.v_type != VAR_STRING
3249 || pli->li_tv.vval.v_string == NULL)
3250 {
3251 emsg(_(e_string_required));
3252 continue;
3253 }
3254 if (do_autocmd_event(event,
3255 pli->li_tv.vval.v_string, once, nested,
3256 cmd, delete | replace, group, 0) ==
3257 FAIL)
3258 {
3259 retval = VVAL_FALSE;
3260 break;
3261 }
3262 }
3263 if (retval == VVAL_FALSE)
3264 break;
3265 }
3266 if (event_name != NULL)
3267 p = end;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003268 }
3269 }
3270
3271 // if only the autocmd group name is specified for delete and the
3272 // autocmd event, pattern and cmd are not specified, then delete the
3273 // autocmd group.
3274 if (delete && group_name != NULL &&
3275 (event_name == NULL || event_name[0] == NUL)
3276 && (pat == NULL || pat[0] == NUL)
3277 && (cmd == NULL || cmd[0] == NUL))
3278 au_del_group(group_name);
3279 }
3280
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003281 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003282 VIM_CLEAR(cmd);
3283
3284 current_augroup = save_augroup;
3285 rettv->vval.v_number = retval;
3286}
3287
3288/*
3289 * autocmd_add() function
3290 */
3291 void
3292f_autocmd_add(typval_T *argvars, typval_T *rettv)
3293{
3294 autocmd_add_or_delete(argvars, rettv, FALSE);
3295}
3296
3297/*
3298 * autocmd_delete() function
3299 */
3300 void
3301f_autocmd_delete(typval_T *argvars, typval_T *rettv)
3302{
3303 autocmd_add_or_delete(argvars, rettv, TRUE);
3304}
3305
3306/*
3307 * autocmd_get() function
3308 * Returns a List of autocmds.
3309 */
3310 void
3311f_autocmd_get(typval_T *argvars, typval_T *rettv)
3312{
3313 event_T event_arg = NUM_EVENTS;
3314 event_T event;
3315 AutoPat *ap;
3316 AutoCmd *ac;
3317 list_T *event_list;
3318 dict_T *event_dict;
3319 char_u *event_name = NULL;
3320 char_u *pat = NULL;
3321 char_u *name = NULL;
3322 int group = AUGROUP_ALL;
3323
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01003324 if (rettv_list_alloc(rettv) == FAIL)
3325 return;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003326 if (check_for_opt_dict_arg(argvars, 0) == FAIL)
3327 return;
3328
3329 if (argvars[0].v_type == VAR_DICT)
3330 {
3331 // return only the autocmds in the specified group
3332 if (dict_has_key(argvars[0].vval.v_dict, "group"))
3333 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003334 name = dict_get_string(argvars[0].vval.v_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003335 if (name == NULL)
3336 return;
3337
3338 if (*name == NUL)
3339 group = AUGROUP_DEFAULT;
3340 else
3341 {
3342 group = au_find_group(name);
3343 if (group == AUGROUP_ERROR)
3344 {
3345 semsg(_(e_no_such_group_str), name);
3346 vim_free(name);
3347 return;
3348 }
3349 }
3350 vim_free(name);
3351 }
3352
3353 // return only the autocmds for the specified event
3354 if (dict_has_key(argvars[0].vval.v_dict, "event"))
3355 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003356 name = dict_get_string(argvars[0].vval.v_dict, "event", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003357 if (name == NULL)
3358 return;
3359
3360 if (name[0] == '*' && name[1] == NUL)
3361 event_arg = NUM_EVENTS;
3362 else
3363 {
John Marriott78d742a2024-04-02 20:26:01 +02003364 keyvalue_T target;
3365 keyvalue_T *entry;
3366
3367 target.key = 0;
3368 target.value = (char *)name;
3369 target.length = (int)STRLEN(target.value);
3370 entry = (keyvalue_T *)bsearch(&target, &event_tab, ARRAY_LENGTH(event_tab), sizeof(event_tab[0]), cmp_keyvalue_value_ni);
3371 if (entry == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003372 {
3373 semsg(_(e_no_such_event_str), name);
3374 vim_free(name);
3375 return;
3376 }
John Marriott78d742a2024-04-02 20:26:01 +02003377 event_arg = (event_T)entry->key;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003378 }
3379 vim_free(name);
3380 }
3381
3382 // return only the autocmds for the specified pattern
3383 if (dict_has_key(argvars[0].vval.v_dict, "pattern"))
3384 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003385 pat = dict_get_string(argvars[0].vval.v_dict, "pattern", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003386 if (pat == NULL)
3387 return;
3388 }
3389 }
3390
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003391 event_list = rettv->vval.v_list;
3392
3393 // iterate through all the autocmd events
3394 for (event = (event_T)0; (int)event < NUM_EVENTS;
3395 event = (event_T)((int)event + 1))
3396 {
3397 if (event_arg != NUM_EVENTS && event != event_arg)
3398 continue;
3399
3400 event_name = event_nr2name(event);
3401
3402 // iterate through all the patterns for this autocmd event
3403 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
3404 {
3405 char_u *group_name;
3406
zeertzjq2d1d5c62024-06-09 16:44:33 +02003407 if (ap->pat == NULL) // pattern has been removed
3408 continue;
3409
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003410 if (group != AUGROUP_ALL && group != ap->group)
3411 continue;
3412
3413 if (pat != NULL && STRCMP(pat, ap->pat) != 0)
3414 continue;
3415
3416 group_name = get_augroup_name(NULL, ap->group);
3417
3418 // iterate through all the commands for this pattern and add one
3419 // item for each cmd.
3420 for (ac = ap->cmds; ac != NULL; ac = ac->next)
3421 {
3422 event_dict = dict_alloc();
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003423 if (event_dict == NULL
3424 || list_append_dict(event_list, event_dict) == FAIL)
Christian Brabandt29269a72024-04-16 22:44:31 +02003425 {
3426 vim_free(pat);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003427 return;
Christian Brabandt29269a72024-04-16 22:44:31 +02003428 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003429
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003430 if (dict_add_string(event_dict, "event", event_name) == FAIL
3431 || dict_add_string(event_dict, "group",
3432 group_name == NULL ? (char_u *)""
3433 : group_name) == FAIL
3434 || (ap->buflocal_nr != 0
3435 && (dict_add_number(event_dict, "bufnr",
3436 ap->buflocal_nr) == FAIL))
3437 || dict_add_string(event_dict, "pattern",
3438 ap->pat) == FAIL
3439 || dict_add_string(event_dict, "cmd", ac->cmd) == FAIL
3440 || dict_add_bool(event_dict, "once", ac->once) == FAIL
3441 || dict_add_bool(event_dict, "nested",
3442 ac->nested) == FAIL)
Christian Brabandt29269a72024-04-16 22:44:31 +02003443 {
3444 vim_free(pat);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003445 return;
Christian Brabandt29269a72024-04-16 22:44:31 +02003446 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003447 }
3448 }
3449 }
3450
3451 vim_free(pat);
3452}
3453
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01003454#endif