]> granicus.if.org Git - vim/commitdiff
patch 8.2.3268: cannot use a block with :autocmd like with :command v8.2.3268
authorBram Moolenaar <Bram@vim.org>
Sun, 1 Aug 2021 12:52:32 +0000 (14:52 +0200)
committerBram Moolenaar <Bram@vim.org>
Sun, 1 Aug 2021 12:52:32 +0000 (14:52 +0200)
Problem:    Cannot use a block with :autocmd like with :command.
Solution:   Add support for a {} block after :autocmd. (closes #8620)

runtime/doc/autocmd.txt
runtime/doc/map.txt
src/autocmd.c
src/ex_docmd.c
src/proto/autocmd.pro
src/proto/usercmd.pro
src/testdir/test_autocmd.vim
src/usercmd.c
src/version.c
src/vim.h

index 7614e8bd73d3b4eb8ffcea6c0bb49fc094aff8dd..d3ebc11fedc2e58ab49eb2120ead062ffc0570a9 100644 (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
 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
index 505f8152247c306184ebdcd6c71faac409a25c21..a10c85b7af7ed3d26180ccc422f1405c6377a3a4 100644 (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
@@ -1571,7 +1571,7 @@ feature.  Use the full name for new scripts.
 
 
 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.
@@ -1580,8 +1580,8 @@ Example: >
                echo 'hello'
                g:calledMyCommand = true
            }
-No nesting is supported.  Using `:normal` directly does not work, you can use
-it indirectly with `:execute`.
+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
index 4c12e729e0bee118a3fb79c99d236e721bcf81f1..83e990df01abb5a6b6e14ecb24addf8c742bee94 100644 (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 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 void auto_next_pat(AutoPatCmd *apc, int stop_at_last);
 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;
                                                            ++current_augroup)
-       do_autocmd((char_u *)"", TRUE);
+       do_autocmd(NULL, (char_u *)"", TRUE);
 
     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.
  *
  * Mostly a {group} argument can optionally appear before <event>.
+ * "eap" can be NULL.
  */
     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     *pat;
     char_u     *envpat = NULL;
     char_u     *cmd;
+    int                cmd_need_free = FALSE;
     event_T    event;
-    int                need_free = FALSE;
+    char_u     *tofree = NULL;
     int                nested = FALSE;
     int                once = FALSE;
     int                group;
     int                i;
+    int                flags = 0;
 
     if (*arg == '|')
     {
@@ -935,10 +938,14 @@ do_autocmd(char_u *arg_in, int forceit)
         */
        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;
-           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;
                                             event = (event_T)((int)event + 1))
                if (do_autocmd_event(event, pat,
-                                   once, nested, cmd, forceit, group) == FAIL)
+                            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) == FAIL)
+                         once, nested, cmd, forceit, group, flags) == FAIL)
                break;
     }
 
-    if (need_free)
+    if (cmd_need_free)
        vim_free(cmd);
+    vim_free(tofree);
     vim_free(envpat);
 }
 
@@ -1024,7 +1032,8 @@ do_autocmd_event(
     int                nested,
     char_u     *cmd,
     int                forceit,
-    int                group)
+    int                group,
+    int                flags)
 {
     AutoPat    *ap;
     AutoPat    **prev_ap;
@@ -1251,6 +1260,8 @@ do_autocmd_event(
                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
index c72e9c1b70c5d6fe5a87d32ad4746278a6299633..f2ff787e082006e2492e4632ee38bf83e25696ce 100644 (file)
@@ -5203,7 +5203,7 @@ ex_autocmd(exarg_T *eap)
              _(e_command_not_allowed_from_vimrc_in_current_dir_or_tag_search);
     }
     else if (eap->cmdidx == CMD_autocmd)
-       do_autocmd(eap->arg, eap->forceit);
+       do_autocmd(eap, eap->arg, eap->forceit);
     else
        do_augroup(eap->arg, eap->forceit);
 }
index 88eae5a78e1ea124c67817061a60e9711c611b68..24dd1ba8e9e70eb260c37927d9b4425232177081 100644 (file)
@@ -6,7 +6,7 @@ void free_all_autocmds(void);
 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);
+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);
index 9e16a80e6a7e8b9e33b93537e438afc48687ed26..e9230d5724631b3ac1c62df2e4e1e2ecc6823da4 100644 (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);
 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);
index 8abf911fa8cb7f0bfb4e3ff7a70455d901cf232a..4a7dd60f6d8be52764a635a738a022c59a6036f5 100644 (file)
@@ -2810,5 +2810,21 @@ func Test_autocmd_vimgrep()
   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
index 09e7b26b3c4db48844f19c305b24417392eb3dd2..48f84b1c3b21846da18de7a4414d49210068421e 100644 (file)
@@ -114,9 +114,6 @@ static struct
     {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".
@@ -974,6 +971,49 @@ 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
  */
@@ -1043,38 +1083,7 @@ ex_command(exarg_T *eap)
     {
        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;
-       }
+       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);
index 962f002d5d33f6f9312575a57c5fd2fabcd0d0da..b5173e6d010087fae83b183ca19d0d89e20d5b05 100644 (file)
@@ -755,6 +755,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    3268,
 /**/
     3267,
 /**/
index 54d507eefe9cd7b2e8f04744c06fbc9d015bf7c2..7d239867137879f827b3eeb2e7c2f7441bb37e8e 100644 (file)
--- a/src/vim.h
+++ b/src/vim.h
@@ -2739,4 +2739,9 @@ long elapsed(DWORD start_tick);
 // 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