mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-25 02:58:56 +01:00
[json] use vscode-json-languageservice
This commit is contained in:
@@ -1,257 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {ISchemaContributions} from './jsonSchemaService';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export var schemaContributions: ISchemaContributions = {
|
||||
schemaAssociations: {
|
||||
|
||||
},
|
||||
schemas: {
|
||||
// bundle the schema-schema to include (localized) descriptions
|
||||
'http://json-schema.org/draft-04/schema#': {
|
||||
'title': localize('schema.json', 'Describes a JSON file using a schema. See json-schema.org for more info.'),
|
||||
'$schema': 'http://json-schema.org/draft-04/schema#',
|
||||
'definitions': {
|
||||
'schemaArray': {
|
||||
'type': 'array',
|
||||
'minItems': 1,
|
||||
'items': { '$ref': '#' }
|
||||
},
|
||||
'positiveInteger': {
|
||||
'type': 'integer',
|
||||
'minimum': 0
|
||||
},
|
||||
'positiveIntegerDefault0': {
|
||||
'allOf': [{ '$ref': '#/definitions/positiveInteger' }, { 'default': 0 }]
|
||||
},
|
||||
'simpleTypes': {
|
||||
'type': 'string',
|
||||
'enum': ['array', 'boolean', 'integer', 'null', 'number', 'object', 'string']
|
||||
},
|
||||
'stringArray': {
|
||||
'type': 'array',
|
||||
'items': { 'type': 'string' },
|
||||
'minItems': 1,
|
||||
'uniqueItems': true
|
||||
}
|
||||
},
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'id': {
|
||||
'type': 'string',
|
||||
'format': 'uri',
|
||||
'description': localize('schema.json.id', 'A unique identifier for the schema.')
|
||||
},
|
||||
'$schema': {
|
||||
'type': 'string',
|
||||
'format': 'uri',
|
||||
'description': localize('schema.json.$schema', 'The schema to verify this document against ')
|
||||
},
|
||||
'title': {
|
||||
'type': 'string',
|
||||
'description': localize('schema.json.title', 'A descriptive title of the element')
|
||||
},
|
||||
'description': {
|
||||
'type': 'string',
|
||||
'description': localize('schema.json.description', 'A long description of the element. Used in hover menus and suggestions.')
|
||||
},
|
||||
'default': {
|
||||
'description': localize('schema.json.default', 'A default value. Used by suggestions.')
|
||||
},
|
||||
'multipleOf': {
|
||||
'type': 'number',
|
||||
'minimum': 0,
|
||||
'exclusiveMinimum': true,
|
||||
'description': localize('schema.json.multipleOf', 'A number that should cleanly divide the current value (i.e. have no remainder)')
|
||||
},
|
||||
'maximum': {
|
||||
'type': 'number',
|
||||
'description': localize('schema.json.maximum', 'The maximum numerical value, inclusive by default.')
|
||||
},
|
||||
'exclusiveMaximum': {
|
||||
'type': 'boolean',
|
||||
'default': false,
|
||||
'description': localize('schema.json.exclusiveMaximum', 'Makes the maximum property exclusive.')
|
||||
},
|
||||
'minimum': {
|
||||
'type': 'number',
|
||||
'description': localize('schema.json.minimum', 'The minimum numerical value, inclusive by default.')
|
||||
},
|
||||
'exclusiveMinimum': {
|
||||
'type': 'boolean',
|
||||
'default': false,
|
||||
'description': localize('schema.json.exclusiveMininum', 'Makes the minimum property exclusive.')
|
||||
},
|
||||
'maxLength': {
|
||||
'allOf': [
|
||||
{ '$ref': '#/definitions/positiveInteger' }
|
||||
],
|
||||
'description': localize('schema.json.maxLength', 'The maximum length of a string.')
|
||||
},
|
||||
'minLength': {
|
||||
'allOf': [
|
||||
{ '$ref': '#/definitions/positiveIntegerDefault0' }
|
||||
],
|
||||
'description': localize('schema.json.minLength', 'The minimum length of a string.')
|
||||
},
|
||||
'pattern': {
|
||||
'type': 'string',
|
||||
'format': 'regex',
|
||||
'description': localize('schema.json.pattern', 'A regular expression to match the string against. It is not implicitly anchored.')
|
||||
},
|
||||
'additionalItems': {
|
||||
'anyOf': [
|
||||
{ 'type': 'boolean' },
|
||||
{ '$ref': '#' }
|
||||
],
|
||||
'default': {},
|
||||
'description': localize('schema.json.additionalItems', 'For arrays, only when items is set as an array. If it is a schema, then this schema validates items after the ones specified by the items array. If it is false, then additional items will cause validation to fail.')
|
||||
},
|
||||
'items': {
|
||||
'anyOf': [
|
||||
{ '$ref': '#' },
|
||||
{ '$ref': '#/definitions/schemaArray' }
|
||||
],
|
||||
'default': {},
|
||||
'description': localize('schema.json.items', 'For arrays. Can either be a schema to validate every element against or an array of schemas to validate each item against in order (the first schema will validate the first element, the second schema will validate the second element, and so on.')
|
||||
},
|
||||
'maxItems': {
|
||||
'allOf': [
|
||||
{ '$ref': '#/definitions/positiveInteger' }
|
||||
],
|
||||
'description': localize('schema.json.maxItems', 'The maximum number of items that can be inside an array. Inclusive.')
|
||||
},
|
||||
'minItems': {
|
||||
'allOf': [
|
||||
{ '$ref': '#/definitions/positiveIntegerDefault0' }
|
||||
],
|
||||
'description': localize('schema.json.minItems', 'The minimum number of items that can be inside an array. Inclusive.')
|
||||
},
|
||||
'uniqueItems': {
|
||||
'type': 'boolean',
|
||||
'default': false,
|
||||
'description': localize('schema.json.uniqueItems', 'If all of the items in the array must be unique. Defaults to false.')
|
||||
},
|
||||
'maxProperties': {
|
||||
'allOf': [
|
||||
{ '$ref': '#/definitions/positiveInteger' }
|
||||
],
|
||||
'description': localize('schema.json.maxProperties', 'The maximum number of properties an object can have. Inclusive.')
|
||||
},
|
||||
'minProperties': {
|
||||
'allOf': [
|
||||
{ '$ref': '#/definitions/positiveIntegerDefault0' },
|
||||
],
|
||||
'description': localize('schema.json.minProperties', 'The minimum number of properties an object can have. Inclusive.')
|
||||
},
|
||||
'required': {
|
||||
'allOf': [
|
||||
{ '$ref': '#/definitions/stringArray' }
|
||||
],
|
||||
'description': localize('schema.json.required', 'An array of strings that lists the names of all properties required on this object.')
|
||||
},
|
||||
'additionalProperties': {
|
||||
'anyOf': [
|
||||
{ 'type': 'boolean' },
|
||||
{ '$ref': '#' }
|
||||
],
|
||||
'default': {},
|
||||
'description': localize('schema.json.additionalProperties', 'Either a schema or a boolean. If a schema, then used to validate all properties not matched by \'properties\' or \'patternProperties\'. If false, then any properties not matched by either will cause this schema to fail.')
|
||||
},
|
||||
'definitions': {
|
||||
'type': 'object',
|
||||
'additionalProperties': { '$ref': '#' },
|
||||
'default': {},
|
||||
'description': localize('schema.json.definitions', 'Not used for validation. Place subschemas here that you wish to reference inline with $ref')
|
||||
},
|
||||
'properties': {
|
||||
'type': 'object',
|
||||
'additionalProperties': { '$ref': '#' },
|
||||
'default': {},
|
||||
'description': localize('schema.json.properties', 'A map of property names to schemas for each property.')
|
||||
},
|
||||
'patternProperties': {
|
||||
'type': 'object',
|
||||
'additionalProperties': { '$ref': '#' },
|
||||
'default': {},
|
||||
'description': localize('schema.json.patternProperties', 'A map of regular expressions on property names to schemas for matching properties.')
|
||||
},
|
||||
'dependencies': {
|
||||
'type': 'object',
|
||||
'additionalProperties': {
|
||||
'anyOf': [
|
||||
{ '$ref': '#' },
|
||||
{ '$ref': '#/definitions/stringArray' }
|
||||
]
|
||||
},
|
||||
'description': localize('schema.json.dependencies', 'A map of property names to either an array of property names or a schema. An array of property names means the property named in the key depends on the properties in the array being present in the object in order to be valid. If the value is a schema, then the schema is only applied to the object if the property in the key exists on the object.')
|
||||
},
|
||||
'enum': {
|
||||
'type': 'array',
|
||||
'minItems': 1,
|
||||
'uniqueItems': true,
|
||||
'description': localize('schema.json.enum', 'The set of literal values that are valid')
|
||||
},
|
||||
'type': {
|
||||
'anyOf': [
|
||||
{ '$ref': '#/definitions/simpleTypes' },
|
||||
{
|
||||
'type': 'array',
|
||||
'items': { '$ref': '#/definitions/simpleTypes' },
|
||||
'minItems': 1,
|
||||
'uniqueItems': true
|
||||
}
|
||||
],
|
||||
'description': localize('schema.json.type', 'Either a string of one of the basic schema types (number, integer, null, array, object, boolean, string) or an array of strings specifying a subset of those types.')
|
||||
},
|
||||
'format': {
|
||||
'anyOf': [
|
||||
{
|
||||
'type': 'string',
|
||||
'description': localize('schema.json.format', 'Describes the format expected for the value.'),
|
||||
'enum': ['date-time', 'uri', 'email', 'hostname', 'ipv4', 'ipv6', 'regex']
|
||||
}, {
|
||||
'type': 'string'
|
||||
}
|
||||
]
|
||||
},
|
||||
'allOf': {
|
||||
'allOf': [
|
||||
{ '$ref': '#/definitions/schemaArray' }
|
||||
],
|
||||
'description': localize('schema.json.allOf', 'An array of schemas, all of which must match.')
|
||||
},
|
||||
'anyOf': {
|
||||
'allOf': [
|
||||
{ '$ref': '#/definitions/schemaArray' }
|
||||
],
|
||||
'description': localize('schema.json.anyOf', 'An array of schemas, where at least one must match.')
|
||||
},
|
||||
'oneOf': {
|
||||
'allOf': [
|
||||
{ '$ref': '#/definitions/schemaArray' }
|
||||
],
|
||||
'description': localize('schema.json.oneOf', 'An array of schemas, exactly one of which must match.')
|
||||
},
|
||||
'not': {
|
||||
'allOf': [
|
||||
{ '$ref': '#' }
|
||||
],
|
||||
'description': localize('schema.json.not', 'A schema which must not match.')
|
||||
}
|
||||
},
|
||||
'dependencies': {
|
||||
'exclusiveMaximum': ['maximum'],
|
||||
'exclusiveMinimum': ['minimum']
|
||||
},
|
||||
'default': {}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,522 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
|
||||
import Parser = require('./jsonParser');
|
||||
import SchemaService = require('./jsonSchemaService');
|
||||
import JsonSchema = require('./jsonSchema');
|
||||
import {IJSONWorkerContribution} from './jsonContributions';
|
||||
|
||||
import {CompletionItem, CompletionItemKind, CompletionList, TextDocument, Position, Range, TextEdit} from 'vscode-languageserver';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export interface ISuggestionsCollector {
|
||||
add(suggestion: CompletionItem): void;
|
||||
error(message:string): void;
|
||||
log(message:string): void;
|
||||
setAsIncomplete(): void;
|
||||
}
|
||||
|
||||
export class JSONCompletion {
|
||||
|
||||
private schemaService: SchemaService.IJSONSchemaService;
|
||||
private contributions: IJSONWorkerContribution[];
|
||||
|
||||
constructor(schemaService: SchemaService.IJSONSchemaService, contributions: IJSONWorkerContribution[] = []) {
|
||||
this.schemaService = schemaService;
|
||||
this.contributions = contributions;
|
||||
}
|
||||
|
||||
public doResolve(item: CompletionItem) : Thenable<CompletionItem> {
|
||||
for (let i = this.contributions.length - 1; i >= 0; i--) {
|
||||
if (this.contributions[i].resolveSuggestion) {
|
||||
let resolver = this.contributions[i].resolveSuggestion(item);
|
||||
if (resolver) {
|
||||
return resolver;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.resolve(item);
|
||||
}
|
||||
|
||||
public doComplete(document: TextDocument, position: Position, doc: Parser.JSONDocument): Thenable<CompletionList> {
|
||||
|
||||
let offset = document.offsetAt(position);
|
||||
let node = doc.getNodeFromOffsetEndInclusive(offset);
|
||||
|
||||
let currentWord = this.getCurrentWord(document, offset);
|
||||
let overwriteRange = null;
|
||||
let result: CompletionList = {
|
||||
items: [],
|
||||
isIncomplete: false
|
||||
};
|
||||
|
||||
if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) {
|
||||
overwriteRange = Range.create(document.positionAt(node.start), document.positionAt(node.end));
|
||||
} else {
|
||||
overwriteRange = Range.create(document.positionAt(offset - currentWord.length), position);
|
||||
}
|
||||
|
||||
let proposed: { [key: string]: boolean } = {};
|
||||
let collector: ISuggestionsCollector = {
|
||||
add: (suggestion: CompletionItem) => {
|
||||
if (!proposed[suggestion.label]) {
|
||||
proposed[suggestion.label] = true;
|
||||
if (overwriteRange) {
|
||||
suggestion.textEdit = TextEdit.replace(overwriteRange, suggestion.insertText);
|
||||
}
|
||||
|
||||
result.items.push(suggestion);
|
||||
}
|
||||
},
|
||||
setAsIncomplete: () => {
|
||||
result.isIncomplete = true;
|
||||
},
|
||||
error: (message: string) => {
|
||||
console.error(message);
|
||||
},
|
||||
log: (message: string) => {
|
||||
console.log(message);
|
||||
}
|
||||
};
|
||||
|
||||
return this.schemaService.getSchemaForResource(document.uri, doc).then((schema) => {
|
||||
let collectionPromises: Thenable<any>[] = [];
|
||||
|
||||
let addValue = true;
|
||||
let currentKey = '';
|
||||
|
||||
let currentProperty: Parser.PropertyASTNode = null;
|
||||
if (node) {
|
||||
|
||||
if (node.type === 'string') {
|
||||
let stringNode = <Parser.StringASTNode>node;
|
||||
if (stringNode.isKey) {
|
||||
addValue = !(node.parent && ((<Parser.PropertyASTNode>node.parent).value));
|
||||
currentProperty = node.parent ? <Parser.PropertyASTNode>node.parent : null;
|
||||
currentKey = document.getText().substring(node.start + 1, node.end - 1);
|
||||
if (node.parent) {
|
||||
node = node.parent.parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// proposals for properties
|
||||
if (node && node.type === 'object') {
|
||||
// don't suggest keys when the cursor is just before the opening curly brace
|
||||
if (node.start === offset) {
|
||||
return result;
|
||||
}
|
||||
// don't suggest properties that are already present
|
||||
let properties = (<Parser.ObjectASTNode>node).properties;
|
||||
properties.forEach(p => {
|
||||
if (!currentProperty || currentProperty !== p) {
|
||||
proposed[p.key.value] = true;
|
||||
}
|
||||
});
|
||||
|
||||
let isLast = properties.length === 0 || offset >= properties[properties.length - 1].start;
|
||||
if (schema) {
|
||||
// property proposals with schema
|
||||
this.getPropertySuggestions(schema, doc, node, addValue, isLast, collector);
|
||||
} else {
|
||||
// property proposals without schema
|
||||
this.getSchemaLessPropertySuggestions(doc, node, currentKey, currentWord, isLast, collector);
|
||||
}
|
||||
|
||||
let location = node.getNodeLocation();
|
||||
this.contributions.forEach((contribution) => {
|
||||
let collectPromise = contribution.collectPropertySuggestions(document.uri, location, currentWord, addValue, isLast, collector);
|
||||
if (collectPromise) {
|
||||
collectionPromises.push(collectPromise);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// proposals for values
|
||||
if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) {
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
if (schema) {
|
||||
// value proposals with schema
|
||||
this.getValueSuggestions(schema, doc, node, offset, collector);
|
||||
} else {
|
||||
// value proposals without schema
|
||||
this.getSchemaLessValueSuggestions(doc, node, offset, document, collector);
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
this.contributions.forEach((contribution) => {
|
||||
let collectPromise = contribution.collectDefaultSuggestions(document.uri, collector);
|
||||
if (collectPromise) {
|
||||
collectionPromises.push(collectPromise);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if ((node.type === 'property') && offset > (<Parser.PropertyASTNode> node).colonOffset) {
|
||||
let parentKey = (<Parser.PropertyASTNode>node).key.value;
|
||||
|
||||
let valueNode = (<Parser.PropertyASTNode> node).value;
|
||||
if (!valueNode || offset <= valueNode.end) {
|
||||
let location = node.parent.getNodeLocation();
|
||||
this.contributions.forEach((contribution) => {
|
||||
let collectPromise = contribution.collectValueSuggestions(document.uri, location, parentKey, collector);
|
||||
if (collectPromise) {
|
||||
collectionPromises.push(collectPromise);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.all(collectionPromises).then(() => result );
|
||||
});
|
||||
}
|
||||
|
||||
private getPropertySuggestions(schema: SchemaService.ResolvedSchema, doc: Parser.JSONDocument, node: Parser.ASTNode, addValue: boolean, isLast: boolean, collector: ISuggestionsCollector): void {
|
||||
let matchingSchemas: Parser.IApplicableSchema[] = [];
|
||||
doc.validate(schema.schema, matchingSchemas, node.start);
|
||||
|
||||
matchingSchemas.forEach((s) => {
|
||||
if (s.node === node && !s.inverted) {
|
||||
let schemaProperties = s.schema.properties;
|
||||
if (schemaProperties) {
|
||||
Object.keys(schemaProperties).forEach((key: string) => {
|
||||
let propertySchema = schemaProperties[key];
|
||||
collector.add({ kind: CompletionItemKind.Property, label: key, insertText: this.getTextForProperty(key, propertySchema, addValue, isLast), documentation: propertySchema.description || '' });
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private getSchemaLessPropertySuggestions(doc: Parser.JSONDocument, node: Parser.ASTNode, currentKey: string, currentWord: string, isLast: boolean, collector: ISuggestionsCollector): void {
|
||||
let collectSuggestionsForSimilarObject = (obj: Parser.ObjectASTNode) => {
|
||||
obj.properties.forEach((p) => {
|
||||
let key = p.key.value;
|
||||
collector.add({ kind: CompletionItemKind.Property, label: key, insertText: this.getTextForSimilarProperty(key, p.value), documentation: '' });
|
||||
});
|
||||
};
|
||||
if (node.parent) {
|
||||
if (node.parent.type === 'property') {
|
||||
// if the object is a property value, check the tree for other objects that hang under a property of the same name
|
||||
let parentKey = (<Parser.PropertyASTNode>node.parent).key.value;
|
||||
doc.visit((n) => {
|
||||
if (n.type === 'property' && (<Parser.PropertyASTNode>n).key.value === parentKey && (<Parser.PropertyASTNode>n).value && (<Parser.PropertyASTNode>n).value.type === 'object') {
|
||||
collectSuggestionsForSimilarObject(<Parser.ObjectASTNode>(<Parser.PropertyASTNode>n).value);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
} else if (node.parent.type === 'array') {
|
||||
// if the object is in an array, use all other array elements as similar objects
|
||||
(<Parser.ArrayASTNode>node.parent).items.forEach((n) => {
|
||||
if (n.type === 'object' && n !== node) {
|
||||
collectSuggestionsForSimilarObject(<Parser.ObjectASTNode>n);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!currentKey && currentWord.length > 0) {
|
||||
collector.add({ kind: CompletionItemKind.Property, label: this.getLabelForValue(currentWord), insertText: this.getTextForProperty(currentWord, null, true, isLast), documentation: '' });
|
||||
}
|
||||
}
|
||||
|
||||
private getSchemaLessValueSuggestions(doc: Parser.JSONDocument, node: Parser.ASTNode, offset: number, document: TextDocument, collector: ISuggestionsCollector): void {
|
||||
let collectSuggestionsForValues = (value: Parser.ASTNode) => {
|
||||
if (!value.contains(offset)) {
|
||||
let content = this.getTextForMatchingNode(value, document);
|
||||
collector.add({ kind: this.getSuggestionKind(value.type), label: content, insertText: content, documentation: '' });
|
||||
}
|
||||
if (value.type === 'boolean') {
|
||||
this.addBooleanSuggestion(!value.getValue(), collector);
|
||||
}
|
||||
};
|
||||
|
||||
if (!node) {
|
||||
collector.add({ kind: this.getSuggestionKind('object'), label: 'Empty object', insertText: '{\n\t{{}}\n}', documentation: '' });
|
||||
collector.add({ kind: this.getSuggestionKind('array'), label: 'Empty array', insertText: '[\n\t{{}}\n]', documentation: '' });
|
||||
} else {
|
||||
if (node.type === 'property' && offset > (<Parser.PropertyASTNode>node).colonOffset) {
|
||||
let valueNode = (<Parser.PropertyASTNode>node).value;
|
||||
if (valueNode && offset > valueNode.end) {
|
||||
return;
|
||||
}
|
||||
// suggest values at the same key
|
||||
let parentKey = (<Parser.PropertyASTNode>node).key.value;
|
||||
doc.visit((n) => {
|
||||
if (n.type === 'property' && (<Parser.PropertyASTNode>n).key.value === parentKey && (<Parser.PropertyASTNode>n).value) {
|
||||
collectSuggestionsForValues((<Parser.PropertyASTNode>n).value);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (node.type === 'array') {
|
||||
if (node.parent && node.parent.type === 'property') {
|
||||
// suggest items of an array at the same key
|
||||
let parentKey = (<Parser.PropertyASTNode>node.parent).key.value;
|
||||
doc.visit((n) => {
|
||||
if (n.type === 'property' && (<Parser.PropertyASTNode>n).key.value === parentKey && (<Parser.PropertyASTNode>n).value && (<Parser.PropertyASTNode>n).value.type === 'array') {
|
||||
((<Parser.ArrayASTNode>(<Parser.PropertyASTNode>n).value).items).forEach((n) => {
|
||||
collectSuggestionsForValues(<Parser.ObjectASTNode>n);
|
||||
});
|
||||
}
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
// suggest items in the same array
|
||||
(<Parser.ArrayASTNode>node).items.forEach((n) => {
|
||||
collectSuggestionsForValues(<Parser.ObjectASTNode>n);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private getValueSuggestions(schema: SchemaService.ResolvedSchema, doc: Parser.JSONDocument, node: Parser.ASTNode, offset: number, collector: ISuggestionsCollector): void {
|
||||
|
||||
if (!node) {
|
||||
this.addDefaultSuggestion(schema.schema, collector);
|
||||
} else {
|
||||
let parentKey: string = null;
|
||||
if (node && (node.type === 'property') && offset > (<Parser.PropertyASTNode>node).colonOffset) {
|
||||
let valueNode = (<Parser.PropertyASTNode>node).value;
|
||||
if (valueNode && offset > valueNode.end) {
|
||||
return; // we are past the value node
|
||||
}
|
||||
parentKey = (<Parser.PropertyASTNode>node).key.value;
|
||||
node = node.parent;
|
||||
}
|
||||
if (node && (parentKey !== null || node.type === 'array')) {
|
||||
let matchingSchemas: Parser.IApplicableSchema[] = [];
|
||||
doc.validate(schema.schema, matchingSchemas, node.start);
|
||||
|
||||
matchingSchemas.forEach((s) => {
|
||||
if (s.node === node && !s.inverted && s.schema) {
|
||||
if (s.schema.items) {
|
||||
this.addDefaultSuggestion(s.schema.items, collector);
|
||||
this.addEnumSuggestion(s.schema.items, collector);
|
||||
}
|
||||
if (s.schema.properties) {
|
||||
let propertySchema = s.schema.properties[parentKey];
|
||||
if (propertySchema) {
|
||||
this.addDefaultSuggestion(propertySchema, collector);
|
||||
this.addEnumSuggestion(propertySchema, collector);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private addBooleanSuggestion(value: boolean, collector: ISuggestionsCollector): void {
|
||||
collector.add({ kind: this.getSuggestionKind('boolean'), label: value ? 'true' : 'false', insertText: this.getTextForValue(value), documentation: '' });
|
||||
}
|
||||
|
||||
private addNullSuggestion(collector: ISuggestionsCollector): void {
|
||||
collector.add({ kind: this.getSuggestionKind('null'), label: 'null', insertText: 'null', documentation: '' });
|
||||
}
|
||||
|
||||
private addEnumSuggestion(schema: JsonSchema.IJSONSchema, collector: ISuggestionsCollector): void {
|
||||
if (Array.isArray(schema.enum)) {
|
||||
schema.enum.forEach((enm) => collector.add({ kind: this.getSuggestionKind(schema.type), label: this.getLabelForValue(enm), insertText: this.getTextForValue(enm), documentation: '' }));
|
||||
} else {
|
||||
if (this.isType(schema, 'boolean')) {
|
||||
this.addBooleanSuggestion(true, collector);
|
||||
this.addBooleanSuggestion(false, collector);
|
||||
}
|
||||
if (this.isType(schema, 'null')) {
|
||||
this.addNullSuggestion(collector);
|
||||
}
|
||||
}
|
||||
if (Array.isArray(schema.allOf)) {
|
||||
schema.allOf.forEach((s) => this.addEnumSuggestion(s, collector));
|
||||
}
|
||||
if (Array.isArray(schema.anyOf)) {
|
||||
schema.anyOf.forEach((s) => this.addEnumSuggestion(s, collector));
|
||||
}
|
||||
if (Array.isArray(schema.oneOf)) {
|
||||
schema.oneOf.forEach((s) => this.addEnumSuggestion(s, collector));
|
||||
}
|
||||
}
|
||||
|
||||
private isType(schema: JsonSchema.IJSONSchema, type: string) {
|
||||
if (Array.isArray(schema.type)) {
|
||||
return schema.type.indexOf(type) !== -1;
|
||||
}
|
||||
return schema.type === type;
|
||||
}
|
||||
|
||||
private addDefaultSuggestion(schema: JsonSchema.IJSONSchema, collector: ISuggestionsCollector): void {
|
||||
if (schema.default) {
|
||||
collector.add({
|
||||
kind: this.getSuggestionKind(schema.type),
|
||||
label: this.getLabelForValue(schema.default),
|
||||
insertText: this.getTextForValue(schema.default),
|
||||
detail: localize('json.suggest.default', 'Default value'),
|
||||
});
|
||||
}
|
||||
if (Array.isArray(schema.defaultSnippets)) {
|
||||
schema.defaultSnippets.forEach(s => {
|
||||
collector.add({
|
||||
kind: CompletionItemKind.Snippet,
|
||||
label: this.getLabelForSnippetValue(s.body),
|
||||
insertText: this.getTextForSnippetValue(s.body)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (Array.isArray(schema.allOf)) {
|
||||
schema.allOf.forEach((s) => this.addDefaultSuggestion(s, collector));
|
||||
}
|
||||
if (Array.isArray(schema.anyOf)) {
|
||||
schema.anyOf.forEach((s) => this.addDefaultSuggestion(s, collector));
|
||||
}
|
||||
if (Array.isArray(schema.oneOf)) {
|
||||
schema.oneOf.forEach((s) => this.addDefaultSuggestion(s, collector));
|
||||
}
|
||||
}
|
||||
|
||||
private getLabelForValue(value: any): string {
|
||||
let label = JSON.stringify(value);
|
||||
if (label.length > 57) {
|
||||
return label.substr(0, 57).trim() + '...';
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
private getLabelForSnippetValue(value: any): string {
|
||||
let label = JSON.stringify(value);
|
||||
label = label.replace(/\{\{|\}\}/g, '');
|
||||
if (label.length > 57) {
|
||||
return label.substr(0, 57).trim() + '...';
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
private getTextForValue(value: any): string {
|
||||
var text = JSON.stringify(value, null, '\t');
|
||||
text = text.replace(/[\\\{\}]/g, '\\$&');
|
||||
return text;
|
||||
}
|
||||
|
||||
private getTextForSnippetValue(value: any): string {
|
||||
return JSON.stringify(value, null, '\t');
|
||||
}
|
||||
|
||||
private getTextForEnumValue(value: any): string {
|
||||
let snippet = this.getTextForValue(value);
|
||||
switch (typeof value) {
|
||||
case 'object':
|
||||
if (value === null) {
|
||||
return '{{null}}';
|
||||
}
|
||||
return snippet;
|
||||
case 'string':
|
||||
return '"{{' + snippet.substr(1, snippet.length - 2) + '}}"';
|
||||
case 'number':
|
||||
case 'boolean':
|
||||
return '{{' + snippet + '}}';
|
||||
}
|
||||
return snippet;
|
||||
}
|
||||
|
||||
private getSuggestionKind(type: any): CompletionItemKind {
|
||||
if (Array.isArray(type)) {
|
||||
let array = <any[]>type;
|
||||
type = array.length > 0 ? array[0] : null;
|
||||
}
|
||||
if (!type) {
|
||||
return CompletionItemKind.Value;
|
||||
}
|
||||
switch (type) {
|
||||
case 'string': return CompletionItemKind.Value;
|
||||
case 'object': return CompletionItemKind.Module;
|
||||
case 'property': return CompletionItemKind.Property;
|
||||
default: return CompletionItemKind.Value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private getTextForMatchingNode(node: Parser.ASTNode, document: TextDocument): string {
|
||||
switch (node.type) {
|
||||
case 'array':
|
||||
return '[]';
|
||||
case 'object':
|
||||
return '{}';
|
||||
default:
|
||||
let content = document.getText().substr(node.start, node.end - node.start);
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
private getTextForProperty(key: string, propertySchema: JsonSchema.IJSONSchema, addValue: boolean, isLast: boolean): string {
|
||||
|
||||
let result = this.getTextForValue(key);
|
||||
if (!addValue) {
|
||||
return result;
|
||||
}
|
||||
result += ': ';
|
||||
|
||||
if (propertySchema) {
|
||||
let defaultVal = propertySchema.default;
|
||||
if (typeof defaultVal !== 'undefined') {
|
||||
result = result + this.getTextForEnumValue(defaultVal);
|
||||
} else if (propertySchema.enum && propertySchema.enum.length > 0) {
|
||||
result = result + this.getTextForEnumValue(propertySchema.enum[0]);
|
||||
} else {
|
||||
var type = Array.isArray(propertySchema.type) ? propertySchema.type[0] : propertySchema.type;
|
||||
switch (type) {
|
||||
case 'boolean':
|
||||
result += '{{false}}';
|
||||
break;
|
||||
case 'string':
|
||||
result += '"{{}}"';
|
||||
break;
|
||||
case 'object':
|
||||
result += '{\n\t{{}}\n}';
|
||||
break;
|
||||
case 'array':
|
||||
result += '[\n\t{{}}\n]';
|
||||
break;
|
||||
case 'number':
|
||||
result += '{{0}}';
|
||||
break;
|
||||
case 'null':
|
||||
result += '{{null}}';
|
||||
break;
|
||||
default:
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result += '{{0}}';
|
||||
}
|
||||
if (!isLast) {
|
||||
result += ',';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private getTextForSimilarProperty(key: string, templateValue: Parser.ASTNode): string {
|
||||
return this.getTextForValue(key);
|
||||
}
|
||||
|
||||
private getCurrentWord(document: TextDocument, offset: number) {
|
||||
var i = offset - 1;
|
||||
var text = document.getText();
|
||||
while (i >= 0 && ' \t\n\r\v":{[,'.indexOf(text.charAt(i)) === -1) {
|
||||
i--;
|
||||
}
|
||||
return text.substring(i+1, offset);
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {JSONLocation} from './jsonLocation';
|
||||
import {ISuggestionsCollector} from './jsonCompletion';
|
||||
|
||||
import {MarkedString, CompletionItem} from 'vscode-languageserver';
|
||||
|
||||
export {ISuggestionsCollector} from './jsonCompletion';
|
||||
|
||||
|
||||
export interface IJSONWorkerContribution {
|
||||
getInfoContribution(resource: string, location: JSONLocation) : Thenable<MarkedString[]>;
|
||||
collectPropertySuggestions(resource: string, location: JSONLocation, currentWord: string, addValue: boolean, isLast:boolean, result: ISuggestionsCollector) : Thenable<any>;
|
||||
collectValueSuggestions(resource: string, location: JSONLocation, propertyKey: string, result: ISuggestionsCollector): Thenable<any>;
|
||||
collectDefaultSuggestions(resource: string, result: ISuggestionsCollector): Thenable<any>;
|
||||
resolveSuggestion?(item: CompletionItem): Thenable<CompletionItem>;
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import Parser = require('./jsonParser');
|
||||
import Strings = require('./utils/strings');
|
||||
|
||||
import {SymbolInformation, SymbolKind, TextDocument, Range, Location} from 'vscode-languageserver';
|
||||
|
||||
export class JSONDocumentSymbols {
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
public findDocumentSymbols(document: TextDocument, doc: Parser.JSONDocument): Promise<SymbolInformation[]> {
|
||||
|
||||
let root = doc.root;
|
||||
if (!root) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
// special handling for key bindings
|
||||
let resourceString = document.uri;
|
||||
if ((resourceString === 'vscode://defaultsettings/keybindings.json') || Strings.endsWith(resourceString.toLowerCase(), '/user/keybindings.json')) {
|
||||
if (root.type === 'array') {
|
||||
let result: SymbolInformation[] = [];
|
||||
(<Parser.ArrayASTNode>root).items.forEach((item) => {
|
||||
if (item.type === 'object') {
|
||||
let property = (<Parser.ObjectASTNode>item).getFirstProperty('key');
|
||||
if (property && property.value) {
|
||||
let location = Location.create(document.uri, Range.create(document.positionAt(item.start), document.positionAt(item.end)));
|
||||
result.push({ name: property.value.getValue(), kind: SymbolKind.Function, location: location });
|
||||
}
|
||||
}
|
||||
});
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
|
||||
let collectOutlineEntries = (result: SymbolInformation[], node: Parser.ASTNode, containerName: string): SymbolInformation[] => {
|
||||
if (node.type === 'array') {
|
||||
(<Parser.ArrayASTNode>node).items.forEach((node: Parser.ASTNode) => {
|
||||
collectOutlineEntries(result, node, containerName);
|
||||
});
|
||||
} else if (node.type === 'object') {
|
||||
let objectNode = <Parser.ObjectASTNode>node;
|
||||
|
||||
objectNode.properties.forEach((property: Parser.PropertyASTNode) => {
|
||||
let location = Location.create(document.uri, Range.create(document.positionAt(property.start), document.positionAt(property.end)));
|
||||
let valueNode = property.value;
|
||||
if (valueNode) {
|
||||
let childContainerName = containerName ? containerName + '.' + property.key.name : property.key.name;
|
||||
result.push({ name: property.key.getValue(), kind: this.getSymbolKind(valueNode.type), location: location, containerName: containerName });
|
||||
collectOutlineEntries(result, valueNode, childContainerName);
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
let result = collectOutlineEntries([], root, void 0);
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
private getSymbolKind(nodeType: string): SymbolKind {
|
||||
switch (nodeType) {
|
||||
case 'object':
|
||||
return SymbolKind.Module;
|
||||
case 'string':
|
||||
return SymbolKind.String;
|
||||
case 'number':
|
||||
return SymbolKind.Number;
|
||||
case 'array':
|
||||
return SymbolKind.Array;
|
||||
case 'boolean':
|
||||
return SymbolKind.Boolean;
|
||||
default: // 'null'
|
||||
return SymbolKind.Variable;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import Json = require('jsonc-parser');
|
||||
import {TextDocument, Range, Position, FormattingOptions, TextEdit} from 'vscode-languageserver';
|
||||
|
||||
export function format(document: TextDocument, range: Range, options: FormattingOptions): TextEdit[] {
|
||||
const documentText = document.getText();
|
||||
let initialIndentLevel: number;
|
||||
let value: string;
|
||||
let rangeOffset: number;
|
||||
if (range) {
|
||||
let startPosition = Position.create(range.start.line, 0);
|
||||
rangeOffset = document.offsetAt(startPosition);
|
||||
|
||||
let endOffset = document.offsetAt(Position.create(range.end.line + 1, 0));
|
||||
let endLineStart = document.offsetAt(Position.create(range.end.line, 0));
|
||||
while (endOffset > endLineStart && isEOL(documentText, endOffset - 1)) {
|
||||
endOffset--;
|
||||
}
|
||||
range = Range.create(startPosition, document.positionAt(endOffset));
|
||||
value = documentText.substring(rangeOffset, endOffset);
|
||||
initialIndentLevel = computeIndentLevel(value, 0, options);
|
||||
} else {
|
||||
value = documentText;
|
||||
range = Range.create(Position.create(0, 0), document.positionAt(value.length));
|
||||
initialIndentLevel = 0;
|
||||
rangeOffset = 0;
|
||||
}
|
||||
let eol = getEOL(document);
|
||||
|
||||
let lineBreak = false;
|
||||
let indentLevel = 0;
|
||||
let indentValue: string;
|
||||
if (options.insertSpaces) {
|
||||
indentValue = repeat(' ', options.tabSize);
|
||||
} else {
|
||||
indentValue = '\t';
|
||||
}
|
||||
|
||||
let scanner = Json.createScanner(value, false);
|
||||
|
||||
function newLineAndIndent(): string {
|
||||
return eol + repeat(indentValue, initialIndentLevel + indentLevel);
|
||||
}
|
||||
function scanNext(): Json.SyntaxKind {
|
||||
let token = scanner.scan();
|
||||
lineBreak = false;
|
||||
while (token === Json.SyntaxKind.Trivia || token === Json.SyntaxKind.LineBreakTrivia) {
|
||||
lineBreak = lineBreak || (token === Json.SyntaxKind.LineBreakTrivia);
|
||||
token = scanner.scan();
|
||||
}
|
||||
return token;
|
||||
}
|
||||
let editOperations: TextEdit[] = [];
|
||||
function addEdit(text: string, startOffset: number, endOffset: number) {
|
||||
if (documentText.substring(startOffset, endOffset) !== text) {
|
||||
let replaceRange = Range.create(document.positionAt(startOffset), document.positionAt(endOffset));
|
||||
editOperations.push(TextEdit.replace(replaceRange, text));
|
||||
}
|
||||
}
|
||||
|
||||
let firstToken = scanNext();
|
||||
if (firstToken !== Json.SyntaxKind.EOF) {
|
||||
let firstTokenStart = scanner.getTokenOffset() + rangeOffset;
|
||||
let initialIndent = repeat(indentValue, initialIndentLevel);
|
||||
addEdit(initialIndent, rangeOffset, firstTokenStart);
|
||||
}
|
||||
|
||||
while (firstToken !== Json.SyntaxKind.EOF) {
|
||||
let firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + rangeOffset;
|
||||
let secondToken = scanNext();
|
||||
|
||||
let replaceContent = '';
|
||||
while (!lineBreak && (secondToken === Json.SyntaxKind.LineCommentTrivia || secondToken === Json.SyntaxKind.BlockCommentTrivia)) {
|
||||
// comments on the same line: keep them on the same line, but ignore them otherwise
|
||||
let commentTokenStart = scanner.getTokenOffset() + rangeOffset;
|
||||
addEdit(' ', firstTokenEnd, commentTokenStart);
|
||||
firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + rangeOffset;
|
||||
replaceContent = secondToken === Json.SyntaxKind.LineCommentTrivia ? newLineAndIndent() : '';
|
||||
secondToken = scanNext();
|
||||
}
|
||||
|
||||
if (secondToken === Json.SyntaxKind.CloseBraceToken) {
|
||||
if (firstToken !== Json.SyntaxKind.OpenBraceToken) {
|
||||
indentLevel--;
|
||||
replaceContent = newLineAndIndent();
|
||||
}
|
||||
} else if (secondToken === Json.SyntaxKind.CloseBracketToken) {
|
||||
if (firstToken !== Json.SyntaxKind.OpenBracketToken) {
|
||||
indentLevel--;
|
||||
replaceContent = newLineAndIndent();
|
||||
}
|
||||
} else {
|
||||
switch (firstToken) {
|
||||
case Json.SyntaxKind.OpenBracketToken:
|
||||
case Json.SyntaxKind.OpenBraceToken:
|
||||
indentLevel++;
|
||||
replaceContent = newLineAndIndent();
|
||||
break;
|
||||
case Json.SyntaxKind.CommaToken:
|
||||
case Json.SyntaxKind.LineCommentTrivia:
|
||||
replaceContent = newLineAndIndent();
|
||||
break;
|
||||
case Json.SyntaxKind.BlockCommentTrivia:
|
||||
if (lineBreak) {
|
||||
replaceContent = newLineAndIndent();
|
||||
} else {
|
||||
// symbol following comment on the same line: keep on same line, separate with ' '
|
||||
replaceContent = ' ';
|
||||
}
|
||||
break;
|
||||
case Json.SyntaxKind.ColonToken:
|
||||
replaceContent = ' ';
|
||||
break;
|
||||
case Json.SyntaxKind.NullKeyword:
|
||||
case Json.SyntaxKind.TrueKeyword:
|
||||
case Json.SyntaxKind.FalseKeyword:
|
||||
case Json.SyntaxKind.NumericLiteral:
|
||||
if (secondToken === Json.SyntaxKind.NullKeyword || secondToken === Json.SyntaxKind.FalseKeyword || secondToken === Json.SyntaxKind.NumericLiteral) {
|
||||
replaceContent = ' ';
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (lineBreak && (secondToken === Json.SyntaxKind.LineCommentTrivia || secondToken === Json.SyntaxKind.BlockCommentTrivia)) {
|
||||
replaceContent = newLineAndIndent();
|
||||
}
|
||||
|
||||
}
|
||||
let secondTokenStart = scanner.getTokenOffset() + rangeOffset;
|
||||
addEdit(replaceContent, firstTokenEnd, secondTokenStart);
|
||||
firstToken = secondToken;
|
||||
}
|
||||
return editOperations;
|
||||
}
|
||||
|
||||
function repeat(s: string, count: number): string {
|
||||
let result = '';
|
||||
for (let i = 0; i < count; i++) {
|
||||
result += s;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function computeIndentLevel(content: string, offset: number, options: FormattingOptions): number {
|
||||
let i = 0;
|
||||
let nChars = 0;
|
||||
let tabSize = options.tabSize || 4;
|
||||
while (i < content.length) {
|
||||
let ch = content.charAt(i);
|
||||
if (ch === ' ') {
|
||||
nChars++;
|
||||
} else if (ch === '\t') {
|
||||
nChars += tabSize;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return Math.floor(nChars / tabSize);
|
||||
}
|
||||
|
||||
function getEOL(document: TextDocument): string {
|
||||
let text = document.getText();
|
||||
if (document.lineCount > 1) {
|
||||
let to = document.offsetAt(Position.create(1, 0));
|
||||
let from = to;
|
||||
while (from > 0 && isEOL(text, from - 1)) {
|
||||
from--;
|
||||
}
|
||||
return text.substr(from, to - from);
|
||||
}
|
||||
return '\n';
|
||||
}
|
||||
|
||||
function isEOL(text: string, offset: number) {
|
||||
return '\r\n'.indexOf(text.charAt(offset)) !== -1;
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
|
||||
import Parser = require('./jsonParser');
|
||||
import SchemaService = require('./jsonSchemaService');
|
||||
import {IJSONWorkerContribution} from './jsonContributions';
|
||||
|
||||
import {Hover, TextDocument, Position, Range, MarkedString} from 'vscode-languageserver';
|
||||
|
||||
export class JSONHover {
|
||||
|
||||
private schemaService: SchemaService.IJSONSchemaService;
|
||||
private contributions: IJSONWorkerContribution[];
|
||||
|
||||
constructor(schemaService: SchemaService.IJSONSchemaService, contributions: IJSONWorkerContribution[] = []) {
|
||||
this.schemaService = schemaService;
|
||||
this.contributions = contributions;
|
||||
}
|
||||
|
||||
public doHover(document: TextDocument, position: Position, doc: Parser.JSONDocument): Thenable<Hover> {
|
||||
|
||||
let offset = document.offsetAt(position);
|
||||
let node = doc.getNodeFromOffset(offset);
|
||||
|
||||
// use the property description when hovering over an object key
|
||||
if (node && node.type === 'string') {
|
||||
let stringNode = <Parser.StringASTNode>node;
|
||||
if (stringNode.isKey) {
|
||||
let propertyNode = <Parser.PropertyASTNode>node.parent;
|
||||
node = propertyNode.value;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
return Promise.resolve(void 0);
|
||||
}
|
||||
|
||||
var createHover = (contents: MarkedString[]) => {
|
||||
let range = Range.create(document.positionAt(node.start), document.positionAt(node.end));
|
||||
let result: Hover = {
|
||||
contents: contents,
|
||||
range: range
|
||||
};
|
||||
return result;
|
||||
};
|
||||
|
||||
let location = node.getNodeLocation();
|
||||
for (let i = this.contributions.length - 1; i >= 0; i--) {
|
||||
let contribution = this.contributions[i];
|
||||
let promise = contribution.getInfoContribution(document.uri, location);
|
||||
if (promise) {
|
||||
return promise.then(htmlContent => createHover(htmlContent));
|
||||
}
|
||||
}
|
||||
|
||||
return this.schemaService.getSchemaForResource(document.uri, doc).then((schema) => {
|
||||
if (schema) {
|
||||
let matchingSchemas: Parser.IApplicableSchema[] = [];
|
||||
doc.validate(schema.schema, matchingSchemas, node.start);
|
||||
|
||||
let description: string = null;
|
||||
matchingSchemas.every((s) => {
|
||||
if (s.node === node && !s.inverted && s.schema) {
|
||||
description = description || s.schema.description;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (description) {
|
||||
return createHover([description]);
|
||||
}
|
||||
}
|
||||
return void 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {TextDocument, Position, CompletionItem, CompletionList, Hover, Range, SymbolInformation, Diagnostic,
|
||||
TextEdit, FormattingOptions} from 'vscode-languageserver';
|
||||
|
||||
import {JSONCompletion} from './jsonCompletion';
|
||||
import {JSONHover} from './jsonHover';
|
||||
import {JSONValidation} from './jsonValidation';
|
||||
import {IJSONSchema} from './jsonSchema';
|
||||
import {JSONDocumentSymbols} from './jsonDocumentSymbols';
|
||||
import {parse as parseJSON, JSONDocument} from './jsonParser';
|
||||
import {schemaContributions} from './configuration';
|
||||
import {XHROptions, XHRResponse} from 'request-light';
|
||||
import {JSONSchemaService} from './jsonSchemaService';
|
||||
import {IJSONWorkerContribution} from './jsonContributions';
|
||||
import {format as formatJSON} from './jsonFormatter';
|
||||
|
||||
export interface LanguageService {
|
||||
configure(schemaConfiguration: JSONSchemaConfiguration[]): void;
|
||||
doValidation(document: TextDocument, jsonDocument: JSONDocument): Thenable<Diagnostic[]>;
|
||||
parseJSONDocument(document: TextDocument): JSONDocument;
|
||||
resetSchema(uri: string): boolean;
|
||||
doResolve(item: CompletionItem): Thenable<CompletionItem>;
|
||||
doComplete(document: TextDocument, position: Position, doc: JSONDocument): Thenable<CompletionList>;
|
||||
findDocumentSymbols(document: TextDocument, doc: JSONDocument): Promise<SymbolInformation[]>;
|
||||
doHover(document: TextDocument, position: Position, doc: JSONDocument): Thenable<Hover>;
|
||||
format(document: TextDocument, range: Range, options: FormattingOptions): Thenable<TextEdit[]>;
|
||||
}
|
||||
|
||||
export interface JSONSchemaConfiguration {
|
||||
uri: string;
|
||||
fileMatch?: string[];
|
||||
schema?: IJSONSchema;
|
||||
}
|
||||
|
||||
export interface TelemetryService {
|
||||
log(key: string, data: any): void;
|
||||
}
|
||||
|
||||
export interface WorkspaceContextService {
|
||||
resolveRelativePath(relativePath: string, resource: string): string;
|
||||
}
|
||||
|
||||
export interface RequestService {
|
||||
(options: XHROptions): Thenable<XHRResponse>;
|
||||
}
|
||||
|
||||
export function getLanguageService(contributions: IJSONWorkerContribution[], request: RequestService, workspaceContext: WorkspaceContextService, telemetry: TelemetryService): LanguageService {
|
||||
let jsonSchemaService = new JSONSchemaService(request, workspaceContext, telemetry);
|
||||
jsonSchemaService.setSchemaContributions(schemaContributions);
|
||||
|
||||
let jsonCompletion = new JSONCompletion(jsonSchemaService, contributions);
|
||||
let jsonHover = new JSONHover(jsonSchemaService, contributions);
|
||||
let jsonDocumentSymbols = new JSONDocumentSymbols();
|
||||
let jsonValidation = new JSONValidation(jsonSchemaService);
|
||||
|
||||
return {
|
||||
configure: (schemaConf: JSONSchemaConfiguration[]) => {
|
||||
schemaConf.forEach(settings => {
|
||||
jsonSchemaService.registerExternalSchema(settings.uri, settings.fileMatch, settings.schema);
|
||||
});
|
||||
},
|
||||
resetSchema: (uri: string) => {
|
||||
return jsonSchemaService.onResourceChange(uri);
|
||||
},
|
||||
doValidation: jsonValidation.doValidation.bind(jsonValidation),
|
||||
parseJSONDocument: (document: TextDocument) => parseJSON(document.getText()),
|
||||
doResolve: jsonCompletion.doResolve.bind(jsonCompletion),
|
||||
doComplete: jsonCompletion.doComplete.bind(jsonCompletion),
|
||||
findDocumentSymbols: jsonDocumentSymbols.findDocumentSymbols.bind(jsonDocumentSymbols),
|
||||
doHover: jsonHover.doHover.bind(jsonHover),
|
||||
format: (document, range, options) => Promise.resolve(formatJSON(document, range, options))
|
||||
};
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
export class JSONLocation {
|
||||
private segments: string[];
|
||||
|
||||
constructor(segments: string[]) {
|
||||
this.segments = segments;
|
||||
}
|
||||
|
||||
public append(segment: string): JSONLocation {
|
||||
return new JSONLocation(this.segments.concat(segment));
|
||||
}
|
||||
|
||||
public getSegments() {
|
||||
return this.segments;
|
||||
}
|
||||
|
||||
public matches(segments: string[]) {
|
||||
let k = 0;
|
||||
for (let i = 0; k < segments.length && i < this.segments.length; i++) {
|
||||
if (segments[k] === this.segments[i] || segments[k] === '*') {
|
||||
k++;
|
||||
} else if (segments[k] !== '**') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return k === segments.length;
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return '[' + this.segments.join('][') + ']';
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,49 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
export interface IJSONSchema {
|
||||
id?: string;
|
||||
$schema?: string;
|
||||
type?: string | string[];
|
||||
title?: string;
|
||||
default?: any;
|
||||
definitions?: IJSONSchemaMap;
|
||||
description?: string;
|
||||
properties?: IJSONSchemaMap;
|
||||
patternProperties?: IJSONSchemaMap;
|
||||
additionalProperties?: boolean | IJSONSchema;
|
||||
minProperties?: number;
|
||||
maxProperties?: number;
|
||||
dependencies?: IJSONSchemaMap | string[];
|
||||
items?: IJSONSchema | IJSONSchema[];
|
||||
minItems?: number;
|
||||
maxItems?: number;
|
||||
uniqueItems?: boolean;
|
||||
additionalItems?: boolean;
|
||||
pattern?: string;
|
||||
minLength?: number;
|
||||
maxLength?: number;
|
||||
minimum?: number;
|
||||
maximum?: number;
|
||||
exclusiveMinimum?: boolean;
|
||||
exclusiveMaximum?: boolean;
|
||||
multipleOf?: number;
|
||||
required?: string[];
|
||||
$ref?: string;
|
||||
anyOf?: IJSONSchema[];
|
||||
allOf?: IJSONSchema[];
|
||||
oneOf?: IJSONSchema[];
|
||||
not?: IJSONSchema;
|
||||
enum?: any[];
|
||||
format?: string;
|
||||
|
||||
defaultSnippets?: { label?: string; description?: string; body: any; }[]; // VSCode extension
|
||||
errorMessage?: string; // VSCode extension
|
||||
}
|
||||
|
||||
export interface IJSONSchemaMap {
|
||||
[name: string]:IJSONSchema;
|
||||
}
|
||||
@@ -1,513 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import Json = require('jsonc-parser');
|
||||
import {IJSONSchema, IJSONSchemaMap} from './jsonSchema';
|
||||
import {XHRResponse, getErrorStatusDescription} from 'request-light';
|
||||
import URI from './utils/uri';
|
||||
import Strings = require('./utils/strings');
|
||||
import Parser = require('./jsonParser');
|
||||
import {TelemetryService, RequestService, WorkspaceContextService} from './jsonLanguageService';
|
||||
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export interface IJSONSchemaService {
|
||||
|
||||
/**
|
||||
* Registers a schema file in the current workspace to be applicable to files that match the pattern
|
||||
*/
|
||||
registerExternalSchema(uri: string, filePatterns?: string[], unresolvedSchema?: IJSONSchema): ISchemaHandle;
|
||||
|
||||
/**
|
||||
* Clears all cached schema files
|
||||
*/
|
||||
clearExternalSchemas(): void;
|
||||
|
||||
/**
|
||||
* Registers contributed schemas
|
||||
*/
|
||||
setSchemaContributions(schemaContributions: ISchemaContributions): void;
|
||||
|
||||
/**
|
||||
* Looks up the appropriate schema for the given URI
|
||||
*/
|
||||
getSchemaForResource(resource: string, document: Parser.JSONDocument): Thenable<ResolvedSchema>;
|
||||
}
|
||||
|
||||
export interface ISchemaAssociations {
|
||||
[pattern: string]: string[];
|
||||
}
|
||||
|
||||
export interface ISchemaContributions {
|
||||
schemas?: { [id: string]: IJSONSchema };
|
||||
schemaAssociations?: ISchemaAssociations;
|
||||
}
|
||||
|
||||
export interface ISchemaHandle {
|
||||
/**
|
||||
* The schema id
|
||||
*/
|
||||
url: string;
|
||||
|
||||
/**
|
||||
* The schema from the file, with potential $ref references
|
||||
*/
|
||||
getUnresolvedSchema(): Thenable<UnresolvedSchema>;
|
||||
|
||||
/**
|
||||
* The schema from the file, with references resolved
|
||||
*/
|
||||
getResolvedSchema(): Thenable<ResolvedSchema>;
|
||||
}
|
||||
|
||||
|
||||
class FilePatternAssociation {
|
||||
|
||||
private schemas: string[];
|
||||
private combinedSchemaId: string;
|
||||
private patternRegExp: RegExp;
|
||||
private combinedSchema: ISchemaHandle;
|
||||
|
||||
constructor(pattern: string) {
|
||||
this.combinedSchemaId = 'local://combinedSchema/' + encodeURIComponent(pattern);
|
||||
try {
|
||||
this.patternRegExp = new RegExp(Strings.convertSimple2RegExpPattern(pattern) + '$');
|
||||
} catch (e) {
|
||||
// invalid pattern
|
||||
this.patternRegExp = null;
|
||||
}
|
||||
this.schemas = [];
|
||||
this.combinedSchema = null;
|
||||
}
|
||||
|
||||
public addSchema(id: string) {
|
||||
this.schemas.push(id);
|
||||
this.combinedSchema = null;
|
||||
}
|
||||
|
||||
public matchesPattern(fileName: string): boolean {
|
||||
return this.patternRegExp && this.patternRegExp.test(fileName);
|
||||
}
|
||||
|
||||
public getCombinedSchema(service: JSONSchemaService): ISchemaHandle {
|
||||
if (!this.combinedSchema) {
|
||||
this.combinedSchema = service.createCombinedSchema(this.combinedSchemaId, this.schemas);
|
||||
}
|
||||
return this.combinedSchema;
|
||||
}
|
||||
}
|
||||
|
||||
class SchemaHandle implements ISchemaHandle {
|
||||
|
||||
public url: string;
|
||||
|
||||
private resolvedSchema: Thenable<ResolvedSchema>;
|
||||
private unresolvedSchema: Thenable<UnresolvedSchema>;
|
||||
private service: JSONSchemaService;
|
||||
|
||||
constructor(service: JSONSchemaService, url: string, unresolvedSchemaContent?: IJSONSchema) {
|
||||
this.service = service;
|
||||
this.url = url;
|
||||
if (unresolvedSchemaContent) {
|
||||
this.unresolvedSchema = Promise.resolve(new UnresolvedSchema(unresolvedSchemaContent));
|
||||
}
|
||||
}
|
||||
|
||||
public getUnresolvedSchema(): Thenable<UnresolvedSchema> {
|
||||
if (!this.unresolvedSchema) {
|
||||
this.unresolvedSchema = this.service.loadSchema(this.url);
|
||||
}
|
||||
return this.unresolvedSchema;
|
||||
}
|
||||
|
||||
public getResolvedSchema(): Thenable<ResolvedSchema> {
|
||||
if (!this.resolvedSchema) {
|
||||
this.resolvedSchema = this.getUnresolvedSchema().then(unresolved => {
|
||||
return this.service.resolveSchemaContent(unresolved, this.url);
|
||||
});
|
||||
}
|
||||
return this.resolvedSchema;
|
||||
}
|
||||
|
||||
public clearSchema(): void {
|
||||
this.resolvedSchema = null;
|
||||
this.unresolvedSchema = null;
|
||||
}
|
||||
}
|
||||
|
||||
export class UnresolvedSchema {
|
||||
public schema: IJSONSchema;
|
||||
public errors: string[];
|
||||
|
||||
constructor(schema: IJSONSchema, errors: string[] = []) {
|
||||
this.schema = schema;
|
||||
this.errors = errors;
|
||||
}
|
||||
}
|
||||
|
||||
export class ResolvedSchema {
|
||||
public schema: IJSONSchema;
|
||||
public errors: string[];
|
||||
|
||||
constructor(schema: IJSONSchema, errors: string[] = []) {
|
||||
this.schema = schema;
|
||||
this.errors = errors;
|
||||
}
|
||||
|
||||
public getSection(path: string[]): IJSONSchema {
|
||||
return this.getSectionRecursive(path, this.schema);
|
||||
}
|
||||
|
||||
private getSectionRecursive(path: string[], schema: IJSONSchema): IJSONSchema {
|
||||
if (!schema || path.length === 0) {
|
||||
return schema;
|
||||
}
|
||||
let next = path.shift();
|
||||
|
||||
if (schema.properties && schema.properties[next]) {
|
||||
return this.getSectionRecursive(path, schema.properties[next]);
|
||||
} else if (schema.patternProperties) {
|
||||
Object.keys(schema.patternProperties).forEach((pattern) => {
|
||||
let regex = new RegExp(pattern);
|
||||
if (regex.test(next)) {
|
||||
return this.getSectionRecursive(path, schema.patternProperties[pattern]);
|
||||
}
|
||||
});
|
||||
} else if (schema.additionalProperties) {
|
||||
return this.getSectionRecursive(path, schema.additionalProperties);
|
||||
} else if (next.match('[0-9]+')) {
|
||||
if (schema.items) {
|
||||
return this.getSectionRecursive(path, schema.items);
|
||||
} else if (Array.isArray(schema.items)) {
|
||||
try {
|
||||
let index = parseInt(next, 10);
|
||||
if (schema.items[index]) {
|
||||
return this.getSectionRecursive(path, schema.items[index]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export class JSONSchemaService implements IJSONSchemaService {
|
||||
|
||||
private contributionSchemas: { [id: string]: SchemaHandle };
|
||||
private contributionAssociations: { [id: string]: string[] };
|
||||
|
||||
private schemasById: { [id: string]: SchemaHandle };
|
||||
private filePatternAssociations: FilePatternAssociation[];
|
||||
private filePatternAssociationById: { [id: string]: FilePatternAssociation };
|
||||
|
||||
private contextService: WorkspaceContextService;
|
||||
private callOnDispose: Function[];
|
||||
private telemetryService: TelemetryService;
|
||||
private requestService: RequestService;
|
||||
|
||||
constructor(requestService: RequestService, contextService?: WorkspaceContextService, telemetryService?: TelemetryService) {
|
||||
this.contextService = contextService;
|
||||
this.requestService = requestService;
|
||||
this.telemetryService = telemetryService;
|
||||
this.callOnDispose = [];
|
||||
|
||||
this.contributionSchemas = {};
|
||||
this.contributionAssociations = {};
|
||||
this.schemasById = {};
|
||||
this.filePatternAssociations = [];
|
||||
this.filePatternAssociationById = {};
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
while (this.callOnDispose.length > 0) {
|
||||
this.callOnDispose.pop()();
|
||||
}
|
||||
}
|
||||
|
||||
public onResourceChange(uri: string): boolean {
|
||||
let schemaFile = this.schemasById[uri];
|
||||
if (schemaFile) {
|
||||
schemaFile.clearSchema();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private normalizeId(id: string) {
|
||||
// remove trailing '#', normalize drive capitalization
|
||||
return URI.parse(id).toString();
|
||||
}
|
||||
|
||||
public setSchemaContributions(schemaContributions: ISchemaContributions): void {
|
||||
if (schemaContributions.schemas) {
|
||||
let schemas = schemaContributions.schemas;
|
||||
for (let id in schemas) {
|
||||
let normalizedId = this.normalizeId(id);
|
||||
this.contributionSchemas[normalizedId] = this.addSchemaHandle(normalizedId, schemas[id]);
|
||||
}
|
||||
}
|
||||
if (schemaContributions.schemaAssociations) {
|
||||
let schemaAssociations = schemaContributions.schemaAssociations;
|
||||
for (let pattern in schemaAssociations) {
|
||||
let associations = schemaAssociations[pattern];
|
||||
this.contributionAssociations[pattern] = associations;
|
||||
|
||||
var fpa = this.getOrAddFilePatternAssociation(pattern);
|
||||
associations.forEach(schemaId => {
|
||||
let id = this.normalizeId(schemaId);
|
||||
fpa.addSchema(id);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private addSchemaHandle(id: string, unresolvedSchemaContent?: IJSONSchema): SchemaHandle {
|
||||
let schemaHandle = new SchemaHandle(this, id, unresolvedSchemaContent);
|
||||
this.schemasById[id] = schemaHandle;
|
||||
return schemaHandle;
|
||||
}
|
||||
|
||||
private getOrAddSchemaHandle(id: string, unresolvedSchemaContent?: IJSONSchema): ISchemaHandle {
|
||||
return this.schemasById[id] || this.addSchemaHandle(id, unresolvedSchemaContent);
|
||||
}
|
||||
|
||||
private getOrAddFilePatternAssociation(pattern: string) {
|
||||
let fpa = this.filePatternAssociationById[pattern];
|
||||
if (!fpa) {
|
||||
fpa = new FilePatternAssociation(pattern);
|
||||
this.filePatternAssociationById[pattern] = fpa;
|
||||
this.filePatternAssociations.push(fpa);
|
||||
}
|
||||
return fpa;
|
||||
}
|
||||
|
||||
public registerExternalSchema(uri: string, filePatterns: string[] = null, unresolvedSchemaContent?: IJSONSchema): ISchemaHandle {
|
||||
let id = this.normalizeId(uri);
|
||||
|
||||
if (filePatterns) {
|
||||
filePatterns.forEach(pattern => {
|
||||
this.getOrAddFilePatternAssociation(pattern).addSchema(uri);
|
||||
});
|
||||
}
|
||||
return unresolvedSchemaContent ? this.addSchemaHandle(id, unresolvedSchemaContent) : this.getOrAddSchemaHandle(id);
|
||||
}
|
||||
|
||||
public clearExternalSchemas(): void {
|
||||
this.schemasById = {};
|
||||
this.filePatternAssociations = [];
|
||||
this.filePatternAssociationById = {};
|
||||
|
||||
for (let id in this.contributionSchemas) {
|
||||
this.schemasById[id] = this.contributionSchemas[id];
|
||||
}
|
||||
for (let pattern in this.contributionAssociations) {
|
||||
var fpa = this.getOrAddFilePatternAssociation(pattern);
|
||||
|
||||
this.contributionAssociations[pattern].forEach(schemaId => {
|
||||
let id = this.normalizeId(schemaId);
|
||||
fpa.addSchema(id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public getResolvedSchema(schemaId: string): Thenable<ResolvedSchema> {
|
||||
let id = this.normalizeId(schemaId);
|
||||
let schemaHandle = this.schemasById[id];
|
||||
if (schemaHandle) {
|
||||
return schemaHandle.getResolvedSchema();
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
public loadSchema(url: string): Thenable<UnresolvedSchema> {
|
||||
if (this.telemetryService && url.indexOf('//schema.management.azure.com/') !== -1) {
|
||||
this.telemetryService.log('json.schema', {
|
||||
schemaURL: url
|
||||
});
|
||||
}
|
||||
|
||||
return this.requestService({ url: url, followRedirects: 5 }).then(
|
||||
request => {
|
||||
let content = request.responseText;
|
||||
if (!content) {
|
||||
let errorMessage = localize('json.schema.nocontent', 'Unable to load schema from \'{0}\': No content.', toDisplayString(url));
|
||||
return new UnresolvedSchema(<IJSONSchema>{}, [errorMessage]);
|
||||
}
|
||||
|
||||
let schemaContent: IJSONSchema = {};
|
||||
let jsonErrors = [];
|
||||
schemaContent = Json.parse(content, jsonErrors);
|
||||
let errors = jsonErrors.length ? [localize('json.schema.invalidFormat', 'Unable to parse content from \'{0}\': {1}.', toDisplayString(url), jsonErrors[0])] : [];
|
||||
return new UnresolvedSchema(schemaContent, errors);
|
||||
},
|
||||
(error: XHRResponse) => {
|
||||
let errorMessage = localize('json.schema.unabletoload', 'Unable to load schema from \'{0}\': {1}', toDisplayString(url), error.responseText || getErrorStatusDescription(error.status) || error.toString());
|
||||
return new UnresolvedSchema(<IJSONSchema>{}, [errorMessage]);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public resolveSchemaContent(schemaToResolve: UnresolvedSchema, schemaURL: string): Thenable<ResolvedSchema> {
|
||||
|
||||
let resolveErrors: string[] = schemaToResolve.errors.slice(0);
|
||||
let schema = schemaToResolve.schema;
|
||||
let contextService = this.contextService;
|
||||
|
||||
let findSection = (schema: IJSONSchema, path: string): any => {
|
||||
if (!path) {
|
||||
return schema;
|
||||
}
|
||||
let current: any = schema;
|
||||
if (path[0] === '/') {
|
||||
path = path.substr(1);
|
||||
}
|
||||
path.split('/').some((part) => {
|
||||
current = current[part];
|
||||
return !current;
|
||||
});
|
||||
return current;
|
||||
};
|
||||
|
||||
let resolveLink = (node: any, linkedSchema: IJSONSchema, linkPath: string): void => {
|
||||
let section = findSection(linkedSchema, linkPath);
|
||||
if (section) {
|
||||
for (let key in section) {
|
||||
if (section.hasOwnProperty(key) && !node.hasOwnProperty(key)) {
|
||||
node[key] = section[key];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
resolveErrors.push(localize('json.schema.invalidref', '$ref \'{0}\' in {1} can not be resolved.', linkPath, linkedSchema.id));
|
||||
}
|
||||
delete node.$ref;
|
||||
};
|
||||
|
||||
let resolveExternalLink = (node: any, uri: string, linkPath: string, parentSchemaURL: string): Thenable<any> => {
|
||||
if (contextService && !/^\w+:\/\/.*/.test(uri)) {
|
||||
uri = contextService.resolveRelativePath(uri, parentSchemaURL);
|
||||
}
|
||||
return this.getOrAddSchemaHandle(uri).getUnresolvedSchema().then(unresolvedSchema => {
|
||||
if (unresolvedSchema.errors.length) {
|
||||
let loc = linkPath ? uri + '#' + linkPath : uri;
|
||||
resolveErrors.push(localize('json.schema.problemloadingref', 'Problems loading reference \'{0}\': {1}', loc, unresolvedSchema.errors[0]));
|
||||
}
|
||||
resolveLink(node, unresolvedSchema.schema, linkPath);
|
||||
return resolveRefs(node, unresolvedSchema.schema, uri);
|
||||
});
|
||||
};
|
||||
|
||||
let resolveRefs = (node: IJSONSchema, parentSchema: IJSONSchema, parentSchemaURL: string): Thenable<any> => {
|
||||
let toWalk : IJSONSchema[] = [node];
|
||||
let seen: IJSONSchema[] = [];
|
||||
|
||||
let openPromises: Thenable<any>[] = [];
|
||||
|
||||
let collectEntries = (...entries: IJSONSchema[]) => {
|
||||
for (let entry of entries) {
|
||||
if (typeof entry === 'object') {
|
||||
toWalk.push(entry);
|
||||
}
|
||||
}
|
||||
};
|
||||
let collectMapEntries = (...maps: IJSONSchemaMap[]) => {
|
||||
for (let map of maps) {
|
||||
if (typeof map === 'object') {
|
||||
for (let key in map) {
|
||||
let entry = map[key];
|
||||
toWalk.push(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let collectArrayEntries = (...arrays: IJSONSchema[][]) => {
|
||||
for (let array of arrays) {
|
||||
if (Array.isArray(array)) {
|
||||
toWalk.push.apply(toWalk, array);
|
||||
}
|
||||
}
|
||||
};
|
||||
while (toWalk.length) {
|
||||
let next = toWalk.pop();
|
||||
if (seen.indexOf(next) >= 0) {
|
||||
continue;
|
||||
}
|
||||
seen.push(next);
|
||||
if (next.$ref) {
|
||||
let segments = next.$ref.split('#', 2);
|
||||
if (segments[0].length > 0) {
|
||||
openPromises.push(resolveExternalLink(next, segments[0], segments[1], parentSchemaURL));
|
||||
continue;
|
||||
} else {
|
||||
resolveLink(next, parentSchema, segments[1]);
|
||||
}
|
||||
}
|
||||
collectEntries(next.items, next.additionalProperties, next.not);
|
||||
collectMapEntries(next.definitions, next.properties, next.patternProperties, <IJSONSchemaMap> next.dependencies);
|
||||
collectArrayEntries(next.anyOf, next.allOf, next.oneOf, <IJSONSchema[]> next.items);
|
||||
}
|
||||
return Promise.all(openPromises);
|
||||
};
|
||||
|
||||
return resolveRefs(schema, schema, schemaURL).then(_ => new ResolvedSchema(schema, resolveErrors));
|
||||
}
|
||||
|
||||
public getSchemaForResource(resource: string, document: Parser.JSONDocument): Thenable<ResolvedSchema> {
|
||||
|
||||
// first use $schema if present
|
||||
if (document && document.root && document.root.type === 'object') {
|
||||
let schemaProperties = (<Parser.ObjectASTNode>document.root).properties.filter((p) => (p.key.value === '$schema') && !!p.value);
|
||||
if (schemaProperties.length > 0) {
|
||||
let schemeId = <string>schemaProperties[0].value.getValue();
|
||||
if (Strings.startsWith(schemeId, '.') && this.contextService) {
|
||||
schemeId = this.contextService.resolveRelativePath(schemeId, resource);
|
||||
}
|
||||
if (schemeId) {
|
||||
let id = this.normalizeId(schemeId);
|
||||
return this.getOrAddSchemaHandle(id).getResolvedSchema();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// then check for matching file names, last to first
|
||||
for (let i = this.filePatternAssociations.length - 1; i >= 0; i--) {
|
||||
let entry = this.filePatternAssociations[i];
|
||||
if (entry.matchesPattern(resource)) {
|
||||
return entry.getCombinedSchema(this).getResolvedSchema();
|
||||
}
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
public createCombinedSchema(combinedSchemaId: string, schemaIds: string[]): ISchemaHandle {
|
||||
if (schemaIds.length === 1) {
|
||||
return this.getOrAddSchemaHandle(schemaIds[0]);
|
||||
} else {
|
||||
let combinedSchema: IJSONSchema = {
|
||||
allOf: schemaIds.map(schemaId => ({ $ref: schemaId }))
|
||||
};
|
||||
return this.addSchemaHandle(combinedSchemaId, combinedSchema);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toDisplayString(url: string) {
|
||||
try {
|
||||
let uri = URI.parse(url);
|
||||
if (uri.scheme === 'file') {
|
||||
return uri.fsPath;
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
return url;
|
||||
}
|
||||
@@ -9,24 +9,25 @@ import {
|
||||
TextDocuments, TextDocument, InitializeParams, InitializeResult, NotificationType, RequestType
|
||||
} from 'vscode-languageserver';
|
||||
|
||||
import {xhr, XHROptions, XHRResponse, configure as configureHttpRequests} from 'request-light';
|
||||
import {xhr, XHRResponse, configure as configureHttpRequests, getErrorStatusDescription} from 'request-light';
|
||||
import path = require('path');
|
||||
import fs = require('fs');
|
||||
import URI from './utils/uri';
|
||||
import * as URL from 'url';
|
||||
import Strings = require('./utils/strings');
|
||||
import {ISchemaAssociations} from './jsonSchemaService';
|
||||
import {JSONDocument} from './jsonParser';
|
||||
import {IJSONSchema} from './jsonSchema';
|
||||
import {JSONDocument, JSONSchema, LanguageSettings, getLanguageService} from 'vscode-json-languageservice';
|
||||
import {ProjectJSONContribution} from './jsoncontributions/projectJSONContribution';
|
||||
import {GlobPatternContribution} from './jsoncontributions/globPatternContribution';
|
||||
import {FileAssociationContribution} from './jsoncontributions/fileAssociationContribution';
|
||||
|
||||
import {JSONSchemaConfiguration, getLanguageService} from './jsonLanguageService';
|
||||
import {getLanguageModelCache} from './languageModelCache';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
nls.config(process.env['VSCODE_NLS_CONFIG']);
|
||||
|
||||
interface ISchemaAssociations {
|
||||
[pattern: string]: string[];
|
||||
}
|
||||
|
||||
namespace SchemaAssociationNotification {
|
||||
export const type: NotificationType<ISchemaAssociations> = { get method() { return 'json/schemaAssociations'; } };
|
||||
}
|
||||
@@ -76,47 +77,51 @@ let workspaceContext = {
|
||||
}
|
||||
};
|
||||
|
||||
let telemetry = {
|
||||
log: (key: string, data: any) => {
|
||||
connection.telemetry.logEvent({ key, data });
|
||||
}
|
||||
};
|
||||
|
||||
let request = (options: XHROptions): Thenable<XHRResponse> => {
|
||||
if (Strings.startsWith(options.url, 'file://')) {
|
||||
let fsPath = URI.parse(options.url).fsPath;
|
||||
return new Promise<XHRResponse>((c, e) => {
|
||||
let schemaRequestService = (uri:string): Thenable<string> => {
|
||||
if (Strings.startsWith(uri, 'file://')) {
|
||||
let fsPath = URI.parse(uri).fsPath;
|
||||
return new Promise<string>((c, e) => {
|
||||
fs.readFile(fsPath, 'UTF-8', (err, result) => {
|
||||
err ? e({ responseText: '', status: 404 }) : c({ responseText: result.toString(), status: 200 });
|
||||
err ? e('') : c(result.toString());
|
||||
});
|
||||
});
|
||||
} else if (Strings.startsWith(options.url, 'vscode://')) {
|
||||
return connection.sendRequest(VSCodeContentRequest.type, options.url).then(responseText => {
|
||||
return {
|
||||
responseText: responseText,
|
||||
status: 200
|
||||
};
|
||||
} else if (Strings.startsWith(uri, 'vscode://')) {
|
||||
return connection.sendRequest(VSCodeContentRequest.type, uri).then(responseText => {
|
||||
return responseText;
|
||||
}, error => {
|
||||
return {
|
||||
responseText: error.message,
|
||||
status: 404
|
||||
};
|
||||
return error.message;
|
||||
});
|
||||
}
|
||||
return xhr(options);
|
||||
if (uri.indexOf('//schema.management.azure.com/') !== -1) {
|
||||
connection.telemetry.logEvent({
|
||||
key: 'json.schema',
|
||||
value: {
|
||||
schemaURL: uri
|
||||
}
|
||||
});
|
||||
}
|
||||
return xhr({ url: uri, followRedirects: 5 }).then(response => {
|
||||
return response.responseText;
|
||||
}, (error: XHRResponse) => {
|
||||
return error.responseText || getErrorStatusDescription(error.status) || error.toString();
|
||||
});
|
||||
};
|
||||
|
||||
let contributions = [
|
||||
new ProjectJSONContribution(request),
|
||||
new GlobPatternContribution(),
|
||||
filesAssociationContribution
|
||||
];
|
||||
let languageService = getLanguageService(contributions, request, workspaceContext, telemetry);
|
||||
let languageService = getLanguageService({
|
||||
schemaRequestService,
|
||||
workspaceContext,
|
||||
contributions: [
|
||||
new ProjectJSONContribution(),
|
||||
new GlobPatternContribution(),
|
||||
filesAssociationContribution
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
// The content of a text document has changed. This event is emitted
|
||||
// when the text document first opened or when its content has changed.
|
||||
documents.onDidChangeContent((change) => {
|
||||
validateTextDocument(change.document);
|
||||
triggerValidation(change.document);
|
||||
});
|
||||
|
||||
// The settings interface describe the server relevant settings part
|
||||
@@ -133,7 +138,7 @@ interface Settings {
|
||||
interface JSONSchemaSettings {
|
||||
fileMatch?: string[];
|
||||
url?: string;
|
||||
schema?: IJSONSchema;
|
||||
schema?: JSONSchema;
|
||||
}
|
||||
|
||||
let jsonConfigurationSettings: JSONSchemaSettings[] = void 0;
|
||||
@@ -149,19 +154,23 @@ connection.onDidChangeConfiguration((change) => {
|
||||
});
|
||||
|
||||
// The jsonValidation extension configuration has changed
|
||||
connection.onNotification(SchemaAssociationNotification.type, (associations) => {
|
||||
connection.onNotification(SchemaAssociationNotification.type, associations => {
|
||||
schemaAssociations = associations;
|
||||
updateConfiguration();
|
||||
});
|
||||
|
||||
function updateConfiguration() {
|
||||
let schemaConfigs: JSONSchemaConfiguration[] = [];
|
||||
let languageSettings : LanguageSettings = {
|
||||
validate: true,
|
||||
allowComments: true,
|
||||
schemas: []
|
||||
};
|
||||
if (schemaAssociations) {
|
||||
for (var pattern in schemaAssociations) {
|
||||
let association = schemaAssociations[pattern];
|
||||
if (Array.isArray(association)) {
|
||||
association.forEach(uri => {
|
||||
schemaConfigs.push({ uri, fileMatch: [pattern] });
|
||||
languageSettings.schemas.push({ uri, fileMatch: [pattern] });
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -181,17 +190,37 @@ function updateConfiguration() {
|
||||
uri = URI.file(path.normalize(path.join(workspaceRoot.fsPath, uri))).toString();
|
||||
}
|
||||
if (uri) {
|
||||
schemaConfigs.push({ uri, fileMatch: schema.fileMatch, schema: schema.schema });
|
||||
languageSettings.schemas.push({ uri, fileMatch: schema.fileMatch, schema: schema.schema });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
languageService.configure(schemaConfigs);
|
||||
languageService.configure(languageSettings);
|
||||
|
||||
// Revalidate any open text documents
|
||||
documents.all().forEach(validateTextDocument);
|
||||
documents.all().forEach(triggerValidation);
|
||||
}
|
||||
|
||||
let pendingValidationRequests : {[uri:string]:number} = {};
|
||||
const validationDelayMs = 200;
|
||||
documents.onDidClose(e => {
|
||||
let request = pendingValidationRequests[e.document.uri];
|
||||
if (request) {
|
||||
clearTimeout(request);
|
||||
delete pendingValidationRequests[e.document.uri];
|
||||
}
|
||||
});
|
||||
|
||||
function triggerValidation(textDocument: TextDocument): void {
|
||||
let request = pendingValidationRequests[textDocument.uri];
|
||||
if (request) {
|
||||
clearTimeout(request);
|
||||
}
|
||||
pendingValidationRequests[textDocument.uri] = setTimeout(() => {
|
||||
delete pendingValidationRequests[textDocument.uri];
|
||||
validateTextDocument(textDocument);
|
||||
}, validationDelayMs);
|
||||
}
|
||||
|
||||
function validateTextDocument(textDocument: TextDocument): void {
|
||||
if (textDocument.getText().length === 0) {
|
||||
@@ -220,8 +249,10 @@ connection.onDidChangeWatchedFiles((change) => {
|
||||
}
|
||||
});
|
||||
|
||||
let jsonDocuments = getLanguageModelCache<JSONDocument>(10, 60, document => languageService.parseJSONDocument(document));
|
||||
|
||||
function getJSONDocument(document: TextDocument): JSONDocument {
|
||||
return languageService.parseJSONDocument(document);
|
||||
return jsonDocuments.get(document);
|
||||
}
|
||||
|
||||
connection.onCompletion(textDocumentPosition => {
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {JSONSchemaService} from './jsonSchemaService';
|
||||
import {JSONDocument, ObjectASTNode} from './jsonParser';
|
||||
import {TextDocument, Diagnostic, DiagnosticSeverity} from 'vscode-languageserver';
|
||||
|
||||
export class JSONValidation {
|
||||
|
||||
private jsonSchemaService: JSONSchemaService;
|
||||
|
||||
public constructor(jsonSchemaService: JSONSchemaService) {
|
||||
this.jsonSchemaService = jsonSchemaService;
|
||||
}
|
||||
|
||||
public doValidation(textDocument: TextDocument, jsonDocument: JSONDocument) {
|
||||
return this.jsonSchemaService.getSchemaForResource(textDocument.uri, jsonDocument).then(schema => {
|
||||
if (schema) {
|
||||
if (schema.errors.length && jsonDocument.root) {
|
||||
let astRoot = jsonDocument.root;
|
||||
let property = astRoot.type === 'object' ? (<ObjectASTNode>astRoot).getFirstProperty('$schema') : null;
|
||||
if (property) {
|
||||
let node = property.value || property;
|
||||
jsonDocument.warnings.push({ location: { start: node.start, end: node.end }, message: schema.errors[0] });
|
||||
} else {
|
||||
jsonDocument.warnings.push({ location: { start: astRoot.start, end: astRoot.start + 1 }, message: schema.errors[0] });
|
||||
}
|
||||
} else {
|
||||
jsonDocument.validate(schema.schema);
|
||||
}
|
||||
}
|
||||
|
||||
let diagnostics: Diagnostic[] = [];
|
||||
let added: { [signature: string]: boolean } = {};
|
||||
jsonDocument.errors.concat(jsonDocument.warnings).forEach((error, idx) => {
|
||||
// remove duplicated messages
|
||||
let signature = error.location.start + ' ' + error.location.end + ' ' + error.message;
|
||||
if (!added[signature]) {
|
||||
added[signature] = true;
|
||||
let range = {
|
||||
start: textDocument.positionAt(error.location.start),
|
||||
end: textDocument.positionAt(error.location.end)
|
||||
};
|
||||
diagnostics.push({
|
||||
severity: idx >= jsonDocument.errors.length ? DiagnosticSeverity.Warning : DiagnosticSeverity.Error,
|
||||
range: range,
|
||||
message: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
return diagnostics;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,7 @@
|
||||
|
||||
import {MarkedString, CompletionItemKind, CompletionItem} from 'vscode-languageserver';
|
||||
import Strings = require('../utils/strings');
|
||||
import {IJSONWorkerContribution, ISuggestionsCollector} from '../jsonContributions';
|
||||
import {JSONLocation} from '../jsonLocation';
|
||||
import {JSONWorkerContribution, JSONPath, CompletionsCollector} from 'vscode-json-languageservice';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
@@ -17,7 +16,7 @@ let globProperties: CompletionItem[] = [
|
||||
{ kind: CompletionItemKind.Value, label: localize('assocLabelPath', "Files with Path"), insertText: '"/{{path to file}}/*.{{extension}}": "{{language}}"', documentation: localize('assocDescriptionPath', "Map all files matching the absolute path glob pattern in their path to the language with the given identifier.") }
|
||||
];
|
||||
|
||||
export class FileAssociationContribution implements IJSONWorkerContribution {
|
||||
export class FileAssociationContribution implements JSONWorkerContribution {
|
||||
private languageIds:string[];
|
||||
|
||||
constructor() {
|
||||
@@ -31,20 +30,20 @@ export class FileAssociationContribution implements IJSONWorkerContribution {
|
||||
return Strings.endsWith(resource, '/settings.json');
|
||||
}
|
||||
|
||||
public collectDefaultSuggestions(resource: string, result: ISuggestionsCollector): Thenable<any> {
|
||||
public collectDefaultCompletions(resource: string, result: CompletionsCollector): Thenable<any> {
|
||||
return null;
|
||||
}
|
||||
|
||||
public collectPropertySuggestions(resource: string, location: JSONLocation, currentWord: string, addValue: boolean, isLast: boolean, result: ISuggestionsCollector): Thenable<any> {
|
||||
if (this.isSettingsFile(resource) && location.matches(['files.associations'])) {
|
||||
public collectPropertyCompletions(resource: string, location: JSONPath, currentWord: string, addValue: boolean, isLast: boolean, result: CompletionsCollector): Thenable<any> {
|
||||
if (this.isSettingsFile(resource) && location.length === 1 && location[0] === 'files.associations') {
|
||||
globProperties.forEach((e) => result.add(e));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public collectValueSuggestions(resource: string, location: JSONLocation, currentKey: string, result: ISuggestionsCollector): Thenable<any> {
|
||||
if (this.isSettingsFile(resource) && location.matches(['files.associations'])) {
|
||||
public collectValueCompletions(resource: string, location: JSONPath, currentKey: string, result: CompletionsCollector): Thenable<any> {
|
||||
if (this.isSettingsFile(resource) && location.length === 1 && location[0] === 'files.associations') {
|
||||
this.languageIds.forEach(l => {
|
||||
result.add({
|
||||
kind: CompletionItemKind.Value,
|
||||
@@ -57,7 +56,7 @@ export class FileAssociationContribution implements IJSONWorkerContribution {
|
||||
return null;
|
||||
}
|
||||
|
||||
public getInfoContribution(resource: string, location: JSONLocation): Thenable<MarkedString[]> {
|
||||
public getInfoContribution(resource: string, location: JSONPath): Thenable<MarkedString[]> {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,7 @@
|
||||
|
||||
import {MarkedString, CompletionItemKind, CompletionItem} from 'vscode-languageserver';
|
||||
import Strings = require('../utils/strings');
|
||||
import {IJSONWorkerContribution, ISuggestionsCollector} from '../jsonContributions';
|
||||
import {JSONLocation} from '../jsonLocation';
|
||||
import {JSONWorkerContribution, JSONPath, CompletionsCollector} from 'vscode-json-languageservice';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
@@ -27,7 +26,7 @@ let globValues: CompletionItem[] = [
|
||||
{ kind: CompletionItemKind.Value, label: localize('derivedLabel', "Files with Siblings by Name"), insertText: '{ "when": "$(basename).{{extension}}" }', documentation: localize('siblingsDescription', "Match files that have siblings with the same name but a different extension.") }
|
||||
];
|
||||
|
||||
export class GlobPatternContribution implements IJSONWorkerContribution {
|
||||
export class GlobPatternContribution implements JSONWorkerContribution {
|
||||
|
||||
constructor() {
|
||||
}
|
||||
@@ -36,27 +35,27 @@ export class GlobPatternContribution implements IJSONWorkerContribution {
|
||||
return Strings.endsWith(resource, '/settings.json');
|
||||
}
|
||||
|
||||
public collectDefaultSuggestions(resource: string, result: ISuggestionsCollector): Thenable<any> {
|
||||
public collectDefaultCompletions(resource: string, result: CompletionsCollector): Thenable<any> {
|
||||
return null;
|
||||
}
|
||||
|
||||
public collectPropertySuggestions(resource: string, location: JSONLocation, currentWord: string, addValue: boolean, isLast: boolean, result: ISuggestionsCollector): Thenable<any> {
|
||||
if (this.isSettingsFile(resource) && (location.matches(['files.exclude']) || location.matches(['search.exclude']))) {
|
||||
public collectPropertyCompletions(resource: string, location: JSONPath, currentWord: string, addValue: boolean, isLast: boolean, result: CompletionsCollector): Thenable<any> {
|
||||
if (this.isSettingsFile(resource) && location.length === 1 && ((location[0] === 'files.exclude') || (location[0] === 'search.exclude'))) {
|
||||
globProperties.forEach((e) => result.add(e));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public collectValueSuggestions(resource: string, location: JSONLocation, currentKey: string, result: ISuggestionsCollector): Thenable<any> {
|
||||
if (this.isSettingsFile(resource) && (location.matches(['files.exclude']) || location.matches(['search.exclude']))) {
|
||||
public collectValueCompletions(resource: string, location: JSONPath, currentKey: string, result: CompletionsCollector): Thenable<any> {
|
||||
if (this.isSettingsFile(resource) && location.length === 1 && ((location[0] === 'files.exclude') || (location[0] === 'search.exclude'))) {
|
||||
globValues.forEach((e) => result.add(e));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public getInfoContribution(resource: string, location: JSONLocation): Thenable<MarkedString[]> {
|
||||
public getInfoContribution(resource: string, location: JSONPath): Thenable<MarkedString[]> {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,8 @@
|
||||
import {MarkedString, CompletionItemKind, CompletionItem} from 'vscode-languageserver';
|
||||
import Strings = require('../utils/strings');
|
||||
import {XHRResponse, getErrorStatusDescription} from 'request-light';
|
||||
import {IJSONWorkerContribution, ISuggestionsCollector} from '../jsonContributions';
|
||||
import {RequestService} from '../jsonLanguageService';
|
||||
import {JSONLocation} from '../jsonLocation';
|
||||
import {JSONWorkerContribution, JSONPath, CompletionsCollector} from 'vscode-json-languageservice';
|
||||
import {xhr} from 'request-light';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
@@ -28,15 +27,13 @@ interface NugetServices {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export class ProjectJSONContribution implements IJSONWorkerContribution {
|
||||
export class ProjectJSONContribution implements JSONWorkerContribution {
|
||||
|
||||
private requestService : RequestService;
|
||||
private cachedProjects: { [id: string]: { version: string, description: string, time: number }} = {};
|
||||
private cacheSize: number = 0;
|
||||
private nugetIndexPromise: Thenable<NugetServices>;
|
||||
|
||||
public constructor(requestService: RequestService) {
|
||||
this.requestService = requestService;
|
||||
public constructor() {
|
||||
}
|
||||
|
||||
private isProjectJSONFile(resource: string): boolean {
|
||||
@@ -104,7 +101,7 @@ export class ProjectJSONContribution implements IJSONWorkerContribution {
|
||||
});
|
||||
}
|
||||
|
||||
public collectDefaultSuggestions(resource: string, result: ISuggestionsCollector): Thenable<any> {
|
||||
public collectDefaultCompletions(resource: string, result: CompletionsCollector): Thenable<any> {
|
||||
if (this.isProjectJSONFile(resource)) {
|
||||
let defaultValue = {
|
||||
'version': '{{1.0.0-*}}',
|
||||
@@ -120,7 +117,7 @@ export class ProjectJSONContribution implements IJSONWorkerContribution {
|
||||
}
|
||||
|
||||
private makeJSONRequest<T>(url: string) : Thenable<T> {
|
||||
return this.requestService({
|
||||
return xhr({
|
||||
url : url
|
||||
}).then(success => {
|
||||
if (success.status === 200) {
|
||||
@@ -136,8 +133,8 @@ export class ProjectJSONContribution implements IJSONWorkerContribution {
|
||||
});
|
||||
}
|
||||
|
||||
public collectPropertySuggestions(resource: string, location: JSONLocation, currentWord: string, addValue: boolean, isLast:boolean, result: ISuggestionsCollector) : Thenable<any> {
|
||||
if (this.isProjectJSONFile(resource) && (location.matches(['dependencies']) || location.matches(['frameworks', '*', 'dependencies']) || location.matches(['frameworks', '*', 'frameworkAssemblies']))) {
|
||||
public collectPropertyCompletions(resource: string, location: JSONPath, currentWord: string, addValue: boolean, isLast:boolean, result: CompletionsCollector) : Thenable<any> {
|
||||
if (this.isProjectJSONFile(resource) && (matches(location, ['dependencies']) || matches(location, ['frameworks', '*', 'dependencies']) || matches(location, ['frameworks', '*', 'frameworkAssemblies']))) {
|
||||
|
||||
return this.getNugetService('SearchAutocompleteService').then(service => {
|
||||
let queryUrl : string;
|
||||
@@ -178,8 +175,8 @@ export class ProjectJSONContribution implements IJSONWorkerContribution {
|
||||
return null;
|
||||
}
|
||||
|
||||
public collectValueSuggestions(resource: string, location: JSONLocation, currentKey: string, result: ISuggestionsCollector): Thenable<any> {
|
||||
if (this.isProjectJSONFile(resource) && (location.matches(['dependencies']) || location.matches(['frameworks', '*', 'dependencies']) || location.matches(['frameworks', '*', 'frameworkAssemblies']))) {
|
||||
public collectValueCompletions(resource: string, location: JSONPath, currentKey: string, result: CompletionsCollector): Thenable<any> {
|
||||
if (this.isProjectJSONFile(resource) && (matches(location, ['dependencies']) || matches(location, ['frameworks', '*', 'dependencies']) || matches(location, ['frameworks', '*', 'frameworkAssemblies']))) {
|
||||
return this.getNugetService('PackageBaseAddress/3.0.0').then(service => {
|
||||
let queryUrl = service + currentKey + '/index.json';
|
||||
return this.makeJSONRequest<any>(queryUrl).then(obj => {
|
||||
@@ -206,9 +203,9 @@ export class ProjectJSONContribution implements IJSONWorkerContribution {
|
||||
return null;
|
||||
}
|
||||
|
||||
public getInfoContribution(resource: string, location: JSONLocation): Thenable<MarkedString[]> {
|
||||
if (this.isProjectJSONFile(resource) && (location.matches(['dependencies', '*']) || location.matches(['frameworks', '*', 'dependencies', '*']) || location.matches(['frameworks', '*', 'frameworkAssemblies', '*']))) {
|
||||
let pack = location.getSegments()[location.getSegments().length - 1];
|
||||
public getInfoContribution(resource: string, location: JSONPath): Thenable<MarkedString[]> {
|
||||
if (this.isProjectJSONFile(resource) && (matches(location, ['dependencies', '*']) || matches(location, ['frameworks', '*', 'dependencies', '*']) || matches(location, ['frameworks', '*', 'frameworkAssemblies', '*']))) {
|
||||
let pack = <string> location[location.length - 1];
|
||||
|
||||
return this.getNugetService('SearchQueryService').then(service => {
|
||||
let queryUrl = service + '?q=' + encodeURIComponent(pack) +'&take=' + 5;
|
||||
@@ -269,4 +266,16 @@ export class ProjectJSONContribution implements IJSONWorkerContribution {
|
||||
};
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function matches(segments: JSONPath, pattern: string[]) {
|
||||
let k = 0;
|
||||
for (let i = 0; k < pattern.length && i < segments.length; i++) {
|
||||
if (pattern[k] === segments[i] || pattern[k] === '*') {
|
||||
k++;
|
||||
} else if (pattern[k] !== '**') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return k === pattern.length;
|
||||
}
|
||||
83
extensions/json/server/src/languageModelCache.ts
Normal file
83
extensions/json/server/src/languageModelCache.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {TextDocument} from 'vscode-languageserver';
|
||||
|
||||
export interface LanguageModelCache<T> {
|
||||
get(document: TextDocument): T;
|
||||
onDocumentRemoved(document: TextDocument): void;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export function getLanguageModelCache<T>(maxEntries: number, cleanupIntervalTimeInSec: number, parse: (document: TextDocument) => T) : LanguageModelCache<T> {
|
||||
let languageModels: { [uri:string]: {version:number, languageId: string, cTime: number, languageModel: T}} = {};
|
||||
let nModels = 0;
|
||||
|
||||
let cleanupInterval = void 0;
|
||||
if (cleanupIntervalTimeInSec > 0) {
|
||||
cleanupInterval = setInterval(() => {
|
||||
let cutoffTime = Date.now() - cleanupIntervalTimeInSec * 1000;
|
||||
let uris = Object.keys(languageModels);
|
||||
for (let uri of uris) {
|
||||
let languageModelInfo = languageModels[uri];
|
||||
if (languageModelInfo.cTime < cutoffTime) {
|
||||
delete languageModels[uri];
|
||||
nModels--;
|
||||
}
|
||||
}
|
||||
}, cleanupIntervalTimeInSec * 1000);
|
||||
}
|
||||
|
||||
return {
|
||||
get(document: TextDocument) : T {
|
||||
let version = document.version;
|
||||
let languageId = document.languageId;
|
||||
let languageModelInfo = languageModels[document.uri];
|
||||
if (languageModelInfo && languageModelInfo.version === version && languageModelInfo.languageId === languageId) {
|
||||
languageModelInfo.cTime = Date.now();
|
||||
return languageModelInfo.languageModel;
|
||||
}
|
||||
let languageModel = parse(document);
|
||||
languageModels[document.uri] = { languageModel, version, languageId, cTime: Date.now()};
|
||||
if (!languageModelInfo) {
|
||||
nModels++;
|
||||
}
|
||||
|
||||
if (nModels === maxEntries) {
|
||||
let oldestTime = Number.MAX_VALUE;
|
||||
let oldestUri = null;
|
||||
for (let uri in languageModels) {
|
||||
let languageModelInfo = languageModels[uri];
|
||||
if (languageModelInfo.cTime < oldestTime) {
|
||||
oldestUri = uri;
|
||||
oldestTime = languageModelInfo.cTime;
|
||||
}
|
||||
}
|
||||
if (oldestUri) {
|
||||
delete languageModels[oldestUri];
|
||||
nModels--;
|
||||
}
|
||||
}
|
||||
return languageModel;
|
||||
|
||||
},
|
||||
onDocumentRemoved(document: TextDocument) {
|
||||
let uri = document.uri;
|
||||
if (languageModels[uri]) {
|
||||
delete languageModels[uri];
|
||||
nModels--;
|
||||
}
|
||||
},
|
||||
dispose() {
|
||||
if (typeof cleanupInterval !== 'undefined') {
|
||||
clearInterval(cleanupInterval);
|
||||
cleanupInterval = void 0;
|
||||
languageModels = {};
|
||||
nModels = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,519 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import assert = require('assert');
|
||||
import Parser = require('../jsonParser');
|
||||
import SchemaService = require('../jsonSchemaService');
|
||||
import JsonSchema = require('../jsonSchema');
|
||||
import {JSONCompletion} from '../jsonCompletion';
|
||||
import {XHROptions, XHRResponse} from 'request-light';
|
||||
|
||||
import {CompletionItem, CompletionItemKind, CompletionOptions, TextDocument, TextDocumentIdentifier, Range, Position, TextEdit} from 'vscode-languageserver';
|
||||
import {applyEdits} from './textEditSupport';
|
||||
|
||||
suite('JSON Completion', () => {
|
||||
|
||||
let requestService = function(options: XHROptions): Promise<XHRResponse> {
|
||||
return Promise.reject<XHRResponse>({ responseText: '', status: 404 });
|
||||
}
|
||||
|
||||
let assertCompletion = function(completions: CompletionItem[], label: string, documentation?: string, document?: TextDocument, resultText?: string) {
|
||||
let matches = completions.filter(function(completion: CompletionItem) {
|
||||
return completion.label === label && (!documentation || completion.documentation === documentation);
|
||||
});
|
||||
assert.equal(matches.length, 1, label + " should only existing once: Actual: " + completions.map(c => c.label).join(', '));
|
||||
if (document && resultText) {
|
||||
assert.equal(applyEdits(document, [ matches[0].textEdit ]), resultText);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
let testCompletionsFor = function(value: string, stringAfter: string, schema: JsonSchema.IJSONSchema, test: (items: CompletionItem[], document: TextDocument) => void) : Thenable<void> {
|
||||
let uri = 'test://test.json';
|
||||
let idx = stringAfter ? value.indexOf(stringAfter) : 0;
|
||||
|
||||
let schemaService = new SchemaService.JSONSchemaService(requestService);
|
||||
let completionProvider = new JSONCompletion(schemaService);
|
||||
if (schema) {
|
||||
let id = "http://myschemastore/test1";
|
||||
schemaService.registerExternalSchema(id, ["*.json"], schema);
|
||||
}
|
||||
|
||||
let document = TextDocument.create(uri, 'json', 0, value);
|
||||
let position = Position.create(0, idx);
|
||||
let jsonDoc = Parser.parse(value);
|
||||
return completionProvider.doComplete(document, position, jsonDoc).then(list => list.items).then(completions => {
|
||||
test(completions, document);
|
||||
return null;
|
||||
})
|
||||
};
|
||||
|
||||
test('Complete keys no schema', function(testDone) {
|
||||
Promise.all([
|
||||
testCompletionsFor('[ { "name": "John", "age": 44 }, { /**/ }', '/**/', null, result => {
|
||||
assert.strictEqual(result.length, 2);
|
||||
assertCompletion(result, 'name');
|
||||
assertCompletion(result, 'age');
|
||||
}),
|
||||
testCompletionsFor('[ { "name": "John", "age": 44 }, { "/**/ }', '/**/', null, result => {
|
||||
assert.strictEqual(result.length, 2);
|
||||
assertCompletion(result, 'name');
|
||||
assertCompletion(result, 'age');
|
||||
}),
|
||||
testCompletionsFor('[ { "name": "John", "age": 44 }, { "n/**/ }', '/**/', null, result => {
|
||||
assert.strictEqual(result.length, 2);
|
||||
assertCompletion(result, 'name');
|
||||
}),
|
||||
testCompletionsFor('[ { "name": "John", "age": 44 }, { "name/**/" }', '/**/', null, result => {
|
||||
assert.strictEqual(result.length, 2);
|
||||
assertCompletion(result, 'name');
|
||||
}),
|
||||
testCompletionsFor('[ { "name": "John", "address": { "street" : "MH Road", "number" : 5 } }, { "name": "Jack", "address": { "street" : "100 Feet Road", /**/ }', '/**/', null, result => {
|
||||
assert.strictEqual(result.length, 1);
|
||||
assertCompletion(result, 'number');
|
||||
})
|
||||
]).then(() => testDone(), (error) => testDone(error));
|
||||
});
|
||||
|
||||
test('Complete values no schema', function(testDone) {
|
||||
Promise.all([
|
||||
testCompletionsFor('[ { "name": "John", "age": 44 }, { "name": /**/', '/**/', null, result => {
|
||||
assert.strictEqual(result.length, 1);
|
||||
assertCompletion(result, '"John"');
|
||||
}),
|
||||
testCompletionsFor('[ { "data": { "key": 1, "data": true } }, { "data": /**/', '/**/', null, result => {
|
||||
assert.strictEqual(result.length, 3);
|
||||
assertCompletion(result, '{}');
|
||||
assertCompletion(result, 'true');
|
||||
assertCompletion(result, 'false');
|
||||
}),
|
||||
testCompletionsFor('[ { "data": "foo" }, { "data": "bar" }, { "data": "/**/" } ]', '/**/', null, result => {
|
||||
assert.strictEqual(result.length, 2);
|
||||
assertCompletion(result, '"foo"');
|
||||
assertCompletion(result, '"bar"');
|
||||
}),
|
||||
testCompletionsFor('[ { "data": "foo" }, { "data": "bar" }, { "data": "f/**/" } ]', '/**/', null, result => {
|
||||
assert.strictEqual(result.length, 2);
|
||||
assertCompletion(result, '"foo"');
|
||||
assertCompletion(result, '"bar"');
|
||||
}),
|
||||
testCompletionsFor('[ { "data": "foo" }, { "data": "bar" }, { "data": "xoo"/**/ } ]', '/**/', null, result => {
|
||||
assert.strictEqual(result.length, 3);
|
||||
assertCompletion(result, '"xoo"');
|
||||
}),
|
||||
testCompletionsFor('[ { "data": "foo" }, { "data": "bar" }, { "data": "xoo" /**/ } ]', '/**/', null, result => {
|
||||
assert.strictEqual(result.length, 0);
|
||||
})
|
||||
]).then(() => testDone(), (error) => testDone(error));
|
||||
});
|
||||
|
||||
test('Complete keys with schema', function(testDone) {
|
||||
let schema: JsonSchema.IJSONSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
'a': {
|
||||
type: 'number',
|
||||
description: 'A'
|
||||
},
|
||||
'b': {
|
||||
type: 'string',
|
||||
description: 'B'
|
||||
},
|
||||
'c': {
|
||||
type: 'boolean',
|
||||
description: 'C'
|
||||
}
|
||||
}
|
||||
};
|
||||
Promise.all([
|
||||
testCompletionsFor('{/**/}', '/**/', schema, result => {
|
||||
assert.strictEqual(result.length, 3);
|
||||
assertCompletion(result, 'a', 'A');
|
||||
assertCompletion(result, 'b', 'B');
|
||||
assertCompletion(result, 'c', 'C');
|
||||
}),
|
||||
testCompletionsFor('{ "/**/}', '/**/', schema, result => {
|
||||
assert.strictEqual(result.length, 3);
|
||||
assertCompletion(result, 'a', 'A');
|
||||
assertCompletion(result, 'b', 'B');
|
||||
assertCompletion(result, 'c', 'C');
|
||||
}),
|
||||
testCompletionsFor('{ "a/**/}', '/**/', schema, (result, document) => {
|
||||
assert.strictEqual(result.length, 3);
|
||||
assertCompletion(result, 'a', 'A', document, '{ "a": {{0}}');
|
||||
}),
|
||||
testCompletionsFor('{ a/**/}', '/**/', schema, (result, document) => {
|
||||
assert.strictEqual(result.length, 3);
|
||||
assertCompletion(result, 'a', 'A', document, '{ "a": {{0}}/**/}');
|
||||
}),
|
||||
testCompletionsFor('{ "a" = 1;/**/}', '/**/', schema, result => {
|
||||
assert.strictEqual(result.length, 2);
|
||||
assertCompletion(result, 'b', 'B');
|
||||
assertCompletion(result, 'c', 'C');
|
||||
})
|
||||
]).then(() => testDone(), (error) => testDone(error));
|
||||
|
||||
});
|
||||
|
||||
test('Complete value with schema', function(testDone) {
|
||||
|
||||
let schema: JsonSchema.IJSONSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
'a': {
|
||||
enum: ['John', 'Jeff', 'George']
|
||||
}
|
||||
}
|
||||
};
|
||||
Promise.all([
|
||||
testCompletionsFor('{ "a": /**/ }', '/**/', schema, result => {
|
||||
assert.strictEqual(result.length, 3);
|
||||
assertCompletion(result, '"John"');
|
||||
assertCompletion(result, '"Jeff"');
|
||||
assertCompletion(result, '"George"');
|
||||
}),
|
||||
|
||||
testCompletionsFor('{ "a": "J/**/ }', '/**/', schema, result => {
|
||||
assert.strictEqual(result.length, 3);
|
||||
assertCompletion(result, '"John"');
|
||||
assertCompletion(result, '"Jeff"');
|
||||
}),
|
||||
|
||||
testCompletionsFor('{ "a": "John"/**/ }', '/**/', schema, result => {
|
||||
assert.strictEqual(result.length, 3);
|
||||
assertCompletion(result, '"John"');
|
||||
})
|
||||
]).then(() => testDone(), (error) => testDone(error));
|
||||
});
|
||||
|
||||
test('Complete value with schema: booleans, null', function(testDone) {
|
||||
|
||||
let schema: JsonSchema.IJSONSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
'a': {
|
||||
type: 'boolean'
|
||||
},
|
||||
'b': {
|
||||
type: ['boolean', 'null']
|
||||
},
|
||||
}
|
||||
};
|
||||
Promise.all([
|
||||
testCompletionsFor('{ "a": /**/ }', '/**/', schema, result => {
|
||||
assert.strictEqual(result.length, 2);
|
||||
assertCompletion(result, 'true');
|
||||
assertCompletion(result, 'false');
|
||||
}),
|
||||
testCompletionsFor('{ "b": "/**/ }', '/**/', schema, result => {
|
||||
assert.strictEqual(result.length, 3);
|
||||
assertCompletion(result, 'true');
|
||||
assertCompletion(result, 'false');
|
||||
assertCompletion(result, 'null');
|
||||
})
|
||||
]).then(() => testDone(), (error) => testDone(error));
|
||||
});
|
||||
|
||||
test('Complete with nested schema', function(testDone) {
|
||||
|
||||
let content = '{/**/}';
|
||||
let schema: JsonSchema.IJSONSchema = {
|
||||
oneOf: [{
|
||||
type: 'object',
|
||||
properties: {
|
||||
'a': {
|
||||
type: 'number',
|
||||
description: 'A'
|
||||
},
|
||||
'b': {
|
||||
type: 'string',
|
||||
description: 'B'
|
||||
},
|
||||
}
|
||||
}, {
|
||||
type: 'array'
|
||||
}]
|
||||
};
|
||||
Promise.all([
|
||||
testCompletionsFor(content, '/**/', schema, result => {
|
||||
assert.strictEqual(result.length, 2);
|
||||
assertCompletion(result, 'a', 'A');
|
||||
assertCompletion(result, 'b', 'B');
|
||||
})
|
||||
]).then(() => testDone(), (error) => testDone(error));
|
||||
});
|
||||
|
||||
test('Complete with required anyOf', function(testDone) {
|
||||
|
||||
let schema: JsonSchema.IJSONSchema = {
|
||||
anyOf: [{
|
||||
type: 'object',
|
||||
required: ['a', 'b'],
|
||||
properties: {
|
||||
'a': {
|
||||
type: 'string',
|
||||
description: 'A'
|
||||
},
|
||||
'b': {
|
||||
type: 'string',
|
||||
description: 'B'
|
||||
},
|
||||
}
|
||||
}, {
|
||||
type: 'object',
|
||||
required: ['c', 'd'],
|
||||
properties: {
|
||||
'c': {
|
||||
type: 'string',
|
||||
description: 'C'
|
||||
},
|
||||
'd': {
|
||||
type: 'string',
|
||||
description: 'D'
|
||||
},
|
||||
}
|
||||
}]
|
||||
};
|
||||
Promise.all([
|
||||
testCompletionsFor('{/**/}', '/**/', schema, result => {
|
||||
assert.strictEqual(result.length, 4);
|
||||
assertCompletion(result, 'a', 'A');
|
||||
assertCompletion(result, 'b', 'B');
|
||||
assertCompletion(result, 'c', 'C');
|
||||
assertCompletion(result, 'd', 'D');
|
||||
}),
|
||||
testCompletionsFor('{ "a": "", /**/}', '/**/', schema, result => {
|
||||
assert.strictEqual(result.length, 1);
|
||||
assertCompletion(result, 'b', 'B');
|
||||
})
|
||||
]).then(() => testDone(), (error) => testDone(error));
|
||||
});
|
||||
|
||||
test('Complete with anyOf', function(testDone) {
|
||||
|
||||
let schema: JsonSchema.IJSONSchema = {
|
||||
anyOf: [{
|
||||
type: 'object',
|
||||
properties: {
|
||||
'type': {
|
||||
enum: ['house']
|
||||
},
|
||||
'b': {
|
||||
type: 'string'
|
||||
},
|
||||
}
|
||||
}, {
|
||||
type: 'object',
|
||||
properties: {
|
||||
'type': {
|
||||
enum: ['appartment']
|
||||
},
|
||||
'c': {
|
||||
type: 'string'
|
||||
},
|
||||
}
|
||||
}]
|
||||
};
|
||||
Promise.all([
|
||||
testCompletionsFor('{/**/}', '/**/', schema, result => {
|
||||
assert.strictEqual(result.length, 3);
|
||||
assertCompletion(result, 'type');
|
||||
assertCompletion(result, 'b');
|
||||
assertCompletion(result, 'c');
|
||||
}),
|
||||
testCompletionsFor('{ "type": "appartment", /**/}', '/**/', schema, result => {
|
||||
assert.strictEqual(result.length, 1);
|
||||
assertCompletion(result, 'c');
|
||||
})
|
||||
]).then(() => testDone(), (error) => testDone(error));
|
||||
});
|
||||
|
||||
test('Complete with oneOf', function(testDone) {
|
||||
|
||||
let schema: JsonSchema.IJSONSchema = {
|
||||
oneOf: [{
|
||||
type: 'object',
|
||||
allOf: [{
|
||||
properties: {
|
||||
'a': {
|
||||
type: 'string',
|
||||
description: 'A'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
anyOf: [{
|
||||
properties: {
|
||||
'b1': {
|
||||
type: 'string',
|
||||
description: 'B1'
|
||||
}
|
||||
},
|
||||
}, {
|
||||
properties: {
|
||||
'b2': {
|
||||
type: 'string',
|
||||
description: 'B2'
|
||||
}
|
||||
},
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
type: 'object',
|
||||
properties: {
|
||||
'c': {
|
||||
type: 'string',
|
||||
description: 'C'
|
||||
},
|
||||
'd': {
|
||||
type: 'string',
|
||||
description: 'D'
|
||||
},
|
||||
}
|
||||
}]
|
||||
};
|
||||
Promise.all([
|
||||
testCompletionsFor('{/**/}', '/**/', schema, result => {
|
||||
assert.strictEqual(result.length, 5);
|
||||
assertCompletion(result, 'a', 'A');
|
||||
assertCompletion(result, 'b1', 'B1');
|
||||
assertCompletion(result, 'b2', 'B2');
|
||||
assertCompletion(result, 'c', 'C');
|
||||
assertCompletion(result, 'd', 'D');
|
||||
}),
|
||||
testCompletionsFor('{ "b1": "", /**/}', '/**/', schema, result => {
|
||||
assert.strictEqual(result.length, 2);
|
||||
assertCompletion(result, 'a', 'A');
|
||||
assertCompletion(result, 'b2', 'B2');
|
||||
})
|
||||
]).then(() => testDone(), (error) => testDone(error));
|
||||
});
|
||||
|
||||
test('Complete with oneOf and enums', function(testDone) {
|
||||
|
||||
let schema: JsonSchema.IJSONSchema = {
|
||||
oneOf: [{
|
||||
type: 'object',
|
||||
properties: {
|
||||
'type': {
|
||||
type: 'string',
|
||||
enum: ['1', '2']
|
||||
},
|
||||
'a': {
|
||||
type: 'object',
|
||||
properties: {
|
||||
'x': {
|
||||
type: 'string'
|
||||
},
|
||||
'y': {
|
||||
type: 'string'
|
||||
}
|
||||
},
|
||||
"required": ['x', 'y']
|
||||
},
|
||||
'b': {}
|
||||
},
|
||||
}, {
|
||||
type: 'object',
|
||||
properties: {
|
||||
'type': {
|
||||
type: 'string',
|
||||
enum: ['3']
|
||||
},
|
||||
'a': {
|
||||
type: 'object',
|
||||
properties: {
|
||||
'x': {
|
||||
type: 'string'
|
||||
},
|
||||
'z': {
|
||||
type: 'string'
|
||||
}
|
||||
},
|
||||
"required": ['x', 'z']
|
||||
},
|
||||
'c': {}
|
||||
},
|
||||
}]
|
||||
};
|
||||
Promise.all([
|
||||
testCompletionsFor('{/**/}', '/**/', schema, result => {
|
||||
assert.strictEqual(result.length, 4);
|
||||
assertCompletion(result, 'type');
|
||||
assertCompletion(result, 'a');
|
||||
assertCompletion(result, 'b');
|
||||
assertCompletion(result, 'c');
|
||||
}),
|
||||
testCompletionsFor('{ "type": /**/}', '/**/', schema, result => {
|
||||
assert.strictEqual(result.length, 3);
|
||||
assertCompletion(result, '"1"');
|
||||
assertCompletion(result, '"2"');
|
||||
assertCompletion(result, '"3"');
|
||||
}),
|
||||
testCompletionsFor('{ "a": { "x": "", "y": "" }, "type": /**/}', '/**/', schema, result => {
|
||||
assert.strictEqual(result.length, 2);
|
||||
assertCompletion(result, '"1"');
|
||||
assertCompletion(result, '"2"');
|
||||
}),
|
||||
testCompletionsFor('{ "type": "1", "a" : { /**/ }', '/**/', schema, result => {
|
||||
assert.strictEqual(result.length, 2);
|
||||
assertCompletion(result, 'x');
|
||||
assertCompletion(result, 'y');
|
||||
}),
|
||||
testCompletionsFor('{ "type": "1", "a" : { "x": "", "z":"" }, /**/', '/**/', schema, result => {
|
||||
// both alternatives have errors: intellisense proposes all options
|
||||
assert.strictEqual(result.length, 2);
|
||||
assertCompletion(result, 'b');
|
||||
assertCompletion(result, 'c');
|
||||
}),
|
||||
testCompletionsFor('{ "a" : { "x": "", "z":"" }, /**/', '/**/', schema, result => {
|
||||
assert.strictEqual(result.length, 2);
|
||||
assertCompletion(result, 'type');
|
||||
assertCompletion(result, 'c');
|
||||
}),
|
||||
]).then(() => testDone(), (error) => testDone(error));
|
||||
});
|
||||
|
||||
test('Escaping no schema', function(testDone) {
|
||||
Promise.all([
|
||||
testCompletionsFor('[ { "\\\\{{}}": "John" }, { "/**/" }', '/**/', null, result => {
|
||||
assertCompletion(result, '\\{{}}');
|
||||
}),
|
||||
testCompletionsFor('[ { "\\\\{{}}": "John" }, { /**/ }', '/**/', null, (result, document) => {
|
||||
assertCompletion(result, '\\{{}}', null, document, '[ { "\\\\{{}}": "John" }, { "\\\\\\\\\\{\\{\\}\\}"/**/ }');
|
||||
}),
|
||||
testCompletionsFor('[ { "name": "\\{" }, { "name": /**/ }', '/**/', null, result => {
|
||||
assertCompletion(result, '"\\{"');
|
||||
})
|
||||
]).then(() => testDone(), (error) => testDone(error));
|
||||
});
|
||||
|
||||
test('Escaping with schema', function(testDone) {
|
||||
let schema: JsonSchema.IJSONSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
'{\\}': {
|
||||
default: "{\\}",
|
||||
defaultSnippets: [ { body: "{{let}}"} ],
|
||||
enum: ['John{\\}']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Promise.all([
|
||||
testCompletionsFor('{ /**/ }', '/**/', schema, (result, document) => {
|
||||
assertCompletion(result, '{\\}', null, document, '{ "\\{\\\\\\\\\\}": "{{\\{\\\\\\\\\\}}}"/**/ }');
|
||||
}),
|
||||
testCompletionsFor('{ "{\\\\}": /**/ }', '/**/', schema, (result, document) => {
|
||||
assertCompletion(result, '"{\\\\}"', null, document, '{ "{\\\\}": "\\{\\\\\\\\\\}"/**/ }');
|
||||
assertCompletion(result, '"John{\\\\}"', null, document, '{ "{\\\\}": "John\\{\\\\\\\\\\}"/**/ }');
|
||||
assertCompletion(result, '"let"', null, document, '{ "{\\\\}": "{{let}}"/**/ }');
|
||||
})
|
||||
]).then(() => testDone(), (error) => testDone(error));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import assert = require('assert');
|
||||
import Parser = require('../jsonParser');
|
||||
import SchemaService = require('../jsonSchemaService');
|
||||
import JsonSchema = require('../jsonSchema');
|
||||
import {JSONCompletion} from '../jsonCompletion';
|
||||
import {JSONDocumentSymbols} from '../jsonDocumentSymbols';
|
||||
|
||||
import {SymbolInformation, SymbolKind, TextDocumentIdentifier, TextDocument, Range, Position, TextEdit} from 'vscode-languageserver';
|
||||
|
||||
suite('JSON Document Symbols', () => {
|
||||
|
||||
function getOutline(value: string): Promise<SymbolInformation[]> {
|
||||
var uri = 'test://test.json';
|
||||
|
||||
var symbolProvider = new JSONDocumentSymbols();
|
||||
|
||||
var document = TextDocument.create(uri, 'json', 0, value);
|
||||
var jsonDoc = Parser.parse(value);
|
||||
return symbolProvider.findDocumentSymbols(document, jsonDoc);
|
||||
}
|
||||
|
||||
var assertOutline: any = function(actual: SymbolInformation[], expected: any[], message: string) {
|
||||
assert.equal(actual.length, expected.length, message);
|
||||
for (var i = 0; i < expected.length; i++) {
|
||||
assert.equal(actual[i].name, expected[i].label, message);
|
||||
assert.equal(actual[i].kind, expected[i].kind, message);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
test('Base types', function(testDone) {
|
||||
var content = '{ "key1": 1, "key2": "foo", "key3" : true }';
|
||||
|
||||
var expected = [
|
||||
{ label: 'key1', kind: SymbolKind.Number },
|
||||
{ label: 'key2', kind: SymbolKind.String },
|
||||
{ label: 'key3', kind: SymbolKind.Boolean },
|
||||
];
|
||||
|
||||
getOutline(content).then((entries: SymbolInformation[]) => {
|
||||
assertOutline(entries, expected);
|
||||
}).then(() => testDone(), (error) => testDone(error));
|
||||
});
|
||||
|
||||
test('Arrays', function(testDone) {
|
||||
var content = '{ "key1": 1, "key2": [ 1, 2, 3 ], "key3" : [ { "k1": 1 }, {"k2": 2 } ] }';
|
||||
|
||||
var expected = [
|
||||
{ label: 'key1', kind: SymbolKind.Number },
|
||||
{ label: 'key2', kind: SymbolKind.Array },
|
||||
{ label: 'key3', kind: SymbolKind.Array },
|
||||
{ label: 'k1', kind: SymbolKind.Number },
|
||||
{ label: 'k2', kind: SymbolKind.Number }
|
||||
];
|
||||
|
||||
getOutline(content).then((entries: SymbolInformation[]) => {
|
||||
assertOutline(entries, expected);
|
||||
}).then(() => testDone(), (error) => testDone(error));
|
||||
});
|
||||
|
||||
test('Objects', function(testDone) {
|
||||
var content = '{ "key1": { "key2": true }, "key3" : { "k1": { } }';
|
||||
|
||||
var expected = [
|
||||
{ label: 'key1', kind: SymbolKind.Module },
|
||||
{ label: 'key2', kind: SymbolKind.Boolean },
|
||||
{ label: 'key3', kind: SymbolKind.Module },
|
||||
{ label: 'k1', kind: SymbolKind.Module }
|
||||
];
|
||||
|
||||
getOutline(content).then((entries: SymbolInformation[]) => {
|
||||
assertOutline(entries, expected);
|
||||
}).then(() => testDone(), (error) => testDone(error));
|
||||
});
|
||||
|
||||
test('Outline - object with syntax error', function(testDone) {
|
||||
var content = '{ "key1": { "key2": true, "key3":, "key4": false } }';
|
||||
|
||||
var expected = [
|
||||
{ label: 'key1', kind: SymbolKind.Module },
|
||||
{ label: 'key2', kind: SymbolKind.Boolean },
|
||||
{ label: 'key4', kind: SymbolKind.Boolean },
|
||||
];
|
||||
|
||||
getOutline(content).then((entries: SymbolInformation[]) => {
|
||||
assertOutline(entries, expected);
|
||||
}).then(() => testDone(), (error) => testDone(error));
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,62 +0,0 @@
|
||||
{
|
||||
"id": "http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Authorization.json",
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "Microsoft.Authorization",
|
||||
"description": "Microsoft Microsoft.Authorization Resource Types",
|
||||
"definitions": {
|
||||
"locks": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": [
|
||||
"Microsoft.Authorization/locks"
|
||||
]
|
||||
},
|
||||
"apiVersion": {
|
||||
"enum": [
|
||||
"2015-01-01"
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"maxLength": 64,
|
||||
"description": "Name of the lock"
|
||||
},
|
||||
"dependsOn": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Collection of resources this resource depends on"
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"level": {
|
||||
"enum": [
|
||||
"CannotDelete",
|
||||
"ReadOnly"
|
||||
],
|
||||
"description": "Microsoft.Authorization/locks: level - specifies the type of lock to apply to the scope. CanNotDelete allows modification but prevents deletion, ReadOnly prevents modification or deletion."
|
||||
},
|
||||
"notes": {
|
||||
"type": "string",
|
||||
"maxLength": 512,
|
||||
"description": "Microsoft.Authorization/locks: notes - user defined notes for the lock"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"level"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"type",
|
||||
"apiVersion",
|
||||
"properties"
|
||||
],
|
||||
"description": "Microsoft.Authorization/locks resource"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,835 +0,0 @@
|
||||
{
|
||||
"id": "http://schema.management.azure.com/schemas/2015-08-01/Microsoft.Compute.json#",
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "Microsoft.Compute",
|
||||
"description": "Microsoft Compute Resource Types",
|
||||
"resourceDefinitions": {
|
||||
"availabilitySets": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": [
|
||||
"Microsoft.Compute/availabilitySets"
|
||||
]
|
||||
},
|
||||
"apiVersion": {
|
||||
"enum": [
|
||||
"2015-05-01-preview",
|
||||
"2015-06-15"
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"platformUpdateDomainCount": {
|
||||
"type": "number"
|
||||
},
|
||||
"platformFaultDomainCount": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"apiVersion",
|
||||
"properties"
|
||||
]
|
||||
},
|
||||
"virtualMachines": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": [
|
||||
"Microsoft.Compute/virtualMachines"
|
||||
]
|
||||
},
|
||||
"apiVersion": {
|
||||
"enum": [
|
||||
"2015-05-01-preview",
|
||||
"2015-06-15"
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"properties": {
|
||||
"availabilitySet": {
|
||||
"$ref": "#/definitions/id"
|
||||
},
|
||||
"hardwareProfile": {
|
||||
"$ref": "#/definitions/hardwareProfile"
|
||||
},
|
||||
"storageProfile": {
|
||||
"$ref": "#/definitions/storageProfile"
|
||||
},
|
||||
"osProfile": {
|
||||
"$ref": "#/definitions/osProfile"
|
||||
},
|
||||
"networkProfile": {
|
||||
"$ref": "#/definitions/networkProfile"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"hardwareProfile",
|
||||
"storageProfile",
|
||||
"networkProfile"
|
||||
]
|
||||
},
|
||||
"resources": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/resourceBase"
|
||||
},
|
||||
{
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Authorization.json#/definitions/locks"
|
||||
},
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Resources.json#/definitions/links"
|
||||
},
|
||||
{
|
||||
"$ref": "#/resourceDefinitions/extensionsChild"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "Microsoft.Compute/virtualMachines: Resource Definition for Virtual Machines."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"apiVersion",
|
||||
"properties"
|
||||
]
|
||||
},
|
||||
"virtualMachineScaleSets": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": [
|
||||
"Microsoft.Compute/virtualMachineScaleSets"
|
||||
]
|
||||
},
|
||||
"apiVersion": {
|
||||
"enum": [
|
||||
"2015-05-01-preview",
|
||||
"2015-06-15"
|
||||
]
|
||||
},
|
||||
"sku": {
|
||||
"$ref": "#/definitions/sku"
|
||||
},
|
||||
"properties": {
|
||||
"properties": {
|
||||
"upgradePolicy": {
|
||||
"$ref": "#/definitions/upgradePolicy"
|
||||
},
|
||||
"virtualMachineProfile": {
|
||||
"$ref": "#/definitions/virtualMachineProfile"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"upgradePolicy",
|
||||
"virtualMachineProfile"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"sku",
|
||||
"properties"
|
||||
]
|
||||
},
|
||||
"extensions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": [
|
||||
"Microsoft.Compute/virtualMachines/extensions"
|
||||
]
|
||||
},
|
||||
"apiVersion": {
|
||||
"enum": [
|
||||
"2015-05-01-preview",
|
||||
"2015-06-15"
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"properties": {
|
||||
"publisher": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"typeHandlerVersion": {
|
||||
"type": "string"
|
||||
},
|
||||
"settings": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"publisher",
|
||||
"type",
|
||||
"typeHandlerVersion",
|
||||
"settings"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"apiVersion",
|
||||
"properties"
|
||||
]
|
||||
},
|
||||
"extensionsChild": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": [
|
||||
"extensions"
|
||||
]
|
||||
},
|
||||
"apiVersion": {
|
||||
"enum": [
|
||||
"2015-05-01-preview",
|
||||
"2015-06-15"
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"properties": {
|
||||
"publisher": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"typeHandlerVersion": {
|
||||
"type": "string"
|
||||
},
|
||||
"settings": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"publisher",
|
||||
"type",
|
||||
"typeHandlerVersion",
|
||||
"settings"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"apiVersion",
|
||||
"properties"
|
||||
]
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"id": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"networkInterfaces": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"properties": {
|
||||
"properties": {
|
||||
"primary": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"primary"
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"hardwareProfile": {
|
||||
"properties": {
|
||||
"vmSize": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"vmSize"
|
||||
]
|
||||
},
|
||||
"imageReference": {
|
||||
"properties": {
|
||||
"publisher": {
|
||||
"type": "string"
|
||||
},
|
||||
"offer": {
|
||||
"type": "string"
|
||||
},
|
||||
"sku": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"default": "latest"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"publisher",
|
||||
"offer",
|
||||
"sku",
|
||||
"version"
|
||||
]
|
||||
},
|
||||
"vhd": {
|
||||
"properties": {
|
||||
"uri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"uri"
|
||||
]
|
||||
},
|
||||
"osDisk": {
|
||||
"properties": {
|
||||
"osType": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"vhd": {
|
||||
"$ref": "#/definitions/vhd"
|
||||
},
|
||||
"image": {
|
||||
"$ref": "#/definitions/vhd"
|
||||
},
|
||||
"caching": {
|
||||
"type": "string"
|
||||
},
|
||||
"createOption": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"vhd",
|
||||
"createOption"
|
||||
]
|
||||
},
|
||||
"vhdUri": {
|
||||
"properties": {
|
||||
"uri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"uri"
|
||||
]
|
||||
},
|
||||
"dataDisk": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"diskSizeGB": {
|
||||
"type": "string"
|
||||
},
|
||||
"lun": {
|
||||
"type": "number"
|
||||
},
|
||||
"vhd": {
|
||||
"$ref": "#/definitions/vhdUri"
|
||||
},
|
||||
"caching": {
|
||||
"type": "string"
|
||||
},
|
||||
"createOption": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"lun",
|
||||
"vhd",
|
||||
"createOption"
|
||||
]
|
||||
},
|
||||
"storageProfile": {
|
||||
"properties": {
|
||||
"imageReference": {
|
||||
"$ref": "#/definitions/imageReference"
|
||||
},
|
||||
"osDisk": {
|
||||
"$ref": "#/definitions/osDisk"
|
||||
},
|
||||
"dataDisks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/dataDisk"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"osDisk"
|
||||
]
|
||||
},
|
||||
"winRMListener": {
|
||||
"properties": {
|
||||
"protocol": {
|
||||
"oneOf": [
|
||||
{
|
||||
"enum": [
|
||||
"http",
|
||||
"https"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
|
||||
}
|
||||
]
|
||||
},
|
||||
"certificateUrl": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"protocol",
|
||||
"certificateUrl"
|
||||
]
|
||||
},
|
||||
"winRM": {
|
||||
"properties": {
|
||||
"listeners": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/winRMListener"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"listeners"
|
||||
]
|
||||
},
|
||||
"additionalUnattendContent": {
|
||||
"properties": {
|
||||
"pass": {
|
||||
"type": "string"
|
||||
},
|
||||
"component": {
|
||||
"type": "string"
|
||||
},
|
||||
"settingName": {
|
||||
"type": "string"
|
||||
},
|
||||
"content": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"pass",
|
||||
"component",
|
||||
"settingName",
|
||||
"content"
|
||||
]
|
||||
},
|
||||
"windowsConfiguration": {
|
||||
"properties": {
|
||||
"provisionVMAgent": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"winRM": {
|
||||
"$ref": "#/definitions/winRM"
|
||||
},
|
||||
"additionalUnattendContent": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/additionalUnattendContent"
|
||||
}
|
||||
},
|
||||
"enableAutomaticUpdates": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"publicKey": {
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"keyData": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"ssh": {
|
||||
"properties": {
|
||||
"publicKeys": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/publicKey"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"linuxConfiguration": {
|
||||
"properties": {
|
||||
"disablePasswordAuthentication": {
|
||||
"type": "string"
|
||||
},
|
||||
"ssh": {
|
||||
"$ref": "#/definitions/ssh"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"vaultCertificateUrl": {
|
||||
"properties": {
|
||||
"certificateUrl": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"certificateUrl"
|
||||
]
|
||||
},
|
||||
"secret": {
|
||||
"properties": {
|
||||
"sourceVault": {
|
||||
"$ref": "#/definitions/id"
|
||||
},
|
||||
"vaultCertificates": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/vaultCertificateUrl"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"sourceVault",
|
||||
"vaultCertificates"
|
||||
]
|
||||
},
|
||||
"osProfile": {
|
||||
"properties": {
|
||||
"computerName": {
|
||||
"type": "string"
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string"
|
||||
},
|
||||
"adminPassword": {
|
||||
"type": "string"
|
||||
},
|
||||
"customData": {
|
||||
"type": "string"
|
||||
},
|
||||
"windowsConfiguration": {
|
||||
"$ref": "#/definitions/windowsConfiguration"
|
||||
},
|
||||
"linuxConfiguration": {
|
||||
"$ref": "#/definitions/linuxConfiguration"
|
||||
},
|
||||
"secrets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/secret"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"computerName",
|
||||
"adminUsername",
|
||||
"adminPassword"
|
||||
]
|
||||
},
|
||||
"networkProfile": {
|
||||
"properties": {
|
||||
"networkInterfaces": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/networkInterfaces"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"networkInterfaces"
|
||||
]
|
||||
},
|
||||
"sku": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"tier": {
|
||||
"type": "string"
|
||||
},
|
||||
"capacity": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"capacity"
|
||||
]
|
||||
},
|
||||
"upgradePolicy": {
|
||||
"properties": {
|
||||
"mode": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"mode"
|
||||
]
|
||||
},
|
||||
"virtualMachineScaleSetOsProfile": {
|
||||
"properties": {
|
||||
"computerNamePrefix": {
|
||||
"type": "string"
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string"
|
||||
},
|
||||
"adminPassword": {
|
||||
"type": "string"
|
||||
},
|
||||
"customData": {
|
||||
"type": "string"
|
||||
},
|
||||
"windowsConfiguration": {
|
||||
"$ref": "#/definitions/windowsConfiguration"
|
||||
},
|
||||
"linuxConfiguration": {
|
||||
"$ref": "#/definitions/linuxConfiguration"
|
||||
},
|
||||
"secrets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/secret"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"computerNamePrefix",
|
||||
"adminUsername",
|
||||
"adminPassword"
|
||||
]
|
||||
},
|
||||
"virtualMachineScaleSetOSDisk": {
|
||||
"properties": {
|
||||
"osType": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"vhdContainers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"caching": {
|
||||
"type": "string"
|
||||
},
|
||||
"createOption": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"createOption"
|
||||
]
|
||||
},
|
||||
"virtualMachineScaleSetStorageProfile": {
|
||||
"properties": {
|
||||
"imageReference": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/imageReference"
|
||||
},
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
|
||||
}
|
||||
]
|
||||
},
|
||||
"osDisk": {
|
||||
"$ref": "#/definitions/virtualMachineScaleSetOSDisk"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"osDisk"
|
||||
]
|
||||
},
|
||||
"virtualMachineScaleSetExtension": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"properties": {
|
||||
"properties": {
|
||||
"publisher": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"typeHandlerVersion": {
|
||||
"type": "string"
|
||||
},
|
||||
"settings": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"publisher",
|
||||
"type",
|
||||
"typeHandlerVersion"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"virtualMachineScaleSetExtensionProfile": {
|
||||
"properties": {
|
||||
"extensions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/virtualMachineScaleSetExtension"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"ipConfiguration": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"subnet": {
|
||||
"$ref": "#/definitions/id"
|
||||
},
|
||||
"loadBalancerBackendAddressPools": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/id"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"properties"
|
||||
]
|
||||
},
|
||||
"networkInterfaceConfiguration": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"primary": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"ipConfigurations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ipConfiguration"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"primary",
|
||||
"ipConfigurations"
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"properties"
|
||||
]
|
||||
},
|
||||
"virtualMachineScaleSetNetworkProfile": {
|
||||
"properties": {
|
||||
"networkInterfaceConfigurations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/networkInterfaceConfiguration"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"networkInterfaceConfigurations"
|
||||
]
|
||||
},
|
||||
"virtualMachineProfile": {
|
||||
"properties": {
|
||||
"osProfile": {
|
||||
"$ref": "#/definitions/virtualMachineScaleSetOsProfile"
|
||||
},
|
||||
"storageProfile": {
|
||||
"$ref": "#/definitions/virtualMachineScaleSetStorageProfile"
|
||||
},
|
||||
"extensionProfile": {
|
||||
"$ref": "#/definitions/virtualMachineScaleSetExtensionProfile"
|
||||
},
|
||||
"networkProfile": {
|
||||
"$ref": "#/definitions/virtualMachineScaleSetNetworkProfile"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"osProfile",
|
||||
"storageProfile",
|
||||
"networkProfile"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
{
|
||||
"id": "http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Resources.json",
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "Microsoft.Resources",
|
||||
"description": "Microsoft Resources Resource Types",
|
||||
"definitions": {
|
||||
"deployments": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": [
|
||||
"Microsoft.Resources/deployments"
|
||||
]
|
||||
},
|
||||
"apiVersion": {
|
||||
"enum": [
|
||||
"2015-01-01"
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name of the deployment"
|
||||
},
|
||||
"dependsOn": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Collection of resources this deployment depends on"
|
||||
},
|
||||
"properties": {
|
||||
"allOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"mode": {
|
||||
"enum": [ "Incremental" ],
|
||||
"description": "Deployment mode"
|
||||
}
|
||||
},
|
||||
"required": [ "mode" ]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"templateLink": {
|
||||
"$ref": "#/definitions/templateLink"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"template": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"parametersLink": {
|
||||
"$ref": "#/definitions/parametersLink"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#/definitions/parameter"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"apiVersion",
|
||||
"name",
|
||||
"properties"
|
||||
]
|
||||
},
|
||||
"templateLink": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"uri": {
|
||||
"type": "string",
|
||||
"description": "URI referencing the deployment template"
|
||||
},
|
||||
"contentVersion": {
|
||||
"type": "string",
|
||||
"pattern": "(^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$)",
|
||||
"description": "If included it must match the contentVersion in the template"
|
||||
}
|
||||
},
|
||||
"required": [ "uri" ],
|
||||
"description": "Template file reference in a deployment"
|
||||
},
|
||||
"parametersLink": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"uri": {
|
||||
"type": "string",
|
||||
"description": "URI referencing the deployment template parameters"
|
||||
},
|
||||
"contentVersion": {
|
||||
"type": "string",
|
||||
"pattern": "(^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$)",
|
||||
"description": "If included it must match the contentVersion in the parameters file"
|
||||
}
|
||||
},
|
||||
"required": [ "uri" ],
|
||||
"description": "Parameter file reference in a deployment"
|
||||
},
|
||||
"links": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": [
|
||||
"Microsoft.Resources/links"
|
||||
]
|
||||
},
|
||||
"apiVersion": {
|
||||
"enum": [
|
||||
"2015-01-01"
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"maxLength": 64,
|
||||
"description": "Name of the link"
|
||||
},
|
||||
"dependsOn": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Collection of resources this link depends on"
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"targetId": {
|
||||
"type": "string",
|
||||
"description": "Target resource id to link to"
|
||||
},
|
||||
"notes": {
|
||||
"type": "string",
|
||||
"maxLength": 512,
|
||||
"description": "Notes for this link"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"targetId"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"apiVersion",
|
||||
"name",
|
||||
"properties"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,376 +0,0 @@
|
||||
{
|
||||
"id": "http://schema.management.azure.com/schemas/2014-04-01-preview/Microsoft.Sql.json#",
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "Microsoft.SQLDatabase",
|
||||
"description": "Microsoft SQL Database Resource Types",
|
||||
"definitions": {
|
||||
"servers": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": [
|
||||
"Microsoft.Sql/servers"
|
||||
]
|
||||
},
|
||||
"apiVersion": {
|
||||
"enum": [
|
||||
"2014-04-01-preview"
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"version": {
|
||||
"enum": [
|
||||
"2.0",
|
||||
"12.0"
|
||||
],
|
||||
"description": "Microsoft.Sql/server: Azure SQL DB server version"
|
||||
},
|
||||
"administratorLogin": {
|
||||
"type": "string",
|
||||
"description": "Microsoft.Sql/server: administrator login name"
|
||||
},
|
||||
"administratorLoginPassword": {
|
||||
"type": "string",
|
||||
"description": "Microsoft.Sql/server: administrator login password"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"administratorLogin",
|
||||
"administratorLoginPassword"
|
||||
]
|
||||
},
|
||||
"resources": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/resourceBase"
|
||||
},
|
||||
{
|
||||
"oneOf": [
|
||||
{ "$ref": "http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Authorization.json#/definitions/locks" },
|
||||
{ "$ref": "http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Resources.json#/definitions/links" },
|
||||
{ "$ref": "#/definitions/databasesChild" },
|
||||
{ "$ref": "#/definitions/firewallrulesChild" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "Microsoft.Sql/servers: Child resources to define databases and firewall rules."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"apiVersion",
|
||||
"properties"
|
||||
]
|
||||
},
|
||||
"databasesBaseCommon": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"edition": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
"Web",
|
||||
"Business",
|
||||
"Basic",
|
||||
"Standard",
|
||||
"Premium"
|
||||
]
|
||||
}
|
||||
],
|
||||
"description": "Microsoft.Sql/server/databases: Optional. Edition of the database to be created. If omitted, the default is Web on server version 2.0 or Standard on server version 12.0."
|
||||
},
|
||||
"collation": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
"SQL_Latin1_General_Cp437_CS_AS_KI_WI",
|
||||
"SQL_Latin1_General_Cp437_CI_AS_KI_WI",
|
||||
"SQL_Latin1_General_Pref_Cp437_CI_AS_KI_WI",
|
||||
"SQL_Latin1_General_Cp437_CI_AI_KI_WI",
|
||||
"SQL_Latin1_General_Cp437_BIN",
|
||||
"SQL_Latin1_General_Cp850_BIN",
|
||||
"SQL_Latin1_General_Cp850_CS_AS_KI_WI",
|
||||
"SQL_Latin1_General_Cp850_CI_AS_KI_WI",
|
||||
"SQL_Latin1_General_Cp850_CI_AI_KI_WI",
|
||||
"SQL_Latin1_General_Pref_Cp850_CI_AS_KI_WI",
|
||||
"SQL_1xCompat_Cp850_CI_AS_KI_WI",
|
||||
"SQL_Latin1_General_Cp1_CS_AS_KI_WI",
|
||||
"SQL_Latin1_General_Cp1_CI_AS_KI_WI",
|
||||
"SQL_Latin1_General_Pref_Cp1_CI_AS_KI_WI",
|
||||
"SQL_Latin1_General_Cp1_CI_AI_KI_WI",
|
||||
"SQL_AltDiction_Cp850_CS_AS_KI_WI",
|
||||
"SQL_AltDiction_Pref_Cp850_CI_AS_KI_WI",
|
||||
"SQL_AltDiction_Cp850_CI_AI_KI_WI",
|
||||
"SQL_Scandainavian_Pref_Cp850_CI_AS_KI_WI",
|
||||
"SQL_Scandainavian_Cp850_CS_AS_KI_WI",
|
||||
"SQL_Scandainavian_Cp850_CI_AS_KI_WI",
|
||||
"SQL_AltDiction_Cp850_CI_AS_KI_WI",
|
||||
"SQL_Latin1_General_1250_BIN",
|
||||
"SQL_Latin1_General_Cp1250_CS_AS_KI_WI",
|
||||
"SQL_Latin1_General_Cp1250_CI_AS_KI_WI",
|
||||
"SQL_Czech_Cp1250_CS_AS_KI_WI",
|
||||
"SQL_Czech_Cp1250_CI_AS_KI_WI",
|
||||
"SQL_Hungarian_Cp1250_CS_AS_KI_WI",
|
||||
"SQL_Hungarian_Cp1250_CI_AS_KI_WI",
|
||||
"SQL_Polish_Cp1250_CS_AS_KI_WI",
|
||||
"SQL_Polish_Cp1250_CI_AS_KI_WI",
|
||||
"SQL_Romanian_Cp1250_CS_AS_KI_WI",
|
||||
"SQL_Romanian_Cp1250_CI_AS_KI_WI",
|
||||
"SQL_Croatian_Cp1250_CS_AS_KI_WI",
|
||||
"SQL_Croatian_Cp1250_CI_AS_KI_WI",
|
||||
"SQL_Slovak_Cp1250_CS_AS_KI_WI",
|
||||
"SQL_Slovak_Cp1250_CI_AS_KI_WI",
|
||||
"SQL_Slovenian_Cp1250_CS_AS_KI_WI",
|
||||
"SQL_Slovenian_Cp1250_CI_AS_KI_WI",
|
||||
"SQL_Latin1_General_1251_BIN",
|
||||
"SQL_Latin1_General_Cp1251_CS_AS_KI_WI",
|
||||
"SQL_Latin1_General_Cp1251_CI_AS_KI_WI",
|
||||
"SQL_Ukrainian_Cp1251_CS_AS_KI_WI",
|
||||
"SQL_Ukrainian_Cp1251_CI_AS_KI_WI",
|
||||
"SQL_Latin1_General_1253_BIN",
|
||||
"SQL_Latin1_General_Cp1253_CS_AS_KI_WI",
|
||||
"SQL_Latin1_General_Cp1253_CI_AS_KI_WI",
|
||||
"SQL_Latin1_General_Cp1253_CI_AI_KI_WI",
|
||||
"SQL_Latin1_General_1254_BIN",
|
||||
"SQL_Latin1_General_Cp1254_CS_AS_KI_WI",
|
||||
"SQL_Latin1_General_Cp1254_CI_AS_KI_WI",
|
||||
"SQL_Latin1_General_1255_BIN",
|
||||
"SQL_Latin1_General_Cp1255_CS_AS_KI_WI",
|
||||
"SQL_Latin1_General_Cp1255_CI_AS_KI_WI",
|
||||
"SQL_Latin1_General_1256_BIN",
|
||||
"SQL_Latin1_General_Cp1256_CS_AS_KI_WI",
|
||||
"SQL_Latin1_General_Cp1256_CI_AS_KI_WI",
|
||||
"SQL_Latin1_General_1257_BIN",
|
||||
"SQL_Latin1_General_Cp1257_CS_AS_KI_WI",
|
||||
"SQL_Latin1_General_Cp1257_CI_AS_KI_WI",
|
||||
"SQL_Estonian_Cp1257_CS_AS_KI_WI",
|
||||
"SQL_Estonian_Cp1257_CI_AS_KI_WI",
|
||||
"SQL_Latvian_Cp1257_CS_AS_KI_WI",
|
||||
"SQL_Latvian_Cp1257_CI_AS_KI_WI",
|
||||
"SQL_Lithuanian_Cp1257_CS_AS_KI_WI",
|
||||
"SQL_Lithuanian_Cp1257_CI_AS_KI_WI",
|
||||
"SQL_Danish_Pref_Cp1_CI_AS_KI_WI",
|
||||
"SQL_SwedishPhone_Pref_Cp1_CI_AS_KI_WI",
|
||||
"SQL_SwedishStd_Pref_Cp1_CI_AS_KI_WI",
|
||||
"SQL_Icelandic_Pref_Cp1_CI_AS_KI_WI"
|
||||
]
|
||||
}
|
||||
],
|
||||
"description": "Microsoft.Sql/server/databases: Database collation"
|
||||
},
|
||||
"maxSizeBytes": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
"104857600",
|
||||
"524288000",
|
||||
"1073741824",
|
||||
"2147483648",
|
||||
"5368709120",
|
||||
"10737418240",
|
||||
"21474836480",
|
||||
"32212254720",
|
||||
"42949672960",
|
||||
"53687091200",
|
||||
"107374182400",
|
||||
"161061273600",
|
||||
"214748364800",
|
||||
"268435456000",
|
||||
"322122547200",
|
||||
"429496729600",
|
||||
"536870912000"
|
||||
]
|
||||
}
|
||||
],
|
||||
"description": "Microsoft.Sql/server/databases: Sets the maximum size, in bytes, for the database. This value must be within the range of allowed values for Edition."
|
||||
},
|
||||
"requestedServiceObjectiveId": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
"910B4FCB-8A29-4C3E-958F-F7BA794388B2",
|
||||
"DD6D99BB-F193-4EC1-86F2-43D3BCCBC49C",
|
||||
"F1173C43-91BD-4AAA-973C-54E79E15235B",
|
||||
"1B1EBD4D-D903-4BAA-97F9-4EA675F5E928",
|
||||
"455330E1-00CD-488B-B5FA-177C226F28B7",
|
||||
"789681B8-CA10-4EB0-BDF2-E0B050601B40",
|
||||
"7203483A-C4FB-4304-9E9F-17C71C904F5D",
|
||||
"A7D1B92D-C987-4375-B54D-2B1D0E0F5BB0",
|
||||
"A7C4C615-CFB1-464B-B252-925BE0A19446"
|
||||
]
|
||||
}
|
||||
],
|
||||
"description": "Microsoft.Sql/server/databases: The GUID corresponding to the performance level for Edition. Shared = 910B4FCB-8A29-4C3E-958F-F7BA794388B2, Basic = DD6D99BB-F193-4EC1-86F2-43D3BCCBC49C, S0 = F1173C43-91BD-4AAA-973C-54E79E15235B, S1 = 1B1EBD4D-D903-4BAA-97F9-4EA675F5E928, S2 = 455330E1-00CD-488B-B5FA-177C226F28B7, S3 = 789681B8-CA10-4EB0-BDF2-E0B050601B40, P1 = 7203483A-C4FB-4304-9E9F-17C71C904F5D, P2 = A7D1B92D-C987-4375-B54D-2B1D0E0F5BB0, P3 = A7C4C615-CFB1-464B-B252-925BE0A19446"
|
||||
}
|
||||
}
|
||||
},
|
||||
"databasesBaseAll": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"createMode": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
"Copy",
|
||||
"OnlineSecondary",
|
||||
"OfflineSecondary",
|
||||
"Recovery",
|
||||
"PointInTimeRestore",
|
||||
"Restore"
|
||||
]
|
||||
}
|
||||
],
|
||||
"description": "Microsoft.Sql/server/databases: Defines that databases is created as a Point-In-Time restoration of another database."
|
||||
},
|
||||
"sourceDatabaseId": {
|
||||
"type": "string",
|
||||
"description": "Microsoft.Sql/server/databases: The URI of the source database."
|
||||
},
|
||||
"restorePointInTime": {
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/UTC",
|
||||
"description": "Microsoft.Sql/server/databases: The point in time for the restore."
|
||||
},
|
||||
"sourceDatabaseDeletionDate": {
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/UTC",
|
||||
"description": "Microsoft.Sql/server/databases: The deletion date time of the source database."
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
"databasesBase": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/databasesBaseCommon"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/databasesBaseAll"
|
||||
}
|
||||
]
|
||||
},
|
||||
"databasesChild": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": [
|
||||
"databases"
|
||||
]
|
||||
},
|
||||
"apiVersion": {
|
||||
"enum": [
|
||||
"2014-04-01-preview"
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"$ref": "#/definitions/databasesBase"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"apiVersion",
|
||||
"properties"
|
||||
]
|
||||
},
|
||||
"databases": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": [
|
||||
"Microsoft.Sql/servers/databases"
|
||||
]
|
||||
},
|
||||
"apiVersion": {
|
||||
"enum": [
|
||||
"2014-04-01-preview"
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"$ref": "#/definitions/databasesBase"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"apiVersion",
|
||||
"properties"
|
||||
]
|
||||
},
|
||||
"firewallrulesBase": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"endIpAddress": {
|
||||
"type": "string",
|
||||
"description": "Microsoft.Sql/server/firewallrules: ending IP address"
|
||||
},
|
||||
"startIpAddress": {
|
||||
"type": "string",
|
||||
"description": "Microsoft.Sql/server/firewallrules: starting IP address"
|
||||
}
|
||||
}
|
||||
},
|
||||
"firewallrulesChild": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": [
|
||||
"firewallrules"
|
||||
]
|
||||
},
|
||||
"apiVersion": {
|
||||
"enum": [
|
||||
"2014-04-01-preview"
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"$ref": "#/definitions/firewallrulesBase"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"apiVersion",
|
||||
"properties"
|
||||
]
|
||||
},
|
||||
"firewallrules": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": [
|
||||
"Microsoft.Sql/servers/firewallrules"
|
||||
]
|
||||
},
|
||||
"apiVersion": {
|
||||
"enum": [
|
||||
"2014-04-01-preview"
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"$ref": "#/definitions/firewallrulesBase"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"apiVersion",
|
||||
"properties"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,332 +0,0 @@
|
||||
{
|
||||
"id": "http://schema.management.azure.com/schemas/2014-06-01/Microsoft.Web.json",
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "Microsoft.Web",
|
||||
"description": "Microsoft Web Resource Types",
|
||||
"definitions": {
|
||||
"serverfarms": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": [
|
||||
"Microsoft.Web/serverfarms"
|
||||
]
|
||||
},
|
||||
"apiVersion": {
|
||||
"enum": [
|
||||
"2014-06-01"
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Microsoft.Web/serverfarms: Name of the server farm."
|
||||
},
|
||||
"sku": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
"Free",
|
||||
"Shared",
|
||||
"Basic",
|
||||
"Standard"
|
||||
]
|
||||
}
|
||||
],
|
||||
"description": "Microsoft.Web/serverfarms: Server farm sku."
|
||||
},
|
||||
"workerSize": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
"Small",
|
||||
"Medium",
|
||||
"Large"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 2
|
||||
}
|
||||
],
|
||||
"description": "Microsoft.Web/serverfarms: The instance size."
|
||||
},
|
||||
"numberOfWorkers": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 10
|
||||
}
|
||||
],
|
||||
"description": "Microsoft.Web/serverfarms: The instance count, which is the number of virtual machines dedicated to the farm. Supported values are 1-10."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"apiVersion",
|
||||
"properties"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": [
|
||||
"Microsoft.Web/sites/config",
|
||||
"config"
|
||||
]
|
||||
},
|
||||
"apiVersion": {
|
||||
"enum": [
|
||||
"2014-06-01"
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"connectionStrings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ConnectionString": {
|
||||
"type": "string",
|
||||
"description": "Microsoft.Web/sites/config: connection string"
|
||||
},
|
||||
"Name": {
|
||||
"type": "string",
|
||||
"description": "Microsoft.Web/sites/config: connection string name"
|
||||
},
|
||||
"Type": {
|
||||
"type": "integer",
|
||||
"description": "Microsoft.Web/sites/config: connection string type"
|
||||
}
|
||||
}
|
||||
},
|
||||
"uniqueItems": true,
|
||||
"description": "Microsoft.Web/sites/config: Connection strings for database and other external resources."
|
||||
},
|
||||
"phpVersion": {
|
||||
"type": "string",
|
||||
"description": "Microsoft.Web/sites/config: PHP version (an empty string disables PHP)."
|
||||
},
|
||||
"netFrameworkVersion": {
|
||||
"type": "string",
|
||||
"description": "Microsoft.Web/sites/config: The .Net Framework version."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Microsoft.Web/sites: Configuration settings for a web site.",
|
||||
"required": [
|
||||
"type",
|
||||
"apiVersion",
|
||||
"properties"
|
||||
]
|
||||
},
|
||||
"extensions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": [
|
||||
"Microsoft.Web/sites/extensions",
|
||||
"extensions"
|
||||
]
|
||||
},
|
||||
"apiVersion": {
|
||||
"enum": [
|
||||
"2014-06-01"
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"packageUri": {
|
||||
"type": "string",
|
||||
"description": "Microsoft.Web/sites/extensions: uri of package"
|
||||
},
|
||||
"dbType": {
|
||||
"type": "string",
|
||||
"description": "Microsoft.Web/sites/extensions: type of database"
|
||||
},
|
||||
"connectionString": {
|
||||
"type": "string",
|
||||
"description": "Microsoft.Web/sites/extensions: connection string"
|
||||
},
|
||||
"setParameters": {
|
||||
"type": "object",
|
||||
"description": "Microsoft.Web/sites/extensions: parameters"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"apiVersion",
|
||||
"properties"
|
||||
]
|
||||
},
|
||||
"sites": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": [
|
||||
"Microsoft.Web/sites"
|
||||
]
|
||||
},
|
||||
"apiVersion": {
|
||||
"enum": [
|
||||
"2014-06-01"
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Microsoft.Web/sites: The name of web site."
|
||||
},
|
||||
"serverFarm": {
|
||||
"type": "string",
|
||||
"description": "Microsoft.Web/sites: The name of server farm site belongs to."
|
||||
},
|
||||
"hostnames": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Microsoft.Web/sites: An array of strings that contains the public hostnames for the site, including custom domains."
|
||||
},
|
||||
"enabledHostnames": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Microsoft.Web/sites: An array of strings that contains enabled hostnames for the site. By default, these are <SiteName>.azurewebsites.net and <SiteName>.scm.azurewebsites.net."
|
||||
},
|
||||
"hostNameSslStates": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Microsoft.Web/sites/hostNameSslStates: The URL of the web site."
|
||||
},
|
||||
"sslState": {
|
||||
"oneOf": [
|
||||
{
|
||||
"enum": [
|
||||
"Disabled",
|
||||
"IpBasedEnabled",
|
||||
"SniEnabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 2
|
||||
},
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
|
||||
}
|
||||
],
|
||||
"description": "Microsoft.Web/sites/hostNameSslStates. The SSL state."
|
||||
},
|
||||
"thumbprint": {
|
||||
"type": "string",
|
||||
"description": "Microsoft.Web/sites/hostNameSslStates: A string that contains the thumbprint of the SSL certificate."
|
||||
},
|
||||
"ipBasedSslState": {
|
||||
"oneOf": [
|
||||
{
|
||||
"enum": [
|
||||
"Disabled",
|
||||
"IpBasedEnabled",
|
||||
"SniEnabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 2
|
||||
},
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
|
||||
}
|
||||
],
|
||||
"description": "Microsoft.Web/sites/hostNameSslStates: IP Based SSL state"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Microsoft.Web/sites: Container for SSL states."
|
||||
}
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"allOf": [
|
||||
{ "$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/resourceBase" },
|
||||
{
|
||||
"oneOf": [
|
||||
{"$ref": "#/definitions/config"},
|
||||
{"$ref": "#/definitions/extensions"}
|
||||
]
|
||||
}
|
||||
],
|
||||
"description": "Microsoft.Web/sites: Child resources to define configuration and extensions."
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"apiVersion",
|
||||
"properties"
|
||||
]
|
||||
},
|
||||
"certificates": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": [
|
||||
"Microsoft.Web/certificates"
|
||||
]
|
||||
},
|
||||
"apiVersion": {
|
||||
"enum": [
|
||||
"2014-06-01"
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pfxBlob": {
|
||||
"type": "string",
|
||||
"description": "Microsoft.Web/certificates: A base64Binary value that contains the PfxBlob of the certificate."
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"description": "Microsoft.Web/certficates: A string that contains the password for the certificate."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
{
|
||||
"id": "http://schema.management.azure.com/schemas/2014-04-01/SuccessBricks.ClearDB.json",
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "SuccessBricks.ClearDB",
|
||||
"description": "SuccessBricks ClearDB Resource Types",
|
||||
"definitions": {
|
||||
"databases": {
|
||||
"type":"object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": [
|
||||
"SuccessBricks.ClearDB/databases"
|
||||
]
|
||||
},
|
||||
"apiVersion": {
|
||||
"enum": [
|
||||
"2014-04-01"
|
||||
]
|
||||
},
|
||||
"plan": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
"Free",
|
||||
"Jupiter",
|
||||
"Saturn",
|
||||
"Venus"
|
||||
]
|
||||
}
|
||||
],
|
||||
"description": "Name of the plan"
|
||||
}
|
||||
},
|
||||
"required": ["name"],
|
||||
"description": "ClearDB database plan"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"apiVersion",
|
||||
"plan"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
{
|
||||
"id": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters#",
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "Parameters",
|
||||
"description": "An Azure deployment parameter file",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"contentVersion": {
|
||||
"type": "string",
|
||||
"pattern": "(^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$)",
|
||||
"description": "A 4 number format for the version number of this parameter file. For example, 1.0.0.0"
|
||||
},
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/parameter"
|
||||
},
|
||||
"description": "Collection of parameters to pass into a template"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"$schema",
|
||||
"contentVersion",
|
||||
"parameters"
|
||||
],
|
||||
"definitions": {
|
||||
"parameter": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"value": {
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/parameterValueTypes",
|
||||
"description": "Input value to template"
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"description": "Client specific metadata"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"value"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,382 +0,0 @@
|
||||
{
|
||||
"id": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "Template",
|
||||
"description": "An Azure deployment template",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"description": "JSON schema reference"
|
||||
},
|
||||
"contentVersion": {
|
||||
"type": "string",
|
||||
"pattern": "(^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$)",
|
||||
"description": "A 4 number format for the version number of this template file. For example, 1.0.0.0"
|
||||
},
|
||||
"variables": {
|
||||
"type": "object",
|
||||
"description": "Variable definitions"
|
||||
},
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"description": "Input parameter definitions",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/parameter"
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"type": "array",
|
||||
"description": "Collection of resources to be deployed",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/resourceBase"
|
||||
},
|
||||
{
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2014-06-01/Microsoft.Web.json#/definitions/certificates"
|
||||
},
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2014-06-01/Microsoft.Web.json#/definitions/serverfarms"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/resourceBaseForParentResources"
|
||||
},
|
||||
{
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2014-06-01/Microsoft.Web.json#/definitions/sites"
|
||||
},
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2014-04-01-preview/Microsoft.Sql.json#/definitions/servers"
|
||||
},
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-08-01/Microsoft.Compute.json#/resourceDefinitions/virtualMachines"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/resourceBaseExternal"
|
||||
},
|
||||
{
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2014-04-01/SuccessBricks.ClearDB.json#/definitions/databases"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ARMResourceBase"
|
||||
},
|
||||
{
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Resources.json#/definitions/deployments"
|
||||
},
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Resources.json#/definitions/links"
|
||||
},
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Authorization.json#/definitions/locks"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"outputs": {
|
||||
"type": "object",
|
||||
"description": "Output parameter definitions",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/output"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"$schema",
|
||||
"contentVersion",
|
||||
"resources"
|
||||
],
|
||||
"definitions": {
|
||||
"resourceBase": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/resourceBaseForParentResources"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"resources": {
|
||||
"$ref": "#/definitions/childResources"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"ARMResourceBase": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name of the resource"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "Resource type"
|
||||
},
|
||||
"apiVersion": {
|
||||
"type": "string",
|
||||
"description": "API Version of the resource type"
|
||||
},
|
||||
"dependsOn": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Collection of resources this resource depends on"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"type",
|
||||
"apiVersion"
|
||||
]
|
||||
},
|
||||
"proxyResourceBase": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ARMResourceBase"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"resources": {
|
||||
"$ref": "#/definitions/childResources"
|
||||
},
|
||||
"location": {
|
||||
"$ref": "#/definitions/resourceLocations",
|
||||
"description": "Location to deploy resource to"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"resourceBaseForParentResources": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ARMResourceBase"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"maxLength": 64,
|
||||
"pattern": "(^[a-zA-Z0-9_.()-]+$)",
|
||||
"description": "Kind of resource"
|
||||
},
|
||||
"location": {
|
||||
"$ref": "#/definitions/resourceLocations",
|
||||
"description": "Location to deploy resource to"
|
||||
},
|
||||
"tags": {
|
||||
"type": "object",
|
||||
"description": "Name-value pairs to add to the resource"
|
||||
},
|
||||
"plan": {
|
||||
"$ref": "#/definitions/resourcePlan"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"location"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"resourceBaseExternal": {
|
||||
"$ref": "#/definitions/resourceBase",
|
||||
"required": [
|
||||
"plan"
|
||||
]
|
||||
},
|
||||
"childResources": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ARMChildResources"
|
||||
}
|
||||
},
|
||||
"ARMChildResources": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Authorization.json#/definitions/locks"
|
||||
},
|
||||
{
|
||||
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Resources.json#/definitions/links"
|
||||
}
|
||||
]
|
||||
},
|
||||
"resourcePlan": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name of the plan"
|
||||
},
|
||||
"promotionCode": {
|
||||
"type": "string",
|
||||
"description": "Plan promotion code"
|
||||
},
|
||||
"publisher": {
|
||||
"type": "string",
|
||||
"description": "Name of the publisher"
|
||||
},
|
||||
"product": {
|
||||
"type": "string",
|
||||
"description": "Name of the product"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"description": "Plan of the resource"
|
||||
},
|
||||
"resourceLocations": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
"East Asia",
|
||||
"Southeast Asia",
|
||||
"Central US",
|
||||
"East US",
|
||||
"East US 2",
|
||||
"West US",
|
||||
"North Central US",
|
||||
"South Central US",
|
||||
"North Europe",
|
||||
"West Europe",
|
||||
"Japan West",
|
||||
"Japan East",
|
||||
"Brazil South",
|
||||
"Australia East",
|
||||
"Australia Southeast",
|
||||
"Central India",
|
||||
"West India",
|
||||
"South India"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"parameter": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"$ref": "#/definitions/parameterTypes",
|
||||
"description": "Type of input parameter"
|
||||
},
|
||||
"defaultValue": {
|
||||
"$ref": "#/definitions/parameterValueTypes",
|
||||
"description": "Default value to be used if one is not provided"
|
||||
},
|
||||
"allowedValues": {
|
||||
"type": "array",
|
||||
"description": "Value can only be one of these values"
|
||||
},
|
||||
"minLength": {
|
||||
"type": "integer",
|
||||
"description": "Minimum number of characters that must be used"
|
||||
},
|
||||
"maxLength": {
|
||||
"type": "integer",
|
||||
"description": "Maximum number of characters that can be used"
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"description": "Metadata for the parameter"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"description": "Input parameter definitions"
|
||||
},
|
||||
"output": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"$ref": "#/definitions/parameterTypes",
|
||||
"description": "Type of output value"
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/definitions/parameterValueTypes",
|
||||
"description": "Value assigned for output"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"description": "Set of output parameters"
|
||||
},
|
||||
"parameterTypes": {
|
||||
"enum": [
|
||||
"string",
|
||||
"securestring",
|
||||
"int",
|
||||
"bool",
|
||||
"object",
|
||||
"array"
|
||||
]
|
||||
},
|
||||
"parameterValueTypes": {
|
||||
"type": [
|
||||
"string",
|
||||
"boolean",
|
||||
"integer",
|
||||
"number",
|
||||
"object",
|
||||
"array",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"expression": {
|
||||
"type": "string",
|
||||
"pattern": "^\\[(concat|parameters|variables|reference|resourceId|resourceGroup|subscription|listKeys|listPackage|base64|providers|copyIndex|padLeft)\\(.*\\).*\\]$",
|
||||
"description": "Deployment template expression. Expressions are enclosed in [] and must start with a function of concat(), parameters(), variables(), reference(), resourceId(), resourceGroup(), subscription(), listKeys(), listPackage(), base64(), providers(), copyIndex(), padLeft()"
|
||||
},
|
||||
"Iso8601Duration": {
|
||||
"type": "string",
|
||||
"pattern": "^P(\\d+Y)?(\\d+M)?(\\d+D)?(T(((\\d+H)(\\d+M)?(\\d+(\\.\\d{1,2})?S)?)|((\\d+M)(\\d+(\\.\\d{1,2})?S)?)|((\\d+(\\.\\d{1,2})?S))))?$"
|
||||
},
|
||||
"UTC": {
|
||||
"type": "string",
|
||||
"pattern": "^\\d{4}(-(0[1-9]|1[0-2])(-([012]\\d|3[01])(T((([01]\\d|2[0123]):[0-5]\\d)|(24:00))(:(([0-5]\\d)|60)(\\.\\d{1,}){0,1}){0,1}){0,1}((Z)|([+-]((([01]\\d|2[0123]):[0-5]\\d)|(24:00)))){0,1}){0,1}){0,1}$"
|
||||
},
|
||||
"apiVersion": {
|
||||
"type": "string",
|
||||
"pattern": "(^((\\d\\d\\d\\d-\\d\\d-\\d\\d)|([0-9]+(\\.[0-9]+)?))(-[a-zA-Z][a-zA-Z0-9]*)?$)",
|
||||
"description": "API version of the resource type"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,444 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import Json = require('jsonc-parser');
|
||||
import {TextDocument, DocumentFormattingParams, Range, Position, FormattingOptions, TextEdit} from 'vscode-languageserver';
|
||||
import Formatter = require('../jsonFormatter');
|
||||
import assert = require('assert');
|
||||
import {applyEdits} from './textEditSupport';
|
||||
|
||||
suite('JSON Formatter', () => {
|
||||
|
||||
function format(unformatted: string, expected: string, insertSpaces = true) {
|
||||
let range: Range = null;
|
||||
let uri = 'test://test.json';
|
||||
|
||||
let rangeStart = unformatted.indexOf('|');
|
||||
let rangeEnd = unformatted.lastIndexOf('|');
|
||||
if (rangeStart !== -1 && rangeEnd !== -1) {
|
||||
// remove '|'
|
||||
var unformattedDoc = TextDocument.create(uri, 'json', 0, unformatted);
|
||||
unformatted = unformatted.substring(0, rangeStart) + unformatted.substring(rangeStart + 1, rangeEnd) + unformatted.substring(rangeEnd + 1);
|
||||
let startPos = unformattedDoc.positionAt(rangeStart);
|
||||
let endPos = unformattedDoc.positionAt(rangeEnd);
|
||||
range = Range.create(startPos, endPos);
|
||||
}
|
||||
|
||||
var document = TextDocument.create(uri, 'json', 0, unformatted);
|
||||
let edits = Formatter.format(document, range, { tabSize: 2, insertSpaces: insertSpaces });
|
||||
let formatted = applyEdits(document, edits);
|
||||
assert.equal(formatted, expected);
|
||||
}
|
||||
|
||||
test('object - single property', () => {
|
||||
var content = [
|
||||
'{"x" : 1}'
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'{',
|
||||
' "x": 1',
|
||||
'}'
|
||||
].join('\n');
|
||||
|
||||
format(content, expected);
|
||||
});
|
||||
test('object - multiple properties', () => {
|
||||
var content = [
|
||||
'{"x" : 1, "y" : "foo", "z" : true}'
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'{',
|
||||
' "x": 1,',
|
||||
' "y": "foo",',
|
||||
' "z": true',
|
||||
'}'
|
||||
].join('\n');
|
||||
|
||||
format(content, expected);
|
||||
});
|
||||
test('object - no properties ', () => {
|
||||
var content = [
|
||||
'{"x" : { }, "y" : {}}'
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'{',
|
||||
' "x": {},',
|
||||
' "y": {}',
|
||||
'}'
|
||||
].join('\n');
|
||||
|
||||
format(content, expected);
|
||||
});
|
||||
test('object - nesting', () => {
|
||||
var content = [
|
||||
'{"x" : { "y" : { "z" : { }}, "a": true}}'
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'{',
|
||||
' "x": {',
|
||||
' "y": {',
|
||||
' "z": {}',
|
||||
' },',
|
||||
' "a": true',
|
||||
' }',
|
||||
'}'
|
||||
].join('\n');
|
||||
|
||||
format(content, expected);
|
||||
});
|
||||
|
||||
test('array - single items', () => {
|
||||
var content = [
|
||||
'["[]"]'
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'[',
|
||||
' "[]"',
|
||||
']'
|
||||
].join('\n');
|
||||
|
||||
format(content, expected);
|
||||
});
|
||||
|
||||
test('array - multiple items', () => {
|
||||
var content = [
|
||||
'[true,null,1.2]'
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'[',
|
||||
' true,',
|
||||
' null,',
|
||||
' 1.2',
|
||||
']'
|
||||
].join('\n');
|
||||
|
||||
format(content, expected);
|
||||
});
|
||||
|
||||
test('array - no items', () => {
|
||||
var content = [
|
||||
'[ ]'
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'[]'
|
||||
].join('\n');
|
||||
|
||||
format(content, expected);
|
||||
});
|
||||
|
||||
test('array - nesting', () => {
|
||||
var content = [
|
||||
'[ [], [ [ {} ], "a" ] ]'
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'[',
|
||||
' [],',
|
||||
' [',
|
||||
' [',
|
||||
' {}',
|
||||
' ],',
|
||||
' "a"',
|
||||
' ]',
|
||||
']',
|
||||
].join('\n');
|
||||
|
||||
format(content, expected);
|
||||
});
|
||||
|
||||
test('syntax errors', () => {
|
||||
var content = [
|
||||
'[ null 1.2 ]'
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'[',
|
||||
' null 1.2',
|
||||
']',
|
||||
].join('\n');
|
||||
|
||||
format(content, expected);
|
||||
});
|
||||
|
||||
test('empty lines', () => {
|
||||
var content = [
|
||||
'{',
|
||||
'"a": true,',
|
||||
'',
|
||||
'"b": true',
|
||||
'}',
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'{',
|
||||
'\t"a": true,',
|
||||
'\t"b": true',
|
||||
'}',
|
||||
].join('\n');
|
||||
|
||||
format(content, expected, false);
|
||||
});
|
||||
test('single line comment', () => {
|
||||
var content = [
|
||||
'[ ',
|
||||
'//comment',
|
||||
'"foo", "bar"',
|
||||
'] '
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'[',
|
||||
' //comment',
|
||||
' "foo",',
|
||||
' "bar"',
|
||||
']',
|
||||
].join('\n');
|
||||
|
||||
format(content, expected);
|
||||
});
|
||||
test('block line comment', () => {
|
||||
var content = [
|
||||
'[{',
|
||||
' /*comment*/ ',
|
||||
'"foo" : true',
|
||||
'}] '
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'[',
|
||||
' {',
|
||||
' /*comment*/',
|
||||
' "foo": true',
|
||||
' }',
|
||||
']',
|
||||
].join('\n');
|
||||
|
||||
format(content, expected);
|
||||
});
|
||||
test('single line comment on same line', () => {
|
||||
var content = [
|
||||
' { ',
|
||||
' "a": {}// comment ',
|
||||
' } '
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'{',
|
||||
' "a": {} // comment ',
|
||||
'}',
|
||||
].join('\n');
|
||||
|
||||
format(content, expected);
|
||||
});
|
||||
test('single line comment on same line 2', () => {
|
||||
var content = [
|
||||
'{ //comment',
|
||||
'}'
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'{ //comment',
|
||||
'}'
|
||||
].join('\n');
|
||||
|
||||
format(content, expected);
|
||||
});
|
||||
test('block comment on same line', () => {
|
||||
var content = [
|
||||
'{ "a": {}, /*comment*/ ',
|
||||
' /*comment*/ "b": {}, ',
|
||||
' "c": {/*comment*/} } ',
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'{',
|
||||
' "a": {}, /*comment*/',
|
||||
' /*comment*/ "b": {},',
|
||||
' "c": { /*comment*/}',
|
||||
'}',
|
||||
].join('\n');
|
||||
|
||||
format(content, expected);
|
||||
});
|
||||
|
||||
test('block comment on same line advanced', () => {
|
||||
var content = [
|
||||
' { "d": [',
|
||||
' null',
|
||||
' ] /*comment*/',
|
||||
' ,"e": /*comment*/ [null] }',
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'{',
|
||||
' "d": [',
|
||||
' null',
|
||||
' ] /*comment*/,',
|
||||
' "e": /*comment*/ [',
|
||||
' null',
|
||||
' ]',
|
||||
'}',
|
||||
].join('\n');
|
||||
|
||||
format(content, expected);
|
||||
});
|
||||
|
||||
test('multiple block comments on same line', () => {
|
||||
var content = [
|
||||
'{ "a": {} /*comment*/, /*comment*/ ',
|
||||
' /*comment*/ "b": {} /*comment*/ } '
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'{',
|
||||
' "a": {} /*comment*/, /*comment*/',
|
||||
' /*comment*/ "b": {} /*comment*/',
|
||||
'}',
|
||||
].join('\n');
|
||||
|
||||
format(content, expected);
|
||||
});
|
||||
|
||||
test('multiple mixed comments on same line', () => {
|
||||
var content = [
|
||||
'[ /*comment*/ /*comment*/ // comment ',
|
||||
']'
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'[ /*comment*/ /*comment*/ // comment ',
|
||||
']'
|
||||
].join('\n');
|
||||
|
||||
format(content, expected);
|
||||
});
|
||||
|
||||
test('range', () => {
|
||||
var content = [
|
||||
'{ "a": {},',
|
||||
'|"b": [null, null]|',
|
||||
'} '
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'{ "a": {},',
|
||||
'"b": [',
|
||||
' null,',
|
||||
' null',
|
||||
']',
|
||||
'} ',
|
||||
].join('\n');
|
||||
|
||||
format(content, expected);
|
||||
});
|
||||
|
||||
test('range with existing indent', () => {
|
||||
var content = [
|
||||
'{ "a": {},',
|
||||
' |"b": [null],',
|
||||
'"c": {}',
|
||||
'} |'
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'{ "a": {},',
|
||||
' "b": [',
|
||||
' null',
|
||||
' ],',
|
||||
' "c": {}',
|
||||
'}',
|
||||
].join('\n');
|
||||
|
||||
format(content, expected);
|
||||
});
|
||||
|
||||
test('range with existing indent - tabs', () => {
|
||||
var content = [
|
||||
'{ "a": {},',
|
||||
'| "b": [null], ',
|
||||
'"c": {}',
|
||||
'} | '
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'{ "a": {},',
|
||||
'\t"b": [',
|
||||
'\t\tnull',
|
||||
'\t],',
|
||||
'\t"c": {}',
|
||||
'}',
|
||||
].join('\n');
|
||||
|
||||
format(content, expected, false);
|
||||
});
|
||||
|
||||
|
||||
test('block comment none-line breaking symbols', () => {
|
||||
var content = [
|
||||
'{ "a": [ 1',
|
||||
'/* comment */',
|
||||
', 2',
|
||||
'/* comment */',
|
||||
']',
|
||||
'/* comment */',
|
||||
',',
|
||||
' "b": true',
|
||||
'/* comment */',
|
||||
'}'
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'{',
|
||||
' "a": [',
|
||||
' 1',
|
||||
' /* comment */',
|
||||
' ,',
|
||||
' 2',
|
||||
' /* comment */',
|
||||
' ]',
|
||||
' /* comment */',
|
||||
' ,',
|
||||
' "b": true',
|
||||
' /* comment */',
|
||||
'}',
|
||||
].join('\n');
|
||||
|
||||
format(content, expected);
|
||||
});
|
||||
test('line comment after none-line breaking symbols', () => {
|
||||
var content = [
|
||||
'{ "a":',
|
||||
'// comment',
|
||||
'null,',
|
||||
' "b"',
|
||||
'// comment',
|
||||
': null',
|
||||
'// comment',
|
||||
'}'
|
||||
].join('\n');
|
||||
|
||||
var expected = [
|
||||
'{',
|
||||
' "a":',
|
||||
' // comment',
|
||||
' null,',
|
||||
' "b"',
|
||||
' // comment',
|
||||
' : null',
|
||||
' // comment',
|
||||
'}',
|
||||
].join('\n');
|
||||
|
||||
format(content, expected);
|
||||
});
|
||||
});
|
||||
@@ -1,103 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import assert = require('assert');
|
||||
import Parser = require('../jsonParser');
|
||||
import SchemaService = require('../jsonSchemaService');
|
||||
import JsonSchema = require('../jsonSchema');
|
||||
import {JSONCompletion} from '../jsonCompletion';
|
||||
import {XHROptions, XHRResponse} from 'request-light';
|
||||
import {JSONHover} from '../jsonHover';
|
||||
|
||||
import {Hover, TextDocument, TextDocumentIdentifier, TextDocumentPositionParams, Range, Position, TextEdit} from 'vscode-languageserver';
|
||||
|
||||
suite('JSON Hover', () => {
|
||||
|
||||
function testComputeInfo(value: string, schema: JsonSchema.IJSONSchema, position: Position): Thenable<Hover> {
|
||||
var uri = 'test://test.json';
|
||||
|
||||
var schemaService = new SchemaService.JSONSchemaService(requestService);
|
||||
var hoverProvider = new JSONHover(schemaService);
|
||||
var id = "http://myschemastore/test1";
|
||||
schemaService.registerExternalSchema(id, ["*.json"], schema);
|
||||
|
||||
var document = TextDocument.create(uri, 'json', 0, value);
|
||||
var jsonDoc = Parser.parse(value);
|
||||
return hoverProvider.doHover(document, position, jsonDoc);
|
||||
}
|
||||
|
||||
var requestService = function(options: XHROptions): Promise<XHRResponse> {
|
||||
return Promise.reject<XHRResponse>({ responseText: '', status: 404 });
|
||||
}
|
||||
|
||||
test('Simple schema', function(testDone) {
|
||||
|
||||
var content = '{"a": 42, "b": "hello", "c": false}';
|
||||
var schema: JsonSchema.IJSONSchema = {
|
||||
type: 'object',
|
||||
description: 'a very special object',
|
||||
properties: {
|
||||
'a': {
|
||||
type: 'number',
|
||||
description: 'A'
|
||||
},
|
||||
'b': {
|
||||
type: 'string',
|
||||
description: 'B'
|
||||
},
|
||||
'c': {
|
||||
type: 'boolean',
|
||||
description: 'C'
|
||||
}
|
||||
}
|
||||
};
|
||||
Promise.all([
|
||||
testComputeInfo(content, schema, { line: 0, character: 0 }).then((result) => {
|
||||
assert.deepEqual(result.contents, ['a very special object']);
|
||||
}),
|
||||
testComputeInfo(content, schema, { line: 0, character: 1 }).then((result) => {
|
||||
assert.deepEqual(result.contents, ['A']);
|
||||
}),
|
||||
testComputeInfo(content, schema, { line: 0, character: 32 }).then((result) => {
|
||||
assert.deepEqual(result.contents, ['C']);
|
||||
}),
|
||||
testComputeInfo(content, schema, { line: 0, character: 7 }).then((result) => {
|
||||
assert.deepEqual(result.contents, ['A']);
|
||||
})
|
||||
]).then(() => testDone(), (error) => testDone(error));
|
||||
});
|
||||
|
||||
test('Nested schema', function(testDone) {
|
||||
|
||||
var content = '{"a": 42, "b": "hello"}';
|
||||
var schema: JsonSchema.IJSONSchema = {
|
||||
oneOf: [{
|
||||
type: 'object',
|
||||
description: 'a very special object',
|
||||
properties: {
|
||||
'a': {
|
||||
type: 'number',
|
||||
description: 'A'
|
||||
},
|
||||
'b': {
|
||||
type: 'string',
|
||||
description: 'B'
|
||||
},
|
||||
}
|
||||
}, {
|
||||
type: 'array'
|
||||
}]
|
||||
};
|
||||
Promise.all([
|
||||
testComputeInfo(content, schema, { line: 0, character: 9 }).then((result) => {
|
||||
assert.deepEqual(result.contents, ['a very special object']);
|
||||
}),
|
||||
testComputeInfo(content, schema, { line: 0, character: 1 }).then((result) => {
|
||||
assert.deepEqual(result.contents, ['A']);
|
||||
})
|
||||
]).then(() => testDone(), (error) => testDone(error));
|
||||
});
|
||||
})
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,625 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import assert = require('assert');
|
||||
import SchemaService = require('../jsonSchemaService');
|
||||
import JsonSchema = require('../jsonSchema');
|
||||
import Json = require('jsonc-parser');
|
||||
import Parser = require('../jsonParser');
|
||||
import fs = require('fs');
|
||||
import url = require('url');
|
||||
import path = require('path');
|
||||
import {XHROptions, XHRResponse} from 'request-light';
|
||||
|
||||
|
||||
suite('JSON Schema', () => {
|
||||
var fixureDocuments = {
|
||||
'http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json': 'deploymentTemplate.json',
|
||||
'http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json': 'deploymentParameters.json',
|
||||
'http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Authorization.json': 'Microsoft.Authorization.json',
|
||||
'http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Resources.json': 'Microsoft.Resources.json',
|
||||
'http://schema.management.azure.com/schemas/2014-04-01-preview/Microsoft.Sql.json': 'Microsoft.Sql.json',
|
||||
'http://schema.management.azure.com/schemas/2014-06-01/Microsoft.Web.json': 'Microsoft.Web.json',
|
||||
'http://schema.management.azure.com/schemas/2014-04-01/SuccessBricks.ClearDB.json': 'SuccessBricks.ClearDB.json',
|
||||
'http://schema.management.azure.com/schemas/2015-08-01/Microsoft.Compute.json': 'Microsoft.Compute.json'
|
||||
}
|
||||
|
||||
var requestServiceMock = function (options:XHROptions) : Promise<XHRResponse> {
|
||||
var uri = options.url;
|
||||
if (uri.length && uri[uri.length - 1] === '#') {
|
||||
uri = uri.substr(0, uri.length - 1);
|
||||
}
|
||||
var fileName = fixureDocuments[uri];
|
||||
if (fileName) {
|
||||
return new Promise<XHRResponse>((c, e) => {
|
||||
var fixturePath = path.join(__dirname, '../../src/test/fixtures', fileName);
|
||||
fs.readFile(fixturePath, 'UTF-8', (err, result) => {
|
||||
err ? e({ responseText: '', status: 404 }) : c({ responseText: result.toString(), status: 200 })
|
||||
});
|
||||
});
|
||||
}
|
||||
return Promise.reject<XHRResponse>({ responseText: '', status: 404 });
|
||||
}
|
||||
|
||||
let workspaceContext = {
|
||||
resolveRelativePath: (relativePath: string, resource: string) => {
|
||||
return url.resolve(resource, relativePath);
|
||||
}
|
||||
};
|
||||
|
||||
test('Resolving $refs', function(testDone) {
|
||||
var service = new SchemaService.JSONSchemaService(requestServiceMock, workspaceContext);
|
||||
service.setSchemaContributions({ schemas: {
|
||||
"https://myschemastore/main" : {
|
||||
id: 'https://myschemastore/main',
|
||||
type: 'object',
|
||||
properties: {
|
||||
child: {
|
||||
'$ref': 'https://myschemastore/child'
|
||||
}
|
||||
}
|
||||
},
|
||||
"https://myschemastore/child" :{
|
||||
id: 'https://myschemastore/child',
|
||||
type: 'bool',
|
||||
description: 'Test description'
|
||||
}
|
||||
}});
|
||||
|
||||
service.getResolvedSchema('https://myschemastore/main').then(fs => {
|
||||
assert.deepEqual(fs.schema.properties['child'], {
|
||||
id: 'https://myschemastore/child',
|
||||
type: 'bool',
|
||||
description: 'Test description'
|
||||
});
|
||||
}).then(() => testDone(), (error) => {
|
||||
testDone(error);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('Resolving $refs 2', function(testDone) {
|
||||
var service = new SchemaService.JSONSchemaService(requestServiceMock, workspaceContext);
|
||||
service.setSchemaContributions({ schemas: {
|
||||
"http://json.schemastore.org/swagger-2.0" : {
|
||||
id: 'http://json.schemastore.org/swagger-2.0',
|
||||
type: 'object',
|
||||
properties: {
|
||||
"responseValue": {
|
||||
"$ref": "#/definitions/jsonReference"
|
||||
}
|
||||
},
|
||||
definitions: {
|
||||
"jsonReference": {
|
||||
"type": "object",
|
||||
"required": [ "$ref" ],
|
||||
"properties": {
|
||||
"$ref": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
service.getResolvedSchema('http://json.schemastore.org/swagger-2.0').then(fs => {
|
||||
assert.deepEqual(fs.schema.properties['responseValue'], {
|
||||
type: 'object',
|
||||
required: [ "$ref" ],
|
||||
properties: { $ref: { type: 'string' }}
|
||||
});
|
||||
}).then(() => testDone(), (error) => {
|
||||
testDone(error);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('Resolving $refs 3', function(testDone) {
|
||||
var service = new SchemaService.JSONSchemaService(requestServiceMock, workspaceContext);
|
||||
service.setSchemaContributions({ schemas: {
|
||||
"https://myschemastore/main/schema1.json" : {
|
||||
id: 'https://myschemastore/schema1.json',
|
||||
type: 'object',
|
||||
properties: {
|
||||
p1: {
|
||||
'$ref': 'schema2.json#/definitions/hello'
|
||||
},
|
||||
p2: {
|
||||
'$ref': './schema2.json#/definitions/hello'
|
||||
},
|
||||
p3: {
|
||||
'$ref': '/main/schema2.json#/definitions/hello'
|
||||
}
|
||||
}
|
||||
},
|
||||
"https://myschemastore/main/schema2.json" :{
|
||||
id: 'https://myschemastore/main/schema2.json',
|
||||
definitions: {
|
||||
"hello": {
|
||||
"type": "string",
|
||||
"enum": [ "object" ],
|
||||
}
|
||||
}
|
||||
}
|
||||
}});
|
||||
|
||||
service.getResolvedSchema('https://myschemastore/main/schema1.json').then(fs => {
|
||||
assert.deepEqual(fs.schema.properties['p1'], {
|
||||
type: 'string',
|
||||
enum: [ "object" ]
|
||||
});
|
||||
assert.deepEqual(fs.schema.properties['p2'], {
|
||||
type: 'string',
|
||||
enum: [ "object" ]
|
||||
});
|
||||
assert.deepEqual(fs.schema.properties['p3'], {
|
||||
type: 'string',
|
||||
enum: [ "object" ]
|
||||
});
|
||||
}).then(() => testDone(), (error) => {
|
||||
testDone(error);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('FileSchema', function(testDone) {
|
||||
var service = new SchemaService.JSONSchemaService(requestServiceMock, workspaceContext);
|
||||
|
||||
service.setSchemaContributions({ schemas: {
|
||||
"main" : {
|
||||
id: 'main',
|
||||
type: 'object',
|
||||
properties: {
|
||||
child: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
'grandchild': {
|
||||
type: 'number',
|
||||
description: 'Meaning of Life'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}});
|
||||
|
||||
service.getResolvedSchema('main').then(fs => {
|
||||
var section = fs.getSection(['child', 'grandchild']);
|
||||
assert.equal(section.description, 'Meaning of Life');
|
||||
}).then(() => testDone(), (error) => {
|
||||
testDone(error);
|
||||
});
|
||||
});
|
||||
|
||||
test('Array FileSchema', function(testDone) {
|
||||
var service = new SchemaService.JSONSchemaService(requestServiceMock, workspaceContext);
|
||||
|
||||
service.setSchemaContributions({ schemas: {
|
||||
"main" : {
|
||||
id: 'main',
|
||||
type: 'object',
|
||||
properties: {
|
||||
child: {
|
||||
type: 'array',
|
||||
items: {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'grandchild': {
|
||||
type: 'number',
|
||||
description: 'Meaning of Life'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}});
|
||||
|
||||
service.getResolvedSchema('main').then(fs => {
|
||||
var section = fs.getSection(['child','0', 'grandchild']);
|
||||
assert.equal(section.description, 'Meaning of Life');
|
||||
}).then(() => testDone(), (error) => {
|
||||
testDone(error);
|
||||
});
|
||||
});
|
||||
|
||||
test('Missing subschema', function(testDone) {
|
||||
var service = new SchemaService.JSONSchemaService(requestServiceMock, workspaceContext);
|
||||
|
||||
service.setSchemaContributions({ schemas: {
|
||||
"main" : {
|
||||
id: 'main',
|
||||
type: 'object',
|
||||
properties: {
|
||||
child: {
|
||||
type: 'object'
|
||||
}
|
||||
}
|
||||
}
|
||||
}});
|
||||
|
||||
service.getResolvedSchema('main').then(fs => {
|
||||
var section = fs.getSection(['child','grandchild']);
|
||||
assert.strictEqual(section, null);
|
||||
}).then(() => testDone(), (error) => {
|
||||
testDone(error);
|
||||
});
|
||||
});
|
||||
|
||||
test('Preloaded Schema', function(testDone) {
|
||||
var service = new SchemaService.JSONSchemaService(requestServiceMock, workspaceContext);
|
||||
var id = 'https://myschemastore/test1';
|
||||
var schema : JsonSchema.IJSONSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
child: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
'grandchild': {
|
||||
type: 'number',
|
||||
description: 'Meaning of Life'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
service.registerExternalSchema(id, [ '*.json' ], schema);
|
||||
|
||||
service.getSchemaForResource('test.json', null).then((schema) => {
|
||||
var section = schema.getSection(['child','grandchild']);
|
||||
assert.equal(section.description, 'Meaning of Life');
|
||||
}).then(() => testDone(), (error) => {
|
||||
testDone(error);
|
||||
});
|
||||
});
|
||||
|
||||
test('External Schema', function(testDone) {
|
||||
var service = new SchemaService.JSONSchemaService(requestServiceMock, workspaceContext);
|
||||
var id = 'https://myschemastore/test1';
|
||||
var schema : JsonSchema.IJSONSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
child: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
'grandchild': {
|
||||
type: 'number',
|
||||
description: 'Meaning of Life'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
service.registerExternalSchema(id, [ '*.json' ], schema);
|
||||
|
||||
service.getSchemaForResource('test.json', null).then((schema) => {
|
||||
var section = schema.getSection(['child','grandchild']);
|
||||
assert.equal(section.description, 'Meaning of Life');
|
||||
}).then(() => testDone(), (error) => {
|
||||
testDone(error);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('Resolving in-line $refs', function (testDone) {
|
||||
var service = new SchemaService.JSONSchemaService(requestServiceMock, workspaceContext);
|
||||
var id = 'https://myschemastore/test1';
|
||||
|
||||
var schema:JsonSchema.IJSONSchema = {
|
||||
id: 'main',
|
||||
type: 'object',
|
||||
definitions: {
|
||||
'grandchild': {
|
||||
type: 'number',
|
||||
description: 'Meaning of Life'
|
||||
}
|
||||
},
|
||||
properties: {
|
||||
child: {
|
||||
type: 'array',
|
||||
items: {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'grandchild': {
|
||||
$ref: '#/definitions/grandchild'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
service.registerExternalSchema(id, [ '*.json' ], schema);
|
||||
|
||||
service.getSchemaForResource('test.json', null).then((fs) => {
|
||||
var section = fs.getSection(['child', '0', 'grandchild']);
|
||||
assert.equal(section.description, 'Meaning of Life');
|
||||
}).then(() => testDone(), (error) => {
|
||||
testDone(error);
|
||||
});
|
||||
});
|
||||
|
||||
test('Resolving in-line $refs automatically for external schemas', function(testDone) {
|
||||
var service = new SchemaService.JSONSchemaService(requestServiceMock, workspaceContext);
|
||||
var id = 'https://myschemastore/test1';
|
||||
var schema:JsonSchema.IJSONSchema = {
|
||||
id: 'main',
|
||||
type: 'object',
|
||||
definitions: {
|
||||
'grandchild': {
|
||||
type: 'number',
|
||||
description: 'Meaning of Life'
|
||||
}
|
||||
},
|
||||
properties: {
|
||||
child: {
|
||||
type: 'array',
|
||||
items: {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'grandchild': {
|
||||
$ref: '#/definitions/grandchild'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var fsm = service.registerExternalSchema(id, [ '*.json' ], schema);
|
||||
fsm.getResolvedSchema().then((fs) => {
|
||||
var section = fs.getSection(['child','0', 'grandchild']);
|
||||
assert.equal(section.description, 'Meaning of Life');
|
||||
}).then(() => testDone(), (error) => {
|
||||
testDone(error);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('Clearing External Schemas', function(testDone) {
|
||||
var service = new SchemaService.JSONSchemaService(requestServiceMock, workspaceContext);
|
||||
var id1 = 'http://myschemastore/test1';
|
||||
var schema1:JsonSchema.IJSONSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
child: {
|
||||
type: 'number'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var id2 = 'http://myschemastore/test2';
|
||||
var schema2:JsonSchema.IJSONSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
child: {
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
service.registerExternalSchema(id1, [ 'test.json', 'bar.json' ], schema1);
|
||||
|
||||
service.getSchemaForResource('test.json', null).then((schema) => {
|
||||
var section = schema.getSection(['child']);
|
||||
assert.equal(section.type, 'number');
|
||||
|
||||
service.clearExternalSchemas();
|
||||
|
||||
service.registerExternalSchema(id2, [ '*.json' ], schema2);
|
||||
|
||||
return service.getSchemaForResource('test.json', null).then((schema) => {
|
||||
var section = schema.getSection(['child']);
|
||||
assert.equal(section.type, 'string');
|
||||
});
|
||||
}).then(() => testDone(), (error) => {
|
||||
testDone(error);
|
||||
});
|
||||
});
|
||||
|
||||
test('Schema contributions', function(testDone) {
|
||||
var service = new SchemaService.JSONSchemaService(requestServiceMock, workspaceContext);
|
||||
|
||||
service.setSchemaContributions({ schemas: {
|
||||
"http://myschemastore/myschemabar" : {
|
||||
id: 'main',
|
||||
type: 'object',
|
||||
properties: {
|
||||
foo: {
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
}
|
||||
}, schemaAssociations: {
|
||||
'*.bar': ['http://myschemastore/myschemabar', 'http://myschemastore/myschemafoo']
|
||||
}});
|
||||
|
||||
var id2 = 'http://myschemastore/myschemafoo';
|
||||
var schema2:JsonSchema.IJSONSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
child: {
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
service.registerExternalSchema(id2, null, schema2);
|
||||
|
||||
service.getSchemaForResource('main.bar', null).then(resolvedSchema => {
|
||||
assert.deepEqual(resolvedSchema.errors, []);
|
||||
assert.equal(2, resolvedSchema.schema.allOf.length);
|
||||
|
||||
service.clearExternalSchemas();
|
||||
return service.getSchemaForResource('main.bar', null).then(resolvedSchema => {
|
||||
assert.equal(resolvedSchema.errors.length, 1);
|
||||
assert.equal(resolvedSchema.errors[0], "Problems loading reference 'http://myschemastore/myschemafoo': Unable to load schema from 'http://myschemastore/myschemafoo': Not Found. The requested location could not be found.");
|
||||
|
||||
service.clearExternalSchemas();
|
||||
service.registerExternalSchema(id2, null, schema2);
|
||||
return service.getSchemaForResource('main.bar', null).then(resolvedSchema => {
|
||||
assert.equal(resolvedSchema.errors.length, 0);
|
||||
});
|
||||
});
|
||||
}).then(() => testDone(), (error) => {
|
||||
testDone(error);
|
||||
});
|
||||
});
|
||||
|
||||
test('Resolving circular $refs', function(testDone) {
|
||||
|
||||
var service : SchemaService.IJSONSchemaService = new SchemaService.JSONSchemaService(requestServiceMock, workspaceContext);
|
||||
|
||||
var input = {
|
||||
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"resources": [
|
||||
{
|
||||
"name": "SQLServer",
|
||||
"type": "Microsoft.Sql/servers",
|
||||
"location": "West US",
|
||||
"apiVersion": "2014-04-01-preview",
|
||||
"dependsOn": [ ],
|
||||
"tags": {
|
||||
"displayName": "SQL Server"
|
||||
},
|
||||
"properties": {
|
||||
"administratorLogin": "asdfasd",
|
||||
"administratorLoginPassword": "asdfasdfasd"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
var document = Parser.parse(JSON.stringify(input));
|
||||
|
||||
service.getSchemaForResource('file://doc/mydoc.json', document).then(resolveSchema => {
|
||||
assert.deepEqual(resolveSchema.errors, []);
|
||||
|
||||
var content = JSON.stringify(resolveSchema.schema);
|
||||
assert.equal(content.indexOf('$ref'), -1); // no more $refs
|
||||
|
||||
var matchingSchemas = [];
|
||||
document.validate(resolveSchema.schema, matchingSchemas);
|
||||
assert.deepEqual(document.errors, []);
|
||||
assert.deepEqual(document.warnings, []);
|
||||
}).then(() => testDone(), (error) => {
|
||||
testDone(error);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('Resolving circular $refs, invalid document', function(testDone) {
|
||||
|
||||
var service : SchemaService.IJSONSchemaService = new SchemaService.JSONSchemaService(requestServiceMock, workspaceContext);
|
||||
|
||||
var input = {
|
||||
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"resources": [
|
||||
{
|
||||
"name": "foo",
|
||||
"type": "Microsoft.Resources/deployments",
|
||||
"apiVersion": "2015-01-01",
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
var document = Parser.parse(JSON.stringify(input));
|
||||
|
||||
service.getSchemaForResource('file://doc/mydoc.json', document).then(resolveSchema => {
|
||||
assert.deepEqual(resolveSchema.errors, []);
|
||||
|
||||
var content = JSON.stringify(resolveSchema.schema);
|
||||
assert.equal(content.indexOf('$ref'), -1); // no more $refs
|
||||
|
||||
var matchingSchemas = [];
|
||||
document.validate(resolveSchema.schema, matchingSchemas);
|
||||
assert.deepEqual(document.errors, []);
|
||||
assert.equal(document.warnings.length, 1);
|
||||
}).then(() => testDone(), (error) => {
|
||||
testDone(error);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('Validate Azure Resource Dfinition', function(testDone) {
|
||||
|
||||
|
||||
var service : SchemaService.IJSONSchemaService = new SchemaService.JSONSchemaService(requestServiceMock, workspaceContext);
|
||||
|
||||
var input = {
|
||||
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"resources": [
|
||||
{
|
||||
"apiVersion": "2015-06-15",
|
||||
"type": "Microsoft.Compute/virtualMachines",
|
||||
"name": "a",
|
||||
"location": "West US",
|
||||
"properties": {
|
||||
"hardwareProfile": {
|
||||
"vmSize": "Small"
|
||||
},
|
||||
"osProfile": {
|
||||
"computername": "a",
|
||||
"adminUsername": "a",
|
||||
"adminPassword": "a"
|
||||
},
|
||||
"storageProfile": {
|
||||
"imageReference": {
|
||||
"publisher": "a",
|
||||
"offer": "a",
|
||||
"sku": "a",
|
||||
"version": "latest"
|
||||
},
|
||||
"osDisk": {
|
||||
"name": "osdisk",
|
||||
"vhd": {
|
||||
"uri": "[concat('http://', 'b','.blob.core.windows.net/',variables('vmStorageAccountContainerName'),'/',variables('OSDiskName'),'.vhd')]"
|
||||
},
|
||||
"caching": "ReadWrite",
|
||||
"createOption": "FromImage"
|
||||
}
|
||||
},
|
||||
"networkProfile": {
|
||||
"networkInterfaces": [
|
||||
{
|
||||
"id": "[resourceId('Microsoft.Network/networkInterfaces',variables('nicName'))]"
|
||||
}
|
||||
]
|
||||
},
|
||||
"diagnosticsProfile": {
|
||||
"bootDiagnostics": {
|
||||
"enabled": "true",
|
||||
"storageUri": "[concat('http://',parameters('newStorageAccountName'),'.blob.core.windows.net')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
var document = Parser.parse(JSON.stringify(input));
|
||||
|
||||
service.getSchemaForResource('file://doc/mydoc.json', document).then(resolvedSchema => {
|
||||
assert.deepEqual(resolvedSchema.errors, []);
|
||||
|
||||
document.validate(resolvedSchema.schema);
|
||||
|
||||
assert.equal(document.warnings.length, 1);
|
||||
assert.equal(document.warnings[0].message, 'Missing property "computerName"');
|
||||
}).then(() => testDone(), (error) => {
|
||||
testDone(error);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,23 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {TextDocument, TextEdit} from 'vscode-languageserver';
|
||||
import assert = require('assert');
|
||||
|
||||
export function applyEdits(document: TextDocument, edits: TextEdit[]) : string {
|
||||
let text = document.getText();
|
||||
let sortedEdits = edits.sort((a, b) => document.offsetAt(b.range.start) - document.offsetAt(a.range.start));
|
||||
let lastOffset = text.length;
|
||||
sortedEdits.forEach(e => {
|
||||
let startOffset = document.offsetAt(e.range.start);
|
||||
let endOffset = document.offsetAt(e.range.end);
|
||||
assert.ok(startOffset <= endOffset);
|
||||
assert.ok(endOffset <= lastOffset);
|
||||
text = text.substring(0, startOffset) + e.newText + text.substring(endOffset, text.length);
|
||||
lastOffset = startOffset;
|
||||
});
|
||||
return text;
|
||||
}
|
||||
Reference in New Issue
Block a user