From a6eac1f182c3e2f9275bcaa2dddf457a2d8531ec Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 8 Jan 2020 11:14:31 +0100 Subject: [PATCH] only map when necessary --- .../src/modes/javascriptSemanticTokens.ts | 5 +- .../server/src/modes/semanticTokens.ts | 100 +++++++++--------- 2 files changed, 55 insertions(+), 50 deletions(-) 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; } - -