22

General info

(Note: Please read this all the way through, because I spent a bit of time organizing it, making sure I addressed each individual problem I'm having and why one proposed solution doesn't work for me.)

I'm using cindent in vim to automagically do indentation. It works great most of the time. However, cindent does three Bad Things (in my opinion) involving C preprocessor directives (statements starting with a hash ('#')).

In case it's relevant, I use tabs for indentation, and I my .vimrc contains filetype indent on.

Bad Thing 1

As soon as I type a preprocessor directive, vim puts it in column 1 (it completely un-indents it). For example, here's what vim does to my code as I type it:

int main(void)
{
    while(1) {
        /* normal things */
#ifdef DEBUG
        printDebugInfo();
#endif
        /* normal things */
    }
}

However, I want it look like this:

int main(void)
{
    while(1) {
        /* normal things */
        #ifdef DEBUG
        printDebugInfo();
        #endif
        /* normal things */
    }
}

In other words, I'd prefer that vim treat preprocessor directives like any other C/C++ statement.

Bad Thing 2

When I use == on (or [movement]= across) a line with a preprocessor directive, vim puts it back in column 1. (This is consistent with Bad Thing 1 but still a problem.)

Bad Thing 3

If (because of Bad Thing 1 or 2 or the use of <<) a preprocessor directive ends up in column 1, it becomes "stuck" there and isn't affected by >>. The only way to increase the indentation is I<Tab> or i<Ctrl-t>. It then becomes "un-stuck" and I can move it with << or >>.

Failed solutions

One solution suggests putting set cinkeys-=0# in my .vimrc. This fixes Bad Things 1 - 3, but adds a new one:

Bad Thing 4 (only with cinkeys-=0#)

Preprocessor directives in column 1 aren't affected by == or [movement]=, meaning I still can't automagically fix their indentation until I manually indent them with >> or by inserting a tab character.

Question

Is there a way to resolve Bad Things 1 - 3 without introducing Bad Thing 4? Can I force vim to treat lines starting with '#' like ordinary C statements?

(Or am I just using #ifdefs in a harmful way, and this is vim's way of telling me to stop?)

I'd prefer not to patch vim and/or recompile, but I will if I have to.

Community
  • 1
  • 1
Dr Kitty
  • 442
  • 3
  • 12
  • 5
    *"However, I want it look like this"* There are a lot of people who would tell you that "No, you don't want it to look like that". Preprocessing happens at a different time and level than compilation proper, and indenting as you propose risks confusing the two ideas. Secondly, many (most?) people feel that preprocessing should be treated as an exceptional case and *should* stand out in the code. – dmckee --- ex-moderator kitten Apr 16 '13 at 21:38
  • 4
    @dmckee I'm sure that's true, but it still becomes a nightmare to debug scoping issues when you have dozens of debug flags, with nested if conditions, and g++ complains that variable `xyz was not declared in this scope` and it literally looks like embedded alien code trying to portray something, like Davinci's Vitruvian Man – puk Nov 29 '13 at 09:24
  • 1
    I'm getting exactly the same problem, but it's happening when I'm in python, and it's every comment. My `cinkeys=0{,0},0),:,!^F,o,O,e`, which is what is recommended in the solution, but it still happens sometimes. It even happens after I `set nocindent` – Theo Belaire Feb 10 '14 at 18:54
  • Oh, got it, smartindent got set. Thanks for all the links! – Theo Belaire Feb 10 '14 at 19:08
  • "Or am I just using #ifdefs in a harmful way, and this is vim's way of telling me to stop?" Yes. – n. m. could be an AI Jul 20 '17 at 08:37
  • Of note: Nowadays indenting the directives is actually okay, but some old compilers might not like this. See [here](https://stackoverflow.com/a/27871240/2550406) – lucidbrot Nov 27 '20 at 07:49
  • @dmckee---ex-moderatorkitten Constant folding happens earlier in the compiler, so any constant expression should also be out to the left, so you don't confuse it with other things. Also, macro **calls** are expanded before compiling, not only `#ifdef` directives, so in case `getchar` might be a macro, you should align that flush with the left column too. – Kaz Apr 12 '21 at 15:11

2 Answers2

7

From the Vim documentation, under cinoptions:

Vim puts a line in column 1 if:

  • It starts with '#' (preprocessor directives), if 'cinkeys' contains '#'.
  • It starts with a label (a keyword followed by ':', other than "case" and "default") and 'cinoptions' does not contain an 'L' entry with a positive value.
  • Any combination of indentations causes the line to have less than 0 indentation.

So, removing 0# from cinkeys should override that default behavior. (This idea turns up in enough places on the interwebs.)

set cinkeys=0{,0},0),:,!^F,o,O,e

With that change to cinkeys, here's what I got, exactly what you're looking for:

int main(void)
{
    while(1) {
        /* normal things */
        #ifdef DEBUG
        printDebugInfo();
        #endif
        /* normal things */
    }
}
Jens
  • 69,818
  • 15
  • 125
  • 179
Mogsdad
  • 44,709
  • 21
  • 151
  • 275
  • Thanks for the effort, but I specifically addressed removing `0#` in my original question under "Failed solutions". It may be the closest I can get to what I want, but I'd still like to see if I can eliminate special treatment of preprocessor directives altogether. – Dr Kitty Apr 17 '13 at 05:37
  • You've typed `set cinkeys`, and confirmed that you don't have `0#` in there? And have you tried just re-inserting the carriage return? That's what I did. Sucks with thousands of lines, of course. – Mogsdad Apr 17 '13 at 11:19
4

Solution for Bad Thing 4

set cinoptions+=#1s

This will indent by 1 shiftwidth if you use == or [movement]=. Combining it with the option cinkeys-=0# worked for me.

poinu
  • 73
  • 1
  • 8
leo
  • 81
  • 4