@@ -14,6 +14,9 @@ import * as nls from 'vscode-nls';
1414const localize = nls . loadMessageBundle ( ) ;
1515
1616const LIMIT = 40 ;
17+ const SCOPED_LIMIT = 250 ;
18+
19+ const USER_AGENT = 'Visual Studio Code' ;
1720
1821export class PackageJSONContribution implements IJSONContribution {
1922
@@ -24,6 +27,8 @@ export class PackageJSONContribution implements IJSONContribution {
2427 'shelljs' , 'gulp' , 'yargs' , 'browserify' , 'minimatch' , 'react' , 'less' , 'prompt' , 'inquirer' , 'ws' , 'event-stream' , 'inherits' , 'mysql' , 'esprima' ,
2528 'jsdom' , 'stylus' , 'when' , 'readable-stream' , 'aws-sdk' , 'concat-stream' , 'chai' , 'Thenable' , 'wrench' ] ;
2629
30+ private knownScopes = [ '@types' , '@angular' ] ;
31+
2732 public getDocumentSelector ( ) : DocumentSelector {
2833 return [ { language : 'json' , pattern : '**/package.json' } ] ;
2934 }
@@ -58,10 +63,15 @@ export class PackageJSONContribution implements IJSONContribution {
5863 if ( ( location . matches ( [ 'dependencies' ] ) || location . matches ( [ 'devDependencies' ] ) || location . matches ( [ 'optionalDependencies' ] ) || location . matches ( [ 'peerDependencies' ] ) ) ) {
5964 let queryUrl : string ;
6065 if ( currentWord . length > 0 ) {
66+ if ( currentWord [ 0 ] === '@' ) {
67+ return this . collectScopedPackages ( currentWord , addValue , isLast , collector ) ;
68+ }
69+
6170 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' ;
6271
6372 return this . xhr ( {
64- url : queryUrl
73+ url : queryUrl ,
74+ agent : USER_AGENT
6575 } ) . then ( ( success ) => {
6676 if ( success . status === 200 ) {
6777 try {
@@ -119,13 +129,81 @@ export class PackageJSONContribution implements IJSONContribution {
119129 proposal . documentation = '' ;
120130 collector . add ( proposal ) ;
121131 } ) ;
132+ this . collectScopedPackages ( currentWord , addValue , isLast , collector ) ;
122133 collector . setAsIncomplete ( ) ;
123134 return Promise . resolve ( null ) ;
124135 }
125136 }
126137 return null ;
127138 }
128139
140+ private collectScopedPackages ( currentWord : string , addValue : boolean , isLast : boolean , collector : ISuggestionsCollector ) : Thenable < any > {
141+ let segments = currentWord . split ( '/' ) ;
142+ if ( segments . length === 1 ) {
143+ for ( let scope of this . knownScopes ) {
144+ const proposal = new CompletionItem ( scope ) ;
145+ proposal . kind = CompletionItemKind . Property ;
146+ proposal . insertText = new SnippetString ( ) . appendText ( `"${ scope } /` ) . appendTabstop ( ) . appendText ( '"' ) ;
147+ proposal . filterText = JSON . stringify ( scope ) ;
148+ proposal . documentation = '' ;
149+ proposal . command = {
150+ title : '' ,
151+ command : 'editor.action.triggerSuggest'
152+ } ;
153+ collector . add ( proposal ) ;
154+ }
155+ } else if ( segments . length === 2 && segments [ 0 ] . length > 1 ) {
156+ let scope = segments [ 0 ] . substr ( 1 ) ;
157+ let queryUrl = `https://registry.npmjs.org/-/v1/search?text=scope:${ scope } %20${ segments [ 1 ] } &size=${ SCOPED_LIMIT } &popularity=1.0` ;
158+ return this . xhr ( {
159+ url : queryUrl ,
160+ agent : USER_AGENT
161+ } ) . then ( ( success ) => {
162+ if ( success . status === 200 ) {
163+ try {
164+ const obj = JSON . parse ( success . responseText ) ;
165+ if ( obj && Array . isArray ( obj . objects ) ) {
166+ const objects = < { package : { name : string ; version : string , description : string ; } } [ ] > obj . objects ;
167+ for ( let object of objects ) {
168+ if ( object . package && object . package . name ) {
169+ const name = object . package . name ;
170+ const insertText = new SnippetString ( ) . appendText ( JSON . stringify ( name ) ) ;
171+ if ( addValue ) {
172+ insertText . appendText ( ': "' ) ;
173+ if ( object . package . version ) {
174+ insertText . appendVariable ( 'version' , object . package . version ) ;
175+ } else {
176+ insertText . appendTabstop ( ) ;
177+ }
178+ insertText . appendText ( '"' ) ;
179+ if ( ! isLast ) {
180+ insertText . appendText ( ',' ) ;
181+ }
182+ }
183+ const proposal = new CompletionItem ( name ) ;
184+ proposal . kind = CompletionItemKind . Property ;
185+ proposal . insertText = insertText ;
186+ proposal . filterText = JSON . stringify ( name ) ;
187+ proposal . documentation = object . package . description || '' ;
188+ collector . add ( proposal ) ;
189+ }
190+ }
191+ if ( objects . length === SCOPED_LIMIT ) {
192+ collector . setAsIncomplete ( ) ;
193+ }
194+ }
195+ } catch ( e ) {
196+ // ignore
197+ }
198+ } else {
199+ collector . error ( localize ( 'json.npm.error.repoaccess' , 'Request to the NPM repository failed: {0}' , success . responseText ) ) ;
200+ }
201+ return null ;
202+ } ) ;
203+ }
204+ return Promise . resolve ( null ) ;
205+ }
206+
129207 public collectValueSuggestions (
130208 _fileName : string ,
131209 location : Location ,
0 commit comments