src/goDebugConfiguration: take program verbatim with external adapter

As a fix for golang/vscode-go#1677, the extension massages launch
configurations to start dlv from the package directory and locate
'program' to be relative from the package directory.

This heuristic does not work well if dlv dap adapter is launched
externally so dlv runs from other directory. Until `delveCWD` or
a similar mechanism that allows to control where delve runs the
build, we take the launch configuration verbatim and avoid any
mutation.

Relative paths are resolved as described in the descriptions in
package.json. This changes only the internal mechanics.

Fixes golang/vscode-go#1793

Change-Id: Ic651be25a692dbb23a51707d5ebc274f22a3c532
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/351272
Trust: Hyang-Ah Hana Kim <hyangah@gmail.com>
Run-TryBot: Hyang-Ah Hana Kim <hyangah@gmail.com>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Polina Sokolova <polina@google.com>
diff --git a/src/goDebugConfiguration.ts b/src/goDebugConfiguration.ts
index a6e8ab5..d4352c7 100644
--- a/src/goDebugConfiguration.ts
+++ b/src/goDebugConfiguration.ts
@@ -394,7 +394,7 @@
 			(attr) => debugConfiguration[attr] && !path.isAbsolute(debugConfiguration[attr])
 		);
 		if (debugAdapter === 'dlv-dap') {
-			// relative paths -> absolute paths
+			// 1. Relative paths -> absolute paths
 			if (entriesWithRelativePaths.length > 0) {
 				const workspaceRoot = folder?.uri.fsPath;
 				if (workspaceRoot) {
@@ -408,8 +408,18 @@
 					);
 				}
 			}
-			// compute build dir, and translate the dirname in program to a path relative to buildDir.
-			if (debugConfiguration.request === 'launch') {
+			// 2. For launch debug/test modes that builds the debug target,
+			//    delve needs to be launched from the right directory (inside the main module of the target).
+			//    Compute the launch dir heuristically, and translate the dirname in program to a path relative to buildDir.
+			//    We skip this step when working with externally launched debug adapter
+			//    because we do not control the adapter's launch process.
+			if (
+				debugConfiguration.request === 'launch' &&
+				// Presence of the following attributes indicates externally launched debug adapter.
+				!debugConfiguration.port &&
+				!debugConfiguration.host &&
+				!debugConfiguration.debugServer
+			) {
 				const mode = debugConfiguration['mode'] || 'debug';
 				if (['debug', 'test', 'auto'].includes(mode)) {
 					// Massage config to build the target from the package directory
diff --git a/test/integration/goDebugConfiguration.test.ts b/test/integration/goDebugConfiguration.test.ts
index 61aad59..4162436 100644
--- a/test/integration/goDebugConfiguration.test.ts
+++ b/test/integration/goDebugConfiguration.test.ts
@@ -561,6 +561,52 @@
 		);
 	});
 
+	test('program and __buildDir are not updated when working with externally launched adapters', () => {
+		const config: vscode.DebugConfiguration = debugConfig('dlv-dap');
+		config.program = path.join('foo', 'bar', 'pkg');
+		config.port = 12345;
+		const workspaceFolder = {
+			uri: vscode.Uri.file(os.tmpdir()),
+			name: 'test',
+			index: 0
+		};
+		const { program, cwd, __buildDir } = debugConfigProvider.resolveDebugConfigurationWithSubstitutedVariables(
+			workspaceFolder,
+			config
+		);
+		assert.deepStrictEqual(
+			{ program, cwd, __buildDir },
+			{
+				program: path.join(os.tmpdir(), 'foo', 'bar', 'pkg'),
+				cwd: os.tmpdir(),
+				__buildDir: undefined
+			}
+		);
+	});
+
+	test('program and __buildDir are not updated when working with externally launched adapters (debugServer)', () => {
+		const config: vscode.DebugConfiguration = debugConfig('dlv-dap');
+		config.program = path.join('foo', 'bar', 'pkg');
+		config.debugServer = 4777;
+		const workspaceFolder = {
+			uri: vscode.Uri.file(os.tmpdir()),
+			name: 'test',
+			index: 0
+		};
+		const { program, cwd, __buildDir } = debugConfigProvider.resolveDebugConfigurationWithSubstitutedVariables(
+			workspaceFolder,
+			config
+		);
+		assert.deepStrictEqual(
+			{ program, cwd, __buildDir },
+			{
+				program: path.join(os.tmpdir(), 'foo', 'bar', 'pkg'),
+				cwd: os.tmpdir(),
+				__buildDir: undefined
+			}
+		);
+	});
+
 	test('empty, undefined paths are not affected', () => {
 		const config = debugConfig('dlv-dap');
 		config.program = undefined;