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