提交 c3984ce5 编写于 作者: M Martin Aeschlimann

[themes] add type to color theme file

上级 b90893cb
{
"name": "Abyss",
"type": "dark",
"tokenColors": [
{
"settings": {
......
{
"$schema": "vscode://schemas/color-theme",
"name": "Dark Default Colors",
"type": "dark",
"colors": {
"editorBackground": "#1e1e1e",
"editorForeground": "#D4D4D4",
......
{
"$schema": "vscode://schemas/color-theme",
"name": "Dark+ (default dark)",
"type": "dark",
"include": "./dark_vs.json",
"tokenColors": [
{
......
{
"$schema": "vscode://schemas/color-theme",
"name": "Dark (Visual Studio)",
"type": "dark",
"include": "./dark_defaults.json",
"tokenColors": [
{
......
{
"$schema": "vscode://schemas/color-theme",
"name": "Dark High Contrast",
"type": "hc",
"include": "./hc_black_defaults.json",
"settings": [
{
......
{
"$schema": "vscode://schemas/color-theme",
"name": "High Contrast Default Colors",
"type": "hc",
"colors": {
"editorBackground": "#000000",
"editorForeground": "#FFFFFF",
......
{
"$schema": "vscode://schemas/color-theme",
"name": "Light Default Colors",
"type": "light",
"colors": {
"editorBackground": "#ffffff",
"editorForeground": "#000000",
......
{
"$schema": "vscode://schemas/color-theme",
"name": "Light+ (default light)",
"type": "light",
"include": "./light_vs.json",
"tokenColors": [
{
......
{
"$schema": "vscode://schemas/color-theme",
"name": "Light (Visual Studio)",
"type": "light",
"include": "./light_defaults.json",
"tokenColors": [
{
......
{
"name": "Quiet Light",
"type": "light",
"tokenColors": [
{
"settings": {
......
{
"name": "Solarized (dark)",
"type": "dark",
"tokenColors": [
{
"settings": {
......
{
"name": "Solarized (light)",
"type": "light",
"tokenColors": [
{
"settings": {
......
......@@ -172,9 +172,15 @@ const schemaId = 'vscode://schemas/color-theme';
const schema: IJSONSchema = {
type: 'object',
properties: {
type: {
description: nls.localize('schema.type', 'The type of the theme, either light, dark or high contrast. Depending on the type, a different set of icons is used.'),
enum: ['light', 'dark', 'hc'],
enumDescriptions: [nls.localize('schema.light', 'Light theme, using dark icons'), nls.localize('schema.dark', 'Dark theme, using light icons'), nls.localize('schema.hc', 'High contrast theme, using light icons')]
},
colors: colorsSchema,
tokenColors: tokenColorsSchema
}
},
required: ['type']
};
export function register() {
......
......@@ -9,7 +9,7 @@ import { Color } from 'vs/base/common/color';
import { ExtensionData, ITokenColorizationRule, IColorTheme, IColorMap, VS_LIGHT_THEME, VS_HC_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { convertSettings } from 'vs/workbench/services/themes/electron-browser/themeCompatibility';
import { TPromise } from 'vs/base/common/winjs.base';
import { getBaseThemeId, getSyntaxThemeId, isDarkTheme, isLightTheme } from 'vs/platform/theme/common/themes';
import { getBaseThemeId, getSyntaxThemeId } from 'vs/platform/theme/common/themes';
import nls = require('vs/nls');
import * as types from 'vs/base/common/types';
import * as objects from 'vs/base/common/objects';
......@@ -31,6 +31,7 @@ export class ColorThemeData implements IColorTheme {
}
id: string;
type: ThemeType;
label: string;
settingsId: string;
description?: string;
......@@ -79,7 +80,8 @@ export class ColorThemeData implements IColorTheme {
this.colorMap = {};
this.defaultColorMap = {};
if (this.path) {
return _loadColorThemeFromFile(this.path, this.tokenColors, this.colorMap).then(_ => {
return _loadColorThemeFromFile(this.path, this.tokenColors, this.colorMap, this.type).then(type => {
this.type = type;
this.isLoaded = true;
_completeTokenColors(this);
});
......@@ -106,6 +108,7 @@ export class ColorThemeData implements IColorTheme {
}
return JSON.stringify({
id: this.id,
type: this.type,
label: this.label,
settingsId: this.settingsId,
selector: this.selector,
......@@ -119,20 +122,12 @@ export class ColorThemeData implements IColorTheme {
return objects.equals(this.colorMap, other.colorMap) && objects.equals(this.tokenColors, other.tokenColors);
}
get type(): ThemeType {
switch (this.getBaseThemeId()) {
case VS_LIGHT_THEME: return 'light';
case VS_HC_THEME: return 'hc';
default: return 'dark';
}
}
isLightTheme() {
return isLightTheme(this.id);
return this.type === 'light';
}
isDarkTheme() {
return isDarkTheme(this.id);
return this.type === 'dark';
}
getSyntaxThemeId() {
......@@ -157,6 +152,9 @@ export function fromStorageData(themeService: WorkbenchThemeService, input: stri
}
}
}
if (!theme.type) {
theme.type = baseThemeToType(theme.id);
}
return theme;
}
......@@ -166,6 +164,7 @@ export function fromExtensionTheme(themeService: WorkbenchThemeService, theme: I
let themeSelector = toCSSSelector(extensionData.extensionId + '-' + Paths.normalize(theme.path));
let themeData = new ColorThemeData(themeService);
themeData.id = `${baseTheme} ${themeSelector}`;
themeData.type = baseThemeToType(baseTheme);
themeData.label = theme.label || Paths.basename(theme.path);
themeData.settingsId = theme.id || themeData.label;
themeData.selector = `${baseTheme}.${themeSelector}`;
......@@ -176,6 +175,14 @@ export function fromExtensionTheme(themeService: WorkbenchThemeService, theme: I
return themeData;
}
function baseThemeToType(baseTheme: string): ThemeType {
switch (getBaseThemeId(baseTheme)) {
case VS_LIGHT_THEME: return 'light';
case VS_HC_THEME: return 'hc';
default: return 'dark';
}
}
function toCSSSelector(str: string) {
str = str.replace(/[^_\-a-zA-Z0-9]/g, '-');
if (str.charAt(0).match(/[0-9\-]/)) {
......@@ -184,27 +191,33 @@ function toCSSSelector(str: string) {
return str;
}
function _loadColorThemeFromFile(themePath: string, resultRules: ITokenColorizationRule[], resultColors: IColorMap): TPromise<any> {
function _loadColorThemeFromFile(themePath: string, resultRules: ITokenColorizationRule[], resultColors: IColorMap, defaultType: ThemeType): TPromise<ThemeType> {
if (Paths.extname(themePath) === '.json') {
return pfs.readFile(themePath).then(content => {
let errors: Json.ParseError[] = [];
let contentValue = Json.parse(content.toString(), errors);
if (errors.length > 0) {
return TPromise.wrapError(new Error(nls.localize('error.cannotparsejson', "Problems parsing JSON theme file: {0}", errors.map(e => Json.getParseErrorMessage(e.error)).join(', '))));
return TPromise.wrapError<ThemeType>(new Error(nls.localize('error.cannotparsejson', "Problems parsing JSON theme file: {0}", errors.map(e => Json.getParseErrorMessage(e.error)).join(', '))));
}
let includeCompletes = TPromise.as(null);
let includeCompletes = TPromise.as(defaultType);
if (contentValue.include) {
includeCompletes = _loadColorThemeFromFile(Paths.join(Paths.dirname(themePath), contentValue.include), resultRules, resultColors);
includeCompletes = _loadColorThemeFromFile(Paths.join(Paths.dirname(themePath), contentValue.include), resultRules, resultColors, defaultType);
}
return includeCompletes.then(_ => {
return includeCompletes.then(type => {
if (Array.isArray(contentValue.settings)) {
convertSettings(contentValue.settings, resultRules, resultColors);
return null;
return type;
}
let themeType = contentValue.type;
if (themeType !== 'light' && themeType !== 'dark' && themeType !== 'hc') {
return TPromise.wrapError<ThemeType>(new Error(nls.localize({ key: 'error.invalidformat.type', comment: ['{0} will be replaced by a path. Values in quotes should not be translated.'] }, "Problem parsing color theme file: {0}. Property 'type' must be 'light', 'dark' or 'hc'", themePath)));
}
type = themeType;
let colors = contentValue.colors;
if (colors) {
if (typeof colors !== 'object') {
return TPromise.wrapError(new Error(nls.localize({ key: 'error.invalidformat.colors', comment: ['{0} will be replaced by a path. Values in quotes should not be translated.'] }, "Problem parsing color theme file: {0}. Property 'colors' is not of type 'object'.", themePath)));
return TPromise.wrapError<ThemeType>(new Error(nls.localize({ key: 'error.invalidformat.colors', comment: ['{0} will be replaced by a path. Values in quotes should not be translated.'] }, "Problem parsing color theme file: {0}. Property 'colors' is not of type 'object'.", themePath)));
}
// new JSON color themes format
for (let colorId in colors) {
......@@ -218,36 +231,35 @@ function _loadColorThemeFromFile(themePath: string, resultRules: ITokenColorizat
if (tokenColors) {
if (Array.isArray(tokenColors)) {
resultRules.push(...tokenColors);
return null;
} else if (typeof tokenColors === 'string') {
return _loadSyntaxTokensFromFile(Paths.join(Paths.dirname(themePath), tokenColors), resultRules, {});
return _loadSyntaxTokensFromFile(Paths.join(Paths.dirname(themePath), tokenColors), resultRules, {}, type);
} else {
return TPromise.wrapError(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 text mate theme file", themePath)));
return TPromise.wrapError<ThemeType>(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 text mate theme file", themePath)));
}
}
return null;
return type;
});
});
} else {
return _loadSyntaxTokensFromFile(themePath, resultRules, resultColors);
return _loadSyntaxTokensFromFile(themePath, resultRules, resultColors, defaultType);
}
}
function _loadSyntaxTokensFromFile(themePath: string, resultRules: ITokenColorizationRule[], resultColors: IColorMap): TPromise<any> {
function _loadSyntaxTokensFromFile(themePath: string, resultRules: ITokenColorizationRule[], resultColors: IColorMap, type: ThemeType): TPromise<ThemeType> {
return pfs.readFile(themePath).then(content => {
try {
let contentValue = plist.parse(content.toString());
let settings: ITokenColorizationRule[] = contentValue.settings;
if (!Array.isArray(settings)) {
return TPromise.wrapError(new Error(nls.localize('error.plist.invalidformat', "Problem parsing tmTheme file: {0}. 'settings' is not array.")));
return TPromise.wrapError<ThemeType>(new Error(nls.localize('error.plist.invalidformat', "Problem parsing tmTheme file: {0}. 'settings' is not array.")));
}
convertSettings(settings, resultRules, resultColors);
return TPromise.as(null);
return TPromise.as(type);
} catch (e) {
return TPromise.wrapError(new Error(nls.localize('error.cannotparse', "Problems parsing tmTheme file: {0}", e.message)));
return TPromise.wrapError<ThemeType>(new Error(nls.localize('error.cannotparse', "Problems parsing tmTheme file: {0}", e.message)));
}
}, error => {
return TPromise.wrapError(new Error(nls.localize('error.cannotload', "Problems loading tmTheme file {0}: {1}", themePath, error.message)));
return TPromise.wrapError<ThemeType>(new Error(nls.localize('error.cannotload', "Problems loading tmTheme file {0}: {1}", themePath, error.message)));
});
}
/**
......
......@@ -84,12 +84,8 @@ let themesExtPoint = ExtensionsRegistry.registerExtensionPoint<IThemeExtensionPo
description: nls.localize('vscode.extension.contributes.themes.label', 'Label of the color theme as shown in the UI.'),
type: 'string'
},
uiTheme: {
description: nls.localize('vscode.extension.contributes.themes.uiTheme', 'Base theme defining the colors around the editor: \'vs\' is the light color theme, \'vs-dark\' is the dark color theme. \'hc-black\' is the dark high contrast theme.'),
enum: [VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME]
},
path: {
description: nls.localize('vscode.extension.contributes.themes.path', 'Path of the tmTheme file. The path is relative to the extension folder and is typically \'./themes/themeFile.tmTheme\'.'),
description: nls.localize('vscode.extension.contributes.themes.path', 'Path of the color theme file. The path is relative to the extension folder and is typically \'./themes/my-color-theme.json\'.'),
type: 'string'
}
},
......@@ -240,6 +236,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
let initialTheme = new ColorThemeData(this);
initialTheme.id = isLightTheme ? VS_LIGHT_THEME : VS_DARK_THEME;
initialTheme.type = isLightTheme ? 'light' : 'dark';
initialTheme.label = '';
initialTheme.selector = isLightTheme ? VS_LIGHT_THEME : VS_DARK_THEME;
initialTheme.settingsId = null;
......@@ -303,9 +300,9 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
return pfs.writeFile(backupFileLocation, content).then(_ => backupFileLocation);
}, err => {
if (err && err.code === 'ENOENT') {
return TPromise.as(null); // ignore, user config file doesn't exist yet
return TPromise.as<string>(null); // ignore, user config file doesn't exist yet
};
return TPromise.wrapError(err);
return TPromise.wrapError<string>(err);
});
}
......@@ -406,14 +403,14 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
if (themeData) {
return themeData.ensureLoaded(this).then(_ => {
if (themeId === this.currentColorTheme.id && !this.currentColorTheme.isLoaded && this.currentColorTheme.hasEqualData(themeData)) {
// the loaded theme is identical to the perisisted theme. Don't need to send an event.
// the loaded theme is identical to the persisted theme. Don't need to send an event.
this.currentColorTheme = themeData;
return TPromise.as(themeData);
}
this.updateDynamicCSSRules(themeData);
return this.applyTheme(themeData, settingsTarget);
}, error => {
return TPromise.wrapError(nls.localize('error.cannotloadtheme', "Unable to load {0}: {1}", themeData.path, error.message));
return TPromise.wrapError<IColorTheme>(nls.localize('error.cannotloadtheme', "Unable to load {0}: {1}", themeData.path, error.message));
});
}
return null;
......@@ -435,7 +432,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
_applyRules(cssRules.join('\n'), colorThemeRulesClassName);
}
private applyTheme(newTheme: ColorThemeData, settingsTarget: ConfigurationTarget, silent = false): TPromise<IFileIconTheme> {
private applyTheme(newTheme: ColorThemeData, settingsTarget: ConfigurationTarget, silent = false): TPromise<IColorTheme> {
if (this.container) {
if (this.currentColorTheme) {
$(this.container).removeClass(this.currentColorTheme.id);
......@@ -466,7 +463,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
return this.writeColorThemeConfiguration(settingsTarget);
};
private writeColorThemeConfiguration(settingsTarget: ConfigurationTarget): TPromise<IFileIconTheme> {
private writeColorThemeConfiguration(settingsTarget: ConfigurationTarget): TPromise<IColorTheme> {
if (!types.isUndefinedOrNull(settingsTarget)) {
return this.configurationWriter.writeConfiguration(COLOR_THEME_SETTING, this.currentColorTheme.settingsId, settingsTarget).then(_ => this.currentColorTheme);
}
......@@ -479,13 +476,13 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
private findThemeData(themeId: string, defaultId?: string): TPromise<ColorThemeData> {
return this.getColorThemes().then(allThemes => {
let defaultTheme: IColorTheme = void 0;
let defaultTheme: ColorThemeData = void 0;
for (let t of allThemes) {
if (t.id === themeId) {
return t;
return <ColorThemeData>t;
}
if (t.id === defaultId) {
defaultTheme = t;
defaultTheme = <ColorThemeData>t;
}
}
return defaultTheme;
......@@ -494,13 +491,13 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
public findThemeDataBySettingsId(settingsId: string, defaultId: string): TPromise<ColorThemeData> {
return this.getColorThemes().then(allThemes => {
let defaultTheme: IColorTheme = void 0;
let defaultTheme: ColorThemeData = void 0;
for (let t of allThemes) {
if (t.settingsId === settingsId) {
return t;
return <ColorThemeData>t;
}
if (t.id === defaultId) {
defaultTheme = t;
defaultTheme = <ColorThemeData>t;
}
}
return defaultTheme;
......@@ -735,7 +732,7 @@ function _applyIconTheme(data: IInternalIconThemeData, onApply: (theme: IInterna
_applyRules(data.styleSheetContent, iconThemeRulesClassName);
return onApply(data);
}, error => {
return TPromise.wrapError(nls.localize('error.cannotloadicontheme', "Unable to load {0}", data.path));
return TPromise.wrapError<IFileIconTheme>(nls.localize('error.cannotloadicontheme', "Unable to load {0}", data.path));
});
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册