Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] - csproj intellisense #1214

Closed
wants to merge 12 commits into from
4 changes: 2 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
],
"stopOnEntry": false,
"sourceMaps": true,
"outDir": "${workspaceRoot}/out/src"
"outFiles": ["${workspaceRoot}/out/src"]
},
{
"name": "Launch Tests",
Expand All @@ -21,7 +21,7 @@
"args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test" ],
"stopOnEntry": false,
"sourceMaps": true,
"outDir": "${workspaceRoot}/out/test"
"outFiles": ["${workspaceRoot}/out/test"]
}
]
}
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ The C# extension now supports basic debugging capabilities! See http://aka.ms/vs
### Development

First install:
* Node.js (newer than 4.3.1)
* Npm (newer 2.14.12)
* Node.js (6.9.5 or newer)
* Npm (3.10.2 or newer)

To **run and develop** do the following:

Expand Down
5 changes: 5 additions & 0 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,8 @@ gulp.task('tslint', () => {
emitError: false
}))
});

gulp.task('testdata', () => {
gulp.src('./test/data/*.*')
.pipe(gulp.dest('./out/test/data'));
})
12 changes: 11 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"main": "./out/src/main",
"scripts": {
"vscode:prepublish": "tsc -p ./",
"compile": "tsc -p ./ && gulp tslint",
"compile": "tsc -p ./ && gulp tslint && gulp testdata",
"watch": "tsc -watch -p ./",
"test": "node ./node_modules/vscode/bin/test",
"postinstall": "node ./node_modules/vscode/bin/install"
Expand All @@ -42,23 +42,33 @@
"tmp": "0.0.28",
"vscode-debugprotocol": "^1.6.1",
"vscode-extension-telemetry": "0.0.4",
"xml-parser": "^1.2.1",
"yauzl": "^2.5.0"
},
"devDependencies": {
"@types/chai": "^3.4.34",
"@types/chai-as-promised": "^0.0.29",
"@types/fs-extra": "0.0.35",
"@types/mkdirp": "^0.3.29",
"@types/mocha": "^2.2.32",
"@types/nock": "^8.2.1",
"@types/node": "^6.0.40",
"@types/semver": "^5.3.30",
"@types/sinon": "^1.16.35",
"@types/sinon-chai": "^2.7.27",
"@types/tmp": "0.0.32",
"@types/xml-parser": "^1.2.28",
"chai": "^3.5.0",
"chai-as-promised": "^6.0.0",
"del": "^2.0.2",
"gulp": "^3.9.1",
"gulp-mocha": "^2.1.3",
"gulp-tslint": "^4.3.0",
"mocha": "^2.3.3",
"nock": "^9.0.6",
"plist": "^2.0.1",
"sinon": "^1.17.7",
"sinon-chai": "^2.8.0",
"tslint": "^3.15.1",
"tslint-microsoft-contrib": "^2.0.12",
"typescript": "^2.0.3",
Expand Down
7 changes: 7 additions & 0 deletions src/features/packageReferenceProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

import AbstractSupport from './abstractProvider';

export default class PackageReferenceProvider extends AbstractSupport {

}
243 changes: 243 additions & 0 deletions src/nuget.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
'use strict';

import * as fs from 'fs';
import * as https from 'https';
import { Logger } from './logger';
import * as path from 'path';

import * as proc from 'process';
import { getProxyAgent } from './proxy';
import { parse as parseUrl } from 'url';
import * as xmlParser from 'xml-parser';

export interface PackageSource {
source: string;
indexUrl: string;
}

export interface NuGetService {
autoCompleteService: string[];
packageRegistrationService: string;
}

export class NuGetClient{

constructor(private configXml: string, private logger: Logger) { }

public PackageSources: PackageSource[] = [];
public NugetServices: { [id: string]: NuGetService } = {};

public ReadConfigFromFile(): Promise<string> {
let pathToConfig: string[] = [];
if (proc.platform === 'win32') {
pathToConfig.push(path.join(proc.env.AppData, 'NuGet', 'NuGet.config'));
// push machine wide config paths
} else {
// found this on StackOverflow (http://stackoverflow.com/questions/30076371/mac-osx-mono-nuget-how-to-set-up-a-default-appdata-nuget-nuget-config)
// and this http://gunnarpeipman.com/2014/10/running-asp-net-5-on-linux/
// TODO: verify the file actually resides there
pathToConfig.push(path.join('~', '.config', 'NuGet', 'NuGet.config'));
}
return new Promise<string>((resolve, reject) => {
for (let i = 0; i < pathToConfig.length; i++) {
if (fs.existsSync(pathToConfig[i])) {
fs.readFile(pathToConfig[i], 'utf-8', (err, data) => {
if (err) {
this.logger.append('Reading ' + pathToConfig + 'failed with error ' + err);
this.configXml = '';
}
if (data.length > 0) {
this.logger.append('Using active package sources from ' + pathToConfig[i]);
this.configXml = data;
resolve(this.configXml);
return;
}
});
}
}
this.logger.append('No config file found.');
resolve('');
});
}

public UpdatePackageSourcesFromConfig(): Promise<boolean> {
let packageSourcesUpdated = false;
return new Promise<boolean>((resolve, reject) => {
if (this.configXml.length === 0 && this.PackageSources.length === 0) {
this.logger.append('No config and no package sources available. Using nuget.org as default');
let nugetOrg: PackageSource = { source: 'nuget.org', indexUrl: 'https://api.nuget.org/v3/index.json' };
this.PackageSources.push(nugetOrg);
packageSourcesUpdated = true;
} else {
let nuget = xmlParser(this.configXml);
let configuration = nuget.root;
let aps = configuration.children.find(n => n.name === 'activePackageSource');
aps.children.forEach(aps => {
let psIndex = this.PackageSources.findIndex(ps => ps.source === aps.attributes['key']);
if (psIndex != -1) {
if (this.PackageSources[psIndex].indexUrl != aps.attributes['value']) {
this.logger.append('Updating the indexUrl of existing package source ' + aps.attributes['key']);
this.PackageSources[psIndex].indexUrl = aps.attributes['value'];
packageSourcesUpdated = true;
}
} else {
this.logger.append('Adding new source ' + aps.attributes['key'] + 'with index URL ' + aps.attributes['value'] + 'to package sources.');
this.PackageSources.push({ source: aps.attributes['key'], indexUrl: aps.attributes['value'] });
packageSourcesUpdated = true;
}
});
}
resolve(packageSourcesUpdated);
});
}

public UpdateNuGetService(packageSource: PackageSource): Promise<boolean> {
let servicesUpdated = false;
return new Promise<boolean>((resolve, reject) => {
this.UpdatePackageSourcesFromConfig().then(updated => {
if (updated) {
this.logger.append('updating index of ' + packageSource.source);
let url = parseUrl(packageSource.indexUrl);
let options: https.RequestOptions = {
host: url.host,
path: url.path,
agent: getProxyAgent(url, '', true)
};
https.get(options, response => {
let svcIndexString = '';
response.on('data', chunk => {
svcIndexString += chunk;
});
response.on('end', () => {
let feedIndex = JSON.parse(svcIndexString);
let autoCompleteServices: string[] = feedIndex.resources.filter(svc => svc['@type'] === 'SearchAutocompleteService').map(svc => svc['@id']);
let pkgRegSvc: string = feedIndex.resources.find(svc => svc['@type'] === 'PackageBaseAddress/3.0.0')['@id'];
if (this.NugetServices[packageSource.source]) {
if (this.NugetServices[packageSource.source].packageRegistrationService != pkgRegSvc) {
this.NugetServices[packageSource.source].packageRegistrationService = pkgRegSvc;
servicesUpdated = true;
}
if (autoCompleteServices.some(acs => this.NugetServices[packageSource.source].autoCompleteService.find(svc => svc === acs) == undefined)) {
this.NugetServices[packageSource.source].autoCompleteService = autoCompleteServices;
servicesUpdated = true;
}
} else {
this.NugetServices[packageSource.source] = {
autoCompleteService: autoCompleteServices,
packageRegistrationService: pkgRegSvc
};
servicesUpdated = true;
}
resolve(servicesUpdated);
});
response.on('error', (err) => reject('Can\'t connect to package source due to ' + err.message));
});
}
});
});
}

public UpdateNuGetServices(): Promise<void> {
return new Promise<void>((resolve, reject) => {
this.PackageSources.forEach(ps => this.UpdateNuGetService(ps));
resolve();
});
}

public FindPackagesByPartialId(partialPackageId: string, maxResults?: number): Promise<string[]> {
if (!maxResults) {
// this could be used to make the limit configurable via a OmniSharp Setting
maxResults = 20;
}
return new Promise<string[]>((resolve, reject) => {
let packageIds: string[] = [];
let requests: Promise<string[]>[] = [];
this.PackageSources.forEach(ps => {
requests.push(new Promise<string[]>((res, rej) => {
let urlString: string;
if (partialPackageId) {
urlString = this.NugetServices[ps.source].autoCompleteService[0] + '?q=' + partialPackageId + '&take=' + maxResults;
} else {
urlString = this.NugetServices[ps.source].autoCompleteService[0] + '?take=' + maxResults;
}
let url = parseUrl(urlString);
let options: https.RequestOptions = {
host: url.host,
path: url.path,
agent: getProxyAgent(url, '', true)
};
https.get(options, response => {
let json: string = '';
response.on('data', (chunk) => json += chunk);
response.on('end', () => {
let payload = JSON.parse(json);
res(payload.data);
});
response.on('error', (err) => rej('Can\'t query packages from ' + ps + ' due to' + err.message));
});
}));
});
Promise.all(requests).then(payloads => {
payloads.forEach(payload => {
if (packageIds.length === 0) {
packageIds = payload;
} else {
packageIds.concat(payload);
}
});
resolve(packageIds);
}, (reasons) => {
debugger;
});
});
}

public FindVersionsByPackageId(packageId: string, partialVersion?: string): Promise<string[]> {
return new Promise<string[]>((resolve, reject) => {
let versions:string[] = [];
let requests: Promise<string[]>[] = [];
this.PackageSources.forEach(ps => {
requests.push(new Promise<string[]>((res, rej) => {
let urlString = this.NugetServices[ps.source].packageRegistrationService + packageId + '/index.json';
let url = parseUrl(urlString);
let options: https.RequestOptions = {
host: url.host,
path: url.path,
agent: getProxyAgent(url, '', true)
};
https.get(options, response => {
let json = '';
response.on('data', (chunk) => json += chunk);
response.on('end', () => {
let versions: string[] = [];
try {
let payload = JSON.parse(json);
if (partialVersion) {
versions = (<string[]>payload.versions).filter(v => v.startsWith(partialVersion));
} else {
versions = payload.versions;
}
} catch (error) {
versions = ['unknown package'];
}
res(versions);
});
response.on('error', (err) => rej('Can\'t query versions from ' + ps + ' due to ' + err.message));
});
}));
});
Promise.all(requests).then(payloads => {
payloads.forEach(payload => {
if (versions.length === 0) {
versions = payload;
} else {
versions.concat(payload);
}
});
resolve(versions);
}, reasons => {
debugger;
});
});
}
}
11 changes: 11 additions & 0 deletions test/data/AutoCompleteResponseNoId.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"@context":{
"@vocab":"http://schema.nuget.org/schema#"
},
"totalHits":67541,
"lastReopen":"2017-02-15T11:22:56.2233078Z",
"index":"v3-lucene0-v2v3-20170110",
"data":[
"Newtonsoft.Json","NUnit","EntityFramework","jQuery","bootstrap","AutoMapper","log4net","Microsoft.AspNet.Mvc","NUnit.Runners","angularjs"
]
}
11 changes: 11 additions & 0 deletions test/data/AutoCompleteResponsePartialId.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"@context":{
"@vocab":"http://schema.nuget.org/schema#"
},
"totalHits":40,
"lastReopen":"2017-02-15T11:14:37.0834689Z",
"index":"v3-lucene0-v2v3-20170110",
"data":[
"Newtonsoft.Json","Rebus.NewtonsoftJson","HybridDb.NewtonsoftJson","Newtonsoft.Msgpack","Newtonsoft.Json.FSharp","rethinkdb-net-newtonsoft","Rock.Core.Newtonsoft","Newtonsoft.Json.Schema","NewtonsoftJsonExt","Sfa.Core.Newtonsoft.Json","Newtonsoft.JsonResult","Newtonsoft.Json.Glimpse","Unity.Newtonsoft.Json","Newtonsoft.Dson","Pollock.Newtonsoft.Json","Newtonsoft.Json.Interface","Newtonsoft.Json.Net20.dll","Lime.Protocol.Serialization.Newtonsoft","Cronus.Serialization.NewtonsoftJson","Gu.Persist.NewtonsoftJson"
]
}
Loading