blob: 93da0bbbb199aa97a765495f0dc53a5eab963aa9 [file] [log] [blame]
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * autocmd.c: Autocommand related functions
12 */
13
14#include "vim.h"
15
16/*
17 * The autocommands are stored in a list for each event.
18 * Autocommands for the same pattern, that are consecutive, are joined
19 * together, to avoid having to match the pattern too often.
20 * The result is an array of Autopat lists, which point to AutoCmd lists:
21 *
22 * last_autopat[0] -----------------------------+
23 * V
24 * first_autopat[0] --> Autopat.next --> Autopat.next --> NULL
25 * Autopat.cmds Autopat.cmds
26 * | |
27 * V V
28 * AutoCmd.next AutoCmd.next
29 * | |
30 * V V
31 * AutoCmd.next NULL
32 * |
33 * V
34 * NULL
35 *
36 * last_autopat[1] --------+
37 * V
38 * first_autopat[1] --> Autopat.next --> NULL
39 * Autopat.cmds
40 * |
41 * V
42 * AutoCmd.next
43 * |
44 * V
45 * NULL
46 * etc.
47 *
48 * The order of AutoCmds is important, this is the order in which they were
49 * defined and will have to be executed.
50 */
51typedef struct AutoCmd
52{
53 char_u *cmd; // The command to be executed (NULL
54 // when command has been removed).
Bram Moolenaareb93f3f2019-04-04 15:04:56 +020055 char once; // "One shot": removed after execution
Bram Moolenaar3e460fd2019-01-26 16:21:07 +010056 char nested; // If autocommands nest here.
57 char last; // last command in list
LemonBoyeca7c602022-04-14 15:39:43 +010058 sctx_T script_ctx; // script context where it is defined
Bram Moolenaar3e460fd2019-01-26 16:21:07 +010059 struct AutoCmd *next; // next AutoCmd in list
60} AutoCmd;
61
62typedef struct AutoPat
63{
64 struct AutoPat *next; // Next AutoPat in AutoPat list; MUST
65 // be the first entry.
66 char_u *pat; // pattern as typed (NULL when pattern
67 // has been removed)
68 regprog_T *reg_prog; // compiled regprog for pattern
69 AutoCmd *cmds; // list of commands to do
70 int group; // group ID
71 int patlen; // strlen() of pat
72 int buflocal_nr; // !=0 for buffer-local AutoPat
73 char allow_dirs; // Pattern may match whole path
74 char last; // last pattern for apply_autocmds()
75} AutoPat;
76
77static struct event_name
78{
79 char *name; // event name
80 event_T event; // event number
81} event_names[] =
82{
83 {"BufAdd", EVENT_BUFADD},
84 {"BufCreate", EVENT_BUFADD},
85 {"BufDelete", EVENT_BUFDELETE},
86 {"BufEnter", EVENT_BUFENTER},
87 {"BufFilePost", EVENT_BUFFILEPOST},
88 {"BufFilePre", EVENT_BUFFILEPRE},
89 {"BufHidden", EVENT_BUFHIDDEN},
90 {"BufLeave", EVENT_BUFLEAVE},
91 {"BufNew", EVENT_BUFNEW},
92 {"BufNewFile", EVENT_BUFNEWFILE},
93 {"BufRead", EVENT_BUFREADPOST},
94 {"BufReadCmd", EVENT_BUFREADCMD},
95 {"BufReadPost", EVENT_BUFREADPOST},
96 {"BufReadPre", EVENT_BUFREADPRE},
97 {"BufUnload", EVENT_BUFUNLOAD},
98 {"BufWinEnter", EVENT_BUFWINENTER},
99 {"BufWinLeave", EVENT_BUFWINLEAVE},
100 {"BufWipeout", EVENT_BUFWIPEOUT},
101 {"BufWrite", EVENT_BUFWRITEPRE},
102 {"BufWritePost", EVENT_BUFWRITEPOST},
103 {"BufWritePre", EVENT_BUFWRITEPRE},
104 {"BufWriteCmd", EVENT_BUFWRITECMD},
105 {"CmdlineChanged", EVENT_CMDLINECHANGED},
106 {"CmdlineEnter", EVENT_CMDLINEENTER},
107 {"CmdlineLeave", EVENT_CMDLINELEAVE},
108 {"CmdwinEnter", EVENT_CMDWINENTER},
109 {"CmdwinLeave", EVENT_CMDWINLEAVE},
110 {"CmdUndefined", EVENT_CMDUNDEFINED},
111 {"ColorScheme", EVENT_COLORSCHEME},
112 {"ColorSchemePre", EVENT_COLORSCHEMEPRE},
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200113 {"CompleteChanged", EVENT_COMPLETECHANGED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100114 {"CompleteDone", EVENT_COMPLETEDONE},
Bram Moolenaar3f169ce2020-01-26 22:43:31 +0100115 {"CompleteDonePre", EVENT_COMPLETEDONEPRE},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100116 {"CursorHold", EVENT_CURSORHOLD},
117 {"CursorHoldI", EVENT_CURSORHOLDI},
118 {"CursorMoved", EVENT_CURSORMOVED},
119 {"CursorMovedI", EVENT_CURSORMOVEDI},
120 {"DiffUpdated", EVENT_DIFFUPDATED},
121 {"DirChanged", EVENT_DIRCHANGED},
Bram Moolenaar28e8f732022-02-09 12:58:20 +0000122 {"DirChangedPre", EVENT_DIRCHANGEDPRE},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100123 {"EncodingChanged", EVENT_ENCODINGCHANGED},
124 {"ExitPre", EVENT_EXITPRE},
125 {"FileEncoding", EVENT_ENCODINGCHANGED},
126 {"FileAppendPost", EVENT_FILEAPPENDPOST},
127 {"FileAppendPre", EVENT_FILEAPPENDPRE},
128 {"FileAppendCmd", EVENT_FILEAPPENDCMD},
129 {"FileChangedShell",EVENT_FILECHANGEDSHELL},
130 {"FileChangedShellPost",EVENT_FILECHANGEDSHELLPOST},
131 {"FileChangedRO", EVENT_FILECHANGEDRO},
132 {"FileReadPost", EVENT_FILEREADPOST},
133 {"FileReadPre", EVENT_FILEREADPRE},
134 {"FileReadCmd", EVENT_FILEREADCMD},
135 {"FileType", EVENT_FILETYPE},
136 {"FileWritePost", EVENT_FILEWRITEPOST},
137 {"FileWritePre", EVENT_FILEWRITEPRE},
138 {"FileWriteCmd", EVENT_FILEWRITECMD},
139 {"FilterReadPost", EVENT_FILTERREADPOST},
140 {"FilterReadPre", EVENT_FILTERREADPRE},
141 {"FilterWritePost", EVENT_FILTERWRITEPOST},
142 {"FilterWritePre", EVENT_FILTERWRITEPRE},
143 {"FocusGained", EVENT_FOCUSGAINED},
144 {"FocusLost", EVENT_FOCUSLOST},
145 {"FuncUndefined", EVENT_FUNCUNDEFINED},
146 {"GUIEnter", EVENT_GUIENTER},
147 {"GUIFailed", EVENT_GUIFAILED},
148 {"InsertChange", EVENT_INSERTCHANGE},
149 {"InsertEnter", EVENT_INSERTENTER},
150 {"InsertLeave", EVENT_INSERTLEAVE},
Bram Moolenaarb53e13a2020-10-21 12:19:53 +0200151 {"InsertLeavePre", EVENT_INSERTLEAVEPRE},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100152 {"InsertCharPre", EVENT_INSERTCHARPRE},
153 {"MenuPopup", EVENT_MENUPOPUP},
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +0200154 {"ModeChanged", EVENT_MODECHANGED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100155 {"OptionSet", EVENT_OPTIONSET},
156 {"QuickFixCmdPost", EVENT_QUICKFIXCMDPOST},
157 {"QuickFixCmdPre", EVENT_QUICKFIXCMDPRE},
158 {"QuitPre", EVENT_QUITPRE},
159 {"RemoteReply", EVENT_REMOTEREPLY},
Bram Moolenaar8aeec402019-09-15 23:02:04 +0200160 {"SafeState", EVENT_SAFESTATE},
Bram Moolenaar69198cb2019-09-16 21:58:13 +0200161 {"SafeStateAgain", EVENT_SAFESTATEAGAIN},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100162 {"SessionLoadPost", EVENT_SESSIONLOADPOST},
163 {"ShellCmdPost", EVENT_SHELLCMDPOST},
164 {"ShellFilterPost", EVENT_SHELLFILTERPOST},
Bram Moolenaarbe5ee862020-06-10 20:56:58 +0200165 {"SigUSR1", EVENT_SIGUSR1},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100166 {"SourceCmd", EVENT_SOURCECMD},
167 {"SourcePre", EVENT_SOURCEPRE},
168 {"SourcePost", EVENT_SOURCEPOST},
169 {"SpellFileMissing",EVENT_SPELLFILEMISSING},
170 {"StdinReadPost", EVENT_STDINREADPOST},
171 {"StdinReadPre", EVENT_STDINREADPRE},
172 {"SwapExists", EVENT_SWAPEXISTS},
173 {"Syntax", EVENT_SYNTAX},
174 {"TabNew", EVENT_TABNEW},
175 {"TabClosed", EVENT_TABCLOSED},
176 {"TabEnter", EVENT_TABENTER},
177 {"TabLeave", EVENT_TABLEAVE},
178 {"TermChanged", EVENT_TERMCHANGED},
179 {"TerminalOpen", EVENT_TERMINALOPEN},
Bram Moolenaar28ed4df2019-10-26 16:21:40 +0200180 {"TerminalWinOpen", EVENT_TERMINALWINOPEN},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100181 {"TermResponse", EVENT_TERMRESPONSE},
182 {"TextChanged", EVENT_TEXTCHANGED},
183 {"TextChangedI", EVENT_TEXTCHANGEDI},
184 {"TextChangedP", EVENT_TEXTCHANGEDP},
185 {"User", EVENT_USER},
186 {"VimEnter", EVENT_VIMENTER},
187 {"VimLeave", EVENT_VIMLEAVE},
188 {"VimLeavePre", EVENT_VIMLEAVEPRE},
189 {"WinNew", EVENT_WINNEW},
naohiro ono23beefe2021-11-13 12:38:49 +0000190 {"WinClosed", EVENT_WINCLOSED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100191 {"WinEnter", EVENT_WINENTER},
192 {"WinLeave", EVENT_WINLEAVE},
LemonBoy09371822022-04-08 15:18:45 +0100193 {"WinScrolled", EVENT_WINSCROLLED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100194 {"VimResized", EVENT_VIMRESIZED},
195 {"TextYankPost", EVENT_TEXTYANKPOST},
Bram Moolenaar100118c2020-12-11 19:30:34 +0100196 {"VimSuspend", EVENT_VIMSUSPEND},
197 {"VimResume", EVENT_VIMRESUME},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100198 {NULL, (event_T)0}
199};
200
201static AutoPat *first_autopat[NUM_EVENTS] =
202{
203 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
204 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
205 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
206 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
207 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
208 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
209};
210
211static AutoPat *last_autopat[NUM_EVENTS] =
212{
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,
217 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
218 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
219};
220
kylo252ae6f1d82022-02-16 19:24:07 +0000221#define AUGROUP_DEFAULT (-1) // default autocmd group
222#define AUGROUP_ERROR (-2) // erroneous autocmd group
223#define AUGROUP_ALL (-3) // all autocmd groups
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100224
225/*
226 * struct used to keep status while executing autocommands for an event.
227 */
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100228struct AutoPatCmd_S
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100229{
230 AutoPat *curpat; // next AutoPat to examine
231 AutoCmd *nextcmd; // next AutoCmd to execute
232 int group; // group being used
233 char_u *fname; // fname to match with
234 char_u *sfname; // sfname to match with
235 char_u *tail; // tail of fname
236 event_T event; // current event
LemonBoyeca7c602022-04-14 15:39:43 +0100237 sctx_T script_ctx; // script context where it is defined
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100238 int arg_bufnr; // Initially equal to <abuf>, set to zero when
239 // buf is deleted.
LemonBoyeca7c602022-04-14 15:39:43 +0100240 AutoPatCmd_T *next; // chain of active apc-s for auto-invalidation
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100241};
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100242
LemonBoyeca7c602022-04-14 15:39:43 +0100243static AutoPatCmd_T *active_apc_list = NULL; // stack of active autocommands
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100244
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200245// Macro to loop over all the patterns for an autocmd event
246#define FOR_ALL_AUTOCMD_PATTERNS(event, ap) \
247 for ((ap) = first_autopat[(int)(event)]; (ap) != NULL; (ap) = (ap)->next)
248
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100249/*
250 * augroups stores a list of autocmd group names.
251 */
252static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
253#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
Bram Moolenaarc667da52019-11-30 20:52:27 +0100254// use get_deleted_augroup() to get this
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100255static char_u *deleted_augroup = NULL;
256
257/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100258 * The ID of the current group. Group 0 is the default one.
259 */
260static int current_augroup = AUGROUP_DEFAULT;
261
Bram Moolenaarc667da52019-11-30 20:52:27 +0100262static int au_need_clean = FALSE; // need to delete marked patterns
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100263
264static char_u *event_nr2name(event_T event);
265static int au_get_grouparg(char_u **argp);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200266static 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 +0100267static 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 +0100268static void auto_next_pat(AutoPatCmd_T *apc, int stop_at_last);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100269static int au_find_group(char_u *name);
270
271static event_T last_event;
272static int last_group;
Bram Moolenaarc667da52019-11-30 20:52:27 +0100273static int autocmd_blocked = 0; // block all autocmds
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100274
275 static char_u *
276get_deleted_augroup(void)
277{
278 if (deleted_augroup == NULL)
279 deleted_augroup = (char_u *)_("--Deleted--");
280 return deleted_augroup;
281}
282
283/*
284 * Show the autocommands for one AutoPat.
285 */
286 static void
287show_autocmd(AutoPat *ap, event_T event)
288{
289 AutoCmd *ac;
290
291 // Check for "got_int" (here and at various places below), which is set
292 // when "q" has been hit for the "--more--" prompt
293 if (got_int)
294 return;
295 if (ap->pat == NULL) // pattern has been removed
296 return;
297
298 msg_putchar('\n');
299 if (got_int)
300 return;
301 if (event != last_event || ap->group != last_group)
302 {
303 if (ap->group != AUGROUP_DEFAULT)
304 {
305 if (AUGROUP_NAME(ap->group) == NULL)
306 msg_puts_attr((char *)get_deleted_augroup(), HL_ATTR(HLF_E));
307 else
308 msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
309 msg_puts(" ");
310 }
311 msg_puts_attr((char *)event_nr2name(event), HL_ATTR(HLF_T));
312 last_event = event;
313 last_group = ap->group;
314 msg_putchar('\n');
315 if (got_int)
316 return;
317 }
318 msg_col = 4;
319 msg_outtrans(ap->pat);
320
321 for (ac = ap->cmds; ac != NULL; ac = ac->next)
322 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100323 if (ac->cmd == NULL) // skip removed commands
324 continue;
325
326 if (msg_col >= 14)
327 msg_putchar('\n');
328 msg_col = 14;
329 if (got_int)
330 return;
331 msg_outtrans(ac->cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100332#ifdef FEAT_EVAL
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100333 if (p_verbose > 0)
334 last_set_msg(ac->script_ctx);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100335#endif
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100336 if (got_int)
337 return;
338 if (ac->next != NULL)
339 {
340 msg_putchar('\n');
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100341 if (got_int)
342 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100343 }
344 }
345}
346
347/*
348 * Mark an autocommand pattern for deletion.
349 */
350 static void
351au_remove_pat(AutoPat *ap)
352{
353 VIM_CLEAR(ap->pat);
354 ap->buflocal_nr = -1;
355 au_need_clean = TRUE;
356}
357
358/*
359 * Mark all commands for a pattern for deletion.
360 */
361 static void
362au_remove_cmds(AutoPat *ap)
363{
364 AutoCmd *ac;
365
366 for (ac = ap->cmds; ac != NULL; ac = ac->next)
367 VIM_CLEAR(ac->cmd);
368 au_need_clean = TRUE;
369}
370
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200371// Delete one command from an autocmd pattern.
372static void au_del_cmd(AutoCmd *ac)
373{
374 VIM_CLEAR(ac->cmd);
375 au_need_clean = TRUE;
376}
377
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100378/*
379 * Cleanup autocommands and patterns that have been deleted.
380 * This is only done when not executing autocommands.
381 */
382 static void
383au_cleanup(void)
384{
385 AutoPat *ap, **prev_ap;
386 AutoCmd *ac, **prev_ac;
387 event_T event;
388
389 if (autocmd_busy || !au_need_clean)
390 return;
391
392 // loop over all events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100393 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100394 event = (event_T)((int)event + 1))
395 {
396 // loop over all autocommand patterns
397 prev_ap = &(first_autopat[(int)event]);
398 for (ap = *prev_ap; ap != NULL; ap = *prev_ap)
399 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200400 int has_cmd = FALSE;
401
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200402 // loop over all commands for this pattern
403 prev_ac = &(ap->cmds);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100404 for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
405 {
406 // remove the command if the pattern is to be deleted or when
407 // the command has been marked for deletion
408 if (ap->pat == NULL || ac->cmd == NULL)
409 {
410 *prev_ac = ac->next;
411 vim_free(ac->cmd);
412 vim_free(ac);
413 }
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200414 else
415 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200416 has_cmd = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100417 prev_ac = &(ac->next);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200418 }
419 }
420
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200421 if (ap->pat != NULL && !has_cmd)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200422 // Pattern was not marked for deletion, but all of its
423 // commands were. So mark the pattern for deletion.
424 au_remove_pat(ap);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100425
426 // remove the pattern if it has been marked for deletion
427 if (ap->pat == NULL)
428 {
429 if (ap->next == NULL)
430 {
431 if (prev_ap == &(first_autopat[(int)event]))
432 last_autopat[(int)event] = NULL;
433 else
434 // this depends on the "next" field being the first in
435 // the struct
436 last_autopat[(int)event] = (AutoPat *)prev_ap;
437 }
438 *prev_ap = ap->next;
439 vim_regfree(ap->reg_prog);
440 vim_free(ap);
441 }
442 else
443 prev_ap = &(ap->next);
444 }
445 }
446
447 au_need_clean = FALSE;
448}
449
450/*
451 * Called when buffer is freed, to remove/invalidate related buffer-local
452 * autocmds.
453 */
454 void
455aubuflocal_remove(buf_T *buf)
456{
LemonBoyeca7c602022-04-14 15:39:43 +0100457 AutoPat *ap;
458 event_T event;
459 AutoPatCmd_T *apc;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100460
461 // invalidate currently executing autocommands
462 for (apc = active_apc_list; apc; apc = apc->next)
463 if (buf->b_fnum == apc->arg_bufnr)
464 apc->arg_bufnr = 0;
465
466 // invalidate buflocals looping through events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100467 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100468 event = (event_T)((int)event + 1))
469 // loop over all autocommand patterns
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200470 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100471 if (ap->buflocal_nr == buf->b_fnum)
472 {
473 au_remove_pat(ap);
474 if (p_verbose >= 6)
475 {
476 verbose_enter();
477 smsg(_("auto-removing autocommand: %s <buffer=%d>"),
478 event_nr2name(event), buf->b_fnum);
479 verbose_leave();
480 }
481 }
482 au_cleanup();
483}
484
485/*
486 * Add an autocmd group name.
487 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
488 */
489 static int
490au_new_group(char_u *name)
491{
492 int i;
493
494 i = au_find_group(name);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100495 if (i != AUGROUP_ERROR)
496 return i;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100497
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100498 // the group doesn't exist yet, add it. First try using a free entry.
499 for (i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100500 if (AUGROUP_NAME(i) == NULL)
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100501 break;
502 if (i == augroups.ga_len && ga_grow(&augroups, 1) == FAIL)
503 return AUGROUP_ERROR;
504
505 AUGROUP_NAME(i) = vim_strsave(name);
506 if (AUGROUP_NAME(i) == NULL)
507 return AUGROUP_ERROR;
508 if (i == augroups.ga_len)
509 ++augroups.ga_len;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100510
511 return i;
512}
513
514 static void
515au_del_group(char_u *name)
516{
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100517 int i;
518 event_T event;
519 AutoPat *ap;
520 int in_use = FALSE;
521
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100522
523 i = au_find_group(name);
524 if (i == AUGROUP_ERROR) // the group doesn't exist
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100525 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100526 semsg(_(e_no_such_group_str), name);
527 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100528 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100529 if (i == current_augroup)
530 {
531 emsg(_(e_cannot_delete_current_group));
532 return;
533 }
534
535 for (event = (event_T)0; (int)event < NUM_EVENTS;
536 event = (event_T)((int)event + 1))
537 {
538 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
539 if (ap->group == i && ap->pat != NULL)
540 {
541 give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
542 in_use = TRUE;
543 event = NUM_EVENTS;
544 break;
545 }
546 }
547 vim_free(AUGROUP_NAME(i));
548 if (in_use)
549 AUGROUP_NAME(i) = get_deleted_augroup();
550 else
551 AUGROUP_NAME(i) = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100552}
553
554/*
555 * Find the ID of an autocmd group name.
556 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
557 */
558 static int
559au_find_group(char_u *name)
560{
561 int i;
562
563 for (i = 0; i < augroups.ga_len; ++i)
564 if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
565 && STRCMP(AUGROUP_NAME(i), name) == 0)
566 return i;
567 return AUGROUP_ERROR;
568}
569
570/*
571 * Return TRUE if augroup "name" exists.
572 */
573 int
574au_has_group(char_u *name)
575{
576 return au_find_group(name) != AUGROUP_ERROR;
577}
578
579/*
580 * ":augroup {name}".
581 */
582 void
583do_augroup(char_u *arg, int del_group)
584{
585 int i;
586
587 if (del_group)
588 {
589 if (*arg == NUL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000590 emsg(_(e_argument_required));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100591 else
592 au_del_group(arg);
593 }
594 else if (STRICMP(arg, "end") == 0) // ":aug end": back to group 0
595 current_augroup = AUGROUP_DEFAULT;
596 else if (*arg) // ":aug xxx": switch to group xxx
597 {
598 i = au_new_group(arg);
599 if (i != AUGROUP_ERROR)
600 current_augroup = i;
601 }
602 else // ":aug": list the group names
603 {
604 msg_start();
605 for (i = 0; i < augroups.ga_len; ++i)
606 {
607 if (AUGROUP_NAME(i) != NULL)
608 {
609 msg_puts((char *)AUGROUP_NAME(i));
610 msg_puts(" ");
611 }
612 }
613 msg_clr_eos();
614 msg_end();
615 }
616}
617
618#if defined(EXITFREE) || defined(PROTO)
619 void
620free_all_autocmds(void)
621{
622 int i;
623 char_u *s;
624
625 for (current_augroup = -1; current_augroup < augroups.ga_len;
626 ++current_augroup)
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200627 do_autocmd(NULL, (char_u *)"", TRUE);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100628
629 for (i = 0; i < augroups.ga_len; ++i)
630 {
631 s = ((char_u **)(augroups.ga_data))[i];
632 if (s != get_deleted_augroup())
633 vim_free(s);
634 }
635 ga_clear(&augroups);
636}
637#endif
638
639/*
640 * Return the event number for event name "start".
641 * Return NUM_EVENTS if the event name was not found.
642 * Return a pointer to the next event name in "end".
643 */
644 static event_T
645event_name2nr(char_u *start, char_u **end)
646{
647 char_u *p;
648 int i;
649 int len;
650
651 // the event name ends with end of line, '|', a blank or a comma
652 for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
653 ;
654 for (i = 0; event_names[i].name != NULL; ++i)
655 {
656 len = (int)STRLEN(event_names[i].name);
657 if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0)
658 break;
659 }
660 if (*p == ',')
661 ++p;
662 *end = p;
663 if (event_names[i].name == NULL)
664 return NUM_EVENTS;
665 return event_names[i].event;
666}
667
668/*
669 * Return the name for event "event".
670 */
671 static char_u *
672event_nr2name(event_T event)
673{
674 int i;
675
676 for (i = 0; event_names[i].name != NULL; ++i)
677 if (event_names[i].event == event)
678 return (char_u *)event_names[i].name;
679 return (char_u *)"Unknown";
680}
681
682/*
683 * Scan over the events. "*" stands for all events.
684 */
685 static char_u *
686find_end_event(
687 char_u *arg,
688 int have_group) // TRUE when group name was found
689{
690 char_u *pat;
691 char_u *p;
692
693 if (*arg == '*')
694 {
695 if (arg[1] && !VIM_ISWHITE(arg[1]))
696 {
Bram Moolenaar6d057012021-12-31 18:49:43 +0000697 semsg(_(e_illegal_character_after_star_str), arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100698 return NULL;
699 }
700 pat = arg + 1;
701 }
702 else
703 {
704 for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p)
705 {
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100706 if ((int)event_name2nr(pat, &p) >= NUM_EVENTS)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100707 {
708 if (have_group)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000709 semsg(_(e_no_such_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100710 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000711 semsg(_(e_no_such_group_or_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100712 return NULL;
713 }
714 }
715 }
716 return pat;
717}
718
719/*
720 * Return TRUE if "event" is included in 'eventignore'.
721 */
722 static int
723event_ignored(event_T event)
724{
725 char_u *p = p_ei;
726
727 while (*p != NUL)
728 {
729 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
730 return TRUE;
731 if (event_name2nr(p, &p) == event)
732 return TRUE;
733 }
734
735 return FALSE;
736}
737
738/*
739 * Return OK when the contents of p_ei is valid, FAIL otherwise.
740 */
741 int
742check_ei(void)
743{
744 char_u *p = p_ei;
745
746 while (*p)
747 {
748 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
749 {
750 p += 3;
751 if (*p == ',')
752 ++p;
753 }
754 else if (event_name2nr(p, &p) == NUM_EVENTS)
755 return FAIL;
756 }
757
758 return OK;
759}
760
761# if defined(FEAT_SYN_HL) || defined(PROTO)
762
763/*
764 * Add "what" to 'eventignore' to skip loading syntax highlighting for every
765 * buffer loaded into the window. "what" must start with a comma.
766 * Returns the old value of 'eventignore' in allocated memory.
767 */
768 char_u *
769au_event_disable(char *what)
770{
771 char_u *new_ei;
772 char_u *save_ei;
773
774 save_ei = vim_strsave(p_ei);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100775 if (save_ei == NULL)
776 return NULL;
777
778 new_ei = vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what));
779 if (new_ei == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100780 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100781 vim_free(save_ei);
782 return NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100783 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100784
785 if (*what == ',' && *p_ei == NUL)
786 STRCPY(new_ei, what + 1);
787 else
788 STRCAT(new_ei, what);
789 set_string_option_direct((char_u *)"ei", -1, new_ei,
790 OPT_FREE, SID_NONE);
791 vim_free(new_ei);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100792 return save_ei;
793}
794
795 void
796au_event_restore(char_u *old_ei)
797{
798 if (old_ei != NULL)
799 {
800 set_string_option_direct((char_u *)"ei", -1, old_ei,
801 OPT_FREE, SID_NONE);
802 vim_free(old_ei);
803 }
804}
Bram Moolenaarc667da52019-11-30 20:52:27 +0100805# endif // FEAT_SYN_HL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100806
807/*
808 * do_autocmd() -- implements the :autocmd command. Can be used in the
809 * following ways:
810 *
811 * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
812 * will be automatically executed for <event>
813 * when editing a file matching <pat>, in
814 * the current group.
815 * :autocmd <event> <pat> Show the autocommands associated with
816 * <event> and <pat>.
817 * :autocmd <event> Show the autocommands associated with
818 * <event>.
819 * :autocmd Show all autocommands.
820 * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
821 * <event> and <pat>, and add the command
822 * <cmd>, for the current group.
823 * :autocmd! <event> <pat> Remove all autocommands associated with
824 * <event> and <pat> for the current group.
825 * :autocmd! <event> Remove all autocommands associated with
826 * <event> for the current group.
827 * :autocmd! Remove ALL autocommands for the current
828 * group.
829 *
830 * Multiple events and patterns may be given separated by commas. Here are
831 * some examples:
832 * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
833 * :autocmd bufleave * set tw=79 nosmartindent ic infercase
834 *
835 * :autocmd * *.c show all autocommands for *.c files.
836 *
837 * Mostly a {group} argument can optionally appear before <event>.
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200838 * "eap" can be NULL.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100839 */
840 void
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200841do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100842{
843 char_u *arg = arg_in;
844 char_u *pat;
845 char_u *envpat = NULL;
846 char_u *cmd;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200847 int cmd_need_free = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100848 event_T event;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200849 char_u *tofree = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100850 int nested = FALSE;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200851 int once = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100852 int group;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200853 int i;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200854 int flags = 0;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100855
856 if (*arg == '|')
857 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000858 eap->nextcmd = arg + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100859 arg = (char_u *)"";
860 group = AUGROUP_ALL; // no argument, use all groups
861 }
862 else
863 {
864 /*
865 * Check for a legal group name. If not, use AUGROUP_ALL.
866 */
867 group = au_get_grouparg(&arg);
868 if (arg == NULL) // out of memory
869 return;
870 }
871
872 /*
873 * Scan over the events.
874 * If we find an illegal name, return here, don't do anything.
875 */
876 pat = find_end_event(arg, group != AUGROUP_ALL);
877 if (pat == NULL)
878 return;
879
880 pat = skipwhite(pat);
881 if (*pat == '|')
882 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000883 eap->nextcmd = pat + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100884 pat = (char_u *)"";
885 cmd = (char_u *)"";
886 }
887 else
888 {
889 /*
890 * Scan over the pattern. Put a NUL at the end.
891 */
892 cmd = pat;
893 while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
894 cmd++;
895 if (*cmd)
896 *cmd++ = NUL;
897
898 // Expand environment variables in the pattern. Set 'shellslash', we
899 // want forward slashes here.
900 if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
901 {
902#ifdef BACKSLASH_IN_FILENAME
903 int p_ssl_save = p_ssl;
904
905 p_ssl = TRUE;
906#endif
907 envpat = expand_env_save(pat);
908#ifdef BACKSLASH_IN_FILENAME
909 p_ssl = p_ssl_save;
910#endif
911 if (envpat != NULL)
912 pat = envpat;
913 }
914
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100915 cmd = skipwhite(cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200916 for (i = 0; i < 2; i++)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100917 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100918 if (*cmd == NUL)
919 continue;
920
921 // Check for "++once" flag.
922 if (STRNCMP(cmd, "++once", 6) == 0 && VIM_ISWHITE(cmd[6]))
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200923 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100924 if (once)
925 semsg(_(e_duplicate_argument_str), "++once");
926 once = TRUE;
927 cmd = skipwhite(cmd + 6);
928 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200929
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100930 // Check for "++nested" flag.
931 if ((STRNCMP(cmd, "++nested", 8) == 0 && VIM_ISWHITE(cmd[8])))
932 {
933 if (nested)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200934 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100935 semsg(_(e_duplicate_argument_str), "++nested");
936 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200937 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100938 nested = TRUE;
939 cmd = skipwhite(cmd + 8);
940 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200941
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100942 // Check for the old "nested" flag in legacy script.
943 if (STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
944 {
945 if (in_vim9script())
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200946 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100947 // If there ever is a :nested command this error should
948 // be removed and "nested" accepted as the start of the
949 // command.
950 emsg(_(e_invalid_command_nested_did_you_mean_plusplus_nested));
951 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200952 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100953 if (nested)
954 {
955 semsg(_(e_duplicate_argument_str), "nested");
956 return;
957 }
958 nested = TRUE;
959 cmd = skipwhite(cmd + 6);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200960 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100961 }
962
963 /*
964 * Find the start of the commands.
965 * Expand <sfile> in it.
966 */
967 if (*cmd != NUL)
968 {
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200969 if (eap != NULL)
970 // Read a {} block if it follows.
971 cmd = may_get_cmd_block(eap, cmd, &tofree, &flags);
972
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100973 cmd = expand_sfile(cmd);
974 if (cmd == NULL) // some error
975 return;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200976 cmd_need_free = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100977 }
978 }
979
980 /*
981 * Print header when showing autocommands.
982 */
983 if (!forceit && *cmd == NUL)
984 // Highlight title
985 msg_puts_title(_("\n--- Autocommands ---"));
986
987 /*
988 * Loop over the events.
989 */
990 last_event = (event_T)-1; // for listing the event name
991 last_group = AUGROUP_ERROR; // for listing the group name
992 if (*arg == '*' || *arg == NUL || *arg == '|')
993 {
Bram Moolenaarb6db1462021-12-24 19:24:47 +0000994 if (*cmd != NUL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +0100995 emsg(_(e_cannot_define_autocommands_for_all_events));
996 else
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100997 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar9a046fd2021-01-28 13:47:59 +0100998 event = (event_T)((int)event + 1))
999 if (do_autocmd_event(event, pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001000 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001001 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001002 }
1003 else
1004 {
1005 while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
1006 if (do_autocmd_event(event_name2nr(arg, &arg), pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001007 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001008 break;
1009 }
1010
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001011 if (cmd_need_free)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001012 vim_free(cmd);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001013 vim_free(tofree);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001014 vim_free(envpat);
1015}
1016
1017/*
1018 * Find the group ID in a ":autocmd" or ":doautocmd" argument.
1019 * The "argp" argument is advanced to the following argument.
1020 *
1021 * Returns the group ID, AUGROUP_ERROR for error (out of memory).
1022 */
1023 static int
1024au_get_grouparg(char_u **argp)
1025{
1026 char_u *group_name;
1027 char_u *p;
1028 char_u *arg = *argp;
1029 int group = AUGROUP_ALL;
1030
1031 for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p)
1032 ;
1033 if (p > arg)
1034 {
Bram Moolenaardf44a272020-06-07 20:49:05 +02001035 group_name = vim_strnsave(arg, p - arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001036 if (group_name == NULL) // out of memory
1037 return AUGROUP_ERROR;
1038 group = au_find_group(group_name);
1039 if (group == AUGROUP_ERROR)
1040 group = AUGROUP_ALL; // no match, use all groups
1041 else
1042 *argp = skipwhite(p); // match, skip over group name
1043 vim_free(group_name);
1044 }
1045 return group;
1046}
1047
1048/*
1049 * do_autocmd() for one event.
1050 * If *pat == NUL do for all patterns.
1051 * If *cmd == NUL show entries.
1052 * If forceit == TRUE delete entries.
1053 * If group is not AUGROUP_ALL, only use this group.
1054 */
1055 static int
1056do_autocmd_event(
1057 event_T event,
1058 char_u *pat,
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001059 int once,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001060 int nested,
1061 char_u *cmd,
1062 int forceit,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001063 int group,
1064 int flags)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001065{
1066 AutoPat *ap;
1067 AutoPat **prev_ap;
1068 AutoCmd *ac;
1069 AutoCmd **prev_ac;
1070 int brace_level;
1071 char_u *endpat;
1072 int findgroup;
1073 int allgroups;
1074 int patlen;
1075 int is_buflocal;
1076 int buflocal_nr;
Bram Moolenaarc667da52019-11-30 20:52:27 +01001077 char_u buflocal_pat[25]; // for "<buffer=X>"
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001078
1079 if (group == AUGROUP_ALL)
1080 findgroup = current_augroup;
1081 else
1082 findgroup = group;
1083 allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
1084
1085 /*
1086 * Show or delete all patterns for an event.
1087 */
1088 if (*pat == NUL)
1089 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001090 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001091 {
1092 if (forceit) // delete the AutoPat, if it's in the current group
1093 {
1094 if (ap->group == findgroup)
1095 au_remove_pat(ap);
1096 }
1097 else if (group == AUGROUP_ALL || ap->group == group)
1098 show_autocmd(ap, event);
1099 }
1100 }
1101
1102 /*
1103 * Loop through all the specified patterns.
1104 */
1105 for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
1106 {
1107 /*
1108 * Find end of the pattern.
1109 * Watch out for a comma in braces, like "*.\{obj,o\}".
1110 */
1111 brace_level = 0;
1112 for (endpat = pat; *endpat && (*endpat != ',' || brace_level
1113 || (endpat > pat && endpat[-1] == '\\')); ++endpat)
1114 {
1115 if (*endpat == '{')
1116 brace_level++;
1117 else if (*endpat == '}')
1118 brace_level--;
1119 }
1120 if (pat == endpat) // ignore single comma
1121 continue;
1122 patlen = (int)(endpat - pat);
1123
1124 /*
1125 * detect special <buflocal[=X]> buffer-local patterns
1126 */
1127 is_buflocal = FALSE;
1128 buflocal_nr = 0;
1129
1130 if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
1131 && pat[patlen - 1] == '>')
1132 {
1133 // "<buffer...>": Error will be printed only for addition.
1134 // printing and removing will proceed silently.
1135 is_buflocal = TRUE;
1136 if (patlen == 8)
1137 // "<buffer>"
1138 buflocal_nr = curbuf->b_fnum;
1139 else if (patlen > 9 && pat[7] == '=')
1140 {
1141 if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0)
1142 // "<buffer=abuf>"
1143 buflocal_nr = autocmd_bufnr;
1144 else if (skipdigits(pat + 8) == pat + patlen - 1)
1145 // "<buffer=123>"
1146 buflocal_nr = atoi((char *)pat + 8);
1147 }
1148 }
1149
1150 if (is_buflocal)
1151 {
1152 // normalize pat into standard "<buffer>#N" form
1153 sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
1154 pat = buflocal_pat; // can modify pat and patlen
1155 patlen = (int)STRLEN(buflocal_pat); // but not endpat
1156 }
1157
1158 /*
1159 * Find AutoPat entries with this pattern. When adding a command it
1160 * always goes at or after the last one, so start at the end.
1161 */
1162 if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL)
1163 prev_ap = &last_autopat[(int)event];
1164 else
1165 prev_ap = &first_autopat[(int)event];
1166 while ((ap = *prev_ap) != NULL)
1167 {
1168 if (ap->pat != NULL)
1169 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001170 /*
1171 * Accept a pattern when:
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001172 * - a group was specified and it's that group, or a group was
1173 * not specified and it's the current group, or a group was
1174 * not specified and we are listing
1175 * - the length of the pattern matches
1176 * - the pattern matches.
1177 * For <buffer[=X]>, this condition works because we normalize
1178 * all buffer-local patterns.
1179 */
1180 if ((allgroups || ap->group == findgroup)
1181 && ap->patlen == patlen
1182 && STRNCMP(pat, ap->pat, patlen) == 0)
1183 {
1184 /*
1185 * Remove existing autocommands.
1186 * If adding any new autocmd's for this AutoPat, don't
1187 * delete the pattern from the autopat list, append to
1188 * this list.
1189 */
1190 if (forceit)
1191 {
1192 if (*cmd != NUL && ap->next == NULL)
1193 {
1194 au_remove_cmds(ap);
1195 break;
1196 }
1197 au_remove_pat(ap);
1198 }
1199
1200 /*
1201 * Show autocmd's for this autopat, or buflocals <buffer=X>
1202 */
1203 else if (*cmd == NUL)
1204 show_autocmd(ap, event);
1205
1206 /*
1207 * Add autocmd to this autopat, if it's the last one.
1208 */
1209 else if (ap->next == NULL)
1210 break;
1211 }
1212 }
1213 prev_ap = &ap->next;
1214 }
1215
1216 /*
1217 * Add a new command.
1218 */
1219 if (*cmd != NUL)
1220 {
1221 /*
1222 * If the pattern we want to add a command to does appear at the
1223 * end of the list (or not is not in the list at all), add the
1224 * pattern at the end of the list.
1225 */
1226 if (ap == NULL)
1227 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001228 // refuse to add buffer-local ap if buffer number is invalid
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001229 if (is_buflocal && (buflocal_nr == 0
1230 || buflist_findnr(buflocal_nr) == NULL))
1231 {
Bram Moolenaarf1474d82021-12-31 19:59:55 +00001232 semsg(_(e_buffer_nr_invalid_buffer_number), buflocal_nr);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001233 return FAIL;
1234 }
1235
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001236 ap = ALLOC_ONE(AutoPat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001237 if (ap == NULL)
1238 return FAIL;
1239 ap->pat = vim_strnsave(pat, patlen);
1240 ap->patlen = patlen;
1241 if (ap->pat == NULL)
1242 {
1243 vim_free(ap);
1244 return FAIL;
1245 }
1246
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001247#ifdef FEAT_EVAL
1248 // need to initialize last_mode for the first ModeChanged
1249 // autocmd
1250 if (event == EVENT_MODECHANGED && !has_modechanged())
LemonBoy2bf52dd2022-04-09 18:17:34 +01001251 get_mode(last_mode);
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001252#endif
LemonBoy09371822022-04-08 15:18:45 +01001253 // Initialize the fields checked by the WinScrolled trigger to
1254 // stop it from firing right after the first autocmd is defined.
1255 if (event == EVENT_WINSCROLLED && !has_winscrolled())
1256 {
1257 curwin->w_last_topline = curwin->w_topline;
1258 curwin->w_last_leftcol = curwin->w_leftcol;
zeertzjq670ab032022-08-28 19:16:15 +01001259 curwin->w_last_skipcol = curwin->w_skipcol;
LemonBoy09371822022-04-08 15:18:45 +01001260 curwin->w_last_width = curwin->w_width;
1261 curwin->w_last_height = curwin->w_height;
1262 }
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001263
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001264 if (is_buflocal)
1265 {
1266 ap->buflocal_nr = buflocal_nr;
1267 ap->reg_prog = NULL;
1268 }
1269 else
1270 {
1271 char_u *reg_pat;
1272
1273 ap->buflocal_nr = 0;
1274 reg_pat = file_pat_to_reg_pat(pat, endpat,
1275 &ap->allow_dirs, TRUE);
1276 if (reg_pat != NULL)
1277 ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
1278 vim_free(reg_pat);
1279 if (reg_pat == NULL || ap->reg_prog == NULL)
1280 {
1281 vim_free(ap->pat);
1282 vim_free(ap);
1283 return FAIL;
1284 }
1285 }
1286 ap->cmds = NULL;
1287 *prev_ap = ap;
1288 last_autopat[(int)event] = ap;
1289 ap->next = NULL;
1290 if (group == AUGROUP_ALL)
1291 ap->group = current_augroup;
1292 else
1293 ap->group = group;
1294 }
1295
1296 /*
1297 * Add the autocmd at the end of the AutoCmd list.
1298 */
1299 prev_ac = &(ap->cmds);
1300 while ((ac = *prev_ac) != NULL)
1301 prev_ac = &ac->next;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001302 ac = ALLOC_ONE(AutoCmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001303 if (ac == NULL)
1304 return FAIL;
1305 ac->cmd = vim_strsave(cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001306 ac->script_ctx = current_sctx;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001307 if (flags & UC_VIM9)
1308 ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001309#ifdef FEAT_EVAL
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001310 ac->script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001311#endif
1312 if (ac->cmd == NULL)
1313 {
1314 vim_free(ac);
1315 return FAIL;
1316 }
1317 ac->next = NULL;
1318 *prev_ac = ac;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001319 ac->once = once;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001320 ac->nested = nested;
1321 }
1322 }
1323
1324 au_cleanup(); // may really delete removed patterns/commands now
1325 return OK;
1326}
1327
1328/*
1329 * Implementation of ":doautocmd [group] event [fname]".
1330 * Return OK for success, FAIL for failure;
1331 */
1332 int
1333do_doautocmd(
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001334 char_u *arg_start,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001335 int do_msg, // give message for no matching autocmds?
1336 int *did_something)
1337{
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001338 char_u *arg = arg_start;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001339 char_u *fname;
1340 int nothing_done = TRUE;
1341 int group;
1342
1343 if (did_something != NULL)
1344 *did_something = FALSE;
1345
1346 /*
1347 * Check for a legal group name. If not, use AUGROUP_ALL.
1348 */
1349 group = au_get_grouparg(&arg);
1350 if (arg == NULL) // out of memory
1351 return FAIL;
1352
1353 if (*arg == '*')
1354 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001355 emsg(_(e_cant_execute_autocommands_for_all_events));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001356 return FAIL;
1357 }
1358
1359 /*
1360 * Scan over the events.
1361 * If we find an illegal name, return here, don't do anything.
1362 */
1363 fname = find_end_event(arg, group != AUGROUP_ALL);
1364 if (fname == NULL)
1365 return FAIL;
1366
1367 fname = skipwhite(fname);
1368
1369 /*
1370 * Loop over the events.
1371 */
1372 while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
1373 if (apply_autocmds_group(event_name2nr(arg, &arg),
1374 fname, NULL, TRUE, group, curbuf, NULL))
1375 nothing_done = FALSE;
1376
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001377 if (nothing_done && do_msg
1378#ifdef FEAT_EVAL
1379 && !aborting()
1380#endif
1381 )
1382 smsg(_("No matching autocommands: %s"), arg_start);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001383 if (did_something != NULL)
1384 *did_something = !nothing_done;
1385
1386#ifdef FEAT_EVAL
1387 return aborting() ? FAIL : OK;
1388#else
1389 return OK;
1390#endif
1391}
1392
1393/*
1394 * ":doautoall": execute autocommands for each loaded buffer.
1395 */
1396 void
1397ex_doautoall(exarg_T *eap)
1398{
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001399 int retval = OK;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001400 aco_save_T aco;
1401 buf_T *buf;
1402 bufref_T bufref;
1403 char_u *arg = eap->arg;
1404 int call_do_modelines = check_nomodeline(&arg);
1405 int did_aucmd;
1406
1407 /*
1408 * This is a bit tricky: For some commands curwin->w_buffer needs to be
1409 * equal to curbuf, but for some buffers there may not be a window.
1410 * So we change the buffer for the current window for a moment. This
1411 * gives problems when the autocommands make changes to the list of
1412 * buffers or windows...
1413 */
1414 FOR_ALL_BUFFERS(buf)
1415 {
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001416 // Only do loaded buffers and skip the current buffer, it's done last.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001417 if (buf->b_ml.ml_mfp == NULL || buf == curbuf)
1418 continue;
1419
1420 // find a window for this buffer and save some values
1421 aucmd_prepbuf(&aco, buf);
1422 set_bufref(&bufref, buf);
1423
1424 // execute the autocommands for this buffer
1425 retval = do_doautocmd(arg, FALSE, &did_aucmd);
1426
1427 if (call_do_modelines && did_aucmd)
1428 // Execute the modeline settings, but don't set window-local
1429 // options if we are using the current window for another
1430 // buffer.
1431 do_modelines(curwin == aucmd_win ? OPT_NOWIN : 0);
1432
1433 // restore the current window
1434 aucmd_restbuf(&aco);
1435
1436 // stop if there is some error or buffer was deleted
1437 if (retval == FAIL || !bufref_valid(&bufref))
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001438 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001439 retval = FAIL;
1440 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001441 }
1442 }
1443
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001444 // Execute autocommands for the current buffer last.
1445 if (retval == OK)
1446 {
1447 do_doautocmd(arg, FALSE, &did_aucmd);
1448 if (call_do_modelines && did_aucmd)
1449 do_modelines(0);
1450 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001451}
1452
1453/*
1454 * Check *argp for <nomodeline>. When it is present return FALSE, otherwise
1455 * return TRUE and advance *argp to after it.
1456 * Thus return TRUE when do_modelines() should be called.
1457 */
1458 int
1459check_nomodeline(char_u **argp)
1460{
1461 if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
1462 {
1463 *argp = skipwhite(*argp + 12);
1464 return FALSE;
1465 }
1466 return TRUE;
1467}
1468
1469/*
1470 * Prepare for executing autocommands for (hidden) buffer "buf".
1471 * Search for a visible window containing the current buffer. If there isn't
1472 * one then use "aucmd_win".
1473 * Set "curbuf" and "curwin" to match "buf".
1474 */
1475 void
1476aucmd_prepbuf(
1477 aco_save_T *aco, // structure to save values in
1478 buf_T *buf) // new curbuf
1479{
1480 win_T *win;
1481 int save_ea;
1482#ifdef FEAT_AUTOCHDIR
1483 int save_acd;
1484#endif
1485
1486 // Find a window that is for the new buffer
1487 if (buf == curbuf) // be quick when buf is curbuf
1488 win = curwin;
1489 else
1490 FOR_ALL_WINDOWS(win)
1491 if (win->w_buffer == buf)
1492 break;
1493
1494 // Allocate "aucmd_win" when needed. If this fails (out of memory) fall
1495 // back to using the current window.
1496 if (win == NULL && aucmd_win == NULL)
1497 {
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001498 aucmd_win = win_alloc_popup_win();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001499 if (aucmd_win == NULL)
1500 win = curwin;
1501 }
1502 if (win == NULL && aucmd_win_used)
1503 // Strange recursive autocommand, fall back to using the current
1504 // window. Expect a few side effects...
1505 win = curwin;
1506
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001507 aco->save_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001508 aco->save_curbuf = curbuf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001509 aco->save_prevwin_id = prevwin == NULL ? 0 : prevwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001510 if (win != NULL)
1511 {
1512 // There is a window for "buf" in the current tab page, make it the
1513 // curwin. This is preferred, it has the least side effects (esp. if
1514 // "buf" is curbuf).
1515 aco->use_aucmd_win = FALSE;
1516 curwin = win;
1517 }
1518 else
1519 {
1520 // There is no window for "buf", use "aucmd_win". To minimize the side
1521 // effects, insert it in the current tab page.
1522 // Anything related to a window (e.g., setting folds) may have
1523 // unexpected results.
1524 aco->use_aucmd_win = TRUE;
1525 aucmd_win_used = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001526
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001527 win_init_popup_win(aucmd_win, buf);
1528
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001529 aco->globaldir = globaldir;
1530 globaldir = NULL;
1531
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001532 // Split the current window, put the aucmd_win in the upper half.
1533 // We don't want the BufEnter or WinEnter autocommands.
1534 block_autocmds();
1535 make_snapshot(SNAP_AUCMD_IDX);
1536 save_ea = p_ea;
1537 p_ea = FALSE;
1538
1539#ifdef FEAT_AUTOCHDIR
1540 // Prevent chdir() call in win_enter_ext(), through do_autochdir().
1541 save_acd = p_acd;
1542 p_acd = FALSE;
1543#endif
1544
Bram Moolenaardff97e62022-01-24 20:00:55 +00001545 // no redrawing and don't set the window title
1546 ++RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001547 (void)win_split_ins(0, WSP_TOP, aucmd_win, 0);
Bram Moolenaardff97e62022-01-24 20:00:55 +00001548 --RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001549 (void)win_comp_pos(); // recompute window positions
1550 p_ea = save_ea;
1551#ifdef FEAT_AUTOCHDIR
1552 p_acd = save_acd;
1553#endif
1554 unblock_autocmds();
1555 curwin = aucmd_win;
1556 }
1557 curbuf = buf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001558 aco->new_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001559 set_bufref(&aco->new_curbuf, curbuf);
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001560
1561 // disable the Visual area, the position may be invalid in another buffer
1562 aco->save_VIsual_active = VIsual_active;
1563 VIsual_active = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001564}
1565
1566/*
1567 * Cleanup after executing autocommands for a (hidden) buffer.
1568 * Restore the window as it was (if possible).
1569 */
1570 void
1571aucmd_restbuf(
1572 aco_save_T *aco) // structure holding saved values
1573{
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001574 int dummy;
1575 win_T *save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001576
1577 if (aco->use_aucmd_win)
1578 {
1579 --curbuf->b_nwindows;
1580 // Find "aucmd_win", it can't be closed, but it may be in another tab
1581 // page. Do not trigger autocommands here.
1582 block_autocmds();
1583 if (curwin != aucmd_win)
1584 {
1585 tabpage_T *tp;
1586 win_T *wp;
1587
1588 FOR_ALL_TAB_WINDOWS(tp, wp)
1589 {
1590 if (wp == aucmd_win)
1591 {
1592 if (tp != curtab)
1593 goto_tabpage_tp(tp, TRUE, TRUE);
1594 win_goto(aucmd_win);
1595 goto win_found;
1596 }
1597 }
1598 }
1599win_found:
1600
1601 // Remove the window and frame from the tree of frames.
1602 (void)winframe_remove(curwin, &dummy, NULL);
1603 win_remove(curwin, NULL);
1604 aucmd_win_used = FALSE;
1605 last_status(FALSE); // may need to remove last status line
1606
1607 if (!valid_tabpage_win(curtab))
1608 // no valid window in current tabpage
1609 close_tabpage(curtab);
1610
1611 restore_snapshot(SNAP_AUCMD_IDX, FALSE);
1612 (void)win_comp_pos(); // recompute window positions
1613 unblock_autocmds();
1614
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001615 save_curwin = win_find_by_id(aco->save_curwin_id);
1616 if (save_curwin != NULL)
1617 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001618 else
1619 // Hmm, original window disappeared. Just use the first one.
1620 curwin = firstwin;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001621 curbuf = curwin->w_buffer;
1622#ifdef FEAT_JOB_CHANNEL
1623 // May need to restore insert mode for a prompt buffer.
1624 entering_window(curwin);
1625#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001626 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001627#ifdef FEAT_EVAL
1628 vars_clear(&aucmd_win->w_vars->dv_hashtab); // free all w: variables
1629 hash_init(&aucmd_win->w_vars->dv_hashtab); // re-use the hashtab
1630#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001631 vim_free(globaldir);
1632 globaldir = aco->globaldir;
1633
1634 // the buffer contents may have changed
1635 check_cursor();
1636 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1637 {
1638 curwin->w_topline = curbuf->b_ml.ml_line_count;
1639#ifdef FEAT_DIFF
1640 curwin->w_topfill = 0;
1641#endif
1642 }
1643#if defined(FEAT_GUI)
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001644 if (gui.in_use)
1645 {
1646 // Hide the scrollbars from the aucmd_win and update.
1647 gui_mch_enable_scrollbar(
1648 &aucmd_win->w_scrollbars[SBAR_LEFT], FALSE);
1649 gui_mch_enable_scrollbar(
1650 &aucmd_win->w_scrollbars[SBAR_RIGHT], FALSE);
1651 gui_may_update_scrollbars();
1652 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001653#endif
1654 }
1655 else
1656 {
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001657 // Restore curwin. Use the window ID, a window may have been closed
1658 // and the memory re-used for another one.
1659 save_curwin = win_find_by_id(aco->save_curwin_id);
1660 if (save_curwin != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001661 {
1662 // Restore the buffer which was previously edited by curwin, if
1663 // it was changed, we are still the same window and the buffer is
1664 // valid.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001665 if (curwin->w_id == aco->new_curwin_id
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001666 && curbuf != aco->new_curbuf.br_buf
1667 && bufref_valid(&aco->new_curbuf)
1668 && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
1669 {
1670# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
1671 if (curwin->w_s == &curbuf->b_s)
1672 curwin->w_s = &aco->new_curbuf.br_buf->b_s;
1673# endif
1674 --curbuf->b_nwindows;
1675 curbuf = aco->new_curbuf.br_buf;
1676 curwin->w_buffer = curbuf;
1677 ++curbuf->b_nwindows;
1678 }
1679
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001680 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001681 curbuf = curwin->w_buffer;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001682 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001683 // In case the autocommand moves the cursor to a position that
1684 // does not exist in curbuf.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001685 check_cursor();
1686 }
1687 }
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001688
1689 check_cursor(); // just in case lines got deleted
1690 VIsual_active = aco->save_VIsual_active;
1691 if (VIsual_active)
1692 check_pos(curbuf, &VIsual);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001693}
1694
1695static int autocmd_nested = FALSE;
1696
1697/*
1698 * Execute autocommands for "event" and file name "fname".
1699 * Return TRUE if some commands were executed.
1700 */
1701 int
1702apply_autocmds(
1703 event_T event,
1704 char_u *fname, // NULL or empty means use actual file name
1705 char_u *fname_io, // fname to use for <afile> on cmdline
1706 int force, // when TRUE, ignore autocmd_busy
1707 buf_T *buf) // buffer for <abuf>
1708{
1709 return apply_autocmds_group(event, fname, fname_io, force,
1710 AUGROUP_ALL, buf, NULL);
1711}
1712
1713/*
1714 * Like apply_autocmds(), but with extra "eap" argument. This takes care of
1715 * setting v:filearg.
1716 */
1717 int
1718apply_autocmds_exarg(
1719 event_T event,
1720 char_u *fname,
1721 char_u *fname_io,
1722 int force,
1723 buf_T *buf,
1724 exarg_T *eap)
1725{
1726 return apply_autocmds_group(event, fname, fname_io, force,
1727 AUGROUP_ALL, buf, eap);
1728}
1729
1730/*
1731 * Like apply_autocmds(), but handles the caller's retval. If the script
1732 * processing is being aborted or if retval is FAIL when inside a try
1733 * conditional, no autocommands are executed. If otherwise the autocommands
1734 * cause the script to be aborted, retval is set to FAIL.
1735 */
1736 int
1737apply_autocmds_retval(
1738 event_T event,
1739 char_u *fname, // NULL or empty means use actual file name
1740 char_u *fname_io, // fname to use for <afile> on cmdline
1741 int force, // when TRUE, ignore autocmd_busy
1742 buf_T *buf, // buffer for <abuf>
1743 int *retval) // pointer to caller's retval
1744{
1745 int did_cmd;
1746
1747#ifdef FEAT_EVAL
1748 if (should_abort(*retval))
1749 return FALSE;
1750#endif
1751
1752 did_cmd = apply_autocmds_group(event, fname, fname_io, force,
1753 AUGROUP_ALL, buf, NULL);
1754 if (did_cmd
1755#ifdef FEAT_EVAL
1756 && aborting()
1757#endif
1758 )
1759 *retval = FAIL;
1760 return did_cmd;
1761}
1762
1763/*
1764 * Return TRUE when there is a CursorHold autocommand defined.
1765 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001766 static int
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001767has_cursorhold(void)
1768{
Bram Moolenaar24959102022-05-07 20:01:16 +01001769 return (first_autopat[(int)(get_real_state() == MODE_NORMAL_BUSY
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001770 ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
1771}
1772
1773/*
1774 * Return TRUE if the CursorHold event can be triggered.
1775 */
1776 int
1777trigger_cursorhold(void)
1778{
1779 int state;
1780
1781 if (!did_cursorhold
1782 && has_cursorhold()
1783 && reg_recording == 0
1784 && typebuf.tb_len == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001785 && !ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001786 {
1787 state = get_real_state();
Bram Moolenaar24959102022-05-07 20:01:16 +01001788 if (state == MODE_NORMAL_BUSY || (state & MODE_INSERT) != 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001789 return TRUE;
1790 }
1791 return FALSE;
1792}
1793
1794/*
LemonBoy09371822022-04-08 15:18:45 +01001795 * Return TRUE when there is a WinScrolled autocommand defined.
1796 */
1797 int
1798has_winscrolled(void)
1799{
1800 return (first_autopat[(int)EVENT_WINSCROLLED] != NULL);
1801}
1802
1803/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001804 * Return TRUE when there is a CursorMoved autocommand defined.
1805 */
1806 int
1807has_cursormoved(void)
1808{
1809 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1810}
1811
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001812/*
1813 * Return TRUE when there is a CursorMovedI autocommand defined.
1814 */
1815 int
1816has_cursormovedI(void)
1817{
1818 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1819}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001820
1821/*
1822 * Return TRUE when there is a TextChanged autocommand defined.
1823 */
1824 int
1825has_textchanged(void)
1826{
1827 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1828}
1829
1830/*
1831 * Return TRUE when there is a TextChangedI autocommand defined.
1832 */
1833 int
1834has_textchangedI(void)
1835{
1836 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
1837}
1838
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001839/*
1840 * Return TRUE when there is a TextChangedP autocommand defined.
1841 */
1842 int
1843has_textchangedP(void)
1844{
1845 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
1846}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001847
1848/*
1849 * Return TRUE when there is an InsertCharPre autocommand defined.
1850 */
1851 int
1852has_insertcharpre(void)
1853{
1854 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
1855}
1856
1857/*
1858 * Return TRUE when there is an CmdUndefined autocommand defined.
1859 */
1860 int
1861has_cmdundefined(void)
1862{
1863 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
1864}
1865
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001866#if defined(FEAT_EVAL) || defined(PROTO)
1867/*
1868 * Return TRUE when there is a TextYankPost autocommand defined.
1869 */
1870 int
1871has_textyankpost(void)
1872{
1873 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
1874}
1875#endif
1876
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001877#if defined(FEAT_EVAL) || defined(PROTO)
1878/*
1879 * Return TRUE when there is a CompleteChanged autocommand defined.
1880 */
1881 int
1882has_completechanged(void)
1883{
1884 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
1885}
1886#endif
1887
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02001888#if defined(FEAT_EVAL) || defined(PROTO)
1889/*
1890 * Return TRUE when there is a ModeChanged autocommand defined.
1891 */
1892 int
1893has_modechanged(void)
1894{
1895 return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
1896}
1897#endif
1898
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001899/*
1900 * Execute autocommands for "event" and file name "fname".
1901 * Return TRUE if some commands were executed.
1902 */
1903 static int
1904apply_autocmds_group(
1905 event_T event,
1906 char_u *fname, // NULL or empty means use actual file name
1907 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
1908 // use fname
1909 int force, // when TRUE, ignore autocmd_busy
1910 int group, // group ID, or AUGROUP_ALL
1911 buf_T *buf, // buffer for <abuf>
1912 exarg_T *eap UNUSED) // command arguments
1913{
1914 char_u *sfname = NULL; // short file name
1915 char_u *tail;
1916 int save_changed;
1917 buf_T *old_curbuf;
1918 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001919 char_u *save_autocmd_fname;
1920 int save_autocmd_fname_full;
1921 int save_autocmd_bufnr;
1922 char_u *save_autocmd_match;
1923 int save_autocmd_busy;
1924 int save_autocmd_nested;
1925 static int nesting = 0;
LemonBoyeca7c602022-04-14 15:39:43 +01001926 AutoPatCmd_T patcmd;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001927 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001928 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001929#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001930 funccal_entry_T funccal_entry;
1931 char_u *save_cmdarg;
1932 long save_cmdbang;
1933#endif
1934 static int filechangeshell_busy = FALSE;
1935#ifdef FEAT_PROFILE
1936 proftime_T wait_time;
1937#endif
1938 int did_save_redobuff = FALSE;
1939 save_redo_T save_redo;
1940 int save_KeyTyped = KeyTyped;
ichizokc3f91c02021-12-17 09:44:33 +00001941 int save_did_emsg;
Bram Moolenaare31ee862020-01-07 20:59:34 +01001942 ESTACK_CHECK_DECLARATION
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001943
1944 /*
1945 * Quickly return if there are no autocommands for this event or
1946 * autocommands are blocked.
1947 */
1948 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
1949 || autocmd_blocked > 0)
1950 goto BYPASS_AU;
1951
1952 /*
1953 * When autocommands are busy, new autocommands are only executed when
1954 * explicitly enabled with the "nested" flag.
1955 */
1956 if (autocmd_busy && !(force || autocmd_nested))
1957 goto BYPASS_AU;
1958
1959#ifdef FEAT_EVAL
1960 /*
1961 * Quickly return when immediately aborting on error, or when an interrupt
1962 * occurred or an exception was thrown but not caught.
1963 */
1964 if (aborting())
1965 goto BYPASS_AU;
1966#endif
1967
1968 /*
1969 * FileChangedShell never nests, because it can create an endless loop.
1970 */
1971 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
1972 || event == EVENT_FILECHANGEDSHELLPOST))
1973 goto BYPASS_AU;
1974
1975 /*
1976 * Ignore events in 'eventignore'.
1977 */
1978 if (event_ignored(event))
1979 goto BYPASS_AU;
1980
1981 /*
1982 * Allow nesting of autocommands, but restrict the depth, because it's
1983 * possible to create an endless loop.
1984 */
1985 if (nesting == 10)
1986 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001987 emsg(_(e_autocommand_nesting_too_deep));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001988 goto BYPASS_AU;
1989 }
1990
1991 /*
1992 * Check if these autocommands are disabled. Used when doing ":all" or
1993 * ":ball".
1994 */
1995 if ( (autocmd_no_enter
1996 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
1997 || (autocmd_no_leave
1998 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
1999 goto BYPASS_AU;
2000
2001 /*
2002 * Save the autocmd_* variables and info about the current buffer.
2003 */
2004 save_autocmd_fname = autocmd_fname;
2005 save_autocmd_fname_full = autocmd_fname_full;
2006 save_autocmd_bufnr = autocmd_bufnr;
2007 save_autocmd_match = autocmd_match;
2008 save_autocmd_busy = autocmd_busy;
2009 save_autocmd_nested = autocmd_nested;
2010 save_changed = curbuf->b_changed;
2011 old_curbuf = curbuf;
2012
2013 /*
2014 * Set the file name to be used for <afile>.
2015 * Make a copy to avoid that changing a buffer name or directory makes it
2016 * invalid.
2017 */
2018 if (fname_io == NULL)
2019 {
2020 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002021 || event == EVENT_OPTIONSET
2022 || event == EVENT_MODECHANGED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002023 autocmd_fname = NULL;
2024 else if (fname != NULL && !ends_excmd(*fname))
2025 autocmd_fname = fname;
2026 else if (buf != NULL)
2027 autocmd_fname = buf->b_ffname;
2028 else
2029 autocmd_fname = NULL;
2030 }
2031 else
2032 autocmd_fname = fname_io;
2033 if (autocmd_fname != NULL)
2034 autocmd_fname = vim_strsave(autocmd_fname);
2035 autocmd_fname_full = FALSE; // call FullName_save() later
2036
2037 /*
2038 * Set the buffer number to be used for <abuf>.
2039 */
2040 if (buf == NULL)
2041 autocmd_bufnr = 0;
2042 else
2043 autocmd_bufnr = buf->b_fnum;
2044
2045 /*
2046 * When the file name is NULL or empty, use the file name of buffer "buf".
2047 * Always use the full path of the file name to match with, in case
2048 * "allow_dirs" is set.
2049 */
2050 if (fname == NULL || *fname == NUL)
2051 {
2052 if (buf == NULL)
2053 fname = NULL;
2054 else
2055 {
2056#ifdef FEAT_SYN_HL
2057 if (event == EVENT_SYNTAX)
2058 fname = buf->b_p_syn;
2059 else
2060#endif
2061 if (event == EVENT_FILETYPE)
2062 fname = buf->b_p_ft;
2063 else
2064 {
2065 if (buf->b_sfname != NULL)
2066 sfname = vim_strsave(buf->b_sfname);
2067 fname = buf->b_ffname;
2068 }
2069 }
2070 if (fname == NULL)
2071 fname = (char_u *)"";
2072 fname = vim_strsave(fname); // make a copy, so we can change it
2073 }
2074 else
2075 {
2076 sfname = vim_strsave(fname);
2077 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002078 // ColorScheme, QuickFixCmd*, DirChanged and similar.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002079 if (event == EVENT_FILETYPE
2080 || event == EVENT_SYNTAX
2081 || event == EVENT_CMDLINECHANGED
2082 || event == EVENT_CMDLINEENTER
2083 || event == EVENT_CMDLINELEAVE
2084 || event == EVENT_CMDWINENTER
2085 || event == EVENT_CMDWINLEAVE
2086 || event == EVENT_CMDUNDEFINED
2087 || event == EVENT_FUNCUNDEFINED
2088 || event == EVENT_REMOTEREPLY
2089 || event == EVENT_SPELLFILEMISSING
2090 || event == EVENT_QUICKFIXCMDPRE
2091 || event == EVENT_COLORSCHEME
2092 || event == EVENT_COLORSCHEMEPRE
2093 || event == EVENT_OPTIONSET
2094 || event == EVENT_QUICKFIXCMDPOST
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002095 || event == EVENT_DIRCHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002096 || event == EVENT_DIRCHANGEDPRE
naohiro ono23beefe2021-11-13 12:38:49 +00002097 || event == EVENT_MODECHANGED
zeertzjqc601d982022-10-10 13:46:15 +01002098 || event == EVENT_MENUPOPUP
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002099 || event == EVENT_USER
LemonBoy09371822022-04-08 15:18:45 +01002100 || event == EVENT_WINCLOSED
2101 || event == EVENT_WINSCROLLED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002102 {
2103 fname = vim_strsave(fname);
2104 autocmd_fname_full = TRUE; // don't expand it later
2105 }
2106 else
2107 fname = FullName_save(fname, FALSE);
2108 }
2109 if (fname == NULL) // out of memory
2110 {
2111 vim_free(sfname);
2112 retval = FALSE;
2113 goto BYPASS_AU;
2114 }
2115
2116#ifdef BACKSLASH_IN_FILENAME
2117 /*
2118 * Replace all backslashes with forward slashes. This makes the
2119 * autocommand patterns portable between Unix and MS-DOS.
2120 */
2121 if (sfname != NULL)
2122 forward_slash(sfname);
2123 forward_slash(fname);
2124#endif
2125
2126#ifdef VMS
2127 // remove version for correct match
2128 if (sfname != NULL)
2129 vms_remove_version(sfname);
2130 vms_remove_version(fname);
2131#endif
2132
2133 /*
2134 * Set the name to be used for <amatch>.
2135 */
2136 autocmd_match = fname;
2137
2138
2139 // Don't redraw while doing autocommands.
2140 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002141
2142 // name and lnum are filled in later
2143 estack_push(ETYPE_AUCMD, NULL, 0);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002144 ESTACK_CHECK_SETUP
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002145
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002146 save_current_sctx = current_sctx;
2147
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002148#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002149# ifdef FEAT_PROFILE
2150 if (do_profiling == PROF_YES)
2151 prof_child_enter(&wait_time); // doesn't count for the caller itself
2152# endif
2153
2154 // Don't use local function variables, if called from a function.
2155 save_funccal(&funccal_entry);
2156#endif
2157
2158 /*
2159 * When starting to execute autocommands, save the search patterns.
2160 */
2161 if (!autocmd_busy)
2162 {
2163 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002164 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002165 {
2166 saveRedobuff(&save_redo);
2167 did_save_redobuff = TRUE;
2168 }
2169 did_filetype = keep_filetype;
2170 }
2171
2172 /*
2173 * Note that we are applying autocmds. Some commands need to know.
2174 */
2175 autocmd_busy = TRUE;
2176 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2177 ++nesting; // see matching decrement below
2178
2179 // Remember that FileType was triggered. Used for did_filetype().
2180 if (event == EVENT_FILETYPE)
2181 did_filetype = TRUE;
2182
2183 tail = gettail(fname);
2184
2185 // Find first autocommand that matches
LemonBoyeca7c602022-04-14 15:39:43 +01002186 CLEAR_FIELD(patcmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002187 patcmd.curpat = first_autopat[(int)event];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002188 patcmd.group = group;
2189 patcmd.fname = fname;
2190 patcmd.sfname = sfname;
2191 patcmd.tail = tail;
2192 patcmd.event = event;
2193 patcmd.arg_bufnr = autocmd_bufnr;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002194 auto_next_pat(&patcmd, FALSE);
2195
2196 // found one, start executing the autocommands
2197 if (patcmd.curpat != NULL)
2198 {
2199 // add to active_apc_list
2200 patcmd.next = active_apc_list;
2201 active_apc_list = &patcmd;
2202
2203#ifdef FEAT_EVAL
2204 // set v:cmdarg (only when there is a matching pattern)
2205 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2206 if (eap != NULL)
2207 {
2208 save_cmdarg = set_cmdarg(eap, NULL);
2209 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2210 }
2211 else
2212 save_cmdarg = NULL; // avoid gcc warning
2213#endif
2214 retval = TRUE;
2215 // mark the last pattern, to avoid an endless loop when more patterns
2216 // are added when executing autocommands
2217 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2218 ap->last = FALSE;
2219 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002220
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002221 // Make sure cursor and topline are valid. The first time the current
2222 // values are saved, restored by reset_lnums(). When nested only the
2223 // values are corrected when needed.
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002224 if (nesting == 1)
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002225 check_lnums(TRUE);
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002226 else
2227 check_lnums_nested(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002228
ichizokc3f91c02021-12-17 09:44:33 +00002229 save_did_emsg = did_emsg;
2230
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002231 do_cmdline(NULL, getnextac, (void *)&patcmd,
2232 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002233
ichizokc3f91c02021-12-17 09:44:33 +00002234 did_emsg += save_did_emsg;
2235
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002236 if (nesting == 1)
2237 // restore cursor and topline, unless they were changed
2238 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002239
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002240#ifdef FEAT_EVAL
2241 if (eap != NULL)
2242 {
2243 (void)set_cmdarg(NULL, save_cmdarg);
2244 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2245 }
2246#endif
2247 // delete from active_apc_list
2248 if (active_apc_list == &patcmd) // just in case
2249 active_apc_list = patcmd.next;
2250 }
2251
2252 --RedrawingDisabled;
2253 autocmd_busy = save_autocmd_busy;
2254 filechangeshell_busy = FALSE;
2255 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002256 vim_free(SOURCING_NAME);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002257 ESTACK_CHECK_NOW
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002258 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002259 vim_free(autocmd_fname);
2260 autocmd_fname = save_autocmd_fname;
2261 autocmd_fname_full = save_autocmd_fname_full;
2262 autocmd_bufnr = save_autocmd_bufnr;
2263 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002264 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002265#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002266 restore_funccal();
2267# ifdef FEAT_PROFILE
2268 if (do_profiling == PROF_YES)
2269 prof_child_exit(&wait_time);
2270# endif
2271#endif
2272 KeyTyped = save_KeyTyped;
2273 vim_free(fname);
2274 vim_free(sfname);
2275 --nesting; // see matching increment above
2276
2277 /*
2278 * When stopping to execute autocommands, restore the search patterns and
2279 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2280 * free any windows in the au_pending_free_win list.
2281 */
2282 if (!autocmd_busy)
2283 {
2284 restore_search_patterns();
2285 if (did_save_redobuff)
2286 restoreRedobuff(&save_redo);
2287 did_filetype = FALSE;
2288 while (au_pending_free_buf != NULL)
2289 {
2290 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002291
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002292 vim_free(au_pending_free_buf);
2293 au_pending_free_buf = b;
2294 }
2295 while (au_pending_free_win != NULL)
2296 {
2297 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002298
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002299 vim_free(au_pending_free_win);
2300 au_pending_free_win = w;
2301 }
2302 }
2303
2304 /*
2305 * Some events don't set or reset the Changed flag.
2306 * Check if still in the same buffer!
2307 */
2308 if (curbuf == old_curbuf
2309 && (event == EVENT_BUFREADPOST
2310 || event == EVENT_BUFWRITEPOST
2311 || event == EVENT_FILEAPPENDPOST
2312 || event == EVENT_VIMLEAVE
2313 || event == EVENT_VIMLEAVEPRE))
2314 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002315 if (curbuf->b_changed != save_changed)
2316 need_maketitle = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002317 curbuf->b_changed = save_changed;
2318 }
2319
2320 au_cleanup(); // may really delete removed patterns/commands now
2321
2322BYPASS_AU:
2323 // When wiping out a buffer make sure all its buffer-local autocommands
2324 // are deleted.
2325 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2326 aubuflocal_remove(buf);
2327
2328 if (retval == OK && event == EVENT_FILETYPE)
2329 au_did_filetype = TRUE;
2330
2331 return retval;
2332}
2333
2334# ifdef FEAT_EVAL
2335static char_u *old_termresponse = NULL;
2336# endif
2337
2338/*
2339 * Block triggering autocommands until unblock_autocmd() is called.
2340 * Can be used recursively, so long as it's symmetric.
2341 */
2342 void
2343block_autocmds(void)
2344{
2345# ifdef FEAT_EVAL
2346 // Remember the value of v:termresponse.
2347 if (autocmd_blocked == 0)
2348 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
2349# endif
2350 ++autocmd_blocked;
2351}
2352
2353 void
2354unblock_autocmds(void)
2355{
2356 --autocmd_blocked;
2357
2358# ifdef FEAT_EVAL
2359 // When v:termresponse was set while autocommands were blocked, trigger
2360 // the autocommands now. Esp. useful when executing a shell command
2361 // during startup (vimdiff).
2362 if (autocmd_blocked == 0
2363 && get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2364 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2365# endif
2366}
2367
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002368 int
2369is_autocmd_blocked(void)
2370{
2371 return autocmd_blocked != 0;
2372}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002373
2374/*
2375 * Find next autocommand pattern that matches.
2376 */
2377 static void
2378auto_next_pat(
LemonBoyeca7c602022-04-14 15:39:43 +01002379 AutoPatCmd_T *apc,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002380 int stop_at_last) // stop when 'last' flag is set
2381{
2382 AutoPat *ap;
2383 AutoCmd *cp;
2384 char_u *name;
2385 char *s;
LemonBoyeca7c602022-04-14 15:39:43 +01002386 estack_T *entry;
2387 char_u *namep;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002388
LemonBoyeca7c602022-04-14 15:39:43 +01002389 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
2390
2391 // Clear the exestack entry for this ETYPE_AUCMD entry.
2392 VIM_CLEAR(entry->es_name);
2393 entry->es_info.aucmd = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002394
2395 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2396 {
2397 apc->curpat = NULL;
2398
2399 // Only use a pattern when it has not been removed, has commands and
2400 // the group matches. For buffer-local autocommands only check the
2401 // buffer number.
2402 if (ap->pat != NULL && ap->cmds != NULL
2403 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2404 {
2405 // execution-condition
2406 if (ap->buflocal_nr == 0
2407 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2408 apc->sfname, apc->tail, ap->allow_dirs))
2409 : ap->buflocal_nr == apc->arg_bufnr)
2410 {
2411 name = event_nr2name(apc->event);
2412 s = _("%s Autocommands for \"%s\"");
LemonBoyeca7c602022-04-14 15:39:43 +01002413 namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1);
2414 if (namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002415 {
LemonBoyeca7c602022-04-14 15:39:43 +01002416 sprintf((char *)namep, s, (char *)name, (char *)ap->pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002417 if (p_verbose >= 8)
2418 {
2419 verbose_enter();
LemonBoyeca7c602022-04-14 15:39:43 +01002420 smsg(_("Executing %s"), namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002421 verbose_leave();
2422 }
2423 }
2424
LemonBoyeca7c602022-04-14 15:39:43 +01002425 // Update the exestack entry for this autocmd.
2426 entry->es_name = namep;
2427 entry->es_info.aucmd = apc;
2428
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002429 apc->curpat = ap;
2430 apc->nextcmd = ap->cmds;
2431 // mark last command
2432 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2433 cp->last = FALSE;
2434 cp->last = TRUE;
2435 }
2436 line_breakcheck();
2437 if (apc->curpat != NULL) // found a match
2438 break;
2439 }
2440 if (stop_at_last && ap->last)
2441 break;
2442 }
2443}
2444
2445/*
LemonBoyeca7c602022-04-14 15:39:43 +01002446 * Get the script context where autocommand "acp" is defined.
2447 */
2448 sctx_T *
2449acp_script_ctx(AutoPatCmd_T *acp)
2450{
2451 return &acp->script_ctx;
2452}
2453
2454/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002455 * Get next autocommand command.
2456 * Called by do_cmdline() to get the next line for ":if".
2457 * Returns allocated string, or NULL for end of autocommands.
2458 */
2459 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002460getnextac(
2461 int c UNUSED,
2462 void *cookie,
2463 int indent UNUSED,
2464 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002465{
LemonBoyeca7c602022-04-14 15:39:43 +01002466 AutoPatCmd_T *acp = (AutoPatCmd_T *)cookie;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002467 char_u *retval;
2468 AutoCmd *ac;
2469
2470 // Can be called again after returning the last line.
2471 if (acp->curpat == NULL)
2472 return NULL;
2473
2474 // repeat until we find an autocommand to execute
2475 for (;;)
2476 {
2477 // skip removed commands
2478 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2479 if (acp->nextcmd->last)
2480 acp->nextcmd = NULL;
2481 else
2482 acp->nextcmd = acp->nextcmd->next;
2483
2484 if (acp->nextcmd != NULL)
2485 break;
2486
2487 // at end of commands, find next pattern that matches
2488 if (acp->curpat->last)
2489 acp->curpat = NULL;
2490 else
2491 acp->curpat = acp->curpat->next;
2492 if (acp->curpat != NULL)
2493 auto_next_pat(acp, TRUE);
2494 if (acp->curpat == NULL)
2495 return NULL;
2496 }
2497
2498 ac = acp->nextcmd;
2499
2500 if (p_verbose >= 9)
2501 {
2502 verbose_enter_scroll();
2503 smsg(_("autocommand %s"), ac->cmd);
2504 msg_puts("\n"); // don't overwrite this either
2505 verbose_leave_scroll();
2506 }
2507 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002508 // Remove one-shot ("once") autocmd in anticipation of its execution.
2509 if (ac->once)
2510 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002511 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002512 current_sctx = ac->script_ctx;
LemonBoyeca7c602022-04-14 15:39:43 +01002513 acp->script_ctx = current_sctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002514 if (ac->last)
2515 acp->nextcmd = NULL;
2516 else
2517 acp->nextcmd = ac->next;
2518 return retval;
2519}
2520
2521/*
2522 * Return TRUE if there is a matching autocommand for "fname".
2523 * To account for buffer-local autocommands, function needs to know
2524 * in which buffer the file will be opened.
2525 */
2526 int
2527has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2528{
2529 AutoPat *ap;
2530 char_u *fname;
2531 char_u *tail = gettail(sfname);
2532 int retval = FALSE;
2533
2534 fname = FullName_save(sfname, FALSE);
2535 if (fname == NULL)
2536 return FALSE;
2537
2538#ifdef BACKSLASH_IN_FILENAME
2539 /*
2540 * Replace all backslashes with forward slashes. This makes the
2541 * autocommand patterns portable between Unix and MS-DOS.
2542 */
2543 sfname = vim_strsave(sfname);
2544 if (sfname != NULL)
2545 forward_slash(sfname);
2546 forward_slash(fname);
2547#endif
2548
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002549 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002550 if (ap->pat != NULL && ap->cmds != NULL
2551 && (ap->buflocal_nr == 0
2552 ? match_file_pat(NULL, &ap->reg_prog,
2553 fname, sfname, tail, ap->allow_dirs)
2554 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2555 ))
2556 {
2557 retval = TRUE;
2558 break;
2559 }
2560
2561 vim_free(fname);
2562#ifdef BACKSLASH_IN_FILENAME
2563 vim_free(sfname);
2564#endif
2565
2566 return retval;
2567}
2568
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002569/*
2570 * Function given to ExpandGeneric() to obtain the list of autocommand group
2571 * names.
2572 */
2573 char_u *
2574get_augroup_name(expand_T *xp UNUSED, int idx)
2575{
2576 if (idx == augroups.ga_len) // add "END" add the end
2577 return (char_u *)"END";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002578 if (idx < 0 || idx >= augroups.ga_len) // end of list
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002579 return NULL;
2580 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2581 // skip deleted entries
2582 return (char_u *)"";
2583 return AUGROUP_NAME(idx); // return a name
2584}
2585
2586static int include_groups = FALSE;
2587
2588 char_u *
2589set_context_in_autocmd(
2590 expand_T *xp,
2591 char_u *arg,
2592 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2593{
2594 char_u *p;
2595 int group;
2596
2597 // check for a group name, skip it if present
2598 include_groups = FALSE;
2599 p = arg;
2600 group = au_get_grouparg(&arg);
2601 if (group == AUGROUP_ERROR)
2602 return NULL;
2603 // If there only is a group name that's what we expand.
2604 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2605 {
2606 arg = p;
2607 group = AUGROUP_ALL;
2608 }
2609
2610 // skip over event name
2611 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2612 if (*p == ',')
2613 arg = p + 1;
2614 if (*p == NUL)
2615 {
2616 if (group == AUGROUP_ALL)
2617 include_groups = TRUE;
2618 xp->xp_context = EXPAND_EVENTS; // expand event name
2619 xp->xp_pattern = arg;
2620 return NULL;
2621 }
2622
2623 // skip over pattern
2624 arg = skipwhite(p);
2625 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2626 arg++;
2627 if (*arg)
2628 return arg; // expand (next) command
2629
2630 if (doautocmd)
2631 xp->xp_context = EXPAND_FILES; // expand file names
2632 else
2633 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2634 return NULL;
2635}
2636
2637/*
2638 * Function given to ExpandGeneric() to obtain the list of event names.
2639 */
2640 char_u *
2641get_event_name(expand_T *xp UNUSED, int idx)
2642{
2643 if (idx < augroups.ga_len) // First list group names, if wanted
2644 {
2645 if (!include_groups || AUGROUP_NAME(idx) == NULL
2646 || AUGROUP_NAME(idx) == get_deleted_augroup())
2647 return (char_u *)""; // skip deleted entries
2648 return AUGROUP_NAME(idx); // return a name
2649 }
2650 return (char_u *)event_names[idx - augroups.ga_len].name;
2651}
2652
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002653
2654#if defined(FEAT_EVAL) || defined(PROTO)
2655/*
2656 * Return TRUE if autocmd is supported.
2657 */
2658 int
2659autocmd_supported(char_u *name)
2660{
2661 char_u *p;
2662
2663 return (event_name2nr(name, &p) != NUM_EVENTS);
2664}
2665
2666/*
2667 * Return TRUE if an autocommand is defined for a group, event and
2668 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2669 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2670 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2671 * Used for:
2672 * exists("#Group") or
2673 * exists("#Group#Event") or
2674 * exists("#Group#Event#pat") or
2675 * exists("#Event") or
2676 * exists("#Event#pat")
2677 */
2678 int
2679au_exists(char_u *arg)
2680{
2681 char_u *arg_save;
2682 char_u *pattern = NULL;
2683 char_u *event_name;
2684 char_u *p;
2685 event_T event;
2686 AutoPat *ap;
2687 buf_T *buflocal_buf = NULL;
2688 int group;
2689 int retval = FALSE;
2690
2691 // Make a copy so that we can change the '#' chars to a NUL.
2692 arg_save = vim_strsave(arg);
2693 if (arg_save == NULL)
2694 return FALSE;
2695 p = vim_strchr(arg_save, '#');
2696 if (p != NULL)
2697 *p++ = NUL;
2698
2699 // First, look for an autocmd group name
2700 group = au_find_group(arg_save);
2701 if (group == AUGROUP_ERROR)
2702 {
2703 // Didn't match a group name, assume the first argument is an event.
2704 group = AUGROUP_ALL;
2705 event_name = arg_save;
2706 }
2707 else
2708 {
2709 if (p == NULL)
2710 {
2711 // "Group": group name is present and it's recognized
2712 retval = TRUE;
2713 goto theend;
2714 }
2715
2716 // Must be "Group#Event" or "Group#Event#pat".
2717 event_name = p;
2718 p = vim_strchr(event_name, '#');
2719 if (p != NULL)
2720 *p++ = NUL; // "Group#Event#pat"
2721 }
2722
2723 pattern = p; // "pattern" is NULL when there is no pattern
2724
2725 // find the index (enum) for the event name
2726 event = event_name2nr(event_name, &p);
2727
2728 // return FALSE if the event name is not recognized
2729 if (event == NUM_EVENTS)
2730 goto theend;
2731
2732 // Find the first autocommand for this event.
2733 // If there isn't any, return FALSE;
2734 // If there is one and no pattern given, return TRUE;
2735 ap = first_autopat[(int)event];
2736 if (ap == NULL)
2737 goto theend;
2738
2739 // if pattern is "<buffer>", special handling is needed which uses curbuf
2740 // for pattern "<buffer=N>, fnamecmp() will work fine
2741 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2742 buflocal_buf = curbuf;
2743
2744 // Check if there is an autocommand with the given pattern.
2745 for ( ; ap != NULL; ap = ap->next)
2746 // only use a pattern when it has not been removed and has commands.
2747 // For buffer-local autocommands, fnamecmp() works fine.
2748 if (ap->pat != NULL && ap->cmds != NULL
2749 && (group == AUGROUP_ALL || ap->group == group)
2750 && (pattern == NULL
2751 || (buflocal_buf == NULL
2752 ? fnamecmp(ap->pat, pattern) == 0
2753 : ap->buflocal_nr == buflocal_buf->b_fnum)))
2754 {
2755 retval = TRUE;
2756 break;
2757 }
2758
2759theend:
2760 vim_free(arg_save);
2761 return retval;
2762}
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002763
2764/*
2765 * autocmd_add() and autocmd_delete() functions
2766 */
2767 static void
2768autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
2769{
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002770 list_T *aucmd_list;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002771 listitem_T *li;
2772 dict_T *event_dict;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002773 dictitem_T *di;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002774 char_u *event_name = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002775 list_T *event_list;
2776 listitem_T *eli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002777 event_T event;
2778 char_u *group_name = NULL;
2779 int group;
2780 char_u *pat = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002781 list_T *pat_list;
2782 listitem_T *pli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002783 char_u *cmd = NULL;
2784 char_u *end;
2785 int once;
2786 int nested;
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01002787 int replace; // replace the cmd for a group/event
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002788 int retval = VVAL_TRUE;
2789 int save_augroup = current_augroup;
2790
2791 rettv->v_type = VAR_BOOL;
2792 rettv->vval.v_number = VVAL_FALSE;
2793
2794 if (check_for_list_arg(argvars, 0) == FAIL)
2795 return;
2796
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002797 aucmd_list = argvars[0].vval.v_list;
2798 if (aucmd_list == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002799 return;
2800
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002801 FOR_ALL_LIST_ITEMS(aucmd_list, li)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002802 {
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002803 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002804 VIM_CLEAR(cmd);
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002805 event_name = NULL;
2806 event_list = NULL;
2807 pat = NULL;
2808 pat_list = NULL;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002809
2810 if (li->li_tv.v_type != VAR_DICT)
2811 continue;
2812
2813 event_dict = li->li_tv.vval.v_dict;
2814 if (event_dict == NULL)
2815 continue;
2816
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002817 di = dict_find(event_dict, (char_u *)"event", -1);
2818 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002819 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002820 if (di->di_tv.v_type == VAR_STRING)
2821 {
2822 event_name = di->di_tv.vval.v_string;
2823 if (event_name == NULL)
2824 {
2825 emsg(_(e_string_required));
2826 continue;
2827 }
2828 }
2829 else if (di->di_tv.v_type == VAR_LIST)
2830 {
2831 event_list = di->di_tv.vval.v_list;
2832 if (event_list == NULL)
2833 {
2834 emsg(_(e_list_required));
2835 continue;
2836 }
2837 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002838 else
2839 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002840 emsg(_(e_string_or_list_expected));
2841 continue;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002842 }
2843 }
2844
Bram Moolenaard61efa52022-07-23 09:52:04 +01002845 group_name = dict_get_string(event_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002846 if (group_name == NULL || *group_name == NUL)
2847 // if the autocmd group name is not specified, then use the current
2848 // autocmd group
2849 group = current_augroup;
2850 else
2851 {
2852 group = au_find_group(group_name);
2853 if (group == AUGROUP_ERROR)
2854 {
2855 if (delete)
2856 {
2857 semsg(_(e_no_such_group_str), group_name);
2858 retval = VVAL_FALSE;
2859 break;
2860 }
2861 // group is not found, create it now
2862 group = au_new_group(group_name);
2863 if (group == AUGROUP_ERROR)
2864 {
2865 semsg(_(e_no_such_group_str), group_name);
2866 retval = VVAL_FALSE;
2867 break;
2868 }
2869
2870 current_augroup = group;
2871 }
2872 }
2873
2874 // if a buffer number is specified, then generate a pattern of the form
2875 // "<buffer=n>. Otherwise, use the pattern supplied by the user.
2876 if (dict_has_key(event_dict, "bufnr"))
2877 {
2878 varnumber_T bnum;
2879
Bram Moolenaard61efa52022-07-23 09:52:04 +01002880 bnum = dict_get_number_def(event_dict, "bufnr", -1);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002881 if (bnum == -1)
2882 continue;
2883
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002884 vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
2885 pat = IObuff;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002886 }
2887 else
2888 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002889 di = dict_find(event_dict, (char_u *)"pattern", -1);
2890 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002891 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002892 if (di->di_tv.v_type == VAR_STRING)
2893 {
2894 pat = di->di_tv.vval.v_string;
2895 if (pat == NULL)
2896 {
2897 emsg(_(e_string_required));
2898 continue;
2899 }
2900 }
2901 else if (di->di_tv.v_type == VAR_LIST)
2902 {
2903 pat_list = di->di_tv.vval.v_list;
2904 if (pat_list == NULL)
2905 {
2906 emsg(_(e_list_required));
2907 continue;
2908 }
2909 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002910 else
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002911 {
2912 emsg(_(e_string_or_list_expected));
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002913 continue;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002914 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002915 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002916 else if (delete)
2917 pat = (char_u *)"";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002918 }
2919
Bram Moolenaard61efa52022-07-23 09:52:04 +01002920 once = dict_get_bool(event_dict, "once", FALSE);
2921 nested = dict_get_bool(event_dict, "nested", FALSE);
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01002922 // if 'replace' is true, then remove all the commands associated with
2923 // this autocmd event/group and add the new command.
Bram Moolenaard61efa52022-07-23 09:52:04 +01002924 replace = dict_get_bool(event_dict, "replace", FALSE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002925
Bram Moolenaard61efa52022-07-23 09:52:04 +01002926 cmd = dict_get_string(event_dict, "cmd", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002927 if (cmd == NULL)
2928 {
2929 if (delete)
2930 cmd = vim_strsave((char_u *)"");
2931 else
2932 continue;
2933 }
2934
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002935 if (delete && (event_name == NULL
2936 || (event_name[0] == '*' && event_name[1] == NUL)))
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002937 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002938 // if the event name is not specified or '*', delete all the events
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002939 for (event = (event_T)0; (int)event < NUM_EVENTS;
2940 event = (event_T)((int)event + 1))
2941 {
2942 if (do_autocmd_event(event, pat, once, nested, cmd, delete,
2943 group, 0) == FAIL)
2944 {
2945 retval = VVAL_FALSE;
2946 break;
2947 }
2948 }
2949 }
2950 else
2951 {
Bram Moolenaar968443e2022-05-27 21:16:34 +01002952 char_u *p = NULL;
2953
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002954 eli = NULL;
2955 end = NULL;
2956 while (TRUE)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002957 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002958 if (event_list != NULL)
2959 {
2960 if (eli == NULL)
2961 eli = event_list->lv_first;
2962 else
2963 eli = eli->li_next;
2964 if (eli == NULL)
2965 break;
2966 if (eli->li_tv.v_type != VAR_STRING
Bram Moolenaar882476a2022-06-01 16:02:38 +01002967 || (p = eli->li_tv.vval.v_string) == NULL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002968 {
2969 emsg(_(e_string_required));
Bram Moolenaar882476a2022-06-01 16:02:38 +01002970 break;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002971 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002972 }
2973 else
2974 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01002975 if (p == NULL)
2976 p = event_name;
2977 if (p == NULL || *p == NUL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002978 break;
2979 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002980
2981 event = event_name2nr(p, &end);
2982 if (event == NUM_EVENTS || *end != NUL)
2983 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01002984 // this also catches something following a valid event name
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002985 semsg(_(e_no_such_event_str), p);
2986 retval = VVAL_FALSE;
2987 break;
2988 }
2989 if (pat != NULL)
2990 {
2991 if (do_autocmd_event(event, pat, once, nested, cmd,
2992 delete | replace, group, 0) == FAIL)
2993 {
2994 retval = VVAL_FALSE;
2995 break;
2996 }
2997 }
2998 else if (pat_list != NULL)
2999 {
3000 FOR_ALL_LIST_ITEMS(pat_list, pli)
3001 {
3002 if (pli->li_tv.v_type != VAR_STRING
3003 || pli->li_tv.vval.v_string == NULL)
3004 {
3005 emsg(_(e_string_required));
3006 continue;
3007 }
3008 if (do_autocmd_event(event,
3009 pli->li_tv.vval.v_string, once, nested,
3010 cmd, delete | replace, group, 0) ==
3011 FAIL)
3012 {
3013 retval = VVAL_FALSE;
3014 break;
3015 }
3016 }
3017 if (retval == VVAL_FALSE)
3018 break;
3019 }
3020 if (event_name != NULL)
3021 p = end;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003022 }
3023 }
3024
3025 // if only the autocmd group name is specified for delete and the
3026 // autocmd event, pattern and cmd are not specified, then delete the
3027 // autocmd group.
3028 if (delete && group_name != NULL &&
3029 (event_name == NULL || event_name[0] == NUL)
3030 && (pat == NULL || pat[0] == NUL)
3031 && (cmd == NULL || cmd[0] == NUL))
3032 au_del_group(group_name);
3033 }
3034
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003035 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003036 VIM_CLEAR(cmd);
3037
3038 current_augroup = save_augroup;
3039 rettv->vval.v_number = retval;
3040}
3041
3042/*
3043 * autocmd_add() function
3044 */
3045 void
3046f_autocmd_add(typval_T *argvars, typval_T *rettv)
3047{
3048 autocmd_add_or_delete(argvars, rettv, FALSE);
3049}
3050
3051/*
3052 * autocmd_delete() function
3053 */
3054 void
3055f_autocmd_delete(typval_T *argvars, typval_T *rettv)
3056{
3057 autocmd_add_or_delete(argvars, rettv, TRUE);
3058}
3059
3060/*
3061 * autocmd_get() function
3062 * Returns a List of autocmds.
3063 */
3064 void
3065f_autocmd_get(typval_T *argvars, typval_T *rettv)
3066{
3067 event_T event_arg = NUM_EVENTS;
3068 event_T event;
3069 AutoPat *ap;
3070 AutoCmd *ac;
3071 list_T *event_list;
3072 dict_T *event_dict;
3073 char_u *event_name = NULL;
3074 char_u *pat = NULL;
3075 char_u *name = NULL;
3076 int group = AUGROUP_ALL;
3077
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01003078 if (rettv_list_alloc(rettv) == FAIL)
3079 return;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003080 if (check_for_opt_dict_arg(argvars, 0) == FAIL)
3081 return;
3082
3083 if (argvars[0].v_type == VAR_DICT)
3084 {
3085 // return only the autocmds in the specified group
3086 if (dict_has_key(argvars[0].vval.v_dict, "group"))
3087 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003088 name = dict_get_string(argvars[0].vval.v_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003089 if (name == NULL)
3090 return;
3091
3092 if (*name == NUL)
3093 group = AUGROUP_DEFAULT;
3094 else
3095 {
3096 group = au_find_group(name);
3097 if (group == AUGROUP_ERROR)
3098 {
3099 semsg(_(e_no_such_group_str), name);
3100 vim_free(name);
3101 return;
3102 }
3103 }
3104 vim_free(name);
3105 }
3106
3107 // return only the autocmds for the specified event
3108 if (dict_has_key(argvars[0].vval.v_dict, "event"))
3109 {
3110 int i;
3111
Bram Moolenaard61efa52022-07-23 09:52:04 +01003112 name = dict_get_string(argvars[0].vval.v_dict, "event", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003113 if (name == NULL)
3114 return;
3115
3116 if (name[0] == '*' && name[1] == NUL)
3117 event_arg = NUM_EVENTS;
3118 else
3119 {
3120 for (i = 0; event_names[i].name != NULL; i++)
3121 if (STRICMP(event_names[i].name, name) == 0)
3122 break;
3123 if (event_names[i].name == NULL)
3124 {
3125 semsg(_(e_no_such_event_str), name);
3126 vim_free(name);
3127 return;
3128 }
3129 event_arg = event_names[i].event;
3130 }
3131 vim_free(name);
3132 }
3133
3134 // return only the autocmds for the specified pattern
3135 if (dict_has_key(argvars[0].vval.v_dict, "pattern"))
3136 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003137 pat = dict_get_string(argvars[0].vval.v_dict, "pattern", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003138 if (pat == NULL)
3139 return;
3140 }
3141 }
3142
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003143 event_list = rettv->vval.v_list;
3144
3145 // iterate through all the autocmd events
3146 for (event = (event_T)0; (int)event < NUM_EVENTS;
3147 event = (event_T)((int)event + 1))
3148 {
3149 if (event_arg != NUM_EVENTS && event != event_arg)
3150 continue;
3151
3152 event_name = event_nr2name(event);
3153
3154 // iterate through all the patterns for this autocmd event
3155 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
3156 {
3157 char_u *group_name;
3158
3159 if (group != AUGROUP_ALL && group != ap->group)
3160 continue;
3161
3162 if (pat != NULL && STRCMP(pat, ap->pat) != 0)
3163 continue;
3164
3165 group_name = get_augroup_name(NULL, ap->group);
3166
3167 // iterate through all the commands for this pattern and add one
3168 // item for each cmd.
3169 for (ac = ap->cmds; ac != NULL; ac = ac->next)
3170 {
3171 event_dict = dict_alloc();
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003172 if (event_dict == NULL
3173 || list_append_dict(event_list, event_dict) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003174 return;
3175
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003176 if (dict_add_string(event_dict, "event", event_name) == FAIL
3177 || dict_add_string(event_dict, "group",
3178 group_name == NULL ? (char_u *)""
3179 : group_name) == FAIL
3180 || (ap->buflocal_nr != 0
3181 && (dict_add_number(event_dict, "bufnr",
3182 ap->buflocal_nr) == FAIL))
3183 || dict_add_string(event_dict, "pattern",
3184 ap->pat) == FAIL
3185 || dict_add_string(event_dict, "cmd", ac->cmd) == FAIL
3186 || dict_add_bool(event_dict, "once", ac->once) == FAIL
3187 || dict_add_bool(event_dict, "nested",
3188 ac->nested) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003189 return;
3190 }
3191 }
3192 }
3193
3194 vim_free(pat);
3195}
3196
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01003197#endif