Skip to content

Commit ad9a87d

Browse files
committed
Add some huristic optimization to not colorize a keyword if precceded by a dot or a keyword. this should handel cases for "a.var" or "module string { }"
1 parent bae6ddd commit ad9a87d

File tree

2 files changed

+83
-35
lines changed

2 files changed

+83
-35
lines changed

src/services/services.ts

Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,8 @@ module ts {
659659
InMultiLineCommentTrivia,
660660
InSingleQuoteStringLiteral,
661661
InDoubleQuoteStringLiteral,
662+
EndingWithKeyword,
663+
EndingWithDotToken,
662664
}
663665

664666
export enum TokenClass {
@@ -2217,25 +2219,33 @@ module ts {
22172219
function getClassificationsForLine(text: string, lexState: EndOfLineState): ClassificationResult {
22182220
var offset = 0;
22192221
var lastTokenOrCommentEnd = 0;
2220-
var inMultiLineComment = false;
2221-
2222-
if (lexState !== EndOfLineState.Start) {
2223-
// If we're in a string literal, then prepend: "\
2224-
// (and a newline). That way when we lex we'll think we're still in a string literal.
2225-
//
2226-
// If we're in a multiline comment, then prepend: /*
2227-
// (and a newline). That way when we lex we'll think we're still in a multiline comment.
2228-
if (lexState === EndOfLineState.InDoubleQuoteStringLiteral) {
2222+
var lastToken = SyntaxKind.Unknown;
2223+
var inUnterminatedMultiLineComment = false;
2224+
2225+
// If we're in a string literal, then prepend: "\
2226+
// (and a newline). That way when we lex we'll think we're still in a string literal.
2227+
//
2228+
// If we're in a multiline comment, then prepend: /*
2229+
// (and a newline). That way when we lex we'll think we're still in a multiline comment.
2230+
switch (lexState) {
2231+
case EndOfLineState.InDoubleQuoteStringLiteral:
22292232
text = '"\\\n' + text;
2230-
}
2231-
else if (lexState === EndOfLineState.InSingleQuoteStringLiteral) {
2233+
offset = 3;
2234+
break;
2235+
case EndOfLineState.InSingleQuoteStringLiteral:
22322236
text = "'\\\n" + text;
2233-
}
2234-
else if (lexState === EndOfLineState.InMultiLineCommentTrivia) {
2237+
offset = 3;
2238+
break;
2239+
case EndOfLineState.InMultiLineCommentTrivia:
22352240
text = "/*\n" + text;
2236-
}
2237-
2238-
offset = 3;
2241+
offset = 3;
2242+
break;
2243+
case EndOfLineState.EndingWithDotToken:
2244+
lastToken = SyntaxKind.DotToken;
2245+
break;
2246+
case EndOfLineState.EndingWithKeyword:
2247+
lastToken = SyntaxKind.FirstKeyword;
2248+
break;
22392249
}
22402250

22412251
var result: ClassificationResult = {
@@ -2245,18 +2255,18 @@ module ts {
22452255

22462256
scanner = createScanner(ScriptTarget.ES5, text, onError, processComment);
22472257

2248-
var lastToken = SyntaxKind.Unknown;
22492258
var token = SyntaxKind.Unknown;
22502259
do {
2251-
inMultiLineComment = false;
2252-
22532260
token = scanner.scan();
22542261

22552262
if ((token === SyntaxKind.SlashToken || token === SyntaxKind.SlashEqualsToken) && !noRegexTable[lastToken]) {
22562263
if (scanner.reScanSlashToken() === SyntaxKind.RegularExpressionLiteral) {
22572264
token = SyntaxKind.RegularExpressionLiteral;
22582265
}
22592266
}
2267+
else if (isKeyword(token) && (isKeyword(lastToken) || lastToken === SyntaxKind.DotToken)) {
2268+
token = SyntaxKind.Identifier;
2269+
}
22602270

22612271
lastToken = token;
22622272

@@ -2268,7 +2278,7 @@ module ts {
22682278

22692279

22702280
function onError(message: DiagnosticMessage): void {
2271-
inMultiLineComment = message.key === Diagnostics.Asterisk_Slash_expected.key;
2281+
inUnterminatedMultiLineComment = message.key === Diagnostics.Asterisk_Slash_expected.key;
22722282
}
22732283

22742284
function processComment(start: number, end: number) {
@@ -2291,21 +2301,24 @@ module ts {
22912301

22922302
if (end >= text.length) {
22932303
// We're at the end.
2294-
if (inMultiLineComment) {
2304+
if (inUnterminatedMultiLineComment) {
22952305
result.finalLexState = EndOfLineState.InMultiLineCommentTrivia;
2296-
return;
22972306
}
2298-
2299-
if (token === SyntaxKind.StringLiteral) {
2307+
else if (token === SyntaxKind.StringLiteral) {
23002308
var tokenText = scanner.getTokenText();
23012309
if (tokenText.length > 0 && tokenText.charCodeAt(tokenText.length - 1) === CharacterCodes.backslash) {
23022310
var quoteChar = tokenText.charCodeAt(0);
23032311
result.finalLexState = quoteChar === CharacterCodes.doubleQuote
23042312
? EndOfLineState.InDoubleQuoteStringLiteral
23052313
: EndOfLineState.InSingleQuoteStringLiteral;
2306-
return;
23072314
}
23082315
}
2316+
else if (token === SyntaxKind.DotToken) {
2317+
result.finalLexState = EndOfLineState.EndingWithDotToken;
2318+
}
2319+
else if (isKeyword(token)) {
2320+
result.finalLexState = EndOfLineState.EndingWithKeyword;
2321+
}
23092322
}
23102323
}
23112324

@@ -2331,8 +2344,8 @@ module ts {
23312344
}
23322345
}
23332346

2334-
function isBinaryExpressionOperatorToken(tokenKind: SyntaxKind): boolean {
2335-
switch (tokenKind) {
2347+
function isBinaryExpressionOperatorToken(token: SyntaxKind): boolean {
2348+
switch (token) {
23362349
case SyntaxKind.AsteriskToken:
23372350
case SyntaxKind.SlashToken:
23382351
case SyntaxKind.PercentToken:
@@ -2374,8 +2387,8 @@ module ts {
23742387
}
23752388
}
23762389

2377-
function isPrefixUnaryExpressionOperatorToken(tokenKind: SyntaxKind): boolean {
2378-
switch (tokenKind) {
2390+
function isPrefixUnaryExpressionOperatorToken(token: SyntaxKind): boolean {
2391+
switch (token) {
23792392
case SyntaxKind.PlusToken:
23802393
case SyntaxKind.MinusToken:
23812394
case SyntaxKind.TildeToken:
@@ -2388,18 +2401,22 @@ module ts {
23882401
}
23892402
}
23902403

2391-
function classFromKind(kind: SyntaxKind) {
2392-
if (kind >= SyntaxKind.FirstKeyword && kind <= SyntaxKind.LastKeyword) {
2404+
function isKeyword(token: SyntaxKind): boolean {
2405+
return token >= SyntaxKind.FirstKeyword && token <= SyntaxKind.LastKeyword;
2406+
}
2407+
2408+
function classFromKind(token: SyntaxKind) {
2409+
if (isKeyword(token)) {
23932410
return TokenClass.Keyword;
23942411
}
2395-
else if (isBinaryExpressionOperatorToken(kind) || isPrefixUnaryExpressionOperatorToken(kind)) {
2412+
else if (isBinaryExpressionOperatorToken(token) || isPrefixUnaryExpressionOperatorToken(token)) {
23962413
return TokenClass.Operator;
23972414
}
2398-
else if (kind >= SyntaxKind.FirstPunctuation && kind <= SyntaxKind.LastPunctuation) {
2415+
else if (token >= SyntaxKind.FirstPunctuation && token <= SyntaxKind.LastPunctuation) {
23992416
return TokenClass.Punctuation;
24002417
}
24012418

2402-
switch (kind) {
2419+
switch (token) {
24032420
case SyntaxKind.NumericLiteral:
24042421
return TokenClass.NumberLiteral;
24052422
case SyntaxKind.StringLiteral:

tests/cases/unittests/services/colorization.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,21 @@ describe('Colorization', function () {
2828
var classResult = myclassifier.getClassificationsForLine(code, initialEndOfLineState).split('\n');
2929
var tuples: Classification[] = [];
3030
var i = 0;
31+
var computedLength = 0;
3132

3233
for (; i < classResult.length - 1; i += 2) {
33-
tuples[i / 2] = {
34+
var t = tuples[i / 2] = {
3435
length: parseInt(classResult[i]),
3536
class: parseInt(classResult[i + 1])
3637
};
38+
39+
assert.isTrue(t.length > 0, "Result length should be greater than 0, got :" + t.length);
40+
computedLength += t.length;
3741
}
3842
var finalEndOfLineState = classResult[classResult.length - 1];
3943

44+
assert.equal(computedLength, code.length, "Expected accumilative length of all entries to match the length of the source. expected: " + code.length + ", but got: " + computedLength);
45+
4046
return {
4147
tuples: tuples,
4248
finalEndOfLineState: parseInt(finalEndOfLineState)
@@ -209,4 +215,29 @@ describe('Colorization', function () {
209215
assert.equal(results.finalEndOfLineState, ts.EndOfLineState.InMultiLineCommentTrivia);
210216
});
211217
});
218+
219+
describe("test cases for colorizing keywords", function () {
220+
it("classifies keyword after a dot", function () {
221+
var results = getClassifications("a.var", ts.EndOfLineState.Start);
222+
verifyClassification(results.tuples[2], 3, ts.TokenClass.Identifier);
223+
});
224+
225+
it("classifies keyword after a keyword", function () {
226+
var results = getClassifications("module string", ts.EndOfLineState.Start);
227+
verifyClassification(results.tuples[2], 6, ts.TokenClass.Identifier);
228+
});
229+
230+
it("reports correct state with a line ending in a keyword", function () {
231+
var results = getClassifications("module", ts.EndOfLineState.Start);
232+
assert.equal(results.finalEndOfLineState, ts.EndOfLineState.EndingWithKeyword);
233+
});
234+
235+
it("classifies keyword after a dot on previous line", function () {
236+
var results = getClassifications("var", ts.EndOfLineState.EndingWithDotToken);
237+
238+
assert.equal(results.tuples.length, 1);
239+
verifyClassification(results.tuples[0], 3, ts.TokenClass.Identifier);
240+
assert.equal(results.finalEndOfLineState, ts.EndOfLineState.Start);
241+
});
242+
});
212243
});

0 commit comments

Comments
 (0)