Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

VIM - Hotkey for VIMGREP on Visual Selection in current buffer

How would I go about setting up a hotkey (eg: CTRL+g) to perform a VIMGREP operation on the current visual selection in the current buffer? My intent is to show a line-numbered list in the "quickfix" window of all matching search results.

Right now, if I want to get a list of results for a regex search, I could do a command-mode query like so:

:vimgrep /foo/ %

However, there are two problems with this:

  1. I don't want to have to type out the entire query. I could always do a visual selection, then use CTRL+r, CTRL+w, to paste the current visual selection into the command buffer, but I'd like something simpler than this.
  2. The above approach requires that the current buffer is already saved to a file. I'd like to be able to work on a temporary buffer I've pasted into VIM rather than having to save a file buffer each time I want to do this.

Thank you.

like image 928
Cloud Avatar asked Feb 11 '26 14:02

Cloud


1 Answers

A low-level solution

Try [I and the :ilist command:

[I                 " lists every occurrence of the word under the cursor
                   " in the current buffer (and includes)

:ilist /foo<CR>    " lists every occurrence of foo in the current buffer 
                   " (and includes)

Press : followed by a line number and <CR> to jump to that line.

You can use them on the visual selection with a simple mapping:

xnoremap <key> "vy:<C-u>ilist /<C-r>v<CR>:

You'll probably need to sanitize the register upon insertion, though.

See :help :ilist.

Another even lower-level solution

Since we are at it, let's dig even deeper and find the amazingly simple and elegant:

:g/foo/#

that you could use in the same way as :ilist above:

xnoremap <key> "vy:<C-u>g/<C-r>v/#<CR>:

Limitations

The solutions above don't use the quickfix window, obviously, but they allow you to:

  • see their result as a list,
  • use line numbers to actually get to where you want.

They have limitations, though:

  • the list is not cached so you must perform the search again if you want to get to a different occurrence,
  • the list is not transient like the quickfix list so you can't use navigation commands like :cnext or :clast to move around the result.

A higher-level solution

If those limitations are a showstopper, the function below, adapted from justinmk's answer in this /r/vim thread, gives you an almost complete solution:

  • press [I in normal mode to search for the word under the cursor in the whole buffer,
  • press ]I in normal mode to search for the word under the cursor after the current line,
  • press [I in visual mode to search for the selected text in the whole buffer,
  • press ]I in visual mode to search for the selected text after the current line.

The function below uses the quickfix list/window when the buffer is associated to a file and falls back to the regular behavior of [I and ]I otherwise. It could probably be modified to be used as part of an :Ilist command.

" Show ]I and [I results in the quickfix window.
" See :help include-search.
function! Ilist_qf(selection, start_at_cursor)

    " there's a file associated with this buffer
    if len(expand('%')) > 0

        " we are working with visually selected text
        if a:selection

            " we build a clean search pattern from the visual selection
            let old_reg = @v
            normal! gv"vy
            let search_pattern = substitute(escape(@v, '\/.*$^~[]'), '\\n', '\\n', 'g')
            let @v = old_reg

            " and we redirect the output of our command for later use
            redir => output
                silent! execute (a:start_at_cursor ? '+,$' : '') . 'ilist /' . search_pattern
            redir END

        " we are working with the word under the cursor
        else

            " we redirect the output of our command for later use
            redir => output
                silent! execute 'normal! ' . (a:start_at_cursor ? ']' : '[') . "I"
            redir END
        endif
        let lines = split(output, '\n')

        " better safe than sorry
        if lines[0] =~ '^Error detected'
            echomsg 'Could not find "' . (a:selection ? search_pattern : expand("<cword>")) . '".'
            return
        endif

        " we retrieve the filename
        let [filename, line_info] = [lines[0], lines[1:-1]]

        " we turn the :ilist output into a quickfix dictionary
        let qf_entries = map(line_info, "{
                    \ 'filename': filename,
                    \ 'lnum': split(v:val)[1],
                    \ 'text': getline(split(v:val)[1])
                    \ }")
        call setqflist(qf_entries)

        " and we finally open the quickfix window if there's something to show
        cwindow

    " there's no file associated with this buffer
    else

        " we are working with visually selected text
        if a:selection

            " we build a clean search pattern from the visual selection
            let old_reg = @v
            normal! gv"vy
            let search_pattern = substitute(escape(@v, '\/.*$^~[]'), '\\n', '\\n', 'g')
            let @v = old_reg

            " and we try to perform the search
            try
                execute (a:start_at_cursor ? '+,$' : '') . 'ilist /' .  search_pattern . '<CR>:'
            catch
                echomsg 'Could not find "' . search_pattern . '".'
                return
            endtry

        " we are working with the word under the cursor
        else

            " we try to perform the search
            try
                execute 'normal! ' . (a:start_at_cursor ? ']' : '[') . "I"
            catch
                echomsg 'Could not find "' . expand("<cword>") . '".'
                return
            endtry
        endif
    endif
endfunction

nnoremap <silent> [I :call Ilist_qf(0, 0)<CR>
nnoremap <silent> ]I :call Ilist_qf(0, 1)<CR>
xnoremap <silent> [I :<C-u>call Ilist_qf(1, 0)<CR>
xnoremap <silent> ]I :<C-u>call Ilist_qf(1, 1)<CR>

NB: <C-r><C-w> inserts the word under the cursor, not the visual selection for which there's unfortunately no such shortcut. We have no choice but to yank.

like image 156
romainl Avatar answered Feb 13 '26 09:02

romainl