1
0
forked from aniani/vim

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)
This commit is contained in:
Yegappan Lakshmanan
2022-05-27 18:05:33 +01:00
committed by Bram Moolenaar
parent cfe456543e
commit e0ff3a7de6
5 changed files with 222 additions and 47 deletions

View File

@@ -938,7 +938,8 @@ autocmd_add({acmds}) *autocmd_add()*
item is ignored. item is ignored.
cmd Ex command to execute for this autocmd event cmd Ex command to execute for this autocmd event
event autocmd event name. Refer to |autocmd-events|. 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|. group autocmd group name. Refer to |autocmd-groups|.
If this group doesn't exist then it is If this group doesn't exist then it is
created. If not specified or empty, then the created. If not specified or empty, then the
@@ -950,7 +951,9 @@ autocmd_add({acmds}) *autocmd_add()*
|autocmd-once|. |autocmd-once|.
pattern autocmd pattern string. Refer to pattern autocmd pattern string. Refer to
|autocmd-patterns|. If "bufnr" item is |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 replace boolean flag, set to v:true to remove all the
commands associated with the specified autocmd commands associated with the specified autocmd
event and group and add the {cmd}. This is event and group and add the {cmd}. This is

View File

@@ -2754,16 +2754,22 @@ theend:
static void static void
autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete) autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
{ {
list_T *event_list; list_T *aucmd_list;
listitem_T *li; listitem_T *li;
dict_T *event_dict; dict_T *event_dict;
dictitem_T *di;
char_u *event_name = NULL; char_u *event_name = NULL;
list_T *event_list;
listitem_T *eli;
event_T event; event_T event;
char_u *group_name = NULL; char_u *group_name = NULL;
int group; int group;
char_u *pat = NULL; char_u *pat = NULL;
list_T *pat_list;
listitem_T *pli;
char_u *cmd = NULL; char_u *cmd = NULL;
char_u *end; char_u *end;
char_u *p;
int once; int once;
int nested; int nested;
int replace; // replace the cmd for a group/event 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) if (check_for_list_arg(argvars, 0) == FAIL)
return; return;
event_list = argvars[0].vval.v_list; aucmd_list = argvars[0].vval.v_list;
if (event_list == NULL) if (aucmd_list == NULL)
return; 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(group_name);
VIM_CLEAR(pat);
VIM_CLEAR(cmd); VIM_CLEAR(cmd);
event_name = NULL;
event_list = NULL;
pat = NULL;
pat_list = NULL;
if (li->li_tv.v_type != VAR_DICT) if (li->li_tv.v_type != VAR_DICT)
continue; continue;
@@ -2794,29 +2802,31 @@ autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
if (event_dict == NULL) if (event_dict == NULL)
continue; continue;
event_name = dict_get_string(event_dict, (char_u *)"event", TRUE); di = dict_find(event_dict, (char_u *)"event", -1);
if (di != NULL)
{
if (di->di_tv.v_type == VAR_STRING)
{
event_name = di->di_tv.vval.v_string;
if (event_name == NULL) if (event_name == NULL)
{ {
if (delete) emsg(_(e_string_required));
// if the event name is not specified, delete all the events
event = NUM_EVENTS;
else
continue; 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;
} }
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;
} }
} }
@@ -2859,22 +2869,41 @@ autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
if (bnum == -1) if (bnum == -1)
continue; continue;
pat = alloc(128 + 1); vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
if (pat == NULL) pat = IObuff;
continue;
vim_snprintf((char *)pat, 128, "<buffer=%d>", (int)bnum);
} }
else else
{ {
pat = dict_get_string(event_dict, (char_u *)"pattern", TRUE); di = dict_find(event_dict, (char_u *)"pattern", -1);
if (di != NULL)
{
if (di->di_tv.v_type == VAR_STRING)
{
pat = di->di_tv.vval.v_string;
if (pat == NULL) if (pat == NULL)
{ {
if (delete) emsg(_(e_string_required));
pat = vim_strsave((char_u *)"");
else
continue; 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); once = dict_get_bool(event_dict, (char_u *)"once", FALSE);
nested = dict_get_bool(event_dict, (char_u *)"nested", FALSE); nested = dict_get_bool(event_dict, (char_u *)"nested", FALSE);
@@ -2891,9 +2920,10 @@ autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
continue; 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; for (event = (event_T)0; (int)event < NUM_EVENTS;
event = (event_T)((int)event + 1)) event = (event_T)((int)event + 1))
{ {
@@ -2906,6 +2936,45 @@ autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
} }
} }
else else
{
eli = NULL;
end = NULL;
while (TRUE)
{
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, if (do_autocmd_event(event, pat, once, nested, cmd,
delete | replace, group, 0) == FAIL) delete | replace, group, 0) == FAIL)
@@ -2914,6 +2983,32 @@ autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
break; 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;
}
}
// if only the autocmd group name is specified for delete and the // if only the autocmd group name is specified for delete and the
// autocmd event, pattern and cmd are not specified, then delete the // autocmd event, pattern and cmd are not specified, then delete the
@@ -2925,9 +3020,7 @@ autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
au_del_group(group_name); au_del_group(group_name);
} }
VIM_CLEAR(event_name);
VIM_CLEAR(group_name); VIM_CLEAR(group_name);
VIM_CLEAR(pat);
VIM_CLEAR(cmd); VIM_CLEAR(cmd);
current_augroup = save_augroup; current_augroup = save_augroup;

View File

@@ -1184,7 +1184,7 @@ EXTERN char e_invalid_argument_str[]
INIT(= N_("E475: Invalid argument: %s")); INIT(= N_("E475: Invalid argument: %s"));
EXTERN char e_invalid_value_for_argument_str[] EXTERN char e_invalid_value_for_argument_str[]
INIT(= N_("E475: Invalid value for argument %s")); 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[] EXTERN char e_invalid_value_for_argument_str_str[]
INIT(= N_("E475: Invalid value for argument %s: %s")); INIT(= N_("E475: Invalid value for argument %s: %s"));
#endif #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")); INIT(= N_("E1281: Atom '\\%%#=%c' must be at the start of the pattern"));
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
EXTERN char e_bitshift_ops_must_be_number[] 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[] 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 #endif

View File

@@ -3429,6 +3429,83 @@ func Test_autocmd_add()
\ cmd: 'echo "bufadd"'}] \ cmd: 'echo "bufadd"'}]
call assert_fails('call autocmd_add(l)', 'E216:') 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_fails("call autocmd_add({})", 'E1211:')
call assert_equal(v:false, autocmd_add(test_null_list())) call assert_equal(v:false, autocmd_add(test_null_list()))
call assert_true(autocmd_add([[]])) call assert_true(autocmd_add([[]]))

View File

@@ -734,6 +734,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
5030,
/**/ /**/
5029, 5029,
/**/ /**/