Skip to content

Commit a221cdd

Browse files
committed
adopt stored workspaceIdentifiers: back, history , windowsState
1 parent 475cb9e commit a221cdd

11 files changed

Lines changed: 221 additions & 93 deletions

File tree

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

Lines changed: 61 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
3131
import { mnemonicButtonLabel } from 'vs/base/common/labels';
3232
import { Schemas } from 'vs/base/common/network';
3333
import { normalizeNFC } from 'vs/base/common/normalization';
34-
import { URI } from 'vs/base/common/uri';
34+
import { URI, UriComponents } from 'vs/base/common/uri';
3535
import { Queue, timeout } from 'vs/base/common/async';
3636
import { exists } from 'vs/base/node/pfs';
3737
import { getComparisonKey, isEqual, normalizePath, basename as resourcesBasename, fsPath } from 'vs/base/common/resources';
@@ -55,16 +55,32 @@ interface IWindowState {
5555
uiState: ISingleWindowState;
5656
}
5757

58-
interface IBackwardCompatibleWindowState extends IWindowState {
59-
folderPath?: string;
60-
}
61-
6258
interface IWindowsState {
6359
lastActiveWindow?: IWindowState;
6460
lastPluginDevelopmentHostWindow?: IWindowState;
6561
openedWindows: IWindowState[];
6662
}
6763

64+
interface ISerializedWindowsState {
65+
lastActiveWindow?: ISerializedWindowState;
66+
lastPluginDevelopmentHostWindow?: ISerializedWindowState;
67+
openedWindows: ISerializedWindowState[];
68+
}
69+
70+
interface ISerializedWindowState {
71+
workspaceIdentifier?: { id: string; configURIPath: string };
72+
folder?: string;
73+
backupPath: string;
74+
remoteAuthority?: string;
75+
uiState: ISingleWindowState;
76+
77+
// deprecated
78+
folderUri?: UriComponents;
79+
folderPath?: string;
80+
workspace?: { id: string; configPath: string };
81+
82+
}
83+
6884
type RestoreWindowsSetting = 'all' | 'folders' | 'one' | 'none';
6985

7086
interface IOpenBrowserWindowOptions {
@@ -175,27 +191,36 @@ export class WindowsManager implements IWindowsMainService {
175191
}
176192

177193
private getWindowsState(): IWindowsState {
178-
const windowsState = this.stateService.getItem<IWindowsState>(WindowsManager.windowsStateStorageKey) || { openedWindows: [] };
194+
const result: IWindowsState = { openedWindows: [] };
195+
const windowsState = this.stateService.getItem<ISerializedWindowsState>(WindowsManager.windowsStateStorageKey) || { openedWindows: [] };
196+
179197
if (windowsState.lastActiveWindow) {
180-
windowsState.lastActiveWindow = this.revive(windowsState.lastActiveWindow);
198+
result.lastActiveWindow = this.deserialize(windowsState.lastActiveWindow);
181199
}
182200
if (windowsState.lastPluginDevelopmentHostWindow) {
183-
windowsState.lastPluginDevelopmentHostWindow = this.revive(windowsState.lastPluginDevelopmentHostWindow);
201+
result.lastPluginDevelopmentHostWindow = this.deserialize(windowsState.lastPluginDevelopmentHostWindow);
184202
}
185-
if (windowsState.openedWindows) {
186-
windowsState.openedWindows = windowsState.openedWindows.map(windowState => this.revive(windowState));
203+
if (Array.isArray(windowsState.openedWindows)) {
204+
result.openedWindows = windowsState.openedWindows.map(windowState => this.deserialize(windowState));
187205
}
188-
return windowsState;
206+
return result;
189207
}
190208

191-
private revive(windowState: IWindowState): IWindowState {
192-
if (windowState.folderUri) {
193-
windowState.folderUri = URI.revive(windowState.folderUri);
209+
private deserialize(windowState: ISerializedWindowState): IWindowState {
210+
const result: IWindowState = { backupPath: windowState.backupPath, remoteAuthority: windowState.remoteAuthority, uiState: windowState.uiState };
211+
if (windowState.folder) {
212+
result.folderUri = URI.parse(windowState.folder);
213+
} else if (windowState.folderUri) {
214+
result.folderUri = URI.revive(windowState.folderUri);
215+
} else if (windowState.folderPath) {
216+
result.folderUri = URI.file(windowState.folderPath);
194217
}
195-
if ((<IBackwardCompatibleWindowState>windowState).folderPath) {
196-
windowState.folderUri = URI.file((<IBackwardCompatibleWindowState>windowState).folderPath);
218+
if (windowState.workspaceIdentifier) {
219+
result.workspace = { id: windowState.workspaceIdentifier.id, configPath: URI.parse(windowState.workspaceIdentifier.configURIPath) };
220+
} else if (windowState.workspace) {
221+
result.workspace = { id: windowState.workspace.id, configPath: URI.file(windowState.workspace.configPath) };
197222
}
198-
return windowState;
223+
return result;
199224
}
200225

201226
ready(initialUserEnv: IProcessEnvironment): void {
@@ -316,7 +341,25 @@ export class WindowsManager implements IWindowsMainService {
316341
}
317342

318343
// Persist
319-
this.stateService.setItem(WindowsManager.windowsStateStorageKey, currentWindowsState);
344+
this.stateService.setItem(WindowsManager.windowsStateStorageKey, this.serializeWindowsState(currentWindowsState));
345+
}
346+
347+
private serializeWindowsState(windowsState: IWindowsState): ISerializedWindowsState {
348+
return {
349+
lastActiveWindow: windowsState.lastActiveWindow && this.serialize(windowsState.lastActiveWindow),
350+
lastPluginDevelopmentHostWindow: windowsState.lastPluginDevelopmentHostWindow && this.serialize(windowsState.lastPluginDevelopmentHostWindow),
351+
openedWindows: windowsState.openedWindows.map(ws => this.serialize(ws))
352+
};
353+
}
354+
355+
private serialize(windowState: IWindowState): ISerializedWindowState {
356+
return {
357+
workspaceIdentifier: windowState.workspace && { id: windowState.workspace.id, configURIPath: windowState.workspace.configPath.toString() },
358+
folder: windowState.folderUri && windowState.folderUri.toString(),
359+
backupPath: windowState.backupPath,
360+
remoteAuthority: windowState.remoteAuthority,
361+
uiState: windowState.uiState
362+
};
320363
}
321364

322365
// See note on #onBeforeShutdown() for details how these events are flowing

src/vs/platform/backup/common/backup.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,17 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
77
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
88
import { URI } from 'vs/base/common/uri';
99

10+
export interface ISerializedWorkspace { id: string; configURIPath: string; }
11+
1012
export interface IBackupWorkspacesFormat {
11-
rootWorkspaces: IWorkspaceIdentifier[];
13+
rootURIWorkspaces: ISerializedWorkspace[];
1214
folderURIWorkspaces: string[];
1315
emptyWorkspaceInfos: IEmptyWindowBackupInfo[];
1416

1517
// deprecated
1618
folderWorkspaces?: string[]; // use folderURIWorkspaces instead
1719
emptyWorkspaces?: string[];
20+
rootWorkspaces?: { id: string, configPath: string }[]; // use rootURIWorkspaces instead
1821
}
1922

2023
export const IBackupMainService = createDecorator<IBackupMainService>('backupMainService');

src/vs/platform/backup/electron-main/backupMainService.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,17 @@ export class BackupMainService implements IBackupMainService {
6060
}
6161

6262
// read workspace backups
63-
this.rootWorkspaces = await this.validateWorkspaces(backups.rootWorkspaces);
63+
let rootWorkspaces: IWorkspaceIdentifier[] = [];
64+
try {
65+
if (Array.isArray(backups.rootURIWorkspaces)) {
66+
rootWorkspaces = backups.rootURIWorkspaces.map(f => ({ id: f.id, configPath: URI.parse(f.configURIPath) }));
67+
} else if (Array.isArray(backups.rootWorkspaces)) {
68+
rootWorkspaces = backups.rootWorkspaces.map(f => ({ id: f.id, configPath: URI.file(f.configPath) }));
69+
}
70+
} catch (e) {
71+
// ignore URI parsing exceptions
72+
}
73+
this.rootWorkspaces = await this.validateWorkspaces(rootWorkspaces);
6474

6575
// read folder backups
6676
let workspaceFolders: URI[] = [];
@@ -421,7 +431,7 @@ export class BackupMainService implements IBackupMainService {
421431

422432
private serializeBackups(): IBackupWorkspacesFormat {
423433
return {
424-
rootWorkspaces: this.rootWorkspaces,
434+
rootURIWorkspaces: this.rootWorkspaces.map(f => ({ id: f.id, configURIPath: f.configPath.toString() })),
425435
folderURIWorkspaces: this.folderWorkspaces.map(f => f.toString()),
426436
emptyWorkspaceInfos: this.emptyWorkspaces,
427437
emptyWorkspaces: this.emptyWorkspaces.map(info => info.backupFolder)

src/vs/platform/backup/test/electron-main/backupMainService.test.ts

Lines changed: 51 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { URI as Uri, URI } from 'vs/base/common/uri';
1313
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
1414
import { parseArgs } from 'vs/platform/environment/node/argv';
1515
import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService';
16-
import { IBackupWorkspacesFormat } from 'vs/platform/backup/common/backup';
16+
import { IBackupWorkspacesFormat, ISerializedWorkspace } from 'vs/platform/backup/common/backup';
1717
import { HotExitConfiguration } from 'vs/platform/files/common/files';
1818
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
1919
import { ConsoleLogMainService } from 'vs/platform/log/common/log';
@@ -64,6 +64,13 @@ suite('BackupMainService', () => {
6464
};
6565
}
6666

67+
function toSerializedWorkspace(ws: IWorkspaceIdentifier): ISerializedWorkspace {
68+
return {
69+
id: ws.id,
70+
configURIPath: ws.configPath.toString()
71+
};
72+
}
73+
6774
async function ensureFolderExists(uri: Uri): Promise<void> {
6875
if (!fs.existsSync(uri.fsPath)) {
6976
fs.mkdirSync(uri.fsPath);
@@ -251,12 +258,8 @@ suite('BackupMainService', () => {
251258
suite('migrate folderPath to folderURI', () => {
252259

253260
test('migration makes sure to preserve existing backups', async () => {
254-
if (platform.isLinux) {
255-
return; // TODO:Martin #54483 fix tests
256-
}
257-
258-
let path1 = path.join(parentDir, 'folder1').toLowerCase();
259-
let path2 = path.join(parentDir, 'folder2').toUpperCase();
261+
let path1 = path.join(parentDir, 'folder1');
262+
let path2 = path.join(parentDir, 'FOLDER2');
260263
let uri1 = Uri.file(path1);
261264
let uri2 = Uri.file(path2);
262265

@@ -387,10 +390,32 @@ suite('BackupMainService', () => {
387390
assert.deepEqual(service.getWorkspaceBackups(), []);
388391
});
389392

393+
test('getWorkspaceBackups() should return [] when rootURIWorkspaces in workspaces.json is not a object array', async () => {
394+
fs.writeFileSync(backupWorkspacesPath, '{"rootURIWorkspaces":{}}');
395+
await service.initialize();
396+
assert.deepEqual(service.getWorkspaceBackups(), []);
397+
fs.writeFileSync(backupWorkspacesPath, '{"rootURIWorkspaces":{"foo": ["bar"]}}');
398+
await service.initialize();
399+
assert.deepEqual(service.getWorkspaceBackups(), []);
400+
fs.writeFileSync(backupWorkspacesPath, '{"rootURIWorkspaces":{"foo": []}}');
401+
await service.initialize();
402+
assert.deepEqual(service.getWorkspaceBackups(), []);
403+
fs.writeFileSync(backupWorkspacesPath, '{"rootURIWorkspaces":{"foo": "bar"}}');
404+
await service.initialize();
405+
assert.deepEqual(service.getWorkspaceBackups(), []);
406+
fs.writeFileSync(backupWorkspacesPath, '{"rootURIWorkspaces":"foo"}');
407+
await service.initialize();
408+
assert.deepEqual(service.getWorkspaceBackups(), []);
409+
fs.writeFileSync(backupWorkspacesPath, '{"rootURIWorkspaces":1}');
410+
await service.initialize();
411+
assert.deepEqual(service.getWorkspaceBackups(), []);
412+
});
413+
390414
test('getWorkspaceBackups() should return [] when files.hotExit = "onExitAndWindowClose"', async () => {
391-
service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath.toUpperCase()));
415+
const upperFooPath = fooFile.fsPath.toUpperCase();
416+
service.registerWorkspaceBackupSync(toWorkspace(upperFooPath));
392417
assert.equal(service.getWorkspaceBackups().length, 1);
393-
assert.deepEqual(service.getWorkspaceBackups().map(r => r.configPath), [fooFile.fsPath.toUpperCase()]);
418+
assertEqualUris(service.getWorkspaceBackups().map(r => r.configPath), [URI.file(upperFooPath)]);
394419
configService.setUserConfiguration('files.hotExit', HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE);
395420
await service.initialize();
396421
assert.deepEqual(service.getWorkspaceBackups(), []);
@@ -447,7 +472,7 @@ suite('BackupMainService', () => {
447472
await ensureFolderExists(existingTestFolder1);
448473

449474
const workspacesJson: IBackupWorkspacesFormat = {
450-
rootWorkspaces: [],
475+
rootURIWorkspaces: [],
451476
folderURIWorkspaces: [existingTestFolder1.toString(), existingTestFolder1.toString()],
452477
emptyWorkspaceInfos: []
453478
};
@@ -464,7 +489,7 @@ suite('BackupMainService', () => {
464489
await ensureFolderExists(existingTestFolder1);
465490

466491
const workspacesJson: IBackupWorkspacesFormat = {
467-
rootWorkspaces: [],
492+
rootURIWorkspaces: [],
468493
folderURIWorkspaces: [existingTestFolder1.toString(), existingTestFolder1.toString().toLowerCase()],
469494
emptyWorkspaceInfos: []
470495
};
@@ -487,7 +512,7 @@ suite('BackupMainService', () => {
487512
const workspace3 = await ensureWorkspaceExists(toWorkspace(workspacePath.toLowerCase()));
488513

489514
const workspacesJson: IBackupWorkspacesFormat = {
490-
rootWorkspaces: [workspace1, workspace2, workspace3],
515+
rootURIWorkspaces: [workspace1, workspace2, workspace3].map(toSerializedWorkspace),
491516
folderURIWorkspaces: [],
492517
emptyWorkspaceInfos: []
493518
};
@@ -496,11 +521,11 @@ suite('BackupMainService', () => {
496521

497522
const buffer = await pfs.readFile(backupWorkspacesPath, 'utf-8');
498523
const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
499-
assert.equal(json.rootWorkspaces.length, platform.isLinux ? 3 : 1);
524+
assert.equal(json.rootURIWorkspaces.length, platform.isLinux ? 3 : 1);
500525
if (platform.isLinux) {
501-
assert.deepEqual(json.rootWorkspaces.map(r => r.configPath), [workspacePath, workspacePath.toUpperCase(), workspacePath.toLowerCase()]);
526+
assert.deepEqual(json.rootURIWorkspaces.map(r => r.configURIPath), [URI.file(workspacePath), URI.file(workspacePath.toUpperCase()), URI.file(workspacePath.toLowerCase())]);
502527
} else {
503-
assert.deepEqual(json.rootWorkspaces.map(r => r.configPath), [workspacePath], 'should return the first duplicated entry');
528+
assert.deepEqual(json.rootURIWorkspaces.map(r => r.configURIPath), [URI.file(workspacePath)], 'should return the first duplicated entry');
504529
}
505530
});
506531
});
@@ -521,16 +546,16 @@ suite('BackupMainService', () => {
521546
const ws2 = toWorkspace(barFile.fsPath);
522547
service.registerWorkspaceBackupSync(ws2);
523548

524-
assert.deepEqual(service.getWorkspaceBackups().map(b => b.configPath), [fooFile.fsPath, barFile.fsPath]);
549+
assertEqualUris(service.getWorkspaceBackups().map(b => b.configPath), [fooFile, barFile]);
525550
assert.equal(ws1.id, service.getWorkspaceBackups()[0].id);
526551
assert.equal(ws2.id, service.getWorkspaceBackups()[1].id);
527552

528553
const buffer = await pfs.readFile(backupWorkspacesPath, 'utf-8');
529554
const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
530555

531-
assert.deepEqual(json.rootWorkspaces.map(b => b.configPath), [fooFile.fsPath, barFile.fsPath]);
532-
assert.equal(ws1.id, json.rootWorkspaces[0].id);
533-
assert.equal(ws2.id, json.rootWorkspaces[1].id);
556+
assert.deepEqual(json.rootURIWorkspaces.map(b => b.configURIPath), [fooFile.toString(), barFile.toString()]);
557+
assert.equal(ws1.id, json.rootURIWorkspaces[0].id);
558+
assert.equal(ws2.id, json.rootURIWorkspaces[1].id);
534559
});
535560
});
536561

@@ -544,11 +569,12 @@ suite('BackupMainService', () => {
544569
});
545570

546571
test('should always store the workspace path in workspaces.json using the case given, regardless of whether the file system is case-sensitive (root workspace)', () => {
547-
service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath.toUpperCase()));
548-
assert.deepEqual(service.getWorkspaceBackups().map(b => b.configPath), [fooFile.fsPath.toUpperCase()]);
572+
const upperFooPath = fooFile.fsPath.toUpperCase();
573+
service.registerWorkspaceBackupSync(toWorkspace(upperFooPath));
574+
assertEqualUris(service.getWorkspaceBackups().map(b => b.configPath), [URI.file(upperFooPath)]);
549575
return pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => {
550576
const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
551-
assert.deepEqual(json.rootWorkspaces.map(b => b.configPath), [fooFile.fsPath.toUpperCase()]);
577+
assert.deepEqual(json.rootURIWorkspaces.map(b => b.configURIPath), [URI.file(upperFooPath).toString()]);
552578
});
553579
});
554580

@@ -576,11 +602,11 @@ suite('BackupMainService', () => {
576602
service.unregisterWorkspaceBackupSync(ws1);
577603
return pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => {
578604
const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
579-
assert.deepEqual(json.rootWorkspaces.map(r => r.configPath), [barFile.fsPath]);
605+
assert.deepEqual(json.rootURIWorkspaces.map(r => r.configURIPath), [barFile.toString()]);
580606
service.unregisterWorkspaceBackupSync(ws2);
581607
return pfs.readFile(backupWorkspacesPath, 'utf-8').then(content => {
582608
const json2 = <IBackupWorkspacesFormat>JSON.parse(content);
583-
assert.deepEqual(json2.rootWorkspaces, []);
609+
assert.deepEqual(json2.rootURIWorkspaces, []);
584610
});
585611
});
586612
});
@@ -604,7 +630,7 @@ suite('BackupMainService', () => {
604630

605631
await ensureFolderExists(existingTestFolder1); // make sure backup folder exists, so the folder is not removed on loadSync
606632

607-
const workspacesJson: IBackupWorkspacesFormat = { rootWorkspaces: [], folderURIWorkspaces: [existingTestFolder1.toString()], emptyWorkspaceInfos: [] };
633+
const workspacesJson: IBackupWorkspacesFormat = { rootURIWorkspaces: [], folderURIWorkspaces: [existingTestFolder1.toString()], emptyWorkspaceInfos: [] };
608634
await pfs.writeFile(backupWorkspacesPath, JSON.stringify(workspacesJson));
609635
await service.initialize();
610636
service.unregisterFolderBackupSync(barFile);

0 commit comments

Comments
 (0)