提交 dd407168 编写于 作者: M Martin Aeschlimann

[settings] add IConfigurationRegistry.getConfigurationProperties to hide 'allOf'. Fixes #15504

上级 53ea1d98
......@@ -9,6 +9,7 @@ import Event, { Emitter } from 'vs/base/common/event';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { Registry } from 'vs/platform/platform';
import objects = require('vs/base/common/objects');
import types = require('vs/base/common/types');
import { ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry';
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
......@@ -35,9 +36,15 @@ export interface IConfigurationRegistry {
onDidRegisterConfiguration: Event<IConfigurationRegistry>;
/**
* Returns all configurations contributed to this registry.
* 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]: IJSONSchema };
}
export interface IConfigurationNode {
......@@ -46,10 +53,8 @@ export interface IConfigurationNode {
type?: string | string[];
title?: string;
description?: string;
default?: any;
properties?: { [path: string]: IJSONSchema; };
allOf?: IJSONSchema[];
definitions?: { [path: string]: IJSONSchema; };
allOf?: IConfigurationNode[];
}
const schemaId = 'vscode://schemas/settings';
......@@ -57,6 +62,7 @@ const contributionRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensio
class ConfigurationRegistry implements IConfigurationRegistry {
private configurationContributors: IConfigurationNode[];
private configurationProperties: { [qualifiedKey: string]: IJSONSchema };
private configurationSchema: IJSONSchema;
private _onDidRegisterConfiguration: Emitter<IConfigurationRegistry>;
......@@ -64,6 +70,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
this.configurationContributors = [];
this.configurationSchema = { allOf: [] };
this._onDidRegisterConfiguration = new Emitter<IConfigurationRegistry>();
this.configurationProperties = {};
contributionRegistry.registerSchema(schemaId, this.configurationSchema);
}
......@@ -78,6 +85,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
public registerConfigurations(configurations: IConfigurationNode[]): void {
configurations.forEach(configuration => {
this.registerProperties(configuration); // fills in defaults
this.configurationContributors.push(configuration);
this.registerJSONConfiguration(configuration);
});
......@@ -85,8 +93,34 @@ class ConfigurationRegistry implements IConfigurationRegistry {
this._onDidRegisterConfiguration.fire(this);
}
public getConfigurations(): IConfigurationNode[] {
return this.configurationContributors.slice(0);
private registerProperties(configuration: IConfigurationNode) {
let properties = configuration.properties;
if (properties) {
for (let key in properties) {
// fill in default values
let property = properties[key];
let defaultValue = property.default;
if (types.isUndefined(defaultValue)) {
property.default = getDefaultValue(property.type);
}
// add to properties map
this.configurationProperties[key] = properties[key];
}
}
let subNodes = configuration.allOf;
if (subNodes) {
for (let node of subNodes) {
this.registerProperties(node);
}
}
}
getConfigurations(): IConfigurationNode[] {
return this.configurationContributors;
}
getConfigurationProperties(): { [qualifiedKey: string]: IJSONSchema } {
return this.configurationProperties;
}
private registerJSONConfiguration(configuration: IConfigurationNode) {
......@@ -96,6 +130,26 @@ class ConfigurationRegistry implements IConfigurationRegistry {
}
}
function getDefaultValue(type: string | string[]): 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);
......
......@@ -5,89 +5,16 @@
'use strict';
import { Registry } from 'vs/platform/platform';
import types = require('vs/base/common/types');
import { IConfigurationNode, IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
export function setNode(root: any, key: string, value: any): void {
const segments = key.split('.');
const last = segments.pop();
let curr = root;
segments.forEach(s => {
let obj = curr[s];
switch (typeof obj) {
case 'undefined':
obj = curr[s] = Object.create(null);
break;
case 'object':
break;
default:
console.error(`Conflicting configuration setting: ${key} at ${s} with ${JSON.stringify(obj)}`);
}
curr = obj;
});
if (typeof curr === 'object') {
curr[last] = value; // workaround https://github.com/Microsoft/vscode/issues/13606
}
}
function processDefaultValues(withConfig: (config: IConfigurationNode, isTop?: boolean) => boolean): void {
const configurations = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurations();
const visit = (config: IConfigurationNode, level: number) => {
const handled = withConfig(config, level === 0);
if (Array.isArray(config.allOf)) {
config.allOf.forEach((c) => {
// if the config node only contains an `allOf` we treat the `allOf` children as if they were at the top level
visit(c, (!handled && level === 0) ? level : level + 1);
});
}
};
configurations.sort((c1, c2) => {
if (typeof c1.order !== 'number') {
return 1;
}
if (typeof c2.order !== 'number') {
return -1;
}
if (c1.order === c2.order) {
const title1 = c1.title || '';
const title2 = c2.title || '';
return title1.localeCompare(title2);
}
return c1.order - c2.order;
}).forEach((config) => {
visit(config, 0);
});
}
export function getDefaultValues(): any {
const ret: any = Object.create(null);
const handleConfig = (config: IConfigurationNode, isTop: boolean): boolean => {
if (config.properties) {
Object.keys(config.properties).forEach((key) => {
const prop = config.properties[key];
let value = prop.default;
if (types.isUndefined(prop.default)) {
value = getDefaultValue(prop.type);
}
setNode(ret, key, value);
});
return true;
}
return false;
};
processDefaultValues(handleConfig);
return ret;
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);
}
return valueTreeRoot;
}
export function getDefaultValuesContent(indent: string): string {
......@@ -95,13 +22,12 @@ export function getDefaultValuesContent(indent: string): string {
const result: string[] = [];
result.push('{');
const handleConfig = (config: IConfigurationNode, isTop: boolean): boolean => {
let handled = false;
const handleConfig = (config: IConfigurationNode, hasTopLevelTitle: boolean) => {
if (config.title) {
handled = true;
if (isTop) {
if (!hasTopLevelTitle) {
result.push('');
result.push('// ' + config.title);
hasTopLevelTitle = true;
} else {
result.push(indent + '// ' + config.title);
}
......@@ -109,18 +35,14 @@ export function getDefaultValuesContent(indent: string): string {
}
if (config.properties) {
handled = true;
Object.keys(config.properties).forEach((key) => {
const prop = config.properties[key];
let defaultValue = prop.default;
if (types.isUndefined(defaultValue)) {
defaultValue = getDefaultValue(prop.type);
}
if (prop.description) {
result.push(indent + '// ' + prop.description);
}
let defaultValue = prop.default;
let valueString = JSON.stringify(defaultValue, null, indent);
if (valueString && (typeof defaultValue === 'object')) {
valueString = addIndent(valueString, indent);
......@@ -136,58 +58,71 @@ export function getDefaultValuesContent(indent: string): string {
});
}
return handled;
if (config.allOf) {
config.allOf.forEach(c => handleConfig(c, hasTopLevelTitle));
}
};
processDefaultValues(handleConfig);
const configurations = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurations();
configurations.sort(compareConfigurationNodes).forEach(c => handleConfig(c, false));
result.push('}');
return result.join('\n');
}
function addIndent(str: string, indent: string): string {
return str.split('\n').join('\n' + indent);
function compareConfigurationNodes(c1: IConfigurationNode, c2: IConfigurationNode): number {
if (typeof c1.order !== 'number') {
return 1;
}
if (typeof c2.order !== 'number') {
return -1;
}
if (c1.order === c2.order) {
const title1 = c1.title || '';
const title2 = c2.title || '';
return title1.localeCompare(title2);
}
return c1.order - c2.order;
}
function getDefaultValue(type: string | string[]): 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;
}
function addIndent(str: string, indent: string): string {
return str.split('\n').join('\n' + indent);
}
export function flatten(contents: any): any {
export function toValuesTree(properties: { [qualifiedKey: string]: any }): any {
const root = Object.create(null);
for (let key in contents) {
setNode(root, key, contents[key]);
for (let key in properties) {
addToValueTree(root, key, properties[key]);
}
return root;
}
export function getConfigurationKeys(): string[] {
const keys: string[] = [];
function addToValueTree(settingsTreeRoot: any, key: string, value: any): void {
const segments = key.split('.');
const last = segments.pop();
const configurations = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurations();
configurations.forEach(config => {
if (config.properties) {
keys.push(...Object.keys(config.properties));
let curr = settingsTreeRoot;
segments.forEach(s => {
let obj = curr[s];
switch (typeof obj) {
case 'undefined':
obj = curr[s] = Object.create(null);
break;
case 'object':
break;
default:
console.error(`Conflicting configuration setting: ${key} at ${s} with ${JSON.stringify(obj)}`);
}
curr = obj;
});
return keys;
if (typeof curr === 'object') {
curr[last] = value; // workaround https://github.com/Microsoft/vscode/issues/13606
}
}
export function getConfigurationKeys(): string[] {
const properties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
return Object.keys(properties);
}
\ No newline at end of file
......@@ -6,7 +6,7 @@
import { TPromise } from 'vs/base/common/winjs.base';
import * as objects from 'vs/base/common/objects';
import { getDefaultValues, flatten, getConfigurationKeys } from 'vs/platform/configuration/common/model';
import { getDefaultValues, toValuesTree, getConfigurationKeys } from 'vs/platform/configuration/common/model';
import { ConfigWatcher } from 'vs/base/node/config';
import { Registry } from 'vs/platform/platform';
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
......@@ -84,7 +84,7 @@ export class ConfigurationService<T> implements IConfigurationService, IDisposab
// make sure to clone the configuration so that the receiver does not tamper with the values
return {
default: objects.clone(getConfigurationValue<C>(getDefaultValues(), key)),
user: objects.clone(getConfigurationValue<C>(flatten(this.rawConfig.getConfig()), key)),
user: objects.clone(getConfigurationValue<C>(toValuesTree(this.rawConfig.getConfig()), key)),
value: objects.clone(getConfigurationValue<C>(this.getConfiguration(), key))
};
}
......@@ -98,7 +98,7 @@ export class ConfigurationService<T> implements IConfigurationService, IDisposab
private getConsolidatedConfig(): T {
const defaults = getDefaultValues(); // defaults coming from contributions to registries
const user = flatten(this.rawConfig.getConfig()); // user configured settings
const user = toValuesTree(this.rawConfig.getConfig()); // user configured settings
return objects.mixin(
objects.clone(defaults), // target: default values (but dont modify!)
......
......@@ -14,7 +14,6 @@ import { Registry } from 'vs/platform/platform';
import { hasClass, getDomNodePagePosition } from 'vs/base/browser/dom';
import { IAction } from 'vs/base/common/actions';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { LinkedMap as Map } from 'vs/base/common/map';
import { Extensions } from 'vs/workbench/common/actionRegistry';
import { asFileEditorInput } from 'vs/workbench/common/editor';
import { StringEditorInput } from 'vs/workbench/common/editor/stringEditorInput';
......@@ -33,7 +32,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { ICodeEditor, IEditorMouseEvent } from 'vs/editor/browser/editorBrowser';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { IConfigurationEditingService, ConfigurationTarget, IConfigurationValue } from 'vs/workbench/services/configuration/common/configurationEditing';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IOpenSettingsService } from 'vs/workbench/parts/settings/common/openSettings';
import { DefaultSettingsInput, DefaultKeybindingsInput } from 'vs/workbench/parts/settings/browser/defaultSettingsEditors';
......@@ -289,7 +288,6 @@ export class OpenSettingsService extends Disposable implements IOpenSettingsServ
class SettingsActionsRenderer extends Disposable {
private decorationIds: string[] = [];
private configurationsMap: Map<string, IConfigurationNode>;
constructor(private settingsEditor: ICodeEditor,
private copyConfiguration: (configurationValue: IConfigurationValue) => void,
......@@ -348,7 +346,7 @@ class SettingsActionsRenderer extends Disposable {
}
private createDecoration(property: string, offset: number, model: editorCommon.IModel): editorCommon.IModelDeltaDecoration {
const jsonSchema: IJSONSchema = this.getConfigurationsMap().get(property);
const jsonSchema: IJSONSchema = this.getConfigurationsMap()[property];
const position = model.getPositionAt(offset);
const maxColumn = model.getLineMaxColumn(position.lineNumber);
const range = {
......@@ -384,26 +382,8 @@ class SettingsActionsRenderer extends Disposable {
}
}
private getConfigurationsMap(): Map<string, IConfigurationNode> {
if (!this.configurationsMap) {
const configurations = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).getConfigurations();
this.configurationsMap = new Map<string, IConfigurationNode>();
configurations.forEach(configuration => this.populateProperties(configuration, this.configurationsMap));
}
return this.configurationsMap;
}
private populateProperties(configuration: IConfigurationNode, configurationsMap: Map<string, IConfigurationNode>) {
if (configuration.properties) {
for (const property of Object.keys(configuration.properties)) {
configurationsMap.set(property, <IConfigurationNode>configuration.properties[property]);
}
}
if (configuration.allOf) {
for (const c of configuration.allOf) {
this.populateProperties(c, configurationsMap);
}
}
private getConfigurationsMap(): { [qualifiedKey: string]: IJSONSchema } {
return Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).getConfigurationProperties();
}
private onClick(e: IEditorMouseEvent) {
......@@ -411,7 +391,7 @@ class SettingsActionsRenderer extends Disposable {
const setting = parse('{' + model.getLineContent(e.target.range.startLineNumber) + '}');
const key = Object.keys(setting)[0];
let value = setting[key];
let jsonSchema: IJSONSchema = this.getConfigurationsMap().get(key);
let jsonSchema: IJSONSchema = this.getConfigurationsMap()[key];
const actions = this.getActions(key, jsonSchema);
if (actions) {
let elementPosition = getDomNodePagePosition(<HTMLElement>e.target.element);
......
......@@ -7,7 +7,7 @@
import objects = require('vs/base/common/objects');
import types = require('vs/base/common/types');
import json = require('vs/base/common/json');
import model = require('vs/platform/configuration/common/model');
import { toValuesTree } from 'vs/platform/configuration/common/model';
import { CONFIG_DEFAULT_NAME, WORKSPACE_CONFIG_DEFAULT_PATH } from 'vs/workbench/services/configuration/common/configuration';
export interface IConfigFile {
......@@ -18,14 +18,9 @@ export interface IConfigFile {
export function newConfigFile(value: string): IConfigFile {
try {
const root: any = Object.create(null);
const contents = json.parse(value) || {};
for (let key in contents) {
model.setNode(root, key, contents[key]);
}
return {
contents: root,
contents: toValuesTree(contents),
raw: contents
};
} catch (e) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册