Merge from vscode 8df646d3c5477b02737fc10343fa7cf0cc3f606b

This commit is contained in:
ADS Merger
2020-03-25 06:20:54 +00:00
parent 6e5fbc9012
commit d810da9d87
114 changed files with 2036 additions and 797 deletions

View File

@@ -4,63 +4,120 @@
*--------------------------------------------------------------------------------------------*/
import * as objects from 'vs/base/common/objects';
import { IGlobalState } from 'vs/platform/userDataSync/common/userDataSync';
import { IStorageValue } from 'vs/platform/userDataSync/common/userDataSync';
import { IStringDictionary } from 'vs/base/common/collections';
import { values } from 'vs/base/common/map';
import { IStorageKey } from 'vs/platform/userDataSync/common/storageKeys';
import { ILogService } from 'vs/platform/log/common/log';
export function merge(localGloablState: IGlobalState, remoteGlobalState: IGlobalState | null, lastSyncGlobalState: IGlobalState | null): { local?: IGlobalState, remote?: IGlobalState } {
if (!remoteGlobalState) {
return { remote: localGloablState };
}
const { local: localArgv, remote: remoteArgv } = doMerge(localGloablState.argv, remoteGlobalState.argv, lastSyncGlobalState ? lastSyncGlobalState.argv : null);
const { local: localStorage, remote: remoteStorage } = doMerge(localGloablState.storage, remoteGlobalState.storage, lastSyncGlobalState ? lastSyncGlobalState.storage : null);
const local: IGlobalState | undefined = localArgv || localStorage ? { argv: localArgv || localGloablState.argv, storage: localStorage || localGloablState.storage } : undefined;
const remote: IGlobalState | undefined = remoteArgv || remoteStorage ? { argv: remoteArgv || remoteGlobalState.argv, storage: remoteStorage || remoteGlobalState.storage } : undefined;
return { local, remote };
export interface IMergeResult {
local: { added: IStringDictionary<IStorageValue>, removed: string[], updated: IStringDictionary<IStorageValue> };
remote: IStringDictionary<IStorageValue> | null;
}
function doMerge(local: IStringDictionary<any>, remote: IStringDictionary<any>, base: IStringDictionary<any> | null): { local?: IStringDictionary<any>, remote?: IStringDictionary<any> } {
const localToRemote = compare(local, remote);
export function merge(localStorage: IStringDictionary<IStorageValue>, remoteStorage: IStringDictionary<IStorageValue> | null, baseStorage: IStringDictionary<IStorageValue> | null, storageKeys: ReadonlyArray<IStorageKey>, logService: ILogService): IMergeResult {
if (!remoteStorage) {
return { remote: localStorage, local: { added: {}, removed: [], updated: {} } };
}
const localToRemote = compare(localStorage, remoteStorage);
if (localToRemote.added.size === 0 && localToRemote.removed.size === 0 && localToRemote.updated.size === 0) {
// No changes found between local and remote.
return {};
return { remote: null, local: { added: {}, removed: [], updated: {} } };
}
const baseToRemote = base ? compare(base, remote) : { added: Object.keys(remote).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
if (baseToRemote.added.size === 0 && baseToRemote.removed.size === 0 && baseToRemote.updated.size === 0) {
// No changes found between base and remote.
return { remote: local };
}
const baseToRemote = baseStorage ? compare(baseStorage, remoteStorage) : { added: Object.keys(remoteStorage).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
const baseToLocal = baseStorage ? compare(baseStorage, localStorage) : { added: Object.keys(localStorage).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
const baseToLocal = base ? compare(base, local) : { added: Object.keys(local).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
if (baseToLocal.added.size === 0 && baseToLocal.removed.size === 0 && baseToLocal.updated.size === 0) {
// No changes found between base and local.
return { local: remote };
}
const merged = objects.deepClone(local);
const local: { added: IStringDictionary<IStorageValue>, removed: string[], updated: IStringDictionary<IStorageValue> } = { added: {}, removed: [], updated: {} };
const remote: IStringDictionary<IStorageValue> = objects.deepClone(remoteStorage);
// Added in remote
for (const key of values(baseToRemote.added)) {
merged[key] = remote[key];
const remoteValue = remoteStorage[key];
const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0];
if (!storageKey) {
logService.info(`GlobalState: Skipped updating ${key} in storage. It is not registered to sync.`);
continue;
}
if (storageKey.version !== remoteValue.version) {
logService.info(`GlobalState: Skipped updating ${key} in storage. Local version '${storageKey.version}' and remote version '${remoteValue.version} are not same.`);
continue;
}
const localValue = localStorage[key];
if (localValue && localValue.value === remoteValue.value) {
continue;
}
if (baseToLocal.added.has(key)) {
local.updated[key] = remoteValue;
} else {
local.added[key] = remoteValue;
}
}
// Updated in Remote
for (const key of values(baseToRemote.updated)) {
merged[key] = remote[key];
const remoteValue = remoteStorage[key];
const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0];
if (!storageKey) {
logService.info(`GlobalState: Skipped updating ${key} in storage. It is not registered to sync.`);
continue;
}
if (storageKey.version !== remoteValue.version) {
logService.info(`GlobalState: Skipped updating ${key} in storage. Local version '${storageKey.version}' and remote version '${remoteValue.version} are not same.`);
continue;
}
const localValue = localStorage[key];
if (localValue && localValue.value === remoteValue.value) {
continue;
}
local.updated[key] = remoteValue;
}
// Removed in remote & local
// Removed in remote
for (const key of values(baseToRemote.removed)) {
// Got removed in local
if (baseToLocal.removed.has(key)) {
delete merged[key];
const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0];
if (!storageKey) {
logService.info(`GlobalState: Skipped updating ${key} in storage. It is not registered to sync.`);
continue;
}
local.removed.push(key);
}
// Added in local
for (const key of values(baseToLocal.added)) {
if (!baseToRemote.added.has(key)) {
remote[key] = localStorage[key];
}
}
return { local: merged, remote: merged };
// Updated in local
for (const key of values(baseToLocal.updated)) {
if (baseToRemote.updated.has(key) || baseToRemote.removed.has(key)) {
continue;
}
const remoteValue = remote[key];
const localValue = localStorage[key];
if (localValue.version < remoteValue.version) {
continue;
}
remote[key] = localValue;
}
// Removed in local
for (const key of values(baseToLocal.removed)) {
if (baseToRemote.updated.has(key)) {
continue;
}
const remoteValue = remote[key];
const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0];
if (storageKey && storageKey.version < remoteValue.version) {
continue;
}
delete remote[key];
}
return { local, remote: areSame(remote, remoteStorage) ? null : remote };
}
function compare(from: IStringDictionary<any>, to: IStringDictionary<any>): { added: Set<string>, removed: Set<string>, updated: Set<string> } {
@@ -83,3 +140,9 @@ function compare(from: IStringDictionary<any>, to: IStringDictionary<any>): { ad
return { added, removed, updated };
}
function areSame(a: IStringDictionary<IStorageValue>, b: IStringDictionary<IStorageValue>): boolean {
const { added, removed, updated } = compare(a, b);
return added.size === 0 && removed.size === 0 && updated.size === 0;
}

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IGlobalState, SyncResource, IUserDataSynchroniser, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, ISyncResourceHandle } from 'vs/platform/userDataSync/common/userDataSync';
import { SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IGlobalState, SyncResource, IUserDataSynchroniser, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, ISyncResourceHandle, IStorageValue } from 'vs/platform/userDataSync/common/userDataSync';
import { VSBuffer } from 'vs/base/common/buffer';
import { Event } from 'vs/base/common/event';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
@@ -19,12 +19,15 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { URI } from 'vs/base/common/uri';
import { format } from 'vs/base/common/jsonFormatter';
import { applyEdits } from 'vs/base/common/jsonEdit';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageKeysSyncRegistryService, IStorageKey } from 'vs/platform/userDataSync/common/storageKeys';
const argvStoragePrefx = 'globalState.argv.';
const argvProperties: string[] = ['locale'];
interface ISyncPreviewResult {
readonly local: IGlobalState | undefined;
readonly remote: IGlobalState | undefined;
readonly local: { added: IStringDictionary<IStorageValue>, removed: string[], updated: IStringDictionary<IStorageValue> };
readonly remote: IStringDictionary<IStorageValue> | null;
readonly localUserData: IGlobalState;
readonly remoteUserData: IRemoteUserData;
readonly lastSyncUserData: IRemoteUserData | null;
@@ -43,10 +46,13 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
@IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService,
@ITelemetryService telemetryService: ITelemetryService,
@IConfigurationService configurationService: IConfigurationService,
@IStorageService private readonly storageService: IStorageService,
@IStorageKeysSyncRegistryService private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
) {
super(SyncResource.GlobalState, fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService);
this._register(this.fileService.watch(dirname(this.environmentService.argvResource)));
this._register(Event.filter(this.fileService.onDidFilesChange, e => e.contains(this.environmentService.argvResource))(() => this._onDidChangeLocal.fire()));
this._register(Event.filter(this.storageService.onDidChangeStorage, e => storageKeysSyncRegistryService.storageKeys.some(({ key }) => e.key === key))(() => this._onDidChangeLocal.fire()));
}
async pull(): Promise<void> {
@@ -65,9 +71,10 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
const remoteUserData = await this.getRemoteUserData(lastSyncUserData);
if (remoteUserData.syncData !== null) {
const localUserData = await this.getLocalGlobalState();
const local: IGlobalState = JSON.parse(remoteUserData.syncData.content);
await this.apply({ local, remote: undefined, remoteUserData, localUserData, lastSyncUserData });
const localGlobalState = await this.getLocalGlobalState();
const remoteGlobalState: IGlobalState = JSON.parse(remoteUserData.syncData.content);
const { local, remote } = merge(localGlobalState.storage, remoteGlobalState.storage, null, this.getSyncStorageKeys(), this.logService);
await this.apply({ local, remote, remoteUserData, localUserData: localGlobalState, lastSyncUserData });
}
// No remote exists to pull
@@ -96,7 +103,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
const localUserData = await this.getLocalGlobalState();
const lastSyncUserData = await this.getLastSyncUserData();
const remoteUserData = await this.getRemoteUserData(lastSyncUserData);
await this.apply({ local: undefined, remote: localUserData, remoteUserData, localUserData, lastSyncUserData }, true);
await this.apply({ local: { added: {}, removed: [], updated: {} }, remote: localUserData.storage, remoteUserData, localUserData, lastSyncUserData }, true);
this.logService.info(`${this.syncResourceLogLabel}: Finished pushing UI State.`);
} finally {
@@ -136,8 +143,8 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
async hasLocalData(): Promise<boolean> {
try {
const localGloablState = await this.getLocalGlobalState();
if (localGloablState.argv['locale'] !== 'en') {
const { storage } = await this.getLocalGlobalState();
if (Object.keys(storage).length > 1 || storage[`${argvStoragePrefx}.locale`]?.value !== 'en') {
return true;
}
} catch (error) {
@@ -154,7 +161,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
private async getPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise<ISyncPreviewResult> {
const remoteGlobalState: IGlobalState = remoteUserData.syncData ? JSON.parse(remoteUserData.syncData.content) : null;
const lastSyncGlobalState = lastSyncUserData && lastSyncUserData.syncData ? JSON.parse(lastSyncUserData.syncData.content) : null;
const lastSyncGlobalState: IGlobalState = lastSyncUserData && lastSyncUserData.syncData ? JSON.parse(lastSyncUserData.syncData.content) : null;
const localGloablState = await this.getLocalGlobalState();
@@ -164,20 +171,21 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
this.logService.trace(`${this.syncResourceLogLabel}: Remote ui state does not exist. Synchronizing ui state for the first time.`);
}
const { local, remote } = merge(localGloablState, remoteGlobalState, lastSyncGlobalState);
const { local, remote } = merge(localGloablState.storage, remoteGlobalState ? remoteGlobalState.storage : null, lastSyncGlobalState ? lastSyncGlobalState.storage : null, this.getSyncStorageKeys(), this.logService);
return { local, remote, remoteUserData, localUserData: localGloablState, lastSyncUserData };
}
private async apply({ local, remote, remoteUserData, lastSyncUserData, localUserData }: ISyncPreviewResult, forcePush?: boolean): Promise<void> {
const hasChanges = local || remote;
const hasLocalChanged = Object.keys(local.added).length > 0 || Object.keys(local.updated).length > 0 || local.removed.length > 0;
const hasRemoteChanged = remote !== null;
if (!hasChanges) {
if (!hasLocalChanged && !hasRemoteChanged) {
this.logService.info(`${this.syncResourceLogLabel}: No changes found during synchronizing ui state.`);
}
if (local) {
if (hasLocalChanged) {
// update local
this.logService.trace(`${this.syncResourceLogLabel}: Updating local ui state...`);
await this.backupLocal(JSON.stringify(localUserData));
@@ -185,10 +193,10 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
this.logService.info(`${this.syncResourceLogLabel}: Updated local ui state`);
}
if (remote) {
if (hasRemoteChanged) {
// update remote
this.logService.trace(`${this.syncResourceLogLabel}: Updating remote ui state...`);
const content = JSON.stringify(remote);
const content = JSON.stringify(<IGlobalState>{ storage: remote });
remoteUserData = await this.updateRemoteUserData(content, forcePush ? null : remoteUserData.ref);
this.logService.info(`${this.syncResourceLogLabel}: Updated remote ui state`);
}
@@ -202,16 +210,21 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
}
private async getLocalGlobalState(): Promise<IGlobalState> {
const argv: IStringDictionary<any> = {};
const storage: IStringDictionary<any> = {};
const storage: IStringDictionary<IStorageValue> = {};
const argvContent: string = await this.getLocalArgvContent();
const argvValue: IStringDictionary<any> = parse(argvContent);
for (const argvProperty of argvProperties) {
if (argvValue[argvProperty] !== undefined) {
argv[argvProperty] = argvValue[argvProperty];
storage[`${argvStoragePrefx}${argvProperty}`] = { version: 1, value: argvValue[argvProperty] };
}
}
return { argv, storage };
for (const { key, version } of this.storageKeysSyncRegistryService.storageKeys) {
const value = this.storageService.get(key, StorageScope.GLOBAL);
if (value) {
storage[key] = { version, value };
}
}
return { storage };
}
private async getLocalArgvContent(): Promise<string> {
@@ -222,15 +235,59 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
return '{}';
}
private async writeLocalGlobalState(globalState: IGlobalState): Promise<void> {
const argvContent = await this.getLocalArgvContent();
let content = argvContent;
for (const argvProperty of Object.keys(globalState.argv)) {
content = edit(content, [argvProperty], globalState.argv[argvProperty], {});
private async writeLocalGlobalState({ added, removed, updated }: { added: IStringDictionary<IStorageValue>, updated: IStringDictionary<IStorageValue>, removed: string[] }): Promise<void> {
const argv: IStringDictionary<any> = {};
const updatedStorage: IStringDictionary<any> = {};
const handleUpdatedStorage = (keys: string[], storage?: IStringDictionary<IStorageValue>): void => {
for (const key of keys) {
if (key.startsWith(argvStoragePrefx)) {
argv[key.substring(argvStoragePrefx.length)] = storage ? storage[key].value : undefined;
continue;
}
if (storage) {
const storageValue = storage[key];
if (storageValue.value !== String(this.storageService.get(key, StorageScope.GLOBAL))) {
updatedStorage[key] = storageValue.value;
}
} else {
if (this.storageService.get(key, StorageScope.GLOBAL) !== undefined) {
updatedStorage[key] = undefined;
}
}
}
};
handleUpdatedStorage(Object.keys(added), added);
handleUpdatedStorage(Object.keys(updated), updated);
handleUpdatedStorage(removed);
if (Object.keys(argv).length) {
this.logService.trace(`${this.syncResourceLogLabel}: Updating locale...`);
await this.updateArgv(argv);
this.logService.info(`${this.syncResourceLogLabel}: Updated locale`);
}
if (argvContent !== content) {
await this.fileService.writeFile(this.environmentService.argvResource, VSBuffer.fromString(content));
const updatedStorageKeys: string[] = Object.keys(updatedStorage);
if (updatedStorageKeys.length) {
this.logService.trace(`${this.syncResourceLogLabel}: Updating global state...`);
for (const key of Object.keys(updatedStorage)) {
this.storageService.store(key, updatedStorage[key], StorageScope.GLOBAL);
}
this.logService.info(`${this.syncResourceLogLabel}: Updated global state`, Object.keys(updatedStorage));
}
}
private async updateArgv(argv: IStringDictionary<any>): Promise<void> {
const argvContent = await this.getLocalArgvContent();
let content = argvContent;
for (const argvProperty of Object.keys(argv)) {
content = edit(content, [argvProperty], argv[argvProperty], {});
}
if (argvContent !== content) {
this.logService.trace(`${this.syncResourceLogLabel}: Updating locale...`);
await this.fileService.writeFile(this.environmentService.argvResource, VSBuffer.fromString(content));
this.logService.info(`${this.syncResourceLogLabel}: Updated locale.`);
}
}
private getSyncStorageKeys(): IStorageKey[] {
return [...this.storageKeysSyncRegistryService.storageKeys, ...argvProperties.map(argvProprety => (<IStorageKey>{ key: `${argvStoragePrefx}${argvProprety}`, version: 1 }))];
}
}

View File

@@ -0,0 +1,63 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import { values } from 'vs/base/common/map';
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export interface IStorageKey {
readonly key: string;
readonly version: number;
}
export const IStorageKeysSyncRegistryService = createDecorator<IStorageKeysSyncRegistryService>('IStorageKeysSyncRegistryService');
export interface IStorageKeysSyncRegistryService {
_serviceBrand: any;
/**
* All registered storage keys
*/
readonly storageKeys: ReadonlyArray<IStorageKey>;
/**
* Event that is triggered when storage keys are changed
*/
readonly onDidChangeStorageKeys: Event<ReadonlyArray<IStorageKey>>;
/**
* Register a storage key that has to be synchronized during sync.
*/
registerStorageKey(key: IStorageKey): void;
}
export class StorageKeysSyncRegistryService extends Disposable implements IStorageKeysSyncRegistryService {
_serviceBrand: any;
private readonly _storageKeys = new Map<string, IStorageKey>();
get storageKeys(): ReadonlyArray<IStorageKey> { return values(this._storageKeys); }
private readonly _onDidChangeStorageKeys: Emitter<ReadonlyArray<IStorageKey>> = this._register(new Emitter<ReadonlyArray<IStorageKey>>());
readonly onDidChangeStorageKeys = this._onDidChangeStorageKeys.event;
constructor() {
super();
this._register(toDisposable(() => this._storageKeys.clear()));
}
registerStorageKey(storageKey: IStorageKey): void {
if (!this._storageKeys.has(storageKey.key)) {
this._storageKeys.set(storageKey.key, storageKey);
this._onDidChangeStorageKeys.fire(this.storageKeys);
}
}
}

View File

@@ -125,7 +125,7 @@ export interface IUserDataSyncStore {
}
export function getUserDataSyncStore(productService: IProductService, configurationService: IConfigurationService): IUserDataSyncStore | undefined {
const value = productService[CONFIGURATION_SYNC_STORE_KEY] || configurationService.getValue<{ url: string, authenticationProviderId: string }>(CONFIGURATION_SYNC_STORE_KEY);
const value = configurationService.getValue<{ url: string, authenticationProviderId: string }>(CONFIGURATION_SYNC_STORE_KEY) || productService[CONFIGURATION_SYNC_STORE_KEY];
if (value && value.url && value.authenticationProviderId) {
return {
url: joinPath(URI.parse(value.url), 'v1'),
@@ -231,9 +231,13 @@ export interface ISyncExtension {
disabled?: boolean;
}
export interface IStorageValue {
version: number;
value: string;
}
export interface IGlobalState {
argv: IStringDictionary<any>;
storage: IStringDictionary<any>;
storage: IStringDictionary<IStorageValue>;
}
export const enum SyncStatus {

View File

@@ -4,11 +4,13 @@
*--------------------------------------------------------------------------------------------*/
import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc';
import { Event } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
import { IUserDataSyncService, IUserDataSyncUtilService, IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync';
import { URI } from 'vs/base/common/uri';
import { IStringDictionary } from 'vs/base/common/collections';
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
import { IStorageKeysSyncRegistryService, IStorageKey } from 'vs/platform/userDataSync/common/storageKeys';
import { Disposable } from 'vs/base/common/lifecycle';
export class UserDataSyncChannel implements IServerChannel {
@@ -101,3 +103,51 @@ export class UserDataSyncUtilServiceClient implements IUserDataSyncUtilService {
}
}
export class StorageKeysSyncRegistryChannel implements IServerChannel {
constructor(private readonly service: IStorageKeysSyncRegistryService) { }
listen(_: unknown, event: string): Event<any> {
switch (event) {
case 'onDidChangeStorageKeys': return this.service.onDidChangeStorageKeys;
}
throw new Error(`Event not found: ${event}`);
}
call(context: any, command: string, args?: any): Promise<any> {
switch (command) {
case '_getInitialData': return Promise.resolve(this.service.storageKeys);
case 'registerStorageKey': return Promise.resolve(this.service.registerStorageKey(args[0]));
}
throw new Error('Invalid call');
}
}
export class StorageKeysSyncRegistryChannelClient extends Disposable implements IStorageKeysSyncRegistryService {
_serviceBrand: undefined;
private _storageKeys: ReadonlyArray<IStorageKey> = [];
get storageKeys(): ReadonlyArray<IStorageKey> { return this._storageKeys; }
private readonly _onDidChangeStorageKeys: Emitter<ReadonlyArray<IStorageKey>> = this._register(new Emitter<ReadonlyArray<IStorageKey>>());
readonly onDidChangeStorageKeys = this._onDidChangeStorageKeys.event;
constructor(private readonly channel: IChannel) {
super();
this.channel.call<IStorageKey[]>('_getInitialData').then(storageKeys => {
this.updateStorageKeys(storageKeys);
this._register(this.channel.listen<ReadonlyArray<IStorageKey>>('onDidChangeStorageKeys')(storageKeys => this.updateStorageKeys(storageKeys)));
});
}
private async updateStorageKeys(storageKeys: ReadonlyArray<IStorageKey>): Promise<void> {
this._storageKeys = storageKeys;
this._onDidChangeStorageKeys.fire(this.storageKeys);
}
registerStorageKey(storageKey: IStorageKey): void {
this.channel.call('registerStorageKey', [storageKey]);
}
}