Skip to main content
10 of 14
added 61 characters in body
user938271
  • 6k
  • 1
  • 16
  • 24

If your Vim binary includes the patch 8.2.0877, you can get the search statistics via the searchcount() function. And the latter is not limited to 99 matches.

You can invoke it right after a / search with a CmdlineLeave autocmd, and right after a n motion by installing a wrapper mapping around the latter.

Example:

const s:MAXCOUNT = 1000
const s:TIMEOUT = 500

augroup index_after_slash | au!
    au CmdlineLeave /,\? call s:index_after_slash()
augroup END

fu s:index_after_slash() abort
    if getcmdline() is# '' || state() =~# 'm'
        return
    endif
    call timer_start(0, {-> mode() =~# '[nv]' ? s:search_index() : 0})
endfu

fu s:search_index() abort
    try
        let result = searchcount(#{maxcount: s:MAXCOUNT, timeout: s:TIMEOUT})
        let [current, total, incomplete] = [result.current, result.total, result.incomplete]
    catch
        echohl ErrorMsg | echom v:exception | echohl NONE
        return ''
    endtry
    let msg = ''
    let pat = substitute(@/, '\%x00', '^@', 'g')
    if incomplete == 0
        let msg = printf('[%*d/%d] %s', len(total), current, total, pat)
    elseif incomplete == 1 " recomputing took too much time
        let msg = printf('[?/??] '..%s', pat)
    elseif incomplete == 2 " too many matches
        if result.total == (result.maxcount+1) && result.current <= result.maxcount
            let msg = printf('[%*d/>%d] %s', len(total-1), current, total-1, pat)
        else
            let msg = printf('[>%*d/>%d] %s', len(total-1), current-1, total-1, pat)
        endif
    endif
    if strchars(msg, 1) > (v:echospace + (&cmdheight-1)*&columns)
        let n = v:echospace - 3
        let [n1, n2] = n%2 ? [n/2, n/2] : [n/2-1, n/2]
        let msg = matchlist(msg, '\(.\{'..n1..'}\).*\(.\{'..n2..'}\)')[1:2]->join('...')
    endif
    echo msg
    return ''
endfu

nmap n <plug>(n)<plug>(search_index)
nmap N <plug>(N)<plug>(search_index)
nno <plug>(n) n
nno <plug>(N) N
nno <expr> <plug>(search_index) <sid>search_index()

searchcount() won't show a total amount of matches greater than 1000. If that's not enough, increase s:MAXCOUNT. And it will stop trying to compute the number of matches after half-a-second. If that's too long, decrease s:TIMEOUT.

Note that increasing s:MAXCOUNT and s:TIMEOUT may have a negative impact on Vim's performance. The values used in the previous snippet work for me; they may or may not work for you. I guess it depends on the machine you're using and/or on the patterns you're usually looking for. Take that into consideration before setting these parameters.

You can improve the performance by rewriting the code in Vim9 script:

vim9script

const MAXCOUNT = 1000
const TIMEOUT = 500

augroup index_after_slash | au!
    au CmdlineLeave /,\? IndexAfterSlash()
augroup END

def IndexAfterSlash()
    if getcmdline() == '' || state() =~# 'm'
        return
    endif
    timer_start(0, {-> mode() =~# '[nv]' ? SearchIndex() : 0})
enddef

def SearchIndex(): string
    var incomplete: number
    var total: number
    var current: number
    var result: dict<any>
    try
        result = searchcount({'maxcount': MAXCOUNT, 'timeout': TIMEOUT})
        current = result.current
        total = result.total
        incomplete = result.incomplete
    catch
        echohl ErrorMsg | echom v:exception | echohl NONE
        return ''
    endtry
    var msg = ''
    var pat = getreg('/')->substitute('\%x00', '^@', 'g')
    if incomplete == 0
        msg = printf('[%*d/%d] %s', len(total), current, total, pat)
    elseif incomplete == 1 # recomputing took too much time
        msg = printf('[?/??] %s', pat)
    elseif incomplete == 2 # too many matches
        if result.total == (result.maxcount + 1) && result.current <= result.maxcount
            msg = printf('[%*d/>%d] %s', len(total - 1), current, total - 1, pat)
        else
            msg = printf('[>%*d/>%d] %s', len(total - 1), current - 1, total - 1, pat)
        endif
    endif
    if strchars(msg, 1) > (v:echospace + (&cmdheight - 1) * &columns)
        var n = v:echospace - 3
        var n1 = n % 2 ? n / 2 : n / 2 - 1
        var n2 = n / 2
        var matchlist = matchlist(msg, '\(.\{' .. n1 .. '}\).*\(.\{' .. n2 .. '}\)')
        msg = matchlist[1] .. '...' .. matchlist[2]
    endif
    echo msg
    return ''
enddef

nmap n <plug>(n)<plug>(search_index)
nmap N <plug>(N)<plug>(search_index)
nno <plug>(n) n
nno <plug>(N) N
nno <expr> <plug>(search_index) <sid>SearchIndex()

This requires a recent Vim version. It works on 8.2.1788.


For more info, see:

user938271
  • 6k
  • 1
  • 16
  • 24