未验证 提交 d38c5b86 编写于 作者: B Benjamin Pasero 提交者: GitHub

storage - enable global storage, disable workspace storage migration (#64719)

上级 29179eaa
......@@ -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);
......
......@@ -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",
......
......@@ -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
}
......
/*---------------------------------------------------------------------------------------------
* 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/<key>
*
* [Workspace]
* storage://workspace/<folder>/<key>
* storage://workspace/empty:<id>/<key>
* storage://workspace/root:<id>/<key>
*
* <folder>
* 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__<key>
* => 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<string, StorageObject>;
folder: Map<string, StorageObject>;
empty: Map<string, StorageObject>;
noWorkspace: StorageObject;
}
export function parseFolderStorage(storage: IStorageLegacy, folderId: string): StorageObject {
const workspaces: { prefix: string; resource: string; }[] = [];
const activeKeys = new Set<string>();
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/<folder>/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/<folder>/workspaceIdentifier => <folder>/
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/<folder>/workspaceIdentifier => storage://workspace/<folder>/
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/<folder>/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:<id>/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:<id>/someKey => someKey
multiRootWorkspaceStorage[key.substr(multiRootStoragePrefix.length)] = storage.getItem(key);
}
}
return multiRootWorkspaceStorage;
}
/*---------------------------------------------------------------------------------------------
* 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<IStorageLegacyService>(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
......@@ -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<IStorageChangeEvent> = this._register(new Emitter<IStorageChangeEvent>());
get onDidChangeStorage(): Event<IStorageChangeEvent> { 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<void> {
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) {
......
......@@ -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<boolean>;
if (useInMemoryStorage) {
workspaceStoragePath = SQLiteStorageDatabase.IN_MEMORY_PATH;
workspaceStorageExists = Promise.resolve(true);
} else {
workspaceStoragePath = join(result.path, StorageService.WORKSPACE_STORAGE_NAME);
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(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');
// 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<void>;
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<void> {
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<string, string>();
[
'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<void> {
this.storageService.storage.logStorage();
this.storageService.logStorage();
return this.windowService.openDevTools();
}
}
export class DelegatingStorageService extends Disposable implements IStorageService {
_serviceBrand: any;
private _onDidChangeStorage: Emitter<IWorkspaceStorageChangeEvent> = this._register(new Emitter<IWorkspaceStorageChangeEvent>());
get onDidChangeStorage(): Event<IWorkspaceStorageChangeEvent> { return this._onDidChangeStorage.event; }
private _onWillSaveState: Emitter<IWillSaveStateEvent> = this._register(new Emitter<IWillSaveStateEvent>());
get onWillSaveState(): Event<IWillSaveStateEvent> { 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<boolean>('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<void> {
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
/*---------------------------------------------------------------------------------------------
* 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
......@@ -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;
}
......@@ -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();
}
......
......@@ -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
}
}
});
......
......@@ -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<void> {
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 = <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);
......
......@@ -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<WillShutdownEvent>());
get onWillShutdown(): Event<WillShutdownEvent> { 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<string>();
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<NUMBER>" : {
"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<NUMBER>" : {
"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
});
}
......
......@@ -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;
......
......@@ -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<void> {
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<void> {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册