I want to catch all linenumbers in the buffer with 1 or more matches.
Till now I used this code:
let lines = []
for line in range(startline, endline)
let @e = ''
:redir @e
:silent exe line.'s/'.mysearch.'/&/gne'
:redir END
if matchstr(@e, 'match') != ''
let nummer = matchstr(@e, '\d\+\ze')
if nummer > 0
call add(lines, ''.line.'')
endif
endif
endfor
But this is incredible slow.
Is there no way to do this faster?
(p.e. execute search silently: 1 for match and catch the linenumber and 0 for no match)
I would use filter()
on getline(1,'$')
. To obtain line numbers instead we could play with map()
and the new (Vim 8) lambdas. Or may be simply filter (range (1, line ('$')), 'getline (v:val) =~ regex')
You will hardly find anything faster with vim, especially manual loops.
EDIT:
For instance, searching "home"
on a 7850 lines long vim log file where it occurs 6438 times:
echo lh#time#bench('filter', range (1, line ('$')), 'getline (v:val) =~ "home"')
takes 0.033297s:echo lh#time#bench('filter', range (1, line ('$')),{ idx, val -> getline (val) =~ 'home'})[1]
takes 0.078506s -- a little bit slower, but the regex is simplerlet r=[]
+ echo = lh#time#bench('map', range(1, line('$')), 'substitute(getline(v:val), "home", "\\=add(g:r, v:val)", "")')[1]
takes 8.841528s -- I guess this is the kind of result you have with a manual loop:let start=reltime()|:let lines=filter(map(getline(1, '$'), {idx, val -> val =~ "home"?idx:0}), "v:val>0")|:echo reltimefloat(reltime(start, reltime()))
takes: 0.075132sBTW, I've also measured on the previous snippets, on my vim 8.0-134, that match()
is a little bit (not really perceptible) slower than plain old =~
. Don't ask me why.
IOW, cryptic regexes seems to be the fastest solution, and manual loops shall be avoided at all cost when performances matter.