Skip to content

Commit d3f677d

Browse files
author
Benjamin Pasero
committed
multi root: restore untitled workspaces based on untitled workspaces on disk
1 parent be413a0 commit d3f677d

4 files changed

Lines changed: 98 additions & 28 deletions

File tree

src/vs/code/electron-main/windows.ts

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -341,8 +341,8 @@ export class WindowsManager implements IWindowsMainService {
341341
if (openConfig.initialStartup && !openConfig.cli.extensionDevelopmentPath) {
342342
foldersToRestore = this.backupService.getFolderBackupPaths();
343343

344-
workspacesToRestore = this.backupService.getWorkspaceBackups(); // collect from workspaces with hot-exit backups
345-
workspacesToRestore.push(...this.doGetUntitledWorkspacesFromLastSession()); // collect from previous window session
344+
workspacesToRestore = this.backupService.getWorkspaceBackups(); // collect from workspaces with hot-exit backups
345+
workspacesToRestore.push(...this.workspacesService.getUntitledWorkspacesSync()); // collect from previous window session
346346

347347
emptyToRestore = this.backupService.getEmptyWindowBackupPaths();
348348
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 {
782782
return [Object.create(null)];
783783
}
784784

785-
private doGetUntitledWorkspacesFromLastSession(): IWorkspaceIdentifier[] {
786-
const candidates: IWorkspaceIdentifier[] = [];
787-
788-
if (this.isUntitledWorkspace(this.windowsState.lastActiveWindow)) {
789-
candidates.push(this.windowsState.lastActiveWindow.workspace);
790-
}
791-
792-
for (let i = 0; i < this.windowsState.openedWindows.length; i++) {
793-
const state = this.windowsState.openedWindows[i];
794-
if (this.isUntitledWorkspace(state)) {
795-
candidates.push(state.workspace);
796-
}
797-
}
798-
799-
// Validate all workspace paths and only return the workspaces that are valid
800-
return arrays.coalesce(candidates.map(candidate => this.parsePath(candidate.configPath)).map(window => window && window.workspace));
801-
}
802-
803-
private isUntitledWorkspace(state: IWindowState): boolean {
804-
return state && state.workspace && this.workspacesService.isUntitledWorkspace(state.workspace);
805-
}
806-
807785
private getRestoreWindowsSetting(): RestoreWindowsSetting {
808786
let restoreWindows: RestoreWindowsSetting;
809787
if (this.lifecycleService.wasRestarted) {

src/vs/platform/workspaces/common/workspaces.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export const IWorkspacesService = createDecorator<IWorkspacesService>('workspace
2020

2121
export const WORKSPACE_EXTENSION = 'code-workspace';
2222
export const WORKSPACE_FILTER = [{ name: localize('codeWorkspace', "Code Workspace"), extensions: [WORKSPACE_EXTENSION] }];
23+
export const UNTITLED_WORKSPACE_NAME = 'workspace.json';
2324

2425
/**
2526
* A single folder workspace identifier is just the path to the folder.
@@ -51,6 +52,8 @@ export interface IWorkspacesMainService extends IWorkspacesService {
5152
isUntitledWorkspace(workspace: IWorkspaceIdentifier): boolean;
5253

5354
deleteUntitledWorkspaceSync(workspace: IWorkspaceIdentifier): void;
55+
56+
getUntitledWorkspacesSync(): IWorkspaceIdentifier[];
5457
}
5558

5659
export interface IWorkspacesService {

src/vs/platform/workspaces/electron-main/workspacesMainService.ts

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,20 @@
55

66
'use strict';
77

8-
import { IWorkspacesMainService, IWorkspaceIdentifier, IStoredWorkspace, WORKSPACE_EXTENSION, IWorkspaceSavedEvent } from "vs/platform/workspaces/common/workspaces";
8+
import { IWorkspacesMainService, IWorkspaceIdentifier, IStoredWorkspace, WORKSPACE_EXTENSION, IWorkspaceSavedEvent, UNTITLED_WORKSPACE_NAME } from "vs/platform/workspaces/common/workspaces";
99
import { TPromise } from "vs/base/common/winjs.base";
1010
import { isParent } from "vs/platform/files/common/files";
1111
import { IEnvironmentService } from "vs/platform/environment/common/environment";
1212
import { extname, join, dirname } from "path";
1313
import { mkdirp, writeFile } from "vs/base/node/pfs";
1414
import { readFileSync } from "fs";
1515
import { isLinux } from "vs/base/common/platform";
16-
import { copy, delSync } from "vs/base/node/extfs";
16+
import { copy, delSync, readdirSync } from "vs/base/node/extfs";
1717
import { nfcall } from "vs/base/common/async";
1818
import Event, { Emitter } from "vs/base/common/event";
1919
import { ILogService } from "vs/platform/log/common/log";
2020
import { isEqual } from "vs/base/common/paths";
21+
import { coalesce } from "vs/base/common/arrays";
2122

2223
export class WorkspacesMainService implements IWorkspacesMainService {
2324

@@ -79,7 +80,7 @@ export class WorkspacesMainService implements IWorkspacesMainService {
7980

8081
const workspaceId = this.nextWorkspaceId();
8182
const workspaceConfigFolder = join(this.workspacesHome, workspaceId);
82-
const workspaceConfigPath = join(workspaceConfigFolder, 'workspace.json');
83+
const workspaceConfigPath = join(workspaceConfigFolder, UNTITLED_WORKSPACE_NAME);
8384

8485
return mkdirp(workspaceConfigFolder).then(() => {
8586
const storedWorkspace: IStoredWorkspace = {
@@ -130,9 +131,39 @@ export class WorkspacesMainService implements IWorkspacesMainService {
130131
}
131132

132133
// Delete from disk
133-
delSync(dirname(workspace.configPath));
134+
this.doDeleteUntitledWorkspaceSync(workspace.configPath);
134135

135136
// Event
136137
this._onWorkspaceDeleted.fire(workspace);
137138
}
139+
140+
private doDeleteUntitledWorkspaceSync(configPath: string): void {
141+
try {
142+
delSync(dirname(configPath));
143+
} catch (error) {
144+
this.logService.log(`Unable to delete untitled workspace ${configPath} (${error}).`);
145+
}
146+
}
147+
148+
public getUntitledWorkspacesSync(): IWorkspaceIdentifier[] {
149+
let untitledWorkspacePaths: string[] = [];
150+
try {
151+
untitledWorkspacePaths = readdirSync(this.workspacesHome).map(folder => join(this.workspacesHome, folder, UNTITLED_WORKSPACE_NAME));
152+
} catch (error) {
153+
this.logService.log(`Unable to read folders in ${this.workspacesHome} (${error}).`);
154+
}
155+
156+
const untitledWorkspaces: IWorkspaceIdentifier[] = coalesce(untitledWorkspacePaths.map(untitledWorkspacePath => {
157+
const workspace = this.resolveWorkspaceSync(untitledWorkspacePath);
158+
if (!workspace) {
159+
this.doDeleteUntitledWorkspaceSync(untitledWorkspacePath);
160+
161+
return null; // invalid workspace
162+
}
163+
164+
return { id: workspace.id, configPath: untitledWorkspacePath };
165+
}));
166+
167+
return untitledWorkspaces;
168+
}
138169
}

src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ suite('WorkspacesMainService', () => {
9393
const resolved = service.resolveWorkspaceSync(workspace.configPath);
9494
assert.deepEqual(resolved, { id: workspace.id, folders: [process.cwd(), os.tmpdir()] });
9595

96+
fs.writeFileSync(workspace.configPath, JSON.stringify({ id: 'someid' })); // invalid workspace
97+
const resolvedInvalid = service.resolveWorkspaceSync(workspace.configPath);
98+
assert.ok(!resolvedInvalid);
99+
96100
done();
97101
});
98102
});
@@ -162,4 +166,58 @@ suite('WorkspacesMainService', () => {
162166
});
163167
});
164168
});
169+
170+
test('deleteUntitledWorkspaceSync (untitled)', done => {
171+
return service.createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => {
172+
assert.ok(fs.existsSync(workspace.configPath));
173+
174+
service.deleteUntitledWorkspaceSync(workspace);
175+
176+
assert.ok(!fs.existsSync(workspace.configPath));
177+
178+
done();
179+
});
180+
});
181+
182+
test('deleteUntitledWorkspaceSync (saved)', done => {
183+
return service.createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => {
184+
const workspaceConfigPath = path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`);
185+
186+
return service.saveWorkspace(workspace, workspaceConfigPath).then(savedWorkspace => {
187+
assert.ok(fs.existsSync(savedWorkspace.configPath));
188+
189+
service.deleteUntitledWorkspaceSync(savedWorkspace);
190+
191+
assert.ok(fs.existsSync(savedWorkspace.configPath));
192+
193+
done();
194+
});
195+
});
196+
});
197+
198+
test('getUntitledWorkspaceSync', done => {
199+
let untitled = service.getUntitledWorkspacesSync();
200+
assert.equal(0, untitled.length);
201+
202+
return service.createWorkspace([process.cwd(), os.tmpdir()]).then(untitledOne => {
203+
untitled = service.getUntitledWorkspacesSync();
204+
205+
assert.equal(1, untitled.length);
206+
assert.equal(untitledOne.id, untitled[0].id);
207+
208+
return service.createWorkspace([process.cwd(), os.tmpdir()]).then(untitledTwo => {
209+
untitled = service.getUntitledWorkspacesSync();
210+
211+
assert.equal(2, untitled.length);
212+
213+
service.deleteUntitledWorkspaceSync(untitledOne);
214+
service.deleteUntitledWorkspaceSync(untitledTwo);
215+
216+
untitled = service.getUntitledWorkspacesSync();
217+
assert.equal(0, untitled.length);
218+
219+
done();
220+
});
221+
});
222+
});
165223
});

0 commit comments

Comments
 (0)