Skip to content

Commit e71b4cb

Browse files
committed
clang-format: [JS] fix template string width counting.
Summary: Simply looking at the final text greatly simplifies the algorithm and also fixes a reported issue. This requires duplicating the "actual encoding width" logic, but that seems cleaner than the column acrobatics before. Reviewers: djasper, bkramer Subscribers: cfe-commits, klimek Differential Revision: http://reviews.llvm.org/D20208 llvm-svn: 269747
1 parent 8c18e11 commit e71b4cb

File tree

2 files changed

+32
-45
lines changed

2 files changed

+32
-45
lines changed

clang/lib/Format/Format.cpp

Lines changed: 23 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,36 +1010,20 @@ class FormatTokenLexer {
10101010
return false;
10111011

10121012
unsigned TokenCount = 0;
1013-
bool IsMultiline = false;
1014-
unsigned EndColumnInFirstLine =
1015-
EndBacktick->OriginalColumn + EndBacktick->ColumnWidth;
10161013
for (auto I = Tokens.rbegin() + 1, E = Tokens.rend(); I != E; I++) {
10171014
++TokenCount;
1018-
if (I[0]->IsMultiline)
1019-
IsMultiline = true;
10201015

10211016
// If there was a preceding template string, this must be the start of a
10221017
// template string, not the end.
10231018
if (I[0]->is(TT_TemplateString))
10241019
return false;
10251020

1026-
if (I[0]->isNot(tok::unknown) || I[0]->TokenText != "`") {
1027-
// Keep track of the rhs offset of the last token to wrap across lines -
1028-
// its the rhs offset of the first line of the template string, used to
1029-
// determine its width.
1030-
if (I[0]->IsMultiline)
1031-
EndColumnInFirstLine = I[0]->OriginalColumn + I[0]->ColumnWidth;
1032-
// If the token has newlines, the token before it (if it exists) is the
1033-
// rhs end of the previous line.
1034-
if (I[0]->NewlinesBefore > 0 && (I + 1 != E)) {
1035-
EndColumnInFirstLine = I[1]->OriginalColumn + I[1]->ColumnWidth;
1036-
IsMultiline = true;
1037-
}
1021+
if (I[0]->isNot(tok::unknown) || I[0]->TokenText != "`")
10381022
continue;
1039-
}
10401023

10411024
Tokens.resize(Tokens.size() - TokenCount);
1042-
Tokens.back()->Type = TT_TemplateString;
1025+
FormatToken *TemplateStringToken = Tokens.back();
1026+
TemplateStringToken->Type = TT_TemplateString;
10431027
const char *EndOffset =
10441028
EndBacktick->TokenText.data() + 1 + CommentBacktickPos;
10451029
if (CommentBacktickPos != 0) {
@@ -1048,32 +1032,26 @@ class FormatTokenLexer {
10481032
SourceLocation Loc = EndBacktick->Tok.getLocation();
10491033
resetLexer(SourceMgr.getFileOffset(Loc) + CommentBacktickPos + 1);
10501034
}
1051-
Tokens.back()->TokenText =
1052-
StringRef(Tokens.back()->TokenText.data(),
1053-
EndOffset - Tokens.back()->TokenText.data());
1054-
1055-
unsigned EndOriginalColumn = EndBacktick->OriginalColumn;
1056-
if (EndOriginalColumn == 0) {
1057-
SourceLocation Loc = EndBacktick->Tok.getLocation();
1058-
EndOriginalColumn = SourceMgr.getSpellingColumnNumber(Loc);
1059-
}
1060-
// If the ` is further down within the token (e.g. in a comment).
1061-
EndOriginalColumn += CommentBacktickPos;
1062-
1063-
if (IsMultiline) {
1064-
// ColumnWidth is from backtick to last token in line.
1065-
// LastLineColumnWidth is 0 to backtick.
1066-
// x = `some content
1067-
// until here`;
1068-
Tokens.back()->ColumnWidth =
1069-
EndColumnInFirstLine - Tokens.back()->OriginalColumn;
1070-
// +1 for the ` itself.
1071-
Tokens.back()->LastLineColumnWidth = EndOriginalColumn + 1;
1072-
Tokens.back()->IsMultiline = true;
1073-
} else {
1074-
// Token simply spans from start to end, +1 for the ` itself.
1075-
Tokens.back()->ColumnWidth =
1076-
EndOriginalColumn - Tokens.back()->OriginalColumn + 1;
1035+
StringRef LiteralText =
1036+
StringRef(TemplateStringToken->TokenText.data(),
1037+
EndOffset - TemplateStringToken->TokenText.data());
1038+
TemplateStringToken->TokenText = LiteralText;
1039+
1040+
size_t FirstBreak = LiteralText.find('\n');
1041+
StringRef FirstLineText = FirstBreak == StringRef::npos
1042+
? LiteralText
1043+
: LiteralText.substr(0, FirstBreak);
1044+
TemplateStringToken->ColumnWidth = encoding::columnWidthWithTabs(
1045+
FirstLineText, TemplateStringToken->OriginalColumn, Style.TabWidth,
1046+
Encoding);
1047+
size_t LastBreak = LiteralText.rfind('\n');
1048+
if (LastBreak != StringRef::npos) {
1049+
TemplateStringToken->IsMultiline = true;
1050+
unsigned StartColumn = 0; // The template tail spans the entire line.
1051+
TemplateStringToken->LastLineColumnWidth =
1052+
encoding::columnWidthWithTabs(
1053+
LiteralText.substr(LastBreak + 1, LiteralText.size()),
1054+
StartColumn, Style.TabWidth, Encoding);
10771055
}
10781056
return true;
10791057
}

clang/unittests/Format/FormatTestJS.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,6 +1076,8 @@ TEST_F(FormatTestJS, TemplateStrings) {
10761076
getGoogleJSStyleWithColumns(34)); // Barely doesn't fit.
10771077
verifyFormat("var x = `hello ${world}` >= some();",
10781078
getGoogleJSStyleWithColumns(35)); // Barely fits.
1079+
verifyFormat("var x = `hellö ${wörld}` >= söme();",
1080+
getGoogleJSStyleWithColumns(35)); // Fits due to UTF-8.
10791081
verifyFormat("var x = `hello\n"
10801082
" ${world}` >=\n"
10811083
" some();",
@@ -1097,6 +1099,13 @@ TEST_F(FormatTestJS, TemplateStrings) {
10971099
getGoogleJSStyleWithColumns(13));
10981100
verifyFormat("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n"
10991101
" `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`);");
1102+
// Repro for an obscure width-miscounting issue with template strings.
1103+
verifyFormat(
1104+
"someLongVariable =\n"
1105+
" "
1106+
"`${logPrefix[11]}/${logPrefix[12]}/${logPrefix[13]}${logPrefix[14]}`;",
1107+
"someLongVariable = "
1108+
"`${logPrefix[11]}/${logPrefix[12]}/${logPrefix[13]}${logPrefix[14]}`;");
11001109

11011110
// Make sure template strings get a proper ColumnWidth assigned, even if they
11021111
// are first token in line.

0 commit comments

Comments
 (0)