@@ -65,8 +65,9 @@ function canChooseGoEnvironment() {
65
65
66
66
return { ok : true } ;
67
67
}
68
+
68
69
/**
69
- * Present a command palette menu to the user to select their go binary
70
+ * Presents a command palette menu to the user to select their go binary.
70
71
*/
71
72
export const chooseGoEnvironment : CommandFactory = ( ) => async ( ) => {
72
73
if ( ! goEnvStatusbarItem ) {
@@ -78,48 +79,63 @@ export const chooseGoEnvironment: CommandFactory = () => async () => {
78
79
return ;
79
80
}
80
81
81
- // fetch default go and uninstalled go versions
82
- let defaultOption : GoEnvironmentOption | undefined ;
83
- let uninstalledOptions : GoEnvironmentOption [ ] ;
84
- let goSDKOptions : GoEnvironmentOption [ ] ;
82
+ let options : vscode . QuickPickItem [ ] = [
83
+ // Option to choose go binary from file browser.
84
+ {
85
+ label : CHOOSE_FROM_FILE_BROWSER ,
86
+ description : 'Select the go binary to use'
87
+ } ,
88
+ // Option to clear the existing selection.
89
+ { label : CLEAR_SELECTION }
90
+ ] ;
85
91
try {
86
- [ defaultOption , uninstalledOptions , goSDKOptions ] = await Promise . all ( [
87
- getDefaultGoOption ( ) ,
88
- fetchDownloadableGoVersions ( ) ,
89
- getSDKGoOptions ( )
90
- ] ) ;
92
+ const seenDescriptions = new Set < string > ( ) ;
93
+ const seenLabels = new Set < string > ( ) ;
94
+ // addOption adds the option to the input array only if it is unique,
95
+ // based on its description and label.
96
+ const addOption = ( options : GoEnvironmentOption [ ] , option : GoEnvironmentOption | undefined ) => {
97
+ if ( option === undefined ) {
98
+ return ;
99
+ }
100
+ if ( ! seenDescriptions . has ( option . description ) && ! seenLabels . has ( option . label ) ) {
101
+ seenDescriptions . add ( option . description ) ;
102
+ seenLabels . add ( option . label ) ;
103
+ options . push ( option ) ;
104
+ }
105
+ } ;
106
+
107
+ const defaultOption = await Promise . resolve ( getDefaultGoOption ( ) ) ;
108
+ const goSDKOptions = await getSDKGoOptions ( ) ;
109
+
110
+ const local : GoEnvironmentOption [ ] = [ ] ;
111
+ addOption ( local , defaultOption ) ;
112
+ goSDKOptions . forEach ( ( option ) => addOption ( local , option ) ) ;
113
+
114
+ if ( local . length > 0 ) {
115
+ options . push ( { kind : vscode . QuickPickItemKind . Separator , label : 'Locally discovered' } ) ;
116
+ options . push ( ...local ) ;
117
+ }
118
+
119
+ const downloadableOptions = await getDownloadableGoVersions ( ) ;
120
+ const downloadable : GoEnvironmentOption [ ] = [ ] ;
121
+ downloadableOptions . forEach ( ( option ) => addOption ( downloadable , option ) ) ;
122
+
123
+ if ( downloadable . length > 0 ) {
124
+ options . push ( { kind : vscode . QuickPickItemKind . Separator , label : 'Downloadable' } ) ;
125
+ options . push ( ...downloadable ) ;
126
+ }
91
127
} catch ( e ) {
92
128
vscode . window . showErrorMessage ( ( e as Error ) . message ) ;
93
129
return ;
94
130
}
95
131
96
- // create quick pick items
97
- const defaultQuickPick = defaultOption ? [ defaultOption ] : [ ] ;
98
-
99
- // dedup options by eliminating duplicate paths (description)
100
- const clearOption : vscode . QuickPickItem = { label : CLEAR_SELECTION } ;
101
- const filePickerOption : vscode . QuickPickItem = {
102
- label : CHOOSE_FROM_FILE_BROWSER ,
103
- description : 'Select the go binary to use'
104
- } ;
105
- // TODO(hyangah): Add separators after clearOption if github.com/microsoft/vscode#74967 is resolved.
106
- const options = [ filePickerOption , clearOption , ...defaultQuickPick , ...goSDKOptions , ...uninstalledOptions ] . reduce (
107
- ( opts , nextOption ) => {
108
- if ( opts . find ( ( op ) => op . description === nextOption . description || op . label === nextOption . label ) ) {
109
- return opts ;
110
- }
111
- return [ ...opts , nextOption ] ;
112
- } ,
113
- [ ] as vscode . QuickPickItem [ ]
114
- ) ;
115
-
116
- // get user's selection, return if none was made
132
+ // Get user's selection, return if none was made.
117
133
const selection = await vscode . window . showQuickPick < vscode . QuickPickItem > ( options ) ;
118
134
if ( ! selection ) {
119
135
return ;
120
136
}
121
137
122
- // update currently selected go
138
+ // Update currently selected go.
123
139
try {
124
140
await setSelectedGo ( selection ) ;
125
141
} catch ( e ) {
@@ -128,17 +144,18 @@ export const chooseGoEnvironment: CommandFactory = () => async () => {
128
144
} ;
129
145
130
146
/**
131
- * update the selected go path and label in the workspace state
147
+ * Updates the selected go path and label in the workspace state.
148
+ * @returns true if set successfully, false otherwise.
132
149
*/
133
150
export async function setSelectedGo ( goOption : vscode . QuickPickItem , promptReload = true ) : Promise < boolean > {
134
151
if ( ! goOption ) {
135
152
return false ;
136
153
}
137
154
138
- // if the selected go version is not installed, install it
155
+ // If the selected go version is not installed, install it.
139
156
if ( goOption instanceof GoEnvironmentOption ) {
140
157
const o = goOption . available ? ( goOption as GoEnvironmentOption ) : await downloadGo ( goOption ) ;
141
- // check that the given binary is not already at the beginning of the PATH
158
+ // Check that the given binary is not already at the beginning of the PATH.
142
159
const go = await getGoVersion ( ) ;
143
160
if ( ! ! go && ( go . binaryPath === o . binpath || 'Go ' + go . format ( ) === o . label ) ) {
144
161
return false ;
@@ -183,7 +200,8 @@ export async function setSelectedGo(goOption: vscode.QuickPickItem, promptReload
183
200
}
184
201
}
185
202
}
186
- // prompt the user to reload the window.
203
+ // Show modal dialog to the user to reload the window, this require user's
204
+ // immediate attention.
187
205
// promptReload defaults to true and should only be false for tests.
188
206
if ( promptReload ) {
189
207
const choice = await vscode . window . showWarningMessage (
@@ -203,7 +221,9 @@ export async function setSelectedGo(goOption: vscode.QuickPickItem, promptReload
203
221
return true ;
204
222
}
205
223
206
- // downloadGo downloads the specified go version available in dl.golang.org.
224
+ /**
225
+ * Downloads the specified go version available in dl.golang.org.
226
+ */
207
227
async function downloadGo ( goOption : GoEnvironmentOption ) : Promise < GoEnvironmentOption > {
208
228
if ( goOption . available ) {
209
229
return Promise . resolve ( goOption ) ;
@@ -268,7 +288,9 @@ async function downloadGo(goOption: GoEnvironmentOption): Promise<GoEnvironmentO
268
288
) ;
269
289
}
270
290
271
- // PATH value cached before addGoRuntimeBaseToPath modified.
291
+ /**
292
+ * PATH value cached before addGoRuntimeBaseToPath modified.
293
+ */
272
294
let defaultPathEnv = '' ;
273
295
274
296
function pathEnvVarName ( ) : string | undefined {
@@ -281,9 +303,11 @@ function pathEnvVarName(): string | undefined {
281
303
}
282
304
}
283
305
284
- // addGoRuntimeBaseToPATH adds the given path to the front of the PATH environment variable.
285
- // It removes duplicates.
286
- // TODO: can we avoid changing PATH but utilize toolExecutionEnv?
306
+ /**
307
+ * addGoRuntimeBaseToPATH adds the given path to the front of the PATH
308
+ * environment variable. It removes duplicates.
309
+ * TODO: can we avoid changing PATH but utilize toolExecutionEnv?
310
+ */
287
311
export function addGoRuntimeBaseToPATH ( newGoRuntimeBase : string ) {
288
312
if ( ! newGoRuntimeBase ) {
289
313
return ;
@@ -305,7 +329,7 @@ export function addGoRuntimeBaseToPATH(newGoRuntimeBase: string) {
305
329
306
330
outputChannel . debug ( `addGoRuntimeBase(${ newGoRuntimeBase } ) when PATH=${ defaultPathEnv } ` ) ;
307
331
308
- // calling this multiple times will override the previous value.
332
+ // Calling this multiple times will override the previous value.
309
333
// environmentVariableCollection.clear();
310
334
if ( process . platform !== 'darwin' ) {
311
335
environmentVariableCollection ?. prepend ( pathEnvVar , newGoRuntimeBase + path . delimiter ) ;
@@ -339,12 +363,13 @@ export function addGoRuntimeBaseToPATH(newGoRuntimeBase: string) {
339
363
pathVars . unshift ( newGoRuntimeBase ) ;
340
364
process . env [ pathEnvVar ] = pathVars . join ( path . delimiter ) ;
341
365
}
342
-
343
- // Clear terminal PATH environment modification previously installed
344
- // using addGoRuntimeBaseToPATH.
345
- // In particular, changes to vscode.EnvironmentVariableCollection persist across
346
- // vscode sessions, so when we decide not to mutate PATH, we need to clear
347
- // the preexisting changes.
366
+ /**
367
+ * Clears terminal PATH environment modification previously installed using
368
+ * addGoRuntimeBaseToPATH.
369
+ * In particular, changes to vscode.EnvironmentVariableCollection persist across
370
+ * vscode sessions, so when we decide not to mutate PATH, we need to clear the
371
+ * preexisting changes.
372
+ */
348
373
export function clearGoRuntimeBaseFromPATH ( ) {
349
374
if ( terminalCreationListener ) {
350
375
const l = terminalCreationListener ;
@@ -366,12 +391,13 @@ function isTerminalOptions(
366
391
}
367
392
368
393
/**
369
- * update the PATH variable in the given terminal to default to the currently selected Go
394
+ * Updates the PATH variable in the given terminal to default to the currently
395
+ * selected Go.
370
396
*/
371
397
export async function updateIntegratedTerminal ( terminal : vscode . Terminal ) : Promise < void > {
372
398
if (
373
399
! terminal ||
374
- // don 't interfere if this terminal was created to run a Go task (goTaskProvider.ts).
400
+ // Don 't interfere if this terminal was created to run a Go task (goTaskProvider.ts).
375
401
// Go task uses ProcessExecution which results in the terminal having `go` or `go.exe`
376
402
// as its shellPath.
377
403
( isTerminalOptions ( terminal . creationOptions ) &&
@@ -385,7 +411,7 @@ export async function updateIntegratedTerminal(terminal: vscode.Terminal): Promi
385
411
return ;
386
412
}
387
413
388
- // append the goroot to the beginning of the PATH so it takes precedence
414
+ // Append the goroot to the beginning of the PATH so it takes precedence.
389
415
// TODO: add support for more terminal names
390
416
if ( vscode . env . shell . search ( / ( p o w e r s h e l l | p w s h ) $ / i) !== - 1 ) {
391
417
terminal . sendText ( `$env:Path="${ gorootBin } ;$env:Path"` , true ) ;
@@ -400,14 +426,14 @@ export async function updateIntegratedTerminal(terminal: vscode.Terminal): Promi
400
426
}
401
427
402
428
/**
403
- * retreive the current selected Go from the workspace state
429
+ * Retreives the current selected Go from the workspace state.
404
430
*/
405
431
export function getSelectedGo ( ) : GoEnvironmentOption {
406
432
return getFromWorkspaceState ( 'selectedGo' ) ;
407
433
}
408
434
409
435
/**
410
- * return reference to the statusbar item
436
+ * @returns reference to the statusbar item.
411
437
*/
412
438
export function getGoEnvironmentStatusbarItem ( ) : vscode . StatusBarItem {
413
439
return goEnvStatusbarItem ;
@@ -427,44 +453,49 @@ export function formatGoVersion(version?: GoVersion): string {
427
453
}
428
454
}
429
455
456
+ /**
457
+ * @returns go versions available in `$HOME/sdk`.
458
+ */
430
459
async function getSDKGoOptions ( ) : Promise < GoEnvironmentOption [ ] > {
431
- // get list of Go versions
460
+ // Get list of Go versions.
432
461
const sdkPath = path . join ( os . homedir ( ) , 'sdk' ) ;
433
462
434
463
if ( ! ( await dirExists ( sdkPath ) ) ) {
435
464
return [ ] ;
436
465
}
437
466
const readdir = promisify ( fs . readdir ) ;
438
467
const subdirs = await readdir ( sdkPath ) ;
439
- // the dir happens to be the version, which will be used as the label
440
- // the path is assembled and used as the description
468
+ // The dir happens to be the version, which will be used as the label.
469
+ // The path is assembled and used as the description.
441
470
return subdirs . map (
442
471
( dir : string ) =>
443
472
new GoEnvironmentOption ( path . join ( sdkPath , dir , 'bin' , correctBinname ( 'go' ) ) , dir . replace ( 'go' , 'Go ' ) )
444
473
) ;
445
474
}
446
475
447
476
export async function getDefaultGoOption ( ) : Promise < GoEnvironmentOption | undefined > {
448
- // make goroot default to go.goroot
477
+ // Make goroot default to " go.goroot" in vscode-go settings.
449
478
const goroot = getCurrentGoRoot ( ) ;
450
479
if ( ! goroot ) {
451
480
return undefined ;
452
481
}
453
482
454
- // set Go version and command
483
+ // Set Go version and command.
455
484
const version = await getGoVersion ( ) ;
456
485
return new GoEnvironmentOption ( path . join ( goroot , 'bin' , correctBinname ( 'go' ) ) , formatGoVersion ( version ) ) ;
457
486
}
458
487
459
488
/**
460
- * make a web request to get versions of Go
489
+ * Makes a web request to get versions of Go.
461
490
*/
462
491
interface GoVersionWebResult {
463
492
version : string ;
464
493
stable : boolean ;
465
494
}
466
-
467
- async function fetchDownloadableGoVersions ( ) : Promise < GoEnvironmentOption [ ] > {
495
+ /**
496
+ * @returns downloadable go versions from `golang.org/dl`.
497
+ */
498
+ async function getDownloadableGoVersions ( ) : Promise < GoEnvironmentOption [ ] > {
468
499
// TODO: use `go list -m --versions -json go` when go1.20+ is the minimum supported version.
469
500
// fetch information about what Go versions are available to install
470
501
let webResults ;
@@ -478,13 +509,13 @@ async function fetchDownloadableGoVersions(): Promise<GoEnvironmentOption[]> {
478
509
if ( ! webResults ) {
479
510
return [ ] ;
480
511
}
481
- // turn the web result into GoEnvironmentOption model
482
- return webResults . reduce ( ( opts , result : GoVersionWebResult ) => {
512
+ // Turn the web result into GoEnvironmentOption model.
513
+ return webResults . reduce ( ( opts : GoEnvironmentOption [ ] , result : GoVersionWebResult ) => {
483
514
// TODO: allow downloading from different sites
484
515
const dlPath = `golang.org/dl/${ result . version } ` ;
485
516
const label = result . version . replace ( 'go' , 'Go ' ) ;
486
517
return [ ...opts , new GoEnvironmentOption ( dlPath , label , false ) ] ;
487
- } , [ ] as GoEnvironmentOption [ ] ) ;
518
+ } , [ ] ) ;
488
519
}
489
520
490
521
export const latestGoVersionKey = 'latestGoVersions' ;
@@ -501,10 +532,10 @@ export async function getLatestGoVersions(): Promise<GoEnvironmentOption[]> {
501
532
if ( cachedResults && now - cachedResults . timestamp < timeout ) {
502
533
results = cachedResults . goVersions ;
503
534
} else {
504
- // fetch the latest supported Go versions
535
+ // Fetch the latest supported Go versions.
505
536
try {
506
- // fetch the latest Go versions and cache the results
507
- results = await fetchDownloadableGoVersions ( ) ;
537
+ // Fetch the latest Go versions and cache the results.
538
+ results = await getDownloadableGoVersions ( ) ;
508
539
await updateGlobalState ( latestGoVersionKey , {
509
540
timestamp : now ,
510
541
goVersions : results
@@ -535,20 +566,20 @@ export async function offerToInstallLatestGoVersion(ctx: Pick<vscode.ExtensionCo
535
566
536
567
let options = await getLatestGoVersions ( ) ;
537
568
538
- // filter out Go versions the user has already dismissed
569
+ // Filter out Go versions the user has already dismissed.
539
570
let dismissedOptions : GoEnvironmentOption [ ] ;
540
571
dismissedOptions = await getFromGlobalState ( dismissedGoVersionUpdatesKey ) ;
541
572
if ( dismissedOptions ) {
542
573
options = options . filter ( ( version ) => ! dismissedOptions . find ( ( x ) => x . label === version . label ) ) ;
543
574
}
544
575
545
- // compare to current go version.
576
+ // Compare to current go version.
546
577
const currentVersion = await getGoVersion ( ) ;
547
578
if ( currentVersion ) {
548
579
options = options . filter ( ( version ) => currentVersion . lt ( version . label ) ) ;
549
580
}
550
581
551
- // notify user that there is a newer version of Go available
582
+ // Notify user that there is a newer version of Go available.
552
583
if ( options . length > 0 ) {
553
584
const versionsText = options . map ( ( x ) => x . label ) . join ( ', ' ) ;
554
585
const statusBarItem = addGoStatus ( STATUS_BAR_ITEM_NAME ) ;
@@ -576,7 +607,7 @@ export async function offerToInstallLatestGoVersion(ctx: Pick<vscode.ExtensionCo
576
607
const neverAgain = {
577
608
title : "Don't Show Again" ,
578
609
async command ( ) {
579
- // mark these versions as seen
610
+ // Mark these versions as seen.
580
611
dismissedOptions = await getFromGlobalState ( dismissedGoVersionUpdatesKey ) ;
581
612
if ( ! dismissedOptions ) {
582
613
dismissedOptions = [ ] ;
0 commit comments