diff options
-rw-r--r-- | include/clang/Format/Format.h | 4 | ||||
-rw-r--r-- | lib/Format/Format.cpp | 2 | ||||
-rw-r--r-- | lib/Format/WhitespaceManager.cpp | 118 | ||||
-rw-r--r-- | lib/Format/WhitespaceManager.h | 34 | ||||
-rw-r--r-- | unittests/Format/FormatTest.cpp | 19 |
5 files changed, 124 insertions, 53 deletions
diff --git a/include/clang/Format/Format.h b/include/clang/Format/Format.h index d6cc114e3c..28aa6bd2ce 100644 --- a/include/clang/Format/Format.h +++ b/include/clang/Format/Format.h @@ -91,6 +91,10 @@ struct FormatStyle { /// \brief Add a space in front of an Objective-C protocol list, i.e. use /// Foo <Protocol> instead of Foo<Protocol>. bool ObjCSpaceBeforeProtocolList; + + /// \brief If \c true, aligns escaped newlines as far left as possible. + /// Otherwise puts them into the right-most column. + bool AlignEscapedNewlinesLeft; }; /// \brief Returns a format style complying with the LLVM coding standards: diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index b483308a5a..7d2041cd3c 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -51,6 +51,7 @@ FormatStyle getLLVMStyle() { LLVMStyle.ObjCSpaceBeforeProtocolList = true; LLVMStyle.PenaltyExcessCharacter = 1000000; LLVMStyle.PenaltyReturnTypeOnItsOwnLine = 75; + LLVMStyle.AlignEscapedNewlinesLeft = false; return LLVMStyle; } @@ -71,6 +72,7 @@ FormatStyle getGoogleStyle() { GoogleStyle.ObjCSpaceBeforeProtocolList = false; GoogleStyle.PenaltyExcessCharacter = 1000000; GoogleStyle.PenaltyReturnTypeOnItsOwnLine = 200; + GoogleStyle.AlignEscapedNewlinesLeft = true; return GoogleStyle; } diff --git a/lib/Format/WhitespaceManager.cpp b/lib/Format/WhitespaceManager.cpp index d29cd52b0e..a75c592bfe 100644 --- a/lib/Format/WhitespaceManager.cpp +++ b/lib/Format/WhitespaceManager.cpp @@ -21,6 +21,9 @@ namespace format { void WhitespaceManager::replaceWhitespace(const AnnotatedToken &Tok, unsigned NewLines, unsigned Spaces, unsigned WhitespaceStartColumn) { + if (NewLines > 0) + alignEscapedNewlines(); + // 2+ newlines mean an empty line separating logic scopes. if (NewLines >= 2) alignComments(); @@ -39,16 +42,14 @@ void WhitespaceManager::replaceWhitespace(const AnnotatedToken &Tok, Spaces + WhitespaceStartColumn + Tok.FormatTok.TokenLength > Style.ColumnLimit; // Align comment with other comments. - if ((Tok.Parent != NULL || !Comments.empty()) && !LineExceedsColumnLimit) { - StoredComment Comment; - Comment.Tok = Tok.FormatTok; - Comment.Spaces = Spaces; - Comment.NewLines = NewLines; - Comment.MinColumn = + if ((Tok.Parent != NULL || !Comments.empty()) && + !LineExceedsColumnLimit) { + unsigned MinColumn = NewLines > 0 ? Spaces : WhitespaceStartColumn + Spaces; - Comment.MaxColumn = Style.ColumnLimit - Tok.FormatTok.TokenLength; - Comment.Untouchable = false; - Comments.push_back(Comment); + unsigned MaxColumn = Style.ColumnLimit - Tok.FormatTok.TokenLength; + Comments.push_back(StoredToken( + Tok.FormatTok.WhiteSpaceStart, Tok.FormatTok.WhiteSpaceLength, + MinColumn, MaxColumn, NewLines, Spaces)); return; } } @@ -57,14 +58,24 @@ void WhitespaceManager::replaceWhitespace(const AnnotatedToken &Tok, if (Tok.Children.empty() && !Tok.isTrailingComment()) alignComments(); - storeReplacement(Tok.FormatTok, getNewLineText(NewLines, Spaces)); + storeReplacement(Tok.FormatTok.WhiteSpaceStart, + Tok.FormatTok.WhiteSpaceLength, + getNewLineText(NewLines, Spaces)); } void WhitespaceManager::replacePPWhitespace(const AnnotatedToken &Tok, unsigned NewLines, unsigned Spaces, unsigned WhitespaceStartColumn) { - storeReplacement(Tok.FormatTok, - getNewLineText(NewLines, Spaces, WhitespaceStartColumn)); + if (NewLines == 0) { + replaceWhitespace(Tok, NewLines, Spaces, WhitespaceStartColumn); + } else { + // The earliest position for "\" is 2 after the last token. + unsigned MinColumn = WhitespaceStartColumn + 2; + unsigned MaxColumn = Style.ColumnLimit; + EscapedNewlines.push_back(StoredToken( + Tok.FormatTok.WhiteSpaceStart, Tok.FormatTok.WhiteSpaceLength, + MinColumn, MaxColumn, NewLines, Spaces)); + } } void WhitespaceManager::breakToken(const FormatToken &Tok, unsigned Offset, @@ -72,20 +83,28 @@ void WhitespaceManager::breakToken(const FormatToken &Tok, unsigned Offset, StringRef Postfix, bool InPPDirective, unsigned Spaces, unsigned WhitespaceStartColumn) { - std::string NewLineText; - if (!InPPDirective) - NewLineText = getNewLineText(1, Spaces); - else - NewLineText = getNewLineText(1, Spaces, WhitespaceStartColumn); - std::string ReplacementText = (Prefix + NewLineText + Postfix).str(); SourceLocation Location = Tok.getStartOfNonWhitespace().getLocWithOffset(Offset); - Replaces.insert( - tooling::Replacement(SourceMgr, Location, ReplaceChars, ReplacementText)); + if (InPPDirective) { + // The earliest position for "\" is 2 after the last token. + unsigned MinColumn = WhitespaceStartColumn + 2; + unsigned MaxColumn = Style.ColumnLimit; + StoredToken StoredTok = StoredToken(Location, ReplaceChars, MinColumn, + MaxColumn, /*NewLines=*/ 1, Spaces); + StoredTok.Prefix = Prefix; + StoredTok.Postfix = Postfix; + EscapedNewlines.push_back(StoredTok); + } else { + std::string ReplacementText = + (Prefix + getNewLineText(1, Spaces) + Postfix).str(); + Replaces.insert(tooling::Replacement(SourceMgr, Location, ReplaceChars, + ReplacementText)); + } } const tooling::Replacements &WhitespaceManager::generateReplacements() { alignComments(); + alignEscapedNewlines(); return Replaces; } @@ -96,11 +115,9 @@ void WhitespaceManager::addReplacement(const SourceLocation &SourceLoc, } void WhitespaceManager::addUntouchableComment(unsigned Column) { - StoredComment Comment; - Comment.MinColumn = Column; - Comment.MaxColumn = Column; - Comment.Untouchable = true; - Comments.push_back(Comment); + StoredToken Tok = StoredToken(SourceLocation(), 0, Column, Column, 0, 0); + Tok.Untouchable = true; + Comments.push_back(Tok); } std::string WhitespaceManager::getNewLineText(unsigned NewLines, @@ -110,13 +127,14 @@ std::string WhitespaceManager::getNewLineText(unsigned NewLines, std::string WhitespaceManager::getNewLineText(unsigned NewLines, unsigned Spaces, - unsigned WhitespaceStartColumn) { + unsigned WhitespaceStartColumn, + unsigned EscapedNewlineColumn) { std::string NewLineText; if (NewLines > 0) { unsigned Offset = - std::min<int>(Style.ColumnLimit - 1, WhitespaceStartColumn); + std::min<int>(EscapedNewlineColumn - 1, WhitespaceStartColumn); for (unsigned i = 0; i < NewLines; ++i) { - NewLineText += std::string(Style.ColumnLimit - Offset - 1, ' '); + NewLineText += std::string(EscapedNewlineColumn - Offset - 1, ' '); NewLineText += "\\\n"; Offset = 0; } @@ -127,8 +145,8 @@ std::string WhitespaceManager::getNewLineText(unsigned NewLines, void WhitespaceManager::alignComments() { unsigned MinColumn = 0; unsigned MaxColumn = UINT_MAX; - comment_iterator Start = Comments.begin(); - for (comment_iterator I = Start, E = Comments.end(); I != E; ++I) { + token_iterator Start = Comments.begin(); + for (token_iterator I = Start, E = Comments.end(); I != E; ++I) { if (I->MinColumn > MaxColumn || I->MaxColumn < MinColumn) { alignComments(Start, I, MinColumn); MinColumn = I->MinColumn; @@ -143,26 +161,50 @@ void WhitespaceManager::alignComments() { Comments.clear(); } -void WhitespaceManager::alignComments(comment_iterator I, comment_iterator E, +void WhitespaceManager::alignComments(token_iterator I, token_iterator E, unsigned Column) { while (I != E) { if (!I->Untouchable) { unsigned Spaces = I->Spaces + Column - I->MinColumn; - storeReplacement(I->Tok, getNewLineText(I->NewLines, Spaces)); + storeReplacement(I->ReplacementLoc, I->ReplacementLength, + getNewLineText(I->NewLines, Spaces)); } ++I; } } -void WhitespaceManager::storeReplacement(const FormatToken &Tok, +void WhitespaceManager::alignEscapedNewlines() { + unsigned MinColumn; + if (Style.AlignEscapedNewlinesLeft) { + MinColumn = 0; + for (token_iterator I = EscapedNewlines.begin(), E = EscapedNewlines.end(); + I != E; ++I) { + if (I->MinColumn > MinColumn) + MinColumn = I->MinColumn; + } + } else { + MinColumn = Style.ColumnLimit; + } + + for (token_iterator I = EscapedNewlines.begin(), E = EscapedNewlines.end(); + I != E; ++I) { + // I->MinColumn - 2 is the end of the previous token (i.e. the + // WhitespaceStartColumn). + storeReplacement( + I->ReplacementLoc, I->ReplacementLength, + I->Prefix + getNewLineText(I->NewLines, I->Spaces, I->MinColumn - 2, + MinColumn) + I->Postfix); + + } + EscapedNewlines.clear(); +} + +void WhitespaceManager::storeReplacement(SourceLocation Loc, unsigned Length, const std::string Text) { // Don't create a replacement, if it does not change anything. - if (StringRef(SourceMgr.getCharacterData(Tok.WhiteSpaceStart), - Tok.WhiteSpaceLength) == Text) + if (StringRef(SourceMgr.getCharacterData(Loc), Length) == Text) return; - - Replaces.insert(tooling::Replacement(SourceMgr, Tok.WhiteSpaceStart, - Tok.WhiteSpaceLength, Text)); + Replaces.insert(tooling::Replacement(SourceMgr, Loc, Length, Text)); } } // namespace format diff --git a/lib/Format/WhitespaceManager.h b/lib/Format/WhitespaceManager.h index 2833e249c4..5f3dc55eda 100644 --- a/lib/Format/WhitespaceManager.h +++ b/lib/Format/WhitespaceManager.h @@ -1,4 +1,4 @@ -//===--- WhitespaceManager.h - Format C++ code ----------------------------===// +//===--- WhitespaceManager.h - Format C++ code ------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -68,31 +68,45 @@ public: /// \brief Try to align all stashed comments. void alignComments(); + /// \brief Try to align all stashed escaped newlines. + void alignEscapedNewlines(); private: std::string getNewLineText(unsigned NewLines, unsigned Spaces); std::string getNewLineText(unsigned NewLines, unsigned Spaces, - unsigned WhitespaceStartColumn); - - /// \brief Structure to store a comment for later layout and alignment. - struct StoredComment { - FormatToken Tok; + unsigned WhitespaceStartColumn, + unsigned EscapedNewlineColumn); + + /// \brief Structure to store tokens for later layout and alignment. + struct StoredToken { + StoredToken(SourceLocation ReplacementLoc, unsigned ReplacementLength, + unsigned MinColumn, unsigned MaxColumn, unsigned NewLines, + unsigned Spaces) + : ReplacementLoc(ReplacementLoc), ReplacementLength(ReplacementLength), + MinColumn(MinColumn), MaxColumn(MaxColumn), NewLines(NewLines), + Spaces(Spaces), Untouchable(false) {} + SourceLocation ReplacementLoc; + unsigned ReplacementLength; unsigned MinColumn; unsigned MaxColumn; unsigned NewLines; unsigned Spaces; bool Untouchable; + std::string Prefix; + std::string Postfix; }; - SmallVector<StoredComment, 16> Comments; - typedef SmallVector<StoredComment, 16>::iterator comment_iterator; + SmallVector<StoredToken, 16> Comments; + SmallVector<StoredToken, 16> EscapedNewlines; + typedef SmallVector<StoredToken, 16>::iterator token_iterator; /// \brief Put all the comments between \p I and \p E into \p Column. - void alignComments(comment_iterator I, comment_iterator E, unsigned Column); + void alignComments(token_iterator I, token_iterator E, unsigned Column); /// \brief Stores \p Text as the replacement for the whitespace in front of /// \p Tok. - void storeReplacement(const FormatToken &Tok, const std::string Text); + void storeReplacement(SourceLocation Loc, unsigned Length, + const std::string Text); SourceManager &SourceMgr; tooling::Replacements Replaces; diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 97539d91a6..97d4df8e5e 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -2974,16 +2974,16 @@ TEST_F(FormatTest, MergeHandlingInTheFaceOfPreprocessorDirectives) { format("if (true)\nreturn 42;", AllowsMergedIf)); FormatStyle ShortMergedIf = AllowsMergedIf; ShortMergedIf.ColumnLimit = 25; - verifyFormat("#define A \\\n" + verifyFormat("#define A \\\n" " if (true) return 42;", ShortMergedIf); - verifyFormat("#define A \\\n" - " f(); \\\n" + verifyFormat("#define A \\\n" + " f(); \\\n" " if (true)\n" "#define B", ShortMergedIf); - verifyFormat("#define A \\\n" - " f(); \\\n" + verifyFormat("#define A \\\n" + " f(); \\\n" " if (true)\n" "g();", ShortMergedIf); @@ -3800,6 +3800,15 @@ TEST_F(FormatTest, BreakStringLiterals) { "\"pathat/\"\n" "\"slashes\"", format("\"split/pathat/slashes\"", getLLVMStyleWithColumns(10))); + + FormatStyle AlignLeft = getLLVMStyleWithColumns(12); + AlignLeft.AlignEscapedNewlinesLeft = true; + EXPECT_EQ( + "#define A \\\n" + " \"some \" \\\n" + " \"text \" \\\n" + " \"other\";", + format("#define A \"some text other\";", AlignLeft)); } TEST_F(FormatTest, DoNotBreakStringLiteralsInEscapeSequence) { |