Skip to content

Commit e42ce9c

Browse files
Rudimentary, but imperfect, lexical classification for templates.
1 parent 1e8e65c commit e42ce9c

File tree

1 file changed

+82
-9
lines changed

1 file changed

+82
-9
lines changed

src/services/services.ts

+82-9
Original file line numberDiff line numberDiff line change
@@ -1134,6 +1134,9 @@ module ts {
11341134
InMultiLineCommentTrivia,
11351135
InSingleQuoteStringLiteral,
11361136
InDoubleQuoteStringLiteral,
1137+
InTemplateHeadLiteral, // this could also be a NoSubstitutionTemplateLiteral
1138+
InTemplateMiddleLiteral, //this could also be a TemplateTail
1139+
InTemplateSubstitutionPosition,
11371140
}
11381141

11391142
export enum TokenClass {
@@ -5472,12 +5475,12 @@ module ts {
54725475
// if there are more cases we want the classifier to be better at.
54735476
return true;
54745477
}
5475-
5476-
// 'classifyKeywordsInGenerics' should be 'true' when a syntactic classifier is not present.
5477-
function getClassificationsForLine(text: string, lexState: EndOfLineState, classifyKeywordsInGenerics?: boolean): ClassificationResult {
5478+
5479+
function getClassificationsForLine(text: string, lexState: EndOfLineState, syntacticClassifierAbsent?: boolean): ClassificationResult {
54785480
var offset = 0;
54795481
var token = SyntaxKind.Unknown;
54805482
var lastNonTriviaToken = SyntaxKind.Unknown;
5483+
var templateStack: SyntaxKind[];
54815484

54825485
// If we're in a string literal, then prepend: "\
54835486
// (and a newline). That way when we lex we'll think we're still in a string literal.
@@ -5497,6 +5500,21 @@ module ts {
54975500
text = "/*\n" + text;
54985501
offset = 3;
54995502
break;
5503+
case EndOfLineState.InTemplateHeadLiteral:
5504+
if (syntacticClassifierAbsent) {
5505+
text = "`\n" + text;
5506+
offset = 2;
5507+
}
5508+
break;
5509+
case EndOfLineState.InTemplateMiddleLiteral:
5510+
if (syntacticClassifierAbsent) {
5511+
text = "${\n" + text;
5512+
offset = 3;
5513+
}
5514+
// fallthrough
5515+
case EndOfLineState.InTemplateSubstitutionPosition:
5516+
templateStack = [SyntaxKind.TemplateHead];
5517+
break;
55005518
}
55015519

55025520
scanner.setText(text);
@@ -5558,16 +5576,54 @@ module ts {
55585576
angleBracketStack--;
55595577
}
55605578
else if (token === SyntaxKind.AnyKeyword ||
5561-
token === SyntaxKind.StringKeyword ||
5562-
token === SyntaxKind.NumberKeyword ||
5563-
token === SyntaxKind.BooleanKeyword) {
5564-
if (angleBracketStack > 0 && !classifyKeywordsInGenerics) {
5579+
token === SyntaxKind.StringKeyword ||
5580+
token === SyntaxKind.NumberKeyword ||
5581+
token === SyntaxKind.BooleanKeyword) {
5582+
if (angleBracketStack > 0 && !syntacticClassifierAbsent) {
55655583
// If it looks like we're could be in something generic, don't classify this
55665584
// as a keyword. We may just get overwritten by the syntactic classifier,
55675585
// causing a noisy experience for the user.
55685586
token = SyntaxKind.Identifier;
55695587
}
55705588
}
5589+
else if (token === SyntaxKind.TemplateHead && syntacticClassifierAbsent) {
5590+
if (!templateStack) {
5591+
templateStack = [token];
5592+
}
5593+
else {
5594+
templateStack.push(token);
5595+
}
5596+
}
5597+
else if (token === SyntaxKind.OpenBraceToken && syntacticClassifierAbsent) {
5598+
// If we don't have anything on the template stack,
5599+
// then we aren't trying to keep track of a previously scanned template head.
5600+
if (templateStack && templateStack.length > 0) {
5601+
templateStack.push(token);
5602+
}
5603+
}
5604+
else if (token === SyntaxKind.CloseBraceToken && syntacticClassifierAbsent) {
5605+
// If we don't have anything on the template stack,
5606+
// then we aren't trying to keep track of a previously scanned template head.
5607+
if (templateStack && templateStack.length > 0) {
5608+
var lastTemplateStackToken = lastOrUndefined(templateStack);
5609+
5610+
if (lastTemplateStackToken === SyntaxKind.TemplateHead) {
5611+
token = scanner.reScanTemplateToken();
5612+
5613+
// Only pop on a TemplateTail; a TemplateMiddle indicates there is more for us.
5614+
if (token === SyntaxKind.TemplateTail) {
5615+
templateStack.pop();
5616+
}
5617+
else {
5618+
Debug.assert(token === SyntaxKind.TemplateMiddle, "Should have been a template middle. Was " + token);
5619+
}
5620+
}
5621+
else {
5622+
Debug.assert(token === SyntaxKind.CloseBraceToken, "Should have been an open brace. Was: " + token);
5623+
templateStack.pop();
5624+
}
5625+
}
5626+
}
55715627

55725628
lastNonTriviaToken = token;
55735629
}
@@ -5582,7 +5638,7 @@ module ts {
55825638
var start = scanner.getTokenPos();
55835639
var end = scanner.getTextPos();
55845640

5585-
addResult(end - start, classFromKind(token));
5641+
addResult(end - start, classFromKind(token, syntacticClassifierAbsent));
55865642

55875643
if (end >= text.length) {
55885644
if (token === SyntaxKind.StringLiteral) {
@@ -5611,6 +5667,19 @@ module ts {
56115667
result.finalLexState = EndOfLineState.InMultiLineCommentTrivia;
56125668
}
56135669
}
5670+
else if (isTemplateLiteralKind(token) && syntacticClassifierAbsent) {
5671+
if (scanner.isUnterminated()) {
5672+
if (token === SyntaxKind.TemplateMiddle) {
5673+
result.finalLexState = EndOfLineState.InTemplateMiddleLiteral;
5674+
}
5675+
else {
5676+
result.finalLexState = EndOfLineState.InTemplateHeadLiteral;
5677+
}
5678+
}
5679+
}
5680+
else if (templateStack && templateStack.length > 0 && lastOrUndefined(templateStack) === SyntaxKind.TemplateHead) {
5681+
result.finalLexState = EndOfLineState.InTemplateSubstitutionPosition;
5682+
}
56145683
}
56155684
}
56165685

@@ -5688,7 +5757,7 @@ module ts {
56885757
return token >= SyntaxKind.FirstKeyword && token <= SyntaxKind.LastKeyword;
56895758
}
56905759

5691-
function classFromKind(token: SyntaxKind) {
5760+
function classFromKind(token: SyntaxKind, syntacticClassifierAbsent?: boolean) {
56925761
if (isKeyword(token)) {
56935762
return TokenClass.Keyword;
56945763
}
@@ -5714,6 +5783,10 @@ module ts {
57145783
return TokenClass.Whitespace;
57155784
case SyntaxKind.Identifier:
57165785
default:
5786+
// Only give a classification if nothing will more accurately classify.
5787+
if (syntacticClassifierAbsent && isTemplateLiteralKind(token)) {
5788+
return TokenClass.StringLiteral; // should make a TemplateLiteral
5789+
}
57175790
return TokenClass.Identifier;
57185791
}
57195792
}

0 commit comments

Comments
 (0)