Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/services/configuration/browser/configuration.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 { URI } from '../../../../base/common/uri.js';
7
import { Event, Emitter } from '../../../../base/common/event.js';
8
import * as errors from '../../../../base/common/errors.js';
9
import { Disposable, IDisposable, dispose, toDisposable, MutableDisposable, combinedDisposable, DisposableStore } from '../../../../base/common/lifecycle.js';
10
import { RunOnceScheduler } from '../../../../base/common/async.js';
11
import { FileChangeType, FileChangesEvent, IFileService, whenProviderRegistered, FileOperationError, FileOperationResult, FileOperation, FileOperationEvent } from '../../../../platform/files/common/files.js';
12
import { ConfigurationModel, ConfigurationModelParser, ConfigurationParseOptions, UserSettings } from '../../../../platform/configuration/common/configurationModels.js';
13
import { WorkspaceConfigurationModelParser, StandaloneConfigurationModelParser } from '../common/configurationModels.js';
14
import { TASKS_CONFIGURATION_KEY, FOLDER_SETTINGS_NAME, LAUNCH_CONFIGURATION_KEY, IConfigurationCache, ConfigurationKey, REMOTE_MACHINE_SCOPES, FOLDER_SCOPES, WORKSPACE_SCOPES, APPLY_ALL_PROFILES_SETTING, APPLICATION_SCOPES, MCP_CONFIGURATION_KEY } from '../common/configuration.js';
15
import { IStoredWorkspaceFolder } from '../../../../platform/workspaces/common/workspaces.js';
16
import { WorkbenchState, IWorkspaceFolder, IWorkspaceIdentifier } from '../../../../platform/workspace/common/workspace.js';
17
import { ConfigurationScope, Extensions, IConfigurationRegistry, OVERRIDE_PROPERTY_REGEX } from '../../../../platform/configuration/common/configurationRegistry.js';
18
import { equals } from '../../../../base/common/objects.js';
19
import { IRemoteAgentService } from '../../remote/common/remoteAgentService.js';
20
import { hash } from '../../../../base/common/hash.js';
21
import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';
22
import { ILogService } from '../../../../platform/log/common/log.js';
23
import { IStringDictionary } from '../../../../base/common/collections.js';
24
import { joinPath } from '../../../../base/common/resources.js';
25
import { Registry } from '../../../../platform/registry/common/platform.js';
26
import { IBrowserWorkbenchEnvironmentService } from '../../environment/browser/environmentService.js';
27
import { isEmptyObject, isObject } from '../../../../base/common/types.js';
28
import { DefaultConfiguration as BaseDefaultConfiguration } from '../../../../platform/configuration/common/configurations.js';
29
import { IJSONEditingService } from '../common/jsonEditing.js';
30
import { IUserDataProfilesService } from '../../../../platform/userDataProfile/common/userDataProfile.js';
31
32
export class DefaultConfiguration extends BaseDefaultConfiguration {
33
34
static readonly DEFAULT_OVERRIDES_CACHE_EXISTS_KEY = 'DefaultOverridesCacheExists';
35
36
private readonly configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
37
private cachedConfigurationDefaultsOverrides: IStringDictionary<any> = {};
38
private readonly cacheKey: ConfigurationKey = { type: 'defaults', key: 'configurationDefaultsOverrides' };
39
40
private updateCache: boolean = false;
41
42
constructor(
43
private readonly configurationCache: IConfigurationCache,
44
environmentService: IBrowserWorkbenchEnvironmentService,
45
logService: ILogService,
46
) {
47
super(logService);
48
if (environmentService.options?.configurationDefaults) {
49
this.configurationRegistry.registerDefaultConfigurations([{ overrides: environmentService.options.configurationDefaults }]);
50
}
51
}
52
53
protected override getConfigurationDefaultOverrides(): IStringDictionary<any> {
54
return this.cachedConfigurationDefaultsOverrides;
55
}
56
57
override async initialize(): Promise<ConfigurationModel> {
58
await this.initializeCachedConfigurationDefaultsOverrides();
59
return super.initialize();
60
}
61
62
override reload(): ConfigurationModel {
63
this.updateCache = true;
64
this.cachedConfigurationDefaultsOverrides = {};
65
this.updateCachedConfigurationDefaultsOverrides();
66
return super.reload();
67
}
68
69
hasCachedConfigurationDefaultsOverrides(): boolean {
70
return !isEmptyObject(this.cachedConfigurationDefaultsOverrides);
71
}
72
73
private initiaizeCachedConfigurationDefaultsOverridesPromise: Promise<void> | undefined;
74
private initializeCachedConfigurationDefaultsOverrides(): Promise<void> {
75
if (!this.initiaizeCachedConfigurationDefaultsOverridesPromise) {
76
this.initiaizeCachedConfigurationDefaultsOverridesPromise = (async () => {
77
try {
78
// Read only when the cache exists
79
if (localStorage.getItem(DefaultConfiguration.DEFAULT_OVERRIDES_CACHE_EXISTS_KEY)) {
80
const content = await this.configurationCache.read(this.cacheKey);
81
if (content) {
82
this.cachedConfigurationDefaultsOverrides = JSON.parse(content);
83
}
84
}
85
} catch (error) { /* ignore */ }
86
this.cachedConfigurationDefaultsOverrides = isObject(this.cachedConfigurationDefaultsOverrides) ? this.cachedConfigurationDefaultsOverrides : {};
87
})();
88
}
89
return this.initiaizeCachedConfigurationDefaultsOverridesPromise;
90
}
91
92
protected override onDidUpdateConfiguration(properties: string[], defaultsOverrides?: boolean): void {
93
super.onDidUpdateConfiguration(properties, defaultsOverrides);
94
if (defaultsOverrides) {
95
this.updateCachedConfigurationDefaultsOverrides();
96
}
97
}
98
99
private async updateCachedConfigurationDefaultsOverrides(): Promise<void> {
100
if (!this.updateCache) {
101
return;
102
}
103
const cachedConfigurationDefaultsOverrides: IStringDictionary<any> = {};
104
const configurationDefaultsOverrides = this.configurationRegistry.getConfigurationDefaultsOverrides();
105
for (const [key, value] of configurationDefaultsOverrides) {
106
if (!OVERRIDE_PROPERTY_REGEX.test(key) && value.value !== undefined) {
107
cachedConfigurationDefaultsOverrides[key] = value.value;
108
}
109
}
110
try {
111
if (Object.keys(cachedConfigurationDefaultsOverrides).length) {
112
localStorage.setItem(DefaultConfiguration.DEFAULT_OVERRIDES_CACHE_EXISTS_KEY, 'yes');
113
await this.configurationCache.write(this.cacheKey, JSON.stringify(cachedConfigurationDefaultsOverrides));
114
} else {
115
localStorage.removeItem(DefaultConfiguration.DEFAULT_OVERRIDES_CACHE_EXISTS_KEY);
116
await this.configurationCache.remove(this.cacheKey);
117
}
118
} catch (error) {/* Ignore error */ }
119
}
120
121
}
122
123
export class ApplicationConfiguration extends UserSettings {
124
125
private readonly _onDidChangeConfiguration: Emitter<ConfigurationModel> = this._register(new Emitter<ConfigurationModel>());
126
readonly onDidChangeConfiguration: Event<ConfigurationModel> = this._onDidChangeConfiguration.event;
127
128
private readonly reloadConfigurationScheduler: RunOnceScheduler;
129
130
constructor(
131
userDataProfilesService: IUserDataProfilesService,
132
fileService: IFileService,
133
uriIdentityService: IUriIdentityService,
134
logService: ILogService,
135
) {
136
super(userDataProfilesService.defaultProfile.settingsResource, { scopes: APPLICATION_SCOPES, skipUnregistered: true }, uriIdentityService.extUri, fileService, logService);
137
this._register(this.onDidChange(() => this.reloadConfigurationScheduler.schedule()));
138
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.loadConfiguration().then(configurationModel => this._onDidChangeConfiguration.fire(configurationModel)), 50));
139
}
140
141
async initialize(): Promise<ConfigurationModel> {
142
return this.loadConfiguration();
143
}
144
145
override async loadConfiguration(): Promise<ConfigurationModel> {
146
const model = await super.loadConfiguration();
147
const value = model.getValue<string[]>(APPLY_ALL_PROFILES_SETTING);
148
const allProfilesSettings = Array.isArray(value) ? value : [];
149
return this.parseOptions.include || allProfilesSettings.length
150
? this.reparse({ ...this.parseOptions, include: allProfilesSettings })
151
: model;
152
}
153
}
154
155
export class UserConfiguration extends Disposable {
156
157
private readonly _onDidChangeConfiguration: Emitter<ConfigurationModel> = this._register(new Emitter<ConfigurationModel>());
158
readonly onDidChangeConfiguration: Event<ConfigurationModel> = this._onDidChangeConfiguration.event;
159
160
private readonly userConfiguration = this._register(new MutableDisposable<UserSettings | FileServiceBasedConfiguration>());
161
private readonly userConfigurationChangeDisposable = this._register(new MutableDisposable<IDisposable>());
162
private readonly reloadConfigurationScheduler: RunOnceScheduler;
163
164
get hasTasksLoaded(): boolean { return this.userConfiguration.value instanceof FileServiceBasedConfiguration; }
165
166
constructor(
167
private settingsResource: URI,
168
private tasksResource: URI | undefined,
169
private mcpResource: URI | undefined,
170
private configurationParseOptions: ConfigurationParseOptions,
171
private readonly fileService: IFileService,
172
private readonly uriIdentityService: IUriIdentityService,
173
private readonly logService: ILogService,
174
) {
175
super();
176
this.userConfiguration.value = new UserSettings(settingsResource, this.configurationParseOptions, uriIdentityService.extUri, this.fileService, logService);
177
this.userConfigurationChangeDisposable.value = this.userConfiguration.value.onDidChange(() => this.reloadConfigurationScheduler.schedule());
178
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.userConfiguration.value!.loadConfiguration().then(configurationModel => this._onDidChangeConfiguration.fire(configurationModel)), 50));
179
}
180
181
async reset(settingsResource: URI, tasksResource: URI | undefined, mcpResource: URI | undefined, configurationParseOptions: ConfigurationParseOptions): Promise<ConfigurationModel> {
182
this.settingsResource = settingsResource;
183
this.tasksResource = tasksResource;
184
this.mcpResource = mcpResource;
185
this.configurationParseOptions = configurationParseOptions;
186
return this.doReset();
187
}
188
189
private async doReset(settingsConfiguration?: ConfigurationModel): Promise<ConfigurationModel> {
190
const folder = this.uriIdentityService.extUri.dirname(this.settingsResource);
191
const standAloneConfigurationResources: [string, URI][] = [];
192
if (this.tasksResource) {
193
standAloneConfigurationResources.push([TASKS_CONFIGURATION_KEY, this.tasksResource]);
194
}
195
if (this.mcpResource) {
196
standAloneConfigurationResources.push([MCP_CONFIGURATION_KEY, this.mcpResource]);
197
}
198
const fileServiceBasedConfiguration = new FileServiceBasedConfiguration(folder.toString(), this.settingsResource, standAloneConfigurationResources, this.configurationParseOptions, this.fileService, this.uriIdentityService, this.logService);
199
const configurationModel = await fileServiceBasedConfiguration.loadConfiguration(settingsConfiguration);
200
this.userConfiguration.value = fileServiceBasedConfiguration;
201
202
// Check for value because userConfiguration might have been disposed.
203
if (this.userConfigurationChangeDisposable.value) {
204
this.userConfigurationChangeDisposable.value = this.userConfiguration.value.onDidChange(() => this.reloadConfigurationScheduler.schedule());
205
}
206
207
return configurationModel;
208
}
209
210
async initialize(): Promise<ConfigurationModel> {
211
return this.userConfiguration.value!.loadConfiguration();
212
}
213
214
async reload(settingsConfiguration?: ConfigurationModel): Promise<ConfigurationModel> {
215
if (this.hasTasksLoaded) {
216
return this.userConfiguration.value!.loadConfiguration();
217
}
218
return this.doReset(settingsConfiguration);
219
}
220
221
reparse(parseOptions?: Partial<ConfigurationParseOptions>): ConfigurationModel {
222
this.configurationParseOptions = { ...this.configurationParseOptions, ...parseOptions };
223
return this.userConfiguration.value!.reparse(this.configurationParseOptions);
224
}
225
226
getRestrictedSettings(): string[] {
227
return this.userConfiguration.value!.getRestrictedSettings();
228
}
229
}
230
231
class FileServiceBasedConfiguration extends Disposable {
232
233
private readonly allResources: URI[];
234
private _folderSettingsModelParser: ConfigurationModelParser;
235
private _folderSettingsParseOptions: ConfigurationParseOptions;
236
private _standAloneConfigurations: ConfigurationModel[];
237
private _cache: ConfigurationModel;
238
239
private readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
240
readonly onDidChange: Event<void> = this._onDidChange.event;
241
242
constructor(
243
name: string,
244
private readonly settingsResource: URI,
245
private readonly standAloneConfigurationResources: [string, URI][],
246
configurationParseOptions: ConfigurationParseOptions,
247
private readonly fileService: IFileService,
248
private readonly uriIdentityService: IUriIdentityService,
249
private readonly logService: ILogService,
250
) {
251
super();
252
this.allResources = [this.settingsResource, ...this.standAloneConfigurationResources.map(([, resource]) => resource)];
253
this._register(combinedDisposable(...this.allResources.map(resource => combinedDisposable(
254
this.fileService.watch(uriIdentityService.extUri.dirname(resource)),
255
// Also listen to the resource incase the resource is a symlink - https://github.com/microsoft/vscode/issues/118134
256
this.fileService.watch(resource)
257
))));
258
259
this._folderSettingsModelParser = new ConfigurationModelParser(name, logService);
260
this._folderSettingsParseOptions = configurationParseOptions;
261
this._standAloneConfigurations = [];
262
this._cache = ConfigurationModel.createEmptyModel(this.logService);
263
264
this._register(Event.debounce(
265
Event.any(
266
Event.filter(this.fileService.onDidFilesChange, e => this.handleFileChangesEvent(e)),
267
Event.filter(this.fileService.onDidRunOperation, e => this.handleFileOperationEvent(e))
268
), () => undefined, 100)(() => this._onDidChange.fire()));
269
}
270
271
async resolveContents(donotResolveSettings?: boolean): Promise<[string | undefined, [string, string | undefined][]]> {
272
273
const resolveContents = async (resources: URI[]): Promise<(string | undefined)[]> => {
274
return Promise.all(resources.map(async resource => {
275
try {
276
const content = await this.fileService.readFile(resource, { atomic: true });
277
return content.value.toString();
278
} catch (error) {
279
this.logService.trace(`Error while resolving configuration file '${resource.toString()}': ${errors.getErrorMessage(error)}`);
280
if ((<FileOperationError>error).fileOperationResult !== FileOperationResult.FILE_NOT_FOUND
281
&& (<FileOperationError>error).fileOperationResult !== FileOperationResult.FILE_NOT_DIRECTORY) {
282
this.logService.error(error);
283
}
284
}
285
return '{}';
286
}));
287
};
288
289
const [[settingsContent], standAloneConfigurationContents] = await Promise.all([
290
donotResolveSettings ? Promise.resolve([undefined]) : resolveContents([this.settingsResource]),
291
resolveContents(this.standAloneConfigurationResources.map(([, resource]) => resource)),
292
]);
293
294
return [settingsContent, standAloneConfigurationContents.map((content, index) => ([this.standAloneConfigurationResources[index][0], content]))];
295
}
296
297
async loadConfiguration(settingsConfiguration?: ConfigurationModel): Promise<ConfigurationModel> {
298
299
const [settingsContent, standAloneConfigurationContents] = await this.resolveContents(!!settingsConfiguration);
300
301
// reset
302
this._standAloneConfigurations = [];
303
this._folderSettingsModelParser.parse('', this._folderSettingsParseOptions);
304
305
// parse
306
if (settingsContent !== undefined) {
307
this._folderSettingsModelParser.parse(settingsContent, this._folderSettingsParseOptions);
308
}
309
for (let index = 0; index < standAloneConfigurationContents.length; index++) {
310
const contents = standAloneConfigurationContents[index][1];
311
if (contents !== undefined) {
312
const standAloneConfigurationModelParser = new StandaloneConfigurationModelParser(this.standAloneConfigurationResources[index][1].toString(), this.standAloneConfigurationResources[index][0], this.logService);
313
standAloneConfigurationModelParser.parse(contents);
314
this._standAloneConfigurations.push(standAloneConfigurationModelParser.configurationModel);
315
}
316
}
317
318
// Consolidate (support *.json files in the workspace settings folder)
319
this.consolidate(settingsConfiguration);
320
321
return this._cache;
322
}
323
324
getRestrictedSettings(): string[] {
325
return this._folderSettingsModelParser.restrictedConfigurations;
326
}
327
328
reparse(configurationParseOptions: ConfigurationParseOptions): ConfigurationModel {
329
const oldContents = this._folderSettingsModelParser.configurationModel.contents;
330
this._folderSettingsParseOptions = configurationParseOptions;
331
this._folderSettingsModelParser.reparse(this._folderSettingsParseOptions);
332
if (!equals(oldContents, this._folderSettingsModelParser.configurationModel.contents)) {
333
this.consolidate();
334
}
335
return this._cache;
336
}
337
338
private consolidate(settingsConfiguration?: ConfigurationModel): void {
339
this._cache = (settingsConfiguration ?? this._folderSettingsModelParser.configurationModel).merge(...this._standAloneConfigurations);
340
}
341
342
private handleFileChangesEvent(event: FileChangesEvent): boolean {
343
// One of the resources has changed
344
if (this.allResources.some(resource => event.contains(resource))) {
345
return true;
346
}
347
// One of the resource's parent got deleted
348
if (this.allResources.some(resource => event.contains(this.uriIdentityService.extUri.dirname(resource), FileChangeType.DELETED))) {
349
return true;
350
}
351
return false;
352
}
353
354
private handleFileOperationEvent(event: FileOperationEvent): boolean {
355
// One of the resources has changed
356
if ((event.isOperation(FileOperation.CREATE) || event.isOperation(FileOperation.COPY) || event.isOperation(FileOperation.DELETE) || event.isOperation(FileOperation.WRITE))
357
&& this.allResources.some(resource => this.uriIdentityService.extUri.isEqual(event.resource, resource))) {
358
return true;
359
}
360
// One of the resource's parent got deleted
361
if (event.isOperation(FileOperation.DELETE) && this.allResources.some(resource => this.uriIdentityService.extUri.isEqual(event.resource, this.uriIdentityService.extUri.dirname(resource)))) {
362
return true;
363
}
364
return false;
365
}
366
367
}
368
369
export class RemoteUserConfiguration extends Disposable {
370
371
private readonly _cachedConfiguration: CachedRemoteUserConfiguration;
372
private readonly _fileService: IFileService;
373
private _userConfiguration: FileServiceBasedRemoteUserConfiguration | CachedRemoteUserConfiguration;
374
private _userConfigurationInitializationPromise: Promise<ConfigurationModel> | null = null;
375
376
private readonly _onDidChangeConfiguration: Emitter<ConfigurationModel> = this._register(new Emitter<ConfigurationModel>());
377
public readonly onDidChangeConfiguration: Event<ConfigurationModel> = this._onDidChangeConfiguration.event;
378
379
private readonly _onDidInitialize = this._register(new Emitter<ConfigurationModel>());
380
public readonly onDidInitialize = this._onDidInitialize.event;
381
382
constructor(
383
remoteAuthority: string,
384
configurationCache: IConfigurationCache,
385
fileService: IFileService,
386
uriIdentityService: IUriIdentityService,
387
remoteAgentService: IRemoteAgentService,
388
logService: ILogService
389
) {
390
super();
391
this._fileService = fileService;
392
this._userConfiguration = this._cachedConfiguration = new CachedRemoteUserConfiguration(remoteAuthority, configurationCache, { scopes: REMOTE_MACHINE_SCOPES }, logService);
393
remoteAgentService.getEnvironment().then(async environment => {
394
if (environment) {
395
const userConfiguration = this._register(new FileServiceBasedRemoteUserConfiguration(environment.settingsPath, { scopes: REMOTE_MACHINE_SCOPES }, this._fileService, uriIdentityService, logService));
396
this._register(userConfiguration.onDidChangeConfiguration(configurationModel => this.onDidUserConfigurationChange(configurationModel)));
397
this._userConfigurationInitializationPromise = userConfiguration.initialize();
398
const configurationModel = await this._userConfigurationInitializationPromise;
399
this._userConfiguration.dispose();
400
this._userConfiguration = userConfiguration;
401
this.onDidUserConfigurationChange(configurationModel);
402
this._onDidInitialize.fire(configurationModel);
403
}
404
});
405
}
406
407
async initialize(): Promise<ConfigurationModel> {
408
if (this._userConfiguration instanceof FileServiceBasedRemoteUserConfiguration) {
409
return this._userConfiguration.initialize();
410
}
411
412
// Initialize cached configuration
413
let configurationModel = await this._userConfiguration.initialize();
414
if (this._userConfigurationInitializationPromise) {
415
// Use user configuration
416
configurationModel = await this._userConfigurationInitializationPromise;
417
this._userConfigurationInitializationPromise = null;
418
}
419
420
return configurationModel;
421
}
422
423
reload(): Promise<ConfigurationModel> {
424
return this._userConfiguration.reload();
425
}
426
427
reparse(): ConfigurationModel {
428
return this._userConfiguration.reparse({ scopes: REMOTE_MACHINE_SCOPES });
429
}
430
431
getRestrictedSettings(): string[] {
432
return this._userConfiguration.getRestrictedSettings();
433
}
434
435
private onDidUserConfigurationChange(configurationModel: ConfigurationModel): void {
436
this.updateCache();
437
this._onDidChangeConfiguration.fire(configurationModel);
438
}
439
440
private async updateCache(): Promise<void> {
441
if (this._userConfiguration instanceof FileServiceBasedRemoteUserConfiguration) {
442
let content: string | undefined;
443
try {
444
content = await this._userConfiguration.resolveContent();
445
} catch (error) {
446
if ((<FileOperationError>error).fileOperationResult !== FileOperationResult.FILE_NOT_FOUND) {
447
return;
448
}
449
}
450
await this._cachedConfiguration.updateConfiguration(content);
451
}
452
}
453
454
}
455
456
class FileServiceBasedRemoteUserConfiguration extends Disposable {
457
458
private readonly parser: ConfigurationModelParser;
459
private parseOptions: ConfigurationParseOptions;
460
private readonly reloadConfigurationScheduler: RunOnceScheduler;
461
protected readonly _onDidChangeConfiguration: Emitter<ConfigurationModel> = this._register(new Emitter<ConfigurationModel>());
462
readonly onDidChangeConfiguration: Event<ConfigurationModel> = this._onDidChangeConfiguration.event;
463
464
private readonly fileWatcherDisposable = this._register(new MutableDisposable());
465
private readonly directoryWatcherDisposable = this._register(new MutableDisposable());
466
467
constructor(
468
private readonly configurationResource: URI,
469
configurationParseOptions: ConfigurationParseOptions,
470
private readonly fileService: IFileService,
471
private readonly uriIdentityService: IUriIdentityService,
472
private readonly logService: ILogService,
473
) {
474
super();
475
476
this.parser = new ConfigurationModelParser(this.configurationResource.toString(), logService);
477
this.parseOptions = configurationParseOptions;
478
this._register(fileService.onDidFilesChange(e => this.handleFileChangesEvent(e)));
479
this._register(fileService.onDidRunOperation(e => this.handleFileOperationEvent(e)));
480
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reload().then(configurationModel => this._onDidChangeConfiguration.fire(configurationModel)), 50));
481
this._register(toDisposable(() => {
482
this.stopWatchingResource();
483
this.stopWatchingDirectory();
484
}));
485
}
486
487
private watchResource(): void {
488
this.fileWatcherDisposable.value = this.fileService.watch(this.configurationResource);
489
}
490
491
private stopWatchingResource(): void {
492
this.fileWatcherDisposable.value = undefined;
493
}
494
495
private watchDirectory(): void {
496
const directory = this.uriIdentityService.extUri.dirname(this.configurationResource);
497
this.directoryWatcherDisposable.value = this.fileService.watch(directory);
498
}
499
500
private stopWatchingDirectory(): void {
501
this.directoryWatcherDisposable.value = undefined;
502
}
503
504
async initialize(): Promise<ConfigurationModel> {
505
const exists = await this.fileService.exists(this.configurationResource);
506
this.onResourceExists(exists);
507
return this.reload();
508
}
509
510
async resolveContent(): Promise<string> {
511
const content = await this.fileService.readFile(this.configurationResource, { atomic: true });
512
return content.value.toString();
513
}
514
515
async reload(): Promise<ConfigurationModel> {
516
try {
517
const content = await this.resolveContent();
518
this.parser.parse(content, this.parseOptions);
519
return this.parser.configurationModel;
520
} catch (e) {
521
return ConfigurationModel.createEmptyModel(this.logService);
522
}
523
}
524
525
reparse(configurationParseOptions: ConfigurationParseOptions): ConfigurationModel {
526
this.parseOptions = configurationParseOptions;
527
this.parser.reparse(this.parseOptions);
528
return this.parser.configurationModel;
529
}
530
531
getRestrictedSettings(): string[] {
532
return this.parser.restrictedConfigurations;
533
}
534
535
private handleFileChangesEvent(event: FileChangesEvent): void {
536
537
// Find changes that affect the resource
538
let affectedByChanges = false;
539
if (event.contains(this.configurationResource, FileChangeType.ADDED)) {
540
affectedByChanges = true;
541
this.onResourceExists(true);
542
} else if (event.contains(this.configurationResource, FileChangeType.DELETED)) {
543
affectedByChanges = true;
544
this.onResourceExists(false);
545
} else if (event.contains(this.configurationResource, FileChangeType.UPDATED)) {
546
affectedByChanges = true;
547
}
548
549
if (affectedByChanges) {
550
this.reloadConfigurationScheduler.schedule();
551
}
552
}
553
554
private handleFileOperationEvent(event: FileOperationEvent): void {
555
if ((event.isOperation(FileOperation.CREATE) || event.isOperation(FileOperation.COPY) || event.isOperation(FileOperation.DELETE) || event.isOperation(FileOperation.WRITE))
556
&& this.uriIdentityService.extUri.isEqual(event.resource, this.configurationResource)) {
557
this.reloadConfigurationScheduler.schedule();
558
}
559
}
560
561
private onResourceExists(exists: boolean): void {
562
if (exists) {
563
this.stopWatchingDirectory();
564
this.watchResource();
565
} else {
566
this.stopWatchingResource();
567
this.watchDirectory();
568
}
569
}
570
}
571
572
class CachedRemoteUserConfiguration extends Disposable {
573
574
private readonly _onDidChange: Emitter<ConfigurationModel> = this._register(new Emitter<ConfigurationModel>());
575
readonly onDidChange: Event<ConfigurationModel> = this._onDidChange.event;
576
577
private readonly key: ConfigurationKey;
578
private readonly parser: ConfigurationModelParser;
579
private parseOptions: ConfigurationParseOptions;
580
private configurationModel: ConfigurationModel;
581
582
constructor(
583
remoteAuthority: string,
584
private readonly configurationCache: IConfigurationCache,
585
configurationParseOptions: ConfigurationParseOptions,
586
logService: ILogService,
587
) {
588
super();
589
this.key = { type: 'user', key: remoteAuthority };
590
this.parser = new ConfigurationModelParser('CachedRemoteUserConfiguration', logService);
591
this.parseOptions = configurationParseOptions;
592
this.configurationModel = ConfigurationModel.createEmptyModel(logService);
593
}
594
595
getConfigurationModel(): ConfigurationModel {
596
return this.configurationModel;
597
}
598
599
initialize(): Promise<ConfigurationModel> {
600
return this.reload();
601
}
602
603
reparse(configurationParseOptions: ConfigurationParseOptions): ConfigurationModel {
604
this.parseOptions = configurationParseOptions;
605
this.parser.reparse(this.parseOptions);
606
this.configurationModel = this.parser.configurationModel;
607
return this.configurationModel;
608
}
609
610
getRestrictedSettings(): string[] {
611
return this.parser.restrictedConfigurations;
612
}
613
614
async reload(): Promise<ConfigurationModel> {
615
try {
616
const content = await this.configurationCache.read(this.key);
617
const parsed: { content: string } = JSON.parse(content);
618
if (parsed.content) {
619
this.parser.parse(parsed.content, this.parseOptions);
620
this.configurationModel = this.parser.configurationModel;
621
}
622
} catch (e) { /* Ignore error */ }
623
return this.configurationModel;
624
}
625
626
async updateConfiguration(content: string | undefined): Promise<void> {
627
if (content) {
628
return this.configurationCache.write(this.key, JSON.stringify({ content }));
629
} else {
630
return this.configurationCache.remove(this.key);
631
}
632
}
633
}
634
635
export class WorkspaceConfiguration extends Disposable {
636
637
private readonly _cachedConfiguration: CachedWorkspaceConfiguration;
638
private _workspaceConfiguration: CachedWorkspaceConfiguration | FileServiceBasedWorkspaceConfiguration;
639
private readonly _workspaceConfigurationDisposables = this._register(new DisposableStore());
640
private _workspaceIdentifier: IWorkspaceIdentifier | null = null;
641
private _isWorkspaceTrusted: boolean = false;
642
643
private readonly _onDidUpdateConfiguration = this._register(new Emitter<boolean>());
644
public readonly onDidUpdateConfiguration = this._onDidUpdateConfiguration.event;
645
646
private _initialized: boolean = false;
647
get initialized(): boolean { return this._initialized; }
648
constructor(
649
private readonly configurationCache: IConfigurationCache,
650
private readonly fileService: IFileService,
651
private readonly uriIdentityService: IUriIdentityService,
652
private readonly logService: ILogService,
653
) {
654
super();
655
this.fileService = fileService;
656
this._workspaceConfiguration = this._cachedConfiguration = new CachedWorkspaceConfiguration(configurationCache, logService);
657
}
658
659
async initialize(workspaceIdentifier: IWorkspaceIdentifier, workspaceTrusted: boolean): Promise<void> {
660
this._workspaceIdentifier = workspaceIdentifier;
661
this._isWorkspaceTrusted = workspaceTrusted;
662
if (!this._initialized) {
663
if (this.configurationCache.needsCaching(this._workspaceIdentifier.configPath)) {
664
this._workspaceConfiguration = this._cachedConfiguration;
665
this.waitAndInitialize(this._workspaceIdentifier);
666
} else {
667
this.doInitialize(new FileServiceBasedWorkspaceConfiguration(this.fileService, this.uriIdentityService, this.logService));
668
}
669
}
670
await this.reload();
671
}
672
673
async reload(): Promise<void> {
674
if (this._workspaceIdentifier) {
675
await this._workspaceConfiguration.load(this._workspaceIdentifier, { scopes: WORKSPACE_SCOPES, skipRestricted: this.isUntrusted() });
676
}
677
}
678
679
getFolders(): IStoredWorkspaceFolder[] {
680
return this._workspaceConfiguration.getFolders();
681
}
682
683
setFolders(folders: IStoredWorkspaceFolder[], jsonEditingService: IJSONEditingService): Promise<void> {
684
if (this._workspaceIdentifier) {
685
return jsonEditingService.write(this._workspaceIdentifier.configPath, [{ path: ['folders'], value: folders }], true)
686
.then(() => this.reload());
687
}
688
return Promise.resolve();
689
}
690
691
isTransient(): boolean {
692
return this._workspaceConfiguration.isTransient();
693
}
694
695
getConfiguration(): ConfigurationModel {
696
return this._workspaceConfiguration.getWorkspaceSettings();
697
}
698
699
updateWorkspaceTrust(trusted: boolean): ConfigurationModel {
700
this._isWorkspaceTrusted = trusted;
701
return this.reparseWorkspaceSettings();
702
}
703
704
reparseWorkspaceSettings(): ConfigurationModel {
705
this._workspaceConfiguration.reparseWorkspaceSettings({ scopes: WORKSPACE_SCOPES, skipRestricted: this.isUntrusted() });
706
return this.getConfiguration();
707
}
708
709
getRestrictedSettings(): string[] {
710
return this._workspaceConfiguration.getRestrictedSettings();
711
}
712
713
private async waitAndInitialize(workspaceIdentifier: IWorkspaceIdentifier): Promise<void> {
714
await whenProviderRegistered(workspaceIdentifier.configPath, this.fileService);
715
if (!(this._workspaceConfiguration instanceof FileServiceBasedWorkspaceConfiguration)) {
716
const fileServiceBasedWorkspaceConfiguration = this._register(new FileServiceBasedWorkspaceConfiguration(this.fileService, this.uriIdentityService, this.logService));
717
await fileServiceBasedWorkspaceConfiguration.load(workspaceIdentifier, { scopes: WORKSPACE_SCOPES, skipRestricted: this.isUntrusted() });
718
this.doInitialize(fileServiceBasedWorkspaceConfiguration);
719
this.onDidWorkspaceConfigurationChange(false, true);
720
}
721
}
722
723
private doInitialize(fileServiceBasedWorkspaceConfiguration: FileServiceBasedWorkspaceConfiguration): void {
724
this._workspaceConfigurationDisposables.clear();
725
this._workspaceConfiguration = this._workspaceConfigurationDisposables.add(fileServiceBasedWorkspaceConfiguration);
726
this._workspaceConfigurationDisposables.add(this._workspaceConfiguration.onDidChange(e => this.onDidWorkspaceConfigurationChange(true, false)));
727
this._initialized = true;
728
}
729
730
private isUntrusted(): boolean {
731
return !this._isWorkspaceTrusted;
732
}
733
734
private async onDidWorkspaceConfigurationChange(reload: boolean, fromCache: boolean): Promise<void> {
735
if (reload) {
736
await this.reload();
737
}
738
this.updateCache();
739
this._onDidUpdateConfiguration.fire(fromCache);
740
}
741
742
private async updateCache(): Promise<void> {
743
if (this._workspaceIdentifier && this.configurationCache.needsCaching(this._workspaceIdentifier.configPath) && this._workspaceConfiguration instanceof FileServiceBasedWorkspaceConfiguration) {
744
const content = await this._workspaceConfiguration.resolveContent(this._workspaceIdentifier);
745
await this._cachedConfiguration.updateWorkspace(this._workspaceIdentifier, content);
746
}
747
}
748
}
749
750
class FileServiceBasedWorkspaceConfiguration extends Disposable {
751
752
workspaceConfigurationModelParser: WorkspaceConfigurationModelParser;
753
workspaceSettings: ConfigurationModel;
754
private _workspaceIdentifier: IWorkspaceIdentifier | null = null;
755
private workspaceConfigWatcher: IDisposable;
756
private readonly reloadConfigurationScheduler: RunOnceScheduler;
757
758
protected readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
759
readonly onDidChange: Event<void> = this._onDidChange.event;
760
761
constructor(
762
private readonly fileService: IFileService,
763
uriIdentityService: IUriIdentityService,
764
private readonly logService: ILogService,
765
) {
766
super();
767
768
this.workspaceConfigurationModelParser = new WorkspaceConfigurationModelParser('', logService);
769
this.workspaceSettings = ConfigurationModel.createEmptyModel(logService);
770
771
this._register(Event.any(
772
Event.filter(this.fileService.onDidFilesChange, e => !!this._workspaceIdentifier && e.contains(this._workspaceIdentifier.configPath)),
773
Event.filter(this.fileService.onDidRunOperation, e => !!this._workspaceIdentifier && (e.isOperation(FileOperation.CREATE) || e.isOperation(FileOperation.COPY) || e.isOperation(FileOperation.DELETE) || e.isOperation(FileOperation.WRITE)) && uriIdentityService.extUri.isEqual(e.resource, this._workspaceIdentifier.configPath))
774
)(() => this.reloadConfigurationScheduler.schedule()));
775
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this._onDidChange.fire(), 50));
776
this.workspaceConfigWatcher = this._register(this.watchWorkspaceConfigurationFile());
777
}
778
779
get workspaceIdentifier(): IWorkspaceIdentifier | null {
780
return this._workspaceIdentifier;
781
}
782
783
async resolveContent(workspaceIdentifier: IWorkspaceIdentifier): Promise<string> {
784
const content = await this.fileService.readFile(workspaceIdentifier.configPath, { atomic: true });
785
return content.value.toString();
786
}
787
788
async load(workspaceIdentifier: IWorkspaceIdentifier, configurationParseOptions: ConfigurationParseOptions): Promise<void> {
789
if (!this._workspaceIdentifier || this._workspaceIdentifier.id !== workspaceIdentifier.id) {
790
this._workspaceIdentifier = workspaceIdentifier;
791
this.workspaceConfigurationModelParser = new WorkspaceConfigurationModelParser(this._workspaceIdentifier.id, this.logService);
792
dispose(this.workspaceConfigWatcher);
793
this.workspaceConfigWatcher = this._register(this.watchWorkspaceConfigurationFile());
794
}
795
let contents = '';
796
try {
797
contents = await this.resolveContent(this._workspaceIdentifier);
798
} catch (error) {
799
const exists = await this.fileService.exists(this._workspaceIdentifier.configPath);
800
if (exists) {
801
this.logService.error(error);
802
}
803
}
804
this.workspaceConfigurationModelParser.parse(contents, configurationParseOptions);
805
this.consolidate();
806
}
807
808
getConfigurationModel(): ConfigurationModel {
809
return this.workspaceConfigurationModelParser.configurationModel;
810
}
811
812
getFolders(): IStoredWorkspaceFolder[] {
813
return this.workspaceConfigurationModelParser.folders;
814
}
815
816
isTransient(): boolean {
817
return this.workspaceConfigurationModelParser.transient;
818
}
819
820
getWorkspaceSettings(): ConfigurationModel {
821
return this.workspaceSettings;
822
}
823
824
reparseWorkspaceSettings(configurationParseOptions: ConfigurationParseOptions): ConfigurationModel {
825
this.workspaceConfigurationModelParser.reparseWorkspaceSettings(configurationParseOptions);
826
this.consolidate();
827
return this.getWorkspaceSettings();
828
}
829
830
getRestrictedSettings(): string[] {
831
return this.workspaceConfigurationModelParser.getRestrictedWorkspaceSettings();
832
}
833
834
private consolidate(): void {
835
this.workspaceSettings = this.workspaceConfigurationModelParser.settingsModel.merge(this.workspaceConfigurationModelParser.launchModel, this.workspaceConfigurationModelParser.tasksModel);
836
}
837
838
private watchWorkspaceConfigurationFile(): IDisposable {
839
return this._workspaceIdentifier ? this.fileService.watch(this._workspaceIdentifier.configPath) : Disposable.None;
840
}
841
842
}
843
844
class CachedWorkspaceConfiguration {
845
846
readonly onDidChange: Event<void> = Event.None;
847
848
workspaceConfigurationModelParser: WorkspaceConfigurationModelParser;
849
workspaceSettings: ConfigurationModel;
850
851
constructor(
852
private readonly configurationCache: IConfigurationCache,
853
private readonly logService: ILogService
854
) {
855
this.workspaceConfigurationModelParser = new WorkspaceConfigurationModelParser('', logService);
856
this.workspaceSettings = ConfigurationModel.createEmptyModel(logService);
857
}
858
859
async load(workspaceIdentifier: IWorkspaceIdentifier, configurationParseOptions: ConfigurationParseOptions): Promise<void> {
860
try {
861
const key = this.getKey(workspaceIdentifier);
862
const contents = await this.configurationCache.read(key);
863
const parsed: { content: string } = JSON.parse(contents);
864
if (parsed.content) {
865
this.workspaceConfigurationModelParser = new WorkspaceConfigurationModelParser(key.key, this.logService);
866
this.workspaceConfigurationModelParser.parse(parsed.content, configurationParseOptions);
867
this.consolidate();
868
}
869
} catch (e) {
870
}
871
}
872
873
get workspaceIdentifier(): IWorkspaceIdentifier | null {
874
return null;
875
}
876
877
getConfigurationModel(): ConfigurationModel {
878
return this.workspaceConfigurationModelParser.configurationModel;
879
}
880
881
getFolders(): IStoredWorkspaceFolder[] {
882
return this.workspaceConfigurationModelParser.folders;
883
}
884
885
isTransient(): boolean {
886
return this.workspaceConfigurationModelParser.transient;
887
}
888
889
getWorkspaceSettings(): ConfigurationModel {
890
return this.workspaceSettings;
891
}
892
893
reparseWorkspaceSettings(configurationParseOptions: ConfigurationParseOptions): ConfigurationModel {
894
this.workspaceConfigurationModelParser.reparseWorkspaceSettings(configurationParseOptions);
895
this.consolidate();
896
return this.getWorkspaceSettings();
897
}
898
899
getRestrictedSettings(): string[] {
900
return this.workspaceConfigurationModelParser.getRestrictedWorkspaceSettings();
901
}
902
903
private consolidate(): void {
904
this.workspaceSettings = this.workspaceConfigurationModelParser.settingsModel.merge(this.workspaceConfigurationModelParser.launchModel, this.workspaceConfigurationModelParser.tasksModel);
905
}
906
907
async updateWorkspace(workspaceIdentifier: IWorkspaceIdentifier, content: string | undefined): Promise<void> {
908
try {
909
const key = this.getKey(workspaceIdentifier);
910
if (content) {
911
await this.configurationCache.write(key, JSON.stringify({ content }));
912
} else {
913
await this.configurationCache.remove(key);
914
}
915
} catch (error) {
916
}
917
}
918
919
private getKey(workspaceIdentifier: IWorkspaceIdentifier): ConfigurationKey {
920
return {
921
type: 'workspaces',
922
key: workspaceIdentifier.id
923
};
924
}
925
}
926
927
class CachedFolderConfiguration {
928
929
readonly onDidChange = Event.None;
930
931
private _folderSettingsModelParser: ConfigurationModelParser;
932
private _folderSettingsParseOptions: ConfigurationParseOptions;
933
private _standAloneConfigurations: ConfigurationModel[];
934
private configurationModel: ConfigurationModel;
935
private readonly key: ConfigurationKey;
936
937
constructor(
938
folder: URI,
939
configFolderRelativePath: string,
940
configurationParseOptions: ConfigurationParseOptions,
941
private readonly configurationCache: IConfigurationCache,
942
private readonly logService: ILogService
943
) {
944
this.key = { type: 'folder', key: hash(joinPath(folder, configFolderRelativePath).toString()).toString(16) };
945
this._folderSettingsModelParser = new ConfigurationModelParser('CachedFolderConfiguration', logService);
946
this._folderSettingsParseOptions = configurationParseOptions;
947
this._standAloneConfigurations = [];
948
this.configurationModel = ConfigurationModel.createEmptyModel(logService);
949
}
950
951
async loadConfiguration(): Promise<ConfigurationModel> {
952
try {
953
const contents = await this.configurationCache.read(this.key);
954
const { content: configurationContents }: { content: IStringDictionary<string> } = JSON.parse(contents.toString());
955
if (configurationContents) {
956
for (const key of Object.keys(configurationContents)) {
957
if (key === FOLDER_SETTINGS_NAME) {
958
this._folderSettingsModelParser.parse(configurationContents[key], this._folderSettingsParseOptions);
959
} else {
960
const standAloneConfigurationModelParser = new StandaloneConfigurationModelParser(key, key, this.logService);
961
standAloneConfigurationModelParser.parse(configurationContents[key]);
962
this._standAloneConfigurations.push(standAloneConfigurationModelParser.configurationModel);
963
}
964
}
965
}
966
this.consolidate();
967
} catch (e) {
968
}
969
return this.configurationModel;
970
}
971
972
async updateConfiguration(settingsContent: string | undefined, standAloneConfigurationContents: [string, string | undefined][]): Promise<void> {
973
const content: any = {};
974
if (settingsContent) {
975
content[FOLDER_SETTINGS_NAME] = settingsContent;
976
}
977
standAloneConfigurationContents.forEach(([key, contents]) => {
978
if (contents) {
979
content[key] = contents;
980
}
981
});
982
if (Object.keys(content).length) {
983
await this.configurationCache.write(this.key, JSON.stringify({ content }));
984
} else {
985
await this.configurationCache.remove(this.key);
986
}
987
}
988
989
getRestrictedSettings(): string[] {
990
return this._folderSettingsModelParser.restrictedConfigurations;
991
}
992
993
reparse(configurationParseOptions: ConfigurationParseOptions): ConfigurationModel {
994
this._folderSettingsParseOptions = configurationParseOptions;
995
this._folderSettingsModelParser.reparse(this._folderSettingsParseOptions);
996
this.consolidate();
997
return this.configurationModel;
998
}
999
1000
private consolidate(): void {
1001
this.configurationModel = this._folderSettingsModelParser.configurationModel.merge(...this._standAloneConfigurations);
1002
}
1003
1004
getUnsupportedKeys(): string[] {
1005
return [];
1006
}
1007
}
1008
1009
export class FolderConfiguration extends Disposable {
1010
1011
protected readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
1012
readonly onDidChange: Event<void> = this._onDidChange.event;
1013
1014
private folderConfiguration: CachedFolderConfiguration | FileServiceBasedConfiguration;
1015
private readonly scopes: ConfigurationScope[];
1016
private readonly configurationFolder: URI;
1017
private cachedFolderConfiguration: CachedFolderConfiguration;
1018
1019
constructor(
1020
useCache: boolean,
1021
readonly workspaceFolder: IWorkspaceFolder,
1022
configFolderRelativePath: string,
1023
private readonly workbenchState: WorkbenchState,
1024
private workspaceTrusted: boolean,
1025
fileService: IFileService,
1026
uriIdentityService: IUriIdentityService,
1027
logService: ILogService,
1028
private readonly configurationCache: IConfigurationCache
1029
) {
1030
super();
1031
1032
this.scopes = WorkbenchState.WORKSPACE === this.workbenchState ? FOLDER_SCOPES : WORKSPACE_SCOPES;
1033
this.configurationFolder = uriIdentityService.extUri.joinPath(workspaceFolder.uri, configFolderRelativePath);
1034
this.cachedFolderConfiguration = new CachedFolderConfiguration(workspaceFolder.uri, configFolderRelativePath, { scopes: this.scopes, skipRestricted: this.isUntrusted() }, configurationCache, logService);
1035
if (useCache && this.configurationCache.needsCaching(workspaceFolder.uri)) {
1036
this.folderConfiguration = this.cachedFolderConfiguration;
1037
whenProviderRegistered(workspaceFolder.uri, fileService)
1038
.then(() => {
1039
this.folderConfiguration = this._register(this.createFileServiceBasedConfiguration(fileService, uriIdentityService, logService));
1040
this._register(this.folderConfiguration.onDidChange(e => this.onDidFolderConfigurationChange()));
1041
this.onDidFolderConfigurationChange();
1042
});
1043
} else {
1044
this.folderConfiguration = this._register(this.createFileServiceBasedConfiguration(fileService, uriIdentityService, logService));
1045
this._register(this.folderConfiguration.onDidChange(e => this.onDidFolderConfigurationChange()));
1046
}
1047
}
1048
1049
loadConfiguration(): Promise<ConfigurationModel> {
1050
return this.folderConfiguration.loadConfiguration();
1051
}
1052
1053
updateWorkspaceTrust(trusted: boolean): ConfigurationModel {
1054
this.workspaceTrusted = trusted;
1055
return this.reparse();
1056
}
1057
1058
reparse(): ConfigurationModel {
1059
const configurationModel = this.folderConfiguration.reparse({ scopes: this.scopes, skipRestricted: this.isUntrusted() });
1060
this.updateCache();
1061
return configurationModel;
1062
}
1063
1064
getRestrictedSettings(): string[] {
1065
return this.folderConfiguration.getRestrictedSettings();
1066
}
1067
1068
private isUntrusted(): boolean {
1069
return !this.workspaceTrusted;
1070
}
1071
1072
private onDidFolderConfigurationChange(): void {
1073
this.updateCache();
1074
this._onDidChange.fire();
1075
}
1076
1077
private createFileServiceBasedConfiguration(fileService: IFileService, uriIdentityService: IUriIdentityService, logService: ILogService) {
1078
const settingsResource = uriIdentityService.extUri.joinPath(this.configurationFolder, `${FOLDER_SETTINGS_NAME}.json`);
1079
const standAloneConfigurationResources: [string, URI][] = [TASKS_CONFIGURATION_KEY, LAUNCH_CONFIGURATION_KEY, MCP_CONFIGURATION_KEY].map(name => ([name, uriIdentityService.extUri.joinPath(this.configurationFolder, `${name}.json`)]));
1080
return new FileServiceBasedConfiguration(this.configurationFolder.toString(), settingsResource, standAloneConfigurationResources, { scopes: this.scopes, skipRestricted: this.isUntrusted() }, fileService, uriIdentityService, logService);
1081
}
1082
1083
private async updateCache(): Promise<void> {
1084
if (this.configurationCache.needsCaching(this.configurationFolder) && this.folderConfiguration instanceof FileServiceBasedConfiguration) {
1085
const [settingsContent, standAloneConfigurationContents] = await this.folderConfiguration.resolveContents();
1086
this.cachedFolderConfiguration.updateConfiguration(settingsContent, standAloneConfigurationContents);
1087
}
1088
}
1089
}
1090
1091