To: vim_dev@googlegroups.com Subject: Patch 8.2.3268 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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+=<:> + / :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 . */ 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 . + * "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+=<:> + /