Skip to content

Commit a0c3fcd

Browse files
committed
token types super types
1 parent 2d58406 commit a0c3fcd

4 files changed

Lines changed: 87 additions & 27 deletions

File tree

src/vs/platform/theme/common/tokenClassificationRegistry.ts

Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,15 @@ export const fontStylePattern = '^(\\s*(-?italic|-?bold|-?underline))*\\s*$';
2222

2323
export interface TokenSelector {
2424
match(type: string, modifiers: string[]): number;
25-
asString(): string;
25+
readonly selectorString: string;
2626
}
2727

2828
export interface TokenTypeOrModifierContribution {
2929
readonly num: number;
3030
readonly id: string;
31+
readonly superType?: string;
3132
readonly description: string;
32-
readonly deprecationMessage: string | undefined;
33+
readonly deprecationMessage?: string;
3334
}
3435

3536

@@ -123,7 +124,7 @@ export interface ITokenClassificationRegistry {
123124
* @param id The TokenType id as used in theme description files
124125
* @param description the description
125126
*/
126-
registerTokenType(id: string, description: string): void;
127+
registerTokenType(id: string, description: string, superType?: string, deprecationMessage?: string): void;
127128

128129
/**
129130
* Register a token modifier to the registry.
@@ -197,6 +198,8 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
197198

198199
private tokenStylingDefaultRules: TokenStylingDefaultRule[] = [];
199200

201+
private typeHierarchy: { [id: string]: string[] };
202+
200203
private tokenStylingSchema: IJSONSchema & { properties: IJSONSchemaMap } = {
201204
type: 'object',
202205
properties: {},
@@ -233,18 +236,23 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
233236
constructor() {
234237
this.tokenTypeById = {};
235238
this.tokenModifierById = {};
239+
this.typeHierarchy = {};
236240
}
237241

238-
public registerTokenType(id: string, description: string, deprecationMessage?: string): void {
242+
public registerTokenType(id: string, description: string, superType?: string, deprecationMessage?: string): void {
239243
if (!id.match(typeAndModifierIdPattern)) {
240244
throw new Error('Invalid token type id.');
241245
}
246+
if (superType && !superType.match(typeAndModifierIdPattern)) {
247+
throw new Error('Invalid token super type id.');
248+
}
242249

243250
const num = this.currentTypeNumber++;
244-
let tokenStyleContribution: TokenTypeOrModifierContribution = { num, id, description, deprecationMessage };
251+
let tokenStyleContribution: TokenTypeOrModifierContribution = { num, id, superType, description, deprecationMessage };
245252
this.tokenTypeById[id] = tokenStyleContribution;
246253

247254
this.tokenStylingSchema.properties[id] = getStylingSchemeEntry(description, deprecationMessage);
255+
this.typeHierarchy = {};
248256
}
249257

250258
public registerTokenModifier(id: string, description: string, deprecationMessage?: string): void {
@@ -266,25 +274,30 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
266274
if (!selectorType) {
267275
return {
268276
match: () => -1,
269-
asString: () => selectorString
277+
selectorString
270278
};
271279
}
272-
const score = selectorModifiers.length + ((selectorString !== TOKEN_TYPE_WILDCARD) ? 1 : 0);
273280

274281
return {
275282
match: (type: string, modifiers: string[]) => {
276-
if (selectorType !== TOKEN_TYPE_WILDCARD && selectorType !== type) {
277-
return -1;
283+
let score = 0;
284+
if (selectorType !== TOKEN_TYPE_WILDCARD) {
285+
const hierarchy = this.getTypeHierarchy(type);
286+
const level = hierarchy.indexOf(selectorType);
287+
if (level === -1) {
288+
return -1;
289+
}
290+
score = 100 - level;
278291
}
279292
// all selector modifiers must be present
280293
for (const selectorModifier of selectorModifiers) {
281294
if (modifiers.indexOf(selectorModifier) === -1) {
282295
return -1;
283296
}
284297
}
285-
return score;
298+
return score + selectorModifiers.length * 100;
286299
},
287-
asString: () => selectorString
300+
selectorString
288301
};
289302
}
290303

@@ -293,13 +306,14 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
293306
}
294307

295308
public deregisterTokenStyleDefault(selector: TokenSelector): void {
296-
const selectorString = selector.asString();
297-
this.tokenStylingDefaultRules = this.tokenStylingDefaultRules.filter(r => !(r.selector.asString() === selectorString));
309+
const selectorString = selector.selectorString;
310+
this.tokenStylingDefaultRules = this.tokenStylingDefaultRules.filter(r => r.selector.selectorString !== selectorString);
298311
}
299312

300313
public deregisterTokenType(id: string): void {
301314
delete this.tokenTypeById[id];
302315
delete this.tokenStylingSchema.properties[id];
316+
this.typeHierarchy = {};
303317
}
304318

305319
public deregisterTokenModifier(id: string): void {
@@ -323,6 +337,19 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
323337
return this.tokenStylingDefaultRules;
324338
}
325339

340+
private getTypeHierarchy(typeId: string): string[] {
341+
let hierarchy = this.typeHierarchy[typeId];
342+
if (!hierarchy) {
343+
this.typeHierarchy[typeId] = hierarchy = [typeId];
344+
let type = this.tokenTypeById[typeId];
345+
while (type && type.superType) {
346+
hierarchy.push(type.superType);
347+
type = this.tokenTypeById[type.superType];
348+
}
349+
}
350+
return hierarchy;
351+
}
352+
326353

327354
public toString() {
328355
let sorter = (a: string, b: string) => {
@@ -346,20 +373,23 @@ platform.Registry.add(Extensions.TokenClassificationContribution, tokenClassific
346373
registerDefaultClassifications();
347374

348375
function registerDefaultClassifications(): void {
349-
function registerTokenType(id: string, description: string, scopesToProbe: ProbeScope[] = [], extendsTC?: string, deprecationMessage?: string): string {
350-
tokenClassificationRegistry.registerTokenType(id, description, deprecationMessage);
351-
352-
if (scopesToProbe || extendsTC) {
353-
try {
354-
const selector = tokenClassificationRegistry.parseTokenSelector(id);
355-
tokenClassificationRegistry.registerTokenStyleDefault(selector, { scopesToProbe, light: extendsTC, dark: extendsTC, hc: extendsTC });
356-
} catch (e) {
357-
console.log(e);
358-
}
376+
function registerTokenType(id: string, description: string, scopesToProbe: ProbeScope[] = [], superType?: string, deprecationMessage?: string): string {
377+
tokenClassificationRegistry.registerTokenType(id, description, superType, deprecationMessage);
378+
if (scopesToProbe) {
379+
registerTokenStyleDefault(id, scopesToProbe);
359380
}
360381
return id;
361382
}
362383

384+
function registerTokenStyleDefault(selectorString: string, scopesToProbe: ProbeScope[]) {
385+
try {
386+
const selector = tokenClassificationRegistry.parseTokenSelector(selectorString);
387+
tokenClassificationRegistry.registerTokenStyleDefault(selector, { scopesToProbe });
388+
} catch (e) {
389+
console.log(e);
390+
}
391+
}
392+
363393
// default token types
364394

365395
registerTokenType('comment', nls.localize('comment', "Style for comments."), [['comment']]);

src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -546,9 +546,9 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget {
546546
} else if (isTokenStylingRule(definition)) {
547547
const scope = theme.getTokenStylingRuleScope(definition);
548548
if (scope === 'setting') {
549-
return `User settings: ${definition.selector.asString()} - ${this._renderStyleProperty(definition.style, property)}`;
549+
return `User settings: ${definition.selector.selectorString} - ${this._renderStyleProperty(definition.style, property)}`;
550550
} else if (scope === 'theme') {
551-
return `Color theme: ${definition.selector.asString()} - ${this._renderStyleProperty(definition.style, property)}`;
551+
return `Color theme: ${definition.selector.selectorString} - ${this._renderStyleProperty(definition.style, property)}`;
552552
}
553553
return '';
554554
} else if (typeof definition === 'string') {

src/vs/workbench/services/themes/common/tokenClassificationExtensionPoint.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { textmateColorSettingsSchemaId } from 'vs/workbench/services/themes/comm
1111
interface ITokenTypeExtensionPoint {
1212
id: string;
1313
description: string;
14+
superType?: string;
1415
}
1516

1617
interface ITokenModifierExtensionPoint {
@@ -136,6 +137,11 @@ export class TokenClassificationExtensionPoints {
136137
collector.error(nls.localize('invalid.id.format', "'configuration.{0}.id' must follow the pattern letterOrDigit[-_letterOrDigit]*", extensionPoint));
137138
return false;
138139
}
140+
const superType = (contribution as ITokenTypeExtensionPoint).superType;
141+
if (superType && !superType.match(typeAndModifierIdPattern)) {
142+
collector.error(nls.localize('invalid.superType.format', "'configuration.{0}.superType' must follow the pattern letterOrDigit[-_letterOrDigit]*", extensionPoint));
143+
return false;
144+
}
139145
if (typeof contribution.description !== 'string' || contribution.id.length === 0) {
140146
collector.error(nls.localize('invalid.description', "'configuration.{0}.description' must be defined and can not be empty", extensionPoint));
141147
return false;
@@ -172,7 +178,7 @@ export class TokenClassificationExtensionPoints {
172178
}
173179
for (const contribution of extensionValue) {
174180
if (validateTypeOrModifier(contribution, 'semanticTokenType', collector)) {
175-
tokenClassificationRegistry.registerTokenType(contribution.id, contribution.description);
181+
tokenClassificationRegistry.registerTokenType(contribution.id, contribution.description, contribution.superType);
176182
}
177183
}
178184
}

src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import { ColorThemeData } from 'vs/workbench/services/themes/common/colorThemeData';
77
import * as assert from 'assert';
88
import { ITokenColorCustomizations } from 'vs/workbench/services/themes/common/workbenchThemeService';
9-
import { TokenStyle } from 'vs/platform/theme/common/tokenClassificationRegistry';
9+
import { TokenStyle, getTokenClassificationRegistry } from 'vs/platform/theme/common/tokenClassificationRegistry';
1010
import { Color } from 'vs/base/common/color';
1111
import { isString } from 'vs/base/common/types';
1212
import { FileService } from 'vs/platform/files/common/fileService';
@@ -313,4 +313,28 @@ suite('Themes - TokenStyleResolving', () => {
313313
});
314314

315315
});
316+
317+
test('super type', async () => {
318+
getTokenClassificationRegistry().registerTokenType('myTestInterface', 'A type just for testing', 'interface');
319+
try {
320+
const themeData = ColorThemeData.createLoadedEmptyTheme('test', 'test');
321+
themeData.setCustomColors({ 'editor.foreground': '#000000' });
322+
themeData.setCustomTokenStyleRules({
323+
'type': '#ff0000',
324+
'interface': { fontStyle: 'italic' },
325+
'type.static': { fontStyle: 'bold' }
326+
});
327+
328+
assertTokenStyles(themeData, { 'myTestInterface': ts('#ff0000', { italic: true }) });
329+
assertTokenStyles(themeData, { 'myTestInterface.static': ts('#ff0000', { italic: true, bold: true }) });
330+
331+
themeData.setCustomTokenStyleRules({
332+
'type': '#ff0000',
333+
'interface': { foreground: '#ff00ff', fontStyle: 'italic' }
334+
});
335+
assertTokenStyles(themeData, { 'myTestInterface': ts('#ff00ff', { italic: true }) });
336+
} finally {
337+
getTokenClassificationRegistry().deregisterTokenType('myTestInterface');
338+
}
339+
});
316340
});

0 commit comments

Comments
 (0)