2019-01-26 16:21:07 +01:00
|
|
|
/* vi:set ts=8 sts=4 sw=4 noet:
|
|
|
|
*
|
|
|
|
* VIM - Vi IMproved by Bram Moolenaar
|
|
|
|
*
|
|
|
|
* Do ":help uganda" in Vim to read copying and usage conditions.
|
|
|
|
* Do ":help credits" in Vim to see a list of people who contributed.
|
|
|
|
* See README.txt for an overview of the Vim source code.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* autocmd.c: Autocommand related functions
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "vim.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The autocommands are stored in a list for each event.
|
|
|
|
* Autocommands for the same pattern, that are consecutive, are joined
|
|
|
|
* together, to avoid having to match the pattern too often.
|
|
|
|
* The result is an array of Autopat lists, which point to AutoCmd lists:
|
|
|
|
*
|
|
|
|
* last_autopat[0] -----------------------------+
|
|
|
|
* V
|
|
|
|
* first_autopat[0] --> Autopat.next --> Autopat.next --> NULL
|
|
|
|
* Autopat.cmds Autopat.cmds
|
|
|
|
* | |
|
|
|
|
* V V
|
|
|
|
* AutoCmd.next AutoCmd.next
|
|
|
|
* | |
|
|
|
|
* V V
|
|
|
|
* AutoCmd.next NULL
|
|
|
|
* |
|
|
|
|
* V
|
|
|
|
* NULL
|
|
|
|
*
|
|
|
|
* last_autopat[1] --------+
|
|
|
|
* V
|
|
|
|
* first_autopat[1] --> Autopat.next --> NULL
|
|
|
|
* Autopat.cmds
|
|
|
|
* |
|
|
|
|
* V
|
|
|
|
* AutoCmd.next
|
|
|
|
* |
|
|
|
|
* V
|
|
|
|
* NULL
|
|
|
|
* etc.
|
|
|
|
*
|
|
|
|
* The order of AutoCmds is important, this is the order in which they were
|
|
|
|
* defined and will have to be executed.
|
|
|
|
*/
|
|
|
|
typedef struct AutoCmd
|
|
|
|
{
|
|
|
|
char_u *cmd; // The command to be executed (NULL
|
|
|
|
// when command has been removed).
|
2019-04-04 15:04:56 +02:00
|
|
|
char once; // "One shot": removed after execution
|
2019-01-26 16:21:07 +01:00
|
|
|
char nested; // If autocommands nest here.
|
|
|
|
char last; // last command in list
|
2022-04-14 15:39:43 +01:00
|
|
|
sctx_T script_ctx; // script context where it is defined
|
2019-01-26 16:21:07 +01:00
|
|
|
struct AutoCmd *next; // next AutoCmd in list
|
|
|
|
} AutoCmd;
|
|
|
|
|
|
|
|
typedef struct AutoPat
|
|
|
|
{
|
|
|
|
struct AutoPat *next; // Next AutoPat in AutoPat list; MUST
|
|
|
|
// be the first entry.
|
|
|
|
char_u *pat; // pattern as typed (NULL when pattern
|
|
|
|
// has been removed)
|
|
|
|
regprog_T *reg_prog; // compiled regprog for pattern
|
|
|
|
AutoCmd *cmds; // list of commands to do
|
|
|
|
int group; // group ID
|
|
|
|
int patlen; // strlen() of pat
|
|
|
|
int buflocal_nr; // !=0 for buffer-local AutoPat
|
|
|
|
char allow_dirs; // Pattern may match whole path
|
|
|
|
char last; // last pattern for apply_autocmds()
|
|
|
|
} AutoPat;
|
|
|
|
|
|
|
|
static struct event_name
|
|
|
|
{
|
|
|
|
char *name; // event name
|
|
|
|
event_T event; // event number
|
|
|
|
} event_names[] =
|
|
|
|
{
|
|
|
|
{"BufAdd", EVENT_BUFADD},
|
|
|
|
{"BufCreate", EVENT_BUFADD},
|
|
|
|
{"BufDelete", EVENT_BUFDELETE},
|
|
|
|
{"BufEnter", EVENT_BUFENTER},
|
|
|
|
{"BufFilePost", EVENT_BUFFILEPOST},
|
|
|
|
{"BufFilePre", EVENT_BUFFILEPRE},
|
|
|
|
{"BufHidden", EVENT_BUFHIDDEN},
|
|
|
|
{"BufLeave", EVENT_BUFLEAVE},
|
|
|
|
{"BufNew", EVENT_BUFNEW},
|
|
|
|
{"BufNewFile", EVENT_BUFNEWFILE},
|
|
|
|
{"BufRead", EVENT_BUFREADPOST},
|
|
|
|
{"BufReadCmd", EVENT_BUFREADCMD},
|
|
|
|
{"BufReadPost", EVENT_BUFREADPOST},
|
|
|
|
{"BufReadPre", EVENT_BUFREADPRE},
|
|
|
|
{"BufUnload", EVENT_BUFUNLOAD},
|
|
|
|
{"BufWinEnter", EVENT_BUFWINENTER},
|
|
|
|
{"BufWinLeave", EVENT_BUFWINLEAVE},
|
|
|
|
{"BufWipeout", EVENT_BUFWIPEOUT},
|
|
|
|
{"BufWrite", EVENT_BUFWRITEPRE},
|
|
|
|
{"BufWritePost", EVENT_BUFWRITEPOST},
|
|
|
|
{"BufWritePre", EVENT_BUFWRITEPRE},
|
|
|
|
{"BufWriteCmd", EVENT_BUFWRITECMD},
|
|
|
|
{"CmdlineChanged", EVENT_CMDLINECHANGED},
|
|
|
|
{"CmdlineEnter", EVENT_CMDLINEENTER},
|
|
|
|
{"CmdlineLeave", EVENT_CMDLINELEAVE},
|
|
|
|
{"CmdwinEnter", EVENT_CMDWINENTER},
|
|
|
|
{"CmdwinLeave", EVENT_CMDWINLEAVE},
|
|
|
|
{"CmdUndefined", EVENT_CMDUNDEFINED},
|
|
|
|
{"ColorScheme", EVENT_COLORSCHEME},
|
|
|
|
{"ColorSchemePre", EVENT_COLORSCHEMEPRE},
|
2019-04-08 18:15:41 +02:00
|
|
|
{"CompleteChanged", EVENT_COMPLETECHANGED},
|
2019-01-26 16:21:07 +01:00
|
|
|
{"CompleteDone", EVENT_COMPLETEDONE},
|
2020-01-26 22:43:31 +01:00
|
|
|
{"CompleteDonePre", EVENT_COMPLETEDONEPRE},
|
2019-01-26 16:21:07 +01:00
|
|
|
{"CursorHold", EVENT_CURSORHOLD},
|
|
|
|
{"CursorHoldI", EVENT_CURSORHOLDI},
|
|
|
|
{"CursorMoved", EVENT_CURSORMOVED},
|
|
|
|
{"CursorMovedI", EVENT_CURSORMOVEDI},
|
|
|
|
{"DiffUpdated", EVENT_DIFFUPDATED},
|
|
|
|
{"DirChanged", EVENT_DIRCHANGED},
|
2022-02-09 12:58:20 +00:00
|
|
|
{"DirChangedPre", EVENT_DIRCHANGEDPRE},
|
2019-01-26 16:21:07 +01:00
|
|
|
{"EncodingChanged", EVENT_ENCODINGCHANGED},
|
|
|
|
{"ExitPre", EVENT_EXITPRE},
|
|
|
|
{"FileEncoding", EVENT_ENCODINGCHANGED},
|
|
|
|
{"FileAppendPost", EVENT_FILEAPPENDPOST},
|
|
|
|
{"FileAppendPre", EVENT_FILEAPPENDPRE},
|
|
|
|
{"FileAppendCmd", EVENT_FILEAPPENDCMD},
|
|
|
|
{"FileChangedShell",EVENT_FILECHANGEDSHELL},
|
|
|
|
{"FileChangedShellPost",EVENT_FILECHANGEDSHELLPOST},
|
|
|
|
{"FileChangedRO", EVENT_FILECHANGEDRO},
|
|
|
|
{"FileReadPost", EVENT_FILEREADPOST},
|
|
|
|
{"FileReadPre", EVENT_FILEREADPRE},
|
|
|
|
{"FileReadCmd", EVENT_FILEREADCMD},
|
|
|
|
{"FileType", EVENT_FILETYPE},
|
|
|
|
{"FileWritePost", EVENT_FILEWRITEPOST},
|
|
|
|
{"FileWritePre", EVENT_FILEWRITEPRE},
|
|
|
|
{"FileWriteCmd", EVENT_FILEWRITECMD},
|
|
|
|
{"FilterReadPost", EVENT_FILTERREADPOST},
|
|
|
|
{"FilterReadPre", EVENT_FILTERREADPRE},
|
|
|
|
{"FilterWritePost", EVENT_FILTERWRITEPOST},
|
|
|
|
{"FilterWritePre", EVENT_FILTERWRITEPRE},
|
|
|
|
{"FocusGained", EVENT_FOCUSGAINED},
|
|
|
|
{"FocusLost", EVENT_FOCUSLOST},
|
|
|
|
{"FuncUndefined", EVENT_FUNCUNDEFINED},
|
|
|
|
{"GUIEnter", EVENT_GUIENTER},
|
|
|
|
{"GUIFailed", EVENT_GUIFAILED},
|
|
|
|
{"InsertChange", EVENT_INSERTCHANGE},
|
|
|
|
{"InsertEnter", EVENT_INSERTENTER},
|
|
|
|
{"InsertLeave", EVENT_INSERTLEAVE},
|
2020-10-21 12:19:53 +02:00
|
|
|
{"InsertLeavePre", EVENT_INSERTLEAVEPRE},
|
2019-01-26 16:21:07 +01:00
|
|
|
{"InsertCharPre", EVENT_INSERTCHARPRE},
|
|
|
|
{"MenuPopup", EVENT_MENUPOPUP},
|
2021-09-12 13:39:55 +02:00
|
|
|
{"ModeChanged", EVENT_MODECHANGED},
|
2019-01-26 16:21:07 +01:00
|
|
|
{"OptionSet", EVENT_OPTIONSET},
|
|
|
|
{"QuickFixCmdPost", EVENT_QUICKFIXCMDPOST},
|
|
|
|
{"QuickFixCmdPre", EVENT_QUICKFIXCMDPRE},
|
|
|
|
{"QuitPre", EVENT_QUITPRE},
|
|
|
|
{"RemoteReply", EVENT_REMOTEREPLY},
|
2019-09-15 23:02:04 +02:00
|
|
|
{"SafeState", EVENT_SAFESTATE},
|
2019-09-16 21:58:13 +02:00
|
|
|
{"SafeStateAgain", EVENT_SAFESTATEAGAIN},
|
2019-01-26 16:21:07 +01:00
|
|
|
{"SessionLoadPost", EVENT_SESSIONLOADPOST},
|
|
|
|
{"ShellCmdPost", EVENT_SHELLCMDPOST},
|
|
|
|
{"ShellFilterPost", EVENT_SHELLFILTERPOST},
|
2020-06-10 20:56:58 +02:00
|
|
|
{"SigUSR1", EVENT_SIGUSR1},
|
2019-01-26 16:21:07 +01:00
|
|
|
{"SourceCmd", EVENT_SOURCECMD},
|
|
|
|
{"SourcePre", EVENT_SOURCEPRE},
|
|
|
|
{"SourcePost", EVENT_SOURCEPOST},
|
|
|
|
{"SpellFileMissing",EVENT_SPELLFILEMISSING},
|
|
|
|
{"StdinReadPost", EVENT_STDINREADPOST},
|
|
|
|
{"StdinReadPre", EVENT_STDINREADPRE},
|
|
|
|
{"SwapExists", EVENT_SWAPEXISTS},
|
|
|
|
{"Syntax", EVENT_SYNTAX},
|
|
|
|
{"TabNew", EVENT_TABNEW},
|
|
|
|
{"TabClosed", EVENT_TABCLOSED},
|
|
|
|
{"TabEnter", EVENT_TABENTER},
|
|
|
|
{"TabLeave", EVENT_TABLEAVE},
|
|
|
|
{"TermChanged", EVENT_TERMCHANGED},
|
|
|
|
{"TerminalOpen", EVENT_TERMINALOPEN},
|
2019-10-26 16:21:40 +02:00
|
|
|
{"TerminalWinOpen", EVENT_TERMINALWINOPEN},
|
2019-01-26 16:21:07 +01:00
|
|
|
{"TermResponse", EVENT_TERMRESPONSE},
|
2024-01-14 20:19:59 +01:00
|
|
|
{"TermResponseAll", EVENT_TERMRESPONSEALL},
|
2019-01-26 16:21:07 +01:00
|
|
|
{"TextChanged", EVENT_TEXTCHANGED},
|
|
|
|
{"TextChangedI", EVENT_TEXTCHANGEDI},
|
|
|
|
{"TextChangedP", EVENT_TEXTCHANGEDP},
|
2022-10-15 11:48:00 +01:00
|
|
|
{"TextChangedT", EVENT_TEXTCHANGEDT},
|
2019-01-26 16:21:07 +01:00
|
|
|
{"User", EVENT_USER},
|
|
|
|
{"VimEnter", EVENT_VIMENTER},
|
|
|
|
{"VimLeave", EVENT_VIMLEAVE},
|
|
|
|
{"VimLeavePre", EVENT_VIMLEAVEPRE},
|
2024-01-25 23:07:00 +01:00
|
|
|
{"WinNewPre", EVENT_WINNEWPRE},
|
2019-01-26 16:21:07 +01:00
|
|
|
{"WinNew", EVENT_WINNEW},
|
2021-11-13 12:38:49 +00:00
|
|
|
{"WinClosed", EVENT_WINCLOSED},
|
2019-01-26 16:21:07 +01:00
|
|
|
{"WinEnter", EVENT_WINENTER},
|
|
|
|
{"WinLeave", EVENT_WINLEAVE},
|
2022-11-22 12:40:50 +00:00
|
|
|
{"WinResized", EVENT_WINRESIZED},
|
2022-04-08 15:18:45 +01:00
|
|
|
{"WinScrolled", EVENT_WINSCROLLED},
|
2019-01-26 16:21:07 +01:00
|
|
|
{"VimResized", EVENT_VIMRESIZED},
|
|
|
|
{"TextYankPost", EVENT_TEXTYANKPOST},
|
2020-12-11 19:30:34 +01:00
|
|
|
{"VimSuspend", EVENT_VIMSUSPEND},
|
|
|
|
{"VimResume", EVENT_VIMRESUME},
|
2019-01-26 16:21:07 +01:00
|
|
|
{NULL, (event_T)0}
|
|
|
|
};
|
|
|
|
|
|
|
|
static AutoPat *first_autopat[NUM_EVENTS] =
|
|
|
|
{
|
|
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static AutoPat *last_autopat[NUM_EVENTS] =
|
|
|
|
{
|
|
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
|
|
|
};
|
|
|
|
|
2022-02-16 19:24:07 +00:00
|
|
|
#define AUGROUP_DEFAULT (-1) // default autocmd group
|
|
|
|
#define AUGROUP_ERROR (-2) // erroneous autocmd group
|
|
|
|
#define AUGROUP_ALL (-3) // all autocmd groups
|
2019-01-26 16:21:07 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* struct used to keep status while executing autocommands for an event.
|
|
|
|
*/
|
2019-12-29 23:04:25 +01:00
|
|
|
struct AutoPatCmd_S
|
2019-01-26 16:21:07 +01:00
|
|
|
{
|
|
|
|
AutoPat *curpat; // next AutoPat to examine
|
|
|
|
AutoCmd *nextcmd; // next AutoCmd to execute
|
|
|
|
int group; // group being used
|
|
|
|
char_u *fname; // fname to match with
|
|
|
|
char_u *sfname; // sfname to match with
|
|
|
|
char_u *tail; // tail of fname
|
|
|
|
event_T event; // current event
|
2022-04-14 15:39:43 +01:00
|
|
|
sctx_T script_ctx; // script context where it is defined
|
2019-01-26 16:21:07 +01:00
|
|
|
int arg_bufnr; // Initially equal to <abuf>, set to zero when
|
|
|
|
// buf is deleted.
|
2022-04-14 15:39:43 +01:00
|
|
|
AutoPatCmd_T *next; // chain of active apc-s for auto-invalidation
|
2019-12-29 23:04:25 +01:00
|
|
|
};
|
2019-01-26 16:21:07 +01:00
|
|
|
|
2022-04-14 15:39:43 +01:00
|
|
|
static AutoPatCmd_T *active_apc_list = NULL; // stack of active autocommands
|
2019-01-26 16:21:07 +01:00
|
|
|
|
2020-04-02 18:50:46 +02:00
|
|
|
// Macro to loop over all the patterns for an autocmd event
|
|
|
|
#define FOR_ALL_AUTOCMD_PATTERNS(event, ap) \
|
|
|
|
for ((ap) = first_autopat[(int)(event)]; (ap) != NULL; (ap) = (ap)->next)
|
|
|
|
|
2019-01-26 16:21:07 +01:00
|
|
|
/*
|
|
|
|
* augroups stores a list of autocmd group names.
|
|
|
|
*/
|
|
|
|
static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
|
|
|
|
#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
|
2019-11-30 20:52:27 +01:00
|
|
|
// use get_deleted_augroup() to get this
|
2019-01-26 16:21:07 +01:00
|
|
|
static char_u *deleted_augroup = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The ID of the current group. Group 0 is the default one.
|
|
|
|
*/
|
|
|
|
static int current_augroup = AUGROUP_DEFAULT;
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
static int au_need_clean = FALSE; // need to delete marked patterns
|
2019-01-26 16:21:07 +01:00
|
|
|
|
|
|
|
static char_u *event_nr2name(event_T event);
|
|
|
|
static int au_get_grouparg(char_u **argp);
|
2021-08-01 14:52:32 +02:00
|
|
|
static int do_autocmd_event(event_T event, char_u *pat, int once, int nested, char_u *cmd, int forceit, int group, int flags);
|
2019-01-26 16:21:07 +01:00
|
|
|
static int apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, int force, int group, buf_T *buf, exarg_T *eap);
|
2022-04-14 15:39:43 +01:00
|
|
|
static void auto_next_pat(AutoPatCmd_T *apc, int stop_at_last);
|
2019-01-26 16:21:07 +01:00
|
|
|
static int au_find_group(char_u *name);
|
|
|
|
|
|
|
|
static event_T last_event;
|
|
|
|
static int last_group;
|
2019-11-30 20:52:27 +01:00
|
|
|
static int autocmd_blocked = 0; // block all autocmds
|
2019-01-26 16:21:07 +01:00
|
|
|
|
|
|
|
static char_u *
|
|
|
|
get_deleted_augroup(void)
|
|
|
|
{
|
|
|
|
if (deleted_augroup == NULL)
|
|
|
|
deleted_augroup = (char_u *)_("--Deleted--");
|
|
|
|
return deleted_augroup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Show the autocommands for one AutoPat.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
show_autocmd(AutoPat *ap, event_T event)
|
|
|
|
{
|
|
|
|
AutoCmd *ac;
|
|
|
|
|
|
|
|
// Check for "got_int" (here and at various places below), which is set
|
|
|
|
// when "q" has been hit for the "--more--" prompt
|
|
|
|
if (got_int)
|
|
|
|
return;
|
|
|
|
if (ap->pat == NULL) // pattern has been removed
|
|
|
|
return;
|
|
|
|
|
2022-11-13 17:53:46 +00:00
|
|
|
// Make sure no info referenced by "ap" is cleared, e.g. when a timer
|
|
|
|
// clears an augroup. Jump to "theend" after this!
|
|
|
|
// "ap->pat" may be cleared anyway.
|
|
|
|
++autocmd_busy;
|
|
|
|
|
2019-01-26 16:21:07 +01:00
|
|
|
msg_putchar('\n');
|
|
|
|
if (got_int)
|
2022-11-13 17:53:46 +00:00
|
|
|
goto theend;
|
2019-01-26 16:21:07 +01:00
|
|
|
if (event != last_event || ap->group != last_group)
|
|
|
|
{
|
|
|
|
if (ap->group != AUGROUP_DEFAULT)
|
|
|
|
{
|
|
|
|
if (AUGROUP_NAME(ap->group) == NULL)
|
|
|
|
msg_puts_attr((char *)get_deleted_augroup(), HL_ATTR(HLF_E));
|
|
|
|
else
|
|
|
|
msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
|
|
|
|
msg_puts(" ");
|
|
|
|
}
|
|
|
|
msg_puts_attr((char *)event_nr2name(event), HL_ATTR(HLF_T));
|
|
|
|
last_event = event;
|
|
|
|
last_group = ap->group;
|
|
|
|
msg_putchar('\n');
|
|
|
|
if (got_int)
|
2022-11-13 17:53:46 +00:00
|
|
|
goto theend;
|
2019-01-26 16:21:07 +01:00
|
|
|
}
|
2022-11-13 17:53:46 +00:00
|
|
|
|
|
|
|
if (ap->pat == NULL)
|
|
|
|
goto theend; // timer might have cleared the pattern or group
|
|
|
|
|
2019-01-26 16:21:07 +01:00
|
|
|
msg_col = 4;
|
|
|
|
msg_outtrans(ap->pat);
|
|
|
|
|
|
|
|
for (ac = ap->cmds; ac != NULL; ac = ac->next)
|
|
|
|
{
|
2022-09-24 11:30:41 +01:00
|
|
|
if (ac->cmd == NULL) // skip removed commands
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (msg_col >= 14)
|
|
|
|
msg_putchar('\n');
|
|
|
|
msg_col = 14;
|
|
|
|
if (got_int)
|
2022-11-13 17:53:46 +00:00
|
|
|
goto theend;
|
2022-09-24 11:30:41 +01:00
|
|
|
msg_outtrans(ac->cmd);
|
2019-01-26 16:21:07 +01:00
|
|
|
#ifdef FEAT_EVAL
|
2022-09-24 11:30:41 +01:00
|
|
|
if (p_verbose > 0)
|
|
|
|
last_set_msg(ac->script_ctx);
|
2019-01-26 16:21:07 +01:00
|
|
|
#endif
|
2022-09-24 11:30:41 +01:00
|
|
|
if (got_int)
|
2022-11-13 17:53:46 +00:00
|
|
|
goto theend;
|
2022-09-24 11:30:41 +01:00
|
|
|
if (ac->next != NULL)
|
|
|
|
{
|
|
|
|
msg_putchar('\n');
|
2019-01-26 16:21:07 +01:00
|
|
|
if (got_int)
|
2022-11-13 17:53:46 +00:00
|
|
|
goto theend;
|
2019-01-26 16:21:07 +01:00
|
|
|
}
|
|
|
|
}
|
2022-11-13 17:53:46 +00:00
|
|
|
|
|
|
|
theend:
|
|
|
|
--autocmd_busy;
|
2019-01-26 16:21:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Mark an autocommand pattern for deletion.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
au_remove_pat(AutoPat *ap)
|
|
|
|
{
|
|
|
|
VIM_CLEAR(ap->pat);
|
|
|
|
ap->buflocal_nr = -1;
|
|
|
|
au_need_clean = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Mark all commands for a pattern for deletion.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
au_remove_cmds(AutoPat *ap)
|
|
|
|
{
|
|
|
|
AutoCmd *ac;
|
|
|
|
|
|
|
|
for (ac = ap->cmds; ac != NULL; ac = ac->next)
|
|
|
|
VIM_CLEAR(ac->cmd);
|
|
|
|
au_need_clean = TRUE;
|
|
|
|
}
|
|
|
|
|
2019-04-04 15:04:56 +02:00
|
|
|
// Delete one command from an autocmd pattern.
|
|
|
|
static void au_del_cmd(AutoCmd *ac)
|
|
|
|
{
|
|
|
|
VIM_CLEAR(ac->cmd);
|
|
|
|
au_need_clean = TRUE;
|
|
|
|
}
|
|
|
|
|
2019-01-26 16:21:07 +01:00
|
|
|
/*
|
|
|
|
* Cleanup autocommands and patterns that have been deleted.
|
|
|
|
* This is only done when not executing autocommands.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
au_cleanup(void)
|
|
|
|
{
|
|
|
|
AutoPat *ap, **prev_ap;
|
|
|
|
AutoCmd *ac, **prev_ac;
|
|
|
|
event_T event;
|
|
|
|
|
|
|
|
if (autocmd_busy || !au_need_clean)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// loop over all events
|
2021-10-02 11:26:51 +01:00
|
|
|
for (event = (event_T)0; (int)event < NUM_EVENTS;
|
2019-01-26 16:21:07 +01:00
|
|
|
event = (event_T)((int)event + 1))
|
|
|
|
{
|
|
|
|
// loop over all autocommand patterns
|
|
|
|
prev_ap = &(first_autopat[(int)event]);
|
|
|
|
for (ap = *prev_ap; ap != NULL; ap = *prev_ap)
|
|
|
|
{
|
2019-04-04 15:04:56 +02:00
|
|
|
int has_cmd = FALSE;
|
|
|
|
|
2019-04-04 15:40:56 +02:00
|
|
|
// loop over all commands for this pattern
|
|
|
|
prev_ac = &(ap->cmds);
|
2019-01-26 16:21:07 +01:00
|
|
|
for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
|
|
|
|
{
|
|
|
|
// remove the command if the pattern is to be deleted or when
|
|
|
|
// the command has been marked for deletion
|
|
|
|
if (ap->pat == NULL || ac->cmd == NULL)
|
|
|
|
{
|
|
|
|
*prev_ac = ac->next;
|
|
|
|
vim_free(ac->cmd);
|
|
|
|
vim_free(ac);
|
|
|
|
}
|
2019-04-04 15:40:56 +02:00
|
|
|
else
|
|
|
|
{
|
2019-04-04 15:04:56 +02:00
|
|
|
has_cmd = TRUE;
|
2019-01-26 16:21:07 +01:00
|
|
|
prev_ac = &(ac->next);
|
2019-04-04 15:04:56 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-04 15:40:56 +02:00
|
|
|
if (ap->pat != NULL && !has_cmd)
|
2019-04-04 15:04:56 +02:00
|
|
|
// Pattern was not marked for deletion, but all of its
|
|
|
|
// commands were. So mark the pattern for deletion.
|
|
|
|
au_remove_pat(ap);
|
2019-01-26 16:21:07 +01:00
|
|
|
|
|
|
|
// remove the pattern if it has been marked for deletion
|
|
|
|
if (ap->pat == NULL)
|
|
|
|
{
|
|
|
|
if (ap->next == NULL)
|
|
|
|
{
|
|
|
|
if (prev_ap == &(first_autopat[(int)event]))
|
|
|
|
last_autopat[(int)event] = NULL;
|
|
|
|
else
|
|
|
|
// this depends on the "next" field being the first in
|
|
|
|
// the struct
|
|
|
|
last_autopat[(int)event] = (AutoPat *)prev_ap;
|
|
|
|
}
|
|
|
|
*prev_ap = ap->next;
|
|
|
|
vim_regfree(ap->reg_prog);
|
|
|
|
vim_free(ap);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
prev_ap = &(ap->next);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
au_need_clean = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Called when buffer is freed, to remove/invalidate related buffer-local
|
|
|
|
* autocmds.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
aubuflocal_remove(buf_T *buf)
|
|
|
|
{
|
2022-04-14 15:39:43 +01:00
|
|
|
AutoPat *ap;
|
|
|
|
event_T event;
|
|
|
|
AutoPatCmd_T *apc;
|
2019-01-26 16:21:07 +01:00
|
|
|
|
|
|
|
// invalidate currently executing autocommands
|
|
|
|
for (apc = active_apc_list; apc; apc = apc->next)
|
|
|
|
if (buf->b_fnum == apc->arg_bufnr)
|
|
|
|
apc->arg_bufnr = 0;
|
|
|
|
|
|
|
|
// invalidate buflocals looping through events
|
2021-10-02 11:26:51 +01:00
|
|
|
for (event = (event_T)0; (int)event < NUM_EVENTS;
|
2019-01-26 16:21:07 +01:00
|
|
|
event = (event_T)((int)event + 1))
|
|
|
|
// loop over all autocommand patterns
|
2020-04-02 18:50:46 +02:00
|
|
|
FOR_ALL_AUTOCMD_PATTERNS(event, ap)
|
2019-01-26 16:21:07 +01:00
|
|
|
if (ap->buflocal_nr == buf->b_fnum)
|
|
|
|
{
|
|
|
|
au_remove_pat(ap);
|
|
|
|
if (p_verbose >= 6)
|
|
|
|
{
|
|
|
|
verbose_enter();
|
|
|
|
smsg(_("auto-removing autocommand: %s <buffer=%d>"),
|
|
|
|
event_nr2name(event), buf->b_fnum);
|
|
|
|
verbose_leave();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
au_cleanup();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add an autocmd group name.
|
|
|
|
* Return its ID. Returns AUGROUP_ERROR (< 0) for error.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
au_new_group(char_u *name)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
i = au_find_group(name);
|
2022-09-24 11:30:41 +01:00
|
|
|
if (i != AUGROUP_ERROR)
|
|
|
|
return i;
|
2019-01-26 16:21:07 +01:00
|
|
|
|
2022-09-24 11:30:41 +01:00
|
|
|
// the group doesn't exist yet, add it. First try using a free entry.
|
|
|
|
for (i = 0; i < augroups.ga_len; ++i)
|
2019-01-26 16:21:07 +01:00
|
|
|
if (AUGROUP_NAME(i) == NULL)
|
2022-09-24 11:30:41 +01:00
|
|
|
break;
|
|
|
|
if (i == augroups.ga_len && ga_grow(&augroups, 1) == FAIL)
|
|
|
|
return AUGROUP_ERROR;
|
|
|
|
|
|
|
|
AUGROUP_NAME(i) = vim_strsave(name);
|
|
|
|
if (AUGROUP_NAME(i) == NULL)
|
|
|
|
return AUGROUP_ERROR;
|
|
|
|
if (i == augroups.ga_len)
|
|
|
|
++augroups.ga_len;
|
2019-01-26 16:21:07 +01:00
|
|
|
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
au_del_group(char_u *name)
|
|
|
|
{
|
2022-09-24 11:30:41 +01:00
|
|
|
int i;
|
|
|
|
event_T event;
|
|
|
|
AutoPat *ap;
|
|
|
|
int in_use = FALSE;
|
|
|
|
|
2019-01-26 16:21:07 +01:00
|
|
|
|
|
|
|
i = au_find_group(name);
|
|
|
|
if (i == AUGROUP_ERROR) // the group doesn't exist
|
2022-09-24 11:30:41 +01:00
|
|
|
{
|
2021-12-31 19:59:55 +00:00
|
|
|
semsg(_(e_no_such_group_str), name);
|
2022-09-24 11:30:41 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (i == current_augroup)
|
2019-01-26 16:21:07 +01:00
|
|
|
{
|
2022-09-24 11:30:41 +01:00
|
|
|
emsg(_(e_cannot_delete_current_group));
|
|
|
|
return;
|
|
|
|
}
|
2019-01-26 16:21:07 +01:00
|
|
|
|
2022-09-24 11:30:41 +01:00
|
|
|
for (event = (event_T)0; (int)event < NUM_EVENTS;
|
|
|
|
event = (event_T)((int)event + 1))
|
|
|
|
{
|
|
|
|
FOR_ALL_AUTOCMD_PATTERNS(event, ap)
|
|
|
|
if (ap->group == i && ap->pat != NULL)
|
|
|
|
{
|
|
|
|
give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
|
|
|
|
in_use = TRUE;
|
|
|
|
event = NUM_EVENTS;
|
|
|
|
break;
|
|
|
|
}
|
2019-01-26 16:21:07 +01:00
|
|
|
}
|
2022-09-24 11:30:41 +01:00
|
|
|
vim_free(AUGROUP_NAME(i));
|
|
|
|
if (in_use)
|
|
|
|
AUGROUP_NAME(i) = get_deleted_augroup();
|
|
|
|
else
|
|
|
|
AUGROUP_NAME(i) = NULL;
|
2019-01-26 16:21:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the ID of an autocmd group name.
|
|
|
|
* Return its ID. Returns AUGROUP_ERROR (< 0) for error.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
au_find_group(char_u *name)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < augroups.ga_len; ++i)
|
|
|
|
if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
|
|
|
|
&& STRCMP(AUGROUP_NAME(i), name) == 0)
|
|
|
|
return i;
|
|
|
|
return AUGROUP_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return TRUE if augroup "name" exists.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
au_has_group(char_u *name)
|
|
|
|
{
|
|
|
|
return au_find_group(name) != AUGROUP_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ":augroup {name}".
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
do_augroup(char_u *arg, int del_group)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (del_group)
|
|
|
|
{
|
|
|
|
if (*arg == NUL)
|
2021-12-31 22:49:24 +00:00
|
|
|
emsg(_(e_argument_required));
|
2019-01-26 16:21:07 +01:00
|
|
|
else
|
|
|
|
au_del_group(arg);
|
|
|
|
}
|
|
|
|
else if (STRICMP(arg, "end") == 0) // ":aug end": back to group 0
|
|
|
|
current_augroup = AUGROUP_DEFAULT;
|
|
|
|
else if (*arg) // ":aug xxx": switch to group xxx
|
|
|
|
{
|
|
|
|
i = au_new_group(arg);
|
|
|
|
if (i != AUGROUP_ERROR)
|
|
|
|
current_augroup = i;
|
|
|
|
}
|
|
|
|
else // ":aug": list the group names
|
|
|
|
{
|
|
|
|
msg_start();
|
|
|
|
for (i = 0; i < augroups.ga_len; ++i)
|
|
|
|
{
|
|
|
|
if (AUGROUP_NAME(i) != NULL)
|
|
|
|
{
|
|
|
|
msg_puts((char *)AUGROUP_NAME(i));
|
|
|
|
msg_puts(" ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
msg_clr_eos();
|
|
|
|
msg_end();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-28 18:51:43 +00:00
|
|
|
void
|
|
|
|
autocmd_init(void)
|
|
|
|
{
|
|
|
|
CLEAR_FIELD(aucmd_win);
|
|
|
|
}
|
|
|
|
|
2019-01-26 16:21:07 +01:00
|
|
|
#if defined(EXITFREE) || defined(PROTO)
|
|
|
|
void
|
|
|
|
free_all_autocmds(void)
|
|
|
|
{
|
|
|
|
char_u *s;
|
|
|
|
|
|
|
|
for (current_augroup = -1; current_augroup < augroups.ga_len;
|
|
|
|
++current_augroup)
|
2021-08-01 14:52:32 +02:00
|
|
|
do_autocmd(NULL, (char_u *)"", TRUE);
|
2019-01-26 16:21:07 +01:00
|
|
|
|
2022-11-28 18:51:43 +00:00
|
|
|
for (int i = 0; i < augroups.ga_len; ++i)
|
2019-01-26 16:21:07 +01:00
|
|
|
{
|
|
|
|
s = ((char_u **)(augroups.ga_data))[i];
|
|
|
|
if (s != get_deleted_augroup())
|
|
|
|
vim_free(s);
|
|
|
|
}
|
|
|
|
ga_clear(&augroups);
|
2022-11-28 18:51:43 +00:00
|
|
|
|
2022-11-28 20:34:52 +00:00
|
|
|
// aucmd_win[] is freed in win_free_all()
|
2019-01-26 16:21:07 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-11-28 18:51:43 +00:00
|
|
|
/*
|
|
|
|
* Return TRUE if "win" is an active entry in aucmd_win[].
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
is_aucmd_win(win_T *win)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
|
|
|
|
if (aucmd_win[i].auc_win_used && aucmd_win[i].auc_win == win)
|
|
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2019-01-26 16:21:07 +01:00
|
|
|
/*
|
|
|
|
* Return the event number for event name "start".
|
|
|
|
* Return NUM_EVENTS if the event name was not found.
|
|
|
|
* Return a pointer to the next event name in "end".
|
|
|
|
*/
|
|
|
|
static event_T
|
|
|
|
event_name2nr(char_u *start, char_u **end)
|
|
|
|
{
|
|
|
|
char_u *p;
|
|
|
|
int i;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
// the event name ends with end of line, '|', a blank or a comma
|
|
|
|
for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
|
|
|
|
;
|
|
|
|
for (i = 0; event_names[i].name != NULL; ++i)
|
|
|
|
{
|
|
|
|
len = (int)STRLEN(event_names[i].name);
|
|
|
|
if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (*p == ',')
|
|
|
|
++p;
|
|
|
|
*end = p;
|
|
|
|
if (event_names[i].name == NULL)
|
|
|
|
return NUM_EVENTS;
|
|
|
|
return event_names[i].event;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return the name for event "event".
|
|
|
|
*/
|
|
|
|
static char_u *
|
|
|
|
event_nr2name(event_T event)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; event_names[i].name != NULL; ++i)
|
|
|
|
if (event_names[i].event == event)
|
|
|
|
return (char_u *)event_names[i].name;
|
|
|
|
return (char_u *)"Unknown";
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Scan over the events. "*" stands for all events.
|
|
|
|
*/
|
|
|
|
static char_u *
|
|
|
|
find_end_event(
|
|
|
|
char_u *arg,
|
|
|
|
int have_group) // TRUE when group name was found
|
|
|
|
{
|
|
|
|
char_u *pat;
|
|
|
|
char_u *p;
|
|
|
|
|
|
|
|
if (*arg == '*')
|
|
|
|
{
|
|
|
|
if (arg[1] && !VIM_ISWHITE(arg[1]))
|
|
|
|
{
|
2021-12-31 18:49:43 +00:00
|
|
|
semsg(_(e_illegal_character_after_star_str), arg);
|
2019-01-26 16:21:07 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
pat = arg + 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p)
|
|
|
|
{
|
2021-10-02 11:26:51 +01:00
|
|
|
if ((int)event_name2nr(pat, &p) >= NUM_EVENTS)
|
2019-01-26 16:21:07 +01:00
|
|
|
{
|
|
|
|
if (have_group)
|
2021-12-31 18:49:43 +00:00
|
|
|
semsg(_(e_no_such_event_str), pat);
|
2019-01-26 16:21:07 +01:00
|
|
|
else
|
2021-12-31 18:49:43 +00:00
|
|
|
semsg(_(e_no_such_group_or_event_str), pat);
|
2019-01-26 16:21:07 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return pat;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return TRUE if "event" is included in 'eventignore'.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
event_ignored(event_T event)
|
|
|
|
{
|
|
|
|
char_u *p = p_ei;
|
|
|
|
|
|
|
|
while (*p != NUL)
|
|
|
|
{
|
|
|
|
if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
|
|
|
|
return TRUE;
|
|
|
|
if (event_name2nr(p, &p) == event)
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return OK when the contents of p_ei is valid, FAIL otherwise.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
check_ei(void)
|
|
|
|
{
|
|
|
|
char_u *p = p_ei;
|
|
|
|
|
|
|
|
while (*p)
|
|
|
|
{
|
|
|
|
if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
|
|
|
|
{
|
|
|
|
p += 3;
|
|
|
|
if (*p == ',')
|
|
|
|
++p;
|
|
|
|
}
|
|
|
|
else if (event_name2nr(p, &p) == NUM_EVENTS)
|
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
# if defined(FEAT_SYN_HL) || defined(PROTO)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add "what" to 'eventignore' to skip loading syntax highlighting for every
|
|
|
|
* buffer loaded into the window. "what" must start with a comma.
|
|
|
|
* Returns the old value of 'eventignore' in allocated memory.
|
|
|
|
*/
|
|
|
|
char_u *
|
|
|
|
au_event_disable(char *what)
|
|
|
|
{
|
|
|
|
char_u *new_ei;
|
|
|
|
char_u *save_ei;
|
|
|
|
|
|
|
|
save_ei = vim_strsave(p_ei);
|
2022-09-24 11:30:41 +01:00
|
|
|
if (save_ei == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
new_ei = vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what));
|
|
|
|
if (new_ei == NULL)
|
2019-01-26 16:21:07 +01:00
|
|
|
{
|
2022-09-24 11:30:41 +01:00
|
|
|
vim_free(save_ei);
|
|
|
|
return NULL;
|
2019-01-26 16:21:07 +01:00
|
|
|
}
|
2022-09-24 11:30:41 +01:00
|
|
|
|
|
|
|
if (*what == ',' && *p_ei == NUL)
|
|
|
|
STRCPY(new_ei, what + 1);
|
|
|
|
else
|
|
|
|
STRCAT(new_ei, what);
|
|
|
|
set_string_option_direct((char_u *)"ei", -1, new_ei,
|
|
|
|
OPT_FREE, SID_NONE);
|
|
|
|
vim_free(new_ei);
|
2019-01-26 16:21:07 +01:00
|
|
|
return save_ei;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
au_event_restore(char_u *old_ei)
|
|
|
|
{
|
|
|
|
if (old_ei != NULL)
|
|
|
|
{
|
|
|
|
set_string_option_direct((char_u *)"ei", -1, old_ei,
|
|
|
|
OPT_FREE, SID_NONE);
|
|
|
|
vim_free(old_ei);
|
|
|
|
}
|
|
|
|
}
|
2019-11-30 20:52:27 +01:00
|
|
|
# endif // FEAT_SYN_HL
|
2019-01-26 16:21:07 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* do_autocmd() -- implements the :autocmd command. Can be used in the
|
|
|
|
* following ways:
|
|
|
|
*
|
|
|
|
* :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
|
|
|
|
* will be automatically executed for <event>
|
|
|
|
* when editing a file matching <pat>, in
|
|
|
|
* the current group.
|
|
|
|
* :autocmd <event> <pat> Show the autocommands associated with
|
|
|
|
* <event> and <pat>.
|
|
|
|
* :autocmd <event> Show the autocommands associated with
|
|
|
|
* <event>.
|
|
|
|
* :autocmd Show all autocommands.
|
|
|
|
* :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
|
|
|
|
* <event> and <pat>, and add the command
|
|
|
|
* <cmd>, for the current group.
|
|
|
|
* :autocmd! <event> <pat> Remove all autocommands associated with
|
|
|
|
* <event> and <pat> for the current group.
|
|
|
|
* :autocmd! <event> Remove all autocommands associated with
|
|
|
|
* <event> for the current group.
|
|
|
|
* :autocmd! Remove ALL autocommands for the current
|
|
|
|
* group.
|
|
|
|
*
|
|
|
|
* Multiple events and patterns may be given separated by commas. Here are
|
|
|
|
* some examples:
|
|
|
|
* :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
|
|
|
|
* :autocmd bufleave * set tw=79 nosmartindent ic infercase
|
|
|
|
*
|
|
|
|
* :autocmd * *.c show all autocommands for *.c files.
|
|
|
|
*
|
|
|
|
* Mostly a {group} argument can optionally appear before <event>.
|
2021-08-01 14:52:32 +02:00
|
|
|
* "eap" can be NULL.
|
2019-01-26 16:21:07 +01:00
|
|
|
*/
|
|
|
|
void
|
2021-08-01 14:52:32 +02:00
|
|
|
do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
|
2019-01-26 16:21:07 +01:00
|
|
|
{
|
|
|
|
char_u *arg = arg_in;
|
|
|
|
char_u *pat;
|
|
|
|
char_u *envpat = NULL;
|
|
|
|
char_u *cmd;
|
2021-08-01 14:52:32 +02:00
|
|
|
int cmd_need_free = FALSE;
|
2019-01-26 16:21:07 +01:00
|
|
|
event_T event;
|
2021-08-01 14:52:32 +02:00
|
|
|
char_u *tofree = NULL;
|
2019-01-26 16:21:07 +01:00
|
|
|
int nested = FALSE;
|
2019-04-04 15:04:56 +02:00
|
|
|
int once = FALSE;
|
2019-01-26 16:21:07 +01:00
|
|
|
int group;
|
2019-04-04 15:04:56 +02:00
|
|
|
int i;
|
2021-08-01 14:52:32 +02:00
|
|
|
int flags = 0;
|
2019-01-26 16:21:07 +01:00
|
|
|
|
|
|
|
if (*arg == '|')
|
|
|
|
{
|
2021-11-20 10:38:25 +00:00
|
|
|
eap->nextcmd = arg + 1;
|
2019-01-26 16:21:07 +01:00
|
|
|
arg = (char_u *)"";
|
|
|
|
group = AUGROUP_ALL; // no argument, use all groups
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Check for a legal group name. If not, use AUGROUP_ALL.
|
|
|
|
*/
|
|
|
|
group = au_get_grouparg(&arg);
|
|
|
|
if (arg == NULL) // out of memory
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Scan over the events.
|
|
|
|
* If we find an illegal name, return here, don't do anything.
|
|
|
|
*/
|
|
|
|
pat = find_end_event(arg, group != AUGROUP_ALL);
|
|
|
|
if (pat == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pat = skipwhite(pat);
|
|
|
|
if (*pat == '|')
|
|
|
|
{
|
2021-11-20 10:38:25 +00:00
|
|
|
eap->nextcmd = pat + 1;
|
2019-01-26 16:21:07 +01:00
|
|
|
pat = (char_u *)"";
|
|
|
|
cmd = (char_u *)"";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Scan over the pattern. Put a NUL at the end.
|
|
|
|
*/
|
|
|
|
cmd = pat;
|
|
|
|
while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
|
|
|
|
cmd++;
|
|
|
|
if (*cmd)
|
|
|
|
*cmd++ = NUL;
|
|
|
|
|
|
|
|
// Expand environment variables in the pattern. Set 'shellslash', we
|
|
|
|
// want forward slashes here.
|
|
|
|
if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
|
|
|
|
{
|
|
|
|
#ifdef BACKSLASH_IN_FILENAME
|
|
|
|
int p_ssl_save = p_ssl;
|
|
|
|
|
|
|
|
p_ssl = TRUE;
|
|
|
|
#endif
|
|
|
|
envpat = expand_env_save(pat);
|
|
|
|
#ifdef BACKSLASH_IN_FILENAME
|
|
|
|
p_ssl = p_ssl_save;
|
|
|
|
#endif
|
|
|
|
if (envpat != NULL)
|
|
|
|
pat = envpat;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd = skipwhite(cmd);
|
2019-04-04 15:04:56 +02:00
|
|
|
for (i = 0; i < 2; i++)
|
2019-01-26 16:21:07 +01:00
|
|
|
{
|
2022-09-24 11:30:41 +01:00
|
|
|
if (*cmd == NUL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Check for "++once" flag.
|
|
|
|
if (STRNCMP(cmd, "++once", 6) == 0 && VIM_ISWHITE(cmd[6]))
|
2019-04-04 15:04:56 +02:00
|
|
|
{
|
2022-09-24 11:30:41 +01:00
|
|
|
if (once)
|
|
|
|
semsg(_(e_duplicate_argument_str), "++once");
|
|
|
|
once = TRUE;
|
|
|
|
cmd = skipwhite(cmd + 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for "++nested" flag.
|
|
|
|
if ((STRNCMP(cmd, "++nested", 8) == 0 && VIM_ISWHITE(cmd[8])))
|
|
|
|
{
|
|
|
|
if (nested)
|
2019-04-04 15:04:56 +02:00
|
|
|
{
|
2022-09-24 11:30:41 +01:00
|
|
|
semsg(_(e_duplicate_argument_str), "++nested");
|
|
|
|
return;
|
2019-04-04 15:04:56 +02:00
|
|
|
}
|
2022-09-24 11:30:41 +01:00
|
|
|
nested = TRUE;
|
|
|
|
cmd = skipwhite(cmd + 8);
|
|
|
|
}
|
2019-04-04 15:04:56 +02:00
|
|
|
|
2022-09-24 11:30:41 +01:00
|
|
|
// Check for the old "nested" flag in legacy script.
|
|
|
|
if (STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
|
|
|
|
{
|
|
|
|
if (in_vim9script())
|
2019-04-04 15:04:56 +02:00
|
|
|
{
|
2022-09-24 11:30:41 +01:00
|
|
|
// If there ever is a :nested command this error should
|
|
|
|
// be removed and "nested" accepted as the start of the
|
|
|
|
// command.
|
|
|
|
emsg(_(e_invalid_command_nested_did_you_mean_plusplus_nested));
|
|
|
|
return;
|
2019-04-04 15:04:56 +02:00
|
|
|
}
|
2022-09-24 11:30:41 +01:00
|
|
|
if (nested)
|
2019-04-04 15:04:56 +02:00
|
|
|
{
|
2022-09-24 11:30:41 +01:00
|
|
|
semsg(_(e_duplicate_argument_str), "nested");
|
|
|
|
return;
|
2019-04-04 15:04:56 +02:00
|
|
|
}
|
2022-09-24 11:30:41 +01:00
|
|
|
nested = TRUE;
|
|
|
|
cmd = skipwhite(cmd + 6);
|
2019-04-04 15:04:56 +02:00
|
|
|
}
|
2019-01-26 16:21:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the start of the commands.
|
|
|
|
* Expand <sfile> in it.
|
|
|
|
*/
|
|
|
|
if (*cmd != NUL)
|
|
|
|
{
|
2021-08-01 14:52:32 +02:00
|
|
|
if (eap != NULL)
|
|
|
|
// Read a {} block if it follows.
|
|
|
|
cmd = may_get_cmd_block(eap, cmd, &tofree, &flags);
|
|
|
|
|
2019-01-26 16:21:07 +01:00
|
|
|
cmd = expand_sfile(cmd);
|
|
|
|
if (cmd == NULL) // some error
|
|
|
|
return;
|
2021-08-01 14:52:32 +02:00
|
|
|
cmd_need_free = TRUE;
|
2019-01-26 16:21:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Print header when showing autocommands.
|
|
|
|
*/
|
|
|
|
if (!forceit && *cmd == NUL)
|
|
|
|
// Highlight title
|
|
|
|
msg_puts_title(_("\n--- Autocommands ---"));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Loop over the events.
|
|
|
|
*/
|
|
|
|
last_event = (event_T)-1; // for listing the event name
|
|
|
|
last_group = AUGROUP_ERROR; // for listing the group name
|
|
|
|
if (*arg == '*' || *arg == NUL || *arg == '|')
|
|
|
|
{
|
2021-12-24 19:24:47 +00:00
|
|
|
if (*cmd != NUL)
|
2021-01-28 13:47:59 +01:00
|
|
|
emsg(_(e_cannot_define_autocommands_for_all_events));
|
|
|
|
else
|
2021-10-02 11:26:51 +01:00
|
|
|
for (event = (event_T)0; (int)event < NUM_EVENTS;
|
2021-01-28 13:47:59 +01:00
|
|
|
event = (event_T)((int)event + 1))
|
|
|
|
if (do_autocmd_event(event, pat,
|
2021-08-01 14:52:32 +02:00
|
|
|
once, nested, cmd, forceit, group, flags) == FAIL)
|
2021-01-28 13:47:59 +01:00
|
|
|
break;
|
2019-01-26 16:21:07 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
|
|
|
|
if (do_autocmd_event(event_name2nr(arg, &arg), pat,
|
2021-08-01 14:52:32 +02:00
|
|
|
once, nested, cmd, forceit, group, flags) == FAIL)
|
2019-01-26 16:21:07 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-08-01 14:52:32 +02:00
|
|
|
if (cmd_need_free)
|
2019-01-26 16:21:07 +01:00
|
|
|
vim_free(cmd);
|
2021-08-01 14:52:32 +02:00
|
|
|
vim_free(tofree);
|
2019-01-26 16:21:07 +01:00
|
|
|
vim_free(envpat);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the group ID in a ":autocmd" or ":doautocmd" argument.
|
|
|
|
* The "argp" argument is advanced to the following argument.
|
|
|
|
*
|
|
|
|
* Returns the group ID, AUGROUP_ERROR for error (out of memory).
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
au_get_grouparg(char_u **argp)
|
|
|
|
{
|
|
|
|
char_u *group_name;
|
|
|
|
char_u *p;
|
|
|
|
char_u *arg = *argp;
|
|
|
|
int group = AUGROUP_ALL;
|
|
|
|
|
|
|
|
for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p)
|
|
|
|
;
|
2023-01-09 19:04:23 +00:00
|
|
|
if (p <= arg)
|
|
|
|
return AUGROUP_ALL;
|
|
|
|
|
|
|
|
group_name = vim_strnsave(arg, p - arg);
|
|
|
|
if (group_name == NULL) // out of memory
|
|
|
|
return AUGROUP_ERROR;
|
|
|
|
group = au_find_group(group_name);
|
|
|
|
if (group == AUGROUP_ERROR)
|
|
|
|
group = AUGROUP_ALL; // no match, use all groups
|
|
|
|
else
|
|
|
|
*argp = skipwhite(p); // match, skip over group name
|
|
|
|
vim_free(group_name);
|
2019-01-26 16:21:07 +01:00
|
|
|
return group;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* do_autocmd() for one event.
|
|
|
|
* If *pat == NUL do for all patterns.
|
|
|
|
* If *cmd == NUL show entries.
|
|
|
|
* If forceit == TRUE delete entries.
|
|
|
|
* If group is not AUGROUP_ALL, only use this group.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
do_autocmd_event(
|
|
|
|
event_T event,
|
|
|
|
char_u *pat,
|
2019-04-04 15:04:56 +02:00
|
|
|
int once,
|
2019-01-26 16:21:07 +01:00
|
|
|
int nested,
|
|
|
|
char_u *cmd,
|
|
|
|
int forceit,
|
2021-08-01 14:52:32 +02:00
|
|
|
int group,
|
|
|
|
int flags)
|
2019-01-26 16:21:07 +01:00
|
|
|
{
|
|
|
|
AutoPat *ap;
|
|
|
|
AutoPat **prev_ap;
|
|
|
|
AutoCmd *ac;
|
|
|
|
AutoCmd **prev_ac;
|
|
|
|
int brace_level;
|
|
|
|
char_u *endpat;
|
|
|
|
int findgroup;
|
|
|
|
int allgroups;
|
|
|
|
int patlen;
|
|
|
|
int is_buflocal;
|
|
|
|
int buflocal_nr;
|
2019-11-30 20:52:27 +01:00
|
|
|
char_u buflocal_pat[25]; // for "<buffer=X>"
|
2019-01-26 16:21:07 +01:00
|
|
|
|
|
|
|
if (group == AUGROUP_ALL)
|
|
|
|
findgroup = current_augroup;
|
|
|
|
else
|
|
|
|
findgroup = group;
|
|
|
|
allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Show or delete all patterns for an event.
|
|
|
|
*/
|
|
|
|
if (*pat == NUL)
|
|
|
|
{
|
2020-04-02 18:50:46 +02:00
|
|
|
FOR_ALL_AUTOCMD_PATTERNS(event, ap)
|
2019-01-26 16:21:07 +01:00
|
|
|
{
|
|
|
|
if (forceit) // delete the AutoPat, if it's in the current group
|
|
|
|
{
|
|
|
|
if (ap->group == findgroup)
|
|
|
|
au_remove_pat(ap);
|
|
|
|
}
|
|
|
|
else if (group == AUGROUP_ALL || ap->group == group)
|
|
|
|
show_autocmd(ap, event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Loop through all the specified patterns.
|
|
|
|
*/
|
|
|
|
for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Find end of the pattern.
|
|
|
|
* Watch out for a comma in braces, like "*.\{obj,o\}".
|
|
|
|
*/
|
|
|
|
brace_level = 0;
|
|
|
|
for (endpat = pat; *endpat && (*endpat != ',' || brace_level
|
|
|
|
|| (endpat > pat && endpat[-1] == '\\')); ++endpat)
|
|
|
|
{
|
|
|
|
if (*endpat == '{')
|
|
|
|
brace_level++;
|
|
|
|
else if (*endpat == '}')
|
|
|
|
brace_level--;
|
|
|
|
}
|
|
|
|
if (pat == endpat) // ignore single comma
|
|
|
|
continue;
|
|
|
|
patlen = (int)(endpat - pat);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* detect special <buflocal[=X]> buffer-local patterns
|
|
|
|
*/
|
|
|
|
is_buflocal = FALSE;
|
|
|
|
buflocal_nr = 0;
|
|
|
|
|
|
|
|
if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
|
|
|
|
&& pat[patlen - 1] == '>')
|
|
|
|
{
|
|
|
|
// "<buffer...>": Error will be printed only for addition.
|
|
|
|
// printing and removing will proceed silently.
|
|
|
|
is_buflocal = TRUE;
|
|
|
|
if (patlen == 8)
|
|
|
|
// "<buffer>"
|
|
|
|
buflocal_nr = curbuf->b_fnum;
|
|
|
|
else if (patlen > 9 && pat[7] == '=')
|
|
|
|
{
|
|
|
|
if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0)
|
|
|
|
// "<buffer=abuf>"
|
|
|
|
buflocal_nr = autocmd_bufnr;
|
|
|
|
else if (skipdigits(pat + 8) == pat + patlen - 1)
|
|
|
|
// "<buffer=123>"
|
|
|
|
buflocal_nr = atoi((char *)pat + 8);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_buflocal)
|
|
|
|
{
|
|
|
|
// normalize pat into standard "<buffer>#N" form
|
|
|
|
sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
|
|
|
|
pat = buflocal_pat; // can modify pat and patlen
|
|
|
|
patlen = (int)STRLEN(buflocal_pat); // but not endpat
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find AutoPat entries with this pattern. When adding a command it
|
|
|
|
* always goes at or after the last one, so start at the end.
|
|
|
|
*/
|
|
|
|
if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL)
|
|
|
|
prev_ap = &last_autopat[(int)event];
|
|
|
|
else
|
|
|
|
prev_ap = &first_autopat[(int)event];
|
|
|
|
while ((ap = *prev_ap) != NULL)
|
|
|
|
{
|
|
|
|
if (ap->pat != NULL)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
/*
|
|
|
|
* Accept a pattern when:
|
2019-01-26 16:21:07 +01:00
|
|
|
* - a group was specified and it's that group, or a group was
|
|
|
|
* not specified and it's the current group, or a group was
|
|
|
|
* not specified and we are listing
|
|
|
|
* - the length of the pattern matches
|
|
|
|
* - the pattern matches.
|
|
|
|
* For <buffer[=X]>, this condition works because we normalize
|
|
|
|
* all buffer-local patterns.
|
|
|
|
*/
|
|
|
|
if ((allgroups || ap->group == findgroup)
|
|
|
|
&& ap->patlen == patlen
|
|
|
|
&& STRNCMP(pat, ap->pat, patlen) == 0)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Remove existing autocommands.
|
|
|
|
* If adding any new autocmd's for this AutoPat, don't
|
|
|
|
* delete the pattern from the autopat list, append to
|
|
|
|
* this list.
|
|
|
|
*/
|
|
|
|
if (forceit)
|
|
|
|
{
|
|
|
|
if (*cmd != NUL && ap->next == NULL)
|
|
|
|
{
|
|
|
|
au_remove_cmds(ap);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
au_remove_pat(ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Show autocmd's for this autopat, or buflocals <buffer=X>
|
|
|
|
*/
|
|
|
|
else if (*cmd == NUL)
|
|
|
|
show_autocmd(ap, event);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add autocmd to this autopat, if it's the last one.
|
|
|
|
*/
|
|
|
|
else if (ap->next == NULL)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
prev_ap = &ap->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add a new command.
|
|
|
|
*/
|
|
|
|
if (*cmd != NUL)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If the pattern we want to add a command to does appear at the
|
|
|
|
* end of the list (or not is not in the list at all), add the
|
|
|
|
* pattern at the end of the list.
|
|
|
|
*/
|
|
|
|
if (ap == NULL)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// refuse to add buffer-local ap if buffer number is invalid
|
2019-01-26 16:21:07 +01:00
|
|
|
if (is_buflocal && (buflocal_nr == 0
|
|
|
|
|| buflist_findnr(buflocal_nr) == NULL))
|
|
|
|
{
|
2021-12-31 19:59:55 +00:00
|
|
|
semsg(_(e_buffer_nr_invalid_buffer_number), buflocal_nr);
|
2019-01-26 16:21:07 +01:00
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
|
2019-05-28 23:08:19 +02:00
|
|
|
ap = ALLOC_ONE(AutoPat);
|
2019-01-26 16:21:07 +01:00
|
|
|
if (ap == NULL)
|
|
|
|
return FAIL;
|
|
|
|
ap->pat = vim_strnsave(pat, patlen);
|
|
|
|
ap->patlen = patlen;
|
|
|
|
if (ap->pat == NULL)
|
|
|
|
{
|
|
|
|
vim_free(ap);
|
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
|
2021-10-22 18:56:39 +01:00
|
|
|
#ifdef FEAT_EVAL
|
|
|
|
// need to initialize last_mode for the first ModeChanged
|
|
|
|
// autocmd
|
|
|
|
if (event == EVENT_MODECHANGED && !has_modechanged())
|
2022-04-09 18:17:34 +01:00
|
|
|
get_mode(last_mode);
|
2021-10-22 18:56:39 +01:00
|
|
|
#endif
|
2022-11-22 12:40:50 +00:00
|
|
|
// Initialize the fields checked by the WinScrolled and
|
|
|
|
// WinResized trigger to prevent them from firing right after
|
|
|
|
// the first autocmd is defined.
|
|
|
|
if ((event == EVENT_WINSCROLLED || event == EVENT_WINRESIZED)
|
|
|
|
&& !(has_winscrolled() || has_winresized()))
|
2022-04-08 15:18:45 +01:00
|
|
|
{
|
2022-11-20 12:11:45 +00:00
|
|
|
tabpage_T *save_curtab = curtab;
|
|
|
|
tabpage_T *tp;
|
|
|
|
FOR_ALL_TABPAGES(tp)
|
|
|
|
{
|
|
|
|
unuse_tabpage(curtab);
|
|
|
|
use_tabpage(tp);
|
|
|
|
snapshot_windows_scroll_size();
|
|
|
|
}
|
|
|
|
unuse_tabpage(curtab);
|
|
|
|
use_tabpage(save_curtab);
|
2022-04-08 15:18:45 +01:00
|
|
|
}
|
2021-10-22 18:56:39 +01:00
|
|
|
|
2019-01-26 16:21:07 +01:00
|
|
|
if (is_buflocal)
|
|
|
|
{
|
|
|
|
ap->buflocal_nr = buflocal_nr;
|
|
|
|
ap->reg_prog = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char_u *reg_pat;
|
|
|
|
|
|
|
|
ap->buflocal_nr = 0;
|
|
|
|
reg_pat = file_pat_to_reg_pat(pat, endpat,
|
|
|
|
&ap->allow_dirs, TRUE);
|
|
|
|
if (reg_pat != NULL)
|
|
|
|
ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
|
|
|
|
vim_free(reg_pat);
|
|
|
|
if (reg_pat == NULL || ap->reg_prog == NULL)
|
|
|
|
{
|
|
|
|
vim_free(ap->pat);
|
|
|
|
vim_free(ap);
|
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ap->cmds = NULL;
|
|
|
|
*prev_ap = ap;
|
|
|
|
last_autopat[(int)event] = ap;
|
|
|
|
ap->next = NULL;
|
|
|
|
if (group == AUGROUP_ALL)
|
|
|
|
ap->group = current_augroup;
|
|
|
|
else
|
|
|
|
ap->group = group;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add the autocmd at the end of the AutoCmd list.
|
|
|
|
*/
|
|
|
|
prev_ac = &(ap->cmds);
|
|
|
|
while ((ac = *prev_ac) != NULL)
|
|
|
|
prev_ac = &ac->next;
|
2019-05-28 23:08:19 +02:00
|
|
|
ac = ALLOC_ONE(AutoCmd);
|
2019-01-26 16:21:07 +01:00
|
|
|
if (ac == NULL)
|
|
|
|
return FAIL;
|
|
|
|
ac->cmd = vim_strsave(cmd);
|
|
|
|
ac->script_ctx = current_sctx;
|
2021-08-01 14:52:32 +02:00
|
|
|
if (flags & UC_VIM9)
|
|
|
|
ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
|
2020-12-28 18:26:00 +01:00
|
|
|
#ifdef FEAT_EVAL
|
2019-12-29 23:04:25 +01:00
|
|
|
ac->script_ctx.sc_lnum += SOURCING_LNUM;
|
2019-01-26 16:21:07 +01:00
|
|
|
#endif
|
|
|
|
if (ac->cmd == NULL)
|
|
|
|
{
|
|
|
|
vim_free(ac);
|
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
ac->next = NULL;
|
|
|
|
*prev_ac = ac;
|
2019-04-04 15:04:56 +02:00
|
|
|
ac->once = once;
|
2019-01-26 16:21:07 +01:00
|
|
|
ac->nested = nested;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
au_cleanup(); // may really delete removed patterns/commands now
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Implementation of ":doautocmd [group] event [fname]".
|
|
|
|
* Return OK for success, FAIL for failure;
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
do_doautocmd(
|
2021-08-07 13:59:43 +02:00
|
|
|
char_u *arg_start,
|
2019-01-26 16:21:07 +01:00
|
|
|
int do_msg, // give message for no matching autocmds?
|
|
|
|
int *did_something)
|
|
|
|
{
|
2021-08-07 13:59:43 +02:00
|
|
|
char_u *arg = arg_start;
|
2019-01-26 16:21:07 +01:00
|
|
|
char_u *fname;
|
|
|
|
int nothing_done = TRUE;
|
|
|
|
int group;
|
|
|
|
|
|
|
|
if (did_something != NULL)
|
|
|
|
*did_something = FALSE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for a legal group name. If not, use AUGROUP_ALL.
|
|
|
|
*/
|
|
|
|
group = au_get_grouparg(&arg);
|
|
|
|
if (arg == NULL) // out of memory
|
|
|
|
return FAIL;
|
|
|
|
|
|
|
|
if (*arg == '*')
|
|
|
|
{
|
2021-12-31 18:49:43 +00:00
|
|
|
emsg(_(e_cant_execute_autocommands_for_all_events));
|
2019-01-26 16:21:07 +01:00
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Scan over the events.
|
|
|
|
* If we find an illegal name, return here, don't do anything.
|
|
|
|
*/
|
|
|
|
fname = find_end_event(arg, group != AUGROUP_ALL);
|
|
|
|
if (fname == NULL)
|
|
|
|
return FAIL;
|
|
|
|
|
|
|
|
fname = skipwhite(fname);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Loop over the events.
|
|
|
|
*/
|
|
|
|
while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
|
|
|
|
if (apply_autocmds_group(event_name2nr(arg, &arg),
|
|
|
|
fname, NULL, TRUE, group, curbuf, NULL))
|
|
|
|
nothing_done = FALSE;
|
|
|
|
|
2021-08-07 13:59:43 +02:00
|
|
|
if (nothing_done && do_msg
|
|
|
|
#ifdef FEAT_EVAL
|
|
|
|
&& !aborting()
|
|
|
|
#endif
|
|
|
|
)
|
|
|
|
smsg(_("No matching autocommands: %s"), arg_start);
|
2019-01-26 16:21:07 +01:00
|
|
|
if (did_something != NULL)
|
|
|
|
*did_something = !nothing_done;
|
|
|
|
|
|
|
|
#ifdef FEAT_EVAL
|
|
|
|
return aborting() ? FAIL : OK;
|
|
|
|
#else
|
|
|
|
return OK;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ":doautoall": execute autocommands for each loaded buffer.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ex_doautoall(exarg_T *eap)
|
|
|
|
{
|
2021-03-13 15:47:56 +01:00
|
|
|
int retval = OK;
|
2019-01-26 16:21:07 +01:00
|
|
|
aco_save_T aco;
|
|
|
|
buf_T *buf;
|
|
|
|
bufref_T bufref;
|
|
|
|
char_u *arg = eap->arg;
|
|
|
|
int call_do_modelines = check_nomodeline(&arg);
|
|
|
|
int did_aucmd;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is a bit tricky: For some commands curwin->w_buffer needs to be
|
|
|
|
* equal to curbuf, but for some buffers there may not be a window.
|
|
|
|
* So we change the buffer for the current window for a moment. This
|
|
|
|
* gives problems when the autocommands make changes to the list of
|
|
|
|
* buffers or windows...
|
|
|
|
*/
|
|
|
|
FOR_ALL_BUFFERS(buf)
|
|
|
|
{
|
2021-03-13 15:47:56 +01:00
|
|
|
// Only do loaded buffers and skip the current buffer, it's done last.
|
2022-09-24 11:30:41 +01:00
|
|
|
if (buf->b_ml.ml_mfp == NULL || buf == curbuf)
|
|
|
|
continue;
|
2019-01-26 16:21:07 +01:00
|
|
|
|
2022-11-28 18:51:43 +00:00
|
|
|
// Find a window for this buffer and save some values.
|
2022-09-24 11:30:41 +01:00
|
|
|
aucmd_prepbuf(&aco, buf);
|
2022-11-28 18:51:43 +00:00
|
|
|
if (curbuf != buf)
|
|
|
|
{
|
|
|
|
// Failed to find a window for this buffer. Better not execute
|
|
|
|
// autocommands then.
|
|
|
|
retval = FAIL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-09-24 11:30:41 +01:00
|
|
|
set_bufref(&bufref, buf);
|
2019-01-26 16:21:07 +01:00
|
|
|
|
2022-09-24 11:30:41 +01:00
|
|
|
// execute the autocommands for this buffer
|
|
|
|
retval = do_doautocmd(arg, FALSE, &did_aucmd);
|
2019-01-26 16:21:07 +01:00
|
|
|
|
2022-09-24 11:30:41 +01:00
|
|
|
if (call_do_modelines && did_aucmd)
|
|
|
|
// Execute the modeline settings, but don't set window-local
|
|
|
|
// options if we are using the current window for another
|
|
|
|
// buffer.
|
2022-11-28 18:51:43 +00:00
|
|
|
do_modelines(is_aucmd_win(curwin) ? OPT_NOWIN : 0);
|
2019-01-26 16:21:07 +01:00
|
|
|
|
2022-09-24 11:30:41 +01:00
|
|
|
// restore the current window
|
|
|
|
aucmd_restbuf(&aco);
|
|
|
|
|
|
|
|
// stop if there is some error or buffer was deleted
|
|
|
|
if (retval == FAIL || !bufref_valid(&bufref))
|
|
|
|
{
|
|
|
|
retval = FAIL;
|
|
|
|
break;
|
2019-01-26 16:21:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-13 15:47:56 +01:00
|
|
|
// Execute autocommands for the current buffer last.
|
|
|
|
if (retval == OK)
|
|
|
|
{
|
|
|
|
do_doautocmd(arg, FALSE, &did_aucmd);
|
|
|
|
if (call_do_modelines && did_aucmd)
|
|
|
|
do_modelines(0);
|
|
|
|
}
|
2019-01-26 16:21:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check *argp for <nomodeline>. When it is present return FALSE, otherwise
|
|
|
|
* return TRUE and advance *argp to after it.
|
|
|
|
* Thus return TRUE when do_modelines() should be called.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
check_nomodeline(char_u **argp)
|
|
|
|
{
|
|
|
|
if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
|
|
|
|
{
|
|
|
|
*argp = skipwhite(*argp + 12);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Prepare for executing autocommands for (hidden) buffer "buf".
|
|
|
|
* Search for a visible window containing the current buffer. If there isn't
|
2022-11-28 18:51:43 +00:00
|
|
|
* one then use an entry in "aucmd_win[]".
|
2019-01-26 16:21:07 +01:00
|
|
|
* Set "curbuf" and "curwin" to match "buf".
|
2022-11-28 18:51:43 +00:00
|
|
|
* When this fails "curbuf" is not equal "buf".
|
2019-01-26 16:21:07 +01:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
aucmd_prepbuf(
|
|
|
|
aco_save_T *aco, // structure to save values in
|
|
|
|
buf_T *buf) // new curbuf
|
|
|
|
{
|
|
|
|
win_T *win;
|
|
|
|
int save_ea;
|
|
|
|
#ifdef FEAT_AUTOCHDIR
|
|
|
|
int save_acd;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Find a window that is for the new buffer
|
|
|
|
if (buf == curbuf) // be quick when buf is curbuf
|
|
|
|
win = curwin;
|
|
|
|
else
|
|
|
|
FOR_ALL_WINDOWS(win)
|
|
|
|
if (win->w_buffer == buf)
|
|
|
|
break;
|
|
|
|
|
2022-11-28 18:51:43 +00:00
|
|
|
// Allocate a window when needed.
|
|
|
|
win_T *auc_win = NULL;
|
|
|
|
int auc_idx = AUCMD_WIN_COUNT;
|
|
|
|
if (win == NULL)
|
2019-01-26 16:21:07 +01:00
|
|
|
{
|
2022-11-28 18:51:43 +00:00
|
|
|
for (auc_idx = 0; auc_idx < AUCMD_WIN_COUNT; ++auc_idx)
|
|
|
|
if (!aucmd_win[auc_idx].auc_win_used)
|
|
|
|
{
|
2022-11-28 20:34:52 +00:00
|
|
|
if (aucmd_win[auc_idx].auc_win == NULL)
|
|
|
|
aucmd_win[auc_idx].auc_win = win_alloc_popup_win();
|
|
|
|
auc_win = aucmd_win[auc_idx].auc_win;
|
2022-11-28 18:51:43 +00:00
|
|
|
if (auc_win != NULL)
|
|
|
|
aucmd_win[auc_idx].auc_win_used = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this fails (out of memory or using all AUCMD_WIN_COUNT
|
|
|
|
// entries) then we can't reliable execute the autocmd, return with
|
|
|
|
// "curbuf" unequal "buf".
|
|
|
|
if (auc_win == NULL)
|
|
|
|
return;
|
2019-01-26 16:21:07 +01:00
|
|
|
}
|
|
|
|
|
2020-11-07 16:58:59 +01:00
|
|
|
aco->save_curwin_id = curwin->w_id;
|
|
|
|
aco->save_prevwin_id = prevwin == NULL ? 0 : prevwin->w_id;
|
2023-04-09 22:01:31 +01:00
|
|
|
aco->save_State = State;
|
2024-01-17 21:22:59 +01:00
|
|
|
#ifdef FEAT_JOB_CHANNEL
|
|
|
|
if (bt_prompt(curbuf))
|
|
|
|
aco->save_prompt_insert = curbuf->b_prompt_insert;
|
|
|
|
#endif
|
2023-04-09 22:01:31 +01:00
|
|
|
|
2019-01-26 16:21:07 +01:00
|
|
|
if (win != NULL)
|
|
|
|
{
|
|
|
|
// There is a window for "buf" in the current tab page, make it the
|
|
|
|
// curwin. This is preferred, it has the least side effects (esp. if
|
|
|
|
// "buf" is curbuf).
|
2022-11-28 18:51:43 +00:00
|
|
|
aco->use_aucmd_win_idx = -1;
|
2019-01-26 16:21:07 +01:00
|
|
|
curwin = win;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-11-28 18:51:43 +00:00
|
|
|
// There is no window for "buf", use "auc_win". To minimize the side
|
2019-01-26 16:21:07 +01:00
|
|
|
// effects, insert it in the current tab page.
|
|
|
|
// Anything related to a window (e.g., setting folds) may have
|
|
|
|
// unexpected results.
|
2022-11-28 18:51:43 +00:00
|
|
|
aco->use_aucmd_win_idx = auc_idx;
|
2019-01-26 16:21:07 +01:00
|
|
|
|
2022-11-28 18:51:43 +00:00
|
|
|
win_init_popup_win(auc_win, buf);
|
2019-05-25 19:51:39 +02:00
|
|
|
|
2019-01-26 16:21:07 +01:00
|
|
|
aco->globaldir = globaldir;
|
|
|
|
globaldir = NULL;
|
|
|
|
|
2022-11-28 18:51:43 +00:00
|
|
|
// Split the current window, put the auc_win in the upper half.
|
2019-01-26 16:21:07 +01:00
|
|
|
// We don't want the BufEnter or WinEnter autocommands.
|
|
|
|
block_autocmds();
|
|
|
|
make_snapshot(SNAP_AUCMD_IDX);
|
|
|
|
save_ea = p_ea;
|
|
|
|
p_ea = FALSE;
|
|
|
|
|
|
|
|
#ifdef FEAT_AUTOCHDIR
|
|
|
|
// Prevent chdir() call in win_enter_ext(), through do_autochdir().
|
|
|
|
save_acd = p_acd;
|
|
|
|
p_acd = FALSE;
|
|
|
|
#endif
|
|
|
|
|
2024-02-20 22:00:33 +01:00
|
|
|
(void)win_split_ins(0, WSP_TOP | WSP_FORCE_ROOM, auc_win, 0, NULL);
|
2019-01-26 16:21:07 +01:00
|
|
|
(void)win_comp_pos(); // recompute window positions
|
|
|
|
p_ea = save_ea;
|
|
|
|
#ifdef FEAT_AUTOCHDIR
|
|
|
|
p_acd = save_acd;
|
|
|
|
#endif
|
|
|
|
unblock_autocmds();
|
2022-11-28 18:51:43 +00:00
|
|
|
curwin = auc_win;
|
2019-01-26 16:21:07 +01:00
|
|
|
}
|
|
|
|
curbuf = buf;
|
2020-11-07 16:58:59 +01:00
|
|
|
aco->new_curwin_id = curwin->w_id;
|
2019-01-26 16:21:07 +01:00
|
|
|
set_bufref(&aco->new_curbuf, curbuf);
|
2022-01-07 15:45:18 +00:00
|
|
|
|
|
|
|
// disable the Visual area, the position may be invalid in another buffer
|
|
|
|
aco->save_VIsual_active = VIsual_active;
|
|
|
|
VIsual_active = FALSE;
|
2019-01-26 16:21:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Cleanup after executing autocommands for a (hidden) buffer.
|
|
|
|
* Restore the window as it was (if possible).
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
aucmd_restbuf(
|
|
|
|
aco_save_T *aco) // structure holding saved values
|
|
|
|
{
|
2020-11-07 16:58:59 +01:00
|
|
|
int dummy;
|
|
|
|
win_T *save_curwin;
|
2019-01-26 16:21:07 +01:00
|
|
|
|
2022-11-28 18:51:43 +00:00
|
|
|
if (aco->use_aucmd_win_idx >= 0)
|
2019-01-26 16:21:07 +01:00
|
|
|
{
|
2022-11-28 18:51:43 +00:00
|
|
|
win_T *awp = aucmd_win[aco->use_aucmd_win_idx].auc_win;
|
|
|
|
|
|
|
|
// Find "awp", it can't be closed, but it may be in another tab
|
2019-01-26 16:21:07 +01:00
|
|
|
// page. Do not trigger autocommands here.
|
|
|
|
block_autocmds();
|
2022-11-28 18:51:43 +00:00
|
|
|
if (curwin != awp)
|
2019-01-26 16:21:07 +01:00
|
|
|
{
|
|
|
|
tabpage_T *tp;
|
|
|
|
win_T *wp;
|
|
|
|
|
|
|
|
FOR_ALL_TAB_WINDOWS(tp, wp)
|
|
|
|
{
|
2022-11-28 18:51:43 +00:00
|
|
|
if (wp == awp)
|
2019-01-26 16:21:07 +01:00
|
|
|
{
|
|
|
|
if (tp != curtab)
|
|
|
|
goto_tabpage_tp(tp, TRUE, TRUE);
|
2022-11-28 18:51:43 +00:00
|
|
|
win_goto(awp);
|
2019-01-26 16:21:07 +01:00
|
|
|
goto win_found;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
win_found:
|
2023-09-24 23:16:08 +02:00
|
|
|
--curbuf->b_nwindows;
|
2023-04-02 22:05:13 +01:00
|
|
|
#ifdef FEAT_JOB_CHANNEL
|
2023-05-27 14:10:08 +01:00
|
|
|
int save_stop_insert_mode = stop_insert_mode;
|
2023-04-02 22:05:13 +01:00
|
|
|
// May need to stop Insert mode if we were in a prompt buffer.
|
|
|
|
leaving_window(curwin);
|
2023-04-09 22:01:31 +01:00
|
|
|
// Do not stop Insert mode when already in Insert mode before.
|
|
|
|
if (aco->save_State & MODE_INSERT)
|
2023-05-27 14:10:08 +01:00
|
|
|
stop_insert_mode = save_stop_insert_mode;
|
2023-04-02 22:05:13 +01:00
|
|
|
#endif
|
2019-01-26 16:21:07 +01:00
|
|
|
// Remove the window and frame from the tree of frames.
|
2024-02-20 22:00:33 +01:00
|
|
|
(void)winframe_remove(curwin, &dummy, NULL, NULL);
|
2019-01-26 16:21:07 +01:00
|
|
|
win_remove(curwin, NULL);
|
2022-11-28 20:34:52 +00:00
|
|
|
|
|
|
|
// The window is marked as not used, but it is not freed, it can be
|
|
|
|
// used again.
|
2022-11-28 18:51:43 +00:00
|
|
|
aucmd_win[aco->use_aucmd_win_idx].auc_win_used = FALSE;
|
2019-01-26 16:21:07 +01:00
|
|
|
last_status(FALSE); // may need to remove last status line
|
|
|
|
|
|
|
|
if (!valid_tabpage_win(curtab))
|
|
|
|
// no valid window in current tabpage
|
|
|
|
close_tabpage(curtab);
|
|
|
|
|
|
|
|
restore_snapshot(SNAP_AUCMD_IDX, FALSE);
|
|
|
|
(void)win_comp_pos(); // recompute window positions
|
|
|
|
unblock_autocmds();
|
|
|
|
|
2020-11-07 16:58:59 +01:00
|
|
|
save_curwin = win_find_by_id(aco->save_curwin_id);
|
|
|
|
if (save_curwin != NULL)
|
|
|
|
curwin = save_curwin;
|
2019-01-26 16:21:07 +01:00
|
|
|
else
|
|
|
|
// Hmm, original window disappeared. Just use the first one.
|
|
|
|
curwin = firstwin;
|
2020-10-01 22:37:40 +02:00
|
|
|
curbuf = curwin->w_buffer;
|
|
|
|
#ifdef FEAT_JOB_CHANNEL
|
|
|
|
// May need to restore insert mode for a prompt buffer.
|
|
|
|
entering_window(curwin);
|
2024-01-17 21:22:59 +01:00
|
|
|
if (bt_prompt(curbuf))
|
|
|
|
curbuf->b_prompt_insert = aco->save_prompt_insert;
|
2020-10-01 22:37:40 +02:00
|
|
|
#endif
|
2020-11-07 16:58:59 +01:00
|
|
|
prevwin = win_find_by_id(aco->save_prevwin_id);
|
2019-01-26 16:21:07 +01:00
|
|
|
#ifdef FEAT_EVAL
|
2022-11-28 18:51:43 +00:00
|
|
|
vars_clear(&awp->w_vars->dv_hashtab); // free all w: variables
|
|
|
|
hash_init(&awp->w_vars->dv_hashtab); // re-use the hashtab
|
2019-01-26 16:21:07 +01:00
|
|
|
#endif
|
|
|
|
vim_free(globaldir);
|
|
|
|
globaldir = aco->globaldir;
|
|
|
|
|
|
|
|
// the buffer contents may have changed
|
2023-02-04 10:58:34 +00:00
|
|
|
VIsual_active = aco->save_VIsual_active;
|
2019-01-26 16:21:07 +01:00
|
|
|
check_cursor();
|
|
|
|
if (curwin->w_topline > curbuf->b_ml.ml_line_count)
|
|
|
|
{
|
|
|
|
curwin->w_topline = curbuf->b_ml.ml_line_count;
|
|
|
|
#ifdef FEAT_DIFF
|
|
|
|
curwin->w_topfill = 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#if defined(FEAT_GUI)
|
2021-12-17 16:00:04 +00:00
|
|
|
if (gui.in_use)
|
|
|
|
{
|
2022-11-28 18:51:43 +00:00
|
|
|
// Hide the scrollbars from the "awp" and update.
|
|
|
|
gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_LEFT], FALSE);
|
|
|
|
gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_RIGHT], FALSE);
|
2021-12-17 16:00:04 +00:00
|
|
|
gui_may_update_scrollbars();
|
|
|
|
}
|
2019-01-26 16:21:07 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-11-07 16:58:59 +01:00
|
|
|
// Restore curwin. Use the window ID, a window may have been closed
|
|
|
|
// and the memory re-used for another one.
|
|
|
|
save_curwin = win_find_by_id(aco->save_curwin_id);
|
|
|
|
if (save_curwin != NULL)
|
2019-01-26 16:21:07 +01:00
|
|
|
{
|
|
|
|
// Restore the buffer which was previously edited by curwin, if
|
|
|
|
// it was changed, we are still the same window and the buffer is
|
|
|
|
// valid.
|
2020-11-07 16:58:59 +01:00
|
|
|
if (curwin->w_id == aco->new_curwin_id
|
2019-01-26 16:21:07 +01:00
|
|
|
&& curbuf != aco->new_curbuf.br_buf
|
|
|
|
&& bufref_valid(&aco->new_curbuf)
|
|
|
|
&& aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
|
|
|
|
{
|
|
|
|
# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
|
|
|
|
if (curwin->w_s == &curbuf->b_s)
|
|
|
|
curwin->w_s = &aco->new_curbuf.br_buf->b_s;
|
|
|
|
# endif
|
|
|
|
--curbuf->b_nwindows;
|
|
|
|
curbuf = aco->new_curbuf.br_buf;
|
|
|
|
curwin->w_buffer = curbuf;
|
|
|
|
++curbuf->b_nwindows;
|
|
|
|
}
|
|
|
|
|
2020-11-07 16:58:59 +01:00
|
|
|
curwin = save_curwin;
|
2019-01-26 16:21:07 +01:00
|
|
|
curbuf = curwin->w_buffer;
|
2020-11-07 16:58:59 +01:00
|
|
|
prevwin = win_find_by_id(aco->save_prevwin_id);
|
2023-02-04 10:58:34 +00:00
|
|
|
|
2019-11-02 22:54:41 +01:00
|
|
|
// In case the autocommand moves the cursor to a position that
|
|
|
|
// does not exist in curbuf.
|
2023-02-04 10:58:34 +00:00
|
|
|
VIsual_active = aco->save_VIsual_active;
|
2019-01-26 16:21:07 +01:00
|
|
|
check_cursor();
|
|
|
|
}
|
|
|
|
}
|
2022-01-07 15:45:18 +00:00
|
|
|
|
|
|
|
VIsual_active = aco->save_VIsual_active;
|
2023-02-04 10:58:34 +00:00
|
|
|
check_cursor(); // just in case lines got deleted
|
2022-01-07 15:45:18 +00:00
|
|
|
if (VIsual_active)
|
|
|
|
check_pos(curbuf, &VIsual);
|
2019-01-26 16:21:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int autocmd_nested = FALSE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Execute autocommands for "event" and file name "fname".
|
|
|
|
* Return TRUE if some commands were executed.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
apply_autocmds(
|
|
|
|
event_T event,
|
|
|
|
char_u *fname, // NULL or empty means use actual file name
|
|
|
|
char_u *fname_io, // fname to use for <afile> on cmdline
|
|
|
|
int force, // when TRUE, ignore autocmd_busy
|
|
|
|
buf_T *buf) // buffer for <abuf>
|
|
|
|
{
|
|
|
|
return apply_autocmds_group(event, fname, fname_io, force,
|
|
|
|
AUGROUP_ALL, buf, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Like apply_autocmds(), but with extra "eap" argument. This takes care of
|
|
|
|
* setting v:filearg.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
apply_autocmds_exarg(
|
|
|
|
event_T event,
|
|
|
|
char_u *fname,
|
|
|
|
char_u *fname_io,
|
|
|
|
int force,
|
|
|
|
buf_T *buf,
|
|
|
|
exarg_T *eap)
|
|
|
|
{
|
|
|
|
return apply_autocmds_group(event, fname, fname_io, force,
|
|
|
|
AUGROUP_ALL, buf, eap);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Like apply_autocmds(), but handles the caller's retval. If the script
|
|
|
|
* processing is being aborted or if retval is FAIL when inside a try
|
|
|
|
* conditional, no autocommands are executed. If otherwise the autocommands
|
|
|
|
* cause the script to be aborted, retval is set to FAIL.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
apply_autocmds_retval(
|
|
|
|
event_T event,
|
|
|
|
char_u *fname, // NULL or empty means use actual file name
|
|
|
|
char_u *fname_io, // fname to use for <afile> on cmdline
|
|
|
|
int force, // when TRUE, ignore autocmd_busy
|
|
|
|
buf_T *buf, // buffer for <abuf>
|
|
|
|
int *retval) // pointer to caller's retval
|
|
|
|
{
|
|
|
|
int did_cmd;
|
|
|
|
|
|
|
|
#ifdef FEAT_EVAL
|
|
|
|
if (should_abort(*retval))
|
|
|
|
return FALSE;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
did_cmd = apply_autocmds_group(event, fname, fname_io, force,
|
|
|
|
AUGROUP_ALL, buf, NULL);
|
|
|
|
if (did_cmd
|
|
|
|
#ifdef FEAT_EVAL
|
|
|
|
&& aborting()
|
|
|
|
#endif
|
|
|
|
)
|
|
|
|
*retval = FAIL;
|
|
|
|
return did_cmd;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return TRUE when there is a CursorHold autocommand defined.
|
|
|
|
*/
|
2019-08-20 20:13:45 +02:00
|
|
|
static int
|
2019-01-26 16:21:07 +01:00
|
|
|
has_cursorhold(void)
|
|
|
|
{
|
2022-05-07 20:01:16 +01:00
|
|
|
return (first_autopat[(int)(get_real_state() == MODE_NORMAL_BUSY
|
2019-01-26 16:21:07 +01:00
|
|
|
? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return TRUE if the CursorHold event can be triggered.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
trigger_cursorhold(void)
|
|
|
|
{
|
|
|
|
int state;
|
|
|
|
|
|
|
|
if (!did_cursorhold
|
|
|
|
&& has_cursorhold()
|
|
|
|
&& reg_recording == 0
|
|
|
|
&& typebuf.tb_len == 0
|
2019-08-21 14:37:09 +02:00
|
|
|
&& !ins_compl_active())
|
2019-01-26 16:21:07 +01:00
|
|
|
{
|
|
|
|
state = get_real_state();
|
2022-05-07 20:01:16 +01:00
|
|
|
if (state == MODE_NORMAL_BUSY || (state & MODE_INSERT) != 0)
|
2019-01-26 16:21:07 +01:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2022-11-22 12:40:50 +00:00
|
|
|
/*
|
|
|
|
* Return TRUE when there is a WinResized autocommand defined.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
has_winresized(void)
|
|
|
|
{
|
|
|
|
return (first_autopat[(int)EVENT_WINRESIZED] != NULL);
|
|
|
|
}
|
|
|
|
|
2022-04-08 15:18:45 +01:00
|
|
|
/*
|
|
|
|
* Return TRUE when there is a WinScrolled autocommand defined.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
has_winscrolled(void)
|
|
|
|
{
|
|
|
|
return (first_autopat[(int)EVENT_WINSCROLLED] != NULL);
|
|
|
|
}
|
|
|
|
|
2019-01-26 16:21:07 +01:00
|
|
|
/*
|
|
|
|
* Return TRUE when there is a CursorMoved autocommand defined.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
has_cursormoved(void)
|
|
|
|
{
|
|
|
|
return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return TRUE when there is a CursorMovedI autocommand defined.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
has_cursormovedI(void)
|
|
|
|
{
|
|
|
|
return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return TRUE when there is a TextChanged autocommand defined.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
has_textchanged(void)
|
|
|
|
{
|
|
|
|
return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return TRUE when there is a TextChangedI autocommand defined.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
has_textchangedI(void)
|
|
|
|
{
|
|
|
|
return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return TRUE when there is a TextChangedP autocommand defined.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
has_textchangedP(void)
|
|
|
|
{
|
|
|
|
return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return TRUE when there is an InsertCharPre autocommand defined.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
has_insertcharpre(void)
|
|
|
|
{
|
|
|
|
return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return TRUE when there is an CmdUndefined autocommand defined.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
has_cmdundefined(void)
|
|
|
|
{
|
|
|
|
return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(FEAT_EVAL) || defined(PROTO)
|
|
|
|
/*
|
|
|
|
* Return TRUE when there is a TextYankPost autocommand defined.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
has_textyankpost(void)
|
|
|
|
{
|
|
|
|
return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2019-04-08 18:15:41 +02:00
|
|
|
#if defined(FEAT_EVAL) || defined(PROTO)
|
|
|
|
/*
|
|
|
|
* Return TRUE when there is a CompleteChanged autocommand defined.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
has_completechanged(void)
|
|
|
|
{
|
|
|
|
return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2021-09-12 13:39:55 +02:00
|
|
|
#if defined(FEAT_EVAL) || defined(PROTO)
|
|
|
|
/*
|
|
|
|
* Return TRUE when there is a ModeChanged autocommand defined.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
has_modechanged(void)
|
|
|
|
{
|
|
|
|
return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2019-01-26 16:21:07 +01:00
|
|
|
/*
|
|
|
|
* Execute autocommands for "event" and file name "fname".
|
|
|
|
* Return TRUE if some commands were executed.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
apply_autocmds_group(
|
|
|
|
event_T event,
|
|
|
|
char_u *fname, // NULL or empty means use actual file name
|
|
|
|
char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
|
|
|
|
// use fname
|
|
|
|
int force, // when TRUE, ignore autocmd_busy
|
|
|
|
int group, // group ID, or AUGROUP_ALL
|
|
|
|
buf_T *buf, // buffer for <abuf>
|
|
|
|
exarg_T *eap UNUSED) // command arguments
|
|
|
|
{
|
|
|
|
char_u *sfname = NULL; // short file name
|
|
|
|
char_u *tail;
|
|
|
|
int save_changed;
|
|
|
|
buf_T *old_curbuf;
|
|
|
|
int retval = FALSE;
|
|
|
|
char_u *save_autocmd_fname;
|
|
|
|
int save_autocmd_fname_full;
|
|
|
|
int save_autocmd_bufnr;
|
|
|
|
char_u *save_autocmd_match;
|
|
|
|
int save_autocmd_busy;
|
|
|
|
int save_autocmd_nested;
|
|
|
|
static int nesting = 0;
|
2022-04-14 15:39:43 +01:00
|
|
|
AutoPatCmd_T patcmd;
|
2019-01-26 16:21:07 +01:00
|
|
|
AutoPat *ap;
|
|
|
|
sctx_T save_current_sctx;
|
2020-12-28 18:26:00 +01:00
|
|
|
#ifdef FEAT_EVAL
|
2019-01-26 16:21:07 +01:00
|
|
|
funccal_entry_T funccal_entry;
|
|
|
|
char_u *save_cmdarg;
|
|
|
|
long save_cmdbang;
|
|
|
|
#endif
|
|
|
|
static int filechangeshell_busy = FALSE;
|
|
|
|
#ifdef FEAT_PROFILE
|
|
|
|
proftime_T wait_time;
|
|
|
|
#endif
|
|
|
|
int did_save_redobuff = FALSE;
|
|
|
|
save_redo_T save_redo;
|
|
|
|
int save_KeyTyped = KeyTyped;
|
2023-04-15 13:17:50 +01:00
|
|
|
ESTACK_CHECK_DECLARATION;
|
2019-01-26 16:21:07 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Quickly return if there are no autocommands for this event or
|
|
|
|
* autocommands are blocked.
|
|
|
|
*/
|
|
|
|
if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
|
|
|
|
|| autocmd_blocked > 0)
|
|
|
|
goto BYPASS_AU;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When autocommands are busy, new autocommands are only executed when
|
|
|
|
* explicitly enabled with the "nested" flag.
|
|
|
|
*/
|
|
|
|
if (autocmd_busy && !(force || autocmd_nested))
|
|
|
|
goto BYPASS_AU;
|
|
|
|
|
|
|
|
#ifdef FEAT_EVAL
|
|
|
|
/*
|
|
|
|
* Quickly return when immediately aborting on error, or when an interrupt
|
|
|
|
* occurred or an exception was thrown but not caught.
|
|
|
|
*/
|
|
|
|
if (aborting())
|
|
|
|
goto BYPASS_AU;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FileChangedShell never nests, because it can create an endless loop.
|
|
|
|
*/
|
|
|
|
if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
|
|
|
|
|| event == EVENT_FILECHANGEDSHELLPOST))
|
|
|
|
goto BYPASS_AU;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ignore events in 'eventignore'.
|
|
|
|
*/
|
|
|
|
if (event_ignored(event))
|
|
|
|
goto BYPASS_AU;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allow nesting of autocommands, but restrict the depth, because it's
|
|
|
|
* possible to create an endless loop.
|
|
|
|
*/
|
|
|
|
if (nesting == 10)
|
|
|
|
{
|
2021-12-31 18:49:43 +00:00
|
|
|
emsg(_(e_autocommand_nesting_too_deep));
|
2019-01-26 16:21:07 +01:00
|
|
|
goto BYPASS_AU;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if these autocommands are disabled. Used when doing ":all" or
|
|
|
|
* ":ball".
|
|
|
|
*/
|
|
|
|
if ( (autocmd_no_enter
|
|
|
|
&& (event == EVENT_WINENTER || event == EVENT_BUFENTER))
|
|
|
|
|| (autocmd_no_leave
|
|
|
|
&& (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
|
|
|
|
goto BYPASS_AU;
|
|
|
|
|
2022-12-09 12:21:50 +00:00
|
|
|
if (event == EVENT_CMDLINECHANGED)
|
|
|
|
++aucmd_cmdline_changed_count;
|
|
|
|
|
2019-01-26 16:21:07 +01:00
|
|
|
/*
|
|
|
|
* Save the autocmd_* variables and info about the current buffer.
|
|
|
|
*/
|
|
|
|
save_autocmd_fname = autocmd_fname;
|
|
|
|
save_autocmd_fname_full = autocmd_fname_full;
|
|
|
|
save_autocmd_bufnr = autocmd_bufnr;
|
|
|
|
save_autocmd_match = autocmd_match;
|
|
|
|
save_autocmd_busy = autocmd_busy;
|
|
|
|
save_autocmd_nested = autocmd_nested;
|
|
|
|
save_changed = curbuf->b_changed;
|
|
|
|
old_curbuf = curbuf;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the file name to be used for <afile>.
|
|
|
|
* Make a copy to avoid that changing a buffer name or directory makes it
|
|
|
|
* invalid.
|
|
|
|
*/
|
|
|
|
if (fname_io == NULL)
|
|
|
|
{
|
|
|
|
if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
|
2022-12-09 12:21:50 +00:00
|
|
|
|| event == EVENT_OPTIONSET
|
2024-01-14 20:19:59 +01:00
|
|
|
|| event == EVENT_MODECHANGED
|
|
|
|
|| event == EVENT_TERMRESPONSEALL)
|
2019-01-26 16:21:07 +01:00
|
|
|
autocmd_fname = NULL;
|
|
|
|
else if (fname != NULL && !ends_excmd(*fname))
|
|
|
|
autocmd_fname = fname;
|
|
|
|
else if (buf != NULL)
|
|
|
|
autocmd_fname = buf->b_ffname;
|
|
|
|
else
|
|
|
|
autocmd_fname = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
autocmd_fname = fname_io;
|
|
|
|
if (autocmd_fname != NULL)
|
|
|
|
autocmd_fname = vim_strsave(autocmd_fname);
|
|
|
|
autocmd_fname_full = FALSE; // call FullName_save() later
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the buffer number to be used for <abuf>.
|
|
|
|
*/
|
|
|
|
if (buf == NULL)
|
|
|
|
autocmd_bufnr = 0;
|
|
|
|
else
|
|
|
|
autocmd_bufnr = buf->b_fnum;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When the file name is NULL or empty, use the file name of buffer "buf".
|
|
|
|
* Always use the full path of the file name to match with, in case
|
|
|
|
* "allow_dirs" is set.
|
|
|
|
*/
|
|
|
|
if (fname == NULL || *fname == NUL)
|
|
|
|
{
|
|
|
|
if (buf == NULL)
|
|
|
|
fname = NULL;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#ifdef FEAT_SYN_HL
|
|
|
|
if (event == EVENT_SYNTAX)
|
|
|
|
fname = buf->b_p_syn;
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
if (event == EVENT_FILETYPE)
|
|
|
|
fname = buf->b_p_ft;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (buf->b_sfname != NULL)
|
|
|
|
sfname = vim_strsave(buf->b_sfname);
|
|
|
|
fname = buf->b_ffname;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (fname == NULL)
|
|
|
|
fname = (char_u *)"";
|
|
|
|
fname = vim_strsave(fname); // make a copy, so we can change it
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sfname = vim_strsave(fname);
|
|
|
|
// Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
|
2022-02-11 16:30:12 +00:00
|
|
|
// ColorScheme, QuickFixCmd*, DirChanged and similar.
|
2019-01-26 16:21:07 +01:00
|
|
|
if (event == EVENT_FILETYPE
|
|
|
|
|| event == EVENT_SYNTAX
|
|
|
|
|| event == EVENT_CMDLINECHANGED
|
|
|
|
|| event == EVENT_CMDLINEENTER
|
|
|
|
|| event == EVENT_CMDLINELEAVE
|
|
|
|
|| event == EVENT_CMDWINENTER
|
|
|
|
|| event == EVENT_CMDWINLEAVE
|
|
|
|
|| event == EVENT_CMDUNDEFINED
|
|
|
|
|| event == EVENT_FUNCUNDEFINED
|
|
|
|
|| event == EVENT_REMOTEREPLY
|
|
|
|
|| event == EVENT_SPELLFILEMISSING
|
|
|
|
|| event == EVENT_QUICKFIXCMDPRE
|
|
|
|
|| event == EVENT_COLORSCHEME
|
|
|
|
|| event == EVENT_COLORSCHEMEPRE
|
|
|
|
|| event == EVENT_OPTIONSET
|
|
|
|
|| event == EVENT_QUICKFIXCMDPOST
|
2021-09-12 13:39:55 +02:00
|
|
|
|| event == EVENT_DIRCHANGED
|
2022-02-11 16:30:12 +00:00
|
|
|
|| event == EVENT_DIRCHANGEDPRE
|
2021-11-13 12:38:49 +00:00
|
|
|
|| event == EVENT_MODECHANGED
|
2022-10-10 13:46:15 +01:00
|
|
|
|| event == EVENT_MENUPOPUP
|
2022-02-11 16:30:12 +00:00
|
|
|
|| event == EVENT_USER
|
2022-04-08 15:18:45 +01:00
|
|
|
|| event == EVENT_WINCLOSED
|
2022-11-22 12:40:50 +00:00
|
|
|
|| event == EVENT_WINRESIZED
|
2024-01-14 20:19:59 +01:00
|
|
|
|| event == EVENT_WINSCROLLED
|
|
|
|
|| event == EVENT_TERMRESPONSEALL)
|
2019-01-26 16:21:07 +01:00
|
|
|
{
|
|
|
|
fname = vim_strsave(fname);
|
|
|
|
autocmd_fname_full = TRUE; // don't expand it later
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fname = FullName_save(fname, FALSE);
|
|
|
|
}
|
|
|
|
if (fname == NULL) // out of memory
|
|
|
|
{
|
|
|
|
vim_free(sfname);
|
|
|
|
retval = FALSE;
|
|
|
|
goto BYPASS_AU;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef BACKSLASH_IN_FILENAME
|
|
|
|
/*
|
|
|
|
* Replace all backslashes with forward slashes. This makes the
|
|
|
|
* autocommand patterns portable between Unix and MS-DOS.
|
|
|
|
*/
|
|
|
|
if (sfname != NULL)
|
|
|
|
forward_slash(sfname);
|
|
|
|
forward_slash(fname);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef VMS
|
|
|
|
// remove version for correct match
|
|
|
|
if (sfname != NULL)
|
|
|
|
vms_remove_version(sfname);
|
|
|
|
vms_remove_version(fname);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the name to be used for <amatch>.
|
|
|
|
*/
|
|
|
|
autocmd_match = fname;
|
|
|
|
|
|
|
|
|
|
|
|
// Don't redraw while doing autocommands.
|
|
|
|
++RedrawingDisabled;
|
2019-12-29 23:04:25 +01:00
|
|
|
|
|
|
|
// name and lnum are filled in later
|
|
|
|
estack_push(ETYPE_AUCMD, NULL, 0);
|
2023-04-15 13:17:50 +01:00
|
|
|
ESTACK_CHECK_SETUP;
|
2019-01-26 16:21:07 +01:00
|
|
|
|
|
|
|
save_current_sctx = current_sctx;
|
|
|
|
|
2020-12-28 18:26:00 +01:00
|
|
|
#ifdef FEAT_EVAL
|
2019-01-26 16:21:07 +01:00
|
|
|
# ifdef FEAT_PROFILE
|
|
|
|
if (do_profiling == PROF_YES)
|
|
|
|
prof_child_enter(&wait_time); // doesn't count for the caller itself
|
|
|
|
# endif
|
|
|
|
|
|
|
|
// Don't use local function variables, if called from a function.
|
|
|
|
save_funccal(&funccal_entry);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When starting to execute autocommands, save the search patterns.
|
|
|
|
*/
|
|
|
|
if (!autocmd_busy)
|
|
|
|
{
|
|
|
|
save_search_patterns();
|
|
|
|
if (!ins_compl_active())
|
|
|
|
{
|
|
|
|
saveRedobuff(&save_redo);
|
|
|
|
did_save_redobuff = TRUE;
|
|
|
|
}
|
|
|
|
did_filetype = keep_filetype;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note that we are applying autocmds. Some commands need to know.
|
|
|
|
*/
|
|
|
|
autocmd_busy = TRUE;
|
|
|
|
filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
|
|
|
|
++nesting; // see matching decrement below
|
|
|
|
|
|
|
|
// Remember that FileType was triggered. Used for did_filetype().
|
|
|
|
if (event == EVENT_FILETYPE)
|
|
|
|
did_filetype = TRUE;
|
|
|
|
|
|
|
|
tail = gettail(fname);
|
|
|
|
|
|
|
|
// Find first autocommand that matches
|
2022-04-14 15:39:43 +01:00
|
|
|
CLEAR_FIELD(patcmd);
|
2019-01-26 16:21:07 +01:00
|
|
|
patcmd.curpat = first_autopat[(int)event];
|
|
|
|
patcmd.group = group;
|
|
|
|
patcmd.fname = fname;
|
|
|
|
patcmd.sfname = sfname;
|
|
|
|
patcmd.tail = tail;
|
|
|
|
patcmd.event = event;
|
|
|
|
patcmd.arg_bufnr = autocmd_bufnr;
|
|
|
|
auto_next_pat(&patcmd, FALSE);
|
|
|
|
|
|
|
|
// found one, start executing the autocommands
|
|
|
|
if (patcmd.curpat != NULL)
|
|
|
|
{
|
|
|
|
// add to active_apc_list
|
|
|
|
patcmd.next = active_apc_list;
|
|
|
|
active_apc_list = &patcmd;
|
|
|
|
|
|
|
|
#ifdef FEAT_EVAL
|
|
|
|
// set v:cmdarg (only when there is a matching pattern)
|
|
|
|
save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
|
|
|
|
if (eap != NULL)
|
|
|
|
{
|
|
|
|
save_cmdarg = set_cmdarg(eap, NULL);
|
|
|
|
set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
save_cmdarg = NULL; // avoid gcc warning
|
|
|
|
#endif
|
|
|
|
retval = TRUE;
|
|
|
|
// mark the last pattern, to avoid an endless loop when more patterns
|
|
|
|
// are added when executing autocommands
|
|
|
|
for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
|
|
|
|
ap->last = FALSE;
|
|
|
|
ap->last = TRUE;
|
2019-04-25 22:22:01 +02:00
|
|
|
|
2022-07-23 09:06:48 +01:00
|
|
|
// Make sure cursor and topline are valid. The first time the current
|
|
|
|
// values are saved, restored by reset_lnums(). When nested only the
|
|
|
|
// values are corrected when needed.
|
2021-04-03 13:19:26 +02:00
|
|
|
if (nesting == 1)
|
|
|
|
check_lnums(TRUE);
|
2022-07-23 09:06:48 +01:00
|
|
|
else
|
|
|
|
check_lnums_nested(TRUE);
|
2019-04-25 22:22:01 +02:00
|
|
|
|
2023-06-25 22:34:22 +01:00
|
|
|
int save_did_emsg = did_emsg;
|
|
|
|
int save_ex_pressedreturn = get_pressedreturn();
|
2021-12-17 09:44:33 +00:00
|
|
|
|
2019-01-26 16:21:07 +01:00
|
|
|
do_cmdline(NULL, getnextac, (void *)&patcmd,
|
|
|
|
DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
|
2019-04-25 22:22:01 +02:00
|
|
|
|
2021-12-17 09:44:33 +00:00
|
|
|
did_emsg += save_did_emsg;
|
2023-06-25 22:34:22 +01:00
|
|
|
set_pressedreturn(save_ex_pressedreturn);
|
2021-12-17 09:44:33 +00:00
|
|
|
|
2021-04-03 13:19:26 +02:00
|
|
|
if (nesting == 1)
|
|
|
|
// restore cursor and topline, unless they were changed
|
|
|
|
reset_lnums();
|
2019-04-25 22:22:01 +02:00
|
|
|
|
2019-01-26 16:21:07 +01:00
|
|
|
#ifdef FEAT_EVAL
|
|
|
|
if (eap != NULL)
|
|
|
|
{
|
|
|
|
(void)set_cmdarg(NULL, save_cmdarg);
|
|
|
|
set_vim_var_nr(VV_CMDBANG, save_cmdbang);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
// delete from active_apc_list
|
|
|
|
if (active_apc_list == &patcmd) // just in case
|
|
|
|
active_apc_list = patcmd.next;
|
|
|
|
}
|
|
|
|
|
2023-05-20 14:07:00 +01:00
|
|
|
if (RedrawingDisabled > 0)
|
|
|
|
--RedrawingDisabled;
|
2019-01-26 16:21:07 +01:00
|
|
|
autocmd_busy = save_autocmd_busy;
|
|
|
|
filechangeshell_busy = FALSE;
|
|
|
|
autocmd_nested = save_autocmd_nested;
|
2019-12-29 23:04:25 +01:00
|
|
|
vim_free(SOURCING_NAME);
|
2023-04-15 13:17:50 +01:00
|
|
|
ESTACK_CHECK_NOW;
|
2019-12-29 23:04:25 +01:00
|
|
|
estack_pop();
|
2019-01-26 16:21:07 +01:00
|
|
|
vim_free(autocmd_fname);
|
|
|
|
autocmd_fname = save_autocmd_fname;
|
|
|
|
autocmd_fname_full = save_autocmd_fname_full;
|
|
|
|
autocmd_bufnr = save_autocmd_bufnr;
|
|
|
|
autocmd_match = save_autocmd_match;
|
|
|
|
current_sctx = save_current_sctx;
|
2020-12-28 18:26:00 +01:00
|
|
|
#ifdef FEAT_EVAL
|
2019-01-26 16:21:07 +01:00
|
|
|
restore_funccal();
|
|
|
|
# ifdef FEAT_PROFILE
|
|
|
|
if (do_profiling == PROF_YES)
|
|
|
|
prof_child_exit(&wait_time);
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
KeyTyped = save_KeyTyped;
|
|
|
|
vim_free(fname);
|
|
|
|
vim_free(sfname);
|
|
|
|
--nesting; // see matching increment above
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When stopping to execute autocommands, restore the search patterns and
|
|
|
|
* the redo buffer. Free any buffers in the au_pending_free_buf list and
|
|
|
|
* free any windows in the au_pending_free_win list.
|
|
|
|
*/
|
|
|
|
if (!autocmd_busy)
|
|
|
|
{
|
|
|
|
restore_search_patterns();
|
|
|
|
if (did_save_redobuff)
|
|
|
|
restoreRedobuff(&save_redo);
|
|
|
|
did_filetype = FALSE;
|
|
|
|
while (au_pending_free_buf != NULL)
|
|
|
|
{
|
|
|
|
buf_T *b = au_pending_free_buf->b_next;
|
2021-03-13 15:47:56 +01:00
|
|
|
|
2019-01-26 16:21:07 +01:00
|
|
|
vim_free(au_pending_free_buf);
|
|
|
|
au_pending_free_buf = b;
|
|
|
|
}
|
|
|
|
while (au_pending_free_win != NULL)
|
|
|
|
{
|
|
|
|
win_T *w = au_pending_free_win->w_next;
|
2021-03-13 15:47:56 +01:00
|
|
|
|
2019-01-26 16:21:07 +01:00
|
|
|
vim_free(au_pending_free_win);
|
|
|
|
au_pending_free_win = w;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Some events don't set or reset the Changed flag.
|
|
|
|
* Check if still in the same buffer!
|
|
|
|
*/
|
|
|
|
if (curbuf == old_curbuf
|
|
|
|
&& (event == EVENT_BUFREADPOST
|
|
|
|
|| event == EVENT_BUFWRITEPOST
|
|
|
|
|| event == EVENT_FILEAPPENDPOST
|
|
|
|
|| event == EVENT_VIMLEAVE
|
|
|
|
|| event == EVENT_VIMLEAVEPRE))
|
|
|
|
{
|
|
|
|
if (curbuf->b_changed != save_changed)
|
|
|
|
need_maketitle = TRUE;
|
|
|
|
curbuf->b_changed = save_changed;
|
|
|
|
}
|
|
|
|
|
|
|
|
au_cleanup(); // may really delete removed patterns/commands now
|
|
|
|
|
|
|
|
BYPASS_AU:
|
|
|
|
// When wiping out a buffer make sure all its buffer-local autocommands
|
|
|
|
// are deleted.
|
|
|
|
if (event == EVENT_BUFWIPEOUT && buf != NULL)
|
|
|
|
aubuflocal_remove(buf);
|
|
|
|
|
|
|
|
if (retval == OK && event == EVENT_FILETYPE)
|
|
|
|
au_did_filetype = TRUE;
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
# ifdef FEAT_EVAL
|
|
|
|
static char_u *old_termresponse = NULL;
|
2024-01-14 20:19:59 +01:00
|
|
|
static char_u *old_termu7resp = NULL;
|
|
|
|
static char_u *old_termblinkresp = NULL;
|
|
|
|
static char_u *old_termrbgresp = NULL;
|
|
|
|
static char_u *old_termrfgresp = NULL;
|
|
|
|
static char_u *old_termstyleresp = NULL;
|
2019-01-26 16:21:07 +01:00
|
|
|
# endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Block triggering autocommands until unblock_autocmd() is called.
|
|
|
|
* Can be used recursively, so long as it's symmetric.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
block_autocmds(void)
|
|
|
|
{
|
|
|
|
# ifdef FEAT_EVAL
|
|
|
|
// Remember the value of v:termresponse.
|
|
|
|
if (autocmd_blocked == 0)
|
2024-01-14 20:19:59 +01:00
|
|
|
{
|
2019-01-26 16:21:07 +01:00
|
|
|
old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
|
2024-01-14 20:19:59 +01:00
|
|
|
old_termu7resp = get_vim_var_str(VV_TERMU7RESP);
|
|
|
|
old_termblinkresp = get_vim_var_str(VV_TERMBLINKRESP);
|
|
|
|
old_termrbgresp = get_vim_var_str(VV_TERMRBGRESP);
|
|
|
|
old_termrfgresp = get_vim_var_str(VV_TERMRFGRESP);
|
|
|
|
old_termstyleresp = get_vim_var_str(VV_TERMSTYLERESP);
|
|
|
|
}
|
2019-01-26 16:21:07 +01:00
|
|
|
# endif
|
|
|
|
++autocmd_blocked;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
unblock_autocmds(void)
|
|
|
|
{
|
|
|
|
--autocmd_blocked;
|
|
|
|
|
|
|
|
# ifdef FEAT_EVAL
|
2024-01-14 20:19:59 +01:00
|
|
|
// When v:termresponse, etc, were set while autocommands were blocked,
|
|
|
|
// trigger the autocommands now. Esp. useful when executing a shell
|
|
|
|
// command during startup (vimdiff).
|
|
|
|
if (autocmd_blocked == 0)
|
|
|
|
{
|
|
|
|
if (get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
|
|
|
|
{
|
|
|
|
apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
|
|
|
|
apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"version", NULL, FALSE, curbuf);
|
|
|
|
}
|
|
|
|
if (get_vim_var_str(VV_TERMU7RESP) != old_termu7resp)
|
|
|
|
{
|
|
|
|
apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"ambiguouswidth", NULL, FALSE, curbuf);
|
|
|
|
}
|
|
|
|
if (get_vim_var_str(VV_TERMBLINKRESP) != old_termblinkresp)
|
|
|
|
{
|
|
|
|
apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"cursorblink", NULL, FALSE, curbuf);
|
|
|
|
}
|
|
|
|
if (get_vim_var_str(VV_TERMRBGRESP) != old_termrbgresp)
|
|
|
|
{
|
|
|
|
apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"background", NULL, FALSE, curbuf);
|
|
|
|
}
|
|
|
|
if (get_vim_var_str(VV_TERMRFGRESP) != old_termrfgresp)
|
|
|
|
{
|
|
|
|
apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"foreground", NULL, FALSE, curbuf);
|
|
|
|
}
|
|
|
|
if (get_vim_var_str(VV_TERMSTYLERESP) != old_termstyleresp)
|
|
|
|
{
|
|
|
|
apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"cursorshape", NULL, FALSE, curbuf);
|
|
|
|
}
|
|
|
|
}
|
2019-01-26 16:21:07 +01:00
|
|
|
# endif
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
is_autocmd_blocked(void)
|
|
|
|
{
|
|
|
|
return autocmd_blocked != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find next autocommand pattern that matches.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
auto_next_pat(
|
2022-04-14 15:39:43 +01:00
|
|
|
AutoPatCmd_T *apc,
|
2019-01-26 16:21:07 +01:00
|
|
|
int stop_at_last) // stop when 'last' flag is set
|
|
|
|
{
|
|
|
|
AutoPat *ap;
|
|
|
|
AutoCmd *cp;
|
|
|
|
char_u *name;
|
|
|
|
char *s;
|
2022-04-14 15:39:43 +01:00
|
|
|
estack_T *entry;
|
|
|
|
char_u *namep;
|
|
|
|
|
|
|
|
entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
|
2019-01-26 16:21:07 +01:00
|
|
|
|
2022-04-14 15:39:43 +01:00
|
|
|
// Clear the exestack entry for this ETYPE_AUCMD entry.
|
|
|
|
VIM_CLEAR(entry->es_name);
|
|
|
|
entry->es_info.aucmd = NULL;
|
2019-01-26 16:21:07 +01:00
|
|
|
|
|
|
|
for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
|
|
|
|
{
|
|
|
|
apc->curpat = NULL;
|
|
|
|
|
|
|
|
// Only use a pattern when it has not been removed, has commands and
|
|
|
|
// the group matches. For buffer-local autocommands only check the
|
|
|
|
// buffer number.
|
|
|
|
if (ap->pat != NULL && ap->cmds != NULL
|
|
|
|
&& (apc->group == AUGROUP_ALL || apc->group == ap->group))
|
|
|
|
{
|
|
|
|
// execution-condition
|
|
|
|
if (ap->buflocal_nr == 0
|
|
|
|
? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
|
|
|
|
apc->sfname, apc->tail, ap->allow_dirs))
|
|
|
|
: ap->buflocal_nr == apc->arg_bufnr)
|
|
|
|
{
|
|
|
|
name = event_nr2name(apc->event);
|
|
|
|
s = _("%s Autocommands for \"%s\"");
|
2022-04-14 15:39:43 +01:00
|
|
|
namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1);
|
|
|
|
if (namep != NULL)
|
2019-01-26 16:21:07 +01:00
|
|
|
{
|
2022-04-14 15:39:43 +01:00
|
|
|
sprintf((char *)namep, s, (char *)name, (char *)ap->pat);
|
2019-01-26 16:21:07 +01:00
|
|
|
if (p_verbose >= 8)
|
|
|
|
{
|
|
|
|
verbose_enter();
|
2022-04-14 15:39:43 +01:00
|
|
|
smsg(_("Executing %s"), namep);
|
2019-01-26 16:21:07 +01:00
|
|
|
verbose_leave();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-14 15:39:43 +01:00
|
|
|
// Update the exestack entry for this autocmd.
|
|
|
|
entry->es_name = namep;
|
|
|
|
entry->es_info.aucmd = apc;
|
|
|
|
|
2019-01-26 16:21:07 +01:00
|
|
|
apc->curpat = ap;
|
|
|
|
apc->nextcmd = ap->cmds;
|
|
|
|
// mark last command
|
|
|
|
for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
|
|
|
|
cp->last = FALSE;
|
|
|
|
cp->last = TRUE;
|
|
|
|
}
|
|
|
|
line_breakcheck();
|
|
|
|
if (apc->curpat != NULL) // found a match
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (stop_at_last && ap->last)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-12 21:20:59 +00:00
|
|
|
#if defined(FEAT_EVAL) || defined(PROTO)
|
2022-04-14 15:39:43 +01:00
|
|
|
/*
|
|
|
|
* Get the script context where autocommand "acp" is defined.
|
|
|
|
*/
|
|
|
|
sctx_T *
|
|
|
|
acp_script_ctx(AutoPatCmd_T *acp)
|
|
|
|
{
|
|
|
|
return &acp->script_ctx;
|
|
|
|
}
|
2023-03-12 21:20:59 +00:00
|
|
|
#endif
|
2022-04-14 15:39:43 +01:00
|
|
|
|
2019-01-26 16:21:07 +01:00
|
|
|
/*
|
|
|
|
* Get next autocommand command.
|
|
|
|
* Called by do_cmdline() to get the next line for ":if".
|
|
|
|
* Returns allocated string, or NULL for end of autocommands.
|
|
|
|
*/
|
|
|
|
char_u *
|
2020-08-20 15:02:42 +02:00
|
|
|
getnextac(
|
|
|
|
int c UNUSED,
|
|
|
|
void *cookie,
|
|
|
|
int indent UNUSED,
|
|
|
|
getline_opt_T options UNUSED)
|
2019-01-26 16:21:07 +01:00
|
|
|
{
|
2022-04-14 15:39:43 +01:00
|
|
|
AutoPatCmd_T *acp = (AutoPatCmd_T *)cookie;
|
2019-01-26 16:21:07 +01:00
|
|
|
char_u *retval;
|
|
|
|
AutoCmd *ac;
|
|
|
|
|
|
|
|
// Can be called again after returning the last line.
|
|
|
|
if (acp->curpat == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
// repeat until we find an autocommand to execute
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
// skip removed commands
|
|
|
|
while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
|
|
|
|
if (acp->nextcmd->last)
|
|
|
|
acp->nextcmd = NULL;
|
|
|
|
else
|
|
|
|
acp->nextcmd = acp->nextcmd->next;
|
|
|
|
|
|
|
|
if (acp->nextcmd != NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
// at end of commands, find next pattern that matches
|
|
|
|
if (acp->curpat->last)
|
|
|
|
acp->curpat = NULL;
|
|
|
|
else
|
|
|
|
acp->curpat = acp->curpat->next;
|
|
|
|
if (acp->curpat != NULL)
|
|
|
|
auto_next_pat(acp, TRUE);
|
|
|
|
if (acp->curpat == NULL)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ac = acp->nextcmd;
|
|
|
|
|
|
|
|
if (p_verbose >= 9)
|
|
|
|
{
|
|
|
|
verbose_enter_scroll();
|
|
|
|
smsg(_("autocommand %s"), ac->cmd);
|
|
|
|
msg_puts("\n"); // don't overwrite this either
|
|
|
|
verbose_leave_scroll();
|
|
|
|
}
|
|
|
|
retval = vim_strsave(ac->cmd);
|
2019-04-04 15:04:56 +02:00
|
|
|
// Remove one-shot ("once") autocmd in anticipation of its execution.
|
|
|
|
if (ac->once)
|
|
|
|
au_del_cmd(ac);
|
2019-01-26 16:21:07 +01:00
|
|
|
autocmd_nested = ac->nested;
|
|
|
|
current_sctx = ac->script_ctx;
|
2022-04-14 15:39:43 +01:00
|
|
|
acp->script_ctx = current_sctx;
|
2019-01-26 16:21:07 +01:00
|
|
|
if (ac->last)
|
|
|
|
acp->nextcmd = NULL;
|
|
|
|
else
|
|
|
|
acp->nextcmd = ac->next;
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return TRUE if there is a matching autocommand for "fname".
|
|
|
|
* To account for buffer-local autocommands, function needs to know
|
|
|
|
* in which buffer the file will be opened.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
has_autocmd(event_T event, char_u *sfname, buf_T *buf)
|
|
|
|
{
|
|
|
|
AutoPat *ap;
|
|
|
|
char_u *fname;
|
|
|
|
char_u *tail = gettail(sfname);
|
|
|
|
int retval = FALSE;
|
|
|
|
|
|
|
|
fname = FullName_save(sfname, FALSE);
|
|
|
|
if (fname == NULL)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
#ifdef BACKSLASH_IN_FILENAME
|
|
|
|
/*
|
|
|
|
* Replace all backslashes with forward slashes. This makes the
|
|
|
|
* autocommand patterns portable between Unix and MS-DOS.
|
|
|
|
*/
|
|
|
|
sfname = vim_strsave(sfname);
|
|
|
|
if (sfname != NULL)
|
|
|
|
forward_slash(sfname);
|
|
|
|
forward_slash(fname);
|
|
|
|
#endif
|
|
|
|
|
2020-04-02 18:50:46 +02:00
|
|
|
FOR_ALL_AUTOCMD_PATTERNS(event, ap)
|
2019-01-26 16:21:07 +01:00
|
|
|
if (ap->pat != NULL && ap->cmds != NULL
|
|
|
|
&& (ap->buflocal_nr == 0
|
|
|
|
? match_file_pat(NULL, &ap->reg_prog,
|
|
|
|
fname, sfname, tail, ap->allow_dirs)
|
|
|
|
: buf != NULL && ap->buflocal_nr == buf->b_fnum
|
|
|
|
))
|
|
|
|
{
|
|
|
|
retval = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
vim_free(fname);
|
|
|
|
#ifdef BACKSLASH_IN_FILENAME
|
|
|
|
vim_free(sfname);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Function given to ExpandGeneric() to obtain the list of autocommand group
|
|
|
|
* names.
|
|
|
|
*/
|
|
|
|
char_u *
|
|
|
|
get_augroup_name(expand_T *xp UNUSED, int idx)
|
|
|
|
{
|
|
|
|
if (idx == augroups.ga_len) // add "END" add the end
|
|
|
|
return (char_u *)"END";
|
2022-05-19 10:31:47 +01:00
|
|
|
if (idx < 0 || idx >= augroups.ga_len) // end of list
|
2019-01-26 16:21:07 +01:00
|
|
|
return NULL;
|
|
|
|
if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
|
|
|
|
// skip deleted entries
|
|
|
|
return (char_u *)"";
|
|
|
|
return AUGROUP_NAME(idx); // return a name
|
|
|
|
}
|
|
|
|
|
|
|
|
static int include_groups = FALSE;
|
|
|
|
|
|
|
|
char_u *
|
|
|
|
set_context_in_autocmd(
|
|
|
|
expand_T *xp,
|
|
|
|
char_u *arg,
|
|
|
|
int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
|
|
|
|
{
|
|
|
|
char_u *p;
|
|
|
|
int group;
|
|
|
|
|
|
|
|
// check for a group name, skip it if present
|
|
|
|
include_groups = FALSE;
|
|
|
|
p = arg;
|
|
|
|
group = au_get_grouparg(&arg);
|
|
|
|
if (group == AUGROUP_ERROR)
|
|
|
|
return NULL;
|
|
|
|
// If there only is a group name that's what we expand.
|
|
|
|
if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
|
|
|
|
{
|
|
|
|
arg = p;
|
|
|
|
group = AUGROUP_ALL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// skip over event name
|
|
|
|
for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
|
|
|
|
if (*p == ',')
|
|
|
|
arg = p + 1;
|
|
|
|
if (*p == NUL)
|
|
|
|
{
|
|
|
|
if (group == AUGROUP_ALL)
|
|
|
|
include_groups = TRUE;
|
|
|
|
xp->xp_context = EXPAND_EVENTS; // expand event name
|
|
|
|
xp->xp_pattern = arg;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// skip over pattern
|
|
|
|
arg = skipwhite(p);
|
|
|
|
while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
|
|
|
|
arg++;
|
|
|
|
if (*arg)
|
|
|
|
return arg; // expand (next) command
|
|
|
|
|
|
|
|
if (doautocmd)
|
|
|
|
xp->xp_context = EXPAND_FILES; // expand file names
|
|
|
|
else
|
|
|
|
xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Function given to ExpandGeneric() to obtain the list of event names.
|
|
|
|
*/
|
|
|
|
char_u *
|
|
|
|
get_event_name(expand_T *xp UNUSED, int idx)
|
|
|
|
{
|
|
|
|
if (idx < augroups.ga_len) // First list group names, if wanted
|
|
|
|
{
|
|
|
|
if (!include_groups || AUGROUP_NAME(idx) == NULL
|
|
|
|
|| AUGROUP_NAME(idx) == get_deleted_augroup())
|
|
|
|
return (char_u *)""; // skip deleted entries
|
|
|
|
return AUGROUP_NAME(idx); // return a name
|
|
|
|
}
|
|
|
|
return (char_u *)event_names[idx - augroups.ga_len].name;
|
|
|
|
}
|
|
|
|
|
patch 9.0.1958: cannot complete option values
Problem: cannot complete option values
Solution: Add completion functions for several options
Add cmdline tab-completion for setting string options
Add tab-completion for setting string options on the cmdline using
`:set=` (along with `:set+=` and `:set-=`).
The existing tab completion for setting options currently only works
when nothing is typed yet, and it only fills in with the existing value,
e.g. when the user does `:set diffopt=<Tab>` it will be completed to
`set diffopt=internal,filler,closeoff` and nothing else. This isn't too
useful as a user usually wants auto-complete to suggest all the possible
values, such as 'iblank', or 'algorithm:patience'.
For set= and set+=, this adds a new optional callback function for each
option that can be invoked when doing completion. This allows for each
option to have control over how completion works. For example, in
'diffopt', it will suggest the default enumeration, but if `algorithm:`
is selected, it will further suggest different algorithm types like
'meyers' and 'patience'. When using set=, the existing option value will
be filled in as the first choice to preserve the existing behavior. When
using set+= this won't happen as it doesn't make sense.
For flag list options (e.g. 'mouse' and 'guioptions'), completion will
take into account existing typed values (and in the case of set+=, the
existing option value) to make sure it doesn't suggest duplicates.
For set-=, there is a new `ExpandSettingSubtract` function which will
handle flag list and comma-separated options smartly, by only suggesting
values that currently exist in the option.
Note that Vim has some existing code that adds special handling for
'filetype', 'syntax', and misc dir options like 'backupdir'. This change
preserves them as they already work, instead of converting to the new
callback API for each option.
closes: #13182
Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Yee Cheng Chin <ychin.git@gmail.com>
2023-09-29 20:42:32 +02:00
|
|
|
/*
|
|
|
|
* Function given to ExpandGeneric() to obtain the list of event names. Don't
|
|
|
|
* include groups.
|
|
|
|
*/
|
|
|
|
char_u *
|
|
|
|
get_event_name_no_group(expand_T *xp UNUSED, int idx)
|
|
|
|
{
|
|
|
|
return (char_u *)event_names[idx].name;
|
|
|
|
}
|
|
|
|
|
2019-01-26 16:21:07 +01:00
|
|
|
|
|
|
|
#if defined(FEAT_EVAL) || defined(PROTO)
|
|
|
|
/*
|
|
|
|
* Return TRUE if autocmd is supported.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
autocmd_supported(char_u *name)
|
|
|
|
{
|
|
|
|
char_u *p;
|
|
|
|
|
|
|
|
return (event_name2nr(name, &p) != NUM_EVENTS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return TRUE if an autocommand is defined for a group, event and
|
|
|
|
* pattern: The group can be omitted to accept any group. "event" and "pattern"
|
|
|
|
* can be NULL to accept any event and pattern. "pattern" can be NULL to accept
|
|
|
|
* any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
|
|
|
|
* Used for:
|
|
|
|
* exists("#Group") or
|
|
|
|
* exists("#Group#Event") or
|
|
|
|
* exists("#Group#Event#pat") or
|
|
|
|
* exists("#Event") or
|
|
|
|
* exists("#Event#pat")
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
au_exists(char_u *arg)
|
|
|
|
{
|
|
|
|
char_u *arg_save;
|
|
|
|
char_u *pattern = NULL;
|
|
|
|
char_u *event_name;
|
|
|
|
char_u *p;
|
|
|
|
event_T event;
|
|
|
|
AutoPat *ap;
|
|
|
|
buf_T *buflocal_buf = NULL;
|
|
|
|
int group;
|
|
|
|
int retval = FALSE;
|
|
|
|
|
|
|
|
// Make a copy so that we can change the '#' chars to a NUL.
|
|
|
|
arg_save = vim_strsave(arg);
|
|
|
|
if (arg_save == NULL)
|
|
|
|
return FALSE;
|
|
|
|
p = vim_strchr(arg_save, '#');
|
|
|
|
if (p != NULL)
|
|
|
|
*p++ = NUL;
|
|
|
|
|
|
|
|
// First, look for an autocmd group name
|
|
|
|
group = au_find_group(arg_save);
|
|
|
|
if (group == AUGROUP_ERROR)
|
|
|
|
{
|
|
|
|
// Didn't match a group name, assume the first argument is an event.
|
|
|
|
group = AUGROUP_ALL;
|
|
|
|
event_name = arg_save;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (p == NULL)
|
|
|
|
{
|
|
|
|
// "Group": group name is present and it's recognized
|
|
|
|
retval = TRUE;
|
|
|
|
goto theend;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Must be "Group#Event" or "Group#Event#pat".
|
|
|
|
event_name = p;
|
|
|
|
p = vim_strchr(event_name, '#');
|
|
|
|
if (p != NULL)
|
|
|
|
*p++ = NUL; // "Group#Event#pat"
|
|
|
|
}
|
|
|
|
|
|
|
|
pattern = p; // "pattern" is NULL when there is no pattern
|
|
|
|
|
|
|
|
// find the index (enum) for the event name
|
|
|
|
event = event_name2nr(event_name, &p);
|
|
|
|
|
|
|
|
// return FALSE if the event name is not recognized
|
|
|
|
if (event == NUM_EVENTS)
|
|
|
|
goto theend;
|
|
|
|
|
|
|
|
// Find the first autocommand for this event.
|
|
|
|
// If there isn't any, return FALSE;
|
|
|
|
// If there is one and no pattern given, return TRUE;
|
|
|
|
ap = first_autopat[(int)event];
|
|
|
|
if (ap == NULL)
|
|
|
|
goto theend;
|
|
|
|
|
|
|
|
// if pattern is "<buffer>", special handling is needed which uses curbuf
|
|
|
|
// for pattern "<buffer=N>, fnamecmp() will work fine
|
|
|
|
if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
|
|
|
|
buflocal_buf = curbuf;
|
|
|
|
|
|
|
|
// Check if there is an autocommand with the given pattern.
|
|
|
|
for ( ; ap != NULL; ap = ap->next)
|
|
|
|
// only use a pattern when it has not been removed and has commands.
|
|
|
|
// For buffer-local autocommands, fnamecmp() works fine.
|
|
|
|
if (ap->pat != NULL && ap->cmds != NULL
|
|
|
|
&& (group == AUGROUP_ALL || ap->group == group)
|
|
|
|
&& (pattern == NULL
|
|
|
|
|| (buflocal_buf == NULL
|
|
|
|
? fnamecmp(ap->pat, pattern) == 0
|
|
|
|
: ap->buflocal_nr == buflocal_buf->b_fnum)))
|
|
|
|
{
|
|
|
|
retval = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
theend:
|
|
|
|
vim_free(arg_save);
|
|
|
|
return retval;
|
|
|
|
}
|
2022-05-19 10:31:47 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* autocmd_add() and autocmd_delete() functions
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
|
|
|
|
{
|
2022-05-27 18:05:33 +01:00
|
|
|
list_T *aucmd_list;
|
2022-05-19 10:31:47 +01:00
|
|
|
listitem_T *li;
|
|
|
|
dict_T *event_dict;
|
2022-05-27 18:05:33 +01:00
|
|
|
dictitem_T *di;
|
2022-05-19 10:31:47 +01:00
|
|
|
char_u *event_name = NULL;
|
2022-05-27 18:05:33 +01:00
|
|
|
list_T *event_list;
|
|
|
|
listitem_T *eli;
|
2022-05-19 10:31:47 +01:00
|
|
|
event_T event;
|
|
|
|
char_u *group_name = NULL;
|
|
|
|
int group;
|
|
|
|
char_u *pat = NULL;
|
2022-05-27 18:05:33 +01:00
|
|
|
list_T *pat_list;
|
|
|
|
listitem_T *pli;
|
2022-05-19 10:31:47 +01:00
|
|
|
char_u *cmd = NULL;
|
|
|
|
char_u *end;
|
|
|
|
int once;
|
|
|
|
int nested;
|
2022-05-24 11:40:11 +01:00
|
|
|
int replace; // replace the cmd for a group/event
|
2022-05-19 10:31:47 +01:00
|
|
|
int retval = VVAL_TRUE;
|
|
|
|
int save_augroup = current_augroup;
|
|
|
|
|
|
|
|
rettv->v_type = VAR_BOOL;
|
|
|
|
rettv->vval.v_number = VVAL_FALSE;
|
|
|
|
|
|
|
|
if (check_for_list_arg(argvars, 0) == FAIL)
|
|
|
|
return;
|
|
|
|
|
2022-05-27 18:05:33 +01:00
|
|
|
aucmd_list = argvars[0].vval.v_list;
|
|
|
|
if (aucmd_list == NULL)
|
2022-05-19 10:31:47 +01:00
|
|
|
return;
|
|
|
|
|
2022-05-27 18:05:33 +01:00
|
|
|
FOR_ALL_LIST_ITEMS(aucmd_list, li)
|
2022-05-19 10:31:47 +01:00
|
|
|
{
|
|
|
|
VIM_CLEAR(group_name);
|
|
|
|
VIM_CLEAR(cmd);
|
2022-05-27 18:05:33 +01:00
|
|
|
event_name = NULL;
|
|
|
|
event_list = NULL;
|
|
|
|
pat = NULL;
|
|
|
|
pat_list = NULL;
|
2022-05-19 10:31:47 +01:00
|
|
|
|
|
|
|
if (li->li_tv.v_type != VAR_DICT)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
event_dict = li->li_tv.vval.v_dict;
|
|
|
|
if (event_dict == NULL)
|
|
|
|
continue;
|
|
|
|
|
2022-05-27 18:05:33 +01:00
|
|
|
di = dict_find(event_dict, (char_u *)"event", -1);
|
|
|
|
if (di != NULL)
|
2022-05-19 10:31:47 +01:00
|
|
|
{
|
2022-05-27 18:05:33 +01:00
|
|
|
if (di->di_tv.v_type == VAR_STRING)
|
2022-05-19 10:31:47 +01:00
|
|
|
{
|
2022-05-27 18:05:33 +01:00
|
|
|
event_name = di->di_tv.vval.v_string;
|
|
|
|
if (event_name == NULL)
|
2022-05-19 10:31:47 +01:00
|
|
|
{
|
2022-05-27 18:05:33 +01:00
|
|
|
emsg(_(e_string_required));
|
|
|
|
continue;
|
2022-05-19 10:31:47 +01:00
|
|
|
}
|
|
|
|
}
|
2022-05-27 18:05:33 +01:00
|
|
|
else if (di->di_tv.v_type == VAR_LIST)
|
|
|
|
{
|
|
|
|
event_list = di->di_tv.vval.v_list;
|
|
|
|
if (event_list == NULL)
|
|
|
|
{
|
|
|
|
emsg(_(e_list_required));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
emsg(_(e_string_or_list_expected));
|
|
|
|
continue;
|
|
|
|
}
|
2022-05-19 10:31:47 +01:00
|
|
|
}
|
|
|
|
|
2022-07-23 09:52:04 +01:00
|
|
|
group_name = dict_get_string(event_dict, "group", TRUE);
|
2022-05-19 10:31:47 +01:00
|
|
|
if (group_name == NULL || *group_name == NUL)
|
|
|
|
// if the autocmd group name is not specified, then use the current
|
|
|
|
// autocmd group
|
|
|
|
group = current_augroup;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
group = au_find_group(group_name);
|
|
|
|
if (group == AUGROUP_ERROR)
|
|
|
|
{
|
|
|
|
if (delete)
|
|
|
|
{
|
|
|
|
semsg(_(e_no_such_group_str), group_name);
|
|
|
|
retval = VVAL_FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// group is not found, create it now
|
|
|
|
group = au_new_group(group_name);
|
|
|
|
if (group == AUGROUP_ERROR)
|
|
|
|
{
|
|
|
|
semsg(_(e_no_such_group_str), group_name);
|
|
|
|
retval = VVAL_FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
current_augroup = group;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if a buffer number is specified, then generate a pattern of the form
|
|
|
|
// "<buffer=n>. Otherwise, use the pattern supplied by the user.
|
|
|
|
if (dict_has_key(event_dict, "bufnr"))
|
|
|
|
{
|
|
|
|
varnumber_T bnum;
|
|
|
|
|
2022-07-23 09:52:04 +01:00
|
|
|
bnum = dict_get_number_def(event_dict, "bufnr", -1);
|
2022-05-19 10:31:47 +01:00
|
|
|
if (bnum == -1)
|
|
|
|
continue;
|
|
|
|
|
2022-05-27 18:05:33 +01:00
|
|
|
vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
|
|
|
|
pat = IObuff;
|
2022-05-19 10:31:47 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-05-27 18:05:33 +01:00
|
|
|
di = dict_find(event_dict, (char_u *)"pattern", -1);
|
|
|
|
if (di != NULL)
|
2022-05-19 10:31:47 +01:00
|
|
|
{
|
2022-05-27 18:05:33 +01:00
|
|
|
if (di->di_tv.v_type == VAR_STRING)
|
|
|
|
{
|
|
|
|
pat = di->di_tv.vval.v_string;
|
|
|
|
if (pat == NULL)
|
|
|
|
{
|
|
|
|
emsg(_(e_string_required));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (di->di_tv.v_type == VAR_LIST)
|
|
|
|
{
|
|
|
|
pat_list = di->di_tv.vval.v_list;
|
|
|
|
if (pat_list == NULL)
|
|
|
|
{
|
|
|
|
emsg(_(e_list_required));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2022-05-19 10:31:47 +01:00
|
|
|
else
|
2022-05-27 18:05:33 +01:00
|
|
|
{
|
|
|
|
emsg(_(e_string_or_list_expected));
|
2022-05-19 10:31:47 +01:00
|
|
|
continue;
|
2022-05-27 18:05:33 +01:00
|
|
|
}
|
2022-05-19 10:31:47 +01:00
|
|
|
}
|
2022-05-27 18:05:33 +01:00
|
|
|
else if (delete)
|
|
|
|
pat = (char_u *)"";
|
2022-05-19 10:31:47 +01:00
|
|
|
}
|
|
|
|
|
2022-07-23 09:52:04 +01:00
|
|
|
once = dict_get_bool(event_dict, "once", FALSE);
|
|
|
|
nested = dict_get_bool(event_dict, "nested", FALSE);
|
2022-05-24 11:40:11 +01:00
|
|
|
// if 'replace' is true, then remove all the commands associated with
|
|
|
|
// this autocmd event/group and add the new command.
|
2022-07-23 09:52:04 +01:00
|
|
|
replace = dict_get_bool(event_dict, "replace", FALSE);
|
2022-05-19 10:31:47 +01:00
|
|
|
|
2022-07-23 09:52:04 +01:00
|
|
|
cmd = dict_get_string(event_dict, "cmd", TRUE);
|
2022-05-19 10:31:47 +01:00
|
|
|
if (cmd == NULL)
|
|
|
|
{
|
|
|
|
if (delete)
|
|
|
|
cmd = vim_strsave((char_u *)"");
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-05-27 18:05:33 +01:00
|
|
|
if (delete && (event_name == NULL
|
|
|
|
|| (event_name[0] == '*' && event_name[1] == NUL)))
|
2022-05-19 10:31:47 +01:00
|
|
|
{
|
2022-05-27 18:05:33 +01:00
|
|
|
// if the event name is not specified or '*', delete all the events
|
2022-05-19 10:31:47 +01:00
|
|
|
for (event = (event_T)0; (int)event < NUM_EVENTS;
|
|
|
|
event = (event_T)((int)event + 1))
|
|
|
|
{
|
|
|
|
if (do_autocmd_event(event, pat, once, nested, cmd, delete,
|
|
|
|
group, 0) == FAIL)
|
|
|
|
{
|
|
|
|
retval = VVAL_FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-05-27 21:16:34 +01:00
|
|
|
char_u *p = NULL;
|
|
|
|
|
2022-05-27 18:05:33 +01:00
|
|
|
eli = NULL;
|
|
|
|
end = NULL;
|
|
|
|
while (TRUE)
|
2022-05-19 10:31:47 +01:00
|
|
|
{
|
2022-05-27 18:05:33 +01:00
|
|
|
if (event_list != NULL)
|
|
|
|
{
|
|
|
|
if (eli == NULL)
|
|
|
|
eli = event_list->lv_first;
|
|
|
|
else
|
|
|
|
eli = eli->li_next;
|
|
|
|
if (eli == NULL)
|
|
|
|
break;
|
|
|
|
if (eli->li_tv.v_type != VAR_STRING
|
2022-06-01 16:02:38 +01:00
|
|
|
|| (p = eli->li_tv.vval.v_string) == NULL)
|
2022-05-27 18:05:33 +01:00
|
|
|
{
|
|
|
|
emsg(_(e_string_required));
|
2022-06-01 16:02:38 +01:00
|
|
|
break;
|
2022-05-27 18:05:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-06-01 16:02:38 +01:00
|
|
|
if (p == NULL)
|
|
|
|
p = event_name;
|
|
|
|
if (p == NULL || *p == NUL)
|
2022-05-27 18:05:33 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
event = event_name2nr(p, &end);
|
|
|
|
if (event == NUM_EVENTS || *end != NUL)
|
|
|
|
{
|
2022-06-01 16:02:38 +01:00
|
|
|
// this also catches something following a valid event name
|
2022-05-27 18:05:33 +01:00
|
|
|
semsg(_(e_no_such_event_str), p);
|
|
|
|
retval = VVAL_FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (pat != NULL)
|
|
|
|
{
|
|
|
|
if (do_autocmd_event(event, pat, once, nested, cmd,
|
|
|
|
delete | replace, group, 0) == FAIL)
|
|
|
|
{
|
|
|
|
retval = VVAL_FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (pat_list != NULL)
|
|
|
|
{
|
|
|
|
FOR_ALL_LIST_ITEMS(pat_list, pli)
|
|
|
|
{
|
|
|
|
if (pli->li_tv.v_type != VAR_STRING
|
|
|
|
|| pli->li_tv.vval.v_string == NULL)
|
|
|
|
{
|
|
|
|
emsg(_(e_string_required));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (do_autocmd_event(event,
|
|
|
|
pli->li_tv.vval.v_string, once, nested,
|
|
|
|
cmd, delete | replace, group, 0) ==
|
|
|
|
FAIL)
|
|
|
|
{
|
|
|
|
retval = VVAL_FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (retval == VVAL_FALSE)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (event_name != NULL)
|
|
|
|
p = end;
|
2022-05-19 10:31:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if only the autocmd group name is specified for delete and the
|
|
|
|
// autocmd event, pattern and cmd are not specified, then delete the
|
|
|
|
// autocmd group.
|
|
|
|
if (delete && group_name != NULL &&
|
|
|
|
(event_name == NULL || event_name[0] == NUL)
|
|
|
|
&& (pat == NULL || pat[0] == NUL)
|
|
|
|
&& (cmd == NULL || cmd[0] == NUL))
|
|
|
|
au_del_group(group_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
VIM_CLEAR(group_name);
|
|
|
|
VIM_CLEAR(cmd);
|
|
|
|
|
|
|
|
current_augroup = save_augroup;
|
|
|
|
rettv->vval.v_number = retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* autocmd_add() function
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
f_autocmd_add(typval_T *argvars, typval_T *rettv)
|
|
|
|
{
|
|
|
|
autocmd_add_or_delete(argvars, rettv, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* autocmd_delete() function
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
f_autocmd_delete(typval_T *argvars, typval_T *rettv)
|
|
|
|
{
|
|
|
|
autocmd_add_or_delete(argvars, rettv, TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* autocmd_get() function
|
|
|
|
* Returns a List of autocmds.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
f_autocmd_get(typval_T *argvars, typval_T *rettv)
|
|
|
|
{
|
|
|
|
event_T event_arg = NUM_EVENTS;
|
|
|
|
event_T event;
|
|
|
|
AutoPat *ap;
|
|
|
|
AutoCmd *ac;
|
|
|
|
list_T *event_list;
|
|
|
|
dict_T *event_dict;
|
|
|
|
char_u *event_name = NULL;
|
|
|
|
char_u *pat = NULL;
|
|
|
|
char_u *name = NULL;
|
|
|
|
int group = AUGROUP_ALL;
|
|
|
|
|
2022-06-14 13:42:26 +01:00
|
|
|
if (rettv_list_alloc(rettv) == FAIL)
|
|
|
|
return;
|
2022-05-19 10:31:47 +01:00
|
|
|
if (check_for_opt_dict_arg(argvars, 0) == FAIL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (argvars[0].v_type == VAR_DICT)
|
|
|
|
{
|
|
|
|
// return only the autocmds in the specified group
|
|
|
|
if (dict_has_key(argvars[0].vval.v_dict, "group"))
|
|
|
|
{
|
2022-07-23 09:52:04 +01:00
|
|
|
name = dict_get_string(argvars[0].vval.v_dict, "group", TRUE);
|
2022-05-19 10:31:47 +01:00
|
|
|
if (name == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (*name == NUL)
|
|
|
|
group = AUGROUP_DEFAULT;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
group = au_find_group(name);
|
|
|
|
if (group == AUGROUP_ERROR)
|
|
|
|
{
|
|
|
|
semsg(_(e_no_such_group_str), name);
|
|
|
|
vim_free(name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
vim_free(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
// return only the autocmds for the specified event
|
|
|
|
if (dict_has_key(argvars[0].vval.v_dict, "event"))
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2022-07-23 09:52:04 +01:00
|
|
|
name = dict_get_string(argvars[0].vval.v_dict, "event", TRUE);
|
2022-05-19 10:31:47 +01:00
|
|
|
if (name == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (name[0] == '*' && name[1] == NUL)
|
|
|
|
event_arg = NUM_EVENTS;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (i = 0; event_names[i].name != NULL; i++)
|
|
|
|
if (STRICMP(event_names[i].name, name) == 0)
|
|
|
|
break;
|
|
|
|
if (event_names[i].name == NULL)
|
|
|
|
{
|
|
|
|
semsg(_(e_no_such_event_str), name);
|
|
|
|
vim_free(name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
event_arg = event_names[i].event;
|
|
|
|
}
|
|
|
|
vim_free(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
// return only the autocmds for the specified pattern
|
|
|
|
if (dict_has_key(argvars[0].vval.v_dict, "pattern"))
|
|
|
|
{
|
2022-07-23 09:52:04 +01:00
|
|
|
pat = dict_get_string(argvars[0].vval.v_dict, "pattern", TRUE);
|
2022-05-19 10:31:47 +01:00
|
|
|
if (pat == NULL)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
event_list = rettv->vval.v_list;
|
|
|
|
|
|
|
|
// iterate through all the autocmd events
|
|
|
|
for (event = (event_T)0; (int)event < NUM_EVENTS;
|
|
|
|
event = (event_T)((int)event + 1))
|
|
|
|
{
|
|
|
|
if (event_arg != NUM_EVENTS && event != event_arg)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
event_name = event_nr2name(event);
|
|
|
|
|
|
|
|
// iterate through all the patterns for this autocmd event
|
|
|
|
FOR_ALL_AUTOCMD_PATTERNS(event, ap)
|
|
|
|
{
|
|
|
|
char_u *group_name;
|
|
|
|
|
|
|
|
if (group != AUGROUP_ALL && group != ap->group)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (pat != NULL && STRCMP(pat, ap->pat) != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
group_name = get_augroup_name(NULL, ap->group);
|
|
|
|
|
|
|
|
// iterate through all the commands for this pattern and add one
|
|
|
|
// item for each cmd.
|
|
|
|
for (ac = ap->cmds; ac != NULL; ac = ac->next)
|
|
|
|
{
|
|
|
|
event_dict = dict_alloc();
|
2022-06-01 12:31:53 +01:00
|
|
|
if (event_dict == NULL
|
|
|
|
|| list_append_dict(event_list, event_dict) == FAIL)
|
2022-05-19 10:31:47 +01:00
|
|
|
return;
|
|
|
|
|
2022-06-01 12:31:53 +01:00
|
|
|
if (dict_add_string(event_dict, "event", event_name) == FAIL
|
|
|
|
|| dict_add_string(event_dict, "group",
|
|
|
|
group_name == NULL ? (char_u *)""
|
|
|
|
: group_name) == FAIL
|
|
|
|
|| (ap->buflocal_nr != 0
|
|
|
|
&& (dict_add_number(event_dict, "bufnr",
|
|
|
|
ap->buflocal_nr) == FAIL))
|
|
|
|
|| dict_add_string(event_dict, "pattern",
|
|
|
|
ap->pat) == FAIL
|
|
|
|
|| dict_add_string(event_dict, "cmd", ac->cmd) == FAIL
|
|
|
|
|| dict_add_bool(event_dict, "once", ac->once) == FAIL
|
|
|
|
|| dict_add_bool(event_dict, "nested",
|
|
|
|
ac->nested) == FAIL)
|
2022-05-19 10:31:47 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
vim_free(pat);
|
|
|
|
}
|
|
|
|
|
2019-01-26 16:21:07 +01:00
|
|
|
#endif
|