Hans Dampf
Hans Dampf

Reputation: 366

vim customize c syntax folding

Vim supports syntax based folding for C code which works well. However the function name is not part of the fold, only the part between { and }.

given this example code:

static inline bool uFuzzyCompare(double p1, double p2)
{
    return (uAbs(p1 - p2) <= 0.000000000001 * uMin(uAbs(p1), uAbs(p2)));
}

static inline bool uFuzzyComparef(float p1, float p2)
{
    return (uAbsf(p1 - p2) <= 0.00001f * uMinf(uAbsf(p1), uAbsf(p2)));
}

static inline bool uFuzzyIsNull(double d)
{
    return uAbs(d) <= 0.000000000001;
}

static inline bool uFuzzyIsNullf(float f)
{
    return uAbs(f) <= 0.00001f;
}

This looks folded like this:

static inline bool uFuzzyCompare(double p1, double p2)
+--  3 lines: {----------------------------------------------------------------------------------------------------------------------------------------------------------------

static inline bool uFuzzyComparef(float p1, float p2)
+--  3 lines: {----------------------------------------------------------------------------------------------------------------------------------------------------------------

static inline bool uFuzzyIsNull(double d)
+--  3 lines: {----------------------------------------------------------------------------------------------------------------------------------------------------------------

static inline bool uFuzzyIsNullf(float f)
+--  3 lines: {----------------------------------------------------------------------------------------------------------------------------------------------------------------

I would prefer this:

+-- uFuzzyCompare(...) (5 lines)
+-- uFuzzyComparef(...) (5 lines)
+-- uFuzzyIsNull(...) (5 lines)
+-- uFuzzyIsNullf(...) (5 lines)

This means including the function declaration into the fold as well as the blank line and showing the function name as fold text.

Is this possible somehow? Maybe using foldmethod expression, but I don't know a good way to create this expression without writing a complete C parser. So using the syntax parser would be the best, but I could not find any configuration options for that.

regards, Gerhard

Upvotes: 2

Views: 640

Answers (2)

Luc Hermitte
Luc Hermitte

Reputation: 32926

Correctly handling folding is not that trivial.

For instance, supporting K&R indenting style requires some work.

if (a_or_b) {
    if (foo) {
       a1();
    } else if (bar) {
       a2();
    } else {
       a3();
    }
} else {
   b();
}

Folding intelligently/nicely things like do {...} while(cond); complicates things even further (as we need to support nested contexts)

do
{
   act();
   do {
      a2();
   } 
   while (inner);
} 
while(outer)
;

Anyway, I've developed a solution that tries its best in a reasonable time: VimFold4C -- but no I don't support do { ... } while(cond); yet, nor I exploit comments appearing at the beginning of folded zones :(.

Upvotes: 0

FDinoff
FDinoff

Reputation: 31419

This will get you on your way. It currently only folds top level functions. Put this in ~/.vim/ftplugin/c.vim

function! CFold()
    if getline(v:lnum - 1)[0] ==# '}'
        return '<1'
    elseif getline(v:lnum + 1)[0] ==# '{'
        return '>1'
    endif
    return '='
endfunction

function! CFoldText()
    let line = getline(v:foldstart)
    let sub = substitute(line, '.*\<\(\w\+\)(.*).*', '\1(...)', 'g')
    return '+-- ' . sub . ' (' . (v:foldend - v:foldstart + 1) . ' lines)'
endfunction

setlocal foldmethod=expr
setlocal foldexpr=CFold()
setlocal foldtext=CFoldText()
setlocal fillchars-=fold:-

Output for the example text

+-- uFuzzyCompare(...) (5 lines)
+-- uFuzzyComparef(...) (5 lines)
+-- uFuzzyIsNull(...) (5 lines)
+-- uFuzzyIsNullf(...) (5 lines)

How this works. When you set foldmethod to expr vim uses the expression in foldexpr to determine how it should fold. All I am doing is checking to see if the line after after the current contains a { in the first column if it does we set the fold level to >1 which says start a fold of level 1. It end on the line the }. <1 says end a fold of level 1.

To set the fold text we just extract the function and replace the arguments with ... (This might break for lines where the arguments are multiple lines). Then we return a nicely formatted string. v:foldstart and v:foldend are variables set that say which line the fold starts and end on.

Removing fold:- from fillchars removes the dashes after the fold text.

Relevant help pages :h 'foldmethod', :h foldexpr, :h foldtext, and :h 'fillchars'

Upvotes: 4

Related Questions