/* tslint:disable */ import * as objects from '@vscode-alt/monaco-editor/esm/vs/base/common/objects'; import * as arrays from '@vscode-alt/monaco-editor/esm/vs/base/common/arrays'; import * as types from '@vscode-alt/monaco-editor/esm/vs/base/common/types'; import { IConfigurationModel, IOverrides, } from '../../../interfaces'; import { removeFromValueTree, getConfigurationValue, addToValueTree } from '../helpers'; export class ConfigurationModel implements IConfigurationModel { private isFrozen: boolean = false; constructor( private _contents: any = {}, private _keys: string[] = [], private _overrides: IOverrides[] = [] ) { } get contents(): any { return this.checkAndFreeze(this._contents); } get overrides(): IOverrides[] { return this.checkAndFreeze(this._overrides); } get keys(): string[] { return this.checkAndFreeze(this._keys); } isEmpty(): boolean { return this._keys.length === 0 && Object.keys(this._contents).length === 0 && this._overrides.length === 0; } getValue(section: string | undefined): V { return section ? getConfigurationValue(this.contents, section) : this.contents; } override(identifier: string): ConfigurationModel { const overrideContents = this.getContentsForOverrideIdentifer(identifier); if (!overrideContents || typeof overrideContents !== 'object' || !Object.keys(overrideContents).length) { // If there are no valid overrides, return self return this; } let contents = {}; for (const key of arrays.distinct([...Object.keys(this.contents), ...Object.keys(overrideContents)])) { let contentsForKey = this.contents[key]; let overrideContentsForKey = overrideContents[key]; // If there are override contents for the key, clone and merge otherwise use base contents if (overrideContentsForKey) { // Clone and merge only if base contents and override contents are of type object otherwise just override if (typeof contentsForKey === 'object' && typeof overrideContentsForKey === 'object') { contentsForKey = objects.deepClone(contentsForKey); this.mergeContents(contentsForKey, overrideContentsForKey); } else { contentsForKey = overrideContentsForKey; } } contents[key] = contentsForKey; } return new ConfigurationModel(contents); } merge(...others: ConfigurationModel[]): ConfigurationModel { const contents = objects.deepClone(this.contents); const overrides = objects.deepClone(this.overrides); const keys = [...this.keys]; for (const other of others) { this.mergeContents(contents, other.contents); for (const otherOverride of other.overrides) { const [override] = overrides.filter(o => arrays.equals(o.identifiers, otherOverride.identifiers)); if (override) { this.mergeContents(override.contents, otherOverride.contents); } else { overrides.push(objects.deepClone(otherOverride)); } } for (const key of other.keys) { if (keys.indexOf(key) === -1) { keys.push(key); } } } return new ConfigurationModel(contents, keys, overrides); } freeze(): ConfigurationModel { this.isFrozen = true; return this; } private mergeContents(source: any, target: any): void { for (const key of Object.keys(target)) { if (key in source) { if (types.isObject(source[key]) && types.isObject(target[key])) { this.mergeContents(source[key], target[key]); continue; } } source[key] = objects.deepClone(target[key]); } } private checkAndFreeze(data: T): T { if (this.isFrozen && !Object.isFrozen(data)) { return objects.deepFreeze(data); } return data; } private getContentsForOverrideIdentifer(identifier: string): any { for (const override of this.overrides) { if (override.identifiers.indexOf(identifier) !== -1) { return override.contents; } } return null; } toJSON(): IConfigurationModel { return { contents: this.contents, overrides: this.overrides, keys: this.keys }; } // Update methods public setValue(key: string, value: any) { this.addKey(key); addToValueTree(this.contents, key, value, e => { throw new Error(e); }); } public removeValue(key: string): void { if (this.removeKey(key)) { removeFromValueTree(this.contents, key); } } private addKey(key: string): void { let index = this.keys.length; for (let i = 0; i < index; i++) { if (key.indexOf(this.keys[i]) === 0) { index = i; } } this.keys.splice(index, 1, key); } private removeKey(key: string): boolean { let index = this.keys.indexOf(key); if (index !== -1) { this.keys.splice(index, 1); return true; } return false; } }