vscode-go: refactor goDoctor, goFillStruct, and goImpl to factory pattern

Change-Id: I8eca27c930c3f659d76c1a653a9224b37abccb1f
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/404581
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
Run-TryBot: Jamal Carvalho <jamal@golang.org>
TryBot-Result: kokoro <noreply+kokoro@google.com>
diff --git a/src/commands/index.ts b/src/commands/index.ts
index aac8912..cffff3f 100644
--- a/src/commands/index.ts
+++ b/src/commands/index.ts
@@ -10,6 +10,9 @@
 export { applyCoverprofile } from './applyCoverprofile';
 export { getConfiguredGoTools } from './getConfiguredGoTools';
 export { getCurrentGoPath } from './getCurrentGoPath';
+export { extractFunction, extractVariable } from '../goDoctor';
+export { runFillStruct } from '../goFillStruct';
+export { implCursor } from '../goImpl';
 export { addTags, removeTags } from '../goModifytags';
 export { installTools } from './installTools';
 export { showCommands } from './showCommands';
diff --git a/src/goDoctor.ts b/src/goDoctor.ts
index 61879a5..dda7f2d 100644
--- a/src/goDoctor.ts
+++ b/src/goDoctor.ts
@@ -17,7 +17,7 @@
  * Extracts function out of current selection and replaces the current selection with a call to the extracted function.
  */
 export function extractFunction() {
-	extract('extract');
+	return () => extract('extract');
 }
 
 /**
@@ -25,7 +25,7 @@
  * replaces the current selection with the new var.
  */
 export function extractVariable() {
-	extract('var');
+	return () => extract('var');
 }
 
 type typeOfExtraction = 'var' | 'extract';
diff --git a/src/goFillStruct.ts b/src/goFillStruct.ts
index b6e817e..006c5eb 100644
--- a/src/goFillStruct.ts
+++ b/src/goFillStruct.ts
@@ -8,6 +8,7 @@
 
 import cp = require('child_process');
 import vscode = require('vscode');
+import { CommandFactory } from './commands';
 import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool } from './goInstallTools';
 import { byteOffsetAt, getBinPath, getFileArchive, makeMemoizedByteOffsetConverter } from './util';
@@ -19,7 +20,7 @@
 	code: string;
 }
 
-export function runFillStruct(editor?: vscode.TextEditor): Promise<void> {
+export const runFillStruct: CommandFactory = () => (editor = vscode.window.activeTextEditor) => {
 	if (!editor) return Promise.resolve();
 	const args = getCommonArgs(editor);
 	if (!args) {
@@ -27,7 +28,7 @@
 	}
 
 	return execFillStruct(editor, args);
-}
+};
 
 function getCommonArgs(editor: vscode.TextEditor): string[] | undefined {
 	if (!editor) {
diff --git a/src/goImpl.ts b/src/goImpl.ts
index dfb7870..61ac926 100644
--- a/src/goImpl.ts
+++ b/src/goImpl.ts
@@ -13,11 +13,12 @@
 import { promptForMissingTool } from './goInstallTools';
 import { getBinPath } from './util';
 import vscode = require('vscode');
+import { CommandFactory } from './commands';
 
 // Supports only passing interface, see TODO in implCursor to finish
 const inputRegex = /^(\w+\ \*?\w+\ )?([\w\.\-\/]+)$/;
 
-export function implCursor() {
+export const implCursor: CommandFactory = () => () => {
 	const editor = vscode.window.activeTextEditor;
 	if (!editor) {
 		vscode.window.showErrorMessage('No active editor found.');
@@ -45,7 +46,7 @@
 
 			runGoImpl([matches[1], matches[2]], cursor.start, editor);
 		});
-}
+};
 
 function runGoImpl(args: string[], insertPos: vscode.Position, editor: vscode.TextEditor) {
 	const goimpl = getBinPath('impl');
diff --git a/src/goMain.ts b/src/goMain.ts
index 3458f08..056da0c 100644
--- a/src/goMain.ts
+++ b/src/goMain.ts
@@ -30,7 +30,6 @@
 	offerToInstallLatestGoVersion,
 	setEnvironmentVariableCollection
 } from './goEnvironmentStatus';
-import { runFillStruct } from './goFillStruct';
 import * as goGenerateTests from './goGenerateTests';
 import { goGetPackage } from './goGetPackage';
 import { implCursor } from './goImpl';
@@ -220,10 +219,10 @@
 	registerCommand('go.locate.tools', commands.getConfiguredGoTools);
 	registerCommand('go.add.tags', commands.addTags);
 	registerCommand('go.remove.tags', commands.removeTags);
-	registerCommand('go.fill.struct', () => () => runFillStruct(vscode.window.activeTextEditor));
-	registerCommand('go.impl.cursor', () => implCursor);
-	registerCommand('go.godoctor.extract', () => extractFunction);
-	registerCommand('go.godoctor.var', () => extractVariable);
+	registerCommand('go.fill.struct', commands.runFillStruct);
+	registerCommand('go.impl.cursor', commands.implCursor);
+	registerCommand('go.godoctor.extract', commands.extractFunction);
+	registerCommand('go.godoctor.var', commands.extractVariable);
 	registerCommand('go.test.cursor', () => (args) => testAtCursor(getGoConfig(), 'test', args));
 	registerCommand('go.test.cursorOrPrevious', () => (args) => testAtCursorOrPrevious(getGoConfig(), 'test', args));
 
diff --git a/test/integration/extension.test.ts b/test/integration/extension.test.ts
index 285dcb0..bbe6de1 100644
--- a/test/integration/extension.test.ts
+++ b/test/integration/extension.test.ts
@@ -46,6 +46,7 @@
 import cp = require('child_process');
 import os = require('os');
 import { GoExtensionContext } from '../../src/context';
+import { MockExtensionContext } from '../mocks/MockContext';
 
 const testAll = (isModuleMode: boolean) => {
 	const dummyCancellationSource = new vscode.CancellationTokenSource();
@@ -1606,7 +1607,13 @@
 		const editor = await vscode.window.showTextDocument(textDocument);
 		const selection = new vscode.Selection(12, 15, 12, 15);
 		editor.selection = selection;
-		await runFillStruct(editor);
+		const ctx = new MockExtensionContext() as any;
+		const goCtx: GoExtensionContext = {
+			lastUserAction: new Date(),
+			crashCount: 0,
+			restartHistory: []
+		};
+		await runFillStruct(ctx, goCtx)(editor);
 		assert.equal(vscode.window.activeTextEditor && vscode.window.activeTextEditor.document.getText(), golden);
 	});
 
@@ -1619,7 +1626,13 @@
 
 		const selection = new vscode.Selection(7, 0, 7, 10);
 		editor.selection = selection;
-		await runFillStruct(editor);
+		const ctx = new MockExtensionContext() as any;
+		const goCtx: GoExtensionContext = {
+			lastUserAction: new Date(),
+			crashCount: 0,
+			restartHistory: []
+		};
+		await runFillStruct(ctx, goCtx)(editor);
 		assert.equal(vscode.window.activeTextEditor && vscode.window.activeTextEditor.document.getText(), golden);
 	});
 };