Skip to content

Commit ba7c96c

Browse files
committed
Faster full-file semantic tokens computation (microsoft/vscode#92789)
1 parent f340b42 commit ba7c96c

File tree

3 files changed

+1943
-11
lines changed

3 files changed

+1943
-11
lines changed

src/index.ts

+59-8
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@
55
*--------------------------------------------------------------------------------------------*/
66

77
import { TokenType, TokenModifier, TokenEncodingConsts, VersionRequirement } from './constants';
8+
import * as semanticTokensVisitor from './semanticTokensVisitor';
9+
10+
const computeOracleTokens = false;
811

912
export = function init(modules: { typescript: typeof import("typescript/lib/tsserverlibrary") }) {
1013
const ts = modules.typescript;
1114

15+
const newImplementation = semanticTokensVisitor.init(ts);
16+
1217
function hasVersion(requiredMajor: number, requiredMinor: number) {
1318
const parts = ts.version.split('.');
1419
const majorVersion = Number(parts[0]);
@@ -20,14 +25,36 @@ export = function init(modules: { typescript: typeof import("typescript/lib/tsse
2025
const intercept: Partial<ts.LanguageService> = Object.create(null);
2126

2227
if (!hasVersion(VersionRequirement.major, VersionRequirement.minor)) {
23-
logger?.msg(`typescript-vscode-sh-plugin not active, version ${VersionRequirement.major}.${VersionRequirement.minor} required, is ${ts.version}`, ts.server.Msg.Info);
28+
// logger?.msg(`typescript-vscode-sh-plugin not active, version ${VersionRequirement.major}.${VersionRequirement.minor} required, is ${ts.version}`, ts.server.Msg.Info);
2429
return languageService;
2530
}
2631
logger?.msg(`typescript-vscode-sh-plugin initialized. Intercepting getEncodedSemanticClassifications and getEncodedSyntacticClassifications.`, ts.server.Msg.Info);
2732

2833
intercept.getEncodedSemanticClassifications = (filename: string, span: ts.TextSpan) => {
34+
const start = Date.now();
35+
const actualTokens = newImplementation.getEncodedSemanticClassifications(languageService, filename, logger);
36+
const duration = Date.now() - start;
37+
38+
logger?.msg(`COMPUTING ${actualTokens.length / 3} TOOK ${duration}ms !`, ts.server.Msg.Info);
39+
40+
if (computeOracleTokens) {
41+
const oracleTokens = getSemanticTokens(languageService, filename, span);
42+
const program = languageService.getProgram();
43+
if (program) {
44+
const sourceFile = program.getSourceFile(filename);
45+
if (sourceFile) {
46+
const diffs = newImplementation.compareTokens(sourceFile, actualTokens, oracleTokens);
47+
if (diffs.length > 0) {
48+
logger?.msg(`REQUEST YIELDED DIFFERENT RESULTS:`, ts.server.Msg.Info);
49+
logger?.msg(diffs.join('\n'), ts.server.Msg.Info);
50+
} else {
51+
logger?.msg(`REQUEST WAS EQUAL IN BOTH TOKENIZERS`);
52+
}
53+
}
54+
}
55+
}
2956
return {
30-
spans: getSemanticTokens(languageService, filename, span),
57+
spans: actualTokens,
3158
endOfLineState: ts.EndOfLineState.None
3259
}
3360
};
@@ -89,8 +116,8 @@ export = function init(modules: { typescript: typeof import("typescript/lib/tsse
89116
if (typeIdx !== undefined) {
90117
let modifierSet = 0;
91118
if (node.parent) {
92-
const parentTypeIdx = tokenFromDeclarationMapping[node.parent.kind];
93-
if (parentTypeIdx === typeIdx && (<ts.NamedDeclaration>node.parent).name === node) {
119+
const parentIsDeclaration = (ts.isBindingElement(node.parent) || tokenFromDeclarationMapping[node.parent.kind] === typeIdx);
120+
if (parentIsDeclaration && (<ts.NamedDeclaration>node.parent).name === node) {
94121
modifierSet = 1 << TokenModifier.declaration;
95122
}
96123
}
@@ -112,15 +139,21 @@ export = function init(modules: { typescript: typeof import("typescript/lib/tsse
112139
if (modifiers & ts.ModifierFlags.Async) {
113140
modifierSet |= 1 << TokenModifier.async;
114141
}
115-
if ((modifiers & ts.ModifierFlags.Readonly) || (nodeFlags & ts.NodeFlags.Const) || (symbol.getFlags() & ts.SymbolFlags.EnumMember)) {
116-
modifierSet |= 1 << TokenModifier.readonly;
142+
if (typeIdx !== TokenType.interface) {
143+
if ((modifiers & ts.ModifierFlags.Readonly) || (nodeFlags & ts.NodeFlags.Const) || (symbol.getFlags() & ts.SymbolFlags.EnumMember)) {
144+
modifierSet |= 1 << TokenModifier.readonly;
145+
}
117146
}
118147
if ((typeIdx === TokenType.variable || typeIdx === TokenType.function) && isLocalDeclaration(decl, sourceFile)) {
119148
modifierSet |= 1 << TokenModifier.local;
120149
}
121150
if (program.isSourceFileDefaultLibrary(decl.getSourceFile())) {
122151
modifierSet |= 1 << TokenModifier.defaultLibrary;
123152
}
153+
} else if (symbol.declarations.length > 0) {
154+
if (program.isSourceFileDefaultLibrary(symbol.declarations[0].getSourceFile())) {
155+
modifierSet |= 1 << TokenModifier.defaultLibrary;
156+
}
124157
}
125158

126159
collector(node, typeIdx, modifierSet);
@@ -150,7 +183,10 @@ export = function init(modules: { typescript: typeof import("typescript/lib/tsse
150183
} else if (flags & ts.SymbolFlags.TypeParameter) {
151184
return TokenType.typeParameter;
152185
}
153-
const decl = symbol.valueDeclaration || symbol.declarations && symbol.declarations[0];
186+
let decl = symbol.valueDeclaration || symbol.declarations && symbol.declarations[0];
187+
if (decl && decl.kind === ts.SyntaxKind.BindingElement) {
188+
decl = findBindingElementParentDeclaration(<ts.BindingElement>decl);
189+
}
154190
return decl && tokenFromDeclarationMapping[decl.kind];
155191
}
156192

@@ -173,8 +209,21 @@ export = function init(modules: { typescript: typeof import("typescript/lib/tsse
173209
return typeIdx;
174210
}
175211

212+
function findBindingElementParentDeclaration(element: ts.BindingElement): ts.VariableDeclaration | ts.ParameterDeclaration {
213+
while (true) {
214+
if (element.parent.parent.kind === ts.SyntaxKind.BindingElement) {
215+
element = element.parent.parent;
216+
} else {
217+
return element.parent.parent;
218+
}
219+
}
220+
}
221+
176222
function isLocalDeclaration(decl: ts.Declaration, sourceFile: ts.SourceFile): boolean {
177-
if (ts.isVariableDeclaration(decl)) {
223+
if (ts.isBindingElement(decl)) {
224+
decl = findBindingElementParentDeclaration(decl);
225+
}
226+
if (ts.isVariableDeclaration(decl) || ts.isBindingElement(decl)) {
178227
return (!ts.isSourceFile(decl.parent.parent.parent) || ts.isCatchClause(decl.parent)) && decl.getSourceFile() === sourceFile;
179228
} else if (ts.isFunctionDeclaration(decl)) {
180229
return !ts.isSourceFile(decl.parent) && decl.getSourceFile() === sourceFile;
@@ -224,8 +273,10 @@ export = function init(modules: { typescript: typeof import("typescript/lib/tsse
224273
[ts.SyntaxKind.ClassDeclaration]: TokenType.class,
225274
[ts.SyntaxKind.MethodDeclaration]: TokenType.member,
226275
[ts.SyntaxKind.FunctionDeclaration]: TokenType.function,
276+
[ts.SyntaxKind.FunctionExpression]: TokenType.function,
227277
[ts.SyntaxKind.MethodSignature]: TokenType.member,
228278
[ts.SyntaxKind.GetAccessor]: TokenType.property,
279+
[ts.SyntaxKind.SetAccessor]: TokenType.property,
229280
[ts.SyntaxKind.PropertySignature]: TokenType.property,
230281
[ts.SyntaxKind.InterfaceDeclaration]: TokenType.interface,
231282
[ts.SyntaxKind.TypeAliasDeclaration]: TokenType.type,

0 commit comments

Comments
 (0)