To: vim_dev@googlegroups.com Subject: Patch 8.2.5030 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.5030 Problem: autocmd_add() can only handle one event and pattern. Solution: Support a list of events and patterns. (Yegappan Lakshmanan, closes #10483) Files: runtime/doc/builtin.txt, src/autocmd.c, src/errors.h, src/testdir/test_autocmd.vim *** ../vim-8.2.5029/runtime/doc/builtin.txt 2022-05-26 12:10:33.589893490 +0100 --- runtime/doc/builtin.txt 2022-05-27 17:57:45.760052327 +0100 *************** *** 938,944 **** item is ignored. cmd Ex command to execute for this autocmd event event autocmd event name. Refer to |autocmd-events|. ! TODO: currently only accepts one event. 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 --- 938,945 ---- item is ignored. cmd Ex command to execute for this autocmd event event autocmd event name. Refer to |autocmd-events|. ! This can be either a String with a single ! event name or a List of event names. group autocmd group name. Refer to |autocmd-groups|. If this group doesn't exist then it is created. If not specified or empty, then the *************** *** 950,956 **** |autocmd-once|. pattern autocmd pattern string. Refer to |autocmd-patterns|. If "bufnr" item is ! present, then this item is ignored. replace boolean flag, set to v:true to remove all the commands associated with the specified autocmd event and group and add the {cmd}. This is --- 951,959 ---- |autocmd-once|. pattern autocmd pattern string. Refer to |autocmd-patterns|. If "bufnr" item is ! present, then this item is ignored. This can ! be a String with a single pattern or a List of ! patterns. replace boolean flag, set to v:true to remove all the commands associated with the specified autocmd event and group and add the {cmd}. This is *** ../vim-8.2.5029/src/autocmd.c 2022-05-24 11:40:07.514685757 +0100 --- src/autocmd.c 2022-05-27 17:57:45.764052324 +0100 *************** *** 2754,2769 **** 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 replace; // replace the cmd for a group/event --- 2754,2775 ---- static void autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete) { ! list_T *aucmd_list; listitem_T *li; dict_T *event_dict; + dictitem_T *di; char_u *event_name = NULL; + list_T *event_list; + listitem_T *eli; event_T event; char_u *group_name = NULL; int group; char_u *pat = NULL; + list_T *pat_list; + listitem_T *pli; char_u *cmd = NULL; char_u *end; + char_u *p; int once; int nested; int replace; // replace the cmd for a group/event *************** *** 2776,2791 **** 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; --- 2782,2799 ---- if (check_for_list_arg(argvars, 0) == FAIL) return; ! aucmd_list = argvars[0].vval.v_list; ! if (aucmd_list == NULL) return; ! FOR_ALL_LIST_ITEMS(aucmd_list, li) { VIM_CLEAR(group_name); VIM_CLEAR(cmd); + event_name = NULL; + event_list = NULL; + pat = NULL; + pat_list = NULL; if (li->li_tv.v_type != VAR_DICT) continue; *************** *** 2794,2823 **** 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); --- 2802,2833 ---- if (event_dict == NULL) continue; ! 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) { ! emsg(_(e_string_required)); ! continue; ! } ! } ! else if (di->di_tv.v_type == VAR_LIST) ! { ! event_list = di->di_tv.vval.v_list; ! if (event_list == NULL) ! { ! emsg(_(e_list_required)); ! continue; } } + else + { + emsg(_(e_string_or_list_expected)); + continue; + } } group_name = dict_get_string(event_dict, (char_u *)"group", TRUE); *************** *** 2859,2879 **** 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); --- 2869,2908 ---- if (bnum == -1) continue; ! vim_snprintf((char *)IObuff, IOSIZE, "", (int)bnum); ! pat = IObuff; } else { ! 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) ! { ! emsg(_(e_string_required)); ! continue; ! } ! } ! else if (di->di_tv.v_type == VAR_LIST) ! { ! pat_list = di->di_tv.vval.v_list; ! if (pat_list == NULL) ! { ! emsg(_(e_list_required)); ! continue; ! } ! } else + { + emsg(_(e_string_or_list_expected)); continue; + } } + else if (delete) + pat = (char_u *)""; } once = dict_get_bool(event_dict, (char_u *)"once", FALSE); *************** *** 2891,2899 **** 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)) { --- 2920,2929 ---- continue; } ! if (delete && (event_name == NULL ! || (event_name[0] == '*' && event_name[1] == NUL))) { ! // if the event name is not specified or '*', delete all the events for (event = (event_T)0; (int)event < NUM_EVENTS; event = (event_T)((int)event + 1)) { *************** *** 2907,2917 **** } else { ! if (do_autocmd_event(event, pat, once, nested, cmd, ! delete | replace, group, 0) == FAIL) { ! retval = VVAL_FALSE; ! break; } } --- 2937,3012 ---- } 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, ! delete | replace, group, 0) == FAIL) ! { ! retval = VVAL_FALSE; ! break; ! } ! } ! else if (pat_list != NULL) ! { ! FOR_ALL_LIST_ITEMS(pat_list, pli) ! { ! if (pli->li_tv.v_type != VAR_STRING ! || pli->li_tv.vval.v_string == NULL) ! { ! emsg(_(e_string_required)); ! continue; ! } ! if (do_autocmd_event(event, ! pli->li_tv.vval.v_string, once, nested, ! cmd, delete | replace, group, 0) == ! FAIL) ! { ! retval = VVAL_FALSE; ! break; ! } ! } ! if (retval == VVAL_FALSE) ! break; ! } ! if (event_name != NULL) ! p = end; } } *************** *** 2925,2933 **** au_del_group(group_name); } - VIM_CLEAR(event_name); VIM_CLEAR(group_name); - VIM_CLEAR(pat); VIM_CLEAR(cmd); current_augroup = save_augroup; --- 3020,3026 ---- *** ../vim-8.2.5029/src/errors.h 2022-05-27 17:26:50.538119977 +0100 --- src/errors.h 2022-05-27 17:57:45.764052324 +0100 *************** *** 1184,1190 **** INIT(= N_("E475: Invalid argument: %s")); EXTERN char e_invalid_value_for_argument_str[] INIT(= N_("E475: Invalid value for argument %s")); ! #if defined(FEAT_JOB_CHANNEL) || defined(FEAT_PROP_POPUP) EXTERN char e_invalid_value_for_argument_str_str[] INIT(= N_("E475: Invalid value for argument %s: %s")); #endif --- 1184,1190 ---- INIT(= N_("E475: Invalid argument: %s")); EXTERN char e_invalid_value_for_argument_str[] INIT(= N_("E475: Invalid value for argument %s")); ! #if defined(FEAT_JOB_CHANNEL) || defined(FEAT_PROP_POPUP) || defined(FEAT_EVAL) EXTERN char e_invalid_value_for_argument_str_str[] INIT(= N_("E475: Invalid value for argument %s: %s")); #endif *************** *** 3280,3286 **** INIT(= N_("E1281: Atom '\\%%#=%c' must be at the start of the pattern")); #ifdef FEAT_EVAL EXTERN char e_bitshift_ops_must_be_number[] ! INIT(= N_("E1282: bitshift operands must be numbers")); EXTERN char e_bitshift_ops_must_be_postive[] ! INIT(= N_("E1283: bitshift amount must be a positive number")); #endif --- 3280,3286 ---- INIT(= N_("E1281: Atom '\\%%#=%c' must be at the start of the pattern")); #ifdef FEAT_EVAL EXTERN char e_bitshift_ops_must_be_number[] ! INIT(= N_("E1282: Bitshift operands must be numbers")); EXTERN char e_bitshift_ops_must_be_postive[] ! INIT(= N_("E1283: Bitshift amount must be a positive number")); #endif *** ../vim-8.2.5029/src/testdir/test_autocmd.vim 2022-05-24 11:40:07.518685751 +0100 --- src/testdir/test_autocmd.vim 2022-05-27 17:57:45.764052324 +0100 *************** *** 3429,3434 **** --- 3429,3511 ---- \ cmd: 'echo "bufadd"'}] call assert_fails('call autocmd_add(l)', 'E216:') + " Test for using a list of events and patterns + call autocmd_delete([#{group: 'TestAcSet'}]) + let l = [#{group: 'TestAcSet', event: ['BufEnter', 'BufLeave'], + \ pattern: ['*.py', '*.sh'], cmd: 'echo "bufcmds"'}] + call autocmd_add(l) + call assert_equal([ + \ #{cmd: 'echo "bufcmds"', group: 'TestAcSet', pattern: '*.py', + \ nested: v:false, once: v:false, event: 'BufEnter'}, + \ #{cmd: 'echo "bufcmds"', group: 'TestAcSet', pattern: '*.sh', + \ nested: v:false, once: v:false, event: 'BufEnter'}, + \ #{cmd: 'echo "bufcmds"', group: 'TestAcSet', pattern: '*.py', + \ nested: v:false, once: v:false, event: 'BufLeave'}, + \ #{cmd: 'echo "bufcmds"', group: 'TestAcSet', pattern: '*.sh', + \ nested: v:false, once: v:false, event: 'BufLeave'}], + \ autocmd_get(#{group: 'TestAcSet'})) + + " Test for invalid values for 'event' item + call autocmd_delete([#{group: 'TestAcSet'}]) + let l = [#{group: 'TestAcSet', event: test_null_string(), + \ pattern: "*.py", cmd: 'echo "bufcmds"'}] + call assert_fails('call autocmd_add(l)', 'E928:') + let l = [#{group: 'TestAcSet', event: test_null_list(), + \ pattern: "*.py", cmd: 'echo "bufcmds"'}] + call assert_fails('call autocmd_add(l)', 'E714:') + let l = [#{group: 'TestAcSet', event: {}, + \ pattern: "*.py", cmd: 'echo "bufcmds"'}] + call assert_fails('call autocmd_add(l)', 'E777:') + let l = [#{group: 'TestAcSet', event: [{}], + \ pattern: "*.py", cmd: 'echo "bufcmds"'}] + call assert_fails('call autocmd_add(l)', 'E928:') + let l = [#{group: 'TestAcSet', event: [test_null_string()], + \ pattern: "*.py", cmd: 'echo "bufcmds"'}] + call assert_fails('call autocmd_add(l)', 'E928:') + let l = [#{group: 'TestAcSet', event: 'BufEnter,BufLeave', + \ pattern: '*.py', cmd: 'echo "bufcmds"'}] + call assert_fails('call autocmd_add(l)', 'E216:') + let l = [#{group: 'TestAcSet', event: [], + \ pattern: "*.py", cmd: 'echo "bufcmds"'}] + call autocmd_add(l) + let l = [#{group: 'TestAcSet', event: [""], + \ pattern: "*.py", cmd: 'echo "bufcmds"'}] + call assert_fails('call autocmd_add(l)', 'E216:') + let l = [#{group: 'TestAcSet', event: "", + \ pattern: "*.py", cmd: 'echo "bufcmds"'}] + call autocmd_add(l) + call assert_equal([], autocmd_get(#{group: 'TestAcSet'})) + + " Test for invalid values for 'pattern' item + let l = [#{group: 'TestAcSet', event: "BufEnter", + \ pattern: test_null_string(), cmd: 'echo "bufcmds"'}] + let l = [#{group: 'TestAcSet', event: "BufEnter", + \ pattern: test_null_list(), cmd: 'echo "bufcmds"'}] + call assert_fails('call autocmd_add(l)', 'E714:') + let l = [#{group: 'TestAcSet', event: "BufEnter", + \ pattern: {}, cmd: 'echo "bufcmds"'}] + call assert_fails('call autocmd_add(l)', 'E777:') + let l = [#{group: 'TestAcSet', event: "BufEnter", + \ pattern: [{}], cmd: 'echo "bufcmds"'}] + call assert_fails('call autocmd_add(l)', 'E928:') + let l = [#{group: 'TestAcSet', event: "BufEnter", + \ pattern: [test_null_string()], cmd: 'echo "bufcmds"'}] + call assert_fails('call autocmd_add(l)', 'E928:') + let l = [#{group: 'TestAcSet', event: "BufEnter", + \ pattern: [], cmd: 'echo "bufcmds"'}] + call autocmd_add(l) + let l = [#{group: 'TestAcSet', event: "BufEnter", + \ pattern: [""], cmd: 'echo "bufcmds"'}] + call autocmd_add(l) + let l = [#{group: 'TestAcSet', event: "BufEnter", + \ pattern: "", cmd: 'echo "bufcmds"'}] + call autocmd_add(l) + call assert_equal([], autocmd_get(#{group: 'TestAcSet'})) + + let l = [#{group: 'TestAcSet', event: 'BufEnter,abc,BufLeave', + \ pattern: '*.py', cmd: 'echo "bufcmds"'}] + call assert_fails('call autocmd_add(l)', 'E216:') + call assert_fails("call autocmd_add({})", 'E1211:') call assert_equal(v:false, autocmd_add(test_null_list())) call assert_true(autocmd_add([[]])) *** ../vim-8.2.5029/src/version.c 2022-05-27 17:26:50.546119970 +0100 --- src/version.c 2022-05-27 18:04:38.939665812 +0100 *************** *** 736,737 **** --- 736,739 ---- { /* Add new patch number below this line */ + /**/ + 5030, /**/ -- LAUNCELOT: At last! A call! A cry of distress ... (he draws his sword, and turns to CONCORDE) Concorde! Brave, Concorde ... you shall not have died in vain! CONCORDE: I'm not quite dead, sir ... "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// 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 ///