Vim Tips Wiki
(Added folding by directory. Added not folding for fewer than 25 lines.)
(Added g:GetLastNonWrappedQFLine to hide || overflow lines in quickfix)
Line 153: Line 153:
   
 
--[[User:Fritzophrenic|Fritzophrenic]] 16:10, February 10, 2012 (UTC)
 
--[[User:Fritzophrenic|Fritzophrenic]] 16:10, February 10, 2012 (UTC)
  +
  +
===Also fold up the || overflow lines in quickfix window===
  +
  +
I loved folding by file or folder in the quickfix, but got very annoyed by the `|| ` lines which appear when very long lines overflow. To work around this, I replaced the calls to <code>getline()</code> with a custom function <code>g:GetLastNonWrappedQFLine()</code>. First I defined it as follows:
  +
  +
<source lang='vim'>
  +
function! g:GetLastNonWrappedQFLine(startline)
  +
let curline = a:startline
  +
while 1
  +
let line = getline(curline)
  +
if line[0:2] != '|| '
  +
return line
  +
endif
  +
let curline -= 1
  +
if curline <= 1
  +
break
  +
endif
  +
endwhile
  +
return ''
  +
endfunction
  +
</source>
  +
  +
Then it can be employed to fold by file:
  +
  +
<source lang='vim'>
  +
setlocal foldexpr=matchstr(g:GetLastNonWrappedQFLine(v:lnum),'^[^\|]\\+')==#matchstr(g:GetLastNonWrappedQFLine(v:lnum+1),'^[^\|]\\+')?1:'<1'
  +
</source>
  +
  +
or to fold by folder:
  +
  +
<source lang='vim'>
  +
setlocal foldexpr=matchstr(substitute(g:GetLastNonWrappedQFLine(v:lnum),'\|.*','',''),'^.*/')==#matchstr(substitute(g:GetLastNonWrappedQFLine(v:lnum+1),'\|.*','',''),'^.*/')?1:'<1'
  +
</source>
  +
  +
--[[User:JoeyTwiddle|JoeyTwiddle]] ([[User talk:JoeyTwiddle|talk]]) 13:44, August 24, 2013 (UTC)

Revision as of 13:44, 24 August 2013

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

Folding by directory instead of filename

If folding by file still creates too many results, you may prefer to fold by directory name instead. Replace the foldexpr above with this one:

setlocal foldexpr=matchstr(substitute(getline(v:lnum),'\|.*','',''),'^.*/')==#matchstr(substitute(getline(v:lnum+1),'\|.*','',''),'^.*/')?1:'<1'

In this expression, first everything after the first '|' is discarded with the calls to substitute(), and then everything up to the last '/' is compared using matchstr(). (This creates only one level of folding, not a tree.)

You may also like to customise the way that folded lines are displayed:

setlocal foldtext=matchstr(substitute(getline(v:foldstart),'\|.*','',''),'^.*/').'\ ['.(v:foldend-v:foldstart+1).'\ lines]'

Avoid folding when there are few results

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

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

This will initially open all the folds if there is only one fold, or if there are fewer than 25 lines in the quickfix window.

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)

Also fold up the || overflow lines in quickfix window

I loved folding by file or folder in the quickfix, but got very annoyed by the `|| ` lines which appear when very long lines overflow. To work around this, I replaced the calls to getline() with a custom function g:GetLastNonWrappedQFLine(). First I defined it as follows:

function! g:GetLastNonWrappedQFLine(startline)
	let curline = a:startline
	while 1
		let line = getline(curline)
		if line[0:2] != '|| '
			return line
		endif
		let curline -= 1
		if curline <= 1
			break
		endif
	endwhile
	return ''
endfunction

Then it can be employed to fold by file:

setlocal foldexpr=matchstr(g:GetLastNonWrappedQFLine(v:lnum),'^[^\|]\\+')==#matchstr(g:GetLastNonWrappedQFLine(v:lnum+1),'^[^\|]\\+')?1:'<1'

or to fold by folder:

setlocal foldexpr=matchstr(substitute(g:GetLastNonWrappedQFLine(v:lnum),'\|.*','',''),'^.*/')==#matchstr(substitute(g:GetLastNonWrappedQFLine(v:lnum+1),'\|.*','',''),'^.*/')?1:'<1'

--JoeyTwiddle (talk) 13:44, August 24, 2013 (UTC)