Skip to content

Commit 73b8b0a

Browse files
committed
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)
1 parent 6db660b commit 73b8b0a

File tree

10 files changed

+100
-50
lines changed

10 files changed

+100
-50
lines changed

runtime/doc/autocmd.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ and in a `:def` function) then {cmd} will be executed as in Vim9
7676
script. Thus this depends on where the autocmd is defined, not where it is
7777
triggered.
7878

79+
{cmd} can use a block, like with `:command`, see |:command-repl|. Example: >
80+
au BufReadPost *.xml {
81+
setlocal matchpairs+=<:>
82+
/<start
83+
}
84+
7985
Note: The ":autocmd" command can only be followed by another command when the
8086
'|' appears before {cmd}. This works: >
8187
:augroup mine | au! BufRead | augroup END

runtime/doc/map.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*map.txt* For Vim version 8.2. Last change: 2021 Jul 28
1+
*map.txt* For Vim version 8.2. Last change: 2021 Aug 01
22

33

44
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -1571,7 +1571,7 @@ feature. Use the full name for new scripts.
15711571

15721572

15731573
Replacement text ~
1574-
1574+
*:command-repl*
15751575
The {repl} argument is normally one long string, possibly with "|" separated
15761576
commands. A special case is when the argument is "{", then the following
15771577
lines, up to a line starting with "}" are used and |Vim9| syntax applies.
@@ -1580,8 +1580,8 @@ Example: >
15801580
echo 'hello'
15811581
g:calledMyCommand = true
15821582
}
1583-
No nesting is supported. Using `:normal` directly does not work, you can use
1584-
it indirectly with `:execute`.
1583+
No nesting is supported, inline functions cannot be used. Using `:normal`
1584+
directly does not work, you can use it indirectly with `:execute`.
15851585

15861586
The replacement text {repl} for a user defined command is scanned for special
15871587
escape sequences, using <...> notation. Escape sequences are replaced with

src/autocmd.c

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ static int au_need_clean = FALSE; // need to delete marked patterns
258258

259259
static char_u *event_nr2name(event_T event);
260260
static int au_get_grouparg(char_u **argp);
261-
static int do_autocmd_event(event_T event, char_u *pat, int once, int nested, char_u *cmd, int forceit, int group);
261+
static int do_autocmd_event(event_T event, char_u *pat, int once, int nested, char_u *cmd, int forceit, int group, int flags);
262262
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);
263263
static void auto_next_pat(AutoPatCmd *apc, int stop_at_last);
264264
static int au_find_group(char_u *name);
@@ -615,7 +615,7 @@ free_all_autocmds(void)
615615

616616
for (current_augroup = -1; current_augroup < augroups.ga_len;
617617
++current_augroup)
618-
do_autocmd((char_u *)"", TRUE);
618+
do_autocmd(NULL, (char_u *)"", TRUE);
619619

620620
for (i = 0; i < augroups.ga_len; ++i)
621621
{
@@ -823,20 +823,23 @@ au_event_restore(char_u *old_ei)
823823
* :autocmd * *.c show all autocommands for *.c files.
824824
*
825825
* Mostly a {group} argument can optionally appear before <event>.
826+
* "eap" can be NULL.
826827
*/
827828
void
828-
do_autocmd(char_u *arg_in, int forceit)
829+
do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
829830
{
830831
char_u *arg = arg_in;
831832
char_u *pat;
832833
char_u *envpat = NULL;
833834
char_u *cmd;
835+
int cmd_need_free = FALSE;
834836
event_T event;
835-
int need_free = FALSE;
837+
char_u *tofree = NULL;
836838
int nested = FALSE;
837839
int once = FALSE;
838840
int group;
839841
int i;
842+
int flags = 0;
840843

841844
if (*arg == '|')
842845
{
@@ -935,10 +938,14 @@ do_autocmd(char_u *arg_in, int forceit)
935938
*/
936939
if (*cmd != NUL)
937940
{
941+
if (eap != NULL)
942+
// Read a {} block if it follows.
943+
cmd = may_get_cmd_block(eap, cmd, &tofree, &flags);
944+
938945
cmd = expand_sfile(cmd);
939946
if (cmd == NULL) // some error
940947
return;
941-
need_free = TRUE;
948+
cmd_need_free = TRUE;
942949
}
943950
}
944951

@@ -962,19 +969,20 @@ do_autocmd(char_u *arg_in, int forceit)
962969
for (event = (event_T)0; (int)event < (int)NUM_EVENTS;
963970
event = (event_T)((int)event + 1))
964971
if (do_autocmd_event(event, pat,
965-
once, nested, cmd, forceit, group) == FAIL)
972+
once, nested, cmd, forceit, group, flags) == FAIL)
966973
break;
967974
}
968975
else
969976
{
970977
while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
971978
if (do_autocmd_event(event_name2nr(arg, &arg), pat,
972-
once, nested, cmd, forceit, group) == FAIL)
979+
once, nested, cmd, forceit, group, flags) == FAIL)
973980
break;
974981
}
975982

976-
if (need_free)
983+
if (cmd_need_free)
977984
vim_free(cmd);
985+
vim_free(tofree);
978986
vim_free(envpat);
979987
}
980988

@@ -1024,7 +1032,8 @@ do_autocmd_event(
10241032
int nested,
10251033
char_u *cmd,
10261034
int forceit,
1027-
int group)
1035+
int group,
1036+
int flags)
10281037
{
10291038
AutoPat *ap;
10301039
AutoPat **prev_ap;
@@ -1251,6 +1260,8 @@ do_autocmd_event(
12511260
return FAIL;
12521261
ac->cmd = vim_strsave(cmd);
12531262
ac->script_ctx = current_sctx;
1263+
if (flags & UC_VIM9)
1264+
ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
12541265
#ifdef FEAT_EVAL
12551266
ac->script_ctx.sc_lnum += SOURCING_LNUM;
12561267
#endif

src/ex_docmd.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5203,7 +5203,7 @@ ex_autocmd(exarg_T *eap)
52035203
_(e_command_not_allowed_from_vimrc_in_current_dir_or_tag_search);
52045204
}
52055205
else if (eap->cmdidx == CMD_autocmd)
5206-
do_autocmd(eap->arg, eap->forceit);
5206+
do_autocmd(eap, eap->arg, eap->forceit);
52075207
else
52085208
do_augroup(eap->arg, eap->forceit);
52095209
}

src/proto/autocmd.pro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ void free_all_autocmds(void);
66
int check_ei(void);
77
char_u *au_event_disable(char *what);
88
void au_event_restore(char_u *old_ei);
9-
void do_autocmd(char_u *arg_in, int forceit);
9+
void do_autocmd(exarg_T *eap, char_u *arg_in, int forceit);
1010
int do_doautocmd(char_u *arg, int do_msg, int *did_something);
1111
void ex_doautoall(exarg_T *eap);
1212
int check_nomodeline(char_u **argp);

src/proto/usercmd.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ char_u *get_user_cmd_complete(expand_T *xp, int idx);
1010
int cmdcomplete_str_to_type(char_u *complete_str);
1111
char *uc_fun_cmd(void);
1212
int parse_compl_arg(char_u *value, int vallen, int *complp, long *argt, char_u **compl_arg);
13+
char_u *may_get_cmd_block(exarg_T *eap, char_u *p, char_u **tofree, int *flags);
1314
void ex_command(exarg_T *eap);
1415
void ex_comclear(exarg_T *eap);
1516
void uc_clear(garray_T *gap);

src/testdir/test_autocmd.vim

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2810,5 +2810,21 @@ func Test_autocmd_vimgrep()
28102810
augroup END
28112811
endfunc
28122812

2813+
func Test_autocmd_with_block()
2814+
augroup block_testing
2815+
au BufReadPost *.xml {
2816+
setlocal matchpairs+=<:>
2817+
/<start
2818+
}
2819+
augroup END
2820+
2821+
let expected = "\n--- Autocommands ---\nblock_testing BufRead\n *.xml {^@ setlocal matchpairs+=<:>^@ /<start^@ }"
2822+
call assert_equal(expected, execute('au BufReadPost *.xml'))
2823+
2824+
augroup block_testing
2825+
au!
2826+
augroup END
2827+
endfunc
2828+
28132829

28142830
" vim: shiftwidth=2 sts=2 expandtab

src/usercmd.c

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,6 @@ static struct
114114
{ADDR_NONE, NULL, NULL}
115115
};
116116

117-
#define UC_BUFFER 1 // -buffer: local to current buffer
118-
#define UC_VIM9 2 // {} argument: Vim9 syntax.
119-
120117
/*
121118
* Search for a user command that matches "eap->cmd".
122119
* Return cmdidx in "eap->cmdidx", flags in "eap->argt", idx in "eap->useridx".
@@ -974,6 +971,49 @@ uc_add_command(
974971
return FAIL;
975972
}
976973

974+
/*
975+
* If "p" starts with "{" then read a block of commands until "}".
976+
* Used for ":command" and ":autocmd".
977+
*/
978+
char_u *
979+
may_get_cmd_block(exarg_T *eap, char_u *p, char_u **tofree, int *flags)
980+
{
981+
char_u *retp = p;
982+
983+
if (*p == '{' && ends_excmd2(eap->arg, skipwhite(p + 1))
984+
&& eap->getline != NULL)
985+
{
986+
garray_T ga;
987+
char_u *line = NULL;
988+
989+
ga_init2(&ga, sizeof(char_u *), 10);
990+
if (ga_add_string(&ga, p) == FAIL)
991+
return retp;
992+
993+
// Read lines between '{' and '}'. Does not support nesting or
994+
// here-doc constructs.
995+
for (;;)
996+
{
997+
vim_free(line);
998+
if ((line = eap->getline(':', eap->cookie,
999+
0, GETLINE_CONCAT_CONTBAR)) == NULL)
1000+
{
1001+
emsg(_(e_missing_rcurly));
1002+
break;
1003+
}
1004+
if (ga_add_string(&ga, line) == FAIL)
1005+
break;
1006+
if (*skipwhite(line) == '}')
1007+
break;
1008+
}
1009+
vim_free(line);
1010+
retp = *tofree = ga_concat_strings(&ga, "\n");
1011+
ga_clear_strings(&ga);
1012+
*flags |= UC_VIM9;
1013+
}
1014+
return retp;
1015+
}
1016+
9771017
/*
9781018
* ":command ..." implementation
9791019
*/
@@ -1043,38 +1083,7 @@ ex_command(exarg_T *eap)
10431083
{
10441084
char_u *tofree = NULL;
10451085

1046-
if (*p == '{' && ends_excmd2(eap->arg, skipwhite(p + 1))
1047-
&& eap->getline != NULL)
1048-
{
1049-
garray_T ga;
1050-
char_u *line = NULL;
1051-
1052-
ga_init2(&ga, sizeof(char_u *), 10);
1053-
if (ga_add_string(&ga, p) == FAIL)
1054-
return;
1055-
1056-
// Read lines between '{' and '}'. Does not support nesting or
1057-
// here-doc constructs.
1058-
//
1059-
for (;;)
1060-
{
1061-
vim_free(line);
1062-
if ((line = eap->getline(':', eap->cookie,
1063-
0, GETLINE_CONCAT_CONTBAR)) == NULL)
1064-
{
1065-
emsg(_(e_missing_rcurly));
1066-
break;
1067-
}
1068-
if (ga_add_string(&ga, line) == FAIL)
1069-
break;
1070-
if (*skipwhite(line) == '}')
1071-
break;
1072-
}
1073-
vim_free(line);
1074-
p = tofree = ga_concat_strings(&ga, "\n");
1075-
ga_clear_strings(&ga);
1076-
flags |= UC_VIM9;
1077-
}
1086+
p = may_get_cmd_block(eap, p, &tofree, &flags);
10781087

10791088
uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg,
10801089
addr_type_arg, eap->forceit);

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,8 @@ static char *(features[]) =
755755

756756
static int included_patches[] =
757757
{ /* Add new patch number below this line */
758+
/**/
759+
3268,
758760
/**/
759761
3267,
760762
/**/

src/vim.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2739,4 +2739,9 @@ long elapsed(DWORD start_tick);
27392739
// flags for equal_type()
27402740
#define ETYPE_ARG_UNKNOWN 1
27412741

2742+
// flags used by user commands and :autocmd
2743+
#define UC_BUFFER 1 // -buffer: local to current buffer
2744+
#define UC_VIM9 2 // {} argument: Vim9 syntax.
2745+
2746+
27422747
#endif // VIM__H

0 commit comments

Comments
 (0)