CtrlP is a popular Vim plugin for jumping to files & buffers by typing a fragment of the path or filename. Its fuzzy-find is a little too fuzzy for my taste. We can fix that with a custom match function!

Per CtrlP documentation:

Example: 

    let g:ctrlp_match_func = { 'match': 'Function_Name' }

Structure of the function: 

    function! Function_Name(items, str, limit, mmode, ispath, crfile, regex)

        " Arguments:
        " |
        " +- a:items  : The full list of items to search in.
        " |
        " +- a:str    : The string entered by the user.
        " |
        " +- a:limit  : The max height of the match window. Can be used to limit
        " |             the number of items to return.
        " |
        " +- a:mmode  : The match mode. Can be one of these strings:
        " |             + "full-line": match the entire line.
        " |             + "filename-only": match only the filename.
        " |             + "first-non-tab": match until the first tab char.
        " |             + "until-last-tab": match until the last tab char.
        " |
        " +- a:ispath : Is 1 when searching in file, buffer, mru, mixed, dir, and
        " |             rtscript modes. Is 0 otherwise.
        " |
        " +- a:crfile : The file in the current window. Should be excluded from the
        " |             results when a:ispath == 1.
        " |
        " +- a:regex  : In regex mode: 1 or 0.

        return list_of_matched_items

    endfunction

So, I’m going to replace the default fuzzy-matcher with a dumb grep. Notice that I’m disabling regexes with the --fixed-strings flag. First, we’ll define the function:

function! MyCtrlpMatch(lItems, szSearch, nHeight, szMode, bIsPath, szCurFile, bRegex)

    " EARLY EXIT ON EMPTY SEARCH STRING
    if strlen(a:szSearch) > 0

        " SYSTEM `grep` SEEMS TO BE FAR FASTER THAN BUILT-INS
        let szCmd = 'grep --ignore-case --fixed-strings ' . shellescape(a:szSearch)
        let lRet = systemlist(szCmd, join(a:lItems, "\n"))

        " CAP LARGE RESULTS TO 1K ITEMS
        let nResults = len(lRet)
        if nResults > 1000
            return lRet[0:999]
        elseif nResults > 0
            return lRet
        endif

    endif

    return []

endfunction

Now we have to tell CtrlP to use this function instead of the default:

let g:ctrlp_match_func = { 'match': 'MyCtrlpMatch' }

I also recommend enabling lazy updates to debounce keystrokes. No point re-filtering on every single keypress as you type. 300 milliseconds feels right for me, but you may need to experiment a little.

let g:ctrlp_lazy_update = 300

Roll it all up into your vimrc, and let it rip!


← Older Newer →

Comments are closed.