0
0
mirror of https://github.com/vim/vim.git synced 2025-07-26 11:04:33 -04:00

patch 8.2.3268: cannot use a block with :autocmd like with :command

Problem:    Cannot use a block with :autocmd like with :command.
Solution:   Add support for a {} block after :autocmd. (closes #8620)
This commit is contained in:
Bram Moolenaar 2021-08-01 14:52:32 +02:00
parent 6db660bed9
commit 73b8b0ae3a
10 changed files with 100 additions and 50 deletions

View File

@ -76,6 +76,12 @@ and in a `:def` function) then {cmd} will be executed as in Vim9
script. Thus this depends on where the autocmd is defined, not where it is script. Thus this depends on where the autocmd is defined, not where it is
triggered. triggered.
{cmd} can use a block, like with `:command`, see |:command-repl|. Example: >
au BufReadPost *.xml {
setlocal matchpairs+=<:>
/<start
}
Note: The ":autocmd" command can only be followed by another command when the Note: The ":autocmd" command can only be followed by another command when the
'|' appears before {cmd}. This works: > '|' appears before {cmd}. This works: >
:augroup mine | au! BufRead | augroup END :augroup mine | au! BufRead | augroup END

View File

@ -1,4 +1,4 @@
*map.txt* For Vim version 8.2. Last change: 2021 Jul 28 *map.txt* For Vim version 8.2. Last change: 2021 Aug 01
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@ -1571,7 +1571,7 @@ feature. Use the full name for new scripts.
Replacement text ~ Replacement text ~
*:command-repl*
The {repl} argument is normally one long string, possibly with "|" separated The {repl} argument is normally one long string, possibly with "|" separated
commands. A special case is when the argument is "{", then the following commands. A special case is when the argument is "{", then the following
lines, up to a line starting with "}" are used and |Vim9| syntax applies. lines, up to a line starting with "}" are used and |Vim9| syntax applies.
@ -1580,8 +1580,8 @@ Example: >
echo 'hello' echo 'hello'
g:calledMyCommand = true g:calledMyCommand = true
} }
No nesting is supported. Using `:normal` directly does not work, you can use No nesting is supported, inline functions cannot be used. Using `:normal`
it indirectly with `:execute`. directly does not work, you can use it indirectly with `:execute`.
The replacement text {repl} for a user defined command is scanned for special The replacement text {repl} for a user defined command is scanned for special
escape sequences, using <...> notation. Escape sequences are replaced with escape sequences, using <...> notation. Escape sequences are replaced with

View File

@ -258,7 +258,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 once, 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, int flags);
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);
@ -615,7 +615,7 @@ free_all_autocmds(void)
for (current_augroup = -1; current_augroup < augroups.ga_len; for (current_augroup = -1; current_augroup < augroups.ga_len;
++current_augroup) ++current_augroup)
do_autocmd((char_u *)"", TRUE); do_autocmd(NULL, (char_u *)"", TRUE);
for (i = 0; i < augroups.ga_len; ++i) for (i = 0; i < augroups.ga_len; ++i)
{ {
@ -823,20 +823,23 @@ au_event_restore(char_u *old_ei)
* :autocmd * *.c show all autocommands for *.c files. * :autocmd * *.c show all autocommands for *.c files.
* *
* Mostly a {group} argument can optionally appear before <event>. * Mostly a {group} argument can optionally appear before <event>.
* "eap" can be NULL.
*/ */
void void
do_autocmd(char_u *arg_in, int forceit) do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
{ {
char_u *arg = arg_in; char_u *arg = arg_in;
char_u *pat; char_u *pat;
char_u *envpat = NULL; char_u *envpat = NULL;
char_u *cmd; char_u *cmd;
int cmd_need_free = FALSE;
event_T event; event_T event;
int need_free = FALSE; char_u *tofree = NULL;
int nested = FALSE; int nested = FALSE;
int once = FALSE; int once = FALSE;
int group; int group;
int i; int i;
int flags = 0;
if (*arg == '|') if (*arg == '|')
{ {
@ -935,10 +938,14 @@ do_autocmd(char_u *arg_in, int forceit)
*/ */
if (*cmd != NUL) if (*cmd != NUL)
{ {
if (eap != NULL)
// Read a {} block if it follows.
cmd = may_get_cmd_block(eap, cmd, &tofree, &flags);
cmd = expand_sfile(cmd); cmd = expand_sfile(cmd);
if (cmd == NULL) // some error if (cmd == NULL) // some error
return; return;
need_free = TRUE; cmd_need_free = TRUE;
} }
} }
@ -962,19 +969,20 @@ 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,
once, nested, cmd, forceit, group) == FAIL) once, nested, cmd, forceit, group, flags) == 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,
once, nested, cmd, forceit, group) == FAIL) once, nested, cmd, forceit, group, flags) == FAIL)
break; break;
} }
if (need_free) if (cmd_need_free)
vim_free(cmd); vim_free(cmd);
vim_free(tofree);
vim_free(envpat); vim_free(envpat);
} }
@ -1024,7 +1032,8 @@ do_autocmd_event(
int nested, int nested,
char_u *cmd, char_u *cmd,
int forceit, int forceit,
int group) int group,
int flags)
{ {
AutoPat *ap; AutoPat *ap;
AutoPat **prev_ap; AutoPat **prev_ap;
@ -1251,6 +1260,8 @@ do_autocmd_event(
return FAIL; return FAIL;
ac->cmd = vim_strsave(cmd); ac->cmd = vim_strsave(cmd);
ac->script_ctx = current_sctx; ac->script_ctx = current_sctx;
if (flags & UC_VIM9)
ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
ac->script_ctx.sc_lnum += SOURCING_LNUM; ac->script_ctx.sc_lnum += SOURCING_LNUM;
#endif #endif

View File

@ -5203,7 +5203,7 @@ ex_autocmd(exarg_T *eap)
_(e_command_not_allowed_from_vimrc_in_current_dir_or_tag_search); _(e_command_not_allowed_from_vimrc_in_current_dir_or_tag_search);
} }
else if (eap->cmdidx == CMD_autocmd) else if (eap->cmdidx == CMD_autocmd)
do_autocmd(eap->arg, eap->forceit); do_autocmd(eap, eap->arg, eap->forceit);
else else
do_augroup(eap->arg, eap->forceit); do_augroup(eap->arg, eap->forceit);
} }

View File

@ -6,7 +6,7 @@ void free_all_autocmds(void);
int check_ei(void); int check_ei(void);
char_u *au_event_disable(char *what); char_u *au_event_disable(char *what);
void au_event_restore(char_u *old_ei); void au_event_restore(char_u *old_ei);
void do_autocmd(char_u *arg_in, int forceit); void do_autocmd(exarg_T *eap, char_u *arg_in, int forceit);
int do_doautocmd(char_u *arg, int do_msg, int *did_something); int do_doautocmd(char_u *arg, int do_msg, int *did_something);
void ex_doautoall(exarg_T *eap); void ex_doautoall(exarg_T *eap);
int check_nomodeline(char_u **argp); int check_nomodeline(char_u **argp);

View File

@ -10,6 +10,7 @@ char_u *get_user_cmd_complete(expand_T *xp, int idx);
int cmdcomplete_str_to_type(char_u *complete_str); int cmdcomplete_str_to_type(char_u *complete_str);
char *uc_fun_cmd(void); char *uc_fun_cmd(void);
int parse_compl_arg(char_u *value, int vallen, int *complp, long *argt, char_u **compl_arg); int parse_compl_arg(char_u *value, int vallen, int *complp, long *argt, char_u **compl_arg);
char_u *may_get_cmd_block(exarg_T *eap, char_u *p, char_u **tofree, int *flags);
void ex_command(exarg_T *eap); void ex_command(exarg_T *eap);
void ex_comclear(exarg_T *eap); void ex_comclear(exarg_T *eap);
void uc_clear(garray_T *gap); void uc_clear(garray_T *gap);

View File

@ -2810,5 +2810,21 @@ func Test_autocmd_vimgrep()
augroup END augroup END
endfunc endfunc
func Test_autocmd_with_block()
augroup block_testing
au BufReadPost *.xml {
setlocal matchpairs+=<:>
/<start
}
augroup END
let expected = "\n--- Autocommands ---\nblock_testing BufRead\n *.xml {^@ setlocal matchpairs+=<:>^@ /<start^@ }"
call assert_equal(expected, execute('au BufReadPost *.xml'))
augroup block_testing
au!
augroup END
endfunc
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab

View File

@ -114,9 +114,6 @@ static struct
{ADDR_NONE, NULL, NULL} {ADDR_NONE, NULL, NULL}
}; };
#define UC_BUFFER 1 // -buffer: local to current buffer
#define UC_VIM9 2 // {} argument: Vim9 syntax.
/* /*
* Search for a user command that matches "eap->cmd". * Search for a user command that matches "eap->cmd".
* Return cmdidx in "eap->cmdidx", flags in "eap->argt", idx in "eap->useridx". * Return cmdidx in "eap->cmdidx", flags in "eap->argt", idx in "eap->useridx".
@ -974,6 +971,49 @@ fail:
return FAIL; return FAIL;
} }
/*
* If "p" starts with "{" then read a block of commands until "}".
* Used for ":command" and ":autocmd".
*/
char_u *
may_get_cmd_block(exarg_T *eap, char_u *p, char_u **tofree, int *flags)
{
char_u *retp = p;
if (*p == '{' && ends_excmd2(eap->arg, skipwhite(p + 1))
&& eap->getline != NULL)
{
garray_T ga;
char_u *line = NULL;
ga_init2(&ga, sizeof(char_u *), 10);
if (ga_add_string(&ga, p) == FAIL)
return retp;
// Read lines between '{' and '}'. Does not support nesting or
// here-doc constructs.
for (;;)
{
vim_free(line);
if ((line = eap->getline(':', eap->cookie,
0, GETLINE_CONCAT_CONTBAR)) == NULL)
{
emsg(_(e_missing_rcurly));
break;
}
if (ga_add_string(&ga, line) == FAIL)
break;
if (*skipwhite(line) == '}')
break;
}
vim_free(line);
retp = *tofree = ga_concat_strings(&ga, "\n");
ga_clear_strings(&ga);
*flags |= UC_VIM9;
}
return retp;
}
/* /*
* ":command ..." implementation * ":command ..." implementation
*/ */
@ -1043,38 +1083,7 @@ ex_command(exarg_T *eap)
{ {
char_u *tofree = NULL; char_u *tofree = NULL;
if (*p == '{' && ends_excmd2(eap->arg, skipwhite(p + 1)) p = may_get_cmd_block(eap, p, &tofree, &flags);
&& eap->getline != NULL)
{
garray_T ga;
char_u *line = NULL;
ga_init2(&ga, sizeof(char_u *), 10);
if (ga_add_string(&ga, p) == FAIL)
return;
// Read lines between '{' and '}'. Does not support nesting or
// here-doc constructs.
//
for (;;)
{
vim_free(line);
if ((line = eap->getline(':', eap->cookie,
0, GETLINE_CONCAT_CONTBAR)) == NULL)
{
emsg(_(e_missing_rcurly));
break;
}
if (ga_add_string(&ga, line) == FAIL)
break;
if (*skipwhite(line) == '}')
break;
}
vim_free(line);
p = tofree = ga_concat_strings(&ga, "\n");
ga_clear_strings(&ga);
flags |= UC_VIM9;
}
uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg, uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg,
addr_type_arg, eap->forceit); addr_type_arg, eap->forceit);

View File

@ -755,6 +755,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 */
/**/
3268,
/**/ /**/
3267, 3267,
/**/ /**/

View File

@ -2739,4 +2739,9 @@ long elapsed(DWORD start_tick);
// flags for equal_type() // flags for equal_type()
#define ETYPE_ARG_UNKNOWN 1 #define ETYPE_ARG_UNKNOWN 1
// flags used by user commands and :autocmd
#define UC_BUFFER 1 // -buffer: local to current buffer
#define UC_VIM9 2 // {} argument: Vim9 syntax.
#endif // VIM__H #endif // VIM__H