From be0c4cf65e9bfe7a79ed262b816be9c7e12e988d Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Mon, 20 Feb 2017 14:51:16 +0000 Subject: [PATCH] clang-format: [JS] Improve line-wrapping behavior of template strings. Specifically, similar to other blocks, clang-format now wraps both after "${" and before the corresponding "}", if the contained expression spans multiple lines. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@295663 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Format/ContinuationIndenter.cpp | 16 +++++++-- lib/Format/FormatToken.h | 4 +++ lib/Format/TokenAnnotator.cpp | 7 ++-- unittests/Format/FormatTestJS.cpp | 56 +++++++++++------------------ 4 files changed, 42 insertions(+), 41 deletions(-) diff --git a/lib/Format/ContinuationIndenter.cpp b/lib/Format/ContinuationIndenter.cpp index 8a28e630db..00645cd57e 100644 --- a/lib/Format/ContinuationIndenter.cpp +++ b/lib/Format/ContinuationIndenter.cpp @@ -383,6 +383,8 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun, Current.FakeLParens.size() > 0 && Current.FakeLParens.back() > prec::Unknown) State.Stack.back().NoLineBreak = true; + if (Previous.is(TT_TemplateString) && Previous.opensScope()) + State.Stack.back().NoLineBreak = true; if (Style.AlignAfterOpenBracket != FormatStyle::BAS_DontAlign && Previous.opensScope() && Previous.isNot(TT_ObjCMethodExpr) && @@ -398,7 +400,7 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun, State.Stack.back().NoLineBreak = true; if (Current.isMemberAccess() && Previous.is(tok::r_paren) && (Previous.MatchingParen && - (Previous.TotalLength - Previous.MatchingParen->TotalLength > 10))) { + (Previous.TotalLength - Previous.MatchingParen->TotalLength > 10))) // If there is a function call with long parameters, break before trailing // calls. This prevents things like: // EXPECT_CALL(SomeLongParameter).Times( @@ -406,7 +408,6 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun, // We don't want to do this for short parameters as they can just be // indexes. State.Stack.back().NoLineBreak = true; - } // Don't allow the RHS of an operator to be split over multiple lines unless // there is a line-break right after the operator. @@ -618,7 +619,9 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State, // If we break after { or the [ of an array initializer, we should also break // before the corresponding } or ]. if (PreviousNonComment && - (PreviousNonComment->isOneOf(tok::l_brace, TT_ArrayInitializerLSquare))) + (PreviousNonComment->isOneOf(tok::l_brace, TT_ArrayInitializerLSquare) || + (PreviousNonComment->is(TT_TemplateString) && + PreviousNonComment->opensScope()))) State.Stack.back().BreakBeforeClosingBrace = true; if (State.Stack.back().AvoidBinPacking) { @@ -666,6 +669,8 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) { return State.Stack[State.Stack.size() - 2].LastSpace; return State.FirstIndent; } + if (NextNonComment->is(TT_TemplateString) && NextNonComment->closesScope()) + return State.Stack[State.Stack.size() - 2].LastSpace; if (Current.is(tok::identifier) && Current.Next && Current.Next->is(TT_DictLiteral)) return State.Stack.back().Indent; @@ -840,6 +845,11 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State, moveStatePastFakeLParens(State, Newline); moveStatePastScopeCloser(State); + if (Current.is(TT_TemplateString) && Current.opensScope()) + State.Stack.back().LastSpace = + (Current.IsMultiline ? Current.LastLineColumnWidth + : State.Column + Current.ColumnWidth) - + strlen("${"); moveStatePastScopeOpener(State, Newline); moveStatePastFakeRParens(State); diff --git a/lib/Format/FormatToken.h b/lib/Format/FormatToken.h index 910ddca998..3cf95ae125 100644 --- a/lib/Format/FormatToken.h +++ b/lib/Format/FormatToken.h @@ -455,6 +455,8 @@ struct FormatToken { /// \brief Returns \c true if this tokens starts a block-type list, i.e. a /// list that should be indented with a block indent. bool opensBlockOrBlockTypeList(const FormatStyle &Style) const { + if (is(TT_TemplateString) && opensScope()) + return true; return is(TT_ArrayInitializerLSquare) || (is(tok::l_brace) && (BlockKind == BK_Block || is(TT_DictLiteral) || @@ -463,6 +465,8 @@ struct FormatToken { /// \brief Same as opensBlockOrBlockTypeList, but for the closing token. bool closesBlockOrBlockTypeList(const FormatStyle &Style) const { + if (is(TT_TemplateString) && closesScope()) + return true; return MatchingParen && MatchingParen->opensBlockOrBlockTypeList(Style); } diff --git a/lib/Format/TokenAnnotator.cpp b/lib/Format/TokenAnnotator.cpp index 0bf1ca4f4d..a771884eea 100644 --- a/lib/Format/TokenAnnotator.cpp +++ b/lib/Format/TokenAnnotator.cpp @@ -2536,9 +2536,12 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line, // https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#A.10 return false; if (Left.isOneOf(Keywords.kw_module, tok::kw_namespace) && - Right.isOneOf(tok::identifier, tok::string_literal)) { + Right.isOneOf(tok::identifier, tok::string_literal)) return false; // must not break in "module foo { ...}" - } + if (Right.is(TT_TemplateString) && Right.closesScope()) + return false; + if (Left.is(TT_TemplateString) && Left.opensScope()) + return true; } if (Left.is(tok::at)) diff --git a/unittests/Format/FormatTestJS.cpp b/unittests/Format/FormatTestJS.cpp index ffd443cc3d..fe87bf248f 100644 --- a/unittests/Format/FormatTestJS.cpp +++ b/unittests/Format/FormatTestJS.cpp @@ -1421,62 +1421,46 @@ TEST_F(FormatTestJS, TemplateStrings) { " aaaaaaaaaaaaa:${ aaaaaaa. aaaaa} aaaaaaaa`;"); verifyFormat("var x = someFunction(`${})`) //\n" " .oooooooooooooooooon();"); - verifyFormat("var x = someFunction(`${aaaa}${aaaaa( //\n" - " aaaaa)})`);"); + verifyFormat("var x = someFunction(`${aaaa}${\n" + " aaaaa( //\n" + " aaaaa)\n" + " })`);"); } TEST_F(FormatTestJS, TemplateStringMultiLineExpression) { - verifyFormat("var f = `aaaaaaaaaaaaaaaaaa: ${aaaaa + //\n" - " bbbb}`;", - "var f = `aaaaaaaaaaaaaaaaaa: ${aaaaa + //\n" - " bbbb}`;"); - verifyFormat("var f = `aaaaaaaaaaaaaaaaaa: ${ //\n" - " aaaaa + //\n" - " bbbb}`;", - "var f = `aaaaaaaaaaaaaaaaaa: ${ //\n" + verifyFormat("var f = `aaaaaaaaaaaaaaaaaa: ${\n" " aaaaa + //\n" + " bbbb\n" + " }`;", + "var f = `aaaaaaaaaaaaaaaaaa: ${aaaaa + //\n" " bbbb}`;"); verifyFormat("var f = `\n" - " aaaaaaaaaaaaaaaaaa: ${aaaaa + //\n" - " bbbb}`;", + " aaaaaaaaaaaaaaaaaa: ${\n" + " aaaaa + //\n" + " bbbb\n" + " }`;", "var f = `\n" " aaaaaaaaaaaaaaaaaa: ${ aaaaa + //\n" " bbbb }`;"); verifyFormat("var f = `\n" - " aaaaaaaaaaaaaaaaaa: ${ //\n" - " aaaaa + //\n" - " bbbb}`;", - "var f = `\n" - " aaaaaaaaaaaaaaaaaa: ${ //\n" - " aaaaa + //\n" - " bbbb}` ;"); - verifyFormat("var f = `\n" - " aaaaaaaaaaaaaaaaaa: ${someFunction(\n" - " aaaaa + //\n" - " bbbb)}`;", - "var f = `\n" - " aaaaaaaaaaaaaaaaaa: ${ someFunction (\n" - " aaaaa + //\n" - " bbbb)}`;"); - verifyFormat("var f = `\n" - " aaaaaaaaaaaaaaaaaa: ${ //\n" + " aaaaaaaaaaaaaaaaaa: ${\n" " someFunction(\n" " aaaaa + //\n" - " bbbb)}`;", + " bbbb)\n" + " }`;", "var f = `\n" - " aaaaaaaaaaaaaaaaaa: ${ //\n" - " someFunction (\n" + " aaaaaaaaaaaaaaaaaa: ${someFunction (\n" " aaaaa + //\n" " bbbb)}`;"); + + // It might be preferable to wrap before "someFunction". verifyFormat("var f = `\n" - " aaaaaaaaaaaaaaaaaa: ${ //\n" - " someFunction({\n" + " aaaaaaaaaaaaaaaaaa: ${someFunction({\n" " aaaa: aaaaa,\n" " bbbb: bbbbb,\n" " })}`;", "var f = `\n" - " aaaaaaaaaaaaaaaaaa: ${ //\n" - " someFunction ({\n" + " aaaaaaaaaaaaaaaaaa: ${someFunction ({\n" " aaaa: aaaaa,\n" " bbbb: bbbbb,\n" " })}`;"); -- 2.40.0