diff --git a/src/services/services.ts b/src/services/services.ts
index 88efbaf2a2a..ebea261d41d 100644
--- a/src/services/services.ts
+++ b/src/services/services.ts
@@ -1,4 +1,4 @@
-///
+ ///
///
///
///
@@ -4692,6 +4692,27 @@ module ts {
entries: []
};
+ // We can run into an unfortunate interaction between the lexical and syntactic classifier
+ // when the user is typing something generic. Consider the case where the user types:
+ //
+ // Foo tokens. It's a weak heuristic, but should
+ // work well enough in practice.
+ var inGenericStack = 0;
+
do {
token = scanner.scan();
@@ -4711,6 +4732,29 @@ module ts {
// we recognize that 'var' is actually an identifier here.
token = SyntaxKind.Identifier;
}
+ else if (lastNonTriviaToken === SyntaxKind.Identifier &&
+ token === SyntaxKind.LessThanToken) {
+ // Could be the start of something generic. Keep track of that by bumping
+ // up the current count of generic contexts we may be in.
+ inGenericStack++;
+ }
+ else if (token === SyntaxKind.GreaterThanToken && inGenericStack > 0) {
+ // If we think we're currently in something generic, then mark that that
+ // generic entity is complete.
+ inGenericStack--;
+ }
+ else if (token === SyntaxKind.AnyKeyword ||
+ token === SyntaxKind.StringKeyword ||
+ token === SyntaxKind.NumberKeyword ||
+ token === SyntaxKind.BooleanKeyword ||
+ token === SyntaxKind.VoidKeyword) {
+ if (inGenericStack > 0) {
+ // 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,
+ // causing a noisy experience for the user.
+ token = SyntaxKind.Identifier;
+ }
+ }
lastNonTriviaToken = token;
}
diff --git a/tests/cases/unittests/services/colorization.ts b/tests/cases/unittests/services/colorization.ts
index 18b157f26b0..ee5350a25eb 100644
--- a/tests/cases/unittests/services/colorization.ts
+++ b/tests/cases/unittests/services/colorization.ts
@@ -231,5 +231,43 @@ describe('Colorization', function () {
identifier("var"),
finalEndOfLineState(ts.EndOfLineState.Start));
});
+
+ it("classifies partially written generics correctly.", function () {
+ test("Foo number",
+ ts.EndOfLineState.Start,
+ identifier("Foo"),
+ operator("<"),
+ identifier("Foo"),
+ operator(">"
+ identifier("keyword"),
+ finalEndOfLineState(ts.EndOfLineState.Start));
+ });
});
});
\ No newline at end of file