diff --git a/src/vs/languages/json/common/contributions/bowerJSONContribution.ts b/src/vs/languages/json/common/contributions/bowerJSONContribution.ts index 38c36f55fb1d06dddec79d4abcf6c75b2caf8b05..30ae8b4d20dfa2e3bdb33527c1691ca8c72055b7 100644 --- a/src/vs/languages/json/common/contributions/bowerJSONContribution.ts +++ b/src/vs/languages/json/common/contributions/bowerJSONContribution.ts @@ -10,6 +10,8 @@ import WinJS = require('vs/base/common/winjs.base'); import nls = require('vs/nls'); import JSONWorker = require('vs/languages/json/common/jsonWorker'); import {IRequestService} from 'vs/platform/request/common/request'; +import URI from 'vs/base/common/uri'; +import {JSONLocation} from 'vs/languages/json/common/parser/jsonLocation'; export class BowerJSONContribution implements JSONWorker.IJSONWorkerContribution { @@ -27,8 +29,13 @@ export class BowerJSONContribution implements JSONWorker.IJSONWorkerContribution this.requestService = requestService; } - public collectDefaultSuggestions(contributionId: string, result: JSONWorker.ISuggestionsCollector): WinJS.Promise { - if (contributionId === 'http://json.schemastore.org/bower') { + private isBowerFile(resource: URI): boolean { + var path = resource.path; + return Strings.endsWith(path, '/bower.json') || Strings.endsWith(path, '/.bower.json'); + } + + public collectDefaultSuggestions(resource: URI, result: JSONWorker.ISuggestionsCollector): WinJS.Promise { + if (this.isBowerFile(resource)) { var defaultValue = { 'name': '{{name}}', 'description': '{{description}}', @@ -39,11 +46,11 @@ export class BowerJSONContribution implements JSONWorker.IJSONWorkerContribution }; result.add({ type: 'type', label: nls.localize('json.bower.default', 'Default bower.json'), codeSnippet: JSON.stringify(defaultValue, null, '\t'), documentationLabel: '' }); } - return WinJS.Promise.as(0); + return null; } - public collectPropertySuggestions(contributionId: string, currentWord: string, addValue: boolean, isLast:boolean, result: JSONWorker.ISuggestionsCollector) : WinJS.Promise { - if (contributionId === 'bower-packages') { + public collectPropertySuggestions(resource: URI, location: JSONLocation, currentWord: string, addValue: boolean, isLast:boolean, result: JSONWorker.ISuggestionsCollector) : WinJS.Promise { + if (this.isBowerFile(resource) && (location.matches(['dependencies']) || location.matches(['devDependencies']))) { if (currentWord.length > 0) { var queryUrl = 'https://bower.herokuapp.com/packages/search/' + encodeURIComponent(currentWord); @@ -94,16 +101,17 @@ export class BowerJSONContribution implements JSONWorker.IJSONWorkerContribution result.setAsIncomplete(); } } - return WinJS.Promise.as(0); + return null; } - public collectValueSuggestions(contributionId: string, currentKey: string, result: JSONWorker.ISuggestionsCollector): WinJS.Promise { + public collectValueSuggestions(resource: URI, location: JSONLocation, currentKey: string, result: JSONWorker.ISuggestionsCollector): WinJS.Promise { // not implemented. Could be do done calling the bower command. Waiting for web API: https://github.com/bower/registry/issues/26 - return WinJS.Promise.as(0); + return null; } - public getInfoContribution(contributionId: string, pack: string): WinJS.TPromise { - if (contributionId === 'bower-package') { + public getInfoContribution(resource: URI, location: JSONLocation): WinJS.TPromise { + if (this.isBowerFile(resource) && (location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']))) { + var pack = location.getSegments()[location.getSegments().length - 1]; var htmlContent : HtmlContent.IHTMLContentElement[] = []; htmlContent.push({className: 'type', text: nls.localize('json.bower.package.hover', '{0}', pack) }); diff --git a/src/vs/languages/json/common/contributions/globPatternContribution.ts b/src/vs/languages/json/common/contributions/globPatternContribution.ts index c2be4eed7e236711e761b8d6646bb083adb74d4d..139fc9938777cf4dfd5b9c737cf1931243ec8a7b 100644 --- a/src/vs/languages/json/common/contributions/globPatternContribution.ts +++ b/src/vs/languages/json/common/contributions/globPatternContribution.ts @@ -5,12 +5,15 @@ 'use strict'; import HtmlContent = require('vs/base/common/htmlContent'); +import Strings = require('vs/base/common/strings'); import EditorCommon = require('vs/editor/common/editorCommon'); import Modes = require('vs/editor/common/modes'); import WinJS = require('vs/base/common/winjs.base'); import nls = require('vs/nls'); import JSONWorker = require('vs/languages/json/common/jsonWorker'); import {INullService} from 'vs/platform/instantiation/common/instantiation'; +import URI from 'vs/base/common/uri'; +import {JSONLocation} from 'vs/languages/json/common/parser/jsonLocation'; var globProperties:Modes.ISuggestion[] = [ { type: 'value', label: nls.localize('fileLabel', "Files by Extension"), codeSnippet: '"**/*.{{extension}}": true', documentationLabel: nls.localize('fileDescription', "Match all files of a specific file extension.")}, @@ -32,27 +35,34 @@ export class GlobPatternContribution implements JSONWorker.IJSONWorkerContributi constructor(@INullService ns) { } - public collectDefaultSuggestions(contributionId: string, result: JSONWorker.ISuggestionsCollector): WinJS.Promise { - return WinJS.Promise.as(0); + private isSettingsFile(resource: URI): boolean { + var path = resource.path; + return Strings.endsWith(path, '/settings.json'); } - public collectPropertySuggestions(contributionId: string, currentWord: string, addValue: boolean, isLast:boolean, result: JSONWorker.ISuggestionsCollector) : WinJS.Promise { - if (contributionId === 'glob-pattern') { + public collectDefaultSuggestions(resource: URI, result: JSONWorker.ISuggestionsCollector): WinJS.Promise { + return null; + } + + public collectPropertySuggestions(resource: URI, location: JSONLocation, currentWord: string, addValue: boolean, isLast:boolean, result: JSONWorker.ISuggestionsCollector) : WinJS.Promise { + if (this.isSettingsFile(resource) && (location.matches(['files.exclude']) || location.matches(['search.exclude']))) { + globProperties.forEach((e) => result.add(e)); } - return WinJS.Promise.as(0); + return null; } - public collectValueSuggestions(contributionId: string, currentKey: string, result: JSONWorker.ISuggestionsCollector): WinJS.Promise { - if (contributionId === 'glob-pattern') { + public collectValueSuggestions(resource: URI, location: JSONLocation, currentKey: string, result: JSONWorker.ISuggestionsCollector): WinJS.Promise { + if (this.isSettingsFile(resource) && (location.matches(['files.exclude']) || location.matches(['search.exclude']))) { + globValues.forEach((e) => result.add(e)); } - return WinJS.Promise.as(0); + return null; } - public getInfoContribution(contributionId: string, pack: string): WinJS.TPromise { + public getInfoContribution(resource: URI, location: JSONLocation): WinJS.TPromise { return null; } } \ No newline at end of file diff --git a/src/vs/languages/json/common/contributions/packageJSONContribution.ts b/src/vs/languages/json/common/contributions/packageJSONContribution.ts index b2bfce5e24cf4c7fe0f322c8eaf72d00d5e732ad..df1167602c374621ee60afdfb38e77151692e153 100644 --- a/src/vs/languages/json/common/contributions/packageJSONContribution.ts +++ b/src/vs/languages/json/common/contributions/packageJSONContribution.ts @@ -5,10 +5,13 @@ 'use strict'; import HtmlContent = require('vs/base/common/htmlContent'); +import Strings = require('vs/base/common/strings'); import WinJS = require('vs/base/common/winjs.base'); import nls = require('vs/nls'); import JSONWorker = require('vs/languages/json/common/jsonWorker'); import {IRequestService} from 'vs/platform/request/common/request'; +import URI from 'vs/base/common/uri'; +import {JSONLocation} from 'vs/languages/json/common/parser/jsonLocation'; var LIMIT = 40; @@ -23,12 +26,17 @@ export class PackageJSONContribution implements JSONWorker.IJSONWorkerContributi private requestService : IRequestService; + private isPackageJSONFile(resource: URI): boolean { + var path = resource.path; + return Strings.endsWith(path, '/package.json'); + } + public constructor(@IRequestService requestService: IRequestService) { this.requestService = requestService; } - public collectDefaultSuggestions(contributionId: string, result: JSONWorker.ISuggestionsCollector): WinJS.Promise { - if (contributionId === 'http://json.schemastore.org/package') { + public collectDefaultSuggestions(resource: URI, result: JSONWorker.ISuggestionsCollector): WinJS.Promise { + if (this.isPackageJSONFile(resource)) { var defaultValue = { 'name': '{{name}}', 'description': '{{description}}', @@ -39,11 +47,11 @@ export class PackageJSONContribution implements JSONWorker.IJSONWorkerContributi }; result.add({ type: 'module', label: nls.localize('json.package.default', 'Default package.json'), codeSnippet: JSON.stringify(defaultValue, null, '\t'), documentationLabel: '' }); } - return WinJS.Promise.as(0); + return null; } - public collectPropertySuggestions(contributionId: string, currentWord: string, addValue: boolean, isLast:boolean, result: JSONWorker.ISuggestionsCollector) : WinJS.Promise { - if (contributionId === 'npm-packages') { + public collectPropertySuggestions(resource: URI, location: JSONLocation, currentWord: string, addValue: boolean, isLast:boolean, result: JSONWorker.ISuggestionsCollector) : WinJS.Promise { + if (this.isPackageJSONFile(resource) && (location.matches(['dependencies']) || location.matches(['devDependencies']) || location.matches(['optionalDependencies']) || location.matches(['peerDependencies']))) { var queryUrl : string; if (currentWord.length > 0) { queryUrl = 'https://skimdb.npmjs.com/registry/_design/app/_view/browseAll?group_level=1&limit=' + LIMIT + '&start_key=%5B%22' + encodeURIComponent(currentWord) + '%22%5D&end_key=%5B%22'+ encodeURIComponent(currentWord + 'z') + '%22,%7B%7D%5D'; @@ -99,11 +107,11 @@ export class PackageJSONContribution implements JSONWorker.IJSONWorkerContributi result.setAsIncomplete(); } } - return WinJS.Promise.as(0); + return null; } - public collectValueSuggestions(contributionId: string, currentKey: string, result: JSONWorker.ISuggestionsCollector): WinJS.Promise { - if (contributionId === 'npm-packages') { + public collectValueSuggestions(resource: URI, location: JSONLocation, currentKey: string, result: JSONWorker.ISuggestionsCollector): WinJS.Promise { + if (this.isPackageJSONFile(resource) && (location.matches(['dependencies']) || location.matches(['devDependencies']) || location.matches(['optionalDependencies']) || location.matches(['peerDependencies']))) { var queryUrl = 'http://registry.npmjs.org/' + encodeURIComponent(currentKey) + '/latest'; return this.requestService.makeRequest({ @@ -128,11 +136,13 @@ export class PackageJSONContribution implements JSONWorker.IJSONWorkerContributi return 0; }); } - return WinJS.Promise.as(0); + return null; } - public getInfoContribution(contributionId: string, pack: string): WinJS.TPromise { - if (contributionId === 'npm-package') { + public getInfoContribution(resource: URI, location: JSONLocation): WinJS.TPromise { + if (this.isPackageJSONFile(resource) && (location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']) || location.matches(['optionalDependencies', '*']) || location.matches(['peerDependencies', '*']))) { + var pack = location.getSegments()[location.getSegments().length - 1]; + var htmlContent : HtmlContent.IHTMLContentElement[] = []; htmlContent.push({className: 'type', text: nls.localize('json.npm.package.hover', '{0}', pack) }); diff --git a/src/vs/languages/json/common/contributions/projectJSONContribution.ts b/src/vs/languages/json/common/contributions/projectJSONContribution.ts index 6d1200a5478bd7d5315a7442f905c20d37bf76b3..7e1f877930c5944dbaf11400d8c40b96b96ea53b 100644 --- a/src/vs/languages/json/common/contributions/projectJSONContribution.ts +++ b/src/vs/languages/json/common/contributions/projectJSONContribution.ts @@ -5,10 +5,13 @@ 'use strict'; import HtmlContent = require('vs/base/common/htmlContent'); +import Strings = require('vs/base/common/strings'); import WinJS = require('vs/base/common/winjs.base'); import nls = require('vs/nls'); import JSONWorker = require('vs/languages/json/common/jsonWorker'); import {IRequestService} from 'vs/platform/request/common/request'; +import {JSONLocation} from 'vs/languages/json/common/parser/jsonLocation'; +import URI from 'vs/base/common/uri'; var LIMIT = 40; @@ -20,8 +23,13 @@ export class ProjectJSONContribution implements JSONWorker.IJSONWorkerContributi this.requestService = requestService; } - public collectDefaultSuggestions(contributionId: string, result: JSONWorker.ISuggestionsCollector): WinJS.Promise { - if (contributionId === 'http://json.schemastore.org/project') { + private isProjectJSONFile(resource: URI): boolean { + var path = resource.path; + return Strings.endsWith(path, '/project.json'); + } + + public collectDefaultSuggestions(resource: URI, result: JSONWorker.ISuggestionsCollector): WinJS.Promise { + if (this.isProjectJSONFile(resource)) { var defaultValue = { 'version': '{{1.0.0-*}}', 'dependencies': {}, @@ -32,11 +40,11 @@ export class ProjectJSONContribution implements JSONWorker.IJSONWorkerContributi }; result.add({ type: 'type', label: nls.localize('json.project.default', 'Default project.json'), codeSnippet: JSON.stringify(defaultValue, null, '\t'), documentationLabel: '' }); } - return WinJS.Promise.as(0); + return null; } - public collectPropertySuggestions(contributionId: string, currentWord: string, addValue: boolean, isLast:boolean, result: JSONWorker.ISuggestionsCollector) : WinJS.Promise { - if (contributionId === 'nugget-packages') { + public collectPropertySuggestions(resource: URI, location: JSONLocation, currentWord: string, addValue: boolean, isLast:boolean, result: JSONWorker.ISuggestionsCollector) : WinJS.Promise { + if (this.isProjectJSONFile(resource) && (location.matches(['dependencies']) || location.matches(['frameworks', '*', 'dependencies']) || location.matches(['frameworks', '*', 'frameworkAssemblies']))) { var queryUrl : string; if (currentWord.length > 0) { queryUrl = 'https://www.nuget.org/api/v2/Packages?' @@ -94,11 +102,11 @@ export class ProjectJSONContribution implements JSONWorker.IJSONWorkerContributi return 0; }); } - return WinJS.Promise.as(0); + return null; } - public collectValueSuggestions(contributionId: string, currentKey: string, result: JSONWorker.ISuggestionsCollector): WinJS.Promise { - if (contributionId === 'nugget-packages') { + public collectValueSuggestions(resource: URI, location: JSONLocation, currentKey: string, result: JSONWorker.ISuggestionsCollector): WinJS.Promise { + if (this.isProjectJSONFile(resource) && (location.matches(['dependencies']) || location.matches(['frameworks', '*', 'dependencies']) || location.matches(['frameworks', '*', 'frameworkAssemblies']))) { var queryUrl = 'https://www.myget.org/F/aspnetrelease/api/v2/Packages?' + '$filter=Id%20eq%20\'' + encodeURIComponent(currentKey) @@ -137,12 +145,12 @@ export class ProjectJSONContribution implements JSONWorker.IJSONWorkerContributi return 0; }); } - return WinJS.Promise.as(0); + return null; } - public getInfoContribution(contributionId: string, pack: string): WinJS.TPromise { - - if (contributionId === 'nugget-package') { + public getInfoContribution(resource: URI, location: JSONLocation): WinJS.TPromise { + if (this.isProjectJSONFile(resource) && (location.matches(['dependencies', '*']) || location.matches(['frameworks', '*', 'dependencies', '*']) || location.matches(['frameworks', '*', 'frameworkAssemblies', '*']))) { + var pack = location.getSegments()[location.getSegments().length - 1]; var htmlContent : HtmlContent.IHTMLContentElement[] = []; htmlContent.push({className: 'type', text: nls.localize('json.nugget.package.hover', '{0}', pack) }); diff --git a/src/vs/languages/json/common/jsonIntellisense.ts b/src/vs/languages/json/common/jsonIntellisense.ts index be543416a2163bb0c400b1f65221157cd016c243..96e60b4042d65531682ca5ec8c9f50ad53b76f15 100644 --- a/src/vs/languages/json/common/jsonIntellisense.ts +++ b/src/vs/languages/json/common/jsonIntellisense.ts @@ -104,12 +104,19 @@ export class JSONIntellisense { if (schema) { // property proposals with schema var isLast = properties.length === 0 || offset >= properties[properties.length - 1].start; - - collectionPromises.push(this.getPropertySuggestions(schema, doc, node, currentKey, addValue, isLast, collector)); + this.getPropertySuggestions(resource, schema, doc, node, currentKey, addValue, isLast, collector); } else if (node.parent) { // property proposals without schema - collectionPromises.push(this.getSchemaLessPropertySuggestions(doc, node, collector)); + this.getSchemaLessPropertySuggestions(doc, node, collector); } + + var location = node.getNodeLocation(); + this.contributions.forEach((contribution) => { + var collectPromise = contribution.collectPropertySuggestions(resource, location, currentWord, addValue, isLast, collector); + if (collectPromise) { + collectionPromises.push(); + } + }); } // proposals for values @@ -122,20 +129,46 @@ export class JSONIntellisense { if (schema) { // value proposals with schema - collectionPromises.push(this.getValueSuggestions(schema, doc, node, offset, collector)); + this.getValueSuggestions(resource, schema, doc, node, offset, collector); } else { // value proposals without schema - collectionPromises.push(this.getSchemaLessValueSuggestions(doc, node, offset, modelMirror, collector)); + this.getSchemaLessValueSuggestions(doc, node, offset, modelMirror, collector); + } + if (!node) { + this.contributions.forEach((contribution) => { + var collectPromise = contribution.collectDefaultSuggestions(resource, collector); + if (collectPromise) { + collectionPromises.push(collectPromise); + } + }); + } else { + if ((node.type === 'property') && offset > ( node).colonOffset) { + var parentKey = (node).key.value; + + var valueNode = ( node).value; + if (!valueNode || offset <= valueNode.end) { + var location = node.parent.getNodeLocation(); + this.contributions.forEach((contribution) => { + var collectPromise = contribution.collectValueSuggestions(resource, location, parentKey, collector); + if (collectPromise) { + collectionPromises.push(collectPromise); + } + }); + } + } } + + return WinJS.Promise.join(collectionPromises).then(() => { return result; } ); }); } - private getPropertySuggestions(schema: SchemaService.ResolvedSchema, doc: Parser.JSONDocument, node: Parser.ASTNode, currentWord: string, addValue: boolean, isLast: boolean, collector: JsonWorker.ISuggestionsCollector): WinJS.Promise { + private getPropertySuggestions(resource: URI, schema: SchemaService.ResolvedSchema, doc: Parser.JSONDocument, node: Parser.ASTNode, currentWord: string, addValue: boolean, isLast: boolean, collector: JsonWorker.ISuggestionsCollector): void { var matchingSchemas: Parser.IApplicableSchema[] = []; doc.validate(schema.schema, matchingSchemas, node.start); var collectPromises: WinJS.TPromise[] = []; + matchingSchemas.forEach((s) => { if (s.node === node && !s.inverted) { var schemaProperties = s.schema.properties; @@ -145,19 +178,11 @@ export class JSONIntellisense { collector.add({ type: 'property', label: key, codeSnippet: this.getSnippetForProperty(key, propertySchema, addValue, isLast), documentationLabel: propertySchema.description || '' }); }); } - - var contributionKey = s.schema.id; - if (contributionKey) { - this.contributions.forEach((contribution) => { - collectPromises.push(contribution.collectPropertySuggestions(contributionKey, currentWord, addValue, isLast, collector)); - }); - } } }); - return WinJS.Promise.join(collectPromises); } - private getSchemaLessPropertySuggestions(doc: Parser.JSONDocument, node: Parser.ASTNode, collector: JsonWorker.ISuggestionsCollector): WinJS.Promise { + private getSchemaLessPropertySuggestions(doc: Parser.JSONDocument, node: Parser.ASTNode, collector: JsonWorker.ISuggestionsCollector): void { var collectSuggestionsForSimilarObject = (obj: Parser.ObjectASTNode) => { obj.properties.forEach((p) => { var key = p.key.value; @@ -181,10 +206,9 @@ export class JSONIntellisense { } }); } - return WinJS.Promise.as(0); } - public getSchemaLessValueSuggestions(doc: Parser.JSONDocument, node: Parser.ASTNode, offset: number, modelMirror: EditorCommon.IMirrorModel, collector: JsonWorker.ISuggestionsCollector): WinJS.Promise { + public getSchemaLessValueSuggestions(doc: Parser.JSONDocument, node: Parser.ASTNode, offset: number, modelMirror: EditorCommon.IMirrorModel, collector: JsonWorker.ISuggestionsCollector): void { var collectSuggestionsForValues = (value: Parser.ASTNode) => { var content = this.getMatchingSnippet(value, modelMirror); collector.add({ type: this.getSuggestionType(value.type), label: content, codeSnippet: content, documentationLabel: '' }); @@ -231,24 +255,20 @@ export class JSONIntellisense { } } } - return WinJS.Promise.as(0); } - public getValueSuggestions(schema: SchemaService.ResolvedSchema, doc: Parser.JSONDocument, node: Parser.ASTNode, offset: number, collector: JsonWorker.ISuggestionsCollector) : WinJS.Promise { + public getValueSuggestions(resource: URI, schema: SchemaService.ResolvedSchema, doc: Parser.JSONDocument, node: Parser.ASTNode, offset: number, collector: JsonWorker.ISuggestionsCollector) : void { var collectPromises: WinJS.TPromise[] = []; if (!node) { this.addDefaultSuggestion(schema.schema, collector); - this.contributions.forEach((contribution) => { - collectPromises.push(contribution.collectDefaultSuggestions(schema.schema.id, collector)); - }); } else { var parentKey: string = null; if (node && (node.type === 'property') && offset > ( node).colonOffset) { var valueNode = ( node).value; if (valueNode && offset > valueNode.end) { - return WinJS.Promise.as(0); // we are past the value node + return; // we are past the value node } parentKey = (node).key.value; node = node.parent; @@ -256,6 +276,7 @@ export class JSONIntellisense { if (node && (parentKey !== null || node.type === 'array')) { var 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) { @@ -269,19 +290,11 @@ export class JSONIntellisense { this.addEnumSuggestion(propertySchema, collector); } } - - var contributionKey = s.schema.id; - if (contributionKey) { - this.contributions.forEach((contribution) => { - collectPromises.push(contribution.collectValueSuggestions(contributionKey, parentKey, collector)); - }); - } } }); } } - return WinJS.Promise.join(collectPromises); } private addBooleanSuggestion(value: boolean, collector: JsonWorker.ISuggestionsCollector): void { diff --git a/src/vs/languages/json/common/jsonWorker.ts b/src/vs/languages/json/common/jsonWorker.ts index e811948f91fa770ebb194cb60468c2d4f367cec7..d77fdd2d3b79cf7349c0a394e242749e314355e8 100644 --- a/src/vs/languages/json/common/jsonWorker.ts +++ b/src/vs/languages/json/common/jsonWorker.ts @@ -32,6 +32,7 @@ import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace'; import {ISchemaContributions} from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import {IResourceService} from 'vs/editor/common/services/resourceService'; import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; +import {JSONLocation} from './parser/jsonLocation'; export interface IOptionsSchema { /** @@ -64,10 +65,10 @@ export interface ISuggestionsCollector { } export interface IJSONWorkerContribution { - getInfoContribution(contributionId: string, propertyKey: string) : WinJS.TPromise; - collectPropertySuggestions(contributionId: string, currentWord: string, addValue: boolean, isLast:boolean, result: ISuggestionsCollector) : WinJS.Promise; - collectValueSuggestions(contributionId: string, propertyKey: string, result: ISuggestionsCollector): WinJS.Promise; - collectDefaultSuggestions(contributionId: string, result: ISuggestionsCollector): WinJS.Promise; + getInfoContribution(resource: URI, location: JSONLocation) : WinJS.TPromise; + collectPropertySuggestions(resource: URI, location: JSONLocation, currentWord: string, addValue: boolean, isLast:boolean, result: ISuggestionsCollector) : WinJS.Promise; + collectValueSuggestions(resource: URI, location: JSONLocation, propertyKey: string, result: ISuggestionsCollector): WinJS.Promise; + collectDefaultSuggestions(resource: URI, result: ISuggestionsCollector): WinJS.Promise; } export class JSONWorker extends AbstractModeWorker implements Modes.IExtraInfoSupport { @@ -235,13 +236,12 @@ export class JSONWorker extends AbstractModeWorker implements Modes.IExtraInfoSu return true; }); - if (contributonId && node.name) { - for (var i= this.contributions.length -1; i >= 0; i--) { - var contribution = this.contributions[i]; - var promise = contribution.getInfoContribution(contributonId, node.name); - if (promise) { - return promise.then((htmlContent) => { return this.createInfoResult(htmlContent, originalNode, modelMirror); } ); - } + var location = node.getNodeLocation(); + for (var i= this.contributions.length -1; i >= 0; i--) { + var contribution = this.contributions[i]; + var promise = contribution.getInfoContribution(resource, location); + if (promise) { + return promise.then((htmlContent) => { return this.createInfoResult(htmlContent, originalNode, modelMirror); } ); } } @@ -356,28 +356,28 @@ export class JSONWorker extends AbstractModeWorker implements Modes.IExtraInfoSu } }; - return this.jsonIntellisense.getValueSuggestions(schema, doc, node.parent, node.start, collector).then(() => { - var range = modelMirror.getRangeFromOffsetAndLength(node.start, node.end - node.start); - var text = modelMirror.getValueInRange(range); - for (var i = 0, len = proposals.length; i < len; i++) { - if (Strings.equalsIgnoreCase(proposals[i].label, text)) { - var nextIdx = i; - if (up) { - nextIdx = (i + 1) % len; - } else { - nextIdx = i - 1; - if (nextIdx < 0) { - nextIdx = len - 1; - } + this.jsonIntellisense.getValueSuggestions(resource, schema, doc, node.parent, node.start, collector); + + var range = modelMirror.getRangeFromOffsetAndLength(node.start, node.end - node.start); + var text = modelMirror.getValueInRange(range); + for (var i = 0, len = proposals.length; i < len; i++) { + if (Strings.equalsIgnoreCase(proposals[i].label, text)) { + var nextIdx = i; + if (up) { + nextIdx = (i + 1) % len; + } else { + nextIdx = i - 1; + if (nextIdx < 0) { + nextIdx = len - 1; } - return { - value: proposals[nextIdx].label, - range: range - }; } + return { + value: proposals[nextIdx].label, + range: range + }; } - return null; - }); + } + return null; } }); } diff --git a/src/vs/languages/json/common/parser/jsonLocation.ts b/src/vs/languages/json/common/parser/jsonLocation.ts new file mode 100644 index 0000000000000000000000000000000000000000..93ca426ded0d18e6ba9fbb9d2068b574e18a0ab8 --- /dev/null +++ b/src/vs/languages/json/common/parser/jsonLocation.ts @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * 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[]) { + var k = 0; + for (var 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/src/vs/languages/json/common/parser/jsonParser.ts b/src/vs/languages/json/common/parser/jsonParser.ts index 17f869c867e7b3f7f6c51cb18242aa38e234282d..b17c95157e32cfea5fc5cfeaa203d3c1e1b9c9ea 100644 --- a/src/vs/languages/json/common/parser/jsonParser.ts +++ b/src/vs/languages/json/common/parser/jsonParser.ts @@ -9,6 +9,7 @@ import Arrays = require('vs/base/common/arrays'); import Types = require('vs/base/common/types'); import Json = require('vs/base/common/json'); import JsonSchema = require('vs/base/common/jsonSchema'); +import {JSONLocation} from './jsonLocation'; import SchemaService = require('vs/languages/json/common/jsonSchemaService'); export interface IRange { @@ -36,21 +37,15 @@ export class ASTNode { this.parent = parent; } - public getPath():string[] { - var path:string[] = []; - if (this.parent) { - path = this.parent.getPath(); - - if (this.name) { - path.push(this.name); - } - - return path; - } else { - return path; + public getNodeLocation(): JSONLocation { + var path = this.parent ? this.parent.getNodeLocation() : new JSONLocation([]); + if (this.name) { + path = path.append(this.name); } + return path; } + public getChildNodes(): ASTNode[] { return []; } @@ -925,7 +920,7 @@ export class JSONParser { _scanner.scan(); // consume ColonToken - if (!node.setValue(_parseValue(node, node.key.getValue()))) { + if (!node.setValue(_parseValue(node, key.value))) { return _error(nls.localize('ValueExpected', 'Value expected'), node, [], [Json.SyntaxKind.CloseBraceToken, Json.SyntaxKind.CommaToken]); } node.end = node.value.end; @@ -1013,7 +1008,7 @@ export class JSONParser { _scanner.scan(); - _doc.root = _parseValue(null, 'root'); + _doc.root = _parseValue(null, null); if (!_doc.root) { _error(nls.localize('Invalid symbol', 'Expected a JSON object, array or literal')); } else if (_scanner.getToken() !== Json.SyntaxKind.EOF) { diff --git a/src/vs/languages/json/test/common/parser.test.ts b/src/vs/languages/json/test/common/parser.test.ts index 09ac4551f9cdd3b126b8e9c58a1d23997ffff16b..e6e9a77ed11ad6cb7b427bb3cd9c599f9d5a7905 100644 --- a/src/vs/languages/json/test/common/parser.test.ts +++ b/src/vs/languages/json/test/common/parser.test.ts @@ -104,7 +104,7 @@ suite('JSON - Parsing', () => { var node = result.getNodeFromOffset(1); assert.equal(node.type, 'object'); - assert.deepEqual(node.getPath(), []); + assert.deepEqual(node.getNodeLocation().getSegments(), []); assert.strictEqual(result.getNodeFromOffset(2), null); @@ -114,8 +114,7 @@ suite('JSON - Parsing', () => { node = result.getNodeFromOffset(2); assert.equal(node.type, 'null'); - assert.equal(node.name, '0'); - assert.deepEqual(node.getPath(), ['0']); + assert.deepEqual(node.getNodeLocation().getSegments(), ['0']); result = parser.parse('{"a":true}'); assert.strictEqual(result.errors.length, 0); @@ -124,7 +123,7 @@ suite('JSON - Parsing', () => { assert.equal(node.type, 'string'); assert.equal((node).isKey, true); - assert.deepEqual(node.getPath(), ['a']); + assert.deepEqual(node.getNodeLocation().getSegments(), ['a']); node = result.getNodeFromOffset(4); @@ -141,8 +140,7 @@ suite('JSON - Parsing', () => { node = result.getNodeFromOffset(5); assert.equal(node.type, 'boolean'); - assert.equal(node.name, 'a'); - assert.deepEqual(node.getPath(), ['a']); + assert.deepEqual(node.getNodeLocation().getSegments(), ['a']); }); @@ -155,14 +153,14 @@ suite('JSON - Parsing', () => { assert.strictEqual(result.errors.length, 0); var node = result.getNodeFromOffset(content.indexOf('key2') + 2); - var path = node.getPath(); + var location = node.getNodeLocation(); - assert.deepEqual(path, ['key', 'key2']); + assert.deepEqual(location.getSegments(), ['key', 'key2']); node = result.getNodeFromOffset(content.indexOf('42') + 1); - path = node.getPath(); + location = node.getNodeLocation(); - assert.deepEqual(path, ['key', 'key2']); + assert.deepEqual(location.getSegments(), ['key', 'key2']); }); test('Nested AST in Array', function() { @@ -173,9 +171,9 @@ suite('JSON - Parsing', () => { assert.strictEqual(result.errors.length, 0); var node = result.getNodeFromOffset(17); - var path = node.getPath(); + var location = node.getNodeLocation(); - assert.deepEqual(path, ['key', '0', 'key2']); + assert.deepEqual(location.getSegments(), ['key', '0', 'key2']); });