[release] prepare v0.26.0 release

1bc09d8 src/goDebugConfiguration: resolve relative paths used in cwd, output, program
ac7b209 package-lock.json: npm audit fix
4574878 package.json: pick up gopls@v0.7.0 settings and update tools version
7ea6b76 src/goDebugConfiguration: default to dlv-dap in preview mode
e7e25d2 Revert ".github/workflows: use 'stable' version for release-nightly"
570a0a4 src/goModules.ts: open go.mod after init
936ecdf .github/workflows: use 'stable' version for release-nightly
a1d1329 src/goImport: use gopls methods to list and add contextual imports
03a8315 src/goDebugFactory.ts: fix problem with attach requests
82d2b5d src/goTest: Add command Test Function At Cursor or Test Previous
505ad8d build/all.bash: use go install for tool installation
e7c91ef package-lock.json: npm audit fix
616928d .github/workflows: use tmp dir as working dir when installing tools
ab4f21a src/goDebugConfiguration: let vscode resolve pick process
ecf30b7 src/goLanguageServer: migrate opt-out survey to Qualtrics
4cb78f6 src/goLint: handle the case where no editor is active
c5070dc CHANGELOG.md: v0.25.1 updates
b79bdf9 test/integration/goDebug.test.ts: include the debugger logging in tests
9b2b1ef src/goToolsInformation: update dlv-dap version
d88ee9b src/goDebugFactory: respond with error if dlv dap failed to start
fe7b40b src/goEnvironmentStatus: avoid TypeError for notification closed without action
f22b65a src/goLint: fix type error caused by a bug in goLint
b6a3630 src/goDebugFactory: connect after createDebugAdapterDescriptor
cbdd328 src/goSurvey: double survey probability and send half to v2 survey
4510773 src/goSurvey: increase the prompt probability to 0.03
ad1dcce test/integration/goDebug.test.ts: accept 'exception' as stopped reason
c54f5e6 package.json: set virtualWorkspaces capability to false
9fa871b src/goMain: warn users if go.goroot is set
0f4b38f tools/generate.go: update gopls and dlv-dap versions in generate
39445e1 src/goDebugConfiguration: use fileWorkspaceFolder in multi root workspace
961ab1c src/goLogging: fix the default logger
646faec package.json: use the updated workspace trust API
e8dc6c4 package.json: remove 'init' debug attribute
b9f1961 [release] Update CHANGELOG
64592bc src/debugAdapter: default path separator to '/'
a2c2efc package.json: bump dev version number to 0.26.0-dev
7340877 [release] Update CHANGELOG for v0.25.0
4bae281 src/goDebugConfiguration.ts: add substitutePath to go.delveConfig
d9fd8f0 docs: update dlv-dap.md
cea0269 test/integration/goDebug: wait for debug adapter dispose in teardown test
41c5ee9 test/integration/goDebug.test.ts: use dc.on to wait for output event
50efa73 test/integration/goDebug: modify teardown+cleanup test
8803f91 docs: update outdated lint tool documentation in features.md
6571316 .github/workflows: move dlv to dlv-dap before getting dlv again
54986cd docs/settings.md: update gopls settings

Change-Id: I6df1e8bbd836573b243fa9f290a149bc56d985a3
diff --git a/.github/workflows/release-nightly.yml b/.github/workflows/release-nightly.yml
index 8a2681b..f026a2b 100644
--- a/.github/workflows/release-nightly.yml
+++ b/.github/workflows/release-nightly.yml
@@ -29,7 +29,7 @@
       - name: Setup Go
         uses: actions/setup-go@v2
         with:
-         go-version: '1.15'
+         go-version: '1.16'
 
       - name: Install dependencies
         run: npm ci
@@ -66,6 +66,7 @@
             go get github.com/go-delve/delve/cmd/dlv@master
             cp "${HOME}/go/bin/dlv${{env.EXT}}" "${HOME}/go/bin/dlv-dap${{env.EXT}}"
             go get github.com/go-delve/delve/cmd/dlv
+        working-directory: ${{ runner.temp }}
         env:
           GO111MODULE: on
           EXT: "${{ matrix.os == 'windows-latest' && '.exe' || ''}}"
diff --git a/.github/workflows/test-long-all.yml b/.github/workflows/test-long-all.yml
index b47da3f..6a1bbad 100644
--- a/.github/workflows/test-long-all.yml
+++ b/.github/workflows/test-long-all.yml
@@ -59,9 +59,11 @@
             go get github.com/cweill/gotests/...
             go get github.com/rogpeppe/godef
             go get github.com/ramya-rao-a/go-outline
+            # Install two versions of dlv (one as dlv-dap)
             go get github.com/go-delve/delve/cmd/dlv@master
-            cp "${HOME}/go/bin/dlv${{env.EXT}}" "${HOME}/go/bin/dlv-dap${{env.EXT}}"
-            go get github.com/go-delve/delve/cmd/dlv
+            mv "${HOME}/go/bin/dlv${{env.EXT}}" "${HOME}/go/bin/dlv-dap${{env.EXT}}"
+            go get github.com/go-delve/delve/cmd/dlv@latest
+        working-directory: ${{ runner.temp }}
         env:
           GO111MODULE: on
           EXT: "${{ matrix.os == 'windows-latest' && '.exe' || ''}}"
diff --git a/.github/workflows/test-long.yml b/.github/workflows/test-long.yml
index 7f450b4..e26ef7d 100644
--- a/.github/workflows/test-long.yml
+++ b/.github/workflows/test-long.yml
@@ -39,6 +39,7 @@
       - name: Compile
         run: npm run vscode:prepublish
 
+      # TODO: use `go install` when we all move to 1.16+ (see build/all.bash)
       - name: Install Go tools (Modules mode)
         run: |
             go version
@@ -58,9 +59,11 @@
             go get github.com/cweill/gotests/...
             go get github.com/rogpeppe/godef
             go get github.com/ramya-rao-a/go-outline
+            # Install two versions of dlv (one as dlv-dap)
             go get github.com/go-delve/delve/cmd/dlv@master
-            cp "${HOME}/go/bin/dlv${{env.EXT}}" "${HOME}/go/bin/dlv-dap${{env.EXT}}"
-            go get github.com/go-delve/delve/cmd/dlv
+            mv "${HOME}/go/bin/dlv${{env.EXT}}" "${HOME}/go/bin/dlv-dap${{env.EXT}}"
+            go get github.com/go-delve/delve/cmd/dlv@latest
+        working-directory: ${{ runner.temp }}
         env:
           GO111MODULE: on
           EXT: "${{ matrix.os == 'windows-latest' && '.exe' || ''}}"
diff --git a/.github/workflows/test-smoke.yml b/.github/workflows/test-smoke.yml
index 83ce5a7..93db2c9 100644
--- a/.github/workflows/test-smoke.yml
+++ b/.github/workflows/test-smoke.yml
@@ -56,9 +56,11 @@
             go get github.com/cweill/gotests/...
             go get github.com/rogpeppe/godef
             go get github.com/ramya-rao-a/go-outline
+            # Install two versions of dlv (one as dlv-dap)
             go get github.com/go-delve/delve/cmd/dlv@master
-            cp "${HOME}/go/bin/dlv${{env.EXT}}"  "${HOME}/go/bin/dlv-dap${{env.EXT}}"
-            go get github.com/go-delve/delve/cmd/dlv
+            mv "${HOME}/go/bin/dlv${{env.EXT}}" "${HOME}/go/bin/dlv-dap${{env.EXT}}"
+            go get github.com/go-delve/delve/cmd/dlv@latest
+        working-directory: ${{ runner.temp }}
         env:
           GO111MODULE: on
           EXT: "${{ matrix.os == 'windows-latest' && '.exe' || ''}}"
diff --git a/build/all.bash b/build/all.bash
index a8ece02..f981def 100755
--- a/build/all.bash
+++ b/build/all.bash
@@ -99,26 +99,27 @@
 	local TARGET="${GOBIN}"
 	if [[ -z "${GOBIN}" ]]; then TARGET="${GOPATHS%%:*}/bin" ; fi
 
-	GO111MODULE=on go get golang.org/x/tools/gopls
-	GO111MODULE=on go get github.com/acroca/go-symbols
-	GO111MODULE=on go get github.com/cweill/gotests/...
-	GO111MODULE=on go get github.com/davidrjenni/reftools/cmd/fillstruct
-	GO111MODULE=on go get github.com/haya14busa/goplay/cmd/goplay
+	GO111MODULE=on go install golang.org/x/tools/gopls@latest
+	GO111MODULE=on go install github.com/acroca/go-symbols@latest
+	GO111MODULE=on go install github.com/cweill/gotests/gotests@latest
+	GO111MODULE=on go install github.com/davidrjenni/reftools/cmd/fillstruct@latest
+	GO111MODULE=on go install github.com/haya14busa/goplay/cmd/goplay@latest
 
 	# We install two versions of gocode, one for module mode (gocode-gomod)
 	# and another for GOPATH mode (gocode).
-	GO111MODULE=on go get github.com/stamblerre/gocode && mv "${TARGET}/gocode" "${TARGET}/gocode-gomod"
-	GO111MODULE=on go get github.com/mdempsky/gocode
+	GO111MODULE=on go install github.com/stamblerre/gocode@latest && mv "${TARGET}/gocode" "${TARGET}/gocode-gomod"
+	GO111MODULE=on go install github.com/mdempsky/gocode@latest
 
-	GO111MODULE=on go get github.com/ramya-rao-a/go-outline
-	GO111MODULE=on go get github.com/rogpeppe/godef
-	GO111MODULE=on go get github.com/sqs/goreturns
-	GO111MODULE=on go get github.com/uudashr/gopkgs/v2/cmd/gopkgs
-	GO111MODULE=on go get github.com/zmb3/gogetdoc
-	GO111MODULE=on go get honnef.co/go/tools/...
-	GO111MODULE=on go get golang.org/x/tools/cmd/gorename
+	GO111MODULE=on go install github.com/ramya-rao-a/go-outline@latest
+	GO111MODULE=on go install github.com/rogpeppe/godef@latest
+	GO111MODULE=on go install github.com/sqs/goreturns@latest
+	GO111MODULE=on go install github.com/uudashr/gopkgs/v2/cmd/gopkgs@latest
+	GO111MODULE=on go install github.com/zmb3/gogetdoc@latest
+	GO111MODULE=on go install honnef.co/go/tools/cmd/staticcheck@latest
+	GO111MODULE=on go install golang.org/x/tools/cmd/gorename@latest
 
-	GO111MODULE=on go get github.com/go-delve/delve/cmd/dlv@master && cp "${TARGET}/dlv" "${TARGET}/dlv-dap"
+	GO111MODULE=on go install github.com/go-delve/delve/cmd/dlv@master && cp "${TARGET}/dlv" "${TARGET}/dlv-dap"
+	GO111MODULE=on go install github.com/go-delve/delve/cmd/dlv@latest
 }
 
 main() {
diff --git a/docs/commands.md b/docs/commands.md
index 6486617..23e0821 100644
--- a/docs/commands.md
+++ b/docs/commands.md
@@ -35,6 +35,10 @@
 
 Runs a unit test at the cursor.
 
+### `Go: Test Function At Cursor or Test Previous`
+
+Runs a unit test at the cursor if one is found, otherwise re-runs the last executed test.
+
 ### `Go: Subtest At Cursor`
 
 Runs a sub test at the cursor.
diff --git a/docs/debugging.md b/docs/debugging.md
index 5e81b51..d800dfd 100644
--- a/docs/debugging.md
+++ b/docs/debugging.md
@@ -70,6 +70,8 @@
     * `maxVariableRecurse`: How far to recurse when evaluating nested types (default: `1`).
     * `followPointers`: Automatically dereference pointers (default: `true`).
   * `showGlobalVariables`: Show global variables in the Debug view (default: `false`).
+  * `debugAdapter`: Controls which debug adapter to use (default: `legacy`).
+  * `substitutePath`: Path mappings to apply to get from a path in the editor to a path in the compiled program (default: `[]`).
 
 There are some common cases when you might want to tweak the Delve configurations.
 
@@ -113,10 +115,10 @@
 logOutput  | Comma-separated list of Delve components (`debugger`, `gdbwire`, `lldbout`, `debuglineerr`, `rpc`) that should produce debug output when `showLog` is `true`. This corresponds to `dlv`'s `--log-output` flag.
 logDest    | Absolute path to the delve log output file. This corresponds to `dlv`'s `--log-dest` flag, but number (used for file descriptor) is disallowed. Supported only in dlv-dap mode on Linux and Mac.
 buildFlags | Build flags to pass to the Go compiler. This corresponds to `dlv`'s `--build-flags` flag.
-dlvFlags   | Extra flags passed to `dlv`. See `dlv help` for the full list of supported flags. This is useful when users need to pass less commonly used or new flags such as `--only-same-user`, `--check-go-version`. Note that some flags such as `--log-output`, `--log`, `--log-dest`, `--init`, `--api-version` already have corresponding properties in the debug configuration, and flags such as `--listen` and `--headless` are used internally. If they are specified in `dlvFlags`, they may be ignored or cause an error.
+dlvFlags   | Extra flags passed to `dlv`. See `dlv help` for the full list of supported flags. This is useful when users need to pass less commonly used or new flags such as `--only-same-user`, `--check-go-version`. Note that some flags such as `--log-output`, `--log`, `--log-dest`, `--api-version` already have corresponding properties in the debug configuration, and flags such as `--listen` and `--headless` are used internally. If they are specified in `dlvFlags`, they may be ignored or cause an error.
 remotePath | If remote debugging (`mode`: `remote`), this should be the absolute path to the package being debugged on the remote machine. See the section on [Remote Debugging](#remote-debugging) for further details. [golang/vscode-go#45](https://github.com/golang/vscode-go/issues/45) is also relevant. Becomes the first mapping in substitutePath.
 substitutePath | An array of mappings from an absolute local path to an absolute remote path that is used by the debuggee. The debug adapter will replace the local path with the remote path in all of the calls. The mappings are applied in order, and the first matching mapping is used. This can be used to map files that have moved since the program was built, different remote paths, and symlinked files or directories. This is intended to be equivalent to the [substitute-path](https://github.com/go-delve/delve/tree/master/Documentation/cli#config) configuration, and will eventually configure substitute-path in Delve directly.
-cwd | The working directory to be used in running the program. If remote debugging (`mode`: `remote`), this should be the absolute path to the working directory being debugged on the local machine. See the section on [Remote Debugging](#remote-debugging) for further details. [golang/vscode-go#45](https://github.com/golang/vscode-go/issues/45) is also relevant.
+cwd | The working directory to be used in running the program. If remote debugging (`mode`: `remote`), this should be the absolute path to the working directory being debugged on the local machine. The extension defaults to the workspace folder, or the workspace folder of the open file in multi root workspaces. See the section on [Remote Debugging](#remote-debugging) for further details. [golang/vscode-go#45](https://github.com/golang/vscode-go/issues/45) is also relevant.
 processId  | This is the process ID of the executable you want to debug. Applicable only when using the `attach` request in `local` mode. By setting this to the command name of the process, `${command:pickProcess}`, or`${command:pickGoProcess}` a quick pick menu will show a list of processes to choose from.
 
 ### Specifying [build tags](https://golang.org/pkg/go/build/#hdr-Build_Constraints)
@@ -161,7 +163,8 @@
 
 Any property in the launch configuration that requires a file path can be specified in terms of [VS Code variables]. Here are some useful ones to know:
 
-* `${workspaceFolder}` refers to the root of the workspace opened in VS Code.
+* `${workspaceFolder}` refers to the root of the workspace opened in VS Code. If using a multi root workspace, you must specify the folder name `${workspaceFolder:folderName}`
+* `${fileWorkspaceFolder}` refers to the the current opened file's workspace folder.
 * `${file}` refers to the currently opened file.
 * `${fileDirname}` refers to the directory containing the currently opened file. This is typically also the name of the Go package containing this file, and as such, can be used to debug the currently opened package.
 
@@ -454,4 +457,4 @@
 [Delve]: https://github.com/go-delve/delve
 [VS Code variables]: https://code.visualstudio.com/docs/editor/variables-reference
 [snippets]: https://code.visualstudio.com/docs/editor/userdefinedsnippets
-[Command Palette]: https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette
\ No newline at end of file
+[Command Palette]: https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette
diff --git a/docs/dlv-dap.md b/docs/dlv-dap.md
index c4168ad..f57d475 100644
--- a/docs/dlv-dap.md
+++ b/docs/dlv-dap.md
@@ -135,7 +135,7 @@
 If you are having issues with seeing logs and or suspect problems in extension's integration, you can start Delve DAP server from a separate terminal and configure the extension to directly connect to it.
 
 ```
-$ dlv-dap dap --listen=:12345 --log-output=dap
+$ dlv-dap dap --listen=:12345 --log --log-output=dap
 ```
 
 ```json5
diff --git a/docs/features.md b/docs/features.md
index 273513a..0bf09cf 100644
--- a/docs/features.md
+++ b/docs/features.md
@@ -187,7 +187,7 @@
 
 Much like vet errors, lint errors can also be shown on save. This behavior is configurable through the [`"go.lintOnSave"`](settings.md#go.lintOnSave) setting.
 
-The default lint tool is the one provided by the `go` command: `go lint`. However, custom lint tools can be easily used instead by configuring the [`"go.lintTool"`](settings.md#go.lintTool) setting. [`staticcheck`], [`golangci-lint`], and [`revive`] are supported.
+The default lint tool is [`staticcheck`]. However, custom lint tools can be easily used instead by configuring the [`"go.lintTool"`](settings.md#go.lintTool) setting. [`golint`], [`golangci-lint`], and [`revive`] are also supported.
 
 For a complete overview of linter options, see the [documentation for diagnostic tools](tools.md#diagnostics).
 
@@ -221,5 +221,6 @@
 
 [`gopls`]: gopls.md
 [`staticcheck`]: https://staticcheck.io/
+[`golint`]: https://pkg.go.dev/golang.org/x/lint/golint?tab=overview
 [`golangci-lint`]: https://golangci-lint.run/
 [`revive`]: https://github.com/mgechev/revive
diff --git a/docs/settings.md b/docs/settings.md
index 69f7768..bfbcf57 100644
--- a/docs/settings.md
+++ b/docs/settings.md
@@ -146,6 +146,7 @@
 | `debugAdapter` | Select which debug adapter to use by default. This is also used for choosing which debug adapter to use when no launch.json is present and with codelenses. <br/> Allowed Options: `legacy`, `dlv-dap` <br/> Default: `"legacy"` |
 | `dlvLoadConfig` | LoadConfig describes to delve, how to load values from target's memory. Ignored by 'dlv-dap'. <br/> Default: ``` { <pre>"followPointers" :	true,<br/>"maxArrayValues" :	64,<br/>"maxStringLen" :	64,<br/>"maxStructFields" :	-1,<br/>"maxVariableRecurse" :	1,</pre>} ``` |
 | `showGlobalVariables` | Boolean value to indicate whether global package variables should be shown in the variables pane or not. <br/> Default: `false` |
+| `substitutePath` | An array of mappings from a local path to the remote path that is used by the debuggee. The debug adapter will replace the local path with the remote path in all of the calls. Overriden by remotePath. |
 
 Default:
 ```
@@ -160,6 +161,7 @@
 		"maxVariableRecurse" :	1,
 	},
 	"showGlobalVariables" :	false,
+	"substitutePath" :	[],
 }
 ```
 ### `go.disableConcurrentTests`
@@ -542,6 +544,13 @@
 
 
 Default: `true`
+### `build.experimentalTemplateSupport`
+
+(Experimental) experimentalTemplateSupport opts into the experimental support
+for template files.
+
+
+Default: `false`
 ### `build.experimentalWorkspaceModule`
 
 (Experimental) experimentalWorkspaceModule opts a user into the experimental support
@@ -625,7 +634,7 @@
 such as "someSlice.sort!".
 
 
-Default: `false`
+Default: `true`
 ### `ui.completion.matcher`
 
 (Advanced) matcher sets the algorithm that is used when calculating completion
diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md
index 9dd7f71..908a158 100644
--- a/docs/troubleshooting.md
+++ b/docs/troubleshooting.md
@@ -46,9 +46,7 @@
 
 ## Collect `gopls` information
 
-The gopls log can be found by navigating to `View` -> `Output`. There will be a drop-down menu titled `Tasks` in the top-right corner. Select the `gopls (server)` item, which will contain the `gopls` logs.
-
-To increase the level of detail in your logs, add the following to your settings:
+Enable `gopls` tracing by adding the following to your settings:
 
 ```json5
 "go.languageServerFlags": [
@@ -56,6 +54,15 @@
 ]
 ```
 
+The gopls log can be found by navigating to `View` -> `Output`. There will be a drop-down menu titled `Tasks` in the top-right corner. Select the `gopls (server)` item, which will contain the `gopls` logs.
+
+In special cases, you may want to increase the verbosity further:
+
+```json5
+"gopls": {
+  "verboseOutput": true
+}
+```
 ## File an issue
 
 We can't diagnose a problem from just a description. When filing an issue, please include as much as possible of the following information:
@@ -66,6 +73,6 @@
 1. Your Go extension version: `Extensions: Show Installed Extensions`
 1. Your Go environment: `go env` in the workspace folder
 1. Relevant VS Code settings: run `Preferences: Open Settings (JSON)` and include anything in a `[go]` block, and anything that starts with `go.` or `gopls.`
-1. Extension and `gopls` logs as seems appropriate for the bug.
+1. Extension and `gopls` logs as seems appropriate for the bug. (Include from the beginning of the logs if possible.)
 
 Once you've collected that information, [file your issue](https://github.com/golang/vscode-go/issues/new/choose).
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 5117a0b..40c9081 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "go",
-  "version": "0.25.1",
+  "version": "0.26.0",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "go",
-      "version": "0.25.0",
+      "version": "0.26.0",
       "license": "MIT",
       "dependencies": {
         "deep-equal": "^2.0.2",
@@ -1180,16 +1180,16 @@
       "dev": true
     },
     "node_modules/browserslist": {
-      "version": "4.16.3",
-      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz",
-      "integrity": "sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==",
+      "version": "4.16.6",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz",
+      "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==",
       "dev": true,
       "dependencies": {
-        "caniuse-lite": "^1.0.30001181",
-        "colorette": "^1.2.1",
-        "electron-to-chromium": "^1.3.649",
+        "caniuse-lite": "^1.0.30001219",
+        "colorette": "^1.2.2",
+        "electron-to-chromium": "^1.3.723",
         "escalade": "^3.1.1",
-        "node-releases": "^1.1.70"
+        "node-releases": "^1.1.71"
       },
       "bin": {
         "browserslist": "cli.js"
@@ -1325,10 +1325,14 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001204",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001204.tgz",
-      "integrity": "sha512-JUdjWpcxfJ9IPamy2f5JaRDCaqJOxDzOSKtbdx4rH9VivMd1vIzoPumsJa9LoMIi4Fx2BV2KZOxWhNkBjaYivQ==",
-      "dev": true
+      "version": "1.0.30001230",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz",
+      "integrity": "sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ==",
+      "dev": true,
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/browserslist"
+      }
     },
     "node_modules/caseless": {
       "version": "0.12.0",
@@ -1861,9 +1865,9 @@
       }
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.3.695",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.695.tgz",
-      "integrity": "sha512-lz66RliUqLHU1Ojxx1A4QUxKydjiQ79Y4dZyPobs2Dmxj5aVL2TM3KoQ2Gs7HS703Bfny+ukI3KOxwAB0xceHQ==",
+      "version": "1.3.740",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.740.tgz",
+      "integrity": "sha512-Mi2m55JrX2BFbNZGKYR+2ItcGnR4O5HhrvgoRRyZQlaMGQULqDhoGkLWHzJoshSzi7k1PUofxcDbNhlFrDZNhg==",
       "dev": true
     },
     "node_modules/emoji-regex": {
@@ -4455,9 +4459,9 @@
       }
     },
     "node_modules/normalize-url": {
-      "version": "4.5.0",
-      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz",
-      "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==",
+      "version": "4.5.1",
+      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz",
+      "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==",
       "dev": true,
       "engines": {
         "node": ">=8"
@@ -5030,9 +5034,9 @@
       }
     },
     "node_modules/read-pkg/node_modules/hosted-git-info": {
-      "version": "2.8.8",
-      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
-      "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
+      "version": "2.8.9",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+      "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
       "dev": true
     },
     "node_modules/read-pkg/node_modules/normalize-package-data": {
@@ -5916,9 +5920,9 @@
       "link": true
     },
     "node_modules/trim-newlines": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz",
-      "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==",
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz",
+      "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==",
       "dev": true,
       "engines": {
         "node": ">=8"
@@ -7865,16 +7869,16 @@
       "dev": true
     },
     "browserslist": {
-      "version": "4.16.3",
-      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz",
-      "integrity": "sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==",
+      "version": "4.16.6",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz",
+      "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==",
       "dev": true,
       "requires": {
-        "caniuse-lite": "^1.0.30001181",
-        "colorette": "^1.2.1",
-        "electron-to-chromium": "^1.3.649",
+        "caniuse-lite": "^1.0.30001219",
+        "colorette": "^1.2.2",
+        "electron-to-chromium": "^1.3.723",
         "escalade": "^3.1.1",
-        "node-releases": "^1.1.70"
+        "node-releases": "^1.1.71"
       }
     },
     "buffer-from": {
@@ -7966,9 +7970,9 @@
       }
     },
     "caniuse-lite": {
-      "version": "1.0.30001204",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001204.tgz",
-      "integrity": "sha512-JUdjWpcxfJ9IPamy2f5JaRDCaqJOxDzOSKtbdx4rH9VivMd1vIzoPumsJa9LoMIi4Fx2BV2KZOxWhNkBjaYivQ==",
+      "version": "1.0.30001230",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz",
+      "integrity": "sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ==",
       "dev": true
     },
     "caseless": {
@@ -8394,9 +8398,9 @@
       }
     },
     "electron-to-chromium": {
-      "version": "1.3.695",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.695.tgz",
-      "integrity": "sha512-lz66RliUqLHU1Ojxx1A4QUxKydjiQ79Y4dZyPobs2Dmxj5aVL2TM3KoQ2Gs7HS703Bfny+ukI3KOxwAB0xceHQ==",
+      "version": "1.3.740",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.740.tgz",
+      "integrity": "sha512-Mi2m55JrX2BFbNZGKYR+2ItcGnR4O5HhrvgoRRyZQlaMGQULqDhoGkLWHzJoshSzi7k1PUofxcDbNhlFrDZNhg==",
       "dev": true
     },
     "emoji-regex": {
@@ -10328,9 +10332,9 @@
       "dev": true
     },
     "normalize-url": {
-      "version": "4.5.0",
-      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz",
-      "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==",
+      "version": "4.5.1",
+      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz",
+      "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==",
       "dev": true
     },
     "npm-run-path": {
@@ -10697,9 +10701,9 @@
       },
       "dependencies": {
         "hosted-git-info": {
-          "version": "2.8.8",
-          "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
-          "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
+          "version": "2.8.9",
+          "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+          "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
           "dev": true
         },
         "normalize-package-data": {
@@ -11423,9 +11427,9 @@
       "version": "file:third_party/tree-kill"
     },
     "trim-newlines": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz",
-      "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==",
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz",
+      "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==",
       "dev": true
     },
     "ts-loader": {
diff --git a/package.json b/package.json
index 4faf38c..165f2b6 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "go",
   "displayName": "Go",
-  "version": "0.25.1",
+  "version": "0.26.0",
   "publisher": "golang",
   "description": "Rich Go language support for Visual Studio Code",
   "author": {
@@ -88,7 +88,6 @@
   "engines": {
     "vscode": "^1.52.0"
   },
-  "requiresWorkspaceTrust": "onStart",
   "activationEvents": [
     "workspaceContains:**/*.go",
     "onLanguage:go",
@@ -102,6 +101,20 @@
     "onWebviewPanel:welcomeGo"
   ],
   "main": "./dist/goMain.js",
+  "capabilities": {
+    "virtualWorkspaces": false,
+    "untrustedWorkspaces": {
+      "supported": "limited",
+      "restrictedConfigurations": [
+        "go.alternateTools",
+        "go.gopath",
+        "go.goroot",
+        "go.inferGopath",
+        "go.toolsGopath",
+        "go.toolsEnvVars"
+      ]
+    }
+  },
   "contributes": {
     "languages": [
       {
@@ -179,6 +192,11 @@
         "description": "Runs a unit test at the cursor."
       },
       {
+        "command": "go.test.cursorOrPrevious",
+        "title": "Go: Test Function At Cursor or Test Previous",
+        "description": "Runs a unit test at the cursor if one is found, otherwise re-runs the last executed test."
+      },
+      {
         "command": "go.subtest.cursor",
         "title": "Go: Subtest At Cursor",
         "description": "Runs a sub test at the cursor."
@@ -548,8 +566,8 @@
               },
               "cwd": {
                 "type": "string",
-                "description": "Workspace relative or absolute path to the working directory of the program being debugged. Default is the current workspace.",
-                "default": "."
+                "description": "Workspace relative or absolute path to the working directory of the program being debugged if a non-empty value is specified. The 'program' folder is used as the working directory if it is omitted or empty.",
+                "default": ""
               },
               "env": {
                 "type": "object",
@@ -581,14 +599,9 @@
                 "description": "Build flags, to be passed to the Go compiler. Maps to dlv's `--build-flags` flag.",
                 "default": ""
               },
-              "init": {
-                "type": "string",
-                "description": "Init file, executed by the terminal client. Maps to dlv's `--init` flag.",
-                "default": ""
-              },
               "dlvFlags": {
                 "type": "array",
-                "description": "Extra flags for `dlv`. See `dlv help` for the full list of supported. Flags such as `--log-output`, `--log`, `--log-dest`, `--init`, `--api-version`, `--output`, `--backend` already have corresponding properties in the debug configuration, and flags such as `--listen` and `--headless` are used internally. If they are specified in `dlvFlags`, they may be ignored or cause an error.",
+                "description": "Extra flags for `dlv`. See `dlv help` for the full list of supported. Flags such as `--log-output`, `--log`, `--log-dest`, `--api-version`, `--output`, `--backend` already have corresponding properties in the debug configuration, and flags such as `--listen` and `--headless` are used internally. If they are specified in `dlvFlags`, they may be ignored or cause an error.",
                 "items": {
                   "type": "string"
                 },
@@ -770,7 +783,7 @@
               },
               "dlvFlags": {
                 "type": "array",
-                "description": "Extra flags for `dlv`. See `dlv help` for the full list of supported. Flags such as `--log-output`, `--log`, `--log-dest`, `--init`, `--api-version`, `--output`, `--backend` already have corresponding properties in the debug configuration, and flags such as `--listen` and `--headless` are used internally. If they are specified in `dlvFlags`, they may be ignored or cause an error.",
+                "description": "Extra flags for `dlv`. See `dlv help` for the full list of supported. Flags such as `--log-output`, `--log`, `--log-dest`, `--api-version`, `--output`, `--backend` already have corresponding properties in the debug configuration, and flags such as `--listen` and `--headless` are used internally. If they are specified in `dlvFlags`, they may be ignored or cause an error.",
                 "items": {
                   "type": "string"
                 },
@@ -1727,6 +1740,26 @@
               ],
               "description": "Select which debug adapter to use by default. This is also used for choosing which debug adapter to use when no launch.json is present and with codelenses.",
               "default": "legacy"
+            },
+            "substitutePath": {
+              "type": "array",
+              "items": {
+                "type": "object",
+                "properties": {
+                  "from": {
+                    "type": "string",
+                    "description": "The absolute local path to be replaced when passing paths to the debugger",
+                    "default": ""
+                  },
+                  "to": {
+                    "type": "string",
+                    "description": "The absolute remote path to be replaced when passing paths back to the client",
+                    "default": ""
+                  }
+                }
+              },
+              "description": "An array of mappings from a local path to the remote path that is used by the debuggee. The debug adapter will replace the local path with the remote path in all of the calls. Overriden by remotePath.",
+              "default": []
             }
           },
           "default": {
@@ -1739,7 +1772,8 @@
             },
             "apiVersion": 2,
             "showGlobalVariables": false,
-            "debugAdapter": "legacy"
+            "debugAdapter": "legacy",
+            "substitutePath": []
           },
           "description": "Delve settings that applies to all debugging sessions. Debug configuration in the launch.json file will override these values.",
           "scope": "resource"
@@ -1822,6 +1856,12 @@
               "default": true,
               "scope": "resource"
             },
+            "build.experimentalTemplateSupport": {
+              "type": "boolean",
+              "markdownDescription": "(Experimental) experimentalTemplateSupport opts into the experimental support\nfor template files.\n",
+              "default": false,
+              "scope": "resource"
+            },
             "build.experimentalWorkspaceModule": {
               "type": "boolean",
               "markdownDescription": "(Experimental) experimentalWorkspaceModule opts a user into the experimental support\nfor multi-module workspaces.\n",
@@ -1905,7 +1945,7 @@
             "ui.completion.experimentalPostfixCompletions": {
               "type": "boolean",
               "markdownDescription": "(Experimental) experimentalPostfixCompletions enables artifical method snippets\nsuch as \"someSlice.sort!\".\n",
-              "default": false,
+              "default": true,
               "scope": "resource"
             },
             "ui.completion.matcher": {
diff --git a/src/config.ts b/src/config.ts
index 9303254..d8f54ac 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -17,15 +17,29 @@
 	'toolsEnvVars'
 ];
 
+// Set true only if the vscode is the recent version that has the workspace trust API AND
+// if the security.workspace.trust is enabled. Change of this configuration requires restart
+// of VSCode, so we don't need to set up the configuration change listener.
+// TODO(hyangah): remove this and Configuration & WrappedConfiguration when we update
+// our extension to require 2021 June VSCode engine.
+const isVscodeWorkspaceTrustAPIAvailable =
+	'boolean' === typeof (vscode.workspace as any).isTrusted &&
+	vscode.workspace.getConfiguration('security.workspace.trust')?.get('enabled') === true;
+
 // Initialize the singleton defaultConfig and register related commands.
 // Prompt if workspace configuration was found but had to be ignored until
 // the user has to explicitly opt in to trust the workspace.
 export async function initConfig(ctx: vscode.ExtensionContext) {
+	ctx.subscriptions.push(vscode.commands.registerCommand('go.workspace.isTrusted.toggle', toggleWorkspaceIsTrusted));
+
+	if (isVscodeWorkspaceTrustAPIAvailable) {
+		return; // let vscode handle configuration management.
+	}
+
 	const isTrusted = getFromWorkspaceState(WORKSPACE_IS_TRUSTED_KEY, false);
 	if (isTrusted !== defaultConfig.workspaceIsTrusted()) {
 		defaultConfig.toggleWorkspaceIsTrusted();
 	}
-	ctx.subscriptions.push(vscode.commands.registerCommand('go.workspace.isTrusted.toggle', toggleWorkspaceIsTrusted));
 
 	if (isTrusted) {
 		return;
@@ -65,6 +79,10 @@
 }
 
 async function toggleWorkspaceIsTrusted() {
+	if (isVscodeWorkspaceTrustAPIAvailable) {
+		vscode.commands.executeCommand('workbench.action.manageTrust');
+		return;
+	}
 	const v = defaultConfig.toggleWorkspaceIsTrusted();
 	await updateWorkspaceState(WORKSPACE_IS_TRUSTED_KEY, v);
 }
@@ -93,7 +111,19 @@
 	}
 }
 
-const defaultConfig = new Configuration();
+class vscodeConfiguration {
+	public toggleWorkspaceIsTrusted() {
+		/* no-op */
+	}
+	public get(section: string, uri?: vscode.Uri): vscode.WorkspaceConfiguration {
+		return vscode.workspace.getConfiguration(section, uri);
+	}
+	public workspaceIsTrusted(): boolean {
+		return !!(vscode.workspace as any).isTrusted;
+	}
+}
+
+const defaultConfig = isVscodeWorkspaceTrustAPIAvailable ? new vscodeConfiguration() : new Configuration();
 
 // Returns the workspace Configuration used by the extension.
 export function DefaultConfig() {
diff --git a/src/debugAdapter/goDebug.ts b/src/debugAdapter/goDebug.ts
index 4b23dfe..1322e02 100644
--- a/src/debugAdapter/goDebug.ts
+++ b/src/debugAdapter/goDebug.ts
@@ -357,7 +357,7 @@
 }
 
 export function findPathSeparator(filePath: string) {
-	return filePath.includes('\\') ? '\\' : '/';
+	return filePath && filePath.includes('\\') ? '\\' : '/';
 }
 
 // Comparing two different file paths while ignoring any different path separators.
@@ -649,9 +649,6 @@
 				if (launchArgs.buildFlags) {
 					dlvArgs.push('--build-flags=' + launchArgs.buildFlags);
 				}
-				if (launchArgs.init) {
-					dlvArgs.push('--init=' + launchArgs.init);
-				}
 				if (launchArgs.backend) {
 					dlvArgs.push('--backend=' + launchArgs.backend);
 				}
diff --git a/src/goDebugConfiguration.ts b/src/goDebugConfiguration.ts
index dad70f3..e86819b 100644
--- a/src/goDebugConfiguration.ts
+++ b/src/goDebugConfiguration.ts
@@ -18,11 +18,12 @@
 	promptForUpdatingTool,
 	shouldUpdateTool
 } from './goInstallTools';
+import { isInPreviewMode } from './goLanguageServer';
 import { packagePathToGoModPathMap } from './goModules';
 import { getTool, getToolAtVersion } from './goTools';
 import { pickProcess, pickProcessByName } from './pickProcess';
 import { getFromGlobalState, updateGlobalState } from './stateUtils';
-import { getBinPath, getGoVersion } from './util';
+import { getBinPath, getGoVersion, getWorkspaceFolderPath, resolvePath } from './util';
 import { parseEnvFiles } from './utils/envUtils';
 import { resolveHomeDir } from './utils/pathUtils';
 
@@ -147,8 +148,26 @@
 		// Figure out which debugAdapter is being used first, so we can use this to send warnings
 		// for properties that don't apply.
 		if (!debugConfiguration.hasOwnProperty('debugAdapter') && dlvConfig.hasOwnProperty('debugAdapter')) {
-			debugConfiguration['debugAdapter'] = dlvConfig['debugAdapter'];
+			const { globalValue, workspaceValue } = goConfig.inspect('delveConfig.debugAdapter');
+			// user configured the default debug adapter through settings.json.
+			if (globalValue !== undefined || workspaceValue !== undefined) {
+				debugConfiguration['debugAdapter'] = dlvConfig['debugAdapter'];
+			}
 		}
+		if (!debugConfiguration['debugAdapter']) {
+			// for nightly/dev mode, default to dlv-dap.
+			// TODO(hyangah): when we switch the stable version's default to 'dlv-dap', adjust this.
+			debugConfiguration['debugAdapter'] =
+				isInPreviewMode() && debugConfiguration['mode'] !== 'remote' ? 'dlv-dap' : 'legacy';
+		}
+		if (debugConfiguration['debugAdapter'] === 'dlv-dap' && debugConfiguration['mode'] === 'remote') {
+			this.showWarning(
+				'ignoreDlvDAPInRemoteModeWarning',
+				"debugAdapter type of 'dlv-dap' with mode 'remote' is unsupported. Fall back to the 'legacy' debugAdapter for 'remote' mode."
+			);
+			debugConfiguration['debugAdapter'] = 'legacy';
+		}
+
 		const debugAdapter = debugConfiguration['debugAdapter'] === 'dlv-dap' ? 'dlv-dap' : 'dlv';
 
 		let useApiV1 = false;
@@ -183,8 +202,19 @@
 		) {
 			debugConfiguration['showGlobalVariables'] = dlvConfig['showGlobalVariables'];
 		}
-		if (debugConfiguration.request === 'attach' && !debugConfiguration['cwd']) {
+		if (!debugConfiguration.hasOwnProperty('substitutePath') && dlvConfig.hasOwnProperty('substitutePath')) {
+			debugConfiguration['substitutePath'] = dlvConfig['substitutePath'];
+		}
+		if (
+			debugAdapter !== 'dlv-dap' &&
+			debugConfiguration.request === 'attach' &&
+			debugConfiguration.mode === 'remote' &&
+			!debugConfiguration['cwd']
+		) {
 			debugConfiguration['cwd'] = '${workspaceFolder}';
+			if (vscode.workspace.workspaceFolders?.length > 1) {
+				debugConfiguration['cwd'] = '${fileWorkspaceFolder}';
+			}
 		}
 		if (debugConfiguration['cwd']) {
 			// expand 'cwd' folder path containing '~', which would cause dlv to fail
@@ -259,7 +289,7 @@
 				// file path instead of the currently active file.
 				filename = debugConfiguration['program'];
 			}
-			debugConfiguration['mode'] = filename.endsWith('_test.go') ? 'test' : 'debug';
+			debugConfiguration['mode'] = filename?.endsWith('_test.go') ? 'test' : 'debug';
 		}
 
 		if (debugConfiguration['mode'] === 'test' && debugConfiguration['program'].endsWith('_test.go')) {
@@ -291,7 +321,11 @@
 			if (!debugConfiguration['processId'] || debugConfiguration['processId'] === 0) {
 				// The processId is not valid, offer a quickpick menu of all processes.
 				debugConfiguration['processId'] = parseInt(await pickProcess(), 10);
-			} else if (typeof debugConfiguration['processId'] === 'string') {
+			} else if (
+				typeof debugConfiguration['processId'] === 'string' &&
+				debugConfiguration['processId'] !== '${command:pickProcess}' &&
+				debugConfiguration['processId'] !== '${command:pickGoProcess}'
+			) {
 				debugConfiguration['processId'] = parseInt(
 					await pickProcessByName(debugConfiguration['processId']),
 					10
@@ -355,6 +389,22 @@
 		debugConfiguration['env'] = Object.assign(goToolsEnvVars, fileEnvs, env);
 		debugConfiguration['envFile'] = undefined; // unset, since we already processed.
 
+		const entriesWithRelativePaths = ['cwd', 'output', 'program'].filter(
+			(attr) => debugConfiguration[attr] && !path.isAbsolute(debugConfiguration[attr])
+		);
+		if (debugConfiguration['debugAdapter'] === 'dlv-dap' && entriesWithRelativePaths.length > 0) {
+			const workspaceRoot = folder?.uri.fsPath;
+			if (!workspaceRoot) {
+				this.showWarning(
+					'relativePathsWithoutWorkspaceFolder',
+					'Relative paths without a workspace folder for `cwd`, `program`, or `output` are not allowed.'
+				);
+				return null;
+			}
+			entriesWithRelativePaths.forEach((attr) => {
+				debugConfiguration[attr] = path.join(workspaceRoot, debugConfiguration[attr]);
+			});
+		}
 		return debugConfiguration;
 	}
 
diff --git a/src/goDebugFactory.ts b/src/goDebugFactory.ts
index ecb9ac4..a247e30 100644
--- a/src/goDebugFactory.ts
+++ b/src/goDebugFactory.ts
@@ -15,6 +15,8 @@
 import * as net from 'net';
 import { getTool } from './goTools';
 import { Logger, TimestampedLogger } from './goLogging';
+import { DebugProtocol } from 'vscode-debugprotocol';
+import { getWorkspaceFolderPath } from './util';
 
 export class GoDebugAdapterDescriptorFactory implements vscode.DebugAdapterDescriptorFactory {
 	constructor(private outputChannel?: vscode.OutputChannel) {}
@@ -41,7 +43,6 @@
 		}
 		const logger = new TimestampedLogger(configuration.trace, this.outputChannel);
 		const d = new DelveDAPOutputAdapter(configuration, logger);
-		await d.startAndConnectToServer();
 		return new vscode.DebugAdapterInlineImplementation(d);
 	}
 }
@@ -194,17 +195,40 @@
 // VSCode and a dlv dap process spawned and managed by this adapter.
 // It turns the process's stdout/stderrr into OutputEvent.
 export class DelveDAPOutputAdapter extends ProxyDebugAdapter {
-	constructor(private config: vscode.DebugConfiguration, logger?: Logger) {
+	constructor(private configuration: vscode.DebugConfiguration, logger?: Logger) {
 		super(logger);
+		this.connected = this.startAndConnectToServer();
 	}
 
+	private connected: Promise<{ connected: boolean; reason?: any }>;
 	private dlvDapServer: ChildProcess;
 	private port: number;
 	private socket: net.Socket;
 	private terminatedOnError = false;
 
 	protected async sendMessageToServer(message: vscode.DebugProtocolMessage): Promise<void> {
-		super.sendMessageToServer(message);
+		const { connected, reason } = await this.connected;
+		if (connected) {
+			super.sendMessageToServer(message);
+			return;
+		}
+		const errMsg = `Couldn't start dlv dap:\n${reason}`;
+		if (this.terminatedOnError) {
+			this.terminatedOnError = true;
+			this.outputEvent('stderr', errMsg);
+			this.sendMessageToClient(new TerminatedEvent());
+		}
+		if ((message as any).type === 'request') {
+			const req = message as DebugProtocol.Request;
+			this.sendMessageToClient({
+				seq: 0,
+				type: 'response',
+				request_seq: req.seq,
+				success: false,
+				command: req.command,
+				message: errMsg
+			});
+		}
 	}
 
 	async dispose(timeoutMS?: number) {
@@ -214,8 +238,12 @@
 		if (!this.dlvDapServer) {
 			return;
 		}
+		if (this.connected === undefined) {
+			return;
+		}
+		this.connected = undefined;
 
-		if (timeoutMS === undefined) {
+		if (timeoutMS === undefined || timeoutMS < 0) {
 			timeoutMS = 1_000;
 		}
 		const dlvDapServer = this.dlvDapServer;
@@ -233,7 +261,6 @@
 			const exitTimeoutToken = setTimeout(() => {
 				this.logger?.error(`dlv dap process (${dlvDapServer.pid}) isn't responding. Killing...`);
 				dlvDapServer.kill('SIGINT'); // Don't use treekill but let dlv handle cleaning up the child processes.
-				resolve();
 			}, timeoutMS);
 			dlvDapServer.on('exit', (code, signal) => {
 				clearTimeout(exitTimeoutToken);
@@ -247,37 +274,43 @@
 		});
 	}
 
-	public async startAndConnectToServer() {
-		const { port, host, dlvDapServer } = await startDapServer(
-			this.config,
-			(msg) => this.outputEvent('stdout', msg),
-			(msg) => this.outputEvent('stderr', msg),
-			(msg) => {
-				this.outputEvent('console', msg);
-				// Some log messages generated after vscode stops the debug session
-				// may not appear in the DEBUG CONSOLE. For easier debugging, log
-				// the messages through the logger that prints to Go Debug output
-				// channel.
-				this.logger?.info(msg);
-			}
-		);
-		const socket = await new Promise<net.Socket>((resolve, reject) => {
-			// eslint-disable-next-line prefer-const
-			let timer: NodeJS.Timeout;
-			const s = net.createConnection(port, host, () => {
-				clearTimeout(timer);
-				resolve(s);
+	private async startAndConnectToServer() {
+		try {
+			const { port, host, dlvDapServer } = await startDapServer(
+				this.configuration,
+				(msg) => this.outputEvent('stdout', msg),
+				(msg) => this.outputEvent('stderr', msg),
+				(msg) => {
+					this.outputEvent('console', msg);
+					// Some log messages generated after vscode stops the debug session
+					// may not appear in the DEBUG CONSOLE. For easier debugging, log
+					// the messages through the logger that prints to Go Debug output
+					// channel.
+					this.logger?.info(msg);
+				}
+			);
+			const socket = await new Promise<net.Socket>((resolve, reject) => {
+				// eslint-disable-next-line prefer-const
+				let timer: NodeJS.Timeout;
+				const s = net.createConnection(port, host, () => {
+					clearTimeout(timer);
+					resolve(s);
+				});
+				timer = setTimeout(() => {
+					reject('connection timeout');
+					s?.destroy();
+				}, 1000);
 			});
-			timer = setTimeout(() => {
-				reject('connection timeout');
-				s?.destroy();
-			}, 1000);
-		});
 
-		this.dlvDapServer = dlvDapServer;
-		this.port = port;
-		this.socket = socket;
-		this.start(this.socket, this.socket);
+			this.dlvDapServer = dlvDapServer;
+			this.port = port;
+			this.socket = socket;
+			this.start(this.socket, this.socket);
+		} catch (err) {
+			return { connected: false, reason: err };
+		}
+		this.logger?.debug(`Running dlv dap server: port=${this.port} pid=${this.dlvDapServer.pid}\n`);
+		return { connected: true };
 	}
 
 	private outputEvent(dest: string, output: string, data?: any) {
@@ -285,11 +318,11 @@
 	}
 }
 
-export async function startDapServer(
+async function startDapServer(
 	configuration: vscode.DebugConfiguration,
-	log?: (msg: string) => void,
-	logErr?: (msg: string) => void,
-	logConsole?: (msg: string) => void
+	log: (msg: string) => void,
+	logErr: (msg: string) => void,
+	logConsole: (msg: string) => void
 ): Promise<{ port: number; host: string; dlvDapServer?: ChildProcessWithoutNullStreams }> {
 	const host = configuration.host || '127.0.0.1';
 
@@ -299,56 +332,56 @@
 		return { port: configuration.port, host };
 	}
 	const port = await getPort();
-	if (!log) {
-		log = appendToDebugConsole;
-	}
-	if (!logErr) {
-		logErr = appendToDebugConsole;
-	}
-	if (!logConsole) {
-		logConsole = appendToDebugConsole;
-	}
 	const dlvDapServer = await spawnDlvDapServerProcess(configuration, host, port, log, logErr, logConsole);
 	return { dlvDapServer, port, host };
 }
 
-async function spawnDlvDapServerProcess(
-	launchArgs: vscode.DebugConfiguration,
+function spawnDlvDapServerProcess(
+	launchAttachArgs: vscode.DebugConfiguration,
 	host: string,
 	port: number,
 	log: (msg: string) => void,
 	logErr: (msg: string) => void,
 	logConsole: (msg: string) => void
 ): Promise<ChildProcess> {
-	const launchArgsEnv = launchArgs.env || {};
+	const launchArgsEnv = launchAttachArgs.env || {};
 	const env = Object.assign({}, process.env, launchArgsEnv);
 
-	const dlvPath = launchArgs.dlvToolPath ?? getTool('dlv-dap');
+	const dlvPath = launchAttachArgs.dlvToolPath ?? getTool('dlv-dap');
 
 	if (!fs.existsSync(dlvPath)) {
 		const envPath = process.env['PATH'] || (process.platform === 'win32' ? process.env['Path'] : null);
 		logErr(
 			`Couldn't find dlv-dap at the Go tools path, ${process.env['GOPATH']}${
 				env['GOPATH'] ? ', ' + env['GOPATH'] : ''
-			} or ${envPath}`
+			} or ${envPath}\n` +
+				'Follow the setup instruction in https://github.com/golang/vscode-go/blob/master/docs/dlv-dap.md#getting-started.\n'
 		);
-		throw new Error(
-			'Cannot find Delve debugger. Install from https://github.com/go-delve/delve & ensure it is in your Go tools path, "GOPATH/bin" or "PATH".'
-		);
+		throw new Error('Cannot find Delve debugger (dlv dap)');
 	}
+	let dir = getWorkspaceFolderPath();
+	if (launchAttachArgs.request === 'launch') {
+		try {
+			dir = parseProgramArgSync(launchAttachArgs).dirname;
+		} catch (err) {
+			logErr(`Program arg: ${launchAttachArgs.program}\n${err}\n`);
+			throw err; // rethrow so the caller knows it failed.
+		}
+	}
+
 	const dlvArgs = new Array<string>();
 	dlvArgs.push('dap');
 	// add user-specified dlv flags first. When duplicate flags are specified,
 	// dlv doesn't mind but accepts the last flag value.
-	if (launchArgs.dlvFlags && launchArgs.dlvFlags.length > 0) {
-		dlvArgs.push(...launchArgs.dlvFlags);
+	if (launchAttachArgs.dlvFlags && launchAttachArgs.dlvFlags.length > 0) {
+		dlvArgs.push(...launchAttachArgs.dlvFlags);
 	}
 	dlvArgs.push(`--listen=${host}:${port}`);
-	if (launchArgs.showLog) {
-		dlvArgs.push('--log=' + launchArgs.showLog.toString());
+	if (launchAttachArgs.showLog) {
+		dlvArgs.push('--log=' + launchAttachArgs.showLog.toString());
 	}
-	if (launchArgs.logOutput) {
-		dlvArgs.push('--log-output=' + launchArgs.logOutput);
+	if (launchAttachArgs.logOutput) {
+		dlvArgs.push('--log-output=' + launchAttachArgs.logOutput);
 	}
 
 	const onWindows = process.platform === 'win32';
@@ -357,14 +390,14 @@
 		dlvArgs.push('--log-dest=3');
 	}
 
-	const logDest = launchArgs.logDest;
+	const logDest = launchAttachArgs.logDest;
 	if (typeof logDest === 'number') {
-		logErr('Using a file descriptor for `logDest` is not allowed.');
+		logErr(`Using a file descriptor for 'logDest' (${logDest}) is not allowed.\n`);
 		throw new Error('Using a file descriptor for `logDest` is not allowed.');
 	}
 	if (logDest && !path.isAbsolute(logDest)) {
 		logErr(
-			'Using a relative path for `logDest` is not allowed.\nSee [variables](https://code.visualstudio.com/docs/editor/variables-reference)'
+			`Using a relative path for 'logDest' (${logDest}) is not allowed.\nSee https://code.visualstudio.com/docs/editor/variables-reference if you want workspace-relative path.\n`
 		);
 		throw new Error('Using a relative path for `logDest` is not allowed');
 	}
@@ -377,14 +410,13 @@
 
 	const logDestStream = logDest ? fs.createWriteStream(logDest) : undefined;
 
-	logConsole(`Running: ${dlvPath} ${dlvArgs.join(' ')}\n`);
+	logConsole(`Starting: ${dlvPath} ${dlvArgs.join(' ')}\n`);
 
-	const dir = parseProgramArgSync(launchArgs).dirname;
-	// TODO(hyangah): determine the directories:
-	//    run `dlv` => where dlv will create the default __debug_bin. (This won't work if the directory is not writable. Fix it)
-	//    build program => 'program' directory. (This won't work for multimodule workspace. Fix it)
-	//    run program => cwd (If test, make sure to run in the package directory.)
-	return await new Promise<ChildProcess>((resolve, reject) => {
+	// TODO(hyangah): In module-module workspace mode, the program should be build in the super module directory
+	// where go.work (gopls.mod) file is present. Where dlv runs determines the build directory currently. Two options:
+	//  1) launch dlv in the super-module module directory and adjust launchArgs.cwd (--wd).
+	//  2) introduce a new buildDir launch attribute.
+	return new Promise<ChildProcess>((resolve, reject) => {
 		const p = spawn(dlvPath, dlvArgs, {
 			cwd: dir,
 			env,
@@ -482,9 +514,9 @@
 }
 
 export function parseProgramArgSync(
-	launchArgs: vscode.DebugConfiguration
+	launchAttachArgs: vscode.DebugConfiguration
 ): { program: string; dirname: string; programIsDirectory: boolean } {
-	const program = launchArgs.program;
+	const program = launchAttachArgs.program;
 	if (!program) {
 		throw new Error('The program attribute is missing in the debug configuration in launch.json');
 	}
@@ -495,14 +527,9 @@
 		// TODO(hyangah): why can't the program be a package name?
 		throw new Error('The program attribute must point to valid directory, .go file or executable.');
 	}
-	if (!programIsDirectory && launchArgs.mode !== 'exec' && path.extname(program) !== '.go') {
+	if (!programIsDirectory && launchAttachArgs.mode !== 'exec' && path.extname(program) !== '.go') {
 		throw new Error('The program attribute must be a directory or .go file in debug and test mode');
 	}
 	const dirname = programIsDirectory ? program : path.dirname(program);
 	return { program, dirname, programIsDirectory };
 }
-
-// appendToDebugConsole is declared as an exported const rather than a function, so it can be stubbbed in testing.
-export const appendToDebugConsole = (msg: string) => {
-	console.error(msg);
-};
diff --git a/src/goEnvironmentStatus.ts b/src/goEnvironmentStatus.ts
index 1190b31..2262aa6 100644
--- a/src/goEnvironmentStatus.ts
+++ b/src/goEnvironmentStatus.ts
@@ -621,8 +621,11 @@
 					neverAgain
 				)
 				.then((selection) => {
+					// TODO: should we removeGoStatus if user has closed the notification
+					// without any action? It's kind of a feature now - without selecting
+					// neverAgain, user can hide this statusbar item.
 					removeGoStatus();
-					selection.command();
+					selection?.command();
 				});
 		});
 	}
diff --git a/src/goImport.ts b/src/goImport.ts
index d885196..f0fa707 100644
--- a/src/goImport.ts
+++ b/src/goImport.ts
@@ -9,8 +9,10 @@
 
 import cp = require('child_process');
 import vscode = require('vscode');
+import { ExecuteCommandRequest, ExecuteCommandParams } from 'vscode-languageserver-protocol';
 import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool } from './goInstallTools';
+import { languageClient } from './goLanguageServer';
 import { documentSymbols, GoOutlineImportsOptions } from './goOutline';
 import { getImportablePackages } from './goPackages';
 import { getBinPath, getImportPath, parseFilePrelude } from './util';
@@ -39,6 +41,31 @@
 	return [...stdLibs.sort(), ...nonStdLibs.sort()];
 }
 
+async function golist(): Promise<string[]> {
+	if (languageClient) {
+		try {
+			const uri = languageClient.code2ProtocolConverter.asTextDocumentIdentifier(
+				vscode.window.activeTextEditor.document
+			).uri;
+			const params: ExecuteCommandParams = {
+				command: 'gopls.list_known_packages',
+				arguments: [
+					{
+						URI: uri
+					}
+				]
+			};
+			const resp = await languageClient.sendRequest(ExecuteCommandRequest.type, params);
+			return resp.Packages;
+		} catch (e) {
+			console.log(`error with gopls.list_known_packages: ${e}`);
+		}
+	}
+
+	// fallback to calling listPackages
+	return listPackages(true);
+}
+
 /**
  * Returns the imported packages in the given file
  *
@@ -64,7 +91,7 @@
 
 async function askUserForImport(): Promise<string | undefined> {
 	try {
-		const packages = await listPackages(true);
+		const packages = await golist();
 		return vscode.window.showQuickPick(packages);
 	} catch (err) {
 		if (typeof err === 'string' && err.startsWith(missingToolMsg)) {
@@ -123,17 +150,40 @@
 	}
 }
 
-export function addImport(arg: { importPath: string; from: string }) {
+export function addImport(arg: { importPath: string }) {
 	const editor = vscode.window.activeTextEditor;
 	if (!editor) {
 		vscode.window.showErrorMessage('No active editor found to add imports.');
 		return;
 	}
 	const p = arg && arg.importPath ? Promise.resolve(arg.importPath) : askUserForImport();
-	p.then((imp) => {
+	p.then(async (imp) => {
 		if (!imp) {
 			return;
 		}
+
+		if (languageClient) {
+			try {
+				const uri = languageClient.code2ProtocolConverter.asTextDocumentIdentifier(
+					vscode.window.activeTextEditor.document
+				).uri;
+				const params: ExecuteCommandParams = {
+					command: 'gopls.add_import',
+					arguments: [
+						{
+							ImportPath: imp,
+							URI: uri
+						}
+					]
+				};
+				await languageClient.sendRequest(ExecuteCommandRequest.type, params);
+				return;
+			} catch (e) {
+				console.log(`error executing gopls.add_import: ${e}`);
+			}
+		}
+
+		// fallback to adding imports directly from client
 		const edits = getTextEditForAddImport(imp);
 		if (edits && edits.length > 0) {
 			const edit = new vscode.WorkspaceEdit();
diff --git a/src/goLanguageServer.ts b/src/goLanguageServer.ts
index a2b2d88..5014946 100644
--- a/src/goLanguageServer.ts
+++ b/src/goLanguageServer.ts
@@ -330,21 +330,19 @@
 	}
 	let goplsVersion = await getLocalGoplsVersion(latestConfig);
 	if (!goplsVersion) {
-		goplsVersion = 'no gopls version found';
+		goplsVersion = 'na';
 	}
-	goplsVersion = `gopls/${goplsVersion}`;
 	const goV = await getGoVersion();
-	let goVersion = 'no go version found';
+	let goVersion = 'na';
 	if (goV) {
-		goVersion = `go${goV.format(true)}`;
+		goVersion = goV.format(true);
 	}
-	const version = [goplsVersion, goVersion, process.platform].join(';');
 	switch (s.title) {
 		case 'Yes':
 			cfg.prompt = false;
 			await vscode.env.openExternal(
 				vscode.Uri.parse(
-					`https://docs.google.com/forms/d/e/1FAIpQLScITGOe2VdQnaXigSIiD19VxN_2KLwjMszZOMZp9TgYvTOw5g/viewform?entry.1049591455=${version}&gxids=7826`
+					`https://google.qualtrics.com/jfe/form/SV_doId0RNgV3pHovc?gopls=${goplsVersion}&go=${goVersion}&os=${process.platform}`
 				)
 			);
 			break;
@@ -459,22 +457,15 @@
 	const goplsWorkspaceConfig = await adjustGoplsWorkspaceConfiguration(cfg, getGoplsConfig(), 'gopls', undefined);
 
 	const documentSelector = [
-		// Filter out unsupported document types, e.g. vsls, git, ssh.
-		// https://docs.microsoft.com/en-us/visualstudio/liveshare/reference/extensions#visual-studio-code-1
-		//
-		// - files
+		// gopls handles only file URIs.
 		{ language: 'go', scheme: 'file' },
 		{ language: 'go.mod', scheme: 'file' },
-		{ language: 'go.sum', scheme: 'file' },
-		// - unsaved files
-		{ language: 'go', scheme: 'untitled' },
-		{ language: 'go.mod', scheme: 'untitled' },
-		{ language: 'go.sum', scheme: 'untitled' }
+		{ language: 'go.sum', scheme: 'file' }
 	];
 
 	// Let gopls know about .tmpl - this is experimental, so enable it only in the experimental mode now.
 	if (isInPreviewMode()) {
-		documentSelector.push({ language: 'tmpl', scheme: 'file' }, { language: 'tmpl', scheme: 'untitled' });
+		documentSelector.push({ language: 'tmpl', scheme: 'file' });
 	}
 	const c = new LanguageClient(
 		'go', // id
diff --git a/src/goLint.ts b/src/goLint.ts
index 49b9178..269763c 100644
--- a/src/goLint.ts
+++ b/src/goLint.ts
@@ -16,15 +16,17 @@
  */
 export function lintCode(scope?: string) {
 	const editor = vscode.window.activeTextEditor;
-	if (!editor && scope !== 'workspace') {
-		vscode.window.showInformationMessage('No editor is active, cannot find current package to lint');
-		return;
-	}
-	if (editor.document.languageId !== 'go' && scope !== 'workspace') {
-		vscode.window.showInformationMessage(
-			'File in the active editor is not a Go file, cannot find current package to lint'
-		);
-		return;
+	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;
+		}
 	}
 
 	const documentUri = editor ? editor.document.uri : null;
@@ -37,7 +39,7 @@
 
 	goLint(documentUri, goConfig, goplsConfig, scope)
 		.then((warnings) => {
-			handleDiagnosticErrors(editor ? editor.document : null, warnings, lintDiagnosticCollection, 'go-lint');
+			handleDiagnosticErrors(editor ? editor.document : null, warnings, lintDiagnosticCollection);
 			diagnosticsStatusBarItem.hide();
 		})
 		.catch((err) => {
diff --git a/src/goMain.ts b/src/goMain.ts
index d03f863..b215f07 100644
--- a/src/goMain.ts
+++ b/src/goMain.ts
@@ -64,6 +64,7 @@
 	debugPrevious,
 	subTestAtCursor,
 	testAtCursor,
+	testAtCursorOrPrevious,
 	testCurrentFile,
 	testCurrentPackage,
 	testPrevious,
@@ -317,6 +318,13 @@
 	);
 
 	ctx.subscriptions.push(
+		vscode.commands.registerCommand('go.test.cursorOrPrevious', (args) => {
+			const goConfig = getGoConfig();
+			testAtCursorOrPrevious(goConfig, 'test', args);
+		})
+	);
+
+	ctx.subscriptions.push(
 		vscode.commands.registerCommand('go.subtest.cursor', (args) => {
 			const goConfig = getGoConfig();
 			subTestAtCursor(goConfig, args);
diff --git a/src/goModules.ts b/src/goModules.ts
index 341d041..a0b6f51 100644
--- a/src/goModules.ts
+++ b/src/goModules.ts
@@ -200,10 +200,15 @@
 	try {
 		const env = toolExecutionEnvironment();
 		const cwd = getWorkspaceFolderPath();
+		outputChannel.appendLine(`Running "${goRuntimePath} mod init ${moduleName}"`);
 		await execFile(goRuntimePath, ['mod', 'init', moduleName], { env, cwd });
+		outputChannel.appendLine('Module successfully initialized. You are ready to Go :)');
+		vscode.commands.executeCommand('vscode.open', vscode.Uri.file(path.join(cwd, 'go.mod')));
 	} catch (e) {
 		outputChannel.appendLine(e);
 		outputChannel.show();
-		vscode.window.showErrorMessage(`Error running 'go mod init ${moduleName}': See Go output channel for details`);
+		vscode.window.showErrorMessage(
+			`Error running "${goRuntimePath} mod init ${moduleName}": See Go output channel for details`
+		);
 	}
 }
diff --git a/src/goStatus.ts b/src/goStatus.ts
index 937c4ee..2968297 100644
--- a/src/goStatus.ts
+++ b/src/goStatus.ts
@@ -19,7 +19,7 @@
 } from './goLanguageServer';
 import { isGoFile } from './goMode';
 import { getModFolderPath, isModSupported } from './goModules';
-import { allToolsInformation } from './goTools';
+import { allToolsInformation } from './goToolsInformation';
 import { getGoVersion } from './util';
 
 export const outputChannel = vscode.window.createOutputChannel('Go');
diff --git a/src/goTest.ts b/src/goTest.ts
index e25c9d7..bbf95c8 100644
--- a/src/goTest.ts
+++ b/src/goTest.ts
@@ -31,50 +31,68 @@
 
 export type TestAtCursorCmd = 'debug' | 'test' | 'benchmark';
 
+class NotFoundError extends Error {}
+
+async function _testAtCursor(goConfig: vscode.WorkspaceConfiguration, cmd: TestAtCursorCmd, args: any) {
+	const editor = vscode.window.activeTextEditor;
+	if (!editor) {
+		throw new NotFoundError('No editor is active.');
+	}
+	if (!editor.document.fileName.endsWith('_test.go')) {
+		throw new NotFoundError('No tests found. Current file is not a test file.');
+	}
+
+	const getFunctions = cmd === 'benchmark' ? getBenchmarkFunctions : getTestFunctions;
+	const testFunctions = await getFunctions(editor.document, null);
+	// We use functionName if it was provided as argument
+	// Otherwise find any test function containing the cursor.
+	const testFunctionName =
+		args && args.functionName
+			? args.functionName
+			: testFunctions.filter((func) => func.range.contains(editor.selection.start)).map((el) => el.name)[0];
+	if (!testFunctionName) {
+		throw new NotFoundError('No test function found at cursor.');
+	}
+
+	await editor.document.save();
+
+	if (cmd === 'debug') {
+		return debugTestAtCursor(editor, testFunctionName, testFunctions, goConfig);
+	} else if (cmd === 'benchmark' || cmd === 'test') {
+		return runTestAtCursor(editor, testFunctionName, testFunctions, goConfig, cmd, args);
+	} else {
+		throw new Error(`Unsupported command: ${cmd}`);
+	}
+}
+
 /**
  * Executes the unit test at the primary cursor using `go test`. Output
  * is sent to the 'Go' channel.
  * @param goConfig Configuration for the Go extension.
- * @param cmd Whether the command is test , benchmark or debug.
+ * @param cmd Whether the command is test, benchmark, or debug.
  * @param args
  */
 export function testAtCursor(goConfig: vscode.WorkspaceConfiguration, cmd: TestAtCursorCmd, args: any) {
-	const editor = vscode.window.activeTextEditor;
-	if (!editor) {
-		vscode.window.showInformationMessage('No editor is active.');
-		return;
-	}
-	if (!editor.document.fileName.endsWith('_test.go')) {
-		vscode.window.showInformationMessage('No tests found. Current file is not a test file.');
-		return;
-	}
+	_testAtCursor(goConfig, cmd, args).catch((err) => {
+		if (err instanceof NotFoundError) {
+			vscode.window.showInformationMessage(err.message);
+		} else {
+			console.error(err);
+		}
+	});
+}
 
-	const getFunctions = cmd === 'benchmark' ? getBenchmarkFunctions : getTestFunctions;
-
-	editor.document.save().then(async () => {
-		try {
-			const testFunctions = await getFunctions(editor.document, null);
-			// We use functionName if it was provided as argument
-			// Otherwise find any test function containing the cursor.
-			const testFunctionName =
-				args && args.functionName
-					? args.functionName
-					: testFunctions
-							.filter((func) => func.range.contains(editor.selection.start))
-							.map((el) => el.name)[0];
-			if (!testFunctionName) {
-				vscode.window.showInformationMessage('No test function found at cursor.');
-				return;
-			}
-
-			if (cmd === 'debug') {
-				await debugTestAtCursor(editor, testFunctionName, testFunctions, goConfig);
-			} else if (cmd === 'benchmark' || cmd === 'test') {
-				await runTestAtCursor(editor, testFunctionName, testFunctions, goConfig, cmd, args);
-			} else {
-				throw new Error('Unsupported command.');
-			}
-		} catch (err) {
+/**
+ * Executes the unit test at the primary cursor if found, otherwise re-runs the previous test.
+ * @param goConfig Configuration for the Go extension.
+ * @param cmd Whether the command is test, benchmark, or debug.
+ * @param args
+ */
+export function testAtCursorOrPrevious(goConfig: vscode.WorkspaceConfiguration, cmd: TestAtCursorCmd, args: any) {
+	_testAtCursor(goConfig, cmd, args).catch((err) => {
+		if (err instanceof NotFoundError) {
+			testPrevious();
+		} else {
 			console.error(err);
 		}
 	});
diff --git a/src/goTools.ts b/src/goTools.ts
index 7f97d1a..cb68bd0 100644
--- a/src/goTools.ts
+++ b/src/goTools.ts
@@ -13,6 +13,7 @@
 import util = require('util');
 import { getFormatTool, usingCustomFormatTool } from './goFormat';
 import { goLiveErrorsEnabled } from './goLiveErrors';
+import { allToolsInformation } from './goToolsInformation';
 import { getBinPath, GoVersion } from './util';
 
 export interface Tool {
@@ -214,259 +215,20 @@
 	return !features || features['diagnostics'] === true;
 }
 
-export const allToolsInformation: { [key: string]: Tool } = {
-	'gocode': {
-		name: 'gocode',
-		importPath: 'github.com/mdempsky/gocode',
-		modulePath: 'github.com/mdempsky/gocode',
-		isImportant: true,
-		replacedByGopls: true,
-		description: 'Auto-completion, does not work with modules',
-		close: async (env: NodeJS.Dict<string>): Promise<string> => {
-			const toolBinPath = getBinPath('gocode');
-			if (!path.isAbsolute(toolBinPath)) {
-				return '';
-			}
-			try {
-				const execFile = util.promisify(cp.execFile);
-				const { stderr } = await execFile(toolBinPath, ['close'], { env, timeout: 10000 }); // give 10sec.
-				if (stderr.indexOf("rpc: can't find service Server.") > -1) {
-					return 'Installing gocode aborted as existing process cannot be closed. Please kill the running process for gocode and try again.';
-				}
-			} catch (err) {
-				// This may fail if gocode isn't already running.
-				console.log(`gocode close failed: ${err}`);
-			}
-			return '';
-		}
-	},
-	'gocode-gomod': {
-		name: 'gocode-gomod',
-		importPath: 'github.com/stamblerre/gocode',
-		modulePath: 'github.com/stamblerre/gocode',
-		isImportant: true,
-		replacedByGopls: true,
-		description: 'Auto-completion, works with modules',
-		minimumGoVersion: semver.coerce('1.11')
-	},
-	'gopkgs': {
-		name: 'gopkgs',
-		importPath: 'github.com/uudashr/gopkgs/v2/cmd/gopkgs',
-		modulePath: 'github.com/uudashr/gopkgs/v2',
-		replacedByGopls: false, // TODO(github.com/golang/vscode-go/issues/258): disable Add Import command.
-		isImportant: true,
-		description: 'Auto-completion of unimported packages & Add Import feature'
-	},
-	'go-outline': {
-		name: 'go-outline',
-		importPath: 'github.com/ramya-rao-a/go-outline',
-		modulePath: 'github.com/ramya-rao-a/go-outline',
-		replacedByGopls: false, // TODO(github.com/golang/vscode-go/issues/1020): replace with Gopls.
-		isImportant: true,
-		description: 'Go to symbol in file' // GoDocumentSymbolProvider, used by 'run test' codelens
-	},
-	'go-symbols': {
-		name: 'go-symbols',
-		importPath: 'github.com/acroca/go-symbols',
-		modulePath: 'github.com/acroca/go-symbols',
-		replacedByGopls: true,
-		isImportant: false,
-		description: 'Go to symbol in workspace'
-	},
-	'guru': {
-		name: 'guru',
-		importPath: 'golang.org/x/tools/cmd/guru',
-		modulePath: 'golang.org/x/tools',
-		replacedByGopls: true,
-		isImportant: false,
-		description: 'Find all references and Go to implementation of symbols'
-	},
-	'gorename': {
-		name: 'gorename',
-		importPath: 'golang.org/x/tools/cmd/gorename',
-		modulePath: 'golang.org/x/tools',
-		replacedByGopls: true,
-		isImportant: false,
-		description: 'Rename symbols'
-	},
-	'gomodifytags': {
-		name: 'gomodifytags',
-		importPath: 'github.com/fatih/gomodifytags',
-		modulePath: 'github.com/fatih/gomodifytags',
-		replacedByGopls: false,
-		isImportant: false,
-		description: 'Modify tags on structs'
-	},
-	'goplay': {
-		name: 'goplay',
-		importPath: 'github.com/haya14busa/goplay/cmd/goplay',
-		modulePath: 'github.com/haya14busa/goplay',
-		replacedByGopls: false,
-		isImportant: false,
-		description: 'The Go playground'
-	},
-	'impl': {
-		name: 'impl',
-		importPath: 'github.com/josharian/impl',
-		modulePath: 'github.com/josharian/impl',
-		replacedByGopls: false,
-		isImportant: false,
-		description: 'Stubs for interfaces'
-	},
-	'gotype-live': {
-		name: 'gotype-live',
-		importPath: 'github.com/tylerb/gotype-live',
-		modulePath: 'github.com/tylerb/gotype-live',
-		replacedByGopls: true, // TODO(github.com/golang/vscode-go/issues/1021): recommend users to turn off.
-		isImportant: false,
-		description: 'Show errors as you type'
-	},
-	'godef': {
-		name: 'godef',
-		importPath: 'github.com/rogpeppe/godef',
-		modulePath: 'github.com/rogpeppe/godef',
-		replacedByGopls: true,
-		isImportant: true,
-		description: 'Go to definition'
-	},
-	'gogetdoc': {
-		name: 'gogetdoc',
-		importPath: 'github.com/zmb3/gogetdoc',
-		modulePath: 'github.com/zmb3/gogetdoc',
-		replacedByGopls: true,
-		isImportant: true,
-		description: 'Go to definition & text shown on hover'
-	},
-	'gofumports': {
-		name: 'gofumports',
-		importPath: 'mvdan.cc/gofumpt/gofumports',
-		modulePath: 'mvdan.cc/gofumpt',
-		replacedByGopls: true,
-		isImportant: false,
-		description: 'Formatter'
-	},
-	'gofumpt': {
-		name: 'gofumpt',
-		importPath: 'mvdan.cc/gofumpt',
-		modulePath: 'mvdan.cc/gofumpt',
-		replacedByGopls: true,
-		isImportant: false,
-		description: 'Formatter'
-	},
-	'goimports': {
-		name: 'goimports',
-		importPath: 'golang.org/x/tools/cmd/goimports',
-		modulePath: 'golang.org/x/tools',
-		replacedByGopls: true,
-		isImportant: true,
-		description: 'Formatter'
-	},
-	'goreturns': {
-		name: 'goreturns',
-		importPath: 'github.com/sqs/goreturns',
-		modulePath: 'github.com/sqs/goreturns',
-		replacedByGopls: true,
-		isImportant: true,
-		description: 'Formatter'
-	},
-	'goformat': {
-		name: 'goformat',
-		importPath: 'winterdrache.de/goformat/goformat',
-		modulePath: 'winterdrache.de/goformat/goformat',
-		replacedByGopls: true,
-		isImportant: false,
-		description: 'Formatter'
-	},
-	'gotests': {
-		name: 'gotests',
-		importPath: 'github.com/cweill/gotests/gotests',
-		modulePath: 'github.com/cweill/gotests',
-		replacedByGopls: false,
-		isImportant: false,
-		description: 'Generate unit tests',
-		minimumGoVersion: semver.coerce('1.9')
-	},
-	// TODO(github.com/golang/vscode-go/issues/189): consider disabling lint when gopls is turned on.
-	'golint': {
-		name: 'golint',
-		importPath: 'golang.org/x/lint/golint',
-		modulePath: 'golang.org/x/lint',
-		replacedByGopls: false,
-		isImportant: false,
-		description: 'Linter',
-		minimumGoVersion: semver.coerce('1.9')
-	},
-	'staticcheck': {
-		name: 'staticcheck',
-		importPath: 'honnef.co/go/tools/cmd/staticcheck',
-		modulePath: 'honnef.co/go/tools',
-		replacedByGopls: false,
-		isImportant: true,
-		description: 'Linter'
-	},
-	'golangci-lint': {
-		name: 'golangci-lint',
-		importPath: 'github.com/golangci/golangci-lint/cmd/golangci-lint',
-		modulePath: 'github.com/golangci/golangci-lint',
-		replacedByGopls: false,
-		isImportant: true,
-		description: 'Linter'
-	},
-	'revive': {
-		name: 'revive',
-		importPath: 'github.com/mgechev/revive',
-		modulePath: 'github.com/mgechev/revive',
-		isImportant: true,
-		description: 'Linter'
-	},
-	'gopls': {
-		name: 'gopls',
-		importPath: 'golang.org/x/tools/gopls',
-		modulePath: 'golang.org/x/tools/gopls',
-		replacedByGopls: false, // lol
-		isImportant: true,
-		description: 'Language Server from Google',
-		usePrereleaseInPreviewMode: true,
-		minimumGoVersion: semver.coerce('1.12'),
-		latestVersion: semver.coerce('0.6.8'),
-		latestVersionTimestamp: moment('2021-03-17', 'YYYY-MM-DD'),
-		latestPrereleaseVersion: semver.coerce('0.6.8'),
-		latestPrereleaseVersionTimestamp: moment('2021-03-17', 'YYYY-MM-DD')
-	},
-	'dlv': {
-		name: 'dlv',
-		importPath: 'github.com/go-delve/delve/cmd/dlv',
-		modulePath: 'github.com/go-delve/delve',
-		replacedByGopls: false,
-		isImportant: true,
-		description: 'Go debugger (Delve)'
-	},
-	'dlv-dap': {
-		name: 'dlv-dap',
-		importPath: 'github.com/go-delve/delve/cmd/dlv',
-		modulePath: 'github.com/go-delve/delve',
-		replacedByGopls: false,
-		isImportant: false,
-		description: 'Go debugger (Delve built for DAP experiment)',
-		defaultVersion: 'master', // Always build from the master.
-		minimumGoVersion: semver.coerce('1.14'), // last 3 versions per delve policy
-		latestVersion: semver.parse('v1.6.2-0.20210521082917-a25d95bd236e'),
-		latestVersionTimestamp: moment('2021-05-21', 'YYYY-MM-DD')
-	},
-	'fillstruct': {
-		name: 'fillstruct',
-		importPath: 'github.com/davidrjenni/reftools/cmd/fillstruct',
-		modulePath: 'github.com/davidrjenni/reftools',
-		replacedByGopls: true,
-		isImportant: false,
-		description: 'Fill structs with defaults'
-	},
-	'godoctor': {
-		name: 'godoctor',
-		importPath: 'github.com/godoctor/godoctor',
-		modulePath: 'github.com/godoctor/godoctor',
-		replacedByGopls: true,
-		isImportant: false,
-		description: 'Extract to functions and variables'
+export const gocodeClose = async (env: NodeJS.Dict<string>): Promise<string> => {
+	const toolBinPath = getBinPath('gocode');
+	if (!path.isAbsolute(toolBinPath)) {
+		return '';
 	}
+	try {
+		const execFile = util.promisify(cp.execFile);
+		const { stderr } = await execFile(toolBinPath, ['close'], { env, timeout: 10000 }); // give 10sec.
+		if (stderr.indexOf("rpc: can't find service Server.") > -1) {
+			return 'Installing gocode aborted as existing process cannot be closed. Please kill the running process for gocode and try again.';
+		}
+	} catch (err) {
+		// This may fail if gocode isn't already running.
+		console.log(`gocode close failed: ${err}`);
+	}
+	return '';
 };
diff --git a/src/goToolsInformation.ts b/src/goToolsInformation.ts
new file mode 100644
index 0000000..401ee50
--- /dev/null
+++ b/src/goToolsInformation.ts
@@ -0,0 +1,246 @@
+// <!-- Everything below this line is generated. DO NOT EDIT. -->
+
+import moment = require('moment');
+import semver = require('semver');
+import { gocodeClose, Tool } from './goTools';
+
+export const allToolsInformation: { [key: string]: Tool } = {
+	'gocode': {
+		name: 'gocode',
+		importPath: 'github.com/mdempsky/gocode',
+		modulePath: 'github.com/mdempsky/gocode',
+		isImportant: true,
+		replacedByGopls: true,
+		description: 'Auto-completion, does not work with modules',
+		close: gocodeClose
+	},
+	'gocode-gomod': {
+		name: 'gocode-gomod',
+		importPath: 'github.com/stamblerre/gocode',
+		modulePath: 'github.com/stamblerre/gocode',
+		isImportant: true,
+		replacedByGopls: true,
+		description: 'Auto-completion, works with modules',
+		minimumGoVersion: semver.coerce('1.11')
+	},
+	'gopkgs': {
+		name: 'gopkgs',
+		importPath: 'github.com/uudashr/gopkgs/v2/cmd/gopkgs',
+		modulePath: 'github.com/uudashr/gopkgs/v2',
+		replacedByGopls: false, // TODO(github.com/golang/vscode-go/issues/258): disable Add Import command.
+		isImportant: true,
+		description: 'Auto-completion of unimported packages & Add Import feature'
+	},
+	'go-outline': {
+		name: 'go-outline',
+		importPath: 'github.com/ramya-rao-a/go-outline',
+		modulePath: 'github.com/ramya-rao-a/go-outline',
+		replacedByGopls: false, // TODO(github.com/golang/vscode-go/issues/1020): replace with Gopls.
+		isImportant: true,
+		description: 'Go to symbol in file' // GoDocumentSymbolProvider, used by 'run test' codelens
+	},
+	'go-symbols': {
+		name: 'go-symbols',
+		importPath: 'github.com/acroca/go-symbols',
+		modulePath: 'github.com/acroca/go-symbols',
+		replacedByGopls: true,
+		isImportant: false,
+		description: 'Go to symbol in workspace'
+	},
+	'guru': {
+		name: 'guru',
+		importPath: 'golang.org/x/tools/cmd/guru',
+		modulePath: 'golang.org/x/tools',
+		replacedByGopls: true,
+		isImportant: false,
+		description: 'Find all references and Go to implementation of symbols'
+	},
+	'gorename': {
+		name: 'gorename',
+		importPath: 'golang.org/x/tools/cmd/gorename',
+		modulePath: 'golang.org/x/tools',
+		replacedByGopls: true,
+		isImportant: false,
+		description: 'Rename symbols'
+	},
+	'gomodifytags': {
+		name: 'gomodifytags',
+		importPath: 'github.com/fatih/gomodifytags',
+		modulePath: 'github.com/fatih/gomodifytags',
+		replacedByGopls: false,
+		isImportant: false,
+		description: 'Modify tags on structs'
+	},
+	'goplay': {
+		name: 'goplay',
+		importPath: 'github.com/haya14busa/goplay/cmd/goplay',
+		modulePath: 'github.com/haya14busa/goplay',
+		replacedByGopls: false,
+		isImportant: false,
+		description: 'The Go playground'
+	},
+	'impl': {
+		name: 'impl',
+		importPath: 'github.com/josharian/impl',
+		modulePath: 'github.com/josharian/impl',
+		replacedByGopls: false,
+		isImportant: false,
+		description: 'Stubs for interfaces'
+	},
+	'gotype-live': {
+		name: 'gotype-live',
+		importPath: 'github.com/tylerb/gotype-live',
+		modulePath: 'github.com/tylerb/gotype-live',
+		replacedByGopls: true, // TODO(github.com/golang/vscode-go/issues/1021): recommend users to turn off.
+		isImportant: false,
+		description: 'Show errors as you type'
+	},
+	'godef': {
+		name: 'godef',
+		importPath: 'github.com/rogpeppe/godef',
+		modulePath: 'github.com/rogpeppe/godef',
+		replacedByGopls: true,
+		isImportant: true,
+		description: 'Go to definition'
+	},
+	'gogetdoc': {
+		name: 'gogetdoc',
+		importPath: 'github.com/zmb3/gogetdoc',
+		modulePath: 'github.com/zmb3/gogetdoc',
+		replacedByGopls: true,
+		isImportant: true,
+		description: 'Go to definition & text shown on hover'
+	},
+	'gofumports': {
+		name: 'gofumports',
+		importPath: 'mvdan.cc/gofumpt/gofumports',
+		modulePath: 'mvdan.cc/gofumpt',
+		replacedByGopls: true,
+		isImportant: false,
+		description: 'Formatter'
+	},
+	'gofumpt': {
+		name: 'gofumpt',
+		importPath: 'mvdan.cc/gofumpt',
+		modulePath: 'mvdan.cc/gofumpt',
+		replacedByGopls: true,
+		isImportant: false,
+		description: 'Formatter'
+	},
+	'goimports': {
+		name: 'goimports',
+		importPath: 'golang.org/x/tools/cmd/goimports',
+		modulePath: 'golang.org/x/tools',
+		replacedByGopls: true,
+		isImportant: true,
+		description: 'Formatter'
+	},
+	'goreturns': {
+		name: 'goreturns',
+		importPath: 'github.com/sqs/goreturns',
+		modulePath: 'github.com/sqs/goreturns',
+		replacedByGopls: true,
+		isImportant: true,
+		description: 'Formatter'
+	},
+	'goformat': {
+		name: 'goformat',
+		importPath: 'winterdrache.de/goformat/goformat',
+		modulePath: 'winterdrache.de/goformat/goformat',
+		replacedByGopls: true,
+		isImportant: false,
+		description: 'Formatter'
+	},
+	'gotests': {
+		name: 'gotests',
+		importPath: 'github.com/cweill/gotests/gotests',
+		modulePath: 'github.com/cweill/gotests',
+		replacedByGopls: false,
+		isImportant: false,
+		description: 'Generate unit tests',
+		minimumGoVersion: semver.coerce('1.9')
+	},
+	// TODO(github.com/golang/vscode-go/issues/189): consider disabling lint when gopls is turned on.
+	'golint': {
+		name: 'golint',
+		importPath: 'golang.org/x/lint/golint',
+		modulePath: 'golang.org/x/lint',
+		replacedByGopls: false,
+		isImportant: false,
+		description: 'Linter',
+		minimumGoVersion: semver.coerce('1.9')
+	},
+	'staticcheck': {
+		name: 'staticcheck',
+		importPath: 'honnef.co/go/tools/cmd/staticcheck',
+		modulePath: 'honnef.co/go/tools',
+		replacedByGopls: false,
+		isImportant: true,
+		description: 'Linter'
+	},
+	'golangci-lint': {
+		name: 'golangci-lint',
+		importPath: 'github.com/golangci/golangci-lint/cmd/golangci-lint',
+		modulePath: 'github.com/golangci/golangci-lint',
+		replacedByGopls: false,
+		isImportant: true,
+		description: 'Linter'
+	},
+	'revive': {
+		name: 'revive',
+		importPath: 'github.com/mgechev/revive',
+		modulePath: 'github.com/mgechev/revive',
+		isImportant: true,
+		description: 'Linter'
+	},
+	'gopls': {
+		name: 'gopls',
+		importPath: 'golang.org/x/tools/gopls',
+		modulePath: 'golang.org/x/tools/gopls',
+		replacedByGopls: false, // lol
+		isImportant: true,
+		description: 'Language Server from Google',
+		usePrereleaseInPreviewMode: true,
+		minimumGoVersion: semver.coerce('1.12'),
+		latestVersion: semver.parse('v0.7.0'),
+		latestVersionTimestamp: moment('2021-06-08', 'YYYY-MM-DD'),
+		latestPrereleaseVersion: semver.parse('v0.7.0'),
+		latestPrereleaseVersionTimestamp: moment('2021-06-08', 'YYYY-MM-DD')
+	},
+	'dlv': {
+		name: 'dlv',
+		importPath: 'github.com/go-delve/delve/cmd/dlv',
+		modulePath: 'github.com/go-delve/delve',
+		replacedByGopls: false,
+		isImportant: true,
+		description: 'Go debugger (Delve)'
+	},
+	'dlv-dap': {
+		name: 'dlv-dap',
+		importPath: 'github.com/go-delve/delve/cmd/dlv',
+		modulePath: 'github.com/go-delve/delve',
+		replacedByGopls: false,
+		isImportant: false,
+		description: 'Go debugger (Delve built for DAP experiment)',
+		defaultVersion: 'master', // Always build from the master.
+		minimumGoVersion: semver.coerce('1.14'), // last 3 versions per delve policy
+		latestVersion: semver.parse('v1.6.2-0.20210604153550-7a3faca71f7e'),
+		latestVersionTimestamp: moment('2021-06-04', 'YYYY-MM-DD')
+	},
+	'fillstruct': {
+		name: 'fillstruct',
+		importPath: 'github.com/davidrjenni/reftools/cmd/fillstruct',
+		modulePath: 'github.com/davidrjenni/reftools',
+		replacedByGopls: true,
+		isImportant: false,
+		description: 'Fill structs with defaults'
+	},
+	'godoctor': {
+		name: 'godoctor',
+		importPath: 'github.com/godoctor/godoctor',
+		modulePath: 'github.com/godoctor/godoctor',
+		replacedByGopls: true,
+		isImportant: false,
+		description: 'Extract to functions and variables'
+	}
+};
diff --git a/test/integration/config.test.ts b/test/integration/config.test.ts
index 1d7eee4..525b42d 100644
--- a/test/integration/config.test.ts
+++ b/test/integration/config.test.ts
@@ -9,7 +9,7 @@
 
 import * as assert from 'assert';
 import { Configuration } from '../../src/config';
-import vscode = require('vscode');
+import { MockCfg } from '../mocks/MockCfg';
 
 suite('GoConfiguration Tests', () => {
 	function check(trusted: boolean, workspaceConfig: { [key: string]: any }, key: string, expected: any) {
@@ -68,48 +68,3 @@
 		checkGopls(false, { env: { GOBIN: 'foo' } }, 'env', { GOBIN: 'foo' });
 	});
 });
-
-// tslint:disable: no-any
-class MockCfg implements vscode.WorkspaceConfiguration {
-	private map: Map<string, any>;
-	private wrapped: vscode.WorkspaceConfiguration;
-
-	constructor(workspaceSettings: { [key: string]: any } = {}) {
-		// getter
-		Object.defineProperties(this, Object.getOwnPropertyDescriptors(workspaceSettings));
-		this.map = new Map<string, any>(Object.entries(workspaceSettings));
-		this.wrapped = vscode.workspace.getConfiguration('go'); // intentionally using vscode API directly.
-	}
-
-	// tslint:disable: no-any
-	public get(section: string, defaultValue?: any): any {
-		if (this.map.has(section)) {
-			return this.map.get(section);
-		}
-		return this.wrapped.get(section, defaultValue);
-	}
-
-	public has(section: string): boolean {
-		if (this.map.has(section)) {
-			return true;
-		}
-		return this.wrapped.has(section);
-	}
-
-	public inspect<T>(section: string) {
-		const i = this.wrapped.inspect<T>(section);
-		if (this.map.has(section)) {
-			i.workspaceValue = this.map.get(section);
-		}
-		return i;
-	}
-
-	public update(
-		section: string,
-		value: any,
-		configurationTarget?: boolean | vscode.ConfigurationTarget,
-		overrideInLanguage?: boolean
-	): Thenable<void> {
-		throw new Error('Method not implemented.');
-	}
-}
diff --git a/test/integration/goDebug.test.ts b/test/integration/goDebug.test.ts
index 5ea7150..c73afd9 100644
--- a/test/integration/goDebug.test.ts
+++ b/test/integration/goDebug.test.ts
@@ -308,7 +308,7 @@
 		name: 'Attach',
 		type: 'go',
 		request: 'attach',
-		mode: 'remote',
+		mode: 'remote', // This implies debugAdapter = legacy.
 		host: '127.0.0.1',
 		port: 3456
 	};
@@ -475,10 +475,9 @@
 				this.skip(); // not working in dlv-dap.
 			}
 
-			if (isDlvDap) {
-				const config = { name: 'Launch', type: 'go', request: 'launch', program: DATA_ROOT };
-				await initializeDebugConfig(config);
-			}
+			// fake config that will be used to initialize fixtures.
+			const config = { name: 'Launch', type: 'go', request: 'launch', program: DATA_ROOT };
+			await initializeDebugConfig(config);
 
 			try {
 				await dc.send('illegal_request');
@@ -491,10 +490,8 @@
 
 	suite('initialize', () => {
 		test('should return supported features', async () => {
-			if (isDlvDap) {
-				const config = { name: 'Launch', type: 'go', request: 'launch', program: DATA_ROOT };
-				await initializeDebugConfig(config);
-			}
+			const config = { name: 'Launch', type: 'go', request: 'launch', program: DATA_ROOT };
+			await initializeDebugConfig(config);
 			await dc.initializeRequest().then((response) => {
 				response.body = response.body || {};
 				assert.strictEqual(response.body.supportsConditionalBreakpoints, true);
@@ -511,10 +508,8 @@
 				this.skip(); // not working in dlv-dap.
 			}
 
-			if (isDlvDap) {
-				const config = { name: 'Launch', type: 'go', request: 'launch', program: DATA_ROOT };
-				await initializeDebugConfig(config);
-			}
+			const config = { name: 'Launch', type: 'go', request: 'launch', program: DATA_ROOT };
+			await initializeDebugConfig(config);
 			try {
 				await dc.initializeRequest({
 					adapterID: 'mock',
@@ -1599,10 +1594,9 @@
 				assert.fail(`debug output ${OUTPUT} wasn't built: ${e}`);
 			}
 
-			// It's possible dlv-dap doesn't respond. So, don't wait.
-			dc.disconnectRequest({ restart: false });
-			await sleep(10);
-			dlvDapAdapter.dispose(1);
+			// Skip the proper disconnect sequence started with a disconnect request.
+
+			await dlvDapAdapter.dispose(1);
 			dc = undefined;
 			await sleep(100); // allow dlv to respond and finish cleanup.
 			let stat: fs.Stats = null;
@@ -1809,13 +1803,13 @@
 				logDest
 			};
 
+			await initializeDebugConfig(config);
 			try {
-				await initializeDebugConfig(config);
+				await dc.initializeRequest();
+				assert.fail('dlv dap started normally, wanted the invalid logDest to cause failure');
 			} catch (error) {
 				assert(error?.message.includes(wantedErrorMessage), `unexpected error: ${error}`);
-				return;
 			}
-			assert.fail('dlv dap started normally, wanted the invalid logDest to cause failure');
 		}
 		test('relative path as logDest triggers an error', async function () {
 			if (!isDlvDap || process.platform === 'win32') this.skip();
@@ -1991,9 +1985,13 @@
 		if (isDlvDap) {
 			config['debugAdapter'] = 'dlv-dap';
 			// Log the output for easier test debugging.
-			config['logOutput'] = 'dap';
+			config['logOutput'] = 'dap,debugger';
 			config['showLog'] = true;
 			config['trace'] = 'verbose';
+		} else {
+			config['debugAdapter'] = 'legacy';
+			// be explicit and prevent resolveDebugConfiguration from picking
+			// a default debugAdapter for us.
 		}
 
 		// Give each test a distinct debug binary. If a previous test
@@ -2034,7 +2032,6 @@
 class DelveDAPDebugAdapterOnSocket extends proxy.DelveDAPOutputAdapter {
 	static async create(config: DebugConfiguration) {
 		const d = new DelveDAPDebugAdapterOnSocket(config);
-		await d.startAndConnectToServer();
 		return d;
 	}
 
diff --git a/test/integration/goDebugConfiguration.test.ts b/test/integration/goDebugConfiguration.test.ts
index d2c112d..508c0d4 100644
--- a/test/integration/goDebugConfiguration.test.ts
+++ b/test/integration/goDebugConfiguration.test.ts
@@ -12,6 +12,8 @@
 import { updateGoVarsFromConfig } from '../../src/goInstallTools';
 import { rmdirRecursive } from '../../src/util';
 import goEnv = require('../../src/goEnv');
+import { isInPreviewMode } from '../../src/goLanguageServer';
+import { MockCfg } from '../mocks/MockCfg';
 
 suite('Debug Environment Variable Merge Test', () => {
 	const debugConfigProvider = new GoDebugConfigurationProvider();
@@ -200,6 +202,8 @@
 			assert.strictEqual(filledResult.dlvToolPath, emptyResult.dlvToolPath);
 			assert.strictEqual(filledResult.apiVersion, emptyResult.apiVersion);
 			assert.strictEqual(filledResult.showGlobalVariables, emptyResult.showGlobalVariables);
+			assert.strictEqual(filledResult.debugAdapter, emptyResult.debugAdapter);
+			assert.strictEqual(filledResult.substitutePath, emptyResult.substitutePath);
 		});
 
 		test('delve config in settings.json is added to debug config', async () => {
@@ -208,21 +212,21 @@
 			// When this expected behavior changes, this test can be updated.
 
 			// Run resolveDebugConfiguration with the default workspace settings.
-			const goConfig = Object.create(getGoConfig(), {
+			const goConfig = new MockCfg({
 				delveConfig: {
-					value: {
-						dlvLoadConfig: {
-							followPointers: false,
-							maxVariableRecurse: 3,
-							maxStringLen: 32,
-							maxArrayValues: 32,
-							maxStructFields: 5
-						},
-						apiVersion: 1,
-						showGlobalVariables: true
-					}
+					dlvLoadConfig: {
+						followPointers: false,
+						maxVariableRecurse: 3,
+						maxStringLen: 32,
+						maxArrayValues: 32,
+						maxStructFields: 5
+					},
+					apiVersion: 1,
+					showGlobalVariables: true,
+					debugAdapter: 'dlv-dap',
+					substitutePath: [{ from: 'hello', to: 'goodbye' }]
 				}
-			}) as vscode.WorkspaceConfiguration;
+			});
 			sinon.stub(config, 'getGoConfig').returns(goConfig);
 
 			const cfg = {
@@ -236,6 +240,10 @@
 			const result = await debugConfigProvider.resolveDebugConfiguration(undefined, cfg);
 			assert.strictEqual(result.apiVersion, 1);
 			assert.strictEqual(result.showGlobalVariables, true);
+			assert.strictEqual(result.debugAdapter, 'dlv-dap');
+			assert.strictEqual(result.substitutePath.length, 1);
+			assert.strictEqual(result.substitutePath[0].from, 'hello');
+			assert.strictEqual(result.substitutePath[0].to, 'goodbye');
 			const dlvLoadConfig = result.dlvLoadConfig;
 			assert.strictEqual(dlvLoadConfig.followPointers, false);
 			assert.strictEqual(dlvLoadConfig.maxVariableRecurse, 3);
@@ -250,24 +258,24 @@
 			// When this expected behavior changes, this test can be updated.
 
 			// Run resolveDebugConfiguration with the default workspace settings.
-			const goConfig = Object.create(getGoConfig(), {
+			const goConfig = new MockCfg({
 				delveConfig: {
-					value: {
-						dlvLoadConfig: {
-							followPointers: false,
-							maxVariableRecurse: 3,
-							maxStringLen: 32,
-							maxArrayValues: 32,
-							maxStructFields: 5
-						},
-						apiVersion: 1,
-						showGlobalVariables: true
-					}
+					dlvLoadConfig: {
+						followPointers: false,
+						maxVariableRecurse: 3,
+						maxStringLen: 32,
+						maxArrayValues: 32,
+						maxStructFields: 5
+					},
+					apiVersion: 1,
+					showGlobalVariables: true,
+					debugAdapter: 'dlv-dap',
+					substitutePath: [{ from: 'hello', to: 'goodbye' }]
 				}
-			}) as vscode.WorkspaceConfiguration;
+			});
 			sinon.stub(config, 'getGoConfig').returns(goConfig);
 
-			const cfg = {
+			const cfg: vscode.DebugConfiguration = {
 				name: 'Launch',
 				type: 'go',
 				request: 'launch',
@@ -281,12 +289,16 @@
 					maxStringLen: 128,
 					maxArrayValues: 128,
 					maxStructFields: -1
-				}
+				},
+				debugAdapter: 'legacy',
+				substitutePath: []
 			};
 
 			const result = await debugConfigProvider.resolveDebugConfiguration(undefined, cfg);
 			assert.strictEqual(result.apiVersion, 2);
 			assert.strictEqual(result.showGlobalVariables, false);
+			assert.strictEqual(result.debugAdapter, 'legacy');
+			assert.strictEqual(result.substitutePath.length, 0);
 			const dlvLoadConfig = result.dlvLoadConfig;
 			assert.strictEqual(dlvLoadConfig.followPointers, true);
 			assert.strictEqual(dlvLoadConfig.maxVariableRecurse, 6);
@@ -424,6 +436,112 @@
 	});
 });
 
+suite('Debug Configuration Converts Relative Paths', () => {
+	const debugConfigProvider = new GoDebugConfigurationProvider();
+
+	function debugConfig(adapter: string) {
+		return {
+			name: 'Launch',
+			type: 'go',
+			request: 'launch',
+			mode: 'auto',
+			debugAdapter: adapter,
+			program: path.join('foo', 'bar.go'),
+			cwd: '.',
+			output: 'debug'
+		};
+	}
+
+	test('resolve relative paths with workspace root in dlv-dap mode', () => {
+		const config = debugConfig('dlv-dap');
+		const workspaceFolder = {
+			uri: vscode.Uri.file(os.tmpdir()),
+			name: 'test',
+			index: 0
+		};
+		const { program, cwd, output } = debugConfigProvider.resolveDebugConfigurationWithSubstitutedVariables(
+			workspaceFolder,
+			config
+		);
+		assert.deepStrictEqual(
+			{ program, cwd, output },
+			{
+				program: path.join(os.tmpdir(), 'foo/bar.go'),
+				cwd: os.tmpdir(),
+				output: path.join(os.tmpdir(), 'debug')
+			}
+		);
+	});
+
+	test('empty, undefined paths are not affected', () => {
+		const config = debugConfig('dlv-dap');
+		config.program = undefined;
+		config.cwd = '';
+		delete config.output;
+
+		const workspaceFolder = {
+			uri: vscode.Uri.file(os.tmpdir()),
+			name: 'test',
+			index: 0
+		};
+		const { program, cwd, output } = debugConfigProvider.resolveDebugConfigurationWithSubstitutedVariables(
+			workspaceFolder,
+			config
+		);
+		assert.deepStrictEqual(
+			{ program, cwd, output },
+			{
+				program: undefined,
+				cwd: '',
+				output: undefined
+			}
+		);
+	});
+
+	test('disallow relative paths with no workspace root', () => {
+		const config = debugConfig('dlv-dap');
+		const got = debugConfigProvider.resolveDebugConfigurationWithSubstitutedVariables(undefined, config);
+		assert.strictEqual(got, null);
+	});
+
+	test('do not affect relative paths (workspace) in legacy mode', () => {
+		const config = debugConfig('legacy');
+		const workspaceFolder = {
+			uri: vscode.Uri.file(os.tmpdir()),
+			name: 'test',
+			index: 0
+		};
+		const { program, cwd, output } = debugConfigProvider.resolveDebugConfigurationWithSubstitutedVariables(
+			workspaceFolder,
+			config
+		);
+		assert.deepStrictEqual(
+			{ program, cwd, output },
+			{
+				program: path.join('foo', 'bar.go'),
+				cwd: '.',
+				output: 'debug'
+			}
+		);
+	});
+
+	test('do not affect relative paths (no workspace) in legacy mode', () => {
+		const config = debugConfig('legacy');
+		const { program, cwd, output } = debugConfigProvider.resolveDebugConfigurationWithSubstitutedVariables(
+			undefined,
+			config
+		);
+		assert.deepStrictEqual(
+			{ program, cwd, output },
+			{
+				program: path.join('foo', 'bar.go'),
+				cwd: '.',
+				output: 'debug'
+			}
+		);
+	});
+});
+
 suite('Debug Configuration Auto Mode', () => {
 	const debugConfigProvider = new GoDebugConfigurationProvider();
 	test('resolve auto to debug with non-test file', () => {
@@ -454,3 +572,54 @@
 		assert.strictEqual(config.program, '/path/to');
 	});
 });
+
+suite('Debug Configuration Default DebugAdapter', () => {
+	const debugConfigProvider = new GoDebugConfigurationProvider();
+	test(`default debugAdapter when isInPreviewMode=${isInPreviewMode()} should be 'dlv-dap'`, () => {
+		const config = {
+			name: 'Launch',
+			type: 'go',
+			request: 'launch',
+			mode: 'auto',
+			program: '/path/to/main.go'
+		};
+
+		debugConfigProvider.resolveDebugConfiguration(undefined, config);
+		const resolvedConfig = config as any;
+		const want = isInPreviewMode() ? 'dlv-dap' : 'legacy';
+		assert.strictEqual(resolvedConfig['debugAdapter'], want);
+	});
+
+	test("default debugAdapter for remote mode should be always 'legacy'", () => {
+		const config = {
+			name: 'Attach',
+			type: 'go',
+			request: 'attach',
+			mode: 'remote',
+			program: '/path/to/main_test.go',
+			cwd: '/path'
+		};
+
+		const want = 'legacy'; // remote mode works only with legacy mode.
+		debugConfigProvider.resolveDebugConfiguration(undefined, config);
+		const resolvedConfig = config as any;
+		assert.strictEqual(resolvedConfig['debugAdapter'], want);
+	});
+
+	test('debugAdapter=dlv-dap should be ignored for remote mode', () => {
+		const config = {
+			name: 'Attach',
+			type: 'go',
+			request: 'attach',
+			mode: 'remote',
+			debugAdapter: 'dlv-dap',
+			program: '/path/to/main_test.go',
+			cwd: '/path'
+		};
+
+		const want = 'legacy'; // remote mode works only with legacy mode.
+		debugConfigProvider.resolveDebugConfiguration(undefined, config);
+		const resolvedConfig = config as any;
+		assert.strictEqual(resolvedConfig['debugAdapter'], want);
+	});
+});
diff --git a/test/integration/install.test.ts b/test/integration/install.test.ts
index 85dd96c..c4875f5 100644
--- a/test/integration/install.test.ts
+++ b/test/integration/install.test.ts
@@ -8,7 +8,7 @@
 import * as assert from 'assert';
 import * as config from '../../src/config';
 import { inspectGoToolVersion, installTools } from '../../src/goInstallTools';
-import { allToolsInformation, getConfiguredTools, getTool, getToolAtVersion } from '../../src/goTools';
+import { getConfiguredTools, getTool, getToolAtVersion } from '../../src/goTools';
 import { getBinPath, getGoVersion, GoVersion, rmdirRecursive } from '../../src/util';
 import { correctBinname } from '../../src/utils/pathUtils';
 import cp = require('child_process');
@@ -20,6 +20,7 @@
 import util = require('util');
 import vscode = require('vscode');
 import { isInPreviewMode } from '../../src/goLanguageServer';
+import { allToolsInformation } from '../../src/goToolsInformation';
 
 interface installationTestCase {
 	name: string;
diff --git a/test/mocks/MockCfg.ts b/test/mocks/MockCfg.ts
new file mode 100644
index 0000000..99a8735
--- /dev/null
+++ b/test/mocks/MockCfg.ts
@@ -0,0 +1,63 @@
+/* eslint-disable @typescript-eslint/no-unused-vars */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/*---------------------------------------------------------
+ * Copyright 2021 The Go Authors. All rights reserved.
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
+ *--------------------------------------------------------*/
+'use strict';
+import vscode = require('vscode');
+
+// tslint:disable: no-any
+export class MockCfg implements vscode.WorkspaceConfiguration {
+	private map: Map<string, any>;
+	private wrapped: vscode.WorkspaceConfiguration;
+
+	constructor(workspaceSettings: { [key: string]: any } = {}) {
+		// getter
+		Object.defineProperties(this, Object.getOwnPropertyDescriptors(workspaceSettings));
+		this.map = new Map<string, any>(Object.entries(workspaceSettings));
+		this.wrapped = vscode.workspace.getConfiguration('go'); // intentionally using vscode API directly.
+	}
+
+	// tslint:disable: no-any
+	public get(section: string, defaultValue?: any): any {
+		if (this.map.has(section)) {
+			return this.map.get(section);
+		}
+		return this.wrapped.get(section, defaultValue);
+	}
+
+	public has(section: string): boolean {
+		if (this.map.has(section)) {
+			return true;
+		}
+		return this.wrapped.has(section);
+	}
+
+	public inspect<T>(section: string) {
+		const i = this.wrapped.inspect<T>(section);
+		const part = section.split('.');
+		if (this.map.has(part[0])) {
+			let v: any = this.map.get(part[0]);
+			for (let i = 1; i < part.length; i++) {
+				if (Object.prototype.hasOwnProperty.call(v, part[i])) {
+					v = v[part[i]];
+				} else {
+					v = undefined;
+					break;
+				}
+			}
+			i.workspaceValue = v;
+		}
+		return i;
+	}
+
+	public update(
+		section: string,
+		value: any,
+		configurationTarget?: boolean | vscode.ConfigurationTarget,
+		overrideInLanguage?: boolean
+	): Thenable<void> {
+		throw new Error('Method not implemented.');
+	}
+}
diff --git a/tools/allTools.ts.in b/tools/allTools.ts.in
new file mode 100644
index 0000000..33d46b7
--- /dev/null
+++ b/tools/allTools.ts.in
@@ -0,0 +1,244 @@
+import moment = require('moment');
+import semver = require('semver');
+import { gocodeClose, Tool } from './goTools';
+
+export const allToolsInformation: { [key: string]: Tool } = {
+	'gocode': {
+		name: 'gocode',
+		importPath: 'github.com/mdempsky/gocode',
+		modulePath: 'github.com/mdempsky/gocode',
+		isImportant: true,
+		replacedByGopls: true,
+		description: 'Auto-completion, does not work with modules',
+		close: gocodeClose
+	},
+	'gocode-gomod': {
+		name: 'gocode-gomod',
+		importPath: 'github.com/stamblerre/gocode',
+		modulePath: 'github.com/stamblerre/gocode',
+		isImportant: true,
+		replacedByGopls: true,
+		description: 'Auto-completion, works with modules',
+		minimumGoVersion: semver.coerce('1.11')
+	},
+	'gopkgs': {
+		name: 'gopkgs',
+		importPath: 'github.com/uudashr/gopkgs/v2/cmd/gopkgs',
+		modulePath: 'github.com/uudashr/gopkgs/v2',
+		replacedByGopls: false, // TODO(github.com/golang/vscode-go/issues/258): disable Add Import command.
+		isImportant: true,
+		description: 'Auto-completion of unimported packages & Add Import feature'
+	},
+	'go-outline': {
+		name: 'go-outline',
+		importPath: 'github.com/ramya-rao-a/go-outline',
+		modulePath: 'github.com/ramya-rao-a/go-outline',
+		replacedByGopls: false, // TODO(github.com/golang/vscode-go/issues/1020): replace with Gopls.
+		isImportant: true,
+		description: 'Go to symbol in file' // GoDocumentSymbolProvider, used by 'run test' codelens
+	},
+	'go-symbols': {
+		name: 'go-symbols',
+		importPath: 'github.com/acroca/go-symbols',
+		modulePath: 'github.com/acroca/go-symbols',
+		replacedByGopls: true,
+		isImportant: false,
+		description: 'Go to symbol in workspace'
+	},
+	'guru': {
+		name: 'guru',
+		importPath: 'golang.org/x/tools/cmd/guru',
+		modulePath: 'golang.org/x/tools',
+		replacedByGopls: true,
+		isImportant: false,
+		description: 'Find all references and Go to implementation of symbols'
+	},
+	'gorename': {
+		name: 'gorename',
+		importPath: 'golang.org/x/tools/cmd/gorename',
+		modulePath: 'golang.org/x/tools',
+		replacedByGopls: true,
+		isImportant: false,
+		description: 'Rename symbols'
+	},
+	'gomodifytags': {
+		name: 'gomodifytags',
+		importPath: 'github.com/fatih/gomodifytags',
+		modulePath: 'github.com/fatih/gomodifytags',
+		replacedByGopls: false,
+		isImportant: false,
+		description: 'Modify tags on structs'
+	},
+	'goplay': {
+		name: 'goplay',
+		importPath: 'github.com/haya14busa/goplay/cmd/goplay',
+		modulePath: 'github.com/haya14busa/goplay',
+		replacedByGopls: false,
+		isImportant: false,
+		description: 'The Go playground'
+	},
+	'impl': {
+		name: 'impl',
+		importPath: 'github.com/josharian/impl',
+		modulePath: 'github.com/josharian/impl',
+		replacedByGopls: false,
+		isImportant: false,
+		description: 'Stubs for interfaces'
+	},
+	'gotype-live': {
+		name: 'gotype-live',
+		importPath: 'github.com/tylerb/gotype-live',
+		modulePath: 'github.com/tylerb/gotype-live',
+		replacedByGopls: true, // TODO(github.com/golang/vscode-go/issues/1021): recommend users to turn off.
+		isImportant: false,
+		description: 'Show errors as you type'
+	},
+	'godef': {
+		name: 'godef',
+		importPath: 'github.com/rogpeppe/godef',
+		modulePath: 'github.com/rogpeppe/godef',
+		replacedByGopls: true,
+		isImportant: true,
+		description: 'Go to definition'
+	},
+	'gogetdoc': {
+		name: 'gogetdoc',
+		importPath: 'github.com/zmb3/gogetdoc',
+		modulePath: 'github.com/zmb3/gogetdoc',
+		replacedByGopls: true,
+		isImportant: true,
+		description: 'Go to definition & text shown on hover'
+	},
+	'gofumports': {
+		name: 'gofumports',
+		importPath: 'mvdan.cc/gofumpt/gofumports',
+		modulePath: 'mvdan.cc/gofumpt',
+		replacedByGopls: true,
+		isImportant: false,
+		description: 'Formatter'
+	},
+	'gofumpt': {
+		name: 'gofumpt',
+		importPath: 'mvdan.cc/gofumpt',
+		modulePath: 'mvdan.cc/gofumpt',
+		replacedByGopls: true,
+		isImportant: false,
+		description: 'Formatter'
+	},
+	'goimports': {
+		name: 'goimports',
+		importPath: 'golang.org/x/tools/cmd/goimports',
+		modulePath: 'golang.org/x/tools',
+		replacedByGopls: true,
+		isImportant: true,
+		description: 'Formatter'
+	},
+	'goreturns': {
+		name: 'goreturns',
+		importPath: 'github.com/sqs/goreturns',
+		modulePath: 'github.com/sqs/goreturns',
+		replacedByGopls: true,
+		isImportant: true,
+		description: 'Formatter'
+	},
+	'goformat': {
+		name: 'goformat',
+		importPath: 'winterdrache.de/goformat/goformat',
+		modulePath: 'winterdrache.de/goformat/goformat',
+		replacedByGopls: true,
+		isImportant: false,
+		description: 'Formatter'
+	},
+	'gotests': {
+		name: 'gotests',
+		importPath: 'github.com/cweill/gotests/gotests',
+		modulePath: 'github.com/cweill/gotests',
+		replacedByGopls: false,
+		isImportant: false,
+		description: 'Generate unit tests',
+		minimumGoVersion: semver.coerce('1.9')
+	},
+	// TODO(github.com/golang/vscode-go/issues/189): consider disabling lint when gopls is turned on.
+	'golint': {
+		name: 'golint',
+		importPath: 'golang.org/x/lint/golint',
+		modulePath: 'golang.org/x/lint',
+		replacedByGopls: false,
+		isImportant: false,
+		description: 'Linter',
+		minimumGoVersion: semver.coerce('1.9')
+	},
+	'staticcheck': {
+		name: 'staticcheck',
+		importPath: 'honnef.co/go/tools/cmd/staticcheck',
+		modulePath: 'honnef.co/go/tools',
+		replacedByGopls: false,
+		isImportant: true,
+		description: 'Linter'
+	},
+	'golangci-lint': {
+		name: 'golangci-lint',
+		importPath: 'github.com/golangci/golangci-lint/cmd/golangci-lint',
+		modulePath: 'github.com/golangci/golangci-lint',
+		replacedByGopls: false,
+		isImportant: true,
+		description: 'Linter'
+	},
+	'revive': {
+		name: 'revive',
+		importPath: 'github.com/mgechev/revive',
+		modulePath: 'github.com/mgechev/revive',
+		isImportant: true,
+		description: 'Linter'
+	},
+	'gopls': {
+		name: 'gopls',
+		importPath: 'golang.org/x/tools/gopls',
+		modulePath: 'golang.org/x/tools/gopls',
+		replacedByGopls: false, // lol
+		isImportant: true,
+		description: 'Language Server from Google',
+		usePrereleaseInPreviewMode: true,
+		minimumGoVersion: semver.coerce('1.12'),
+		latestVersion: semver.parse('%s'),
+		latestVersionTimestamp: moment('%s', 'YYYY-MM-DD'),
+		latestPrereleaseVersion: semver.parse('%s'),
+		latestPrereleaseVersionTimestamp: moment('%s', 'YYYY-MM-DD')
+	},
+	'dlv': {
+		name: 'dlv',
+		importPath: 'github.com/go-delve/delve/cmd/dlv',
+		modulePath: 'github.com/go-delve/delve',
+		replacedByGopls: false,
+		isImportant: true,
+		description: 'Go debugger (Delve)'
+	},
+	'dlv-dap': {
+		name: 'dlv-dap',
+		importPath: 'github.com/go-delve/delve/cmd/dlv',
+		modulePath: 'github.com/go-delve/delve',
+		replacedByGopls: false,
+		isImportant: false,
+		description: 'Go debugger (Delve built for DAP experiment)',
+		defaultVersion: 'master', // Always build from the master.
+		minimumGoVersion: semver.coerce('1.14'), // last 3 versions per delve policy
+		latestVersion: semver.parse('%s'),
+		latestVersionTimestamp: moment('%s', 'YYYY-MM-DD')
+	},
+	'fillstruct': {
+		name: 'fillstruct',
+		importPath: 'github.com/davidrjenni/reftools/cmd/fillstruct',
+		modulePath: 'github.com/davidrjenni/reftools',
+		replacedByGopls: true,
+		isImportant: false,
+		description: 'Fill structs with defaults'
+	},
+	'godoctor': {
+		name: 'godoctor',
+		importPath: 'github.com/godoctor/godoctor',
+		modulePath: 'github.com/godoctor/godoctor',
+		replacedByGopls: true,
+		isImportant: false,
+		description: 'Extract to functions and variables'
+	}
+};
\ No newline at end of file
diff --git a/tools/generate.go b/tools/generate.go
index 9999738..87e9379 100644
--- a/tools/generate.go
+++ b/tools/generate.go
@@ -20,6 +20,7 @@
 	"io/ioutil"
 	"log"
 	"os"
+	"os/exec"
 	"path/filepath"
 	"sort"
 	"strings"
@@ -28,8 +29,9 @@
 )
 
 var (
-	writeFlag               = flag.Bool("w", true, "Write new file contents to disk.")
-	updateGoplsSettingsFlag = flag.Bool("gopls", false, "Update gopls settings in package.json. This is disabled by default because 'jq' tool is needed for generation.")
+	writeFlag                    = flag.Bool("w", true, "Write new file contents to disk.")
+	updateGoplsSettingsFlag      = flag.Bool("gopls", false, "Update gopls settings in package.json. This is disabled by default because 'jq' tool is needed for generation.")
+	updateLatestToolVersionsFlag = flag.Bool("tools", false, "Update the latest versions of tools in src/src/goToolsInformation.ts. This is disabled by default because the latest versions may change frequently and should not block a release.")
 
 	debugFlag = flag.Bool("debug", false, "If true, enable extra logging and skip deletion of intermediate files.")
 )
@@ -65,6 +67,13 @@
 	MarkdownEnumDescriptions   []string               `json:"markdownEnumDescriptions,omitempty"`
 }
 
+type moduleVersion struct {
+	Path     string   `json:",omitempty"`
+	Version  string   `json:",omitempty"`
+	Time     string   `json:",omitempty"`
+	Versions []string `json:",omitempty"`
+}
+
 func main() {
 	flag.Parse()
 
@@ -105,11 +114,21 @@
 		if len(split) == 1 {
 			log.Fatalf("expected to find %q in %s, not found", gen, filename)
 		}
-		s := bytes.Join([][]byte{
-			bytes.TrimSpace(split[0]),
-			gen,
-			toAdd,
-		}, []byte("\n\n"))
+		var s []byte
+		if strings.HasSuffix(filename, ".ts") {
+			s = bytes.Join([][]byte{
+				split[0],
+				gen,
+				[]byte("\n\n"),
+				toAdd,
+			}, []byte{})
+		} else {
+			s = bytes.Join([][]byte{
+				bytes.TrimSpace(split[0]),
+				gen,
+				toAdd,
+			}, []byte("\n\n"))
+		}
 		newContent := append(s, '\n')
 
 		// Return early if the contents are unchanged.
@@ -177,6 +196,85 @@
 	writeGoplsSettingsSection(b, goplsProperty)
 
 	rewrite(filepath.Join(dir, "docs", "settings.md"), b.Bytes())
+
+	// Only update the latest tool versions if the flag is set.
+	if !*updateLatestToolVersionsFlag {
+		return
+	}
+
+	// Clear so that we can rewrite src/goToolsInformation.ts.
+	b.Reset()
+
+	// Check for latest dlv-dap version.
+	dlvVersion, err := listModuleVersion("github.com/go-delve/delve@master")
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	// Check for the latest gopls version.
+	versions, err := listAllModuleVersions("golang.org/x/tools/gopls")
+	if err != nil {
+		log.Fatal(err)
+	}
+	latestIndex := len(versions.Versions) - 1
+	latestPre := versions.Versions[latestIndex]
+	// We need to find the last version that was not a pre-release.
+	var latest string
+	for latest = versions.Versions[latestIndex]; latestIndex >= 0; latestIndex-- {
+		if !strings.Contains(latest, "pre") {
+			break
+		}
+	}
+
+	goplsVersion, err := listModuleVersion(fmt.Sprintf("golang.org/x/tools/gopls@%s", latest))
+	if err != nil {
+		log.Fatal(err)
+	}
+	goplsVersionPre, err := listModuleVersion(fmt.Sprintf("golang.org/x/tools/gopls@%s", latestPre))
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	allToolsFile := filepath.Join(dir, "tools", "allTools.ts.in")
+
+	// Find the package.json file.
+	data, err = ioutil.ReadFile(allToolsFile)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	// TODO(suzmue): change input to json and avoid magic string printing.
+	toolsString := fmt.Sprintf(string(data), goplsVersion.Version, goplsVersion.Time[:len("YYYY-MM-DD")], goplsVersionPre.Version, goplsVersionPre.Time[:len("YYYY-MM-DD")], dlvVersion.Version, dlvVersion.Time[:len("YYYY-MM-DD")])
+
+	// Write tools section.
+	b.WriteString(toolsString)
+	rewrite(filepath.Join(dir, "src", "goToolsInformation.ts"), b.Bytes())
+}
+
+func listModuleVersion(path string) (moduleVersion, error) {
+	output, err := exec.Command("go", "list", "-m", "-json", path).Output()
+	if err != nil {
+		return moduleVersion{}, err
+	}
+	var version moduleVersion
+	err = json.Unmarshal(output, &version)
+	if err != nil {
+		return moduleVersion{}, err
+	}
+	return version, nil
+}
+
+func listAllModuleVersions(path string) (moduleVersion, error) {
+	output, err := exec.Command("go", "list", "-m", "-json", "-versions", path).Output()
+	if err != nil {
+		return moduleVersion{}, err
+	}
+	var version moduleVersion
+	err = json.Unmarshal(output, &version)
+	if err != nil {
+		return moduleVersion{}, err
+	}
+	return version, nil
 }
 
 func writeProperty(b *bytes.Buffer, heading string, p Property) {