diff --git a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts index ded00afd7ce34647e73d4a63ee3dc9c37ce36cd6..53f53612572df116f5ea47d75810aea706fd4ff9 100644 --- a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts +++ b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts @@ -4,30 +4,87 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from 'vs/base/common/lifecycle'; -import { IFileService } from 'vs/platform/files/common/files'; +import { IFileService, IFileContent } from 'vs/platform/files/common/files'; import { VSBuffer } from 'vs/base/common/buffer'; import { URI } from 'vs/base/common/uri'; -import { SyncSource } from 'vs/platform/userDataSync/common/userDataSync'; +import { SyncSource, SyncStatus, IUserData, IUserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSync'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { joinPath } from 'vs/base/common/resources'; +import { joinPath, dirname } from 'vs/base/common/resources'; import { toLocalISOString } from 'vs/base/common/date'; import { ThrottledDelayer } from 'vs/base/common/async'; +import { Emitter, Event } from 'vs/base/common/event'; export abstract class AbstractSynchroniser extends Disposable { protected readonly syncFolder: URI; private cleanUpDelayer: ThrottledDelayer; + private _status: SyncStatus = SyncStatus.Idle; + get status(): SyncStatus { return this._status; } + private _onDidChangStatus: Emitter = this._register(new Emitter()); + readonly onDidChangeStatus: Event = this._onDidChangStatus.event; + + protected readonly _onDidChangeLocal: Emitter = this._register(new Emitter()); + readonly onDidChangeLocal: Event = this._onDidChangeLocal.event; + + protected readonly lastSyncResource: URI; + constructor( readonly source: SyncSource, @IFileService protected readonly fileService: IFileService, - @IEnvironmentService environmentService: IEnvironmentService + @IEnvironmentService environmentService: IEnvironmentService, + @IUserDataSyncStoreService protected readonly userDataSyncStoreService: IUserDataSyncStoreService, ) { super(); this.syncFolder = joinPath(environmentService.userDataSyncHome, source); + this.lastSyncResource = joinPath(this.syncFolder, `.lastSync${source}.json`); this.cleanUpDelayer = new ThrottledDelayer(50); } + protected setStatus(status: SyncStatus): void { + if (this._status !== status) { + this._status = status; + this._onDidChangStatus.fire(status); + } + } + + async hasPreviouslySynced(): Promise { + const lastSyncData = await this.getLastSyncUserData(); + return !!lastSyncData; + } + + async hasRemoteData(): Promise { + const remoteUserData = await this.getRemoteUserData(); + return remoteUserData.content !== null; + } + + async resetLocal(): Promise { + try { + await this.fileService.del(this.lastSyncResource); + } catch (e) { /* ignore */ } + } + + protected async getLastSyncUserData(): Promise { + try { + const content = await this.fileService.readFile(this.lastSyncResource); + return JSON.parse(content.value.toString()); + } catch (error) { + return null; + } + } + + protected async updateLastSyncUserData(lastSyncUserData: T): Promise { + await this.fileService.writeFile(this.lastSyncResource, VSBuffer.fromString(JSON.stringify(lastSyncUserData))); + } + + protected getRemoteUserData(lastSyncData?: IUserData | null): Promise { + return this.userDataSyncStoreService.read(this.getRemoteDataResourceKey(), lastSyncData || null, this.source); + } + + protected async updateRemoteUserData(content: string, ref: string | null): Promise { + return this.userDataSyncStoreService.write(this.getRemoteDataResourceKey(), content, ref, this.source); + } + protected async backupLocal(content: VSBuffer): Promise { const resource = joinPath(this.syncFolder, toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')); await this.fileService.writeFile(resource, content); @@ -43,4 +100,40 @@ export abstract class AbstractSynchroniser extends Disposable { } } + protected abstract getRemoteDataResourceKey(): string; +} + +export abstract class AbstractFileSynchroniser extends AbstractSynchroniser { + + constructor( + protected readonly file: URI, + readonly source: SyncSource, + @IFileService fileService: IFileService, + @IEnvironmentService environmentService: IEnvironmentService, + @IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService, + ) { + super(source, fileService, environmentService, userDataSyncStoreService); + this._register(this.fileService.watch(dirname(file))); + this._register(Event.filter(this.fileService.onFileChanges, e => e.contains(file))(() => this._onDidChangeLocal.fire())); + } + + protected async getLocalFileContent(): Promise { + try { + return await this.fileService.readFile(this.file); + } catch (error) { + return null; + } + } + + protected async updateLocalFileContent(newContent: string, oldContent: IFileContent | null): Promise { + if (oldContent) { + // file exists already + await this.backupLocal(oldContent.value); + await this.fileService.writeFile(this.file, VSBuffer.fromString(newContent), oldContent); + } else { + // file does not exist + await this.fileService.createFile(this.file, VSBuffer.fromString(newContent), { overwrite: false }); + } + } + } diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index 82eb46f185a463be18e132c5c03b97e4b6303873..95d03f6bc0b80f32c0cb5755ff0589d0e622b8a1 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -4,16 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { IUserData, UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, ISyncExtension, IUserDataSyncLogService, IUserDataSynchroniser, SyncSource } from 'vs/platform/userDataSync/common/userDataSync'; -import { VSBuffer } from 'vs/base/common/buffer'; -import { Emitter, Event } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { URI } from 'vs/base/common/uri'; -import { joinPath } from 'vs/base/common/resources'; import { IExtensionManagementService, IExtensionGalleryService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionType, IExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IFileService } from 'vs/platform/files/common/files'; -import { Queue } from 'vs/base/common/async'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { localize } from 'vs/nls'; import { merge } from 'vs/platform/userDataSync/common/extensionsMerge'; @@ -35,32 +31,17 @@ interface ILastSyncUserData extends IUserData { export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUserDataSynchroniser { - private static EXTERNAL_USER_DATA_EXTENSIONS_KEY: string = 'extensions'; - - private _status: SyncStatus = SyncStatus.Idle; - get status(): SyncStatus { return this._status; } - private _onDidChangStatus: Emitter = this._register(new Emitter()); - readonly onDidChangeStatus: Event = this._onDidChangStatus.event; - - private _onDidChangeLocal: Emitter = this._register(new Emitter()); - readonly onDidChangeLocal: Event = this._onDidChangeLocal.event; - - private readonly lastSyncExtensionsResource: URI; - private readonly replaceQueue: Queue; - constructor( @IEnvironmentService environmentService: IEnvironmentService, @IFileService fileService: IFileService, - @IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService, + @IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @IGlobalExtensionEnablementService private readonly extensionEnablementService: IGlobalExtensionEnablementService, @IUserDataSyncLogService private readonly logService: IUserDataSyncLogService, @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, @IConfigurationService private readonly configurationService: IConfigurationService, ) { - super(SyncSource.Extensions, fileService, environmentService); - this.replaceQueue = this._register(new Queue()); - this.lastSyncExtensionsResource = joinPath(this.syncFolder, '.lastSyncExtensions'); + super(SyncSource.Extensions, fileService, environmentService, userDataSyncStoreService); this._register( Event.debounce( Event.any( @@ -69,12 +50,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse () => undefined, 500)(() => this._onDidChangeLocal.fire())); } - private setStatus(status: SyncStatus): void { - if (this._status !== status) { - this._status = status; - this._onDidChangStatus.fire(status); - } - } + protected getRemoteDataResourceKey(): string { return 'extensions'; } async pull(): Promise { if (!this.configurationService.getValue('sync.enableExtensions')) { @@ -176,16 +152,6 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse throw new Error('Extensions: Conflicts should not occur'); } - async hasPreviouslySynced(): Promise { - const lastSyncData = await this.getLastSyncUserData(); - return !!lastSyncData; - } - - async hasRemoteData(): Promise { - const remoteUserData = await this.getRemoteUserData(); - return remoteUserData.content !== null; - } - async hasLocalData(): Promise { try { const localExtensions = await this.getLocalExtensions(); @@ -202,30 +168,8 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse return null; } - removeExtension(identifier: IExtensionIdentifier): Promise { - return this.replaceQueue.queue(async () => { - const remoteData = await this.userDataSyncStoreService.read(ExtensionsSynchroniser.EXTERNAL_USER_DATA_EXTENSIONS_KEY, null); - const remoteExtensions: ISyncExtension[] = remoteData.content ? JSON.parse(remoteData.content) : []; - const ignoredExtensions = this.configurationService.getValue('sync.ignoredExtensions') || []; - const removedExtensions = remoteExtensions.filter(e => !ignoredExtensions.some(id => areSameExtensions({ id }, e.identifier)) && areSameExtensions(e.identifier, identifier)); - if (removedExtensions.length) { - for (const removedExtension of removedExtensions) { - remoteExtensions.splice(remoteExtensions.indexOf(removedExtension), 1); - } - this.logService.info(`Extensions: Removing extension '${identifier.id}' from remote.`); - await this.writeToRemote(remoteExtensions, remoteData.ref); - } - }); - } - - async resetLocal(): Promise { - try { - await this.fileService.del(this.lastSyncExtensionsResource); - } catch (e) { /* ignore */ } - } - private async getPreview(): Promise { - const lastSyncData = await this.getLastSyncUserData(); + const lastSyncData = await this.getLastSyncUserData(); const lastSyncExtensions: ISyncExtension[] | null = lastSyncData ? JSON.parse(lastSyncData.content!) : null; const skippedExtensions: ISyncExtension[] = lastSyncData ? lastSyncData.skippedExtensions || [] : []; @@ -262,13 +206,15 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse if (remote) { // update remote this.logService.info('Extensions: Updating remote extensions...'); - remoteUserData = await this.writeToRemote(remote, forcePush ? null : remoteUserData.ref); + const content = JSON.stringify(remote); + const ref = await this.updateRemoteUserData(content, forcePush ? null : remoteUserData.ref); + remoteUserData = { ref, content }; } if (remoteUserData.content) { // update last sync this.logService.info('Extensions: Updating last synchronised extensions...'); - await this.updateLastSyncValue({ ...remoteUserData, skippedExtensions }); + await this.updateLastSyncUserData({ ...remoteUserData, skippedExtensions }); } } @@ -331,27 +277,4 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse .map(({ identifier }) => ({ identifier, enabled: !disabledExtensions.some(disabledExtension => areSameExtensions(disabledExtension, identifier)) })); } - private async getLastSyncUserData(): Promise { - try { - const content = await this.fileService.readFile(this.lastSyncExtensionsResource); - return JSON.parse(content.value.toString()); - } catch (error) { - return null; - } - } - - private async updateLastSyncValue(lastSyncUserData: ILastSyncUserData): Promise { - await this.fileService.writeFile(this.lastSyncExtensionsResource, VSBuffer.fromString(JSON.stringify(lastSyncUserData))); - } - - private getRemoteUserData(lastSyncData?: IUserData | null): Promise { - return this.userDataSyncStoreService.read(ExtensionsSynchroniser.EXTERNAL_USER_DATA_EXTENSIONS_KEY, lastSyncData || null, this.source); - } - - private async writeToRemote(extensions: ISyncExtension[], ref: string | null): Promise { - const content = JSON.stringify(extensions); - ref = await this.userDataSyncStoreService.write(ExtensionsSynchroniser.EXTERNAL_USER_DATA_EXTENSIONS_KEY, content, ref, this.source); - return { content, ref }; - } - } diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts index 58b60ff6f6ee2aa7a06e37372532ba8fb5a3ab13..dd865d65b8af4a1f49d2aaa080de115f37cbbf9e 100644 --- a/src/vs/platform/userDataSync/common/globalStateSync.ts +++ b/src/vs/platform/userDataSync/common/globalStateSync.ts @@ -5,10 +5,9 @@ import { IUserData, UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IGlobalState, SyncSource, IUserDataSynchroniser } from 'vs/platform/userDataSync/common/userDataSync'; import { VSBuffer } from 'vs/base/common/buffer'; -import { Emitter, Event } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { URI } from 'vs/base/common/uri'; -import { joinPath, dirname } from 'vs/base/common/resources'; +import { dirname } from 'vs/base/common/resources'; import { IFileService } from 'vs/platform/files/common/files'; import { IStringDictionary } from 'vs/base/common/collections'; import { edit } from 'vs/platform/userDataSync/common/content'; @@ -27,37 +26,19 @@ interface ISyncPreviewResult { export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUserDataSynchroniser { - private static EXTERNAL_USER_DATA_GLOBAL_STATE_KEY: string = 'globalState'; - - private _status: SyncStatus = SyncStatus.Idle; - get status(): SyncStatus { return this._status; } - private _onDidChangStatus: Emitter = this._register(new Emitter()); - readonly onDidChangeStatus: Event = this._onDidChangStatus.event; - - private _onDidChangeLocal: Emitter = this._register(new Emitter()); - readonly onDidChangeLocal: Event = this._onDidChangeLocal.event; - - private readonly lastSyncGlobalStateResource: URI; - constructor( @IFileService fileService: IFileService, - @IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService, + @IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService, @IUserDataSyncLogService private readonly logService: IUserDataSyncLogService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @IConfigurationService private readonly configurationService: IConfigurationService, ) { - super(SyncSource.UIState, fileService, environmentService); - this.lastSyncGlobalStateResource = joinPath(this.syncFolder, '.lastSyncGlobalState'); + super(SyncSource.GlobalState, fileService, environmentService, userDataSyncStoreService); this._register(this.fileService.watch(dirname(this.environmentService.argvResource))); this._register(Event.filter(this.fileService.onFileChanges, e => e.contains(this.environmentService.argvResource))(() => this._onDidChangeLocal.fire())); } - private setStatus(status: SyncStatus): void { - if (this._status !== status) { - this._status = status; - this._onDidChangStatus.fire(status); - } - } + protected getRemoteDataResourceKey(): string { return 'globalState'; } async pull(): Promise { if (!this.configurationService.getValue('sync.enableUIState')) { @@ -153,16 +134,6 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs throw new Error('UI State: Conflicts should not occur'); } - async hasPreviouslySynced(): Promise { - const lastSyncData = await this.getLastSyncUserData(); - return !!lastSyncData; - } - - async hasRemoteData(): Promise { - const remoteUserData = await this.getRemoteUserData(); - return remoteUserData.content !== null; - } - async hasLocalData(): Promise { try { const localGloablState = await this.getLocalGlobalState(); @@ -179,12 +150,6 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs return null; } - async resetLocal(): Promise { - try { - await this.fileService.del(this.lastSyncGlobalStateResource); - } catch (e) { /* ignore */ } - } - private async getPreview(): Promise { const lastSyncData = await this.getLastSyncUserData(); const lastSyncGlobalState = lastSyncData && lastSyncData.content ? JSON.parse(lastSyncData.content) : null; @@ -209,13 +174,15 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs if (remote) { // update remote this.logService.info('UI State: Updating remote ui state...'); - remoteUserData = await this.writeToRemote(remote, forcePush ? null : remoteUserData.ref); + const content = JSON.stringify(remote); + const ref = await this.updateRemoteUserData(content, forcePush ? null : remoteUserData.ref); + remoteUserData = { ref, content }; } if (remoteUserData.content) { // update last sync this.logService.info('UI State: Updating last synchronised ui state...'); - await this.updateLastSyncValue(remoteUserData); + await this.updateLastSyncUserData(remoteUserData); } } @@ -245,27 +212,4 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs } } - private async getLastSyncUserData(): Promise { - try { - const content = await this.fileService.readFile(this.lastSyncGlobalStateResource); - return JSON.parse(content.value.toString()); - } catch (error) { - return null; - } - } - - private async updateLastSyncValue(remoteUserData: IUserData): Promise { - await this.fileService.writeFile(this.lastSyncGlobalStateResource, VSBuffer.fromString(JSON.stringify(remoteUserData))); - } - - private getRemoteUserData(lastSyncData?: IUserData | null): Promise { - return this.userDataSyncStoreService.read(GlobalStateSynchroniser.EXTERNAL_USER_DATA_GLOBAL_STATE_KEY, lastSyncData || null, this.source); - } - - private async writeToRemote(globalState: IGlobalState, ref: string | null): Promise { - const content = JSON.stringify(globalState); - ref = await this.userDataSyncStoreService.write(GlobalStateSynchroniser.EXTERNAL_USER_DATA_GLOBAL_STATE_KEY, content, ref, this.source); - return { content, ref }; - } - } diff --git a/src/vs/platform/userDataSync/common/keybindingsSync.ts b/src/vs/platform/userDataSync/common/keybindingsSync.ts index 4d20c895d03310a7b1128b3dfe9d1ee763cc45a9..dc75dba900802583564c711877c77e19eff52dc8 100644 --- a/src/vs/platform/userDataSync/common/keybindingsSync.ts +++ b/src/vs/platform/userDataSync/common/keybindingsSync.ts @@ -9,18 +9,15 @@ import { merge } from 'vs/platform/userDataSync/common/keybindingsMerge'; import { VSBuffer } from 'vs/base/common/buffer'; import { parse, ParseError } from 'vs/base/common/json'; import { localize } from 'vs/nls'; -import { Emitter, Event } from 'vs/base/common/event'; import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { URI } from 'vs/base/common/uri'; -import { joinPath, dirname } from 'vs/base/common/resources'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { CancellationToken } from 'vs/base/common/cancellation'; import { OS, OperatingSystem } from 'vs/base/common/platform'; import { isUndefined } from 'vs/base/common/types'; import { FormattingOptions } from 'vs/base/common/jsonFormatter'; import { isNonEmptyArray } from 'vs/base/common/arrays'; -import { AbstractSynchroniser } from 'vs/platform/userDataSync/common/abstractSynchronizer'; +import { AbstractFileSynchroniser } from 'vs/platform/userDataSync/common/abstractSynchronizer'; interface ISyncContent { mac?: string; @@ -37,42 +34,22 @@ interface ISyncPreviewResult { readonly hasConflicts: boolean; } -export class KeybindingsSynchroniser extends AbstractSynchroniser implements IUserDataSynchroniser { - - private static EXTERNAL_USER_DATA_KEYBINDINGS_KEY: string = 'keybindings'; +export class KeybindingsSynchroniser extends AbstractFileSynchroniser implements IUserDataSynchroniser { private syncPreviewResultPromise: CancelablePromise | null = null; - private _status: SyncStatus = SyncStatus.Idle; - get status(): SyncStatus { return this._status; } - private _onDidChangStatus: Emitter = this._register(new Emitter()); - readonly onDidChangeStatus: Event = this._onDidChangStatus.event; - - private _onDidChangeLocal: Emitter = this._register(new Emitter()); - readonly onDidChangeLocal: Event = this._onDidChangeLocal.event; - - private readonly lastSyncKeybindingsResource: URI; - constructor( - @IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService, + @IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService, @IUserDataSyncLogService private readonly logService: IUserDataSyncLogService, @IConfigurationService private readonly configurationService: IConfigurationService, @IFileService fileService: IFileService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @IUserDataSyncUtilService private readonly userDataSyncUtilService: IUserDataSyncUtilService, ) { - super(SyncSource.Keybindings, fileService, environmentService); - this.lastSyncKeybindingsResource = joinPath(this.syncFolder, '.lastSyncKeybindings.json'); - this._register(this.fileService.watch(dirname(this.environmentService.keybindingsResource))); - this._register(Event.filter(this.fileService.onFileChanges, e => e.contains(this.environmentService.keybindingsResource))(() => this._onDidChangeLocal.fire())); + super(environmentService.keybindingsResource, SyncSource.Keybindings, fileService, environmentService, userDataSyncStoreService); } - private setStatus(status: SyncStatus): void { - if (this._status !== status) { - this._status = status; - this._onDidChangStatus.fire(status); - } - } + protected getRemoteDataResourceKey(): string { return 'keybindings'; } async pull(): Promise { if (!this.configurationService.getValue('sync.enableKeybindings')) { @@ -204,16 +181,6 @@ export class KeybindingsSynchroniser extends AbstractSynchroniser implements IUs } } - async hasPreviouslySynced(): Promise { - const lastSyncData = await this.getLastSyncUserData(); - return !!lastSyncData; - } - - async hasRemoteData(): Promise { - const remoteUserData = await this.getRemoteUserData(); - return remoteUserData.content !== null; - } - async hasLocalData(): Promise { try { const localFileContent = await this.getLocalFileContent(); @@ -243,12 +210,6 @@ export class KeybindingsSynchroniser extends AbstractSynchroniser implements IUs return content ? this.getKeybindingsContentFromSyncContent(content) : null; } - async resetLocal(): Promise { - try { - await this.fileService.del(this.lastSyncKeybindingsResource); - } catch (e) { /* ignore */ } - } - private async doSync(): Promise { try { const result = await this.getPreview(); @@ -306,7 +267,7 @@ export class KeybindingsSynchroniser extends AbstractSynchroniser implements IUs } if (hasLocalChanged) { this.logService.info('Keybindings: Updating local keybindings'); - await this.updateLocalContent(content, fileContent); + await this.updateLocalFileContent(content, fileContent); } if (hasRemoteChanged) { this.logService.info('Keybindings: Updating remote keybindings'); @@ -400,46 +361,6 @@ export class KeybindingsSynchroniser extends AbstractSynchroniser implements IUs return this._formattingOptions; } - private async getLocalFileContent(): Promise { - try { - return await this.fileService.readFile(this.environmentService.keybindingsResource); - } catch (error) { - return null; - } - } - - private async updateLocalContent(newContent: string, oldContent: IFileContent | null): Promise { - if (oldContent) { - // file exists already - await this.backupLocal(oldContent.value); - await this.fileService.writeFile(this.environmentService.keybindingsResource, VSBuffer.fromString(newContent), oldContent); - } else { - // file does not exist - await this.fileService.createFile(this.environmentService.keybindingsResource, VSBuffer.fromString(newContent), { overwrite: false }); - } - } - - private async getLastSyncUserData(): Promise { - try { - const content = await this.fileService.readFile(this.lastSyncKeybindingsResource); - return JSON.parse(content.value.toString()); - } catch (error) { - return null; - } - } - - private async updateLastSyncUserData(remoteUserData: IUserData): Promise { - await this.fileService.writeFile(this.lastSyncKeybindingsResource, VSBuffer.fromString(JSON.stringify(remoteUserData))); - } - - private async getRemoteUserData(lastSyncData?: IUserData | null): Promise { - return this.userDataSyncStoreService.read(KeybindingsSynchroniser.EXTERNAL_USER_DATA_KEYBINDINGS_KEY, lastSyncData || null, this.source); - } - - private async updateRemoteUserData(content: string, ref: string | null): Promise { - return this.userDataSyncStoreService.write(KeybindingsSynchroniser.EXTERNAL_USER_DATA_KEYBINDINGS_KEY, content, ref, this.source); - } - private getKeybindingsContentFromSyncContent(syncContent: string): string | null { try { const parsed = JSON.parse(syncContent); diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index f8dd7b0ba634a5dde54bb82219a3e110906b903d..74de5ba73972b2deaa25aadc251972236be4cae7 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -11,8 +11,6 @@ import { localize } from 'vs/nls'; import { Emitter, Event } from 'vs/base/common/event'; import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { URI } from 'vs/base/common/uri'; -import { joinPath, dirname } from 'vs/base/common/resources'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { startsWith } from 'vs/base/common/strings'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -22,7 +20,7 @@ import * as arrays from 'vs/base/common/arrays'; import * as objects from 'vs/base/common/objects'; import { isEmptyObject } from 'vs/base/common/types'; import { edit } from 'vs/platform/userDataSync/common/content'; -import { AbstractSynchroniser } from 'vs/platform/userDataSync/common/abstractSynchronizer'; +import { AbstractFileSynchroniser } from 'vs/platform/userDataSync/common/abstractSynchronizer'; interface ISyncPreviewResult { readonly fileContent: IFileContent | null; @@ -34,49 +32,33 @@ interface ISyncPreviewResult { readonly conflictSettings: IConflictSetting[]; } -export class SettingsSynchroniser extends AbstractSynchroniser implements ISettingsSyncService { +export class SettingsSynchroniser extends AbstractFileSynchroniser implements ISettingsSyncService { _serviceBrand: any; - private static EXTERNAL_USER_DATA_SETTINGS_KEY: string = 'settings'; - private syncPreviewResultPromise: CancelablePromise | null = null; - private _status: SyncStatus = SyncStatus.Idle; - get status(): SyncStatus { return this._status; } - private _onDidChangStatus: Emitter = this._register(new Emitter()); - readonly onDidChangeStatus: Event = this._onDidChangStatus.event; - private _conflicts: IConflictSetting[] = []; get conflicts(): IConflictSetting[] { return this._conflicts; } private _onDidChangeConflicts: Emitter = this._register(new Emitter()); readonly onDidChangeConflicts: Event = this._onDidChangeConflicts.event; - private _onDidChangeLocal: Emitter = this._register(new Emitter()); - readonly onDidChangeLocal: Event = this._onDidChangeLocal.event; - - private readonly lastSyncSettingsResource: URI; - constructor( @IFileService fileService: IFileService, @IEnvironmentService private readonly environmentService: IEnvironmentService, - @IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService, + @IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService, @IUserDataSyncLogService private readonly logService: IUserDataSyncLogService, @IUserDataSyncUtilService private readonly userDataSyncUtilService: IUserDataSyncUtilService, @IConfigurationService private readonly configurationService: IConfigurationService, ) { - super(SyncSource.Settings, fileService, environmentService); - this.lastSyncSettingsResource = joinPath(this.syncFolder, '.lastSyncSettings.json'); - this._register(this.fileService.watch(dirname(this.environmentService.settingsResource))); - this._register(Event.filter(this.fileService.onFileChanges, e => e.contains(this.environmentService.settingsResource))(() => this._onDidChangeLocal.fire())); + super(environmentService.settingsResource, SyncSource.Settings, fileService, environmentService, userDataSyncStoreService); } - private setStatus(status: SyncStatus): void { - if (this._status !== status) { - this._status = status; - this._onDidChangStatus.fire(status); - } - if (this._status !== SyncStatus.HasConflicts) { + protected getRemoteDataResourceKey(): string { return 'settings'; } + + protected setStatus(status: SyncStatus): void { + super.setStatus(status); + if (this.status !== SyncStatus.HasConflicts) { this.setConflicts([]); } } @@ -206,16 +188,6 @@ export class SettingsSynchroniser extends AbstractSynchroniser implements ISetti this.setStatus(SyncStatus.Idle); } - async hasPreviouslySynced(): Promise { - const lastSyncData = await this.getLastSyncUserData(); - return !!lastSyncData; - } - - async hasRemoteData(): Promise { - const remoteUserData = await this.getRemoteUserData(); - return remoteUserData.content !== null; - } - async hasLocalData(): Promise { try { const localFileContent = await this.getLocalFileContent(); @@ -279,12 +251,6 @@ export class SettingsSynchroniser extends AbstractSynchroniser implements ISetti } } - async resetLocal(): Promise { - try { - await this.fileService.del(this.lastSyncSettingsResource); - } catch (e) { /* ignore */ } - } - private async doSync(resolvedConflicts: { key: string, value: any | undefined }[]): Promise { try { const result = await this.getPreview(resolvedConflicts); @@ -343,18 +309,18 @@ export class SettingsSynchroniser extends AbstractSynchroniser implements ISetti } if (hasLocalChanged) { this.logService.info('Settings: Updating local settings'); - await this.writeToLocal(content, fileContent); + await this.updateLocalFileContent(content, fileContent); } if (hasRemoteChanged) { const formatUtils = await this.getFormattingOptions(); const remoteContent = updateIgnoredSettings(content, remoteUserData.content || '{}', getIgnoredSettings(this.configurationService, content), formatUtils); this.logService.info('Settings: Updating remote settings'); - const ref = await this.writeToRemote(remoteContent, forcePush ? null : remoteUserData.ref); + const ref = await this.updateRemoteUserData(remoteContent, forcePush ? null : remoteUserData.ref); remoteUserData = { ref, content }; } if (remoteUserData.content) { this.logService.info('Settings: Updating last synchronised sttings'); - await this.updateLastSyncValue(remoteUserData); + await this.updateLastSyncUserData(remoteUserData); } // Delete the preview @@ -436,46 +402,6 @@ export class SettingsSynchroniser extends AbstractSynchroniser implements ISetti return this._formattingOptions; } - private async getLastSyncUserData(): Promise { - try { - const content = await this.fileService.readFile(this.lastSyncSettingsResource); - return JSON.parse(content.value.toString()); - } catch (error) { - return null; - } - } - - private async getLocalFileContent(): Promise { - try { - return await this.fileService.readFile(this.environmentService.settingsResource); - } catch (error) { - return null; - } - } - - private getRemoteUserData(lastSyncData?: IUserData | null): Promise { - return this.userDataSyncStoreService.read(SettingsSynchroniser.EXTERNAL_USER_DATA_SETTINGS_KEY, lastSyncData || null, this.source); - } - - private async writeToRemote(content: string, ref: string | null): Promise { - return this.userDataSyncStoreService.write(SettingsSynchroniser.EXTERNAL_USER_DATA_SETTINGS_KEY, content, ref, this.source); - } - - private async writeToLocal(newContent: string, oldContent: IFileContent | null): Promise { - if (oldContent) { - // file exists already - await this.backupLocal(oldContent.value); - await this.fileService.writeFile(this.environmentService.settingsResource, VSBuffer.fromString(newContent), oldContent); - } else { - // file does not exist - await this.fileService.createFile(this.environmentService.settingsResource, VSBuffer.fromString(newContent), { overwrite: false }); - } - } - - private async updateLastSyncValue(remoteUserData: IUserData): Promise { - await this.fileService.writeFile(this.lastSyncSettingsResource, VSBuffer.fromString(JSON.stringify(remoteUserData))); - } - } export function getIgnoredSettings(configurationService: IConfigurationService, settingsContent?: string): string[] { diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index 6580f517dfb64d34c974f509bb7db5424ad5af52..ccdc00d60f17987e26f7dc2ae50aaeb65e10ef75 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -174,7 +174,7 @@ export const enum SyncSource { Settings = 'Settings', Keybindings = 'Keybindings', Extensions = 'Extensions', - UIState = 'UI State' + GlobalState = 'GlobalState' } export const enum SyncStatus { @@ -212,7 +212,6 @@ export interface IUserDataSyncService extends ISynchroniser { isFirstTimeSyncAndHasUserData(): Promise; reset(): Promise; resetLocal(): Promise; - removeExtension(identifier: IExtensionIdentifier): Promise; getRemoteContent(source: SyncSource): Promise; resolveConflictsAndContinueSync(content: string): Promise; } @@ -267,5 +266,5 @@ export function toRemoteContentResource(source: SyncSource): URI { return URI.from({ scheme: USER_DATA_SYNC_SCHEME, path: `${source}/remoteContent` }); } export function getSyncSourceFromRemoteContentResource(uri: URI): SyncSource | undefined { - return [SyncSource.Settings, SyncSource.Keybindings, SyncSource.Extensions, SyncSource.UIState].filter(source => isEqual(uri, toRemoteContentResource(source)))[0]; + return [SyncSource.Settings, SyncSource.Keybindings, SyncSource.Extensions, SyncSource.GlobalState].filter(source => isEqual(uri, toRemoteContentResource(source)))[0]; } diff --git a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts index caf9856c728672747554e439a2fc1f352d41f6f5..4519914e70c25563a58e77911ed295f0827bb335 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts @@ -30,7 +30,6 @@ export class UserDataSyncChannel implements IServerChannel { case 'push': return this.service.push(); case '_getInitialStatus': return Promise.resolve(this.service.status); case 'getConflictsSource': return Promise.resolve(this.service.conflictsSource); - case 'removeExtension': return this.service.removeExtension(args[0]); case 'stop': this.service.stop(); return Promise.resolve(); case 'restart': return this.service.restart().then(() => this.service.status); case 'reset': return this.service.reset(); diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index 770f57e941f6c9709b93eb6b590962604df83775..141533aa02fe9b9eb2a37074c58e5d4145e03728 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -9,7 +9,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync'; import { Emitter, Event } from 'vs/base/common/event'; import { ExtensionsSynchroniser } from 'vs/platform/userDataSync/common/extensionsSync'; -import { IExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { KeybindingsSynchroniser } from 'vs/platform/userDataSync/common/keybindingsSync'; import { GlobalStateSynchroniser } from 'vs/platform/userDataSync/common/globalStateSync'; import { toErrorMessage } from 'vs/base/common/errorMessage'; @@ -253,10 +252,6 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ this.logService.info('Completed resetting local cache'); } - removeExtension(identifier: IExtensionIdentifier): Promise { - return this.extensionsSynchroniser.removeExtension(identifier); - } - private updateStatus(): void { const status = this.computeStatus(); if (this._status !== status) { @@ -309,7 +304,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ if (synchroniser instanceof ExtensionsSynchroniser) { return SyncSource.Extensions; } - return SyncSource.UIState; + return SyncSource.GlobalState; } private onDidChangeAuthTokenStatus(token: string | undefined): void { diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index b42fb3f25838505c24b0033259e4d53329292d01..2901974d03c52b5b3c7a3e2e98216562a697ff75 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -60,7 +60,7 @@ function getSyncAreaLabel(source: SyncSource): string { case SyncSource.Settings: return localize('settings', "Settings"); case SyncSource.Keybindings: return localize('keybindings', "Keybindings"); case SyncSource.Extensions: return localize('extensions', "Extensions"); - case SyncSource.UIState: return localize('ui state label', "UI State"); + case SyncSource.GlobalState: return localize('ui state label', "UI State"); } } @@ -367,7 +367,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo label: getSyncAreaLabel(SyncSource.Extensions) }, { id: 'sync.enableUIState', - label: getSyncAreaLabel(SyncSource.UIState), + label: getSyncAreaLabel(SyncSource.GlobalState), description: localize('ui state description', "Display Language (Only)") }]; } diff --git a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts index c0fda78d2f928d99bc35504e11c0c4a19704f2f3..43a904996c6582c4585f6b4f171ff590e33106ac 100644 --- a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts +++ b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts @@ -9,7 +9,6 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export class UserDataSyncService extends Disposable implements IUserDataSyncService { @@ -91,10 +90,6 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ return this.channel.call('isFirstTimeSyncAndHasUserData'); } - removeExtension(identifier: IExtensionIdentifier): Promise { - return this.channel.call('removeExtension', [identifier]); - } - private async updateStatus(status: SyncStatus): Promise { this._conflictsSource = await this.channel.call('getConflictsSource'); this._status = status;