diff --git a/src/vs/platform/lifecycle/common/lifecycle.ts b/src/vs/platform/lifecycle/common/lifecycle.ts index 4879ee13e8d3fded3afe46e9bdb66fa92ae62fa0..18782a0d0b38466fedb2d05fd111e5ec265d4b45 100644 --- a/src/vs/platform/lifecycle/common/lifecycle.ts +++ b/src/vs/platform/lifecycle/common/lifecycle.ts @@ -133,7 +133,7 @@ export interface ILifecycleService { /** * A flag indicating in what phase of the lifecycle we currently are. */ - readonly phase: LifecyclePhase; + phase: LifecyclePhase; /** * Fired before shutdown happens. Allows listeners to veto against the diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index b1e469d99db809d61e8993d16209aedd7e4a5b89..0625ab7372a1da82a413ae03a37fb7342221d645 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -105,8 +105,6 @@ import { WorkbenchContextKeysHandler } from 'vs/workbench/browser/contextkeys'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; // import@node -import { BackupFileService, InMemoryBackupFileService } from 'vs/workbench/services/backup/node/backupFileService'; -import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService'; import { getDelayedChannel } from 'vs/base/parts/ipc/node/ipc'; import { connect as connectNet } from 'vs/base/parts/ipc/node/ipc.net'; import { DialogChannel } from 'vs/platform/dialogs/node/dialogIpc'; @@ -190,13 +188,13 @@ export class Workbench extends Disposable implements IPartService { private editorService: EditorService; private editorGroupService: IEditorGroupsService; private contextViewService: ContextViewService; - private backupFileService: IBackupFileService; private windowService: IWindowService; - private lifecycleService: LifecycleService; + private lifecycleService: ILifecycleService; + private instantiationService: IInstantiationService; private contextService: IWorkspaceContextService; private storageService: IStorageService; - private configurationService: WorkspaceService; + private configurationService: IConfigurationService; private environmentService: IEnvironmentService; private logService: ILogService; private windowsService: IWindowsService; @@ -221,7 +219,7 @@ export class Workbench extends Disposable implements IPartService { @IInstantiationService instantiationService: IInstantiationService, @IWorkspaceContextService contextService: IWorkspaceContextService, @IStorageService storageService: IStorageService, - @IConfigurationService configurationService: WorkspaceService, + @IConfigurationService configurationService: IConfigurationService, @IEnvironmentService environmentService: IEnvironmentService, @ILogService logService: ILogService, @IWindowsService windowsService: IWindowsService @@ -333,7 +331,7 @@ export class Workbench extends Disposable implements IPartService { this.registerLayoutListeners(); // Layout State - this.initLayoutState(); + this.instantiationService.invokeFunction(accessor => this.initLayoutState(accessor)); // Render Workbench this.renderWorkbench(); @@ -525,14 +523,6 @@ export class Workbench extends Disposable implements IPartService { // History serviceCollection.set(IHistoryService, new SyncDescriptor(HistoryService)); - // Backup File Service - if (this.configuration.backupPath) { - this.backupFileService = this.instantiationService.createInstance(BackupFileService, this.configuration.backupPath); - } else { - this.backupFileService = new InMemoryBackupFileService(); - } - serviceCollection.set(IBackupFileService, this.backupFileService); - // Quick open service (quick open controller) this.quickOpen = this.instantiationService.createInstance(QuickOpenController); serviceCollection.set(IQuickOpenService, this.quickOpen); @@ -561,12 +551,20 @@ export class Workbench extends Disposable implements IPartService { this.instantiationService.invokeFunction(accessor => { const fileService = accessor.get(IFileService); const instantiationService = accessor.get(IInstantiationService); - const themeService = accessor.get(IWorkbenchThemeService) as WorkbenchThemeService; + const configurationService = accessor.get(IConfigurationService) as any; + const themeService = accessor.get(IWorkbenchThemeService) as any; - this.configurationService.acquireFileService(fileService); - this.configurationService.acquireInstantiationService(instantiationService); + if (typeof configurationService.acquireFileService === 'function') { + configurationService.acquireFileService(fileService); + } + + if (typeof configurationService.acquireInstantiationService === 'function') { + configurationService.acquireInstantiationService(instantiationService); + } - themeService.acquireFileService(fileService); + if (typeof themeService.acquireFileService === 'function') { + themeService.acquireFileService(fileService); + } }); } @@ -785,7 +783,7 @@ export class Workbench extends Disposable implements IPartService { } private whenStarted(accessor: ServicesAccessor, error?: Error): void { - const lifecycleService = accessor.get(ILifecycleService) as LifecycleService; + const lifecycleService = accessor.get(ILifecycleService); this.restored = true; @@ -1020,22 +1018,27 @@ export class Workbench extends Disposable implements IPartService { } } - private initLayoutState(): void { + private initLayoutState(accessor: ServicesAccessor): void { + const configurationService = accessor.get(IConfigurationService); + const storageService = accessor.get(IStorageService); + const lifecycleService = accessor.get(ILifecycleService); + const contextService = accessor.get(IWorkspaceContextService); + const environmentService = accessor.get(IEnvironmentService); // Fullscreen this.state.fullscreen = isFullscreen(); // Menubar visibility - this.state.menuBar.visibility = this.configurationService.getValue(Settings.MENUBAR_VISIBLE); + this.state.menuBar.visibility = configurationService.getValue(Settings.MENUBAR_VISIBLE); // Activity bar visibility - this.state.activityBar.hidden = !this.configurationService.getValue(Settings.ACTIVITYBAR_VISIBLE); + this.state.activityBar.hidden = !configurationService.getValue(Settings.ACTIVITYBAR_VISIBLE); // Sidebar visibility - this.state.sideBar.hidden = this.storageService.getBoolean(Storage.SIDEBAR_HIDDEN, StorageScope.WORKSPACE, this.contextService.getWorkbenchState() === WorkbenchState.EMPTY); + this.state.sideBar.hidden = storageService.getBoolean(Storage.SIDEBAR_HIDDEN, StorageScope.WORKSPACE, contextService.getWorkbenchState() === WorkbenchState.EMPTY); // Sidebar position - this.state.sideBar.position = (this.configurationService.getValue(Settings.SIDEBAR_POSITION) === 'right') ? Position.RIGHT : Position.LEFT; + this.state.sideBar.position = (configurationService.getValue(Settings.SIDEBAR_POSITION) === 'right') ? Position.RIGHT : Position.LEFT; // Sidebar viewlet if (!this.state.sideBar.hidden) { @@ -1043,8 +1046,8 @@ export class Workbench extends Disposable implements IPartService { // Only restore last viewlet if window was reloaded or we are in development mode let viewletToRestore: string; - if (!this.environmentService.isBuilt || this.lifecycleService.startupKind === StartupKind.ReloadedWindow) { - viewletToRestore = this.storageService.get(SidebarPart.activeViewletSettingsKey, StorageScope.WORKSPACE, viewletRegistry.getDefaultViewletId()); + if (!environmentService.isBuilt || lifecycleService.startupKind === StartupKind.ReloadedWindow) { + viewletToRestore = storageService.get(SidebarPart.activeViewletSettingsKey, StorageScope.WORKSPACE, viewletRegistry.getDefaultViewletId()); } else { viewletToRestore = viewletRegistry.getDefaultViewletId(); } @@ -1057,13 +1060,13 @@ export class Workbench extends Disposable implements IPartService { } // Editor centered layout - this.state.editor.restoreCentered = this.storageService.getBoolean(Storage.CENTERED_LAYOUT_ENABLED, StorageScope.WORKSPACE, false); + this.state.editor.restoreCentered = storageService.getBoolean(Storage.CENTERED_LAYOUT_ENABLED, StorageScope.WORKSPACE, false); // Editors to open - this.state.editor.editorsToOpen = this.resolveEditorsToOpen(); + this.state.editor.editorsToOpen = this.resolveEditorsToOpen(accessor); // Panel visibility - this.state.panel.hidden = this.storageService.getBoolean(Storage.PANEL_HIDDEN, StorageScope.WORKSPACE, true); + this.state.panel.hidden = storageService.getBoolean(Storage.PANEL_HIDDEN, StorageScope.WORKSPACE, true); // Panel position this.updatePanelPosition(); @@ -1072,7 +1075,7 @@ export class Workbench extends Disposable implements IPartService { if (!this.state.panel.hidden) { const panelRegistry = Registry.as(PanelExtensions.Panels); - let panelToRestore = this.storageService.get(PanelPart.activePanelSettingsKey, StorageScope.WORKSPACE, panelRegistry.getDefaultPanelId()); + let panelToRestore = storageService.get(PanelPart.activePanelSettingsKey, StorageScope.WORKSPACE, panelRegistry.getDefaultPanelId()); if (!panelRegistry.hasPanel(panelToRestore)) { panelToRestore = panelRegistry.getDefaultPanelId(); // fallback to default if panel is unknown } @@ -1085,19 +1088,24 @@ export class Workbench extends Disposable implements IPartService { } // Statusbar visibility - this.state.statusBar.hidden = !this.configurationService.getValue(Settings.STATUSBAR_VISIBLE); + this.state.statusBar.hidden = !configurationService.getValue(Settings.STATUSBAR_VISIBLE); // Zen mode enablement - this.state.zenMode.restore = this.storageService.getBoolean(Storage.ZEN_MODE_ENABLED, StorageScope.WORKSPACE, false) && this.configurationService.getValue(Settings.ZEN_MODE_RESTORE); + this.state.zenMode.restore = storageService.getBoolean(Storage.ZEN_MODE_ENABLED, StorageScope.WORKSPACE, false) && configurationService.getValue(Settings.ZEN_MODE_RESTORE); } - private resolveEditorsToOpen(): Promise | IResourceEditor[] { + private resolveEditorsToOpen(accessor: ServicesAccessor): Promise | IResourceEditor[] { + const configuration = accessor.get(IWindowService).getConfiguration(); + const configurationService = accessor.get(IConfigurationService); + const contextService = accessor.get(IWorkspaceContextService); + const editorGroupService = accessor.get(IEditorGroupsService); + const backupFileService = accessor.get(IBackupFileService); // Files to open, diff or create if (this.hasInitialFilesToOpen()) { // Files to diff is exclusive - const filesToDiff = this.toInputs(this.configuration.filesToDiff, false); + const filesToDiff = this.toInputs(configuration.filesToDiff, false); if (filesToDiff && filesToDiff.length === 2) { return [{ leftResource: filesToDiff[0].resource, @@ -1107,21 +1115,21 @@ export class Workbench extends Disposable implements IPartService { }]; } - const filesToCreate = this.toInputs(this.configuration.filesToCreate, true); - const filesToOpen = this.toInputs(this.configuration.filesToOpen, false); + const filesToCreate = this.toInputs(configuration.filesToCreate, true); + const filesToOpen = this.toInputs(configuration.filesToOpen, false); // Otherwise: Open/Create files return [...filesToOpen, ...filesToCreate]; } // Empty workbench - else if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY && this.configurationService.inspect('workbench.startupEditor').value === 'newUntitledFile') { - const isEmpty = this.editorGroupService.count === 1 && this.editorGroupService.activeGroup.count === 0; + else if (contextService.getWorkbenchState() === WorkbenchState.EMPTY && configurationService.inspect('workbench.startupEditor').value === 'newUntitledFile') { + const isEmpty = editorGroupService.count === 1 && editorGroupService.activeGroup.count === 0; if (!isEmpty) { return []; // do not open any empty untitled file if we restored editors from previous session } - return this.backupFileService.hasBackups().then(hasBackups => { + return backupFileService.hasBackups().then(hasBackups => { if (hasBackups) { return []; // do not open any empty untitled file if we have backups to restore } diff --git a/src/vs/workbench/services/backup/node/backupFileService.ts b/src/vs/workbench/services/backup/node/backupFileService.ts index a089102b7f986ea9f0f4974197abcf931ec7f626..5eb3e0402c54470a99f38031068e44a7f839b00f 100644 --- a/src/vs/workbench/services/backup/node/backupFileService.ts +++ b/src/vs/workbench/services/backup/node/backupFileService.ts @@ -15,6 +15,8 @@ import { ITextBufferFactory } from 'vs/editor/common/model'; import { createTextBufferFactoryFromStream, createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; import { keys } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export interface IBackupFilesModel { resolve(backupRoot: string): Promise; @@ -107,6 +109,63 @@ export class BackupFilesModel implements IBackupFilesModel { export class BackupFileService implements IBackupFileService { + _serviceBrand: any; + + private impl: IBackupFileService; + + constructor( + @IWindowService windowService: IWindowService, + @IFileService fileService: IFileService + ) { + const backupWorkspacePath = windowService.getConfiguration().backupPath; + if (backupWorkspacePath) { + this.impl = new BackupFileServiceImpl(backupWorkspacePath, fileService); + } else { + this.impl = new InMemoryBackupFileService(); + } + } + + initialize(backupWorkspacePath: string): void { + if (this.impl instanceof BackupFileServiceImpl) { + this.impl.initialize(backupWorkspacePath); + } + } + + hasBackups(): Promise { + return this.impl.hasBackups(); + } + + loadBackupResource(resource: Uri): Promise { + return this.impl.loadBackupResource(resource); + } + + backupResource(resource: Uri, content: ITextSnapshot, versionId?: number): Promise { + return this.impl.backupResource(resource, content, versionId); + } + + discardResourceBackup(resource: Uri): Promise { + return this.impl.discardResourceBackup(resource); + } + + discardAllWorkspaceBackups(): Promise { + return this.impl.discardAllWorkspaceBackups(); + } + + getWorkspaceFileBackups(): Promise { + return this.impl.getWorkspaceFileBackups(); + } + + resolveBackupContent(backup: Uri): Promise { + return this.impl.resolveBackupContent(backup); + } + + toBackupResource(resource: Uri): Uri { + return this.impl.toBackupResource(resource); + } +} + +class BackupFileServiceImpl implements IBackupFileService { + private static readonly META_MARKER = '\n'; _serviceBrand: any; @@ -170,7 +229,7 @@ export class BackupFileService implements IBackupFileService { } return this.ioOperationQueues.queueFor(backupResource).queue(() => { - const preamble = `${resource.toString()}${BackupFileService.META_MARKER}`; + const preamble = `${resource.toString()}${BackupFileServiceImpl.META_MARKER}`; // Update content with value return this.fileService.updateContent(backupResource, new BackupSnapshot(content, preamble), BACKUP_FILE_UPDATE_OPTIONS).then(() => model.add(backupResource, versionId)); @@ -202,7 +261,7 @@ export class BackupFileService implements IBackupFileService { model.get().forEach(fileBackup => { readPromises.push( - readToMatchingString(fileBackup.fsPath, BackupFileService.META_MARKER, 2000, 10000).then(Uri.parse) + readToMatchingString(fileBackup.fsPath, BackupFileServiceImpl.META_MARKER, 2000, 10000).then(Uri.parse) ); }); @@ -217,7 +276,7 @@ export class BackupFileService implements IBackupFileService { let metaFound = false; const metaPreambleFilter = (chunk: string) => { if (!metaFound && chunk) { - const metaIndex = chunk.indexOf(BackupFileService.META_MARKER); + const metaIndex = chunk.indexOf(BackupFileServiceImpl.META_MARKER); if (metaIndex === -1) { return ''; // meta not yet found, return empty string } @@ -302,3 +361,5 @@ export function hashPath(resource: Uri): string { const str = resource.scheme === Schemas.file ? resource.fsPath : resource.toString(); return crypto.createHash('md5').update(str).digest('hex'); } + +registerSingleton(IBackupFileService, BackupFileService); \ No newline at end of file diff --git a/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts b/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts index 5f1ea987873e4dc6f41e3994d6def7b5cc58871c..d2d9a82d2c2f92656e691ae69d17f1b7f346632f 100644 --- a/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts +++ b/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts @@ -13,7 +13,7 @@ import { URI as Uri } from 'vs/base/common/uri'; import { BackupFileService, BackupFilesModel, hashPath } from 'vs/workbench/services/backup/node/backupFileService'; import { FileService } from 'vs/workbench/services/files/node/fileService'; import { TextModel, createTextBufferFactory } from 'vs/editor/common/model/textModel'; -import { TestContextService, TestTextResourceConfigurationService, TestLifecycleService, TestEnvironmentService, TestStorageService } from 'vs/workbench/test/workbenchTestServices'; +import { TestContextService, TestTextResourceConfigurationService, TestLifecycleService, TestEnvironmentService, TestStorageService, TestWindowService } from 'vs/workbench/test/workbenchTestServices'; import { getRandomTestPath } from 'vs/base/test/node/testUtils'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; import { Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; @@ -21,6 +21,7 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/ import { DefaultEndOfLine } from 'vs/editor/common/model'; import { snapshotToString } from 'vs/platform/files/common/files'; import { Schemas } from 'vs/base/common/network'; +import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backupfileservice'); const backupHome = path.join(parentDir, 'Backups'); @@ -35,11 +36,28 @@ const fooBackupPath = path.join(workspaceBackupPath, 'file', hashPath(fooFile)); const barBackupPath = path.join(workspaceBackupPath, 'file', hashPath(barFile)); const untitledBackupPath = path.join(workspaceBackupPath, 'untitled', hashPath(untitledFile)); +class TestBackupWindowService extends TestWindowService { + + private config: IWindowConfiguration; + + constructor(workspaceBackupPath: string) { + super(); + + this.config = Object.create(null); + this.config.backupPath = workspaceBackupPath; + } + + getConfiguration(): IWindowConfiguration { + return this.config; + } +} + class TestBackupFileService extends BackupFileService { constructor(workspace: Uri, backupHome: string, workspacesJsonPath: string) { const fileService = new FileService(new TestContextService(new Workspace(workspace.fsPath, toWorkspaceFolders([{ path: workspace.fsPath }]))), TestEnvironmentService, new TestTextResourceConfigurationService(), new TestConfigurationService(), new TestLifecycleService(), new TestStorageService(), new TestNotificationService(), { disableWatcher: true }); + const windowService = new TestBackupWindowService(workspaceBackupPath); - super(workspaceBackupPath, fileService); + super(windowService, fileService); } public toBackupResource(resource: Uri): Uri { diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index ceffd2a872766c5a6ac9b7215958d9b3cb696dfe..e06ec70c0b0b03d284376cf7a389cb83569894f0 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -73,6 +73,7 @@ import 'vs/workbench/services/configuration/node/jsonEditingService'; import 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; import 'vs/workbench/services/textfile/common/textFileService'; import 'vs/workbench/services/dialogs/electron-browser/dialogService'; +import 'vs/workbench/services/backup/node/backupFileService'; registerSingleton(IMenuService, MenuService, true); registerSingleton(IListService, ListService, true);