Index: lib/Format/BreakableToken.h =================================================================== --- lib/Format/BreakableToken.h +++ lib/Format/BreakableToken.h @@ -149,14 +149,12 @@ virtual void updateNextToken(LineState &State) const {} protected: - BreakableToken(const FormatToken &Tok, unsigned IndentLevel, - bool InPPDirective, encoding::Encoding Encoding, - const FormatStyle &Style) - : Tok(Tok), IndentLevel(IndentLevel), InPPDirective(InPPDirective), - Encoding(Encoding), Style(Style) {} + BreakableToken(const FormatToken &Tok, bool InPPDirective, + encoding::Encoding Encoding, const FormatStyle &Style) + : Tok(Tok), InPPDirective(InPPDirective), Encoding(Encoding), + Style(Style) {} const FormatToken &Tok; - const unsigned IndentLevel; const bool InPPDirective; const encoding::Encoding Encoding; const FormatStyle &Style; @@ -172,10 +170,9 @@ StringRef::size_type Length) const override; protected: - BreakableSingleLineToken(const FormatToken &Tok, unsigned IndentLevel, - unsigned StartColumn, StringRef Prefix, - StringRef Postfix, bool InPPDirective, - encoding::Encoding Encoding, + BreakableSingleLineToken(const FormatToken &Tok, unsigned StartColumn, + StringRef Prefix, StringRef Postfix, + bool InPPDirective, encoding::Encoding Encoding, const FormatStyle &Style); // The column in which the token starts. @@ -194,10 +191,10 @@ /// /// \p StartColumn specifies the column in which the token will start /// after formatting. - BreakableStringLiteral(const FormatToken &Tok, unsigned IndentLevel, - unsigned StartColumn, StringRef Prefix, - StringRef Postfix, bool InPPDirective, - encoding::Encoding Encoding, const FormatStyle &Style); + BreakableStringLiteral(const FormatToken &Tok, unsigned StartColumn, + StringRef Prefix, StringRef Postfix, + bool InPPDirective, encoding::Encoding Encoding, + const FormatStyle &Style); Split getSplit(unsigned LineIndex, unsigned TailOffset, unsigned ColumnLimit) const override; @@ -215,10 +212,10 @@ /// 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. - BreakableComment(const FormatToken &Token, unsigned IndentLevel, - unsigned StartColumn, unsigned OriginalStartColumn, - bool FirstInLine, bool InPPDirective, - encoding::Encoding Encoding, const FormatStyle &Style); + BreakableComment(const FormatToken &Token, unsigned StartColumn, + unsigned OriginalStartColumn, bool FirstInLine, + bool InPPDirective, encoding::Encoding Encoding, + const FormatStyle &Style); public: unsigned getLineCount() const override; @@ -299,10 +296,10 @@ class BreakableBlockComment : public BreakableComment { public: - BreakableBlockComment(const FormatToken &Token, unsigned IndentLevel, - unsigned StartColumn, unsigned OriginalStartColumn, - bool FirstInLine, bool InPPDirective, - encoding::Encoding Encoding, const FormatStyle &Style); + BreakableBlockComment(const FormatToken &Token, unsigned StartColumn, + unsigned OriginalStartColumn, bool FirstInLine, + bool InPPDirective, encoding::Encoding Encoding, + const FormatStyle &Style); unsigned getLineLengthAfterSplit(unsigned LineIndex, unsigned TailOffset, @@ -363,8 +360,7 @@ class BreakableLineCommentSection : public BreakableComment { public: - BreakableLineCommentSection(const FormatToken &Token, unsigned IndentLevel, - unsigned StartColumn, + BreakableLineCommentSection(const FormatToken &Token, unsigned StartColumn, unsigned OriginalStartColumn, bool FirstInLine, bool InPPDirective, encoding::Encoding Encoding, const FormatStyle &Style); Index: lib/Format/BreakableToken.cpp =================================================================== --- lib/Format/BreakableToken.cpp +++ lib/Format/BreakableToken.cpp @@ -181,10 +181,10 @@ } BreakableSingleLineToken::BreakableSingleLineToken( - 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), + const FormatToken &Tok, unsigned StartColumn, StringRef Prefix, + StringRef Postfix, bool InPPDirective, encoding::Encoding Encoding, + const FormatStyle &Style) + : BreakableToken(Tok, InPPDirective, Encoding, Style), StartColumn(StartColumn), Prefix(Prefix), Postfix(Postfix) { assert(Tok.TokenText.endswith(Postfix)); Line = Tok.TokenText.substr( @@ -192,11 +192,11 @@ } BreakableStringLiteral::BreakableStringLiteral( - 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) {} + 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) {} BreakableToken::Split BreakableStringLiteral::getSplit(unsigned LineIndex, unsigned TailOffset, @@ -218,16 +218,16 @@ --LeadingSpaces; Whitespaces.replaceWhitespaceInToken( Tok, Prefix.size() + TailOffset + Split.first, Split.second, Postfix, - Prefix, InPPDirective, 1, IndentLevel, LeadingSpaces); + Prefix, InPPDirective, 1, LeadingSpaces); } BreakableComment::BreakableComment(const FormatToken &Token, - unsigned IndentLevel, unsigned StartColumn, + unsigned StartColumn, unsigned OriginalStartColumn, bool FirstInLine, bool InPPDirective, encoding::Encoding Encoding, const FormatStyle &Style) - : BreakableToken(Token, IndentLevel, InPPDirective, Encoding, Style), + : BreakableToken(Token, InPPDirective, Encoding, Style), StartColumn(StartColumn), OriginalStartColumn(OriginalStartColumn), FirstInLine(FirstInLine) {} @@ -254,8 +254,7 @@ unsigned CharsToRemove = Split.second; Whitespaces.replaceWhitespaceInToken( tokenAt(LineIndex), BreakOffsetInToken, CharsToRemove, "", "", - /*InPPDirective=*/false, - /*Newlines=*/0, /*IndentLevel=*/0, /*Spaces=*/1); + /*InPPDirective=*/false, /*Newlines=*/0, /*Spaces=*/1); } BreakableToken::Split @@ -319,11 +318,11 @@ } BreakableBlockComment::BreakableBlockComment( - const FormatToken &Token, unsigned IndentLevel, unsigned StartColumn, + const FormatToken &Token, unsigned StartColumn, unsigned OriginalStartColumn, bool FirstInLine, bool InPPDirective, encoding::Encoding Encoding, const FormatStyle &Style) - : BreakableComment(Token, IndentLevel, StartColumn, OriginalStartColumn, - FirstInLine, InPPDirective, Encoding, Style) { + : BreakableComment(Token, StartColumn, OriginalStartColumn, FirstInLine, + InPPDirective, Encoding, Style) { assert(Tok.is(TT_BlockComment) && "block comment section must start with a block comment"); @@ -484,7 +483,7 @@ assert(LocalIndentAtLineBreak >= Prefix.size()); Whitespaces.replaceWhitespaceInToken( tokenAt(LineIndex), BreakOffsetInToken, CharsToRemove, "", Prefix, - InPPDirective, /*Newlines=*/1, IndentLevel, + InPPDirective, /*Newlines=*/1, /*Spaces=*/LocalIndentAtLineBreak - Prefix.size()); } @@ -555,10 +554,9 @@ WhitespaceOffsetInToken; Whitespaces.replaceWhitespaceInToken( tokenAt(LineIndex), WhitespaceOffsetInToken, - /*ReplaceChars=*/WhitespaceLength, - /*PreviousPostfix=*/"", - /*CurrentPrefix=*/ReflowPrefix, InPPDirective, - /*Newlines=*/0, IndentLevel, /*Spaces=*/0); + /*ReplaceChars=*/WhitespaceLength, /*PreviousPostfix=*/"", + /*CurrentPrefix=*/ReflowPrefix, InPPDirective, /*Newlines=*/0, + /*Spaces=*/0); // Check if we need to also insert a break at the whitespace range. // For this we first adapt the reflow split relative to the beginning of the // content. @@ -604,9 +602,8 @@ tokenAt(LineIndex).TokenText.data() - WhitespaceOffsetInToken; Whitespaces.replaceWhitespaceInToken( - tokenAt(LineIndex), WhitespaceOffsetInToken, WhitespaceLength, "", - Prefix, InPPDirective, /*Newlines=*/1, IndentLevel, - ContentColumn[LineIndex] - Prefix.size()); + tokenAt(LineIndex), WhitespaceOffsetInToken, WhitespaceLength, "", Prefix, + InPPDirective, /*Newlines=*/1, ContentColumn[LineIndex] - Prefix.size()); } unsigned @@ -619,11 +616,11 @@ } BreakableLineCommentSection::BreakableLineCommentSection( - const FormatToken &Token, unsigned IndentLevel, unsigned StartColumn, + const FormatToken &Token, unsigned StartColumn, unsigned OriginalStartColumn, bool FirstInLine, bool InPPDirective, encoding::Encoding Encoding, const FormatStyle &Style) - : BreakableComment(Token, IndentLevel, StartColumn, OriginalStartColumn, - FirstInLine, InPPDirective, Encoding, Style) { + : BreakableComment(Token, StartColumn, OriginalStartColumn, FirstInLine, + InPPDirective, Encoding, Style) { assert(Tok.is(TT_LineComment) && "line comment section must start with a line comment"); FormatToken *LineTok = nullptr; @@ -709,7 +706,7 @@ assert(IndentAtLineBreak >= Prefix[LineIndex].size()); Whitespaces.replaceWhitespaceInToken( tokenAt(LineIndex), BreakOffsetInToken, CharsToRemove, "", - Prefix[LineIndex], InPPDirective, /*Newlines=*/1, IndentLevel, + Prefix[LineIndex], InPPDirective, /*Newlines=*/1, /*Spaces=*/IndentAtLineBreak - Prefix[LineIndex].size()); } @@ -754,12 +751,9 @@ if (SplitBefore.first != StringRef::npos) { // Reflow happens between tokens. Replace the whitespace between the // tokens by the empty string. - Whitespaces.replaceWhitespace(*Tokens[LineIndex], - /*Newlines=*/0, - /*IndentLevel=*/IndentLevel, - /*Spaces=*/0, - /*StartOfTokenColumn=*/StartColumn, - /*InPPDirective=*/false); + Whitespaces.replaceWhitespace( + *Tokens[LineIndex], /*Newlines=*/0, /*Spaces=*/0, + /*StartOfTokenColumn=*/StartColumn, /*InPPDirective=*/false); // Replace the indent and prefix of the token with the reflow prefix. unsigned WhitespaceLength = Content[LineIndex].data() - tokenAt(LineIndex).TokenText.data(); @@ -770,7 +764,6 @@ /*CurrentPrefix=*/ReflowPrefix, /*InPPDirective=*/false, /*Newlines=*/0, - /*IndentLevel=*/IndentLevel, /*Spaces=*/0); } else { // This is the first line for the current token, but no reflow with the @@ -782,7 +775,6 @@ if (tokenAt(LineIndex).OriginalColumn != LineColumn) { Whitespaces.replaceWhitespace(*Tokens[LineIndex], /*Newlines=*/1, - /*IndentLevel=*/IndentLevel, /*Spaces=*/LineColumn, /*StartOfTokenColumn=*/LineColumn, /*InPPDirective=*/false); @@ -802,9 +794,7 @@ "at most a space"); Whitespaces.replaceWhitespaceInToken( tokenAt(LineIndex), OriginalPrefix[LineIndex].size(), 0, "", "", - /*InPPDirective=*/false, - /*Newlines=*/0, /*IndentLevel=*/0, - /*Spaces=*/1); + /*InPPDirective=*/false, /*Newlines=*/0, /*Spaces=*/1); } // Add a break after a reflow split has been introduced, if necessary. // Note that this break doesn't need to be penalized, since it doesn't change Index: lib/Format/ContinuationIndenter.h =================================================================== --- lib/Format/ContinuationIndenter.h +++ lib/Format/ContinuationIndenter.h @@ -146,24 +146,20 @@ }; struct ParenState { - ParenState(unsigned Indent, unsigned IndentLevel, unsigned LastSpace, - bool AvoidBinPacking, bool NoLineBreak) - : Indent(Indent), IndentLevel(IndentLevel), LastSpace(LastSpace), - NestedBlockIndent(Indent), BreakBeforeClosingBrace(false), - AvoidBinPacking(AvoidBinPacking), BreakBeforeParameter(false), - NoLineBreak(NoLineBreak), NoLineBreakInOperand(false), - LastOperatorWrapped(true), ContainsLineBreak(false), - ContainsUnwrappedBuilder(false), AlignColons(true), - ObjCSelectorNameFound(false), HasMultipleNestedBlocks(false), - NestedBlockInlined(false) {} + ParenState(unsigned Indent, unsigned LastSpace, bool AvoidBinPacking, + bool NoLineBreak) + : Indent(Indent), LastSpace(LastSpace), NestedBlockIndent(Indent), + BreakBeforeClosingBrace(false), AvoidBinPacking(AvoidBinPacking), + BreakBeforeParameter(false), NoLineBreak(NoLineBreak), + NoLineBreakInOperand(false), LastOperatorWrapped(true), + ContainsLineBreak(false), ContainsUnwrappedBuilder(false), + AlignColons(true), ObjCSelectorNameFound(false), + HasMultipleNestedBlocks(false), NestedBlockInlined(false) {} /// \brief The position to which a specific parenthesis level needs to be /// indented. unsigned Indent; - /// \brief The number of indentation levels of the block. - unsigned IndentLevel; - /// \brief The position of the last space on each level. /// /// Used e.g. to break like: Index: lib/Format/ContinuationIndenter.cpp =================================================================== --- lib/Format/ContinuationIndenter.cpp +++ lib/Format/ContinuationIndenter.cpp @@ -80,7 +80,7 @@ State.Column = FirstIndent; State.Line = Line; State.NextToken = Line->First; - State.Stack.push_back(ParenState(FirstIndent, Line->Level, FirstIndent, + State.Stack.push_back(ParenState(FirstIndent, FirstIndent, /*AvoidBinPacking=*/false, /*NoLineBreak=*/false)); State.LineContainsContinuedForLoopSection = false; @@ -199,7 +199,14 @@ if (startsSegmentOfBuilderTypeCall(Current) && (State.Stack.back().CallContinuation != 0 || - State.Stack.back().BreakBeforeParameter)) + State.Stack.back().BreakBeforeParameter) && + // JavaScript is treated different here as there is a frequent pattern: + // SomeFunction(function() { + // ... + // }.bind(...)); + // FIXME: We should find a more generic solution to this problem. + !(State.Column <= NewLineColumn && + Style.Language == FormatStyle::LK_JavaScript)) return true; if (State.Column <= NewLineColumn) @@ -340,8 +347,8 @@ unsigned Spaces = Current.SpacesRequiredBefore + ExtraSpaces; if (!DryRun) - Whitespaces.replaceWhitespace(Current, /*Newlines=*/0, /*IndentLevel=*/0, - Spaces, State.Column + Spaces); + Whitespaces.replaceWhitespace(Current, /*Newlines=*/0, Spaces, + State.Column + Spaces); if (Current.is(TT_SelectorName) && !State.Stack.back().ObjCSelectorNameFound) { @@ -567,9 +574,8 @@ if (!DryRun) { unsigned Newlines = std::max( 1u, std::min(Current.NewlinesBefore, Style.MaxEmptyLinesToKeep + 1)); - Whitespaces.replaceWhitespace(Current, Newlines, - State.Stack.back().IndentLevel, State.Column, - State.Column, State.Line->InPPDirective); + Whitespaces.replaceWhitespace(Current, Newlines, State.Column, State.Column, + State.Line->InPPDirective); } if (!Current.isTrailingComment()) @@ -946,7 +952,6 @@ } unsigned NewIndent; - unsigned NewIndentLevel = State.Stack.back().IndentLevel; unsigned LastSpace = State.Stack.back().LastSpace; bool AvoidBinPacking; bool BreakBeforeParameter = false; @@ -956,7 +961,6 @@ if (Current.opensBlockOrBlockTypeList(Style)) { NewIndent = State.Stack.back().NestedBlockIndent + Style.IndentWidth; NewIndent = std::min(State.Column + 2, NewIndent); - ++NewIndentLevel; } else { NewIndent = State.Stack.back().LastSpace + Style.ContinuationIndentWidth; } @@ -1025,8 +1029,8 @@ State.Stack.back().NoLineBreakInOperand || (Current.is(TT_TemplateOpener) && State.Stack.back().ContainsUnwrappedBuilder)); - State.Stack.push_back(ParenState(NewIndent, NewIndentLevel, LastSpace, - AvoidBinPacking, NoLineBreak)); + State.Stack.push_back( + ParenState(NewIndent, LastSpace, AvoidBinPacking, NoLineBreak)); State.Stack.back().NestedBlockIndent = NestedBlockIndent; State.Stack.back().BreakBeforeParameter = BreakBeforeParameter; State.Stack.back().HasMultipleNestedBlocks = Current.BlockParameterCount > 1; @@ -1060,10 +1064,9 @@ NestedBlockIndent + (State.NextToken->is(TT_ObjCBlockLBrace) ? Style.ObjCBlockIndentWidth : Style.IndentWidth); - State.Stack.push_back(ParenState( - NewIndent, /*NewIndentLevel=*/State.Stack.back().IndentLevel + 1, - State.Stack.back().LastSpace, /*AvoidBinPacking=*/true, - /*NoLineBreak=*/false)); + State.Stack.push_back(ParenState(NewIndent, State.Stack.back().LastSpace, + /*AvoidBinPacking=*/true, + /*NoLineBreak=*/false)); State.Stack.back().NestedBlockIndent = NestedBlockIndent; State.Stack.back().BreakBeforeParameter = true; } @@ -1146,9 +1149,9 @@ Text.startswith(Prefix = "u8\"") || Text.startswith(Prefix = "L\""))) || (Text.startswith(Prefix = "_T(\"") && Text.endswith(Postfix = "\")"))) { - Token.reset(new BreakableStringLiteral( - Current, State.Line->Level, StartColumn, Prefix, Postfix, - State.Line->InPPDirective, Encoding, Style)); + Token.reset(new BreakableStringLiteral(Current, StartColumn, Prefix, + Postfix, State.Line->InPPDirective, + Encoding, Style)); } else { return 0; } @@ -1161,8 +1164,8 @@ switchesFormatting(Current)) return addMultilineToken(Current, State); Token.reset(new BreakableBlockComment( - Current, State.Line->Level, StartColumn, Current.OriginalColumn, - !Current.Previous, State.Line->InPPDirective, Encoding, Style)); + Current, StartColumn, Current.OriginalColumn, !Current.Previous, + State.Line->InPPDirective, Encoding, Style)); } else if (Current.is(TT_LineComment) && (Current.Previous == nullptr || Current.Previous->isNot(TT_ImplicitStringLiteral))) { @@ -1171,8 +1174,7 @@ switchesFormatting(Current)) return 0; Token.reset(new BreakableLineCommentSection( - Current, State.Line->Level, StartColumn, Current.OriginalColumn, - !Current.Previous, + Current, StartColumn, Current.OriginalColumn, !Current.Previous, /*InPPDirective=*/false, Encoding, Style)); // We don't insert backslashes when breaking line comments. ColumnLimit = Style.ColumnLimit; Index: lib/Format/FormatToken.h =================================================================== --- lib/Format/FormatToken.h +++ lib/Format/FormatToken.h @@ -220,6 +220,9 @@ /// [], {} or <>. unsigned NestingLevel = 0; + /// \brief The indent level of this token. Copied from the surrounding line. + unsigned IndentLevel = 0; + /// \brief Penalty for inserting a line break before this token. unsigned SplitPenalty = 0; Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -1759,8 +1759,6 @@ Line.First->TotalLength = Line.First->IsMultiline ? Style.ColumnLimit : Line.First->ColumnWidth; - if (!Line.First->Next) - return; FormatToken *Current = Line.First->Next; bool InFunctionDecl = Line.MightBeFunctionDecl; while (Current) { @@ -1836,9 +1834,16 @@ } calculateUnbreakableTailLengths(Line); + unsigned IndentLevel = Line.Level; for (Current = Line.First; Current != nullptr; Current = Current->Next) { if (Current->Role) Current->Role->precomputeFormattingInfos(Current); + if (Current->MatchingParen && + Current->MatchingParen->opensBlockOrBlockTypeList(Style)) + --IndentLevel; + Current->IndentLevel = IndentLevel; + if (Current->opensBlockOrBlockTypeList(Style)) + ++IndentLevel; } DEBUG({ printDebugInfo(Line); }); Index: lib/Format/UnwrappedLineFormatter.h =================================================================== --- lib/Format/UnwrappedLineFormatter.h +++ lib/Format/UnwrappedLineFormatter.h @@ -44,9 +44,8 @@ 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. - void formatFirstToken(FormatToken &RootToken, - const AnnotatedLine *PreviousLine, unsigned IndentLevel, - unsigned Indent, bool InPPDirective); + void formatFirstToken(const AnnotatedLine &Line, + const AnnotatedLine *PreviousLine, unsigned Indent); /// \brief Returns the column limit for a line, taking into account whether we /// need an escaped newline due to a continued preprocessor directive. @@ -57,7 +56,8 @@ // starting from a specific additional offset. Improves performance if there // are many nested blocks. std::map *, unsigned>, - unsigned> PenaltyCache; + unsigned> + PenaltyCache; ContinuationIndenter *Indenter; WhitespaceManager *Whitespaces; Index: lib/Format/UnwrappedLineFormatter.cpp =================================================================== --- lib/Format/UnwrappedLineFormatter.cpp +++ lib/Format/UnwrappedLineFormatter.cpp @@ -530,34 +530,33 @@ if (Previous.Children[0]->First->MustBreakBefore) return false; - // Cannot merge multiple statements into a single line. - if (Previous.Children.size() > 1) - return false; - // Cannot merge into one line if this line ends on a comment. if (Previous.is(tok::comment)) return false; + // Cannot merge multiple statements into a single line. + if (Previous.Children.size() > 1) + return false; + + const AnnotatedLine *Child = Previous.Children[0]; // We can't put the closing "}" on a line with a trailing comment. - if (Previous.Children[0]->Last->isTrailingComment()) + if (Child->Last->isTrailingComment()) return false; // If the child line exceeds the column limit, we wouldn't want to merge it. // We add +2 for the trailing " }". if (Style.ColumnLimit > 0 && - Previous.Children[0]->Last->TotalLength + State.Column + 2 > - Style.ColumnLimit) + Child->Last->TotalLength + State.Column + 2 > Style.ColumnLimit) return false; if (!DryRun) { Whitespaces->replaceWhitespace( - *Previous.Children[0]->First, - /*Newlines=*/0, /*IndentLevel=*/0, /*Spaces=*/1, + *Child->First, /*Newlines=*/0, /*Spaces=*/1, /*StartOfTokenColumn=*/State.Column, State.Line->InPPDirective); } - Penalty += formatLine(*Previous.Children[0], State.Column + 1, DryRun); + Penalty += formatLine(*Child, State.Column + 1, DryRun); - State.Column += 1 + Previous.Children[0]->Last->TotalLength; + State.Column += 1 + Child->Last->TotalLength; return true; } @@ -841,8 +840,7 @@ if (ShouldFormat && TheLine.Type != LT_Invalid) { if (!DryRun) - formatFirstToken(*TheLine.First, PreviousLine, TheLine.Level, Indent, - TheLine.InPPDirective); + formatFirstToken(TheLine, PreviousLine, Indent); NextLine = Joiner.getNextMergedLine(DryRun, IndentTracker); unsigned ColumnLimit = getColumnLimit(TheLine.InPPDirective, NextLine); @@ -882,9 +880,8 @@ TheLine.LeadingEmptyLinesAffected); // Format the first token. if (ReformatLeadingWhitespace) - formatFirstToken(*TheLine.First, PreviousLine, TheLine.Level, - TheLine.First->OriginalColumn, - TheLine.InPPDirective); + formatFirstToken(TheLine, PreviousLine, + TheLine.First->OriginalColumn); else Whitespaces->addUntouchableToken(*TheLine.First, TheLine.InPPDirective); @@ -904,15 +901,14 @@ return Penalty; } -void UnwrappedLineFormatter::formatFirstToken(FormatToken &RootToken, +void UnwrappedLineFormatter::formatFirstToken(const AnnotatedLine &Line, const AnnotatedLine *PreviousLine, - unsigned IndentLevel, - unsigned Indent, - bool InPPDirective) { + unsigned Indent) { + FormatToken& RootToken = *Line.First; if (RootToken.is(tok::eof)) { unsigned Newlines = std::min(RootToken.NewlinesBefore, 1u); - Whitespaces->replaceWhitespace(RootToken, Newlines, /*IndentLevel=*/0, - /*Spaces=*/0, /*TargetColumn=*/0); + Whitespaces->replaceWhitespace(RootToken, Newlines, /*Spaces=*/0, + /*TargetColumn=*/0); return; } unsigned Newlines = @@ -944,9 +940,9 @@ (!PreviousLine->InPPDirective || !RootToken.HasUnescapedNewline)) Newlines = std::min(1u, Newlines); - Whitespaces->replaceWhitespace(RootToken, Newlines, IndentLevel, Indent, - Indent, InPPDirective && - !RootToken.HasUnescapedNewline); + Whitespaces->replaceWhitespace(RootToken, Newlines, Indent, Indent, + Line.InPPDirective && + !RootToken.HasUnescapedNewline); } unsigned Index: lib/Format/WhitespaceManager.h =================================================================== --- lib/Format/WhitespaceManager.h +++ lib/Format/WhitespaceManager.h @@ -43,8 +43,7 @@ /// \brief Replaces the whitespace in front of \p Tok. Only call once for /// each \c AnnotatedToken. - void replaceWhitespace(FormatToken &Tok, unsigned Newlines, - unsigned IndentLevel, unsigned Spaces, + void replaceWhitespace(FormatToken &Tok, unsigned Newlines, unsigned Spaces, unsigned StartOfTokenColumn, bool InPPDirective = false); @@ -72,8 +71,7 @@ unsigned ReplaceChars, StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective, - unsigned Newlines, unsigned IndentLevel, - int Spaces); + unsigned Newlines, int Spaces); /// \brief Returns all the \c Replacements created during formatting. const tooling::Replacements &generateReplacements(); @@ -91,8 +89,6 @@ const SourceManager &SourceMgr; }; - Change() {} - /// \brief Creates a \c Change. /// /// The generated \c Change will replace the characters at @@ -102,12 +98,18 @@ /// /// \p StartOfTokenColumn and \p InPPDirective will be used to lay out /// trailing comments and escaped newlines. - Change(bool CreateReplacement, SourceRange OriginalWhitespaceRange, - unsigned IndentLevel, int Spaces, unsigned StartOfTokenColumn, - unsigned NewlinesBefore, StringRef PreviousLinePostfix, - StringRef CurrentLinePrefix, tok::TokenKind Kind, - bool ContinuesPPDirective, bool IsStartOfDeclName, - bool IsInsideToken); + Change(const FormatToken &Tok, bool CreateReplacement, + SourceRange OriginalWhitespaceRange, int Spaces, + unsigned StartOfTokenColumn, unsigned NewlinesBefore, + StringRef PreviousLinePostfix, StringRef CurrentLinePrefix, + bool ContinuesPPDirective, bool IsInsideToken); + + // The kind of the token whose whitespace this change replaces, or in which + // this change inserts whitespace. + // FIXME: Currently this is not set correctly for breaks inside comments, as + // the \c BreakableToken is still doing its own alignment. + const FormatToken *Tok; + bool InToken; bool CreateReplacement; // Changes might be in the middle of a token, so we cannot just keep the @@ -117,18 +119,7 @@ unsigned NewlinesBefore; std::string PreviousLinePostfix; std::string CurrentLinePrefix; - // The kind of the token whose whitespace this change replaces, or in which - // this change inserts whitespace. - // FIXME: Currently this is not set correctly for breaks inside comments, as - // the \c BreakableToken is still doing its own alignment. - tok::TokenKind Kind; bool ContinuesPPDirective; - bool IsStartOfDeclName; - - // 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. Index: lib/Format/WhitespaceManager.cpp =================================================================== --- lib/Format/WhitespaceManager.cpp +++ lib/Format/WhitespaceManager.cpp @@ -25,64 +25,60 @@ C2.OriginalWhitespaceRange.getBegin()); } -WhitespaceManager::Change::Change( - bool CreateReplacement, SourceRange OriginalWhitespaceRange, - unsigned IndentLevel, int Spaces, unsigned StartOfTokenColumn, - unsigned NewlinesBefore, StringRef PreviousLinePostfix, - StringRef CurrentLinePrefix, tok::TokenKind Kind, bool ContinuesPPDirective, - bool IsStartOfDeclName, bool IsInsideToken) - : CreateReplacement(CreateReplacement), +WhitespaceManager::Change::Change(const FormatToken &Tok, + bool CreateReplacement, + SourceRange OriginalWhitespaceRange, + int Spaces, unsigned StartOfTokenColumn, + unsigned NewlinesBefore, + StringRef PreviousLinePostfix, + StringRef CurrentLinePrefix, + bool ContinuesPPDirective, bool IsInsideToken) + : Tok(&Tok), CreateReplacement(CreateReplacement), OriginalWhitespaceRange(OriginalWhitespaceRange), StartOfTokenColumn(StartOfTokenColumn), NewlinesBefore(NewlinesBefore), PreviousLinePostfix(PreviousLinePostfix), - CurrentLinePrefix(CurrentLinePrefix), Kind(Kind), - ContinuesPPDirective(ContinuesPPDirective), - IsStartOfDeclName(IsStartOfDeclName), IndentLevel(IndentLevel), - Spaces(Spaces), IsInsideToken(IsInsideToken), IsTrailingComment(false), - TokenLength(0), PreviousEndOfTokenColumn(0), EscapedNewlineColumn(0), + CurrentLinePrefix(CurrentLinePrefix), + ContinuesPPDirective(ContinuesPPDirective), Spaces(Spaces), + IsInsideToken(IsInsideToken), IsTrailingComment(false), TokenLength(0), + PreviousEndOfTokenColumn(0), EscapedNewlineColumn(0), StartOfBlockComment(nullptr), IndentationOffset(0) {} void WhitespaceManager::replaceWhitespace(FormatToken &Tok, unsigned Newlines, - unsigned IndentLevel, unsigned Spaces, + unsigned Spaces, unsigned StartOfTokenColumn, bool InPPDirective) { if (Tok.Finalized) return; Tok.Decision = (Newlines > 0) ? FD_Break : FD_Continue; - Changes.push_back( - Change(/*CreateReplacement=*/true, Tok.WhitespaceRange, IndentLevel, - Spaces, StartOfTokenColumn, Newlines, "", "", Tok.Tok.getKind(), - InPPDirective && !Tok.IsFirst, - Tok.is(TT_StartOfName) || Tok.is(TT_FunctionDeclarationName), - /*IsInsideToken=*/false)); + Changes.push_back(Change(Tok, /*CreateReplacement=*/true, + Tok.WhitespaceRange, Spaces, StartOfTokenColumn, + Newlines, "", "", InPPDirective && !Tok.IsFirst, + /*IsInsideToken=*/false)); } void WhitespaceManager::addUntouchableToken(const FormatToken &Tok, bool InPPDirective) { if (Tok.Finalized) return; - Changes.push_back(Change( - /*CreateReplacement=*/false, Tok.WhitespaceRange, /*IndentLevel=*/0, - /*Spaces=*/0, Tok.OriginalColumn, Tok.NewlinesBefore, "", "", - Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst, - Tok.is(TT_StartOfName) || Tok.is(TT_FunctionDeclarationName), - /*IsInsideToken=*/false)); + Changes.push_back(Change(Tok, /*CreateReplacement=*/false, + Tok.WhitespaceRange, /*Spaces=*/0, + Tok.OriginalColumn, Tok.NewlinesBefore, "", "", + InPPDirective && !Tok.IsFirst, + /*IsInsideToken=*/false)); } void WhitespaceManager::replaceWhitespaceInToken( const FormatToken &Tok, unsigned Offset, unsigned ReplaceChars, StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective, - unsigned Newlines, unsigned IndentLevel, int Spaces) { + unsigned Newlines, int Spaces) { if (Tok.Finalized) return; SourceLocation Start = Tok.getStartOfNonWhitespace().getLocWithOffset(Offset); - Changes.push_back(Change( - true, SourceRange(Start, Start.getLocWithOffset(ReplaceChars)), - IndentLevel, Spaces, std::max(0, Spaces), Newlines, PreviousPostfix, - CurrentPrefix, Tok.is(TT_LineComment) ? tok::comment : tok::unknown, - InPPDirective && !Tok.IsFirst, - Tok.is(TT_StartOfName) || Tok.is(TT_FunctionDeclarationName), - /*IsInsideToken=*/Newlines == 0)); + Changes.push_back( + Change(Tok, /*CreateReplacement=*/true, + SourceRange(Start, Start.getLocWithOffset(ReplaceChars)), Spaces, + std::max(0, Spaces), Newlines, PreviousPostfix, CurrentPrefix, + InPPDirective && !Tok.IsFirst, /*IsInsideToken=*/true)); } const tooling::Replacements &WhitespaceManager::generateReplacements() { @@ -125,9 +121,9 @@ Changes[i - 1].StartOfTokenColumn + Changes[i - 1].TokenLength; Changes[i - 1].IsTrailingComment = - (Changes[i].NewlinesBefore > 0 || Changes[i].Kind == tok::eof || - (Changes[i].IsInsideToken && Changes[i].Kind == tok::comment)) && - Changes[i - 1].Kind == tok::comment && + (Changes[i].NewlinesBefore > 0 || Changes[i].Tok->is(tok::eof) || + (Changes[i].IsInsideToken && Changes[i].Tok->is(tok::comment))) && + Changes[i - 1].Tok->is(tok::comment) && // FIXME: This is a dirty hack. The problem is that // BreakableLineCommentSection does comment reflow changes and here is // the aligning of trailing comments. Consider the case where we reflow @@ -163,7 +159,7 @@ // FIXME: The last token is currently not always an eof token; in those // cases, setting TokenLength of the last token to 0 is wrong. Changes.back().TokenLength = 0; - Changes.back().IsTrailingComment = Changes.back().Kind == tok::comment; + Changes.back().IsTrailingComment = Changes.back().Tok->is(tok::comment); const WhitespaceManager::Change *LastBlockComment = nullptr; for (auto &Change : Changes) { @@ -173,13 +169,15 @@ Change.IsTrailingComment = false; Change.StartOfBlockComment = nullptr; Change.IndentationOffset = 0; - if (Change.Kind == tok::comment) { - LastBlockComment = &Change; - } else if (Change.Kind == tok::unknown) { - if ((Change.StartOfBlockComment = LastBlockComment)) - Change.IndentationOffset = - Change.StartOfTokenColumn - - Change.StartOfBlockComment->StartOfTokenColumn; + if (Change.Tok->is(tok::comment)) { + if (Change.Tok->is(TT_LineComment) || !Change.IsInsideToken) + LastBlockComment = &Change; + else { + if ((Change.StartOfBlockComment = LastBlockComment)) + Change.IndentationOffset = + Change.StartOfTokenColumn - + Change.StartOfBlockComment->StartOfTokenColumn; + } } else { LastBlockComment = nullptr; } @@ -278,15 +276,13 @@ FoundMatchOnLine = false; } - if (Changes[i].Kind == tok::comma) { + if (Changes[i].Tok->is(tok::comma)) { ++CommasBeforeMatch; - } else if (Changes[i].Kind == tok::r_brace || - Changes[i].Kind == tok::r_paren || - Changes[i].Kind == tok::r_square) { + } else if (Changes[i].Tok->isOneOf(tok::r_brace, tok::r_paren, + tok::r_square)) { --NestingLevel; - } else if (Changes[i].Kind == tok::l_brace || - Changes[i].Kind == tok::l_paren || - Changes[i].Kind == tok::l_square) { + } else if (Changes[i].Tok->isOneOf(tok::l_brace, tok::l_paren, + tok::l_square)) { // We want sequences to skip over child scopes if possible, but not the // other way around. NestingLevelOfLastMatch = std::min(NestingLevelOfLastMatch, NestingLevel); @@ -345,7 +341,7 @@ if (&C != &Changes.back() && (&C + 1)->NewlinesBefore > 0) return false; - return C.Kind == tok::equal; + return C.Tok->is(tok::equal); }, Changes); } @@ -361,7 +357,11 @@ // float const* v2; // SomeVeryLongType const& v3; - AlignTokens(Style, [](Change const &C) { return C.IsStartOfDeclName; }, + AlignTokens(Style, + [](Change const &C) { + return C.Tok->isOneOf(TT_StartOfName, + TT_FunctionDeclarationName); + }, Changes); } @@ -391,17 +391,14 @@ // If this comment follows an } in column 0, it probably documents the // closing of a namespace and we don't want to align it. bool FollowsRBraceInColumn0 = i > 0 && Changes[i].NewlinesBefore == 0 && - Changes[i - 1].Kind == tok::r_brace && + Changes[i - 1].Tok->is(tok::r_brace) && Changes[i - 1].StartOfTokenColumn == 0; bool WasAlignedWithStartOfNextLine = false; if (Changes[i].NewlinesBefore == 1) { // A comment on its own line. unsigned CommentColumn = SourceMgr.getSpellingColumnNumber( Changes[i].OriginalWhitespaceRange.getEnd()); for (unsigned j = i + 1; j != e; ++j) { - if (Changes[j].Kind == tok::comment || - Changes[j].Kind == tok::unknown) - // Skip over comments and unknown tokens. "unknown tokens are used for - // the continuation of multiline comments. + if (Changes[j].Tok->is(tok::comment)) continue; unsigned NextColumn = SourceMgr.getSpellingColumnNumber( @@ -512,7 +509,8 @@ C.PreviousEndOfTokenColumn, C.EscapedNewlineColumn); else appendNewlineText(ReplacementText, C.NewlinesBefore); - appendIndentText(ReplacementText, C.IndentLevel, std::max(0, C.Spaces), + appendIndentText(ReplacementText, C.Tok->IndentLevel, + std::max(0, C.Spaces), C.StartOfTokenColumn - std::max(0, C.Spaces)); ReplacementText.append(C.CurrentLinePrefix); storeReplacement(C.OriginalWhitespaceRange, ReplacementText); Index: unittests/Format/FormatTestJS.cpp =================================================================== --- unittests/Format/FormatTestJS.cpp +++ unittests/Format/FormatTestJS.cpp @@ -497,6 +497,11 @@ " [];"); verifyFormat("someFunction([], {a: a});"); + + verifyFormat("var string = [\n" + " 'aaaaaa',\n" + " 'bbbbbb',\n" + "].join('+');"); } TEST_F(FormatTestJS, ColumnLayoutForArrayLiterals) { @@ -587,6 +592,11 @@ " doSomething();\n" "}, this));"); + verifyFormat("SomeFunction(function() {\n" + " foo();\n" + " bar();\n" + "}.bind(this));"); + // FIXME: This is bad, we should be wrapping before "function() {". verifyFormat("someFunction(function() {\n" " doSomething(); // break\n"