mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 11:28:22 +00:00
Merge #9728 from justinmk/autocmd-once
This commit is contained in:
@@ -40,7 +40,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} [-once] [-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|.
|
||||||
@@ -48,9 +48,9 @@ effects. Be careful not to destroy your text.
|
|||||||
:autocmd and won't start a comment.
|
:autocmd and won't start a comment.
|
||||||
Nvim always adds {cmd} after existing autocommands so
|
Nvim always adds {cmd} after existing autocommands so
|
||||||
they execute in the order in which they were defined.
|
they execute in the order in which they were defined.
|
||||||
See |autocmd-nested| for [-nested].
|
See |autocmd-nested| for [++nested].
|
||||||
*autocmd-once*
|
*autocmd-once*
|
||||||
If [-once] is supplied the command is executed once,
|
If [++once] is supplied the command is executed once,
|
||||||
then removed ("one shot").
|
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.
|
||||||
@@ -119,11 +119,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} [-once] [-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}.
|
{pat}, and add the command {cmd}.
|
||||||
See |autocmd-once| for [-once].
|
See |autocmd-once| for [++once].
|
||||||
See |autocmd-nested| for [-nested].
|
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
|
||||||
@@ -1446,9 +1446,9 @@ instead of ":q!".
|
|||||||
*autocmd-nested* *E218*
|
*autocmd-nested* *E218*
|
||||||
By default, autocommands do not nest. If you use ":e" or ":w" in an
|
By default, autocommands do not nest. If you use ":e" or ":w" in an
|
||||||
autocommand, Vim does not execute the BufRead and BufWrite autocommands for
|
autocommand, Vim does not execute the BufRead and BufWrite autocommands for
|
||||||
those commands. If you do want this, use the "-nested" flag for those
|
those commands. If you do want this, use the "++nested" flag for those
|
||||||
commands in which you want nesting. For example: >
|
commands 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
|
||||||
|
@@ -35,7 +35,7 @@ There are 3 ways to create a terminal buffer:
|
|||||||
<
|
<
|
||||||
Note: The "term://" pattern is handled by a BufReadCmd handler, so the
|
Note: The "term://" pattern is handled by a BufReadCmd handler, so the
|
||||||
|autocmd-nested| modifier is required to use it in an autocmd. >
|
|autocmd-nested| modifier is required to use it in an autocmd. >
|
||||||
autocmd VimEnter * -nested split term://sh
|
autocmd VimEnter * ++nested split term://sh
|
||||||
< This is only mentioned for reference; use |:terminal| instead.
|
< This is only mentioned for reference; use |:terminal| instead.
|
||||||
|
|
||||||
When the terminal starts, the buffer contents are updated and the buffer is
|
When the terminal starts, the buffer contents are updated and the buffer is
|
||||||
|
@@ -453,14 +453,14 @@ matching BufWritePre autocommands and executes them, and then it
|
|||||||
performs the ":write".
|
performs the ":write".
|
||||||
The general form of the :autocmd command is as follows: >
|
The general form of the :autocmd command is as follows: >
|
||||||
|
|
||||||
:autocmd [group] {events} {file_pattern} [-nested] {command}
|
:autocmd [group] {events} {file_pattern} [++nested] {command}
|
||||||
|
|
||||||
The [group] name is optional. It is used in managing and calling the commands
|
The [group] name is optional. It is used in managing and calling the commands
|
||||||
(more on this later). The {events} parameter is a list of events (comma
|
(more on this later). The {events} parameter is a list of events (comma
|
||||||
separated) that trigger the command.
|
separated) that trigger the command.
|
||||||
{file_pattern} is a filename, usually with wildcards. For example, using
|
{file_pattern} is a filename, usually with wildcards. For example, using
|
||||||
"*.txt" makes the autocommand be used for all files whose name end in ".txt".
|
"*.txt" makes the autocommand be used for all files whose name end in ".txt".
|
||||||
The optional [-nested] flag allows for nesting of autocommands (see below),
|
The optional [++nested] flag allows for nesting of autocommands (see below),
|
||||||
and finally, {command} is the command to be executed.
|
and finally, {command} is the command to be executed.
|
||||||
|
|
||||||
|
|
||||||
@@ -576,9 +576,9 @@ NESTING
|
|||||||
Generally, commands executed as the result of an autocommand event will not
|
Generally, commands executed as the result of an autocommand event will not
|
||||||
trigger any new events. If you read a file in response to a FileChangedShell
|
trigger any new events. If you read a file in response to a FileChangedShell
|
||||||
event, it will not trigger the autocommands that would set the syntax, for
|
event, it will not trigger the autocommands that would set the syntax, for
|
||||||
example. To make the events triggered, add the "-nested" flag: >
|
example. To make the events triggered, add the "++nested" flag: >
|
||||||
|
|
||||||
:autocmd FileChangedShell * -nested edit
|
:autocmd FileChangedShell * ++nested edit
|
||||||
|
|
||||||
|
|
||||||
EXECUTING AUTOCOMMANDS
|
EXECUTING AUTOCOMMANDS
|
||||||
|
@@ -133,7 +133,7 @@ Command-line highlighting:
|
|||||||
removed in the future).
|
removed in the future).
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
|:autocmd| accepts the `once` flag
|
|:autocmd| accepts the `++once` flag
|
||||||
|:checkhealth|
|
|:checkhealth|
|
||||||
|:cquit| can use [count] to set the exit code
|
|:cquit| can use [count] to set the exit code
|
||||||
|:drop| is always available
|
|:drop| is always available
|
||||||
|
@@ -880,15 +880,15 @@ CTRL-W g } *CTRL-W_g}*
|
|||||||
cursor. This is less clever than using |:ptag|, but you don't
|
cursor. This is less clever than using |:ptag|, but you don't
|
||||||
need a tags file and it will also find matches in system
|
need a tags file and it will also find matches in system
|
||||||
include files. Example: >
|
include files. Example: >
|
||||||
:au! CursorHold *.[ch] -nested exe "silent! psearch " . expand("<cword>")
|
:au! CursorHold *.[ch] ++nested exe "silent! psearch " . expand("<cword>")
|
||||||
< Warning: This can be slow.
|
< Warning: This can be slow.
|
||||||
|
|
||||||
Example *CursorHold-example* >
|
Example *CursorHold-example* >
|
||||||
|
|
||||||
:au! CursorHold *.[ch] -nested exe "silent! ptag " . expand("<cword>")
|
:au! CursorHold *.[ch] ++nested exe "silent! ptag " . expand("<cword>")
|
||||||
|
|
||||||
This will cause a ":ptag" to be executed for the keyword under the cursor,
|
This will cause a ":ptag" to be executed for the keyword under the cursor,
|
||||||
when the cursor hasn't moved for the time set with 'updatetime'. "-nested"
|
when the cursor hasn't moved for the time set with 'updatetime'. "++nested"
|
||||||
makes other autocommands be executed, so that syntax highlighting works in the
|
makes other autocommands be executed, so that syntax highlighting works in the
|
||||||
preview window. The "silent!" avoids an error message when the tag could not
|
preview window. The "silent!" avoids an error message when the tag could not
|
||||||
be found. Also see |CursorHold|. To disable this again: >
|
be found. Also see |CursorHold|. To disable this again: >
|
||||||
@@ -898,7 +898,7 @@ be found. Also see |CursorHold|. To disable this again: >
|
|||||||
A nice addition is to highlight the found tag, avoid the ":ptag" when there
|
A nice addition is to highlight the found tag, avoid the ":ptag" when there
|
||||||
is no word under the cursor, and a few other things: >
|
is no word under the cursor, and a few other things: >
|
||||||
|
|
||||||
:au! CursorHold *.[ch] -nested call PreviewWord()
|
:au! CursorHold *.[ch] ++nested call PreviewWord()
|
||||||
:func PreviewWord()
|
:func PreviewWord()
|
||||||
: if &previewwindow " don't do this in the preview window
|
: if &previewwindow " don't do this in the preview window
|
||||||
: return
|
: return
|
||||||
|
@@ -5591,39 +5591,48 @@ static void au_del_cmd(AutoCmd *ac)
|
|||||||
au_need_clean = true;
|
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.
|
|
||||||
*/
|
|
||||||
static void au_cleanup(void)
|
static void au_cleanup(void)
|
||||||
{
|
{
|
||||||
AutoPat *ap, **prev_ap;
|
AutoPat *ap, **prev_ap;
|
||||||
AutoCmd *ac, **prev_ac;
|
AutoCmd *ac, **prev_ac;
|
||||||
event_T event;
|
event_T event;
|
||||||
|
|
||||||
if (autocmd_busy || !au_need_clean)
|
if (autocmd_busy || !au_need_clean) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* loop over all events */
|
// Loop over all events.
|
||||||
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)) {
|
||||||
/* loop over all autocommand patterns */
|
// Loop over all autocommand patterns.
|
||||||
prev_ap = &(first_autopat[(int)event]);
|
prev_ap = &(first_autopat[(int)event]);
|
||||||
for (ap = *prev_ap; ap != NULL; ap = *prev_ap) {
|
for (ap = *prev_ap; ap != NULL; ap = *prev_ap) {
|
||||||
/* loop over all commands for this pattern */
|
// Loop over all commands for this pattern.
|
||||||
prev_ac = &(ap->cmds);
|
prev_ac = &(ap->cmds);
|
||||||
|
bool 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
|
||||||
* the command has been marked for deletion */
|
// the command has been marked for deletion.
|
||||||
if (ap->pat == NULL || ac->cmd == NULL) {
|
if (ap->pat == NULL || ac->cmd == NULL) {
|
||||||
*prev_ac = ac->next;
|
*prev_ac = ac->next;
|
||||||
xfree(ac->cmd);
|
xfree(ac->cmd);
|
||||||
xfree(ac);
|
xfree(ac);
|
||||||
} else
|
} else {
|
||||||
|
has_cmd = true;
|
||||||
prev_ac = &(ac->next);
|
prev_ac = &(ac->next);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* remove the pattern if it has been marked for deletion */
|
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.
|
||||||
if (ap->pat == NULL) {
|
if (ap->pat == NULL) {
|
||||||
if (ap->next == NULL) {
|
if (ap->next == NULL) {
|
||||||
if (prev_ap == &(first_autopat[(int)event])) {
|
if (prev_ap == &(first_autopat[(int)event])) {
|
||||||
@@ -5637,12 +5646,13 @@ static void au_cleanup(void)
|
|||||||
*prev_ap = ap->next;
|
*prev_ap = ap->next;
|
||||||
vim_regfree(ap->reg_prog);
|
vim_regfree(ap->reg_prog);
|
||||||
xfree(ap);
|
xfree(ap);
|
||||||
} else
|
} else {
|
||||||
prev_ap = &(ap->next);
|
prev_ap = &(ap->next);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
au_need_clean = FALSE;
|
au_need_clean = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -6050,18 +6060,18 @@ void do_autocmd(char_u *arg_in, int forceit)
|
|||||||
cmd = skipwhite(cmd);
|
cmd = skipwhite(cmd);
|
||||||
for (size_t i = 0; i < 2; i++) {
|
for (size_t i = 0; i < 2; i++) {
|
||||||
if (*cmd != NUL) {
|
if (*cmd != NUL) {
|
||||||
// Check for "-once" flag.
|
// Check for "++once" flag.
|
||||||
if (!once && STRNCMP(cmd, "-once", 5) == 0 && ascii_iswhite(cmd[5])) {
|
if (!once && STRNCMP(cmd, "++once", 6) == 0 && ascii_iswhite(cmd[6])) {
|
||||||
once = true;
|
once = true;
|
||||||
cmd = skipwhite(cmd + 5);
|
cmd = skipwhite(cmd + 6);
|
||||||
}
|
}
|
||||||
// Check for "-nested" flag.
|
// Check for "++nested" flag.
|
||||||
if (!nested
|
if (!nested
|
||||||
&& ((STRNCMP(cmd, "-nested", 7) == 0 && ascii_iswhite(cmd[7]))
|
&& ((STRNCMP(cmd, "++nested", 8) == 0 && ascii_iswhite(cmd[8]))
|
||||||
// Deprecated form (without "-").
|
// Deprecated form (without "++").
|
||||||
|| (STRNCMP(cmd, "nested", 6) == 0 && ascii_iswhite(cmd[6])))) {
|
|| (STRNCMP(cmd, "nested", 6) == 0 && ascii_iswhite(cmd[6])))) {
|
||||||
nested = true;
|
nested = true;
|
||||||
cmd = skipwhite(cmd + ('-' == cmd[0] ? 7 : 6));
|
cmd = skipwhite(cmd + ('+' == cmd[0] ? 8 : 6));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -363,8 +363,8 @@ static void set_bg_deferred(void **argv)
|
|||||||
if (starting) {
|
if (starting) {
|
||||||
// Wait until after startup, so OptionSet is triggered.
|
// Wait until after startup, so OptionSet is triggered.
|
||||||
do_cmdline_cmd((bgvalue[0] == 'l')
|
do_cmdline_cmd((bgvalue[0] == 'l')
|
||||||
? "autocmd VimEnter * once nested set background=light"
|
? "autocmd VimEnter * ++once ++nested set bg=light"
|
||||||
: "autocmd VimEnter * once nested set background=dark");
|
: "autocmd VimEnter * ++once ++nested set bg=dark");
|
||||||
} else {
|
} else {
|
||||||
set_option_value("bg", 0L, bgvalue, 0);
|
set_option_value("bg", 0L, bgvalue, 0);
|
||||||
reset_option_was_set("bg");
|
reset_option_was_set("bg");
|
||||||
|
@@ -59,9 +59,9 @@ describe('autocmd', function()
|
|||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('-once', function() -- :help autocmd-once
|
it('++once', function() -- :help autocmd-once
|
||||||
--
|
--
|
||||||
-- ":autocmd ... -once" executes its handler once, then removes the handler.
|
-- ":autocmd ... ++once" executes its handler once, then removes the handler.
|
||||||
--
|
--
|
||||||
local expected = {
|
local expected = {
|
||||||
'Many1',
|
'Many1',
|
||||||
@@ -76,10 +76,10 @@ describe('autocmd', function()
|
|||||||
}
|
}
|
||||||
command('let g:foo = []')
|
command('let g:foo = []')
|
||||||
command('autocmd TabNew * :call add(g:foo, "Many1")')
|
command('autocmd TabNew * :call add(g:foo, "Many1")')
|
||||||
command('autocmd TabNew * -once :call add(g:foo, "Once1")')
|
command('autocmd TabNew * ++once :call add(g:foo, "Once1")')
|
||||||
command('autocmd TabNew * -once :call add(g:foo, "Once2")')
|
command('autocmd TabNew * ++once :call add(g:foo, "Once2")')
|
||||||
command('autocmd TabNew * :call add(g:foo, "Many2")')
|
command('autocmd TabNew * :call add(g:foo, "Many2")')
|
||||||
command('autocmd TabNew * -once :call add(g:foo, "Once3")')
|
command('autocmd TabNew * ++once :call add(g:foo, "Once3")')
|
||||||
eq(dedent([[
|
eq(dedent([[
|
||||||
|
|
||||||
--- Autocommands ---
|
--- Autocommands ---
|
||||||
@@ -103,27 +103,45 @@ describe('autocmd', function()
|
|||||||
funcs.execute('autocmd Tabnew'))
|
funcs.execute('autocmd Tabnew'))
|
||||||
|
|
||||||
--
|
--
|
||||||
-- ":autocmd ... -once" handlers can be deleted.
|
-- ":autocmd ... ++once" handlers can be deleted.
|
||||||
--
|
--
|
||||||
expected = {}
|
expected = {}
|
||||||
command('let g:foo = []')
|
command('let g:foo = []')
|
||||||
command('autocmd TabNew * -once :call add(g:foo, "Once1")')
|
command('autocmd TabNew * ++once :call add(g:foo, "Once1")')
|
||||||
command('autocmd! TabNew')
|
command('autocmd! TabNew')
|
||||||
command('tabnew')
|
command('tabnew')
|
||||||
eq(expected, eval('g:foo'))
|
eq(expected, eval('g:foo'))
|
||||||
|
|
||||||
--
|
--
|
||||||
-- ":autocmd ... <buffer> -once -nested"
|
-- ":autocmd ... <buffer> ++once ++nested"
|
||||||
--
|
--
|
||||||
expected = {
|
expected = {
|
||||||
'OptionSet-Once',
|
'OptionSet-Once',
|
||||||
'CursorMoved-Once',
|
'CursorMoved-Once',
|
||||||
}
|
}
|
||||||
command('let g:foo = []')
|
command('let g:foo = []')
|
||||||
command('autocmd OptionSet binary -nested -once :call add(g:foo, "OptionSet-Once")')
|
command('autocmd OptionSet binary ++nested ++once :call add(g:foo, "OptionSet-Once")')
|
||||||
command('autocmd CursorMoved <buffer> -once -nested setlocal binary|:call add(g:foo, "CursorMoved-Once")')
|
command('autocmd CursorMoved <buffer> ++once ++nested setlocal binary|:call add(g:foo, "CursorMoved-Once")')
|
||||||
command("put ='foo bar baz'")
|
command("put ='foo bar baz'")
|
||||||
feed('0llhlh')
|
feed('0llhlh')
|
||||||
eq(expected, eval('g:foo'))
|
eq(expected, eval('g:foo'))
|
||||||
|
|
||||||
|
--
|
||||||
|
-- :autocmd should not show empty section after ++once handlers expire.
|
||||||
|
--
|
||||||
|
expected = {
|
||||||
|
'Once1',
|
||||||
|
'Once2',
|
||||||
|
}
|
||||||
|
command('let g:foo = []')
|
||||||
|
command('autocmd! TabNew') -- Clear all TabNew handlers.
|
||||||
|
command('autocmd TabNew * ++once :call add(g:foo, "Once1")')
|
||||||
|
command('autocmd TabNew * ++once :call add(g:foo, "Once2")')
|
||||||
|
command('tabnew')
|
||||||
|
eq(expected, eval('g:foo'))
|
||||||
|
eq(dedent([[
|
||||||
|
|
||||||
|
--- Autocommands ---]]),
|
||||||
|
funcs.execute('autocmd Tabnew'))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
Reference in New Issue
Block a user