0

To my .vimrc I added a hook which automates entering the quickfix after :grep processing:

autocmd QuickFixCmdPost [^l]* nested cwindow 20

Another thing I care about but can't achieve is automation of applying highlight/color of all found occurrences while being in the quickfix. What I observed, it's really easy to do manually:

/pattern

by re-typing the same pattern which went to the :grep we will get all occurrences highlighted in all of the files referenced by the quickfix.

Is it possible to program it so that vim will know the grep's matches (or at least the pattern passed) and automatically perform the /pattern search command after :cwindow is executed - or perform any other method to apply that highlight?


UPDATE

Assuming we can deliver a custom command for :grep in .vimrc, like:

command! -nargs=+ G execute "grep -rIn <args> ."

now the pattern itself is provided by the means of a custom command which might be helpful(?).

I admit I'm not a good vim programmer. Can it all be somehow chained together (as another autocmd/function and preserving the effect of cwindow hook) to get that highlight to be applied?

1
  • This won't be easy, as the regex expressions of grep are different from Vims.
    – Ralf
    May 2, 2020 at 6:11

3 Answers 3

2

Option 1 Hack

A hack... you could get the title of the quickfix window, and then strip off the parts that arent the search term:

    let pattern = getqflist({'title' : 0}).title
    let pattern = strip_off_not_search_parts(pattern)

And then add a match with:

    call clearmatches()
    silent call matchadd('Search', pattern) " Hi group / pattern

I have found it hard to get the search highlighting as if you typed it, you could try feedkeys() but I had little joy with it.

Option 2 Wrapper

You are going to want to improve it as time passes by :) I recommend vim the hardway, and :help input

function! GrepWithHighlight()
    let searchString = input("Input search string: ")
    if searchString == '' || searchString == '\n'
        echom "Aborted Search."
        return
    endif
    exe 'vimgrep /'.searchString.'/ **/*'
    " Open the quickfix window first so match applies to it
    copen
    call clearmatches()
    silent call matchadd('Search', searchString) " Hi group / pattern
endfunction
noremap <leader>v :call GrepWithHighlight()<Cr>
4
  • Thanks. And how to chain it all together with my hook to get these instructions executed (I'm not good at vim programming I admit)? Does this involve another autocmd/function? I'd appreciate some minimal aggregated example.
    – bloody
    May 2, 2020 at 8:10
  • Please also look at my question's update. Perhaps we don't have to "hack" it from the quickfix title. Can it be somehow crafted taking advantage of input at higher level?
    – bloody
    May 2, 2020 at 8:42
  • It was late, my wife was calling me to bed, hence i didnt flesh out the 1st option completely last night, and was such a hack didnt seem worth completing. If you okay with a wrapper, see option2 I added. May 2, 2020 at 9:04
  • Your function works perfectly. I let myself inject grep instead of vimgrep after all because it's much faster and the highlight works equally fine as well. Thank you very much!
    – bloody
    May 2, 2020 at 14:45
1

After experiments I have observed that matchadd() proposed by run_the_race highlights occurrences only in the quickfix window but not in the window with file buffers which would be even more desirable. We could apply matchadd() twice (before copen and after) but this method has side-effects while applying to files anyway (e.g. clearmatches() also clears existing custom matches).

For above reasons I tried to replace matchadd() with regular search highlight (managed by hlsearch and the search register) but this is impossible to achieve within a function, what :h function-search-undo clearly states. Therefore I figured out slightly different solution (seems even simpler by occasion):

autocmd QuickFixCmdPost * nested cwindow 20 | redraw!
command! -nargs=+ Grep execute "let @/ = '<args>' | set hlsearch | silent grep -rI <args> ."
noremap <leader>g :Grep<space>

redraw! prevents weird artifacts when drawing quickfix over quickfix or grep returns no results. let @/ = and hl search take care about populating the search register and triggering the highlight itself. silent avoids a need of pressing Enter after shell processing. Of course one can add custom "must be" options between grep and <args> strings, like:

--exclude=tags --exclude-dir=build --exclude-dir=.git

It's most ergonomic and robust highlighted grep I could reach (keeping in mind that some REGEX inconsistencies between grep and vim may diverge the highlight effect, for that one can use vimgrep which is unacceptably slow).

5
  • 1
    Glad you found a solution you are happy with.. "matchadd()... highlights occurrences only in file buffers but not in quickfix items": I use matchadd to highlight the quickfix window, so I don't quite understand? (I use it in a wrapper around ripgrep). A benefits of using matchadd is as one works through the QF entries, one may be doing incsearches, but with matchadd the search terms remain highlighted in the QF window (matchadd is window local, and hence clearmatches() only changed the QF window). One can use :cn and :cp (or vim-unimpaired [q or ]q) to jump between QF matches. May 4, 2020 at 21:26
  • @run_the_race [1] You're right, I'm sorry for inaccuracy, I have edited my answer to make it truthful (I tested different variations and I accidentally referred not to your original code). The thing is that I cared both about "in-files" highlight and in quickfix what I expressed in my question by "(...) we will get all occurrences highlighted in all of the files referenced by the quickfix".
    – bloody
    May 5, 2020 at 9:04
  • 1
    @run_the_race [2] And a few versions of matchadd which I tried ultimately proved less likeable (it would be then even cool to may use n/N in files, although I perfectly understand your assumption with "only quickfix highlight" - and then have it independent from hlsearch - but not quite in aggregated solution if you know what I mean + this "global" clearmatches issue). Anyway I appreciate your answer not less, it was a driving force behind. The upvote fully deserved.
    – bloody
    May 5, 2020 at 9:16
  • I like you idea about incsearch too, so will incorporate both ways (hlsearch and matchadd) into my search, giving what I feel the best of both. May 6, 2020 at 13:09
  • @run_the_race Oh, I haven't even thought about this. I must check this combination out too:) Thanks.
    – bloody
    May 7, 2020 at 17:02
0

I wrote a wrapper for :grep. I'm using ripgrep as grep replacement. The wrapper automatically opens the quickfix window and highlights the matches. Whether it's a single word or a "string with spaces"

" :Grep "Peding Transaction"               " with fixed strings
" :Grep! "\sTransaction"                   " regex
" :Grep \$stateParam
" :Grep "Pending Transaction" -g "*.php"   " with rg parameters
" :Grep "Pending Transaction" vendor/      " with path
" :Grep "Pending Transaction" -g "*.php" vendor   " with rg parameters and path

" rg as :grep
set grepprg=rg\ --vimgrep\ --follow\ --hidden
set grepformat^=%f:%l:%c:%m

hi QuickFixMatch guifg=#839e00

function! s:Grep(isFixed, args) abort
    let fixedArg = a:isFixed ? '' : '-F'
    let argArray = split(a:args)
    let restArgs = ''

    let quotedStringPattern = '^\".\{-}\"'
    let pattern = matchstr(a:args, quotedStringPattern) " check for first pattern with space in quotes

    if !empty(pattern)
        let matchPattern = substitute(pattern, '"', '', 'g')
        let restArgs = substitute(a:args, quotedStringPattern, '', '')
    else
        let pattern = argArray[0]
        let matchPattern = pattern
        let restArgs = join(argArray[1:], ' ')
    endif

    execute 'silent! grep! '. fixedArg . ' ' . pattern . ' ' . restArgs . ' | cwindow 20 | redraw! | match QuickFixMatch /' . matchPattern . '/'
endfunction

command! -nargs=+ -complete=file -bang Grep silent! call s:Grep(<bang>0, <q-args>)

https://gist.github.com/dr0bz/2932b41ee57b8dfeca161e77b2572354

Your Answer

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

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