9

I want my C code indented as follows:

switch (x)
{
    case 1:
        break;
    case 2:
    {
        break;
    }
}

Instead of this:

switch (x)
{
    case 1:
        break;
    case 2:
        {
            break;
        }
}

I looked through cinoptions, but didn't find what I need. I'm looking for some options I missed or other indentation script.

Thanks in advance

3 Answers3

5

I don't think it's possible as is, you can work around that though.


For your current example you can set

:set  cino={-s

This will reindent each {/} pair found to reduce the shift width setting. This will work on the example you gave:

switch (x)
{
  case 1:
    break;
  case 2:
  {
      break;
  }
}

But not on an example with more complex syntax:

switch (x)
{
  case 1:
    break;
  case 2:
  {
      break;
      if( 1 )
    {
        //stuff
    }
  }
}

Apparently, by looking at :h cindent, Vim prefers the curly brace on the same line as the statement.

So I would recommand using the following formatting instead:

switch (x) {

  case 1:
    break;

  case 2: {
    break;
  }

}

With this, you can set cino=l1 and indent will be:

switch (x) {

  case 1:
    break;

  case 2: {
    break;
  }

}

Again, it might not suit your coding style but AFAIK, it the best way to properly indent the case statement.

nobe4
  • 16,033
  • 4
  • 48
  • 81
3

A solution I discovered while answering a duplicate: it should be faster than the existing answer, as it uses just a couple of matches/builtin functions, rather than looping over lines. The algorithm is simpler, too. However, it may not work if there are blank lines or comments between the { and the case, or comments at the front of those lines—I would consider both so rare to not warrant fixing those issues, but if you need it, it's doable.


I put together a little function that defaults to using cindent() for the indent, unless we are indenting a { line that directly follows a case statement (I've been twiddling a lot with indentexpr lately):

function s:indent(lnum, offset) abort
  return a:lnum <= 0
        \ ? indent('.')
        \ : indent(a:lnum) + a:offset
endfunction

function! c#myindentexpr(lnum) abort if a:lnum <= 1 return cindent(a:lnum) endif if getline(a:lnum) =~# '^\s{' && getline(a:lnum-1) =~# '^\scase' return s:indent(a:lnum-1, 0) else return cindent(a:lnum) endif endfunction

Drop it in ~/.vim/autoload/c.vim, and put the following line in ~/.vim/after/ftplugin/c.vim:

setlocal indentexpr=c#myindentexpr(v:lnum)

You probably want to set b:undo_ftplugin as well.


I tested this on a couple of simple cases and it seems to be working.

D. Ben Knoble
  • 26,070
  • 3
  • 29
  • 65
1

I finally came up with a solution with custom indent script:

setlocal comments=s1:/*,mb:*,ex:*/

setlocal cindent
setlocal cinoptions=Ls,t0,(0

setlocal indentexpr=GetCaseBlockCorrectedIndent()
setlocal indentkeys=!^F,0{,0},0),0#,o,O,e,:

function! s:PrevNonBlankOrComment(startlnum)
    let lnum = a:startlnum
    while lnum > 0
        let lnum = prevnonblank(lnum)
        if (getline(lnum) =~ '^\s*\(/\|\*\)')
            let lnum -= 1
        else
            break
        endif
    endwhile
    return lnum
endfunction

function! GetCaseBlockCorrectedIndent()
    let lnum = v:lnum
    let prevlnum = s:PrevNonBlankOrComment(lnum - 1)
    let idnt = cindent(v:lnum)
    let adj = 0
    if getline(prevlnum) =~ '^\s*case\>' && getline(lnum) =~ '^\s*{'
       let adj = -&shiftwidth
    endif
    return idnt + adj
endfunction