aboutsummaryrefslogtreecommitdiff
path: root/lib/Format/ContinuationIndenter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Format/ContinuationIndenter.cpp')
-rw-r--r--lib/Format/ContinuationIndenter.cpp759
1 files changed, 495 insertions, 264 deletions
diff --git a/lib/Format/ContinuationIndenter.cpp b/lib/Format/ContinuationIndenter.cpp
index 971acc2b7a3c..014c30e346ad 100644
--- a/lib/Format/ContinuationIndenter.cpp
+++ b/lib/Format/ContinuationIndenter.cpp
@@ -12,8 +12,6 @@
///
//===----------------------------------------------------------------------===//
-#define DEBUG_TYPE "format-formatter"
-
#include "BreakableToken.h"
#include "ContinuationIndenter.h"
#include "WhitespaceManager.h"
@@ -23,13 +21,15 @@
#include "llvm/Support/Debug.h"
#include <string>
+#define DEBUG_TYPE "format-formatter"
+
namespace clang {
namespace format {
// Returns the length of everything up to the first possible line break after
// the ), ], } or > matching \c Tok.
static unsigned getLengthToMatchingParen(const FormatToken &Tok) {
- if (Tok.MatchingParen == NULL)
+ if (!Tok.MatchingParen)
return 0;
FormatToken *End = Tok.MatchingParen;
while (End->Next && !End->Next->CanBreakBefore) {
@@ -63,7 +63,8 @@ ContinuationIndenter::ContinuationIndenter(const FormatStyle &Style,
bool BinPackInconclusiveFunctions)
: Style(Style), SourceMgr(SourceMgr), Whitespaces(Whitespaces),
Encoding(Encoding),
- BinPackInconclusiveFunctions(BinPackInconclusiveFunctions) {}
+ BinPackInconclusiveFunctions(BinPackInconclusiveFunctions),
+ CommentPragmasRegex(Style.CommentPragmas) {}
LineState ContinuationIndenter::getInitialState(unsigned FirstIndent,
const AnnotatedLine *Line,
@@ -77,10 +78,9 @@ LineState ContinuationIndenter::getInitialState(unsigned FirstIndent,
/*AvoidBinPacking=*/false,
/*NoLineBreak=*/false));
State.LineContainsContinuedForLoopSection = false;
- State.ParenLevel = 0;
State.StartOfStringLiteral = 0;
- State.StartOfLineLevel = State.ParenLevel;
- State.LowestLevelOnLine = State.ParenLevel;
+ State.StartOfLineLevel = 0;
+ State.LowestLevelOnLine = 0;
State.IgnoreStackForComparison = false;
// The first token has already been indented and thus consumed.
@@ -98,8 +98,8 @@ bool ContinuationIndenter::canBreak(const LineState &State) {
// The opening "{" of a braced list has to be on the same line as the first
// element if it is nested in another braced init list or function call.
if (!Current.MustBreakBefore && Previous.is(tok::l_brace) &&
- Previous.Type != TT_DictLiteral &&
- Previous.BlockKind == BK_BracedInit && Previous.Previous &&
+ Previous.Type != TT_DictLiteral && Previous.BlockKind == BK_BracedInit &&
+ Previous.Previous &&
Previous.Previous->isOneOf(tok::l_brace, tok::l_paren, tok::comma))
return false;
// This prevents breaks like:
@@ -107,10 +107,21 @@ bool ContinuationIndenter::canBreak(const LineState &State) {
// SomeParameter, OtherParameter).DoSomething(
// ...
// As they hide "DoSomething" and are generally bad for readability.
- if (Previous.opensScope() && State.LowestLevelOnLine < State.StartOfLineLevel)
+ if (Previous.opensScope() && Previous.isNot(tok::l_brace) &&
+ State.LowestLevelOnLine < State.StartOfLineLevel &&
+ State.LowestLevelOnLine < Current.NestingLevel)
return false;
if (Current.isMemberAccess() && State.Stack.back().ContainsUnwrappedBuilder)
return false;
+
+ // Don't create a 'hanging' indent if there are multiple blocks in a single
+ // statement.
+ if (Style.Language == FormatStyle::LK_JavaScript &&
+ Previous.is(tok::l_brace) && State.Stack.size() > 1 &&
+ State.Stack[State.Stack.size() - 2].JSFunctionInlined &&
+ State.Stack[State.Stack.size() - 2].HasMultipleNestedBlocks)
+ return false;
+
return !State.Stack.back().NoLineBreak;
}
@@ -136,13 +147,21 @@ bool ContinuationIndenter::mustBreak(const LineState &State) {
if (Style.AlwaysBreakBeforeMultilineStrings &&
State.Column > State.Stack.back().Indent && // Breaking saves columns.
!Previous.isOneOf(tok::kw_return, tok::lessless, tok::at) &&
- Previous.Type != TT_InlineASMColon && NextIsMultilineString(State))
+ Previous.Type != TT_InlineASMColon &&
+ Previous.Type != TT_ConditionalExpr && nextIsMultilineString(State))
return true;
if (((Previous.Type == TT_DictLiteral && Previous.is(tok::l_brace)) ||
Previous.Type == TT_ArrayInitializerLSquare) &&
+ Style.ColumnLimit > 0 &&
getLengthToMatchingParen(Previous) + State.Column > getColumnLimit(State))
return true;
+ if (Current.Type == TT_CtorInitializerColon &&
+ ((Style.AllowShortFunctionsOnASingleLine != FormatStyle::SFS_All) ||
+ Style.BreakConstructorInitializersBeforeComma || Style.ColumnLimit != 0))
+ return true;
+ if (State.Column < getNewLineColumn(State))
+ return false;
if (!Style.BreakBeforeBinaryOperators) {
// If we need to break somewhere inside the LHS of a binary expression, we
// should also break after the operator. Otherwise, the formatting would
@@ -165,38 +184,45 @@ bool ContinuationIndenter::mustBreak(const LineState &State) {
if (Previous.Type == TT_BinaryOperator &&
(!IsComparison || LHSIsBinaryExpr) &&
Current.Type != TT_BinaryOperator && // For >>.
- !Current.isTrailingComment() &&
- !Previous.isOneOf(tok::lessless, tok::question) &&
+ !Current.isTrailingComment() && !Previous.is(tok::lessless) &&
Previous.getPrecedence() != prec::Assignment &&
State.Stack.back().BreakBeforeParameter)
return true;
}
// Same as above, but for the first "<<" operator.
- if (Current.is(tok::lessless) && State.Stack.back().BreakBeforeParameter &&
+ if (Current.is(tok::lessless) && Current.Type != TT_OverloadedOperator &&
+ State.Stack.back().BreakBeforeParameter &&
State.Stack.back().FirstLessLess == 0)
return true;
- // FIXME: Comparing LongestObjCSelectorName to 0 is a hacky way of finding
- // out whether it is the first parameter. Clean this up.
- if (Current.Type == TT_ObjCSelectorName &&
- Current.LongestObjCSelectorName == 0 &&
+ if (Current.Type == TT_SelectorName &&
+ State.Stack.back().ObjCSelectorNameFound &&
State.Stack.back().BreakBeforeParameter)
return true;
- if ((Current.Type == TT_CtorInitializerColon ||
- (Previous.ClosesTemplateDeclaration && State.ParenLevel == 0 &&
- !Current.isTrailingComment())))
+ if (Previous.ClosesTemplateDeclaration && Current.NestingLevel == 0 &&
+ !Current.isTrailingComment())
return true;
- if ((Current.Type == TT_StartOfName || Current.is(tok::kw_operator)) &&
- State.Line->MightBeFunctionDecl &&
- State.Stack.back().BreakBeforeParameter && State.ParenLevel == 0)
+ // If the return type spans multiple lines, wrap before the function name.
+ if ((Current.Type == TT_FunctionDeclarationName ||
+ Current.is(tok::kw_operator)) &&
+ State.Stack.back().BreakBeforeParameter)
return true;
+
if (startsSegmentOfBuilderTypeCall(Current) &&
(State.Stack.back().CallContinuation != 0 ||
(State.Stack.back().BreakBeforeParameter &&
State.Stack.back().ContainsUnwrappedBuilder)))
return true;
+
+ // The following could be precomputed as they do not depend on the state.
+ // However, as they should take effect only if the UnwrappedLine does not fit
+ // into the ColumnLimit, they are checked here in the ContinuationIndenter.
+ if (Style.ColumnLimit != 0 && Previous.BlockKind == BK_Block &&
+ Previous.is(tok::l_brace) && !Current.isOneOf(tok::r_brace, tok::comment))
+ return true;
+
return false;
}
@@ -205,9 +231,9 @@ unsigned ContinuationIndenter::addTokenToState(LineState &State, bool Newline,
unsigned ExtraSpaces) {
const FormatToken &Current = *State.NextToken;
- if (State.Stack.size() == 0 ||
- (Current.Type == TT_ImplicitStringLiteral &&
- (Current.Previous->Tok.getIdentifierInfo() == NULL ||
+ assert(!State.Stack.empty());
+ if ((Current.Type == TT_ImplicitStringLiteral &&
+ (Current.Previous->Tok.getIdentifierInfo() == nullptr ||
Current.Previous->Tok.getIdentifierInfo()->getPPKeywordID() ==
tok::pp_not_keyword))) {
// FIXME: Is this correct?
@@ -215,8 +241,8 @@ unsigned ContinuationIndenter::addTokenToState(LineState &State, bool Newline,
State.NextToken->WhitespaceRange.getEnd()) -
SourceMgr.getSpellingColumnNumber(
State.NextToken->WhitespaceRange.getBegin());
- State.Column += WhitespaceLength + State.NextToken->ColumnWidth;
- State.NextToken = State.NextToken->Next;
+ State.Column += WhitespaceLength;
+ moveStateToNextToken(State, DryRun, /*Newline=*/false);
return 0;
}
@@ -234,7 +260,7 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
FormatToken &Current = *State.NextToken;
const FormatToken &Previous = *State.NextToken->Previous;
if (Current.is(tok::equal) &&
- (State.Line->First->is(tok::kw_for) || State.ParenLevel == 0) &&
+ (State.Line->First->is(tok::kw_for) || Current.NestingLevel == 0) &&
State.Stack.back().VariablePos == 0) {
State.Stack.back().VariablePos = State.Column;
// Move over * and & if they are bound to the variable name.
@@ -255,9 +281,12 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
Whitespaces.replaceWhitespace(Current, /*Newlines=*/0, /*IndentLevel=*/0,
Spaces, State.Column + Spaces);
- if (Current.Type == TT_ObjCSelectorName && State.Stack.back().ColonPos == 0) {
- if (State.Stack.back().Indent + Current.LongestObjCSelectorName >
- State.Column + Spaces + Current.ColumnWidth)
+ if (Current.Type == TT_SelectorName &&
+ !State.Stack.back().ObjCSelectorNameFound) {
+ if (Current.LongestObjCSelectorName == 0)
+ State.Stack.back().AlignColons = false;
+ else if (State.Stack.back().Indent + Current.LongestObjCSelectorName >
+ State.Column + Spaces + Current.ColumnWidth)
State.Stack.back().ColonPos =
State.Stack.back().Indent + Current.LongestObjCSelectorName;
else
@@ -265,7 +294,7 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
}
if (Previous.opensScope() && Previous.Type != TT_ObjCMethodExpr &&
- Current.Type != TT_LineComment)
+ (Current.Type != TT_LineComment || Previous.BlockKind == BK_BracedInit))
State.Stack.back().Indent = State.Column + Spaces;
if (State.Stack.back().AvoidBinPacking && startsNextParameter(Current, Style))
State.Stack.back().NoLineBreak = true;
@@ -273,17 +302,21 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
State.Stack.back().ContainsUnwrappedBuilder = true;
State.Column += Spaces;
- if (Current.is(tok::l_paren) && Previous.isOneOf(tok::kw_if, tok::kw_for))
+ if (Current.isNot(tok::comment) && Previous.is(tok::l_paren) &&
+ Previous.Previous && Previous.Previous->isOneOf(tok::kw_if, tok::kw_for))
// Treat the condition inside an if as if it was a second function
// parameter, i.e. let nested calls have a continuation indent.
- State.Stack.back().LastSpace = State.Column + 1; // 1 is length of "(".
- else if (Previous.is(tok::comma) || Previous.Type == TT_ObjCMethodExpr)
+ State.Stack.back().LastSpace = State.Column;
+ else if (!Current.isOneOf(tok::comment, tok::caret) &&
+ (Previous.is(tok::comma) ||
+ (Previous.is(tok::colon) && Previous.Type == TT_ObjCMethodExpr)))
State.Stack.back().LastSpace = State.Column;
else if ((Previous.Type == TT_BinaryOperator ||
Previous.Type == TT_ConditionalExpr ||
- Previous.Type == TT_UnaryOperator ||
Previous.Type == TT_CtorInitializerColon) &&
- (Previous.getPrecedence() != prec::Assignment ||
+ ((Previous.getPrecedence() != prec::Assignment &&
+ (Previous.isNot(tok::lessless) || Previous.OperatorIndex != 0 ||
+ !Previous.LastOperator)) ||
Current.StartsBinaryExpression))
// Always indent relative to the RHS of the expression unless this is a
// simple assignment without binary expression on the RHS. Also indent
@@ -313,17 +346,16 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State,
bool DryRun) {
FormatToken &Current = *State.NextToken;
const FormatToken &Previous = *State.NextToken->Previous;
- // If we are continuing an expression, we want to use the continuation indent.
- unsigned ContinuationIndent =
- std::max(State.Stack.back().LastSpace, State.Stack.back().Indent) +
- Style.ContinuationIndentWidth;
+
// Extra penalty that needs to be added because of the way certain line
// breaks are chosen.
unsigned Penalty = 0;
- const FormatToken *PreviousNonComment =
- State.NextToken->getPreviousNonComment();
- // The first line break on any ParenLevel causes an extra penalty in order
+ const FormatToken *PreviousNonComment = Current.getPreviousNonComment();
+ const FormatToken *NextNonComment = Previous.getNextNonComment();
+ if (!NextNonComment)
+ NextNonComment = &Current;
+ // The first line break on any NestingLevel causes an extra penalty in order
// prefer similar line breaks.
if (!State.Stack.back().ContainsLineBreak)
Penalty += 15;
@@ -332,96 +364,61 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State,
Penalty += State.NextToken->SplitPenalty;
// Breaking before the first "<<" is generally not desirable if the LHS is
- // short.
- if (Current.is(tok::lessless) && State.Stack.back().FirstLessLess == 0 &&
- State.Column <= Style.ColumnLimit / 2)
+ // short. Also always add the penalty if the LHS is split over mutliple lines
+ // to avoid unnecessary line breaks that just work around this penalty.
+ if (NextNonComment->is(tok::lessless) &&
+ State.Stack.back().FirstLessLess == 0 &&
+ (State.Column <= Style.ColumnLimit / 3 ||
+ State.Stack.back().BreakBeforeParameter))
Penalty += Style.PenaltyBreakFirstLessLess;
- if (Current.is(tok::l_brace) && Current.BlockKind == BK_Block) {
- State.Column = State.FirstIndent;
- } else if (Current.isOneOf(tok::r_brace, tok::r_square)) {
- if (Current.closesBlockTypeList(Style) ||
- (Current.MatchingParen &&
- Current.MatchingParen->BlockKind == BK_BracedInit))
- State.Column = State.Stack[State.Stack.size() - 2].LastSpace;
- else
- State.Column = State.FirstIndent;
- } else if (Current.is(tok::string_literal) &&
- State.StartOfStringLiteral != 0) {
- State.Column = State.StartOfStringLiteral;
- State.Stack.back().BreakBeforeParameter = true;
- } else if (Current.is(tok::lessless) &&
- State.Stack.back().FirstLessLess != 0) {
- State.Column = State.Stack.back().FirstLessLess;
- } else if (Current.isMemberAccess()) {
- if (State.Stack.back().CallContinuation == 0) {
- State.Column = ContinuationIndent;
+ State.Column = getNewLineColumn(State);
+ if (NextNonComment->isMemberAccess()) {
+ if (State.Stack.back().CallContinuation == 0)
State.Stack.back().CallContinuation = State.Column;
- } else {
- State.Column = State.Stack.back().CallContinuation;
- }
- } else if (State.Stack.back().QuestionColumn != 0 &&
- (Current.Type == TT_ConditionalExpr ||
- Previous.Type == TT_ConditionalExpr)) {
- State.Column = State.Stack.back().QuestionColumn;
- } else if (Previous.is(tok::comma) && State.Stack.back().VariablePos != 0) {
- State.Column = State.Stack.back().VariablePos;
- } else if ((PreviousNonComment &&
- PreviousNonComment->ClosesTemplateDeclaration) ||
- ((Current.Type == TT_StartOfName ||
- Current.is(tok::kw_operator)) &&
- State.ParenLevel == 0 &&
- (!Style.IndentFunctionDeclarationAfterType ||
- State.Line->StartsDefinition))) {
- State.Column = State.Stack.back().Indent;
- } else if (Current.Type == TT_ObjCSelectorName) {
- if (State.Stack.back().ColonPos == 0) {
- State.Stack.back().ColonPos =
- State.Stack.back().Indent + Current.LongestObjCSelectorName;
- State.Column = State.Stack.back().ColonPos - Current.ColumnWidth;
- } else if (State.Stack.back().ColonPos > Current.ColumnWidth) {
- State.Column = State.Stack.back().ColonPos - Current.ColumnWidth;
- } else {
- State.Column = State.Stack.back().Indent;
- State.Stack.back().ColonPos = State.Column + Current.ColumnWidth;
+ } else if (NextNonComment->Type == TT_SelectorName) {
+ if (!State.Stack.back().ObjCSelectorNameFound) {
+ if (NextNonComment->LongestObjCSelectorName == 0) {
+ State.Stack.back().AlignColons = false;
+ } else {
+ State.Stack.back().ColonPos =
+ State.Stack.back().Indent + NextNonComment->LongestObjCSelectorName;
+ }
+ } else if (State.Stack.back().AlignColons &&
+ State.Stack.back().ColonPos <= NextNonComment->ColumnWidth) {
+ State.Stack.back().ColonPos = State.Column + NextNonComment->ColumnWidth;
}
- } else if (Current.Type == TT_ArraySubscriptLSquare) {
- if (State.Stack.back().StartOfArraySubscripts != 0)
- State.Column = State.Stack.back().StartOfArraySubscripts;
- else
- State.Column = ContinuationIndent;
- } else if (Current.Type == TT_StartOfName ||
- Previous.isOneOf(tok::coloncolon, tok::equal) ||
- Previous.Type == TT_ObjCMethodExpr) {
- State.Column = ContinuationIndent;
- } else if (Current.Type == TT_CtorInitializerColon) {
- State.Column = State.FirstIndent + Style.ConstructorInitializerIndentWidth;
- } else if (Current.Type == TT_CtorInitializerComma) {
- State.Column = State.Stack.back().Indent;
- } else {
- State.Column = State.Stack.back().Indent;
- // Ensure that we fall back to the continuation indent width instead of just
- // flushing continuations left.
- if (State.Column == State.FirstIndent &&
- PreviousNonComment->isNot(tok::r_brace))
- State.Column += Style.ContinuationIndentWidth;
+ } else if (PreviousNonComment && PreviousNonComment->is(tok::colon) &&
+ (PreviousNonComment->Type == TT_ObjCMethodExpr ||
+ PreviousNonComment->Type == TT_DictLiteral)) {
+ // FIXME: This is hacky, find a better way. The problem is that in an ObjC
+ // method expression, the block should be aligned to the line starting it,
+ // e.g.:
+ // [aaaaaaaaaaaaaaa aaaaaaaaa: \\ break for some reason
+ // ^(int *i) {
+ // // ...
+ // }];
+ // Thus, we set LastSpace of the next higher NestingLevel, to which we move
+ // when we consume all of the "}"'s FakeRParens at the "{".
+ if (State.Stack.size() > 1)
+ State.Stack[State.Stack.size() - 2].LastSpace =
+ std::max(State.Stack.back().LastSpace, State.Stack.back().Indent) +
+ Style.ContinuationIndentWidth;
}
if ((Previous.isOneOf(tok::comma, tok::semi) &&
!State.Stack.back().AvoidBinPacking) ||
Previous.Type == TT_BinaryOperator)
State.Stack.back().BreakBeforeParameter = false;
- if (Previous.Type == TT_TemplateCloser && State.ParenLevel == 0)
+ if (Previous.Type == TT_TemplateCloser && Current.NestingLevel == 0)
State.Stack.back().BreakBeforeParameter = false;
- if (Current.is(tok::question) ||
+ if (NextNonComment->is(tok::question) ||
(PreviousNonComment && PreviousNonComment->is(tok::question)))
State.Stack.back().BreakBeforeParameter = true;
if (!DryRun) {
- unsigned Newlines = 1;
- if (Current.is(tok::comment))
- Newlines = std::max(Newlines, std::min(Current.NewlinesBefore,
- Style.MaxEmptyLinesToKeep + 1));
+ 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);
@@ -429,51 +426,164 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State,
if (!Current.isTrailingComment())
State.Stack.back().LastSpace = State.Column;
- if (Current.isMemberAccess())
- State.Stack.back().LastSpace += Current.ColumnWidth;
- State.StartOfLineLevel = State.ParenLevel;
- State.LowestLevelOnLine = State.ParenLevel;
+ State.StartOfLineLevel = Current.NestingLevel;
+ State.LowestLevelOnLine = Current.NestingLevel;
// Any break on this level means that the parent level has been broken
// and we need to avoid bin packing there.
- for (unsigned i = 0, e = State.Stack.size() - 1; i != e; ++i) {
- State.Stack[i].BreakBeforeParameter = true;
+ bool JavaScriptFormat = Style.Language == FormatStyle::LK_JavaScript &&
+ Current.is(tok::r_brace) &&
+ State.Stack.size() > 1 &&
+ State.Stack[State.Stack.size() - 2].JSFunctionInlined;
+ if (!JavaScriptFormat) {
+ for (unsigned i = 0, e = State.Stack.size() - 1; i != e; ++i) {
+ State.Stack[i].BreakBeforeParameter = true;
+ }
}
+
if (PreviousNonComment &&
!PreviousNonComment->isOneOf(tok::comma, tok::semi) &&
PreviousNonComment->Type != TT_TemplateCloser &&
PreviousNonComment->Type != TT_BinaryOperator &&
- Current.Type != TT_BinaryOperator &&
- !PreviousNonComment->opensScope())
+ Current.Type != TT_BinaryOperator && !PreviousNonComment->opensScope())
State.Stack.back().BreakBeforeParameter = true;
// If we break after { or the [ of an array initializer, we should also break
// before the corresponding } or ].
- if (Previous.is(tok::l_brace) || Previous.Type == TT_ArrayInitializerLSquare)
+ if (PreviousNonComment &&
+ (PreviousNonComment->is(tok::l_brace) ||
+ PreviousNonComment->Type == TT_ArrayInitializerLSquare))
State.Stack.back().BreakBeforeClosingBrace = true;
if (State.Stack.back().AvoidBinPacking) {
// If we are breaking after '(', '{', '<', this is not bin packing
- // unless AllowAllParametersOfDeclarationOnNextLine is false.
+ // unless AllowAllParametersOfDeclarationOnNextLine is false or this is a
+ // dict/object literal.
if (!(Previous.isOneOf(tok::l_paren, tok::l_brace) ||
Previous.Type == TT_BinaryOperator) ||
(!Style.AllowAllParametersOfDeclarationOnNextLine &&
- State.Line->MustBeDeclaration))
+ State.Line->MustBeDeclaration) ||
+ Previous.Type == TT_DictLiteral)
State.Stack.back().BreakBeforeParameter = true;
}
return Penalty;
}
+unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) {
+ if (!State.NextToken || !State.NextToken->Previous)
+ return 0;
+ FormatToken &Current = *State.NextToken;
+ const FormatToken &Previous = *State.NextToken->Previous;
+ // If we are continuing an expression, we want to use the continuation indent.
+ unsigned ContinuationIndent =
+ std::max(State.Stack.back().LastSpace, State.Stack.back().Indent) +
+ Style.ContinuationIndentWidth;
+ const FormatToken *PreviousNonComment = Current.getPreviousNonComment();
+ const FormatToken *NextNonComment = Previous.getNextNonComment();
+ if (!NextNonComment)
+ NextNonComment = &Current;
+ if (NextNonComment->is(tok::l_brace) && NextNonComment->BlockKind == BK_Block)
+ return Current.NestingLevel == 0 ? State.FirstIndent
+ : State.Stack.back().Indent;
+ if (Current.isOneOf(tok::r_brace, tok::r_square)) {
+ if (State.Stack.size() > 1 &&
+ State.Stack[State.Stack.size() - 2].JSFunctionInlined)
+ return State.FirstIndent;
+ if (Current.closesBlockTypeList(Style) ||
+ (Current.MatchingParen &&
+ Current.MatchingParen->BlockKind == BK_BracedInit))
+ return State.Stack[State.Stack.size() - 2].LastSpace;
+ else
+ return State.FirstIndent;
+ }
+ if (Current.is(tok::identifier) && Current.Next &&
+ Current.Next->Type == TT_DictLiteral)
+ return State.Stack.back().Indent;
+ if (NextNonComment->isStringLiteral() && State.StartOfStringLiteral != 0)
+ return State.StartOfStringLiteral;
+ if (NextNonComment->is(tok::lessless) &&
+ State.Stack.back().FirstLessLess != 0)
+ return State.Stack.back().FirstLessLess;
+ if (NextNonComment->isMemberAccess()) {
+ if (State.Stack.back().CallContinuation == 0) {
+ return ContinuationIndent;
+ } else {
+ return State.Stack.back().CallContinuation;
+ }
+ }
+ if (State.Stack.back().QuestionColumn != 0 &&
+ ((NextNonComment->is(tok::colon) &&
+ NextNonComment->Type == TT_ConditionalExpr) ||
+ Previous.Type == TT_ConditionalExpr))
+ return State.Stack.back().QuestionColumn;
+ if (Previous.is(tok::comma) && State.Stack.back().VariablePos != 0)
+ return State.Stack.back().VariablePos;
+ if ((PreviousNonComment && (PreviousNonComment->ClosesTemplateDeclaration ||
+ PreviousNonComment->Type == TT_AttributeParen)) ||
+ (!Style.IndentWrappedFunctionNames &&
+ (NextNonComment->is(tok::kw_operator) ||
+ NextNonComment->Type == TT_FunctionDeclarationName)))
+ return std::max(State.Stack.back().LastSpace, State.Stack.back().Indent);
+ if (NextNonComment->Type == TT_SelectorName) {
+ if (!State.Stack.back().ObjCSelectorNameFound) {
+ if (NextNonComment->LongestObjCSelectorName == 0) {
+ return State.Stack.back().Indent;
+ } else {
+ return State.Stack.back().Indent +
+ NextNonComment->LongestObjCSelectorName -
+ NextNonComment->ColumnWidth;
+ }
+ } else if (!State.Stack.back().AlignColons) {
+ return State.Stack.back().Indent;
+ } else if (State.Stack.back().ColonPos > NextNonComment->ColumnWidth) {
+ return State.Stack.back().ColonPos - NextNonComment->ColumnWidth;
+ } else {
+ return State.Stack.back().Indent;
+ }
+ }
+ if (NextNonComment->Type == TT_ArraySubscriptLSquare) {
+ if (State.Stack.back().StartOfArraySubscripts != 0)
+ return State.Stack.back().StartOfArraySubscripts;
+ else
+ return ContinuationIndent;
+ }
+ if (NextNonComment->Type == TT_StartOfName ||
+ Previous.isOneOf(tok::coloncolon, tok::equal)) {
+ return ContinuationIndent;
+ }
+ if (PreviousNonComment && PreviousNonComment->is(tok::colon) &&
+ (PreviousNonComment->Type == TT_ObjCMethodExpr ||
+ PreviousNonComment->Type == TT_DictLiteral))
+ return ContinuationIndent;
+ if (NextNonComment->Type == TT_CtorInitializerColon)
+ return State.FirstIndent + Style.ConstructorInitializerIndentWidth;
+ if (NextNonComment->Type == TT_CtorInitializerComma)
+ return State.Stack.back().Indent;
+ if (State.Stack.back().Indent == State.FirstIndent && PreviousNonComment &&
+ PreviousNonComment->isNot(tok::r_brace))
+ // Ensure that we fall back to the continuation indent width instead of
+ // just flushing continuations left.
+ return State.Stack.back().Indent + Style.ContinuationIndentWidth;
+ return State.Stack.back().Indent;
+}
+
unsigned ContinuationIndenter::moveStateToNextToken(LineState &State,
bool DryRun, bool Newline) {
- const FormatToken &Current = *State.NextToken;
assert(State.Stack.size());
+ const FormatToken &Current = *State.NextToken;
if (Current.Type == TT_InheritanceColon)
State.Stack.back().AvoidBinPacking = true;
- if (Current.is(tok::lessless) && State.Stack.back().FirstLessLess == 0)
- State.Stack.back().FirstLessLess = State.Column;
+ if (Current.is(tok::lessless) && Current.Type != TT_OverloadedOperator) {
+ if (State.Stack.back().FirstLessLess == 0)
+ State.Stack.back().FirstLessLess = State.Column;
+ else
+ State.Stack.back().LastOperatorWrapped = Newline;
+ }
+ if ((Current.Type == TT_BinaryOperator && Current.isNot(tok::lessless)) ||
+ Current.Type == TT_ConditionalExpr)
+ State.Stack.back().LastOperatorWrapped = Newline;
if (Current.Type == TT_ArraySubscriptLSquare &&
State.Stack.back().StartOfArraySubscripts == 0)
State.Stack.back().StartOfArraySubscripts = State.Column;
@@ -484,10 +594,12 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State,
State.Stack.back().QuestionColumn = State.Column;
if (!Current.opensScope() && !Current.closesScope())
State.LowestLevelOnLine =
- std::min(State.LowestLevelOnLine, State.ParenLevel);
+ std::min(State.LowestLevelOnLine, Current.NestingLevel);
if (Current.isMemberAccess())
State.Stack.back().StartOfFunctionCall =
- Current.LastInChainOfCalls ? 0 : State.Column + Current.ColumnWidth;
+ Current.LastOperator ? 0 : State.Column + Current.ColumnWidth;
+ if (Current.Type == TT_SelectorName)
+ State.Stack.back().ObjCSelectorNameFound = true;
if (Current.Type == TT_CtorInitializerColon) {
// Indent 2 from the column, so:
// SomeClass::SomeClass()
@@ -509,8 +621,67 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State,
// Insert scopes created by fake parenthesis.
const FormatToken *Previous = Current.getPreviousNonComment();
+
+ // Add special behavior to support a format commonly used for JavaScript
+ // closures:
+ // SomeFunction(function() {
+ // foo();
+ // bar();
+ // }, a, b, c);
+ if (Style.Language == FormatStyle::LK_JavaScript) {
+ if (Current.isNot(tok::comment) && Previous && Previous->is(tok::l_brace) &&
+ State.Stack.size() > 1) {
+ if (State.Stack[State.Stack.size() - 2].JSFunctionInlined && Newline) {
+ for (unsigned i = 0, e = State.Stack.size() - 1; i != e; ++i) {
+ State.Stack[i].NoLineBreak = true;
+ }
+ }
+ State.Stack[State.Stack.size() - 2].JSFunctionInlined = false;
+ }
+ if (Current.TokenText == "function")
+ State.Stack.back().JSFunctionInlined = !Newline;
+ }
+
+ moveStatePastFakeLParens(State, Newline);
+ moveStatePastScopeOpener(State, Newline);
+ moveStatePastScopeCloser(State);
+ moveStatePastFakeRParens(State);
+
+ if (Current.isStringLiteral() && State.StartOfStringLiteral == 0) {
+ State.StartOfStringLiteral = State.Column;
+ } else if (!Current.isOneOf(tok::comment, tok::identifier, tok::hash) &&
+ !Current.isStringLiteral()) {
+ State.StartOfStringLiteral = 0;
+ }
+
+ State.Column += Current.ColumnWidth;
+ State.NextToken = State.NextToken->Next;
+ unsigned Penalty = breakProtrudingToken(Current, State, DryRun);
+ if (State.Column > getColumnLimit(State)) {
+ unsigned ExcessCharacters = State.Column - getColumnLimit(State);
+ Penalty += Style.PenaltyExcessCharacter * ExcessCharacters;
+ }
+
+ if (Current.Role)
+ Current.Role->formatFromToken(State, this, DryRun);
+ // If the previous has a special role, let it consume tokens as appropriate.
+ // It is necessary to start at the previous token for the only implemented
+ // role (comma separated list). That way, the decision whether or not to break
+ // after the "{" is already done and both options are tried and evaluated.
+ // FIXME: This is ugly, find a better way.
+ if (Previous && Previous->Role)
+ Penalty += Previous->Role->formatAfterToken(State, this, DryRun);
+
+ return Penalty;
+}
+
+void ContinuationIndenter::moveStatePastFakeLParens(LineState &State,
+ bool Newline) {
+ const FormatToken &Current = *State.NextToken;
+ const FormatToken *Previous = Current.getPreviousNonComment();
+
// Don't add extra indentation for the first fake parenthesis after
- // 'return', assignements or opening <({[. The indentation for these cases
+ // 'return', assignments or opening <({[. The indentation for these cases
// is special cased.
bool SkipFirstExtraIndent =
(Previous && (Previous->opensScope() || Previous->is(tok::kw_return) ||
@@ -531,6 +702,24 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State,
std::max(std::max(State.Column, NewParenState.Indent),
State.Stack.back().LastSpace);
+ // Don't allow the RHS of an operator to be split over multiple lines unless
+ // there is a line-break right after the operator.
+ // Exclude relational operators, as there, it is always more desirable to
+ // have the LHS 'left' of the RHS.
+ if (Previous && Previous->getPrecedence() > prec::Assignment &&
+ (Previous->Type == TT_BinaryOperator ||
+ Previous->Type == TT_ConditionalExpr) &&
+ Previous->getPrecedence() != prec::Relational) {
+ bool BreakBeforeOperator = Previous->is(tok::lessless) ||
+ (Previous->Type == TT_BinaryOperator &&
+ Style.BreakBeforeBinaryOperators) ||
+ (Previous->Type == TT_ConditionalExpr &&
+ Style.BreakBeforeTernaryOperators);
+ if ((!Newline && !BreakBeforeOperator) ||
+ (!State.Stack.back().LastOperatorWrapped && BreakBeforeOperator))
+ NewParenState.NoLineBreak = true;
+ }
+
// Do not indent relative to the fake parentheses inserted for "." or "->".
// This is a special case to make the following to statements consistent:
// OuterFunction(InnerFunctionCall( // break
@@ -539,6 +728,7 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State,
// ParameterToInnerFunction));
if (*I > prec::Unknown)
NewParenState.LastSpace = std::max(NewParenState.LastSpace, State.Column);
+ NewParenState.StartOfFunctionCall = State.Column;
// Always indent conditional expressions. Never indent expression where
// the 'operator' is ',', ';' or an assignment (i.e. *I <=
@@ -553,131 +743,160 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State,
State.Stack.push_back(NewParenState);
SkipFirstExtraIndent = false;
}
+}
- // If we encounter an opening (, [, { or <, we add a level to our stacks to
- // prepare for the following tokens.
- if (Current.opensScope()) {
- unsigned NewIndent;
- unsigned NewIndentLevel = State.Stack.back().IndentLevel;
- bool AvoidBinPacking;
- bool BreakBeforeParameter = false;
- if (Current.is(tok::l_brace) ||
- Current.Type == TT_ArrayInitializerLSquare) {
- if (Current.MatchingParen && Current.BlockKind == BK_Block) {
- // If this is an l_brace starting a nested block, we pretend (wrt. to
- // indentation) that we already consumed the corresponding r_brace.
- // Thus, we remove all ParenStates caused bake fake parentheses that end
- // at the r_brace. The net effect of this is that we don't indent
- // relative to the l_brace, if the nested block is the last parameter of
- // a function. For example, this formats:
- //
- // SomeFunction(a, [] {
- // f(); // break
- // });
- //
- // instead of:
- // SomeFunction(a, [] {
- // f(); // break
- // });
- for (unsigned i = 0; i != Current.MatchingParen->FakeRParens; ++i)
- State.Stack.pop_back();
- NewIndent = State.Stack.back().LastSpace + Style.IndentWidth;
- ++NewIndentLevel;
- BreakBeforeParameter = true;
- } else {
- NewIndent = State.Stack.back().LastSpace;
- if (Current.opensBlockTypeList(Style)) {
- NewIndent += Style.IndentWidth;
- ++NewIndentLevel;
- } else {
- NewIndent += Style.ContinuationIndentWidth;
- }
- }
- const FormatToken *NextNoComment = Current.getNextNonComment();
- AvoidBinPacking = Current.BlockKind == BK_Block ||
- Current.Type == TT_ArrayInitializerLSquare ||
- Current.Type == TT_DictLiteral ||
- (NextNoComment &&
- NextNoComment->Type == TT_DesignatedInitializerPeriod);
- } else {
- NewIndent = Style.ContinuationIndentWidth +
- std::max(State.Stack.back().LastSpace,
- State.Stack.back().StartOfFunctionCall);
- AvoidBinPacking = !Style.BinPackParameters ||
- (Style.ExperimentalAutoDetectBinPacking &&
- (Current.PackingKind == PPK_OnePerLine ||
- (!BinPackInconclusiveFunctions &&
- Current.PackingKind == PPK_Inconclusive)));
- // If this '[' opens an ObjC call, determine whether all parameters fit
- // into one line and put one per line if they don't.
- if (Current.Type == TT_ObjCMethodExpr &&
- getLengthToMatchingParen(Current) + State.Column >
- getColumnLimit(State))
- BreakBeforeParameter = true;
+// Remove the fake r_parens after 'Tok'.
+static void consumeRParens(LineState& State, const FormatToken &Tok) {
+ for (unsigned i = 0, e = Tok.FakeRParens; i != e; ++i) {
+ unsigned VariablePos = State.Stack.back().VariablePos;
+ assert(State.Stack.size() > 1);
+ if (State.Stack.size() == 1) {
+ // Do not pop the last element.
+ break;
}
+ State.Stack.pop_back();
+ State.Stack.back().VariablePos = VariablePos;
+ }
+}
+
+// Returns whether 'Tok' opens or closes a scope requiring special handling
+// of the subsequent fake r_parens.
+//
+// For example, if this is an l_brace starting a nested block, we pretend (wrt.
+// to indentation) that we already consumed the corresponding r_brace. Thus, we
+// remove all ParenStates caused by fake parentheses that end at the r_brace.
+// The net effect of this is that we don't indent relative to the l_brace, if
+// the nested block is the last parameter of a function. This formats:
+//
+// SomeFunction(a, [] {
+// f(); // break
+// });
+//
+// instead of:
+// SomeFunction(a, [] {
+// f(); // break
+// });
+static bool fakeRParenSpecialCase(const LineState &State) {
+ const FormatToken &Tok = *State.NextToken;
+ if (!Tok.MatchingParen)
+ return false;
+ const FormatToken *Left = &Tok;
+ if (Tok.isOneOf(tok::r_brace, tok::r_square))
+ Left = Tok.MatchingParen;
+ return !State.Stack.back().HasMultipleNestedBlocks &&
+ Left->isOneOf(tok::l_brace, tok::l_square) &&
+ (Left->BlockKind == BK_Block ||
+ Left->Type == TT_ArrayInitializerLSquare ||
+ Left->Type == TT_DictLiteral);
+}
+
+void ContinuationIndenter::moveStatePastFakeRParens(LineState &State) {
+ // Don't remove FakeRParens attached to r_braces that surround nested blocks
+ // as they will have been removed early (see above).
+ if (fakeRParenSpecialCase(State))
+ return;
+
+ consumeRParens(State, *State.NextToken);
+}
- bool NoLineBreak = State.Stack.back().NoLineBreak ||
- (Current.Type == TT_TemplateOpener &&
- State.Stack.back().ContainsUnwrappedBuilder);
- State.Stack.push_back(ParenState(NewIndent, NewIndentLevel,
- State.Stack.back().LastSpace,
- AvoidBinPacking, NoLineBreak));
- State.Stack.back().BreakBeforeParameter = BreakBeforeParameter;
- ++State.ParenLevel;
+void ContinuationIndenter::moveStatePastScopeOpener(LineState &State,
+ bool Newline) {
+ const FormatToken &Current = *State.NextToken;
+ if (!Current.opensScope())
+ return;
+
+ if (Current.MatchingParen && Current.BlockKind == BK_Block) {
+ moveStateToNewBlock(State);
+ return;
}
+ unsigned NewIndent;
+ unsigned NewIndentLevel = State.Stack.back().IndentLevel;
+ bool AvoidBinPacking;
+ bool BreakBeforeParameter = false;
+ if (Current.is(tok::l_brace) || Current.Type == TT_ArrayInitializerLSquare) {
+ if (fakeRParenSpecialCase(State))
+ consumeRParens(State, *Current.MatchingParen);
+
+ NewIndent = State.Stack.back().LastSpace;
+ if (Current.opensBlockTypeList(Style)) {
+ NewIndent += Style.IndentWidth;
+ NewIndent = std::min(State.Column + 2, NewIndent);
+ ++NewIndentLevel;
+ } else {
+ NewIndent += Style.ContinuationIndentWidth;
+ NewIndent = std::min(State.Column + 1, NewIndent);
+ }
+ const FormatToken *NextNoComment = Current.getNextNonComment();
+ AvoidBinPacking = Current.Type == TT_ArrayInitializerLSquare ||
+ Current.Type == TT_DictLiteral ||
+ Style.Language == FormatStyle::LK_Proto ||
+ !Style.BinPackParameters ||
+ (NextNoComment &&
+ NextNoComment->Type == TT_DesignatedInitializerPeriod);
+ } else {
+ NewIndent = Style.ContinuationIndentWidth +
+ std::max(State.Stack.back().LastSpace,
+ State.Stack.back().StartOfFunctionCall);
+ AvoidBinPacking = !Style.BinPackParameters ||
+ (Style.ExperimentalAutoDetectBinPacking &&
+ (Current.PackingKind == PPK_OnePerLine ||
+ (!BinPackInconclusiveFunctions &&
+ Current.PackingKind == PPK_Inconclusive)));
+ // If this '[' opens an ObjC call, determine whether all parameters fit
+ // into one line and put one per line if they don't.
+ if (Current.Type == TT_ObjCMethodExpr && Style.ColumnLimit != 0 &&
+ getLengthToMatchingParen(Current) + State.Column >
+ getColumnLimit(State))
+ BreakBeforeParameter = true;
+ }
+ bool NoLineBreak = State.Stack.back().NoLineBreak ||
+ (Current.Type == TT_TemplateOpener &&
+ State.Stack.back().ContainsUnwrappedBuilder);
+ State.Stack.push_back(ParenState(NewIndent, NewIndentLevel,
+ State.Stack.back().LastSpace,
+ AvoidBinPacking, NoLineBreak));
+ State.Stack.back().BreakBeforeParameter = BreakBeforeParameter;
+ State.Stack.back().HasMultipleNestedBlocks = Current.BlockParameterCount > 1;
+}
+
+void ContinuationIndenter::moveStatePastScopeCloser(LineState &State) {
+ const FormatToken &Current = *State.NextToken;
+ if (!Current.closesScope())
+ return;
+
// If we encounter a closing ), ], } or >, we can remove a level from our
// stacks.
if (State.Stack.size() > 1 &&
(Current.isOneOf(tok::r_paren, tok::r_square) ||
(Current.is(tok::r_brace) && State.NextToken != State.Line->First) ||
- State.NextToken->Type == TT_TemplateCloser)) {
+ State.NextToken->Type == TT_TemplateCloser))
State.Stack.pop_back();
- --State.ParenLevel;
- }
+
if (Current.is(tok::r_square)) {
// If this ends the array subscript expr, reset the corresponding value.
const FormatToken *NextNonComment = Current.getNextNonComment();
if (NextNonComment && NextNonComment->isNot(tok::l_square))
State.Stack.back().StartOfArraySubscripts = 0;
}
+}
- // Remove scopes created by fake parenthesis.
- if (Current.isNot(tok::r_brace) ||
- (Current.MatchingParen && Current.MatchingParen->BlockKind != BK_Block)) {
- // Don't remove FakeRParens attached to r_braces that surround nested blocks
- // as they will have been removed early (see above).
- for (unsigned i = 0, e = Current.FakeRParens; i != e; ++i) {
- unsigned VariablePos = State.Stack.back().VariablePos;
- State.Stack.pop_back();
- State.Stack.back().VariablePos = VariablePos;
- }
- }
-
- if (Current.is(tok::string_literal) && State.StartOfStringLiteral == 0) {
- State.StartOfStringLiteral = State.Column;
- } else if (!Current.isOneOf(tok::comment, tok::identifier, tok::hash,
- tok::string_literal)) {
- State.StartOfStringLiteral = 0;
- }
-
- State.Column += Current.ColumnWidth;
- State.NextToken = State.NextToken->Next;
- unsigned Penalty = breakProtrudingToken(Current, State, DryRun);
- if (State.Column > getColumnLimit(State)) {
- unsigned ExcessCharacters = State.Column - getColumnLimit(State);
- Penalty += Style.PenaltyExcessCharacter * ExcessCharacters;
- }
-
- // If the previous has a special role, let it consume tokens as appropriate.
- // It is necessary to start at the previous token for the only implemented
- // role (comma separated list). That way, the decision whether or not to break
- // after the "{" is already done and both options are tried and evaluated.
- // FIXME: This is ugly, find a better way.
- if (Previous && Previous->Role)
- Penalty += Previous->Role->format(State, this, DryRun);
-
- return Penalty;
+void ContinuationIndenter::moveStateToNewBlock(LineState &State) {
+ // If we have already found more than one lambda introducers on this level, we
+ // opt out of this because similarity between the lambdas is more important.
+ if (fakeRParenSpecialCase(State))
+ consumeRParens(State, *State.NextToken->MatchingParen);
+
+ // For some reason, ObjC blocks are indented like continuations.
+ unsigned NewIndent = State.Stack.back().LastSpace +
+ (State.NextToken->Type == TT_ObjCBlockLBrace
+ ? Style.ContinuationIndentWidth
+ : Style.IndentWidth);
+ State.Stack.push_back(ParenState(
+ NewIndent, /*NewIndentLevel=*/State.Stack.back().IndentLevel + 1,
+ State.Stack.back().LastSpace, /*AvoidBinPacking=*/true,
+ State.Stack.back().NoLineBreak));
+ State.Stack.back().BreakBeforeParameter = true;
}
unsigned ContinuationIndenter::addMultilineToken(const FormatToken &Current,
@@ -696,8 +915,7 @@ unsigned ContinuationIndenter::addMultilineToken(const FormatToken &Current,
return 0;
}
-static bool getRawStringLiteralPrefixPostfix(StringRef Text,
- StringRef &Prefix,
+static bool getRawStringLiteralPrefixPostfix(StringRef Text, StringRef &Prefix,
StringRef &Postfix) {
if (Text.startswith(Prefix = "R\"") || Text.startswith(Prefix = "uR\"") ||
Text.startswith(Prefix = "UR\"") || Text.startswith(Prefix = "u8R\"") ||
@@ -727,19 +945,14 @@ unsigned ContinuationIndenter::breakProtrudingToken(const FormatToken &Current,
if (Current.Type == TT_ImplicitStringLiteral)
return 0;
- if (!Current.isOneOf(tok::string_literal, tok::wide_string_literal,
- tok::utf8_string_literal, tok::utf16_string_literal,
- tok::utf32_string_literal, tok::comment))
+ if (!Current.isStringLiteral() && !Current.is(tok::comment))
return 0;
- llvm::OwningPtr<BreakableToken> Token;
+ std::unique_ptr<BreakableToken> Token;
unsigned StartColumn = State.Column - Current.ColumnWidth;
unsigned ColumnLimit = getColumnLimit(State);
- if (Current.isOneOf(tok::string_literal, tok::wide_string_literal,
- tok::utf8_string_literal, tok::utf16_string_literal,
- tok::utf32_string_literal) &&
- Current.Type != TT_ImplicitStringLiteral) {
+ if (Current.isStringLiteral()) {
// Don't break string literals inside preprocessor directives (except for
// #define directives, as their contents are stored in separate lines and
// are not affected by this check).
@@ -755,13 +968,20 @@ unsigned ContinuationIndenter::breakProtrudingToken(const FormatToken &Current,
StringRef Text = Current.TokenText;
StringRef Prefix;
StringRef Postfix;
+ bool IsNSStringLiteral = false;
// FIXME: Handle whitespace between '_T', '(', '"..."', and ')'.
// FIXME: Store Prefix and Suffix (or PrefixLength and SuffixLength to
// reduce the overhead) for each FormatToken, which is a string, so that we
// don't run multiple checks here on the hot path.
+ if (Text.startswith("\"") && Current.Previous &&
+ Current.Previous->is(tok::at)) {
+ IsNSStringLiteral = true;
+ Prefix = "@\"";
+ }
if ((Text.endswith(Postfix = "\"") &&
- (Text.startswith(Prefix = "\"") || Text.startswith(Prefix = "u\"") ||
- Text.startswith(Prefix = "U\"") || Text.startswith(Prefix = "u8\"") ||
+ (IsNSStringLiteral || Text.startswith(Prefix = "\"") ||
+ Text.startswith(Prefix = "u\"") || Text.startswith(Prefix = "U\"") ||
+ Text.startswith(Prefix = "u8\"") ||
Text.startswith(Prefix = "L\""))) ||
(Text.startswith(Prefix = "_T(\"") && Text.endswith(Postfix = "\")")) ||
getRawStringLiteralPrefixPostfix(Text, Prefix, Postfix)) {
@@ -772,12 +992,16 @@ unsigned ContinuationIndenter::breakProtrudingToken(const FormatToken &Current,
return 0;
}
} else if (Current.Type == TT_BlockComment && Current.isTrailingComment()) {
+ if (CommentPragmasRegex.match(Current.TokenText.substr(2)))
+ return 0;
Token.reset(new BreakableBlockComment(
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 == nullptr ||
Current.Previous->Type != TT_ImplicitStringLiteral)) {
+ if (CommentPragmasRegex.match(Current.TokenText.substr(2)))
+ return 0;
Token.reset(new BreakableLineComment(Current, State.Line->Level,
StartColumn, /*InPPDirective=*/false,
Encoding, Style));
@@ -822,6 +1046,12 @@ unsigned ContinuationIndenter::breakProtrudingToken(const FormatToken &Current,
break;
}
+ // When breaking before a tab character, it may be moved by a few columns,
+ // but will still be expanded to the next tab stop, so we don't save any
+ // columns.
+ if (NewRemainingTokenColumns == RemainingTokenColumns)
+ break;
+
assert(NewRemainingTokenColumns < RemainingTokenColumns);
if (!DryRun)
Token->insertBreak(LineIndex, TailOffset, Split, Whitespaces);
@@ -848,8 +1078,8 @@ unsigned ContinuationIndenter::breakProtrudingToken(const FormatToken &Current,
State.Stack[i].BreakBeforeParameter = true;
}
- Penalty += Current.is(tok::string_literal) ? Style.PenaltyBreakString
- : Style.PenaltyBreakComment;
+ Penalty += Current.isStringLiteral() ? Style.PenaltyBreakString
+ : Style.PenaltyBreakComment;
State.Stack.back().LastSpace = StartColumn;
}
@@ -861,18 +1091,19 @@ unsigned ContinuationIndenter::getColumnLimit(const LineState &State) const {
return Style.ColumnLimit - (State.Line->InPPDirective ? 2 : 0);
}
-bool ContinuationIndenter::NextIsMultilineString(const LineState &State) {
+bool ContinuationIndenter::nextIsMultilineString(const LineState &State) {
const FormatToken &Current = *State.NextToken;
- if (!Current.is(tok::string_literal))
+ if (!Current.isStringLiteral() || Current.Type == TT_ImplicitStringLiteral)
return false;
// We never consider raw string literals "multiline" for the purpose of
- // AlwaysBreakBeforeMultilineStrings implementation.
+ // AlwaysBreakBeforeMultilineStrings implementation as they are special-cased
+ // (see TokenAnnotator::mustBreakBefore().
if (Current.TokenText.startswith("R\""))
return false;
if (Current.IsMultiline)
return true;
if (Current.getNextNonComment() &&
- Current.getNextNonComment()->is(tok::string_literal))
+ Current.getNextNonComment()->isStringLiteral())
return true; // Implicit concatenation.
if (State.Column + Current.ColumnWidth + Current.UnbreakableTailLength >
Style.ColumnLimit)