diff --git a/src/vs/platform/configuration/common/configurationModels.ts b/src/vs/platform/configuration/common/configurationModels.ts index 0e7e1bff810..eb599ee7f30 100644 --- a/src/vs/platform/configuration/common/configurationModels.ts +++ b/src/vs/platform/configuration/common/configurationModels.ts @@ -595,12 +595,25 @@ export class Configuration { this._foldersConsolidatedConfigurations.delete(resource); } - compareAndUpdateDefaultConfiguration(defaults: ConfigurationModel): IConfigurationChange { - const { added, updated, removed, overrides } = compare(this._defaultConfiguration, defaults); - const keys = [...added, ...updated, ...removed]; - if (keys.length) { - this.updateDefaultConfiguration(defaults); + compareAndUpdateDefaultConfiguration(defaults: ConfigurationModel, keys?: string[]): IConfigurationChange { + const overrides: [string, string[]][] = []; + if (!keys) { + const { added, updated, removed } = compare(this._defaultConfiguration, defaults); + keys = [...added, ...updated, ...removed]; } + for (const key of keys) { + for (const overrideIdentifier of overrideIdentifiersFromKey(key)) { + const fromKeys = this._defaultConfiguration.getKeysForOverrideIdentifier(overrideIdentifier); + const toKeys = defaults.getKeysForOverrideIdentifier(overrideIdentifier); + const keys = [ + ...toKeys.filter(key => fromKeys.indexOf(key) === -1), + ...fromKeys.filter(key => toKeys.indexOf(key) === -1), + ...fromKeys.filter(key => !objects.equals(this._defaultConfiguration.override(overrideIdentifier).getValue(key), defaults.override(overrideIdentifier).getValue(key))) + ]; + overrides.push([overrideIdentifier, keys]); + } + } + this.updateDefaultConfiguration(defaults); return { keys, overrides }; } diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index 1083115b16c..28b6aa327ea 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -215,6 +215,7 @@ export const windowSettings: { properties: IStringDictionary, patternProperties: IStringDictionary } = { properties: {}, patternProperties: {} }; export const resourceLanguageSettingsSchemaId = 'vscode://schemas/settings/resourceLanguage'; +export const configurationDefaultsSchemaId = 'vscode://schemas/settings/configurationDefaults'; const contributionRegistry = Registry.as(JSONExtensions.JSONContribution); diff --git a/src/vs/platform/configuration/common/configurationService.ts b/src/vs/platform/configuration/common/configurationService.ts index 3bc96c77572..7860c72ea8e 100644 --- a/src/vs/platform/configuration/common/configurationService.ts +++ b/src/vs/platform/configuration/common/configurationService.ts @@ -34,7 +34,7 @@ export class ConfigurationService extends Disposable implements IConfigurationSe this.configuration = new Configuration(new DefaultConfigurationModel(), new ConfigurationModel()); this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reloadConfiguration(), 50)); - this._register(Registry.as(Extensions.Configuration).onDidUpdateConfiguration(() => this.onDidDefaultConfigurationChange())); + this._register(Registry.as(Extensions.Configuration).onDidUpdateConfiguration(({ properties }) => this.onDidDefaultConfigurationChange(properties))); this._register(this.userConfiguration.onDidChange(() => this.reloadConfigurationScheduler.schedule())); } @@ -89,9 +89,9 @@ export class ConfigurationService extends Disposable implements IConfigurationSe this.trigger(change, previous, ConfigurationTarget.USER); } - private onDidDefaultConfigurationChange(): void { + private onDidDefaultConfigurationChange(properties: string[]): void { const previous = this.configuration.toData(); - const change = this.configuration.compareAndUpdateDefaultConfiguration(new DefaultConfigurationModel()); + const change = this.configuration.compareAndUpdateDefaultConfiguration(new DefaultConfigurationModel(), properties); this.trigger(change, previous, ConfigurationTarget.DEFAULT); } diff --git a/src/vs/platform/configuration/test/common/configurationModels.test.ts b/src/vs/platform/configuration/test/common/configurationModels.test.ts index 3b0b466cc55..ae1add24a78 100644 --- a/src/vs/platform/configuration/test/common/configurationModels.test.ts +++ b/src/vs/platform/configuration/test/common/configurationModels.test.ts @@ -525,9 +525,9 @@ suite('Configuration', () => { '[markdown]': { 'editor.wordWrap': 'off' } - })); + }), ['editor.lineNumbers', '[markdown]']); - assert.deepStrictEqual(actual, { keys: ['[markdown]', 'editor.lineNumbers'], overrides: [['markdown', ['editor.wordWrap']]] }); + assert.deepStrictEqual(actual, { keys: ['editor.lineNumbers', '[markdown]'], overrides: [['markdown', ['editor.wordWrap']]] }); }); @@ -890,7 +890,7 @@ suite('ConfigurationChangeEvent', () => { '[markdown]': { 'editor.wordWrap': 'off' } - })), + }), ['editor.lineNumbers', '[markdown]']), configuration.compareAndUpdateLocalUserConfiguration(toConfigurationModel({ '[json]': { 'editor.lineNumbers': 'relative' diff --git a/src/vs/workbench/api/common/configurationExtensionPoint.ts b/src/vs/workbench/api/common/configurationExtensionPoint.ts index f1e7bff0d35..dae0689cafe 100644 --- a/src/vs/workbench/api/common/configurationExtensionPoint.ts +++ b/src/vs/workbench/api/common/configurationExtensionPoint.ts @@ -8,7 +8,7 @@ import * as objects from 'vs/base/common/objects'; import { Registry } from 'vs/platform/registry/common/platform'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { IConfigurationNode, IConfigurationRegistry, Extensions, resourceLanguageSettingsSchemaId, validateProperty, ConfigurationScope, OVERRIDE_PROPERTY_PATTERN, OVERRIDE_PROPERTY_REGEX, windowSettings, resourceSettings, machineOverridableSettings, IConfigurationDefaults } from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationNode, IConfigurationRegistry, Extensions, validateProperty, ConfigurationScope, OVERRIDE_PROPERTY_REGEX, IConfigurationDefaults, configurationDefaultsSchemaId } from 'vs/platform/configuration/common/configurationRegistry'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { workspaceSettingsSchemaId, launchSchemaId, tasksSchemaId } from 'vs/workbench/services/configuration/common/configuration'; import { isObject } from 'vs/base/common/types'; @@ -112,29 +112,6 @@ const configurationEntrySchema: IJSONSchema = { } }; -const configurationDefaultsSchemaId = 'vscode://schemas/settings/configurationDefaults'; -const configurationDefaultsSchema: IJSONSchema = { - type: 'object', - description: nls.localize('configurationDefaults.description', 'Contribute defaults for configurations'), - properties: {}, - patternProperties: { - [OVERRIDE_PROPERTY_PATTERN]: { - type: 'object', - default: {}, - $ref: resourceLanguageSettingsSchemaId, - } - }, - additionalProperties: false -}; -jsonRegistry.registerSchema(configurationDefaultsSchemaId, configurationDefaultsSchema); -configurationRegistry.onDidSchemaChange(() => { - configurationDefaultsSchema.properties = { - ...machineOverridableSettings.properties, - ...windowSettings.properties, - ...resourceSettings.properties - }; -}); - // BEGIN VSCode extension point `configurationDefaults` const defaultConfigurationExtPoint = ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'configurationDefaults', diff --git a/src/vs/workbench/services/configuration/browser/configuration.ts b/src/vs/workbench/services/configuration/browser/configuration.ts index bc9ad5c1fdf..b92fea9cdda 100644 --- a/src/vs/workbench/services/configuration/browser/configuration.ts +++ b/src/vs/workbench/services/configuration/browser/configuration.ts @@ -36,8 +36,8 @@ export class DefaultConfiguration extends Disposable { private cachedConfigurationDefaultsOverrides: IStringDictionary = {}; private readonly cacheKey: ConfigurationKey = { type: 'defaults', key: 'configurationDefaultsOverrides' }; - private readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); - readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; + private readonly _onDidChangeConfiguration = this._register(new Emitter<{ defaults: ConfigurationModel, properties: string[] }>()); + readonly onDidChangeConfiguration = this._onDidChangeConfiguration.event; private updateCache: boolean = false; @@ -62,7 +62,7 @@ export class DefaultConfiguration extends Disposable { async initialize(): Promise { await this.initializeCachedConfigurationDefaultsOverrides(); this._configurationModel = undefined; - this._register(this.configurationRegistry.onDidUpdateConfiguration(({ defaultsOverrides }) => this.onDidUpdateConfiguration(defaultsOverrides))); + this._register(this.configurationRegistry.onDidUpdateConfiguration(({ properties, defaultsOverrides }) => this.onDidUpdateConfiguration(properties, defaultsOverrides))); return this.configurationModel; } @@ -93,9 +93,9 @@ export class DefaultConfiguration extends Disposable { return this.initiaizeCachedConfigurationDefaultsOverridesPromise; } - private onDidUpdateConfiguration(defaultsOverrides?: boolean): void { + private onDidUpdateConfiguration(properties: string[], defaultsOverrides?: boolean): void { this._configurationModel = undefined; - this._onDidChangeConfiguration.fire(this.configurationModel); + this._onDidChangeConfiguration.fire({ defaults: this.configurationModel, properties }); if (defaultsOverrides) { this.updateCachedConfigurationDefaultsOverrides(); } diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 9e8880d7057..81cb9f779a1 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -16,7 +16,7 @@ import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides import { Configuration } from 'vs/workbench/services/configuration/common/configurationModels'; import { FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId, IConfigurationCache, machineSettingsSchemaId, LOCAL_MACHINE_SCOPES, IWorkbenchConfigurationService, RestrictedSettings } from 'vs/workbench/services/configuration/common/configuration'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IConfigurationRegistry, Extensions, allSettings, windowSettings, resourceSettings, applicationSettings, machineSettings, machineOverridableSettings, ConfigurationScope, IConfigurationPropertySchema, keyFromOverrideIdentifiers } from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationRegistry, Extensions, allSettings, windowSettings, resourceSettings, applicationSettings, machineSettings, machineOverridableSettings, ConfigurationScope, IConfigurationPropertySchema, keyFromOverrideIdentifiers, OVERRIDE_PROPERTY_PATTERN, resourceLanguageSettingsSchemaId, configurationDefaultsSchemaId } from 'vs/platform/configuration/common/configurationRegistry'; import { IWorkspaceIdentifier, isWorkspaceIdentifier, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, IWorkspaceInitializationPayload, IEmptyWorkspaceIdentifier, useSlashForPath, getStoredWorkspaceFolder, isSingleFolderWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, toWorkspaceFolders } from 'vs/platform/workspaces/common/workspaces'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ConfigurationEditingService, EditableConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditingService'; @@ -137,7 +137,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat }); })); - this._register(this.defaultConfiguration.onDidChangeConfiguration(configurationModel => this.onDefaultConfigurationChanged(configurationModel))); + this._register(this.defaultConfiguration.onDidChangeConfiguration(({ properties, defaults }) => this.onDefaultConfigurationChanged(defaults, properties))); this.workspaceEditingQueue = new Queue(); } @@ -663,10 +663,10 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat } } - private onDefaultConfigurationChanged(configurationModel: ConfigurationModel): void { + private onDefaultConfigurationChanged(configurationModel: ConfigurationModel, properties?: string[]): void { if (this.workspace) { const previousData = this._configuration.toData(); - const change = this._configuration.compareAndUpdateDefaultConfiguration(configurationModel); + const change = this._configuration.compareAndUpdateDefaultConfiguration(configurationModel, properties); if (this.remoteUserConfiguration) { this._configuration.updateLocalUserConfiguration(this.localUserConfiguration.reparse()); this._configuration.updateRemoteUserConfiguration(this.remoteUserConfiguration.reparse()); @@ -1092,6 +1092,24 @@ class RegisterConfigurationSchemasContribution extends Disposable implements IWo jsonRegistry.registerSchema(workspaceSettingsSchemaId, workspaceSettingsSchema); jsonRegistry.registerSchema(folderSettingsSchemaId, workspaceSettingsSchema); } + + jsonRegistry.registerSchema(configurationDefaultsSchemaId, { + type: 'object', + description: localize('configurationDefaults.description', 'Contribute defaults for configurations'), + properties: { + ...machineOverridableSettings.properties, + ...windowSettings.properties, + ...resourceSettings.properties + }, + patternProperties: { + [OVERRIDE_PROPERTY_PATTERN]: { + type: 'object', + default: {}, + $ref: resourceLanguageSettingsSchemaId, + } + }, + additionalProperties: false + }); } private checkAndFilterPropertiesRequiringTrust(properties: IStringDictionary): IStringDictionary { diff --git a/src/vs/workbench/services/configuration/test/browser/configuration.test.ts b/src/vs/workbench/services/configuration/test/browser/configuration.test.ts index b8011291bc6..05f5d53e8b3 100644 --- a/src/vs/workbench/services/configuration/test/browser/configuration.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configuration.test.ts @@ -116,7 +116,7 @@ suite('DefaultConfiguration', () => { } }); - const actual = await promise; + const { defaults: actual } = await promise; assert.deepStrictEqual(actual.getValue('test.configurationDefaultsOverride'), 'overrideValue'); });