package.json: add command to run 'go mod init'

When users start a new Go project, one of the first things they need
to do is initialize their go.mod. This is required for debugging and
other tools to work correctly. Currently users have to navigate to
the terminal and run the command there. This adds a command that runs
in the workspace root, allowing users to initialize go.mod from the
command prompt instead.

Fixes golang/vscode-go#1449

Change-Id: Ie3daf565770402fe79f8720dd3fca8cf4ab01fa8
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/312092
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/commands.md b/docs/commands.md
index 34f8a0c..6486617 100644
--- a/docs/commands.md
+++ b/docs/commands.md
@@ -179,6 +179,10 @@
 
 Install the current package.
 
+### `Go: Initialize go.mod`
+
+Run `go mod init` in the workspace folder.
+
 ### `Go: Cancel Running Tests`
 
 Cancels running tests.
diff --git a/package.json b/package.json
index 4a28cde..8f77d60 100644
--- a/package.json
+++ b/package.json
@@ -97,6 +97,7 @@
     "onCommand:go.tools.install",
     "onCommand:go.locate.tools",
     "onCommand:go.show.commands",
+    "onCommand:go.run.modinit",
     "onDebugInitialConfigurations",
     "onDebugResolve:go",
     "onWebviewPanel:welcomeGo"
@@ -368,6 +369,11 @@
         "description": "Install the current package."
       },
       {
+        "command": "go.run.modinit",
+        "title": "Go: Initialize go.mod",
+        "description": "Run `go mod init` in the workspace folder."
+      },
+      {
         "command": "go.test.cancel",
         "title": "Go: Cancel Running Tests",
         "description": "Cancels running tests."
diff --git a/src/goMain.ts b/src/goMain.ts
index d622ef9..5e1a3c6 100644
--- a/src/goMain.ts
+++ b/src/goMain.ts
@@ -58,7 +58,7 @@
 import { logVerbose, setLogConfig } from './goLogging';
 import { GO_MODE } from './goMode';
 import { addTags, removeTags } from './goModifytags';
-import { GO111MODULE, isModSupported } from './goModules';
+import { GO111MODULE, goModInit, isModSupported } from './goModules';
 import { playgroundCommand } from './goPlayground';
 import { GoReferencesCodeLensProvider } from './goReferencesCodelens';
 import { GoRunTestCodeLensProvider } from './goRunTestCodelens';
@@ -586,6 +586,8 @@
 
 	ctx.subscriptions.push(vscode.commands.registerCommand('go.install.package', installCurrentPackage));
 
+	ctx.subscriptions.push(vscode.commands.registerCommand('go.run.modinit', goModInit));
+
 	ctx.subscriptions.push(
 		vscode.commands.registerCommand('go.extractServerChannel', () => {
 			showServerOutputChannel();
diff --git a/src/goModules.ts b/src/goModules.ts
index 7cbd939..341d041 100644
--- a/src/goModules.ts
+++ b/src/goModules.ts
@@ -6,16 +6,17 @@
 
 import cp = require('child_process');
 import path = require('path');
+import util = require('util');
 import vscode = require('vscode');
 import { getGoConfig } from './config';
 import { toolExecutionEnvironment } from './goEnv';
 import { getFormatTool } from './goFormat';
 import { installTools } from './goInstallTools';
+import { outputChannel } from './goStatus';
 import { getTool } from './goTools';
 import { getFromGlobalState, updateGlobalState } from './stateUtils';
-import { getBinPath, getGoVersion, getModuleCache } from './util';
+import { getBinPath, getGoVersion, getModuleCache, getWorkspaceFolderPath } from './util';
 import { envPath, fixDriveCasingInWindows, getCurrentGoRoot } from './utils/pathUtils';
-
 export let GO111MODULE: string;
 
 async function runGoModEnv(folderPath: string): Promise<string> {
@@ -184,3 +185,25 @@
 		});
 	});
 }
+
+export async function goModInit() {
+	outputChannel.clear();
+
+	const moduleName = await vscode.window.showInputBox({
+		prompt: 'Enter module name',
+		value: '',
+		placeHolder: 'example.com/m'
+	});
+
+	const goRuntimePath = getBinPath('go');
+	const execFile = util.promisify(cp.execFile);
+	try {
+		const env = toolExecutionEnvironment();
+		const cwd = getWorkspaceFolderPath();
+		await execFile(goRuntimePath, ['mod', 'init', moduleName], { env, cwd });
+	} catch (e) {
+		outputChannel.appendLine(e);
+		outputChannel.show();
+		vscode.window.showErrorMessage(`Error running 'go mod init ${moduleName}': See Go output channel for details`);
+	}
+}