Skip to content

Commit dc10492

Browse files
Merge pull request #787 from Microsoft/classifierTweaks
Tweak classification so it does not classify things as keywords that cou...
2 parents f2880ce + f09971f commit dc10492

File tree

2 files changed

+68
-17
lines changed

2 files changed

+68
-17
lines changed

src/services/services.ts

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4599,26 +4599,58 @@ module ts {
45994599
/// If we consider every slash token to be a regex, we could be missing cases like "1/2/3", where
46004600
/// we have a series of divide operator. this list allows us to be more accurate by ruling out
46014601
/// locations where a regexp cannot exist.
4602-
var noRegexTable: boolean[];
4603-
if (!noRegexTable) {
4604-
noRegexTable = [];
4605-
noRegexTable[SyntaxKind.Identifier] = true;
4606-
noRegexTable[SyntaxKind.StringLiteral] = true;
4607-
noRegexTable[SyntaxKind.NumericLiteral] = true;
4608-
noRegexTable[SyntaxKind.RegularExpressionLiteral] = true;
4609-
noRegexTable[SyntaxKind.ThisKeyword] = true;
4610-
noRegexTable[SyntaxKind.PlusPlusToken] = true;
4611-
noRegexTable[SyntaxKind.MinusMinusToken] = true;
4612-
noRegexTable[SyntaxKind.CloseParenToken] = true;
4613-
noRegexTable[SyntaxKind.CloseBracketToken] = true;
4614-
noRegexTable[SyntaxKind.CloseBraceToken] = true;
4615-
noRegexTable[SyntaxKind.TrueKeyword] = true;
4616-
noRegexTable[SyntaxKind.FalseKeyword] = true;
4602+
var noRegexTable: boolean[] = [];
4603+
noRegexTable[SyntaxKind.Identifier] = true;
4604+
noRegexTable[SyntaxKind.StringLiteral] = true;
4605+
noRegexTable[SyntaxKind.NumericLiteral] = true;
4606+
noRegexTable[SyntaxKind.RegularExpressionLiteral] = true;
4607+
noRegexTable[SyntaxKind.ThisKeyword] = true;
4608+
noRegexTable[SyntaxKind.PlusPlusToken] = true;
4609+
noRegexTable[SyntaxKind.MinusMinusToken] = true;
4610+
noRegexTable[SyntaxKind.CloseParenToken] = true;
4611+
noRegexTable[SyntaxKind.CloseBracketToken] = true;
4612+
noRegexTable[SyntaxKind.CloseBraceToken] = true;
4613+
noRegexTable[SyntaxKind.TrueKeyword] = true;
4614+
noRegexTable[SyntaxKind.FalseKeyword] = true;
4615+
4616+
function isAccessibilityModifier(kind: SyntaxKind) {
4617+
switch (kind) {
4618+
case SyntaxKind.PublicKeyword:
4619+
case SyntaxKind.PrivateKeyword:
4620+
case SyntaxKind.ProtectedKeyword:
4621+
return true;
4622+
}
4623+
4624+
return false;
4625+
}
4626+
4627+
/** Returns true if 'keyword2' can legally follow 'keyword1' in any language construct. */
4628+
function canFollow(keyword1: SyntaxKind, keyword2: SyntaxKind) {
4629+
if (isAccessibilityModifier(keyword1)) {
4630+
if (keyword2 === SyntaxKind.GetKeyword ||
4631+
keyword2 === SyntaxKind.SetKeyword ||
4632+
keyword2 === SyntaxKind.ConstructorKeyword ||
4633+
keyword2 === SyntaxKind.StaticKeyword) {
4634+
4635+
// Allow things like "public get", "public constructor" and "public static".
4636+
// These are all legal.
4637+
return true;
4638+
}
4639+
4640+
// Any other keyword following "public" is actually an identifier an not a real
4641+
// keyword.
4642+
return false;
4643+
}
4644+
4645+
// Assume any other keyword combination is legal. This can be refined in the future
4646+
// if there are more cases we want the classifier to be better at.
4647+
return true;
46174648
}
46184649

46194650
function getClassificationsForLine(text: string, lexState: EndOfLineState): ClassificationResult {
46204651
var offset = 0;
46214652
var lastTokenOrCommentEnd = 0;
4653+
var token = SyntaxKind.Unknown;
46224654
var lastNonTriviaToken = SyntaxKind.Unknown;
46234655

46244656
// If we're in a string literal, then prepend: "\
@@ -4648,8 +4680,6 @@ module ts {
46484680
entries: []
46494681
};
46504682

4651-
4652-
var token = SyntaxKind.Unknown;
46534683
do {
46544684
token = scanner.scan();
46554685

@@ -4662,6 +4692,13 @@ module ts {
46624692
else if (lastNonTriviaToken === SyntaxKind.DotToken && isKeyword(token)) {
46634693
token = SyntaxKind.Identifier;
46644694
}
4695+
else if (isKeyword(lastNonTriviaToken) && isKeyword(token) && !canFollow(lastNonTriviaToken, token)) {
4696+
// We have two keywords in a row. Only treat the second as a keyword if
4697+
// it's a sequence that could legally occur in the language. Otherwise
4698+
// treat it as an identifier. This way, if someone writes "private var"
4699+
// we recognize that 'var' is actually an identifier here.
4700+
token = SyntaxKind.Identifier;
4701+
}
46654702

46664703
lastNonTriviaToken = token;
46674704
}

tests/cases/unittests/services/colorization.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,5 +217,19 @@ describe('Colorization', function () {
217217
keyword("var"),
218218
finalEndOfLineState(ts.EndOfLineState.Start));
219219
});
220+
221+
it("classifies multiple keywords properly", function () {
222+
test("public static",
223+
ts.EndOfLineState.Start,
224+
keyword("public"),
225+
keyword("static"),
226+
finalEndOfLineState(ts.EndOfLineState.Start));
227+
228+
test("public var",
229+
ts.EndOfLineState.Start,
230+
keyword("public"),
231+
identifier("var"),
232+
finalEndOfLineState(ts.EndOfLineState.Start));
233+
});
220234
});
221235
});

0 commit comments

Comments
 (0)