diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index 17ef43624d1..3716593b741 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -15,7 +15,6 @@ 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 { @@ -210,8 +209,8 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser { return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts }; } - private getIgnoredSettings(): IStringDictionary { - return this.configurationService.getValue>('userConfiguration.ignoreSettings'); + private getIgnoredSettings(): string[] { + return this.configurationService.getValue('userConfiguration.ignoreSettings'); } private async getLastSyncUserData(): Promise { diff --git a/src/vs/platform/userDataSync/common/settingsSyncIpc.ts b/src/vs/platform/userDataSync/common/settingsSyncIpc.ts index 6f14928a58d..3f231e86454 100644 --- a/src/vs/platform/userDataSync/common/settingsSyncIpc.ts +++ b/src/vs/platform/userDataSync/common/settingsSyncIpc.ts @@ -6,7 +6,6 @@ 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 { @@ -32,11 +31,11 @@ export class SettingsMergeChannelClient implements ISettingsMergeService { constructor(private readonly channel: IChannel) { } - merge(localContent: string, remoteContent: string, baseContent: string | null, ignoredSettings: IStringDictionary): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }> { + merge(localContent: string, remoteContent: string, baseContent: string | null, ignoredSettings: string[]): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }> { return this.channel.call('merge', [localContent, remoteContent, baseContent, ignoredSettings]); } - computeRemoteContent(localContent: string, remoteContent: string, ignoredSettings: IStringDictionary): Promise { + computeRemoteContent(localContent: string, remoteContent: string, ignoredSettings: string[]): 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 551664b00e6..d146ece7798 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -7,51 +7,62 @@ 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 { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope, allSettings } from 'vs/platform/configuration/common/configurationRegistry'; import { localize } from 'vs/nls'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; -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 function registerConfiguration(): IDisposable { + const ignoredSettingsSchemaId = 'vscode://schemas/ignoredSettings'; + const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + configurationRegistry.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': 'array', + description: localize('userConfiguration.ignoreSettings', "Configure settings to be ignored while syncing"), + 'default': [ + 'userConfiguration.enableSync', + 'userConfiguration.syncExtensions', + 'userConfiguration.ignoreSettings' + ], + 'scope': ConfigurationScope.APPLICATION, + $ref: ignoredSettingsSchemaId, + additionalProperties: true, + allowTrailingCommas: true, + allowComments: true, + uniqueItems: true } - }); + } + }); + const registerIgnoredSettingsSchema = () => { + const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); + const ignoredSettingsSchema: IJSONSchema = { + items: { + type: 'string', + enum: Object.keys(allSettings.properties) + } + }; + jsonRegistry.registerSchema(ignoredSettingsSchemaId, ignoredSettingsSchema); + }; + return configurationRegistry.onDidUpdateConfiguration(() => registerIgnoredSettingsSchema()); } export interface IUserData { @@ -131,9 +142,9 @@ export interface ISettingsMergeService { _serviceBrand: undefined; - merge(localContent: string, remoteContent: string, baseContent: string | null, ignoredSettings: IStringDictionary): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }>; + merge(localContent: string, remoteContent: string, baseContent: string | null, ignoredSettings: string[]): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }>; - computeRemoteContent(localContent: string, remoteContent: string, ignoredSettings: IStringDictionary): Promise; + computeRemoteContent(localContent: string, remoteContent: string, ignoredSettings: string[]): Promise; } diff --git a/src/vs/workbench/services/userDataSync/common/settingsMergeService.ts b/src/vs/workbench/services/userDataSync/common/settingsMergeService.ts index bed3e19da8a..973a7fd7a68 100644 --- a/src/vs/workbench/services/userDataSync/common/settingsMergeService.ts +++ b/src/vs/workbench/services/userDataSync/common/settingsMergeService.ts @@ -27,20 +27,21 @@ class SettingsMergeService implements ISettingsMergeService { @IModeService private readonly modeService: IModeService, ) { } - async merge(localContent: string, remoteContent: string, baseContent: string | null, ignoredSettings: IStringDictionary): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }> { + async merge(localContent: string, remoteContent: string, baseContent: string | null, ignoredSettings: string[]): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }> { const local = parse(localContent); const remote = parse(remoteContent); const base = baseContent ? parse(baseContent) : null; + const ignored = ignoredSettings.reduce((set, key) => { set.add(key); return set; }, new Set()); - const localToRemote = this.compare(local, remote, ignoredSettings); + const localToRemote = this.compare(local, remote, ignored); 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, 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 baseToLocal = base ? this.compare(base, local, ignored) : { 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, ignored) : { 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,11 +152,12 @@ class SettingsMergeService implements ISettingsMergeService { return { mergeContent: settingsPreviewModel.getValue(), hasChanges: true, hasConflicts: conflicts.size > 0 }; } - async computeRemoteContent(localContent: string, remoteContent: string, ignoredSettings: IStringDictionary): Promise { + async computeRemoteContent(localContent: string, remoteContent: string, ignoredSettings: string[]): Promise { const remote = parse(remoteContent); const remoteModel = this.modelService.createModel(localContent, this.modeService.create('jsonc')); + const ignored = ignoredSettings.reduce((set, key) => { set.add(key); return set; }, new Set()); for (const key of Object.keys(ignoredSettings)) { - if (ignoredSettings[key]) { + if (ignored.has(key)) { this.editSetting(remoteModel, key, undefined); this.editSetting(remoteModel, key, remote[key]); } @@ -180,9 +182,9 @@ class SettingsMergeService implements ISettingsMergeService { } } - 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]); + private compare(from: IStringDictionary, to: IStringDictionary, ignored: Set): { added: Set, removed: Set, updated: Set } { + const fromKeys = Object.keys(from).filter(key => !ignored.has(key)); + const toKeys = Object.keys(to).filter(key => !ignored.has(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();