2

The default is this:

switch(x)
{
    case a:
        {
            y();
        }
}

But I need this:

switch(x)
{
    case a:
    {
        y();
    }
}

With :set cindent and :set cino={-s this is the result (indentation of switch's scope is wrong this time):

int main()
{
    switch(X)
{
    case a:
    {
            //...
    }

    case b:
    {
            //...
    }
}
}
  • 2
    Please add a question to your question. How does Indent multiple lines quickly in vi fail to solve your problem? – Quasímodo Sep 15 at 14:15
  • 1
    Does this answer your question? Vim switch statement cindent options – Rich Sep 15 at 14:58
  • @Quasímodo: Thanks for your suggestion. No, – user31439 Sep 15 at 15:03
  • @Rich: thanks, No, that solves the first case, but fails at others. – user31439 Sep 15 at 15:04
  • 2
    I'm not sure what you mean. I think there's only one case in your question. Could you clarify? – Rich Sep 15 at 15:10
4

You can align statements following the case label with the =N member of 'cinoptions'. The following will fix your specific example:

:set cinoptions+==0

Unfortunately, it breaks indentation if you don't use a block:

switch(x)
{
    case a:
    {
        y();
    }
    case b:
    y();
}

You've indicated in the comments that this solution works for you, though, so I'm adding it as an answer.

See this very-nearly-identical question (actually, just a more general case) for various solutions to that issue.

| improve this answer | |
2

Indentation of C/C++ code is typically done by enabling the 'cindent' option (built-in to Vim), which is in turn controlled by options set in 'cinoptions'.

There are two interesting settings in 'cinoptions' that somewhat do what you want.

The first is :set cinoptions==0, which instructs Vim to not indent the block under a case statement. So you do get this effect:

case a:
{
    y();
    break;
}

On the downside, when you don't use a block starting with {, then what you get is:

case a:
y();
break;

Which looks wrong...

Then there's :set cinoptions=l1, which doesn't do exactly what you described here, but on the other hand it works well for when the { is opened on the same line as the case statement.

So you get:

case a: {
    y();
    break;
}

And if you omit the braces, then you get:

case a:
    y();
    break;

But unfortunately it doesn't help in the case where the { is opened on a line of its own, below the case statement.

These two options affect case statements exclusively, so they don't affect any other { blocks connected to any other statements (I see you found :set cino={-s but that affects the {s elsewhere.)

It's theoretically possible to get exactly what you need by writing an indentation function and setting 'indentexpr' (instead of using the built-in 'cindent'), but this would require you to write Vimscript to essentially reimplement all the built-in features of 'cindent' as well, which would surely be a herculean task... Probably best is to settle for one of the options above, or perhaps manually fix the indentation (with Ctrl+D and similar keystrokes) when you use blocks inside case statements, particularly if you don't do it too often.

| improve this answer | |
  • Thank you for your detailed reply, what is the difference between :set cinoptions==0 and :set cinoptions+==0 which @Rich suggested? – user31439 Sep 15 at 16:40
  • 1
    Using += on an option will add to the existing list of options. Since 'cinoptions' is typically empty (and it's empty by default), both will do the same. If 'cinoptions' had previous settings in it, then = will drop the existing ones, while += will preserve them. I guess I wrote my answer on the assumption you would set one of these options in your vimrc, and if you had more to set, you'd set it all in a single command... – filbranden Sep 15 at 16:45
  • See :help :set+= for the actual explanation from Vim docs. – filbranden Sep 15 at 16:45
  • 2
    Actually the indentexpr could return cindent() for all lines except those blocks following a case... not too hard if you can match the right spots (searchpairpos will be useful). – D. Ben Knoble Sep 16 at 12:13
2

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) =~# '^\s*case'
    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.

| improve this answer | |
  • 1
    You should post this on the almost duplicate question, too. – Rich Sep 16 at 13:38
  • 1
    ...or perhaps better, just post it there and then close this one as a duplicate, as that one's a more general case. – Rich Sep 16 at 13:39
  • 2
    @Rich good find; I read your earlier comments, but everything said « almost/not quite a dupe » so I didn’t actually look at the Q. I think it is a full dupe though, not sure sure how this Q differs in anyway. I’ll do both, I think. – D. Ben Knoble Sep 16 at 13:49
  • 2
    I agree it's a dupe. The only difference is that this question doesn't have the requirement for it to work if there's no braces: it only cares about the scenario where there is a block after the case label. If that question had had a working answer, I'd have left my close vote anyway, but it seemed a bit mean to close this as a dupe when no full answer had yet been posted. – Rich Sep 16 at 14:18