mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 (#6381)
* Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 * disable strict null check
This commit is contained in:
138
src/vs/platform/storage/browser/storageService.ts
Normal file
138
src/vs/platform/storage/browser/storageService.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IWorkspaceStorageChangeEvent, IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason, logStorage, FileStorageDatabase } from 'vs/platform/storage/common/storage';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IStorage, Storage } from 'vs/base/parts/storage/common/storage';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { runWhenIdle } from 'vs/base/common/async';
|
||||
|
||||
export class BrowserStorageService extends Disposable implements IStorageService {
|
||||
|
||||
_serviceBrand: ServiceIdentifier<any>;
|
||||
|
||||
private readonly _onDidChangeStorage: Emitter<IWorkspaceStorageChangeEvent> = this._register(new Emitter<IWorkspaceStorageChangeEvent>());
|
||||
readonly onDidChangeStorage: Event<IWorkspaceStorageChangeEvent> = this._onDidChangeStorage.event;
|
||||
|
||||
private readonly _onWillSaveState: Emitter<IWillSaveStateEvent> = this._register(new Emitter<IWillSaveStateEvent>());
|
||||
readonly onWillSaveState: Event<IWillSaveStateEvent> = this._onWillSaveState.event;
|
||||
|
||||
private globalStorage: IStorage;
|
||||
private workspaceStorage: IStorage;
|
||||
|
||||
private globalStorageFile: URI;
|
||||
private workspaceStorageFile: URI;
|
||||
|
||||
private initializePromise: Promise<void>;
|
||||
|
||||
constructor(
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService,
|
||||
@IFileService private readonly fileService: IFileService
|
||||
) {
|
||||
super();
|
||||
|
||||
// In the browser we do not have support for long running unload sequences. As such,
|
||||
// we cannot ask for saving state in that moment, because that would result in a
|
||||
// long running operation.
|
||||
// Instead, periodically ask customers to save save. The library will be clever enough
|
||||
// to only save state that has actually changed.
|
||||
this.saveStatePeriodically();
|
||||
}
|
||||
|
||||
private saveStatePeriodically(): void {
|
||||
setTimeout(() => {
|
||||
runWhenIdle(() => {
|
||||
|
||||
// this event will potentially cause new state to be stored
|
||||
this._onWillSaveState.fire({ reason: WillSaveStateReason.NONE });
|
||||
|
||||
// repeat
|
||||
this.saveStatePeriodically();
|
||||
});
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
initialize(payload: IWorkspaceInitializationPayload): Promise<void> {
|
||||
if (!this.initializePromise) {
|
||||
this.initializePromise = this.doInitialize(payload);
|
||||
}
|
||||
|
||||
return this.initializePromise;
|
||||
}
|
||||
|
||||
private async doInitialize(payload: IWorkspaceInitializationPayload): Promise<void> {
|
||||
|
||||
// Ensure state folder exists
|
||||
const stateRoot = joinPath(this.environmentService.userRoamingDataHome, 'state');
|
||||
await this.fileService.createFolder(stateRoot);
|
||||
|
||||
// Workspace Storage
|
||||
this.workspaceStorageFile = joinPath(stateRoot, `${payload.id}.json`);
|
||||
this.workspaceStorage = new Storage(this._register(new FileStorageDatabase(this.workspaceStorageFile, this.fileService)));
|
||||
this._register(this.workspaceStorage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key, scope: StorageScope.WORKSPACE })));
|
||||
|
||||
// Global Storage
|
||||
this.globalStorageFile = joinPath(stateRoot, 'global.json');
|
||||
this.globalStorage = new Storage(this._register(new FileStorageDatabase(this.globalStorageFile, this.fileService)));
|
||||
this._register(this.globalStorage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key, scope: StorageScope.GLOBAL })));
|
||||
|
||||
// Init both
|
||||
await Promise.all([
|
||||
this.workspaceStorage.init(),
|
||||
this.globalStorage.init()
|
||||
]);
|
||||
}
|
||||
|
||||
get(key: string, scope: StorageScope, fallbackValue: string): string;
|
||||
get(key: string, scope: StorageScope): string | undefined;
|
||||
get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined {
|
||||
return this.getStorage(scope).get(key, fallbackValue);
|
||||
}
|
||||
|
||||
getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean;
|
||||
getBoolean(key: string, scope: StorageScope): boolean | undefined;
|
||||
getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined {
|
||||
return this.getStorage(scope).getBoolean(key, fallbackValue);
|
||||
}
|
||||
|
||||
getNumber(key: string, scope: StorageScope, fallbackValue: number): number;
|
||||
getNumber(key: string, scope: StorageScope): number | undefined;
|
||||
getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined {
|
||||
return this.getStorage(scope).getNumber(key, fallbackValue);
|
||||
}
|
||||
|
||||
store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void {
|
||||
this.getStorage(scope).set(key, value);
|
||||
}
|
||||
|
||||
remove(key: string, scope: StorageScope): void {
|
||||
this.getStorage(scope).delete(key);
|
||||
}
|
||||
|
||||
private getStorage(scope: StorageScope): IStorage {
|
||||
return scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage;
|
||||
}
|
||||
|
||||
async logStorage(): Promise<void> {
|
||||
const result = await Promise.all([
|
||||
this.globalStorage.items,
|
||||
this.workspaceStorage.items
|
||||
]);
|
||||
|
||||
return logStorage(result[0], result[1], this.globalStorageFile.toString(), this.workspaceStorageFile.toString());
|
||||
}
|
||||
|
||||
close(): void {
|
||||
|
||||
// Signal as event so that clients can still store data
|
||||
this._onWillSaveState.fire({ reason: WillSaveStateReason.SHUTDOWN });
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,15 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import { IUpdateRequest, IStorageDatabase } from 'vs/base/parts/storage/common/storage';
|
||||
import { serializableToMap, mapToSerializable } from 'vs/base/common/map';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
|
||||
export const IStorageService = createDecorator<IStorageService>('storageService');
|
||||
|
||||
@@ -20,7 +25,8 @@ export interface IWillSaveStateEvent {
|
||||
}
|
||||
|
||||
export interface IStorageService {
|
||||
_serviceBrand: any;
|
||||
|
||||
_serviceBrand: ServiceIdentifier<any>;
|
||||
|
||||
/**
|
||||
* Emitted whenever data is updated or deleted.
|
||||
@@ -35,6 +41,10 @@ export interface IStorageService {
|
||||
* The will save state event allows to optionally ask for the reason of
|
||||
* saving the state, e.g. to find out if the state is saved due to a
|
||||
* shutdown.
|
||||
*
|
||||
* Note: this event may be fired many times, not only on shutdown to prevent
|
||||
* loss of state in situations where the shutdown is not sufficient to
|
||||
* persist the data properly.
|
||||
*/
|
||||
readonly onWillSaveState: Event<IWillSaveStateEvent>;
|
||||
|
||||
@@ -86,6 +96,11 @@ export interface IStorageService {
|
||||
* operation to either the current workspace only or all workspaces.
|
||||
*/
|
||||
remove(key: string, scope: StorageScope): void;
|
||||
|
||||
/**
|
||||
* Log the contents of the storage to the console.
|
||||
*/
|
||||
logStorage(): void;
|
||||
}
|
||||
|
||||
export const enum StorageScope {
|
||||
@@ -107,10 +122,11 @@ export interface IWorkspaceStorageChangeEvent {
|
||||
}
|
||||
|
||||
export class InMemoryStorageService extends Disposable implements IStorageService {
|
||||
_serviceBrand = undefined;
|
||||
|
||||
_serviceBrand = null as any;
|
||||
|
||||
private readonly _onDidChangeStorage: Emitter<IWorkspaceStorageChangeEvent> = this._register(new Emitter<IWorkspaceStorageChangeEvent>());
|
||||
get onDidChangeStorage(): Event<IWorkspaceStorageChangeEvent> { return this._onDidChangeStorage.event; }
|
||||
readonly onDidChangeStorage: Event<IWorkspaceStorageChangeEvent> = this._onDidChangeStorage.event;
|
||||
|
||||
readonly onWillSaveState = Event.None;
|
||||
|
||||
@@ -190,4 +206,110 @@ export class InMemoryStorageService extends Disposable implements IStorageServic
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
logStorage(): void {
|
||||
logStorage(this.globalCache, this.workspaceCache, 'inMemory', 'inMemory');
|
||||
}
|
||||
}
|
||||
|
||||
export class FileStorageDatabase extends Disposable implements IStorageDatabase {
|
||||
|
||||
readonly onDidChangeItemsExternal = Event.None; // TODO@Ben implement global UI storage events
|
||||
|
||||
private cache: Map<string, string> | undefined;
|
||||
|
||||
private pendingUpdate: Promise<void> = Promise.resolve();
|
||||
|
||||
constructor(
|
||||
private readonly file: URI,
|
||||
private readonly fileService: IFileService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async getItems(): Promise<Map<string, string>> {
|
||||
if (!this.cache) {
|
||||
try {
|
||||
this.cache = await this.doGetItemsFromFile();
|
||||
} catch (error) {
|
||||
this.cache = new Map();
|
||||
}
|
||||
}
|
||||
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
private async doGetItemsFromFile(): Promise<Map<string, string>> {
|
||||
await this.pendingUpdate;
|
||||
|
||||
const itemsRaw = await this.fileService.readFile(this.file);
|
||||
|
||||
return serializableToMap(JSON.parse(itemsRaw.value.toString()));
|
||||
}
|
||||
|
||||
async updateItems(request: IUpdateRequest): Promise<void> {
|
||||
const items = await this.getItems();
|
||||
|
||||
if (request.insert) {
|
||||
request.insert.forEach((value, key) => items.set(key, value));
|
||||
}
|
||||
|
||||
if (request.delete) {
|
||||
request.delete.forEach(key => items.delete(key));
|
||||
}
|
||||
|
||||
await this.pendingUpdate;
|
||||
|
||||
this.pendingUpdate = this.fileService.writeFile(this.file, VSBuffer.fromString(JSON.stringify(mapToSerializable(items)))).then();
|
||||
|
||||
return this.pendingUpdate;
|
||||
}
|
||||
|
||||
close(): Promise<void> {
|
||||
return this.pendingUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
export async function logStorage(global: Map<string, string>, workspace: Map<string, string>, globalPath: string, workspacePath: string): Promise<void> {
|
||||
const safeParse = (value: string) => {
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
} catch (error) {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
const globalItems = new Map<string, string>();
|
||||
const globalItemsParsed = new Map<string, string>();
|
||||
global.forEach((value, key) => {
|
||||
globalItems.set(key, value);
|
||||
globalItemsParsed.set(key, safeParse(value));
|
||||
});
|
||||
|
||||
const workspaceItems = new Map<string, string>();
|
||||
const workspaceItemsParsed = new Map<string, string>();
|
||||
workspace.forEach((value, key) => {
|
||||
workspaceItems.set(key, value);
|
||||
workspaceItemsParsed.set(key, safeParse(value));
|
||||
});
|
||||
|
||||
console.group(`Storage: Global (path: ${globalPath})`);
|
||||
let globalValues: { key: string, value: string }[] = [];
|
||||
globalItems.forEach((value, key) => {
|
||||
globalValues.push({ key, value });
|
||||
});
|
||||
console.table(globalValues);
|
||||
console.groupEnd();
|
||||
|
||||
console.log(globalItemsParsed);
|
||||
|
||||
console.group(`Storage: Workspace (path: ${workspacePath})`);
|
||||
let workspaceValues: { key: string, value: string }[] = [];
|
||||
workspaceItems.forEach((value, key) => {
|
||||
workspaceValues.push({ key, value });
|
||||
});
|
||||
console.table(workspaceValues);
|
||||
console.groupEnd();
|
||||
|
||||
console.log(workspaceItemsParsed);
|
||||
}
|
||||
@@ -6,13 +6,13 @@
|
||||
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { StorageMainService, IStorageChangeEvent } from 'vs/platform/storage/node/storageMainService';
|
||||
import { IUpdateRequest, IStorageDatabase, IStorageItemsChangeEvent } from 'vs/base/node/storage';
|
||||
import { IUpdateRequest, IStorageDatabase, IStorageItemsChangeEvent } from 'vs/base/parts/storage/common/storage';
|
||||
import { mapToSerializable, serializableToMap, values } from 'vs/base/common/map';
|
||||
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { instanceStorageKey, firstSessionDateStorageKey, lastSessionDateStorageKey, currentSessionDateStorageKey } from 'vs/platform/telemetry/node/workbenchCommonProperties';
|
||||
import { instanceStorageKey, firstSessionDateStorageKey, lastSessionDateStorageKey, currentSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
||||
type Key = string;
|
||||
type Value = string;
|
||||
@@ -32,7 +32,7 @@ export class GlobalStorageDatabaseChannel extends Disposable implements IServerC
|
||||
private static STORAGE_CHANGE_DEBOUNCE_TIME = 100;
|
||||
|
||||
private readonly _onDidChangeItems: Emitter<ISerializableItemsChangeEvent> = this._register(new Emitter<ISerializableItemsChangeEvent>());
|
||||
get onDidChangeItems(): Event<ISerializableItemsChangeEvent> { return this._onDidChangeItems.event; }
|
||||
readonly onDidChangeItems: Event<ISerializableItemsChangeEvent> = this._onDidChangeItems.event;
|
||||
|
||||
private whenReady: Promise<void>;
|
||||
|
||||
@@ -139,10 +139,6 @@ export class GlobalStorageDatabaseChannel extends Disposable implements IServerC
|
||||
break;
|
||||
}
|
||||
|
||||
case 'checkIntegrity': {
|
||||
return this.storageMainService.checkIntegrity(arg);
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Call not found: ${command}`);
|
||||
}
|
||||
@@ -154,7 +150,7 @@ export class GlobalStorageDatabaseChannelClient extends Disposable implements IS
|
||||
_serviceBrand: any;
|
||||
|
||||
private readonly _onDidChangeItemsExternal: Emitter<IStorageItemsChangeEvent> = this._register(new Emitter<IStorageItemsChangeEvent>());
|
||||
get onDidChangeItemsExternal(): Event<IStorageItemsChangeEvent> { return this._onDidChangeItemsExternal.event; }
|
||||
readonly onDidChangeItemsExternal: Event<IStorageItemsChangeEvent> = this._onDidChangeItemsExternal.event;
|
||||
|
||||
private onDidChangeItemsOnMainListener: IDisposable;
|
||||
|
||||
@@ -181,30 +177,19 @@ export class GlobalStorageDatabaseChannelClient extends Disposable implements IS
|
||||
}
|
||||
|
||||
updateItems(request: IUpdateRequest): Promise<void> {
|
||||
let updateCount = 0;
|
||||
const serializableRequest: ISerializableUpdateRequest = Object.create(null);
|
||||
|
||||
if (request.insert) {
|
||||
serializableRequest.insert = mapToSerializable(request.insert);
|
||||
updateCount += request.insert.size;
|
||||
}
|
||||
|
||||
if (request.delete) {
|
||||
serializableRequest.delete = values(request.delete);
|
||||
updateCount += request.delete.size;
|
||||
}
|
||||
|
||||
if (updateCount === 0) {
|
||||
return Promise.resolve(); // prevent work if not needed
|
||||
}
|
||||
|
||||
return this.channel.call('updateItems', serializableRequest);
|
||||
}
|
||||
|
||||
checkIntegrity(full: boolean): Promise<string> {
|
||||
return this.channel.call('checkIntegrity', full);
|
||||
}
|
||||
|
||||
close(): Promise<void> {
|
||||
|
||||
// when we are about to close, we start to ignore main-side changes since we close anyway
|
||||
|
||||
@@ -3,19 +3,20 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IStorage, Storage, SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions, InMemoryStorageDatabase } from 'vs/base/node/storage';
|
||||
import { SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions } from 'vs/base/parts/storage/node/storage';
|
||||
import { Storage, IStorage, InMemoryStorageDatabase } from 'vs/base/parts/storage/common/storage';
|
||||
import { join } from 'vs/base/common/path';
|
||||
|
||||
export const IStorageMainService = createDecorator<IStorageMainService>('storageMainService');
|
||||
|
||||
export interface IStorageMainService {
|
||||
|
||||
_serviceBrand: any;
|
||||
_serviceBrand: ServiceIdentifier<any>;
|
||||
|
||||
/**
|
||||
* Emitted whenever data is updated or deleted.
|
||||
@@ -26,6 +27,10 @@ export interface IStorageMainService {
|
||||
* Emitted when the storage is about to persist. This is the right time
|
||||
* to persist data to ensure it is stored before the application shuts
|
||||
* down.
|
||||
*
|
||||
* Note: this event may be fired many times, not only on shutdown to prevent
|
||||
* loss of state in situations where the shutdown is not sufficient to
|
||||
* persist the data properly.
|
||||
*/
|
||||
readonly onWillSaveState: Event<void>;
|
||||
|
||||
@@ -70,15 +75,15 @@ export interface IStorageChangeEvent {
|
||||
|
||||
export class StorageMainService extends Disposable implements IStorageMainService {
|
||||
|
||||
_serviceBrand: any;
|
||||
_serviceBrand: ServiceIdentifier<any>;
|
||||
|
||||
private static STORAGE_NAME = 'state.vscdb';
|
||||
|
||||
private readonly _onDidChangeStorage: Emitter<IStorageChangeEvent> = this._register(new Emitter<IStorageChangeEvent>());
|
||||
get onDidChangeStorage(): Event<IStorageChangeEvent> { return this._onDidChangeStorage.event; }
|
||||
readonly onDidChangeStorage: Event<IStorageChangeEvent> = this._onDidChangeStorage.event;
|
||||
|
||||
private readonly _onWillSaveState: Emitter<void> = this._register(new Emitter<void>());
|
||||
get onWillSaveState(): Event<void> { return this._onWillSaveState.event; }
|
||||
readonly onWillSaveState: Event<void> = this._onWillSaveState.event;
|
||||
|
||||
get items(): Map<string, string> { return this.storage.items; }
|
||||
|
||||
@@ -164,8 +169,4 @@ export class StorageMainService extends Disposable implements IStorageMainServic
|
||||
// Do it
|
||||
return this.storage.close();
|
||||
}
|
||||
|
||||
checkIntegrity(full: boolean): Promise<string> {
|
||||
return this.storage.checkIntegrity(full);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,29 +6,29 @@
|
||||
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
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 { 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 { IWorkspaceStorageChangeEvent, IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason, logStorage } from 'vs/platform/storage/common/storage';
|
||||
import { SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions } from 'vs/base/parts/storage/node/storage';
|
||||
import { Storage, IStorageDatabase, IStorage, StorageHint } from 'vs/base/parts/storage/common/storage';
|
||||
import { mark } from 'vs/base/common/performance';
|
||||
import { join } from 'vs/base/common/path';
|
||||
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 { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export class StorageService extends Disposable implements IStorageService {
|
||||
_serviceBrand: any;
|
||||
|
||||
_serviceBrand: ServiceIdentifier<any>;
|
||||
|
||||
private static WORKSPACE_STORAGE_NAME = 'state.vscdb';
|
||||
private static WORKSPACE_META_NAME = 'workspace.json';
|
||||
|
||||
private readonly _onDidChangeStorage: Emitter<IWorkspaceStorageChangeEvent> = this._register(new Emitter<IWorkspaceStorageChangeEvent>());
|
||||
get onDidChangeStorage(): Event<IWorkspaceStorageChangeEvent> { return this._onDidChangeStorage.event; }
|
||||
readonly onDidChangeStorage: Event<IWorkspaceStorageChangeEvent> = this._onDidChangeStorage.event;
|
||||
|
||||
private readonly _onWillSaveState: Emitter<IWillSaveStateEvent> = this._register(new Emitter<IWillSaveStateEvent>());
|
||||
get onWillSaveState(): Event<IWillSaveStateEvent> { return this._onWillSaveState.event; }
|
||||
readonly onWillSaveState: Event<IWillSaveStateEvent> = this._onWillSaveState.event;
|
||||
|
||||
private globalStorage: IStorage;
|
||||
|
||||
@@ -201,63 +201,13 @@ export class StorageService extends Disposable implements IStorageService {
|
||||
return scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage;
|
||||
}
|
||||
|
||||
getSize(scope: StorageScope): number {
|
||||
return scope === StorageScope.GLOBAL ? this.globalStorage.size : this.workspaceStorage.size;
|
||||
}
|
||||
|
||||
checkIntegrity(scope: StorageScope, full: boolean): Promise<string> {
|
||||
return scope === StorageScope.GLOBAL ? this.globalStorage.checkIntegrity(full) : this.workspaceStorage.checkIntegrity(full);
|
||||
}
|
||||
|
||||
async logStorage(): Promise<void> {
|
||||
const result = await Promise.all([
|
||||
this.globalStorage.items,
|
||||
this.workspaceStorage.items,
|
||||
this.globalStorage.checkIntegrity(true /* full */),
|
||||
this.workspaceStorage.checkIntegrity(true /* full */)
|
||||
this.workspaceStorage.items
|
||||
]);
|
||||
|
||||
const safeParse = (value: string) => {
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
} catch (error) {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
const globalItems = new Map<string, string>();
|
||||
const globalItemsParsed = new Map<string, string>();
|
||||
result[0].forEach((value, key) => {
|
||||
globalItems.set(key, value);
|
||||
globalItemsParsed.set(key, safeParse(value));
|
||||
});
|
||||
|
||||
const workspaceItems = new Map<string, string>();
|
||||
const workspaceItemsParsed = new Map<string, string>();
|
||||
result[1].forEach((value, key) => {
|
||||
workspaceItems.set(key, value);
|
||||
workspaceItemsParsed.set(key, safeParse(value));
|
||||
});
|
||||
|
||||
console.group(`Storage: Global (integrity: ${result[2]}, path: ${this.environmentService.globalStorageHome})`);
|
||||
let globalValues: { key: string, value: string }[] = [];
|
||||
globalItems.forEach((value, key) => {
|
||||
globalValues.push({ key, value });
|
||||
});
|
||||
console.table(globalValues);
|
||||
console.groupEnd();
|
||||
|
||||
console.log(globalItemsParsed);
|
||||
|
||||
console.group(`Storage: Workspace (integrity: ${result[3]}, load: ${getDuration('willInitWorkspaceStorage', 'didInitWorkspaceStorage')}, path: ${this.workspaceStoragePath})`);
|
||||
let workspaceValues: { key: string, value: string }[] = [];
|
||||
workspaceItems.forEach((value, key) => {
|
||||
workspaceValues.push({ key, value });
|
||||
});
|
||||
console.table(workspaceValues);
|
||||
console.groupEnd();
|
||||
|
||||
console.log(workspaceItemsParsed);
|
||||
logStorage(result[0], result[1], this.environmentService.globalStorageHome, this.workspaceStoragePath);
|
||||
}
|
||||
|
||||
async migrate(toWorkspace: IWorkspaceInitializationPayload): Promise<void> {
|
||||
@@ -280,24 +230,3 @@ export class StorageService extends Disposable implements IStorageService {
|
||||
return this.createWorkspaceStorage(newWorkspaceStoragePath).init();
|
||||
}
|
||||
}
|
||||
|
||||
export class LogStorageAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.logStorage';
|
||||
static LABEL = localize({ key: 'logStorage', comment: ['A developer only action to log the contents of the storage for the current window.'] }, "Log Storage Database Contents");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IStorageService private readonly storageService: StorageService,
|
||||
@IWindowService private readonly windowService: IWindowService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
this.storageService.logStorage();
|
||||
|
||||
return this.windowService.openDevTools();
|
||||
}
|
||||
}
|
||||
|
||||
92
src/vs/platform/storage/test/node/storage.test.ts
Normal file
92
src/vs/platform/storage/test/node/storage.test.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { equal } from 'assert';
|
||||
import { FileStorageDatabase } from 'vs/platform/storage/common/storage';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { tmpdir } from 'os';
|
||||
import { rimraf, RimRafMode } from 'vs/base/node/pfs';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { Storage } from 'vs/base/parts/storage/common/storage';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { FileService } from 'vs/platform/files/common/fileService';
|
||||
import { getRandomTestPath } from 'vs/base/test/node/testUtils';
|
||||
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
suite('Storage', () => {
|
||||
|
||||
const parentDir = getRandomTestPath(tmpdir(), 'vsctests', 'storageservice');
|
||||
|
||||
let fileService: FileService;
|
||||
let fileProvider: DiskFileSystemProvider;
|
||||
let testDir: string;
|
||||
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
setup(async () => {
|
||||
const logService = new NullLogService();
|
||||
|
||||
fileService = new FileService(logService);
|
||||
disposables.add(fileService);
|
||||
|
||||
fileProvider = new DiskFileSystemProvider(logService);
|
||||
disposables.add(fileService.registerProvider(Schemas.file, fileProvider));
|
||||
disposables.add(fileProvider);
|
||||
|
||||
const id = generateUuid();
|
||||
testDir = join(parentDir, id);
|
||||
});
|
||||
|
||||
teardown(async () => {
|
||||
disposables.clear();
|
||||
|
||||
await rimraf(parentDir, RimRafMode.MOVE);
|
||||
});
|
||||
|
||||
test('File Based Storage', async () => {
|
||||
let storage = new Storage(new FileStorageDatabase(URI.file(join(testDir, 'storage.json')), fileService));
|
||||
|
||||
await storage.init();
|
||||
|
||||
storage.set('bar', 'foo');
|
||||
storage.set('barNumber', 55);
|
||||
storage.set('barBoolean', true);
|
||||
|
||||
equal(storage.get('bar'), 'foo');
|
||||
equal(storage.get('barNumber'), '55');
|
||||
equal(storage.get('barBoolean'), 'true');
|
||||
|
||||
await storage.close();
|
||||
|
||||
storage = new Storage(new FileStorageDatabase(URI.file(join(testDir, 'storage.json')), fileService));
|
||||
|
||||
await storage.init();
|
||||
|
||||
equal(storage.get('bar'), 'foo');
|
||||
equal(storage.get('barNumber'), '55');
|
||||
equal(storage.get('barBoolean'), 'true');
|
||||
|
||||
storage.delete('bar');
|
||||
storage.delete('barNumber');
|
||||
storage.delete('barBoolean');
|
||||
|
||||
equal(storage.get('bar', 'undefined'), 'undefined');
|
||||
equal(storage.get('barNumber', 'undefinedNumber'), 'undefinedNumber');
|
||||
equal(storage.get('barBoolean', 'undefinedBoolean'), 'undefinedBoolean');
|
||||
|
||||
await storage.close();
|
||||
|
||||
storage = new Storage(new FileStorageDatabase(URI.file(join(testDir, 'storage.json')), fileService));
|
||||
|
||||
await storage.init();
|
||||
|
||||
equal(storage.get('bar', 'undefined'), 'undefined');
|
||||
equal(storage.get('barNumber', 'undefinedNumber'), 'undefinedNumber');
|
||||
equal(storage.get('barBoolean', 'undefinedBoolean'), 'undefinedBoolean');
|
||||
});
|
||||
});
|
||||
@@ -13,7 +13,7 @@ import { mkdirp, rimraf, RimRafMode } from 'vs/base/node/pfs';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
|
||||
import { parseArgs } from 'vs/platform/environment/node/argv';
|
||||
import { InMemoryStorageDatabase } from 'vs/base/node/storage';
|
||||
import { InMemoryStorageDatabase } from 'vs/base/parts/storage/common/storage';
|
||||
|
||||
suite('StorageService', () => {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user