blob: cf0f0946a8116c6c17d6c81176eb3a2f962126d4 [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},
Shougo Matsushita4ccaedf2022-10-15 11:48:00 +0100185 {"TextChangedT", EVENT_TEXTCHANGEDT},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100186 {"User", EVENT_USER},
187 {"VimEnter", EVENT_VIMENTER},
188 {"VimLeave", EVENT_VIMLEAVE},
189 {"VimLeavePre", EVENT_VIMLEAVEPRE},
190 {"WinNew", EVENT_WINNEW},
naohiro ono23beefe2021-11-13 12:38:49 +0000191 {"WinClosed", EVENT_WINCLOSED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100192 {"WinEnter", EVENT_WINENTER},
193 {"WinLeave", EVENT_WINLEAVE},
LemonBoy09371822022-04-08 15:18:45 +0100194 {"WinScrolled", EVENT_WINSCROLLED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100195 {"VimResized", EVENT_VIMRESIZED},
196 {"TextYankPost", EVENT_TEXTYANKPOST},
Bram Moolenaar100118c2020-12-11 19:30:34 +0100197 {"VimSuspend", EVENT_VIMSUSPEND},
198 {"VimResume", EVENT_VIMRESUME},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100199 {NULL, (event_T)0}
200};
201
202static AutoPat *first_autopat[NUM_EVENTS] =
203{
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 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
210};
211
212static AutoPat *last_autopat[NUM_EVENTS] =
213{
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 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
220};
221
kylo252ae6f1d82022-02-16 19:24:07 +0000222#define AUGROUP_DEFAULT (-1) // default autocmd group
223#define AUGROUP_ERROR (-2) // erroneous autocmd group
224#define AUGROUP_ALL (-3) // all autocmd groups
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100225
226/*
227 * struct used to keep status while executing autocommands for an event.
228 */
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100229struct AutoPatCmd_S
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100230{
231 AutoPat *curpat; // next AutoPat to examine
232 AutoCmd *nextcmd; // next AutoCmd to execute
233 int group; // group being used
234 char_u *fname; // fname to match with
235 char_u *sfname; // sfname to match with
236 char_u *tail; // tail of fname
237 event_T event; // current event
LemonBoyeca7c602022-04-14 15:39:43 +0100238 sctx_T script_ctx; // script context where it is defined
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100239 int arg_bufnr; // Initially equal to <abuf>, set to zero when
240 // buf is deleted.
LemonBoyeca7c602022-04-14 15:39:43 +0100241 AutoPatCmd_T *next; // chain of active apc-s for auto-invalidation
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100242};
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100243
LemonBoyeca7c602022-04-14 15:39:43 +0100244static AutoPatCmd_T *active_apc_list = NULL; // stack of active autocommands
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100245
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200246// Macro to loop over all the patterns for an autocmd event
247#define FOR_ALL_AUTOCMD_PATTERNS(event, ap) \
248 for ((ap) = first_autopat[(int)(event)]; (ap) != NULL; (ap) = (ap)->next)
249
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100250/*
251 * augroups stores a list of autocmd group names.
252 */
253static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
254#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
Bram Moolenaarc667da52019-11-30 20:52:27 +0100255// use get_deleted_augroup() to get this
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100256static char_u *deleted_augroup = NULL;
257
258/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100259 * The ID of the current group. Group 0 is the default one.
260 */
261static int current_augroup = AUGROUP_DEFAULT;
262
Bram Moolenaarc667da52019-11-30 20:52:27 +0100263static int au_need_clean = FALSE; // need to delete marked patterns
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100264
265static char_u *event_nr2name(event_T event);
266static int au_get_grouparg(char_u **argp);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200267static 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 +0100268static 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 +0100269static void auto_next_pat(AutoPatCmd_T *apc, int stop_at_last);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100270static int au_find_group(char_u *name);
271
272static event_T last_event;
273static int last_group;
Bram Moolenaarc667da52019-11-30 20:52:27 +0100274static int autocmd_blocked = 0; // block all autocmds
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100275
276 static char_u *
277get_deleted_augroup(void)
278{
279 if (deleted_augroup == NULL)
280 deleted_augroup = (char_u *)_("--Deleted--");
281 return deleted_augroup;
282}
283
284/*
285 * Show the autocommands for one AutoPat.
286 */
287 static void
288show_autocmd(AutoPat *ap, event_T event)
289{
290 AutoCmd *ac;
291
292 // Check for "got_int" (here and at various places below), which is set
293 // when "q" has been hit for the "--more--" prompt
294 if (got_int)
295 return;
296 if (ap->pat == NULL) // pattern has been removed
297 return;
298
299 msg_putchar('\n');
300 if (got_int)
301 return;
302 if (event != last_event || ap->group != last_group)
303 {
304 if (ap->group != AUGROUP_DEFAULT)
305 {
306 if (AUGROUP_NAME(ap->group) == NULL)
307 msg_puts_attr((char *)get_deleted_augroup(), HL_ATTR(HLF_E));
308 else
309 msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
310 msg_puts(" ");
311 }
312 msg_puts_attr((char *)event_nr2name(event), HL_ATTR(HLF_T));
313 last_event = event;
314 last_group = ap->group;
315 msg_putchar('\n');
316 if (got_int)
317 return;
318 }
319 msg_col = 4;
320 msg_outtrans(ap->pat);
321
322 for (ac = ap->cmds; ac != NULL; ac = ac->next)
323 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100324 if (ac->cmd == NULL) // skip removed commands
325 continue;
326
327 if (msg_col >= 14)
328 msg_putchar('\n');
329 msg_col = 14;
330 if (got_int)
331 return;
332 msg_outtrans(ac->cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100333#ifdef FEAT_EVAL
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100334 if (p_verbose > 0)
335 last_set_msg(ac->script_ctx);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100336#endif
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100337 if (got_int)
338 return;
339 if (ac->next != NULL)
340 {
341 msg_putchar('\n');
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100342 if (got_int)
343 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100344 }
345 }
346}
347
348/*
349 * Mark an autocommand pattern for deletion.
350 */
351 static void
352au_remove_pat(AutoPat *ap)
353{
354 VIM_CLEAR(ap->pat);
355 ap->buflocal_nr = -1;
356 au_need_clean = TRUE;
357}
358
359/*
360 * Mark all commands for a pattern for deletion.
361 */
362 static void
363au_remove_cmds(AutoPat *ap)
364{
365 AutoCmd *ac;
366
367 for (ac = ap->cmds; ac != NULL; ac = ac->next)
368 VIM_CLEAR(ac->cmd);
369 au_need_clean = TRUE;
370}
371
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200372// Delete one command from an autocmd pattern.
373static void au_del_cmd(AutoCmd *ac)
374{
375 VIM_CLEAR(ac->cmd);
376 au_need_clean = TRUE;
377}
378
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100379/*
380 * Cleanup autocommands and patterns that have been deleted.
381 * This is only done when not executing autocommands.
382 */
383 static void
384au_cleanup(void)
385{
386 AutoPat *ap, **prev_ap;
387 AutoCmd *ac, **prev_ac;
388 event_T event;
389
390 if (autocmd_busy || !au_need_clean)
391 return;
392
393 // loop over all events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100394 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100395 event = (event_T)((int)event + 1))
396 {
397 // loop over all autocommand patterns
398 prev_ap = &(first_autopat[(int)event]);
399 for (ap = *prev_ap; ap != NULL; ap = *prev_ap)
400 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200401 int has_cmd = FALSE;
402
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200403 // loop over all commands for this pattern
404 prev_ac = &(ap->cmds);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100405 for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
406 {
407 // remove the command if the pattern is to be deleted or when
408 // the command has been marked for deletion
409 if (ap->pat == NULL || ac->cmd == NULL)
410 {
411 *prev_ac = ac->next;
412 vim_free(ac->cmd);
413 vim_free(ac);
414 }
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200415 else
416 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200417 has_cmd = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100418 prev_ac = &(ac->next);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200419 }
420 }
421
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200422 if (ap->pat != NULL && !has_cmd)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200423 // Pattern was not marked for deletion, but all of its
424 // commands were. So mark the pattern for deletion.
425 au_remove_pat(ap);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100426
427 // remove the pattern if it has been marked for deletion
428 if (ap->pat == NULL)
429 {
430 if (ap->next == NULL)
431 {
432 if (prev_ap == &(first_autopat[(int)event]))
433 last_autopat[(int)event] = NULL;
434 else
435 // this depends on the "next" field being the first in
436 // the struct
437 last_autopat[(int)event] = (AutoPat *)prev_ap;
438 }
439 *prev_ap = ap->next;
440 vim_regfree(ap->reg_prog);
441 vim_free(ap);
442 }
443 else
444 prev_ap = &(ap->next);
445 }
446 }
447
448 au_need_clean = FALSE;
449}
450
451/*
452 * Called when buffer is freed, to remove/invalidate related buffer-local
453 * autocmds.
454 */
455 void
456aubuflocal_remove(buf_T *buf)
457{
LemonBoyeca7c602022-04-14 15:39:43 +0100458 AutoPat *ap;
459 event_T event;
460 AutoPatCmd_T *apc;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100461
462 // invalidate currently executing autocommands
463 for (apc = active_apc_list; apc; apc = apc->next)
464 if (buf->b_fnum == apc->arg_bufnr)
465 apc->arg_bufnr = 0;
466
467 // invalidate buflocals looping through events
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100468 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100469 event = (event_T)((int)event + 1))
470 // loop over all autocommand patterns
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200471 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100472 if (ap->buflocal_nr == buf->b_fnum)
473 {
474 au_remove_pat(ap);
475 if (p_verbose >= 6)
476 {
477 verbose_enter();
478 smsg(_("auto-removing autocommand: %s <buffer=%d>"),
479 event_nr2name(event), buf->b_fnum);
480 verbose_leave();
481 }
482 }
483 au_cleanup();
484}
485
486/*
487 * Add an autocmd group name.
488 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
489 */
490 static int
491au_new_group(char_u *name)
492{
493 int i;
494
495 i = au_find_group(name);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100496 if (i != AUGROUP_ERROR)
497 return i;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100498
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100499 // the group doesn't exist yet, add it. First try using a free entry.
500 for (i = 0; i < augroups.ga_len; ++i)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100501 if (AUGROUP_NAME(i) == NULL)
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100502 break;
503 if (i == augroups.ga_len && ga_grow(&augroups, 1) == FAIL)
504 return AUGROUP_ERROR;
505
506 AUGROUP_NAME(i) = vim_strsave(name);
507 if (AUGROUP_NAME(i) == NULL)
508 return AUGROUP_ERROR;
509 if (i == augroups.ga_len)
510 ++augroups.ga_len;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100511
512 return i;
513}
514
515 static void
516au_del_group(char_u *name)
517{
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100518 int i;
519 event_T event;
520 AutoPat *ap;
521 int in_use = FALSE;
522
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100523
524 i = au_find_group(name);
525 if (i == AUGROUP_ERROR) // the group doesn't exist
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100526 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100527 semsg(_(e_no_such_group_str), name);
528 return;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100529 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100530 if (i == current_augroup)
531 {
532 emsg(_(e_cannot_delete_current_group));
533 return;
534 }
535
536 for (event = (event_T)0; (int)event < NUM_EVENTS;
537 event = (event_T)((int)event + 1))
538 {
539 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
540 if (ap->group == i && ap->pat != NULL)
541 {
542 give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
543 in_use = TRUE;
544 event = NUM_EVENTS;
545 break;
546 }
547 }
548 vim_free(AUGROUP_NAME(i));
549 if (in_use)
550 AUGROUP_NAME(i) = get_deleted_augroup();
551 else
552 AUGROUP_NAME(i) = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100553}
554
555/*
556 * Find the ID of an autocmd group name.
557 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
558 */
559 static int
560au_find_group(char_u *name)
561{
562 int i;
563
564 for (i = 0; i < augroups.ga_len; ++i)
565 if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
566 && STRCMP(AUGROUP_NAME(i), name) == 0)
567 return i;
568 return AUGROUP_ERROR;
569}
570
571/*
572 * Return TRUE if augroup "name" exists.
573 */
574 int
575au_has_group(char_u *name)
576{
577 return au_find_group(name) != AUGROUP_ERROR;
578}
579
580/*
581 * ":augroup {name}".
582 */
583 void
584do_augroup(char_u *arg, int del_group)
585{
586 int i;
587
588 if (del_group)
589 {
590 if (*arg == NUL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000591 emsg(_(e_argument_required));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100592 else
593 au_del_group(arg);
594 }
595 else if (STRICMP(arg, "end") == 0) // ":aug end": back to group 0
596 current_augroup = AUGROUP_DEFAULT;
597 else if (*arg) // ":aug xxx": switch to group xxx
598 {
599 i = au_new_group(arg);
600 if (i != AUGROUP_ERROR)
601 current_augroup = i;
602 }
603 else // ":aug": list the group names
604 {
605 msg_start();
606 for (i = 0; i < augroups.ga_len; ++i)
607 {
608 if (AUGROUP_NAME(i) != NULL)
609 {
610 msg_puts((char *)AUGROUP_NAME(i));
611 msg_puts(" ");
612 }
613 }
614 msg_clr_eos();
615 msg_end();
616 }
617}
618
619#if defined(EXITFREE) || defined(PROTO)
620 void
621free_all_autocmds(void)
622{
623 int i;
624 char_u *s;
625
626 for (current_augroup = -1; current_augroup < augroups.ga_len;
627 ++current_augroup)
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200628 do_autocmd(NULL, (char_u *)"", TRUE);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100629
630 for (i = 0; i < augroups.ga_len; ++i)
631 {
632 s = ((char_u **)(augroups.ga_data))[i];
633 if (s != get_deleted_augroup())
634 vim_free(s);
635 }
636 ga_clear(&augroups);
637}
638#endif
639
640/*
641 * Return the event number for event name "start".
642 * Return NUM_EVENTS if the event name was not found.
643 * Return a pointer to the next event name in "end".
644 */
645 static event_T
646event_name2nr(char_u *start, char_u **end)
647{
648 char_u *p;
649 int i;
650 int len;
651
652 // the event name ends with end of line, '|', a blank or a comma
653 for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
654 ;
655 for (i = 0; event_names[i].name != NULL; ++i)
656 {
657 len = (int)STRLEN(event_names[i].name);
658 if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0)
659 break;
660 }
661 if (*p == ',')
662 ++p;
663 *end = p;
664 if (event_names[i].name == NULL)
665 return NUM_EVENTS;
666 return event_names[i].event;
667}
668
669/*
670 * Return the name for event "event".
671 */
672 static char_u *
673event_nr2name(event_T event)
674{
675 int i;
676
677 for (i = 0; event_names[i].name != NULL; ++i)
678 if (event_names[i].event == event)
679 return (char_u *)event_names[i].name;
680 return (char_u *)"Unknown";
681}
682
683/*
684 * Scan over the events. "*" stands for all events.
685 */
686 static char_u *
687find_end_event(
688 char_u *arg,
689 int have_group) // TRUE when group name was found
690{
691 char_u *pat;
692 char_u *p;
693
694 if (*arg == '*')
695 {
696 if (arg[1] && !VIM_ISWHITE(arg[1]))
697 {
Bram Moolenaar6d057012021-12-31 18:49:43 +0000698 semsg(_(e_illegal_character_after_star_str), arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100699 return NULL;
700 }
701 pat = arg + 1;
702 }
703 else
704 {
705 for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p)
706 {
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100707 if ((int)event_name2nr(pat, &p) >= NUM_EVENTS)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100708 {
709 if (have_group)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000710 semsg(_(e_no_such_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100711 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000712 semsg(_(e_no_such_group_or_event_str), pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100713 return NULL;
714 }
715 }
716 }
717 return pat;
718}
719
720/*
721 * Return TRUE if "event" is included in 'eventignore'.
722 */
723 static int
724event_ignored(event_T event)
725{
726 char_u *p = p_ei;
727
728 while (*p != NUL)
729 {
730 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
731 return TRUE;
732 if (event_name2nr(p, &p) == event)
733 return TRUE;
734 }
735
736 return FALSE;
737}
738
739/*
740 * Return OK when the contents of p_ei is valid, FAIL otherwise.
741 */
742 int
743check_ei(void)
744{
745 char_u *p = p_ei;
746
747 while (*p)
748 {
749 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
750 {
751 p += 3;
752 if (*p == ',')
753 ++p;
754 }
755 else if (event_name2nr(p, &p) == NUM_EVENTS)
756 return FAIL;
757 }
758
759 return OK;
760}
761
762# if defined(FEAT_SYN_HL) || defined(PROTO)
763
764/*
765 * Add "what" to 'eventignore' to skip loading syntax highlighting for every
766 * buffer loaded into the window. "what" must start with a comma.
767 * Returns the old value of 'eventignore' in allocated memory.
768 */
769 char_u *
770au_event_disable(char *what)
771{
772 char_u *new_ei;
773 char_u *save_ei;
774
775 save_ei = vim_strsave(p_ei);
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100776 if (save_ei == NULL)
777 return NULL;
778
779 new_ei = vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what));
780 if (new_ei == NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100781 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100782 vim_free(save_ei);
783 return NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100784 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100785
786 if (*what == ',' && *p_ei == NUL)
787 STRCPY(new_ei, what + 1);
788 else
789 STRCAT(new_ei, what);
790 set_string_option_direct((char_u *)"ei", -1, new_ei,
791 OPT_FREE, SID_NONE);
792 vim_free(new_ei);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100793 return save_ei;
794}
795
796 void
797au_event_restore(char_u *old_ei)
798{
799 if (old_ei != NULL)
800 {
801 set_string_option_direct((char_u *)"ei", -1, old_ei,
802 OPT_FREE, SID_NONE);
803 vim_free(old_ei);
804 }
805}
Bram Moolenaarc667da52019-11-30 20:52:27 +0100806# endif // FEAT_SYN_HL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100807
808/*
809 * do_autocmd() -- implements the :autocmd command. Can be used in the
810 * following ways:
811 *
812 * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
813 * will be automatically executed for <event>
814 * when editing a file matching <pat>, in
815 * the current group.
816 * :autocmd <event> <pat> Show the autocommands associated with
817 * <event> and <pat>.
818 * :autocmd <event> Show the autocommands associated with
819 * <event>.
820 * :autocmd Show all autocommands.
821 * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
822 * <event> and <pat>, and add the command
823 * <cmd>, for the current group.
824 * :autocmd! <event> <pat> Remove all autocommands associated with
825 * <event> and <pat> for the current group.
826 * :autocmd! <event> Remove all autocommands associated with
827 * <event> for the current group.
828 * :autocmd! Remove ALL autocommands for the current
829 * group.
830 *
831 * Multiple events and patterns may be given separated by commas. Here are
832 * some examples:
833 * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
834 * :autocmd bufleave * set tw=79 nosmartindent ic infercase
835 *
836 * :autocmd * *.c show all autocommands for *.c files.
837 *
838 * Mostly a {group} argument can optionally appear before <event>.
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200839 * "eap" can be NULL.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100840 */
841 void
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200842do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100843{
844 char_u *arg = arg_in;
845 char_u *pat;
846 char_u *envpat = NULL;
847 char_u *cmd;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200848 int cmd_need_free = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100849 event_T event;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200850 char_u *tofree = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100851 int nested = FALSE;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200852 int once = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100853 int group;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200854 int i;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200855 int flags = 0;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100856
857 if (*arg == '|')
858 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000859 eap->nextcmd = arg + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100860 arg = (char_u *)"";
861 group = AUGROUP_ALL; // no argument, use all groups
862 }
863 else
864 {
865 /*
866 * Check for a legal group name. If not, use AUGROUP_ALL.
867 */
868 group = au_get_grouparg(&arg);
869 if (arg == NULL) // out of memory
870 return;
871 }
872
873 /*
874 * Scan over the events.
875 * If we find an illegal name, return here, don't do anything.
876 */
877 pat = find_end_event(arg, group != AUGROUP_ALL);
878 if (pat == NULL)
879 return;
880
881 pat = skipwhite(pat);
882 if (*pat == '|')
883 {
Bram Moolenaarb8e642f2021-11-20 10:38:25 +0000884 eap->nextcmd = pat + 1;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100885 pat = (char_u *)"";
886 cmd = (char_u *)"";
887 }
888 else
889 {
890 /*
891 * Scan over the pattern. Put a NUL at the end.
892 */
893 cmd = pat;
894 while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
895 cmd++;
896 if (*cmd)
897 *cmd++ = NUL;
898
899 // Expand environment variables in the pattern. Set 'shellslash', we
900 // want forward slashes here.
901 if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
902 {
903#ifdef BACKSLASH_IN_FILENAME
904 int p_ssl_save = p_ssl;
905
906 p_ssl = TRUE;
907#endif
908 envpat = expand_env_save(pat);
909#ifdef BACKSLASH_IN_FILENAME
910 p_ssl = p_ssl_save;
911#endif
912 if (envpat != NULL)
913 pat = envpat;
914 }
915
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100916 cmd = skipwhite(cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200917 for (i = 0; i < 2; i++)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100918 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100919 if (*cmd == NUL)
920 continue;
921
922 // Check for "++once" flag.
923 if (STRNCMP(cmd, "++once", 6) == 0 && VIM_ISWHITE(cmd[6]))
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200924 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100925 if (once)
926 semsg(_(e_duplicate_argument_str), "++once");
927 once = TRUE;
928 cmd = skipwhite(cmd + 6);
929 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200930
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100931 // Check for "++nested" flag.
932 if ((STRNCMP(cmd, "++nested", 8) == 0 && VIM_ISWHITE(cmd[8])))
933 {
934 if (nested)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200935 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100936 semsg(_(e_duplicate_argument_str), "++nested");
937 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200938 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100939 nested = TRUE;
940 cmd = skipwhite(cmd + 8);
941 }
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200942
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100943 // Check for the old "nested" flag in legacy script.
944 if (STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
945 {
946 if (in_vim9script())
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200947 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100948 // If there ever is a :nested command this error should
949 // be removed and "nested" accepted as the start of the
950 // command.
951 emsg(_(e_invalid_command_nested_did_you_mean_plusplus_nested));
952 return;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200953 }
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +0100954 if (nested)
955 {
956 semsg(_(e_duplicate_argument_str), "nested");
957 return;
958 }
959 nested = TRUE;
960 cmd = skipwhite(cmd + 6);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200961 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100962 }
963
964 /*
965 * Find the start of the commands.
966 * Expand <sfile> in it.
967 */
968 if (*cmd != NUL)
969 {
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200970 if (eap != NULL)
971 // Read a {} block if it follows.
972 cmd = may_get_cmd_block(eap, cmd, &tofree, &flags);
973
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100974 cmd = expand_sfile(cmd);
975 if (cmd == NULL) // some error
976 return;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +0200977 cmd_need_free = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100978 }
979 }
980
981 /*
982 * Print header when showing autocommands.
983 */
984 if (!forceit && *cmd == NUL)
985 // Highlight title
986 msg_puts_title(_("\n--- Autocommands ---"));
987
988 /*
989 * Loop over the events.
990 */
991 last_event = (event_T)-1; // for listing the event name
992 last_group = AUGROUP_ERROR; // for listing the group name
993 if (*arg == '*' || *arg == NUL || *arg == '|')
994 {
Bram Moolenaarb6db1462021-12-24 19:24:47 +0000995 if (*cmd != NUL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +0100996 emsg(_(e_cannot_define_autocommands_for_all_events));
997 else
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +0100998 for (event = (event_T)0; (int)event < NUM_EVENTS;
Bram Moolenaar9a046fd2021-01-28 13:47:59 +0100999 event = (event_T)((int)event + 1))
1000 if (do_autocmd_event(event, pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001001 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar9a046fd2021-01-28 13:47:59 +01001002 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001003 }
1004 else
1005 {
1006 while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
1007 if (do_autocmd_event(event_name2nr(arg, &arg), pat,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001008 once, nested, cmd, forceit, group, flags) == FAIL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001009 break;
1010 }
1011
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001012 if (cmd_need_free)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001013 vim_free(cmd);
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001014 vim_free(tofree);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001015 vim_free(envpat);
1016}
1017
1018/*
1019 * Find the group ID in a ":autocmd" or ":doautocmd" argument.
1020 * The "argp" argument is advanced to the following argument.
1021 *
1022 * Returns the group ID, AUGROUP_ERROR for error (out of memory).
1023 */
1024 static int
1025au_get_grouparg(char_u **argp)
1026{
1027 char_u *group_name;
1028 char_u *p;
1029 char_u *arg = *argp;
1030 int group = AUGROUP_ALL;
1031
1032 for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p)
1033 ;
1034 if (p > arg)
1035 {
Bram Moolenaardf44a272020-06-07 20:49:05 +02001036 group_name = vim_strnsave(arg, p - arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001037 if (group_name == NULL) // out of memory
1038 return AUGROUP_ERROR;
1039 group = au_find_group(group_name);
1040 if (group == AUGROUP_ERROR)
1041 group = AUGROUP_ALL; // no match, use all groups
1042 else
1043 *argp = skipwhite(p); // match, skip over group name
1044 vim_free(group_name);
1045 }
1046 return group;
1047}
1048
1049/*
1050 * do_autocmd() for one event.
1051 * If *pat == NUL do for all patterns.
1052 * If *cmd == NUL show entries.
1053 * If forceit == TRUE delete entries.
1054 * If group is not AUGROUP_ALL, only use this group.
1055 */
1056 static int
1057do_autocmd_event(
1058 event_T event,
1059 char_u *pat,
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001060 int once,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001061 int nested,
1062 char_u *cmd,
1063 int forceit,
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001064 int group,
1065 int flags)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001066{
1067 AutoPat *ap;
1068 AutoPat **prev_ap;
1069 AutoCmd *ac;
1070 AutoCmd **prev_ac;
1071 int brace_level;
1072 char_u *endpat;
1073 int findgroup;
1074 int allgroups;
1075 int patlen;
1076 int is_buflocal;
1077 int buflocal_nr;
Bram Moolenaarc667da52019-11-30 20:52:27 +01001078 char_u buflocal_pat[25]; // for "<buffer=X>"
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001079
1080 if (group == AUGROUP_ALL)
1081 findgroup = current_augroup;
1082 else
1083 findgroup = group;
1084 allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
1085
1086 /*
1087 * Show or delete all patterns for an event.
1088 */
1089 if (*pat == NUL)
1090 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001091 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001092 {
1093 if (forceit) // delete the AutoPat, if it's in the current group
1094 {
1095 if (ap->group == findgroup)
1096 au_remove_pat(ap);
1097 }
1098 else if (group == AUGROUP_ALL || ap->group == group)
1099 show_autocmd(ap, event);
1100 }
1101 }
1102
1103 /*
1104 * Loop through all the specified patterns.
1105 */
1106 for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
1107 {
1108 /*
1109 * Find end of the pattern.
1110 * Watch out for a comma in braces, like "*.\{obj,o\}".
1111 */
1112 brace_level = 0;
1113 for (endpat = pat; *endpat && (*endpat != ',' || brace_level
1114 || (endpat > pat && endpat[-1] == '\\')); ++endpat)
1115 {
1116 if (*endpat == '{')
1117 brace_level++;
1118 else if (*endpat == '}')
1119 brace_level--;
1120 }
1121 if (pat == endpat) // ignore single comma
1122 continue;
1123 patlen = (int)(endpat - pat);
1124
1125 /*
1126 * detect special <buflocal[=X]> buffer-local patterns
1127 */
1128 is_buflocal = FALSE;
1129 buflocal_nr = 0;
1130
1131 if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
1132 && pat[patlen - 1] == '>')
1133 {
1134 // "<buffer...>": Error will be printed only for addition.
1135 // printing and removing will proceed silently.
1136 is_buflocal = TRUE;
1137 if (patlen == 8)
1138 // "<buffer>"
1139 buflocal_nr = curbuf->b_fnum;
1140 else if (patlen > 9 && pat[7] == '=')
1141 {
1142 if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0)
1143 // "<buffer=abuf>"
1144 buflocal_nr = autocmd_bufnr;
1145 else if (skipdigits(pat + 8) == pat + patlen - 1)
1146 // "<buffer=123>"
1147 buflocal_nr = atoi((char *)pat + 8);
1148 }
1149 }
1150
1151 if (is_buflocal)
1152 {
1153 // normalize pat into standard "<buffer>#N" form
1154 sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
1155 pat = buflocal_pat; // can modify pat and patlen
1156 patlen = (int)STRLEN(buflocal_pat); // but not endpat
1157 }
1158
1159 /*
1160 * Find AutoPat entries with this pattern. When adding a command it
1161 * always goes at or after the last one, so start at the end.
1162 */
1163 if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL)
1164 prev_ap = &last_autopat[(int)event];
1165 else
1166 prev_ap = &first_autopat[(int)event];
1167 while ((ap = *prev_ap) != NULL)
1168 {
1169 if (ap->pat != NULL)
1170 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001171 /*
1172 * Accept a pattern when:
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001173 * - a group was specified and it's that group, or a group was
1174 * not specified and it's the current group, or a group was
1175 * not specified and we are listing
1176 * - the length of the pattern matches
1177 * - the pattern matches.
1178 * For <buffer[=X]>, this condition works because we normalize
1179 * all buffer-local patterns.
1180 */
1181 if ((allgroups || ap->group == findgroup)
1182 && ap->patlen == patlen
1183 && STRNCMP(pat, ap->pat, patlen) == 0)
1184 {
1185 /*
1186 * Remove existing autocommands.
1187 * If adding any new autocmd's for this AutoPat, don't
1188 * delete the pattern from the autopat list, append to
1189 * this list.
1190 */
1191 if (forceit)
1192 {
1193 if (*cmd != NUL && ap->next == NULL)
1194 {
1195 au_remove_cmds(ap);
1196 break;
1197 }
1198 au_remove_pat(ap);
1199 }
1200
1201 /*
1202 * Show autocmd's for this autopat, or buflocals <buffer=X>
1203 */
1204 else if (*cmd == NUL)
1205 show_autocmd(ap, event);
1206
1207 /*
1208 * Add autocmd to this autopat, if it's the last one.
1209 */
1210 else if (ap->next == NULL)
1211 break;
1212 }
1213 }
1214 prev_ap = &ap->next;
1215 }
1216
1217 /*
1218 * Add a new command.
1219 */
1220 if (*cmd != NUL)
1221 {
1222 /*
1223 * If the pattern we want to add a command to does appear at the
1224 * end of the list (or not is not in the list at all), add the
1225 * pattern at the end of the list.
1226 */
1227 if (ap == NULL)
1228 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001229 // refuse to add buffer-local ap if buffer number is invalid
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001230 if (is_buflocal && (buflocal_nr == 0
1231 || buflist_findnr(buflocal_nr) == NULL))
1232 {
Bram Moolenaarf1474d82021-12-31 19:59:55 +00001233 semsg(_(e_buffer_nr_invalid_buffer_number), buflocal_nr);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001234 return FAIL;
1235 }
1236
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001237 ap = ALLOC_ONE(AutoPat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001238 if (ap == NULL)
1239 return FAIL;
1240 ap->pat = vim_strnsave(pat, patlen);
1241 ap->patlen = patlen;
1242 if (ap->pat == NULL)
1243 {
1244 vim_free(ap);
1245 return FAIL;
1246 }
1247
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001248#ifdef FEAT_EVAL
1249 // need to initialize last_mode for the first ModeChanged
1250 // autocmd
1251 if (event == EVENT_MODECHANGED && !has_modechanged())
LemonBoy2bf52dd2022-04-09 18:17:34 +01001252 get_mode(last_mode);
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001253#endif
LemonBoy09371822022-04-08 15:18:45 +01001254 // Initialize the fields checked by the WinScrolled trigger to
1255 // stop it from firing right after the first autocmd is defined.
1256 if (event == EVENT_WINSCROLLED && !has_winscrolled())
1257 {
1258 curwin->w_last_topline = curwin->w_topline;
1259 curwin->w_last_leftcol = curwin->w_leftcol;
zeertzjq670ab032022-08-28 19:16:15 +01001260 curwin->w_last_skipcol = curwin->w_skipcol;
LemonBoy09371822022-04-08 15:18:45 +01001261 curwin->w_last_width = curwin->w_width;
1262 curwin->w_last_height = curwin->w_height;
1263 }
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001264
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001265 if (is_buflocal)
1266 {
1267 ap->buflocal_nr = buflocal_nr;
1268 ap->reg_prog = NULL;
1269 }
1270 else
1271 {
1272 char_u *reg_pat;
1273
1274 ap->buflocal_nr = 0;
1275 reg_pat = file_pat_to_reg_pat(pat, endpat,
1276 &ap->allow_dirs, TRUE);
1277 if (reg_pat != NULL)
1278 ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
1279 vim_free(reg_pat);
1280 if (reg_pat == NULL || ap->reg_prog == NULL)
1281 {
1282 vim_free(ap->pat);
1283 vim_free(ap);
1284 return FAIL;
1285 }
1286 }
1287 ap->cmds = NULL;
1288 *prev_ap = ap;
1289 last_autopat[(int)event] = ap;
1290 ap->next = NULL;
1291 if (group == AUGROUP_ALL)
1292 ap->group = current_augroup;
1293 else
1294 ap->group = group;
1295 }
1296
1297 /*
1298 * Add the autocmd at the end of the AutoCmd list.
1299 */
1300 prev_ac = &(ap->cmds);
1301 while ((ac = *prev_ac) != NULL)
1302 prev_ac = &ac->next;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001303 ac = ALLOC_ONE(AutoCmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001304 if (ac == NULL)
1305 return FAIL;
1306 ac->cmd = vim_strsave(cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001307 ac->script_ctx = current_sctx;
Bram Moolenaar73b8b0a2021-08-01 14:52:32 +02001308 if (flags & UC_VIM9)
1309 ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001310#ifdef FEAT_EVAL
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001311 ac->script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001312#endif
1313 if (ac->cmd == NULL)
1314 {
1315 vim_free(ac);
1316 return FAIL;
1317 }
1318 ac->next = NULL;
1319 *prev_ac = ac;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001320 ac->once = once;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001321 ac->nested = nested;
1322 }
1323 }
1324
1325 au_cleanup(); // may really delete removed patterns/commands now
1326 return OK;
1327}
1328
1329/*
1330 * Implementation of ":doautocmd [group] event [fname]".
1331 * Return OK for success, FAIL for failure;
1332 */
1333 int
1334do_doautocmd(
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001335 char_u *arg_start,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001336 int do_msg, // give message for no matching autocmds?
1337 int *did_something)
1338{
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001339 char_u *arg = arg_start;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001340 char_u *fname;
1341 int nothing_done = TRUE;
1342 int group;
1343
1344 if (did_something != NULL)
1345 *did_something = FALSE;
1346
1347 /*
1348 * Check for a legal group name. If not, use AUGROUP_ALL.
1349 */
1350 group = au_get_grouparg(&arg);
1351 if (arg == NULL) // out of memory
1352 return FAIL;
1353
1354 if (*arg == '*')
1355 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001356 emsg(_(e_cant_execute_autocommands_for_all_events));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001357 return FAIL;
1358 }
1359
1360 /*
1361 * Scan over the events.
1362 * If we find an illegal name, return here, don't do anything.
1363 */
1364 fname = find_end_event(arg, group != AUGROUP_ALL);
1365 if (fname == NULL)
1366 return FAIL;
1367
1368 fname = skipwhite(fname);
1369
1370 /*
1371 * Loop over the events.
1372 */
1373 while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
1374 if (apply_autocmds_group(event_name2nr(arg, &arg),
1375 fname, NULL, TRUE, group, curbuf, NULL))
1376 nothing_done = FALSE;
1377
Bram Moolenaar1b154ea2021-08-07 13:59:43 +02001378 if (nothing_done && do_msg
1379#ifdef FEAT_EVAL
1380 && !aborting()
1381#endif
1382 )
1383 smsg(_("No matching autocommands: %s"), arg_start);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001384 if (did_something != NULL)
1385 *did_something = !nothing_done;
1386
1387#ifdef FEAT_EVAL
1388 return aborting() ? FAIL : OK;
1389#else
1390 return OK;
1391#endif
1392}
1393
1394/*
1395 * ":doautoall": execute autocommands for each loaded buffer.
1396 */
1397 void
1398ex_doautoall(exarg_T *eap)
1399{
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001400 int retval = OK;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001401 aco_save_T aco;
1402 buf_T *buf;
1403 bufref_T bufref;
1404 char_u *arg = eap->arg;
1405 int call_do_modelines = check_nomodeline(&arg);
1406 int did_aucmd;
1407
1408 /*
1409 * This is a bit tricky: For some commands curwin->w_buffer needs to be
1410 * equal to curbuf, but for some buffers there may not be a window.
1411 * So we change the buffer for the current window for a moment. This
1412 * gives problems when the autocommands make changes to the list of
1413 * buffers or windows...
1414 */
1415 FOR_ALL_BUFFERS(buf)
1416 {
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001417 // Only do loaded buffers and skip the current buffer, it's done last.
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001418 if (buf->b_ml.ml_mfp == NULL || buf == curbuf)
1419 continue;
1420
1421 // find a window for this buffer and save some values
1422 aucmd_prepbuf(&aco, buf);
1423 set_bufref(&bufref, buf);
1424
1425 // execute the autocommands for this buffer
1426 retval = do_doautocmd(arg, FALSE, &did_aucmd);
1427
1428 if (call_do_modelines && did_aucmd)
1429 // Execute the modeline settings, but don't set window-local
1430 // options if we are using the current window for another
1431 // buffer.
1432 do_modelines(curwin == aucmd_win ? OPT_NOWIN : 0);
1433
1434 // restore the current window
1435 aucmd_restbuf(&aco);
1436
1437 // stop if there is some error or buffer was deleted
1438 if (retval == FAIL || !bufref_valid(&bufref))
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001439 {
Yegappan Lakshmanane9dcf132022-09-24 11:30:41 +01001440 retval = FAIL;
1441 break;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001442 }
1443 }
1444
Bram Moolenaar41cd8032021-03-13 15:47:56 +01001445 // Execute autocommands for the current buffer last.
1446 if (retval == OK)
1447 {
1448 do_doautocmd(arg, FALSE, &did_aucmd);
1449 if (call_do_modelines && did_aucmd)
1450 do_modelines(0);
1451 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001452}
1453
1454/*
1455 * Check *argp for <nomodeline>. When it is present return FALSE, otherwise
1456 * return TRUE and advance *argp to after it.
1457 * Thus return TRUE when do_modelines() should be called.
1458 */
1459 int
1460check_nomodeline(char_u **argp)
1461{
1462 if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
1463 {
1464 *argp = skipwhite(*argp + 12);
1465 return FALSE;
1466 }
1467 return TRUE;
1468}
1469
1470/*
1471 * Prepare for executing autocommands for (hidden) buffer "buf".
1472 * Search for a visible window containing the current buffer. If there isn't
1473 * one then use "aucmd_win".
1474 * Set "curbuf" and "curwin" to match "buf".
1475 */
1476 void
1477aucmd_prepbuf(
1478 aco_save_T *aco, // structure to save values in
1479 buf_T *buf) // new curbuf
1480{
1481 win_T *win;
1482 int save_ea;
1483#ifdef FEAT_AUTOCHDIR
1484 int save_acd;
1485#endif
1486
1487 // Find a window that is for the new buffer
1488 if (buf == curbuf) // be quick when buf is curbuf
1489 win = curwin;
1490 else
1491 FOR_ALL_WINDOWS(win)
1492 if (win->w_buffer == buf)
1493 break;
1494
1495 // Allocate "aucmd_win" when needed. If this fails (out of memory) fall
1496 // back to using the current window.
1497 if (win == NULL && aucmd_win == NULL)
1498 {
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001499 aucmd_win = win_alloc_popup_win();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001500 if (aucmd_win == NULL)
1501 win = curwin;
1502 }
1503 if (win == NULL && aucmd_win_used)
1504 // Strange recursive autocommand, fall back to using the current
1505 // window. Expect a few side effects...
1506 win = curwin;
1507
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001508 aco->save_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001509 aco->save_curbuf = curbuf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001510 aco->save_prevwin_id = prevwin == NULL ? 0 : prevwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001511 if (win != NULL)
1512 {
1513 // There is a window for "buf" in the current tab page, make it the
1514 // curwin. This is preferred, it has the least side effects (esp. if
1515 // "buf" is curbuf).
1516 aco->use_aucmd_win = FALSE;
1517 curwin = win;
1518 }
1519 else
1520 {
1521 // There is no window for "buf", use "aucmd_win". To minimize the side
1522 // effects, insert it in the current tab page.
1523 // Anything related to a window (e.g., setting folds) may have
1524 // unexpected results.
1525 aco->use_aucmd_win = TRUE;
1526 aucmd_win_used = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001527
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001528 win_init_popup_win(aucmd_win, buf);
1529
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001530 aco->globaldir = globaldir;
1531 globaldir = NULL;
1532
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001533 // Split the current window, put the aucmd_win in the upper half.
1534 // We don't want the BufEnter or WinEnter autocommands.
1535 block_autocmds();
1536 make_snapshot(SNAP_AUCMD_IDX);
1537 save_ea = p_ea;
1538 p_ea = FALSE;
1539
1540#ifdef FEAT_AUTOCHDIR
1541 // Prevent chdir() call in win_enter_ext(), through do_autochdir().
1542 save_acd = p_acd;
1543 p_acd = FALSE;
1544#endif
1545
Bram Moolenaardff97e62022-01-24 20:00:55 +00001546 // no redrawing and don't set the window title
1547 ++RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001548 (void)win_split_ins(0, WSP_TOP, aucmd_win, 0);
Bram Moolenaardff97e62022-01-24 20:00:55 +00001549 --RedrawingDisabled;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001550 (void)win_comp_pos(); // recompute window positions
1551 p_ea = save_ea;
1552#ifdef FEAT_AUTOCHDIR
1553 p_acd = save_acd;
1554#endif
1555 unblock_autocmds();
1556 curwin = aucmd_win;
1557 }
1558 curbuf = buf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001559 aco->new_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001560 set_bufref(&aco->new_curbuf, curbuf);
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001561
1562 // disable the Visual area, the position may be invalid in another buffer
1563 aco->save_VIsual_active = VIsual_active;
1564 VIsual_active = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001565}
1566
1567/*
1568 * Cleanup after executing autocommands for a (hidden) buffer.
1569 * Restore the window as it was (if possible).
1570 */
1571 void
1572aucmd_restbuf(
1573 aco_save_T *aco) // structure holding saved values
1574{
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001575 int dummy;
1576 win_T *save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001577
1578 if (aco->use_aucmd_win)
1579 {
1580 --curbuf->b_nwindows;
1581 // Find "aucmd_win", it can't be closed, but it may be in another tab
1582 // page. Do not trigger autocommands here.
1583 block_autocmds();
1584 if (curwin != aucmd_win)
1585 {
1586 tabpage_T *tp;
1587 win_T *wp;
1588
1589 FOR_ALL_TAB_WINDOWS(tp, wp)
1590 {
1591 if (wp == aucmd_win)
1592 {
1593 if (tp != curtab)
1594 goto_tabpage_tp(tp, TRUE, TRUE);
1595 win_goto(aucmd_win);
1596 goto win_found;
1597 }
1598 }
1599 }
1600win_found:
1601
1602 // Remove the window and frame from the tree of frames.
1603 (void)winframe_remove(curwin, &dummy, NULL);
1604 win_remove(curwin, NULL);
1605 aucmd_win_used = FALSE;
1606 last_status(FALSE); // may need to remove last status line
1607
1608 if (!valid_tabpage_win(curtab))
1609 // no valid window in current tabpage
1610 close_tabpage(curtab);
1611
1612 restore_snapshot(SNAP_AUCMD_IDX, FALSE);
1613 (void)win_comp_pos(); // recompute window positions
1614 unblock_autocmds();
1615
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001616 save_curwin = win_find_by_id(aco->save_curwin_id);
1617 if (save_curwin != NULL)
1618 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001619 else
1620 // Hmm, original window disappeared. Just use the first one.
1621 curwin = firstwin;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001622 curbuf = curwin->w_buffer;
1623#ifdef FEAT_JOB_CHANNEL
1624 // May need to restore insert mode for a prompt buffer.
1625 entering_window(curwin);
1626#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001627 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001628#ifdef FEAT_EVAL
1629 vars_clear(&aucmd_win->w_vars->dv_hashtab); // free all w: variables
1630 hash_init(&aucmd_win->w_vars->dv_hashtab); // re-use the hashtab
1631#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001632 vim_free(globaldir);
1633 globaldir = aco->globaldir;
1634
1635 // the buffer contents may have changed
1636 check_cursor();
1637 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1638 {
1639 curwin->w_topline = curbuf->b_ml.ml_line_count;
1640#ifdef FEAT_DIFF
1641 curwin->w_topfill = 0;
1642#endif
1643 }
1644#if defined(FEAT_GUI)
Bram Moolenaard2ff7052021-12-17 16:00:04 +00001645 if (gui.in_use)
1646 {
1647 // Hide the scrollbars from the aucmd_win and update.
1648 gui_mch_enable_scrollbar(
1649 &aucmd_win->w_scrollbars[SBAR_LEFT], FALSE);
1650 gui_mch_enable_scrollbar(
1651 &aucmd_win->w_scrollbars[SBAR_RIGHT], FALSE);
1652 gui_may_update_scrollbars();
1653 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001654#endif
1655 }
1656 else
1657 {
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001658 // Restore curwin. Use the window ID, a window may have been closed
1659 // and the memory re-used for another one.
1660 save_curwin = win_find_by_id(aco->save_curwin_id);
1661 if (save_curwin != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001662 {
1663 // Restore the buffer which was previously edited by curwin, if
1664 // it was changed, we are still the same window and the buffer is
1665 // valid.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001666 if (curwin->w_id == aco->new_curwin_id
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001667 && curbuf != aco->new_curbuf.br_buf
1668 && bufref_valid(&aco->new_curbuf)
1669 && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
1670 {
1671# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
1672 if (curwin->w_s == &curbuf->b_s)
1673 curwin->w_s = &aco->new_curbuf.br_buf->b_s;
1674# endif
1675 --curbuf->b_nwindows;
1676 curbuf = aco->new_curbuf.br_buf;
1677 curwin->w_buffer = curbuf;
1678 ++curbuf->b_nwindows;
1679 }
1680
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001681 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001682 curbuf = curwin->w_buffer;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001683 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001684 // In case the autocommand moves the cursor to a position that
1685 // does not exist in curbuf.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001686 check_cursor();
1687 }
1688 }
Bram Moolenaarcb1956d2022-01-07 15:45:18 +00001689
1690 check_cursor(); // just in case lines got deleted
1691 VIsual_active = aco->save_VIsual_active;
1692 if (VIsual_active)
1693 check_pos(curbuf, &VIsual);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001694}
1695
1696static int autocmd_nested = FALSE;
1697
1698/*
1699 * Execute autocommands for "event" and file name "fname".
1700 * Return TRUE if some commands were executed.
1701 */
1702 int
1703apply_autocmds(
1704 event_T event,
1705 char_u *fname, // NULL or empty means use actual file name
1706 char_u *fname_io, // fname to use for <afile> on cmdline
1707 int force, // when TRUE, ignore autocmd_busy
1708 buf_T *buf) // buffer for <abuf>
1709{
1710 return apply_autocmds_group(event, fname, fname_io, force,
1711 AUGROUP_ALL, buf, NULL);
1712}
1713
1714/*
1715 * Like apply_autocmds(), but with extra "eap" argument. This takes care of
1716 * setting v:filearg.
1717 */
1718 int
1719apply_autocmds_exarg(
1720 event_T event,
1721 char_u *fname,
1722 char_u *fname_io,
1723 int force,
1724 buf_T *buf,
1725 exarg_T *eap)
1726{
1727 return apply_autocmds_group(event, fname, fname_io, force,
1728 AUGROUP_ALL, buf, eap);
1729}
1730
1731/*
1732 * Like apply_autocmds(), but handles the caller's retval. If the script
1733 * processing is being aborted or if retval is FAIL when inside a try
1734 * conditional, no autocommands are executed. If otherwise the autocommands
1735 * cause the script to be aborted, retval is set to FAIL.
1736 */
1737 int
1738apply_autocmds_retval(
1739 event_T event,
1740 char_u *fname, // NULL or empty means use actual file name
1741 char_u *fname_io, // fname to use for <afile> on cmdline
1742 int force, // when TRUE, ignore autocmd_busy
1743 buf_T *buf, // buffer for <abuf>
1744 int *retval) // pointer to caller's retval
1745{
1746 int did_cmd;
1747
1748#ifdef FEAT_EVAL
1749 if (should_abort(*retval))
1750 return FALSE;
1751#endif
1752
1753 did_cmd = apply_autocmds_group(event, fname, fname_io, force,
1754 AUGROUP_ALL, buf, NULL);
1755 if (did_cmd
1756#ifdef FEAT_EVAL
1757 && aborting()
1758#endif
1759 )
1760 *retval = FAIL;
1761 return did_cmd;
1762}
1763
1764/*
1765 * Return TRUE when there is a CursorHold autocommand defined.
1766 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001767 static int
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001768has_cursorhold(void)
1769{
Bram Moolenaar24959102022-05-07 20:01:16 +01001770 return (first_autopat[(int)(get_real_state() == MODE_NORMAL_BUSY
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001771 ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
1772}
1773
1774/*
1775 * Return TRUE if the CursorHold event can be triggered.
1776 */
1777 int
1778trigger_cursorhold(void)
1779{
1780 int state;
1781
1782 if (!did_cursorhold
1783 && has_cursorhold()
1784 && reg_recording == 0
1785 && typebuf.tb_len == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001786 && !ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001787 {
1788 state = get_real_state();
Bram Moolenaar24959102022-05-07 20:01:16 +01001789 if (state == MODE_NORMAL_BUSY || (state & MODE_INSERT) != 0)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001790 return TRUE;
1791 }
1792 return FALSE;
1793}
1794
1795/*
LemonBoy09371822022-04-08 15:18:45 +01001796 * Return TRUE when there is a WinScrolled autocommand defined.
1797 */
1798 int
1799has_winscrolled(void)
1800{
1801 return (first_autopat[(int)EVENT_WINSCROLLED] != NULL);
1802}
1803
1804/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001805 * Return TRUE when there is a CursorMoved autocommand defined.
1806 */
1807 int
1808has_cursormoved(void)
1809{
1810 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1811}
1812
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001813/*
1814 * Return TRUE when there is a CursorMovedI autocommand defined.
1815 */
1816 int
1817has_cursormovedI(void)
1818{
1819 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1820}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001821
1822/*
1823 * Return TRUE when there is a TextChanged autocommand defined.
1824 */
1825 int
1826has_textchanged(void)
1827{
1828 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1829}
1830
1831/*
1832 * Return TRUE when there is a TextChangedI autocommand defined.
1833 */
1834 int
1835has_textchangedI(void)
1836{
1837 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
1838}
1839
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001840/*
1841 * Return TRUE when there is a TextChangedP autocommand defined.
1842 */
1843 int
1844has_textchangedP(void)
1845{
1846 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
1847}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001848
1849/*
1850 * Return TRUE when there is an InsertCharPre autocommand defined.
1851 */
1852 int
1853has_insertcharpre(void)
1854{
1855 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
1856}
1857
1858/*
1859 * Return TRUE when there is an CmdUndefined autocommand defined.
1860 */
1861 int
1862has_cmdundefined(void)
1863{
1864 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
1865}
1866
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001867#if defined(FEAT_EVAL) || defined(PROTO)
1868/*
1869 * Return TRUE when there is a TextYankPost autocommand defined.
1870 */
1871 int
1872has_textyankpost(void)
1873{
1874 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
1875}
1876#endif
1877
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001878#if defined(FEAT_EVAL) || defined(PROTO)
1879/*
1880 * Return TRUE when there is a CompleteChanged autocommand defined.
1881 */
1882 int
1883has_completechanged(void)
1884{
1885 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
1886}
1887#endif
1888
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02001889#if defined(FEAT_EVAL) || defined(PROTO)
1890/*
1891 * Return TRUE when there is a ModeChanged autocommand defined.
1892 */
1893 int
1894has_modechanged(void)
1895{
1896 return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
1897}
1898#endif
1899
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001900/*
1901 * Execute autocommands for "event" and file name "fname".
1902 * Return TRUE if some commands were executed.
1903 */
1904 static int
1905apply_autocmds_group(
1906 event_T event,
1907 char_u *fname, // NULL or empty means use actual file name
1908 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
1909 // use fname
1910 int force, // when TRUE, ignore autocmd_busy
1911 int group, // group ID, or AUGROUP_ALL
1912 buf_T *buf, // buffer for <abuf>
1913 exarg_T *eap UNUSED) // command arguments
1914{
1915 char_u *sfname = NULL; // short file name
1916 char_u *tail;
1917 int save_changed;
1918 buf_T *old_curbuf;
1919 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001920 char_u *save_autocmd_fname;
1921 int save_autocmd_fname_full;
1922 int save_autocmd_bufnr;
1923 char_u *save_autocmd_match;
1924 int save_autocmd_busy;
1925 int save_autocmd_nested;
1926 static int nesting = 0;
LemonBoyeca7c602022-04-14 15:39:43 +01001927 AutoPatCmd_T patcmd;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001928 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001929 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001930#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001931 funccal_entry_T funccal_entry;
1932 char_u *save_cmdarg;
1933 long save_cmdbang;
1934#endif
1935 static int filechangeshell_busy = FALSE;
1936#ifdef FEAT_PROFILE
1937 proftime_T wait_time;
1938#endif
1939 int did_save_redobuff = FALSE;
1940 save_redo_T save_redo;
1941 int save_KeyTyped = KeyTyped;
ichizokc3f91c02021-12-17 09:44:33 +00001942 int save_did_emsg;
Bram Moolenaare31ee862020-01-07 20:59:34 +01001943 ESTACK_CHECK_DECLARATION
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001944
1945 /*
1946 * Quickly return if there are no autocommands for this event or
1947 * autocommands are blocked.
1948 */
1949 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
1950 || autocmd_blocked > 0)
1951 goto BYPASS_AU;
1952
1953 /*
1954 * When autocommands are busy, new autocommands are only executed when
1955 * explicitly enabled with the "nested" flag.
1956 */
1957 if (autocmd_busy && !(force || autocmd_nested))
1958 goto BYPASS_AU;
1959
1960#ifdef FEAT_EVAL
1961 /*
1962 * Quickly return when immediately aborting on error, or when an interrupt
1963 * occurred or an exception was thrown but not caught.
1964 */
1965 if (aborting())
1966 goto BYPASS_AU;
1967#endif
1968
1969 /*
1970 * FileChangedShell never nests, because it can create an endless loop.
1971 */
1972 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
1973 || event == EVENT_FILECHANGEDSHELLPOST))
1974 goto BYPASS_AU;
1975
1976 /*
1977 * Ignore events in 'eventignore'.
1978 */
1979 if (event_ignored(event))
1980 goto BYPASS_AU;
1981
1982 /*
1983 * Allow nesting of autocommands, but restrict the depth, because it's
1984 * possible to create an endless loop.
1985 */
1986 if (nesting == 10)
1987 {
Bram Moolenaar6d057012021-12-31 18:49:43 +00001988 emsg(_(e_autocommand_nesting_too_deep));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001989 goto BYPASS_AU;
1990 }
1991
1992 /*
1993 * Check if these autocommands are disabled. Used when doing ":all" or
1994 * ":ball".
1995 */
1996 if ( (autocmd_no_enter
1997 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
1998 || (autocmd_no_leave
1999 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
2000 goto BYPASS_AU;
2001
2002 /*
2003 * Save the autocmd_* variables and info about the current buffer.
2004 */
2005 save_autocmd_fname = autocmd_fname;
2006 save_autocmd_fname_full = autocmd_fname_full;
2007 save_autocmd_bufnr = autocmd_bufnr;
2008 save_autocmd_match = autocmd_match;
2009 save_autocmd_busy = autocmd_busy;
2010 save_autocmd_nested = autocmd_nested;
2011 save_changed = curbuf->b_changed;
2012 old_curbuf = curbuf;
2013
2014 /*
2015 * Set the file name to be used for <afile>.
2016 * Make a copy to avoid that changing a buffer name or directory makes it
2017 * invalid.
2018 */
2019 if (fname_io == NULL)
2020 {
2021 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002022 || event == EVENT_OPTIONSET
2023 || event == EVENT_MODECHANGED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002024 autocmd_fname = NULL;
2025 else if (fname != NULL && !ends_excmd(*fname))
2026 autocmd_fname = fname;
2027 else if (buf != NULL)
2028 autocmd_fname = buf->b_ffname;
2029 else
2030 autocmd_fname = NULL;
2031 }
2032 else
2033 autocmd_fname = fname_io;
2034 if (autocmd_fname != NULL)
2035 autocmd_fname = vim_strsave(autocmd_fname);
2036 autocmd_fname_full = FALSE; // call FullName_save() later
2037
2038 /*
2039 * Set the buffer number to be used for <abuf>.
2040 */
2041 if (buf == NULL)
2042 autocmd_bufnr = 0;
2043 else
2044 autocmd_bufnr = buf->b_fnum;
2045
2046 /*
2047 * When the file name is NULL or empty, use the file name of buffer "buf".
2048 * Always use the full path of the file name to match with, in case
2049 * "allow_dirs" is set.
2050 */
2051 if (fname == NULL || *fname == NUL)
2052 {
2053 if (buf == NULL)
2054 fname = NULL;
2055 else
2056 {
2057#ifdef FEAT_SYN_HL
2058 if (event == EVENT_SYNTAX)
2059 fname = buf->b_p_syn;
2060 else
2061#endif
2062 if (event == EVENT_FILETYPE)
2063 fname = buf->b_p_ft;
2064 else
2065 {
2066 if (buf->b_sfname != NULL)
2067 sfname = vim_strsave(buf->b_sfname);
2068 fname = buf->b_ffname;
2069 }
2070 }
2071 if (fname == NULL)
2072 fname = (char_u *)"";
2073 fname = vim_strsave(fname); // make a copy, so we can change it
2074 }
2075 else
2076 {
2077 sfname = vim_strsave(fname);
2078 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002079 // ColorScheme, QuickFixCmd*, DirChanged and similar.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002080 if (event == EVENT_FILETYPE
2081 || event == EVENT_SYNTAX
2082 || event == EVENT_CMDLINECHANGED
2083 || event == EVENT_CMDLINEENTER
2084 || event == EVENT_CMDLINELEAVE
2085 || event == EVENT_CMDWINENTER
2086 || event == EVENT_CMDWINLEAVE
2087 || event == EVENT_CMDUNDEFINED
2088 || event == EVENT_FUNCUNDEFINED
2089 || event == EVENT_REMOTEREPLY
2090 || event == EVENT_SPELLFILEMISSING
2091 || event == EVENT_QUICKFIXCMDPRE
2092 || event == EVENT_COLORSCHEME
2093 || event == EVENT_COLORSCHEMEPRE
2094 || event == EVENT_OPTIONSET
2095 || event == EVENT_QUICKFIXCMDPOST
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02002096 || event == EVENT_DIRCHANGED
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002097 || event == EVENT_DIRCHANGEDPRE
naohiro ono23beefe2021-11-13 12:38:49 +00002098 || event == EVENT_MODECHANGED
zeertzjqc601d982022-10-10 13:46:15 +01002099 || event == EVENT_MENUPOPUP
Bram Moolenaarf6246f52022-02-11 16:30:12 +00002100 || event == EVENT_USER
LemonBoy09371822022-04-08 15:18:45 +01002101 || event == EVENT_WINCLOSED
2102 || event == EVENT_WINSCROLLED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002103 {
2104 fname = vim_strsave(fname);
2105 autocmd_fname_full = TRUE; // don't expand it later
2106 }
2107 else
2108 fname = FullName_save(fname, FALSE);
2109 }
2110 if (fname == NULL) // out of memory
2111 {
2112 vim_free(sfname);
2113 retval = FALSE;
2114 goto BYPASS_AU;
2115 }
2116
2117#ifdef BACKSLASH_IN_FILENAME
2118 /*
2119 * Replace all backslashes with forward slashes. This makes the
2120 * autocommand patterns portable between Unix and MS-DOS.
2121 */
2122 if (sfname != NULL)
2123 forward_slash(sfname);
2124 forward_slash(fname);
2125#endif
2126
2127#ifdef VMS
2128 // remove version for correct match
2129 if (sfname != NULL)
2130 vms_remove_version(sfname);
2131 vms_remove_version(fname);
2132#endif
2133
2134 /*
2135 * Set the name to be used for <amatch>.
2136 */
2137 autocmd_match = fname;
2138
2139
2140 // Don't redraw while doing autocommands.
2141 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002142
2143 // name and lnum are filled in later
2144 estack_push(ETYPE_AUCMD, NULL, 0);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002145 ESTACK_CHECK_SETUP
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002146
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002147 save_current_sctx = current_sctx;
2148
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002149#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002150# ifdef FEAT_PROFILE
2151 if (do_profiling == PROF_YES)
2152 prof_child_enter(&wait_time); // doesn't count for the caller itself
2153# endif
2154
2155 // Don't use local function variables, if called from a function.
2156 save_funccal(&funccal_entry);
2157#endif
2158
2159 /*
2160 * When starting to execute autocommands, save the search patterns.
2161 */
2162 if (!autocmd_busy)
2163 {
2164 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002165 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002166 {
2167 saveRedobuff(&save_redo);
2168 did_save_redobuff = TRUE;
2169 }
2170 did_filetype = keep_filetype;
2171 }
2172
2173 /*
2174 * Note that we are applying autocmds. Some commands need to know.
2175 */
2176 autocmd_busy = TRUE;
2177 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2178 ++nesting; // see matching decrement below
2179
2180 // Remember that FileType was triggered. Used for did_filetype().
2181 if (event == EVENT_FILETYPE)
2182 did_filetype = TRUE;
2183
2184 tail = gettail(fname);
2185
2186 // Find first autocommand that matches
LemonBoyeca7c602022-04-14 15:39:43 +01002187 CLEAR_FIELD(patcmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002188 patcmd.curpat = first_autopat[(int)event];
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002189 patcmd.group = group;
2190 patcmd.fname = fname;
2191 patcmd.sfname = sfname;
2192 patcmd.tail = tail;
2193 patcmd.event = event;
2194 patcmd.arg_bufnr = autocmd_bufnr;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002195 auto_next_pat(&patcmd, FALSE);
2196
2197 // found one, start executing the autocommands
2198 if (patcmd.curpat != NULL)
2199 {
2200 // add to active_apc_list
2201 patcmd.next = active_apc_list;
2202 active_apc_list = &patcmd;
2203
2204#ifdef FEAT_EVAL
2205 // set v:cmdarg (only when there is a matching pattern)
2206 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2207 if (eap != NULL)
2208 {
2209 save_cmdarg = set_cmdarg(eap, NULL);
2210 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2211 }
2212 else
2213 save_cmdarg = NULL; // avoid gcc warning
2214#endif
2215 retval = TRUE;
2216 // mark the last pattern, to avoid an endless loop when more patterns
2217 // are added when executing autocommands
2218 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2219 ap->last = FALSE;
2220 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002221
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002222 // Make sure cursor and topline are valid. The first time the current
2223 // values are saved, restored by reset_lnums(). When nested only the
2224 // values are corrected when needed.
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002225 if (nesting == 1)
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002226 check_lnums(TRUE);
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01002227 else
2228 check_lnums_nested(TRUE);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002229
ichizokc3f91c02021-12-17 09:44:33 +00002230 save_did_emsg = did_emsg;
2231
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002232 do_cmdline(NULL, getnextac, (void *)&patcmd,
2233 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002234
ichizokc3f91c02021-12-17 09:44:33 +00002235 did_emsg += save_did_emsg;
2236
Bram Moolenaar1e6bbfb2021-04-03 13:19:26 +02002237 if (nesting == 1)
2238 // restore cursor and topline, unless they were changed
2239 reset_lnums();
Bram Moolenaara68e5952019-04-25 22:22:01 +02002240
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002241#ifdef FEAT_EVAL
2242 if (eap != NULL)
2243 {
2244 (void)set_cmdarg(NULL, save_cmdarg);
2245 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2246 }
2247#endif
2248 // delete from active_apc_list
2249 if (active_apc_list == &patcmd) // just in case
2250 active_apc_list = patcmd.next;
2251 }
2252
2253 --RedrawingDisabled;
2254 autocmd_busy = save_autocmd_busy;
2255 filechangeshell_busy = FALSE;
2256 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002257 vim_free(SOURCING_NAME);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002258 ESTACK_CHECK_NOW
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002259 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002260 vim_free(autocmd_fname);
2261 autocmd_fname = save_autocmd_fname;
2262 autocmd_fname_full = save_autocmd_fname_full;
2263 autocmd_bufnr = save_autocmd_bufnr;
2264 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002265 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002266#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002267 restore_funccal();
2268# ifdef FEAT_PROFILE
2269 if (do_profiling == PROF_YES)
2270 prof_child_exit(&wait_time);
2271# endif
2272#endif
2273 KeyTyped = save_KeyTyped;
2274 vim_free(fname);
2275 vim_free(sfname);
2276 --nesting; // see matching increment above
2277
2278 /*
2279 * When stopping to execute autocommands, restore the search patterns and
2280 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2281 * free any windows in the au_pending_free_win list.
2282 */
2283 if (!autocmd_busy)
2284 {
2285 restore_search_patterns();
2286 if (did_save_redobuff)
2287 restoreRedobuff(&save_redo);
2288 did_filetype = FALSE;
2289 while (au_pending_free_buf != NULL)
2290 {
2291 buf_T *b = au_pending_free_buf->b_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002292
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002293 vim_free(au_pending_free_buf);
2294 au_pending_free_buf = b;
2295 }
2296 while (au_pending_free_win != NULL)
2297 {
2298 win_T *w = au_pending_free_win->w_next;
Bram Moolenaar41cd8032021-03-13 15:47:56 +01002299
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002300 vim_free(au_pending_free_win);
2301 au_pending_free_win = w;
2302 }
2303 }
2304
2305 /*
2306 * Some events don't set or reset the Changed flag.
2307 * Check if still in the same buffer!
2308 */
2309 if (curbuf == old_curbuf
2310 && (event == EVENT_BUFREADPOST
2311 || event == EVENT_BUFWRITEPOST
2312 || event == EVENT_FILEAPPENDPOST
2313 || event == EVENT_VIMLEAVE
2314 || event == EVENT_VIMLEAVEPRE))
2315 {
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002316 if (curbuf->b_changed != save_changed)
2317 need_maketitle = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002318 curbuf->b_changed = save_changed;
2319 }
2320
2321 au_cleanup(); // may really delete removed patterns/commands now
2322
2323BYPASS_AU:
2324 // When wiping out a buffer make sure all its buffer-local autocommands
2325 // are deleted.
2326 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2327 aubuflocal_remove(buf);
2328
2329 if (retval == OK && event == EVENT_FILETYPE)
2330 au_did_filetype = TRUE;
2331
2332 return retval;
2333}
2334
2335# ifdef FEAT_EVAL
2336static char_u *old_termresponse = NULL;
2337# endif
2338
2339/*
2340 * Block triggering autocommands until unblock_autocmd() is called.
2341 * Can be used recursively, so long as it's symmetric.
2342 */
2343 void
2344block_autocmds(void)
2345{
2346# ifdef FEAT_EVAL
2347 // Remember the value of v:termresponse.
2348 if (autocmd_blocked == 0)
2349 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
2350# endif
2351 ++autocmd_blocked;
2352}
2353
2354 void
2355unblock_autocmds(void)
2356{
2357 --autocmd_blocked;
2358
2359# ifdef FEAT_EVAL
2360 // When v:termresponse was set while autocommands were blocked, trigger
2361 // the autocommands now. Esp. useful when executing a shell command
2362 // during startup (vimdiff).
2363 if (autocmd_blocked == 0
2364 && get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2365 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2366# endif
2367}
2368
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002369 int
2370is_autocmd_blocked(void)
2371{
2372 return autocmd_blocked != 0;
2373}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002374
2375/*
2376 * Find next autocommand pattern that matches.
2377 */
2378 static void
2379auto_next_pat(
LemonBoyeca7c602022-04-14 15:39:43 +01002380 AutoPatCmd_T *apc,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002381 int stop_at_last) // stop when 'last' flag is set
2382{
2383 AutoPat *ap;
2384 AutoCmd *cp;
2385 char_u *name;
2386 char *s;
LemonBoyeca7c602022-04-14 15:39:43 +01002387 estack_T *entry;
2388 char_u *namep;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002389
LemonBoyeca7c602022-04-14 15:39:43 +01002390 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
2391
2392 // Clear the exestack entry for this ETYPE_AUCMD entry.
2393 VIM_CLEAR(entry->es_name);
2394 entry->es_info.aucmd = NULL;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002395
2396 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2397 {
2398 apc->curpat = NULL;
2399
2400 // Only use a pattern when it has not been removed, has commands and
2401 // the group matches. For buffer-local autocommands only check the
2402 // buffer number.
2403 if (ap->pat != NULL && ap->cmds != NULL
2404 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2405 {
2406 // execution-condition
2407 if (ap->buflocal_nr == 0
2408 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2409 apc->sfname, apc->tail, ap->allow_dirs))
2410 : ap->buflocal_nr == apc->arg_bufnr)
2411 {
2412 name = event_nr2name(apc->event);
2413 s = _("%s Autocommands for \"%s\"");
LemonBoyeca7c602022-04-14 15:39:43 +01002414 namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1);
2415 if (namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002416 {
LemonBoyeca7c602022-04-14 15:39:43 +01002417 sprintf((char *)namep, s, (char *)name, (char *)ap->pat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002418 if (p_verbose >= 8)
2419 {
2420 verbose_enter();
LemonBoyeca7c602022-04-14 15:39:43 +01002421 smsg(_("Executing %s"), namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002422 verbose_leave();
2423 }
2424 }
2425
LemonBoyeca7c602022-04-14 15:39:43 +01002426 // Update the exestack entry for this autocmd.
2427 entry->es_name = namep;
2428 entry->es_info.aucmd = apc;
2429
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002430 apc->curpat = ap;
2431 apc->nextcmd = ap->cmds;
2432 // mark last command
2433 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2434 cp->last = FALSE;
2435 cp->last = TRUE;
2436 }
2437 line_breakcheck();
2438 if (apc->curpat != NULL) // found a match
2439 break;
2440 }
2441 if (stop_at_last && ap->last)
2442 break;
2443 }
2444}
2445
2446/*
LemonBoyeca7c602022-04-14 15:39:43 +01002447 * Get the script context where autocommand "acp" is defined.
2448 */
2449 sctx_T *
2450acp_script_ctx(AutoPatCmd_T *acp)
2451{
2452 return &acp->script_ctx;
2453}
2454
2455/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002456 * Get next autocommand command.
2457 * Called by do_cmdline() to get the next line for ":if".
2458 * Returns allocated string, or NULL for end of autocommands.
2459 */
2460 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002461getnextac(
2462 int c UNUSED,
2463 void *cookie,
2464 int indent UNUSED,
2465 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002466{
LemonBoyeca7c602022-04-14 15:39:43 +01002467 AutoPatCmd_T *acp = (AutoPatCmd_T *)cookie;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002468 char_u *retval;
2469 AutoCmd *ac;
2470
2471 // Can be called again after returning the last line.
2472 if (acp->curpat == NULL)
2473 return NULL;
2474
2475 // repeat until we find an autocommand to execute
2476 for (;;)
2477 {
2478 // skip removed commands
2479 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2480 if (acp->nextcmd->last)
2481 acp->nextcmd = NULL;
2482 else
2483 acp->nextcmd = acp->nextcmd->next;
2484
2485 if (acp->nextcmd != NULL)
2486 break;
2487
2488 // at end of commands, find next pattern that matches
2489 if (acp->curpat->last)
2490 acp->curpat = NULL;
2491 else
2492 acp->curpat = acp->curpat->next;
2493 if (acp->curpat != NULL)
2494 auto_next_pat(acp, TRUE);
2495 if (acp->curpat == NULL)
2496 return NULL;
2497 }
2498
2499 ac = acp->nextcmd;
2500
2501 if (p_verbose >= 9)
2502 {
2503 verbose_enter_scroll();
2504 smsg(_("autocommand %s"), ac->cmd);
2505 msg_puts("\n"); // don't overwrite this either
2506 verbose_leave_scroll();
2507 }
2508 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002509 // Remove one-shot ("once") autocmd in anticipation of its execution.
2510 if (ac->once)
2511 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002512 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002513 current_sctx = ac->script_ctx;
LemonBoyeca7c602022-04-14 15:39:43 +01002514 acp->script_ctx = current_sctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002515 if (ac->last)
2516 acp->nextcmd = NULL;
2517 else
2518 acp->nextcmd = ac->next;
2519 return retval;
2520}
2521
2522/*
2523 * Return TRUE if there is a matching autocommand for "fname".
2524 * To account for buffer-local autocommands, function needs to know
2525 * in which buffer the file will be opened.
2526 */
2527 int
2528has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2529{
2530 AutoPat *ap;
2531 char_u *fname;
2532 char_u *tail = gettail(sfname);
2533 int retval = FALSE;
2534
2535 fname = FullName_save(sfname, FALSE);
2536 if (fname == NULL)
2537 return FALSE;
2538
2539#ifdef BACKSLASH_IN_FILENAME
2540 /*
2541 * Replace all backslashes with forward slashes. This makes the
2542 * autocommand patterns portable between Unix and MS-DOS.
2543 */
2544 sfname = vim_strsave(sfname);
2545 if (sfname != NULL)
2546 forward_slash(sfname);
2547 forward_slash(fname);
2548#endif
2549
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002550 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002551 if (ap->pat != NULL && ap->cmds != NULL
2552 && (ap->buflocal_nr == 0
2553 ? match_file_pat(NULL, &ap->reg_prog,
2554 fname, sfname, tail, ap->allow_dirs)
2555 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2556 ))
2557 {
2558 retval = TRUE;
2559 break;
2560 }
2561
2562 vim_free(fname);
2563#ifdef BACKSLASH_IN_FILENAME
2564 vim_free(sfname);
2565#endif
2566
2567 return retval;
2568}
2569
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002570/*
2571 * Function given to ExpandGeneric() to obtain the list of autocommand group
2572 * names.
2573 */
2574 char_u *
2575get_augroup_name(expand_T *xp UNUSED, int idx)
2576{
2577 if (idx == augroups.ga_len) // add "END" add the end
2578 return (char_u *)"END";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002579 if (idx < 0 || idx >= augroups.ga_len) // end of list
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002580 return NULL;
2581 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2582 // skip deleted entries
2583 return (char_u *)"";
2584 return AUGROUP_NAME(idx); // return a name
2585}
2586
2587static int include_groups = FALSE;
2588
2589 char_u *
2590set_context_in_autocmd(
2591 expand_T *xp,
2592 char_u *arg,
2593 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2594{
2595 char_u *p;
2596 int group;
2597
2598 // check for a group name, skip it if present
2599 include_groups = FALSE;
2600 p = arg;
2601 group = au_get_grouparg(&arg);
2602 if (group == AUGROUP_ERROR)
2603 return NULL;
2604 // If there only is a group name that's what we expand.
2605 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2606 {
2607 arg = p;
2608 group = AUGROUP_ALL;
2609 }
2610
2611 // skip over event name
2612 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2613 if (*p == ',')
2614 arg = p + 1;
2615 if (*p == NUL)
2616 {
2617 if (group == AUGROUP_ALL)
2618 include_groups = TRUE;
2619 xp->xp_context = EXPAND_EVENTS; // expand event name
2620 xp->xp_pattern = arg;
2621 return NULL;
2622 }
2623
2624 // skip over pattern
2625 arg = skipwhite(p);
2626 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2627 arg++;
2628 if (*arg)
2629 return arg; // expand (next) command
2630
2631 if (doautocmd)
2632 xp->xp_context = EXPAND_FILES; // expand file names
2633 else
2634 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2635 return NULL;
2636}
2637
2638/*
2639 * Function given to ExpandGeneric() to obtain the list of event names.
2640 */
2641 char_u *
2642get_event_name(expand_T *xp UNUSED, int idx)
2643{
2644 if (idx < augroups.ga_len) // First list group names, if wanted
2645 {
2646 if (!include_groups || AUGROUP_NAME(idx) == NULL
2647 || AUGROUP_NAME(idx) == get_deleted_augroup())
2648 return (char_u *)""; // skip deleted entries
2649 return AUGROUP_NAME(idx); // return a name
2650 }
2651 return (char_u *)event_names[idx - augroups.ga_len].name;
2652}
2653
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002654
2655#if defined(FEAT_EVAL) || defined(PROTO)
2656/*
2657 * Return TRUE if autocmd is supported.
2658 */
2659 int
2660autocmd_supported(char_u *name)
2661{
2662 char_u *p;
2663
2664 return (event_name2nr(name, &p) != NUM_EVENTS);
2665}
2666
2667/*
2668 * Return TRUE if an autocommand is defined for a group, event and
2669 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2670 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2671 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2672 * Used for:
2673 * exists("#Group") or
2674 * exists("#Group#Event") or
2675 * exists("#Group#Event#pat") or
2676 * exists("#Event") or
2677 * exists("#Event#pat")
2678 */
2679 int
2680au_exists(char_u *arg)
2681{
2682 char_u *arg_save;
2683 char_u *pattern = NULL;
2684 char_u *event_name;
2685 char_u *p;
2686 event_T event;
2687 AutoPat *ap;
2688 buf_T *buflocal_buf = NULL;
2689 int group;
2690 int retval = FALSE;
2691
2692 // Make a copy so that we can change the '#' chars to a NUL.
2693 arg_save = vim_strsave(arg);
2694 if (arg_save == NULL)
2695 return FALSE;
2696 p = vim_strchr(arg_save, '#');
2697 if (p != NULL)
2698 *p++ = NUL;
2699
2700 // First, look for an autocmd group name
2701 group = au_find_group(arg_save);
2702 if (group == AUGROUP_ERROR)
2703 {
2704 // Didn't match a group name, assume the first argument is an event.
2705 group = AUGROUP_ALL;
2706 event_name = arg_save;
2707 }
2708 else
2709 {
2710 if (p == NULL)
2711 {
2712 // "Group": group name is present and it's recognized
2713 retval = TRUE;
2714 goto theend;
2715 }
2716
2717 // Must be "Group#Event" or "Group#Event#pat".
2718 event_name = p;
2719 p = vim_strchr(event_name, '#');
2720 if (p != NULL)
2721 *p++ = NUL; // "Group#Event#pat"
2722 }
2723
2724 pattern = p; // "pattern" is NULL when there is no pattern
2725
2726 // find the index (enum) for the event name
2727 event = event_name2nr(event_name, &p);
2728
2729 // return FALSE if the event name is not recognized
2730 if (event == NUM_EVENTS)
2731 goto theend;
2732
2733 // Find the first autocommand for this event.
2734 // If there isn't any, return FALSE;
2735 // If there is one and no pattern given, return TRUE;
2736 ap = first_autopat[(int)event];
2737 if (ap == NULL)
2738 goto theend;
2739
2740 // if pattern is "<buffer>", special handling is needed which uses curbuf
2741 // for pattern "<buffer=N>, fnamecmp() will work fine
2742 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2743 buflocal_buf = curbuf;
2744
2745 // Check if there is an autocommand with the given pattern.
2746 for ( ; ap != NULL; ap = ap->next)
2747 // only use a pattern when it has not been removed and has commands.
2748 // For buffer-local autocommands, fnamecmp() works fine.
2749 if (ap->pat != NULL && ap->cmds != NULL
2750 && (group == AUGROUP_ALL || ap->group == group)
2751 && (pattern == NULL
2752 || (buflocal_buf == NULL
2753 ? fnamecmp(ap->pat, pattern) == 0
2754 : ap->buflocal_nr == buflocal_buf->b_fnum)))
2755 {
2756 retval = TRUE;
2757 break;
2758 }
2759
2760theend:
2761 vim_free(arg_save);
2762 return retval;
2763}
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002764
2765/*
2766 * autocmd_add() and autocmd_delete() functions
2767 */
2768 static void
2769autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
2770{
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002771 list_T *aucmd_list;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002772 listitem_T *li;
2773 dict_T *event_dict;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002774 dictitem_T *di;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002775 char_u *event_name = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002776 list_T *event_list;
2777 listitem_T *eli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002778 event_T event;
2779 char_u *group_name = NULL;
2780 int group;
2781 char_u *pat = NULL;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002782 list_T *pat_list;
2783 listitem_T *pli;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002784 char_u *cmd = NULL;
2785 char_u *end;
2786 int once;
2787 int nested;
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01002788 int replace; // replace the cmd for a group/event
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002789 int retval = VVAL_TRUE;
2790 int save_augroup = current_augroup;
2791
2792 rettv->v_type = VAR_BOOL;
2793 rettv->vval.v_number = VVAL_FALSE;
2794
2795 if (check_for_list_arg(argvars, 0) == FAIL)
2796 return;
2797
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002798 aucmd_list = argvars[0].vval.v_list;
2799 if (aucmd_list == NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002800 return;
2801
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002802 FOR_ALL_LIST_ITEMS(aucmd_list, li)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002803 {
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002804 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002805 VIM_CLEAR(cmd);
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002806 event_name = NULL;
2807 event_list = NULL;
2808 pat = NULL;
2809 pat_list = NULL;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002810
2811 if (li->li_tv.v_type != VAR_DICT)
2812 continue;
2813
2814 event_dict = li->li_tv.vval.v_dict;
2815 if (event_dict == NULL)
2816 continue;
2817
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002818 di = dict_find(event_dict, (char_u *)"event", -1);
2819 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002820 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002821 if (di->di_tv.v_type == VAR_STRING)
2822 {
2823 event_name = di->di_tv.vval.v_string;
2824 if (event_name == NULL)
2825 {
2826 emsg(_(e_string_required));
2827 continue;
2828 }
2829 }
2830 else if (di->di_tv.v_type == VAR_LIST)
2831 {
2832 event_list = di->di_tv.vval.v_list;
2833 if (event_list == NULL)
2834 {
2835 emsg(_(e_list_required));
2836 continue;
2837 }
2838 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002839 else
2840 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002841 emsg(_(e_string_or_list_expected));
2842 continue;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002843 }
2844 }
2845
Bram Moolenaard61efa52022-07-23 09:52:04 +01002846 group_name = dict_get_string(event_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002847 if (group_name == NULL || *group_name == NUL)
2848 // if the autocmd group name is not specified, then use the current
2849 // autocmd group
2850 group = current_augroup;
2851 else
2852 {
2853 group = au_find_group(group_name);
2854 if (group == AUGROUP_ERROR)
2855 {
2856 if (delete)
2857 {
2858 semsg(_(e_no_such_group_str), group_name);
2859 retval = VVAL_FALSE;
2860 break;
2861 }
2862 // group is not found, create it now
2863 group = au_new_group(group_name);
2864 if (group == AUGROUP_ERROR)
2865 {
2866 semsg(_(e_no_such_group_str), group_name);
2867 retval = VVAL_FALSE;
2868 break;
2869 }
2870
2871 current_augroup = group;
2872 }
2873 }
2874
2875 // if a buffer number is specified, then generate a pattern of the form
2876 // "<buffer=n>. Otherwise, use the pattern supplied by the user.
2877 if (dict_has_key(event_dict, "bufnr"))
2878 {
2879 varnumber_T bnum;
2880
Bram Moolenaard61efa52022-07-23 09:52:04 +01002881 bnum = dict_get_number_def(event_dict, "bufnr", -1);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002882 if (bnum == -1)
2883 continue;
2884
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002885 vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
2886 pat = IObuff;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002887 }
2888 else
2889 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002890 di = dict_find(event_dict, (char_u *)"pattern", -1);
2891 if (di != NULL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002892 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002893 if (di->di_tv.v_type == VAR_STRING)
2894 {
2895 pat = di->di_tv.vval.v_string;
2896 if (pat == NULL)
2897 {
2898 emsg(_(e_string_required));
2899 continue;
2900 }
2901 }
2902 else if (di->di_tv.v_type == VAR_LIST)
2903 {
2904 pat_list = di->di_tv.vval.v_list;
2905 if (pat_list == NULL)
2906 {
2907 emsg(_(e_list_required));
2908 continue;
2909 }
2910 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002911 else
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002912 {
2913 emsg(_(e_string_or_list_expected));
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002914 continue;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002915 }
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002916 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002917 else if (delete)
2918 pat = (char_u *)"";
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002919 }
2920
Bram Moolenaard61efa52022-07-23 09:52:04 +01002921 once = dict_get_bool(event_dict, "once", FALSE);
2922 nested = dict_get_bool(event_dict, "nested", FALSE);
Yegappan Lakshmanan971f6822022-05-24 11:40:11 +01002923 // if 'replace' is true, then remove all the commands associated with
2924 // this autocmd event/group and add the new command.
Bram Moolenaard61efa52022-07-23 09:52:04 +01002925 replace = dict_get_bool(event_dict, "replace", FALSE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002926
Bram Moolenaard61efa52022-07-23 09:52:04 +01002927 cmd = dict_get_string(event_dict, "cmd", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002928 if (cmd == NULL)
2929 {
2930 if (delete)
2931 cmd = vim_strsave((char_u *)"");
2932 else
2933 continue;
2934 }
2935
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002936 if (delete && (event_name == NULL
2937 || (event_name[0] == '*' && event_name[1] == NUL)))
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002938 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002939 // if the event name is not specified or '*', delete all the events
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002940 for (event = (event_T)0; (int)event < NUM_EVENTS;
2941 event = (event_T)((int)event + 1))
2942 {
2943 if (do_autocmd_event(event, pat, once, nested, cmd, delete,
2944 group, 0) == FAIL)
2945 {
2946 retval = VVAL_FALSE;
2947 break;
2948 }
2949 }
2950 }
2951 else
2952 {
Bram Moolenaar968443e2022-05-27 21:16:34 +01002953 char_u *p = NULL;
2954
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002955 eli = NULL;
2956 end = NULL;
2957 while (TRUE)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01002958 {
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002959 if (event_list != NULL)
2960 {
2961 if (eli == NULL)
2962 eli = event_list->lv_first;
2963 else
2964 eli = eli->li_next;
2965 if (eli == NULL)
2966 break;
2967 if (eli->li_tv.v_type != VAR_STRING
Bram Moolenaar882476a2022-06-01 16:02:38 +01002968 || (p = eli->li_tv.vval.v_string) == NULL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002969 {
2970 emsg(_(e_string_required));
Bram Moolenaar882476a2022-06-01 16:02:38 +01002971 break;
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002972 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002973 }
2974 else
2975 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01002976 if (p == NULL)
2977 p = event_name;
2978 if (p == NULL || *p == NUL)
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002979 break;
2980 }
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002981
2982 event = event_name2nr(p, &end);
2983 if (event == NUM_EVENTS || *end != NUL)
2984 {
Bram Moolenaar882476a2022-06-01 16:02:38 +01002985 // this also catches something following a valid event name
Yegappan Lakshmanane0ff3a72022-05-27 18:05:33 +01002986 semsg(_(e_no_such_event_str), p);
2987 retval = VVAL_FALSE;
2988 break;
2989 }
2990 if (pat != NULL)
2991 {
2992 if (do_autocmd_event(event, pat, once, nested, cmd,
2993 delete | replace, group, 0) == FAIL)
2994 {
2995 retval = VVAL_FALSE;
2996 break;
2997 }
2998 }
2999 else if (pat_list != NULL)
3000 {
3001 FOR_ALL_LIST_ITEMS(pat_list, pli)
3002 {
3003 if (pli->li_tv.v_type != VAR_STRING
3004 || pli->li_tv.vval.v_string == NULL)
3005 {
3006 emsg(_(e_string_required));
3007 continue;
3008 }
3009 if (do_autocmd_event(event,
3010 pli->li_tv.vval.v_string, once, nested,
3011 cmd, delete | replace, group, 0) ==
3012 FAIL)
3013 {
3014 retval = VVAL_FALSE;
3015 break;
3016 }
3017 }
3018 if (retval == VVAL_FALSE)
3019 break;
3020 }
3021 if (event_name != NULL)
3022 p = end;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003023 }
3024 }
3025
3026 // if only the autocmd group name is specified for delete and the
3027 // autocmd event, pattern and cmd are not specified, then delete the
3028 // autocmd group.
3029 if (delete && group_name != NULL &&
3030 (event_name == NULL || event_name[0] == NUL)
3031 && (pat == NULL || pat[0] == NUL)
3032 && (cmd == NULL || cmd[0] == NUL))
3033 au_del_group(group_name);
3034 }
3035
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003036 VIM_CLEAR(group_name);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003037 VIM_CLEAR(cmd);
3038
3039 current_augroup = save_augroup;
3040 rettv->vval.v_number = retval;
3041}
3042
3043/*
3044 * autocmd_add() function
3045 */
3046 void
3047f_autocmd_add(typval_T *argvars, typval_T *rettv)
3048{
3049 autocmd_add_or_delete(argvars, rettv, FALSE);
3050}
3051
3052/*
3053 * autocmd_delete() function
3054 */
3055 void
3056f_autocmd_delete(typval_T *argvars, typval_T *rettv)
3057{
3058 autocmd_add_or_delete(argvars, rettv, TRUE);
3059}
3060
3061/*
3062 * autocmd_get() function
3063 * Returns a List of autocmds.
3064 */
3065 void
3066f_autocmd_get(typval_T *argvars, typval_T *rettv)
3067{
3068 event_T event_arg = NUM_EVENTS;
3069 event_T event;
3070 AutoPat *ap;
3071 AutoCmd *ac;
3072 list_T *event_list;
3073 dict_T *event_dict;
3074 char_u *event_name = NULL;
3075 char_u *pat = NULL;
3076 char_u *name = NULL;
3077 int group = AUGROUP_ALL;
3078
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01003079 if (rettv_list_alloc(rettv) == FAIL)
3080 return;
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003081 if (check_for_opt_dict_arg(argvars, 0) == FAIL)
3082 return;
3083
3084 if (argvars[0].v_type == VAR_DICT)
3085 {
3086 // return only the autocmds in the specified group
3087 if (dict_has_key(argvars[0].vval.v_dict, "group"))
3088 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003089 name = dict_get_string(argvars[0].vval.v_dict, "group", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003090 if (name == NULL)
3091 return;
3092
3093 if (*name == NUL)
3094 group = AUGROUP_DEFAULT;
3095 else
3096 {
3097 group = au_find_group(name);
3098 if (group == AUGROUP_ERROR)
3099 {
3100 semsg(_(e_no_such_group_str), name);
3101 vim_free(name);
3102 return;
3103 }
3104 }
3105 vim_free(name);
3106 }
3107
3108 // return only the autocmds for the specified event
3109 if (dict_has_key(argvars[0].vval.v_dict, "event"))
3110 {
3111 int i;
3112
Bram Moolenaard61efa52022-07-23 09:52:04 +01003113 name = dict_get_string(argvars[0].vval.v_dict, "event", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003114 if (name == NULL)
3115 return;
3116
3117 if (name[0] == '*' && name[1] == NUL)
3118 event_arg = NUM_EVENTS;
3119 else
3120 {
3121 for (i = 0; event_names[i].name != NULL; i++)
3122 if (STRICMP(event_names[i].name, name) == 0)
3123 break;
3124 if (event_names[i].name == NULL)
3125 {
3126 semsg(_(e_no_such_event_str), name);
3127 vim_free(name);
3128 return;
3129 }
3130 event_arg = event_names[i].event;
3131 }
3132 vim_free(name);
3133 }
3134
3135 // return only the autocmds for the specified pattern
3136 if (dict_has_key(argvars[0].vval.v_dict, "pattern"))
3137 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003138 pat = dict_get_string(argvars[0].vval.v_dict, "pattern", TRUE);
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003139 if (pat == NULL)
3140 return;
3141 }
3142 }
3143
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003144 event_list = rettv->vval.v_list;
3145
3146 // iterate through all the autocmd events
3147 for (event = (event_T)0; (int)event < NUM_EVENTS;
3148 event = (event_T)((int)event + 1))
3149 {
3150 if (event_arg != NUM_EVENTS && event != event_arg)
3151 continue;
3152
3153 event_name = event_nr2name(event);
3154
3155 // iterate through all the patterns for this autocmd event
3156 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
3157 {
3158 char_u *group_name;
3159
3160 if (group != AUGROUP_ALL && group != ap->group)
3161 continue;
3162
3163 if (pat != NULL && STRCMP(pat, ap->pat) != 0)
3164 continue;
3165
3166 group_name = get_augroup_name(NULL, ap->group);
3167
3168 // iterate through all the commands for this pattern and add one
3169 // item for each cmd.
3170 for (ac = ap->cmds; ac != NULL; ac = ac->next)
3171 {
3172 event_dict = dict_alloc();
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003173 if (event_dict == NULL
3174 || list_append_dict(event_list, event_dict) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003175 return;
3176
Yegappan Lakshmanan00e977c2022-06-01 12:31:53 +01003177 if (dict_add_string(event_dict, "event", event_name) == FAIL
3178 || dict_add_string(event_dict, "group",
3179 group_name == NULL ? (char_u *)""
3180 : group_name) == FAIL
3181 || (ap->buflocal_nr != 0
3182 && (dict_add_number(event_dict, "bufnr",
3183 ap->buflocal_nr) == FAIL))
3184 || dict_add_string(event_dict, "pattern",
3185 ap->pat) == FAIL
3186 || dict_add_string(event_dict, "cmd", ac->cmd) == FAIL
3187 || dict_add_bool(event_dict, "once", ac->once) == FAIL
3188 || dict_add_bool(event_dict, "nested",
3189 ac->nested) == FAIL)
Yegappan Lakshmanan1755a912022-05-19 10:31:47 +01003190 return;
3191 }
3192 }
3193 }
3194
3195 vim_free(pat);
3196}
3197
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01003198#endif