mirror of
https://github.com/vim/vim.git
synced 2025-09-25 03:54:15 -04:00
patch 8.1.1113: making an autocommand trigger once is not so easy
Problem: Making an autocommand trigger once is not so easy. Solution: Add the ++once argument. Also add ++nested as an alias for "nested". (Justin M. Keyes, closes #4100)
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
*autocmd.txt* For Vim version 8.1. Last change: 2019 Mar 13
|
*autocmd.txt* For Vim version 8.1. Last change: 2019 Apr 04
|
||||||
|
|
||||||
|
|
||||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||||
@@ -52,7 +52,7 @@ effects. Be careful not to destroy your text.
|
|||||||
2. Defining autocommands *autocmd-define*
|
2. Defining autocommands *autocmd-define*
|
||||||
|
|
||||||
*:au* *:autocmd*
|
*:au* *:autocmd*
|
||||||
:au[tocmd] [group] {event} {pat} [nested] {cmd}
|
:au[tocmd] [group] {event} {pat} [++once] [++nested] {cmd}
|
||||||
Add {cmd} to the list of commands that Vim will
|
Add {cmd} to the list of commands that Vim will
|
||||||
execute automatically on {event} for a file matching
|
execute automatically on {event} for a file matching
|
||||||
{pat} |autocmd-patterns|.
|
{pat} |autocmd-patterns|.
|
||||||
@@ -60,7 +60,13 @@ effects. Be careful not to destroy your text.
|
|||||||
:autocmd and won't start a comment.
|
:autocmd and won't start a comment.
|
||||||
Vim always adds the {cmd} after existing autocommands,
|
Vim always adds the {cmd} after existing autocommands,
|
||||||
so that the autocommands execute in the order in which
|
so that the autocommands execute in the order in which
|
||||||
they were given. See |autocmd-nested| for [nested].
|
they were given.
|
||||||
|
See |autocmd-nested| for [++nested]. "nested"
|
||||||
|
(without the ++) can also be used, for backwards
|
||||||
|
compatibility.
|
||||||
|
*autocmd-once*
|
||||||
|
If [++once] is supplied the command is executed once,
|
||||||
|
then removed ("one shot").
|
||||||
|
|
||||||
The special pattern <buffer> or <buffer=N> defines a buffer-local autocommand.
|
The special pattern <buffer> or <buffer=N> defines a buffer-local autocommand.
|
||||||
See |autocmd-buflocal|.
|
See |autocmd-buflocal|.
|
||||||
@@ -128,10 +134,11 @@ prompt. When one command outputs two messages this can happen anyway.
|
|||||||
==============================================================================
|
==============================================================================
|
||||||
3. Removing autocommands *autocmd-remove*
|
3. Removing autocommands *autocmd-remove*
|
||||||
|
|
||||||
:au[tocmd]! [group] {event} {pat} [nested] {cmd}
|
:au[tocmd]! [group] {event} {pat} [++once] [++nested] {cmd}
|
||||||
Remove all autocommands associated with {event} and
|
Remove all autocommands associated with {event} and
|
||||||
{pat}, and add the command {cmd}. See
|
{pat}, and add the command {cmd}.
|
||||||
|autocmd-nested| for [nested].
|
See |autocmd-once| for [++once].
|
||||||
|
See |autocmd-nested| for [++nested].
|
||||||
|
|
||||||
:au[tocmd]! [group] {event} {pat}
|
:au[tocmd]! [group] {event} {pat}
|
||||||
Remove all autocommands associated with {event} and
|
Remove all autocommands associated with {event} and
|
||||||
@@ -1473,7 +1480,7 @@ By default, autocommands do not nest. For example, if you use ":e" or ":w" in
|
|||||||
an autocommand, Vim does not execute the BufRead and BufWrite autocommands for
|
an autocommand, Vim does not execute the BufRead and BufWrite autocommands for
|
||||||
those commands. If you do want this, use the "nested" flag for those commands
|
those commands. If you do want this, use the "nested" flag for those commands
|
||||||
in which you want nesting. For example: >
|
in which you want nesting. For example: >
|
||||||
:autocmd FileChangedShell *.c nested e!
|
:autocmd FileChangedShell *.c ++nested e!
|
||||||
The nesting is limited to 10 levels to get out of recursive loops.
|
The nesting is limited to 10 levels to get out of recursive loops.
|
||||||
|
|
||||||
It's possible to use the ":au" command in an autocommand. This can be a
|
It's possible to use the ":au" command in an autocommand. This can be a
|
||||||
|
@@ -52,6 +52,7 @@ typedef struct AutoCmd
|
|||||||
{
|
{
|
||||||
char_u *cmd; // The command to be executed (NULL
|
char_u *cmd; // The command to be executed (NULL
|
||||||
// when command has been removed).
|
// when command has been removed).
|
||||||
|
char once; // "One shot": removed after execution
|
||||||
char nested; // If autocommands nest here.
|
char nested; // If autocommands nest here.
|
||||||
char last; // last command in list
|
char last; // last command in list
|
||||||
#ifdef FEAT_EVAL
|
#ifdef FEAT_EVAL
|
||||||
@@ -256,7 +257,7 @@ static int au_need_clean = FALSE; /* need to delete marked patterns */
|
|||||||
|
|
||||||
static char_u *event_nr2name(event_T event);
|
static char_u *event_nr2name(event_T event);
|
||||||
static int au_get_grouparg(char_u **argp);
|
static int au_get_grouparg(char_u **argp);
|
||||||
static int do_autocmd_event(event_T event, char_u *pat, int nested, char_u *cmd, int forceit, int group);
|
static int do_autocmd_event(event_T event, char_u *pat, int once, int nested, char_u *cmd, int forceit, int group);
|
||||||
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);
|
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);
|
||||||
static void auto_next_pat(AutoPatCmd *apc, int stop_at_last);
|
static void auto_next_pat(AutoPatCmd *apc, int stop_at_last);
|
||||||
static int au_find_group(char_u *name);
|
static int au_find_group(char_u *name);
|
||||||
@@ -361,6 +362,13 @@ au_remove_cmds(AutoPat *ap)
|
|||||||
au_need_clean = TRUE;
|
au_need_clean = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete one command from an autocmd pattern.
|
||||||
|
static void au_del_cmd(AutoCmd *ac)
|
||||||
|
{
|
||||||
|
VIM_CLEAR(ac->cmd);
|
||||||
|
au_need_clean = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Cleanup autocommands and patterns that have been deleted.
|
* Cleanup autocommands and patterns that have been deleted.
|
||||||
* This is only done when not executing autocommands.
|
* This is only done when not executing autocommands.
|
||||||
@@ -385,6 +393,8 @@ au_cleanup(void)
|
|||||||
{
|
{
|
||||||
// loop over all commands for this pattern
|
// loop over all commands for this pattern
|
||||||
prev_ac = &(ap->cmds);
|
prev_ac = &(ap->cmds);
|
||||||
|
int has_cmd = FALSE;
|
||||||
|
|
||||||
for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
|
for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
|
||||||
{
|
{
|
||||||
// remove the command if the pattern is to be deleted or when
|
// remove the command if the pattern is to be deleted or when
|
||||||
@@ -395,9 +405,17 @@ au_cleanup(void)
|
|||||||
vim_free(ac->cmd);
|
vim_free(ac->cmd);
|
||||||
vim_free(ac);
|
vim_free(ac);
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
|
has_cmd = TRUE;
|
||||||
prev_ac = &(ac->next);
|
prev_ac = &(ac->next);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ap->pat != NULL && !has_cmd) {
|
||||||
|
// Pattern was not marked for deletion, but all of its
|
||||||
|
// commands were. So mark the pattern for deletion.
|
||||||
|
au_remove_pat(ap);
|
||||||
|
}
|
||||||
|
|
||||||
// remove the pattern if it has been marked for deletion
|
// remove the pattern if it has been marked for deletion
|
||||||
if (ap->pat == NULL)
|
if (ap->pat == NULL)
|
||||||
@@ -815,7 +833,9 @@ do_autocmd(char_u *arg_in, int forceit)
|
|||||||
event_T event;
|
event_T event;
|
||||||
int need_free = FALSE;
|
int need_free = FALSE;
|
||||||
int nested = FALSE;
|
int nested = FALSE;
|
||||||
|
int once = FALSE;
|
||||||
int group;
|
int group;
|
||||||
|
int i;
|
||||||
|
|
||||||
if (*arg == '|')
|
if (*arg == '|')
|
||||||
{
|
{
|
||||||
@@ -874,16 +894,39 @@ do_autocmd(char_u *arg_in, int forceit)
|
|||||||
pat = envpat;
|
pat = envpat;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Check for "nested" flag.
|
|
||||||
*/
|
|
||||||
cmd = skipwhite(cmd);
|
cmd = skipwhite(cmd);
|
||||||
if (*cmd != NUL && STRNCMP(cmd, "nested", 6) == 0
|
for (i = 0; i < 2; i++)
|
||||||
&& VIM_ISWHITE(cmd[6]))
|
|
||||||
{
|
{
|
||||||
|
if (*cmd != NUL)
|
||||||
|
{
|
||||||
|
// Check for "++once" flag.
|
||||||
|
if (STRNCMP(cmd, "++once", 6) == 0 && VIM_ISWHITE(cmd[6]))
|
||||||
|
{
|
||||||
|
if (once)
|
||||||
|
semsg(_(e_duparg2), "++once");
|
||||||
|
once = TRUE;
|
||||||
|
cmd = skipwhite(cmd + 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for "++nested" flag.
|
||||||
|
if ((STRNCMP(cmd, "++nested", 8) == 0 && VIM_ISWHITE(cmd[8])))
|
||||||
|
{
|
||||||
|
if (nested)
|
||||||
|
semsg(_(e_duparg2), "++nested");
|
||||||
|
nested = TRUE;
|
||||||
|
cmd = skipwhite(cmd + 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for the old "nested" flag.
|
||||||
|
if (STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
|
||||||
|
{
|
||||||
|
if (nested)
|
||||||
|
semsg(_(e_duparg2), "nested");
|
||||||
nested = TRUE;
|
nested = TRUE;
|
||||||
cmd = skipwhite(cmd + 6);
|
cmd = skipwhite(cmd + 6);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find the start of the commands.
|
* Find the start of the commands.
|
||||||
@@ -915,14 +958,14 @@ do_autocmd(char_u *arg_in, int forceit)
|
|||||||
for (event = (event_T)0; (int)event < (int)NUM_EVENTS;
|
for (event = (event_T)0; (int)event < (int)NUM_EVENTS;
|
||||||
event = (event_T)((int)event + 1))
|
event = (event_T)((int)event + 1))
|
||||||
if (do_autocmd_event(event, pat,
|
if (do_autocmd_event(event, pat,
|
||||||
nested, cmd, forceit, group) == FAIL)
|
once, nested, cmd, forceit, group) == FAIL)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
|
while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
|
||||||
if (do_autocmd_event(event_name2nr(arg, &arg), pat,
|
if (do_autocmd_event(event_name2nr(arg, &arg), pat,
|
||||||
nested, cmd, forceit, group) == FAIL)
|
once, nested, cmd, forceit, group) == FAIL)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -973,6 +1016,7 @@ au_get_grouparg(char_u **argp)
|
|||||||
do_autocmd_event(
|
do_autocmd_event(
|
||||||
event_T event,
|
event_T event,
|
||||||
char_u *pat,
|
char_u *pat,
|
||||||
|
int once,
|
||||||
int nested,
|
int nested,
|
||||||
char_u *cmd,
|
char_u *cmd,
|
||||||
int forceit,
|
int forceit,
|
||||||
@@ -1212,6 +1256,7 @@ do_autocmd_event(
|
|||||||
}
|
}
|
||||||
ac->next = NULL;
|
ac->next = NULL;
|
||||||
*prev_ac = ac;
|
*prev_ac = ac;
|
||||||
|
ac->once = once;
|
||||||
ac->nested = nested;
|
ac->nested = nested;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2319,6 +2364,9 @@ getnextac(int c UNUSED, void *cookie, int indent UNUSED)
|
|||||||
verbose_leave_scroll();
|
verbose_leave_scroll();
|
||||||
}
|
}
|
||||||
retval = vim_strsave(ac->cmd);
|
retval = vim_strsave(ac->cmd);
|
||||||
|
// Remove one-shot ("once") autocmd in anticipation of its execution.
|
||||||
|
if (ac->once)
|
||||||
|
au_del_cmd(ac);
|
||||||
autocmd_nested = ac->nested;
|
autocmd_nested = ac->nested;
|
||||||
#ifdef FEAT_EVAL
|
#ifdef FEAT_EVAL
|
||||||
current_sctx = ac->script_ctx;
|
current_sctx = ac->script_ctx;
|
||||||
|
@@ -1401,6 +1401,7 @@ EXTERN char e_interr[] INIT(= N_("Interrupted"));
|
|||||||
EXTERN char e_invaddr[] INIT(= N_("E14: Invalid address"));
|
EXTERN char e_invaddr[] INIT(= N_("E14: Invalid address"));
|
||||||
EXTERN char e_invarg[] INIT(= N_("E474: Invalid argument"));
|
EXTERN char e_invarg[] INIT(= N_("E474: Invalid argument"));
|
||||||
EXTERN char e_invarg2[] INIT(= N_("E475: Invalid argument: %s"));
|
EXTERN char e_invarg2[] INIT(= N_("E475: Invalid argument: %s"));
|
||||||
|
EXTERN char e_duparg2[] INIT(= N_("E983: Duplicate argument: %s"));
|
||||||
EXTERN char e_invargval[] INIT(= N_("E475: Invalid value for argument %s"));
|
EXTERN char e_invargval[] INIT(= N_("E475: Invalid value for argument %s"));
|
||||||
EXTERN char e_invargNval[] INIT(= N_("E475: Invalid value for argument %s: %s"));
|
EXTERN char e_invargNval[] INIT(= N_("E475: Invalid value for argument %s: %s"));
|
||||||
#ifdef FEAT_EVAL
|
#ifdef FEAT_EVAL
|
||||||
|
@@ -1415,4 +1415,74 @@ func Test_Changed_FirstTime()
|
|||||||
bwipe!
|
bwipe!
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_autocmd_nested()
|
||||||
|
let g:did_nested = 0
|
||||||
|
augroup Testing
|
||||||
|
au WinNew * edit somefile
|
||||||
|
au BufNew * let g:did_nested = 1
|
||||||
|
augroup END
|
||||||
|
split
|
||||||
|
call assert_equal(0, g:did_nested)
|
||||||
|
close
|
||||||
|
bwipe! somefile
|
||||||
|
|
||||||
|
" old nested argument still works
|
||||||
|
augroup Testing
|
||||||
|
au!
|
||||||
|
au WinNew * nested edit somefile
|
||||||
|
au BufNew * let g:did_nested = 1
|
||||||
|
augroup END
|
||||||
|
split
|
||||||
|
call assert_equal(1, g:did_nested)
|
||||||
|
close
|
||||||
|
bwipe! somefile
|
||||||
|
|
||||||
|
" New ++nested argument works
|
||||||
|
augroup Testing
|
||||||
|
au!
|
||||||
|
au WinNew * ++nested edit somefile
|
||||||
|
au BufNew * let g:did_nested = 1
|
||||||
|
augroup END
|
||||||
|
split
|
||||||
|
call assert_equal(1, g:did_nested)
|
||||||
|
close
|
||||||
|
bwipe! somefile
|
||||||
|
|
||||||
|
augroup Testing
|
||||||
|
au!
|
||||||
|
augroup END
|
||||||
|
|
||||||
|
call assert_fails('au WinNew * ++nested ++nested echo bad', 'E983:')
|
||||||
|
call assert_fails('au WinNew * nested nested echo bad', 'E983:')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_autocmd_once()
|
||||||
|
" Without ++once WinNew triggers twice
|
||||||
|
let g:did_split = 0
|
||||||
|
augroup Testing
|
||||||
|
au WinNew * let g:did_split += 1
|
||||||
|
augroup END
|
||||||
|
split
|
||||||
|
split
|
||||||
|
call assert_equal(2, g:did_split)
|
||||||
|
call assert_true(exists('#WinNew'))
|
||||||
|
close
|
||||||
|
close
|
||||||
|
|
||||||
|
" With ++once WinNew triggers once
|
||||||
|
let g:did_split = 0
|
||||||
|
augroup Testing
|
||||||
|
au!
|
||||||
|
au WinNew * ++once let g:did_split += 1
|
||||||
|
augroup END
|
||||||
|
split
|
||||||
|
split
|
||||||
|
call assert_equal(1, g:did_split)
|
||||||
|
call assert_false(exists('#WinNew'))
|
||||||
|
close
|
||||||
|
close
|
||||||
|
|
||||||
|
call assert_fails('au WinNew * ++once ++once echo bad', 'E983:')
|
||||||
|
endfunc
|
||||||
|
|
||||||
" FileChangedShell tested in test_filechanged.vim
|
" FileChangedShell tested in test_filechanged.vim
|
||||||
|
@@ -771,6 +771,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1113,
|
||||||
/**/
|
/**/
|
||||||
1112,
|
1112,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user