Patch 8.2.4981
Problem:    It is not possible to manipulate autocommands.
Solution:   Add functions to add, get and set autocommands. (Yegappan
            Lakshmanan, closes #10291)
Files:      runtime/doc/autocmd.txt, runtime/doc/builtin.txt,
            runtime/doc/usr_41.txt, src/autocmd.c, src/evalfunc.c,
            src/proto/autocmd.pro, src/testdir/test_autocmd.vim,
            src/testdir/test_vim9_builtin.vim


*** ../vim-8.2.4980/runtime/doc/autocmd.txt     2022-04-09 11:37:08.196325535 
+0100
--- runtime/doc/autocmd.txt     2022-05-19 10:22:46.206265964 +0100
***************
*** 82,89 ****
                  /<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
  But this sees "augroup" as part of the defined command: >
        :augroup mine | au! BufRead * | augroup END
--- 82,92 ----
                  /<start
                }
  
+ The |autocmd_add()| function can be used to add a list of autocmds and autocmd
+ groups from a Vim script.
+ 
  Note: The ":autocmd" command can only be followed by another command when the
! '|' appears where the pattern is expected.  This works: >
        :augroup mine | au! BufRead | augroup END
  But this sees "augroup" as part of the defined command: >
        :augroup mine | au! BufRead * | augroup END
***************
*** 145,163 ****
  ==============================================================================
  3. Removing autocommands                              *autocmd-remove*
  
! :au[tocmd]! [group] {event} {pat} [++once] [++nested] {cmd}
                        Remove all autocommands associated with {event} and
!                       {pat}, and add the command {cmd}.
                        See |autocmd-once| for [++once].
                        See |autocmd-nested| for [++nested].
  
! :au[tocmd]! [group] {event} {pat}
                        Remove all autocommands associated with {event} and
!                       {pat}.
  
! :au[tocmd]! [group] * {pat}
!                       Remove all autocommands associated with {pat} for all
!                       events.
  
  :au[tocmd]! [group] {event}
                        Remove ALL autocommands for {event}.
--- 149,170 ----
  ==============================================================================
  3. Removing autocommands                              *autocmd-remove*
  
! In addition to the below described commands, the |autocmd_delete()| function 
can
! be used to remove a list of autocmds and autocmd groups from a Vim script.
! 
! :au[tocmd]! [group] {event} {aupat} [++once] [++nested] {cmd}
                        Remove all autocommands associated with {event} and
!                       {aupat}, and add the command {cmd}.
                        See |autocmd-once| for [++once].
                        See |autocmd-nested| for [++nested].
  
! :au[tocmd]! [group] {event} {aupat}
                        Remove all autocommands associated with {event} and
!                       {aupat}.
  
! :au[tocmd]! [group] * {aupat}
!                       Remove all autocommands associated with {aupat} for
!                       all events.
  
  :au[tocmd]! [group] {event}
                        Remove ALL autocommands for {event}.
***************
*** 197,202 ****
--- 204,212 ----
  In order to list buffer-local autocommands, use a pattern in the form <buffer>
  or <buffer=N>.  See |autocmd-buflocal|.
  
+ The |autocmd_get()| function can be used from a Vim script to get a list of
+ autocmds.
+ 
                                                        *:autocmd-verbose*
  When 'verbose' is non-zero, listing an autocommand will also display where it
  was last defined. Example: >
*** ../vim-8.2.4980/runtime/doc/builtin.txt     2022-05-11 14:15:32.339235975 
+0100
--- runtime/doc/builtin.txt     2022-05-19 10:22:46.206265964 +0100
***************
*** 60,65 ****
--- 60,68 ----
  assert_true({actual} [, {msg}])       Number  assert {actual} is true
  atan({expr})                  Float   arc tangent of {expr}
  atan2({expr1}, {expr2})               Float   arc tangent of {expr1} / {expr2}
+ autocmd_add({acmds})          Bool    add a list of autocmds and groups
+ autocmd_delete({acmds})               Bool    delete a list of autocmds and 
groups
+ autocmd_get([{opts}])         List    return a list of autocmds
  balloon_gettext()             String  current text in the balloon
  balloon_show({expr})          none    show {expr} inside the balloon
  balloon_split({msg})          List    split {msg} as used for a balloon
***************
*** 922,927 ****
--- 925,1069 ----
  <
                {only available when compiled with the |+float| feature}
  
+ 
+ autocmd_add({acmds})                                  *autocmd_add()*
+               Adds a List of autocmds and autocmd groups.
+ 
+               The {acmds} argument is a List where each item is a Dict with
+               the following optional items:
+                   bufnr       buffer number to add a buffer-local autocmd.
+                               If this item is specified, then the "pattern"
+                               item is ignored.
+                   cmd         Ex command to execute for this autocmd event
+                   event       autocmd event name. Refer to |autocmd-events|.
+                   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
+                               default group is used.
+                   nested      set to v:true to add a nested autocmd.
+                               Refer to |autocmd-nested|.
+                   once        set to v:true to add a autocmd which executes
+                               only once. Refer to |autocmd-once|.
+                   pattern     autocmd pattern string. Refer to
+                               |autocmd-patterns|.  If "bufnr" item is
+                               present, then this item is ignored.
+ 
+               Returns v:true on success and v:false on failure.
+               Examples: >
+                       " Create a buffer-local autocmd for buffer 5
+                       let acmd = {}
+                       let acmd.group = 'MyGroup'
+                       let acmd.event = 'BufEnter'
+                       let acmd.bufnr = 5
+                       let acmd.cmd = 'call BufEnterFunc()'
+                       call autocmd_add([acmd])
+ 
+               Can also be used as a |method|: >
+                       GetAutocmdList()->autocmd_add()
+ <
+ autocmd_delete({acmds})                                       
*autocmd_delete()*
+               Deletes a List of autocmds and autocmd groups.
+ 
+               The {acmds} argument is a List where each item is a Dict with
+               the following optional items:
+                   bufnr       buffer number to delete a buffer-local autocmd.
+                               If this item is specified, then the "pattern"
+                               item is ignored.
+                   cmd         Ex command for this autocmd event
+                   event       autocmd event name. Refer to |autocmd-events|.
+                               If '*' then all the autocmd events in this
+                               group are deleted.
+                   group       autocmd group name. Refer to |autocmd-groups|.
+                               If not specified or empty, then the default
+                               group is used.
+                   nested      set to v:true for a nested autocmd.
+                               Refer to |autocmd-nested|.
+                   once        set to v:true for an autocmd which executes
+                               only once. Refer to |autocmd-once|.
+                   pattern     autocmd pattern string. Refer to
+                               |autocmd-patterns|.  If "bufnr" item is
+                               present, then this item is ignored.
+ 
+               If only {group} is specified in a {acmds} entry and {event},
+               {pattern} and {cmd} are not specified, then that autocmd group
+               is deleted.
+ 
+               Returns v:true on success and v:false on failure.
+               Examples: >
+                       " :autocmd! BufLeave *.vim
+                       let acmd = #{event: 'BufLeave', pattern: '*.vim'}
+                       call autocmd_delete([acmd]})
+                       " :autocmd! MyGroup1 BufLeave
+                       let acmd = #{group: 'MyGroup1', event: 'BufLeave'}
+                       call autocmd_delete([acmd])
+                       " :autocmd! MyGroup2 BufEnter *.c
+                       let acmd = #{group: 'MyGroup2', event: 'BufEnter',
+                                                       \ pattern: '*.c'}
+                       " :autocmd! MyGroup2 * *.c
+                       let acmd = #{group: 'MyGroup2', event: '*',
+                                                       \ pattern: '*.c'}
+                       call autocmd_delete([acmd])
+                       " :autocmd! MyGroup3
+                       let acmd = #{group: 'MyGroup3'}
+                       call autocmd_delete([acmd])
+ <
+               Can also be used as a |method|: >
+                       GetAutocmdList()->autocmd_delete()
+ 
+ autocmd_get([{opts}])                                 *autocmd_get()*
+               Returns a |List| of autocmds. If {opts} is not supplied, then
+               returns the autocmds for all the events in all the groups.
+ 
+               The optional {opts} Dict argument supports the following
+               items:
+                   group       Autocmd group name. If specified, returns only
+                               the autocmds defined in this group. If the
+                               specified group doesn't exist, results in an
+                               error message.  If set to an empty string,
+                               then the default autocmd group is used.
+                   event       Autocmd event name. If specified, returns only
+                               the autocmds defined for this event.  If set
+                               to "*", then returns autocmds for all the
+                               events.  If the specified event doesn't exist,
+                               results in an error message.
+                   pattern     Autocmd pattern. If specified, returns only
+                               the autocmds defined for this pattern.
+               A combination of the above three times can be supplied in
+               {opts}.
+ 
+               Each Dict in the returned List contains the following items:
+                   bufnr       For buffer-local autocmds, buffer number where
+                               the autocmd is defined.
+                   cmd         Command executed for this autocmd.
+                   event       Autocmd event name.
+                   group       Autocmd group name.
+                   nested      Set to v:true for a nested autocmd. See
+                               |autocmd-nested|.
+                   once        Set to v:true, if the autocmd will be executed
+                               only once. See |autocmd-once|.
+                   pattern     Autocmd pattern.  For a buffer-local
+                               autocmd, this will be of the form "<buffer=n>".
+               If there are multiple commands for an autocmd event in a
+               group, then separate items are returned for each command.
+ 
+               Examples: >
+                       " :autocmd MyGroup
+                       echo autocmd_get(#{group: 'Mygroup'})
+                       " :autocmd G BufUnload
+                       echo autocmd_get(#{group: 'G', event: 'BufUnload'})
+                       " :autocmd G * *.ts
+                       let acmd = #{group: 'G', event: '*', pattern: '*.ts'}
+                       echo autocmd_get(acmd)
+                       " :autocmd Syntax
+                       echo autocmd_get(#{event: 'Syntax'})
+                       " :autocmd G BufEnter *.ts
+                       let acmd = #{group: 'G', event: 'BufEnter',
+                                                       \ pattern: '*.ts'}
+                       echo autocmd_get(acmd)
+ <
+               Can also be used as a |method|: >
+                       Getopts()->autocmd_get()
+ <
  balloon_gettext()                                     *balloon_gettext()*
                Return the current text in the balloon.  Only for the string,
                not used for the List.
*** ../vim-8.2.4980/runtime/doc/usr_41.txt      2022-05-07 12:48:24.070194799 
+0100
--- runtime/doc/usr_41.txt      2022-05-19 10:22:46.206265964 +0100
***************
*** 844,849 ****
--- 925,935 ----
        reltimestr()            convert reltime() result to a string
        reltimefloat()          convert reltime() result to a Float
  
+ Autocmds:                                     *autocmd-functions*
+       autocmd_add()           add a list of autocmds and groups
+       autocmd_delete()        delete a list of autocmds and groups
+       autocmd_get()           return a list of autocmds
+ 
                        *buffer-functions* *window-functions* *arg-functions*
  Buffers, windows and the argument list:
        argc()                  number of entries in the argument list
*** ../vim-8.2.4980/src/autocmd.c       2022-05-07 20:01:10.050731702 +0100
--- src/autocmd.c       2022-05-19 10:27:53.505876019 +0100
***************
*** 2562,2568 ****
  {
      if (idx == augroups.ga_len)               // add "END" add the end
        return (char_u *)"END";
!     if (idx >= augroups.ga_len)               // end of list
        return NULL;
      if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == 
get_deleted_augroup())
        // skip deleted entries
--- 2562,2568 ----
  {
      if (idx == augroups.ga_len)               // add "END" add the end
        return (char_u *)"END";
!     if (idx < 0 || idx >= augroups.ga_len)    // end of list
        return NULL;
      if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == 
get_deleted_augroup())
        // skip deleted entries
***************
*** 2747,2750 ****
--- 2747,3101 ----
      vim_free(arg_save);
      return retval;
  }
+ 
+ /*
+  * autocmd_add() and autocmd_delete() functions
+  */
+     static void
+ autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
+ {
+     list_T    *event_list;
+     listitem_T        *li;
+     dict_T    *event_dict;
+     char_u    *event_name = NULL;
+     event_T   event;
+     char_u    *group_name = NULL;
+     int               group;
+     char_u    *pat = NULL;
+     char_u    *cmd = NULL;
+     char_u    *end;
+     int               once;
+     int               nested;
+     int               retval = VVAL_TRUE;
+     int               save_augroup = current_augroup;
+ 
+     rettv->v_type = VAR_BOOL;
+     rettv->vval.v_number = VVAL_FALSE;
+ 
+     if (check_for_list_arg(argvars, 0) == FAIL)
+       return;
+ 
+     event_list = argvars[0].vval.v_list;
+     if (event_list == NULL)
+       return;
+ 
+     FOR_ALL_LIST_ITEMS(event_list, li)
+     {
+       VIM_CLEAR(event_name);
+       VIM_CLEAR(group_name);
+       VIM_CLEAR(pat);
+       VIM_CLEAR(cmd);
+ 
+       if (li->li_tv.v_type != VAR_DICT)
+           continue;
+ 
+       event_dict = li->li_tv.vval.v_dict;
+       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
+       {
+           if (delete && event_name[0] == '*' && event_name[1] == NUL)
+               // if the event name is '*', delete all the events
+               event = NUM_EVENTS;
+           else
+           {
+               event = event_name2nr(event_name, &end);
+               if (event == NUM_EVENTS)
+               {
+                   semsg(_(e_no_such_event_str), event_name);
+                   retval = VVAL_FALSE;
+                   break;
+               }
+           }
+       }
+ 
+       group_name = dict_get_string(event_dict, (char_u *)"group", TRUE);
+       if (group_name == NULL || *group_name == NUL)
+           // if the autocmd group name is not specified, then use the current
+           // autocmd group
+           group = current_augroup;
+       else
+       {
+           group = au_find_group(group_name);
+           if (group == AUGROUP_ERROR)
+           {
+               if (delete)
+               {
+                   semsg(_(e_no_such_group_str), group_name);
+                   retval = VVAL_FALSE;
+                   break;
+               }
+               // group is not found, create it now
+               group = au_new_group(group_name);
+               if (group == AUGROUP_ERROR)
+               {
+                   semsg(_(e_no_such_group_str), group_name);
+                   retval = VVAL_FALSE;
+                   break;
+               }
+ 
+               current_augroup = group;
+           }
+       }
+ 
+       // if a buffer number is specified, then generate a pattern of the form
+       // "<buffer=n>. Otherwise, use the pattern supplied by the user.
+       if (dict_has_key(event_dict, "bufnr"))
+       {
+           varnumber_T bnum;
+ 
+           bnum = dict_get_number_def(event_dict, (char_u *)"bufnr", -1);
+           if (bnum == -1)
+               continue;
+ 
+           pat = alloc(128 + 1);
+           if (pat == NULL)
+               continue;
+           vim_snprintf((char *)pat, 128, "<buffer=%d>", (int)bnum);
+       }
+       else
+       {
+           pat = dict_get_string(event_dict, (char_u *)"pattern", TRUE);
+           if (pat == NULL)
+           {
+               if (delete)
+                   pat = vim_strsave((char_u *)"");
+               else
+                   continue;
+           }
+       }
+ 
+       once = dict_get_bool(event_dict, (char_u *)"once", FALSE);
+       nested = dict_get_bool(event_dict, (char_u *)"nested", FALSE);
+ 
+       cmd = dict_get_string(event_dict, (char_u *)"cmd", TRUE);
+       if (cmd == NULL)
+       {
+           if (delete)
+               cmd = vim_strsave((char_u *)"");
+           else
+               continue;
+       }
+ 
+       if (event == NUM_EVENTS)
+       {
+           // event is '*', apply for all the events
+           for (event = (event_T)0; (int)event < NUM_EVENTS;
+                   event = (event_T)((int)event + 1))
+           {
+               if (do_autocmd_event(event, pat, once, nested, cmd, delete,
+                                                       group, 0) == FAIL)
+               {
+                   retval = VVAL_FALSE;
+                   break;
+               }
+           }
+       }
+       else
+       {
+           if (do_autocmd_event(event, pat, once, nested, cmd, delete, group,
+                                                                   0) == FAIL)
+           {
+               retval = VVAL_FALSE;
+               break;
+           }
+       }
+ 
+       // if only the autocmd group name is specified for delete and the
+       // autocmd event, pattern and cmd are not specified, then delete the
+       // autocmd group.
+       if (delete && group_name != NULL &&
+               (event_name == NULL || event_name[0] == NUL)
+               && (pat == NULL || pat[0] == NUL)
+               && (cmd == NULL || cmd[0] == NUL))
+           au_del_group(group_name);
+     }
+ 
+     VIM_CLEAR(event_name);
+     VIM_CLEAR(group_name);
+     VIM_CLEAR(pat);
+     VIM_CLEAR(cmd);
+ 
+     current_augroup = save_augroup;
+     rettv->vval.v_number = retval;
+ }
+ 
+ /*
+  * autocmd_add() function
+  */
+     void
+ f_autocmd_add(typval_T *argvars, typval_T *rettv)
+ {
+     autocmd_add_or_delete(argvars, rettv, FALSE);
+ }
+ 
+ /*
+  * autocmd_delete() function
+  */
+     void
+ f_autocmd_delete(typval_T *argvars, typval_T *rettv)
+ {
+     autocmd_add_or_delete(argvars, rettv, TRUE);
+ }
+ 
+ /*
+  * autocmd_get() function
+  * Returns a List of autocmds.
+  */
+     void
+ f_autocmd_get(typval_T *argvars, typval_T *rettv)
+ {
+     event_T   event_arg = NUM_EVENTS;
+     event_T   event;
+     AutoPat   *ap;
+     AutoCmd   *ac;
+     list_T    *event_list;
+     dict_T    *event_dict;
+     char_u    *event_name = NULL;
+     char_u    *pat = NULL;
+     char_u    *name = NULL;
+     int               group = AUGROUP_ALL;
+ 
+     if (check_for_opt_dict_arg(argvars, 0) == FAIL)
+       return;
+ 
+     if (argvars[0].v_type == VAR_DICT)
+     {
+       // return only the autocmds in the specified group
+       if (dict_has_key(argvars[0].vval.v_dict, "group"))
+       {
+           name = dict_get_string(argvars[0].vval.v_dict,
+                                                     (char_u *)"group", TRUE);
+           if (name == NULL)
+               return;
+ 
+           if (*name == NUL)
+               group = AUGROUP_DEFAULT;
+           else
+           {
+               group = au_find_group(name);
+               if (group == AUGROUP_ERROR)
+               {
+                   semsg(_(e_no_such_group_str), name);
+                   vim_free(name);
+                   return;
+               }
+           }
+           vim_free(name);
+       }
+ 
+       // return only the autocmds for the specified event
+       if (dict_has_key(argvars[0].vval.v_dict, "event"))
+       {
+           int         i;
+ 
+           name = dict_get_string(argvars[0].vval.v_dict,
+                                                     (char_u *)"event", TRUE);
+           if (name == NULL)
+               return;
+ 
+           if (name[0] == '*' && name[1] == NUL)
+               event_arg = NUM_EVENTS;
+           else
+           {
+               for (i = 0; event_names[i].name != NULL; i++)
+                   if (STRICMP(event_names[i].name, name) == 0)
+                       break;
+               if (event_names[i].name == NULL)
+               {
+                   semsg(_(e_no_such_event_str), name);
+                   vim_free(name);
+                   return;
+               }
+               event_arg = event_names[i].event;
+           }
+           vim_free(name);
+       }
+ 
+       // return only the autocmds for the specified pattern
+       if (dict_has_key(argvars[0].vval.v_dict, "pattern"))
+       {
+           pat = dict_get_string(argvars[0].vval.v_dict,
+                                                   (char_u *)"pattern", TRUE);
+           if (pat == NULL)
+               return;
+       }
+     }
+ 
+     if (rettv_list_alloc(rettv) == FAIL)
+       return;
+     event_list = rettv->vval.v_list;
+ 
+     // iterate through all the autocmd events
+     for (event = (event_T)0; (int)event < NUM_EVENTS;
+           event = (event_T)((int)event + 1))
+     {
+       if (event_arg != NUM_EVENTS && event != event_arg)
+           continue;
+ 
+       event_name = event_nr2name(event);
+ 
+       // iterate through all the patterns for this autocmd event
+       FOR_ALL_AUTOCMD_PATTERNS(event, ap)
+       {
+           char_u      *group_name;
+ 
+           if (group != AUGROUP_ALL && group != ap->group)
+               continue;
+ 
+           if (pat != NULL && STRCMP(pat, ap->pat) != 0)
+               continue;
+ 
+           group_name = get_augroup_name(NULL, ap->group);
+ 
+           // iterate through all the commands for this pattern and add one
+           // item for each cmd.
+           for (ac = ap->cmds; ac != NULL; ac = ac->next)
+           {
+               event_dict = dict_alloc();
+               if (event_dict == NULL)
+                   return;
+ 
+               if (list_append_dict(event_list, event_dict) == FAIL)
+                   return;
+ 
+               if (dict_add_string(event_dict, "event", event_name) == FAIL)
+                   return;
+ 
+               if (dict_add_string(event_dict, "group", group_name == NULL
+                           ? (char_u *)"" : group_name) == FAIL)
+                   return;
+ 
+               if (ap->buflocal_nr != 0)
+                   if (dict_add_number(event_dict, "bufnr", ap->buflocal_nr)
+                                                                      == FAIL)
+                       return;
+ 
+               if (dict_add_string(event_dict, "pattern", ap->pat) == FAIL)
+                   return;
+ 
+               if (dict_add_string(event_dict, "cmd", ac->cmd) == FAIL)
+                   return;
+ 
+               if (dict_add_bool(event_dict, "once", ac->once) == FAIL)
+                   return;
+               if (dict_add_bool(event_dict, "nested", ac->nested) == FAIL)
+                   return;
+           }
+       }
+     }
+ 
+     vim_free(pat);
+ }
+ 
  #endif
*** ../vim-8.2.4980/src/evalfunc.c      2022-05-15 13:59:08.700167480 +0100
--- src/evalfunc.c      2022-05-19 10:22:46.206265964 +0100
***************
*** 1587,1592 ****
--- 1587,1598 ----
                        ret_float,          FLOAT_FUNC(f_atan)},
      {"atan2",         2, 2, FEARG_1,      arg2_float_or_nr,
                        ret_float,          FLOAT_FUNC(f_atan2)},
+     {"autocmd_add",   1, 1, FEARG_1,      arg1_list_any,
+                       ret_number_bool,    f_autocmd_add},
+     {"autocmd_delete",        1, 1, FEARG_1,      arg1_list_any,
+                       ret_number_bool,    f_autocmd_delete},
+     {"autocmd_get",   0, 1, FEARG_1,      arg1_dict_any,
+                       ret_list_dict_any,  f_autocmd_get},
      {"balloon_gettext",       0, 0, 0,            NULL,
                        ret_string,
  #ifdef FEAT_BEVAL
*** ../vim-8.2.4980/src/proto/autocmd.pro       2022-04-14 15:39:39.281754582 
+0100
--- src/proto/autocmd.pro       2022-05-19 10:22:46.210265958 +0100
***************
*** 38,41 ****
--- 38,44 ----
  char_u *get_event_name(expand_T *xp, int idx);
  int autocmd_supported(char_u *name);
  int au_exists(char_u *arg);
+ void f_autocmd_add(typval_T *argvars, typval_T *rettv);
+ void f_autocmd_delete(typval_T *argvars, typval_T *rettv);
+ void f_autocmd_get(typval_T *argvars, typval_T *rettv);
  /* vim: set ft=c : */
*** ../vim-8.2.4980/src/testdir/test_autocmd.vim        2022-04-21 
22:52:07.062317208 +0100
--- src/testdir/test_autocmd.vim        2022-05-19 10:22:46.210265958 +0100
***************
*** 3210,3213 ****
--- 3210,3483 ----
    augroup! test_noname_autocmd_group
  endfunc
  
+ " Test for the autocmd_get() function
+ func Test_autocmd_get()
+   augroup TestAutoCmdFns
+     au!
+     autocmd BufAdd *.vim echo "bufadd-vim"
+     autocmd BufAdd *.py echo "bufadd-py"
+     autocmd BufHidden *.vim echo "bufhidden"
+   augroup END
+   augroup TestAutoCmdFns2
+     autocmd BufAdd *.vim echo "bufadd-vim-2"
+     autocmd BufRead *.a1b2c3 echo "bufadd-vim-2"
+   augroup END
+ 
+   let l = autocmd_get()
+   call assert_true(l->len() > 0)
+ 
+   " Test for getting all the autocmds in a group
+   let expected = [
+         \ #{cmd: 'echo "bufadd-vim"', group: 'TestAutoCmdFns',
+         \  pattern: '*.vim', nested: v:false, once: v:false,
+         \  event: 'BufAdd'},
+         \ #{cmd: 'echo "bufadd-py"', group: 'TestAutoCmdFns',
+         \  pattern: '*.py', nested: v:false, once: v:false,
+         \  event: 'BufAdd'},
+         \ #{cmd: 'echo "bufhidden"', group: 'TestAutoCmdFns',
+         \  pattern: '*.vim', nested: v:false,
+         \  once: v:false, event: 'BufHidden'}]
+   call assert_equal(expected, autocmd_get(#{group: 'TestAutoCmdFns'}))
+ 
+   " Test for getting autocmds for all the patterns in a group
+   call assert_equal(expected, autocmd_get(#{group: 'TestAutoCmdFns',
+         \ event: '*'}))
+ 
+   " Test for getting autocmds for an event in a group
+   let expected = [
+         \ #{cmd: 'echo "bufadd-vim"', group: 'TestAutoCmdFns',
+         \  pattern: '*.vim', nested: v:false, once: v:false,
+         \  event: 'BufAdd'},
+         \ #{cmd: 'echo "bufadd-py"', group: 'TestAutoCmdFns',
+         \  pattern: '*.py', nested: v:false, once: v:false,
+         \  event: 'BufAdd'}]
+   call assert_equal(expected, autocmd_get(#{group: 'TestAutoCmdFns',
+         \ event: 'BufAdd'}))
+ 
+   " Test for getting the autocmds for all the events in a group for particular
+   " pattern
+   call assert_equal([{'cmd': 'echo "bufadd-py"', 'group': 'TestAutoCmdFns',
+         \ 'pattern': '*.py', 'nested': v:false, 'once': v:false,
+         \ 'event': 'BufAdd'}],
+         \ autocmd_get(#{group: 'TestAutoCmdFns', event: '*', pattern: 
'*.py'}))
+ 
+   " Test for getting the autocmds for an events in a group for particular
+   " pattern
+   let l = autocmd_get(#{group: 'TestAutoCmdFns', event: 'BufAdd',
+         \ pattern: '*.vim'})
+   call assert_equal([
+         \ #{cmd: 'echo "bufadd-vim"', group: 'TestAutoCmdFns',
+         \  pattern: '*.vim', nested: v:false, once: v:false,
+         \  event: 'BufAdd'}], l)
+ 
+   " Test for getting the autocmds for a pattern in a group
+   let l = autocmd_get(#{group: 'TestAutoCmdFns', pattern: '*.vim'})
+   call assert_equal([
+         \ #{cmd: 'echo "bufadd-vim"', group: 'TestAutoCmdFns',
+         \  pattern: '*.vim', nested: v:false, once: v:false,
+         \  event: 'BufAdd'},
+         \ #{cmd: 'echo "bufhidden"', group: 'TestAutoCmdFns',
+         \  pattern: '*.vim', nested: v:false,
+         \  once: v:false, event: 'BufHidden'}], l)
+ 
+   " Test for getting the autocmds for a pattern in all the groups
+   let l = autocmd_get(#{pattern: '*.a1b2c3'})
+   call assert_equal([{'cmd': 'echo "bufadd-vim-2"', 'group': 
'TestAutoCmdFns2',
+         \ 'pattern': '*.a1b2c3', 'nested': v:false, 'once': v:false,
+         \ 'event': 'BufRead'}], l)
+ 
+   " Test for getting autocmds for a pattern without any autocmds
+   call assert_equal([], autocmd_get(#{group: 'TestAutoCmdFns',
+         \ pattern: '*.abc'}))
+   call assert_equal([], autocmd_get(#{group: 'TestAutoCmdFns',
+         \ event: 'BufAdd', pattern: '*.abc'}))
+   call assert_equal([], autocmd_get(#{group: 'TestAutoCmdFns',
+         \ event: 'BufWipeout'}))
+   call assert_fails("call autocmd_get(#{group: 'abc', event: 'BufAdd'})",
+         \ 'E367:')
+   let cmd = "echo autocmd_get(#{group: 'TestAutoCmdFns', event: 'abc'})"
+   call assert_fails(cmd, 'E216:')
+   call assert_fails("call autocmd_get(#{group: 'abc'})", 'E367:')
+   call assert_fails("echo autocmd_get(#{event: 'abc'})", 'E216:')
+ 
+   augroup TestAutoCmdFns
+     au!
+   augroup END
+   call assert_equal([], autocmd_get(#{group: 'TestAutoCmdFns'}))
+ 
+   " Test for nested and once autocmds
+   augroup TestAutoCmdFns
+     au!
+     autocmd VimSuspend * ++nested echo "suspend"
+     autocmd VimResume * ++once echo "resume"
+   augroup END
+ 
+   let expected = [
+         \ {'cmd': 'echo "suspend"', 'group': 'TestAutoCmdFns', 'pattern': '*',
+         \ 'nested': v:true, 'once': v:false, 'event': 'VimSuspend'},
+         \ {'cmd': 'echo "resume"', 'group': 'TestAutoCmdFns', 'pattern': '*',
+         \  'nested': v:false, 'once': v:true, 'event': 'VimResume'}]
+   call assert_equal(expected, autocmd_get(#{group: 'TestAutoCmdFns'}))
+ 
+   " Test for buffer-local autocmd
+   augroup TestAutoCmdFns
+     au!
+     autocmd TextYankPost <buffer> echo "textyankpost"
+   augroup END
+ 
+   let expected = [
+         \ {'cmd': 'echo "textyankpost"', 'group': 'TestAutoCmdFns',
+         \  'pattern': '<buffer=' .. bufnr() .. '>', 'nested': v:false,
+         \  'once': v:false, 'bufnr': bufnr(), 'event': 'TextYankPost'}]
+   call assert_equal(expected, autocmd_get(#{group: 'TestAutoCmdFns'}))
+ 
+   augroup TestAutoCmdFns
+     au!
+   augroup END
+   augroup! TestAutoCmdFns
+   augroup TestAutoCmdFns2
+     au!
+   augroup END
+   augroup! TestAutoCmdFns2
+ 
+   call assert_fails("echo autocmd_get(#{group: []})", 'E730:')
+   call assert_fails("echo autocmd_get(#{event: {}})", 'E731:')
+   call assert_fails("echo autocmd_get([])", 'E1206:')
+ endfunc
+ 
+ " Test for the autocmd_add() function
+ func Test_autocmd_add()
+   " Define a single autocmd in a group
+   call autocmd_add([#{group: 'TestAcSet', event: 'BufAdd', pattern: '*.sh',
+         \ cmd: 'echo "bufadd"', once: v:true, nested: v:true}])
+   call assert_equal([#{cmd: 'echo "bufadd"', group: 'TestAcSet',
+         \ pattern: '*.sh', nested: v:true, once: v:true,
+         \ event: 'BufAdd'}], autocmd_get(#{group: 'TestAcSet'}))
+ 
+   " Define two autocmds in the same group
+   call autocmd_delete([#{group: 'TestAcSet'}])
+   call autocmd_add([#{group: 'TestAcSet', event: 'BufAdd', pattern: '*.sh',
+         \ cmd: 'echo "bufadd"'},
+         \ #{group: 'TestAcSet', event: 'BufEnter', pattern: '*.sh',
+         \   cmd: 'echo "bufenter"'}])
+   call assert_equal([
+         \ #{cmd: 'echo "bufadd"', group: 'TestAcSet', pattern: '*.sh',
+         \   nested: v:false, once: v:false, event: 'BufAdd'},
+         \ #{cmd: 'echo "bufenter"', group: 'TestAcSet', pattern: '*.sh',
+         \   nested: v:false, once: v:false, event: 'BufEnter'}],
+         \   autocmd_get(#{group: 'TestAcSet'}))
+ 
+   " Define a buffer-local autocmd
+   call autocmd_delete([#{group: 'TestAcSet'}])
+   call autocmd_add([#{group: 'TestAcSet', event: 'CursorHold',
+         \ bufnr: bufnr(), cmd: 'echo "cursorhold"'}])
+   call assert_equal([
+         \ #{cmd: 'echo "cursorhold"', group: 'TestAcSet',
+         \   pattern: '<buffer=' .. bufnr() .. '>', nested: v:false,
+         \   once: v:false, bufnr: bufnr(), event: 'CursorHold'}],
+         \   autocmd_get(#{group: 'TestAcSet'}))
+ 
+   " Use an invalid buffer number
+   call autocmd_delete([#{group: 'TestAcSet'}])
+   call autocmd_add([#{group: 'TestAcSet', event: 'BufEnter',
+         \ bufnr: -1, cmd: 'echo "bufenter"'}])
+   let l = [#{group: 'TestAcSet', event: 'BufAdd', bufnr: 9999,
+         \ cmd: 'echo "bufadd"'}]
+   call assert_fails("echo autocmd_add(l)", 'E680:')
+   let l = [#{group: 'TestAcSet', event: 'BufRead', bufnr: [],
+         \ cmd: 'echo "bufread"'}]
+   call assert_fails("echo autocmd_add(l)", 'E745:')
+   call assert_equal([], autocmd_get(#{group: 'TestAcSet'}))
+ 
+   " Add two commands to the same group, event and pattern
+   call autocmd_delete([#{group: 'TestAcSet'}])
+   call autocmd_add([#{group: 'TestAcSet', event: 'BufUnload',
+         \ pattern: 'abc', cmd: 'echo "cmd1"'}])
+   call autocmd_add([#{group: 'TestAcSet', event: 'BufUnload',
+         \ pattern: 'abc', cmd: 'echo "cmd2"'}])
+   call assert_equal([
+         \ #{cmd: 'echo "cmd1"', group: 'TestAcSet', pattern: 'abc',
+         \   nested: v:false,  once: v:false, event: 'BufUnload'},
+         \ #{cmd: 'echo "cmd2"', group: 'TestAcSet', pattern: 'abc',
+         \   nested: v:false,  once: v:false, event: 'BufUnload'}],
+         \   autocmd_get(#{group: 'TestAcSet'}))
+ 
+   " When adding a new autocmd, if the autocmd 'group' is not specified, then
+   " the current autocmd group should be used.
+   call autocmd_delete([#{group: 'TestAcSet'}])
+   augroup TestAcSet
+     call autocmd_add([#{event: 'BufHidden', pattern: 'abc', cmd: 'echo 
"abc"'}])
+   augroup END
+   call assert_equal([
+         \ #{cmd: 'echo "abc"', group: 'TestAcSet', pattern: 'abc',
+         \   nested: v:false,  once: v:false, event: 'BufHidden'}],
+         \   autocmd_get(#{group: 'TestAcSet'}))
+ 
+   let l = [#{group: 'TestAcSet', event: 'abc', pattern: '*.sh',
+         \ cmd: 'echo "bufadd"'}]
+   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([[]]))
+   call assert_true(autocmd_add([test_null_dict()]))
+ 
+   augroup TestAcSet
+     au!
+   augroup END
+ 
+   call autocmd_add([#{group: 'TestAcSet'}])
+   call autocmd_add([#{group: 'TestAcSet', event: 'BufAdd'}])
+   call autocmd_add([#{group: 'TestAcSet', pat: '*.sh'}])
+   call autocmd_add([#{group: 'TestAcSet', cmd: 'echo "a"'}])
+   call autocmd_add([#{group: 'TestAcSet', event: 'BufAdd', pat: '*.sh'}])
+   call autocmd_add([#{group: 'TestAcSet', event: 'BufAdd', cmd: 'echo "a"'}])
+   call autocmd_add([#{group: 'TestAcSet', pat: '*.sh', cmd: 'echo "a"'}])
+   call assert_equal([], autocmd_get(#{group: 'TestAcSet'}))
+ 
+   augroup! TestAcSet
+ endfunc
+ 
+ " Test for deleting autocmd events and groups
+ func Test_autocmd_delete()
+   " Delete an event in an autocmd group
+   augroup TestAcSet
+     au!
+     au BufAdd *.sh echo "bufadd"
+     au BufEnter *.sh echo "bufenter"
+   augroup END
+   call autocmd_delete([#{group: 'TestAcSet', event: 'BufAdd'}])
+   call assert_equal([#{cmd: 'echo "bufenter"', group: 'TestAcSet',
+         \ pattern: '*.sh', nested: v:false, once: v:false,
+         \ event: 'BufEnter'}], autocmd_get(#{group: 'TestAcSet'}))
+ 
+   " Delete all the events in an autocmd group
+   augroup TestAcSet
+     au BufAdd *.sh echo "bufadd"
+   augroup END
+   call autocmd_delete([#{group: 'TestAcSet', event: '*'}])
+   call assert_equal([], autocmd_get(#{group: 'TestAcSet'}))
+ 
+   " Delete a non-existing autocmd group
+   call assert_fails("call autocmd_delete([#{group: 'abc'}])", 'E367:')
+   " Delete a non-existing autocmd event
+   let l = [#{group: 'TestAcSet', event: 'abc'}]
+   call assert_fails("call autocmd_delete(l)", 'E216:')
+   " Delete a non-existing autocmd pattern
+   let l = [#{group: 'TestAcSet', event: 'BufAdd', pat: 'abc'}]
+   call assert_true(autocmd_delete(l))
+ 
+   " Delete an autocmd group
+   augroup TestAcSet
+     au!
+     au BufAdd *.sh echo "bufadd"
+     au BufEnter *.sh echo "bufenter"
+   augroup END
+   call autocmd_delete([#{group: 'TestAcSet'}])
+   call assert_fails("call autocmd_get(#{group: 'TestAcSet'})", 'E367:')
+ 
+   call assert_true(autocmd_delete([[]]))
+   call assert_true(autocmd_delete([test_null_dict()]))
+ endfunc
+ 
  " vim: shiftwidth=2 sts=2 expandtab
*** ../vim-8.2.4980/src/testdir/test_vim9_builtin.vim   2022-05-12 
22:02:53.287681016 +0100
--- src/testdir/test_vim9_builtin.vim   2022-05-19 10:22:46.210265958 +0100
***************
*** 304,309 ****
--- 304,321 ----
    v9.CheckDefAndScriptFailure(['assert_report([1, 2])'], ['E1013: Argument 1: 
type mismatch, expected string but got list<number>', 'E1174: String required 
for argument 1'])
  enddef
  
+ def Test_autocmd_add()
+   v9.CheckDefAndScriptFailure(['autocmd_add({})'], ['E1013: Argument 1: type 
mismatch, expected list<any> but got dict<unknown>', 'E1211: List required for 
argument 1'])
+ enddef
+ 
+ def Test_autocmd_delete()
+   v9.CheckDefAndScriptFailure(['autocmd_delete({})'], ['E1013: Argument 1: 
type mismatch, expected list<any> but got dict<unknown>', 'E1211: List required 
for argument 1'])
+ enddef
+ 
+ def Test_autocmd_get()
+   v9.CheckDefAndScriptFailure(['autocmd_get(10)'], ['E1013: Argument 1: type 
mismatch, expected dict<any> but got number', 'E1206: Dictionary required for 
argument 1'])
+ enddef
+ 
  def Test_balloon_show()
    CheckGui
    CheckFeature balloon_eval
*** ../vim-8.2.4980/src/version.c       2022-05-18 22:07:44.079562195 +0100
--- src/version.c       2022-05-19 10:26:11.686005228 +0100
***************
*** 748,749 ****
--- 748,751 ----
  {   /* Add new patch number below this line */
+ /**/
+     4981,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
233. You start dreaming about web pages...in html.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20220519093229.5DAAE1C0AE8%40moolenaar.net.

Raspunde prin e-mail lui