Skip to content

Commit

Permalink
feat: add overridable storage service
Browse files Browse the repository at this point in the history
  • Loading branch information
nihonium committed Aug 8, 2023
1 parent bcc5ea9 commit 8250a40
Showing 1 changed file with 128 additions and 0 deletions.
128 changes: 128 additions & 0 deletions src/service-override/storage.ts
Original file line number Diff line number Diff line change
@@ -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<string, string> | undefined
write (scope: StorageScope, data: Map<string, string>): Promise<void>
close? (scope: StorageScope): Promise<void>
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<IStorageItemsChangeEvent>

constructor (
private readonly scope: StorageScope,
private readonly provider: IStorageProvider,
private readonly items = new Map<string, string>()
) {
this.onDidChangeItemsExternal = this.provider.onDidChange ?? Event.None
}

async getItems (): Promise<Map<string, string>> {
return this.items
}

async updateItems (request: IUpdateRequest): Promise<void> {
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<void> {
// no-op
}

protected async switchToProfile (): Promise<void> {
// no-op
}

protected async switchToWorkspace (): Promise<void> {
// 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
}

0 comments on commit 8250a40

Please sign in to comment.