29

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.

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


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...

31
:vimgrep pattern %
:cwin

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

cwin 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.

  • 1
    :vimgrep can be abbreviated to :vim – D. Ben Knoble Jun 11 '18 at 14:51
  • how do i get back to the buffer again? – pasha Dec 2 '18 at 12:19
  • also how do i close the buffer? thanks in advance – pasha Dec 2 '18 at 12:23
  • 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 at 12:59
14

You can list all matching lines with

:g/{pattern}

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

  • 1
    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 at 8:23
  • @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 at 9:41
12

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.

  • Seems like there is a set foldlevel=0 needed? But maybe that's default. – Volker Siegel Feb 28 '15 at 3:16
  • 1
    Yeah the default is zero, but if it doesn't work without, adding it can't hurt. :-) – Ben Feb 28 '15 at 3:18
  • But now... how to switch it back again, with no traces? – Volker Siegel Feb 28 '15 at 3:23
  • in normal mode, you can use 'zi' to toggle folding off and on. – John O'M. Feb 28 '15 at 4:04
  • Most of the time when I do this I first :tab sp to get a new throwaway view on the file. Then going back is as simple as :close or any other way to kill the tab. – Ben Feb 28 '15 at 17:49
6

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

:g/pat/m0

For more help see:

:h :g
:h :m
  • 1
    And :g/pat/m$ (move the matched lines to the end) is also OK, – Lerner Zhang Sep 28 '16 at 9:05
6

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

:g!/pattern/d

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

:v/pattern/d
  • 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 4:06
2

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.

0

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]+$/'))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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