Path: blob/main/src/vs/workbench/services/configuration/browser/configurationService.ts
3296 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, 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: ResourceMap<FolderConfiguration>;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 },111environmentService: 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(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.cachedFolderConfigs = new ResourceMap<FolderConfiguration>();134this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration)));135if (remoteAuthority) {136const remoteUserConfiguration = this.remoteUserConfiguration = this._register(new RemoteUserConfiguration(remoteAuthority, configurationCache, fileService, uriIdentityService, remoteAgentService, logService));137this._register(remoteUserConfiguration.onDidInitialize(remoteUserConfigurationModel => {138this._register(remoteUserConfiguration.onDidChangeConfiguration(remoteUserConfigurationModel => this.onRemoteUserConfigurationChanged(remoteUserConfigurationModel)));139this.onRemoteUserConfigurationChanged(remoteUserConfigurationModel);140this.initRemoteUserConfigurationBarrier.open();141}));142} else {143this.initRemoteUserConfigurationBarrier.open();144}145146this.workspaceConfiguration = this._register(new WorkspaceConfiguration(configurationCache, fileService, uriIdentityService, logService));147this._register(this.workspaceConfiguration.onDidUpdateConfiguration(fromCache => {148this.onWorkspaceConfigurationChanged(fromCache).then(() => {149this.workspace.initialized = this.workspaceConfiguration.initialized;150this.checkAndMarkWorkspaceComplete(fromCache);151});152}));153154this._register(this.defaultConfiguration.onDidChangeConfiguration(({ properties, defaults }) => this.onDefaultConfigurationChanged(defaults, properties)));155this._register(this.policyConfiguration.onDidChangeConfiguration(configurationModel => this.onPolicyConfigurationChanged(configurationModel)));156this._register(userDataProfileService.onDidChangeCurrentProfile(e => this.onUserDataProfileChanged(e)));157158this.workspaceEditingQueue = new Queue<void>();159}160161private createApplicationConfiguration(): void {162this.applicationConfigurationDisposables.clear();163if (this.userDataProfileService.currentProfile.isDefault || this.userDataProfileService.currentProfile.useDefaultFlags?.settings) {164this.applicationConfiguration = null;165} else {166this.applicationConfiguration = this.applicationConfigurationDisposables.add(this._register(new ApplicationConfiguration(this.userDataProfilesService, this.fileService, this.uriIdentityService, this.logService)));167this.applicationConfigurationDisposables.add(this.applicationConfiguration.onDidChangeConfiguration(configurationModel => this.onApplicationConfigurationChanged(configurationModel)));168}169}170171// Workspace Context Service Impl172173public async getCompleteWorkspace(): Promise<Workspace> {174await this.completeWorkspaceBarrier.wait();175return this.getWorkspace();176}177178public getWorkspace(): Workspace {179return this.workspace;180}181182public getWorkbenchState(): WorkbenchState {183// Workspace has configuration file184if (this.workspace.configuration) {185return WorkbenchState.WORKSPACE;186}187188// Folder has single root189if (this.workspace.folders.length === 1) {190return WorkbenchState.FOLDER;191}192193// Empty194return WorkbenchState.EMPTY;195}196197public getWorkspaceFolder(resource: URI): IWorkspaceFolder | null {198return this.workspace.getFolder(resource);199}200201public addFolders(foldersToAdd: IWorkspaceFolderCreationData[], index?: number): Promise<void> {202return this.updateFolders(foldersToAdd, [], index);203}204205public removeFolders(foldersToRemove: URI[]): Promise<void> {206return this.updateFolders([], foldersToRemove);207}208209public async updateFolders(foldersToAdd: IWorkspaceFolderCreationData[], foldersToRemove: URI[], index?: number): Promise<void> {210return this.workspaceEditingQueue.queue(() => this.doUpdateFolders(foldersToAdd, foldersToRemove, index));211}212213public isInsideWorkspace(resource: URI): boolean {214return !!this.getWorkspaceFolder(resource);215}216217public isCurrentWorkspace(workspaceIdOrFolder: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI): boolean {218switch (this.getWorkbenchState()) {219case WorkbenchState.FOLDER: {220let folderUri: URI | undefined = undefined;221if (URI.isUri(workspaceIdOrFolder)) {222folderUri = workspaceIdOrFolder;223} else if (isSingleFolderWorkspaceIdentifier(workspaceIdOrFolder)) {224folderUri = workspaceIdOrFolder.uri;225}226227return URI.isUri(folderUri) && this.uriIdentityService.extUri.isEqual(folderUri, this.workspace.folders[0].uri);228}229case WorkbenchState.WORKSPACE:230return isWorkspaceIdentifier(workspaceIdOrFolder) && this.workspace.id === workspaceIdOrFolder.id;231}232return false;233}234235private async doUpdateFolders(foldersToAdd: IWorkspaceFolderCreationData[], foldersToRemove: URI[], index?: number): Promise<void> {236if (this.getWorkbenchState() !== WorkbenchState.WORKSPACE) {237return Promise.resolve(undefined); // we need a workspace to begin with238}239240if (foldersToAdd.length + foldersToRemove.length === 0) {241return Promise.resolve(undefined); // nothing to do242}243244let foldersHaveChanged = false;245246// Remove first (if any)247let currentWorkspaceFolders = this.getWorkspace().folders;248let newStoredFolders: IStoredWorkspaceFolder[] = currentWorkspaceFolders.map(f => f.raw).filter((folder, index): folder is IStoredWorkspaceFolder => {249if (!isStoredWorkspaceFolder(folder)) {250return true; // keep entries which are unrelated251}252253return !this.contains(foldersToRemove, currentWorkspaceFolders[index].uri); // keep entries which are unrelated254});255256foldersHaveChanged = currentWorkspaceFolders.length !== newStoredFolders.length;257258// Add afterwards (if any)259if (foldersToAdd.length) {260261// Recompute current workspace folders if we have folders to add262const workspaceConfigPath = this.getWorkspace().configuration!;263const workspaceConfigFolder = this.uriIdentityService.extUri.dirname(workspaceConfigPath);264currentWorkspaceFolders = toWorkspaceFolders(newStoredFolders, workspaceConfigPath, this.uriIdentityService.extUri);265const currentWorkspaceFolderUris = currentWorkspaceFolders.map(folder => folder.uri);266267const storedFoldersToAdd: IStoredWorkspaceFolder[] = [];268269for (const folderToAdd of foldersToAdd) {270const folderURI = folderToAdd.uri;271if (this.contains(currentWorkspaceFolderUris, folderURI)) {272continue; // already existing273}274try {275const result = await this.fileService.stat(folderURI);276if (!result.isDirectory) {277continue;278}279} catch (e) { /* Ignore */ }280storedFoldersToAdd.push(getStoredWorkspaceFolder(folderURI, false, folderToAdd.name, workspaceConfigFolder, this.uriIdentityService.extUri));281}282283// Apply to array of newStoredFolders284if (storedFoldersToAdd.length > 0) {285foldersHaveChanged = true;286287if (typeof index === 'number' && index >= 0 && index < newStoredFolders.length) {288newStoredFolders = newStoredFolders.slice(0);289newStoredFolders.splice(index, 0, ...storedFoldersToAdd);290} else {291newStoredFolders = [...newStoredFolders, ...storedFoldersToAdd];292}293}294}295296// Set folders if we recorded a change297if (foldersHaveChanged) {298return this.setFolders(newStoredFolders);299}300301return Promise.resolve(undefined);302}303304private async setFolders(folders: IStoredWorkspaceFolder[]): Promise<void> {305if (!this.instantiationService) {306throw new Error('Cannot update workspace folders because workspace service is not yet ready to accept writes.');307}308309await this.instantiationService.invokeFunction(accessor => this.workspaceConfiguration.setFolders(folders, accessor.get(IJSONEditingService)));310return this.onWorkspaceConfigurationChanged(false);311}312313private contains(resources: URI[], toCheck: URI): boolean {314return resources.some(resource => this.uriIdentityService.extUri.isEqual(resource, toCheck));315}316317// Workspace Configuration Service Impl318319getConfigurationData(): IConfigurationData {320return this._configuration.toData();321}322323getValue<T>(): T;324getValue<T>(section: string): T;325getValue<T>(overrides: IConfigurationOverrides): T;326getValue<T>(section: string, overrides: IConfigurationOverrides): T;327getValue(arg1?: any, arg2?: any): any {328const section = typeof arg1 === 'string' ? arg1 : undefined;329const overrides = isConfigurationOverrides(arg1) ? arg1 : isConfigurationOverrides(arg2) ? arg2 : undefined;330return this._configuration.getValue(section, overrides);331}332333updateValue(key: string, value: any): Promise<void>;334updateValue(key: string, value: unknown, overrides: IConfigurationOverrides | IConfigurationUpdateOverrides): Promise<void>;335updateValue(key: string, value: unknown, target: ConfigurationTarget): Promise<void>;336updateValue(key: string, value: unknown, overrides: IConfigurationOverrides | IConfigurationUpdateOverrides, target: ConfigurationTarget, options?: IConfigurationUpdateOptions): Promise<void>;337async updateValue(key: string, value: unknown, arg3?: any, arg4?: any, options?: any): Promise<void> {338const overrides: IConfigurationUpdateOverrides | undefined = isConfigurationUpdateOverrides(arg3) ? arg3339: isConfigurationOverrides(arg3) ? { resource: arg3.resource, overrideIdentifiers: arg3.overrideIdentifier ? [arg3.overrideIdentifier] : undefined } : undefined;340const target: ConfigurationTarget | undefined = overrides ? arg4 : arg3;341const targets: ConfigurationTarget[] = target ? [target] : [];342343if (overrides?.overrideIdentifiers) {344overrides.overrideIdentifiers = distinct(overrides.overrideIdentifiers);345overrides.overrideIdentifiers = overrides.overrideIdentifiers.length ? overrides.overrideIdentifiers : undefined;346}347348if (!targets.length) {349if (overrides?.overrideIdentifiers && overrides.overrideIdentifiers.length > 1) {350throw new Error('Configuration Target is required while updating the value for multiple override identifiers');351}352const inspect = this.inspect(key, { resource: overrides?.resource, overrideIdentifier: overrides?.overrideIdentifiers ? overrides.overrideIdentifiers[0] : undefined });353targets.push(...this.deriveConfigurationTargets(key, value, inspect));354355// Remove the setting, if the value is same as default value and is updated only in user target356if (equals(value, inspect.defaultValue) && targets.length === 1 && (targets[0] === ConfigurationTarget.USER || targets[0] === ConfigurationTarget.USER_LOCAL)) {357value = undefined;358}359}360361await Promises.settled(targets.map(target => this.writeConfigurationValue(key, value, target, overrides, options)));362}363364async reloadConfiguration(target?: ConfigurationTarget | IWorkspaceFolder): Promise<void> {365if (target === undefined) {366this.reloadDefaultConfiguration();367const application = await this.reloadApplicationConfiguration(true);368const { local, remote } = await this.reloadUserConfiguration();369await this.reloadWorkspaceConfiguration();370await this.loadConfiguration(application, local, remote, true);371return;372}373374if (isWorkspaceFolder(target)) {375await this.reloadWorkspaceFolderConfiguration(target);376return;377}378379switch (target) {380case ConfigurationTarget.DEFAULT:381this.reloadDefaultConfiguration();382return;383384case ConfigurationTarget.USER: {385const { local, remote } = await this.reloadUserConfiguration();386await this.loadConfiguration(this._configuration.applicationConfiguration, local, remote, true);387return;388}389case ConfigurationTarget.USER_LOCAL:390await this.reloadLocalUserConfiguration();391return;392393case ConfigurationTarget.USER_REMOTE:394await this.reloadRemoteUserConfiguration();395return;396397case ConfigurationTarget.WORKSPACE:398case ConfigurationTarget.WORKSPACE_FOLDER:399await this.reloadWorkspaceConfiguration();400return;401}402}403404hasCachedConfigurationDefaultsOverrides(): boolean {405return this.defaultConfiguration.hasCachedConfigurationDefaultsOverrides();406}407408inspect<T>(key: string, overrides?: IConfigurationOverrides): IConfigurationValue<T> {409return this._configuration.inspect<T>(key, overrides);410}411412keys(): {413default: 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 workspace = new Workspace(workspaceId, workspaceFolders, this.workspaceConfiguration.isTransient(), workspaceConfigPath, uri => this.uriIdentityService.extUri.ignorePathCasing(uri));522workspace.initialized = this.workspaceConfiguration.initialized;523return workspace;524}525526private createSingleFolderWorkspace(singleFolderWorkspaceIdentifier: ISingleFolderWorkspaceIdentifier): Workspace {527const workspace = new Workspace(singleFolderWorkspaceIdentifier.id, [toWorkspaceFolder(singleFolderWorkspaceIdentifier.uri)], false, null, uri => this.uriIdentityService.extUri.ignorePathCasing(uri));528workspace.initialized = true;529return workspace;530}531532private createEmptyWorkspace(emptyWorkspaceIdentifier: IEmptyWorkspaceIdentifier): Promise<Workspace> {533const workspace = new Workspace(emptyWorkspaceIdentifier.id, [], false, null, uri => this.uriIdentityService.extUri.ignorePathCasing(uri));534workspace.initialized = true;535return Promise.resolve(workspace);536}537538private checkAndMarkWorkspaceComplete(fromCache: boolean): void {539if (!this.completeWorkspaceBarrier.isOpen() && this.workspace.initialized) {540this.completeWorkspaceBarrier.open();541this.validateWorkspaceFoldersAndReload(fromCache);542}543}544545private async updateWorkspaceAndInitializeConfiguration(workspace: Workspace, trigger: boolean): Promise<void> {546const hasWorkspaceBefore = !!this.workspace;547let previousState: WorkbenchState | undefined;548let previousWorkspacePath: string | undefined;549let previousFolders: WorkspaceFolder[] = [];550551if (hasWorkspaceBefore) {552previousState = this.getWorkbenchState();553previousWorkspacePath = this.workspace.configuration ? this.workspace.configuration.fsPath : undefined;554previousFolders = this.workspace.folders;555this.workspace.update(workspace);556} else {557this.workspace = workspace;558}559560await this.initializeConfiguration(trigger);561562// Trigger changes after configuration initialization so that configuration is up to date.563if (hasWorkspaceBefore) {564const newState = this.getWorkbenchState();565if (previousState && newState !== previousState) {566this._onDidChangeWorkbenchState.fire(newState);567}568569const newWorkspacePath = this.workspace.configuration ? this.workspace.configuration.fsPath : undefined;570if (previousWorkspacePath && newWorkspacePath !== previousWorkspacePath || newState !== previousState) {571this._onDidChangeWorkspaceName.fire();572}573574const folderChanges = this.compareFolders(previousFolders, this.workspace.folders);575if (folderChanges && (folderChanges.added.length || folderChanges.removed.length || folderChanges.changed.length)) {576await this.handleWillChangeWorkspaceFolders(folderChanges, false);577this._onDidChangeWorkspaceFolders.fire(folderChanges);578}579}580581if (!this.localUserConfiguration.hasTasksLoaded) {582// Reload local user configuration again to load user tasks583this._register(runWhenWindowIdle(mainWindow, () => this.reloadLocalUserConfiguration(false, this._configuration.localUserConfiguration)));584}585}586587private compareFolders(currentFolders: IWorkspaceFolder[], newFolders: IWorkspaceFolder[]): IWorkspaceFoldersChangeEvent {588const result: IWorkspaceFoldersChangeEvent = { added: [], removed: [], changed: [] };589result.added = newFolders.filter(newFolder => !currentFolders.some(currentFolder => newFolder.uri.toString() === currentFolder.uri.toString()));590for (let currentIndex = 0; currentIndex < currentFolders.length; currentIndex++) {591const currentFolder = currentFolders[currentIndex];592let newIndex = 0;593for (newIndex = 0; newIndex < newFolders.length && currentFolder.uri.toString() !== newFolders[newIndex].uri.toString(); newIndex++) { }594if (newIndex < newFolders.length) {595if (currentIndex !== newIndex || currentFolder.name !== newFolders[newIndex].name) {596result.changed.push(currentFolder);597}598} else {599result.removed.push(currentFolder);600}601}602return result;603}604605private async initializeConfiguration(trigger: boolean): Promise<void> {606await this.defaultConfiguration.initialize();607608const initPolicyConfigurationPromise = this.policyConfiguration.initialize();609const initApplicationConfigurationPromise = this.applicationConfiguration ? this.applicationConfiguration.initialize() : Promise.resolve(ConfigurationModel.createEmptyModel(this.logService));610const initUserConfiguration = async () => {611mark('code/willInitUserConfiguration');612const result = await Promise.all([this.localUserConfiguration.initialize(), this.remoteUserConfiguration ? this.remoteUserConfiguration.initialize() : Promise.resolve(ConfigurationModel.createEmptyModel(this.logService))]);613if (this.applicationConfiguration) {614const applicationConfigurationModel = await initApplicationConfigurationPromise;615result[0] = this.localUserConfiguration.reparse({ exclude: applicationConfigurationModel.getValue(APPLY_ALL_PROFILES_SETTING) });616}617mark('code/didInitUserConfiguration');618return result;619};620621const [, application, [local, remote]] = await Promise.all([622initPolicyConfigurationPromise,623initApplicationConfigurationPromise,624initUserConfiguration()625]);626627mark('code/willInitWorkspaceConfiguration');628await this.loadConfiguration(application, local, remote, trigger);629mark('code/didInitWorkspaceConfiguration');630}631632private reloadDefaultConfiguration(): void {633this.onDefaultConfigurationChanged(this.defaultConfiguration.reload());634}635636private async reloadApplicationConfiguration(donotTrigger?: boolean): Promise<ConfigurationModel> {637if (!this.applicationConfiguration) {638return ConfigurationModel.createEmptyModel(this.logService);639}640const model = await this.applicationConfiguration.loadConfiguration();641if (!donotTrigger) {642this.onApplicationConfigurationChanged(model);643}644return model;645}646647private async reloadUserConfiguration(): Promise<{ local: ConfigurationModel; remote: ConfigurationModel }> {648const [local, remote] = await Promise.all([this.reloadLocalUserConfiguration(true), this.reloadRemoteUserConfiguration(true)]);649return { local, remote };650}651652async reloadLocalUserConfiguration(donotTrigger?: boolean, settingsConfiguration?: ConfigurationModel): Promise<ConfigurationModel> {653const model = await this.localUserConfiguration.reload(settingsConfiguration);654if (!donotTrigger) {655this.onLocalUserConfigurationChanged(model);656}657return model;658}659660private async reloadRemoteUserConfiguration(donotTrigger?: boolean): Promise<ConfigurationModel> {661if (this.remoteUserConfiguration) {662const model = await this.remoteUserConfiguration.reload();663if (!donotTrigger) {664this.onRemoteUserConfigurationChanged(model);665}666return model;667}668return ConfigurationModel.createEmptyModel(this.logService);669}670671private async reloadWorkspaceConfiguration(): Promise<void> {672const workbenchState = this.getWorkbenchState();673if (workbenchState === WorkbenchState.FOLDER) {674return this.onWorkspaceFolderConfigurationChanged(this.workspace.folders[0]);675}676if (workbenchState === WorkbenchState.WORKSPACE) {677return this.workspaceConfiguration.reload().then(() => this.onWorkspaceConfigurationChanged(false));678}679}680681private reloadWorkspaceFolderConfiguration(folder: IWorkspaceFolder): Promise<void> {682return this.onWorkspaceFolderConfigurationChanged(folder);683}684685private async loadConfiguration(applicationConfigurationModel: ConfigurationModel, userConfigurationModel: ConfigurationModel, remoteUserConfigurationModel: ConfigurationModel, trigger: boolean): Promise<void> {686// reset caches687this.cachedFolderConfigs = new ResourceMap<FolderConfiguration>();688689const folders = this.workspace.folders;690const folderConfigurations = await this.loadFolderConfigurations(folders);691692const workspaceConfiguration = this.getWorkspaceConfigurationModel(folderConfigurations);693const folderConfigurationModels = new ResourceMap<ConfigurationModel>();694folderConfigurations.forEach((folderConfiguration, index) => folderConfigurationModels.set(folders[index].uri, folderConfiguration));695696const currentConfiguration = this._configuration;697this._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);698699this.initialized = true;700701if (trigger) {702const change = this._configuration.compare(currentConfiguration);703this.triggerConfigurationChange(change, { data: currentConfiguration.toData(), workspace: this.workspace }, ConfigurationTarget.WORKSPACE);704}705706this.updateRestrictedSettings();707}708709private getWorkspaceConfigurationModel(folderConfigurations: ConfigurationModel[]): ConfigurationModel {710switch (this.getWorkbenchState()) {711case WorkbenchState.FOLDER:712return folderConfigurations[0];713case WorkbenchState.WORKSPACE:714return this.workspaceConfiguration.getConfiguration();715default:716return ConfigurationModel.createEmptyModel(this.logService);717}718}719720private onUserDataProfileChanged(e: DidChangeUserDataProfileEvent): void {721e.join((async () => {722const promises: Promise<ConfigurationModel>[] = [];723promises.push(this.localUserConfiguration.reset(e.profile.settingsResource, e.profile.tasksResource, e.profile.mcpResource, { scopes: getLocalUserConfigurationScopes(e.profile, !!this.remoteUserConfiguration) }));724if (e.previous.isDefault !== e.profile.isDefault725|| !!e.previous.useDefaultFlags?.settings !== !!e.profile.useDefaultFlags?.settings) {726this.createApplicationConfiguration();727if (this.applicationConfiguration) {728promises.push(this.reloadApplicationConfiguration(true));729}730}731let [localUser, application] = await Promise.all(promises);732application = application ?? this._configuration.applicationConfiguration;733if (this.applicationConfiguration) {734localUser = this.localUserConfiguration.reparse({ exclude: application.getValue(APPLY_ALL_PROFILES_SETTING) });735}736await this.loadConfiguration(application, localUser, this._configuration.remoteUserConfiguration, true);737})());738}739740private onDefaultConfigurationChanged(configurationModel: ConfigurationModel, properties?: string[]): void {741if (this.workspace) {742const previousData = this._configuration.toData();743const change = this._configuration.compareAndUpdateDefaultConfiguration(configurationModel, properties);744if (this.applicationConfiguration) {745this._configuration.updateApplicationConfiguration(this.applicationConfiguration.reparse());746}747if (this.remoteUserConfiguration) {748this._configuration.updateLocalUserConfiguration(this.localUserConfiguration.reparse());749this._configuration.updateRemoteUserConfiguration(this.remoteUserConfiguration.reparse());750}751if (this.getWorkbenchState() === WorkbenchState.FOLDER) {752const folderConfiguration = this.cachedFolderConfigs.get(this.workspace.folders[0].uri);753if (folderConfiguration) {754this._configuration.updateWorkspaceConfiguration(folderConfiguration.reparse());755this._configuration.updateFolderConfiguration(this.workspace.folders[0].uri, folderConfiguration.reparse());756}757} else {758this._configuration.updateWorkspaceConfiguration(this.workspaceConfiguration.reparseWorkspaceSettings());759for (const folder of this.workspace.folders) {760const folderConfiguration = this.cachedFolderConfigs.get(folder.uri);761if (folderConfiguration) {762this._configuration.updateFolderConfiguration(folder.uri, folderConfiguration.reparse());763}764}765}766this.triggerConfigurationChange(change, { data: previousData, workspace: this.workspace }, ConfigurationTarget.DEFAULT);767this.updateRestrictedSettings();768}769}770771private onPolicyConfigurationChanged(policyConfiguration: ConfigurationModel): void {772const previous = { data: this._configuration.toData(), workspace: this.workspace };773const change = this._configuration.compareAndUpdatePolicyConfiguration(policyConfiguration);774this.triggerConfigurationChange(change, previous, ConfigurationTarget.DEFAULT);775}776777private onApplicationConfigurationChanged(applicationConfiguration: ConfigurationModel): void {778const previous = { data: this._configuration.toData(), workspace: this.workspace };779const previousAllProfilesSettings = this._configuration.applicationConfiguration.getValue<string[]>(APPLY_ALL_PROFILES_SETTING) ?? [];780const change = this._configuration.compareAndUpdateApplicationConfiguration(applicationConfiguration);781const currentAllProfilesSettings = this.getValue<string[]>(APPLY_ALL_PROFILES_SETTING) ?? [];782const configurationProperties = this.configurationRegistry.getConfigurationProperties();783const changedKeys: string[] = [];784for (const changedKey of change.keys) {785const scope = configurationProperties[changedKey]?.scope;786if (scope && APPLICATION_SCOPES.includes(scope)) {787changedKeys.push(changedKey);788if (changedKey === APPLY_ALL_PROFILES_SETTING) {789for (const previousAllProfileSetting of previousAllProfilesSettings) {790if (!currentAllProfilesSettings.includes(previousAllProfileSetting)) {791changedKeys.push(previousAllProfileSetting);792}793}794for (const currentAllProfileSetting of currentAllProfilesSettings) {795if (!previousAllProfilesSettings.includes(currentAllProfileSetting)) {796changedKeys.push(currentAllProfileSetting);797}798}799}800}801else if (currentAllProfilesSettings.includes(changedKey)) {802changedKeys.push(changedKey);803}804}805change.keys = changedKeys;806if (change.keys.includes(APPLY_ALL_PROFILES_SETTING)) {807this._configuration.updateLocalUserConfiguration(this.localUserConfiguration.reparse({ exclude: currentAllProfilesSettings }));808}809this.triggerConfigurationChange(change, previous, ConfigurationTarget.USER);810}811812private onLocalUserConfigurationChanged(userConfiguration: ConfigurationModel): void {813const previous = { data: this._configuration.toData(), workspace: this.workspace };814const change = this._configuration.compareAndUpdateLocalUserConfiguration(userConfiguration);815this.triggerConfigurationChange(change, previous, ConfigurationTarget.USER);816}817818private onRemoteUserConfigurationChanged(userConfiguration: ConfigurationModel): void {819const previous = { data: this._configuration.toData(), workspace: this.workspace };820const change = this._configuration.compareAndUpdateRemoteUserConfiguration(userConfiguration);821this.triggerConfigurationChange(change, previous, ConfigurationTarget.USER);822}823824private async onWorkspaceConfigurationChanged(fromCache: boolean): Promise<void> {825if (this.workspace && this.workspace.configuration) {826let newFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), this.workspace.configuration, this.uriIdentityService.extUri);827828// Validate only if workspace is initialized829if (this.workspace.initialized) {830const { added, removed, changed } = this.compareFolders(this.workspace.folders, newFolders);831832/* If changed validate new folders */833if (added.length || removed.length || changed.length) {834newFolders = await this.toValidWorkspaceFolders(newFolders);835}836/* Otherwise use existing */837else {838newFolders = this.workspace.folders;839}840}841842await this.updateWorkspaceConfiguration(newFolders, this.workspaceConfiguration.getConfiguration(), fromCache);843}844}845846private updateRestrictedSettings(): void {847const changed: string[] = [];848849const allProperties = this.configurationRegistry.getConfigurationProperties();850const defaultRestrictedSettings: string[] = Object.keys(allProperties).filter(key => allProperties[key].restricted).sort((a, b) => a.localeCompare(b));851const defaultDelta = delta(defaultRestrictedSettings, this._restrictedSettings.default, (a, b) => a.localeCompare(b));852changed.push(...defaultDelta.added, ...defaultDelta.removed);853854const application = (this.applicationConfiguration?.getRestrictedSettings() || []).sort((a, b) => a.localeCompare(b));855const applicationDelta = delta(application, this._restrictedSettings.application || [], (a, b) => a.localeCompare(b));856changed.push(...applicationDelta.added, ...applicationDelta.removed);857858const userLocal = this.localUserConfiguration.getRestrictedSettings().sort((a, b) => a.localeCompare(b));859const userLocalDelta = delta(userLocal, this._restrictedSettings.userLocal || [], (a, b) => a.localeCompare(b));860changed.push(...userLocalDelta.added, ...userLocalDelta.removed);861862const userRemote = (this.remoteUserConfiguration?.getRestrictedSettings() || []).sort((a, b) => a.localeCompare(b));863const userRemoteDelta = delta(userRemote, this._restrictedSettings.userRemote || [], (a, b) => a.localeCompare(b));864changed.push(...userRemoteDelta.added, ...userRemoteDelta.removed);865866const workspaceFolderMap = new ResourceMap<ReadonlyArray<string>>();867for (const workspaceFolder of this.workspace.folders) {868const cachedFolderConfig = this.cachedFolderConfigs.get(workspaceFolder.uri);869const folderRestrictedSettings = (cachedFolderConfig?.getRestrictedSettings() || []).sort((a, b) => a.localeCompare(b));870if (folderRestrictedSettings.length) {871workspaceFolderMap.set(workspaceFolder.uri, folderRestrictedSettings);872}873const previous = this._restrictedSettings.workspaceFolder?.get(workspaceFolder.uri) || [];874const workspaceFolderDelta = delta(folderRestrictedSettings, previous, (a, b) => a.localeCompare(b));875changed.push(...workspaceFolderDelta.added, ...workspaceFolderDelta.removed);876}877878const workspace = this.getWorkbenchState() === WorkbenchState.WORKSPACE ? this.workspaceConfiguration.getRestrictedSettings().sort((a, b) => a.localeCompare(b))879: this.workspace.folders[0] ? (workspaceFolderMap.get(this.workspace.folders[0].uri) || []) : [];880const workspaceDelta = delta(workspace, this._restrictedSettings.workspace || [], (a, b) => a.localeCompare(b));881changed.push(...workspaceDelta.added, ...workspaceDelta.removed);882883if (changed.length) {884this._restrictedSettings = {885default: defaultRestrictedSettings,886application: application.length ? application : undefined,887userLocal: userLocal.length ? userLocal : undefined,888userRemote: userRemote.length ? userRemote : undefined,889workspace: workspace.length ? workspace : undefined,890workspaceFolder: workspaceFolderMap.size ? workspaceFolderMap : undefined,891};892this._onDidChangeRestrictedSettings.fire(this.restrictedSettings);893}894}895896private async updateWorkspaceConfiguration(workspaceFolders: WorkspaceFolder[], configuration: ConfigurationModel, fromCache: boolean): Promise<void> {897const previous = { data: this._configuration.toData(), workspace: this.workspace };898const change = this._configuration.compareAndUpdateWorkspaceConfiguration(configuration);899const changes = this.compareFolders(this.workspace.folders, workspaceFolders);900if (changes.added.length || changes.removed.length || changes.changed.length) {901this.workspace.folders = workspaceFolders;902const change = await this.onFoldersChanged();903await this.handleWillChangeWorkspaceFolders(changes, fromCache);904this.triggerConfigurationChange(change, previous, ConfigurationTarget.WORKSPACE_FOLDER);905this._onDidChangeWorkspaceFolders.fire(changes);906} else {907this.triggerConfigurationChange(change, previous, ConfigurationTarget.WORKSPACE);908}909this.updateRestrictedSettings();910}911912private async handleWillChangeWorkspaceFolders(changes: IWorkspaceFoldersChangeEvent, fromCache: boolean): Promise<void> {913const joiners: Promise<void>[] = [];914this._onWillChangeWorkspaceFolders.fire({915join(updateWorkspaceTrustStatePromise) {916joiners.push(updateWorkspaceTrustStatePromise);917},918changes,919fromCache920});921try { await Promises.settled(joiners); } catch (error) { /* Ignore */ }922}923924private async onWorkspaceFolderConfigurationChanged(folder: IWorkspaceFolder): Promise<void> {925const [folderConfiguration] = await this.loadFolderConfigurations([folder]);926const previous = { data: this._configuration.toData(), workspace: this.workspace };927const folderConfigurationChange = this._configuration.compareAndUpdateFolderConfiguration(folder.uri, folderConfiguration);928if (this.getWorkbenchState() === WorkbenchState.FOLDER) {929const workspaceConfigurationChange = this._configuration.compareAndUpdateWorkspaceConfiguration(folderConfiguration);930this.triggerConfigurationChange(mergeChanges(folderConfigurationChange, workspaceConfigurationChange), previous, ConfigurationTarget.WORKSPACE);931} else {932this.triggerConfigurationChange(folderConfigurationChange, previous, ConfigurationTarget.WORKSPACE_FOLDER);933}934this.updateRestrictedSettings();935}936937private async onFoldersChanged(): Promise<IConfigurationChange> {938const changes: IConfigurationChange[] = [];939940// Remove the configurations of deleted folders941for (const key of this.cachedFolderConfigs.keys()) {942if (!this.workspace.folders.filter(folder => folder.uri.toString() === key.toString())[0]) {943const folderConfiguration = this.cachedFolderConfigs.get(key);944folderConfiguration!.dispose();945this.cachedFolderConfigs.delete(key);946changes.push(this._configuration.compareAndDeleteFolderConfiguration(key));947}948}949950const toInitialize = this.workspace.folders.filter(folder => !this.cachedFolderConfigs.has(folder.uri));951if (toInitialize.length) {952const folderConfigurations = await this.loadFolderConfigurations(toInitialize);953folderConfigurations.forEach((folderConfiguration, index) => {954changes.push(this._configuration.compareAndUpdateFolderConfiguration(toInitialize[index].uri, folderConfiguration));955});956}957return mergeChanges(...changes);958}959960private loadFolderConfigurations(folders: IWorkspaceFolder[]): Promise<ConfigurationModel[]> {961return Promise.all([...folders.map(folder => {962let folderConfiguration = this.cachedFolderConfigs.get(folder.uri);963if (!folderConfiguration) {964folderConfiguration = new FolderConfiguration(!this.initialized, folder, FOLDER_CONFIG_FOLDER_NAME, this.getWorkbenchState(), this.isWorkspaceTrusted, this.fileService, this.uriIdentityService, this.logService, this.configurationCache);965this._register(folderConfiguration.onDidChange(() => this.onWorkspaceFolderConfigurationChanged(folder)));966this.cachedFolderConfigs.set(folder.uri, this._register(folderConfiguration));967}968return folderConfiguration.loadConfiguration();969})]);970}971972private async validateWorkspaceFoldersAndReload(fromCache: boolean): Promise<void> {973const validWorkspaceFolders = await this.toValidWorkspaceFolders(this.workspace.folders);974const { removed } = this.compareFolders(this.workspace.folders, validWorkspaceFolders);975if (removed.length) {976await this.updateWorkspaceConfiguration(validWorkspaceFolders, this.workspaceConfiguration.getConfiguration(), fromCache);977}978}979980// 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.982private async toValidWorkspaceFolders(workspaceFolders: WorkspaceFolder[]): Promise<WorkspaceFolder[]> {983const validWorkspaceFolders: WorkspaceFolder[] = [];984for (const workspaceFolder of workspaceFolders) {985try {986const result = await this.fileService.stat(workspaceFolder.uri);987if (!result.isDirectory) {988continue;989}990} catch (e) {991this.logService.warn(`Ignoring the error while validating workspace folder ${workspaceFolder.uri.toString()} - ${toErrorMessage(e)}`);992}993validWorkspaceFolders.push(workspaceFolder);994}995return validWorkspaceFolders;996}997998private async writeConfigurationValue(key: string, value: unknown, target: ConfigurationTarget, overrides: IConfigurationUpdateOverrides | undefined, options?: IConfigurationUpdateOverrides): Promise<void> {999if (!this.instantiationService) {1000throw new Error('Cannot write configuration because the configuration service is not yet ready to accept writes.');1001}10021003if (target === ConfigurationTarget.DEFAULT) {1004throw new Error('Invalid configuration target');1005}10061007if (target === ConfigurationTarget.MEMORY) {1008const previous = { data: this._configuration.toData(), workspace: this.workspace };1009this._configuration.updateValue(key, value, overrides);1010this.triggerConfigurationChange({ keys: overrides?.overrideIdentifiers?.length ? [keyFromOverrideIdentifiers(overrides.overrideIdentifiers), key] : [key], overrides: overrides?.overrideIdentifiers?.length ? overrides.overrideIdentifiers.map(overrideIdentifier => ([overrideIdentifier, [key]])) : [] }, previous, target);1011return;1012}10131014const editableConfigurationTarget = this.toEditableConfigurationTarget(target, key);1015if (!editableConfigurationTarget) {1016throw new Error('Invalid configuration target');1017}10181019if (editableConfigurationTarget === EditableConfigurationTarget.USER_REMOTE && !this.remoteUserConfiguration) {1020throw new Error('Invalid configuration target');1021}10221023if (overrides?.overrideIdentifiers?.length && overrides.overrideIdentifiers.length > 1) {1024const configurationModel = this.getConfigurationModelForEditableConfigurationTarget(editableConfigurationTarget, overrides.resource);1025if (configurationModel) {1026const overrideIdentifiers = overrides.overrideIdentifiers.sort();1027const existingOverrides = configurationModel.overrides.find(override => arrayEquals([...override.identifiers].sort(), overrideIdentifiers));1028if (existingOverrides) {1029overrides.overrideIdentifiers = existingOverrides.identifiers;1030}1031}1032}10331034// Use same instance of ConfigurationEditing to make sure all writes go through the same queue1035this.configurationEditing = this.configurationEditing ?? this.createConfigurationEditingService(this.instantiationService);1036await (await this.configurationEditing).writeConfiguration(editableConfigurationTarget, { key, value }, { scopes: overrides, ...options });1037switch (editableConfigurationTarget) {1038case EditableConfigurationTarget.USER_LOCAL:1039if (this.applicationConfiguration && this.isSettingAppliedForAllProfiles(key)) {1040await this.reloadApplicationConfiguration();1041} else {1042await this.reloadLocalUserConfiguration();1043}1044return;1045case EditableConfigurationTarget.USER_REMOTE:1046return this.reloadRemoteUserConfiguration().then(() => undefined);1047case EditableConfigurationTarget.WORKSPACE:1048return this.reloadWorkspaceConfiguration();1049case EditableConfigurationTarget.WORKSPACE_FOLDER: {1050const workspaceFolder = overrides && overrides.resource ? this.workspace.getFolder(overrides.resource) : null;1051if (workspaceFolder) {1052return this.reloadWorkspaceFolderConfiguration(workspaceFolder);1053}1054}1055}1056}10571058private async createConfigurationEditingService(instantiationService: IInstantiationService): Promise<ConfigurationEditing> {1059const remoteSettingsResource = (await this.remoteAgentService.getEnvironment())?.settingsPath ?? null;1060return instantiationService.createInstance(ConfigurationEditing, remoteSettingsResource);1061}10621063private getConfigurationModelForEditableConfigurationTarget(target: EditableConfigurationTarget, resource?: URI | null): ConfigurationModel | undefined {1064switch (target) {1065case EditableConfigurationTarget.USER_LOCAL: return this._configuration.localUserConfiguration;1066case EditableConfigurationTarget.USER_REMOTE: return this._configuration.remoteUserConfiguration;1067case EditableConfigurationTarget.WORKSPACE: return this._configuration.workspaceConfiguration;1068case EditableConfigurationTarget.WORKSPACE_FOLDER: return resource ? this._configuration.folderConfigurations.get(resource) : undefined;1069}1070}10711072getConfigurationModel(target: ConfigurationTarget, resource?: URI | null): ConfigurationModel | undefined {1073switch (target) {1074case ConfigurationTarget.USER_LOCAL: return this._configuration.localUserConfiguration;1075case ConfigurationTarget.USER_REMOTE: return this._configuration.remoteUserConfiguration;1076case ConfigurationTarget.WORKSPACE: return this._configuration.workspaceConfiguration;1077case ConfigurationTarget.WORKSPACE_FOLDER: return resource ? this._configuration.folderConfigurations.get(resource) : undefined;1078default: return undefined;1079}1080}10811082private deriveConfigurationTargets(key: string, value: unknown, inspect: IConfigurationValue<any>): ConfigurationTarget[] {1083if (equals(value, inspect.value)) {1084return [];1085}10861087const definedTargets: ConfigurationTarget[] = [];1088if (inspect.workspaceFolderValue !== undefined) {1089definedTargets.push(ConfigurationTarget.WORKSPACE_FOLDER);1090}1091if (inspect.workspaceValue !== undefined) {1092definedTargets.push(ConfigurationTarget.WORKSPACE);1093}1094if (inspect.userRemoteValue !== undefined) {1095definedTargets.push(ConfigurationTarget.USER_REMOTE);1096}1097if (inspect.userLocalValue !== undefined) {1098definedTargets.push(ConfigurationTarget.USER_LOCAL);1099}1100if (inspect.applicationValue !== undefined) {1101definedTargets.push(ConfigurationTarget.APPLICATION);1102}11031104if (value === undefined) {1105// Remove the setting in all defined targets1106return definedTargets;1107}11081109return [definedTargets[0] || ConfigurationTarget.USER];1110}11111112private triggerConfigurationChange(change: IConfigurationChange, previous: { data: IConfigurationData; workspace?: Workspace } | undefined, target: ConfigurationTarget): void {1113if (change.keys.length) {1114if (target !== ConfigurationTarget.DEFAULT) {1115this.logService.debug(`Configuration keys changed in ${ConfigurationTargetToString(target)} target`, ...change.keys);1116}1117const configurationChangeEvent = new ConfigurationChangeEvent(change, previous, this._configuration, this.workspace, this.logService);1118configurationChangeEvent.source = target;1119this._onDidChangeConfiguration.fire(configurationChangeEvent);1120}1121}11221123private toEditableConfigurationTarget(target: ConfigurationTarget, key: string): EditableConfigurationTarget | null {1124if (target === ConfigurationTarget.APPLICATION) {1125return EditableConfigurationTarget.USER_LOCAL;1126}1127if (target === ConfigurationTarget.USER) {1128if (this.remoteUserConfiguration) {1129const scope = this.configurationRegistry.getConfigurationProperties()[key]?.scope;1130if (scope === ConfigurationScope.MACHINE || scope === ConfigurationScope.MACHINE_OVERRIDABLE || scope === ConfigurationScope.APPLICATION_MACHINE) {1131return EditableConfigurationTarget.USER_REMOTE;1132}1133if (this.inspect(key).userRemoteValue !== undefined) {1134return EditableConfigurationTarget.USER_REMOTE;1135}1136}1137return EditableConfigurationTarget.USER_LOCAL;1138}1139if (target === ConfigurationTarget.USER_LOCAL) {1140return EditableConfigurationTarget.USER_LOCAL;1141}1142if (target === ConfigurationTarget.USER_REMOTE) {1143return EditableConfigurationTarget.USER_REMOTE;1144}1145if (target === ConfigurationTarget.WORKSPACE) {1146return EditableConfigurationTarget.WORKSPACE;1147}1148if (target === ConfigurationTarget.WORKSPACE_FOLDER) {1149return EditableConfigurationTarget.WORKSPACE_FOLDER;1150}1151return null;1152}1153}11541155class RegisterConfigurationSchemasContribution extends Disposable implements IWorkbenchContribution {1156constructor(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) {1163super();11641165extensionService.whenInstalledExtensionsRegistered().then(() => {1166this.registerConfigurationSchemas();11671168const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);1169const delayer = this._register(new Delayer<void>(50));1170this._register(Event.any(configurationRegistry.onDidUpdateConfiguration, configurationRegistry.onDidSchemaChange, workspaceTrustManagementService.onDidChangeTrust)(() =>1171delayer.trigger(() => this.registerConfigurationSchemas(), lifecycleService.phase === LifecyclePhase.Eventually ? undefined : 2500 /* delay longer in early phases */)));1172});1173}11741175private registerConfigurationSchemas(): void {1176const allSettingsSchema: IJSONSchema = {1177properties: allSettings.properties,1178patternProperties: allSettings.patternProperties,1179additionalProperties: true,1180allowTrailingCommas: true,1181allowComments: true1182};11831184const userSettingsSchema: IJSONSchema = this.environmentService.remoteAuthority ?1185{1186properties: Object.assign({},1187applicationSettings.properties,1188windowSettings.properties,1189resourceSettings.properties1190),1191patternProperties: allSettings.patternProperties,1192additionalProperties: true,1193allowTrailingCommas: true,1194allowComments: true1195}1196: allSettingsSchema;11971198const profileSettingsSchema: IJSONSchema = {1199properties: Object.assign({},1200machineSettings.properties,1201machineOverridableSettings.properties,1202windowSettings.properties,1203resourceSettings.properties1204),1205patternProperties: allSettings.patternProperties,1206additionalProperties: true,1207allowTrailingCommas: true,1208allowComments: true1209};12101211const machineSettingsSchema: IJSONSchema = {1212properties: Object.assign({},1213applicationMachineSettings.properties,1214machineSettings.properties,1215machineOverridableSettings.properties,1216windowSettings.properties,1217resourceSettings.properties1218),1219patternProperties: allSettings.patternProperties,1220additionalProperties: true,1221allowTrailingCommas: true,1222allowComments: true1223};12241225const workspaceSettingsSchema: IJSONSchema = {1226properties: Object.assign({},1227this.checkAndFilterPropertiesRequiringTrust(machineOverridableSettings.properties),1228this.checkAndFilterPropertiesRequiringTrust(windowSettings.properties),1229this.checkAndFilterPropertiesRequiringTrust(resourceSettings.properties)1230),1231patternProperties: allSettings.patternProperties,1232additionalProperties: true,1233allowTrailingCommas: true,1234allowComments: true1235};12361237const defaultSettingsSchema = {1238properties: Object.keys(allSettings.properties).reduce<IJSONSchemaMap>((result, key) => {1239result[key] = Object.assign({ deprecationMessage: undefined }, allSettings.properties[key]);1240return result;1241}, {}),1242patternProperties: Object.keys(allSettings.patternProperties).reduce<IJSONSchemaMap>((result, key) => {1243result[key] = Object.assign({ deprecationMessage: undefined }, allSettings.patternProperties[key]);1244return result;1245}, {}),1246additionalProperties: true,1247allowTrailingCommas: true,1248allowComments: true1249};12501251const folderSettingsSchema: IJSONSchema = WorkbenchState.WORKSPACE === this.workspaceContextService.getWorkbenchState() ?1252{1253properties: Object.assign({},1254this.checkAndFilterPropertiesRequiringTrust(machineOverridableSettings.properties),1255this.checkAndFilterPropertiesRequiringTrust(resourceSettings.properties)1256),1257patternProperties: allSettings.patternProperties,1258additionalProperties: true,1259allowTrailingCommas: true,1260allowComments: true1261} : workspaceSettingsSchema;12621263const configDefaultsSchema: IJSONSchema = {1264type: 'object',1265description: localize('configurationDefaults.description', 'Contribute defaults for configurations'),1266properties: Object.assign({},1267this.filterDefaultOverridableProperties(machineOverridableSettings.properties),1268this.filterDefaultOverridableProperties(windowSettings.properties),1269this.filterDefaultOverridableProperties(resourceSettings.properties)1270),1271patternProperties: {1272[OVERRIDE_PROPERTY_PATTERN]: {1273type: 'object',1274default: {},1275$ref: resourceLanguageSettingsSchemaId,1276}1277},1278additionalProperties: false1279};1280this.registerSchemas({1281defaultSettingsSchema,1282userSettingsSchema,1283profileSettingsSchema,1284machineSettingsSchema,1285workspaceSettingsSchema,1286folderSettingsSchema,1287configDefaultsSchema,1288});1289}12901291private registerSchemas(schemas: {1292defaultSettingsSchema: IJSONSchema;1293userSettingsSchema: IJSONSchema;1294profileSettingsSchema: IJSONSchema;1295machineSettingsSchema: IJSONSchema;1296workspaceSettingsSchema: IJSONSchema;1297folderSettingsSchema: IJSONSchema;1298configDefaultsSchema: IJSONSchema;1299}): void {1300const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);1301jsonRegistry.registerSchema(defaultSettingsSchemaId, schemas.defaultSettingsSchema);1302jsonRegistry.registerSchema(userSettingsSchemaId, schemas.userSettingsSchema);1303jsonRegistry.registerSchema(profileSettingsSchemaId, schemas.profileSettingsSchema);1304jsonRegistry.registerSchema(machineSettingsSchemaId, schemas.machineSettingsSchema);1305jsonRegistry.registerSchema(workspaceSettingsSchemaId, schemas.workspaceSettingsSchema);1306jsonRegistry.registerSchema(folderSettingsSchemaId, schemas.folderSettingsSchema);1307jsonRegistry.registerSchema(configurationDefaultsSchemaId, schemas.configDefaultsSchema);1308}13091310private checkAndFilterPropertiesRequiringTrust(properties: IStringDictionary<IConfigurationPropertySchema>): IStringDictionary<IConfigurationPropertySchema> {1311if (this.workspaceTrustManagementService.isWorkspaceTrusted()) {1312return properties;1313}13141315const result: IStringDictionary<IConfigurationPropertySchema> = {};1316Object.entries(properties).forEach(([key, value]) => {1317if (!value.restricted) {1318result[key] = value;1319}1320});1321return result;1322}13231324private filterDefaultOverridableProperties(properties: IStringDictionary<IConfigurationPropertySchema>): IStringDictionary<IConfigurationPropertySchema> {1325const result: IStringDictionary<IConfigurationPropertySchema> = {};1326Object.entries(properties).forEach(([key, value]) => {1327if (!value.disallowConfigurationDefault) {1328result[key] = value;1329}1330});1331return result;1332}1333}13341335class ConfigurationDefaultOverridesContribution extends Disposable implements IWorkbenchContribution {13361337static readonly ID = 'workbench.contrib.configurationDefaultOverridesContribution';13381339private readonly processedExperimentalSettings = new Set<string>();1340private readonly autoExperimentalSettings = new Set<string>();1341private readonly configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);1342private readonly throttler = this._register(new Throttler());13431344constructor(1345@IWorkbenchAssignmentService private readonly workbenchAssignmentService: IWorkbenchAssignmentService,1346@IExtensionService private readonly extensionService: IExtensionService,1347@IConfigurationService private readonly configurationService: WorkspaceService,1348@ILogService private readonly logService: ILogService1349) {1350super();13511352this.throttler.queue(() => this.updateDefaults());1353this._register(workbenchAssignmentService.onDidRefetchAssignments(() => this.throttler.queue(() => this.processExperimentalSettings(this.autoExperimentalSettings, true))));13541355// When configuration is updated make sure to apply experimental configuration overrides1356this._register(this.configurationRegistry.onDidUpdateConfiguration(({ properties }) => this.processExperimentalSettings(properties, false)));1357}13581359private async updateDefaults(): Promise<void> {1360this.logService.trace('ConfigurationService#updateDefaults: begin');1361try {1362// Check for experiments1363await this.processExperimentalSettings(Object.keys(this.configurationRegistry.getConfigurationProperties()), false);1364} finally {1365// Invalidate defaults cache after extensions have registered1366// and after the experiments have been resolved to prevent1367// resetting the overrides too early.1368await this.extensionService.whenInstalledExtensionsRegistered();1369this.logService.trace('ConfigurationService#updateDefaults: resetting the defaults');1370this.configurationService.reloadConfiguration(ConfigurationTarget.DEFAULT);1371}1372}13731374private async processExperimentalSettings(properties: Iterable<string>, autoRefetch: boolean): Promise<void> {1375const overrides: IStringDictionary<any> = {};1376const allProperties = this.configurationRegistry.getConfigurationProperties();1377for (const property of properties) {1378const schema = allProperties[property];1379if (!schema?.experiment) {1380continue;1381}1382if (!autoRefetch && this.processedExperimentalSettings.has(property)) {1383continue;1384}1385this.processedExperimentalSettings.add(property);1386if (schema.experiment.mode === 'auto') {1387this.autoExperimentalSettings.add(property);1388}1389try {1390const value = await this.workbenchAssignmentService.getTreatment(schema.experiment.name ?? `config.${property}`);1391if (!isUndefined(value) && !equals(value, schema.default)) {1392overrides[property] = value;1393}1394} catch (error) {/*ignore */ }1395}1396if (Object.keys(overrides).length) {1397this.configurationRegistry.registerDefaultConfigurations([{ overrides }]);1398}1399}1400}14011402const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);1403workbenchContributionsRegistry.registerWorkbenchContribution(RegisterConfigurationSchemasContribution, LifecyclePhase.Restored);1404registerWorkbenchContribution2(ConfigurationDefaultOverridesContribution.ID, ConfigurationDefaultOverridesContribution, WorkbenchPhase.BlockRestore);14051406const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);1407configurationRegistry.registerConfiguration({1408...workbenchConfigurationNodeBase,1409properties: {1410[APPLY_ALL_PROFILES_SETTING]: {1411'type': 'array',1412description: localize('setting description', "Configure settings to be applied for all profiles."),1413'default': [],1414'scope': ConfigurationScope.APPLICATION,1415additionalProperties: true,1416uniqueItems: true,1417}1418}1419});142014211422