diff --git a/src/vs/platform/theme/common/tokenClassificationRegistry.ts b/src/vs/platform/theme/common/tokenClassificationRegistry.ts index 46d85c25fad340ae34bf2c98d0acd7aa2f765d98..14a1b52e4c32c08d68726e503b392187548afa86 100644 --- a/src/vs/platform/theme/common/tokenClassificationRegistry.ts +++ b/src/vs/platform/theme/common/tokenClassificationRegistry.ts @@ -24,7 +24,7 @@ export const typeAndModifierIdPattern = `^${idPattern}$`; export const selectorPattern = `^(${idPattern}|\\*)(\\${CLASSIFIER_MODIFIER_SEPARATOR}${idPattern})*(\\${TOKEN_CLASSIFIER_LANGUAGE_SEPARATOR}${idPattern})?$`; -export const fontStylePattern = '^(\\s*(-?italic|-?bold|-?underline))*\\s*$'; +export const fontStylePattern = '^(\\s*(italic|bold|underline))*\\s*$'; export interface TokenSelector { match(type: string, modifiers: string[], language: string): number; @@ -124,18 +124,18 @@ export interface TokenStyleDefaults { hc?: TokenStyleValue; } -export interface TokenStylingDefaultRule { +export interface SemanticTokenDefaultRule { selector: TokenSelector; defaults: TokenStyleDefaults; } -export interface TokenStylingRule { +export interface SemanticTokenRule { style: TokenStyle; selector: TokenSelector; } -export namespace TokenStylingRule { - export function fromJSONObject(registry: ITokenClassificationRegistry, o: any): TokenStylingRule | undefined { +export namespace SemanticTokenRule { + export function fromJSONObject(registry: ITokenClassificationRegistry, o: any): SemanticTokenRule | undefined { if (o && typeof o._selector === 'string' && o._style) { const style = TokenStyle.fromJSONObject(o._style); if (style) { @@ -147,13 +147,13 @@ export namespace TokenStylingRule { } return undefined; } - export function toJSONObject(rule: TokenStylingRule): any { + export function toJSONObject(rule: SemanticTokenRule): any { return { _selector: rule.selector.id, _style: TokenStyle.toJSONObject(rule.style) }; } - export function equals(r1: TokenStylingRule | undefined, r2: TokenStylingRule | undefined) { + export function equals(r1: SemanticTokenRule | undefined, r2: SemanticTokenRule | undefined) { if (r1 === r2) { return true; } @@ -161,7 +161,7 @@ export namespace TokenStylingRule { && r1.selector && r2.selector && r1.selector.id === r2.selector.id && TokenStyle.equals(r1.style, r2.style); } - export function is(r: any): r is TokenStylingRule { + export function is(r: any): r is SemanticTokenRule { return r && r.selector && typeof r.selector.selectorString === 'string' && TokenStyle.is(r.style); } } @@ -239,7 +239,7 @@ export interface ITokenClassificationRegistry { /** * The styling rules to used when a schema does not define any styling rules. */ - getTokenStylingDefaultRules(): TokenStylingDefaultRule[]; + getTokenStylingDefaultRules(): SemanticTokenDefaultRule[]; /** * JSON schema for an object to assign styling to token classifications @@ -258,14 +258,18 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { private tokenTypeById: { [key: string]: TokenTypeOrModifierContribution }; private tokenModifierById: { [key: string]: TokenTypeOrModifierContribution }; - private tokenStylingDefaultRules: TokenStylingDefaultRule[] = []; + private tokenStylingDefaultRules: SemanticTokenDefaultRule[] = []; private typeHierarchy: { [id: string]: string[] }; - private tokenStylingSchema: IJSONSchema & { properties: IJSONSchemaMap } = { + private tokenStylingSchema: IJSONSchema & { properties: IJSONSchemaMap, patternProperties: IJSONSchemaMap } = { type: 'object', properties: {}, - additionalProperties: getStylingSchemeEntry(), + patternProperties: { + [selectorPattern]: getStylingSchemeEntry() + }, + //errorMessage: nls.localize('schema.token.errors', 'Valid token selectors have the form (*|tokenType)(.tokenModifier)*(:tokenLanguage)?.'), + additionalProperties: false, definitions: { style: { type: 'object', @@ -325,7 +329,8 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { let tokenStyleContribution: TokenTypeOrModifierContribution = { num, id, superType, description, deprecationMessage }; this.tokenTypeById[id] = tokenStyleContribution; - this.tokenStylingSchema.properties[id] = getStylingSchemeEntry(description, deprecationMessage); + const stylingSchemeEntry = getStylingSchemeEntry(description, deprecationMessage); + this.tokenStylingSchema.properties[id] = stylingSchemeEntry; this.typeHierarchy = {}; } @@ -413,7 +418,7 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { return this.tokenStylingSchema; } - public getTokenStylingDefaultRules(): TokenStylingDefaultRule[] { + public getTokenStylingDefaultRules(): SemanticTokenDefaultRule[] { return this.tokenStylingDefaultRules; } diff --git a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts index 133f4febc65ca3121b5fc17f178c9eb1f04796b9..3f5ac4d934ee57439613badc40e1f8c5021d67e3 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, TextMateThemingRuleDefinitions } from 'vs/workbench/services/themes/common/colorThemeData'; -import { TokenStylingRule, TokenStyleData, TokenStyle } from 'vs/platform/theme/common/tokenClassificationRegistry'; +import { SemanticTokenRule, TokenStyleData, TokenStyle } from 'vs/platform/theme/common/tokenClassificationRegistry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export interface IEditorSemanticHighlightingOptions { @@ -557,7 +557,7 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget { return `${escape(scopesDefinition.scope.join(' '))}
${strScopes}\n${JSON.stringify(matchingRule.settings, null, '\t')}`; } return ''; - } else if (TokenStylingRule.is(definition)) { + } else if (SemanticTokenRule.is(definition)) { const scope = theme.getTokenStylingRuleScope(definition); if (scope === 'setting') { return `User settings: ${definition.selector.id} - ${this._renderStyleProperty(definition.style, property)}`; diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index f98a3dd8b3472a9e963de0f8e643bc423c8defd1..5097ebc2918d1af18fe2f61bf1d088f31b509ce5 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -218,8 +218,8 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.currentColorTheme.setCustomTokenColors(this.settings.tokenColorCustomizations); hasColorChanges = true; } - if (e.affectsConfiguration(ThemeSettings.TOKEN_COLOR_CUSTOMIZATIONS_EXPERIMENTAL)) { - this.currentColorTheme.setCustomTokenStyleRules(this.settings.tokenStylesCustomizations); + if (e.affectsConfiguration(ThemeSettings.SEMANTIC_TOKEN_COLOR_CUSTOMIZATIONS) || e.affectsConfiguration(ThemeSettings.TOKEN_COLOR_CUSTOMIZATIONS_EXPERIMENTAL)) { + this.currentColorTheme.setCustomSemanticTokenColors(this.settings.semanticTokenColorCustomizations, this.settings.experimentalSemanticTokenColorCustomizations); hasColorChanges = true; } if (hasColorChanges) { diff --git a/src/vs/workbench/services/themes/common/colorThemeData.ts b/src/vs/workbench/services/themes/common/colorThemeData.ts index 37c7efb72ba248fb16caf5d3ed85c111f3e7a712..df2c0f3c4bcbaebd4bf4f1bb04096aa747375de0 100644 --- a/src/vs/workbench/services/themes/common/colorThemeData.ts +++ b/src/vs/workbench/services/themes/common/colorThemeData.ts @@ -6,7 +6,7 @@ import { basename } from 'vs/base/common/path'; import * as Json from 'vs/base/common/json'; import { Color } from 'vs/base/common/color'; -import { ExtensionData, ITokenColorCustomizations, ITextMateThemingRule, IWorkbenchColorTheme, IColorMap, IThemeExtensionPoint, VS_LIGHT_THEME, VS_HC_THEME, IColorCustomizations, IExperimentalTokenStyleCustomizations, ISemanticTokenColorizationSetting } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { ExtensionData, ITokenColorCustomizations, ITextMateThemingRule, IWorkbenchColorTheme, IColorMap, IThemeExtensionPoint, VS_LIGHT_THEME, VS_HC_THEME, IColorCustomizations, ISemanticTokenRules, ISemanticTokenColorizationSetting, ISemanticTokenColorCustomizations, IExperimentalSemanticTokenColorCustomizations } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { convertSettings } from 'vs/workbench/services/themes/common/themeCompatibility'; import * as nls from 'vs/nls'; import * as types from 'vs/base/common/types'; @@ -20,7 +20,7 @@ import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages'; import { URI } from 'vs/base/common/uri'; import { parse as parsePList } from 'vs/workbench/services/themes/common/plistParser'; import { startsWith } from 'vs/base/common/strings'; -import { TokenStyle, ProbeScope, TokenStylingRule, getTokenClassificationRegistry, TokenStyleValue, TokenStyleData, parseClassifierString } from 'vs/platform/theme/common/tokenClassificationRegistry'; +import { TokenStyle, SemanticTokenRule, ProbeScope, getTokenClassificationRegistry, TokenStyleValue, TokenStyleData, parseClassifierString } from 'vs/platform/theme/common/tokenClassificationRegistry'; import { MatcherWithPriority, Matcher, createMatchers } from 'vs/workbench/services/themes/common/textMateScopeMatcher'; import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; import { CharCode } from 'vs/base/common/charCode'; @@ -42,7 +42,7 @@ const tokenGroupToScopesMap = { }; -export type TokenStyleDefinition = TokenStylingRule | ProbeScope[] | TokenStyleValue; +export type TokenStyleDefinition = SemanticTokenRule | ProbeScope[] | TokenStyleValue; export type TokenStyleDefinitions = { [P in keyof TokenStyleData]?: TokenStyleDefinition | undefined }; export type TextMateThemingRuleDefinitions = { [P in keyof TokenStyleData]?: ITextMateThemingRule | undefined; } & { scope?: ProbeScope; }; @@ -62,14 +62,15 @@ export class ColorThemeData implements IWorkbenchColorTheme { private themeSemanticHighlighting: boolean | undefined; private customSemanticHighlighting: boolean | undefined; + private customSemanticHighlightingDeprecated: boolean | undefined; private themeTokenColors: ITextMateThemingRule[] = []; private customTokenColors: ITextMateThemingRule[] = []; private colorMap: IColorMap = {}; private customColorMap: IColorMap = {}; - private tokenStylingRules: TokenStylingRule[] = []; - private customTokenStylingRules: TokenStylingRule[] = []; + private semanticTokenRules: SemanticTokenRule[] = []; + private customSemanticTokenRules: SemanticTokenRule[] = []; private themeTokenScopeMatchers: Matcher[] | undefined; private customTokenScopeMatchers: Matcher[] | undefined; @@ -85,7 +86,13 @@ export class ColorThemeData implements IWorkbenchColorTheme { } get semanticHighlighting(): boolean { - return this.customSemanticHighlighting !== undefined ? this.customSemanticHighlighting : !!this.themeSemanticHighlighting; + if (this.customSemanticHighlighting !== undefined) { + return this.customSemanticHighlighting; + } + if (this.customSemanticHighlightingDeprecated !== undefined) { + return this.customSemanticHighlightingDeprecated; + } + return !!this.themeSemanticHighlighting; } get tokenColors(): ITextMateThemingRule[] { @@ -170,18 +177,16 @@ export class ColorThemeData implements IWorkbenchColorTheme { } } } - for (const rule of this.tokenStylingRules) { - const matchScore = rule.selector.match(type, modifiers, language); - if (matchScore >= 0) { - _processStyle(matchScore, rule.style, rule); - } - } - for (const rule of this.customTokenStylingRules) { + function _processSemanticTokenRule(rule: SemanticTokenRule) { const matchScore = rule.selector.match(type, modifiers, language); if (matchScore >= 0) { _processStyle(matchScore, rule.style, rule); } } + + this.semanticTokenRules.forEach(_processSemanticTokenRule); + this.customSemanticTokenRules.forEach(_processSemanticTokenRule); + let hasUndefinedStyleProperty = false; for (let k in score) { const key = k as keyof TokenStyle; @@ -240,14 +245,14 @@ export class ColorThemeData implements IWorkbenchColorTheme { index.add(rule.settings.background); }); - this.tokenStylingRules.forEach(r => index.add(r.style.foreground)); + this.semanticTokenRules.forEach(r => index.add(r.style.foreground)); tokenClassificationRegistry.getTokenStylingDefaultRules().forEach(r => { const defaultColor = r.defaults[this.type]; if (defaultColor && typeof defaultColor === 'object') { index.add(defaultColor.foreground); } }); - this.customTokenStylingRules.forEach(r => index.add(r.style.foreground)); + this.customSemanticTokenRules.forEach(r => index.add(r.style.foreground)); this.tokenColorIndex = index; } @@ -273,11 +278,11 @@ export class ColorThemeData implements IWorkbenchColorTheme { }; } - public getTokenStylingRuleScope(rule: TokenStylingRule): 'setting' | 'theme' | undefined { - if (this.customTokenStylingRules.indexOf(rule) !== -1) { + public getTokenStylingRuleScope(rule: SemanticTokenRule): 'setting' | 'theme' | undefined { + if (this.customSemanticTokenRules.indexOf(rule) !== -1) { return 'setting'; } - if (this.tokenStylingRules.indexOf(rule) !== -1) { + if (this.semanticTokenRules.indexOf(rule) !== -1) { return 'theme'; } return undefined; @@ -346,7 +351,7 @@ export class ColorThemeData implements IWorkbenchColorTheme { public setCustomizations(settings: ThemeConfiguration) { this.setCustomColors(settings.colorCustomizations); this.setCustomTokenColors(settings.tokenColorCustomizations); - this.setCustomTokenStyleRules(settings.tokenStylesCustomizations); + this.setCustomSemanticTokenColors(settings.semanticTokenColorCustomizations, settings.experimentalSemanticTokenColorCustomizations); } public setCustomColors(colors: IColorCustomizations) { @@ -374,7 +379,7 @@ export class ColorThemeData implements IWorkbenchColorTheme { public setCustomTokenColors(customTokenColors: ITokenColorCustomizations) { this.customTokenColors = []; - this.customSemanticHighlighting = undefined; + this.customSemanticHighlightingDeprecated = undefined; // first add the non-theme specific settings this.addCustomTokenColors(customTokenColors); @@ -390,19 +395,53 @@ export class ColorThemeData implements IWorkbenchColorTheme { this.customTokenScopeMatchers = undefined; } - public setCustomTokenStyleRules(tokenStylingRules: IExperimentalTokenStyleCustomizations) { - this.customTokenStylingRules = []; - readCustomTokenStyleRules(tokenStylingRules, this.customTokenStylingRules); + public setCustomSemanticTokenColors(semanticTokenColors: ISemanticTokenColorCustomizations | undefined, experimental?: IExperimentalSemanticTokenColorCustomizations) { + this.customSemanticTokenRules = []; + this.customSemanticHighlighting = undefined; - const themeSpecificColors = tokenStylingRules[`[${this.settingsId}]`] as IExperimentalTokenStyleCustomizations; - if (types.isObject(themeSpecificColors)) { - readCustomTokenStyleRules(themeSpecificColors, this.customTokenStylingRules); + if (experimental) { // apply deprecated settings first + this.readSemanticTokenRules(experimental); + const themeSpecificColors = experimental[`[${this.settingsId}]`] as IExperimentalSemanticTokenColorCustomizations; + if (types.isObject(themeSpecificColors)) { + this.readSemanticTokenRules(themeSpecificColors); + } + } + if (semanticTokenColors) { + this.customSemanticHighlighting = semanticTokenColors.enabled; + if (semanticTokenColors.rules) { + this.readSemanticTokenRules(semanticTokenColors.rules); + } + const themeSpecificColors = semanticTokenColors[`[${this.settingsId}]`] as ISemanticTokenColorCustomizations; + if (types.isObject(themeSpecificColors)) { + if (themeSpecificColors.enabled !== undefined) { + this.customSemanticHighlighting = themeSpecificColors.enabled; + } + if (themeSpecificColors.rules) { + this.readSemanticTokenRules(themeSpecificColors.rules); + } + } } this.tokenColorIndex = undefined; this.textMateThemingRules = undefined; } + + private readSemanticTokenRules(tokenStylingRuleSection: ISemanticTokenRules) { + for (let key in tokenStylingRuleSection) { + if (key[0] !== '[') { // still do this test until experimental settings are gone + try { + const rule = readSemanticTokenRule(key, tokenStylingRuleSection[key]); + if (rule) { + this.customSemanticTokenRules.push(rule); + } + } catch (e) { + // invalid selector, ignore + } + } + } + } + private addCustomTokenColors(customTokenColors: ITokenColorCustomizations) { // Put the general customizations such as comments, strings, etc. first so that // they can be overridden by specific customizations like "string.interpolated" @@ -427,7 +466,7 @@ export class ColorThemeData implements IWorkbenchColorTheme { } } if (customTokenColors.semanticHighlighting !== undefined) { - this.customSemanticHighlighting = customTokenColors.semanticHighlighting; + this.customSemanticHighlightingDeprecated = customTokenColors.semanticHighlighting; } } @@ -449,12 +488,12 @@ export class ColorThemeData implements IWorkbenchColorTheme { const result = { colors: {}, textMateRules: [], - stylingRules: [], + semanticTokenRules: [], semanticHighlighting: false }; return _loadColorTheme(extensionResourceLoaderService, this.location, result).then(_ => { this.isLoaded = true; - this.tokenStylingRules = result.stylingRules; + this.semanticTokenRules = result.semanticTokenRules; this.colorMap = result.colors; this.themeTokenColors = result.textMateRules; this.themeSemanticHighlighting = result.semanticHighlighting; @@ -480,7 +519,7 @@ export class ColorThemeData implements IWorkbenchColorTheme { settingsId: this.settingsId, selector: this.id.split(' ').join('.'), // to not break old clients themeTokenColors: this.themeTokenColors, - tokenStylingRules: this.tokenStylingRules.map(TokenStylingRule.toJSONObject), + semanticTokenRules: this.semanticTokenRules.map(SemanticTokenRule.toJSONObject), extensionData: ExtensionData.toJSONObject(this.extensionData), location: this.location?.toJSON(), themeSemanticHighlighting: this.themeSemanticHighlighting, @@ -493,7 +532,7 @@ export class ColorThemeData implements IWorkbenchColorTheme { hasEqualData(other: ColorThemeData) { return objects.equals(this.colorMap, other.colorMap) && objects.equals(this.themeTokenColors, other.themeTokenColors) - && arrays.equals(this.tokenStylingRules, other.tokenStylingRules, TokenStylingRule.equals) + && arrays.equals(this.semanticTokenRules, other.semanticTokenRules, SemanticTokenRule.equals) && this.themeSemanticHighlighting === other.themeSemanticHighlighting; } @@ -547,13 +586,13 @@ export class ColorThemeData implements IWorkbenchColorTheme { case 'id': case 'label': case 'settingsId': case 'watch': case 'themeSemanticHighlighting': (theme as any)[key] = data[key]; break; - case 'tokenStylingRules': + case 'semanticTokenRules': const rulesData = data[key]; if (Array.isArray(rulesData)) { for (let d of rulesData) { - const rule = TokenStylingRule.fromJSONObject(tokenClassificationRegistry, d); + const rule = SemanticTokenRule.fromJSONObject(tokenClassificationRegistry, d); if (rule) { - theme.tokenStylingRules.push(rule); + theme.semanticTokenRules.push(rule); } } } @@ -605,7 +644,7 @@ function toCSSSelector(extensionId: string, path: string) { return str; } -async function _loadColorTheme(extensionResourceLoaderService: IExtensionResourceLoaderService, themeLocation: URI, result: { textMateRules: ITextMateThemingRule[], colors: IColorMap, stylingRules: TokenStylingRule[], semanticHighlighting: boolean }): Promise { +async function _loadColorTheme(extensionResourceLoaderService: IExtensionResourceLoaderService, themeLocation: URI, result: { textMateRules: ITextMateThemingRule[], colors: IColorMap, semanticTokenRules: SemanticTokenRule[], semanticHighlighting: boolean }): Promise { if (resources.extname(themeLocation) === '.json') { const content = await extensionResourceLoaderService.readExtensionResource(themeLocation); let errors: Json.ParseError[] = []; @@ -650,9 +689,9 @@ async function _loadColorTheme(extensionResourceLoaderService: IExtensionResourc if (semanticTokenColors && typeof semanticTokenColors === 'object') { for (let key in semanticTokenColors) { try { - const rule = readCustomTokenStyleRule(key, semanticTokenColors[key]); + const rule = readSemanticTokenRule(key, semanticTokenColors[key]); if (rule) { - result.stylingRules.push(rule); + result.semanticTokenRules.push(rule); } } catch (e) { return Promise.reject(new Error(nls.localize({ key: 'error.invalidformat.semanticTokenColors', comment: ['{0} will be replaced by a path. Values in quotes should not be translated.'] }, "Problem parsing color theme file: {0}. Property 'semanticTokenColors' conatains a invalid selector", themeLocation.toString()))); @@ -770,7 +809,7 @@ function getScopeMatcher(rule: ITextMateThemingRule): Matcher { }; } -function readCustomTokenStyleRule(selectorString: string, settings: ISemanticTokenColorizationSetting | string | undefined): TokenStylingRule | undefined { +function readSemanticTokenRule(selectorString: string, settings: ISemanticTokenColorizationSetting | string | boolean | undefined): SemanticTokenRule | undefined { const selector = tokenClassificationRegistry.parseTokenSelector(selectorString); let style: TokenStyle | undefined; if (typeof settings === 'string') { @@ -784,22 +823,6 @@ function readCustomTokenStyleRule(selectorString: string, settings: ISemanticTok return undefined; } -function readCustomTokenStyleRules(tokenStylingRuleSection: IExperimentalTokenStyleCustomizations, result: TokenStylingRule[] = []) { - for (let key in tokenStylingRuleSection) { - if (key[0] !== '[') { - try { - const rule = readCustomTokenStyleRule(key, tokenStylingRuleSection[key]); - if (rule) { - result.push(rule); - } - } catch (e) { - // invalid selector, ignore - } - } - } - return result; -} - function isSemanticTokenColorizationSetting(style: any): style is ISemanticTokenColorizationSetting { return style && (types.isString(style.foreground) || types.isString(style.fontStyle) || types.isBoolean(style.italic) || types.isBoolean(style.underline) || types.isBoolean(style.bold)); diff --git a/src/vs/workbench/services/themes/common/themeConfiguration.ts b/src/vs/workbench/services/themes/common/themeConfiguration.ts index 23d689a28506d376e0bfb70751771fa3ef931bb8..5e5665b01e780ad81511435b36b021d9a166baa1 100644 --- a/src/vs/workbench/services/themes/common/themeConfiguration.ts +++ b/src/vs/workbench/services/themes/common/themeConfiguration.ts @@ -12,9 +12,8 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { textmateColorsSchemaId, textmateColorGroupSchemaId } from 'vs/workbench/services/themes/common/colorThemeSchema'; import { workbenchColorsSchemaId } from 'vs/platform/theme/common/colorRegistry'; import { tokenStylingSchemaId } from 'vs/platform/theme/common/tokenClassificationRegistry'; -import { ThemeSettings, IWorkbenchColorTheme, IWorkbenchFileIconTheme, IColorCustomizations, ITokenColorCustomizations, IExperimentalTokenStyleCustomizations, IWorkbenchProductIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { ThemeSettings, IWorkbenchColorTheme, IWorkbenchFileIconTheme, IColorCustomizations, ITokenColorCustomizations, IWorkbenchProductIconTheme, ISemanticTokenColorCustomizations, IExperimentalSemanticTokenColorCustomizations } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; - const DEFAULT_THEME_SETTING_VALUE = 'Default Dark+'; const DEFAULT_THEME_DARK_SETTING_VALUE = 'Default Dark+'; const DEFAULT_THEME_LIGHT_SETTING_VALUE = 'Default Light+'; @@ -134,19 +133,43 @@ const tokenColorSchema: IJSONSchema = { }, semanticHighlighting: { description: nls.localize('editorColors.semanticHighlighting', 'Whether semantic highlighting should be enabled for this theme.'), + deprecationMessage: nls.localize('editorColors.semanticHighlighting.deprecationMessage', 'Use `enabled` in `editor.semanticTokenColorCustomizations` setting instead.'), type: 'boolean' } } }; + const tokenColorCustomizationSchema: IConfigurationPropertySchema = { - description: nls.localize('editorColors', "Overrides editor colors and font style from the currently selected color theme."), + description: nls.localize('editorColors', "Overrides editor syntax colors and font style from the currently selected color theme."), default: {}, allOf: [tokenColorSchema] }; + +const semanticTokenColorSchema: IJSONSchema = { + type: 'object', + properties: { + enabled: { + type: 'boolean', + description: nls.localize('editorColors.semanticHighlighting.enabled', 'Whether semantic highlighting is enabled or disabled for this theme') + }, + rules: { + $ref: tokenStylingSchemaId, + description: nls.localize('editorColors.semanticHighlighting.rules', 'Semantic token styling rules for this theme.') + } + }, + additionalProperties: false +}; + +const semanticTokenColorCustomizationSchema: IConfigurationPropertySchema = { + description: nls.localize('semanticTokenColors', "Overrides editor semantic token color and styles from the currently selected color theme."), + default: {}, + allOf: [{ ...semanticTokenColorSchema, patternProperties: { '^\\[': {} } }] +}; + const experimentalTokenStylingCustomizationSchema: IConfigurationPropertySchema = { - description: nls.localize('editorColorsTokenStyles', "Overrides token color and styles from the currently selected color theme."), + deprecationMessage: nls.localize('editorColors.experimentalTokenStyling.deprecationMessage', 'Use `editor.semanticTokenColorCustomizations` instead.'), default: {}, - allOf: [{ $ref: tokenStylingSchemaId }] + allOf: [{ $ref: tokenStylingSchemaId }], }; const tokenColorCustomizationConfiguration: IConfigurationNode = { id: 'editor', @@ -154,6 +177,7 @@ const tokenColorCustomizationConfiguration: IConfigurationNode = { type: 'object', properties: { [ThemeSettings.TOKEN_COLOR_CUSTOMIZATIONS]: tokenColorCustomizationSchema, + [ThemeSettings.SEMANTIC_TOKEN_COLOR_CUSTOMIZATIONS]: semanticTokenColorCustomizationSchema, [ThemeSettings.TOKEN_COLOR_CUSTOMIZATIONS_EXPERIMENTAL]: experimentalTokenStylingCustomizationSchema } }; @@ -167,22 +191,24 @@ export function updateColorThemeConfigurationSchemas(themes: IWorkbenchColorThem const themeSpecificWorkbenchColors: IJSONSchema = { properties: {} }; const themeSpecificTokenColors: IJSONSchema = { properties: {} }; - const themeSpecificTokenStyling: IJSONSchema = { properties: {} }; + const themeSpecificSemanticTokenColors: IJSONSchema = { properties: {} }; + const experimentalThemeSpecificSemanticTokenColors: IJSONSchema = { properties: {} }; const workbenchColors = { $ref: workbenchColorsSchemaId, additionalProperties: false }; const tokenColors = { properties: tokenColorSchema.properties, additionalProperties: false }; - const tokenStyling = { $ref: tokenStylingSchemaId, additionalProperties: false }; for (let t of themes) { // add theme specific color customization ("[Abyss]":{ ... }) const themeId = `[${t.settingsId}]`; themeSpecificWorkbenchColors.properties![themeId] = workbenchColors; themeSpecificTokenColors.properties![themeId] = tokenColors; - themeSpecificTokenStyling.properties![themeId] = tokenStyling; + themeSpecificSemanticTokenColors.properties![themeId] = semanticTokenColorSchema; + experimentalThemeSpecificSemanticTokenColors.properties![themeId] = { $ref: tokenStylingSchemaId, additionalProperties: false }; } colorCustomizationsSchema.allOf![1] = themeSpecificWorkbenchColors; tokenColorCustomizationSchema.allOf![1] = themeSpecificTokenColors; - experimentalTokenStylingCustomizationSchema.allOf![1] = themeSpecificTokenStyling; + semanticTokenColorCustomizationSchema.allOf![1] = themeSpecificSemanticTokenColors; + experimentalTokenStylingCustomizationSchema.allOf![1] = experimentalThemeSpecificSemanticTokenColors; configurationRegistry.notifyConfigurationSchemaUpdated(themeSettingsConfiguration, tokenColorCustomizationConfiguration); } @@ -226,8 +252,12 @@ export class ThemeConfiguration { return this.configurationService.getValue(ThemeSettings.TOKEN_COLOR_CUSTOMIZATIONS) || {}; } - public get tokenStylesCustomizations(): IExperimentalTokenStyleCustomizations { - return this.configurationService.getValue(ThemeSettings.TOKEN_COLOR_CUSTOMIZATIONS_EXPERIMENTAL) || {}; + public get semanticTokenColorCustomizations(): ISemanticTokenColorCustomizations | undefined { + return this.configurationService.getValue(ThemeSettings.SEMANTIC_TOKEN_COLOR_CUSTOMIZATIONS); + } + + public get experimentalSemanticTokenColorCustomizations(): IExperimentalSemanticTokenColorCustomizations | undefined { + return this.configurationService.getValue(ThemeSettings.TOKEN_COLOR_CUSTOMIZATIONS_EXPERIMENTAL); } public async setColorTheme(theme: IWorkbenchColorTheme, settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise { diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index bc37417470ff682a5a9b0da5f9685e6b94b9ce4d..c1409abd2a8ebbb5d16554098b89d3815cbfb8f1 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -25,6 +25,7 @@ export enum ThemeSettings { PRODUCT_ICON_THEME = 'workbench.productIconTheme', COLOR_CUSTOMIZATIONS = 'workbench.colorCustomizations', TOKEN_COLOR_CUSTOMIZATIONS = 'editor.tokenColorCustomizations', + SEMANTIC_TOKEN_COLOR_CUSTOMIZATIONS = 'editor.semanticTokenColorCustomizations', TOKEN_COLOR_CUSTOMIZATIONS_EXPERIMENTAL = 'editor.tokenColorCustomizationsExperimental', PREFERRED_DARK_THEME = 'workbench.preferredDarkColorTheme', @@ -93,11 +94,21 @@ export interface ITokenColorCustomizations { functions?: string | ITokenColorizationSetting; variables?: string | ITokenColorizationSetting; textMateRules?: ITextMateThemingRule[]; - semanticHighlighting?: boolean; + semanticHighlighting?: boolean; // deprecated, use ISemanticTokenColorCustomizations.enabled instead } -export interface IExperimentalTokenStyleCustomizations { - [styleRuleOrThemeSettingsId: string]: string | ISemanticTokenColorizationSetting | IExperimentalTokenStyleCustomizations | undefined; +export interface ISemanticTokenColorCustomizations { + enabled?: boolean; + rules?: ISemanticTokenRules; + [styleRuleOrThemeSettingsId: string]: ISemanticTokenRules | ISemanticTokenColorCustomizations | boolean | undefined; +} + +export interface IExperimentalSemanticTokenColorCustomizations { + [styleRuleOrThemeSettingsId: string]: ISemanticTokenRules | IExperimentalSemanticTokenColorCustomizations | undefined; +} + +export interface ISemanticTokenRules { + [selector: string]: string | ISemanticTokenColorizationSetting | undefined; } export interface ITextMateThemingRule { diff --git a/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts b/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts index cf8fc36547df8651295ceb6b26a90af8c0d80ffc..15422f0975f736efc2934cbf1b7dab67008a0cf1 100644 --- a/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts +++ b/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts @@ -326,13 +326,16 @@ suite('Themes - TokenStyleResolving', () => { test('rule matching', async () => { const themeData = ColorThemeData.createLoadedEmptyTheme('test', 'test'); themeData.setCustomColors({ 'editor.foreground': '#000000' }); - themeData.setCustomTokenStyleRules({ - 'type': '#ff0000', - 'class': { foreground: '#0000ff', italic: true }, - '*.static': { bold: true }, - '*.declaration': { italic: true }, - '*.async.static': { italic: true, underline: true }, - '*.async': { foreground: '#000fff', underline: true } + themeData.setCustomSemanticTokenColors({ + enabled: true, + rules: { + 'type': '#ff0000', + 'class': { foreground: '#0000ff', italic: true }, + '*.static': { bold: true }, + '*.declaration': { italic: true }, + '*.async.static': { italic: true, underline: true }, + '*.async': { foreground: '#000fff', underline: true } + } }); assertTokenStyles(themeData, { @@ -357,18 +360,24 @@ suite('Themes - TokenStyleResolving', () => { try { const themeData = ColorThemeData.createLoadedEmptyTheme('test', 'test'); themeData.setCustomColors({ 'editor.foreground': '#000000' }); - themeData.setCustomTokenStyleRules({ - 'interface': '#ff0000', - 'myTestInterface': { italic: true }, - 'interface.static': { bold: true } + themeData.setCustomSemanticTokenColors({ + enabled: true, + rules: { + 'interface': '#ff0000', + 'myTestInterface': { italic: true }, + 'interface.static': { bold: true } + } }); assertTokenStyles(themeData, { 'myTestSubInterface': ts('#ff0000', { italic: true }) }); assertTokenStyles(themeData, { 'myTestSubInterface.static': ts('#ff0000', { italic: true, bold: true }) }); - themeData.setCustomTokenStyleRules({ - 'interface': '#ff0000', - 'myTestInterface': { foreground: '#ff00ff', italic: true } + themeData.setCustomSemanticTokenColors({ + enabled: true, + rules: { + 'interface': '#ff0000', + 'myTestInterface': { foreground: '#ff00ff', italic: true } + } }); assertTokenStyles(themeData, { 'myTestSubInterface': ts('#ff00ff', { italic: true }) }); } finally { @@ -381,11 +390,14 @@ suite('Themes - TokenStyleResolving', () => { try { const themeData = ColorThemeData.createLoadedEmptyTheme('test', 'test'); themeData.setCustomColors({ 'editor.foreground': '#000000' }); - themeData.setCustomTokenStyleRules({ - 'interface': '#fff000', - 'interface:java': '#ff0000', - 'interface.static': { bold: true }, - 'interface.static:typescript': { italic: true } + themeData.setCustomSemanticTokenColors({ + enabled: true, + rules: { + 'interface': '#fff000', + 'interface:java': '#ff0000', + 'interface.static': { bold: true }, + 'interface.static:typescript': { italic: true } + } }); assertTokenStyles(themeData, { 'interface': ts('#ff0000', undefined) }, 'java');