mirror of
https://github.com/Microsoft/vscode
synced 2024-09-18 01:58:27 +00:00
Move bower/package.json dependency completions to javascript extension
This commit is contained in:
parent
4fe233c752
commit
235cbcdf9d
29
extensions/javascript/.vscode/launch.json
vendored
Normal file
29
extensions/javascript/.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Launch Extension",
|
||||||
|
"type": "extensionHost",
|
||||||
|
"request": "launch",
|
||||||
|
"runtimeExecutable": "${execPath}",
|
||||||
|
"args": [
|
||||||
|
"--extensionDevelopmentPath=${workspaceRoot}"
|
||||||
|
],
|
||||||
|
"stopOnEntry": false,
|
||||||
|
"sourceMaps": true,
|
||||||
|
"outDir": "${workspaceRoot}/out",
|
||||||
|
"preLaunchTask": "npm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Launch Tests",
|
||||||
|
"type": "extensionHost",
|
||||||
|
"request": "launch",
|
||||||
|
"runtimeExecutable": "${execPath}",
|
||||||
|
"args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test" ],
|
||||||
|
"stopOnEntry": false,
|
||||||
|
"sourceMaps": true,
|
||||||
|
"outDir": "${workspaceRoot}/out/test",
|
||||||
|
"preLaunchTask": "npm"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
30
extensions/javascript/.vscode/tasks.json
vendored
Normal file
30
extensions/javascript/.vscode/tasks.json
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// Available variables which can be used inside of strings.
|
||||||
|
// ${workspaceRoot}: the root folder of the team
|
||||||
|
// ${file}: the current opened file
|
||||||
|
// ${fileBasename}: the current opened file's basename
|
||||||
|
// ${fileDirname}: the current opened file's dirname
|
||||||
|
// ${fileExtname}: the current opened file's extension
|
||||||
|
// ${cwd}: the current working directory of the spawned process
|
||||||
|
|
||||||
|
// A task runner that calls a custom npm script that compiles the extension.
|
||||||
|
{
|
||||||
|
"version": "0.1.0",
|
||||||
|
|
||||||
|
// we want to run npm
|
||||||
|
"command": "npm",
|
||||||
|
|
||||||
|
// the command is a shell script
|
||||||
|
"isShellCommand": true,
|
||||||
|
|
||||||
|
// show the output window only if unrecognized errors occur.
|
||||||
|
"showOutput": "silent",
|
||||||
|
|
||||||
|
// we run the custom script "compile" as defined in package.json
|
||||||
|
"args": ["run", "compile"],
|
||||||
|
|
||||||
|
// The tsc compiler is started in watching mode
|
||||||
|
"isWatching": true,
|
||||||
|
|
||||||
|
// use the standard tsc in watch mode problem matcher to find compile problems in the output.
|
||||||
|
"problemMatcher": "$tsc-watch"
|
||||||
|
}
|
|
@ -2,8 +2,19 @@
|
||||||
"name": "javascript",
|
"name": "javascript",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"publisher": "vscode",
|
"publisher": "vscode",
|
||||||
"engines": {
|
"engines": { "vscode": "0.10.x" },
|
||||||
"vscode": "*"
|
"activationEvents": [
|
||||||
|
"onLanguage:javascript", "onLanguage:json"
|
||||||
|
],
|
||||||
|
"main": "./out/javascriptMain",
|
||||||
|
"dependencies": {
|
||||||
|
"vscode-nls": "^1.0.4",
|
||||||
|
"request-light": "^0.0.3",
|
||||||
|
"jsonc-parser": "^0.0.1"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"compile": "gulp compile-extension:javascript",
|
||||||
|
"watch": "gulp watch-extension:javascript"
|
||||||
},
|
},
|
||||||
"contributes": {
|
"contributes": {
|
||||||
"languages": [
|
"languages": [
|
||||||
|
|
177
extensions/javascript/src/features/bowerJSONContribution.ts
Normal file
177
extensions/javascript/src/features/bowerJSONContribution.ts
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 {MarkedString, CompletionItemKind, CompletionItem, DocumentSelector} from 'vscode';
|
||||||
|
import {IJSONContribution, ISuggestionsCollector} from './jsonContributions';
|
||||||
|
import {XHRRequest} from 'request-light';
|
||||||
|
import {Location} from 'jsonc-parser';
|
||||||
|
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
|
export class BowerJSONContribution implements IJSONContribution {
|
||||||
|
|
||||||
|
private topRanked = ['twitter','bootstrap','angular-1.1.6','angular-latest','angulerjs','d3','myjquery','jq','abcdef1234567890','jQuery','jquery-1.11.1','jquery',
|
||||||
|
'sushi-vanilla-x-data','font-awsome','Font-Awesome','font-awesome','fontawesome','html5-boilerplate','impress.js','homebrew',
|
||||||
|
'backbone','moment1','momentjs','moment','linux','animate.css','animate-css','reveal.js','jquery-file-upload','blueimp-file-upload','threejs','express','chosen',
|
||||||
|
'normalize-css','normalize.css','semantic','semantic-ui','Semantic-UI','modernizr','underscore','underscore1',
|
||||||
|
'material-design-icons','ionic','chartjs','Chart.js','nnnick-chartjs','select2-ng','select2-dist','phantom','skrollr','scrollr','less.js','leancss','parser-lib',
|
||||||
|
'hui','bootstrap-languages','async','gulp','jquery-pjax','coffeescript','hammer.js','ace','leaflet','jquery-mobile','sweetalert','typeahead.js','soup','typehead.js',
|
||||||
|
'sails','codeigniter2'];
|
||||||
|
|
||||||
|
public constructor(private xhr: XHRRequest) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public getDocumentSelector(): DocumentSelector {
|
||||||
|
return [{ language: 'json', pattern: '**/bower.json' }, { language: 'json', pattern: '**/.bower.json' }];
|
||||||
|
}
|
||||||
|
|
||||||
|
public collectDefaultSuggestions(resource: string, collector: ISuggestionsCollector): Thenable<any> {
|
||||||
|
let defaultValue = {
|
||||||
|
'name': '{{name}}',
|
||||||
|
'description': '{{description}}',
|
||||||
|
'authors': [ '{{author}}' ],
|
||||||
|
'version': '{{1.0.0}}',
|
||||||
|
'main': '{{pathToMain}}',
|
||||||
|
'dependencies': {}
|
||||||
|
};
|
||||||
|
let proposal = new CompletionItem(localize('json.bower.default', 'Default bower.json'));
|
||||||
|
proposal.kind = CompletionItemKind.Class;
|
||||||
|
proposal.insertText = JSON.stringify(defaultValue, null, '\t');
|
||||||
|
collector.add(proposal);
|
||||||
|
return Promise.resolve(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public collectPropertySuggestions(resource: string, location: Location, currentWord: string, addValue: boolean, isLast:boolean, collector: ISuggestionsCollector) : Thenable<any> {
|
||||||
|
if ((location.matches(['dependencies']) || location.matches(['devDependencies']))) {
|
||||||
|
if (currentWord.length > 0) {
|
||||||
|
let queryUrl = 'https://bower.herokuapp.com/packages/search/' + encodeURIComponent(currentWord);
|
||||||
|
|
||||||
|
return this.xhr({
|
||||||
|
url : queryUrl
|
||||||
|
}).then((success) => {
|
||||||
|
if (success.status === 200) {
|
||||||
|
try {
|
||||||
|
let obj = JSON.parse(success.responseText);
|
||||||
|
if (Array.isArray(obj)) {
|
||||||
|
let results = <{name:string; description:string;}[]> obj;
|
||||||
|
for (let i = 0; i < results.length; i++) {
|
||||||
|
let name = results[i].name;
|
||||||
|
let description = results[i].description || '';
|
||||||
|
let insertText = JSON.stringify(name);
|
||||||
|
if (addValue) {
|
||||||
|
insertText += ': "{{latest}}"';
|
||||||
|
if (!isLast) {
|
||||||
|
insertText += ',';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let proposal = new CompletionItem(name);
|
||||||
|
proposal.kind = CompletionItemKind.Property;
|
||||||
|
proposal.insertText = insertText;
|
||||||
|
proposal.documentation = description;
|
||||||
|
collector.add(proposal);
|
||||||
|
}
|
||||||
|
collector.setAsIncomplete();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
collector.error(localize('json.bower.error.repoaccess', 'Request to the bower repository failed: {0}', success.responseText));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}, (error) => {
|
||||||
|
collector.error(localize('json.bower.error.repoaccess', 'Request to the bower repository failed: {0}', error.responseText));
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.topRanked.forEach((name) => {
|
||||||
|
let insertText = JSON.stringify(name);
|
||||||
|
if (addValue) {
|
||||||
|
insertText += ': "{{latest}}"';
|
||||||
|
if (!isLast) {
|
||||||
|
insertText += ',';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let proposal = new CompletionItem(name);
|
||||||
|
proposal.kind = CompletionItemKind.Property;
|
||||||
|
proposal.insertText = insertText;
|
||||||
|
proposal.documentation = '';
|
||||||
|
collector.add(proposal);
|
||||||
|
});
|
||||||
|
collector.setAsIncomplete();
|
||||||
|
return Promise.resolve(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public collectValueSuggestions(resource: string, location: Location, collector: ISuggestionsCollector): Thenable<any> {
|
||||||
|
// not implemented. Could be do done calling the bower command. Waiting for web API: https://github.com/bower/registry/issues/26
|
||||||
|
let proposal = new CompletionItem(localize('json.bower.latest.version', 'latest'));
|
||||||
|
proposal.insertText = '"{{latest}}"';
|
||||||
|
proposal.kind = CompletionItemKind.Value;
|
||||||
|
proposal.documentation = 'The latest version of the package';
|
||||||
|
collector.add(proposal);
|
||||||
|
return Promise.resolve(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public resolveSuggestion(item: CompletionItem) : Thenable<CompletionItem> {
|
||||||
|
if (item.kind === CompletionItemKind.Property && item.documentation === '') {
|
||||||
|
return this.getInfo(item.label).then(documentation => {
|
||||||
|
if (documentation) {
|
||||||
|
item.documentation = documentation;
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getInfo(pack: string): Thenable<string> {
|
||||||
|
let queryUrl = 'https://bower.herokuapp.com/packages/' + encodeURIComponent(pack);
|
||||||
|
|
||||||
|
return this.xhr({
|
||||||
|
url : queryUrl
|
||||||
|
}).then((success) => {
|
||||||
|
try {
|
||||||
|
let obj = JSON.parse(success.responseText);
|
||||||
|
if (obj && obj.url) {
|
||||||
|
let url : string = obj.url;
|
||||||
|
if (url.indexOf('git://') === 0) {
|
||||||
|
url = url.substring(6);
|
||||||
|
}
|
||||||
|
if (url.lastIndexOf('.git') === url.length - 4) {
|
||||||
|
url = url.substring(0, url.length - 4);
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return void 0;
|
||||||
|
}, (error) => {
|
||||||
|
return void 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getInfoContribution(resource: string, location: Location): Thenable<MarkedString[]> {
|
||||||
|
if ((location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']))) {
|
||||||
|
let pack = location.segments[location.segments.length - 1];
|
||||||
|
let htmlContent : MarkedString[] = [];
|
||||||
|
htmlContent.push(localize('json.bower.package.hover', '{0}', pack));
|
||||||
|
return this.getInfo(pack).then(documentation => {
|
||||||
|
if (documentation) {
|
||||||
|
htmlContent.push(documentation);
|
||||||
|
}
|
||||||
|
return htmlContent;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
154
extensions/javascript/src/features/jsonContributions.ts
Normal file
154
extensions/javascript/src/features/jsonContributions.ts
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 {Location, getLocation, createScanner, SyntaxKind} from 'jsonc-parser';
|
||||||
|
import {basename} from 'path';
|
||||||
|
import {BowerJSONContribution} from './bowerJSONContribution';
|
||||||
|
import {PackageJSONContribution} from './packageJSONContribution';
|
||||||
|
import {XHRRequest} from 'request-light';
|
||||||
|
|
||||||
|
import {CompletionItem, CompletionItemProvider, CompletionList, TextDocument, Position, Hover, HoverProvider,
|
||||||
|
CancellationToken, Range, TextEdit, MarkedString, DocumentSelector, languages} from 'vscode';
|
||||||
|
|
||||||
|
export interface ISuggestionsCollector {
|
||||||
|
add(suggestion: CompletionItem): void;
|
||||||
|
error(message:string): void;
|
||||||
|
log(message:string): void;
|
||||||
|
setAsIncomplete(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IJSONContribution {
|
||||||
|
getDocumentSelector(): DocumentSelector;
|
||||||
|
getInfoContribution(fileName: string, location: Location) : Thenable<MarkedString[]>;
|
||||||
|
collectPropertySuggestions(fileName: string, location: Location, currentWord: string, addValue: boolean, isLast:boolean, result: ISuggestionsCollector) : Thenable<any>;
|
||||||
|
collectValueSuggestions(fileName: string, location: Location, result: ISuggestionsCollector): Thenable<any>;
|
||||||
|
collectDefaultSuggestions(fileName: string, result: ISuggestionsCollector): Thenable<any>;
|
||||||
|
resolveSuggestion?(item: CompletionItem): Thenable<CompletionItem>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addJSONProviders(xhr: XHRRequest, subscriptions: { dispose(): any }[]) {
|
||||||
|
let contributions = [new PackageJSONContribution(xhr), new BowerJSONContribution(xhr)];
|
||||||
|
contributions.forEach(contribution => {
|
||||||
|
let selector = contribution.getDocumentSelector();
|
||||||
|
subscriptions.push(languages.registerCompletionItemProvider(selector, new JSONCompletionItemProvider(contribution), '.', '$'));
|
||||||
|
subscriptions.push(languages.registerHoverProvider(selector, new JSONHoverProvider(contribution)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export class JSONHoverProvider implements HoverProvider {
|
||||||
|
|
||||||
|
constructor(private jsonContribution: IJSONContribution) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public provideHover(document: TextDocument, position: Position, token: CancellationToken): Thenable<Hover> {
|
||||||
|
let fileName = basename(document.fileName);
|
||||||
|
let offset = document.offsetAt(position);
|
||||||
|
let location = getLocation(document.getText(), offset);
|
||||||
|
let node = location.previousNode;
|
||||||
|
if (node && node.offset <= offset && offset <= node.offset + node.length) {
|
||||||
|
let promise = this.jsonContribution.getInfoContribution(fileName, location);
|
||||||
|
if (promise) {
|
||||||
|
return promise.then(htmlContent => {
|
||||||
|
let range = new Range(document.positionAt(node.offset), document.positionAt(node.offset + node.length));
|
||||||
|
let result: Hover = {
|
||||||
|
contents: htmlContent,
|
||||||
|
range: range
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class JSONCompletionItemProvider implements CompletionItemProvider {
|
||||||
|
|
||||||
|
constructor(private jsonContribution: IJSONContribution) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public resolveCompletionItem(item: CompletionItem, token: CancellationToken) : Thenable<CompletionItem> {
|
||||||
|
if (this.jsonContribution.resolveSuggestion) {
|
||||||
|
let resolver = this.jsonContribution.resolveSuggestion(item);
|
||||||
|
if (resolver) {
|
||||||
|
return resolver;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.resolve(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken): Thenable<CompletionList> {
|
||||||
|
|
||||||
|
let fileName = basename(document.fileName);
|
||||||
|
|
||||||
|
let currentWord = this.getCurrentWord(document, position);
|
||||||
|
let overwriteRange = null;
|
||||||
|
let items: CompletionItem[] = [];
|
||||||
|
let isIncomplete = false;
|
||||||
|
|
||||||
|
let offset = document.offsetAt(position);
|
||||||
|
let location = getLocation(document.getText(), offset);
|
||||||
|
|
||||||
|
let node = location.previousNode;
|
||||||
|
if (node && node.offset <= offset && offset <= node.offset + node.length && (node.type === 'property' || node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) {
|
||||||
|
overwriteRange = new Range(document.positionAt(node.offset), document.positionAt(node.offset + node.length));
|
||||||
|
} else {
|
||||||
|
overwriteRange = new Range(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
items.push(suggestion);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setAsIncomplete: () => isIncomplete = true,
|
||||||
|
error: (message: string) => console.error(message),
|
||||||
|
log: (message: string) => console.log(message)
|
||||||
|
};
|
||||||
|
|
||||||
|
let collectPromise : Thenable<any> = null;
|
||||||
|
|
||||||
|
if (location.completeProperty) {
|
||||||
|
let addValue = !location.previousNode || !location.previousNode.columnOffset;
|
||||||
|
let scanner = createScanner(document.getText(), true);
|
||||||
|
scanner.setPosition(offset);
|
||||||
|
scanner.scan();
|
||||||
|
let isLast = scanner.getToken() === SyntaxKind.CloseBraceToken || scanner.getToken() === SyntaxKind.EOF;
|
||||||
|
collectPromise = this.jsonContribution.collectPropertySuggestions(fileName, location, currentWord, addValue, isLast, collector);
|
||||||
|
} else {
|
||||||
|
if (location.segments.length === 0) {
|
||||||
|
collectPromise = this.jsonContribution.collectDefaultSuggestions(fileName, collector);
|
||||||
|
} else {
|
||||||
|
collectPromise = this.jsonContribution.collectValueSuggestions(fileName, location, collector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (collectPromise) {
|
||||||
|
return collectPromise.then(() => {
|
||||||
|
if (items.length > 0) {
|
||||||
|
return new CompletionList(items, isIncomplete);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getCurrentWord(document: TextDocument, position: Position) {
|
||||||
|
var i = position.character - 1;
|
||||||
|
var text = document.lineAt(position.line).text;
|
||||||
|
while (i >= 0 && ' \t\n\r\v":{[,'.indexOf(text.charAt(i)) === -1) {
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
return text.substring(i+1, position.character);
|
||||||
|
}
|
||||||
|
}
|
220
extensions/javascript/src/features/packageJSONContribution.ts
Normal file
220
extensions/javascript/src/features/packageJSONContribution.ts
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 {MarkedString, CompletionItemKind, CompletionItem, DocumentSelector} from 'vscode';
|
||||||
|
import {IJSONContribution, ISuggestionsCollector} from './jsonContributions';
|
||||||
|
import {XHRRequest} from 'request-light';
|
||||||
|
import {Location} from 'jsonc-parser';
|
||||||
|
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
|
let LIMIT = 40;
|
||||||
|
|
||||||
|
export class PackageJSONContribution implements IJSONContribution {
|
||||||
|
|
||||||
|
private mostDependedOn = [ 'lodash', 'async', 'underscore', 'request', 'commander', 'express', 'debug', 'chalk', 'colors', 'q', 'coffee-script',
|
||||||
|
'mkdirp', 'optimist', 'through2', 'yeoman-generator', 'moment', 'bluebird', 'glob', 'gulp-util', 'minimist', 'cheerio', 'jade', 'redis', 'node-uuid',
|
||||||
|
'socket', 'io', 'uglify-js', 'winston', 'through', 'fs-extra', 'handlebars', 'body-parser', 'rimraf', 'mime', 'semver', 'mongodb', 'jquery',
|
||||||
|
'grunt', 'connect', 'yosay', 'underscore', 'string', 'xml2js', 'ejs', 'mongoose', 'marked', 'extend', 'mocha', 'superagent', 'js-yaml', 'xtend',
|
||||||
|
'shelljs', 'gulp', 'yargs', 'browserify', 'minimatch', 'react', 'less', 'prompt', 'inquirer', 'ws', 'event-stream', 'inherits', 'mysql', 'esprima',
|
||||||
|
'jsdom', 'stylus', 'when', 'readable-stream', 'aws-sdk', 'concat-stream', 'chai', 'Thenable', 'wrench'];
|
||||||
|
|
||||||
|
public getDocumentSelector(): DocumentSelector {
|
||||||
|
return [{ language: 'json', pattern: '**/package.json' }];
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor(private xhr: XHRRequest) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public collectDefaultSuggestions(fileName: string, result: ISuggestionsCollector): Thenable<any> {
|
||||||
|
let defaultValue = {
|
||||||
|
'name': '{{name}}',
|
||||||
|
'description': '{{description}}',
|
||||||
|
'author': '{{author}}',
|
||||||
|
'version': '{{1.0.0}}',
|
||||||
|
'main': '{{pathToMain}}',
|
||||||
|
'dependencies': {}
|
||||||
|
};
|
||||||
|
let proposal = new CompletionItem(localize('json.package.default', 'Default package.json'));
|
||||||
|
proposal.kind = CompletionItemKind.Module;
|
||||||
|
proposal.insertText = JSON.stringify(defaultValue, null, '\t');
|
||||||
|
result.add(proposal);
|
||||||
|
return Promise.resolve(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public collectPropertySuggestions(resource: string, location: Location, currentWord: string, addValue: boolean, isLast:boolean, collector: ISuggestionsCollector) : Thenable<any> {
|
||||||
|
if ((location.matches(['dependencies']) || location.matches(['devDependencies']) || location.matches(['optionalDependencies']) || location.matches(['peerDependencies']))) {
|
||||||
|
let 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';
|
||||||
|
|
||||||
|
return this.xhr({
|
||||||
|
url : queryUrl
|
||||||
|
}).then((success) => {
|
||||||
|
if (success.status === 200) {
|
||||||
|
try {
|
||||||
|
let obj = JSON.parse(success.responseText);
|
||||||
|
if (obj && Array.isArray(obj.rows)) {
|
||||||
|
let results = <{ key: string[]; }[]> obj.rows;
|
||||||
|
for (let i = 0; i < results.length; i++) {
|
||||||
|
let keys = results[i].key;
|
||||||
|
if (Array.isArray(keys) && keys.length > 0) {
|
||||||
|
let name = keys[0];
|
||||||
|
let insertText = JSON.stringify(name);
|
||||||
|
if (addValue) {
|
||||||
|
insertText += ': "{{*}}"';
|
||||||
|
if (!isLast) {
|
||||||
|
insertText += ',';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let proposal = new CompletionItem(name);
|
||||||
|
proposal.kind = CompletionItemKind.Property;
|
||||||
|
proposal.insertText = insertText;
|
||||||
|
proposal.documentation = '';
|
||||||
|
collector.add(proposal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (results.length === LIMIT) {
|
||||||
|
collector.setAsIncomplete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
collector.error(localize('json.npm.error.repoaccess', 'Request to the NPM repository failed: {0}', success.responseText));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}, (error) => {
|
||||||
|
collector.error(localize('json.npm.error.repoaccess', 'Request to the NPM repository failed: {0}', error.responseText));
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.mostDependedOn.forEach((name) => {
|
||||||
|
let insertText = JSON.stringify(name);
|
||||||
|
if (addValue) {
|
||||||
|
insertText += ': "{{*}}"';
|
||||||
|
if (!isLast) {
|
||||||
|
insertText += ',';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let proposal = new CompletionItem(name);
|
||||||
|
proposal.kind = CompletionItemKind.Property;
|
||||||
|
proposal.insertText = insertText;
|
||||||
|
proposal.documentation = '';
|
||||||
|
collector.add(proposal);
|
||||||
|
});
|
||||||
|
collector.setAsIncomplete();
|
||||||
|
return Promise.resolve(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public collectValueSuggestions(fileName: string, location: Location, result: ISuggestionsCollector): Thenable<any> {
|
||||||
|
if ((location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']) || location.matches(['optionalDependencies', '*']) || location.matches(['peerDependencies', '*']))) {
|
||||||
|
let currentKey = location.segments[location.segments.length - 1];
|
||||||
|
let queryUrl = 'http://registry.npmjs.org/' + encodeURIComponent(currentKey) + '/latest';
|
||||||
|
|
||||||
|
return this.xhr({
|
||||||
|
url : queryUrl
|
||||||
|
}).then((success) => {
|
||||||
|
try {
|
||||||
|
let obj = JSON.parse(success.responseText);
|
||||||
|
if (obj && obj.version) {
|
||||||
|
let version = obj.version;
|
||||||
|
let name = JSON.stringify(version);
|
||||||
|
let proposal = new CompletionItem(name);
|
||||||
|
proposal.kind = CompletionItemKind.Property;
|
||||||
|
proposal.insertText = name;
|
||||||
|
proposal.documentation = localize('json.npm.latestversion', 'The currently latest version of the package');
|
||||||
|
result.add(proposal);
|
||||||
|
|
||||||
|
name = JSON.stringify('^' + version);
|
||||||
|
proposal = new CompletionItem(name);
|
||||||
|
proposal.kind = CompletionItemKind.Property;
|
||||||
|
proposal.insertText = name;
|
||||||
|
proposal.documentation = localize('json.npm.majorversion', 'Matches the most recent major version (1.x.x)');
|
||||||
|
result.add(proposal);
|
||||||
|
|
||||||
|
name = JSON.stringify('~' + version);
|
||||||
|
proposal = new CompletionItem(name);
|
||||||
|
proposal.kind = CompletionItemKind.Property;
|
||||||
|
proposal.insertText = name;
|
||||||
|
proposal.documentation = localize('json.npm.minorversion', 'Matches the most recent minor version (1.2.x)');
|
||||||
|
result.add(proposal);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}, (error) => {
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public resolveSuggestion(item: CompletionItem) : Thenable<CompletionItem> {
|
||||||
|
if (item.kind === CompletionItemKind.Property && item.documentation === '') {
|
||||||
|
return this.getInfo(item.label).then(infos => {
|
||||||
|
if (infos.length > 0) {
|
||||||
|
item.documentation = infos[0];
|
||||||
|
if (infos.length > 1) {
|
||||||
|
item.detail = infos[1];
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getInfo(pack: string): Thenable<string[]> {
|
||||||
|
let queryUrl = 'http://registry.npmjs.org/' + encodeURIComponent(pack) + '/latest';
|
||||||
|
|
||||||
|
return this.xhr({
|
||||||
|
url : queryUrl
|
||||||
|
}).then((success) => {
|
||||||
|
try {
|
||||||
|
let obj = JSON.parse(success.responseText);
|
||||||
|
if (obj) {
|
||||||
|
let result = [];
|
||||||
|
if (obj.description) {
|
||||||
|
result.push(obj.description);
|
||||||
|
}
|
||||||
|
if (obj.version) {
|
||||||
|
result.push(localize('json.npm.version.hover', 'Latest version: {0}', obj.version));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}, (error) => {
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getInfoContribution(fileName: string, location: Location): Thenable<MarkedString[]> {
|
||||||
|
if ((location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']) || location.matches(['optionalDependencies', '*']) || location.matches(['peerDependencies', '*']))) {
|
||||||
|
let pack = location.segments[location.segments.length - 1];
|
||||||
|
|
||||||
|
let htmlContent : MarkedString[] = [];
|
||||||
|
htmlContent.push(localize('json.npm.package.hover', '{0}', pack));
|
||||||
|
return this.getInfo(pack).then(infos => {
|
||||||
|
infos.forEach(info => {
|
||||||
|
htmlContent.push(info);
|
||||||
|
});
|
||||||
|
return htmlContent;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
27
extensions/javascript/src/javascriptMain.ts
Normal file
27
extensions/javascript/src/javascriptMain.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 {addJSONProviders} from './features/jsonContributions';
|
||||||
|
import * as httpRequest from 'request-light';
|
||||||
|
|
||||||
|
import {ExtensionContext, env, workspace} from 'vscode';
|
||||||
|
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
|
|
||||||
|
export function activate(context: ExtensionContext): any {
|
||||||
|
nls.config({locale: env.language});
|
||||||
|
|
||||||
|
configureHttpRequest();
|
||||||
|
workspace.onDidChangeConfiguration(e => configureHttpRequest());
|
||||||
|
|
||||||
|
addJSONProviders(httpRequest.xhr, context.subscriptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
function configureHttpRequest() {
|
||||||
|
let httpSettings = workspace.getConfiguration('http');
|
||||||
|
httpRequest.configure(httpSettings.get<string>('proxy'), httpSettings.get<boolean>('proxyStrictSSL'));
|
||||||
|
}
|
|
@ -3,18 +3,8 @@
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
declare module 'http-proxy-agent' {
|
/// <reference path='../../../../src/vs/vscode.d.ts'/>
|
||||||
|
/// <reference path='../../../../src/typings/mocha.d.ts'/>
|
||||||
interface IHttpProxyAgentOptions {
|
/// <reference path='../../../../extensions/node.d.ts'/>
|
||||||
host: string;
|
/// <reference path='../../../../extensions/lib.core.d.ts'/>
|
||||||
port: number;
|
/// <reference path='../../../../extensions/declares.d.ts'/>
|
||||||
auth?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
class HttpProxyAgent {
|
|
||||||
constructor(proxy: string);
|
|
||||||
constructor(opts: IHttpProxyAgentOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
export = HttpProxyAgent;
|
|
||||||
}
|
|
11
extensions/javascript/tsconfig.json
Normal file
11
extensions/javascript/tsconfig.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"noLib": true,
|
||||||
|
"target": "es5",
|
||||||
|
"module": "commonjs",
|
||||||
|
"outDir": "./out"
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
|
}
|
|
@ -8,8 +8,8 @@
|
||||||
"node": "*"
|
"node": "*"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"http-proxy-agent": "^0.2.6",
|
"request-light": "^0.0.3",
|
||||||
"https-proxy-agent": "^0.3.5",
|
"jsonc-parser": "^0.0.1",
|
||||||
"vscode-languageserver": "^1.3.0",
|
"vscode-languageserver": "^1.3.0",
|
||||||
"vscode-nls": "^1.0.4"
|
"vscode-nls": "^1.0.4"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,980 +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 enum ScanError {
|
|
||||||
None,
|
|
||||||
UnexpectedEndOfComment,
|
|
||||||
UnexpectedEndOfString,
|
|
||||||
UnexpectedEndOfNumber,
|
|
||||||
InvalidUnicode,
|
|
||||||
InvalidEscapeCharacter
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum SyntaxKind {
|
|
||||||
Unknown = 0,
|
|
||||||
OpenBraceToken,
|
|
||||||
CloseBraceToken,
|
|
||||||
OpenBracketToken,
|
|
||||||
CloseBracketToken,
|
|
||||||
CommaToken,
|
|
||||||
ColonToken,
|
|
||||||
NullKeyword,
|
|
||||||
TrueKeyword,
|
|
||||||
FalseKeyword,
|
|
||||||
StringLiteral,
|
|
||||||
NumericLiteral,
|
|
||||||
LineCommentTrivia,
|
|
||||||
BlockCommentTrivia,
|
|
||||||
LineBreakTrivia,
|
|
||||||
Trivia,
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface JSONScanner {
|
|
||||||
scan(): SyntaxKind;
|
|
||||||
getPosition(): number;
|
|
||||||
getToken(): SyntaxKind;
|
|
||||||
getTokenValue(): string;
|
|
||||||
getTokenOffset(): number;
|
|
||||||
getTokenLength(): number;
|
|
||||||
getTokenError(): ScanError;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createScanner(text:string, ignoreTrivia:boolean = false):JSONScanner {
|
|
||||||
|
|
||||||
var pos = 0,
|
|
||||||
len = text.length,
|
|
||||||
value:string = '',
|
|
||||||
tokenOffset = 0,
|
|
||||||
token:SyntaxKind = SyntaxKind.Unknown,
|
|
||||||
scanError:ScanError = ScanError.None;
|
|
||||||
|
|
||||||
function scanHexDigits(count: number, exact?: boolean): number {
|
|
||||||
var digits = 0;
|
|
||||||
var value = 0;
|
|
||||||
while (digits < count || !exact) {
|
|
||||||
var ch = text.charCodeAt(pos);
|
|
||||||
if (ch >= CharacterCodes._0 && ch <= CharacterCodes._9) {
|
|
||||||
value = value * 16 + ch - CharacterCodes._0;
|
|
||||||
}
|
|
||||||
else if (ch >= CharacterCodes.A && ch <= CharacterCodes.F) {
|
|
||||||
value = value * 16 + ch - CharacterCodes.A + 10;
|
|
||||||
}
|
|
||||||
else if (ch >= CharacterCodes.a && ch <= CharacterCodes.f) {
|
|
||||||
value = value * 16 + ch - CharacterCodes.a + 10;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pos++;
|
|
||||||
digits++;
|
|
||||||
}
|
|
||||||
if (digits < count) {
|
|
||||||
value = -1;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function scanNumber(): string {
|
|
||||||
var start = pos;
|
|
||||||
if (text.charCodeAt(pos) === CharacterCodes._0) {
|
|
||||||
pos++;
|
|
||||||
} else {
|
|
||||||
pos++;
|
|
||||||
while (pos < text.length && isDigit(text.charCodeAt(pos))) {
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (pos < text.length && text.charCodeAt(pos) === CharacterCodes.dot) {
|
|
||||||
pos++;
|
|
||||||
if (pos < text.length && isDigit(text.charCodeAt(pos))) {
|
|
||||||
pos++;
|
|
||||||
while (pos < text.length && isDigit(text.charCodeAt(pos))) {
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
scanError = ScanError.UnexpectedEndOfNumber;
|
|
||||||
return text.substring(start, end);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var end = pos;
|
|
||||||
if (pos < text.length && (text.charCodeAt(pos) === CharacterCodes.E || text.charCodeAt(pos) === CharacterCodes.e)) {
|
|
||||||
pos++;
|
|
||||||
if (pos < text.length && text.charCodeAt(pos) === CharacterCodes.plus || text.charCodeAt(pos) === CharacterCodes.minus) {
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
if (pos < text.length && isDigit(text.charCodeAt(pos))) {
|
|
||||||
pos++;
|
|
||||||
while (pos < text.length && isDigit(text.charCodeAt(pos))) {
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
end = pos;
|
|
||||||
} else {
|
|
||||||
scanError = ScanError.UnexpectedEndOfNumber;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return text.substring(start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
function scanString(): string {
|
|
||||||
|
|
||||||
var result = '',
|
|
||||||
start = pos;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (pos >= len) {
|
|
||||||
result += text.substring(start, pos);
|
|
||||||
scanError = ScanError.UnexpectedEndOfString;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
var ch = text.charCodeAt(pos);
|
|
||||||
if (ch === CharacterCodes.doubleQuote) {
|
|
||||||
result += text.substring(start, pos);
|
|
||||||
pos++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (ch === CharacterCodes.backslash) {
|
|
||||||
result += text.substring(start, pos);
|
|
||||||
pos++;
|
|
||||||
if (pos >= len) {
|
|
||||||
scanError = ScanError.UnexpectedEndOfString;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ch = text.charCodeAt(pos++);
|
|
||||||
switch (ch) {
|
|
||||||
case CharacterCodes.doubleQuote:
|
|
||||||
result += '\"';
|
|
||||||
break;
|
|
||||||
case CharacterCodes.backslash:
|
|
||||||
result += '\\';
|
|
||||||
break;
|
|
||||||
case CharacterCodes.slash:
|
|
||||||
result += '/';
|
|
||||||
break;
|
|
||||||
case CharacterCodes.b:
|
|
||||||
result += '\b';
|
|
||||||
break;
|
|
||||||
case CharacterCodes.f:
|
|
||||||
result += '\f';
|
|
||||||
break;
|
|
||||||
case CharacterCodes.n:
|
|
||||||
result += '\n';
|
|
||||||
break;
|
|
||||||
case CharacterCodes.r:
|
|
||||||
result += '\r';
|
|
||||||
break;
|
|
||||||
case CharacterCodes.t:
|
|
||||||
result += '\t';
|
|
||||||
break;
|
|
||||||
case CharacterCodes.u:
|
|
||||||
var ch = scanHexDigits(4, true);
|
|
||||||
if (ch >= 0) {
|
|
||||||
result += String.fromCharCode(ch);
|
|
||||||
} else {
|
|
||||||
scanError = ScanError.InvalidUnicode;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
scanError = ScanError.InvalidEscapeCharacter;
|
|
||||||
}
|
|
||||||
start = pos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (isLineBreak(ch)) {
|
|
||||||
result += text.substring(start, pos);
|
|
||||||
scanError = ScanError.UnexpectedEndOfString;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function scanNext():SyntaxKind {
|
|
||||||
|
|
||||||
value = '';
|
|
||||||
scanError = ScanError.None;
|
|
||||||
|
|
||||||
tokenOffset = pos;
|
|
||||||
|
|
||||||
if(pos >= len) {
|
|
||||||
// at the end
|
|
||||||
tokenOffset = len;
|
|
||||||
return token = SyntaxKind.EOF;
|
|
||||||
}
|
|
||||||
|
|
||||||
var code = text.charCodeAt(pos);
|
|
||||||
// trivia: whitespace
|
|
||||||
if (isWhiteSpace(code)) {
|
|
||||||
do {
|
|
||||||
pos++;
|
|
||||||
value += String.fromCharCode(code);
|
|
||||||
code = text.charCodeAt(pos);
|
|
||||||
} while (isWhiteSpace(code));
|
|
||||||
|
|
||||||
return token = SyntaxKind.Trivia;
|
|
||||||
}
|
|
||||||
|
|
||||||
// trivia: newlines
|
|
||||||
if (isLineBreak(code)) {
|
|
||||||
pos++;
|
|
||||||
value += String.fromCharCode(code);
|
|
||||||
if (code === CharacterCodes.carriageReturn && text.charCodeAt(pos) === CharacterCodes.lineFeed) {
|
|
||||||
pos++;
|
|
||||||
value += '\n';
|
|
||||||
}
|
|
||||||
return token = SyntaxKind.LineBreakTrivia;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(code) {
|
|
||||||
// tokens: []{}:,
|
|
||||||
case CharacterCodes.openBrace:
|
|
||||||
pos++;
|
|
||||||
return token = SyntaxKind.OpenBraceToken;
|
|
||||||
case CharacterCodes.closeBrace:
|
|
||||||
pos++;
|
|
||||||
return token = SyntaxKind.CloseBraceToken;
|
|
||||||
case CharacterCodes.openBracket:
|
|
||||||
pos++;
|
|
||||||
return token = SyntaxKind.OpenBracketToken;
|
|
||||||
case CharacterCodes.closeBracket:
|
|
||||||
pos++;
|
|
||||||
return token = SyntaxKind.CloseBracketToken;
|
|
||||||
case CharacterCodes.colon:
|
|
||||||
pos++;
|
|
||||||
return token = SyntaxKind.ColonToken;
|
|
||||||
case CharacterCodes.comma:
|
|
||||||
pos++;
|
|
||||||
return token = SyntaxKind.CommaToken;
|
|
||||||
|
|
||||||
// strings
|
|
||||||
case CharacterCodes.doubleQuote:
|
|
||||||
pos++;
|
|
||||||
value = scanString();
|
|
||||||
return token = SyntaxKind.StringLiteral;
|
|
||||||
|
|
||||||
// comments
|
|
||||||
case CharacterCodes.slash:
|
|
||||||
var start = pos - 1;
|
|
||||||
// Single-line comment
|
|
||||||
if (text.charCodeAt(pos + 1) === CharacterCodes.slash) {
|
|
||||||
pos += 2;
|
|
||||||
|
|
||||||
while (pos < len) {
|
|
||||||
if (isLineBreak(text.charCodeAt(pos))) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pos++;
|
|
||||||
|
|
||||||
}
|
|
||||||
value = text.substring(start, pos);
|
|
||||||
return token = SyntaxKind.LineCommentTrivia;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Multi-line comment
|
|
||||||
if (text.charCodeAt(pos + 1) === CharacterCodes.asterisk) {
|
|
||||||
pos += 2;
|
|
||||||
|
|
||||||
var safeLength = len - 1; // For lookahead.
|
|
||||||
var commentClosed = false;
|
|
||||||
while (pos < safeLength) {
|
|
||||||
var ch = text.charCodeAt(pos);
|
|
||||||
|
|
||||||
if (ch === CharacterCodes.asterisk && text.charCodeAt(pos + 1) === CharacterCodes.slash) {
|
|
||||||
pos += 2;
|
|
||||||
commentClosed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!commentClosed) {
|
|
||||||
pos++;
|
|
||||||
scanError = ScanError.UnexpectedEndOfComment;
|
|
||||||
}
|
|
||||||
|
|
||||||
value = text.substring(start, pos);
|
|
||||||
return token = SyntaxKind.BlockCommentTrivia;
|
|
||||||
}
|
|
||||||
// just a single slash
|
|
||||||
value += String.fromCharCode(code);
|
|
||||||
pos++;
|
|
||||||
return token = SyntaxKind.Unknown;
|
|
||||||
|
|
||||||
// numbers
|
|
||||||
case CharacterCodes.minus:
|
|
||||||
value += String.fromCharCode(code);
|
|
||||||
pos++;
|
|
||||||
if (pos === len || !isDigit(text.charCodeAt(pos))) {
|
|
||||||
return token = SyntaxKind.Unknown;
|
|
||||||
}
|
|
||||||
// found a minus, followed by a number so
|
|
||||||
// we fall through to proceed with scanning
|
|
||||||
// numbers
|
|
||||||
case CharacterCodes._0:
|
|
||||||
case CharacterCodes._1:
|
|
||||||
case CharacterCodes._2:
|
|
||||||
case CharacterCodes._3:
|
|
||||||
case CharacterCodes._4:
|
|
||||||
case CharacterCodes._5:
|
|
||||||
case CharacterCodes._6:
|
|
||||||
case CharacterCodes._7:
|
|
||||||
case CharacterCodes._8:
|
|
||||||
case CharacterCodes._9:
|
|
||||||
value += scanNumber();
|
|
||||||
return token = SyntaxKind.NumericLiteral;
|
|
||||||
// literals and unknown symbols
|
|
||||||
default:
|
|
||||||
// is a literal? Read the full word.
|
|
||||||
while (pos < len && isUnknownContentCharacter(code)) {
|
|
||||||
pos++;
|
|
||||||
code = text.charCodeAt(pos);
|
|
||||||
}
|
|
||||||
if (tokenOffset !== pos) {
|
|
||||||
value = text.substring(tokenOffset, pos);
|
|
||||||
// keywords: true, false, null
|
|
||||||
switch (value) {
|
|
||||||
case 'true': return token = SyntaxKind.TrueKeyword;
|
|
||||||
case 'false': return token = SyntaxKind.FalseKeyword;
|
|
||||||
case 'null': return token = SyntaxKind.NullKeyword;
|
|
||||||
}
|
|
||||||
return token = SyntaxKind.Unknown;
|
|
||||||
}
|
|
||||||
// some
|
|
||||||
value += String.fromCharCode(code);
|
|
||||||
pos++;
|
|
||||||
return token = SyntaxKind.Unknown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isUnknownContentCharacter(code: CharacterCodes) {
|
|
||||||
if (isWhiteSpace(code) || isLineBreak(code)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
switch (code) {
|
|
||||||
case CharacterCodes.closeBrace:
|
|
||||||
case CharacterCodes.closeBracket:
|
|
||||||
case CharacterCodes.openBrace:
|
|
||||||
case CharacterCodes.openBracket:
|
|
||||||
case CharacterCodes.doubleQuote:
|
|
||||||
case CharacterCodes.colon:
|
|
||||||
case CharacterCodes.comma:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function scanNextNonTrivia():SyntaxKind {
|
|
||||||
var result : SyntaxKind;
|
|
||||||
do {
|
|
||||||
result = scanNext();
|
|
||||||
} while (result >= SyntaxKind.LineCommentTrivia && result <= SyntaxKind.Trivia);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
getPosition: () => pos,
|
|
||||||
scan: ignoreTrivia ? scanNextNonTrivia : scanNext,
|
|
||||||
getToken: () => token,
|
|
||||||
getTokenValue: () => value,
|
|
||||||
getTokenOffset: () => tokenOffset,
|
|
||||||
getTokenLength: () => pos - tokenOffset,
|
|
||||||
getTokenError: () => scanError
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function isWhiteSpace(ch: number): boolean {
|
|
||||||
return ch === CharacterCodes.space || ch === CharacterCodes.tab || ch === CharacterCodes.verticalTab || ch === CharacterCodes.formFeed ||
|
|
||||||
ch === CharacterCodes.nonBreakingSpace || ch === CharacterCodes.ogham || ch >= CharacterCodes.enQuad && ch <= CharacterCodes.zeroWidthSpace ||
|
|
||||||
ch === CharacterCodes.narrowNoBreakSpace || ch === CharacterCodes.mathematicalSpace || ch === CharacterCodes.ideographicSpace || ch === CharacterCodes.byteOrderMark;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isLineBreak(ch: number): boolean {
|
|
||||||
return ch === CharacterCodes.lineFeed || ch === CharacterCodes.carriageReturn || ch === CharacterCodes.lineSeparator || ch === CharacterCodes.paragraphSeparator;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDigit(ch: number): boolean {
|
|
||||||
return ch >= CharacterCodes._0 && ch <= CharacterCodes._9;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isLetter(ch: number): boolean {
|
|
||||||
return ch >= CharacterCodes.a && ch <= CharacterCodes.z || ch >= CharacterCodes.A && ch <= CharacterCodes.Z;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum CharacterCodes {
|
|
||||||
nullCharacter = 0,
|
|
||||||
maxAsciiCharacter = 0x7F,
|
|
||||||
|
|
||||||
lineFeed = 0x0A, // \n
|
|
||||||
carriageReturn = 0x0D, // \r
|
|
||||||
lineSeparator = 0x2028,
|
|
||||||
paragraphSeparator = 0x2029,
|
|
||||||
|
|
||||||
// REVIEW: do we need to support this? The scanner doesn't, but our IText does. This seems
|
|
||||||
// like an odd disparity? (Or maybe it's completely fine for them to be different).
|
|
||||||
nextLine = 0x0085,
|
|
||||||
|
|
||||||
// Unicode 3.0 space characters
|
|
||||||
space = 0x0020, // " "
|
|
||||||
nonBreakingSpace = 0x00A0, //
|
|
||||||
enQuad = 0x2000,
|
|
||||||
emQuad = 0x2001,
|
|
||||||
enSpace = 0x2002,
|
|
||||||
emSpace = 0x2003,
|
|
||||||
threePerEmSpace = 0x2004,
|
|
||||||
fourPerEmSpace = 0x2005,
|
|
||||||
sixPerEmSpace = 0x2006,
|
|
||||||
figureSpace = 0x2007,
|
|
||||||
punctuationSpace = 0x2008,
|
|
||||||
thinSpace = 0x2009,
|
|
||||||
hairSpace = 0x200A,
|
|
||||||
zeroWidthSpace = 0x200B,
|
|
||||||
narrowNoBreakSpace = 0x202F,
|
|
||||||
ideographicSpace = 0x3000,
|
|
||||||
mathematicalSpace = 0x205F,
|
|
||||||
ogham = 0x1680,
|
|
||||||
|
|
||||||
_ = 0x5F,
|
|
||||||
$ = 0x24,
|
|
||||||
|
|
||||||
_0 = 0x30,
|
|
||||||
_1 = 0x31,
|
|
||||||
_2 = 0x32,
|
|
||||||
_3 = 0x33,
|
|
||||||
_4 = 0x34,
|
|
||||||
_5 = 0x35,
|
|
||||||
_6 = 0x36,
|
|
||||||
_7 = 0x37,
|
|
||||||
_8 = 0x38,
|
|
||||||
_9 = 0x39,
|
|
||||||
|
|
||||||
a = 0x61,
|
|
||||||
b = 0x62,
|
|
||||||
c = 0x63,
|
|
||||||
d = 0x64,
|
|
||||||
e = 0x65,
|
|
||||||
f = 0x66,
|
|
||||||
g = 0x67,
|
|
||||||
h = 0x68,
|
|
||||||
i = 0x69,
|
|
||||||
j = 0x6A,
|
|
||||||
k = 0x6B,
|
|
||||||
l = 0x6C,
|
|
||||||
m = 0x6D,
|
|
||||||
n = 0x6E,
|
|
||||||
o = 0x6F,
|
|
||||||
p = 0x70,
|
|
||||||
q = 0x71,
|
|
||||||
r = 0x72,
|
|
||||||
s = 0x73,
|
|
||||||
t = 0x74,
|
|
||||||
u = 0x75,
|
|
||||||
v = 0x76,
|
|
||||||
w = 0x77,
|
|
||||||
x = 0x78,
|
|
||||||
y = 0x79,
|
|
||||||
z = 0x7A,
|
|
||||||
|
|
||||||
A = 0x41,
|
|
||||||
B = 0x42,
|
|
||||||
C = 0x43,
|
|
||||||
D = 0x44,
|
|
||||||
E = 0x45,
|
|
||||||
F = 0x46,
|
|
||||||
G = 0x47,
|
|
||||||
H = 0x48,
|
|
||||||
I = 0x49,
|
|
||||||
J = 0x4A,
|
|
||||||
K = 0x4B,
|
|
||||||
L = 0x4C,
|
|
||||||
M = 0x4D,
|
|
||||||
N = 0x4E,
|
|
||||||
O = 0x4F,
|
|
||||||
P = 0x50,
|
|
||||||
Q = 0x51,
|
|
||||||
R = 0x52,
|
|
||||||
S = 0x53,
|
|
||||||
T = 0x54,
|
|
||||||
U = 0x55,
|
|
||||||
V = 0x56,
|
|
||||||
W = 0x57,
|
|
||||||
X = 0x58,
|
|
||||||
Y = 0x59,
|
|
||||||
Z = 0x5a,
|
|
||||||
|
|
||||||
ampersand = 0x26, // &
|
|
||||||
asterisk = 0x2A, // *
|
|
||||||
at = 0x40, // @
|
|
||||||
backslash = 0x5C, // \
|
|
||||||
bar = 0x7C, // |
|
|
||||||
caret = 0x5E, // ^
|
|
||||||
closeBrace = 0x7D, // }
|
|
||||||
closeBracket = 0x5D, // ]
|
|
||||||
closeParen = 0x29, // )
|
|
||||||
colon = 0x3A, // :
|
|
||||||
comma = 0x2C, // ,
|
|
||||||
dot = 0x2E, // .
|
|
||||||
doubleQuote = 0x22, // "
|
|
||||||
equals = 0x3D, // =
|
|
||||||
exclamation = 0x21, // !
|
|
||||||
greaterThan = 0x3E, // >
|
|
||||||
lessThan = 0x3C, // <
|
|
||||||
minus = 0x2D, // -
|
|
||||||
openBrace = 0x7B, // {
|
|
||||||
openBracket = 0x5B, // [
|
|
||||||
openParen = 0x28, // (
|
|
||||||
percent = 0x25, // %
|
|
||||||
plus = 0x2B, // +
|
|
||||||
question = 0x3F, // ?
|
|
||||||
semicolon = 0x3B, // ;
|
|
||||||
singleQuote = 0x27, // '
|
|
||||||
slash = 0x2F, // /
|
|
||||||
tilde = 0x7E, // ~
|
|
||||||
|
|
||||||
backspace = 0x08, // \b
|
|
||||||
formFeed = 0x0C, // \f
|
|
||||||
byteOrderMark = 0xFEFF,
|
|
||||||
tab = 0x09, // \t
|
|
||||||
verticalTab = 0x0B, // \v
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Takes JSON with JavaScript-style comments and remove
|
|
||||||
* them. Optionally replaces every none-newline character
|
|
||||||
* of comments with a replaceCharacter
|
|
||||||
*/
|
|
||||||
export function stripComments(text:string, replaceCh?:string):string {
|
|
||||||
|
|
||||||
var _scanner = createScanner(text),
|
|
||||||
parts: string[] = [],
|
|
||||||
kind:SyntaxKind,
|
|
||||||
offset = 0,
|
|
||||||
pos:number;
|
|
||||||
|
|
||||||
do {
|
|
||||||
pos = _scanner.getPosition();
|
|
||||||
kind = _scanner.scan();
|
|
||||||
switch (kind) {
|
|
||||||
case SyntaxKind.LineCommentTrivia:
|
|
||||||
case SyntaxKind.BlockCommentTrivia:
|
|
||||||
case SyntaxKind.EOF:
|
|
||||||
if(offset !== pos) {
|
|
||||||
parts.push(text.substring(offset, pos));
|
|
||||||
}
|
|
||||||
if(replaceCh !== void 0) {
|
|
||||||
parts.push(_scanner.getTokenValue().replace(/[^\r\n]/g, replaceCh));
|
|
||||||
}
|
|
||||||
offset = _scanner.getPosition();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while(kind !== SyntaxKind.EOF);
|
|
||||||
|
|
||||||
return parts.join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum ParseErrorCode {
|
|
||||||
InvalidSymbol,
|
|
||||||
InvalidNumberFormat,
|
|
||||||
PropertyNameExpected,
|
|
||||||
ValueExpected,
|
|
||||||
ColonExpected,
|
|
||||||
CommaExpected,
|
|
||||||
CloseBraceExpected,
|
|
||||||
CloseBracketExpected,
|
|
||||||
EndOfFileExpected
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getParseErrorMessage(errorCode: ParseErrorCode) : string {
|
|
||||||
switch (errorCode) {
|
|
||||||
case ParseErrorCode.InvalidSymbol: return 'Invalid symbol';
|
|
||||||
case ParseErrorCode.InvalidNumberFormat: return 'Invalid number format';
|
|
||||||
case ParseErrorCode.PropertyNameExpected: return 'Property name expected';
|
|
||||||
case ParseErrorCode.ValueExpected: return 'Value expected';
|
|
||||||
case ParseErrorCode.ColonExpected: return 'Colon expected';
|
|
||||||
case ParseErrorCode.CommaExpected: return 'Comma expected';
|
|
||||||
case ParseErrorCode.CloseBraceExpected: return 'Closing brace expected';
|
|
||||||
case ParseErrorCode.CloseBracketExpected: return 'Closing bracket expected';
|
|
||||||
case ParseErrorCode.EndOfFileExpected: return 'End of file expected';
|
|
||||||
default:
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type NodeType = "object" | "array" | "property" | "string" | "number" | "null";
|
|
||||||
|
|
||||||
|
|
||||||
export interface Node {
|
|
||||||
type: NodeType;
|
|
||||||
value: any;
|
|
||||||
offset: number;
|
|
||||||
length: number;
|
|
||||||
columnOffset?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Location {
|
|
||||||
previousNode: Node;
|
|
||||||
segments: string[];
|
|
||||||
completeProperty: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getLocation(text:string, position: number) : Location {
|
|
||||||
let segments: string[] = [];
|
|
||||||
let earlyReturnException = new Object();
|
|
||||||
let previousNode : Node = {
|
|
||||||
value: void 0,
|
|
||||||
offset: void 0,
|
|
||||||
length: void 0,
|
|
||||||
type: void 0
|
|
||||||
};
|
|
||||||
let completeProperty = false;
|
|
||||||
let hasComma = false;
|
|
||||||
try {
|
|
||||||
function setNode(value: string, offset: number, length: number, type: NodeType) {
|
|
||||||
previousNode.value = value;
|
|
||||||
previousNode.offset = offset;
|
|
||||||
previousNode.length = length;
|
|
||||||
previousNode.type = type;
|
|
||||||
previousNode.columnOffset = void 0;
|
|
||||||
}
|
|
||||||
visit(text, {
|
|
||||||
onObjectBegin: (offset: number, length: number) => {
|
|
||||||
if (position <= offset) {
|
|
||||||
throw earlyReturnException;
|
|
||||||
}
|
|
||||||
setNode(void 0, void 0, void 0, void 0);
|
|
||||||
completeProperty = position > offset;
|
|
||||||
hasComma = false;
|
|
||||||
},
|
|
||||||
onObjectProperty: (name: string, offset: number, length: number) => {
|
|
||||||
if (position < offset) {
|
|
||||||
throw earlyReturnException;
|
|
||||||
}
|
|
||||||
setNode(name, offset, length, 'property');
|
|
||||||
hasComma = false;
|
|
||||||
segments.push(name);
|
|
||||||
if (position <= offset + length) {
|
|
||||||
throw earlyReturnException;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onObjectEnd: (offset: number, length: number) => {
|
|
||||||
if (position <= offset) {
|
|
||||||
throw earlyReturnException;
|
|
||||||
}
|
|
||||||
setNode(void 0, void 0, void 0, void 0);
|
|
||||||
if (!hasComma) {
|
|
||||||
segments.pop();
|
|
||||||
}
|
|
||||||
hasComma = false;
|
|
||||||
},
|
|
||||||
onArrayBegin: (offset: number, length: number) => {
|
|
||||||
if (position <= offset) {
|
|
||||||
throw earlyReturnException;
|
|
||||||
}
|
|
||||||
setNode(void 0, void 0, void 0, void 0);
|
|
||||||
segments.push('[0]');
|
|
||||||
hasComma = false;
|
|
||||||
},
|
|
||||||
onArrayEnd: (offset: number, length: number) => {
|
|
||||||
if (position <= offset) {
|
|
||||||
throw earlyReturnException;
|
|
||||||
}
|
|
||||||
setNode(void 0, void 0, void 0, void 0);
|
|
||||||
if (!hasComma) {
|
|
||||||
segments.pop();
|
|
||||||
}
|
|
||||||
hasComma = false;
|
|
||||||
},
|
|
||||||
onLiteralValue: (value: any, offset: number, length: number) => {
|
|
||||||
if (position < offset) {
|
|
||||||
throw earlyReturnException;
|
|
||||||
}
|
|
||||||
setNode(value, offset, length, value === null ? 'null' : (typeof value === 'string' ? 'string' : 'number'));
|
|
||||||
if (position <= offset + length) {
|
|
||||||
throw earlyReturnException;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onSeparator: (sep: string, offset: number, length: number) => {
|
|
||||||
if (position <= offset) {
|
|
||||||
throw earlyReturnException;
|
|
||||||
}
|
|
||||||
if (sep === ':' && previousNode.type === 'property') {
|
|
||||||
previousNode.columnOffset = offset;
|
|
||||||
completeProperty = false;
|
|
||||||
setNode(void 0, void 0, void 0, void 0);
|
|
||||||
} else if (sep === ',') {
|
|
||||||
let last = segments.pop();
|
|
||||||
if (last[0] === '[' && last[last.length - 1] === ']') {
|
|
||||||
segments.push('[' + (parseInt(last.substr(1, last.length - 2)) + 1) + ']');
|
|
||||||
} else {
|
|
||||||
completeProperty = true;
|
|
||||||
}
|
|
||||||
setNode(void 0, void 0, void 0, void 0);
|
|
||||||
hasComma = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
if (e !== earlyReturnException) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
segments,
|
|
||||||
previousNode,
|
|
||||||
completeProperty
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function parse(text:string, errors: { error:ParseErrorCode; }[] = []) : any {
|
|
||||||
let currentProperty : string = null;
|
|
||||||
let currentParent : any = [];
|
|
||||||
let previousParents : any[] = [];
|
|
||||||
|
|
||||||
function onValue(value: any) {
|
|
||||||
if (Array.isArray(currentParent)) {
|
|
||||||
(<any[]> currentParent).push(value);
|
|
||||||
} else if (currentProperty) {
|
|
||||||
currentParent[currentProperty] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let visitor = {
|
|
||||||
onObjectBegin: () => {
|
|
||||||
let object = {};
|
|
||||||
onValue(object);
|
|
||||||
previousParents.push(currentParent);
|
|
||||||
currentParent = object;
|
|
||||||
currentProperty = null;
|
|
||||||
},
|
|
||||||
onObjectProperty: (name: string) => {
|
|
||||||
currentProperty = name;
|
|
||||||
},
|
|
||||||
onObjectEnd: () => {
|
|
||||||
currentParent = previousParents.pop();
|
|
||||||
},
|
|
||||||
onArrayBegin: () => {
|
|
||||||
let array = [];
|
|
||||||
onValue(array);
|
|
||||||
previousParents.push(currentParent);
|
|
||||||
currentParent = array;
|
|
||||||
currentProperty = null;
|
|
||||||
},
|
|
||||||
onArrayEnd: () => {
|
|
||||||
currentParent = previousParents.pop();
|
|
||||||
},
|
|
||||||
onLiteralValue: onValue,
|
|
||||||
onError:(error:ParseErrorCode) => {
|
|
||||||
errors.push({error: error});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
visit(text, visitor);
|
|
||||||
return currentParent[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function visit(text:string, visitor: JSONVisitor) : any {
|
|
||||||
var _scanner = createScanner(text, true);
|
|
||||||
|
|
||||||
function toNoArgVisit(visitFunction: (offset: number, length: number) => void) : () => void {
|
|
||||||
return visitFunction ? () => visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength()) : () => true;
|
|
||||||
}
|
|
||||||
function toOneArgVisit<T>(visitFunction: (arg: T, offset: number, length: number) => void) : (arg: T) => void {
|
|
||||||
return visitFunction ? (arg: T) => visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength()) : () => true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let onObjectBegin = toNoArgVisit(visitor.onObjectBegin),
|
|
||||||
onObjectProperty = toOneArgVisit(visitor.onObjectProperty),
|
|
||||||
onObjectEnd = toNoArgVisit(visitor.onObjectEnd),
|
|
||||||
onArrayBegin = toNoArgVisit(visitor.onArrayBegin),
|
|
||||||
onArrayEnd = toNoArgVisit(visitor.onArrayEnd),
|
|
||||||
onLiteralValue = toOneArgVisit(visitor.onLiteralValue),
|
|
||||||
onSeparator = toOneArgVisit(visitor.onSeparator),
|
|
||||||
onError = toOneArgVisit(visitor.onError);
|
|
||||||
|
|
||||||
function scanNext() : SyntaxKind {
|
|
||||||
var token = _scanner.scan();
|
|
||||||
while (token === SyntaxKind.Unknown) {
|
|
||||||
handleError(ParseErrorCode.InvalidSymbol);
|
|
||||||
token = _scanner.scan();
|
|
||||||
}
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleError(error:ParseErrorCode, skipUntilAfter: SyntaxKind[] = [], skipUntil: SyntaxKind[] = []) : void {
|
|
||||||
onError(error);
|
|
||||||
if (skipUntilAfter.length + skipUntil.length > 0) {
|
|
||||||
var token = _scanner.getToken();
|
|
||||||
while (token !== SyntaxKind.EOF) {
|
|
||||||
if (skipUntilAfter.indexOf(token) !== -1) {
|
|
||||||
scanNext();
|
|
||||||
break;
|
|
||||||
} else if (skipUntil.indexOf(token) !== -1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
token = scanNext();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseString(isValue: boolean) : boolean {
|
|
||||||
if (_scanner.getToken() !== SyntaxKind.StringLiteral) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var value = _scanner.getTokenValue();
|
|
||||||
if (isValue) {
|
|
||||||
onLiteralValue(value);
|
|
||||||
} else {
|
|
||||||
onObjectProperty(value);
|
|
||||||
}
|
|
||||||
scanNext();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseLiteral() : boolean {
|
|
||||||
switch (_scanner.getToken()) {
|
|
||||||
case SyntaxKind.NumericLiteral:
|
|
||||||
let value = 0;
|
|
||||||
try {
|
|
||||||
value = JSON.parse(_scanner.getTokenValue());
|
|
||||||
if (typeof value !== 'number') {
|
|
||||||
handleError(ParseErrorCode.InvalidNumberFormat);
|
|
||||||
value = 0;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
handleError(ParseErrorCode.InvalidNumberFormat);
|
|
||||||
}
|
|
||||||
onLiteralValue(value);
|
|
||||||
break;
|
|
||||||
case SyntaxKind.NullKeyword:
|
|
||||||
onLiteralValue(null);
|
|
||||||
break;
|
|
||||||
case SyntaxKind.TrueKeyword:
|
|
||||||
onLiteralValue(true);
|
|
||||||
break;
|
|
||||||
case SyntaxKind.FalseKeyword:
|
|
||||||
onLiteralValue(false);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
scanNext();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseProperty() : boolean {
|
|
||||||
if (!parseString(false)) {
|
|
||||||
handleError(ParseErrorCode.PropertyNameExpected, [], [SyntaxKind.CloseBraceToken, SyntaxKind.CommaToken] );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (_scanner.getToken() === SyntaxKind.ColonToken) {
|
|
||||||
onSeparator(':');
|
|
||||||
scanNext(); // consume colon
|
|
||||||
|
|
||||||
if (!parseValue()) {
|
|
||||||
handleError(ParseErrorCode.ValueExpected, [], [SyntaxKind.CloseBraceToken, SyntaxKind.CommaToken] );
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
handleError(ParseErrorCode.ColonExpected, [], [SyntaxKind.CloseBraceToken, SyntaxKind.CommaToken] );
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseObject() : boolean {
|
|
||||||
if (_scanner.getToken() !== SyntaxKind.OpenBraceToken) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
onObjectBegin();
|
|
||||||
scanNext(); // consume open brace
|
|
||||||
|
|
||||||
var needsComma = false;
|
|
||||||
while (_scanner.getToken() !== SyntaxKind.CloseBraceToken && _scanner.getToken() !== SyntaxKind.EOF) {
|
|
||||||
if (_scanner.getToken() === SyntaxKind.CommaToken) {
|
|
||||||
if (!needsComma) {
|
|
||||||
handleError(ParseErrorCode.ValueExpected, [], [] );
|
|
||||||
}
|
|
||||||
onSeparator(',');
|
|
||||||
scanNext(); // consume comma
|
|
||||||
} else if (needsComma) {
|
|
||||||
handleError(ParseErrorCode.CommaExpected, [], [] );
|
|
||||||
}
|
|
||||||
if (!parseProperty()) {
|
|
||||||
handleError(ParseErrorCode.ValueExpected, [], [SyntaxKind.CloseBraceToken, SyntaxKind.CommaToken] );
|
|
||||||
}
|
|
||||||
needsComma = true;
|
|
||||||
}
|
|
||||||
onObjectEnd();
|
|
||||||
if (_scanner.getToken() !== SyntaxKind.CloseBraceToken) {
|
|
||||||
handleError(ParseErrorCode.CloseBraceExpected, [SyntaxKind.CloseBraceToken], []);
|
|
||||||
} else {
|
|
||||||
scanNext(); // consume close brace
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseArray() : boolean {
|
|
||||||
if (_scanner.getToken() !== SyntaxKind.OpenBracketToken) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
onArrayBegin();
|
|
||||||
scanNext(); // consume open bracket
|
|
||||||
|
|
||||||
var needsComma = false;
|
|
||||||
while (_scanner.getToken() !== SyntaxKind.CloseBracketToken && _scanner.getToken() !== SyntaxKind.EOF) {
|
|
||||||
if (_scanner.getToken() === SyntaxKind.CommaToken) {
|
|
||||||
if (!needsComma) {
|
|
||||||
handleError(ParseErrorCode.ValueExpected, [], [] );
|
|
||||||
}
|
|
||||||
onSeparator(',');
|
|
||||||
scanNext(); // consume comma
|
|
||||||
} else if (needsComma) {
|
|
||||||
handleError(ParseErrorCode.CommaExpected, [], [] );
|
|
||||||
}
|
|
||||||
if (!parseValue()) {
|
|
||||||
handleError(ParseErrorCode.ValueExpected, [], [SyntaxKind.CloseBracketToken, SyntaxKind.CommaToken] );
|
|
||||||
}
|
|
||||||
needsComma = true;
|
|
||||||
}
|
|
||||||
onArrayEnd();
|
|
||||||
if (_scanner.getToken() !== SyntaxKind.CloseBracketToken) {
|
|
||||||
handleError(ParseErrorCode.CloseBracketExpected, [SyntaxKind.CloseBracketToken], []);
|
|
||||||
} else {
|
|
||||||
scanNext(); // consume close bracket
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseValue() : boolean {
|
|
||||||
return parseArray() || parseObject() || parseString(true) || parseLiteral();
|
|
||||||
}
|
|
||||||
|
|
||||||
scanNext();
|
|
||||||
if (!parseValue()) {
|
|
||||||
handleError(ParseErrorCode.ValueExpected, [], []);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (_scanner.getToken() !== SyntaxKind.EOF) {
|
|
||||||
handleError(ParseErrorCode.EndOfFileExpected, [], []);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface JSONVisitor {
|
|
||||||
onObjectBegin?: (offset:number, length:number) => void;
|
|
||||||
onObjectProperty?: (property: string, offset:number, length:number) => void;
|
|
||||||
|
|
||||||
onObjectEnd?: (offset:number, length:number) => void;
|
|
||||||
|
|
||||||
onArrayBegin?: (offset:number, length:number) => void;
|
|
||||||
onArrayEnd?: (offset:number, length:number) => void;
|
|
||||||
|
|
||||||
onLiteralValue?: (value: any, offset:number, length:number) => void;
|
|
||||||
|
|
||||||
onSeparator?: (charcter: string, offset:number, length:number) => void;
|
|
||||||
|
|
||||||
onError?: (error: ParseErrorCode, offset:number, length:number) => void;
|
|
||||||
}
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import Parser = require('./jsonParser');
|
import Parser = require('./jsonParser');
|
||||||
import SchemaService = require('./jsonSchemaService');
|
import SchemaService = require('./jsonSchemaService');
|
||||||
import JsonSchema = require('./json-toolbox/jsonSchema');
|
import JsonSchema = require('./jsonSchema');
|
||||||
import {IJSONWorkerContribution} from './jsonContributions';
|
import {IJSONWorkerContribution} from './jsonContributions';
|
||||||
|
|
||||||
import {CompletionItem, CompletionItemKind, CompletionList, ITextDocument, TextDocumentPosition, Range, TextEdit, RemoteConsole} from 'vscode-languageserver';
|
import {CompletionItem, CompletionItemKind, CompletionList, ITextDocument, TextDocumentPosition, Range, TextEdit, RemoteConsole} from 'vscode-languageserver';
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import Json = require('./json-toolbox/json');
|
import Json = require('jsonc-parser');
|
||||||
import {ITextDocument, Range, Position, FormattingOptions, TextEdit} from 'vscode-languageserver';
|
import {ITextDocument, Range, Position, FormattingOptions, TextEdit} from 'vscode-languageserver';
|
||||||
|
|
||||||
export function format(document: ITextDocument, range: Range, options: FormattingOptions): TextEdit[] {
|
export function format(document: ITextDocument, range: Range, options: FormattingOptions): TextEdit[] {
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import Json = require('./json-toolbox/json');
|
import Json = require('jsonc-parser');
|
||||||
import JsonSchema = require('./json-toolbox/jsonSchema');
|
import JsonSchema = require('./jsonSchema');
|
||||||
import {JSONLocation} from './jsonLocation';
|
import {JSONLocation} from './jsonLocation';
|
||||||
|
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
|
@ -898,7 +898,7 @@ export function parse(text: string, config = new JSONDocumentConfig()): JSONDocu
|
||||||
if (_scanner.getToken() === Json.SyntaxKind.Unknown) {
|
if (_scanner.getToken() === Json.SyntaxKind.Unknown) {
|
||||||
// give a more helpful error message
|
// give a more helpful error message
|
||||||
let value = _scanner.getTokenValue();
|
let value = _scanner.getTokenValue();
|
||||||
if (value.length > 0 && (value.charAt(0) === '\'' || Json.isLetter(value.charAt(0).charCodeAt(0)))) {
|
if (value.match(/^['\w]/)) {
|
||||||
_error(localize('DoubleQuotesExpected', 'Property keys must be doublequoted'));
|
_error(localize('DoubleQuotesExpected', 'Property keys must be doublequoted'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import Json = require('./json-toolbox/json');
|
import Json = require('jsonc-parser');
|
||||||
import {IJSONSchema} from './json-toolbox/jsonSchema';
|
import {IJSONSchema} from './jsonSchema';
|
||||||
import {IXHROptions, IXHRResponse, getErrorStatusDescription} from './utils/httpRequest';
|
import {XHROptions, XHRResponse, getErrorStatusDescription} from 'request-light';
|
||||||
import URI from './utils/uri';
|
import URI from './utils/uri';
|
||||||
import Strings = require('./utils/strings');
|
import Strings = require('./utils/strings');
|
||||||
import Parser = require('./jsonParser');
|
import Parser = require('./jsonParser');
|
||||||
|
@ -208,7 +208,7 @@ export interface IWorkspaceContextService {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IRequestService {
|
export interface IRequestService {
|
||||||
(options: IXHROptions): Thenable<IXHRResponse>;
|
(options: XHROptions): Thenable<XHRResponse>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class JSONSchemaService implements IJSONSchemaService {
|
export class JSONSchemaService implements IJSONSchemaService {
|
||||||
|
@ -362,7 +362,7 @@ export class JSONSchemaService implements IJSONSchemaService {
|
||||||
let errors = jsonErrors.length ? [localize('json.schema.invalidFormat', 'Unable to parse content from \'{0}\': {1}.', toDisplayString(url), jsonErrors[0])] : [];
|
let errors = jsonErrors.length ? [localize('json.schema.invalidFormat', 'Unable to parse content from \'{0}\': {1}.', toDisplayString(url), jsonErrors[0])] : [];
|
||||||
return new UnresolvedSchema(schemaContent, errors);
|
return new UnresolvedSchema(schemaContent, errors);
|
||||||
},
|
},
|
||||||
(error: IXHRResponse) => {
|
(error: XHRResponse) => {
|
||||||
let errorMessage = localize('json.schema.unabletoload', 'Unable to load schema from \'{0}\': {1}', toDisplayString(url), error.responseText || getErrorStatusDescription(error.status) || error.toString());
|
let errorMessage = localize('json.schema.unabletoload', 'Unable to load schema from \'{0}\': {1}', toDisplayString(url), error.responseText || getErrorStatusDescription(error.status) || error.toString());
|
||||||
return new UnresolvedSchema(<IJSONSchema>{}, [errorMessage]);
|
return new UnresolvedSchema(<IJSONSchema>{}, [errorMessage]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,144 +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 {MarkedString, CompletionItemKind} from 'vscode-languageserver';
|
|
||||||
import Strings = require('../utils/strings');
|
|
||||||
import {IJSONWorkerContribution, ISuggestionsCollector} from '../jsonContributions';
|
|
||||||
import {IRequestService} from '../jsonSchemaService';
|
|
||||||
import {JSONLocation} from '../jsonLocation';
|
|
||||||
|
|
||||||
import * as nls from 'vscode-nls';
|
|
||||||
const localize = nls.loadMessageBundle();
|
|
||||||
|
|
||||||
export class BowerJSONContribution implements IJSONWorkerContribution {
|
|
||||||
|
|
||||||
private requestService : IRequestService;
|
|
||||||
|
|
||||||
private topRanked = ['twitter','bootstrap','angular-1.1.6','angular-latest','angulerjs','d3','myjquery','jq','abcdef1234567890','jQuery','jquery-1.11.1','jquery',
|
|
||||||
'sushi-vanilla-x-data','font-awsome','Font-Awesome','font-awesome','fontawesome','html5-boilerplate','impress.js','homebrew',
|
|
||||||
'backbone','moment1','momentjs','moment','linux','animate.css','animate-css','reveal.js','jquery-file-upload','blueimp-file-upload','threejs','express','chosen',
|
|
||||||
'normalize-css','normalize.css','semantic','semantic-ui','Semantic-UI','modernizr','underscore','underscore1',
|
|
||||||
'material-design-icons','ionic','chartjs','Chart.js','nnnick-chartjs','select2-ng','select2-dist','phantom','skrollr','scrollr','less.js','leancss','parser-lib',
|
|
||||||
'hui','bootstrap-languages','async','gulp','jquery-pjax','coffeescript','hammer.js','ace','leaflet','jquery-mobile','sweetalert','typeahead.js','soup','typehead.js',
|
|
||||||
'sails','codeigniter2'];
|
|
||||||
|
|
||||||
public constructor(requestService: IRequestService) {
|
|
||||||
this.requestService = requestService;
|
|
||||||
}
|
|
||||||
|
|
||||||
private isBowerFile(resource: string): boolean {
|
|
||||||
return Strings.endsWith(resource, '/bower.json') || Strings.endsWith(resource, '/.bower.json');
|
|
||||||
}
|
|
||||||
|
|
||||||
public collectDefaultSuggestions(resource: string, result: ISuggestionsCollector): Thenable<any> {
|
|
||||||
if (this.isBowerFile(resource)) {
|
|
||||||
let defaultValue = {
|
|
||||||
'name': '{{name}}',
|
|
||||||
'description': '{{description}}',
|
|
||||||
'authors': [ '{{author}}' ],
|
|
||||||
'version': '{{1.0.0}}',
|
|
||||||
'main': '{{pathToMain}}',
|
|
||||||
'dependencies': {}
|
|
||||||
};
|
|
||||||
result.add({ kind: CompletionItemKind.Class, label: localize('json.bower.default', 'Default bower.json'), insertText: JSON.stringify(defaultValue, null, '\t'), documentation: '' });
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public collectPropertySuggestions(resource: string, location: JSONLocation, currentWord: string, addValue: boolean, isLast:boolean, result: ISuggestionsCollector) : Thenable<any> {
|
|
||||||
if (this.isBowerFile(resource) && (location.matches(['dependencies']) || location.matches(['devDependencies']))) {
|
|
||||||
if (currentWord.length > 0) {
|
|
||||||
let queryUrl = 'https://bower.herokuapp.com/packages/search/' + encodeURIComponent(currentWord);
|
|
||||||
|
|
||||||
return this.requestService({
|
|
||||||
url : queryUrl
|
|
||||||
}).then((success) => {
|
|
||||||
if (success.status === 200) {
|
|
||||||
try {
|
|
||||||
let obj = JSON.parse(success.responseText);
|
|
||||||
if (Array.isArray(obj)) {
|
|
||||||
let results = <{name:string; description:string;}[]> obj;
|
|
||||||
for (let i = 0; i < results.length; i++) {
|
|
||||||
let name = results[i].name;
|
|
||||||
let description = results[i].description || '';
|
|
||||||
let insertText = JSON.stringify(name);
|
|
||||||
if (addValue) {
|
|
||||||
insertText += ': "{{*}}"';
|
|
||||||
if (!isLast) {
|
|
||||||
insertText += ',';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.add({ kind: CompletionItemKind.Property, label: name, insertText: insertText, documentation: description });
|
|
||||||
}
|
|
||||||
result.setAsIncomplete();
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result.error(localize('json.bower.error.repoaccess', 'Request to the bower repository failed: {0}', success.responseText));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}, (error) => {
|
|
||||||
result.error(localize('json.bower.error.repoaccess', 'Request to the bower repository failed: {0}', error.responseText));
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.topRanked.forEach((name) => {
|
|
||||||
let insertText = JSON.stringify(name);
|
|
||||||
if (addValue) {
|
|
||||||
insertText += ': "{{*}}"';
|
|
||||||
if (!isLast) {
|
|
||||||
insertText += ',';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.add({ kind: CompletionItemKind.Property, label: name, insertText: insertText, documentation: '' });
|
|
||||||
});
|
|
||||||
result.setAsIncomplete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public collectValueSuggestions(resource: string, location: JSONLocation, currentKey: string, result: ISuggestionsCollector): Thenable<any> {
|
|
||||||
// not implemented. Could be do done calling the bower command. Waiting for web API: https://github.com/bower/registry/issues/26
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getInfoContribution(resource: string, location: JSONLocation): Thenable<MarkedString[]> {
|
|
||||||
if (this.isBowerFile(resource) && (location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']))) {
|
|
||||||
let pack = location.getSegments()[location.getSegments().length - 1];
|
|
||||||
let htmlContent : MarkedString[] = [];
|
|
||||||
htmlContent.push(localize('json.bower.package.hover', '{0}', pack));
|
|
||||||
|
|
||||||
let queryUrl = 'https://bower.herokuapp.com/packages/' + encodeURIComponent(pack);
|
|
||||||
|
|
||||||
return this.requestService({
|
|
||||||
url : queryUrl
|
|
||||||
}).then((success) => {
|
|
||||||
try {
|
|
||||||
let obj = JSON.parse(success.responseText);
|
|
||||||
if (obj && obj.url) {
|
|
||||||
let url = obj.url;
|
|
||||||
if (Strings.startsWith(url, 'git://')) {
|
|
||||||
url = url.substring(6);
|
|
||||||
}
|
|
||||||
if (Strings.endsWith(url, '.git')) {
|
|
||||||
url = url.substring(0, url.length - 4);
|
|
||||||
}
|
|
||||||
htmlContent.push(url);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
return htmlContent;
|
|
||||||
}, (error) => {
|
|
||||||
return htmlContent;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,173 +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 {MarkedString, CompletionItemKind} from 'vscode-languageserver';
|
|
||||||
import Strings = require('../utils/strings');
|
|
||||||
import {IJSONWorkerContribution, ISuggestionsCollector} from '../jsonContributions';
|
|
||||||
import {IRequestService} from '../jsonSchemaService';
|
|
||||||
import {JSONLocation} from '../jsonLocation';
|
|
||||||
|
|
||||||
import * as nls from 'vscode-nls';
|
|
||||||
const localize = nls.loadMessageBundle();
|
|
||||||
|
|
||||||
let LIMIT = 40;
|
|
||||||
|
|
||||||
export class PackageJSONContribution implements IJSONWorkerContribution {
|
|
||||||
|
|
||||||
private mostDependedOn = [ 'lodash', 'async', 'underscore', 'request', 'commander', 'express', 'debug', 'chalk', 'colors', 'q', 'coffee-script',
|
|
||||||
'mkdirp', 'optimist', 'through2', 'yeoman-generator', 'moment', 'bluebird', 'glob', 'gulp-util', 'minimist', 'cheerio', 'jade', 'redis', 'node-uuid',
|
|
||||||
'socket', 'io', 'uglify-js', 'winston', 'through', 'fs-extra', 'handlebars', 'body-parser', 'rimraf', 'mime', 'semver', 'mongodb', 'jquery',
|
|
||||||
'grunt', 'connect', 'yosay', 'underscore', 'string', 'xml2js', 'ejs', 'mongoose', 'marked', 'extend', 'mocha', 'superagent', 'js-yaml', 'xtend',
|
|
||||||
'shelljs', 'gulp', 'yargs', 'browserify', 'minimatch', 'react', 'less', 'prompt', 'inquirer', 'ws', 'event-stream', 'inherits', 'mysql', 'esprima',
|
|
||||||
'jsdom', 'stylus', 'when', 'readable-stream', 'aws-sdk', 'concat-stream', 'chai', 'Thenable', 'wrench'];
|
|
||||||
|
|
||||||
private requestService : IRequestService;
|
|
||||||
|
|
||||||
private isPackageJSONFile(resource: string): boolean {
|
|
||||||
return Strings.endsWith(resource, '/package.json');
|
|
||||||
}
|
|
||||||
|
|
||||||
public constructor(requestService: IRequestService) {
|
|
||||||
this.requestService = requestService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public collectDefaultSuggestions(resource: string, result: ISuggestionsCollector): Thenable<any> {
|
|
||||||
if (this.isPackageJSONFile(resource)) {
|
|
||||||
let defaultValue = {
|
|
||||||
'name': '{{name}}',
|
|
||||||
'description': '{{description}}',
|
|
||||||
'author': '{{author}}',
|
|
||||||
'version': '{{1.0.0}}',
|
|
||||||
'main': '{{pathToMain}}',
|
|
||||||
'dependencies': {}
|
|
||||||
};
|
|
||||||
result.add({ kind: CompletionItemKind.Module, label: localize('json.package.default', 'Default package.json'), insertText: JSON.stringify(defaultValue, null, '\t'), documentation: '' });
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public collectPropertySuggestions(resource: string, location: JSONLocation, currentWord: string, addValue: boolean, isLast:boolean, result: ISuggestionsCollector) : Thenable<any> {
|
|
||||||
if (this.isPackageJSONFile(resource) && (location.matches(['dependencies']) || location.matches(['devDependencies']) || location.matches(['optionalDependencies']) || location.matches(['peerDependencies']))) {
|
|
||||||
let 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';
|
|
||||||
|
|
||||||
return this.requestService({
|
|
||||||
url : queryUrl
|
|
||||||
}).then((success) => {
|
|
||||||
if (success.status === 200) {
|
|
||||||
try {
|
|
||||||
let obj = JSON.parse(success.responseText);
|
|
||||||
if (obj && Array.isArray(obj.rows)) {
|
|
||||||
let results = <{ key: string[]; }[]> obj.rows;
|
|
||||||
for (let i = 0; i < results.length; i++) {
|
|
||||||
let keys = results[i].key;
|
|
||||||
if (Array.isArray(keys) && keys.length > 0) {
|
|
||||||
let name = keys[0];
|
|
||||||
let insertText = JSON.stringify(name);
|
|
||||||
if (addValue) {
|
|
||||||
insertText += ': "{{*}}"';
|
|
||||||
if (!isLast) {
|
|
||||||
insertText += ',';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.add({ kind: CompletionItemKind.Property, label: name, insertText: insertText, documentation: '' });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (results.length === LIMIT) {
|
|
||||||
result.setAsIncomplete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result.error(localize('json.npm.error.repoaccess', 'Request to the NPM repository failed: {0}', success.responseText));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}, (error) => {
|
|
||||||
result.error(localize('json.npm.error.repoaccess', 'Request to the NPM repository failed: {0}', error.responseText));
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.mostDependedOn.forEach((name) => {
|
|
||||||
let insertText = JSON.stringify(name);
|
|
||||||
if (addValue) {
|
|
||||||
insertText += ': "{{*}}"';
|
|
||||||
if (!isLast) {
|
|
||||||
insertText += ',';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.add({ kind: CompletionItemKind.Property, label: name, insertText: insertText, documentation: '' });
|
|
||||||
});
|
|
||||||
result.setAsIncomplete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public collectValueSuggestions(resource: string, location: JSONLocation, currentKey: string, result: ISuggestionsCollector): Thenable<any> {
|
|
||||||
if (this.isPackageJSONFile(resource) && (location.matches(['dependencies']) || location.matches(['devDependencies']) || location.matches(['optionalDependencies']) || location.matches(['peerDependencies']))) {
|
|
||||||
let queryUrl = 'http://registry.npmjs.org/' + encodeURIComponent(currentKey) + '/latest';
|
|
||||||
|
|
||||||
return this.requestService({
|
|
||||||
url : queryUrl
|
|
||||||
}).then((success) => {
|
|
||||||
try {
|
|
||||||
let obj = JSON.parse(success.responseText);
|
|
||||||
if (obj && obj.version) {
|
|
||||||
let version = obj.version;
|
|
||||||
let name = JSON.stringify(version);
|
|
||||||
result.add({ kind: CompletionItemKind.Class, label: name, insertText: name, documentation: localize('json.npm.latestversion', 'The currently latest version of the package') });
|
|
||||||
name = JSON.stringify('^' + version);
|
|
||||||
result.add({ kind: CompletionItemKind.Class, label: name, insertText: name, documentation: localize('json.npm.majorversion', 'Matches the most recent major version (1.x.x)') });
|
|
||||||
name = JSON.stringify('~' + version);
|
|
||||||
result.add({ kind: CompletionItemKind.Class, label: name, insertText: name, documentation: localize('json.npm.minorversion', 'Matches the most recent minor version (1.2.x)') });
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}, (error) => {
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getInfoContribution(resource: string, location: JSONLocation): Thenable<MarkedString[]> {
|
|
||||||
if (this.isPackageJSONFile(resource) && (location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']) || location.matches(['optionalDependencies', '*']) || location.matches(['peerDependencies', '*']))) {
|
|
||||||
let pack = location.getSegments()[location.getSegments().length - 1];
|
|
||||||
|
|
||||||
let htmlContent : MarkedString[] = [];
|
|
||||||
htmlContent.push(localize('json.npm.package.hover', '{0}', pack));
|
|
||||||
|
|
||||||
let queryUrl = 'http://registry.npmjs.org/' + encodeURIComponent(pack) + '/latest';
|
|
||||||
|
|
||||||
return this.requestService({
|
|
||||||
url : queryUrl
|
|
||||||
}).then((success) => {
|
|
||||||
try {
|
|
||||||
let obj = JSON.parse(success.responseText);
|
|
||||||
if (obj) {
|
|
||||||
if (obj.description) {
|
|
||||||
htmlContent.push(obj.description);
|
|
||||||
}
|
|
||||||
if (obj.version) {
|
|
||||||
htmlContent.push(localize('json.npm.version.hover', 'Latest version: {0}', obj.version));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
return htmlContent;
|
|
||||||
}, (error) => {
|
|
||||||
return htmlContent;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import {MarkedString, CompletionItemKind, CompletionItem} from 'vscode-languageserver';
|
import {MarkedString, CompletionItemKind, CompletionItem} from 'vscode-languageserver';
|
||||||
import Strings = require('../utils/strings');
|
import Strings = require('../utils/strings');
|
||||||
import {IXHRResponse, getErrorStatusDescription} from '../utils/httpRequest';
|
import {XHRResponse, getErrorStatusDescription} from 'request-light';
|
||||||
import {IJSONWorkerContribution, ISuggestionsCollector} from '../jsonContributions';
|
import {IJSONWorkerContribution, ISuggestionsCollector} from '../jsonContributions';
|
||||||
import {IRequestService} from '../jsonSchemaService';
|
import {IRequestService} from '../jsonSchemaService';
|
||||||
import {JSONLocation} from '../jsonLocation';
|
import {JSONLocation} from '../jsonLocation';
|
||||||
|
@ -131,7 +131,7 @@ export class ProjectJSONContribution implements IJSONWorkerContribution {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Promise.reject<T>(localize('json.nugget.error.indexaccess', 'Request to {0} failed: {1}', url, success.responseText));
|
return Promise.reject<T>(localize('json.nugget.error.indexaccess', 'Request to {0} failed: {1}', url, success.responseText));
|
||||||
}, (error: IXHRResponse) => {
|
}, (error: XHRResponse) => {
|
||||||
return Promise.reject<T>(localize('json.nugget.error.access', 'Request to {0} failed: {1}', url, getErrorStatusDescription(error.status)));
|
return Promise.reject<T>(localize('json.nugget.error.access', 'Request to {0} failed: {1}', url, getErrorStatusDescription(error.status)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {
|
||||||
DocumentRangeFormattingParams, NotificationType, RequestType
|
DocumentRangeFormattingParams, NotificationType, RequestType
|
||||||
} from 'vscode-languageserver';
|
} from 'vscode-languageserver';
|
||||||
|
|
||||||
import {xhr, IXHROptions, IXHRResponse, configure as configureHttpRequests} from './utils/httpRequest';
|
import {xhr, XHROptions, XHRResponse, configure as configureHttpRequests} from 'request-light';
|
||||||
import path = require('path');
|
import path = require('path');
|
||||||
import fs = require('fs');
|
import fs = require('fs');
|
||||||
import URI from './utils/uri';
|
import URI from './utils/uri';
|
||||||
|
@ -22,12 +22,10 @@ import {JSONSchemaService, ISchemaAssociations} from './jsonSchemaService';
|
||||||
import {parse as parseJSON, ObjectASTNode, JSONDocument} from './jsonParser';
|
import {parse as parseJSON, ObjectASTNode, JSONDocument} from './jsonParser';
|
||||||
import {JSONCompletion} from './jsonCompletion';
|
import {JSONCompletion} from './jsonCompletion';
|
||||||
import {JSONHover} from './jsonHover';
|
import {JSONHover} from './jsonHover';
|
||||||
import {IJSONSchema} from './json-toolbox/jsonSchema';
|
import {IJSONSchema} from './jsonSchema';
|
||||||
import {JSONDocumentSymbols} from './jsonDocumentSymbols';
|
import {JSONDocumentSymbols} from './jsonDocumentSymbols';
|
||||||
import {format as formatJSON} from './jsonFormatter';
|
import {format as formatJSON} from './jsonFormatter';
|
||||||
import {schemaContributions} from './configuration';
|
import {schemaContributions} from './configuration';
|
||||||
import {BowerJSONContribution} from './jsoncontributions/bowerJSONContribution';
|
|
||||||
import {PackageJSONContribution} from './jsoncontributions/packageJSONContribution';
|
|
||||||
import {ProjectJSONContribution} from './jsoncontributions/projectJSONContribution';
|
import {ProjectJSONContribution} from './jsoncontributions/projectJSONContribution';
|
||||||
import {GlobPatternContribution} from './jsoncontributions/globPatternContribution';
|
import {GlobPatternContribution} from './jsoncontributions/globPatternContribution';
|
||||||
import {FileAssociationContribution} from './jsoncontributions/fileAssociationContribution';
|
import {FileAssociationContribution} from './jsoncontributions/fileAssociationContribution';
|
||||||
|
@ -94,10 +92,10 @@ let telemetry = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let request = (options: IXHROptions): Thenable<IXHRResponse> => {
|
let request = (options: XHROptions): Thenable<XHRResponse> => {
|
||||||
if (Strings.startsWith(options.url, 'file://')) {
|
if (Strings.startsWith(options.url, 'file://')) {
|
||||||
let fsPath = URI.parse(options.url).fsPath;
|
let fsPath = URI.parse(options.url).fsPath;
|
||||||
return new Promise<IXHRResponse>((c, e) => {
|
return new Promise<XHRResponse>((c, e) => {
|
||||||
fs.readFile(fsPath, 'UTF-8', (err, result) => {
|
fs.readFile(fsPath, 'UTF-8', (err, result) => {
|
||||||
err ? e({ responseText: '', status: 404 }) : c({ responseText: result.toString(), status: 200 });
|
err ? e({ responseText: '', status: 404 }) : c({ responseText: result.toString(), status: 200 });
|
||||||
});
|
});
|
||||||
|
@ -120,8 +118,6 @@ let request = (options: IXHROptions): Thenable<IXHRResponse> => {
|
||||||
|
|
||||||
let contributions = [
|
let contributions = [
|
||||||
new ProjectJSONContribution(request),
|
new ProjectJSONContribution(request),
|
||||||
new PackageJSONContribution(request),
|
|
||||||
new BowerJSONContribution(request),
|
|
||||||
new GlobPatternContribution(),
|
new GlobPatternContribution(),
|
||||||
filesAssociationContribution
|
filesAssociationContribution
|
||||||
];
|
];
|
||||||
|
|
|
@ -7,17 +7,17 @@
|
||||||
import assert = require('assert');
|
import assert = require('assert');
|
||||||
import Parser = require('../jsonParser');
|
import Parser = require('../jsonParser');
|
||||||
import SchemaService = require('../jsonSchemaService');
|
import SchemaService = require('../jsonSchemaService');
|
||||||
import JsonSchema = require('../json-toolbox/jsonSchema');
|
import JsonSchema = require('../jsonSchema');
|
||||||
import {JSONCompletion} from '../jsonCompletion';
|
import {JSONCompletion} from '../jsonCompletion';
|
||||||
import {IXHROptions, IXHRResponse} from '../utils/httpRequest';
|
import {XHROptions, XHRResponse} from 'request-light';
|
||||||
|
|
||||||
import {CompletionItem, CompletionItemKind, CompletionOptions, ITextDocument, TextDocumentIdentifier, TextDocumentPosition, Range, Position, TextEdit} from 'vscode-languageserver';
|
import {CompletionItem, CompletionItemKind, CompletionOptions, ITextDocument, TextDocumentIdentifier, TextDocumentPosition, Range, Position, TextEdit} from 'vscode-languageserver';
|
||||||
import {applyEdits} from './textEditSupport';
|
import {applyEdits} from './textEditSupport';
|
||||||
|
|
||||||
suite('JSON Completion', () => {
|
suite('JSON Completion', () => {
|
||||||
|
|
||||||
var requestService = function(options: IXHROptions): Promise<IXHRResponse> {
|
var requestService = function(options: XHROptions): Promise<XHRResponse> {
|
||||||
return Promise.reject<IXHRResponse>({ responseText: '', status: 404 });
|
return Promise.reject<XHRResponse>({ responseText: '', status: 404 });
|
||||||
}
|
}
|
||||||
|
|
||||||
var assertSuggestion = function(completions: CompletionItem[], label: string, documentation?: string, document?: ITextDocument, resultText?: string) {
|
var assertSuggestion = function(completions: CompletionItem[], label: string, documentation?: string, document?: ITextDocument, resultText?: string) {
|
||||||
|
|
|
@ -7,9 +7,8 @@
|
||||||
import assert = require('assert');
|
import assert = require('assert');
|
||||||
import Parser = require('../jsonParser');
|
import Parser = require('../jsonParser');
|
||||||
import SchemaService = require('../jsonSchemaService');
|
import SchemaService = require('../jsonSchemaService');
|
||||||
import JsonSchema = require('../json-toolbox/jsonSchema');
|
import JsonSchema = require('../jsonSchema');
|
||||||
import {JSONCompletion} from '../jsonCompletion';
|
import {JSONCompletion} from '../jsonCompletion';
|
||||||
import {IXHROptions, IXHRResponse} from '../utils/httpRequest';
|
|
||||||
import {JSONDocumentSymbols} from '../jsonDocumentSymbols';
|
import {JSONDocumentSymbols} from '../jsonDocumentSymbols';
|
||||||
|
|
||||||
import {SymbolInformation, SymbolKind, TextDocumentIdentifier, ITextDocument, TextDocumentPosition, Range, Position, TextEdit} from 'vscode-languageserver';
|
import {SymbolInformation, SymbolKind, TextDocumentIdentifier, ITextDocument, TextDocumentPosition, Range, Position, TextEdit} from 'vscode-languageserver';
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import Json = require('../json-toolbox/json');
|
import Json = require('jsonc-parser');
|
||||||
import {ITextDocument, DocumentFormattingParams, Range, Position, FormattingOptions, TextEdit} from 'vscode-languageserver';
|
import {ITextDocument, DocumentFormattingParams, Range, Position, FormattingOptions, TextEdit} from 'vscode-languageserver';
|
||||||
import Formatter = require('../jsonFormatter');
|
import Formatter = require('../jsonFormatter');
|
||||||
import assert = require('assert');
|
import assert = require('assert');
|
||||||
|
|
|
@ -7,9 +7,9 @@
|
||||||
import assert = require('assert');
|
import assert = require('assert');
|
||||||
import Parser = require('../jsonParser');
|
import Parser = require('../jsonParser');
|
||||||
import SchemaService = require('../jsonSchemaService');
|
import SchemaService = require('../jsonSchemaService');
|
||||||
import JsonSchema = require('../json-toolbox/jsonSchema');
|
import JsonSchema = require('../jsonSchema');
|
||||||
import {JSONCompletion} from '../jsonCompletion';
|
import {JSONCompletion} from '../jsonCompletion';
|
||||||
import {IXHROptions, IXHRResponse} from '../utils/httpRequest';
|
import {XHROptions, XHRResponse} from 'request-light';
|
||||||
import {JSONHover} from '../jsonHover';
|
import {JSONHover} from '../jsonHover';
|
||||||
|
|
||||||
import {Hover, ITextDocument, TextDocumentIdentifier, TextDocumentPosition, Range, Position, TextEdit} from 'vscode-languageserver';
|
import {Hover, ITextDocument, TextDocumentIdentifier, TextDocumentPosition, Range, Position, TextEdit} from 'vscode-languageserver';
|
||||||
|
@ -30,8 +30,8 @@ suite('JSON Hover', () => {
|
||||||
return hoverProvider.doHover(document, textDocumentLocation, jsonDoc);
|
return hoverProvider.doHover(document, textDocumentLocation, jsonDoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
var requestService = function(options: IXHROptions): Promise<IXHRResponse> {
|
var requestService = function(options: XHROptions): Promise<XHRResponse> {
|
||||||
return Promise.reject<IXHRResponse>({ responseText: '', status: 404 });
|
return Promise.reject<XHRResponse>({ responseText: '', status: 404 });
|
||||||
}
|
}
|
||||||
|
|
||||||
test('Simple schema', function(testDone) {
|
test('Simple schema', function(testDone) {
|
||||||
|
|
|
@ -1,226 +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 * as assert from 'assert';
|
|
||||||
import { SyntaxKind, createScanner, parse, getLocation, ParseErrorCode, getParseErrorMessage } from '../json-toolbox/json';
|
|
||||||
|
|
||||||
function assertKinds(text:string, ...kinds:SyntaxKind[]):void {
|
|
||||||
var _json = createScanner(text);
|
|
||||||
var kind: SyntaxKind;
|
|
||||||
while((kind = _json.scan()) !== SyntaxKind.EOF) {
|
|
||||||
assert.equal(kind, kinds.shift());
|
|
||||||
}
|
|
||||||
assert.equal(kinds.length, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function assertValidParse(input:string, expected:any) : void {
|
|
||||||
var errors : {error: ParseErrorCode}[] = [];
|
|
||||||
var actual = parse(input, errors);
|
|
||||||
|
|
||||||
if (errors.length !== 0) {
|
|
||||||
assert(false, getParseErrorMessage(errors[0].error));
|
|
||||||
}
|
|
||||||
assert.deepEqual(actual, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
function assertInvalidParse(input:string, expected:any) : void {
|
|
||||||
var errors : {error: ParseErrorCode}[] = [];
|
|
||||||
var actual = parse(input, errors);
|
|
||||||
|
|
||||||
assert(errors.length > 0);
|
|
||||||
assert.deepEqual(actual, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
function assertLocation(input:string, expectedSegments: string[], expectedNodeType: string, expectedCompleteProperty: boolean) : void {
|
|
||||||
var errors : {error: ParseErrorCode}[] = [];
|
|
||||||
var offset = input.indexOf('|');
|
|
||||||
input = input.substring(0, offset) + input.substring(offset+1, input.length);
|
|
||||||
var actual = getLocation(input, offset);
|
|
||||||
assert(actual);
|
|
||||||
assert.deepEqual(actual.segments, expectedSegments, input);
|
|
||||||
assert.equal(actual.previousNode && actual.previousNode.type, expectedNodeType, input);
|
|
||||||
assert.equal(actual.completeProperty, expectedCompleteProperty, input);
|
|
||||||
}
|
|
||||||
|
|
||||||
suite('JSON', () => {
|
|
||||||
test('tokens', () => {
|
|
||||||
assertKinds('{', SyntaxKind.OpenBraceToken);
|
|
||||||
assertKinds('}', SyntaxKind.CloseBraceToken);
|
|
||||||
assertKinds('[', SyntaxKind.OpenBracketToken);
|
|
||||||
assertKinds(']', SyntaxKind.CloseBracketToken);
|
|
||||||
assertKinds(':', SyntaxKind.ColonToken);
|
|
||||||
assertKinds(',', SyntaxKind.CommaToken);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('comments', () => {
|
|
||||||
assertKinds('// this is a comment', SyntaxKind.LineCommentTrivia);
|
|
||||||
assertKinds('// this is a comment\n', SyntaxKind.LineCommentTrivia, SyntaxKind.LineBreakTrivia);
|
|
||||||
assertKinds('/* this is a comment*/', SyntaxKind.BlockCommentTrivia);
|
|
||||||
assertKinds('/* this is a \r\ncomment*/', SyntaxKind.BlockCommentTrivia);
|
|
||||||
assertKinds('/* this is a \ncomment*/', SyntaxKind.BlockCommentTrivia);
|
|
||||||
|
|
||||||
// unexpected end
|
|
||||||
assertKinds('/* this is a', SyntaxKind.BlockCommentTrivia);
|
|
||||||
assertKinds('/* this is a \ncomment', SyntaxKind.BlockCommentTrivia);
|
|
||||||
|
|
||||||
// broken comment
|
|
||||||
assertKinds('/ ttt', SyntaxKind.Unknown, SyntaxKind.Trivia, SyntaxKind.Unknown);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('strings', () => {
|
|
||||||
assertKinds('"test"', SyntaxKind.StringLiteral);
|
|
||||||
assertKinds('"\\""', SyntaxKind.StringLiteral);
|
|
||||||
assertKinds('"\\/"', SyntaxKind.StringLiteral);
|
|
||||||
assertKinds('"\\b"', SyntaxKind.StringLiteral);
|
|
||||||
assertKinds('"\\f"', SyntaxKind.StringLiteral);
|
|
||||||
assertKinds('"\\n"', SyntaxKind.StringLiteral);
|
|
||||||
assertKinds('"\\r"', SyntaxKind.StringLiteral);
|
|
||||||
assertKinds('"\\t"', SyntaxKind.StringLiteral);
|
|
||||||
assertKinds('"\\v"', SyntaxKind.StringLiteral);
|
|
||||||
assertKinds('"\u88ff"', SyntaxKind.StringLiteral);
|
|
||||||
|
|
||||||
// unexpected end
|
|
||||||
assertKinds('"test', SyntaxKind.StringLiteral);
|
|
||||||
assertKinds('"test\n"', SyntaxKind.StringLiteral, SyntaxKind.LineBreakTrivia, SyntaxKind.StringLiteral);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('numbers', () => {
|
|
||||||
assertKinds('0', SyntaxKind.NumericLiteral);
|
|
||||||
assertKinds('0.1', SyntaxKind.NumericLiteral);
|
|
||||||
assertKinds('-0.1', SyntaxKind.NumericLiteral);
|
|
||||||
assertKinds('-1', SyntaxKind.NumericLiteral);
|
|
||||||
assertKinds('1', SyntaxKind.NumericLiteral);
|
|
||||||
assertKinds('123456789', SyntaxKind.NumericLiteral);
|
|
||||||
assertKinds('10', SyntaxKind.NumericLiteral);
|
|
||||||
assertKinds('90', SyntaxKind.NumericLiteral);
|
|
||||||
assertKinds('90E+123', SyntaxKind.NumericLiteral);
|
|
||||||
assertKinds('90e+123', SyntaxKind.NumericLiteral);
|
|
||||||
assertKinds('90e-123', SyntaxKind.NumericLiteral);
|
|
||||||
assertKinds('90E-123', SyntaxKind.NumericLiteral);
|
|
||||||
assertKinds('90E123', SyntaxKind.NumericLiteral);
|
|
||||||
assertKinds('90e123', SyntaxKind.NumericLiteral);
|
|
||||||
|
|
||||||
// zero handling
|
|
||||||
assertKinds('01', SyntaxKind.NumericLiteral, SyntaxKind.NumericLiteral);
|
|
||||||
assertKinds('-01', SyntaxKind.NumericLiteral, SyntaxKind.NumericLiteral);
|
|
||||||
|
|
||||||
// unexpected end
|
|
||||||
assertKinds('-', SyntaxKind.Unknown);
|
|
||||||
assertKinds('.0', SyntaxKind.Unknown);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('keywords: true, false, null', () => {
|
|
||||||
assertKinds('true', SyntaxKind.TrueKeyword);
|
|
||||||
assertKinds('false', SyntaxKind.FalseKeyword);
|
|
||||||
assertKinds('null', SyntaxKind.NullKeyword);
|
|
||||||
|
|
||||||
|
|
||||||
assertKinds('true false null',
|
|
||||||
SyntaxKind.TrueKeyword,
|
|
||||||
SyntaxKind.Trivia,
|
|
||||||
SyntaxKind.FalseKeyword,
|
|
||||||
SyntaxKind.Trivia,
|
|
||||||
SyntaxKind.NullKeyword);
|
|
||||||
|
|
||||||
// invalid words
|
|
||||||
assertKinds('nulllll', SyntaxKind.Unknown);
|
|
||||||
assertKinds('True', SyntaxKind.Unknown);
|
|
||||||
assertKinds('foo-bar', SyntaxKind.Unknown);
|
|
||||||
assertKinds('foo bar', SyntaxKind.Unknown, SyntaxKind.Trivia, SyntaxKind.Unknown);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('trivia', () => {
|
|
||||||
assertKinds(' ', SyntaxKind.Trivia);
|
|
||||||
assertKinds(' \t ', SyntaxKind.Trivia);
|
|
||||||
assertKinds(' \t \n \t ', SyntaxKind.Trivia, SyntaxKind.LineBreakTrivia, SyntaxKind.Trivia);
|
|
||||||
assertKinds('\r\n', SyntaxKind.LineBreakTrivia);
|
|
||||||
assertKinds('\r', SyntaxKind.LineBreakTrivia);
|
|
||||||
assertKinds('\n', SyntaxKind.LineBreakTrivia);
|
|
||||||
assertKinds('\n\r', SyntaxKind.LineBreakTrivia, SyntaxKind.LineBreakTrivia);
|
|
||||||
assertKinds('\n \n', SyntaxKind.LineBreakTrivia, SyntaxKind.Trivia, SyntaxKind.LineBreakTrivia);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('parse: literals', () => {
|
|
||||||
|
|
||||||
assertValidParse('true', true);
|
|
||||||
assertValidParse('false', false);
|
|
||||||
assertValidParse('null', null);
|
|
||||||
assertValidParse('"foo"', 'foo');
|
|
||||||
assertValidParse('"\\"-\\\\-\\/-\\b-\\f-\\n-\\r-\\t"', '"-\\-/-\b-\f-\n-\r-\t');
|
|
||||||
assertValidParse('"\\u00DC"', 'Ü');
|
|
||||||
assertValidParse('9', 9);
|
|
||||||
assertValidParse('-9', -9);
|
|
||||||
assertValidParse('0.129', 0.129);
|
|
||||||
assertValidParse('23e3', 23e3);
|
|
||||||
assertValidParse('1.2E+3', 1.2E+3);
|
|
||||||
assertValidParse('1.2E-3', 1.2E-3);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
test('parse: objects', () => {
|
|
||||||
assertValidParse('{}', {});
|
|
||||||
assertValidParse('{ "foo": true }', { foo: true });
|
|
||||||
assertValidParse('{ "bar": 8, "xoo": "foo" }', { bar: 8, xoo: 'foo' });
|
|
||||||
assertValidParse('{ "hello": [], "world": {} }', { hello: [], world: {} });
|
|
||||||
assertValidParse('{ "a": false, "b": true, "c": [ 7.4 ] }', { a: false, b: true, c: [ 7.4 ]});
|
|
||||||
assertValidParse('{ "lineComment": "//", "blockComment": ["/*", "*/"], "brackets": [ ["{", "}"], ["[", "]"], ["(", ")"] ] }', { lineComment: '//', blockComment: ["/*", "*/"], brackets: [ ["{", "}"], ["[", "]"], ["(", ")"] ] });
|
|
||||||
assertValidParse('{ "hello": [], "world": {} }', { hello: [], world: {} });
|
|
||||||
assertValidParse('{ "hello": { "again": { "inside": 5 }, "world": 1 }}', { hello: { again: { inside: 5 }, world: 1 }});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('parse: arrays', () => {
|
|
||||||
assertValidParse('[]', []);
|
|
||||||
assertValidParse('[ [], [ [] ]]', [[], [[]]]);
|
|
||||||
assertValidParse('[ 1, 2, 3 ]', [ 1, 2, 3 ]);
|
|
||||||
assertValidParse('[ { "a": null } ]', [ { a: null } ]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('parse: objects with errors', () => {
|
|
||||||
assertInvalidParse('{,}', {});
|
|
||||||
assertInvalidParse('{ "foo": true, }', { foo: true });
|
|
||||||
assertInvalidParse('{ "bar": 8 "xoo": "foo" }', { bar: 8, xoo: 'foo' });
|
|
||||||
assertInvalidParse('{ ,"bar": 8 }', { bar: 8 });
|
|
||||||
assertInvalidParse('{ ,"bar": 8, "foo" }', { bar: 8 });
|
|
||||||
assertInvalidParse('{ "bar": 8, "foo": }', { bar: 8 });
|
|
||||||
assertInvalidParse('{ 8, "foo": 9 }', { foo: 9 });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('parse: array with errors', () => {
|
|
||||||
assertInvalidParse('[,]', []);
|
|
||||||
assertInvalidParse('[ 1, 2, ]', [ 1, 2]);
|
|
||||||
assertInvalidParse('[ 1 2, 3 ]', [ 1, 2, 3 ]);
|
|
||||||
assertInvalidParse('[ ,1, 2, 3 ]', [ 1, 2, 3 ]);
|
|
||||||
assertInvalidParse('[ ,1, 2, 3, ]', [ 1, 2, 3 ]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('location: properties', () => {
|
|
||||||
assertLocation('|{ "foo": "bar" }', [], void 0, false);
|
|
||||||
assertLocation('{| "foo": "bar" }', [], void 0, true);
|
|
||||||
assertLocation('{ |"foo": "bar" }', ["foo" ], "property", true);
|
|
||||||
assertLocation('{ "foo|": "bar" }', [ "foo" ], "property", true);
|
|
||||||
assertLocation('{ "foo"|: "bar" }', ["foo" ], "property", true);
|
|
||||||
assertLocation('{ "foo": "bar"| }', ["foo" ], "string", false);
|
|
||||||
assertLocation('{ "foo":| "bar" }', ["foo" ], void 0, false);
|
|
||||||
assertLocation('{ "foo": {"bar|": 1, "car": 2 } }', ["foo", "bar" ], "property", true);
|
|
||||||
assertLocation('{ "foo": {"bar": 1|, "car": 3 } }', ["foo", "bar" ], "number", false);
|
|
||||||
assertLocation('{ "foo": {"bar": 1,| "car": 4 } }', ["foo"], void 0, true);
|
|
||||||
assertLocation('{ "foo": {"bar": 1, "ca|r": 5 } }', ["foo", "car" ], "property", true);
|
|
||||||
assertLocation('{ "foo": {"bar": 1, "car": 6| } }', ["foo", "car" ], "number", false);
|
|
||||||
assertLocation('{ "foo": {"bar": 1, "car": 7 }| }', ["foo"], void 0, false);
|
|
||||||
assertLocation('{ "foo": {"bar": 1, "car": 8 },| "goo": {} }', [], void 0, true);
|
|
||||||
assertLocation('{ "foo": {"bar": 1, "car": 9 }, "go|o": {} }', ["goo" ], "property", true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('location: arrays', () => {
|
|
||||||
assertLocation('|["foo", null ]', [], void 0, false);
|
|
||||||
assertLocation('[|"foo", null ]', ["[0]"], "string", false);
|
|
||||||
assertLocation('["foo"|, null ]', ["[0]"], "string", false);
|
|
||||||
assertLocation('["foo",| null ]', ["[1]"], void 0, false);
|
|
||||||
assertLocation('["foo", |null ]', ["[1]"], "null", false);
|
|
||||||
assertLocation('["foo", null,| ]', ["[2]"], void 0, false);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -7,7 +7,7 @@
|
||||||
import assert = require('assert');
|
import assert = require('assert');
|
||||||
import Parser = require('../jsonParser');
|
import Parser = require('../jsonParser');
|
||||||
import SchemaService = require('../jsonSchemaService');
|
import SchemaService = require('../jsonSchemaService');
|
||||||
import JsonSchema = require('../json-toolbox/jsonSchema');
|
import JsonSchema = require('../jsonSchema');
|
||||||
|
|
||||||
suite('JSON Parser', () => {
|
suite('JSON Parser', () => {
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,12 @@
|
||||||
|
|
||||||
import assert = require('assert');
|
import assert = require('assert');
|
||||||
import SchemaService = require('../jsonSchemaService');
|
import SchemaService = require('../jsonSchemaService');
|
||||||
import JsonSchema = require('../json-toolbox/jsonSchema');
|
import JsonSchema = require('../jsonSchema');
|
||||||
import Json = require('../json-toolbox/json');
|
import Json = require('jsonc-parser');
|
||||||
import Parser = require('../jsonParser');
|
import Parser = require('../jsonParser');
|
||||||
import fs = require('fs');
|
import fs = require('fs');
|
||||||
import path = require('path');
|
import path = require('path');
|
||||||
import {IXHROptions, IXHRResponse} from '../utils/httpRequest';
|
import {XHROptions, XHRResponse} from 'request-light';
|
||||||
|
|
||||||
|
|
||||||
suite('JSON Schema', () => {
|
suite('JSON Schema', () => {
|
||||||
|
@ -26,21 +26,21 @@ suite('JSON Schema', () => {
|
||||||
'http://schema.management.azure.com/schemas/2015-08-01/Microsoft.Compute.json': 'Microsoft.Compute.json'
|
'http://schema.management.azure.com/schemas/2015-08-01/Microsoft.Compute.json': 'Microsoft.Compute.json'
|
||||||
}
|
}
|
||||||
|
|
||||||
var requestServiceMock = function (options:IXHROptions) : Promise<IXHRResponse> {
|
var requestServiceMock = function (options:XHROptions) : Promise<XHRResponse> {
|
||||||
var uri = options.url;
|
var uri = options.url;
|
||||||
if (uri.length && uri[uri.length - 1] === '#') {
|
if (uri.length && uri[uri.length - 1] === '#') {
|
||||||
uri = uri.substr(0, uri.length - 1);
|
uri = uri.substr(0, uri.length - 1);
|
||||||
}
|
}
|
||||||
var fileName = fixureDocuments[uri];
|
var fileName = fixureDocuments[uri];
|
||||||
if (fileName) {
|
if (fileName) {
|
||||||
return new Promise<IXHRResponse>((c, e) => {
|
return new Promise<XHRResponse>((c, e) => {
|
||||||
var fixturePath = path.join(__dirname, '../../src/test/fixtures', fileName);
|
var fixturePath = path.join(__dirname, '../../src/test/fixtures', fileName);
|
||||||
fs.readFile(fixturePath, 'UTF-8', (err, result) => {
|
fs.readFile(fixturePath, 'UTF-8', (err, result) => {
|
||||||
err ? e({ responseText: '', status: 404 }) : c({ responseText: result.toString(), status: 200 })
|
err ? e({ responseText: '', status: 404 }) : c({ responseText: result.toString(), status: 200 })
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return Promise.reject<IXHRResponse>({ responseText: '', status: 404 });
|
return Promise.reject<XHRResponse>({ responseText: '', status: 404 });
|
||||||
}
|
}
|
||||||
|
|
||||||
test('Resolving $refs', function(testDone) {
|
test('Resolving $refs', function(testDone) {
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
declare module 'https-proxy-agent' {
|
|
||||||
|
|
||||||
import * as tls from 'tls';
|
|
||||||
|
|
||||||
interface IHttpsProxyAgentOptions extends tls.ConnectionOptions {
|
|
||||||
host: string;
|
|
||||||
port: number;
|
|
||||||
auth?: string;
|
|
||||||
secureProxy?: boolean;
|
|
||||||
secureEndpoint?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
class HttpsProxyAgent {
|
|
||||||
constructor(proxy: string);
|
|
||||||
constructor(opts: IHttpsProxyAgentOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
export = HttpsProxyAgent;
|
|
||||||
}
|
|
|
@ -1,174 +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 { parse as parseUrl } from 'url';
|
|
||||||
import { getProxyAgent } from './proxy';
|
|
||||||
import https = require('https');
|
|
||||||
import http = require('http');
|
|
||||||
|
|
||||||
import * as nls from 'vscode-nls';
|
|
||||||
const localize = nls.loadMessageBundle();
|
|
||||||
|
|
||||||
export interface IXHROptions {
|
|
||||||
type?: string;
|
|
||||||
url?: string;
|
|
||||||
user?: string;
|
|
||||||
password?: string;
|
|
||||||
headers?: any;
|
|
||||||
timeout?: number;
|
|
||||||
data?: any;
|
|
||||||
agent?: any;
|
|
||||||
strictSSL?: boolean;
|
|
||||||
responseType?: string;
|
|
||||||
followRedirects?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IXHRResponse {
|
|
||||||
responseText: string;
|
|
||||||
status: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
let proxyUrl: string = null;
|
|
||||||
let strictSSL: boolean = true;
|
|
||||||
|
|
||||||
function assign(destination: any, ...sources: any[]): any {
|
|
||||||
sources.forEach(source => Object.keys(source).forEach((key) => destination[key] = source[key]));
|
|
||||||
return destination;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function configure(_proxyUrl: string, _strictSSL: boolean): void {
|
|
||||||
proxyUrl = _proxyUrl;
|
|
||||||
strictSSL = _strictSSL;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function xhr(options: IXHROptions): Promise<IXHRResponse> {
|
|
||||||
const agent = getProxyAgent(options.url, { proxyUrl, strictSSL });
|
|
||||||
options = assign({}, options);
|
|
||||||
options = assign(options, { agent, strictSSL });
|
|
||||||
if (typeof options.followRedirects !== 'number') {
|
|
||||||
options.followRedirects = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
return request(options).then(result => new Promise<IXHRResponse>((c, e) => {
|
|
||||||
let res = result.res;
|
|
||||||
let data: string[] = [];
|
|
||||||
res.on('data', c => data.push(c));
|
|
||||||
res.on('end', () => {
|
|
||||||
if (options.followRedirects > 0 && (res.statusCode >= 300 && res.statusCode <= 303 || res.statusCode === 307)) {
|
|
||||||
let location = res.headers['location'];
|
|
||||||
if (location) {
|
|
||||||
let newOptions = {
|
|
||||||
type: options.type, url: location, user: options.user, password: options.password, responseType: options.responseType, headers: options.headers,
|
|
||||||
timeout: options.timeout, followRedirects: options.followRedirects - 1, data: options.data
|
|
||||||
};
|
|
||||||
xhr(newOptions).then(c, e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let response: IXHRResponse = {
|
|
||||||
responseText: data.join(''),
|
|
||||||
status: res.statusCode
|
|
||||||
};
|
|
||||||
|
|
||||||
if ((res.statusCode >= 200 && res.statusCode < 300) || res.statusCode === 1223) {
|
|
||||||
c(response);
|
|
||||||
} else {
|
|
||||||
e(response);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}), err => {
|
|
||||||
let message: string;
|
|
||||||
|
|
||||||
if (agent) {
|
|
||||||
message = 'Unable to to connect to ' + options.url + ' through a proxy . Error: ' + err.message;
|
|
||||||
} else {
|
|
||||||
message = 'Unable to to connect to ' + options.url + '. Error: ' + err.message;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.reject<IXHRResponse>({
|
|
||||||
responseText: message,
|
|
||||||
status: 404
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IRequestResult {
|
|
||||||
req: http.ClientRequest;
|
|
||||||
res: http.ClientResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
function request(options: IXHROptions): Promise<IRequestResult> {
|
|
||||||
let req: http.ClientRequest;
|
|
||||||
|
|
||||||
return new Promise<IRequestResult>((c, e) => {
|
|
||||||
let endpoint = parseUrl(options.url);
|
|
||||||
|
|
||||||
let opts: https.RequestOptions = {
|
|
||||||
hostname: endpoint.hostname,
|
|
||||||
port: endpoint.port ? parseInt(endpoint.port) : (endpoint.protocol === 'https:' ? 443 : 80),
|
|
||||||
path: endpoint.path,
|
|
||||||
method: options.type || 'GET',
|
|
||||||
headers: options.headers,
|
|
||||||
agent: options.agent,
|
|
||||||
rejectUnauthorized: (typeof options.strictSSL === 'boolean') ? options.strictSSL : true
|
|
||||||
};
|
|
||||||
|
|
||||||
if (options.user && options.password) {
|
|
||||||
opts.auth = options.user + ':' + options.password;
|
|
||||||
}
|
|
||||||
|
|
||||||
let protocol = endpoint.protocol === 'https:' ? https : http;
|
|
||||||
req = protocol.request(opts, (res: http.ClientResponse) => {
|
|
||||||
if (res.statusCode >= 300 && res.statusCode < 400 && options.followRedirects && options.followRedirects > 0 && res.headers['location']) {
|
|
||||||
c(<any> request(assign({}, options, {
|
|
||||||
url: res.headers['location'],
|
|
||||||
followRedirects: options.followRedirects - 1
|
|
||||||
})));
|
|
||||||
} else {
|
|
||||||
c({ req, res });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
req.on('error', e);
|
|
||||||
|
|
||||||
if (options.timeout) {
|
|
||||||
req.setTimeout(options.timeout);
|
|
||||||
}
|
|
||||||
if (options.data) {
|
|
||||||
req.write(options.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
req.end();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getErrorStatusDescription(status: number) : string {
|
|
||||||
if (status < 400) {
|
|
||||||
return void 0;
|
|
||||||
}
|
|
||||||
switch (status) {
|
|
||||||
case 400: return localize('status.400', 'Bad request. The request cannot be fulfilled due to bad syntax.');
|
|
||||||
case 401: return localize('status.401', 'Unauthorized. The server is refusing to respond.');
|
|
||||||
case 403: return localize('status.403', 'Forbidden. The server is refusing to respond.');
|
|
||||||
case 404: return localize('status.404', 'Not Found. The requested location could not be found.');
|
|
||||||
case 405: return localize('status.405', 'Method not allowed. A request was made using a request method not supported by that location.');
|
|
||||||
case 406: return localize('status.406', 'Not Acceptable. The server can only generate a response that is not accepted by the client.');
|
|
||||||
case 407: return localize('status.407', 'Proxy Authentication Required. The client must first authenticate itself with the proxy.');
|
|
||||||
case 408: return localize('status.408', 'Request Timeout. The server timed out waiting for the request.');
|
|
||||||
case 409: return localize('status.409', 'Conflict. The request could not be completed because of a conflict in the request.');
|
|
||||||
case 410: return localize('status.410', 'Gone. The requested page is no longer available.');
|
|
||||||
case 411: return localize('status.411', 'Length Required. The "Content-Length" is not defined.');
|
|
||||||
case 412: return localize('status.412', 'Precondition Failed. The precondition given in the request evaluated to false by the server.');
|
|
||||||
case 413: return localize('status.413', 'Request Entity Too Large. The server will not accept the request, because the request entity is too large.');
|
|
||||||
case 414: return localize('status.414', 'Request-URI Too Long. The server will not accept the request, because the URL is too long.');
|
|
||||||
case 415: return localize('status.415', 'Unsupported Media Type. The server will not accept the request, because the media type is not supported.');
|
|
||||||
case 500: return localize('status.500', 'Internal Server Error.');
|
|
||||||
case 501: return localize('status.501', 'Not Implemented. The server either does not recognize the request method, or it lacks the ability to fulfill the request.');
|
|
||||||
case 503: return localize('status.503', 'Service Unavailable. The server is currently unavailable (overloaded or down).');
|
|
||||||
default: return localize('status.416', 'HTTP status code {0}', status);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +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 function localize2(key: string, message: string, ...formatArgs: any[]) {
|
|
||||||
if (formatArgs.length > 0) {
|
|
||||||
return message.replace(/\{(\d+)\}/g, function(match, rest) {
|
|
||||||
var index = rest[0];
|
|
||||||
return typeof formatArgs[index] !== 'undefined' ? formatArgs[index] : match;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return message;
|
|
||||||
}
|
|
|
@ -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';
|
|
||||||
|
|
||||||
import { Url, parse as parseUrl } from 'url';
|
|
||||||
import HttpProxyAgent = require('http-proxy-agent');
|
|
||||||
import HttpsProxyAgent = require('https-proxy-agent');
|
|
||||||
|
|
||||||
function getSystemProxyURI(requestURL: Url): string {
|
|
||||||
if (requestURL.protocol === 'http:') {
|
|
||||||
return process.env.HTTP_PROXY || process.env.http_proxy || null;
|
|
||||||
} else if (requestURL.protocol === 'https:') {
|
|
||||||
return process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IOptions {
|
|
||||||
proxyUrl?: string;
|
|
||||||
strictSSL?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getProxyAgent(rawRequestURL: string, options: IOptions = {}): any {
|
|
||||||
const requestURL = parseUrl(rawRequestURL);
|
|
||||||
const proxyURL = options.proxyUrl || getSystemProxyURI(requestURL);
|
|
||||||
|
|
||||||
if (!proxyURL) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const proxyEndpoint = parseUrl(proxyURL);
|
|
||||||
|
|
||||||
if (!/^https?:$/.test(proxyEndpoint.protocol)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const opts = {
|
|
||||||
host: proxyEndpoint.hostname,
|
|
||||||
port: Number(proxyEndpoint.port),
|
|
||||||
auth: proxyEndpoint.auth,
|
|
||||||
rejectUnauthorized: (typeof options.strictSSL === 'boolean') ? options.strictSSL : true
|
|
||||||
};
|
|
||||||
|
|
||||||
return requestURL.protocol === 'http:' ? new HttpProxyAgent(opts) : new HttpsProxyAgent(opts);
|
|
||||||
}
|
|
|
@ -13,7 +13,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "mocha",
|
"test": "mocha",
|
||||||
"preinstall": "node build/npm/preinstall.js",
|
"preinstall": "node build/npm/preinstall.js",
|
||||||
"postinstall": "npm --prefix extensions/vscode-api-tests/ install extensions/vscode-api-tests/ && npm --prefix extensions/vscode-colorize-tests/ install extensions/vscode-colorize-tests/ && npm --prefix extensions/json/ install extensions/json/ && npm --prefix extensions/typescript/ install extensions/typescript/ && npm --prefix extensions/php/ install extensions/php/",
|
"postinstall": "npm --prefix extensions/vscode-api-tests/ install extensions/vscode-api-tests/ && npm --prefix extensions/vscode-colorize-tests/ install extensions/vscode-colorize-tests/ && npm --prefix extensions/json/ install extensions/json/ && npm --prefix extensions/typescript/ install extensions/typescript/ && npm --prefix extensions/php/ install extensions/php/ && npm --prefix extensions/javascript/ install extensions/javascript/",
|
||||||
"watch": "gulp watch"
|
"watch": "gulp watch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
Loading…
Reference in a new issue