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

6
import * as nls from 'vs/nls';
M
Martin Aeschlimann 已提交
7
import * as types from 'vs/base/common/types';
8
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
M
polish  
Martin Aeschlimann 已提交
9
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, IColorCustomizations, CUSTOM_EDITOR_TOKENSTYLES_SETTING, IExperimentalTokenStyleCustomizations } from 'vs/workbench/services/themes/common/workbenchThemeService';
B
Benjamin Pasero 已提交
10
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
J
Johannes Rieken 已提交
11
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
12
import { Registry } from 'vs/platform/registry/common/platform';
13
import * as errors from 'vs/base/common/errors';
14
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
15
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, IConfigurationPropertySchema, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry';
16
import { ColorThemeData } from 'vs/workbench/services/themes/common/colorThemeData';
M
polish  
Martin Aeschlimann 已提交
17
import { ITheme, Extensions as ThemingExtensions, IThemingRegistry, ThemeType, LIGHT, DARK, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService';
M
Matt Bierner 已提交
18
import { Event, Emitter } from 'vs/base/common/event';
19
import { registerFileIconThemeSchemas } from 'vs/workbench/services/themes/common/fileIconThemeSchema';
20
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
21
import { ColorThemeStore } from 'vs/workbench/services/themes/common/colorThemeStore';
22 23
import { FileIconThemeStore } from 'vs/workbench/services/themes/browser/fileIconThemeStore';
import { FileIconThemeData } from 'vs/workbench/services/themes/browser/fileIconThemeData';
24
import { removeClasses, addClasses } from 'vs/base/browser/dom';
25
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
26
import { IFileService, FileChangeType } from 'vs/platform/files/common/files';
M
Martin Aeschlimann 已提交
27 28
import { URI } from 'vs/base/common/uri';
import * as resources from 'vs/base/common/resources';
29
import { IJSONSchema } from 'vs/base/common/jsonSchema';
30
import { textmateColorsSchemaId, registerColorThemeSchemas, textmateColorGroupSchemaId } from 'vs/workbench/services/themes/common/colorThemeSchema';
31
import { workbenchColorsSchemaId } from 'vs/platform/theme/common/colorRegistry';
32
import { tokenStylingSchemaId } from 'vs/platform/theme/common/tokenClassificationRegistry';
33
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
34
import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts';
35
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
36
import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader';
37

M
polish  
Martin Aeschlimann 已提交
38 39 40 41 42
// settings

const PREFERRED_DARK_THEME_SETTING = 'workbench.preferredDarkColorTheme';
const PREFERRED_LIGHT_THEME_SETTING = 'workbench.preferredLightColorTheme';
const PREFERRED_HC_THEME_SETTING = 'workbench.preferredHighContrastColorTheme';
43
const DETECT_COLOR_SCHEME_SETTING = 'window.autoDetectColorScheme';
M
polish  
Martin Aeschlimann 已提交
44 45
const DETECT_HC_SETTING = 'window.autoDetectHighContrast';

E
Erich Gamma 已提交
46 47
// implementation

M
Martin Aeschlimann 已提交
48
const DEFAULT_THEME_ID = 'vs-dark vscode-theme-defaults-themes-dark_plus-json';
49
const DEFAULT_THEME_SETTING_VALUE = 'Default Dark+';
M
Marvin Heilemann 已提交
50 51
const DEFAULT_THEME_DARK_SETTING_VALUE = 'Default Dark+';
const DEFAULT_THEME_LIGHT_SETTING_VALUE = 'Default Light+';
M
polish  
Martin Aeschlimann 已提交
52
const DEFAULT_THEME_HC_SETTING_VALUE = 'Default High Contrast';
M
Marvin Heilemann 已提交
53

54
const PERSISTED_THEME_STORAGE_KEY = 'colorThemeData';
55
const PERSISTED_ICON_THEME_STORAGE_KEY = 'iconThemeData';
M
update  
Martin Aeschlimann 已提交
56
const PERSISTED_OS_COLOR_SCHEME = 'osColorScheme';
57

58 59 60
const defaultThemeExtensionId = 'vscode-theme-defaults';
const oldDefaultThemeExtensionId = 'vscode-theme-colorful-defaults';

61
const DEFAULT_ICON_THEME_SETTING_VALUE = 'vs-seti';
62
const DEFAULT_ICON_THEME_ID = 'vscode.vscode-theme-seti-vs-seti';
63 64
const fileIconsEnabledClass = 'file-icons-enabled';

65 66 67
const colorThemeRulesClassName = 'contributedColorTheme';
const iconThemeRulesClassName = 'contributedIconTheme';

68 69
const themingRegistry = Registry.as<IThemingRegistry>(ThemingExtensions.ThemingContribution);

J
Johannes Rieken 已提交
70
function validateThemeId(theme: string): string {
71 72
	// migrations
	switch (theme) {
73 74 75 76 77
		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`;
		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`;
78 79 80 81
	}
	return theme;
}

82
export class WorkbenchThemeService implements IWorkbenchThemeService {
83
	_serviceBrand: undefined;
E
Erich Gamma 已提交
84

85
	private colorThemeStore: ColorThemeStore;
86
	private currentColorTheme: ColorThemeData;
M
Martin Aeschlimann 已提交
87
	private container: HTMLElement;
M
Matt Bierner 已提交
88
	private readonly onColorThemeChange: Emitter<IColorTheme>;
89
	private watchedColorThemeLocation: URI | undefined;
90
	private watchedColorThemeDisposable: IDisposable | undefined;
M
Martin Aeschlimann 已提交
91

92
	private iconThemeStore: FileIconThemeStore;
M
Martin Aeschlimann 已提交
93
	private currentIconTheme: FileIconThemeData;
M
Matt Bierner 已提交
94
	private readonly onFileIconThemeChange: Emitter<IFileIconTheme>;
95
	private watchedIconThemeLocation: URI | undefined;
96
	private watchedIconThemeDisposable: IDisposable | undefined;
E
Erich Gamma 已提交
97

98
	private themingParticipantChangeListener: IDisposable | undefined;
99

100
	private get colorCustomizations(): IColorCustomizations {
101
		return this.configurationService.getValue<IColorCustomizations>(CUSTOM_WORKBENCH_COLORS_SETTING) || {};
102 103 104
	}

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

108
	private get tokenStylesCustomizations(): IExperimentalTokenStyleCustomizations {
109
		return this.configurationService.getValue<IExperimentalTokenStyleCustomizations>(CUSTOM_EDITOR_TOKENSTYLES_SETTING) || {};
110 111
	}

M
Martin Aeschlimann 已提交
112
	constructor(
113
		@IExtensionService extensionService: IExtensionService,
114 115 116
		@IStorageService private readonly storageService: IStorageService,
		@IConfigurationService private readonly configurationService: IConfigurationService,
		@ITelemetryService private readonly telemetryService: ITelemetryService,
117
		@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
118
		@IFileService private readonly fileService: IFileService,
119
		@IExtensionResourceLoaderService private readonly extensionResourceLoaderService: IExtensionResourceLoaderService,
120
		@IWorkbenchLayoutService readonly layoutService: IWorkbenchLayoutService
121
	) {
122

123
		this.container = layoutService.getWorkbenchContainer();
124
		this.colorThemeStore = new ColorThemeStore(extensionService);
125
		this.onFileIconThemeChange = new Emitter<IFileIconTheme>();
126
		this.iconThemeStore = new FileIconThemeStore(extensionService);
127
		this.onColorThemeChange = new Emitter<IColorTheme>({ leakWarningThreshold: 400 });
128

129
		this.currentColorTheme = ColorThemeData.createUnloadedTheme('');
M
Martin Aeschlimann 已提交
130
		this.currentIconTheme = FileIconThemeData.createUnloadedTheme('');
131

132 133 134
		// 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
135
		let themeData: ColorThemeData | undefined = undefined;
136
		let persistedThemeData = this.storageService.get(PERSISTED_THEME_STORAGE_KEY, StorageScope.GLOBAL);
137
		if (persistedThemeData) {
138
			themeData = ColorThemeData.fromStorageData(persistedThemeData);
139
		}
140
		let containerBaseTheme = this.getBaseThemeFromContainer();
M
Martin Aeschlimann 已提交
141
		if (!themeData || themeData.baseTheme !== containerBaseTheme) {
142
			themeData = ColorThemeData.createUnloadedTheme(containerBaseTheme);
143
		}
144
		themeData.setCustomColors(this.colorCustomizations);
145
		themeData.setCustomTokenColors(this.tokenColorCustomizations);
146
		themeData.setCustomTokenStyleRules(this.tokenStylesCustomizations);
147
		this.updateDynamicCSSRules(themeData);
148
		this.applyTheme(themeData, undefined, true);
E
Erich Gamma 已提交
149

150
		let persistedIconThemeData = this.storageService.get(PERSISTED_ICON_THEME_STORAGE_KEY, StorageScope.GLOBAL);
151
		if (persistedIconThemeData) {
152
			const iconData = FileIconThemeData.fromStorageData(persistedIconThemeData);
153 154 155
			if (iconData) {
				_applyIconTheme(iconData, () => {
					this.doSetFileIconTheme(iconData);
M
Martin Aeschlimann 已提交
156
					return Promise.resolve(iconData);
157 158 159 160
				});
			}
		}

161 162
		this.initialize().then(undefined, errors.onUnexpectedError).then(_ => {
			this.installConfigurationListener();
M
polish  
Martin Aeschlimann 已提交
163
			this.installPreferredSchemeListener();
164
		});
165

166 167
		let prevColorId: string | undefined = undefined;

168
		// update settings schema setting for theme specific settings
169
		this.colorThemeStore.onDidChange(async event => {
170
			// updates enum for the 'workbench.colorTheme` setting
171 172
			colorThemeSettingEnum.splice(0, colorThemeSettingEnum.length, ...event.themes.map(t => t.settingsId));
			colorThemeSettingEnumDescriptions.splice(0, colorThemeSettingEnumDescriptions.length, ...event.themes.map(t => t.description || ''));
173

174 175
			const themeSpecificWorkbenchColors: IJSONSchema = { properties: {} };
			const themeSpecificTokenColors: IJSONSchema = { properties: {} };
176
			const themeSpecificTokenStyling: IJSONSchema = { properties: {} };
A
Alex 已提交
177

178 179
			const workbenchColors = { $ref: workbenchColorsSchemaId, additionalProperties: false };
			const tokenColors = { properties: tokenColorSchema.properties, additionalProperties: false };
180
			const tokenStyling = { $ref: tokenStylingSchemaId, additionalProperties: false };
181
			for (let t of event.themes) {
182
				// add theme specific color customization ("[Abyss]":{ ... })
183
				const themeId = `[${t.settingsId}]`;
184 185
				themeSpecificWorkbenchColors.properties![themeId] = workbenchColors;
				themeSpecificTokenColors.properties![themeId] = tokenColors;
186
				themeSpecificTokenStyling.properties![themeId] = tokenStyling;
187
			}
A
Alex 已提交
188

189 190
			colorCustomizationsSchema.allOf![1] = themeSpecificWorkbenchColors;
			tokenColorCustomizationSchema.allOf![1] = themeSpecificTokenColors;
191
			experimentalTokenStylingCustomizationSchema.allOf![1] = themeSpecificTokenStyling;
192

193
			configurationRegistry.notifyConfigurationSchemaUpdated(themeSettingsConfiguration, tokenColorCustomizationConfiguration);
194

195
			if (this.currentColorTheme.isLoaded) {
196 197 198
				const themeData = await this.colorThemeStore.findThemeData(this.currentColorTheme.id);
				if (!themeData) {
					// current theme is no longer available
199
					prevColorId = this.currentColorTheme.id;
200
					this.setColorTheme(DEFAULT_THEME_ID, 'auto');
201
				} else {
202
					if (this.currentColorTheme.id === DEFAULT_THEME_ID && !types.isUndefined(prevColorId) && await this.colorThemeStore.findThemeData(prevColorId)) {
203 204 205
						// restore color
						this.setColorTheme(prevColorId, 'auto');
						prevColorId = undefined;
206 207
					} else {
						this.reloadCurrentColorTheme();
208
					}
209
				}
210
			}
211
		});
212 213 214 215

		let prevFileIconId: string | undefined = undefined;
		this.iconThemeStore.onDidChange(async event => {
			iconThemeSettingSchema.enum = [null, ...event.themes.map(t => t.settingsId)];
216
			iconThemeSettingSchema.enumDescriptions = [iconThemeSettingSchema.enumDescriptions![0], ...event.themes.map(t => t.description || '')];
217
			configurationRegistry.notifyConfigurationSchemaUpdated(themeSettingsConfiguration);
218 219

			if (this.currentIconTheme.isLoaded) {
220 221 222
				const theme = await this.iconThemeStore.findThemeData(this.currentIconTheme.id);
				if (!theme) {
					// current theme is no longer available
223
					prevFileIconId = this.currentIconTheme.id;
224
					this.setFileIconTheme(DEFAULT_ICON_THEME_ID, 'auto');
225
				} else {
226
					// restore color
227
					if (this.currentIconTheme.id === DEFAULT_ICON_THEME_ID && !types.isUndefined(prevFileIconId) && await this.iconThemeStore.findThemeData(prevFileIconId)) {
228 229
						this.setFileIconTheme(prevFileIconId, 'auto');
						prevFileIconId = undefined;
230 231
					} else {
						this.reloadCurrentFileIconTheme();
232
					}
233
				}
234
			}
235
		});
M
Martin Aeschlimann 已提交
236 237

		this.fileService.onFileChanges(async e => {
238
			if (this.watchedColorThemeLocation && this.currentColorTheme && e.contains(this.watchedColorThemeLocation, FileChangeType.UPDATED)) {
239
				this.reloadCurrentColorTheme();
M
Martin Aeschlimann 已提交
240
			}
241
			if (this.watchedIconThemeLocation && this.currentIconTheme && e.contains(this.watchedIconThemeLocation, FileChangeType.UPDATED)) {
242
				this.reloadCurrentFileIconTheme();
M
Martin Aeschlimann 已提交
243 244
			}
		});
A
Alex Dima 已提交
245 246
	}

M
Martin Aeschlimann 已提交
247
	public get onDidColorThemeChange(): Event<IColorTheme> {
M
Martin Aeschlimann 已提交
248
		return this.onColorThemeChange.event;
E
Erich Gamma 已提交
249 250
	}

M
Martin Aeschlimann 已提交
251 252 253 254
	public get onDidFileIconThemeChange(): Event<IFileIconTheme> {
		return this.onFileIconThemeChange.event;
	}

255 256 257 258
	public get onIconThemeChange(): Event<IFileIconTheme> {
		return this.onFileIconThemeChange.event;
	}

259 260
	public get onThemeChange(): Event<ITheme> {
		return this.onColorThemeChange.event;
261 262
	}

263
	private initialize(): Promise<[IColorTheme | null, IFileIconTheme | null]> {
M
polish  
Martin Aeschlimann 已提交
264 265
		const colorThemeSetting = this.configurationService.getValue<string>(COLOR_THEME_SETTING);
		const iconThemeSetting = this.configurationService.getValue<string | null>(ICON_THEME_SETTING);
266

267
		const extDevLocs = this.environmentService.extensionDevelopmentLocationURI;
M
Marvin Heilemann 已提交
268

M
polish  
Martin Aeschlimann 已提交
269 270 271 272 273 274 275 276
		const initializeColorTheme = async () => {
			if (extDevLocs && extDevLocs.length > 0) { // in dev mode, switch to a theme provided by the extension under dev.
				const devThemes = await this.colorThemeStore.findThemeDataByParentLocation(extDevLocs[0]);
				if (devThemes.length) {
					return this.setColorTheme(devThemes[0].id, ConfigurationTarget.MEMORY);
				}
			}
			let theme = await this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID);
M
update  
Martin Aeschlimann 已提交
277 278 279 280 281

			const persistedColorScheme = this.storageService.get(PERSISTED_OS_COLOR_SCHEME, StorageScope.GLOBAL);
			const preferredColorScheme = this.getPreferredColorScheme();
			if (persistedColorScheme && preferredColorScheme && persistedColorScheme !== preferredColorScheme) {
				return this.applyPreferredColorTheme(preferredColorScheme);
M
polish  
Martin Aeschlimann 已提交
282
			}
M
update  
Martin Aeschlimann 已提交
283
			return this.setColorTheme(theme && theme.id, undefined);
M
polish  
Martin Aeschlimann 已提交
284
		};
M
Marvin Heilemann 已提交
285

M
polish  
Martin Aeschlimann 已提交
286 287 288 289 290 291
		const initializeIconTheme = async () => {
			if (extDevLocs && extDevLocs.length > 0) { // in dev mode, switch to a theme provided by the extension under dev.
				const devThemes = await this.iconThemeStore.findThemeDataByParentLocation(extDevLocs[0]);
				if (devThemes.length) {
					return this.setFileIconTheme(devThemes[0].id, ConfigurationTarget.MEMORY);
				}
M
Marvin Heilemann 已提交
292
			}
M
polish  
Martin Aeschlimann 已提交
293 294 295 296 297
			const theme = await this.iconThemeStore.findThemeBySettingsId(iconThemeSetting);
			return this.setFileIconTheme(theme ? theme.id : DEFAULT_ICON_THEME_ID, undefined);
		};

		return Promise.all([initializeColorTheme(), initializeIconTheme()]);
M
Marvin Heilemann 已提交
298 299
	}

300
	private installConfigurationListener() {
301
		this.configurationService.onDidChangeConfiguration(e => {
302 303 304
			if (e.affectsConfiguration(COLOR_THEME_SETTING)) {
				let colorThemeSetting = this.configurationService.getValue<string>(COLOR_THEME_SETTING);
				if (colorThemeSetting !== this.currentColorTheme.settingsId) {
M
polish  
Martin Aeschlimann 已提交
305
					this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, undefined).then(theme => {
M
Marvin Heilemann 已提交
306 307 308 309
						if (theme) {
							this.setColorTheme(theme.id, undefined);
						}
					});
M
Marvin Heilemann 已提交
310 311
				}
			}
M
polish  
Martin Aeschlimann 已提交
312
			if (e.affectsConfiguration(DETECT_COLOR_SCHEME_SETTING)) {
M
polish  
Martin Aeschlimann 已提交
313
				this.handlePreferredSchemeUpdated();
M
update  
Martin Aeschlimann 已提交
314 315 316 317 318 319 320 321 322
			}
			if (e.affectsConfiguration(PREFERRED_DARK_THEME_SETTING) && this.getPreferredColorScheme() === DARK) {
				this.applyPreferredColorTheme(DARK);
			}
			if (e.affectsConfiguration(PREFERRED_LIGHT_THEME_SETTING) && this.getPreferredColorScheme() === LIGHT) {
				this.applyPreferredColorTheme(LIGHT);
			}
			if (e.affectsConfiguration(PREFERRED_HC_THEME_SETTING) && this.getPreferredColorScheme() === HIGH_CONTRAST) {
				this.applyPreferredColorTheme(HIGH_CONTRAST);
323
			}
324
			if (e.affectsConfiguration(ICON_THEME_SETTING)) {
325
				let iconThemeSetting = this.configurationService.getValue<string | null>(ICON_THEME_SETTING);
326 327
				if (iconThemeSetting !== this.currentIconTheme.settingsId) {
					this.iconThemeStore.findThemeBySettingsId(iconThemeSetting).then(theme => {
328
						this.setFileIconTheme(theme ? theme.id : DEFAULT_ICON_THEME_ID, undefined);
329 330 331 332 333 334 335 336 337 338 339 340 341
					});
				}
			}
			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;
				}
342 343 344 345
				if (e.affectsConfiguration(CUSTOM_EDITOR_TOKENSTYLES_SETTING)) {
					this.currentColorTheme.setCustomTokenStyleRules(this.tokenStylesCustomizations);
					hasColorChanges = true;
				}
346 347 348 349
				if (hasColorChanges) {
					this.updateDynamicCSSRules(this.currentColorTheme);
					this.onColorThemeChange.fire(this.currentColorTheme);
				}
350 351 352 353
			}
		});
	}

M
polish  
Martin Aeschlimann 已提交
354 355 356
	// preferred scheme handling

	private installPreferredSchemeListener() {
M
polish  
Martin Aeschlimann 已提交
357
		window.matchMedia('(prefers-color-scheme: dark)').addListener(async () => this.handlePreferredSchemeUpdated());
M
polish  
Martin Aeschlimann 已提交
358 359
	}

M
polish  
Martin Aeschlimann 已提交
360
	private async handlePreferredSchemeUpdated() {
M
polish  
Martin Aeschlimann 已提交
361
		const scheme = this.getPreferredColorScheme();
M
update  
Martin Aeschlimann 已提交
362 363 364
		this.storageService.store(PERSISTED_OS_COLOR_SCHEME, scheme, StorageScope.GLOBAL);
		if (scheme) {
			return this.applyPreferredColorTheme(scheme);
M
Marvin Heilemann 已提交
365
		}
M
polish  
Martin Aeschlimann 已提交
366
		return undefined;
M
Marvin Heilemann 已提交
367 368
	}

M
polish  
Martin Aeschlimann 已提交
369 370 371 372
	private getPreferredColorScheme(): ThemeType | undefined {
		let detectHCThemeSetting = this.configurationService.getValue<boolean>(DETECT_HC_SETTING);
		if (this.environmentService.configuration.highContrast && detectHCThemeSetting) {
			return HIGH_CONTRAST;
M
Marvin Heilemann 已提交
373
		}
M
polish  
Martin Aeschlimann 已提交
374 375 376 377 378 379 380 381 382 383
		if (this.configurationService.getValue<boolean>(DETECT_COLOR_SCHEME_SETTING)) {
			if (window.matchMedia(`(prefers-color-scheme: light)`).matches) {
				return LIGHT;
			} else if (window.matchMedia(`(prefers-color-scheme: dark)`).matches) {
				return DARK;
			}
		}
		return undefined;
	}

M
update  
Martin Aeschlimann 已提交
384
	private async applyPreferredColorTheme(type: ThemeType): Promise<IColorTheme | null> {
M
polish  
Martin Aeschlimann 已提交
385 386 387
		const settingId = type === DARK ? PREFERRED_DARK_THEME_SETTING : type === LIGHT ? PREFERRED_LIGHT_THEME_SETTING : PREFERRED_HC_THEME_SETTING;
		const themeSettingId = this.configurationService.getValue<string>(settingId);
		if (themeSettingId) {
M
update  
Martin Aeschlimann 已提交
388 389 390 391
			const theme = await this.colorThemeStore.findThemeDataBySettingsId(themeSettingId, undefined);
			if (theme) {
				return this.setColorTheme(theme.id, 'auto');
			}
M
polish  
Martin Aeschlimann 已提交
392
		}
M
polish  
Martin Aeschlimann 已提交
393
		return null;
M
Marvin Heilemann 已提交
394 395
	}

396 397 398 399
	public getColorTheme(): IColorTheme {
		return this.currentColorTheme;
	}

400 401
	public getColorThemes(): Promise<IColorTheme[]> {
		return this.colorThemeStore.getColorThemes();
402 403
	}

404 405 406 407
	public getTheme(): ITheme {
		return this.getColorTheme();
	}

408
	public setColorTheme(themeId: string | undefined, settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise<IColorTheme | null> {
M
Martin Aeschlimann 已提交
409
		if (!themeId) {
M
Martin Aeschlimann 已提交
410
			return Promise.resolve(null);
M
Martin Aeschlimann 已提交
411
		}
M
Martin Aeschlimann 已提交
412
		if (themeId === this.currentColorTheme.id && this.currentColorTheme.isLoaded) {
413
			return this.writeColorThemeConfiguration(settingsTarget);
M
Martin Aeschlimann 已提交
414 415 416 417
		}

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

M
Marvin Heilemann 已提交
418 419
		return this.colorThemeStore.findThemeData(themeId, DEFAULT_THEME_ID).then(themeData => {
			if (!themeData) {
420 421
				return null;
			}
422 423 424 425 426
			return themeData.ensureLoaded(this.extensionResourceLoaderService).then(_ => {
				if (themeId === this.currentColorTheme.id && !this.currentColorTheme.isLoaded && this.currentColorTheme.hasEqualData(themeData)) {
					this.currentColorTheme.clearCaches();
					// the loaded theme is identical to the perisisted theme. Don't need to send an event.
					this.currentColorTheme = themeData;
427
					themeData.setCustomColors(this.colorCustomizations);
C
Cody Hoover 已提交
428
					themeData.setCustomTokenColors(this.tokenColorCustomizations);
429
					themeData.setCustomTokenStyleRules(this.tokenStylesCustomizations);
430
					return Promise.resolve(themeData);
431
				}
432 433 434 435 436 437 438 439
				themeData.setCustomColors(this.colorCustomizations);
				themeData.setCustomTokenColors(this.tokenColorCustomizations);
				themeData.setCustomTokenStyleRules(this.tokenStylesCustomizations);
				this.updateDynamicCSSRules(themeData);
				return this.applyTheme(themeData, settingsTarget);
			}, error => {
				return Promise.reject(new Error(nls.localize('error.cannotloadtheme', "Unable to load {0}: {1}", themeData.location!.toString(), error.message)));
			});
M
Martin Aeschlimann 已提交
440 441 442
		});
	}

443
	private async reloadCurrentColorTheme() {
444
		await this.currentColorTheme.reload(this.extensionResourceLoaderService);
445 446
		this.currentColorTheme.setCustomColors(this.colorCustomizations);
		this.currentColorTheme.setCustomTokenColors(this.tokenColorCustomizations);
447
		this.currentColorTheme.setCustomTokenStyleRules(this.tokenStylesCustomizations);
448 449 450 451
		this.updateDynamicCSSRules(this.currentColorTheme);
		this.applyTheme(this.currentColorTheme, undefined, false);
	}

452 453 454
	public restoreColorTheme() {
		let colorThemeSetting = this.configurationService.getValue<string>(COLOR_THEME_SETTING);
		if (colorThemeSetting !== this.currentColorTheme.settingsId) {
M
polish  
Martin Aeschlimann 已提交
455
			this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, undefined).then(theme => {
M
Marvin Heilemann 已提交
456 457 458 459
				if (theme) {
					this.setColorTheme(theme.id, undefined);
				}
			});
460 461 462
		}
	}

463
	private updateDynamicCSSRules(themeData: ITheme) {
464 465
		const cssRules = new Set<string>();
		const ruleCollector = {
466
			addRule: (rule: string) => {
467 468
				if (!cssRules.has(rule)) {
					cssRules.add(rule);
469 470 471
				}
			}
		};
472
		themingRegistry.getThemingParticipants().forEach(p => p(themeData, ruleCollector, this.environmentService));
473
		_applyRules([...cssRules].join('\n'), colorThemeRulesClassName);
474 475
	}

476
	private applyTheme(newTheme: ColorThemeData, settingsTarget: ConfigurationTarget | undefined | 'auto', silent = false): Promise<IColorTheme | null> {
477
		if (this.currentColorTheme.id) {
478 479 480
			removeClasses(this.container, this.currentColorTheme.id);
		} else {
			removeClasses(this.container, VS_DARK_THEME, VS_LIGHT_THEME, VS_HC_THEME);
481
		}
482 483
		addClasses(this.container, newTheme.id);

484
		this.currentColorTheme.clearCaches();
485
		this.currentColorTheme = newTheme;
486
		if (!this.themingParticipantChangeListener) {
487
			this.themingParticipantChangeListener = themingRegistry.onThemingParticipantAdded(_ => this.updateDynamicCSSRules(this.currentColorTheme));
M
Martin Aeschlimann 已提交
488 489 490
		}

		if (this.fileService && !resources.isEqual(newTheme.location, this.watchedColorThemeLocation)) {
B
Benjamin Pasero 已提交
491
			dispose(this.watchedColorThemeDisposable);
492 493
			this.watchedColorThemeLocation = undefined;

494 495
			if (newTheme.location && (newTheme.watch || !!this.environmentService.extensionDevelopmentLocationURI)) {
				this.watchedColorThemeLocation = newTheme.location;
496
				this.watchedColorThemeDisposable = this.fileService.watch(newTheme.location);
M
Martin Aeschlimann 已提交
497
			}
498
		}
499

500 501
		this.sendTelemetry(newTheme.id, newTheme.extensionData, 'color');

502
		if (silent) {
M
Martin Aeschlimann 已提交
503
			return Promise.resolve(null);
504 505 506 507 508
		}

		this.onColorThemeChange.fire(this.currentColorTheme);

		// remember theme data for a quick restore
509 510 511
		if (newTheme.isLoaded) {
			this.storageService.store(PERSISTED_THEME_STORAGE_KEY, newTheme.toStorageData(), StorageScope.GLOBAL);
		}
512 513

		return this.writeColorThemeConfiguration(settingsTarget);
514
	}
515

516
	private writeColorThemeConfiguration(settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise<IColorTheme> {
517
		if (!types.isUndefinedOrNull(settingsTarget)) {
518
			return this.writeConfiguration(COLOR_THEME_SETTING, this.currentColorTheme.settingsId, settingsTarget).then(_ => this.currentColorTheme);
M
Martin Aeschlimann 已提交
519
		}
M
Martin Aeschlimann 已提交
520
		return Promise.resolve(this.currentColorTheme);
M
Martin Aeschlimann 已提交
521 522
	}

K
katainaka0503 已提交
523
	private themeExtensionsActivated = new Map<string, boolean>();
524
	private sendTelemetry(themeId: string, themeData: ExtensionData | undefined, themeType: string) {
525 526 527
		if (themeData) {
			let key = themeType + themeData.extensionId;
			if (!this.themeExtensionsActivated.get(key)) {
528
				type ActivatePluginClassification = {
529 530 531 532 533
					id: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' };
					name: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' };
					isBuiltin: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
					publisherDisplayName: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
					themeId: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' };
534 535 536 537 538 539 540 541 542
				};
				type ActivatePluginEvent = {
					id: string;
					name: string;
					isBuiltin: boolean;
					publisherDisplayName: string;
					themeId: string;
				};
				this.telemetryService.publicLog2<ActivatePluginEvent, ActivatePluginClassification>('activatePlugin', {
543 544 545
					id: themeData.extensionId,
					name: themeData.extensionName,
					isBuiltin: themeData.extensionIsBuiltin,
546
					publisherDisplayName: themeData.extensionPublisher,
547 548 549 550
					themeId: themeId
				});
				this.themeExtensionsActivated.set(key, true);
			}
551 552
		}
	}
M
Martin Aeschlimann 已提交
553

554 555
	public getFileIconThemes(): Promise<IFileIconTheme[]> {
		return this.iconThemeStore.getFileIconThemes();
M
Martin Aeschlimann 已提交
556 557
	}

558
	public getFileIconTheme() {
M
Martin Aeschlimann 已提交
559
		return this.currentIconTheme;
560 561
	}

562 563 564 565
	public getIconTheme() {
		return this.currentIconTheme;
	}

566
	public setFileIconTheme(iconTheme: string | undefined, settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise<IFileIconTheme> {
567
		iconTheme = iconTheme || '';
M
Martin Aeschlimann 已提交
568
		if (iconTheme === this.currentIconTheme.id && this.currentIconTheme.isLoaded) {
M
Martin Aeschlimann 已提交
569
			return this.writeFileIconConfiguration(settingsTarget);
570
		}
571
		let onApply = (newIconTheme: FileIconThemeData) => {
572 573 574
			this.doSetFileIconTheme(newIconTheme);

			// remember theme data for a quick restore
B
Benjamin Pasero 已提交
575
			if (newIconTheme.isLoaded && (!newIconTheme.location || !getRemoteAuthority(newIconTheme.location))) {
576 577
				this.storageService.store(PERSISTED_ICON_THEME_STORAGE_KEY, newIconTheme.toStorageData(), StorageScope.GLOBAL);
			}
M
Martin Aeschlimann 已提交
578 579

			return this.writeFileIconConfiguration(settingsTarget);
580 581
		};

582 583
		return this.iconThemeStore.findThemeData(iconTheme).then(data => {
			const iconThemeData = data || FileIconThemeData.noIconTheme();
A
Alex Dima 已提交
584
			return iconThemeData.ensureLoaded(this.fileService).then(_ => {
585 586
				return _applyIconTheme(iconThemeData, onApply);
			});
M
Martin Aeschlimann 已提交
587 588
		});
	}
M
Martin Aeschlimann 已提交
589

590 591 592 593 594 595 596 597
	private async reloadCurrentFileIconTheme() {
		await this.currentIconTheme.reload(this.fileService);
		_applyIconTheme(this.currentIconTheme, () => {
			this.doSetFileIconTheme(this.currentIconTheme);
			return Promise.resolve(this.currentIconTheme);
		});
	}

598 599 600 601 602 603 604 605 606 607 608
	public restoreFileIconTheme() {
		let fileIconThemeSetting = this.configurationService.getValue<string | null>(ICON_THEME_SETTING);
		if (fileIconThemeSetting !== this.currentIconTheme.settingsId) {
			this.iconThemeStore.findThemeBySettingsId(fileIconThemeSetting).then(theme => {
				if (theme) {
					this.setFileIconTheme(theme.id, undefined);
				}
			});
		}
	}

609
	private doSetFileIconTheme(iconThemeData: FileIconThemeData): void {
610
		this.currentIconTheme = iconThemeData;
611

612 613 614 615
		if (iconThemeData.id) {
			addClasses(this.container, fileIconsEnabledClass);
		} else {
			removeClasses(this.container, fileIconsEnabledClass);
616
		}
M
Martin Aeschlimann 已提交
617 618

		if (this.fileService && !resources.isEqual(iconThemeData.location, this.watchedIconThemeLocation)) {
B
Benjamin Pasero 已提交
619
			dispose(this.watchedIconThemeDisposable);
620 621
			this.watchedIconThemeLocation = undefined;

622
			if (iconThemeData.location && (iconThemeData.watch || !!this.environmentService.extensionDevelopmentLocationURI)) {
623
				this.watchedIconThemeLocation = iconThemeData.location;
624
				this.watchedIconThemeDisposable = this.fileService.watch(iconThemeData.location);
M
Martin Aeschlimann 已提交
625 626 627
			}
		}

628
		if (iconThemeData.id) {
629 630 631
			this.sendTelemetry(iconThemeData.id, iconThemeData.extensionData, 'fileIcon');
		}
		this.onFileIconThemeChange.fire(this.currentIconTheme);
632

633 634
	}

635
	private writeFileIconConfiguration(settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise<IFileIconTheme> {
636
		if (!types.isUndefinedOrNull(settingsTarget)) {
637
			return this.writeConfiguration(ICON_THEME_SETTING, this.currentIconTheme.settingsId, settingsTarget).then(_ => this.currentIconTheme);
M
Martin Aeschlimann 已提交
638
		}
M
Martin Aeschlimann 已提交
639
		return Promise.resolve(this.currentIconTheme);
M
Martin Aeschlimann 已提交
640 641
	}

642 643 644
	public writeConfiguration(key: string, value: any, settingsTarget: ConfigurationTarget | 'auto'): Promise<void> {
		let settings = this.configurationService.inspect(key);
		if (settingsTarget === 'auto') {
S
rename  
Sandeep Somavarapu 已提交
645
			if (!types.isUndefined(settings.workspaceFolderValue)) {
646
				settingsTarget = ConfigurationTarget.WORKSPACE_FOLDER;
S
rename  
Sandeep Somavarapu 已提交
647
			} else if (!types.isUndefined(settings.workspaceValue)) {
648 649 650 651
				settingsTarget = ConfigurationTarget.WORKSPACE;
			} else {
				settingsTarget = ConfigurationTarget.USER;
			}
652
		}
653 654

		if (settingsTarget === ConfigurationTarget.USER) {
S
rename  
Sandeep Somavarapu 已提交
655
			if (value === settings.userValue) {
656
				return Promise.resolve(undefined); // nothing to do
S
rename  
Sandeep Somavarapu 已提交
657 658
			} else if (value === settings.defaultValue) {
				if (types.isUndefined(settings.userValue)) {
659 660 661 662
					return Promise.resolve(undefined); // nothing to do
				}
				value = undefined; // remove configuration from user settings
			}
663
		} else if (settingsTarget === ConfigurationTarget.WORKSPACE || settingsTarget === ConfigurationTarget.WORKSPACE_FOLDER) {
664 665 666 667 668
			if (value === settings.value) {
				return Promise.resolve(undefined); // nothing to do
			}
		}
		return this.configurationService.updateValue(key, value, settingsTarget);
669
	}
670 671

	private getBaseThemeFromContainer() {
672 673 674 675
		for (let i = this.container.classList.length - 1; i >= 0; i--) {
			const item = this.container.classList.item(i);
			if (item === VS_LIGHT_THEME || item === VS_DARK_THEME || item === VS_HC_THEME) {
				return item;
676 677 678 679
			}
		}
		return VS_DARK_THEME;
	}
E
Erich Gamma 已提交
680 681
}

682
function _applyIconTheme(data: FileIconThemeData, onApply: (theme: FileIconThemeData) => Promise<IFileIconTheme>): Promise<IFileIconTheme> {
683
	_applyRules(data.styleSheetContent!, iconThemeRulesClassName);
684
	return onApply(data);
M
Martin Aeschlimann 已提交
685 686 687 688
}

function _applyRules(styleSheetContent: string, rulesClassName: string) {
	let themeStyles = document.head.getElementsByClassName(rulesClassName);
E
Erich Gamma 已提交
689
	if (themeStyles.length === 0) {
B
Benjamin Pasero 已提交
690 691
		let elStyle = document.createElement('style');
		elStyle.type = 'text/css';
M
Martin Aeschlimann 已提交
692
		elStyle.className = rulesClassName;
E
Erich Gamma 已提交
693 694 695
		elStyle.innerHTML = styleSheetContent;
		document.head.appendChild(elStyle);
	} else {
B
Benjamin Pasero 已提交
696
		(<HTMLStyleElement>themeStyles[0]).innerHTML = styleSheetContent;
E
Erich Gamma 已提交
697 698 699
	}
}

700 701
registerColorThemeSchemas();
registerFileIconThemeSchemas();
M
Martin Aeschlimann 已提交
702

703
// Configuration: Themes
M
Martin Aeschlimann 已提交
704
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
705

706 707 708
const colorThemeSettingEnum: string[] = [];
const colorThemeSettingEnumDescriptions: string[] = [];

709
const colorThemeSettingSchema: IConfigurationPropertySchema = {
710
	type: 'string',
711
	description: nls.localize('colorTheme', "Specifies the color theme used in the workbench."),
712
	default: DEFAULT_THEME_SETTING_VALUE,
713 714
	enum: colorThemeSettingEnum,
	enumDescriptions: colorThemeSettingEnumDescriptions,
715
	errorMessage: nls.localize('colorThemeError', "Theme is unknown or not installed."),
716
};
M
polish  
Martin Aeschlimann 已提交
717
const preferredDarkThemeSettingSchema: IConfigurationPropertySchema = {
M
Marvin Heilemann 已提交
718
	type: 'string',
M
polish  
Martin Aeschlimann 已提交
719
	description: nls.localize('preferredDarkColorTheme', 'Specifies the preferred color theme for dark OS appearance when \'{0}\' is enabled.', DETECT_COLOR_SCHEME_SETTING),
M
Marvin Heilemann 已提交
720
	default: DEFAULT_THEME_DARK_SETTING_VALUE,
721 722 723
	enum: colorThemeSettingEnum,
	enumDescriptions: colorThemeSettingEnumDescriptions,
	errorMessage: nls.localize('colorThemeError', "Theme is unknown or not installed."),
M
Marvin Heilemann 已提交
724
};
M
polish  
Martin Aeschlimann 已提交
725
const preferredLightThemeSettingSchema: IConfigurationPropertySchema = {
M
Marvin Heilemann 已提交
726
	type: 'string',
M
polish  
Martin Aeschlimann 已提交
727
	description: nls.localize('preferredLightColorTheme', 'Specifies the preferred color theme for light OS appearance when \'{0}\' is enabled.', DETECT_COLOR_SCHEME_SETTING),
M
Marvin Heilemann 已提交
728
	default: DEFAULT_THEME_LIGHT_SETTING_VALUE,
729 730 731
	enum: colorThemeSettingEnum,
	enumDescriptions: colorThemeSettingEnumDescriptions,
	errorMessage: nls.localize('colorThemeError', "Theme is unknown or not installed."),
M
Marvin Heilemann 已提交
732
};
M
polish  
Martin Aeschlimann 已提交
733 734 735 736
const preferredHCThemeSettingSchema: IConfigurationPropertySchema = {
	type: 'string',
	description: nls.localize('preferredHCColorTheme', 'Specifies the preferred color theme used in high contrast mode when \'{0}\' is enabled.', DETECT_HC_SETTING),
	default: DEFAULT_THEME_HC_SETTING_VALUE,
737 738 739
	enum: colorThemeSettingEnum,
	enumDescriptions: colorThemeSettingEnumDescriptions,
	errorMessage: nls.localize('colorThemeError', "Theme is unknown or not installed."),
M
polish  
Martin Aeschlimann 已提交
740 741
};
const detectColorSchemeSettingSchema: IConfigurationPropertySchema = {
M
Marvin Heilemann 已提交
742
	type: 'boolean',
M
update  
Martin Aeschlimann 已提交
743
	description: nls.localize('detectColorScheme', 'If set, automatically switch to the preferred color theme based on the OS appearance.'),
744
	default: false
M
Marvin Heilemann 已提交
745
};
746

747
const iconThemeSettingSchema: IConfigurationPropertySchema = {
748
	type: ['string', 'null'],
749
	default: DEFAULT_ICON_THEME_SETTING_VALUE,
750
	description: nls.localize('iconTheme', "Specifies the icon theme used in the workbench or 'null' to not show any file icons."),
751 752
	enum: [null],
	enumDescriptions: [nls.localize('noIconThemeDesc', 'No file icons')],
753
	errorMessage: nls.localize('iconThemeError', "File icon theme is unknown or not installed.")
754
};
755
const colorCustomizationsSchema: IConfigurationPropertySchema = {
756
	type: 'object',
757
	description: nls.localize('workbenchColors', "Overrides colors from the currently selected color theme."),
758
	allOf: [{ $ref: workbenchColorsSchemaId }],
759
	default: {},
760 761
	defaultSnippets: [{
		body: {
762
		}
763
	}]
764 765
};

766
const themeSettingsConfiguration: IConfigurationNode = {
767 768 769 770
	id: 'workbench',
	order: 7.1,
	type: 'object',
	properties: {
771
		[COLOR_THEME_SETTING]: colorThemeSettingSchema,
M
polish  
Martin Aeschlimann 已提交
772 773 774 775
		[PREFERRED_DARK_THEME_SETTING]: preferredDarkThemeSettingSchema,
		[PREFERRED_LIGHT_THEME_SETTING]: preferredLightThemeSettingSchema,
		[PREFERRED_HC_THEME_SETTING]: preferredHCThemeSettingSchema,
		[DETECT_COLOR_SCHEME_SETTING]: detectColorSchemeSettingSchema,
776
		[ICON_THEME_SETTING]: iconThemeSettingSchema,
777
		[CUSTOM_WORKBENCH_COLORS_SETTING]: colorCustomizationsSchema
M
Martin Aeschlimann 已提交
778
	}
779 780
};
configurationRegistry.registerConfiguration(themeSettingsConfiguration);
781

782
function tokenGroupSettings(description: string): IJSONSchema {
783 784
	return {
		description,
785
		$ref: textmateColorGroupSchemaId
786
	};
787
}
788

789 790
const tokenColorSchema: IJSONSchema = {
	properties: {
791 792 793 794 795 796 797
		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.")),
798
		textMateRules: {
799
			description: nls.localize('editorColors.textMateRules', 'Sets colors and styles using textmate theming rules (advanced).'),
800 801 802
			$ref: textmateColorsSchemaId
		}
	}
803
};
804
const tokenColorCustomizationSchema: IConfigurationPropertySchema = {
805
	description: nls.localize('editorColors', "Overrides editor colors and font style from the currently selected color theme."),
806
	default: {},
807
	allOf: [tokenColorSchema]
808
};
809
const experimentalTokenStylingCustomizationSchema: IConfigurationPropertySchema = {
810
	description: nls.localize('editorColorsTokenStyles', "Overrides token color and styles from the currently selected color theme."),
811 812 813
	default: {},
	allOf: [{ $ref: tokenStylingSchemaId }]
};
814
const tokenColorCustomizationConfiguration: IConfigurationNode = {
815 816 817 818
	id: 'editor',
	order: 7.2,
	type: 'object',
	properties: {
819 820
		[CUSTOM_EDITOR_COLORS_SETTING]: tokenColorCustomizationSchema,
		[CUSTOM_EDITOR_TOKENSTYLES_SETTING]: experimentalTokenStylingCustomizationSchema
821
	}
822
};
823
configurationRegistry.registerConfiguration(tokenColorCustomizationConfiguration);
824

M
polish  
Martin Aeschlimann 已提交
825
registerSingleton(IWorkbenchThemeService, WorkbenchThemeService);