Path: blob/main/src/vs/workbench/services/configuration/common/configurationModels.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 { equals } from '../../../../base/common/objects.js';6import { toValuesTree, IConfigurationModel, IConfigurationOverrides, IConfigurationValue, IConfigurationChange } from '../../../../platform/configuration/common/configuration.js';7import { Configuration as BaseConfiguration, ConfigurationModelParser, ConfigurationModel, ConfigurationParseOptions } from '../../../../platform/configuration/common/configurationModels.js';8import { IStoredWorkspaceFolder } from '../../../../platform/workspaces/common/workspaces.js';9import { Workspace } from '../../../../platform/workspace/common/workspace.js';10import { ResourceMap } from '../../../../base/common/map.js';11import { URI } from '../../../../base/common/uri.js';12import { isBoolean } from '../../../../base/common/types.js';13import { distinct } from '../../../../base/common/arrays.js';14import { ILogService } from '../../../../platform/log/common/log.js';1516export class WorkspaceConfigurationModelParser extends ConfigurationModelParser {1718private _folders: IStoredWorkspaceFolder[] = [];19private _transient: boolean = false;20private _settingsModelParser: ConfigurationModelParser;21private _launchModel: ConfigurationModel;22private _tasksModel: ConfigurationModel;2324constructor(name: string, logService: ILogService) {25super(name, logService);26this._settingsModelParser = new ConfigurationModelParser(name, logService);27this._launchModel = ConfigurationModel.createEmptyModel(logService);28this._tasksModel = ConfigurationModel.createEmptyModel(logService);29}3031get folders(): IStoredWorkspaceFolder[] {32return this._folders;33}3435get transient(): boolean {36return this._transient;37}3839get settingsModel(): ConfigurationModel {40return this._settingsModelParser.configurationModel;41}4243get launchModel(): ConfigurationModel {44return this._launchModel;45}4647get tasksModel(): ConfigurationModel {48return this._tasksModel;49}5051reparseWorkspaceSettings(configurationParseOptions: ConfigurationParseOptions): void {52this._settingsModelParser.reparse(configurationParseOptions);53}5455getRestrictedWorkspaceSettings(): string[] {56return this._settingsModelParser.restrictedConfigurations;57}5859protected override doParseRaw(raw: any, configurationParseOptions?: ConfigurationParseOptions): IConfigurationModel {60this._folders = (raw['folders'] || []) as IStoredWorkspaceFolder[];61this._transient = isBoolean(raw['transient']) && raw['transient'];62this._settingsModelParser.parseRaw(raw['settings'], configurationParseOptions);63this._launchModel = this.createConfigurationModelFrom(raw, 'launch');64this._tasksModel = this.createConfigurationModelFrom(raw, 'tasks');65return super.doParseRaw(raw, configurationParseOptions);66}6768private createConfigurationModelFrom(raw: any, key: string): ConfigurationModel {69const data = raw[key];70if (data) {71const contents = toValuesTree(data, message => console.error(`Conflict in settings file ${this._name}: ${message}`));72const scopedContents = Object.create(null);73scopedContents[key] = contents;74const keys = Object.keys(data).map(k => `${key}.${k}`);75return new ConfigurationModel(scopedContents, keys, [], undefined, this.logService);76}77return ConfigurationModel.createEmptyModel(this.logService);78}79}8081export class StandaloneConfigurationModelParser extends ConfigurationModelParser {8283constructor(name: string, private readonly scope: string, logService: ILogService,) {84super(name, logService);85}8687protected override doParseRaw(raw: any, configurationParseOptions?: ConfigurationParseOptions): IConfigurationModel {88const contents = toValuesTree(raw, message => console.error(`Conflict in settings file ${this._name}: ${message}`));89const scopedContents = Object.create(null);90scopedContents[this.scope] = contents;91const keys = Object.keys(raw).map(key => `${this.scope}.${key}`);92return { contents: scopedContents, keys, overrides: [] };93}9495}9697export class Configuration extends BaseConfiguration {9899constructor(100defaults: ConfigurationModel,101policy: ConfigurationModel,102application: ConfigurationModel,103localUser: ConfigurationModel,104remoteUser: ConfigurationModel,105workspaceConfiguration: ConfigurationModel,106folders: ResourceMap<ConfigurationModel>,107memoryConfiguration: ConfigurationModel,108memoryConfigurationByResource: ResourceMap<ConfigurationModel>,109private readonly _workspace: Workspace | undefined,110logService: ILogService111) {112super(defaults, policy, application, localUser, remoteUser, workspaceConfiguration, folders, memoryConfiguration, memoryConfigurationByResource, logService);113}114115override getValue(key: string | undefined, overrides: IConfigurationOverrides = {}): any {116return super.getValue(key, overrides, this._workspace);117}118119override inspect<C>(key: string, overrides: IConfigurationOverrides = {}): IConfigurationValue<C> {120return super.inspect(key, overrides, this._workspace);121}122123override keys(): {124default: string[];125user: string[];126workspace: string[];127workspaceFolder: string[];128} {129return super.keys(this._workspace);130}131132override compareAndDeleteFolderConfiguration(folder: URI): IConfigurationChange {133if (this._workspace && this._workspace.folders.length > 0 && this._workspace.folders[0].uri.toString() === folder.toString()) {134// Do not remove workspace configuration135return { keys: [], overrides: [] };136}137return super.compareAndDeleteFolderConfiguration(folder);138}139140compare(other: Configuration): IConfigurationChange {141const compare = (fromKeys: string[], toKeys: string[], overrideIdentifier?: string): string[] => {142const keys: string[] = [];143keys.push(...toKeys.filter(key => fromKeys.indexOf(key) === -1));144keys.push(...fromKeys.filter(key => toKeys.indexOf(key) === -1));145keys.push(...fromKeys.filter(key => {146// Ignore if the key does not exist in both models147if (toKeys.indexOf(key) === -1) {148return false;149}150// Compare workspace value151if (!equals(this.getValue(key, { overrideIdentifier }), other.getValue(key, { overrideIdentifier }))) {152return true;153}154// Compare workspace folder value155return this._workspace && this._workspace.folders.some(folder => !equals(this.getValue(key, { resource: folder.uri, overrideIdentifier }), other.getValue(key, { resource: folder.uri, overrideIdentifier })));156}));157return keys;158};159const keys = compare(this.allKeys(), other.allKeys());160const overrides: [string, string[]][] = [];161const allOverrideIdentifiers = distinct([...this.allOverrideIdentifiers(), ...other.allOverrideIdentifiers()]);162for (const overrideIdentifier of allOverrideIdentifiers) {163const keys = compare(this.getAllKeysForOverrideIdentifier(overrideIdentifier), other.getAllKeysForOverrideIdentifier(overrideIdentifier), overrideIdentifier);164if (keys.length) {165overrides.push([overrideIdentifier, keys]);166}167}168return { keys, overrides };169}170171}172173174