diff --git a/src/main.js b/src/main.js index 44658a3172f..36f55c6f888 100644 --- a/src/main.js +++ b/src/main.js @@ -28,20 +28,18 @@ bootstrap.enableASARSupport(); const args = parseCLIArgs(); const userDataPath = getUserDataPath(args); -// TODO@Ben global storage migration needs to happen very early before app.on("ready") -// We copy the DB instead of moving it to ensure we are not running into locking issues -if (process.env['VSCODE_TEST_STORAGE_MIGRATION']) { - try { - const globalStorageHome = path.join(userDataPath, 'User', 'globalStorage', 'temp.vscdb'); - const localStorageHome = path.join(userDataPath, 'Local Storage'); - const localStorageDB = path.join(localStorageHome, 'file__0.localstorage'); - const localStorageDBBackup = path.join(localStorageHome, 'file__0.localstorage.vscmig'); - if (!fs.existsSync(globalStorageHome) && fs.existsSync(localStorageDB)) { - fs.copyFileSync(localStorageDB, localStorageDBBackup); - } - } catch (error) { - console.error(error); +// global storage migration needs to happen very early before app.on("ready") +// TODO@Ben remove after a while +try { + const globalStorageHome = path.join(userDataPath, 'User', 'globalStorage', 'state.vscdb'); + const localStorageHome = path.join(userDataPath, 'Local Storage'); + const localStorageDB = path.join(localStorageHome, 'file__0.localstorage'); + const localStorageDBBackup = path.join(localStorageHome, 'file__0.vscmig'); + if (!fs.existsSync(globalStorageHome) && fs.existsSync(localStorageDB)) { + fs.renameSync(localStorageDB, localStorageDBBackup); } +} catch (error) { + console.error(error); } app.setPath('userData', userDataPath); diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 961327beced..8b0bd6caae6 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -505,7 +505,6 @@ "./vs/platform/state/node/stateService.ts", "./vs/platform/statusbar/common/statusbar.ts", "./vs/platform/storage/common/storage.ts", - "./vs/platform/storage/common/storageLegacyService.ts", "./vs/platform/storage/node/storageMainService.ts", "./vs/platform/telemetry/browser/errorTelemetry.ts", "./vs/platform/telemetry/common/telemetry.ts", diff --git a/src/vs/code/electron-browser/workbench/workbench.js b/src/vs/code/electron-browser/workbench/workbench.js index 79ea6b6c6fe..decf98fc7ee 100644 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ b/src/vs/code/electron-browser/workbench/workbench.js @@ -57,15 +57,7 @@ function showPartsSplash(configuration) { let data; try { - if (!process.env['VSCODE_TEST_STORAGE_MIGRATION']) { - // TODO@Ben remove me after a while - perf.mark('willReadLocalStorage'); - let raw = window.localStorage.getItem('storage://global/parts-splash-data'); - perf.mark('didReadLocalStorage'); - data = JSON.parse(raw); - } else { - data = JSON.parse(configuration.partsSplashData); - } + data = JSON.parse(configuration.partsSplashData); } catch (e) { // ignore } diff --git a/src/vs/platform/storage/common/storageLegacyMigration.ts b/src/vs/platform/storage/common/storageLegacyMigration.ts deleted file mode 100644 index 5c39dffb47f..00000000000 --- a/src/vs/platform/storage/common/storageLegacyMigration.ts +++ /dev/null @@ -1,156 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { StorageLegacyService, IStorageLegacy } from 'vs/platform/storage/common/storageLegacyService'; -import { endsWith, startsWith, rtrim } from 'vs/base/common/strings'; - -/** - * We currently store local storage with the following format: - * - * [Global] - * storage://global/ - * - * [Workspace] - * storage://workspace// - * storage://workspace/empty:/ - * storage://workspace/root:/ - * - * - * macOS/Linux: /some/folder/path - * Windows: c%3A/Users/name/folder (normal path) - * file://localhost/c%24/name/folder (unc path) - * - * [no workspace] - * storage://workspace/__$noWorkspace__ - * => no longer being used (used for empty workspaces previously) - */ - -const COMMON_WORKSPACE_PREFIX = `${StorageLegacyService.COMMON_PREFIX}workspace/`; -const NO_WORKSPACE_PREFIX = 'storage://workspace/__$noWorkspace__'; - -export type StorageObject = { [key: string]: string }; - -export interface IParsedStorage { - multiRoot: Map; - folder: Map; - empty: Map; - noWorkspace: StorageObject; -} - -export function parseFolderStorage(storage: IStorageLegacy, folderId: string): StorageObject { - - const workspaces: { prefix: string; resource: string; }[] = []; - const activeKeys = new Set(); - - for (let i = 0; i < storage.length; i++) { - const key = storage.key(i); - - // Workspace Storage (storage://workspace/) - if (!startsWith(key, StorageLegacyService.WORKSPACE_PREFIX)) { - continue; - } - - activeKeys.add(key); - - // We are looking for key: storage://workspace//workspaceIdentifier to be able to find all folder - // paths that are known to the storage. is the only way how to parse all folder paths known in storage. - if (endsWith(key, StorageLegacyService.WORKSPACE_IDENTIFIER)) { - - // storage://workspace//workspaceIdentifier => / - let workspace = key.substring(StorageLegacyService.WORKSPACE_PREFIX.length, key.length - StorageLegacyService.WORKSPACE_IDENTIFIER.length); - - // macOS/Unix: Users/name/folder/ - // Windows: c%3A/Users/name/folder/ - if (!startsWith(workspace, 'file:')) { - workspace = `file:///${rtrim(workspace, '/')}`; - } - - // Windows UNC path: file://localhost/c%3A/Users/name/folder/ - else { - workspace = rtrim(workspace, '/'); - } - - // storage://workspace//workspaceIdentifier => storage://workspace// - const prefix = key.substr(0, key.length - StorageLegacyService.WORKSPACE_IDENTIFIER.length); - if (startsWith(workspace, folderId)) { - workspaces.push({ prefix, resource: workspace }); - } - } - } - - // With all the folder paths known we can now extract storage for each path. We have to go through all workspaces - // from the longest path first to reliably extract the storage. The reason is that one folder path can be a parent - // of another folder path and as such a simple indexOf check is not enough. - const workspacesByLength = workspaces.sort((w1, w2) => w1.prefix.length >= w2.prefix.length ? -1 : 1); - - const folderWorkspaceStorage: StorageObject = Object.create(null); - - workspacesByLength.forEach(workspace => { - activeKeys.forEach(key => { - if (!startsWith(key, workspace.prefix)) { - return; // not part of workspace prefix or already handled - } - - activeKeys.delete(key); - - if (workspace.resource === folderId) { - // storage://workspace//someKey => someKey - const storageKey = key.substr(workspace.prefix.length); - folderWorkspaceStorage[storageKey] = storage.getItem(key); - } - }); - }); - - return folderWorkspaceStorage; -} - -export function parseNoWorkspaceStorage(storage: IStorageLegacy) { - const noWorkspacePrefix = `${StorageLegacyService.WORKSPACE_PREFIX}__$noWorkspace__`; - - const noWorkspaceStorage: StorageObject = Object.create(null); - for (let i = 0; i < storage.length; i++) { - const key = storage.key(i); - - // No Workspace key is for extension development windows - if (startsWith(key, noWorkspacePrefix) && !endsWith(key, StorageLegacyService.WORKSPACE_IDENTIFIER)) { - // storage://workspace/__$noWorkspace__someKey => someKey - noWorkspaceStorage[key.substr(NO_WORKSPACE_PREFIX.length)] = storage.getItem(key); - } - } - - return noWorkspaceStorage; -} - -export function parseEmptyStorage(storage: IStorageLegacy, targetWorkspaceId: string): StorageObject { - const emptyStoragePrefix = `${COMMON_WORKSPACE_PREFIX}${targetWorkspaceId}/`; - - const emptyWorkspaceStorage: StorageObject = Object.create(null); - for (let i = 0; i < storage.length; i++) { - const key = storage.key(i); - - if (startsWith(key, emptyStoragePrefix) && !endsWith(key, StorageLegacyService.WORKSPACE_IDENTIFIER)) { - // storage://workspace/empty:/someKey => someKey - emptyWorkspaceStorage[key.substr(emptyStoragePrefix.length)] = storage.getItem(key); - } - } - - return emptyWorkspaceStorage; -} - -export function parseMultiRootStorage(storage: IStorageLegacy, targetWorkspaceId: string): StorageObject { - const multiRootStoragePrefix = `${COMMON_WORKSPACE_PREFIX}${targetWorkspaceId}/`; - - const multiRootWorkspaceStorage: StorageObject = Object.create(null); - for (let i = 0; i < storage.length; i++) { - const key = storage.key(i); - - if (startsWith(key, multiRootStoragePrefix) && !endsWith(key, StorageLegacyService.WORKSPACE_IDENTIFIER)) { - // storage://workspace/root:/someKey => someKey - multiRootWorkspaceStorage[key.substr(multiRootStoragePrefix.length)] = storage.getItem(key); - } - } - - return multiRootWorkspaceStorage; -} diff --git a/src/vs/platform/storage/common/storageLegacyService.ts b/src/vs/platform/storage/common/storageLegacyService.ts deleted file mode 100644 index 7ed240b8bc2..00000000000 --- a/src/vs/platform/storage/common/storageLegacyService.ts +++ /dev/null @@ -1,303 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as types from 'vs/base/common/types'; -import * as errors from 'vs/base/common/errors'; -import * as strings from 'vs/base/common/strings'; -import * as perf from 'vs/base/common/performance'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; - -// Browser localStorage interface -export interface IStorageLegacy { - length: number; - key(index: number): string | null; - setItem(key: string, value: any): void; - getItem(key: string): string | null; - removeItem(key: string): void; -} - -export const ID = 'storageLegacyService'; - -export const IStorageLegacyService = createDecorator(ID); - -export interface IStorageLegacyService { - _serviceBrand: any; - - /** - * Store a string value under the given key to local storage. - * - * The optional scope argument allows to define the scope of the operation. - */ - store(key: string, value: any, scope?: StorageLegacyScope): void; - - /** - * Delete an element stored under the provided key from local storage. - * - * The optional scope argument allows to define the scope of the operation. - */ - remove(key: string, scope?: StorageLegacyScope): void; - - /** - * Retrieve an element stored with the given key from local storage. Use - * the provided defaultValue if the element is null or undefined. - * - * The optional scope argument allows to define the scope of the operation. - */ - get(key: string, scope?: StorageLegacyScope, defaultValue?: string): string | undefined; - - /** - * Retrieve an element stored with the given key from local storage. Use - * the provided defaultValue if the element is null or undefined. The element - * will be converted to a number using parseInt with a base of 10. - * - * The optional scope argument allows to define the scope of the operation. - */ - getInteger(key: string, scope?: StorageLegacyScope, defaultValue?: number): number | undefined; - - /** - * Retrieve an element stored with the given key from local storage. Use - * the provided defaultValue if the element is null or undefined. The element - * will be converted to a boolean. - * - * The optional scope argument allows to define the scope of the operation. - */ - getBoolean(key: string, scope?: StorageLegacyScope, defaultValue?: boolean): boolean | undefined; -} - -export const enum StorageLegacyScope { - - /** - * The stored data will be scoped to all workspaces of this domain. - */ - GLOBAL, - - /** - * The stored data will be scoped to the current workspace. - */ - WORKSPACE -} - - -export class StorageLegacyService implements IStorageLegacyService { - - _serviceBrand: any; - - static readonly COMMON_PREFIX = 'storage://'; - static readonly GLOBAL_PREFIX = `${StorageLegacyService.COMMON_PREFIX}global/`; - static readonly WORKSPACE_PREFIX = `${StorageLegacyService.COMMON_PREFIX}workspace/`; - static readonly WORKSPACE_IDENTIFIER = 'workspaceidentifier'; - static readonly NO_WORKSPACE_IDENTIFIER = '__$noWorkspace__'; - - private _workspaceStorage: IStorageLegacy; - private _globalStorage: IStorageLegacy; - - private workspaceKey: string; - private _workspaceId: string | undefined; - - constructor( - globalStorage: IStorageLegacy, - workspaceStorage: IStorageLegacy, - workspaceId?: string, - legacyWorkspaceId?: number - ) { - this._globalStorage = globalStorage; - this._workspaceStorage = workspaceStorage || globalStorage; - - this.setWorkspaceId(workspaceId, legacyWorkspaceId); - } - - get workspaceId(): string | undefined { - return this._workspaceId; - } - - setWorkspaceId(workspaceId: string | undefined, legacyWorkspaceId?: number): void { - this._workspaceId = workspaceId; - - // Calculate workspace storage key - this.workspaceKey = this.getWorkspaceKey(workspaceId); - - // Make sure to delete all workspace storage if the workspace has been recreated meanwhile - // which is only possible if a id property is provided that we can check on - if (types.isNumber(legacyWorkspaceId)) { - this.cleanupWorkspaceScope(legacyWorkspaceId); - } else { - // ensure that we always store a workspace identifier because this key - // is used to migrate data out as needed - const workspaceIdentifier = this.getInteger(StorageLegacyService.WORKSPACE_IDENTIFIER, StorageLegacyScope.WORKSPACE); - if (!workspaceIdentifier) { - this.store(StorageLegacyService.WORKSPACE_IDENTIFIER, 42, StorageLegacyScope.WORKSPACE); - } - } - } - - get globalStorage(): IStorageLegacy { - return this._globalStorage; - } - - get workspaceStorage(): IStorageLegacy { - return this._workspaceStorage; - } - - private getWorkspaceKey(id?: string): string { - if (!id) { - return StorageLegacyService.NO_WORKSPACE_IDENTIFIER; - } - - // Special case file:// URIs: strip protocol from key to produce shorter key - const fileProtocol = 'file:///'; - if (id.indexOf(fileProtocol) === 0) { - id = id.substr(fileProtocol.length); - } - - // Always end with "/" - return `${strings.rtrim(id, '/')}/`; - } - - private cleanupWorkspaceScope(workspaceUid: number): void { - - // Get stored identifier from storage - perf.mark('willReadWorkspaceIdentifier'); - const id = this.getInteger(StorageLegacyService.WORKSPACE_IDENTIFIER, StorageLegacyScope.WORKSPACE); - perf.mark('didReadWorkspaceIdentifier'); - - // If identifier differs, assume the workspace got recreated and thus clean all storage for this workspace - if (types.isNumber(id) && workspaceUid !== id) { - const keyPrefix = this.toStorageKey('', StorageLegacyScope.WORKSPACE); - const toDelete: string[] = []; - const length = this._workspaceStorage.length; - - for (let i = 0; i < length; i++) { - const key = this._workspaceStorage.key(i); - if (!key || key.indexOf(StorageLegacyService.WORKSPACE_PREFIX) < 0) { - continue; // ignore stored things that don't belong to storage service or are defined globally - } - - // Check for match on prefix - if (key.indexOf(keyPrefix) === 0) { - toDelete.push(key); - } - } - - // Run the delete - toDelete.forEach((keyToDelete) => { - this._workspaceStorage.removeItem(keyToDelete); - }); - } - - // Store workspace identifier now - if (workspaceUid !== id) { - this.store(StorageLegacyService.WORKSPACE_IDENTIFIER, workspaceUid, StorageLegacyScope.WORKSPACE); - } - } - - store(key: string, value: any, scope = StorageLegacyScope.GLOBAL): void { - const storage = (scope === StorageLegacyScope.GLOBAL) ? this._globalStorage : this._workspaceStorage; - - if (types.isUndefinedOrNull(value)) { - this.remove(key, scope); // we cannot store null or undefined, in that case we remove the key - return; - } - - const storageKey = this.toStorageKey(key, scope); - - // Store - try { - storage.setItem(storageKey, value); - } catch (error) { - errors.onUnexpectedError(error); - } - } - - get(key: string, scope = StorageLegacyScope.GLOBAL, defaultValue?: any): string { - const storage = (scope === StorageLegacyScope.GLOBAL) ? this._globalStorage : this._workspaceStorage; - - const value = storage.getItem(this.toStorageKey(key, scope)); - if (types.isUndefinedOrNull(value)) { - return defaultValue; - } - - return value; - } - - getInteger(key: string, scope = StorageLegacyScope.GLOBAL, defaultValue: number = 0): number { - const value = this.get(key, scope, defaultValue); - - if (types.isUndefinedOrNull(value)) { - return defaultValue; - } - - return parseInt(value, 10); - } - - getBoolean(key: string, scope = StorageLegacyScope.GLOBAL, defaultValue: boolean = false): boolean { - const value = this.get(key, scope, defaultValue); - - if (types.isUndefinedOrNull(value)) { - return defaultValue; - } - - if (types.isString(value)) { - return value.toLowerCase() === 'true' ? true : false; - } - - return value ? true : false; - } - - remove(key: string, scope = StorageLegacyScope.GLOBAL): void { - const storage = (scope === StorageLegacyScope.GLOBAL) ? this._globalStorage : this._workspaceStorage; - const storageKey = this.toStorageKey(key, scope); - - // Remove - storage.removeItem(storageKey); - } - - private toStorageKey(key: string, scope: StorageLegacyScope): string { - if (scope === StorageLegacyScope.GLOBAL) { - return StorageLegacyService.GLOBAL_PREFIX + key.toLowerCase(); - } - - return StorageLegacyService.WORKSPACE_PREFIX + this.workspaceKey + key.toLowerCase(); - } -} - -export class InMemoryLocalStorage implements IStorageLegacy { - private store: { [key: string]: string; }; - - constructor() { - this.store = {}; - } - - get length() { - return Object.keys(this.store).length; - } - - key(index: number): string | null { - const keys = Object.keys(this.store); - if (keys.length > index) { - return keys[index]; - } - - return null; - } - - setItem(key: string, value: any): void { - this.store[key] = value.toString(); - } - - getItem(key: string): string | null { - const item = this.store[key]; - if (!types.isUndefinedOrNull(item)) { - return item; - } - - return null; - } - - removeItem(key: string): void { - delete this.store[key]; - } -} - -export const inMemoryLocalStorageInstance = new InMemoryLocalStorage(); \ No newline at end of file diff --git a/src/vs/platform/storage/node/storageMainService.ts b/src/vs/platform/storage/node/storageMainService.ts index 9c2c14e9d7b..345a913a092 100644 --- a/src/vs/platform/storage/node/storageMainService.ts +++ b/src/vs/platform/storage/node/storageMainService.ts @@ -74,7 +74,7 @@ export class StorageMainService extends Disposable implements IStorageMainServic _serviceBrand: any; - private static STORAGE_NAME = 'temp.vscdb'; + private static STORAGE_NAME = 'state.vscdb'; private _onDidChangeStorage: Emitter = this._register(new Emitter()); get onDidChangeStorage(): Event { return this._onDidChangeStorage.event; } @@ -98,7 +98,7 @@ export class StorageMainService extends Disposable implements IStorageMainServic } private get storagePath(): string { - if (!!this.environmentService.extensionTestsPath || !process.env['VSCODE_TEST_STORAGE_MIGRATION']) { + if (!!this.environmentService.extensionTestsPath) { return SQLiteStorageDatabase.IN_MEMORY_PATH; // no storage during extension tests! } @@ -174,7 +174,7 @@ export class StorageMainService extends Disposable implements IStorageMainServic private migrateGlobalStorage(): Thenable { this.logService.info('[storage] migrating global storage from localStorage into SQLite'); - const localStorageDBBackup = join(this.environmentService.userDataPath, 'Local Storage', 'file__0.localstorage.vscmig'); + const localStorageDBBackup = join(this.environmentService.userDataPath, 'Local Storage', 'file__0.vscmig'); return exists(localStorageDBBackup).then(exists => { if (!exists) { diff --git a/src/vs/platform/storage/node/storageService.ts b/src/vs/platform/storage/node/storageService.ts index 216cab9720a..0e93da4eee9 100644 --- a/src/vs/platform/storage/node/storageService.ts +++ b/src/vs/platform/storage/node/storageService.ts @@ -8,19 +8,15 @@ import { Event, Emitter } from 'vs/base/common/event'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { IWorkspaceStorageChangeEvent, IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason } from 'vs/platform/storage/common/storage'; import { Storage, ISQLiteStorageDatabaseLoggingOptions, IStorage, StorageHint, IStorageDatabase, SQLiteStorageDatabase } from 'vs/base/node/storage'; -import { IStorageLegacyService, StorageLegacyScope } from 'vs/platform/storage/common/storageLegacyService'; -import { startsWith, endsWith } from 'vs/base/common/strings'; import { Action } from 'vs/base/common/actions'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { localize } from 'vs/nls'; import { mark, getDuration } from 'vs/base/common/performance'; import { join } from 'path'; -import { copy, exists, mkdirp, readdir, writeFile } from 'vs/base/node/pfs'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { copy, exists, mkdirp, writeFile } from 'vs/base/node/pfs'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkspaceInitializationPayload, isWorkspaceIdentifier, isSingleFolderWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { StorageObject, parseMultiRootStorage, parseFolderStorage, parseNoWorkspaceStorage, parseEmptyStorage } from 'vs/platform/storage/common/storageLegacyMigration'; export class StorageService extends Disposable implements IStorageService { _serviceBrand: any; @@ -70,9 +66,7 @@ export class StorageService extends Disposable implements IStorageService { // Global Storage this.globalStorage = new Storage(globalStorageDatabase); - if (process.env['VSCODE_TEST_STORAGE_MIGRATION']) { - this._register(this.globalStorage.onDidChangeStorage(key => this.handleDidChangeStorage(key, StorageScope.GLOBAL))); - } + this._register(this.globalStorage.onDidChangeStorage(key => this.handleDidChangeStorage(key, StorageScope.GLOBAL))); } private handleDidChangeStorage(key: string, scope: StorageScope): void { @@ -104,170 +98,18 @@ export class StorageService extends Disposable implements IStorageService { return this.prepareWorkspaceStorageFolder(payload).then(result => { const useInMemoryStorage = !!this.environmentService.extensionTestsPath; // no storage during extension tests! - let workspaceStoragePath: string; - let workspaceStorageExists: Thenable; - if (useInMemoryStorage) { - workspaceStoragePath = SQLiteStorageDatabase.IN_MEMORY_PATH; - workspaceStorageExists = Promise.resolve(true); - } else { - workspaceStoragePath = join(result.path, StorageService.WORKSPACE_STORAGE_NAME); + // Create workspace storage and initalize + mark('willInitWorkspaceStorage'); + return this.createWorkspaceStorage(useInMemoryStorage ? SQLiteStorageDatabase.IN_MEMORY_PATH : join(result.path, StorageService.WORKSPACE_STORAGE_NAME), result.wasCreated ? StorageHint.STORAGE_DOES_NOT_EXIST : void 0).init().then(() => { + mark('didInitWorkspaceStorage'); + }, error => { + mark('didInitWorkspaceStorage'); - mark('willCheckWorkspaceStorageExists'); - workspaceStorageExists = exists(workspaceStoragePath).then(exists => { - mark('didCheckWorkspaceStorageExists'); - - return exists; - }); - } - - return workspaceStorageExists.then(exists => { - - // Create workspace storage and initalize - mark('willInitWorkspaceStorage'); - return this.createWorkspaceStorage(workspaceStoragePath, result.wasCreated ? StorageHint.STORAGE_DOES_NOT_EXIST : void 0).init().then(() => { - mark('didInitWorkspaceStorage'); - }, error => { - mark('didInitWorkspaceStorage'); - - return Promise.reject(error); - }).then(() => { - - // Migrate storage if this is the first start and we are not using in-memory - let migrationPromise: Thenable; - if (!useInMemoryStorage && !exists) { - migrationPromise = this.migrateWorkspaceStorage(payload); - } else { - migrationPromise = Promise.resolve(); - } - - return migrationPromise; - }); + return Promise.reject(error); }); }); } - // TODO@Ben remove migration after a while - private migrateWorkspaceStorage(payload: IWorkspaceInitializationPayload): Thenable { - mark('willMigrateWorkspaceStorageKeys'); - return readdir(this.environmentService.extensionsPath).then(extensions => { - - // Otherwise, we migrate data from window.localStorage over - try { - let workspaceItems: StorageObject; - if (isWorkspaceIdentifier(payload)) { - workspaceItems = parseMultiRootStorage(window.localStorage, `root:${payload.id}`); - } else if (isSingleFolderWorkspaceInitializationPayload(payload)) { - workspaceItems = parseFolderStorage(window.localStorage, payload.folder.toString()); - } else { - if (payload.id === 'ext-dev') { - workspaceItems = parseNoWorkspaceStorage(window.localStorage); - } else { - workspaceItems = parseEmptyStorage(window.localStorage, `${payload.id}`); - } - } - - const workspaceItemsKeys = workspaceItems ? Object.keys(workspaceItems) : []; - if (workspaceItemsKeys.length > 0) { - const supportedKeys = new Map(); - [ - 'workbench.search.history', - 'history.entries', - 'ignoreNetVersionError', - 'ignoreEnospcError', - 'extensionUrlHandler.urlToHandle', - 'terminal.integrated.isWorkspaceShellAllowed', - 'workbench.tasks.ignoreTask010Shown', - 'workbench.tasks.recentlyUsedTasks', - 'workspaces.dontPromptToOpen', - 'output.activechannel', - 'outline/state', - 'extensionsAssistant/workspaceRecommendationsIgnore', - 'extensionsAssistant/dynamicWorkspaceRecommendations', - 'debug.repl.history', - 'editor.matchCase', - 'editor.wholeWord', - 'editor.isRegex', - 'lifecyle.lastShutdownReason', - 'debug.selectedroot', - 'debug.selectedconfigname', - 'debug.breakpoint', - 'debug.breakpointactivated', - 'debug.functionbreakpoint', - 'debug.exceptionbreakpoint', - 'debug.watchexpressions', - 'workbench.sidebar.activeviewletid', - 'workbench.panelpart.activepanelid', - 'workbench.zenmode.active', - 'workbench.centerededitorlayout.active', - 'workbench.sidebar.hidden', - 'workbench.panel.hidden', - 'workbench.panel.location', - 'extensionsIdentifiers/disabled', - 'extensionsIdentifiers/enabled', - 'scm.views', - 'suggest/memories/first', - 'suggest/memories/recentlyUsed', - 'suggest/memories/recentlyUsedByPrefix', - 'workbench.view.explorer.numberOfVisibleViews', - 'workbench.view.extensions.numberOfVisibleViews', - 'workbench.view.debug.numberOfVisibleViews', - 'workbench.explorer.views.state', - 'workbench.view.extensions.state', - 'workbench.view.debug.state', - 'memento/workbench.editor.walkThroughPart', - 'memento/workbench.editor.settings2', - 'memento/workbench.editor.htmlPreviewPart', - 'memento/workbench.editor.defaultPreferences', - 'memento/workbench.editors.files.textFileEditor', - 'memento/workbench.editors.logViewer', - 'memento/workbench.editors.textResourceEditor', - 'memento/workbench.panel.output' - ].forEach(key => supportedKeys.set(key.toLowerCase(), key)); - - // Support extension storage as well (always the ID of the extension) - extensions.forEach(extension => { - let extensionId: string; - if (extension.indexOf('-') >= 0) { - extensionId = extension.substring(0, extension.lastIndexOf('-')); // convert "author.extension-0.2.5" => "author.extension" - } else { - extensionId = extension; - } - - if (extensionId) { - supportedKeys.set(extensionId.toLowerCase(), extensionId); - } - }); - - workspaceItemsKeys.forEach(key => { - const value = workspaceItems[key]; - - // first check for a well known supported key and store with realcase value - const supportedKey = supportedKeys.get(key); - if (supportedKey) { - this.store(supportedKey, value, StorageScope.WORKSPACE); - } - - // fix lowercased ".numberOfVisibleViews" - else if (endsWith(key, '.numberOfVisibleViews'.toLowerCase())) { - const normalizedKey = key.substring(0, key.length - '.numberOfVisibleViews'.length) + '.numberOfVisibleViews'; - this.store(normalizedKey, value, StorageScope.WORKSPACE); - } - - // support dynamic keys - else if (key.indexOf('memento/') === 0 || endsWith(key, '.state')) { - this.store(key, value, StorageScope.WORKSPACE); - } - }); - } - } catch (error) { - onUnexpectedError(error); - this.logService.error(error); - } - - mark('didMigrateWorkspaceStorageKeys'); - }); - } - private createWorkspaceStorage(workspaceStoragePath: string, hint?: StorageHint): IStorage { // Logger for workspace storage @@ -476,129 +318,15 @@ export class LogStorageAction extends Action { constructor( id: string, label: string, - @IStorageService private storageService: DelegatingStorageService, + @IStorageService private storageService: StorageService, @IWindowService private windowService: IWindowService ) { super(id, label); } run(): Thenable { - this.storageService.storage.logStorage(); + this.storageService.logStorage(); return this.windowService.openDevTools(); } } - -export class DelegatingStorageService extends Disposable implements IStorageService { - _serviceBrand: any; - - private _onDidChangeStorage: Emitter = this._register(new Emitter()); - get onDidChangeStorage(): Event { return this._onDidChangeStorage.event; } - - private _onWillSaveState: Emitter = this._register(new Emitter()); - get onWillSaveState(): Event { return this._onWillSaveState.event; } - - private closed: boolean; - private useLegacyWorkspaceStorage: boolean; - - constructor( - private storageService: IStorageService, - private storageLegacyService: IStorageLegacyService, - private logService: ILogService, - configurationService: IConfigurationService - ) { - super(); - - this.useLegacyWorkspaceStorage = configurationService.inspect('workbench.enableLegacyStorage').value === true; - - this.registerListeners(); - } - - private registerListeners(): void { - this._register(this.storageService.onDidChangeStorage(e => this._onDidChangeStorage.fire(e))); - this._register(this.storageService.onWillSaveState(e => this._onWillSaveState.fire(e))); - - const globalKeyMarker = 'storage://global/'; - - window.addEventListener('storage', e => { - if (e.key && startsWith(e.key, globalKeyMarker)) { - const key = e.key.substr(globalKeyMarker.length); - - this._onDidChangeStorage.fire({ key, scope: StorageScope.GLOBAL }); - } - }); - } - - get storage(): StorageService { - return this.storageService as StorageService; - } - - get(key: string, scope: StorageScope, fallbackValue: string): string; - get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined { - if (!this.useLegacyWorkspaceStorage) { - if (scope === StorageScope.WORKSPACE || process.env['VSCODE_TEST_STORAGE_MIGRATION']) { - return this.storageService.get(key, scope, fallbackValue); - } - } - - return this.storageLegacyService.get(key, this.convertScope(scope), fallbackValue); - } - - getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean; - getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined { - if (!this.useLegacyWorkspaceStorage) { - if (scope === StorageScope.WORKSPACE || process.env['VSCODE_TEST_STORAGE_MIGRATION']) { - return this.storageService.getBoolean(key, scope, fallbackValue); - } - } - - return this.storageLegacyService.getBoolean(key, this.convertScope(scope), fallbackValue); - } - - getInteger(key: string, scope: StorageScope, fallbackValue: number): number; - getInteger(key: string, scope: StorageScope, fallbackValue?: number): number | undefined { - if (!this.useLegacyWorkspaceStorage) { - if (scope === StorageScope.WORKSPACE || process.env['VSCODE_TEST_STORAGE_MIGRATION']) { - return this.storageService.getInteger(key, scope, fallbackValue); - } - } - - return this.storageLegacyService.getInteger(key, this.convertScope(scope), fallbackValue); - } - - store(key: string, value: any, scope: StorageScope): void { - if (this.closed) { - this.logService.warn(`Unsupported write (store) access after close (key: ${key})`); - - return; // prevent writing after close to detect late write access - } - - this.storageLegacyService.store(key, value, this.convertScope(scope)); - - this.storageService.store(key, value, scope); - } - - remove(key: string, scope: StorageScope): void { - if (this.closed) { - this.logService.warn(`Unsupported write (remove) access after close (key: ${key})`); - - return; // prevent writing after close to detect late write access - } - - this.storageLegacyService.remove(key, this.convertScope(scope)); - - this.storageService.remove(key, scope); - } - - close(): Promise { - const promise = this.storage.close(); - - this.closed = true; - - return promise; - } - - private convertScope(scope: StorageScope): StorageLegacyScope { - return scope === StorageScope.GLOBAL ? StorageLegacyScope.GLOBAL : StorageLegacyScope.WORKSPACE; - } -} \ No newline at end of file diff --git a/src/vs/platform/storage/test/browser/storageLegacyMigration.test.ts b/src/vs/platform/storage/test/browser/storageLegacyMigration.test.ts deleted file mode 100644 index 2aee7230ea8..00000000000 --- a/src/vs/platform/storage/test/browser/storageLegacyMigration.test.ts +++ /dev/null @@ -1,142 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import { StorageLegacyScope, StorageLegacyService } from 'vs/platform/storage/common/storageLegacyService'; -import { parseEmptyStorage, parseMultiRootStorage, parseFolderStorage } from 'vs/platform/storage/common/storageLegacyMigration'; -import { URI } from 'vs/base/common/uri'; -import { startsWith } from 'vs/base/common/strings'; - -suite('Storage Migration', () => { - let storage = window.localStorage; - - setup(() => { - storage.clear(); - }); - - teardown(() => { - storage.clear(); - }); - - test('Parse Storage (mixed)', () => { - - // Fill the storage with multiple workspaces of all kinds (empty, root, folders) - const workspaceIds = [ - - // Multi Root Workspace - URI.from({ path: '1500007676869', scheme: 'root' }).toString(), - URI.from({ path: '2500007676869', scheme: 'root' }).toString(), - URI.from({ path: '3500007676869', scheme: 'root' }).toString(), - - // Empty Workspace - URI.from({ path: '4500007676869', scheme: 'empty' }).toString(), - URI.from({ path: '5500007676869', scheme: 'empty' }).toString(), - URI.from({ path: '6500007676869', scheme: 'empty' }).toString(), - - // Unix Paths - URI.file('/some/folder/folder1').toString(), - URI.file('/some/folder/folder2').toString(), - URI.file('/some/folder/folder3').toString(), - URI.file('/some/folder/folder1/sub1').toString(), - URI.file('/some/folder/folder2/sub2').toString(), - URI.file('/some/folder/folder3/sub3').toString(), - - // Windows Paths - URI.file('c:\\some\\folder\\folder1').toString(), - URI.file('c:\\some\\folder\\folder2').toString(), - URI.file('c:\\some\\folder\\folder3').toString(), - URI.file('c:\\some\\folder\\folder1\\sub1').toString(), - URI.file('c:\\some\\folder\\folder2\\sub2').toString(), - URI.file('c:\\some\\folder\\folder3\\sub3').toString(), - - // UNC Paths - 'file://localhost/c%3A/some/folder/folder1', - 'file://localhost/c%3A/some/folder/folder2', - 'file://localhost/c%3A/some/folder/folder3', - 'file://localhost/c%3A/some/folder/folder1/sub1', - 'file://localhost/c%3A/some/folder/folder2/sub2', - 'file://localhost/c%3A/some/folder/folder3/sub3' - ]; - - const services = workspaceIds.map(id => createService(id)); - - services.forEach((service, index) => { - let expectedKeyCount = 4; - let storageToTest; - - const workspaceId = workspaceIds[index]; - if (startsWith(workspaceId, 'file:')) { - storageToTest = parseFolderStorage(storage, workspaceId); - expectedKeyCount++; // workspaceIdentifier gets added! - } else if (startsWith(workspaceId, 'empty:')) { - storageToTest = parseEmptyStorage(storage, workspaceId); - } else if (startsWith(workspaceId, 'root:')) { - storageToTest = parseMultiRootStorage(storage, workspaceId); - } - - assert.equal(Object.keys(storageToTest).length, expectedKeyCount, 's'); - assert.equal(storageToTest['key1'], service.get('key1', StorageLegacyScope.WORKSPACE)); - assert.equal(storageToTest['key2.something'], service.get('key2.something', StorageLegacyScope.WORKSPACE)); - assert.equal(storageToTest['key3/special'], service.get('key3/special', StorageLegacyScope.WORKSPACE)); - assert.equal(storageToTest['key4 space'], service.get('key4 space', StorageLegacyScope.WORKSPACE)); - }); - }); - - test('Parse Storage (handle subfolders properly)', () => { - const ws1 = URI.file('/some/folder/folder1').toString(); - const ws2 = URI.file('/some/folder/folder1/sub1').toString(); - - const s1 = new StorageLegacyService(storage, storage, ws1, Date.now()); - const s2 = new StorageLegacyService(storage, storage, ws2, Date.now()); - - s1.store('s1key1', 'value1', StorageLegacyScope.WORKSPACE); - s1.store('s1key2.something', JSON.stringify({ foo: 'bar' }), StorageLegacyScope.WORKSPACE); - s1.store('s1key3/special', true, StorageLegacyScope.WORKSPACE); - s1.store('s1key4 space', 4, StorageLegacyScope.WORKSPACE); - - s2.store('s2key1', 'value1', StorageLegacyScope.WORKSPACE); - s2.store('s2key2.something', JSON.stringify({ foo: 'bar' }), StorageLegacyScope.WORKSPACE); - s2.store('s2key3/special', true, StorageLegacyScope.WORKSPACE); - s2.store('s2key4 space', 4, StorageLegacyScope.WORKSPACE); - - - const s1Storage = parseFolderStorage(storage, ws1); - assert.equal(Object.keys(s1Storage).length, 5); - assert.equal(s1Storage['s1key1'], s1.get('s1key1', StorageLegacyScope.WORKSPACE)); - assert.equal(s1Storage['s1key2.something'], s1.get('s1key2.something', StorageLegacyScope.WORKSPACE)); - assert.equal(s1Storage['s1key3/special'], s1.get('s1key3/special', StorageLegacyScope.WORKSPACE)); - assert.equal(s1Storage['s1key4 space'], s1.get('s1key4 space', StorageLegacyScope.WORKSPACE)); - - const s2Storage = parseFolderStorage(storage, ws2); - assert.equal(Object.keys(s2Storage).length, 5); - assert.equal(s2Storage['s2key1'], s2.get('s2key1', StorageLegacyScope.WORKSPACE)); - assert.equal(s2Storage['s2key2.something'], s2.get('s2key2.something', StorageLegacyScope.WORKSPACE)); - assert.equal(s2Storage['s2key3/special'], s2.get('s2key3/special', StorageLegacyScope.WORKSPACE)); - assert.equal(s2Storage['s2key4 space'], s2.get('s2key4 space', StorageLegacyScope.WORKSPACE)); - }); - - function createService(workspaceId?: string): StorageLegacyService { - const service = new StorageLegacyService(storage, storage, workspaceId, workspaceId && startsWith(workspaceId, 'file:') ? Date.now() : void 0); - - // Unrelated - storage.setItem('foo', 'bar'); - storage.setItem('storage://foo', 'bar'); - storage.setItem('storage://global/storage://foo', 'bar'); - - // Global - service.store('key1', 'value1'); - service.store('key2.something', JSON.stringify({ foo: 'bar' })); - service.store('key3/special', true); - service.store('key4 space', 4); - - // Workspace - service.store('key1', 'value1', StorageLegacyScope.WORKSPACE); - service.store('key2.something', JSON.stringify({ foo: 'bar' }), StorageLegacyScope.WORKSPACE); - service.store('key3/special', true, StorageLegacyScope.WORKSPACE); - service.store('key4 space', 4, StorageLegacyScope.WORKSPACE); - - return service; - } -}); \ No newline at end of file diff --git a/src/vs/platform/telemetry/node/workbenchCommonProperties.ts b/src/vs/platform/telemetry/node/workbenchCommonProperties.ts index aa0cb002ac4..bf35b0df648 100644 --- a/src/vs/platform/telemetry/node/workbenchCommonProperties.ts +++ b/src/vs/platform/telemetry/node/workbenchCommonProperties.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as uuid from 'vs/base/common/uuid'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; @@ -11,53 +10,23 @@ export const lastSessionDateStorageKey = 'telemetry.lastSessionDate'; export function resolveWorkbenchCommonProperties(storageService: IStorageService, commit: string, version: string, machineId: string, installSourcePath: string): Promise<{ [name: string]: string }> { return resolveCommonProperties(commit, version, machineId, installSourcePath).then(result => { + const instanceId = storageService.get('telemetry.instanceId', StorageScope.GLOBAL)!; + const firstSessionDate = storageService.get('telemetry.firstSessionDate', StorageScope.GLOBAL)!; + const lastSessionDate = storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL)!; + // __GDPR__COMMON__ "common.version.shell" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } result['common.version.shell'] = process.versions && process.versions['electron']; // __GDPR__COMMON__ "common.version.renderer" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } result['common.version.renderer'] = process.versions && process.versions['chrome']; - - const lastSessionDate = storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL); - if (!process.env['VSCODE_TEST_STORAGE_MIGRATION']) { - storageService.store(lastSessionDateStorageKey, new Date().toUTCString(), StorageScope.GLOBAL); - } - // __GDPR__COMMON__ "common.firstSessionDate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - result['common.firstSessionDate'] = getOrCreateFirstSessionDate(storageService); + result['common.firstSessionDate'] = firstSessionDate; // __GDPR__COMMON__ "common.lastSessionDate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } result['common.lastSessionDate'] = lastSessionDate || ''; // __GDPR__COMMON__ "common.isNewSession" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } result['common.isNewSession'] = !lastSessionDate ? '1' : '0'; // __GDPR__COMMON__ "common.instanceId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - result['common.instanceId'] = getOrCreateInstanceId(storageService); + result['common.instanceId'] = instanceId; return result; }); } - -function getOrCreateInstanceId(storageService: IStorageService): string { - const key = 'telemetry.instanceId'; - - let instanceId = storageService.get(key, StorageScope.GLOBAL, void 0); - if (instanceId) { - return instanceId; - } - - instanceId = uuid.generateUuid(); - storageService.store(key, instanceId, StorageScope.GLOBAL); - - return instanceId; -} - -function getOrCreateFirstSessionDate(storageService: IStorageService): string { - const key = 'telemetry.firstSessionDate'; - - let firstSessionDate = storageService.get(key, StorageScope.GLOBAL, void 0); - if (firstSessionDate) { - return firstSessionDate; - } - - firstSessionDate = new Date().toUTCString(); - storageService.store(key, firstSessionDate, StorageScope.GLOBAL); - - return firstSessionDate; -} diff --git a/src/vs/platform/update/electron-main/updateService.darwin.ts b/src/vs/platform/update/electron-main/updateService.darwin.ts index 768cbcad49f..2bf4b3509a8 100644 --- a/src/vs/platform/update/electron-main/updateService.darwin.ts +++ b/src/vs/platform/update/electron-main/updateService.darwin.ts @@ -106,14 +106,6 @@ export class DarwinUpdateService extends AbstractUpdateService { } protected doQuitAndInstall(): void { - // for some reason updating on Mac causes the local storage not to be flushed. - // we workaround this issue by forcing an explicit flush of the storage data. - // see also https://github.com/Microsoft/vscode/issues/172 - this.logService.trace('update#quitAndInstall(): calling flushStorageData()'); - if (electron.session.defaultSession) { - electron.session.defaultSession.flushStorageData(); - } - this.logService.trace('update#quitAndInstall(): running raw#quitAndInstall()'); electron.autoUpdater.quitAndInstall(); } diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index 727780a6704..7b97176234c 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -678,12 +678,6 @@ configurationRegistry.registerConfiguration({ 'description': nls.localize('workbench.enableExperiments', "Fetches experiments to run from a Microsoft online service."), 'default': true, 'tags': ['usesOnlineServices'] - }, - //TODO@Ben remove ('enableLegacyStorage') after a while - 'workbench.enableLegacyStorage': { - 'type': 'boolean', - 'description': nls.localize('workbench.enableLegacyStorage', "Switches back to the previous storage implementation. Only change this setting if advised to do so."), - 'default': false } } }); diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index a43d63aa514..c32573c9701 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -12,7 +12,6 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import * as comparer from 'vs/base/common/comparers'; import * as platform from 'vs/base/common/platform'; import { URI as uri } from 'vs/base/common/uri'; -import { IWorkspaceContextService, Workspace, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; @@ -22,7 +21,6 @@ import * as gracefulFs from 'graceful-fs'; import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; import { IWindowConfiguration, IWindowsService } from 'vs/platform/windows/common/windows'; import { WindowsChannelClient } from 'vs/platform/windows/node/windowsIpc'; -import { IStorageLegacyService, StorageLegacyService, inMemoryLocalStorageInstance, IStorageLegacy } from 'vs/platform/storage/common/storageLegacyService'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { Client as ElectronIPCClient } from 'vs/base/parts/ipc/electron-browser/ipc.electron-browser'; import { webFrame } from 'electron'; @@ -35,7 +33,7 @@ import { IWorkspacesService, ISingleFolderWorkspaceIdentifier, IWorkspaceInitial import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; import * as fs from 'fs'; import { ConsoleLogService, MultiplexLogService, ILogService } from 'vs/platform/log/common/log'; -import { StorageService, DelegatingStorageService } from 'vs/platform/storage/node/storageService'; +import { StorageService } from 'vs/platform/storage/node/storageService'; import { IssueChannelClient } from 'vs/platform/issue/node/issueIpc'; import { IIssueService } from 'vs/platform/issue/common/issue'; import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/node/logIpc'; @@ -124,7 +122,7 @@ function openWorkbench(configuration: IWindowConfiguration): Promise { createStorageService(payload, environmentService, logService, mainProcessClient) ]).then(services => { const workspaceService = services[0]; - const storageService = new DelegatingStorageService(services[1], createStorageLegacyService(workspaceService, environmentService), logService, workspaceService); + const storageService = services[1]; return domContentLoaded().then(() => { perf.mark('willStartWorkbench'); @@ -252,46 +250,6 @@ function createStorageService(payload: IWorkspaceInitializationPayload, environm }); } -function createStorageLegacyService(workspaceService: IWorkspaceContextService, environmentService: IEnvironmentService): IStorageLegacyService { - let workspaceId: string; - - switch (workspaceService.getWorkbenchState()) { - - // in multi root workspace mode we use the provided ID as key for workspace storage - case WorkbenchState.WORKSPACE: - workspaceId = uri.from({ path: workspaceService.getWorkspace().id, scheme: 'root' }).toString(); - break; - - // in single folder mode we use the path of the opened folder as key for workspace storage - // the ctime is used as secondary workspace id to clean up stale UI state if necessary - case WorkbenchState.FOLDER: - const workspace: Workspace = workspaceService.getWorkspace(); - workspaceId = workspace.folders[0].uri.toString(); - break; - - // finally, if we do not have a workspace open, we need to find another identifier for the window to store - // workspace UI state. if we have a backup path in the configuration we can use that because this - // will be a unique identifier per window that is stable between restarts as long as there are - // dirty files in the workspace. - // We use basename() to produce a short identifier, we do not need the full path. We use a custom - // scheme so that we can later distinguish these identifiers from the workspace one. - case WorkbenchState.EMPTY: - workspaceId = workspaceService.getWorkspace().id; - break; - } - - const disableStorage = !!environmentService.extensionTestsPath; // never keep any state when running extension tests! - - let storage: IStorageLegacy; - if (disableStorage) { - storage = inMemoryLocalStorageInstance; - } else { - storage = window.localStorage; - } - - return new StorageLegacyService(storage, storage, workspaceId); -} - function createLogService(mainProcessClient: ElectronIPCClient, configuration: IWindowConfiguration, environmentService: IEnvironmentService): ILogService { const spdlogService = createSpdLogService(`renderer${configuration.windowId}`, configuration.logLevel, environmentService.logsPath); const consoleLogService = new ConsoleLogService(configuration.logLevel); diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index 4049ee4783c..14bae36b787 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -75,7 +75,7 @@ import { HashService } from 'vs/workbench/services/hash/node/hashService'; import { IHashService } from 'vs/workbench/services/hash/common/hashService'; import { ILogService } from 'vs/platform/log/common/log'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { DelegatingStorageService } from 'vs/platform/storage/node/storageService'; +import { StorageService } from 'vs/platform/storage/node/storageService'; import { Event, Emitter } from 'vs/base/common/event'; import { WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme'; import { LocalizationsChannelClient } from 'vs/platform/localizations/node/localizationsIpc'; @@ -113,7 +113,7 @@ export interface ICoreServices { configurationService: IConfigurationService; environmentService: IEnvironmentService; logService: ILogService; - storageService: DelegatingStorageService; + storageService: StorageService; } /** @@ -125,7 +125,7 @@ export class WorkbenchShell extends Disposable { private readonly _onWillShutdown = this._register(new Emitter()); get onWillShutdown(): Event { return this._onWillShutdown.event; } - private storageService: DelegatingStorageService; + private storageService: StorageService; private environmentService: IEnvironmentService; private logService: ILogService; private configurationService: IConfigurationService; @@ -280,31 +280,25 @@ export class WorkbenchShell extends Disposable { const globalStorageInitDurationMain = perf.getDuration('main:willInitGlobalStorage', 'main:didInitGlobalStorage'); const globalStorageInitDuratioRenderer = perf.getDuration('willInitGlobalStorage', 'didInitGlobalStorage'); const workspaceStorageInitDuration = perf.getDuration('willInitWorkspaceStorage', 'didInitWorkspaceStorage'); - const workspaceStorageFileExistsDuration = perf.getDuration('willCheckWorkspaceStorageExists', 'didCheckWorkspaceStorageExists'); - const workspaceStorageMigrationDuration = perf.getDuration('willMigrateWorkspaceStorageKeys', 'didMigrateWorkspaceStorageKeys'); const workbenchLoadDuration = perf.getDuration('willLoadWorkbenchMain', 'didLoadWorkbenchMain'); - const localStorageDuration = perf.getDuration('willReadLocalStorage', 'didReadLocalStorage'); // Handle errors (avoid duplicates to reduce spam) const loggedStorageErrors = new Set(); - this._register(this.storageService.storage.onWorkspaceStorageError(error => { + this._register(this.storageService.onWorkspaceStorageError(error => { const errorStr = `${error}`; if (!loggedStorageErrors.has(errorStr)) { loggedStorageErrors.add(errorStr); /* __GDPR__ - "sqliteStorageError5" : { + "sqliteStorageError6" : { "appReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workbenchReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceExistsTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceMigrationTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspaceRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspaceSchemaTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "globalReadTimeMain" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "globalReadTimeRenderer" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspaceReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "localStorageTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workbenchRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspaceKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "startupKind": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, @@ -316,14 +310,11 @@ export class WorkbenchShell extends Disposable { "sqliteStorageError" : { "appReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workbenchReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceExistsTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceMigrationTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspaceRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspaceSchemaTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "globalReadTimeMain" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "globalReadTimeRenderer" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspaceReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "localStorageTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workbenchRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspaceKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "startupKind": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, @@ -333,16 +324,13 @@ export class WorkbenchShell extends Disposable { this.telemetryService.publicLog('sqliteStorageError5', { 'appReadyTime': appReadyDuration, 'workbenchReadyTime': workbenchReadyDuration, - 'workspaceExistsTime': workspaceStorageFileExistsDuration, - 'workspaceMigrationTime': workspaceStorageMigrationDuration, 'workspaceRequireTime': workspaceStorageRequireDuration, 'workspaceSchemaTime': workspaceStorageSchemaDuration, 'globalReadTimeMain': globalStorageInitDurationMain, 'globalReadTimeRenderer': globalStorageInitDuratioRenderer, 'workspaceReadTime': workspaceStorageInitDuration, - 'localStorageTime': localStorageDuration, 'workbenchRequireTime': workbenchLoadDuration, - 'workspaceKeys': this.storageService.storage.getSize(StorageScope.WORKSPACE), + 'workspaceKeys': this.storageService.getSize(StorageScope.WORKSPACE), 'startupKind': this.lifecycleService.startupKind, 'storageError': errorStr }); @@ -350,7 +338,7 @@ export class WorkbenchShell extends Disposable { })); - if (this.storageService.storage.hasErrors) { + if (this.storageService.hasErrors) { return; // do not log performance numbers when errors occured } @@ -359,17 +347,14 @@ export class WorkbenchShell extends Disposable { } /* __GDPR__ - "sqliteStorageTimers5" : { + "sqliteStorageTimers6" : { "appReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workbenchReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceExistsTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceMigrationTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspaceRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspaceSchemaTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "globalReadTimeMain" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "globalReadTimeRenderer" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspaceReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "localStorageTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workbenchRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspaceKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "startupKind": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } @@ -380,14 +365,11 @@ export class WorkbenchShell extends Disposable { "sqliteStorageTimers" : { "appReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workbenchReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceExistsTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceMigrationTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspaceRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspaceSchemaTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "globalReadTimeMain" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "globalReadTimeRenderer" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspaceReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "localStorageTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workbenchRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspaceKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "startupKind": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } @@ -396,16 +378,13 @@ export class WorkbenchShell extends Disposable { this.telemetryService.publicLog('sqliteStorageTimers5', { 'appReadyTime': appReadyDuration, 'workbenchReadyTime': workbenchReadyDuration, - 'workspaceExistsTime': workspaceStorageFileExistsDuration, - 'workspaceMigrationTime': workspaceStorageMigrationDuration, 'workspaceRequireTime': workspaceStorageRequireDuration, 'workspaceSchemaTime': workspaceStorageSchemaDuration, 'globalReadTimeMain': globalStorageInitDurationMain, 'globalReadTimeRenderer': globalStorageInitDuratioRenderer, 'workspaceReadTime': workspaceStorageInitDuration, - 'localStorageTime': localStorageDuration, 'workbenchRequireTime': workbenchLoadDuration, - 'workspaceKeys': this.storageService.storage.getSize(StorageScope.WORKSPACE), + 'workspaceKeys': this.storageService.getSize(StorageScope.WORKSPACE), 'startupKind': this.lifecycleService.startupKind }); } diff --git a/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts b/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts index 9892f923f61..25c48d35b30 100644 --- a/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts +++ b/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts @@ -24,7 +24,7 @@ interface IConfiguration extends IWindowsConfiguration { update: { channel: string; }; telemetry: { enableCrashReporter: boolean }; keyboard: { touchbar: { enabled: boolean } }; - workbench: { tree: { horizontalScrolling: boolean }, enableLegacyStorage: boolean }; + workbench: { tree: { horizontalScrolling: boolean } }; files: { useExperimentalFileWatcher: boolean, watcherExclude: object }; } @@ -41,7 +41,6 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo private windowsSmoothScrollingWorkaround: boolean; private experimentalFileWatcher: boolean; private fileWatcherExclude: object; - private legacyStorage: boolean; private firstFolderResource?: URI; private extensionHostRestarter: RunOnceScheduler; @@ -139,11 +138,6 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo changed = true; } - // Legacy Workspace Storage - if (config.workbench && typeof config.workbench.enableLegacyStorage === 'boolean' && config.workbench.enableLegacyStorage !== this.legacyStorage) { - this.legacyStorage = config.workbench.enableLegacyStorage; - changed = true; - } // Windows: smooth scrolling workaround if (isWindows && config.window && typeof config.window.smoothScrollingWorkaround === 'boolean' && config.window.smoothScrollingWorkaround !== this.windowsSmoothScrollingWorkaround) { this.windowsSmoothScrollingWorkaround = config.window.smoothScrollingWorkaround; diff --git a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts index 52bb8cb870e..0adbdf06582 100644 --- a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts @@ -13,7 +13,7 @@ import { IWorkspaceIdentifier, IWorkspaceFolderCreationData } from 'vs/platform/ import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { DelegatingStorageService } from 'vs/platform/storage/node/storageService'; +import { StorageService } from 'vs/platform/storage/node/storageService'; import { ConfigurationScope, IConfigurationRegistry, Extensions as ConfigurationExtensions, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -238,9 +238,9 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { } private migrateStorage(toWorkspace: IWorkspaceIdentifier): Thenable { - const storageImpl = this.storageService as DelegatingStorageService; + const storageImpl = this.storageService as StorageService; - return storageImpl.storage.migrate(toWorkspace); + return storageImpl.migrate(toWorkspace); } private migrateWorkspaceSettings(toWorkspace: IWorkspaceIdentifier): Thenable {