To: vim_dev@googlegroups.com Subject: Patch 8.2.4981 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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 **** / :augroup mine | au! BufRead | augroup END But this sees "augroup" as part of the defined command: > :augroup mine | au! BufRead * | augroup END --- 82,92 ---- / :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 or . 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 "". + 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 + // ". 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, "", (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 echo "textyankpost" + augroup END + + let expected = [ + \ {'cmd': 'echo "textyankpost"', 'group': 'TestAutoCmdFns', + \ 'pattern': '', '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: '', 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', 'E1174: String required for argument 1']) enddef + def Test_autocmd_add() + v9.CheckDefAndScriptFailure(['autocmd_add({})'], ['E1013: Argument 1: type mismatch, expected list but got dict', 'E1211: List required for argument 1']) + enddef + + def Test_autocmd_delete() + v9.CheckDefAndScriptFailure(['autocmd_delete({})'], ['E1013: Argument 1: type mismatch, expected list but got dict', 'E1211: List required for argument 1']) + enddef + + def Test_autocmd_get() + v9.CheckDefAndScriptFailure(['autocmd_get(10)'], ['E1013: Argument 1: type mismatch, expected dict 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 -- Bram@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 ///