# HG changeset patch # User Christian Brabandt # Date 1712083503 -7200 # Node ID 63e3334bcadb7b81bdd666584ed8d06633a3f00b # Parent 317067ddcba2ec5cb4b6516bbcc4c2b43b53de26 patch 9.1.0256: Finding autocmd events is inefficient Commit: https://github.com/vim/vim/commit/78d742ab8845578f78039ddd71a6444c6929257c Author: John Marriott Date: Tue Apr 2 20:26:01 2024 +0200 patch 9.1.0256: Finding autocmd events is inefficient Problem: Finding autocmd events is inefficient Solution: Use binary search to find events, cache last found events, avoid use of strlen(), add SessionWritePost autocmd, fix test_codestyle and avoid endless loop (John Marriott) closes: #14287 Signed-off-by: John Marriott Signed-off-by: Christian Brabandt diff --git a/src/autocmd.c b/src/autocmd.c --- a/src/autocmd.c +++ b/src/autocmd.c @@ -74,153 +74,166 @@ typedef struct AutoPat char last; // last pattern for apply_autocmds() } AutoPat; -static struct event_name -{ - char *name; // event name - event_T event; // event number -} event_names[] = -{ - {"BufAdd", EVENT_BUFADD}, - {"BufCreate", EVENT_BUFADD}, - {"BufDelete", EVENT_BUFDELETE}, - {"BufEnter", EVENT_BUFENTER}, - {"BufFilePost", EVENT_BUFFILEPOST}, - {"BufFilePre", EVENT_BUFFILEPRE}, - {"BufHidden", EVENT_BUFHIDDEN}, - {"BufLeave", EVENT_BUFLEAVE}, - {"BufNew", EVENT_BUFNEW}, - {"BufNewFile", EVENT_BUFNEWFILE}, - {"BufRead", EVENT_BUFREADPOST}, - {"BufReadCmd", EVENT_BUFREADCMD}, - {"BufReadPost", EVENT_BUFREADPOST}, - {"BufReadPre", EVENT_BUFREADPRE}, - {"BufUnload", EVENT_BUFUNLOAD}, - {"BufWinEnter", EVENT_BUFWINENTER}, - {"BufWinLeave", EVENT_BUFWINLEAVE}, - {"BufWipeout", EVENT_BUFWIPEOUT}, - {"BufWrite", EVENT_BUFWRITEPRE}, - {"BufWritePost", EVENT_BUFWRITEPOST}, - {"BufWritePre", EVENT_BUFWRITEPRE}, - {"BufWriteCmd", EVENT_BUFWRITECMD}, - {"CmdlineChanged", EVENT_CMDLINECHANGED}, - {"CmdlineEnter", EVENT_CMDLINEENTER}, - {"CmdlineLeave", EVENT_CMDLINELEAVE}, - {"CmdwinEnter", EVENT_CMDWINENTER}, - {"CmdwinLeave", EVENT_CMDWINLEAVE}, - {"CmdUndefined", EVENT_CMDUNDEFINED}, - {"ColorScheme", EVENT_COLORSCHEME}, - {"ColorSchemePre", EVENT_COLORSCHEMEPRE}, - {"CompleteChanged", EVENT_COMPLETECHANGED}, - {"CompleteDone", EVENT_COMPLETEDONE}, - {"CompleteDonePre", EVENT_COMPLETEDONEPRE}, - {"CursorHold", EVENT_CURSORHOLD}, - {"CursorHoldI", EVENT_CURSORHOLDI}, - {"CursorMoved", EVENT_CURSORMOVED}, - {"CursorMovedI", EVENT_CURSORMOVEDI}, - {"DiffUpdated", EVENT_DIFFUPDATED}, - {"DirChanged", EVENT_DIRCHANGED}, - {"DirChangedPre", EVENT_DIRCHANGEDPRE}, - {"EncodingChanged", EVENT_ENCODINGCHANGED}, - {"ExitPre", EVENT_EXITPRE}, - {"FileEncoding", EVENT_ENCODINGCHANGED}, - {"FileAppendPost", EVENT_FILEAPPENDPOST}, - {"FileAppendPre", EVENT_FILEAPPENDPRE}, - {"FileAppendCmd", EVENT_FILEAPPENDCMD}, - {"FileChangedShell",EVENT_FILECHANGEDSHELL}, - {"FileChangedShellPost",EVENT_FILECHANGEDSHELLPOST}, - {"FileChangedRO", EVENT_FILECHANGEDRO}, - {"FileReadPost", EVENT_FILEREADPOST}, - {"FileReadPre", EVENT_FILEREADPRE}, - {"FileReadCmd", EVENT_FILEREADCMD}, - {"FileType", EVENT_FILETYPE}, - {"FileWritePost", EVENT_FILEWRITEPOST}, - {"FileWritePre", EVENT_FILEWRITEPRE}, - {"FileWriteCmd", EVENT_FILEWRITECMD}, - {"FilterReadPost", EVENT_FILTERREADPOST}, - {"FilterReadPre", EVENT_FILTERREADPRE}, - {"FilterWritePost", EVENT_FILTERWRITEPOST}, - {"FilterWritePre", EVENT_FILTERWRITEPRE}, - {"FocusGained", EVENT_FOCUSGAINED}, - {"FocusLost", EVENT_FOCUSLOST}, - {"FuncUndefined", EVENT_FUNCUNDEFINED}, - {"GUIEnter", EVENT_GUIENTER}, - {"GUIFailed", EVENT_GUIFAILED}, - {"InsertChange", EVENT_INSERTCHANGE}, - {"InsertEnter", EVENT_INSERTENTER}, - {"InsertLeave", EVENT_INSERTLEAVE}, - {"InsertLeavePre", EVENT_INSERTLEAVEPRE}, - {"InsertCharPre", EVENT_INSERTCHARPRE}, - {"MenuPopup", EVENT_MENUPOPUP}, - {"ModeChanged", EVENT_MODECHANGED}, - {"OptionSet", EVENT_OPTIONSET}, - {"QuickFixCmdPost", EVENT_QUICKFIXCMDPOST}, - {"QuickFixCmdPre", EVENT_QUICKFIXCMDPRE}, - {"QuitPre", EVENT_QUITPRE}, - {"RemoteReply", EVENT_REMOTEREPLY}, - {"SafeState", EVENT_SAFESTATE}, - {"SafeStateAgain", EVENT_SAFESTATEAGAIN}, - {"SessionLoadPost", EVENT_SESSIONLOADPOST}, - {"SessionWritePost",EVENT_SESSIONWRITEPOST}, - {"ShellCmdPost", EVENT_SHELLCMDPOST}, - {"ShellFilterPost", EVENT_SHELLFILTERPOST}, - {"SigUSR1", EVENT_SIGUSR1}, - {"SourceCmd", EVENT_SOURCECMD}, - {"SourcePre", EVENT_SOURCEPRE}, - {"SourcePost", EVENT_SOURCEPOST}, - {"SpellFileMissing",EVENT_SPELLFILEMISSING}, - {"StdinReadPost", EVENT_STDINREADPOST}, - {"StdinReadPre", EVENT_STDINREADPRE}, - {"SwapExists", EVENT_SWAPEXISTS}, - {"Syntax", EVENT_SYNTAX}, - {"TabNew", EVENT_TABNEW}, - {"TabClosed", EVENT_TABCLOSED}, - {"TabEnter", EVENT_TABENTER}, - {"TabLeave", EVENT_TABLEAVE}, - {"TermChanged", EVENT_TERMCHANGED}, - {"TerminalOpen", EVENT_TERMINALOPEN}, - {"TerminalWinOpen", EVENT_TERMINALWINOPEN}, - {"TermResponse", EVENT_TERMRESPONSE}, - {"TermResponseAll", EVENT_TERMRESPONSEALL}, - {"TextChanged", EVENT_TEXTCHANGED}, - {"TextChangedI", EVENT_TEXTCHANGEDI}, - {"TextChangedP", EVENT_TEXTCHANGEDP}, - {"TextChangedT", EVENT_TEXTCHANGEDT}, - {"User", EVENT_USER}, - {"VimEnter", EVENT_VIMENTER}, - {"VimLeave", EVENT_VIMLEAVE}, - {"VimLeavePre", EVENT_VIMLEAVEPRE}, - {"WinNewPre", EVENT_WINNEWPRE}, - {"WinNew", EVENT_WINNEW}, - {"WinClosed", EVENT_WINCLOSED}, - {"WinEnter", EVENT_WINENTER}, - {"WinLeave", EVENT_WINLEAVE}, - {"WinResized", EVENT_WINRESIZED}, - {"WinScrolled", EVENT_WINSCROLLED}, - {"VimResized", EVENT_VIMRESIZED}, - {"TextYankPost", EVENT_TEXTYANKPOST}, - {"VimSuspend", EVENT_VIMSUSPEND}, - {"VimResume", EVENT_VIMRESUME}, - {NULL, (event_T)0} +// +// special cases: +// BufNewFile and BufRead are searched for ALOT (especially at startup) +// so we pre-determine their index into the event_tab[] table for fast access. +// Keep these values in sync with event_tab[]! +#define BUFNEWFILE_INDEX 9 +#define BUFREAD_INDEX 10 + +// must be sorted by the 'value' field because it is used by bsearch()! +static keyvalue_T event_tab[] = { + KEYVALUE_ENTRY(EVENT_BUFADD, "BufAdd"), + KEYVALUE_ENTRY(EVENT_BUFADD, "BufCreate"), + KEYVALUE_ENTRY(EVENT_BUFDELETE, "BufDelete"), + KEYVALUE_ENTRY(EVENT_BUFENTER, "BufEnter"), + KEYVALUE_ENTRY(EVENT_BUFFILEPOST, "BufFilePost"), + KEYVALUE_ENTRY(EVENT_BUFFILEPRE, "BufFilePre"), + KEYVALUE_ENTRY(EVENT_BUFHIDDEN, "BufHidden"), + KEYVALUE_ENTRY(EVENT_BUFLEAVE, "BufLeave"), + KEYVALUE_ENTRY(EVENT_BUFNEW, "BufNew"), + KEYVALUE_ENTRY(EVENT_BUFNEWFILE, "BufNewFile"), // BUFNEWFILE_INDEX + KEYVALUE_ENTRY(EVENT_BUFREADPOST, "BufRead"), // BUFREAD_INDEX + KEYVALUE_ENTRY(EVENT_BUFREADCMD, "BufReadCmd"), + KEYVALUE_ENTRY(EVENT_BUFREADPOST, "BufReadPost"), + KEYVALUE_ENTRY(EVENT_BUFREADPRE, "BufReadPre"), + KEYVALUE_ENTRY(EVENT_BUFUNLOAD, "BufUnload"), + KEYVALUE_ENTRY(EVENT_BUFWINENTER, "BufWinEnter"), + KEYVALUE_ENTRY(EVENT_BUFWINLEAVE, "BufWinLeave"), + KEYVALUE_ENTRY(EVENT_BUFWIPEOUT, "BufWipeout"), + KEYVALUE_ENTRY(EVENT_BUFWRITEPRE, "BufWrite"), + KEYVALUE_ENTRY(EVENT_BUFWRITECMD, "BufWriteCmd"), + KEYVALUE_ENTRY(EVENT_BUFWRITEPOST, "BufWritePost"), + KEYVALUE_ENTRY(EVENT_BUFWRITEPRE, "BufWritePre"), + KEYVALUE_ENTRY(EVENT_CMDLINECHANGED, "CmdlineChanged"), + KEYVALUE_ENTRY(EVENT_CMDLINEENTER, "CmdlineEnter"), + KEYVALUE_ENTRY(EVENT_CMDLINELEAVE, "CmdlineLeave"), + KEYVALUE_ENTRY(EVENT_CMDUNDEFINED, "CmdUndefined"), + KEYVALUE_ENTRY(EVENT_CMDWINENTER, "CmdwinEnter"), + KEYVALUE_ENTRY(EVENT_CMDWINLEAVE, "CmdwinLeave"), + KEYVALUE_ENTRY(EVENT_COLORSCHEME, "ColorScheme"), + KEYVALUE_ENTRY(EVENT_COLORSCHEMEPRE, "ColorSchemePre"), + KEYVALUE_ENTRY(EVENT_COMPLETECHANGED, "CompleteChanged"), + KEYVALUE_ENTRY(EVENT_COMPLETEDONE, "CompleteDone"), + KEYVALUE_ENTRY(EVENT_COMPLETEDONEPRE, "CompleteDonePre"), + KEYVALUE_ENTRY(EVENT_CURSORHOLD, "CursorHold"), + KEYVALUE_ENTRY(EVENT_CURSORHOLDI, "CursorHoldI"), + KEYVALUE_ENTRY(EVENT_CURSORMOVED, "CursorMoved"), + KEYVALUE_ENTRY(EVENT_CURSORMOVEDI, "CursorMovedI"), + KEYVALUE_ENTRY(EVENT_DIFFUPDATED, "DiffUpdated"), + KEYVALUE_ENTRY(EVENT_DIRCHANGED, "DirChanged"), + KEYVALUE_ENTRY(EVENT_DIRCHANGEDPRE, "DirChangedPre"), + KEYVALUE_ENTRY(EVENT_ENCODINGCHANGED, "EncodingChanged"), + KEYVALUE_ENTRY(EVENT_EXITPRE, "ExitPre"), + KEYVALUE_ENTRY(EVENT_FILEAPPENDCMD, "FileAppendCmd"), + KEYVALUE_ENTRY(EVENT_FILEAPPENDPOST, "FileAppendPost"), + KEYVALUE_ENTRY(EVENT_FILEAPPENDPRE, "FileAppendPre"), + KEYVALUE_ENTRY(EVENT_FILECHANGEDRO, "FileChangedRO"), + KEYVALUE_ENTRY(EVENT_FILECHANGEDSHELL, "FileChangedShell"), + KEYVALUE_ENTRY(EVENT_FILECHANGEDSHELLPOST, "FileChangedShellPost"), + KEYVALUE_ENTRY(EVENT_ENCODINGCHANGED, "FileEncoding"), + KEYVALUE_ENTRY(EVENT_FILEREADCMD, "FileReadCmd"), + KEYVALUE_ENTRY(EVENT_FILEREADPOST, "FileReadPost"), + KEYVALUE_ENTRY(EVENT_FILEREADPRE, "FileReadPre"), + KEYVALUE_ENTRY(EVENT_FILETYPE, "FileType"), + KEYVALUE_ENTRY(EVENT_FILEWRITECMD, "FileWriteCmd"), + KEYVALUE_ENTRY(EVENT_FILEWRITEPOST, "FileWritePost"), + KEYVALUE_ENTRY(EVENT_FILEWRITEPRE, "FileWritePre"), + KEYVALUE_ENTRY(EVENT_FILTERREADPOST, "FilterReadPost"), + KEYVALUE_ENTRY(EVENT_FILTERREADPRE, "FilterReadPre"), + KEYVALUE_ENTRY(EVENT_FILTERWRITEPOST, "FilterWritePost"), + KEYVALUE_ENTRY(EVENT_FILTERWRITEPRE, "FilterWritePre"), + KEYVALUE_ENTRY(EVENT_FOCUSGAINED, "FocusGained"), + KEYVALUE_ENTRY(EVENT_FOCUSLOST, "FocusLost"), + KEYVALUE_ENTRY(EVENT_FUNCUNDEFINED, "FuncUndefined"), + KEYVALUE_ENTRY(EVENT_GUIENTER, "GUIEnter"), + KEYVALUE_ENTRY(EVENT_GUIFAILED, "GUIFailed"), + KEYVALUE_ENTRY(EVENT_INSERTCHANGE, "InsertChange"), + KEYVALUE_ENTRY(EVENT_INSERTCHARPRE, "InsertCharPre"), + KEYVALUE_ENTRY(EVENT_INSERTENTER, "InsertEnter"), + KEYVALUE_ENTRY(EVENT_INSERTLEAVE, "InsertLeave"), + KEYVALUE_ENTRY(EVENT_INSERTLEAVEPRE, "InsertLeavePre"), + KEYVALUE_ENTRY(EVENT_MENUPOPUP, "MenuPopup"), + KEYVALUE_ENTRY(EVENT_MODECHANGED, "ModeChanged"), + KEYVALUE_ENTRY(EVENT_OPTIONSET, "OptionSet"), + KEYVALUE_ENTRY(EVENT_QUICKFIXCMDPOST, "QuickFixCmdPost"), + KEYVALUE_ENTRY(EVENT_QUICKFIXCMDPRE, "QuickFixCmdPre"), + KEYVALUE_ENTRY(EVENT_QUITPRE, "QuitPre"), + KEYVALUE_ENTRY(EVENT_REMOTEREPLY, "RemoteReply"), + KEYVALUE_ENTRY(EVENT_SAFESTATE, "SafeState"), + KEYVALUE_ENTRY(EVENT_SAFESTATEAGAIN, "SafeStateAgain"), + KEYVALUE_ENTRY(EVENT_SESSIONLOADPOST, "SessionLoadPost"), + KEYVALUE_ENTRY(EVENT_SESSIONWRITEPOST, "SessionWritePost"), + KEYVALUE_ENTRY(EVENT_SHELLCMDPOST, "ShellCmdPost"), + KEYVALUE_ENTRY(EVENT_SHELLFILTERPOST, "ShellFilterPost"), + KEYVALUE_ENTRY(EVENT_SIGUSR1, "SigUSR1"), + KEYVALUE_ENTRY(EVENT_SOURCECMD, "SourceCmd"), + KEYVALUE_ENTRY(EVENT_SOURCEPOST, "SourcePost"), + KEYVALUE_ENTRY(EVENT_SOURCEPRE, "SourcePre"), + KEYVALUE_ENTRY(EVENT_SPELLFILEMISSING, "SpellFileMissing"), + KEYVALUE_ENTRY(EVENT_STDINREADPOST, "StdinReadPost"), + KEYVALUE_ENTRY(EVENT_STDINREADPRE, "StdinReadPre"), + KEYVALUE_ENTRY(EVENT_SWAPEXISTS, "SwapExists"), + KEYVALUE_ENTRY(EVENT_SYNTAX, "Syntax"), + KEYVALUE_ENTRY(EVENT_TABCLOSED, "TabClosed"), + KEYVALUE_ENTRY(EVENT_TABENTER, "TabEnter"), + KEYVALUE_ENTRY(EVENT_TABLEAVE, "TabLeave"), + KEYVALUE_ENTRY(EVENT_TABNEW, "TabNew"), + KEYVALUE_ENTRY(EVENT_TERMCHANGED, "TermChanged"), + KEYVALUE_ENTRY(EVENT_TERMINALOPEN, "TerminalOpen"), + KEYVALUE_ENTRY(EVENT_TERMINALWINOPEN, "TerminalWinOpen"), + KEYVALUE_ENTRY(EVENT_TERMRESPONSE, "TermResponse"), + KEYVALUE_ENTRY(EVENT_TERMRESPONSEALL, "TermResponseAll"), + KEYVALUE_ENTRY(EVENT_TEXTCHANGED, "TextChanged"), + KEYVALUE_ENTRY(EVENT_TEXTCHANGEDI, "TextChangedI"), + KEYVALUE_ENTRY(EVENT_TEXTCHANGEDP, "TextChangedP"), + KEYVALUE_ENTRY(EVENT_TEXTCHANGEDT, "TextChangedT"), + KEYVALUE_ENTRY(EVENT_TEXTYANKPOST, "TextYankPost"), + KEYVALUE_ENTRY(EVENT_USER, "User"), + KEYVALUE_ENTRY(EVENT_VIMENTER, "VimEnter"), + KEYVALUE_ENTRY(EVENT_VIMLEAVE, "VimLeave"), + KEYVALUE_ENTRY(EVENT_VIMLEAVEPRE, "VimLeavePre"), + KEYVALUE_ENTRY(EVENT_VIMRESIZED, "VimResized"), + KEYVALUE_ENTRY(EVENT_VIMRESUME, "VimResume"), + KEYVALUE_ENTRY(EVENT_VIMSUSPEND, "VimSuspend"), + KEYVALUE_ENTRY(EVENT_WINCLOSED, "WinClosed"), + KEYVALUE_ENTRY(EVENT_WINENTER, "WinEnter"), + KEYVALUE_ENTRY(EVENT_WINLEAVE, "WinLeave"), + KEYVALUE_ENTRY(EVENT_WINNEW, "WinNew"), + KEYVALUE_ENTRY(EVENT_WINNEWPRE, "WinNewPre"), + KEYVALUE_ENTRY(EVENT_WINRESIZED, "WinResized"), + KEYVALUE_ENTRY(EVENT_WINSCROLLED, "WinScrolled") }; -static AutoPat *first_autopat[NUM_EVENTS] = -{ +static AutoPat *first_autopat[NUM_EVENTS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL -}; - -static AutoPat *last_autopat[NUM_EVENTS] = -{ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL + NULL, NULL, NULL, NULL, NULL, NULL +}; + +static AutoPat *last_autopat[NUM_EVENTS] = { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL }; #define AUGROUP_DEFAULT (-1) // default autocmd group @@ -266,6 +279,7 @@ static int current_augroup = AUGROUP_DEF static int au_need_clean = FALSE; // need to delete marked patterns +static event_T event_name2nr(char_u *start, char_u **end); static char_u *event_nr2name(event_T event); static int au_get_grouparg(char_u **argp); static int do_autocmd_event(event_T event, char_u *pat, int once, int nested, char_u *cmd, int forceit, int group, int flags); @@ -681,24 +695,35 @@ is_aucmd_win(win_T *win) event_name2nr(char_u *start, char_u **end) { char_u *p; - int i; - int len; + keyvalue_T target; + keyvalue_T *entry; + static keyvalue_T *bufnewfile = &event_tab[BUFNEWFILE_INDEX]; + static keyvalue_T *bufread = &event_tab[BUFREAD_INDEX]; // the event name ends with end of line, '|', a blank or a comma for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p) ; - for (i = 0; event_names[i].name != NULL; ++i) - { - len = (int)STRLEN(event_names[i].name); - if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0) - break; - } + + target.key = 0; + target.value = (char *)start; + target.length = (size_t)(p - start); + + // special cases: + // BufNewFile and BufRead are searched for ALOT (especially at startup) + // so we check for them first. + if (cmp_keyvalue_value_ni(&target, bufnewfile) == 0) + entry = bufnewfile; + else + if (cmp_keyvalue_value_ni(&target, bufread) == 0) + entry = bufread; + else + entry = (keyvalue_T *)bsearch(&target, &event_tab, ARRAY_LENGTH(event_tab), sizeof(event_tab[0]), cmp_keyvalue_value_ni); + if (*p == ',') ++p; *end = p; - if (event_names[i].name == NULL) - return NUM_EVENTS; - return event_names[i].event; + + return (entry == NULL) ? NUM_EVENTS : (event_T)entry->key; } /* @@ -708,11 +733,52 @@ event_name2nr(char_u *start, char_u **en event_nr2name(event_T event) { int i; - - for (i = 0; event_names[i].name != NULL; ++i) - if (event_names[i].event == event) - return (char_u *)event_names[i].name; - return (char_u *)"Unknown"; +#define CACHE_SIZE 12 + static int cache_tab[CACHE_SIZE]; + static int cache_last_index = -1; + + if (cache_last_index < 0) + { + for (i = 0; i < (int)ARRAY_LENGTH(cache_tab); ++i) + cache_tab[i] = -1; + cache_last_index = ARRAY_LENGTH(cache_tab) - 1; + } + + // first look in the cache + // the cache is circular. to search it we start at the most recent entry + // and go backwards wrapping around when we get to index 0. + for (i = cache_last_index; cache_tab[i] >= 0; ) + { + if ((event_T)event_tab[cache_tab[i]].key == event) + return (char_u *)event_tab[cache_tab[i]].value; + + if (i == 0) + i = ARRAY_LENGTH(cache_tab) - 1; + else + --i; + + // are we back at the start? + if (i == cache_last_index) + break; + } + + // look in the event table itself + for (i = 0; i < (int)ARRAY_LENGTH(event_tab); ++i) + { + if ((event_T)event_tab[i].key == event) + { + // store the found entry in the next position in the cache, + // wrapping around when we get to the maximum index. + if (cache_last_index == ARRAY_LENGTH(cache_tab) - 1) + cache_last_index = 0; + else + ++cache_last_index; + cache_tab[cache_last_index] = i; + break; + } + } + + return (i == (int)ARRAY_LENGTH(event_tab)) ? (char_u *)"Unknown" : (char_u *)event_tab[i].value; } /* @@ -806,12 +872,14 @@ au_event_disable(char *what) { char_u *new_ei; char_u *save_ei; - - save_ei = vim_strsave(p_ei); + size_t p_ei_len; + + p_ei_len = STRLEN(p_ei); + save_ei = vim_strnsave(p_ei, p_ei_len); if (save_ei == NULL) return NULL; - new_ei = vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what)); + new_ei = vim_strnsave(p_ei, p_ei_len + STRLEN(what)); if (new_ei == NULL) { vim_free(save_ei); @@ -2774,6 +2842,8 @@ set_context_in_autocmd( char_u * get_event_name(expand_T *xp UNUSED, int idx) { + int i; + if (idx < augroups.ga_len) // First list group names, if wanted { if (!include_groups || AUGROUP_NAME(idx) == NULL @@ -2781,7 +2851,12 @@ get_event_name(expand_T *xp UNUSED, int return (char_u *)""; // skip deleted entries return AUGROUP_NAME(idx); // return a name } - return (char_u *)event_names[idx - augroups.ga_len].name; + + i = idx - augroups.ga_len; + if (i < 0 || i >= (int)ARRAY_LENGTH(event_tab)) + return NULL; + + return (char_u *)event_tab[i].value; } /* @@ -2791,7 +2866,10 @@ get_event_name(expand_T *xp UNUSED, int char_u * get_event_name_no_group(expand_T *xp UNUSED, int idx) { - return (char_u *)event_names[idx].name; + if (idx < 0 || idx >= (int)ARRAY_LENGTH(event_tab)) + return NULL; + + return (char_u *)event_tab[idx].value; } @@ -3251,8 +3329,6 @@ f_autocmd_get(typval_T *argvars, typval_ // 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, "event", TRUE); if (name == NULL) return; @@ -3261,16 +3337,20 @@ f_autocmd_get(typval_T *argvars, typval_ 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) + keyvalue_T target; + keyvalue_T *entry; + + target.key = 0; + target.value = (char *)name; + target.length = (int)STRLEN(target.value); + entry = (keyvalue_T *)bsearch(&target, &event_tab, ARRAY_LENGTH(event_tab), sizeof(event_tab[0]), cmp_keyvalue_value_ni); + if (entry == NULL) { semsg(_(e_no_such_event_str), name); vim_free(name); return; } - event_arg = event_names[i].event; + event_arg = (event_T)entry->key; } vim_free(name); } diff --git a/src/misc2.c b/src/misc2.c --- a/src/misc2.c +++ b/src/misc2.c @@ -3069,3 +3069,45 @@ get_special_pty_type(void) return 0; #endif } + +// compare two keyvalue_T structs by case sensitive value + int +cmp_keyvalue_value(const void *a, const void *b) +{ + keyvalue_T *kv1 = (keyvalue_T *)a; + keyvalue_T *kv2 = (keyvalue_T *)b; + + return STRCMP(kv1->value, kv2->value); +} + +// compare two keyvalue_T structs by value with length + int +cmp_keyvalue_value_n(const void *a, const void *b) +{ + keyvalue_T *kv1 = (keyvalue_T *)a; + keyvalue_T *kv2 = (keyvalue_T *)b; + + return STRNCMP(kv1->value, kv2->value, MAX(kv1->length, kv2->length)); +} + +// compare two keyvalue_T structs by case insensitive value + int +cmp_keyvalue_value_i(const void *a, const void *b) +{ + keyvalue_T *kv1 = (keyvalue_T *)a; + keyvalue_T *kv2 = (keyvalue_T *)b; + + return STRICMP(kv1->value, kv2->value); +} + +// compare two keyvalue_T structs by case insensitive value +// with length + int +cmp_keyvalue_value_ni(const void *a, const void *b) +{ + keyvalue_T *kv1 = (keyvalue_T *)a; + keyvalue_T *kv2 = (keyvalue_T *)b; + + return STRNICMP(kv1->value, kv2->value, MAX(kv1->length, kv2->length)); +} + diff --git a/src/proto/misc2.pro b/src/proto/misc2.pro --- a/src/proto/misc2.pro +++ b/src/proto/misc2.pro @@ -61,4 +61,8 @@ int mch_parse_cmd(char_u *cmd, int use_s int build_argv_from_string(char_u *cmd, char ***argv, int *argc); int build_argv_from_list(list_T *l, char ***argv, int *argc); int get_special_pty_type(void); +int cmp_keyvalue_value(const void *a, const void *b); +int cmp_keyvalue_value_n(const void *a, const void *b); +int cmp_keyvalue_value_i(const void *a, const void *b); +int cmp_keyvalue_value_ni(const void *a, const void *b); /* vim: set ft=c : */ diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -5060,3 +5060,18 @@ typedef struct { linenr_T spv_capcol_lnum; // line number for "cap_col" #endif } spellvars_T; + +// Return the length of a string literal +#define STRLEN_LITERAL(s) (sizeof(s) - 1) + +// Store a key/value pair +typedef struct +{ + int key; // the key + char *value; // the value string + size_t length; // length of the value string +} keyvalue_T; + +#define KEYVALUE_ENTRY(k, v) \ + {(k), (v), STRLEN_LITERAL(v)} + diff --git a/src/testdir/test_codestyle.vim b/src/testdir/test_codestyle.vim --- a/src/testdir/test_codestyle.vim +++ b/src/testdir/test_codestyle.vim @@ -7,13 +7,17 @@ def s:ReportError(fname: string, lnum: n enddef def s:PerformCheck(fname: string, pattern: string, msg: string, skip: string) + var prev_lnum = 1 var lnum = 1 while (lnum > 0) cursor(lnum, 1) lnum = search(pattern, 'W', 0, 0, skip) - ReportError(fname, lnum, msg) + if (prev_lnum == lnum) + break + endif + prev_lnum = lnum if (lnum > 0) - lnum += 1 + ReportError(fname, lnum, msg) endif endwhile enddef diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -705,6 +705,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 256, +/**/ 255, /**/ 254,