Skip to content

Commit f09971f

Browse files
Tweak classification so it does not classify things as keywords that could not actually be keywords according to the grammar.
1 parent 8714673 commit f09971f

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
@@ -4577,26 +4577,58 @@ module ts {
45774577
/// If we consider every slash token to be a regex, we could be missing cases like "1/2/3", where
45784578
/// we have a series of divide operator. this list allows us to be more accurate by ruling out
45794579
/// locations where a regexp cannot exist.
4580-
var noRegexTable: boolean[];
4581-
if (!noRegexTable) {
4582-
noRegexTable = [];
4583-
noRegexTable[SyntaxKind.Identifier] = true;
4584-
noRegexTable[SyntaxKind.StringLiteral] = true;
4585-
noRegexTable[SyntaxKind.NumericLiteral] = true;
4586-
noRegexTable[SyntaxKind.RegularExpressionLiteral] = true;
4587-
noRegexTable[SyntaxKind.ThisKeyword] = true;
4588-
noRegexTable[SyntaxKind.PlusPlusToken] = true;
4589-
noRegexTable[SyntaxKind.MinusMinusToken] = true;
4590-
noRegexTable[SyntaxKind.CloseParenToken] = true;
4591-
noRegexTable[SyntaxKind.CloseBracketToken] = true;
4592-
noRegexTable[SyntaxKind.CloseBraceToken] = true;
4593-
noRegexTable[SyntaxKind.TrueKeyword] = true;
4594-
noRegexTable[SyntaxKind.FalseKeyword] = true;
4580+
var noRegexTable: boolean[] = [];
4581+
noRegexTable[SyntaxKind.Identifier] = true;
4582+
noRegexTable[SyntaxKind.StringLiteral] = true;
4583+
noRegexTable[SyntaxKind.NumericLiteral] = true;
4584+
noRegexTable[SyntaxKind.RegularExpressionLiteral] = true;
4585+
noRegexTable[SyntaxKind.ThisKeyword] = true;
4586+
noRegexTable[SyntaxKind.PlusPlusToken] = true;
4587+
noRegexTable[SyntaxKind.MinusMinusToken] = true;
4588+
noRegexTable[SyntaxKind.CloseParenToken] = true;
4589+
noRegexTable[SyntaxKind.CloseBracketToken] = true;
4590+
noRegexTable[SyntaxKind.CloseBraceToken] = true;
4591+
noRegexTable[SyntaxKind.TrueKeyword] = true;
4592+
noRegexTable[SyntaxKind.FalseKeyword] = true;
4593+
4594+
function isAccessibilityModifier(kind: SyntaxKind) {
4595+
switch (kind) {
4596+
case SyntaxKind.PublicKeyword:
4597+
case SyntaxKind.PrivateKeyword:
4598+
case SyntaxKind.ProtectedKeyword:
4599+
return true;
4600+
}
4601+
4602+
return false;
4603+
}
4604+
4605+
/** Returns true if 'keyword2' can legally follow 'keyword1' in any language construct. */
4606+
function canFollow(keyword1: SyntaxKind, keyword2: SyntaxKind) {
4607+
if (isAccessibilityModifier(keyword1)) {
4608+
if (keyword2 === SyntaxKind.GetKeyword ||
4609+
keyword2 === SyntaxKind.SetKeyword ||
4610+
keyword2 === SyntaxKind.ConstructorKeyword ||
4611+
keyword2 === SyntaxKind.StaticKeyword) {
4612+
4613+
// Allow things like "public get", "public constructor" and "public static".
4614+
// These are all legal.
4615+
return true;
4616+
}
4617+
4618+
// Any other keyword following "public" is actually an identifier an not a real
4619+
// keyword.
4620+
return false;
4621+
}
4622+
4623+
// Assume any other keyword combination is legal. This can be refined in the future
4624+
// if there are more cases we want the classifier to be better at.
4625+
return true;
45954626
}
45964627

45974628
function getClassificationsForLine(text: string, lexState: EndOfLineState): ClassificationResult {
45984629
var offset = 0;
45994630
var lastTokenOrCommentEnd = 0;
4631+
var token = SyntaxKind.Unknown;
46004632
var lastNonTriviaToken = SyntaxKind.Unknown;
46014633

46024634
// If we're in a string literal, then prepend: "\
@@ -4626,8 +4658,6 @@ module ts {
46264658
entries: []
46274659
};
46284660

4629-
4630-
var token = SyntaxKind.Unknown;
46314661
do {
46324662
token = scanner.scan();
46334663

@@ -4640,6 +4670,13 @@ module ts {
46404670
else if (lastNonTriviaToken === SyntaxKind.DotToken && isKeyword(token)) {
46414671
token = SyntaxKind.Identifier;
46424672
}
4673+
else if (isKeyword(lastNonTriviaToken) && isKeyword(token) && !canFollow(lastNonTriviaToken, token)) {
4674+
// We have two keywords in a row. Only treat the second as a keyword if
4675+
// it's a sequence that could legally occur in the language. Otherwise
4676+
// treat it as an identifier. This way, if someone writes "private var"
4677+
// we recognize that 'var' is actually an identifier here.
4678+
token = SyntaxKind.Identifier;
4679+
}
46434680

46444681
lastNonTriviaToken = token;
46454682
}

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)