From 8250a407510397a640d7f117f8a6f8304a338e49 Mon Sep 17 00:00:00 2001 From: nihonium Date: Thu, 27 Jul 2023 21:41:47 +0200 Subject: [PATCH] feat: add overridable storage service --- src/service-override/storage.ts | 128 ++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/service-override/storage.ts diff --git a/src/service-override/storage.ts b/src/service-override/storage.ts new file mode 100644 index 00000000..bfcb56bd --- /dev/null +++ b/src/service-override/storage.ts @@ -0,0 +1,128 @@ +import { Event } from 'vs/base/common/event' +import { IDisposable } from 'vs/base/common/lifecycle' +import { IStorage, IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest, Storage } from 'vs/base/parts/storage/common/storage' +import { IEditorOverrideServices } from 'vs/editor/standalone/browser/standaloneServices' +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors' +import { AbstractStorageService, IStorageService, StorageScope as VSStorageScope } from 'vs/platform/storage/common/storage' +import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile' +import { IAnyWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace' + +export enum StorageScope { + APPLICATION = VSStorageScope.APPLICATION, + PROFILE = VSStorageScope.PROFILE, + WORKSPACE = VSStorageScope.WORKSPACE +} + +export interface IStorageProvider { + read (scope: StorageScope): Map | undefined + write (scope: StorageScope, data: Map): Promise + close? (scope: StorageScope): Promise + onDidChange? (listener: (event: IStorageItemsChangeEvent) => void): IDisposable +} + +class ExternalStorage extends Storage { + constructor ( + scope: StorageScope, + provider: IStorageProvider + ) { + const items = provider.read(scope) + + super(new ExternalStorageDatabase(scope, provider, items)) + + if (items != null) { + for (const [key, value] of items) { + this.items.set(key, value) + } + } + } +} + +class ExternalStorageDatabase implements IStorageDatabase { + readonly onDidChangeItemsExternal: Event + + constructor ( + private readonly scope: StorageScope, + private readonly provider: IStorageProvider, + private readonly items = new Map() + ) { + this.onDidChangeItemsExternal = this.provider.onDidChange ?? Event.None + } + + async getItems (): Promise> { + return this.items + } + + async updateItems (request: IUpdateRequest): Promise { + request.insert?.forEach((value, key) => this.items.set(key, value)) + + request.delete?.forEach(key => this.items.delete(key)) + + await this.provider.write(this.scope, this.items) + } + + async close () { + return this.provider.close?.(this.scope) + } +} + +class ExternalStorageService extends AbstractStorageService { + private readonly applicationStorage = this._register(new ExternalStorage(StorageScope.APPLICATION, this.provider)) + private readonly profileStorage = this._register(new ExternalStorage(StorageScope.PROFILE, this.provider)) + private readonly workspaceStorage = this._register(new ExternalStorage(StorageScope.WORKSPACE, this.provider)) + + constructor (protected readonly provider: IStorageProvider) { + super() + + this._register(this.workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(VSStorageScope.WORKSPACE, key))) + this._register(this.profileStorage.onDidChangeStorage(key => this.emitDidChangeValue(VSStorageScope.PROFILE, key))) + this._register(this.applicationStorage.onDidChangeStorage(key => this.emitDidChangeValue(VSStorageScope.APPLICATION, key))) + } + + protected getStorage (scope: VSStorageScope): IStorage { + switch (scope) { + case VSStorageScope.APPLICATION: + return this.applicationStorage + case VSStorageScope.PROFILE: + return this.profileStorage + default: + return this.workspaceStorage + } + } + + protected getLogDetails (scope: VSStorageScope): string | undefined { + switch (scope) { + case VSStorageScope.APPLICATION: + return 'External (application)' + case VSStorageScope.PROFILE: + return 'External (profile)' + default: + return 'External (workspace)' + } + } + + protected async doInitialize (): Promise { + // no-op + } + + protected async switchToProfile (): Promise { + // no-op + } + + protected async switchToWorkspace (): Promise { + // no-op + } + + hasScope (_scope: IAnyWorkspaceIdentifier | IUserDataProfile): boolean { + return false + } +} + +export default function getStorageServiceOverride (provider: IStorageProvider): IEditorOverrideServices { + return { + [IStorageService.toString()]: new SyncDescriptor(ExternalStorageService, [provider]) + } +} + +export { + IStorageItemsChangeEvent +}