mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Refresh master with initial release/0.24 snapshot (#332)
* Initial port of release/0.24 source code * Fix additional headers * Fix a typo in launch.json
This commit is contained in:
@@ -4,94 +4,186 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { StrictResourceMap } from 'vs/base/common/map';
|
||||
import { Workspace } from 'vs/platform/workspace/common/workspace';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import Event from 'vs/base/common/event';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { StrictResourceMap } from 'vs/base/common/map';
|
||||
|
||||
export const IConfigurationService = createDecorator<IConfigurationService>('configurationService');
|
||||
|
||||
export function isConfigurationOverrides(thing: any): thing is IConfigurationOverrides {
|
||||
return thing
|
||||
&& typeof thing === 'object'
|
||||
&& (!thing.overrideIdentifier || typeof thing.overrideIdentifier === 'string')
|
||||
&& (!thing.resource || thing.resource instanceof URI);
|
||||
}
|
||||
|
||||
export interface IConfigurationOverrides {
|
||||
overrideIdentifier?: string;
|
||||
resource?: URI;
|
||||
}
|
||||
|
||||
export type IConfigurationValues = { [key: string]: IConfigurationValue<any> };
|
||||
export enum ConfigurationTarget {
|
||||
USER = 1,
|
||||
WORKSPACE,
|
||||
WORKSPACE_FOLDER,
|
||||
DEFAULT,
|
||||
MEMORY
|
||||
}
|
||||
|
||||
export interface IConfigurationChangeEvent {
|
||||
|
||||
source: ConfigurationTarget;
|
||||
affectedKeys: string[];
|
||||
affectsConfiguration(configuration: string, resource?: URI): boolean;
|
||||
|
||||
// Following data is used for telemetry
|
||||
sourceConfig: any;
|
||||
|
||||
// Following data is used for Extension host configuration event
|
||||
changedConfiguration: IConfigurationModel;
|
||||
changedConfigurationByResource: StrictResourceMap<IConfigurationModel>;
|
||||
}
|
||||
|
||||
export interface IConfigurationService {
|
||||
_serviceBrand: any;
|
||||
|
||||
getConfigurationData<T>(): IConfigurationData<T>;
|
||||
onDidChangeConfiguration: Event<IConfigurationChangeEvent>;
|
||||
|
||||
/**
|
||||
* Fetches the appropriate section of the configuration JSON file.
|
||||
* This will be an object keyed off the section name.
|
||||
*/
|
||||
getConfiguration<T>(section?: string, overrides?: IConfigurationOverrides): T;
|
||||
getConfigurationData(): IConfigurationData;
|
||||
|
||||
/**
|
||||
* Resolves a configuration key to its values in the different scopes
|
||||
* the setting is defined.
|
||||
*/
|
||||
lookup<T>(key: string, overrides?: IConfigurationOverrides): IConfigurationValue<T>;
|
||||
getConfiguration<T>(): T;
|
||||
getConfiguration<T>(section: string): T;
|
||||
getConfiguration<T>(overrides: IConfigurationOverrides): T;
|
||||
getConfiguration<T>(section: string, overrides: IConfigurationOverrides): T;
|
||||
|
||||
/**
|
||||
* Returns the defined keys of configurations in the different scopes
|
||||
* the key is defined.
|
||||
*/
|
||||
keys(overrides?: IConfigurationOverrides): IConfigurationKeys;
|
||||
getValue<T>(key: string, overrides?: IConfigurationOverrides): T;
|
||||
|
||||
/**
|
||||
* Similar to #getConfiguration() but ensures that the latest configuration
|
||||
* from disk is fetched.
|
||||
*/
|
||||
reloadConfiguration<T>(section?: string): TPromise<T>;
|
||||
updateValue(key: string, value: any): TPromise<void>;
|
||||
updateValue(key: string, value: any, overrides: IConfigurationOverrides): TPromise<void>;
|
||||
updateValue(key: string, value: any, target: ConfigurationTarget): TPromise<void>;
|
||||
updateValue(key: string, value: any, overrides: IConfigurationOverrides, target: ConfigurationTarget, donotNotifyError?: boolean): TPromise<void>;
|
||||
|
||||
/**
|
||||
* Event that fires when the configuration changes.
|
||||
*/
|
||||
onDidUpdateConfiguration: Event<IConfigurationServiceEvent>;
|
||||
reloadConfiguration(): TPromise<void>;
|
||||
reloadConfiguration(folder: IWorkspaceFolder): TPromise<void>;
|
||||
|
||||
/**
|
||||
* Returns the defined values of configurations in the different scopes.
|
||||
*/
|
||||
values(): IConfigurationValues;
|
||||
inspect<T>(key: string): {
|
||||
default: T,
|
||||
user: T,
|
||||
workspace: T,
|
||||
workspaceFolder: T,
|
||||
memory?: T,
|
||||
value: T,
|
||||
};
|
||||
|
||||
keys(): {
|
||||
default: string[];
|
||||
user: string[];
|
||||
workspace: string[];
|
||||
workspaceFolder: string[];
|
||||
memory?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export enum ConfigurationSource {
|
||||
Default = 1,
|
||||
User,
|
||||
Workspace
|
||||
export interface IConfigurationModel {
|
||||
contents: any;
|
||||
keys: string[];
|
||||
overrides: IOverrides[];
|
||||
}
|
||||
|
||||
export interface IConfigurationServiceEvent {
|
||||
/**
|
||||
* The type of source that triggered this event.
|
||||
*/
|
||||
source: ConfigurationSource;
|
||||
/**
|
||||
* The part of the configuration contributed by the source of this event.
|
||||
*/
|
||||
sourceConfig: any;
|
||||
export interface IOverrides {
|
||||
contents: any;
|
||||
identifiers: string[];
|
||||
}
|
||||
|
||||
export interface IConfigurationValue<T> {
|
||||
value: T;
|
||||
default: T;
|
||||
user: T;
|
||||
workspace: T;
|
||||
folder: T;
|
||||
export interface IConfigurationData {
|
||||
defaults: IConfigurationModel;
|
||||
user: IConfigurationModel;
|
||||
workspace: IConfigurationModel;
|
||||
folders: { [folder: string]: IConfigurationModel };
|
||||
}
|
||||
|
||||
export interface IConfigurationKeys {
|
||||
default: string[];
|
||||
user: string[];
|
||||
workspace: string[];
|
||||
folder: string[];
|
||||
export function compare(from: IConfigurationModel, to: IConfigurationModel): { added: string[], removed: string[], updated: string[] } {
|
||||
const added = to.keys.filter(key => from.keys.indexOf(key) === -1);
|
||||
const removed = from.keys.filter(key => to.keys.indexOf(key) === -1);
|
||||
const updated = [];
|
||||
|
||||
for (const key of from.keys) {
|
||||
const value1 = getConfigurationValue(from.contents, key);
|
||||
const value2 = getConfigurationValue(to.contents, key);
|
||||
if (!objects.equals(value1, value2)) {
|
||||
updated.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
return { added, removed, updated };
|
||||
}
|
||||
|
||||
export function toValuesTree(properties: { [qualifiedKey: string]: any }, conflictReporter: (message: string) => void): any {
|
||||
const root = Object.create(null);
|
||||
|
||||
for (let key in properties) {
|
||||
addToValueTree(root, key, properties[key], conflictReporter);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
export function addToValueTree(settingsTreeRoot: any, key: string, value: any, conflictReporter: (message: string) => void): void {
|
||||
const segments = key.split('.');
|
||||
const last = segments.pop();
|
||||
|
||||
let curr = settingsTreeRoot;
|
||||
for (let i = 0; i < segments.length; i++) {
|
||||
let s = segments[i];
|
||||
let obj = curr[s];
|
||||
switch (typeof obj) {
|
||||
case 'undefined':
|
||||
obj = curr[s] = Object.create(null);
|
||||
break;
|
||||
case 'object':
|
||||
break;
|
||||
default:
|
||||
conflictReporter(`Ignoring ${key} as ${segments.slice(0, i + 1).join('.')} is ${JSON.stringify(obj)}`);
|
||||
return;
|
||||
}
|
||||
curr = obj;
|
||||
};
|
||||
|
||||
if (typeof curr === 'object') {
|
||||
curr[last] = value; // workaround https://github.com/Microsoft/vscode/issues/13606
|
||||
} else {
|
||||
conflictReporter(`Ignoring ${key} as ${segments.join('.')} is ${JSON.stringify(curr)}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function removeFromValueTree(valueTree: any, key: string): void {
|
||||
const segments = key.split('.');
|
||||
doRemoveFromValueTree(valueTree, segments);
|
||||
}
|
||||
|
||||
function doRemoveFromValueTree(valueTree: any, segments: string[]): void {
|
||||
const first = segments.shift();
|
||||
if (segments.length === 0) {
|
||||
// Reached last segment
|
||||
delete valueTree[first];
|
||||
return;
|
||||
}
|
||||
|
||||
if (Object.keys(valueTree).indexOf(first) !== -1) {
|
||||
const value = valueTree[first];
|
||||
if (typeof value === 'object' && !Array.isArray(value)) {
|
||||
doRemoveFromValueTree(value, segments);
|
||||
if (Object.keys(value).length === 0) {
|
||||
delete valueTree[first];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,256 +221,27 @@ export function merge(base: any, add: any, overwrite: boolean): void {
|
||||
});
|
||||
}
|
||||
|
||||
export interface IConfiguraionModel<T> {
|
||||
contents: T;
|
||||
keys: string[];
|
||||
overrides: IOverrides<T>[];
|
||||
export function getConfigurationKeys(): string[] {
|
||||
const properties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
|
||||
return Object.keys(properties);
|
||||
}
|
||||
|
||||
export interface IOverrides<T> {
|
||||
contents: T;
|
||||
identifiers: string[];
|
||||
export function getDefaultValues(): any {
|
||||
const valueTreeRoot: any = Object.create(null);
|
||||
const properties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
|
||||
|
||||
for (let key in properties) {
|
||||
let value = properties[key].default;
|
||||
addToValueTree(valueTreeRoot, key, value, message => console.error(`Conflict in default settings: ${message}`));
|
||||
}
|
||||
|
||||
return valueTreeRoot;
|
||||
}
|
||||
|
||||
export class ConfigurationModel<T> implements IConfiguraionModel<T> {
|
||||
|
||||
constructor(protected _contents: T = <T>{}, protected _keys: string[] = [], protected _overrides: IOverrides<T>[] = []) {
|
||||
}
|
||||
|
||||
public get contents(): T {
|
||||
return this._contents;
|
||||
}
|
||||
|
||||
public get overrides(): IOverrides<T>[] {
|
||||
return this._overrides;
|
||||
}
|
||||
|
||||
public get keys(): string[] {
|
||||
return this._keys;
|
||||
}
|
||||
|
||||
public getContentsFor<V>(section: string): V {
|
||||
return objects.clone(this.contents[section]);
|
||||
}
|
||||
|
||||
public override<V>(identifier: string): ConfigurationModel<V> {
|
||||
const result = new ConfigurationModel<V>();
|
||||
const contents = objects.clone<any>(this.contents);
|
||||
if (this._overrides) {
|
||||
for (const override of this._overrides) {
|
||||
if (override.identifiers.indexOf(identifier) !== -1) {
|
||||
merge(contents, override.contents, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
result._contents = contents;
|
||||
return result;
|
||||
}
|
||||
|
||||
public merge(other: ConfigurationModel<T>, overwrite: boolean = true): ConfigurationModel<T> {
|
||||
const mergedModel = new ConfigurationModel<T>();
|
||||
this.doMerge(mergedModel, this, overwrite);
|
||||
this.doMerge(mergedModel, other, overwrite);
|
||||
return mergedModel;
|
||||
}
|
||||
|
||||
protected doMerge(source: ConfigurationModel<T>, target: ConfigurationModel<T>, overwrite: boolean = true) {
|
||||
merge(source.contents, objects.clone(target.contents), overwrite);
|
||||
const overrides = objects.clone(source._overrides);
|
||||
for (const override of target._overrides) {
|
||||
const [sourceOverride] = overrides.filter(o => arrays.equals(o.identifiers, override.identifiers));
|
||||
if (sourceOverride) {
|
||||
merge(sourceOverride.contents, override.contents, overwrite);
|
||||
} else {
|
||||
overrides.push(override);
|
||||
}
|
||||
}
|
||||
source._overrides = overrides;
|
||||
}
|
||||
export function overrideIdentifierFromKey(key: string): string {
|
||||
return key.substring(1, key.length - 1);
|
||||
}
|
||||
|
||||
export interface IConfigurationData<T> {
|
||||
defaults: IConfiguraionModel<T>;
|
||||
user: IConfiguraionModel<T>;
|
||||
workspace: IConfiguraionModel<T>;
|
||||
folders: { [folder: string]: IConfiguraionModel<T> };
|
||||
export function keyFromOverrideIdentifier(overrideIdentifier: string): string {
|
||||
return `[${overrideIdentifier}]`;
|
||||
}
|
||||
|
||||
export class Configuration<T> {
|
||||
|
||||
private _globalConfiguration: ConfigurationModel<T>;
|
||||
private _workspaceConsolidatedConfiguration: ConfigurationModel<T>;
|
||||
private _legacyWorkspaceConsolidatedConfiguration: ConfigurationModel<T>;
|
||||
protected _foldersConsolidatedConfigurations: StrictResourceMap<ConfigurationModel<T>>;
|
||||
|
||||
constructor(protected _defaults: ConfigurationModel<T>, protected _user: ConfigurationModel<T>, protected _workspaceConfiguration: ConfigurationModel<T> = new ConfigurationModel<T>(), protected folders: StrictResourceMap<ConfigurationModel<T>> = new StrictResourceMap<ConfigurationModel<T>>(), protected _workspace?: Workspace) {
|
||||
this.merge();
|
||||
}
|
||||
|
||||
get defaults(): ConfigurationModel<T> {
|
||||
return this._defaults;
|
||||
}
|
||||
|
||||
get user(): ConfigurationModel<T> {
|
||||
return this._user;
|
||||
}
|
||||
|
||||
protected merge(): void {
|
||||
this._globalConfiguration = new ConfigurationModel<T>().merge(this._defaults).merge(this._user);
|
||||
this._workspaceConsolidatedConfiguration = new ConfigurationModel<T>().merge(this._globalConfiguration).merge(this._workspaceConfiguration);
|
||||
this._legacyWorkspaceConsolidatedConfiguration = null;
|
||||
this._foldersConsolidatedConfigurations = new StrictResourceMap<ConfigurationModel<T>>();
|
||||
for (const folder of this.folders.keys()) {
|
||||
this.mergeFolder(folder);
|
||||
}
|
||||
}
|
||||
|
||||
protected mergeFolder(folder: URI) {
|
||||
this._foldersConsolidatedConfigurations.set(folder, new ConfigurationModel<T>().merge(this._workspaceConsolidatedConfiguration).merge(this.folders.get(folder)));
|
||||
}
|
||||
|
||||
getValue<C>(section: string = '', overrides: IConfigurationOverrides = {}): C {
|
||||
const configModel = this.getConsolidateConfigurationModel(overrides);
|
||||
return section ? configModel.getContentsFor<C>(section) : configModel.contents;
|
||||
}
|
||||
|
||||
lookup<C>(key: string, overrides: IConfigurationOverrides = {}): IConfigurationValue<C> {
|
||||
// make sure to clone the configuration so that the receiver does not tamper with the values
|
||||
const consolidateConfigurationModel = this.getConsolidateConfigurationModel(overrides);
|
||||
const folderConfigurationModel = this.getFolderConfigurationModelForResource(overrides.resource);
|
||||
return {
|
||||
default: objects.clone(getConfigurationValue<C>(overrides.overrideIdentifier ? this._defaults.override(overrides.overrideIdentifier).contents : this._defaults.contents, key)),
|
||||
user: objects.clone(getConfigurationValue<C>(overrides.overrideIdentifier ? this._user.override(overrides.overrideIdentifier).contents : this._user.contents, key)),
|
||||
workspace: objects.clone(this._workspace ? getConfigurationValue<C>(overrides.overrideIdentifier ? this._workspaceConfiguration.override(overrides.overrideIdentifier).contents : this._workspaceConfiguration.contents, key) : void 0), //Check on workspace exists or not because _workspaceConfiguration is never null
|
||||
folder: objects.clone(folderConfigurationModel ? getConfigurationValue<C>(overrides.overrideIdentifier ? folderConfigurationModel.override(overrides.overrideIdentifier).contents : folderConfigurationModel.contents, key) : void 0),
|
||||
value: objects.clone(getConfigurationValue<C>(consolidateConfigurationModel.contents, key))
|
||||
};
|
||||
}
|
||||
|
||||
lookupLegacy<C>(key: string): IConfigurationValue<C> {
|
||||
if (!this._legacyWorkspaceConsolidatedConfiguration) {
|
||||
this._legacyWorkspaceConsolidatedConfiguration = this._workspace ? new ConfigurationModel<any>().merge(this._workspaceConfiguration).merge(this.folders.get(this._workspace.roots[0])) : null;
|
||||
}
|
||||
const consolidateConfigurationModel = this.getConsolidateConfigurationModel({});
|
||||
return {
|
||||
default: objects.clone(getConfigurationValue<C>(this._defaults.contents, key)),
|
||||
user: objects.clone(getConfigurationValue<C>(this._user.contents, key)),
|
||||
workspace: objects.clone(this._legacyWorkspaceConsolidatedConfiguration ? getConfigurationValue<C>(this._legacyWorkspaceConsolidatedConfiguration.contents, key) : void 0),
|
||||
folder: void 0,
|
||||
value: objects.clone(getConfigurationValue<C>(consolidateConfigurationModel.contents, key))
|
||||
};
|
||||
}
|
||||
|
||||
keys(overrides: IConfigurationOverrides = {}): IConfigurationKeys {
|
||||
const folderConfigurationModel = this.getFolderConfigurationModelForResource(overrides.resource);
|
||||
return {
|
||||
default: this._defaults.keys,
|
||||
user: this._user.keys,
|
||||
workspace: this._workspaceConfiguration.keys,
|
||||
folder: folderConfigurationModel ? folderConfigurationModel.keys : []
|
||||
};
|
||||
}
|
||||
|
||||
values(): IConfigurationValues {
|
||||
const result = Object.create(null);
|
||||
const keyset = this.keys();
|
||||
const keys = [...keyset.workspace, ...keyset.user, ...keyset.default].sort();
|
||||
|
||||
let lastKey: string;
|
||||
for (const key of keys) {
|
||||
if (key !== lastKey) {
|
||||
lastKey = key;
|
||||
result[key] = this.lookup(key);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
values2(): Map<string, IConfigurationValue<T>> {
|
||||
const result: Map<string, IConfigurationValue<T>> = new Map<string, IConfigurationValue<T>>();
|
||||
const keyset = this.keys();
|
||||
const keys = [...keyset.workspace, ...keyset.user, ...keyset.default].sort();
|
||||
|
||||
let lastKey: string;
|
||||
for (const key of keys) {
|
||||
if (key !== lastKey) {
|
||||
lastKey = key;
|
||||
result.set(key, this.lookup<T>(key));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private getConsolidateConfigurationModel<C>(overrides: IConfigurationOverrides): ConfigurationModel<any> {
|
||||
let configurationModel = this.getConsolidatedConfigurationModelForResource(overrides);
|
||||
return overrides.overrideIdentifier ? configurationModel.override<T>(overrides.overrideIdentifier) : configurationModel;
|
||||
}
|
||||
|
||||
private getConsolidatedConfigurationModelForResource({ resource }: IConfigurationOverrides): ConfigurationModel<any> {
|
||||
if (!this._workspace) {
|
||||
return this._globalConfiguration;
|
||||
}
|
||||
|
||||
if (!resource) {
|
||||
return this._workspaceConsolidatedConfiguration;
|
||||
}
|
||||
|
||||
const root = this._workspace.getRoot(resource);
|
||||
if (!root) {
|
||||
return this._workspaceConsolidatedConfiguration;
|
||||
}
|
||||
|
||||
return this._foldersConsolidatedConfigurations.get(root) || this._workspaceConsolidatedConfiguration;
|
||||
}
|
||||
|
||||
private getFolderConfigurationModelForResource(resource: URI): ConfigurationModel<any> {
|
||||
if (!this._workspace || !resource) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const root = this._workspace.getRoot(resource);
|
||||
return root ? this.folders.get(root) : null;
|
||||
}
|
||||
|
||||
public toData(): IConfigurationData<any> {
|
||||
return {
|
||||
defaults: {
|
||||
contents: this._defaults.contents,
|
||||
overrides: this._defaults.overrides,
|
||||
keys: this._defaults.keys
|
||||
},
|
||||
user: {
|
||||
contents: this._user.contents,
|
||||
overrides: this._user.overrides,
|
||||
keys: this._user.keys
|
||||
},
|
||||
workspace: {
|
||||
contents: this._workspaceConfiguration.contents,
|
||||
overrides: this._workspaceConfiguration.overrides,
|
||||
keys: this._workspaceConfiguration.keys
|
||||
},
|
||||
folders: this.folders.keys().reduce((result, folder) => {
|
||||
const { contents, overrides, keys } = this.folders.get(folder);
|
||||
result[folder.toString()] = { contents, overrides, keys };
|
||||
return result;
|
||||
}, Object.create({}))
|
||||
};
|
||||
}
|
||||
|
||||
public static parse(data: IConfigurationData<any>, workspace: Workspace): Configuration<any> {
|
||||
const defaultConfiguration = Configuration.parseConfigurationModel(data.defaults);
|
||||
const userConfiguration = Configuration.parseConfigurationModel(data.user);
|
||||
const workspaceConfiguration = Configuration.parseConfigurationModel(data.workspace);
|
||||
const folders: StrictResourceMap<ConfigurationModel<any>> = Object.keys(data.folders).reduce((result, key) => {
|
||||
result.set(URI.parse(key), Configuration.parseConfigurationModel(data.folders[key]));
|
||||
return result;
|
||||
}, new StrictResourceMap<ConfigurationModel<any>>());
|
||||
return new Configuration<any>(defaultConfiguration, userConfiguration, workspaceConfiguration, folders, workspace);
|
||||
}
|
||||
|
||||
private static parseConfigurationModel(model: IConfiguraionModel<any>): ConfigurationModel<any> {
|
||||
return new ConfigurationModel(model.contents, model.keys, model.overrides);
|
||||
}
|
||||
}
|
||||
602
src/vs/platform/configuration/common/configurationModels.ts
Normal file
602
src/vs/platform/configuration/common/configurationModels.ts
Normal file
@@ -0,0 +1,602 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as json from 'vs/base/common/json';
|
||||
import { StrictResourceMap } from 'vs/base/common/map';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IConfigurationRegistry, Extensions, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IOverrides, overrideIdentifierFromKey, addToValueTree, toValuesTree, IConfigurationModel, merge, getConfigurationValue, IConfigurationOverrides, IConfigurationData, getDefaultValues, getConfigurationKeys, IConfigurationChangeEvent, ConfigurationTarget, removeFromValueTree } from 'vs/platform/configuration/common/configuration';
|
||||
import { Workspace } from 'vs/platform/workspace/common/workspace';
|
||||
|
||||
export class ConfigurationModel implements IConfigurationModel {
|
||||
|
||||
constructor(protected _contents: any = {}, protected _keys: string[] = [], protected _overrides: IOverrides[] = []) {
|
||||
}
|
||||
|
||||
public get contents(): any {
|
||||
return this._contents;
|
||||
}
|
||||
|
||||
public get overrides(): IOverrides[] {
|
||||
return this._overrides;
|
||||
}
|
||||
|
||||
public get keys(): string[] {
|
||||
return this._keys;
|
||||
}
|
||||
|
||||
public getSectionContents<V>(section: string): V {
|
||||
return this.contents[section];
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public setValueInOverrides(overrideIdentifier: string, key: string, value: any): void {
|
||||
let override = this._overrides.filter(override => override.identifiers.indexOf(overrideIdentifier) !== -1)[0];
|
||||
if (!override) {
|
||||
override = { identifiers: [overrideIdentifier], contents: {} };
|
||||
this._overrides.push(override);
|
||||
}
|
||||
addToValueTree(override.contents, key, value, e => { throw new Error(e); });
|
||||
}
|
||||
|
||||
public override<V>(identifier: string): ConfigurationModel {
|
||||
const overrideContents = this.getContentsForOverrideIdentifer(identifier);
|
||||
|
||||
if (!overrideContents || typeof overrideContents !== 'object' || !Object.keys(overrideContents).length) {
|
||||
// If there are no valid overrides, use base contents
|
||||
return new ConfigurationModel(this._contents);
|
||||
}
|
||||
|
||||
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.clone(contentsForKey);
|
||||
merge(contentsForKey, overrideContentsForKey, true);
|
||||
} else {
|
||||
contentsForKey = overrideContentsForKey;
|
||||
}
|
||||
}
|
||||
|
||||
contents[key] = contentsForKey;
|
||||
}
|
||||
return new ConfigurationModel(contents);
|
||||
}
|
||||
|
||||
public merge(other: ConfigurationModel, overwrite: boolean = true): ConfigurationModel {
|
||||
const mergedModel = new ConfigurationModel();
|
||||
this.doMerge(mergedModel, this, overwrite);
|
||||
this.doMerge(mergedModel, other, overwrite);
|
||||
return mergedModel;
|
||||
}
|
||||
|
||||
protected doMerge(source: ConfigurationModel, target: ConfigurationModel, overwrite: boolean = true) {
|
||||
merge(source.contents, objects.clone(target.contents), overwrite);
|
||||
const overrides = objects.clone(source._overrides);
|
||||
for (const override of target._overrides) {
|
||||
const [sourceOverride] = overrides.filter(o => arrays.equals(o.identifiers, override.identifiers));
|
||||
if (sourceOverride) {
|
||||
merge(sourceOverride.contents, override.contents, overwrite);
|
||||
} else {
|
||||
overrides.push(override);
|
||||
}
|
||||
}
|
||||
source._overrides = overrides;
|
||||
source._keys = arrays.distinct([...source._keys, ...target.keys]);
|
||||
}
|
||||
|
||||
private getContentsForOverrideIdentifer(identifier: string): any {
|
||||
for (const override of this._overrides) {
|
||||
if (override.identifiers.indexOf(identifier) !== -1) {
|
||||
return override.contents;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
toJSON(): IConfigurationModel {
|
||||
return {
|
||||
contents: this.contents,
|
||||
overrides: this.overrides,
|
||||
keys: this.keys
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class DefaultConfigurationModel extends ConfigurationModel {
|
||||
|
||||
constructor() {
|
||||
super(getDefaultValues());
|
||||
this._keys = getConfigurationKeys();
|
||||
this._overrides = Object.keys(this._contents)
|
||||
.filter(key => OVERRIDE_PROPERTY_PATTERN.test(key))
|
||||
.map(key => {
|
||||
return <IOverrides>{
|
||||
identifiers: [overrideIdentifierFromKey(key).trim()],
|
||||
contents: toValuesTree(this._contents[key], message => console.error(`Conflict in default settings file: ${message}`))
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
public get keys(): string[] {
|
||||
return this._keys;
|
||||
}
|
||||
}
|
||||
|
||||
interface Overrides extends IOverrides {
|
||||
raw: any;
|
||||
}
|
||||
|
||||
export class CustomConfigurationModel extends ConfigurationModel {
|
||||
|
||||
protected _parseErrors: any[] = [];
|
||||
|
||||
constructor(content: string = '', private name: string = '') {
|
||||
super();
|
||||
if (content) {
|
||||
this.update(content);
|
||||
}
|
||||
}
|
||||
|
||||
public get errors(): any[] {
|
||||
return this._parseErrors;
|
||||
}
|
||||
|
||||
public update(content: string): void {
|
||||
let parsed: any = {};
|
||||
let overrides: Overrides[] = [];
|
||||
let currentProperty: string = null;
|
||||
let currentParent: any = [];
|
||||
let previousParents: any[] = [];
|
||||
let parseErrors: json.ParseError[] = [];
|
||||
|
||||
function onValue(value: any) {
|
||||
if (Array.isArray(currentParent)) {
|
||||
(<any[]>currentParent).push(value);
|
||||
} else if (currentProperty) {
|
||||
currentParent[currentProperty] = value;
|
||||
}
|
||||
if (OVERRIDE_PROPERTY_PATTERN.test(currentProperty)) {
|
||||
onOverrideSettingsValue(currentProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
function onOverrideSettingsValue(property: string, value: any): void {
|
||||
overrides.push({
|
||||
identifiers: [overrideIdentifierFromKey(property).trim()],
|
||||
raw: value,
|
||||
contents: null
|
||||
});
|
||||
}
|
||||
|
||||
let visitor: json.JSONVisitor = {
|
||||
onObjectBegin: () => {
|
||||
let object = {};
|
||||
onValue(object);
|
||||
previousParents.push(currentParent);
|
||||
currentParent = object;
|
||||
currentProperty = null;
|
||||
},
|
||||
onObjectProperty: (name: string) => {
|
||||
currentProperty = name;
|
||||
},
|
||||
onObjectEnd: () => {
|
||||
currentParent = previousParents.pop();
|
||||
},
|
||||
onArrayBegin: () => {
|
||||
let array: any[] = [];
|
||||
onValue(array);
|
||||
previousParents.push(currentParent);
|
||||
currentParent = array;
|
||||
currentProperty = null;
|
||||
},
|
||||
onArrayEnd: () => {
|
||||
currentParent = previousParents.pop();
|
||||
},
|
||||
onLiteralValue: onValue,
|
||||
onError: (error: json.ParseErrorCode) => {
|
||||
parseErrors.push({ error: error });
|
||||
}
|
||||
};
|
||||
if (content) {
|
||||
try {
|
||||
json.visit(content, visitor);
|
||||
parsed = currentParent[0] || {};
|
||||
} catch (e) {
|
||||
console.error(`Error while parsing settings file ${this.name}: ${e}`);
|
||||
this._parseErrors = [e];
|
||||
}
|
||||
}
|
||||
this.processRaw(parsed);
|
||||
|
||||
const configurationProperties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
|
||||
this._overrides = overrides.map<IOverrides>(override => {
|
||||
// Filter unknown and non-overridable properties
|
||||
const raw = {};
|
||||
for (const key in override.raw) {
|
||||
if (configurationProperties[key] && configurationProperties[key].overridable) {
|
||||
raw[key] = override.raw[key];
|
||||
}
|
||||
}
|
||||
return {
|
||||
identifiers: override.identifiers,
|
||||
contents: toValuesTree(raw, message => console.error(`Conflict in settings file ${this.name}: ${message}`))
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
protected processRaw(raw: any): void {
|
||||
this._contents = toValuesTree(raw, message => console.error(`Conflict in settings file ${this.name}: ${message}`));
|
||||
this._keys = Object.keys(raw);
|
||||
}
|
||||
}
|
||||
|
||||
export class Configuration {
|
||||
|
||||
private _globalConfiguration: ConfigurationModel;
|
||||
private _workspaceConsolidatedConfiguration: ConfigurationModel;
|
||||
protected _foldersConsolidatedConfigurations: StrictResourceMap<ConfigurationModel>;
|
||||
|
||||
constructor(protected _defaults: ConfigurationModel,
|
||||
protected _user: ConfigurationModel,
|
||||
protected _workspaceConfiguration: ConfigurationModel = new ConfigurationModel(),
|
||||
protected folders: StrictResourceMap<ConfigurationModel> = new StrictResourceMap<ConfigurationModel>(),
|
||||
protected _memoryConfiguration: ConfigurationModel = new ConfigurationModel(),
|
||||
protected _memoryConfigurationByResource: StrictResourceMap<ConfigurationModel> = new StrictResourceMap<ConfigurationModel>()) {
|
||||
this.merge();
|
||||
}
|
||||
|
||||
get defaults(): ConfigurationModel {
|
||||
return this._defaults;
|
||||
}
|
||||
|
||||
get user(): ConfigurationModel {
|
||||
return this._user;
|
||||
}
|
||||
|
||||
get workspace(): ConfigurationModel {
|
||||
return this._workspaceConfiguration;
|
||||
}
|
||||
|
||||
protected merge(): void {
|
||||
this._globalConfiguration = this._defaults.merge(this._user);
|
||||
this.updateWorkspaceConsolidateConfiguration();
|
||||
this._foldersConsolidatedConfigurations = new StrictResourceMap<ConfigurationModel>();
|
||||
for (const folder of this.folders.keys()) {
|
||||
this.mergeFolder(folder);
|
||||
}
|
||||
}
|
||||
|
||||
private updateWorkspaceConsolidateConfiguration() {
|
||||
this._workspaceConsolidatedConfiguration = this._globalConfiguration.merge(this._workspaceConfiguration).merge(this._memoryConfiguration);
|
||||
}
|
||||
|
||||
protected mergeFolder(folder: URI) {
|
||||
this._foldersConsolidatedConfigurations.set(folder, this._workspaceConsolidatedConfiguration.merge(this.folders.get(folder)));
|
||||
}
|
||||
|
||||
getSection<C>(section: string = '', overrides: IConfigurationOverrides, workspace: Workspace): C {
|
||||
const configModel = this.getConsolidateConfigurationModel(overrides, workspace);
|
||||
return objects.clone(section ? configModel.getSectionContents<C>(section) : configModel.contents);
|
||||
}
|
||||
|
||||
getValue(key: string, overrides: IConfigurationOverrides, workspace: Workspace): any {
|
||||
const consolidateConfigurationModel = this.getConsolidateConfigurationModel(overrides, workspace);
|
||||
return objects.clone(getConfigurationValue<any>(consolidateConfigurationModel.contents, key));
|
||||
}
|
||||
|
||||
updateValue(key: string, value: any, overrides: IConfigurationOverrides = {}): void {
|
||||
let memoryConfiguration: ConfigurationModel;
|
||||
if (overrides.resource) {
|
||||
memoryConfiguration = this._memoryConfigurationByResource.get(overrides.resource);
|
||||
if (!memoryConfiguration) {
|
||||
memoryConfiguration = new ConfigurationModel();
|
||||
this._memoryConfigurationByResource.set(overrides.resource, memoryConfiguration);
|
||||
}
|
||||
} else {
|
||||
memoryConfiguration = this._memoryConfiguration;
|
||||
}
|
||||
|
||||
if (value === void 0) {
|
||||
memoryConfiguration.removeValue(key);
|
||||
} else {
|
||||
memoryConfiguration.setValue(key, value);
|
||||
}
|
||||
|
||||
if (!overrides.resource) {
|
||||
this.updateWorkspaceConsolidateConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
lookup<C>(key: string, overrides: IConfigurationOverrides, workspace: Workspace): {
|
||||
default: C,
|
||||
user: C,
|
||||
workspace: C,
|
||||
workspaceFolder: C
|
||||
memory?: C
|
||||
value: C,
|
||||
} {
|
||||
const consolidateConfigurationModel = this.getConsolidateConfigurationModel(overrides, workspace);
|
||||
const folderConfigurationModel = this.getFolderConfigurationModelForResource(overrides.resource, workspace);
|
||||
const memoryConfigurationModel = overrides.resource ? this._memoryConfigurationByResource.get(overrides.resource) || this._memoryConfiguration : this._memoryConfiguration;
|
||||
return objects.clone({
|
||||
default: getConfigurationValue<C>(overrides.overrideIdentifier ? this._defaults.override(overrides.overrideIdentifier).contents : this._defaults.contents, key),
|
||||
user: getConfigurationValue<C>(overrides.overrideIdentifier ? this._user.override(overrides.overrideIdentifier).contents : this._user.contents, key),
|
||||
workspace: workspace ? getConfigurationValue<C>(overrides.overrideIdentifier ? this._workspaceConfiguration.override(overrides.overrideIdentifier).contents : this._workspaceConfiguration.contents, key) : void 0, //Check on workspace exists or not because _workspaceConfiguration is never null
|
||||
workspaceFolder: folderConfigurationModel ? getConfigurationValue<C>(overrides.overrideIdentifier ? folderConfigurationModel.override(overrides.overrideIdentifier).contents : folderConfigurationModel.contents, key) : void 0,
|
||||
memory: getConfigurationValue<C>(overrides.overrideIdentifier ? memoryConfigurationModel.override(overrides.overrideIdentifier).contents : memoryConfigurationModel.contents, key),
|
||||
value: getConfigurationValue<C>(consolidateConfigurationModel.contents, key)
|
||||
});
|
||||
}
|
||||
|
||||
keys(workspace: Workspace): {
|
||||
default: string[];
|
||||
user: string[];
|
||||
workspace: string[];
|
||||
workspaceFolder: string[];
|
||||
} {
|
||||
const folderConfigurationModel = this.getFolderConfigurationModelForResource(null, workspace);
|
||||
return objects.clone({
|
||||
default: this._defaults.keys,
|
||||
user: this._user.keys,
|
||||
workspace: this._workspaceConfiguration.keys,
|
||||
workspaceFolder: folderConfigurationModel ? folderConfigurationModel.keys : []
|
||||
});
|
||||
}
|
||||
|
||||
private getConsolidateConfigurationModel<C>(overrides: IConfigurationOverrides, workspace: Workspace): ConfigurationModel {
|
||||
let configurationModel = this.getConsolidatedConfigurationModelForResource(overrides, workspace);
|
||||
return overrides.overrideIdentifier ? configurationModel.override(overrides.overrideIdentifier) : configurationModel;
|
||||
}
|
||||
|
||||
private getConsolidatedConfigurationModelForResource({ resource }: IConfigurationOverrides, workspace: Workspace): ConfigurationModel {
|
||||
if (!workspace) {
|
||||
return this._globalConfiguration;
|
||||
}
|
||||
|
||||
if (!resource) {
|
||||
return this._workspaceConsolidatedConfiguration;
|
||||
}
|
||||
|
||||
let consolidateConfiguration = this._workspaceConsolidatedConfiguration;
|
||||
const root = workspace.getFolder(resource);
|
||||
if (root) {
|
||||
consolidateConfiguration = this._foldersConsolidatedConfigurations.get(root.uri) || this._workspaceConsolidatedConfiguration;
|
||||
}
|
||||
|
||||
const memoryConfigurationForResource = this._memoryConfigurationByResource.get(resource);
|
||||
if (memoryConfigurationForResource) {
|
||||
consolidateConfiguration = consolidateConfiguration.merge(memoryConfigurationForResource);
|
||||
}
|
||||
|
||||
return consolidateConfiguration;
|
||||
}
|
||||
|
||||
private getFolderConfigurationModelForResource(resource: URI, workspace: Workspace): ConfigurationModel {
|
||||
if (!workspace || !resource) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const root = workspace.getFolder(resource);
|
||||
return root ? this.folders.get(root.uri) : null;
|
||||
}
|
||||
|
||||
public toData(): IConfigurationData {
|
||||
return {
|
||||
defaults: {
|
||||
contents: this._defaults.contents,
|
||||
overrides: this._defaults.overrides,
|
||||
keys: this._defaults.keys
|
||||
},
|
||||
user: {
|
||||
contents: this._user.contents,
|
||||
overrides: this._user.overrides,
|
||||
keys: this._user.keys
|
||||
},
|
||||
workspace: {
|
||||
contents: this._workspaceConfiguration.contents,
|
||||
overrides: this._workspaceConfiguration.overrides,
|
||||
keys: this._workspaceConfiguration.keys
|
||||
},
|
||||
folders: this.folders.keys().reduce((result, folder) => {
|
||||
const { contents, overrides, keys } = this.folders.get(folder);
|
||||
result[folder.toString()] = { contents, overrides, keys };
|
||||
return result;
|
||||
}, Object.create({}))
|
||||
};
|
||||
}
|
||||
|
||||
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<ConfigurationModel> = Object.keys(data.folders).reduce((result, key) => {
|
||||
result.set(URI.parse(key), Configuration.parseConfigurationModel(data.folders[key]));
|
||||
return result;
|
||||
}, new StrictResourceMap<ConfigurationModel>());
|
||||
return new Configuration(defaultConfiguration, userConfiguration, workspaceConfiguration, folders, new ConfigurationModel(), new StrictResourceMap<ConfigurationModel>());
|
||||
}
|
||||
|
||||
private static parseConfigurationModel(model: IConfigurationModel): ConfigurationModel {
|
||||
return new ConfigurationModel(model.contents, model.keys, model.overrides);
|
||||
}
|
||||
}
|
||||
|
||||
export class AbstractConfigurationChangeEvent {
|
||||
|
||||
protected doesConfigurationContains(configuration: ConfigurationModel, config: string): boolean {
|
||||
let changedKeysTree = configuration.contents;
|
||||
let requestedTree = toValuesTree({ [config]: true }, () => { });
|
||||
|
||||
let key;
|
||||
while (typeof requestedTree === 'object' && (key = Object.keys(requestedTree)[0])) { // Only one key should present, since we added only one property
|
||||
changedKeysTree = changedKeysTree[key];
|
||||
if (!changedKeysTree) {
|
||||
return false; // Requested tree is not found
|
||||
}
|
||||
requestedTree = requestedTree[key];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected updateKeys(configuration: ConfigurationModel, keys: string[], resource?: URI): void {
|
||||
for (const key of keys) {
|
||||
configuration.setValue(key, {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class AllKeysConfigurationChangeEvent extends AbstractConfigurationChangeEvent implements IConfigurationChangeEvent {
|
||||
|
||||
private _changedConfiguration: ConfigurationModel = null;
|
||||
|
||||
constructor(readonly affectedKeys: string[], readonly source: ConfigurationTarget, readonly sourceConfig: any) { super(); }
|
||||
|
||||
get changedConfiguration(): ConfigurationModel {
|
||||
if (!this._changedConfiguration) {
|
||||
this._changedConfiguration = new ConfigurationModel();
|
||||
this.updateKeys(this._changedConfiguration, this.affectedKeys);
|
||||
}
|
||||
return this._changedConfiguration;
|
||||
}
|
||||
|
||||
get changedConfigurationByResource(): StrictResourceMap<IConfigurationModel> {
|
||||
return new StrictResourceMap();
|
||||
}
|
||||
|
||||
affectsConfiguration(config: string, resource?: URI): boolean {
|
||||
return this.doesConfigurationContains(this.changedConfiguration, config);
|
||||
}
|
||||
}
|
||||
|
||||
export class ConfigurationChangeEvent extends AbstractConfigurationChangeEvent implements IConfigurationChangeEvent {
|
||||
|
||||
private _source: ConfigurationTarget;
|
||||
private _sourceConfig: any;
|
||||
|
||||
constructor(
|
||||
private _changedConfiguration: ConfigurationModel = new ConfigurationModel(),
|
||||
private _changedConfigurationByResource: StrictResourceMap<ConfigurationModel> = new StrictResourceMap<ConfigurationModel>()) {
|
||||
super();
|
||||
}
|
||||
|
||||
get changedConfiguration(): IConfigurationModel {
|
||||
return this._changedConfiguration;
|
||||
}
|
||||
|
||||
get changedConfigurationByResource(): StrictResourceMap<IConfigurationModel> {
|
||||
return this._changedConfigurationByResource;
|
||||
}
|
||||
|
||||
change(event: ConfigurationChangeEvent): ConfigurationChangeEvent
|
||||
change(keys: string[], resource?: URI): ConfigurationChangeEvent
|
||||
change(arg1: any, arg2?: any): ConfigurationChangeEvent {
|
||||
if (arg1 instanceof ConfigurationChangeEvent) {
|
||||
this._changedConfiguration = this._changedConfiguration.merge(arg1._changedConfiguration);
|
||||
for (const resource of arg1._changedConfigurationByResource.keys()) {
|
||||
let changedConfigurationByResource = this.getOrSetChangedConfigurationForResource(resource);
|
||||
changedConfigurationByResource = changedConfigurationByResource.merge(arg1._changedConfigurationByResource.get(resource));
|
||||
this._changedConfigurationByResource.set(resource, changedConfigurationByResource);
|
||||
}
|
||||
} else {
|
||||
this.changeWithKeys(arg1, arg2);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
telemetryData(source: ConfigurationTarget, sourceConfig: any): ConfigurationChangeEvent {
|
||||
this._source = source;
|
||||
this._sourceConfig = sourceConfig;
|
||||
return this;
|
||||
}
|
||||
|
||||
get affectedKeys(): string[] {
|
||||
const keys = [...this._changedConfiguration.keys];
|
||||
this._changedConfigurationByResource.forEach(model => keys.push(...model.keys));
|
||||
return keys;
|
||||
}
|
||||
|
||||
get source(): ConfigurationTarget {
|
||||
return this._source;
|
||||
}
|
||||
|
||||
get sourceConfig(): any {
|
||||
return this._sourceConfig;
|
||||
}
|
||||
|
||||
affectsConfiguration(config: string, resource?: URI): boolean {
|
||||
let configurationModelsToSearch: ConfigurationModel[] = [this._changedConfiguration];
|
||||
|
||||
if (resource) {
|
||||
let model = this._changedConfigurationByResource.get(resource);
|
||||
if (model) {
|
||||
configurationModelsToSearch.push(model);
|
||||
}
|
||||
} else {
|
||||
configurationModelsToSearch.push(...this._changedConfigurationByResource.values());
|
||||
}
|
||||
|
||||
for (const configuration of configurationModelsToSearch) {
|
||||
if (this.doesConfigurationContains(configuration, config)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private changeWithKeys(keys: string[], resource?: URI): void {
|
||||
let changedConfiguration = resource ? this.getOrSetChangedConfigurationForResource(resource) : this._changedConfiguration;
|
||||
this.updateKeys(changedConfiguration, keys);
|
||||
}
|
||||
|
||||
private getOrSetChangedConfigurationForResource(resource: URI): ConfigurationModel {
|
||||
let changedConfigurationByResource = this._changedConfigurationByResource.get(resource);
|
||||
if (!changedConfigurationByResource) {
|
||||
changedConfigurationByResource = new ConfigurationModel();
|
||||
this._changedConfigurationByResource.set(resource, changedConfigurationByResource);
|
||||
}
|
||||
return changedConfigurationByResource;
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import types = require('vs/base/common/types');
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
|
||||
import { clone } from 'vs/base/common/objects';
|
||||
|
||||
export const Extensions = {
|
||||
Configuration: 'base.contributions.configuration'
|
||||
@@ -28,13 +29,19 @@ export interface IConfigurationRegistry {
|
||||
*/
|
||||
registerConfigurations(configurations: IConfigurationNode[], validate?: boolean): void;
|
||||
|
||||
/**
|
||||
* Signal that the schema of a configuration setting has changes. It is currently only supported to change enumeration values.
|
||||
* Property or default value changes are not allowed.
|
||||
*/
|
||||
notifyConfigurationSchemaUpdated(configuration: IConfigurationNode): void;
|
||||
|
||||
registerDefaultConfigurations(defaultConfigurations: IDefaultConfigurationExtension[]): void;
|
||||
|
||||
/**
|
||||
* Event that fires whenver a configuratio has been
|
||||
* Event that fires whenver a configuration has been
|
||||
* registered.
|
||||
*/
|
||||
onDidRegisterConfiguration: Event<IConfigurationRegistry>;
|
||||
onDidRegisterConfiguration: Event<string[]>;
|
||||
|
||||
/**
|
||||
* Returns all configuration nodes contributed to this registry.
|
||||
@@ -61,6 +68,7 @@ export interface IConfigurationPropertySchema extends IJSONSchema {
|
||||
overridable?: boolean;
|
||||
isExecutable?: boolean;
|
||||
scope?: ConfigurationScope;
|
||||
notMultiRootAdopted?: boolean;
|
||||
}
|
||||
|
||||
export interface IConfigurationNode {
|
||||
@@ -81,37 +89,30 @@ export interface IDefaultConfigurationExtension {
|
||||
defaults: { [key: string]: {} };
|
||||
}
|
||||
|
||||
export const schemaId = 'vscode://schemas/settings';
|
||||
export const settingsSchema: IJSONSchema = { properties: {}, patternProperties: {}, additionalProperties: false, errorMessage: 'Unknown configuration setting' };
|
||||
export const resourceSettingsSchema: IJSONSchema = { properties: {}, patternProperties: {}, additionalProperties: false, errorMessage: 'Unknown configuration setting' };
|
||||
|
||||
export const editorConfigurationSchemaId = 'vscode://schemas/settings/editor';
|
||||
export const resourceConfigurationSchemaId = 'vscode://schemas/settings/resource';
|
||||
const contributionRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
|
||||
|
||||
class ConfigurationRegistry implements IConfigurationRegistry {
|
||||
|
||||
private configurationContributors: IConfigurationNode[];
|
||||
private configurationProperties: { [qualifiedKey: string]: IJSONSchema };
|
||||
private configurationSchema: IJSONSchema;
|
||||
private editorConfigurationSchema: IJSONSchema;
|
||||
private resourceConfigurationSchema: IJSONSchema;
|
||||
private _onDidRegisterConfiguration: Emitter<IConfigurationRegistry>;
|
||||
private overrideIdentifiers: string[] = [];
|
||||
private overridePropertyPattern: string;
|
||||
|
||||
private _onDidRegisterConfiguration: Emitter<string[]> = new Emitter<string[]>();
|
||||
readonly onDidRegisterConfiguration: Event<string[]> = this._onDidRegisterConfiguration.event;
|
||||
|
||||
constructor() {
|
||||
this.configurationContributors = [];
|
||||
this.configurationSchema = { properties: {}, patternProperties: {}, additionalProperties: false, errorMessage: 'Unknown configuration setting' };
|
||||
this.editorConfigurationSchema = { properties: {}, patternProperties: {}, additionalProperties: false, errorMessage: 'Unknown editor configuration setting' };
|
||||
this.resourceConfigurationSchema = { properties: {}, patternProperties: {}, additionalProperties: false, errorMessage: 'Not a resource configuration setting' };
|
||||
this._onDidRegisterConfiguration = new Emitter<IConfigurationRegistry>();
|
||||
this.configurationProperties = {};
|
||||
this.computeOverridePropertyPattern();
|
||||
|
||||
contributionRegistry.registerSchema(schemaId, this.configurationSchema);
|
||||
contributionRegistry.registerSchema(editorConfigurationSchemaId, this.editorConfigurationSchema);
|
||||
contributionRegistry.registerSchema(resourceConfigurationSchemaId, this.resourceConfigurationSchema);
|
||||
}
|
||||
|
||||
public get onDidRegisterConfiguration() {
|
||||
return this._onDidRegisterConfiguration.event;
|
||||
}
|
||||
|
||||
public registerConfiguration(configuration: IConfigurationNode, validate: boolean = true): void {
|
||||
@@ -119,14 +120,19 @@ class ConfigurationRegistry implements IConfigurationRegistry {
|
||||
}
|
||||
|
||||
public registerConfigurations(configurations: IConfigurationNode[], validate: boolean = true): void {
|
||||
const properties: string[] = [];
|
||||
configurations.forEach(configuration => {
|
||||
this.validateAndRegisterProperties(configuration, validate); // fills in defaults
|
||||
properties.push(...this.validateAndRegisterProperties(configuration, validate)); // fills in defaults
|
||||
this.configurationContributors.push(configuration);
|
||||
this.registerJSONConfiguration(configuration);
|
||||
this.updateSchemaForOverrideSettingsConfiguration(configuration);
|
||||
});
|
||||
|
||||
this._onDidRegisterConfiguration.fire(this);
|
||||
this._onDidRegisterConfiguration.fire(properties);
|
||||
}
|
||||
|
||||
public notifyConfigurationSchemaUpdated(configuration: IConfigurationNode) {
|
||||
contributionRegistry.registerSchema(editorConfigurationSchemaId, this.editorConfigurationSchema);
|
||||
}
|
||||
|
||||
public registerOverrideIdentifiers(overrideIdentifiers: string[]): void {
|
||||
@@ -158,9 +164,10 @@ class ConfigurationRegistry implements IConfigurationRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
private validateAndRegisterProperties(configuration: IConfigurationNode, validate: boolean = true, scope: ConfigurationScope = ConfigurationScope.WINDOW, overridable: boolean = false) {
|
||||
private validateAndRegisterProperties(configuration: IConfigurationNode, validate: boolean = true, scope: ConfigurationScope = ConfigurationScope.WINDOW, overridable: boolean = false): string[] {
|
||||
scope = configuration.scope !== void 0 && configuration.scope !== null ? configuration.scope : scope;
|
||||
overridable = configuration.overridable || overridable;
|
||||
let propertyKeys = [];
|
||||
let properties = configuration.properties;
|
||||
if (properties) {
|
||||
for (let key in properties) {
|
||||
@@ -185,14 +192,16 @@ class ConfigurationRegistry implements IConfigurationRegistry {
|
||||
}
|
||||
// add to properties map
|
||||
this.configurationProperties[key] = properties[key];
|
||||
propertyKeys.push(key);
|
||||
}
|
||||
}
|
||||
let subNodes = configuration.allOf;
|
||||
if (subNodes) {
|
||||
for (let node of subNodes) {
|
||||
this.validateAndRegisterProperties(node, validate, scope, overridable);
|
||||
propertyKeys.push(...this.validateAndRegisterProperties(node, validate, scope, overridable));
|
||||
}
|
||||
}
|
||||
return propertyKeys;
|
||||
}
|
||||
|
||||
validateProperty(property: string): boolean {
|
||||
@@ -208,12 +217,15 @@ class ConfigurationRegistry implements IConfigurationRegistry {
|
||||
}
|
||||
|
||||
private registerJSONConfiguration(configuration: IConfigurationNode) {
|
||||
let configurationSchema = this.configurationSchema;
|
||||
function register(configuration: IConfigurationNode) {
|
||||
let properties = configuration.properties;
|
||||
if (properties) {
|
||||
for (let key in properties) {
|
||||
configurationSchema.properties[key] = properties[key];
|
||||
settingsSchema.properties[key] = properties[key];
|
||||
resourceSettingsSchema.properties[key] = clone(properties[key]);
|
||||
if (properties[key].scope !== ConfigurationScope.RESOURCE) {
|
||||
resourceSettingsSchema.properties[key].doNotSuggest = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
let subNodes = configuration.allOf;
|
||||
@@ -222,19 +234,17 @@ class ConfigurationRegistry implements IConfigurationRegistry {
|
||||
}
|
||||
};
|
||||
register(configuration);
|
||||
contributionRegistry.registerSchema(schemaId, configurationSchema);
|
||||
}
|
||||
|
||||
private updateSchemaForOverrideSettingsConfiguration(configuration: IConfigurationNode): void {
|
||||
if (configuration.id !== SETTINGS_OVERRRIDE_NODE_ID) {
|
||||
this.update(configuration);
|
||||
contributionRegistry.registerSchema(editorConfigurationSchemaId, this.editorConfigurationSchema);
|
||||
contributionRegistry.registerSchema(resourceConfigurationSchemaId, this.resourceConfigurationSchema);
|
||||
}
|
||||
}
|
||||
|
||||
private updateOverridePropertyPatternKey(): void {
|
||||
let patternProperties: IJSONSchema = this.configurationSchema.patternProperties[this.overridePropertyPattern];
|
||||
let patternProperties: IJSONSchema = settingsSchema.patternProperties[this.overridePropertyPattern];
|
||||
if (!patternProperties) {
|
||||
patternProperties = {
|
||||
type: 'object',
|
||||
@@ -243,10 +253,11 @@ class ConfigurationRegistry implements IConfigurationRegistry {
|
||||
$ref: editorConfigurationSchemaId
|
||||
};
|
||||
}
|
||||
delete this.configurationSchema.patternProperties[this.overridePropertyPattern];
|
||||
delete settingsSchema.patternProperties[this.overridePropertyPattern];
|
||||
this.computeOverridePropertyPattern();
|
||||
this.configurationSchema.patternProperties[this.overridePropertyPattern] = patternProperties;
|
||||
contributionRegistry.registerSchema(schemaId, this.configurationSchema);
|
||||
|
||||
settingsSchema.patternProperties[this.overridePropertyPattern] = patternProperties;
|
||||
resourceSettingsSchema.patternProperties[this.overridePropertyPattern] = patternProperties;
|
||||
}
|
||||
|
||||
private update(configuration: IConfigurationNode): void {
|
||||
@@ -256,11 +267,6 @@ class ConfigurationRegistry implements IConfigurationRegistry {
|
||||
if (properties[key].overridable) {
|
||||
this.editorConfigurationSchema.properties[key] = this.getConfigurationProperties()[key];
|
||||
}
|
||||
switch (properties[key].scope) {
|
||||
case ConfigurationScope.RESOURCE:
|
||||
this.resourceConfigurationSchema.properties[key] = this.getConfigurationProperties()[key];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let subNodes = configuration.allOf;
|
||||
@@ -311,3 +317,8 @@ export function validateProperty(property: string): string {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getScopes(keys: string[]): ConfigurationScope[] {
|
||||
const configurationProperties = configurationRegistry.getConfigurationProperties();
|
||||
return keys.map(key => configurationProperties[key].scope);
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import * as json from 'vs/base/common/json';
|
||||
import { IConfigurationRegistry, Extensions, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { ConfigurationModel, IOverrides } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
export function getDefaultValues(): any {
|
||||
const valueTreeRoot: any = Object.create(null);
|
||||
const properties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
|
||||
|
||||
for (let key in properties) {
|
||||
let value = properties[key].default;
|
||||
addToValueTree(valueTreeRoot, key, value, message => console.error(`Conflict in default settings: ${message}`));
|
||||
}
|
||||
|
||||
return valueTreeRoot;
|
||||
}
|
||||
|
||||
export function toValuesTree(properties: { [qualifiedKey: string]: any }, conflictReporter: (message: string) => void): any {
|
||||
const root = Object.create(null);
|
||||
|
||||
for (let key in properties) {
|
||||
addToValueTree(root, key, properties[key], conflictReporter);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
function addToValueTree(settingsTreeRoot: any, key: string, value: any, conflictReporter: (message: string) => void): void {
|
||||
const segments = key.split('.');
|
||||
const last = segments.pop();
|
||||
|
||||
let curr = settingsTreeRoot;
|
||||
for (let i = 0; i < segments.length; i++) {
|
||||
let s = segments[i];
|
||||
let obj = curr[s];
|
||||
switch (typeof obj) {
|
||||
case 'undefined':
|
||||
obj = curr[s] = Object.create(null);
|
||||
break;
|
||||
case 'object':
|
||||
break;
|
||||
default:
|
||||
conflictReporter(`Ignoring ${key} as ${segments.slice(0, i + 1).join('.')} is ${JSON.stringify(obj)}`);
|
||||
return;
|
||||
}
|
||||
curr = obj;
|
||||
};
|
||||
|
||||
if (typeof curr === 'object') {
|
||||
curr[last] = value; // workaround https://github.com/Microsoft/vscode/issues/13606
|
||||
} else {
|
||||
conflictReporter(`Ignoring ${key} as ${segments.join('.')} is ${JSON.stringify(curr)}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function getConfigurationKeys(): string[] {
|
||||
const properties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
|
||||
|
||||
return Object.keys(properties);
|
||||
}
|
||||
|
||||
export class DefaultConfigurationModel<T> extends ConfigurationModel<T> {
|
||||
|
||||
constructor() {
|
||||
super(getDefaultValues());
|
||||
this._keys = getConfigurationKeys();
|
||||
this._overrides = Object.keys(this._contents)
|
||||
.filter(key => OVERRIDE_PROPERTY_PATTERN.test(key))
|
||||
.map(key => {
|
||||
return <IOverrides<any>>{
|
||||
identifiers: [overrideIdentifierFromKey(key).trim()],
|
||||
contents: toValuesTree(this._contents[key], message => console.error(`Conflict in default settings file: ${message}`))
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
public get keys(): string[] {
|
||||
return this._keys;
|
||||
}
|
||||
}
|
||||
|
||||
interface Overrides<T> extends IOverrides<T> {
|
||||
raw: any;
|
||||
}
|
||||
|
||||
export class CustomConfigurationModel<T> extends ConfigurationModel<T> {
|
||||
|
||||
protected _parseErrors: any[] = [];
|
||||
|
||||
constructor(content: string = '', private name: string = '') {
|
||||
super();
|
||||
if (content) {
|
||||
this.update(content);
|
||||
}
|
||||
}
|
||||
|
||||
public get errors(): any[] {
|
||||
return this._parseErrors;
|
||||
}
|
||||
|
||||
public update(content: string): void {
|
||||
let parsed: T = <T>{};
|
||||
let overrides: Overrides<T>[] = [];
|
||||
let currentProperty: string = null;
|
||||
let currentParent: any = [];
|
||||
let previousParents: any[] = [];
|
||||
let parseErrors: json.ParseError[] = [];
|
||||
|
||||
function onValue(value: any) {
|
||||
if (Array.isArray(currentParent)) {
|
||||
(<any[]>currentParent).push(value);
|
||||
} else if (currentProperty) {
|
||||
currentParent[currentProperty] = value;
|
||||
}
|
||||
if (OVERRIDE_PROPERTY_PATTERN.test(currentProperty)) {
|
||||
onOverrideSettingsValue(currentProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
function onOverrideSettingsValue(property: string, value: any): void {
|
||||
overrides.push({
|
||||
identifiers: [overrideIdentifierFromKey(property).trim()],
|
||||
raw: value,
|
||||
contents: null
|
||||
});
|
||||
}
|
||||
|
||||
let visitor: json.JSONVisitor = {
|
||||
onObjectBegin: () => {
|
||||
let object = {};
|
||||
onValue(object);
|
||||
previousParents.push(currentParent);
|
||||
currentParent = object;
|
||||
currentProperty = null;
|
||||
},
|
||||
onObjectProperty: (name: string) => {
|
||||
currentProperty = name;
|
||||
},
|
||||
onObjectEnd: () => {
|
||||
currentParent = previousParents.pop();
|
||||
},
|
||||
onArrayBegin: () => {
|
||||
let array = [];
|
||||
onValue(array);
|
||||
previousParents.push(currentParent);
|
||||
currentParent = array;
|
||||
currentProperty = null;
|
||||
},
|
||||
onArrayEnd: () => {
|
||||
currentParent = previousParents.pop();
|
||||
},
|
||||
onLiteralValue: onValue,
|
||||
onError: (error: json.ParseErrorCode) => {
|
||||
parseErrors.push({ error: error });
|
||||
}
|
||||
};
|
||||
if (content) {
|
||||
try {
|
||||
json.visit(content, visitor);
|
||||
parsed = currentParent[0] || {};
|
||||
} catch (e) {
|
||||
console.error(`Error while parsing settings file ${this.name}: ${e}`);
|
||||
this._parseErrors = [e];
|
||||
}
|
||||
}
|
||||
this.processRaw(parsed);
|
||||
|
||||
const configurationProperties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
|
||||
this._overrides = overrides.map<IOverrides<T>>(override => {
|
||||
// Filter unknown and non-overridable properties
|
||||
const raw = {};
|
||||
for (const key in override.raw) {
|
||||
if (configurationProperties[key] && configurationProperties[key].overridable) {
|
||||
raw[key] = override.raw[key];
|
||||
}
|
||||
}
|
||||
return {
|
||||
identifiers: override.identifiers,
|
||||
contents: <T>toValuesTree(raw, message => console.error(`Conflict in settings file ${this.name}: ${message}`))
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
protected processRaw(raw: T): void {
|
||||
this._contents = toValuesTree(raw, message => console.error(`Conflict in settings file ${this.name}: ${message}`));
|
||||
this._keys = Object.keys(raw);
|
||||
}
|
||||
}
|
||||
|
||||
export function overrideIdentifierFromKey(key: string): string {
|
||||
return key.substring(1, key.length - 1);
|
||||
}
|
||||
|
||||
export function keyFromOverrideIdentifier(overrideIdentifier: string): string {
|
||||
return `[${overrideIdentifier}]`;
|
||||
}
|
||||
@@ -4,26 +4,28 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { ConfigWatcher } from 'vs/base/node/config';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IDisposable, toDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { ConfigurationSource, IConfigurationService, IConfigurationServiceEvent, IConfigurationValue, IConfigurationKeys, ConfigurationModel, IConfigurationOverrides, Configuration, IConfigurationValues, IConfigurationData } from 'vs/platform/configuration/common/configuration';
|
||||
import { CustomConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/model';
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IConfigurationService, IConfigurationChangeEvent, IConfigurationOverrides, ConfigurationTarget, compare, isConfigurationOverrides, IConfigurationData } from 'vs/platform/configuration/common/configuration';
|
||||
import { CustomConfigurationModel, DefaultConfigurationModel, ConfigurationModel, Configuration, ConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { equals } from 'vs/base/common/objects';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
|
||||
export class ConfigurationService<T> extends Disposable implements IConfigurationService, IDisposable {
|
||||
export class ConfigurationService extends Disposable implements IConfigurationService, IDisposable {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
private _configuration: Configuration<T>;
|
||||
private userConfigModelWatcher: ConfigWatcher<ConfigurationModel<T>>;
|
||||
private _configuration: Configuration;
|
||||
private userConfigModelWatcher: ConfigWatcher<ConfigurationModel>;
|
||||
|
||||
private _onDidUpdateConfiguration: Emitter<IConfigurationServiceEvent> = this._register(new Emitter<IConfigurationServiceEvent>());
|
||||
public readonly onDidUpdateConfiguration: Event<IConfigurationServiceEvent> = this._onDidUpdateConfiguration.event;
|
||||
private _onDidChangeConfiguration: Emitter<IConfigurationChangeEvent> = this._register(new Emitter<IConfigurationChangeEvent>());
|
||||
readonly onDidChangeConfiguration: Event<IConfigurationChangeEvent> = this._onDidChangeConfiguration.event;
|
||||
|
||||
constructor(
|
||||
@IEnvironmentService environmentService: IEnvironmentService
|
||||
@@ -31,70 +33,111 @@ export class ConfigurationService<T> extends Disposable implements IConfiguratio
|
||||
super();
|
||||
|
||||
this.userConfigModelWatcher = new ConfigWatcher(environmentService.appSettingsPath, {
|
||||
changeBufferDelay: 300, onError: error => onUnexpectedError(error), defaultConfig: new CustomConfigurationModel<T>(null, environmentService.appSettingsPath), parse: (content: string, parseErrors: any[]) => {
|
||||
const userConfigModel = new CustomConfigurationModel<T>(content, environmentService.appSettingsPath);
|
||||
changeBufferDelay: 300, onError: error => onUnexpectedError(error), defaultConfig: new CustomConfigurationModel(null, environmentService.appSettingsPath), parse: (content: string, parseErrors: any[]) => {
|
||||
const userConfigModel = new CustomConfigurationModel(content, environmentService.appSettingsPath);
|
||||
parseErrors = [...userConfigModel.errors];
|
||||
return userConfigModel;
|
||||
}
|
||||
});
|
||||
this._register(toDisposable(() => this.userConfigModelWatcher.dispose()));
|
||||
this._register(this.userConfigModelWatcher);
|
||||
|
||||
this.reset();
|
||||
|
||||
// Listeners
|
||||
this._register(this.userConfigModelWatcher.onDidUpdateConfiguration(() => this.onConfigurationChange(ConfigurationSource.User)));
|
||||
this._register(Registry.as<IConfigurationRegistry>(Extensions.Configuration).onDidRegisterConfiguration(() => this.onConfigurationChange(ConfigurationSource.Default)));
|
||||
this._register(this.userConfigModelWatcher.onDidUpdateConfiguration(() => this.onDidUpdateConfigModel()));
|
||||
this._register(Registry.as<IConfigurationRegistry>(Extensions.Configuration).onDidRegisterConfiguration(configurationProperties => this.onDidRegisterConfiguration(configurationProperties)));
|
||||
}
|
||||
|
||||
public configuration(): Configuration<any> {
|
||||
return this._configuration || (this._configuration = this.consolidateConfigurations());
|
||||
get configuration(): Configuration {
|
||||
return this._configuration;
|
||||
}
|
||||
|
||||
private onConfigurationChange(source: ConfigurationSource): void {
|
||||
getConfigurationData(): IConfigurationData {
|
||||
return this.configuration.toData();
|
||||
}
|
||||
|
||||
getConfiguration<T>(): T
|
||||
getConfiguration<T>(section: string): T
|
||||
getConfiguration<T>(overrides: IConfigurationOverrides): T
|
||||
getConfiguration<T>(section: string, overrides: IConfigurationOverrides): T
|
||||
getConfiguration(arg1?: any, arg2?: any): any {
|
||||
const section = typeof arg1 === 'string' ? arg1 : void 0;
|
||||
const overrides = isConfigurationOverrides(arg1) ? arg1 : isConfigurationOverrides(arg2) ? arg2 : {};
|
||||
return this.configuration.getSection(section, overrides, null);
|
||||
}
|
||||
|
||||
getValue(key: string, overrides: IConfigurationOverrides = {}): any {
|
||||
return this.configuration.getValue(key, overrides, null);
|
||||
}
|
||||
|
||||
updateValue(key: string, value: any): TPromise<void>
|
||||
updateValue(key: string, value: any, overrides: IConfigurationOverrides): TPromise<void>
|
||||
updateValue(key: string, value: any, target: ConfigurationTarget): TPromise<void>
|
||||
updateValue(key: string, value: any, overrides: IConfigurationOverrides, target: ConfigurationTarget): TPromise<void>
|
||||
updateValue(key: string, value: any, arg3?: any, arg4?: any): TPromise<void> {
|
||||
return TPromise.wrapError(new Error('not supported'));
|
||||
}
|
||||
|
||||
inspect<T>(key: string): {
|
||||
default: T,
|
||||
user: T,
|
||||
workspace: T,
|
||||
workspaceFolder: T
|
||||
value: T
|
||||
} {
|
||||
return this.configuration.lookup<T>(key, {}, null);
|
||||
}
|
||||
|
||||
keys(): {
|
||||
default: string[];
|
||||
user: string[];
|
||||
workspace: string[];
|
||||
workspaceFolder: string[];
|
||||
} {
|
||||
return this.configuration.keys(null);
|
||||
}
|
||||
|
||||
reloadConfiguration(folder?: IWorkspaceFolder): TPromise<void> {
|
||||
return folder ? TPromise.as(null) :
|
||||
new TPromise((c, e) => this.userConfigModelWatcher.reload(() => c(this.onDidUpdateConfigModel())));
|
||||
}
|
||||
|
||||
private onDidUpdateConfigModel(): void {
|
||||
let changedKeys = [];
|
||||
const { added, updated, removed } = compare(this._configuration.user, this.userConfigModelWatcher.getConfig());
|
||||
changedKeys = [...added, ...updated, ...removed];
|
||||
if (changedKeys.length) {
|
||||
const oldConfiguartion = this._configuration;
|
||||
this.reset();
|
||||
changedKeys = changedKeys.filter(key => !equals(oldConfiguartion.getValue(key, {}, null), this._configuration.getValue(key, {}, null)));
|
||||
if (changedKeys.length) {
|
||||
this.trigger(changedKeys, ConfigurationTarget.USER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private onDidRegisterConfiguration(keys: string[]): void {
|
||||
this.reset(); // reset our caches
|
||||
|
||||
const cache = this.configuration();
|
||||
|
||||
this._onDidUpdateConfiguration.fire({
|
||||
source,
|
||||
sourceConfig: source === ConfigurationSource.Default ? cache.defaults.contents : cache.user.contents
|
||||
});
|
||||
}
|
||||
|
||||
public reloadConfiguration<C>(section?: string): TPromise<C> {
|
||||
return new TPromise<C>(c => {
|
||||
this.userConfigModelWatcher.reload(() => {
|
||||
this.reset(); // reset our caches
|
||||
c(this.getConfiguration<C>(section));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public getConfiguration<C>(section?: string, options?: IConfigurationOverrides): C {
|
||||
return this.configuration().getValue<C>(section, options);
|
||||
}
|
||||
|
||||
public lookup<C>(key: string, overrides?: IConfigurationOverrides): IConfigurationValue<C> {
|
||||
return this.configuration().lookup<C>(key, overrides);
|
||||
}
|
||||
|
||||
public keys(overrides?: IConfigurationOverrides): IConfigurationKeys {
|
||||
return this.configuration().keys(overrides);
|
||||
}
|
||||
|
||||
public values<V>(): IConfigurationValues {
|
||||
return this._configuration.values();
|
||||
}
|
||||
|
||||
public getConfigurationData<T2>(): IConfigurationData<T2> {
|
||||
return this.configuration().toData();
|
||||
this.trigger(keys, ConfigurationTarget.DEFAULT);
|
||||
}
|
||||
|
||||
private reset(): void {
|
||||
this._configuration = this.consolidateConfigurations();
|
||||
const defaults = new DefaultConfigurationModel();
|
||||
const user = this.userConfigModelWatcher.getConfig();
|
||||
this._configuration = new Configuration(defaults, user);
|
||||
}
|
||||
|
||||
private consolidateConfigurations(): Configuration<T> {
|
||||
const defaults = new DefaultConfigurationModel<T>();
|
||||
const user = this.userConfigModelWatcher.getConfig();
|
||||
return new Configuration(defaults, user);
|
||||
private trigger(keys: string[], source: ConfigurationTarget): void {
|
||||
this._onDidChangeConfiguration.fire(new ConfigurationChangeEvent().change(keys).telemetryData(source, this.getTargetConfiguration(source)));
|
||||
}
|
||||
|
||||
private getTargetConfiguration(target: ConfigurationTarget): any {
|
||||
switch (target) {
|
||||
case ConfigurationTarget.DEFAULT:
|
||||
return this._configuration.defaults.contents;
|
||||
case ConfigurationTarget.USER:
|
||||
return this._configuration.user.contents;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -5,29 +5,10 @@
|
||||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { ConfigurationModel, merge } from 'vs/platform/configuration/common/configuration';
|
||||
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { merge, removeFromValueTree } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
suite('Configuration', () => {
|
||||
|
||||
suiteSetup(() => {
|
||||
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
|
||||
'id': 'a',
|
||||
'order': 1,
|
||||
'title': 'a',
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'a': {
|
||||
'description': 'a',
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'overridable': true
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('simple merge', () => {
|
||||
let base = { 'a': 1, 'b': 2 };
|
||||
merge(base, { 'a': 3, 'c': 4 }, true);
|
||||
@@ -37,43 +18,92 @@ suite('Configuration', () => {
|
||||
assert.deepEqual(base, { 'a': 1, 'b': 2, 'c': 4 });
|
||||
});
|
||||
|
||||
test('Recursive merge', () => {
|
||||
const base = { 'a': { 'b': 1 } };
|
||||
merge(base, { 'a': { 'b': 2 } }, true);
|
||||
assert.deepEqual(base, { 'a': { 'b': 2 } });
|
||||
test('removeFromValueTree: remove a non existing key', () => {
|
||||
let target = { 'a': { 'b': 2 } };
|
||||
|
||||
removeFromValueTree(target, 'c');
|
||||
|
||||
assert.deepEqual(target, { 'a': { 'b': 2 } });
|
||||
});
|
||||
|
||||
test('simple merge using configuration', () => {
|
||||
let base = new ConfigurationModel<any>({ 'a': 1, 'b': 2 });
|
||||
let add = new ConfigurationModel<any>({ 'a': 3, 'c': 4 });
|
||||
let result = base.merge(add);
|
||||
assert.deepEqual(result.contents, { 'a': 3, 'b': 2, 'c': 4 });
|
||||
test('removeFromValueTree: remove a multi segmented key from an object that has only sub sections of the key', () => {
|
||||
let target = { 'a': { 'b': 2 } };
|
||||
|
||||
removeFromValueTree(target, 'a.b.c');
|
||||
|
||||
assert.deepEqual(target, { 'a': { 'b': 2 } });
|
||||
});
|
||||
|
||||
test('Recursive merge using config models', () => {
|
||||
let base = new ConfigurationModel({ 'a': { 'b': 1 } });
|
||||
let add = new ConfigurationModel({ 'a': { 'b': 2 } });
|
||||
let result = base.merge(add);
|
||||
assert.deepEqual(result.contents, { 'a': { 'b': 2 } });
|
||||
test('removeFromValueTree: remove a single segemented key', () => {
|
||||
let target = { 'a': 1 };
|
||||
|
||||
removeFromValueTree(target, 'a');
|
||||
|
||||
assert.deepEqual(target, {});
|
||||
});
|
||||
|
||||
test('Test contents while getting an existing property', () => {
|
||||
let testObject = new ConfigurationModel({ 'a': 1 });
|
||||
assert.deepEqual(testObject.getContentsFor('a'), 1);
|
||||
test('removeFromValueTree: remove a single segemented key when its value is undefined', () => {
|
||||
let target = { 'a': void 0 };
|
||||
|
||||
testObject = new ConfigurationModel<any>({ 'a': { 'b': 1 } });
|
||||
assert.deepEqual(testObject.getContentsFor('a'), { 'b': 1 });
|
||||
removeFromValueTree(target, 'a');
|
||||
|
||||
assert.deepEqual(target, {});
|
||||
});
|
||||
|
||||
test('Test contents are undefined for non existing properties', () => {
|
||||
const testObject = new ConfigurationModel({ awesome: true });
|
||||
test('removeFromValueTree: remove a multi segemented key when its value is undefined', () => {
|
||||
let target = { 'a': { 'b': 1 } };
|
||||
|
||||
assert.deepEqual(testObject.getContentsFor('unknownproperty'), undefined);
|
||||
removeFromValueTree(target, 'a.b');
|
||||
|
||||
assert.deepEqual(target, {});
|
||||
});
|
||||
|
||||
test('Test override gives all content merged with overrides', () => {
|
||||
const testObject = new ConfigurationModel<any>({ 'a': 1, 'c': 1 }, [], [{ identifiers: ['b'], contents: { 'a': 2 } }]);
|
||||
test('removeFromValueTree: remove a multi segemented key when its value is array', () => {
|
||||
let target = { 'a': { 'b': [1] } };
|
||||
|
||||
assert.deepEqual(testObject.override('b').contents, { 'a': 2, 'c': 1 });
|
||||
removeFromValueTree(target, 'a.b');
|
||||
|
||||
assert.deepEqual(target, {});
|
||||
});
|
||||
|
||||
test('removeFromValueTree: remove a multi segemented key first segment value is array', () => {
|
||||
let target = { 'a': [1] };
|
||||
|
||||
removeFromValueTree(target, 'a.0');
|
||||
|
||||
assert.deepEqual(target, { 'a': [1] });
|
||||
});
|
||||
|
||||
test('removeFromValueTree: remove when key is the first segmenet', () => {
|
||||
let target = { 'a': { 'b': 1 } };
|
||||
|
||||
removeFromValueTree(target, 'a');
|
||||
|
||||
assert.deepEqual(target, {});
|
||||
});
|
||||
|
||||
test('removeFromValueTree: remove a multi segemented key when the first node has more values', () => {
|
||||
let target = { 'a': { 'b': { 'c': 1 }, 'd': 1 } };
|
||||
|
||||
removeFromValueTree(target, 'a.b.c');
|
||||
|
||||
assert.deepEqual(target, { 'a': { 'd': 1 } });
|
||||
});
|
||||
|
||||
test('removeFromValueTree: remove a multi segemented key when in between node has more values', () => {
|
||||
let target = { 'a': { 'b': { 'c': { 'd': 1 }, 'd': 1 } } };
|
||||
|
||||
removeFromValueTree(target, 'a.b.c.d');
|
||||
|
||||
assert.deepEqual(target, { 'a': { 'b': { 'd': 1 } } });
|
||||
});
|
||||
|
||||
test('removeFromValueTree: remove a multi segemented key when the last but one node has more values', () => {
|
||||
let target = { 'a': { 'b': { 'c': 1, 'd': 1 } } };
|
||||
|
||||
removeFromValueTree(target, 'a.b.c');
|
||||
|
||||
assert.deepEqual(target, { 'a': { 'b': { 'd': 1 } } });
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,550 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { ConfigurationModel, CustomConfigurationModel, DefaultConfigurationModel, ConfigurationChangeEvent, AllKeysConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels';
|
||||
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
suite('ConfigurationModel', () => {
|
||||
|
||||
test('setValue for a key that has no sections and not defined', () => {
|
||||
let testObject = new ConfigurationModel({ 'a': { 'b': 1 } }, ['a.b']);
|
||||
|
||||
testObject.setValue('f', 1);
|
||||
|
||||
assert.deepEqual(testObject.contents, { 'a': { 'b': 1 }, 'f': 1 });
|
||||
assert.deepEqual(testObject.keys, ['a.b', 'f']);
|
||||
});
|
||||
|
||||
test('setValue for a key that has no sections and defined', () => {
|
||||
let testObject = new ConfigurationModel({ 'a': { 'b': 1 }, 'f': 1 }, ['a.b', 'f']);
|
||||
|
||||
testObject.setValue('f', 3);
|
||||
|
||||
assert.deepEqual(testObject.contents, { 'a': { 'b': 1 }, 'f': 3 });
|
||||
assert.deepEqual(testObject.keys, ['a.b', 'f']);
|
||||
});
|
||||
|
||||
test('setValue for a key that has sections and not defined', () => {
|
||||
let testObject = new ConfigurationModel({ 'a': { 'b': 1 }, 'f': 1 }, ['a.b', 'f']);
|
||||
|
||||
testObject.setValue('b.c', 1);
|
||||
|
||||
assert.deepEqual(testObject.contents, { 'a': { 'b': 1 }, 'b': { 'c': 1 }, 'f': 1 });
|
||||
assert.deepEqual(testObject.keys, ['a.b', 'f', 'b.c']);
|
||||
});
|
||||
|
||||
test('setValue for a key that has sections and defined', () => {
|
||||
let testObject = new ConfigurationModel({ 'a': { 'b': 1 }, 'b': { 'c': 1 }, 'f': 1 }, ['a.b', 'b.c', 'f']);
|
||||
|
||||
testObject.setValue('b.c', 3);
|
||||
|
||||
assert.deepEqual(testObject.contents, { 'a': { 'b': 1 }, 'b': { 'c': 3 }, 'f': 1 });
|
||||
assert.deepEqual(testObject.keys, ['a.b', 'b.c', 'f']);
|
||||
});
|
||||
|
||||
test('setValue for a key that has sections and sub section not defined', () => {
|
||||
let testObject = new ConfigurationModel({ 'a': { 'b': 1 }, 'f': 1 }, ['a.b', 'f']);
|
||||
|
||||
testObject.setValue('a.c', 1);
|
||||
|
||||
assert.deepEqual(testObject.contents, { 'a': { 'b': 1, 'c': 1 }, 'f': 1 });
|
||||
assert.deepEqual(testObject.keys, ['a.b', 'f', 'a.c']);
|
||||
});
|
||||
|
||||
test('setValue for a key that has sections and sub section defined', () => {
|
||||
let testObject = new ConfigurationModel({ 'a': { 'b': 1, 'c': 1 }, 'f': 1 }, ['a.b', 'a.c', 'f']);
|
||||
|
||||
testObject.setValue('a.c', 3);
|
||||
|
||||
assert.deepEqual(testObject.contents, { 'a': { 'b': 1, 'c': 3 }, 'f': 1 });
|
||||
assert.deepEqual(testObject.keys, ['a.b', 'a.c', 'f']);
|
||||
});
|
||||
|
||||
test('setValue for a key that has sections and last section is added', () => {
|
||||
let testObject = new ConfigurationModel({ 'a': { 'b': {} }, 'f': 1 }, ['a.b', 'f']);
|
||||
|
||||
testObject.setValue('a.b.c', 1);
|
||||
|
||||
assert.deepEqual(testObject.contents, { 'a': { 'b': { 'c': 1 } }, 'f': 1 });
|
||||
assert.deepEqual(testObject.keys, ['a.b.c', 'f']);
|
||||
});
|
||||
|
||||
test('removeValue: remove a non existing key', () => {
|
||||
let testObject = new ConfigurationModel({ 'a': { 'b': 2 } }, ['a.b']);
|
||||
|
||||
testObject.removeValue('a.b.c');
|
||||
|
||||
assert.deepEqual(testObject.contents, { 'a': { 'b': 2 } });
|
||||
assert.deepEqual(testObject.keys, ['a.b']);
|
||||
});
|
||||
|
||||
test('removeValue: remove a single segemented key', () => {
|
||||
let testObject = new ConfigurationModel({ 'a': 1 }, ['a']);
|
||||
|
||||
testObject.removeValue('a');
|
||||
|
||||
assert.deepEqual(testObject.contents, {});
|
||||
assert.deepEqual(testObject.keys, []);
|
||||
});
|
||||
|
||||
test('removeValue: remove a multi segemented key', () => {
|
||||
let testObject = new ConfigurationModel({ 'a': { 'b': 1 } }, ['a.b']);
|
||||
|
||||
testObject.removeValue('a.b');
|
||||
|
||||
assert.deepEqual(testObject.contents, {});
|
||||
assert.deepEqual(testObject.keys, []);
|
||||
});
|
||||
|
||||
test('setValueInOverrides adds to overrides if does not exist', () => {
|
||||
let testObject = new ConfigurationModel({ 'a': 1, 'b': 1 }, ['a']);
|
||||
|
||||
testObject.setValueInOverrides('or', 'a', 2);
|
||||
|
||||
assert.deepEqual(testObject.overrides[0].contents, { 'a': 2 });
|
||||
assert.deepEqual(testObject.override('or').contents, { 'a': 2, 'b': 1 });
|
||||
});
|
||||
|
||||
test('setValueInOverrides adds to overrides if exist', () => {
|
||||
let testObject = new ConfigurationModel({ 'a': 1, 'b': 1 }, ['a'], [{ identifiers: ['or'], contents: { 'a': 2 } }]);
|
||||
|
||||
testObject.setValueInOverrides('or', 'a', 3);
|
||||
|
||||
assert.deepEqual(testObject.overrides[0].contents, { 'a': 3 });
|
||||
assert.deepEqual(testObject.override('or').contents, { 'a': 3, 'b': 1 });
|
||||
});
|
||||
|
||||
test('setValueInOverrides adds a nested key to overrides if exist', () => {
|
||||
let testObject = new ConfigurationModel({ 'a': 1, 'b': 1 }, ['a'], [{ identifiers: ['or'], contents: { 'a': { 'c': 1 } } }]);
|
||||
|
||||
testObject.setValueInOverrides('or', 'a.c', 2);
|
||||
|
||||
assert.deepEqual(testObject.overrides[0].contents, { 'a': { 'c': 2 } });
|
||||
assert.deepEqual(testObject.override('or').contents, { 'a': { 'c': 2 }, 'b': 1 });
|
||||
});
|
||||
|
||||
test('setValueInOverrides adds new overrides if exist', () => {
|
||||
let testObject = new ConfigurationModel({ 'a': 1, 'b': 1 }, ['a'], [{ identifiers: ['or1'], contents: { 'a': 2 } }]);
|
||||
|
||||
testObject.setValueInOverrides('or2', 'b', 2);
|
||||
|
||||
assert.deepEqual(testObject.overrides[0].contents, { 'a': 2 });
|
||||
assert.deepEqual(testObject.overrides[1].contents, { 'b': 2 });
|
||||
assert.deepEqual(testObject.override('or1').contents, { 'a': 2, 'b': 1 });
|
||||
assert.deepEqual(testObject.override('or2').contents, { 'a': 1, 'b': 2 });
|
||||
});
|
||||
|
||||
test('get overriding configuration model for an existing identifier', () => {
|
||||
let testObject = new ConfigurationModel(
|
||||
{ 'a': { 'b': 1 }, 'f': 1 }, [],
|
||||
[{ identifiers: ['c'], contents: { 'a': { 'd': 1 } } }]);
|
||||
|
||||
assert.deepEqual(testObject.override('c').contents, { 'a': { 'b': 1, 'd': 1 }, 'f': 1 });
|
||||
});
|
||||
|
||||
test('get overriding configuration model for an identifier that does not exist', () => {
|
||||
let testObject = new ConfigurationModel(
|
||||
{ 'a': { 'b': 1 }, 'f': 1 }, [],
|
||||
[{ identifiers: ['c'], contents: { 'a': { 'd': 1 } } }]);
|
||||
|
||||
assert.deepEqual(testObject.override('xyz').contents, { 'a': { 'b': 1 }, 'f': 1 });
|
||||
});
|
||||
|
||||
test('get overriding configuration when one of the keys does not exist in base', () => {
|
||||
let testObject = new ConfigurationModel(
|
||||
{ 'a': { 'b': 1 }, 'f': 1 }, [],
|
||||
[{ identifiers: ['c'], contents: { 'a': { 'd': 1 }, 'g': 1 } }]);
|
||||
|
||||
assert.deepEqual(testObject.override('c').contents, { 'a': { 'b': 1, 'd': 1 }, 'f': 1, 'g': 1 });
|
||||
});
|
||||
|
||||
test('get overriding configuration when one of the key in base is not of object type', () => {
|
||||
let testObject = new ConfigurationModel(
|
||||
{ 'a': { 'b': 1 }, 'f': 1 }, [],
|
||||
[{ identifiers: ['c'], contents: { 'a': { 'd': 1 }, 'f': { 'g': 1 } } }]);
|
||||
|
||||
assert.deepEqual(testObject.override('c').contents, { 'a': { 'b': 1, 'd': 1 }, 'f': { 'g': 1 } });
|
||||
});
|
||||
|
||||
test('get overriding configuration when one of the key in overriding contents is not of object type', () => {
|
||||
let testObject = new ConfigurationModel(
|
||||
{ 'a': { 'b': 1 }, 'f': { 'g': 1 } }, [],
|
||||
[{ identifiers: ['c'], contents: { 'a': { 'd': 1 }, 'f': 1 } }]);
|
||||
|
||||
assert.deepEqual(testObject.override('c').contents, { 'a': { 'b': 1, 'd': 1 }, 'f': 1 });
|
||||
});
|
||||
|
||||
test('get overriding configuration if the value of overriding identifier is not object', () => {
|
||||
let testObject = new ConfigurationModel(
|
||||
{ 'a': { 'b': 1 }, 'f': { 'g': 1 } }, [],
|
||||
[{ identifiers: ['c'], contents: 'abc' }]);
|
||||
|
||||
assert.deepEqual(testObject.override('c').contents, { 'a': { 'b': 1 }, 'f': { 'g': 1 } });
|
||||
});
|
||||
|
||||
test('get overriding configuration if the value of overriding identifier is an empty object', () => {
|
||||
let testObject = new ConfigurationModel(
|
||||
{ 'a': { 'b': 1 }, 'f': { 'g': 1 } }, [],
|
||||
[{ identifiers: ['c'], contents: {} }]);
|
||||
|
||||
assert.deepEqual(testObject.override('c').contents, { 'a': { 'b': 1 }, 'f': { 'g': 1 } });
|
||||
});
|
||||
|
||||
test('simple merge', () => {
|
||||
let base = new ConfigurationModel({ 'a': 1, 'b': 2 }, ['a', 'b']);
|
||||
let add = new ConfigurationModel({ 'a': 3, 'c': 4 }, ['a', 'c']);
|
||||
let result = base.merge(add);
|
||||
|
||||
assert.deepEqual(result.contents, { 'a': 3, 'b': 2, 'c': 4 });
|
||||
assert.deepEqual(result.keys, ['a', 'b', 'c']);
|
||||
});
|
||||
|
||||
test('recursive merge', () => {
|
||||
let base = new ConfigurationModel({ 'a': { 'b': 1 } }, ['a.b']);
|
||||
let add = new ConfigurationModel({ 'a': { 'b': 2 } }, ['a.b']);
|
||||
let result = base.merge(add);
|
||||
|
||||
assert.deepEqual(result.contents, { 'a': { 'b': 2 } });
|
||||
assert.deepEqual(result.getSectionContents('a'), { 'b': 2 });
|
||||
assert.deepEqual(result.keys, ['a.b']);
|
||||
});
|
||||
|
||||
test('simple merge overrides', () => {
|
||||
let base = new ConfigurationModel({ 'a': { 'b': 1 } }, ['a.b'], [{ identifiers: ['c'], contents: { 'a': 2 } }]);
|
||||
let add = new ConfigurationModel({ 'a': { 'b': 2 } }, ['a.b'], [{ identifiers: ['c'], contents: { 'b': 2 } }]);
|
||||
let result = base.merge(add);
|
||||
|
||||
assert.deepEqual(result.contents, { 'a': { 'b': 2 } });
|
||||
assert.deepEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': 2, 'b': 2 } }]);
|
||||
assert.deepEqual(result.override('c').contents, { 'a': 2, 'b': 2 });
|
||||
assert.deepEqual(result.keys, ['a.b']);
|
||||
});
|
||||
|
||||
test('recursive merge overrides', () => {
|
||||
let base = new ConfigurationModel({ 'a': { 'b': 1 }, 'f': 1 }, ['a.b', 'f'], [{ identifiers: ['c'], contents: { 'a': { 'd': 1 } } }]);
|
||||
let add = new ConfigurationModel({ 'a': { 'b': 2 } }, ['a.b'], [{ identifiers: ['c'], contents: { 'a': { 'e': 2 } } }]);
|
||||
let result = base.merge(add);
|
||||
|
||||
assert.deepEqual(result.contents, { 'a': { 'b': 2 }, 'f': 1 });
|
||||
assert.deepEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': { 'd': 1, 'e': 2 } } }]);
|
||||
assert.deepEqual(result.override('c').contents, { 'a': { 'b': 2, 'd': 1, 'e': 2 }, 'f': 1 });
|
||||
assert.deepEqual(result.keys, ['a.b', 'f']);
|
||||
});
|
||||
|
||||
test('Test contents while getting an existing property', () => {
|
||||
let testObject = new ConfigurationModel({ 'a': 1 });
|
||||
assert.deepEqual(testObject.getSectionContents('a'), 1);
|
||||
|
||||
testObject = new ConfigurationModel({ 'a': { 'b': 1 } });
|
||||
assert.deepEqual(testObject.getSectionContents('a'), { 'b': 1 });
|
||||
});
|
||||
|
||||
test('Test contents are undefined for non existing properties', () => {
|
||||
const testObject = new ConfigurationModel({ awesome: true });
|
||||
|
||||
assert.deepEqual(testObject.getSectionContents('unknownproperty'), undefined);
|
||||
});
|
||||
|
||||
test('Test override gives all content merged with overrides', () => {
|
||||
const testObject = new ConfigurationModel({ 'a': 1, 'c': 1 }, [], [{ identifiers: ['b'], contents: { 'a': 2 } }]);
|
||||
|
||||
assert.deepEqual(testObject.override('b').contents, { 'a': 2, 'c': 1 });
|
||||
});
|
||||
});
|
||||
|
||||
suite('CustomConfigurationModel', () => {
|
||||
|
||||
suiteSetup(() => {
|
||||
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
|
||||
'id': 'a',
|
||||
'order': 1,
|
||||
'title': 'a',
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'a': {
|
||||
'description': 'a',
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'overridable': true
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('simple merge using models', () => {
|
||||
let base = new CustomConfigurationModel(JSON.stringify({ 'a': 1, 'b': 2 }));
|
||||
let add = new CustomConfigurationModel(JSON.stringify({ 'a': 3, 'c': 4 }));
|
||||
let result = base.merge(add);
|
||||
assert.deepEqual(result.contents, { 'a': 3, 'b': 2, 'c': 4 });
|
||||
});
|
||||
|
||||
test('simple merge with an undefined contents', () => {
|
||||
let base = new CustomConfigurationModel(JSON.stringify({ 'a': 1, 'b': 2 }));
|
||||
let add = new CustomConfigurationModel(null);
|
||||
let result = base.merge(add);
|
||||
assert.deepEqual(result.contents, { 'a': 1, 'b': 2 });
|
||||
|
||||
base = new CustomConfigurationModel(null);
|
||||
add = new CustomConfigurationModel(JSON.stringify({ 'a': 1, 'b': 2 }));
|
||||
result = base.merge(add);
|
||||
assert.deepEqual(result.contents, { 'a': 1, 'b': 2 });
|
||||
|
||||
base = new CustomConfigurationModel(null);
|
||||
add = new CustomConfigurationModel(null);
|
||||
result = base.merge(add);
|
||||
assert.deepEqual(result.contents, {});
|
||||
});
|
||||
|
||||
test('Recursive merge using config models', () => {
|
||||
let base = new CustomConfigurationModel(JSON.stringify({ 'a': { 'b': 1 } }));
|
||||
let add = new CustomConfigurationModel(JSON.stringify({ 'a': { 'b': 2 } }));
|
||||
let result = base.merge(add);
|
||||
assert.deepEqual(result.contents, { 'a': { 'b': 2 } });
|
||||
});
|
||||
|
||||
test('Test contents while getting an existing property', () => {
|
||||
let testObject = new CustomConfigurationModel(JSON.stringify({ 'a': 1 }));
|
||||
assert.deepEqual(testObject.getSectionContents('a'), 1);
|
||||
|
||||
testObject = new CustomConfigurationModel(JSON.stringify({ 'a': { 'b': 1 } }));
|
||||
assert.deepEqual(testObject.getSectionContents('a'), { 'b': 1 });
|
||||
});
|
||||
|
||||
test('Test contents are undefined for non existing properties', () => {
|
||||
const testObject = new CustomConfigurationModel(JSON.stringify({
|
||||
awesome: true
|
||||
}));
|
||||
|
||||
assert.deepEqual(testObject.getSectionContents('unknownproperty'), undefined);
|
||||
});
|
||||
|
||||
test('Test contents are undefined for undefined config', () => {
|
||||
const testObject = new CustomConfigurationModel(null);
|
||||
|
||||
assert.deepEqual(testObject.getSectionContents('unknownproperty'), undefined);
|
||||
});
|
||||
|
||||
test('Test configWithOverrides gives all content merged with overrides', () => {
|
||||
const testObject = new CustomConfigurationModel(JSON.stringify({ 'a': 1, 'c': 1, '[b]': { 'a': 2 } }));
|
||||
|
||||
assert.deepEqual(testObject.override('b').contents, { 'a': 2, 'c': 1, '[b]': { 'a': 2 } });
|
||||
});
|
||||
|
||||
test('Test configWithOverrides gives empty contents', () => {
|
||||
const testObject = new CustomConfigurationModel(null);
|
||||
|
||||
assert.deepEqual(testObject.override('b').contents, {});
|
||||
});
|
||||
|
||||
test('Test update with empty data', () => {
|
||||
const testObject = new CustomConfigurationModel();
|
||||
testObject.update('');
|
||||
|
||||
assert.deepEqual(testObject.contents, {});
|
||||
assert.deepEqual(testObject.keys, []);
|
||||
|
||||
testObject.update(null);
|
||||
|
||||
assert.deepEqual(testObject.contents, {});
|
||||
assert.deepEqual(testObject.keys, []);
|
||||
|
||||
testObject.update(undefined);
|
||||
|
||||
assert.deepEqual(testObject.contents, {});
|
||||
assert.deepEqual(testObject.keys, []);
|
||||
});
|
||||
|
||||
test('Test registering the same property again', () => {
|
||||
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
|
||||
'id': 'a',
|
||||
'order': 1,
|
||||
'title': 'a',
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'a': {
|
||||
'description': 'a',
|
||||
'type': 'boolean',
|
||||
'default': false,
|
||||
}
|
||||
}
|
||||
});
|
||||
assert.equal(true, new DefaultConfigurationModel().getSectionContents('a'));
|
||||
});
|
||||
|
||||
test('Test registering the language property', () => {
|
||||
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
|
||||
'id': '[a]',
|
||||
'order': 1,
|
||||
'title': 'a',
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'[a]': {
|
||||
'description': 'a',
|
||||
'type': 'boolean',
|
||||
'default': false,
|
||||
}
|
||||
}
|
||||
});
|
||||
assert.equal(undefined, new DefaultConfigurationModel().getSectionContents('[a]'));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('ConfigurationChangeEvent', () => {
|
||||
|
||||
test('changeEvent affecting keys for all resources', () => {
|
||||
let testObject = new ConfigurationChangeEvent();
|
||||
|
||||
testObject.change(['window.zoomLevel', 'workbench.editor.enablePreview', 'files', '[markdown]']);
|
||||
|
||||
assert.deepEqual(testObject.affectedKeys, ['window.zoomLevel', 'workbench.editor.enablePreview', 'files', '[markdown]']);
|
||||
assert.ok(testObject.affectsConfiguration('window.zoomLevel'));
|
||||
assert.ok(testObject.affectsConfiguration('window'));
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview'));
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor'));
|
||||
assert.ok(testObject.affectsConfiguration('workbench'));
|
||||
assert.ok(testObject.affectsConfiguration('files'));
|
||||
assert.ok(!testObject.affectsConfiguration('files.exclude'));
|
||||
assert.ok(testObject.affectsConfiguration('[markdown]'));
|
||||
});
|
||||
|
||||
test('changeEvent affecting a root key and its children', () => {
|
||||
let testObject = new ConfigurationChangeEvent();
|
||||
|
||||
testObject.change(['launch', 'launch.version', 'tasks']);
|
||||
|
||||
assert.deepEqual(testObject.affectedKeys, ['launch.version', 'tasks']);
|
||||
assert.ok(testObject.affectsConfiguration('launch'));
|
||||
assert.ok(testObject.affectsConfiguration('launch.version'));
|
||||
assert.ok(testObject.affectsConfiguration('tasks'));
|
||||
});
|
||||
|
||||
test('changeEvent affecting keys for resources', () => {
|
||||
let testObject = new ConfigurationChangeEvent();
|
||||
|
||||
testObject.change(['window.title']);
|
||||
testObject.change(['window.zoomLevel'], URI.file('file1'));
|
||||
testObject.change(['workbench.editor.enablePreview'], URI.file('file2'));
|
||||
testObject.change(['window.restoreFullscreen'], URI.file('file1'));
|
||||
testObject.change(['window.restoreWindows'], URI.file('file2'));
|
||||
|
||||
assert.deepEqual(testObject.affectedKeys, ['window.title', 'window.zoomLevel', 'window.restoreFullscreen', 'workbench.editor.enablePreview', 'window.restoreWindows']);
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window.zoomLevel'));
|
||||
assert.ok(testObject.affectsConfiguration('window.zoomLevel', URI.file('file1')));
|
||||
assert.ok(!testObject.affectsConfiguration('window.zoomLevel', URI.file('file2')));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen'));
|
||||
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen', URI.file('file1')));
|
||||
assert.ok(!testObject.affectsConfiguration('window.restoreFullscreen', URI.file('file2')));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window.restoreWindows'));
|
||||
assert.ok(testObject.affectsConfiguration('window.restoreWindows', URI.file('file2')));
|
||||
assert.ok(!testObject.affectsConfiguration('window.restoreWindows', URI.file('file1')));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window.title'));
|
||||
assert.ok(testObject.affectsConfiguration('window.title', URI.file('file1')));
|
||||
assert.ok(testObject.affectsConfiguration('window.title', URI.file('file2')));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window'));
|
||||
assert.ok(testObject.affectsConfiguration('window', URI.file('file1')));
|
||||
assert.ok(testObject.affectsConfiguration('window', URI.file('file2')));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview'));
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview', URI.file('file2')));
|
||||
assert.ok(!testObject.affectsConfiguration('workbench.editor.enablePreview', URI.file('file1')));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor'));
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor', URI.file('file2')));
|
||||
assert.ok(!testObject.affectsConfiguration('workbench.editor', URI.file('file1')));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('workbench'));
|
||||
assert.ok(testObject.affectsConfiguration('workbench', URI.file('file2')));
|
||||
assert.ok(!testObject.affectsConfiguration('workbench', URI.file('file1')));
|
||||
|
||||
assert.ok(!testObject.affectsConfiguration('files'));
|
||||
assert.ok(!testObject.affectsConfiguration('files', URI.file('file1')));
|
||||
assert.ok(!testObject.affectsConfiguration('files', URI.file('file2')));
|
||||
});
|
||||
|
||||
test('merging change events', () => {
|
||||
let event1 = new ConfigurationChangeEvent().change(['window.zoomLevel', 'files']);
|
||||
let event2 = new ConfigurationChangeEvent().change(['window.title'], URI.file('file1')).change(['[markdown]']);
|
||||
|
||||
let actual = event1.change(event2);
|
||||
|
||||
assert.deepEqual(actual.affectedKeys, ['window.zoomLevel', 'files', '[markdown]', 'window.title']);
|
||||
|
||||
assert.ok(actual.affectsConfiguration('window.zoomLevel'));
|
||||
assert.ok(actual.affectsConfiguration('window.zoomLevel', URI.file('file1')));
|
||||
assert.ok(actual.affectsConfiguration('window.zoomLevel', URI.file('file2')));
|
||||
|
||||
assert.ok(actual.affectsConfiguration('window'));
|
||||
assert.ok(actual.affectsConfiguration('window', URI.file('file1')));
|
||||
assert.ok(actual.affectsConfiguration('window', URI.file('file2')));
|
||||
|
||||
assert.ok(actual.affectsConfiguration('files'));
|
||||
assert.ok(actual.affectsConfiguration('files', URI.file('file1')));
|
||||
assert.ok(actual.affectsConfiguration('files', URI.file('file2')));
|
||||
|
||||
assert.ok(actual.affectsConfiguration('window.title'));
|
||||
assert.ok(actual.affectsConfiguration('window.title', URI.file('file1')));
|
||||
assert.ok(!actual.affectsConfiguration('window.title', URI.file('file2')));
|
||||
|
||||
assert.ok(actual.affectsConfiguration('[markdown]'));
|
||||
assert.ok(actual.affectsConfiguration('[markdown]', URI.file('file1')));
|
||||
assert.ok(actual.affectsConfiguration('[markdown]', URI.file('file2')));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('AllKeysConfigurationChangeEvent', () => {
|
||||
|
||||
test('changeEvent affects keys for any resource', () => {
|
||||
let testObject = new AllKeysConfigurationChangeEvent(['window.title', 'window.zoomLevel', 'window.restoreFullscreen', 'workbench.editor.enablePreview', 'window.restoreWindows'], ConfigurationTarget.USER, null);
|
||||
|
||||
assert.deepEqual(testObject.affectedKeys, ['window.title', 'window.zoomLevel', 'window.restoreFullscreen', 'workbench.editor.enablePreview', 'window.restoreWindows']);
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window.zoomLevel'));
|
||||
assert.ok(testObject.affectsConfiguration('window.zoomLevel', URI.file('file1')));
|
||||
assert.ok(testObject.affectsConfiguration('window.zoomLevel', URI.file('file2')));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen'));
|
||||
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen', URI.file('file1')));
|
||||
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen', URI.file('file2')));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window.restoreWindows'));
|
||||
assert.ok(testObject.affectsConfiguration('window.restoreWindows', URI.file('file2')));
|
||||
assert.ok(testObject.affectsConfiguration('window.restoreWindows', URI.file('file1')));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window.title'));
|
||||
assert.ok(testObject.affectsConfiguration('window.title', URI.file('file1')));
|
||||
assert.ok(testObject.affectsConfiguration('window.title', URI.file('file2')));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window'));
|
||||
assert.ok(testObject.affectsConfiguration('window', URI.file('file1')));
|
||||
assert.ok(testObject.affectsConfiguration('window', URI.file('file2')));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview'));
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview', URI.file('file2')));
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview', URI.file('file1')));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor'));
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor', URI.file('file2')));
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor', URI.file('file1')));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('workbench'));
|
||||
assert.ok(testObject.affectsConfiguration('workbench', URI.file('file2')));
|
||||
assert.ok(testObject.affectsConfiguration('workbench', URI.file('file1')));
|
||||
|
||||
assert.ok(!testObject.affectsConfiguration('files'));
|
||||
assert.ok(!testObject.affectsConfiguration('files', URI.file('file1')));
|
||||
});
|
||||
});
|
||||
@@ -1,148 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { CustomConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/model';
|
||||
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
|
||||
suite('Configuration', () => {
|
||||
|
||||
suiteSetup(() => {
|
||||
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
|
||||
'id': 'a',
|
||||
'order': 1,
|
||||
'title': 'a',
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'a': {
|
||||
'description': 'a',
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'overridable': true
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('simple merge using models', () => {
|
||||
let base = new CustomConfigurationModel(JSON.stringify({ 'a': 1, 'b': 2 }));
|
||||
let add = new CustomConfigurationModel(JSON.stringify({ 'a': 3, 'c': 4 }));
|
||||
let result = base.merge(add);
|
||||
assert.deepEqual(result.contents, { 'a': 3, 'b': 2, 'c': 4 });
|
||||
});
|
||||
|
||||
test('simple merge with an undefined contents', () => {
|
||||
let base = new CustomConfigurationModel(JSON.stringify({ 'a': 1, 'b': 2 }));
|
||||
let add = new CustomConfigurationModel(null);
|
||||
let result = base.merge(add);
|
||||
assert.deepEqual(result.contents, { 'a': 1, 'b': 2 });
|
||||
|
||||
base = new CustomConfigurationModel(null);
|
||||
add = new CustomConfigurationModel(JSON.stringify({ 'a': 1, 'b': 2 }));
|
||||
result = base.merge(add);
|
||||
assert.deepEqual(result.contents, { 'a': 1, 'b': 2 });
|
||||
|
||||
base = new CustomConfigurationModel(null);
|
||||
add = new CustomConfigurationModel(null);
|
||||
result = base.merge(add);
|
||||
assert.deepEqual(result.contents, {});
|
||||
});
|
||||
|
||||
test('Recursive merge using config models', () => {
|
||||
let base = new CustomConfigurationModel(JSON.stringify({ 'a': { 'b': 1 } }));
|
||||
let add = new CustomConfigurationModel(JSON.stringify({ 'a': { 'b': 2 } }));
|
||||
let result = base.merge(add);
|
||||
assert.deepEqual(result.contents, { 'a': { 'b': 2 } });
|
||||
});
|
||||
|
||||
test('Test contents while getting an existing property', () => {
|
||||
let testObject = new CustomConfigurationModel(JSON.stringify({ 'a': 1 }));
|
||||
assert.deepEqual(testObject.getContentsFor('a'), 1);
|
||||
|
||||
testObject = new CustomConfigurationModel(JSON.stringify({ 'a': { 'b': 1 } }));
|
||||
assert.deepEqual(testObject.getContentsFor('a'), { 'b': 1 });
|
||||
});
|
||||
|
||||
test('Test contents are undefined for non existing properties', () => {
|
||||
const testObject = new CustomConfigurationModel(JSON.stringify({
|
||||
awesome: true
|
||||
}));
|
||||
|
||||
assert.deepEqual(testObject.getContentsFor('unknownproperty'), undefined);
|
||||
});
|
||||
|
||||
test('Test contents are undefined for undefined config', () => {
|
||||
const testObject = new CustomConfigurationModel(null);
|
||||
|
||||
assert.deepEqual(testObject.getContentsFor('unknownproperty'), undefined);
|
||||
});
|
||||
|
||||
test('Test configWithOverrides gives all content merged with overrides', () => {
|
||||
const testObject = new CustomConfigurationModel(JSON.stringify({ 'a': 1, 'c': 1, '[b]': { 'a': 2 } }));
|
||||
|
||||
assert.deepEqual(testObject.override('b').contents, { 'a': 2, 'c': 1, '[b]': { 'a': 2 } });
|
||||
});
|
||||
|
||||
test('Test configWithOverrides gives empty contents', () => {
|
||||
const testObject = new CustomConfigurationModel(null);
|
||||
|
||||
assert.deepEqual(testObject.override('b').contents, {});
|
||||
});
|
||||
|
||||
test('Test update with empty data', () => {
|
||||
const testObject = new CustomConfigurationModel();
|
||||
testObject.update('');
|
||||
|
||||
assert.deepEqual(testObject.contents, {});
|
||||
assert.deepEqual(testObject.keys, []);
|
||||
|
||||
testObject.update(null);
|
||||
|
||||
assert.deepEqual(testObject.contents, {});
|
||||
assert.deepEqual(testObject.keys, []);
|
||||
|
||||
testObject.update(undefined);
|
||||
|
||||
assert.deepEqual(testObject.contents, {});
|
||||
assert.deepEqual(testObject.keys, []);
|
||||
});
|
||||
|
||||
test('Test registering the same property again', () => {
|
||||
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
|
||||
'id': 'a',
|
||||
'order': 1,
|
||||
'title': 'a',
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'a': {
|
||||
'description': 'a',
|
||||
'type': 'boolean',
|
||||
'default': false,
|
||||
}
|
||||
}
|
||||
});
|
||||
assert.equal(true, new DefaultConfigurationModel().getContentsFor('a'));
|
||||
});
|
||||
|
||||
test('Test registering the language property', () => {
|
||||
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
|
||||
'id': '[a]',
|
||||
'order': 1,
|
||||
'title': 'a',
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'[a]': {
|
||||
'description': 'a',
|
||||
'type': 'boolean',
|
||||
'default': false,
|
||||
}
|
||||
}
|
||||
});
|
||||
assert.equal(undefined, new DefaultConfigurationModel().getContentsFor('[a]'));
|
||||
});
|
||||
|
||||
});
|
||||
@@ -5,25 +5,25 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import { TrieMap } from 'vs/base/common/map';
|
||||
import { TernarySearchTree } from 'vs/base/common/map';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { EventEmitter } from 'vs/base/common/eventEmitter';
|
||||
import { getConfigurationKeys } from 'vs/platform/configuration/common/model';
|
||||
import { IConfigurationOverrides, IConfigurationService, getConfigurationValue, IConfigurationValue, IConfigurationKeys, IConfigurationValues, IConfigurationData, Configuration, ConfigurationModel } from 'vs/platform/configuration/common/configuration';
|
||||
import { getConfigurationKeys, IConfigurationOverrides, IConfigurationService, getConfigurationValue, isConfigurationOverrides } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
export class TestConfigurationService extends EventEmitter implements IConfigurationService {
|
||||
public _serviceBrand: any;
|
||||
|
||||
private configuration = Object.create(null);
|
||||
|
||||
private configurationByRoot: TrieMap<any> = new TrieMap<any>();
|
||||
private configurationByRoot: TernarySearchTree<any> = TernarySearchTree.forPaths<any>();
|
||||
|
||||
public reloadConfiguration<T>(section?: string): TPromise<T> {
|
||||
public reloadConfiguration<T>(): TPromise<T> {
|
||||
return TPromise.as(this.getConfiguration());
|
||||
}
|
||||
|
||||
public getConfiguration(section?: string, overrides?: IConfigurationOverrides): any {
|
||||
public getConfiguration<C>(arg1?: any, arg2?: any): C {
|
||||
const overrides = isConfigurationOverrides(arg1) ? arg1 : isConfigurationOverrides(arg2) ? arg2 : void 0;
|
||||
if (overrides && overrides.resource) {
|
||||
const configForResource = this.configurationByRoot.findSubstr(overrides.resource.fsPath);
|
||||
return configForResource || this.configuration;
|
||||
@@ -32,15 +32,19 @@ export class TestConfigurationService extends EventEmitter implements IConfigura
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
public getConfigurationData(): IConfigurationData<any> {
|
||||
return new Configuration(new ConfigurationModel(), new ConfigurationModel(this.configuration)).toData();
|
||||
public getValue(key: string, overrides?: IConfigurationOverrides): any {
|
||||
return this.inspect(key).value;
|
||||
}
|
||||
|
||||
public updateValue(key: string, overrides?: IConfigurationOverrides): TPromise<void> {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
public setUserConfiguration(key: any, value: any, root?: URI): Thenable<void> {
|
||||
if (root) {
|
||||
const configForRoot = this.configurationByRoot.lookUp(root.fsPath) || Object.create(null);
|
||||
const configForRoot = this.configurationByRoot.get(root.fsPath) || Object.create(null);
|
||||
configForRoot[key] = value;
|
||||
this.configurationByRoot.insert(root.fsPath, configForRoot);
|
||||
this.configurationByRoot.set(root.fsPath, configForRoot);
|
||||
} else {
|
||||
this.configuration[key] = value;
|
||||
}
|
||||
@@ -48,32 +52,38 @@ export class TestConfigurationService extends EventEmitter implements IConfigura
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
public onDidUpdateConfiguration() {
|
||||
public onDidChangeConfiguration() {
|
||||
return { dispose() { } };
|
||||
}
|
||||
|
||||
public lookup<C>(key: string, overrides?: IConfigurationOverrides): IConfigurationValue<C> {
|
||||
public inspect<T>(key: string, overrides?: IConfigurationOverrides): {
|
||||
default: T,
|
||||
user: T,
|
||||
workspace: T,
|
||||
workspaceFolder: T
|
||||
value: T,
|
||||
} {
|
||||
const config = this.getConfiguration(undefined, overrides);
|
||||
|
||||
return {
|
||||
value: getConfigurationValue<C>(config, key),
|
||||
default: getConfigurationValue<C>(config, key),
|
||||
user: getConfigurationValue<C>(config, key),
|
||||
value: getConfigurationValue<T>(config, key),
|
||||
default: getConfigurationValue<T>(config, key),
|
||||
user: getConfigurationValue<T>(config, key),
|
||||
workspace: null,
|
||||
folder: null
|
||||
workspaceFolder: null
|
||||
};
|
||||
}
|
||||
|
||||
public keys(): IConfigurationKeys {
|
||||
public keys() {
|
||||
return {
|
||||
default: getConfigurationKeys(),
|
||||
user: Object.keys(this.configuration),
|
||||
workspace: [],
|
||||
folder: []
|
||||
workspaceFolder: []
|
||||
};
|
||||
}
|
||||
|
||||
public values(): IConfigurationValues {
|
||||
return {};
|
||||
public getConfigurationData() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,8 @@ suite('ConfigurationService - Node', () => {
|
||||
assert.equal(config.foo, 'bar');
|
||||
|
||||
// force a reload to get latest
|
||||
service.reloadConfiguration<{ foo: string }>().then(config => {
|
||||
service.reloadConfiguration().then(() => {
|
||||
config = service.getConfiguration<{ foo: string }>();
|
||||
assert.ok(config);
|
||||
assert.equal(config.foo, 'changed');
|
||||
|
||||
@@ -202,12 +203,12 @@ suite('ConfigurationService - Node', () => {
|
||||
testFile((testFile, cleanUp) => {
|
||||
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, testFile));
|
||||
|
||||
let res = service.lookup('something.missing');
|
||||
let res = service.inspect('something.missing');
|
||||
assert.strictEqual(res.value, void 0);
|
||||
assert.strictEqual(res.default, void 0);
|
||||
assert.strictEqual(res.user, void 0);
|
||||
|
||||
res = service.lookup('lookup.service.testSetting');
|
||||
res = service.inspect('lookup.service.testSetting');
|
||||
assert.strictEqual(res.default, 'isSet');
|
||||
assert.strictEqual(res.value, 'isSet');
|
||||
assert.strictEqual(res.user, void 0);
|
||||
@@ -215,7 +216,7 @@ suite('ConfigurationService - Node', () => {
|
||||
fs.writeFileSync(testFile, '{ "lookup.service.testSetting": "bar" }');
|
||||
|
||||
return service.reloadConfiguration().then(() => {
|
||||
res = service.lookup('lookup.service.testSetting');
|
||||
res = service.inspect('lookup.service.testSetting');
|
||||
assert.strictEqual(res.default, 'isSet');
|
||||
assert.strictEqual(res.user, 'bar');
|
||||
assert.strictEqual(res.value, 'bar');
|
||||
@@ -242,7 +243,7 @@ suite('ConfigurationService - Node', () => {
|
||||
testFile((testFile, cleanUp) => {
|
||||
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, testFile));
|
||||
|
||||
let res = service.lookup('lookup.service.testNullSetting');
|
||||
let res = service.inspect('lookup.service.testNullSetting');
|
||||
assert.strictEqual(res.default, null);
|
||||
assert.strictEqual(res.value, null);
|
||||
assert.strictEqual(res.user, void 0);
|
||||
@@ -250,7 +251,7 @@ suite('ConfigurationService - Node', () => {
|
||||
fs.writeFileSync(testFile, '{ "lookup.service.testNullSetting": null }');
|
||||
|
||||
return service.reloadConfiguration().then(() => {
|
||||
res = service.lookup('lookup.service.testNullSetting');
|
||||
res = service.inspect('lookup.service.testNullSetting');
|
||||
assert.strictEqual(res.default, null);
|
||||
assert.strictEqual(res.value, null);
|
||||
assert.strictEqual(res.user, null);
|
||||
|
||||
Reference in New Issue
Block a user