diff --git a/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts b/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts index 59ee56d0eddd38261d219b48c7698ccddb755a13..823f339d164e5327efabd88194b32b55f2ab69c9 100644 --- a/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts +++ b/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts @@ -135,15 +135,21 @@ export class ColorThemeData implements IColorTheme { } public ensureLoaded(fileService: IFileService): Promise { - if (!this.isLoaded) { - if (this.location) { - return _loadColorTheme(fileService, this.location, this.themeTokenColors, this.colorMap).then(_ => { - this.isLoaded = true; - this.sanitizeTokenColors(); - }); - } + return !this.isLoaded ? this.load(fileService) : Promise.resolve(undefined); + } + + public reload(fileService: IFileService): Promise { + return this.load(fileService); + } + + private load(fileService: IFileService): Promise { + if (!this.location) { + return Promise.resolve(undefined); } - return Promise.resolve(undefined); + return _loadColorTheme(fileService, this.location, this.themeTokenColors, this.colorMap).then(_ => { + this.isLoaded = true; + this.sanitizeTokenColors(); + }); } /** diff --git a/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts b/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts index d1f0a073413ad54c67bce471df7528ef645fad32..3815c063373e1168617ec2f8476ab8e5eb34cec6 100644 --- a/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts +++ b/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts @@ -29,20 +29,26 @@ export class FileIconThemeData implements IFileIconTheme { private constructor() { } public ensureLoaded(fileService: IFileService): Promise { - if (!this.isLoaded) { - if (this.location) { - return _loadIconThemeDocument(fileService, this.location).then(iconThemeDocument => { - let result = _processIconThemeDocument(this.id, this.location!, iconThemeDocument); - this.styleSheetContent = result.content; - this.hasFileIcons = result.hasFileIcons; - this.hasFolderIcons = result.hasFolderIcons; - this.hidesExplorerArrows = result.hidesExplorerArrows; - this.isLoaded = true; - return this.styleSheetContent; - }); - } + return !this.isLoaded ? this.load(fileService) : Promise.resolve(this.styleSheetContent); + } + + public reload(fileService: IFileService): Promise { + return this.load(fileService); + } + + private load(fileService: IFileService): Promise { + if (!this.location) { + return Promise.resolve(this.styleSheetContent); } - return Promise.resolve(this.styleSheetContent); + return _loadIconThemeDocument(fileService, this.location).then(iconThemeDocument => { + const result = _processIconThemeDocument(this.id, this.location!, iconThemeDocument); + this.styleSheetContent = result.content; + this.hasFileIcons = result.hasFileIcons; + this.hasFolderIcons = result.hasFolderIcons; + this.hidesExplorerArrows = result.hidesExplorerArrows; + this.isLoaded = true; + return this.styleSheetContent; + }); } static fromExtensionTheme(iconTheme: IThemeExtensionPoint, iconThemeLocation: URI, extensionData: ExtensionData): FileIconThemeData { diff --git a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts index 90c579d0d735424e15c863b542c46c539b182788..71c24e5c3d566b9d65728937a8a18cc3c84d281c 100644 --- a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts @@ -26,7 +26,7 @@ import { FileIconThemeData } from 'vs/workbench/services/themes/electron-browser import { IWindowService } from 'vs/platform/windows/common/windows'; import { removeClasses, addClasses } from 'vs/base/browser/dom'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IFileService } from 'vs/platform/files/common/files'; +import { IFileService, FileChangeType } from 'vs/platform/files/common/files'; // implementation @@ -72,10 +72,12 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { private currentColorTheme: ColorThemeData; private container: HTMLElement; private readonly onColorThemeChange: Emitter; + private onColorThemeFileChangeListener: IDisposable; private iconThemeStore: FileIconThemeStore; private currentIconTheme: IFileIconTheme; private readonly onFileIconThemeChange: Emitter; + private onIconThemeFileChangeListener: IDisposable; private themingParticipantChangeListener: IDisposable; private _configurationWriter: ConfigurationWriter; @@ -279,27 +281,54 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.writeColorThemeConfiguration(settingsTarget); } + const onApply = (themeData: ColorThemeData) => { + // determine which color theme file to monitor for changes + if (this.fileService) { + const _currentColorThemeData = this.currentColorTheme as ColorThemeData; + if (!this.onColorThemeFileChangeListener) { + this.onColorThemeFileChangeListener = this.fileService.onFileChanges(e => { + return e.contains(themeData.location, FileChangeType.UPDATED) + && themeData.reload(this.fileService) + .then(_ => { + themeData.setCustomColors(this.colorCustomizations); + themeData.setCustomTokenColors(this.tokenColorCustomizations); + this.updateDynamicCSSRules(themeData); + }); + }); + } + // stop monitoring previous color theme file for changes + if (_currentColorThemeData.location) { + this.fileService.unwatchFileChanges(_currentColorThemeData.location); + } + // start monitoring new color theme file for changes + if (themeData.location) { + this.fileService.watchFileChanges(themeData.location); + } + } + }; + themeId = validateThemeId(themeId); // migrate theme ids return this.colorThemeStore.findThemeData(themeId, DEFAULT_THEME_ID).then(themeData => { - if (themeData) { - return themeData.ensureLoaded(this.fileService).then(_ => { - if (themeId === this.currentColorTheme.id && !this.currentColorTheme.isLoaded && this.currentColorTheme.hasEqualData(themeData)) { - // the loaded theme is identical to the perisisted theme. Don't need to send an event. - this.currentColorTheme = themeData; - themeData.setCustomColors(this.colorCustomizations); - themeData.setCustomTokenColors(this.tokenColorCustomizations); - return Promise.resolve(themeData); - } + if (!themeData) { + return null; + } + return themeData.ensureLoaded(this.fileService).then(_ => { + onApply(themeData); + if (themeId === this.currentColorTheme.id && !this.currentColorTheme.isLoaded && this.currentColorTheme.hasEqualData(themeData)) { + // the loaded theme is identical to the perisisted theme. Don't need to send an event. + this.currentColorTheme = themeData; themeData.setCustomColors(this.colorCustomizations); themeData.setCustomTokenColors(this.tokenColorCustomizations); - 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))); - }); - } - return null; + return Promise.resolve(themeData); + } + themeData.setCustomColors(this.colorCustomizations); + themeData.setCustomTokenColors(this.tokenColorCustomizations); + 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))); + }); }); } @@ -408,6 +437,28 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.writeFileIconConfiguration(settingsTarget); } let onApply = (newIconTheme: FileIconThemeData) => { + // determine which icon theme file to monitor for changes + if (this.currentIconTheme.id !== newIconTheme.id || !this.currentIconTheme.isLoaded) { + if (this.fileService) { + if (!this.onIconThemeFileChangeListener) { + this.onIconThemeFileChangeListener = this.fileService.onFileChanges(e => { + return e.contains(newIconTheme.location, FileChangeType.UPDATED) + && newIconTheme.reload(this.fileService) + .then(_ => _applyIconTheme(newIconTheme, () => Promise.resolve(this.currentIconTheme))); + }); + } + const _currentIconThemeData = this.currentIconTheme as FileIconThemeData; + // stop monitoring previous icon theme file for changes + if (_currentIconThemeData.location) { + this.fileService.unwatchFileChanges(_currentIconThemeData.location); + } + // start monitoring new icon theme file for changes + if (newIconTheme.location) { + this.fileService.watchFileChanges(newIconTheme.location); + } + } + } + this.doSetFileIconTheme(newIconTheme); // remember theme data for a quick restore