From 3d9ffcf3aa0ed84fa297e3c461bb84e48221aa2d Mon Sep 17 00:00:00 2001 From: Alexander Kornienko Date: Fri, 27 Sep 2013 16:14:22 +0000 Subject: [PATCH] Implemented tab usage only for indentation (http://llvm.org/PR17363) Summary: Changed UseTab to be a enum with three options: Never, Always, ForIndentation (true/false are still supported when reading .clang-format). IndentLevel should currently be propagated correctly for all tokens, except for block comments. Please take a look at the general idea before I start dealing with block comments. Reviewers: klimek, djasper Reviewed By: klimek CC: cfe-commits Differential Revision: http://llvm-reviews.chandlerc.com/D1770 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@191527 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Format/Format.h | 16 +++++- lib/Format/BreakableToken.cpp | 53 ++++++++--------- lib/Format/BreakableToken.h | 39 +++++++------ lib/Format/ContinuationIndenter.cpp | 27 +++++---- lib/Format/Format.cpp | 34 +++++++---- lib/Format/WhitespaceManager.cpp | 50 +++++++++++----- lib/Format/WhitespaceManager.h | 17 ++++-- unittests/Format/FormatTest.cpp | 88 ++++++++++++++++++++++++++++- 8 files changed, 228 insertions(+), 96 deletions(-) diff --git a/include/clang/Format/Format.h b/include/clang/Format/Format.h index 1feddf15d6..c326f66ef2 100644 --- a/include/clang/Format/Format.h +++ b/include/clang/Format/Format.h @@ -166,9 +166,19 @@ struct FormatStyle { /// \brief If \c true, always break before multiline string literals. bool AlwaysBreakBeforeMultilineStrings; - /// \brief If \c true, \c IndentWidth consecutive spaces will be replaced - /// with tab characters. - bool UseTab; + /// \brief Different ways to use tab in formatting. + enum UseTabStyle { + /// Never use tab. + UT_Never, + /// Use tabs only for indentation. + UT_ForIndentation, + /// Use tabs whenever we need to fill whitespace that spans at least from + /// one tab stop to the next one. + UT_Always + }; + + /// \brief The way to use tab characters in the resulting file. + UseTabStyle UseTab; /// \brief If \c true, binary operators will be placed after line breaks. bool BreakBeforeBinaryOperators; diff --git a/lib/Format/BreakableToken.cpp b/lib/Format/BreakableToken.cpp index b23819d102..cbc50f566a 100644 --- a/lib/Format/BreakableToken.cpp +++ b/lib/Format/BreakableToken.cpp @@ -145,10 +145,10 @@ unsigned BreakableSingleLineToken::getLineLengthAfterSplit( } BreakableSingleLineToken::BreakableSingleLineToken( - const FormatToken &Tok, unsigned StartColumn, StringRef Prefix, - StringRef Postfix, bool InPPDirective, encoding::Encoding Encoding, - const FormatStyle &Style) - : BreakableToken(Tok, InPPDirective, Encoding, Style), + const FormatToken &Tok, unsigned IndentLevel, unsigned StartColumn, + StringRef Prefix, StringRef Postfix, bool InPPDirective, + encoding::Encoding Encoding, const FormatStyle &Style) + : BreakableToken(Tok, IndentLevel, InPPDirective, Encoding, Style), StartColumn(StartColumn), Prefix(Prefix), Postfix(Postfix) { assert(Tok.TokenText.startswith(Prefix) && Tok.TokenText.endswith(Postfix)); Line = Tok.TokenText.substr( @@ -156,11 +156,11 @@ BreakableSingleLineToken::BreakableSingleLineToken( } BreakableStringLiteral::BreakableStringLiteral( - const FormatToken &Tok, unsigned StartColumn, StringRef Prefix, - StringRef Postfix, bool InPPDirective, encoding::Encoding Encoding, - const FormatStyle &Style) - : BreakableSingleLineToken(Tok, StartColumn, Prefix, Postfix, InPPDirective, - Encoding, Style) {} + const FormatToken &Tok, unsigned IndentLevel, unsigned StartColumn, + StringRef Prefix, StringRef Postfix, bool InPPDirective, + encoding::Encoding Encoding, const FormatStyle &Style) + : BreakableSingleLineToken(Tok, IndentLevel, StartColumn, Prefix, Postfix, + InPPDirective, Encoding, Style) {} BreakableToken::Split BreakableStringLiteral::getSplit(unsigned LineIndex, unsigned TailOffset, @@ -175,7 +175,7 @@ void BreakableStringLiteral::insertBreak(unsigned LineIndex, WhitespaceManager &Whitespaces) { Whitespaces.replaceWhitespaceInToken( Tok, Prefix.size() + TailOffset + Split.first, Split.second, Postfix, - Prefix, InPPDirective, 1, StartColumn); + Prefix, InPPDirective, 1, IndentLevel, StartColumn); } static StringRef getLineCommentPrefix(StringRef Comment) { @@ -186,12 +186,10 @@ static StringRef getLineCommentPrefix(StringRef Comment) { return ""; } -BreakableLineComment::BreakableLineComment(const FormatToken &Token, - unsigned StartColumn, - bool InPPDirective, - encoding::Encoding Encoding, - const FormatStyle &Style) - : BreakableSingleLineToken(Token, StartColumn, +BreakableLineComment::BreakableLineComment( + const FormatToken &Token, unsigned IndentLevel, unsigned StartColumn, + bool InPPDirective, encoding::Encoding Encoding, const FormatStyle &Style) + : BreakableSingleLineToken(Token, IndentLevel, StartColumn, getLineCommentPrefix(Token.TokenText), "", InPPDirective, Encoding, Style) { OriginalPrefix = Prefix; @@ -216,7 +214,7 @@ void BreakableLineComment::insertBreak(unsigned LineIndex, unsigned TailOffset, WhitespaceManager &Whitespaces) { Whitespaces.replaceWhitespaceInToken( Tok, OriginalPrefix.size() + TailOffset + Split.first, Split.second, - Postfix, Prefix, InPPDirective, 1, StartColumn); + Postfix, Prefix, InPPDirective, 1, IndentLevel, StartColumn); } void @@ -224,15 +222,15 @@ BreakableLineComment::replaceWhitespaceBefore(unsigned LineIndex, WhitespaceManager &Whitespaces) { if (OriginalPrefix != Prefix) { Whitespaces.replaceWhitespaceInToken(Tok, OriginalPrefix.size(), 0, "", "", - false, 0, 1); + false, 0, /*IndentLevel=*/0, 1); } } BreakableBlockComment::BreakableBlockComment( - const FormatToken &Token, unsigned StartColumn, + const FormatToken &Token, unsigned IndentLevel, unsigned StartColumn, unsigned OriginalStartColumn, bool FirstInLine, bool InPPDirective, encoding::Encoding Encoding, const FormatStyle &Style) - : BreakableToken(Token, InPPDirective, Encoding, Style) { + : BreakableToken(Token, IndentLevel, InPPDirective, Encoding, Style) { StringRef TokenText(Token.TokenText); assert(TokenText.startswith("/*") && TokenText.endswith("*/")); TokenText.substr(2, TokenText.size() - 4).split(Lines, "\n"); @@ -327,12 +325,6 @@ void BreakableBlockComment::adjustWhitespace(unsigned LineIndex, LeadingWhitespace[LineIndex] = Lines[LineIndex].begin() - Lines[LineIndex - 1].end(); - // FIXME: We currently count tabs as 1 character. To solve this, we need to - // get the correct indentation width of the start of the comment, which - // requires correct counting of the tab expansions before the comment, and - // a configurable tab width. Since the current implementation only breaks - // if leading tabs are intermixed with spaces, that is not a high priority. - // Adjust the start column uniformly accross all lines. StartOfLineColumn[LineIndex] = std::max( 0, @@ -376,9 +368,9 @@ void BreakableBlockComment::insertBreak(unsigned LineIndex, unsigned TailOffset, Text.data() - Tok.TokenText.data() + Split.first; unsigned CharsToRemove = Split.second; assert(IndentAtLineBreak >= Decoration.size()); - Whitespaces.replaceWhitespaceInToken(Tok, BreakOffsetInToken, CharsToRemove, - "", Prefix, InPPDirective, 1, - IndentAtLineBreak - Decoration.size()); + Whitespaces.replaceWhitespaceInToken( + Tok, BreakOffsetInToken, CharsToRemove, "", Prefix, InPPDirective, 1, + IndentLevel, IndentAtLineBreak - Decoration.size()); } void @@ -412,7 +404,8 @@ BreakableBlockComment::replaceWhitespaceBefore(unsigned LineIndex, assert(StartOfLineColumn[LineIndex] >= Prefix.size()); Whitespaces.replaceWhitespaceInToken( Tok, WhitespaceOffsetInToken, LeadingWhitespace[LineIndex], "", Prefix, - InPPDirective, 1, StartOfLineColumn[LineIndex] - Prefix.size()); + InPPDirective, 1, IndentLevel, + StartOfLineColumn[LineIndex] - Prefix.size()); } unsigned diff --git a/lib/Format/BreakableToken.h b/lib/Format/BreakableToken.h index af3b09b5a5..132301c9f6 100644 --- a/lib/Format/BreakableToken.h +++ b/lib/Format/BreakableToken.h @@ -66,12 +66,14 @@ public: WhitespaceManager &Whitespaces) {} protected: - BreakableToken(const FormatToken &Tok, bool InPPDirective, - encoding::Encoding Encoding, const FormatStyle &Style) - : Tok(Tok), InPPDirective(InPPDirective), Encoding(Encoding), - Style(Style) {} + BreakableToken(const FormatToken &Tok, unsigned IndentLevel, + bool InPPDirective, encoding::Encoding Encoding, + const FormatStyle &Style) + : Tok(Tok), IndentLevel(IndentLevel), InPPDirective(InPPDirective), + Encoding(Encoding), Style(Style) {} const FormatToken &Tok; + const unsigned IndentLevel; const bool InPPDirective; const encoding::Encoding Encoding; const FormatStyle &Style; @@ -88,9 +90,10 @@ public: StringRef::size_type Length) const; protected: - BreakableSingleLineToken(const FormatToken &Tok, unsigned StartColumn, - StringRef Prefix, StringRef Postfix, - bool InPPDirective, encoding::Encoding Encoding, + BreakableSingleLineToken(const FormatToken &Tok, unsigned IndentLevel, + unsigned StartColumn, StringRef Prefix, + StringRef Postfix, bool InPPDirective, + encoding::Encoding Encoding, const FormatStyle &Style); // The column in which the token starts. @@ -109,10 +112,10 @@ public: /// /// \p StartColumn specifies the column in which the token will start /// after formatting. - BreakableStringLiteral(const FormatToken &Tok, unsigned StartColumn, - StringRef Prefix, StringRef Postfix, - bool InPPDirective, encoding::Encoding Encoding, - const FormatStyle &Style); + BreakableStringLiteral(const FormatToken &Tok, unsigned IndentLevel, + unsigned StartColumn, StringRef Prefix, + StringRef Postfix, bool InPPDirective, + encoding::Encoding Encoding, const FormatStyle &Style); virtual Split getSplit(unsigned LineIndex, unsigned TailOffset, unsigned ColumnLimit) const; @@ -126,9 +129,9 @@ public: /// /// \p StartColumn specifies the column in which the comment will start /// after formatting. - BreakableLineComment(const FormatToken &Token, unsigned StartColumn, - bool InPPDirective, encoding::Encoding Encoding, - const FormatStyle &Style); + BreakableLineComment(const FormatToken &Token, unsigned IndentLevel, + unsigned StartColumn, bool InPPDirective, + encoding::Encoding Encoding, const FormatStyle &Style); virtual Split getSplit(unsigned LineIndex, unsigned TailOffset, unsigned ColumnLimit) const; @@ -150,10 +153,10 @@ public: /// after formatting, while \p OriginalStartColumn specifies in which /// column the comment started before formatting. /// If the comment starts a line after formatting, set \p FirstInLine to true. - BreakableBlockComment(const FormatToken &Token, unsigned StartColumn, - unsigned OriginaStartColumn, bool FirstInLine, - bool InPPDirective, encoding::Encoding Encoding, - const FormatStyle &Style); + BreakableBlockComment(const FormatToken &Token, unsigned IndentLevel, + unsigned StartColumn, unsigned OriginaStartColumn, + bool FirstInLine, bool InPPDirective, + encoding::Encoding Encoding, const FormatStyle &Style); virtual unsigned getLineCount() const; virtual unsigned getLineLengthAfterSplit(unsigned LineIndex, diff --git a/lib/Format/ContinuationIndenter.cpp b/lib/Format/ContinuationIndenter.cpp index 8b0baead67..b3888c1c1a 100644 --- a/lib/Format/ContinuationIndenter.cpp +++ b/lib/Format/ContinuationIndenter.cpp @@ -303,12 +303,13 @@ unsigned ContinuationIndenter::addTokenToState(LineState &State, bool Newline, State.Stack.back().BreakBeforeParameter = false; if (!DryRun) { - unsigned NewLines = 1; + unsigned Newlines = 1; if (Current.is(tok::comment)) - NewLines = std::max(NewLines, std::min(Current.NewlinesBefore, + Newlines = std::max(Newlines, std::min(Current.NewlinesBefore, Style.MaxEmptyLinesToKeep + 1)); - Whitespaces.replaceWhitespace(Current, NewLines, State.Column, - State.Column, State.Line->InPPDirective); + Whitespaces.replaceWhitespace(Current, Newlines, State.Line->Level, + State.Column, State.Column, + State.Line->InPPDirective); } if (!Current.isTrailingComment()) @@ -363,7 +364,8 @@ unsigned ContinuationIndenter::addTokenToState(LineState &State, bool Newline, unsigned Spaces = State.NextToken->SpacesRequiredBefore + ExtraSpaces; if (!DryRun) - Whitespaces.replaceWhitespace(Current, 0, Spaces, State.Column + Spaces); + Whitespaces.replaceWhitespace(Current, /*Newlines=*/0, /*IndentLevel=*/0, + Spaces, State.Column + Spaces); if (Current.Type == TT_ObjCSelectorName && State.Stack.back().ColonPos == 0) { @@ -693,21 +695,22 @@ unsigned ContinuationIndenter::breakProtrudingToken(const FormatToken &Current, Text.startswith(Prefix = "L\""))) || (Text.startswith(Prefix = "_T(\"") && Text.endswith(Postfix = "\")")) || getRawStringLiteralPrefixPostfix(Text, Prefix, Postfix)) { - Token.reset(new BreakableStringLiteral(Current, StartColumn, Prefix, - Postfix, State.Line->InPPDirective, - Encoding, Style)); + Token.reset(new BreakableStringLiteral( + Current, State.Line->Level, StartColumn, Prefix, Postfix, + State.Line->InPPDirective, Encoding, Style)); } else { return 0; } } else if (Current.Type == TT_BlockComment && Current.isTrailingComment()) { Token.reset(new BreakableBlockComment( - Current, StartColumn, Current.OriginalColumn, !Current.Previous, - State.Line->InPPDirective, Encoding, Style)); + Current, State.Line->Level, StartColumn, Current.OriginalColumn, + !Current.Previous, State.Line->InPPDirective, Encoding, Style)); } else if (Current.Type == TT_LineComment && (Current.Previous == NULL || Current.Previous->Type != TT_ImplicitStringLiteral)) { - Token.reset(new BreakableLineComment( - Current, StartColumn, State.Line->InPPDirective, Encoding, Style)); + Token.reset(new BreakableLineComment(Current, State.Line->Level, + StartColumn, State.Line->InPPDirective, + Encoding, Style)); } else { return 0; } diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index 6166ecc1b3..d616769061 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -44,6 +44,19 @@ struct ScalarEnumerationTraits { } }; +template <> +struct ScalarEnumerationTraits { + static void + enumeration(IO &IO, clang::format::FormatStyle::UseTabStyle &Value) { + IO.enumCase(Value, "Never", clang::format::FormatStyle::UT_Never); + IO.enumCase(Value, "false", clang::format::FormatStyle::UT_Never); + IO.enumCase(Value, "Always", clang::format::FormatStyle::UT_Always); + IO.enumCase(Value, "true", clang::format::FormatStyle::UT_Always); + IO.enumCase(Value, "ForIndentation", + clang::format::FormatStyle::UT_ForIndentation); + } +}; + template <> struct ScalarEnumerationTraits { static void @@ -194,7 +207,7 @@ FormatStyle getLLVMStyle() { LLVMStyle.PointerBindsToType = false; LLVMStyle.SpacesBeforeTrailingComments = 1; LLVMStyle.Standard = FormatStyle::LS_Cpp03; - LLVMStyle.UseTab = false; + LLVMStyle.UseTab = FormatStyle::UT_Never; LLVMStyle.SpacesInParentheses = false; LLVMStyle.SpaceInEmptyParentheses = false; LLVMStyle.SpacesInCStyleCastParentheses = false; @@ -237,7 +250,7 @@ FormatStyle getGoogleStyle() { GoogleStyle.PointerBindsToType = true; GoogleStyle.SpacesBeforeTrailingComments = 2; GoogleStyle.Standard = FormatStyle::LS_Auto; - GoogleStyle.UseTab = false; + GoogleStyle.UseTab = FormatStyle::UT_Never; GoogleStyle.SpacesInParentheses = false; GoogleStyle.SpaceInEmptyParentheses = false; GoogleStyle.SpacesInCStyleCastParentheses = false; @@ -539,7 +552,7 @@ private: Style.MaxEmptyLinesToKeep + 1); Newlines = std::max(1u, Newlines); Whitespaces->replaceWhitespace( - *(*I)->First, Newlines, /*Spaces=*/Indent, + *(*I)->First, Newlines, (*I)->Level, /*Spaces=*/Indent, /*StartOfTokenColumn=*/Indent, Line.InPPDirective); } UnwrappedLineFormatter Formatter(Indenter, Whitespaces, Style, **I); @@ -556,10 +569,10 @@ private: return false; if (!DryRun) { - Whitespaces->replaceWhitespace(*LBrace.Children[0]->First, - /*Newlines=*/0, /*Spaces=*/1, - /*StartOfTokenColumn=*/State.Column, - State.Line->InPPDirective); + Whitespaces->replaceWhitespace( + *LBrace.Children[0]->First, + /*Newlines=*/0, /*IndentLevel=*/1, /*Spaces=*/1, + /*StartOfTokenColumn=*/State.Column, State.Line->InPPDirective); UnwrappedLineFormatter Formatter(Indenter, Whitespaces, Style, *LBrace.Children[0]); Penalty += Formatter.format(State.Column + 1, DryRun); @@ -847,8 +860,9 @@ public: if (TheLine.First->is(tok::eof)) { if (PreviousLineWasTouched) { unsigned NewLines = std::min(FirstTok->NewlinesBefore, 1u); - Whitespaces.replaceWhitespace(*TheLine.First, NewLines, /*Indent*/ 0, - /*TargetColumn*/ 0); + Whitespaces.replaceWhitespace(*TheLine.First, NewLines, + /*IndentLevel=*/0, /*Spaces=*/0, + /*TargetColumn=*/0); } } else if (TheLine.Type != LT_Invalid && (WasMoved || FormatPPDirective || touchesLine(TheLine))) { @@ -1225,7 +1239,7 @@ private: ++Newlines; Whitespaces.replaceWhitespace( - RootToken, Newlines, Indent, Indent, + RootToken, Newlines, Indent / Style.IndentWidth, Indent, Indent, InPPDirective && !RootToken.HasUnescapedNewline); } diff --git a/lib/Format/WhitespaceManager.cpp b/lib/Format/WhitespaceManager.cpp index 50ff8582d3..802145e72d 100644 --- a/lib/Format/WhitespaceManager.cpp +++ b/lib/Format/WhitespaceManager.cpp @@ -28,41 +28,44 @@ WhitespaceManager::Change::IsBeforeInFile::operator()(const Change &C1, WhitespaceManager::Change::Change( bool CreateReplacement, const SourceRange &OriginalWhitespaceRange, - unsigned Spaces, unsigned StartOfTokenColumn, unsigned NewlinesBefore, - StringRef PreviousLinePostfix, StringRef CurrentLinePrefix, - tok::TokenKind Kind, bool ContinuesPPDirective) + unsigned IndentLevel, unsigned Spaces, unsigned StartOfTokenColumn, + unsigned NewlinesBefore, StringRef PreviousLinePostfix, + StringRef CurrentLinePrefix, tok::TokenKind Kind, bool ContinuesPPDirective) : CreateReplacement(CreateReplacement), OriginalWhitespaceRange(OriginalWhitespaceRange), StartOfTokenColumn(StartOfTokenColumn), NewlinesBefore(NewlinesBefore), PreviousLinePostfix(PreviousLinePostfix), CurrentLinePrefix(CurrentLinePrefix), Kind(Kind), - ContinuesPPDirective(ContinuesPPDirective), Spaces(Spaces) {} + ContinuesPPDirective(ContinuesPPDirective), IndentLevel(IndentLevel), + Spaces(Spaces) {} void WhitespaceManager::replaceWhitespace(const FormatToken &Tok, - unsigned Newlines, unsigned Spaces, + unsigned Newlines, + unsigned IndentLevel, unsigned Spaces, unsigned StartOfTokenColumn, bool InPPDirective) { - Changes.push_back(Change(true, Tok.WhitespaceRange, Spaces, + Changes.push_back(Change(true, Tok.WhitespaceRange, IndentLevel, Spaces, StartOfTokenColumn, Newlines, "", "", Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst)); } void WhitespaceManager::addUntouchableToken(const FormatToken &Tok, bool InPPDirective) { - Changes.push_back(Change(false, Tok.WhitespaceRange, /*Spaces=*/0, - Tok.OriginalColumn, Tok.NewlinesBefore, "", "", - Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst)); + Changes.push_back(Change(false, Tok.WhitespaceRange, /*IndentLevel=*/0, + /*Spaces=*/0, Tok.OriginalColumn, Tok.NewlinesBefore, + "", "", Tok.Tok.getKind(), + InPPDirective && !Tok.IsFirst)); } void WhitespaceManager::replaceWhitespaceInToken( const FormatToken &Tok, unsigned Offset, unsigned ReplaceChars, StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective, - unsigned Newlines, unsigned Spaces) { + unsigned Newlines, unsigned IndentLevel, unsigned Spaces) { Changes.push_back(Change( true, SourceRange(Tok.getStartOfNonWhitespace().getLocWithOffset(Offset), Tok.getStartOfNonWhitespace().getLocWithOffset( Offset + ReplaceChars)), - Spaces, Spaces, Newlines, PreviousPostfix, CurrentPrefix, + IndentLevel, Spaces, Spaces, Newlines, PreviousPostfix, CurrentPrefix, // If we don't add a newline this change doesn't start a comment. Thus, // when we align line comments, we don't need to treat this change as one. // FIXME: We still need to take this change in account to properly @@ -225,7 +228,8 @@ void WhitespaceManager::generateChanges() { C.PreviousEndOfTokenColumn, C.EscapedNewlineColumn); else appendNewlineText(ReplacementText, C.NewlinesBefore); - appendIndentText(ReplacementText, C.Spaces, C.StartOfTokenColumn - C.Spaces); + appendIndentText(ReplacementText, C.IndentLevel, C.Spaces, + C.StartOfTokenColumn - C.Spaces); ReplacementText.append(C.CurrentLinePrefix); storeReplacement(C.OriginalWhitespaceRange, ReplacementText); } @@ -264,11 +268,14 @@ void WhitespaceManager::appendNewlineText(std::string &Text, unsigned Newlines, } } -void WhitespaceManager::appendIndentText(std::string &Text, unsigned Spaces, +void WhitespaceManager::appendIndentText(std::string &Text, + unsigned IndentLevel, unsigned Spaces, unsigned WhitespaceStartColumn) { - if (!Style.UseTab) { + switch (Style.UseTab) { + case FormatStyle::UT_Never: Text.append(std::string(Spaces, ' ')); - } else { + break; + case FormatStyle::UT_Always: { unsigned FirstTabWidth = Style.TabWidth - WhitespaceStartColumn % Style.TabWidth; // Indent with tabs only when there's at least one full tab. @@ -278,6 +285,19 @@ void WhitespaceManager::appendIndentText(std::string &Text, unsigned Spaces, } Text.append(std::string(Spaces / Style.TabWidth, '\t')); Text.append(std::string(Spaces % Style.TabWidth, ' ')); + break; + } + case FormatStyle::UT_ForIndentation: + if (WhitespaceStartColumn == 0) { + unsigned Indentation = IndentLevel * Style.IndentWidth; + if (Indentation > Spaces) + Indentation = Spaces; + unsigned Tabs = Indentation / Style.TabWidth; + Text.append(std::string(Tabs, '\t')); + Spaces -= Tabs * Style.TabWidth; + } + Text.append(std::string(Spaces, ' ')); + break; } } diff --git a/lib/Format/WhitespaceManager.h b/lib/Format/WhitespaceManager.h index 642b5b682b..93d331e125 100644 --- a/lib/Format/WhitespaceManager.h +++ b/lib/Format/WhitespaceManager.h @@ -44,7 +44,8 @@ public: /// \brief Replaces the whitespace in front of \p Tok. Only call once for /// each \c AnnotatedToken. void replaceWhitespace(const FormatToken &Tok, unsigned Newlines, - unsigned Spaces, unsigned StartOfTokenColumn, + unsigned IndentLevel, unsigned Spaces, + unsigned StartOfTokenColumn, bool InPPDirective = false); /// \brief Adds information about an unchangable token's whitespace. @@ -65,7 +66,8 @@ public: unsigned ReplaceChars, StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective, - unsigned Newlines, unsigned Spaces); + unsigned Newlines, unsigned IndentLevel, + unsigned Spaces); /// \brief Returns all the \c Replacements created during formatting. const tooling::Replacements &generateReplacements(); @@ -96,7 +98,7 @@ private: /// \p StartOfTokenColumn and \p InPPDirective will be used to lay out /// trailing comments and escaped newlines. Change(bool CreateReplacement, const SourceRange &OriginalWhitespaceRange, - unsigned Spaces, unsigned StartOfTokenColumn, + unsigned IndentLevel, unsigned Spaces, unsigned StartOfTokenColumn, unsigned NewlinesBefore, StringRef PreviousLinePostfix, StringRef CurrentLinePrefix, tok::TokenKind Kind, bool ContinuesPPDirective); @@ -116,6 +118,11 @@ private: tok::TokenKind Kind; bool ContinuesPPDirective; + // The number of nested blocks the token is in. This is used to add tabs + // only for the indentation, and not for alignment, when + // UseTab = US_ForIndentation. + unsigned IndentLevel; + // The number of spaces in front of the token or broken part of the token. // This will be adapted when aligning tokens. unsigned Spaces; @@ -157,8 +164,8 @@ private: void appendNewlineText(std::string &Text, unsigned Newlines, unsigned PreviousEndOfTokenColumn, unsigned EscapedNewlineColumn); - void appendIndentText(std::string &Text, unsigned Spaces, - unsigned WhitespaceStartColumn); + void appendIndentText(std::string &Text, unsigned IndentLevel, + unsigned Spaces, unsigned WhitespaceStartColumn); SmallVector Changes; SourceManager &SourceMgr; diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index c5fa70c98e..4c7399113d 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -5758,7 +5758,7 @@ TEST_F(FormatTest, ConfigurableFunctionDeclarationIndentAfterType) { TEST_F(FormatTest, ConfigurableUseOfTab) { FormatStyle Tab = getLLVMStyleWithColumns(42); Tab.IndentWidth = 8; - Tab.UseTab = true; + Tab.UseTab = FormatStyle::UT_Always; Tab.AlignEscapedNewlinesLeft = true; EXPECT_EQ("if (aaaaaaaa && // q\n" @@ -5858,7 +5858,83 @@ TEST_F(FormatTest, ConfigurableUseOfTab) { " \t */", Tab)); - Tab.UseTab = false; + Tab.UseTab = FormatStyle::UT_ForIndentation; + EXPECT_EQ("if (aaaaaaaa && // q\n" + " bb) // w\n" + "\t;", + format("if (aaaaaaaa &&// q\n" + "bb)// w\n" + ";", + Tab)); + verifyFormat("class X {\n" + "\tvoid f() {\n" + "\t\tsomeFunction(parameter1,\n" + "\t\t parameter2);\n" + "\t}\n" + "};", + Tab); + verifyFormat("{\n" + "\tQ({\n" + "\t\t int a;\n" + "\t\t someFunction(aaaaaaaaaa,\n" + "\t\t bbbbbbbbb);\n" + "\t },\n" + "\t p);\n" + "}", + Tab); + EXPECT_EQ("{\n" + "\t/* aaaa\n" + "\t bbbb */\n" + "}", + format("{\n" + "/* aaaa\n" + " bbbb */\n" + "}", + Tab)); + EXPECT_EQ("{\n" + "\t/*\n" + "\t aaaaaaaaaaaaaaaaaaaaaaaaaa\n" + "\t bbbbbbbbbbbbb\n" + "\t*/\n" + "}", + format("{\n" + "/*\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbb\n" + "*/\n" + "}", + Tab)); + EXPECT_EQ("{\n" + "\t// aaaaaaaaaaaaaaaaaaaaaaaaaa\n" + "\t// bbbbbbbbbbbbb\n" + "}", + format("{\n" + "\t// aaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbb\n" + "}", + Tab)); + EXPECT_EQ("{\n" + "\t/*\n" + "\t aaaaaaaaaaaaaaaaaaaaaaaaaa\n" + "\t bbbbbbbbbbbbb\n" + "\t*/\n" + "}", + format("{\n" + "\t/*\n" + "\t aaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbb\n" + "\t*/\n" + "}", + Tab)); + EXPECT_EQ("{\n" + "\t/*\n" + "\n" + "\t*/\n" + "}", + format("{\n" + "\t/*\n" + "\n" + "\t*/\n" + "}", Tab)); + + Tab.UseTab = FormatStyle::UT_Never; EXPECT_EQ("/*\n" " a\t\tcomment\n" " in multiple lines\n" @@ -6265,7 +6341,6 @@ TEST_F(FormatTest, ParsesConfiguration) { CHECK_PARSE_BOOL(ObjCSpaceBeforeProtocolList); CHECK_PARSE_BOOL(PointerBindsToType); CHECK_PARSE_BOOL(Cpp11BracedListStyle); - CHECK_PARSE_BOOL(UseTab); CHECK_PARSE_BOOL(IndentFunctionDeclarationAfterType); CHECK_PARSE_BOOL(SpacesInParentheses); CHECK_PARSE_BOOL(SpaceInEmptyParentheses); @@ -6292,6 +6367,13 @@ TEST_F(FormatTest, ParsesConfiguration) { CHECK_PARSE("Standard: C++11", Standard, FormatStyle::LS_Cpp11); CHECK_PARSE("Standard: Auto", Standard, FormatStyle::LS_Auto); + Style.UseTab = FormatStyle::UT_ForIndentation; + CHECK_PARSE("UseTab: false", UseTab, FormatStyle::UT_Never); + CHECK_PARSE("UseTab: true", UseTab, FormatStyle::UT_Always); + CHECK_PARSE("UseTab: Never", UseTab, FormatStyle::UT_Never); + CHECK_PARSE("UseTab: ForIndentation", UseTab, FormatStyle::UT_ForIndentation); + CHECK_PARSE("UseTab: Always", UseTab, FormatStyle::UT_Always); + Style.ColumnLimit = 123; FormatStyle BaseStyle = getLLVMStyle(); CHECK_PARSE("BasedOnStyle: LLVM", ColumnLimit, BaseStyle.ColumnLimit); -- 2.40.0