未验证 提交 c5963831 编写于 作者: M Martin Aeschlimann 提交者: GitHub

Merge pull request #66115 from JimiC/reload_theme

[themes] Add ability to reload themes without restarting the editor
......@@ -100,4 +100,5 @@ export interface IThemeExtensionPoint {
label?: string;
description?: string;
path: string;
_watch: boolean; // unsupported options to watch location
}
\ No newline at end of file
......@@ -43,6 +43,7 @@ export class ColorThemeData implements IColorTheme {
description?: string;
isLoaded: boolean;
location?: URI;
watch: boolean;
extensionData: ExtensionData;
get tokenColors(): ITokenColorizationRule[] {
......@@ -135,15 +136,21 @@ export class ColorThemeData implements IColorTheme {
}
public ensureLoaded(fileService: IFileService): Promise<void> {
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<void> {
return this.load(fileService);
}
private load(fileService: IFileService): Promise<void> {
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();
});
}
/**
......@@ -179,7 +186,8 @@ export class ColorThemeData implements IColorTheme {
selector: this.id.split(' ').join('.'), // to not break old clients
themeTokenColors: this.themeTokenColors,
extensionData: this.extensionData,
colorMap: colorMapData
colorMap: colorMapData,
watch: this.watch
});
}
......@@ -208,6 +216,7 @@ export class ColorThemeData implements IColorTheme {
themeData.settingsId = null;
themeData.isLoaded = false;
themeData.themeTokenColors = [{ settings: {} }];
themeData.watch = false;
return themeData;
}
......@@ -218,6 +227,7 @@ export class ColorThemeData implements IColorTheme {
themeData.settingsId = settingsId;
themeData.isLoaded = true;
themeData.themeTokenColors = [{ settings: {} }];
themeData.watch = false;
return themeData;
}
......@@ -234,7 +244,7 @@ export class ColorThemeData implements IColorTheme {
}
break;
case 'themeTokenColors':
case 'id': case 'label': case 'settingsId': case 'extensionData':
case 'id': case 'label': case 'settingsId': case 'extensionData': case 'watch':
theme[key] = data[key];
break;
}
......@@ -254,6 +264,7 @@ export class ColorThemeData implements IColorTheme {
themeData.label = theme.label || Paths.basename(theme.path);
themeData.settingsId = theme.id || themeData.label;
themeData.description = theme.description;
themeData.watch = theme._watch === true;
themeData.location = colorThemeLocation;
themeData.extensionData = extensionData;
themeData.isLoaded = false;
......
......@@ -29,20 +29,26 @@ export class FileIconThemeData implements IFileIconTheme {
private constructor() { }
public ensureLoaded(fileService: IFileService): Promise<string> {
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<string> {
return this.load(fileService);
}
private load(fileService: IFileService): Promise<string> {
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 {
......@@ -75,6 +81,19 @@ export class FileIconThemeData implements IFileIconTheme {
return themeData;
}
static createUnloadedTheme(id: string): FileIconThemeData {
let themeData = new FileIconThemeData();
themeData.id = '';
themeData.label = '';
themeData.settingsId = undefined;
themeData.isLoaded = false;
themeData.hasFileIcons = false;
themeData.hasFolderIcons = false;
themeData.hidesExplorerArrows = false;
themeData.extensionData = undefined;
return themeData;
}
static fromStorageData(input: string): FileIconThemeData | null {
try {
let data = JSON.parse(input);
......
......@@ -26,7 +26,9 @@ 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';
import { URI } from 'vs/base/common/uri';
import * as resources from 'vs/base/common/resources';
// implementation
......@@ -72,10 +74,12 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
private currentColorTheme: ColorThemeData;
private container: HTMLElement;
private readonly onColorThemeChange: Emitter<IColorTheme>;
private watchedColorThemeLocation: URI;
private iconThemeStore: FileIconThemeStore;
private currentIconTheme: IFileIconTheme;
private currentIconTheme: FileIconThemeData;
private readonly onFileIconThemeChange: Emitter<IFileIconTheme>;
private watchedIconThemeLocation: URI;
private themingParticipantChangeListener: IDisposable;
private _configurationWriter: ConfigurationWriter;
......@@ -105,16 +109,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
this.iconThemeStore = new FileIconThemeStore(extensionService);
this.onColorThemeChange = new Emitter<IColorTheme>();
this.currentIconTheme = {
id: '',
label: '',
settingsId: null,
isLoaded: false,
hasFileIcons: false,
hasFolderIcons: false,
hidesExplorerArrows: false,
extensionData: null
};
this.currentIconTheme = FileIconThemeData.createUnloadedTheme('');
// In order to avoid paint flashing for tokens, because
// themes are loaded asynchronously, we need to initialize
......@@ -181,6 +176,20 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
acquireFileService(fileService: IFileService): void {
this.fileService = fileService;
this.fileService.onFileChanges(async e => {
if (this.watchedColorThemeLocation && this.currentColorTheme && e.contains(this.watchedColorThemeLocation, FileChangeType.UPDATED)) {
await this.currentColorTheme.reload(this.fileService);
this.currentColorTheme.setCustomColors(this.colorCustomizations);
this.currentColorTheme.setCustomTokenColors(this.tokenColorCustomizations);
this.updateDynamicCSSRules(this.currentColorTheme);
this.applyTheme(this.currentColorTheme, null, false);
}
if (this.watchedIconThemeLocation && this.currentIconTheme && e.contains(this.watchedIconThemeLocation, FileChangeType.UPDATED)) {
await this.currentIconTheme.reload(this.fileService);
_applyIconTheme(this.currentIconTheme, () => Promise.resolve(this.currentIconTheme));
}
});
}
public get onDidColorThemeChange(): Event<IColorTheme> {
......@@ -282,24 +291,24 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
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(_ => {
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)));
});
});
}
......@@ -340,7 +349,17 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
}
this.currentColorTheme = newTheme;
if (!this.themingParticipantChangeListener) {
this.themingParticipantChangeListener = themingRegistry.onThemingParticipantAdded(p => this.updateDynamicCSSRules(this.currentColorTheme));
this.themingParticipantChangeListener = themingRegistry.onThemingParticipantAdded(_ => this.updateDynamicCSSRules(this.currentColorTheme));
}
if (this.fileService && !resources.isEqual(newTheme.location, this.watchedColorThemeLocation)) {
if (this.watchedColorThemeLocation) {
this.fileService.unwatchFileChanges(this.watchedColorThemeLocation);
}
if (newTheme.location && (newTheme.watch || !!this.environmentService.extensionDevelopmentLocationURI)) {
this.watchedColorThemeLocation = newTheme.location;
this.fileService.watchFileChanges(this.watchedColorThemeLocation);
}
}
this.sendTelemetry(newTheme.id, newTheme.extensionData, 'color');
......@@ -436,6 +455,17 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
removeClasses(this.container, fileIconsEnabledClass);
}
}
if (this.fileService && !resources.isEqual(iconThemeData.location, this.watchedIconThemeLocation)) {
if (this.watchedIconThemeLocation) {
this.fileService.unwatchFileChanges(this.watchedIconThemeLocation);
}
this.watchedIconThemeLocation = iconThemeData.location;
if (this.watchedIconThemeLocation) {
this.fileService.watchFileChanges(this.watchedIconThemeLocation);
}
}
if (iconThemeData.id) {
this.sendTelemetry(iconThemeData.id, iconThemeData.extensionData, 'fileIcon');
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册