17

I got a plain text file with whitespace separated columns of values. Like this:

AU 3030 .... ... ....  
AU 3031 .... ... ....  
AU 3032 .... ... .... 
AU 3033 .... ... .... 
IT 48100 ... .. .....
IT 40100 ... .. .....
IT 48123 ... .. .....
UK 3333 ... ... ..... 
UK 4444 ... ... .....
UK 5555 ... ... .....

I also got this regex which will match any adjacent line with the same value in the first column (assume the file is sorted on the first column) except the last:

/^\(\([A-Z0-9]\+\)\s\+.*\n\)\(\2\)\@=

(or to make it less "hairy"):

/^\v([A-Z0-9]+)\s+.*\n(\1)@=

Is it possible to fold lines over the line which was not matched? Having this result:

+-- 4 lines AU ....
+-- 3 lines IT ....
+-- 3 lines UK ....

1 Answer 1

18

Do set foldmethod=expr and use 'foldexpr' to set a vim script expression that will define the fold start points.

set foldmethod=expr
set foldexpr=get(split(getline(v:lnum-1)),0,'')!=get(split(getline(v:lnum)),0,'')?'>1':'='

This looks more complicated than it is, because we can't easily use spaces in :set, but with spaces, and a newline or 2, it looks like:

get(split(getline(v:lnum - 1)), 0, '') != get(split(getline(v:lnum)), 0, '')
    \ ? '>1'
    \ : '='

Overview

Basically this compares the first word of each line with the previous line. If the words are different then the line is start of the fold, >1. Otherwise it keeps the same fold level, =.

Glory of Details

  • set foldmethod=expr to tell Vim to use a vim script expression to determine the foldings
  • 'foldexpr' option holds the vim script expression
  • Evaluating the condition with a ternary that returns >1 when a fold should start and = when the fold level should continue
  • v:lnum is the current line that that 'foldexpr' is running on to update the folds
  • Get the contents the current line (v:lnum) and the previous line (v:lnum - 1) via getline()
  • Split each line into words via split()
  • Use get() to get the first index of the freshly split words
  • Use a default value of '' in case of a blank line. e.g. get(words, 0, '')
  • Compare the first word of the current line with the first word of the previous line in the condition portion of the ternary

Note: this method may have some performance issues with very large documents

For more help see:

:h 'foldmethod'
:h 'foldexpr'
:h getline(
:h v:lnum
:h split(
:h get(
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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