Path: blob/main/src/vs/platform/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 * as arrays from '../../../base/common/arrays.js';6import { IStringDictionary } from '../../../base/common/collections.js';7import { Emitter, Event } from '../../../base/common/event.js';8import * as json from '../../../base/common/json.js';9import { Disposable } from '../../../base/common/lifecycle.js';10import { getOrSet, ResourceMap } from '../../../base/common/map.js';11import * as objects from '../../../base/common/objects.js';12import { IExtUri } from '../../../base/common/resources.js';13import * as types from '../../../base/common/types.js';14import { URI, UriComponents } from '../../../base/common/uri.js';15import { addToValueTree, ConfigurationTarget, getConfigurationValue, IConfigurationChange, IConfigurationChangeEvent, IConfigurationCompareResult, IConfigurationData, IConfigurationModel, IConfigurationOverrides, IConfigurationUpdateOverrides, IConfigurationValue, IInspectValue, IOverrides, removeFromValueTree, toValuesTree } from './configuration.js';16import { ConfigurationScope, Extensions, IConfigurationPropertySchema, IConfigurationRegistry, overrideIdentifiersFromKey, OVERRIDE_PROPERTY_REGEX, IRegisteredConfigurationPropertySchema } from './configurationRegistry.js';17import { FileOperation, IFileService } from '../../files/common/files.js';18import { ILogService } from '../../log/common/log.js';19import { Registry } from '../../registry/common/platform.js';20import { Workspace } from '../../workspace/common/workspace.js';2122function freeze<T>(data: T): T {23return Object.isFrozen(data) ? data : objects.deepFreeze(data);24}2526type InspectValue<V> = IInspectValue<V> & { merged?: V };2728export class ConfigurationModel implements IConfigurationModel {2930static createEmptyModel(logService: ILogService): ConfigurationModel {31return new ConfigurationModel({}, [], [], undefined, logService);32}3334private readonly overrideConfigurations = new Map<string, ConfigurationModel>();3536constructor(37private readonly _contents: any,38private readonly _keys: string[],39private readonly _overrides: IOverrides[],40readonly raw: IStringDictionary<any> | ReadonlyArray<IStringDictionary<any> | ConfigurationModel> | undefined,41private readonly logService: ILogService42) {43}4445private _rawConfiguration: ConfigurationModel | undefined;46get rawConfiguration(): ConfigurationModel {47if (!this._rawConfiguration) {48if (this.raw) {49const rawConfigurationModels = (Array.isArray(this.raw) ? this.raw : [this.raw]).map(raw => {50if (raw instanceof ConfigurationModel) {51return raw;52}53const parser = new ConfigurationModelParser('', this.logService);54parser.parseRaw(raw);55return parser.configurationModel;56});57this._rawConfiguration = rawConfigurationModels.reduce((previous, current) => current === previous ? current : previous.merge(current), rawConfigurationModels[0]);58} else {59// raw is same as current60this._rawConfiguration = this;61}62}63return this._rawConfiguration;64}6566get contents(): any {67return this._contents;68}6970get overrides(): IOverrides[] {71return this._overrides;72}7374get keys(): string[] {75return this._keys;76}7778isEmpty(): boolean {79return this._keys.length === 0 && Object.keys(this._contents).length === 0 && this._overrides.length === 0;80}8182getValue<V>(section: string | undefined): V {83return section ? getConfigurationValue<any>(this.contents, section) : this.contents;84}8586inspect<V>(section: string | undefined, overrideIdentifier?: string | null): InspectValue<V> {87const that = this;88return {89get value() {90return freeze(that.rawConfiguration.getValue<V>(section));91},92get override() {93return overrideIdentifier ? freeze(that.rawConfiguration.getOverrideValue<V>(section, overrideIdentifier)) : undefined;94},95get merged() {96return freeze(overrideIdentifier ? that.rawConfiguration.override(overrideIdentifier).getValue<V>(section) : that.rawConfiguration.getValue<V>(section));97},98get overrides() {99const overrides: { readonly identifiers: string[]; readonly value: V }[] = [];100for (const { contents, identifiers, keys } of that.rawConfiguration.overrides) {101const value = new ConfigurationModel(contents, keys, [], undefined, that.logService).getValue<V>(section);102if (value !== undefined) {103overrides.push({ identifiers, value });104}105}106return overrides.length ? freeze(overrides) : undefined;107}108};109}110111getOverrideValue<V>(section: string | undefined, overrideIdentifier: string): V | undefined {112const overrideContents = this.getContentsForOverrideIdentifer(overrideIdentifier);113return overrideContents114? section ? getConfigurationValue<any>(overrideContents, section) : overrideContents115: undefined;116}117118getKeysForOverrideIdentifier(identifier: string): string[] {119const keys: string[] = [];120for (const override of this.overrides) {121if (override.identifiers.includes(identifier)) {122keys.push(...override.keys);123}124}125return arrays.distinct(keys);126}127128getAllOverrideIdentifiers(): string[] {129const result: string[] = [];130for (const override of this.overrides) {131result.push(...override.identifiers);132}133return arrays.distinct(result);134}135136override(identifier: string): ConfigurationModel {137let overrideConfigurationModel = this.overrideConfigurations.get(identifier);138if (!overrideConfigurationModel) {139overrideConfigurationModel = this.createOverrideConfigurationModel(identifier);140this.overrideConfigurations.set(identifier, overrideConfigurationModel);141}142return overrideConfigurationModel;143}144145merge(...others: ConfigurationModel[]): ConfigurationModel {146const contents = objects.deepClone(this.contents);147const overrides = objects.deepClone(this.overrides);148const keys = [...this.keys];149const raws = this.raw ? Array.isArray(this.raw) ? [...this.raw] : [this.raw] : [this];150151for (const other of others) {152raws.push(...(other.raw ? Array.isArray(other.raw) ? other.raw : [other.raw] : [other]));153if (other.isEmpty()) {154continue;155}156this.mergeContents(contents, other.contents);157158for (const otherOverride of other.overrides) {159const [override] = overrides.filter(o => arrays.equals(o.identifiers, otherOverride.identifiers));160if (override) {161this.mergeContents(override.contents, otherOverride.contents);162override.keys.push(...otherOverride.keys);163override.keys = arrays.distinct(override.keys);164} else {165overrides.push(objects.deepClone(otherOverride));166}167}168for (const key of other.keys) {169if (keys.indexOf(key) === -1) {170keys.push(key);171}172}173}174return new ConfigurationModel(contents, keys, overrides, !raws.length || raws.every(raw => raw instanceof ConfigurationModel) ? undefined : raws, this.logService);175}176177private createOverrideConfigurationModel(identifier: string): ConfigurationModel {178const overrideContents = this.getContentsForOverrideIdentifer(identifier);179180if (!overrideContents || typeof overrideContents !== 'object' || !Object.keys(overrideContents).length) {181// If there are no valid overrides, return self182return this;183}184185const contents: any = {};186for (const key of arrays.distinct([...Object.keys(this.contents), ...Object.keys(overrideContents)])) {187188let contentsForKey = this.contents[key];189const overrideContentsForKey = overrideContents[key];190191// If there are override contents for the key, clone and merge otherwise use base contents192if (overrideContentsForKey) {193// Clone and merge only if base contents and override contents are of type object otherwise just override194if (typeof contentsForKey === 'object' && typeof overrideContentsForKey === 'object') {195contentsForKey = objects.deepClone(contentsForKey);196this.mergeContents(contentsForKey, overrideContentsForKey);197} else {198contentsForKey = overrideContentsForKey;199}200}201202contents[key] = contentsForKey;203}204205return new ConfigurationModel(contents, this.keys, this.overrides, undefined, this.logService);206}207208private mergeContents(source: any, target: any): void {209for (const key of Object.keys(target)) {210if (key in source) {211if (types.isObject(source[key]) && types.isObject(target[key])) {212this.mergeContents(source[key], target[key]);213continue;214}215}216source[key] = objects.deepClone(target[key]);217}218}219220private getContentsForOverrideIdentifer(identifier: string): any {221let contentsForIdentifierOnly: IStringDictionary<any> | null = null;222let contents: IStringDictionary<any> | null = null;223const mergeContents = (contentsToMerge: any) => {224if (contentsToMerge) {225if (contents) {226this.mergeContents(contents, contentsToMerge);227} else {228contents = objects.deepClone(contentsToMerge);229}230}231};232for (const override of this.overrides) {233if (override.identifiers.length === 1 && override.identifiers[0] === identifier) {234contentsForIdentifierOnly = override.contents;235} else if (override.identifiers.includes(identifier)) {236mergeContents(override.contents);237}238}239// Merge contents of the identifier only at the end to take precedence.240mergeContents(contentsForIdentifierOnly);241return contents;242}243244toJSON(): IConfigurationModel {245return {246contents: this.contents,247overrides: this.overrides,248keys: this.keys249};250}251252// Update methods253254public addValue(key: string, value: any): void {255this.updateValue(key, value, true);256}257258public setValue(key: string, value: any): void {259this.updateValue(key, value, false);260}261262public removeValue(key: string): void {263const index = this.keys.indexOf(key);264if (index === -1) {265return;266}267this.keys.splice(index, 1);268removeFromValueTree(this.contents, key);269if (OVERRIDE_PROPERTY_REGEX.test(key)) {270this.overrides.splice(this.overrides.findIndex(o => arrays.equals(o.identifiers, overrideIdentifiersFromKey(key))), 1);271}272}273274private updateValue(key: string, value: any, add: boolean): void {275addToValueTree(this.contents, key, value, e => this.logService.error(e));276add = add || this.keys.indexOf(key) === -1;277if (add) {278this.keys.push(key);279}280if (OVERRIDE_PROPERTY_REGEX.test(key)) {281const identifiers = overrideIdentifiersFromKey(key);282const override = {283identifiers,284keys: Object.keys(this.contents[key]),285contents: toValuesTree(this.contents[key], message => this.logService.error(message)),286};287const index = this.overrides.findIndex(o => arrays.equals(o.identifiers, identifiers));288if (index !== -1) {289this.overrides[index] = override;290} else {291this.overrides.push(override);292}293}294}295}296297export interface ConfigurationParseOptions {298skipUnregistered?: boolean;299scopes?: ConfigurationScope[];300skipRestricted?: boolean;301include?: string[];302exclude?: string[];303}304305export class ConfigurationModelParser {306307private _raw: any = null;308private _configurationModel: ConfigurationModel | null = null;309private _restrictedConfigurations: string[] = [];310private _parseErrors: any[] = [];311312constructor(313protected readonly _name: string,314protected readonly logService: ILogService315) { }316317get configurationModel(): ConfigurationModel {318return this._configurationModel || ConfigurationModel.createEmptyModel(this.logService);319}320321get restrictedConfigurations(): string[] {322return this._restrictedConfigurations;323}324325get errors(): any[] {326return this._parseErrors;327}328329public parse(content: string | null | undefined, options?: ConfigurationParseOptions): void {330if (!types.isUndefinedOrNull(content)) {331const raw = this.doParseContent(content);332this.parseRaw(raw, options);333}334}335336public reparse(options: ConfigurationParseOptions): void {337if (this._raw) {338this.parseRaw(this._raw, options);339}340}341342public parseRaw(raw: any, options?: ConfigurationParseOptions): void {343this._raw = raw;344const { contents, keys, overrides, restricted, hasExcludedProperties } = this.doParseRaw(raw, options);345this._configurationModel = new ConfigurationModel(contents, keys, overrides, hasExcludedProperties ? [raw] : undefined /* raw has not changed */, this.logService);346this._restrictedConfigurations = restricted || [];347}348349private doParseContent(content: string): any {350let raw: any = {};351let currentProperty: string | null = null;352let currentParent: any = [];353const previousParents: any[] = [];354const parseErrors: json.ParseError[] = [];355356function onValue(value: any) {357if (Array.isArray(currentParent)) {358(<any[]>currentParent).push(value);359} else if (currentProperty !== null) {360currentParent[currentProperty] = value;361}362}363364const visitor: json.JSONVisitor = {365onObjectBegin: () => {366const object = {};367onValue(object);368previousParents.push(currentParent);369currentParent = object;370currentProperty = null;371},372onObjectProperty: (name: string) => {373currentProperty = name;374},375onObjectEnd: () => {376currentParent = previousParents.pop();377},378onArrayBegin: () => {379const array: any[] = [];380onValue(array);381previousParents.push(currentParent);382currentParent = array;383currentProperty = null;384},385onArrayEnd: () => {386currentParent = previousParents.pop();387},388onLiteralValue: onValue,389onError: (error: json.ParseErrorCode, offset: number, length: number) => {390parseErrors.push({ error, offset, length });391}392};393if (content) {394try {395json.visit(content, visitor);396raw = currentParent[0] || {};397} catch (e) {398this.logService.error(`Error while parsing settings file ${this._name}: ${e}`);399this._parseErrors = [e];400}401}402403return raw;404}405406protected doParseRaw(raw: any, options?: ConfigurationParseOptions): IConfigurationModel & { restricted?: string[]; hasExcludedProperties?: boolean } {407const registry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);408const configurationProperties = registry.getConfigurationProperties();409const excludedConfigurationProperties = registry.getExcludedConfigurationProperties();410const filtered = this.filter(raw, configurationProperties, excludedConfigurationProperties, true, options);411raw = filtered.raw;412const contents = toValuesTree(raw, message => this.logService.error(`Conflict in settings file ${this._name}: ${message}`));413const keys = Object.keys(raw);414const overrides = this.toOverrides(raw, message => this.logService.error(`Conflict in settings file ${this._name}: ${message}`));415return { contents, keys, overrides, restricted: filtered.restricted, hasExcludedProperties: filtered.hasExcludedProperties };416}417418private filter(properties: any, configurationProperties: IStringDictionary<IRegisteredConfigurationPropertySchema>, excludedConfigurationProperties: IStringDictionary<IRegisteredConfigurationPropertySchema>, filterOverriddenProperties: boolean, options?: ConfigurationParseOptions): { raw: {}; restricted: string[]; hasExcludedProperties: boolean } {419let hasExcludedProperties = false;420if (!options?.scopes && !options?.skipRestricted && !options?.skipUnregistered && !options?.exclude?.length) {421return { raw: properties, restricted: [], hasExcludedProperties };422}423const raw: any = {};424const restricted: string[] = [];425for (const key in properties) {426if (OVERRIDE_PROPERTY_REGEX.test(key) && filterOverriddenProperties) {427const result = this.filter(properties[key], configurationProperties, excludedConfigurationProperties, false, options);428raw[key] = result.raw;429hasExcludedProperties = hasExcludedProperties || result.hasExcludedProperties;430restricted.push(...result.restricted);431} else {432const propertySchema = configurationProperties[key];433if (propertySchema?.restricted) {434restricted.push(key);435}436if (this.shouldInclude(key, propertySchema, excludedConfigurationProperties, options)) {437raw[key] = properties[key];438} else {439hasExcludedProperties = true;440}441}442}443return { raw, restricted, hasExcludedProperties };444}445446private shouldInclude(key: string, propertySchema: IConfigurationPropertySchema | undefined, excludedConfigurationProperties: IStringDictionary<IRegisteredConfigurationPropertySchema>, options: ConfigurationParseOptions): boolean {447if (options.exclude?.includes(key)) {448return false;449}450451if (options.include?.includes(key)) {452return true;453}454455if (options.skipRestricted && propertySchema?.restricted) {456return false;457}458459if (options.skipUnregistered && !propertySchema) {460return false;461}462463const schema = propertySchema ?? excludedConfigurationProperties[key];464const scope = schema ? typeof schema.scope !== 'undefined' ? schema.scope : ConfigurationScope.WINDOW : undefined;465if (scope === undefined || options.scopes === undefined) {466return true;467}468469return options.scopes.includes(scope);470}471472private toOverrides(raw: any, conflictReporter: (message: string) => void): IOverrides[] {473const overrides: IOverrides[] = [];474for (const key of Object.keys(raw)) {475if (OVERRIDE_PROPERTY_REGEX.test(key)) {476const overrideRaw: any = {};477for (const keyInOverrideRaw in raw[key]) {478overrideRaw[keyInOverrideRaw] = raw[key][keyInOverrideRaw];479}480overrides.push({481identifiers: overrideIdentifiersFromKey(key),482keys: Object.keys(overrideRaw),483contents: toValuesTree(overrideRaw, conflictReporter)484});485}486}487return overrides;488}489490}491492export class UserSettings extends Disposable {493494private readonly parser: ConfigurationModelParser;495protected readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());496readonly onDidChange: Event<void> = this._onDidChange.event;497498constructor(499private readonly userSettingsResource: URI,500protected parseOptions: ConfigurationParseOptions,501extUri: IExtUri,502private readonly fileService: IFileService,503private readonly logService: ILogService,504) {505super();506this.parser = new ConfigurationModelParser(this.userSettingsResource.toString(), logService);507this._register(this.fileService.watch(extUri.dirname(this.userSettingsResource)));508// Also listen to the resource incase the resource is a symlink - https://github.com/microsoft/vscode/issues/118134509this._register(this.fileService.watch(this.userSettingsResource));510this._register(Event.any(511Event.filter(this.fileService.onDidFilesChange, e => e.contains(this.userSettingsResource)),512Event.filter(this.fileService.onDidRunOperation, e => (e.isOperation(FileOperation.CREATE) || e.isOperation(FileOperation.COPY) || e.isOperation(FileOperation.DELETE) || e.isOperation(FileOperation.WRITE)) && extUri.isEqual(e.resource, userSettingsResource))513)(() => this._onDidChange.fire()));514}515516async loadConfiguration(): Promise<ConfigurationModel> {517try {518const content = await this.fileService.readFile(this.userSettingsResource);519this.parser.parse(content.value.toString() || '{}', this.parseOptions);520return this.parser.configurationModel;521} catch (e) {522return ConfigurationModel.createEmptyModel(this.logService);523}524}525526reparse(parseOptions?: ConfigurationParseOptions): ConfigurationModel {527if (parseOptions) {528this.parseOptions = parseOptions;529}530this.parser.reparse(this.parseOptions);531return this.parser.configurationModel;532}533534getRestrictedSettings(): string[] {535return this.parser.restrictedConfigurations;536}537}538539class ConfigurationInspectValue<V> implements IConfigurationValue<V> {540541constructor(542private readonly key: string,543private readonly overrides: IConfigurationOverrides,544private readonly _value: V | undefined,545readonly overrideIdentifiers: string[] | undefined,546private readonly defaultConfiguration: ConfigurationModel,547private readonly policyConfiguration: ConfigurationModel | undefined,548private readonly applicationConfiguration: ConfigurationModel | undefined,549private readonly userConfiguration: ConfigurationModel,550private readonly localUserConfiguration: ConfigurationModel,551private readonly remoteUserConfiguration: ConfigurationModel,552private readonly workspaceConfiguration: ConfigurationModel | undefined,553private readonly folderConfigurationModel: ConfigurationModel | undefined,554private readonly memoryConfigurationModel: ConfigurationModel555) {556}557558get value(): V | undefined {559return freeze(this._value);560}561562private toInspectValue(inspectValue: IInspectValue<V> | undefined | null): IInspectValue<V> | undefined {563return inspectValue?.value !== undefined || inspectValue?.override !== undefined || inspectValue?.overrides !== undefined ? inspectValue : undefined;564}565566private _defaultInspectValue: InspectValue<V> | undefined;567private get defaultInspectValue(): InspectValue<V> {568if (!this._defaultInspectValue) {569this._defaultInspectValue = this.defaultConfiguration.inspect<V>(this.key, this.overrides.overrideIdentifier);570}571return this._defaultInspectValue;572}573574get defaultValue(): V | undefined {575return this.defaultInspectValue.merged;576}577578get default(): IInspectValue<V> | undefined {579return this.toInspectValue(this.defaultInspectValue);580}581582private _policyInspectValue: InspectValue<V> | undefined | null;583private get policyInspectValue(): InspectValue<V> | null {584if (this._policyInspectValue === undefined) {585this._policyInspectValue = this.policyConfiguration ? this.policyConfiguration.inspect<V>(this.key) : null;586}587return this._policyInspectValue;588}589590get policyValue(): V | undefined {591return this.policyInspectValue?.merged;592}593594get policy(): IInspectValue<V> | undefined {595return this.policyInspectValue?.value !== undefined ? { value: this.policyInspectValue.value } : undefined;596}597598private _applicationInspectValue: InspectValue<V> | undefined | null;599private get applicationInspectValue(): InspectValue<V> | null {600if (this._applicationInspectValue === undefined) {601this._applicationInspectValue = this.applicationConfiguration ? this.applicationConfiguration.inspect<V>(this.key) : null;602}603return this._applicationInspectValue;604}605606get applicationValue(): V | undefined {607return this.applicationInspectValue?.merged;608}609610get application(): IInspectValue<V> | undefined {611return this.toInspectValue(this.applicationInspectValue);612}613614private _userInspectValue: InspectValue<V> | undefined;615private get userInspectValue(): InspectValue<V> {616if (!this._userInspectValue) {617this._userInspectValue = this.userConfiguration.inspect<V>(this.key, this.overrides.overrideIdentifier);618}619return this._userInspectValue;620}621622get userValue(): V | undefined {623return this.userInspectValue.merged;624}625626get user(): IInspectValue<V> | undefined {627return this.toInspectValue(this.userInspectValue);628}629630private _userLocalInspectValue: InspectValue<V> | undefined;631private get userLocalInspectValue(): InspectValue<V> {632if (!this._userLocalInspectValue) {633this._userLocalInspectValue = this.localUserConfiguration.inspect<V>(this.key, this.overrides.overrideIdentifier);634}635return this._userLocalInspectValue;636}637638get userLocalValue(): V | undefined {639return this.userLocalInspectValue.merged;640}641642get userLocal(): IInspectValue<V> | undefined {643return this.toInspectValue(this.userLocalInspectValue);644}645646private _userRemoteInspectValue: InspectValue<V> | undefined;647private get userRemoteInspectValue(): InspectValue<V> {648if (!this._userRemoteInspectValue) {649this._userRemoteInspectValue = this.remoteUserConfiguration.inspect<V>(this.key, this.overrides.overrideIdentifier);650}651return this._userRemoteInspectValue;652}653654get userRemoteValue(): V | undefined {655return this.userRemoteInspectValue.merged;656}657658get userRemote(): IInspectValue<V> | undefined {659return this.toInspectValue(this.userRemoteInspectValue);660}661662private _workspaceInspectValue: InspectValue<V> | undefined | null;663private get workspaceInspectValue(): InspectValue<V> | null {664if (this._workspaceInspectValue === undefined) {665this._workspaceInspectValue = this.workspaceConfiguration ? this.workspaceConfiguration.inspect<V>(this.key, this.overrides.overrideIdentifier) : null;666}667return this._workspaceInspectValue;668}669670get workspaceValue(): V | undefined {671return this.workspaceInspectValue?.merged;672}673674get workspace(): IInspectValue<V> | undefined {675return this.toInspectValue(this.workspaceInspectValue);676}677678private _workspaceFolderInspectValue: InspectValue<V> | undefined | null;679private get workspaceFolderInspectValue(): InspectValue<V> | null {680if (this._workspaceFolderInspectValue === undefined) {681this._workspaceFolderInspectValue = this.folderConfigurationModel ? this.folderConfigurationModel.inspect<V>(this.key, this.overrides.overrideIdentifier) : null;682}683return this._workspaceFolderInspectValue;684}685686get workspaceFolderValue(): V | undefined {687return this.workspaceFolderInspectValue?.merged;688}689690get workspaceFolder(): IInspectValue<V> | undefined {691return this.toInspectValue(this.workspaceFolderInspectValue);692}693694private _memoryInspectValue: InspectValue<V> | undefined;695private get memoryInspectValue(): InspectValue<V> {696if (this._memoryInspectValue === undefined) {697this._memoryInspectValue = this.memoryConfigurationModel.inspect<V>(this.key, this.overrides.overrideIdentifier);698}699return this._memoryInspectValue;700}701702get memoryValue(): V | undefined {703return this.memoryInspectValue.merged;704}705706get memory(): IInspectValue<V> | undefined {707return this.toInspectValue(this.memoryInspectValue);708}709710}711712export class Configuration {713714private _workspaceConsolidatedConfiguration: ConfigurationModel | null = null;715private _foldersConsolidatedConfigurations = new ResourceMap<ConfigurationModel>();716717constructor(718private _defaultConfiguration: ConfigurationModel,719private _policyConfiguration: ConfigurationModel,720private _applicationConfiguration: ConfigurationModel,721private _localUserConfiguration: ConfigurationModel,722private _remoteUserConfiguration: ConfigurationModel,723private _workspaceConfiguration: ConfigurationModel,724private _folderConfigurations: ResourceMap<ConfigurationModel>,725private _memoryConfiguration: ConfigurationModel,726private _memoryConfigurationByResource: ResourceMap<ConfigurationModel>,727private readonly logService: ILogService728) {729}730731getValue(section: string | undefined, overrides: IConfigurationOverrides, workspace: Workspace | undefined): any {732const consolidateConfigurationModel = this.getConsolidatedConfigurationModel(section, overrides, workspace);733return consolidateConfigurationModel.getValue(section);734}735736updateValue(key: string, value: any, overrides: IConfigurationUpdateOverrides = {}): void {737let memoryConfiguration: ConfigurationModel | undefined;738if (overrides.resource) {739memoryConfiguration = this._memoryConfigurationByResource.get(overrides.resource);740if (!memoryConfiguration) {741memoryConfiguration = ConfigurationModel.createEmptyModel(this.logService);742this._memoryConfigurationByResource.set(overrides.resource, memoryConfiguration);743}744} else {745memoryConfiguration = this._memoryConfiguration;746}747748if (value === undefined) {749memoryConfiguration.removeValue(key);750} else {751memoryConfiguration.setValue(key, value);752}753754if (!overrides.resource) {755this._workspaceConsolidatedConfiguration = null;756}757}758759inspect<C>(key: string, overrides: IConfigurationOverrides, workspace: Workspace | undefined): IConfigurationValue<C> {760const consolidateConfigurationModel = this.getConsolidatedConfigurationModel(key, overrides, workspace);761const folderConfigurationModel = this.getFolderConfigurationModelForResource(overrides.resource, workspace);762const memoryConfigurationModel = overrides.resource ? this._memoryConfigurationByResource.get(overrides.resource) || this._memoryConfiguration : this._memoryConfiguration;763const overrideIdentifiers = new Set<string>();764for (const override of consolidateConfigurationModel.overrides) {765for (const overrideIdentifier of override.identifiers) {766if (consolidateConfigurationModel.getOverrideValue(key, overrideIdentifier) !== undefined) {767overrideIdentifiers.add(overrideIdentifier);768}769}770}771772return new ConfigurationInspectValue<C>(773key,774overrides,775consolidateConfigurationModel.getValue<C>(key),776overrideIdentifiers.size ? [...overrideIdentifiers] : undefined,777this._defaultConfiguration,778this._policyConfiguration.isEmpty() ? undefined : this._policyConfiguration,779this.applicationConfiguration.isEmpty() ? undefined : this.applicationConfiguration,780this.userConfiguration,781this.localUserConfiguration,782this.remoteUserConfiguration,783workspace ? this._workspaceConfiguration : undefined,784folderConfigurationModel ? folderConfigurationModel : undefined,785memoryConfigurationModel786);787788}789790keys(workspace: Workspace | undefined): {791default: string[];792user: string[];793workspace: string[];794workspaceFolder: string[];795} {796const folderConfigurationModel = this.getFolderConfigurationModelForResource(undefined, workspace);797return {798default: this._defaultConfiguration.keys.slice(0),799user: this.userConfiguration.keys.slice(0),800workspace: this._workspaceConfiguration.keys.slice(0),801workspaceFolder: folderConfigurationModel ? folderConfigurationModel.keys.slice(0) : []802};803}804805updateDefaultConfiguration(defaultConfiguration: ConfigurationModel): void {806this._defaultConfiguration = defaultConfiguration;807this._workspaceConsolidatedConfiguration = null;808this._foldersConsolidatedConfigurations.clear();809}810811updatePolicyConfiguration(policyConfiguration: ConfigurationModel): void {812this._policyConfiguration = policyConfiguration;813}814815updateApplicationConfiguration(applicationConfiguration: ConfigurationModel): void {816this._applicationConfiguration = applicationConfiguration;817this._workspaceConsolidatedConfiguration = null;818this._foldersConsolidatedConfigurations.clear();819}820821updateLocalUserConfiguration(localUserConfiguration: ConfigurationModel): void {822this._localUserConfiguration = localUserConfiguration;823this._userConfiguration = null;824this._workspaceConsolidatedConfiguration = null;825this._foldersConsolidatedConfigurations.clear();826}827828updateRemoteUserConfiguration(remoteUserConfiguration: ConfigurationModel): void {829this._remoteUserConfiguration = remoteUserConfiguration;830this._userConfiguration = null;831this._workspaceConsolidatedConfiguration = null;832this._foldersConsolidatedConfigurations.clear();833}834835updateWorkspaceConfiguration(workspaceConfiguration: ConfigurationModel): void {836this._workspaceConfiguration = workspaceConfiguration;837this._workspaceConsolidatedConfiguration = null;838this._foldersConsolidatedConfigurations.clear();839}840841updateFolderConfiguration(resource: URI, configuration: ConfigurationModel): void {842this._folderConfigurations.set(resource, configuration);843this._foldersConsolidatedConfigurations.delete(resource);844}845846deleteFolderConfiguration(resource: URI): void {847this.folderConfigurations.delete(resource);848this._foldersConsolidatedConfigurations.delete(resource);849}850851compareAndUpdateDefaultConfiguration(defaults: ConfigurationModel, keys?: string[]): IConfigurationChange {852const overrides: [string, string[]][] = [];853if (!keys) {854const { added, updated, removed } = compare(this._defaultConfiguration, defaults);855keys = [...added, ...updated, ...removed];856}857for (const key of keys) {858for (const overrideIdentifier of overrideIdentifiersFromKey(key)) {859const fromKeys = this._defaultConfiguration.getKeysForOverrideIdentifier(overrideIdentifier);860const toKeys = defaults.getKeysForOverrideIdentifier(overrideIdentifier);861const keys = [862...toKeys.filter(key => fromKeys.indexOf(key) === -1),863...fromKeys.filter(key => toKeys.indexOf(key) === -1),864...fromKeys.filter(key => !objects.equals(this._defaultConfiguration.override(overrideIdentifier).getValue(key), defaults.override(overrideIdentifier).getValue(key)))865];866overrides.push([overrideIdentifier, keys]);867}868}869this.updateDefaultConfiguration(defaults);870return { keys, overrides };871}872873compareAndUpdatePolicyConfiguration(policyConfiguration: ConfigurationModel): IConfigurationChange {874const { added, updated, removed } = compare(this._policyConfiguration, policyConfiguration);875const keys = [...added, ...updated, ...removed];876if (keys.length) {877this.updatePolicyConfiguration(policyConfiguration);878}879return { keys, overrides: [] };880}881882compareAndUpdateApplicationConfiguration(application: ConfigurationModel): IConfigurationChange {883const { added, updated, removed, overrides } = compare(this.applicationConfiguration, application);884const keys = [...added, ...updated, ...removed];885if (keys.length) {886this.updateApplicationConfiguration(application);887}888return { keys, overrides };889}890891compareAndUpdateLocalUserConfiguration(user: ConfigurationModel): IConfigurationChange {892const { added, updated, removed, overrides } = compare(this.localUserConfiguration, user);893const keys = [...added, ...updated, ...removed];894if (keys.length) {895this.updateLocalUserConfiguration(user);896}897return { keys, overrides };898}899900compareAndUpdateRemoteUserConfiguration(user: ConfigurationModel): IConfigurationChange {901const { added, updated, removed, overrides } = compare(this.remoteUserConfiguration, user);902const keys = [...added, ...updated, ...removed];903if (keys.length) {904this.updateRemoteUserConfiguration(user);905}906return { keys, overrides };907}908909compareAndUpdateWorkspaceConfiguration(workspaceConfiguration: ConfigurationModel): IConfigurationChange {910const { added, updated, removed, overrides } = compare(this.workspaceConfiguration, workspaceConfiguration);911const keys = [...added, ...updated, ...removed];912if (keys.length) {913this.updateWorkspaceConfiguration(workspaceConfiguration);914}915return { keys, overrides };916}917918compareAndUpdateFolderConfiguration(resource: URI, folderConfiguration: ConfigurationModel): IConfigurationChange {919const currentFolderConfiguration = this.folderConfigurations.get(resource);920const { added, updated, removed, overrides } = compare(currentFolderConfiguration, folderConfiguration);921const keys = [...added, ...updated, ...removed];922if (keys.length || !currentFolderConfiguration) {923this.updateFolderConfiguration(resource, folderConfiguration);924}925return { keys, overrides };926}927928compareAndDeleteFolderConfiguration(folder: URI): IConfigurationChange {929const folderConfig = this.folderConfigurations.get(folder);930if (!folderConfig) {931throw new Error('Unknown folder');932}933this.deleteFolderConfiguration(folder);934const { added, updated, removed, overrides } = compare(folderConfig, undefined);935return { keys: [...added, ...updated, ...removed], overrides };936}937938get defaults(): ConfigurationModel {939return this._defaultConfiguration;940}941942get applicationConfiguration(): ConfigurationModel {943return this._applicationConfiguration;944}945946private _userConfiguration: ConfigurationModel | null = null;947get userConfiguration(): ConfigurationModel {948if (!this._userConfiguration) {949if (this._remoteUserConfiguration.isEmpty()) {950this._userConfiguration = this._localUserConfiguration;951} else {952const merged = this._localUserConfiguration.merge(this._remoteUserConfiguration);953this._userConfiguration = new ConfigurationModel(merged.contents, merged.keys, merged.overrides, undefined, this.logService);954}955}956return this._userConfiguration;957}958959get localUserConfiguration(): ConfigurationModel {960return this._localUserConfiguration;961}962963get remoteUserConfiguration(): ConfigurationModel {964return this._remoteUserConfiguration;965}966967get workspaceConfiguration(): ConfigurationModel {968return this._workspaceConfiguration;969}970971get folderConfigurations(): ResourceMap<ConfigurationModel> {972return this._folderConfigurations;973}974975private getConsolidatedConfigurationModel(section: string | undefined, overrides: IConfigurationOverrides, workspace: Workspace | undefined): ConfigurationModel {976let configurationModel = this.getConsolidatedConfigurationModelForResource(overrides, workspace);977if (overrides.overrideIdentifier) {978configurationModel = configurationModel.override(overrides.overrideIdentifier);979}980if (!this._policyConfiguration.isEmpty() && this._policyConfiguration.getValue(section) !== undefined) {981// clone by merging982configurationModel = configurationModel.merge();983for (const key of this._policyConfiguration.keys) {984configurationModel.setValue(key, this._policyConfiguration.getValue(key));985}986}987return configurationModel;988}989990private getConsolidatedConfigurationModelForResource({ resource }: IConfigurationOverrides, workspace: Workspace | undefined): ConfigurationModel {991let consolidateConfiguration = this.getWorkspaceConsolidatedConfiguration();992993if (workspace && resource) {994const root = workspace.getFolder(resource);995if (root) {996consolidateConfiguration = this.getFolderConsolidatedConfiguration(root.uri) || consolidateConfiguration;997}998const memoryConfigurationForResource = this._memoryConfigurationByResource.get(resource);999if (memoryConfigurationForResource) {1000consolidateConfiguration = consolidateConfiguration.merge(memoryConfigurationForResource);1001}1002}10031004return consolidateConfiguration;1005}10061007private getWorkspaceConsolidatedConfiguration(): ConfigurationModel {1008if (!this._workspaceConsolidatedConfiguration) {1009this._workspaceConsolidatedConfiguration = this._defaultConfiguration.merge(this.applicationConfiguration, this.userConfiguration, this._workspaceConfiguration, this._memoryConfiguration);1010}1011return this._workspaceConsolidatedConfiguration;1012}10131014private getFolderConsolidatedConfiguration(folder: URI): ConfigurationModel {1015let folderConsolidatedConfiguration = this._foldersConsolidatedConfigurations.get(folder);1016if (!folderConsolidatedConfiguration) {1017const workspaceConsolidateConfiguration = this.getWorkspaceConsolidatedConfiguration();1018const folderConfiguration = this._folderConfigurations.get(folder);1019if (folderConfiguration) {1020folderConsolidatedConfiguration = workspaceConsolidateConfiguration.merge(folderConfiguration);1021this._foldersConsolidatedConfigurations.set(folder, folderConsolidatedConfiguration);1022} else {1023folderConsolidatedConfiguration = workspaceConsolidateConfiguration;1024}1025}1026return folderConsolidatedConfiguration;1027}10281029private getFolderConfigurationModelForResource(resource: URI | null | undefined, workspace: Workspace | undefined): ConfigurationModel | undefined {1030if (workspace && resource) {1031const root = workspace.getFolder(resource);1032if (root) {1033return this._folderConfigurations.get(root.uri);1034}1035}1036return undefined;1037}10381039toData(): IConfigurationData {1040return {1041defaults: {1042contents: this._defaultConfiguration.contents,1043overrides: this._defaultConfiguration.overrides,1044keys: this._defaultConfiguration.keys,1045},1046policy: {1047contents: this._policyConfiguration.contents,1048overrides: this._policyConfiguration.overrides,1049keys: this._policyConfiguration.keys1050},1051application: {1052contents: this.applicationConfiguration.contents,1053overrides: this.applicationConfiguration.overrides,1054keys: this.applicationConfiguration.keys,1055raw: Array.isArray(this.applicationConfiguration.raw) ? undefined : this.applicationConfiguration.raw1056},1057userLocal: {1058contents: this.localUserConfiguration.contents,1059overrides: this.localUserConfiguration.overrides,1060keys: this.localUserConfiguration.keys,1061raw: Array.isArray(this.localUserConfiguration.raw) ? undefined : this.localUserConfiguration.raw1062},1063userRemote: {1064contents: this.remoteUserConfiguration.contents,1065overrides: this.remoteUserConfiguration.overrides,1066keys: this.remoteUserConfiguration.keys,1067raw: Array.isArray(this.remoteUserConfiguration.raw) ? undefined : this.remoteUserConfiguration.raw1068},1069workspace: {1070contents: this._workspaceConfiguration.contents,1071overrides: this._workspaceConfiguration.overrides,1072keys: this._workspaceConfiguration.keys1073},1074folders: [...this._folderConfigurations.keys()].reduce<[UriComponents, IConfigurationModel][]>((result, folder) => {1075const { contents, overrides, keys } = this._folderConfigurations.get(folder)!;1076result.push([folder, { contents, overrides, keys }]);1077return result;1078}, [])1079};1080}10811082allKeys(): string[] {1083const keys: Set<string> = new Set<string>();1084this._defaultConfiguration.keys.forEach(key => keys.add(key));1085this.userConfiguration.keys.forEach(key => keys.add(key));1086this._workspaceConfiguration.keys.forEach(key => keys.add(key));1087this._folderConfigurations.forEach(folderConfiguration => folderConfiguration.keys.forEach(key => keys.add(key)));1088return [...keys.values()];1089}10901091protected allOverrideIdentifiers(): string[] {1092const keys: Set<string> = new Set<string>();1093this._defaultConfiguration.getAllOverrideIdentifiers().forEach(key => keys.add(key));1094this.userConfiguration.getAllOverrideIdentifiers().forEach(key => keys.add(key));1095this._workspaceConfiguration.getAllOverrideIdentifiers().forEach(key => keys.add(key));1096this._folderConfigurations.forEach(folderConfiguration => folderConfiguration.getAllOverrideIdentifiers().forEach(key => keys.add(key)));1097return [...keys.values()];1098}10991100protected getAllKeysForOverrideIdentifier(overrideIdentifier: string): string[] {1101const keys: Set<string> = new Set<string>();1102this._defaultConfiguration.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key));1103this.userConfiguration.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key));1104this._workspaceConfiguration.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key));1105this._folderConfigurations.forEach(folderConfiguration => folderConfiguration.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key)));1106return [...keys.values()];1107}11081109static parse(data: IConfigurationData, logService: ILogService): Configuration {1110const defaultConfiguration = this.parseConfigurationModel(data.defaults, logService);1111const policyConfiguration = this.parseConfigurationModel(data.policy, logService);1112const applicationConfiguration = this.parseConfigurationModel(data.application, logService);1113const userLocalConfiguration = this.parseConfigurationModel(data.userLocal, logService);1114const userRemoteConfiguration = this.parseConfigurationModel(data.userRemote, logService);1115const workspaceConfiguration = this.parseConfigurationModel(data.workspace, logService);1116const folders: ResourceMap<ConfigurationModel> = data.folders.reduce((result, value) => {1117result.set(URI.revive(value[0]), this.parseConfigurationModel(value[1], logService));1118return result;1119}, new ResourceMap<ConfigurationModel>());1120return new Configuration(1121defaultConfiguration,1122policyConfiguration,1123applicationConfiguration,1124userLocalConfiguration,1125userRemoteConfiguration,1126workspaceConfiguration,1127folders,1128ConfigurationModel.createEmptyModel(logService),1129new ResourceMap<ConfigurationModel>(),1130logService1131);1132}11331134private static parseConfigurationModel(model: IConfigurationModel, logService: ILogService): ConfigurationModel {1135return new ConfigurationModel(model.contents, model.keys, model.overrides, model.raw, logService);1136}11371138}11391140export function mergeChanges(...changes: IConfigurationChange[]): IConfigurationChange {1141if (changes.length === 0) {1142return { keys: [], overrides: [] };1143}1144if (changes.length === 1) {1145return changes[0];1146}1147const keysSet = new Set<string>();1148const overridesMap = new Map<string, Set<string>>();1149for (const change of changes) {1150change.keys.forEach(key => keysSet.add(key));1151change.overrides.forEach(([identifier, keys]) => {1152const result = getOrSet(overridesMap, identifier, new Set<string>());1153keys.forEach(key => result.add(key));1154});1155}1156const overrides: [string, string[]][] = [];1157overridesMap.forEach((keys, identifier) => overrides.push([identifier, [...keys.values()]]));1158return { keys: [...keysSet.values()], overrides };1159}11601161export class ConfigurationChangeEvent implements IConfigurationChangeEvent {11621163private readonly _marker = '\n';1164private readonly _markerCode1 = this._marker.charCodeAt(0);1165private readonly _markerCode2 = '.'.charCodeAt(0);1166private readonly _affectsConfigStr: string;11671168readonly affectedKeys = new Set<string>();1169source!: ConfigurationTarget;11701171constructor(1172readonly change: IConfigurationChange,1173private readonly previous: { workspace?: Workspace; data: IConfigurationData } | undefined,1174private readonly currentConfiguraiton: Configuration,1175private readonly currentWorkspace: Workspace | undefined,1176private readonly logService: ILogService1177) {1178for (const key of change.keys) {1179this.affectedKeys.add(key);1180}1181for (const [, keys] of change.overrides) {1182for (const key of keys) {1183this.affectedKeys.add(key);1184}1185}11861187// Example: '\nfoo.bar\nabc.def\n'1188this._affectsConfigStr = this._marker;1189for (const key of this.affectedKeys) {1190this._affectsConfigStr += key + this._marker;1191}1192}11931194private _previousConfiguration: Configuration | undefined = undefined;1195get previousConfiguration(): Configuration | undefined {1196if (!this._previousConfiguration && this.previous) {1197this._previousConfiguration = Configuration.parse(this.previous.data, this.logService);1198}1199return this._previousConfiguration;1200}12011202affectsConfiguration(section: string, overrides?: IConfigurationOverrides): boolean {1203// we have one large string with all keys that have changed. we pad (marker) the section1204// and check that either find it padded or before a segment character1205const needle = this._marker + section;1206const idx = this._affectsConfigStr.indexOf(needle);1207if (idx < 0) {1208// NOT: (marker + section)1209return false;1210}1211const pos = idx + needle.length;1212if (pos >= this._affectsConfigStr.length) {1213return false;1214}1215const code = this._affectsConfigStr.charCodeAt(pos);1216if (code !== this._markerCode1 && code !== this._markerCode2) {1217// NOT: section + (marker | segment)1218return false;1219}1220if (overrides) {1221const value1 = this.previousConfiguration ? this.previousConfiguration.getValue(section, overrides, this.previous?.workspace) : undefined;1222const value2 = this.currentConfiguraiton.getValue(section, overrides, this.currentWorkspace);1223return !objects.equals(value1, value2);1224}1225return true;1226}1227}12281229function compare(from: ConfigurationModel | undefined, to: ConfigurationModel | undefined): IConfigurationCompareResult {1230const { added, removed, updated } = compareConfigurationContents(to?.rawConfiguration, from?.rawConfiguration);1231const overrides: [string, string[]][] = [];12321233const fromOverrideIdentifiers = from?.getAllOverrideIdentifiers() || [];1234const toOverrideIdentifiers = to?.getAllOverrideIdentifiers() || [];12351236if (to) {1237const addedOverrideIdentifiers = toOverrideIdentifiers.filter(key => !fromOverrideIdentifiers.includes(key));1238for (const identifier of addedOverrideIdentifiers) {1239overrides.push([identifier, to.getKeysForOverrideIdentifier(identifier)]);1240}1241}12421243if (from) {1244const removedOverrideIdentifiers = fromOverrideIdentifiers.filter(key => !toOverrideIdentifiers.includes(key));1245for (const identifier of removedOverrideIdentifiers) {1246overrides.push([identifier, from.getKeysForOverrideIdentifier(identifier)]);1247}1248}12491250if (to && from) {1251for (const identifier of fromOverrideIdentifiers) {1252if (toOverrideIdentifiers.includes(identifier)) {1253const result = compareConfigurationContents({ contents: from.getOverrideValue(undefined, identifier) || {}, keys: from.getKeysForOverrideIdentifier(identifier) }, { contents: to.getOverrideValue(undefined, identifier) || {}, keys: to.getKeysForOverrideIdentifier(identifier) });1254overrides.push([identifier, [...result.added, ...result.removed, ...result.updated]]);1255}1256}1257}12581259return { added, removed, updated, overrides };1260}12611262function compareConfigurationContents(to: { keys: string[]; contents: any } | undefined, from: { keys: string[]; contents: any } | undefined) {1263const added = to1264? from ? to.keys.filter(key => from.keys.indexOf(key) === -1) : [...to.keys]1265: [];1266const removed = from1267? to ? from.keys.filter(key => to.keys.indexOf(key) === -1) : [...from.keys]1268: [];1269const updated: string[] = [];12701271if (to && from) {1272for (const key of from.keys) {1273if (to.keys.indexOf(key) !== -1) {1274const value1 = getConfigurationValue(from.contents, key);1275const value2 = getConfigurationValue(to.contents, key);1276if (!objects.equals(value1, value2)) {1277updated.push(key);1278}1279}1280}1281}1282return { added, removed, updated };1283}128412851286