mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 17:19:48 +01:00
semantic theming: add TokenSelector
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
+11
-4
@@ -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]) : '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<ProbeScope> {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 = <ITokenStyleDefaultExtensionPoint[]>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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user