diff --git a/src/vs/platform/theme/common/tokenClassificationRegistry.ts b/src/vs/platform/theme/common/tokenClassificationRegistry.ts index 882d5c7e3ab..2ecc4b0b023 100644 --- a/src/vs/platform/theme/common/tokenClassificationRegistry.ts +++ b/src/vs/platform/theme/common/tokenClassificationRegistry.ts @@ -26,6 +26,11 @@ export interface TokenClassification { modifiers: number; } +export interface TokenSelector { + match(classification: TokenClassification): number; + asString(): string; +} + export interface TokenTypeOrModifierContribution { readonly num: number; readonly id: string; @@ -96,15 +101,13 @@ export interface TokenStyleDefaults { } export interface TokenStylingDefaultRule { - match(classification: TokenClassification): number; - selector: TokenClassification; + selector: TokenSelector; defaults: TokenStyleDefaults; } export interface TokenStylingRule { - match(classification: TokenClassification): number; - value: TokenStyle; - selector: TokenClassification; + style: TokenStyle; + selector: TokenSelector; } /** @@ -124,33 +127,39 @@ export interface ITokenClassificationRegistry { /** * Register a token type to the registry. * @param id The TokenType id as used in theme description files - * @description the description + * @param description the description */ registerTokenType(id: string, description: string): void; /** * Register a token modifier to the registry. * @param id The TokenModifier id as used in theme description files - * @description the description + * @param description the description */ registerTokenModifier(id: string, description: string): void; getTokenClassification(type: string, modifiers: string[]): TokenClassification | undefined; - getTokenStylingRule(classification: TokenClassification, value: TokenStyle): TokenStylingRule; + /** + * Parses a token selector from a selector string. + * @param selectorString selector string in the form (*|type)(.modifier)* + * @returns the parsesd selector + * @throws an error if the string is not a valid selector + */ + parseTokenSelector(selectorString: string): TokenSelector; /** * Register a TokenStyle default to the registry. * @param selector The rule selector * @param defaults The default values */ - registerTokenStyleDefault(selector: TokenClassification, defaults: TokenStyleDefaults): void; + registerTokenStyleDefault(selector: TokenSelector, defaults: TokenStyleDefaults): void; /** * Deregister a TokenStyle default to the registry. * @param selector The rule selector */ - deregisterTokenStyleDefault(selector: TokenClassification): void; + deregisterTokenStyleDefault(selector: TokenSelector): void; /** * Deregister a TokenType from the registry. @@ -277,35 +286,40 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { } - private newMatcher(selector: TokenClassification) { - const score = getTokenStylingScore(selector); - return (classification: TokenClassification) => { - const selectorType = selector.type; - if (selectorType !== TOKEN_TYPE_WILDCARD_NUM && selectorType !== classification.type) { - return -1; - } - const selectorModifier = selector.modifiers; - if ((classification.modifiers & selectorModifier) !== selectorModifier) { - return -1; - } - return score; - }; - } + public parseTokenSelector(selectorString: string): TokenSelector { + const [type, ...modifiers] = selectorString.split('.'); + + const selectorClassification = this.getTokenClassification(type, modifiers); + if (!selectorClassification) { + return { + match: () => -1, + asString: () => selectorString + }; + } + const score = getTokenStylingScore(selectorClassification); - public getTokenStylingRule(selector: TokenClassification, value: TokenStyle): TokenStylingRule { return { - match: this.newMatcher(selector), - value, - selector + match: (classification: TokenClassification) => { + if (selectorClassification.type !== TOKEN_TYPE_WILDCARD_NUM && selectorClassification.type !== classification.type) { + return -1; + } + const selectorModifier = selectorClassification.modifiers; + if ((classification.modifiers & selectorModifier) !== selectorModifier) { + return -1; + } + return score; + }, + asString: () => selectorString }; } - public registerTokenStyleDefault(selector: TokenClassification, defaults: TokenStyleDefaults): void { - this.tokenStylingDefaultRules.push({ selector, match: this.newMatcher(selector), defaults }); + public registerTokenStyleDefault(selector: TokenSelector, defaults: TokenStyleDefaults): void { + this.tokenStylingDefaultRules.push({ selector, defaults }); } - public deregisterTokenStyleDefault(classification: TokenClassification): void { - this.tokenStylingDefaultRules = this.tokenStylingDefaultRules.filter(r => !(r.selector.type === classification.type && r.selector.modifiers === classification.modifiers)); + public deregisterTokenStyleDefault(selector: TokenSelector): void { + const selectorString = selector.asString(); + this.tokenStylingDefaultRules = this.tokenStylingDefaultRules.filter(r => !(r.selector.asString() === selectorString)); } public deregisterTokenType(id: string): void { @@ -361,8 +375,12 @@ function registerDefaultClassifications(): void { tokenClassificationRegistry.registerTokenType(id, description, deprecationMessage); if (scopesToProbe || extendsTC) { - const classification = tokenClassificationRegistry.getTokenClassification(id, []); - tokenClassificationRegistry.registerTokenStyleDefault(classification!, { scopesToProbe, light: extendsTC, dark: extendsTC, hc: extendsTC }); + try { + const selector = tokenClassificationRegistry.parseTokenSelector(id); + tokenClassificationRegistry.registerTokenStyleDefault(selector, { scopesToProbe, light: extendsTC, dark: extendsTC, hc: extendsTC }); + } catch (e) { + console.log(e); + } } return id; } diff --git a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts index 6868714c914..a69ea27e832 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts @@ -27,7 +27,7 @@ import { ITextMateService, IGrammar, IToken, StackElement } from 'vs/workbench/s import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { ColorThemeData, TokenStyleDefinitions, TokenStyleDefinition } from 'vs/workbench/services/themes/common/colorThemeData'; -import { TokenStylingRule, TokenStyleData } from 'vs/platform/theme/common/tokenClassificationRegistry'; +import { TokenStylingRule, TokenStyleData, TokenStyle } from 'vs/platform/theme/common/tokenClassificationRegistry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export interface IEditorSemanticHighlightingOptions { @@ -546,9 +546,9 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget { } else if (isTokenStylingRule(definition)) { const scope = theme.getTokenStylingRuleScope(definition); if (scope === 'setting') { - return `User settings`; // todo: print selector and style once selector is a string + return `User settings: ${definition.selector.asString()} - ${this._renderStyleProperty(definition.style, property)}`; } else if (scope === 'theme') { - return `Color theme`; // todo: print selector and style once selector is a string + return `Color theme: ${definition.selector.asString()} - ${this._renderStyleProperty(definition.style, property)}`; } return ''; } else if (typeof definition === 'string') { @@ -560,7 +560,14 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget { } return ''; } else { - return String(definition[property]); + return this._renderStyleProperty(definition, property); + } + } + + private _renderStyleProperty(style: TokenStyle, property: keyof TokenStyleData) { + switch (property) { + case 'foreground': return style.foreground ? Color.Format.CSS.formatHexA(style.foreground, true) : ''; + default: return style[property] !== undefined ? String(style[property]) : ''; } } diff --git a/src/vs/workbench/services/themes/common/colorThemeData.ts b/src/vs/workbench/services/themes/common/colorThemeData.ts index 225a3ed565f..b781d3dd8d8 100644 --- a/src/vs/workbench/services/themes/common/colorThemeData.ts +++ b/src/vs/workbench/services/themes/common/colorThemeData.ts @@ -158,7 +158,7 @@ export class ColorThemeData implements IColorTheme { } if (this.tokenStylingRules === undefined) { for (const rule of tokenClassificationRegistry.getTokenStylingDefaultRules()) { - const matchScore = rule.match(classification); + const matchScore = rule.selector.match(classification); if (matchScore >= 0) { let style: TokenStyle | undefined; if (rule.defaults.scopesToProbe) { @@ -178,16 +178,16 @@ export class ColorThemeData implements IColorTheme { } } else { for (const rule of this.tokenStylingRules) { - const matchScore = rule.match(classification); + const matchScore = rule.selector.match(classification); if (matchScore >= 0) { - _processStyle(matchScore, rule.value, rule); + _processStyle(matchScore, rule.style, rule); } } } for (const rule of this.customTokenStylingRules) { - const matchScore = rule.match(classification); + const matchScore = rule.selector.match(classification); if (matchScore >= 0) { - _processStyle(matchScore, rule.value, rule); + _processStyle(matchScore, rule.style, rule); } } return TokenStyle.fromData(result); @@ -222,7 +222,7 @@ export class ColorThemeData implements IColorTheme { }); if (this.tokenStylingRules) { - this.tokenStylingRules.forEach(r => index.add(r.value.foreground)); + this.tokenStylingRules.forEach(r => index.add(r.style.foreground)); } else { tokenClassificationRegistry.getTokenStylingDefaultRules().forEach(r => { const defaultColor = r.defaults[this.type]; @@ -231,7 +231,7 @@ export class ColorThemeData implements IColorTheme { } }); } - this.customTokenStylingRules.forEach(r => index.add(r.value.foreground)); + this.customTokenStylingRules.forEach(r => index.add(r.style.foreground)); this.tokenColorIndex = index; } @@ -704,9 +704,8 @@ function getScopeMatcher(rule: ITextMateThemingRule): Matcher { function readCustomTokenStyleRules(tokenStylingRuleSection: IExperimentalTokenStyleCustomizations, result: TokenStylingRule[] = []) { for (let key in tokenStylingRuleSection) { if (key[0] !== '[') { - const [type, ...modifiers] = key.split('.'); - const classification = tokenClassificationRegistry.getTokenClassification(type, modifiers); - if (classification) { + try { + const selector = tokenClassificationRegistry.parseTokenSelector(key); const settings = tokenStylingRuleSection[key]; let style: TokenStyle | undefined; if (typeof settings === 'string') { @@ -715,8 +714,10 @@ function readCustomTokenStyleRules(tokenStylingRuleSection: IExperimentalTokenSt style = TokenStyle.fromSettings(settings.foreground, settings.fontStyle); } if (style) { - result.push(tokenClassificationRegistry.getTokenStylingRule(classification, style)); + result.push({ selector, style }); } + } catch (e) { + // invalid selector, ignore } } } diff --git a/src/vs/workbench/services/themes/common/tokenClassificationExtensionPoint.ts b/src/vs/workbench/services/themes/common/tokenClassificationExtensionPoint.ts index d0b63fc59e9..127177d52cf 100644 --- a/src/vs/workbench/services/themes/common/tokenClassificationExtensionPoint.ts +++ b/src/vs/workbench/services/themes/common/tokenClassificationExtensionPoint.ts @@ -237,20 +237,23 @@ export class TokenClassificationExtensionPoints { tokenStyleDefault.dark = validateStyle(contribution.dark, 'semanticTokenStyleDefaults.dark', collector); tokenStyleDefault.hc = validateStyle(contribution.highContrast, 'semanticTokenStyleDefaults.highContrast', collector); - const [type, ...modifiers] = contribution.selector.split('.'); - const classification = tokenClassificationRegistry.getTokenClassification(type, modifiers); - if (classification) { - tokenClassificationRegistry.registerTokenStyleDefault(classification, tokenStyleDefault); + try { + const selector = tokenClassificationRegistry.parseTokenSelector(contribution.selector); + tokenClassificationRegistry.registerTokenStyleDefault(selector, tokenStyleDefault); + } catch (e) { + collector.error(nls.localize('invalid.selector.parsing', "configuration.semanticTokenStyleDefaults.selector': Problems parsing {0}.", contribution.selector)); + // invalid selector, ignore } } } for (const extension of delta.removed) { const extensionValue = extension.value; for (const contribution of extensionValue) { - const [type, ...modifiers] = contribution.selector.split('.'); - const classification = tokenClassificationRegistry.getTokenClassification(type, modifiers); - if (classification) { - tokenClassificationRegistry.deregisterTokenStyleDefault(classification); + try { + const selector = tokenClassificationRegistry.parseTokenSelector(contribution.selector); + tokenClassificationRegistry.deregisterTokenStyleDefault(selector); + } catch (e) { + // invalid selector, ignore } } }