Index: docs/ClangFormatStyleOptions.rst =================================================================== --- docs/ClangFormatStyleOptions.rst +++ docs/ClangFormatStyleOptions.rst @@ -1179,6 +1179,16 @@ plop(); plop(); } } +**IndentPPDirectives** (``bool``) + Indent preprocessor directives on conditionals. + + .. code-block:: c++ + + true: false: + #ifdef FOO vs. #ifdef FOO + # define A 0 #define A 0 + #endif #endif + **IndentWidth** (``unsigned``) The number of columns to use for indentation. Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -1024,6 +1024,19 @@ /// \endcode bool IndentCaseLabels; + /// \brief Indent preprocessor directives. + /// \code + /// true: + /// #ifdef FOO + /// # define A 0 + /// #endif + /// + /// false: + /// #ifdef FOO + /// #define A 0 + /// #endif + bool IndentPPDirectives; + /// \brief The number of columns to use for indentation. /// \code /// IndentWidth: 3 @@ -1514,6 +1527,7 @@ ForEachMacros == R.ForEachMacros && IncludeCategories == R.IncludeCategories && IndentCaseLabels == R.IndentCaseLabels && + IndentPPDirectives == R.IndentPPDirectives && IndentWidth == R.IndentWidth && Language == R.Language && IndentWrappedFunctionNames == R.IndentWrappedFunctionNames && JavaScriptQuotes == R.JavaScriptQuotes && Index: lib/Format/ContinuationIndenter.cpp =================================================================== --- lib/Format/ContinuationIndenter.cpp +++ lib/Format/ContinuationIndenter.cpp @@ -376,6 +376,19 @@ unsigned Spaces = Current.SpacesRequiredBefore + ExtraSpaces; + // indent preprocessor directives after the hash if required. + if ((State.Line->Type == LT_PreprocessorDirective || + State.Line->Type == LT_ImportStatement) && + Current.Previous->is(tok::hash) && State.FirstIndent > 0) { + // subtract 1 so indent lines up with non-preprocessor code + Spaces += State.FirstIndent; + // when tabs are not enabled, subtract one so the indent still lines up with + // non-preprocessor code + if (Style.UseTab == FormatStyle::UT_Never && Spaces > 0) { + --Spaces; + } + } + if (!DryRun) Whitespaces.replaceWhitespace(Current, /*Newlines=*/0, Spaces, State.Column + Spaces); Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -350,6 +350,7 @@ IO.mapOptional("IncludeCategories", Style.IncludeCategories); IO.mapOptional("IncludeIsMainRegex", Style.IncludeIsMainRegex); IO.mapOptional("IndentCaseLabels", Style.IndentCaseLabels); + IO.mapOptional("IndentPPDirectives", Style.IndentPPDirectives); IO.mapOptional("IndentWidth", Style.IndentWidth); IO.mapOptional("IndentWrappedFunctionNames", Style.IndentWrappedFunctionNames); @@ -589,6 +590,7 @@ {".*", 1}}; LLVMStyle.IncludeIsMainRegex = "(Test)?$"; LLVMStyle.IndentCaseLabels = false; + LLVMStyle.IndentPPDirectives = false; LLVMStyle.IndentWrappedFunctionNames = false; LLVMStyle.IndentWidth = 2; LLVMStyle.JavaScriptQuotes = FormatStyle::JSQS_Leave; Index: lib/Format/UnwrappedLineFormatter.cpp =================================================================== --- lib/Format/UnwrappedLineFormatter.cpp +++ lib/Format/UnwrappedLineFormatter.cpp @@ -1018,6 +1018,12 @@ if (RootToken.IsFirst && !RootToken.HasUnescapedNewline) Newlines = 0; + // Preprocessor directives get indented after the hash. + if (Line.Type == LT_PreprocessorDirective || + Line.Type == LT_ImportStatement) { + Indent = 0; + } + // Remove empty lines after "{". if (!Style.KeepEmptyLinesAtTheStartOfBlocks && PreviousLine && PreviousLine->Last->is(tok::l_brace) && Index: lib/Format/UnwrappedLineParser.h =================================================================== --- lib/Format/UnwrappedLineParser.h +++ lib/Format/UnwrappedLineParser.h @@ -235,6 +235,9 @@ // sequence. std::stack PPChainBranchIndex; + unsigned PPIndentLevel; + FormatToken *PPMaybeIncludeGuard; + friend class ScopedLineState; friend class CompoundStatementIndenter; }; Index: lib/Format/UnwrappedLineParser.cpp =================================================================== --- lib/Format/UnwrappedLineParser.cpp +++ lib/Format/UnwrappedLineParser.cpp @@ -231,7 +231,8 @@ : Line(new UnwrappedLine), MustBreakBeforeNextToken(false), CurrentLines(&Lines), Style(Style), Keywords(Keywords), CommentPragmasRegex(Style.CommentPragmas), Tokens(nullptr), - Callback(Callback), AllTokens(Tokens), PPBranchLevel(-1) {} + Callback(Callback), AllTokens(Tokens), PPBranchLevel(-1), + PPIndentLevel(0), PPMaybeIncludeGuard(nullptr) {} void UnwrappedLineParser::reset() { PPBranchLevel = -1; @@ -661,17 +662,29 @@ if (IfDef && !IfNDef && FormatTok->TokenText == "SWIG") Unreachable = true; conditionalCompilationStart(Unreachable); + FormatToken *IfCondition = FormatTok; parsePPUnknown(); + if (IfNDef) { + PPMaybeIncludeGuard = IfCondition; + } + ++PPIndentLevel; } void UnwrappedLineParser::parsePPElse() { + if (PPIndentLevel > 0) { + --PPIndentLevel; + } conditionalCompilationAlternative(); parsePPUnknown(); + ++PPIndentLevel; } void UnwrappedLineParser::parsePPElIf() { parsePPElse(); } void UnwrappedLineParser::parsePPEndIf() { + if (PPIndentLevel > 0) { + --PPIndentLevel; + } conditionalCompilationEnd(); parsePPUnknown(); } @@ -683,14 +696,22 @@ parsePPUnknown(); return; } + if (PPMaybeIncludeGuard != nullptr && + PPMaybeIncludeGuard->TokenText == FormatTok->TokenText && + PPIndentLevel > 0) { + --PPIndentLevel; + } + PPMaybeIncludeGuard = nullptr; nextToken(); if (FormatTok->Tok.getKind() == tok::l_paren && FormatTok->WhitespaceRange.getBegin() == FormatTok->WhitespaceRange.getEnd()) { parseParens(); } + if (Style.IndentPPDirectives) + Line->Level += PPIndentLevel; addUnwrappedLine(); - Line->Level = 1; + ++Line->Level; // Errors during a preprocessor directive can only affect the layout of the // preprocessor directive, and thus we ignore them. An alternative approach @@ -704,7 +725,10 @@ do { nextToken(); } while (!eof()); + if (Style.IndentPPDirectives) + Line->Level += PPIndentLevel; addUnwrappedLine(); + PPMaybeIncludeGuard = nullptr; } // Here we blacklist certain tokens that are not usually the first token in an Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -2215,8 +2215,55 @@ getLLVMStyleWithColumns(11)); } -TEST_F(FormatTest, IndentPreprocessorDirectivesAtZero) { - EXPECT_EQ("{\n {\n#define A\n }\n}", format("{{\n#define A\n}}")); +TEST_F(FormatTest, IndentPreprocessorDirectives) { + FormatStyle Style = getLLVMStyle(); + Style.IndentPPDirectives = false; + verifyFormat("#ifdef _WIN32\n" + "#define A 0\n" + "#ifdef VAR2\n" + "#define B 1\n" + "#include \n" + "#define MACRO " + " \\\n" + " some_very_long_func_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaa();\n" + "#endif\n" + "#else\n" + "#define A 1\n" + "#endif", Style); + + Style.IndentPPDirectives = true; + verifyFormat("#ifdef _WIN32\n" + "# define A 0\n" + "# ifdef VAR2\n" + "# define B 1\n" + "# include \n" + "# define MACRO " + " \\\n" + " some_very_long_func_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaa();\n" + "# endif\n" + "#else\n" + "# define A 1\n" + "#endif", Style); + // don't indent include guards + // verifyFormat() currently calls test::messUp() which incorrectly merges L2 + // and L3, so call EXPECT_EQ manually + EXPECT_EQ("#ifndef _SOMEFILE_H\n" + "#define _SOMEFILE_H\n" + "code();\n" + "#endif", format("#ifndef _SOMEFILE_H\n" + "#define _SOMEFILE_H\n" + "code();\n" + "#endif", Style)); + + // make sure it works even with tabs + Style.UseTab = FormatStyle::UT_Always; + Style.IndentWidth = 8; + Style.TabWidth = 8; + verifyFormat("#ifdef _WIN32\n" + "#\tdefine C 1\n" + "#endif", Style); } TEST_F(FormatTest, FormatHashIfNotAtStartOfLine) {