From ad601533c5eaa7c958283845ba7a48657944f425 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 24 Sep 2019 19:03:01 +0200 Subject: [PATCH] setting to ignore during sync --- .../sharedProcess/sharedProcessMain.ts | 14 +++--- .../userDataSync/common/extensionsSync.ts | 3 +- .../userDataSync/common/settingsSync.ts | 43 ++++++++-------- .../userDataSync/common/settingsSyncIpc.ts | 12 +++-- .../userDataSync/common/userDataSync.ts | 50 ++++++++++++++++++- .../common/userDataSyncService.ts | 3 +- .../browser/userDataSync.contribution.ts | 25 +--------- .../common/settingsMergeService.ts | 28 ++++++++--- 8 files changed, 111 insertions(+), 67 deletions(-) diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index dac1b8f7377..57950194bf9 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -51,7 +51,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { DiskFileSystemProvider } from 'vs/platform/files/electron-browser/diskFileSystemProvider'; import { Schemas } from 'vs/base/common/network'; import { IProductService } from 'vs/platform/product/common/productService'; -import { IUserDataSyncService, IUserDataSyncStoreService, ISettingsMergeService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, IUserDataSyncStoreService, ISettingsMergeService, registerConfiguration } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncService, UserDataAutoSync } from 'vs/platform/userDataSync/common/userDataSyncService'; import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; import { UserDataSyncChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; @@ -122,11 +122,6 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat const windowsService = new WindowsService(mainProcessService); services.set(IWindowsService, windowsService); - const activeWindowManager = new ActiveWindowManager(windowsService); - const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id)); - const settingsMergeChannel = server.getChannel('settingsMerge', activeWindowRouter); - services.set(ISettingsMergeService, new SettingsMergeChannelClient(settingsMergeChannel)); - // Files const fileService = new FileService(logService); services.set(IFileService, fileService); @@ -173,8 +168,15 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); services.set(ILocalizationsService, new SyncDescriptor(LocalizationsService)); services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService)); + + // User Data Sync Contributions + const activeWindowManager = new ActiveWindowManager(windowsService); + const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id)); + const settingsMergeChannel = server.getChannel('settingsMerge', activeWindowRouter); + services.set(ISettingsMergeService, new SettingsMergeChannelClient(settingsMergeChannel)); services.set(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService)); services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService)); + registerConfiguration(); const instantiationService2 = instantiationService.createChild(services); diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index 84a3192096f..4d507e6dc5e 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -70,8 +70,7 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser } async sync(): Promise { - const syncExtensions = this.configurationService.getValue('userConfiguration.syncExtensions'); - if (syncExtensions === false) { + if (!this.configurationService.getValue('userConfiguration.syncExtensions')) { return false; } diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index d16ac10bad3..17ef43624d1 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -15,6 +15,8 @@ import { CancelablePromise, createCancelablePromise, ThrottledDelayer } from 'vs import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { URI } from 'vs/base/common/uri'; import { joinPath } from 'vs/base/common/resources'; +import { IStringDictionary } from 'vs/base/common/collections'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; interface ISyncPreviewResult { readonly fileContent: IFileContent | null; @@ -47,6 +49,7 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser { @IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService, @ISettingsMergeService private readonly settingsMergeService: ISettingsMergeService, @ILogService private readonly logService: ILogService, + @IConfigurationService private readonly configurationService: IConfigurationService, ) { super(); this.lastSyncSettingsResource = joinPath(this.environmentService.userRoamingDataHome, '.lastSyncSettings.json'); @@ -135,7 +138,8 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser { let { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged } = await this.syncPreviewResultPromise; if (hasRemoteChanged) { - const ref = await this.writeToRemote(content, remoteUserData.ref); + const remoteContent = remoteUserData.content ? await this.settingsMergeService.computeRemoteContent(content, remoteUserData.content, this.getIgnoredSettings()) : content; + const ref = await this.writeToRemote(remoteContent, remoteUserData.ref); remoteUserData = { ref, content }; } if (hasLocalChanged) { @@ -176,45 +180,40 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser { let hasRemoteChanged: boolean = false; let hasConflicts: boolean = false; - // First time sync to remote - if (fileContent && !remoteContent) { - this.logService.trace('Settings Sync: Remote contents does not exist. So sync with settings file.'); - hasRemoteChanged = true; - await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(fileContent.value.toString())); - return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts }; - } - - // Settings file does not exist, so sync with remote contents. - if (remoteContent && !fileContent) { - this.logService.trace('Settings Sync: Settings file does not exist. So sync with remote contents'); - hasLocalChanged = true; - await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(remoteContent)); - return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts }; - } - - if (fileContent && remoteContent) { - const localContent: string = fileContent.value.toString(); + if (remoteContent) { + const localContent: string = fileContent ? fileContent.value.toString() : '{}'; if (!lastSyncData // First time sync || lastSyncData.content !== localContent // Local has moved forwarded || lastSyncData.content !== remoteContent // Remote has moved forwarded ) { this.logService.trace('Settings Sync: Merging remote contents with settings file.'); - const result = await this.settingsMergeService.merge(localContent, remoteContent, lastSyncData ? lastSyncData.content : null); + const result = await this.settingsMergeService.merge(localContent, remoteContent, lastSyncData ? lastSyncData.content : null, this.getIgnoredSettings()); // Sync only if there are changes if (result.hasChanges) { hasLocalChanged = result.mergeContent !== localContent; hasRemoteChanged = result.mergeContent !== remoteContent; hasConflicts = result.hasConflicts; await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(result.mergeContent)); - return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts }; } } + return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts }; + } + + // First time sync to remote + if (fileContent) { + this.logService.trace('Settings Sync: Remote contents does not exist. So sync with settings file.'); + hasRemoteChanged = true; + await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(fileContent.value.toString())); + return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts }; } - this.logService.trace('Settings Sync: No changes.'); return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts }; } + private getIgnoredSettings(): IStringDictionary { + return this.configurationService.getValue>('userConfiguration.ignoreSettings'); + } + private async getLastSyncUserData(): Promise { try { const content = await this.fileService.readFile(this.lastSyncSettingsResource); diff --git a/src/vs/platform/userDataSync/common/settingsSyncIpc.ts b/src/vs/platform/userDataSync/common/settingsSyncIpc.ts index dfee58a6191..6f14928a58d 100644 --- a/src/vs/platform/userDataSync/common/settingsSyncIpc.ts +++ b/src/vs/platform/userDataSync/common/settingsSyncIpc.ts @@ -6,6 +6,7 @@ import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event } from 'vs/base/common/event'; import { ISettingsMergeService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IStringDictionary } from 'vs/base/common/collections'; export class SettingsMergeChannel implements IServerChannel { @@ -17,7 +18,8 @@ export class SettingsMergeChannel implements IServerChannel { call(context: any, command: string, args?: any): Promise { switch (command) { - case 'merge': return this.service.merge(args[0], args[1], args[2]); + case 'merge': return this.service.merge(args[0], args[1], args[2], args[3]); + case 'computeRemoteContent': return this.service.computeRemoteContent(args[0], args[1], args[2]); } throw new Error('Invalid call'); } @@ -30,8 +32,12 @@ export class SettingsMergeChannelClient implements ISettingsMergeService { constructor(private readonly channel: IChannel) { } - merge(localContent: string, remoteContent: string, baseContent: string | null): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }> { - return this.channel.call('merge', [localContent, remoteContent, baseContent]); + merge(localContent: string, remoteContent: string, baseContent: string | null, ignoredSettings: IStringDictionary): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }> { + return this.channel.call('merge', [localContent, remoteContent, baseContent, ignoredSettings]); + } + + computeRemoteContent(localContent: string, remoteContent: string, ignoredSettings: IStringDictionary): Promise { + return this.channel.call('computeRemoteContent', [localContent, remoteContent, ignoredSettings]); } } diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index b741bb48f21..551664b00e6 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -7,6 +7,52 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { Event } from 'vs/base/common/event'; import { IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IStringDictionary } from 'vs/base/common/collections'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { localize } from 'vs/nls'; + +export function registerConfiguration() { + Registry.as(ConfigurationExtensions.Configuration) + .registerConfiguration({ + id: 'userConfiguration', + order: 30, + title: localize('userConfiguration', "User Configuration"), + type: 'object', + properties: { + 'userConfiguration.enableSync': { + type: 'boolean', + description: localize('userConfiguration.enableSync', "When enabled, synchronises User Configuration: Settings, Keybindings, Extensions & Snippets."), + default: true, + scope: ConfigurationScope.APPLICATION + }, + 'userConfiguration.syncExtensions': { + type: 'boolean', + description: localize('userConfiguration.syncExtensions', "When enabled extensions are synchronised while synchronising user configuration."), + default: true, + scope: ConfigurationScope.APPLICATION, + }, + 'userConfiguration.ignoreSettings': { + 'type': 'object', + description: localize('userConfiguration.ignoreSettings', "Configure settings to be ignored while syncing"), + 'default': { + 'userConfiguration.enableSync': true, + 'userConfiguration.syncExtensions': true, + 'userConfiguration.ignoreSettings': true + }, + 'scope': ConfigurationScope.APPLICATION, + 'additionalProperties': { + 'anyOf': [ + { + 'type': 'boolean', + 'description': localize('ignoredSetting', "Id of the stting to be ignored. Set to true or false to enable or disable."), + } + ] + } + } + } + }); +} export interface IUserData { ref: string; @@ -85,7 +131,9 @@ export interface ISettingsMergeService { _serviceBrand: undefined; - merge(localContent: string, remoteContent: string, baseContent: string | null): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }>; + merge(localContent: string, remoteContent: string, baseContent: string | null, ignoredSettings: IStringDictionary): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }>; + + computeRemoteContent(localContent: string, remoteContent: string, ignoredSettings: IStringDictionary): Promise; } diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index bad0a1c1589..4db123c4a02 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -134,8 +134,7 @@ export class UserDataAutoSync extends Disposable { } private isSyncEnabled(): boolean { - const { user: userLocal } = this.configurationService.inspect('userConfiguration.enableSync'); - return userLocal === undefined || userLocal; + return this.configurationService.getValue('userConfiguration.enableSync'); } } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts index d56566b8849..ba89fb5267a 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts @@ -4,13 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IUserDataSyncService, SyncStatus, SyncSource, CONTEXT_SYNC_STATE } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, SyncStatus, SyncSource, CONTEXT_SYNC_STATE, registerConfiguration } from 'vs/platform/userDataSync/common/userDataSync'; import { localize } from 'vs/nls'; import { Disposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { Registry } from 'vs/platform/registry/common/platform'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { MenuRegistry, MenuId, IMenuItem } from 'vs/platform/actions/common/actions'; import { IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -37,27 +36,7 @@ class UserDataSyncConfigurationContribution implements IWorkbenchContribution { @IProductService productService: IProductService ) { if (productService.settingsSyncStoreUrl) { - Registry.as(ConfigurationExtensions.Configuration) - .registerConfiguration({ - id: 'userConfiguration', - order: 30, - title: localize('userConfiguration', "User Configuration"), - type: 'object', - properties: { - 'userConfiguration.enableSync': { - type: 'boolean', - description: localize('userConfiguration.enableSync', "When enabled, synchronises User Configuration: Settings, Keybindings, Extensions & Snippets."), - default: true, - scope: ConfigurationScope.APPLICATION - }, - 'userConfiguration.syncExtensions': { - type: 'boolean', - description: localize('userConfiguration.syncExtensions', "When enabled extensions are synchronised while synchronising user configuration."), - default: true, - scope: ConfigurationScope.APPLICATION - } - } - }); + registerConfiguration(); } } } diff --git a/src/vs/workbench/services/userDataSync/common/settingsMergeService.ts b/src/vs/workbench/services/userDataSync/common/settingsMergeService.ts index 59e0da25bab..bed3e19da8a 100644 --- a/src/vs/workbench/services/userDataSync/common/settingsMergeService.ts +++ b/src/vs/workbench/services/userDataSync/common/settingsMergeService.ts @@ -24,23 +24,23 @@ class SettingsMergeService implements ISettingsMergeService { constructor( @IModelService private readonly modelService: IModelService, - @IModeService private readonly modeService: IModeService + @IModeService private readonly modeService: IModeService, ) { } - async merge(localContent: string, remoteContent: string, baseContent: string | null): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }> { + async merge(localContent: string, remoteContent: string, baseContent: string | null, ignoredSettings: IStringDictionary): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }> { const local = parse(localContent); const remote = parse(remoteContent); const base = baseContent ? parse(baseContent) : null; - const localToRemote = this.compare(local, remote); + const localToRemote = this.compare(local, remote, ignoredSettings); if (localToRemote.added.size === 0 && localToRemote.removed.size === 0 && localToRemote.updated.size === 0) { // No changes found between local and remote. return { mergeContent: localContent, hasChanges: false, hasConflicts: false }; } const conflicts: Set = new Set(); - const baseToLocal = base ? this.compare(base, local) : { added: Object.keys(local).reduce((r, k) => { r.add(k); return r; }, new Set()), removed: new Set(), updated: new Set() }; - const baseToRemote = base ? this.compare(base, remote) : { added: Object.keys(remote).reduce((r, k) => { r.add(k); return r; }, new Set()), removed: new Set(), updated: new Set() }; + const baseToLocal = base ? this.compare(base, local, ignoredSettings) : { added: Object.keys(local).reduce((r, k) => { r.add(k); return r; }, new Set()), removed: new Set(), updated: new Set() }; + const baseToRemote = base ? this.compare(base, remote, ignoredSettings) : { added: Object.keys(remote).reduce((r, k) => { r.add(k); return r; }, new Set()), removed: new Set(), updated: new Set() }; const settingsPreviewModel = this.modelService.createModel(localContent, this.modeService.create('jsonc')); // Removed settings in Local @@ -151,6 +151,18 @@ class SettingsMergeService implements ISettingsMergeService { return { mergeContent: settingsPreviewModel.getValue(), hasChanges: true, hasConflicts: conflicts.size > 0 }; } + async computeRemoteContent(localContent: string, remoteContent: string, ignoredSettings: IStringDictionary): Promise { + const remote = parse(remoteContent); + const remoteModel = this.modelService.createModel(localContent, this.modeService.create('jsonc')); + for (const key of Object.keys(ignoredSettings)) { + if (ignoredSettings[key]) { + this.editSetting(remoteModel, key, undefined); + this.editSetting(remoteModel, key, remote[key]); + } + } + return remoteModel.getValue(); + } + private editSetting(model: ITextModel, key: string, value: any | undefined): void { const insertSpaces = false; const tabSize = 4; @@ -168,9 +180,9 @@ class SettingsMergeService implements ISettingsMergeService { } } - private compare(from: IStringDictionary, to: IStringDictionary): { added: Set, removed: Set, updated: Set } { - const fromKeys = Object.keys(from); - const toKeys = Object.keys(to); + private compare(from: IStringDictionary, to: IStringDictionary, ignored: IStringDictionary): { added: Set, removed: Set, updated: Set } { + const fromKeys = Object.keys(from).filter(key => !ignored[key]); + const toKeys = Object.keys(to).filter(key => !ignored[key]); const added = toKeys.filter(key => fromKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set()); const removed = fromKeys.filter(key => toKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set()); const updated: Set = new Set(); -- GitLab