Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/services/configuration/browser/configurationService.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 { ResourceMap } from '../../../../base/common/map.js';
9
import { equals } from '../../../../base/common/objects.js';
10
import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js';
11
import { Queue, Barrier, Promises, Delayer, Throttler } from '../../../../base/common/async.js';
12
import { IJSONContributionRegistry, Extensions as JSONExtensions } from '../../../../platform/jsonschemas/common/jsonContributionRegistry.js';
13
import { IWorkspaceContextService, Workspace as BaseWorkspace, WorkbenchState, IWorkspaceFolder, IWorkspaceFoldersChangeEvent, WorkspaceFolder, toWorkspaceFolder, isWorkspaceFolder, IWorkspaceFoldersWillChangeEvent, IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier, IAnyWorkspaceIdentifier } from '../../../../platform/workspace/common/workspace.js';
14
import { ConfigurationModel, ConfigurationChangeEvent, mergeChanges } from '../../../../platform/configuration/common/configurationModels.js';
15
import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, isConfigurationOverrides, IConfigurationData, IConfigurationValue, IConfigurationChange, ConfigurationTargetToString, IConfigurationUpdateOverrides, isConfigurationUpdateOverrides, IConfigurationService, IConfigurationUpdateOptions } from '../../../../platform/configuration/common/configuration.js';
16
import { IPolicyConfiguration, NullPolicyConfiguration, PolicyConfiguration } from '../../../../platform/configuration/common/configurations.js';
17
import { Configuration } from '../common/configurationModels.js';
18
import { FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId, IConfigurationCache, machineSettingsSchemaId, LOCAL_MACHINE_SCOPES, IWorkbenchConfigurationService, RestrictedSettings, PROFILE_SCOPES, LOCAL_MACHINE_PROFILE_SCOPES, profileSettingsSchemaId, APPLY_ALL_PROFILES_SETTING, APPLICATION_SCOPES } from '../common/configuration.js';
19
import { Registry } from '../../../../platform/registry/common/platform.js';
20
import { IConfigurationRegistry, Extensions, allSettings, windowSettings, resourceSettings, applicationSettings, machineSettings, machineOverridableSettings, ConfigurationScope, IConfigurationPropertySchema, keyFromOverrideIdentifiers, OVERRIDE_PROPERTY_PATTERN, resourceLanguageSettingsSchemaId, configurationDefaultsSchemaId, applicationMachineSettings } from '../../../../platform/configuration/common/configurationRegistry.js';
21
import { IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, getStoredWorkspaceFolder, toWorkspaceFolders } from '../../../../platform/workspaces/common/workspaces.js';
22
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
23
import { ConfigurationEditing, EditableConfigurationTarget } from '../common/configurationEditing.js';
24
import { WorkspaceConfiguration, FolderConfiguration, RemoteUserConfiguration, UserConfiguration, DefaultConfiguration, ApplicationConfiguration } from './configuration.js';
25
import { IJSONSchema, IJSONSchemaMap } from '../../../../base/common/jsonSchema.js';
26
import { mark } from '../../../../base/common/performance.js';
27
import { IRemoteAgentService } from '../../remote/common/remoteAgentService.js';
28
import { IFileService } from '../../../../platform/files/common/files.js';
29
import { IWorkbenchEnvironmentService } from '../../environment/common/environmentService.js';
30
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, WorkbenchPhase, Extensions as WorkbenchExtensions, registerWorkbenchContribution2 } from '../../../common/contributions.js';
31
import { ILifecycleService, LifecyclePhase } from '../../lifecycle/common/lifecycle.js';
32
import { ILogService } from '../../../../platform/log/common/log.js';
33
import { toErrorMessage } from '../../../../base/common/errorMessage.js';
34
import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';
35
import { IWorkspaceTrustManagementService } from '../../../../platform/workspace/common/workspaceTrust.js';
36
import { delta, distinct, equals as arrayEquals } from '../../../../base/common/arrays.js';
37
import { IStringDictionary } from '../../../../base/common/collections.js';
38
import { IExtensionService } from '../../extensions/common/extensions.js';
39
import { IWorkbenchAssignmentService } from '../../assignment/common/assignmentService.js';
40
import { isUndefined } from '../../../../base/common/types.js';
41
import { localize } from '../../../../nls.js';
42
import { DidChangeUserDataProfileEvent, IUserDataProfileService } from '../../userDataProfile/common/userDataProfile.js';
43
import { IPolicyService, NullPolicyService } from '../../../../platform/policy/common/policy.js';
44
import { IUserDataProfile, IUserDataProfilesService } from '../../../../platform/userDataProfile/common/userDataProfile.js';
45
import { IJSONEditingService } from '../common/jsonEditing.js';
46
import { IBrowserWorkbenchEnvironmentService } from '../../environment/browser/environmentService.js';
47
import { workbenchConfigurationNodeBase } from '../../../common/configuration.js';
48
import { mainWindow } from '../../../../base/browser/window.js';
49
import { runWhenWindowIdle } from '../../../../base/browser/dom.js';
50
51
function getLocalUserConfigurationScopes(userDataProfile: IUserDataProfile, hasRemote: boolean): ConfigurationScope[] | undefined {
52
const isDefaultProfile = userDataProfile.isDefault || userDataProfile.useDefaultFlags?.settings;
53
if (isDefaultProfile) {
54
return hasRemote ? LOCAL_MACHINE_SCOPES : undefined;
55
}
56
return hasRemote ? LOCAL_MACHINE_PROFILE_SCOPES : PROFILE_SCOPES;
57
}
58
59
class Workspace extends BaseWorkspace {
60
initialized: boolean = false;
61
}
62
63
export class WorkspaceService extends Disposable implements IWorkbenchConfigurationService, IWorkspaceContextService {
64
65
public _serviceBrand: undefined;
66
67
private workspace!: Workspace;
68
private initRemoteUserConfigurationBarrier: Barrier;
69
private completeWorkspaceBarrier: Barrier;
70
private readonly configurationCache: IConfigurationCache;
71
private _configuration: Configuration;
72
private initialized: boolean = false;
73
private readonly defaultConfiguration: DefaultConfiguration;
74
private readonly policyConfiguration: IPolicyConfiguration;
75
private applicationConfiguration: ApplicationConfiguration | null = null;
76
private readonly applicationConfigurationDisposables: DisposableStore;
77
private readonly localUserConfiguration: UserConfiguration;
78
private readonly remoteUserConfiguration: RemoteUserConfiguration | null = null;
79
private readonly workspaceConfiguration: WorkspaceConfiguration;
80
private cachedFolderConfigs: ResourceMap<FolderConfiguration>;
81
private readonly workspaceEditingQueue: Queue<void>;
82
83
private readonly _onDidChangeConfiguration: Emitter<IConfigurationChangeEvent> = this._register(new Emitter<IConfigurationChangeEvent>());
84
public readonly onDidChangeConfiguration: Event<IConfigurationChangeEvent> = this._onDidChangeConfiguration.event;
85
86
protected readonly _onWillChangeWorkspaceFolders: Emitter<IWorkspaceFoldersWillChangeEvent> = this._register(new Emitter<IWorkspaceFoldersWillChangeEvent>());
87
public readonly onWillChangeWorkspaceFolders: Event<IWorkspaceFoldersWillChangeEvent> = this._onWillChangeWorkspaceFolders.event;
88
89
private readonly _onDidChangeWorkspaceFolders: Emitter<IWorkspaceFoldersChangeEvent> = this._register(new Emitter<IWorkspaceFoldersChangeEvent>());
90
public readonly onDidChangeWorkspaceFolders: Event<IWorkspaceFoldersChangeEvent> = this._onDidChangeWorkspaceFolders.event;
91
92
private readonly _onDidChangeWorkspaceName: Emitter<void> = this._register(new Emitter<void>());
93
public readonly onDidChangeWorkspaceName: Event<void> = this._onDidChangeWorkspaceName.event;
94
95
private readonly _onDidChangeWorkbenchState: Emitter<WorkbenchState> = this._register(new Emitter<WorkbenchState>());
96
public readonly onDidChangeWorkbenchState: Event<WorkbenchState> = this._onDidChangeWorkbenchState.event;
97
98
private isWorkspaceTrusted: boolean = true;
99
100
private _restrictedSettings: RestrictedSettings = { default: [] };
101
get restrictedSettings() { return this._restrictedSettings; }
102
private readonly _onDidChangeRestrictedSettings = this._register(new Emitter<RestrictedSettings>());
103
public readonly onDidChangeRestrictedSettings = this._onDidChangeRestrictedSettings.event;
104
105
private readonly configurationRegistry: IConfigurationRegistry;
106
107
private instantiationService: IInstantiationService | undefined;
108
private configurationEditing: Promise<ConfigurationEditing> | undefined;
109
110
constructor(
111
{ remoteAuthority, configurationCache }: { remoteAuthority?: string; configurationCache: IConfigurationCache },
112
environmentService: IBrowserWorkbenchEnvironmentService,
113
private readonly userDataProfileService: IUserDataProfileService,
114
private readonly userDataProfilesService: IUserDataProfilesService,
115
private readonly fileService: IFileService,
116
private readonly remoteAgentService: IRemoteAgentService,
117
private readonly uriIdentityService: IUriIdentityService,
118
private readonly logService: ILogService,
119
policyService: IPolicyService
120
) {
121
super();
122
123
this.configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
124
125
this.initRemoteUserConfigurationBarrier = new Barrier();
126
this.completeWorkspaceBarrier = new Barrier();
127
this.defaultConfiguration = this._register(new DefaultConfiguration(configurationCache, environmentService, logService));
128
this.policyConfiguration = policyService instanceof NullPolicyService ? new NullPolicyConfiguration() : this._register(new PolicyConfiguration(this.defaultConfiguration, policyService, logService));
129
this.configurationCache = configurationCache;
130
this._configuration = new Configuration(this.defaultConfiguration.configurationModel, this.policyConfiguration.configurationModel, ConfigurationModel.createEmptyModel(logService), ConfigurationModel.createEmptyModel(logService), ConfigurationModel.createEmptyModel(logService), ConfigurationModel.createEmptyModel(logService), new ResourceMap(), ConfigurationModel.createEmptyModel(logService), new ResourceMap<ConfigurationModel>(), this.workspace, logService);
131
this.applicationConfigurationDisposables = this._register(new DisposableStore());
132
this.createApplicationConfiguration();
133
this.localUserConfiguration = this._register(new UserConfiguration(userDataProfileService.currentProfile.settingsResource, userDataProfileService.currentProfile.tasksResource, userDataProfileService.currentProfile.mcpResource, { scopes: getLocalUserConfigurationScopes(userDataProfileService.currentProfile, !!remoteAuthority) }, fileService, uriIdentityService, logService));
134
this.cachedFolderConfigs = new ResourceMap<FolderConfiguration>();
135
this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration)));
136
if (remoteAuthority) {
137
const remoteUserConfiguration = this.remoteUserConfiguration = this._register(new RemoteUserConfiguration(remoteAuthority, configurationCache, fileService, uriIdentityService, remoteAgentService, logService));
138
this._register(remoteUserConfiguration.onDidInitialize(remoteUserConfigurationModel => {
139
this._register(remoteUserConfiguration.onDidChangeConfiguration(remoteUserConfigurationModel => this.onRemoteUserConfigurationChanged(remoteUserConfigurationModel)));
140
this.onRemoteUserConfigurationChanged(remoteUserConfigurationModel);
141
this.initRemoteUserConfigurationBarrier.open();
142
}));
143
} else {
144
this.initRemoteUserConfigurationBarrier.open();
145
}
146
147
this.workspaceConfiguration = this._register(new WorkspaceConfiguration(configurationCache, fileService, uriIdentityService, logService));
148
this._register(this.workspaceConfiguration.onDidUpdateConfiguration(fromCache => {
149
this.onWorkspaceConfigurationChanged(fromCache).then(() => {
150
this.workspace.initialized = this.workspaceConfiguration.initialized;
151
this.checkAndMarkWorkspaceComplete(fromCache);
152
});
153
}));
154
155
this._register(this.defaultConfiguration.onDidChangeConfiguration(({ properties, defaults }) => this.onDefaultConfigurationChanged(defaults, properties)));
156
this._register(this.policyConfiguration.onDidChangeConfiguration(configurationModel => this.onPolicyConfigurationChanged(configurationModel)));
157
this._register(userDataProfileService.onDidChangeCurrentProfile(e => this.onUserDataProfileChanged(e)));
158
159
this.workspaceEditingQueue = new Queue<void>();
160
}
161
162
private createApplicationConfiguration(): void {
163
this.applicationConfigurationDisposables.clear();
164
if (this.userDataProfileService.currentProfile.isDefault || this.userDataProfileService.currentProfile.useDefaultFlags?.settings) {
165
this.applicationConfiguration = null;
166
} else {
167
this.applicationConfiguration = this.applicationConfigurationDisposables.add(this._register(new ApplicationConfiguration(this.userDataProfilesService, this.fileService, this.uriIdentityService, this.logService)));
168
this.applicationConfigurationDisposables.add(this.applicationConfiguration.onDidChangeConfiguration(configurationModel => this.onApplicationConfigurationChanged(configurationModel)));
169
}
170
}
171
172
// Workspace Context Service Impl
173
174
public async getCompleteWorkspace(): Promise<Workspace> {
175
await this.completeWorkspaceBarrier.wait();
176
return this.getWorkspace();
177
}
178
179
public getWorkspace(): Workspace {
180
return this.workspace;
181
}
182
183
public getWorkbenchState(): WorkbenchState {
184
// Workspace has configuration file
185
if (this.workspace.configuration) {
186
return WorkbenchState.WORKSPACE;
187
}
188
189
// Folder has single root
190
if (this.workspace.folders.length === 1) {
191
return WorkbenchState.FOLDER;
192
}
193
194
// Empty
195
return WorkbenchState.EMPTY;
196
}
197
198
public getWorkspaceFolder(resource: URI): IWorkspaceFolder | null {
199
return this.workspace.getFolder(resource);
200
}
201
202
public addFolders(foldersToAdd: IWorkspaceFolderCreationData[], index?: number): Promise<void> {
203
return this.updateFolders(foldersToAdd, [], index);
204
}
205
206
public removeFolders(foldersToRemove: URI[]): Promise<void> {
207
return this.updateFolders([], foldersToRemove);
208
}
209
210
public async updateFolders(foldersToAdd: IWorkspaceFolderCreationData[], foldersToRemove: URI[], index?: number): Promise<void> {
211
return this.workspaceEditingQueue.queue(() => this.doUpdateFolders(foldersToAdd, foldersToRemove, index));
212
}
213
214
public isInsideWorkspace(resource: URI): boolean {
215
return !!this.getWorkspaceFolder(resource);
216
}
217
218
public isCurrentWorkspace(workspaceIdOrFolder: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI): boolean {
219
switch (this.getWorkbenchState()) {
220
case WorkbenchState.FOLDER: {
221
let folderUri: URI | undefined = undefined;
222
if (URI.isUri(workspaceIdOrFolder)) {
223
folderUri = workspaceIdOrFolder;
224
} else if (isSingleFolderWorkspaceIdentifier(workspaceIdOrFolder)) {
225
folderUri = workspaceIdOrFolder.uri;
226
}
227
228
return URI.isUri(folderUri) && this.uriIdentityService.extUri.isEqual(folderUri, this.workspace.folders[0].uri);
229
}
230
case WorkbenchState.WORKSPACE:
231
return isWorkspaceIdentifier(workspaceIdOrFolder) && this.workspace.id === workspaceIdOrFolder.id;
232
}
233
return false;
234
}
235
236
private async doUpdateFolders(foldersToAdd: IWorkspaceFolderCreationData[], foldersToRemove: URI[], index?: number): Promise<void> {
237
if (this.getWorkbenchState() !== WorkbenchState.WORKSPACE) {
238
return Promise.resolve(undefined); // we need a workspace to begin with
239
}
240
241
if (foldersToAdd.length + foldersToRemove.length === 0) {
242
return Promise.resolve(undefined); // nothing to do
243
}
244
245
let foldersHaveChanged = false;
246
247
// Remove first (if any)
248
let currentWorkspaceFolders = this.getWorkspace().folders;
249
let newStoredFolders: IStoredWorkspaceFolder[] = currentWorkspaceFolders.map(f => f.raw).filter((folder, index): folder is IStoredWorkspaceFolder => {
250
if (!isStoredWorkspaceFolder(folder)) {
251
return true; // keep entries which are unrelated
252
}
253
254
return !this.contains(foldersToRemove, currentWorkspaceFolders[index].uri); // keep entries which are unrelated
255
});
256
257
foldersHaveChanged = currentWorkspaceFolders.length !== newStoredFolders.length;
258
259
// Add afterwards (if any)
260
if (foldersToAdd.length) {
261
262
// Recompute current workspace folders if we have folders to add
263
const workspaceConfigPath = this.getWorkspace().configuration!;
264
const workspaceConfigFolder = this.uriIdentityService.extUri.dirname(workspaceConfigPath);
265
currentWorkspaceFolders = toWorkspaceFolders(newStoredFolders, workspaceConfigPath, this.uriIdentityService.extUri);
266
const currentWorkspaceFolderUris = currentWorkspaceFolders.map(folder => folder.uri);
267
268
const storedFoldersToAdd: IStoredWorkspaceFolder[] = [];
269
270
for (const folderToAdd of foldersToAdd) {
271
const folderURI = folderToAdd.uri;
272
if (this.contains(currentWorkspaceFolderUris, folderURI)) {
273
continue; // already existing
274
}
275
try {
276
const result = await this.fileService.stat(folderURI);
277
if (!result.isDirectory) {
278
continue;
279
}
280
} catch (e) { /* Ignore */ }
281
storedFoldersToAdd.push(getStoredWorkspaceFolder(folderURI, false, folderToAdd.name, workspaceConfigFolder, this.uriIdentityService.extUri));
282
}
283
284
// Apply to array of newStoredFolders
285
if (storedFoldersToAdd.length > 0) {
286
foldersHaveChanged = true;
287
288
if (typeof index === 'number' && index >= 0 && index < newStoredFolders.length) {
289
newStoredFolders = newStoredFolders.slice(0);
290
newStoredFolders.splice(index, 0, ...storedFoldersToAdd);
291
} else {
292
newStoredFolders = [...newStoredFolders, ...storedFoldersToAdd];
293
}
294
}
295
}
296
297
// Set folders if we recorded a change
298
if (foldersHaveChanged) {
299
return this.setFolders(newStoredFolders);
300
}
301
302
return Promise.resolve(undefined);
303
}
304
305
private async setFolders(folders: IStoredWorkspaceFolder[]): Promise<void> {
306
if (!this.instantiationService) {
307
throw new Error('Cannot update workspace folders because workspace service is not yet ready to accept writes.');
308
}
309
310
await this.instantiationService.invokeFunction(accessor => this.workspaceConfiguration.setFolders(folders, accessor.get(IJSONEditingService)));
311
return this.onWorkspaceConfigurationChanged(false);
312
}
313
314
private contains(resources: URI[], toCheck: URI): boolean {
315
return resources.some(resource => this.uriIdentityService.extUri.isEqual(resource, toCheck));
316
}
317
318
// Workspace Configuration Service Impl
319
320
getConfigurationData(): IConfigurationData {
321
return this._configuration.toData();
322
}
323
324
getValue<T>(): T;
325
getValue<T>(section: string): T;
326
getValue<T>(overrides: IConfigurationOverrides): T;
327
getValue<T>(section: string, overrides: IConfigurationOverrides): T;
328
getValue(arg1?: any, arg2?: any): any {
329
const section = typeof arg1 === 'string' ? arg1 : undefined;
330
const overrides = isConfigurationOverrides(arg1) ? arg1 : isConfigurationOverrides(arg2) ? arg2 : undefined;
331
return this._configuration.getValue(section, overrides);
332
}
333
334
updateValue(key: string, value: any): Promise<void>;
335
updateValue(key: string, value: unknown, overrides: IConfigurationOverrides | IConfigurationUpdateOverrides): Promise<void>;
336
updateValue(key: string, value: unknown, target: ConfigurationTarget): Promise<void>;
337
updateValue(key: string, value: unknown, overrides: IConfigurationOverrides | IConfigurationUpdateOverrides, target: ConfigurationTarget, options?: IConfigurationUpdateOptions): Promise<void>;
338
async updateValue(key: string, value: unknown, arg3?: any, arg4?: any, options?: any): Promise<void> {
339
const overrides: IConfigurationUpdateOverrides | undefined = isConfigurationUpdateOverrides(arg3) ? arg3
340
: isConfigurationOverrides(arg3) ? { resource: arg3.resource, overrideIdentifiers: arg3.overrideIdentifier ? [arg3.overrideIdentifier] : undefined } : undefined;
341
const target: ConfigurationTarget | undefined = overrides ? arg4 : arg3;
342
const targets: ConfigurationTarget[] = target ? [target] : [];
343
344
if (overrides?.overrideIdentifiers) {
345
overrides.overrideIdentifiers = distinct(overrides.overrideIdentifiers);
346
overrides.overrideIdentifiers = overrides.overrideIdentifiers.length ? overrides.overrideIdentifiers : undefined;
347
}
348
349
if (!targets.length) {
350
if (overrides?.overrideIdentifiers && overrides.overrideIdentifiers.length > 1) {
351
throw new Error('Configuration Target is required while updating the value for multiple override identifiers');
352
}
353
const inspect = this.inspect(key, { resource: overrides?.resource, overrideIdentifier: overrides?.overrideIdentifiers ? overrides.overrideIdentifiers[0] : undefined });
354
targets.push(...this.deriveConfigurationTargets(key, value, inspect));
355
356
// Remove the setting, if the value is same as default value and is updated only in user target
357
if (equals(value, inspect.defaultValue) && targets.length === 1 && (targets[0] === ConfigurationTarget.USER || targets[0] === ConfigurationTarget.USER_LOCAL)) {
358
value = undefined;
359
}
360
}
361
362
await Promises.settled(targets.map(target => this.writeConfigurationValue(key, value, target, overrides, options)));
363
}
364
365
async reloadConfiguration(target?: ConfigurationTarget | IWorkspaceFolder): Promise<void> {
366
if (target === undefined) {
367
this.reloadDefaultConfiguration();
368
const application = await this.reloadApplicationConfiguration(true);
369
const { local, remote } = await this.reloadUserConfiguration();
370
await this.reloadWorkspaceConfiguration();
371
await this.loadConfiguration(application, local, remote, true);
372
return;
373
}
374
375
if (isWorkspaceFolder(target)) {
376
await this.reloadWorkspaceFolderConfiguration(target);
377
return;
378
}
379
380
switch (target) {
381
case ConfigurationTarget.DEFAULT:
382
this.reloadDefaultConfiguration();
383
return;
384
385
case ConfigurationTarget.USER: {
386
const { local, remote } = await this.reloadUserConfiguration();
387
await this.loadConfiguration(this._configuration.applicationConfiguration, local, remote, true);
388
return;
389
}
390
case ConfigurationTarget.USER_LOCAL:
391
await this.reloadLocalUserConfiguration();
392
return;
393
394
case ConfigurationTarget.USER_REMOTE:
395
await this.reloadRemoteUserConfiguration();
396
return;
397
398
case ConfigurationTarget.WORKSPACE:
399
case ConfigurationTarget.WORKSPACE_FOLDER:
400
await this.reloadWorkspaceConfiguration();
401
return;
402
}
403
}
404
405
hasCachedConfigurationDefaultsOverrides(): boolean {
406
return this.defaultConfiguration.hasCachedConfigurationDefaultsOverrides();
407
}
408
409
inspect<T>(key: string, overrides?: IConfigurationOverrides): IConfigurationValue<T> {
410
return this._configuration.inspect<T>(key, overrides);
411
}
412
413
keys(): {
414
default: string[];
415
user: string[];
416
workspace: string[];
417
workspaceFolder: string[];
418
} {
419
return this._configuration.keys();
420
}
421
422
public async whenRemoteConfigurationLoaded(): Promise<void> {
423
await this.initRemoteUserConfigurationBarrier.wait();
424
}
425
426
/**
427
* At present, all workspaces (empty, single-folder, multi-root) in local and remote
428
* can be initialized without requiring extension host except following case:
429
*
430
* A multi root workspace with .code-workspace file that has to be resolved by an extension.
431
* Because of readonly `rootPath` property in extension API we have to resolve multi root workspace
432
* before extension host starts so that `rootPath` can be set to first folder.
433
*
434
* This restriction is lifted partially for web in `MainThreadWorkspace`.
435
* In web, we start extension host with empty `rootPath` in this case.
436
*
437
* Related root path issue discussion is being tracked here - https://github.com/microsoft/vscode/issues/69335
438
*/
439
async initialize(arg: IAnyWorkspaceIdentifier): Promise<void> {
440
mark('code/willInitWorkspaceService');
441
442
const trigger = this.initialized;
443
this.initialized = false;
444
const workspace = await this.createWorkspace(arg);
445
await this.updateWorkspaceAndInitializeConfiguration(workspace, trigger);
446
this.checkAndMarkWorkspaceComplete(false);
447
448
mark('code/didInitWorkspaceService');
449
}
450
451
updateWorkspaceTrust(trusted: boolean): void {
452
if (this.isWorkspaceTrusted !== trusted) {
453
this.isWorkspaceTrusted = trusted;
454
const data = this._configuration.toData();
455
const folderConfigurationModels: (ConfigurationModel | undefined)[] = [];
456
for (const folder of this.workspace.folders) {
457
const folderConfiguration = this.cachedFolderConfigs.get(folder.uri);
458
let configurationModel: ConfigurationModel | undefined;
459
if (folderConfiguration) {
460
configurationModel = folderConfiguration.updateWorkspaceTrust(this.isWorkspaceTrusted);
461
this._configuration.updateFolderConfiguration(folder.uri, configurationModel);
462
}
463
folderConfigurationModels.push(configurationModel);
464
}
465
if (this.getWorkbenchState() === WorkbenchState.FOLDER) {
466
if (folderConfigurationModels[0]) {
467
this._configuration.updateWorkspaceConfiguration(folderConfigurationModels[0]);
468
}
469
} else {
470
this._configuration.updateWorkspaceConfiguration(this.workspaceConfiguration.updateWorkspaceTrust(this.isWorkspaceTrusted));
471
}
472
this.updateRestrictedSettings();
473
474
let keys: string[] = [];
475
if (this.restrictedSettings.userLocal) {
476
keys.push(...this.restrictedSettings.userLocal);
477
}
478
if (this.restrictedSettings.userRemote) {
479
keys.push(...this.restrictedSettings.userRemote);
480
}
481
if (this.restrictedSettings.workspace) {
482
keys.push(...this.restrictedSettings.workspace);
483
}
484
this.restrictedSettings.workspaceFolder?.forEach((value) => keys.push(...value));
485
keys = distinct(keys);
486
if (keys.length) {
487
this.triggerConfigurationChange({ keys, overrides: [] }, { data, workspace: this.workspace }, ConfigurationTarget.WORKSPACE);
488
}
489
}
490
}
491
492
acquireInstantiationService(instantiationService: IInstantiationService): void {
493
this.instantiationService = instantiationService;
494
}
495
496
isSettingAppliedForAllProfiles(key: string): boolean {
497
const scope = this.configurationRegistry.getConfigurationProperties()[key]?.scope;
498
if (scope && APPLICATION_SCOPES.includes(scope)) {
499
return true;
500
}
501
const allProfilesSettings = this.getValue<string[]>(APPLY_ALL_PROFILES_SETTING) ?? [];
502
return Array.isArray(allProfilesSettings) && allProfilesSettings.includes(key);
503
}
504
505
private async createWorkspace(arg: IAnyWorkspaceIdentifier): Promise<Workspace> {
506
if (isWorkspaceIdentifier(arg)) {
507
return this.createMultiFolderWorkspace(arg);
508
}
509
510
if (isSingleFolderWorkspaceIdentifier(arg)) {
511
return this.createSingleFolderWorkspace(arg);
512
}
513
514
return this.createEmptyWorkspace(arg);
515
}
516
517
private async createMultiFolderWorkspace(workspaceIdentifier: IWorkspaceIdentifier): Promise<Workspace> {
518
await this.workspaceConfiguration.initialize({ id: workspaceIdentifier.id, configPath: workspaceIdentifier.configPath }, this.isWorkspaceTrusted);
519
const workspaceConfigPath = workspaceIdentifier.configPath;
520
const workspaceFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), workspaceConfigPath, this.uriIdentityService.extUri);
521
const workspaceId = workspaceIdentifier.id;
522
const workspace = new Workspace(workspaceId, workspaceFolders, this.workspaceConfiguration.isTransient(), workspaceConfigPath, uri => this.uriIdentityService.extUri.ignorePathCasing(uri));
523
workspace.initialized = this.workspaceConfiguration.initialized;
524
return workspace;
525
}
526
527
private createSingleFolderWorkspace(singleFolderWorkspaceIdentifier: ISingleFolderWorkspaceIdentifier): Workspace {
528
const workspace = new Workspace(singleFolderWorkspaceIdentifier.id, [toWorkspaceFolder(singleFolderWorkspaceIdentifier.uri)], false, null, uri => this.uriIdentityService.extUri.ignorePathCasing(uri));
529
workspace.initialized = true;
530
return workspace;
531
}
532
533
private createEmptyWorkspace(emptyWorkspaceIdentifier: IEmptyWorkspaceIdentifier): Promise<Workspace> {
534
const workspace = new Workspace(emptyWorkspaceIdentifier.id, [], false, null, uri => this.uriIdentityService.extUri.ignorePathCasing(uri));
535
workspace.initialized = true;
536
return Promise.resolve(workspace);
537
}
538
539
private checkAndMarkWorkspaceComplete(fromCache: boolean): void {
540
if (!this.completeWorkspaceBarrier.isOpen() && this.workspace.initialized) {
541
this.completeWorkspaceBarrier.open();
542
this.validateWorkspaceFoldersAndReload(fromCache);
543
}
544
}
545
546
private async updateWorkspaceAndInitializeConfiguration(workspace: Workspace, trigger: boolean): Promise<void> {
547
const hasWorkspaceBefore = !!this.workspace;
548
let previousState: WorkbenchState | undefined;
549
let previousWorkspacePath: string | undefined;
550
let previousFolders: WorkspaceFolder[] = [];
551
552
if (hasWorkspaceBefore) {
553
previousState = this.getWorkbenchState();
554
previousWorkspacePath = this.workspace.configuration ? this.workspace.configuration.fsPath : undefined;
555
previousFolders = this.workspace.folders;
556
this.workspace.update(workspace);
557
} else {
558
this.workspace = workspace;
559
}
560
561
await this.initializeConfiguration(trigger);
562
563
// Trigger changes after configuration initialization so that configuration is up to date.
564
if (hasWorkspaceBefore) {
565
const newState = this.getWorkbenchState();
566
if (previousState && newState !== previousState) {
567
this._onDidChangeWorkbenchState.fire(newState);
568
}
569
570
const newWorkspacePath = this.workspace.configuration ? this.workspace.configuration.fsPath : undefined;
571
if (previousWorkspacePath && newWorkspacePath !== previousWorkspacePath || newState !== previousState) {
572
this._onDidChangeWorkspaceName.fire();
573
}
574
575
const folderChanges = this.compareFolders(previousFolders, this.workspace.folders);
576
if (folderChanges && (folderChanges.added.length || folderChanges.removed.length || folderChanges.changed.length)) {
577
await this.handleWillChangeWorkspaceFolders(folderChanges, false);
578
this._onDidChangeWorkspaceFolders.fire(folderChanges);
579
}
580
}
581
582
if (!this.localUserConfiguration.hasTasksLoaded) {
583
// Reload local user configuration again to load user tasks
584
this._register(runWhenWindowIdle(mainWindow, () => this.reloadLocalUserConfiguration(false, this._configuration.localUserConfiguration)));
585
}
586
}
587
588
private compareFolders(currentFolders: IWorkspaceFolder[], newFolders: IWorkspaceFolder[]): IWorkspaceFoldersChangeEvent {
589
const result: IWorkspaceFoldersChangeEvent = { added: [], removed: [], changed: [] };
590
result.added = newFolders.filter(newFolder => !currentFolders.some(currentFolder => newFolder.uri.toString() === currentFolder.uri.toString()));
591
for (let currentIndex = 0; currentIndex < currentFolders.length; currentIndex++) {
592
const currentFolder = currentFolders[currentIndex];
593
let newIndex = 0;
594
for (newIndex = 0; newIndex < newFolders.length && currentFolder.uri.toString() !== newFolders[newIndex].uri.toString(); newIndex++) { }
595
if (newIndex < newFolders.length) {
596
if (currentIndex !== newIndex || currentFolder.name !== newFolders[newIndex].name) {
597
result.changed.push(currentFolder);
598
}
599
} else {
600
result.removed.push(currentFolder);
601
}
602
}
603
return result;
604
}
605
606
private async initializeConfiguration(trigger: boolean): Promise<void> {
607
await this.defaultConfiguration.initialize();
608
609
const initPolicyConfigurationPromise = this.policyConfiguration.initialize();
610
const initApplicationConfigurationPromise = this.applicationConfiguration ? this.applicationConfiguration.initialize() : Promise.resolve(ConfigurationModel.createEmptyModel(this.logService));
611
const initUserConfiguration = async () => {
612
mark('code/willInitUserConfiguration');
613
const result = await Promise.all([this.localUserConfiguration.initialize(), this.remoteUserConfiguration ? this.remoteUserConfiguration.initialize() : Promise.resolve(ConfigurationModel.createEmptyModel(this.logService))]);
614
if (this.applicationConfiguration) {
615
const applicationConfigurationModel = await initApplicationConfigurationPromise;
616
result[0] = this.localUserConfiguration.reparse({ exclude: applicationConfigurationModel.getValue(APPLY_ALL_PROFILES_SETTING) });
617
}
618
mark('code/didInitUserConfiguration');
619
return result;
620
};
621
622
const [, application, [local, remote]] = await Promise.all([
623
initPolicyConfigurationPromise,
624
initApplicationConfigurationPromise,
625
initUserConfiguration()
626
]);
627
628
mark('code/willInitWorkspaceConfiguration');
629
await this.loadConfiguration(application, local, remote, trigger);
630
mark('code/didInitWorkspaceConfiguration');
631
}
632
633
private reloadDefaultConfiguration(): void {
634
this.onDefaultConfigurationChanged(this.defaultConfiguration.reload());
635
}
636
637
private async reloadApplicationConfiguration(donotTrigger?: boolean): Promise<ConfigurationModel> {
638
if (!this.applicationConfiguration) {
639
return ConfigurationModel.createEmptyModel(this.logService);
640
}
641
const model = await this.applicationConfiguration.loadConfiguration();
642
if (!donotTrigger) {
643
this.onApplicationConfigurationChanged(model);
644
}
645
return model;
646
}
647
648
private async reloadUserConfiguration(): Promise<{ local: ConfigurationModel; remote: ConfigurationModel }> {
649
const [local, remote] = await Promise.all([this.reloadLocalUserConfiguration(true), this.reloadRemoteUserConfiguration(true)]);
650
return { local, remote };
651
}
652
653
async reloadLocalUserConfiguration(donotTrigger?: boolean, settingsConfiguration?: ConfigurationModel): Promise<ConfigurationModel> {
654
const model = await this.localUserConfiguration.reload(settingsConfiguration);
655
if (!donotTrigger) {
656
this.onLocalUserConfigurationChanged(model);
657
}
658
return model;
659
}
660
661
private async reloadRemoteUserConfiguration(donotTrigger?: boolean): Promise<ConfigurationModel> {
662
if (this.remoteUserConfiguration) {
663
const model = await this.remoteUserConfiguration.reload();
664
if (!donotTrigger) {
665
this.onRemoteUserConfigurationChanged(model);
666
}
667
return model;
668
}
669
return ConfigurationModel.createEmptyModel(this.logService);
670
}
671
672
private async reloadWorkspaceConfiguration(): Promise<void> {
673
const workbenchState = this.getWorkbenchState();
674
if (workbenchState === WorkbenchState.FOLDER) {
675
return this.onWorkspaceFolderConfigurationChanged(this.workspace.folders[0]);
676
}
677
if (workbenchState === WorkbenchState.WORKSPACE) {
678
return this.workspaceConfiguration.reload().then(() => this.onWorkspaceConfigurationChanged(false));
679
}
680
}
681
682
private reloadWorkspaceFolderConfiguration(folder: IWorkspaceFolder): Promise<void> {
683
return this.onWorkspaceFolderConfigurationChanged(folder);
684
}
685
686
private async loadConfiguration(applicationConfigurationModel: ConfigurationModel, userConfigurationModel: ConfigurationModel, remoteUserConfigurationModel: ConfigurationModel, trigger: boolean): Promise<void> {
687
// reset caches
688
this.cachedFolderConfigs = new ResourceMap<FolderConfiguration>();
689
690
const folders = this.workspace.folders;
691
const folderConfigurations = await this.loadFolderConfigurations(folders);
692
693
const workspaceConfiguration = this.getWorkspaceConfigurationModel(folderConfigurations);
694
const folderConfigurationModels = new ResourceMap<ConfigurationModel>();
695
folderConfigurations.forEach((folderConfiguration, index) => folderConfigurationModels.set(folders[index].uri, folderConfiguration));
696
697
const currentConfiguration = this._configuration;
698
this._configuration = new Configuration(this.defaultConfiguration.configurationModel, this.policyConfiguration.configurationModel, applicationConfigurationModel, userConfigurationModel, remoteUserConfigurationModel, workspaceConfiguration, folderConfigurationModels, ConfigurationModel.createEmptyModel(this.logService), new ResourceMap<ConfigurationModel>(), this.workspace, this.logService);
699
700
this.initialized = true;
701
702
if (trigger) {
703
const change = this._configuration.compare(currentConfiguration);
704
this.triggerConfigurationChange(change, { data: currentConfiguration.toData(), workspace: this.workspace }, ConfigurationTarget.WORKSPACE);
705
}
706
707
this.updateRestrictedSettings();
708
}
709
710
private getWorkspaceConfigurationModel(folderConfigurations: ConfigurationModel[]): ConfigurationModel {
711
switch (this.getWorkbenchState()) {
712
case WorkbenchState.FOLDER:
713
return folderConfigurations[0];
714
case WorkbenchState.WORKSPACE:
715
return this.workspaceConfiguration.getConfiguration();
716
default:
717
return ConfigurationModel.createEmptyModel(this.logService);
718
}
719
}
720
721
private onUserDataProfileChanged(e: DidChangeUserDataProfileEvent): void {
722
e.join((async () => {
723
const promises: Promise<ConfigurationModel>[] = [];
724
promises.push(this.localUserConfiguration.reset(e.profile.settingsResource, e.profile.tasksResource, e.profile.mcpResource, { scopes: getLocalUserConfigurationScopes(e.profile, !!this.remoteUserConfiguration) }));
725
if (e.previous.isDefault !== e.profile.isDefault
726
|| !!e.previous.useDefaultFlags?.settings !== !!e.profile.useDefaultFlags?.settings) {
727
this.createApplicationConfiguration();
728
if (this.applicationConfiguration) {
729
promises.push(this.reloadApplicationConfiguration(true));
730
}
731
}
732
let [localUser, application] = await Promise.all(promises);
733
application = application ?? this._configuration.applicationConfiguration;
734
if (this.applicationConfiguration) {
735
localUser = this.localUserConfiguration.reparse({ exclude: application.getValue(APPLY_ALL_PROFILES_SETTING) });
736
}
737
await this.loadConfiguration(application, localUser, this._configuration.remoteUserConfiguration, true);
738
})());
739
}
740
741
private onDefaultConfigurationChanged(configurationModel: ConfigurationModel, properties?: string[]): void {
742
if (this.workspace) {
743
const previousData = this._configuration.toData();
744
const change = this._configuration.compareAndUpdateDefaultConfiguration(configurationModel, properties);
745
if (this.applicationConfiguration) {
746
this._configuration.updateApplicationConfiguration(this.applicationConfiguration.reparse());
747
}
748
if (this.remoteUserConfiguration) {
749
this._configuration.updateLocalUserConfiguration(this.localUserConfiguration.reparse());
750
this._configuration.updateRemoteUserConfiguration(this.remoteUserConfiguration.reparse());
751
}
752
if (this.getWorkbenchState() === WorkbenchState.FOLDER) {
753
const folderConfiguration = this.cachedFolderConfigs.get(this.workspace.folders[0].uri);
754
if (folderConfiguration) {
755
this._configuration.updateWorkspaceConfiguration(folderConfiguration.reparse());
756
this._configuration.updateFolderConfiguration(this.workspace.folders[0].uri, folderConfiguration.reparse());
757
}
758
} else {
759
this._configuration.updateWorkspaceConfiguration(this.workspaceConfiguration.reparseWorkspaceSettings());
760
for (const folder of this.workspace.folders) {
761
const folderConfiguration = this.cachedFolderConfigs.get(folder.uri);
762
if (folderConfiguration) {
763
this._configuration.updateFolderConfiguration(folder.uri, folderConfiguration.reparse());
764
}
765
}
766
}
767
this.triggerConfigurationChange(change, { data: previousData, workspace: this.workspace }, ConfigurationTarget.DEFAULT);
768
this.updateRestrictedSettings();
769
}
770
}
771
772
private onPolicyConfigurationChanged(policyConfiguration: ConfigurationModel): void {
773
const previous = { data: this._configuration.toData(), workspace: this.workspace };
774
const change = this._configuration.compareAndUpdatePolicyConfiguration(policyConfiguration);
775
this.triggerConfigurationChange(change, previous, ConfigurationTarget.DEFAULT);
776
}
777
778
private onApplicationConfigurationChanged(applicationConfiguration: ConfigurationModel): void {
779
const previous = { data: this._configuration.toData(), workspace: this.workspace };
780
const previousAllProfilesSettings = this._configuration.applicationConfiguration.getValue<string[]>(APPLY_ALL_PROFILES_SETTING) ?? [];
781
const change = this._configuration.compareAndUpdateApplicationConfiguration(applicationConfiguration);
782
const currentAllProfilesSettings = this.getValue<string[]>(APPLY_ALL_PROFILES_SETTING) ?? [];
783
const configurationProperties = this.configurationRegistry.getConfigurationProperties();
784
const changedKeys: string[] = [];
785
for (const changedKey of change.keys) {
786
const scope = configurationProperties[changedKey]?.scope;
787
if (scope && APPLICATION_SCOPES.includes(scope)) {
788
changedKeys.push(changedKey);
789
if (changedKey === APPLY_ALL_PROFILES_SETTING) {
790
for (const previousAllProfileSetting of previousAllProfilesSettings) {
791
if (!currentAllProfilesSettings.includes(previousAllProfileSetting)) {
792
changedKeys.push(previousAllProfileSetting);
793
}
794
}
795
for (const currentAllProfileSetting of currentAllProfilesSettings) {
796
if (!previousAllProfilesSettings.includes(currentAllProfileSetting)) {
797
changedKeys.push(currentAllProfileSetting);
798
}
799
}
800
}
801
}
802
else if (currentAllProfilesSettings.includes(changedKey)) {
803
changedKeys.push(changedKey);
804
}
805
}
806
change.keys = changedKeys;
807
if (change.keys.includes(APPLY_ALL_PROFILES_SETTING)) {
808
this._configuration.updateLocalUserConfiguration(this.localUserConfiguration.reparse({ exclude: currentAllProfilesSettings }));
809
}
810
this.triggerConfigurationChange(change, previous, ConfigurationTarget.USER);
811
}
812
813
private onLocalUserConfigurationChanged(userConfiguration: ConfigurationModel): void {
814
const previous = { data: this._configuration.toData(), workspace: this.workspace };
815
const change = this._configuration.compareAndUpdateLocalUserConfiguration(userConfiguration);
816
this.triggerConfigurationChange(change, previous, ConfigurationTarget.USER);
817
}
818
819
private onRemoteUserConfigurationChanged(userConfiguration: ConfigurationModel): void {
820
const previous = { data: this._configuration.toData(), workspace: this.workspace };
821
const change = this._configuration.compareAndUpdateRemoteUserConfiguration(userConfiguration);
822
this.triggerConfigurationChange(change, previous, ConfigurationTarget.USER);
823
}
824
825
private async onWorkspaceConfigurationChanged(fromCache: boolean): Promise<void> {
826
if (this.workspace && this.workspace.configuration) {
827
let newFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), this.workspace.configuration, this.uriIdentityService.extUri);
828
829
// Validate only if workspace is initialized
830
if (this.workspace.initialized) {
831
const { added, removed, changed } = this.compareFolders(this.workspace.folders, newFolders);
832
833
/* If changed validate new folders */
834
if (added.length || removed.length || changed.length) {
835
newFolders = await this.toValidWorkspaceFolders(newFolders);
836
}
837
/* Otherwise use existing */
838
else {
839
newFolders = this.workspace.folders;
840
}
841
}
842
843
await this.updateWorkspaceConfiguration(newFolders, this.workspaceConfiguration.getConfiguration(), fromCache);
844
}
845
}
846
847
private updateRestrictedSettings(): void {
848
const changed: string[] = [];
849
850
const allProperties = this.configurationRegistry.getConfigurationProperties();
851
const defaultRestrictedSettings: string[] = Object.keys(allProperties).filter(key => allProperties[key].restricted).sort((a, b) => a.localeCompare(b));
852
const defaultDelta = delta(defaultRestrictedSettings, this._restrictedSettings.default, (a, b) => a.localeCompare(b));
853
changed.push(...defaultDelta.added, ...defaultDelta.removed);
854
855
const application = (this.applicationConfiguration?.getRestrictedSettings() || []).sort((a, b) => a.localeCompare(b));
856
const applicationDelta = delta(application, this._restrictedSettings.application || [], (a, b) => a.localeCompare(b));
857
changed.push(...applicationDelta.added, ...applicationDelta.removed);
858
859
const userLocal = this.localUserConfiguration.getRestrictedSettings().sort((a, b) => a.localeCompare(b));
860
const userLocalDelta = delta(userLocal, this._restrictedSettings.userLocal || [], (a, b) => a.localeCompare(b));
861
changed.push(...userLocalDelta.added, ...userLocalDelta.removed);
862
863
const userRemote = (this.remoteUserConfiguration?.getRestrictedSettings() || []).sort((a, b) => a.localeCompare(b));
864
const userRemoteDelta = delta(userRemote, this._restrictedSettings.userRemote || [], (a, b) => a.localeCompare(b));
865
changed.push(...userRemoteDelta.added, ...userRemoteDelta.removed);
866
867
const workspaceFolderMap = new ResourceMap<ReadonlyArray<string>>();
868
for (const workspaceFolder of this.workspace.folders) {
869
const cachedFolderConfig = this.cachedFolderConfigs.get(workspaceFolder.uri);
870
const folderRestrictedSettings = (cachedFolderConfig?.getRestrictedSettings() || []).sort((a, b) => a.localeCompare(b));
871
if (folderRestrictedSettings.length) {
872
workspaceFolderMap.set(workspaceFolder.uri, folderRestrictedSettings);
873
}
874
const previous = this._restrictedSettings.workspaceFolder?.get(workspaceFolder.uri) || [];
875
const workspaceFolderDelta = delta(folderRestrictedSettings, previous, (a, b) => a.localeCompare(b));
876
changed.push(...workspaceFolderDelta.added, ...workspaceFolderDelta.removed);
877
}
878
879
const workspace = this.getWorkbenchState() === WorkbenchState.WORKSPACE ? this.workspaceConfiguration.getRestrictedSettings().sort((a, b) => a.localeCompare(b))
880
: this.workspace.folders[0] ? (workspaceFolderMap.get(this.workspace.folders[0].uri) || []) : [];
881
const workspaceDelta = delta(workspace, this._restrictedSettings.workspace || [], (a, b) => a.localeCompare(b));
882
changed.push(...workspaceDelta.added, ...workspaceDelta.removed);
883
884
if (changed.length) {
885
this._restrictedSettings = {
886
default: defaultRestrictedSettings,
887
application: application.length ? application : undefined,
888
userLocal: userLocal.length ? userLocal : undefined,
889
userRemote: userRemote.length ? userRemote : undefined,
890
workspace: workspace.length ? workspace : undefined,
891
workspaceFolder: workspaceFolderMap.size ? workspaceFolderMap : undefined,
892
};
893
this._onDidChangeRestrictedSettings.fire(this.restrictedSettings);
894
}
895
}
896
897
private async updateWorkspaceConfiguration(workspaceFolders: WorkspaceFolder[], configuration: ConfigurationModel, fromCache: boolean): Promise<void> {
898
const previous = { data: this._configuration.toData(), workspace: this.workspace };
899
const change = this._configuration.compareAndUpdateWorkspaceConfiguration(configuration);
900
const changes = this.compareFolders(this.workspace.folders, workspaceFolders);
901
if (changes.added.length || changes.removed.length || changes.changed.length) {
902
this.workspace.folders = workspaceFolders;
903
const change = await this.onFoldersChanged();
904
await this.handleWillChangeWorkspaceFolders(changes, fromCache);
905
this.triggerConfigurationChange(change, previous, ConfigurationTarget.WORKSPACE_FOLDER);
906
this._onDidChangeWorkspaceFolders.fire(changes);
907
} else {
908
this.triggerConfigurationChange(change, previous, ConfigurationTarget.WORKSPACE);
909
}
910
this.updateRestrictedSettings();
911
}
912
913
private async handleWillChangeWorkspaceFolders(changes: IWorkspaceFoldersChangeEvent, fromCache: boolean): Promise<void> {
914
const joiners: Promise<void>[] = [];
915
this._onWillChangeWorkspaceFolders.fire({
916
join(updateWorkspaceTrustStatePromise) {
917
joiners.push(updateWorkspaceTrustStatePromise);
918
},
919
changes,
920
fromCache
921
});
922
try { await Promises.settled(joiners); } catch (error) { /* Ignore */ }
923
}
924
925
private async onWorkspaceFolderConfigurationChanged(folder: IWorkspaceFolder): Promise<void> {
926
const [folderConfiguration] = await this.loadFolderConfigurations([folder]);
927
const previous = { data: this._configuration.toData(), workspace: this.workspace };
928
const folderConfigurationChange = this._configuration.compareAndUpdateFolderConfiguration(folder.uri, folderConfiguration);
929
if (this.getWorkbenchState() === WorkbenchState.FOLDER) {
930
const workspaceConfigurationChange = this._configuration.compareAndUpdateWorkspaceConfiguration(folderConfiguration);
931
this.triggerConfigurationChange(mergeChanges(folderConfigurationChange, workspaceConfigurationChange), previous, ConfigurationTarget.WORKSPACE);
932
} else {
933
this.triggerConfigurationChange(folderConfigurationChange, previous, ConfigurationTarget.WORKSPACE_FOLDER);
934
}
935
this.updateRestrictedSettings();
936
}
937
938
private async onFoldersChanged(): Promise<IConfigurationChange> {
939
const changes: IConfigurationChange[] = [];
940
941
// Remove the configurations of deleted folders
942
for (const key of this.cachedFolderConfigs.keys()) {
943
if (!this.workspace.folders.filter(folder => folder.uri.toString() === key.toString())[0]) {
944
const folderConfiguration = this.cachedFolderConfigs.get(key);
945
folderConfiguration!.dispose();
946
this.cachedFolderConfigs.delete(key);
947
changes.push(this._configuration.compareAndDeleteFolderConfiguration(key));
948
}
949
}
950
951
const toInitialize = this.workspace.folders.filter(folder => !this.cachedFolderConfigs.has(folder.uri));
952
if (toInitialize.length) {
953
const folderConfigurations = await this.loadFolderConfigurations(toInitialize);
954
folderConfigurations.forEach((folderConfiguration, index) => {
955
changes.push(this._configuration.compareAndUpdateFolderConfiguration(toInitialize[index].uri, folderConfiguration));
956
});
957
}
958
return mergeChanges(...changes);
959
}
960
961
private loadFolderConfigurations(folders: IWorkspaceFolder[]): Promise<ConfigurationModel[]> {
962
return Promise.all([...folders.map(folder => {
963
let folderConfiguration = this.cachedFolderConfigs.get(folder.uri);
964
if (!folderConfiguration) {
965
folderConfiguration = new FolderConfiguration(!this.initialized, folder, FOLDER_CONFIG_FOLDER_NAME, this.getWorkbenchState(), this.isWorkspaceTrusted, this.fileService, this.uriIdentityService, this.logService, this.configurationCache);
966
this._register(folderConfiguration.onDidChange(() => this.onWorkspaceFolderConfigurationChanged(folder)));
967
this.cachedFolderConfigs.set(folder.uri, this._register(folderConfiguration));
968
}
969
return folderConfiguration.loadConfiguration();
970
})]);
971
}
972
973
private async validateWorkspaceFoldersAndReload(fromCache: boolean): Promise<void> {
974
const validWorkspaceFolders = await this.toValidWorkspaceFolders(this.workspace.folders);
975
const { removed } = this.compareFolders(this.workspace.folders, validWorkspaceFolders);
976
if (removed.length) {
977
await this.updateWorkspaceConfiguration(validWorkspaceFolders, this.workspaceConfiguration.getConfiguration(), fromCache);
978
}
979
}
980
981
// Filter out workspace folders which are files (not directories)
982
// Workspace folders those cannot be resolved are not filtered because they are handled by the Explorer.
983
private async toValidWorkspaceFolders(workspaceFolders: WorkspaceFolder[]): Promise<WorkspaceFolder[]> {
984
const validWorkspaceFolders: WorkspaceFolder[] = [];
985
for (const workspaceFolder of workspaceFolders) {
986
try {
987
const result = await this.fileService.stat(workspaceFolder.uri);
988
if (!result.isDirectory) {
989
continue;
990
}
991
} catch (e) {
992
this.logService.warn(`Ignoring the error while validating workspace folder ${workspaceFolder.uri.toString()} - ${toErrorMessage(e)}`);
993
}
994
validWorkspaceFolders.push(workspaceFolder);
995
}
996
return validWorkspaceFolders;
997
}
998
999
private async writeConfigurationValue(key: string, value: unknown, target: ConfigurationTarget, overrides: IConfigurationUpdateOverrides | undefined, options?: IConfigurationUpdateOverrides): Promise<void> {
1000
if (!this.instantiationService) {
1001
throw new Error('Cannot write configuration because the configuration service is not yet ready to accept writes.');
1002
}
1003
1004
if (target === ConfigurationTarget.DEFAULT) {
1005
throw new Error('Invalid configuration target');
1006
}
1007
1008
if (target === ConfigurationTarget.MEMORY) {
1009
const previous = { data: this._configuration.toData(), workspace: this.workspace };
1010
this._configuration.updateValue(key, value, overrides);
1011
this.triggerConfigurationChange({ keys: overrides?.overrideIdentifiers?.length ? [keyFromOverrideIdentifiers(overrides.overrideIdentifiers), key] : [key], overrides: overrides?.overrideIdentifiers?.length ? overrides.overrideIdentifiers.map(overrideIdentifier => ([overrideIdentifier, [key]])) : [] }, previous, target);
1012
return;
1013
}
1014
1015
const editableConfigurationTarget = this.toEditableConfigurationTarget(target, key);
1016
if (!editableConfigurationTarget) {
1017
throw new Error('Invalid configuration target');
1018
}
1019
1020
if (editableConfigurationTarget === EditableConfigurationTarget.USER_REMOTE && !this.remoteUserConfiguration) {
1021
throw new Error('Invalid configuration target');
1022
}
1023
1024
if (overrides?.overrideIdentifiers?.length && overrides.overrideIdentifiers.length > 1) {
1025
const configurationModel = this.getConfigurationModelForEditableConfigurationTarget(editableConfigurationTarget, overrides.resource);
1026
if (configurationModel) {
1027
const overrideIdentifiers = overrides.overrideIdentifiers.sort();
1028
const existingOverrides = configurationModel.overrides.find(override => arrayEquals([...override.identifiers].sort(), overrideIdentifiers));
1029
if (existingOverrides) {
1030
overrides.overrideIdentifiers = existingOverrides.identifiers;
1031
}
1032
}
1033
}
1034
1035
// Use same instance of ConfigurationEditing to make sure all writes go through the same queue
1036
this.configurationEditing = this.configurationEditing ?? this.createConfigurationEditingService(this.instantiationService);
1037
await (await this.configurationEditing).writeConfiguration(editableConfigurationTarget, { key, value }, { scopes: overrides, ...options });
1038
switch (editableConfigurationTarget) {
1039
case EditableConfigurationTarget.USER_LOCAL:
1040
if (this.applicationConfiguration && this.isSettingAppliedForAllProfiles(key)) {
1041
await this.reloadApplicationConfiguration();
1042
} else {
1043
await this.reloadLocalUserConfiguration();
1044
}
1045
return;
1046
case EditableConfigurationTarget.USER_REMOTE:
1047
return this.reloadRemoteUserConfiguration().then(() => undefined);
1048
case EditableConfigurationTarget.WORKSPACE:
1049
return this.reloadWorkspaceConfiguration();
1050
case EditableConfigurationTarget.WORKSPACE_FOLDER: {
1051
const workspaceFolder = overrides && overrides.resource ? this.workspace.getFolder(overrides.resource) : null;
1052
if (workspaceFolder) {
1053
return this.reloadWorkspaceFolderConfiguration(workspaceFolder);
1054
}
1055
}
1056
}
1057
}
1058
1059
private async createConfigurationEditingService(instantiationService: IInstantiationService): Promise<ConfigurationEditing> {
1060
const remoteSettingsResource = (await this.remoteAgentService.getEnvironment())?.settingsPath ?? null;
1061
return instantiationService.createInstance(ConfigurationEditing, remoteSettingsResource);
1062
}
1063
1064
private getConfigurationModelForEditableConfigurationTarget(target: EditableConfigurationTarget, resource?: URI | null): ConfigurationModel | undefined {
1065
switch (target) {
1066
case EditableConfigurationTarget.USER_LOCAL: return this._configuration.localUserConfiguration;
1067
case EditableConfigurationTarget.USER_REMOTE: return this._configuration.remoteUserConfiguration;
1068
case EditableConfigurationTarget.WORKSPACE: return this._configuration.workspaceConfiguration;
1069
case EditableConfigurationTarget.WORKSPACE_FOLDER: return resource ? this._configuration.folderConfigurations.get(resource) : undefined;
1070
}
1071
}
1072
1073
getConfigurationModel(target: ConfigurationTarget, resource?: URI | null): ConfigurationModel | undefined {
1074
switch (target) {
1075
case ConfigurationTarget.USER_LOCAL: return this._configuration.localUserConfiguration;
1076
case ConfigurationTarget.USER_REMOTE: return this._configuration.remoteUserConfiguration;
1077
case ConfigurationTarget.WORKSPACE: return this._configuration.workspaceConfiguration;
1078
case ConfigurationTarget.WORKSPACE_FOLDER: return resource ? this._configuration.folderConfigurations.get(resource) : undefined;
1079
default: return undefined;
1080
}
1081
}
1082
1083
private deriveConfigurationTargets(key: string, value: unknown, inspect: IConfigurationValue<any>): ConfigurationTarget[] {
1084
if (equals(value, inspect.value)) {
1085
return [];
1086
}
1087
1088
const definedTargets: ConfigurationTarget[] = [];
1089
if (inspect.workspaceFolderValue !== undefined) {
1090
definedTargets.push(ConfigurationTarget.WORKSPACE_FOLDER);
1091
}
1092
if (inspect.workspaceValue !== undefined) {
1093
definedTargets.push(ConfigurationTarget.WORKSPACE);
1094
}
1095
if (inspect.userRemoteValue !== undefined) {
1096
definedTargets.push(ConfigurationTarget.USER_REMOTE);
1097
}
1098
if (inspect.userLocalValue !== undefined) {
1099
definedTargets.push(ConfigurationTarget.USER_LOCAL);
1100
}
1101
if (inspect.applicationValue !== undefined) {
1102
definedTargets.push(ConfigurationTarget.APPLICATION);
1103
}
1104
1105
if (value === undefined) {
1106
// Remove the setting in all defined targets
1107
return definedTargets;
1108
}
1109
1110
return [definedTargets[0] || ConfigurationTarget.USER];
1111
}
1112
1113
private triggerConfigurationChange(change: IConfigurationChange, previous: { data: IConfigurationData; workspace?: Workspace } | undefined, target: ConfigurationTarget): void {
1114
if (change.keys.length) {
1115
if (target !== ConfigurationTarget.DEFAULT) {
1116
this.logService.debug(`Configuration keys changed in ${ConfigurationTargetToString(target)} target`, ...change.keys);
1117
}
1118
const configurationChangeEvent = new ConfigurationChangeEvent(change, previous, this._configuration, this.workspace, this.logService);
1119
configurationChangeEvent.source = target;
1120
this._onDidChangeConfiguration.fire(configurationChangeEvent);
1121
}
1122
}
1123
1124
private toEditableConfigurationTarget(target: ConfigurationTarget, key: string): EditableConfigurationTarget | null {
1125
if (target === ConfigurationTarget.APPLICATION) {
1126
return EditableConfigurationTarget.USER_LOCAL;
1127
}
1128
if (target === ConfigurationTarget.USER) {
1129
if (this.remoteUserConfiguration) {
1130
const scope = this.configurationRegistry.getConfigurationProperties()[key]?.scope;
1131
if (scope === ConfigurationScope.MACHINE || scope === ConfigurationScope.MACHINE_OVERRIDABLE || scope === ConfigurationScope.APPLICATION_MACHINE) {
1132
return EditableConfigurationTarget.USER_REMOTE;
1133
}
1134
if (this.inspect(key).userRemoteValue !== undefined) {
1135
return EditableConfigurationTarget.USER_REMOTE;
1136
}
1137
}
1138
return EditableConfigurationTarget.USER_LOCAL;
1139
}
1140
if (target === ConfigurationTarget.USER_LOCAL) {
1141
return EditableConfigurationTarget.USER_LOCAL;
1142
}
1143
if (target === ConfigurationTarget.USER_REMOTE) {
1144
return EditableConfigurationTarget.USER_REMOTE;
1145
}
1146
if (target === ConfigurationTarget.WORKSPACE) {
1147
return EditableConfigurationTarget.WORKSPACE;
1148
}
1149
if (target === ConfigurationTarget.WORKSPACE_FOLDER) {
1150
return EditableConfigurationTarget.WORKSPACE_FOLDER;
1151
}
1152
return null;
1153
}
1154
}
1155
1156
class RegisterConfigurationSchemasContribution extends Disposable implements IWorkbenchContribution {
1157
constructor(
1158
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
1159
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
1160
@IWorkspaceTrustManagementService private readonly workspaceTrustManagementService: IWorkspaceTrustManagementService,
1161
@IExtensionService extensionService: IExtensionService,
1162
@ILifecycleService lifecycleService: ILifecycleService,
1163
) {
1164
super();
1165
1166
extensionService.whenInstalledExtensionsRegistered().then(() => {
1167
this.registerConfigurationSchemas();
1168
1169
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
1170
const delayer = this._register(new Delayer<void>(50));
1171
this._register(Event.any(configurationRegistry.onDidUpdateConfiguration, configurationRegistry.onDidSchemaChange, workspaceTrustManagementService.onDidChangeTrust)(() =>
1172
delayer.trigger(() => this.registerConfigurationSchemas(), lifecycleService.phase === LifecyclePhase.Eventually ? undefined : 2500 /* delay longer in early phases */)));
1173
});
1174
}
1175
1176
private registerConfigurationSchemas(): void {
1177
const allSettingsSchema: IJSONSchema = {
1178
properties: allSettings.properties,
1179
patternProperties: allSettings.patternProperties,
1180
additionalProperties: true,
1181
allowTrailingCommas: true,
1182
allowComments: true
1183
};
1184
1185
const userSettingsSchema: IJSONSchema = this.environmentService.remoteAuthority ?
1186
{
1187
properties: Object.assign({},
1188
applicationSettings.properties,
1189
windowSettings.properties,
1190
resourceSettings.properties
1191
),
1192
patternProperties: allSettings.patternProperties,
1193
additionalProperties: true,
1194
allowTrailingCommas: true,
1195
allowComments: true
1196
}
1197
: allSettingsSchema;
1198
1199
const profileSettingsSchema: IJSONSchema = {
1200
properties: Object.assign({},
1201
machineSettings.properties,
1202
machineOverridableSettings.properties,
1203
windowSettings.properties,
1204
resourceSettings.properties
1205
),
1206
patternProperties: allSettings.patternProperties,
1207
additionalProperties: true,
1208
allowTrailingCommas: true,
1209
allowComments: true
1210
};
1211
1212
const machineSettingsSchema: IJSONSchema = {
1213
properties: Object.assign({},
1214
applicationMachineSettings.properties,
1215
machineSettings.properties,
1216
machineOverridableSettings.properties,
1217
windowSettings.properties,
1218
resourceSettings.properties
1219
),
1220
patternProperties: allSettings.patternProperties,
1221
additionalProperties: true,
1222
allowTrailingCommas: true,
1223
allowComments: true
1224
};
1225
1226
const workspaceSettingsSchema: IJSONSchema = {
1227
properties: Object.assign({},
1228
this.checkAndFilterPropertiesRequiringTrust(machineOverridableSettings.properties),
1229
this.checkAndFilterPropertiesRequiringTrust(windowSettings.properties),
1230
this.checkAndFilterPropertiesRequiringTrust(resourceSettings.properties)
1231
),
1232
patternProperties: allSettings.patternProperties,
1233
additionalProperties: true,
1234
allowTrailingCommas: true,
1235
allowComments: true
1236
};
1237
1238
const defaultSettingsSchema = {
1239
properties: Object.keys(allSettings.properties).reduce<IJSONSchemaMap>((result, key) => {
1240
result[key] = Object.assign({ deprecationMessage: undefined }, allSettings.properties[key]);
1241
return result;
1242
}, {}),
1243
patternProperties: Object.keys(allSettings.patternProperties).reduce<IJSONSchemaMap>((result, key) => {
1244
result[key] = Object.assign({ deprecationMessage: undefined }, allSettings.patternProperties[key]);
1245
return result;
1246
}, {}),
1247
additionalProperties: true,
1248
allowTrailingCommas: true,
1249
allowComments: true
1250
};
1251
1252
const folderSettingsSchema: IJSONSchema = WorkbenchState.WORKSPACE === this.workspaceContextService.getWorkbenchState() ?
1253
{
1254
properties: Object.assign({},
1255
this.checkAndFilterPropertiesRequiringTrust(machineOverridableSettings.properties),
1256
this.checkAndFilterPropertiesRequiringTrust(resourceSettings.properties)
1257
),
1258
patternProperties: allSettings.patternProperties,
1259
additionalProperties: true,
1260
allowTrailingCommas: true,
1261
allowComments: true
1262
} : workspaceSettingsSchema;
1263
1264
const configDefaultsSchema: IJSONSchema = {
1265
type: 'object',
1266
description: localize('configurationDefaults.description', 'Contribute defaults for configurations'),
1267
properties: Object.assign({},
1268
this.filterDefaultOverridableProperties(machineOverridableSettings.properties),
1269
this.filterDefaultOverridableProperties(windowSettings.properties),
1270
this.filterDefaultOverridableProperties(resourceSettings.properties)
1271
),
1272
patternProperties: {
1273
[OVERRIDE_PROPERTY_PATTERN]: {
1274
type: 'object',
1275
default: {},
1276
$ref: resourceLanguageSettingsSchemaId,
1277
}
1278
},
1279
additionalProperties: false
1280
};
1281
this.registerSchemas({
1282
defaultSettingsSchema,
1283
userSettingsSchema,
1284
profileSettingsSchema,
1285
machineSettingsSchema,
1286
workspaceSettingsSchema,
1287
folderSettingsSchema,
1288
configDefaultsSchema,
1289
});
1290
}
1291
1292
private registerSchemas(schemas: {
1293
defaultSettingsSchema: IJSONSchema;
1294
userSettingsSchema: IJSONSchema;
1295
profileSettingsSchema: IJSONSchema;
1296
machineSettingsSchema: IJSONSchema;
1297
workspaceSettingsSchema: IJSONSchema;
1298
folderSettingsSchema: IJSONSchema;
1299
configDefaultsSchema: IJSONSchema;
1300
}): void {
1301
const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
1302
jsonRegistry.registerSchema(defaultSettingsSchemaId, schemas.defaultSettingsSchema);
1303
jsonRegistry.registerSchema(userSettingsSchemaId, schemas.userSettingsSchema);
1304
jsonRegistry.registerSchema(profileSettingsSchemaId, schemas.profileSettingsSchema);
1305
jsonRegistry.registerSchema(machineSettingsSchemaId, schemas.machineSettingsSchema);
1306
jsonRegistry.registerSchema(workspaceSettingsSchemaId, schemas.workspaceSettingsSchema);
1307
jsonRegistry.registerSchema(folderSettingsSchemaId, schemas.folderSettingsSchema);
1308
jsonRegistry.registerSchema(configurationDefaultsSchemaId, schemas.configDefaultsSchema);
1309
}
1310
1311
private checkAndFilterPropertiesRequiringTrust(properties: IStringDictionary<IConfigurationPropertySchema>): IStringDictionary<IConfigurationPropertySchema> {
1312
if (this.workspaceTrustManagementService.isWorkspaceTrusted()) {
1313
return properties;
1314
}
1315
1316
const result: IStringDictionary<IConfigurationPropertySchema> = {};
1317
Object.entries(properties).forEach(([key, value]) => {
1318
if (!value.restricted) {
1319
result[key] = value;
1320
}
1321
});
1322
return result;
1323
}
1324
1325
private filterDefaultOverridableProperties(properties: IStringDictionary<IConfigurationPropertySchema>): IStringDictionary<IConfigurationPropertySchema> {
1326
const result: IStringDictionary<IConfigurationPropertySchema> = {};
1327
Object.entries(properties).forEach(([key, value]) => {
1328
if (!value.disallowConfigurationDefault) {
1329
result[key] = value;
1330
}
1331
});
1332
return result;
1333
}
1334
}
1335
1336
class ConfigurationDefaultOverridesContribution extends Disposable implements IWorkbenchContribution {
1337
1338
static readonly ID = 'workbench.contrib.configurationDefaultOverridesContribution';
1339
1340
private readonly processedExperimentalSettings = new Set<string>();
1341
private readonly autoExperimentalSettings = new Set<string>();
1342
private readonly configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
1343
private readonly throttler = this._register(new Throttler());
1344
1345
constructor(
1346
@IWorkbenchAssignmentService private readonly workbenchAssignmentService: IWorkbenchAssignmentService,
1347
@IExtensionService private readonly extensionService: IExtensionService,
1348
@IConfigurationService private readonly configurationService: WorkspaceService,
1349
@ILogService private readonly logService: ILogService
1350
) {
1351
super();
1352
1353
this.throttler.queue(() => this.updateDefaults());
1354
this._register(workbenchAssignmentService.onDidRefetchAssignments(() => this.throttler.queue(() => this.processExperimentalSettings(this.autoExperimentalSettings, true))));
1355
1356
// When configuration is updated make sure to apply experimental configuration overrides
1357
this._register(this.configurationRegistry.onDidUpdateConfiguration(({ properties }) => this.processExperimentalSettings(properties, false)));
1358
}
1359
1360
private async updateDefaults(): Promise<void> {
1361
this.logService.trace('ConfigurationService#updateDefaults: begin');
1362
try {
1363
// Check for experiments
1364
await this.processExperimentalSettings(Object.keys(this.configurationRegistry.getConfigurationProperties()), false);
1365
} finally {
1366
// Invalidate defaults cache after extensions have registered
1367
// and after the experiments have been resolved to prevent
1368
// resetting the overrides too early.
1369
await this.extensionService.whenInstalledExtensionsRegistered();
1370
this.logService.trace('ConfigurationService#updateDefaults: resetting the defaults');
1371
this.configurationService.reloadConfiguration(ConfigurationTarget.DEFAULT);
1372
}
1373
}
1374
1375
private async processExperimentalSettings(properties: Iterable<string>, autoRefetch: boolean): Promise<void> {
1376
const overrides: IStringDictionary<any> = {};
1377
const allProperties = this.configurationRegistry.getConfigurationProperties();
1378
for (const property of properties) {
1379
const schema = allProperties[property];
1380
if (!schema?.experiment) {
1381
continue;
1382
}
1383
if (!autoRefetch && this.processedExperimentalSettings.has(property)) {
1384
continue;
1385
}
1386
this.processedExperimentalSettings.add(property);
1387
if (schema.experiment.mode === 'auto') {
1388
this.autoExperimentalSettings.add(property);
1389
}
1390
try {
1391
const value = await this.workbenchAssignmentService.getTreatment(schema.experiment.name ?? `config.${property}`);
1392
if (!isUndefined(value) && !equals(value, schema.default)) {
1393
overrides[property] = value;
1394
}
1395
} catch (error) {/*ignore */ }
1396
}
1397
if (Object.keys(overrides).length) {
1398
this.configurationRegistry.registerDefaultConfigurations([{ overrides }]);
1399
}
1400
}
1401
}
1402
1403
const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
1404
workbenchContributionsRegistry.registerWorkbenchContribution(RegisterConfigurationSchemasContribution, LifecyclePhase.Restored);
1405
registerWorkbenchContribution2(ConfigurationDefaultOverridesContribution.ID, ConfigurationDefaultOverridesContribution, WorkbenchPhase.BlockRestore);
1406
1407
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
1408
configurationRegistry.registerConfiguration({
1409
...workbenchConfigurationNodeBase,
1410
properties: {
1411
[APPLY_ALL_PROFILES_SETTING]: {
1412
'type': 'array',
1413
description: localize('setting description', "Configure settings to be applied for all profiles."),
1414
'default': [],
1415
'scope': ConfigurationScope.APPLICATION,
1416
additionalProperties: true,
1417
uniqueItems: true,
1418
}
1419
}
1420
});
1421
1422