-
Notifications
You must be signed in to change notification settings - Fork 2.6k
/
Copy pathpreference-service.ts
594 lines (542 loc) · 27.8 KB
/
preference-service.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
// *****************************************************************************
// Copyright (C) 2018 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
// *****************************************************************************
/* eslint-disable @typescript-eslint/no-explicit-any */
import { injectable, inject, postConstruct } from 'inversify';
import { Event, Emitter, DisposableCollection, Disposable, deepFreeze, unreachable } from '../../common';
import { Deferred } from '../../common/promise-util';
import { PreferenceProvider, PreferenceProviderDataChange, PreferenceProviderDataChanges, PreferenceResolveResult } from './preference-provider';
import { PreferenceSchemaProvider } from './preference-contribution';
import URI from '../../common/uri';
import { PreferenceScope } from './preference-scope';
import { PreferenceConfigurations } from './preference-configurations';
import { JSONExt, JSONValue } from '@phosphor/coreutils/lib/json';
import { OverridePreferenceName, PreferenceLanguageOverrideService } from './preference-language-override-service';
export { PreferenceScope };
/**
* Representation of a preference change. A preference value can be set to `undefined` for a specific scope.
* This means that the value from a more general scope will be used.
*/
export interface PreferenceChange extends PreferenceProviderDataChange {
/**
* Tests wether the given resource is affected by the preference change.
* @param resourceUri the uri of the resource to test.
*/
affects(resourceUri?: string): boolean;
}
export class PreferenceChangeImpl implements PreferenceChange {
protected readonly change: PreferenceProviderDataChange;
constructor(change: PreferenceProviderDataChange) {
this.change = deepFreeze(change);
}
get preferenceName(): string {
return this.change.preferenceName;
}
get newValue(): string {
return this.change.newValue;
}
get oldValue(): string {
return this.change.oldValue;
}
get scope(): PreferenceScope {
return this.change.scope;
}
get domain(): string[] | undefined {
return this.change.domain;
}
// TODO add tests
affects(resourceUri?: string): boolean {
const resourcePath = resourceUri && new URI(resourceUri).path;
const domain = this.change.domain;
return !resourcePath || !domain || domain.some(uri => new URI(uri).path.relativity(resourcePath) >= 0);
}
}
/**
* A key-value storage for {@link PreferenceChange}s. Used to aggregate multiple simultaneous preference changes.
*/
export interface PreferenceChanges {
[preferenceName: string]: PreferenceChange
}
export const PreferenceService = Symbol('PreferenceService');
/**
* Service to manage preferences including, among others, getting and setting preference values as well
* as listening to preference changes.
*
* Depending on your use case you might also want to look at {@link createPreferenceProxy} with which
* you can easily create a typesafe schema-based interface for your preferences. Internally the proxy
* uses the PreferenceService so both approaches are compatible.
*/
export interface PreferenceService extends Disposable {
/**
* Promise indicating whether the service successfully initialized.
*/
readonly ready: Promise<void>;
/**
* Indicates whether the service has successfully initialized. Will be `true` when {@link PreferenceService.ready the `ready` Promise} resolves.
*/
readonly isReady: boolean;
/**
* Retrieve the stored value for the given preference.
*
* @param preferenceName the preference identifier.
*
* @returns the value stored for the given preference when it exists, `undefined` otherwise.
*/
get<T>(preferenceName: string): T | undefined;
/**
* Retrieve the stored value for the given preference.
*
* @param preferenceName the preference identifier.
* @param defaultValue the value to return when no value for the given preference is stored.
*
* @returns the value stored for the given preference when it exists, otherwise the given default value.
*/
get<T>(preferenceName: string, defaultValue: T): T;
/**
* Retrieve the stored value for the given preference and resourceUri.
*
* @param preferenceName the preference identifier.
* @param defaultValue the value to return when no value for the given preference is stored.
* @param resourceUri the uri of the resource for which the preference is stored. This used to retrieve
* a potentially different value for the same preference for different resources, for example `files.encoding`.
*
* @returns the value stored for the given preference and resourceUri when it exists, otherwise the given
* default value.
*/
get<T>(preferenceName: string, defaultValue: T, resourceUri?: string): T;
/**
* Retrieve the stored value for the given preference and resourceUri.
*
* @param preferenceName the preference identifier.
* @param defaultValue the value to return when no value for the given preference is stored.
* @param resourceUri the uri of the resource for which the preference is stored. This used to retrieve
* a potentially different value for the same preference for different resources, for example `files.encoding`.
*
* @returns the value stored for the given preference and resourceUri when it exists, otherwise the given
* default value.
*/
get<T>(preferenceName: string, defaultValue?: T, resourceUri?: string): T | undefined;
/**
* Sets the given preference to the given value.
*
* @param preferenceName the preference identifier.
* @param value the new value of the preference.
* @param scope the scope for which the value shall be set, i.e. user, workspace etc.
* When the folder scope is specified a resourceUri must be provided.
* @param resourceUri the uri of the resource for which the preference is stored. This used to store
* a potentially different value for the same preference for different resources, for example `files.encoding`.
*
* @returns a promise which resolves to `undefined` when setting the preference was successful. Otherwise it rejects
* with an error.
*/
set(preferenceName: string, value: any, scope?: PreferenceScope, resourceUri?: string): Promise<void>;
/**
* Determines and applies the changes necessary to apply `value` to either the `resourceUri` supplied or the active session.
* If there is no setting for the `preferenceName`, the change will be applied in user scope.
* If there is a setting conflicting with the specified `value`, the change will be applied in the most specific scope with a conflicting value.
*
* @param preferenceName the identifier of the preference to modify.
* @param value the value to which to set the preference. `undefined` will reset the preference to its default value.
* @param resourceUri the uri of the resource to which the change is to apply. If none is provided, folder scope will be ignored.
*/
updateValue(preferenceName: string, value: any, resourceUri?: string): Promise<void>
/**
* Registers a callback which will be called whenever a preference is changed.
*/
onPreferenceChanged: Event<PreferenceChange>;
/**
* Registers a callback which will be called whenever one or more preferences are changed.
*/
onPreferencesChanged: Event<PreferenceChanges>;
/**
* Retrieve the stored value for the given preference and resourceUri in all available scopes.
*
* @param preferenceName the preference identifier.
* @param resourceUri the uri of the resource for which the preference is stored.
* @param forceLanguageOverride if `true` and `preferenceName` is a language override, only values for the specified override will be returned.
* Otherwise, values for the override will be returned where defined, and values from the base preference will be returned otherwise.
*
* @return an object containing the value of the given preference for all scopes.
*/
inspect<T extends JSONValue>(preferenceName: string, resourceUri?: string, forceLanguageOverride?: boolean): PreferenceInspection<T> | undefined;
/**
* For behavior, see {@link PreferenceService.inspect}.
*
* @returns the value in the scope specified.
*/
inspectInScope<T extends JSONValue>(preferenceName: string, scope: PreferenceScope, resourceUri?: string, forceLanguageOverride?: boolean): T | undefined
/**
* Returns a new preference identifier based on the given OverridePreferenceName.
*
* @param options the override specification.
*
* @returns the calculated string based on the given OverridePreferenceName.
*/
overridePreferenceName(options: OverridePreferenceName): string;
/**
* Tries to split the given preference identifier into the original OverridePreferenceName attributes
* with which this identifier was created. Returns `undefined` if this is not possible, for example
* when the given preference identifier was not generated by `overridePreferenceName`.
*
* This method is checked when resolving preferences. Therefore together with "overridePreferenceName"
* this can be used to handle specialized preferences, e.g. "[markdown].editor.autoIndent" and "editor.autoIndent".
*
* @param preferenceName the preferenceName which might have been created via {@link PreferenceService.overridePreferenceName}.
*
* @returns the OverridePreferenceName which was used to create the given `preferenceName` if this was the case,
* `undefined` otherwise.
*/
overriddenPreferenceName(preferenceName: string): OverridePreferenceName | undefined;
/**
* Retrieve the stored value for the given preference and resourceUri.
*
* @param preferenceName the preference identifier.
* @param defaultValue the value to return when no value for the given preference is stored.
* @param resourceUri the uri of the resource for which the preference is stored. This used to retrieve
* a potentially different value for the same preference for different resources, for example `files.encoding`.
*
* @returns an object containing the value stored for the given preference and resourceUri when it exists,
* otherwise the given default value. If determinable the object will also contain the uri of the configuration
* resource in which the preference was stored.
*/
resolve<T>(preferenceName: string, defaultValue?: T, resourceUri?: string): PreferenceResolveResult<T>;
/**
* Returns the uri of the configuration resource for the given scope and optional resource uri.
*
* @param scope the PreferenceScope to query for.
* @param resourceUri the optional uri of the resource-specific preference handling
* @param sectionName the optional preference section to query for.
*
* @returns the uri of the configuration resource for the given scope and optional resource uri it it exists,
* `undefined` otherwise.
*/
getConfigUri(scope: PreferenceScope, resourceUri?: string, sectionName?: string): URI | undefined;
}
/**
* Return type of the {@link PreferenceService.inspect} call.
*/
export interface PreferenceInspection<T = JSONValue> {
/**
* The preference identifier.
*/
preferenceName: string,
/**
* Value in default scope.
*/
defaultValue: T | undefined,
/**
* Value in user scope.
*/
globalValue: T | undefined,
/**
* Value in workspace scope.
*/
workspaceValue: T | undefined,
/**
* Value in folder scope.
*/
workspaceFolderValue: T | undefined,
/**
* The value that is active, i.e. the value set in the lowest scope available.
*/
value: T | undefined;
}
export type PreferenceInspectionScope = keyof Omit<PreferenceInspection<unknown>, 'preferenceName'>;
/**
* We cannot load providers directly in the case if they depend on `PreferenceService` somehow.
* It allows to load them lazily after DI is configured.
*/
export const PreferenceProviderProvider = Symbol('PreferenceProviderProvider');
export type PreferenceProviderProvider = (scope: PreferenceScope, uri?: URI) => PreferenceProvider;
@injectable()
export class PreferenceServiceImpl implements PreferenceService {
protected readonly onPreferenceChangedEmitter = new Emitter<PreferenceChange>();
readonly onPreferenceChanged = this.onPreferenceChangedEmitter.event;
protected readonly onPreferencesChangedEmitter = new Emitter<PreferenceChanges>();
readonly onPreferencesChanged = this.onPreferencesChangedEmitter.event;
protected readonly toDispose = new DisposableCollection(this.onPreferenceChangedEmitter, this.onPreferencesChangedEmitter);
@inject(PreferenceSchemaProvider)
protected readonly schema: PreferenceSchemaProvider;
@inject(PreferenceProviderProvider)
protected readonly providerProvider: PreferenceProviderProvider;
@inject(PreferenceConfigurations)
protected readonly configurations: PreferenceConfigurations;
@inject(PreferenceLanguageOverrideService)
protected readonly preferenceOverrideService: PreferenceLanguageOverrideService;
protected readonly preferenceProviders = new Map<PreferenceScope, PreferenceProvider>();
protected async initializeProviders(): Promise<void> {
try {
for (const scope of PreferenceScope.getScopes()) {
const provider = this.providerProvider(scope);
this.preferenceProviders.set(scope, provider);
this.toDispose.push(provider.onDidPreferencesChanged(changes =>
this.reconcilePreferences(changes)
));
await provider.ready;
}
this._ready.resolve();
this._isReady = true;
} catch (e) {
this._ready.reject(e);
}
}
@postConstruct()
protected init(): void {
this.toDispose.push(Disposable.create(() => this._ready.reject(new Error('preference service is disposed'))));
this.initializeProviders();
}
dispose(): void {
this.toDispose.dispose();
}
protected readonly _ready = new Deferred<void>();
get ready(): Promise<void> {
return this._ready.promise;
}
protected _isReady = false;
get isReady(): boolean {
return this._isReady;
}
protected reconcilePreferences(changes: PreferenceProviderDataChanges): void {
const changesToEmit: PreferenceChanges = {};
const acceptChange = (change: PreferenceProviderDataChange) =>
this.getAffectedPreferenceNames(change, preferenceName =>
changesToEmit[preferenceName] = new PreferenceChangeImpl({ ...change, preferenceName })
);
for (const preferenceName of Object.keys(changes)) {
let change = changes[preferenceName];
if (change.newValue === undefined) {
const overridden = this.overriddenPreferenceName(change.preferenceName);
if (overridden) {
change = {
...change, newValue: this.doGet(overridden.preferenceName)
};
}
}
if (this.schema.isValidInScope(preferenceName, PreferenceScope.Folder)) {
acceptChange(change);
continue;
}
for (const scope of PreferenceScope.getReversedScopes()) {
if (this.schema.isValidInScope(preferenceName, scope)) {
const provider = this.getProvider(scope);
if (provider) {
const value = provider.get(preferenceName);
if (scope > change.scope && value !== undefined) {
// preference defined in a more specific scope
break;
} else if (scope === change.scope && change.newValue !== undefined) {
// preference is changed into something other than `undefined`
acceptChange(change);
} else if (scope < change.scope && change.newValue === undefined && value !== undefined) {
// preference is changed to `undefined`, use the value from a more general scope
change = {
...change,
newValue: value,
scope
};
acceptChange(change);
}
}
} else if (change.newValue === undefined && change.scope === PreferenceScope.Default) {
// preference is removed
acceptChange(change);
break;
}
}
}
// emit the changes
const changedPreferenceNames = Object.keys(changesToEmit);
if (changedPreferenceNames.length > 0) {
this.onPreferencesChangedEmitter.fire(changesToEmit);
}
changedPreferenceNames.forEach(preferenceName => this.onPreferenceChangedEmitter.fire(changesToEmit[preferenceName]));
}
protected getAffectedPreferenceNames(change: PreferenceProviderDataChange, accept: (affectedPreferenceName: string) => void): void {
accept(change.preferenceName);
for (const overridePreferenceName of this.schema.getOverridePreferenceNames(change.preferenceName)) {
if (!this.doHas(overridePreferenceName)) {
accept(overridePreferenceName);
}
}
}
protected getProvider(scope: PreferenceScope): PreferenceProvider | undefined {
return this.preferenceProviders.get(scope);
}
has(preferenceName: string, resourceUri?: string): boolean {
return this.get(preferenceName, undefined, resourceUri) !== undefined;
}
get<T>(preferenceName: string): T | undefined;
get<T>(preferenceName: string, defaultValue: T): T;
get<T>(preferenceName: string, defaultValue: T, resourceUri: string): T;
get<T>(preferenceName: string, defaultValue?: T, resourceUri?: string): T | undefined;
get<T>(preferenceName: string, defaultValue?: T, resourceUri?: string): T | undefined {
return this.resolve<T>(preferenceName, defaultValue, resourceUri).value;
}
resolve<T>(preferenceName: string, defaultValue?: T, resourceUri?: string): PreferenceResolveResult<T> {
const { value, configUri } = this.doResolve(preferenceName, defaultValue, resourceUri);
if (value === undefined) {
const overridden = this.overriddenPreferenceName(preferenceName);
if (overridden) {
return this.doResolve(overridden.preferenceName, defaultValue, resourceUri);
}
}
return { value, configUri };
}
async set(preferenceName: string, value: any, scope: PreferenceScope | undefined, resourceUri?: string): Promise<void> {
const resolvedScope = scope ?? (!resourceUri ? PreferenceScope.Workspace : PreferenceScope.Folder);
if (resolvedScope === PreferenceScope.Folder && !resourceUri) {
throw new Error('Unable to write to Folder Settings because no resource is provided.');
}
const provider = this.getProvider(resolvedScope);
if (provider && await provider.setPreference(preferenceName, value, resourceUri)) {
return;
}
throw new Error(`Unable to write to ${PreferenceScope[resolvedScope]} Settings.`);
}
getBoolean(preferenceName: string): boolean | undefined;
getBoolean(preferenceName: string, defaultValue: boolean): boolean;
getBoolean(preferenceName: string, defaultValue: boolean, resourceUri: string): boolean;
getBoolean(preferenceName: string, defaultValue?: boolean, resourceUri?: string): boolean | undefined {
const value = resourceUri ? this.get(preferenceName, defaultValue, resourceUri) : this.get(preferenceName, defaultValue);
// eslint-disable-next-line no-null/no-null
return value !== null && value !== undefined ? !!value : defaultValue;
}
getString(preferenceName: string): string | undefined;
getString(preferenceName: string, defaultValue: string): string;
getString(preferenceName: string, defaultValue: string, resourceUri: string): string;
getString(preferenceName: string, defaultValue?: string, resourceUri?: string): string | undefined {
const value = resourceUri ? this.get(preferenceName, defaultValue, resourceUri) : this.get(preferenceName, defaultValue);
// eslint-disable-next-line no-null/no-null
if (value === null || value === undefined) {
return defaultValue;
}
return value.toString();
}
getNumber(preferenceName: string): number | undefined;
getNumber(preferenceName: string, defaultValue: number): number;
getNumber(preferenceName: string, defaultValue: number, resourceUri: string): number;
getNumber(preferenceName: string, defaultValue?: number, resourceUri?: string): number | undefined {
const value = resourceUri ? this.get(preferenceName, defaultValue, resourceUri) : this.get(preferenceName, defaultValue);
// eslint-disable-next-line no-null/no-null
if (value === null || value === undefined) {
return defaultValue;
}
if (typeof value === 'number') {
return value;
}
return Number(value);
}
inspect<T extends JSONValue>(preferenceName: string, resourceUri?: string, forceLanguageOverride?: boolean): PreferenceInspection<T> | undefined {
const defaultValue = this.inspectInScope<T>(preferenceName, PreferenceScope.Default, resourceUri, forceLanguageOverride);
const globalValue = this.inspectInScope<T>(preferenceName, PreferenceScope.User, resourceUri, forceLanguageOverride);
const workspaceValue = this.inspectInScope<T>(preferenceName, PreferenceScope.Workspace, resourceUri, forceLanguageOverride);
const workspaceFolderValue = this.inspectInScope<T>(preferenceName, PreferenceScope.Folder, resourceUri, forceLanguageOverride);
const valueApplied = workspaceFolderValue ?? workspaceValue ?? globalValue ?? defaultValue;
return { preferenceName, defaultValue, globalValue, workspaceValue, workspaceFolderValue, value: valueApplied };
}
inspectInScope<T extends JSONValue>(preferenceName: string, scope: PreferenceScope, resourceUri?: string, forceLanguageOverride?: boolean): T | undefined {
const value = this.doInspectInScope<T>(preferenceName, scope, resourceUri);
if (value === undefined && !forceLanguageOverride) {
const overridden = this.overriddenPreferenceName(preferenceName);
if (overridden) {
return this.doInspectInScope(overridden.preferenceName, scope, resourceUri);
}
}
return value;
}
protected getScopedValueFromInspection<T>(inspection: PreferenceInspection<T>, scope: PreferenceScope): T | undefined {
switch (scope) {
case PreferenceScope.Default:
return inspection.defaultValue;
case PreferenceScope.User:
return inspection.globalValue;
case PreferenceScope.Workspace:
return inspection.workspaceValue;
case PreferenceScope.Folder:
return inspection.workspaceFolderValue;
}
unreachable(scope, 'Not all PreferenceScope enum variants handled.');
}
async updateValue(preferenceName: string, value: any, resourceUri?: string): Promise<void> {
const inspection = this.inspect<any>(preferenceName, resourceUri);
if (inspection) {
const scopesToChange = this.getScopesToChange(inspection, value);
const isDeletion = value === undefined
|| (scopesToChange.length === 1 && scopesToChange[0] === PreferenceScope.User && JSONExt.deepEqual(value, inspection.defaultValue));
const effectiveValue = isDeletion ? undefined : value;
await Promise.all(scopesToChange.map(scope => this.set(preferenceName, effectiveValue, scope, resourceUri)));
}
}
protected getScopesToChange(inspection: PreferenceInspection<any>, intendedValue: any): PreferenceScope[] {
if (JSONExt.deepEqual(inspection.value, intendedValue)) {
return [];
}
// Scopes in ascending order of scope breadth.
const allScopes = PreferenceScope.getReversedScopes();
// Get rid of Default scope. We can't set anything there.
allScopes.pop();
const isScopeDefined = (scope: PreferenceScope) => this.getScopedValueFromInspection(inspection, scope) !== undefined;
if (intendedValue === undefined) {
return allScopes.filter(isScopeDefined);
}
return [allScopes.find(isScopeDefined) ?? PreferenceScope.User];
}
overridePreferenceName(options: OverridePreferenceName): string {
return this.preferenceOverrideService.overridePreferenceName(options);
}
overriddenPreferenceName(preferenceName: string): OverridePreferenceName | undefined {
return this.preferenceOverrideService.overriddenPreferenceName(preferenceName);
}
protected doHas(preferenceName: string, resourceUri?: string): boolean {
return this.doGet(preferenceName, undefined, resourceUri) !== undefined;
}
protected doInspectInScope<T>(preferenceName: string, scope: PreferenceScope, resourceUri?: string): T | undefined {
const provider = this.getProvider(scope);
return provider && provider.get<T>(preferenceName, resourceUri);
}
protected doGet<T>(preferenceName: string, defaultValue?: T, resourceUri?: string): T | undefined {
return this.doResolve(preferenceName, defaultValue, resourceUri).value;
}
protected doResolve<T>(preferenceName: string, defaultValue?: T, resourceUri?: string): PreferenceResolveResult<T> {
const result: PreferenceResolveResult<T> = {};
for (const scope of PreferenceScope.getScopes()) {
if (this.schema.isValidInScope(preferenceName, scope)) {
const provider = this.getProvider(scope);
if (provider?.canHandleScope(scope)) {
const { configUri, value } = provider.resolve<T>(preferenceName, resourceUri);
if (value !== undefined) {
result.configUri = configUri;
result.value = PreferenceProvider.merge(result.value as any, value as any) as any;
}
}
}
}
return {
configUri: result.configUri,
value: result.value !== undefined ? deepFreeze(result.value) : defaultValue
};
}
getConfigUri(scope: PreferenceScope, resourceUri?: string, sectionName: string = this.configurations.getConfigName()): URI | undefined {
const provider = this.getProvider(scope);
if (!provider || !this.configurations.isAnyConfig(sectionName)) {
return undefined;
}
const configUri = provider.getConfigUri(resourceUri, sectionName);
if (configUri) {
return configUri;
}
return provider.getContainingConfigUri && provider.getContainingConfigUri(resourceUri, sectionName);
}
}