src/goStatus.ts: consolidate Go status bar items

There are a number of status bar items that the extension
currently shows to the user, including the current Go version
and whether modules is enabled or not. We additionally want to
be able to let users easily check if they are using gopls, the
Go language server, since this is one of the first questions we
ask when people are having issues.

This change consolidates these items into a single item that displays
the current Go version, as well as an icon indicating if gopls is enabled.
Clicking on this item pulls up a menu to:
1. Locate the go tools and display go env
2. Choose a new go environment
3. Open the gopls trace (if applicable)
4. Open the go.mod file (if applicable)

Updates golang/vscode-go#156
Updates golang/vscode-go#525

Change-Id: I970bbe0ca64394836f62453fe5e53d57bba41f64
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/256817
Trust: Suzy Mueller <suzmue@golang.org>
Run-TryBot: Suzy Mueller <suzmue@golang.org>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
diff --git a/docs/go-version.md b/docs/go-version.md
index d35c14b..a7ac0aa 100644
--- a/docs/go-version.md
+++ b/docs/go-version.md
@@ -2,7 +2,7 @@
 
 ## Using The Go Status Bar
 
-You can view the current Go version by looking at the status bar item in the bottom left corner of VS Code. Clicking this button will present you with a menu from which you can select any version of Go that exists in your $HOME/sdk directory or on <https://golang.org/dl>.
+You can view the current Go version by looking at the status bar item in the bottom left corner of VS Code. Clicking this button and selecting `Choose Go Environment` will present you with a menu from which you can select any version of Go that exists in your $HOME/sdk directory or on <https://golang.org/dl>. This command is also available through the command pallette using `Go: Choose Go Environment`.
 
 Previously, the `go.goroot` and `go.alternateTools` settings controlled the Go version used by VS Code Go. If you have configured these settings, they are no longer needed and should be deleted.
 
diff --git a/src/goEnvironmentStatus.ts b/src/goEnvironmentStatus.ts
index 91c7b8c..8a3d38d 100644
--- a/src/goEnvironmentStatus.ts
+++ b/src/goEnvironmentStatus.ts
@@ -14,8 +14,9 @@
 import vscode = require('vscode');
 import WebRequest = require('web-request');
 import { toolInstallationEnvironment } from './goEnv';
+import { buildLanguageServerConfig } from './goLanguageServer';
 import { logVerbose } from './goLogging';
-import { hideGoStatus, outputChannel, showGoStatus } from './goStatus';
+import { hideGoStatus, languageServerIcon, outputChannel, showGoStatus } 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';
@@ -52,10 +53,34 @@
 
 	hideGoStatusBar();
 	goEnvStatusbarItem.text = goOption.label;
-	goEnvStatusbarItem.command = 'go.environment.choose';
+	goEnvStatusbarItem.command = 'go.environment.status';
+
+	// Add an icon to indicate that the 'gopls' server is running.
+	// Assume if it is configured it is already running, since the
+	// icon will be updated on an attempt to start.
+	const cfg = buildLanguageServerConfig();
+	updateLanguageServerIconGoStatusBar(true, cfg.serverName);
+
 	showGoStatusBar();
 }
 
+export async function updateLanguageServerIconGoStatusBar(started: boolean, server: string) {
+	if (!goEnvStatusbarItem) {
+		return;
+	}
+
+	const text = goEnvStatusbarItem.text;
+	if (started && server === 'gopls') {
+		if (!text.endsWith(languageServerIcon)) {
+			goEnvStatusbarItem.text = text + languageServerIcon;
+		}
+	} else {
+		if (text.endsWith(languageServerIcon)) {
+			goEnvStatusbarItem.text = text.substring(0, text.length - languageServerIcon.length);
+		}
+	}
+}
+
 /**
  * disable the Go environment status bar item
  */
diff --git a/src/goLanguageServer.ts b/src/goLanguageServer.ts
index cdf23ad..a15febc 100644
--- a/src/goLanguageServer.ts
+++ b/src/goLanguageServer.ts
@@ -35,6 +35,7 @@
 import { GoCodeActionProvider } from './goCodeAction';
 import { GoDefinitionProvider } from './goDeclaration';
 import { toolExecutionEnvironment } from './goEnv';
+import { updateLanguageServerIconGoStatusBar } from './goEnvironmentStatus';
 import { GoHoverProvider } from './goExtraInfo';
 import { GoDocumentFormattingEditProvider } from './goFormat';
 import { GoImplementationProvider } from './goImplementations';
@@ -74,7 +75,7 @@
 let languageClient: LanguageClient;
 let languageServerDisposable: vscode.Disposable;
 let latestConfig: LanguageServerConfig;
-let serverOutputChannel: vscode.OutputChannel;
+export let serverOutputChannel: vscode.OutputChannel;
 let serverTraceChannel: vscode.OutputChannel;
 let crashCount = 0;
 
@@ -115,6 +116,8 @@
 	if (!started && defaultLanguageProviders.length === 0) {
 		registerDefaultProviders(ctx);
 	}
+
+	updateLanguageServerIconGoStatusBar(started, cfg.serverName);
 }
 
 // scheduleGoplsSuggestions sets timeouts for the various gopls-specific
diff --git a/src/goMain.ts b/src/goMain.ts
index 7c94d91..9104b37 100644
--- a/src/goMain.ts
+++ b/src/goMain.ts
@@ -38,7 +38,7 @@
 import { playgroundCommand } from './goPlayground';
 import { GoReferencesCodeLensProvider } from './goReferencesCodelens';
 import { GoRunTestCodeLensProvider } from './goRunTestCodelens';
-import { outputChannel, showHideStatus } from './goStatus';
+import { expandGoStatusBar, outputChannel, showHideStatus } from './goStatus';
 import { subTestAtCursor, testAtCursor, testCurrentFile, testCurrentPackage, testPrevious, testWorkspace } from './goTest';
 import { getConfiguredTools } from './goTools';
 import { vetCode } from './goVet';
@@ -108,11 +108,8 @@
 	initCoverageDecorators(ctx);
 
 	ctx.subscriptions.push(
-		vscode.commands.registerCommand('go.open.modulesdoc', async () => {
-			vscode.commands.executeCommand(
-				'vscode.open',
-				vscode.Uri.parse('https://github.com/golang/vscode-go/blob/master/docs/modules.md')
-			);
+		vscode.commands.registerCommand('go.environment.status', async () => {
+			expandGoStatusBar();
 		})
 	);
 	const testCodeLensProvider = new GoRunTestCodeLensProvider();
diff --git a/src/goStatus.ts b/src/goStatus.ts
index c13cd4f..bac8c3b 100644
--- a/src/goStatus.ts
+++ b/src/goStatus.ts
@@ -1,24 +1,24 @@
 /*---------------------------------------------------------
  * Copyright (C) Microsoft Corporation. All rights reserved.
+ * Modification copyright 2020 The Go Authors. All rights reserved.
  * Licensed under the MIT License. See LICENSE in the project root for license information.
  *--------------------------------------------------------*/
 
 'use strict';
 
+import path = require('path');
 import vscode = require('vscode');
+import { buildLanguageServerConfig, getLocalGoplsVersion, serverOutputChannel } from './goLanguageServer';
 import { GO_MODE } from './goMode';
-import { isModSupported } from './goModules';
+import { getModFolderPath, isModSupported } from './goModules';
 
 export let outputChannel = vscode.window.createOutputChannel('Go');
 
 export let diagnosticsStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
 
 let statusBarEntry: vscode.StatusBarItem;
-const statusBarItemModule = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
-statusBarItemModule.text = '$(megaphone) Go Modules';
-statusBarItemModule.tooltip =
-	'Modules is enabled for this project. Click to learn more about Modules support in VS Code.';
-statusBarItemModule.command = 'go.open.modulesdoc';
+let modulePath: string;
+export const languageServerIcon = '$(zap)';
 
 export function showHideStatus(editor: vscode.TextEditor) {
 	if (statusBarEntry) {
@@ -34,16 +34,58 @@
 	if (editor) {
 		isModSupported(editor.document.uri).then((isMod) => {
 			if (isMod) {
-				statusBarItemModule.show();
+				getModFolderPath(editor.document.uri).then((p) => modulePath = p);
 			} else {
-				statusBarItemModule.hide();
+				modulePath = '';
 			}
 		});
-	} else {
-		statusBarItemModule.hide();
 	}
 }
 
+export async function expandGoStatusBar() {
+	const options = [
+		{label: `Locate Configured Go Tools`, description: 'display go env'},
+		{label: `Choose Go Environment`}
+	];
+
+	// Get the gopls configuration
+	const cfg = buildLanguageServerConfig();
+	if (cfg.serverName === 'gopls') {
+		const goplsVersion = await getLocalGoplsVersion(cfg);
+		options.push({label: `${languageServerIcon}Open 'gopls' trace`, description: `${goplsVersion}`});
+	}
+
+	// If modules is enabled, add link to mod file
+	if (!!modulePath) {
+		options.push({label: `Open 'go.mod'`, description: path.join(modulePath, 'go.mod')});
+	}
+
+	vscode.window.showQuickPick(options).then((item) => {
+		if (!!item) {
+			switch (item.label) {
+				case `Locate Configured Go Tools`:
+					vscode.commands.executeCommand('go.locate.tools');
+					break;
+				case `Choose Go Environment`:
+					vscode.commands.executeCommand('go.environment.choose');
+					break;
+				case `${languageServerIcon}Open 'gopls' trace`:
+					if (!!serverOutputChannel) {
+						serverOutputChannel.show();
+					}
+					break;
+				case `Open 'go.mod'`:
+					const openPath = vscode.Uri.file(item.description);
+					vscode.workspace.openTextDocument(openPath).then((doc) => {
+						vscode.window.showTextDocument(doc);
+					});
+					break;
+			}
+		}
+	});
+
+}
+
 export function hideGoStatus() {
 	if (statusBarEntry) {
 		statusBarEntry.dispose();
diff --git a/test/integration/statusbar.test.ts b/test/integration/statusbar.test.ts
index 10d1dcf..124cf91 100644
--- a/test/integration/statusbar.test.ts
+++ b/test/integration/statusbar.test.ts
@@ -44,8 +44,13 @@
 	it('should create a status bar item with a label matching go.goroot version', async () => {
 		const version = await ourutil.getGoVersion();
 		const versionLabel = formatGoVersion(version);
+		let label = getGoEnvironmentStatusbarItem().text;
+		const iconPos = label.indexOf('$');
+		if (iconPos >= 0) {
+			label = label.substring(0, iconPos);
+		}
 		assert.equal(
-			getGoEnvironmentStatusbarItem().text,
+			label,
 			versionLabel,
 			'goroot version does not match status bar item text'
 		);