mirror of
https://github.com/Microsoft/vscode
synced 2024-10-04 02:14:06 +00:00
[settings] add IConfigurationRegistry.getConfigurationProperties to hide 'allOf'. Fixes #15504
This commit is contained in:
parent
53ea1d9820
commit
dd407168ea
|
@ -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,14 +5,104 @@
|
|||
'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 {
|
||||
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);
|
||||
}
|
||||
return valueTreeRoot;
|
||||
}
|
||||
|
||||
export function getDefaultValuesContent(indent: string): string {
|
||||
let lastEntry = -1;
|
||||
const result: string[] = [];
|
||||
result.push('{');
|
||||
|
||||
const handleConfig = (config: IConfigurationNode, hasTopLevelTitle: boolean) => {
|
||||
if (config.title) {
|
||||
if (!hasTopLevelTitle) {
|
||||
result.push('');
|
||||
result.push('// ' + config.title);
|
||||
hasTopLevelTitle = true;
|
||||
} else {
|
||||
result.push(indent + '// ' + config.title);
|
||||
}
|
||||
result.push('');
|
||||
}
|
||||
|
||||
if (config.properties) {
|
||||
Object.keys(config.properties).forEach((key) => {
|
||||
|
||||
const prop = config.properties[key];
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (lastEntry !== -1) {
|
||||
result[lastEntry] += ',';
|
||||
}
|
||||
lastEntry = result.length;
|
||||
|
||||
result.push(indent + JSON.stringify(key) + ': ' + valueString);
|
||||
result.push('');
|
||||
});
|
||||
}
|
||||
|
||||
if (config.allOf) {
|
||||
config.allOf.forEach(c => handleConfig(c, hasTopLevelTitle));
|
||||
}
|
||||
};
|
||||
|
||||
const configurations = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurations();
|
||||
configurations.sort(compareConfigurationNodes).forEach(c => handleConfig(c, false));
|
||||
|
||||
result.push('}');
|
||||
|
||||
return result.join('\n');
|
||||
}
|
||||
|
||||
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 addIndent(str: string, indent: string): string {
|
||||
return str.split('\n').join('\n' + indent);
|
||||
}
|
||||
|
||||
export function toValuesTree(properties: { [qualifiedKey: string]: any }): any {
|
||||
const root = Object.create(null);
|
||||
for (let key in properties) {
|
||||
addToValueTree(root, key, properties[key]);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
function addToValueTree(settingsTreeRoot: any, key: string, value: any): void {
|
||||
const segments = key.split('.');
|
||||
const last = segments.pop();
|
||||
|
||||
let curr = root;
|
||||
let curr = settingsTreeRoot;
|
||||
segments.forEach(s => {
|
||||
let obj = curr[s];
|
||||
switch (typeof obj) {
|
||||
|
@ -32,162 +122,7 @@ export function setNode(root: any, key: string, value: any): void {
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
export function getDefaultValuesContent(indent: string): string {
|
||||
let lastEntry = -1;
|
||||
const result: string[] = [];
|
||||
result.push('{');
|
||||
|
||||
const handleConfig = (config: IConfigurationNode, isTop: boolean): boolean => {
|
||||
let handled = false;
|
||||
if (config.title) {
|
||||
handled = true;
|
||||
if (isTop) {
|
||||
result.push('');
|
||||
result.push('// ' + config.title);
|
||||
} else {
|
||||
result.push(indent + '// ' + config.title);
|
||||
}
|
||||
result.push('');
|
||||
}
|
||||
|
||||
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 valueString = JSON.stringify(defaultValue, null, indent);
|
||||
if (valueString && (typeof defaultValue === 'object')) {
|
||||
valueString = addIndent(valueString, indent);
|
||||
}
|
||||
|
||||
if (lastEntry !== -1) {
|
||||
result[lastEntry] += ',';
|
||||
}
|
||||
lastEntry = result.length;
|
||||
|
||||
result.push(indent + JSON.stringify(key) + ': ' + valueString);
|
||||
result.push('');
|
||||
});
|
||||
}
|
||||
|
||||
return handled;
|
||||
};
|
||||
|
||||
processDefaultValues(handleConfig);
|
||||
|
||||
result.push('}');
|
||||
|
||||
return result.join('\n');
|
||||
}
|
||||
|
||||
function addIndent(str: string, indent: string): string {
|
||||
return str.split('\n').join('\n' + indent);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
export function flatten(contents: any): any {
|
||||
const root = Object.create(null);
|
||||
|
||||
for (let key in contents) {
|
||||
setNode(root, key, contents[key]);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
export function getConfigurationKeys(): string[] {
|
||||
const keys: string[] = [];
|
||||
|
||||
const configurations = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurations();
|
||||
configurations.forEach(config => {
|
||||
if (config.properties) {
|
||||
keys.push(...Object.keys(config.properties));
|
||||
}
|
||||
});
|
||||
|
||||
return keys;
|
||||
const properties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
|
||||
return Object.keys(properties);
|
||||
}
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue