Is there any way to store the number of matches in a variable in a VIMScript function?
For instance I'm using:
%s/,//gn
Is there any way to store the number of matches in a variable in a VIMScript function?
For instance I'm using:
%s/,//gn
I know that @romainl debugged the question as a XY problem in the comments but I guess it still could be useful to some people, so here is a solution. (It is deeply inspired from this answer)
You can use this function (to add to your .vimrc or to your script):
function! Count( word )
redir => cnt
silent exe '%s/' . a:word . '//gn'
redir END
let res = strpart(cnt, 0, stridx(cnt, " "))
return res
endfunction
You can call the function like this: :call Count("pattern").
For example in this file:
a,bc
akj,dh
jlkdfa,
oiua ,lkj
oiua, lkj
oiua , lkj
i,
With :echo Count(",") you'll get 7.
^@ in-front of the result, so can't really be used. e.g. echo Count("word") + 1 will always return 1. See my amended answer below.
– Orwellophile
Jul 18 '19 at 12:06
Warning: The accepted answer does not return a number or number-like object. This does.
function! Count( word )
redir => cnt
silent exe '%s/' . a:word . '//n'
redir END
return matchstr( cnt, '\d\+' )
endfunction
With the same instructions as the accepted answer, except you can actually perform further calculations with the result, e.g.:
echo Count("word") + 2
or something superbly more complex:
function! MyMatch()
let results = ingo#text#frompattern#Get(1, '$', 'j\w\{1,5\} \zs\(loc\|lab\)_\w\+\ze', '', '', '1')
let counter=1
for result in results
if Count(result) != 2
execute '%s/' . result . '/label' . counter . '/g'
let counter+=1
endif
endfor
endfun
There is another way that doesn't move the cursor nor requires :exe, but uses other tricks. Here I count by using a function that modifies and returns something (if += was an expression like in C, it would have been simpler)
command! -nargs=1 -range Count call s:Count2(<f-args>, <line1>,<line2>)
" Not using the usual :function-range to avoid moving the cursor
function! s:Count2(param, firstl, lastl) abort
let s:c = []
call map(getline(a:firstl, a:lastl), { k,v -> substitute(v, 'line', '\=add(s:c, v)[-1]', 'g')})
echo len(s:c)
endfunction
let l:c, no reason to leak it to the script namespace.
– D. Ben Knoble
Jul 18 '19 at 15:48
call map(getline(a:firstl, a:lastl), "substitute(v:val, 'line', '\\=add(l:c, v:val)[-1]', 'g')"), and I don't even need to add this noisy l: prefix.
– Luc Hermitte
Jul 18 '19 at 16:06
If you have a new enough version of vim which suport :h execute() , you can get count like this:
function! GetCount(pattern)
let l:cnt = 0
silent exe '%s/' . a:pattern . '/\=execute(''let l:cnt += 1'')/gn'
return l:cnt
endfunction
In fact you can get all the matches and put them in a list:
function! GetMatches(pattern)
let l:matches = []
silent exe '%s/' . a:pattern .
\ '/\=execute(''let l:matches += [submatch(0)]'')/gn'
return l:matches
endfunction
update
As Christian Brabandt pointed out, you can use :h add() , it's simpler :
function! GetMatches(pattern)
let l:matches = []
silent exe '%s/' . a:pattern . '/\=add(l:matches, submatch(0))/gn'
return l:matches
endfunction
\=add(matches, submatch(0))/gn, don't need execute() for that.
– Christian Brabandt
Jul 18 '19 at 14:43
:s/,/,\r/g? – romainl Mar 22 '16 at 14:42%s/,\W*/,\r/gbecause of white space. – leeand00 Mar 22 '16 at 14:49\s,\_sif you want to match newlines as well. – Luc Hermitte Mar 22 '16 at 16:09\rto represent EOL in the replacement (and\nin the search) regardless of what line ending is in effect for the buffer. The type of line ending is controlled by the'fileformat'option. – jamessan Mar 23 '16 at 00:42