Patch 8.2.3268
Problem: Cannot use a block with :autocmd like with :command.
Solution: Add support for a {} block after :autocmd. (closes #8620)
Files: runtime/doc/autocmd.txt, runtime/doc/map.txt, src/autocmd.c,
src/proto/
autocmd.pro, src/usercmd.c, src/proto/
usercmd.pro,
src/ex_docmd.c, src/vim.h, src/testdir/test_autocmd.vim
*** ../vim-8.2.3267/runtime/doc/autocmd.txt 2021-01-31 17:02:06.246490203 +0100
--- runtime/doc/autocmd.txt 2021-08-01 14:19:11.551359402 +0200
***************
*** 76,81 ****
--- 76,87 ----
script. Thus this depends on where the autocmd is defined, not where it is
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
'|' appears before {cmd}. This works: >
:augroup mine | au! BufRead | augroup END
*** ../vim-8.2.3267/runtime/doc/map.txt 2021-07-27 21:17:28.483675842 +0200
--- runtime/doc/map.txt 2021-08-01 14:19:07.899370556 +0200
***************
*** 1552,1558 ****
Replacement text ~
!
The {repl} argument is normally one long string, possibly with "|" separated
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.
--- 1571,1577 ----
Replacement text ~
! *:command-repl*
The {repl} argument is normally one long string, possibly with "|" separated
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.
***************
*** 1561,1567 ****
echo 'hello'
g:calledMyCommand = true
}
! No nesting is supported.
The replacement text {repl} for a user defined command is scanned for special
escape sequences, using <...> notation. Escape sequences are replaced with
--- 1580,1587 ----
echo 'hello'
g:calledMyCommand = true
}
! No nesting is supported, inline functions cannot be used. Using `:normal`
! directly does not work, you can use it indirectly with `:execute`.
The replacement text {repl} for a user defined command is scanned for special
escape sequences, using <...> notation. Escape sequences are replaced with
*** ../vim-8.2.3267/src/autocmd.c 2021-04-03 13:19:23.102814497 +0200
--- src/autocmd.c 2021-08-01 14:35:10.456871122 +0200
***************
*** 258,264 ****
static char_u *event_nr2name(event_T event);
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 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 int au_find_group(char_u *name);
--- 258,264 ----
static char_u *event_nr2name(event_T event);
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, 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 void auto_next_pat(AutoPatCmd *apc, int stop_at_last);
static int au_find_group(char_u *name);
***************
*** 615,621 ****
for (current_augroup = -1; current_augroup < augroups.ga_len;
++current_augroup)
! do_autocmd((char_u *)"", TRUE);
for (i = 0; i < augroups.ga_len; ++i)
{
--- 615,621 ----
for (current_augroup = -1; current_augroup < augroups.ga_len;
++current_augroup)
! do_autocmd(NULL, (char_u *)"", TRUE);
for (i = 0; i < augroups.ga_len; ++i)
{
***************
*** 823,842 ****
* :autocmd * *.c show all autocommands for *.c files.
*
* Mostly a {group} argument can optionally appear before <event>.
*/
void
! do_autocmd(char_u *arg_in, int forceit)
{
char_u *arg = arg_in;
char_u *pat;
char_u *envpat = NULL;
char_u *cmd;
event_T event;
! int need_free = FALSE;
int nested = FALSE;
int once = FALSE;
int group;
int i;
if (*arg == '|')
{
--- 823,845 ----
* :autocmd * *.c show all autocommands for *.c files.
*
* Mostly a {group} argument can optionally appear before <event>.
+ * "eap" can be NULL.
*/
void
! do_autocmd(exarg_T *eap, char_u *arg_in, int forceit)
{
char_u *arg = arg_in;
char_u *pat;
char_u *envpat = NULL;
char_u *cmd;
+ int cmd_need_free = FALSE;
event_T event;
! char_u *tofree = NULL;
int nested = FALSE;
int once = FALSE;
int group;
int i;
+ int flags = 0;
if (*arg == '|')
{
***************
*** 935,944 ****
*/
if (*cmd != NUL)
{
cmd = expand_sfile(cmd);
if (cmd == NULL) // some error
return;
! need_free = TRUE;
}
}
--- 938,951 ----
*/
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);
if (cmd == NULL) // some error
return;
! cmd_need_free = TRUE;
}
}
***************
*** 962,980 ****
for (event = (event_T)0; (int)event < (int)NUM_EVENTS;
event = (event_T)((int)event + 1))
if (do_autocmd_event(event, pat,
! once, nested, cmd, forceit, group) == FAIL)
break;
}
else
{
while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
if (do_autocmd_event(event_name2nr(arg, &arg), pat,
! once, nested, cmd, forceit, group) == FAIL)
break;
}
! if (need_free)
vim_free(cmd);
vim_free(envpat);
}
--- 969,988 ----
for (event = (event_T)0; (int)event < (int)NUM_EVENTS;
event = (event_T)((int)event + 1))
if (do_autocmd_event(event, pat,
! once, nested, cmd, forceit, group, flags) == FAIL)
break;
}
else
{
while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
if (do_autocmd_event(event_name2nr(arg, &arg), pat,
! once, nested, cmd, forceit, group, flags) == FAIL)
break;
}
! if (cmd_need_free)
vim_free(cmd);
+ vim_free(tofree);
vim_free(envpat);
}
***************
*** 1024,1030 ****
int nested,
char_u *cmd,
int forceit,
! int group)
{
AutoPat *ap;
AutoPat **prev_ap;
--- 1032,1039 ----
int nested,
char_u *cmd,
int forceit,
! int group,
! int flags)
{
AutoPat *ap;
AutoPat **prev_ap;
***************
*** 1251,1256 ****
--- 1260,1267 ----
return FAIL;
ac->cmd = vim_strsave(cmd);
ac->script_ctx = current_sctx;
+ if (flags & UC_VIM9)
+ ac->script_ctx.sc_version = SCRIPT_VERSION_VIM9;
#ifdef FEAT_EVAL
ac->script_ctx.sc_lnum += SOURCING_LNUM;
#endif
*** ../vim-8.2.3267/src/proto/
autocmd.pro 2020-08-20 15:02:38.536534973 +0200
--- src/proto/
autocmd.pro 2021-08-01 14:31:16.769427927 +0200
***************
*** 6,12 ****
int check_ei(void);
char_u *au_event_disable(char *what);
void au_event_restore(char_u *old_ei);
! void do_autocmd(char_u *arg_in, int forceit);
int do_doautocmd(char_u *arg, int do_msg, int *did_something);
void ex_doautoall(exarg_T *eap);
int check_nomodeline(char_u **argp);
--- 6,12 ----
int check_ei(void);
char_u *au_event_disable(char *what);
void au_event_restore(char_u *old_ei);
! void do_autocmd(exarg_T *eap, char_u *arg_in, int forceit);
int do_doautocmd(char_u *arg, int do_msg, int *did_something);
void ex_doautoall(exarg_T *eap);
int check_nomodeline(char_u **argp);
*** ../vim-8.2.3267/src/usercmd.c 2021-07-27 21:17:28.483675842 +0200
--- src/usercmd.c 2021-08-01 14:35:29.132827267 +0200
***************
*** 114,122 ****
{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".
* Return cmdidx in "eap->cmdidx", flags in "eap->argt", idx in "eap->useridx".
--- 114,119 ----
***************
*** 975,980 ****
--- 972,1020 ----
}
/*
+ * 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
*/
void
***************
*** 1043,1080 ****
{
char_u *tofree = NULL;
! 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;
!
! // 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,
addr_type_arg, eap->forceit);
--- 1083,1089 ----
{
char_u *tofree = NULL;
! p = may_get_cmd_block(eap, p, &tofree, &flags);
uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg,
addr_type_arg, eap->forceit);
*** ../vim-8.2.3267/src/proto/
usercmd.pro 2020-10-26 18:46:49.480589241 +0100
--- src/proto/
usercmd.pro 2021-08-01 14:28:59.305764688 +0200
***************
*** 10,15 ****
--- 10,16 ----
int cmdcomplete_str_to_type(char_u *complete_str);
char *uc_fun_cmd(void);
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_comclear(exarg_T *eap);
void uc_clear(garray_T *gap);
*** ../vim-8.2.3267/src/ex_docmd.c 2021-07-31 22:17:25.045867805 +0200
--- src/ex_docmd.c 2021-08-01 14:30:32.953534388 +0200
***************
*** 5203,5209 ****
_(e_command_not_allowed_from_vimrc_in_current_dir_or_tag_search);
}
else if (eap->cmdidx == CMD_autocmd)
! do_autocmd(eap->arg, eap->forceit);
else
do_augroup(eap->arg, eap->forceit);
}
--- 5203,5209 ----
_(e_command_not_allowed_from_vimrc_in_current_dir_or_tag_search);
}
else if (eap->cmdidx == CMD_autocmd)
! do_autocmd(eap, eap->arg, eap->forceit);
else
do_augroup(eap->arg, eap->forceit);
}
*** ../vim-8.2.3267/src/vim.h 2021-07-29 22:48:50.107129898 +0200
--- src/vim.h 2021-08-01 14:36:02.344749512 +0200
***************
*** 2739,2742 ****
--- 2739,2747 ----
// flags for equal_type()
#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
*** ../vim-8.2.3267/src/testdir/test_autocmd.vim 2021-03-13 15:47:51.577753545 +0100
--- src/testdir/test_autocmd.vim 2021-08-01 14:44:52.523534994 +0200
***************
*** 2810,2814 ****
--- 2810,2830 ----
augroup END
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-8.2.3267/src/version.c 2021-08-01 14:08:50.780946552 +0200
--- src/version.c 2021-08-01 14:45:53.627397054 +0200
***************
*** 757,758 ****
--- 757,760 ----
{ /* Add new patch number below this line */
+ /**/
+ 3268,
/**/
--
"My particular problem is with registry entries, which seem to just
accumulate like plastic coffee cups..." -- Paul Moore
/// Bram Moolenaar -- Br...@Moolenaar.net --
http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features --
http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims --
http://ICCF-Holland.org ///