Path: blob/main/src/vs/workbench/services/configuration/browser/configurationService.ts
5243 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { URI } from '../../../../base/common/uri.js';6import { Event, Emitter } from '../../../../base/common/event.js';7import { ResourceMap } from '../../../../base/common/map.js';8import { equals } from '../../../../base/common/objects.js';9import { Disposable, DisposableMap, DisposableStore } from '../../../../base/common/lifecycle.js';10import { Queue, Barrier, Promises, Delayer, Throttler } from '../../../../base/common/async.js';11import { IJSONContributionRegistry, Extensions as JSONExtensions } from '../../../../platform/jsonschemas/common/jsonContributionRegistry.js';12import { IWorkspaceContextService, Workspace as BaseWorkspace, WorkbenchState, IWorkspaceFolder, IWorkspaceFoldersChangeEvent, WorkspaceFolder, toWorkspaceFolder, isWorkspaceFolder, IWorkspaceFoldersWillChangeEvent, IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier, IAnyWorkspaceIdentifier } from '../../../../platform/workspace/common/workspace.js';13import { ConfigurationModel, ConfigurationChangeEvent, mergeChanges } from '../../../../platform/configuration/common/configurationModels.js';14import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, isConfigurationOverrides, IConfigurationData, IConfigurationValue, IConfigurationChange, ConfigurationTargetToString, IConfigurationUpdateOverrides, isConfigurationUpdateOverrides, IConfigurationService, IConfigurationUpdateOptions } from '../../../../platform/configuration/common/configuration.js';15import { IPolicyConfiguration, NullPolicyConfiguration, PolicyConfiguration } from '../../../../platform/configuration/common/configurations.js';16import { Configuration } from '../common/configurationModels.js';17import { 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';18import { Registry } from '../../../../platform/registry/common/platform.js';19import { IConfigurationRegistry, Extensions, allSettings, windowSettings, resourceSettings, applicationSettings, machineSettings, machineOverridableSettings, ConfigurationScope, IConfigurationPropertySchema, keyFromOverrideIdentifiers, OVERRIDE_PROPERTY_PATTERN, resourceLanguageSettingsSchemaId, configurationDefaultsSchemaId, applicationMachineSettings } from '../../../../platform/configuration/common/configurationRegistry.js';20import { IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, getStoredWorkspaceFolder, toWorkspaceFolders } from '../../../../platform/workspaces/common/workspaces.js';21import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';22import { ConfigurationEditing, EditableConfigurationTarget } from '../common/configurationEditing.js';23import { WorkspaceConfiguration, FolderConfiguration, RemoteUserConfiguration, UserConfiguration, DefaultConfiguration, ApplicationConfiguration } from './configuration.js';24import { IJSONSchema, IJSONSchemaMap } from '../../../../base/common/jsonSchema.js';25import { mark } from '../../../../base/common/performance.js';26import { IRemoteAgentService } from '../../remote/common/remoteAgentService.js';27import { IFileService } from '../../../../platform/files/common/files.js';28import { IWorkbenchEnvironmentService } from '../../environment/common/environmentService.js';29import { IWorkbenchContribution, IWorkbenchContributionsRegistry, WorkbenchPhase, Extensions as WorkbenchExtensions, registerWorkbenchContribution2 } from '../../../common/contributions.js';30import { ILifecycleService, LifecyclePhase } from '../../lifecycle/common/lifecycle.js';31import { ILogService } from '../../../../platform/log/common/log.js';32import { toErrorMessage } from '../../../../base/common/errorMessage.js';33import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';34import { IWorkspaceTrustManagementService } from '../../../../platform/workspace/common/workspaceTrust.js';35import { delta, distinct, equals as arrayEquals } from '../../../../base/common/arrays.js';36import { IStringDictionary } from '../../../../base/common/collections.js';37import { IExtensionService } from '../../extensions/common/extensions.js';38import { IWorkbenchAssignmentService } from '../../assignment/common/assignmentService.js';39import { isUndefined } from '../../../../base/common/types.js';40import { localize } from '../../../../nls.js';41import { DidChangeUserDataProfileEvent, IUserDataProfileService } from '../../userDataProfile/common/userDataProfile.js';42import { IPolicyService, NullPolicyService } from '../../../../platform/policy/common/policy.js';43import { IUserDataProfile, IUserDataProfilesService } from '../../../../platform/userDataProfile/common/userDataProfile.js';44import { IJSONEditingService } from '../common/jsonEditing.js';45import { IBrowserWorkbenchEnvironmentService } from '../../environment/browser/environmentService.js';46import { workbenchConfigurationNodeBase } from '../../../common/configuration.js';47import { mainWindow } from '../../../../base/browser/window.js';48import { runWhenWindowIdle } from '../../../../base/browser/dom.js';4950function getLocalUserConfigurationScopes(userDataProfile: IUserDataProfile, hasRemote: boolean): ConfigurationScope[] | undefined {51const isDefaultProfile = userDataProfile.isDefault || userDataProfile.useDefaultFlags?.settings;52if (isDefaultProfile) {53return hasRemote ? LOCAL_MACHINE_SCOPES : undefined;54}55return hasRemote ? LOCAL_MACHINE_PROFILE_SCOPES : PROFILE_SCOPES;56}5758class Workspace extends BaseWorkspace {59initialized: boolean = false;60}6162export class WorkspaceService extends Disposable implements IWorkbenchConfigurationService, IWorkspaceContextService {6364public _serviceBrand: undefined;6566private workspace!: Workspace;67private initRemoteUserConfigurationBarrier: Barrier;68private completeWorkspaceBarrier: Barrier;69private readonly configurationCache: IConfigurationCache;70private _configuration: Configuration;71private initialized: boolean = false;72private readonly defaultConfiguration: DefaultConfiguration;73private readonly policyConfiguration: IPolicyConfiguration;74private applicationConfiguration: ApplicationConfiguration | null = null;75private readonly applicationConfigurationDisposables: DisposableStore;76private readonly localUserConfiguration: UserConfiguration;77private readonly remoteUserConfiguration: RemoteUserConfiguration | null = null;78private readonly workspaceConfiguration: WorkspaceConfiguration;79private cachedFolderConfigs: DisposableMap<URI, FolderConfiguration> = this._register(new DisposableMap(new ResourceMap()));80private readonly workspaceEditingQueue: Queue<void>;8182private readonly _onDidChangeConfiguration: Emitter<IConfigurationChangeEvent> = this._register(new Emitter<IConfigurationChangeEvent>());83public readonly onDidChangeConfiguration: Event<IConfigurationChangeEvent> = this._onDidChangeConfiguration.event;8485protected readonly _onWillChangeWorkspaceFolders: Emitter<IWorkspaceFoldersWillChangeEvent> = this._register(new Emitter<IWorkspaceFoldersWillChangeEvent>());86public readonly onWillChangeWorkspaceFolders: Event<IWorkspaceFoldersWillChangeEvent> = this._onWillChangeWorkspaceFolders.event;8788private readonly _onDidChangeWorkspaceFolders: Emitter<IWorkspaceFoldersChangeEvent> = this._register(new Emitter<IWorkspaceFoldersChangeEvent>());89public readonly onDidChangeWorkspaceFolders: Event<IWorkspaceFoldersChangeEvent> = this._onDidChangeWorkspaceFolders.event;9091private readonly _onDidChangeWorkspaceName: Emitter<void> = this._register(new Emitter<void>());92public readonly onDidChangeWorkspaceName: Event<void> = this._onDidChangeWorkspaceName.event;9394private readonly _onDidChangeWorkbenchState: Emitter<WorkbenchState> = this._register(new Emitter<WorkbenchState>());95public readonly onDidChangeWorkbenchState: Event<WorkbenchState> = this._onDidChangeWorkbenchState.event;9697private isWorkspaceTrusted: boolean = true;9899private _restrictedSettings: RestrictedSettings = { default: [] };100get restrictedSettings() { return this._restrictedSettings; }101private readonly _onDidChangeRestrictedSettings = this._register(new Emitter<RestrictedSettings>());102public readonly onDidChangeRestrictedSettings = this._onDidChangeRestrictedSettings.event;103104private readonly configurationRegistry: IConfigurationRegistry;105106private instantiationService: IInstantiationService | undefined;107private configurationEditing: Promise<ConfigurationEditing> | undefined;108109constructor(110{ remoteAuthority, configurationCache }: { remoteAuthority?: string; configurationCache: IConfigurationCache },111private readonly environmentService: IBrowserWorkbenchEnvironmentService,112private readonly userDataProfileService: IUserDataProfileService,113private readonly userDataProfilesService: IUserDataProfilesService,114private readonly fileService: IFileService,115private readonly remoteAgentService: IRemoteAgentService,116private readonly uriIdentityService: IUriIdentityService,117private readonly logService: ILogService,118policyService: IPolicyService119) {120super();121122this.configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);123124this.initRemoteUserConfigurationBarrier = new Barrier();125this.completeWorkspaceBarrier = new Barrier();126this.defaultConfiguration = this._register(new DefaultConfiguration(userDataProfileService.currentProfile.id, configurationCache, environmentService, logService));127this.policyConfiguration = policyService instanceof NullPolicyService ? new NullPolicyConfiguration() : this._register(new PolicyConfiguration(this.defaultConfiguration, policyService, logService));128this.configurationCache = configurationCache;129this._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);130this.applicationConfigurationDisposables = this._register(new DisposableStore());131this.createApplicationConfiguration();132this.localUserConfiguration = this._register(new UserConfiguration(userDataProfileService.currentProfile.settingsResource, userDataProfileService.currentProfile.tasksResource, userDataProfileService.currentProfile.mcpResource, { scopes: getLocalUserConfigurationScopes(userDataProfileService.currentProfile, !!remoteAuthority) }, fileService, uriIdentityService, logService));133this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration)));134if (remoteAuthority) {135const remoteUserConfiguration = this.remoteUserConfiguration = this._register(new RemoteUserConfiguration(remoteAuthority, configurationCache, fileService, uriIdentityService, remoteAgentService, logService));136this._register(remoteUserConfiguration.onDidInitialize(remoteUserConfigurationModel => {137this._register(remoteUserConfiguration.onDidChangeConfiguration(remoteUserConfigurationModel => this.onRemoteUserConfigurationChanged(remoteUserConfigurationModel)));138this.onRemoteUserConfigurationChanged(remoteUserConfigurationModel);139this.initRemoteUserConfigurationBarrier.open();140}));141} else {142this.initRemoteUserConfigurationBarrier.open();143}144145this.workspaceConfiguration = this._register(new WorkspaceConfiguration(configurationCache, fileService, uriIdentityService, logService));146this._register(this.workspaceConfiguration.onDidUpdateConfiguration(fromCache => {147this.onWorkspaceConfigurationChanged(fromCache).then(() => {148this.workspace.initialized = this.workspaceConfiguration.initialized;149this.checkAndMarkWorkspaceComplete(fromCache);150});151}));152153this._register(this.defaultConfiguration.onDidChangeConfiguration(({ properties, defaults }) => this.onDefaultConfigurationChanged(defaults, properties)));154this._register(this.policyConfiguration.onDidChangeConfiguration(configurationModel => this.onPolicyConfigurationChanged(configurationModel)));155this._register(userDataProfileService.onDidChangeCurrentProfile(e => this.onUserDataProfileChanged(e)));156157this.workspaceEditingQueue = new Queue<void>();158}159160private createApplicationConfiguration(): void {161this.applicationConfigurationDisposables.clear();162if (this.userDataProfileService.currentProfile.isDefault || this.userDataProfileService.currentProfile.useDefaultFlags?.settings) {163this.applicationConfiguration = null;164} else {165this.applicationConfiguration = this.applicationConfigurationDisposables.add(this._register(new ApplicationConfiguration(this.userDataProfilesService, this.fileService, this.uriIdentityService, this.logService)));166this.applicationConfigurationDisposables.add(this.applicationConfiguration.onDidChangeConfiguration(configurationModel => this.onApplicationConfigurationChanged(configurationModel)));167}168}169170// Workspace Context Service Impl171172public async getCompleteWorkspace(): Promise<Workspace> {173await this.completeWorkspaceBarrier.wait();174return this.getWorkspace();175}176177public getWorkspace(): Workspace {178return this.workspace;179}180181public getWorkbenchState(): WorkbenchState {182// Workspace has configuration file183if (this.workspace.configuration) {184return WorkbenchState.WORKSPACE;185}186187// Folder has single root188if (this.workspace.folders.length === 1) {189return WorkbenchState.FOLDER;190}191192// Empty193return WorkbenchState.EMPTY;194}195196public getWorkspaceFolder(resource: URI): IWorkspaceFolder | null {197return this.workspace.getFolder(resource);198}199200public addFolders(foldersToAdd: IWorkspaceFolderCreationData[], index?: number): Promise<void> {201return this.updateFolders(foldersToAdd, [], index);202}203204public removeFolders(foldersToRemove: URI[]): Promise<void> {205return this.updateFolders([], foldersToRemove);206}207208public async updateFolders(foldersToAdd: IWorkspaceFolderCreationData[], foldersToRemove: URI[], index?: number): Promise<void> {209return this.workspaceEditingQueue.queue(() => this.doUpdateFolders(foldersToAdd, foldersToRemove, index));210}211212public isInsideWorkspace(resource: URI): boolean {213return !!this.getWorkspaceFolder(resource);214}215216public isCurrentWorkspace(workspaceIdOrFolder: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI): boolean {217switch (this.getWorkbenchState()) {218case WorkbenchState.FOLDER: {219let folderUri: URI | undefined = undefined;220if (URI.isUri(workspaceIdOrFolder)) {221folderUri = workspaceIdOrFolder;222} else if (isSingleFolderWorkspaceIdentifier(workspaceIdOrFolder)) {223folderUri = workspaceIdOrFolder.uri;224}225226return URI.isUri(folderUri) && this.uriIdentityService.extUri.isEqual(folderUri, this.workspace.folders[0].uri);227}228case WorkbenchState.WORKSPACE:229return isWorkspaceIdentifier(workspaceIdOrFolder) && this.workspace.id === workspaceIdOrFolder.id;230}231return false;232}233234private async doUpdateFolders(foldersToAdd: IWorkspaceFolderCreationData[], foldersToRemove: URI[], index?: number): Promise<void> {235if (this.getWorkbenchState() !== WorkbenchState.WORKSPACE) {236return Promise.resolve(undefined); // we need a workspace to begin with237}238239if (foldersToAdd.length + foldersToRemove.length === 0) {240return Promise.resolve(undefined); // nothing to do241}242243let foldersHaveChanged = false;244245// Remove first (if any)246let currentWorkspaceFolders = this.getWorkspace().folders;247let newStoredFolders: IStoredWorkspaceFolder[] = currentWorkspaceFolders.map(f => f.raw).filter((folder, index): folder is IStoredWorkspaceFolder => {248if (!isStoredWorkspaceFolder(folder)) {249return true; // keep entries which are unrelated250}251252return !this.contains(foldersToRemove, currentWorkspaceFolders[index].uri); // keep entries which are unrelated253});254255foldersHaveChanged = currentWorkspaceFolders.length !== newStoredFolders.length;256257// Add afterwards (if any)258if (foldersToAdd.length) {259260// Recompute current workspace folders if we have folders to add261const workspaceConfigPath = this.getWorkspace().configuration!;262const workspaceConfigFolder = this.uriIdentityService.extUri.dirname(workspaceConfigPath);263currentWorkspaceFolders = toWorkspaceFolders(newStoredFolders, workspaceConfigPath, this.uriIdentityService.extUri);264const currentWorkspaceFolderUris = currentWorkspaceFolders.map(folder => folder.uri);265266const storedFoldersToAdd: IStoredWorkspaceFolder[] = [];267268for (const folderToAdd of foldersToAdd) {269const folderURI = folderToAdd.uri;270if (this.contains(currentWorkspaceFolderUris, folderURI)) {271continue; // already existing272}273try {274const result = await this.fileService.stat(folderURI);275if (!result.isDirectory) {276continue;277}278} catch (e) { /* Ignore */ }279storedFoldersToAdd.push(getStoredWorkspaceFolder(folderURI, false, folderToAdd.name, workspaceConfigFolder, this.uriIdentityService.extUri));280}281282// Apply to array of newStoredFolders283if (storedFoldersToAdd.length > 0) {284foldersHaveChanged = true;285286if (typeof index === 'number' && index >= 0 && index < newStoredFolders.length) {287newStoredFolders = newStoredFolders.slice(0);288newStoredFolders.splice(index, 0, ...storedFoldersToAdd);289} else {290newStoredFolders = [...newStoredFolders, ...storedFoldersToAdd];291}292}293}294295// Set folders if we recorded a change296if (foldersHaveChanged) {297return this.setFolders(newStoredFolders);298}299300return Promise.resolve(undefined);301}302303private async setFolders(folders: IStoredWorkspaceFolder[]): Promise<void> {304if (!this.instantiationService) {305throw new Error('Cannot update workspace folders because workspace service is not yet ready to accept writes.');306}307308await this.instantiationService.invokeFunction(accessor => this.workspaceConfiguration.setFolders(folders, accessor.get(IJSONEditingService)));309return this.onWorkspaceConfigurationChanged(false);310}311312private contains(resources: URI[], toCheck: URI): boolean {313return resources.some(resource => this.uriIdentityService.extUri.isEqual(resource, toCheck));314}315316// Workspace Configuration Service Impl317318getConfigurationData(): IConfigurationData {319return this._configuration.toData();320}321322getValue<T>(): T;323getValue<T>(section: string): T;324getValue<T>(overrides: IConfigurationOverrides): T;325getValue<T>(section: string, overrides: IConfigurationOverrides): T;326getValue(arg1?: unknown, arg2?: unknown): unknown {327const section = typeof arg1 === 'string' ? arg1 : undefined;328const overrides = isConfigurationOverrides(arg1) ? arg1 : isConfigurationOverrides(arg2) ? arg2 : undefined;329return this._configuration.getValue(section, overrides);330}331332updateValue(key: string, value: unknown): Promise<void>;333updateValue(key: string, value: unknown, overrides: IConfigurationOverrides | IConfigurationUpdateOverrides): Promise<void>;334updateValue(key: string, value: unknown, target: ConfigurationTarget): Promise<void>;335updateValue(key: string, value: unknown, overrides: IConfigurationOverrides | IConfigurationUpdateOverrides, target: ConfigurationTarget, options?: IConfigurationUpdateOptions): Promise<void>;336async updateValue(key: string, value: unknown, arg3?: unknown, arg4?: unknown, options?: IConfigurationUpdateOptions): Promise<void> {337const overrides: IConfigurationUpdateOverrides | undefined = isConfigurationUpdateOverrides(arg3) ? arg3338: isConfigurationOverrides(arg3) ? { resource: arg3.resource, overrideIdentifiers: arg3.overrideIdentifier ? [arg3.overrideIdentifier] : undefined } : undefined;339const target: ConfigurationTarget | undefined = (overrides ? arg4 : arg3) as ConfigurationTarget | undefined;340const targets: ConfigurationTarget[] = target ? [target] : [];341342if (overrides?.overrideIdentifiers) {343overrides.overrideIdentifiers = distinct(overrides.overrideIdentifiers);344overrides.overrideIdentifiers = overrides.overrideIdentifiers.length ? overrides.overrideIdentifiers : undefined;345}346347if (!targets.length) {348if (overrides?.overrideIdentifiers && overrides.overrideIdentifiers.length > 1) {349throw new Error('Configuration Target is required while updating the value for multiple override identifiers');350}351const inspect = this.inspect(key, { resource: overrides?.resource, overrideIdentifier: overrides?.overrideIdentifiers ? overrides.overrideIdentifiers[0] : undefined });352targets.push(...this.deriveConfigurationTargets(key, value, inspect));353354// Remove the setting, if the value is same as default value and is updated only in user target355if (equals(value, inspect.defaultValue) && targets.length === 1 && (targets[0] === ConfigurationTarget.USER || targets[0] === ConfigurationTarget.USER_LOCAL)) {356value = undefined;357}358}359360await Promises.settled(targets.map(target => this.writeConfigurationValue(key, value, target, overrides, options)));361}362363async reloadConfiguration(target?: ConfigurationTarget | IWorkspaceFolder): Promise<void> {364if (target === undefined) {365this.reloadDefaultConfiguration();366const application = await this.reloadApplicationConfiguration(true);367const { local, remote } = await this.reloadUserConfiguration();368await this.reloadWorkspaceConfiguration();369await this.loadConfiguration(application, local, remote, true);370return;371}372373if (isWorkspaceFolder(target)) {374await this.reloadWorkspaceFolderConfiguration(target);375return;376}377378switch (target) {379case ConfigurationTarget.DEFAULT:380this.reloadDefaultConfiguration();381return;382383case ConfigurationTarget.USER: {384const { local, remote } = await this.reloadUserConfiguration();385await this.loadConfiguration(this._configuration.applicationConfiguration, local, remote, true);386return;387}388case ConfigurationTarget.USER_LOCAL:389await this.reloadLocalUserConfiguration();390return;391392case ConfigurationTarget.USER_REMOTE:393await this.reloadRemoteUserConfiguration();394return;395396case ConfigurationTarget.WORKSPACE:397case ConfigurationTarget.WORKSPACE_FOLDER:398await this.reloadWorkspaceConfiguration();399return;400}401}402403hasCachedConfigurationDefaultsOverrides(): boolean {404return this.defaultConfiguration.hasCachedConfigurationDefaultsOverrides();405}406407inspect<T>(key: string, overrides?: IConfigurationOverrides): IConfigurationValue<T> {408return this._configuration.inspect<T>(key, overrides);409}410411keys(): {412default: string[];413policy: string[];414user: string[];415workspace: string[];416workspaceFolder: string[];417} {418return this._configuration.keys();419}420421public async whenRemoteConfigurationLoaded(): Promise<void> {422await this.initRemoteUserConfigurationBarrier.wait();423}424425/**426* At present, all workspaces (empty, single-folder, multi-root) in local and remote427* can be initialized without requiring extension host except following case:428*429* A multi root workspace with .code-workspace file that has to be resolved by an extension.430* Because of readonly `rootPath` property in extension API we have to resolve multi root workspace431* before extension host starts so that `rootPath` can be set to first folder.432*433* This restriction is lifted partially for web in `MainThreadWorkspace`.434* In web, we start extension host with empty `rootPath` in this case.435*436* Related root path issue discussion is being tracked here - https://github.com/microsoft/vscode/issues/69335437*/438async initialize(arg: IAnyWorkspaceIdentifier): Promise<void> {439mark('code/willInitWorkspaceService');440441const trigger = this.initialized;442this.initialized = false;443const workspace = await this.createWorkspace(arg);444await this.updateWorkspaceAndInitializeConfiguration(workspace, trigger);445this.checkAndMarkWorkspaceComplete(false);446447mark('code/didInitWorkspaceService');448}449450updateWorkspaceTrust(trusted: boolean): void {451if (this.isWorkspaceTrusted !== trusted) {452this.isWorkspaceTrusted = trusted;453const data = this._configuration.toData();454const folderConfigurationModels: (ConfigurationModel | undefined)[] = [];455for (const folder of this.workspace.folders) {456const folderConfiguration = this.cachedFolderConfigs.get(folder.uri);457let configurationModel: ConfigurationModel | undefined;458if (folderConfiguration) {459configurationModel = folderConfiguration.updateWorkspaceTrust(this.isWorkspaceTrusted);460this._configuration.updateFolderConfiguration(folder.uri, configurationModel);461}462folderConfigurationModels.push(configurationModel);463}464if (this.getWorkbenchState() === WorkbenchState.FOLDER) {465if (folderConfigurationModels[0]) {466this._configuration.updateWorkspaceConfiguration(folderConfigurationModels[0]);467}468} else {469this._configuration.updateWorkspaceConfiguration(this.workspaceConfiguration.updateWorkspaceTrust(this.isWorkspaceTrusted));470}471this.updateRestrictedSettings();472473let keys: string[] = [];474if (this.restrictedSettings.userLocal) {475keys.push(...this.restrictedSettings.userLocal);476}477if (this.restrictedSettings.userRemote) {478keys.push(...this.restrictedSettings.userRemote);479}480if (this.restrictedSettings.workspace) {481keys.push(...this.restrictedSettings.workspace);482}483this.restrictedSettings.workspaceFolder?.forEach((value) => keys.push(...value));484keys = distinct(keys);485if (keys.length) {486this.triggerConfigurationChange({ keys, overrides: [] }, { data, workspace: this.workspace }, ConfigurationTarget.WORKSPACE);487}488}489}490491acquireInstantiationService(instantiationService: IInstantiationService): void {492this.instantiationService = instantiationService;493}494495isSettingAppliedForAllProfiles(key: string): boolean {496const scope = this.configurationRegistry.getConfigurationProperties()[key]?.scope;497if (scope && APPLICATION_SCOPES.includes(scope)) {498return true;499}500const allProfilesSettings = this.getValue<string[]>(APPLY_ALL_PROFILES_SETTING) ?? [];501return Array.isArray(allProfilesSettings) && allProfilesSettings.includes(key);502}503504private async createWorkspace(arg: IAnyWorkspaceIdentifier): Promise<Workspace> {505if (isWorkspaceIdentifier(arg)) {506return this.createMultiFolderWorkspace(arg);507}508509if (isSingleFolderWorkspaceIdentifier(arg)) {510return this.createSingleFolderWorkspace(arg);511}512513return this.createEmptyWorkspace(arg);514}515516private async createMultiFolderWorkspace(workspaceIdentifier: IWorkspaceIdentifier): Promise<Workspace> {517await this.workspaceConfiguration.initialize({ id: workspaceIdentifier.id, configPath: workspaceIdentifier.configPath }, this.isWorkspaceTrusted);518const workspaceConfigPath = workspaceIdentifier.configPath;519const workspaceFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), workspaceConfigPath, this.uriIdentityService.extUri);520const workspaceId = workspaceIdentifier.id;521const isAgentSessionsWorkspace = this.uriIdentityService.extUri.isEqual(workspaceConfigPath, this.environmentService.agentSessionsWorkspace);522const workspace = new Workspace(workspaceId, workspaceFolders, this.workspaceConfiguration.isTransient(), workspaceConfigPath, uri => this.uriIdentityService.extUri.ignorePathCasing(uri), isAgentSessionsWorkspace);523workspace.initialized = this.workspaceConfiguration.initialized;524return workspace;525}526527private createSingleFolderWorkspace(singleFolderWorkspaceIdentifier: ISingleFolderWorkspaceIdentifier): Workspace {528const workspace = new Workspace(singleFolderWorkspaceIdentifier.id, [toWorkspaceFolder(singleFolderWorkspaceIdentifier.uri)], false, null, uri => this.uriIdentityService.extUri.ignorePathCasing(uri));529workspace.initialized = true;530return workspace;531}532533private createEmptyWorkspace(emptyWorkspaceIdentifier: IEmptyWorkspaceIdentifier): Promise<Workspace> {534const workspace = new Workspace(emptyWorkspaceIdentifier.id, [], false, null, uri => this.uriIdentityService.extUri.ignorePathCasing(uri));535workspace.initialized = true;536return Promise.resolve(workspace);537}538539private checkAndMarkWorkspaceComplete(fromCache: boolean): void {540if (!this.completeWorkspaceBarrier.isOpen() && this.workspace.initialized) {541this.completeWorkspaceBarrier.open();542this.validateWorkspaceFoldersAndReload(fromCache);543}544}545546private async updateWorkspaceAndInitializeConfiguration(workspace: Workspace, trigger: boolean): Promise<void> {547const hasWorkspaceBefore = !!this.workspace;548let previousState: WorkbenchState | undefined;549let previousWorkspacePath: string | undefined;550let previousFolders: WorkspaceFolder[] = [];551552if (hasWorkspaceBefore) {553previousState = this.getWorkbenchState();554previousWorkspacePath = this.workspace.configuration ? this.workspace.configuration.fsPath : undefined;555previousFolders = this.workspace.folders;556this.workspace.update(workspace);557} else {558this.workspace = workspace;559}560561await this.initializeConfiguration(trigger);562563// Trigger changes after configuration initialization so that configuration is up to date.564if (hasWorkspaceBefore) {565const newState = this.getWorkbenchState();566if (previousState && newState !== previousState) {567this._onDidChangeWorkbenchState.fire(newState);568}569570const newWorkspacePath = this.workspace.configuration ? this.workspace.configuration.fsPath : undefined;571if (previousWorkspacePath && newWorkspacePath !== previousWorkspacePath || newState !== previousState) {572this._onDidChangeWorkspaceName.fire();573}574575const folderChanges = this.compareFolders(previousFolders, this.workspace.folders);576if (folderChanges && (folderChanges.added.length || folderChanges.removed.length || folderChanges.changed.length)) {577await this.handleWillChangeWorkspaceFolders(folderChanges, false);578this._onDidChangeWorkspaceFolders.fire(folderChanges);579}580}581582if (!this.localUserConfiguration.hasTasksLoaded) {583// Reload local user configuration again to load user tasks584this._register(runWhenWindowIdle(mainWindow, () => this.reloadLocalUserConfiguration(false, this._configuration.localUserConfiguration)));585}586}587588private compareFolders(currentFolders: IWorkspaceFolder[], newFolders: IWorkspaceFolder[]): IWorkspaceFoldersChangeEvent {589const result: IWorkspaceFoldersChangeEvent = { added: [], removed: [], changed: [] };590result.added = newFolders.filter(newFolder => !currentFolders.some(currentFolder => newFolder.uri.toString() === currentFolder.uri.toString()));591for (let currentIndex = 0; currentIndex < currentFolders.length; currentIndex++) {592const currentFolder = currentFolders[currentIndex];593let newIndex = 0;594for (newIndex = 0; newIndex < newFolders.length && currentFolder.uri.toString() !== newFolders[newIndex].uri.toString(); newIndex++) { }595if (newIndex < newFolders.length) {596if (currentIndex !== newIndex || currentFolder.name !== newFolders[newIndex].name) {597result.changed.push(currentFolder);598}599} else {600result.removed.push(currentFolder);601}602}603return result;604}605606private async initializeConfiguration(trigger: boolean): Promise<void> {607await this.defaultConfiguration.initialize();608609const initPolicyConfigurationPromise = this.policyConfiguration.initialize();610const initApplicationConfigurationPromise = this.applicationConfiguration ? this.applicationConfiguration.initialize() : Promise.resolve(ConfigurationModel.createEmptyModel(this.logService));611const initUserConfiguration = async () => {612mark('code/willInitUserConfiguration');613const result = await Promise.all([this.localUserConfiguration.initialize(), this.remoteUserConfiguration ? this.remoteUserConfiguration.initialize() : Promise.resolve(ConfigurationModel.createEmptyModel(this.logService))]);614if (this.applicationConfiguration) {615const applicationConfigurationModel = await initApplicationConfigurationPromise;616result[0] = this.localUserConfiguration.reparse({ exclude: applicationConfigurationModel.getValue(APPLY_ALL_PROFILES_SETTING) });617}618mark('code/didInitUserConfiguration');619return result;620};621622const [, application, [local, remote]] = await Promise.all([623initPolicyConfigurationPromise,624initApplicationConfigurationPromise,625initUserConfiguration()626]);627628mark('code/willInitWorkspaceConfiguration');629await this.loadConfiguration(application, local, remote, trigger);630mark('code/didInitWorkspaceConfiguration');631}632633private reloadDefaultConfiguration(): void {634this.onDefaultConfigurationChanged(this.defaultConfiguration.reload());635}636637private async reloadApplicationConfiguration(donotTrigger?: boolean): Promise<ConfigurationModel> {638if (!this.applicationConfiguration) {639return ConfigurationModel.createEmptyModel(this.logService);640}641const model = await this.applicationConfiguration.loadConfiguration();642if (!donotTrigger) {643this.onApplicationConfigurationChanged(model);644}645return model;646}647648private async reloadUserConfiguration(): Promise<{ local: ConfigurationModel; remote: ConfigurationModel }> {649const [local, remote] = await Promise.all([this.reloadLocalUserConfiguration(true), this.reloadRemoteUserConfiguration(true)]);650return { local, remote };651}652653async reloadLocalUserConfiguration(donotTrigger?: boolean, settingsConfiguration?: ConfigurationModel): Promise<ConfigurationModel> {654const model = await this.localUserConfiguration.reload(settingsConfiguration);655if (!donotTrigger) {656this.onLocalUserConfigurationChanged(model);657}658return model;659}660661private async reloadRemoteUserConfiguration(donotTrigger?: boolean): Promise<ConfigurationModel> {662if (this.remoteUserConfiguration) {663const model = await this.remoteUserConfiguration.reload();664if (!donotTrigger) {665this.onRemoteUserConfigurationChanged(model);666}667return model;668}669return ConfigurationModel.createEmptyModel(this.logService);670}671672private async reloadWorkspaceConfiguration(): Promise<void> {673const workbenchState = this.getWorkbenchState();674if (workbenchState === WorkbenchState.FOLDER) {675return this.onWorkspaceFolderConfigurationChanged(this.workspace.folders[0]);676}677if (workbenchState === WorkbenchState.WORKSPACE) {678return this.workspaceConfiguration.reload().then(() => this.onWorkspaceConfigurationChanged(false));679}680}681682private reloadWorkspaceFolderConfiguration(folder: IWorkspaceFolder): Promise<void> {683return this.onWorkspaceFolderConfigurationChanged(folder);684}685686private async loadConfiguration(applicationConfigurationModel: ConfigurationModel, userConfigurationModel: ConfigurationModel, remoteUserConfigurationModel: ConfigurationModel, trigger: boolean): Promise<void> {687// reset caches688this.cachedFolderConfigs.clearAndDisposeAll();689690const folders = this.workspace.folders;691const folderConfigurations = await this.loadFolderConfigurations(folders);692693const workspaceConfiguration = this.getWorkspaceConfigurationModel(folderConfigurations);694const folderConfigurationModels = new ResourceMap<ConfigurationModel>();695folderConfigurations.forEach((folderConfiguration, index) => folderConfigurationModels.set(folders[index].uri, folderConfiguration));696697const currentConfiguration = this._configuration;698this._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);699700this.initialized = true;701702if (trigger) {703const change = this._configuration.compare(currentConfiguration);704this.triggerConfigurationChange(change, { data: currentConfiguration.toData(), workspace: this.workspace }, ConfigurationTarget.WORKSPACE);705}706707this.updateRestrictedSettings();708}709710private getWorkspaceConfigurationModel(folderConfigurations: ConfigurationModel[]): ConfigurationModel {711switch (this.getWorkbenchState()) {712case WorkbenchState.FOLDER:713return folderConfigurations[0];714case WorkbenchState.WORKSPACE:715return this.workspaceConfiguration.getConfiguration();716default:717return ConfigurationModel.createEmptyModel(this.logService);718}719}720721private onUserDataProfileChanged(e: DidChangeUserDataProfileEvent): void {722e.join((async () => {723const promises: Promise<ConfigurationModel>[] = [];724promises.push(this.localUserConfiguration.reset(e.profile.settingsResource, e.profile.tasksResource, e.profile.mcpResource, { scopes: getLocalUserConfigurationScopes(e.profile, !!this.remoteUserConfiguration) }));725if (e.previous.isDefault !== e.profile.isDefault726|| !!e.previous.useDefaultFlags?.settings !== !!e.profile.useDefaultFlags?.settings) {727this.createApplicationConfiguration();728if (this.applicationConfiguration) {729promises.push(this.reloadApplicationConfiguration(true));730}731}732let [localUser, application] = await Promise.all(promises);733application = application ?? this._configuration.applicationConfiguration;734if (this.applicationConfiguration) {735localUser = this.localUserConfiguration.reparse({ exclude: application.getValue(APPLY_ALL_PROFILES_SETTING) });736}737await this.loadConfiguration(application, localUser, this._configuration.remoteUserConfiguration, true);738})());739}740741private onDefaultConfigurationChanged(configurationModel: ConfigurationModel, properties?: string[]): void {742if (this.workspace) {743const previousData = this._configuration.toData();744const change = this._configuration.compareAndUpdateDefaultConfiguration(configurationModel, properties);745if (this.applicationConfiguration) {746this._configuration.updateApplicationConfiguration(this.applicationConfiguration.reparse());747}748if (this.remoteUserConfiguration) {749this._configuration.updateLocalUserConfiguration(this.localUserConfiguration.reparse());750this._configuration.updateRemoteUserConfiguration(this.remoteUserConfiguration.reparse());751}752if (this.getWorkbenchState() === WorkbenchState.FOLDER) {753const folderConfiguration = this.cachedFolderConfigs.get(this.workspace.folders[0].uri);754if (folderConfiguration) {755this._configuration.updateWorkspaceConfiguration(folderConfiguration.reparse());756this._configuration.updateFolderConfiguration(this.workspace.folders[0].uri, folderConfiguration.reparse());757}758} else {759this._configuration.updateWorkspaceConfiguration(this.workspaceConfiguration.reparseWorkspaceSettings());760for (const folder of this.workspace.folders) {761const folderConfiguration = this.cachedFolderConfigs.get(folder.uri);762if (folderConfiguration) {763this._configuration.updateFolderConfiguration(folder.uri, folderConfiguration.reparse());764}765}766}767this.triggerConfigurationChange(change, { data: previousData, workspace: this.workspace }, ConfigurationTarget.DEFAULT);768this.updateRestrictedSettings();769}770}771772private onPolicyConfigurationChanged(policyConfiguration: ConfigurationModel): void {773const previous = { data: this._configuration.toData(), workspace: this.workspace };774const change = this._configuration.compareAndUpdatePolicyConfiguration(policyConfiguration);775this.triggerConfigurationChange(change, previous, ConfigurationTarget.DEFAULT);776}777778private onApplicationConfigurationChanged(applicationConfiguration: ConfigurationModel): void {779const previous = { data: this._configuration.toData(), workspace: this.workspace };780const previousAllProfilesSettings = this._configuration.applicationConfiguration.getValue<string[]>(APPLY_ALL_PROFILES_SETTING) ?? [];781const change = this._configuration.compareAndUpdateApplicationConfiguration(applicationConfiguration);782const currentAllProfilesSettings = this.getValue<string[]>(APPLY_ALL_PROFILES_SETTING) ?? [];783const configurationProperties = this.configurationRegistry.getConfigurationProperties();784const changedKeys: string[] = [];785for (const changedKey of change.keys) {786const scope = configurationProperties[changedKey]?.scope;787if (scope && APPLICATION_SCOPES.includes(scope)) {788changedKeys.push(changedKey);789if (changedKey === APPLY_ALL_PROFILES_SETTING) {790for (const previousAllProfileSetting of previousAllProfilesSettings) {791if (!currentAllProfilesSettings.includes(previousAllProfileSetting)) {792changedKeys.push(previousAllProfileSetting);793}794}795for (const currentAllProfileSetting of currentAllProfilesSettings) {796if (!previousAllProfilesSettings.includes(currentAllProfileSetting)) {797changedKeys.push(currentAllProfileSetting);798}799}800}801}802else if (currentAllProfilesSettings.includes(changedKey)) {803changedKeys.push(changedKey);804}805}806change.keys = changedKeys;807if (change.keys.includes(APPLY_ALL_PROFILES_SETTING)) {808this._configuration.updateLocalUserConfiguration(this.localUserConfiguration.reparse({ exclude: currentAllProfilesSettings }));809}810this.triggerConfigurationChange(change, previous, ConfigurationTarget.USER);811}812813private onLocalUserConfigurationChanged(userConfiguration: ConfigurationModel): void {814const previous = { data: this._configuration.toData(), workspace: this.workspace };815const change = this._configuration.compareAndUpdateLocalUserConfiguration(userConfiguration);816this.triggerConfigurationChange(change, previous, ConfigurationTarget.USER);817}818819private onRemoteUserConfigurationChanged(userConfiguration: ConfigurationModel): void {820const previous = { data: this._configuration.toData(), workspace: this.workspace };821const change = this._configuration.compareAndUpdateRemoteUserConfiguration(userConfiguration);822this.triggerConfigurationChange(change, previous, ConfigurationTarget.USER);823}824825private async onWorkspaceConfigurationChanged(fromCache: boolean): Promise<void> {826if (this.workspace && this.workspace.configuration) {827let newFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), this.workspace.configuration, this.uriIdentityService.extUri);828829// Validate only if workspace is initialized830if (this.workspace.initialized) {831const { added, removed, changed } = this.compareFolders(this.workspace.folders, newFolders);832833/* If changed validate new folders */834if (added.length || removed.length || changed.length) {835newFolders = await this.toValidWorkspaceFolders(newFolders);836}837/* Otherwise use existing */838else {839newFolders = this.workspace.folders;840}841}842843await this.updateWorkspaceConfiguration(newFolders, this.workspaceConfiguration.getConfiguration(), fromCache);844}845}846847private updateRestrictedSettings(): void {848const changed: string[] = [];849850const allProperties = this.configurationRegistry.getConfigurationProperties();851const defaultRestrictedSettings: string[] = Object.keys(allProperties).filter(key => allProperties[key].restricted).sort((a, b) => a.localeCompare(b));852const defaultDelta = delta(defaultRestrictedSettings, this._restrictedSettings.default, (a, b) => a.localeCompare(b));853changed.push(...defaultDelta.added, ...defaultDelta.removed);854855const application = (this.applicationConfiguration?.getRestrictedSettings() || []).sort((a, b) => a.localeCompare(b));856const applicationDelta = delta(application, this._restrictedSettings.application || [], (a, b) => a.localeCompare(b));857changed.push(...applicationDelta.added, ...applicationDelta.removed);858859const userLocal = this.localUserConfiguration.getRestrictedSettings().sort((a, b) => a.localeCompare(b));860const userLocalDelta = delta(userLocal, this._restrictedSettings.userLocal || [], (a, b) => a.localeCompare(b));861changed.push(...userLocalDelta.added, ...userLocalDelta.removed);862863const userRemote = (this.remoteUserConfiguration?.getRestrictedSettings() || []).sort((a, b) => a.localeCompare(b));864const userRemoteDelta = delta(userRemote, this._restrictedSettings.userRemote || [], (a, b) => a.localeCompare(b));865changed.push(...userRemoteDelta.added, ...userRemoteDelta.removed);866867const workspaceFolderMap = new ResourceMap<ReadonlyArray<string>>();868for (const workspaceFolder of this.workspace.folders) {869const cachedFolderConfig = this.cachedFolderConfigs.get(workspaceFolder.uri);870const folderRestrictedSettings = (cachedFolderConfig?.getRestrictedSettings() || []).sort((a, b) => a.localeCompare(b));871if (folderRestrictedSettings.length) {872workspaceFolderMap.set(workspaceFolder.uri, folderRestrictedSettings);873}874const previous = this._restrictedSettings.workspaceFolder?.get(workspaceFolder.uri) || [];875const workspaceFolderDelta = delta(folderRestrictedSettings, previous, (a, b) => a.localeCompare(b));876changed.push(...workspaceFolderDelta.added, ...workspaceFolderDelta.removed);877}878879const 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) || []) : [];881const workspaceDelta = delta(workspace, this._restrictedSettings.workspace || [], (a, b) => a.localeCompare(b));882changed.push(...workspaceDelta.added, ...workspaceDelta.removed);883884if (changed.length) {885this._restrictedSettings = {886default: defaultRestrictedSettings,887application: application.length ? application : undefined,888userLocal: userLocal.length ? userLocal : undefined,889userRemote: userRemote.length ? userRemote : undefined,890workspace: workspace.length ? workspace : undefined,891workspaceFolder: workspaceFolderMap.size ? workspaceFolderMap : undefined,892};893this._onDidChangeRestrictedSettings.fire(this.restrictedSettings);894}895}896897private async updateWorkspaceConfiguration(workspaceFolders: WorkspaceFolder[], configuration: ConfigurationModel, fromCache: boolean): Promise<void> {898const previous = { data: this._configuration.toData(), workspace: this.workspace };899const change = this._configuration.compareAndUpdateWorkspaceConfiguration(configuration);900const changes = this.compareFolders(this.workspace.folders, workspaceFolders);901if (changes.added.length || changes.removed.length || changes.changed.length) {902this.workspace.folders = workspaceFolders;903const change = await this.onFoldersChanged();904await this.handleWillChangeWorkspaceFolders(changes, fromCache);905this.triggerConfigurationChange(change, previous, ConfigurationTarget.WORKSPACE_FOLDER);906this._onDidChangeWorkspaceFolders.fire(changes);907} else {908this.triggerConfigurationChange(change, previous, ConfigurationTarget.WORKSPACE);909}910this.updateRestrictedSettings();911}912913private async handleWillChangeWorkspaceFolders(changes: IWorkspaceFoldersChangeEvent, fromCache: boolean): Promise<void> {914const joiners: Promise<void>[] = [];915this._onWillChangeWorkspaceFolders.fire({916join(updateWorkspaceTrustStatePromise) {917joiners.push(updateWorkspaceTrustStatePromise);918},919changes,920fromCache921});922try { await Promises.settled(joiners); } catch (error) { /* Ignore */ }923}924925private async onWorkspaceFolderConfigurationChanged(folder: IWorkspaceFolder): Promise<void> {926const [folderConfiguration] = await this.loadFolderConfigurations([folder]);927const previous = { data: this._configuration.toData(), workspace: this.workspace };928const folderConfigurationChange = this._configuration.compareAndUpdateFolderConfiguration(folder.uri, folderConfiguration);929if (this.getWorkbenchState() === WorkbenchState.FOLDER) {930const workspaceConfigurationChange = this._configuration.compareAndUpdateWorkspaceConfiguration(folderConfiguration);931this.triggerConfigurationChange(mergeChanges(folderConfigurationChange, workspaceConfigurationChange), previous, ConfigurationTarget.WORKSPACE);932} else {933this.triggerConfigurationChange(folderConfigurationChange, previous, ConfigurationTarget.WORKSPACE_FOLDER);934}935this.updateRestrictedSettings();936}937938private async onFoldersChanged(): Promise<IConfigurationChange> {939const changes: IConfigurationChange[] = [];940941// Remove the configurations of deleted folders942for (const key of this.cachedFolderConfigs.keys()) {943if (!this.workspace.folders.filter(folder => folder.uri.toString() === key.toString())[0]) {944this.cachedFolderConfigs.deleteAndDispose(key);945changes.push(this._configuration.compareAndDeleteFolderConfiguration(key));946}947}948949const toInitialize = this.workspace.folders.filter(folder => !this.cachedFolderConfigs.has(folder.uri));950if (toInitialize.length) {951const folderConfigurations = await this.loadFolderConfigurations(toInitialize);952folderConfigurations.forEach((folderConfiguration, index) => {953changes.push(this._configuration.compareAndUpdateFolderConfiguration(toInitialize[index].uri, folderConfiguration));954});955}956return mergeChanges(...changes);957}958959private loadFolderConfigurations(folders: IWorkspaceFolder[]): Promise<ConfigurationModel[]> {960return Promise.all([...folders.map(folder => {961let folderConfiguration = this.cachedFolderConfigs.get(folder.uri);962if (!folderConfiguration) {963folderConfiguration = new FolderConfiguration(!this.initialized, folder, FOLDER_CONFIG_FOLDER_NAME, this.getWorkbenchState(), this.isWorkspaceTrusted, this.fileService, this.uriIdentityService, this.logService, this.configurationCache);964folderConfiguration.addRelated(folderConfiguration.onDidChange(() => this.onWorkspaceFolderConfigurationChanged(folder)));965this.cachedFolderConfigs.set(folder.uri, folderConfiguration);966}967return folderConfiguration.loadConfiguration();968})]);969}970971private async validateWorkspaceFoldersAndReload(fromCache: boolean): Promise<void> {972const validWorkspaceFolders = await this.toValidWorkspaceFolders(this.workspace.folders);973const { removed } = this.compareFolders(this.workspace.folders, validWorkspaceFolders);974if (removed.length) {975await this.updateWorkspaceConfiguration(validWorkspaceFolders, this.workspaceConfiguration.getConfiguration(), fromCache);976}977}978979// Filter out workspace folders which are files (not directories)980// Workspace folders those cannot be resolved are not filtered because they are handled by the Explorer.981private async toValidWorkspaceFolders(workspaceFolders: WorkspaceFolder[]): Promise<WorkspaceFolder[]> {982const validWorkspaceFolders: WorkspaceFolder[] = [];983for (const workspaceFolder of workspaceFolders) {984try {985const result = await this.fileService.stat(workspaceFolder.uri);986if (!result.isDirectory) {987continue;988}989} catch (e) {990this.logService.warn(`Ignoring the error while validating workspace folder ${workspaceFolder.uri.toString()} - ${toErrorMessage(e)}`);991}992validWorkspaceFolders.push(workspaceFolder);993}994return validWorkspaceFolders;995}996997private async writeConfigurationValue(key: string, value: unknown, target: ConfigurationTarget, overrides: IConfigurationUpdateOverrides | undefined, options?: IConfigurationUpdateOptions): Promise<void> {998if (!this.instantiationService) {999throw new Error('Cannot write configuration because the configuration service is not yet ready to accept writes.');1000}10011002if (target === ConfigurationTarget.DEFAULT) {1003throw new Error('Invalid configuration target');1004}10051006if (target === ConfigurationTarget.MEMORY) {1007const previous = { data: this._configuration.toData(), workspace: this.workspace };1008this._configuration.updateValue(key, value, overrides);1009this.triggerConfigurationChange({ keys: overrides?.overrideIdentifiers?.length ? [keyFromOverrideIdentifiers(overrides.overrideIdentifiers), key] : [key], overrides: overrides?.overrideIdentifiers?.length ? overrides.overrideIdentifiers.map(overrideIdentifier => ([overrideIdentifier, [key]])) : [] }, previous, target);1010return;1011}10121013const editableConfigurationTarget = this.toEditableConfigurationTarget(target, key);1014if (!editableConfigurationTarget) {1015throw new Error('Invalid configuration target');1016}10171018if (editableConfigurationTarget === EditableConfigurationTarget.USER_REMOTE && !this.remoteUserConfiguration) {1019throw new Error('Invalid configuration target');1020}10211022if (overrides?.overrideIdentifiers?.length && overrides.overrideIdentifiers.length > 1) {1023const configurationModel = this.getConfigurationModelForEditableConfigurationTarget(editableConfigurationTarget, overrides.resource);1024if (configurationModel) {1025const overrideIdentifiers = overrides.overrideIdentifiers.sort();1026const existingOverrides = configurationModel.overrides.find(override => arrayEquals([...override.identifiers].sort(), overrideIdentifiers));1027if (existingOverrides) {1028overrides.overrideIdentifiers = existingOverrides.identifiers;1029}1030}1031}10321033// Use same instance of ConfigurationEditing to make sure all writes go through the same queue1034this.configurationEditing = this.configurationEditing ?? this.createConfigurationEditingService(this.instantiationService);1035await (await this.configurationEditing).writeConfiguration(editableConfigurationTarget, { key, value }, { scopes: overrides, ...options });1036switch (editableConfigurationTarget) {1037case EditableConfigurationTarget.USER_LOCAL:1038if (this.applicationConfiguration && this.isSettingAppliedForAllProfiles(key)) {1039await this.reloadApplicationConfiguration();1040} else {1041await this.reloadLocalUserConfiguration();1042}1043return;1044case EditableConfigurationTarget.USER_REMOTE:1045return this.reloadRemoteUserConfiguration().then(() => undefined);1046case EditableConfigurationTarget.WORKSPACE:1047return this.reloadWorkspaceConfiguration();1048case EditableConfigurationTarget.WORKSPACE_FOLDER: {1049const workspaceFolder = overrides && overrides.resource ? this.workspace.getFolder(overrides.resource) : null;1050if (workspaceFolder) {1051return this.reloadWorkspaceFolderConfiguration(workspaceFolder);1052}1053}1054}1055}10561057private async createConfigurationEditingService(instantiationService: IInstantiationService): Promise<ConfigurationEditing> {1058const remoteSettingsResource = (await this.remoteAgentService.getEnvironment())?.settingsPath ?? null;1059return instantiationService.createInstance(ConfigurationEditing, remoteSettingsResource);1060}10611062private getConfigurationModelForEditableConfigurationTarget(target: EditableConfigurationTarget, resource?: URI | null): ConfigurationModel | undefined {1063switch (target) {1064case EditableConfigurationTarget.USER_LOCAL: return this._configuration.localUserConfiguration;1065case EditableConfigurationTarget.USER_REMOTE: return this._configuration.remoteUserConfiguration;1066case EditableConfigurationTarget.WORKSPACE: return this._configuration.workspaceConfiguration;1067case EditableConfigurationTarget.WORKSPACE_FOLDER: return resource ? this._configuration.folderConfigurations.get(resource) : undefined;1068}1069}10701071getConfigurationModel(target: ConfigurationTarget, resource?: URI | null): ConfigurationModel | undefined {1072switch (target) {1073case ConfigurationTarget.USER_LOCAL: return this._configuration.localUserConfiguration;1074case ConfigurationTarget.USER_REMOTE: return this._configuration.remoteUserConfiguration;1075case ConfigurationTarget.WORKSPACE: return this._configuration.workspaceConfiguration;1076case ConfigurationTarget.WORKSPACE_FOLDER: return resource ? this._configuration.folderConfigurations.get(resource) : undefined;1077default: return undefined;1078}1079}10801081private deriveConfigurationTargets(key: string, value: unknown, inspect: IConfigurationValue<unknown>): ConfigurationTarget[] {1082if (equals(value, inspect.value)) {1083return [];1084}10851086const definedTargets: ConfigurationTarget[] = [];1087if (inspect.workspaceFolderValue !== undefined) {1088definedTargets.push(ConfigurationTarget.WORKSPACE_FOLDER);1089}1090if (inspect.workspaceValue !== undefined) {1091definedTargets.push(ConfigurationTarget.WORKSPACE);1092}1093if (inspect.userRemoteValue !== undefined) {1094definedTargets.push(ConfigurationTarget.USER_REMOTE);1095}1096if (inspect.userLocalValue !== undefined) {1097definedTargets.push(ConfigurationTarget.USER_LOCAL);1098}1099if (inspect.applicationValue !== undefined) {1100definedTargets.push(ConfigurationTarget.APPLICATION);1101}11021103if (value === undefined) {1104// Remove the setting in all defined targets1105return definedTargets;1106}11071108return [definedTargets[0] || ConfigurationTarget.USER];1109}11101111private triggerConfigurationChange(change: IConfigurationChange, previous: { data: IConfigurationData; workspace?: Workspace } | undefined, target: ConfigurationTarget): void {1112if (change.keys.length) {1113if (target !== ConfigurationTarget.DEFAULT) {1114this.logService.debug(`Configuration keys changed in ${ConfigurationTargetToString(target)} target`, ...change.keys);1115}1116const configurationChangeEvent = new ConfigurationChangeEvent(change, previous, this._configuration, this.workspace, this.logService);1117configurationChangeEvent.source = target;1118this._onDidChangeConfiguration.fire(configurationChangeEvent);1119}1120}11211122private toEditableConfigurationTarget(target: ConfigurationTarget, key: string): EditableConfigurationTarget | null {1123if (target === ConfigurationTarget.APPLICATION) {1124return EditableConfigurationTarget.USER_LOCAL;1125}1126if (target === ConfigurationTarget.USER) {1127if (this.remoteUserConfiguration) {1128const scope = this.configurationRegistry.getConfigurationProperties()[key]?.scope;1129if (scope === ConfigurationScope.MACHINE || scope === ConfigurationScope.MACHINE_OVERRIDABLE || scope === ConfigurationScope.APPLICATION_MACHINE) {1130return EditableConfigurationTarget.USER_REMOTE;1131}1132if (this.inspect(key).userRemoteValue !== undefined) {1133return EditableConfigurationTarget.USER_REMOTE;1134}1135}1136return EditableConfigurationTarget.USER_LOCAL;1137}1138if (target === ConfigurationTarget.USER_LOCAL) {1139return EditableConfigurationTarget.USER_LOCAL;1140}1141if (target === ConfigurationTarget.USER_REMOTE) {1142return EditableConfigurationTarget.USER_REMOTE;1143}1144if (target === ConfigurationTarget.WORKSPACE) {1145return EditableConfigurationTarget.WORKSPACE;1146}1147if (target === ConfigurationTarget.WORKSPACE_FOLDER) {1148return EditableConfigurationTarget.WORKSPACE_FOLDER;1149}1150return null;1151}1152}11531154class RegisterConfigurationSchemasContribution extends Disposable implements IWorkbenchContribution {1155constructor(1156@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,1157@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,1158@IWorkspaceTrustManagementService private readonly workspaceTrustManagementService: IWorkspaceTrustManagementService,1159@IExtensionService extensionService: IExtensionService,1160@ILifecycleService lifecycleService: ILifecycleService,1161) {1162super();11631164extensionService.whenInstalledExtensionsRegistered().then(() => {1165this.registerConfigurationSchemas();11661167const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);1168const delayer = this._register(new Delayer<void>(50));1169this._register(Event.any(configurationRegistry.onDidUpdateConfiguration, configurationRegistry.onDidSchemaChange, workspaceTrustManagementService.onDidChangeTrust)(() =>1170delayer.trigger(() => this.registerConfigurationSchemas(), lifecycleService.phase === LifecyclePhase.Eventually ? undefined : 2500 /* delay longer in early phases */)));1171});1172}11731174private registerConfigurationSchemas(): void {1175const allSettingsSchema: IJSONSchema = {1176properties: allSettings.properties,1177patternProperties: allSettings.patternProperties,1178additionalProperties: true,1179allowTrailingCommas: true,1180allowComments: true1181};11821183const userSettingsSchema: IJSONSchema = this.environmentService.remoteAuthority ?1184{1185properties: Object.assign({},1186applicationSettings.properties,1187windowSettings.properties,1188resourceSettings.properties1189),1190patternProperties: allSettings.patternProperties,1191additionalProperties: true,1192allowTrailingCommas: true,1193allowComments: true1194}1195: allSettingsSchema;11961197const profileSettingsSchema: IJSONSchema = {1198properties: Object.assign({},1199machineSettings.properties,1200machineOverridableSettings.properties,1201windowSettings.properties,1202resourceSettings.properties1203),1204patternProperties: allSettings.patternProperties,1205additionalProperties: true,1206allowTrailingCommas: true,1207allowComments: true1208};12091210const machineSettingsSchema: IJSONSchema = {1211properties: Object.assign({},1212applicationMachineSettings.properties,1213machineSettings.properties,1214machineOverridableSettings.properties,1215windowSettings.properties,1216resourceSettings.properties1217),1218patternProperties: allSettings.patternProperties,1219additionalProperties: true,1220allowTrailingCommas: true,1221allowComments: true1222};12231224const workspaceSettingsSchema: IJSONSchema = {1225properties: Object.assign({},1226this.checkAndFilterPropertiesRequiringTrust(machineOverridableSettings.properties),1227this.checkAndFilterPropertiesRequiringTrust(windowSettings.properties),1228this.checkAndFilterPropertiesRequiringTrust(resourceSettings.properties)1229),1230patternProperties: allSettings.patternProperties,1231additionalProperties: true,1232allowTrailingCommas: true,1233allowComments: true1234};12351236const defaultSettingsSchema = {1237properties: Object.keys(allSettings.properties).reduce<IJSONSchemaMap>((result, key) => {1238result[key] = Object.assign({ deprecationMessage: undefined }, allSettings.properties[key]);1239return result;1240}, {}),1241patternProperties: Object.keys(allSettings.patternProperties).reduce<IJSONSchemaMap>((result, key) => {1242result[key] = Object.assign({ deprecationMessage: undefined }, allSettings.patternProperties[key]);1243return result;1244}, {}),1245additionalProperties: true,1246allowTrailingCommas: true,1247allowComments: true1248};12491250const folderSettingsSchema: IJSONSchema = WorkbenchState.WORKSPACE === this.workspaceContextService.getWorkbenchState() ?1251{1252properties: Object.assign({},1253this.checkAndFilterPropertiesRequiringTrust(machineOverridableSettings.properties),1254this.checkAndFilterPropertiesRequiringTrust(resourceSettings.properties)1255),1256patternProperties: allSettings.patternProperties,1257additionalProperties: true,1258allowTrailingCommas: true,1259allowComments: true1260} : workspaceSettingsSchema;12611262const configDefaultsSchema: IJSONSchema = {1263type: 'object',1264description: localize('configurationDefaults.description', 'Contribute defaults for configurations'),1265properties: Object.assign({},1266this.filterDefaultOverridableProperties(machineOverridableSettings.properties),1267this.filterDefaultOverridableProperties(windowSettings.properties),1268this.filterDefaultOverridableProperties(resourceSettings.properties)1269),1270patternProperties: {1271[OVERRIDE_PROPERTY_PATTERN]: {1272type: 'object',1273default: {},1274$ref: resourceLanguageSettingsSchemaId,1275}1276},1277additionalProperties: false1278};1279this.registerSchemas({1280defaultSettingsSchema,1281userSettingsSchema,1282profileSettingsSchema,1283machineSettingsSchema,1284workspaceSettingsSchema,1285folderSettingsSchema,1286configDefaultsSchema,1287});1288}12891290private registerSchemas(schemas: {1291defaultSettingsSchema: IJSONSchema;1292userSettingsSchema: IJSONSchema;1293profileSettingsSchema: IJSONSchema;1294machineSettingsSchema: IJSONSchema;1295workspaceSettingsSchema: IJSONSchema;1296folderSettingsSchema: IJSONSchema;1297configDefaultsSchema: IJSONSchema;1298}): void {1299const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);1300jsonRegistry.registerSchema(defaultSettingsSchemaId, schemas.defaultSettingsSchema);1301jsonRegistry.registerSchema(userSettingsSchemaId, schemas.userSettingsSchema);1302jsonRegistry.registerSchema(profileSettingsSchemaId, schemas.profileSettingsSchema);1303jsonRegistry.registerSchema(machineSettingsSchemaId, schemas.machineSettingsSchema);1304jsonRegistry.registerSchema(workspaceSettingsSchemaId, schemas.workspaceSettingsSchema);1305jsonRegistry.registerSchema(folderSettingsSchemaId, schemas.folderSettingsSchema);1306jsonRegistry.registerSchema(configurationDefaultsSchemaId, schemas.configDefaultsSchema);1307}13081309private checkAndFilterPropertiesRequiringTrust(properties: IStringDictionary<IConfigurationPropertySchema>): IStringDictionary<IConfigurationPropertySchema> {1310if (this.workspaceTrustManagementService.isWorkspaceTrusted()) {1311return properties;1312}13131314const result: IStringDictionary<IConfigurationPropertySchema> = {};1315Object.entries(properties).forEach(([key, value]) => {1316if (!value.restricted) {1317result[key] = value;1318}1319});1320return result;1321}13221323private filterDefaultOverridableProperties(properties: IStringDictionary<IConfigurationPropertySchema>): IStringDictionary<IConfigurationPropertySchema> {1324const result: IStringDictionary<IConfigurationPropertySchema> = {};1325Object.entries(properties).forEach(([key, value]) => {1326if (!value.disallowConfigurationDefault) {1327result[key] = value;1328}1329});1330return result;1331}1332}13331334class ConfigurationDefaultOverridesContribution extends Disposable implements IWorkbenchContribution {13351336static readonly ID = 'workbench.contrib.configurationDefaultOverridesContribution';13371338private readonly processedExperimentalSettings = new Set<string>();1339private readonly autoExperimentalSettings = new Set<string>();1340private readonly configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);1341private readonly throttler = this._register(new Throttler());13421343constructor(1344@IWorkbenchAssignmentService private readonly workbenchAssignmentService: IWorkbenchAssignmentService,1345@IExtensionService private readonly extensionService: IExtensionService,1346@IConfigurationService private readonly configurationService: WorkspaceService,1347@ILogService private readonly logService: ILogService1348) {1349super();13501351this.throttler.queue(() => this.updateDefaults());1352this._register(workbenchAssignmentService.onDidRefetchAssignments(() => this.throttler.queue(() => this.processExperimentalSettings(this.autoExperimentalSettings, true))));13531354// When configuration is updated make sure to apply experimental configuration overrides1355this._register(this.configurationRegistry.onDidUpdateConfiguration(({ properties }) => this.processExperimentalSettings(properties, false)));1356}13571358private async updateDefaults(): Promise<void> {1359this.logService.trace('ConfigurationService#updateDefaults: begin');1360try {1361// Check for experiments1362await this.processExperimentalSettings(Object.keys(this.configurationRegistry.getConfigurationProperties()), false);1363} finally {1364// Invalidate defaults cache after extensions have registered1365// and after the experiments have been resolved to prevent1366// resetting the overrides too early.1367await this.extensionService.whenInstalledExtensionsRegistered();1368this.logService.trace('ConfigurationService#updateDefaults: resetting the defaults');1369this.configurationService.reloadConfiguration(ConfigurationTarget.DEFAULT);1370}1371}13721373private async processExperimentalSettings(properties: Iterable<string>, autoRefetch: boolean): Promise<void> {1374const overrides: IStringDictionary<unknown> = {};1375const allProperties = this.configurationRegistry.getConfigurationProperties();1376for (const property of properties) {1377const schema = allProperties[property];1378if (!schema?.experiment) {1379continue;1380}1381if (!autoRefetch && this.processedExperimentalSettings.has(property)) {1382continue;1383}1384this.processedExperimentalSettings.add(property);1385if (schema.experiment.mode === 'auto') {1386this.autoExperimentalSettings.add(property);1387}1388try {1389const value = await this.workbenchAssignmentService.getTreatment(schema.experiment.name ?? `config.${property}`);1390if (!isUndefined(value) && !equals(value, schema.default)) {1391overrides[property] = value;1392}1393} catch (error) {/*ignore */ }1394}1395if (Object.keys(overrides).length) {1396this.configurationRegistry.registerDefaultConfigurations([{ overrides }]);1397}1398}1399}14001401const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);1402workbenchContributionsRegistry.registerWorkbenchContribution(RegisterConfigurationSchemasContribution, LifecyclePhase.Restored);1403registerWorkbenchContribution2(ConfigurationDefaultOverridesContribution.ID, ConfigurationDefaultOverridesContribution, WorkbenchPhase.BlockRestore);14041405const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);1406configurationRegistry.registerConfiguration({1407...workbenchConfigurationNodeBase,1408properties: {1409[APPLY_ALL_PROFILES_SETTING]: {1410'type': 'array',1411description: localize('setting description', "Configure settings to be applied for all profiles."),1412'default': [],1413'scope': ConfigurationScope.APPLICATION,1414additionalProperties: true,1415uniqueItems: true,1416}1417}1418});141914201421