@@ -65,8 +65,9 @@ function canChooseGoEnvironment() {
6565
6666 return { ok : true } ;
6767}
68+
6869/**
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.
7071 */
7172export const chooseGoEnvironment : CommandFactory = ( ) => async ( ) => {
7273 if ( ! goEnvStatusbarItem ) {
@@ -78,48 +79,63 @@ export const chooseGoEnvironment: CommandFactory = () => async () => {
7879 return ;
7980 }
8081
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+ ] ;
8591 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+ }
91127 } catch ( e ) {
92128 vscode . window . showErrorMessage ( ( e as Error ) . message ) ;
93129 return ;
94130 }
95131
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.
117133 const selection = await vscode . window . showQuickPick < vscode . QuickPickItem > ( options ) ;
118134 if ( ! selection ) {
119135 return ;
120136 }
121137
122- // update currently selected go
138+ // Update currently selected go.
123139 try {
124140 await setSelectedGo ( selection ) ;
125141 } catch ( e ) {
@@ -128,17 +144,18 @@ export const chooseGoEnvironment: CommandFactory = () => async () => {
128144} ;
129145
130146/**
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.
132149 */
133150export async function setSelectedGo ( goOption : vscode . QuickPickItem , promptReload = true ) : Promise < boolean > {
134151 if ( ! goOption ) {
135152 return false ;
136153 }
137154
138- // if the selected go version is not installed, install it
155+ // If the selected go version is not installed, install it.
139156 if ( goOption instanceof GoEnvironmentOption ) {
140157 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.
142159 const go = await getGoVersion ( ) ;
143160 if ( ! ! go && ( go . binaryPath === o . binpath || 'Go ' + go . format ( ) === o . label ) ) {
144161 return false ;
@@ -183,7 +200,8 @@ export async function setSelectedGo(goOption: vscode.QuickPickItem, promptReload
183200 }
184201 }
185202 }
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.
187205 // promptReload defaults to true and should only be false for tests.
188206 if ( promptReload ) {
189207 const choice = await vscode . window . showWarningMessage (
@@ -203,7 +221,9 @@ export async function setSelectedGo(goOption: vscode.QuickPickItem, promptReload
203221 return true ;
204222}
205223
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+ */
207227async function downloadGo ( goOption : GoEnvironmentOption ) : Promise < GoEnvironmentOption > {
208228 if ( goOption . available ) {
209229 return Promise . resolve ( goOption ) ;
@@ -268,7 +288,9 @@ async function downloadGo(goOption: GoEnvironmentOption): Promise<GoEnvironmentO
268288 ) ;
269289}
270290
271- // PATH value cached before addGoRuntimeBaseToPath modified.
291+ /**
292+ * PATH value cached before addGoRuntimeBaseToPath modified.
293+ */
272294let defaultPathEnv = '' ;
273295
274296function pathEnvVarName ( ) : string | undefined {
@@ -281,9 +303,11 @@ function pathEnvVarName(): string | undefined {
281303 }
282304}
283305
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+ */
287311export function addGoRuntimeBaseToPATH ( newGoRuntimeBase : string ) {
288312 if ( ! newGoRuntimeBase ) {
289313 return ;
@@ -305,7 +329,7 @@ export function addGoRuntimeBaseToPATH(newGoRuntimeBase: string) {
305329
306330 outputChannel . debug ( `addGoRuntimeBase(${ newGoRuntimeBase } ) when PATH=${ defaultPathEnv } ` ) ;
307331
308- // calling this multiple times will override the previous value.
332+ // Calling this multiple times will override the previous value.
309333 // environmentVariableCollection.clear();
310334 if ( process . platform !== 'darwin' ) {
311335 environmentVariableCollection ?. prepend ( pathEnvVar , newGoRuntimeBase + path . delimiter ) ;
@@ -339,12 +363,13 @@ export function addGoRuntimeBaseToPATH(newGoRuntimeBase: string) {
339363 pathVars . unshift ( newGoRuntimeBase ) ;
340364 process . env [ pathEnvVar ] = pathVars . join ( path . delimiter ) ;
341365}
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+ */
348373export function clearGoRuntimeBaseFromPATH ( ) {
349374 if ( terminalCreationListener ) {
350375 const l = terminalCreationListener ;
@@ -366,12 +391,13 @@ function isTerminalOptions(
366391}
367392
368393/**
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.
370396 */
371397export async function updateIntegratedTerminal ( terminal : vscode . Terminal ) : Promise < void > {
372398 if (
373399 ! 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).
375401 // Go task uses ProcessExecution which results in the terminal having `go` or `go.exe`
376402 // as its shellPath.
377403 ( isTerminalOptions ( terminal . creationOptions ) &&
@@ -385,7 +411,7 @@ export async function updateIntegratedTerminal(terminal: vscode.Terminal): Promi
385411 return ;
386412 }
387413
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.
389415 // TODO: add support for more terminal names
390416 if ( vscode . env . shell . search ( / ( p o w e r s h e l l | p w s h ) $ / i) !== - 1 ) {
391417 terminal . sendText ( `$env:Path="${ gorootBin } ;$env:Path"` , true ) ;
@@ -400,14 +426,14 @@ export async function updateIntegratedTerminal(terminal: vscode.Terminal): Promi
400426}
401427
402428/**
403- * retreive the current selected Go from the workspace state
429+ * Retreives the current selected Go from the workspace state.
404430 */
405431export function getSelectedGo ( ) : GoEnvironmentOption {
406432 return getFromWorkspaceState ( 'selectedGo' ) ;
407433}
408434
409435/**
410- * return reference to the statusbar item
436+ * @returns reference to the statusbar item.
411437 */
412438export function getGoEnvironmentStatusbarItem ( ) : vscode . StatusBarItem {
413439 return goEnvStatusbarItem ;
@@ -427,44 +453,49 @@ export function formatGoVersion(version?: GoVersion): string {
427453 }
428454}
429455
456+ /**
457+ * @returns go versions available in `$HOME/sdk`.
458+ */
430459async function getSDKGoOptions ( ) : Promise < GoEnvironmentOption [ ] > {
431- // get list of Go versions
460+ // Get list of Go versions.
432461 const sdkPath = path . join ( os . homedir ( ) , 'sdk' ) ;
433462
434463 if ( ! ( await dirExists ( sdkPath ) ) ) {
435464 return [ ] ;
436465 }
437466 const readdir = promisify ( fs . readdir ) ;
438467 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.
441470 return subdirs . map (
442471 ( dir : string ) =>
443472 new GoEnvironmentOption ( path . join ( sdkPath , dir , 'bin' , correctBinname ( 'go' ) ) , dir . replace ( 'go' , 'Go ' ) )
444473 ) ;
445474}
446475
447476export async function getDefaultGoOption ( ) : Promise < GoEnvironmentOption | undefined > {
448- // make goroot default to go.goroot
477+ // Make goroot default to " go.goroot" in vscode-go settings.
449478 const goroot = getCurrentGoRoot ( ) ;
450479 if ( ! goroot ) {
451480 return undefined ;
452481 }
453482
454- // set Go version and command
483+ // Set Go version and command.
455484 const version = await getGoVersion ( ) ;
456485 return new GoEnvironmentOption ( path . join ( goroot , 'bin' , correctBinname ( 'go' ) ) , formatGoVersion ( version ) ) ;
457486}
458487
459488/**
460- * make a web request to get versions of Go
489+ * Makes a web request to get versions of Go.
461490 */
462491interface GoVersionWebResult {
463492 version : string ;
464493 stable : boolean ;
465494}
466-
467- async function fetchDownloadableGoVersions ( ) : Promise < GoEnvironmentOption [ ] > {
495+ /**
496+ * @returns downloadable go versions from `golang.org/dl`.
497+ */
498+ async function getDownloadableGoVersions ( ) : Promise < GoEnvironmentOption [ ] > {
468499 // TODO: use `go list -m --versions -json go` when go1.20+ is the minimum supported version.
469500 // fetch information about what Go versions are available to install
470501 let webResults ;
@@ -478,13 +509,13 @@ async function fetchDownloadableGoVersions(): Promise<GoEnvironmentOption[]> {
478509 if ( ! webResults ) {
479510 return [ ] ;
480511 }
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 ) => {
483514 // TODO: allow downloading from different sites
484515 const dlPath = `golang.org/dl/${ result . version } ` ;
485516 const label = result . version . replace ( 'go' , 'Go ' ) ;
486517 return [ ...opts , new GoEnvironmentOption ( dlPath , label , false ) ] ;
487- } , [ ] as GoEnvironmentOption [ ] ) ;
518+ } , [ ] ) ;
488519}
489520
490521export const latestGoVersionKey = 'latestGoVersions' ;
@@ -501,10 +532,10 @@ export async function getLatestGoVersions(): Promise<GoEnvironmentOption[]> {
501532 if ( cachedResults && now - cachedResults . timestamp < timeout ) {
502533 results = cachedResults . goVersions ;
503534 } else {
504- // fetch the latest supported Go versions
535+ // Fetch the latest supported Go versions.
505536 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 ( ) ;
508539 await updateGlobalState ( latestGoVersionKey , {
509540 timestamp : now ,
510541 goVersions : results
@@ -535,20 +566,20 @@ export async function offerToInstallLatestGoVersion(ctx: Pick<vscode.ExtensionCo
535566
536567 let options = await getLatestGoVersions ( ) ;
537568
538- // filter out Go versions the user has already dismissed
569+ // Filter out Go versions the user has already dismissed.
539570 let dismissedOptions : GoEnvironmentOption [ ] ;
540571 dismissedOptions = await getFromGlobalState ( dismissedGoVersionUpdatesKey ) ;
541572 if ( dismissedOptions ) {
542573 options = options . filter ( ( version ) => ! dismissedOptions . find ( ( x ) => x . label === version . label ) ) ;
543574 }
544575
545- // compare to current go version.
576+ // Compare to current go version.
546577 const currentVersion = await getGoVersion ( ) ;
547578 if ( currentVersion ) {
548579 options = options . filter ( ( version ) => currentVersion . lt ( version . label ) ) ;
549580 }
550581
551- // notify user that there is a newer version of Go available
582+ // Notify user that there is a newer version of Go available.
552583 if ( options . length > 0 ) {
553584 const versionsText = options . map ( ( x ) => x . label ) . join ( ', ' ) ;
554585 const statusBarItem = addGoStatus ( STATUS_BAR_ITEM_NAME ) ;
@@ -576,7 +607,7 @@ export async function offerToInstallLatestGoVersion(ctx: Pick<vscode.ExtensionCo
576607 const neverAgain = {
577608 title : "Don't Show Again" ,
578609 async command ( ) {
579- // mark these versions as seen
610+ // Mark these versions as seen.
580611 dismissedOptions = await getFromGlobalState ( dismissedGoVersionUpdatesKey ) ;
581612 if ( ! dismissedOptions ) {
582613 dismissedOptions = [ ] ;
0 commit comments