goLanguageServer: prompt user to fill out survey when they disable gopls

Change-Id: Iaa28bbb4fa590028d7582543f35cb798717f2713
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/291010
Trust: Rebecca Stambler <rstambler@golang.org>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
diff --git a/src/goLanguageServer.ts b/src/goLanguageServer.ts
index 50d24fa..a4157e6 100644
--- a/src/goLanguageServer.ts
+++ b/src/goLanguageServer.ts
@@ -199,16 +199,8 @@
 			if (cfg.serverName !== '') {
 				return;
 			}
-			// Check if the configuration is set in the workspace.
-			const useLanguageServer = getGoConfig().inspect('useLanguageServer');
-			let workspace: boolean;
-			if (useLanguageServer.workspaceFolderValue === false || useLanguageServer.workspaceValue === false) {
-				workspace = true;
-			}
 			// Prompt the user to enable gopls and record what actions they took.
-			let optOutCfg = getGoplsOptOutConfig(workspace);
-			optOutCfg = await promptAboutGoplsOptOut(optOutCfg);
-			flushGoplsOptOutConfig(optOutCfg, workspace);
+			await promptAboutGoplsOptOut(false);
 			// Check if the language server has now been enabled, and if so,
 			// it will be installed below.
 			cfg = buildLanguageServerConfig(getGoConfig());
@@ -232,6 +224,76 @@
 	setTimeout(survey, 30 * timeMinute);
 }
 
+export async function promptAboutGoplsOptOut(surveyOnly: boolean) {
+	// Check if the configuration is set in the workspace.
+	const useLanguageServer = getGoConfig().inspect('useLanguageServer');
+	const workspace = useLanguageServer.workspaceFolderValue === false || useLanguageServer.workspaceValue === false;
+
+	let cfg = getGoplsOptOutConfig(workspace);
+	const promptFn = async (): Promise<GoplsOptOutConfig> => {
+		if (cfg.prompt === false) {
+			return cfg;
+		}
+		// Prompt the user ~once a month.
+		if (cfg.lastDatePrompted && daysBetween(new Date(), cfg.lastDatePrompted) < 30) {
+			return cfg;
+		}
+		cfg.lastDatePrompted = new Date();
+		if (surveyOnly) {
+			await promptForGoplsOptOutSurvey(cfg, `Looks like you've disabled the Go language server, which is the recommended default for this extension.
+Would you be willing to tell us why you've disabled it?`);
+			return cfg;
+		}
+		const selected = await vscode.window.showInformationMessage(`We noticed that you have disabled the language server.
+It has [stabilized](https://blog.golang.org/gopls-vscode-go) and is now enabled by default in this extension.
+Would you like to enable it now?`, { title: 'Enable' }, { title: 'Not now' }, { title: 'Never' });
+		if (!selected) {
+			return cfg;
+		}
+		switch (selected.title) {
+			case 'Enable':
+				// Change the user's Go configuration to enable the language server.
+				// Remove the setting entirely, since it's on by default now.
+				const goConfig = getGoConfig();
+				await goConfig.update('useLanguageServer', undefined, vscode.ConfigurationTarget.Global);
+				if (goConfig.inspect('useLanguageServer').workspaceValue === false) {
+					await goConfig.update('useLanguageServer', undefined, vscode.ConfigurationTarget.Workspace);
+				}
+				if (goConfig.inspect('useLanguageServer').workspaceFolderValue === false) {
+					await goConfig.update('useLanguageServer', undefined, vscode.ConfigurationTarget.WorkspaceFolder);
+				}
+				cfg.prompt = false;
+				break;
+			case 'Not now':
+				cfg.prompt = true;
+				break;
+			case 'Never':
+				cfg.prompt = false;
+				await promptForGoplsOptOutSurvey(cfg, `No problem. Would you be willing to tell us why you have opted out of the language server?`);
+				break;
+		}
+		return cfg;
+	};
+	cfg = await promptFn();
+	flushGoplsOptOutConfig(cfg, workspace);
+}
+
+async function promptForGoplsOptOutSurvey(cfg: GoplsOptOutConfig, msg: string): Promise<GoplsOptOutConfig> {
+	const s = await vscode.window.showInformationMessage(msg, { title: 'Yes' }, { title: 'No' });
+	if (!s) {
+		return cfg;
+	}
+	switch (s.title) {
+		case 'Yes':
+			cfg.prompt = false;
+			await vscode.env.openExternal(vscode.Uri.parse(`https://forms.gle/hwC8CncV7Cyc2yBN6`));
+			break;
+		case 'No':
+			break;
+	}
+	return cfg;
+}
+
 export interface GoplsOptOutConfig {
 	prompt?: boolean;
 	lastDatePrompted?: Date;
@@ -239,9 +301,9 @@
 
 const goplsOptOutConfigKey = 'goplsOptOutConfig';
 
-function getGoplsOptOutConfig(workspace: boolean): GoplsOptOutConfig {
+export const getGoplsOptOutConfig = (workspace: boolean): GoplsOptOutConfig => {
 	return getStateConfig(goplsOptOutConfigKey, workspace) as GoplsOptOutConfig;
-}
+};
 
 function flushGoplsOptOutConfig(cfg: GoplsOptOutConfig, workspace: boolean) {
 	if (workspace) {
@@ -250,58 +312,6 @@
 	updateGlobalState(goplsOptOutConfigKey, JSON.stringify(cfg));
 }
 
-export async function promptAboutGoplsOptOut(cfg: GoplsOptOutConfig): Promise<GoplsOptOutConfig> {
-	if (cfg.prompt === false) {
-		return cfg;
-	}
-	// Prompt the user ~once a month.
-	if (cfg.lastDatePrompted && daysBetween(new Date(), cfg.lastDatePrompted) < 30) {
-		return cfg;
-	}
-	cfg.lastDatePrompted = new Date();
-	const selected = await vscode.window.showInformationMessage(`We noticed that you have disabled the language server.
-It has [stabilized](https://blog.golang.org/gopls-vscode-go) and is now enabled by default in this extension.
-Would you like to enable it now?`, { title: 'Enable' }, { title: 'Not now' }, { title: 'Never' });
-	if (!selected) {
-		return cfg;
-	}
-	switch (selected.title) {
-		case 'Enable':
-			// Change the user's Go configuration to enable the language server.
-			// Remove the setting entirely, since it's on by default now.
-			const goConfig = getGoConfig();
-			await goConfig.update('useLanguageServer', undefined, vscode.ConfigurationTarget.Global);
-			if (goConfig.inspect('useLanguageServer').workspaceValue === false) {
-				await goConfig.update('useLanguageServer', undefined, vscode.ConfigurationTarget.Workspace);
-			}
-			if (goConfig.inspect('useLanguageServer').workspaceFolderValue === false) {
-				await goConfig.update('useLanguageServer', undefined, vscode.ConfigurationTarget.WorkspaceFolder);
-			}
-
-			cfg.prompt = false;
-			break;
-		case 'Not now':
-			cfg.prompt = true;
-			break;
-		case 'Never':
-			cfg.prompt = false;
-			await promptForGoplsOptOutSurvey();
-			break;
-	}
-	return cfg;
-}
-
-async function promptForGoplsOptOutSurvey() {
-	const selected = await vscode.window.showInformationMessage(`No problem. Would you be willing to tell us why you have opted out of the language server?`, { title: 'Yes' }, { title: 'No' });
-	switch (selected.title) {
-		case 'Yes':
-			await vscode.env.openExternal(vscode.Uri.parse(`https://forms.gle/hwC8CncV7Cyc2yBN6`));
-			break;
-		case 'No':
-			break;
-	}
-}
-
 async function startLanguageServer(ctx: vscode.ExtensionContext, config: LanguageServerConfig): Promise<boolean> {
 	// If the client has already been started, make sure to clear existing
 	// diagnostics and stop it.
@@ -786,7 +796,7 @@
 	defaultLanguageProviders = [];
 }
 
-export function watchLanguageServerConfiguration(e: vscode.ConfigurationChangeEvent) {
+export async function watchLanguageServerConfiguration(e: vscode.ConfigurationChangeEvent) {
 	if (!e.affectsConfiguration('go')) {
 		return;
 	}
@@ -801,6 +811,10 @@
 	) {
 		restartLanguageServer();
 	}
+
+	if (e.affectsConfiguration('go.useLanguageServer') && getGoConfig()['useLanguageServer'] === false) {
+		promptAboutGoplsOptOut(true);
+	}
 }
 
 export function buildLanguageServerConfig(goConfig: vscode.WorkspaceConfiguration): LanguageServerConfig {
diff --git a/test/gopls/survey.test.ts b/test/gopls/survey.test.ts
index 3e667db..405c6af 100644
--- a/test/gopls/survey.test.ts
+++ b/test/gopls/survey.test.ts
@@ -6,12 +6,12 @@
 import * as assert from 'assert';
 import sinon = require('sinon');
 import vscode = require('vscode');
-import { GoplsOptOutConfig, promptAboutGoplsOptOut, shouldPromptForGoplsSurvey, SurveyConfig } from '../../src/goLanguageServer';
+import goLanguageServer = require('../../src/goLanguageServer');
 
 suite('gopls survey tests', () => {
 	test('prompt for survey', () => {
 		// global state -> offer survey
-		const testCases: [SurveyConfig, boolean][] = [
+		const testCases: [goLanguageServer.SurveyConfig, boolean][] = [
 			// User who is activating the extension for the first time.
 			[
 				{},
@@ -73,7 +73,7 @@
 			sinon.replace(Math, 'random', () => 0);
 
 			const now = new Date('2020-04-29');
-			const gotPrompt = shouldPromptForGoplsSurvey(now, testConfig);
+			const gotPrompt = goLanguageServer.shouldPromptForGoplsSurvey(now, testConfig);
 			if (wantPrompt) {
 				assert.ok(gotPrompt, `prompt determination failed for ${i}`);
 			} else {
@@ -95,7 +95,7 @@
 		sandbox.restore();
 	});
 
-	const testCases: [GoplsOptOutConfig, string, number][] = [
+	const testCases: [goLanguageServer.GoplsOptOutConfig, string, number][] = [
 		// No saved config, different choices in the first dialog box.
 		[{}, 'Enable', 1],
 		[{}, 'Not now', 1],
@@ -110,10 +110,11 @@
 	testCases.map(async ([testConfig, choice, wantCount], i) => {
 		test(`opt out: ${i}`, async () => {
 			const stub = sandbox.stub(vscode.window, 'showInformationMessage').resolves({ title: choice });
+			const getGoplsOptOutConfigStub = sandbox.stub(goLanguageServer, 'getGoplsOptOutConfig').returns(testConfig);
 
-			await promptAboutGoplsOptOut(testConfig);
+			await goLanguageServer.promptAboutGoplsOptOut(false);
 			assert.strictEqual(stub.callCount, wantCount);
-
+			sandbox.assert.called(getGoplsOptOutConfigStub);
 		});
 	});
 });