Skip to content

Commit ea30c68

Browse files
Rudimentary, but imperfect, lexical classification for templates.
1 parent 3e8babe commit ea30c68

File tree

1 file changed

+84
-11
lines changed

1 file changed

+84
-11
lines changed

src/services/services.ts

+84-11
Original file line numberDiff line numberDiff line change
@@ -1143,6 +1143,9 @@ module ts {
11431143
InMultiLineCommentTrivia,
11441144
InSingleQuoteStringLiteral,
11451145
InDoubleQuoteStringLiteral,
1146+
InTemplateHeadLiteral, // this could also be a NoSubstitutionTemplateLiteral
1147+
InTemplateMiddleLiteral, //this could also be a TemplateTail
1148+
InTemplateSubstitutionPosition,
11461149
}
11471150

11481151
export enum TokenClass {
@@ -5650,12 +5653,12 @@ module ts {
56505653
// if there are more cases we want the classifier to be better at.
56515654
return true;
56525655
}
5653-
5654-
// 'classifyKeywordsInGenerics' should be 'true' when a syntactic classifier is not present.
5655-
function getClassificationsForLine(text: string, lexState: EndOfLineState, classifyKeywordsInGenerics?: boolean): ClassificationResult {
5656+
5657+
function getClassificationsForLine(text: string, lexState: EndOfLineState, syntacticClassifierAbsent?: boolean): ClassificationResult {
56565658
var offset = 0;
56575659
var token = SyntaxKind.Unknown;
56585660
var lastNonTriviaToken = SyntaxKind.Unknown;
5661+
var templateStack: SyntaxKind[];
56595662

56605663
// If we're in a string literal, then prepend: "\
56615664
// (and a newline). That way when we lex we'll think we're still in a string literal.
@@ -5675,6 +5678,21 @@ module ts {
56755678
text = "/*\n" + text;
56765679
offset = 3;
56775680
break;
5681+
case EndOfLineState.InTemplateHeadLiteral:
5682+
if (syntacticClassifierAbsent) {
5683+
text = "`\n" + text;
5684+
offset = 2;
5685+
}
5686+
break;
5687+
case EndOfLineState.InTemplateMiddleLiteral:
5688+
if (syntacticClassifierAbsent) {
5689+
text = "${\n" + text;
5690+
offset = 3;
5691+
}
5692+
// fallthrough
5693+
case EndOfLineState.InTemplateSubstitutionPosition:
5694+
templateStack = [SyntaxKind.TemplateHead];
5695+
break;
56785696
}
56795697

56805698
scanner.setText(text);
@@ -5739,12 +5757,50 @@ module ts {
57395757
token === SyntaxKind.StringKeyword ||
57405758
token === SyntaxKind.NumberKeyword ||
57415759
token === SyntaxKind.BooleanKeyword) {
5742-
if (angleBracketStack > 0 && !classifyKeywordsInGenerics) {
5743-
// If it looks like we're could be in something generic, don't classify this
5744-
// as a keyword. We may just get overwritten by the syntactic classifier,
5745-
// causing a noisy experience for the user.
5746-
token = SyntaxKind.Identifier;
5747-
}
5760+
if (angleBracketStack > 0 && !syntacticClassifierAbsent) {
5761+
// If it looks like we're could be in something generic, don't classify this
5762+
// as a keyword. We may just get overwritten by the syntactic classifier,
5763+
// causing a noisy experience for the user.
5764+
token = SyntaxKind.Identifier;
5765+
}
5766+
}
5767+
else if (token === SyntaxKind.TemplateHead && syntacticClassifierAbsent) {
5768+
if (!templateStack) {
5769+
templateStack = [token];
5770+
}
5771+
else {
5772+
templateStack.push(token);
5773+
}
5774+
}
5775+
else if (token === SyntaxKind.OpenBraceToken && syntacticClassifierAbsent) {
5776+
// If we don't have anything on the template stack,
5777+
// then we aren't trying to keep track of a previously scanned template head.
5778+
if (templateStack && templateStack.length > 0) {
5779+
templateStack.push(token);
5780+
}
5781+
}
5782+
else if (token === SyntaxKind.CloseBraceToken && syntacticClassifierAbsent) {
5783+
// If we don't have anything on the template stack,
5784+
// then we aren't trying to keep track of a previously scanned template head.
5785+
if (templateStack && templateStack.length > 0) {
5786+
var lastTemplateStackToken = lastOrUndefined(templateStack);
5787+
5788+
if (lastTemplateStackToken === SyntaxKind.TemplateHead) {
5789+
token = scanner.reScanTemplateToken();
5790+
5791+
// Only pop on a TemplateTail; a TemplateMiddle indicates there is more for us.
5792+
if (token === SyntaxKind.TemplateTail) {
5793+
templateStack.pop();
5794+
}
5795+
else {
5796+
Debug.assert(token === SyntaxKind.TemplateMiddle, "Should have been a template middle. Was " + token);
5797+
}
5798+
}
5799+
else {
5800+
Debug.assert(token === SyntaxKind.CloseBraceToken, "Should have been an open brace. Was: " + token);
5801+
templateStack.pop();
5802+
}
5803+
}
57485804
}
57495805

57505806
lastNonTriviaToken = token;
@@ -5760,7 +5816,7 @@ module ts {
57605816
var start = scanner.getTokenPos();
57615817
var end = scanner.getTextPos();
57625818

5763-
addResult(end - start, classFromKind(token));
5819+
addResult(end - start, classFromKind(token, syntacticClassifierAbsent));
57645820

57655821
if (end >= text.length) {
57665822
if (token === SyntaxKind.StringLiteral) {
@@ -5789,6 +5845,19 @@ module ts {
57895845
result.finalLexState = EndOfLineState.InMultiLineCommentTrivia;
57905846
}
57915847
}
5848+
else if (isTemplateLiteralKind(token) && syntacticClassifierAbsent) {
5849+
if (scanner.isUnterminated()) {
5850+
if (token === SyntaxKind.TemplateMiddle) {
5851+
result.finalLexState = EndOfLineState.InTemplateMiddleLiteral;
5852+
}
5853+
else {
5854+
result.finalLexState = EndOfLineState.InTemplateHeadLiteral;
5855+
}
5856+
}
5857+
}
5858+
else if (templateStack && templateStack.length > 0 && lastOrUndefined(templateStack) === SyntaxKind.TemplateHead) {
5859+
result.finalLexState = EndOfLineState.InTemplateSubstitutionPosition;
5860+
}
57925861
}
57935862
}
57945863

@@ -5866,7 +5935,7 @@ module ts {
58665935
return token >= SyntaxKind.FirstKeyword && token <= SyntaxKind.LastKeyword;
58675936
}
58685937

5869-
function classFromKind(token: SyntaxKind) {
5938+
function classFromKind(token: SyntaxKind, syntacticClassifierAbsent?: boolean) {
58705939
if (isKeyword(token)) {
58715940
return TokenClass.Keyword;
58725941
}
@@ -5892,6 +5961,10 @@ module ts {
58925961
return TokenClass.Whitespace;
58935962
case SyntaxKind.Identifier:
58945963
default:
5964+
// Only give a classification if nothing will more accurately classify.
5965+
if (syntacticClassifierAbsent && isTemplateLiteralKind(token)) {
5966+
return TokenClass.StringLiteral; // should make a TemplateLiteral
5967+
}
58955968
return TokenClass.Identifier;
58965969
}
58975970
}

0 commit comments

Comments
 (0)