diff --git a/src/vs/platform/history/common/historyStorage.ts b/src/vs/platform/history/common/historyStorage.ts index 2d5f47f398170..089d73ec3139f 100644 --- a/src/vs/platform/history/common/historyStorage.ts +++ b/src/vs/platform/history/common/historyStorage.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { UriComponents, URI } from 'vs/base/common/uri'; import { IRecentlyOpened, isRecentFolder } from 'vs/platform/history/common/history'; +import { ILogService } from 'vs/platform/log/common/log'; interface ISerializedRecentlyOpened { workspaces3: Array; // workspace or URI.toString() // added in 1.32 @@ -31,32 +32,41 @@ function isUriComponents(curr: any): curr is UriComponents { export type RecentlyOpenedStorageData = object; -export function restoreRecentlyOpened(data: RecentlyOpenedStorageData | undefined): IRecentlyOpened { +export function restoreRecentlyOpened(data: RecentlyOpenedStorageData | undefined, logService: ILogService): IRecentlyOpened { const result: IRecentlyOpened = { workspaces: [], files: [] }; if (data) { + const restoreGracefully = function (entries: T[], func: (entry: T, index: number) => void) { + for (let i = 0; i < entries.length; i++) { + try { + func(entries[i], i); + } catch (e) { + logService.warn(`Error restoring recent entry ${JSON.stringify(entries[i])}: ${e.toString()}. Skip entry.`); + } + } + }; + const storedRecents = data as ISerializedRecentlyOpened & ILegacySerializedRecentlyOpened; if (Array.isArray(storedRecents.workspaces3)) { - for (let i = 0; i < storedRecents.workspaces3.length; i++) { - const workspace = storedRecents.workspaces3[i]; + restoreGracefully(storedRecents.workspaces3, (workspace, i) => { const label: string | undefined = (Array.isArray(storedRecents.workspaceLabels) && storedRecents.workspaceLabels[i]) || undefined; if (typeof workspace === 'object' && typeof workspace.id === 'string' && typeof workspace.configURIPath === 'string') { result.workspaces.push({ label, workspace: { id: workspace.id, configPath: URI.parse(workspace.configURIPath) } }); } else if (typeof workspace === 'string') { result.workspaces.push({ label, folderUri: URI.parse(workspace) }); } - } + }); } else if (Array.isArray(storedRecents.workspaces2)) { - for (const workspace of storedRecents.workspaces2) { + restoreGracefully(storedRecents.workspaces2, workspace => { if (typeof workspace === 'object' && typeof workspace.id === 'string' && typeof workspace.configPath === 'string') { result.workspaces.push({ workspace: { id: workspace.id, configPath: URI.file(workspace.configPath) } }); } else if (typeof workspace === 'string') { result.workspaces.push({ folderUri: URI.parse(workspace) }); } - } + }); } else if (Array.isArray(storedRecents.workspaces)) { // TODO@martin legacy support can be removed at some point (6 month?) // format of 1.25 and before - for (const workspace of storedRecents.workspaces) { + restoreGracefully(storedRecents.workspaces, workspace => { if (typeof workspace === 'string') { result.workspaces.push({ folderUri: URI.file(workspace) }); } else if (isLegacySerializedWorkspace(workspace)) { @@ -65,23 +75,21 @@ export function restoreRecentlyOpened(data: RecentlyOpenedStorageData | undefine // added by 1.26-insiders result.workspaces.push({ folderUri: URI.revive(workspace) }); } - } + }); } - if (Array.isArray(storedRecents.files2)) { - for (let i = 0; i < storedRecents.files2.length; i++) { - const file = storedRecents.files2[i]; + restoreGracefully(storedRecents.files2, (file, i) => { const label: string | undefined = (Array.isArray(storedRecents.fileLabels) && storedRecents.fileLabels[i]) || undefined; if (typeof file === 'string') { result.files.push({ label, fileUri: URI.parse(file) }); } - } + }); } else if (Array.isArray(storedRecents.files)) { - for (const file of storedRecents.files) { + restoreGracefully(storedRecents.files, file => { if (typeof file === 'string') { result.files.push({ fileUri: URI.file(file) }); } - } + }); } } diff --git a/src/vs/platform/history/electron-main/historyMainService.ts b/src/vs/platform/history/electron-main/historyMainService.ts index c55fcc6e8cc1d..6447b2c65e174 100644 --- a/src/vs/platform/history/electron-main/historyMainService.ts +++ b/src/vs/platform/history/electron-main/historyMainService.ts @@ -270,7 +270,7 @@ export class HistoryMainService implements IHistoryMainService { private getRecentlyOpenedFromStorage(): IRecentlyOpened { const storedRecents = this.stateService.getItem(HistoryMainService.recentlyOpenedStorageKey); - return restoreRecentlyOpened(storedRecents); + return restoreRecentlyOpened(storedRecents, this.logService); } private saveRecentlyOpened(recent: IRecentlyOpened): void { diff --git a/src/vs/platform/history/test/electron-main/historyStorage.test.ts b/src/vs/platform/history/test/electron-main/historyStorage.test.ts index cb591285b1a88..4a76b1515ead1 100644 --- a/src/vs/platform/history/test/electron-main/historyStorage.test.ts +++ b/src/vs/platform/history/test/electron-main/historyStorage.test.ts @@ -10,6 +10,7 @@ import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { URI } from 'vs/base/common/uri'; import { IRecentlyOpened, isRecentFolder, IRecentFolder, IRecentWorkspace } from 'vs/platform/history/common/history'; import { toStoreData, restoreRecentlyOpened } from 'vs/platform/history/common/historyStorage'; +import { NullLogService } from 'vs/platform/log/common/log'; function toWorkspace(uri: URI): IWorkspaceIdentifier { return { @@ -51,7 +52,7 @@ function assertEqualRecentlyOpened(actual: IRecentlyOpened, expected: IRecentlyO function assertRestoring(state: IRecentlyOpened, message?: string) { const stored = toStoreData(state); - const restored = restoreRecentlyOpened(stored); + const restored = restoreRecentlyOpened(stored, new NullLogService()); assertEqualRecentlyOpened(state, restored, message); } @@ -118,7 +119,7 @@ suite('History Storage', () => { ] }`; - let actual = restoreRecentlyOpened(JSON.parse(v1_25_win)); + let actual = restoreRecentlyOpened(JSON.parse(v1_25_win), new NullLogService()); let expected: IRecentlyOpened = { files: [{ fileUri: URI.file('C:\\workspaces\\test.code-workspace') }, { fileUri: URI.file('C:\\workspaces\\testing\\test-ext\\.gitignore') }], workspaces: [ @@ -146,7 +147,7 @@ suite('History Storage', () => { ] }`; - let actual = restoreRecentlyOpened(JSON.parse(v1_31_win)); + let actual = restoreRecentlyOpened(JSON.parse(v1_31_win), new NullLogService()); let expected: IRecentlyOpened = { files: [{ fileUri: URI.parse('file:///c%3A/workspaces/vscode/.yarnrc') }], workspaces: [ @@ -173,7 +174,7 @@ suite('History Storage', () => { ] }`; - let windowsState = restoreRecentlyOpened(JSON.parse(v1_32)); + let windowsState = restoreRecentlyOpened(JSON.parse(v1_32), new NullLogService()); let expected: IRecentlyOpened = { files: [{ fileUri: URI.parse('file:///home/user/.config/code-oss-dev/storage.json') }], workspaces: [ @@ -206,7 +207,7 @@ suite('History Storage', () => { ] }`; - let windowsState = restoreRecentlyOpened(JSON.parse(v1_33)); + let windowsState = restoreRecentlyOpened(JSON.parse(v1_33), new NullLogService()); let expected: IRecentlyOpened = { files: [{ label: 'def', fileUri: URI.parse('file:///home/user/.config/code-oss-dev/storage.json') }], workspaces: [ diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts index 548813c555bdb..8c48a04d3e4ed 100644 --- a/src/vs/workbench/browser/web.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -16,7 +16,7 @@ import { IPager } from 'vs/base/common/paging'; import { IExtensionManifest, ExtensionType, ExtensionIdentifier, IExtension } from 'vs/platform/extensions/common/extensions'; import { IURLHandler, IURLService } from 'vs/platform/url/common/url'; import { ITelemetryService, ITelemetryData, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; -import { ConsoleLogService } from 'vs/platform/log/common/log'; +import { ConsoleLogService, ILogService } from 'vs/platform/log/common/log'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IUpdateService, State } from 'vs/platform/update/common/update'; @@ -489,7 +489,8 @@ export class SimpleWindowService extends Disposable implements IWindowService { @IFileService private readonly fileService: IFileService, @IConfigurationService private readonly configurationService: IConfigurationService, @IStorageService private readonly storageService: IStorageService, - @IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService + @IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService, + @ILogService private readonly logService: ILogService ) { super(); @@ -617,7 +618,7 @@ export class SimpleWindowService extends Disposable implements IWindowService { async getRecentlyOpened(): Promise { const recentlyOpenedRaw = this.storageService.get(SimpleWindowService.RECENTLY_OPENED_KEY, StorageScope.GLOBAL); if (recentlyOpenedRaw) { - return restoreRecentlyOpened(JSON.parse(recentlyOpenedRaw)); + return restoreRecentlyOpened(JSON.parse(recentlyOpenedRaw), this.logService); } return { workspaces: [], files: [] };