blob: 599db46c7625ba935cf68d378d55969ebb77d97d [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
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002098 || event == EVENT_USER
LemonBoy09371822022-04-08 15:18:45 +01002099 || event == EVENT_WINCLOSED
2100 || event == EVENT_WINSCROLLED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002101 {
2102 fname = vim_strsave(fname);
2103 autocmd_fname_full = TRUE; // don't expand it later
2104 }
2105 else
2106 fname = FullName_save(fname, FALSE);
2107 }
2108 if (fname == NULL) // out of memory
2109 {
2110 vim_free(sfname);
2111 retval = FALSE;
2112 goto BYPASS_AU;
2113 }
2114
2115#ifdef BACKSLASH_IN_FILENAME
2116 /*
2117 * Replace all backslashes with forward slashes. This makes the
2118 * autocommand patterns portable between Unix and MS-DOS.
2119 */
2120 if (sfname != NULL)
2121 forward_slash(sfname);
2122 forward_slash(fname);
2123#endif
2124
2125#ifdef VMS
2126 // remove version for correct match
2127 if (sfname != NULL)
2128 vms_remove_version(sfname);
2129 vms_remove_version(fname);
2130#endif
2131
2132 /*
2133 * Set the name to be used for <amatch>.
2134 */
2135 autocmd_match = fname;
2136
2137
2138 // Don't redraw while doing autocommands.
2139 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002140
2141 // name and lnum are filled in later
2142 estack_push(ETYPE_AUCMD, NULL, 0);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002143 ESTACK_CHECK_SETUP
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002144
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002145 save_current_sctx = current_sctx;
2146
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002147#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002148# ifdef FEAT_PROFILE
2149 if (do_profiling == PROF_YES)
2150 prof_child_enter(&wait_time); // doesn't count for the caller itself
2151# endif
2152
2153 // Don't use local function variables, if called from a function.
2154 save_funccal(&funccal_entry);
2155#endif
2156
2157 /*
2158 * When starting to execute autocommands, save the search patterns.
2159 */
2160 if (!autocmd_busy)
2161 {
2162 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002163 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002164 {
2165 saveRedobuff(&save_redo);
2166 did_save_redobuff = TRUE;
2167 }
2168 did_filetype = keep_filetype;
2169 }
2170
2171 /*
2172 * Note that we are applying autocmds. Some commands need to know.
2173 */
2174 autocmd_busy = TRUE;
2175 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2176 ++nesting; // see matching decrement below
2177
2178 // Remember that FileType was triggered. Used for did_filetype().
2179 if (event == EVENT_FILETYPE)
2180 did_filetype = TRUE;
2181
2182 tail = gettail(fname);
2183
2184 // Find first autocommand that matches
LemonBoyeca7c602022-04-14 15:39:43 +01002185 CLEAR_FIELD(patcmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002186 patcmd.curpat = first_autopat[(int)event];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002187 patcmd.group = group;
2188 patcmd.fname = fname;
2189 patcmd.sfname = sfname;
2190 patcmd.tail = tail;
2191 patcmd.event = event;
2192 patcmd.arg_bufnr = autocmd_bufnr;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002193 auto_next_pat(&patcmd, FALSE);
2194
2195 // found one, start executing the autocommands
2196 if (patcmd.curpat != NULL)
2197 {
2198 // add to active_apc_list
2199 patcmd.next = active_apc_list;
2200 active_apc_list = &patcmd;
2201
2202#ifdef FEAT_EVAL
2203 // set v:cmdarg (only when there is a matching pattern)
2204 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2205 if (eap != NULL)
2206 {
2207 save_cmdarg = set_cmdarg(eap, NULL);
2208 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2209 }
2210 else
2211 save_cmdarg = NULL; // avoid gcc warning
2212#endif
2213 retval = TRUE;
2214 // mark the last pattern, to avoid an endless loop when more patterns
2215 // are added when executing autocommands
2216 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2217 ap->last = FALSE;
2218 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002219
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002220 // Make sure cursor and topline are valid. The first time the current
2221 // values are saved, restored by reset_lnums(). When nested only the
2222 // values are corrected when needed.
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002223 if (nesting == 1)
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002224 check_lnums(TRUE);
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002225 else
2226 check_lnums_nested(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002227
ichizokc3f91c02021-12-17 09:44:33 +00002228 save_did_emsg = did_emsg;
2229
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002230 do_cmdline(NULL, getnextac, (void *)&patcmd,
2231 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002232
ichizokc3f91c02021-12-17 09:44:33 +00002233 did_emsg += save_did_emsg;
2234
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002235 if (nesting == 1)
2236 // restore cursor and topline, unless they were changed
2237 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002238
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002239#ifdef FEAT_EVAL
2240 if (eap != NULL)
2241 {
2242 (void)set_cmdarg(NULL, save_cmdarg);
2243 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2244 }
2245#endif
2246 // delete from active_apc_list
2247 if (active_apc_list == &patcmd) // just in case
2248 active_apc_list = patcmd.next;
2249 }
2250
2251 --RedrawingDisabled;
2252 autocmd_busy = save_autocmd_busy;
2253 filechangeshell_busy = FALSE;
2254 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002255 vim_free(SOURCING_NAME);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002256 ESTACK_CHECK_NOW
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002257 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002258 vim_free(autocmd_fname);
2259 autocmd_fname = save_autocmd_fname;
2260 autocmd_fname_full = save_autocmd_fname_full;
2261 autocmd_bufnr = save_autocmd_bufnr;
2262 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002263 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002264#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002265 restore_funccal();
2266# ifdef FEAT_PROFILE
2267 if (do_profiling == PROF_YES)
2268 prof_child_exit(&wait_time);
2269# endif
2270#endif
2271 KeyTyped = save_KeyTyped;
2272 vim_free(fname);
2273 vim_free(sfname);
2274 --nesting; // see matching increment above
2275
2276 /*
2277 * When stopping to execute autocommands, restore the search patterns and
2278 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2279 * free any windows in the au_pending_free_win list.
2280 */
2281 if (!autocmd_busy)
2282 {
2283 restore_search_patterns();
2284 if (did_save_redobuff)
2285 restoreRedobuff(&save_redo);
2286 did_filetype = FALSE;
2287 while (au_pending_free_buf != NULL)
2288 {
2289 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002290
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002291 vim_free(au_pending_free_buf);
2292 au_pending_free_buf = b;
2293 }
2294 while (au_pending_free_win != NULL)
2295 {
2296 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002297
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002298 vim_free(au_pending_free_win);
2299 au_pending_free_win = w;
2300 }
2301 }
2302
2303 /*
2304 * Some events don't set or reset the Changed flag.
2305 * Check if still in the same buffer!
2306 */
2307 if (curbuf == old_curbuf
2308 && (event == EVENT_BUFREADPOST
2309 || event == EVENT_BUFWRITEPOST
2310 || event == EVENT_FILEAPPENDPOST
2311 || event == EVENT_VIMLEAVE
2312 || event == EVENT_VIMLEAVEPRE))
2313 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002314 if (curbuf->b_changed != save_changed)
2315 need_maketitle = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002316 curbuf->b_changed = save_changed;
2317 }
2318
2319 au_cleanup(); // may really delete removed patterns/commands now
2320
2321BYPASS_AU:
2322 // When wiping out a buffer make sure all its buffer-local autocommands
2323 // are deleted.
2324 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2325 aubuflocal_remove(buf);
2326
2327 if (retval == OK && event == EVENT_FILETYPE)
2328 au_did_filetype = TRUE;
2329
2330 return retval;
2331}
2332
2333# ifdef FEAT_EVAL
2334static char_u *old_termresponse = NULL;
2335# endif
2336
2337/*
2338 * Block triggering autocommands until unblock_autocmd() is called.
2339 * Can be used recursively, so long as it's symmetric.
2340 */
2341 void
2342block_autocmds(void)
2343{
2344# ifdef FEAT_EVAL
2345 // Remember the value of v:termresponse.
2346 if (autocmd_blocked == 0)
2347 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
2348# endif
2349 ++autocmd_blocked;
2350}
2351
2352 void
2353unblock_autocmds(void)
2354{
2355 --autocmd_blocked;
2356
2357# ifdef FEAT_EVAL
2358 // When v:termresponse was set while autocommands were blocked, trigger
2359 // the autocommands now. Esp. useful when executing a shell command
2360 // during startup (vimdiff).
2361 if (autocmd_blocked == 0
2362 && get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2363 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2364# endif
2365}
2366
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002367 int
2368is_autocmd_blocked(void)
2369{
2370 return autocmd_blocked != 0;
2371}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002372
2373/*
2374 * Find next autocommand pattern that matches.
2375 */
2376 static void
2377auto_next_pat(
LemonBoyeca7c602022-04-14 15:39:43 +01002378 AutoPatCmd_T *apc,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002379 int stop_at_last) // stop when 'last' flag is set
2380{
2381 AutoPat *ap;
2382 AutoCmd *cp;
2383 char_u *name;
2384 char *s;
LemonBoyeca7c602022-04-14 15:39:43 +01002385 estack_T *entry;
2386 char_u *namep;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002387
LemonBoyeca7c602022-04-14 15:39:43 +01002388 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
2389
2390 // Clear the exestack entry for this ETYPE_AUCMD entry.
2391 VIM_CLEAR(entry->es_name);
2392 entry->es_info.aucmd = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002393
2394 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2395 {
2396 apc->curpat = NULL;
2397
2398 // Only use a pattern when it has not been removed, has commands and
2399 // the group matches. For buffer-local autocommands only check the
2400 // buffer number.
2401 if (ap->pat != NULL && ap->cmds != NULL
2402 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2403 {
2404 // execution-condition
2405 if (ap->buflocal_nr == 0
2406 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2407 apc->sfname, apc->tail, ap->allow_dirs))
2408 : ap->buflocal_nr == apc->arg_bufnr)
2409 {
2410 name = event_nr2name(apc->event);
2411 s = _("%s Autocommands for \"%s\"");
LemonBoyeca7c602022-04-14 15:39:43 +01002412 namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1);
2413 if (namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002414 {
LemonBoyeca7c602022-04-14 15:39:43 +01002415 sprintf((char *)namep, s, (char *)name, (char *)ap->pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002416 if (p_verbose >= 8)
2417 {
2418 verbose_enter();
LemonBoyeca7c602022-04-14 15:39:43 +01002419 smsg(_("Executing %s"), namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002420 verbose_leave();
2421 }
2422 }
2423
LemonBoyeca7c602022-04-14 15:39:43 +01002424 // Update the exestack entry for this autocmd.
2425 entry->es_name = namep;
2426 entry->es_info.aucmd = apc;
2427
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002428 apc->curpat = ap;
2429 apc->nextcmd = ap->cmds;
2430 // mark last command
2431 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2432 cp->last = FALSE;
2433 cp->last = TRUE;
2434 }
2435 line_breakcheck();
2436 if (apc->curpat != NULL) // found a match
2437 break;
2438 }
2439 if (stop_at_last && ap->last)
2440 break;
2441 }
2442}
2443
2444/*
LemonBoyeca7c602022-04-14 15:39:43 +01002445 * Get the script context where autocommand "acp" is defined.
2446 */
2447 sctx_T *
2448acp_script_ctx(AutoPatCmd_T *acp)
2449{
2450 return &acp->script_ctx;
2451}
2452
2453/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002454 * Get next autocommand command.
2455 * Called by do_cmdline() to get the next line for ":if".
2456 * Returns allocated string, or NULL for end of autocommands.
2457 */
2458 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002459getnextac(
2460 int c UNUSED,
2461 void *cookie,
2462 int indent UNUSED,
2463 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002464{
LemonBoyeca7c602022-04-14 15:39:43 +01002465 AutoPatCmd_T *acp = (AutoPatCmd_T *)cookie;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002466 char_u *retval;
2467 AutoCmd *ac;
2468
2469 // Can be called again after returning the last line.
2470 if (acp->curpat == NULL)
2471 return NULL;
2472
2473 // repeat until we find an autocommand to execute
2474 for (;;)
2475 {
2476 // skip removed commands
2477 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2478 if (acp->nextcmd->last)
2479 acp->nextcmd = NULL;
2480 else
2481 acp->nextcmd = acp->nextcmd->next;
2482
2483 if (acp->nextcmd != NULL)
2484 break;
2485
2486 // at end of commands, find next pattern that matches
2487 if (acp->curpat->last)
2488 acp->curpat = NULL;
2489 else
2490 acp->curpat = acp->curpat->next;
2491 if (acp->curpat != NULL)
2492 auto_next_pat(acp, TRUE);
2493 if (acp->curpat == NULL)
2494 return NULL;
2495 }
2496
2497 ac = acp->nextcmd;
2498
2499 if (p_verbose >= 9)
2500 {
2501 verbose_enter_scroll();
2502 smsg(_("autocommand %s"), ac->cmd);
2503 msg_puts("\n"); // don't overwrite this either
2504 verbose_leave_scroll();
2505 }
2506 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002507 // Remove one-shot ("once") autocmd in anticipation of its execution.
2508 if (ac->once)
2509 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002510 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002511 current_sctx = ac->script_ctx;
LemonBoyeca7c602022-04-14 15:39:43 +01002512 acp->script_ctx = current_sctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002513 if (ac->last)
2514 acp->nextcmd = NULL;
2515 else
2516 acp->nextcmd = ac->next;
2517 return retval;
2518}
2519
2520/*
2521 * Return TRUE if there is a matching autocommand for "fname".
2522 * To account for buffer-local autocommands, function needs to know
2523 * in which buffer the file will be opened.
2524 */
2525 int
2526has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2527{
2528 AutoPat *ap;
2529 char_u *fname;
2530 char_u *tail = gettail(sfname);
2531 int retval = FALSE;
2532
2533 fname = FullName_save(sfname, FALSE);
2534 if (fname == NULL)
2535 return FALSE;
2536
2537#ifdef BACKSLASH_IN_FILENAME
2538 /*
2539 * Replace all backslashes with forward slashes. This makes the
2540 * autocommand patterns portable between Unix and MS-DOS.
2541 */
2542 sfname = vim_strsave(sfname);
2543 if (sfname != NULL)
2544 forward_slash(sfname);
2545 forward_slash(fname);
2546#endif
2547
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002548 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002549 if (ap->pat != NULL && ap->cmds != NULL
2550 && (ap->buflocal_nr == 0
2551 ? match_file_pat(NULL, &ap->reg_prog,
2552 fname, sfname, tail, ap->allow_dirs)
2553 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2554 ))
2555 {
2556 retval = TRUE;
2557 break;
2558 }
2559
2560 vim_free(fname);
2561#ifdef BACKSLASH_IN_FILENAME
2562 vim_free(sfname);
2563#endif
2564
2565 return retval;
2566}
2567
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002568/*
2569 * Function given to ExpandGeneric() to obtain the list of autocommand group
2570 * names.
2571 */
2572 char_u *
2573get_augroup_name(expand_T *xp UNUSED, int idx)
2574{
2575 if (idx == augroups.ga_len) // add "END" add the end
2576 return (char_u *)"END";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002577 if (idx < 0 || idx >= augroups.ga_len) // end of list
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002578 return NULL;
2579 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2580 // skip deleted entries
2581 return (char_u *)"";
2582 return AUGROUP_NAME(idx); // return a name
2583}
2584
2585static int include_groups = FALSE;
2586
2587 char_u *
2588set_context_in_autocmd(
2589 expand_T *xp,
2590 char_u *arg,
2591 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2592{
2593 char_u *p;
2594 int group;
2595
2596 // check for a group name, skip it if present
2597 include_groups = FALSE;
2598 p = arg;
2599 group = au_get_grouparg(&arg);
2600 if (group == AUGROUP_ERROR)
2601 return NULL;
2602 // If there only is a group name that's what we expand.
2603 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2604 {
2605 arg = p;
2606 group = AUGROUP_ALL;
2607 }
2608
2609 // skip over event name
2610 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2611 if (*p == ',')
2612 arg = p + 1;
2613 if (*p == NUL)
2614 {
2615 if (group == AUGROUP_ALL)
2616 include_groups = TRUE;
2617 xp->xp_context = EXPAND_EVENTS; // expand event name
2618 xp->xp_pattern = arg;
2619 return NULL;
2620 }
2621
2622 // skip over pattern
2623 arg = skipwhite(p);
2624 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2625 arg++;
2626 if (*arg)
2627 return arg; // expand (next) command
2628
2629 if (doautocmd)
2630 xp->xp_context = EXPAND_FILES; // expand file names
2631 else
2632 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2633 return NULL;
2634}
2635
2636/*
2637 * Function given to ExpandGeneric() to obtain the list of event names.
2638 */
2639 char_u *
2640get_event_name(expand_T *xp UNUSED, int idx)
2641{
2642 if (idx < augroups.ga_len) // First list group names, if wanted
2643 {
2644 if (!include_groups || AUGROUP_NAME(idx) == NULL
2645 || AUGROUP_NAME(idx) == get_deleted_augroup())
2646 return (char_u *)""; // skip deleted entries
2647 return AUGROUP_NAME(idx); // return a name
2648 }
2649 return (char_u *)event_names[idx - augroups.ga_len].name;
2650}
2651
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002652
2653#if defined(FEAT_EVAL) || defined(PROTO)
2654/*
2655 * Return TRUE if autocmd is supported.
2656 */
2657 int
2658autocmd_supported(char_u *name)
2659{
2660 char_u *p;
2661
2662 return (event_name2nr(name, &p) != NUM_EVENTS);
2663}
2664
2665/*
2666 * Return TRUE if an autocommand is defined for a group, event and
2667 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2668 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2669 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2670 * Used for:
2671 * exists("#Group") or
2672 * exists("#Group#Event") or
2673 * exists("#Group#Event#pat") or
2674 * exists("#Event") or
2675 * exists("#Event#pat")
2676 */
2677 int
2678au_exists(char_u *arg)
2679{
2680 char_u *arg_save;
2681 char_u *pattern = NULL;
2682 char_u *event_name;
2683 char_u *p;
2684 event_T event;
2685 AutoPat *ap;
2686 buf_T *buflocal_buf = NULL;
2687 int group;
2688 int retval = FALSE;
2689
2690 // Make a copy so that we can change the '#' chars to a NUL.
2691 arg_save = vim_strsave(arg);
2692 if (arg_save == NULL)
2693 return FALSE;
2694 p = vim_strchr(arg_save, '#');
2695 if (p != NULL)
2696 *p++ = NUL;
2697
2698 // First, look for an autocmd group name
2699 group = au_find_group(arg_save);
2700 if (group == AUGROUP_ERROR)
2701 {
2702 // Didn't match a group name, assume the first argument is an event.
2703 group = AUGROUP_ALL;
2704 event_name = arg_save;
2705 }
2706 else
2707 {
2708 if (p == NULL)
2709 {
2710 // "Group": group name is present and it's recognized
2711 retval = TRUE;
2712 goto theend;
2713 }
2714
2715 // Must be "Group#Event" or "Group#Event#pat".
2716 event_name = p;
2717 p = vim_strchr(event_name, '#');
2718 if (p != NULL)
2719 *p++ = NUL; // "Group#Event#pat"
2720 }
2721
2722 pattern = p; // "pattern" is NULL when there is no pattern
2723
2724 // find the index (enum) for the event name
2725 event = event_name2nr(event_name, &p);
2726
2727 // return FALSE if the event name is not recognized
2728 if (event == NUM_EVENTS)
2729 goto theend;
2730
2731 // Find the first autocommand for this event.
2732 // If there isn't any, return FALSE;
2733 // If there is one and no pattern given, return TRUE;
2734 ap = first_autopat[(int)event];
2735 if (ap == NULL)
2736 goto theend;
2737
2738 // if pattern is "<buffer>", special handling is needed which uses curbuf
2739 // for pattern "<buffer=N>, fnamecmp() will work fine
2740 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2741 buflocal_buf = curbuf;
2742
2743 // Check if there is an autocommand with the given pattern.
2744 for ( ; ap != NULL; ap = ap->next)
2745 // only use a pattern when it has not been removed and has commands.
2746 // For buffer-local autocommands, fnamecmp() works fine.
2747 if (ap->pat != NULL && ap->cmds != NULL
2748 && (group == AUGROUP_ALL || ap->group == group)
2749 && (pattern == NULL
2750 || (buflocal_buf == NULL
2751 ? fnamecmp(ap->pat, pattern) == 0
2752 : ap->buflocal_nr == buflocal_buf->b_fnum)))
2753 {
2754 retval = TRUE;
2755 break;
2756 }
2757
2758theend:
2759 vim_free(arg_save);
2760 return retval;
2761}
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002762
2763/*
2764 * autocmd_add() and autocmd_delete() functions
2765 */
2766 static void
2767autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
2768{
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002769 list_T *aucmd_list;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002770 listitem_T *li;
2771 dict_T *event_dict;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002772 dictitem_T *di;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002773 char_u *event_name = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002774 list_T *event_list;
2775 listitem_T *eli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002776 event_T event;
2777 char_u *group_name = NULL;
2778 int group;
2779 char_u *pat = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002780 list_T *pat_list;
2781 listitem_T *pli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002782 char_u *cmd = NULL;
2783 char_u *end;
2784 int once;
2785 int nested;
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01002786 int replace; // replace the cmd for a group/event
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002787 int retval = VVAL_TRUE;
2788 int save_augroup = current_augroup;
2789
2790 rettv->v_type = VAR_BOOL;
2791 rettv->vval.v_number = VVAL_FALSE;
2792
2793 if (check_for_list_arg(argvars, 0) == FAIL)
2794 return;
2795
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002796 aucmd_list = argvars[0].vval.v_list;
2797 if (aucmd_list == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002798 return;
2799
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002800 FOR_ALL_LIST_ITEMS(aucmd_list, li)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002801 {
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002802 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002803 VIM_CLEAR(cmd);
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002804 event_name = NULL;
2805 event_list = NULL;
2806 pat = NULL;
2807 pat_list = NULL;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002808
2809 if (li->li_tv.v_type != VAR_DICT)
2810 continue;
2811
2812 event_dict = li->li_tv.vval.v_dict;
2813 if (event_dict == NULL)
2814 continue;
2815
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002816 di = dict_find(event_dict, (char_u *)"event", -1);
2817 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002818 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002819 if (di->di_tv.v_type == VAR_STRING)
2820 {
2821 event_name = di->di_tv.vval.v_string;
2822 if (event_name == NULL)
2823 {
2824 emsg(_(e_string_required));
2825 continue;
2826 }
2827 }
2828 else if (di->di_tv.v_type == VAR_LIST)
2829 {
2830 event_list = di->di_tv.vval.v_list;
2831 if (event_list == NULL)
2832 {
2833 emsg(_(e_list_required));
2834 continue;
2835 }
2836 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002837 else
2838 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002839 emsg(_(e_string_or_list_expected));
2840 continue;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002841 }
2842 }
2843
Bram Moolenaard61efa52022-07-23 09:52:04 +01002844 group_name = dict_get_string(event_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002845 if (group_name == NULL || *group_name == NUL)
2846 // if the autocmd group name is not specified, then use the current
2847 // autocmd group
2848 group = current_augroup;
2849 else
2850 {
2851 group = au_find_group(group_name);
2852 if (group == AUGROUP_ERROR)
2853 {
2854 if (delete)
2855 {
2856 semsg(_(e_no_such_group_str), group_name);
2857 retval = VVAL_FALSE;
2858 break;
2859 }
2860 // group is not found, create it now
2861 group = au_new_group(group_name);
2862 if (group == AUGROUP_ERROR)
2863 {
2864 semsg(_(e_no_such_group_str), group_name);
2865 retval = VVAL_FALSE;
2866 break;
2867 }
2868
2869 current_augroup = group;
2870 }
2871 }
2872
2873 // if a buffer number is specified, then generate a pattern of the form
2874 // "<buffer=n>. Otherwise, use the pattern supplied by the user.
2875 if (dict_has_key(event_dict, "bufnr"))
2876 {
2877 varnumber_T bnum;
2878
Bram Moolenaard61efa52022-07-23 09:52:04 +01002879 bnum = dict_get_number_def(event_dict, "bufnr", -1);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002880 if (bnum == -1)
2881 continue;
2882
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002883 vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
2884 pat = IObuff;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002885 }
2886 else
2887 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002888 di = dict_find(event_dict, (char_u *)"pattern", -1);
2889 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002890 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002891 if (di->di_tv.v_type == VAR_STRING)
2892 {
2893 pat = di->di_tv.vval.v_string;
2894 if (pat == NULL)
2895 {
2896 emsg(_(e_string_required));
2897 continue;
2898 }
2899 }
2900 else if (di->di_tv.v_type == VAR_LIST)
2901 {
2902 pat_list = di->di_tv.vval.v_list;
2903 if (pat_list == NULL)
2904 {
2905 emsg(_(e_list_required));
2906 continue;
2907 }
2908 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002909 else
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002910 {
2911 emsg(_(e_string_or_list_expected));
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002912 continue;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002913 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002914 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002915 else if (delete)
2916 pat = (char_u *)"";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002917 }
2918
Bram Moolenaard61efa52022-07-23 09:52:04 +01002919 once = dict_get_bool(event_dict, "once", FALSE);
2920 nested = dict_get_bool(event_dict, "nested", FALSE);
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01002921 // if 'replace' is true, then remove all the commands associated with
2922 // this autocmd event/group and add the new command.
Bram Moolenaard61efa52022-07-23 09:52:04 +01002923 replace = dict_get_bool(event_dict, "replace", FALSE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002924
Bram Moolenaard61efa52022-07-23 09:52:04 +01002925 cmd = dict_get_string(event_dict, "cmd", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002926 if (cmd == NULL)
2927 {
2928 if (delete)
2929 cmd = vim_strsave((char_u *)"");
2930 else
2931 continue;
2932 }
2933
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002934 if (delete && (event_name == NULL
2935 || (event_name[0] == '*' && event_name[1] == NUL)))
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002936 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002937 // if the event name is not specified or '*', delete all the events
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002938 for (event = (event_T)0; (int)event < NUM_EVENTS;
2939 event = (event_T)((int)event + 1))
2940 {
2941 if (do_autocmd_event(event, pat, once, nested, cmd, delete,
2942 group, 0) == FAIL)
2943 {
2944 retval = VVAL_FALSE;
2945 break;
2946 }
2947 }
2948 }
2949 else
2950 {
Bram Moolenaar968443e2022-05-27 21:16:34 +01002951 char_u *p = NULL;
2952
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002953 eli = NULL;
2954 end = NULL;
2955 while (TRUE)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002956 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002957 if (event_list != NULL)
2958 {
2959 if (eli == NULL)
2960 eli = event_list->lv_first;
2961 else
2962 eli = eli->li_next;
2963 if (eli == NULL)
2964 break;
2965 if (eli->li_tv.v_type != VAR_STRING
Bram Moolenaar882476a2022-06-01 16:02:38 +01002966 || (p = eli->li_tv.vval.v_string) == NULL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002967 {
2968 emsg(_(e_string_required));
Bram Moolenaar882476a2022-06-01 16:02:38 +01002969 break;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002970 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002971 }
2972 else
2973 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01002974 if (p == NULL)
2975 p = event_name;
2976 if (p == NULL || *p == NUL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002977 break;
2978 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002979
2980 event = event_name2nr(p, &end);
2981 if (event == NUM_EVENTS || *end != NUL)
2982 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01002983 // this also catches something following a valid event name
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002984 semsg(_(e_no_such_event_str), p);
2985 retval = VVAL_FALSE;
2986 break;
2987 }
2988 if (pat != NULL)
2989 {
2990 if (do_autocmd_event(event, pat, once, nested, cmd,
2991 delete | replace, group, 0) == FAIL)
2992 {
2993 retval = VVAL_FALSE;
2994 break;
2995 }
2996 }
2997 else if (pat_list != NULL)
2998 {
2999 FOR_ALL_LIST_ITEMS(pat_list, pli)
3000 {
3001 if (pli->li_tv.v_type != VAR_STRING
3002 || pli->li_tv.vval.v_string == NULL)
3003 {
3004 emsg(_(e_string_required));
3005 continue;
3006 }
3007 if (do_autocmd_event(event,
3008 pli->li_tv.vval.v_string, once, nested,
3009 cmd, delete | replace, group, 0) ==
3010 FAIL)
3011 {
3012 retval = VVAL_FALSE;
3013 break;
3014 }
3015 }
3016 if (retval == VVAL_FALSE)
3017 break;
3018 }
3019 if (event_name != NULL)
3020 p = end;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003021 }
3022 }
3023
3024 // if only the autocmd group name is specified for delete and the
3025 // autocmd event, pattern and cmd are not specified, then delete the
3026 // autocmd group.
3027 if (delete && group_name != NULL &&
3028 (event_name == NULL || event_name[0] == NUL)
3029 && (pat == NULL || pat[0] == NUL)
3030 && (cmd == NULL || cmd[0] == NUL))
3031 au_del_group(group_name);
3032 }
3033
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003034 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003035 VIM_CLEAR(cmd);
3036
3037 current_augroup = save_augroup;
3038 rettv->vval.v_number = retval;
3039}
3040
3041/*
3042 * autocmd_add() function
3043 */
3044 void
3045f_autocmd_add(typval_T *argvars, typval_T *rettv)
3046{
3047 autocmd_add_or_delete(argvars, rettv, FALSE);
3048}
3049
3050/*
3051 * autocmd_delete() function
3052 */
3053 void
3054f_autocmd_delete(typval_T *argvars, typval_T *rettv)
3055{
3056 autocmd_add_or_delete(argvars, rettv, TRUE);
3057}
3058
3059/*
3060 * autocmd_get() function
3061 * Returns a List of autocmds.
3062 */
3063 void
3064f_autocmd_get(typval_T *argvars, typval_T *rettv)
3065{
3066 event_T event_arg = NUM_EVENTS;
3067 event_T event;
3068 AutoPat *ap;
3069 AutoCmd *ac;
3070 list_T *event_list;
3071 dict_T *event_dict;
3072 char_u *event_name = NULL;
3073 char_u *pat = NULL;
3074 char_u *name = NULL;
3075 int group = AUGROUP_ALL;
3076
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01003077 if (rettv_list_alloc(rettv) == FAIL)
3078 return;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003079 if (check_for_opt_dict_arg(argvars, 0) == FAIL)
3080 return;
3081
3082 if (argvars[0].v_type == VAR_DICT)
3083 {
3084 // return only the autocmds in the specified group
3085 if (dict_has_key(argvars[0].vval.v_dict, "group"))
3086 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003087 name = dict_get_string(argvars[0].vval.v_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003088 if (name == NULL)
3089 return;
3090
3091 if (*name == NUL)
3092 group = AUGROUP_DEFAULT;
3093 else
3094 {
3095 group = au_find_group(name);
3096 if (group == AUGROUP_ERROR)
3097 {
3098 semsg(_(e_no_such_group_str), name);
3099 vim_free(name);
3100 return;
3101 }
3102 }
3103 vim_free(name);
3104 }
3105
3106 // return only the autocmds for the specified event
3107 if (dict_has_key(argvars[0].vval.v_dict, "event"))
3108 {
3109 int i;
3110
Bram Moolenaard61efa52022-07-23 09:52:04 +01003111 name = dict_get_string(argvars[0].vval.v_dict, "event", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003112 if (name == NULL)
3113 return;
3114
3115 if (name[0] == '*' && name[1] == NUL)
3116 event_arg = NUM_EVENTS;
3117 else
3118 {
3119 for (i = 0; event_names[i].name != NULL; i++)
3120 if (STRICMP(event_names[i].name, name) == 0)
3121 break;
3122 if (event_names[i].name == NULL)
3123 {
3124 semsg(_(e_no_such_event_str), name);
3125 vim_free(name);
3126 return;
3127 }
3128 event_arg = event_names[i].event;
3129 }
3130 vim_free(name);
3131 }
3132
3133 // return only the autocmds for the specified pattern
3134 if (dict_has_key(argvars[0].vval.v_dict, "pattern"))
3135 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003136 pat = dict_get_string(argvars[0].vval.v_dict, "pattern", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003137 if (pat == NULL)
3138 return;
3139 }
3140 }
3141
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003142 event_list = rettv->vval.v_list;
3143
3144 // iterate through all the autocmd events
3145 for (event = (event_T)0; (int)event < NUM_EVENTS;
3146 event = (event_T)((int)event + 1))
3147 {
3148 if (event_arg != NUM_EVENTS && event != event_arg)
3149 continue;
3150
3151 event_name = event_nr2name(event);
3152
3153 // iterate through all the patterns for this autocmd event
3154 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
3155 {
3156 char_u *group_name;
3157
3158 if (group != AUGROUP_ALL && group != ap->group)
3159 continue;
3160
3161 if (pat != NULL && STRCMP(pat, ap->pat) != 0)
3162 continue;
3163
3164 group_name = get_augroup_name(NULL, ap->group);
3165
3166 // iterate through all the commands for this pattern and add one
3167 // item for each cmd.
3168 for (ac = ap->cmds; ac != NULL; ac = ac->next)
3169 {
3170 event_dict = dict_alloc();
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003171 if (event_dict == NULL
3172 || list_append_dict(event_list, event_dict) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003173 return;
3174
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003175 if (dict_add_string(event_dict, "event", event_name) == FAIL
3176 || dict_add_string(event_dict, "group",
3177 group_name == NULL ? (char_u *)""
3178 : group_name) == FAIL
3179 || (ap->buflocal_nr != 0
3180 && (dict_add_number(event_dict, "bufnr",
3181 ap->buflocal_nr) == FAIL))
3182 || dict_add_string(event_dict, "pattern",
3183 ap->pat) == FAIL
3184 || dict_add_string(event_dict, "cmd", ac->cmd) == FAIL
3185 || dict_add_bool(event_dict, "once", ac->once) == FAIL
3186 || dict_add_bool(event_dict, "nested",
3187 ac->nested) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003188 return;
3189 }
3190 }
3191 }
3192
3193 vim_free(pat);
3194}
3195
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01003196#endif