src/goEnvironmentStatus.ts: allow to choose go from file browser
This adds an additional QuickPickItem to the go environment select quick pick menu
that opens a file browser when selected. The selected file should be an executable.
Fixes golang/vscode-go#490
Change-Id: I63b940af15e5b0cfed20f3c88146597e16cba45c
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/260003
Trust: Hyang-Ah Hana Kim <hyangah@gmail.com>
Trust: Suzy Mueller <suzmue@golang.org>
Run-TryBot: Hyang-Ah Hana Kim <hyangah@gmail.com>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Suzy Mueller <suzmue@golang.org>
diff --git a/src/goEnvironmentStatus.ts b/src/goEnvironmentStatus.ts
index 126fce7..e7242ba 100644
--- a/src/goEnvironmentStatus.ts
+++ b/src/goEnvironmentStatus.ts
@@ -18,7 +18,7 @@
import { addGoStatus, goEnvStatusbarItem, outputChannel, removeGoStatus } from './goStatus';
import { getFromGlobalState, getFromWorkspaceState, updateGlobalState, updateWorkspaceState } from './stateUtils';
import { getBinPath, getGoConfig, getGoVersion, getTempFilePath, GoVersion, rmdirRecursive } from './util';
-import { correctBinname, getBinPathFromEnvVar, getCurrentGoRoot, pathExists } from './utils/pathUtils';
+import { correctBinname, executableFileExists, getBinPathFromEnvVar, getCurrentGoRoot, pathExists } from './utils/pathUtils';
export class GoEnvironmentOption {
public static fromQuickPickItem({ description, label }: vscode.QuickPickItem): GoEnvironmentOption {
@@ -42,6 +42,10 @@
environmentVariableCollection = env;
}
+// QuickPickItem names for chooseGoEnvironment menu.
+const CLEAR_SELECTION = '$(clear-all) Clear selection';
+const CHOOSE_FROM_FILE_BROWSER = '$(folder) Choose from file browser';
+
/**
* Present a command palette menu to the user to select their go binary
*/
@@ -77,8 +81,13 @@
const goSDKQuickPicks = goSDKOptions.map((op) => op.toQuickPickItem());
// dedup options by eliminating duplicate paths (description)
- const clearOption: vscode.QuickPickItem = { label: 'Clear selection' };
- const options = [clearOption, defaultQuickPick, ...goSDKQuickPicks, ...uninstalledQuickPicks]
+ const clearOption: vscode.QuickPickItem = { label: CLEAR_SELECTION };
+ const filePickerOption: vscode.QuickPickItem = {
+ label: CHOOSE_FROM_FILE_BROWSER,
+ description: 'Select the go binary to use',
+ };
+ // TODO(hyangah): Add separators after clearOption if github.com/microsoft/vscode#74967 is resolved.
+ const options = [filePickerOption, clearOption, defaultQuickPick, ...goSDKQuickPicks, ...uninstalledQuickPicks]
.reduce((opts, nextOption) => {
if (opts.find((op) => op.description === nextOption.description || op.label === nextOption.label)) {
return opts;
@@ -115,12 +124,39 @@
if (goOption.binpath?.startsWith('go get')) {
// start a loading indicator
await downloadGo(goOption);
- } else if (goOption.label === 'Clear selection') {
+ } else if (goOption.label === CLEAR_SELECTION) {
if (!getSelectedGo()) {
return false; // do nothing.
}
await updateWorkspaceState('selectedGo', undefined);
- // TODO: goEnvStatusbarItem?
+ } else if (goOption.label === CHOOSE_FROM_FILE_BROWSER) {
+ const currentGOROOT = getCurrentGoRoot();
+ const defaultUri = currentGOROOT ? vscode.Uri.file(path.join(currentGOROOT, 'bin')) : undefined;
+
+ const newGoUris = await vscode.window.showOpenDialog({
+ canSelectFiles: true,
+ canSelectFolders: false,
+ canSelectMany: false,
+ defaultUri,
+ });
+ if (!newGoUris || newGoUris.length !== 1) {
+ return false;
+ }
+ const newGoUri = newGoUris[0];
+
+ if (defaultUri === newGoUri) {
+ return false;
+ }
+ if (!executableFileExists(newGoUri.path)) {
+ vscode.window.showErrorMessage(`${newGoUri.path} is not an executable`);
+ return false;
+ }
+ const newGo = await getGoVersion(newGoUri.path);
+ if (!newGo) {
+ vscode.window.showErrorMessage(`failed to get "${newGoUri.path} version", invalid Go binary`);
+ return false;
+ }
+ await updateWorkspaceState('selectedGo', new GoEnvironmentOption(newGo.binaryPath, formatGoVersion(newGo)));
} else {
// check that the given binary is not already at the beginning of the PATH
const go = await getGoVersion();
diff --git a/src/util.ts b/src/util.ts
index 279092c..083fcf9 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -326,12 +326,12 @@
* Returns undefined if go version can't be determined because
* go is not available or `go version` fails.
*/
-export async function getGoVersion(): Promise<GoVersion | undefined> {
+export async function getGoVersion(goBinPath?: string): Promise<GoVersion | undefined> {
// TODO(hyangah): limit the number of concurrent getGoVersion call.
// When the extension starts, at least 4 concurrent calls race
// and end up calling `go version`.
- const goRuntimePath = getBinPath('go');
+ const goRuntimePath = goBinPath ?? getBinPath('go');
const warn = (msg: string) => {
outputChannel.appendLine(msg);
@@ -348,6 +348,7 @@
}
warn(`cached Go version (${JSON.stringify(cachedGoVersion)}) is invalid, recomputing`);
}
+ let goVersion: GoVersion;
try {
const env = toolExecutionEnvironment();
const docUri = vscode.window.activeTextEditor?.document.uri;
@@ -358,16 +359,19 @@
warn(`failed to run "${goRuntimePath} version": stdout: ${stdout}, stderr: ${stderr}`);
return;
}
- cachedGoBinPath = goRuntimePath;
- cachedGoVersion = new GoVersion(goRuntimePath, stdout);
- if (!cachedGoVersion.isValid()) {
- warn(`unable to determine version from the output of "${goRuntimePath} version": "${stdout}"`);
- }
+ goVersion = new GoVersion(goRuntimePath, stdout);
} catch (err) {
warn(`failed to run "${goRuntimePath} version": ${err}`);
return;
}
- return cachedGoVersion;
+ if (!goBinPath) { // if getGoVersion was called with a given goBinPath, don't cache the result.
+ cachedGoBinPath = goRuntimePath;
+ cachedGoVersion = goVersion;
+ if (!cachedGoVersion.isValid()) {
+ warn(`unable to determine version from the output of "${goRuntimePath} version": "${goVersion.svString}"`);
+ }
+ }
+ return goVersion;
}
/**
diff --git a/src/utils/pathUtils.ts b/src/utils/pathUtils.ts
index f133df8..0b93650 100644
--- a/src/utils/pathUtils.ts
+++ b/src/utils/pathUtils.ts
@@ -132,7 +132,7 @@
return toolName;
}
-function executableFileExists(filePath: string): boolean {
+export function executableFileExists(filePath: string): boolean {
let exists = true;
try {
exists = fs.statSync(filePath).isFile();