diff --git a/src/vs/platform/configuration/common/configurationModels.ts b/src/vs/platform/configuration/common/configurationModels.ts index 02827df0c62..cc4a855cf08 100644 --- a/src/vs/platform/configuration/common/configurationModels.ts +++ b/src/vs/platform/configuration/common/configurationModels.ts @@ -14,8 +14,6 @@ import { OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/conf import { IOverrides, overrideIdentifierFromKey, addToValueTree, toValuesTree, IConfigurationModel, getConfigurationValue, IConfigurationOverrides, IConfigurationData, getDefaultValues, getConfigurationKeys, IConfigurationChangeEvent, ConfigurationTarget, removeFromValueTree, toOverrides } from 'vs/platform/configuration/common/configuration'; import { Workspace } from 'vs/platform/workspace/common/workspace'; -declare const Proxy: any; // TODO@TypeScript - export class ConfigurationModel implements IConfigurationModel { private isFrozen: boolean = false; @@ -288,13 +286,13 @@ export class Configuration { private _workspaceConfiguration: ConfigurationModel = new ConfigurationModel(), private _folderConfigurations: StrictResourceMap = new StrictResourceMap(), private _memoryConfiguration: ConfigurationModel = new ConfigurationModel(), - private _memoryConfigurationByResource: StrictResourceMap = new StrictResourceMap()) { + private _memoryConfigurationByResource: StrictResourceMap = new StrictResourceMap(), + private _freeze: boolean = true) { } getValue(section: string, overrides: IConfigurationOverrides, workspace: Workspace): any { const consolidateConfigurationModel = this.getConsolidateConfigurationModel(overrides, workspace); - const result = consolidateConfigurationModel.getValue(section); - return this.toReadonlyValue(result); + return consolidateConfigurationModel.getValue(section); } updateValue(key: string, value: any, overrides: IConfigurationOverrides = {}): void { @@ -337,7 +335,7 @@ export class Configuration { workspace: workspace ? overrides.overrideIdentifier ? this._workspaceConfiguration.freeze().override(overrides.overrideIdentifier).getValue(key) : this._workspaceConfiguration.freeze().getValue(key) : void 0, //Check on workspace exists or not because _workspaceConfiguration is never null workspaceFolder: folderConfigurationModel ? overrides.overrideIdentifier ? folderConfigurationModel.freeze().override(overrides.overrideIdentifier).getValue(key) : folderConfigurationModel.freeze().getValue(key) : void 0, memory: overrides.overrideIdentifier ? memoryConfigurationModel.freeze().override(overrides.overrideIdentifier).getValue(key) : memoryConfigurationModel.freeze().getValue(key), - value: this.toReadonlyValue(consolidateConfigurationModel.getValue(key)) + value: consolidateConfigurationModel.getValue(key) }; } @@ -425,6 +423,9 @@ export class Configuration { private getWorkspaceConsolidatedConfiguration(): ConfigurationModel { if (!this._workspaceConsolidatedConfiguration) { this._workspaceConsolidatedConfiguration = this._defaultConfiguration.merge(this._userConfiguration, this._workspaceConfiguration, this._memoryConfiguration); + if (this._freeze) { + this._workspaceConfiguration = this._workspaceConfiguration.freeze(); + } } return this._workspaceConsolidatedConfiguration; } @@ -436,6 +437,9 @@ export class Configuration { const folderConfiguration = this._folderConfigurations.get(folder); if (folderConfiguration) { folderConsolidatedConfiguration = workspaceConsolidateConfiguration.merge(folderConfiguration); + if (this._freeze) { + folderConsolidatedConfiguration = folderConsolidatedConfiguration.freeze(); + } this._foldersConsolidatedConfigurations.set(folder, folderConsolidatedConfiguration); } else { folderConsolidatedConfiguration = workspaceConsolidateConfiguration; @@ -454,22 +458,6 @@ export class Configuration { return null; } - private toReadonlyValue(result: any): any { - const readonlyProxy = (target) => { - return types.isObject(target) ? - new Proxy(target, { - get: (target: any, property: string) => readonlyProxy(target[property]), - set: (target: any, property: string, value: any) => { throw new Error(`TypeError: Cannot assign to read only property '${property}' of object`); }, - deleteProperty: (target: any, property: string) => { throw new Error(`TypeError: Cannot delete read only property '${property}' of object`); }, - defineProperty: (target: any, property: string) => { throw new Error(`TypeError: Cannot define property '${property}' for a readonly object`); }, - setPrototypeOf: (target: any) => { throw new Error(`TypeError: Cannot set prototype for a readonly object`); }, - isExtensible: () => false, - preventExtensions: () => true - }) : target; - }; - return readonlyProxy(result); - } - toData(): IConfigurationData { return { defaults: { @@ -512,21 +500,6 @@ export class Configuration { } return all; } - - public static parse(data: IConfigurationData): Configuration { - const defaultConfiguration = Configuration.parseConfigurationModel(data.defaults); - const userConfiguration = Configuration.parseConfigurationModel(data.user); - const workspaceConfiguration = Configuration.parseConfigurationModel(data.workspace); - const folders: StrictResourceMap = Object.keys(data.folders).reduce((result, key) => { - result.set(URI.parse(key), Configuration.parseConfigurationModel(data.folders[key])); - return result; - }, new StrictResourceMap()); - return new Configuration(defaultConfiguration, userConfiguration, workspaceConfiguration, folders); - } - - private static parseConfigurationModel(model: IConfigurationModel): ConfigurationModel { - return new ConfigurationModel(model.contents, model.keys, model.overrides).freeze(); - } } export class AbstractConfigurationChangeEvent { diff --git a/src/vs/workbench/api/node/extHostConfiguration.ts b/src/vs/workbench/api/node/extHostConfiguration.ts index 415420d4415..c7c10c31fb2 100644 --- a/src/vs/workbench/api/node/extHostConfiguration.ts +++ b/src/vs/workbench/api/node/extHostConfiguration.ts @@ -11,7 +11,7 @@ import * as vscode from 'vscode'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; import { ExtHostConfigurationShape, MainThreadConfigurationShape, IWorkspaceConfigurationChangeEventData, IConfigurationInitData } from './extHost.protocol'; import { ConfigurationTarget as ExtHostConfigurationTarget } from './extHostTypes'; -import { IConfigurationData, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationData, ConfigurationTarget, IConfigurationModel } from 'vs/platform/configuration/common/configuration'; import { Configuration, ConfigurationChangeEvent, ConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { WorkspaceConfigurationChangeEvent } from 'vs/workbench/services/configuration/common/configurationModels'; import { StrictResourceMap } from 'vs/base/common/map'; @@ -50,7 +50,7 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape { constructor(proxy: MainThreadConfigurationShape, extHostWorkspace: ExtHostWorkspace, data: IConfigurationInitData) { this._proxy = proxy; this._extHostWorkspace = extHostWorkspace; - this._configuration = Configuration.parse(data); + this._configuration = ExtHostConfiguration.parse(data); this._configurationScopes = data.configurationScopes; } @@ -59,14 +59,14 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape { } $acceptConfigurationChanged(data: IConfigurationData, eventData: IWorkspaceConfigurationChangeEventData) { - this._configuration = Configuration.parse(data); + this._configuration = ExtHostConfiguration.parse(data); this._onDidChangeConfiguration.fire(this._toConfigurationChangeEvent(eventData)); } getConfiguration(section?: string, resource?: URI, extensionId?: string): vscode.WorkspaceConfiguration { - const config = section + const config = this._toReadonlyValue(section ? lookUp(this._configuration.getValue(null, { resource }, this._extHostWorkspace.workspace), section) - : this._configuration.getValue(null, { resource }, this._extHostWorkspace.workspace); + : this._configuration.getValue(null, { resource }, this._extHostWorkspace.workspace)); if (section) { this._validateConfigurationAccess(section, resource, extensionId); @@ -157,6 +157,22 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape { return Object.freeze(result); } + private _toReadonlyValue(result: any): any { + const readonlyProxy = (target) => { + return isObject(target) ? + new Proxy(target, { + get: (target: any, property: string) => readonlyProxy(target[property]), + set: (target: any, property: string, value: any) => { throw new Error(`TypeError: Cannot assign to read only property '${property}' of object`); }, + deleteProperty: (target: any, property: string) => { throw new Error(`TypeError: Cannot delete read only property '${property}' of object`); }, + defineProperty: (target: any, property: string) => { throw new Error(`TypeError: Cannot define property '${property}' for a readonly object`); }, + setPrototypeOf: (target: any) => { throw new Error(`TypeError: Cannot set prototype for a readonly object`); }, + isExtensible: () => false, + preventExtensions: () => true + }) : target; + }; + return readonlyProxy(result); + } + private _validateConfigurationAccess(key: string, resource: URI, extensionId: string): void { const scope = this._configurationScopes[key]; const extensionIdText = extensionId ? `[${extensionId}] ` : ''; @@ -187,4 +203,19 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape { affectsConfiguration: (section: string, resource?: URI) => event.affectsConfiguration(section, resource) }); } + + private static parse(data: IConfigurationData): Configuration { + const defaultConfiguration = ExtHostConfiguration.parseConfigurationModel(data.defaults); + const userConfiguration = ExtHostConfiguration.parseConfigurationModel(data.user); + const workspaceConfiguration = ExtHostConfiguration.parseConfigurationModel(data.workspace); + const folders: StrictResourceMap = Object.keys(data.folders).reduce((result, key) => { + result.set(URI.parse(key), ExtHostConfiguration.parseConfigurationModel(data.folders[key])); + return result; + }, new StrictResourceMap()); + return new Configuration(defaultConfiguration, userConfiguration, workspaceConfiguration, folders, new ConfigurationModel(), new StrictResourceMap(), false); + } + + private static parseConfigurationModel(model: IConfigurationModel): ConfigurationModel { + return new ConfigurationModel(model.contents, model.keys, model.overrides).freeze(); + } }