@@ -4692,6 +4692,27 @@ module ts {
4692
4692
entries : [ ]
4693
4693
} ;
4694
4694
4695
+ // We can run into an unfortunate interaction between the lexical and syntactic classifier
4696
+ // when the user is typing something generic. Consider the case where the user types:
4697
+ //
4698
+ // Foo<number
4699
+ //
4700
+ // From the lexical classifier's perspective, 'number' is a keyword, and so the word will
4701
+ // be classified as such. However, from the syntactic classifier's tree-based perspective
4702
+ // this is simply an expression with the identifier 'number' on the RHS of the less than
4703
+ // token. So the classification will go back to being an identifier. The moment the user
4704
+ // types again, number will become a keyword, then an identifier, etc. etc.
4705
+ //
4706
+ // To try to avoid this problem, we avoid classifying contextual keywords as keywords
4707
+ // when the user is potentially typing something generic. We just can't do a good enough
4708
+ // job at the lexical level, and so well leave it up to the syntactic classifier to make
4709
+ // the determination.
4710
+ //
4711
+ // In order to determine if the user is potentially typing something generic, we use a
4712
+ // weak heuristic where we track < and > tokens. It's a weak heuristic, but should
4713
+ // work well enough in practice.
4714
+ var angleBracketStack = 0 ;
4715
+
4695
4716
do {
4696
4717
token = scanner . scan ( ) ;
4697
4718
@@ -4711,6 +4732,28 @@ module ts {
4711
4732
// we recognize that 'var' is actually an identifier here.
4712
4733
token = SyntaxKind . Identifier ;
4713
4734
}
4735
+ else if ( lastNonTriviaToken === SyntaxKind . Identifier &&
4736
+ token === SyntaxKind . LessThanToken ) {
4737
+ // Could be the start of something generic. Keep track of that by bumping
4738
+ // up the current count of generic contexts we may be in.
4739
+ angleBracketStack ++ ;
4740
+ }
4741
+ else if ( token === SyntaxKind . GreaterThanToken && angleBracketStack > 0 ) {
4742
+ // If we think we're currently in something generic, then mark that that
4743
+ // generic entity is complete.
4744
+ angleBracketStack -- ;
4745
+ }
4746
+ else if ( token === SyntaxKind . AnyKeyword ||
4747
+ token === SyntaxKind . StringKeyword ||
4748
+ token === SyntaxKind . NumberKeyword ||
4749
+ token === SyntaxKind . BooleanKeyword ) {
4750
+ if ( angleBracketStack > 0 ) {
4751
+ // If it looks like we're could be in something generic, don't classify this
4752
+ // as a keyword. We may just get overwritten by the syntactic classifier,
4753
+ // causing a noisy experience for the user.
4754
+ token = SyntaxKind . Identifier ;
4755
+ }
4756
+ }
4714
4757
4715
4758
lastNonTriviaToken = token ;
4716
4759
}
0 commit comments