From d3f677dfd6ea25f12c9d510306e8bc2ed27e5e32 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 20 Jul 2017 12:31:02 +0200 Subject: [PATCH] multi root: restore untitled workspaces based on untitled workspaces on disk --- src/vs/code/electron-main/windows.ts | 26 +-------- .../platform/workspaces/common/workspaces.ts | 3 + .../electron-main/workspacesMainService.ts | 39 +++++++++++-- .../workspacesMainService.test.ts | 58 +++++++++++++++++++ 4 files changed, 98 insertions(+), 28 deletions(-) diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index a3a7c9e4e44..b53d7843d5d 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -341,8 +341,8 @@ export class WindowsManager implements IWindowsMainService { if (openConfig.initialStartup && !openConfig.cli.extensionDevelopmentPath) { foldersToRestore = this.backupService.getFolderBackupPaths(); - workspacesToRestore = this.backupService.getWorkspaceBackups(); // collect from workspaces with hot-exit backups - workspacesToRestore.push(...this.doGetUntitledWorkspacesFromLastSession()); // collect from previous window session + workspacesToRestore = this.backupService.getWorkspaceBackups(); // collect from workspaces with hot-exit backups + workspacesToRestore.push(...this.workspacesService.getUntitledWorkspacesSync()); // collect from previous window session emptyToRestore = this.backupService.getEmptyWindowBackupPaths(); emptyToRestore.push(...windowsToOpen.filter(w => !w.workspace && !w.folderPath && w.backupPath).map(w => path.basename(w.backupPath))); // add empty windows with backupPath @@ -782,28 +782,6 @@ export class WindowsManager implements IWindowsMainService { return [Object.create(null)]; } - private doGetUntitledWorkspacesFromLastSession(): IWorkspaceIdentifier[] { - const candidates: IWorkspaceIdentifier[] = []; - - if (this.isUntitledWorkspace(this.windowsState.lastActiveWindow)) { - candidates.push(this.windowsState.lastActiveWindow.workspace); - } - - for (let i = 0; i < this.windowsState.openedWindows.length; i++) { - const state = this.windowsState.openedWindows[i]; - if (this.isUntitledWorkspace(state)) { - candidates.push(state.workspace); - } - } - - // Validate all workspace paths and only return the workspaces that are valid - return arrays.coalesce(candidates.map(candidate => this.parsePath(candidate.configPath)).map(window => window && window.workspace)); - } - - private isUntitledWorkspace(state: IWindowState): boolean { - return state && state.workspace && this.workspacesService.isUntitledWorkspace(state.workspace); - } - private getRestoreWindowsSetting(): RestoreWindowsSetting { let restoreWindows: RestoreWindowsSetting; if (this.lifecycleService.wasRestarted) { diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index 2a3abc9b50c..c23a2d3db6d 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -20,6 +20,7 @@ export const IWorkspacesService = createDecorator('workspace export const WORKSPACE_EXTENSION = 'code-workspace'; export const WORKSPACE_FILTER = [{ name: localize('codeWorkspace', "Code Workspace"), extensions: [WORKSPACE_EXTENSION] }]; +export const UNTITLED_WORKSPACE_NAME = 'workspace.json'; /** * A single folder workspace identifier is just the path to the folder. @@ -51,6 +52,8 @@ export interface IWorkspacesMainService extends IWorkspacesService { isUntitledWorkspace(workspace: IWorkspaceIdentifier): boolean; deleteUntitledWorkspaceSync(workspace: IWorkspaceIdentifier): void; + + getUntitledWorkspacesSync(): IWorkspaceIdentifier[]; } export interface IWorkspacesService { diff --git a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts index e0956c97f7d..b3f6161159f 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts @@ -5,7 +5,7 @@ 'use strict'; -import { IWorkspacesMainService, IWorkspaceIdentifier, IStoredWorkspace, WORKSPACE_EXTENSION, IWorkspaceSavedEvent } from "vs/platform/workspaces/common/workspaces"; +import { IWorkspacesMainService, IWorkspaceIdentifier, IStoredWorkspace, WORKSPACE_EXTENSION, IWorkspaceSavedEvent, UNTITLED_WORKSPACE_NAME } from "vs/platform/workspaces/common/workspaces"; import { TPromise } from "vs/base/common/winjs.base"; import { isParent } from "vs/platform/files/common/files"; import { IEnvironmentService } from "vs/platform/environment/common/environment"; @@ -13,11 +13,12 @@ import { extname, join, dirname } from "path"; import { mkdirp, writeFile } from "vs/base/node/pfs"; import { readFileSync } from "fs"; import { isLinux } from "vs/base/common/platform"; -import { copy, delSync } from "vs/base/node/extfs"; +import { copy, delSync, readdirSync } from "vs/base/node/extfs"; import { nfcall } from "vs/base/common/async"; import Event, { Emitter } from "vs/base/common/event"; import { ILogService } from "vs/platform/log/common/log"; import { isEqual } from "vs/base/common/paths"; +import { coalesce } from "vs/base/common/arrays"; export class WorkspacesMainService implements IWorkspacesMainService { @@ -79,7 +80,7 @@ export class WorkspacesMainService implements IWorkspacesMainService { const workspaceId = this.nextWorkspaceId(); const workspaceConfigFolder = join(this.workspacesHome, workspaceId); - const workspaceConfigPath = join(workspaceConfigFolder, 'workspace.json'); + const workspaceConfigPath = join(workspaceConfigFolder, UNTITLED_WORKSPACE_NAME); return mkdirp(workspaceConfigFolder).then(() => { const storedWorkspace: IStoredWorkspace = { @@ -130,9 +131,39 @@ export class WorkspacesMainService implements IWorkspacesMainService { } // Delete from disk - delSync(dirname(workspace.configPath)); + this.doDeleteUntitledWorkspaceSync(workspace.configPath); // Event this._onWorkspaceDeleted.fire(workspace); } + + private doDeleteUntitledWorkspaceSync(configPath: string): void { + try { + delSync(dirname(configPath)); + } catch (error) { + this.logService.log(`Unable to delete untitled workspace ${configPath} (${error}).`); + } + } + + public getUntitledWorkspacesSync(): IWorkspaceIdentifier[] { + let untitledWorkspacePaths: string[] = []; + try { + untitledWorkspacePaths = readdirSync(this.workspacesHome).map(folder => join(this.workspacesHome, folder, UNTITLED_WORKSPACE_NAME)); + } catch (error) { + this.logService.log(`Unable to read folders in ${this.workspacesHome} (${error}).`); + } + + const untitledWorkspaces: IWorkspaceIdentifier[] = coalesce(untitledWorkspacePaths.map(untitledWorkspacePath => { + const workspace = this.resolveWorkspaceSync(untitledWorkspacePath); + if (!workspace) { + this.doDeleteUntitledWorkspaceSync(untitledWorkspacePath); + + return null; // invalid workspace + } + + return { id: workspace.id, configPath: untitledWorkspacePath }; + })); + + return untitledWorkspaces; + } } \ No newline at end of file diff --git a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts index 2b410c9c510..b59867d25af 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts @@ -93,6 +93,10 @@ suite('WorkspacesMainService', () => { const resolved = service.resolveWorkspaceSync(workspace.configPath); assert.deepEqual(resolved, { id: workspace.id, folders: [process.cwd(), os.tmpdir()] }); + fs.writeFileSync(workspace.configPath, JSON.stringify({ id: 'someid' })); // invalid workspace + const resolvedInvalid = service.resolveWorkspaceSync(workspace.configPath); + assert.ok(!resolvedInvalid); + done(); }); }); @@ -162,4 +166,58 @@ suite('WorkspacesMainService', () => { }); }); }); + + test('deleteUntitledWorkspaceSync (untitled)', done => { + return service.createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => { + assert.ok(fs.existsSync(workspace.configPath)); + + service.deleteUntitledWorkspaceSync(workspace); + + assert.ok(!fs.existsSync(workspace.configPath)); + + done(); + }); + }); + + test('deleteUntitledWorkspaceSync (saved)', done => { + return service.createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => { + const workspaceConfigPath = path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`); + + return service.saveWorkspace(workspace, workspaceConfigPath).then(savedWorkspace => { + assert.ok(fs.existsSync(savedWorkspace.configPath)); + + service.deleteUntitledWorkspaceSync(savedWorkspace); + + assert.ok(fs.existsSync(savedWorkspace.configPath)); + + done(); + }); + }); + }); + + test('getUntitledWorkspaceSync', done => { + let untitled = service.getUntitledWorkspacesSync(); + assert.equal(0, untitled.length); + + return service.createWorkspace([process.cwd(), os.tmpdir()]).then(untitledOne => { + untitled = service.getUntitledWorkspacesSync(); + + assert.equal(1, untitled.length); + assert.equal(untitledOne.id, untitled[0].id); + + return service.createWorkspace([process.cwd(), os.tmpdir()]).then(untitledTwo => { + untitled = service.getUntitledWorkspacesSync(); + + assert.equal(2, untitled.length); + + service.deleteUntitledWorkspaceSync(untitledOne); + service.deleteUntitledWorkspaceSync(untitledTwo); + + untitled = service.getUntitledWorkspacesSync(); + assert.equal(0, untitled.length); + + done(); + }); + }); + }); }); \ No newline at end of file