Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/configuration/common/configurationModels.ts
5262 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 * as arrays from '../../../base/common/arrays.js';
7
import { IStringDictionary } from '../../../base/common/collections.js';
8
import { Emitter, Event } from '../../../base/common/event.js';
9
import * as json from '../../../base/common/json.js';
10
import { Disposable } from '../../../base/common/lifecycle.js';
11
import { getOrSet, ResourceMap } from '../../../base/common/map.js';
12
import * as objects from '../../../base/common/objects.js';
13
import { IExtUri } from '../../../base/common/resources.js';
14
import * as types from '../../../base/common/types.js';
15
import { URI, UriComponents } from '../../../base/common/uri.js';
16
import { addToValueTree, ConfigurationTarget, getConfigurationValue, IConfigurationChange, IConfigurationChangeEvent, IConfigurationCompareResult, IConfigurationData, IConfigurationModel, IConfigurationOverrides, IConfigurationUpdateOverrides, IConfigurationValue, IInspectValue, IOverrides, removeFromValueTree, toValuesTree } from './configuration.js';
17
import { ConfigurationScope, Extensions, IConfigurationPropertySchema, IConfigurationRegistry, overrideIdentifiersFromKey, OVERRIDE_PROPERTY_REGEX, IRegisteredConfigurationPropertySchema } from './configurationRegistry.js';
18
import { FileOperation, IFileService } from '../../files/common/files.js';
19
import { ILogService } from '../../log/common/log.js';
20
import { Registry } from '../../registry/common/platform.js';
21
import { Workspace } from '../../workspace/common/workspace.js';
22
23
function freeze<T>(data: T): T {
24
return Object.isFrozen(data) ? data : objects.deepFreeze(data);
25
}
26
27
type InspectValue<V> = IInspectValue<V> & { merged?: V };
28
29
export class ConfigurationModel implements IConfigurationModel {
30
31
static createEmptyModel(logService: ILogService): ConfigurationModel {
32
return new ConfigurationModel({}, [], [], undefined, logService);
33
}
34
35
private readonly overrideConfigurations = new Map<string, ConfigurationModel>();
36
37
constructor(
38
private readonly _contents: IStringDictionary<unknown>,
39
private readonly _keys: string[],
40
private readonly _overrides: IOverrides[],
41
private readonly _raw: IStringDictionary<unknown> | ReadonlyArray<IStringDictionary<unknown> | ConfigurationModel> | undefined,
42
private readonly logService: ILogService
43
) {
44
}
45
46
private _rawConfiguration: ConfigurationModel | undefined;
47
get rawConfiguration(): ConfigurationModel {
48
if (!this._rawConfiguration) {
49
if (this._raw) {
50
const rawConfigurationModels = (Array.isArray(this._raw) ? this._raw : [this._raw]).map(raw => {
51
if (raw instanceof ConfigurationModel) {
52
return raw;
53
}
54
const parser = new ConfigurationModelParser('', this.logService);
55
parser.parseRaw(raw);
56
return parser.configurationModel;
57
});
58
this._rawConfiguration = rawConfigurationModels.reduce((previous, current) => current === previous ? current : previous.merge(current), rawConfigurationModels[0]);
59
} else {
60
// raw is same as current
61
this._rawConfiguration = this;
62
}
63
}
64
return this._rawConfiguration;
65
}
66
67
get contents(): IStringDictionary<unknown> {
68
return this._contents;
69
}
70
71
get overrides(): IOverrides[] {
72
return this._overrides;
73
}
74
75
get keys(): string[] {
76
return this._keys;
77
}
78
79
get raw(): IStringDictionary<unknown> | IStringDictionary<unknown>[] | undefined {
80
if (!this._raw) {
81
return undefined;
82
}
83
if (Array.isArray(this._raw) && this._raw.every(raw => raw instanceof ConfigurationModel)) {
84
return undefined;
85
}
86
return this._raw as IStringDictionary<unknown> | IStringDictionary<unknown>[];
87
}
88
89
isEmpty(): boolean {
90
return this._keys.length === 0 && Object.keys(this._contents).length === 0 && this._overrides.length === 0;
91
}
92
93
getValue<V>(section: string | undefined): V | undefined {
94
return section ? getConfigurationValue<V>(this.contents, section) : this.contents as V;
95
}
96
97
inspect<V>(section: string | undefined, overrideIdentifier?: string | null): InspectValue<V> {
98
const that = this;
99
return {
100
get value() {
101
return freeze(that.rawConfiguration.getValue<V>(section));
102
},
103
get override() {
104
return overrideIdentifier ? freeze(that.rawConfiguration.getOverrideValue<V>(section, overrideIdentifier)) : undefined;
105
},
106
get merged() {
107
return freeze(overrideIdentifier ? that.rawConfiguration.override(overrideIdentifier).getValue<V>(section) : that.rawConfiguration.getValue<V>(section));
108
},
109
get overrides() {
110
const overrides: { readonly identifiers: string[]; readonly value: V }[] = [];
111
for (const { contents, identifiers, keys } of that.rawConfiguration.overrides) {
112
const value = new ConfigurationModel(contents, keys, [], undefined, that.logService).getValue<V>(section);
113
if (value !== undefined) {
114
overrides.push({ identifiers, value });
115
}
116
}
117
return overrides.length ? freeze(overrides) : undefined;
118
}
119
};
120
}
121
122
getOverrideValue<V>(section: string | undefined, overrideIdentifier: string): V | undefined {
123
const overrideContents = this.getContentsForOverrideIdentifer(overrideIdentifier);
124
return overrideContents
125
? section ? getConfigurationValue<V>(overrideContents, section) : overrideContents as V
126
: undefined;
127
}
128
129
getKeysForOverrideIdentifier(identifier: string): string[] {
130
const keys: string[] = [];
131
for (const override of this.overrides) {
132
if (override.identifiers.includes(identifier)) {
133
keys.push(...override.keys);
134
}
135
}
136
return arrays.distinct(keys);
137
}
138
139
getAllOverrideIdentifiers(): string[] {
140
const result: string[] = [];
141
for (const override of this.overrides) {
142
result.push(...override.identifiers);
143
}
144
return arrays.distinct(result);
145
}
146
147
override(identifier: string): ConfigurationModel {
148
let overrideConfigurationModel = this.overrideConfigurations.get(identifier);
149
if (!overrideConfigurationModel) {
150
overrideConfigurationModel = this.createOverrideConfigurationModel(identifier);
151
this.overrideConfigurations.set(identifier, overrideConfigurationModel);
152
}
153
return overrideConfigurationModel;
154
}
155
156
merge(...others: ConfigurationModel[]): ConfigurationModel {
157
const contents = objects.deepClone(this.contents);
158
const overrides = objects.deepClone(this.overrides);
159
const keys = [...this.keys];
160
const raws = this._raw ? Array.isArray(this._raw) ? [...this._raw] : [this._raw] : [this];
161
162
for (const other of others) {
163
raws.push(...(other._raw ? Array.isArray(other._raw) ? other._raw : [other._raw] : [other]));
164
if (other.isEmpty()) {
165
continue;
166
}
167
this.mergeContents(contents, other.contents);
168
169
for (const otherOverride of other.overrides) {
170
const [override] = overrides.filter(o => arrays.equals(o.identifiers, otherOverride.identifiers));
171
if (override) {
172
this.mergeContents(override.contents, otherOverride.contents);
173
override.keys.push(...otherOverride.keys);
174
override.keys = arrays.distinct(override.keys);
175
} else {
176
overrides.push(objects.deepClone(otherOverride));
177
}
178
}
179
for (const key of other.keys) {
180
if (keys.indexOf(key) === -1) {
181
keys.push(key);
182
}
183
}
184
}
185
return new ConfigurationModel(contents, keys, overrides, !raws.length || raws.every(raw => raw instanceof ConfigurationModel) ? undefined : raws, this.logService);
186
}
187
188
private createOverrideConfigurationModel(identifier: string): ConfigurationModel {
189
const overrideContents = this.getContentsForOverrideIdentifer(identifier);
190
191
if (!overrideContents || typeof overrideContents !== 'object' || !Object.keys(overrideContents).length) {
192
// If there are no valid overrides, return self
193
return this;
194
}
195
196
const contents: IStringDictionary<unknown> = {};
197
for (const key of arrays.distinct([...Object.keys(this.contents), ...Object.keys(overrideContents)])) {
198
199
let contentsForKey = this.contents[key];
200
const overrideContentsForKey = overrideContents[key];
201
202
// If there are override contents for the key, clone and merge otherwise use base contents
203
if (overrideContentsForKey) {
204
// Clone and merge only if base contents and override contents are of type object otherwise just override
205
if (typeof contentsForKey === 'object' && typeof overrideContentsForKey === 'object') {
206
contentsForKey = objects.deepClone(contentsForKey);
207
this.mergeContents(contentsForKey as IStringDictionary<unknown>, overrideContentsForKey as IStringDictionary<unknown>);
208
} else {
209
contentsForKey = overrideContentsForKey;
210
}
211
}
212
213
contents[key] = contentsForKey;
214
}
215
216
return new ConfigurationModel(contents, this.keys, this.overrides, undefined, this.logService);
217
}
218
219
private mergeContents(source: IStringDictionary<unknown>, target: IStringDictionary<unknown>): void {
220
for (const key of Object.keys(target)) {
221
if (key in source) {
222
if (types.isObject(source[key]) && types.isObject(target[key])) {
223
this.mergeContents(source[key] as IStringDictionary<unknown>, target[key] as IStringDictionary<unknown>);
224
continue;
225
}
226
}
227
source[key] = objects.deepClone(target[key]);
228
}
229
}
230
231
private getContentsForOverrideIdentifer(identifier: string): IStringDictionary<unknown> | null {
232
let contentsForIdentifierOnly: IStringDictionary<unknown> | null = null;
233
let contents: IStringDictionary<unknown> | null = null;
234
const mergeContents = (contentsToMerge: IStringDictionary<unknown> | null) => {
235
if (contentsToMerge) {
236
if (contents) {
237
this.mergeContents(contents, contentsToMerge);
238
} else {
239
contents = objects.deepClone(contentsToMerge);
240
}
241
}
242
};
243
for (const override of this.overrides) {
244
if (override.identifiers.length === 1 && override.identifiers[0] === identifier) {
245
contentsForIdentifierOnly = override.contents;
246
} else if (override.identifiers.includes(identifier)) {
247
mergeContents(override.contents);
248
}
249
}
250
// Merge contents of the identifier only at the end to take precedence.
251
mergeContents(contentsForIdentifierOnly);
252
return contents;
253
}
254
255
toJSON(): IConfigurationModel {
256
return {
257
contents: this.contents,
258
overrides: this.overrides,
259
keys: this.keys
260
};
261
}
262
263
// Update methods
264
265
public addValue(key: string, value: unknown): void {
266
this.updateValue(key, value, true);
267
}
268
269
public setValue(key: string, value: unknown): void {
270
this.updateValue(key, value, false);
271
}
272
273
public removeValue(key: string): void {
274
const index = this.keys.indexOf(key);
275
if (index === -1) {
276
return;
277
}
278
this.keys.splice(index, 1);
279
removeFromValueTree(this.contents, key);
280
if (OVERRIDE_PROPERTY_REGEX.test(key)) {
281
this.overrides.splice(this.overrides.findIndex(o => arrays.equals(o.identifiers, overrideIdentifiersFromKey(key))), 1);
282
}
283
}
284
285
private updateValue(key: string, value: unknown, add: boolean): void {
286
addToValueTree(this.contents, key, value, e => this.logService.error(e));
287
add = add || this.keys.indexOf(key) === -1;
288
if (add) {
289
this.keys.push(key);
290
}
291
if (OVERRIDE_PROPERTY_REGEX.test(key)) {
292
const overrideContents = this.contents[key] as IStringDictionary<unknown>;
293
const identifiers = overrideIdentifiersFromKey(key);
294
const override = {
295
identifiers,
296
keys: Object.keys(overrideContents),
297
contents: toValuesTree(overrideContents, message => this.logService.error(message)),
298
};
299
const index = this.overrides.findIndex(o => arrays.equals(o.identifiers, identifiers));
300
if (index !== -1) {
301
this.overrides[index] = override;
302
} else {
303
this.overrides.push(override);
304
}
305
}
306
}
307
}
308
309
export interface ConfigurationParseOptions {
310
skipUnregistered?: boolean;
311
scopes?: ConfigurationScope[];
312
skipRestricted?: boolean;
313
include?: string[];
314
exclude?: string[];
315
}
316
317
export class ConfigurationModelParser {
318
319
private _raw: IStringDictionary<unknown> | null = null;
320
private _configurationModel: ConfigurationModel | null = null;
321
private _restrictedConfigurations: string[] = [];
322
private _parseErrors: json.ParseError[] = [];
323
324
constructor(
325
protected readonly _name: string,
326
protected readonly logService: ILogService
327
) { }
328
329
get configurationModel(): ConfigurationModel {
330
return this._configurationModel || ConfigurationModel.createEmptyModel(this.logService);
331
}
332
333
get restrictedConfigurations(): string[] {
334
return this._restrictedConfigurations;
335
}
336
337
get errors(): json.ParseError[] {
338
return this._parseErrors;
339
}
340
341
public parse(content: string | null | undefined, options?: ConfigurationParseOptions): void {
342
if (!types.isUndefinedOrNull(content)) {
343
const raw = this.doParseContent(content);
344
this.parseRaw(raw, options);
345
}
346
}
347
348
public reparse(options: ConfigurationParseOptions): void {
349
if (this._raw) {
350
this.parseRaw(this._raw, options);
351
}
352
}
353
354
public parseRaw(raw: IStringDictionary<unknown>, options?: ConfigurationParseOptions): void {
355
this._raw = raw;
356
const { contents, keys, overrides, restricted, hasExcludedProperties } = this.doParseRaw(raw, options);
357
this._configurationModel = new ConfigurationModel(contents, keys, overrides, hasExcludedProperties ? [raw] : undefined /* raw has not changed */, this.logService);
358
this._restrictedConfigurations = restricted || [];
359
}
360
361
private doParseContent(content: string): IStringDictionary<unknown> {
362
let raw: IStringDictionary<unknown> = {};
363
let currentProperty: string | null = null;
364
let currentParent: unknown[] | IStringDictionary<unknown> = [];
365
const previousParents: (unknown[] | IStringDictionary<unknown>)[] = [];
366
const parseErrors: json.ParseError[] = [];
367
368
function onValue(value: unknown) {
369
if (Array.isArray(currentParent)) {
370
currentParent.push(value);
371
} else if (currentProperty !== null) {
372
currentParent[currentProperty] = value;
373
}
374
}
375
376
const visitor: json.JSONVisitor = {
377
onObjectBegin: () => {
378
const object = {};
379
onValue(object);
380
previousParents.push(currentParent);
381
currentParent = object;
382
currentProperty = null;
383
},
384
onObjectProperty: (name: string) => {
385
currentProperty = name;
386
},
387
onObjectEnd: () => {
388
currentParent = previousParents.pop()!;
389
},
390
onArrayBegin: () => {
391
const array: unknown[] = [];
392
onValue(array);
393
previousParents.push(currentParent);
394
currentParent = array;
395
currentProperty = null;
396
},
397
onArrayEnd: () => {
398
currentParent = previousParents.pop()!;
399
},
400
onLiteralValue: onValue,
401
onError: (error: json.ParseErrorCode, offset: number, length: number) => {
402
parseErrors.push({ error, offset, length });
403
}
404
};
405
if (content) {
406
try {
407
json.visit(content, visitor);
408
raw = (currentParent[0] as IStringDictionary<unknown>) || {};
409
} catch (e) {
410
this.logService.error(`Error while parsing settings file ${this._name}: ${e}`);
411
this._parseErrors = [e as json.ParseError];
412
}
413
}
414
415
return raw;
416
}
417
418
protected doParseRaw(raw: IStringDictionary<unknown>, options?: ConfigurationParseOptions): IConfigurationModel & { restricted?: string[]; hasExcludedProperties?: boolean } {
419
const registry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
420
const configurationProperties = registry.getConfigurationProperties();
421
const excludedConfigurationProperties = registry.getExcludedConfigurationProperties();
422
const filtered = this.filter(raw, configurationProperties, excludedConfigurationProperties, true, options);
423
raw = filtered.raw;
424
const contents = toValuesTree(raw, message => this.logService.error(`Conflict in settings file ${this._name}: ${message}`));
425
const keys = Object.keys(raw);
426
const overrides = this.toOverrides(raw, message => this.logService.error(`Conflict in settings file ${this._name}: ${message}`));
427
return { contents, keys, overrides, restricted: filtered.restricted, hasExcludedProperties: filtered.hasExcludedProperties };
428
}
429
430
private filter(properties: IStringDictionary<unknown>, configurationProperties: IStringDictionary<IRegisteredConfigurationPropertySchema>, excludedConfigurationProperties: IStringDictionary<IRegisteredConfigurationPropertySchema>, filterOverriddenProperties: boolean, options?: ConfigurationParseOptions): { raw: IStringDictionary<unknown>; restricted: string[]; hasExcludedProperties: boolean } {
431
let hasExcludedProperties = false;
432
if (!options?.scopes && !options?.skipRestricted && !options?.skipUnregistered && !options?.exclude?.length) {
433
return { raw: properties, restricted: [], hasExcludedProperties };
434
}
435
const raw: IStringDictionary<unknown> = {};
436
const restricted: string[] = [];
437
for (const key in properties) {
438
if (OVERRIDE_PROPERTY_REGEX.test(key) && filterOverriddenProperties) {
439
const result = this.filter(properties[key] as IStringDictionary<unknown>, configurationProperties, excludedConfigurationProperties, false, options);
440
raw[key] = result.raw;
441
hasExcludedProperties = hasExcludedProperties || result.hasExcludedProperties;
442
restricted.push(...result.restricted);
443
} else {
444
const propertySchema = configurationProperties[key];
445
if (propertySchema?.restricted) {
446
restricted.push(key);
447
}
448
if (this.shouldInclude(key, propertySchema, excludedConfigurationProperties, options)) {
449
raw[key] = properties[key];
450
} else {
451
hasExcludedProperties = true;
452
}
453
}
454
}
455
return { raw, restricted, hasExcludedProperties };
456
}
457
458
private shouldInclude(key: string, propertySchema: IConfigurationPropertySchema | undefined, excludedConfigurationProperties: IStringDictionary<IRegisteredConfigurationPropertySchema>, options: ConfigurationParseOptions): boolean {
459
if (options.exclude?.includes(key)) {
460
return false;
461
}
462
463
if (options.include?.includes(key)) {
464
return true;
465
}
466
467
if (options.skipRestricted && propertySchema?.restricted) {
468
return false;
469
}
470
471
if (options.skipUnregistered && !propertySchema) {
472
return false;
473
}
474
475
const schema = propertySchema ?? excludedConfigurationProperties[key];
476
const scope = schema ? typeof schema.scope !== 'undefined' ? schema.scope : ConfigurationScope.WINDOW : undefined;
477
if (scope === undefined || options.scopes === undefined) {
478
return true;
479
}
480
481
return options.scopes.includes(scope);
482
}
483
484
private toOverrides(raw: IStringDictionary<unknown>, conflictReporter: (message: string) => void): IOverrides[] {
485
const overrides: IOverrides[] = [];
486
for (const key of Object.keys(raw)) {
487
if (OVERRIDE_PROPERTY_REGEX.test(key)) {
488
const overrideRaw: IStringDictionary<unknown> = {};
489
const rawKey = raw[key] as IStringDictionary<unknown>;
490
for (const keyInOverrideRaw in rawKey) {
491
overrideRaw[keyInOverrideRaw] = rawKey[keyInOverrideRaw];
492
}
493
overrides.push({
494
identifiers: overrideIdentifiersFromKey(key),
495
keys: Object.keys(overrideRaw),
496
contents: toValuesTree(overrideRaw, conflictReporter)
497
});
498
}
499
}
500
return overrides;
501
}
502
503
}
504
505
export class UserSettings extends Disposable {
506
507
private readonly parser: ConfigurationModelParser;
508
protected readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
509
readonly onDidChange: Event<void> = this._onDidChange.event;
510
511
constructor(
512
private readonly userSettingsResource: URI,
513
protected parseOptions: ConfigurationParseOptions,
514
extUri: IExtUri,
515
private readonly fileService: IFileService,
516
private readonly logService: ILogService,
517
) {
518
super();
519
this.parser = new ConfigurationModelParser(this.userSettingsResource.toString(), logService);
520
this._register(this.fileService.watch(extUri.dirname(this.userSettingsResource)));
521
// Also listen to the resource incase the resource is a symlink - https://github.com/microsoft/vscode/issues/118134
522
this._register(this.fileService.watch(this.userSettingsResource));
523
this._register(Event.any(
524
Event.filter(this.fileService.onDidFilesChange, e => e.contains(this.userSettingsResource)),
525
Event.filter(this.fileService.onDidRunOperation, e => (e.isOperation(FileOperation.CREATE) || e.isOperation(FileOperation.COPY) || e.isOperation(FileOperation.DELETE) || e.isOperation(FileOperation.WRITE)) && extUri.isEqual(e.resource, userSettingsResource))
526
)(() => this._onDidChange.fire()));
527
}
528
529
async loadConfiguration(): Promise<ConfigurationModel> {
530
try {
531
const content = await this.fileService.readFile(this.userSettingsResource);
532
this.parser.parse(content.value.toString() || '{}', this.parseOptions);
533
return this.parser.configurationModel;
534
} catch (e) {
535
return ConfigurationModel.createEmptyModel(this.logService);
536
}
537
}
538
539
reparse(parseOptions?: ConfigurationParseOptions): ConfigurationModel {
540
if (parseOptions) {
541
this.parseOptions = parseOptions;
542
}
543
this.parser.reparse(this.parseOptions);
544
return this.parser.configurationModel;
545
}
546
547
getRestrictedSettings(): string[] {
548
return this.parser.restrictedConfigurations;
549
}
550
}
551
552
class ConfigurationInspectValue<V> implements IConfigurationValue<V> {
553
554
constructor(
555
private readonly key: string,
556
private readonly overrides: IConfigurationOverrides,
557
private readonly _value: V | undefined,
558
readonly overrideIdentifiers: string[] | undefined,
559
private readonly defaultConfiguration: ConfigurationModel,
560
private readonly policyConfiguration: ConfigurationModel | undefined,
561
private readonly applicationConfiguration: ConfigurationModel | undefined,
562
private readonly userConfiguration: ConfigurationModel,
563
private readonly localUserConfiguration: ConfigurationModel,
564
private readonly remoteUserConfiguration: ConfigurationModel,
565
private readonly workspaceConfiguration: ConfigurationModel | undefined,
566
private readonly folderConfigurationModel: ConfigurationModel | undefined,
567
private readonly memoryConfigurationModel: ConfigurationModel
568
) {
569
}
570
571
get value(): V | undefined {
572
return freeze(this._value);
573
}
574
575
private toInspectValue(inspectValue: IInspectValue<V> | undefined | null): IInspectValue<V> | undefined {
576
return inspectValue?.value !== undefined || inspectValue?.override !== undefined || inspectValue?.overrides !== undefined ? inspectValue : undefined;
577
}
578
579
private _defaultInspectValue: InspectValue<V> | undefined;
580
private get defaultInspectValue(): InspectValue<V> {
581
if (!this._defaultInspectValue) {
582
this._defaultInspectValue = this.defaultConfiguration.inspect<V>(this.key, this.overrides.overrideIdentifier);
583
}
584
return this._defaultInspectValue;
585
}
586
587
get defaultValue(): V | undefined {
588
return this.defaultInspectValue.merged;
589
}
590
591
get default(): IInspectValue<V> | undefined {
592
return this.toInspectValue(this.defaultInspectValue);
593
}
594
595
private _policyInspectValue: InspectValue<V> | undefined | null;
596
private get policyInspectValue(): InspectValue<V> | null {
597
if (this._policyInspectValue === undefined) {
598
this._policyInspectValue = this.policyConfiguration ? this.policyConfiguration.inspect<V>(this.key) : null;
599
}
600
return this._policyInspectValue;
601
}
602
603
get policyValue(): V | undefined {
604
return this.policyInspectValue?.merged;
605
}
606
607
get policy(): IInspectValue<V> | undefined {
608
return this.policyInspectValue?.value !== undefined ? { value: this.policyInspectValue.value } : undefined;
609
}
610
611
private _applicationInspectValue: InspectValue<V> | undefined | null;
612
private get applicationInspectValue(): InspectValue<V> | null {
613
if (this._applicationInspectValue === undefined) {
614
this._applicationInspectValue = this.applicationConfiguration ? this.applicationConfiguration.inspect<V>(this.key) : null;
615
}
616
return this._applicationInspectValue;
617
}
618
619
get applicationValue(): V | undefined {
620
return this.applicationInspectValue?.merged;
621
}
622
623
get application(): IInspectValue<V> | undefined {
624
return this.toInspectValue(this.applicationInspectValue);
625
}
626
627
private _userInspectValue: InspectValue<V> | undefined;
628
private get userInspectValue(): InspectValue<V> {
629
if (!this._userInspectValue) {
630
this._userInspectValue = this.userConfiguration.inspect<V>(this.key, this.overrides.overrideIdentifier);
631
}
632
return this._userInspectValue;
633
}
634
635
get userValue(): V | undefined {
636
return this.userInspectValue.merged;
637
}
638
639
get user(): IInspectValue<V> | undefined {
640
return this.toInspectValue(this.userInspectValue);
641
}
642
643
private _userLocalInspectValue: InspectValue<V> | undefined;
644
private get userLocalInspectValue(): InspectValue<V> {
645
if (!this._userLocalInspectValue) {
646
this._userLocalInspectValue = this.localUserConfiguration.inspect<V>(this.key, this.overrides.overrideIdentifier);
647
}
648
return this._userLocalInspectValue;
649
}
650
651
get userLocalValue(): V | undefined {
652
return this.userLocalInspectValue.merged;
653
}
654
655
get userLocal(): IInspectValue<V> | undefined {
656
return this.toInspectValue(this.userLocalInspectValue);
657
}
658
659
private _userRemoteInspectValue: InspectValue<V> | undefined;
660
private get userRemoteInspectValue(): InspectValue<V> {
661
if (!this._userRemoteInspectValue) {
662
this._userRemoteInspectValue = this.remoteUserConfiguration.inspect<V>(this.key, this.overrides.overrideIdentifier);
663
}
664
return this._userRemoteInspectValue;
665
}
666
667
get userRemoteValue(): V | undefined {
668
return this.userRemoteInspectValue.merged;
669
}
670
671
get userRemote(): IInspectValue<V> | undefined {
672
return this.toInspectValue(this.userRemoteInspectValue);
673
}
674
675
private _workspaceInspectValue: InspectValue<V> | undefined | null;
676
private get workspaceInspectValue(): InspectValue<V> | null {
677
if (this._workspaceInspectValue === undefined) {
678
this._workspaceInspectValue = this.workspaceConfiguration ? this.workspaceConfiguration.inspect<V>(this.key, this.overrides.overrideIdentifier) : null;
679
}
680
return this._workspaceInspectValue;
681
}
682
683
get workspaceValue(): V | undefined {
684
return this.workspaceInspectValue?.merged;
685
}
686
687
get workspace(): IInspectValue<V> | undefined {
688
return this.toInspectValue(this.workspaceInspectValue);
689
}
690
691
private _workspaceFolderInspectValue: InspectValue<V> | undefined | null;
692
private get workspaceFolderInspectValue(): InspectValue<V> | null {
693
if (this._workspaceFolderInspectValue === undefined) {
694
this._workspaceFolderInspectValue = this.folderConfigurationModel ? this.folderConfigurationModel.inspect<V>(this.key, this.overrides.overrideIdentifier) : null;
695
}
696
return this._workspaceFolderInspectValue;
697
}
698
699
get workspaceFolderValue(): V | undefined {
700
return this.workspaceFolderInspectValue?.merged;
701
}
702
703
get workspaceFolder(): IInspectValue<V> | undefined {
704
return this.toInspectValue(this.workspaceFolderInspectValue);
705
}
706
707
private _memoryInspectValue: InspectValue<V> | undefined;
708
private get memoryInspectValue(): InspectValue<V> {
709
if (this._memoryInspectValue === undefined) {
710
this._memoryInspectValue = this.memoryConfigurationModel.inspect<V>(this.key, this.overrides.overrideIdentifier);
711
}
712
return this._memoryInspectValue;
713
}
714
715
get memoryValue(): V | undefined {
716
return this.memoryInspectValue.merged;
717
}
718
719
get memory(): IInspectValue<V> | undefined {
720
return this.toInspectValue(this.memoryInspectValue);
721
}
722
723
}
724
725
export class Configuration {
726
727
private _workspaceConsolidatedConfiguration: ConfigurationModel | null = null;
728
private _foldersConsolidatedConfigurations = new ResourceMap<ConfigurationModel>();
729
730
constructor(
731
private _defaultConfiguration: ConfigurationModel,
732
private _policyConfiguration: ConfigurationModel,
733
private _applicationConfiguration: ConfigurationModel,
734
private _localUserConfiguration: ConfigurationModel,
735
private _remoteUserConfiguration: ConfigurationModel,
736
private _workspaceConfiguration: ConfigurationModel,
737
private _folderConfigurations: ResourceMap<ConfigurationModel>,
738
private _memoryConfiguration: ConfigurationModel,
739
private _memoryConfigurationByResource: ResourceMap<ConfigurationModel>,
740
private readonly logService: ILogService
741
) {
742
}
743
744
getValue(section: string | undefined, overrides: IConfigurationOverrides, workspace: Workspace | undefined): unknown {
745
const consolidateConfigurationModel = this.getConsolidatedConfigurationModel(section, overrides, workspace);
746
return consolidateConfigurationModel.getValue(section);
747
}
748
749
updateValue(key: string, value: unknown, overrides: IConfigurationUpdateOverrides = {}): void {
750
let memoryConfiguration: ConfigurationModel | undefined;
751
if (overrides.resource) {
752
memoryConfiguration = this._memoryConfigurationByResource.get(overrides.resource);
753
if (!memoryConfiguration) {
754
memoryConfiguration = ConfigurationModel.createEmptyModel(this.logService);
755
this._memoryConfigurationByResource.set(overrides.resource, memoryConfiguration);
756
}
757
} else {
758
memoryConfiguration = this._memoryConfiguration;
759
}
760
761
if (value === undefined) {
762
memoryConfiguration.removeValue(key);
763
} else {
764
memoryConfiguration.setValue(key, value);
765
}
766
767
if (!overrides.resource) {
768
this._workspaceConsolidatedConfiguration = null;
769
}
770
}
771
772
inspect<C>(key: string, overrides: IConfigurationOverrides, workspace: Workspace | undefined): IConfigurationValue<C> {
773
const consolidateConfigurationModel = this.getConsolidatedConfigurationModel(key, overrides, workspace);
774
const folderConfigurationModel = this.getFolderConfigurationModelForResource(overrides.resource, workspace);
775
const memoryConfigurationModel = overrides.resource ? this._memoryConfigurationByResource.get(overrides.resource) || this._memoryConfiguration : this._memoryConfiguration;
776
const overrideIdentifiers = new Set<string>();
777
for (const override of consolidateConfigurationModel.overrides) {
778
for (const overrideIdentifier of override.identifiers) {
779
if (consolidateConfigurationModel.getOverrideValue(key, overrideIdentifier) !== undefined) {
780
overrideIdentifiers.add(overrideIdentifier);
781
}
782
}
783
}
784
785
return new ConfigurationInspectValue<C>(
786
key,
787
overrides,
788
consolidateConfigurationModel.getValue<C>(key),
789
overrideIdentifiers.size ? [...overrideIdentifiers] : undefined,
790
this._defaultConfiguration,
791
this._policyConfiguration.isEmpty() ? undefined : this._policyConfiguration,
792
this.applicationConfiguration.isEmpty() ? undefined : this.applicationConfiguration,
793
this.userConfiguration,
794
this.localUserConfiguration,
795
this.remoteUserConfiguration,
796
workspace ? this._workspaceConfiguration : undefined,
797
folderConfigurationModel ? folderConfigurationModel : undefined,
798
memoryConfigurationModel
799
);
800
801
}
802
803
keys(workspace: Workspace | undefined): {
804
default: string[];
805
policy: string[];
806
user: string[];
807
workspace: string[];
808
workspaceFolder: string[];
809
} {
810
const folderConfigurationModel = this.getFolderConfigurationModelForResource(undefined, workspace);
811
return {
812
default: this._defaultConfiguration.keys.slice(0),
813
policy: this._policyConfiguration.keys.slice(0),
814
user: this.userConfiguration.keys.slice(0),
815
workspace: this._workspaceConfiguration.keys.slice(0),
816
workspaceFolder: folderConfigurationModel ? folderConfigurationModel.keys.slice(0) : []
817
};
818
}
819
820
updateDefaultConfiguration(defaultConfiguration: ConfigurationModel): void {
821
this._defaultConfiguration = defaultConfiguration;
822
this._workspaceConsolidatedConfiguration = null;
823
this._foldersConsolidatedConfigurations.clear();
824
}
825
826
updatePolicyConfiguration(policyConfiguration: ConfigurationModel): void {
827
this._policyConfiguration = policyConfiguration;
828
}
829
830
updateApplicationConfiguration(applicationConfiguration: ConfigurationModel): void {
831
this._applicationConfiguration = applicationConfiguration;
832
this._workspaceConsolidatedConfiguration = null;
833
this._foldersConsolidatedConfigurations.clear();
834
}
835
836
updateLocalUserConfiguration(localUserConfiguration: ConfigurationModel): void {
837
this._localUserConfiguration = localUserConfiguration;
838
this._userConfiguration = null;
839
this._workspaceConsolidatedConfiguration = null;
840
this._foldersConsolidatedConfigurations.clear();
841
}
842
843
updateRemoteUserConfiguration(remoteUserConfiguration: ConfigurationModel): void {
844
this._remoteUserConfiguration = remoteUserConfiguration;
845
this._userConfiguration = null;
846
this._workspaceConsolidatedConfiguration = null;
847
this._foldersConsolidatedConfigurations.clear();
848
}
849
850
updateWorkspaceConfiguration(workspaceConfiguration: ConfigurationModel): void {
851
this._workspaceConfiguration = workspaceConfiguration;
852
this._workspaceConsolidatedConfiguration = null;
853
this._foldersConsolidatedConfigurations.clear();
854
}
855
856
updateFolderConfiguration(resource: URI, configuration: ConfigurationModel): void {
857
this._folderConfigurations.set(resource, configuration);
858
this._foldersConsolidatedConfigurations.delete(resource);
859
}
860
861
deleteFolderConfiguration(resource: URI): void {
862
this.folderConfigurations.delete(resource);
863
this._foldersConsolidatedConfigurations.delete(resource);
864
}
865
866
compareAndUpdateDefaultConfiguration(defaults: ConfigurationModel, keys?: string[]): IConfigurationChange {
867
const overrides: [string, string[]][] = [];
868
if (!keys) {
869
const { added, updated, removed } = compare(this._defaultConfiguration, defaults);
870
keys = [...added, ...updated, ...removed];
871
}
872
for (const key of keys) {
873
for (const overrideIdentifier of overrideIdentifiersFromKey(key)) {
874
const fromKeys = this._defaultConfiguration.getKeysForOverrideIdentifier(overrideIdentifier);
875
const toKeys = defaults.getKeysForOverrideIdentifier(overrideIdentifier);
876
const keys = [
877
...toKeys.filter(key => fromKeys.indexOf(key) === -1),
878
...fromKeys.filter(key => toKeys.indexOf(key) === -1),
879
...fromKeys.filter(key => !objects.equals(this._defaultConfiguration.override(overrideIdentifier).getValue(key), defaults.override(overrideIdentifier).getValue(key)))
880
];
881
overrides.push([overrideIdentifier, keys]);
882
}
883
}
884
this.updateDefaultConfiguration(defaults);
885
return { keys, overrides };
886
}
887
888
compareAndUpdatePolicyConfiguration(policyConfiguration: ConfigurationModel): IConfigurationChange {
889
const { added, updated, removed } = compare(this._policyConfiguration, policyConfiguration);
890
const keys = [...added, ...updated, ...removed];
891
if (keys.length) {
892
this.updatePolicyConfiguration(policyConfiguration);
893
}
894
return { keys, overrides: [] };
895
}
896
897
compareAndUpdateApplicationConfiguration(application: ConfigurationModel): IConfigurationChange {
898
const { added, updated, removed, overrides } = compare(this.applicationConfiguration, application);
899
const keys = [...added, ...updated, ...removed];
900
if (keys.length) {
901
this.updateApplicationConfiguration(application);
902
}
903
return { keys, overrides };
904
}
905
906
compareAndUpdateLocalUserConfiguration(user: ConfigurationModel): IConfigurationChange {
907
const { added, updated, removed, overrides } = compare(this.localUserConfiguration, user);
908
const keys = [...added, ...updated, ...removed];
909
if (keys.length) {
910
this.updateLocalUserConfiguration(user);
911
}
912
return { keys, overrides };
913
}
914
915
compareAndUpdateRemoteUserConfiguration(user: ConfigurationModel): IConfigurationChange {
916
const { added, updated, removed, overrides } = compare(this.remoteUserConfiguration, user);
917
const keys = [...added, ...updated, ...removed];
918
if (keys.length) {
919
this.updateRemoteUserConfiguration(user);
920
}
921
return { keys, overrides };
922
}
923
924
compareAndUpdateWorkspaceConfiguration(workspaceConfiguration: ConfigurationModel): IConfigurationChange {
925
const { added, updated, removed, overrides } = compare(this.workspaceConfiguration, workspaceConfiguration);
926
const keys = [...added, ...updated, ...removed];
927
if (keys.length) {
928
this.updateWorkspaceConfiguration(workspaceConfiguration);
929
}
930
return { keys, overrides };
931
}
932
933
compareAndUpdateFolderConfiguration(resource: URI, folderConfiguration: ConfigurationModel): IConfigurationChange {
934
const currentFolderConfiguration = this.folderConfigurations.get(resource);
935
const { added, updated, removed, overrides } = compare(currentFolderConfiguration, folderConfiguration);
936
const keys = [...added, ...updated, ...removed];
937
if (keys.length || !currentFolderConfiguration) {
938
this.updateFolderConfiguration(resource, folderConfiguration);
939
}
940
return { keys, overrides };
941
}
942
943
compareAndDeleteFolderConfiguration(folder: URI): IConfigurationChange {
944
const folderConfig = this.folderConfigurations.get(folder);
945
if (!folderConfig) {
946
throw new Error('Unknown folder');
947
}
948
this.deleteFolderConfiguration(folder);
949
const { added, updated, removed, overrides } = compare(folderConfig, undefined);
950
return { keys: [...added, ...updated, ...removed], overrides };
951
}
952
953
get defaults(): ConfigurationModel {
954
return this._defaultConfiguration;
955
}
956
957
get applicationConfiguration(): ConfigurationModel {
958
return this._applicationConfiguration;
959
}
960
961
private _userConfiguration: ConfigurationModel | null = null;
962
get userConfiguration(): ConfigurationModel {
963
if (!this._userConfiguration) {
964
if (this._remoteUserConfiguration.isEmpty()) {
965
this._userConfiguration = this._localUserConfiguration;
966
} else {
967
const merged = this._localUserConfiguration.merge(this._remoteUserConfiguration);
968
this._userConfiguration = new ConfigurationModel(merged.contents, merged.keys, merged.overrides, undefined, this.logService);
969
}
970
}
971
return this._userConfiguration;
972
}
973
974
get localUserConfiguration(): ConfigurationModel {
975
return this._localUserConfiguration;
976
}
977
978
get remoteUserConfiguration(): ConfigurationModel {
979
return this._remoteUserConfiguration;
980
}
981
982
get workspaceConfiguration(): ConfigurationModel {
983
return this._workspaceConfiguration;
984
}
985
986
get folderConfigurations(): ResourceMap<ConfigurationModel> {
987
return this._folderConfigurations;
988
}
989
990
private getConsolidatedConfigurationModel(section: string | undefined, overrides: IConfigurationOverrides, workspace: Workspace | undefined): ConfigurationModel {
991
let configurationModel = this.getConsolidatedConfigurationModelForResource(overrides, workspace);
992
if (overrides.overrideIdentifier) {
993
configurationModel = configurationModel.override(overrides.overrideIdentifier);
994
}
995
if (!this._policyConfiguration.isEmpty() && this._policyConfiguration.getValue(section) !== undefined) {
996
// clone by merging
997
configurationModel = configurationModel.merge();
998
for (const key of this._policyConfiguration.keys) {
999
configurationModel.setValue(key, this._policyConfiguration.getValue(key));
1000
}
1001
}
1002
return configurationModel;
1003
}
1004
1005
private getConsolidatedConfigurationModelForResource({ resource }: IConfigurationOverrides, workspace: Workspace | undefined): ConfigurationModel {
1006
let consolidateConfiguration = this.getWorkspaceConsolidatedConfiguration();
1007
1008
if (workspace && resource) {
1009
const root = workspace.getFolder(resource);
1010
if (root) {
1011
consolidateConfiguration = this.getFolderConsolidatedConfiguration(root.uri) || consolidateConfiguration;
1012
}
1013
const memoryConfigurationForResource = this._memoryConfigurationByResource.get(resource);
1014
if (memoryConfigurationForResource) {
1015
consolidateConfiguration = consolidateConfiguration.merge(memoryConfigurationForResource);
1016
}
1017
}
1018
1019
return consolidateConfiguration;
1020
}
1021
1022
private getWorkspaceConsolidatedConfiguration(): ConfigurationModel {
1023
if (!this._workspaceConsolidatedConfiguration) {
1024
this._workspaceConsolidatedConfiguration = this._defaultConfiguration.merge(this.applicationConfiguration, this.userConfiguration, this._workspaceConfiguration, this._memoryConfiguration);
1025
}
1026
return this._workspaceConsolidatedConfiguration;
1027
}
1028
1029
private getFolderConsolidatedConfiguration(folder: URI): ConfigurationModel {
1030
let folderConsolidatedConfiguration = this._foldersConsolidatedConfigurations.get(folder);
1031
if (!folderConsolidatedConfiguration) {
1032
const workspaceConsolidateConfiguration = this.getWorkspaceConsolidatedConfiguration();
1033
const folderConfiguration = this._folderConfigurations.get(folder);
1034
if (folderConfiguration) {
1035
folderConsolidatedConfiguration = workspaceConsolidateConfiguration.merge(folderConfiguration);
1036
this._foldersConsolidatedConfigurations.set(folder, folderConsolidatedConfiguration);
1037
} else {
1038
folderConsolidatedConfiguration = workspaceConsolidateConfiguration;
1039
}
1040
}
1041
return folderConsolidatedConfiguration;
1042
}
1043
1044
private getFolderConfigurationModelForResource(resource: URI | null | undefined, workspace: Workspace | undefined): ConfigurationModel | undefined {
1045
if (workspace && resource) {
1046
const root = workspace.getFolder(resource);
1047
if (root) {
1048
return this._folderConfigurations.get(root.uri);
1049
}
1050
}
1051
return undefined;
1052
}
1053
1054
toData(): IConfigurationData {
1055
return {
1056
defaults: {
1057
contents: this._defaultConfiguration.contents,
1058
overrides: this._defaultConfiguration.overrides,
1059
keys: this._defaultConfiguration.keys,
1060
},
1061
policy: {
1062
contents: this._policyConfiguration.contents,
1063
overrides: this._policyConfiguration.overrides,
1064
keys: this._policyConfiguration.keys
1065
},
1066
application: {
1067
contents: this.applicationConfiguration.contents,
1068
overrides: this.applicationConfiguration.overrides,
1069
keys: this.applicationConfiguration.keys,
1070
raw: Array.isArray(this.applicationConfiguration.raw) ? undefined : this.applicationConfiguration.raw
1071
},
1072
userLocal: {
1073
contents: this.localUserConfiguration.contents,
1074
overrides: this.localUserConfiguration.overrides,
1075
keys: this.localUserConfiguration.keys,
1076
raw: Array.isArray(this.localUserConfiguration.raw) ? undefined : this.localUserConfiguration.raw
1077
},
1078
userRemote: {
1079
contents: this.remoteUserConfiguration.contents,
1080
overrides: this.remoteUserConfiguration.overrides,
1081
keys: this.remoteUserConfiguration.keys,
1082
raw: Array.isArray(this.remoteUserConfiguration.raw) ? undefined : this.remoteUserConfiguration.raw
1083
},
1084
workspace: {
1085
contents: this._workspaceConfiguration.contents,
1086
overrides: this._workspaceConfiguration.overrides,
1087
keys: this._workspaceConfiguration.keys
1088
},
1089
folders: [...this._folderConfigurations.keys()].reduce<[UriComponents, IConfigurationModel][]>((result, folder) => {
1090
const { contents, overrides, keys } = this._folderConfigurations.get(folder)!;
1091
result.push([folder, { contents, overrides, keys }]);
1092
return result;
1093
}, [])
1094
};
1095
}
1096
1097
allKeys(): string[] {
1098
const keys: Set<string> = new Set<string>();
1099
this._defaultConfiguration.keys.forEach(key => keys.add(key));
1100
this.userConfiguration.keys.forEach(key => keys.add(key));
1101
this._workspaceConfiguration.keys.forEach(key => keys.add(key));
1102
this._folderConfigurations.forEach(folderConfiguration => folderConfiguration.keys.forEach(key => keys.add(key)));
1103
return [...keys.values()];
1104
}
1105
1106
protected allOverrideIdentifiers(): string[] {
1107
const keys: Set<string> = new Set<string>();
1108
this._defaultConfiguration.getAllOverrideIdentifiers().forEach(key => keys.add(key));
1109
this.userConfiguration.getAllOverrideIdentifiers().forEach(key => keys.add(key));
1110
this._workspaceConfiguration.getAllOverrideIdentifiers().forEach(key => keys.add(key));
1111
this._folderConfigurations.forEach(folderConfiguration => folderConfiguration.getAllOverrideIdentifiers().forEach(key => keys.add(key)));
1112
return [...keys.values()];
1113
}
1114
1115
protected getAllKeysForOverrideIdentifier(overrideIdentifier: string): string[] {
1116
const keys: Set<string> = new Set<string>();
1117
this._defaultConfiguration.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key));
1118
this.userConfiguration.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key));
1119
this._workspaceConfiguration.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key));
1120
this._folderConfigurations.forEach(folderConfiguration => folderConfiguration.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key)));
1121
return [...keys.values()];
1122
}
1123
1124
static parse(data: IConfigurationData, logService: ILogService): Configuration {
1125
const defaultConfiguration = this.parseConfigurationModel(data.defaults, logService);
1126
const policyConfiguration = this.parseConfigurationModel(data.policy, logService);
1127
const applicationConfiguration = this.parseConfigurationModel(data.application, logService);
1128
const userLocalConfiguration = this.parseConfigurationModel(data.userLocal, logService);
1129
const userRemoteConfiguration = this.parseConfigurationModel(data.userRemote, logService);
1130
const workspaceConfiguration = this.parseConfigurationModel(data.workspace, logService);
1131
const folders: ResourceMap<ConfigurationModel> = data.folders.reduce((result, value) => {
1132
result.set(URI.revive(value[0]), this.parseConfigurationModel(value[1], logService));
1133
return result;
1134
}, new ResourceMap<ConfigurationModel>());
1135
return new Configuration(
1136
defaultConfiguration,
1137
policyConfiguration,
1138
applicationConfiguration,
1139
userLocalConfiguration,
1140
userRemoteConfiguration,
1141
workspaceConfiguration,
1142
folders,
1143
ConfigurationModel.createEmptyModel(logService),
1144
new ResourceMap<ConfigurationModel>(),
1145
logService
1146
);
1147
}
1148
1149
private static parseConfigurationModel(model: IConfigurationModel, logService: ILogService): ConfigurationModel {
1150
return new ConfigurationModel(model.contents, model.keys, model.overrides, model.raw, logService);
1151
}
1152
1153
}
1154
1155
export function mergeChanges(...changes: IConfigurationChange[]): IConfigurationChange {
1156
if (changes.length === 0) {
1157
return { keys: [], overrides: [] };
1158
}
1159
if (changes.length === 1) {
1160
return changes[0];
1161
}
1162
const keysSet = new Set<string>();
1163
const overridesMap = new Map<string, Set<string>>();
1164
for (const change of changes) {
1165
change.keys.forEach(key => keysSet.add(key));
1166
change.overrides.forEach(([identifier, keys]) => {
1167
const result = getOrSet(overridesMap, identifier, new Set<string>());
1168
keys.forEach(key => result.add(key));
1169
});
1170
}
1171
const overrides: [string, string[]][] = [];
1172
overridesMap.forEach((keys, identifier) => overrides.push([identifier, [...keys.values()]]));
1173
return { keys: [...keysSet.values()], overrides };
1174
}
1175
1176
export class ConfigurationChangeEvent implements IConfigurationChangeEvent {
1177
1178
private readonly _marker = '\n';
1179
private readonly _markerCode1 = this._marker.charCodeAt(0);
1180
private readonly _markerCode2 = '.'.charCodeAt(0);
1181
private readonly _affectsConfigStr: string;
1182
1183
readonly affectedKeys = new Set<string>();
1184
source!: ConfigurationTarget;
1185
1186
constructor(
1187
readonly change: IConfigurationChange,
1188
private readonly previous: { workspace?: Workspace; data: IConfigurationData } | undefined,
1189
private readonly currentConfiguraiton: Configuration,
1190
private readonly currentWorkspace: Workspace | undefined,
1191
private readonly logService: ILogService
1192
) {
1193
for (const key of change.keys) {
1194
this.affectedKeys.add(key);
1195
}
1196
for (const [, keys] of change.overrides) {
1197
for (const key of keys) {
1198
this.affectedKeys.add(key);
1199
}
1200
}
1201
1202
// Example: '\nfoo.bar\nabc.def\n'
1203
this._affectsConfigStr = this._marker;
1204
for (const key of this.affectedKeys) {
1205
this._affectsConfigStr += key + this._marker;
1206
}
1207
}
1208
1209
private _previousConfiguration: Configuration | undefined = undefined;
1210
get previousConfiguration(): Configuration | undefined {
1211
if (!this._previousConfiguration && this.previous) {
1212
this._previousConfiguration = Configuration.parse(this.previous.data, this.logService);
1213
}
1214
return this._previousConfiguration;
1215
}
1216
1217
affectsConfiguration(section: string, overrides?: IConfigurationOverrides): boolean {
1218
// we have one large string with all keys that have changed. we pad (marker) the section
1219
// and check that either find it padded or before a segment character
1220
const needle = this._marker + section;
1221
const idx = this._affectsConfigStr.indexOf(needle);
1222
if (idx < 0) {
1223
// NOT: (marker + section)
1224
return false;
1225
}
1226
const pos = idx + needle.length;
1227
if (pos >= this._affectsConfigStr.length) {
1228
return false;
1229
}
1230
const code = this._affectsConfigStr.charCodeAt(pos);
1231
if (code !== this._markerCode1 && code !== this._markerCode2) {
1232
// NOT: section + (marker | segment)
1233
return false;
1234
}
1235
if (overrides) {
1236
const value1 = this.previousConfiguration ? this.previousConfiguration.getValue(section, overrides, this.previous?.workspace) : undefined;
1237
const value2 = this.currentConfiguraiton.getValue(section, overrides, this.currentWorkspace);
1238
return !objects.equals(value1, value2);
1239
}
1240
return true;
1241
}
1242
}
1243
1244
function compare(from: ConfigurationModel | undefined, to: ConfigurationModel | undefined): IConfigurationCompareResult {
1245
const { added, removed, updated } = compareConfigurationContents(to?.rawConfiguration, from?.rawConfiguration);
1246
const overrides: [string, string[]][] = [];
1247
1248
const fromOverrideIdentifiers = from?.getAllOverrideIdentifiers() || [];
1249
const toOverrideIdentifiers = to?.getAllOverrideIdentifiers() || [];
1250
1251
if (to) {
1252
const addedOverrideIdentifiers = toOverrideIdentifiers.filter(key => !fromOverrideIdentifiers.includes(key));
1253
for (const identifier of addedOverrideIdentifiers) {
1254
overrides.push([identifier, to.getKeysForOverrideIdentifier(identifier)]);
1255
}
1256
}
1257
1258
if (from) {
1259
const removedOverrideIdentifiers = fromOverrideIdentifiers.filter(key => !toOverrideIdentifiers.includes(key));
1260
for (const identifier of removedOverrideIdentifiers) {
1261
overrides.push([identifier, from.getKeysForOverrideIdentifier(identifier)]);
1262
}
1263
}
1264
1265
if (to && from) {
1266
for (const identifier of fromOverrideIdentifiers) {
1267
if (toOverrideIdentifiers.includes(identifier)) {
1268
const result = compareConfigurationContents({ contents: from.getOverrideValue(undefined, identifier) || {}, keys: from.getKeysForOverrideIdentifier(identifier) }, { contents: to.getOverrideValue(undefined, identifier) || {}, keys: to.getKeysForOverrideIdentifier(identifier) });
1269
overrides.push([identifier, [...result.added, ...result.removed, ...result.updated]]);
1270
}
1271
}
1272
}
1273
1274
return { added, removed, updated, overrides };
1275
}
1276
1277
function compareConfigurationContents(to: { keys: string[]; contents: IStringDictionary<unknown> } | undefined, from: { keys: string[]; contents: IStringDictionary<unknown> } | undefined) {
1278
const added = to
1279
? from ? to.keys.filter(key => from.keys.indexOf(key) === -1) : [...to.keys]
1280
: [];
1281
const removed = from
1282
? to ? from.keys.filter(key => to.keys.indexOf(key) === -1) : [...from.keys]
1283
: [];
1284
const updated: string[] = [];
1285
1286
if (to && from) {
1287
for (const key of from.keys) {
1288
if (to.keys.indexOf(key) !== -1) {
1289
const value1 = getConfigurationValue(from.contents, key);
1290
const value2 = getConfigurationValue(to.contents, key);
1291
if (!objects.equals(value1, value2)) {
1292
updated.push(key);
1293
}
1294
}
1295
}
1296
}
1297
return { added, removed, updated };
1298
}
1299
1300