workbenchThemeService.ts 22.2 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

J
Johannes Rieken 已提交
7
import { TPromise, Promise } from 'vs/base/common/winjs.base';
E
Erich Gamma 已提交
8
import nls = require('vs/nls');
M
Martin Aeschlimann 已提交
9
import * as types from 'vs/base/common/types';
J
Johannes Rieken 已提交
10
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
11
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, CUSTOM_EDITOR_SCOPE_COLORS_SETTING } from 'vs/workbench/services/themes/common/workbenchThemeService';
12
import { IStorageService } from 'vs/platform/storage/common/storage';
J
Johannes Rieken 已提交
13
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
14
import { Registry } from 'vs/platform/registry/common/platform';
B
Benjamin Pasero 已提交
15
import errors = require('vs/base/common/errors');
16
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
17
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, IConfigurationPropertySchema, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry';
18
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
19
import { ColorThemeData } from './colorThemeData';
20
import { ITheme, Extensions as ThemingExtensions, IThemingRegistry } from 'vs/platform/theme/common/themeService';
21
import { editorBackground } from 'vs/platform/theme/common/colorRegistry';
J
Joao Moreno 已提交
22
import { Color } from 'vs/base/common/color';
J
Johannes Rieken 已提交
23 24 25

import { $ } from 'vs/base/browser/builder';
import Event, { Emitter } from 'vs/base/common/event';
E
Erich Gamma 已提交
26

27 28
import colorThemeSchema = require('vs/workbench/services/themes/common/colorThemeSchema');
import fileIconThemeSchema = require('vs/workbench/services/themes/common/fileIconThemeSchema');
29
import { IDisposable } from 'vs/base/common/lifecycle';
30
import { IBroadcastService } from 'vs/platform/broadcast/electron-browser/broadcastService';
31 32 33
import { ColorThemeStore } from 'vs/workbench/services/themes/electron-browser/colorThemeStore';
import { FileIconThemeStore } from 'vs/workbench/services/themes/electron-browser/fileIconThemeStore';
import { FileIconThemeData } from 'vs/workbench/services/themes/electron-browser/fileIconThemeData';
34

E
Erich Gamma 已提交
35 36
// implementation

M
Martin Aeschlimann 已提交
37
const DEFAULT_THEME_ID = 'vs-dark vscode-theme-defaults-themes-dark_plus-json';
38
const DEFAULT_THEME_SETTING_VALUE = 'Default Dark+';
M
Martin Aeschlimann 已提交
39

40
const PERSISTED_THEME_STORAGE_KEY = 'colorThemeData';
41
const PERSISTED_ICON_THEME_STORAGE_KEY = 'iconThemeData';
42

43 44 45
const defaultThemeExtensionId = 'vscode-theme-defaults';
const oldDefaultThemeExtensionId = 'vscode-theme-colorful-defaults';

46
const DEFAULT_ICON_THEME_SETTING_VALUE = 'vs-seti';
47 48
const fileIconsEnabledClass = 'file-icons-enabled';

49 50 51
const colorThemeRulesClassName = 'contributedColorTheme';
const iconThemeRulesClassName = 'contributedIconTheme';

52 53
const themingRegistry = Registry.as<IThemingRegistry>(ThemingExtensions.ThemingContribution);

J
Johannes Rieken 已提交
54
function validateThemeId(theme: string): string {
55 56
	// migrations
	switch (theme) {
57 58 59
		case VS_LIGHT_THEME: return `vs ${defaultThemeExtensionId}-themes-light_vs-json`;
		case VS_DARK_THEME: return `vs-dark ${defaultThemeExtensionId}-themes-dark_vs-json`;
		case VS_HC_THEME: return `hc-black ${defaultThemeExtensionId}-themes-hc_black-json`;
60 61 62 63 64 65
		case `vs ${oldDefaultThemeExtensionId}-themes-light_plus-tmTheme`: return `vs ${defaultThemeExtensionId}-themes-light_plus-json`;
		case `vs-dark ${oldDefaultThemeExtensionId}-themes-dark_plus-tmTheme`: return `vs-dark ${defaultThemeExtensionId}-themes-dark_plus-json`;
	}
	return theme;
}

66
export interface IColorCustomizations {
67
	[colorId: string]: string;
68 69
}

70
export class WorkbenchThemeService implements IWorkbenchThemeService {
71
	_serviceBrand: any;
E
Erich Gamma 已提交
72

73
	private colorThemeStore: ColorThemeStore;
74
	private currentColorTheme: ColorThemeData;
M
Martin Aeschlimann 已提交
75
	private container: HTMLElement;
M
Martin Aeschlimann 已提交
76
	private onColorThemeChange: Emitter<IColorTheme>;
M
Martin Aeschlimann 已提交
77

78
	private iconThemeStore: FileIconThemeStore;
M
Martin Aeschlimann 已提交
79 80
	private currentIconTheme: IFileIconTheme;
	private onFileIconThemeChange: Emitter<IFileIconTheme>;
E
Erich Gamma 已提交
81

82
	private themingParticipantChangeListener: IDisposable;
83
	private _configurationWriter: ConfigurationWriter;
84

85 86 87 88 89 90 91 92
	private get colorCustomizations(): IColorCustomizations {
		return this.configurationService.getValue<IColorCustomizations>(CUSTOM_WORKBENCH_COLORS_SETTING);
	}

	private get tokenColorCustomizations(): ITokenColorCustomizations {
		return this.configurationService.getValue<ITokenColorCustomizations>(CUSTOM_EDITOR_COLORS_SETTING);
	}

M
Martin Aeschlimann 已提交
93
	constructor(
B
Benjamin Pasero 已提交
94
		container: HTMLElement,
95
		@IExtensionService extensionService: IExtensionService,
J
Johannes Rieken 已提交
96
		@IStorageService private storageService: IStorageService,
97
		@IBroadcastService private broadcastService: IBroadcastService,
M
Martin Aeschlimann 已提交
98
		@IConfigurationService private configurationService: IConfigurationService,
99 100
		@ITelemetryService private telemetryService: ITelemetryService,
		@IInstantiationService private instantiationService: IInstantiationService) {
101

B
Benjamin Pasero 已提交
102
		this.container = container;
103
		this.colorThemeStore = new ColorThemeStore(extensionService);
104
		this.onFileIconThemeChange = new Emitter<IFileIconTheme>();
105
		this.iconThemeStore = new FileIconThemeStore(extensionService);
106 107
		this.onColorThemeChange = new Emitter<IColorTheme>();

M
Martin Aeschlimann 已提交
108 109 110
		this.currentIconTheme = {
			id: '',
			label: '',
M
Martin Aeschlimann 已提交
111
			settingsId: null,
M
Martin Aeschlimann 已提交
112 113
			isLoaded: false,
			hasFileIcons: false,
M
Martin Aeschlimann 已提交
114
			hasFolderIcons: false,
115
			hidesExplorerArrows: false,
M
Martin Aeschlimann 已提交
116
			extensionData: null
M
Martin Aeschlimann 已提交
117
		};
118

119 120 121
		// In order to avoid paint flashing for tokens, because
		// themes are loaded asynchronously, we need to initialize
		// a color theme document with good defaults until the theme is loaded
122
		let themeData = null;
123 124
		let persistedThemeData = this.storageService.get(PERSISTED_THEME_STORAGE_KEY);
		if (persistedThemeData) {
125
			themeData = ColorThemeData.fromStorageData(persistedThemeData);
126
		}
127
		if (!themeData) {
128
			let isLightTheme = (Array.prototype.indexOf.call(document.body.classList, 'vs') >= 0);
129
			themeData = ColorThemeData.createUnloadedTheme(isLightTheme ? VS_LIGHT_THEME : VS_DARK_THEME);
130
		}
131
		themeData.setCustomColors(this.colorCustomizations);
132
		themeData.setCustomTokenColors(this.tokenColorCustomizations);
133 134
		this.updateDynamicCSSRules(themeData);
		this.applyTheme(themeData, null, true);
E
Erich Gamma 已提交
135

136 137 138 139 140 141 142 143 144 145 146 147 148 149
		let iconData: FileIconThemeData = null;
		let persistedIconThemeData = this.storageService.get(PERSISTED_ICON_THEME_STORAGE_KEY);
		if (persistedIconThemeData) {
			iconData = FileIconThemeData.fromStorageData(persistedIconThemeData);

			if (iconData) {
				_applyIconTheme(iconData, () => {
					this.doSetFileIconTheme(iconData);

					return TPromise.wrap(iconData);
				});
			}
		}

150 151
		this.initialize().then(null, errors.onUnexpectedError).then(_ => {
			this.installConfigurationListener();
M
Martin Aeschlimann 已提交
152
		});
153 154 155 156 157

		// update settings schema setting
		this.colorThemeStore.onDidChange(themes => {
			colorThemeSettingSchema.enum = themes.map(t => t.settingsId);
			colorThemeSettingSchema.enumDescriptions = themes.map(t => themeData.description || '');
158
			configurationRegistry.notifyConfigurationSchemaUpdated(themeSettingsConfiguration);
159 160
		});
		this.iconThemeStore.onDidChange(themes => {
161 162
			iconThemeSettingSchema.enum = [null, ...themes.map(t => t.settingsId)];
			iconThemeSettingSchema.enumDescriptions = [iconThemeSettingSchema.enumDescriptions[0], ...themes.map(t => themeData.description || '')];
163
			configurationRegistry.notifyConfigurationSchemaUpdated(themeSettingsConfiguration);
164
		});
M
Martin Aeschlimann 已提交
165 166
	}

M
Martin Aeschlimann 已提交
167
	public get onDidColorThemeChange(): Event<IColorTheme> {
M
Martin Aeschlimann 已提交
168
		return this.onColorThemeChange.event;
E
Erich Gamma 已提交
169 170
	}

M
Martin Aeschlimann 已提交
171 172 173 174
	public get onDidFileIconThemeChange(): Event<IFileIconTheme> {
		return this.onFileIconThemeChange.event;
	}

175 176
	public get onThemeChange(): Event<ITheme> {
		return this.onColorThemeChange.event;
177 178
	}

R
Ron Buckton 已提交
179
	private initialize(): TPromise<[IColorTheme, IFileIconTheme]> {
180

181 182
		let colorThemeSetting = this.configurationService.getValue<string>(COLOR_THEME_SETTING);
		let iconThemeSetting = this.configurationService.getValue<string>(ICON_THEME_SETTING) || '';
M
Martin Aeschlimann 已提交
183

184
		return Promise.join([
185
			this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID).then(theme => {
M
Martin Aeschlimann 已提交
186 187
				return this.setColorTheme(theme && theme.id, null);
			}),
188
			this.iconThemeStore.findThemeBySettingsId(iconThemeSetting).then(theme => {
M
Martin Aeschlimann 已提交
189 190
				return this.setFileIconTheme(theme && theme.id, null);
			}),
191
		]);
M
Martin Aeschlimann 已提交
192 193
	}

194
	private installConfigurationListener() {
195
		this.configurationService.onDidChangeConfiguration(e => {
196 197 198 199 200 201 202 203 204
			if (e.affectsConfiguration(COLOR_THEME_SETTING)) {
				let colorThemeSetting = this.configurationService.getValue<string>(COLOR_THEME_SETTING);
				if (colorThemeSetting !== this.currentColorTheme.settingsId) {
					this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, null).then(theme => {
						if (theme) {
							this.setColorTheme(theme.id, null);
						}
					});
				}
205
			}
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
			if (e.affectsConfiguration(ICON_THEME_SETTING)) {
				let iconThemeSetting = this.configurationService.getValue<string>(ICON_THEME_SETTING) || '';
				if (iconThemeSetting !== this.currentIconTheme.settingsId) {
					this.iconThemeStore.findThemeBySettingsId(iconThemeSetting).then(theme => {
						this.setFileIconTheme(theme && theme.id, null);
					});
				}
			}
			if (this.currentColorTheme) {
				let hasColorChanges = false;
				if (e.affectsConfiguration(CUSTOM_WORKBENCH_COLORS_SETTING)) {
					this.currentColorTheme.setCustomColors(this.colorCustomizations);
					hasColorChanges = true;
				}
				if (e.affectsConfiguration(CUSTOM_EDITOR_COLORS_SETTING)) {
					this.currentColorTheme.setCustomTokenColors(this.tokenColorCustomizations);
					hasColorChanges = true;
				}
				if (hasColorChanges) {
					this.updateDynamicCSSRules(this.currentColorTheme);
					this.onColorThemeChange.fire(this.currentColorTheme);
				}
228 229 230 231
			}
		});
	}

232 233 234 235 236 237 238 239
	public getColorTheme(): IColorTheme {
		return this.currentColorTheme;
	}

	public getColorThemes(): TPromise<IColorTheme[]> {
		return this.colorThemeStore.getColorThemes();
	}

240 241 242 243
	public getTheme(): ITheme {
		return this.getColorTheme();
	}

M
Martin Aeschlimann 已提交
244
	public setColorTheme(themeId: string, settingsTarget: ConfigurationTarget): TPromise<IColorTheme> {
M
Martin Aeschlimann 已提交
245
		if (!themeId) {
M
Martin Aeschlimann 已提交
246
			return TPromise.as(null);
M
Martin Aeschlimann 已提交
247
		}
M
Martin Aeschlimann 已提交
248
		if (themeId === this.currentColorTheme.id && this.currentColorTheme.isLoaded) {
249
			return this.writeColorThemeConfiguration(settingsTarget);
M
Martin Aeschlimann 已提交
250 251 252 253
		}

		themeId = validateThemeId(themeId); // migrate theme ids

254
		return this.colorThemeStore.findThemeData(themeId, DEFAULT_THEME_ID).then(themeData => {
M
Martin Aeschlimann 已提交
255
			if (themeData) {
256
				return themeData.ensureLoaded(this).then(_ => {
257
					if (themeId === this.currentColorTheme.id && !this.currentColorTheme.isLoaded && this.currentColorTheme.hasEqualData(themeData)) {
258
						// the loaded theme is identical to the perisisted theme. Don't need to send an event.
259
						this.currentColorTheme = themeData;
260
						themeData.setCustomColors(this.colorCustomizations);
C
Cody Hoover 已提交
261
						themeData.setCustomTokenColors(this.tokenColorCustomizations);
262 263
						return TPromise.as(themeData);
					}
264
					themeData.setCustomColors(this.colorCustomizations);
C
Cody Hoover 已提交
265
					themeData.setCustomTokenColors(this.tokenColorCustomizations);
266
					this.updateDynamicCSSRules(themeData);
267
					return this.applyTheme(themeData, settingsTarget);
268
				}, error => {
269
					return TPromise.wrapError<IColorTheme>(new Error(nls.localize('error.cannotloadtheme', "Unable to load {0}: {1}", themeData.path, error.message)));
270
				});
M
Martin Aeschlimann 已提交
271 272 273 274 275
			}
			return null;
		});
	}

276
	private updateDynamicCSSRules(themeData: ITheme) {
277 278
		let cssRules: string[] = [];
		let hasRule: { [rule: string]: boolean } = {};
279 280 281 282 283 284 285 286 287 288 289 290
		let ruleCollector = {
			addRule: (rule: string) => {
				if (!hasRule[rule]) {
					cssRules.push(rule);
					hasRule[rule] = true;
				}
			}
		};
		themingRegistry.getThemingParticipants().forEach(p => p(themeData, ruleCollector));
		_applyRules(cssRules.join('\n'), colorThemeRulesClassName);
	}

291
	private applyTheme(newTheme: ColorThemeData, settingsTarget: ConfigurationTarget, silent = false): TPromise<IColorTheme> {
292 293 294
		if (this.container) {
			if (this.currentColorTheme) {
				$(this.container).removeClass(this.currentColorTheme.id);
295 296
			} else {
				$(this.container).removeClass(VS_DARK_THEME, VS_LIGHT_THEME, VS_HC_THEME);
297 298 299 300
			}
			$(this.container).addClass(newTheme.id);
		}
		this.currentColorTheme = newTheme;
301 302 303
		if (!this.themingParticipantChangeListener) {
			this.themingParticipantChangeListener = themingRegistry.onThemingParticipantAdded(p => this.updateDynamicCSSRules(this.currentColorTheme));
		}
304

305 306
		this.sendTelemetry(newTheme.id, newTheme.extensionData, 'color');

307 308 309 310 311 312 313
		if (silent) {
			return TPromise.as(null);
		}

		this.onColorThemeChange.fire(this.currentColorTheme);

		if (settingsTarget !== ConfigurationTarget.WORKSPACE) {
J
Joao Moreno 已提交
314
			let background = Color.Format.CSS.formatHex(newTheme.getColor(editorBackground)); // only take RGB, its what is used in the initial CSS
315
			let data = { id: newTheme.id, background: background };
316
			this.broadcastService.broadcast({ channel: 'vscode:changeColorTheme', payload: JSON.stringify(data) });
317 318 319 320 321
		}
		// remember theme data for a quick restore
		this.storageService.store(PERSISTED_THEME_STORAGE_KEY, newTheme.toStorageData());

		return this.writeColorThemeConfiguration(settingsTarget);
322
	}
323

324
	private writeColorThemeConfiguration(settingsTarget: ConfigurationTarget): TPromise<IColorTheme> {
325
		if (!types.isUndefinedOrNull(settingsTarget)) {
326
			return this.configurationWriter.writeConfiguration(COLOR_THEME_SETTING, this.currentColorTheme.settingsId, settingsTarget).then(_ => this.currentColorTheme);
M
Martin Aeschlimann 已提交
327 328
		}
		return TPromise.as(this.currentColorTheme);
M
Martin Aeschlimann 已提交
329 330
	}

K
katainaka0503 已提交
331
	private themeExtensionsActivated = new Map<string, boolean>();
M
Martin Aeschlimann 已提交
332
	private sendTelemetry(themeId: string, themeData: ExtensionData, themeType: string) {
333 334 335
		if (themeData) {
			let key = themeType + themeData.extensionId;
			if (!this.themeExtensionsActivated.get(key)) {
K
kieferrm 已提交
336
				/* __GDPR__
K
kieferrm 已提交
337 338 339 340 341 342 343 344
					"activatePlugin" : {
						"id" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
						"name": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
						"isBuiltin": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
						"publisherDisplayName": { "classification": "PublicPersonalData", "purpose": "FeatureInsight" },
						"themeId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
					}
				*/
345 346 347 348 349 350 351 352 353
				this.telemetryService.publicLog('activatePlugin', {
					id: themeData.extensionId,
					name: themeData.extensionName,
					isBuiltin: themeData.extensionIsBuiltin,
					publisherDisplayName: themeData.extensionPublisher,
					themeId: themeId
				});
				this.themeExtensionsActivated.set(key, true);
			}
354 355
		}
	}
M
Martin Aeschlimann 已提交
356

M
Martin Aeschlimann 已提交
357
	public getFileIconThemes(): TPromise<IFileIconTheme[]> {
358
		return this.iconThemeStore.getFileIconThemes();
M
Martin Aeschlimann 已提交
359 360
	}

361
	public getFileIconTheme() {
M
Martin Aeschlimann 已提交
362
		return this.currentIconTheme;
363 364
	}

M
Martin Aeschlimann 已提交
365
	public setFileIconTheme(iconTheme: string, settingsTarget: ConfigurationTarget): TPromise<IFileIconTheme> {
366
		iconTheme = iconTheme || '';
M
Martin Aeschlimann 已提交
367
		if (iconTheme === this.currentIconTheme.id && this.currentIconTheme.isLoaded) {
M
Martin Aeschlimann 已提交
368
			return this.writeFileIconConfiguration(settingsTarget);
369
		}
370
		let onApply = (newIconTheme: FileIconThemeData) => {
371 372 373
			this.doSetFileIconTheme(newIconTheme);

			// remember theme data for a quick restore
M
Martin Aeschlimann 已提交
374
			if (newIconTheme) {
375
				this.storageService.store(PERSISTED_ICON_THEME_STORAGE_KEY, newIconTheme.toStorageData());
M
Martin Aeschlimann 已提交
376
			} else {
377
				this.storageService.remove(PERSISTED_ICON_THEME_STORAGE_KEY);
M
Martin Aeschlimann 已提交
378
			}
M
Martin Aeschlimann 已提交
379 380

			return this.writeFileIconConfiguration(settingsTarget);
381 382
		};

383
		return this.iconThemeStore.findThemeData(iconTheme).then(iconThemeData => {
M
Martin Aeschlimann 已提交
384 385 386
			return _applyIconTheme(iconThemeData, onApply);
		});
	}
M
Martin Aeschlimann 已提交
387

388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
	private doSetFileIconTheme(iconThemeData: FileIconThemeData): void {
		if (iconThemeData) {
			this.currentIconTheme = iconThemeData;
		} else {
			this.currentIconTheme = FileIconThemeData.noIconTheme();
		}

		if (this.container) {
			if (iconThemeData) {
				$(this.container).addClass(fileIconsEnabledClass);
			} else {
				$(this.container).removeClass(fileIconsEnabledClass);
			}
		}
		if (iconThemeData) {
			this.sendTelemetry(iconThemeData.id, iconThemeData.extensionData, 'fileIcon');
		}
		this.onFileIconThemeChange.fire(this.currentIconTheme);

	}

M
Martin Aeschlimann 已提交
409
	private writeFileIconConfiguration(settingsTarget: ConfigurationTarget): TPromise<IFileIconTheme> {
410
		if (!types.isUndefinedOrNull(settingsTarget)) {
411
			return this.configurationWriter.writeConfiguration(ICON_THEME_SETTING, this.currentIconTheme.settingsId, settingsTarget).then(_ => this.currentIconTheme);
M
Martin Aeschlimann 已提交
412
		}
413
		return TPromise.wrap(this.currentIconTheme);
M
Martin Aeschlimann 已提交
414 415
	}

416 417 418 419
	private get configurationWriter(): ConfigurationWriter {
		// separate out the ConfigurationWriter to avoid a dependency of the IConfigurationEditingService
		if (!this._configurationWriter) {
			this._configurationWriter = this.instantiationService.createInstance(ConfigurationWriter);
420
		}
421
		return this._configurationWriter;
422
	}
E
Erich Gamma 已提交
423 424
}

425
function _applyIconTheme(this: any, data: FileIconThemeData, onApply: (theme: FileIconThemeData) => TPromise<IFileIconTheme>): TPromise<IFileIconTheme> {
426
	if (!data) {
427
		_applyRules('', iconThemeRulesClassName);
428
		return onApply(data);
429
	}
430 431
	return data.ensureLoaded(this).then(styleSheetContent => {
		_applyRules(styleSheetContent, iconThemeRulesClassName);
M
Martin Aeschlimann 已提交
432
		return onApply(data);
M
Martin Aeschlimann 已提交
433
	}, error => {
434
		return TPromise.wrapError<IFileIconTheme>(new Error(nls.localize('error.cannotloadicontheme', "Unable to load {0}", data.path)));
M
Martin Aeschlimann 已提交
435 436 437 438 439
	});
}

function _applyRules(styleSheetContent: string, rulesClassName: string) {
	let themeStyles = document.head.getElementsByClassName(rulesClassName);
E
Erich Gamma 已提交
440
	if (themeStyles.length === 0) {
B
Benjamin Pasero 已提交
441 442
		let elStyle = document.createElement('style');
		elStyle.type = 'text/css';
M
Martin Aeschlimann 已提交
443
		elStyle.className = rulesClassName;
E
Erich Gamma 已提交
444 445 446
		elStyle.innerHTML = styleSheetContent;
		document.head.appendChild(elStyle);
	} else {
B
Benjamin Pasero 已提交
447
		(<HTMLStyleElement>themeStyles[0]).innerHTML = styleSheetContent;
E
Erich Gamma 已提交
448 449 450
	}
}

451 452
colorThemeSchema.register();
fileIconThemeSchema.register();
M
Martin Aeschlimann 已提交
453

454
class ConfigurationWriter {
455
	constructor( @IConfigurationService private configurationService: IConfigurationService) {
456 457
	}

B
Benjamin Pasero 已提交
458
	public writeConfiguration(key: string, value: any, settingsTarget: ConfigurationTarget): TPromise<void> {
459
		let settings = this.configurationService.inspect(key);
460 461 462 463 464 465 466 467 468 469 470 471 472 473
		if (settingsTarget === ConfigurationTarget.USER) {
			if (value === settings.user) {
				return TPromise.as(null); // nothing to do
			} else if (value === settings.default) {
				if (types.isUndefined(settings.user)) {
					return TPromise.as(null); // nothing to do
				}
				value = void 0; // remove configuration from user settings
			}
		} else if (settingsTarget === ConfigurationTarget.WORKSPACE) {
			if (value === settings.value) {
				return TPromise.as(null); // nothing to do
			}
		}
474
		return this.configurationService.updateValue(key, value, settingsTarget);
475 476 477
	}
}

478
// Configuration: Themes
M
Martin Aeschlimann 已提交
479
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
480

481
const colorThemeSettingSchema: IConfigurationPropertySchema = {
482 483
	type: 'string',
	description: nls.localize('colorTheme', "Specifies the color theme used in the workbench."),
484
	default: DEFAULT_THEME_SETTING_VALUE,
485 486
	enum: [],
	enumDescriptions: [],
487 488
	errorMessage: nls.localize('colorThemeError', "Theme is unknown or not installed."),
};
489

490
const iconThemeSettingSchema: IConfigurationPropertySchema = {
491
	type: ['string', 'null'],
492
	default: DEFAULT_ICON_THEME_SETTING_VALUE,
493
	description: nls.localize('iconTheme', "Specifies the icon theme used in the workbench or 'null' to not show any file icons."),
494 495 496 497
	enum: [null],
	enumDescriptions: [nls.localize('noIconThemeDesc', 'No file icons')],
	errorMessage: nls.localize('iconThemeError', "File icon theme is unknown or not installed.")
};
498
const colorCustomizationsSchema: IConfigurationPropertySchema = {
499 500
	type: ['object'],
	description: nls.localize('workbenchColors', "Overrides colors from the currently selected color theme."),
501
	properties: colorThemeSchema.colorsSchema.properties,
M
Martin Aeschlimann 已提交
502
	additionalProperties: false,
503
	default: {},
504 505
	defaultSnippets: [{
		body: {
506 507 508
			'statusBar.background': '#666666',
			'panel.background': '#555555',
			'sideBar.background': '#444444'
509 510
		}
	}]
511 512
};

513
const themeSettingsConfiguration: IConfigurationNode = {
514 515 516 517
	id: 'workbench',
	order: 7.1,
	type: 'object',
	properties: {
518 519
		[COLOR_THEME_SETTING]: colorThemeSettingSchema,
		[ICON_THEME_SETTING]: iconThemeSettingSchema,
520
		[CUSTOM_WORKBENCH_COLORS_SETTING]: colorCustomizationsSchema
M
Martin Aeschlimann 已提交
521
	}
522 523
};
configurationRegistry.registerConfiguration(themeSettingsConfiguration);
524

525 526 527
function tokenGroupSettings(description: string) {
	return {
		description,
528
		default: '#FF0000',
529 530 531
		anyOf: [
			{
				type: 'string',
532
				format: 'color-hex'
533 534 535 536
			},
			colorThemeSchema.tokenColorizationSettingSchema
		]
	};
537
}
538 539 540 541 542 543 544 545

configurationRegistry.registerConfiguration({
	id: 'editor',
	order: 7.2,
	type: 'object',
	properties: {
		[CUSTOM_EDITOR_COLORS_SETTING]: {
			description: nls.localize('editorColors', "Overrides editor colors and font style from the currently selected color theme."),
546
			default: {},
M
Martin Aeschlimann 已提交
547
			additionalProperties: false,
548
			properties: {
549 550 551 552 553 554 555 556
				comments: tokenGroupSettings(nls.localize('editorColors.comments', "Sets the colors and styles for comments")),
				strings: tokenGroupSettings(nls.localize('editorColors.strings', "Sets the colors and styles for strings literals.")),
				keywords: tokenGroupSettings(nls.localize('editorColors.keywords', "Sets the colors and styles for keywords.")),
				numbers: tokenGroupSettings(nls.localize('editorColors.numbers', "Sets the colors and styles for number literals.")),
				types: tokenGroupSettings(nls.localize('editorColors.types', "Sets the colors and styles for type declarations and references.")),
				functions: tokenGroupSettings(nls.localize('editorColors.functions', "Sets the colors and styles for functions declarations and references.")),
				variables: tokenGroupSettings(nls.localize('editorColors.variables', "Sets the colors and styles for variables declarations and references.")),
				[CUSTOM_EDITOR_SCOPE_COLORS_SETTING]: colorThemeSchema.tokenColorsSchema(nls.localize('editorColors.textMateRules', 'Sets colors and styles using textmate theming rules (advanced).'))
557 558 559 560 561
			}
		}
	}
});