goLanguageServer: add special handling for incorrect flags

Offer a more helpful error message to the user when the language server
can't be started because of an invalid "go.useLanguageServerFlags"
setting. Also, try more times to collect the gopls logs because I
noticed it didn't always collect them in time.

Change-Id: I137a6c0778ec08d16673ad2118716417e558b789
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/290289
Trust: Rebecca Stambler <rstambler@golang.org>
Trust: Hyang-Ah Hana Kim <hyangah@gmail.com>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
TryBot-Result: kokoro <noreply+kokoro@google.com>
diff --git a/src/goLanguageServer.ts b/src/goLanguageServer.ts
index 3875ad1..6a62d9e 100644
--- a/src/goLanguageServer.ts
+++ b/src/goLanguageServer.ts
@@ -336,13 +336,13 @@
 			},
 			errorHandler: {
 				error: (error: Error, message: Message, count: number): ErrorAction => {
-					vscode.window.showErrorMessage(
-						`Error communicating with the language server: ${error}: ${message}.`
-					);
 					// Allow 5 crashes before shutdown.
 					if (count < 5) {
 						return ErrorAction.Continue;
 					}
+					vscode.window.showErrorMessage(
+						`Error communicating with the language server: ${error}: ${message}.`
+					);
 					return ErrorAction.Shutdown;
 				},
 				closed: (): CloseAction => {
@@ -1320,7 +1320,30 @@
 			return;
 		}
 	}
-	const selected = await vscode.window.showInformationMessage(`${msg} Would you like to report a gopls issue on GitHub?
+
+	const { sanitizedLog, failureReason } = await collectGoplsLog();
+
+	// If the user has invalid values for "go.languageServerFlags", we may get
+	// this error. Prompt them to double check their flags.
+	let selected: string;
+	if (failureReason === GoplsFailureModes.INCORRECT_COMMAND_USAGE) {
+		const languageServerFlags = getGoConfig()['languageServerFlags'] as string[];
+		if (languageServerFlags && languageServerFlags.length > 0) {
+			selected = await vscode.window.showInformationMessage(`The extension was unable to start the language server.
+You may have an invalid value in your "go.languageServerFlags" setting.
+It is currently set to [${languageServerFlags}]. Please correct the setting by navigating to Preferences -> Settings.`,
+'Open settings', 'I need more help.');
+			switch (selected) {
+				case 'Open settings':
+					await vscode.commands.executeCommand('workbench.action.openSettings', 'go.languageServerFlags');
+					return;
+				case 'I need more help':
+					// Fall through the automated issue report.
+					break;
+			}
+		}
+	}
+	selected = await vscode.window.showInformationMessage(`${msg} Would you like to report a gopls issue on GitHub?
 You will be asked to provide additional information and logs, so PLEASE READ THE CONTENT IN YOUR BROWSER.`, 'Yes', 'Next time', 'Never');
 	switch (selected) {
 		case 'Yes':
@@ -1339,7 +1362,6 @@
 			const extInfo = getExtensionInfo();
 			const settings = latestConfig.flags.join(' ');
 			const title = `gopls: automated issue report (${errKind})`;
-			const { sanitizedLog, failureReason } = await collectGoplsLog();
 			const goplsLog = (sanitizedLog) ?
 				`<pre>${sanitizedLog}</pre>` :
 				`Please attach the stack trace from the crash.
@@ -1453,7 +1475,7 @@
 	// document, since we just surfaced the output channel to the user.
 	// See https://github.com/microsoft/vscode/issues/65108.
 	let logs: string;
-	for (let i = 0; i < 3; i++) {
+	for (let i = 0; i < 10; i++) {
 		// try a couple of times until successfully finding the channel.
 		for (const doc of vscode.workspace.textDocuments) {
 			if (doc.languageId !== 'Log') {
@@ -1473,17 +1495,25 @@
 			break;
 		}
 		// sleep a bit before the next try. The choice of the sleep time is arbitrary.
-		await sleep((i + 1) * 10);
+		await sleep((i + 1) * 100);
 	}
 
 	return sanitizeGoplsTrace(logs);
 }
 
+enum GoplsFailureModes {
+	NO_GOPLS_LOG = 'no gopls log',
+	EMPTY_PANIC_TRACE = 'empty panic trace',
+	INCOMPLETE_PANIC_TRACE = 'incomplete panic trace',
+	INCORRECT_COMMAND_USAGE = 'incorrect gopls command usage',
+	UNRECOGNIZED_CRASH_PATTERN = 'unrecognized crash pattern'
+}
+
 // capture only panic stack trace and the initialization error message.
 // exported for testing.
 export function sanitizeGoplsTrace(logs?: string): { sanitizedLog?: string, failureReason?: string } {
 	if (!logs) {
-		return { failureReason: 'no gopls log' };
+		return { failureReason: GoplsFailureModes.NO_GOPLS_LOG };
 	}
 	const panicMsgBegin = logs.lastIndexOf('panic: ');
 	if (panicMsgBegin > -1) {  // panic message was found.
@@ -1509,9 +1539,9 @@
 			if (sanitized) {
 				return { sanitizedLog: sanitized };
 			}
-			return { failureReason: 'empty panic trace' };
+			return { failureReason: GoplsFailureModes.EMPTY_PANIC_TRACE };
 		}
-		return { failureReason: 'incomplete panic trace' };
+		return { failureReason: GoplsFailureModes.INCOMPLETE_PANIC_TRACE };
 	}
 	const initFailMsgBegin = logs.lastIndexOf('Starting client failed');
 	if (initFailMsgBegin > -1) {  // client start failed. Capture up to the 'Code:' line.
@@ -1522,9 +1552,9 @@
 		}
 	}
 	if (logs.lastIndexOf('Usage: gopls') > -1) {
-		return { failureReason: 'incorrect gopls command usage' };
+		return { failureReason: GoplsFailureModes.INCORRECT_COMMAND_USAGE };
 	}
-	return { failureReason: 'unrecognized crash pattern' };
+	return { failureReason: GoplsFailureModes.UNRECOGNIZED_CRASH_PATTERN };
 }
 
 function languageServerUsingDefault(cfg: vscode.WorkspaceConfiguration): boolean {