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