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