blob: 330db42303625d7494551974bbe5a054cc790698 [file] [log] [blame]
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * autocmd.c: Autocommand related functions
12 */
13
14#include "vim.h"
15
16/*
17 * The autocommands are stored in a list for each event.
18 * Autocommands for the same pattern, that are consecutive, are joined
19 * together, to avoid having to match the pattern too often.
20 * The result is an array of Autopat lists, which point to AutoCmd lists:
21 *
22 * last_autopat[0] -----------------------------+
23 * V
24 * first_autopat[0] --> Autopat.next --> Autopat.next --> NULL
25 * Autopat.cmds Autopat.cmds
26 * | |
27 * V V
28 * AutoCmd.next AutoCmd.next
29 * | |
30 * V V
31 * AutoCmd.next NULL
32 * |
33 * V
34 * NULL
35 *
36 * last_autopat[1] --------+
37 * V
38 * first_autopat[1] --> Autopat.next --> NULL
39 * Autopat.cmds
40 * |
41 * V
42 * AutoCmd.next
43 * |
44 * V
45 * NULL
46 * etc.
47 *
48 * The order of AutoCmds is important, this is the order in which they were
49 * defined and will have to be executed.
50 */
51typedef struct AutoCmd
52{
53 char_u *cmd; // The command to be executed (NULL
54 // when command has been removed).
Bram Moolenaareb93f3f2019-04-04 15:04:56 +020055 char once; // "One shot": removed after execution
Bram Moolenaar3e460fd2019-01-26 16:21:07 +010056 char nested; // If autocommands nest here.
57 char last; // last command in list
LemonBoyeca7c602022-04-14 15:39:43 +010058 sctx_T script_ctx; // script context where it is defined
Bram Moolenaar3e460fd2019-01-26 16:21:07 +010059 struct AutoCmd *next; // next AutoCmd in list
60} AutoCmd;
61
62typedef struct AutoPat
63{
64 struct AutoPat *next; // Next AutoPat in AutoPat list; MUST
65 // be the first entry.
66 char_u *pat; // pattern as typed (NULL when pattern
67 // has been removed)
68 regprog_T *reg_prog; // compiled regprog for pattern
69 AutoCmd *cmds; // list of commands to do
70 int group; // group ID
71 int patlen; // strlen() of pat
72 int buflocal_nr; // !=0 for buffer-local AutoPat
73 char allow_dirs; // Pattern may match whole path
74 char last; // last pattern for apply_autocmds()
75} AutoPat;
76
John Marriott78d742a2024-04-02 20:26:01 +020077//
78// special cases:
79// BufNewFile and BufRead are searched for ALOT (especially at startup)
80// so we pre-determine their index into the event_tab[] table for fast access.
81// Keep these values in sync with event_tab[]!
82#define BUFNEWFILE_INDEX 9
83#define BUFREAD_INDEX 10
84
85// must be sorted by the 'value' field because it is used by bsearch()!
86static keyvalue_T event_tab[] = {
87 KEYVALUE_ENTRY(EVENT_BUFADD, "BufAdd"),
88 KEYVALUE_ENTRY(EVENT_BUFADD, "BufCreate"),
89 KEYVALUE_ENTRY(EVENT_BUFDELETE, "BufDelete"),
90 KEYVALUE_ENTRY(EVENT_BUFENTER, "BufEnter"),
91 KEYVALUE_ENTRY(EVENT_BUFFILEPOST, "BufFilePost"),
92 KEYVALUE_ENTRY(EVENT_BUFFILEPRE, "BufFilePre"),
93 KEYVALUE_ENTRY(EVENT_BUFHIDDEN, "BufHidden"),
94 KEYVALUE_ENTRY(EVENT_BUFLEAVE, "BufLeave"),
95 KEYVALUE_ENTRY(EVENT_BUFNEW, "BufNew"),
96 KEYVALUE_ENTRY(EVENT_BUFNEWFILE, "BufNewFile"), // BUFNEWFILE_INDEX
97 KEYVALUE_ENTRY(EVENT_BUFREADPOST, "BufRead"), // BUFREAD_INDEX
98 KEYVALUE_ENTRY(EVENT_BUFREADCMD, "BufReadCmd"),
99 KEYVALUE_ENTRY(EVENT_BUFREADPOST, "BufReadPost"),
100 KEYVALUE_ENTRY(EVENT_BUFREADPRE, "BufReadPre"),
101 KEYVALUE_ENTRY(EVENT_BUFUNLOAD, "BufUnload"),
102 KEYVALUE_ENTRY(EVENT_BUFWINENTER, "BufWinEnter"),
103 KEYVALUE_ENTRY(EVENT_BUFWINLEAVE, "BufWinLeave"),
104 KEYVALUE_ENTRY(EVENT_BUFWIPEOUT, "BufWipeout"),
105 KEYVALUE_ENTRY(EVENT_BUFWRITEPRE, "BufWrite"),
106 KEYVALUE_ENTRY(EVENT_BUFWRITECMD, "BufWriteCmd"),
107 KEYVALUE_ENTRY(EVENT_BUFWRITEPOST, "BufWritePost"),
108 KEYVALUE_ENTRY(EVENT_BUFWRITEPRE, "BufWritePre"),
109 KEYVALUE_ENTRY(EVENT_CMDLINECHANGED, "CmdlineChanged"),
110 KEYVALUE_ENTRY(EVENT_CMDLINEENTER, "CmdlineEnter"),
111 KEYVALUE_ENTRY(EVENT_CMDLINELEAVE, "CmdlineLeave"),
112 KEYVALUE_ENTRY(EVENT_CMDUNDEFINED, "CmdUndefined"),
113 KEYVALUE_ENTRY(EVENT_CMDWINENTER, "CmdwinEnter"),
114 KEYVALUE_ENTRY(EVENT_CMDWINLEAVE, "CmdwinLeave"),
115 KEYVALUE_ENTRY(EVENT_COLORSCHEME, "ColorScheme"),
116 KEYVALUE_ENTRY(EVENT_COLORSCHEMEPRE, "ColorSchemePre"),
117 KEYVALUE_ENTRY(EVENT_COMPLETECHANGED, "CompleteChanged"),
118 KEYVALUE_ENTRY(EVENT_COMPLETEDONE, "CompleteDone"),
119 KEYVALUE_ENTRY(EVENT_COMPLETEDONEPRE, "CompleteDonePre"),
120 KEYVALUE_ENTRY(EVENT_CURSORHOLD, "CursorHold"),
121 KEYVALUE_ENTRY(EVENT_CURSORHOLDI, "CursorHoldI"),
122 KEYVALUE_ENTRY(EVENT_CURSORMOVED, "CursorMoved"),
Shougo Matsushitad0952142024-06-20 22:05:16 +0200123 KEYVALUE_ENTRY(EVENT_CURSORMOVEDC, "CursorMovedC"),
John Marriott78d742a2024-04-02 20:26:01 +0200124 KEYVALUE_ENTRY(EVENT_CURSORMOVEDI, "CursorMovedI"),
125 KEYVALUE_ENTRY(EVENT_DIFFUPDATED, "DiffUpdated"),
126 KEYVALUE_ENTRY(EVENT_DIRCHANGED, "DirChanged"),
127 KEYVALUE_ENTRY(EVENT_DIRCHANGEDPRE, "DirChangedPre"),
128 KEYVALUE_ENTRY(EVENT_ENCODINGCHANGED, "EncodingChanged"),
129 KEYVALUE_ENTRY(EVENT_EXITPRE, "ExitPre"),
130 KEYVALUE_ENTRY(EVENT_FILEAPPENDCMD, "FileAppendCmd"),
131 KEYVALUE_ENTRY(EVENT_FILEAPPENDPOST, "FileAppendPost"),
132 KEYVALUE_ENTRY(EVENT_FILEAPPENDPRE, "FileAppendPre"),
133 KEYVALUE_ENTRY(EVENT_FILECHANGEDRO, "FileChangedRO"),
134 KEYVALUE_ENTRY(EVENT_FILECHANGEDSHELL, "FileChangedShell"),
135 KEYVALUE_ENTRY(EVENT_FILECHANGEDSHELLPOST, "FileChangedShellPost"),
136 KEYVALUE_ENTRY(EVENT_ENCODINGCHANGED, "FileEncoding"),
137 KEYVALUE_ENTRY(EVENT_FILEREADCMD, "FileReadCmd"),
138 KEYVALUE_ENTRY(EVENT_FILEREADPOST, "FileReadPost"),
139 KEYVALUE_ENTRY(EVENT_FILEREADPRE, "FileReadPre"),
140 KEYVALUE_ENTRY(EVENT_FILETYPE, "FileType"),
141 KEYVALUE_ENTRY(EVENT_FILEWRITECMD, "FileWriteCmd"),
142 KEYVALUE_ENTRY(EVENT_FILEWRITEPOST, "FileWritePost"),
143 KEYVALUE_ENTRY(EVENT_FILEWRITEPRE, "FileWritePre"),
144 KEYVALUE_ENTRY(EVENT_FILTERREADPOST, "FilterReadPost"),
145 KEYVALUE_ENTRY(EVENT_FILTERREADPRE, "FilterReadPre"),
146 KEYVALUE_ENTRY(EVENT_FILTERWRITEPOST, "FilterWritePost"),
147 KEYVALUE_ENTRY(EVENT_FILTERWRITEPRE, "FilterWritePre"),
148 KEYVALUE_ENTRY(EVENT_FOCUSGAINED, "FocusGained"),
149 KEYVALUE_ENTRY(EVENT_FOCUSLOST, "FocusLost"),
150 KEYVALUE_ENTRY(EVENT_FUNCUNDEFINED, "FuncUndefined"),
151 KEYVALUE_ENTRY(EVENT_GUIENTER, "GUIEnter"),
152 KEYVALUE_ENTRY(EVENT_GUIFAILED, "GUIFailed"),
153 KEYVALUE_ENTRY(EVENT_INSERTCHANGE, "InsertChange"),
154 KEYVALUE_ENTRY(EVENT_INSERTCHARPRE, "InsertCharPre"),
155 KEYVALUE_ENTRY(EVENT_INSERTENTER, "InsertEnter"),
156 KEYVALUE_ENTRY(EVENT_INSERTLEAVE, "InsertLeave"),
157 KEYVALUE_ENTRY(EVENT_INSERTLEAVEPRE, "InsertLeavePre"),
Shougo Matsushita83678842024-07-11 22:05:12 +0200158 KEYVALUE_ENTRY(EVENT_KEYINPUTPRE, "KeyInputPre"),
John Marriott78d742a2024-04-02 20:26:01 +0200159 KEYVALUE_ENTRY(EVENT_MENUPOPUP, "MenuPopup"),
160 KEYVALUE_ENTRY(EVENT_MODECHANGED, "ModeChanged"),
161 KEYVALUE_ENTRY(EVENT_OPTIONSET, "OptionSet"),
162 KEYVALUE_ENTRY(EVENT_QUICKFIXCMDPOST, "QuickFixCmdPost"),
163 KEYVALUE_ENTRY(EVENT_QUICKFIXCMDPRE, "QuickFixCmdPre"),
164 KEYVALUE_ENTRY(EVENT_QUITPRE, "QuitPre"),
165 KEYVALUE_ENTRY(EVENT_REMOTEREPLY, "RemoteReply"),
166 KEYVALUE_ENTRY(EVENT_SAFESTATE, "SafeState"),
167 KEYVALUE_ENTRY(EVENT_SAFESTATEAGAIN, "SafeStateAgain"),
168 KEYVALUE_ENTRY(EVENT_SESSIONLOADPOST, "SessionLoadPost"),
169 KEYVALUE_ENTRY(EVENT_SESSIONWRITEPOST, "SessionWritePost"),
170 KEYVALUE_ENTRY(EVENT_SHELLCMDPOST, "ShellCmdPost"),
171 KEYVALUE_ENTRY(EVENT_SHELLFILTERPOST, "ShellFilterPost"),
172 KEYVALUE_ENTRY(EVENT_SIGUSR1, "SigUSR1"),
173 KEYVALUE_ENTRY(EVENT_SOURCECMD, "SourceCmd"),
174 KEYVALUE_ENTRY(EVENT_SOURCEPOST, "SourcePost"),
175 KEYVALUE_ENTRY(EVENT_SOURCEPRE, "SourcePre"),
176 KEYVALUE_ENTRY(EVENT_SPELLFILEMISSING, "SpellFileMissing"),
177 KEYVALUE_ENTRY(EVENT_STDINREADPOST, "StdinReadPost"),
178 KEYVALUE_ENTRY(EVENT_STDINREADPRE, "StdinReadPre"),
179 KEYVALUE_ENTRY(EVENT_SWAPEXISTS, "SwapExists"),
180 KEYVALUE_ENTRY(EVENT_SYNTAX, "Syntax"),
181 KEYVALUE_ENTRY(EVENT_TABCLOSED, "TabClosed"),
182 KEYVALUE_ENTRY(EVENT_TABENTER, "TabEnter"),
183 KEYVALUE_ENTRY(EVENT_TABLEAVE, "TabLeave"),
184 KEYVALUE_ENTRY(EVENT_TABNEW, "TabNew"),
185 KEYVALUE_ENTRY(EVENT_TERMCHANGED, "TermChanged"),
186 KEYVALUE_ENTRY(EVENT_TERMINALOPEN, "TerminalOpen"),
187 KEYVALUE_ENTRY(EVENT_TERMINALWINOPEN, "TerminalWinOpen"),
188 KEYVALUE_ENTRY(EVENT_TERMRESPONSE, "TermResponse"),
189 KEYVALUE_ENTRY(EVENT_TERMRESPONSEALL, "TermResponseAll"),
190 KEYVALUE_ENTRY(EVENT_TEXTCHANGED, "TextChanged"),
191 KEYVALUE_ENTRY(EVENT_TEXTCHANGEDI, "TextChangedI"),
192 KEYVALUE_ENTRY(EVENT_TEXTCHANGEDP, "TextChangedP"),
193 KEYVALUE_ENTRY(EVENT_TEXTCHANGEDT, "TextChangedT"),
194 KEYVALUE_ENTRY(EVENT_TEXTYANKPOST, "TextYankPost"),
195 KEYVALUE_ENTRY(EVENT_USER, "User"),
196 KEYVALUE_ENTRY(EVENT_VIMENTER, "VimEnter"),
197 KEYVALUE_ENTRY(EVENT_VIMLEAVE, "VimLeave"),
198 KEYVALUE_ENTRY(EVENT_VIMLEAVEPRE, "VimLeavePre"),
199 KEYVALUE_ENTRY(EVENT_VIMRESIZED, "VimResized"),
200 KEYVALUE_ENTRY(EVENT_VIMRESUME, "VimResume"),
201 KEYVALUE_ENTRY(EVENT_VIMSUSPEND, "VimSuspend"),
202 KEYVALUE_ENTRY(EVENT_WINCLOSED, "WinClosed"),
203 KEYVALUE_ENTRY(EVENT_WINENTER, "WinEnter"),
204 KEYVALUE_ENTRY(EVENT_WINLEAVE, "WinLeave"),
205 KEYVALUE_ENTRY(EVENT_WINNEW, "WinNew"),
206 KEYVALUE_ENTRY(EVENT_WINNEWPRE, "WinNewPre"),
207 KEYVALUE_ENTRY(EVENT_WINRESIZED, "WinResized"),
208 KEYVALUE_ENTRY(EVENT_WINSCROLLED, "WinScrolled")
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100209};
210
John Marriott78d742a2024-04-02 20:26:01 +0200211static AutoPat *first_autopat[NUM_EVENTS] = {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100212 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
213 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
214 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
215 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
216 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
John Marriott78d742a2024-04-02 20:26:01 +0200217 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
218 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
219 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
220 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
221 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
222 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
223 NULL, NULL, NULL, NULL, NULL, NULL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100224};
225
John Marriott78d742a2024-04-02 20:26:01 +0200226static AutoPat *last_autopat[NUM_EVENTS] = {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100227 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
228 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
229 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
230 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
231 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
John Marriott78d742a2024-04-02 20:26:01 +0200232 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
233 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
234 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
235 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
236 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
237 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
238 NULL, NULL, NULL, NULL, NULL, NULL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100239};
240
kylo252ae6f1d82022-02-16 19:24:07 +0000241#define AUGROUP_DEFAULT (-1) // default autocmd group
242#define AUGROUP_ERROR (-2) // erroneous autocmd group
243#define AUGROUP_ALL (-3) // all autocmd groups
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100244
245/*
246 * struct used to keep status while executing autocommands for an event.
247 */
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100248struct AutoPatCmd_S
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100249{
250 AutoPat *curpat; // next AutoPat to examine
251 AutoCmd *nextcmd; // next AutoCmd to execute
252 int group; // group being used
253 char_u *fname; // fname to match with
254 char_u *sfname; // sfname to match with
255 char_u *tail; // tail of fname
256 event_T event; // current event
LemonBoyeca7c602022-04-14 15:39:43 +0100257 sctx_T script_ctx; // script context where it is defined
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100258 int arg_bufnr; // Initially equal to <abuf>, set to zero when
259 // buf is deleted.
LemonBoyeca7c602022-04-14 15:39:43 +0100260 AutoPatCmd_T *next; // chain of active apc-s for auto-invalidation
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100261};
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100262
LemonBoyeca7c602022-04-14 15:39:43 +0100263static AutoPatCmd_T *active_apc_list = NULL; // stack of active autocommands
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100264
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200265// Macro to loop over all the patterns for an autocmd event
266#define FOR_ALL_AUTOCMD_PATTERNS(event, ap) \
267 for ((ap) = first_autopat[(int)(event)]; (ap) != NULL; (ap) = (ap)->next)
268
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100269/*
270 * augroups stores a list of autocmd group names.
271 */
272static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
273#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
Bram Moolenaarc667da52019-11-30 20:52:27 +0100274// use get_deleted_augroup() to get this
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100275static char_u *deleted_augroup = NULL;
276
277/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100278 * The ID of the current group. Group 0 is the default one.
279 */
280static int current_augroup = AUGROUP_DEFAULT;
281
Bram Moolenaarc667da52019-11-30 20:52:27 +0100282static int au_need_clean = FALSE; // need to delete marked patterns
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100283
John Marriott78d742a2024-04-02 20:26:01 +0200284static event_T event_name2nr(char_u *start, char_u **end);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100285static char_u *event_nr2name(event_T event);
286static int au_get_grouparg(char_u **argp);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200287static int do_autocmd_event(event_T event, char_u *pat, int once, int nested, char_u *cmd, int forceit, int group, int flags);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100288static int apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, int force, int group, buf_T *buf, exarg_T *eap);
LemonBoyeca7c602022-04-14 15:39:43 +0100289static void auto_next_pat(AutoPatCmd_T *apc, int stop_at_last);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100290static int au_find_group(char_u *name);
291
292static event_T last_event;
293static int last_group;
Bram Moolenaarc667da52019-11-30 20:52:27 +0100294static int autocmd_blocked = 0; // block all autocmds
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100295
296 static char_u *
297get_deleted_augroup(void)
298{
299 if (deleted_augroup == NULL)
300 deleted_augroup = (char_u *)_("--Deleted--");
301 return deleted_augroup;
302}
303
304/*
305 * Show the autocommands for one AutoPat.
306 */
307 static void
308show_autocmd(AutoPat *ap, event_T event)
309{
310 AutoCmd *ac;
311
312 // Check for "got_int" (here and at various places below), which is set
313 // when "q" has been hit for the "--more--" prompt
314 if (got_int)
315 return;
316 if (ap->pat == NULL) // pattern has been removed
317 return;
318
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000319 // Make sure no info referenced by "ap" is cleared, e.g. when a timer
320 // clears an augroup. Jump to "theend" after this!
321 // "ap->pat" may be cleared anyway.
322 ++autocmd_busy;
323
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100324 msg_putchar('\n');
325 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000326 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100327 if (event != last_event || ap->group != last_group)
328 {
329 if (ap->group != AUGROUP_DEFAULT)
330 {
331 if (AUGROUP_NAME(ap->group) == NULL)
332 msg_puts_attr((char *)get_deleted_augroup(), HL_ATTR(HLF_E));
333 else
334 msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
335 msg_puts(" ");
336 }
337 msg_puts_attr((char *)event_nr2name(event), HL_ATTR(HLF_T));
338 last_event = event;
339 last_group = ap->group;
340 msg_putchar('\n');
341 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000342 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100343 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000344
345 if (ap->pat == NULL)
346 goto theend; // timer might have cleared the pattern or group
347
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100348 msg_col = 4;
349 msg_outtrans(ap->pat);
350
351 for (ac = ap->cmds; ac != NULL; ac = ac->next)
352 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100353 if (ac->cmd == NULL) // skip removed commands
354 continue;
355
356 if (msg_col >= 14)
357 msg_putchar('\n');
358 msg_col = 14;
359 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000360 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100361 msg_outtrans(ac->cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100362#ifdef FEAT_EVAL
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100363 if (p_verbose > 0)
364 last_set_msg(ac->script_ctx);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100365#endif
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100366 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000367 goto theend;
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100368 if (ac->next != NULL)
369 {
370 msg_putchar('\n');
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100371 if (got_int)
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000372 goto theend;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100373 }
374 }
Bram Moolenaar3b014be2022-11-13 17:53:46 +0000375
376theend:
377 --autocmd_busy;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100378}
379
380/*
381 * Mark an autocommand pattern for deletion.
382 */
383 static void
384au_remove_pat(AutoPat *ap)
385{
386 VIM_CLEAR(ap->pat);
387 ap->buflocal_nr = -1;
388 au_need_clean = TRUE;
389}
390
391/*
392 * Mark all commands for a pattern for deletion.
393 */
394 static void
395au_remove_cmds(AutoPat *ap)
396{
397 AutoCmd *ac;
398
399 for (ac = ap->cmds; ac != NULL; ac = ac->next)
400 VIM_CLEAR(ac->cmd);
401 au_need_clean = TRUE;
402}
403
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200404// Delete one command from an autocmd pattern.
405static void au_del_cmd(AutoCmd *ac)
406{
407 VIM_CLEAR(ac->cmd);
408 au_need_clean = TRUE;
409}
410
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100411/*
412 * Cleanup autocommands and patterns that have been deleted.
413 * This is only done when not executing autocommands.
414 */
415 static void
416au_cleanup(void)
417{
418 AutoPat *ap, **prev_ap;
419 AutoCmd *ac, **prev_ac;
420 event_T event;
421
422 if (autocmd_busy || !au_need_clean)
423 return;
424
425 // loop over all events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100426 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100427 event = (event_T)((int)event + 1))
428 {
429 // loop over all autocommand patterns
430 prev_ap = &(first_autopat[(int)event]);
431 for (ap = *prev_ap; ap != NULL; ap = *prev_ap)
432 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200433 int has_cmd = FALSE;
434
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200435 // loop over all commands for this pattern
436 prev_ac = &(ap->cmds);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100437 for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
438 {
439 // remove the command if the pattern is to be deleted or when
440 // the command has been marked for deletion
441 if (ap->pat == NULL || ac->cmd == NULL)
442 {
443 *prev_ac = ac->next;
444 vim_free(ac->cmd);
445 vim_free(ac);
446 }
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200447 else
448 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200449 has_cmd = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100450 prev_ac = &(ac->next);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200451 }
452 }
453
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200454 if (ap->pat != NULL && !has_cmd)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200455 // Pattern was not marked for deletion, but all of its
456 // commands were. So mark the pattern for deletion.
457 au_remove_pat(ap);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100458
459 // remove the pattern if it has been marked for deletion
460 if (ap->pat == NULL)
461 {
462 if (ap->next == NULL)
463 {
464 if (prev_ap == &(first_autopat[(int)event]))
465 last_autopat[(int)event] = NULL;
466 else
467 // this depends on the "next" field being the first in
468 // the struct
469 last_autopat[(int)event] = (AutoPat *)prev_ap;
470 }
471 *prev_ap = ap->next;
472 vim_regfree(ap->reg_prog);
473 vim_free(ap);
474 }
475 else
476 prev_ap = &(ap->next);
477 }
478 }
479
480 au_need_clean = FALSE;
481}
482
483/*
484 * Called when buffer is freed, to remove/invalidate related buffer-local
485 * autocmds.
486 */
487 void
488aubuflocal_remove(buf_T *buf)
489{
LemonBoyeca7c602022-04-14 15:39:43 +0100490 AutoPat *ap;
491 event_T event;
492 AutoPatCmd_T *apc;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100493
494 // invalidate currently executing autocommands
495 for (apc = active_apc_list; apc; apc = apc->next)
496 if (buf->b_fnum == apc->arg_bufnr)
497 apc->arg_bufnr = 0;
498
499 // invalidate buflocals looping through events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100500 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100501 event = (event_T)((int)event + 1))
502 // loop over all autocommand patterns
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200503 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100504 if (ap->buflocal_nr == buf->b_fnum)
505 {
506 au_remove_pat(ap);
507 if (p_verbose >= 6)
508 {
509 verbose_enter();
510 smsg(_("auto-removing autocommand: %s <buffer=%d>"),
511 event_nr2name(event), buf->b_fnum);
512 verbose_leave();
513 }
514 }
515 au_cleanup();
516}
517
518/*
519 * Add an autocmd group name.
520 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
521 */
522 static int
523au_new_group(char_u *name)
524{
525 int i;
526
527 i = au_find_group(name);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100528 if (i != AUGROUP_ERROR)
529 return i;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100530
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100531 // the group doesn't exist yet, add it. First try using a free entry.
532 for (i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100533 if (AUGROUP_NAME(i) == NULL)
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100534 break;
535 if (i == augroups.ga_len && ga_grow(&augroups, 1) == FAIL)
536 return AUGROUP_ERROR;
537
538 AUGROUP_NAME(i) = vim_strsave(name);
539 if (AUGROUP_NAME(i) == NULL)
540 return AUGROUP_ERROR;
541 if (i == augroups.ga_len)
542 ++augroups.ga_len;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100543
544 return i;
545}
546
547 static void
548au_del_group(char_u *name)
549{
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100550 int i;
551 event_T event;
552 AutoPat *ap;
553 int in_use = FALSE;
554
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100555
556 i = au_find_group(name);
557 if (i == AUGROUP_ERROR) // the group doesn't exist
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100558 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100559 semsg(_(e_no_such_group_str), name);
560 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100561 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100562 if (i == current_augroup)
563 {
564 emsg(_(e_cannot_delete_current_group));
565 return;
566 }
567
568 for (event = (event_T)0; (int)event < NUM_EVENTS;
569 event = (event_T)((int)event + 1))
570 {
571 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
572 if (ap->group == i && ap->pat != NULL)
573 {
574 give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
575 in_use = TRUE;
576 event = NUM_EVENTS;
577 break;
578 }
579 }
580 vim_free(AUGROUP_NAME(i));
581 if (in_use)
582 AUGROUP_NAME(i) = get_deleted_augroup();
583 else
584 AUGROUP_NAME(i) = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100585}
586
587/*
588 * Find the ID of an autocmd group name.
589 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
590 */
591 static int
592au_find_group(char_u *name)
593{
594 int i;
595
596 for (i = 0; i < augroups.ga_len; ++i)
597 if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
598 && STRCMP(AUGROUP_NAME(i), name) == 0)
599 return i;
600 return AUGROUP_ERROR;
601}
602
603/*
604 * Return TRUE if augroup "name" exists.
605 */
606 int
607au_has_group(char_u *name)
608{
609 return au_find_group(name) != AUGROUP_ERROR;
610}
611
612/*
613 * ":augroup {name}".
614 */
615 void
616do_augroup(char_u *arg, int del_group)
617{
618 int i;
619
620 if (del_group)
621 {
622 if (*arg == NUL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000623 emsg(_(e_argument_required));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100624 else
625 au_del_group(arg);
626 }
627 else if (STRICMP(arg, "end") == 0) // ":aug end": back to group 0
628 current_augroup = AUGROUP_DEFAULT;
629 else if (*arg) // ":aug xxx": switch to group xxx
630 {
631 i = au_new_group(arg);
632 if (i != AUGROUP_ERROR)
633 current_augroup = i;
634 }
635 else // ":aug": list the group names
636 {
637 msg_start();
638 for (i = 0; i < augroups.ga_len; ++i)
639 {
640 if (AUGROUP_NAME(i) != NULL)
641 {
642 msg_puts((char *)AUGROUP_NAME(i));
643 msg_puts(" ");
644 }
645 }
646 msg_clr_eos();
647 msg_end();
648 }
649}
650
Bram Moolenaare76062c2022-11-28 18:51:43 +0000651 void
652autocmd_init(void)
653{
654 CLEAR_FIELD(aucmd_win);
655}
656
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100657#if defined(EXITFREE) || defined(PROTO)
658 void
659free_all_autocmds(void)
660{
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100661 char_u *s;
662
663 for (current_augroup = -1; current_augroup < augroups.ga_len;
664 ++current_augroup)
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200665 do_autocmd(NULL, (char_u *)"", TRUE);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100666
Bram Moolenaare76062c2022-11-28 18:51:43 +0000667 for (int i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100668 {
669 s = ((char_u **)(augroups.ga_data))[i];
670 if (s != get_deleted_augroup())
671 vim_free(s);
672 }
673 ga_clear(&augroups);
Bram Moolenaare76062c2022-11-28 18:51:43 +0000674
Bram Moolenaar84497cd2022-11-28 20:34:52 +0000675 // aucmd_win[] is freed in win_free_all()
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100676}
677#endif
678
679/*
Bram Moolenaare76062c2022-11-28 18:51:43 +0000680 * Return TRUE if "win" is an active entry in aucmd_win[].
681 */
682 int
683is_aucmd_win(win_T *win)
684{
685 for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
686 if (aucmd_win[i].auc_win_used && aucmd_win[i].auc_win == win)
687 return TRUE;
688 return FALSE;
689}
690
691/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100692 * Return the event number for event name "start".
693 * Return NUM_EVENTS if the event name was not found.
694 * Return a pointer to the next event name in "end".
695 */
696 static event_T
697event_name2nr(char_u *start, char_u **end)
698{
699 char_u *p;
John Marriott78d742a2024-04-02 20:26:01 +0200700 keyvalue_T target;
701 keyvalue_T *entry;
702 static keyvalue_T *bufnewfile = &event_tab[BUFNEWFILE_INDEX];
703 static keyvalue_T *bufread = &event_tab[BUFREAD_INDEX];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100704
705 // the event name ends with end of line, '|', a blank or a comma
706 for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
707 ;
John Marriott78d742a2024-04-02 20:26:01 +0200708
709 target.key = 0;
John Marriott8d4477e2024-11-02 15:59:01 +0100710 target.value.string = start;
711 target.value.length = (size_t)(p - start);
John Marriott78d742a2024-04-02 20:26:01 +0200712
713 // special cases:
714 // BufNewFile and BufRead are searched for ALOT (especially at startup)
715 // so we check for them first.
716 if (cmp_keyvalue_value_ni(&target, bufnewfile) == 0)
717 entry = bufnewfile;
718 else
719 if (cmp_keyvalue_value_ni(&target, bufread) == 0)
720 entry = bufread;
721 else
722 entry = (keyvalue_T *)bsearch(&target, &event_tab, ARRAY_LENGTH(event_tab), sizeof(event_tab[0]), cmp_keyvalue_value_ni);
723
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100724 if (*p == ',')
725 ++p;
726 *end = p;
John Marriott78d742a2024-04-02 20:26:01 +0200727
728 return (entry == NULL) ? NUM_EVENTS : (event_T)entry->key;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100729}
730
731/*
732 * Return the name for event "event".
733 */
734 static char_u *
735event_nr2name(event_T event)
736{
737 int i;
John Marriott78d742a2024-04-02 20:26:01 +0200738#define CACHE_SIZE 12
739 static int cache_tab[CACHE_SIZE];
740 static int cache_last_index = -1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100741
John Marriott78d742a2024-04-02 20:26:01 +0200742 if (cache_last_index < 0)
743 {
744 for (i = 0; i < (int)ARRAY_LENGTH(cache_tab); ++i)
745 cache_tab[i] = -1;
746 cache_last_index = ARRAY_LENGTH(cache_tab) - 1;
747 }
748
749 // first look in the cache
750 // the cache is circular. to search it we start at the most recent entry
751 // and go backwards wrapping around when we get to index 0.
752 for (i = cache_last_index; cache_tab[i] >= 0; )
753 {
754 if ((event_T)event_tab[cache_tab[i]].key == event)
John Marriott8d4477e2024-11-02 15:59:01 +0100755 return event_tab[cache_tab[i]].value.string;
John Marriott78d742a2024-04-02 20:26:01 +0200756
757 if (i == 0)
758 i = ARRAY_LENGTH(cache_tab) - 1;
759 else
760 --i;
761
762 // are we back at the start?
763 if (i == cache_last_index)
764 break;
765 }
766
767 // look in the event table itself
768 for (i = 0; i < (int)ARRAY_LENGTH(event_tab); ++i)
769 {
770 if ((event_T)event_tab[i].key == event)
771 {
772 // store the found entry in the next position in the cache,
773 // wrapping around when we get to the maximum index.
774 if (cache_last_index == ARRAY_LENGTH(cache_tab) - 1)
775 cache_last_index = 0;
776 else
777 ++cache_last_index;
778 cache_tab[cache_last_index] = i;
779 break;
780 }
781 }
782
John Marriott8d4477e2024-11-02 15:59:01 +0100783 return (i == (int)ARRAY_LENGTH(event_tab)) ? (char_u *)"Unknown" :
784 event_tab[i].value.string;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100785}
786
787/*
788 * Scan over the events. "*" stands for all events.
789 */
790 static char_u *
791find_end_event(
792 char_u *arg,
793 int have_group) // TRUE when group name was found
794{
795 char_u *pat;
796 char_u *p;
797
798 if (*arg == '*')
799 {
800 if (arg[1] && !VIM_ISWHITE(arg[1]))
801 {
Bram Moolenaar6d057012021-12-31 18:49:43 +0000802 semsg(_(e_illegal_character_after_star_str), arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100803 return NULL;
804 }
805 pat = arg + 1;
806 }
807 else
808 {
809 for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p)
810 {
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100811 if ((int)event_name2nr(pat, &p) >= NUM_EVENTS)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100812 {
813 if (have_group)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000814 semsg(_(e_no_such_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100815 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000816 semsg(_(e_no_such_group_or_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100817 return NULL;
818 }
819 }
820 }
821 return pat;
822}
823
824/*
825 * Return TRUE if "event" is included in 'eventignore'.
826 */
827 static int
828event_ignored(event_T event)
829{
830 char_u *p = p_ei;
831
832 while (*p != NUL)
833 {
834 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
835 return TRUE;
836 if (event_name2nr(p, &p) == event)
837 return TRUE;
838 }
839
840 return FALSE;
841}
842
843/*
844 * Return OK when the contents of p_ei is valid, FAIL otherwise.
845 */
846 int
847check_ei(void)
848{
849 char_u *p = p_ei;
850
851 while (*p)
852 {
853 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
854 {
855 p += 3;
856 if (*p == ',')
857 ++p;
858 }
859 else if (event_name2nr(p, &p) == NUM_EVENTS)
860 return FAIL;
861 }
862
863 return OK;
864}
865
866# if defined(FEAT_SYN_HL) || defined(PROTO)
867
868/*
869 * Add "what" to 'eventignore' to skip loading syntax highlighting for every
870 * buffer loaded into the window. "what" must start with a comma.
871 * Returns the old value of 'eventignore' in allocated memory.
872 */
873 char_u *
874au_event_disable(char *what)
875{
876 char_u *new_ei;
877 char_u *save_ei;
John Marriott78d742a2024-04-02 20:26:01 +0200878 size_t p_ei_len;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100879
John Marriott78d742a2024-04-02 20:26:01 +0200880 p_ei_len = STRLEN(p_ei);
881 save_ei = vim_strnsave(p_ei, p_ei_len);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100882 if (save_ei == NULL)
883 return NULL;
884
John Marriott78d742a2024-04-02 20:26:01 +0200885 new_ei = vim_strnsave(p_ei, p_ei_len + STRLEN(what));
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100886 if (new_ei == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100887 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100888 vim_free(save_ei);
889 return NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100890 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100891
892 if (*what == ',' && *p_ei == NUL)
893 STRCPY(new_ei, what + 1);
894 else
895 STRCAT(new_ei, what);
896 set_string_option_direct((char_u *)"ei", -1, new_ei,
897 OPT_FREE, SID_NONE);
898 vim_free(new_ei);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100899 return save_ei;
900}
901
902 void
903au_event_restore(char_u *old_ei)
904{
905 if (old_ei != NULL)
906 {
907 set_string_option_direct((char_u *)"ei", -1, old_ei,
908 OPT_FREE, SID_NONE);
909 vim_free(old_ei);
910 }
911}
Bram Moolenaarc667da52019-11-30 20:52:27 +0100912# endif // FEAT_SYN_HL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100913
914/*
915 * do_autocmd() -- implements the :autocmd command. Can be used in the
916 * following ways:
917 *
918 * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
919 * will be automatically executed for <event>
920 * when editing a file matching <pat>, in
921 * the current group.
922 * :autocmd <event> <pat> Show the autocommands associated with
923 * <event> and <pat>.
924 * :autocmd <event> Show the autocommands associated with
925 * <event>.
926 * :autocmd Show all autocommands.
927 * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
928 * <event> and <pat>, and add the command
929 * <cmd>, for the current group.
930 * :autocmd! <event> <pat> Remove all autocommands associated with
931 * <event> and <pat> for the current group.
932 * :autocmd! <event> Remove all autocommands associated with
933 * <event> for the current group.
934 * :autocmd! Remove ALL autocommands for the current
935 * group.
936 *
937 * Multiple events and patterns may be given separated by commas. Here are
938 * some examples:
939 * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
940 * :autocmd bufleave * set tw=79 nosmartindent ic infercase
941 *
942 * :autocmd * *.c show all autocommands for *.c files.
943 *
944 * Mostly a {group} argument can optionally appear before <event>.
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200945 * "eap" can be NULL.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100946 */
947 void
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200948do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100949{
950 char_u *arg = arg_in;
951 char_u *pat;
952 char_u *envpat = NULL;
953 char_u *cmd;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200954 int cmd_need_free = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100955 event_T event;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200956 char_u *tofree = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100957 int nested = FALSE;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200958 int once = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100959 int group;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200960 int i;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200961 int flags = 0;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100962
963 if (*arg == '|')
964 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000965 eap->nextcmd = arg + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100966 arg = (char_u *)"";
967 group = AUGROUP_ALL; // no argument, use all groups
968 }
969 else
970 {
971 /*
972 * Check for a legal group name. If not, use AUGROUP_ALL.
973 */
974 group = au_get_grouparg(&arg);
975 if (arg == NULL) // out of memory
976 return;
977 }
978
979 /*
980 * Scan over the events.
981 * If we find an illegal name, return here, don't do anything.
982 */
983 pat = find_end_event(arg, group != AUGROUP_ALL);
984 if (pat == NULL)
985 return;
986
987 pat = skipwhite(pat);
988 if (*pat == '|')
989 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000990 eap->nextcmd = pat + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100991 pat = (char_u *)"";
992 cmd = (char_u *)"";
993 }
994 else
995 {
996 /*
997 * Scan over the pattern. Put a NUL at the end.
998 */
999 cmd = pat;
1000 while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
1001 cmd++;
1002 if (*cmd)
1003 *cmd++ = NUL;
1004
1005 // Expand environment variables in the pattern. Set 'shellslash', we
1006 // want forward slashes here.
1007 if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
1008 {
1009#ifdef BACKSLASH_IN_FILENAME
1010 int p_ssl_save = p_ssl;
1011
1012 p_ssl = TRUE;
1013#endif
1014 envpat = expand_env_save(pat);
1015#ifdef BACKSLASH_IN_FILENAME
1016 p_ssl = p_ssl_save;
1017#endif
1018 if (envpat != NULL)
1019 pat = envpat;
1020 }
1021
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001022 cmd = skipwhite(cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001023 for (i = 0; i < 2; i++)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001024 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001025 if (*cmd == NUL)
1026 continue;
1027
1028 // Check for "++once" flag.
1029 if (STRNCMP(cmd, "++once", 6) == 0 && VIM_ISWHITE(cmd[6]))
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001030 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001031 if (once)
1032 semsg(_(e_duplicate_argument_str), "++once");
1033 once = TRUE;
1034 cmd = skipwhite(cmd + 6);
1035 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001036
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001037 // Check for "++nested" flag.
1038 if ((STRNCMP(cmd, "++nested", 8) == 0 && VIM_ISWHITE(cmd[8])))
1039 {
1040 if (nested)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001041 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001042 semsg(_(e_duplicate_argument_str), "++nested");
1043 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001044 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001045 nested = TRUE;
1046 cmd = skipwhite(cmd + 8);
1047 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001048
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001049 // Check for the old "nested" flag in legacy script.
1050 if (STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
1051 {
1052 if (in_vim9script())
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001053 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001054 // If there ever is a :nested command this error should
1055 // be removed and "nested" accepted as the start of the
1056 // command.
1057 emsg(_(e_invalid_command_nested_did_you_mean_plusplus_nested));
1058 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001059 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001060 if (nested)
1061 {
1062 semsg(_(e_duplicate_argument_str), "nested");
1063 return;
1064 }
1065 nested = TRUE;
1066 cmd = skipwhite(cmd + 6);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001067 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001068 }
1069
1070 /*
1071 * Find the start of the commands.
1072 * Expand <sfile> in it.
1073 */
1074 if (*cmd != NUL)
1075 {
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001076 if (eap != NULL)
1077 // Read a {} block if it follows.
1078 cmd = may_get_cmd_block(eap, cmd, &tofree, &flags);
1079
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001080 cmd = expand_sfile(cmd);
1081 if (cmd == NULL) // some error
1082 return;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001083 cmd_need_free = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001084 }
1085 }
1086
1087 /*
1088 * Print header when showing autocommands.
1089 */
1090 if (!forceit && *cmd == NUL)
1091 // Highlight title
1092 msg_puts_title(_("\n--- Autocommands ---"));
1093
1094 /*
1095 * Loop over the events.
1096 */
1097 last_event = (event_T)-1; // for listing the event name
1098 last_group = AUGROUP_ERROR; // for listing the group name
1099 if (*arg == '*' || *arg == NUL || *arg == '|')
1100 {
Bram Moolenaarb6db1462021-12-24 19:24:47 +00001101 if (*cmd != NUL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001102 emsg(_(e_cannot_define_autocommands_for_all_events));
1103 else
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +01001104 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001105 event = (event_T)((int)event + 1))
1106 if (do_autocmd_event(event, pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001107 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001108 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001109 }
1110 else
1111 {
1112 while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
1113 if (do_autocmd_event(event_name2nr(arg, &arg), pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001114 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001115 break;
1116 }
1117
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001118 if (cmd_need_free)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001119 vim_free(cmd);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001120 vim_free(tofree);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001121 vim_free(envpat);
1122}
1123
1124/*
1125 * Find the group ID in a ":autocmd" or ":doautocmd" argument.
1126 * The "argp" argument is advanced to the following argument.
1127 *
1128 * Returns the group ID, AUGROUP_ERROR for error (out of memory).
1129 */
1130 static int
1131au_get_grouparg(char_u **argp)
1132{
1133 char_u *group_name;
1134 char_u *p;
1135 char_u *arg = *argp;
1136 int group = AUGROUP_ALL;
1137
1138 for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p)
1139 ;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001140 if (p <= arg)
1141 return AUGROUP_ALL;
1142
1143 group_name = vim_strnsave(arg, p - arg);
1144 if (group_name == NULL) // out of memory
1145 return AUGROUP_ERROR;
1146 group = au_find_group(group_name);
1147 if (group == AUGROUP_ERROR)
1148 group = AUGROUP_ALL; // no match, use all groups
1149 else
1150 *argp = skipwhite(p); // match, skip over group name
1151 vim_free(group_name);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001152 return group;
1153}
1154
1155/*
1156 * do_autocmd() for one event.
1157 * If *pat == NUL do for all patterns.
1158 * If *cmd == NUL show entries.
1159 * If forceit == TRUE delete entries.
1160 * If group is not AUGROUP_ALL, only use this group.
1161 */
1162 static int
1163do_autocmd_event(
1164 event_T event,
1165 char_u *pat,
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001166 int once,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001167 int nested,
1168 char_u *cmd,
1169 int forceit,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001170 int group,
1171 int flags)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001172{
1173 AutoPat *ap;
1174 AutoPat **prev_ap;
1175 AutoCmd *ac;
1176 AutoCmd **prev_ac;
1177 int brace_level;
1178 char_u *endpat;
1179 int findgroup;
1180 int allgroups;
1181 int patlen;
1182 int is_buflocal;
1183 int buflocal_nr;
Bram Moolenaarc667da52019-11-30 20:52:27 +01001184 char_u buflocal_pat[25]; // for "<buffer=X>"
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001185
1186 if (group == AUGROUP_ALL)
1187 findgroup = current_augroup;
1188 else
1189 findgroup = group;
1190 allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
1191
1192 /*
1193 * Show or delete all patterns for an event.
1194 */
1195 if (*pat == NUL)
1196 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001197 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001198 {
1199 if (forceit) // delete the AutoPat, if it's in the current group
1200 {
1201 if (ap->group == findgroup)
1202 au_remove_pat(ap);
1203 }
1204 else if (group == AUGROUP_ALL || ap->group == group)
1205 show_autocmd(ap, event);
1206 }
1207 }
1208
1209 /*
1210 * Loop through all the specified patterns.
1211 */
1212 for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
1213 {
1214 /*
1215 * Find end of the pattern.
1216 * Watch out for a comma in braces, like "*.\{obj,o\}".
1217 */
1218 brace_level = 0;
1219 for (endpat = pat; *endpat && (*endpat != ',' || brace_level
1220 || (endpat > pat && endpat[-1] == '\\')); ++endpat)
1221 {
1222 if (*endpat == '{')
1223 brace_level++;
1224 else if (*endpat == '}')
1225 brace_level--;
1226 }
1227 if (pat == endpat) // ignore single comma
1228 continue;
1229 patlen = (int)(endpat - pat);
1230
1231 /*
1232 * detect special <buflocal[=X]> buffer-local patterns
1233 */
1234 is_buflocal = FALSE;
1235 buflocal_nr = 0;
1236
1237 if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
1238 && pat[patlen - 1] == '>')
1239 {
1240 // "<buffer...>": Error will be printed only for addition.
1241 // printing and removing will proceed silently.
1242 is_buflocal = TRUE;
1243 if (patlen == 8)
1244 // "<buffer>"
1245 buflocal_nr = curbuf->b_fnum;
1246 else if (patlen > 9 && pat[7] == '=')
1247 {
1248 if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0)
1249 // "<buffer=abuf>"
1250 buflocal_nr = autocmd_bufnr;
1251 else if (skipdigits(pat + 8) == pat + patlen - 1)
1252 // "<buffer=123>"
1253 buflocal_nr = atoi((char *)pat + 8);
1254 }
1255 }
1256
1257 if (is_buflocal)
1258 {
1259 // normalize pat into standard "<buffer>#N" form
1260 sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
1261 pat = buflocal_pat; // can modify pat and patlen
1262 patlen = (int)STRLEN(buflocal_pat); // but not endpat
1263 }
1264
1265 /*
1266 * Find AutoPat entries with this pattern. When adding a command it
1267 * always goes at or after the last one, so start at the end.
1268 */
1269 if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL)
1270 prev_ap = &last_autopat[(int)event];
1271 else
1272 prev_ap = &first_autopat[(int)event];
1273 while ((ap = *prev_ap) != NULL)
1274 {
1275 if (ap->pat != NULL)
1276 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001277 /*
1278 * Accept a pattern when:
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001279 * - a group was specified and it's that group, or a group was
1280 * not specified and it's the current group, or a group was
1281 * not specified and we are listing
1282 * - the length of the pattern matches
1283 * - the pattern matches.
1284 * For <buffer[=X]>, this condition works because we normalize
1285 * all buffer-local patterns.
1286 */
1287 if ((allgroups || ap->group == findgroup)
1288 && ap->patlen == patlen
1289 && STRNCMP(pat, ap->pat, patlen) == 0)
1290 {
1291 /*
1292 * Remove existing autocommands.
1293 * If adding any new autocmd's for this AutoPat, don't
1294 * delete the pattern from the autopat list, append to
1295 * this list.
1296 */
1297 if (forceit)
1298 {
1299 if (*cmd != NUL && ap->next == NULL)
1300 {
1301 au_remove_cmds(ap);
1302 break;
1303 }
1304 au_remove_pat(ap);
1305 }
1306
1307 /*
1308 * Show autocmd's for this autopat, or buflocals <buffer=X>
1309 */
1310 else if (*cmd == NUL)
1311 show_autocmd(ap, event);
1312
1313 /*
1314 * Add autocmd to this autopat, if it's the last one.
1315 */
1316 else if (ap->next == NULL)
1317 break;
1318 }
1319 }
1320 prev_ap = &ap->next;
1321 }
1322
1323 /*
1324 * Add a new command.
1325 */
1326 if (*cmd != NUL)
1327 {
1328 /*
1329 * If the pattern we want to add a command to does appear at the
1330 * end of the list (or not is not in the list at all), add the
1331 * pattern at the end of the list.
1332 */
1333 if (ap == NULL)
1334 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001335 // refuse to add buffer-local ap if buffer number is invalid
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001336 if (is_buflocal && (buflocal_nr == 0
1337 || buflist_findnr(buflocal_nr) == NULL))
1338 {
Bram Moolenaarf1474d82021-12-31 19:59:55 +00001339 semsg(_(e_buffer_nr_invalid_buffer_number), buflocal_nr);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001340 return FAIL;
1341 }
1342
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001343 ap = ALLOC_ONE(AutoPat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001344 if (ap == NULL)
1345 return FAIL;
1346 ap->pat = vim_strnsave(pat, patlen);
1347 ap->patlen = patlen;
1348 if (ap->pat == NULL)
1349 {
1350 vim_free(ap);
1351 return FAIL;
1352 }
1353
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001354#ifdef FEAT_EVAL
1355 // need to initialize last_mode for the first ModeChanged
1356 // autocmd
1357 if (event == EVENT_MODECHANGED && !has_modechanged())
LemonBoy2bf52dd2022-04-09 18:17:34 +01001358 get_mode(last_mode);
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001359#endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001360 // Initialize the fields checked by the WinScrolled and
1361 // WinResized trigger to prevent them from firing right after
1362 // the first autocmd is defined.
1363 if ((event == EVENT_WINSCROLLED || event == EVENT_WINRESIZED)
1364 && !(has_winscrolled() || has_winresized()))
LemonBoy09371822022-04-08 15:18:45 +01001365 {
Bram Moolenaar29967732022-11-20 12:11:45 +00001366 tabpage_T *save_curtab = curtab;
1367 tabpage_T *tp;
1368 FOR_ALL_TABPAGES(tp)
1369 {
1370 unuse_tabpage(curtab);
1371 use_tabpage(tp);
1372 snapshot_windows_scroll_size();
1373 }
1374 unuse_tabpage(curtab);
1375 use_tabpage(save_curtab);
LemonBoy09371822022-04-08 15:18:45 +01001376 }
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001377
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001378 if (is_buflocal)
1379 {
1380 ap->buflocal_nr = buflocal_nr;
1381 ap->reg_prog = NULL;
1382 }
1383 else
1384 {
1385 char_u *reg_pat;
1386
1387 ap->buflocal_nr = 0;
1388 reg_pat = file_pat_to_reg_pat(pat, endpat,
1389 &ap->allow_dirs, TRUE);
1390 if (reg_pat != NULL)
1391 ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
1392 vim_free(reg_pat);
1393 if (reg_pat == NULL || ap->reg_prog == NULL)
1394 {
1395 vim_free(ap->pat);
1396 vim_free(ap);
1397 return FAIL;
1398 }
1399 }
1400 ap->cmds = NULL;
1401 *prev_ap = ap;
1402 last_autopat[(int)event] = ap;
1403 ap->next = NULL;
1404 if (group == AUGROUP_ALL)
1405 ap->group = current_augroup;
1406 else
1407 ap->group = group;
1408 }
1409
1410 /*
1411 * Add the autocmd at the end of the AutoCmd list.
1412 */
1413 prev_ac = &(ap->cmds);
1414 while ((ac = *prev_ac) != NULL)
1415 prev_ac = &ac->next;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001416 ac = ALLOC_ONE(AutoCmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001417 if (ac == NULL)
1418 return FAIL;
1419 ac->cmd = vim_strsave(cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001420 ac->script_ctx = current_sctx;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001421 if (flags & UC_VIM9)
1422 ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001423#ifdef FEAT_EVAL
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001424 ac->script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001425#endif
1426 if (ac->cmd == NULL)
1427 {
1428 vim_free(ac);
1429 return FAIL;
1430 }
1431 ac->next = NULL;
1432 *prev_ac = ac;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001433 ac->once = once;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001434 ac->nested = nested;
1435 }
1436 }
1437
1438 au_cleanup(); // may really delete removed patterns/commands now
1439 return OK;
1440}
1441
1442/*
1443 * Implementation of ":doautocmd [group] event [fname]".
1444 * Return OK for success, FAIL for failure;
1445 */
1446 int
1447do_doautocmd(
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001448 char_u *arg_start,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001449 int do_msg, // give message for no matching autocmds?
1450 int *did_something)
1451{
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001452 char_u *arg = arg_start;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001453 char_u *fname;
1454 int nothing_done = TRUE;
1455 int group;
1456
1457 if (did_something != NULL)
1458 *did_something = FALSE;
1459
1460 /*
1461 * Check for a legal group name. If not, use AUGROUP_ALL.
1462 */
1463 group = au_get_grouparg(&arg);
1464 if (arg == NULL) // out of memory
1465 return FAIL;
1466
1467 if (*arg == '*')
1468 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001469 emsg(_(e_cant_execute_autocommands_for_all_events));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001470 return FAIL;
1471 }
1472
1473 /*
1474 * Scan over the events.
1475 * If we find an illegal name, return here, don't do anything.
1476 */
1477 fname = find_end_event(arg, group != AUGROUP_ALL);
1478 if (fname == NULL)
1479 return FAIL;
1480
1481 fname = skipwhite(fname);
1482
1483 /*
1484 * Loop over the events.
1485 */
1486 while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
1487 if (apply_autocmds_group(event_name2nr(arg, &arg),
1488 fname, NULL, TRUE, group, curbuf, NULL))
1489 nothing_done = FALSE;
1490
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001491 if (nothing_done && do_msg
1492#ifdef FEAT_EVAL
1493 && !aborting()
1494#endif
1495 )
1496 smsg(_("No matching autocommands: %s"), arg_start);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001497 if (did_something != NULL)
1498 *did_something = !nothing_done;
1499
1500#ifdef FEAT_EVAL
1501 return aborting() ? FAIL : OK;
1502#else
1503 return OK;
1504#endif
1505}
1506
1507/*
1508 * ":doautoall": execute autocommands for each loaded buffer.
1509 */
1510 void
1511ex_doautoall(exarg_T *eap)
1512{
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001513 int retval = OK;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001514 aco_save_T aco;
1515 buf_T *buf;
1516 bufref_T bufref;
1517 char_u *arg = eap->arg;
1518 int call_do_modelines = check_nomodeline(&arg);
1519 int did_aucmd;
1520
1521 /*
1522 * This is a bit tricky: For some commands curwin->w_buffer needs to be
1523 * equal to curbuf, but for some buffers there may not be a window.
1524 * So we change the buffer for the current window for a moment. This
1525 * gives problems when the autocommands make changes to the list of
1526 * buffers or windows...
1527 */
1528 FOR_ALL_BUFFERS(buf)
1529 {
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001530 // Only do loaded buffers and skip the current buffer, it's done last.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001531 if (buf->b_ml.ml_mfp == NULL || buf == curbuf)
1532 continue;
1533
Bram Moolenaare76062c2022-11-28 18:51:43 +00001534 // Find a window for this buffer and save some values.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001535 aucmd_prepbuf(&aco, buf);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001536 if (curbuf != buf)
1537 {
1538 // Failed to find a window for this buffer. Better not execute
1539 // autocommands then.
1540 retval = FAIL;
1541 break;
1542 }
1543
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001544 set_bufref(&bufref, buf);
1545
1546 // execute the autocommands for this buffer
1547 retval = do_doautocmd(arg, FALSE, &did_aucmd);
1548
1549 if (call_do_modelines && did_aucmd)
1550 // Execute the modeline settings, but don't set window-local
1551 // options if we are using the current window for another
1552 // buffer.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001553 do_modelines(is_aucmd_win(curwin) ? OPT_NOWIN : 0);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001554
1555 // restore the current window
1556 aucmd_restbuf(&aco);
1557
1558 // stop if there is some error or buffer was deleted
1559 if (retval == FAIL || !bufref_valid(&bufref))
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001560 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001561 retval = FAIL;
1562 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001563 }
1564 }
1565
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001566 // Execute autocommands for the current buffer last.
1567 if (retval == OK)
1568 {
1569 do_doautocmd(arg, FALSE, &did_aucmd);
1570 if (call_do_modelines && did_aucmd)
1571 do_modelines(0);
1572 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001573}
1574
1575/*
1576 * Check *argp for <nomodeline>. When it is present return FALSE, otherwise
1577 * return TRUE and advance *argp to after it.
1578 * Thus return TRUE when do_modelines() should be called.
1579 */
1580 int
1581check_nomodeline(char_u **argp)
1582{
1583 if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
1584 {
1585 *argp = skipwhite(*argp + 12);
1586 return FALSE;
1587 }
1588 return TRUE;
1589}
1590
1591/*
1592 * Prepare for executing autocommands for (hidden) buffer "buf".
1593 * Search for a visible window containing the current buffer. If there isn't
Bram Moolenaare76062c2022-11-28 18:51:43 +00001594 * one then use an entry in "aucmd_win[]".
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001595 * Set "curbuf" and "curwin" to match "buf".
Bram Moolenaare76062c2022-11-28 18:51:43 +00001596 * When this fails "curbuf" is not equal "buf".
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001597 */
1598 void
1599aucmd_prepbuf(
1600 aco_save_T *aco, // structure to save values in
1601 buf_T *buf) // new curbuf
1602{
1603 win_T *win;
1604 int save_ea;
1605#ifdef FEAT_AUTOCHDIR
1606 int save_acd;
1607#endif
1608
1609 // Find a window that is for the new buffer
1610 if (buf == curbuf) // be quick when buf is curbuf
1611 win = curwin;
1612 else
1613 FOR_ALL_WINDOWS(win)
1614 if (win->w_buffer == buf)
1615 break;
1616
Bram Moolenaare76062c2022-11-28 18:51:43 +00001617 // Allocate a window when needed.
1618 win_T *auc_win = NULL;
1619 int auc_idx = AUCMD_WIN_COUNT;
1620 if (win == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001621 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001622 for (auc_idx = 0; auc_idx < AUCMD_WIN_COUNT; ++auc_idx)
1623 if (!aucmd_win[auc_idx].auc_win_used)
1624 {
Bram Moolenaar84497cd2022-11-28 20:34:52 +00001625 if (aucmd_win[auc_idx].auc_win == NULL)
1626 aucmd_win[auc_idx].auc_win = win_alloc_popup_win();
1627 auc_win = aucmd_win[auc_idx].auc_win;
Bram Moolenaare76062c2022-11-28 18:51:43 +00001628 if (auc_win != NULL)
Bram Moolenaare76062c2022-11-28 18:51:43 +00001629 aucmd_win[auc_idx].auc_win_used = TRUE;
Bram Moolenaare76062c2022-11-28 18:51:43 +00001630 break;
1631 }
1632
1633 // If this fails (out of memory or using all AUCMD_WIN_COUNT
1634 // entries) then we can't reliable execute the autocmd, return with
1635 // "curbuf" unequal "buf".
1636 if (auc_win == NULL)
1637 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001638 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001639
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001640 aco->save_curwin_id = curwin->w_id;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001641 aco->save_prevwin_id = prevwin == NULL ? 0 : prevwin->w_id;
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001642 aco->save_State = State;
zeertzjqf2678472024-01-17 21:22:59 +01001643#ifdef FEAT_JOB_CHANNEL
1644 if (bt_prompt(curbuf))
1645 aco->save_prompt_insert = curbuf->b_prompt_insert;
1646#endif
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001647
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001648 if (win != NULL)
1649 {
1650 // There is a window for "buf" in the current tab page, make it the
1651 // curwin. This is preferred, it has the least side effects (esp. if
1652 // "buf" is curbuf).
Bram Moolenaare76062c2022-11-28 18:51:43 +00001653 aco->use_aucmd_win_idx = -1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001654 curwin = win;
1655 }
1656 else
1657 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001658 // There is no window for "buf", use "auc_win". To minimize the side
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001659 // effects, insert it in the current tab page.
1660 // Anything related to a window (e.g., setting folds) may have
1661 // unexpected results.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001662 aco->use_aucmd_win_idx = auc_idx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001663
Bram Moolenaare76062c2022-11-28 18:51:43 +00001664 win_init_popup_win(auc_win, buf);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001665
zeertzjq9d956ee2024-04-07 18:16:10 +02001666 // Make sure tp_localdir and globaldir are NULL to avoid a
1667 // chdir() in win_enter_ext().
1668 // win_init_popup_win() has already set w_localdir to NULL.
1669 aco->tp_localdir = curtab->tp_localdir;
1670 curtab->tp_localdir = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001671 aco->globaldir = globaldir;
1672 globaldir = NULL;
1673
Bram Moolenaare76062c2022-11-28 18:51:43 +00001674 // Split the current window, put the auc_win in the upper half.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001675 // We don't want the BufEnter or WinEnter autocommands.
1676 block_autocmds();
1677 make_snapshot(SNAP_AUCMD_IDX);
1678 save_ea = p_ea;
1679 p_ea = FALSE;
1680
1681#ifdef FEAT_AUTOCHDIR
1682 // Prevent chdir() call in win_enter_ext(), through do_autochdir().
1683 save_acd = p_acd;
1684 p_acd = FALSE;
1685#endif
1686
Sean Dewar704966c2024-02-20 22:00:33 +01001687 (void)win_split_ins(0, WSP_TOP | WSP_FORCE_ROOM, auc_win, 0, NULL);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001688 (void)win_comp_pos(); // recompute window positions
1689 p_ea = save_ea;
1690#ifdef FEAT_AUTOCHDIR
1691 p_acd = save_acd;
1692#endif
1693 unblock_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001694 curwin = auc_win;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001695 }
1696 curbuf = buf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001697 aco->new_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001698 set_bufref(&aco->new_curbuf, curbuf);
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001699
1700 // disable the Visual area, the position may be invalid in another buffer
1701 aco->save_VIsual_active = VIsual_active;
1702 VIsual_active = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001703}
1704
1705/*
1706 * Cleanup after executing autocommands for a (hidden) buffer.
1707 * Restore the window as it was (if possible).
1708 */
1709 void
1710aucmd_restbuf(
1711 aco_save_T *aco) // structure holding saved values
1712{
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001713 int dummy;
1714 win_T *save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001715
Bram Moolenaare76062c2022-11-28 18:51:43 +00001716 if (aco->use_aucmd_win_idx >= 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001717 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001718 win_T *awp = aucmd_win[aco->use_aucmd_win_idx].auc_win;
1719
Bram Moolenaare76062c2022-11-28 18:51:43 +00001720 // Find "awp", it can't be closed, but it may be in another tab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001721 // page. Do not trigger autocommands here.
1722 block_autocmds();
Bram Moolenaare76062c2022-11-28 18:51:43 +00001723 if (curwin != awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001724 {
1725 tabpage_T *tp;
1726 win_T *wp;
1727
1728 FOR_ALL_TAB_WINDOWS(tp, wp)
1729 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001730 if (wp == awp)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001731 {
1732 if (tp != curtab)
1733 goto_tabpage_tp(tp, TRUE, TRUE);
Bram Moolenaare76062c2022-11-28 18:51:43 +00001734 win_goto(awp);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001735 goto win_found;
1736 }
1737 }
1738 }
1739win_found:
zeertzjq46bdae02023-09-24 23:16:08 +02001740 --curbuf->b_nwindows;
orbitalcde8de02023-04-02 22:05:13 +01001741#ifdef FEAT_JOB_CHANNEL
zeertzjqa40c0bc2023-05-27 14:10:08 +01001742 int save_stop_insert_mode = stop_insert_mode;
orbitalcde8de02023-04-02 22:05:13 +01001743 // May need to stop Insert mode if we were in a prompt buffer.
1744 leaving_window(curwin);
Bram Moolenaar05a627c2023-04-09 22:01:31 +01001745 // Do not stop Insert mode when already in Insert mode before.
1746 if (aco->save_State & MODE_INSERT)
zeertzjqa40c0bc2023-05-27 14:10:08 +01001747 stop_insert_mode = save_stop_insert_mode;
orbitalcde8de02023-04-02 22:05:13 +01001748#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001749 // Remove the window and frame from the tree of frames.
Sean Dewar704966c2024-02-20 22:00:33 +01001750 (void)winframe_remove(curwin, &dummy, NULL, NULL);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001751 win_remove(curwin, NULL);
Bram Moolenaar84497cd2022-11-28 20:34:52 +00001752
1753 // The window is marked as not used, but it is not freed, it can be
1754 // used again.
Bram Moolenaare76062c2022-11-28 18:51:43 +00001755 aucmd_win[aco->use_aucmd_win_idx].auc_win_used = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001756 last_status(FALSE); // may need to remove last status line
1757
1758 if (!valid_tabpage_win(curtab))
1759 // no valid window in current tabpage
1760 close_tabpage(curtab);
1761
1762 restore_snapshot(SNAP_AUCMD_IDX, FALSE);
1763 (void)win_comp_pos(); // recompute window positions
1764 unblock_autocmds();
1765
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001766 save_curwin = win_find_by_id(aco->save_curwin_id);
1767 if (save_curwin != NULL)
1768 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001769 else
1770 // Hmm, original window disappeared. Just use the first one.
1771 curwin = firstwin;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001772 curbuf = curwin->w_buffer;
1773#ifdef FEAT_JOB_CHANNEL
1774 // May need to restore insert mode for a prompt buffer.
1775 entering_window(curwin);
zeertzjqf2678472024-01-17 21:22:59 +01001776 if (bt_prompt(curbuf))
1777 curbuf->b_prompt_insert = aco->save_prompt_insert;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001778#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001779 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001780#ifdef FEAT_EVAL
Bram Moolenaare76062c2022-11-28 18:51:43 +00001781 vars_clear(&awp->w_vars->dv_hashtab); // free all w: variables
1782 hash_init(&awp->w_vars->dv_hashtab); // re-use the hashtab
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001783#endif
zeertzjq9d956ee2024-04-07 18:16:10 +02001784 // If :lcd has been used in the autocommand window, correct current
1785 // directory before restoring tp_localdir and globaldir.
1786 if (awp->w_localdir != NULL)
1787 win_fix_current_dir();
1788 vim_free(curtab->tp_localdir);
1789 curtab->tp_localdir = aco->tp_localdir;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001790 vim_free(globaldir);
1791 globaldir = aco->globaldir;
1792
1793 // the buffer contents may have changed
zeertzjq49f05242023-02-04 10:58:34 +00001794 VIsual_active = aco->save_VIsual_active;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001795 check_cursor();
1796 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1797 {
1798 curwin->w_topline = curbuf->b_ml.ml_line_count;
1799#ifdef FEAT_DIFF
1800 curwin->w_topfill = 0;
1801#endif
1802 }
1803#if defined(FEAT_GUI)
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001804 if (gui.in_use)
1805 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00001806 // Hide the scrollbars from the "awp" and update.
1807 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_LEFT], FALSE);
1808 gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_RIGHT], FALSE);
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001809 gui_may_update_scrollbars();
1810 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001811#endif
1812 }
1813 else
1814 {
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001815 // Restore curwin. Use the window ID, a window may have been closed
1816 // and the memory re-used for another one.
1817 save_curwin = win_find_by_id(aco->save_curwin_id);
1818 if (save_curwin != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001819 {
1820 // Restore the buffer which was previously edited by curwin, if
1821 // it was changed, we are still the same window and the buffer is
1822 // valid.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001823 if (curwin->w_id == aco->new_curwin_id
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001824 && curbuf != aco->new_curbuf.br_buf
1825 && bufref_valid(&aco->new_curbuf)
1826 && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
1827 {
1828# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
1829 if (curwin->w_s == &curbuf->b_s)
1830 curwin->w_s = &aco->new_curbuf.br_buf->b_s;
1831# endif
1832 --curbuf->b_nwindows;
1833 curbuf = aco->new_curbuf.br_buf;
1834 curwin->w_buffer = curbuf;
1835 ++curbuf->b_nwindows;
1836 }
1837
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001838 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001839 curbuf = curwin->w_buffer;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001840 prevwin = win_find_by_id(aco->save_prevwin_id);
zeertzjq49f05242023-02-04 10:58:34 +00001841
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001842 // In case the autocommand moves the cursor to a position that
1843 // does not exist in curbuf.
zeertzjq49f05242023-02-04 10:58:34 +00001844 VIsual_active = aco->save_VIsual_active;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001845 check_cursor();
1846 }
1847 }
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001848
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001849 VIsual_active = aco->save_VIsual_active;
zeertzjq49f05242023-02-04 10:58:34 +00001850 check_cursor(); // just in case lines got deleted
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001851 if (VIsual_active)
1852 check_pos(curbuf, &VIsual);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001853}
1854
1855static int autocmd_nested = FALSE;
1856
1857/*
1858 * Execute autocommands for "event" and file name "fname".
1859 * Return TRUE if some commands were executed.
1860 */
1861 int
1862apply_autocmds(
1863 event_T event,
1864 char_u *fname, // NULL or empty means use actual file name
1865 char_u *fname_io, // fname to use for <afile> on cmdline
1866 int force, // when TRUE, ignore autocmd_busy
1867 buf_T *buf) // buffer for <abuf>
1868{
1869 return apply_autocmds_group(event, fname, fname_io, force,
1870 AUGROUP_ALL, buf, NULL);
1871}
1872
1873/*
1874 * Like apply_autocmds(), but with extra "eap" argument. This takes care of
1875 * setting v:filearg.
1876 */
1877 int
1878apply_autocmds_exarg(
1879 event_T event,
1880 char_u *fname,
1881 char_u *fname_io,
1882 int force,
1883 buf_T *buf,
1884 exarg_T *eap)
1885{
1886 return apply_autocmds_group(event, fname, fname_io, force,
1887 AUGROUP_ALL, buf, eap);
1888}
1889
1890/*
1891 * Like apply_autocmds(), but handles the caller's retval. If the script
1892 * processing is being aborted or if retval is FAIL when inside a try
1893 * conditional, no autocommands are executed. If otherwise the autocommands
1894 * cause the script to be aborted, retval is set to FAIL.
1895 */
1896 int
1897apply_autocmds_retval(
1898 event_T event,
1899 char_u *fname, // NULL or empty means use actual file name
1900 char_u *fname_io, // fname to use for <afile> on cmdline
1901 int force, // when TRUE, ignore autocmd_busy
1902 buf_T *buf, // buffer for <abuf>
1903 int *retval) // pointer to caller's retval
1904{
1905 int did_cmd;
1906
1907#ifdef FEAT_EVAL
1908 if (should_abort(*retval))
1909 return FALSE;
1910#endif
1911
1912 did_cmd = apply_autocmds_group(event, fname, fname_io, force,
1913 AUGROUP_ALL, buf, NULL);
1914 if (did_cmd
1915#ifdef FEAT_EVAL
1916 && aborting()
1917#endif
1918 )
1919 *retval = FAIL;
1920 return did_cmd;
1921}
1922
1923/*
1924 * Return TRUE when there is a CursorHold autocommand defined.
1925 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001926 static int
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001927has_cursorhold(void)
1928{
Bram Moolenaar24959102022-05-07 20:01:16 +01001929 return (first_autopat[(int)(get_real_state() == MODE_NORMAL_BUSY
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001930 ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
1931}
1932
1933/*
1934 * Return TRUE if the CursorHold event can be triggered.
1935 */
1936 int
1937trigger_cursorhold(void)
1938{
1939 int state;
1940
1941 if (!did_cursorhold
1942 && has_cursorhold()
1943 && reg_recording == 0
1944 && typebuf.tb_len == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001945 && !ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001946 {
1947 state = get_real_state();
Bram Moolenaar24959102022-05-07 20:01:16 +01001948 if (state == MODE_NORMAL_BUSY || (state & MODE_INSERT) != 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001949 return TRUE;
1950 }
1951 return FALSE;
1952}
1953
1954/*
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00001955 * Return TRUE when there is a WinResized autocommand defined.
1956 */
1957 int
1958has_winresized(void)
1959{
1960 return (first_autopat[(int)EVENT_WINRESIZED] != NULL);
1961}
1962
1963/*
LemonBoy09371822022-04-08 15:18:45 +01001964 * Return TRUE when there is a WinScrolled autocommand defined.
1965 */
1966 int
1967has_winscrolled(void)
1968{
1969 return (first_autopat[(int)EVENT_WINSCROLLED] != NULL);
1970}
1971
1972/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001973 * Return TRUE when there is a CursorMoved autocommand defined.
1974 */
1975 int
1976has_cursormoved(void)
1977{
1978 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1979}
1980
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001981/*
1982 * Return TRUE when there is a CursorMovedI autocommand defined.
1983 */
1984 int
1985has_cursormovedI(void)
1986{
1987 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1988}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001989
1990/*
1991 * Return TRUE when there is a TextChanged autocommand defined.
1992 */
1993 int
1994has_textchanged(void)
1995{
1996 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1997}
1998
1999/*
2000 * Return TRUE when there is a TextChangedI autocommand defined.
2001 */
2002 int
2003has_textchangedI(void)
2004{
2005 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
2006}
2007
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002008/*
2009 * Return TRUE when there is a TextChangedP autocommand defined.
2010 */
2011 int
2012has_textchangedP(void)
2013{
2014 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
2015}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002016
2017/*
2018 * Return TRUE when there is an InsertCharPre autocommand defined.
2019 */
2020 int
2021has_insertcharpre(void)
2022{
2023 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
2024}
2025
2026/*
Shougo Matsushita83678842024-07-11 22:05:12 +02002027 * Return TRUE when there is an KeyInputPre autocommand defined.
2028 */
2029 int
2030has_keyinputpre(void)
2031{
2032 return (first_autopat[(int)EVENT_KEYINPUTPRE] != NULL);
2033}
2034
2035/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002036 * Return TRUE when there is an CmdUndefined autocommand defined.
2037 */
2038 int
2039has_cmdundefined(void)
2040{
2041 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
2042}
2043
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002044#if defined(FEAT_EVAL) || defined(PROTO)
2045/*
2046 * Return TRUE when there is a TextYankPost autocommand defined.
2047 */
2048 int
2049has_textyankpost(void)
2050{
2051 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
2052}
2053#endif
2054
Bram Moolenaard7f246c2019-04-08 18:15:41 +02002055#if defined(FEAT_EVAL) || defined(PROTO)
2056/*
2057 * Return TRUE when there is a CompleteChanged autocommand defined.
2058 */
2059 int
2060has_completechanged(void)
2061{
2062 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
2063}
2064#endif
2065
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002066#if defined(FEAT_EVAL) || defined(PROTO)
2067/*
2068 * Return TRUE when there is a ModeChanged autocommand defined.
2069 */
2070 int
2071has_modechanged(void)
2072{
2073 return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
2074}
2075#endif
2076
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002077/*
2078 * Execute autocommands for "event" and file name "fname".
2079 * Return TRUE if some commands were executed.
2080 */
2081 static int
2082apply_autocmds_group(
2083 event_T event,
2084 char_u *fname, // NULL or empty means use actual file name
2085 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
2086 // use fname
2087 int force, // when TRUE, ignore autocmd_busy
2088 int group, // group ID, or AUGROUP_ALL
2089 buf_T *buf, // buffer for <abuf>
2090 exarg_T *eap UNUSED) // command arguments
2091{
2092 char_u *sfname = NULL; // short file name
2093 char_u *tail;
2094 int save_changed;
2095 buf_T *old_curbuf;
2096 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002097 char_u *save_autocmd_fname;
2098 int save_autocmd_fname_full;
2099 int save_autocmd_bufnr;
2100 char_u *save_autocmd_match;
2101 int save_autocmd_busy;
2102 int save_autocmd_nested;
2103 static int nesting = 0;
LemonBoyeca7c602022-04-14 15:39:43 +01002104 AutoPatCmd_T patcmd;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002105 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002106 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002107#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002108 funccal_entry_T funccal_entry;
2109 char_u *save_cmdarg;
2110 long save_cmdbang;
2111#endif
2112 static int filechangeshell_busy = FALSE;
2113#ifdef FEAT_PROFILE
2114 proftime_T wait_time;
2115#endif
2116 int did_save_redobuff = FALSE;
2117 save_redo_T save_redo;
2118 int save_KeyTyped = KeyTyped;
ichizok7e5fe382023-04-15 13:17:50 +01002119 ESTACK_CHECK_DECLARATION;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002120
2121 /*
2122 * Quickly return if there are no autocommands for this event or
2123 * autocommands are blocked.
2124 */
2125 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
2126 || autocmd_blocked > 0)
2127 goto BYPASS_AU;
2128
2129 /*
2130 * When autocommands are busy, new autocommands are only executed when
2131 * explicitly enabled with the "nested" flag.
2132 */
2133 if (autocmd_busy && !(force || autocmd_nested))
2134 goto BYPASS_AU;
2135
2136#ifdef FEAT_EVAL
2137 /*
2138 * Quickly return when immediately aborting on error, or when an interrupt
2139 * occurred or an exception was thrown but not caught.
2140 */
2141 if (aborting())
2142 goto BYPASS_AU;
2143#endif
2144
2145 /*
2146 * FileChangedShell never nests, because it can create an endless loop.
2147 */
2148 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
2149 || event == EVENT_FILECHANGEDSHELLPOST))
2150 goto BYPASS_AU;
2151
2152 /*
2153 * Ignore events in 'eventignore'.
2154 */
2155 if (event_ignored(event))
2156 goto BYPASS_AU;
2157
2158 /*
2159 * Allow nesting of autocommands, but restrict the depth, because it's
2160 * possible to create an endless loop.
2161 */
2162 if (nesting == 10)
2163 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00002164 emsg(_(e_autocommand_nesting_too_deep));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002165 goto BYPASS_AU;
2166 }
2167
2168 /*
2169 * Check if these autocommands are disabled. Used when doing ":all" or
2170 * ":ball".
2171 */
2172 if ( (autocmd_no_enter
2173 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
2174 || (autocmd_no_leave
2175 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
2176 goto BYPASS_AU;
2177
Bram Moolenaarbb393d82022-12-09 12:21:50 +00002178 if (event == EVENT_CMDLINECHANGED)
2179 ++aucmd_cmdline_changed_count;
2180
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002181 /*
2182 * Save the autocmd_* variables and info about the current buffer.
2183 */
2184 save_autocmd_fname = autocmd_fname;
2185 save_autocmd_fname_full = autocmd_fname_full;
2186 save_autocmd_bufnr = autocmd_bufnr;
2187 save_autocmd_match = autocmd_match;
2188 save_autocmd_busy = autocmd_busy;
2189 save_autocmd_nested = autocmd_nested;
2190 save_changed = curbuf->b_changed;
2191 old_curbuf = curbuf;
2192
2193 /*
2194 * Set the file name to be used for <afile>.
2195 * Make a copy to avoid that changing a buffer name or directory makes it
2196 * invalid.
2197 */
2198 if (fname_io == NULL)
2199 {
2200 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
Bram Moolenaarbb393d82022-12-09 12:21:50 +00002201 || event == EVENT_OPTIONSET
Danek Duvalld7d56032024-01-14 20:19:59 +01002202 || event == EVENT_MODECHANGED
2203 || event == EVENT_TERMRESPONSEALL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002204 autocmd_fname = NULL;
2205 else if (fname != NULL && !ends_excmd(*fname))
2206 autocmd_fname = fname;
2207 else if (buf != NULL)
2208 autocmd_fname = buf->b_ffname;
2209 else
2210 autocmd_fname = NULL;
2211 }
2212 else
2213 autocmd_fname = fname_io;
2214 if (autocmd_fname != NULL)
2215 autocmd_fname = vim_strsave(autocmd_fname);
2216 autocmd_fname_full = FALSE; // call FullName_save() later
2217
2218 /*
2219 * Set the buffer number to be used for <abuf>.
2220 */
2221 if (buf == NULL)
2222 autocmd_bufnr = 0;
2223 else
2224 autocmd_bufnr = buf->b_fnum;
2225
2226 /*
2227 * When the file name is NULL or empty, use the file name of buffer "buf".
2228 * Always use the full path of the file name to match with, in case
2229 * "allow_dirs" is set.
2230 */
2231 if (fname == NULL || *fname == NUL)
2232 {
2233 if (buf == NULL)
2234 fname = NULL;
2235 else
2236 {
2237#ifdef FEAT_SYN_HL
2238 if (event == EVENT_SYNTAX)
2239 fname = buf->b_p_syn;
2240 else
2241#endif
2242 if (event == EVENT_FILETYPE)
2243 fname = buf->b_p_ft;
2244 else
2245 {
2246 if (buf->b_sfname != NULL)
2247 sfname = vim_strsave(buf->b_sfname);
2248 fname = buf->b_ffname;
2249 }
2250 }
2251 if (fname == NULL)
2252 fname = (char_u *)"";
2253 fname = vim_strsave(fname); // make a copy, so we can change it
2254 }
2255 else
2256 {
2257 sfname = vim_strsave(fname);
2258 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002259 // ColorScheme, QuickFixCmd*, DirChanged and similar.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002260 if (event == EVENT_FILETYPE
2261 || event == EVENT_SYNTAX
2262 || event == EVENT_CMDLINECHANGED
2263 || event == EVENT_CMDLINEENTER
2264 || event == EVENT_CMDLINELEAVE
Shougo Matsushitad0952142024-06-20 22:05:16 +02002265 || event == EVENT_CURSORMOVEDC
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002266 || event == EVENT_CMDWINENTER
2267 || event == EVENT_CMDWINLEAVE
2268 || event == EVENT_CMDUNDEFINED
2269 || event == EVENT_FUNCUNDEFINED
Shougo Matsushita83678842024-07-11 22:05:12 +02002270 || event == EVENT_KEYINPUTPRE
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002271 || event == EVENT_REMOTEREPLY
2272 || event == EVENT_SPELLFILEMISSING
2273 || event == EVENT_QUICKFIXCMDPRE
2274 || event == EVENT_COLORSCHEME
2275 || event == EVENT_COLORSCHEMEPRE
2276 || event == EVENT_OPTIONSET
2277 || event == EVENT_QUICKFIXCMDPOST
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002278 || event == EVENT_DIRCHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002279 || event == EVENT_DIRCHANGEDPRE
naohiro ono23beefe2021-11-13 12:38:49 +00002280 || event == EVENT_MODECHANGED
zeertzjqc601d982022-10-10 13:46:15 +01002281 || event == EVENT_MENUPOPUP
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002282 || event == EVENT_USER
LemonBoy09371822022-04-08 15:18:45 +01002283 || event == EVENT_WINCLOSED
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00002284 || event == EVENT_WINRESIZED
Danek Duvalld7d56032024-01-14 20:19:59 +01002285 || event == EVENT_WINSCROLLED
2286 || event == EVENT_TERMRESPONSEALL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002287 {
2288 fname = vim_strsave(fname);
2289 autocmd_fname_full = TRUE; // don't expand it later
2290 }
2291 else
2292 fname = FullName_save(fname, FALSE);
2293 }
2294 if (fname == NULL) // out of memory
2295 {
2296 vim_free(sfname);
2297 retval = FALSE;
2298 goto BYPASS_AU;
2299 }
2300
2301#ifdef BACKSLASH_IN_FILENAME
2302 /*
2303 * Replace all backslashes with forward slashes. This makes the
2304 * autocommand patterns portable between Unix and MS-DOS.
2305 */
2306 if (sfname != NULL)
2307 forward_slash(sfname);
2308 forward_slash(fname);
2309#endif
2310
2311#ifdef VMS
2312 // remove version for correct match
2313 if (sfname != NULL)
2314 vms_remove_version(sfname);
2315 vms_remove_version(fname);
2316#endif
2317
2318 /*
2319 * Set the name to be used for <amatch>.
2320 */
2321 autocmd_match = fname;
2322
2323
2324 // Don't redraw while doing autocommands.
2325 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002326
2327 // name and lnum are filled in later
2328 estack_push(ETYPE_AUCMD, NULL, 0);
ichizok7e5fe382023-04-15 13:17:50 +01002329 ESTACK_CHECK_SETUP;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002330
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002331 save_current_sctx = current_sctx;
2332
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002333#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002334# ifdef FEAT_PROFILE
2335 if (do_profiling == PROF_YES)
2336 prof_child_enter(&wait_time); // doesn't count for the caller itself
2337# endif
2338
2339 // Don't use local function variables, if called from a function.
2340 save_funccal(&funccal_entry);
2341#endif
2342
2343 /*
2344 * When starting to execute autocommands, save the search patterns.
2345 */
2346 if (!autocmd_busy)
2347 {
2348 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002349 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002350 {
2351 saveRedobuff(&save_redo);
2352 did_save_redobuff = TRUE;
2353 }
zeertzjq5bf6c212024-03-31 18:41:27 +02002354 curbuf->b_did_filetype = curbuf->b_keep_filetype;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002355 }
2356
2357 /*
2358 * Note that we are applying autocmds. Some commands need to know.
2359 */
2360 autocmd_busy = TRUE;
2361 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2362 ++nesting; // see matching decrement below
2363
2364 // Remember that FileType was triggered. Used for did_filetype().
2365 if (event == EVENT_FILETYPE)
zeertzjq5bf6c212024-03-31 18:41:27 +02002366 curbuf->b_did_filetype = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002367
2368 tail = gettail(fname);
2369
2370 // Find first autocommand that matches
LemonBoyeca7c602022-04-14 15:39:43 +01002371 CLEAR_FIELD(patcmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002372 patcmd.curpat = first_autopat[(int)event];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002373 patcmd.group = group;
2374 patcmd.fname = fname;
2375 patcmd.sfname = sfname;
2376 patcmd.tail = tail;
2377 patcmd.event = event;
2378 patcmd.arg_bufnr = autocmd_bufnr;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002379 auto_next_pat(&patcmd, FALSE);
2380
2381 // found one, start executing the autocommands
2382 if (patcmd.curpat != NULL)
2383 {
2384 // add to active_apc_list
2385 patcmd.next = active_apc_list;
2386 active_apc_list = &patcmd;
2387
2388#ifdef FEAT_EVAL
2389 // set v:cmdarg (only when there is a matching pattern)
2390 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2391 if (eap != NULL)
2392 {
2393 save_cmdarg = set_cmdarg(eap, NULL);
2394 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2395 }
2396 else
2397 save_cmdarg = NULL; // avoid gcc warning
2398#endif
2399 retval = TRUE;
2400 // mark the last pattern, to avoid an endless loop when more patterns
2401 // are added when executing autocommands
2402 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2403 ap->last = FALSE;
2404 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002405
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002406 // Make sure cursor and topline are valid. The first time the current
2407 // values are saved, restored by reset_lnums(). When nested only the
2408 // values are corrected when needed.
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002409 if (nesting == 1)
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002410 check_lnums(TRUE);
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002411 else
2412 check_lnums_nested(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002413
Christian Brabandt590aae32023-06-25 22:34:22 +01002414 int save_did_emsg = did_emsg;
2415 int save_ex_pressedreturn = get_pressedreturn();
ichizokc3f91c02021-12-17 09:44:33 +00002416
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002417 do_cmdline(NULL, getnextac, (void *)&patcmd,
2418 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002419
ichizokc3f91c02021-12-17 09:44:33 +00002420 did_emsg += save_did_emsg;
Christian Brabandt590aae32023-06-25 22:34:22 +01002421 set_pressedreturn(save_ex_pressedreturn);
ichizokc3f91c02021-12-17 09:44:33 +00002422
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002423 if (nesting == 1)
2424 // restore cursor and topline, unless they were changed
2425 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002426
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002427#ifdef FEAT_EVAL
2428 if (eap != NULL)
2429 {
2430 (void)set_cmdarg(NULL, save_cmdarg);
2431 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2432 }
2433#endif
2434 // delete from active_apc_list
2435 if (active_apc_list == &patcmd) // just in case
2436 active_apc_list = patcmd.next;
2437 }
2438
Bram Moolenaar79cdf022023-05-20 14:07:00 +01002439 if (RedrawingDisabled > 0)
2440 --RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002441 autocmd_busy = save_autocmd_busy;
2442 filechangeshell_busy = FALSE;
2443 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002444 vim_free(SOURCING_NAME);
ichizok7e5fe382023-04-15 13:17:50 +01002445 ESTACK_CHECK_NOW;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002446 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002447 vim_free(autocmd_fname);
2448 autocmd_fname = save_autocmd_fname;
2449 autocmd_fname_full = save_autocmd_fname_full;
2450 autocmd_bufnr = save_autocmd_bufnr;
2451 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002452 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002453#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002454 restore_funccal();
2455# ifdef FEAT_PROFILE
2456 if (do_profiling == PROF_YES)
2457 prof_child_exit(&wait_time);
2458# endif
2459#endif
2460 KeyTyped = save_KeyTyped;
2461 vim_free(fname);
2462 vim_free(sfname);
2463 --nesting; // see matching increment above
2464
2465 /*
2466 * When stopping to execute autocommands, restore the search patterns and
2467 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2468 * free any windows in the au_pending_free_win list.
2469 */
2470 if (!autocmd_busy)
2471 {
2472 restore_search_patterns();
2473 if (did_save_redobuff)
2474 restoreRedobuff(&save_redo);
zeertzjq5bf6c212024-03-31 18:41:27 +02002475 curbuf->b_did_filetype = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002476 while (au_pending_free_buf != NULL)
2477 {
2478 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002479
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002480 vim_free(au_pending_free_buf);
2481 au_pending_free_buf = b;
2482 }
2483 while (au_pending_free_win != NULL)
2484 {
2485 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002486
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002487 vim_free(au_pending_free_win);
2488 au_pending_free_win = w;
2489 }
2490 }
2491
2492 /*
2493 * Some events don't set or reset the Changed flag.
2494 * Check if still in the same buffer!
2495 */
2496 if (curbuf == old_curbuf
2497 && (event == EVENT_BUFREADPOST
2498 || event == EVENT_BUFWRITEPOST
2499 || event == EVENT_FILEAPPENDPOST
2500 || event == EVENT_VIMLEAVE
2501 || event == EVENT_VIMLEAVEPRE))
2502 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002503 if (curbuf->b_changed != save_changed)
2504 need_maketitle = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002505 curbuf->b_changed = save_changed;
2506 }
2507
2508 au_cleanup(); // may really delete removed patterns/commands now
2509
2510BYPASS_AU:
2511 // When wiping out a buffer make sure all its buffer-local autocommands
2512 // are deleted.
2513 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2514 aubuflocal_remove(buf);
2515
2516 if (retval == OK && event == EVENT_FILETYPE)
zeertzjq5bf6c212024-03-31 18:41:27 +02002517 curbuf->b_au_did_filetype = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002518
2519 return retval;
2520}
2521
2522# ifdef FEAT_EVAL
2523static char_u *old_termresponse = NULL;
Danek Duvalld7d56032024-01-14 20:19:59 +01002524static char_u *old_termu7resp = NULL;
2525static char_u *old_termblinkresp = NULL;
2526static char_u *old_termrbgresp = NULL;
2527static char_u *old_termrfgresp = NULL;
2528static char_u *old_termstyleresp = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002529# endif
2530
2531/*
2532 * Block triggering autocommands until unblock_autocmd() is called.
2533 * Can be used recursively, so long as it's symmetric.
2534 */
2535 void
2536block_autocmds(void)
2537{
2538# ifdef FEAT_EVAL
2539 // Remember the value of v:termresponse.
2540 if (autocmd_blocked == 0)
Danek Duvalld7d56032024-01-14 20:19:59 +01002541 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002542 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
Danek Duvalld7d56032024-01-14 20:19:59 +01002543 old_termu7resp = get_vim_var_str(VV_TERMU7RESP);
2544 old_termblinkresp = get_vim_var_str(VV_TERMBLINKRESP);
2545 old_termrbgresp = get_vim_var_str(VV_TERMRBGRESP);
2546 old_termrfgresp = get_vim_var_str(VV_TERMRFGRESP);
2547 old_termstyleresp = get_vim_var_str(VV_TERMSTYLERESP);
2548 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002549# endif
2550 ++autocmd_blocked;
2551}
2552
2553 void
2554unblock_autocmds(void)
2555{
2556 --autocmd_blocked;
2557
2558# ifdef FEAT_EVAL
Danek Duvalld7d56032024-01-14 20:19:59 +01002559 // When v:termresponse, etc, were set while autocommands were blocked,
2560 // trigger the autocommands now. Esp. useful when executing a shell
2561 // command during startup (vimdiff).
2562 if (autocmd_blocked == 0)
2563 {
2564 if (get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2565 {
2566 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2567 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"version", NULL, FALSE, curbuf);
2568 }
2569 if (get_vim_var_str(VV_TERMU7RESP) != old_termu7resp)
2570 {
2571 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"ambiguouswidth", NULL, FALSE, curbuf);
2572 }
2573 if (get_vim_var_str(VV_TERMBLINKRESP) != old_termblinkresp)
2574 {
2575 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"cursorblink", NULL, FALSE, curbuf);
2576 }
2577 if (get_vim_var_str(VV_TERMRBGRESP) != old_termrbgresp)
2578 {
2579 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"background", NULL, FALSE, curbuf);
2580 }
2581 if (get_vim_var_str(VV_TERMRFGRESP) != old_termrfgresp)
2582 {
2583 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"foreground", NULL, FALSE, curbuf);
2584 }
2585 if (get_vim_var_str(VV_TERMSTYLERESP) != old_termstyleresp)
2586 {
2587 apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"cursorshape", NULL, FALSE, curbuf);
2588 }
2589 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002590# endif
2591}
2592
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002593 int
2594is_autocmd_blocked(void)
2595{
2596 return autocmd_blocked != 0;
2597}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002598
2599/*
2600 * Find next autocommand pattern that matches.
2601 */
2602 static void
2603auto_next_pat(
LemonBoyeca7c602022-04-14 15:39:43 +01002604 AutoPatCmd_T *apc,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002605 int stop_at_last) // stop when 'last' flag is set
2606{
2607 AutoPat *ap;
2608 AutoCmd *cp;
2609 char_u *name;
2610 char *s;
LemonBoyeca7c602022-04-14 15:39:43 +01002611 estack_T *entry;
2612 char_u *namep;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002613
LemonBoyeca7c602022-04-14 15:39:43 +01002614 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
2615
2616 // Clear the exestack entry for this ETYPE_AUCMD entry.
2617 VIM_CLEAR(entry->es_name);
2618 entry->es_info.aucmd = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002619
2620 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2621 {
2622 apc->curpat = NULL;
2623
2624 // Only use a pattern when it has not been removed, has commands and
2625 // the group matches. For buffer-local autocommands only check the
2626 // buffer number.
2627 if (ap->pat != NULL && ap->cmds != NULL
2628 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2629 {
2630 // execution-condition
2631 if (ap->buflocal_nr == 0
2632 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2633 apc->sfname, apc->tail, ap->allow_dirs))
2634 : ap->buflocal_nr == apc->arg_bufnr)
2635 {
2636 name = event_nr2name(apc->event);
2637 s = _("%s Autocommands for \"%s\"");
LemonBoyeca7c602022-04-14 15:39:43 +01002638 namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1);
2639 if (namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002640 {
LemonBoyeca7c602022-04-14 15:39:43 +01002641 sprintf((char *)namep, s, (char *)name, (char *)ap->pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002642 if (p_verbose >= 8)
2643 {
2644 verbose_enter();
LemonBoyeca7c602022-04-14 15:39:43 +01002645 smsg(_("Executing %s"), namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002646 verbose_leave();
2647 }
2648 }
2649
LemonBoyeca7c602022-04-14 15:39:43 +01002650 // Update the exestack entry for this autocmd.
2651 entry->es_name = namep;
2652 entry->es_info.aucmd = apc;
2653
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002654 apc->curpat = ap;
2655 apc->nextcmd = ap->cmds;
2656 // mark last command
2657 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2658 cp->last = FALSE;
2659 cp->last = TRUE;
2660 }
2661 line_breakcheck();
2662 if (apc->curpat != NULL) // found a match
2663 break;
2664 }
2665 if (stop_at_last && ap->last)
2666 break;
2667 }
2668}
2669
Dominique Pellee764d1b2023-03-12 21:20:59 +00002670#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002671/*
LemonBoyeca7c602022-04-14 15:39:43 +01002672 * Get the script context where autocommand "acp" is defined.
2673 */
2674 sctx_T *
2675acp_script_ctx(AutoPatCmd_T *acp)
2676{
2677 return &acp->script_ctx;
2678}
Dominique Pellee764d1b2023-03-12 21:20:59 +00002679#endif
LemonBoyeca7c602022-04-14 15:39:43 +01002680
2681/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002682 * Get next autocommand command.
2683 * Called by do_cmdline() to get the next line for ":if".
2684 * Returns allocated string, or NULL for end of autocommands.
2685 */
2686 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002687getnextac(
2688 int c UNUSED,
2689 void *cookie,
2690 int indent UNUSED,
2691 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002692{
LemonBoyeca7c602022-04-14 15:39:43 +01002693 AutoPatCmd_T *acp = (AutoPatCmd_T *)cookie;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002694 char_u *retval;
2695 AutoCmd *ac;
2696
2697 // Can be called again after returning the last line.
2698 if (acp->curpat == NULL)
2699 return NULL;
2700
2701 // repeat until we find an autocommand to execute
2702 for (;;)
2703 {
2704 // skip removed commands
2705 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2706 if (acp->nextcmd->last)
2707 acp->nextcmd = NULL;
2708 else
2709 acp->nextcmd = acp->nextcmd->next;
2710
2711 if (acp->nextcmd != NULL)
2712 break;
2713
2714 // at end of commands, find next pattern that matches
2715 if (acp->curpat->last)
2716 acp->curpat = NULL;
2717 else
2718 acp->curpat = acp->curpat->next;
2719 if (acp->curpat != NULL)
2720 auto_next_pat(acp, TRUE);
2721 if (acp->curpat == NULL)
2722 return NULL;
2723 }
2724
2725 ac = acp->nextcmd;
2726
2727 if (p_verbose >= 9)
2728 {
2729 verbose_enter_scroll();
2730 smsg(_("autocommand %s"), ac->cmd);
2731 msg_puts("\n"); // don't overwrite this either
2732 verbose_leave_scroll();
2733 }
2734 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002735 // Remove one-shot ("once") autocmd in anticipation of its execution.
2736 if (ac->once)
2737 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002738 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002739 current_sctx = ac->script_ctx;
LemonBoyeca7c602022-04-14 15:39:43 +01002740 acp->script_ctx = current_sctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002741 if (ac->last)
2742 acp->nextcmd = NULL;
2743 else
2744 acp->nextcmd = ac->next;
2745 return retval;
2746}
2747
2748/*
2749 * Return TRUE if there is a matching autocommand for "fname".
2750 * To account for buffer-local autocommands, function needs to know
2751 * in which buffer the file will be opened.
2752 */
2753 int
2754has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2755{
2756 AutoPat *ap;
2757 char_u *fname;
2758 char_u *tail = gettail(sfname);
2759 int retval = FALSE;
2760
2761 fname = FullName_save(sfname, FALSE);
2762 if (fname == NULL)
2763 return FALSE;
2764
2765#ifdef BACKSLASH_IN_FILENAME
2766 /*
2767 * Replace all backslashes with forward slashes. This makes the
2768 * autocommand patterns portable between Unix and MS-DOS.
2769 */
2770 sfname = vim_strsave(sfname);
2771 if (sfname != NULL)
2772 forward_slash(sfname);
2773 forward_slash(fname);
2774#endif
2775
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002776 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002777 if (ap->pat != NULL && ap->cmds != NULL
2778 && (ap->buflocal_nr == 0
2779 ? match_file_pat(NULL, &ap->reg_prog,
2780 fname, sfname, tail, ap->allow_dirs)
2781 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2782 ))
2783 {
2784 retval = TRUE;
2785 break;
2786 }
2787
2788 vim_free(fname);
2789#ifdef BACKSLASH_IN_FILENAME
2790 vim_free(sfname);
2791#endif
2792
2793 return retval;
2794}
2795
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002796/*
2797 * Function given to ExpandGeneric() to obtain the list of autocommand group
2798 * names.
2799 */
2800 char_u *
2801get_augroup_name(expand_T *xp UNUSED, int idx)
2802{
2803 if (idx == augroups.ga_len) // add "END" add the end
2804 return (char_u *)"END";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002805 if (idx < 0 || idx >= augroups.ga_len) // end of list
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002806 return NULL;
2807 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2808 // skip deleted entries
2809 return (char_u *)"";
2810 return AUGROUP_NAME(idx); // return a name
2811}
2812
2813static int include_groups = FALSE;
2814
2815 char_u *
2816set_context_in_autocmd(
2817 expand_T *xp,
2818 char_u *arg,
2819 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2820{
2821 char_u *p;
2822 int group;
2823
2824 // check for a group name, skip it if present
2825 include_groups = FALSE;
2826 p = arg;
2827 group = au_get_grouparg(&arg);
2828 if (group == AUGROUP_ERROR)
2829 return NULL;
2830 // If there only is a group name that's what we expand.
2831 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2832 {
2833 arg = p;
2834 group = AUGROUP_ALL;
2835 }
2836
2837 // skip over event name
2838 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2839 if (*p == ',')
2840 arg = p + 1;
2841 if (*p == NUL)
2842 {
2843 if (group == AUGROUP_ALL)
2844 include_groups = TRUE;
2845 xp->xp_context = EXPAND_EVENTS; // expand event name
2846 xp->xp_pattern = arg;
2847 return NULL;
2848 }
2849
2850 // skip over pattern
2851 arg = skipwhite(p);
2852 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2853 arg++;
2854 if (*arg)
2855 return arg; // expand (next) command
2856
2857 if (doautocmd)
2858 xp->xp_context = EXPAND_FILES; // expand file names
2859 else
2860 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2861 return NULL;
2862}
2863
2864/*
2865 * Function given to ExpandGeneric() to obtain the list of event names.
2866 */
2867 char_u *
2868get_event_name(expand_T *xp UNUSED, int idx)
2869{
John Marriott78d742a2024-04-02 20:26:01 +02002870 int i;
2871
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002872 if (idx < augroups.ga_len) // First list group names, if wanted
2873 {
2874 if (!include_groups || AUGROUP_NAME(idx) == NULL
2875 || AUGROUP_NAME(idx) == get_deleted_augroup())
2876 return (char_u *)""; // skip deleted entries
2877 return AUGROUP_NAME(idx); // return a name
2878 }
John Marriott78d742a2024-04-02 20:26:01 +02002879
2880 i = idx - augroups.ga_len;
2881 if (i < 0 || i >= (int)ARRAY_LENGTH(event_tab))
2882 return NULL;
2883
John Marriott8d4477e2024-11-02 15:59:01 +01002884 return event_tab[i].value.string;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002885}
2886
Yee Cheng Chin900894b2023-09-29 20:42:32 +02002887/*
2888 * Function given to ExpandGeneric() to obtain the list of event names. Don't
2889 * include groups.
2890 */
2891 char_u *
2892get_event_name_no_group(expand_T *xp UNUSED, int idx)
2893{
John Marriott78d742a2024-04-02 20:26:01 +02002894 if (idx < 0 || idx >= (int)ARRAY_LENGTH(event_tab))
2895 return NULL;
2896
John Marriott8d4477e2024-11-02 15:59:01 +01002897 return event_tab[idx].value.string;
Yee Cheng Chin900894b2023-09-29 20:42:32 +02002898}
2899
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002900
2901#if defined(FEAT_EVAL) || defined(PROTO)
2902/*
2903 * Return TRUE if autocmd is supported.
2904 */
2905 int
2906autocmd_supported(char_u *name)
2907{
2908 char_u *p;
2909
2910 return (event_name2nr(name, &p) != NUM_EVENTS);
2911}
2912
2913/*
2914 * Return TRUE if an autocommand is defined for a group, event and
2915 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2916 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2917 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2918 * Used for:
2919 * exists("#Group") or
2920 * exists("#Group#Event") or
2921 * exists("#Group#Event#pat") or
2922 * exists("#Event") or
2923 * exists("#Event#pat")
2924 */
2925 int
2926au_exists(char_u *arg)
2927{
2928 char_u *arg_save;
2929 char_u *pattern = NULL;
2930 char_u *event_name;
2931 char_u *p;
2932 event_T event;
2933 AutoPat *ap;
2934 buf_T *buflocal_buf = NULL;
2935 int group;
2936 int retval = FALSE;
2937
2938 // Make a copy so that we can change the '#' chars to a NUL.
2939 arg_save = vim_strsave(arg);
2940 if (arg_save == NULL)
2941 return FALSE;
2942 p = vim_strchr(arg_save, '#');
2943 if (p != NULL)
2944 *p++ = NUL;
2945
2946 // First, look for an autocmd group name
2947 group = au_find_group(arg_save);
2948 if (group == AUGROUP_ERROR)
2949 {
2950 // Didn't match a group name, assume the first argument is an event.
2951 group = AUGROUP_ALL;
2952 event_name = arg_save;
2953 }
2954 else
2955 {
2956 if (p == NULL)
2957 {
2958 // "Group": group name is present and it's recognized
2959 retval = TRUE;
2960 goto theend;
2961 }
2962
2963 // Must be "Group#Event" or "Group#Event#pat".
2964 event_name = p;
2965 p = vim_strchr(event_name, '#');
2966 if (p != NULL)
2967 *p++ = NUL; // "Group#Event#pat"
2968 }
2969
2970 pattern = p; // "pattern" is NULL when there is no pattern
2971
2972 // find the index (enum) for the event name
2973 event = event_name2nr(event_name, &p);
2974
2975 // return FALSE if the event name is not recognized
2976 if (event == NUM_EVENTS)
2977 goto theend;
2978
2979 // Find the first autocommand for this event.
2980 // If there isn't any, return FALSE;
2981 // If there is one and no pattern given, return TRUE;
2982 ap = first_autopat[(int)event];
2983 if (ap == NULL)
2984 goto theend;
2985
2986 // if pattern is "<buffer>", special handling is needed which uses curbuf
2987 // for pattern "<buffer=N>, fnamecmp() will work fine
2988 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2989 buflocal_buf = curbuf;
2990
2991 // Check if there is an autocommand with the given pattern.
2992 for ( ; ap != NULL; ap = ap->next)
2993 // only use a pattern when it has not been removed and has commands.
2994 // For buffer-local autocommands, fnamecmp() works fine.
2995 if (ap->pat != NULL && ap->cmds != NULL
2996 && (group == AUGROUP_ALL || ap->group == group)
2997 && (pattern == NULL
2998 || (buflocal_buf == NULL
2999 ? fnamecmp(ap->pat, pattern) == 0
3000 : ap->buflocal_nr == buflocal_buf->b_fnum)))
3001 {
3002 retval = TRUE;
3003 break;
3004 }
3005
3006theend:
3007 vim_free(arg_save);
3008 return retval;
3009}
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003010
3011/*
3012 * autocmd_add() and autocmd_delete() functions
3013 */
3014 static void
3015autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
3016{
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003017 list_T *aucmd_list;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003018 listitem_T *li;
3019 dict_T *event_dict;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003020 dictitem_T *di;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003021 char_u *event_name = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003022 list_T *event_list;
3023 listitem_T *eli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003024 event_T event;
3025 char_u *group_name = NULL;
3026 int group;
3027 char_u *pat = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003028 list_T *pat_list;
3029 listitem_T *pli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003030 char_u *cmd = NULL;
3031 char_u *end;
3032 int once;
3033 int nested;
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01003034 int replace; // replace the cmd for a group/event
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003035 int retval = VVAL_TRUE;
3036 int save_augroup = current_augroup;
3037
3038 rettv->v_type = VAR_BOOL;
3039 rettv->vval.v_number = VVAL_FALSE;
3040
3041 if (check_for_list_arg(argvars, 0) == FAIL)
3042 return;
3043
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003044 aucmd_list = argvars[0].vval.v_list;
3045 if (aucmd_list == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003046 return;
3047
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003048 FOR_ALL_LIST_ITEMS(aucmd_list, li)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003049 {
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003050 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003051 VIM_CLEAR(cmd);
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003052 event_name = NULL;
3053 event_list = NULL;
3054 pat = NULL;
3055 pat_list = NULL;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003056
3057 if (li->li_tv.v_type != VAR_DICT)
3058 continue;
3059
3060 event_dict = li->li_tv.vval.v_dict;
3061 if (event_dict == NULL)
3062 continue;
3063
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003064 di = dict_find(event_dict, (char_u *)"event", -1);
3065 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003066 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003067 if (di->di_tv.v_type == VAR_STRING)
3068 {
3069 event_name = di->di_tv.vval.v_string;
3070 if (event_name == NULL)
3071 {
3072 emsg(_(e_string_required));
3073 continue;
3074 }
3075 }
3076 else if (di->di_tv.v_type == VAR_LIST)
3077 {
3078 event_list = di->di_tv.vval.v_list;
3079 if (event_list == NULL)
3080 {
3081 emsg(_(e_list_required));
3082 continue;
3083 }
3084 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003085 else
3086 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003087 emsg(_(e_string_or_list_expected));
3088 continue;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003089 }
3090 }
3091
Bram Moolenaard61efa52022-07-23 09:52:04 +01003092 group_name = dict_get_string(event_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003093 if (group_name == NULL || *group_name == NUL)
3094 // if the autocmd group name is not specified, then use the current
3095 // autocmd group
3096 group = current_augroup;
3097 else
3098 {
3099 group = au_find_group(group_name);
3100 if (group == AUGROUP_ERROR)
3101 {
3102 if (delete)
3103 {
3104 semsg(_(e_no_such_group_str), group_name);
3105 retval = VVAL_FALSE;
3106 break;
3107 }
3108 // group is not found, create it now
3109 group = au_new_group(group_name);
3110 if (group == AUGROUP_ERROR)
3111 {
3112 semsg(_(e_no_such_group_str), group_name);
3113 retval = VVAL_FALSE;
3114 break;
3115 }
3116
3117 current_augroup = group;
3118 }
3119 }
3120
3121 // if a buffer number is specified, then generate a pattern of the form
3122 // "<buffer=n>. Otherwise, use the pattern supplied by the user.
3123 if (dict_has_key(event_dict, "bufnr"))
3124 {
3125 varnumber_T bnum;
3126
Bram Moolenaard61efa52022-07-23 09:52:04 +01003127 bnum = dict_get_number_def(event_dict, "bufnr", -1);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003128 if (bnum == -1)
3129 continue;
3130
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003131 vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
3132 pat = IObuff;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003133 }
3134 else
3135 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003136 di = dict_find(event_dict, (char_u *)"pattern", -1);
3137 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003138 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003139 if (di->di_tv.v_type == VAR_STRING)
3140 {
3141 pat = di->di_tv.vval.v_string;
3142 if (pat == NULL)
3143 {
3144 emsg(_(e_string_required));
3145 continue;
3146 }
3147 }
3148 else if (di->di_tv.v_type == VAR_LIST)
3149 {
3150 pat_list = di->di_tv.vval.v_list;
3151 if (pat_list == NULL)
3152 {
3153 emsg(_(e_list_required));
3154 continue;
3155 }
3156 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003157 else
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003158 {
3159 emsg(_(e_string_or_list_expected));
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003160 continue;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003161 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003162 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003163 else if (delete)
3164 pat = (char_u *)"";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003165 }
3166
Bram Moolenaard61efa52022-07-23 09:52:04 +01003167 once = dict_get_bool(event_dict, "once", FALSE);
3168 nested = dict_get_bool(event_dict, "nested", FALSE);
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01003169 // if 'replace' is true, then remove all the commands associated with
3170 // this autocmd event/group and add the new command.
Bram Moolenaard61efa52022-07-23 09:52:04 +01003171 replace = dict_get_bool(event_dict, "replace", FALSE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003172
Bram Moolenaard61efa52022-07-23 09:52:04 +01003173 cmd = dict_get_string(event_dict, "cmd", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003174 if (cmd == NULL)
3175 {
3176 if (delete)
3177 cmd = vim_strsave((char_u *)"");
3178 else
3179 continue;
3180 }
3181
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003182 if (delete && (event_name == NULL
3183 || (event_name[0] == '*' && event_name[1] == NUL)))
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003184 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003185 // if the event name is not specified or '*', delete all the events
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003186 for (event = (event_T)0; (int)event < NUM_EVENTS;
3187 event = (event_T)((int)event + 1))
3188 {
3189 if (do_autocmd_event(event, pat, once, nested, cmd, delete,
3190 group, 0) == FAIL)
3191 {
3192 retval = VVAL_FALSE;
3193 break;
3194 }
3195 }
3196 }
3197 else
3198 {
Bram Moolenaar968443e2022-05-27 21:16:34 +01003199 char_u *p = NULL;
3200
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003201 eli = NULL;
3202 end = NULL;
3203 while (TRUE)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003204 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003205 if (event_list != NULL)
3206 {
3207 if (eli == NULL)
3208 eli = event_list->lv_first;
3209 else
3210 eli = eli->li_next;
3211 if (eli == NULL)
3212 break;
3213 if (eli->li_tv.v_type != VAR_STRING
Bram Moolenaar882476a2022-06-01 16:02:38 +01003214 || (p = eli->li_tv.vval.v_string) == NULL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003215 {
3216 emsg(_(e_string_required));
Bram Moolenaar882476a2022-06-01 16:02:38 +01003217 break;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003218 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003219 }
3220 else
3221 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003222 if (p == NULL)
3223 p = event_name;
3224 if (p == NULL || *p == NUL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003225 break;
3226 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003227
3228 event = event_name2nr(p, &end);
3229 if (event == NUM_EVENTS || *end != NUL)
3230 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01003231 // this also catches something following a valid event name
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01003232 semsg(_(e_no_such_event_str), p);
3233 retval = VVAL_FALSE;
3234 break;
3235 }
3236 if (pat != NULL)
3237 {
3238 if (do_autocmd_event(event, pat, once, nested, cmd,
3239 delete | replace, group, 0) == FAIL)
3240 {
3241 retval = VVAL_FALSE;
3242 break;
3243 }
3244 }
3245 else if (pat_list != NULL)
3246 {
3247 FOR_ALL_LIST_ITEMS(pat_list, pli)
3248 {
3249 if (pli->li_tv.v_type != VAR_STRING
3250 || pli->li_tv.vval.v_string == NULL)
3251 {
3252 emsg(_(e_string_required));
3253 continue;
3254 }
3255 if (do_autocmd_event(event,
3256 pli->li_tv.vval.v_string, once, nested,
3257 cmd, delete | replace, group, 0) ==
3258 FAIL)
3259 {
3260 retval = VVAL_FALSE;
3261 break;
3262 }
3263 }
3264 if (retval == VVAL_FALSE)
3265 break;
3266 }
3267 if (event_name != NULL)
3268 p = end;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003269 }
3270 }
3271
3272 // if only the autocmd group name is specified for delete and the
3273 // autocmd event, pattern and cmd are not specified, then delete the
3274 // autocmd group.
3275 if (delete && group_name != NULL &&
3276 (event_name == NULL || event_name[0] == NUL)
3277 && (pat == NULL || pat[0] == NUL)
3278 && (cmd == NULL || cmd[0] == NUL))
3279 au_del_group(group_name);
3280 }
3281
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003282 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003283 VIM_CLEAR(cmd);
3284
3285 current_augroup = save_augroup;
3286 rettv->vval.v_number = retval;
3287}
3288
3289/*
3290 * autocmd_add() function
3291 */
3292 void
3293f_autocmd_add(typval_T *argvars, typval_T *rettv)
3294{
3295 autocmd_add_or_delete(argvars, rettv, FALSE);
3296}
3297
3298/*
3299 * autocmd_delete() function
3300 */
3301 void
3302f_autocmd_delete(typval_T *argvars, typval_T *rettv)
3303{
3304 autocmd_add_or_delete(argvars, rettv, TRUE);
3305}
3306
3307/*
3308 * autocmd_get() function
3309 * Returns a List of autocmds.
3310 */
3311 void
3312f_autocmd_get(typval_T *argvars, typval_T *rettv)
3313{
3314 event_T event_arg = NUM_EVENTS;
3315 event_T event;
3316 AutoPat *ap;
3317 AutoCmd *ac;
3318 list_T *event_list;
3319 dict_T *event_dict;
3320 char_u *event_name = NULL;
3321 char_u *pat = NULL;
3322 char_u *name = NULL;
3323 int group = AUGROUP_ALL;
3324
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01003325 if (rettv_list_alloc(rettv) == FAIL)
3326 return;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003327 if (check_for_opt_dict_arg(argvars, 0) == FAIL)
3328 return;
3329
3330 if (argvars[0].v_type == VAR_DICT)
3331 {
3332 // return only the autocmds in the specified group
3333 if (dict_has_key(argvars[0].vval.v_dict, "group"))
3334 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003335 name = dict_get_string(argvars[0].vval.v_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003336 if (name == NULL)
3337 return;
3338
3339 if (*name == NUL)
3340 group = AUGROUP_DEFAULT;
3341 else
3342 {
3343 group = au_find_group(name);
3344 if (group == AUGROUP_ERROR)
3345 {
3346 semsg(_(e_no_such_group_str), name);
3347 vim_free(name);
3348 return;
3349 }
3350 }
3351 vim_free(name);
3352 }
3353
3354 // return only the autocmds for the specified event
3355 if (dict_has_key(argvars[0].vval.v_dict, "event"))
3356 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003357 name = dict_get_string(argvars[0].vval.v_dict, "event", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003358 if (name == NULL)
3359 return;
3360
3361 if (name[0] == '*' && name[1] == NUL)
3362 event_arg = NUM_EVENTS;
3363 else
3364 {
John Marriott78d742a2024-04-02 20:26:01 +02003365 keyvalue_T target;
3366 keyvalue_T *entry;
3367
3368 target.key = 0;
John Marriott8d4477e2024-11-02 15:59:01 +01003369 target.value.string = name;
3370 target.value.length = STRLEN(target.value.string);
3371 entry = (keyvalue_T *)bsearch(&target, &event_tab,
3372 ARRAY_LENGTH(event_tab), sizeof(event_tab[0]),
3373 cmp_keyvalue_value_ni);
John Marriott78d742a2024-04-02 20:26:01 +02003374 if (entry == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003375 {
3376 semsg(_(e_no_such_event_str), name);
3377 vim_free(name);
3378 return;
3379 }
John Marriott78d742a2024-04-02 20:26:01 +02003380 event_arg = (event_T)entry->key;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003381 }
3382 vim_free(name);
3383 }
3384
3385 // return only the autocmds for the specified pattern
3386 if (dict_has_key(argvars[0].vval.v_dict, "pattern"))
3387 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003388 pat = dict_get_string(argvars[0].vval.v_dict, "pattern", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003389 if (pat == NULL)
3390 return;
3391 }
3392 }
3393
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003394 event_list = rettv->vval.v_list;
3395
3396 // iterate through all the autocmd events
3397 for (event = (event_T)0; (int)event < NUM_EVENTS;
3398 event = (event_T)((int)event + 1))
3399 {
3400 if (event_arg != NUM_EVENTS && event != event_arg)
3401 continue;
3402
3403 event_name = event_nr2name(event);
3404
3405 // iterate through all the patterns for this autocmd event
3406 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
3407 {
3408 char_u *group_name;
3409
zeertzjq2d1d5c62024-06-09 16:44:33 +02003410 if (ap->pat == NULL) // pattern has been removed
3411 continue;
3412
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003413 if (group != AUGROUP_ALL && group != ap->group)
3414 continue;
3415
3416 if (pat != NULL && STRCMP(pat, ap->pat) != 0)
3417 continue;
3418
3419 group_name = get_augroup_name(NULL, ap->group);
3420
3421 // iterate through all the commands for this pattern and add one
3422 // item for each cmd.
3423 for (ac = ap->cmds; ac != NULL; ac = ac->next)
3424 {
3425 event_dict = dict_alloc();
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003426 if (event_dict == NULL
3427 || list_append_dict(event_list, event_dict) == FAIL)
Christian Brabandt29269a72024-04-16 22:44:31 +02003428 {
3429 vim_free(pat);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003430 return;
Christian Brabandt29269a72024-04-16 22:44:31 +02003431 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003432
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003433 if (dict_add_string(event_dict, "event", event_name) == FAIL
3434 || dict_add_string(event_dict, "group",
3435 group_name == NULL ? (char_u *)""
3436 : group_name) == FAIL
3437 || (ap->buflocal_nr != 0
3438 && (dict_add_number(event_dict, "bufnr",
3439 ap->buflocal_nr) == FAIL))
3440 || dict_add_string(event_dict, "pattern",
3441 ap->pat) == FAIL
3442 || dict_add_string(event_dict, "cmd", ac->cmd) == FAIL
3443 || dict_add_bool(event_dict, "once", ac->once) == FAIL
3444 || dict_add_bool(event_dict, "nested",
3445 ac->nested) == FAIL)
Christian Brabandt29269a72024-04-16 22:44:31 +02003446 {
3447 vim_free(pat);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003448 return;
Christian Brabandt29269a72024-04-16 22:44:31 +02003449 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003450 }
3451 }
3452 }
3453
3454 vim_free(pat);
3455}
3456
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01003457#endif