Index: lib/Format/FormatTokenLexer.h =================================================================== --- lib/Format/FormatTokenLexer.h +++ lib/Format/FormatTokenLexer.h @@ -66,6 +66,7 @@ FormatToken *FormatTok; bool IsFirstToken; bool GreaterStashed, LessStashed; + unsigned TemplateStringDepth; unsigned Column; unsigned TrailingWhitespace; std::unique_ptr Lex; Index: lib/Format/FormatTokenLexer.cpp =================================================================== --- lib/Format/FormatTokenLexer.cpp +++ lib/Format/FormatTokenLexer.cpp @@ -27,8 +27,8 @@ const FormatStyle &Style, encoding::Encoding Encoding) : FormatTok(nullptr), IsFirstToken(true), GreaterStashed(false), - LessStashed(false), Column(0), TrailingWhitespace(0), - SourceMgr(SourceMgr), ID(ID), Style(Style), + LessStashed(false), TemplateStringDepth(0), Column(0), + TrailingWhitespace(0), SourceMgr(SourceMgr), ID(ID), Style(Style), IdentTable(getFormattingLangOpts(Style)), Keywords(IdentTable), Encoding(Encoding), FirstInLineIndex(0), FormattingDisabled(false), MacroBlockBeginRegex(Style.MacroBlockBegin), @@ -230,7 +230,10 @@ void FormatTokenLexer::tryParseTemplateString() { FormatToken *BacktickToken = Tokens.back(); - if (!BacktickToken->is(tok::unknown) || BacktickToken->TokenText != "`") + if (TemplateStringDepth > 0 && BacktickToken->TokenText == "}") + TemplateStringDepth--; + else if (!BacktickToken->is(tok::unknown) || + BacktickToken->TokenText != "`") return; // 'Manually' lex ahead in the current file buffer. @@ -239,6 +242,13 @@ for (; Offset != Lex->getBuffer().end() && *Offset != '`'; ++Offset) { if (*Offset == '\\') ++Offset; // Skip the escaped character. + if (Offset + 1 < Lex->getBuffer().end() && *Offset == '$' && + *(Offset + 1) == '{') { + // '${' introduces an expression interpolation in the template string. + TemplateStringDepth++; + ++Offset; + break; + } } StringRef LiteralText(TmplBegin, Offset - TmplBegin + 1); Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -858,7 +858,7 @@ if (!CurrentToken->isOneOf(TT_LambdaLSquare, TT_ForEachMacro, TT_FunctionLBrace, TT_ImplicitStringLiteral, TT_InlineASMBrace, TT_JsFatArrow, TT_LambdaArrow, - TT_RegexLiteral)) + TT_RegexLiteral, TT_TemplateString)) CurrentToken->Type = TT_Unknown; CurrentToken->Role.reset(); CurrentToken->MatchingParen = nullptr; @@ -1816,6 +1816,9 @@ return 100; if (Left.is(TT_JsTypeColon)) return 35; + if ((Left.is(TT_TemplateString) && Left.TokenText.endswith("${")) || + (Right.is(TT_TemplateString) && Right.TokenText.startswith("}"))) + return 100; } if (Left.is(tok::comma) || (Right.is(tok::identifier) && Right.Next && @@ -2114,6 +2117,11 @@ } else if (Style.Language == FormatStyle::LK_JavaScript) { if (Left.is(TT_JsFatArrow)) return true; + if ((Left.is(TT_TemplateString) && Left.TokenText.endswith("${")) || + (Right.is(TT_TemplateString) && Right.TokenText.startswith("}"))) + return false; + if (Left.is(tok::identifier) && Right.is(TT_TemplateString)) + return false; if (Right.is(tok::star) && Left.isOneOf(Keywords.kw_function, Keywords.kw_yield)) return false; @@ -2121,6 +2129,9 @@ Keywords.kw_of, tok::kw_const) && (!Left.Previous || !Left.Previous->is(tok::period))) return true; + if (Left.is(Keywords.kw_as) && + Right.isOneOf(tok::l_square, tok::l_brace, tok::l_paren)) + return true; if (Left.is(tok::kw_default) && Left.Previous && Left.Previous->is(tok::kw_export)) return true; Index: unittests/Format/FormatTestJS.cpp =================================================================== --- unittests/Format/FormatTestJS.cpp +++ unittests/Format/FormatTestJS.cpp @@ -1122,7 +1122,7 @@ TEST_F(FormatTestJS, TemplateStrings) { // Keeps any whitespace/indentation within the template string. verifyFormat("var x = `hello\n" - " ${ name }\n" + " ${name}\n" " !`;", "var x = `hello\n" " ${ name }\n" @@ -1206,6 +1206,12 @@ "var y;", "var x = ` \\` a`;\n" "var y;"); + verifyFormat( + "var x = ``;"); +} + +TEST_F(FormatTestJS, TaggedTemplateStrings) { + verifyFormat("var x = html`