64

Is there a way to hide all lines that did not match, while going through the list of result lines?

In a long file, I would like to search for a pattern that will match roughly 200 lines or so. The matched lines are in random places in the file.
When a line matches, only the line itself is relevant, no context above or below.

Normally, I would search with / and jump through the matches with n.
But that means there are only one or two relevant lines on the screen at a time.

A brute force approach would be to delete all non-matching lines, and undo that deletion later. But that's ugly in various ways, even if it would not end up in persistent undo.

Rob Bednark
  • 371
  • 1
  • 13
Volker Siegel
  • 2,157
  • 2
  • 18
  • 21

7 Answers7

69
:vimgrep pattern %
:cwindow

vimgrep will search for your pattern in the current file (%), or whatever files you specify.

cwindow will then open a buffer in your window that will only show the desired lines. You can use pretty much any navigating/search command within the cwin buffer. Press return to jump to the line under your cursor in the source file.

For help:
:help vimgrep
:help cwindow

Rob Bednark
  • 371
  • 1
  • 13
Zach Ingbretsen
  • 1,313
  • 7
  • 13
  • 2
    :vimgrep can be abbreviated to :vim – D. Ben Knoble Jun 11 '18 at 14:51
  • 1
    how do i get back to the buffer again? – pasha Dec 02 '18 at 12:19
  • also how do i close the buffer? thanks in advance – pasha Dec 02 '18 at 12:23
  • 2
    From the quickfix (cwindow) buffer you can hit return on any line to jump to that location in the original buffer. You can can close the cwindow buffer from anywhere with :ccl[ose] or however you normally close a buffer (e.g., switch to it and :bd). You can reopen the quickfix buffer by running :cw[indow] again, and it will use your last vimgrep results. – Zach Ingbretsen Mar 23 '19 at 12:59
  • :vim YOUR_SEARCH % and :cw, use ctrl_w,ctrl_w to switch between frames – sjas Mar 05 '22 at 14:18
  • The % trick will not work on a [No Name] buffer (E499: Empty file name for % or #). – Arnie97 Mar 22 '23 at 08:51
41

You can list all matching lines with

:g/{pattern}

(The :print command can be omitted; it is the default for :g.)

Ingo Karkat
  • 17,819
  • 1
  • 45
  • 61
  • 3
    This is the "original grep" (g/REgexp/p) ... Problem is that you can't easily jump to one of the matches, though... – Martin Tournoij Feb 28 '15 at 19:44
  • @Carpetsmoker: You're right; for that, I have a plugin, see my other answer. – Ingo Karkat Feb 28 '15 at 20:00
  • That could be useful for many cases. Can the output be redirected to a new buffer? That would allow to still have syntax highlighting on the lines. (Hmm... that raises another question, wait a second...) – Volker Siegel Feb 28 '15 at 20:18
  • @IngoKarkat what is the meaning the g. I assume that it is a global pattern. I usually do VI search with a slash /pattern and this would find all occurrences. One can navigate to them by pressing the letter n – Alexander Cska Jun 13 '19 at 08:23
  • 1
    @AlexanderCska: No, it's not a modifier for search, but a separate command :g[lobal] that takes a pattern as an argument. You'll find all details at :help :global. – Ingo Karkat Jun 13 '19 at 09:41
  • @IngoKarkat is it possible to redirect the result of :g/{pattern} to a file. Say, something like :g\{pattern}\w file – Alexander Cska Nov 11 '23 at 14:20
  • @AlexanderCska Yes, :redir > {file} before and :redir END after the command(s). – Ingo Karkat Nov 13 '23 at 06:13
  • @IngoKarkat thank you. – Alexander Cska Nov 13 '23 at 07:25
26

[d]elete all lines not(!) matching patterns:

:g!/pattern/d

or even simpler (thanks for the comments by 'B Layer'):

:v/pattern/d
Kevin Liu
  • 359
  • 3
  • 5
  • 2
    Yeah, but it's more conventional to use the equivalent :v rather than :g!. (I don't remember the last time I saw the latter used, TBH!) – B Layer Jun 12 '18 at 04:06
  • If I wasn't used to v being "invert" thanks to the grep (shell) command, I'd find :g! much easier to remember than :v. So as a general recommendation I think :g! works better. – Sundar R Oct 10 '21 at 07:43
18

You can use the foldmethod and foldexpr options to fold away irrelevant lines.

After searching, the last search is stored in the @/ register. So, you can readily fold away everything that doesn't match like this (for one-line matches only):

:setlocal foldexpr=getline(v:lnum)=~@/?0:1 foldmethod=expr

More advanced methods, like adding multiple levels of folding for context lines you can show or hide, or matching multiple lines, can be found on the Vim Tips Wiki.

Ben
  • 350
  • 1
  • 8
10

If order doesn't matter then just move the lines.

:g/pat/m0

For more help see:

:h :g
:h :m
Peter Rincker
  • 15,854
  • 1
  • 36
  • 45
3

If you want to list all matching lines (as in my other answer), and then jump to one particular match, my FindOccurrence plugin provides a [/ mapping for that (and [n for the last search pattern, instead of querying for one). [/pattern lists like :g/pattern, but then asks for the number of the match to jump to.

Ingo Karkat
  • 17,819
  • 1
  • 45
  • 61
1

Another way is to use :h :ilist, i use it to get an overview of man page. it's result looks like this:

~/find.~
  1:    3 NAME
  2:    6 SYNOPSIS
  3:    9 DESCRIPTION
  4:   18 OPTIONS
  5:   93 EXPRESSION
  6:  779 EXAMPLES
  7:  877 HISTORY
  8:  931 BUGS

Here is a small function that let you choose to go to the result line by index:

function! s:select_ilist(ilist_result)
  if empty(a:ilist_result) || a:ilist_result =~# '^\_s*Error'
    return
  endif

  let select = input(a:ilist_result . "\ngoto : ")
  if empty(select) | return | endif

  " ilist result starts with '\nfilename\n', filename can be empty
  let lines = split(a:ilist_result[stridx(a:ilist_result, "\n", 1) : ], "\n")
  if select <= 0 || select > len(lines) | return | endif

  exec matchstr(lines[select-1], '^\v\s*\d+\:\s+\zs\d+')
endfunction

It can be used like this:

command! -buffer Section call s:select_ilist(execute('silent! ilist /\v\C^[A-Z][^a-z]+$/'))
dedowsdi
  • 6,248
  • 1
  • 18
  • 31