diff --git a/extensions/json/server/package.json b/extensions/json/server/package.json index d4db5625a96..7105fc912ef 100644 --- a/extensions/json/server/package.json +++ b/extensions/json/server/package.json @@ -9,7 +9,7 @@ }, "dependencies": { "request-light": "^0.1.0", - "jsonc-parser": "^0.2.2", + "vscode-json-languageservice": "^1.1.0", "vscode-languageserver": "^2.2.0", "vscode-nls": "^1.0.4" }, diff --git a/extensions/json/server/src/configuration.ts b/extensions/json/server/src/configuration.ts deleted file mode 100644 index 9e459258cfb..00000000000 --- a/extensions/json/server/src/configuration.ts +++ /dev/null @@ -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': {} - } - } -}; \ No newline at end of file diff --git a/extensions/json/server/src/jsonCompletion.ts b/extensions/json/server/src/jsonCompletion.ts deleted file mode 100644 index 0cf688b49b3..00000000000 --- a/extensions/json/server/src/jsonCompletion.ts +++ /dev/null @@ -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 { - 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 { - - 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[] = []; - - let addValue = true; - let currentKey = ''; - - let currentProperty: Parser.PropertyASTNode = null; - if (node) { - - if (node.type === 'string') { - let stringNode = node; - if (stringNode.isKey) { - addValue = !(node.parent && ((node.parent).value)); - currentProperty = node.parent ? 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 = (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 > ( node).colonOffset) { - let parentKey = (node).key.value; - - let valueNode = ( 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 = (node.parent).key.value; - doc.visit((n) => { - if (n.type === 'property' && (n).key.value === parentKey && (n).value && (n).value.type === 'object') { - collectSuggestionsForSimilarObject((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 - (node.parent).items.forEach((n) => { - if (n.type === 'object' && n !== node) { - collectSuggestionsForSimilarObject(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 > (node).colonOffset) { - let valueNode = (node).value; - if (valueNode && offset > valueNode.end) { - return; - } - // suggest values at the same key - let parentKey = (node).key.value; - doc.visit((n) => { - if (n.type === 'property' && (n).key.value === parentKey && (n).value) { - collectSuggestionsForValues((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 = (node.parent).key.value; - doc.visit((n) => { - if (n.type === 'property' && (n).key.value === parentKey && (n).value && (n).value.type === 'array') { - (((n).value).items).forEach((n) => { - collectSuggestionsForValues(n); - }); - } - return true; - }); - } else { - // suggest items in the same array - (node).items.forEach((n) => { - collectSuggestionsForValues(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 > (node).colonOffset) { - let valueNode = (node).value; - if (valueNode && offset > valueNode.end) { - return; // we are past the value node - } - parentKey = (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 = 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); - } -} \ No newline at end of file diff --git a/extensions/json/server/src/jsonContributions.ts b/extensions/json/server/src/jsonContributions.ts deleted file mode 100644 index b6211fb63cc..00000000000 --- a/extensions/json/server/src/jsonContributions.ts +++ /dev/null @@ -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; - collectPropertySuggestions(resource: string, location: JSONLocation, currentWord: string, addValue: boolean, isLast:boolean, result: ISuggestionsCollector) : Thenable; - collectValueSuggestions(resource: string, location: JSONLocation, propertyKey: string, result: ISuggestionsCollector): Thenable; - collectDefaultSuggestions(resource: string, result: ISuggestionsCollector): Thenable; - resolveSuggestion?(item: CompletionItem): Thenable; -} \ No newline at end of file diff --git a/extensions/json/server/src/jsonDocumentSymbols.ts b/extensions/json/server/src/jsonDocumentSymbols.ts deleted file mode 100644 index e0937f35ac4..00000000000 --- a/extensions/json/server/src/jsonDocumentSymbols.ts +++ /dev/null @@ -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 { - - 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[] = []; - (root).items.forEach((item) => { - if (item.type === 'object') { - let property = (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') { - (node).items.forEach((node: Parser.ASTNode) => { - collectOutlineEntries(result, node, containerName); - }); - } else if (node.type === 'object') { - let objectNode = 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; - } - } -} \ No newline at end of file diff --git a/extensions/json/server/src/jsonFormatter.ts b/extensions/json/server/src/jsonFormatter.ts deleted file mode 100644 index c95ec714d53..00000000000 --- a/extensions/json/server/src/jsonFormatter.ts +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/extensions/json/server/src/jsonHover.ts b/extensions/json/server/src/jsonHover.ts deleted file mode 100644 index 987e7f4a6a0..00000000000 --- a/extensions/json/server/src/jsonHover.ts +++ /dev/null @@ -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 { - - 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 = node; - if (stringNode.isKey) { - let propertyNode = 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; - }); - } -} \ No newline at end of file diff --git a/extensions/json/server/src/jsonLanguageService.ts b/extensions/json/server/src/jsonLanguageService.ts deleted file mode 100644 index 2b0c7028679..00000000000 --- a/extensions/json/server/src/jsonLanguageService.ts +++ /dev/null @@ -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; - parseJSONDocument(document: TextDocument): JSONDocument; - resetSchema(uri: string): boolean; - doResolve(item: CompletionItem): Thenable; - doComplete(document: TextDocument, position: Position, doc: JSONDocument): Thenable; - findDocumentSymbols(document: TextDocument, doc: JSONDocument): Promise; - doHover(document: TextDocument, position: Position, doc: JSONDocument): Thenable; - format(document: TextDocument, range: Range, options: FormattingOptions): Thenable; -} - -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; -} - -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)) - }; -} diff --git a/extensions/json/server/src/jsonLocation.ts b/extensions/json/server/src/jsonLocation.ts deleted file mode 100644 index 9ed3f0c0f3d..00000000000 --- a/extensions/json/server/src/jsonLocation.ts +++ /dev/null @@ -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('][') + ']'; - } -} \ No newline at end of file diff --git a/extensions/json/server/src/jsonParser.ts b/extensions/json/server/src/jsonParser.ts deleted file mode 100644 index cab510464d3..00000000000 --- a/extensions/json/server/src/jsonParser.ts +++ /dev/null @@ -1,1017 +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 JsonSchema = require('./jsonSchema'); -import {JSONLocation} from './jsonLocation'; - -import * as nls from 'vscode-nls'; -const localize = nls.loadMessageBundle(); - - -export interface IRange { - start: number; - end: number; -} - -export interface IError { - location: IRange; - message: string; -} - -export class ASTNode { - public start: number; - public end: number; - public type: string; - public parent: ASTNode; - public name: string; - - constructor(parent: ASTNode, type: string, name: string, start: number, end?: number) { - this.type = type; - this.name = name; - this.start = start; - this.end = end; - this.parent = parent; - } - - public getNodeLocation(): JSONLocation { - let path = this.parent ? this.parent.getNodeLocation() : new JSONLocation([]); - if (this.name) { - path = path.append(this.name); - } - return path; - } - - - public getChildNodes(): ASTNode[] { - return []; - } - - public getValue(): any { - // override in children - return; - } - - public contains(offset: number, includeRightBound: boolean = false): boolean { - return offset >= this.start && offset < this.end || includeRightBound && offset === this.end; - } - - public visit(visitor: (node: ASTNode) => boolean): boolean { - return visitor(this); - } - - public getNodeFromOffset(offset: number): ASTNode { - let findNode = (node: ASTNode): ASTNode => { - if (offset >= node.start && offset < node.end) { - let children = node.getChildNodes(); - for (let i = 0; i < children.length && children[i].start <= offset; i++) { - let item = findNode(children[i]); - if (item) { - return item; - } - } - return node; - } - return null; - }; - return findNode(this); - } - - public getNodeFromOffsetEndInclusive(offset: number): ASTNode { - let findNode = (node: ASTNode): ASTNode => { - if (offset >= node.start && offset <= node.end) { - let children = node.getChildNodes(); - for (let i = 0; i < children.length && children[i].start <= offset; i++) { - let item = findNode(children[i]); - if (item) { - return item; - } - } - return node; - } - return null; - }; - return findNode(this); - } - - public validate(schema: JsonSchema.IJSONSchema, validationResult: ValidationResult, matchingSchemas: IApplicableSchema[], offset: number = -1): void { - if (offset !== -1 && !this.contains(offset)) { - return; - } - - if (Array.isArray(schema.type)) { - if ((schema.type).indexOf(this.type) === -1) { - validationResult.warnings.push({ - location: { start: this.start, end: this.end }, - message: schema.errorMessage || localize('typeArrayMismatchWarning', 'Incorrect type. Expected one of {0}', (schema.type).join(', ')) - }); - } - } - else if (schema.type) { - if (this.type !== schema.type) { - validationResult.warnings.push({ - location: { start: this.start, end: this.end }, - message: schema.errorMessage || localize('typeMismatchWarning', 'Incorrect type. Expected "{0}"', schema.type) - }); - } - } - if (Array.isArray(schema.allOf)) { - schema.allOf.forEach((subSchema) => { - this.validate(subSchema, validationResult, matchingSchemas, offset); - }); - } - if (schema.not) { - let subValidationResult = new ValidationResult(); - let subMatchingSchemas: IApplicableSchema[] = []; - this.validate(schema.not, subValidationResult, subMatchingSchemas, offset); - if (!subValidationResult.hasErrors()) { - validationResult.warnings.push({ - location: { start: this.start, end: this.end }, - message: localize('notSchemaWarning', "Matches a schema that is not allowed.") - }); - } - if (matchingSchemas) { - subMatchingSchemas.forEach((ms) => { - ms.inverted = !ms.inverted; - matchingSchemas.push(ms); - }); - } - } - - let testAlternatives = (alternatives: JsonSchema.IJSONSchema[], maxOneMatch: boolean) => { - let matches = []; - - // remember the best match that is used for error messages - let bestMatch: { schema: JsonSchema.IJSONSchema; validationResult: ValidationResult; matchingSchemas: IApplicableSchema[]; } = null; - alternatives.forEach((subSchema) => { - let subValidationResult = new ValidationResult(); - let subMatchingSchemas: IApplicableSchema[] = []; - this.validate(subSchema, subValidationResult, subMatchingSchemas); - if (!subValidationResult.hasErrors()) { - matches.push(subSchema); - } - if (!bestMatch) { - bestMatch = { schema: subSchema, validationResult: subValidationResult, matchingSchemas: subMatchingSchemas }; - } else { - if (!maxOneMatch && !subValidationResult.hasErrors() && !bestMatch.validationResult.hasErrors()) { - // no errors, both are equally good matches - bestMatch.matchingSchemas.push.apply(bestMatch.matchingSchemas, subMatchingSchemas); - bestMatch.validationResult.propertiesMatches += subValidationResult.propertiesMatches; - bestMatch.validationResult.propertiesValueMatches += subValidationResult.propertiesValueMatches; - } else { - let compareResult = subValidationResult.compare(bestMatch.validationResult); - if (compareResult > 0) { - // our node is the best matching so far - bestMatch = { schema: subSchema, validationResult: subValidationResult, matchingSchemas: subMatchingSchemas }; - } else if (compareResult === 0) { - // there's already a best matching but we are as good - bestMatch.matchingSchemas.push.apply(bestMatch.matchingSchemas, subMatchingSchemas); - } - } - } - }); - - if (matches.length > 1 && maxOneMatch) { - validationResult.warnings.push({ - location: { start: this.start, end: this.start + 1 }, - message: localize('oneOfWarning', "Matches multiple schemas when only one must validate.") - }); - } - if (bestMatch !== null) { - validationResult.merge(bestMatch.validationResult); - validationResult.propertiesMatches += bestMatch.validationResult.propertiesMatches; - validationResult.propertiesValueMatches += bestMatch.validationResult.propertiesValueMatches; - if (matchingSchemas) { - matchingSchemas.push.apply(matchingSchemas, bestMatch.matchingSchemas); - } - } - return matches.length; - }; - if (Array.isArray(schema.anyOf)) { - testAlternatives(schema.anyOf, false); - } - if (Array.isArray(schema.oneOf)) { - testAlternatives(schema.oneOf, true); - } - - if (Array.isArray(schema.enum)) { - if (schema.enum.indexOf(this.getValue()) === -1) { - validationResult.warnings.push({ - location: { start: this.start, end: this.end }, - message: localize('enumWarning', 'Value is not an accepted value. Valid values: {0}', JSON.stringify(schema.enum)) - }); - } else { - validationResult.enumValueMatch = true; - } - } - - if (matchingSchemas !== null) { - matchingSchemas.push({ node: this, schema: schema }); - } - } -} - -export class NullASTNode extends ASTNode { - - constructor(parent: ASTNode, name: string, start: number, end?: number) { - super(parent, 'null', name, start, end); - } - - public getValue(): any { - return null; - } -} - -export class BooleanASTNode extends ASTNode { - - private value: boolean; - - constructor(parent: ASTNode, name: string, value: boolean, start: number, end?: number) { - super(parent, 'boolean', name, start, end); - this.value = value; - } - - public getValue(): any { - return this.value; - } - -} - -export class ArrayASTNode extends ASTNode { - - public items: ASTNode[]; - - constructor(parent: ASTNode, name: string, start: number, end?: number) { - super(parent, 'array', name, start, end); - this.items = []; - } - - public getChildNodes(): ASTNode[] { - return this.items; - } - - public getValue(): any { - return this.items.map((v) => v.getValue()); - } - - public addItem(item: ASTNode): boolean { - if (item) { - this.items.push(item); - return true; - } - return false; - } - - public visit(visitor: (node: ASTNode) => boolean): boolean { - let ctn = visitor(this); - for (let i = 0; i < this.items.length && ctn; i++) { - ctn = this.items[i].visit(visitor); - } - return ctn; - } - - public validate(schema: JsonSchema.IJSONSchema, validationResult: ValidationResult, matchingSchemas: IApplicableSchema[], offset: number = -1): void { - if (offset !== -1 && !this.contains(offset)) { - return; - } - super.validate(schema, validationResult, matchingSchemas, offset); - - if (Array.isArray(schema.items)) { - let subSchemas = schema.items; - subSchemas.forEach((subSchema, index) => { - let itemValidationResult = new ValidationResult(); - let item = this.items[index]; - if (item) { - item.validate(subSchema, itemValidationResult, matchingSchemas, offset); - validationResult.mergePropertyMatch(itemValidationResult); - } else if (this.items.length >= subSchemas.length) { - validationResult.propertiesValueMatches++; - } - }); - - if (schema.additionalItems === false && this.items.length > subSchemas.length) { - validationResult.warnings.push({ - location: { start: this.start, end: this.end }, - message: localize('additionalItemsWarning', 'Array has too many items according to schema. Expected {0} or fewer', subSchemas.length) - }); - } else if (this.items.length >= subSchemas.length) { - validationResult.propertiesValueMatches += (this.items.length - subSchemas.length); - } - } - else if (schema.items) { - this.items.forEach((item) => { - let itemValidationResult = new ValidationResult(); - item.validate(schema.items, itemValidationResult, matchingSchemas, offset); - validationResult.mergePropertyMatch(itemValidationResult); - }); - } - - if (schema.minItems && this.items.length < schema.minItems) { - validationResult.warnings.push({ - location: { start: this.start, end: this.end }, - message: localize('minItemsWarning', 'Array has too few items. Expected {0} or more', schema.minItems) - }); - } - - if (schema.maxItems && this.items.length > schema.maxItems) { - validationResult.warnings.push({ - location: { start: this.start, end: this.end }, - message: localize('maxItemsWarning', 'Array has too many items. Expected {0} or fewer', schema.minItems) - }); - } - - if (schema.uniqueItems === true) { - let values = this.items.map((node) => { - return node.getValue(); - }); - let duplicates = values.some((value, index) => { - return index !== values.lastIndexOf(value); - }); - if (duplicates) { - validationResult.warnings.push({ - location: { start: this.start, end: this.end }, - message: localize('uniqueItemsWarning', 'Array has duplicate items') - }); - } - } - } -} - -export class NumberASTNode extends ASTNode { - - public isInteger: boolean; - public value: number; - - constructor(parent: ASTNode, name: string, start: number, end?: number) { - super(parent, 'number', name, start, end); - this.isInteger = true; - this.value = Number.NaN; - } - - public getValue(): any { - return this.value; - } - - public validate(schema: JsonSchema.IJSONSchema, validationResult: ValidationResult, matchingSchemas: IApplicableSchema[], offset: number = -1): void { - if (offset !== -1 && !this.contains(offset)) { - return; - } - - // work around type validation in the base class - let typeIsInteger = false; - if (schema.type === 'integer' || (Array.isArray(schema.type) && (schema.type).indexOf('integer') !== -1)) { - typeIsInteger = true; - } - if (typeIsInteger && this.isInteger === true) { - this.type = 'integer'; - } - super.validate(schema, validationResult, matchingSchemas, offset); - this.type = 'number'; - - let val = this.getValue(); - - if (typeof schema.multipleOf === 'number') { - if (val % schema.multipleOf !== 0) { - validationResult.warnings.push({ - location: { start: this.start, end: this.end }, - message: localize('multipleOfWarning', 'Value is not divisible by {0}', schema.multipleOf) - }); - } - } - - if (typeof schema.minimum === 'number') { - if (schema.exclusiveMinimum && val <= schema.minimum) { - validationResult.warnings.push({ - location: { start: this.start, end: this.end }, - message: localize('exclusiveMinimumWarning', 'Value is below the exclusive minimum of {0}', schema.minimum) - }); - } - if (!schema.exclusiveMinimum && val < schema.minimum) { - validationResult.warnings.push({ - location: { start: this.start, end: this.end }, - message: localize('minimumWarning', 'Value is below the minimum of {0}', schema.minimum) - }); - } - } - - if (typeof schema.maximum === 'number') { - if (schema.exclusiveMaximum && val >= schema.maximum) { - validationResult.warnings.push({ - location: { start: this.start, end: this.end }, - message: localize('exclusiveMaximumWarning', 'Value is above the exclusive maximum of {0}', schema.maximum) - }); - } - if (!schema.exclusiveMaximum && val > schema.maximum) { - validationResult.warnings.push({ - location: { start: this.start, end: this.end }, - message: localize('maximumWarning', 'Value is above the maximum of {0}', schema.maximum) - }); - } - } - - } -} - -export class StringASTNode extends ASTNode { - public isKey: boolean; - public value: string; - - constructor(parent: ASTNode, name: string, isKey: boolean, start: number, end?: number) { - super(parent, 'string', name, start, end); - this.isKey = isKey; - this.value = ''; - } - - public getValue(): any { - return this.value; - } - - public validate(schema: JsonSchema.IJSONSchema, validationResult: ValidationResult, matchingSchemas: IApplicableSchema[], offset: number = -1): void { - if (offset !== -1 && !this.contains(offset)) { - return; - } - super.validate(schema, validationResult, matchingSchemas, offset); - - if (schema.minLength && this.value.length < schema.minLength) { - validationResult.warnings.push({ - location: { start: this.start, end: this.end }, - message: localize('minLengthWarning', 'String is shorter than the minimum length of ', schema.minLength) - }); - } - - if (schema.maxLength && this.value.length > schema.maxLength) { - validationResult.warnings.push({ - location: { start: this.start, end: this.end }, - message: localize('maxLengthWarning', 'String is shorter than the maximum length of ', schema.maxLength) - }); - } - - if (schema.pattern) { - let regex = new RegExp(schema.pattern); - if (!regex.test(this.value)) { - validationResult.warnings.push({ - location: { start: this.start, end: this.end }, - message: schema.errorMessage || localize('patternWarning', 'String does not match the pattern of "{0}"', schema.pattern) - }); - } - } - - } -} - -export class PropertyASTNode extends ASTNode { - public key: StringASTNode; - public value: ASTNode; - public colonOffset: number; - - constructor(parent: ASTNode, key: StringASTNode) { - super(parent, 'property', null, key.start); - this.key = key; - key.parent = this; - key.name = key.value; - this.colonOffset = -1; - } - - public getChildNodes(): ASTNode[] { - return this.value ? [this.key, this.value] : [this.key]; - } - - public setValue(value: ASTNode): boolean { - this.value = value; - return value !== null; - } - - public visit(visitor: (node: ASTNode) => boolean): boolean { - return visitor(this) && this.key.visit(visitor) && this.value && this.value.visit(visitor); - } - - public validate(schema: JsonSchema.IJSONSchema, validationResult: ValidationResult, matchingSchemas: IApplicableSchema[], offset: number = -1): void { - if (offset !== -1 && !this.contains(offset)) { - return; - } - if (this.value) { - this.value.validate(schema, validationResult, matchingSchemas, offset); - } - } -} - -export class ObjectASTNode extends ASTNode { - public properties: PropertyASTNode[]; - - constructor(parent: ASTNode, name: string, start: number, end?: number) { - super(parent, 'object', name, start, end); - - this.properties = []; - } - - public getChildNodes(): ASTNode[] { - return this.properties; - } - - public addProperty(node: PropertyASTNode): boolean { - if (!node) { - return false; - } - this.properties.push(node); - return true; - } - - public getFirstProperty(key: string): PropertyASTNode { - for (let i = 0; i < this.properties.length; i++) { - if (this.properties[i].key.value === key) { - return this.properties[i]; - } - } - return null; - } - - public getKeyList(): string[] { - return this.properties.map((p) => p.key.getValue()); - } - - public getValue(): any { - let value: any = {}; - this.properties.forEach((p) => { - let v = p.value && p.value.getValue(); - if (v) { - value[p.key.getValue()] = v; - } - }); - return value; - } - - public visit(visitor: (node: ASTNode) => boolean): boolean { - let ctn = visitor(this); - for (let i = 0; i < this.properties.length && ctn; i++) { - ctn = this.properties[i].visit(visitor); - } - return ctn; - } - - public validate(schema: JsonSchema.IJSONSchema, validationResult: ValidationResult, matchingSchemas: IApplicableSchema[], offset: number = -1): void { - if (offset !== -1 && !this.contains(offset)) { - return; - } - - super.validate(schema, validationResult, matchingSchemas, offset); - let seenKeys: { [key: string]: ASTNode } = {}; - let unprocessedProperties: string[] = []; - this.properties.forEach((node) => { - let key = node.key.value; - seenKeys[key] = node.value; - unprocessedProperties.push(key); - }); - - if (Array.isArray(schema.required)) { - schema.required.forEach((propertyName: string) => { - if (!seenKeys[propertyName]) { - let key = this.parent && this.parent && (this.parent).key; - let location = key ? { start: key.start, end: key.end } : { start: this.start, end: this.start + 1 }; - validationResult.warnings.push({ - location: location, - message: localize('MissingRequiredPropWarning', 'Missing property "{0}"', propertyName) - }); - } - }); - } - - - let propertyProcessed = (prop: string) => { - let index = unprocessedProperties.indexOf(prop); - while (index >= 0) { - unprocessedProperties.splice(index, 1); - index = unprocessedProperties.indexOf(prop); - } - }; - - if (schema.properties) { - Object.keys(schema.properties).forEach((propertyName: string) => { - propertyProcessed(propertyName); - let prop = schema.properties[propertyName]; - let child = seenKeys[propertyName]; - if (child) { - let propertyvalidationResult = new ValidationResult(); - child.validate(prop, propertyvalidationResult, matchingSchemas, offset); - validationResult.mergePropertyMatch(propertyvalidationResult); - } - - }); - } - - if (schema.patternProperties) { - Object.keys(schema.patternProperties).forEach((propertyPattern: string) => { - let regex = new RegExp(propertyPattern); - unprocessedProperties.slice(0).forEach((propertyName: string) => { - if (regex.test(propertyName)) { - propertyProcessed(propertyName); - let child = seenKeys[propertyName]; - if (child) { - let propertyvalidationResult = new ValidationResult(); - child.validate(schema.patternProperties[propertyPattern], propertyvalidationResult, matchingSchemas, offset); - validationResult.mergePropertyMatch(propertyvalidationResult); - } - - } - }); - }); - } - - if (schema.additionalProperties) { - unprocessedProperties.forEach((propertyName: string) => { - let child = seenKeys[propertyName]; - if (child) { - let propertyvalidationResult = new ValidationResult(); - child.validate(schema.additionalProperties, propertyvalidationResult, matchingSchemas, offset); - validationResult.mergePropertyMatch(propertyvalidationResult); - } - }); - } else if (schema.additionalProperties === false) { - if (unprocessedProperties.length > 0) { - unprocessedProperties.forEach((propertyName: string) => { - let child = seenKeys[propertyName]; - if (child) { - let propertyNode = child.parent; - - validationResult.warnings.push({ - location: { start: propertyNode.key.start, end: propertyNode.key.end }, - message: localize('DisallowedExtraPropWarning', 'Property {0} is not allowed', propertyName) - }); - } - }); - } - } - - if (schema.maxProperties) { - if (this.properties.length > schema.maxProperties) { - validationResult.warnings.push({ - location: { start: this.start, end: this.end }, - message: localize('MaxPropWarning', 'Object has more properties than limit of {0}', schema.maxProperties) - }); - } - } - - if (schema.minProperties) { - if (this.properties.length < schema.minProperties) { - validationResult.warnings.push({ - location: { start: this.start, end: this.end }, - message: localize('MinPropWarning', 'Object has fewer properties than the required number of {0}', schema.minProperties) - }); - } - } - - if (schema.dependencies) { - Object.keys(schema.dependencies).forEach((key: string) => { - let prop = seenKeys[key]; - if (prop) { - if (Array.isArray(schema.dependencies[key])) { - let valueAsArray: string[] = schema.dependencies[key]; - valueAsArray.forEach((requiredProp: string) => { - if (!seenKeys[requiredProp]) { - validationResult.warnings.push({ - location: { start: this.start, end: this.end }, - message: localize('RequiredDependentPropWarning', 'Object is missing property {0} required by property {1}', requiredProp, key) - }); - } else { - validationResult.propertiesValueMatches++; - } - }); - } else if (schema.dependencies[key]) { - let valueAsSchema: JsonSchema.IJSONSchema = schema.dependencies[key]; - let propertyvalidationResult = new ValidationResult(); - this.validate(valueAsSchema, propertyvalidationResult, matchingSchemas, offset); - validationResult.mergePropertyMatch(propertyvalidationResult); - } - } - }); - } - } -} - -export class JSONDocumentConfig { - public ignoreDanglingComma: boolean; - - constructor() { - this.ignoreDanglingComma = false; - } -} - -export interface IApplicableSchema { - node: ASTNode; - inverted?: boolean; - schema: JsonSchema.IJSONSchema; -} - -export class ValidationResult { - public errors: IError[]; - public warnings: IError[]; - - public propertiesMatches: number; - public propertiesValueMatches: number; - public enumValueMatch: boolean; - - constructor() { - this.errors = []; - this.warnings = []; - this.propertiesMatches = 0; - this.propertiesValueMatches = 0; - this.enumValueMatch = false; - } - - public hasErrors(): boolean { - return !!this.errors.length || !!this.warnings.length; - } - - public mergeAll(validationResults: ValidationResult[]): void { - validationResults.forEach((validationResult) => { - this.merge(validationResult); - }); - } - - public merge(validationResult: ValidationResult): void { - this.errors = this.errors.concat(validationResult.errors); - this.warnings = this.warnings.concat(validationResult.warnings); - } - - public mergePropertyMatch(propertyValidationResult: ValidationResult): void { - this.merge(propertyValidationResult); - this.propertiesMatches++; - if (propertyValidationResult.enumValueMatch || !propertyValidationResult.hasErrors() && propertyValidationResult.propertiesMatches) { - this.propertiesValueMatches++; - } - } - - public compare(other: ValidationResult): number { - let hasErrors = this.hasErrors(); - if (hasErrors !== other.hasErrors()) { - return hasErrors ? -1 : 1; - } - if (this.enumValueMatch !== other.enumValueMatch) { - return other.enumValueMatch ? -1 : 1; - } - if (this.propertiesValueMatches !== other.propertiesValueMatches) { - return this.propertiesValueMatches - other.propertiesValueMatches; - } - return this.propertiesMatches - other.propertiesMatches; - } - -} - -export class JSONDocument { - public config: JSONDocumentConfig; - public root: ASTNode; - - private validationResult: ValidationResult; - - constructor(config: JSONDocumentConfig) { - this.config = config; - this.validationResult = new ValidationResult(); - } - - public get errors(): IError[] { - return this.validationResult.errors; - } - - public get warnings(): IError[] { - return this.validationResult.warnings; - } - - public getNodeFromOffset(offset: number): ASTNode { - return this.root && this.root.getNodeFromOffset(offset); - } - - public getNodeFromOffsetEndInclusive(offset: number): ASTNode { - return this.root && this.root.getNodeFromOffsetEndInclusive(offset); - } - - public visit(visitor: (node: ASTNode) => boolean): void { - if (this.root) { - this.root.visit(visitor); - } - } - - public validate(schema: JsonSchema.IJSONSchema, matchingSchemas: IApplicableSchema[] = null, offset: number = -1): void { - if (this.root) { - this.root.validate(schema, this.validationResult, matchingSchemas, offset); - } - } -} - -export function parse(text: string, config = new JSONDocumentConfig()): JSONDocument { - - let _doc = new JSONDocument(config); - let _scanner = Json.createScanner(text, true); - - function _accept(token: Json.SyntaxKind): boolean { - if (_scanner.getToken() === token) { - _scanner.scan(); - return true; - } - return false; - } - - function _error(message: string, node: T = null, skipUntilAfter: Json.SyntaxKind[] = [], skipUntil: Json.SyntaxKind[] = []): T { - if (_doc.errors.length === 0 || _doc.errors[0].location.start !== _scanner.getTokenOffset()) { - // ignore multiple errors on the same offset - let error = { message: message, location: { start: _scanner.getTokenOffset(), end: _scanner.getTokenOffset() + _scanner.getTokenLength() } }; - _doc.errors.push(error); - } - - if (node) { - _finalize(node, false); - } - if (skipUntilAfter.length + skipUntil.length > 0) { - let token = _scanner.getToken(); - while (token !== Json.SyntaxKind.EOF) { - if (skipUntilAfter.indexOf(token) !== -1) { - _scanner.scan(); - break; - } else if (skipUntil.indexOf(token) !== -1) { - break; - } - token = _scanner.scan(); - } - } - return node; - } - - function _checkScanError(): boolean { - switch (_scanner.getTokenError()) { - case Json.ScanError.InvalidUnicode: - _error(localize('InvalidUnicode', 'Invalid unicode sequence in string')); - return true; - case Json.ScanError.InvalidEscapeCharacter: - _error(localize('InvalidEscapeCharacter', 'Invalid escape character in string')); - return true; - case Json.ScanError.UnexpectedEndOfNumber: - _error(localize('UnexpectedEndOfNumber', 'Unexpected end of number')); - return true; - case Json.ScanError.UnexpectedEndOfComment: - _error(localize('UnexpectedEndOfComment', 'Unexpected end of comment')); - return true; - case Json.ScanError.UnexpectedEndOfString: - _error(localize('UnexpectedEndOfString', 'Unexpected end of string')); - return true; - } - return false; - } - - function _finalize(node: T, scanNext: boolean): T { - node.end = _scanner.getTokenOffset() + _scanner.getTokenLength(); - - if (scanNext) { - _scanner.scan(); - } - - return node; - } - - function _parseArray(parent: ASTNode, name: string): ArrayASTNode { - if (_scanner.getToken() !== Json.SyntaxKind.OpenBracketToken) { - return null; - } - let node = new ArrayASTNode(parent, name, _scanner.getTokenOffset()); - _scanner.scan(); // consume OpenBracketToken - - let count = 0; - if (node.addItem(_parseValue(node, '' + count++))) { - while (_accept(Json.SyntaxKind.CommaToken)) { - if (!node.addItem(_parseValue(node, '' + count++)) && !_doc.config.ignoreDanglingComma) { - _error(localize('ValueExpected', 'Value expected')); - } - } - } - - if (_scanner.getToken() !== Json.SyntaxKind.CloseBracketToken) { - return _error(localize('ExpectedCloseBracket', 'Expected comma or closing bracket'), node); - } - - return _finalize(node, true); - } - - function _parseProperty(parent: ObjectASTNode, keysSeen: any): PropertyASTNode { - - let key = _parseString(null, null, true); - if (!key) { - if (_scanner.getToken() === Json.SyntaxKind.Unknown) { - // give a more helpful error message - let value = _scanner.getTokenValue(); - if (value.match(/^['\w]/)) { - _error(localize('DoubleQuotesExpected', 'Property keys must be doublequoted')); - } - } - return null; - } - let node = new PropertyASTNode(parent, key); - - if (keysSeen[key.value]) { - _doc.warnings.push({ location: { start: node.key.start, end: node.key.end }, message: localize('DuplicateKeyWarning', "Duplicate object key") }); - } - keysSeen[key.value] = true; - - if (_scanner.getToken() === Json.SyntaxKind.ColonToken) { - node.colonOffset = _scanner.getTokenOffset(); - } else { - return _error(localize('ColonExpected', 'Colon expected'), node, [], [Json.SyntaxKind.CloseBraceToken, Json.SyntaxKind.CommaToken]); - } - - _scanner.scan(); // consume ColonToken - - if (!node.setValue(_parseValue(node, key.value))) { - return _error(localize('ValueExpected', 'Value expected'), node, [], [Json.SyntaxKind.CloseBraceToken, Json.SyntaxKind.CommaToken]); - } - node.end = node.value.end; - return node; - } - - function _parseObject(parent: ASTNode, name: string): ObjectASTNode { - if (_scanner.getToken() !== Json.SyntaxKind.OpenBraceToken) { - return null; - } - let node = new ObjectASTNode(parent, name, _scanner.getTokenOffset()); - _scanner.scan(); // consume OpenBraceToken - - let keysSeen: any = {}; - if (node.addProperty(_parseProperty(node, keysSeen))) { - while (_accept(Json.SyntaxKind.CommaToken)) { - if (!node.addProperty(_parseProperty(node, keysSeen)) && !_doc.config.ignoreDanglingComma) { - _error(localize('PropertyExpected', 'Property expected')); - } - } - } - - if (_scanner.getToken() !== Json.SyntaxKind.CloseBraceToken) { - return _error(localize('ExpectedCloseBrace', 'Expected comma or closing brace'), node); - } - return _finalize(node, true); - } - - function _parseString(parent: ASTNode, name: string, isKey: boolean): StringASTNode { - if (_scanner.getToken() !== Json.SyntaxKind.StringLiteral) { - return null; - } - - let node = new StringASTNode(parent, name, isKey, _scanner.getTokenOffset()); - node.value = _scanner.getTokenValue(); - - _checkScanError(); - - return _finalize(node, true); - } - - function _parseNumber(parent: ASTNode, name: string): NumberASTNode { - if (_scanner.getToken() !== Json.SyntaxKind.NumericLiteral) { - return null; - } - - let node = new NumberASTNode(parent, name, _scanner.getTokenOffset()); - if (!_checkScanError()) { - let tokenValue = _scanner.getTokenValue(); - try { - let numberValue = JSON.parse(tokenValue); - if (typeof numberValue !== 'number') { - return _error(localize('InvalidNumberFormat', 'Invalid number format'), node); - } - node.value = numberValue; - } catch (e) { - return _error(localize('InvalidNumberFormat', 'Invalid number format'), node); - } - node.isInteger = tokenValue.indexOf('.') === -1; - } - return _finalize(node, true); - } - - function _parseLiteral(parent: ASTNode, name: string): ASTNode { - let node: ASTNode; - switch (_scanner.getToken()) { - case Json.SyntaxKind.NullKeyword: - node = new NullASTNode(parent, name, _scanner.getTokenOffset()); - break; - case Json.SyntaxKind.TrueKeyword: - node = new BooleanASTNode(parent, name, true, _scanner.getTokenOffset()); - break; - case Json.SyntaxKind.FalseKeyword: - node = new BooleanASTNode(parent, name, false, _scanner.getTokenOffset()); - break; - default: - return null; - } - return _finalize(node, true); - } - - function _parseValue(parent: ASTNode, name: string): ASTNode { - return _parseArray(parent, name) || _parseObject(parent, name) || _parseString(parent, name, false) || _parseNumber(parent, name) || _parseLiteral(parent, name); - } - - _scanner.scan(); - - _doc.root = _parseValue(null, null); - if (!_doc.root) { - _error(localize('Invalid symbol', 'Expected a JSON object, array or literal')); - } else if (_scanner.getToken() !== Json.SyntaxKind.EOF) { - _error(localize('End of file expected', 'End of file expected')); - } - return _doc; -} \ No newline at end of file diff --git a/extensions/json/server/src/jsonSchema.ts b/extensions/json/server/src/jsonSchema.ts deleted file mode 100644 index 9b892d1cad8..00000000000 --- a/extensions/json/server/src/jsonSchema.ts +++ /dev/null @@ -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; -} diff --git a/extensions/json/server/src/jsonSchemaService.ts b/extensions/json/server/src/jsonSchemaService.ts deleted file mode 100644 index 60614b7adf8..00000000000 --- a/extensions/json/server/src/jsonSchemaService.ts +++ /dev/null @@ -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; -} - -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; - - /** - * The schema from the file, with references resolved - */ - getResolvedSchema(): Thenable; -} - - -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; - private unresolvedSchema: Thenable; - 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 { - if (!this.unresolvedSchema) { - this.unresolvedSchema = this.service.loadSchema(this.url); - } - return this.unresolvedSchema; - } - - public getResolvedSchema(): Thenable { - 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 { - let id = this.normalizeId(schemaId); - let schemaHandle = this.schemasById[id]; - if (schemaHandle) { - return schemaHandle.getResolvedSchema(); - } - return Promise.resolve(null); - } - - public loadSchema(url: string): Thenable { - 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({}, [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({}, [errorMessage]); - } - ); - } - - public resolveSchemaContent(schemaToResolve: UnresolvedSchema, schemaURL: string): Thenable { - - 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 => { - 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 => { - let toWalk : IJSONSchema[] = [node]; - let seen: IJSONSchema[] = []; - - let openPromises: Thenable[] = []; - - 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, next.dependencies); - collectArrayEntries(next.anyOf, next.allOf, next.oneOf, next.items); - } - return Promise.all(openPromises); - }; - - return resolveRefs(schema, schema, schemaURL).then(_ => new ResolvedSchema(schema, resolveErrors)); - } - - public getSchemaForResource(resource: string, document: Parser.JSONDocument): Thenable { - - // first use $schema if present - if (document && document.root && document.root.type === 'object') { - let schemaProperties = (document.root).properties.filter((p) => (p.key.value === '$schema') && !!p.value); - if (schemaProperties.length > 0) { - let schemeId = 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; -} \ No newline at end of file diff --git a/extensions/json/server/src/jsonServerMain.ts b/extensions/json/server/src/jsonServerMain.ts index fa57e8f72dd..95bf8a89c5e 100644 --- a/extensions/json/server/src/jsonServerMain.ts +++ b/extensions/json/server/src/jsonServerMain.ts @@ -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 = { 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 => { - if (Strings.startsWith(options.url, 'file://')) { - let fsPath = URI.parse(options.url).fsPath; - return new Promise((c, e) => { +let schemaRequestService = (uri:string): Thenable => { + if (Strings.startsWith(uri, 'file://')) { + let fsPath = URI.parse(uri).fsPath; + return new Promise((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(10, 60, document => languageService.parseJSONDocument(document)); + function getJSONDocument(document: TextDocument): JSONDocument { - return languageService.parseJSONDocument(document); + return jsonDocuments.get(document); } connection.onCompletion(textDocumentPosition => { diff --git a/extensions/json/server/src/jsonValidation.ts b/extensions/json/server/src/jsonValidation.ts deleted file mode 100644 index b4cfb5df6d5..00000000000 --- a/extensions/json/server/src/jsonValidation.ts +++ /dev/null @@ -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' ? (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; - }); - } -} \ No newline at end of file diff --git a/extensions/json/server/src/jsoncontributions/fileAssociationContribution.ts b/extensions/json/server/src/jsoncontributions/fileAssociationContribution.ts index ba6967d75b6..2fe85c551e5 100644 --- a/extensions/json/server/src/jsoncontributions/fileAssociationContribution.ts +++ b/extensions/json/server/src/jsoncontributions/fileAssociationContribution.ts @@ -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 { + public collectDefaultCompletions(resource: string, result: CompletionsCollector): Thenable { return null; } - public collectPropertySuggestions(resource: string, location: JSONLocation, currentWord: string, addValue: boolean, isLast: boolean, result: ISuggestionsCollector): Thenable { - if (this.isSettingsFile(resource) && location.matches(['files.associations'])) { + public collectPropertyCompletions(resource: string, location: JSONPath, currentWord: string, addValue: boolean, isLast: boolean, result: CompletionsCollector): Thenable { + 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 { - if (this.isSettingsFile(resource) && location.matches(['files.associations'])) { + public collectValueCompletions(resource: string, location: JSONPath, currentKey: string, result: CompletionsCollector): Thenable { + 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 { + public getInfoContribution(resource: string, location: JSONPath): Thenable { return null; } } \ No newline at end of file diff --git a/extensions/json/server/src/jsoncontributions/globPatternContribution.ts b/extensions/json/server/src/jsoncontributions/globPatternContribution.ts index 47f1f61c07e..65a958bb919 100644 --- a/extensions/json/server/src/jsoncontributions/globPatternContribution.ts +++ b/extensions/json/server/src/jsoncontributions/globPatternContribution.ts @@ -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 { + public collectDefaultCompletions(resource: string, result: CompletionsCollector): Thenable { return null; } - public collectPropertySuggestions(resource: string, location: JSONLocation, currentWord: string, addValue: boolean, isLast: boolean, result: ISuggestionsCollector): Thenable { - 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 { + 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 { - if (this.isSettingsFile(resource) && (location.matches(['files.exclude']) || location.matches(['search.exclude']))) { + public collectValueCompletions(resource: string, location: JSONPath, currentKey: string, result: CompletionsCollector): Thenable { + 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 { + public getInfoContribution(resource: string, location: JSONPath): Thenable { return null; } } \ No newline at end of file diff --git a/extensions/json/server/src/jsoncontributions/projectJSONContribution.ts b/extensions/json/server/src/jsoncontributions/projectJSONContribution.ts index 624fa992c4c..887ac07354c 100644 --- a/extensions/json/server/src/jsoncontributions/projectJSONContribution.ts +++ b/extensions/json/server/src/jsoncontributions/projectJSONContribution.ts @@ -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; - 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 { + public collectDefaultCompletions(resource: string, result: CompletionsCollector): Thenable { if (this.isProjectJSONFile(resource)) { let defaultValue = { 'version': '{{1.0.0-*}}', @@ -120,7 +117,7 @@ export class ProjectJSONContribution implements IJSONWorkerContribution { } private makeJSONRequest(url: string) : Thenable { - 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 { - 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 { + 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 { - 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 { + 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(queryUrl).then(obj => { @@ -206,9 +203,9 @@ export class ProjectJSONContribution implements IJSONWorkerContribution { return null; } - public getInfoContribution(resource: string, location: JSONLocation): Thenable { - 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 { + if (this.isProjectJSONFile(resource) && (matches(location, ['dependencies', '*']) || matches(location, ['frameworks', '*', 'dependencies', '*']) || matches(location, ['frameworks', '*', 'frameworkAssemblies', '*']))) { + let pack = 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; } \ No newline at end of file diff --git a/extensions/json/server/src/languageModelCache.ts b/extensions/json/server/src/languageModelCache.ts new file mode 100644 index 00000000000..dce68bed9a3 --- /dev/null +++ b/extensions/json/server/src/languageModelCache.ts @@ -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 { + get(document: TextDocument): T; + onDocumentRemoved(document: TextDocument): void; + dispose(): void; +} + +export function getLanguageModelCache(maxEntries: number, cleanupIntervalTimeInSec: number, parse: (document: TextDocument) => T) : LanguageModelCache { + 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; + } + } + }; +} \ No newline at end of file diff --git a/extensions/json/server/src/test/completion.test.ts b/extensions/json/server/src/test/completion.test.ts deleted file mode 100644 index e25fdeb15f5..00000000000 --- a/extensions/json/server/src/test/completion.test.ts +++ /dev/null @@ -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 { - return Promise.reject({ 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 { - 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)); - }); - -}); - diff --git a/extensions/json/server/src/test/documentSymbols.test.ts b/extensions/json/server/src/test/documentSymbols.test.ts deleted file mode 100644 index 83d0eec257d..00000000000 --- a/extensions/json/server/src/test/documentSymbols.test.ts +++ /dev/null @@ -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 { - 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)); - }); - -}); \ No newline at end of file diff --git a/extensions/json/server/src/test/fixtures/Microsoft.Authorization.json b/extensions/json/server/src/test/fixtures/Microsoft.Authorization.json deleted file mode 100644 index ba1fd3fb5dc..00000000000 --- a/extensions/json/server/src/test/fixtures/Microsoft.Authorization.json +++ /dev/null @@ -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" - } - } -} \ No newline at end of file diff --git a/extensions/json/server/src/test/fixtures/Microsoft.Compute.json b/extensions/json/server/src/test/fixtures/Microsoft.Compute.json deleted file mode 100644 index c80631c076e..00000000000 --- a/extensions/json/server/src/test/fixtures/Microsoft.Compute.json +++ /dev/null @@ -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" - ] - } - } -} diff --git a/extensions/json/server/src/test/fixtures/Microsoft.Resources.json b/extensions/json/server/src/test/fixtures/Microsoft.Resources.json deleted file mode 100644 index 3b7bf88044c..00000000000 --- a/extensions/json/server/src/test/fixtures/Microsoft.Resources.json +++ /dev/null @@ -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" - ] - } - } -} \ No newline at end of file diff --git a/extensions/json/server/src/test/fixtures/Microsoft.Sql.json b/extensions/json/server/src/test/fixtures/Microsoft.Sql.json deleted file mode 100644 index 876e1194281..00000000000 --- a/extensions/json/server/src/test/fixtures/Microsoft.Sql.json +++ /dev/null @@ -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" - ] - } - } -} diff --git a/extensions/json/server/src/test/fixtures/Microsoft.Web.json b/extensions/json/server/src/test/fixtures/Microsoft.Web.json deleted file mode 100644 index 7d096e7e930..00000000000 --- a/extensions/json/server/src/test/fixtures/Microsoft.Web.json +++ /dev/null @@ -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 .azurewebsites.net and .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." - } - } - } - } - } -} -} \ No newline at end of file diff --git a/extensions/json/server/src/test/fixtures/SuccessBricks.ClearDB.json b/extensions/json/server/src/test/fixtures/SuccessBricks.ClearDB.json deleted file mode 100644 index cebd060b65e..00000000000 --- a/extensions/json/server/src/test/fixtures/SuccessBricks.ClearDB.json +++ /dev/null @@ -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" - ] - } -} -} diff --git a/extensions/json/server/src/test/fixtures/deploymentParameters.json b/extensions/json/server/src/test/fixtures/deploymentParameters.json deleted file mode 100644 index e9f39c3f76a..00000000000 --- a/extensions/json/server/src/test/fixtures/deploymentParameters.json +++ /dev/null @@ -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" - ] - } - } -} \ No newline at end of file diff --git a/extensions/json/server/src/test/fixtures/deploymentTemplate.json b/extensions/json/server/src/test/fixtures/deploymentTemplate.json deleted file mode 100644 index ed16aafa5d8..00000000000 --- a/extensions/json/server/src/test/fixtures/deploymentTemplate.json +++ /dev/null @@ -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" - } -} -} diff --git a/extensions/json/server/src/test/formatter.test.ts b/extensions/json/server/src/test/formatter.test.ts deleted file mode 100644 index a1e367ecc10..00000000000 --- a/extensions/json/server/src/test/formatter.test.ts +++ /dev/null @@ -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); - }); -}); \ No newline at end of file diff --git a/extensions/json/server/src/test/hover.test.ts b/extensions/json/server/src/test/hover.test.ts deleted file mode 100644 index 8315e6a5cda..00000000000 --- a/extensions/json/server/src/test/hover.test.ts +++ /dev/null @@ -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 { - 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 { - return Promise.reject({ 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)); - }); -}) \ No newline at end of file diff --git a/extensions/json/server/src/test/parser.test.ts b/extensions/json/server/src/test/parser.test.ts deleted file mode 100644 index 3bfd447837c..00000000000 --- a/extensions/json/server/src/test/parser.test.ts +++ /dev/null @@ -1,1265 +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'); - -suite('JSON Parser', () => { - - function isValid(json: string): void { - var result = Parser.parse(json); - assert.equal(result.errors.length, 0); - } - - function isInvalid(json: string): void { - var result = Parser.parse(json); - assert.ok(result.errors.length > 0); - // these should be caught by the parser, not the last-ditch guard - assert.notEqual(result.errors[0].message, 'Invalid JSON'); - } - - - test('Invalid body', function() { - var result = Parser.parse('*'); - assert.equal(result.errors.length, 1); - - isInvalid('{}[]'); - }); - - test('Trailing Whitespace', function() { - isValid('{}\n\n'); - }); - - - test('Objects', function() { - isValid('{}'); - isValid('{"key": "value"}'); - isValid('{"key1": true, "key2": 3, "key3": [null], "key4": { "nested": {}}}'); - - - isInvalid('{'); - isInvalid('{3:3}'); - isInvalid('{\'key\': 3}'); - isInvalid('{"key" 3}'); - isInvalid('{"key":3 "key2": 4}'); - isInvalid('{"key":42, }'); - isInvalid('{"key:42'); - }); - - test('Arrays', function() { - isValid('[]'); - isValid('[1, 2]'); - isValid('[1, "string", false, {}, [null]]'); - - isInvalid('['); - isInvalid('[,]'); - isInvalid('[1 2]'); - isInvalid('[true false]'); - isInvalid('[1, ]'); - isInvalid('[[]'); - isInvalid('["something"'); - isInvalid('[magic]'); - }); - - test('Strings', function() { - isValid('["string"]'); - isValid('["\\"\\\\\\/\\b\\f\\n\\r\\t\\u1234\\u12AB"]'); - isValid('["\\\\"]'); - - isInvalid('["'); - isInvalid('["]'); - isInvalid('["\\z"]'); - isInvalid('["\\u"]'); - isInvalid('["\\u123"]'); - isInvalid('["\\u123Z"]'); - isInvalid('[\'string\']'); - }); - - test('Numbers', function() { - isValid('[0, -1, 186.1, 0.123, -1.583e+4, 1.583E-4, 5e8]'); - - isInvalid('[+1]'); - isInvalid('[01]'); - isInvalid('[1.]'); - isInvalid('[1.1+3]'); - isInvalid('[1.4e]'); - isInvalid('[-A]'); - }); - - test('Simple AST', function() { - - var result = Parser.parse('{}'); - - assert.strictEqual(result.errors.length, 0); - - var node = result.getNodeFromOffset(1); - - assert.equal(node.type, 'object'); - assert.deepEqual(node.getNodeLocation().getSegments(), []); - - assert.strictEqual(result.getNodeFromOffset(2), null); - - result = Parser.parse('[null]'); - assert.strictEqual(result.errors.length, 0); - - node = result.getNodeFromOffset(2); - - assert.equal(node.type, 'null'); - assert.deepEqual(node.getNodeLocation().getSegments(), ['0']); - - result = Parser.parse('{"a":true}'); - assert.strictEqual(result.errors.length, 0); - - node = result.getNodeFromOffset(3); - - assert.equal(node.type, 'string'); - assert.equal((node).isKey, true); - assert.deepEqual(node.getNodeLocation().getSegments(), ['a']); - - node = result.getNodeFromOffset(4); - - assert.equal(node.type, 'property'); - - node = result.getNodeFromOffset(0); - - assert.equal(node.type, 'object'); - - node = result.getNodeFromOffset(10); - - assert.equal(node, null); - - node = result.getNodeFromOffset(5); - - assert.equal(node.type, 'boolean'); - assert.deepEqual(node.getNodeLocation().getSegments(), ['a']); - - }); - - test('Nested AST', function() { - - var content = '{\n\t"key" : {\n\t"key2": 42\n\t}\n}'; - var result = Parser.parse(content); - - assert.strictEqual(result.errors.length, 0); - - var node = result.getNodeFromOffset(content.indexOf('key2') + 2); - var location = node.getNodeLocation(); - - assert.deepEqual(location.getSegments(), ['key', 'key2']); - - node = result.getNodeFromOffset(content.indexOf('42') + 1); - location = node.getNodeLocation(); - - assert.deepEqual(location.getSegments(), ['key', 'key2']); - }); - - test('Nested AST in Array', function() { - - var result = Parser.parse('{"key":[{"key2":42}]}'); - - assert.strictEqual(result.errors.length, 0); - - var node = result.getNodeFromOffset(17); - var location = node.getNodeLocation(); - - assert.deepEqual(location.getSegments(), ['key', '0', 'key2']); - - }); - - test('Multiline', function() { - - var content = '{\n\t\n}'; - var result = Parser.parse(content); - - assert.strictEqual(result.errors.length, 0); - - var node = result.getNodeFromOffset(content.indexOf('\t') + 1); - - assert.notEqual(node, null); - - content = '{\n"first":true\n\n}'; - result = Parser.parse(content); - - node = result.getNodeFromOffset(content.length - 2); - assert.equal(node.type, 'object'); - - node = result.getNodeFromOffset(content.length - 4); - assert.equal(node.type, 'boolean'); - }); - - test('Expand errors to entire tokens', function() { - - var content = '{\n"key":32,\nerror\n}'; - var result = Parser.parse(content); - assert.equal(result.errors.length, 1); - assert.equal(result.errors[0].location.start, content.indexOf('error')); - assert.equal(result.errors[0].location.end, content.indexOf('error') + 5); - }); - - test('Getting keys out of an object', function() { - - var content = '{\n"key":32,\n\n"key2":45}'; - var result = Parser.parse(content); - assert.equal(result.errors.length, 0); - var node = result.getNodeFromOffset(content.indexOf('32,\n') + 4); - - assert.equal(node.type, 'object'); - var keyList = (node).getKeyList(); - assert.deepEqual(keyList, ['key', 'key2']); - }); - - test('Validate types', function() { - - var str = '{"number": 3.4, "integer": 42, "string": "some string", "boolean":true, "null":null, "object":{}, "array":[1, 2]}'; - var result = Parser.parse(str); - - assert.strictEqual(result.errors.length, 0); - - result.validate({ - type: 'object' - }); - - assert.strictEqual(result.warnings.length, 0); - - result = Parser.parse(str); - result.validate({ - type: 'array' - }); - - assert.strictEqual(result.warnings.length, 1); - - result = Parser.parse(str); - - result.validate({ - type: 'object', - properties: { - "number": { - type: 'number' - }, - "integer": { - type: 'integer' - }, - "string": { - type: 'string' - }, - "boolean": { - type: 'boolean' - }, - "null": { - type: 'null' - }, - "object": { - type: 'object' - }, - "array": { - type: 'array' - } - } - }); - - assert.strictEqual(result.warnings.length, 0); - - result = Parser.parse(str); - result.validate({ - type: 'object', - properties: { - "number": { - type: 'array' - }, - "integer": { - type: 'string' - }, - "string": { - type: 'object' - }, - "boolean": { - type: 'null' - }, - "null": { - type: 'integer' - }, - "object": { - type: 'boolean' - }, - "array": { - type: 'number' - } - } - }); - - assert.strictEqual(result.warnings.length, 7); - - result = Parser.parse(str); - result.validate({ - type: 'object', - properties: { - "number": { - type: 'integer' - }, - } - }); - - assert.strictEqual(result.warnings.length, 1); - - result = Parser.parse(str); - result.validate({ - type: 'object', - properties: { - "integer": { - type: 'number' - }, - } - }); - - assert.strictEqual(result.warnings.length, 0); - - result = Parser.parse(str); - result.validate({ - type: 'object', - properties: { - "array": { - type: 'array', - items: { - type: 'integer' - } - }, - } - }); - - assert.strictEqual(result.warnings.length, 0); - - result = Parser.parse(str); - result.validate({ - type: 'object', - properties: { - "array": { - type: 'array', - items: { - type: 'string' - } - }, - } - }); - - assert.strictEqual(result.warnings.length, 2); - }); - - test('Required properties', function() { - - var result = Parser.parse('{"integer": 42, "string": "some string", "boolean":true}'); - - assert.strictEqual(result.errors.length, 0); - - result.validate({ - type: 'object', - required: ['string'] - }); - - assert.strictEqual(result.warnings.length, 0); - - result = Parser.parse('{"integer": 42, "string": "some string", "boolean":true}'); - result.validate({ - type: 'object', - required: ['notpresent'] - }); - - assert.strictEqual(result.warnings.length, 1); - }); - - test('Arrays', function() { - - var result = Parser.parse('[1, 2, 3]'); - - assert.strictEqual(result.errors.length, 0); - - result.validate({ - type: 'array', - items: { - type: 'number' - }, - minItems: 1, - maxItems: 5 - }); - - assert.strictEqual(result.warnings.length, 0); - - result = Parser.parse('[1, 2, 3]'); - result.validate({ - type: 'array', - items: { - type: 'number' - }, - minItems: 10 - }); - - assert.strictEqual(result.warnings.length, 1); - - result = Parser.parse('[1, 2, 3]'); - result.validate({ - type: 'array', - items: { - type: 'number' - }, - maxItems: 2 - }); - - assert.strictEqual(result.warnings.length, 1); - - }); - - test('Strings', function() { - - var result = Parser.parse('{"one":"test"}'); - - assert.strictEqual(result.errors.length, 0); - - result.validate({ - type: 'object', - properties: { - "one": { - type: 'string', - minLength: 1, - maxLength: 10 - } - } - }); - - assert.strictEqual(result.warnings.length, 0); - - result = Parser.parse('{"one":"test"}'); - result.validate({ - type: 'object', - properties: { - "one": { - type: 'string', - minLength: 10, - } - } - }); - - assert.strictEqual(result.warnings.length, 1); - - result = Parser.parse('{"one":"test"}'); - result.validate({ - type: 'object', - properties: { - "one": { - type: 'string', - maxLength: 3, - } - } - }); - - assert.strictEqual(result.warnings.length, 1); - - result = Parser.parse('{"one":"test"}'); - result.validate({ - type: 'object', - properties: { - "one": { - type: 'string', - pattern: '^test$' - } - } - }); - - assert.strictEqual(result.warnings.length, 0); - - result = Parser.parse('{"one":"test"}'); - result.validate({ - type: 'object', - properties: { - "one": { - type: 'string', - pattern: 'fail' - } - } - }); - - assert.strictEqual(result.warnings.length, 1); - - }); - - test('Numbers', function() { - - var result = Parser.parse('{"one": 13.45e+1}'); - - assert.strictEqual(result.errors.length, 0); - - result.validate({ - type: 'object', - properties: { - "one": { - type: 'number', - minimum: 1, - maximum: 135 - } - } - }); - - assert.strictEqual(result.warnings.length, 0); - - result = Parser.parse('{"one": 13.45e+1}'); - - result.validate({ - type: 'object', - properties: { - "one": { - type: 'number', - minimum: 200, - } - } - }); - - assert.strictEqual(result.warnings.length, 1, 'below minimum'); - - result = Parser.parse('{"one": 13.45e+1}'); - result.validate({ - type: 'object', - properties: { - "one": { - type: 'number', - maximum: 130, - } - } - }); - - assert.strictEqual(result.warnings.length, 1, 'above maximum'); - - result = Parser.parse('{"one": 13.45e+1}'); - result.validate({ - type: 'object', - properties: { - "one": { - type: 'number', - minimum: 134.5, - exclusiveMinimum: true - } - } - }); - - assert.strictEqual(result.warnings.length, 1, 'at exclusive mininum'); - - result = Parser.parse('{"one": 13.45e+1}'); - result.validate({ - type: 'object', - properties: { - "one": { - type: 'number', - maximum: 134.5, - exclusiveMaximum: true - } - } - }); - - assert.strictEqual(result.warnings.length, 1, 'at exclusive maximum'); - - result = Parser.parse('{"one": 13.45e+1}'); - result.validate({ - type: 'object', - properties: { - "one": { - type: 'number', - minimum: 134.5, - maximum: 134.5 - } - } - }); - - assert.strictEqual(result.warnings.length, 0, 'equal to min and max'); - }); - - test('getNodeFromOffset', function() { - var content = '{"a": 1,\n\n"d": 2}'; - var doc = Parser.parse(content); - - assert.strictEqual(doc.errors.length, 0); - - var node = doc.getNodeFromOffset(content.indexOf(': 2') + 1); - - assert.strictEqual(node.type, 'property'); - }); - - - test('Duplicate keys', function() { - var doc = Parser.parse('{"a": 1, "a": 2}'); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 1, 'Keys should not be the same'); - - var doc = Parser.parse('{"a": { "a": 2, "a": 3}}'); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 1, 'Keys should not be the same'); - - var doc = Parser.parse('[{ "a": 2, "a": 3}]'); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 1, 'Keys should not be the same'); - - }); - - test('allOf', function() { - - var doc = Parser.parse('{"prop1": 42, "prop2": true}'); - - var schema: JsonSchema.IJSONSchema = { - id: 'main', - allOf: [ - { - type: 'object' - }, - { - properties: { - 'prop1': { - type: 'number' - } - } - }, - { - properties: { - 'prop2': { - type: 'boolean' - } - } - } - - ] - }; - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - - doc = Parser.parse('{"prop1": 42, "prop2": 123}'); - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 1); - }); - - test('anyOf', function() { - - var doc = Parser.parse('{"prop1": 42, "prop2": true}'); - - var schema: JsonSchema.IJSONSchema = { - id: 'main', - anyOf: [ - { - properties: { - 'prop1': { - type: 'number' - } - } - }, - { - properties: { - 'prop2': { - type: 'boolean' - } - } - } - - ] - }; - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - - doc = Parser.parse('{"prop1": 42, "prop2": 123}'); - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - - doc = Parser.parse('{"prop1": "a string", "prop2": 123}'); - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 1); - }); - - test('oneOf', function() { - - var doc = Parser.parse('{"prop1": 42, "prop2": true}'); - - var schema: JsonSchema.IJSONSchema = { - id: 'main', - oneOf: [ - { - properties: { - 'prop1': { - type: 'number' - } - } - }, - { - properties: { - 'prop2': { - type: 'boolean' - } - } - } - - ] - }; - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 1); - - doc = Parser.parse('{"prop1": 42, "prop2": 123}'); - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - - doc = Parser.parse('{"prop1": "a string", "prop2": 123}'); - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 1); - }); - - - test('not', function() { - - var doc = Parser.parse('{"prop1": 42, "prop2": true}'); - - var schema: JsonSchema.IJSONSchema = { - id: 'main', - not: { - properties: { - 'prop1': { - type: 'number' - } - } - } - - }; - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 1); - - doc = Parser.parse('{"prop1": "test"}'); - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - }); - - test('minProperties', function() { - - var doc = Parser.parse('{"prop1": 42, "prop2": true}'); - - var schema: JsonSchema.IJSONSchema = { - minProperties: 2 - }; - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - - schema.minProperties = 1; - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - - schema.minProperties = 3; - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 1); - }); - - test('maxProperties', function() { - - var doc = Parser.parse('{"prop1": 42, "prop2": true}'); - - var schema: JsonSchema.IJSONSchema = { - maxProperties: 2 - }; - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - - schema.maxProperties = 3; - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - - schema.maxProperties = 1; - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 1); - }); - - test('patternProperties', function() { - - var doc = Parser.parse('{"prop1": 42, "prop2": 42}'); - - var schema: JsonSchema.IJSONSchema = { - id: 'main', - patternProperties: { - '^prop\\d$': { - type: 'number' - } - } - }; - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - - doc = Parser.parse('{"prop1": 42, "prop2": true}'); - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 1); - - doc = Parser.parse('{"prop1": 42, "prop2": 123, "aprop3": true}'); - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - }); - - test('additionalProperties', function() { - - var doc = Parser.parse('{"prop1": 42, "prop2": 42}'); - - var schema: JsonSchema.IJSONSchema = { - additionalProperties: { - type: 'number' - } - }; - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - - doc = Parser.parse('{"prop1": 42, "prop2": true}'); - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 1); - - schema = { - properties: { - "prop1": { - type: 'boolean' - } - }, - additionalProperties: { - type: 'number' - } - }; - - doc = Parser.parse('{"prop1": true, "prop2": 42}'); - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - - schema = { - properties: { - "prop1": { - type: 'boolean' - } - }, - additionalProperties: false - }; - - doc = Parser.parse('{"prop1": true, "prop2": 42}'); - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 1); - - doc = Parser.parse('{"prop1": true}'); - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - }); - - //only support enums for string and number values - test('enum', function() { - - var doc = Parser.parse('{"prop": "harmonica"}'); - - var schema: JsonSchema.IJSONSchema = { - properties: { - 'prop': { - enum: ['violin', 'harmonica', 'banjo'] - } - } - }; - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - - doc = Parser.parse('{"prop": "harp"}'); - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 1); - - schema = { - properties: { - 'prop': { - enum: [1, 42, 999] - } - } - }; - - doc = Parser.parse('{"prop": 42}'); - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - - doc = Parser.parse('{"prop": 1337}'); - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 1); - }); - - test('uniqueItems', function() { - - var doc = Parser.parse('[1, 2, 3]'); - - var schema: JsonSchema.IJSONSchema = { - type: 'array', - uniqueItems: true - }; - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - - doc = Parser.parse('[1, 2, 3, 2]'); - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 1); - - doc = Parser.parse('[1, 2, "string", 52, "string"]'); - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 1); - }); - - test('items as array', function() { - - var doc = Parser.parse('[1, true, "string"]'); - - var schema: JsonSchema.IJSONSchema = { - type: 'array', - items: [ - { - type: 'integer' - }, - { - type: 'boolean' - }, - { - type: 'string' - } - ] - }; - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - - doc = Parser.parse('["string", 1, true]'); - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 3); - - doc = Parser.parse('[1, true, "string", "another", 42]'); - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - }); - - test('additionalItems', function() { - - var doc = Parser.parse('[1, true, "string"]'); - - var schema: JsonSchema.IJSONSchema = { - type: 'array', - items: [ - { - type: 'integer' - }, - { - type: 'boolean' - }, - { - type: 'string' - } - ], - additionalItems: false - }; - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - - doc = Parser.parse('[1, true, "string", 42]'); - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 1); - }); - - test('multipleOf', function() { - - var doc = Parser.parse('[42]'); - - var schema: JsonSchema.IJSONSchema = { - type: 'array', - items: { - type: 'integer', - multipleOf: 2 - } - }; - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - - doc = Parser.parse('[43]'); - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 1); - }); - - test('dependencies with array', function() { - - var doc = Parser.parse('{"a":true, "b":42}'); - - var schema: JsonSchema.IJSONSchema = { - type: 'object', - properties: { - a: { - type: 'boolean' - } - }, - dependencies: { - a: ['b'] - } - }; - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - - doc = Parser.parse('{}'); - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - - doc = Parser.parse('{"a":true}'); - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 1); - }); - - test('dependencies with schema', function() { - - var doc = Parser.parse('{"a":true, "b":42}'); - - var schema: JsonSchema.IJSONSchema = { - type: 'object', - properties: { - a: { - type: 'boolean' - } - }, - dependencies: { - a: { - properties: { - b: { - type: 'integer' - } - } - } - } - }; - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - - doc = Parser.parse('{}'); - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - - doc = Parser.parse('{"a":true}'); - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - - doc = Parser.parse('{"a":true, "b": "string"}'); - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 1); - }); - - test('type as array', function() { - - var doc = Parser.parse('{"prop": 42}'); - - var schema: JsonSchema.IJSONSchema = { - type: 'object', - properties: { - 'prop': { - type: ['number', 'string'] - } - } - }; - - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - - doc = Parser.parse('{"prop": "string"}'); - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 0); - - doc = Parser.parse('{"prop": true}'); - doc.validate(schema); - - assert.strictEqual(doc.errors.length, 0); - assert.strictEqual(doc.warnings.length, 1); - }); - - test('Strings with spaces', function() { - - var result = Parser.parse('{"key1":"first string", "key2":["second string"]}'); - assert.strictEqual(result.errors.length, 0); - - var node = result.getNodeFromOffset(9); - assert.strictEqual(node.getValue(), 'first string'); - - node = result.getNodeFromOffset(34); - assert.strictEqual(node.getValue(), 'second string'); - - }); - - test('Schema information on node', function() { - - var result = Parser.parse('{"key":42}'); - assert.strictEqual(result.errors.length, 0); - - var schema: JsonSchema.IJSONSchema = { - type: 'object', - properties: { - 'key': { - oneOf: [{ - type: 'number', - description: 'this is a number' - }, { - type: 'string', - description: 'this is a string' - }] - } - } - }; - - var matchingSchemas: Parser.IApplicableSchema[] = []; - result.validate(schema, matchingSchemas); - - var node = result.getNodeFromOffset(7); - assert.strictEqual(node.type, 'number'); - assert.strictEqual(node.getValue(), 42); - - var schemas = matchingSchemas.filter((s) => s.node === node && !s.inverted).map((s) => s.schema); - - assert.ok(Array.isArray(schemas)); - // 0 is the most specific schema, - // 1 is the schema that contained the "oneOf" clause, - assert.strictEqual(schemas.length, 2); - assert.strictEqual(schemas[0].description, 'this is a number'); - }); - - test('parse with comments', function() { - - function parse(v: string): T { - var result = Parser.parse(v); - assert.equal(result.errors.length, 0); - return result.root.getValue(); - } - - var value = parse<{ far: string; }>('// comment\n{\n"far": "boo"\n}'); - assert.equal(value.far, 'boo'); - - var value = parse<{ far: string; }>('/* comm\nent\nent */\n{\n"far": "boo"\n}'); - assert.equal(value.far, 'boo'); - - var value = parse<{ far: string; }>('{\n"far": "boo"\n}'); - assert.equal(value.far, 'boo'); - - }); - -}); diff --git a/extensions/json/server/src/test/schema.test.ts b/extensions/json/server/src/test/schema.test.ts deleted file mode 100644 index b2f145c1fc2..00000000000 --- a/extensions/json/server/src/test/schema.test.ts +++ /dev/null @@ -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 { - 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((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({ 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); - }); - - }); - -}); diff --git a/extensions/json/server/src/test/textEditSupport.ts b/extensions/json/server/src/test/textEditSupport.ts deleted file mode 100644 index d3bc683820d..00000000000 --- a/extensions/json/server/src/test/textEditSupport.ts +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/extensions/json/server/test/mocha.opts b/extensions/json/server/test/mocha.opts deleted file mode 100644 index 710cf36a9f0..00000000000 --- a/extensions/json/server/test/mocha.opts +++ /dev/null @@ -1,3 +0,0 @@ ---ui tdd ---useColors true -./out/test