Skip to content

Commit e0ff3a7

Browse files
yegappanbrammool
authored andcommitted
patch 8.2.5030: autocmd_add() can only handle one event and pattern
Problem: autocmd_add() can only handle one event and pattern. Solution: Support a list of events and patterns. (Yegappan Lakshmanan, closes #10483)
1 parent cfe4565 commit e0ff3a7

File tree

5 files changed

+221
-46
lines changed

5 files changed

+221
-46
lines changed

runtime/doc/builtin.txt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -938,7 +938,8 @@ autocmd_add({acmds}) *autocmd_add()*
938938
item is ignored.
939939
cmd Ex command to execute for this autocmd event
940940
event autocmd event name. Refer to |autocmd-events|.
941-
TODO: currently only accepts one event.
941+
This can be either a String with a single
942+
event name or a List of event names.
942943
group autocmd group name. Refer to |autocmd-groups|.
943944
If this group doesn't exist then it is
944945
created. If not specified or empty, then the
@@ -950,7 +951,9 @@ autocmd_add({acmds}) *autocmd_add()*
950951
|autocmd-once|.
951952
pattern autocmd pattern string. Refer to
952953
|autocmd-patterns|. If "bufnr" item is
953-
present, then this item is ignored.
954+
present, then this item is ignored. This can
955+
be a String with a single pattern or a List of
956+
patterns.
954957
replace boolean flag, set to v:true to remove all the
955958
commands associated with the specified autocmd
956959
event and group and add the {cmd}. This is

src/autocmd.c

Lines changed: 134 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2754,16 +2754,22 @@ au_exists(char_u *arg)
27542754
static void
27552755
autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
27562756
{
2757-
list_T *event_list;
2757+
list_T *aucmd_list;
27582758
listitem_T *li;
27592759
dict_T *event_dict;
2760+
dictitem_T *di;
27602761
char_u *event_name = NULL;
2762+
list_T *event_list;
2763+
listitem_T *eli;
27612764
event_T event;
27622765
char_u *group_name = NULL;
27632766
int group;
27642767
char_u *pat = NULL;
2768+
list_T *pat_list;
2769+
listitem_T *pli;
27652770
char_u *cmd = NULL;
27662771
char_u *end;
2772+
char_u *p;
27672773
int once;
27682774
int nested;
27692775
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)
27762782
if (check_for_list_arg(argvars, 0) == FAIL)
27772783
return;
27782784

2779-
event_list = argvars[0].vval.v_list;
2780-
if (event_list == NULL)
2785+
aucmd_list = argvars[0].vval.v_list;
2786+
if (aucmd_list == NULL)
27812787
return;
27822788

2783-
FOR_ALL_LIST_ITEMS(event_list, li)
2789+
FOR_ALL_LIST_ITEMS(aucmd_list, li)
27842790
{
2785-
VIM_CLEAR(event_name);
27862791
VIM_CLEAR(group_name);
2787-
VIM_CLEAR(pat);
27882792
VIM_CLEAR(cmd);
2793+
event_name = NULL;
2794+
event_list = NULL;
2795+
pat = NULL;
2796+
pat_list = NULL;
27892797

27902798
if (li->li_tv.v_type != VAR_DICT)
27912799
continue;
@@ -2794,30 +2802,32 @@ autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
27942802
if (event_dict == NULL)
27952803
continue;
27962804

2797-
event_name = dict_get_string(event_dict, (char_u *)"event", TRUE);
2798-
if (event_name == NULL)
2799-
{
2800-
if (delete)
2801-
// if the event name is not specified, delete all the events
2802-
event = NUM_EVENTS;
2803-
else
2804-
continue;
2805-
}
2806-
else
2805+
di = dict_find(event_dict, (char_u *)"event", -1);
2806+
if (di != NULL)
28072807
{
2808-
if (delete && event_name[0] == '*' && event_name[1] == NUL)
2809-
// if the event name is '*', delete all the events
2810-
event = NUM_EVENTS;
2811-
else
2808+
if (di->di_tv.v_type == VAR_STRING)
28122809
{
2813-
event = event_name2nr(event_name, &end);
2814-
if (event == NUM_EVENTS)
2810+
event_name = di->di_tv.vval.v_string;
2811+
if (event_name == NULL)
28152812
{
2816-
semsg(_(e_no_such_event_str), event_name);
2817-
retval = VVAL_FALSE;
2818-
break;
2813+
emsg(_(e_string_required));
2814+
continue;
2815+
}
2816+
}
2817+
else if (di->di_tv.v_type == VAR_LIST)
2818+
{
2819+
event_list = di->di_tv.vval.v_list;
2820+
if (event_list == NULL)
2821+
{
2822+
emsg(_(e_list_required));
2823+
continue;
28192824
}
28202825
}
2826+
else
2827+
{
2828+
emsg(_(e_string_or_list_expected));
2829+
continue;
2830+
}
28212831
}
28222832

28232833
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)
28592869
if (bnum == -1)
28602870
continue;
28612871

2862-
pat = alloc(128 + 1);
2863-
if (pat == NULL)
2864-
continue;
2865-
vim_snprintf((char *)pat, 128, "<buffer=%d>", (int)bnum);
2872+
vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
2873+
pat = IObuff;
28662874
}
28672875
else
28682876
{
2869-
pat = dict_get_string(event_dict, (char_u *)"pattern", TRUE);
2870-
if (pat == NULL)
2877+
di = dict_find(event_dict, (char_u *)"pattern", -1);
2878+
if (di != NULL)
28712879
{
2872-
if (delete)
2873-
pat = vim_strsave((char_u *)"");
2880+
if (di->di_tv.v_type == VAR_STRING)
2881+
{
2882+
pat = di->di_tv.vval.v_string;
2883+
if (pat == NULL)
2884+
{
2885+
emsg(_(e_string_required));
2886+
continue;
2887+
}
2888+
}
2889+
else if (di->di_tv.v_type == VAR_LIST)
2890+
{
2891+
pat_list = di->di_tv.vval.v_list;
2892+
if (pat_list == NULL)
2893+
{
2894+
emsg(_(e_list_required));
2895+
continue;
2896+
}
2897+
}
28742898
else
2899+
{
2900+
emsg(_(e_string_or_list_expected));
28752901
continue;
2902+
}
28762903
}
2904+
else if (delete)
2905+
pat = (char_u *)"";
28772906
}
28782907

28792908
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)
28912920
continue;
28922921
}
28932922

2894-
if (event == NUM_EVENTS)
2923+
if (delete && (event_name == NULL
2924+
|| (event_name[0] == '*' && event_name[1] == NUL)))
28952925
{
2896-
// event is '*', apply for all the events
2926+
// if the event name is not specified or '*', delete all the events
28972927
for (event = (event_T)0; (int)event < NUM_EVENTS;
28982928
event = (event_T)((int)event + 1))
28992929
{
@@ -2907,11 +2937,76 @@ autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
29072937
}
29082938
else
29092939
{
2910-
if (do_autocmd_event(event, pat, once, nested, cmd,
2911-
delete | replace, group, 0) == FAIL)
2940+
eli = NULL;
2941+
end = NULL;
2942+
while (TRUE)
29122943
{
2913-
retval = VVAL_FALSE;
2914-
break;
2944+
if (event_list != NULL)
2945+
{
2946+
if (eli == NULL)
2947+
eli = event_list->lv_first;
2948+
else
2949+
eli = eli->li_next;
2950+
if (eli == NULL)
2951+
break;
2952+
if (eli->li_tv.v_type != VAR_STRING
2953+
|| eli->li_tv.vval.v_string == NULL)
2954+
{
2955+
emsg(_(e_string_required));
2956+
continue;
2957+
}
2958+
p = eli->li_tv.vval.v_string;
2959+
}
2960+
else
2961+
{
2962+
if (end == NULL)
2963+
p = end = event_name;
2964+
if (end == NULL || *end == NUL)
2965+
break;
2966+
}
2967+
if (p == NULL)
2968+
continue;
2969+
2970+
event = event_name2nr(p, &end);
2971+
if (event == NUM_EVENTS || *end != NUL)
2972+
{
2973+
semsg(_(e_no_such_event_str), p);
2974+
retval = VVAL_FALSE;
2975+
break;
2976+
}
2977+
if (pat != NULL)
2978+
{
2979+
if (do_autocmd_event(event, pat, once, nested, cmd,
2980+
delete | replace, group, 0) == FAIL)
2981+
{
2982+
retval = VVAL_FALSE;
2983+
break;
2984+
}
2985+
}
2986+
else if (pat_list != NULL)
2987+
{
2988+
FOR_ALL_LIST_ITEMS(pat_list, pli)
2989+
{
2990+
if (pli->li_tv.v_type != VAR_STRING
2991+
|| pli->li_tv.vval.v_string == NULL)
2992+
{
2993+
emsg(_(e_string_required));
2994+
continue;
2995+
}
2996+
if (do_autocmd_event(event,
2997+
pli->li_tv.vval.v_string, once, nested,
2998+
cmd, delete | replace, group, 0) ==
2999+
FAIL)
3000+
{
3001+
retval = VVAL_FALSE;
3002+
break;
3003+
}
3004+
}
3005+
if (retval == VVAL_FALSE)
3006+
break;
3007+
}
3008+
if (event_name != NULL)
3009+
p = end;
29153010
}
29163011
}
29173012

@@ -2925,9 +3020,7 @@ autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
29253020
au_del_group(group_name);
29263021
}
29273022

2928-
VIM_CLEAR(event_name);
29293023
VIM_CLEAR(group_name);
2930-
VIM_CLEAR(pat);
29313024
VIM_CLEAR(cmd);
29323025

29333026
current_augroup = save_augroup;

src/errors.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1184,7 +1184,7 @@ EXTERN char e_invalid_argument_str[]
11841184
INIT(= N_("E475: Invalid argument: %s"));
11851185
EXTERN char e_invalid_value_for_argument_str[]
11861186
INIT(= N_("E475: Invalid value for argument %s"));
1187-
#if defined(FEAT_JOB_CHANNEL) || defined(FEAT_PROP_POPUP)
1187+
#if defined(FEAT_JOB_CHANNEL) || defined(FEAT_PROP_POPUP) || defined(FEAT_EVAL)
11881188
EXTERN char e_invalid_value_for_argument_str_str[]
11891189
INIT(= N_("E475: Invalid value for argument %s: %s"));
11901190
#endif
@@ -3280,7 +3280,7 @@ EXTERN char e_atom_engine_must_be_at_start_of_pattern[]
32803280
INIT(= N_("E1281: Atom '\\%%#=%c' must be at the start of the pattern"));
32813281
#ifdef FEAT_EVAL
32823282
EXTERN char e_bitshift_ops_must_be_number[]
3283-
INIT(= N_("E1282: bitshift operands must be numbers"));
3283+
INIT(= N_("E1282: Bitshift operands must be numbers"));
32843284
EXTERN char e_bitshift_ops_must_be_postive[]
3285-
INIT(= N_("E1283: bitshift amount must be a positive number"));
3285+
INIT(= N_("E1283: Bitshift amount must be a positive number"));
32863286
#endif

src/testdir/test_autocmd.vim

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3429,6 +3429,83 @@ func Test_autocmd_add()
34293429
\ cmd: 'echo "bufadd"'}]
34303430
call assert_fails('call autocmd_add(l)', 'E216:')
34313431

3432+
" Test for using a list of events and patterns
3433+
call autocmd_delete([#{group: 'TestAcSet'}])
3434+
let l = [#{group: 'TestAcSet', event: ['BufEnter', 'BufLeave'],
3435+
\ pattern: ['*.py', '*.sh'], cmd: 'echo "bufcmds"'}]
3436+
call autocmd_add(l)
3437+
call assert_equal([
3438+
\ #{cmd: 'echo "bufcmds"', group: 'TestAcSet', pattern: '*.py',
3439+
\ nested: v:false, once: v:false, event: 'BufEnter'},
3440+
\ #{cmd: 'echo "bufcmds"', group: 'TestAcSet', pattern: '*.sh',
3441+
\ nested: v:false, once: v:false, event: 'BufEnter'},
3442+
\ #{cmd: 'echo "bufcmds"', group: 'TestAcSet', pattern: '*.py',
3443+
\ nested: v:false, once: v:false, event: 'BufLeave'},
3444+
\ #{cmd: 'echo "bufcmds"', group: 'TestAcSet', pattern: '*.sh',
3445+
\ nested: v:false, once: v:false, event: 'BufLeave'}],
3446+
\ autocmd_get(#{group: 'TestAcSet'}))
3447+
3448+
" Test for invalid values for 'event' item
3449+
call autocmd_delete([#{group: 'TestAcSet'}])
3450+
let l = [#{group: 'TestAcSet', event: test_null_string(),
3451+
\ pattern: "*.py", cmd: 'echo "bufcmds"'}]
3452+
call assert_fails('call autocmd_add(l)', 'E928:')
3453+
let l = [#{group: 'TestAcSet', event: test_null_list(),
3454+
\ pattern: "*.py", cmd: 'echo "bufcmds"'}]
3455+
call assert_fails('call autocmd_add(l)', 'E714:')
3456+
let l = [#{group: 'TestAcSet', event: {},
3457+
\ pattern: "*.py", cmd: 'echo "bufcmds"'}]
3458+
call assert_fails('call autocmd_add(l)', 'E777:')
3459+
let l = [#{group: 'TestAcSet', event: [{}],
3460+
\ pattern: "*.py", cmd: 'echo "bufcmds"'}]
3461+
call assert_fails('call autocmd_add(l)', 'E928:')
3462+
let l = [#{group: 'TestAcSet', event: [test_null_string()],
3463+
\ pattern: "*.py", cmd: 'echo "bufcmds"'}]
3464+
call assert_fails('call autocmd_add(l)', 'E928:')
3465+
let l = [#{group: 'TestAcSet', event: 'BufEnter,BufLeave',
3466+
\ pattern: '*.py', cmd: 'echo "bufcmds"'}]
3467+
call assert_fails('call autocmd_add(l)', 'E216:')
3468+
let l = [#{group: 'TestAcSet', event: [],
3469+
\ pattern: "*.py", cmd: 'echo "bufcmds"'}]
3470+
call autocmd_add(l)
3471+
let l = [#{group: 'TestAcSet', event: [""],
3472+
\ pattern: "*.py", cmd: 'echo "bufcmds"'}]
3473+
call assert_fails('call autocmd_add(l)', 'E216:')
3474+
let l = [#{group: 'TestAcSet', event: "",
3475+
\ pattern: "*.py", cmd: 'echo "bufcmds"'}]
3476+
call autocmd_add(l)
3477+
call assert_equal([], autocmd_get(#{group: 'TestAcSet'}))
3478+
3479+
" Test for invalid values for 'pattern' item
3480+
let l = [#{group: 'TestAcSet', event: "BufEnter",
3481+
\ pattern: test_null_string(), cmd: 'echo "bufcmds"'}]
3482+
let l = [#{group: 'TestAcSet', event: "BufEnter",
3483+
\ pattern: test_null_list(), cmd: 'echo "bufcmds"'}]
3484+
call assert_fails('call autocmd_add(l)', 'E714:')
3485+
let l = [#{group: 'TestAcSet', event: "BufEnter",
3486+
\ pattern: {}, cmd: 'echo "bufcmds"'}]
3487+
call assert_fails('call autocmd_add(l)', 'E777:')
3488+
let l = [#{group: 'TestAcSet', event: "BufEnter",
3489+
\ pattern: [{}], cmd: 'echo "bufcmds"'}]
3490+
call assert_fails('call autocmd_add(l)', 'E928:')
3491+
let l = [#{group: 'TestAcSet', event: "BufEnter",
3492+
\ pattern: [test_null_string()], cmd: 'echo "bufcmds"'}]
3493+
call assert_fails('call autocmd_add(l)', 'E928:')
3494+
let l = [#{group: 'TestAcSet', event: "BufEnter",
3495+
\ pattern: [], cmd: 'echo "bufcmds"'}]
3496+
call autocmd_add(l)
3497+
let l = [#{group: 'TestAcSet', event: "BufEnter",
3498+
\ pattern: [""], cmd: 'echo "bufcmds"'}]
3499+
call autocmd_add(l)
3500+
let l = [#{group: 'TestAcSet', event: "BufEnter",
3501+
\ pattern: "", cmd: 'echo "bufcmds"'}]
3502+
call autocmd_add(l)
3503+
call assert_equal([], autocmd_get(#{group: 'TestAcSet'}))
3504+
3505+
let l = [#{group: 'TestAcSet', event: 'BufEnter,abc,BufLeave',
3506+
\ pattern: '*.py', cmd: 'echo "bufcmds"'}]
3507+
call assert_fails('call autocmd_add(l)', 'E216:')
3508+
34323509
call assert_fails("call autocmd_add({})", 'E1211:')
34333510
call assert_equal(v:false, autocmd_add(test_null_list()))
34343511
call assert_true(autocmd_add([[]]))

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,8 @@ static char *(features[]) =
734734

735735
static int included_patches[] =
736736
{ /* Add new patch number below this line */
737+
/**/
738+
5030,
737739
/**/
738740
5029,
739741
/**/

0 commit comments

Comments
 (0)