Wikia

Vim Tips Wiki

Fold quickfix list on directory or file names

Talk0
1,610pages on
this wiki
Revision as of 05:49, July 13, 2012 by JohnBot (Talk | contribs)

Tip 801 Printable Monobook Previous Next

created 2004 · complexity basic · author Jonas Lööf · version 6.0


The quickfix window is often used to process the results of using make to build a program, or using vimgrep (or grep) to search files. When the quickfix list is long, it can be helpful to fold related lines together to help show an overview.

This tip concerns folding lines in the quickfix window. See here for how to fold the current buffer based on the content of the quickfix list.

Folding output from make

When using make on a large software project with multiple directories, the quickfix window can be quite long. Use the following in your vimrc to automatically fold on each subdirectory, and open folds containing errors (the string "error:"). Additionally, zw opens also the folds containing the string "warning:", while zq switches back to the original.

" Folding of (gnu)make output.
au BufReadPost quickfix setlocal foldmethod=marker
au BufReadPost quickfix setlocal foldmarker=Entering\ directory,Leaving\ directory
au BufReadPost quickfix map <buffer> <silent> zq zM:g/error:/normal zv<CR>
au BufReadPost quickfix map <buffer> <silent> zw zq:g/warning:/normal zv<CR>
au BufReadPost quickfix normal zq

Folding output from vimgrep

You may search files with a command like :vimgrep /pattern/ *.c, then open the quickfix window with :cwindow. The following script will fold all the search hits for one file into a single line.

Put the following in file ~/.vim/ftplugin/qf_fold.vim (Unix) or $HOME/vimfiles/ftplugin/qf_fold.vim (Windows):

setlocal foldlevel=0
setlocal foldmethod=expr
setlocal foldexpr=matchstr(getline(v:lnum),'^[^\|]\\+')==#matchstr(getline(v:lnum+1),'^[^\|]\\+')?1:'<1'

The fold expression compares the file name of the current line with that on the next line. If they are the same, the lines are part of a fold (level 1). If the next line is for a different file, the current line is the end of a fold.

In the setlocal command, the string '^[^\|]\\+' is used. That command processes the backslashes with the result that the pattern used by matchstr() is '^[^|]\+' (that is, the file path/name consisting of one or more characters that are not '|', at the start of the line).


As several commands that populate the quickfix list are only concerned about a single file, you can optionally add the following enhancement:

if foldclosedend(1) == line('$')
  " When all matches come from a single file, do not close that single fold;
  " the user probably is interested in the contents.
  setlocal foldlevel=1
endif

See also

References

Comments

 TO DO 

  • The "Folding output from vimgrep" section is my interpretation of messages from David Fishburn and Gary Johnson on vim_use. Need to check a little more.
  • This tip needs to be renamed (tip should be about folding the quickfix list). Any suggestions?
  • Will remove these comments in a few days.

--JohnBeckett 09:21, 30 October 2008 (UTC)

Add folding not based on file/directory?

I'm using the following, based on the 'vimgrep' portion for grep searches, and folding away based on whether the line is a hit for an error or warning otherwise (with context):

In ~/vimfiles/after/ftplugin/qf.vim:

" set up folding of quickfix list based on command
setlocal foldlevel=0
setlocal foldmethod=expr
setlocal foldexpr=QfFoldByType(v:lnum)
 
" for some reason w:quickfix_title is not set until after the filetype plugin
" runs, so decide which folding to use in the function itself instead
function! QfFoldByType(lnum)
  if exists('w:quickfix_title') && w:quickfix_title =~? 'grep'
    return QfFoldFiles(a:lnum)
  else
    return QfFoldWarningsAndErrors(a:lnum)
  endif
endfun
 
function! QfFoldFiles(lnum)
  return matchstr(getline(a:lnum),'^[^|]\+')==#matchstr(getline(a:lnum+1),'^[^|]\+')?1:'<1'
endfun
 
function! QfFoldWarningsAndErrors(lnum)
  if v:version >= 700
    let contextlines=getline(a:lnum-2, a:lnum+2)
  else
    " Vim before 7.0 had no List data type and getline() only gets a single line
    " but luckily the match() function can give a result for either arrays or
    " strings, with -1 meaning 'no match' in either case.
    let contextlines=""
    let aline = a:lnum-2
    while aline <= a:lnum+2 && aline <= line('$')
      if aline >= 1
        let contextlines=contextlines."\n".getline(aline)
      endif
      let aline = aline + 1
    endwhile
  endif
  let thisline=getline(a:lnum)
  if thisline =~? '\%(^\|\n\)\f\+|[^|]*\%(warning\|error\)[^|]*|'
    return 0
  elseif match(contextlines, '\%(^\|\n\)\f\+|[^|]*\%(warning\|error\)[^|]*|') >= 0
    return 1
  else
    return 2
  endif
endfun

In ~/.vimrc:

  augroup QUICKFIX_TITLE_VAR_HACK
    au!
    " force refresh of foldexpr for quickfix in case it depends on
    " w:quickfix_title which for unknown reasons is not set until after the
    " filetype plugin loads
    au BufWinEnter * if &buftype==#'quickfix' | let &foldexpr=&foldexpr | endif
  augroup END

This is very useful for me but doesn't really fit in the current tip, because (1) it doesn't include the existing 'make output' portion, and (2) includes folding not based on file or directory names. Should this go into a new tip or can we generalize this one and include it here?

--Fritzophrenic 16:10, February 10, 2012 (UTC)

Around Wikia's network

Random Wiki