diff --git a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts
index 38a87159a32..2824a880d4c 100644
--- a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts
+++ b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts
@@ -6,44 +6,63 @@
import { TextDocument, SemanticTokenData } from './languageModes';
import * as ts from 'typescript';
+export function getSemanticTokenLegend() {
+ if (tokenTypes.length !== TokenType._) {
+ console.warn('TokenType has added new entries.');
+ }
+ if (tokenModifiers.length !== TokenModifier._) {
+ console.warn('TokenModifier has added new entries.');
+ }
+ return { types: tokenTypes, modifiers: tokenModifiers };
+}
export function getSemanticTokens(jsLanguageService: ts.LanguageService, currentTextDocument: TextDocument, fileName: string): SemanticTokenData[] {
//https://ts-ast-viewer.com/#code/AQ0g2CmAuwGbALzAJwG4BQZQGNwEMBnQ4AQQEYBmYAb2C22zgEtJwATJVTRxgcwD27AQAp8AGmAAjAJS0A9POB8+7NQ168oscAJz5wANXwAnLug2bsJmAFcTAO2XAA1MHyvgu-UdOeWbOw8ViAAvpagocBAA
let resultTokens: SemanticTokenData[] = [];
+ const collector = (node: ts.Node, typeIdx: number, modifierSet: number) => {
+ resultTokens.push({ start: currentTextDocument.positionAt(node.getStart()), length: node.getWidth(), typeIdx, modifierSet });
+ };
+ collectTokens(jsLanguageService, fileName, { start: 0, length: currentTextDocument.getText().length }, collector);
+
+ return resultTokens;
+}
+
+function collectTokens(jsLanguageService: ts.LanguageService, fileName: string, span: ts.TextSpan, collector: (node: ts.Node, tokenType: number, tokenModifier: number) => void) {
const program = jsLanguageService.getProgram();
if (program) {
const typeChecker = program.getTypeChecker();
function visit(node: ts.Node) {
+ if (!node || !ts.textSpanIntersectsWith(span, node.pos, node.getFullWidth())) {
+ return;
+ }
if (ts.isIdentifier(node)) {
const symbol = typeChecker.getSymbolAtLocation(node);
if (symbol) {
let typeIdx = classifySymbol(symbol);
-
if (typeIdx !== undefined) {
-
let modifierSet = 0;
if (node.parent) {
const parentTypeIdx = tokenFromDeclarationMapping[node.parent.kind];
if (parentTypeIdx === typeIdx && (node.parent).name === node) {
- modifierSet = TokenModifier.declaration;
+ modifierSet = 1 << TokenModifier.declaration;
}
}
const decl = symbol.valueDeclaration;
const modifiers = decl ? ts.getCombinedModifierFlags(decl) : 0;
const nodeFlags = decl ? ts.getCombinedNodeFlags(decl) : 0;
if (modifiers & ts.ModifierFlags.Static) {
- modifierSet |= TokenModifier.static;
+ modifierSet |= 1 << TokenModifier.static;
}
if (modifiers & ts.ModifierFlags.Async) {
- modifierSet |= TokenModifier.async;
+ modifierSet |= 1 << TokenModifier.async;
}
if ((modifiers & ts.ModifierFlags.Readonly) || (nodeFlags & ts.NodeFlags.Const) || (symbol.getFlags() & ts.SymbolFlags.EnumMember)) {
- modifierSet |= TokenModifier.readonly;
+ modifierSet |= 1 << TokenModifier.readonly;
}
- resultTokens.push({ start: currentTextDocument.positionAt(node.getStart()), length: node.getWidth(), typeIdx, modifierSet });
+ collector(node, typeIdx, modifierSet);
}
}
}
@@ -55,12 +74,9 @@ export function getSemanticTokens(jsLanguageService: ts.LanguageService, current
visit(sourceFile);
}
}
-
- return resultTokens;
}
function classifySymbol(symbol: ts.Symbol) {
-
const flags = symbol.getFlags();
if (flags & ts.SymbolFlags.Class) {
return TokenType.class;
@@ -79,38 +95,32 @@ function classifySymbol(symbol: ts.Symbol) {
return decl && tokenFromDeclarationMapping[decl.kind];
}
-
-
-export function getSemanticTokenLegend() {
- return { types: tokenTypes, modifiers: tokenModifiers };
+export const enum TokenType {
+ class, enum, interface, namespace, typeParameter, type, parameter, variable, property, function, member, _
}
-
-const tokenTypes: string[] = ['class', 'enum', 'interface', 'namespace', 'typeParameter', 'type', 'parameter', 'variable', 'property', 'constant', 'function', 'member'];
-const tokenModifiers: string[] = ['declaration', 'static', 'async', 'readonly'];
-
-const enum TokenType {
- 'class' = 0,
- 'enum' = 1,
- 'interface' = 2,
- 'namespace' = 3,
- 'typeParameter' = 4,
- 'type' = 5,
- 'parameter' = 6,
- 'variable' = 7,
- 'property' = 8,
- 'constant' = 9,
- 'function' = 10,
- 'member' = 11
+export const enum TokenModifier {
+ declaration, static, async, readonly, _
}
+const tokenTypes: string[] = [];
+tokenTypes[TokenType.class] = 'class';
+tokenTypes[TokenType.enum] = 'enum';
+tokenTypes[TokenType.interface] = 'interface';
+tokenTypes[TokenType.namespace] = 'namespace';
+tokenTypes[TokenType.typeParameter] = 'typeParameter';
+tokenTypes[TokenType.type] = 'type';
+tokenTypes[TokenType.parameter] = 'parameter';
+tokenTypes[TokenType.variable] = 'variable';
+tokenTypes[TokenType.property] = 'property';
+tokenTypes[TokenType.function] = 'function';
+tokenTypes[TokenType.member] = 'member';
-const enum TokenModifier {
- 'declaration' = 0x01,
- 'static' = 0x02,
- 'async' = 0x04,
- 'readonly' = 0x08,
-}
+const tokenModifiers: string[] = [];
+tokenModifiers[TokenModifier.async] = 'async';
+tokenModifiers[TokenModifier.declaration] = 'declaration';
+tokenModifiers[TokenModifier.readonly] = 'readonly';
+tokenModifiers[TokenModifier.static] = 'static';
const tokenFromDeclarationMapping: { [name: string]: TokenType } = {
[ts.SyntaxKind.VariableDeclaration]: TokenType.variable,