Skip to content

Commit 25da974

Browse files
h9jianggopherbot
authored andcommitted
extension/src: add separator in command go.environment.choose
Go: Choose Go Environment command will separate the options in three parts: - Fixed options: selecting go from file browser & clear selection. - Locally discovered: go currently being selected or available in $HOME/sdk. - Downloadable: go version that is downloadable from golang.org/dl. UI screenshot before: https://github.com/user-attachments/assets/094fe705-886f-4074-a564-b8021208ebe2 UI screenshot after: https://github.com/user-attachments/assets/6f5858f9-9c78-4610-9fa0-da30c78bb7c9 https://google.github.io/styleguide/tsguide.html#comments-documentation - JSDoc for documentation, line comments for implementation. - Method/Function comment should be written as if there is an implied "This method ..." before it. For #3697 Change-Id: I779bbd978103974b7c3e68a56820934312cdce04 Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/654215 LUCI-TryBot-Result: Go LUCI <[email protected]> Auto-Submit: Hongxiang Jiang <[email protected]> kokoro-CI: kokoro <[email protected]> Reviewed-by: Peter Weinberger <[email protected]>
1 parent 1579d6e commit 25da974

File tree

1 file changed

+102
-71
lines changed

1 file changed

+102
-71
lines changed

extension/src/goEnvironmentStatus.ts

Lines changed: 102 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -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
*/
7172
export 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
*/
133150
export 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+
*/
207227
async 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+
*/
272294
let defaultPathEnv = '';
273295

274296
function 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+
*/
287311
export 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+
*/
348373
export 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
*/
371397
export 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(/(powershell|pwsh)$/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
*/
405431
export 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
*/
412438
export 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+
*/
430459
async 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

447476
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.
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
*/
462491
interface 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

490521
export 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

Comments
 (0)