Copy the search results into clipboardedit this page

From Vim Tips Wiki

(Redirected from VimTip478)
Tip 478 Previous Next

created 2003 · complexity basic · author JAS · version 7.0


Several methods allow you to capture all search hits. Usually, you want to copy each line which contains text matching your search pattern, but you can also copy just the matching text. We start with methods to easily list search hits, without copying them.

Contents

[show]

edit Search within a file

To search, press / then type what you want to find, or press * to search for the current word.

To list all lines matching your search pattern, use the following. The first command lists lines matching your last search. The second lists lines matching pattern.

:g/
:g/pattern

On some systems, at the "Press ENTER or type command to continue" prompt, you can select displayed text with the mouse then press Ctrl-Y to copy the selection to the clipboard. :help press-enter

To view a window of search results, see Find in files within Vim. You can use % for the file path to search only the current file, for example:

" Save file, search it for 'pattern', and open a clickable list.
:w
:vimgrep /pattern/ %
:copen

To view only the parts of a file that match your pattern, see Folding with Regular Expression. You can fold away non-matching lines, and use zr to reveal more context around the search hits.

edit Copying lines containing search hits

You can copy matching lines to the clipboard, so they can be pasted into another application.

To copy all lines containing a search pattern, use the following (see Power of g). The first command clears register a (:help q). The second appends all matching lines to that register (:help quotea). The third copies register a to the clipboard (register +) for easy pasting into another application. Replace pattern with what you want to search for.

qaq
:g/pattern/y A
:let @+ = @a

The above procedure captures only the first line of each match. If your pattern matches more than one line, use the following.

" Use /pattern to search for something, then
"   :call CopyMatchingLines()
" to copy all lines containing hits (whole lines).
" The pattern may extend over multiple lines.
" The 'normal! $' attempts to avoid copying the same line more than once.
" BUG: For some patterns, it could miss a second hit?
function! CopyMatchingLines()
  let posinit = getpos(".")
  call cursor(1, 1)
  let cnt = 0
  let hits = []
  let snum = search(@/, 'cW')
  while snum > 0
    let enum = search(@/, 'ceW')
    call extend(hits, getline(snum, enum))
    let cnt += 1
    normal! $
    let snum = search(@/, 'W')
  endwhile
  if cnt > 0
    let @+ = join(hits, "\n") . "\n"
  endif
  call cursor(posinit[1], posinit[2])
  echomsg cnt 'lines (or blocks) were appended to the clipboard.'
endfunction

edit Copying only the matching text

A search pattern may use a regular expression. For example, the following finds all words that begin with 'a':

/\<a\w*\>

The following procedure allows you to copy just the text which matches a pattern. Source the CopyMatches command and function, then perform the search above. To copy all matching text to the clipboard, enter the following.

:CopyMatches

The command can also copy matches from a range of lines, and can copy into a register other than the clipboard. For example, the first of the following copies all matches in lines 1 to 10 inclusive to register a, and the second copies matches from the current line (.) to the default register ("):

:1,10CopyMatches a
:.CopyMatches "

The script includes let end += 1 to handle searches with a pattern like .\{-}\ze, which matches characters (as few as possible) up to, but not including, a comma. It is necessary to increment end to get past the comma.

" Copy matches of the last search to a register (default is the clipboard).
" Accepts a range (default is whole file).
" 'CopyMatches'   copy matches to clipboard (each match has \n added).
" 'CopyMatches x' copy matches to register x (clears register first).
" 'CopyMatches X' append matches to register x.
" We skip empty hits to ensure patterns using '\ze' don't loop forever.
command! -range=% -register CopyMatches call s:CopyMatches(<line1>, <line2>, '<reg>')
function! s:CopyMatches(line1, line2, reg)
  let hits = []
  for line in range(a:line1, a:line2)
    let txt = getline(line)
    let idx = match(txt, @/)
    while idx >= 0
      let end = matchend(txt, @/, idx)
      if end > idx
	call add(hits, strpart(txt, idx, end-idx))
      else
	let end += 1
      endif
      if @/[0] == '^'
        break  " to avoid false hits
      endif
      let idx = match(txt, @/, end)
    endwhile
  endfor
  if len(hits) > 0
    let reg = empty(a:reg) ? '+' : a:reg
    execute 'let @'.reg.' = join(hits, "\n") . "\n"'
  else
    echo 'No hits'
  endif
endfunction

The following alternative is to copy matches which extend over more than one line.

" Use 0"+y0 to clear the clipboard, then
"    :g/pattern/call CopyMultiMatches()
" to copy all multiline hits (just the matching text).
" This is for when the match extends over multiple lines.
" Only the first match from each line is found.
" BUG: When searching for "^function.*\_s*let" the '.*' stops at the end
" of a line, but it greedily skips "\n" in the following (we copy too much).
function! CopyMultiMatches()
  let text = join(getline(".", "$"), "\n") . "\n"
  let @+ .= matchstr(text, @/) . "\n"
endfunction

edit See also

edit Comments

 TO DO 

  • The code for multiline matching needs fixing (see 'BUG').