@@ -5,6 +5,7 @@ import * as vscode from "vscode";
5
5
import { log , samples } from "qsharp-lang" ;
6
6
import { EventType , sendTelemetryEvent } from "./telemetry" ;
7
7
import { qsharpExtensionId } from "./common" ;
8
+ import registryJson from "./registry.json" ;
8
9
9
10
export async function initProjectCreator ( context : vscode . ExtensionContext ) {
10
11
context . subscriptions . push (
@@ -40,7 +41,7 @@ export async function initProjectCreator(context: vscode.ExtensionContext) {
40
41
41
42
const sample = samples . find ( ( elem ) => elem . title === "Minimal" ) ;
42
43
if ( ! sample ) {
43
- // Should never happen.
44
+ // Should never happen, because we bake this sample in .
44
45
log . error ( "Unable to find the Minimal sample" ) ;
45
46
return ;
46
47
}
@@ -177,23 +178,12 @@ export async function initProjectCreator(context: vscode.ExtensionContext) {
177
178
repo : string ;
178
179
ref : string ;
179
180
path ?: string ; // Optional, defaults to the root of the repo
181
+ refs : undefined ;
180
182
} ;
181
183
} ;
182
184
183
185
type Dependency = LocalProjectRef | GitHubProjectRef ;
184
186
185
- // TODO: Replace with a list of legitimate known Q# projects on GitHub
186
- const githubProjects : { [ name : string ] : GitHubProjectRef } = {
187
- // Add a template to the end of the list users can use to easily add their own
188
- "<id>" : {
189
- github : {
190
- owner : "<owner>" ,
191
- repo : "<project>" ,
192
- ref : "<commit>" ,
193
- } ,
194
- } ,
195
- } ;
196
-
197
187
// Given two directory paths, return the relative path from the first to the second
198
188
function getRelativeDirPath ( from : string , to : string ) : string {
199
189
// Ensure we have something
@@ -265,93 +255,168 @@ export async function initProjectCreator(context: vscode.ExtensionContext) {
265
255
return ;
266
256
}
267
257
268
- // Find all the other Q# projects in the workspace
269
- const projectFiles = (
270
- await vscode . workspace . findFiles ( "**/qsharp.json" )
271
- ) . filter ( ( file ) => file . toString ( ) !== qsharpJsonUri . toString ( ) ) ;
272
-
273
- const projectChoices : Array < { name : string ; ref : Dependency } > = [ ] ;
274
-
275
- projectFiles . forEach ( ( file ) => {
276
- const dirName = file . path . slice ( 0 , - "/qsharp.json" . length ) ;
277
- const relPath = getRelativeDirPath ( qsharpJsonDir . path , dirName ) ;
278
- projectChoices . push ( {
279
- name : dirName . slice ( dirName . lastIndexOf ( "/" ) + 1 ) ,
280
- ref : {
281
- path : relPath ,
282
- } ,
283
- } ) ;
284
- } ) ;
285
-
286
- Object . keys ( githubProjects ) . forEach ( ( name ) => {
287
- projectChoices . push ( {
288
- name : name ,
289
- ref : githubProjects [ name ] ,
290
- } ) ;
291
- } ) ;
292
-
293
- // Convert any spaces, dashes, dots, tildes, or quotes in project names
294
- // to underscores. (Leave more 'exotic' non-identifier patterns to the user to fix)
295
- //
296
- // Note: At some point we may want to detect/avoid duplicate names, e.g. if the user already
297
- // references a project via 'foo', and they add a reference to a 'foo' on GitHub or in another dir.
298
- projectChoices . forEach (
299
- ( val , idx , arr ) =>
300
- ( arr [ idx ] . name = val . name . replace ( / [ - " ' . ~ ] / g, "_" ) ) ,
301
- ) ;
302
-
303
- const folderIcon = new vscode . ThemeIcon ( "folder" ) ;
304
- const githubIcon = new vscode . ThemeIcon ( "github" ) ;
305
-
306
- // Ask the user to pick a project to add as a reference
307
- const projectChoice = await vscode . window . showQuickPick (
308
- projectChoices . map ( ( choice ) => {
309
- if ( "github" in choice . ref ) {
310
- return {
311
- label : choice . name ,
312
- detail : `github://${ choice . ref . github . owner } /${ choice . ref . github . repo } #${ choice . ref . github . ref } ` ,
313
- iconPath : githubIcon ,
314
- ref : choice . ref ,
315
- } ;
316
- } else {
317
- return {
318
- label : choice . name ,
319
- detail : choice . ref . path ,
320
- iconPath : folderIcon ,
321
- ref : choice . ref ,
322
- } ;
323
- }
324
- } ) ,
325
- { placeHolder : "Pick a project to add as a reference" } ,
258
+ const importChoice = await vscode . window . showQuickPick (
259
+ [ "Import from GitHub" , "Import from local directory" ] ,
260
+ { placeHolder : "Pick a source to import from" } ,
326
261
) ;
327
262
328
- if ( ! projectChoice ) {
329
- log . info ( "User cancelled project choice" ) ;
263
+ if ( ! importChoice ) {
264
+ log . info ( "User cancelled import choice" ) ;
330
265
return ;
331
266
}
332
267
333
- log . info ( "User picked project: " , projectChoice ) ;
334
-
335
- if ( ! manifestObj [ "dependencies" ] ) manifestObj [ "dependencies" ] = { } ;
336
- manifestObj [ "dependencies" ] [ projectChoice . label ] = projectChoice . ref ;
337
-
338
- // Apply the edits to the qsharp.json
339
- const edit = new vscode . WorkspaceEdit ( ) ;
340
- edit . replace (
341
- qsharpJsonUri ,
342
- new vscode . Range ( 0 , 0 , qsharpJsonDoc . lineCount , 0 ) ,
343
- JSON . stringify ( manifestObj , null , 2 ) ,
344
- ) ;
345
- if ( ! ( await vscode . workspace . applyEdit ( edit ) ) ) {
346
- vscode . window . showErrorMessage (
347
- "Unable to update the qsharp.json file. Check the file is writable" ,
268
+ if ( importChoice === "Import from GitHub" ) {
269
+ await importPackage ( qsharpJsonDoc , qsharpJsonUri , manifestObj , true ) ;
270
+ } else {
271
+ await importPackage (
272
+ qsharpJsonDoc ,
273
+ qsharpJsonUri ,
274
+ manifestObj ,
275
+ false ,
276
+ qsharpJsonDir ,
348
277
) ;
349
- return ;
350
278
}
351
-
352
- // Bring the qsharp.json to the front for the user to save
353
- await vscode . window . showTextDocument ( qsharpJsonDoc ) ;
354
279
} ,
355
280
) ,
356
281
) ;
282
+
283
+ async function importPackage (
284
+ qsharpJsonDoc : vscode . TextDocument ,
285
+ qsharpJsonUri : vscode . Uri ,
286
+ manifestObj : any ,
287
+ isGitHub : boolean ,
288
+ qsharpJsonDir ?: vscode . Uri ,
289
+ ) {
290
+ let dependencyRef : Dependency ;
291
+ let label : string ;
292
+
293
+ if ( isGitHub ) {
294
+ const packageChoice = await vscode . window . showQuickPick (
295
+ registryJson . knownPackages . map (
296
+ ( pkg : { name : string ; description : string ; dependency : object } ) => ( {
297
+ label : pkg . name ,
298
+ description : pkg . description ,
299
+ } ) ,
300
+ ) ,
301
+ { placeHolder : "Pick a package to import" } ,
302
+ ) ;
303
+
304
+ if ( ! packageChoice ) {
305
+ log . info ( "User cancelled package choice" ) ;
306
+ return ;
307
+ }
308
+
309
+ const chosenPackage = registryJson . knownPackages . find (
310
+ ( pkg : { name : string ; description : string ; dependency : object } ) =>
311
+ pkg . name === packageChoice . label ,
312
+ ) ! ;
313
+
314
+ const versionChoice = await vscode . window . showQuickPick (
315
+ chosenPackage . dependency . github . refs . map ( ( { ref, notes } ) => ( {
316
+ label : ref ,
317
+ description : notes ,
318
+ } ) ) ,
319
+ { placeHolder : "Pick a version to import" } ,
320
+ ) ;
321
+
322
+ if ( ! versionChoice ) {
323
+ log . info ( "User cancelled version choice" ) ;
324
+ return ;
325
+ }
326
+
327
+ dependencyRef = {
328
+ github : {
329
+ ref : versionChoice . label ,
330
+ ...chosenPackage . dependency . github ,
331
+ refs : undefined ,
332
+ } ,
333
+ } ;
334
+
335
+ label = packageChoice . label ;
336
+ } else {
337
+ // Find all the other Q# projects in the workspace
338
+ const projectFiles = (
339
+ await vscode . workspace . findFiles ( "**/qsharp.json" )
340
+ ) . filter ( ( file ) => file . toString ( ) !== qsharpJsonUri . toString ( ) ) ;
341
+
342
+ // Convert any spaces, dashes, dots, tildes, or quotes in project names
343
+ // to underscores. (Leave more 'exotic' non-identifier patterns to the user to fix)
344
+ //
345
+ // Note: At some point we may want to detect/avoid duplicate names, e.g. if the user already
346
+ // references a project via 'foo', and they add a reference to a 'foo' on GitHub or in another dir.
347
+
348
+ const projectChoices = projectFiles . map ( ( file ) => {
349
+ // normalize the path using the vscode Uri API
350
+ const dirUri = vscode . Uri . joinPath ( file , ".." ) ;
351
+ const relPath = getRelativeDirPath ( qsharpJsonDir ! . path , dirUri . path ) ;
352
+ return {
353
+ name : dirUri . path . split ( "/" ) . pop ( ) ! ,
354
+ ref : {
355
+ path : relPath ,
356
+ } ,
357
+ } ;
358
+ } ) ;
359
+
360
+ projectChoices . forEach (
361
+ ( val , idx , arr ) => ( arr [ idx ] . name = val . name . replace ( / [ - " ' . ~ ] / g, "_" ) ) ,
362
+ ) ;
363
+
364
+ const folderIcon = new vscode . ThemeIcon ( "folder" ) ;
365
+
366
+ // Ask the user to pick a project to add as a reference
367
+ const projectChoice = await vscode . window . showQuickPick (
368
+ projectChoices . map ( ( choice ) => ( {
369
+ label : choice . name ,
370
+ detail : choice . ref . path ,
371
+ iconPath : folderIcon ,
372
+ ref : choice . ref ,
373
+ } ) ) ,
374
+ { placeHolder : "Pick a project to add as a reference" } ,
375
+ ) ;
376
+
377
+ if ( ! projectChoice ) {
378
+ log . info ( "User cancelled project choice" ) ;
379
+ return ;
380
+ }
381
+
382
+ dependencyRef = projectChoice . ref ;
383
+ label = projectChoice . label ;
384
+ }
385
+
386
+ await updateManifestAndSave (
387
+ qsharpJsonDoc ,
388
+ qsharpJsonUri ,
389
+ manifestObj ,
390
+ label ,
391
+ dependencyRef ,
392
+ ) ;
393
+ }
394
+
395
+ async function updateManifestAndSave (
396
+ qsharpJsonDoc : vscode . TextDocument ,
397
+ qsharpJsonUri : vscode . Uri ,
398
+ manifestObj : any ,
399
+ label : string ,
400
+ ref : Dependency ,
401
+ ) {
402
+ if ( ! manifestObj [ "dependencies" ] ) manifestObj [ "dependencies" ] = { } ;
403
+ manifestObj [ "dependencies" ] [ label ] = ref ;
404
+
405
+ // Apply the edits to the qsharp.json
406
+ const edit = new vscode . WorkspaceEdit ( ) ;
407
+ edit . replace (
408
+ qsharpJsonUri ,
409
+ new vscode . Range ( 0 , 0 , qsharpJsonDoc . lineCount , 0 ) ,
410
+ JSON . stringify ( manifestObj , null , 4 ) ,
411
+ ) ;
412
+ if ( ! ( await vscode . workspace . applyEdit ( edit ) ) ) {
413
+ await vscode . window . showErrorMessage (
414
+ "Unable to update the qsharp.json file. Check the file is writable" ,
415
+ ) ;
416
+ return ;
417
+ }
418
+
419
+ // Bring the qsharp.json to the front for the user to save
420
+ await vscode . window . showTextDocument ( qsharpJsonDoc ) ;
421
+ }
357
422
}
0 commit comments