diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/contributions.ts b/src/vs/code/electron-browser/sharedProcess/contrib/contributions.ts index 1231824cff5..6784a68ebd9 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/contributions.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/contributions.ts @@ -7,10 +7,12 @@ import { NodeCachedDataCleaner } from 'vs/code/electron-browser/sharedProcess/co import { LanguagePackCachedDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; +import { StorageDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner'; export function createSharedProcessContributions(service: IInstantiationService): IDisposable { return combinedDisposable([ service.createInstance(NodeCachedDataCleaner), - service.createInstance(LanguagePackCachedDataCleaner) + service.createInstance(LanguagePackCachedDataCleaner), + service.createInstance(StorageDataCleaner) ]); } diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts new file mode 100644 index 00000000000..40112aa81d9 --- /dev/null +++ b/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { join } from 'path'; +import { readdir, readFile, rimraf } from 'vs/base/node/pfs'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IBackupWorkspacesFormat } from 'vs/platform/backup/common/backup'; + +export class StorageDataCleaner extends Disposable { + + // Workspace/Folder storage names are MD5 hashes (128bits / 4 due to hex presentation) + private static NON_EMPTY_WORKSPACE_ID_LENGTH = 128 / 4; + + constructor( + @IEnvironmentService private environmentService: IEnvironmentService + ) { + super(); + + this.cleanUpStorageSoon(); + } + + private cleanUpStorageSoon(): void { + let handle = setTimeout(() => { + handle = void 0; + + // Leverage the backup workspace file to find out which empty workspace is currently in use to + // determine which empty workspace storage can safely be deleted + readFile(this.environmentService.backupWorkspacesPath, 'utf8').then(contents => { + const workspaces = JSON.parse(contents) as IBackupWorkspacesFormat; + const emptyWorkspaces = workspaces.emptyWorkspaceInfos.map(info => info.backupFolder); + + // Read all workspace storage folders that exist + return readdir(this.environmentService.workspaceStorageHome).then(storageFolders => { + const deletes: Promise[] = []; + + storageFolders.forEach(storageFolder => { + if (storageFolder.length === StorageDataCleaner.NON_EMPTY_WORKSPACE_ID_LENGTH) { + return; + } + + if (emptyWorkspaces.indexOf(storageFolder) === -1) { + deletes.push(rimraf(join(this.environmentService.workspaceStorageHome, storageFolder))); + } + }); + + return Promise.all(deletes); + }); + }).then(null, onUnexpectedError); + }, 30 * 1000); + + this._register(toDisposable(() => clearTimeout(handle))); + } +} diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 3e313bfa1ef..82677e20985 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -471,9 +471,9 @@ export class WindowsManager implements IWindowsMainService { for (let i = usedWindows.length - 1; i >= 0; i--) { const usedWindow = usedWindows[i]; if ( - (usedWindow.openedWorkspace && workspacesToRestore.some(workspace => workspace.id === usedWindow.openedWorkspace.id)) || // skip over restored workspace - (usedWindow.openedFolderUri && foldersToRestore.some(folder => isEqual(folder, usedWindow.openedFolderUri))) || // skip over restored folder - (usedWindow.backupPath && emptyToRestore.some(empty => empty.backupFolder === basename(usedWindow.backupPath))) // skip over restored empty window + (usedWindow.openedWorkspace && workspacesToRestore.some(workspace => workspace.id === usedWindow.openedWorkspace.id)) || // skip over restored workspace + (usedWindow.openedFolderUri && foldersToRestore.some(folder => isEqual(folder, usedWindow.openedFolderUri))) || // skip over restored folder + (usedWindow.backupPath && emptyToRestore.some(empty => empty.backupFolder === basename(usedWindow.backupPath))) // skip over restored empty window ) { continue; }