Skip to content

Commit 4e4cb81

Browse files
author
Benjamin Pasero
committed
debt - merge workspaces and history into one
1 parent fc7f575 commit 4e4cb81

27 files changed

Lines changed: 419 additions & 558 deletions

File tree

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ import { ProxyAuthHandler } from 'vs/code/electron-main/auth';
3838
import { Disposable } from 'vs/base/common/lifecycle';
3939
import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows';
4040
import { URI } from 'vs/base/common/uri';
41-
import { WorkspacesChannel } from 'vs/platform/workspaces/electron-main/workspacesIpc';
42-
import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
41+
import { hasWorkspaceFileExtension, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
42+
import { WorkspacesService } from 'vs/platform/workspaces/electron-main/workspacesService';
4343
import { getMachineId } from 'vs/base/node/id';
4444
import { Win32UpdateService } from 'vs/platform/update/electron-main/updateService.win32';
4545
import { LinuxUpdateService } from 'vs/platform/update/electron-main/updateService.linux';
@@ -457,6 +457,7 @@ export class CodeApplication extends Disposable {
457457

458458
services.set(IIssueService, new SyncDescriptor(IssueMainService, [machineId, this.userEnv]));
459459
services.set(IElectronService, new SyncDescriptor(ElectronMainService));
460+
services.set(IWorkspacesService, new SyncDescriptor(WorkspacesService));
460461
services.set(IMenubarService, new SyncDescriptor(MenubarMainService));
461462

462463
const storageMainService = new StorageMainService(this.logService, this.environmentService);
@@ -551,8 +552,8 @@ export class CodeApplication extends Disposable {
551552
const sharedProcessChannel = createChannelReceiver(sharedProcessMainService);
552553
electronIpcServer.registerChannel('sharedProcess', sharedProcessChannel);
553554

554-
const workspacesMainService = accessor.get(IWorkspacesMainService);
555-
const workspacesChannel = new WorkspacesChannel(workspacesMainService, accessor.get(IWindowsMainService));
555+
const workspacesService = accessor.get(IWorkspacesService);
556+
const workspacesChannel = createChannelReceiver(workspacesService);
556557
electronIpcServer.registerChannel('workspaces', workspacesChannel);
557558

558559
const menubarService = accessor.get(IMenubarService);

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,9 @@ import { Event as CommonEvent, Emitter } from 'vs/base/common/event';
2525
import product from 'vs/platform/product/common/product';
2626
import { ITelemetryService, ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
2727
import { IWindowsMainService, IOpenConfiguration, IWindowsCountChangedEvent, ICodeWindow, IWindowState as ISingleWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows';
28-
import { IRecent } from 'vs/platform/workspaces/common/workspacesHistory';
2928
import { IWorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-main/workspacesHistoryMainService';
3029
import { IProcessEnvironment, isMacintosh, isWindows } from 'vs/base/common/platform';
31-
import { IWorkspaceIdentifier, WORKSPACE_FILTER, isSingleFolderWorkspaceIdentifier, hasWorkspaceFileExtension, IEnterWorkspaceResult } from 'vs/platform/workspaces/common/workspaces';
30+
import { IWorkspaceIdentifier, WORKSPACE_FILTER, isSingleFolderWorkspaceIdentifier, hasWorkspaceFileExtension, IEnterWorkspaceResult, IRecent } from 'vs/platform/workspaces/common/workspaces';
3231
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
3332
import { mnemonicButtonLabel } from 'vs/base/common/labels';
3433
import { Schemas } from 'vs/base/common/network';

src/vs/platform/electron/electron-main/electronMainService.ts

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ import { IElectronService } from 'vs/platform/electron/node/electron';
1414
import { ISerializableCommandAction } from 'vs/platform/actions/common/actions';
1515
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
1616
import { AddFirstParameterToFunctions } from 'vs/base/common/types';
17-
import { IWorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-main/workspacesHistoryMainService';
18-
import { IRecentlyOpened, IRecent } from 'vs/platform/workspaces/common/workspacesHistory';
19-
import { URI } from 'vs/base/common/uri';
2017

2118
export class ElectronMainService implements AddFirstParameterToFunctions<IElectronService, Promise<any> /* only methods, not events */, number /* window ID */> {
2219

@@ -25,8 +22,7 @@ export class ElectronMainService implements AddFirstParameterToFunctions<IElectr
2522
constructor(
2623
@IWindowsMainService private readonly windowsMainService: IWindowsMainService,
2724
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
28-
@IEnvironmentService private readonly environmentService: IEnvironmentService,
29-
@IWorkspacesHistoryMainService private readonly workspacesHistoryMainService: IWorkspacesHistoryMainService
25+
@IEnvironmentService private readonly environmentService: IEnvironmentService
3026
) {
3127
}
3228

@@ -332,33 +328,6 @@ export class ElectronMainService implements AddFirstParameterToFunctions<IElectr
332328

333329
//#endregion
334330

335-
//#region Workspaces History
336-
337-
readonly onRecentlyOpenedChange = this.workspacesHistoryMainService.onRecentlyOpenedChange;
338-
339-
async getRecentlyOpened(windowId: number): Promise<IRecentlyOpened> {
340-
const window = this.windowsMainService.getWindowById(windowId);
341-
if (window) {
342-
return this.workspacesHistoryMainService.getRecentlyOpened(window.config.workspace, window.config.folderUri, window.config.filesToOpenOrCreate);
343-
}
344-
345-
return this.workspacesHistoryMainService.getRecentlyOpened();
346-
}
347-
348-
async addRecentlyOpened(windowId: number, recents: IRecent[]): Promise<void> {
349-
return this.workspacesHistoryMainService.addRecentlyOpened(recents);
350-
}
351-
352-
async removeFromRecentlyOpened(windowId: number, paths: URI[]): Promise<void> {
353-
return this.workspacesHistoryMainService.removeFromRecentlyOpened(paths);
354-
}
355-
356-
async clearRecentlyOpened(windowId: number): Promise<void> {
357-
return this.workspacesHistoryMainService.clearRecentlyOpened();
358-
}
359-
360-
//#endregion
361-
362331
//#region Debug
363332

364333
// TODO@Isidor move into debug IPC channel (https://github.com/microsoft/vscode/issues/81060)

src/vs/platform/electron/node/electron.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
99
import { IWindowOpenable, IOpenInWindowOptions, IOpenEmptyWindowOptions, IOpenedWindow } from 'vs/platform/windows/common/windows';
1010
import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs';
1111
import { ISerializableCommandAction } from 'vs/platform/actions/common/actions';
12-
import { IRecentlyOpened, IRecent } from 'vs/platform/workspaces/common/workspacesHistory';
13-
import { URI } from 'vs/base/common/uri';
1412
import { ParsedArgs } from 'vscode-minimist';
1513
import { IProcessEnvironment } from 'vs/base/common/platform';
1614

@@ -89,13 +87,6 @@ export interface IElectronService {
8987
// Connectivity
9088
resolveProxy(url: string): Promise<string | undefined>;
9189

92-
// Workspaces History
93-
readonly onRecentlyOpenedChange: Event<void>;
94-
getRecentlyOpened(): Promise<IRecentlyOpened>;
95-
addRecentlyOpened(recents: IRecent[]): Promise<void>;
96-
removeFromRecentlyOpened(paths: URI[]): Promise<void>;
97-
clearRecentlyOpened(): Promise<void>;
98-
9990
// Debug (TODO@Isidor move into debug IPC channel (https://github.com/microsoft/vscode/issues/81060)
10091
openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise<void>;
10192
}

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

Lines changed: 183 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,68 @@ import { normalizeDriveLetter } from 'vs/base/common/labels';
1717
import { toSlashes } from 'vs/base/common/extpath';
1818
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
1919
import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts';
20+
import { ILogService } from 'vs/platform/log/common/log';
21+
import { Event as CommonEvent } from 'vs/base/common/event';
2022

2123
export const WORKSPACE_EXTENSION = 'code-workspace';
2224
export const WORKSPACE_FILTER = [{ name: localize('codeWorkspace', "Code Workspace"), extensions: [WORKSPACE_EXTENSION] }];
2325
export const UNTITLED_WORKSPACE_NAME = 'workspace.json';
2426

27+
export const IWorkspacesService = createDecorator<IWorkspacesService>('workspacesService');
28+
29+
export interface IWorkspacesService {
30+
31+
_serviceBrand: undefined;
32+
33+
readonly onRecentlyOpenedChange: CommonEvent<void>;
34+
35+
// Management
36+
enterWorkspace(path: URI): Promise<IEnterWorkspaceResult | undefined>;
37+
createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise<IWorkspaceIdentifier>;
38+
deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise<void>;
39+
getWorkspaceIdentifier(workspacePath: URI): Promise<IWorkspaceIdentifier>;
40+
41+
// History
42+
addRecentlyOpened(recents: IRecent[]): Promise<void>;
43+
removeFromRecentlyOpened(workspaces: URI[]): Promise<void>;
44+
clearRecentlyOpened(): Promise<void>;
45+
getRecentlyOpened(): Promise<IRecentlyOpened>;
46+
}
47+
48+
export interface IRecentlyOpened {
49+
workspaces: Array<IRecentWorkspace | IRecentFolder>;
50+
files: IRecentFile[];
51+
}
52+
53+
export type IRecent = IRecentWorkspace | IRecentFolder | IRecentFile;
54+
55+
export interface IRecentWorkspace {
56+
workspace: IWorkspaceIdentifier;
57+
label?: string;
58+
}
59+
60+
export interface IRecentFolder {
61+
folderUri: ISingleFolderWorkspaceIdentifier;
62+
label?: string;
63+
}
64+
65+
export interface IRecentFile {
66+
fileUri: URI;
67+
label?: string;
68+
}
69+
70+
export function isRecentWorkspace(curr: IRecent): curr is IRecentWorkspace {
71+
return curr.hasOwnProperty('workspace');
72+
}
73+
74+
export function isRecentFolder(curr: IRecent): curr is IRecentFolder {
75+
return curr.hasOwnProperty('folderUri');
76+
}
77+
78+
export function isRecentFile(curr: IRecent): curr is IRecentFile {
79+
return curr.hasOwnProperty('fileUri');
80+
}
81+
2582
/**
2683
* A single folder workspace identifier is just the path to the folder.
2784
*/
@@ -96,21 +153,6 @@ export interface IEnterWorkspaceResult {
96153
backupPath?: string;
97154
}
98155

99-
export const IWorkspacesService = createDecorator<IWorkspacesService>('workspacesService');
100-
101-
export interface IWorkspacesService {
102-
103-
_serviceBrand: undefined;
104-
105-
enterWorkspace(path: URI): Promise<IEnterWorkspaceResult | undefined>;
106-
107-
createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise<IWorkspaceIdentifier>;
108-
109-
deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise<void>;
110-
111-
getWorkspaceIdentifier(workspacePath: URI): Promise<IWorkspaceIdentifier>;
112-
}
113-
114156
export function isSingleFolderWorkspaceIdentifier(obj: any): obj is ISingleFolderWorkspaceIdentifier {
115157
return obj instanceof URI;
116158
}
@@ -267,3 +309,129 @@ export function useSlashForPath(storedFolders: IStoredWorkspaceFolder[]): boolea
267309
}
268310
return true;
269311
}
312+
313+
//#region Workspace Storage
314+
315+
interface ISerializedRecentlyOpened {
316+
workspaces3: Array<ISerializedWorkspace | string>; // workspace or URI.toString() // added in 1.32
317+
workspaceLabels?: Array<string | null>; // added in 1.33
318+
files2: string[]; // files as URI.toString() // added in 1.32
319+
fileLabels?: Array<string | null>; // added in 1.33
320+
}
321+
322+
interface ILegacySerializedRecentlyOpened {
323+
workspaces2: Array<ILegacySerializedWorkspace | string>; // legacy, configPath as file path
324+
workspaces: Array<ILegacySerializedWorkspace | string | UriComponents>; // legacy (UriComponents was also supported for a few insider builds)
325+
files: string[]; // files as paths
326+
}
327+
328+
interface ISerializedWorkspace { id: string; configURIPath: string; }
329+
interface ILegacySerializedWorkspace { id: string; configPath: string; }
330+
331+
function isLegacySerializedWorkspace(curr: any): curr is ILegacySerializedWorkspace {
332+
return typeof curr === 'object' && typeof curr['id'] === 'string' && typeof curr['configPath'] === 'string';
333+
}
334+
335+
function isUriComponents(curr: any): curr is UriComponents {
336+
return curr && typeof curr['path'] === 'string' && typeof curr['scheme'] === 'string';
337+
}
338+
339+
export type RecentlyOpenedStorageData = object;
340+
341+
export function restoreRecentlyOpened(data: RecentlyOpenedStorageData | undefined, logService: ILogService): IRecentlyOpened {
342+
const result: IRecentlyOpened = { workspaces: [], files: [] };
343+
if (data) {
344+
const restoreGracefully = function <T>(entries: T[], func: (entry: T, index: number) => void) {
345+
for (let i = 0; i < entries.length; i++) {
346+
try {
347+
func(entries[i], i);
348+
} catch (e) {
349+
logService.warn(`Error restoring recent entry ${JSON.stringify(entries[i])}: ${e.toString()}. Skip entry.`);
350+
}
351+
}
352+
};
353+
354+
const storedRecents = data as ISerializedRecentlyOpened & ILegacySerializedRecentlyOpened;
355+
if (Array.isArray(storedRecents.workspaces3)) {
356+
restoreGracefully(storedRecents.workspaces3, (workspace, i) => {
357+
const label: string | undefined = (Array.isArray(storedRecents.workspaceLabels) && storedRecents.workspaceLabels[i]) || undefined;
358+
if (typeof workspace === 'object' && typeof workspace.id === 'string' && typeof workspace.configURIPath === 'string') {
359+
result.workspaces.push({ label, workspace: { id: workspace.id, configPath: URI.parse(workspace.configURIPath) } });
360+
} else if (typeof workspace === 'string') {
361+
result.workspaces.push({ label, folderUri: URI.parse(workspace) });
362+
}
363+
});
364+
} else if (Array.isArray(storedRecents.workspaces2)) {
365+
restoreGracefully(storedRecents.workspaces2, workspace => {
366+
if (typeof workspace === 'object' && typeof workspace.id === 'string' && typeof workspace.configPath === 'string') {
367+
result.workspaces.push({ workspace: { id: workspace.id, configPath: URI.file(workspace.configPath) } });
368+
} else if (typeof workspace === 'string') {
369+
result.workspaces.push({ folderUri: URI.parse(workspace) });
370+
}
371+
});
372+
} else if (Array.isArray(storedRecents.workspaces)) {
373+
// TODO@martin legacy support can be removed at some point (6 month?)
374+
// format of 1.25 and before
375+
restoreGracefully(storedRecents.workspaces, workspace => {
376+
if (typeof workspace === 'string') {
377+
result.workspaces.push({ folderUri: URI.file(workspace) });
378+
} else if (isLegacySerializedWorkspace(workspace)) {
379+
result.workspaces.push({ workspace: { id: workspace.id, configPath: URI.file(workspace.configPath) } });
380+
} else if (isUriComponents(workspace)) {
381+
// added by 1.26-insiders
382+
result.workspaces.push({ folderUri: URI.revive(<UriComponents>workspace) });
383+
}
384+
});
385+
}
386+
if (Array.isArray(storedRecents.files2)) {
387+
restoreGracefully(storedRecents.files2, (file, i) => {
388+
const label: string | undefined = (Array.isArray(storedRecents.fileLabels) && storedRecents.fileLabels[i]) || undefined;
389+
if (typeof file === 'string') {
390+
result.files.push({ label, fileUri: URI.parse(file) });
391+
}
392+
});
393+
} else if (Array.isArray(storedRecents.files)) {
394+
restoreGracefully(storedRecents.files, file => {
395+
if (typeof file === 'string') {
396+
result.files.push({ fileUri: URI.file(file) });
397+
}
398+
});
399+
}
400+
}
401+
402+
return result;
403+
}
404+
405+
export function toStoreData(recents: IRecentlyOpened): RecentlyOpenedStorageData {
406+
const serialized: ISerializedRecentlyOpened = { workspaces3: [], files2: [] };
407+
408+
let hasLabel = false;
409+
const workspaceLabels: (string | null)[] = [];
410+
for (const recent of recents.workspaces) {
411+
if (isRecentFolder(recent)) {
412+
serialized.workspaces3.push(recent.folderUri.toString());
413+
} else {
414+
serialized.workspaces3.push({ id: recent.workspace.id, configURIPath: recent.workspace.configPath.toString() });
415+
}
416+
workspaceLabels.push(recent.label || null);
417+
hasLabel = hasLabel || !!recent.label;
418+
}
419+
if (hasLabel) {
420+
serialized.workspaceLabels = workspaceLabels;
421+
}
422+
423+
hasLabel = false;
424+
const fileLabels: (string | null)[] = [];
425+
for (const recent of recents.files) {
426+
serialized.files2.push(recent.fileUri.toString());
427+
fileLabels.push(recent.label || null);
428+
hasLabel = hasLabel || !!recent.label;
429+
}
430+
if (hasLabel) {
431+
serialized.fileLabels = fileLabels;
432+
}
433+
434+
return serialized;
435+
}
436+
437+
//#endregion

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

Lines changed: 0 additions & 41 deletions
This file was deleted.

0 commit comments

Comments
 (0)