only map when necessary

This commit is contained in:
Martin Aeschlimann 2020-01-08 11:14:31 +01:00
parent c1f573b050
commit a6eac1f182
2 changed files with 55 additions and 50 deletions

View file

@ -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,

View file

@ -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;
}