Path: blob/main/src/vs/platform/configuration/common/configurationRegistry.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { distinct } from '../../../base/common/arrays.js';6import { IStringDictionary } from '../../../base/common/collections.js';7import { Emitter, Event } from '../../../base/common/event.js';8import { IJSONSchema } from '../../../base/common/jsonSchema.js';9import * as types from '../../../base/common/types.js';10import * as nls from '../../../nls.js';11import { getLanguageTagSettingPlainKey } from './configuration.js';12import { Extensions as JSONExtensions, IJSONContributionRegistry } from '../../jsonschemas/common/jsonContributionRegistry.js';13import { Registry } from '../../registry/common/platform.js';14import { IPolicy, PolicyName } from '../../../base/common/policy.js';15import { Disposable } from '../../../base/common/lifecycle.js';1617export enum EditPresentationTypes {18Multiline = 'multilineText',19Singleline = 'singlelineText'20}2122export const Extensions = {23Configuration: 'base.contributions.configuration'24};2526export interface IConfigurationDelta {27removedDefaults?: IConfigurationDefaults[];28removedConfigurations?: IConfigurationNode[];29addedDefaults?: IConfigurationDefaults[];30addedConfigurations?: IConfigurationNode[];31}3233export interface IConfigurationRegistry {3435/**36* Register a configuration to the registry.37*/38registerConfiguration(configuration: IConfigurationNode): IConfigurationNode;3940/**41* Register multiple configurations to the registry.42*/43registerConfigurations(configurations: IConfigurationNode[], validate?: boolean): void;4445/**46* Deregister multiple configurations from the registry.47*/48deregisterConfigurations(configurations: IConfigurationNode[]): void;4950/**51* update the configuration registry by52* - registering the configurations to add53* - dereigstering the configurations to remove54*/55updateConfigurations(configurations: { add: IConfigurationNode[]; remove: IConfigurationNode[] }): void;5657/**58* Register multiple default configurations to the registry.59*/60registerDefaultConfigurations(defaultConfigurations: IConfigurationDefaults[]): void;6162/**63* Deregister multiple default configurations from the registry.64*/65deregisterDefaultConfigurations(defaultConfigurations: IConfigurationDefaults[]): void;6667/**68* Bulk update of the configuration registry (default and configurations, remove and add)69* @param delta70*/71deltaConfiguration(delta: IConfigurationDelta): void;7273/**74* Return the registered default configurations75*/76getRegisteredDefaultConfigurations(): IConfigurationDefaults[];7778/**79* Return the registered configuration defaults overrides80*/81getConfigurationDefaultsOverrides(): Map<string, IConfigurationDefaultOverrideValue>;8283/**84* Signal that the schema of a configuration setting has changes. It is currently only supported to change enumeration values.85* Property or default value changes are not allowed.86*/87notifyConfigurationSchemaUpdated(...configurations: IConfigurationNode[]): void;8889/**90* Event that fires whenever a configuration has been91* registered.92*/93readonly onDidSchemaChange: Event<void>;9495/**96* Event that fires whenever a configuration has been97* registered.98*/99readonly onDidUpdateConfiguration: Event<{ properties: ReadonlySet<string>; defaultsOverrides?: boolean }>;100101/**102* Returns all configuration nodes contributed to this registry.103*/104getConfigurations(): IConfigurationNode[];105106/**107* Returns all configurations settings of all configuration nodes contributed to this registry.108*/109getConfigurationProperties(): IStringDictionary<IRegisteredConfigurationPropertySchema>;110111/**112* Return all configurations by policy name113*/114getPolicyConfigurations(): Map<PolicyName, string>;115116/**117* Returns all excluded configurations settings of all configuration nodes contributed to this registry.118*/119getExcludedConfigurationProperties(): IStringDictionary<IRegisteredConfigurationPropertySchema>;120121/**122* Register the identifiers for editor configurations123*/124registerOverrideIdentifiers(identifiers: string[]): void;125}126127export const enum ConfigurationScope {128/**129* Application specific configuration, which can be configured only in default profile user settings.130*/131APPLICATION = 1,132/**133* Machine specific configuration, which can be configured only in local and remote user settings.134*/135MACHINE,136/**137* An application machine specific configuration, which can be configured only in default profile user settings and remote user settings.138*/139APPLICATION_MACHINE,140/**141* Window specific configuration, which can be configured in the user or workspace settings.142*/143WINDOW,144/**145* Resource specific configuration, which can be configured in the user, workspace or folder settings.146*/147RESOURCE,148/**149* Resource specific configuration that can be configured in language specific settings150*/151LANGUAGE_OVERRIDABLE,152/**153* Machine specific configuration that can also be configured in workspace or folder settings.154*/155MACHINE_OVERRIDABLE,156}157158159export interface IConfigurationPropertySchema extends IJSONSchema {160161scope?: ConfigurationScope;162163/**164* When restricted, value of this configuration will be read only from trusted sources.165* For eg., If the workspace is not trusted, then the value of this configuration is not read from workspace settings file.166*/167restricted?: boolean;168169/**170* When `false` this property is excluded from the registry. Default is to include.171*/172included?: boolean;173174/**175* List of tags associated to the property.176* - A tag can be used for filtering177* - Use `experimental` tag for marking the setting as experimental.178*/179tags?: string[];180181/**182* When enabled this setting is ignored during sync and user can override this.183*/184ignoreSync?: boolean;185186/**187* When enabled this setting is ignored during sync and user cannot override this.188*/189disallowSyncIgnore?: boolean;190191/**192* Disallow extensions to contribute configuration default value for this setting.193*/194disallowConfigurationDefault?: boolean;195196/**197* Labels for enumeration items198*/199enumItemLabels?: string[];200201/**202* When specified, controls the presentation format of string settings.203* Otherwise, the presentation format defaults to `singleline`.204*/205editPresentation?: EditPresentationTypes;206207/**208* When specified, gives an order number for the setting209* within the settings editor. Otherwise, the setting is placed at the end.210*/211order?: number;212213/**214* When specified, this setting's value can always be overwritten by215* a system-wide policy.216*/217policy?: IPolicy;218219/**220* When specified, this setting's default value can always be overwritten by221* an experiment.222*/223experiment?: {224/**225* The mode of the experiment.226* - `startup`: The setting value is updated to the experiment value only on startup.227* - `auto`: The setting value is updated to the experiment value automatically (whenever the experiment value changes).228*/229mode: 'startup' | 'auto';230231/**232* The name of the experiment. By default, this is `config.${settingId}`233*/234name?: string;235};236}237238export interface IExtensionInfo {239id: string;240displayName?: string;241}242243export interface IConfigurationNode {244id?: string;245order?: number;246type?: string | string[];247title?: string;248description?: string;249properties?: IStringDictionary<IConfigurationPropertySchema>;250allOf?: IConfigurationNode[];251scope?: ConfigurationScope;252extensionInfo?: IExtensionInfo;253restrictedProperties?: string[];254}255256export type ConfigurationDefaultValueSource = IExtensionInfo | Map<string, IExtensionInfo>;257258export interface IConfigurationDefaults {259overrides: IStringDictionary<any>;260source?: IExtensionInfo;261}262263export type IRegisteredConfigurationPropertySchema = IConfigurationPropertySchema & {264defaultDefaultValue?: any;265source?: IExtensionInfo; // Source of the Property266defaultValueSource?: ConfigurationDefaultValueSource; // Source of the Default Value267};268269export interface IConfigurationDefaultOverride {270readonly value: any;271readonly source?: IExtensionInfo; // Source of the default override272}273274export interface IConfigurationDefaultOverrideValue {275readonly value: any;276readonly source?: ConfigurationDefaultValueSource;277}278279export const allSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };280export const applicationSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };281export const applicationMachineSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };282export const machineSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };283export const machineOverridableSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };284export const windowSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };285export const resourceSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };286287export const resourceLanguageSettingsSchemaId = 'vscode://schemas/settings/resourceLanguage';288export const configurationDefaultsSchemaId = 'vscode://schemas/settings/configurationDefaults';289290const contributionRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);291292class ConfigurationRegistry extends Disposable implements IConfigurationRegistry {293294private readonly registeredConfigurationDefaults: IConfigurationDefaults[] = [];295private readonly configurationDefaultsOverrides: Map<string, { configurationDefaultOverrides: IConfigurationDefaultOverride[]; configurationDefaultOverrideValue?: IConfigurationDefaultOverrideValue }>;296private readonly defaultLanguageConfigurationOverridesNode: IConfigurationNode;297private readonly configurationContributors: IConfigurationNode[];298private readonly configurationProperties: IStringDictionary<IRegisteredConfigurationPropertySchema>;299private readonly policyConfigurations: Map<PolicyName, string>;300private readonly excludedConfigurationProperties: IStringDictionary<IRegisteredConfigurationPropertySchema>;301private readonly resourceLanguageSettingsSchema: IJSONSchema;302private readonly overrideIdentifiers = new Set<string>();303304private readonly _onDidSchemaChange = this._register(new Emitter<void>());305readonly onDidSchemaChange: Event<void> = this._onDidSchemaChange.event;306307private readonly _onDidUpdateConfiguration = this._register(new Emitter<{ properties: ReadonlySet<string>; defaultsOverrides?: boolean }>());308readonly onDidUpdateConfiguration = this._onDidUpdateConfiguration.event;309310constructor() {311super();312this.configurationDefaultsOverrides = new Map();313this.defaultLanguageConfigurationOverridesNode = {314id: 'defaultOverrides',315title: nls.localize('defaultLanguageConfigurationOverrides.title', "Default Language Configuration Overrides"),316properties: {}317};318this.configurationContributors = [this.defaultLanguageConfigurationOverridesNode];319this.resourceLanguageSettingsSchema = {320properties: {},321patternProperties: {},322additionalProperties: true,323allowTrailingCommas: true,324allowComments: true325};326this.configurationProperties = {};327this.policyConfigurations = new Map<PolicyName, string>();328this.excludedConfigurationProperties = {};329330contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema);331this.registerOverridePropertyPatternKey();332}333334public registerConfiguration(configuration: IConfigurationNode, validate: boolean = true): IConfigurationNode {335this.registerConfigurations([configuration], validate);336return configuration;337}338339public registerConfigurations(configurations: IConfigurationNode[], validate: boolean = true): void {340const properties = new Set<string>();341this.doRegisterConfigurations(configurations, validate, properties);342343contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema);344this._onDidSchemaChange.fire();345this._onDidUpdateConfiguration.fire({ properties });346}347348public deregisterConfigurations(configurations: IConfigurationNode[]): void {349const properties = new Set<string>();350this.doDeregisterConfigurations(configurations, properties);351352contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema);353this._onDidSchemaChange.fire();354this._onDidUpdateConfiguration.fire({ properties });355}356357public updateConfigurations({ add, remove }: { add: IConfigurationNode[]; remove: IConfigurationNode[] }): void {358const properties = new Set<string>();359this.doDeregisterConfigurations(remove, properties);360this.doRegisterConfigurations(add, false, properties);361362contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema);363this._onDidSchemaChange.fire();364this._onDidUpdateConfiguration.fire({ properties });365}366367public registerDefaultConfigurations(configurationDefaults: IConfigurationDefaults[]): void {368const properties = new Set<string>();369this.doRegisterDefaultConfigurations(configurationDefaults, properties);370this._onDidSchemaChange.fire();371this._onDidUpdateConfiguration.fire({ properties, defaultsOverrides: true });372}373374private doRegisterDefaultConfigurations(configurationDefaults: IConfigurationDefaults[], bucket: Set<string>) {375376this.registeredConfigurationDefaults.push(...configurationDefaults);377378const overrideIdentifiers: string[] = [];379380for (const { overrides, source } of configurationDefaults) {381for (const key in overrides) {382bucket.add(key);383384const configurationDefaultOverridesForKey = this.configurationDefaultsOverrides.get(key)385?? this.configurationDefaultsOverrides.set(key, { configurationDefaultOverrides: [] }).get(key)!;386387const value = overrides[key];388configurationDefaultOverridesForKey.configurationDefaultOverrides.push({ value, source });389390// Configuration defaults for Override Identifiers391if (OVERRIDE_PROPERTY_REGEX.test(key)) {392const newDefaultOverride = this.mergeDefaultConfigurationsForOverrideIdentifier(key, value, source, configurationDefaultOverridesForKey.configurationDefaultOverrideValue);393if (!newDefaultOverride) {394continue;395}396397configurationDefaultOverridesForKey.configurationDefaultOverrideValue = newDefaultOverride;398this.updateDefaultOverrideProperty(key, newDefaultOverride, source);399overrideIdentifiers.push(...overrideIdentifiersFromKey(key));400}401402// Configuration defaults for Configuration Properties403else {404const newDefaultOverride = this.mergeDefaultConfigurationsForConfigurationProperty(key, value, source, configurationDefaultOverridesForKey.configurationDefaultOverrideValue);405if (!newDefaultOverride) {406continue;407}408409configurationDefaultOverridesForKey.configurationDefaultOverrideValue = newDefaultOverride;410const property = this.configurationProperties[key];411if (property) {412this.updatePropertyDefaultValue(key, property);413this.updateSchema(key, property);414}415}416417}418}419420this.doRegisterOverrideIdentifiers(overrideIdentifiers);421}422423public deregisterDefaultConfigurations(defaultConfigurations: IConfigurationDefaults[]): void {424const properties = new Set<string>();425this.doDeregisterDefaultConfigurations(defaultConfigurations, properties);426this._onDidSchemaChange.fire();427this._onDidUpdateConfiguration.fire({ properties, defaultsOverrides: true });428}429430private doDeregisterDefaultConfigurations(defaultConfigurations: IConfigurationDefaults[], bucket: Set<string>): void {431for (const defaultConfiguration of defaultConfigurations) {432const index = this.registeredConfigurationDefaults.indexOf(defaultConfiguration);433if (index !== -1) {434this.registeredConfigurationDefaults.splice(index, 1);435}436}437438for (const { overrides, source } of defaultConfigurations) {439for (const key in overrides) {440const configurationDefaultOverridesForKey = this.configurationDefaultsOverrides.get(key);441if (!configurationDefaultOverridesForKey) {442continue;443}444445const index = configurationDefaultOverridesForKey.configurationDefaultOverrides446.findIndex(configurationDefaultOverride => source ? configurationDefaultOverride.source?.id === source.id : configurationDefaultOverride.value === overrides[key]);447if (index === -1) {448continue;449}450451configurationDefaultOverridesForKey.configurationDefaultOverrides.splice(index, 1);452if (configurationDefaultOverridesForKey.configurationDefaultOverrides.length === 0) {453this.configurationDefaultsOverrides.delete(key);454}455456if (OVERRIDE_PROPERTY_REGEX.test(key)) {457let configurationDefaultOverrideValue: IConfigurationDefaultOverrideValue | undefined;458for (const configurationDefaultOverride of configurationDefaultOverridesForKey.configurationDefaultOverrides) {459configurationDefaultOverrideValue = this.mergeDefaultConfigurationsForOverrideIdentifier(key, configurationDefaultOverride.value, configurationDefaultOverride.source, configurationDefaultOverrideValue);460}461if (configurationDefaultOverrideValue && !types.isEmptyObject(configurationDefaultOverrideValue.value)) {462configurationDefaultOverridesForKey.configurationDefaultOverrideValue = configurationDefaultOverrideValue;463this.updateDefaultOverrideProperty(key, configurationDefaultOverrideValue, source);464} else {465this.configurationDefaultsOverrides.delete(key);466delete this.configurationProperties[key];467delete this.defaultLanguageConfigurationOverridesNode.properties![key];468}469} else {470let configurationDefaultOverrideValue: IConfigurationDefaultOverrideValue | undefined;471for (const configurationDefaultOverride of configurationDefaultOverridesForKey.configurationDefaultOverrides) {472configurationDefaultOverrideValue = this.mergeDefaultConfigurationsForConfigurationProperty(key, configurationDefaultOverride.value, configurationDefaultOverride.source, configurationDefaultOverrideValue);473}474configurationDefaultOverridesForKey.configurationDefaultOverrideValue = configurationDefaultOverrideValue;475const property = this.configurationProperties[key];476if (property) {477this.updatePropertyDefaultValue(key, property);478this.updateSchema(key, property);479}480}481bucket.add(key);482}483}484this.updateOverridePropertyPatternKey();485}486487private updateDefaultOverrideProperty(key: string, newDefaultOverride: IConfigurationDefaultOverrideValue, source: IExtensionInfo | undefined): void {488const property: IRegisteredConfigurationPropertySchema = {489type: 'object',490default: newDefaultOverride.value,491description: nls.localize('defaultLanguageConfiguration.description', "Configure settings to be overridden for {0}.", getLanguageTagSettingPlainKey(key)),492$ref: resourceLanguageSettingsSchemaId,493defaultDefaultValue: newDefaultOverride.value,494source,495defaultValueSource: source496};497this.configurationProperties[key] = property;498this.defaultLanguageConfigurationOverridesNode.properties![key] = property;499}500501private mergeDefaultConfigurationsForOverrideIdentifier(overrideIdentifier: string, configurationValueObject: IStringDictionary<any>, valueSource: IExtensionInfo | undefined, existingDefaultOverride: IConfigurationDefaultOverrideValue | undefined): IConfigurationDefaultOverrideValue | undefined {502const defaultValue = existingDefaultOverride?.value || {};503const source = existingDefaultOverride?.source ?? new Map<string, IExtensionInfo>();504505// This should not happen506if (!(source instanceof Map)) {507console.error('objectConfigurationSources is not a Map');508return undefined;509}510511for (const propertyKey of Object.keys(configurationValueObject)) {512const propertyDefaultValue = configurationValueObject[propertyKey];513514const isObjectSetting = types.isObject(propertyDefaultValue) &&515(types.isUndefined(defaultValue[propertyKey]) || types.isObject(defaultValue[propertyKey]));516517// If the default value is an object, merge the objects and store the source of each keys518if (isObjectSetting) {519defaultValue[propertyKey] = { ...(defaultValue[propertyKey] ?? {}), ...propertyDefaultValue };520// Track the source of each value in the object521if (valueSource) {522for (const objectKey in propertyDefaultValue) {523source.set(`${propertyKey}.${objectKey}`, valueSource);524}525}526}527528// Primitive values are overridden529else {530defaultValue[propertyKey] = propertyDefaultValue;531if (valueSource) {532source.set(propertyKey, valueSource);533} else {534source.delete(propertyKey);535}536}537}538539return { value: defaultValue, source };540}541542private mergeDefaultConfigurationsForConfigurationProperty(propertyKey: string, value: any, valuesSource: IExtensionInfo | undefined, existingDefaultOverride: IConfigurationDefaultOverrideValue | undefined): IConfigurationDefaultOverrideValue | undefined {543const property = this.configurationProperties[propertyKey];544const existingDefaultValue = existingDefaultOverride?.value ?? property?.defaultDefaultValue;545let source: ConfigurationDefaultValueSource | undefined = valuesSource;546547const isObjectSetting = types.isObject(value) &&548(549property !== undefined && property.type === 'object' ||550property === undefined && (types.isUndefined(existingDefaultValue) || types.isObject(existingDefaultValue))551);552553// If the default value is an object, merge the objects and store the source of each keys554if (isObjectSetting) {555source = existingDefaultOverride?.source ?? new Map<string, IExtensionInfo>();556557// This should not happen558if (!(source instanceof Map)) {559console.error('defaultValueSource is not a Map');560return undefined;561}562563for (const objectKey in value) {564if (valuesSource) {565source.set(`${propertyKey}.${objectKey}`, valuesSource);566}567}568value = { ...(types.isObject(existingDefaultValue) ? existingDefaultValue : {}), ...value };569}570571return { value, source };572}573574public deltaConfiguration(delta: IConfigurationDelta): void {575// defaults: remove576let defaultsOverrides = false;577const properties = new Set<string>();578if (delta.removedDefaults) {579this.doDeregisterDefaultConfigurations(delta.removedDefaults, properties);580defaultsOverrides = true;581}582// defaults: add583if (delta.addedDefaults) {584this.doRegisterDefaultConfigurations(delta.addedDefaults, properties);585defaultsOverrides = true;586}587// configurations: remove588if (delta.removedConfigurations) {589this.doDeregisterConfigurations(delta.removedConfigurations, properties);590}591// configurations: add592if (delta.addedConfigurations) {593this.doRegisterConfigurations(delta.addedConfigurations, false, properties);594}595this._onDidSchemaChange.fire();596this._onDidUpdateConfiguration.fire({ properties, defaultsOverrides });597}598599public notifyConfigurationSchemaUpdated(...configurations: IConfigurationNode[]) {600this._onDidSchemaChange.fire();601}602603public registerOverrideIdentifiers(overrideIdentifiers: string[]): void {604this.doRegisterOverrideIdentifiers(overrideIdentifiers);605this._onDidSchemaChange.fire();606}607608private doRegisterOverrideIdentifiers(overrideIdentifiers: string[]) {609for (const overrideIdentifier of overrideIdentifiers) {610this.overrideIdentifiers.add(overrideIdentifier);611}612this.updateOverridePropertyPatternKey();613}614615private doRegisterConfigurations(configurations: IConfigurationNode[], validate: boolean, bucket: Set<string>): void {616617configurations.forEach(configuration => {618619this.validateAndRegisterProperties(configuration, validate, configuration.extensionInfo, configuration.restrictedProperties, undefined, bucket);620621this.configurationContributors.push(configuration);622this.registerJSONConfiguration(configuration);623});624}625626private doDeregisterConfigurations(configurations: IConfigurationNode[], bucket: Set<string>): void {627628const deregisterConfiguration = (configuration: IConfigurationNode) => {629if (configuration.properties) {630for (const key in configuration.properties) {631bucket.add(key);632const property = this.configurationProperties[key];633if (property?.policy?.name) {634this.policyConfigurations.delete(property.policy.name);635}636delete this.configurationProperties[key];637this.removeFromSchema(key, configuration.properties[key]);638}639}640configuration.allOf?.forEach(node => deregisterConfiguration(node));641};642for (const configuration of configurations) {643deregisterConfiguration(configuration);644const index = this.configurationContributors.indexOf(configuration);645if (index !== -1) {646this.configurationContributors.splice(index, 1);647}648}649}650651private validateAndRegisterProperties(configuration: IConfigurationNode, validate: boolean = true, extensionInfo: IExtensionInfo | undefined, restrictedProperties: string[] | undefined, scope: ConfigurationScope = ConfigurationScope.WINDOW, bucket: Set<string>): void {652scope = types.isUndefinedOrNull(configuration.scope) ? scope : configuration.scope;653const properties = configuration.properties;654if (properties) {655for (const key in properties) {656const property: IRegisteredConfigurationPropertySchema = properties[key];657if (validate && validateProperty(key, property)) {658delete properties[key];659continue;660}661662property.source = extensionInfo;663664// update default value665property.defaultDefaultValue = properties[key].default;666this.updatePropertyDefaultValue(key, property);667668// update scope669if (OVERRIDE_PROPERTY_REGEX.test(key)) {670property.scope = undefined; // No scope for overridable properties `[${identifier}]`671} else {672property.scope = types.isUndefinedOrNull(property.scope) ? scope : property.scope;673property.restricted = types.isUndefinedOrNull(property.restricted) ? !!restrictedProperties?.includes(key) : property.restricted;674}675676if (property.experiment) {677if (!property.tags?.some(tag => tag.toLowerCase() === 'onexp')) {678property.tags = property.tags ?? [];679property.tags.push('onExP');680}681} else if (property.tags?.some(tag => tag.toLowerCase() === 'onexp')) {682console.error(`Invalid tag 'onExP' found for property '${key}'. Please use 'experiment' property instead.`);683property.experiment = { mode: 'startup' };684}685686const excluded = properties[key].hasOwnProperty('included') && !properties[key].included;687const policyName = properties[key].policy?.name;688689if (excluded) {690this.excludedConfigurationProperties[key] = properties[key];691if (policyName) {692this.policyConfigurations.set(policyName, key);693bucket.add(key);694}695delete properties[key];696} else {697bucket.add(key);698if (policyName) {699this.policyConfigurations.set(policyName, key);700}701this.configurationProperties[key] = properties[key];702if (!properties[key].deprecationMessage && properties[key].markdownDeprecationMessage) {703// If not set, default deprecationMessage to the markdown source704properties[key].deprecationMessage = properties[key].markdownDeprecationMessage;705}706}707708709}710}711const subNodes = configuration.allOf;712if (subNodes) {713for (const node of subNodes) {714this.validateAndRegisterProperties(node, validate, extensionInfo, restrictedProperties, scope, bucket);715}716}717}718719// TODO: @sandy081 - Remove this method and include required info in getConfigurationProperties720getConfigurations(): IConfigurationNode[] {721return this.configurationContributors;722}723724getConfigurationProperties(): IStringDictionary<IRegisteredConfigurationPropertySchema> {725return this.configurationProperties;726}727728getPolicyConfigurations(): Map<PolicyName, string> {729return this.policyConfigurations;730}731732getExcludedConfigurationProperties(): IStringDictionary<IRegisteredConfigurationPropertySchema> {733return this.excludedConfigurationProperties;734}735736getRegisteredDefaultConfigurations(): IConfigurationDefaults[] {737return [...this.registeredConfigurationDefaults];738}739740getConfigurationDefaultsOverrides(): Map<string, IConfigurationDefaultOverrideValue> {741const configurationDefaultsOverrides = new Map<string, IConfigurationDefaultOverrideValue>();742for (const [key, value] of this.configurationDefaultsOverrides) {743if (value.configurationDefaultOverrideValue) {744configurationDefaultsOverrides.set(key, value.configurationDefaultOverrideValue);745}746}747return configurationDefaultsOverrides;748}749750private registerJSONConfiguration(configuration: IConfigurationNode) {751const register = (configuration: IConfigurationNode) => {752const properties = configuration.properties;753if (properties) {754for (const key in properties) {755this.updateSchema(key, properties[key]);756}757}758const subNodes = configuration.allOf;759subNodes?.forEach(register);760};761register(configuration);762}763764private updateSchema(key: string, property: IConfigurationPropertySchema): void {765allSettings.properties[key] = property;766switch (property.scope) {767case ConfigurationScope.APPLICATION:768applicationSettings.properties[key] = property;769break;770case ConfigurationScope.MACHINE:771machineSettings.properties[key] = property;772break;773case ConfigurationScope.APPLICATION_MACHINE:774applicationMachineSettings.properties[key] = property;775break;776case ConfigurationScope.MACHINE_OVERRIDABLE:777machineOverridableSettings.properties[key] = property;778break;779case ConfigurationScope.WINDOW:780windowSettings.properties[key] = property;781break;782case ConfigurationScope.RESOURCE:783resourceSettings.properties[key] = property;784break;785case ConfigurationScope.LANGUAGE_OVERRIDABLE:786resourceSettings.properties[key] = property;787this.resourceLanguageSettingsSchema.properties![key] = property;788break;789}790}791792private removeFromSchema(key: string, property: IConfigurationPropertySchema): void {793delete allSettings.properties[key];794switch (property.scope) {795case ConfigurationScope.APPLICATION:796delete applicationSettings.properties[key];797break;798case ConfigurationScope.MACHINE:799delete machineSettings.properties[key];800break;801case ConfigurationScope.APPLICATION_MACHINE:802delete applicationMachineSettings.properties[key];803break;804case ConfigurationScope.MACHINE_OVERRIDABLE:805delete machineOverridableSettings.properties[key];806break;807case ConfigurationScope.WINDOW:808delete windowSettings.properties[key];809break;810case ConfigurationScope.RESOURCE:811case ConfigurationScope.LANGUAGE_OVERRIDABLE:812delete resourceSettings.properties[key];813delete this.resourceLanguageSettingsSchema.properties![key];814break;815}816}817818private updateOverridePropertyPatternKey(): void {819for (const overrideIdentifier of this.overrideIdentifiers.values()) {820const overrideIdentifierProperty = `[${overrideIdentifier}]`;821const resourceLanguagePropertiesSchema: IJSONSchema = {822type: 'object',823description: nls.localize('overrideSettings.defaultDescription', "Configure editor settings to be overridden for a language."),824errorMessage: nls.localize('overrideSettings.errorMessage', "This setting does not support per-language configuration."),825$ref: resourceLanguageSettingsSchemaId,826};827this.updatePropertyDefaultValue(overrideIdentifierProperty, resourceLanguagePropertiesSchema);828allSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;829applicationSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;830applicationMachineSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;831machineSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;832machineOverridableSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;833windowSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;834resourceSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;835}836}837838private registerOverridePropertyPatternKey(): void {839const resourceLanguagePropertiesSchema: IJSONSchema = {840type: 'object',841description: nls.localize('overrideSettings.defaultDescription', "Configure editor settings to be overridden for a language."),842errorMessage: nls.localize('overrideSettings.errorMessage', "This setting does not support per-language configuration."),843$ref: resourceLanguageSettingsSchemaId,844};845allSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;846applicationSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;847applicationMachineSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;848machineSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;849machineOverridableSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;850windowSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;851resourceSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;852this._onDidSchemaChange.fire();853}854855private updatePropertyDefaultValue(key: string, property: IRegisteredConfigurationPropertySchema): void {856const configurationdefaultOverride = this.configurationDefaultsOverrides.get(key)?.configurationDefaultOverrideValue;857let defaultValue = undefined;858let defaultSource = undefined;859if (configurationdefaultOverride860&& (!property.disallowConfigurationDefault || !configurationdefaultOverride.source) // Prevent overriding the default value if the property is disallowed to be overridden by configuration defaults from extensions861) {862defaultValue = configurationdefaultOverride.value;863defaultSource = configurationdefaultOverride.source;864}865if (types.isUndefined(defaultValue)) {866defaultValue = property.defaultDefaultValue;867defaultSource = undefined;868}869if (types.isUndefined(defaultValue)) {870defaultValue = getDefaultValue(property.type);871}872property.default = defaultValue;873property.defaultValueSource = defaultSource;874}875}876877const OVERRIDE_IDENTIFIER_PATTERN = `\\[([^\\]]+)\\]`;878const OVERRIDE_IDENTIFIER_REGEX = new RegExp(OVERRIDE_IDENTIFIER_PATTERN, 'g');879export const OVERRIDE_PROPERTY_PATTERN = `^(${OVERRIDE_IDENTIFIER_PATTERN})+$`;880export const OVERRIDE_PROPERTY_REGEX = new RegExp(OVERRIDE_PROPERTY_PATTERN);881882export function overrideIdentifiersFromKey(key: string): string[] {883const identifiers: string[] = [];884if (OVERRIDE_PROPERTY_REGEX.test(key)) {885let matches = OVERRIDE_IDENTIFIER_REGEX.exec(key);886while (matches?.length) {887const identifier = matches[1].trim();888if (identifier) {889identifiers.push(identifier);890}891matches = OVERRIDE_IDENTIFIER_REGEX.exec(key);892}893}894return distinct(identifiers);895}896897export function keyFromOverrideIdentifiers(overrideIdentifiers: string[]): string {898return overrideIdentifiers.reduce((result, overrideIdentifier) => `${result}[${overrideIdentifier}]`, '');899}900901export function getDefaultValue(type: string | string[] | undefined) {902const t = Array.isArray(type) ? (<string[]>type)[0] : <string>type;903switch (t) {904case 'boolean':905return false;906case 'integer':907case 'number':908return 0;909case 'string':910return '';911case 'array':912return [];913case 'object':914return {};915default:916return null;917}918}919920const configurationRegistry = new ConfigurationRegistry();921Registry.add(Extensions.Configuration, configurationRegistry);922923export function validateProperty(property: string, schema: IRegisteredConfigurationPropertySchema): string | null {924if (!property.trim()) {925return nls.localize('config.property.empty', "Cannot register an empty property");926}927if (OVERRIDE_PROPERTY_REGEX.test(property)) {928return nls.localize('config.property.languageDefault', "Cannot register '{0}'. This matches property pattern '\\\\[.*\\\\]$' for describing language specific editor settings. Use 'configurationDefaults' contribution.", property);929}930if (configurationRegistry.getConfigurationProperties()[property] !== undefined) {931return nls.localize('config.property.duplicate', "Cannot register '{0}'. This property is already registered.", property);932}933if (schema.policy?.name && configurationRegistry.getPolicyConfigurations().get(schema.policy?.name) !== undefined) {934return nls.localize('config.policy.duplicate', "Cannot register '{0}'. The associated policy {1} is already registered with {2}.", property, schema.policy?.name, configurationRegistry.getPolicyConfigurations().get(schema.policy?.name));935}936return null;937}938939export function getScopes(): [string, ConfigurationScope | undefined][] {940const scopes: [string, ConfigurationScope | undefined][] = [];941const configurationProperties = configurationRegistry.getConfigurationProperties();942for (const key of Object.keys(configurationProperties)) {943scopes.push([key, configurationProperties[key].scope]);944}945scopes.push(['launch', ConfigurationScope.RESOURCE]);946scopes.push(['task', ConfigurationScope.RESOURCE]);947return scopes;948}949950export function getAllConfigurationProperties(configurationNode: IConfigurationNode[]): IStringDictionary<IRegisteredConfigurationPropertySchema> {951const result: IStringDictionary<IRegisteredConfigurationPropertySchema> = {};952for (const configuration of configurationNode) {953const properties = configuration.properties;954if (types.isObject(properties)) {955for (const key in properties) {956result[key] = properties[key];957}958}959if (configuration.allOf) {960Object.assign(result, getAllConfigurationProperties(configuration.allOf));961}962}963return result;964}965966export function parseScope(scope: string): ConfigurationScope {967switch (scope) {968case 'application':969return ConfigurationScope.APPLICATION;970case 'machine':971return ConfigurationScope.MACHINE;972case 'resource':973return ConfigurationScope.RESOURCE;974case 'machine-overridable':975return ConfigurationScope.MACHINE_OVERRIDABLE;976case 'language-overridable':977return ConfigurationScope.LANGUAGE_OVERRIDABLE;978default:979return ConfigurationScope.WINDOW;980}981}982983984