src/goEnvironmentStatus.ts: fix powershell Path and add fish support

This CL fixes an issue  where the Path is not properly set in powershell. This change also adds support for the fish shell.

Additionally, this CL implements the suggestion made to avoid updating
the PATH redundantly.

Fixes golang/vscode-go#314

Change-Id: Ib9f105ab16e034ffe7af16df1c6b4dcc922f0398
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/241777
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
diff --git a/src/goEnvironmentStatus.ts b/src/goEnvironmentStatus.ts
index d43ed21..5e2a6f8 100644
--- a/src/goEnvironmentStatus.ts
+++ b/src/goEnvironmentStatus.ts
@@ -15,7 +15,7 @@
 import { toolInstallationEnvironment } from './goEnv';
 import { outputChannel } from './goStatus';
 import { getBinPath, getGoConfig, getGoVersion, getTempFilePath, rmdirRecursive } from './util';
-import { getCurrentGoRoot, pathExists } from './utils/goPath';
+import { getBinPathFromEnvVar, getCurrentGoRoot, pathExists } from './utils/goPath';
 
 export class GoEnvironmentOption {
 	public static fromQuickPickItem({ description, label }: vscode.QuickPickItem): GoEnvironmentOption {
@@ -35,14 +35,21 @@
 // statusbar item for switching the Go environment
 let goEnvStatusbarItem: vscode.StatusBarItem;
 let terminalCreationListener: vscode.Disposable;
+let terminalPATH: string;
 
 /**
  * Initialize the status bar item with current Go binary
  */
-export async function initGoStatusBar() {
+export async function initGoStatusBar(cachePath: string) {
 	if (!goEnvStatusbarItem) {
 		goEnvStatusbarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 50);
 	}
+	// cache the PATH on first initialization
+	// this will be the path that new integrated terminals have
+	if (!terminalPATH) {
+		terminalPATH = cachePath;
+	}
+
 	// set Go version and command
 	const version = await getGoVersion();
 	const goOption = new GoEnvironmentOption(version.binaryPath, formatGoVersion(version.format()));
@@ -97,6 +104,12 @@
 		return;
 	}
 
+	// if there is no workspace, show GOROOT with message
+	if (!vscode.workspace.name) {
+		vscode.window.showInformationMessage(`GOROOT: ${getCurrentGoRoot()}. Switching Go version is not yet supported in single-file mode.`);
+		return;
+	}
+
 	// fetch default go and uninstalled go versions
 	let defaultOption: GoEnvironmentOption;
 	let uninstalledOptions: GoEnvironmentOption[];
@@ -231,15 +244,18 @@
 			await goConfig.update('alternateTools', newAlternateTools, scope);
 			goEnvStatusbarItem.text = selectedGo.label;
 
-			outputChannel.appendLine('Updating integrated terminals');
-			vscode.window.terminals.forEach(updateIntegratedTerminal);
-
 			// remove tmp directories
 			outputChannel.appendLine('Cleaning up...');
 			rmdirRecursive(toolsTmpDir);
 			outputChannel.appendLine('Success!');
 		});
 	} else {
+		// check that the given binary is not already at the beginning of the PATH
+		const go = await getGoVersion();
+		if (go.binaryPath === selectedGo.binpath) {
+			return;
+		}
+
 		const newAlternateTools = {
 			...alternateTools,
 			go: selectedGo.binpath,
@@ -263,14 +279,22 @@
 export async function updateIntegratedTerminal(terminal: vscode.Terminal) {
 	if (!terminal) { return; }
 	const goroot = path.join(getCurrentGoRoot(), 'bin');
-	const isWindows = terminal.name.toLowerCase() === 'powershell' || terminal.name.toLowerCase() === 'cmd';
+	if (goroot === getBinPathFromEnvVar('go', terminalPATH, false)) {
+		return;
+	}
 
 	// append the goroot to the beginning of the PATH so it takes precedence
 	// TODO: add support for more terminal names
 	// this assumes all non-windows shells are bash-like.
-	if (isWindows) {
+	if (terminal.name.toLowerCase() === 'cmd') {
 		terminal.sendText(`set PATH=${goroot};%Path%`, true);
 		terminal.sendText('cls');
+	} else if (terminal.name.toLowerCase() === 'powershell') {
+		terminal.sendText(`$env:Path="${goroot};$env:Path"`, true);
+		terminal.sendText('clear');
+	} else if (terminal.name.toLowerCase() === 'fish') {
+		terminal.sendText(`set -gx PATH ${goroot} $PATH`);
+		terminal.sendText('clear');
 	} else {
 		terminal.sendText(`export PATH=${goroot}:$PATH`, true);
 		terminal.sendText('clear');
diff --git a/src/goInstallTools.ts b/src/goInstallTools.ts
index 0479a18..6695123 100644
--- a/src/goInstallTools.ts
+++ b/src/goInstallTools.ts
@@ -377,8 +377,14 @@
 
 			// cgo, gopls, and other underlying tools will inherit the environment and attempt
 			// to locate 'go' from the PATH env var.
+			let cachePath = '';
+			if (process.env.hasOwnProperty('PATH')) {
+				cachePath = process.env.PATH;
+			} else {
+				cachePath = process.env.Path;
+			}
 			addGoRuntimeBaseToPATH(path.join(getCurrentGoRoot(), 'bin'));
-			initGoStatusBar();
+			initGoStatusBar(cachePath);
 			// TODO: restart language server or synchronize with language server update.
 
 			return resolve();
diff --git a/test/integration/statusbar.test.ts b/test/integration/statusbar.test.ts
index 0420342..0b17a43 100644
--- a/test/integration/statusbar.test.ts
+++ b/test/integration/statusbar.test.ts
@@ -50,9 +50,9 @@
 	});
 });
 
-describe.skip('#setSelectedGo()', function () {
+describe('#setSelectedGo()', function () {
 	// Disabled due to https://github.com/golang/vscode-go/issues/303.
-	this.timeout(20000);
+	this.timeout(40000);
 	let sandbox: sinon.SinonSandbox | undefined;
 	let goOption: GoEnvironmentOption;
 	let defaultGoConfig: vscode.WorkspaceConfiguration;