From b95f5450a97a879c18e37575315bb7ff98e84392 Mon Sep 17 00:00:00 2001 From: Manuel Klimek Date: Fri, 8 Feb 2013 17:38:27 +0000 Subject: [PATCH] Takes the context into account when re-indenting regions. Fixes llvm.org/PR14916. llvm-svn: 174720 --- clang/lib/Format/Format.cpp | 87 +++++++++++++++++++++++++---------- clang/unittests/Format/FormatTest.cpp | 51 ++++++++++++++++++++ 2 files changed, 113 insertions(+), 25 deletions(-) diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index bef5f92..0797fb8 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -921,34 +921,90 @@ public: for (unsigned i = 0, e = AnnotatedLines.size(); i != e; ++i) { Annotator.calculateFormattingInformation(AnnotatedLines[i]); } + std::vector IndentForLevel; for (std::vector::iterator I = AnnotatedLines.begin(), E = AnnotatedLines.end(); I != E; ++I) { const AnnotatedLine &TheLine = *I; + int Offset = GetIndentOffset(TheLine.First); + while (IndentForLevel.size() <= TheLine.Level) + IndentForLevel.push_back(-1); + IndentForLevel.resize(TheLine.Level + 1); if (touchesRanges(TheLine) && TheLine.Type != LT_Invalid) { - unsigned Indent = - formatFirstToken(TheLine.First, TheLine.Level, - TheLine.InPPDirective, PreviousEndOfLineColumn); + unsigned LevelIndent = GetIndent(IndentForLevel, TheLine.Level); + unsigned Indent = LevelIndent; + if (static_cast(Indent) + Offset >= 0) + Indent += Offset; + if (!TheLine.First.FormatTok.WhiteSpaceStart.isValid() || + StructuralError) { + Indent = LevelIndent = SourceMgr.getSpellingColumnNumber( + TheLine.First.FormatTok.Tok.getLocation()) - 1; + } else { + formatFirstToken(TheLine.First, Indent, TheLine.InPPDirective, + PreviousEndOfLineColumn); + } tryFitMultipleLinesInOne(Indent, I, E); UnwrappedLineFormatter Formatter(Style, SourceMgr, TheLine, Indent, TheLine.First, Whitespaces, StructuralError); PreviousEndOfLineColumn = Formatter.format(); + IndentForLevel[TheLine.Level] = LevelIndent; } else { // If we did not reformat this unwrapped line, the column at the end of // the last token is unchanged - thus, we can calculate the end of the - // last token, and return the result. + // last token. PreviousEndOfLineColumn = SourceMgr.getSpellingColumnNumber( TheLine.Last->FormatTok.Tok.getLocation()) + Lex.MeasureTokenLength(TheLine.Last->FormatTok.Tok.getLocation(), SourceMgr, Lex.getLangOpts()) - 1; + unsigned Indent = SourceMgr.getSpellingColumnNumber( + TheLine.First.FormatTok.Tok.getLocation()) - 1; + unsigned LevelIndent = Indent; + if (static_cast(LevelIndent) - Offset >= 0) + LevelIndent -= Offset; + IndentForLevel[TheLine.Level] = LevelIndent; } } return Whitespaces.generateReplacements(); } private: + /// \brief Get the indent of \p Level from \p IndentForLevel. + /// + /// \p IndentForLevel must contain the indent for the level \c l + /// at \p IndentForLevel[l], or a value < 0 if the indent for + /// that level is unknown. + unsigned GetIndent(const std::vector IndentForLevel, + unsigned Level) { + if (Level == 0) + return 0; + if (IndentForLevel[Level] != -1) + return IndentForLevel[Level]; + return GetIndent(IndentForLevel, Level - 1) + 2; + } + + /// \brief Get the offset of the line relatively to the level. + /// + /// For example, 'public:' labels in classes are offset by 1 or 2 + /// characters to the left from their level. + int GetIndentOffset(const AnnotatedToken &RootToken) { + bool IsAccessModifier = false; + if (RootToken.is(tok::kw_public) || RootToken.is(tok::kw_protected) || + RootToken.is(tok::kw_private)) + IsAccessModifier = true; + else if (RootToken.is(tok::at) && !RootToken.Children.empty() && + (RootToken.Children[0].isObjCAtKeyword(tok::objc_public) || + RootToken.Children[0].isObjCAtKeyword(tok::objc_protected) || + RootToken.Children[0].isObjCAtKeyword(tok::objc_package) || + RootToken.Children[0].isObjCAtKeyword(tok::objc_private))) + IsAccessModifier = true; + + if (IsAccessModifier) + return Style.AccessModifierOffset; + return 0; + } + /// \brief Tries to merge lines into one. /// /// This will change \c Line and \c AnnotatedLine to contain the merged line, @@ -1109,40 +1165,21 @@ private: /// \brief Add a new line and the required indent before the first Token /// of the \c UnwrappedLine if there was no structural parsing error. /// Returns the indent level of the \c UnwrappedLine. - unsigned formatFirstToken(const AnnotatedToken &RootToken, unsigned Level, - bool InPPDirective, - unsigned PreviousEndOfLineColumn) { + void formatFirstToken(const AnnotatedToken &RootToken, unsigned Indent, + bool InPPDirective, unsigned PreviousEndOfLineColumn) { const FormatToken &Tok = RootToken.FormatTok; - if (!Tok.WhiteSpaceStart.isValid() || StructuralError) - return SourceMgr.getSpellingColumnNumber(Tok.Tok.getLocation()) - 1; unsigned Newlines = std::min(Tok.NewlinesBefore, Style.MaxEmptyLinesToKeep + 1); if (Newlines == 0 && !Tok.IsFirst) Newlines = 1; - unsigned Indent = Level * 2; - - bool IsAccessModifier = false; - if (RootToken.is(tok::kw_public) || RootToken.is(tok::kw_protected) || - RootToken.is(tok::kw_private)) - IsAccessModifier = true; - else if (RootToken.is(tok::at) && !RootToken.Children.empty() && - (RootToken.Children[0].isObjCAtKeyword(tok::objc_public) || - RootToken.Children[0].isObjCAtKeyword(tok::objc_protected) || - RootToken.Children[0].isObjCAtKeyword(tok::objc_package) || - RootToken.Children[0].isObjCAtKeyword(tok::objc_private))) - IsAccessModifier = true; - if (IsAccessModifier && - static_cast(Indent) + Style.AccessModifierOffset >= 0) - Indent += Style.AccessModifierOffset; if (!InPPDirective || Tok.HasUnescapedNewline) { Whitespaces.replaceWhitespace(RootToken, Newlines, Indent, 0, Style); } else { Whitespaces.replacePPWhitespace(RootToken, Newlines, Indent, PreviousEndOfLineColumn, Style); } - return Indent; } DiagnosticsEngine &Diag; diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 997a553..df0b296 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -2544,5 +2544,56 @@ TEST_F(FormatTest, ObjCLiterals) { } +TEST_F(FormatTest, ReformatRegionAdjustsIndent) { + EXPECT_EQ("{\n" + "{\n" + "a;\n" + "b;\n" + "}\n" + "}", format("{\n" + "{\n" + "a;\n" + " b;\n" + "}\n" + "}", 13, 2, getLLVMStyle())); + EXPECT_EQ("{\n" + "{\n" + " a;\n" + "b;\n" + "}\n" + "}", format("{\n" + "{\n" + " a;\n" + "b;\n" + "}\n" + "}", 9, 2, getLLVMStyle())); + EXPECT_EQ("{\n" + "{\n" + "public:\n" + " b;\n" + "}\n" + "}", format("{\n" + "{\n" + "public:\n" + " b;\n" + "}\n" + "}", 17, 2, getLLVMStyle())); + EXPECT_EQ("{\n" + "{\n" + "a;\n" + "}\n" + "{\n" + " b;\n" + "}\n" + "}", format("{\n" + "{\n" + "a;\n" + "}\n" + "{\n" + " b;\n" + "}\n" + "}", 22, 2, getLLVMStyle())); +} + } // end namespace tooling } // end namespace clang -- 2.7.4