diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index 31bd9419b2a..1742e39a855 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import * as types from 'vs/base/common/types'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IWorkbenchThemeService, IWorkbenchColorTheme, ITokenColorCustomizations, IWorkbenchFileIconTheme, ExtensionData, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME, ThemeSettings, IColorCustomizations, IExperimentalTokenStyleCustomizations } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IWorkbenchThemeService, IWorkbenchColorTheme, IWorkbenchFileIconTheme, ExtensionData, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME, ThemeSettings } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -29,14 +29,12 @@ import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; import { ThemeRegistry, registerColorThemeExtensionPoint, registerFileIconThemeExtensionPoint } from 'vs/workbench/services/themes/common/themeExtensionPoints'; -import { updateColorThemeConfigurationSchemas, updateFileIconThemeConfigurationSchemas } from 'vs/workbench/services/themes/common/themeConfiguration'; - +import { updateColorThemeConfigurationSchemas, updateFileIconThemeConfigurationSchemas, ThemeConfiguration } from 'vs/workbench/services/themes/common/themeConfiguration'; // implementation const DEFAULT_THEME_ID = 'vs-dark vscode-theme-defaults-themes-dark_plus-json'; - const PERSISTED_THEME_STORAGE_KEY = 'colorThemeData'; const PERSISTED_ICON_THEME_STORAGE_KEY = 'iconThemeData'; const PERSISTED_OS_COLOR_SCHEME = 'osColorScheme'; @@ -70,32 +68,19 @@ const fileIconThemesExtPoint = registerFileIconThemeExtensionPoint(); export class WorkbenchThemeService implements IWorkbenchThemeService { _serviceBrand: undefined; - private colorThemeRegistry: ThemeRegistry; + private readonly colorThemeRegistry: ThemeRegistry; private currentColorTheme: ColorThemeData; - private container: HTMLElement; + private readonly container: HTMLElement; private readonly onColorThemeChange: Emitter; - private watchedColorThemeLocation: URI | undefined; - private watchedColorThemeDisposable: IDisposable | undefined; + private readonly colorThemeWatcher: ThemeFileWatcher; + private colorThemingParticipantChangeListener: IDisposable | undefined; - private iconThemeRegistry: ThemeRegistry; + private readonly fileIconThemeRegistry: ThemeRegistry; private currentFileIconTheme: FileIconThemeData; private readonly onFileIconThemeChange: Emitter; - private watchedIconThemeLocation: URI | undefined; - private watchedIconThemeDisposable: IDisposable | undefined; + private readonly fileIconThemeWatcher: ThemeFileWatcher; - private themingParticipantChangeListener: IDisposable | undefined; - - private get colorCustomizations(): IColorCustomizations { - return this.configurationService.getValue(ThemeSettings.COLOR_CUSTOMIZATIONS) || {}; - } - - private get tokenColorCustomizations(): ITokenColorCustomizations { - return this.configurationService.getValue(ThemeSettings.TOKEN_COLOR_CUSTOMIZATIONS) || {}; - } - - private get tokenStylesCustomizations(): IExperimentalTokenStyleCustomizations { - return this.configurationService.getValue(ThemeSettings.TOKEN_COLOR_CUSTOMIZATIONS_EXPERIMENTAL) || {}; - } + private settings: ThemeConfiguration; constructor( @IExtensionService extensionService: IExtensionService, @@ -107,14 +92,17 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { @IExtensionResourceLoaderService private readonly extensionResourceLoaderService: IExtensionResourceLoaderService, @IWorkbenchLayoutService readonly layoutService: IWorkbenchLayoutService ) { - this.container = layoutService.getWorkbenchContainer(); - this.colorThemeRegistry = new ThemeRegistry(extensionService, colorThemesExtPoint, ColorThemeData.fromExtensionTheme); - this.onFileIconThemeChange = new Emitter(); - this.iconThemeRegistry = new ThemeRegistry(extensionService, fileIconThemesExtPoint, FileIconThemeData.fromExtensionTheme, true); - this.onColorThemeChange = new Emitter({ leakWarningThreshold: 400 }); + this.settings = new ThemeConfiguration(configurationService); + this.colorThemeRegistry = new ThemeRegistry(extensionService, colorThemesExtPoint, ColorThemeData.fromExtensionTheme); + this.colorThemeWatcher = new ThemeFileWatcher(fileService, environmentService, () => this.reloadCurrentColorTheme()); + this.onColorThemeChange = new Emitter({ leakWarningThreshold: 400 }); this.currentColorTheme = ColorThemeData.createUnloadedTheme(''); + + this.fileIconThemeWatcher = new ThemeFileWatcher(fileService, environmentService, () => this.reloadCurrentFileIconTheme()); + this.fileIconThemeRegistry = new ThemeRegistry(extensionService, fileIconThemesExtPoint, FileIconThemeData.fromExtensionTheme, true); + this.onFileIconThemeChange = new Emitter(); this.currentFileIconTheme = FileIconThemeData.createUnloadedTheme(''); // In order to avoid paint flashing for tokens, because @@ -125,17 +113,17 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (persistedThemeData) { themeData = ColorThemeData.fromStorageData(persistedThemeData); } - let containerBaseTheme = this.getBaseThemeFromContainer(); + const containerBaseTheme = this.getBaseThemeFromContainer(); if (!themeData || themeData.baseTheme !== containerBaseTheme) { themeData = ColorThemeData.createUnloadedTheme(containerBaseTheme); } - themeData.setCustomColors(this.colorCustomizations); - themeData.setCustomTokenColors(this.tokenColorCustomizations); - themeData.setCustomTokenStyleRules(this.tokenStylesCustomizations); + themeData.setCustomColors(this.settings.colorCustomizations); + themeData.setCustomTokenColors(this.settings.tokenColorCustomizations); + themeData.setCustomTokenStyleRules(this.settings.tokenStylesCustomizations); this.updateDynamicCSSRules(themeData); this.applyTheme(themeData, undefined, true); - let persistedIconThemeData = this.storageService.get(PERSISTED_ICON_THEME_STORAGE_KEY, StorageScope.GLOBAL); + const persistedIconThemeData = this.storageService.get(PERSISTED_ICON_THEME_STORAGE_KEY, StorageScope.GLOBAL); if (persistedIconThemeData) { const iconData = FileIconThemeData.fromStorageData(persistedIconThemeData); if (iconData) { @@ -157,7 +145,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.colorThemeRegistry.onDidChange(async event => { updateColorThemeConfigurationSchemas(event.themes); - let colorThemeSetting = this.configurationService.getValue(ThemeSettings.COLOR_THEME); + const colorThemeSetting = this.settings.colorTheme; if (colorThemeSetting !== this.currentColorTheme.settingsId) { const theme = await this.colorThemeRegistry.findThemeBySettingsId(colorThemeSetting, undefined); if (theme) { @@ -185,10 +173,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { }); let prevFileIconId: string | undefined = undefined; - this.iconThemeRegistry.onDidChange(async event => { + this.fileIconThemeRegistry.onDidChange(async event => { updateFileIconThemeConfigurationSchemas(event.themes); - let iconThemeSetting = this.configurationService.getValue(ThemeSettings.ICON_THEME); + const iconThemeSetting = this.settings.fileIconTheme; if (iconThemeSetting !== this.currentFileIconTheme.settingsId) { const theme = await this.findFileIconThemeBySettingId(iconThemeSetting); if (theme) { @@ -214,15 +202,6 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } } }); - - this.fileService.onDidFilesChange(async e => { - if (this.watchedColorThemeLocation && this.currentColorTheme && e.contains(this.watchedColorThemeLocation, FileChangeType.UPDATED)) { - this.reloadCurrentColorTheme(); - } - if (this.watchedIconThemeLocation && this.currentFileIconTheme && e.contains(this.watchedIconThemeLocation, FileChangeType.UPDATED)) { - this.reloadCurrentFileIconTheme(); - } - }); } public get onDidColorThemeChange(): Event { @@ -234,9 +213,6 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } private initialize(): Promise<[IWorkbenchColorTheme | null, IWorkbenchFileIconTheme | null]> { - const colorThemeSetting = this.configurationService.getValue(ThemeSettings.COLOR_THEME); - const iconThemeSetting = this.configurationService.getValue(ThemeSettings.ICON_THEME); - const extDevLocs = this.environmentService.extensionDevelopmentLocationURI; const initializeColorTheme = async () => { @@ -246,7 +222,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.setColorTheme(devThemes[0].id, ConfigurationTarget.MEMORY); } } - let theme = await this.colorThemeRegistry.findThemeBySettingsId(colorThemeSetting, DEFAULT_THEME_ID); + const theme = await this.colorThemeRegistry.findThemeBySettingsId(this.settings.colorTheme, DEFAULT_THEME_ID); const persistedColorScheme = this.storageService.get(PERSISTED_OS_COLOR_SCHEME, StorageScope.GLOBAL); const preferredColorScheme = this.getPreferredColorScheme(); @@ -258,12 +234,12 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { const initializeIconTheme = async () => { if (extDevLocs && extDevLocs.length === 1) { // in dev mode, switch to a theme provided by the extension under dev. - const devThemes = await this.iconThemeRegistry.findThemeByExtensionLocation(extDevLocs[0]); + const devThemes = await this.fileIconThemeRegistry.findThemeByExtensionLocation(extDevLocs[0]); if (devThemes.length) { return this.setFileIconTheme(devThemes[0].id, ConfigurationTarget.MEMORY); } } - const theme = await this.findFileIconThemeBySettingId(iconThemeSetting); + const theme = await this.findFileIconThemeBySettingId(this.settings.fileIconTheme); return this.setFileIconTheme(theme ? theme.id : DEFAULT_ICON_THEME_ID, undefined); }; @@ -273,7 +249,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { private installConfigurationListener() { this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(ThemeSettings.COLOR_THEME)) { - let colorThemeSetting = this.configurationService.getValue(ThemeSettings.COLOR_THEME); + const colorThemeSetting = this.settings.colorTheme; if (colorThemeSetting !== this.currentColorTheme.settingsId) { this.colorThemeRegistry.findThemeBySettingsId(colorThemeSetting, undefined).then(theme => { if (theme) { @@ -295,7 +271,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.applyPreferredColorTheme(HIGH_CONTRAST); } if (e.affectsConfiguration(ThemeSettings.ICON_THEME)) { - let iconThemeSetting = this.configurationService.getValue(ThemeSettings.ICON_THEME); + const iconThemeSetting = this.settings.fileIconTheme; if (iconThemeSetting !== this.currentFileIconTheme.settingsId) { this.findFileIconThemeBySettingId(iconThemeSetting).then(theme => { this.setFileIconTheme(theme ? theme.id : DEFAULT_ICON_THEME_ID, undefined); @@ -305,15 +281,15 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (this.currentColorTheme) { let hasColorChanges = false; if (e.affectsConfiguration(ThemeSettings.COLOR_CUSTOMIZATIONS)) { - this.currentColorTheme.setCustomColors(this.colorCustomizations); + this.currentColorTheme.setCustomColors(this.settings.colorCustomizations); hasColorChanges = true; } if (e.affectsConfiguration(ThemeSettings.TOKEN_COLOR_CUSTOMIZATIONS)) { - this.currentColorTheme.setCustomTokenColors(this.tokenColorCustomizations); + this.currentColorTheme.setCustomTokenColors(this.settings.tokenColorCustomizations); hasColorChanges = true; } if (e.affectsConfiguration(ThemeSettings.TOKEN_COLOR_CUSTOMIZATIONS_EXPERIMENTAL)) { - this.currentColorTheme.setCustomTokenStyleRules(this.tokenStylesCustomizations); + this.currentColorTheme.setCustomTokenStyleRules(this.settings.tokenStylesCustomizations); hasColorChanges = true; } if (hasColorChanges) { @@ -340,7 +316,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } private getPreferredColorScheme(): ThemeType | undefined { - let detectHCThemeSetting = this.configurationService.getValue(ThemeSettings.DETECT_HC); + const detectHCThemeSetting = this.configurationService.getValue(ThemeSettings.DETECT_HC); if (this.environmentService.configuration.highContrast && detectHCThemeSetting) { return HIGH_CONTRAST; } @@ -379,7 +355,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return Promise.resolve(null); } if (themeId === this.currentColorTheme.id && this.currentColorTheme.isLoaded) { - return this.writeColorThemeConfiguration(settingsTarget); + return this.settings.setColorTheme(this.currentColorTheme, settingsTarget); } themeId = validateThemeId(themeId); // migrate theme ids @@ -393,14 +369,14 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.currentColorTheme.clearCaches(); // the loaded theme is identical to the perisisted theme. Don't need to send an event. this.currentColorTheme = themeData; - themeData.setCustomColors(this.colorCustomizations); - themeData.setCustomTokenColors(this.tokenColorCustomizations); - themeData.setCustomTokenStyleRules(this.tokenStylesCustomizations); + themeData.setCustomColors(this.settings.colorCustomizations); + themeData.setCustomTokenColors(this.settings.tokenColorCustomizations); + themeData.setCustomTokenStyleRules(this.settings.tokenStylesCustomizations); return Promise.resolve(themeData); } - themeData.setCustomColors(this.colorCustomizations); - themeData.setCustomTokenColors(this.tokenColorCustomizations); - themeData.setCustomTokenStyleRules(this.tokenStylesCustomizations); + themeData.setCustomColors(this.settings.colorCustomizations); + themeData.setCustomTokenColors(this.settings.tokenColorCustomizations); + themeData.setCustomTokenStyleRules(this.settings.tokenStylesCustomizations); this.updateDynamicCSSRules(themeData); return this.applyTheme(themeData, settingsTarget); }, error => { @@ -411,15 +387,15 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { private async reloadCurrentColorTheme() { await this.currentColorTheme.reload(this.extensionResourceLoaderService); - this.currentColorTheme.setCustomColors(this.colorCustomizations); - this.currentColorTheme.setCustomTokenColors(this.tokenColorCustomizations); - this.currentColorTheme.setCustomTokenStyleRules(this.tokenStylesCustomizations); + this.currentColorTheme.setCustomColors(this.settings.colorCustomizations); + this.currentColorTheme.setCustomTokenColors(this.settings.tokenColorCustomizations); + this.currentColorTheme.setCustomTokenStyleRules(this.settings.tokenStylesCustomizations); this.updateDynamicCSSRules(this.currentColorTheme); this.applyTheme(this.currentColorTheme, undefined, false); } public restoreColorTheme() { - let colorThemeSetting = this.configurationService.getValue(ThemeSettings.COLOR_THEME); + const colorThemeSetting = this.settings.colorTheme; if (colorThemeSetting !== this.currentColorTheme.settingsId) { this.colorThemeRegistry.findThemeBySettingsId(colorThemeSetting, undefined).then(theme => { if (theme) { @@ -452,19 +428,11 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.currentColorTheme.clearCaches(); this.currentColorTheme = newTheme; - if (!this.themingParticipantChangeListener) { - this.themingParticipantChangeListener = themingRegistry.onThemingParticipantAdded(_ => this.updateDynamicCSSRules(this.currentColorTheme)); + if (!this.colorThemingParticipantChangeListener) { + this.colorThemingParticipantChangeListener = themingRegistry.onThemingParticipantAdded(_ => this.updateDynamicCSSRules(this.currentColorTheme)); } - if (this.fileService && !resources.isEqual(newTheme.location, this.watchedColorThemeLocation)) { - dispose(this.watchedColorThemeDisposable); - this.watchedColorThemeLocation = undefined; - - if (newTheme.location && (newTheme.watch || !!this.environmentService.extensionDevelopmentLocationURI)) { - this.watchedColorThemeLocation = newTheme.location; - this.watchedColorThemeDisposable = this.fileService.watch(newTheme.location); - } - } + this.colorThemeWatcher.update(newTheme); this.sendTelemetry(newTheme.id, newTheme.extensionData, 'color'); @@ -479,20 +447,14 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.storageService.store(PERSISTED_THEME_STORAGE_KEY, newTheme.toStorageData(), StorageScope.GLOBAL); } - return this.writeColorThemeConfiguration(settingsTarget); + return this.settings.setColorTheme(this.currentColorTheme, settingsTarget); } - private writeColorThemeConfiguration(settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise { - if (!types.isUndefinedOrNull(settingsTarget)) { - return this.writeConfiguration(ThemeSettings.COLOR_THEME, this.currentColorTheme.settingsId, settingsTarget).then(_ => this.currentColorTheme); - } - return Promise.resolve(this.currentColorTheme); - } private themeExtensionsActivated = new Map(); private sendTelemetry(themeId: string, themeData: ExtensionData | undefined, themeType: string) { if (themeData) { - let key = themeType + themeData.extensionId; + const key = themeType + themeData.extensionId; if (!this.themeExtensionsActivated.get(key)) { type ActivatePluginClassification = { id: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; @@ -521,7 +483,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } public getFileIconThemes(): Promise { - return this.iconThemeRegistry.getThemes(); + return this.fileIconThemeRegistry.getThemes(); } public getFileIconTheme() { @@ -531,7 +493,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { public setFileIconTheme(iconTheme: string | undefined, settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise { iconTheme = iconTheme || ''; if (iconTheme === this.currentFileIconTheme.id && this.currentFileIconTheme.isLoaded) { - return this.writeFileIconConfiguration(settingsTarget); + return this.settings.setFileIconTheme(this.currentFileIconTheme, settingsTarget); } const onApply = (newIconTheme: FileIconThemeData) => { this.doSetFileIconTheme(newIconTheme); @@ -541,7 +503,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.storageService.store(PERSISTED_ICON_THEME_STORAGE_KEY, newIconTheme.toStorageData(), StorageScope.GLOBAL); } - return this.writeFileIconConfiguration(settingsTarget); + return this.settings.setFileIconTheme(this.currentFileIconTheme, settingsTarget); }; return this.findFileIconThemeById(iconTheme).then(data => { @@ -553,11 +515,11 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } private async findFileIconThemeById(id: string): Promise { - return id.length === 0 ? FileIconThemeData.noIconTheme() : this.iconThemeRegistry.findThemeById(id); + return id.length === 0 ? FileIconThemeData.noIconTheme() : this.fileIconThemeRegistry.findThemeById(id); } private async findFileIconThemeBySettingId(settingsId: string | null): Promise { - return !settingsId ? FileIconThemeData.noIconTheme() : this.iconThemeRegistry.findThemeBySettingsId(settingsId); + return !settingsId ? FileIconThemeData.noIconTheme() : this.fileIconThemeRegistry.findThemeBySettingsId(settingsId); } private async reloadCurrentFileIconTheme() { @@ -569,9 +531,9 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } public restoreFileIconTheme() { - let fileIconThemeSetting = this.configurationService.getValue(ThemeSettings.ICON_THEME); + const fileIconThemeSetting = this.settings.fileIconTheme; if (fileIconThemeSetting !== this.currentFileIconTheme.settingsId) { - this.iconThemeRegistry.findThemeBySettingsId(fileIconThemeSetting).then(theme => { + this.fileIconThemeRegistry.findThemeBySettingsId(fileIconThemeSetting).then(theme => { if (theme) { this.setFileIconTheme(theme.id, undefined); } @@ -588,15 +550,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { removeClasses(this.container, fileIconsEnabledClass); } - if (this.fileService && !resources.isEqual(iconThemeData.location, this.watchedIconThemeLocation)) { - dispose(this.watchedIconThemeDisposable); - this.watchedIconThemeLocation = undefined; - - if (iconThemeData.location && (iconThemeData.watch || !!this.environmentService.extensionDevelopmentLocationURI)) { - this.watchedIconThemeLocation = iconThemeData.location; - this.watchedIconThemeDisposable = this.fileService.watch(iconThemeData.location); - } - } + this.fileIconThemeWatcher.update(iconThemeData); if (iconThemeData.id) { this.sendTelemetry(iconThemeData.id, iconThemeData.extensionData, 'fileIcon'); @@ -605,42 +559,6 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } - private writeFileIconConfiguration(settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise { - if (!types.isUndefinedOrNull(settingsTarget)) { - return this.writeConfiguration(ThemeSettings.ICON_THEME, this.currentFileIconTheme.settingsId, settingsTarget).then(_ => this.currentFileIconTheme); - } - return Promise.resolve(this.currentFileIconTheme); - } - - public writeConfiguration(key: string, value: any, settingsTarget: ConfigurationTarget | 'auto'): Promise { - let settings = this.configurationService.inspect(key); - if (settingsTarget === 'auto') { - if (!types.isUndefined(settings.workspaceFolderValue)) { - settingsTarget = ConfigurationTarget.WORKSPACE_FOLDER; - } else if (!types.isUndefined(settings.workspaceValue)) { - settingsTarget = ConfigurationTarget.WORKSPACE; - } else { - settingsTarget = ConfigurationTarget.USER; - } - } - - if (settingsTarget === ConfigurationTarget.USER) { - if (value === settings.userValue) { - return Promise.resolve(undefined); // nothing to do - } else if (value === settings.defaultValue) { - if (types.isUndefined(settings.userValue)) { - return Promise.resolve(undefined); // nothing to do - } - value = undefined; // remove configuration from user settings - } - } else if (settingsTarget === ConfigurationTarget.WORKSPACE || settingsTarget === ConfigurationTarget.WORKSPACE_FOLDER) { - if (value === settings.value) { - return Promise.resolve(undefined); // nothing to do - } - } - return this.configurationService.updateValue(key, value, settingsTarget); - } - private getBaseThemeFromContainer() { for (let i = this.container.classList.length - 1; i >= 0; i--) { const item = this.container.classList.item(i); @@ -652,15 +570,48 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } } +class ThemeFileWatcher { + + private inExtensionDevelopment: boolean; + private watchedLocation: URI | undefined; + private watcherDisposable: IDisposable | undefined; + private fileChangeListener: IDisposable | undefined; + + constructor(private fileService: IFileService, environmentService: IWorkbenchEnvironmentService, private onUpdate: () => void) { + this.inExtensionDevelopment = !!environmentService.extensionDevelopmentLocationURI; + } + + update(theme: { location?: URI, watch?: boolean; }) { + if (!resources.isEqual(theme.location, this.watchedLocation)) { + this.dispose(); + if (theme.location && (theme.watch || this.inExtensionDevelopment)) { + this.watchedLocation = theme.location; + this.watcherDisposable = this.fileService.watch(theme.location); + this.fileService.onDidFilesChange(e => { + if (this.watchedLocation && e.contains(this.watchedLocation, FileChangeType.UPDATED)) { + this.onUpdate(); + } + }); + } + } + } + + dispose() { + this.watcherDisposable = dispose(this.watcherDisposable); + this.fileChangeListener = dispose(this.fileChangeListener); + this.watchedLocation = undefined; + } +} + function _applyIconTheme(data: FileIconThemeData, onApply: (theme: FileIconThemeData) => Promise): Promise { _applyRules(data.styleSheetContent!, iconThemeRulesClassName); return onApply(data); } function _applyRules(styleSheetContent: string, rulesClassName: string) { - let themeStyles = document.head.getElementsByClassName(rulesClassName); + const themeStyles = document.head.getElementsByClassName(rulesClassName); if (themeStyles.length === 0) { - let elStyle = document.createElement('style'); + const elStyle = document.createElement('style'); elStyle.type = 'text/css'; elStyle.className = rulesClassName; elStyle.innerHTML = styleSheetContent; diff --git a/src/vs/workbench/services/themes/common/themeConfiguration.ts b/src/vs/workbench/services/themes/common/themeConfiguration.ts index 0ae3f23ad5e..ea4b9edb36b 100644 --- a/src/vs/workbench/services/themes/common/themeConfiguration.ts +++ b/src/vs/workbench/services/themes/common/themeConfiguration.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; +import * as types from 'vs/base/common/types'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, IConfigurationPropertySchema, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; @@ -11,7 +12,8 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { textmateColorsSchemaId, textmateColorGroupSchemaId } from 'vs/workbench/services/themes/common/colorThemeSchema'; import { workbenchColorsSchemaId } from 'vs/platform/theme/common/colorRegistry'; import { tokenStylingSchemaId } from 'vs/platform/theme/common/tokenClassificationRegistry'; -import { ThemeSettings, IWorkbenchColorTheme, IWorkbenchFileIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { ThemeSettings, IWorkbenchColorTheme, IWorkbenchFileIconTheme, IColorCustomizations, ITokenColorCustomizations, IExperimentalTokenStyleCustomizations } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; const DEFAULT_THEME_SETTING_VALUE = 'Default Dark+'; const DEFAULT_THEME_DARK_SETTING_VALUE = 'Default Dark+'; @@ -175,3 +177,73 @@ export function updateFileIconThemeConfigurationSchemas(themes: IWorkbenchFileIc configurationRegistry.notifyConfigurationSchemaUpdated(themeSettingsConfiguration); } + + +export class ThemeConfiguration { + constructor(private configurationService: IConfigurationService) { + } + + public get colorTheme(): string { + return this.configurationService.getValue(ThemeSettings.COLOR_THEME); + } + + public get fileIconTheme(): string | null { + return this.configurationService.getValue(ThemeSettings.ICON_THEME); + } + + public get colorCustomizations(): IColorCustomizations { + return this.configurationService.getValue(ThemeSettings.COLOR_CUSTOMIZATIONS) || {}; + } + + public get tokenColorCustomizations(): ITokenColorCustomizations { + return this.configurationService.getValue(ThemeSettings.TOKEN_COLOR_CUSTOMIZATIONS) || {}; + } + + public get tokenStylesCustomizations(): IExperimentalTokenStyleCustomizations { + return this.configurationService.getValue(ThemeSettings.TOKEN_COLOR_CUSTOMIZATIONS_EXPERIMENTAL) || {}; + } + + public async setColorTheme(theme: IWorkbenchColorTheme, settingsTarget: ConfigurationTarget | undefined | 'auto',): Promise { + if (!types.isUndefinedOrNull(settingsTarget)) { + await this.writeConfiguration(ThemeSettings.COLOR_THEME, theme.settingsId, settingsTarget); + } + return theme; + } + + public async setFileIconTheme(theme: IWorkbenchFileIconTheme, settingsTarget: ConfigurationTarget | undefined | 'auto',): Promise { + if (!types.isUndefinedOrNull(settingsTarget)) { + await this.writeConfiguration(ThemeSettings.ICON_THEME, theme.settingsId, settingsTarget); + } + return theme; + } + + private writeConfiguration(key: string, value: any, settingsTarget: ConfigurationTarget | 'auto'): Promise { + let settings = this.configurationService.inspect(key); + if (settingsTarget === 'auto') { + if (!types.isUndefined(settings.workspaceFolderValue)) { + settingsTarget = ConfigurationTarget.WORKSPACE_FOLDER; + } else if (!types.isUndefined(settings.workspaceValue)) { + settingsTarget = ConfigurationTarget.WORKSPACE; + } else { + settingsTarget = ConfigurationTarget.USER; + } + } + + if (settingsTarget === ConfigurationTarget.USER) { + if (value === settings.userValue) { + return Promise.resolve(undefined); // nothing to do + } else if (value === settings.defaultValue) { + if (types.isUndefined(settings.userValue)) { + return Promise.resolve(undefined); // nothing to do + } + value = undefined; // remove configuration from user settings + } + } else if (settingsTarget === ConfigurationTarget.WORKSPACE || settingsTarget === ConfigurationTarget.WORKSPACE_FOLDER) { + if (value === settings.value) { + return Promise.resolve(undefined); // nothing to do + } + } + return this.configurationService.updateValue(key, value, settingsTarget); + } + +}