mirror of
https://github.com/coder/code-server.git
synced 2026-05-26 22:27:27 +02:00
Merge commit 'be3e8236086165e5e45a5a10783823874b3f3ebd' as 'lib/vscode'
This commit is contained in:
372
lib/vscode/src/vs/platform/configuration/common/configuration.ts
Normal file
372
lib/vscode/src/vs/platform/configuration/common/configuration.ts
Normal file
@@ -0,0 +1,372 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
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, OVERRIDE_PROPERTY_PATTERN, overrideIdentifierFromKey } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
|
||||
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 | null;
|
||||
resource?: URI | null;
|
||||
}
|
||||
|
||||
export const enum ConfigurationTarget {
|
||||
USER = 1,
|
||||
USER_LOCAL,
|
||||
USER_REMOTE,
|
||||
WORKSPACE,
|
||||
WORKSPACE_FOLDER,
|
||||
DEFAULT,
|
||||
MEMORY
|
||||
}
|
||||
export function ConfigurationTargetToString(configurationTarget: ConfigurationTarget) {
|
||||
switch (configurationTarget) {
|
||||
case ConfigurationTarget.USER: return 'USER';
|
||||
case ConfigurationTarget.USER_LOCAL: return 'USER_LOCAL';
|
||||
case ConfigurationTarget.USER_REMOTE: return 'USER_REMOTE';
|
||||
case ConfigurationTarget.WORKSPACE: return 'WORKSPACE';
|
||||
case ConfigurationTarget.WORKSPACE_FOLDER: return 'WORKSPACE_FOLDER';
|
||||
case ConfigurationTarget.DEFAULT: return 'DEFAULT';
|
||||
case ConfigurationTarget.MEMORY: return 'MEMORY';
|
||||
}
|
||||
}
|
||||
|
||||
export interface IConfigurationChange {
|
||||
keys: string[];
|
||||
overrides: [string, string[]][];
|
||||
}
|
||||
|
||||
export interface IConfigurationChangeEvent {
|
||||
|
||||
readonly source: ConfigurationTarget;
|
||||
readonly affectedKeys: string[];
|
||||
readonly change: IConfigurationChange;
|
||||
|
||||
affectsConfiguration(configuration: string, overrides?: IConfigurationOverrides): boolean;
|
||||
|
||||
// Following data is used for telemetry
|
||||
readonly sourceConfig: any;
|
||||
}
|
||||
|
||||
export interface IConfigurationValue<T> {
|
||||
|
||||
readonly defaultValue?: T;
|
||||
readonly userValue?: T;
|
||||
readonly userLocalValue?: T;
|
||||
readonly userRemoteValue?: T;
|
||||
readonly workspaceValue?: T;
|
||||
readonly workspaceFolderValue?: T;
|
||||
readonly memoryValue?: T;
|
||||
readonly value?: T;
|
||||
|
||||
readonly default?: { value?: T, override?: T };
|
||||
readonly user?: { value?: T, override?: T };
|
||||
readonly userLocal?: { value?: T, override?: T };
|
||||
readonly userRemote?: { value?: T, override?: T };
|
||||
readonly workspace?: { value?: T, override?: T };
|
||||
readonly workspaceFolder?: { value?: T, override?: T };
|
||||
readonly memory?: { value?: T, override?: T };
|
||||
|
||||
readonly overrideIdentifiers?: string[];
|
||||
}
|
||||
|
||||
export interface IConfigurationService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
onDidChangeConfiguration: Event<IConfigurationChangeEvent>;
|
||||
|
||||
getConfigurationData(): IConfigurationData | null;
|
||||
|
||||
/**
|
||||
* Fetches the value of the section for the given overrides.
|
||||
* Value can be of native type or an object keyed off the section name.
|
||||
*
|
||||
* @param section - Section of the configuraion. Can be `null` or `undefined`.
|
||||
* @param overrides - Overrides that has to be applied while fetching
|
||||
*
|
||||
*/
|
||||
getValue<T>(): T;
|
||||
getValue<T>(section: string): T;
|
||||
getValue<T>(overrides: IConfigurationOverrides): T;
|
||||
getValue<T>(section: string, overrides: IConfigurationOverrides): T;
|
||||
|
||||
updateValue(key: string, value: any): Promise<void>;
|
||||
updateValue(key: string, value: any, overrides: IConfigurationOverrides): Promise<void>;
|
||||
updateValue(key: string, value: any, target: ConfigurationTarget): Promise<void>;
|
||||
updateValue(key: string, value: any, overrides: IConfigurationOverrides, target: ConfigurationTarget, donotNotifyError?: boolean): Promise<void>;
|
||||
|
||||
inspect<T>(key: string, overrides?: IConfigurationOverrides): IConfigurationValue<T>;
|
||||
|
||||
reloadConfiguration(folder?: IWorkspaceFolder): Promise<void>;
|
||||
|
||||
keys(): {
|
||||
default: string[];
|
||||
user: string[];
|
||||
workspace: string[];
|
||||
workspaceFolder: string[];
|
||||
memory?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface IConfigurationModel {
|
||||
contents: any;
|
||||
keys: string[];
|
||||
overrides: IOverrides[];
|
||||
}
|
||||
|
||||
export interface IOverrides {
|
||||
keys: string[];
|
||||
contents: any;
|
||||
identifiers: string[];
|
||||
}
|
||||
|
||||
export interface IConfigurationData {
|
||||
defaults: IConfigurationModel;
|
||||
user: IConfigurationModel;
|
||||
workspace: IConfigurationModel;
|
||||
folders: [UriComponents, IConfigurationModel][];
|
||||
}
|
||||
|
||||
export interface IConfigurationCompareResult {
|
||||
added: string[];
|
||||
removed: string[];
|
||||
updated: string[];
|
||||
overrides: [string, string[]][];
|
||||
}
|
||||
|
||||
export function compare(from: IConfigurationModel | undefined, to: IConfigurationModel | undefined): IConfigurationCompareResult {
|
||||
const added = to
|
||||
? from ? to.keys.filter(key => from.keys.indexOf(key) === -1) : [...to.keys]
|
||||
: [];
|
||||
const removed = from
|
||||
? to ? from.keys.filter(key => to.keys.indexOf(key) === -1) : [...from.keys]
|
||||
: [];
|
||||
const updated: string[] = [];
|
||||
|
||||
if (to && from) {
|
||||
for (const key of from.keys) {
|
||||
if (to.keys.indexOf(key) !== -1) {
|
||||
const value1 = getConfigurationValue(from.contents, key);
|
||||
const value2 = getConfigurationValue(to.contents, key);
|
||||
if (!objects.equals(value1, value2)) {
|
||||
updated.push(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const overrides: [string, string[]][] = [];
|
||||
const byOverrideIdentifier = (overrides: IOverrides[]): IStringDictionary<IOverrides> => {
|
||||
const result: IStringDictionary<IOverrides> = {};
|
||||
for (const override of overrides) {
|
||||
for (const identifier of override.identifiers) {
|
||||
result[keyFromOverrideIdentifier(identifier)] = override;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
const toOverridesByIdentifier: IStringDictionary<IOverrides> = to ? byOverrideIdentifier(to.overrides) : {};
|
||||
const fromOverridesByIdentifier: IStringDictionary<IOverrides> = from ? byOverrideIdentifier(from.overrides) : {};
|
||||
|
||||
if (Object.keys(toOverridesByIdentifier).length) {
|
||||
for (const key of added) {
|
||||
const override = toOverridesByIdentifier[key];
|
||||
if (override) {
|
||||
overrides.push([overrideIdentifierFromKey(key), override.keys]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Object.keys(fromOverridesByIdentifier).length) {
|
||||
for (const key of removed) {
|
||||
const override = fromOverridesByIdentifier[key];
|
||||
if (override) {
|
||||
overrides.push([overrideIdentifierFromKey(key), override.keys]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(toOverridesByIdentifier).length && Object.keys(fromOverridesByIdentifier).length) {
|
||||
for (const key of updated) {
|
||||
const fromOverride = fromOverridesByIdentifier[key];
|
||||
const toOverride = toOverridesByIdentifier[key];
|
||||
if (fromOverride && toOverride) {
|
||||
const result = compare({ contents: fromOverride.contents, keys: fromOverride.keys, overrides: [] }, { contents: toOverride.contents, keys: toOverride.keys, overrides: [] });
|
||||
overrides.push([overrideIdentifierFromKey(key), [...result.added, ...result.removed, ...result.updated]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { added, removed, updated, overrides };
|
||||
}
|
||||
|
||||
export function toOverrides(raw: any, conflictReporter: (message: string) => void): IOverrides[] {
|
||||
const overrides: IOverrides[] = [];
|
||||
for (const key of Object.keys(raw)) {
|
||||
if (OVERRIDE_PROPERTY_PATTERN.test(key)) {
|
||||
const overrideRaw: any = {};
|
||||
for (const keyInOverrideRaw in raw[key]) {
|
||||
overrideRaw[keyInOverrideRaw] = raw[key][keyInOverrideRaw];
|
||||
}
|
||||
overrides.push({
|
||||
identifiers: [overrideIdentifierFromKey(key).trim()],
|
||||
keys: Object.keys(overrideRaw),
|
||||
contents: toValuesTree(overrideRaw, conflictReporter)
|
||||
});
|
||||
}
|
||||
}
|
||||
return overrides;
|
||||
}
|
||||
|
||||
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 !== null) {
|
||||
try {
|
||||
curr[last] = value; // workaround https://github.com/microsoft/vscode/issues/13606
|
||||
} catch (e) {
|
||||
conflictReporter(`Ignoring ${key} as ${segments.join('.')} is ${JSON.stringify(curr)}`);
|
||||
}
|
||||
} 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function to get the configuration value with a specific settings path (e.g. config.some.setting)
|
||||
*/
|
||||
export function getConfigurationValue<T>(config: any, settingPath: string, defaultValue?: T): T {
|
||||
function accessSetting(config: any, path: string[]): any {
|
||||
let current = config;
|
||||
for (const component of path) {
|
||||
if (typeof current !== 'object' || current === null) {
|
||||
return undefined;
|
||||
}
|
||||
current = current[component];
|
||||
}
|
||||
return <T>current;
|
||||
}
|
||||
|
||||
const path = settingPath.split('.');
|
||||
const result = accessSetting(config, path);
|
||||
|
||||
return typeof result === 'undefined' ? defaultValue : result;
|
||||
}
|
||||
|
||||
export function merge(base: any, add: any, overwrite: boolean): void {
|
||||
Object.keys(add).forEach(key => {
|
||||
if (key !== '__proto__') {
|
||||
if (key in base) {
|
||||
if (types.isObject(base[key]) && types.isObject(add[key])) {
|
||||
merge(base[key], add[key], overwrite);
|
||||
} else if (overwrite) {
|
||||
base[key] = add[key];
|
||||
}
|
||||
} else {
|
||||
base[key] = add[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function getConfigurationKeys(): string[] {
|
||||
const properties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
|
||||
return Object.keys(properties);
|
||||
}
|
||||
|
||||
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 keyFromOverrideIdentifier(overrideIdentifier: string): string {
|
||||
return `[${overrideIdentifier}]`;
|
||||
}
|
||||
|
||||
export function getMigratedSettingValue<T>(configurationService: IConfigurationService, currentSettingName: string, legacySettingName: string): T {
|
||||
const setting = configurationService.inspect<T>(currentSettingName);
|
||||
const legacySetting = configurationService.inspect<T>(legacySettingName);
|
||||
|
||||
if (typeof setting.userValue !== 'undefined' || typeof setting.workspaceValue !== 'undefined' || typeof setting.workspaceFolderValue !== 'undefined') {
|
||||
return setting.value!;
|
||||
} else if (typeof legacySetting.userValue !== 'undefined' || typeof legacySetting.workspaceValue !== 'undefined' || typeof legacySetting.workspaceFolderValue !== 'undefined') {
|
||||
return legacySetting.value!;
|
||||
} else {
|
||||
return setting.defaultValue!;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,803 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as json from 'vs/base/common/json';
|
||||
import { ResourceMap, getOrSet } from 'vs/base/common/map';
|
||||
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 { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { OVERRIDE_PROPERTY_PATTERN, ConfigurationScope, IConfigurationRegistry, Extensions, IConfigurationPropertySchema, overrideIdentifierFromKey } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IOverrides, addToValueTree, toValuesTree, IConfigurationModel, getConfigurationValue, IConfigurationOverrides, IConfigurationData, getDefaultValues, getConfigurationKeys, removeFromValueTree, toOverrides, IConfigurationValue, ConfigurationTarget, compare, IConfigurationChangeEvent, IConfigurationChange } from 'vs/platform/configuration/common/configuration';
|
||||
import { Workspace } from 'vs/platform/workspace/common/workspace';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { dirname } from 'vs/base/common/resources';
|
||||
|
||||
export class ConfigurationModel implements IConfigurationModel {
|
||||
|
||||
private isFrozen: boolean = false;
|
||||
|
||||
constructor(
|
||||
private _contents: any = {},
|
||||
private _keys: string[] = [],
|
||||
private _overrides: IOverrides[] = []
|
||||
) {
|
||||
}
|
||||
|
||||
get contents(): any {
|
||||
return this.checkAndFreeze(this._contents);
|
||||
}
|
||||
|
||||
get overrides(): IOverrides[] {
|
||||
return this.checkAndFreeze(this._overrides);
|
||||
}
|
||||
|
||||
get keys(): string[] {
|
||||
return this.checkAndFreeze(this._keys);
|
||||
}
|
||||
|
||||
isEmpty(): boolean {
|
||||
return this._keys.length === 0 && Object.keys(this._contents).length === 0 && this._overrides.length === 0;
|
||||
}
|
||||
|
||||
getValue<V>(section: string | undefined): V {
|
||||
return section ? getConfigurationValue<any>(this.contents, section) : this.contents;
|
||||
}
|
||||
|
||||
getOverrideValue<V>(section: string | undefined, overrideIdentifier: string): V | undefined {
|
||||
const overrideContents = this.getContentsForOverrideIdentifer(overrideIdentifier);
|
||||
return overrideContents
|
||||
? section ? getConfigurationValue<any>(overrideContents, section) : overrideContents
|
||||
: undefined;
|
||||
}
|
||||
|
||||
getKeysForOverrideIdentifier(identifier: string): string[] {
|
||||
for (const override of this.overrides) {
|
||||
if (override.identifiers.indexOf(identifier) !== -1) {
|
||||
return override.keys;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
override(identifier: string): ConfigurationModel {
|
||||
const overrideContents = this.getContentsForOverrideIdentifer(identifier);
|
||||
|
||||
if (!overrideContents || typeof overrideContents !== 'object' || !Object.keys(overrideContents).length) {
|
||||
// If there are no valid overrides, return self
|
||||
return this;
|
||||
}
|
||||
|
||||
let contents: any = {};
|
||||
for (const key of arrays.distinct([...Object.keys(this.contents), ...Object.keys(overrideContents)])) {
|
||||
|
||||
let contentsForKey = this.contents[key];
|
||||
let overrideContentsForKey = overrideContents[key];
|
||||
|
||||
// If there are override contents for the key, clone and merge otherwise use base contents
|
||||
if (overrideContentsForKey) {
|
||||
// Clone and merge only if base contents and override contents are of type object otherwise just override
|
||||
if (typeof contentsForKey === 'object' && typeof overrideContentsForKey === 'object') {
|
||||
contentsForKey = objects.deepClone(contentsForKey);
|
||||
this.mergeContents(contentsForKey, overrideContentsForKey);
|
||||
} else {
|
||||
contentsForKey = overrideContentsForKey;
|
||||
}
|
||||
}
|
||||
|
||||
contents[key] = contentsForKey;
|
||||
}
|
||||
|
||||
return new ConfigurationModel(contents, this.keys, this.overrides);
|
||||
}
|
||||
|
||||
merge(...others: ConfigurationModel[]): ConfigurationModel {
|
||||
const contents = objects.deepClone(this.contents);
|
||||
const overrides = objects.deepClone(this.overrides);
|
||||
const keys = [...this.keys];
|
||||
|
||||
for (const other of others) {
|
||||
this.mergeContents(contents, other.contents);
|
||||
|
||||
for (const otherOverride of other.overrides) {
|
||||
const [override] = overrides.filter(o => arrays.equals(o.identifiers, otherOverride.identifiers));
|
||||
if (override) {
|
||||
this.mergeContents(override.contents, otherOverride.contents);
|
||||
} else {
|
||||
overrides.push(objects.deepClone(otherOverride));
|
||||
}
|
||||
}
|
||||
for (const key of other.keys) {
|
||||
if (keys.indexOf(key) === -1) {
|
||||
keys.push(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ConfigurationModel(contents, keys, overrides);
|
||||
}
|
||||
|
||||
freeze(): ConfigurationModel {
|
||||
this.isFrozen = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
private mergeContents(source: any, target: any): void {
|
||||
for (const key of Object.keys(target)) {
|
||||
if (key in source) {
|
||||
if (types.isObject(source[key]) && types.isObject(target[key])) {
|
||||
this.mergeContents(source[key], target[key]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
source[key] = objects.deepClone(target[key]);
|
||||
}
|
||||
}
|
||||
|
||||
private checkAndFreeze<T>(data: T): T {
|
||||
if (this.isFrozen && !Object.isFrozen(data)) {
|
||||
return objects.deepFreeze(data);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private getContentsForOverrideIdentifer(identifier: string): any {
|
||||
for (const override of this.overrides) {
|
||||
if (override.identifiers.indexOf(identifier) !== -1) {
|
||||
return override.contents;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
toJSON(): IConfigurationModel {
|
||||
return {
|
||||
contents: this.contents,
|
||||
overrides: this.overrides,
|
||||
keys: this.keys
|
||||
};
|
||||
}
|
||||
|
||||
// Update methods
|
||||
|
||||
public setValue(key: string, value: any) {
|
||||
this.addKey(key);
|
||||
addToValueTree(this.contents, key, value, e => { throw new Error(e); });
|
||||
}
|
||||
|
||||
public removeValue(key: string): void {
|
||||
if (this.removeKey(key)) {
|
||||
removeFromValueTree(this.contents, key);
|
||||
}
|
||||
}
|
||||
|
||||
private addKey(key: string): void {
|
||||
let index = this.keys.length;
|
||||
for (let i = 0; i < index; i++) {
|
||||
if (key.indexOf(this.keys[i]) === 0) {
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
this.keys.splice(index, 1, key);
|
||||
}
|
||||
|
||||
private removeKey(key: string): boolean {
|
||||
let index = this.keys.indexOf(key);
|
||||
if (index !== -1) {
|
||||
this.keys.splice(index, 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class DefaultConfigurationModel extends ConfigurationModel {
|
||||
|
||||
constructor() {
|
||||
const contents = getDefaultValues();
|
||||
const keys = getConfigurationKeys();
|
||||
const overrides: IOverrides[] = [];
|
||||
for (const key of Object.keys(contents)) {
|
||||
if (OVERRIDE_PROPERTY_PATTERN.test(key)) {
|
||||
overrides.push({
|
||||
identifiers: [overrideIdentifierFromKey(key).trim()],
|
||||
keys: Object.keys(contents[key]),
|
||||
contents: toValuesTree(contents[key], message => console.error(`Conflict in default settings file: ${message}`)),
|
||||
});
|
||||
}
|
||||
}
|
||||
super(contents, keys, overrides);
|
||||
}
|
||||
}
|
||||
|
||||
export class ConfigurationModelParser {
|
||||
|
||||
private _raw: any = null;
|
||||
private _configurationModel: ConfigurationModel | null = null;
|
||||
private _parseErrors: any[] = [];
|
||||
|
||||
constructor(protected readonly _name: string, private _scopes?: ConfigurationScope[]) { }
|
||||
|
||||
get configurationModel(): ConfigurationModel {
|
||||
return this._configurationModel || new ConfigurationModel();
|
||||
}
|
||||
|
||||
get errors(): any[] {
|
||||
return this._parseErrors;
|
||||
}
|
||||
|
||||
public parseContent(content: string | null | undefined): void {
|
||||
if (!types.isUndefinedOrNull(content)) {
|
||||
const raw = this.doParseContent(content);
|
||||
this.parseRaw(raw);
|
||||
}
|
||||
}
|
||||
|
||||
public parseRaw(raw: any): void {
|
||||
this._raw = raw;
|
||||
const configurationModel = this.doParseRaw(raw);
|
||||
this._configurationModel = new ConfigurationModel(configurationModel.contents, configurationModel.keys, configurationModel.overrides);
|
||||
}
|
||||
|
||||
public parse(): void {
|
||||
if (this._raw) {
|
||||
this.parseRaw(this._raw);
|
||||
}
|
||||
}
|
||||
|
||||
protected doParseContent(content: string): any {
|
||||
let raw: any = {};
|
||||
let currentProperty: string | null = 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;
|
||||
}
|
||||
}
|
||||
|
||||
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, offset: number, length: number) => {
|
||||
parseErrors.push({ error, offset, length });
|
||||
}
|
||||
};
|
||||
if (content) {
|
||||
try {
|
||||
json.visit(content, visitor);
|
||||
raw = currentParent[0] || {};
|
||||
} catch (e) {
|
||||
console.error(`Error while parsing settings file ${this._name}: ${e}`);
|
||||
this._parseErrors = [e];
|
||||
}
|
||||
}
|
||||
|
||||
return raw;
|
||||
}
|
||||
|
||||
protected doParseRaw(raw: any): IConfigurationModel {
|
||||
if (this._scopes) {
|
||||
const configurationProperties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
|
||||
raw = this.filterByScope(raw, configurationProperties, true, this._scopes);
|
||||
}
|
||||
const contents = toValuesTree(raw, message => console.error(`Conflict in settings file ${this._name}: ${message}`));
|
||||
const keys = Object.keys(raw);
|
||||
const overrides: IOverrides[] = toOverrides(raw, message => console.error(`Conflict in settings file ${this._name}: ${message}`));
|
||||
return { contents, keys, overrides };
|
||||
}
|
||||
|
||||
private filterByScope(properties: any, configurationProperties: { [qualifiedKey: string]: IConfigurationPropertySchema }, filterOverriddenProperties: boolean, scopes: ConfigurationScope[]): {} {
|
||||
const result: any = {};
|
||||
for (let key in properties) {
|
||||
if (OVERRIDE_PROPERTY_PATTERN.test(key) && filterOverriddenProperties) {
|
||||
result[key] = this.filterByScope(properties[key], configurationProperties, false, scopes);
|
||||
} else {
|
||||
const scope = this.getScope(key, configurationProperties);
|
||||
// Load unregistered configurations always.
|
||||
if (scope === undefined || scopes.indexOf(scope) !== -1) {
|
||||
result[key] = properties[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private getScope(key: string, configurationProperties: { [qualifiedKey: string]: IConfigurationPropertySchema }): ConfigurationScope | undefined {
|
||||
const propertySchema = configurationProperties[key];
|
||||
return propertySchema ? typeof propertySchema.scope !== 'undefined' ? propertySchema.scope : ConfigurationScope.WINDOW : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export class UserSettings extends Disposable {
|
||||
|
||||
private readonly parser: ConfigurationModelParser;
|
||||
protected readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onDidChange: Event<void> = this._onDidChange.event;
|
||||
|
||||
constructor(
|
||||
private readonly userSettingsResource: URI,
|
||||
private readonly scopes: ConfigurationScope[] | undefined,
|
||||
private readonly fileService: IFileService
|
||||
) {
|
||||
super();
|
||||
this.parser = new ConfigurationModelParser(this.userSettingsResource.toString(), this.scopes);
|
||||
this._register(this.fileService.watch(dirname(this.userSettingsResource)));
|
||||
this._register(Event.filter(this.fileService.onDidFilesChange, e => e.contains(this.userSettingsResource))(() => this._onDidChange.fire()));
|
||||
}
|
||||
|
||||
async loadConfiguration(): Promise<ConfigurationModel> {
|
||||
try {
|
||||
const content = await this.fileService.readFile(this.userSettingsResource);
|
||||
this.parser.parseContent(content.value.toString() || '{}');
|
||||
return this.parser.configurationModel;
|
||||
} catch (e) {
|
||||
return new ConfigurationModel();
|
||||
}
|
||||
}
|
||||
|
||||
reprocess(): ConfigurationModel {
|
||||
this.parser.parse();
|
||||
return this.parser.configurationModel;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class Configuration {
|
||||
|
||||
private _workspaceConsolidatedConfiguration: ConfigurationModel | null = null;
|
||||
private _foldersConsolidatedConfigurations: ResourceMap<ConfigurationModel> = new ResourceMap<ConfigurationModel>();
|
||||
|
||||
constructor(
|
||||
private _defaultConfiguration: ConfigurationModel,
|
||||
private _localUserConfiguration: ConfigurationModel,
|
||||
private _remoteUserConfiguration: ConfigurationModel = new ConfigurationModel(),
|
||||
private _workspaceConfiguration: ConfigurationModel = new ConfigurationModel(),
|
||||
private _folderConfigurations: ResourceMap<ConfigurationModel> = new ResourceMap<ConfigurationModel>(),
|
||||
private _memoryConfiguration: ConfigurationModel = new ConfigurationModel(),
|
||||
private _memoryConfigurationByResource: ResourceMap<ConfigurationModel> = new ResourceMap<ConfigurationModel>(),
|
||||
private _freeze: boolean = true) {
|
||||
}
|
||||
|
||||
getValue(section: string | undefined, overrides: IConfigurationOverrides, workspace: Workspace | undefined): any {
|
||||
const consolidateConfigurationModel = this.getConsolidateConfigurationModel(overrides, workspace);
|
||||
return consolidateConfigurationModel.getValue(section);
|
||||
}
|
||||
|
||||
updateValue(key: string, value: any, overrides: IConfigurationOverrides = {}): void {
|
||||
let memoryConfiguration: ConfigurationModel | undefined;
|
||||
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 === undefined) {
|
||||
memoryConfiguration.removeValue(key);
|
||||
} else {
|
||||
memoryConfiguration.setValue(key, value);
|
||||
}
|
||||
|
||||
if (!overrides.resource) {
|
||||
this._workspaceConsolidatedConfiguration = null;
|
||||
}
|
||||
}
|
||||
|
||||
inspect<C>(key: string, overrides: IConfigurationOverrides, workspace: Workspace | undefined): IConfigurationValue<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;
|
||||
|
||||
const defaultValue = overrides.overrideIdentifier ? this._defaultConfiguration.freeze().override(overrides.overrideIdentifier).getValue<C>(key) : this._defaultConfiguration.freeze().getValue<C>(key);
|
||||
const userValue = overrides.overrideIdentifier ? this.userConfiguration.freeze().override(overrides.overrideIdentifier).getValue<C>(key) : this.userConfiguration.freeze().getValue<C>(key);
|
||||
const userLocalValue = overrides.overrideIdentifier ? this.localUserConfiguration.freeze().override(overrides.overrideIdentifier).getValue<C>(key) : this.localUserConfiguration.freeze().getValue<C>(key);
|
||||
const userRemoteValue = overrides.overrideIdentifier ? this.remoteUserConfiguration.freeze().override(overrides.overrideIdentifier).getValue<C>(key) : this.remoteUserConfiguration.freeze().getValue<C>(key);
|
||||
const workspaceValue = workspace ? overrides.overrideIdentifier ? this._workspaceConfiguration.freeze().override(overrides.overrideIdentifier).getValue<C>(key) : this._workspaceConfiguration.freeze().getValue<C>(key) : undefined; //Check on workspace exists or not because _workspaceConfiguration is never null
|
||||
const workspaceFolderValue = folderConfigurationModel ? overrides.overrideIdentifier ? folderConfigurationModel.freeze().override(overrides.overrideIdentifier).getValue<C>(key) : folderConfigurationModel.freeze().getValue<C>(key) : undefined;
|
||||
const memoryValue = overrides.overrideIdentifier ? memoryConfigurationModel.override(overrides.overrideIdentifier).getValue<C>(key) : memoryConfigurationModel.getValue<C>(key);
|
||||
const value = consolidateConfigurationModel.getValue<C>(key);
|
||||
const overrideIdentifiers: string[] = arrays.distinct(arrays.flatten(consolidateConfigurationModel.overrides.map(override => override.identifiers))).filter(overrideIdentifier => consolidateConfigurationModel.getOverrideValue(key, overrideIdentifier) !== undefined);
|
||||
|
||||
return {
|
||||
defaultValue: defaultValue,
|
||||
userValue: userValue,
|
||||
userLocalValue: userLocalValue,
|
||||
userRemoteValue: userRemoteValue,
|
||||
workspaceValue: workspaceValue,
|
||||
workspaceFolderValue: workspaceFolderValue,
|
||||
memoryValue: memoryValue,
|
||||
value,
|
||||
|
||||
default: defaultValue !== undefined ? { value: this._defaultConfiguration.freeze().getValue(key), override: overrides.overrideIdentifier ? this._defaultConfiguration.freeze().getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined,
|
||||
user: userValue !== undefined ? { value: this.userConfiguration.freeze().getValue(key), override: overrides.overrideIdentifier ? this.userConfiguration.freeze().getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined,
|
||||
userLocal: userLocalValue !== undefined ? { value: this.localUserConfiguration.freeze().getValue(key), override: overrides.overrideIdentifier ? this.localUserConfiguration.freeze().getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined,
|
||||
userRemote: userRemoteValue !== undefined ? { value: this.remoteUserConfiguration.freeze().getValue(key), override: overrides.overrideIdentifier ? this.remoteUserConfiguration.freeze().getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined,
|
||||
workspace: workspaceValue !== undefined ? { value: this._workspaceConfiguration.freeze().getValue(key), override: overrides.overrideIdentifier ? this._workspaceConfiguration.freeze().getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined,
|
||||
workspaceFolder: workspaceFolderValue !== undefined ? { value: folderConfigurationModel?.freeze().getValue(key), override: overrides.overrideIdentifier ? folderConfigurationModel?.freeze().getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined,
|
||||
memory: memoryValue !== undefined ? { value: memoryConfigurationModel.getValue(key), override: overrides.overrideIdentifier ? memoryConfigurationModel.getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined,
|
||||
|
||||
overrideIdentifiers: overrideIdentifiers.length ? overrideIdentifiers : undefined
|
||||
};
|
||||
}
|
||||
|
||||
keys(workspace: Workspace | undefined): {
|
||||
default: string[];
|
||||
user: string[];
|
||||
workspace: string[];
|
||||
workspaceFolder: string[];
|
||||
} {
|
||||
const folderConfigurationModel = this.getFolderConfigurationModelForResource(undefined, workspace);
|
||||
return {
|
||||
default: this._defaultConfiguration.freeze().keys,
|
||||
user: this.userConfiguration.freeze().keys,
|
||||
workspace: this._workspaceConfiguration.freeze().keys,
|
||||
workspaceFolder: folderConfigurationModel ? folderConfigurationModel.freeze().keys : []
|
||||
};
|
||||
}
|
||||
|
||||
updateDefaultConfiguration(defaultConfiguration: ConfigurationModel): void {
|
||||
this._defaultConfiguration = defaultConfiguration;
|
||||
this._workspaceConsolidatedConfiguration = null;
|
||||
this._foldersConsolidatedConfigurations.clear();
|
||||
}
|
||||
|
||||
updateLocalUserConfiguration(localUserConfiguration: ConfigurationModel): void {
|
||||
this._localUserConfiguration = localUserConfiguration;
|
||||
this._userConfiguration = null;
|
||||
this._workspaceConsolidatedConfiguration = null;
|
||||
this._foldersConsolidatedConfigurations.clear();
|
||||
}
|
||||
|
||||
updateRemoteUserConfiguration(remoteUserConfiguration: ConfigurationModel): void {
|
||||
this._remoteUserConfiguration = remoteUserConfiguration;
|
||||
this._userConfiguration = null;
|
||||
this._workspaceConsolidatedConfiguration = null;
|
||||
this._foldersConsolidatedConfigurations.clear();
|
||||
}
|
||||
|
||||
updateWorkspaceConfiguration(workspaceConfiguration: ConfigurationModel): void {
|
||||
this._workspaceConfiguration = workspaceConfiguration;
|
||||
this._workspaceConsolidatedConfiguration = null;
|
||||
this._foldersConsolidatedConfigurations.clear();
|
||||
}
|
||||
|
||||
updateFolderConfiguration(resource: URI, configuration: ConfigurationModel): void {
|
||||
this._folderConfigurations.set(resource, configuration);
|
||||
this._foldersConsolidatedConfigurations.delete(resource);
|
||||
}
|
||||
|
||||
deleteFolderConfiguration(resource: URI): void {
|
||||
this.folderConfigurations.delete(resource);
|
||||
this._foldersConsolidatedConfigurations.delete(resource);
|
||||
}
|
||||
|
||||
compareAndUpdateDefaultConfiguration(defaults: ConfigurationModel, keys: string[]): IConfigurationChange {
|
||||
const overrides: [string, string[]][] = keys
|
||||
.filter(key => OVERRIDE_PROPERTY_PATTERN.test(key))
|
||||
.map(key => {
|
||||
const overrideIdentifier = overrideIdentifierFromKey(key);
|
||||
const fromKeys = this._defaultConfiguration.getKeysForOverrideIdentifier(overrideIdentifier);
|
||||
const toKeys = defaults.getKeysForOverrideIdentifier(overrideIdentifier);
|
||||
const keys = [
|
||||
...toKeys.filter(key => fromKeys.indexOf(key) === -1),
|
||||
...fromKeys.filter(key => toKeys.indexOf(key) === -1),
|
||||
...fromKeys.filter(key => !objects.equals(this._defaultConfiguration.override(overrideIdentifier).getValue(key), defaults.override(overrideIdentifier).getValue(key)))
|
||||
];
|
||||
return [overrideIdentifier, keys];
|
||||
});
|
||||
this.updateDefaultConfiguration(defaults);
|
||||
return { keys, overrides };
|
||||
}
|
||||
|
||||
compareAndUpdateLocalUserConfiguration(user: ConfigurationModel): IConfigurationChange {
|
||||
const { added, updated, removed, overrides } = compare(this.localUserConfiguration, user);
|
||||
const keys = [...added, ...updated, ...removed];
|
||||
if (keys.length) {
|
||||
this.updateLocalUserConfiguration(user);
|
||||
}
|
||||
return { keys, overrides };
|
||||
}
|
||||
|
||||
compareAndUpdateRemoteUserConfiguration(user: ConfigurationModel): IConfigurationChange {
|
||||
const { added, updated, removed, overrides } = compare(this.remoteUserConfiguration, user);
|
||||
let keys = [...added, ...updated, ...removed];
|
||||
if (keys.length) {
|
||||
this.updateRemoteUserConfiguration(user);
|
||||
}
|
||||
return { keys, overrides };
|
||||
}
|
||||
|
||||
compareAndUpdateWorkspaceConfiguration(workspaceConfiguration: ConfigurationModel): IConfigurationChange {
|
||||
const { added, updated, removed, overrides } = compare(this.workspaceConfiguration, workspaceConfiguration);
|
||||
let keys = [...added, ...updated, ...removed];
|
||||
if (keys.length) {
|
||||
this.updateWorkspaceConfiguration(workspaceConfiguration);
|
||||
}
|
||||
return { keys, overrides };
|
||||
}
|
||||
|
||||
compareAndUpdateFolderConfiguration(resource: URI, folderConfiguration: ConfigurationModel): IConfigurationChange {
|
||||
const currentFolderConfiguration = this.folderConfigurations.get(resource);
|
||||
const { added, updated, removed, overrides } = compare(currentFolderConfiguration, folderConfiguration);
|
||||
let keys = [...added, ...updated, ...removed];
|
||||
if (keys.length || !currentFolderConfiguration) {
|
||||
this.updateFolderConfiguration(resource, folderConfiguration);
|
||||
}
|
||||
return { keys, overrides };
|
||||
}
|
||||
|
||||
compareAndDeleteFolderConfiguration(folder: URI): IConfigurationChange {
|
||||
const folderConfig = this.folderConfigurations.get(folder);
|
||||
if (!folderConfig) {
|
||||
throw new Error('Unknown folder');
|
||||
}
|
||||
this.deleteFolderConfiguration(folder);
|
||||
const { added, updated, removed, overrides } = compare(folderConfig, undefined);
|
||||
return { keys: [...added, ...updated, ...removed], overrides };
|
||||
}
|
||||
|
||||
get defaults(): ConfigurationModel {
|
||||
return this._defaultConfiguration;
|
||||
}
|
||||
|
||||
private _userConfiguration: ConfigurationModel | null = null;
|
||||
get userConfiguration(): ConfigurationModel {
|
||||
if (!this._userConfiguration) {
|
||||
this._userConfiguration = this._remoteUserConfiguration.isEmpty() ? this._localUserConfiguration : this._localUserConfiguration.merge(this._remoteUserConfiguration);
|
||||
if (this._freeze) {
|
||||
this._userConfiguration.freeze();
|
||||
}
|
||||
}
|
||||
return this._userConfiguration;
|
||||
}
|
||||
|
||||
get localUserConfiguration(): ConfigurationModel {
|
||||
return this._localUserConfiguration;
|
||||
}
|
||||
|
||||
get remoteUserConfiguration(): ConfigurationModel {
|
||||
return this._remoteUserConfiguration;
|
||||
}
|
||||
|
||||
get workspaceConfiguration(): ConfigurationModel {
|
||||
return this._workspaceConfiguration;
|
||||
}
|
||||
|
||||
protected get folderConfigurations(): ResourceMap<ConfigurationModel> {
|
||||
return this._folderConfigurations;
|
||||
}
|
||||
|
||||
private getConsolidateConfigurationModel(overrides: IConfigurationOverrides, workspace: Workspace | undefined): ConfigurationModel {
|
||||
let configurationModel = this.getConsolidatedConfigurationModelForResource(overrides, workspace);
|
||||
return overrides.overrideIdentifier ? configurationModel.override(overrides.overrideIdentifier) : configurationModel;
|
||||
}
|
||||
|
||||
private getConsolidatedConfigurationModelForResource({ resource }: IConfigurationOverrides, workspace: Workspace | undefined): ConfigurationModel {
|
||||
let consolidateConfiguration = this.getWorkspaceConsolidatedConfiguration();
|
||||
|
||||
if (workspace && resource) {
|
||||
const root = workspace.getFolder(resource);
|
||||
if (root) {
|
||||
consolidateConfiguration = this.getFolderConsolidatedConfiguration(root.uri) || consolidateConfiguration;
|
||||
}
|
||||
const memoryConfigurationForResource = this._memoryConfigurationByResource.get(resource);
|
||||
if (memoryConfigurationForResource) {
|
||||
consolidateConfiguration = consolidateConfiguration.merge(memoryConfigurationForResource);
|
||||
}
|
||||
}
|
||||
|
||||
return consolidateConfiguration;
|
||||
}
|
||||
|
||||
private getWorkspaceConsolidatedConfiguration(): ConfigurationModel {
|
||||
if (!this._workspaceConsolidatedConfiguration) {
|
||||
this._workspaceConsolidatedConfiguration = this._defaultConfiguration.merge(this.userConfiguration, this._workspaceConfiguration, this._memoryConfiguration);
|
||||
if (this._freeze) {
|
||||
this._workspaceConfiguration = this._workspaceConfiguration.freeze();
|
||||
}
|
||||
}
|
||||
return this._workspaceConsolidatedConfiguration;
|
||||
}
|
||||
|
||||
private getFolderConsolidatedConfiguration(folder: URI): ConfigurationModel {
|
||||
let folderConsolidatedConfiguration = this._foldersConsolidatedConfigurations.get(folder);
|
||||
if (!folderConsolidatedConfiguration) {
|
||||
const workspaceConsolidateConfiguration = this.getWorkspaceConsolidatedConfiguration();
|
||||
const folderConfiguration = this._folderConfigurations.get(folder);
|
||||
if (folderConfiguration) {
|
||||
folderConsolidatedConfiguration = workspaceConsolidateConfiguration.merge(folderConfiguration);
|
||||
if (this._freeze) {
|
||||
folderConsolidatedConfiguration = folderConsolidatedConfiguration.freeze();
|
||||
}
|
||||
this._foldersConsolidatedConfigurations.set(folder, folderConsolidatedConfiguration);
|
||||
} else {
|
||||
folderConsolidatedConfiguration = workspaceConsolidateConfiguration;
|
||||
}
|
||||
}
|
||||
return folderConsolidatedConfiguration;
|
||||
}
|
||||
|
||||
private getFolderConfigurationModelForResource(resource: URI | null | undefined, workspace: Workspace | undefined): ConfigurationModel | undefined {
|
||||
if (workspace && resource) {
|
||||
const root = workspace.getFolder(resource);
|
||||
if (root) {
|
||||
return this._folderConfigurations.get(root.uri);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
toData(): IConfigurationData {
|
||||
return {
|
||||
defaults: {
|
||||
contents: this._defaultConfiguration.contents,
|
||||
overrides: this._defaultConfiguration.overrides,
|
||||
keys: this._defaultConfiguration.keys
|
||||
},
|
||||
user: {
|
||||
contents: this.userConfiguration.contents,
|
||||
overrides: this.userConfiguration.overrides,
|
||||
keys: this.userConfiguration.keys
|
||||
},
|
||||
workspace: {
|
||||
contents: this._workspaceConfiguration.contents,
|
||||
overrides: this._workspaceConfiguration.overrides,
|
||||
keys: this._workspaceConfiguration.keys
|
||||
},
|
||||
folders: [...this._folderConfigurations.keys()].reduce<[UriComponents, IConfigurationModel][]>((result, folder) => {
|
||||
const { contents, overrides, keys } = this._folderConfigurations.get(folder)!;
|
||||
result.push([folder, { contents, overrides, keys }]);
|
||||
return result;
|
||||
}, [])
|
||||
};
|
||||
}
|
||||
|
||||
allKeys(): string[] {
|
||||
const keys: Set<string> = new Set<string>();
|
||||
this._defaultConfiguration.freeze().keys.forEach(key => keys.add(key));
|
||||
this.userConfiguration.freeze().keys.forEach(key => keys.add(key));
|
||||
this._workspaceConfiguration.freeze().keys.forEach(key => keys.add(key));
|
||||
this._folderConfigurations.forEach(folderConfiguraiton => folderConfiguraiton.freeze().keys.forEach(key => keys.add(key)));
|
||||
return [...keys.values()];
|
||||
}
|
||||
|
||||
protected getAllKeysForOverrideIdentifier(overrideIdentifier: string): string[] {
|
||||
const keys: Set<string> = new Set<string>();
|
||||
this._defaultConfiguration.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key));
|
||||
this.userConfiguration.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key));
|
||||
this._workspaceConfiguration.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key));
|
||||
this._folderConfigurations.forEach(folderConfiguraiton => folderConfiguraiton.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key)));
|
||||
return [...keys.values()];
|
||||
}
|
||||
|
||||
static parse(data: IConfigurationData): Configuration {
|
||||
const defaultConfiguration = this.parseConfigurationModel(data.defaults);
|
||||
const userConfiguration = this.parseConfigurationModel(data.user);
|
||||
const workspaceConfiguration = this.parseConfigurationModel(data.workspace);
|
||||
const folders: ResourceMap<ConfigurationModel> = data.folders.reduce((result, value) => {
|
||||
result.set(URI.revive(value[0]), this.parseConfigurationModel(value[1]));
|
||||
return result;
|
||||
}, new ResourceMap<ConfigurationModel>());
|
||||
return new Configuration(defaultConfiguration, userConfiguration, new ConfigurationModel(), workspaceConfiguration, folders, new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), false);
|
||||
}
|
||||
|
||||
private static parseConfigurationModel(model: IConfigurationModel): ConfigurationModel {
|
||||
return new ConfigurationModel(model.contents, model.keys, model.overrides).freeze();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export function mergeChanges(...changes: IConfigurationChange[]): IConfigurationChange {
|
||||
if (changes.length === 0) {
|
||||
return { keys: [], overrides: [] };
|
||||
}
|
||||
if (changes.length === 1) {
|
||||
return changes[0];
|
||||
}
|
||||
const keysSet = new Set<string>();
|
||||
const overridesMap = new Map<string, Set<string>>();
|
||||
for (const change of changes) {
|
||||
change.keys.forEach(key => keysSet.add(key));
|
||||
change.overrides.forEach(([identifier, keys]) => {
|
||||
const result = getOrSet(overridesMap, identifier, new Set<string>());
|
||||
keys.forEach(key => result.add(key));
|
||||
});
|
||||
}
|
||||
const overrides: [string, string[]][] = [];
|
||||
overridesMap.forEach((keys, identifier) => overrides.push([identifier, [...keys.values()]]));
|
||||
return { keys: [...keysSet.values()], overrides };
|
||||
}
|
||||
|
||||
export class ConfigurationChangeEvent implements IConfigurationChangeEvent {
|
||||
|
||||
private readonly affectedKeysTree: any;
|
||||
readonly affectedKeys: string[];
|
||||
source!: ConfigurationTarget;
|
||||
sourceConfig: any;
|
||||
|
||||
constructor(readonly change: IConfigurationChange, private readonly previous: { workspace?: Workspace, data: IConfigurationData } | undefined, private readonly currentConfiguraiton: Configuration, private readonly currentWorkspace?: Workspace) {
|
||||
const keysSet = new Set<string>();
|
||||
change.keys.forEach(key => keysSet.add(key));
|
||||
change.overrides.forEach(([, keys]) => keys.forEach(key => keysSet.add(key)));
|
||||
this.affectedKeys = [...keysSet.values()];
|
||||
|
||||
const configurationModel = new ConfigurationModel();
|
||||
this.affectedKeys.forEach(key => configurationModel.setValue(key, {}));
|
||||
this.affectedKeysTree = configurationModel.contents;
|
||||
}
|
||||
|
||||
private _previousConfiguration: Configuration | undefined = undefined;
|
||||
get previousConfiguration(): Configuration | undefined {
|
||||
if (!this._previousConfiguration && this.previous) {
|
||||
this._previousConfiguration = Configuration.parse(this.previous.data);
|
||||
}
|
||||
return this._previousConfiguration;
|
||||
}
|
||||
|
||||
affectsConfiguration(section: string, overrides?: IConfigurationOverrides): boolean {
|
||||
if (this.doesAffectedKeysTreeContains(this.affectedKeysTree, section)) {
|
||||
if (overrides) {
|
||||
const value1 = this.previousConfiguration ? this.previousConfiguration.getValue(section, overrides, this.previous?.workspace) : undefined;
|
||||
const value2 = this.currentConfiguraiton.getValue(section, overrides, this.currentWorkspace);
|
||||
return !objects.equals(value1, value2);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private doesAffectedKeysTreeContains(affectedKeysTree: any, section: string): boolean {
|
||||
let requestedTree = toValuesTree({ [section]: true }, () => { });
|
||||
|
||||
let key;
|
||||
while (typeof requestedTree === 'object' && (key = Object.keys(requestedTree)[0])) { // Only one key should present, since we added only one property
|
||||
affectedKeysTree = affectedKeysTree[key];
|
||||
if (!affectedKeysTree) {
|
||||
return false; // Requested tree is not found
|
||||
}
|
||||
requestedTree = requestedTree[key];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class AllKeysConfigurationChangeEvent extends ConfigurationChangeEvent {
|
||||
constructor(configuration: Configuration, workspace: Workspace, public source: ConfigurationTarget, public sourceConfig: any) {
|
||||
super({ keys: configuration.allKeys(), overrides: [] }, undefined, configuration, workspace);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,499 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
|
||||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
|
||||
export const Extensions = {
|
||||
Configuration: 'base.contributions.configuration'
|
||||
};
|
||||
|
||||
export interface IConfigurationRegistry {
|
||||
|
||||
/**
|
||||
* Register a configuration to the registry.
|
||||
*/
|
||||
registerConfiguration(configuration: IConfigurationNode): void;
|
||||
|
||||
/**
|
||||
* Register multiple configurations to the registry.
|
||||
*/
|
||||
registerConfigurations(configurations: IConfigurationNode[], validate?: boolean): void;
|
||||
|
||||
/**
|
||||
* Deregister multiple configurations from the registry.
|
||||
*/
|
||||
deregisterConfigurations(configurations: IConfigurationNode[]): void;
|
||||
|
||||
/**
|
||||
* Register multiple default configurations to the registry.
|
||||
*/
|
||||
registerDefaultConfigurations(defaultConfigurations: IStringDictionary<any>[]): void;
|
||||
|
||||
/**
|
||||
* Deregister multiple default configurations from the registry.
|
||||
*/
|
||||
deregisterDefaultConfigurations(defaultConfigurations: IStringDictionary<any>[]): 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(...configurations: IConfigurationNode[]): void;
|
||||
|
||||
/**
|
||||
* Event that fires whenver a configuration has been
|
||||
* registered.
|
||||
*/
|
||||
onDidSchemaChange: Event<void>;
|
||||
|
||||
/**
|
||||
* Event that fires whenver a configuration has been
|
||||
* registered.
|
||||
*/
|
||||
onDidUpdateConfiguration: Event<string[]>;
|
||||
|
||||
/**
|
||||
* Returns all configuration nodes contributed to this registry.
|
||||
*/
|
||||
getConfigurations(): IConfigurationNode[];
|
||||
|
||||
/**
|
||||
* Returns all configurations settings of all configuration nodes contributed to this registry.
|
||||
*/
|
||||
getConfigurationProperties(): { [qualifiedKey: string]: IConfigurationPropertySchema };
|
||||
|
||||
/**
|
||||
* Returns all excluded configurations settings of all configuration nodes contributed to this registry.
|
||||
*/
|
||||
getExcludedConfigurationProperties(): { [qualifiedKey: string]: IConfigurationPropertySchema };
|
||||
|
||||
/**
|
||||
* Register the identifiers for editor configurations
|
||||
*/
|
||||
registerOverrideIdentifiers(identifiers: string[]): void;
|
||||
}
|
||||
|
||||
export const enum ConfigurationScope {
|
||||
/**
|
||||
* Application specific configuration, which can be configured only in local user settings.
|
||||
*/
|
||||
APPLICATION = 1,
|
||||
/**
|
||||
* Machine specific configuration, which can be configured only in local and remote user settings.
|
||||
*/
|
||||
MACHINE,
|
||||
/**
|
||||
* Window specific configuration, which can be configured in the user or workspace settings.
|
||||
*/
|
||||
WINDOW,
|
||||
/**
|
||||
* Resource specific configuration, which can be configured in the user, workspace or folder settings.
|
||||
*/
|
||||
RESOURCE,
|
||||
/**
|
||||
* Resource specific configuration that can be configured in language specific settings
|
||||
*/
|
||||
LANGUAGE_OVERRIDABLE,
|
||||
/**
|
||||
* Machine specific configuration that can also be configured in workspace or folder settings.
|
||||
*/
|
||||
MACHINE_OVERRIDABLE,
|
||||
}
|
||||
|
||||
export interface IConfigurationPropertySchema extends IJSONSchema {
|
||||
scope?: ConfigurationScope;
|
||||
included?: boolean;
|
||||
tags?: string[];
|
||||
disallowSyncIgnore?: boolean;
|
||||
enumItemLabels?: string[];
|
||||
}
|
||||
|
||||
export interface IConfigurationExtensionInfo {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface IConfigurationNode {
|
||||
id?: string;
|
||||
order?: number;
|
||||
type?: string | string[];
|
||||
title?: string;
|
||||
description?: string;
|
||||
properties?: { [path: string]: IConfigurationPropertySchema; };
|
||||
allOf?: IConfigurationNode[];
|
||||
scope?: ConfigurationScope;
|
||||
extensionInfo?: IConfigurationExtensionInfo;
|
||||
}
|
||||
|
||||
type SettingProperties = { [key: string]: any };
|
||||
|
||||
export const allSettings: { properties: SettingProperties, patternProperties: SettingProperties } = { properties: {}, patternProperties: {} };
|
||||
export const applicationSettings: { properties: SettingProperties, patternProperties: SettingProperties } = { properties: {}, patternProperties: {} };
|
||||
export const machineSettings: { properties: SettingProperties, patternProperties: SettingProperties } = { properties: {}, patternProperties: {} };
|
||||
export const machineOverridableSettings: { properties: SettingProperties, patternProperties: SettingProperties } = { properties: {}, patternProperties: {} };
|
||||
export const windowSettings: { properties: SettingProperties, patternProperties: SettingProperties } = { properties: {}, patternProperties: {} };
|
||||
export const resourceSettings: { properties: SettingProperties, patternProperties: SettingProperties } = { properties: {}, patternProperties: {} };
|
||||
|
||||
export const resourceLanguageSettingsSchemaId = 'vscode://schemas/settings/resourceLanguage';
|
||||
|
||||
const contributionRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
|
||||
|
||||
class ConfigurationRegistry implements IConfigurationRegistry {
|
||||
|
||||
private readonly defaultValues: IStringDictionary<any>;
|
||||
private readonly defaultLanguageConfigurationOverridesNode: IConfigurationNode;
|
||||
private readonly configurationContributors: IConfigurationNode[];
|
||||
private readonly configurationProperties: { [qualifiedKey: string]: IJSONSchema };
|
||||
private readonly excludedConfigurationProperties: { [qualifiedKey: string]: IJSONSchema };
|
||||
private readonly resourceLanguageSettingsSchema: IJSONSchema;
|
||||
private readonly overrideIdentifiers = new Set<string>();
|
||||
|
||||
private readonly _onDidSchemaChange = new Emitter<void>();
|
||||
readonly onDidSchemaChange: Event<void> = this._onDidSchemaChange.event;
|
||||
|
||||
private readonly _onDidUpdateConfiguration: Emitter<string[]> = new Emitter<string[]>();
|
||||
readonly onDidUpdateConfiguration: Event<string[]> = this._onDidUpdateConfiguration.event;
|
||||
|
||||
constructor() {
|
||||
this.defaultValues = {};
|
||||
this.defaultLanguageConfigurationOverridesNode = {
|
||||
id: 'defaultOverrides',
|
||||
title: nls.localize('defaultLanguageConfigurationOverrides.title', "Default Language Configuration Overrides"),
|
||||
properties: {}
|
||||
};
|
||||
this.configurationContributors = [this.defaultLanguageConfigurationOverridesNode];
|
||||
this.resourceLanguageSettingsSchema = { properties: {}, patternProperties: {}, additionalProperties: false, errorMessage: 'Unknown editor configuration setting', allowTrailingCommas: true, allowComments: true };
|
||||
this.configurationProperties = {};
|
||||
this.excludedConfigurationProperties = {};
|
||||
|
||||
contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema);
|
||||
}
|
||||
|
||||
public registerConfiguration(configuration: IConfigurationNode, validate: boolean = true): void {
|
||||
this.registerConfigurations([configuration], validate);
|
||||
}
|
||||
|
||||
public registerConfigurations(configurations: IConfigurationNode[], validate: boolean = true): void {
|
||||
const properties: string[] = [];
|
||||
configurations.forEach(configuration => {
|
||||
properties.push(...this.validateAndRegisterProperties(configuration, validate)); // fills in defaults
|
||||
this.configurationContributors.push(configuration);
|
||||
this.registerJSONConfiguration(configuration);
|
||||
});
|
||||
|
||||
contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema);
|
||||
this._onDidSchemaChange.fire();
|
||||
this._onDidUpdateConfiguration.fire(properties);
|
||||
}
|
||||
|
||||
public deregisterConfigurations(configurations: IConfigurationNode[]): void {
|
||||
const properties: string[] = [];
|
||||
const deregisterConfiguration = (configuration: IConfigurationNode) => {
|
||||
if (configuration.properties) {
|
||||
for (const key in configuration.properties) {
|
||||
properties.push(key);
|
||||
delete this.configurationProperties[key];
|
||||
this.removeFromSchema(key, configuration.properties[key]);
|
||||
}
|
||||
}
|
||||
if (configuration.allOf) {
|
||||
configuration.allOf.forEach(node => deregisterConfiguration(node));
|
||||
}
|
||||
};
|
||||
for (const configuration of configurations) {
|
||||
deregisterConfiguration(configuration);
|
||||
const index = this.configurationContributors.indexOf(configuration);
|
||||
if (index !== -1) {
|
||||
this.configurationContributors.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema);
|
||||
this._onDidSchemaChange.fire();
|
||||
this._onDidUpdateConfiguration.fire(properties);
|
||||
}
|
||||
|
||||
public registerDefaultConfigurations(defaultConfigurations: IStringDictionary<any>[]): void {
|
||||
const properties: string[] = [];
|
||||
const overrideIdentifiers: string[] = [];
|
||||
|
||||
for (const defaultConfiguration of defaultConfigurations) {
|
||||
for (const key in defaultConfiguration) {
|
||||
properties.push(key);
|
||||
|
||||
if (OVERRIDE_PROPERTY_PATTERN.test(key)) {
|
||||
this.defaultValues[key] = { ...(this.defaultValues[key] || {}), ...defaultConfiguration[key] };
|
||||
const property: IConfigurationPropertySchema = {
|
||||
type: 'object',
|
||||
default: this.defaultValues[key],
|
||||
description: nls.localize('defaultLanguageConfiguration.description', "Configure settings to be overridden for {0} language.", key),
|
||||
$ref: resourceLanguageSettingsSchemaId
|
||||
};
|
||||
overrideIdentifiers.push(overrideIdentifierFromKey(key));
|
||||
this.configurationProperties[key] = property;
|
||||
this.defaultLanguageConfigurationOverridesNode.properties![key] = property;
|
||||
} else {
|
||||
this.defaultValues[key] = defaultConfiguration[key];
|
||||
const property = this.configurationProperties[key];
|
||||
if (property) {
|
||||
this.updatePropertyDefaultValue(key, property);
|
||||
this.updateSchema(key, property);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.registerOverrideIdentifiers(overrideIdentifiers);
|
||||
this._onDidSchemaChange.fire();
|
||||
this._onDidUpdateConfiguration.fire(properties);
|
||||
}
|
||||
|
||||
public deregisterDefaultConfigurations(defaultConfigurations: IStringDictionary<any>[]): void {
|
||||
const properties: string[] = [];
|
||||
for (const defaultConfiguration of defaultConfigurations) {
|
||||
for (const key in defaultConfiguration) {
|
||||
properties.push(key);
|
||||
delete this.defaultValues[key];
|
||||
if (OVERRIDE_PROPERTY_PATTERN.test(key)) {
|
||||
delete this.configurationProperties[key];
|
||||
delete this.defaultLanguageConfigurationOverridesNode.properties![key];
|
||||
} else {
|
||||
const property = this.configurationProperties[key];
|
||||
if (property) {
|
||||
this.updatePropertyDefaultValue(key, property);
|
||||
this.updateSchema(key, property);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.updateOverridePropertyPatternKey();
|
||||
this._onDidSchemaChange.fire();
|
||||
this._onDidUpdateConfiguration.fire(properties);
|
||||
}
|
||||
|
||||
public notifyConfigurationSchemaUpdated(...configurations: IConfigurationNode[]) {
|
||||
this._onDidSchemaChange.fire();
|
||||
}
|
||||
|
||||
public registerOverrideIdentifiers(overrideIdentifiers: string[]): void {
|
||||
for (const overrideIdentifier of overrideIdentifiers) {
|
||||
this.overrideIdentifiers.add(overrideIdentifier);
|
||||
}
|
||||
this.updateOverridePropertyPatternKey();
|
||||
}
|
||||
|
||||
private validateAndRegisterProperties(configuration: IConfigurationNode, validate: boolean = true, scope: ConfigurationScope = ConfigurationScope.WINDOW): string[] {
|
||||
scope = types.isUndefinedOrNull(configuration.scope) ? scope : configuration.scope;
|
||||
let propertyKeys: string[] = [];
|
||||
let properties = configuration.properties;
|
||||
if (properties) {
|
||||
for (let key in properties) {
|
||||
if (validate && validateProperty(key)) {
|
||||
delete properties[key];
|
||||
continue;
|
||||
}
|
||||
|
||||
const property = properties[key];
|
||||
|
||||
// update default value
|
||||
this.updatePropertyDefaultValue(key, property);
|
||||
|
||||
// update scope
|
||||
if (OVERRIDE_PROPERTY_PATTERN.test(key)) {
|
||||
property.scope = undefined; // No scope for overridable properties `[${identifier}]`
|
||||
} else {
|
||||
property.scope = types.isUndefinedOrNull(property.scope) ? scope : property.scope;
|
||||
}
|
||||
|
||||
// Add to properties maps
|
||||
// Property is included by default if 'included' is unspecified
|
||||
if (properties[key].hasOwnProperty('included') && !properties[key].included) {
|
||||
this.excludedConfigurationProperties[key] = properties[key];
|
||||
delete properties[key];
|
||||
continue;
|
||||
} else {
|
||||
this.configurationProperties[key] = properties[key];
|
||||
}
|
||||
|
||||
if (!properties[key].deprecationMessage && properties[key].markdownDeprecationMessage) {
|
||||
// If not set, default deprecationMessage to the markdown source
|
||||
properties[key].deprecationMessage = properties[key].markdownDeprecationMessage;
|
||||
}
|
||||
|
||||
propertyKeys.push(key);
|
||||
}
|
||||
}
|
||||
let subNodes = configuration.allOf;
|
||||
if (subNodes) {
|
||||
for (let node of subNodes) {
|
||||
propertyKeys.push(...this.validateAndRegisterProperties(node, validate, scope));
|
||||
}
|
||||
}
|
||||
return propertyKeys;
|
||||
}
|
||||
|
||||
getConfigurations(): IConfigurationNode[] {
|
||||
return this.configurationContributors;
|
||||
}
|
||||
|
||||
getConfigurationProperties(): { [qualifiedKey: string]: IConfigurationPropertySchema } {
|
||||
return this.configurationProperties;
|
||||
}
|
||||
|
||||
getExcludedConfigurationProperties(): { [qualifiedKey: string]: IConfigurationPropertySchema } {
|
||||
return this.excludedConfigurationProperties;
|
||||
}
|
||||
|
||||
private registerJSONConfiguration(configuration: IConfigurationNode) {
|
||||
const register = (configuration: IConfigurationNode) => {
|
||||
let properties = configuration.properties;
|
||||
if (properties) {
|
||||
for (const key in properties) {
|
||||
this.updateSchema(key, properties[key]);
|
||||
}
|
||||
}
|
||||
let subNodes = configuration.allOf;
|
||||
if (subNodes) {
|
||||
subNodes.forEach(register);
|
||||
}
|
||||
};
|
||||
register(configuration);
|
||||
}
|
||||
|
||||
private updateSchema(key: string, property: IConfigurationPropertySchema): void {
|
||||
allSettings.properties[key] = property;
|
||||
switch (property.scope) {
|
||||
case ConfigurationScope.APPLICATION:
|
||||
applicationSettings.properties[key] = property;
|
||||
break;
|
||||
case ConfigurationScope.MACHINE:
|
||||
machineSettings.properties[key] = property;
|
||||
break;
|
||||
case ConfigurationScope.MACHINE_OVERRIDABLE:
|
||||
machineOverridableSettings.properties[key] = property;
|
||||
break;
|
||||
case ConfigurationScope.WINDOW:
|
||||
windowSettings.properties[key] = property;
|
||||
break;
|
||||
case ConfigurationScope.RESOURCE:
|
||||
resourceSettings.properties[key] = property;
|
||||
break;
|
||||
case ConfigurationScope.LANGUAGE_OVERRIDABLE:
|
||||
resourceSettings.properties[key] = property;
|
||||
this.resourceLanguageSettingsSchema.properties![key] = property;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private removeFromSchema(key: string, property: IConfigurationPropertySchema): void {
|
||||
delete allSettings.properties[key];
|
||||
switch (property.scope) {
|
||||
case ConfigurationScope.APPLICATION:
|
||||
delete applicationSettings.properties[key];
|
||||
break;
|
||||
case ConfigurationScope.MACHINE:
|
||||
delete machineSettings.properties[key];
|
||||
break;
|
||||
case ConfigurationScope.MACHINE_OVERRIDABLE:
|
||||
delete machineOverridableSettings.properties[key];
|
||||
break;
|
||||
case ConfigurationScope.WINDOW:
|
||||
delete windowSettings.properties[key];
|
||||
break;
|
||||
case ConfigurationScope.RESOURCE:
|
||||
case ConfigurationScope.LANGUAGE_OVERRIDABLE:
|
||||
delete resourceSettings.properties[key];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private updateOverridePropertyPatternKey(): void {
|
||||
for (const overrideIdentifier of this.overrideIdentifiers.values()) {
|
||||
const overrideIdentifierProperty = `[${overrideIdentifier}]`;
|
||||
const resourceLanguagePropertiesSchema: IJSONSchema = {
|
||||
type: 'object',
|
||||
description: nls.localize('overrideSettings.defaultDescription', "Configure editor settings to be overridden for a language."),
|
||||
errorMessage: nls.localize('overrideSettings.errorMessage', "This setting does not support per-language configuration."),
|
||||
$ref: resourceLanguageSettingsSchemaId,
|
||||
};
|
||||
this.updatePropertyDefaultValue(overrideIdentifierProperty, resourceLanguagePropertiesSchema);
|
||||
allSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;
|
||||
applicationSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;
|
||||
machineSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;
|
||||
machineOverridableSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;
|
||||
windowSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;
|
||||
resourceSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;
|
||||
}
|
||||
this._onDidSchemaChange.fire();
|
||||
}
|
||||
|
||||
private updatePropertyDefaultValue(key: string, property: IConfigurationPropertySchema): void {
|
||||
let defaultValue = this.defaultValues[key];
|
||||
if (types.isUndefined(defaultValue)) {
|
||||
defaultValue = property.default;
|
||||
}
|
||||
if (types.isUndefined(defaultValue)) {
|
||||
defaultValue = getDefaultValue(property.type);
|
||||
}
|
||||
property.default = defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
const OVERRIDE_PROPERTY = '\\[.*\\]$';
|
||||
export const OVERRIDE_PROPERTY_PATTERN = new RegExp(OVERRIDE_PROPERTY);
|
||||
|
||||
export function overrideIdentifierFromKey(key: string): string {
|
||||
return key.substring(1, key.length - 1);
|
||||
}
|
||||
|
||||
export function getDefaultValue(type: string | string[] | undefined): any {
|
||||
const t = Array.isArray(type) ? (<string[]>type)[0] : <string>type;
|
||||
switch (t) {
|
||||
case 'boolean':
|
||||
return false;
|
||||
case 'integer':
|
||||
case 'number':
|
||||
return 0;
|
||||
case 'string':
|
||||
return '';
|
||||
case 'array':
|
||||
return [];
|
||||
case 'object':
|
||||
return {};
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const configurationRegistry = new ConfigurationRegistry();
|
||||
Registry.add(Extensions.Configuration, configurationRegistry);
|
||||
|
||||
export function validateProperty(property: string): string | null {
|
||||
if (OVERRIDE_PROPERTY_PATTERN.test(property)) {
|
||||
return nls.localize('config.property.languageDefault', "Cannot register '{0}'. This matches property pattern '\\\\[.*\\\\]$' for describing language specific editor settings. Use 'configurationDefaults' contribution.", property);
|
||||
}
|
||||
if (configurationRegistry.getConfigurationProperties()[property] !== undefined) {
|
||||
return nls.localize('config.property.duplicate', "Cannot register '{0}'. This property is already registered.", property);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getScopes(): [string, ConfigurationScope | undefined][] {
|
||||
const scopes: [string, ConfigurationScope | undefined][] = [];
|
||||
const configurationProperties = configurationRegistry.getConfigurationProperties();
|
||||
for (const key of Object.keys(configurationProperties)) {
|
||||
scopes.push([key, configurationProperties[key].scope]);
|
||||
}
|
||||
scopes.push(['launch', ConfigurationScope.RESOURCE]);
|
||||
scopes.push(['task', ConfigurationScope.RESOURCE]);
|
||||
return scopes;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IConfigurationService, IConfigurationChangeEvent, IConfigurationOverrides, ConfigurationTarget, isConfigurationOverrides, IConfigurationData, IConfigurationValue, IConfigurationChange } from 'vs/platform/configuration/common/configuration';
|
||||
import { DefaultConfigurationModel, Configuration, ConfigurationModel, ConfigurationChangeEvent, UserSettings } from 'vs/platform/configuration/common/configurationModels';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
|
||||
export class ConfigurationService extends Disposable implements IConfigurationService, IDisposable {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
private configuration: Configuration;
|
||||
private userConfiguration: UserSettings;
|
||||
private readonly reloadConfigurationScheduler: RunOnceScheduler;
|
||||
|
||||
private readonly _onDidChangeConfiguration: Emitter<IConfigurationChangeEvent> = this._register(new Emitter<IConfigurationChangeEvent>());
|
||||
readonly onDidChangeConfiguration: Event<IConfigurationChangeEvent> = this._onDidChangeConfiguration.event;
|
||||
|
||||
constructor(
|
||||
private readonly settingsResource: URI,
|
||||
fileService: IFileService
|
||||
) {
|
||||
super();
|
||||
this.userConfiguration = this._register(new UserSettings(this.settingsResource, undefined, fileService));
|
||||
this.configuration = new Configuration(new DefaultConfigurationModel(), new ConfigurationModel());
|
||||
|
||||
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reloadConfiguration(), 50));
|
||||
this._register(Registry.as<IConfigurationRegistry>(Extensions.Configuration).onDidUpdateConfiguration(configurationProperties => this.onDidDefaultConfigurationChange(configurationProperties)));
|
||||
this._register(this.userConfiguration.onDidChange(() => this.reloadConfigurationScheduler.schedule()));
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
const userConfiguration = await this.userConfiguration.loadConfiguration();
|
||||
this.configuration = new Configuration(new DefaultConfigurationModel(), userConfiguration);
|
||||
}
|
||||
|
||||
getConfigurationData(): IConfigurationData {
|
||||
return this.configuration.toData();
|
||||
}
|
||||
|
||||
getValue<T>(): T;
|
||||
getValue<T>(section: string): T;
|
||||
getValue<T>(overrides: IConfigurationOverrides): T;
|
||||
getValue<T>(section: string, overrides: IConfigurationOverrides): T;
|
||||
getValue(arg1?: any, arg2?: any): any {
|
||||
const section = typeof arg1 === 'string' ? arg1 : undefined;
|
||||
const overrides = isConfigurationOverrides(arg1) ? arg1 : isConfigurationOverrides(arg2) ? arg2 : {};
|
||||
return this.configuration.getValue(section, overrides, undefined);
|
||||
}
|
||||
|
||||
updateValue(key: string, value: any): Promise<void>;
|
||||
updateValue(key: string, value: any, overrides: IConfigurationOverrides): Promise<void>;
|
||||
updateValue(key: string, value: any, target: ConfigurationTarget): Promise<void>;
|
||||
updateValue(key: string, value: any, overrides: IConfigurationOverrides, target: ConfigurationTarget): Promise<void>;
|
||||
updateValue(key: string, value: any, arg3?: any, arg4?: any): Promise<void> {
|
||||
return Promise.reject(new Error('not supported'));
|
||||
}
|
||||
|
||||
inspect<T>(key: string): IConfigurationValue<T> {
|
||||
return this.configuration.inspect<T>(key, {}, undefined);
|
||||
}
|
||||
|
||||
keys(): {
|
||||
default: string[];
|
||||
user: string[];
|
||||
workspace: string[];
|
||||
workspaceFolder: string[];
|
||||
} {
|
||||
return this.configuration.keys(undefined);
|
||||
}
|
||||
|
||||
async reloadConfiguration(): Promise<void> {
|
||||
const configurationModel = await this.userConfiguration.loadConfiguration();
|
||||
this.onDidChangeUserConfiguration(configurationModel);
|
||||
}
|
||||
|
||||
private onDidChangeUserConfiguration(userConfigurationModel: ConfigurationModel): void {
|
||||
const previous = this.configuration.toData();
|
||||
const change = this.configuration.compareAndUpdateLocalUserConfiguration(userConfigurationModel);
|
||||
this.trigger(change, previous, ConfigurationTarget.USER);
|
||||
}
|
||||
|
||||
private onDidDefaultConfigurationChange(keys: string[]): void {
|
||||
const previous = this.configuration.toData();
|
||||
const change = this.configuration.compareAndUpdateDefaultConfiguration(new DefaultConfigurationModel(), keys);
|
||||
this.trigger(change, previous, ConfigurationTarget.DEFAULT);
|
||||
}
|
||||
|
||||
private trigger(configurationChange: IConfigurationChange, previous: IConfigurationData, source: ConfigurationTarget): void {
|
||||
const event = new ConfigurationChangeEvent(configurationChange, { data: previous }, this.configuration);
|
||||
event.source = source;
|
||||
event.sourceConfig = this.getTargetConfiguration(source);
|
||||
this._onDidChangeConfiguration.fire(event);
|
||||
}
|
||||
|
||||
private getTargetConfiguration(target: ConfigurationTarget): any {
|
||||
switch (target) {
|
||||
case ConfigurationTarget.DEFAULT:
|
||||
return this.configuration.defaults.contents;
|
||||
case ConfigurationTarget.USER:
|
||||
return this.configuration.localUserConfiguration.contents;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as assert from 'assert';
|
||||
import { merge, removeFromValueTree } from 'vs/platform/configuration/common/configuration';
|
||||
import { mergeChanges } from 'vs/platform/configuration/common/configurationModels';
|
||||
|
||||
suite('Configuration', () => {
|
||||
|
||||
test('simple merge', () => {
|
||||
let base = { 'a': 1, 'b': 2 };
|
||||
merge(base, { 'a': 3, 'c': 4 }, true);
|
||||
assert.deepEqual(base, { 'a': 3, 'b': 2, 'c': 4 });
|
||||
base = { 'a': 1, 'b': 2 };
|
||||
merge(base, { 'a': 3, 'c': 4 }, false);
|
||||
assert.deepEqual(base, { 'a': 1, 'b': 2, 'c': 4 });
|
||||
});
|
||||
|
||||
test('removeFromValueTree: remove a non existing key', () => {
|
||||
let target = { 'a': { 'b': 2 } };
|
||||
|
||||
removeFromValueTree(target, 'c');
|
||||
|
||||
assert.deepEqual(target, { 'a': { 'b': 2 } });
|
||||
});
|
||||
|
||||
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('removeFromValueTree: remove a single segmented key', () => {
|
||||
let target = { 'a': 1 };
|
||||
|
||||
removeFromValueTree(target, 'a');
|
||||
|
||||
assert.deepEqual(target, {});
|
||||
});
|
||||
|
||||
test('removeFromValueTree: remove a single segmented key when its value is undefined', () => {
|
||||
let target = { 'a': undefined };
|
||||
|
||||
removeFromValueTree(target, 'a');
|
||||
|
||||
assert.deepEqual(target, {});
|
||||
});
|
||||
|
||||
test('removeFromValueTree: remove a multi segmented key when its value is undefined', () => {
|
||||
let target = { 'a': { 'b': 1 } };
|
||||
|
||||
removeFromValueTree(target, 'a.b');
|
||||
|
||||
assert.deepEqual(target, {});
|
||||
});
|
||||
|
||||
test('removeFromValueTree: remove a multi segmented key when its value is array', () => {
|
||||
let target = { 'a': { 'b': [1] } };
|
||||
|
||||
removeFromValueTree(target, 'a.b');
|
||||
|
||||
assert.deepEqual(target, {});
|
||||
});
|
||||
|
||||
test('removeFromValueTree: remove a multi segmented 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 segmented 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 segmented 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 segmented 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 } } });
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('Configuration Changes: Merge', () => {
|
||||
|
||||
test('merge only keys', () => {
|
||||
const actual = mergeChanges({ keys: ['a', 'b'], overrides: [] }, { keys: ['c', 'd'], overrides: [] });
|
||||
assert.deepEqual(actual, { keys: ['a', 'b', 'c', 'd'], overrides: [] });
|
||||
});
|
||||
|
||||
test('merge only keys with duplicates', () => {
|
||||
const actual = mergeChanges({ keys: ['a', 'b'], overrides: [] }, { keys: ['c', 'd'], overrides: [] }, { keys: ['a', 'd', 'e'], overrides: [] });
|
||||
assert.deepEqual(actual, { keys: ['a', 'b', 'c', 'd', 'e'], overrides: [] });
|
||||
});
|
||||
|
||||
test('merge only overrides', () => {
|
||||
const actual = mergeChanges({ keys: [], overrides: [['a', ['1', '2']]] }, { keys: [], overrides: [['b', ['3', '4']]] });
|
||||
assert.deepEqual(actual, { keys: [], overrides: [['a', ['1', '2']], ['b', ['3', '4']]] });
|
||||
});
|
||||
|
||||
test('merge only overrides with duplicates', () => {
|
||||
const actual = mergeChanges({ keys: [], overrides: [['a', ['1', '2']], ['b', ['5', '4']]] }, { keys: [], overrides: [['b', ['3', '4']]] }, { keys: [], overrides: [['c', ['1', '4']], ['a', ['2', '3']]] });
|
||||
assert.deepEqual(actual, { keys: [], overrides: [['a', ['1', '2', '3']], ['b', ['5', '4', '3']], ['c', ['1', '4']]] });
|
||||
});
|
||||
|
||||
test('merge', () => {
|
||||
const actual = mergeChanges({ keys: ['b', 'b'], overrides: [['a', ['1', '2']], ['b', ['5', '4']]] }, { keys: ['b'], overrides: [['b', ['3', '4']]] }, { keys: ['c', 'a'], overrides: [['c', ['1', '4']], ['a', ['2', '3']]] });
|
||||
assert.deepEqual(actual, { keys: ['b', 'c', 'a'], overrides: [['a', ['1', '2', '3']], ['b', ['5', '4', '3']], ['c', ['1', '4']]] });
|
||||
});
|
||||
|
||||
test('merge single change', () => {
|
||||
const actual = mergeChanges({ keys: ['b', 'b'], overrides: [['a', ['1', '2']], ['b', ['5', '4']]] });
|
||||
assert.deepEqual(actual, { keys: ['b', 'b'], overrides: [['a', ['1', '2']], ['b', ['5', '4']]] });
|
||||
});
|
||||
|
||||
test('merge no changes', () => {
|
||||
const actual = mergeChanges();
|
||||
assert.deepEqual(actual, { keys: [], overrides: [] });
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,950 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as assert from 'assert';
|
||||
import { ConfigurationModel, DefaultConfigurationModel, ConfigurationChangeEvent, ConfigurationModelParser, Configuration, mergeChanges, 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 { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { join } from 'vs/base/common/path';
|
||||
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 segmented key', () => {
|
||||
let testObject = new ConfigurationModel({ 'a': 1 }, ['a']);
|
||||
|
||||
testObject.removeValue('a');
|
||||
|
||||
assert.deepEqual(testObject.contents, {});
|
||||
assert.deepEqual(testObject.keys, []);
|
||||
});
|
||||
|
||||
test('removeValue: remove a multi segmented key', () => {
|
||||
let testObject = new ConfigurationModel({ 'a': { 'b': 1 } }, ['a.b']);
|
||||
|
||||
testObject.removeValue('a.b');
|
||||
|
||||
assert.deepEqual(testObject.contents, {});
|
||||
assert.deepEqual(testObject.keys, []);
|
||||
});
|
||||
|
||||
test('get overriding configuration model for an existing identifier', () => {
|
||||
let testObject = new ConfigurationModel(
|
||||
{ 'a': { 'b': 1 }, 'f': 1 }, [],
|
||||
[{ identifiers: ['c'], contents: { 'a': { 'd': 1 } }, keys: ['a'] }]);
|
||||
|
||||
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 } }, keys: ['a'] }]);
|
||||
|
||||
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 }, keys: ['a', 'g'] }]);
|
||||
|
||||
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 } }, keys: ['a', 'f'] }]);
|
||||
|
||||
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 }, keys: ['a', 'f'] }]);
|
||||
|
||||
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', keys: [] }]);
|
||||
|
||||
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: {}, keys: [] }]);
|
||||
|
||||
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.getValue('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 }, keys: ['a'] }]);
|
||||
let add = new ConfigurationModel({ 'a': { 'b': 2 } }, ['a.b'], [{ identifiers: ['c'], contents: { 'b': 2 }, keys: ['b'] }]);
|
||||
let result = base.merge(add);
|
||||
|
||||
assert.deepEqual(result.contents, { 'a': { 'b': 2 } });
|
||||
assert.deepEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': 2, 'b': 2 }, keys: ['a'] }]);
|
||||
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 } }, keys: ['a'] }]);
|
||||
let add = new ConfigurationModel({ 'a': { 'b': 2 } }, ['a.b'], [{ identifiers: ['c'], contents: { 'a': { 'e': 2 } }, keys: ['a'] }]);
|
||||
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 } }, keys: ['a'] }]);
|
||||
assert.deepEqual(result.override('c').contents, { 'a': { 'b': 2, 'd': 1, 'e': 2 }, 'f': 1 });
|
||||
assert.deepEqual(result.keys, ['a.b', 'f']);
|
||||
});
|
||||
|
||||
test('merge overrides when frozen', () => {
|
||||
let model1 = new ConfigurationModel({ 'a': { 'b': 1 }, 'f': 1 }, ['a.b', 'f'], [{ identifiers: ['c'], contents: { 'a': { 'd': 1 } }, keys: ['a'] }]).freeze();
|
||||
let model2 = new ConfigurationModel({ 'a': { 'b': 2 } }, ['a.b'], [{ identifiers: ['c'], contents: { 'a': { 'e': 2 } }, keys: ['a'] }]).freeze();
|
||||
let result = new ConfigurationModel().merge(model1, model2);
|
||||
|
||||
assert.deepEqual(result.contents, { 'a': { 'b': 2 }, 'f': 1 });
|
||||
assert.deepEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': { 'd': 1, 'e': 2 } }, keys: ['a'] }]);
|
||||
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.getValue('a'), 1);
|
||||
|
||||
testObject = new ConfigurationModel({ 'a': { 'b': 1 } });
|
||||
assert.deepEqual(testObject.getValue('a'), { 'b': 1 });
|
||||
});
|
||||
|
||||
test('Test contents are undefined for non existing properties', () => {
|
||||
const testObject = new ConfigurationModel({ awesome: true });
|
||||
|
||||
assert.deepEqual(testObject.getValue('unknownproperty'), undefined);
|
||||
});
|
||||
|
||||
test('Test override gives all content merged with overrides', () => {
|
||||
const testObject = new ConfigurationModel({ 'a': 1, 'c': 1 }, [], [{ identifiers: ['b'], contents: { 'a': 2 }, keys: ['a'] }]);
|
||||
|
||||
assert.deepEqual(testObject.override('b').contents, { 'a': 2, 'c': 1 });
|
||||
});
|
||||
});
|
||||
|
||||
suite('CustomConfigurationModel', () => {
|
||||
|
||||
test('simple merge using models', () => {
|
||||
let base = new ConfigurationModelParser('base');
|
||||
base.parseContent(JSON.stringify({ 'a': 1, 'b': 2 }));
|
||||
|
||||
let add = new ConfigurationModelParser('add');
|
||||
add.parseContent(JSON.stringify({ 'a': 3, 'c': 4 }));
|
||||
|
||||
let result = base.configurationModel.merge(add.configurationModel);
|
||||
assert.deepEqual(result.contents, { 'a': 3, 'b': 2, 'c': 4 });
|
||||
});
|
||||
|
||||
test('simple merge with an undefined contents', () => {
|
||||
let base = new ConfigurationModelParser('base');
|
||||
base.parseContent(JSON.stringify({ 'a': 1, 'b': 2 }));
|
||||
let add = new ConfigurationModelParser('add');
|
||||
let result = base.configurationModel.merge(add.configurationModel);
|
||||
assert.deepEqual(result.contents, { 'a': 1, 'b': 2 });
|
||||
|
||||
base = new ConfigurationModelParser('base');
|
||||
add = new ConfigurationModelParser('add');
|
||||
add.parseContent(JSON.stringify({ 'a': 1, 'b': 2 }));
|
||||
result = base.configurationModel.merge(add.configurationModel);
|
||||
assert.deepEqual(result.contents, { 'a': 1, 'b': 2 });
|
||||
|
||||
base = new ConfigurationModelParser('base');
|
||||
add = new ConfigurationModelParser('add');
|
||||
result = base.configurationModel.merge(add.configurationModel);
|
||||
assert.deepEqual(result.contents, {});
|
||||
});
|
||||
|
||||
test('Recursive merge using config models', () => {
|
||||
let base = new ConfigurationModelParser('base');
|
||||
base.parseContent(JSON.stringify({ 'a': { 'b': 1 } }));
|
||||
let add = new ConfigurationModelParser('add');
|
||||
add.parseContent(JSON.stringify({ 'a': { 'b': 2 } }));
|
||||
let result = base.configurationModel.merge(add.configurationModel);
|
||||
assert.deepEqual(result.contents, { 'a': { 'b': 2 } });
|
||||
});
|
||||
|
||||
test('Test contents while getting an existing property', () => {
|
||||
let testObject = new ConfigurationModelParser('test');
|
||||
testObject.parseContent(JSON.stringify({ 'a': 1 }));
|
||||
assert.deepEqual(testObject.configurationModel.getValue('a'), 1);
|
||||
|
||||
testObject.parseContent(JSON.stringify({ 'a': { 'b': 1 } }));
|
||||
assert.deepEqual(testObject.configurationModel.getValue('a'), { 'b': 1 });
|
||||
});
|
||||
|
||||
test('Test contents are undefined for non existing properties', () => {
|
||||
const testObject = new ConfigurationModelParser('test');
|
||||
testObject.parseContent(JSON.stringify({
|
||||
awesome: true
|
||||
}));
|
||||
|
||||
assert.deepEqual(testObject.configurationModel.getValue('unknownproperty'), undefined);
|
||||
});
|
||||
|
||||
test('Test contents are undefined for undefined config', () => {
|
||||
const testObject = new ConfigurationModelParser('test');
|
||||
|
||||
assert.deepEqual(testObject.configurationModel.getValue('unknownproperty'), undefined);
|
||||
});
|
||||
|
||||
test('Test configWithOverrides gives all content merged with overrides', () => {
|
||||
const testObject = new ConfigurationModelParser('test');
|
||||
testObject.parseContent(JSON.stringify({ 'a': 1, 'c': 1, '[b]': { 'a': 2 } }));
|
||||
|
||||
assert.deepEqual(testObject.configurationModel.override('b').contents, { 'a': 2, 'c': 1, '[b]': { 'a': 2 } });
|
||||
});
|
||||
|
||||
test('Test configWithOverrides gives empty contents', () => {
|
||||
const testObject = new ConfigurationModelParser('test');
|
||||
|
||||
assert.deepEqual(testObject.configurationModel.override('b').contents, {});
|
||||
});
|
||||
|
||||
test('Test update with empty data', () => {
|
||||
const testObject = new ConfigurationModelParser('test');
|
||||
testObject.parseContent('');
|
||||
|
||||
assert.deepEqual(testObject.configurationModel.contents, {});
|
||||
assert.deepEqual(testObject.configurationModel.keys, []);
|
||||
|
||||
testObject.parseContent(null!);
|
||||
|
||||
assert.deepEqual(testObject.configurationModel.contents, {});
|
||||
assert.deepEqual(testObject.configurationModel.keys, []);
|
||||
|
||||
testObject.parseContent(undefined!);
|
||||
|
||||
assert.deepEqual(testObject.configurationModel.contents, {});
|
||||
assert.deepEqual(testObject.configurationModel.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': true,
|
||||
}
|
||||
}
|
||||
});
|
||||
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().getValue('a'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('Configuration', () => {
|
||||
|
||||
test('Test inspect for overrideIdentifiers', () => {
|
||||
const defaultConfigurationModel = parseConfigurationModel({ '[l1]': { 'a': 1 }, '[l2]': { 'b': 1 } });
|
||||
const userConfigurationModel = parseConfigurationModel({ '[l3]': { 'a': 2 } });
|
||||
const workspaceConfigurationModel = parseConfigurationModel({ '[l1]': { 'a': 3 }, '[l4]': { 'a': 3 } });
|
||||
const testObject: Configuration = new Configuration(defaultConfigurationModel, userConfigurationModel, new ConfigurationModel(), workspaceConfigurationModel);
|
||||
|
||||
const { overrideIdentifiers } = testObject.inspect('a', {}, undefined);
|
||||
|
||||
assert.deepEqual(overrideIdentifiers, ['l1', 'l3', 'l4']);
|
||||
});
|
||||
|
||||
test('Test update value', () => {
|
||||
const parser = new ConfigurationModelParser('test');
|
||||
parser.parseContent(JSON.stringify({ 'a': 1 }));
|
||||
const testObject: Configuration = new Configuration(parser.configurationModel, new ConfigurationModel());
|
||||
|
||||
testObject.updateValue('a', 2);
|
||||
|
||||
assert.equal(testObject.getValue('a', {}, undefined), 2);
|
||||
});
|
||||
|
||||
test('Test update value after inspect', () => {
|
||||
const parser = new ConfigurationModelParser('test');
|
||||
parser.parseContent(JSON.stringify({ 'a': 1 }));
|
||||
const testObject: Configuration = new Configuration(parser.configurationModel, new ConfigurationModel());
|
||||
|
||||
testObject.inspect('a', {}, undefined);
|
||||
testObject.updateValue('a', 2);
|
||||
|
||||
assert.equal(testObject.getValue('a', {}, undefined), 2);
|
||||
});
|
||||
|
||||
test('Test compare and update default configuration', () => {
|
||||
const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
testObject.updateDefaultConfiguration(toConfigurationModel({
|
||||
'editor.lineNumbers': 'on',
|
||||
}));
|
||||
|
||||
const actual = testObject.compareAndUpdateDefaultConfiguration(toConfigurationModel({
|
||||
'editor.lineNumbers': 'off',
|
||||
'[markdown]': {
|
||||
'editor.wordWrap': 'off'
|
||||
}
|
||||
}), ['editor.lineNumbers', '[markdown]']);
|
||||
|
||||
assert.deepEqual(actual, { keys: ['editor.lineNumbers', '[markdown]'], overrides: [['markdown', ['editor.wordWrap']]] });
|
||||
|
||||
});
|
||||
|
||||
test('Test compare and update user configuration', () => {
|
||||
const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
testObject.updateLocalUserConfiguration(toConfigurationModel({
|
||||
'editor.lineNumbers': 'off',
|
||||
'editor.fontSize': 12,
|
||||
'[typescript]': {
|
||||
'editor.wordWrap': 'off'
|
||||
}
|
||||
}));
|
||||
|
||||
const actual = testObject.compareAndUpdateLocalUserConfiguration(toConfigurationModel({
|
||||
'editor.lineNumbers': 'on',
|
||||
'window.zoomLevel': 1,
|
||||
'[typescript]': {
|
||||
'editor.wordWrap': 'on',
|
||||
'editor.insertSpaces': false
|
||||
}
|
||||
}));
|
||||
|
||||
assert.deepEqual(actual, { keys: ['window.zoomLevel', 'editor.lineNumbers', '[typescript]', 'editor.fontSize'], overrides: [['typescript', ['editor.insertSpaces', 'editor.wordWrap']]] });
|
||||
|
||||
});
|
||||
|
||||
test('Test compare and update workspace configuration', () => {
|
||||
const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
testObject.updateWorkspaceConfiguration(toConfigurationModel({
|
||||
'editor.lineNumbers': 'off',
|
||||
'editor.fontSize': 12,
|
||||
'[typescript]': {
|
||||
'editor.wordWrap': 'off'
|
||||
}
|
||||
}));
|
||||
|
||||
const actual = testObject.compareAndUpdateWorkspaceConfiguration(toConfigurationModel({
|
||||
'editor.lineNumbers': 'on',
|
||||
'window.zoomLevel': 1,
|
||||
'[typescript]': {
|
||||
'editor.wordWrap': 'on',
|
||||
'editor.insertSpaces': false
|
||||
}
|
||||
}));
|
||||
|
||||
assert.deepEqual(actual, { keys: ['window.zoomLevel', 'editor.lineNumbers', '[typescript]', 'editor.fontSize'], overrides: [['typescript', ['editor.insertSpaces', 'editor.wordWrap']]] });
|
||||
|
||||
});
|
||||
|
||||
test('Test compare and update workspace folder configuration', () => {
|
||||
const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
testObject.updateFolderConfiguration(URI.file('file1'), toConfigurationModel({
|
||||
'editor.lineNumbers': 'off',
|
||||
'editor.fontSize': 12,
|
||||
'[typescript]': {
|
||||
'editor.wordWrap': 'off'
|
||||
}
|
||||
}));
|
||||
|
||||
const actual = testObject.compareAndUpdateFolderConfiguration(URI.file('file1'), toConfigurationModel({
|
||||
'editor.lineNumbers': 'on',
|
||||
'window.zoomLevel': 1,
|
||||
'[typescript]': {
|
||||
'editor.wordWrap': 'on',
|
||||
'editor.insertSpaces': false
|
||||
}
|
||||
}));
|
||||
|
||||
assert.deepEqual(actual, { keys: ['window.zoomLevel', 'editor.lineNumbers', '[typescript]', 'editor.fontSize'], overrides: [['typescript', ['editor.insertSpaces', 'editor.wordWrap']]] });
|
||||
|
||||
});
|
||||
|
||||
test('Test compare and delete workspace folder configuration', () => {
|
||||
const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
testObject.updateFolderConfiguration(URI.file('file1'), toConfigurationModel({
|
||||
'editor.lineNumbers': 'off',
|
||||
'editor.fontSize': 12,
|
||||
'[typescript]': {
|
||||
'editor.wordWrap': 'off'
|
||||
}
|
||||
}));
|
||||
|
||||
const actual = testObject.compareAndDeleteFolderConfiguration(URI.file('file1'));
|
||||
|
||||
assert.deepEqual(actual, { keys: ['editor.lineNumbers', 'editor.fontSize', '[typescript]'], overrides: [['typescript', ['editor.wordWrap']]] });
|
||||
|
||||
});
|
||||
|
||||
function parseConfigurationModel(content: any): ConfigurationModel {
|
||||
const parser = new ConfigurationModelParser('test');
|
||||
parser.parseContent(JSON.stringify(content));
|
||||
return parser.configurationModel;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
suite('ConfigurationChangeEvent', () => {
|
||||
|
||||
test('changeEvent affecting keys with new configuration', () => {
|
||||
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
const change = configuration.compareAndUpdateLocalUserConfiguration(toConfigurationModel({
|
||||
'window.zoomLevel': 1,
|
||||
'workbench.editor.enablePreview': false,
|
||||
'files.autoSave': 'off',
|
||||
}));
|
||||
let testObject = new ConfigurationChangeEvent(change, undefined, configuration);
|
||||
|
||||
assert.deepEqual(testObject.affectedKeys, ['window.zoomLevel', 'workbench.editor.enablePreview', 'files.autoSave']);
|
||||
|
||||
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.autoSave'));
|
||||
assert.ok(!testObject.affectsConfiguration('files.exclude'));
|
||||
|
||||
assert.ok(!testObject.affectsConfiguration('[markdown]'));
|
||||
assert.ok(!testObject.affectsConfiguration('editor'));
|
||||
});
|
||||
|
||||
test('changeEvent affecting keys when configuration changed', () => {
|
||||
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
configuration.updateLocalUserConfiguration(toConfigurationModel({
|
||||
'window.zoomLevel': 2,
|
||||
'workbench.editor.enablePreview': true,
|
||||
'files.autoSave': 'off',
|
||||
}));
|
||||
const data = configuration.toData();
|
||||
const change = configuration.compareAndUpdateLocalUserConfiguration(toConfigurationModel({
|
||||
'window.zoomLevel': 1,
|
||||
'workbench.editor.enablePreview': false,
|
||||
'files.autoSave': 'off',
|
||||
}));
|
||||
let testObject = new ConfigurationChangeEvent(change, { data }, configuration);
|
||||
|
||||
assert.deepEqual(testObject.affectedKeys, ['window.zoomLevel', 'workbench.editor.enablePreview']);
|
||||
|
||||
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('[markdown]'));
|
||||
assert.ok(!testObject.affectsConfiguration('editor'));
|
||||
});
|
||||
|
||||
test('changeEvent affecting overrides with new configuration', () => {
|
||||
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
const change = configuration.compareAndUpdateLocalUserConfiguration(toConfigurationModel({
|
||||
'files.autoSave': 'off',
|
||||
'[markdown]': {
|
||||
'editor.wordWrap': 'off'
|
||||
}
|
||||
}));
|
||||
let testObject = new ConfigurationChangeEvent(change, undefined, configuration);
|
||||
|
||||
assert.deepEqual(testObject.affectedKeys, ['files.autoSave', '[markdown]', 'editor.wordWrap']);
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('files'));
|
||||
assert.ok(testObject.affectsConfiguration('files.autoSave'));
|
||||
assert.ok(!testObject.affectsConfiguration('files.exclude'));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('[markdown]'));
|
||||
assert.ok(!testObject.affectsConfiguration('[markdown].editor'));
|
||||
assert.ok(!testObject.affectsConfiguration('[markdown].workbench'));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('editor'));
|
||||
assert.ok(testObject.affectsConfiguration('editor.wordWrap'));
|
||||
assert.ok(testObject.affectsConfiguration('editor', { overrideIdentifier: 'markdown' }));
|
||||
assert.ok(testObject.affectsConfiguration('editor.wordWrap', { overrideIdentifier: 'markdown' }));
|
||||
assert.ok(!testObject.affectsConfiguration('editor', { overrideIdentifier: 'json' }));
|
||||
assert.ok(!testObject.affectsConfiguration('editor.fontSize', { overrideIdentifier: 'markdown' }));
|
||||
|
||||
assert.ok(!testObject.affectsConfiguration('editor.fontSize'));
|
||||
assert.ok(!testObject.affectsConfiguration('window'));
|
||||
});
|
||||
|
||||
test('changeEvent affecting overrides when configuration changed', () => {
|
||||
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
configuration.updateLocalUserConfiguration(toConfigurationModel({
|
||||
'workbench.editor.enablePreview': true,
|
||||
'[markdown]': {
|
||||
'editor.fontSize': 12,
|
||||
'editor.wordWrap': 'off'
|
||||
},
|
||||
'files.autoSave': 'off',
|
||||
}));
|
||||
const data = configuration.toData();
|
||||
const change = configuration.compareAndUpdateLocalUserConfiguration(toConfigurationModel({
|
||||
'files.autoSave': 'off',
|
||||
'[markdown]': {
|
||||
'editor.fontSize': 13,
|
||||
'editor.wordWrap': 'off'
|
||||
},
|
||||
'window.zoomLevel': 1,
|
||||
}));
|
||||
let testObject = new ConfigurationChangeEvent(change, { data }, configuration);
|
||||
|
||||
assert.deepEqual(testObject.affectedKeys, ['window.zoomLevel', '[markdown]', 'workbench.editor.enablePreview', 'editor.fontSize']);
|
||||
|
||||
assert.ok(!testObject.affectsConfiguration('files'));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('[markdown]'));
|
||||
assert.ok(!testObject.affectsConfiguration('[markdown].editor'));
|
||||
assert.ok(!testObject.affectsConfiguration('[markdown].editor.fontSize'));
|
||||
assert.ok(!testObject.affectsConfiguration('[markdown].editor.wordWrap'));
|
||||
assert.ok(!testObject.affectsConfiguration('[markdown].workbench'));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('editor'));
|
||||
assert.ok(testObject.affectsConfiguration('editor', { overrideIdentifier: 'markdown' }));
|
||||
assert.ok(testObject.affectsConfiguration('editor.fontSize', { overrideIdentifier: 'markdown' }));
|
||||
assert.ok(!testObject.affectsConfiguration('editor.wordWrap'));
|
||||
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { overrideIdentifier: 'markdown' }));
|
||||
assert.ok(!testObject.affectsConfiguration('editor', { overrideIdentifier: 'json' }));
|
||||
assert.ok(!testObject.affectsConfiguration('editor.fontSize', { overrideIdentifier: 'json' }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window'));
|
||||
assert.ok(testObject.affectsConfiguration('window.zoomLevel'));
|
||||
assert.ok(testObject.affectsConfiguration('window', { overrideIdentifier: 'markdown' }));
|
||||
assert.ok(testObject.affectsConfiguration('window.zoomLevel', { overrideIdentifier: 'markdown' }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('workbench'));
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor'));
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview'));
|
||||
assert.ok(testObject.affectsConfiguration('workbench', { overrideIdentifier: 'markdown' }));
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor', { overrideIdentifier: 'markdown' }));
|
||||
});
|
||||
|
||||
test('changeEvent affecting workspace folders', () => {
|
||||
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
configuration.updateWorkspaceConfiguration(toConfigurationModel({ 'window.title': 'custom' }));
|
||||
configuration.updateFolderConfiguration(URI.file('folder1'), toConfigurationModel({ 'window.zoomLevel': 2, 'window.restoreFullscreen': true }));
|
||||
configuration.updateFolderConfiguration(URI.file('folder2'), toConfigurationModel({ 'workbench.editor.enablePreview': true, 'window.restoreWindows': true }));
|
||||
const data = configuration.toData();
|
||||
const workspace = new Workspace('a', [new WorkspaceFolder({ index: 0, name: 'a', uri: URI.file('folder1') }), new WorkspaceFolder({ index: 1, name: 'b', uri: URI.file('folder2') }), new WorkspaceFolder({ index: 2, name: 'c', uri: URI.file('folder3') })]);
|
||||
const change = mergeChanges(
|
||||
configuration.compareAndUpdateWorkspaceConfiguration(toConfigurationModel({ 'window.title': 'native' })),
|
||||
configuration.compareAndUpdateFolderConfiguration(URI.file('folder1'), toConfigurationModel({ 'window.zoomLevel': 1, 'window.restoreFullscreen': false })),
|
||||
configuration.compareAndUpdateFolderConfiguration(URI.file('folder2'), toConfigurationModel({ 'workbench.editor.enablePreview': false, 'window.restoreWindows': false }))
|
||||
);
|
||||
let testObject = new ConfigurationChangeEvent(change, { data, workspace }, configuration, workspace);
|
||||
|
||||
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', { resource: URI.file('folder1') }));
|
||||
assert.ok(testObject.affectsConfiguration('window.zoomLevel', { resource: URI.file(join('folder1', 'file1')) }));
|
||||
assert.ok(!testObject.affectsConfiguration('window.zoomLevel', { resource: URI.file('file1') }));
|
||||
assert.ok(!testObject.affectsConfiguration('window.zoomLevel', { resource: URI.file('file2') }));
|
||||
assert.ok(!testObject.affectsConfiguration('window.zoomLevel', { resource: URI.file(join('folder2', 'file2')) }));
|
||||
assert.ok(!testObject.affectsConfiguration('window.zoomLevel', { resource: URI.file(join('folder3', 'file3')) }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen'));
|
||||
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen', { resource: URI.file(join('folder1', 'file1')) }));
|
||||
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen', { resource: URI.file('folder1') }));
|
||||
assert.ok(!testObject.affectsConfiguration('window.restoreFullscreen', { resource: URI.file('file1') }));
|
||||
assert.ok(!testObject.affectsConfiguration('window.restoreFullscreen', { resource: URI.file('file2') }));
|
||||
assert.ok(!testObject.affectsConfiguration('window.restoreFullscreen', { resource: URI.file(join('folder2', 'file2')) }));
|
||||
assert.ok(!testObject.affectsConfiguration('window.restoreFullscreen', { resource: URI.file(join('folder3', 'file3')) }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window.restoreWindows'));
|
||||
assert.ok(testObject.affectsConfiguration('window.restoreWindows', { resource: URI.file('folder2') }));
|
||||
assert.ok(testObject.affectsConfiguration('window.restoreWindows', { resource: URI.file(join('folder2', 'file2')) }));
|
||||
assert.ok(!testObject.affectsConfiguration('window.restoreWindows', { resource: URI.file('file2') }));
|
||||
assert.ok(!testObject.affectsConfiguration('window.restoreWindows', { resource: URI.file(join('folder1', 'file1')) }));
|
||||
assert.ok(!testObject.affectsConfiguration('window.restoreWindows', { resource: URI.file(join('folder3', 'file3')) }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window.title'));
|
||||
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file('folder1') }));
|
||||
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file(join('folder1', 'file1')) }));
|
||||
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file('folder2') }));
|
||||
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file(join('folder2', 'file2')) }));
|
||||
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file('folder3') }));
|
||||
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file(join('folder3', 'file3')) }));
|
||||
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file('file1') }));
|
||||
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file('file2') }));
|
||||
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file('file3') }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window'));
|
||||
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file('folder1') }));
|
||||
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file(join('folder1', 'file1')) }));
|
||||
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file('folder2') }));
|
||||
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file(join('folder2', 'file2')) }));
|
||||
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file('folder3') }));
|
||||
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file(join('folder3', 'file3')) }));
|
||||
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file('file1') }));
|
||||
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file('file2') }));
|
||||
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file('file3') }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview'));
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview', { resource: URI.file('folder2') }));
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview', { resource: URI.file(join('folder2', 'file2')) }));
|
||||
assert.ok(!testObject.affectsConfiguration('workbench.editor.enablePreview', { resource: URI.file('folder1') }));
|
||||
assert.ok(!testObject.affectsConfiguration('workbench.editor.enablePreview', { resource: URI.file(join('folder1', 'file1')) }));
|
||||
assert.ok(!testObject.affectsConfiguration('workbench.editor.enablePreview', { resource: URI.file('folder3') }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor'));
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor', { resource: URI.file('folder2') }));
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor', { resource: URI.file(join('folder2', 'file2')) }));
|
||||
assert.ok(!testObject.affectsConfiguration('workbench.editor', { resource: URI.file('folder1') }));
|
||||
assert.ok(!testObject.affectsConfiguration('workbench.editor', { resource: URI.file(join('folder1', 'file1')) }));
|
||||
assert.ok(!testObject.affectsConfiguration('workbench.editor', { resource: URI.file('folder3') }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('workbench'));
|
||||
assert.ok(testObject.affectsConfiguration('workbench', { resource: URI.file('folder2') }));
|
||||
assert.ok(testObject.affectsConfiguration('workbench', { resource: URI.file(join('folder2', 'file2')) }));
|
||||
assert.ok(!testObject.affectsConfiguration('workbench', { resource: URI.file('folder1') }));
|
||||
assert.ok(!testObject.affectsConfiguration('workbench', { resource: URI.file('folder3') }));
|
||||
|
||||
assert.ok(!testObject.affectsConfiguration('files'));
|
||||
assert.ok(!testObject.affectsConfiguration('files', { resource: URI.file('folder1') }));
|
||||
assert.ok(!testObject.affectsConfiguration('files', { resource: URI.file(join('folder1', 'file1')) }));
|
||||
assert.ok(!testObject.affectsConfiguration('files', { resource: URI.file('folder2') }));
|
||||
assert.ok(!testObject.affectsConfiguration('files', { resource: URI.file(join('folder2', 'file2')) }));
|
||||
assert.ok(!testObject.affectsConfiguration('files', { resource: URI.file('folder3') }));
|
||||
assert.ok(!testObject.affectsConfiguration('files', { resource: URI.file(join('folder3', 'file3')) }));
|
||||
});
|
||||
|
||||
test('changeEvent - all', () => {
|
||||
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
configuration.updateFolderConfiguration(URI.file('file1'), toConfigurationModel({ 'window.zoomLevel': 2, 'window.restoreFullscreen': true }));
|
||||
const data = configuration.toData();
|
||||
const change = mergeChanges(
|
||||
configuration.compareAndUpdateDefaultConfiguration(toConfigurationModel({
|
||||
'editor.lineNumbers': 'off',
|
||||
'[markdown]': {
|
||||
'editor.wordWrap': 'off'
|
||||
}
|
||||
}), ['editor.lineNumbers', '[markdown]']),
|
||||
configuration.compareAndUpdateLocalUserConfiguration(toConfigurationModel({
|
||||
'[json]': {
|
||||
'editor.lineNumbers': 'relative'
|
||||
}
|
||||
})),
|
||||
configuration.compareAndUpdateWorkspaceConfiguration(toConfigurationModel({ 'window.title': 'custom' })),
|
||||
configuration.compareAndDeleteFolderConfiguration(URI.file('file1')),
|
||||
configuration.compareAndUpdateFolderConfiguration(URI.file('file2'), toConfigurationModel({ 'workbench.editor.enablePreview': true, 'window.restoreWindows': true })));
|
||||
const workspace = new Workspace('a', [new WorkspaceFolder({ index: 0, name: 'a', uri: URI.file('file1') }), new WorkspaceFolder({ index: 1, name: 'b', uri: URI.file('file2') }), new WorkspaceFolder({ index: 2, name: 'c', uri: URI.file('folder3') })]);
|
||||
const testObject = new ConfigurationChangeEvent(change, { data, workspace }, configuration, workspace);
|
||||
|
||||
assert.deepEqual(testObject.affectedKeys, ['editor.lineNumbers', '[markdown]', '[json]', 'window.title', 'window.zoomLevel', 'window.restoreFullscreen', 'workbench.editor.enablePreview', 'window.restoreWindows', 'editor.wordWrap']);
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window.title'));
|
||||
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file('file1') }));
|
||||
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file('file2') }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window'));
|
||||
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file('file1') }));
|
||||
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file('file2') }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window.zoomLevel'));
|
||||
assert.ok(testObject.affectsConfiguration('window.zoomLevel', { resource: URI.file('file1') }));
|
||||
assert.ok(!testObject.affectsConfiguration('window.zoomLevel', { resource: URI.file('file2') }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen'));
|
||||
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen', { resource: URI.file('file1') }));
|
||||
assert.ok(!testObject.affectsConfiguration('window.restoreFullscreen', { resource: URI.file('file2') }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window.restoreWindows'));
|
||||
assert.ok(testObject.affectsConfiguration('window.restoreWindows', { resource: URI.file('file2') }));
|
||||
assert.ok(!testObject.affectsConfiguration('window.restoreWindows', { resource: URI.file('file1') }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview'));
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview', { resource: URI.file('file2') }));
|
||||
assert.ok(!testObject.affectsConfiguration('workbench.editor.enablePreview', { resource: URI.file('file1') }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor'));
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor', { resource: URI.file('file2') }));
|
||||
assert.ok(!testObject.affectsConfiguration('workbench.editor', { resource: URI.file('file1') }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('workbench'));
|
||||
assert.ok(testObject.affectsConfiguration('workbench', { resource: URI.file('file2') }));
|
||||
assert.ok(!testObject.affectsConfiguration('workbench', { resource: URI.file('file1') }));
|
||||
|
||||
assert.ok(!testObject.affectsConfiguration('files'));
|
||||
assert.ok(!testObject.affectsConfiguration('files', { resource: URI.file('file1') }));
|
||||
assert.ok(!testObject.affectsConfiguration('files', { resource: URI.file('file2') }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('editor'));
|
||||
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file1') }));
|
||||
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file2') }));
|
||||
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file1'), overrideIdentifier: 'json' }));
|
||||
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file1'), overrideIdentifier: 'markdown' }));
|
||||
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file1'), overrideIdentifier: 'typescript' }));
|
||||
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file2'), overrideIdentifier: 'json' }));
|
||||
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file2'), overrideIdentifier: 'markdown' }));
|
||||
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file2'), overrideIdentifier: 'typescript' }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('editor.lineNumbers'));
|
||||
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file1') }));
|
||||
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file2') }));
|
||||
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file1'), overrideIdentifier: 'json' }));
|
||||
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file1'), overrideIdentifier: 'markdown' }));
|
||||
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file1'), overrideIdentifier: 'typescript' }));
|
||||
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file2'), overrideIdentifier: 'json' }));
|
||||
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file2'), overrideIdentifier: 'markdown' }));
|
||||
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file2'), overrideIdentifier: 'typescript' }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('editor.wordWrap'));
|
||||
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file1') }));
|
||||
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file2') }));
|
||||
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file1'), overrideIdentifier: 'json' }));
|
||||
assert.ok(testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file1'), overrideIdentifier: 'markdown' }));
|
||||
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file1'), overrideIdentifier: 'typescript' }));
|
||||
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file2'), overrideIdentifier: 'json' }));
|
||||
assert.ok(testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file2'), overrideIdentifier: 'markdown' }));
|
||||
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file2'), overrideIdentifier: 'typescript' }));
|
||||
|
||||
assert.ok(!testObject.affectsConfiguration('editor.fontSize'));
|
||||
assert.ok(!testObject.affectsConfiguration('editor.fontSize', { resource: URI.file('file1') }));
|
||||
assert.ok(!testObject.affectsConfiguration('editor.fontSize', { resource: URI.file('file2') }));
|
||||
});
|
||||
|
||||
test('changeEvent affecting tasks and launches', () => {
|
||||
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
const change = configuration.compareAndUpdateLocalUserConfiguration(toConfigurationModel({
|
||||
'launch': {
|
||||
'configuraiton': {}
|
||||
},
|
||||
'launch.version': 1,
|
||||
'tasks': {
|
||||
'version': 2
|
||||
}
|
||||
}));
|
||||
let testObject = new ConfigurationChangeEvent(change, undefined, configuration);
|
||||
|
||||
assert.deepEqual(testObject.affectedKeys, ['launch', 'launch.version', 'tasks']);
|
||||
assert.ok(testObject.affectsConfiguration('launch'));
|
||||
assert.ok(testObject.affectsConfiguration('launch.version'));
|
||||
assert.ok(testObject.affectsConfiguration('tasks'));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('AllKeysConfigurationChangeEvent', () => {
|
||||
|
||||
test('changeEvent', () => {
|
||||
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
configuration.updateDefaultConfiguration(toConfigurationModel({
|
||||
'editor.lineNumbers': 'off',
|
||||
'[markdown]': {
|
||||
'editor.wordWrap': 'off'
|
||||
}
|
||||
}));
|
||||
configuration.updateLocalUserConfiguration(toConfigurationModel({
|
||||
'[json]': {
|
||||
'editor.lineNumbers': 'relative'
|
||||
}
|
||||
}));
|
||||
configuration.updateWorkspaceConfiguration(toConfigurationModel({ 'window.title': 'custom' }));
|
||||
configuration.updateFolderConfiguration(URI.file('file1'), toConfigurationModel({ 'window.zoomLevel': 2, 'window.restoreFullscreen': true }));
|
||||
configuration.updateFolderConfiguration(URI.file('file2'), toConfigurationModel({ 'workbench.editor.enablePreview': true, 'window.restoreWindows': true }));
|
||||
const workspace = new Workspace('a', [new WorkspaceFolder({ index: 0, name: 'a', uri: URI.file('file1') }), new WorkspaceFolder({ index: 1, name: 'b', uri: URI.file('file2') }), new WorkspaceFolder({ index: 2, name: 'c', uri: URI.file('folder3') })]);
|
||||
let testObject = new AllKeysConfigurationChangeEvent(configuration, workspace, ConfigurationTarget.USER, null);
|
||||
|
||||
assert.deepEqual(testObject.affectedKeys, ['editor.lineNumbers', '[markdown]', '[json]', 'window.title', 'window.zoomLevel', 'window.restoreFullscreen', 'workbench.editor.enablePreview', 'window.restoreWindows']);
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window.title'));
|
||||
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file('file1') }));
|
||||
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file('file2') }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window'));
|
||||
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file('file1') }));
|
||||
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file('file2') }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window.zoomLevel'));
|
||||
assert.ok(testObject.affectsConfiguration('window.zoomLevel', { resource: URI.file('file1') }));
|
||||
assert.ok(!testObject.affectsConfiguration('window.zoomLevel', { resource: URI.file('file2') }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen'));
|
||||
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen', { resource: URI.file('file1') }));
|
||||
assert.ok(!testObject.affectsConfiguration('window.restoreFullscreen', { resource: URI.file('file2') }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('window.restoreWindows'));
|
||||
assert.ok(testObject.affectsConfiguration('window.restoreWindows', { resource: URI.file('file2') }));
|
||||
assert.ok(!testObject.affectsConfiguration('window.restoreWindows', { resource: URI.file('file1') }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview'));
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview', { resource: URI.file('file2') }));
|
||||
assert.ok(!testObject.affectsConfiguration('workbench.editor.enablePreview', { resource: URI.file('file1') }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor'));
|
||||
assert.ok(testObject.affectsConfiguration('workbench.editor', { resource: URI.file('file2') }));
|
||||
assert.ok(!testObject.affectsConfiguration('workbench.editor', { resource: URI.file('file1') }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('workbench'));
|
||||
assert.ok(testObject.affectsConfiguration('workbench', { resource: URI.file('file2') }));
|
||||
assert.ok(!testObject.affectsConfiguration('workbench', { resource: URI.file('file1') }));
|
||||
|
||||
assert.ok(!testObject.affectsConfiguration('files'));
|
||||
assert.ok(!testObject.affectsConfiguration('files', { resource: URI.file('file1') }));
|
||||
assert.ok(!testObject.affectsConfiguration('files', { resource: URI.file('file2') }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('editor'));
|
||||
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file1') }));
|
||||
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file2') }));
|
||||
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file1'), overrideIdentifier: 'json' }));
|
||||
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file1'), overrideIdentifier: 'markdown' }));
|
||||
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file1'), overrideIdentifier: 'typescript' }));
|
||||
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file2'), overrideIdentifier: 'json' }));
|
||||
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file2'), overrideIdentifier: 'markdown' }));
|
||||
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file2'), overrideIdentifier: 'typescript' }));
|
||||
|
||||
assert.ok(testObject.affectsConfiguration('editor.lineNumbers'));
|
||||
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file1') }));
|
||||
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file2') }));
|
||||
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file1'), overrideIdentifier: 'json' }));
|
||||
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file1'), overrideIdentifier: 'markdown' }));
|
||||
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file1'), overrideIdentifier: 'typescript' }));
|
||||
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file2'), overrideIdentifier: 'json' }));
|
||||
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file2'), overrideIdentifier: 'markdown' }));
|
||||
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file2'), overrideIdentifier: 'typescript' }));
|
||||
|
||||
assert.ok(!testObject.affectsConfiguration('editor.wordWrap'));
|
||||
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file1') }));
|
||||
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file2') }));
|
||||
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file1'), overrideIdentifier: 'json' }));
|
||||
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file1'), overrideIdentifier: 'markdown' }));
|
||||
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file1'), overrideIdentifier: 'typescript' }));
|
||||
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file2'), overrideIdentifier: 'json' }));
|
||||
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file2'), overrideIdentifier: 'markdown' }));
|
||||
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file2'), overrideIdentifier: 'typescript' }));
|
||||
|
||||
assert.ok(!testObject.affectsConfiguration('editor.fontSize'));
|
||||
assert.ok(!testObject.affectsConfiguration('editor.fontSize', { resource: URI.file('file1') }));
|
||||
assert.ok(!testObject.affectsConfiguration('editor.fontSize', { resource: URI.file('file2') }));
|
||||
});
|
||||
});
|
||||
|
||||
function toConfigurationModel(obj: any): ConfigurationModel {
|
||||
const parser = new ConfigurationModelParser('test');
|
||||
parser.parseContent(JSON.stringify(obj));
|
||||
return parser.configurationModel;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
|
||||
suite('ConfigurationRegistry', () => {
|
||||
|
||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
|
||||
|
||||
test('configuration override', async () => {
|
||||
configurationRegistry.registerConfiguration({
|
||||
'id': '_test_default',
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'config': {
|
||||
'type': 'object',
|
||||
}
|
||||
}
|
||||
});
|
||||
configurationRegistry.registerDefaultConfigurations([{ 'config': { a: 1, b: 2 } }]);
|
||||
configurationRegistry.registerDefaultConfigurations([{ '[lang]': { a: 2, c: 3 } }]);
|
||||
|
||||
assert.deepEqual(configurationRegistry.getConfigurationProperties()['config'].default, { a: 1, b: 2 });
|
||||
assert.deepEqual(configurationRegistry.getConfigurationProperties()['[lang]'].default, { a: 2, c: 3 });
|
||||
});
|
||||
|
||||
test('configuration override defaults - merges defaults', async () => {
|
||||
configurationRegistry.registerDefaultConfigurations([{ '[lang]': { a: 1, b: 2 } }]);
|
||||
configurationRegistry.registerDefaultConfigurations([{ '[lang]': { a: 2, c: 3 } }]);
|
||||
|
||||
assert.deepEqual(configurationRegistry.getConfigurationProperties()['[lang]'].default, { a: 2, b: 2, c: 3 });
|
||||
});
|
||||
|
||||
test('configuration defaults - overrides defaults', async () => {
|
||||
configurationRegistry.registerConfiguration({
|
||||
'id': '_test_default',
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'config': {
|
||||
'type': 'object',
|
||||
}
|
||||
}
|
||||
});
|
||||
configurationRegistry.registerDefaultConfigurations([{ 'config': { a: 1, b: 2 } }]);
|
||||
configurationRegistry.registerDefaultConfigurations([{ 'config': { a: 2, c: 3 } }]);
|
||||
|
||||
assert.deepEqual(configurationRegistry.getConfigurationProperties()['config'].default, { a: 2, c: 3 });
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,246 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { ConfigurationService } from 'vs/platform/configuration/common/configurationService';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { FileService } from 'vs/platform/files/common/fileService';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider';
|
||||
|
||||
suite('ConfigurationService', () => {
|
||||
|
||||
let fileService: IFileService;
|
||||
let settingsResource: URI;
|
||||
const disposables: DisposableStore = new DisposableStore();
|
||||
|
||||
setup(async () => {
|
||||
fileService = disposables.add(new FileService(new NullLogService()));
|
||||
const diskFileSystemProvider = disposables.add(new InMemoryFileSystemProvider());
|
||||
fileService.registerProvider(Schemas.file, diskFileSystemProvider);
|
||||
settingsResource = URI.file('settings.json');
|
||||
});
|
||||
|
||||
teardown(() => disposables.clear());
|
||||
|
||||
test('simple', async () => {
|
||||
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }'));
|
||||
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
|
||||
await testObject.initialize();
|
||||
const config = testObject.getValue<{
|
||||
foo: string;
|
||||
}>();
|
||||
|
||||
assert.ok(config);
|
||||
assert.equal(config.foo, 'bar');
|
||||
});
|
||||
|
||||
test('config gets flattened', async () => {
|
||||
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }'));
|
||||
|
||||
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
|
||||
await testObject.initialize();
|
||||
const config = testObject.getValue<{
|
||||
testworkbench: {
|
||||
editor: {
|
||||
tabs: boolean;
|
||||
};
|
||||
};
|
||||
}>();
|
||||
|
||||
assert.ok(config);
|
||||
assert.ok(config.testworkbench);
|
||||
assert.ok(config.testworkbench.editor);
|
||||
assert.equal(config.testworkbench.editor.tabs, true);
|
||||
});
|
||||
|
||||
test('error case does not explode', async () => {
|
||||
await fileService.writeFile(settingsResource, VSBuffer.fromString(',,,,'));
|
||||
|
||||
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
|
||||
await testObject.initialize();
|
||||
const config = testObject.getValue<{
|
||||
foo: string;
|
||||
}>();
|
||||
|
||||
assert.ok(config);
|
||||
});
|
||||
|
||||
test('missing file does not explode', async () => {
|
||||
const testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService));
|
||||
await testObject.initialize();
|
||||
|
||||
const config = testObject.getValue<{ foo: string }>();
|
||||
|
||||
assert.ok(config);
|
||||
});
|
||||
|
||||
test('trigger configuration change event when file does not exist', async () => {
|
||||
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
|
||||
await testObject.initialize();
|
||||
return new Promise<void>(async (c) => {
|
||||
disposables.add(Event.filter(testObject.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(() => {
|
||||
assert.equal(testObject.getValue('foo'), 'bar');
|
||||
c();
|
||||
}));
|
||||
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }'));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('trigger configuration change event when file exists', async () => {
|
||||
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
|
||||
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }'));
|
||||
await testObject.initialize();
|
||||
|
||||
return new Promise<void>((c) => {
|
||||
disposables.add(Event.filter(testObject.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(async (e) => {
|
||||
assert.equal(testObject.getValue('foo'), 'barz');
|
||||
c();
|
||||
}));
|
||||
fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "barz" }'));
|
||||
});
|
||||
});
|
||||
|
||||
test('reloadConfiguration', async () => {
|
||||
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }'));
|
||||
|
||||
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
|
||||
await testObject.initialize();
|
||||
let config = testObject.getValue<{
|
||||
foo: string;
|
||||
}>();
|
||||
assert.ok(config);
|
||||
assert.equal(config.foo, 'bar');
|
||||
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "changed" }'));
|
||||
|
||||
// force a reload to get latest
|
||||
await testObject.reloadConfiguration();
|
||||
config = testObject.getValue<{
|
||||
foo: string;
|
||||
}>();
|
||||
assert.ok(config);
|
||||
assert.equal(config.foo, 'changed');
|
||||
});
|
||||
|
||||
test('model defaults', async () => {
|
||||
interface ITestSetting {
|
||||
configuration: {
|
||||
service: {
|
||||
testSetting: string;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
|
||||
configurationRegistry.registerConfiguration({
|
||||
'id': '_test',
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'configuration.service.testSetting': {
|
||||
'type': 'string',
|
||||
'default': 'isSet'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService));
|
||||
await testObject.initialize();
|
||||
let setting = testObject.getValue<ITestSetting>();
|
||||
|
||||
assert.ok(setting);
|
||||
assert.equal(setting.configuration.service.testSetting, 'isSet');
|
||||
|
||||
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }'));
|
||||
testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
|
||||
|
||||
setting = testObject.getValue<ITestSetting>();
|
||||
|
||||
assert.ok(setting);
|
||||
assert.equal(setting.configuration.service.testSetting, 'isSet');
|
||||
|
||||
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "configuration.service.testSetting": "isChanged" }'));
|
||||
|
||||
await testObject.reloadConfiguration();
|
||||
setting = testObject.getValue<ITestSetting>();
|
||||
assert.ok(setting);
|
||||
assert.equal(setting.configuration.service.testSetting, 'isChanged');
|
||||
});
|
||||
|
||||
test('lookup', async () => {
|
||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
|
||||
configurationRegistry.registerConfiguration({
|
||||
'id': '_test',
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'lookup.service.testSetting': {
|
||||
'type': 'string',
|
||||
'default': 'isSet'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
|
||||
testObject.initialize();
|
||||
|
||||
let res = testObject.inspect('something.missing');
|
||||
assert.strictEqual(res.value, undefined);
|
||||
assert.strictEqual(res.defaultValue, undefined);
|
||||
assert.strictEqual(res.userValue, undefined);
|
||||
|
||||
res = testObject.inspect('lookup.service.testSetting');
|
||||
assert.strictEqual(res.defaultValue, 'isSet');
|
||||
assert.strictEqual(res.value, 'isSet');
|
||||
assert.strictEqual(res.userValue, undefined);
|
||||
|
||||
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "lookup.service.testSetting": "bar" }'));
|
||||
|
||||
await testObject.reloadConfiguration();
|
||||
res = testObject.inspect('lookup.service.testSetting');
|
||||
assert.strictEqual(res.defaultValue, 'isSet');
|
||||
assert.strictEqual(res.userValue, 'bar');
|
||||
assert.strictEqual(res.value, 'bar');
|
||||
|
||||
});
|
||||
|
||||
test('lookup with null', async () => {
|
||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
|
||||
configurationRegistry.registerConfiguration({
|
||||
'id': '_testNull',
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'lookup.service.testNullSetting': {
|
||||
'type': 'null',
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
|
||||
testObject.initialize();
|
||||
|
||||
let res = testObject.inspect('lookup.service.testNullSetting');
|
||||
assert.strictEqual(res.defaultValue, null);
|
||||
assert.strictEqual(res.value, null);
|
||||
assert.strictEqual(res.userValue, undefined);
|
||||
|
||||
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "lookup.service.testNullSetting": null }'));
|
||||
|
||||
await testObject.reloadConfiguration();
|
||||
|
||||
res = testObject.inspect('lookup.service.testNullSetting');
|
||||
assert.strictEqual(res.defaultValue, null);
|
||||
assert.strictEqual(res.value, null);
|
||||
assert.strictEqual(res.userValue, null);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,80 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { TernarySearchTree } from 'vs/base/common/map';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { getConfigurationKeys, IConfigurationOverrides, IConfigurationService, getConfigurationValue, isConfigurationOverrides, IConfigurationValue } from 'vs/platform/configuration/common/configuration';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
|
||||
export class TestConfigurationService implements IConfigurationService {
|
||||
public _serviceBrand: undefined;
|
||||
|
||||
private configuration: any;
|
||||
readonly onDidChangeConfiguration = new Emitter<any>().event;
|
||||
|
||||
constructor(configuration?: any) {
|
||||
this.configuration = configuration || Object.create(null);
|
||||
}
|
||||
|
||||
private configurationByRoot: TernarySearchTree<string, any> = TernarySearchTree.forPaths<any>();
|
||||
|
||||
public reloadConfiguration<T>(): Promise<T> {
|
||||
return Promise.resolve(this.getValue());
|
||||
}
|
||||
|
||||
public getValue(arg1?: any, arg2?: any): any {
|
||||
let configuration;
|
||||
const overrides = isConfigurationOverrides(arg1) ? arg1 : isConfigurationOverrides(arg2) ? arg2 : undefined;
|
||||
if (overrides) {
|
||||
if (overrides.resource) {
|
||||
configuration = this.configurationByRoot.findSubstr(overrides.resource.fsPath);
|
||||
}
|
||||
}
|
||||
configuration = configuration ? configuration : this.configuration;
|
||||
if (arg1 && typeof arg1 === 'string') {
|
||||
return getConfigurationValue(configuration, arg1);
|
||||
}
|
||||
return configuration;
|
||||
}
|
||||
|
||||
public updateValue(key: string, value: any): Promise<void> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
public setUserConfiguration(key: any, value: any, root?: URI): Promise<void> {
|
||||
if (root) {
|
||||
const configForRoot = this.configurationByRoot.get(root.fsPath) || Object.create(null);
|
||||
configForRoot[key] = value;
|
||||
this.configurationByRoot.set(root.fsPath, configForRoot);
|
||||
} else {
|
||||
this.configuration[key] = value;
|
||||
}
|
||||
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
public inspect<T>(key: string, overrides?: IConfigurationOverrides): IConfigurationValue<T> {
|
||||
const config = this.getValue(undefined, overrides);
|
||||
|
||||
return {
|
||||
value: getConfigurationValue<T>(config, key),
|
||||
defaultValue: getConfigurationValue<T>(config, key),
|
||||
userValue: getConfigurationValue<T>(config, key)
|
||||
};
|
||||
}
|
||||
|
||||
public keys() {
|
||||
return {
|
||||
default: getConfigurationKeys(),
|
||||
user: Object.keys(this.configuration),
|
||||
workspace: [],
|
||||
workspaceFolder: []
|
||||
};
|
||||
}
|
||||
|
||||
public getConfigurationData() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user