vscode-go: refactor various commands to factory pattern

Change-Id: Ieef9f2527168466bf6c8dcefbb242e3d399fd905
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/405076
TryBot-Result: kokoro <noreply+kokoro@google.com>
Run-TryBot: Jamal Carvalho <jamal@golang.org>
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
diff --git a/src/goBrowsePackage.ts b/src/goBrowsePackage.ts
index f032b6b..fc109be 100644
--- a/src/goBrowsePackage.ts
+++ b/src/goBrowsePackage.ts
@@ -10,11 +10,12 @@
 import cp = require('child_process');
 import path = require('path');
 import vscode = require('vscode');
+import { CommandFactory } from './commands';
 import { getAllPackages } from './goPackages';
 import { getBinPath, getCurrentGoPath, getImportPath } from './util';
 import { envPath, getCurrentGoRoot } from './utils/pathUtils';
 
-export function browsePackages() {
+export const browsePackages: CommandFactory = () => () => {
 	let workDir = '';
 	let selectedText = '';
 	const editor = vscode.window.activeTextEditor;
@@ -36,7 +37,7 @@
 	}
 
 	showPackageFiles(selectedText, true, workDir);
-}
+};
 
 function showPackageFiles(pkg: string, showAllPkgsIfPkgNotFound: boolean, workDir: string) {
 	const goRuntimePath = getBinPath('go');
diff --git a/src/goBuild.ts b/src/goBuild.ts
index fdcfea6..6b16814 100644
--- a/src/goBuild.ts
+++ b/src/goBuild.ts
@@ -5,6 +5,7 @@
 
 import path = require('path');
 import vscode = require('vscode');
+import { CommandFactory } from './commands';
 import { getGoConfig } from './config';
 import { toolExecutionEnvironment } from './goEnv';
 import { buildDiagnosticCollection } from './goMain';
@@ -26,39 +27,41 @@
 /**
  * Builds current package or workspace.
  */
-export function buildCode(buildWorkspace?: boolean) {
-	const editor = vscode.window.activeTextEditor;
-	if (!buildWorkspace) {
-		if (!editor) {
-			vscode.window.showInformationMessage('No editor is active, cannot find current package to build');
-			return;
+export function buildCode(buildWorkspace?: boolean): CommandFactory {
+	return () => () => {
+		const editor = vscode.window.activeTextEditor;
+		if (!buildWorkspace) {
+			if (!editor) {
+				vscode.window.showInformationMessage('No editor is active, cannot find current package to build');
+				return;
+			}
+			if (editor.document.languageId !== 'go') {
+				vscode.window.showInformationMessage(
+					'File in the active editor is not a Go file, cannot find current package to build'
+				);
+				return;
+			}
 		}
-		if (editor.document.languageId !== 'go') {
-			vscode.window.showInformationMessage(
-				'File in the active editor is not a Go file, cannot find current package to build'
-			);
-			return;
-		}
-	}
 
-	const documentUri = editor?.document.uri;
-	const goConfig = getGoConfig(documentUri);
+		const documentUri = editor?.document.uri;
+		const goConfig = getGoConfig(documentUri);
 
-	outputChannel.clear(); // Ensures stale output from build on save is cleared
-	diagnosticsStatusBarItem.show();
-	diagnosticsStatusBarItem.text = 'Building...';
+		outputChannel.clear(); // Ensures stale output from build on save is cleared
+		diagnosticsStatusBarItem.show();
+		diagnosticsStatusBarItem.text = 'Building...';
 
-	isModSupported(documentUri).then((isMod) => {
-		goBuild(documentUri, isMod, goConfig, buildWorkspace)
-			.then((errors) => {
-				handleDiagnosticErrors(editor?.document, errors, buildDiagnosticCollection);
-				diagnosticsStatusBarItem.hide();
-			})
-			.catch((err) => {
-				vscode.window.showInformationMessage('Error: ' + err);
-				diagnosticsStatusBarItem.text = 'Build Failed';
-			});
-	});
+		isModSupported(documentUri).then((isMod) => {
+			goBuild(documentUri, isMod, goConfig, buildWorkspace)
+				.then((errors) => {
+					handleDiagnosticErrors(editor?.document, errors, buildDiagnosticCollection);
+					diagnosticsStatusBarItem.hide();
+				})
+				.catch((err) => {
+					vscode.window.showInformationMessage('Error: ' + err);
+					diagnosticsStatusBarItem.text = 'Build Failed';
+				});
+		});
+	};
 }
 
 /**
diff --git a/src/goCover.ts b/src/goCover.ts
index d2f8bcc..24078e8 100644
--- a/src/goCover.ts
+++ b/src/goCover.ts
@@ -11,6 +11,7 @@
 import fs = require('fs');
 import path = require('path');
 import vscode = require('vscode');
+import { CommandFactory } from './commands';
 import { getGoConfig } from './config';
 import { isModSupported } from './goModules';
 import { getImportPathToFolder } from './goPackages';
@@ -489,7 +490,7 @@
  * If current editor has Code coverage applied, then remove it.
  * Else run tests to get the coverage and apply.
  */
-export async function toggleCoverageCurrentPackage() {
+export const toggleCoverageCurrentPackage: CommandFactory = () => async () => {
 	const editor = vscode.window.activeTextEditor;
 	if (!editor) {
 		vscode.window.showInformationMessage('No editor is active.');
@@ -520,7 +521,7 @@
 			showTestOutput();
 		}
 	});
-}
+};
 
 export function isPartOfComment(e: vscode.TextDocumentChangeEvent): boolean {
 	return e.contentChanges.every((change) => {
diff --git a/src/goEnvironmentStatus.ts b/src/goEnvironmentStatus.ts
index 7328326..c770bb1 100644
--- a/src/goEnvironmentStatus.ts
+++ b/src/goEnvironmentStatus.ts
@@ -30,6 +30,7 @@
 import vscode = require('vscode');
 import WebRequest = require('web-request');
 import { installTool } from './goInstallTools';
+import { CommandFactory } from './commands';
 
 export class GoEnvironmentOption implements vscode.QuickPickItem {
 	readonly description: string;
@@ -68,7 +69,7 @@
 /**
  * Present a command palette menu to the user to select their go binary
  */
-export async function chooseGoEnvironment() {
+export const chooseGoEnvironment: CommandFactory = () => async () => {
 	if (!goEnvStatusbarItem) {
 		return;
 	}
@@ -125,7 +126,7 @@
 	} catch (e) {
 		vscode.window.showErrorMessage((e as Error).message);
 	}
-}
+};
 
 /**
  * update the selected go path and label in the workspace state
diff --git a/src/goGenerateTests.ts b/src/goGenerateTests.ts
index e06354d..2a2d0f1 100644
--- a/src/goGenerateTests.ts
+++ b/src/goGenerateTests.ts
@@ -17,6 +17,8 @@
 import { GoDocumentSymbolProvider } from './goDocumentSymbols';
 import { outputChannel } from './goStatus';
 import { getBinPath } from './util';
+import { CommandFactory } from './commands';
+import { GoExtensionContext } from './context';
 
 const generatedWord = 'Generated ';
 
@@ -43,7 +45,7 @@
 /**
  * Toggles between file in current active editor and the corresponding test file.
  */
-export function toggleTestFile(): void {
+export const toggleTestFile: CommandFactory = () => () => {
 	const editor = vscode.window.activeTextEditor;
 	if (!editor) {
 		vscode.window.showInformationMessage('Cannot toggle test file. No editor selected.');
@@ -67,38 +69,42 @@
 		}
 	}
 	vscode.commands.executeCommand('vscode.open', vscode.Uri.file(targetFilePath));
-}
+};
 
-export async function generateTestCurrentPackage(): Promise<boolean> {
+export const generateTestCurrentPackage: CommandFactory = (ctx, goCtx) => () => {
 	const editor = checkActiveEditor();
 	if (!editor) {
 		return false;
 	}
 	return generateTests(
+		ctx,
+		goCtx,
 		{
 			dir: path.dirname(editor.document.uri.fsPath),
 			isTestFile: editor.document.fileName.endsWith('_test.go')
 		},
 		getGoConfig(editor.document.uri)
 	);
-}
+};
 
-export async function generateTestCurrentFile(): Promise<boolean> {
+export const generateTestCurrentFile: CommandFactory = (ctx, goCtx) => () => {
 	const editor = checkActiveEditor();
 	if (!editor) {
 		return false;
 	}
 
 	return generateTests(
+		ctx,
+		goCtx,
 		{
 			dir: editor.document.uri.fsPath,
 			isTestFile: editor.document.fileName.endsWith('_test.go')
 		},
 		getGoConfig(editor.document.uri)
 	);
-}
+};
 
-export async function generateTestCurrentFunction(): Promise<boolean> {
+export const generateTestCurrentFunction: CommandFactory = (ctx, goCtx) => async () => {
 	const editor = checkActiveEditor();
 	if (!editor) {
 		return false;
@@ -122,6 +128,8 @@
 	}
 
 	return generateTests(
+		ctx,
+		goCtx,
 		{
 			dir: editor.document.uri.fsPath,
 			func: funcName,
@@ -129,7 +137,7 @@
 		},
 		getGoConfig(editor.document.uri)
 	);
-}
+};
 
 /**
  * Input to goTests.
@@ -150,7 +158,12 @@
 	isTestFile?: boolean;
 }
 
-function generateTests(conf: Config, goConfig: vscode.WorkspaceConfiguration): Promise<boolean> {
+function generateTests(
+	ctx: vscode.ExtensionContext,
+	goCtx: GoExtensionContext,
+	conf: Config,
+	goConfig: vscode.WorkspaceConfiguration
+): Promise<boolean> {
 	return new Promise<boolean>((resolve, reject) => {
 		const cmd = getBinPath('gotests');
 		let args = ['-w'];
@@ -209,7 +222,7 @@
 				outputChannel.append(message);
 
 				if (testsGenerated && !conf.isTestFile) {
-					toggleTestFile();
+					toggleTestFile(ctx, goCtx)();
 				}
 
 				return resolve(true);
diff --git a/src/goGetPackage.ts b/src/goGetPackage.ts
index 6e8ce30..bc1426e 100644
--- a/src/goGetPackage.ts
+++ b/src/goGetPackage.ts
@@ -7,12 +7,13 @@
 
 import cp = require('child_process');
 import vscode = require('vscode');
+import { CommandFactory } from './commands';
 import { buildCode } from './goBuild';
 import { outputChannel } from './goStatus';
 import { getBinPath, getCurrentGoPath, getImportPath } from './util';
 import { envPath, getCurrentGoRoot } from './utils/pathUtils';
 
-export function goGetPackage() {
+export const goGetPackage: CommandFactory = (ctx, goCtx) => () => {
 	const editor = vscode.window.activeTextEditor;
 	const selection = editor?.selection;
 	const selectedText = editor?.document.lineAt(selection?.active.line ?? 0).text ?? '';
@@ -37,11 +38,11 @@
 			outputChannel.show();
 			outputChannel.clear();
 			outputChannel.appendLine(stderr);
-			buildCode();
+			buildCode(false)(ctx, goCtx)();
 			return;
 		}
 
 		// go get -v doesn't write anything when the package already exists
 		vscode.window.showInformationMessage(`Package already exists: ${importPath}`);
 	});
-}
+};
diff --git a/src/goImport.ts b/src/goImport.ts
index e6d4520..7c7cc2a 100644
--- a/src/goImport.ts
+++ b/src/goImport.ts
@@ -17,6 +17,7 @@
 import { getBinPath, getImportPath, parseFilePrelude } from './util';
 import { envPath, getCurrentGoRoot } from './utils/pathUtils';
 import { GoExtensionContext } from './context';
+import { CommandFactory } from './commands';
 
 const missingToolMsg = 'Missing tool: ';
 
@@ -163,7 +164,7 @@
 	}
 }
 
-export function addImport(goCtx: GoExtensionContext, arg: { importPath: string }) {
+export const addImport: CommandFactory = (ctx, goCtx) => (arg: { importPath: string }) => {
 	const { languageClient, serverInfo } = goCtx;
 	const editor = vscode.window.activeTextEditor;
 	if (!editor) {
@@ -209,9 +210,9 @@
 			vscode.workspace.applyEdit(edit);
 		}
 	});
-}
+};
 
-export function addImportToWorkspace() {
+export const addImportToWorkspace: CommandFactory = () => () => {
 	const editor = vscode.window.activeTextEditor;
 	if (!editor) {
 		vscode.window.showErrorMessage('No active editor found to determine current package.');
@@ -277,4 +278,4 @@
 			{ uri: importPathUri }
 		);
 	});
-}
+};
diff --git a/src/goInstall.ts b/src/goInstall.ts
index aecf947..a1fab87 100644
--- a/src/goInstall.ts
+++ b/src/goInstall.ts
@@ -6,6 +6,7 @@
 import cp = require('child_process');
 import path = require('path');
 import vscode = require('vscode');
+import { CommandFactory } from './commands';
 import { getGoConfig } from './config';
 import { toolExecutionEnvironment } from './goEnv';
 import { isModSupported } from './goModules';
@@ -13,7 +14,7 @@
 import { getBinPath, getCurrentGoPath, getModuleCache } from './util';
 import { envPath, getCurrentGoRoot, getCurrentGoWorkspaceFromGOPATH } from './utils/pathUtils';
 
-export async function installCurrentPackage(): Promise<void> {
+export const installCurrentPackage: CommandFactory = () => async () => {
 	const editor = vscode.window.activeTextEditor;
 	if (!editor) {
 		vscode.window.showInformationMessage('No editor is active, cannot find current package to install');
@@ -64,4 +65,4 @@
 	cp.execFile(goRuntimePath, args, { env, cwd }, (err, stdout, stderr) => {
 		outputChannel.appendLine(err ? `Installation failed: ${stderr}` : 'Installation successful');
 	});
-}
+};
diff --git a/src/goLint.ts b/src/goLint.ts
index db1171c..f3ede74 100644
--- a/src/goLint.ts
+++ b/src/goLint.ts
@@ -5,47 +5,51 @@
 
 import path = require('path');
 import vscode = require('vscode');
+import { CommandFactory } from './commands';
 import { getGoConfig, getGoplsConfig } from './config';
 import { toolExecutionEnvironment } from './goEnv';
 import { lintDiagnosticCollection } from './goMain';
 import { diagnosticsStatusBarItem, outputChannel } from './goStatus';
 import { goplsStaticcheckEnabled } from './goTools';
 import { getWorkspaceFolderPath, handleDiagnosticErrors, ICheckResult, resolvePath, runTool } from './util';
+
 /**
  * Runs linter on the current file, package or workspace.
  */
-export function lintCode(scope?: string) {
-	const editor = vscode.window.activeTextEditor;
-	if (scope !== 'workspace') {
-		if (!editor) {
-			vscode.window.showInformationMessage('No editor is active, cannot find current package to lint');
-			return;
+export function lintCode(scope?: string): CommandFactory {
+	return () => () => {
+		const editor = vscode.window.activeTextEditor;
+		if (scope !== 'workspace') {
+			if (!editor) {
+				vscode.window.showInformationMessage('No editor is active, cannot find current package to lint');
+				return;
+			}
+			if (editor.document.languageId !== 'go') {
+				vscode.window.showInformationMessage(
+					'File in the active editor is not a Go file, cannot find current package to lint'
+				);
+				return;
+			}
 		}
-		if (editor.document.languageId !== 'go') {
-			vscode.window.showInformationMessage(
-				'File in the active editor is not a Go file, cannot find current package to lint'
-			);
-			return;
-		}
-	}
 
-	const documentUri = editor ? editor.document.uri : undefined;
-	const goConfig = getGoConfig(documentUri);
-	const goplsConfig = getGoplsConfig(documentUri);
+		const documentUri = editor ? editor.document.uri : undefined;
+		const goConfig = getGoConfig(documentUri);
+		const goplsConfig = getGoplsConfig(documentUri);
 
-	outputChannel.clear(); // Ensures stale output from lint on save is cleared
-	diagnosticsStatusBarItem.show();
-	diagnosticsStatusBarItem.text = 'Linting...';
+		outputChannel.clear(); // Ensures stale output from lint on save is cleared
+		diagnosticsStatusBarItem.show();
+		diagnosticsStatusBarItem.text = 'Linting...';
 
-	goLint(documentUri, goConfig, goplsConfig, scope)
-		.then((warnings) => {
-			handleDiagnosticErrors(editor ? editor.document : undefined, warnings, lintDiagnosticCollection);
-			diagnosticsStatusBarItem.hide();
-		})
-		.catch((err) => {
-			vscode.window.showInformationMessage('Error: ' + err);
-			diagnosticsStatusBarItem.text = 'Linting Failed';
-		});
+		goLint(documentUri, goConfig, goplsConfig, scope)
+			.then((warnings) => {
+				handleDiagnosticErrors(editor ? editor.document : undefined, warnings, lintDiagnosticCollection);
+				diagnosticsStatusBarItem.hide();
+			})
+			.catch((err) => {
+				vscode.window.showInformationMessage('Error: ' + err);
+				diagnosticsStatusBarItem.text = 'Linting Failed';
+			});
+	};
 }
 
 /**
diff --git a/src/goMain.ts b/src/goMain.ts
index a0b5a0b..32428d9 100644
--- a/src/goMain.ts
+++ b/src/goMain.ts
@@ -182,7 +182,7 @@
 		vscode.workspace.onDidChangeConfiguration((e) => watchLanguageServerConfiguration(goCtx, e))
 	);
 
-	registerCommand('go.environment.status', (_ctx, goCtx) => () => expandGoStatusBar(goCtx));
+	registerCommand('go.environment.status', expandGoStatusBar);
 
 	GoRunTestCodeLensProvider.activate(ctx);
 	GoReferencesCodeLensProvider.activate(ctx);
@@ -223,13 +223,13 @@
 	registerCommand('go.test.previous', commands.testPrevious);
 	registerCommand('go.debug.previous', commands.debugPrevious);
 
-	registerCommand('go.test.coverage', () => toggleCoverageCurrentPackage);
+	registerCommand('go.test.coverage', toggleCoverageCurrentPackage);
 	registerCommand('go.test.showOutput', () => showTestOutput);
 	registerCommand('go.test.cancel', () => cancelRunningTests);
-	registerCommand('go.import.add', () => (arg) => addImport(goCtx, arg));
-	registerCommand('go.add.package.workspace', () => addImportToWorkspace);
+	registerCommand('go.import.add', addImport);
+	registerCommand('go.add.package.workspace', addImportToWorkspace);
 	registerCommand('go.tools.install', commands.installTools);
-	registerCommand('go.browse.packages', () => browsePackages);
+	registerCommand('go.browse.packages', browsePackages);
 
 	if (isVscodeTestingAPIAvailable && cfg.get<boolean>('testExplorer.enable')) {
 		GoTestExplorer.setup(ctx);
@@ -313,35 +313,35 @@
 		})
 	);
 
-	registerCommand('go.test.generate.package', () => goGenerateTests.generateTestCurrentPackage);
-	registerCommand('go.test.generate.file', () => goGenerateTests.generateTestCurrentFile);
-	registerCommand('go.test.generate.function', () => goGenerateTests.generateTestCurrentFunction);
-	registerCommand('go.toggle.test.file', () => goGenerateTests.toggleTestFile);
+	registerCommand('go.test.generate.package', goGenerateTests.generateTestCurrentPackage);
+	registerCommand('go.test.generate.file', goGenerateTests.generateTestCurrentFile);
+	registerCommand('go.test.generate.function', goGenerateTests.generateTestCurrentFunction);
+	registerCommand('go.toggle.test.file', goGenerateTests.toggleTestFile);
 	registerCommand('go.debug.startSession', commands.startDebugSession);
 	registerCommand('go.show.commands', commands.showCommands);
-	registerCommand('go.get.package', () => goGetPackage);
-	registerCommand('go.playground', () => playgroundCommand);
-	registerCommand('go.lint.package', () => () => lintCode('package'));
-	registerCommand('go.lint.workspace', () => () => lintCode('workspace'));
-	registerCommand('go.lint.file', () => () => lintCode('file'));
-	registerCommand('go.vet.package', () => vetCode);
-	registerCommand('go.vet.workspace', () => () => vetCode(true));
-	registerCommand('go.build.package', () => buildCode);
-	registerCommand('go.build.workspace', () => () => buildCode(true));
-	registerCommand('go.install.package', () => installCurrentPackage);
-	registerCommand('go.run.modinit', () => goModInit);
-	registerCommand('go.extractServerChannel', (_ctx, goCtx) => () => showServerOutputChannel(goCtx));
-	registerCommand('go.workspace.resetState', () => resetWorkspaceState);
-	registerCommand('go.global.resetState', () => resetGlobalState);
+	registerCommand('go.get.package', goGetPackage);
+	registerCommand('go.playground', playgroundCommand);
+	registerCommand('go.lint.package', lintCode('package'));
+	registerCommand('go.lint.workspace', lintCode('workspace'));
+	registerCommand('go.lint.file', lintCode('file'));
+	registerCommand('go.vet.package', vetCode(false));
+	registerCommand('go.vet.workspace', vetCode(true));
+	registerCommand('go.build.package', buildCode(false));
+	registerCommand('go.build.workspace', buildCode(true));
+	registerCommand('go.install.package', installCurrentPackage);
+	registerCommand('go.run.modinit', goModInit);
+	registerCommand('go.extractServerChannel', showServerOutputChannel);
+	registerCommand('go.workspace.resetState', resetWorkspaceState);
+	registerCommand('go.global.resetState', resetGlobalState);
 	registerCommand('go.toggle.gc_details', commands.toggleGCDetails);
 	registerCommand('go.apply.coverprofile', commands.applyCoverprofile);
 
 	// Go Enviornment switching commands
-	registerCommand('go.environment.choose', () => chooseGoEnvironment);
+	registerCommand('go.environment.choose', chooseGoEnvironment);
 
 	// Survey related commands
-	registerCommand('go.survey.showConfig', (_ctx, goCtx) => () => showSurveyConfig(goCtx));
-	registerCommand('go.survey.resetConfig', () => resetSurveyConfigs);
+	registerCommand('go.survey.showConfig', showSurveyConfig);
+	registerCommand('go.survey.resetConfig', resetSurveyConfigs);
 
 	vscode.languages.setLanguageConfiguration(GO_MODE.language, {
 		wordPattern: /(-?\d*\.\d\w*)|([^`~!@#%^&*()\-=+[{\]}\\|;:'",.<>/?\s]+)/g
diff --git a/src/goModules.ts b/src/goModules.ts
index 34d230e..9d3f39d 100644
--- a/src/goModules.ts
+++ b/src/goModules.ts
@@ -18,6 +18,7 @@
 import { getFromGlobalState, updateGlobalState } from './stateUtils';
 import { getBinPath, getGoVersion, getModuleCache, getWorkspaceFolderPath } from './util';
 import { envPath, fixDriveCasingInWindows, getCurrentGoRoot } from './utils/pathUtils';
+import { CommandFactory } from './commands';
 export let GO111MODULE: string | undefined;
 
 export async function runGoEnv(uri?: vscode.Uri, envvars: string[] = []): Promise<any> {
@@ -191,7 +192,7 @@
 	});
 }
 
-export async function goModInit() {
+export const goModInit: CommandFactory = () => async () => {
 	outputChannel.clear();
 
 	const moduleName = await vscode.window.showInputBox({
@@ -220,4 +221,4 @@
 			`Error running "${goRuntimePath} mod init ${moduleName}": See Go output channel for details`
 		);
 	}
-}
+};
diff --git a/src/goPlayground.ts b/src/goPlayground.ts
index dc16de4..ddaf312 100644
--- a/src/goPlayground.ts
+++ b/src/goPlayground.ts
@@ -11,10 +11,11 @@
 import { outputChannel } from './goStatus';
 import { getBinPath } from './util';
 import vscode = require('vscode');
+import { CommandFactory } from './commands';
 
 const TOOL_CMD_NAME = 'goplay';
 
-export const playgroundCommand = () => {
+export const playgroundCommand: CommandFactory = () => () => {
 	const editor = vscode.window.activeTextEditor;
 	if (!editor) {
 		vscode.window.showInformationMessage('No editor is active.');
diff --git a/src/goStatus.ts b/src/goStatus.ts
index 87807ab..01b1224 100644
--- a/src/goStatus.ts
+++ b/src/goStatus.ts
@@ -17,6 +17,7 @@
 import { allToolsInformation } from './goToolsInformation';
 import { getGoVersion } from './util';
 import { GoExtensionContext } from './context';
+import { CommandFactory } from './commands';
 
 export const outputChannel = vscode.window.createOutputChannel('Go');
 
@@ -53,7 +54,7 @@
 	}
 }
 
-export async function expandGoStatusBar(goCtx: GoExtensionContext) {
+export const expandGoStatusBar: CommandFactory = (ctx, goCtx) => async () => {
 	const { languageServerIsRunning, serverOutputChannel } = goCtx;
 	const options = [
 		{ label: 'Locate Configured Go Tools', description: 'display go env' },
@@ -112,7 +113,7 @@
 			}
 		}
 	});
-}
+};
 
 /**
  * Initialize the status bar item with current Go binary
diff --git a/src/goSurvey.ts b/src/goSurvey.ts
index cf76bb0..8a0912d 100644
--- a/src/goSurvey.ts
+++ b/src/goSurvey.ts
@@ -20,6 +20,7 @@
 import { getGoConfig } from './config';
 import { getGoVersion } from './util';
 import { GoExtensionContext } from './context';
+import { CommandFactory } from './commands';
 
 // GoplsSurveyConfig is the set of global properties used to determine if
 // we should prompt a user to take the gopls survey.
@@ -213,10 +214,10 @@
 	return getStateConfig(goplsSurveyConfig) as GoplsSurveyConfig;
 }
 
-export function resetSurveyConfigs() {
+export const resetSurveyConfigs: CommandFactory = () => () => {
 	flushSurveyConfig(goplsSurveyConfig, null);
 	flushSurveyConfig(developerSurveyConfig, null);
-}
+};
 
 export function flushSurveyConfig(key: string, cfg: any) {
 	if (cfg) {
@@ -251,7 +252,7 @@
 	}
 }
 
-export async function showSurveyConfig(goCtx: GoExtensionContext) {
+export const showSurveyConfig: CommandFactory = (ctx, goCtx) => async () => {
 	// TODO(rstambler): Add developer survey config.
 	outputChannel.appendLine('HaTs Survey Configuration');
 	outputChannel.appendLine(JSON.stringify(getGoplsSurveyConfig(), null, 2));
@@ -283,7 +284,7 @@
 		default:
 			break;
 	}
-}
+};
 
 export const timeMinute = 1000 * 60;
 const timeHour = timeMinute * 60;
diff --git a/src/goVet.ts b/src/goVet.ts
index 25436e6..15005dc 100644
--- a/src/goVet.ts
+++ b/src/goVet.ts
@@ -5,6 +5,7 @@
 
 import path = require('path');
 import vscode = require('vscode');
+import { CommandFactory } from './commands';
 import { getGoConfig } from './config';
 import { toolExecutionEnvironment } from './goEnv';
 import { vetDiagnosticCollection } from './goMain';
@@ -21,35 +22,37 @@
 /**
  * Runs go vet in the current package or workspace.
  */
-export function vetCode(vetWorkspace?: boolean) {
-	const editor = vscode.window.activeTextEditor;
-	if (!editor && !vetWorkspace) {
-		vscode.window.showInformationMessage('No editor is active, cannot find current package to vet');
-		return;
-	}
-	if (editor?.document.languageId !== 'go' && !vetWorkspace) {
-		vscode.window.showInformationMessage(
-			'File in the active editor is not a Go file, cannot find current package to vet'
-		);
-		return;
-	}
+export function vetCode(vetWorkspace?: boolean): CommandFactory {
+	return () => () => {
+		const editor = vscode.window.activeTextEditor;
+		if (!editor && !vetWorkspace) {
+			vscode.window.showInformationMessage('No editor is active, cannot find current package to vet');
+			return;
+		}
+		if (editor?.document.languageId !== 'go' && !vetWorkspace) {
+			vscode.window.showInformationMessage(
+				'File in the active editor is not a Go file, cannot find current package to vet'
+			);
+			return;
+		}
 
-	const documentUri = editor?.document.uri;
-	const goConfig = getGoConfig(documentUri);
+		const documentUri = editor?.document.uri;
+		const goConfig = getGoConfig(documentUri);
 
-	outputChannel.clear(); // Ensures stale output from vet on save is cleared
-	diagnosticsStatusBarItem.show();
-	diagnosticsStatusBarItem.text = 'Vetting...';
+		outputChannel.clear(); // Ensures stale output from vet on save is cleared
+		diagnosticsStatusBarItem.show();
+		diagnosticsStatusBarItem.text = 'Vetting...';
 
-	goVet(documentUri, goConfig, vetWorkspace)
-		.then((warnings) => {
-			handleDiagnosticErrors(editor?.document, warnings, vetDiagnosticCollection);
-			diagnosticsStatusBarItem.hide();
-		})
-		.catch((err) => {
-			vscode.window.showInformationMessage('Error: ' + err);
-			diagnosticsStatusBarItem.text = 'Vetting Failed';
-		});
+		goVet(documentUri, goConfig, vetWorkspace)
+			.then((warnings) => {
+				handleDiagnosticErrors(editor?.document, warnings, vetDiagnosticCollection);
+				diagnosticsStatusBarItem.hide();
+			})
+			.catch((err) => {
+				vscode.window.showInformationMessage('Error: ' + err);
+				diagnosticsStatusBarItem.text = 'Vetting Failed';
+			});
+	};
 }
 
 /**
diff --git a/src/language/goLanguageServer.ts b/src/language/goLanguageServer.ts
index c4b751f..bbd583a 100644
--- a/src/language/goLanguageServer.ts
+++ b/src/language/goLanguageServer.ts
@@ -56,6 +56,7 @@
 import { ProvideFoldingRangeSignature } from 'vscode-languageclient/lib/common/foldingRange';
 import { daysBetween, getStateConfig, maybePromptForGoplsSurvey, timeDay, timeMinute } from '../goSurvey';
 import { maybePromptForDeveloperSurvey } from '../goDeveloperSurvey';
+import { CommandFactory } from '../commands';
 
 export interface LanguageServerConfig {
 	serverName: string;
@@ -1299,7 +1300,7 @@
 	}
 }
 
-export function showServerOutputChannel(goCtx: GoExtensionContext) {
+export const showServerOutputChannel: CommandFactory = (ctx, goCtx) => () => {
 	if (!goCtx.languageServerIsRunning) {
 		vscode.window.showInformationMessage('gopls is not running');
 		return;
@@ -1325,7 +1326,7 @@
 	if (found === undefined) {
 		vscode.window.showErrorMessage('make sure "gopls (server)" output is showing');
 	}
-}
+};
 
 function sleep(ms: number) {
 	return new Promise((resolve) => setTimeout(resolve, ms));
diff --git a/src/stateUtils.ts b/src/stateUtils.ts
index 75abf55..639a51b 100644
--- a/src/stateUtils.ts
+++ b/src/stateUtils.ts
@@ -5,6 +5,7 @@
  *--------------------------------------------------------*/
 
 import vscode = require('vscode');
+import { CommandFactory } from './commands';
 
 let globalState: vscode.Memento;
 let workspaceState: vscode.Memento;
@@ -31,9 +32,9 @@
 	return globalState;
 }
 
-export function resetGlobalState() {
+export const resetGlobalState: CommandFactory = () => () => {
 	resetStateQuickPick(globalState, updateGlobalState);
-}
+};
 
 export function getFromWorkspaceState(key: string, defaultValue?: any) {
 	if (!workspaceState) {
@@ -57,9 +58,9 @@
 	return workspaceState;
 }
 
-export function resetWorkspaceState() {
+export const resetWorkspaceState: CommandFactory = () => () => {
 	resetStateQuickPick(workspaceState, updateWorkspaceState);
-}
+};
 
 export function getMementoKeys(state: vscode.Memento): string[] {
 	if (!state) {
diff --git a/test/integration/extension.test.ts b/test/integration/extension.test.ts
index fa74231..1dbef2d 100644
--- a/test/integration/extension.test.ts
+++ b/test/integration/extension.test.ts
@@ -553,7 +553,13 @@
 		const uri = vscode.Uri.file(path.join(generateTestsSourcePath, 'generatetests.go'));
 		const document = await vscode.workspace.openTextDocument(uri);
 		await vscode.window.showTextDocument(document);
-		await generateTestCurrentFile();
+		const ctx = new MockExtensionContext() as any;
+		const goCtx: GoExtensionContext = {
+			lastUserAction: new Date(),
+			crashCount: 0,
+			restartHistory: []
+		};
+		await generateTestCurrentFile(ctx, goCtx)();
 
 		const testFileGenerated = fs.existsSync(path.join(generateTestsSourcePath, 'generatetests_test.go'));
 		assert.equal(testFileGenerated, true, 'Test file not generated.');
@@ -570,7 +576,13 @@
 		const document = await vscode.workspace.openTextDocument(uri);
 		const editor = await vscode.window.showTextDocument(document);
 		editor.selection = new vscode.Selection(5, 0, 6, 0);
-		await generateTestCurrentFunction();
+		const ctx = new MockExtensionContext() as any;
+		const goCtx: GoExtensionContext = {
+			lastUserAction: new Date(),
+			crashCount: 0,
+			restartHistory: []
+		};
+		await generateTestCurrentFunction(ctx, goCtx)();
 
 		const testFileGenerated = fs.existsSync(path.join(generateTestsSourcePath, 'generatetests_test.go'));
 		assert.equal(testFileGenerated, true, 'Test file not generated.');
@@ -586,7 +598,13 @@
 		const uri = vscode.Uri.file(path.join(generatePackageTestSourcePath, 'generatetests.go'));
 		const document = await vscode.workspace.openTextDocument(uri);
 		await vscode.window.showTextDocument(document);
-		await generateTestCurrentPackage();
+		const ctx = new MockExtensionContext() as any;
+		const goCtx: GoExtensionContext = {
+			lastUserAction: new Date(),
+			crashCount: 0,
+			restartHistory: []
+		};
+		await generateTestCurrentPackage(ctx, goCtx)();
 
 		const testFileGenerated = fs.existsSync(path.join(generateTestsSourcePath, 'generatetests_test.go'));
 		assert.equal(testFileGenerated, true, 'Test file not generated.');