Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/configuration/common/configurationRegistry.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { distinct } from '../../../base/common/arrays.js';
7
import { IStringDictionary } from '../../../base/common/collections.js';
8
import { Emitter, Event } from '../../../base/common/event.js';
9
import { IJSONSchema } from '../../../base/common/jsonSchema.js';
10
import * as types from '../../../base/common/types.js';
11
import * as nls from '../../../nls.js';
12
import { getLanguageTagSettingPlainKey } from './configuration.js';
13
import { Extensions as JSONExtensions, IJSONContributionRegistry } from '../../jsonschemas/common/jsonContributionRegistry.js';
14
import { Registry } from '../../registry/common/platform.js';
15
import { IPolicy, PolicyName } from '../../../base/common/policy.js';
16
import { Disposable } from '../../../base/common/lifecycle.js';
17
18
export enum EditPresentationTypes {
19
Multiline = 'multilineText',
20
Singleline = 'singlelineText'
21
}
22
23
export const Extensions = {
24
Configuration: 'base.contributions.configuration'
25
};
26
27
export interface IConfigurationDelta {
28
removedDefaults?: IConfigurationDefaults[];
29
removedConfigurations?: IConfigurationNode[];
30
addedDefaults?: IConfigurationDefaults[];
31
addedConfigurations?: IConfigurationNode[];
32
}
33
34
export interface IConfigurationRegistry {
35
36
/**
37
* Register a configuration to the registry.
38
*/
39
registerConfiguration(configuration: IConfigurationNode): IConfigurationNode;
40
41
/**
42
* Register multiple configurations to the registry.
43
*/
44
registerConfigurations(configurations: IConfigurationNode[], validate?: boolean): void;
45
46
/**
47
* Deregister multiple configurations from the registry.
48
*/
49
deregisterConfigurations(configurations: IConfigurationNode[]): void;
50
51
/**
52
* update the configuration registry by
53
* - registering the configurations to add
54
* - dereigstering the configurations to remove
55
*/
56
updateConfigurations(configurations: { add: IConfigurationNode[]; remove: IConfigurationNode[] }): void;
57
58
/**
59
* Register multiple default configurations to the registry.
60
*/
61
registerDefaultConfigurations(defaultConfigurations: IConfigurationDefaults[]): void;
62
63
/**
64
* Deregister multiple default configurations from the registry.
65
*/
66
deregisterDefaultConfigurations(defaultConfigurations: IConfigurationDefaults[]): void;
67
68
/**
69
* Bulk update of the configuration registry (default and configurations, remove and add)
70
* @param delta
71
*/
72
deltaConfiguration(delta: IConfigurationDelta): void;
73
74
/**
75
* Return the registered default configurations
76
*/
77
getRegisteredDefaultConfigurations(): IConfigurationDefaults[];
78
79
/**
80
* Return the registered configuration defaults overrides
81
*/
82
getConfigurationDefaultsOverrides(): Map<string, IConfigurationDefaultOverrideValue>;
83
84
/**
85
* Signal that the schema of a configuration setting has changes. It is currently only supported to change enumeration values.
86
* Property or default value changes are not allowed.
87
*/
88
notifyConfigurationSchemaUpdated(...configurations: IConfigurationNode[]): void;
89
90
/**
91
* Event that fires whenever a configuration has been
92
* registered.
93
*/
94
readonly onDidSchemaChange: Event<void>;
95
96
/**
97
* Event that fires whenever a configuration has been
98
* registered.
99
*/
100
readonly onDidUpdateConfiguration: Event<{ properties: ReadonlySet<string>; defaultsOverrides?: boolean }>;
101
102
/**
103
* Returns all configuration nodes contributed to this registry.
104
*/
105
getConfigurations(): IConfigurationNode[];
106
107
/**
108
* Returns all configurations settings of all configuration nodes contributed to this registry.
109
*/
110
getConfigurationProperties(): IStringDictionary<IRegisteredConfigurationPropertySchema>;
111
112
/**
113
* Return all configurations by policy name
114
*/
115
getPolicyConfigurations(): Map<PolicyName, string>;
116
117
/**
118
* Returns all excluded configurations settings of all configuration nodes contributed to this registry.
119
*/
120
getExcludedConfigurationProperties(): IStringDictionary<IRegisteredConfigurationPropertySchema>;
121
122
/**
123
* Register the identifiers for editor configurations
124
*/
125
registerOverrideIdentifiers(identifiers: string[]): void;
126
}
127
128
export const enum ConfigurationScope {
129
/**
130
* Application specific configuration, which can be configured only in default profile user settings.
131
*/
132
APPLICATION = 1,
133
/**
134
* Machine specific configuration, which can be configured only in local and remote user settings.
135
*/
136
MACHINE,
137
/**
138
* An application machine specific configuration, which can be configured only in default profile user settings and remote user settings.
139
*/
140
APPLICATION_MACHINE,
141
/**
142
* Window specific configuration, which can be configured in the user or workspace settings.
143
*/
144
WINDOW,
145
/**
146
* Resource specific configuration, which can be configured in the user, workspace or folder settings.
147
*/
148
RESOURCE,
149
/**
150
* Resource specific configuration that can be configured in language specific settings
151
*/
152
LANGUAGE_OVERRIDABLE,
153
/**
154
* Machine specific configuration that can also be configured in workspace or folder settings.
155
*/
156
MACHINE_OVERRIDABLE,
157
}
158
159
160
export interface IConfigurationPropertySchema extends IJSONSchema {
161
162
scope?: ConfigurationScope;
163
164
/**
165
* When restricted, value of this configuration will be read only from trusted sources.
166
* For eg., If the workspace is not trusted, then the value of this configuration is not read from workspace settings file.
167
*/
168
restricted?: boolean;
169
170
/**
171
* When `false` this property is excluded from the registry. Default is to include.
172
*/
173
included?: boolean;
174
175
/**
176
* List of tags associated to the property.
177
* - A tag can be used for filtering
178
* - Use `experimental` tag for marking the setting as experimental.
179
*/
180
tags?: string[];
181
182
/**
183
* When enabled this setting is ignored during sync and user can override this.
184
*/
185
ignoreSync?: boolean;
186
187
/**
188
* When enabled this setting is ignored during sync and user cannot override this.
189
*/
190
disallowSyncIgnore?: boolean;
191
192
/**
193
* Disallow extensions to contribute configuration default value for this setting.
194
*/
195
disallowConfigurationDefault?: boolean;
196
197
/**
198
* Labels for enumeration items
199
*/
200
enumItemLabels?: string[];
201
202
/**
203
* When specified, controls the presentation format of string settings.
204
* Otherwise, the presentation format defaults to `singleline`.
205
*/
206
editPresentation?: EditPresentationTypes;
207
208
/**
209
* When specified, gives an order number for the setting
210
* within the settings editor. Otherwise, the setting is placed at the end.
211
*/
212
order?: number;
213
214
/**
215
* When specified, this setting's value can always be overwritten by
216
* a system-wide policy.
217
*/
218
policy?: IPolicy;
219
220
/**
221
* When specified, this setting's default value can always be overwritten by
222
* an experiment.
223
*/
224
experiment?: {
225
/**
226
* The mode of the experiment.
227
* - `startup`: The setting value is updated to the experiment value only on startup.
228
* - `auto`: The setting value is updated to the experiment value automatically (whenever the experiment value changes).
229
*/
230
mode: 'startup' | 'auto';
231
232
/**
233
* The name of the experiment. By default, this is `config.${settingId}`
234
*/
235
name?: string;
236
};
237
}
238
239
export interface IExtensionInfo {
240
id: string;
241
displayName?: string;
242
}
243
244
export interface IConfigurationNode {
245
id?: string;
246
order?: number;
247
type?: string | string[];
248
title?: string;
249
description?: string;
250
properties?: IStringDictionary<IConfigurationPropertySchema>;
251
allOf?: IConfigurationNode[];
252
scope?: ConfigurationScope;
253
extensionInfo?: IExtensionInfo;
254
restrictedProperties?: string[];
255
}
256
257
export type ConfigurationDefaultValueSource = IExtensionInfo | Map<string, IExtensionInfo>;
258
259
export interface IConfigurationDefaults {
260
overrides: IStringDictionary<any>;
261
source?: IExtensionInfo;
262
}
263
264
export type IRegisteredConfigurationPropertySchema = IConfigurationPropertySchema & {
265
defaultDefaultValue?: any;
266
source?: IExtensionInfo; // Source of the Property
267
defaultValueSource?: ConfigurationDefaultValueSource; // Source of the Default Value
268
};
269
270
export interface IConfigurationDefaultOverride {
271
readonly value: any;
272
readonly source?: IExtensionInfo; // Source of the default override
273
}
274
275
export interface IConfigurationDefaultOverrideValue {
276
readonly value: any;
277
readonly source?: ConfigurationDefaultValueSource;
278
}
279
280
export const allSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
281
export const applicationSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
282
export const applicationMachineSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
283
export const machineSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
284
export const machineOverridableSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
285
export const windowSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
286
export const resourceSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
287
288
export const resourceLanguageSettingsSchemaId = 'vscode://schemas/settings/resourceLanguage';
289
export const configurationDefaultsSchemaId = 'vscode://schemas/settings/configurationDefaults';
290
291
const contributionRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
292
293
class ConfigurationRegistry extends Disposable implements IConfigurationRegistry {
294
295
private readonly registeredConfigurationDefaults: IConfigurationDefaults[] = [];
296
private readonly configurationDefaultsOverrides: Map<string, { configurationDefaultOverrides: IConfigurationDefaultOverride[]; configurationDefaultOverrideValue?: IConfigurationDefaultOverrideValue }>;
297
private readonly defaultLanguageConfigurationOverridesNode: IConfigurationNode;
298
private readonly configurationContributors: IConfigurationNode[];
299
private readonly configurationProperties: IStringDictionary<IRegisteredConfigurationPropertySchema>;
300
private readonly policyConfigurations: Map<PolicyName, string>;
301
private readonly excludedConfigurationProperties: IStringDictionary<IRegisteredConfigurationPropertySchema>;
302
private readonly resourceLanguageSettingsSchema: IJSONSchema;
303
private readonly overrideIdentifiers = new Set<string>();
304
305
private readonly _onDidSchemaChange = this._register(new Emitter<void>());
306
readonly onDidSchemaChange: Event<void> = this._onDidSchemaChange.event;
307
308
private readonly _onDidUpdateConfiguration = this._register(new Emitter<{ properties: ReadonlySet<string>; defaultsOverrides?: boolean }>());
309
readonly onDidUpdateConfiguration = this._onDidUpdateConfiguration.event;
310
311
constructor() {
312
super();
313
this.configurationDefaultsOverrides = new Map();
314
this.defaultLanguageConfigurationOverridesNode = {
315
id: 'defaultOverrides',
316
title: nls.localize('defaultLanguageConfigurationOverrides.title', "Default Language Configuration Overrides"),
317
properties: {}
318
};
319
this.configurationContributors = [this.defaultLanguageConfigurationOverridesNode];
320
this.resourceLanguageSettingsSchema = {
321
properties: {},
322
patternProperties: {},
323
additionalProperties: true,
324
allowTrailingCommas: true,
325
allowComments: true
326
};
327
this.configurationProperties = {};
328
this.policyConfigurations = new Map<PolicyName, string>();
329
this.excludedConfigurationProperties = {};
330
331
contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema);
332
this.registerOverridePropertyPatternKey();
333
}
334
335
public registerConfiguration(configuration: IConfigurationNode, validate: boolean = true): IConfigurationNode {
336
this.registerConfigurations([configuration], validate);
337
return configuration;
338
}
339
340
public registerConfigurations(configurations: IConfigurationNode[], validate: boolean = true): void {
341
const properties = new Set<string>();
342
this.doRegisterConfigurations(configurations, validate, properties);
343
344
contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema);
345
this._onDidSchemaChange.fire();
346
this._onDidUpdateConfiguration.fire({ properties });
347
}
348
349
public deregisterConfigurations(configurations: IConfigurationNode[]): void {
350
const properties = new Set<string>();
351
this.doDeregisterConfigurations(configurations, properties);
352
353
contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema);
354
this._onDidSchemaChange.fire();
355
this._onDidUpdateConfiguration.fire({ properties });
356
}
357
358
public updateConfigurations({ add, remove }: { add: IConfigurationNode[]; remove: IConfigurationNode[] }): void {
359
const properties = new Set<string>();
360
this.doDeregisterConfigurations(remove, properties);
361
this.doRegisterConfigurations(add, false, properties);
362
363
contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema);
364
this._onDidSchemaChange.fire();
365
this._onDidUpdateConfiguration.fire({ properties });
366
}
367
368
public registerDefaultConfigurations(configurationDefaults: IConfigurationDefaults[]): void {
369
const properties = new Set<string>();
370
this.doRegisterDefaultConfigurations(configurationDefaults, properties);
371
this._onDidSchemaChange.fire();
372
this._onDidUpdateConfiguration.fire({ properties, defaultsOverrides: true });
373
}
374
375
private doRegisterDefaultConfigurations(configurationDefaults: IConfigurationDefaults[], bucket: Set<string>) {
376
377
this.registeredConfigurationDefaults.push(...configurationDefaults);
378
379
const overrideIdentifiers: string[] = [];
380
381
for (const { overrides, source } of configurationDefaults) {
382
for (const key in overrides) {
383
bucket.add(key);
384
385
const configurationDefaultOverridesForKey = this.configurationDefaultsOverrides.get(key)
386
?? this.configurationDefaultsOverrides.set(key, { configurationDefaultOverrides: [] }).get(key)!;
387
388
const value = overrides[key];
389
configurationDefaultOverridesForKey.configurationDefaultOverrides.push({ value, source });
390
391
// Configuration defaults for Override Identifiers
392
if (OVERRIDE_PROPERTY_REGEX.test(key)) {
393
const newDefaultOverride = this.mergeDefaultConfigurationsForOverrideIdentifier(key, value, source, configurationDefaultOverridesForKey.configurationDefaultOverrideValue);
394
if (!newDefaultOverride) {
395
continue;
396
}
397
398
configurationDefaultOverridesForKey.configurationDefaultOverrideValue = newDefaultOverride;
399
this.updateDefaultOverrideProperty(key, newDefaultOverride, source);
400
overrideIdentifiers.push(...overrideIdentifiersFromKey(key));
401
}
402
403
// Configuration defaults for Configuration Properties
404
else {
405
const newDefaultOverride = this.mergeDefaultConfigurationsForConfigurationProperty(key, value, source, configurationDefaultOverridesForKey.configurationDefaultOverrideValue);
406
if (!newDefaultOverride) {
407
continue;
408
}
409
410
configurationDefaultOverridesForKey.configurationDefaultOverrideValue = newDefaultOverride;
411
const property = this.configurationProperties[key];
412
if (property) {
413
this.updatePropertyDefaultValue(key, property);
414
this.updateSchema(key, property);
415
}
416
}
417
418
}
419
}
420
421
this.doRegisterOverrideIdentifiers(overrideIdentifiers);
422
}
423
424
public deregisterDefaultConfigurations(defaultConfigurations: IConfigurationDefaults[]): void {
425
const properties = new Set<string>();
426
this.doDeregisterDefaultConfigurations(defaultConfigurations, properties);
427
this._onDidSchemaChange.fire();
428
this._onDidUpdateConfiguration.fire({ properties, defaultsOverrides: true });
429
}
430
431
private doDeregisterDefaultConfigurations(defaultConfigurations: IConfigurationDefaults[], bucket: Set<string>): void {
432
for (const defaultConfiguration of defaultConfigurations) {
433
const index = this.registeredConfigurationDefaults.indexOf(defaultConfiguration);
434
if (index !== -1) {
435
this.registeredConfigurationDefaults.splice(index, 1);
436
}
437
}
438
439
for (const { overrides, source } of defaultConfigurations) {
440
for (const key in overrides) {
441
const configurationDefaultOverridesForKey = this.configurationDefaultsOverrides.get(key);
442
if (!configurationDefaultOverridesForKey) {
443
continue;
444
}
445
446
const index = configurationDefaultOverridesForKey.configurationDefaultOverrides
447
.findIndex(configurationDefaultOverride => source ? configurationDefaultOverride.source?.id === source.id : configurationDefaultOverride.value === overrides[key]);
448
if (index === -1) {
449
continue;
450
}
451
452
configurationDefaultOverridesForKey.configurationDefaultOverrides.splice(index, 1);
453
if (configurationDefaultOverridesForKey.configurationDefaultOverrides.length === 0) {
454
this.configurationDefaultsOverrides.delete(key);
455
}
456
457
if (OVERRIDE_PROPERTY_REGEX.test(key)) {
458
let configurationDefaultOverrideValue: IConfigurationDefaultOverrideValue | undefined;
459
for (const configurationDefaultOverride of configurationDefaultOverridesForKey.configurationDefaultOverrides) {
460
configurationDefaultOverrideValue = this.mergeDefaultConfigurationsForOverrideIdentifier(key, configurationDefaultOverride.value, configurationDefaultOverride.source, configurationDefaultOverrideValue);
461
}
462
if (configurationDefaultOverrideValue && !types.isEmptyObject(configurationDefaultOverrideValue.value)) {
463
configurationDefaultOverridesForKey.configurationDefaultOverrideValue = configurationDefaultOverrideValue;
464
this.updateDefaultOverrideProperty(key, configurationDefaultOverrideValue, source);
465
} else {
466
this.configurationDefaultsOverrides.delete(key);
467
delete this.configurationProperties[key];
468
delete this.defaultLanguageConfigurationOverridesNode.properties![key];
469
}
470
} else {
471
let configurationDefaultOverrideValue: IConfigurationDefaultOverrideValue | undefined;
472
for (const configurationDefaultOverride of configurationDefaultOverridesForKey.configurationDefaultOverrides) {
473
configurationDefaultOverrideValue = this.mergeDefaultConfigurationsForConfigurationProperty(key, configurationDefaultOverride.value, configurationDefaultOverride.source, configurationDefaultOverrideValue);
474
}
475
configurationDefaultOverridesForKey.configurationDefaultOverrideValue = configurationDefaultOverrideValue;
476
const property = this.configurationProperties[key];
477
if (property) {
478
this.updatePropertyDefaultValue(key, property);
479
this.updateSchema(key, property);
480
}
481
}
482
bucket.add(key);
483
}
484
}
485
this.updateOverridePropertyPatternKey();
486
}
487
488
private updateDefaultOverrideProperty(key: string, newDefaultOverride: IConfigurationDefaultOverrideValue, source: IExtensionInfo | undefined): void {
489
const property: IRegisteredConfigurationPropertySchema = {
490
type: 'object',
491
default: newDefaultOverride.value,
492
description: nls.localize('defaultLanguageConfiguration.description', "Configure settings to be overridden for {0}.", getLanguageTagSettingPlainKey(key)),
493
$ref: resourceLanguageSettingsSchemaId,
494
defaultDefaultValue: newDefaultOverride.value,
495
source,
496
defaultValueSource: source
497
};
498
this.configurationProperties[key] = property;
499
this.defaultLanguageConfigurationOverridesNode.properties![key] = property;
500
}
501
502
private mergeDefaultConfigurationsForOverrideIdentifier(overrideIdentifier: string, configurationValueObject: IStringDictionary<any>, valueSource: IExtensionInfo | undefined, existingDefaultOverride: IConfigurationDefaultOverrideValue | undefined): IConfigurationDefaultOverrideValue | undefined {
503
const defaultValue = existingDefaultOverride?.value || {};
504
const source = existingDefaultOverride?.source ?? new Map<string, IExtensionInfo>();
505
506
// This should not happen
507
if (!(source instanceof Map)) {
508
console.error('objectConfigurationSources is not a Map');
509
return undefined;
510
}
511
512
for (const propertyKey of Object.keys(configurationValueObject)) {
513
const propertyDefaultValue = configurationValueObject[propertyKey];
514
515
const isObjectSetting = types.isObject(propertyDefaultValue) &&
516
(types.isUndefined(defaultValue[propertyKey]) || types.isObject(defaultValue[propertyKey]));
517
518
// If the default value is an object, merge the objects and store the source of each keys
519
if (isObjectSetting) {
520
defaultValue[propertyKey] = { ...(defaultValue[propertyKey] ?? {}), ...propertyDefaultValue };
521
// Track the source of each value in the object
522
if (valueSource) {
523
for (const objectKey in propertyDefaultValue) {
524
source.set(`${propertyKey}.${objectKey}`, valueSource);
525
}
526
}
527
}
528
529
// Primitive values are overridden
530
else {
531
defaultValue[propertyKey] = propertyDefaultValue;
532
if (valueSource) {
533
source.set(propertyKey, valueSource);
534
} else {
535
source.delete(propertyKey);
536
}
537
}
538
}
539
540
return { value: defaultValue, source };
541
}
542
543
private mergeDefaultConfigurationsForConfigurationProperty(propertyKey: string, value: any, valuesSource: IExtensionInfo | undefined, existingDefaultOverride: IConfigurationDefaultOverrideValue | undefined): IConfigurationDefaultOverrideValue | undefined {
544
const property = this.configurationProperties[propertyKey];
545
const existingDefaultValue = existingDefaultOverride?.value ?? property?.defaultDefaultValue;
546
let source: ConfigurationDefaultValueSource | undefined = valuesSource;
547
548
const isObjectSetting = types.isObject(value) &&
549
(
550
property !== undefined && property.type === 'object' ||
551
property === undefined && (types.isUndefined(existingDefaultValue) || types.isObject(existingDefaultValue))
552
);
553
554
// If the default value is an object, merge the objects and store the source of each keys
555
if (isObjectSetting) {
556
source = existingDefaultOverride?.source ?? new Map<string, IExtensionInfo>();
557
558
// This should not happen
559
if (!(source instanceof Map)) {
560
console.error('defaultValueSource is not a Map');
561
return undefined;
562
}
563
564
for (const objectKey in value) {
565
if (valuesSource) {
566
source.set(`${propertyKey}.${objectKey}`, valuesSource);
567
}
568
}
569
value = { ...(types.isObject(existingDefaultValue) ? existingDefaultValue : {}), ...value };
570
}
571
572
return { value, source };
573
}
574
575
public deltaConfiguration(delta: IConfigurationDelta): void {
576
// defaults: remove
577
let defaultsOverrides = false;
578
const properties = new Set<string>();
579
if (delta.removedDefaults) {
580
this.doDeregisterDefaultConfigurations(delta.removedDefaults, properties);
581
defaultsOverrides = true;
582
}
583
// defaults: add
584
if (delta.addedDefaults) {
585
this.doRegisterDefaultConfigurations(delta.addedDefaults, properties);
586
defaultsOverrides = true;
587
}
588
// configurations: remove
589
if (delta.removedConfigurations) {
590
this.doDeregisterConfigurations(delta.removedConfigurations, properties);
591
}
592
// configurations: add
593
if (delta.addedConfigurations) {
594
this.doRegisterConfigurations(delta.addedConfigurations, false, properties);
595
}
596
this._onDidSchemaChange.fire();
597
this._onDidUpdateConfiguration.fire({ properties, defaultsOverrides });
598
}
599
600
public notifyConfigurationSchemaUpdated(...configurations: IConfigurationNode[]) {
601
this._onDidSchemaChange.fire();
602
}
603
604
public registerOverrideIdentifiers(overrideIdentifiers: string[]): void {
605
this.doRegisterOverrideIdentifiers(overrideIdentifiers);
606
this._onDidSchemaChange.fire();
607
}
608
609
private doRegisterOverrideIdentifiers(overrideIdentifiers: string[]) {
610
for (const overrideIdentifier of overrideIdentifiers) {
611
this.overrideIdentifiers.add(overrideIdentifier);
612
}
613
this.updateOverridePropertyPatternKey();
614
}
615
616
private doRegisterConfigurations(configurations: IConfigurationNode[], validate: boolean, bucket: Set<string>): void {
617
618
configurations.forEach(configuration => {
619
620
this.validateAndRegisterProperties(configuration, validate, configuration.extensionInfo, configuration.restrictedProperties, undefined, bucket);
621
622
this.configurationContributors.push(configuration);
623
this.registerJSONConfiguration(configuration);
624
});
625
}
626
627
private doDeregisterConfigurations(configurations: IConfigurationNode[], bucket: Set<string>): void {
628
629
const deregisterConfiguration = (configuration: IConfigurationNode) => {
630
if (configuration.properties) {
631
for (const key in configuration.properties) {
632
bucket.add(key);
633
const property = this.configurationProperties[key];
634
if (property?.policy?.name) {
635
this.policyConfigurations.delete(property.policy.name);
636
}
637
delete this.configurationProperties[key];
638
this.removeFromSchema(key, configuration.properties[key]);
639
}
640
}
641
configuration.allOf?.forEach(node => deregisterConfiguration(node));
642
};
643
for (const configuration of configurations) {
644
deregisterConfiguration(configuration);
645
const index = this.configurationContributors.indexOf(configuration);
646
if (index !== -1) {
647
this.configurationContributors.splice(index, 1);
648
}
649
}
650
}
651
652
private validateAndRegisterProperties(configuration: IConfigurationNode, validate: boolean = true, extensionInfo: IExtensionInfo | undefined, restrictedProperties: string[] | undefined, scope: ConfigurationScope = ConfigurationScope.WINDOW, bucket: Set<string>): void {
653
scope = types.isUndefinedOrNull(configuration.scope) ? scope : configuration.scope;
654
const properties = configuration.properties;
655
if (properties) {
656
for (const key in properties) {
657
const property: IRegisteredConfigurationPropertySchema = properties[key];
658
if (validate && validateProperty(key, property)) {
659
delete properties[key];
660
continue;
661
}
662
663
property.source = extensionInfo;
664
665
// update default value
666
property.defaultDefaultValue = properties[key].default;
667
this.updatePropertyDefaultValue(key, property);
668
669
// update scope
670
if (OVERRIDE_PROPERTY_REGEX.test(key)) {
671
property.scope = undefined; // No scope for overridable properties `[${identifier}]`
672
} else {
673
property.scope = types.isUndefinedOrNull(property.scope) ? scope : property.scope;
674
property.restricted = types.isUndefinedOrNull(property.restricted) ? !!restrictedProperties?.includes(key) : property.restricted;
675
}
676
677
if (property.experiment) {
678
if (!property.tags?.some(tag => tag.toLowerCase() === 'onexp')) {
679
property.tags = property.tags ?? [];
680
property.tags.push('onExP');
681
}
682
} else if (property.tags?.some(tag => tag.toLowerCase() === 'onexp')) {
683
console.error(`Invalid tag 'onExP' found for property '${key}'. Please use 'experiment' property instead.`);
684
property.experiment = { mode: 'startup' };
685
}
686
687
const excluded = properties[key].hasOwnProperty('included') && !properties[key].included;
688
const policyName = properties[key].policy?.name;
689
690
if (excluded) {
691
this.excludedConfigurationProperties[key] = properties[key];
692
if (policyName) {
693
this.policyConfigurations.set(policyName, key);
694
bucket.add(key);
695
}
696
delete properties[key];
697
} else {
698
bucket.add(key);
699
if (policyName) {
700
this.policyConfigurations.set(policyName, key);
701
}
702
this.configurationProperties[key] = properties[key];
703
if (!properties[key].deprecationMessage && properties[key].markdownDeprecationMessage) {
704
// If not set, default deprecationMessage to the markdown source
705
properties[key].deprecationMessage = properties[key].markdownDeprecationMessage;
706
}
707
}
708
709
710
}
711
}
712
const subNodes = configuration.allOf;
713
if (subNodes) {
714
for (const node of subNodes) {
715
this.validateAndRegisterProperties(node, validate, extensionInfo, restrictedProperties, scope, bucket);
716
}
717
}
718
}
719
720
// TODO: @sandy081 - Remove this method and include required info in getConfigurationProperties
721
getConfigurations(): IConfigurationNode[] {
722
return this.configurationContributors;
723
}
724
725
getConfigurationProperties(): IStringDictionary<IRegisteredConfigurationPropertySchema> {
726
return this.configurationProperties;
727
}
728
729
getPolicyConfigurations(): Map<PolicyName, string> {
730
return this.policyConfigurations;
731
}
732
733
getExcludedConfigurationProperties(): IStringDictionary<IRegisteredConfigurationPropertySchema> {
734
return this.excludedConfigurationProperties;
735
}
736
737
getRegisteredDefaultConfigurations(): IConfigurationDefaults[] {
738
return [...this.registeredConfigurationDefaults];
739
}
740
741
getConfigurationDefaultsOverrides(): Map<string, IConfigurationDefaultOverrideValue> {
742
const configurationDefaultsOverrides = new Map<string, IConfigurationDefaultOverrideValue>();
743
for (const [key, value] of this.configurationDefaultsOverrides) {
744
if (value.configurationDefaultOverrideValue) {
745
configurationDefaultsOverrides.set(key, value.configurationDefaultOverrideValue);
746
}
747
}
748
return configurationDefaultsOverrides;
749
}
750
751
private registerJSONConfiguration(configuration: IConfigurationNode) {
752
const register = (configuration: IConfigurationNode) => {
753
const properties = configuration.properties;
754
if (properties) {
755
for (const key in properties) {
756
this.updateSchema(key, properties[key]);
757
}
758
}
759
const subNodes = configuration.allOf;
760
subNodes?.forEach(register);
761
};
762
register(configuration);
763
}
764
765
private updateSchema(key: string, property: IConfigurationPropertySchema): void {
766
allSettings.properties[key] = property;
767
switch (property.scope) {
768
case ConfigurationScope.APPLICATION:
769
applicationSettings.properties[key] = property;
770
break;
771
case ConfigurationScope.MACHINE:
772
machineSettings.properties[key] = property;
773
break;
774
case ConfigurationScope.APPLICATION_MACHINE:
775
applicationMachineSettings.properties[key] = property;
776
break;
777
case ConfigurationScope.MACHINE_OVERRIDABLE:
778
machineOverridableSettings.properties[key] = property;
779
break;
780
case ConfigurationScope.WINDOW:
781
windowSettings.properties[key] = property;
782
break;
783
case ConfigurationScope.RESOURCE:
784
resourceSettings.properties[key] = property;
785
break;
786
case ConfigurationScope.LANGUAGE_OVERRIDABLE:
787
resourceSettings.properties[key] = property;
788
this.resourceLanguageSettingsSchema.properties![key] = property;
789
break;
790
}
791
}
792
793
private removeFromSchema(key: string, property: IConfigurationPropertySchema): void {
794
delete allSettings.properties[key];
795
switch (property.scope) {
796
case ConfigurationScope.APPLICATION:
797
delete applicationSettings.properties[key];
798
break;
799
case ConfigurationScope.MACHINE:
800
delete machineSettings.properties[key];
801
break;
802
case ConfigurationScope.APPLICATION_MACHINE:
803
delete applicationMachineSettings.properties[key];
804
break;
805
case ConfigurationScope.MACHINE_OVERRIDABLE:
806
delete machineOverridableSettings.properties[key];
807
break;
808
case ConfigurationScope.WINDOW:
809
delete windowSettings.properties[key];
810
break;
811
case ConfigurationScope.RESOURCE:
812
case ConfigurationScope.LANGUAGE_OVERRIDABLE:
813
delete resourceSettings.properties[key];
814
delete this.resourceLanguageSettingsSchema.properties![key];
815
break;
816
}
817
}
818
819
private updateOverridePropertyPatternKey(): void {
820
for (const overrideIdentifier of this.overrideIdentifiers.values()) {
821
const overrideIdentifierProperty = `[${overrideIdentifier}]`;
822
const resourceLanguagePropertiesSchema: IJSONSchema = {
823
type: 'object',
824
description: nls.localize('overrideSettings.defaultDescription', "Configure editor settings to be overridden for a language."),
825
errorMessage: nls.localize('overrideSettings.errorMessage', "This setting does not support per-language configuration."),
826
$ref: resourceLanguageSettingsSchemaId,
827
};
828
this.updatePropertyDefaultValue(overrideIdentifierProperty, resourceLanguagePropertiesSchema);
829
allSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;
830
applicationSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;
831
applicationMachineSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;
832
machineSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;
833
machineOverridableSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;
834
windowSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;
835
resourceSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;
836
}
837
}
838
839
private registerOverridePropertyPatternKey(): void {
840
const resourceLanguagePropertiesSchema: IJSONSchema = {
841
type: 'object',
842
description: nls.localize('overrideSettings.defaultDescription', "Configure editor settings to be overridden for a language."),
843
errorMessage: nls.localize('overrideSettings.errorMessage', "This setting does not support per-language configuration."),
844
$ref: resourceLanguageSettingsSchemaId,
845
};
846
allSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
847
applicationSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
848
applicationMachineSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
849
machineSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
850
machineOverridableSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
851
windowSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
852
resourceSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
853
this._onDidSchemaChange.fire();
854
}
855
856
private updatePropertyDefaultValue(key: string, property: IRegisteredConfigurationPropertySchema): void {
857
const configurationdefaultOverride = this.configurationDefaultsOverrides.get(key)?.configurationDefaultOverrideValue;
858
let defaultValue = undefined;
859
let defaultSource = undefined;
860
if (configurationdefaultOverride
861
&& (!property.disallowConfigurationDefault || !configurationdefaultOverride.source) // Prevent overriding the default value if the property is disallowed to be overridden by configuration defaults from extensions
862
) {
863
defaultValue = configurationdefaultOverride.value;
864
defaultSource = configurationdefaultOverride.source;
865
}
866
if (types.isUndefined(defaultValue)) {
867
defaultValue = property.defaultDefaultValue;
868
defaultSource = undefined;
869
}
870
if (types.isUndefined(defaultValue)) {
871
defaultValue = getDefaultValue(property.type);
872
}
873
property.default = defaultValue;
874
property.defaultValueSource = defaultSource;
875
}
876
}
877
878
const OVERRIDE_IDENTIFIER_PATTERN = `\\[([^\\]]+)\\]`;
879
const OVERRIDE_IDENTIFIER_REGEX = new RegExp(OVERRIDE_IDENTIFIER_PATTERN, 'g');
880
export const OVERRIDE_PROPERTY_PATTERN = `^(${OVERRIDE_IDENTIFIER_PATTERN})+$`;
881
export const OVERRIDE_PROPERTY_REGEX = new RegExp(OVERRIDE_PROPERTY_PATTERN);
882
883
export function overrideIdentifiersFromKey(key: string): string[] {
884
const identifiers: string[] = [];
885
if (OVERRIDE_PROPERTY_REGEX.test(key)) {
886
let matches = OVERRIDE_IDENTIFIER_REGEX.exec(key);
887
while (matches?.length) {
888
const identifier = matches[1].trim();
889
if (identifier) {
890
identifiers.push(identifier);
891
}
892
matches = OVERRIDE_IDENTIFIER_REGEX.exec(key);
893
}
894
}
895
return distinct(identifiers);
896
}
897
898
export function keyFromOverrideIdentifiers(overrideIdentifiers: string[]): string {
899
return overrideIdentifiers.reduce((result, overrideIdentifier) => `${result}[${overrideIdentifier}]`, '');
900
}
901
902
export function getDefaultValue(type: string | string[] | undefined) {
903
const t = Array.isArray(type) ? (<string[]>type)[0] : <string>type;
904
switch (t) {
905
case 'boolean':
906
return false;
907
case 'integer':
908
case 'number':
909
return 0;
910
case 'string':
911
return '';
912
case 'array':
913
return [];
914
case 'object':
915
return {};
916
default:
917
return null;
918
}
919
}
920
921
const configurationRegistry = new ConfigurationRegistry();
922
Registry.add(Extensions.Configuration, configurationRegistry);
923
924
export function validateProperty(property: string, schema: IRegisteredConfigurationPropertySchema): string | null {
925
if (!property.trim()) {
926
return nls.localize('config.property.empty', "Cannot register an empty property");
927
}
928
if (OVERRIDE_PROPERTY_REGEX.test(property)) {
929
return nls.localize('config.property.languageDefault', "Cannot register '{0}'. This matches property pattern '\\\\[.*\\\\]$' for describing language specific editor settings. Use 'configurationDefaults' contribution.", property);
930
}
931
if (configurationRegistry.getConfigurationProperties()[property] !== undefined) {
932
return nls.localize('config.property.duplicate', "Cannot register '{0}'. This property is already registered.", property);
933
}
934
if (schema.policy?.name && configurationRegistry.getPolicyConfigurations().get(schema.policy?.name) !== undefined) {
935
return 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));
936
}
937
return null;
938
}
939
940
export function getScopes(): [string, ConfigurationScope | undefined][] {
941
const scopes: [string, ConfigurationScope | undefined][] = [];
942
const configurationProperties = configurationRegistry.getConfigurationProperties();
943
for (const key of Object.keys(configurationProperties)) {
944
scopes.push([key, configurationProperties[key].scope]);
945
}
946
scopes.push(['launch', ConfigurationScope.RESOURCE]);
947
scopes.push(['task', ConfigurationScope.RESOURCE]);
948
return scopes;
949
}
950
951
export function getAllConfigurationProperties(configurationNode: IConfigurationNode[]): IStringDictionary<IRegisteredConfigurationPropertySchema> {
952
const result: IStringDictionary<IRegisteredConfigurationPropertySchema> = {};
953
for (const configuration of configurationNode) {
954
const properties = configuration.properties;
955
if (types.isObject(properties)) {
956
for (const key in properties) {
957
result[key] = properties[key];
958
}
959
}
960
if (configuration.allOf) {
961
Object.assign(result, getAllConfigurationProperties(configuration.allOf));
962
}
963
}
964
return result;
965
}
966
967
export function parseScope(scope: string): ConfigurationScope {
968
switch (scope) {
969
case 'application':
970
return ConfigurationScope.APPLICATION;
971
case 'machine':
972
return ConfigurationScope.MACHINE;
973
case 'resource':
974
return ConfigurationScope.RESOURCE;
975
case 'machine-overridable':
976
return ConfigurationScope.MACHINE_OVERRIDABLE;
977
case 'language-overridable':
978
return ConfigurationScope.LANGUAGE_OVERRIDABLE;
979
default:
980
return ConfigurationScope.WINDOW;
981
}
982
}
983
984