Rudimentary, but imperfect, lexical classification for templates.

This commit is contained in:
Daniel Rosenwasser 2015-01-19 18:07:54 -08:00
parent 3e8babedcf
commit ea30c68128

View file

@ -1143,6 +1143,9 @@ module ts {
InMultiLineCommentTrivia, InMultiLineCommentTrivia,
InSingleQuoteStringLiteral, InSingleQuoteStringLiteral,
InDoubleQuoteStringLiteral, InDoubleQuoteStringLiteral,
InTemplateHeadLiteral, // this could also be a NoSubstitutionTemplateLiteral
InTemplateMiddleLiteral, //this could also be a TemplateTail
InTemplateSubstitutionPosition,
} }
export enum TokenClass { export enum TokenClass {
@ -5650,12 +5653,12 @@ module ts {
// if there are more cases we want the classifier to be better at. // if there are more cases we want the classifier to be better at.
return true; return true;
} }
// 'classifyKeywordsInGenerics' should be 'true' when a syntactic classifier is not present. function getClassificationsForLine(text: string, lexState: EndOfLineState, syntacticClassifierAbsent?: boolean): ClassificationResult {
function getClassificationsForLine(text: string, lexState: EndOfLineState, classifyKeywordsInGenerics?: boolean): ClassificationResult {
var offset = 0; var offset = 0;
var token = SyntaxKind.Unknown; var token = SyntaxKind.Unknown;
var lastNonTriviaToken = SyntaxKind.Unknown; var lastNonTriviaToken = SyntaxKind.Unknown;
var templateStack: SyntaxKind[];
// If we're in a string literal, then prepend: "\ // If we're in a string literal, then prepend: "\
// (and a newline). That way when we lex we'll think we're still in a string literal. // (and a newline). That way when we lex we'll think we're still in a string literal.
@ -5675,6 +5678,21 @@ module ts {
text = "/*\n" + text; text = "/*\n" + text;
offset = 3; offset = 3;
break; break;
case EndOfLineState.InTemplateHeadLiteral:
if (syntacticClassifierAbsent) {
text = "`\n" + text;
offset = 2;
}
break;
case EndOfLineState.InTemplateMiddleLiteral:
if (syntacticClassifierAbsent) {
text = "${\n" + text;
offset = 3;
}
// fallthrough
case EndOfLineState.InTemplateSubstitutionPosition:
templateStack = [SyntaxKind.TemplateHead];
break;
} }
scanner.setText(text); scanner.setText(text);
@ -5739,12 +5757,50 @@ module ts {
token === SyntaxKind.StringKeyword || token === SyntaxKind.StringKeyword ||
token === SyntaxKind.NumberKeyword || token === SyntaxKind.NumberKeyword ||
token === SyntaxKind.BooleanKeyword) { token === SyntaxKind.BooleanKeyword) {
if (angleBracketStack > 0 && !classifyKeywordsInGenerics) { if (angleBracketStack > 0 && !syntacticClassifierAbsent) {
// If it looks like we're could be in something generic, don't classify this // If it looks like we're could be in something generic, don't classify this
// as a keyword. We may just get overwritten by the syntactic classifier, // as a keyword. We may just get overwritten by the syntactic classifier,
// causing a noisy experience for the user. // causing a noisy experience for the user.
token = SyntaxKind.Identifier; token = SyntaxKind.Identifier;
} }
}
else if (token === SyntaxKind.TemplateHead && syntacticClassifierAbsent) {
if (!templateStack) {
templateStack = [token];
}
else {
templateStack.push(token);
}
}
else if (token === SyntaxKind.OpenBraceToken && syntacticClassifierAbsent) {
// If we don't have anything on the template stack,
// then we aren't trying to keep track of a previously scanned template head.
if (templateStack && templateStack.length > 0) {
templateStack.push(token);
}
}
else if (token === SyntaxKind.CloseBraceToken && syntacticClassifierAbsent) {
// If we don't have anything on the template stack,
// then we aren't trying to keep track of a previously scanned template head.
if (templateStack && templateStack.length > 0) {
var lastTemplateStackToken = lastOrUndefined(templateStack);
if (lastTemplateStackToken === SyntaxKind.TemplateHead) {
token = scanner.reScanTemplateToken();
// Only pop on a TemplateTail; a TemplateMiddle indicates there is more for us.
if (token === SyntaxKind.TemplateTail) {
templateStack.pop();
}
else {
Debug.assert(token === SyntaxKind.TemplateMiddle, "Should have been a template middle. Was " + token);
}
}
else {
Debug.assert(token === SyntaxKind.CloseBraceToken, "Should have been an open brace. Was: " + token);
templateStack.pop();
}
}
} }
lastNonTriviaToken = token; lastNonTriviaToken = token;
@ -5760,7 +5816,7 @@ module ts {
var start = scanner.getTokenPos(); var start = scanner.getTokenPos();
var end = scanner.getTextPos(); var end = scanner.getTextPos();
addResult(end - start, classFromKind(token)); addResult(end - start, classFromKind(token, syntacticClassifierAbsent));
if (end >= text.length) { if (end >= text.length) {
if (token === SyntaxKind.StringLiteral) { if (token === SyntaxKind.StringLiteral) {
@ -5789,6 +5845,19 @@ module ts {
result.finalLexState = EndOfLineState.InMultiLineCommentTrivia; result.finalLexState = EndOfLineState.InMultiLineCommentTrivia;
} }
} }
else if (isTemplateLiteralKind(token) && syntacticClassifierAbsent) {
if (scanner.isUnterminated()) {
if (token === SyntaxKind.TemplateMiddle) {
result.finalLexState = EndOfLineState.InTemplateMiddleLiteral;
}
else {
result.finalLexState = EndOfLineState.InTemplateHeadLiteral;
}
}
}
else if (templateStack && templateStack.length > 0 && lastOrUndefined(templateStack) === SyntaxKind.TemplateHead) {
result.finalLexState = EndOfLineState.InTemplateSubstitutionPosition;
}
} }
} }
@ -5866,7 +5935,7 @@ module ts {
return token >= SyntaxKind.FirstKeyword && token <= SyntaxKind.LastKeyword; return token >= SyntaxKind.FirstKeyword && token <= SyntaxKind.LastKeyword;
} }
function classFromKind(token: SyntaxKind) { function classFromKind(token: SyntaxKind, syntacticClassifierAbsent?: boolean) {
if (isKeyword(token)) { if (isKeyword(token)) {
return TokenClass.Keyword; return TokenClass.Keyword;
} }
@ -5892,6 +5961,10 @@ module ts {
return TokenClass.Whitespace; return TokenClass.Whitespace;
case SyntaxKind.Identifier: case SyntaxKind.Identifier:
default: default:
// Only give a classification if nothing will more accurately classify.
if (syntacticClassifierAbsent && isTemplateLiteralKind(token)) {
return TokenClass.StringLiteral; // should make a TemplateLiteral
}
return TokenClass.Identifier; return TokenClass.Identifier;
} }
} }