blob: bce57cb759e06299a5b9e5f1ab0c761a65667902 [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"),
123 KEYVALUE_ENTRY(EVENT_CURSORMOVEDI, "CursorMovedI"),
124 KEYVALUE_ENTRY(EVENT_DIFFUPDATED, "DiffUpdated"),
125 KEYVALUE_ENTRY(EVENT_DIRCHANGED, "DirChanged"),
126 KEYVALUE_ENTRY(EVENT_DIRCHANGEDPRE, "DirChangedPre"),
127 KEYVALUE_ENTRY(EVENT_ENCODINGCHANGED, "EncodingChanged"),
128 KEYVALUE_ENTRY(EVENT_EXITPRE, "ExitPre"),
129 KEYVALUE_ENTRY(EVENT_FILEAPPENDCMD, "FileAppendCmd"),
130 KEYVALUE_ENTRY(EVENT_FILEAPPENDPOST, "FileAppendPost"),
131 KEYVALUE_ENTRY(EVENT_FILEAPPENDPRE, "FileAppendPre"),
132 KEYVALUE_ENTRY(EVENT_FILECHANGEDRO, "FileChangedRO"),
133 KEYVALUE_ENTRY(EVENT_FILECHANGEDSHELL, "FileChangedShell"),
134 KEYVALUE_ENTRY(EVENT_FILECHANGEDSHELLPOST, "FileChangedShellPost"),
135 KEYVALUE_ENTRY(EVENT_ENCODINGCHANGED, "FileEncoding"),
136 KEYVALUE_ENTRY(EVENT_FILEREADCMD, "FileReadCmd"),
137 KEYVALUE_ENTRY(EVENT_FILEREADPOST, "FileReadPost"),
138 KEYVALUE_ENTRY(EVENT_FILEREADPRE, "FileReadPre"),
139 KEYVALUE_ENTRY(EVENT_FILETYPE, "FileType"),
140 KEYVALUE_ENTRY(EVENT_FILEWRITECMD, "FileWriteCmd"),
141 KEYVALUE_ENTRY(EVENT_FILEWRITEPOST, "FileWritePost"),
142 KEYVALUE_ENTRY(EVENT_FILEWRITEPRE, "FileWritePre"),
143 KEYVALUE_ENTRY(EVENT_FILTERREADPOST, "FilterReadPost"),
144 KEYVALUE_ENTRY(EVENT_FILTERREADPRE, "FilterReadPre"),
145 KEYVALUE_ENTRY(EVENT_FILTERWRITEPOST, "FilterWritePost"),
146 KEYVALUE_ENTRY(EVENT_FILTERWRITEPRE, "FilterWritePre"),
147 KEYVALUE_ENTRY(EVENT_FOCUSGAINED, "FocusGained"),
148 KEYVALUE_ENTRY(EVENT_FOCUSLOST, "FocusLost"),
149 KEYVALUE_ENTRY(EVENT_FUNCUNDEFINED, "FuncUndefined"),
150 KEYVALUE_ENTRY(EVENT_GUIENTER, "GUIEnter"),
151 KEYVALUE_ENTRY(EVENT_GUIFAILED, "GUIFailed"),
152 KEYVALUE_ENTRY(EVENT_INSERTCHANGE, "InsertChange"),
153 KEYVALUE_ENTRY(EVENT_INSERTCHARPRE, "InsertCharPre"),
154 KEYVALUE_ENTRY(EVENT_INSERTENTER, "InsertEnter"),
155 KEYVALUE_ENTRY(EVENT_INSERTLEAVE, "InsertLeave"),
156 KEYVALUE_ENTRY(EVENT_INSERTLEAVEPRE, "InsertLeavePre"),
157 KEYVALUE_ENTRY(EVENT_MENUPOPUP, "MenuPopup"),
158 KEYVALUE_ENTRY(EVENT_MODECHANGED, "ModeChanged"),
159 KEYVALUE_ENTRY(EVENT_OPTIONSET, "OptionSet"),
160 KEYVALUE_ENTRY(EVENT_QUICKFIXCMDPOST, "QuickFixCmdPost"),
161 KEYVALUE_ENTRY(EVENT_QUICKFIXCMDPRE, "QuickFixCmdPre"),
162 KEYVALUE_ENTRY(EVENT_QUITPRE, "QuitPre"),
163 KEYVALUE_ENTRY(EVENT_REMOTEREPLY, "RemoteReply"),
164 KEYVALUE_ENTRY(EVENT_SAFESTATE, "SafeState"),
165 KEYVALUE_ENTRY(EVENT_SAFESTATEAGAIN, "SafeStateAgain"),
166 KEYVALUE_ENTRY(EVENT_SESSIONLOADPOST, "SessionLoadPost"),
167 KEYVALUE_ENTRY(EVENT_SESSIONWRITEPOST, "SessionWritePost"),
168 KEYVALUE_ENTRY(EVENT_SHELLCMDPOST, "ShellCmdPost"),
169 KEYVALUE_ENTRY(EVENT_SHELLFILTERPOST, "ShellFilterPost"),
170 KEYVALUE_ENTRY(EVENT_SIGUSR1, "SigUSR1"),
171 KEYVALUE_ENTRY(EVENT_SOURCECMD, "SourceCmd"),
172 KEYVALUE_ENTRY(EVENT_SOURCEPOST, "SourcePost"),
173 KEYVALUE_ENTRY(EVENT_SOURCEPRE, "SourcePre"),
174 KEYVALUE_ENTRY(EVENT_SPELLFILEMISSING, "SpellFileMissing"),
175 KEYVALUE_ENTRY(EVENT_STDINREADPOST, "StdinReadPost"),
176 KEYVALUE_ENTRY(EVENT_STDINREADPRE, "StdinReadPre"),
177 KEYVALUE_ENTRY(EVENT_SWAPEXISTS, "SwapExists"),
178 KEYVALUE_ENTRY(EVENT_SYNTAX, "Syntax"),
179 KEYVALUE_ENTRY(EVENT_TABCLOSED, "TabClosed"),
180 KEYVALUE_ENTRY(EVENT_TABENTER, "TabEnter"),
181 KEYVALUE_ENTRY(EVENT_TABLEAVE, "TabLeave"),
182 KEYVALUE_ENTRY(EVENT_TABNEW, "TabNew"),
183 KEYVALUE_ENTRY(EVENT_TERMCHANGED, "TermChanged"),
184 KEYVALUE_ENTRY(EVENT_TERMINALOPEN, "TerminalOpen"),
185 KEYVALUE_ENTRY(EVENT_TERMINALWINOPEN, "TerminalWinOpen"),
186 KEYVALUE_ENTRY(EVENT_TERMRESPONSE, "TermResponse"),
187 KEYVALUE_ENTRY(EVENT_TERMRESPONSEALL, "TermResponseAll"),
188 KEYVALUE_ENTRY(EVENT_TEXTCHANGED, "TextChanged"),
189 KEYVALUE_ENTRY(EVENT_TEXTCHANGEDI, "TextChangedI"),
190 KEYVALUE_ENTRY(EVENT_TEXTCHANGEDP, "TextChangedP"),
191 KEYVALUE_ENTRY(EVENT_TEXTCHANGEDT, "TextChangedT"),
192 KEYVALUE_ENTRY(EVENT_TEXTYANKPOST, "TextYankPost"),
193 KEYVALUE_ENTRY(EVENT_USER, "User"),
194 KEYVALUE_ENTRY(EVENT_VIMENTER, "VimEnter"),
195 KEYVALUE_ENTRY(EVENT_VIMLEAVE, "VimLeave"),
196 KEYVALUE_ENTRY(EVENT_VIMLEAVEPRE, "VimLeavePre"),
197 KEYVALUE_ENTRY(EVENT_VIMRESIZED, "VimResized"),
198 KEYVALUE_ENTRY(EVENT_VIMRESUME, "VimResume"),
199 KEYVALUE_ENTRY(EVENT_VIMSUSPEND, "VimSuspend"),
200 KEYVALUE_ENTRY(EVENT_WINCLOSED, "WinClosed"),
201 KEYVALUE_ENTRY(EVENT_WINENTER, "WinEnter"),
202 KEYVALUE_ENTRY(EVENT_WINLEAVE, "WinLeave"),
203 KEYVALUE_ENTRY(EVENT_WINNEW, "WinNew"),
204 KEYVALUE_ENTRY(EVENT_WINNEWPRE, "WinNewPre"),
205 KEYVALUE_ENTRY(EVENT_WINRESIZED, "WinResized"),
206 KEYVALUE_ENTRY(EVENT_WINSCROLLED, "WinScrolled")
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100207};
208
John Marriott78d742a2024-04-02 20:26:01 +0200209static AutoPat *first_autopat[NUM_EVENTS] = {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100210 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
211 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
212 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
213 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
214 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
John Marriott78d742a2024-04-02 20:26:01 +0200215 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
216 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
217 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
218 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
219 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
220 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
221 NULL, NULL, NULL, NULL, NULL, NULL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100222};
223
John Marriott78d742a2024-04-02 20:26:01 +0200224static AutoPat *last_autopat[NUM_EVENTS] = {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100225 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
226 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
227 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,
John Marriott78d742a2024-04-02 20:26:01 +0200230 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
231 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
232 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
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100237};
238
kylo252ae6f1d82022-02-16 19:24:07 +0000239#define AUGROUP_DEFAULT (-1) // default autocmd group
240#define AUGROUP_ERROR (-2) // erroneous autocmd group
241#define AUGROUP_ALL (-3) // all autocmd groups
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100242
243/*
244 * struct used to keep status while executing autocommands for an event.
245 */
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100246struct AutoPatCmd_S
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100247{
248 AutoPat *curpat; // next AutoPat to examine
249 AutoCmd *nextcmd; // next AutoCmd to execute
250 int group; // group being used
251 char_u *fname; // fname to match with
252 char_u *sfname; // sfname to match with
253 char_u *tail; // tail of fname
254 event_T event; // current event
LemonBoyeca7c602022-04-14 15:39:43 +0100255 sctx_T script_ctx; // script context where it is defined
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100256 int arg_bufnr; // Initially equal to <abuf>, set to zero when
257 // buf is deleted.
LemonBoyeca7c602022-04-14 15:39:43 +0100258 AutoPatCmd_T *next; // chain of active apc-s for auto-invalidation
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100259};
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100260
LemonBoyeca7c602022-04-14 15:39:43 +0100261static AutoPatCmd_T *active_apc_list = NULL; // stack of active autocommands
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100262
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200263// Macro to loop over all the patterns for an autocmd event
264#define FOR_ALL_AUTOCMD_PATTERNS(event, ap) \
265 for ((ap) = first_autopat[(int)(event)]; (ap) != NULL; (ap) = (ap)->next)
266
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100267/*
268 * augroups stores a list of autocmd group names.
269 */
270static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
271#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
Bram Moolenaarc667da52019-11-30 20:52:27 +0100272// use get_deleted_augroup() to get this
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100273static char_u *deleted_augroup = NULL;
274
275/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100276 * The ID of the current group. Group 0 is the default one.
277 */
278static int current_augroup = AUGROUP_DEFAULT;
279
Bram Moolenaarc667da52019-11-30 20:52:27 +0100280static int au_need_clean = FALSE; // need to delete marked patterns
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100281
John Marriott78d742a2024-04-02 20:26:01 +0200282static event_T event_name2nr(char_u *start, char_u **end);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100283static char_u *event_nr2name(event_T event);
284static int au_get_grouparg(char_u **argp);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200285static 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 +0100286static 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 +0100287static void auto_next_pat(AutoPatCmd_T *apc, int stop_at_last);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100288static int au_find_group(char_u *name);
289
290static event_T last_event;
291static int last_group;
Bram Moolenaarc667da52019-11-30 20:52:27 +0100292static int autocmd_blocked = 0; // block all autocmds
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100293
294 static char_u *
295get_deleted_augroup(void)
296{
297 if (deleted_augroup == NULL)
298 deleted_augroup = (char_u *)_("--Deleted--");
299 return deleted_augroup;
300}
301
302/*
303 * Show the autocommands for one AutoPat.
304 */
305 static void
306show_autocmd(AutoPat *ap, event_T event)
307{
308 AutoCmd *ac;
309
310 // Check for "got_int" (here and at various places below), which is set
311 // when "q" has been hit for the "--more--" prompt
312 if (got_int)
313 return;
314 if (ap->pat == NULL) // pattern has been removed
315 return;
316
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000317 // Make sure no info referenced by "ap" is cleared, e.g. when a timer
318 // clears an augroup. Jump to "theend" after this!
319 // "ap->pat" may be cleared anyway.
320 ++autocmd_busy;
321
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100322 msg_putchar('\n');
323 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000324 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100325 if (event != last_event || ap->group != last_group)
326 {
327 if (ap->group != AUGROUP_DEFAULT)
328 {
329 if (AUGROUP_NAME(ap->group) == NULL)
330 msg_puts_attr((char *)get_deleted_augroup(), HL_ATTR(HLF_E));
331 else
332 msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
333 msg_puts(" ");
334 }
335 msg_puts_attr((char *)event_nr2name(event), HL_ATTR(HLF_T));
336 last_event = event;
337 last_group = ap->group;
338 msg_putchar('\n');
339 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000340 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100341 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000342
343 if (ap->pat == NULL)
344 goto theend; // timer might have cleared the pattern or group
345
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100346 msg_col = 4;
347 msg_outtrans(ap->pat);
348
349 for (ac = ap->cmds; ac != NULL; ac = ac->next)
350 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100351 if (ac->cmd == NULL) // skip removed commands
352 continue;
353
354 if (msg_col >= 14)
355 msg_putchar('\n');
356 msg_col = 14;
357 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000358 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100359 msg_outtrans(ac->cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100360#ifdef FEAT_EVAL
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100361 if (p_verbose > 0)
362 last_set_msg(ac->script_ctx);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100363#endif
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100364 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000365 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100366 if (ac->next != NULL)
367 {
368 msg_putchar('\n');
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100369 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000370 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100371 }
372 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000373
374theend:
375 --autocmd_busy;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100376}
377
378/*
379 * Mark an autocommand pattern for deletion.
380 */
381 static void
382au_remove_pat(AutoPat *ap)
383{
384 VIM_CLEAR(ap->pat);
385 ap->buflocal_nr = -1;
386 au_need_clean = TRUE;
387}
388
389/*
390 * Mark all commands for a pattern for deletion.
391 */
392 static void
393au_remove_cmds(AutoPat *ap)
394{
395 AutoCmd *ac;
396
397 for (ac = ap->cmds; ac != NULL; ac = ac->next)
398 VIM_CLEAR(ac->cmd);
399 au_need_clean = TRUE;
400}
401
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200402// Delete one command from an autocmd pattern.
403static void au_del_cmd(AutoCmd *ac)
404{
405 VIM_CLEAR(ac->cmd);
406 au_need_clean = TRUE;
407}
408
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100409/*
410 * Cleanup autocommands and patterns that have been deleted.
411 * This is only done when not executing autocommands.
412 */
413 static void
414au_cleanup(void)
415{
416 AutoPat *ap, **prev_ap;
417 AutoCmd *ac, **prev_ac;
418 event_T event;
419
420 if (autocmd_busy || !au_need_clean)
421 return;
422
423 // loop over all events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100424 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100425 event = (event_T)((int)event + 1))
426 {
427 // loop over all autocommand patterns
428 prev_ap = &(first_autopat[(int)event]);
429 for (ap = *prev_ap; ap != NULL; ap = *prev_ap)
430 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200431 int has_cmd = FALSE;
432
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200433 // loop over all commands for this pattern
434 prev_ac = &(ap->cmds);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100435 for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
436 {
437 // remove the command if the pattern is to be deleted or when
438 // the command has been marked for deletion
439 if (ap->pat == NULL || ac->cmd == NULL)
440 {
441 *prev_ac = ac->next;
442 vim_free(ac->cmd);
443 vim_free(ac);
444 }
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200445 else
446 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200447 has_cmd = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100448 prev_ac = &(ac->next);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200449 }
450 }
451
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200452 if (ap->pat != NULL && !has_cmd)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200453 // Pattern was not marked for deletion, but all of its
454 // commands were. So mark the pattern for deletion.
455 au_remove_pat(ap);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100456
457 // remove the pattern if it has been marked for deletion
458 if (ap->pat == NULL)
459 {
460 if (ap->next == NULL)
461 {
462 if (prev_ap == &(first_autopat[(int)event]))
463 last_autopat[(int)event] = NULL;
464 else
465 // this depends on the "next" field being the first in
466 // the struct
467 last_autopat[(int)event] = (AutoPat *)prev_ap;
468 }
469 *prev_ap = ap->next;
470 vim_regfree(ap->reg_prog);
471 vim_free(ap);
472 }
473 else
474 prev_ap = &(ap->next);
475 }
476 }
477
478 au_need_clean = FALSE;
479}
480
481/*
482 * Called when buffer is freed, to remove/invalidate related buffer-local
483 * autocmds.
484 */
485 void
486aubuflocal_remove(buf_T *buf)
487{
LemonBoyeca7c602022-04-14 15:39:43 +0100488 AutoPat *ap;
489 event_T event;
490 AutoPatCmd_T *apc;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100491
492 // invalidate currently executing autocommands
493 for (apc = active_apc_list; apc; apc = apc->next)
494 if (buf->b_fnum == apc->arg_bufnr)
495 apc->arg_bufnr = 0;
496
497 // invalidate buflocals looping through events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100498 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100499 event = (event_T)((int)event + 1))
500 // loop over all autocommand patterns
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200501 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100502 if (ap->buflocal_nr == buf->b_fnum)
503 {
504 au_remove_pat(ap);
505 if (p_verbose >= 6)
506 {
507 verbose_enter();
508 smsg(_("auto-removing autocommand: %s <buffer=%d>"),
509 event_nr2name(event), buf->b_fnum);
510 verbose_leave();
511 }
512 }
513 au_cleanup();
514}
515
516/*
517 * Add an autocmd group name.
518 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
519 */
520 static int
521au_new_group(char_u *name)
522{
523 int i;
524
525 i = au_find_group(name);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100526 if (i != AUGROUP_ERROR)
527 return i;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100528
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100529 // the group doesn't exist yet, add it. First try using a free entry.
530 for (i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100531 if (AUGROUP_NAME(i) == NULL)
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100532 break;
533 if (i == augroups.ga_len && ga_grow(&augroups, 1) == FAIL)
534 return AUGROUP_ERROR;
535
536 AUGROUP_NAME(i) = vim_strsave(name);
537 if (AUGROUP_NAME(i) == NULL)
538 return AUGROUP_ERROR;
539 if (i == augroups.ga_len)
540 ++augroups.ga_len;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100541
542 return i;
543}
544
545 static void
546au_del_group(char_u *name)
547{
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100548 int i;
549 event_T event;
550 AutoPat *ap;
551 int in_use = FALSE;
552
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100553
554 i = au_find_group(name);
555 if (i == AUGROUP_ERROR) // the group doesn't exist
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100556 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100557 semsg(_(e_no_such_group_str), name);
558 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100559 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100560 if (i == current_augroup)
561 {
562 emsg(_(e_cannot_delete_current_group));
563 return;
564 }
565
566 for (event = (event_T)0; (int)event < NUM_EVENTS;
567 event = (event_T)((int)event + 1))
568 {
569 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
570 if (ap->group == i && ap->pat != NULL)
571 {
572 give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
573 in_use = TRUE;
574 event = NUM_EVENTS;
575 break;
576 }
577 }
578 vim_free(AUGROUP_NAME(i));
579 if (in_use)
580 AUGROUP_NAME(i) = get_deleted_augroup();
581 else
582 AUGROUP_NAME(i) = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100583}
584
585/*
586 * Find the ID of an autocmd group name.
587 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
588 */
589 static int
590au_find_group(char_u *name)
591{
592 int i;
593
594 for (i = 0; i < augroups.ga_len; ++i)
595 if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
596 && STRCMP(AUGROUP_NAME(i), name) == 0)
597 return i;
598 return AUGROUP_ERROR;
599}
600
601/*
602 * Return TRUE if augroup "name" exists.
603 */
604 int
605au_has_group(char_u *name)
606{
607 return au_find_group(name) != AUGROUP_ERROR;
608}
609
610/*
611 * ":augroup {name}".
612 */
613 void
614do_augroup(char_u *arg, int del_group)
615{
616 int i;
617
618 if (del_group)
619 {
620 if (*arg == NUL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000621 emsg(_(e_argument_required));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100622 else
623 au_del_group(arg);
624 }
625 else if (STRICMP(arg, "end") == 0) // ":aug end": back to group 0
626 current_augroup = AUGROUP_DEFAULT;
627 else if (*arg) // ":aug xxx": switch to group xxx
628 {
629 i = au_new_group(arg);
630 if (i != AUGROUP_ERROR)
631 current_augroup = i;
632 }
633 else // ":aug": list the group names
634 {
635 msg_start();
636 for (i = 0; i < augroups.ga_len; ++i)
637 {
638 if (AUGROUP_NAME(i) != NULL)
639 {
640 msg_puts((char *)AUGROUP_NAME(i));
641 msg_puts(" ");
642 }
643 }
644 msg_clr_eos();
645 msg_end();
646 }
647}
648
Bram Moolenaare76062c2022-11-28 18:51:43 +0000649 void
650autocmd_init(void)
651{
652 CLEAR_FIELD(aucmd_win);
653}
654
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100655#if defined(EXITFREE) || defined(PROTO)
656 void
657free_all_autocmds(void)
658{
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100659 char_u *s;
660
661 for (current_augroup = -1; current_augroup < augroups.ga_len;
662 ++current_augroup)
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200663 do_autocmd(NULL, (char_u *)"", TRUE);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100664
Bram Moolenaare76062c2022-11-28 18:51:43 +0000665 for (int i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100666 {
667 s = ((char_u **)(augroups.ga_data))[i];
668 if (s != get_deleted_augroup())
669 vim_free(s);
670 }
671 ga_clear(&augroups);
Bram Moolenaare76062c2022-11-28 18:51:43 +0000672
Bram Moolenaar84497cd2022-11-28 20:34:52 +0000673 // aucmd_win[] is freed in win_free_all()
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100674}
675#endif
676
677/*
Bram Moolenaare76062c2022-11-28 18:51:43 +0000678 * Return TRUE if "win" is an active entry in aucmd_win[].
679 */
680 int
681is_aucmd_win(win_T *win)
682{
683 for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
684 if (aucmd_win[i].auc_win_used && aucmd_win[i].auc_win == win)
685 return TRUE;
686 return FALSE;
687}
688
689/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100690 * Return the event number for event name "start".
691 * Return NUM_EVENTS if the event name was not found.
692 * Return a pointer to the next event name in "end".
693 */
694 static event_T
695event_name2nr(char_u *start, char_u **end)
696{
697 char_u *p;
John Marriott78d742a2024-04-02 20:26:01 +0200698 keyvalue_T target;
699 keyvalue_T *entry;
700 static keyvalue_T *bufnewfile = &event_tab[BUFNEWFILE_INDEX];
701 static keyvalue_T *bufread = &event_tab[BUFREAD_INDEX];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100702
703 // the event name ends with end of line, '|', a blank or a comma
704 for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
705 ;
John Marriott78d742a2024-04-02 20:26:01 +0200706
707 target.key = 0;
708 target.value = (char *)start;
709 target.length = (size_t)(p - start);
710
711 // special cases:
712 // BufNewFile and BufRead are searched for ALOT (especially at startup)
713 // so we check for them first.
714 if (cmp_keyvalue_value_ni(&target, bufnewfile) == 0)
715 entry = bufnewfile;
716 else
717 if (cmp_keyvalue_value_ni(&target, bufread) == 0)
718 entry = bufread;
719 else
720 entry = (keyvalue_T *)bsearch(&target, &event_tab, ARRAY_LENGTH(event_tab), sizeof(event_tab[0]), cmp_keyvalue_value_ni);
721
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100722 if (*p == ',')
723 ++p;
724 *end = p;
John Marriott78d742a2024-04-02 20:26:01 +0200725
726 return (entry == NULL) ? NUM_EVENTS : (event_T)entry->key;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100727}
728
729/*
730 * Return the name for event "event".
731 */
732 static char_u *
733event_nr2name(event_T event)
734{
735 int i;
John Marriott78d742a2024-04-02 20:26:01 +0200736#define CACHE_SIZE 12
737 static int cache_tab[CACHE_SIZE];
738 static int cache_last_index = -1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100739
John Marriott78d742a2024-04-02 20:26:01 +0200740 if (cache_last_index < 0)
741 {
742 for (i = 0; i < (int)ARRAY_LENGTH(cache_tab); ++i)
743 cache_tab[i] = -1;
744 cache_last_index = ARRAY_LENGTH(cache_tab) - 1;
745 }
746
747 // first look in the cache
748 // the cache is circular. to search it we start at the most recent entry
749 // and go backwards wrapping around when we get to index 0.
750 for (i = cache_last_index; cache_tab[i] >= 0; )
751 {
752 if ((event_T)event_tab[cache_tab[i]].key == event)
753 return (char_u *)event_tab[cache_tab[i]].value;
754
755 if (i == 0)
756 i = ARRAY_LENGTH(cache_tab) - 1;
757 else
758 --i;
759
760 // are we back at the start?
761 if (i == cache_last_index)
762 break;
763 }
764
765 // look in the event table itself
766 for (i = 0; i < (int)ARRAY_LENGTH(event_tab); ++i)
767 {
768 if ((event_T)event_tab[i].key == event)
769 {
770 // store the found entry in the next position in the cache,
771 // wrapping around when we get to the maximum index.
772 if (cache_last_index == ARRAY_LENGTH(cache_tab) - 1)
773 cache_last_index = 0;
774 else
775 ++cache_last_index;
776 cache_tab[cache_last_index] = i;
777 break;
778 }
779 }
780
781 return (i == (int)ARRAY_LENGTH(event_tab)) ? (char_u *)"Unknown" : (char_u *)event_tab[i].value;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100782}
783
784/*
785 * Scan over the events. "*" stands for all events.
786 */
787 static char_u *
788find_end_event(
789 char_u *arg,
790 int have_group) // TRUE when group name was found
791{
792 char_u *pat;
793 char_u *p;
794
795 if (*arg == '*')
796 {
797 if (arg[1] && !VIM_ISWHITE(arg[1]))
798 {
Bram Moolenaar6d057012021-12-31 18:49:43 +0000799 semsg(_(e_illegal_character_after_star_str), arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100800 return NULL;
801 }
802 pat = arg + 1;
803 }
804 else
805 {
806 for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p)
807 {
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100808 if ((int)event_name2nr(pat, &p) >= NUM_EVENTS)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100809 {
810 if (have_group)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000811 semsg(_(e_no_such_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100812 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000813 semsg(_(e_no_such_group_or_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100814 return NULL;
815 }
816 }
817 }
818 return pat;
819}
820
821/*
822 * Return TRUE if "event" is included in 'eventignore'.
823 */
824 static int
825event_ignored(event_T event)
826{
827 char_u *p = p_ei;
828
829 while (*p != NUL)
830 {
831 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
832 return TRUE;
833 if (event_name2nr(p, &p) == event)
834 return TRUE;
835 }
836
837 return FALSE;
838}
839
840/*
841 * Return OK when the contents of p_ei is valid, FAIL otherwise.
842 */
843 int
844check_ei(void)
845{
846 char_u *p = p_ei;
847
848 while (*p)
849 {
850 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
851 {
852 p += 3;
853 if (*p == ',')
854 ++p;
855 }
856 else if (event_name2nr(p, &p) == NUM_EVENTS)
857 return FAIL;
858 }
859
860 return OK;
861}
862
863# if defined(FEAT_SYN_HL) || defined(PROTO)
864
865/*
866 * Add "what" to 'eventignore' to skip loading syntax highlighting for every
867 * buffer loaded into the window. "what" must start with a comma.
868 * Returns the old value of 'eventignore' in allocated memory.
869 */
870 char_u *
871au_event_disable(char *what)
872{
873 char_u *new_ei;
874 char_u *save_ei;
John Marriott78d742a2024-04-02 20:26:01 +0200875 size_t p_ei_len;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100876
John Marriott78d742a2024-04-02 20:26:01 +0200877 p_ei_len = STRLEN(p_ei);
878 save_ei = vim_strnsave(p_ei, p_ei_len);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100879 if (save_ei == NULL)
880 return NULL;
881
John Marriott78d742a2024-04-02 20:26:01 +0200882 new_ei = vim_strnsave(p_ei, p_ei_len + STRLEN(what));
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100883 if (new_ei == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100884 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100885 vim_free(save_ei);
886 return NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100887 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100888
889 if (*what == ',' && *p_ei == NUL)
890 STRCPY(new_ei, what + 1);
891 else
892 STRCAT(new_ei, what);
893 set_string_option_direct((char_u *)"ei", -1, new_ei,
894 OPT_FREE, SID_NONE);
895 vim_free(new_ei);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100896 return save_ei;
897}
898
899 void
900au_event_restore(char_u *old_ei)
901{
902 if (old_ei != NULL)
903 {
904 set_string_option_direct((char_u *)"ei", -1, old_ei,
905 OPT_FREE, SID_NONE);
906 vim_free(old_ei);
907 }
908}
Bram Moolenaarc667da52019-11-30 20:52:27 +0100909# endif // FEAT_SYN_HL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100910
911/*
912 * do_autocmd() -- implements the :autocmd command. Can be used in the
913 * following ways:
914 *
915 * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
916 * will be automatically executed for <event>
917 * when editing a file matching <pat>, in
918 * the current group.
919 * :autocmd <event> <pat> Show the autocommands associated with
920 * <event> and <pat>.
921 * :autocmd <event> Show the autocommands associated with
922 * <event>.
923 * :autocmd Show all autocommands.
924 * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
925 * <event> and <pat>, and add the command
926 * <cmd>, for the current group.
927 * :autocmd! <event> <pat> Remove all autocommands associated with
928 * <event> and <pat> for the current group.
929 * :autocmd! <event> Remove all autocommands associated with
930 * <event> for the current group.
931 * :autocmd! Remove ALL autocommands for the current
932 * group.
933 *
934 * Multiple events and patterns may be given separated by commas. Here are
935 * some examples:
936 * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
937 * :autocmd bufleave * set tw=79 nosmartindent ic infercase
938 *
939 * :autocmd * *.c show all autocommands for *.c files.
940 *
941 * Mostly a {group} argument can optionally appear before <event>.
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200942 * "eap" can be NULL.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100943 */
944 void
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200945do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100946{
947 char_u *arg = arg_in;
948 char_u *pat;
949 char_u *envpat = NULL;
950 char_u *cmd;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200951 int cmd_need_free = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100952 event_T event;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200953 char_u *tofree = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100954 int nested = FALSE;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200955 int once = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100956 int group;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200957 int i;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200958 int flags = 0;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100959
960 if (*arg == '|')
961 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000962 eap->nextcmd = arg + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100963 arg = (char_u *)"";
964 group = AUGROUP_ALL; // no argument, use all groups
965 }
966 else
967 {
968 /*
969 * Check for a legal group name. If not, use AUGROUP_ALL.
970 */
971 group = au_get_grouparg(&arg);
972 if (arg == NULL) // out of memory
973 return;
974 }
975
976 /*
977 * Scan over the events.
978 * If we find an illegal name, return here, don't do anything.
979 */
980 pat = find_end_event(arg, group != AUGROUP_ALL);
981 if (pat == NULL)
982 return;
983
984 pat = skipwhite(pat);
985 if (*pat == '|')
986 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000987 eap->nextcmd = pat + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100988 pat = (char_u *)"";
989 cmd = (char_u *)"";
990 }
991 else
992 {
993 /*
994 * Scan over the pattern. Put a NUL at the end.
995 */
996 cmd = pat;
997 while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
998 cmd++;
999 if (*cmd)
1000 *cmd++ = NUL;
1001
1002 // Expand environment variables in the pattern. Set 'shellslash', we
1003 // want forward slashes here.
1004 if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
1005 {
1006#ifdef BACKSLASH_IN_FILENAME
1007 int p_ssl_save = p_ssl;
1008
1009 p_ssl = TRUE;
1010#endif
1011 envpat = expand_env_save(pat);
1012#ifdef BACKSLASH_IN_FILENAME
1013 p_ssl = p_ssl_save;
1014#endif
1015 if (envpat != NULL)
1016 pat = envpat;
1017 }
1018
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001019 cmd = skipwhite(cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001020 for (i = 0; i < 2; i++)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001021 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001022 if (*cmd == NUL)
1023 continue;
1024
1025 // Check for "++once" flag.
1026 if (STRNCMP(cmd, "++once", 6) == 0 && VIM_ISWHITE(cmd[6]))
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001027 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001028 if (once)
1029 semsg(_(e_duplicate_argument_str), "++once");
1030 once = TRUE;
1031 cmd = skipwhite(cmd + 6);
1032 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001033
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001034 // Check for "++nested" flag.
1035 if ((STRNCMP(cmd, "++nested", 8) == 0 && VIM_ISWHITE(cmd[8])))
1036 {
1037 if (nested)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001038 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001039 semsg(_(e_duplicate_argument_str), "++nested");
1040 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001041 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001042 nested = TRUE;
1043 cmd = skipwhite(cmd + 8);
1044 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001045
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001046 // Check for the old "nested" flag in legacy script.
1047 if (STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
1048 {
1049 if (in_vim9script())
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001050 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001051 // If there ever is a :nested command this error should
1052 // be removed and "nested" accepted as the start of the
1053 // command.
1054 emsg(_(e_invalid_command_nested_did_you_mean_plusplus_nested));
1055 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001056 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001057 if (nested)
1058 {
1059 semsg(_(e_duplicate_argument_str), "nested");
1060 return;
1061 }
1062 nested = TRUE;
1063 cmd = skipwhite(cmd + 6);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001064 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001065 }
1066
1067 /*
1068 * Find the start of the commands.
1069 * Expand <sfile> in it.
1070 */
1071 if (*cmd != NUL)
1072 {
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001073 if (eap != NULL)
1074 // Read a {} block if it follows.
1075 cmd = may_get_cmd_block(eap, cmd, &tofree, &flags);
1076
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001077 cmd = expand_sfile(cmd);
1078 if (cmd == NULL) // some error
1079 return;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001080 cmd_need_free = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001081 }
1082 }
1083
1084 /*
1085 * Print header when showing autocommands.
1086 */
1087 if (!forceit && *cmd == NUL)
1088 // Highlight title
1089 msg_puts_title(_("\n--- Autocommands ---"));
1090
1091 /*
1092 * Loop over the events.
1093 */
1094 last_event = (event_T)-1; // for listing the event name
1095 last_group = AUGROUP_ERROR; // for listing the group name
1096 if (*arg == '*' || *arg == NUL || *arg == '|')
1097 {
Bram Moolenaarb6db1462021-12-24 19:24:47 +00001098 if (*cmd != NUL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001099 emsg(_(e_cannot_define_autocommands_for_all_events));
1100 else
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +01001101 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001102 event = (event_T)((int)event + 1))
1103 if (do_autocmd_event(event, pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001104 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001105 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001106 }
1107 else
1108 {
1109 while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
1110 if (do_autocmd_event(event_name2nr(arg, &arg), pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001111 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001112 break;
1113 }
1114
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001115 if (cmd_need_free)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001116 vim_free(cmd);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001117 vim_free(tofree);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001118 vim_free(envpat);
1119}
1120
1121/*
1122 * Find the group ID in a ":autocmd" or ":doautocmd" argument.
1123 * The "argp" argument is advanced to the following argument.
1124 *
1125 * Returns the group ID, AUGROUP_ERROR for error (out of memory).
1126 */
1127 static int
1128au_get_grouparg(char_u **argp)
1129{
1130 char_u *group_name;
1131 char_u *p;
1132 char_u *arg = *argp;
1133 int group = AUGROUP_ALL;
1134
1135 for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p)
1136 ;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001137 if (p <= arg)
1138 return AUGROUP_ALL;
1139
1140 group_name = vim_strnsave(arg, p - arg);
1141 if (group_name == NULL) // out of memory
1142 return AUGROUP_ERROR;
1143 group = au_find_group(group_name);
1144 if (group == AUGROUP_ERROR)
1145 group = AUGROUP_ALL; // no match, use all groups
1146 else
1147 *argp = skipwhite(p); // match, skip over group name
1148 vim_free(group_name);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001149 return group;
1150}
1151
1152/*
1153 * do_autocmd() for one event.
1154 * If *pat == NUL do for all patterns.
1155 * If *cmd == NUL show entries.
1156 * If forceit == TRUE delete entries.
1157 * If group is not AUGROUP_ALL, only use this group.
1158 */
1159 static int
1160do_autocmd_event(
1161 event_T event,
1162 char_u *pat,
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001163 int once,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001164 int nested,
1165 char_u *cmd,
1166 int forceit,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001167 int group,
1168 int flags)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001169{
1170 AutoPat *ap;
1171 AutoPat **prev_ap;
1172 AutoCmd *ac;
1173 AutoCmd **prev_ac;
1174 int brace_level;
1175 char_u *endpat;
1176 int findgroup;
1177 int allgroups;
1178 int patlen;
1179 int is_buflocal;
1180 int buflocal_nr;
Bram Moolenaarc667da52019-11-30 20:52:27 +01001181 char_u buflocal_pat[25]; // for "<buffer=X>"
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001182
1183 if (group == AUGROUP_ALL)
1184 findgroup = current_augroup;
1185 else
1186 findgroup = group;
1187 allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
1188
1189 /*
1190 * Show or delete all patterns for an event.
1191 */
1192 if (*pat == NUL)
1193 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001194 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001195 {
1196 if (forceit) // delete the AutoPat, if it's in the current group
1197 {
1198 if (ap->group == findgroup)
1199 au_remove_pat(ap);
1200 }
1201 else if (group == AUGROUP_ALL || ap->group == group)
1202 show_autocmd(ap, event);
1203 }
1204 }
1205
1206 /*
1207 * Loop through all the specified patterns.
1208 */
1209 for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
1210 {
1211 /*
1212 * Find end of the pattern.
1213 * Watch out for a comma in braces, like "*.\{obj,o\}".
1214 */
1215 brace_level = 0;
1216 for (endpat = pat; *endpat && (*endpat != ',' || brace_level
1217 || (endpat > pat && endpat[-1] == '\\')); ++endpat)
1218 {
1219 if (*endpat == '{')
1220 brace_level++;
1221 else if (*endpat == '}')
1222 brace_level--;
1223 }
1224 if (pat == endpat) // ignore single comma
1225 continue;
1226 patlen = (int)(endpat - pat);
1227
1228 /*
1229 * detect special <buflocal[=X]> buffer-local patterns
1230 */
1231 is_buflocal = FALSE;
1232 buflocal_nr = 0;
1233
1234 if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
1235 && pat[patlen - 1] == '>')
1236 {
1237 // "<buffer...>": Error will be printed only for addition.
1238 // printing and removing will proceed silently.
1239 is_buflocal = TRUE;
1240 if (patlen == 8)
1241 // "<buffer>"
1242 buflocal_nr = curbuf->b_fnum;
1243 else if (patlen > 9 && pat[7] == '=')
1244 {
1245 if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0)
1246 // "<buffer=abuf>"
1247 buflocal_nr = autocmd_bufnr;
1248 else if (skipdigits(pat + 8) == pat + patlen - 1)
1249 // "<buffer=123>"
1250 buflocal_nr = atoi((char *)pat + 8);
1251 }
1252 }
1253
1254 if (is_buflocal)
1255 {
1256 // normalize pat into standard "<buffer>#N" form
1257 sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
1258 pat = buflocal_pat; // can modify pat and patlen
1259 patlen = (int)STRLEN(buflocal_pat); // but not endpat
1260 }
1261
1262 /*
1263 * Find AutoPat entries with this pattern. When adding a command it
1264 * always goes at or after the last one, so start at the end.
1265 */
1266 if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL)
1267 prev_ap = &last_autopat[(int)event];
1268 else
1269 prev_ap = &first_autopat[(int)event];
1270 while ((ap = *prev_ap) != NULL)
1271 {
1272 if (ap->pat != NULL)
1273 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001274 /*
1275 * Accept a pattern when:
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001276 * - a group was specified and it's that group, or a group was
1277 * not specified and it's the current group, or a group was
1278 * not specified and we are listing
1279 * - the length of the pattern matches
1280 * - the pattern matches.
1281 * For <buffer[=X]>, this condition works because we normalize
1282 * all buffer-local patterns.
1283 */
1284 if ((allgroups || ap->group == findgroup)
1285 && ap->patlen == patlen
1286 && STRNCMP(pat, ap->pat, patlen) == 0)
1287 {
1288 /*
1289 * Remove existing autocommands.
1290 * If adding any new autocmd's for this AutoPat, don't
1291 * delete the pattern from the autopat list, append to
1292 * this list.
1293 */
1294 if (forceit)
1295 {
1296 if (*cmd != NUL && ap->next == NULL)
1297 {
1298 au_remove_cmds(ap);
1299 break;
1300 }
1301 au_remove_pat(ap);
1302 }
1303
1304 /*
1305 * Show autocmd's for this autopat, or buflocals <buffer=X>
1306 */
1307 else if (*cmd == NUL)
1308 show_autocmd(ap, event);
1309
1310 /*
1311 * Add autocmd to this autopat, if it's the last one.
1312 */
1313 else if (ap->next == NULL)
1314 break;
1315 }
1316 }
1317 prev_ap = &ap->next;
1318 }
1319
1320 /*
1321 * Add a new command.
1322 */
1323 if (*cmd != NUL)
1324 {
1325 /*
1326 * If the pattern we want to add a command to does appear at the
1327 * end of the list (or not is not in the list at all), add the
1328 * pattern at the end of the list.
1329 */
1330 if (ap == NULL)
1331 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001332 // refuse to add buffer-local ap if buffer number is invalid
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001333 if (is_buflocal && (buflocal_nr == 0
1334 || buflist_findnr(buflocal_nr) == NULL))
1335 {
Bram Moolenaarf1474d82021-12-31 19:59:55 +00001336 semsg(_(e_buffer_nr_invalid_buffer_number), buflocal_nr);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001337 return FAIL;
1338 }
1339
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001340 ap = ALLOC_ONE(AutoPat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001341 if (ap == NULL)
1342 return FAIL;
1343 ap->pat = vim_strnsave(pat, patlen);
1344 ap->patlen = patlen;
1345 if (ap->pat == NULL)
1346 {
1347 vim_free(ap);
1348 return FAIL;
1349 }
1350
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001351#ifdef FEAT_EVAL
1352 // need to initialize last_mode for the first ModeChanged
1353 // autocmd
1354 if (event == EVENT_MODECHANGED && !has_modechanged())
LemonBoy2bf52dd2022-04-09 18:17:34 +01001355 get_mode(last_mode);
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001356#endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001357 // Initialize the fields checked by the WinScrolled and
1358 // WinResized trigger to prevent them from firing right after
1359 // the first autocmd is defined.
1360 if ((event == EVENT_WINSCROLLED || event == EVENT_WINRESIZED)
1361 && !(has_winscrolled() || has_winresized()))
LemonBoy09371822022-04-08 15:18:45 +01001362 {
Bram Moolenaar29967732022-11-20 12:11:45 +00001363 tabpage_T *save_curtab = curtab;
1364 tabpage_T *tp;
1365 FOR_ALL_TABPAGES(tp)
1366 {
1367 unuse_tabpage(curtab);
1368 use_tabpage(tp);
1369 snapshot_windows_scroll_size();
1370 }
1371 unuse_tabpage(curtab);
1372 use_tabpage(save_curtab);
LemonBoy09371822022-04-08 15:18:45 +01001373 }
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001374
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001375 if (is_buflocal)
1376 {
1377 ap->buflocal_nr = buflocal_nr;
1378 ap->reg_prog = NULL;
1379 }
1380 else
1381 {
1382 char_u *reg_pat;
1383
1384 ap->buflocal_nr = 0;
1385 reg_pat = file_pat_to_reg_pat(pat, endpat,
1386 &ap->allow_dirs, TRUE);
1387 if (reg_pat != NULL)
1388 ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
1389 vim_free(reg_pat);
1390 if (reg_pat == NULL || ap->reg_prog == NULL)
1391 {
1392 vim_free(ap->pat);
1393 vim_free(ap);
1394 return FAIL;
1395 }
1396 }
1397 ap->cmds = NULL;
1398 *prev_ap = ap;
1399 last_autopat[(int)event] = ap;
1400 ap->next = NULL;
1401 if (group == AUGROUP_ALL)
1402 ap->group = current_augroup;
1403 else
1404 ap->group = group;
1405 }
1406
1407 /*
1408 * Add the autocmd at the end of the AutoCmd list.
1409 */
1410 prev_ac = &(ap->cmds);
1411 while ((ac = *prev_ac) != NULL)
1412 prev_ac = &ac->next;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001413 ac = ALLOC_ONE(AutoCmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001414 if (ac == NULL)
1415 return FAIL;
1416 ac->cmd = vim_strsave(cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001417 ac->script_ctx = current_sctx;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001418 if (flags & UC_VIM9)
1419 ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001420#ifdef FEAT_EVAL
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001421 ac->script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001422#endif
1423 if (ac->cmd == NULL)
1424 {
1425 vim_free(ac);
1426 return FAIL;
1427 }
1428 ac->next = NULL;
1429 *prev_ac = ac;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001430 ac->once = once;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001431 ac->nested = nested;
1432 }
1433 }
1434
1435 au_cleanup(); // may really delete removed patterns/commands now
1436 return OK;
1437}
1438
1439/*
1440 * Implementation of ":doautocmd [group] event [fname]".
1441 * Return OK for success, FAIL for failure;
1442 */
1443 int
1444do_doautocmd(
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001445 char_u *arg_start,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001446 int do_msg, // give message for no matching autocmds?
1447 int *did_something)
1448{
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001449 char_u *arg = arg_start;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001450 char_u *fname;
1451 int nothing_done = TRUE;
1452 int group;
1453
1454 if (did_something != NULL)
1455 *did_something = FALSE;
1456
1457 /*
1458 * Check for a legal group name. If not, use AUGROUP_ALL.
1459 */
1460 group = au_get_grouparg(&arg);
1461 if (arg == NULL) // out of memory
1462 return FAIL;
1463
1464 if (*arg == '*')
1465 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001466 emsg(_(e_cant_execute_autocommands_for_all_events));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001467 return FAIL;
1468 }
1469
1470 /*
1471 * Scan over the events.
1472 * If we find an illegal name, return here, don't do anything.
1473 */
1474 fname = find_end_event(arg, group != AUGROUP_ALL);
1475 if (fname == NULL)
1476 return FAIL;
1477
1478 fname = skipwhite(fname);
1479
1480 /*
1481 * Loop over the events.
1482 */
1483 while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
1484 if (apply_autocmds_group(event_name2nr(arg, &arg),
1485 fname, NULL, TRUE, group, curbuf, NULL))
1486 nothing_done = FALSE;
1487
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001488 if (nothing_done && do_msg
1489#ifdef FEAT_EVAL
1490 && !aborting()
1491#endif
1492 )
1493 smsg(_("No matching autocommands: %s"), arg_start);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001494 if (did_something != NULL)
1495 *did_something = !nothing_done;
1496
1497#ifdef FEAT_EVAL
1498 return aborting() ? FAIL : OK;
1499#else
1500 return OK;
1501#endif
1502}
1503
1504/*
1505 * ":doautoall": execute autocommands for each loaded buffer.
1506 */
1507 void
1508ex_doautoall(exarg_T *eap)
1509{
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001510 int retval = OK;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001511 aco_save_T aco;
1512 buf_T *buf;
1513 bufref_T bufref;
1514 char_u *arg = eap->arg;
1515 int call_do_modelines = check_nomodeline(&arg);
1516 int did_aucmd;
1517
1518 /*
1519 * This is a bit tricky: For some commands curwin->w_buffer needs to be
1520 * equal to curbuf, but for some buffers there may not be a window.
1521 * So we change the buffer for the current window for a moment. This
1522 * gives problems when the autocommands make changes to the list of
1523 * buffers or windows...
1524 */
1525 FOR_ALL_BUFFERS(buf)
1526 {
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001527 // Only do loaded buffers and skip the current buffer, it's done last.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001528 if (buf->b_ml.ml_mfp == NULL || buf == curbuf)
1529 continue;
1530
Bram Moolenaare76062c2022-11-28 18:51:43 +00001531 // Find a window for this buffer and save some values.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001532 aucmd_prepbuf(&aco, buf);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001533 if (curbuf != buf)
1534 {
1535 // Failed to find a window for this buffer. Better not execute
1536 // autocommands then.
1537 retval = FAIL;
1538 break;
1539 }
1540
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001541 set_bufref(&bufref, buf);
1542
1543 // execute the autocommands for this buffer
1544 retval = do_doautocmd(arg, FALSE, &did_aucmd);
1545
1546 if (call_do_modelines && did_aucmd)
1547 // Execute the modeline settings, but don't set window-local
1548 // options if we are using the current window for another
1549 // buffer.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001550 do_modelines(is_aucmd_win(curwin) ? OPT_NOWIN : 0);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001551
1552 // restore the current window
1553 aucmd_restbuf(&aco);
1554
1555 // stop if there is some error or buffer was deleted
1556 if (retval == FAIL || !bufref_valid(&bufref))
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001557 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001558 retval = FAIL;
1559 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001560 }
1561 }
1562
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001563 // Execute autocommands for the current buffer last.
1564 if (retval == OK)
1565 {
1566 do_doautocmd(arg, FALSE, &did_aucmd);
1567 if (call_do_modelines && did_aucmd)
1568 do_modelines(0);
1569 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001570}
1571
1572/*
1573 * Check *argp for <nomodeline>. When it is present return FALSE, otherwise
1574 * return TRUE and advance *argp to after it.
1575 * Thus return TRUE when do_modelines() should be called.
1576 */
1577 int
1578check_nomodeline(char_u **argp)
1579{
1580 if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
1581 {
1582 *argp = skipwhite(*argp + 12);
1583 return FALSE;
1584 }
1585 return TRUE;
1586}
1587
1588/*
1589 * Prepare for executing autocommands for (hidden) buffer "buf".
1590 * Search for a visible window containing the current buffer. If there isn't
Bram Moolenaare76062c2022-11-28 18:51:43 +00001591 * one then use an entry in "aucmd_win[]".
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001592 * Set "curbuf" and "curwin" to match "buf".
Bram Moolenaare76062c2022-11-28 18:51:43 +00001593 * When this fails "curbuf" is not equal "buf".
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001594 */
1595 void
1596aucmd_prepbuf(
1597 aco_save_T *aco, // structure to save values in
1598 buf_T *buf) // new curbuf
1599{
1600 win_T *win;
1601 int save_ea;
1602#ifdef FEAT_AUTOCHDIR
1603 int save_acd;
1604#endif
1605
1606 // Find a window that is for the new buffer
1607 if (buf == curbuf) // be quick when buf is curbuf
1608 win = curwin;
1609 else
1610 FOR_ALL_WINDOWS(win)
1611 if (win->w_buffer == buf)
1612 break;
1613
Bram Moolenaare76062c2022-11-28 18:51:43 +00001614 // Allocate a window when needed.
1615 win_T *auc_win = NULL;
1616 int auc_idx = AUCMD_WIN_COUNT;
1617 if (win == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001618 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001619 for (auc_idx = 0; auc_idx < AUCMD_WIN_COUNT; ++auc_idx)
1620 if (!aucmd_win[auc_idx].auc_win_used)
1621 {
Bram Moolenaar84497cd2022-11-28 20:34:52 +00001622 if (aucmd_win[auc_idx].auc_win == NULL)
1623 aucmd_win[auc_idx].auc_win = win_alloc_popup_win();
1624 auc_win = aucmd_win[auc_idx].auc_win;
Bram Moolenaare76062c2022-11-28 18:51:43 +00001625 if (auc_win != NULL)
Bram Moolenaare76062c2022-11-28 18:51:43 +00001626 aucmd_win[auc_idx].auc_win_used = TRUE;
Bram Moolenaare76062c2022-11-28 18:51:43 +00001627 break;
1628 }
1629
1630 // If this fails (out of memory or using all AUCMD_WIN_COUNT
1631 // entries) then we can't reliable execute the autocmd, return with
1632 // "curbuf" unequal "buf".
1633 if (auc_win == NULL)
1634 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001635 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001636
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001637 aco->save_curwin_id = curwin->w_id;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001638 aco->save_prevwin_id = prevwin == NULL ? 0 : prevwin->w_id;
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001639 aco->save_State = State;
zeertzjqf2678472024-01-17 21:22:59 +01001640#ifdef FEAT_JOB_CHANNEL
1641 if (bt_prompt(curbuf))
1642 aco->save_prompt_insert = curbuf->b_prompt_insert;
1643#endif
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001644
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001645 if (win != NULL)
1646 {
1647 // There is a window for "buf" in the current tab page, make it the
1648 // curwin. This is preferred, it has the least side effects (esp. if
1649 // "buf" is curbuf).
Bram Moolenaare76062c2022-11-28 18:51:43 +00001650 aco->use_aucmd_win_idx = -1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001651 curwin = win;
1652 }
1653 else
1654 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001655 // There is no window for "buf", use "auc_win". To minimize the side
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001656 // effects, insert it in the current tab page.
1657 // Anything related to a window (e.g., setting folds) may have
1658 // unexpected results.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001659 aco->use_aucmd_win_idx = auc_idx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001660
Bram Moolenaare76062c2022-11-28 18:51:43 +00001661 win_init_popup_win(auc_win, buf);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001662
zeertzjq9d956ee2024-04-07 18:16:10 +02001663 // Make sure tp_localdir and globaldir are NULL to avoid a
1664 // chdir() in win_enter_ext().
1665 // win_init_popup_win() has already set w_localdir to NULL.
1666 aco->tp_localdir = curtab->tp_localdir;
1667 curtab->tp_localdir = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001668 aco->globaldir = globaldir;
1669 globaldir = NULL;
1670
Bram Moolenaare76062c2022-11-28 18:51:43 +00001671 // Split the current window, put the auc_win in the upper half.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001672 // We don't want the BufEnter or WinEnter autocommands.
1673 block_autocmds();
1674 make_snapshot(SNAP_AUCMD_IDX);
1675 save_ea = p_ea;
1676 p_ea = FALSE;
1677
1678#ifdef FEAT_AUTOCHDIR
1679 // Prevent chdir() call in win_enter_ext(), through do_autochdir().
1680 save_acd = p_acd;
1681 p_acd = FALSE;
1682#endif
1683
Sean Dewar704966c2024-02-20 22:00:33 +01001684 (void)win_split_ins(0, WSP_TOP | WSP_FORCE_ROOM, auc_win, 0, NULL);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001685 (void)win_comp_pos(); // recompute window positions
1686 p_ea = save_ea;
1687#ifdef FEAT_AUTOCHDIR
1688 p_acd = save_acd;
1689#endif
1690 unblock_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001691 curwin = auc_win;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001692 }
1693 curbuf = buf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001694 aco->new_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001695 set_bufref(&aco->new_curbuf, curbuf);
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001696
1697 // disable the Visual area, the position may be invalid in another buffer
1698 aco->save_VIsual_active = VIsual_active;
1699 VIsual_active = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001700}
1701
1702/*
1703 * Cleanup after executing autocommands for a (hidden) buffer.
1704 * Restore the window as it was (if possible).
1705 */
1706 void
1707aucmd_restbuf(
1708 aco_save_T *aco) // structure holding saved values
1709{
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001710 int dummy;
1711 win_T *save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001712
Bram Moolenaare76062c2022-11-28 18:51:43 +00001713 if (aco->use_aucmd_win_idx >= 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001714 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001715 win_T *awp = aucmd_win[aco->use_aucmd_win_idx].auc_win;
1716
Bram Moolenaare76062c2022-11-28 18:51:43 +00001717 // Find "awp", it can't be closed, but it may be in another tab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001718 // page. Do not trigger autocommands here.
1719 block_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001720 if (curwin != awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001721 {
1722 tabpage_T *tp;
1723 win_T *wp;
1724
1725 FOR_ALL_TAB_WINDOWS(tp, wp)
1726 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001727 if (wp == awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001728 {
1729 if (tp != curtab)
1730 goto_tabpage_tp(tp, TRUE, TRUE);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001731 win_goto(awp);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001732 goto win_found;
1733 }
1734 }
1735 }
1736win_found:
zeertzjq46bdae02023-09-24 23:16:08 +02001737 --curbuf->b_nwindows;
orbitalcde8de02023-04-02 22:05:13 +01001738#ifdef FEAT_JOB_CHANNEL
zeertzjqa40c0bc2023-05-27 14:10:08 +01001739 int save_stop_insert_mode = stop_insert_mode;
orbitalcde8de02023-04-02 22:05:13 +01001740 // May need to stop Insert mode if we were in a prompt buffer.
1741 leaving_window(curwin);
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001742 // Do not stop Insert mode when already in Insert mode before.
1743 if (aco->save_State & MODE_INSERT)
zeertzjqa40c0bc2023-05-27 14:10:08 +01001744 stop_insert_mode = save_stop_insert_mode;
orbitalcde8de02023-04-02 22:05:13 +01001745#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001746 // Remove the window and frame from the tree of frames.
Sean Dewar704966c2024-02-20 22:00:33 +01001747 (void)winframe_remove(curwin, &dummy, NULL, NULL);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001748 win_remove(curwin, NULL);
Bram Moolenaar84497cd2022-11-28 20:34:52 +00001749
1750 // The window is marked as not used, but it is not freed, it can be
1751 // used again.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001752 aucmd_win[aco->use_aucmd_win_idx].auc_win_used = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001753 last_status(FALSE); // may need to remove last status line
1754
1755 if (!valid_tabpage_win(curtab))
1756 // no valid window in current tabpage
1757 close_tabpage(curtab);
1758
1759 restore_snapshot(SNAP_AUCMD_IDX, FALSE);
1760 (void)win_comp_pos(); // recompute window positions
1761 unblock_autocmds();
1762
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001763 save_curwin = win_find_by_id(aco->save_curwin_id);
1764 if (save_curwin != NULL)
1765 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001766 else
1767 // Hmm, original window disappeared. Just use the first one.
1768 curwin = firstwin;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001769 curbuf = curwin->w_buffer;
1770#ifdef FEAT_JOB_CHANNEL
1771 // May need to restore insert mode for a prompt buffer.
1772 entering_window(curwin);
zeertzjqf2678472024-01-17 21:22:59 +01001773 if (bt_prompt(curbuf))
1774 curbuf->b_prompt_insert = aco->save_prompt_insert;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001775#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001776 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001777#ifdef FEAT_EVAL
Bram Moolenaare76062c2022-11-28 18:51:43 +00001778 vars_clear(&awp->w_vars->dv_hashtab); // free all w: variables
1779 hash_init(&awp->w_vars->dv_hashtab); // re-use the hashtab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001780#endif
zeertzjq9d956ee2024-04-07 18:16:10 +02001781 // If :lcd has been used in the autocommand window, correct current
1782 // directory before restoring tp_localdir and globaldir.
1783 if (awp->w_localdir != NULL)
1784 win_fix_current_dir();
1785 vim_free(curtab->tp_localdir);
1786 curtab->tp_localdir = aco->tp_localdir;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001787 vim_free(globaldir);
1788 globaldir = aco->globaldir;
1789
1790 // the buffer contents may have changed
zeertzjq49f05242023-02-04 10:58:34 +00001791 VIsual_active = aco->save_VIsual_active;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001792 check_cursor();
1793 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1794 {
1795 curwin->w_topline = curbuf->b_ml.ml_line_count;
1796#ifdef FEAT_DIFF
1797 curwin->w_topfill = 0;
1798#endif
1799 }
1800#if defined(FEAT_GUI)
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001801 if (gui.in_use)
1802 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001803 // Hide the scrollbars from the "awp" and update.
1804 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_LEFT], FALSE);
1805 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_RIGHT], FALSE);
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001806 gui_may_update_scrollbars();
1807 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001808#endif
1809 }
1810 else
1811 {
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001812 // Restore curwin. Use the window ID, a window may have been closed
1813 // and the memory re-used for another one.
1814 save_curwin = win_find_by_id(aco->save_curwin_id);
1815 if (save_curwin != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001816 {
1817 // Restore the buffer which was previously edited by curwin, if
1818 // it was changed, we are still the same window and the buffer is
1819 // valid.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001820 if (curwin->w_id == aco->new_curwin_id
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001821 && curbuf != aco->new_curbuf.br_buf
1822 && bufref_valid(&aco->new_curbuf)
1823 && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
1824 {
1825# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
1826 if (curwin->w_s == &curbuf->b_s)
1827 curwin->w_s = &aco->new_curbuf.br_buf->b_s;
1828# endif
1829 --curbuf->b_nwindows;
1830 curbuf = aco->new_curbuf.br_buf;
1831 curwin->w_buffer = curbuf;
1832 ++curbuf->b_nwindows;
1833 }
1834
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001835 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001836 curbuf = curwin->w_buffer;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001837 prevwin = win_find_by_id(aco->save_prevwin_id);
zeertzjq49f05242023-02-04 10:58:34 +00001838
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001839 // In case the autocommand moves the cursor to a position that
1840 // does not exist in curbuf.
zeertzjq49f05242023-02-04 10:58:34 +00001841 VIsual_active = aco->save_VIsual_active;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001842 check_cursor();
1843 }
1844 }
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001845
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001846 VIsual_active = aco->save_VIsual_active;
zeertzjq49f05242023-02-04 10:58:34 +00001847 check_cursor(); // just in case lines got deleted
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001848 if (VIsual_active)
1849 check_pos(curbuf, &VIsual);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001850}
1851
1852static int autocmd_nested = FALSE;
1853
1854/*
1855 * Execute autocommands for "event" and file name "fname".
1856 * Return TRUE if some commands were executed.
1857 */
1858 int
1859apply_autocmds(
1860 event_T event,
1861 char_u *fname, // NULL or empty means use actual file name
1862 char_u *fname_io, // fname to use for <afile> on cmdline
1863 int force, // when TRUE, ignore autocmd_busy
1864 buf_T *buf) // buffer for <abuf>
1865{
1866 return apply_autocmds_group(event, fname, fname_io, force,
1867 AUGROUP_ALL, buf, NULL);
1868}
1869
1870/*
1871 * Like apply_autocmds(), but with extra "eap" argument. This takes care of
1872 * setting v:filearg.
1873 */
1874 int
1875apply_autocmds_exarg(
1876 event_T event,
1877 char_u *fname,
1878 char_u *fname_io,
1879 int force,
1880 buf_T *buf,
1881 exarg_T *eap)
1882{
1883 return apply_autocmds_group(event, fname, fname_io, force,
1884 AUGROUP_ALL, buf, eap);
1885}
1886
1887/*
1888 * Like apply_autocmds(), but handles the caller's retval. If the script
1889 * processing is being aborted or if retval is FAIL when inside a try
1890 * conditional, no autocommands are executed. If otherwise the autocommands
1891 * cause the script to be aborted, retval is set to FAIL.
1892 */
1893 int
1894apply_autocmds_retval(
1895 event_T event,
1896 char_u *fname, // NULL or empty means use actual file name
1897 char_u *fname_io, // fname to use for <afile> on cmdline
1898 int force, // when TRUE, ignore autocmd_busy
1899 buf_T *buf, // buffer for <abuf>
1900 int *retval) // pointer to caller's retval
1901{
1902 int did_cmd;
1903
1904#ifdef FEAT_EVAL
1905 if (should_abort(*retval))
1906 return FALSE;
1907#endif
1908
1909 did_cmd = apply_autocmds_group(event, fname, fname_io, force,
1910 AUGROUP_ALL, buf, NULL);
1911 if (did_cmd
1912#ifdef FEAT_EVAL
1913 && aborting()
1914#endif
1915 )
1916 *retval = FAIL;
1917 return did_cmd;
1918}
1919
1920/*
1921 * Return TRUE when there is a CursorHold autocommand defined.
1922 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001923 static int
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001924has_cursorhold(void)
1925{
Bram Moolenaar24959102022-05-07 20:01:16 +01001926 return (first_autopat[(int)(get_real_state() == MODE_NORMAL_BUSY
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001927 ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
1928}
1929
1930/*
1931 * Return TRUE if the CursorHold event can be triggered.
1932 */
1933 int
1934trigger_cursorhold(void)
1935{
1936 int state;
1937
1938 if (!did_cursorhold
1939 && has_cursorhold()
1940 && reg_recording == 0
1941 && typebuf.tb_len == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001942 && !ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001943 {
1944 state = get_real_state();
Bram Moolenaar24959102022-05-07 20:01:16 +01001945 if (state == MODE_NORMAL_BUSY || (state & MODE_INSERT) != 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001946 return TRUE;
1947 }
1948 return FALSE;
1949}
1950
1951/*
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001952 * Return TRUE when there is a WinResized autocommand defined.
1953 */
1954 int
1955has_winresized(void)
1956{
1957 return (first_autopat[(int)EVENT_WINRESIZED] != NULL);
1958}
1959
1960/*
LemonBoy09371822022-04-08 15:18:45 +01001961 * Return TRUE when there is a WinScrolled autocommand defined.
1962 */
1963 int
1964has_winscrolled(void)
1965{
1966 return (first_autopat[(int)EVENT_WINSCROLLED] != NULL);
1967}
1968
1969/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001970 * Return TRUE when there is a CursorMoved autocommand defined.
1971 */
1972 int
1973has_cursormoved(void)
1974{
1975 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1976}
1977
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001978/*
1979 * Return TRUE when there is a CursorMovedI autocommand defined.
1980 */
1981 int
1982has_cursormovedI(void)
1983{
1984 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1985}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001986
1987/*
1988 * Return TRUE when there is a TextChanged autocommand defined.
1989 */
1990 int
1991has_textchanged(void)
1992{
1993 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1994}
1995
1996/*
1997 * Return TRUE when there is a TextChangedI autocommand defined.
1998 */
1999 int
2000has_textchangedI(void)
2001{
2002 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
2003}
2004
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002005/*
2006 * Return TRUE when there is a TextChangedP autocommand defined.
2007 */
2008 int
2009has_textchangedP(void)
2010{
2011 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
2012}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002013
2014/*
2015 * Return TRUE when there is an InsertCharPre autocommand defined.
2016 */
2017 int
2018has_insertcharpre(void)
2019{
2020 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
2021}
2022
2023/*
2024 * Return TRUE when there is an CmdUndefined autocommand defined.
2025 */
2026 int
2027has_cmdundefined(void)
2028{
2029 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
2030}
2031
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002032#if defined(FEAT_EVAL) || defined(PROTO)
2033/*
2034 * Return TRUE when there is a TextYankPost autocommand defined.
2035 */
2036 int
2037has_textyankpost(void)
2038{
2039 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
2040}
2041#endif
2042
Bram Moolenaard7f246c2019-04-08 18:15:41 +02002043#if defined(FEAT_EVAL) || defined(PROTO)
2044/*
2045 * Return TRUE when there is a CompleteChanged autocommand defined.
2046 */
2047 int
2048has_completechanged(void)
2049{
2050 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
2051}
2052#endif
2053
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002054#if defined(FEAT_EVAL) || defined(PROTO)
2055/*
2056 * Return TRUE when there is a ModeChanged autocommand defined.
2057 */
2058 int
2059has_modechanged(void)
2060{
2061 return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
2062}
2063#endif
2064
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002065/*
2066 * Execute autocommands for "event" and file name "fname".
2067 * Return TRUE if some commands were executed.
2068 */
2069 static int
2070apply_autocmds_group(
2071 event_T event,
2072 char_u *fname, // NULL or empty means use actual file name
2073 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
2074 // use fname
2075 int force, // when TRUE, ignore autocmd_busy
2076 int group, // group ID, or AUGROUP_ALL
2077 buf_T *buf, // buffer for <abuf>
2078 exarg_T *eap UNUSED) // command arguments
2079{
2080 char_u *sfname = NULL; // short file name
2081 char_u *tail;
2082 int save_changed;
2083 buf_T *old_curbuf;
2084 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002085 char_u *save_autocmd_fname;
2086 int save_autocmd_fname_full;
2087 int save_autocmd_bufnr;
2088 char_u *save_autocmd_match;
2089 int save_autocmd_busy;
2090 int save_autocmd_nested;
2091 static int nesting = 0;
LemonBoyeca7c602022-04-14 15:39:43 +01002092 AutoPatCmd_T patcmd;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002093 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002094 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002095#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002096 funccal_entry_T funccal_entry;
2097 char_u *save_cmdarg;
2098 long save_cmdbang;
2099#endif
2100 static int filechangeshell_busy = FALSE;
2101#ifdef FEAT_PROFILE
2102 proftime_T wait_time;
2103#endif
2104 int did_save_redobuff = FALSE;
2105 save_redo_T save_redo;
2106 int save_KeyTyped = KeyTyped;
ichizok7e5fe382023-04-15 13:17:50 +01002107 ESTACK_CHECK_DECLARATION;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002108
2109 /*
2110 * Quickly return if there are no autocommands for this event or
2111 * autocommands are blocked.
2112 */
2113 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
2114 || autocmd_blocked > 0)
2115 goto BYPASS_AU;
2116
2117 /*
2118 * When autocommands are busy, new autocommands are only executed when
2119 * explicitly enabled with the "nested" flag.
2120 */
2121 if (autocmd_busy && !(force || autocmd_nested))
2122 goto BYPASS_AU;
2123
2124#ifdef FEAT_EVAL
2125 /*
2126 * Quickly return when immediately aborting on error, or when an interrupt
2127 * occurred or an exception was thrown but not caught.
2128 */
2129 if (aborting())
2130 goto BYPASS_AU;
2131#endif
2132
2133 /*
2134 * FileChangedShell never nests, because it can create an endless loop.
2135 */
2136 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
2137 || event == EVENT_FILECHANGEDSHELLPOST))
2138 goto BYPASS_AU;
2139
2140 /*
2141 * Ignore events in 'eventignore'.
2142 */
2143 if (event_ignored(event))
2144 goto BYPASS_AU;
2145
2146 /*
2147 * Allow nesting of autocommands, but restrict the depth, because it's
2148 * possible to create an endless loop.
2149 */
2150 if (nesting == 10)
2151 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00002152 emsg(_(e_autocommand_nesting_too_deep));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002153 goto BYPASS_AU;
2154 }
2155
2156 /*
2157 * Check if these autocommands are disabled. Used when doing ":all" or
2158 * ":ball".
2159 */
2160 if ( (autocmd_no_enter
2161 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
2162 || (autocmd_no_leave
2163 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
2164 goto BYPASS_AU;
2165
Bram Moolenaarbb393d82022-12-09 12:21:50 +00002166 if (event == EVENT_CMDLINECHANGED)
2167 ++aucmd_cmdline_changed_count;
2168
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002169 /*
2170 * Save the autocmd_* variables and info about the current buffer.
2171 */
2172 save_autocmd_fname = autocmd_fname;
2173 save_autocmd_fname_full = autocmd_fname_full;
2174 save_autocmd_bufnr = autocmd_bufnr;
2175 save_autocmd_match = autocmd_match;
2176 save_autocmd_busy = autocmd_busy;
2177 save_autocmd_nested = autocmd_nested;
2178 save_changed = curbuf->b_changed;
2179 old_curbuf = curbuf;
2180
2181 /*
2182 * Set the file name to be used for <afile>.
2183 * Make a copy to avoid that changing a buffer name or directory makes it
2184 * invalid.
2185 */
2186 if (fname_io == NULL)
2187 {
2188 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
Bram Moolenaarbb393d82022-12-09 12:21:50 +00002189 || event == EVENT_OPTIONSET
Danek Duvalld7d56032024-01-14 20:19:59 +01002190 || event == EVENT_MODECHANGED
2191 || event == EVENT_TERMRESPONSEALL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002192 autocmd_fname = NULL;
2193 else if (fname != NULL && !ends_excmd(*fname))
2194 autocmd_fname = fname;
2195 else if (buf != NULL)
2196 autocmd_fname = buf->b_ffname;
2197 else
2198 autocmd_fname = NULL;
2199 }
2200 else
2201 autocmd_fname = fname_io;
2202 if (autocmd_fname != NULL)
2203 autocmd_fname = vim_strsave(autocmd_fname);
2204 autocmd_fname_full = FALSE; // call FullName_save() later
2205
2206 /*
2207 * Set the buffer number to be used for <abuf>.
2208 */
2209 if (buf == NULL)
2210 autocmd_bufnr = 0;
2211 else
2212 autocmd_bufnr = buf->b_fnum;
2213
2214 /*
2215 * When the file name is NULL or empty, use the file name of buffer "buf".
2216 * Always use the full path of the file name to match with, in case
2217 * "allow_dirs" is set.
2218 */
2219 if (fname == NULL || *fname == NUL)
2220 {
2221 if (buf == NULL)
2222 fname = NULL;
2223 else
2224 {
2225#ifdef FEAT_SYN_HL
2226 if (event == EVENT_SYNTAX)
2227 fname = buf->b_p_syn;
2228 else
2229#endif
2230 if (event == EVENT_FILETYPE)
2231 fname = buf->b_p_ft;
2232 else
2233 {
2234 if (buf->b_sfname != NULL)
2235 sfname = vim_strsave(buf->b_sfname);
2236 fname = buf->b_ffname;
2237 }
2238 }
2239 if (fname == NULL)
2240 fname = (char_u *)"";
2241 fname = vim_strsave(fname); // make a copy, so we can change it
2242 }
2243 else
2244 {
2245 sfname = vim_strsave(fname);
2246 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002247 // ColorScheme, QuickFixCmd*, DirChanged and similar.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002248 if (event == EVENT_FILETYPE
2249 || event == EVENT_SYNTAX
2250 || event == EVENT_CMDLINECHANGED
2251 || event == EVENT_CMDLINEENTER
2252 || event == EVENT_CMDLINELEAVE
2253 || event == EVENT_CMDWINENTER
2254 || event == EVENT_CMDWINLEAVE
2255 || event == EVENT_CMDUNDEFINED
2256 || event == EVENT_FUNCUNDEFINED
2257 || event == EVENT_REMOTEREPLY
2258 || event == EVENT_SPELLFILEMISSING
2259 || event == EVENT_QUICKFIXCMDPRE
2260 || event == EVENT_COLORSCHEME
2261 || event == EVENT_COLORSCHEMEPRE
2262 || event == EVENT_OPTIONSET
2263 || event == EVENT_QUICKFIXCMDPOST
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002264 || event == EVENT_DIRCHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002265 || event == EVENT_DIRCHANGEDPRE
naohiro ono23beefe2021-11-13 12:38:49 +00002266 || event == EVENT_MODECHANGED
zeertzjqc601d982022-10-10 13:46:15 +01002267 || event == EVENT_MENUPOPUP
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002268 || event == EVENT_USER
LemonBoy09371822022-04-08 15:18:45 +01002269 || event == EVENT_WINCLOSED
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00002270 || event == EVENT_WINRESIZED
Danek Duvalld7d56032024-01-14 20:19:59 +01002271 || event == EVENT_WINSCROLLED
2272 || event == EVENT_TERMRESPONSEALL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002273 {
2274 fname = vim_strsave(fname);
2275 autocmd_fname_full = TRUE; // don't expand it later
2276 }
2277 else
2278 fname = FullName_save(fname, FALSE);
2279 }
2280 if (fname == NULL) // out of memory
2281 {
2282 vim_free(sfname);
2283 retval = FALSE;
2284 goto BYPASS_AU;
2285 }
2286
2287#ifdef BACKSLASH_IN_FILENAME
2288 /*
2289 * Replace all backslashes with forward slashes. This makes the
2290 * autocommand patterns portable between Unix and MS-DOS.
2291 */
2292 if (sfname != NULL)
2293 forward_slash(sfname);
2294 forward_slash(fname);
2295#endif
2296
2297#ifdef VMS
2298 // remove version for correct match
2299 if (sfname != NULL)
2300 vms_remove_version(sfname);
2301 vms_remove_version(fname);
2302#endif
2303
2304 /*
2305 * Set the name to be used for <amatch>.
2306 */
2307 autocmd_match = fname;
2308
2309
2310 // Don't redraw while doing autocommands.
2311 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002312
2313 // name and lnum are filled in later
2314 estack_push(ETYPE_AUCMD, NULL, 0);
ichizok7e5fe382023-04-15 13:17:50 +01002315 ESTACK_CHECK_SETUP;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002316
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002317 save_current_sctx = current_sctx;
2318
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002319#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002320# ifdef FEAT_PROFILE
2321 if (do_profiling == PROF_YES)
2322 prof_child_enter(&wait_time); // doesn't count for the caller itself
2323# endif
2324
2325 // Don't use local function variables, if called from a function.
2326 save_funccal(&funccal_entry);
2327#endif
2328
2329 /*
2330 * When starting to execute autocommands, save the search patterns.
2331 */
2332 if (!autocmd_busy)
2333 {
2334 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002335 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002336 {
2337 saveRedobuff(&save_redo);
2338 did_save_redobuff = TRUE;
2339 }
zeertzjq5bf6c212024-03-31 18:41:27 +02002340 curbuf->b_did_filetype = curbuf->b_keep_filetype;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002341 }
2342
2343 /*
2344 * Note that we are applying autocmds. Some commands need to know.
2345 */
2346 autocmd_busy = TRUE;
2347 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2348 ++nesting; // see matching decrement below
2349
2350 // Remember that FileType was triggered. Used for did_filetype().
2351 if (event == EVENT_FILETYPE)
zeertzjq5bf6c212024-03-31 18:41:27 +02002352 curbuf->b_did_filetype = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002353
2354 tail = gettail(fname);
2355
2356 // Find first autocommand that matches
LemonBoyeca7c602022-04-14 15:39:43 +01002357 CLEAR_FIELD(patcmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002358 patcmd.curpat = first_autopat[(int)event];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002359 patcmd.group = group;
2360 patcmd.fname = fname;
2361 patcmd.sfname = sfname;
2362 patcmd.tail = tail;
2363 patcmd.event = event;
2364 patcmd.arg_bufnr = autocmd_bufnr;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002365 auto_next_pat(&patcmd, FALSE);
2366
2367 // found one, start executing the autocommands
2368 if (patcmd.curpat != NULL)
2369 {
2370 // add to active_apc_list
2371 patcmd.next = active_apc_list;
2372 active_apc_list = &patcmd;
2373
2374#ifdef FEAT_EVAL
2375 // set v:cmdarg (only when there is a matching pattern)
2376 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2377 if (eap != NULL)
2378 {
2379 save_cmdarg = set_cmdarg(eap, NULL);
2380 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2381 }
2382 else
2383 save_cmdarg = NULL; // avoid gcc warning
2384#endif
2385 retval = TRUE;
2386 // mark the last pattern, to avoid an endless loop when more patterns
2387 // are added when executing autocommands
2388 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2389 ap->last = FALSE;
2390 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002391
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002392 // Make sure cursor and topline are valid. The first time the current
2393 // values are saved, restored by reset_lnums(). When nested only the
2394 // values are corrected when needed.
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002395 if (nesting == 1)
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002396 check_lnums(TRUE);
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002397 else
2398 check_lnums_nested(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002399
Christian Brabandt590aae32023-06-25 22:34:22 +01002400 int save_did_emsg = did_emsg;
2401 int save_ex_pressedreturn = get_pressedreturn();
ichizokc3f91c02021-12-17 09:44:33 +00002402
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002403 do_cmdline(NULL, getnextac, (void *)&patcmd,
2404 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002405
ichizokc3f91c02021-12-17 09:44:33 +00002406 did_emsg += save_did_emsg;
Christian Brabandt590aae32023-06-25 22:34:22 +01002407 set_pressedreturn(save_ex_pressedreturn);
ichizokc3f91c02021-12-17 09:44:33 +00002408
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002409 if (nesting == 1)
2410 // restore cursor and topline, unless they were changed
2411 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002412
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002413#ifdef FEAT_EVAL
2414 if (eap != NULL)
2415 {
2416 (void)set_cmdarg(NULL, save_cmdarg);
2417 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2418 }
2419#endif
2420 // delete from active_apc_list
2421 if (active_apc_list == &patcmd) // just in case
2422 active_apc_list = patcmd.next;
2423 }
2424
Bram Moolenaar79cdf022023-05-20 14:07:00 +01002425 if (RedrawingDisabled > 0)
2426 --RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002427 autocmd_busy = save_autocmd_busy;
2428 filechangeshell_busy = FALSE;
2429 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002430 vim_free(SOURCING_NAME);
ichizok7e5fe382023-04-15 13:17:50 +01002431 ESTACK_CHECK_NOW;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002432 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002433 vim_free(autocmd_fname);
2434 autocmd_fname = save_autocmd_fname;
2435 autocmd_fname_full = save_autocmd_fname_full;
2436 autocmd_bufnr = save_autocmd_bufnr;
2437 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002438 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002439#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002440 restore_funccal();
2441# ifdef FEAT_PROFILE
2442 if (do_profiling == PROF_YES)
2443 prof_child_exit(&wait_time);
2444# endif
2445#endif
2446 KeyTyped = save_KeyTyped;
2447 vim_free(fname);
2448 vim_free(sfname);
2449 --nesting; // see matching increment above
2450
2451 /*
2452 * When stopping to execute autocommands, restore the search patterns and
2453 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2454 * free any windows in the au_pending_free_win list.
2455 */
2456 if (!autocmd_busy)
2457 {
2458 restore_search_patterns();
2459 if (did_save_redobuff)
2460 restoreRedobuff(&save_redo);
zeertzjq5bf6c212024-03-31 18:41:27 +02002461 curbuf->b_did_filetype = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002462 while (au_pending_free_buf != NULL)
2463 {
2464 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002465
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002466 vim_free(au_pending_free_buf);
2467 au_pending_free_buf = b;
2468 }
2469 while (au_pending_free_win != NULL)
2470 {
2471 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002472
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002473 vim_free(au_pending_free_win);
2474 au_pending_free_win = w;
2475 }
2476 }
2477
2478 /*
2479 * Some events don't set or reset the Changed flag.
2480 * Check if still in the same buffer!
2481 */
2482 if (curbuf == old_curbuf
2483 && (event == EVENT_BUFREADPOST
2484 || event == EVENT_BUFWRITEPOST
2485 || event == EVENT_FILEAPPENDPOST
2486 || event == EVENT_VIMLEAVE
2487 || event == EVENT_VIMLEAVEPRE))
2488 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002489 if (curbuf->b_changed != save_changed)
2490 need_maketitle = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002491 curbuf->b_changed = save_changed;
2492 }
2493
2494 au_cleanup(); // may really delete removed patterns/commands now
2495
2496BYPASS_AU:
2497 // When wiping out a buffer make sure all its buffer-local autocommands
2498 // are deleted.
2499 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2500 aubuflocal_remove(buf);
2501
2502 if (retval == OK && event == EVENT_FILETYPE)
zeertzjq5bf6c212024-03-31 18:41:27 +02002503 curbuf->b_au_did_filetype = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002504
2505 return retval;
2506}
2507
2508# ifdef FEAT_EVAL
2509static char_u *old_termresponse = NULL;
Danek Duvalld7d56032024-01-14 20:19:59 +01002510static char_u *old_termu7resp = NULL;
2511static char_u *old_termblinkresp = NULL;
2512static char_u *old_termrbgresp = NULL;
2513static char_u *old_termrfgresp = NULL;
2514static char_u *old_termstyleresp = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002515# endif
2516
2517/*
2518 * Block triggering autocommands until unblock_autocmd() is called.
2519 * Can be used recursively, so long as it's symmetric.
2520 */
2521 void
2522block_autocmds(void)
2523{
2524# ifdef FEAT_EVAL
2525 // Remember the value of v:termresponse.
2526 if (autocmd_blocked == 0)
Danek Duvalld7d56032024-01-14 20:19:59 +01002527 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002528 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
Danek Duvalld7d56032024-01-14 20:19:59 +01002529 old_termu7resp = get_vim_var_str(VV_TERMU7RESP);
2530 old_termblinkresp = get_vim_var_str(VV_TERMBLINKRESP);
2531 old_termrbgresp = get_vim_var_str(VV_TERMRBGRESP);
2532 old_termrfgresp = get_vim_var_str(VV_TERMRFGRESP);
2533 old_termstyleresp = get_vim_var_str(VV_TERMSTYLERESP);
2534 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002535# endif
2536 ++autocmd_blocked;
2537}
2538
2539 void
2540unblock_autocmds(void)
2541{
2542 --autocmd_blocked;
2543
2544# ifdef FEAT_EVAL
Danek Duvalld7d56032024-01-14 20:19:59 +01002545 // When v:termresponse, etc, were set while autocommands were blocked,
2546 // trigger the autocommands now. Esp. useful when executing a shell
2547 // command during startup (vimdiff).
2548 if (autocmd_blocked == 0)
2549 {
2550 if (get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2551 {
2552 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2553 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"version", NULL, FALSE, curbuf);
2554 }
2555 if (get_vim_var_str(VV_TERMU7RESP) != old_termu7resp)
2556 {
2557 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"ambiguouswidth", NULL, FALSE, curbuf);
2558 }
2559 if (get_vim_var_str(VV_TERMBLINKRESP) != old_termblinkresp)
2560 {
2561 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"cursorblink", NULL, FALSE, curbuf);
2562 }
2563 if (get_vim_var_str(VV_TERMRBGRESP) != old_termrbgresp)
2564 {
2565 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"background", NULL, FALSE, curbuf);
2566 }
2567 if (get_vim_var_str(VV_TERMRFGRESP) != old_termrfgresp)
2568 {
2569 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"foreground", NULL, FALSE, curbuf);
2570 }
2571 if (get_vim_var_str(VV_TERMSTYLERESP) != old_termstyleresp)
2572 {
2573 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"cursorshape", NULL, FALSE, curbuf);
2574 }
2575 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002576# endif
2577}
2578
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002579 int
2580is_autocmd_blocked(void)
2581{
2582 return autocmd_blocked != 0;
2583}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002584
2585/*
2586 * Find next autocommand pattern that matches.
2587 */
2588 static void
2589auto_next_pat(
LemonBoyeca7c602022-04-14 15:39:43 +01002590 AutoPatCmd_T *apc,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002591 int stop_at_last) // stop when 'last' flag is set
2592{
2593 AutoPat *ap;
2594 AutoCmd *cp;
2595 char_u *name;
2596 char *s;
LemonBoyeca7c602022-04-14 15:39:43 +01002597 estack_T *entry;
2598 char_u *namep;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002599
LemonBoyeca7c602022-04-14 15:39:43 +01002600 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
2601
2602 // Clear the exestack entry for this ETYPE_AUCMD entry.
2603 VIM_CLEAR(entry->es_name);
2604 entry->es_info.aucmd = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002605
2606 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2607 {
2608 apc->curpat = NULL;
2609
2610 // Only use a pattern when it has not been removed, has commands and
2611 // the group matches. For buffer-local autocommands only check the
2612 // buffer number.
2613 if (ap->pat != NULL && ap->cmds != NULL
2614 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2615 {
2616 // execution-condition
2617 if (ap->buflocal_nr == 0
2618 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2619 apc->sfname, apc->tail, ap->allow_dirs))
2620 : ap->buflocal_nr == apc->arg_bufnr)
2621 {
2622 name = event_nr2name(apc->event);
2623 s = _("%s Autocommands for \"%s\"");
LemonBoyeca7c602022-04-14 15:39:43 +01002624 namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1);
2625 if (namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002626 {
LemonBoyeca7c602022-04-14 15:39:43 +01002627 sprintf((char *)namep, s, (char *)name, (char *)ap->pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002628 if (p_verbose >= 8)
2629 {
2630 verbose_enter();
LemonBoyeca7c602022-04-14 15:39:43 +01002631 smsg(_("Executing %s"), namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002632 verbose_leave();
2633 }
2634 }
2635
LemonBoyeca7c602022-04-14 15:39:43 +01002636 // Update the exestack entry for this autocmd.
2637 entry->es_name = namep;
2638 entry->es_info.aucmd = apc;
2639
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002640 apc->curpat = ap;
2641 apc->nextcmd = ap->cmds;
2642 // mark last command
2643 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2644 cp->last = FALSE;
2645 cp->last = TRUE;
2646 }
2647 line_breakcheck();
2648 if (apc->curpat != NULL) // found a match
2649 break;
2650 }
2651 if (stop_at_last && ap->last)
2652 break;
2653 }
2654}
2655
Dominique Pellee764d1b2023-03-12 21:20:59 +00002656#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002657/*
LemonBoyeca7c602022-04-14 15:39:43 +01002658 * Get the script context where autocommand "acp" is defined.
2659 */
2660 sctx_T *
2661acp_script_ctx(AutoPatCmd_T *acp)
2662{
2663 return &acp->script_ctx;
2664}
Dominique Pellee764d1b2023-03-12 21:20:59 +00002665#endif
LemonBoyeca7c602022-04-14 15:39:43 +01002666
2667/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002668 * Get next autocommand command.
2669 * Called by do_cmdline() to get the next line for ":if".
2670 * Returns allocated string, or NULL for end of autocommands.
2671 */
2672 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002673getnextac(
2674 int c UNUSED,
2675 void *cookie,
2676 int indent UNUSED,
2677 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002678{
LemonBoyeca7c602022-04-14 15:39:43 +01002679 AutoPatCmd_T *acp = (AutoPatCmd_T *)cookie;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002680 char_u *retval;
2681 AutoCmd *ac;
2682
2683 // Can be called again after returning the last line.
2684 if (acp->curpat == NULL)
2685 return NULL;
2686
2687 // repeat until we find an autocommand to execute
2688 for (;;)
2689 {
2690 // skip removed commands
2691 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2692 if (acp->nextcmd->last)
2693 acp->nextcmd = NULL;
2694 else
2695 acp->nextcmd = acp->nextcmd->next;
2696
2697 if (acp->nextcmd != NULL)
2698 break;
2699
2700 // at end of commands, find next pattern that matches
2701 if (acp->curpat->last)
2702 acp->curpat = NULL;
2703 else
2704 acp->curpat = acp->curpat->next;
2705 if (acp->curpat != NULL)
2706 auto_next_pat(acp, TRUE);
2707 if (acp->curpat == NULL)
2708 return NULL;
2709 }
2710
2711 ac = acp->nextcmd;
2712
2713 if (p_verbose >= 9)
2714 {
2715 verbose_enter_scroll();
2716 smsg(_("autocommand %s"), ac->cmd);
2717 msg_puts("\n"); // don't overwrite this either
2718 verbose_leave_scroll();
2719 }
2720 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002721 // Remove one-shot ("once") autocmd in anticipation of its execution.
2722 if (ac->once)
2723 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002724 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002725 current_sctx = ac->script_ctx;
LemonBoyeca7c602022-04-14 15:39:43 +01002726 acp->script_ctx = current_sctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002727 if (ac->last)
2728 acp->nextcmd = NULL;
2729 else
2730 acp->nextcmd = ac->next;
2731 return retval;
2732}
2733
2734/*
2735 * Return TRUE if there is a matching autocommand for "fname".
2736 * To account for buffer-local autocommands, function needs to know
2737 * in which buffer the file will be opened.
2738 */
2739 int
2740has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2741{
2742 AutoPat *ap;
2743 char_u *fname;
2744 char_u *tail = gettail(sfname);
2745 int retval = FALSE;
2746
2747 fname = FullName_save(sfname, FALSE);
2748 if (fname == NULL)
2749 return FALSE;
2750
2751#ifdef BACKSLASH_IN_FILENAME
2752 /*
2753 * Replace all backslashes with forward slashes. This makes the
2754 * autocommand patterns portable between Unix and MS-DOS.
2755 */
2756 sfname = vim_strsave(sfname);
2757 if (sfname != NULL)
2758 forward_slash(sfname);
2759 forward_slash(fname);
2760#endif
2761
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002762 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002763 if (ap->pat != NULL && ap->cmds != NULL
2764 && (ap->buflocal_nr == 0
2765 ? match_file_pat(NULL, &ap->reg_prog,
2766 fname, sfname, tail, ap->allow_dirs)
2767 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2768 ))
2769 {
2770 retval = TRUE;
2771 break;
2772 }
2773
2774 vim_free(fname);
2775#ifdef BACKSLASH_IN_FILENAME
2776 vim_free(sfname);
2777#endif
2778
2779 return retval;
2780}
2781
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002782/*
2783 * Function given to ExpandGeneric() to obtain the list of autocommand group
2784 * names.
2785 */
2786 char_u *
2787get_augroup_name(expand_T *xp UNUSED, int idx)
2788{
2789 if (idx == augroups.ga_len) // add "END" add the end
2790 return (char_u *)"END";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002791 if (idx < 0 || idx >= augroups.ga_len) // end of list
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002792 return NULL;
2793 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2794 // skip deleted entries
2795 return (char_u *)"";
2796 return AUGROUP_NAME(idx); // return a name
2797}
2798
2799static int include_groups = FALSE;
2800
2801 char_u *
2802set_context_in_autocmd(
2803 expand_T *xp,
2804 char_u *arg,
2805 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2806{
2807 char_u *p;
2808 int group;
2809
2810 // check for a group name, skip it if present
2811 include_groups = FALSE;
2812 p = arg;
2813 group = au_get_grouparg(&arg);
2814 if (group == AUGROUP_ERROR)
2815 return NULL;
2816 // If there only is a group name that's what we expand.
2817 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2818 {
2819 arg = p;
2820 group = AUGROUP_ALL;
2821 }
2822
2823 // skip over event name
2824 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2825 if (*p == ',')
2826 arg = p + 1;
2827 if (*p == NUL)
2828 {
2829 if (group == AUGROUP_ALL)
2830 include_groups = TRUE;
2831 xp->xp_context = EXPAND_EVENTS; // expand event name
2832 xp->xp_pattern = arg;
2833 return NULL;
2834 }
2835
2836 // skip over pattern
2837 arg = skipwhite(p);
2838 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2839 arg++;
2840 if (*arg)
2841 return arg; // expand (next) command
2842
2843 if (doautocmd)
2844 xp->xp_context = EXPAND_FILES; // expand file names
2845 else
2846 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2847 return NULL;
2848}
2849
2850/*
2851 * Function given to ExpandGeneric() to obtain the list of event names.
2852 */
2853 char_u *
2854get_event_name(expand_T *xp UNUSED, int idx)
2855{
John Marriott78d742a2024-04-02 20:26:01 +02002856 int i;
2857
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002858 if (idx < augroups.ga_len) // First list group names, if wanted
2859 {
2860 if (!include_groups || AUGROUP_NAME(idx) == NULL
2861 || AUGROUP_NAME(idx) == get_deleted_augroup())
2862 return (char_u *)""; // skip deleted entries
2863 return AUGROUP_NAME(idx); // return a name
2864 }
John Marriott78d742a2024-04-02 20:26:01 +02002865
2866 i = idx - augroups.ga_len;
2867 if (i < 0 || i >= (int)ARRAY_LENGTH(event_tab))
2868 return NULL;
2869
2870 return (char_u *)event_tab[i].value;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002871}
2872
Yee Cheng Chin900894b2023-09-29 20:42:32 +02002873/*
2874 * Function given to ExpandGeneric() to obtain the list of event names. Don't
2875 * include groups.
2876 */
2877 char_u *
2878get_event_name_no_group(expand_T *xp UNUSED, int idx)
2879{
John Marriott78d742a2024-04-02 20:26:01 +02002880 if (idx < 0 || idx >= (int)ARRAY_LENGTH(event_tab))
2881 return NULL;
2882
2883 return (char_u *)event_tab[idx].value;
Yee Cheng Chin900894b2023-09-29 20:42:32 +02002884}
2885
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002886
2887#if defined(FEAT_EVAL) || defined(PROTO)
2888/*
2889 * Return TRUE if autocmd is supported.
2890 */
2891 int
2892autocmd_supported(char_u *name)
2893{
2894 char_u *p;
2895
2896 return (event_name2nr(name, &p) != NUM_EVENTS);
2897}
2898
2899/*
2900 * Return TRUE if an autocommand is defined for a group, event and
2901 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2902 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2903 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2904 * Used for:
2905 * exists("#Group") or
2906 * exists("#Group#Event") or
2907 * exists("#Group#Event#pat") or
2908 * exists("#Event") or
2909 * exists("#Event#pat")
2910 */
2911 int
2912au_exists(char_u *arg)
2913{
2914 char_u *arg_save;
2915 char_u *pattern = NULL;
2916 char_u *event_name;
2917 char_u *p;
2918 event_T event;
2919 AutoPat *ap;
2920 buf_T *buflocal_buf = NULL;
2921 int group;
2922 int retval = FALSE;
2923
2924 // Make a copy so that we can change the '#' chars to a NUL.
2925 arg_save = vim_strsave(arg);
2926 if (arg_save == NULL)
2927 return FALSE;
2928 p = vim_strchr(arg_save, '#');
2929 if (p != NULL)
2930 *p++ = NUL;
2931
2932 // First, look for an autocmd group name
2933 group = au_find_group(arg_save);
2934 if (group == AUGROUP_ERROR)
2935 {
2936 // Didn't match a group name, assume the first argument is an event.
2937 group = AUGROUP_ALL;
2938 event_name = arg_save;
2939 }
2940 else
2941 {
2942 if (p == NULL)
2943 {
2944 // "Group": group name is present and it's recognized
2945 retval = TRUE;
2946 goto theend;
2947 }
2948
2949 // Must be "Group#Event" or "Group#Event#pat".
2950 event_name = p;
2951 p = vim_strchr(event_name, '#');
2952 if (p != NULL)
2953 *p++ = NUL; // "Group#Event#pat"
2954 }
2955
2956 pattern = p; // "pattern" is NULL when there is no pattern
2957
2958 // find the index (enum) for the event name
2959 event = event_name2nr(event_name, &p);
2960
2961 // return FALSE if the event name is not recognized
2962 if (event == NUM_EVENTS)
2963 goto theend;
2964
2965 // Find the first autocommand for this event.
2966 // If there isn't any, return FALSE;
2967 // If there is one and no pattern given, return TRUE;
2968 ap = first_autopat[(int)event];
2969 if (ap == NULL)
2970 goto theend;
2971
2972 // if pattern is "<buffer>", special handling is needed which uses curbuf
2973 // for pattern "<buffer=N>, fnamecmp() will work fine
2974 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2975 buflocal_buf = curbuf;
2976
2977 // Check if there is an autocommand with the given pattern.
2978 for ( ; ap != NULL; ap = ap->next)
2979 // only use a pattern when it has not been removed and has commands.
2980 // For buffer-local autocommands, fnamecmp() works fine.
2981 if (ap->pat != NULL && ap->cmds != NULL
2982 && (group == AUGROUP_ALL || ap->group == group)
2983 && (pattern == NULL
2984 || (buflocal_buf == NULL
2985 ? fnamecmp(ap->pat, pattern) == 0
2986 : ap->buflocal_nr == buflocal_buf->b_fnum)))
2987 {
2988 retval = TRUE;
2989 break;
2990 }
2991
2992theend:
2993 vim_free(arg_save);
2994 return retval;
2995}
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002996
2997/*
2998 * autocmd_add() and autocmd_delete() functions
2999 */
3000 static void
3001autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
3002{
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003003 list_T *aucmd_list;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003004 listitem_T *li;
3005 dict_T *event_dict;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003006 dictitem_T *di;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003007 char_u *event_name = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003008 list_T *event_list;
3009 listitem_T *eli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003010 event_T event;
3011 char_u *group_name = NULL;
3012 int group;
3013 char_u *pat = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003014 list_T *pat_list;
3015 listitem_T *pli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003016 char_u *cmd = NULL;
3017 char_u *end;
3018 int once;
3019 int nested;
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01003020 int replace; // replace the cmd for a group/event
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003021 int retval = VVAL_TRUE;
3022 int save_augroup = current_augroup;
3023
3024 rettv->v_type = VAR_BOOL;
3025 rettv->vval.v_number = VVAL_FALSE;
3026
3027 if (check_for_list_arg(argvars, 0) == FAIL)
3028 return;
3029
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003030 aucmd_list = argvars[0].vval.v_list;
3031 if (aucmd_list == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003032 return;
3033
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003034 FOR_ALL_LIST_ITEMS(aucmd_list, li)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003035 {
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003036 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003037 VIM_CLEAR(cmd);
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003038 event_name = NULL;
3039 event_list = NULL;
3040 pat = NULL;
3041 pat_list = NULL;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003042
3043 if (li->li_tv.v_type != VAR_DICT)
3044 continue;
3045
3046 event_dict = li->li_tv.vval.v_dict;
3047 if (event_dict == NULL)
3048 continue;
3049
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003050 di = dict_find(event_dict, (char_u *)"event", -1);
3051 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003052 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003053 if (di->di_tv.v_type == VAR_STRING)
3054 {
3055 event_name = di->di_tv.vval.v_string;
3056 if (event_name == NULL)
3057 {
3058 emsg(_(e_string_required));
3059 continue;
3060 }
3061 }
3062 else if (di->di_tv.v_type == VAR_LIST)
3063 {
3064 event_list = di->di_tv.vval.v_list;
3065 if (event_list == NULL)
3066 {
3067 emsg(_(e_list_required));
3068 continue;
3069 }
3070 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003071 else
3072 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003073 emsg(_(e_string_or_list_expected));
3074 continue;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003075 }
3076 }
3077
Bram Moolenaard61efa52022-07-23 09:52:04 +01003078 group_name = dict_get_string(event_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003079 if (group_name == NULL || *group_name == NUL)
3080 // if the autocmd group name is not specified, then use the current
3081 // autocmd group
3082 group = current_augroup;
3083 else
3084 {
3085 group = au_find_group(group_name);
3086 if (group == AUGROUP_ERROR)
3087 {
3088 if (delete)
3089 {
3090 semsg(_(e_no_such_group_str), group_name);
3091 retval = VVAL_FALSE;
3092 break;
3093 }
3094 // group is not found, create it now
3095 group = au_new_group(group_name);
3096 if (group == AUGROUP_ERROR)
3097 {
3098 semsg(_(e_no_such_group_str), group_name);
3099 retval = VVAL_FALSE;
3100 break;
3101 }
3102
3103 current_augroup = group;
3104 }
3105 }
3106
3107 // if a buffer number is specified, then generate a pattern of the form
3108 // "<buffer=n>. Otherwise, use the pattern supplied by the user.
3109 if (dict_has_key(event_dict, "bufnr"))
3110 {
3111 varnumber_T bnum;
3112
Bram Moolenaard61efa52022-07-23 09:52:04 +01003113 bnum = dict_get_number_def(event_dict, "bufnr", -1);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003114 if (bnum == -1)
3115 continue;
3116
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003117 vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
3118 pat = IObuff;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003119 }
3120 else
3121 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003122 di = dict_find(event_dict, (char_u *)"pattern", -1);
3123 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003124 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003125 if (di->di_tv.v_type == VAR_STRING)
3126 {
3127 pat = di->di_tv.vval.v_string;
3128 if (pat == NULL)
3129 {
3130 emsg(_(e_string_required));
3131 continue;
3132 }
3133 }
3134 else if (di->di_tv.v_type == VAR_LIST)
3135 {
3136 pat_list = di->di_tv.vval.v_list;
3137 if (pat_list == NULL)
3138 {
3139 emsg(_(e_list_required));
3140 continue;
3141 }
3142 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003143 else
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003144 {
3145 emsg(_(e_string_or_list_expected));
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003146 continue;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003147 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003148 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003149 else if (delete)
3150 pat = (char_u *)"";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003151 }
3152
Bram Moolenaard61efa52022-07-23 09:52:04 +01003153 once = dict_get_bool(event_dict, "once", FALSE);
3154 nested = dict_get_bool(event_dict, "nested", FALSE);
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01003155 // if 'replace' is true, then remove all the commands associated with
3156 // this autocmd event/group and add the new command.
Bram Moolenaard61efa52022-07-23 09:52:04 +01003157 replace = dict_get_bool(event_dict, "replace", FALSE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003158
Bram Moolenaard61efa52022-07-23 09:52:04 +01003159 cmd = dict_get_string(event_dict, "cmd", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003160 if (cmd == NULL)
3161 {
3162 if (delete)
3163 cmd = vim_strsave((char_u *)"");
3164 else
3165 continue;
3166 }
3167
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003168 if (delete && (event_name == NULL
3169 || (event_name[0] == '*' && event_name[1] == NUL)))
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003170 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003171 // if the event name is not specified or '*', delete all the events
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003172 for (event = (event_T)0; (int)event < NUM_EVENTS;
3173 event = (event_T)((int)event + 1))
3174 {
3175 if (do_autocmd_event(event, pat, once, nested, cmd, delete,
3176 group, 0) == FAIL)
3177 {
3178 retval = VVAL_FALSE;
3179 break;
3180 }
3181 }
3182 }
3183 else
3184 {
Bram Moolenaar968443e2022-05-27 21:16:34 +01003185 char_u *p = NULL;
3186
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003187 eli = NULL;
3188 end = NULL;
3189 while (TRUE)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003190 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003191 if (event_list != NULL)
3192 {
3193 if (eli == NULL)
3194 eli = event_list->lv_first;
3195 else
3196 eli = eli->li_next;
3197 if (eli == NULL)
3198 break;
3199 if (eli->li_tv.v_type != VAR_STRING
Bram Moolenaar882476a2022-06-01 16:02:38 +01003200 || (p = eli->li_tv.vval.v_string) == NULL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003201 {
3202 emsg(_(e_string_required));
Bram Moolenaar882476a2022-06-01 16:02:38 +01003203 break;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003204 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003205 }
3206 else
3207 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003208 if (p == NULL)
3209 p = event_name;
3210 if (p == NULL || *p == NUL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003211 break;
3212 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003213
3214 event = event_name2nr(p, &end);
3215 if (event == NUM_EVENTS || *end != NUL)
3216 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003217 // this also catches something following a valid event name
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003218 semsg(_(e_no_such_event_str), p);
3219 retval = VVAL_FALSE;
3220 break;
3221 }
3222 if (pat != NULL)
3223 {
3224 if (do_autocmd_event(event, pat, once, nested, cmd,
3225 delete | replace, group, 0) == FAIL)
3226 {
3227 retval = VVAL_FALSE;
3228 break;
3229 }
3230 }
3231 else if (pat_list != NULL)
3232 {
3233 FOR_ALL_LIST_ITEMS(pat_list, pli)
3234 {
3235 if (pli->li_tv.v_type != VAR_STRING
3236 || pli->li_tv.vval.v_string == NULL)
3237 {
3238 emsg(_(e_string_required));
3239 continue;
3240 }
3241 if (do_autocmd_event(event,
3242 pli->li_tv.vval.v_string, once, nested,
3243 cmd, delete | replace, group, 0) ==
3244 FAIL)
3245 {
3246 retval = VVAL_FALSE;
3247 break;
3248 }
3249 }
3250 if (retval == VVAL_FALSE)
3251 break;
3252 }
3253 if (event_name != NULL)
3254 p = end;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003255 }
3256 }
3257
3258 // if only the autocmd group name is specified for delete and the
3259 // autocmd event, pattern and cmd are not specified, then delete the
3260 // autocmd group.
3261 if (delete && group_name != NULL &&
3262 (event_name == NULL || event_name[0] == NUL)
3263 && (pat == NULL || pat[0] == NUL)
3264 && (cmd == NULL || cmd[0] == NUL))
3265 au_del_group(group_name);
3266 }
3267
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003268 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003269 VIM_CLEAR(cmd);
3270
3271 current_augroup = save_augroup;
3272 rettv->vval.v_number = retval;
3273}
3274
3275/*
3276 * autocmd_add() function
3277 */
3278 void
3279f_autocmd_add(typval_T *argvars, typval_T *rettv)
3280{
3281 autocmd_add_or_delete(argvars, rettv, FALSE);
3282}
3283
3284/*
3285 * autocmd_delete() function
3286 */
3287 void
3288f_autocmd_delete(typval_T *argvars, typval_T *rettv)
3289{
3290 autocmd_add_or_delete(argvars, rettv, TRUE);
3291}
3292
3293/*
3294 * autocmd_get() function
3295 * Returns a List of autocmds.
3296 */
3297 void
3298f_autocmd_get(typval_T *argvars, typval_T *rettv)
3299{
3300 event_T event_arg = NUM_EVENTS;
3301 event_T event;
3302 AutoPat *ap;
3303 AutoCmd *ac;
3304 list_T *event_list;
3305 dict_T *event_dict;
3306 char_u *event_name = NULL;
3307 char_u *pat = NULL;
3308 char_u *name = NULL;
3309 int group = AUGROUP_ALL;
3310
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01003311 if (rettv_list_alloc(rettv) == FAIL)
3312 return;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003313 if (check_for_opt_dict_arg(argvars, 0) == FAIL)
3314 return;
3315
3316 if (argvars[0].v_type == VAR_DICT)
3317 {
3318 // return only the autocmds in the specified group
3319 if (dict_has_key(argvars[0].vval.v_dict, "group"))
3320 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003321 name = dict_get_string(argvars[0].vval.v_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003322 if (name == NULL)
3323 return;
3324
3325 if (*name == NUL)
3326 group = AUGROUP_DEFAULT;
3327 else
3328 {
3329 group = au_find_group(name);
3330 if (group == AUGROUP_ERROR)
3331 {
3332 semsg(_(e_no_such_group_str), name);
3333 vim_free(name);
3334 return;
3335 }
3336 }
3337 vim_free(name);
3338 }
3339
3340 // return only the autocmds for the specified event
3341 if (dict_has_key(argvars[0].vval.v_dict, "event"))
3342 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003343 name = dict_get_string(argvars[0].vval.v_dict, "event", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003344 if (name == NULL)
3345 return;
3346
3347 if (name[0] == '*' && name[1] == NUL)
3348 event_arg = NUM_EVENTS;
3349 else
3350 {
John Marriott78d742a2024-04-02 20:26:01 +02003351 keyvalue_T target;
3352 keyvalue_T *entry;
3353
3354 target.key = 0;
3355 target.value = (char *)name;
3356 target.length = (int)STRLEN(target.value);
3357 entry = (keyvalue_T *)bsearch(&target, &event_tab, ARRAY_LENGTH(event_tab), sizeof(event_tab[0]), cmp_keyvalue_value_ni);
3358 if (entry == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003359 {
3360 semsg(_(e_no_such_event_str), name);
3361 vim_free(name);
3362 return;
3363 }
John Marriott78d742a2024-04-02 20:26:01 +02003364 event_arg = (event_T)entry->key;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003365 }
3366 vim_free(name);
3367 }
3368
3369 // return only the autocmds for the specified pattern
3370 if (dict_has_key(argvars[0].vval.v_dict, "pattern"))
3371 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003372 pat = dict_get_string(argvars[0].vval.v_dict, "pattern", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003373 if (pat == NULL)
3374 return;
3375 }
3376 }
3377
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003378 event_list = rettv->vval.v_list;
3379
3380 // iterate through all the autocmd events
3381 for (event = (event_T)0; (int)event < NUM_EVENTS;
3382 event = (event_T)((int)event + 1))
3383 {
3384 if (event_arg != NUM_EVENTS && event != event_arg)
3385 continue;
3386
3387 event_name = event_nr2name(event);
3388
3389 // iterate through all the patterns for this autocmd event
3390 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
3391 {
3392 char_u *group_name;
3393
3394 if (group != AUGROUP_ALL && group != ap->group)
3395 continue;
3396
3397 if (pat != NULL && STRCMP(pat, ap->pat) != 0)
3398 continue;
3399
3400 group_name = get_augroup_name(NULL, ap->group);
3401
3402 // iterate through all the commands for this pattern and add one
3403 // item for each cmd.
3404 for (ac = ap->cmds; ac != NULL; ac = ac->next)
3405 {
3406 event_dict = dict_alloc();
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003407 if (event_dict == NULL
3408 || list_append_dict(event_list, event_dict) == FAIL)
Christian Brabandt29269a72024-04-16 22:44:31 +02003409 {
3410 vim_free(pat);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003411 return;
Christian Brabandt29269a72024-04-16 22:44:31 +02003412 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003413
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003414 if (dict_add_string(event_dict, "event", event_name) == FAIL
3415 || dict_add_string(event_dict, "group",
3416 group_name == NULL ? (char_u *)""
3417 : group_name) == FAIL
3418 || (ap->buflocal_nr != 0
3419 && (dict_add_number(event_dict, "bufnr",
3420 ap->buflocal_nr) == FAIL))
3421 || dict_add_string(event_dict, "pattern",
3422 ap->pat) == FAIL
3423 || dict_add_string(event_dict, "cmd", ac->cmd) == FAIL
3424 || dict_add_bool(event_dict, "once", ac->once) == FAIL
3425 || dict_add_bool(event_dict, "nested",
3426 ac->nested) == FAIL)
Christian Brabandt29269a72024-04-16 22:44:31 +02003427 {
3428 vim_free(pat);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003429 return;
Christian Brabandt29269a72024-04-16 22:44:31 +02003430 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003431 }
3432 }
3433 }
3434
3435 vim_free(pat);
3436}
3437
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01003438#endif