]> granicus.if.org Git - vim/commitdiff
patch 8.2.5030: autocmd_add() can only handle one event and pattern v8.2.5030
authorYegappan Lakshmanan <yegappan@yahoo.com>
Fri, 27 May 2022 17:05:33 +0000 (18:05 +0100)
committerBram Moolenaar <Bram@vim.org>
Fri, 27 May 2022 17:05:33 +0000 (18:05 +0100)
Problem:    autocmd_add() can only handle one event and pattern.
Solution:   Support a list of events and patterns. (Yegappan Lakshmanan,
            closes #10483)

runtime/doc/builtin.txt
src/autocmd.c
src/errors.h
src/testdir/test_autocmd.vim
src/version.c

index 0aa2effcd0f122bcb9dcea609da153b82e9a2ff4..371670ceba216d487915ac11dd24323f54ad22c3 100644 (file)
@@ -938,7 +938,8 @@ autocmd_add({acmds})                                        *autocmd_add()*
                                item is ignored.
                    cmd         Ex command to execute for this autocmd event
                    event       autocmd event name. Refer to |autocmd-events|.
-                               TODO: currently only accepts one event.
+                               This can be either a String with a single
+                               event name or a List of event names.
                    group       autocmd group name. Refer to |autocmd-groups|.
                                If this group doesn't exist then it is
                                created.  If not specified or empty, then the
@@ -950,7 +951,9 @@ autocmd_add({acmds})                                        *autocmd_add()*
                                |autocmd-once|.
                    pattern     autocmd pattern string. Refer to
                                |autocmd-patterns|.  If "bufnr" item is
-                               present, then this item is ignored.
+                               present, then this item is ignored.  This can
+                               be a String with a single pattern or a List of
+                               patterns.
                    replace     boolean flag, set to v:true to remove all the
                                commands associated with the specified autocmd
                                event and group and add the {cmd}.  This is
index c9733ba5d34848ef37c075c098d6b212d0ea6047..06cd02850187bb33e570599fe02aa4ae3a789d13 100644 (file)
@@ -2754,16 +2754,22 @@ theend:
     static void
 autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
 {
-    list_T     *event_list;
+    list_T     *aucmd_list;
     listitem_T *li;
     dict_T     *event_dict;
+    dictitem_T *di;
     char_u     *event_name = NULL;
+    list_T     *event_list;
+    listitem_T *eli;
     event_T    event;
     char_u     *group_name = NULL;
     int                group;
     char_u     *pat = NULL;
+    list_T     *pat_list;
+    listitem_T *pli;
     char_u     *cmd = NULL;
     char_u     *end;
+    char_u     *p;
     int                once;
     int                nested;
     int                replace;                // replace the cmd for a group/event
@@ -2776,16 +2782,18 @@ autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
     if (check_for_list_arg(argvars, 0) == FAIL)
        return;
 
-    event_list = argvars[0].vval.v_list;
-    if (event_list == NULL)
+    aucmd_list = argvars[0].vval.v_list;
+    if (aucmd_list == NULL)
        return;
 
-    FOR_ALL_LIST_ITEMS(event_list, li)
+    FOR_ALL_LIST_ITEMS(aucmd_list, li)
     {
-       VIM_CLEAR(event_name);
        VIM_CLEAR(group_name);
-       VIM_CLEAR(pat);
        VIM_CLEAR(cmd);
+       event_name = NULL;
+       event_list = NULL;
+       pat = NULL;
+       pat_list = NULL;
 
        if (li->li_tv.v_type != VAR_DICT)
            continue;
@@ -2794,30 +2802,32 @@ autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
        if (event_dict == NULL)
            continue;
 
-       event_name = dict_get_string(event_dict, (char_u *)"event", TRUE);
-       if (event_name == NULL)
-       {
-           if (delete)
-               // if the event name is not specified, delete all the events
-               event = NUM_EVENTS;
-           else
-               continue;
-       }
-       else
+       di = dict_find(event_dict, (char_u *)"event", -1);
+       if (di != NULL)
        {
-           if (delete && event_name[0] == '*' && event_name[1] == NUL)
-               // if the event name is '*', delete all the events
-               event = NUM_EVENTS;
-           else
+           if (di->di_tv.v_type == VAR_STRING)
            {
-               event = event_name2nr(event_name, &end);
-               if (event == NUM_EVENTS)
+               event_name = di->di_tv.vval.v_string;
+               if (event_name == NULL)
                {
-                   semsg(_(e_no_such_event_str), event_name);
-                   retval = VVAL_FALSE;
-                   break;
+                   emsg(_(e_string_required));
+                   continue;
+               }
+           }
+           else if (di->di_tv.v_type == VAR_LIST)
+           {
+               event_list = di->di_tv.vval.v_list;
+               if (event_list == NULL)
+               {
+                   emsg(_(e_list_required));
+                   continue;
                }
            }
+           else
+           {
+               emsg(_(e_string_or_list_expected));
+               continue;
+           }
        }
 
        group_name = dict_get_string(event_dict, (char_u *)"group", TRUE);
@@ -2859,21 +2869,40 @@ autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
            if (bnum == -1)
                continue;
 
-           pat = alloc(128 + 1);
-           if (pat == NULL)
-               continue;
-           vim_snprintf((char *)pat, 128, "<buffer=%d>", (int)bnum);
+           vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
+           pat = IObuff;
        }
        else
        {
-           pat = dict_get_string(event_dict, (char_u *)"pattern", TRUE);
-           if (pat == NULL)
+           di = dict_find(event_dict, (char_u *)"pattern", -1);
+           if (di != NULL)
            {
-               if (delete)
-                   pat = vim_strsave((char_u *)"");
+               if (di->di_tv.v_type == VAR_STRING)
+               {
+                   pat = di->di_tv.vval.v_string;
+                   if (pat == NULL)
+                   {
+                       emsg(_(e_string_required));
+                       continue;
+                   }
+               }
+               else if (di->di_tv.v_type == VAR_LIST)
+               {
+                   pat_list = di->di_tv.vval.v_list;
+                   if (pat_list == NULL)
+                   {
+                       emsg(_(e_list_required));
+                       continue;
+                   }
+               }
                else
+               {
+                   emsg(_(e_string_or_list_expected));
                    continue;
+               }
            }
+           else if (delete)
+               pat = (char_u *)"";
        }
 
        once = dict_get_bool(event_dict, (char_u *)"once", FALSE);
@@ -2891,9 +2920,10 @@ autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
                continue;
        }
 
-       if (event == NUM_EVENTS)
+       if (delete && (event_name == NULL
+                   || (event_name[0] == '*' && event_name[1] == NUL)))
        {
-           // event is '*', apply for all the events
+           // if the event name is not specified or '*', delete all the events
            for (event = (event_T)0; (int)event < NUM_EVENTS;
                    event = (event_T)((int)event + 1))
            {
@@ -2907,11 +2937,76 @@ autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
        }
        else
        {
-           if (do_autocmd_event(event, pat, once, nested, cmd,
-                                       delete | replace, group, 0) == FAIL)
+           eli = NULL;
+           end = NULL;
+           while (TRUE)
            {
-               retval = VVAL_FALSE;
-               break;
+               if (event_list != NULL)
+               {
+                   if (eli == NULL)
+                       eli = event_list->lv_first;
+                   else
+                       eli = eli->li_next;
+                   if (eli == NULL)
+                       break;
+                   if (eli->li_tv.v_type != VAR_STRING
+                           || eli->li_tv.vval.v_string == NULL)
+                   {
+                       emsg(_(e_string_required));
+                       continue;
+                   }
+                   p = eli->li_tv.vval.v_string;
+               }
+               else
+               {
+                   if (end == NULL)
+                       p = end = event_name;
+                   if (end == NULL || *end == NUL)
+                       break;
+               }
+               if (p == NULL)
+                   continue;
+
+               event = event_name2nr(p, &end);
+               if (event == NUM_EVENTS || *end != NUL)
+               {
+                   semsg(_(e_no_such_event_str), p);
+                   retval = VVAL_FALSE;
+                   break;
+               }
+               if (pat != NULL)
+               {
+                   if (do_autocmd_event(event, pat, once, nested, cmd,
+                               delete | replace, group, 0) == FAIL)
+                   {
+                       retval = VVAL_FALSE;
+                       break;
+                   }
+               }
+               else if (pat_list != NULL)
+               {
+                   FOR_ALL_LIST_ITEMS(pat_list, pli)
+                   {
+                       if (pli->li_tv.v_type != VAR_STRING
+                               || pli->li_tv.vval.v_string == NULL)
+                       {
+                           emsg(_(e_string_required));
+                           continue;
+                       }
+                       if (do_autocmd_event(event,
+                                   pli->li_tv.vval.v_string, once, nested,
+                                   cmd, delete | replace, group, 0) ==
+                               FAIL)
+                       {
+                           retval = VVAL_FALSE;
+                           break;
+                       }
+                   }
+                   if (retval == VVAL_FALSE)
+                       break;
+               }
+               if (event_name != NULL)
+                   p = end;
            }
        }
 
@@ -2925,9 +3020,7 @@ autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
            au_del_group(group_name);
     }
 
-    VIM_CLEAR(event_name);
     VIM_CLEAR(group_name);
-    VIM_CLEAR(pat);
     VIM_CLEAR(cmd);
 
     current_augroup = save_augroup;
index 4e02986a77891ea54ec80fd5a60fa805cfc3009b..9bdc77d90d170707c6472b08767b1f7ac9fbad48 100644 (file)
@@ -1184,7 +1184,7 @@ EXTERN char e_invalid_argument_str[]
        INIT(= N_("E475: Invalid argument: %s"));
 EXTERN char e_invalid_value_for_argument_str[]
        INIT(= N_("E475: Invalid value for argument %s"));
-#if defined(FEAT_JOB_CHANNEL) || defined(FEAT_PROP_POPUP)
+#if defined(FEAT_JOB_CHANNEL) || defined(FEAT_PROP_POPUP) || defined(FEAT_EVAL)
 EXTERN char e_invalid_value_for_argument_str_str[]
        INIT(= N_("E475: Invalid value for argument %s: %s"));
 #endif
@@ -3280,7 +3280,7 @@ EXTERN char e_atom_engine_must_be_at_start_of_pattern[]
        INIT(= N_("E1281: Atom '\\%%#=%c' must be at the start of the pattern"));
 #ifdef FEAT_EVAL
 EXTERN char e_bitshift_ops_must_be_number[]
-       INIT(= N_("E1282: bitshift operands must be numbers"));
+       INIT(= N_("E1282: Bitshift operands must be numbers"));
 EXTERN char e_bitshift_ops_must_be_postive[]
-       INIT(= N_("E1283: bitshift amount must be a positive number"));
+       INIT(= N_("E1283: Bitshift amount must be a positive number"));
 #endif
index e3df115a6d46b504e2aaa3a5cb258e125bc5d662..0aaa968c88bf22f393831b5866584f972e1848b5 100644 (file)
@@ -3429,6 +3429,83 @@ func Test_autocmd_add()
         \ cmd: 'echo "bufadd"'}]
   call assert_fails('call autocmd_add(l)', 'E216:')
 
+  " Test for using a list of events and patterns
+  call autocmd_delete([#{group: 'TestAcSet'}])
+  let l = [#{group: 'TestAcSet', event: ['BufEnter', 'BufLeave'],
+        \ pattern: ['*.py', '*.sh'], cmd: 'echo "bufcmds"'}]
+  call autocmd_add(l)
+  call assert_equal([
+        \ #{cmd: 'echo "bufcmds"', group: 'TestAcSet', pattern: '*.py',
+        \   nested: v:false,  once: v:false, event: 'BufEnter'},
+        \ #{cmd: 'echo "bufcmds"', group: 'TestAcSet', pattern: '*.sh',
+        \   nested: v:false,  once: v:false, event: 'BufEnter'},
+        \ #{cmd: 'echo "bufcmds"', group: 'TestAcSet', pattern: '*.py',
+        \   nested: v:false,  once: v:false, event: 'BufLeave'},
+        \ #{cmd: 'echo "bufcmds"', group: 'TestAcSet', pattern: '*.sh',
+        \   nested: v:false,  once: v:false, event: 'BufLeave'}],
+        \   autocmd_get(#{group: 'TestAcSet'}))
+
+  " Test for invalid values for 'event' item
+  call autocmd_delete([#{group: 'TestAcSet'}])
+  let l = [#{group: 'TestAcSet', event: test_null_string(),
+        \ pattern: "*.py", cmd: 'echo "bufcmds"'}]
+  call assert_fails('call autocmd_add(l)', 'E928:')
+  let l = [#{group: 'TestAcSet', event: test_null_list(),
+        \ pattern: "*.py", cmd: 'echo "bufcmds"'}]
+  call assert_fails('call autocmd_add(l)', 'E714:')
+  let l = [#{group: 'TestAcSet', event: {},
+        \ pattern: "*.py", cmd: 'echo "bufcmds"'}]
+  call assert_fails('call autocmd_add(l)', 'E777:')
+  let l = [#{group: 'TestAcSet', event: [{}],
+        \ pattern: "*.py", cmd: 'echo "bufcmds"'}]
+  call assert_fails('call autocmd_add(l)', 'E928:')
+  let l = [#{group: 'TestAcSet', event: [test_null_string()],
+        \ pattern: "*.py", cmd: 'echo "bufcmds"'}]
+  call assert_fails('call autocmd_add(l)', 'E928:')
+  let l = [#{group: 'TestAcSet', event: 'BufEnter,BufLeave',
+        \ pattern: '*.py', cmd: 'echo "bufcmds"'}]
+  call assert_fails('call autocmd_add(l)', 'E216:')
+  let l = [#{group: 'TestAcSet', event: [],
+        \ pattern: "*.py", cmd: 'echo "bufcmds"'}]
+  call autocmd_add(l)
+  let l = [#{group: 'TestAcSet', event: [""],
+        \ pattern: "*.py", cmd: 'echo "bufcmds"'}]
+  call assert_fails('call autocmd_add(l)', 'E216:')
+  let l = [#{group: 'TestAcSet', event: "",
+        \ pattern: "*.py", cmd: 'echo "bufcmds"'}]
+  call autocmd_add(l)
+  call assert_equal([], autocmd_get(#{group: 'TestAcSet'}))
+
+  " Test for invalid values for 'pattern' item
+  let l = [#{group: 'TestAcSet', event: "BufEnter",
+        \ pattern: test_null_string(), cmd: 'echo "bufcmds"'}]
+  let l = [#{group: 'TestAcSet', event: "BufEnter",
+        \ pattern: test_null_list(), cmd: 'echo "bufcmds"'}]
+  call assert_fails('call autocmd_add(l)', 'E714:')
+  let l = [#{group: 'TestAcSet', event: "BufEnter",
+        \ pattern: {}, cmd: 'echo "bufcmds"'}]
+  call assert_fails('call autocmd_add(l)', 'E777:')
+  let l = [#{group: 'TestAcSet', event: "BufEnter",
+        \ pattern: [{}], cmd: 'echo "bufcmds"'}]
+  call assert_fails('call autocmd_add(l)', 'E928:')
+  let l = [#{group: 'TestAcSet', event: "BufEnter",
+        \ pattern: [test_null_string()], cmd: 'echo "bufcmds"'}]
+  call assert_fails('call autocmd_add(l)', 'E928:')
+  let l = [#{group: 'TestAcSet', event: "BufEnter",
+        \ pattern: [], cmd: 'echo "bufcmds"'}]
+  call autocmd_add(l)
+  let l = [#{group: 'TestAcSet', event: "BufEnter",
+        \ pattern: [""], cmd: 'echo "bufcmds"'}]
+  call autocmd_add(l)
+  let l = [#{group: 'TestAcSet', event: "BufEnter",
+        \ pattern: "", cmd: 'echo "bufcmds"'}]
+  call autocmd_add(l)
+  call assert_equal([], autocmd_get(#{group: 'TestAcSet'}))
+
+  let l = [#{group: 'TestAcSet', event: 'BufEnter,abc,BufLeave',
+        \ pattern: '*.py', cmd: 'echo "bufcmds"'}]
+  call assert_fails('call autocmd_add(l)', 'E216:')
+
   call assert_fails("call autocmd_add({})", 'E1211:')
   call assert_equal(v:false,  autocmd_add(test_null_list()))
   call assert_true(autocmd_add([[]]))
index cc9cb49b49e27f897ea9a0c688711de4f2734399..cc3542a314f0efa15655eb9fc7760403e42e196a 100644 (file)
@@ -734,6 +734,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    5030,
 /**/
     5029,
 /**/