提交 100318a6 编写于 作者: M Martin Aeschlimann

styling in settings and themes

上级 a5fe35b7
...@@ -7,6 +7,10 @@ import * as platform from 'vs/platform/registry/common/platform'; ...@@ -7,6 +7,10 @@ import * as platform from 'vs/platform/registry/common/platform';
import { Color } from 'vs/base/common/color'; import { Color } from 'vs/base/common/color';
import { ITheme } from 'vs/platform/theme/common/themeService'; import { ITheme } from 'vs/platform/theme/common/themeService';
import * as nls from 'vs/nls'; import * as nls from 'vs/nls';
import { Extensions as JSONExtensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { RunOnceScheduler } from 'vs/base/common/async';
import { Event, Emitter } from 'vs/base/common/event';
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
// ------ API types // ------ API types
...@@ -89,6 +93,8 @@ export const Extensions = { ...@@ -89,6 +93,8 @@ export const Extensions = {
export interface ITokenClassificationRegistry { export interface ITokenClassificationRegistry {
readonly onDidChangeSchema: Event<void>;
/** /**
* Register a token type to the registry. * Register a token type to the registry.
* @param id The TokenType id as used in theme description files * @param id The TokenType id as used in theme description files
...@@ -106,7 +112,7 @@ export interface ITokenClassificationRegistry { ...@@ -106,7 +112,7 @@ export interface ITokenClassificationRegistry {
getTokenClassificationFromString(str: TokenClassificationString): TokenClassification | undefined; getTokenClassificationFromString(str: TokenClassificationString): TokenClassification | undefined;
getTokenClassification(type: string, modifiers: string[]): TokenClassification | undefined; getTokenClassification(type: string, modifiers: string[]): TokenClassification | undefined;
getTokenStylingRule(classification: TokenClassification | string | undefined, value: TokenStyle): TokenStylingRule | undefined; getTokenStylingRule(classification: TokenClassification, value: TokenStyle): TokenStylingRule;
/** /**
* Register a TokenStyle default to the registry. * Register a TokenStyle default to the registry.
...@@ -138,13 +144,19 @@ export interface ITokenClassificationRegistry { ...@@ -138,13 +144,19 @@ export interface ITokenClassificationRegistry {
/** /**
* Resolves a token classification against the given rules and default rules from the registry. * Resolves a token classification against the given rules and default rules from the registry.
*/ */
resolveTokenStyle(classification: TokenClassification, themingRules: TokenStylingRule[], useDefault: boolean, theme: ITheme): TokenStyle | undefined; resolveTokenStyle(classification: TokenClassification, themingRules: TokenStylingRule[] | undefined, customThemingRules: TokenStylingRule[], theme: ITheme): TokenStyle | undefined;
}
/**
* JSON schema for an object to assign styling to token classifications
*/
getTokenStylingSchema(): IJSONSchema;
}
class TokenClassificationRegistry implements ITokenClassificationRegistry { class TokenClassificationRegistry implements ITokenClassificationRegistry {
private readonly _onDidChangeSchema = new Emitter<void>();
readonly onDidChangeSchema: Event<void> = this._onDidChangeSchema.event;
private currentTypeNumber = 0; private currentTypeNumber = 0;
private currentModifierBit = 1; private currentModifierBit = 1;
...@@ -153,6 +165,38 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { ...@@ -153,6 +165,38 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
private tokenStylingDefaultRules: TokenStylingDefaultRule[] = []; private tokenStylingDefaultRules: TokenStylingDefaultRule[] = [];
private tokenStylingSchema: IJSONSchema & { properties: IJSONSchemaMap } = {
type: 'object',
properties: {},
definitions: {
style: {
type: 'object',
description: nls.localize('schema.token.settings', 'Colors and styles for the token.'),
properties: {
foreground: {
type: 'string',
description: nls.localize('schema.token.foreground', 'Foreground color for the token.'),
format: 'color-hex',
default: '#ff0000'
},
background: {
type: 'string',
deprecationMessage: nls.localize('schema.token.background.warning', 'Token background colors are currently not supported.')
},
fontStyle: {
type: 'string',
description: nls.localize('schema.token.fontStyle', 'Font style of the rule: \'italic\', \'bold\' or \'underline\', \'-italic\', \'-bold\' or \'-underline\'or a combination. The empty string unsets inherited settings.'),
pattern: '^(\\s*(-?italic|-?bold|-?underline))*\\s*$',
patternErrorMessage: nls.localize('schema.fontStyle.error', 'Font style must be \'italic\', \'bold\' or \'underline\' to set a style or \'-italic\', \'-bold\' or \'-underline\' to unset or a combination. The empty string unsets all styles.'),
defaultSnippets: [{ label: nls.localize('schema.token.fontStyle.none', 'None (clear inherited style)'), bodyText: '""' }, { body: 'italic' }, { body: 'bold' }, { body: 'underline' }, { body: '-italic' }, { body: '-bold' }, { body: '-underline' }, { body: 'italic bold' }, { body: 'italic underline' }, { body: 'bold underline' }, { body: 'italic bold underline' }]
}
},
additionalProperties: false,
defaultSnippets: [{ body: { foreground: '${1:#FF0000}', fontStyle: '${2:bold}' } }]
}
}
};
constructor() { constructor() {
this.tokenTypeById = {}; this.tokenTypeById = {};
this.tokenModifierById = {}; this.tokenModifierById = {};
...@@ -164,6 +208,8 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { ...@@ -164,6 +208,8 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
const num = this.currentTypeNumber++; const num = this.currentTypeNumber++;
let tokenStyleContribution: TokenTypeOrModifierContribution = { num, id, description, deprecationMessage }; let tokenStyleContribution: TokenTypeOrModifierContribution = { num, id, description, deprecationMessage };
this.tokenTypeById[id] = tokenStyleContribution; this.tokenTypeById[id] = tokenStyleContribution;
this.tokenStylingSchema.properties[id] = getStylingSchemeEntry(description, deprecationMessage);
} }
public registerTokenModifier(id: string, description: string, deprecationMessage?: string): void { public registerTokenModifier(id: string, description: string, deprecationMessage?: string): void {
...@@ -171,6 +217,8 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { ...@@ -171,6 +217,8 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
this.currentModifierBit = this.currentModifierBit * 2; this.currentModifierBit = this.currentModifierBit * 2;
let tokenStyleContribution: TokenTypeOrModifierContribution = { num, id, description, deprecationMessage }; let tokenStyleContribution: TokenTypeOrModifierContribution = { num, id, description, deprecationMessage };
this.tokenModifierById[id] = tokenStyleContribution; this.tokenModifierById[id] = tokenStyleContribution;
this.tokenStylingSchema.properties[`*.${id}`] = getStylingSchemeEntry(description, deprecationMessage);
} }
public getTokenClassification(type: string, modifiers: string[]): TokenClassification | undefined { public getTokenClassification(type: string, modifiers: string[]): TokenClassification | undefined {
...@@ -197,14 +245,8 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { ...@@ -197,14 +245,8 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
return undefined; return undefined;
} }
public getTokenStylingRule(classification: TokenClassification | string | undefined, value: TokenStyle): TokenStylingRule | undefined { public getTokenStylingRule(classification: TokenClassification, value: TokenStyle): TokenStylingRule {
if (typeof classification === 'string') { return { classification, matchScore: getTokenStylingScore(classification), value };
classification = this.getTokenClassificationFromString(classification);
}
if (classification) {
return { classification, matchScore: getTokenStylingScore(classification), value };
}
return undefined;
} }
public registerTokenStyleDefault(classification: TokenClassification, defaults: TokenStyleDefaults): void { public registerTokenStyleDefault(classification: TokenClassification, defaults: TokenStyleDefaults): void {
...@@ -213,10 +255,12 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { ...@@ -213,10 +255,12 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
public deregisterTokenType(id: string): void { public deregisterTokenType(id: string): void {
delete this.tokenTypeById[id]; delete this.tokenTypeById[id];
delete this.tokenStylingSchema.properties[id];
} }
public deregisterTokenModifier(id: string): void { public deregisterTokenModifier(id: string): void {
delete this.tokenModifierById[id]; delete this.tokenModifierById[id];
delete this.tokenStylingSchema.properties[`*.${id}`];
} }
public getTokenTypes(): TokenTypeOrModifierContribution[] { public getTokenTypes(): TokenTypeOrModifierContribution[] {
...@@ -227,7 +271,7 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { ...@@ -227,7 +271,7 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
return Object.keys(this.tokenModifierById).map(id => this.tokenModifierById[id]); return Object.keys(this.tokenModifierById).map(id => this.tokenModifierById[id]);
} }
public resolveTokenStyle(classification: TokenClassification, themingRules: TokenStylingRule[], useDefault: boolean, theme: ITheme): TokenStyle | undefined { public resolveTokenStyle(classification: TokenClassification, themingRules: TokenStylingRule[] | undefined, customThemingRules: TokenStylingRule[], theme: ITheme): TokenStyle | undefined {
let result: any = { let result: any = {
foreground: undefined, foreground: undefined,
bold: undefined, bold: undefined,
...@@ -257,7 +301,7 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { ...@@ -257,7 +301,7 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
} }
} }
} }
if (useDefault) { if (themingRules === undefined) {
for (const rule of this.tokenStylingDefaultRules) { for (const rule of this.tokenStylingDefaultRules) {
const matchScore = match(rule, classification); const matchScore = match(rule, classification);
if (matchScore >= 0) { if (matchScore >= 0) {
...@@ -270,8 +314,15 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { ...@@ -270,8 +314,15 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
} }
} }
} }
} else {
for (const rule of themingRules) {
const matchScore = match(rule, classification);
if (matchScore >= 0) {
_processStyle(matchScore, rule.value);
}
}
} }
for (const rule of themingRules) { for (const rule of customThemingRules) {
const matchScore = match(rule, classification); const matchScore = match(rule, classification);
if (matchScore >= 0) { if (matchScore >= 0) {
_processStyle(matchScore, rule.value); _processStyle(matchScore, rule.value);
...@@ -297,6 +348,10 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { ...@@ -297,6 +348,10 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
return undefined; return undefined;
} }
public getTokenStylingSchema(): IJSONSchema {
return this.tokenStylingSchema;
}
public toString() { public toString() {
let sorter = (a: string, b: string) => { let sorter = (a: string, b: string) => {
...@@ -348,40 +403,40 @@ export function getTokenClassificationRegistry(): ITokenClassificationRegistry { ...@@ -348,40 +403,40 @@ export function getTokenClassificationRegistry(): ITokenClassificationRegistry {
return tokenClassificationRegistry; return tokenClassificationRegistry;
} }
export const comments = registerTokenType('comments', nls.localize('comments', "Token style for comments."), [['comment']]); export const comments = registerTokenType('comments', nls.localize('comments', "Style for comments."), [['comment']]);
export const strings = registerTokenType('strings', nls.localize('strings', "Token style for strings."), [['string']]); export const strings = registerTokenType('strings', nls.localize('strings', "Style for strings."), [['string']]);
export const keywords = registerTokenType('keywords', nls.localize('keywords', "Token style for keywords."), [['keyword.control']]); export const keywords = registerTokenType('keywords', nls.localize('keywords', "Style for keywords."), [['keyword.control']]);
export const numbers = registerTokenType('numbers', nls.localize('numbers', "Token style for numbers."), [['constant.numeric']]); export const numbers = registerTokenType('numbers', nls.localize('numbers', "Style for numbers."), [['constant.numeric']]);
export const regexp = registerTokenType('regexp', nls.localize('regexp', "Token style for regular expressions."), [['constant.regexp']]); export const regexp = registerTokenType('regexp', nls.localize('regexp', "Style for expressions."), [['constant.regexp']]);
export const operators = registerTokenType('operators', nls.localize('operator', "Token style for operators."), [['keyword.operator']]); export const operators = registerTokenType('operators', nls.localize('operator', "Style for operators."), [['keyword.operator']]);
export const namespaces = registerTokenType('namespaces', nls.localize('namespace', "Token style for namespaces."), [['entity.name.namespace']]); export const namespaces = registerTokenType('namespaces', nls.localize('namespace', "Style for namespaces."), [['entity.name.namespace']]);
export const types = registerTokenType('types', nls.localize('types', "Token style for types."), [['entity.name.type'], ['entity.name.class'], ['support.type'], ['support.class']]); export const types = registerTokenType('types', nls.localize('types', "Style for types."), [['entity.name.type'], ['entity.name.class'], ['support.type'], ['support.class']]);
export const structs = registerTokenType('structs', nls.localize('struct', "Token style for struct."), [['storage.type.struct']], types); export const structs = registerTokenType('structs', nls.localize('struct', "Style for structs."), [['storage.type.struct']], types);
export const classes = registerTokenType('classes', nls.localize('class', "Token style for classes."), [['ntity.name.class']], types); export const classes = registerTokenType('classes', nls.localize('class', "Style for classes."), [['entity.name.class']], types);
export const interfaces = registerTokenType('interfaces', nls.localize('interface', "Token style for interfaces."), undefined, types); export const interfaces = registerTokenType('interfaces', nls.localize('interface', "Style for interfaces."), undefined, types);
export const enums = registerTokenType('enums', nls.localize('enum', "Token style for enums."), undefined, types); export const enums = registerTokenType('enums', nls.localize('enum', "Style for enums."), undefined, types);
export const parameterTypes = registerTokenType('parameterTypes', nls.localize('parameterType', "Token style for parameterTypes."), undefined, types); export const parameterTypes = registerTokenType('parameterTypes', nls.localize('parameterType', "Style for parameter types."), undefined, types);
export const functions = registerTokenType('functions', nls.localize('functions', "Token style for functions."), [['entity.name.function'], ['support.function']]); export const functions = registerTokenType('functions', nls.localize('functions', "Style for functions"), [['entity.name.function'], ['support.function']]);
export const macros = registerTokenType('macros', nls.localize('macro', "Token style for macros."), undefined, functions); export const macros = registerTokenType('macros', nls.localize('macro', "Style for macros."), undefined, functions);
export const variables = registerTokenType('variables', nls.localize('variables', "Token style for variables."), [['variable'], ['entity.name.variable']]); export const variables = registerTokenType('variables', nls.localize('variables', "Style for variables."), [['variable'], ['entity.name.variable']]);
export const constants = registerTokenType('constants', nls.localize('constants', "Token style for constants."), undefined, variables); export const constants = registerTokenType('constants', nls.localize('constants', "Style for constants."), undefined, variables);
export const parameters = registerTokenType('parameters', nls.localize('parameters', "Token style for parameters."), undefined, variables); export const parameters = registerTokenType('parameters', nls.localize('parameters', "Style for parameters."), undefined, variables);
export const property = registerTokenType('properties', nls.localize('properties', "Token style for properties."), undefined, variables); export const property = registerTokenType('properties', nls.localize('properties', "Style for properties."), undefined, variables);
export const labels = registerTokenType('labels', nls.localize('labels', "Token style for labels."), undefined); export const labels = registerTokenType('labels', nls.localize('labels', "Style for labels. "), undefined);
export const m_declaration = registerTokenModifier('declaration', nls.localize('declaration', "Token modifier for declarations."), undefined); export const m_declaration = registerTokenModifier('declaration', nls.localize('declaration', "Style for all symbol declarations."), undefined);
export const m_documentation = registerTokenModifier('documentation', nls.localize('documentation', "Token modifier for documentation."), undefined); export const m_documentation = registerTokenModifier('documentation', nls.localize('documentation', "Style to use for references in documentation."), undefined);
export const m_member = registerTokenModifier('member', nls.localize('member', "Token modifier for member."), undefined); export const m_member = registerTokenModifier('member', nls.localize('member', "Style to use for member functions, variables (fields) and types."), undefined);
export const m_static = registerTokenModifier('static', nls.localize('static', "Token modifier for statics."), undefined); export const m_static = registerTokenModifier('static', nls.localize('static', "Style to use for symbols that are static."), undefined);
export const m_abstract = registerTokenModifier('abstract', nls.localize('abstract', "Token modifier for abstracts."), undefined); export const m_abstract = registerTokenModifier('abstract', nls.localize('abstract', "Style to use for symbols that are abstract."), undefined);
export const m_deprecated = registerTokenModifier('deprecated', nls.localize('deprecated', "Token modifier for deprecated."), undefined); export const m_deprecated = registerTokenModifier('deprecated', nls.localize('deprecated', "Style to use for symbols that are deprecated."), undefined);
export const m_modification = registerTokenModifier('modification', nls.localize('modification', "Token modifier for modification."), undefined); export const m_modification = registerTokenModifier('modification', nls.localize('modification', "Style to use for write accesses."), undefined);
export const m_async = registerTokenModifier('async', nls.localize('async', "Token modifier for async."), undefined); export const m_async = registerTokenModifier('async', nls.localize('async', "Style to use for symbols that are async."), undefined);
function bitCount(u: number) { function bitCount(u: number) {
// https://blogs.msdn.microsoft.com/jeuge/2005/06/08/bit-fiddling-3/ // https://blogs.msdn.microsoft.com/jeuge/2005/06/08/bit-fiddling-3/
...@@ -392,3 +447,32 @@ function bitCount(u: number) { ...@@ -392,3 +447,32 @@ function bitCount(u: number) {
function getTokenStylingScore(classification: TokenClassification) { function getTokenStylingScore(classification: TokenClassification) {
return bitCount(classification.modifiers) + ((classification.type !== TOKEN_TYPE_WILDCARD_NUM) ? 1 : 0); return bitCount(classification.modifiers) + ((classification.type !== TOKEN_TYPE_WILDCARD_NUM) ? 1 : 0);
} }
function getStylingSchemeEntry(description: string, deprecationMessage?: string): IJSONSchema {
return {
description,
deprecationMessage,
defaultSnippets: [{ body: '${1:#ff0000}' }],
anyOf: [
{
type: 'string',
format: 'color-hex'
},
{
$ref: '#definitions/style'
}
]
};
}
export const tokenStylingSchemaId = 'vscode://schemas/token-styling';
let schemaRegistry = platform.Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
schemaRegistry.registerSchema(tokenStylingSchemaId, tokenClassificationRegistry.getTokenStylingSchema());
const delayer = new RunOnceScheduler(() => schemaRegistry.notifySchemaChanged(tokenStylingSchemaId), 200);
tokenClassificationRegistry.onDidChangeSchema(() => {
if (!delayer.isScheduled()) {
delayer.schedule();
}
});
...@@ -22,7 +22,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag ...@@ -22,7 +22,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag
import { ExtensionMessageCollector } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { ExtensionMessageCollector } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { ITMSyntaxExtensionPoint, grammarsExtPoint } from 'vs/workbench/services/textMate/common/TMGrammars'; import { ITMSyntaxExtensionPoint, grammarsExtPoint } from 'vs/workbench/services/textMate/common/TMGrammars';
import { ITextMateService } from 'vs/workbench/services/textMate/common/textMateService'; import { ITextMateService } from 'vs/workbench/services/textMate/common/textMateService';
import { ITokenColorizationRule, IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ITextMateThemingRule, IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IGrammar, StackElement, IOnigLib, IRawTheme } from 'vscode-textmate'; import { IGrammar, StackElement, IOnigLib, IRawTheme } from 'vscode-textmate';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
...@@ -257,7 +257,7 @@ export abstract class AbstractTextMateService extends Disposable implements ITex ...@@ -257,7 +257,7 @@ export abstract class AbstractTextMateService extends Disposable implements ITex
TokenizationRegistry.setColorMap(colorMap); TokenizationRegistry.setColorMap(colorMap);
} }
private static equalsTokenRules(a: ITokenColorizationRule[] | null, b: ITokenColorizationRule[] | null): boolean { private static equalsTokenRules(a: ITextMateThemingRule[] | null, b: ITextMateThemingRule[] | null): boolean {
if (!b || !a || b.length !== a.length) { if (!b || !a || b.length !== a.length) {
return false; return false;
} }
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
import * as nls from 'vs/nls'; import * as nls from 'vs/nls';
import * as types from 'vs/base/common/types'; import * as types from 'vs/base/common/types';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IWorkbenchThemeService, IColorTheme, ITokenColorCustomizations, IFileIconTheme, ExtensionData, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME, COLOR_THEME_SETTING, ICON_THEME_SETTING, CUSTOM_WORKBENCH_COLORS_SETTING, CUSTOM_EDITOR_COLORS_SETTING, DETECT_HC_SETTING, HC_THEME_ID, IColorCustomizations } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IWorkbenchThemeService, IColorTheme, ITokenColorCustomizations, IFileIconTheme, ExtensionData, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME, COLOR_THEME_SETTING, ICON_THEME_SETTING, CUSTOM_WORKBENCH_COLORS_SETTING, CUSTOM_EDITOR_COLORS_SETTING, DETECT_HC_SETTING, HC_THEME_ID, IColorCustomizations, CUSTOM_EDITOR_TOKENSTYLES_SETTING, IExperimentalTokenStyleCustomizations } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { Registry } from 'vs/platform/registry/common/platform'; import { Registry } from 'vs/platform/registry/common/platform';
...@@ -29,6 +29,7 @@ import * as resources from 'vs/base/common/resources'; ...@@ -29,6 +29,7 @@ import * as resources from 'vs/base/common/resources';
import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { textmateColorsSchemaId, registerColorThemeSchemas, textmateColorSettingsSchemaId } from 'vs/workbench/services/themes/common/colorThemeSchema'; import { textmateColorsSchemaId, registerColorThemeSchemas, textmateColorSettingsSchemaId } from 'vs/workbench/services/themes/common/colorThemeSchema';
import { workbenchColorsSchemaId } from 'vs/platform/theme/common/colorRegistry'; import { workbenchColorsSchemaId } from 'vs/platform/theme/common/colorRegistry';
import { tokenStylingSchemaId } from 'vs/platform/theme/common/tokenClassificationRegistry';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
...@@ -92,6 +93,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { ...@@ -92,6 +93,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
return this.configurationService.getValue<ITokenColorCustomizations>(CUSTOM_EDITOR_COLORS_SETTING) || {}; return this.configurationService.getValue<ITokenColorCustomizations>(CUSTOM_EDITOR_COLORS_SETTING) || {};
} }
private get tokenStylesCustomizations(): IExperimentalTokenStyleCustomizations {
return this.configurationService.getValue<IExperimentalTokenStyleCustomizations>(CUSTOM_EDITOR_TOKENSTYLES_SETTING) || {};
}
constructor( constructor(
@IExtensionService extensionService: IExtensionService, @IExtensionService extensionService: IExtensionService,
@IStorageService private readonly storageService: IStorageService, @IStorageService private readonly storageService: IStorageService,
...@@ -126,6 +131,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { ...@@ -126,6 +131,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
} }
themeData.setCustomColors(this.colorCustomizations); themeData.setCustomColors(this.colorCustomizations);
themeData.setCustomTokenColors(this.tokenColorCustomizations); themeData.setCustomTokenColors(this.tokenColorCustomizations);
themeData.setCustomTokenStyleRules(this.tokenStylesCustomizations);
this.updateDynamicCSSRules(themeData); this.updateDynamicCSSRules(themeData);
this.applyTheme(themeData, undefined, true); this.applyTheme(themeData, undefined, true);
...@@ -154,18 +160,22 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { ...@@ -154,18 +160,22 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
const themeSpecificWorkbenchColors: IJSONSchema = { properties: {} }; const themeSpecificWorkbenchColors: IJSONSchema = { properties: {} };
const themeSpecificTokenColors: IJSONSchema = { properties: {} }; const themeSpecificTokenColors: IJSONSchema = { properties: {} };
const themeSpecificTokenStyling: IJSONSchema = { properties: {} };
const workbenchColors = { $ref: workbenchColorsSchemaId, additionalProperties: false }; const workbenchColors = { $ref: workbenchColorsSchemaId, additionalProperties: false };
const tokenColors = { properties: tokenColorSchema.properties, additionalProperties: false }; const tokenColors = { properties: tokenColorSchema.properties, additionalProperties: false };
const tokenStyling = { $ref: tokenStylingSchemaId, additionalProperties: false };
for (let t of event.themes) { for (let t of event.themes) {
// add theme specific color customization ("[Abyss]":{ ... }) // add theme specific color customization ("[Abyss]":{ ... })
const themeId = `[${t.settingsId}]`; const themeId = `[${t.settingsId}]`;
themeSpecificWorkbenchColors.properties![themeId] = workbenchColors; themeSpecificWorkbenchColors.properties![themeId] = workbenchColors;
themeSpecificTokenColors.properties![themeId] = tokenColors; themeSpecificTokenColors.properties![themeId] = tokenColors;
themeSpecificTokenStyling.properties![themeId] = tokenStyling;
} }
colorCustomizationsSchema.allOf![1] = themeSpecificWorkbenchColors; colorCustomizationsSchema.allOf![1] = themeSpecificWorkbenchColors;
tokenColorCustomizationSchema.allOf![1] = themeSpecificTokenColors; tokenColorCustomizationSchema.allOf![1] = themeSpecificTokenColors;
experimentalTokenStylingCustomizationSchema.allOf![1] = themeSpecificTokenStyling;
configurationRegistry.notifyConfigurationSchemaUpdated(themeSettingsConfiguration, tokenColorCustomizationConfiguration); configurationRegistry.notifyConfigurationSchemaUpdated(themeSettingsConfiguration, tokenColorCustomizationConfiguration);
...@@ -308,6 +318,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { ...@@ -308,6 +318,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
this.currentColorTheme.setCustomTokenColors(this.tokenColorCustomizations); this.currentColorTheme.setCustomTokenColors(this.tokenColorCustomizations);
hasColorChanges = true; hasColorChanges = true;
} }
if (e.affectsConfiguration(CUSTOM_EDITOR_TOKENSTYLES_SETTING)) {
this.currentColorTheme.setCustomTokenStyleRules(this.tokenStylesCustomizations);
hasColorChanges = true;
}
if (hasColorChanges) { if (hasColorChanges) {
this.updateDynamicCSSRules(this.currentColorTheme); this.updateDynamicCSSRules(this.currentColorTheme);
this.onColorThemeChange.fire(this.currentColorTheme); this.onColorThemeChange.fire(this.currentColorTheme);
...@@ -698,12 +712,18 @@ const tokenColorCustomizationSchema: IConfigurationPropertySchema = { ...@@ -698,12 +712,18 @@ const tokenColorCustomizationSchema: IConfigurationPropertySchema = {
default: {}, default: {},
allOf: [tokenColorSchema] allOf: [tokenColorSchema]
}; };
const experimentalTokenStylingCustomizationSchema: IConfigurationPropertySchema = {
description: nls.localize('editorColorsTokenStyles', "Overrides token color and styles from the currently selected color theme."),
default: {},
allOf: [{ $ref: tokenStylingSchemaId }]
};
const tokenColorCustomizationConfiguration: IConfigurationNode = { const tokenColorCustomizationConfiguration: IConfigurationNode = {
id: 'editor', id: 'editor',
order: 7.2, order: 7.2,
type: 'object', type: 'object',
properties: { properties: {
[CUSTOM_EDITOR_COLORS_SETTING]: tokenColorCustomizationSchema [CUSTOM_EDITOR_COLORS_SETTING]: tokenColorCustomizationSchema,
[CUSTOM_EDITOR_TOKENSTYLES_SETTING]: experimentalTokenStylingCustomizationSchema
} }
}; };
configurationRegistry.registerConfiguration(tokenColorCustomizationConfiguration); configurationRegistry.registerConfiguration(tokenColorCustomizationConfiguration);
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
import { basename } from 'vs/base/common/path'; import { basename } from 'vs/base/common/path';
import * as Json from 'vs/base/common/json'; import * as Json from 'vs/base/common/json';
import { Color } from 'vs/base/common/color'; import { Color } from 'vs/base/common/color';
import { ExtensionData, ITokenColorCustomizations, ITokenColorizationRule, IColorTheme, IColorMap, IThemeExtensionPoint, VS_LIGHT_THEME, VS_HC_THEME, IColorCustomizations } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ExtensionData, ITokenColorCustomizations, ITextMateThemingRule, IColorTheme, IColorMap, IThemeExtensionPoint, VS_LIGHT_THEME, VS_HC_THEME, IColorCustomizations, IExperimentalTokenStyleCustomizations, ITokenColorizationSetting } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { convertSettings } from 'vs/workbench/services/themes/common/themeCompatibility'; import { convertSettings } from 'vs/workbench/services/themes/common/themeCompatibility';
import * as nls from 'vs/nls'; import * as nls from 'vs/nls';
import * as types from 'vs/base/common/types'; import * as types from 'vs/base/common/types';
...@@ -49,12 +49,13 @@ export class ColorThemeData implements IColorTheme { ...@@ -49,12 +49,13 @@ export class ColorThemeData implements IColorTheme {
watch?: boolean; watch?: boolean;
extensionData?: ExtensionData; extensionData?: ExtensionData;
private themeTokenColors: ITokenColorizationRule[] = []; private themeTokenColors: ITextMateThemingRule[] = [];
private customTokenColors: ITokenColorizationRule[] = []; private customTokenColors: ITextMateThemingRule[] = [];
private colorMap: IColorMap = {}; private colorMap: IColorMap = {};
private customColorMap: IColorMap = {}; private customColorMap: IColorMap = {};
private tokenStylingRules: TokenStylingRule[] = []; private tokenStylingRules: TokenStylingRule[] | undefined = undefined;
private customTokenStylingRules: TokenStylingRule[] = [];
private themeTokenScopeMatchers: Matcher<ProbeScope>[] | undefined; private themeTokenScopeMatchers: Matcher<ProbeScope>[] | undefined;
private customTokenScopeMatchers: Matcher<ProbeScope>[] | undefined; private customTokenScopeMatchers: Matcher<ProbeScope>[] | undefined;
...@@ -66,8 +67,8 @@ export class ColorThemeData implements IColorTheme { ...@@ -66,8 +67,8 @@ export class ColorThemeData implements IColorTheme {
this.isLoaded = false; this.isLoaded = false;
} }
get tokenColors(): ITokenColorizationRule[] { get tokenColors(): ITextMateThemingRule[] {
const result: ITokenColorizationRule[] = []; const result: ITextMateThemingRule[] = [];
// the default rule (scope empty) is always the first rule. Ignore all other default rules. // the default rule (scope empty) is always the first rule. Ignore all other default rules.
const foreground = this.getColor(editorForeground) || this.getDefault(editorForeground)!; const foreground = this.getColor(editorForeground) || this.getDefault(editorForeground)!;
...@@ -81,7 +82,7 @@ export class ColorThemeData implements IColorTheme { ...@@ -81,7 +82,7 @@ export class ColorThemeData implements IColorTheme {
let hasDefaultTokens = false; let hasDefaultTokens = false;
function addRule(rule: ITokenColorizationRule) { function addRule(rule: ITextMateThemingRule) {
if (rule.scope && rule.settings) { if (rule.scope && rule.settings) {
if (rule.scope === 'token.info-token') { if (rule.scope === 'token.info-token') {
hasDefaultTokens = true; hasDefaultTokens = true;
...@@ -115,7 +116,7 @@ export class ColorThemeData implements IColorTheme { ...@@ -115,7 +116,7 @@ export class ColorThemeData implements IColorTheme {
public getTokenStyle(tokenClassification: TokenClassification, useDefault?: boolean): TokenStyle | undefined { public getTokenStyle(tokenClassification: TokenClassification, useDefault?: boolean): TokenStyle | undefined {
// todo: cache results // todo: cache results
return tokenClassificationRegistry.resolveTokenStyle(tokenClassification, this.tokenStylingRules, useDefault !== false, this); return tokenClassificationRegistry.resolveTokenStyle(tokenClassification, this.tokenStylingRules, this.customTokenStylingRules, this);
} }
public getDefault(colorId: ColorIdentifier): Color | undefined { public getDefault(colorId: ColorIdentifier): Color | undefined {
...@@ -123,7 +124,7 @@ export class ColorThemeData implements IColorTheme { ...@@ -123,7 +124,7 @@ export class ColorThemeData implements IColorTheme {
} }
public getDefaultTokenStyle(tokenClassification: TokenClassification): TokenStyle | undefined { public getDefaultTokenStyle(tokenClassification: TokenClassification): TokenStyle | undefined {
return tokenClassificationRegistry.resolveTokenStyle(tokenClassification, [], true, this); return tokenClassificationRegistry.resolveTokenStyle(tokenClassification, undefined, [], this);
} }
public resolveScopes(scopes: ProbeScope[]): TokenStyle | undefined { public resolveScopes(scopes: ProbeScope[]): TokenStyle | undefined {
...@@ -136,12 +137,12 @@ export class ColorThemeData implements IColorTheme { ...@@ -136,12 +137,12 @@ export class ColorThemeData implements IColorTheme {
} }
for (let scope of scopes) { for (let scope of scopes) {
let foreground: string | null = null; let foreground: string | undefined = undefined;
let fontStyle: string | null = null; let fontStyle: string | undefined = undefined;
let foregroundScore = -1; let foregroundScore = -1;
let fontStyleScore = -1; let fontStyleScore = -1;
function findTokenStyleForScopeInScopes(scopeMatchers: Matcher<ProbeScope>[], tokenColors: ITokenColorizationRule[]) { function findTokenStyleForScopeInScopes(scopeMatchers: Matcher<ProbeScope>[], tokenColors: ITextMateThemingRule[]) {
for (let i = 0; i < scopeMatchers.length; i++) { for (let i = 0; i < scopeMatchers.length; i++) {
const score = scopeMatchers[i](scope); const score = scopeMatchers[i](scope);
if (score >= 0) { if (score >= 0) {
...@@ -157,7 +158,7 @@ export class ColorThemeData implements IColorTheme { ...@@ -157,7 +158,7 @@ export class ColorThemeData implements IColorTheme {
} }
findTokenStyleForScopeInScopes(this.themeTokenScopeMatchers, this.themeTokenColors); findTokenStyleForScopeInScopes(this.themeTokenScopeMatchers, this.themeTokenColors);
findTokenStyleForScopeInScopes(this.customTokenScopeMatchers, this.customTokenColors); findTokenStyleForScopeInScopes(this.customTokenScopeMatchers, this.customTokenColors);
if (foreground !== null || fontStyle !== null) { if (foreground !== undefined || fontStyle !== undefined) {
return getTokenStyle(foreground, fontStyle); return getTokenStyle(foreground, fontStyle);
} }
} }
...@@ -200,8 +201,14 @@ export class ColorThemeData implements IColorTheme { ...@@ -200,8 +201,14 @@ export class ColorThemeData implements IColorTheme {
} }
} }
public setTokenStyleRules(tokenStylingRules: TokenStylingRule[]) { public setCustomTokenStyleRules(tokenStylingRules: IExperimentalTokenStyleCustomizations) {
this.tokenStylingRules = tokenStylingRules; this.tokenStylingRules = [];
readCustomTokenStyleRules(tokenStylingRules, this.tokenStylingRules);
const themeSpecificColors = tokenStylingRules[`[${this.settingsId}]`] as IExperimentalTokenStyleCustomizations;
if (types.isObject(themeSpecificColors)) {
readCustomTokenStyleRules(themeSpecificColors, this.tokenStylingRules);
}
} }
private addCustomTokenColors(customTokenColors: ITokenColorCustomizations) { private addCustomTokenColors(customTokenColors: ITokenColorCustomizations) {
...@@ -243,9 +250,17 @@ export class ColorThemeData implements IColorTheme { ...@@ -243,9 +250,17 @@ export class ColorThemeData implements IColorTheme {
} }
this.themeTokenColors = []; this.themeTokenColors = [];
this.themeTokenScopeMatchers = undefined; this.themeTokenScopeMatchers = undefined;
this.colorMap = {};
return _loadColorTheme(extensionResourceLoaderService, this.location, this.themeTokenColors, this.colorMap).then(_ => { const result = {
colors: {},
textMateRules: [],
stylingRules: undefined
};
return _loadColorTheme(extensionResourceLoaderService, this.location, result).then(_ => {
this.isLoaded = true; this.isLoaded = true;
this.tokenStylingRules = result.stylingRules;
this.colorMap = result.colors;
this.themeTokenColors = result.textMateRules;
}); });
} }
...@@ -358,7 +373,7 @@ function toCSSSelector(extensionId: string, path: string) { ...@@ -358,7 +373,7 @@ function toCSSSelector(extensionId: string, path: string) {
return str; return str;
} }
function _loadColorTheme(extensionResourceLoaderService: IExtensionResourceLoaderService, themeLocation: URI, resultRules: ITokenColorizationRule[], resultColors: IColorMap): Promise<any> { function _loadColorTheme(extensionResourceLoaderService: IExtensionResourceLoaderService, themeLocation: URI, result: { textMateRules: ITextMateThemingRule[], colors: IColorMap, stylingRules: TokenStylingRule[] | undefined }): Promise<any> {
if (resources.extname(themeLocation) === '.json') { if (resources.extname(themeLocation) === '.json') {
return extensionResourceLoaderService.readExtensionResource(themeLocation).then(content => { return extensionResourceLoaderService.readExtensionResource(themeLocation).then(content => {
let errors: Json.ParseError[] = []; let errors: Json.ParseError[] = [];
...@@ -370,11 +385,11 @@ function _loadColorTheme(extensionResourceLoaderService: IExtensionResourceLoade ...@@ -370,11 +385,11 @@ function _loadColorTheme(extensionResourceLoaderService: IExtensionResourceLoade
} }
let includeCompletes: Promise<any> = Promise.resolve(null); let includeCompletes: Promise<any> = Promise.resolve(null);
if (contentValue.include) { if (contentValue.include) {
includeCompletes = _loadColorTheme(extensionResourceLoaderService, resources.joinPath(resources.dirname(themeLocation), contentValue.include), resultRules, resultColors); includeCompletes = _loadColorTheme(extensionResourceLoaderService, resources.joinPath(resources.dirname(themeLocation), contentValue.include), result);
} }
return includeCompletes.then(_ => { return includeCompletes.then(_ => {
if (Array.isArray(contentValue.settings)) { if (Array.isArray(contentValue.settings)) {
convertSettings(contentValue.settings, resultRules, resultColors); convertSettings(contentValue.settings, result);
return null; return null;
} }
let colors = contentValue.colors; let colors = contentValue.colors;
...@@ -386,38 +401,42 @@ function _loadColorTheme(extensionResourceLoaderService: IExtensionResourceLoade ...@@ -386,38 +401,42 @@ function _loadColorTheme(extensionResourceLoaderService: IExtensionResourceLoade
for (let colorId in colors) { for (let colorId in colors) {
let colorHex = colors[colorId]; let colorHex = colors[colorId];
if (typeof colorHex === 'string') { // ignore colors tht are null if (typeof colorHex === 'string') { // ignore colors tht are null
resultColors[colorId] = Color.fromHex(colors[colorId]); result.colors[colorId] = Color.fromHex(colors[colorId]);
} }
} }
} }
let tokenColors = contentValue.tokenColors; let tokenColors = contentValue.tokenColors;
if (tokenColors) { if (tokenColors) {
if (Array.isArray(tokenColors)) { if (Array.isArray(tokenColors)) {
resultRules.push(...tokenColors); result.textMateRules.push(...tokenColors);
return null; return null;
} else if (typeof tokenColors === 'string') { } else if (typeof tokenColors === 'string') {
return _loadSyntaxTokens(extensionResourceLoaderService, resources.joinPath(resources.dirname(themeLocation), tokenColors), resultRules, {}); return _loadSyntaxTokens(extensionResourceLoaderService, resources.joinPath(resources.dirname(themeLocation), tokenColors), result);
} else { } else {
return Promise.reject(new Error(nls.localize({ key: 'error.invalidformat.tokenColors', comment: ['{0} will be replaced by a path. Values in quotes should not be translated.'] }, "Problem parsing color theme file: {0}. Property 'tokenColors' should be either an array specifying colors or a path to a TextMate theme file", themeLocation.toString()))); return Promise.reject(new Error(nls.localize({ key: 'error.invalidformat.tokenColors', comment: ['{0} will be replaced by a path. Values in quotes should not be translated.'] }, "Problem parsing color theme file: {0}. Property 'tokenColors' should be either an array specifying colors or a path to a TextMate theme file", themeLocation.toString())));
} }
} }
let tokenStylingRules = contentValue.tokenStylingRules;
if (tokenStylingRules && typeof tokenStylingRules === 'object') {
result.stylingRules = readCustomTokenStyleRules(tokenStylingRules, result.stylingRules);
}
return null; return null;
}); });
}); });
} else { } else {
return _loadSyntaxTokens(extensionResourceLoaderService, themeLocation, resultRules, resultColors); return _loadSyntaxTokens(extensionResourceLoaderService, themeLocation, result);
} }
} }
function _loadSyntaxTokens(extensionResourceLoaderService: IExtensionResourceLoaderService, themeLocation: URI, resultRules: ITokenColorizationRule[], resultColors: IColorMap): Promise<any> { function _loadSyntaxTokens(extensionResourceLoaderService: IExtensionResourceLoaderService, themeLocation: URI, result: { textMateRules: ITextMateThemingRule[], colors: IColorMap }): Promise<any> {
return extensionResourceLoaderService.readExtensionResource(themeLocation).then(content => { return extensionResourceLoaderService.readExtensionResource(themeLocation).then(content => {
try { try {
let contentValue = parsePList(content); let contentValue = parsePList(content);
let settings: ITokenColorizationRule[] = contentValue.settings; let settings: ITextMateThemingRule[] = contentValue.settings;
if (!Array.isArray(settings)) { if (!Array.isArray(settings)) {
return Promise.reject(new Error(nls.localize('error.plist.invalidformat', "Problem parsing tmTheme file: {0}. 'settings' is not array."))); return Promise.reject(new Error(nls.localize('error.plist.invalidformat', "Problem parsing tmTheme file: {0}. 'settings' is not array.")));
} }
convertSettings(settings, resultRules, resultColors); convertSettings(settings, result);
return Promise.resolve(null); return Promise.resolve(null);
} catch (e) { } catch (e) {
return Promise.reject(new Error(nls.localize('error.cannotparse', "Problems parsing tmTheme file: {0}", e.message))); return Promise.reject(new Error(nls.localize('error.cannotparse', "Problems parsing tmTheme file: {0}", e.message)));
...@@ -427,7 +446,7 @@ function _loadSyntaxTokens(extensionResourceLoaderService: IExtensionResourceLoa ...@@ -427,7 +446,7 @@ function _loadSyntaxTokens(extensionResourceLoaderService: IExtensionResourceLoa
}); });
} }
let defaultThemeColors: { [baseTheme: string]: ITokenColorizationRule[] } = { let defaultThemeColors: { [baseTheme: string]: ITextMateThemingRule[] } = {
'light': [ 'light': [
{ scope: 'token.info-token', settings: { foreground: '#316bcd' } }, { scope: 'token.info-token', settings: { foreground: '#316bcd' } },
{ scope: 'token.warn-token', settings: { foreground: '#cd9731' } }, { scope: 'token.warn-token', settings: { foreground: '#cd9731' } },
...@@ -489,7 +508,7 @@ function scopesAreMatching(thisScopeName: string, scopeName: string): boolean { ...@@ -489,7 +508,7 @@ function scopesAreMatching(thisScopeName: string, scopeName: string): boolean {
return thisScopeName.length > len && thisScopeName.substr(0, len) === scopeName && thisScopeName[len] === '.'; return thisScopeName.length > len && thisScopeName.substr(0, len) === scopeName && thisScopeName[len] === '.';
} }
function getScopeMatcher(rule: ITokenColorizationRule): Matcher<ProbeScope> { function getScopeMatcher(rule: ITextMateThemingRule): Matcher<ProbeScope> {
const ruleScope = rule.scope; const ruleScope = rule.scope;
if (!ruleScope || !rule.settings) { if (!ruleScope || !rule.settings) {
return noMatch; return noMatch;
...@@ -515,17 +534,56 @@ function getScopeMatcher(rule: ITokenColorizationRule): Matcher<ProbeScope> { ...@@ -515,17 +534,56 @@ function getScopeMatcher(rule: ITokenColorizationRule): Matcher<ProbeScope> {
}; };
} }
function getTokenStyle(foreground: string | null, fontStyle: string | null): TokenStyle | undefined { function getTokenStyle(foreground: string | undefined, fontStyle: string | undefined): TokenStyle {
let foregroundColor = undefined; let foregroundColor = undefined;
if (foreground !== null) { if (foreground !== undefined) {
foregroundColor = Color.fromHex(foreground); foregroundColor = Color.fromHex(foreground);
} }
let bold, underline, italic; let bold, underline, italic;
if (fontStyle !== null) { if (fontStyle !== undefined) {
bold = fontStyle.indexOf('bold') !== -1; fontStyle = fontStyle.trim();
underline = fontStyle.indexOf('underline') !== -1; if (fontStyle.length === 0) {
italic = fontStyle.indexOf('italic') !== -1; bold = italic = underline = false;
} else {
const expression = /-?italic|-?bold|-?underline/g;
let match;
while ((match = expression.exec(fontStyle))) {
switch (match[0]) {
case 'bold': bold = true; break;
case '-bold': bold = false; break;
case 'italic': italic = true; break;
case '-italic': italic = false; break;
case 'underline': underline = true; break;
case '-underline': underline = false; break;
}
}
}
} }
return new TokenStyle(foregroundColor, bold, underline, italic); return new TokenStyle(foregroundColor, bold, underline, italic);
} }
function readCustomTokenStyleRules(tokenStylingRuleSection: IExperimentalTokenStyleCustomizations, result: TokenStylingRule[] = []) {
for (let key in tokenStylingRuleSection) {
if (key[0] !== '[') {
const classification = tokenClassificationRegistry.getTokenClassificationFromString(key);
if (classification) {
const settings = tokenStylingRuleSection[key];
let style: TokenStyle | undefined;
if (typeof settings === 'string') {
style = getTokenStyle(settings, undefined);
} else if (isTokenColorizationSetting(settings)) {
style = getTokenStyle(settings.foreground, settings.fontStyle);
}
if (style) {
result.push(tokenClassificationRegistry.getTokenStylingRule(classification, style));
}
}
}
}
return result;
}
function isTokenColorizationSetting(style: any): style is ITokenColorizationSetting {
return style && (style.foreground || style.fontStyle);
}
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information. * Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { ITokenColorizationRule, IColorMap } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ITextMateThemingRule, IColorMap } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { Color } from 'vs/base/common/color'; import { Color } from 'vs/base/common/color';
import * as colorRegistry from 'vs/platform/theme/common/colorRegistry'; import * as colorRegistry from 'vs/platform/theme/common/colorRegistry';
...@@ -18,9 +18,9 @@ function addSettingMapping(settingId: string, colorId: string) { ...@@ -18,9 +18,9 @@ function addSettingMapping(settingId: string, colorId: string) {
colorIds.push(colorId); colorIds.push(colorId);
} }
export function convertSettings(oldSettings: ITokenColorizationRule[], resultRules: ITokenColorizationRule[], resultColors: IColorMap): void { export function convertSettings(oldSettings: ITextMateThemingRule[], result: { textMateRules: ITextMateThemingRule[], colors: IColorMap }): void {
for (let rule of oldSettings) { for (let rule of oldSettings) {
resultRules.push(rule); result.textMateRules.push(rule);
if (!rule.scope) { if (!rule.scope) {
let settings = rule.settings; let settings = rule.settings;
if (!settings) { if (!settings) {
...@@ -34,7 +34,7 @@ export function convertSettings(oldSettings: ITokenColorizationRule[], resultRul ...@@ -34,7 +34,7 @@ export function convertSettings(oldSettings: ITokenColorizationRule[], resultRul
if (typeof colorHex === 'string') { if (typeof colorHex === 'string') {
let color = Color.fromHex(colorHex); let color = Color.fromHex(colorHex);
for (let colorId of mappings) { for (let colorId of mappings) {
resultColors[colorId] = color; result.colors[colorId] = color;
} }
} }
} }
......
...@@ -22,6 +22,7 @@ export const DETECT_HC_SETTING = 'window.autoDetectHighContrast'; ...@@ -22,6 +22,7 @@ export const DETECT_HC_SETTING = 'window.autoDetectHighContrast';
export const ICON_THEME_SETTING = 'workbench.iconTheme'; export const ICON_THEME_SETTING = 'workbench.iconTheme';
export const CUSTOM_WORKBENCH_COLORS_SETTING = 'workbench.colorCustomizations'; export const CUSTOM_WORKBENCH_COLORS_SETTING = 'workbench.colorCustomizations';
export const CUSTOM_EDITOR_COLORS_SETTING = 'editor.tokenColorCustomizations'; export const CUSTOM_EDITOR_COLORS_SETTING = 'editor.tokenColorCustomizations';
export const CUSTOM_EDITOR_TOKENSTYLES_SETTING = 'editor.tokenColorCustomizationsExperimental';
export interface IColorTheme extends ITheme { export interface IColorTheme extends ITheme {
readonly id: string; readonly id: string;
...@@ -30,7 +31,7 @@ export interface IColorTheme extends ITheme { ...@@ -30,7 +31,7 @@ export interface IColorTheme extends ITheme {
readonly extensionData?: ExtensionData; readonly extensionData?: ExtensionData;
readonly description?: string; readonly description?: string;
readonly isLoaded: boolean; readonly isLoaded: boolean;
readonly tokenColors: ITokenColorizationRule[]; readonly tokenColors: ITextMateThemingRule[];
} }
export interface IColorMap { export interface IColorMap {
...@@ -69,7 +70,7 @@ export interface IColorCustomizations { ...@@ -69,7 +70,7 @@ export interface IColorCustomizations {
} }
export interface ITokenColorCustomizations { export interface ITokenColorCustomizations {
[groupIdOrThemeSettingsId: string]: string | ITokenColorizationSetting | ITokenColorCustomizations | undefined | ITokenColorizationRule[]; [groupIdOrThemeSettingsId: string]: string | ITokenColorizationSetting | ITokenColorCustomizations | undefined | ITextMateThemingRule[];
comments?: string | ITokenColorizationSetting; comments?: string | ITokenColorizationSetting;
strings?: string | ITokenColorizationSetting; strings?: string | ITokenColorizationSetting;
numbers?: string | ITokenColorizationSetting; numbers?: string | ITokenColorizationSetting;
...@@ -77,10 +78,14 @@ export interface ITokenColorCustomizations { ...@@ -77,10 +78,14 @@ export interface ITokenColorCustomizations {
types?: string | ITokenColorizationSetting; types?: string | ITokenColorizationSetting;
functions?: string | ITokenColorizationSetting; functions?: string | ITokenColorizationSetting;
variables?: string | ITokenColorizationSetting; variables?: string | ITokenColorizationSetting;
textMateRules?: ITokenColorizationRule[]; textMateRules?: ITextMateThemingRule[];
} }
export interface ITokenColorizationRule { export interface IExperimentalTokenStyleCustomizations {
[styleRuleOrThemeSettingsId: string]: string | ITokenColorizationSetting | IExperimentalTokenStyleCustomizations | undefined;
}
export interface ITextMateThemingRule {
name?: string; name?: string;
scope?: string | string[]; scope?: string | string[];
settings: ITokenColorizationSetting; settings: ITokenColorizationSetting;
...@@ -89,7 +94,7 @@ export interface ITokenColorizationRule { ...@@ -89,7 +94,7 @@ export interface ITokenColorizationRule {
export interface ITokenColorizationSetting { export interface ITokenColorizationSetting {
foreground?: string; foreground?: string;
background?: string; background?: string;
fontStyle?: string; // italic, underline, bold fontStyle?: string; /* [italic|underline|bold] */
} }
export interface ExtensionData { export interface ExtensionData {
...@@ -106,4 +111,4 @@ export interface IThemeExtensionPoint { ...@@ -106,4 +111,4 @@ export interface IThemeExtensionPoint {
path: string; path: string;
uiTheme?: typeof VS_LIGHT_THEME | typeof VS_DARK_THEME | typeof VS_HC_THEME; uiTheme?: typeof VS_LIGHT_THEME | typeof VS_DARK_THEME | typeof VS_HC_THEME;
_watch: boolean; // unsupported options to watch location _watch: boolean; // unsupported options to watch location
} }
\ No newline at end of file
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
import { ColorThemeData } from 'vs/workbench/services/themes/common/colorThemeData'; import { ColorThemeData } from 'vs/workbench/services/themes/common/colorThemeData';
import * as assert from 'assert'; import * as assert from 'assert';
import { ITokenColorCustomizations } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ITokenColorCustomizations } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { TokenStyle, comments, variables, types, functions, keywords, numbers, strings, getTokenClassificationRegistry, TokenStylingRule } from 'vs/platform/theme/common/tokenClassificationRegistry'; import { TokenStyle, comments, variables, types, functions, keywords, numbers, strings, getTokenClassificationRegistry } from 'vs/platform/theme/common/tokenClassificationRegistry';
import { Color } from 'vs/base/common/color'; import { Color } from 'vs/base/common/color';
import { isString } from 'vs/base/common/types'; import { isString } from 'vs/base/common/types';
import { FileService } from 'vs/platform/files/common/fileService'; import { FileService } from 'vs/platform/files/common/fileService';
...@@ -44,14 +44,6 @@ function tokenStyleAsString(ts: TokenStyle | undefined | null) { ...@@ -44,14 +44,6 @@ function tokenStyleAsString(ts: TokenStyle | undefined | null) {
return str; return str;
} }
function getTokenStyleRules(rules: [string, TokenStyle][]): TokenStylingRule[] {
return rules.map(e => {
const rule = tokenClassificationRegistry.getTokenStylingRule(e[0], e[1]);
assert.ok(rule);
return rule!;
});
}
function assertTokenStyle(actual: TokenStyle | undefined | null, expected: TokenStyle | undefined | null, message?: string) { function assertTokenStyle(actual: TokenStyle | undefined | null, expected: TokenStyle | undefined | null, message?: string) {
assert.equal(tokenStyleAsString(actual), tokenStyleAsString(expected), message); assert.equal(tokenStyleAsString(actual), tokenStyleAsString(expected), message);
} }
...@@ -87,7 +79,7 @@ suite('Themes - TokenStyleResolving', () => { ...@@ -87,7 +79,7 @@ suite('Themes - TokenStyleResolving', () => {
assertTokenStyles(themeData, { assertTokenStyles(themeData, {
[comments]: ts('#88846f', undefinedStyle), [comments]: ts('#88846f', undefinedStyle),
[variables]: ts('#F8F8F2', unsetStyle), [variables]: ts('#F8F8F2', unsetStyle),
[types]: ts('#A6E22E', { underline: true, bold: false, italic: false }), [types]: ts('#A6E22E', { underline: true }),
[functions]: ts('#A6E22E', unsetStyle), [functions]: ts('#A6E22E', unsetStyle),
[strings]: ts('#E6DB74', undefinedStyle), [strings]: ts('#E6DB74', undefinedStyle),
[numbers]: ts('#AE81FF', undefinedStyle), [numbers]: ts('#AE81FF', undefinedStyle),
...@@ -187,7 +179,7 @@ suite('Themes - TokenStyleResolving', () => { ...@@ -187,7 +179,7 @@ suite('Themes - TokenStyleResolving', () => {
assertTokenStyles(themeData, { assertTokenStyles(themeData, {
[comments]: ts('#384887', undefinedStyle), [comments]: ts('#384887', undefinedStyle),
[variables]: ts(undefined, unsetStyle), [variables]: ts(undefined, unsetStyle),
[types]: ts('#ffeebb', { underline: true, bold: false, italic: false }), [types]: ts('#ffeebb', { underline: true }),
[functions]: ts('#ddbb88', unsetStyle), [functions]: ts('#ddbb88', unsetStyle),
[strings]: ts('#22aa44', undefinedStyle), [strings]: ts('#22aa44', undefinedStyle),
[numbers]: ts('#f280d0', undefinedStyle), [numbers]: ts('#f280d0', undefinedStyle),
...@@ -259,43 +251,43 @@ suite('Themes - TokenStyleResolving', () => { ...@@ -259,43 +251,43 @@ suite('Themes - TokenStyleResolving', () => {
assertTokenStyle(tokenStyle, defaultTokenStyle, 'keyword.operators'); assertTokenStyle(tokenStyle, defaultTokenStyle, 'keyword.operators');
tokenStyle = themeData.resolveScopes([['storage']]); tokenStyle = themeData.resolveScopes([['storage']]);
assertTokenStyle(tokenStyle, ts('#F92672', { italic: true, underline: false, bold: false }), 'storage'); assertTokenStyle(tokenStyle, ts('#F92672', { italic: true }), 'storage');
tokenStyle = themeData.resolveScopes([['storage.type']]); tokenStyle = themeData.resolveScopes([['storage.type']]);
assertTokenStyle(tokenStyle, ts('#66D9EF', { italic: true, underline: false, bold: false }), 'storage.type'); assertTokenStyle(tokenStyle, ts('#66D9EF', { italic: true }), 'storage.type');
tokenStyle = themeData.resolveScopes([['entity.name.class']]); tokenStyle = themeData.resolveScopes([['entity.name.class']]);
assertTokenStyle(tokenStyle, ts('#A6E22E', { underline: true, italic: false, bold: false }), 'entity.name.class'); assertTokenStyle(tokenStyle, ts('#A6E22E', { underline: true }), 'entity.name.class');
tokenStyle = themeData.resolveScopes([['meta.structure.dictionary.json', 'string.quoted.double.json']]); tokenStyle = themeData.resolveScopes([['meta.structure.dictionary.json', 'string.quoted.double.json']]);
assertTokenStyle(tokenStyle, ts('#66D9EF', undefined), 'json property'); assertTokenStyle(tokenStyle, ts('#66D9EF', undefined), 'json property');
tokenStyle = themeData.resolveScopes([['keyword'], ['storage.type'], ['entity.name.class']]); tokenStyle = themeData.resolveScopes([['keyword'], ['storage.type'], ['entity.name.class']]);
assertTokenStyle(tokenStyle, ts('#66D9EF', { italic: true, underline: false, bold: false }), 'storage.type'); assertTokenStyle(tokenStyle, ts('#66D9EF', { italic: true }), 'storage.type');
}); });
test('rule matching', async () => { test('rule matching', async () => {
const themeData = ColorThemeData.createLoadedEmptyTheme('test', 'test'); const themeData = ColorThemeData.createLoadedEmptyTheme('test', 'test');
themeData.setCustomColors({ 'editor.foreground': '#000000' }); themeData.setCustomColors({ 'editor.foreground': '#000000' });
themeData.setTokenStyleRules(getTokenStyleRules([ themeData.setCustomTokenStyleRules({
['types', ts('#ff0000', undefined)], 'types': '#ff0000',
['classes', ts('#0000ff', { italic: true })], 'classes': { foreground: '#0000ff', fontStyle: 'italic' },
['*.static', ts(undefined, { bold: true })], '*.static': { fontStyle: 'bold' },
['*.declaration', ts(undefined, { italic: true })], '*.declaration': { fontStyle: 'italic' },
['*.async.static', ts('#00ffff', { bold: false, underline: true })], '*.async.static': { fontStyle: 'italic underline' },
['*.async', ts('#000fff', { italic: false, underline: true })] '*.async': { foreground: '#000fff', fontStyle: '-italic underline' }
])); });
assertTokenStyles(themeData, { assertTokenStyles(themeData, {
'types': ts('#ff0000', undefinedStyle), 'types': ts('#ff0000', undefinedStyle),
'types.static': ts('#ff0000', { bold: true, italic: undefined, underline: undefined }), 'types.static': ts('#ff0000', { bold: true }),
'types.static.declaration': ts('#ff0000', { bold: true, italic: true, underline: undefined }), 'types.static.declaration': ts('#ff0000', { bold: true, italic: true }),
'classes': ts('#0000ff', { bold: undefined, italic: true, underline: undefined }), 'classes': ts('#0000ff', { italic: true }),
'classes.static.declaration': ts('#0000ff', { bold: true, italic: true, underline: undefined }), 'classes.static.declaration': ts('#0000ff', { bold: true, italic: true }),
'classes.declaration': ts('#0000ff', { bold: undefined, italic: true, underline: undefined }), 'classes.declaration': ts('#0000ff', { italic: true }),
'classes.declaration.async': ts('#000fff', { bold: undefined, italic: false, underline: true }), 'classes.declaration.async': ts('#000fff', { underline: true, italic: false }),
'classes.declaration.async.static': ts('#00ffff', { bold: false, italic: false, underline: true }), 'classes.declaration.async.static': ts('#000fff', { italic: true, underline: true, bold: true }),
}); });
}); });
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册