4

I would like to use search in file with folded functions to search only in functions titles. But common vim search searches inside folding and unfolds any hidden text. Is there a way to search only in open text ignoring the rest?

1
  • 2
    If you :set foldopen-=search, then Vim will still search within the folds, but at least it won't unfold them.
    – Rich
    Feb 14, 2020 at 14:52

2 Answers 2

3

You could use :folddoopen to capture the addresses of the lines which are not in open folds, then build a sub-expression describing those lines (see :h \%l, :h \%>l, :h \%<l).

As an example:

cno <c-o> <c-r>=<sid>limit_search_to_open_folds(1)<cr><c-c>
fu s:limit_search_to_open_folds(step) abort
    if a:step == 1
        let s:pat = getcmdline()
        call timer_start(0, {-> s:limit_search_to_open_folds(2)})
    else
        let s:pat = get(s:, 'pat', '')..'\m\%('
        folddoopen let s:pat ..= '\%'..line('.')..'l\|'
        let s:pat = substitute(s:pat, '\\|$', '\\)', '')
        call feedkeys('/'..s:pat, 'in')
    endif
    return ''
endfu

Write your pattern, then press C-o. It should append a sub-expression which limits the search to the lines which are not in closed folds.


If the sub-expression is too long, try this:

cno <c-o> <c-r>=<sid>limit_search_to_open_folds(1)<cr><c-c>
fu s:limit_search_to_open_folds(step) abort
    if a:step == 1
        let s:cmdline = getcmdline()
        call timer_start(0, {-> s:limit_search_to_open_folds(2)})
    else
        let lnums = []
        folddoopen let lnums += [line('.')]
        let ranges = []
        for i in range(len(lnums))
            if i > 0 && lnums[i] == lnums[i-1] + 1
                if type(ranges[-1]) == type(0)
                    let ranges[-1] = [ranges[-1], ranges[-1]+1]
                else
                    let ranges[-1][1] += 1
                endif
            else
                let ranges += [lnums[i]]
            endif
        endfor
        call map(ranges, {_,v -> type(v) == type(0) ? '\%'..v..'l' : '\%>'..(v[0]-1)..'l\%<'..(v[1]+1)..'l'})
        let pat = get(s:, 'cmdline', '')
            \ ..'\m\%('
            \ ..join(ranges, '\|')
            \ ..'\)'
        call feedkeys('/'..pat, 'in')
    endif
    return ''
endfu
3

While this solution is not as powerful as @user938271's one, it is far more simple and I find it sufficient for basic navigation:

:set fdo-=search

Setting this will make your search only find at most one result by fold and prevents unfolding them even when moving to them by cycling through search results with n/N, while working normally for unfolded text. This effectively make you find results in unfolded text quickly by being minimally bothered by folded text, and still getting the info of folds that contain the search pattern, secondarily.

New contributor
adamency is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.