diff --git a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts
index b215458d641..fa615319354 100644
--- a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts
+++ b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts
@@ -64,7 +64,7 @@ export function getSemanticTokenLegend() {
const tokenTypes: string[] = ['class', 'enum', 'interface', 'namespace', 'typeParameter', 'type', 'parameter', 'variable', 'property', 'constant', 'function', 'member'];
const tokenModifiers: string[] = ['declaration', 'static', 'async'];
-enum TokenType {
+const enum TokenType {
'class' = 0,
'enum' = 1,
'interface' = 2,
@@ -79,7 +79,8 @@ enum TokenType {
'member' = 11
}
-enum TokenModifier {
+
+const enum TokenModifier {
'declaration' = 0x01,
'static' = 0x02,
'async' = 0x04,
diff --git a/extensions/html-language-features/server/src/modes/semanticTokens.ts b/extensions/html-language-features/server/src/modes/semanticTokens.ts
index ebdd56656d2..027c17e5dd9 100644
--- a/extensions/html-language-features/server/src/modes/semanticTokens.ts
+++ b/extensions/html-language-features/server/src/modes/semanticTokens.ts
@@ -7,8 +7,8 @@ import { SemanticTokenData, Range, TextDocument, LanguageModes, Position } from
import { beforeOrSame } from '../utils/positions';
interface LegendMapping {
- types: number[];
- modifiers: number[];
+ types: number[] | undefined;
+ modifiers: number[] | undefined;
}
export interface SemanticTokenProvider {
@@ -19,78 +19,84 @@ export interface SemanticTokenProvider {
export function newSemanticTokenProvider(languageModes: LanguageModes): SemanticTokenProvider {
- // build legend across language
- const types: string[] = [];
- const modifiers: string[] = [];
-
+ // combined legend across modes
+ const legend = { types: [], modifiers: [] };
const legendMappings: { [modeId: string]: LegendMapping } = {};
for (let mode of languageModes.getAllModes()) {
if (mode.getSemanticTokenLegend && mode.getSemanticTokens) {
- const legend = mode.getSemanticTokenLegend();
- const legendMapping: LegendMapping = { types: [], modifiers: [] };
- for (let type of legend.types) {
- let index = types.indexOf(type);
- if (index === -1) {
- index = types.length;
- types.push(type);
- }
- legendMapping.types.push(index);
- }
- for (let modifier of legend.modifiers) {
- let index = modifiers.indexOf(modifier);
- if (index === -1) {
- index = modifiers.length;
- modifiers.push(modifier);
- }
- legendMapping.modifiers.push(index);
- }
- legendMappings[mode.getId()] = legendMapping;
+ const modeLegend = mode.getSemanticTokenLegend();
+ legendMappings[mode.getId()] = { types: createMapping(modeLegend.types, legend.types), modifiers: createMapping(modeLegend.modifiers, legend.modifiers) };
}
-
}
return {
- legend: { types, modifiers },
+ legend,
getSemanticTokens(document: TextDocument, ranges?: Range[]): number[] {
const allTokens: SemanticTokenData[] = [];
for (let mode of languageModes.getAllModesInDocument(document)) {
if (mode.getSemanticTokens) {
const mapping = legendMappings[mode.getId()];
const tokens = mode.getSemanticTokens(document);
+ applyTypesMapping(tokens, mapping.types);
+ applyModifiersMapping(tokens, mapping.modifiers);
for (let token of tokens) {
- allTokens.push(applyMapping(token, mapping));
+ allTokens.push(token);
}
}
}
- return encodeAndFilterTokens(allTokens, ranges);
+ return encodeTokens(allTokens, ranges);
}
};
}
-function applyMapping(token: SemanticTokenData, legendMapping: LegendMapping): SemanticTokenData {
- token.typeIdx = legendMapping.types[token.typeIdx];
-
- let modifierSet = token.modifierSet;
- if (modifierSet) {
- let index = 0;
- let result = 0;
- const mapping = legendMapping.modifiers;
- while (modifierSet > 0) {
- if ((modifierSet & 1) !== 0) {
- result = result + (1 << mapping[index]);
- }
- index++;
- modifierSet = modifierSet >> 1;
+function createMapping(origLegend: string[], newLegend: string[]): number[] | undefined {
+ const mapping: number[] = [];
+ let needsMapping = false;
+ for (let origIndex = 0; origIndex < origLegend.length; origIndex++) {
+ const entry = origLegend[origIndex];
+ let newIndex = newLegend.indexOf(entry);
+ if (newIndex === -1) {
+ newIndex = newLegend.length;
+ newLegend.push(entry);
+ }
+ mapping.push(newIndex);
+ needsMapping = needsMapping || (newIndex !== origIndex);
+ }
+ return needsMapping ? mapping : undefined;
+}
+
+function applyTypesMapping(tokens: SemanticTokenData[], typesMapping: number[] | undefined): void {
+ if (typesMapping) {
+ for (let token of tokens) {
+ token.typeIdx = typesMapping[token.typeIdx];
+ }
+ }
+}
+
+function applyModifiersMapping(tokens: SemanticTokenData[], modifiersMapping: number[] | undefined): void {
+ if (modifiersMapping) {
+ for (let token of tokens) {
+ let modifierSet = token.modifierSet;
+ if (modifierSet) {
+ let index = 0;
+ let result = 0;
+ while (modifierSet > 0) {
+ if ((modifierSet & 1) !== 0) {
+ result = result + (1 << modifiersMapping[index]);
+ }
+ index++;
+ modifierSet = modifierSet >> 1;
+ }
+ token.modifierSet = result;
+ }
}
- token.modifierSet = result;
}
- return token;
}
const fullRange = [Range.create(Position.create(0, 0), Position.create(Number.MAX_VALUE, 0))];
-function encodeAndFilterTokens(tokens: SemanticTokenData[], ranges?: Range[]): number[] {
+function encodeTokens(tokens: SemanticTokenData[], ranges?: Range[]): number[] {
const resultTokens = tokens.sort((d1, d2) => d1.start.line - d2.start.line || d1.start.character - d2.start.character);
if (ranges) {
@@ -131,5 +137,3 @@ function encodeAndFilterTokens(tokens: SemanticTokenData[], ranges?: Range[]): n
}
return encodedResult;
}
-
-