[release] prepare v0.20.0 release

The following is the output of
git log --oneline --no-decorate --cherry-pick release..master

Some commits were already cherry-picked and released in v0.19.1.

6e8bbba build: run vsce package during testing
cf03db6 package.json: update gopls settings based on gopls v0.6.0
1985e90 docs/settings.md: add description of object type settings' properties
4b24e75 package.json: add go.toolsManagement.checkForUpdates
63b4bd1 tools/generate: include deprecationMessage
9fcd568 test: remove symlink tests for substitutePath
0dee1a8 src/goInstallTools: fix broken gocode-gomod install
1812139 src/goLanguageServer: disable language service on guest side completely
a1ed38c snippets: add snippet for type function declaration
211c7a6 src/goLanguageServer.ts: update gopls when gopls is turned on by default
93f32bb src/debugAdapter: add substitutePath config for debugging
8b7bc62 src/goLanguageServer: show language server start progress
b950da5 test: increase timeout for extension tests
bb26907 src/goInstallTools: stop requiring to install legacy tools
a9f7034 CHANGELOG: v0.19.1 change
6407dc1 test: fix resolve file names in logs tests for GOPATH
27a25c4 test: fix remote attach tests on windows long tests
43afbf2 src/goInstallTools: fix gocode-gomod installation path
2a643de src/goInstallTools: add reference for incorrect PATH issue
8511760 src/pathUtils: check /usr/local/bin/go for go
a6d5d51 src/goDebugConfiguration.ts: offer quick pick menu for creating launch.json
da0f1c6 src/goLanguageServer: enable user survey in stable version.
4e42d93 package.json: declare gopls settings as a one property
685e302 src/goLanguageServer: change back revealOutputChannelOn to Never
203dbe9 [master] update CHANGELOG for v0.19.0
4fee2d6 src/goLanguageServer: improve automated gopls log collection
f379f50 src/goLanguageServer: prompt users to file an issue if they opted out
49bca83 package.json: mark settings that are not applicable when using gopls
650b8ae src/goDebug: check for undefined launchArgs.packagePathToGoModMap
e2d78f1 src/goDebug: fix noDebug mode for windows
e8d462e snippets: avoid using "var" as default name in snippets
cecb575 package.json: embed autogenerated gopls settings
98e508a tools/goplssetting: use text note instead of emoji for section
8ea68d8 src/testUtils: adjust file path expansion regexp to capture subtest failures
795bf32 package.json: Add "template" property to go.addTags configuration
f6c5b08 .github/workflows: update to use env file instead of set-env
dd08b76 src/goLanguageServer: suggest updating gopls before filing an issue
9dd4a94 webpack.config.js: ignore warnings from yargs dependency
15a6a04 .github/ISSUE_TEMPLATE: request gopls version

Change-Id: Ia0754df12a8b4b2dcf3f02456fe452623d14b6a0
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 4d6fb1b..e25a2d0 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -12,9 +12,10 @@
 
 Please review the [documentation](https://github.com/golang/vscode-go/tree/master/docs) before filing an issue.
 Helpful pages include:
-
+ 
 - [GOPATH](https://github.com/golang/vscode-go/tree/master/docs/gopath.md)
 - [Module Support](https://github.com/golang/vscode-go/tree/master/docs/modules.md)
+- [Go Language Server](https://github.com/golang/vscode-go/blob/master/docs/gopls.md)
 - [Debugging](https://github.com/golang/vscode-go/tree/master/docs/debugging.md)
   - Set "trace": "log" and share the resulting logs in the debug console when logging an issue.
 
@@ -23,9 +24,11 @@
 ### What version of Go, VS Code & VS Code Go extension are you using?
 - Run `go version` to get version of Go
 	- <Paste go version here>
+- Run `gopls -v version` to get version of Gopls if you are using [the language server](https://github.com/golang/vscode-go/blob/master/docs/gopls.md).
+	- <Paste gopls version here>
 - Run `code -v` or `code-insiders -v` to get version of VS Code or VS Code Insiders
 	- <Paste VS Code version here>
-- Check your installed extensions to get the version of the VS Code Go extension 
+- Check your installed extensions to get the version of the VS Code Go extension
 	- <Paste Go extension version here>
 - Run `go env` to get the go development environment details
 	- <Paste the output here>
diff --git a/build/Dockerfile b/build/Dockerfile
index 62a5b98..2d70051 100644
--- a/build/Dockerfile
+++ b/build/Dockerfile
@@ -27,6 +27,7 @@
 ENV DEBIAN_FRONTEND noninteractive
 
 RUN apt-get update && apt-get install -y libnss3 libgtk-3-dev libxss1 libasound2 xvfb libsecret-1-0
+RUN npm install -g vsce
 
 WORKDIR /workspace
 ENTRYPOINT ["build/all.bash"]
diff --git a/build/all.bash b/build/all.bash
index f91df52..afd0706 100755
--- a/build/all.bash
+++ b/build/all.bash
@@ -52,6 +52,9 @@
 
   echo "**** Run settings generator ****"
   go run tools/generate.go -w=false
+
+  echo "**** Check if vsce works ****"
+  vsce package
 }
 
 run_test_in_docker() {
diff --git a/docs/debugging.md b/docs/debugging.md
index 48c1bfe..5ae9ddb 100644
--- a/docs/debugging.md
+++ b/docs/debugging.md
@@ -110,7 +110,8 @@
 showLog    | If `true`, Delve logs will be printed in the Debug Console panel.
 logOutput  | Comma-separated list of Delve components (`debugger`, `gdbwire`, `lldbout`, `debuglineerr`, `rpc`) that should produce debug output when `showLog` is `true`.
 buildFlags | Build flags to pass to the Go compiler.
-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.
+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)(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.
 processId  | This is the process ID of the executable you want to debug. Applicable only when using the `attach` request in `local` mode.
 
@@ -308,7 +309,27 @@
 
 In the example, the VS Code debugger will run on the same machine as the headless `dlv` server. Make sure to update the `port` and `host` settings to point to your remote machine.
 
-`remotePath` should point to the absolute path of the program being debugged in the remote machine. `cwd` should point to the absolute path of the working directory of the program being debugged on your local machine. This should be the counterpart of the folder in `remotePath`. See [golang/vscode-go#45](https://github.com/golang/vscode-go/issues/45) for updates regarding `remotePath` and `cwd`.
+`remotePath` should point to the absolute path of the program being debugged in the remote machine. `cwd` should point to the absolute path of the working directory of the program being debugged on your local machine. This should be the counterpart of the folder in `remotePath`. See [golang/vscode-go#45](https://github.com/golang/vscode-go/issues/45) for updates regarding `remotePath` and `cwd`. You can also use the equivalent `substitutePath` configuration.
+
+```json5
+{
+    "name": "Launch remote",
+    "type": "go",
+    "request": "attach",
+    "mode": "remote",
+    "substitutePath": [
+		{
+			"from": "/absolute/path/dir/on/local/machine",
+			"to": "/absolute/path/dir/on/remote/machine",
+		},
+	],
+    "port": 2345,
+    "host": "127.0.0.1",
+    "cwd": "/absolute/path/dir/on/local/machine",
+}
+```
+
+If you do not set, `remotePath` or `substitutePath`, then the debug adapter will attempt to infer the path mappings. See [golang/vscode-go#45](https://github.com/golang/vscode-go/issues/45) for more information.
 
 When you run the `Launch remote` target, VS Code will send debugging commands to the `dlv` server you started, instead of launching it's own `dlv` instance against your program.
 
@@ -405,9 +426,25 @@
 
 ### Debugging symlink directories
 
-This extension does not provide support for debugging projects containing symlinks. Make sure that you are setting breakpoints in the files that Go will use to compile your program.
+Since the debugger and go compiler use the actual filenames, extra configuration is required to debug symlinked directories. Use the `substitutePath` property to tell the debugAdapter how to properly translate the paths. For example, if your project lives in `/path/to/actual/helloWorld`, but the project is open in vscode under the linked folder `/path/to/hello`, you can add the following to your config to set breakpoints in the files in `/path/to/hello`:
 
-For updates to symlink support reference [golang/vscode-go#622](https://github.com/golang/vscode-go/issues/622).
+```json5
+{
+    "name": "Launch remote",
+    "type": "go",
+    "request": "launch",
+    "mode": "auto",
+    "program": "/path/to/hello",
+    "substitutePath": [
+		{
+			"from": "/path/to/hello",
+			"to": "/path/to/actual/helloWorld",
+		},
+	],
+}
+```
+
+This extension does not provide general support for debugging projects containing symlinks. If `substitutePath` does not meet your needs, please consider commenting on this issue that contains updates to symlink support reference [golang/vscode-go#622](https://github.com/golang/vscode-go/issues/622).
 
 [Delve]: https://github.com/go-delve/delve
 [VS Code variables]: https://code.visualstudio.com/docs/editor/variables-reference
diff --git a/docs/settings.md b/docs/settings.md
index a6c533d..80cf4bf 100644
--- a/docs/settings.md
+++ b/docs/settings.md
@@ -34,13 +34,46 @@
     }
 
 
+#### `options`
+Comma separated tag=options pairs to be used by Go: Add Tags command
+
+#### `promptForTags`
+If true, Go: Add Tags command will prompt the user to provide tags, options, transform values instead of using the configured values
+
+#### `tags`
+Comma separated tags to be used by Go: Add Tags command
+
+#### `template`
+Custom format used by Go: Add Tags command for the tag value to be applied
+
+#### `transform`
+Transformation rule used by Go: Add Tags command to add tags
+
 ### `go.alternateTools`
 
 Alternate tools or alternate paths for the same tools used by the Go extension. Provide either absolute path or the name of the binary in GOPATH/bin, GOROOT/bin or PATH. Useful when you want to use wrapper script for the Go tools or versioned tools from https://gopkg.in.
 
+#### `go`
+Alternate tool to use instead of the go binary or alternate path to use for the go binary.
+
+#### `go-outline`
+Alternate tool to use instead of the go-outline binary or alternate path to use for the go-outline binary.
+
+#### `gocode`
+Alternate tool to use instead of the gocode binary or alternate path to use for the gocode binary.
+
+#### `gopkgs`
+Alternate tool to use instead of the gopkgs binary or alternate path to use for the gopkgs binary.
+
+#### `gopls`
+Alternate tool to use instead of the gopls binary or alternate path to use for the gopls binary.
+
+#### `guru`
+Alternate tool to use instead of the guru binary or alternate path to use for the guru binary.
+
 ### `go.autocompleteUnimportedPackages`
 
-Include unimported packages in auto-complete suggestions.
+Include unimported packages in auto-complete suggestions. Not applicable when using the language server.
 
 Default: `false`
 
@@ -115,6 +148,27 @@
     }
 
 
+#### `coveredBorderColor`
+Color to use for the border of covered code.
+
+#### `coveredGutterStyle`
+Gutter style to indicate covered code.
+
+#### `coveredHighlightColor`
+Color in the rgba format to use to highlight covered code.
+
+#### `type`
+
+
+#### `uncoveredBorderColor`
+Color to use for the border of uncovered code.
+
+#### `uncoveredGutterStyle`
+Gutter style to indicate covered code.
+
+#### `uncoveredHighlightColor`
+Color in the rgba format to use to highlight uncovered code.
+
 ### `go.coverageOptions`
 
 Use these options to control whether only covered or only uncovered code or both should be highlighted after running test coverage
@@ -134,6 +188,15 @@
     }
 
 
+#### `apiVersion`
+Delve Api Version to use. Default value is 2.
+
+#### `dlvLoadConfig`
+LoadConfig describes to delve, how to load values from target's memory
+
+#### `showGlobalVariables`
+Boolean value to indicate whether global package variables should be shown in the variables pane or not.
+
 ### `go.docsTool`
 
 Pick 'godoc' or 'gogetdoc' to get documentation. Not applicable when using the language server.
@@ -163,6 +226,48 @@
     }
 
 
+#### `addImport`
+If true, adds command to import a package to the editor context menu
+
+#### `addTags`
+If true, adds command to add configured tags from struct fields to the editor context menu
+
+#### `debugTestAtCursor`
+If true, adds command to debug the test under the cursor to the editor context menu
+
+#### `fillStruct`
+If true, adds command to fill struct literal with default values to the editor context menu
+
+#### `generateTestForFile`
+If true, adds command to generate unit tests for current file to the editor context menu
+
+#### `generateTestForFunction`
+If true, adds command to generate unit tests for function under the cursor to the editor context menu
+
+#### `generateTestForPackage`
+If true, adds command to generate unit tests for currnt package to the editor context menu
+
+#### `playground`
+If true, adds command to upload the current file or selection to the Go Playground
+
+#### `removeTags`
+If true, adds command to remove configured tags from struct fields to the editor context menu
+
+#### `testAtCursor`
+If true, adds command to run the test under the cursor to the editor context menu
+
+#### `testCoverage`
+If true, adds command to run test coverage to the editor context menu
+
+#### `testFile`
+If true, adds command to run all tests in the current file to the editor context menu
+
+#### `testPackage`
+If true, adds command to run all tests in the current package to the editor context menu
+
+#### `toggleTestFile`
+If true, adds command to toggle between a Go file and its test file to the editor context menu
+
 ### `go.enableCodeLens`
 
 Feature level setting to enable/disable code lens for references and run/debug tests
@@ -173,9 +278,15 @@
     }
 
 
+#### `references`
+If true, enables the references code lens. Uses guru. Recalculates when there is change to the document followed by scrolling. Unnecessary when using the language server; use the call graph feature instead.
+
+#### `runtest`
+If true, enables code lens for running and debugging tests
+
 ### `go.formatFlags`
 
-Flags to pass to format tool (e.g. ["-s"])
+Flags to pass to format tool (e.g. ["-s"]). Not applicable when using the language server.
 
 ### `go.formatTool`
 
@@ -223,17 +334,17 @@
 
 ### `go.gotoSymbol.ignoreFolders`
 
-Folder names (not paths) to ignore while using Go to Symbol in Workspace feature
+Folder names (not paths) to ignore while using Go to Symbol in Workspace feature. Not applicable when using the language server.
 
 ### `go.gotoSymbol.includeGoroot`
 
-If false, the standard library located at $GOROOT will be excluded while using the Go to Symbol in File feature
+If false, the standard library located at $GOROOT will be excluded while using the Go to Symbol in File feature. Not applicable when using the language server.
 
 Default: `false`
 
 ### `go.gotoSymbol.includeImports`
 
-If false, the import statements will be excluded while using the Go to Symbol in File feature
+If false, the import statements will be excluded while using the Go to Symbol in File feature. Not applicable when using the language server.
 
 Default: `false`
 
@@ -259,6 +370,12 @@
     }
 
 
+#### `diagnostics`
+If true, the language server will provide build, vet errors and the extension will ignore the `buildOnSave`, `vetOnSave` settings.
+
+#### `documentLink`
+If true, the language server will provide clickable Godoc links for import statements.
+
 ### `go.languageServerFlags`
 
 Flags like -rpc.trace and -logfile to be used while running the language server.
@@ -285,7 +402,7 @@
 
 ### `go.liveErrors`
 
-Use gotype on the file currently being edited and report any semantic or syntactic errors found after configured delay.
+Use gotype on the file currently being edited and report any semantic or syntactic errors found after configured delay. Not applicable when using the language server.
 
 Default:{<br/>
 &nbsp;&nbsp;`"delay": 500`,<br/>
@@ -293,6 +410,12 @@
     }
 
 
+#### `delay`
+The number of milliseconds to delay before execution. Resets with each keystroke.
+
+#### `enabled`
+If true, runs gotype on the file currently being edited and reports any semantic or syntactic errors found.
+
 ### `go.logging.level`
 
 The logging level the extension logs at, defaults to 'error'
@@ -301,10 +424,17 @@
 
 Default: `error`
 
-### `go.overwriteGoplsMiddleware`
+### `go.overwriteGoplsMiddleware (deprecated)`
 
+This option is deprecated.
 This option provides a set of flags which determine if vscode-go should intercept certain commands from gopls. These flags assume the `gopls` settings, which enable codelens from gopls, are also present.
 
+#### `codelens`
+
+
+#### `default`
+
+
 ### `go.playground`
 
 The flags configured here will be passed through to command `goplay`
@@ -316,6 +446,15 @@
     }
 
 
+#### `openbrowser`
+Whether to open the created Go Playground in the default browser
+
+#### `run`
+Whether to run the created Go Playground after creation
+
+#### `share`
+Whether to make the created Go Playground shareable
+
 ### `go.removeTags`
 
 Tags and options configured here will be used by the Remove Tags command to remove tags to struct fields. If promptForTags is true, then user will be prompted for tags and options. By default, all tags and options will be removed.
@@ -327,6 +466,15 @@
     }
 
 
+#### `options`
+Comma separated tag=options pairs to be used by Go: Remove Tags command
+
+#### `promptForTags`
+If true, Go: Remove Tags command will prompt the user to provide tags and options instead of using the configured values
+
+#### `tags`
+Comma separated tags to be used by Go: Remove Tags command
+
 ### `go.testEnvFile`
 
 Absolute path to a file containing environment variables definitions. File contents should be of the form key=value.
@@ -339,7 +487,7 @@
 
 ### `go.testFlags`
 
-Flags to pass to `go test`. If null, then buildFlags will be used.
+Flags to pass to `go test`. If null, then buildFlags will be used. This is not propagated to the language server.
 
 efault: `<nil>`
 
@@ -363,7 +511,7 @@
 
 ### `go.toolsEnvVars`
 
-Environment variables that will passed to the processes that run the Go tools (e.g. CGO_CFLAGS)
+Environment variables that will passed to the tools that run the Go tools (e.g. CGO_CFLAGS)
 
 ### `go.toolsGopath`
 
@@ -371,6 +519,14 @@
 
 Default: ``
 
+### `go.toolsManagement.checkForUpdates`
+
+Specify whether to prompt about new versions of Go and the Go tools (currently, only `gopls`) the extension depends on
+
+Allowed Values:`[proxy local off]`
+
+Default: `proxy`
+
 ### `go.trace.server`
 
 Trace the communication between VS Code and the Go language server.
@@ -381,25 +537,26 @@
 
 ### `go.useCodeSnippetsOnFunctionSuggest`
 
-Complete functions with their parameter signature, including the variable types
+Complete functions with their parameter signature, including the variable type. Not propagated to the language server.
 
 Default: `false`
 
 ### `go.useCodeSnippetsOnFunctionSuggestWithoutType`
 
-Complete functions with their parameter signature, excluding the variable types
+Complete functions with their parameter signature, excluding the variable types. Use `gopls.usePlaceholders` when using the language server.
 
 Default: `false`
 
-### `go.useGoProxyToCheckForToolUpdates`
+### `go.useGoProxyToCheckForToolUpdates (deprecated)`
 
+Use `go.toolsManagement.checkForUpdates` instead.
 When enabled, the extension automatically checks the Go proxy if there are updates available for Go and the Go tools (at present, only gopls) it depends on and prompts the user accordingly
 
 Default: `true`
 
 ### `go.useLanguageServer`
 
-Use the Go language server "gopls" from Google for powering language features like code navigation, completion, formatting & diagnostics.
+Use the Go language server "gopls" from Google for powering language features like code navigation, completion, refactoring, formatting & diagnostics.
 
 Default: `false`
 
@@ -414,3 +571,196 @@
 Allowed Values:`[package workspace off]`
 
 Default: `package`
+
+### `gopls`
+
+Configure the default Go language server ('gopls'). In most cases, configuring this section is unnecessary. See [the documentation](https://github.com/golang/tools/blob/master/gopls/doc/settings.md) for all available settings.
+
+#### `allowImplicitNetworkAccess`
+(Experimental) allowImplicitNetworkAccess disables GOPROXY=off, allowing implicit module
+downloads rather than requiring user action. This option will eventually
+be removed.
+
+
+#### `allowModfileModifications`
+(Experimental) allowModfileModifications disables -mod=readonly, allowing imports from
+out-of-scope modules. This option will eventually be removed.
+
+
+#### `analyses`
+analyses specify analyses that the user would like to enable or disable.
+A map of the names of analysis passes that should be enabled/disabled.
+A full list of analyzers that gopls uses can be found [here](analyzers.md)
+
+Example Usage:
+```json5
+...
+"analyses": {
+  "unreachable": false, // Disable the unreachable analyzer.
+  "unusedparams": true  // Enable the unusedparams analyzer.
+}
+...
+```
+
+
+#### `annotations`
+(Experimental) annotations suppress various kinds of optimization diagnostics
+that would be reported by the gc_details command.
+ * noNilcheck suppresses display of nilchecks.
+ * noEscape suppresses escape choices.
+ * noInline suppresses inlining choices.
+ * noBounds suppresses bounds checking diagnostics.
+
+
+#### `buildFlags`
+buildFlags is the set of flags passed on to the build system when invoked.
+It is applied to queries like `go list`, which is used when discovering files.
+The most common use is to set `-tags`.
+
+
+#### `codelenses`
+codelenses overrides the enabled/disabled state of code lenses. See the "Code Lenses"
+section of settings.md for the list of supported lenses.
+
+Example Usage:
+```json5
+"gopls": {
+...
+  "codelenses": {
+    "generate": false,  // Don't show the `go generate` lens.
+    "gc_details": true  // Show a code lens toggling the display of gc's choices.
+  }
+...
+}
+```
+
+
+#### `completionBudget`
+(For Debugging) completionBudget is the soft latency goal for completion requests. Most
+requests finish in a couple milliseconds, but in some cases deep
+completions can take much longer. As we use up our budget we
+dynamically reduce the search scope to ensure we return timely
+results. Zero means unlimited.
+
+
+#### `directoryFilters`
+directoryFilters can be used to exclude unwanted directories from the
+workspace. By default, all directories are included. Filters are an
+operator, `+` to include and `-` to exclude, followed by a path prefix
+relative to the workspace folder. They are evaluated in order, and
+the last filter that applies to a path controls whether it is included.
+The path prefix can be empty, so an initial `-` excludes everything.
+
+Examples:
+Exclude node_modules: `-node_modules`
+Include only project_a: `-` (exclude everything), `+project_a`
+Include only project_a, but not node_modules inside it: `-`, `+project_a`, `-project_a/node_modules`
+
+
+#### `env`
+env adds environment variables to external commands run by `gopls`, most notably `go list`.
+
+
+#### `expandWorkspaceToModule`
+(Experimental) expandWorkspaceToModule instructs `gopls` to adjust the scope of the
+workspace to find the best available module root. `gopls` first looks for
+a go.mod file in any parent directory of the workspace folder, expanding
+the scope to that directory if it exists. If no viable parent directory is
+found, gopls will check if there is exactly one child directory containing
+a go.mod file, narrowing the scope to that directory if it exists.
+
+
+#### `experimentalDiagnosticsDelay`
+(Experimental) experimentalDiagnosticsDelay controls the amount of time that gopls waits
+after the most recent file modification before computing deep diagnostics.
+Simple diagnostics (parsing and type-checking) are always run immediately
+on recently modified packages.
+
+This option must be set to a valid duration string, for example `"250ms"`.
+
+
+#### `experimentalPackageCacheKey`
+(Experimental) experimentalPackageCacheKey controls whether to use a coarser cache key
+for package type information to increase cache hits. This setting removes
+the user's environment, build flags, and working directory from the cache
+key, which should be a safe change as all relevant inputs into the type
+checking pass are already hashed into the key. This is temporarily guarded
+by an experiment because caching behavior is subtle and difficult to
+comprehensively test.
+
+
+#### `experimentalWorkspaceModule`
+(Experimental) experimentalWorkspaceModule opts a user into the experimental support
+for multi-module workspaces.
+
+
+#### `gofumpt`
+gofumpt indicates if we should run gofumpt formatting.
+
+
+#### `hoverKind`
+hoverKind controls the information that appears in the hover text.
+SingleLine and Structured are intended for use only by authors of editor plugins.
+
+
+#### `importShortcut`
+importShortcut specifies whether import statements should link to
+documentation or go to definitions.
+
+
+#### `linkTarget`
+linkTarget controls where documentation links go.
+It might be one of:
+
+* `"godoc.org"`
+* `"pkg.go.dev"`
+
+If company chooses to use its own `godoc.org`, its address can be used as well.
+
+
+#### `linksInHover`
+linksInHover toggles the presence of links to documentation in hover.
+
+
+#### `local`
+local is the equivalent of the `goimports -local` flag, which puts imports beginning with this string after 3rd-party packages.
+It should be the prefix of the import path whose imports should be grouped separately.
+
+
+#### `matcher`
+matcher sets the algorithm that is used when calculating completion candidates.
+
+
+#### `semanticTokens`
+(Experimental) semanticTokens controls whether the LSP server will send
+semantic tokens to the client.
+
+
+#### `staticcheck`
+(Experimental) staticcheck enables additional analyses from staticcheck.io.
+
+
+#### `symbolMatcher`
+symbolMatcher sets the algorithm that is used when finding workspace symbols.
+
+
+#### `symbolStyle`
+symbolStyle controls how symbols are qualified in symbol responses.
+
+Example Usage:
+```json5
+"gopls": {
+...
+  "symbolStyle": "dynamic",
+...
+}
+```
+
+
+#### `usePlaceholders`
+placeholders enables placeholders for function parameters or struct fields in completion responses.
+
+
+#### `verboseOutput`
+(For Debugging) verboseOutput enables additional debug logging.
+
diff --git a/package-lock.json b/package-lock.json
index 1a238ff..eee8d10 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "go",
-  "version": "0.19.1",
+  "version": "0.20.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
diff --git a/package.json b/package.json
index 22cce84..df096e0 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "go",
   "displayName": "Go",
-  "version": "0.19.1",
+  "version": "0.20.0",
   "publisher": "golang",
   "description": "Rich Go language support for Visual Studio Code",
   "author": {
@@ -522,6 +522,26 @@
                 "description": "Environment variables passed to the program.",
                 "default": {}
               },
+              "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": []
+              },
               "buildFlags": {
                 "type": "string",
                 "description": "Build flags, to be passed to the Go compiler.",
@@ -534,7 +554,7 @@
               },
               "remotePath": {
                 "type": "string",
-                "description": "Absolute path to the file being debugged on the remote machine in case of remote debugging.",
+                "description": "Absolute path to the file being debugged on the remote machine in case of remote debugging. If specified, becomes the first entry in substitutePath.",
                 "default": ""
               },
               "port": {
@@ -680,7 +700,7 @@
               },
               "remotePath": {
                 "type": "string",
-                "description": "If remote debugging, the path to the source code on the remote machine, if different from the local machine.",
+                "description": "If remote debugging, the path to the source code on the remote machine, if different from the local machine. If specified, becomes the first entry in substitutePath.",
                 "default": ""
               },
               "port": {
@@ -693,6 +713,26 @@
                 "description": "The host name of the machine the delve debugger will be listening on.",
                 "default": "127.0.0.1"
               },
+              "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.",
+                "default": []
+              },
               "trace": {
                 "type": "string",
                 "enum": [
@@ -904,7 +944,7 @@
             "type": "string"
           },
           "default": [],
-          "description": "Flags to pass to format tool (e.g. [\"-s\"])",
+          "description": "Flags to pass to format tool (e.g. [\"-s\"]). Not applicable when using the language server.",
           "scope": "resource"
         },
         "go.inferGopath": {
@@ -1096,7 +1136,7 @@
             "type": "string"
           },
           "default": null,
-          "description": "Flags to pass to `go test`. If null, then buildFlags will be used.",
+          "description": "Flags to pass to `go test`. If null, then buildFlags will be used. This is not propagated to the language server.",
           "scope": "resource"
         },
         "go.generateTestsFlags": {
@@ -1111,7 +1151,7 @@
         "go.toolsEnvVars": {
           "type": "object",
           "default": {},
-          "description": "Environment variables that will passed to the processes that run the Go tools (e.g. CGO_CFLAGS)",
+          "description": "Environment variables that will passed to the tools that run the Go tools (e.g. CGO_CFLAGS)",
           "scope": "resource"
         },
         "go.gocodeFlags": {
@@ -1147,19 +1187,19 @@
         "go.useCodeSnippetsOnFunctionSuggest": {
           "type": "boolean",
           "default": false,
-          "description": "Complete functions with their parameter signature, including the variable types",
+          "description": "Complete functions with their parameter signature, including the variable type. Not propagated to the language server.",
           "scope": "resource"
         },
         "go.useCodeSnippetsOnFunctionSuggestWithoutType": {
           "type": "boolean",
           "default": false,
-          "description": "Complete functions with their parameter signature, excluding the variable types",
+          "description": "Complete functions with their parameter signature, excluding the variable types. Use `gopls.usePlaceholders` when using the language server.",
           "scope": "resource"
         },
         "go.autocompleteUnimportedPackages": {
           "type": "boolean",
           "default": false,
-          "description": "Include unimported packages in auto-complete suggestions.",
+          "description": "Include unimported packages in auto-complete suggestions. Not applicable when using the language server.",
           "scope": "resource"
         },
         "go.docsTool": {
@@ -1176,7 +1216,7 @@
         "go.useLanguageServer": {
           "type": "boolean",
           "default": false,
-          "description": "Use the Go language server \"gopls\" from Google for powering language features like code navigation, completion, formatting & diagnostics."
+          "description": "Use the Go language server \"gopls\" from Google for powering language features like code navigation, completion, refactoring, formatting & diagnostics."
         },
         "go.languageServerFlags": {
           "type": "array",
@@ -1226,21 +1266,37 @@
           "description": "The logging level the extension logs at, defaults to 'error'",
           "scope": "machine-overridable"
         },
+        "go.toolsManagement.checkForUpdates": {
+          "type": "string",
+          "default": "proxy",
+          "enum": [
+            "proxy",
+            "local",
+            "off"
+          ],
+          "enumDescriptions": [
+            "keeps notified of new releases by checking the Go module proxy (GOPROXY)",
+            "checks only the minimum tools versions required by the extension",
+            "completely disables version check (not recommended)"
+          ],
+          "markdownDescription": "Specify whether to prompt about new versions of Go and the Go tools (currently, only `gopls`) the extension depends on"
+        },
         "go.useGoProxyToCheckForToolUpdates": {
           "type": "boolean",
           "default": true,
-          "description": "When enabled, the extension automatically checks the Go proxy if there are updates available for Go and the Go tools (at present, only gopls) it depends on and prompts the user accordingly"
+          "description": "When enabled, the extension automatically checks the Go proxy if there are updates available for Go and the Go tools (at present, only gopls) it depends on and prompts the user accordingly",
+          "markdownDeprecationMessage": "Use `go.toolsManagement.checkForUpdates` instead."
         },
         "go.gotoSymbol.includeImports": {
           "type": "boolean",
           "default": false,
-          "description": "If false, the import statements will be excluded while using the Go to Symbol in File feature",
+          "description": "If false, the import statements will be excluded while using the Go to Symbol in File feature. Not applicable when using the language server.",
           "scope": "resource"
         },
         "go.gotoSymbol.includeGoroot": {
           "type": "boolean",
           "default": false,
-          "description": "If false, the standard library located at $GOROOT will be excluded while using the Go to Symbol in File feature",
+          "description": "If false, the standard library located at $GOROOT will be excluded while using the Go to Symbol in File feature. Not applicable when using the language server.",
           "scope": "resource"
         },
         "go.enableCodeLens": {
@@ -1249,7 +1305,7 @@
             "references": {
               "type": "boolean",
               "default": false,
-              "description": "If true, enables the references code lens. Uses guru. Recalculates when there is change to the document followed by scrolling."
+              "description": "If true, enables the references code lens. Uses guru. Recalculates when there is change to the document followed by scrolling. Unnecessary when using the language server; use the call graph feature instead."
             },
             "runtest": {
               "type": "boolean",
@@ -1291,6 +1347,7 @@
             }
           },
           "scope": "resource",
+          "deprecationMessage": "This option is deprecated.",
           "description": "This option provides a set of flags which determine if vscode-go should intercept certain commands from gopls. These flags assume the `gopls` settings, which enable codelens from gopls, are also present."
         },
         "go.addTags": {
@@ -1299,7 +1356,7 @@
             "promptForTags": {
               "type": "boolean",
               "default": false,
-              "description": "If true, Go: Add Tags command will prompt the user to provide tags and options instead of using the configured values"
+              "description": "If true, Go: Add Tags command will prompt the user to provide tags, options, transform values instead of using the configured values"
             },
             "tags": {
               "type": "string",
@@ -1359,7 +1416,7 @@
             "enabled": false,
             "delay": 500
           },
-          "description": "Use gotype on the file currently being edited and report any semantic or syntactic errors found after configured delay.",
+          "description": "Use gotype on the file currently being edited and report any semantic or syntactic errors found after configured delay. Not applicable when using the language server.",
           "scope": "resource"
         },
         "go.removeTags": {
@@ -1516,7 +1573,7 @@
             "type": "string"
           },
           "default": [],
-          "description": "Folder names (not paths) to ignore while using Go to Symbol in Workspace feature",
+          "description": "Folder names (not paths) to ignore while using Go to Symbol in Workspace feature. Not applicable when using the language server.",
           "scope": "resource"
         },
         "go.delveConfig": {
@@ -1627,6 +1684,231 @@
             }
           },
           "additionalProperties": true
+        },
+        "gopls": {
+          "type": "object",
+          "markdownDescription": "Configure the default Go language server ('gopls'). In most cases, configuring this section is unnecessary. See [the documentation](https://github.com/golang/tools/blob/master/gopls/doc/settings.md) for all available settings.",
+          "scope": "resource",
+          "additionalProperties": false,
+          "properties": {
+            "buildFlags": {
+              "type": "array",
+              "markdownDescription": "buildFlags is the set of flags passed on to the build system when invoked.\nIt is applied to queries like `go list`, which is used when discovering files.\nThe most common use is to set `-tags`.\n",
+              "default": [],
+              "scope": "resource"
+            },
+            "env": {
+              "type": "object",
+              "markdownDescription": "env adds environment variables to external commands run by `gopls`, most notably `go list`.\n",
+              "default": {},
+              "scope": "resource"
+            },
+            "hoverKind": {
+              "type": "string",
+              "markdownDescription": "hoverKind controls the information that appears in the hover text.\nSingleLine and Structured are intended for use only by authors of editor plugins.\n",
+              "enum": [
+                "FullDocumentation",
+                "NoDocumentation",
+                "SingleLine",
+                "Structured",
+                "SynopsisDocumentation"
+              ],
+              "markdownEnumDescriptions": [
+                "",
+                "",
+                "",
+                "`\"Structured\"` is an experimental setting that returns a structured hover format.\nThis format separates the signature from the documentation, so that the client\ncan do more manipulation of these fields.\n\nThis should only be used by clients that support this behavior.\n",
+                ""
+              ],
+              "default": "FullDocumentation",
+              "scope": "resource"
+            },
+            "usePlaceholders": {
+              "type": "boolean",
+              "markdownDescription": "placeholders enables placeholders for function parameters or struct fields in completion responses.\n",
+              "default": false,
+              "scope": "resource"
+            },
+            "linkTarget": {
+              "type": "string",
+              "markdownDescription": "linkTarget controls where documentation links go.\nIt might be one of:\n\n* `\"godoc.org\"`\n* `\"pkg.go.dev\"`\n\nIf company chooses to use its own `godoc.org`, its address can be used as well.\n",
+              "default": "pkg.go.dev",
+              "scope": "resource"
+            },
+            "local": {
+              "type": "string",
+              "markdownDescription": "local is the equivalent of the `goimports -local` flag, which puts imports beginning with this string after 3rd-party packages.\nIt should be the prefix of the import path whose imports should be grouped separately.\n",
+              "default": "",
+              "scope": "resource"
+            },
+            "gofumpt": {
+              "type": "boolean",
+              "markdownDescription": "gofumpt indicates if we should run gofumpt formatting.\n",
+              "default": false,
+              "scope": "resource"
+            },
+            "analyses": {
+              "type": "object",
+              "markdownDescription": "analyses specify analyses that the user would like to enable or disable.\nA map of the names of analysis passes that should be enabled/disabled.\nA full list of analyzers that gopls uses can be found [here](analyzers.md)\n\nExample Usage:\n```json5\n...\n\"analyses\": {\n  \"unreachable\": false, // Disable the unreachable analyzer.\n  \"unusedparams\": true  // Enable the unusedparams analyzer.\n}\n...\n```\n",
+              "default": {},
+              "scope": "resource"
+            },
+            "codelenses": {
+              "type": "object",
+              "markdownDescription": "codelenses overrides the enabled/disabled state of code lenses. See the \"Code Lenses\"\nsection of settings.md for the list of supported lenses.\n\nExample Usage:\n```json5\n\"gopls\": {\n...\n  \"codelenses\": {\n    \"generate\": false,  // Don't show the `go generate` lens.\n    \"gc_details\": true  // Show a code lens toggling the display of gc's choices.\n  }\n...\n}\n```\n",
+              "default": {
+                "gc_details": false,
+                "generate": true,
+                "regenerate_cgo": true,
+                "tidy": true,
+                "upgrade_dependency": true,
+                "vendor": true
+              },
+              "scope": "resource"
+            },
+            "linksInHover": {
+              "type": "boolean",
+              "markdownDescription": "linksInHover toggles the presence of links to documentation in hover.\n",
+              "default": true,
+              "scope": "resource"
+            },
+            "importShortcut": {
+              "type": "string",
+              "markdownDescription": "importShortcut specifies whether import statements should link to\ndocumentation or go to definitions.\n",
+              "enum": [
+                "Both",
+                "Definition",
+                "Link"
+              ],
+              "markdownEnumDescriptions": [
+                "",
+                "",
+                ""
+              ],
+              "default": "Both",
+              "scope": "resource"
+            },
+            "matcher": {
+              "type": "string",
+              "markdownDescription": "matcher sets the algorithm that is used when calculating completion candidates.\n",
+              "enum": [
+                "CaseInsensitive",
+                "CaseSensitive",
+                "Fuzzy"
+              ],
+              "markdownEnumDescriptions": [
+                "",
+                "",
+                ""
+              ],
+              "default": "Fuzzy",
+              "scope": "resource"
+            },
+            "symbolMatcher": {
+              "type": "string",
+              "markdownDescription": "symbolMatcher sets the algorithm that is used when finding workspace symbols.\n",
+              "enum": [
+                "CaseInsensitive",
+                "CaseSensitive",
+                "Fuzzy"
+              ],
+              "markdownEnumDescriptions": [
+                "",
+                "",
+                ""
+              ],
+              "default": "Fuzzy",
+              "scope": "resource"
+            },
+            "symbolStyle": {
+              "type": "string",
+              "markdownDescription": "symbolStyle controls how symbols are qualified in symbol responses.\n\nExample Usage:\n```json5\n\"gopls\": {\n...\n  \"symbolStyle\": \"dynamic\",\n...\n}\n```\n",
+              "enum": [
+                "Dynamic",
+                "Full",
+                "Package"
+              ],
+              "markdownEnumDescriptions": [
+                "`\"Dynamic\"` uses whichever qualifier results in the highest scoring\nmatch for the given symbol query. Here a \"qualifier\" is any \"/\" or \".\"\ndelimited suffix of the fully qualified symbol. i.e. \"to/pkg.Foo.Field\" or\njust \"Foo.Field\".\n",
+                "`\"Full\"` is fully qualified symbols, i.e.\n\"path/to/pkg.Foo.Field\".\n",
+                "`\"Package\"` is package qualified symbols i.e.\n\"pkg.Foo.Field\".\n"
+              ],
+              "default": "Dynamic",
+              "scope": "resource"
+            },
+            "directoryFilters": {
+              "type": "array",
+              "markdownDescription": "directoryFilters can be used to exclude unwanted directories from the\nworkspace. By default, all directories are included. Filters are an\noperator, `+` to include and `-` to exclude, followed by a path prefix\nrelative to the workspace folder. They are evaluated in order, and\nthe last filter that applies to a path controls whether it is included.\nThe path prefix can be empty, so an initial `-` excludes everything.\n\nExamples:\nExclude node_modules: `-node_modules`\nInclude only project_a: `-` (exclude everything), `+project_a`\nInclude only project_a, but not node_modules inside it: `-`, `+project_a`, `-project_a/node_modules`\n",
+              "default": [],
+              "scope": "resource"
+            },
+            "annotations": {
+              "type": "object",
+              "markdownDescription": "(Experimental) annotations suppress various kinds of optimization diagnostics\nthat would be reported by the gc_details command.\n * noNilcheck suppresses display of nilchecks.\n * noEscape suppresses escape choices.\n * noInline suppresses inlining choices.\n * noBounds suppresses bounds checking diagnostics.\n",
+              "default": {},
+              "scope": "resource"
+            },
+            "staticcheck": {
+              "type": "boolean",
+              "markdownDescription": "(Experimental) staticcheck enables additional analyses from staticcheck.io.\n",
+              "default": false,
+              "scope": "resource"
+            },
+            "semanticTokens": {
+              "type": "boolean",
+              "markdownDescription": "(Experimental) semanticTokens controls whether the LSP server will send\nsemantic tokens to the client.\n",
+              "default": false,
+              "scope": "resource"
+            },
+            "expandWorkspaceToModule": {
+              "type": "boolean",
+              "markdownDescription": "(Experimental) expandWorkspaceToModule instructs `gopls` to adjust the scope of the\nworkspace to find the best available module root. `gopls` first looks for\na go.mod file in any parent directory of the workspace folder, expanding\nthe scope to that directory if it exists. If no viable parent directory is\nfound, gopls will check if there is exactly one child directory containing\na go.mod file, narrowing the scope to that directory if it exists.\n",
+              "default": true,
+              "scope": "resource"
+            },
+            "experimentalWorkspaceModule": {
+              "type": "boolean",
+              "markdownDescription": "(Experimental) experimentalWorkspaceModule opts a user into the experimental support\nfor multi-module workspaces.\n",
+              "default": false,
+              "scope": "resource"
+            },
+            "experimentalDiagnosticsDelay": {
+              "type": "string",
+              "markdownDescription": "(Experimental) experimentalDiagnosticsDelay controls the amount of time that gopls waits\nafter the most recent file modification before computing deep diagnostics.\nSimple diagnostics (parsing and type-checking) are always run immediately\non recently modified packages.\n\nThis option must be set to a valid duration string, for example `\"250ms\"`.\n",
+              "default": "250ms",
+              "scope": "resource"
+            },
+            "experimentalPackageCacheKey": {
+              "type": "boolean",
+              "markdownDescription": "(Experimental) experimentalPackageCacheKey controls whether to use a coarser cache key\nfor package type information to increase cache hits. This setting removes\nthe user's environment, build flags, and working directory from the cache\nkey, which should be a safe change as all relevant inputs into the type\nchecking pass are already hashed into the key. This is temporarily guarded\nby an experiment because caching behavior is subtle and difficult to\ncomprehensively test.\n",
+              "default": true,
+              "scope": "resource"
+            },
+            "allowModfileModifications": {
+              "type": "boolean",
+              "markdownDescription": "(Experimental) allowModfileModifications disables -mod=readonly, allowing imports from\nout-of-scope modules. This option will eventually be removed.\n",
+              "default": false,
+              "scope": "resource"
+            },
+            "allowImplicitNetworkAccess": {
+              "type": "boolean",
+              "markdownDescription": "(Experimental) allowImplicitNetworkAccess disables GOPROXY=off, allowing implicit module\ndownloads rather than requiring user action. This option will eventually\nbe removed.\n",
+              "default": false,
+              "scope": "resource"
+            },
+            "verboseOutput": {
+              "type": "boolean",
+              "markdownDescription": "(For Debugging) verboseOutput enables additional debug logging.\n",
+              "default": false,
+              "scope": "resource"
+            },
+            "completionBudget": {
+              "type": "string",
+              "markdownDescription": "(For Debugging) completionBudget is the soft latency goal for completion requests. Most\nrequests finish in a couple milliseconds, but in some cases deep\ncompletions can take much longer. As we use up our budget we\ndynamically reduce the search scope to ensure we return timely\nresults. Zero means unlimited.\n",
+              "default": "100ms",
+              "scope": "resource"
+            }
+          }
         }
       }
     },
diff --git a/snippets/go.json b/snippets/go.json
index 79c7fa6..53d0967 100644
--- a/snippets/go.json
+++ b/snippets/go.json
@@ -20,6 +20,11 @@
 			"body": "const (\n\t${1:name} = ${2:value}\n)",
 			"description": "Snippet for a constant block"
 		},
+		"type function declaration": {
+			"prefix": "tyf",
+			"body": "type ${1:name} func($3) $4",
+			"description": "Snippet for a type function declaration"
+		},
 		"type interface declaration": {
 			"prefix": "tyi",
 			"body": "type ${1:name} interface {\n\t$0\n}",
diff --git a/src/debugAdapter/goDebug.ts b/src/debugAdapter/goDebug.ts
index 84e36d7..788af3d 100644
--- a/src/debugAdapter/goDebug.ts
+++ b/src/debugAdapter/goDebug.ts
@@ -276,6 +276,7 @@
 	trace?: 'verbose' | 'log' | 'error';
 	backend?: string;
 	output?: string;
+	substitutePath?: {from: string, to: string}[];
 	/** Delve LoadConfig parameters */
 	dlvLoadConfig?: LoadConfig;
 	dlvToolPath: string;
@@ -307,6 +308,7 @@
 	host?: string;
 	trace?: 'verbose' | 'log' | 'error';
 	backend?: string;
+	substitutePath?: {from: string, to: string}[];
 	/** Delve LoadConfig parameters */
 	dlvLoadConfig?: LoadConfig;
 	dlvToolPath: string;
@@ -369,6 +371,10 @@
 	return filePath;
 }
 
+function normalizeSeparators(filePath: string): string {
+	return filePath.replace(/\/|\\/g, '/');
+}
+
 function getBaseName(filePath: string) {
 	return filePath.includes('/') ? path.basename(filePath) : path.win32.basename(filePath);
 }
@@ -853,6 +859,9 @@
 	private localToRemotePathMapping = new Map<string, string>();
 	private remoteToLocalPathMapping = new Map<string, string>();
 
+	// TODO(suzmue): Use delve's implementation of substitute-path.
+	private substitutePath: {from: string, to: string}[];
+
 	private showGlobalVariables: boolean = false;
 
 	private continueEpoch = 0;
@@ -1038,7 +1047,7 @@
 	}
 
 	protected async toDebuggerPath(filePath: string): Promise<string> {
-		if (this.delve.remotePath.length === 0) {
+		if (this.substitutePath.length === 0) {
 			if (this.delve.isRemoteDebugging) {
 				// The user trusts us to infer the remote path mapping!
 				await this.initializeRemotePackagesAndSources();
@@ -1047,14 +1056,28 @@
 					return matchedRemoteFile;
 				}
 			}
+
 			return this.convertClientPathToDebugger(filePath);
 		}
 
 		// The filePath may have a different path separator than the localPath
-		// So, update it to use the same separator as the remote path to ease
-		// in replacing the local path in it with remote path
-		filePath = filePath.replace(/\/|\\/g, this.remotePathSeparator);
-		return filePath.replace(this.delve.program.replace(/\/|\\/g, this.remotePathSeparator), this.delve.remotePath);
+		// So, update it to use the same separator for ease in path replacement.
+		filePath = normalizeSeparators(filePath);
+		let substitutedPath = filePath;
+		let substituteRule: {from: string, to: string};
+		this.substitutePath.forEach((value) => {
+			if (filePath.startsWith(value.from)) {
+				if (!!substituteRule) {
+					log(`Substitutition rule ${value.from}:${value.to} applies to local path ${filePath} but it was already mapped to debugger path using rule ${substituteRule.from}:${substituteRule.to}`);
+					return;
+				}
+				substitutedPath = filePath.replace(value.from, value.to);
+				substituteRule = {from: value.from, to: value.to};
+			}
+		});
+		filePath = substitutedPath;
+
+		return filePath = filePath.replace(/\/|\\/g, this.remotePathSeparator);
 	}
 
 	/**
@@ -1203,7 +1226,7 @@
 	 * have been initialized.
 	 */
 	protected toLocalPath(pathToConvert: string): string {
-		if (this.delve.remotePath.length === 0) {
+		if (this.substitutePath.length === 0) {
 			// User trusts use to infer the path
 			if (this.delve.isRemoteDebugging) {
 				const inferredPath = this.inferLocalPathFromRemotePath(pathToConvert);
@@ -1211,11 +1234,28 @@
 					return inferredPath;
 				}
 			}
+
 			return this.convertDebuggerPathToClient(pathToConvert);
 		}
 
+		// If there is a substitutePath mapping, then we replace the path.
+		pathToConvert = normalizeSeparators(pathToConvert);
+		let substitutedPath = pathToConvert;
+		let substituteRule: {from: string, to: string};
+		this.substitutePath.forEach((value) => {
+			if (pathToConvert.startsWith(value.to)) {
+				if (!!substituteRule) {
+					log(`Substitutition rule ${value.from}:${value.to} applies to debugger path ${pathToConvert} but it was already mapped to local path using rule ${substituteRule.from}:${substituteRule.to}`);
+					return;
+				}
+				substitutedPath = pathToConvert.replace(value.to, value.from);
+				substituteRule = {from: value.from, to: value.to};
+			}
+		});
+		pathToConvert = substitutedPath;
+
 		// When the pathToConvert is under GOROOT or Go module cache, replace path appropriately
-		if (!pathToConvert.startsWith(this.delve.remotePath)) {
+		if (!substituteRule) {
 			// Fix for https://github.com/Microsoft/vscode-go/issues/1178
 			const index = pathToConvert.indexOf(`${this.remotePathSeparator}src${this.remotePathSeparator}`);
 			const goroot = this.getGOROOT();
@@ -1239,7 +1279,6 @@
 			}
 		}
 		return pathToConvert
-			.replace(this.delve.remotePath, this.delve.program)
 			.split(this.remotePathSeparator)
 			.join(this.localPathSeparator);
 	}
@@ -1835,6 +1874,7 @@
 		}
 
 		this.localPathSeparator = findPathSeparator(localPath);
+		this.substitutePath = [];
 		if (args.remotePath.length > 0) {
 			this.remotePathSeparator = findPathSeparator(args.remotePath);
 
@@ -1857,6 +1897,25 @@
 			) {
 				args.remotePath = args.remotePath.substring(0, args.remotePath.length - 1);
 			}
+
+			// Make the remotePath mapping the first one in substitutePath
+			// so that it will take precedence over the other mappings.
+			this.substitutePath.push({
+				from: normalizeSeparators(localPath),
+				to: normalizeSeparators(args.remotePath)
+			});
+		}
+
+		if (!!args.substitutePath) {
+			args.substitutePath.forEach((value) => {
+				if (!this.remotePathSeparator) {
+					this.remotePathSeparator = findPathSeparator(value.to);
+				}
+				this.substitutePath.push({
+					from: normalizeSeparators(value.from),
+					to: normalizeSeparators(value.to)
+				});
+			});
 		}
 
 		// Launch the Delve debugger on the program
diff --git a/src/goDebugConfiguration.ts b/src/goDebugConfiguration.ts
index 09a44fc..7ac8851 100644
--- a/src/goDebugConfiguration.ts
+++ b/src/goDebugConfiguration.ts
@@ -19,21 +19,135 @@
 export class GoDebugConfigurationProvider implements vscode.DebugConfigurationProvider {
 	constructor(private defaultDebugAdapterType: string = 'go') { }
 
-	public provideDebugConfigurations(
+	public async provideDebugConfigurations(
 		folder: vscode.WorkspaceFolder | undefined,
 		token?: vscode.CancellationToken
-	): vscode.DebugConfiguration[] {
-		return [
+	): Promise<vscode.DebugConfiguration[] | undefined> {
+		return await this.pickConfiguration();
+	}
+
+	public async pickConfiguration(): Promise<vscode.DebugConfiguration[]> {
+		const debugConfigurations = [
 			{
-				name: 'Launch',
-				type: this.defaultDebugAdapterType,
-				request: 'launch',
-				mode: 'auto',
-				program: '${fileDirname}',
-				env: {},
-				args: []
+				label: 'Go: Launch package',
+				description: 'Debug the package in the program attribute',
+				config: {
+					name: 'Launch Package',
+					type: this.defaultDebugAdapterType,
+					request: 'launch',
+					mode: 'debug',
+					program: '${workspaceFolder}'
+				},
+			},
+			{
+				label: 'Go: Launch file',
+				description: 'Debug the file in the program attribute',
+				config: {
+					name: 'Launch file',
+					type: 'go',
+					request: 'launch',
+					mode: 'debug',
+					program: '${file}'
+				}
+			},
+			{
+				label: 'Go: Launch test package',
+				description: 'Debug the test package in the program attribute',
+				config: {
+					name: 'Launch test package',
+					type: 'go',
+					request: 'launch',
+					mode: 'test',
+					program: '${workspaceFolder}'
+				}
+			},
+			{
+				label: 'Go: Launch test function',
+				description: 'Debug the test function in the args, ensure program attributes points to right package',
+				config: {
+					name: 'Launch test function',
+					type: 'go',
+					request: 'launch',
+					mode: 'test',
+					program: '${workspaceFolder}',
+					args: [
+						'-test.run',
+						'MyTestFunction'
+					]
+				},
+				fill: async (config: vscode.DebugConfiguration) => {
+					const testFunc = await vscode.window.showInputBox({
+						placeHolder: 'MyTestFunction',
+						prompt: 'Name of the function to test'
+					});
+					if (!!testFunc) {
+						config.args = [
+							'-test.run',
+							testFunc
+						];
+					}
+				}
+			},
+			{
+				label: 'Go: Attach to local process',
+				description: 'Attach to an existing process by process ID',
+				config: {
+					name: 'Attach to Process',
+					type: 'go',
+					request: 'attach',
+					mode: 'local',
+					processId: 0
+				}
+			},
+			{
+				label: 'Go: Connect to server',
+				description: 'Connect to a remote headless debug server',
+				config: {
+					name: 'Connect to server',
+					type: 'go',
+					request: 'attach',
+					mode: 'remote',
+					remotePath: '${workspaceFolder}',
+					port: 2345,
+					host: '127.0.0.1'
+				},
+				fill: async (config: vscode.DebugConfiguration) => {
+					const host = await vscode.window.showInputBox({
+						prompt: 'Enter hostname',
+						value: '127.0.0.1',
+					});
+					if (!!host) {
+						config.host = host;
+					}
+					const port = Number(await vscode.window.showInputBox({
+						prompt: 'Enter port',
+						value: '2345',
+						validateInput: (value: string) => {
+							if (isNaN(Number(value))) {
+								return 'Please enter a number.';
+							}
+							return '';
+						}
+					}));
+					if (!!port) {
+						config.port = port;
+					}
+
+				}
 			}
 		];
+
+		const choice = await vscode.window.showQuickPick(debugConfigurations, {
+			placeHolder: 'Choose debug configuration',
+		});
+		if (!choice) {
+			return [];
+		}
+
+		if (!!choice.fill) {
+			await choice.fill(choice.config);
+		}
+		return [choice.config];
 	}
 
 	public resolveDebugConfiguration(
diff --git a/src/goEnvironmentStatus.ts b/src/goEnvironmentStatus.ts
index b96fd03..1f6073b 100644
--- a/src/goEnvironmentStatus.ts
+++ b/src/goEnvironmentStatus.ts
@@ -17,7 +17,9 @@
 import { logVerbose } from './goLogging';
 import { addGoStatus, goEnvStatusbarItem, outputChannel, removeGoStatus } from './goStatus';
 import { getFromGlobalState, getFromWorkspaceState, updateGlobalState, updateWorkspaceState } from './stateUtils';
-import { getBinPath, getGoConfig, getGoVersion, getTempFilePath, GoVersion, rmdirRecursive } from './util';
+import {
+	getBinPath, getCheckForToolsUpdatesConfig, getGoConfig, getGoVersion,
+	getTempFilePath, GoVersion, rmdirRecursive } from './util';
 import {
 	correctBinname,
 	executableFileExists,
@@ -517,7 +519,8 @@
 
 export async function offerToInstallLatestGoVersion() {
 	const goConfig = getGoConfig();
-	if (!goConfig['useGoProxyToCheckForToolUpdates']) {
+	const checkForUpdate = getCheckForToolsUpdatesConfig(goConfig);
+	if (checkForUpdate === 'off' || checkForUpdate === 'local') {  // 'proxy' or misconfiguration..
 		return;
 	}
 
diff --git a/src/goInstallTools.ts b/src/goInstallTools.ts
index 231c00b..107d450 100644
--- a/src/goInstallTools.ts
+++ b/src/goInstallTools.ts
@@ -49,7 +49,7 @@
 
 export async function installAllTools(updateExistingToolsOnly: boolean = false) {
 	const goVersion = await getGoVersion();
-	let allTools = getConfiguredTools(goVersion);
+	let allTools = getConfiguredTools(goVersion, getGoConfig());
 
 	// exclude tools replaced by alternateTools.
 	const alternateTools: { [key: string]: string } = getGoConfig().get('alternateTools');
@@ -95,10 +95,14 @@
  * @param missing array of tool names and optionally, their versions to be installed.
  *                If a tool's version is not specified, it will install the latest.
  * @param goVersion version of Go that affects how to install the tool. (e.g. modules vs legacy GOPATH mode)
+ * @returns a list of tools that failed to install.
  */
-export async function installTools(missing: ToolAtVersion[], goVersion: GoVersion): Promise<void> {
+export async function installTools(
+	missing: ToolAtVersion[],
+	goVersion: GoVersion
+): Promise<{ tool: ToolAtVersion, reason: string }[]> {
 	if (!missing) {
-		return;
+		return [];
 	}
 
 	outputChannel.show();
@@ -174,6 +178,7 @@
 			outputChannel.appendLine(`${failure.tool.name}: ${failure.reason} `);
 		}
 	}
+	return failures;
 }
 
 export async function installTool(
@@ -199,13 +204,15 @@
 		const writeFile = util.promisify(fs.writeFile);
 		await writeFile(tmpGoModFile, 'module tools');
 	} else {
-		envForTools['GO111MODULE'] = 'off';
+		env['GO111MODULE'] = 'off';
 	}
 	// Some users use direnv-like setup where the choice of go is affected by
 	// the current directory path. In order to avoid choosing a different go,
 	// we will explicitly use `GOROOT/bin/go` instead of goVersion.binaryPath
 	// (which can be a wrapper script that switches 'go').
-	const goBinary = path.join(getCurrentGoRoot(), 'bin', correctBinname('go'));
+	const goBinary = getCurrentGoRoot() ?
+		path.join(getCurrentGoRoot(), 'bin', correctBinname('go')) :
+		goVersion.binaryPath;
 
 	// Build the arguments list for the tool installation.
 	const args = ['get', '-v'];
@@ -226,7 +233,7 @@
 	}
 	args.push(importPath);
 
-	let output: string;
+	let output: string = 'no output';
 	let result: string = '';
 	try {
 		const opts = {
@@ -238,13 +245,10 @@
 		output = `${stdout} ${stderr}`;
 		logVerbose(`install: %s %s\n%s%s`, goBinary, args.join(' '), stdout, stderr);
 
-		// TODO(rstambler): Figure out why this happens and maybe delete it.
-		if (stderr.indexOf('unexpected directory layout:') > -1) {
-			await execFile(goBinary, args, opts);
-		} else if (hasModSuffix(tool)) {
-			const gopath = env['GOPATH'];
+		if (hasModSuffix(tool)) {  // Actual installation of the -gomod tool is done by running go build.
+			const gopath = env['GOBIN'] || env['GOPATH'];
 			if (!gopath) {
-				return `GOPATH not configured in environment`;
+				return `GOBIN/GOPATH not configured in environment`;
 			}
 			const destDir = gopath.split(path.delimiter)[0];
 			const outputFile = path.join(destDir, 'bin', process.platform === 'win32' ? `${tool.name}.exe` : tool.name);
@@ -254,7 +258,8 @@
 		outputChannel.appendLine(`Installing ${importPath} (${toolInstallPath}) SUCCEEDED`);
 	} catch (e) {
 		outputChannel.appendLine(`Installing ${importPath} FAILED`);
-		result = `failed to install ${tool.name}(${importPath}): ${e} ${output} `;
+		outputChannel.appendLine(`${JSON.stringify(e, null, 1)}`);
+		result = `failed to install ${tool.name}(${importPath}): ${e} ${output}`;
 	}
 
 	// Delete the temporary installation directory.
@@ -452,8 +457,9 @@
 				title: 'Show',
 				command() {
 					outputChannel.clear();
+					outputChannel.show();
 					outputChannel.appendLine('Below tools are needed for the basic features of the Go extension.');
-					missing.forEach((x) => outputChannel.appendLine(x.name));
+					missing.forEach((x) => outputChannel.appendLine(`  ${x.name}`));
 				}
 			};
 			vscode.window
@@ -498,7 +504,7 @@
 }
 
 function getMissingTools(goVersion: GoVersion): Promise<Tool[]> {
-	const keys = getConfiguredTools(goVersion);
+	const keys = getConfiguredTools(goVersion, getGoConfig());
 	return Promise.all<Tool>(
 		keys.map(
 			(tool) =>
@@ -515,18 +521,15 @@
 let suggestedDownloadGo = false;
 
 async function suggestDownloadGo() {
+	const msg = `Failed to find the "go" binary in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath}).` +
+			`Check PATH, or Install Go and reload the window. ` +
+			`If PATH isn't what you expected, see https://github.com/golang/vscode-go/issues/971`;
 	if (suggestedDownloadGo) {
-		vscode.window.showErrorMessage(
-			`Failed to find the "go" binary in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath}).`
-		);
+		vscode.window.showErrorMessage(msg);
 		return;
 	}
 
-	const choice = await vscode.window.showErrorMessage(
-		`Failed to find the "go" binary in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath}). ` +
-		`Check PATH, or Install Go and reload the window.`,
-		'Go to Download Page'
-	);
+	const choice = await vscode.window.showErrorMessage(msg, 'Go to Download Page');
 	if (choice === 'Go to Download Page') {
 		vscode.env.openExternal(vscode.Uri.parse('https://golang.org/dl/'));
 	}
diff --git a/src/goLanguageServer.ts b/src/goLanguageServer.ts
index 41a2162..8ef78b9 100644
--- a/src/goLanguageServer.ts
+++ b/src/goLanguageServer.ts
@@ -41,7 +41,7 @@
 import { GoHoverProvider } from './goExtraInfo';
 import { GoDocumentFormattingEditProvider } from './goFormat';
 import { GoImplementationProvider } from './goImplementations';
-import { promptForMissingTool, promptForUpdatingTool } from './goInstallTools';
+import { installTools, promptForMissingTool, promptForUpdatingTool } from './goInstallTools';
 import { parseLiveFile } from './goLiveErrors';
 import { restartLanguageServer } from './goMain';
 import { GO_MODE } from './goMode';
@@ -52,10 +52,18 @@
 import { outputChannel, updateLanguageServerIconGoStatusBar } from './goStatus';
 import { GoCompletionItemProvider } from './goSuggest';
 import { GoWorkspaceSymbolProvider } from './goSymbol';
-import { getTool, Tool } from './goTools';
+import { getTool, Tool, ToolAtVersion } from './goTools';
 import { GoTypeDefinitionProvider } from './goTypeDefinition';
 import { getFromGlobalState, updateGlobalState } from './stateUtils';
-import { getBinPath, getCurrentGoPath, getGoConfig, getGoplsConfig, getWorkspaceFolderPath } from './util';
+import {
+	getBinPath,
+	getCheckForToolsUpdatesConfig,
+	getCurrentGoPath,
+	getGoConfig,
+	getGoplsConfig,
+	getGoVersion,
+	getWorkspaceFolderPath
+} from './util';
 import { getToolFromToolPath } from './utils/pathUtils';
 
 export interface LanguageServerConfig {
@@ -70,7 +78,7 @@
 		diagnostics: boolean;
 		documentLink: boolean;
 	};
-	checkForUpdates: boolean;
+	checkForUpdates: string;
 }
 
 // Global variables used for management of the language client.
@@ -81,6 +89,9 @@
 let latestConfig: LanguageServerConfig;
 export let serverOutputChannel: vscode.OutputChannel;
 export let languageServerIsRunning = false;
+// TODO: combine languageServerIsRunning & languageServerStartInProgress
+// as one languageServerStatus variable.
+let languageServerStartInProgress = false;
 let serverTraceChannel: vscode.OutputChannel;
 let crashCount = 0;
 
@@ -98,28 +109,78 @@
 // startLanguageServerWithFallback starts the language server, if enabled,
 // or falls back to the default language providers.
 export async function startLanguageServerWithFallback(ctx: vscode.ExtensionContext, activation: boolean) {
-	const cfg = buildLanguageServerConfig(getGoConfig());
 
-	// If the language server is gopls, we enable a few additional features.
-	// These include prompting for updates and surveys.
-	if (activation && cfg.serverName === 'gopls') {
-		const tool = getTool(cfg.serverName);
-		if (tool) {
-			scheduleGoplsSuggestions(tool);
+	for (const folder of vscode.workspace.workspaceFolders || []) {
+		if (folder.uri.scheme === 'vsls') {
+			outputChannel.appendLine(`Language service on the guest side is disabled. ` +
+				`The server-side language service will provide the language features.`);
+			return;
 		}
 	}
 
-	const started = await startLanguageServer(ctx, cfg);
+	if (!activation && languageServerStartInProgress) {
+		console.log('language server restart is already in progress...');
+		return;
+	}
+	languageServerStartInProgress = true;
 
-	// If the server has been disabled, or failed to start,
-	// fall back to the default providers, while making sure not to
-	// re-register any providers.
-	if (!started && defaultLanguageProviders.length === 0) {
-		registerDefaultProviders(ctx);
+	const goConfig = getGoConfig();
+	const cfg = buildLanguageServerConfig(goConfig);
+
+	// If the language server is gopls, we enable a few additional features.
+	// These include prompting for updates and surveys.
+	if (cfg.serverName === 'gopls') {
+		const tool = getTool(cfg.serverName);
+		if (tool) {
+			if (activation) {
+				scheduleGoplsSuggestions(tool);
+			}
+
+			// If the language server is turned on because it is enabled by default,
+			// make sure that the user is using a new enough version.
+			if (cfg.enabled && languageServerUsingDefault(goConfig)) {
+				const updated = await forceUpdateGopls(tool, cfg);
+				if (updated) {
+					// restartLanguageServer will be called when the new version of gopls was installed.
+					return;
+				}
+			}
+		}
 	}
 
-	languageServerIsRunning = started;
-	updateLanguageServerIconGoStatusBar(started, cfg.serverName);
+	const progressMsg = languageServerIsRunning ? 'Restarting language service' : 'Starting language service';
+	await vscode.window.withProgress({
+		title: progressMsg,
+		cancellable: !activation,
+		location: vscode.ProgressLocation.Notification,
+	}, async (progress, token) => {
+		let disposable: vscode.Disposable;
+		if (token) {
+			disposable = token.onCancellationRequested(async () => {
+				const choice = await vscode.window.showErrorMessage(
+					'Language service restart request was interrupted and language service may be in a bad state. ' +
+					'Please reload the window.',
+					'Reload Window');
+				if (choice === 'Reload Window') {
+					await vscode.commands.executeCommand('workbench.action.reloadWindow');
+				}
+			});
+		}
+
+		const started = await startLanguageServer(ctx, cfg);
+
+		// If the server has been disabled, or failed to start,
+		// fall back to the default providers, while making sure not to
+		// re-register any providers.
+		if (!started && defaultLanguageProviders.length === 0) {
+			registerDefaultProviders(ctx);
+		}
+
+		if (disposable) { disposable.dispose(); }
+		languageServerIsRunning = started;
+		updateLanguageServerIconGoStatusBar(started, cfg.serverName);
+		languageServerStartInProgress = false;
+	});
 }
 
 // scheduleGoplsSuggestions sets timeouts for the various gopls-specific
@@ -229,7 +290,8 @@
 // buildLanguageClient returns a language client built using the given language server config.
 // The returned language client need to be started before use.
 export async function buildLanguageClient(cfg: BuildLanguageClientOption): Promise<LanguageClient> {
-	let goplsWorkspaceConfig = getGoplsConfig();
+	let goplsWorkspaceConfig = getGoplsConfig() as any;
+	goplsWorkspaceConfig = filterDefaultConfigValues(goplsWorkspaceConfig, 'gopls', undefined);
 	goplsWorkspaceConfig = await adjustGoplsWorkspaceConfiguration(cfg, goplsWorkspaceConfig);
 	const c = new LanguageClient(
 		'go',  // id
@@ -433,13 +495,22 @@
 				workspace: {
 					configuration: async (params: ConfigurationParams, token: CancellationToken, next: ConfigurationRequest.HandlerSignature): Promise<any[] | ResponseError<void>> => {
 						const configs = await next(params, token);
-						if (!Array.isArray(configs)) {
+						if (!configs || !Array.isArray(configs)) {
 							return configs;
 						}
-						for (let workspaceConfig of configs) {
-							workspaceConfig = await adjustGoplsWorkspaceConfiguration(cfg, workspaceConfig);
+						const ret = [] as any[];
+						for (let i = 0; i < configs.length; i++) {
+							let workspaceConfig = configs[i];
+							if (!!workspaceConfig && typeof workspaceConfig === 'object') {
+								const scopeUri = params.items[i].scopeUri;
+								const resource = scopeUri ? vscode.Uri.parse(scopeUri) : undefined;
+								const section = params.items[i].section;
+								workspaceConfig = filterDefaultConfigValues(workspaceConfig, section, resource);
+								workspaceConfig = await adjustGoplsWorkspaceConfiguration(cfg, workspaceConfig);
+							}
+							ret.push(workspaceConfig);
 						}
-						return configs;
+						return ret;
 					},
 				},
 			}
@@ -448,6 +519,43 @@
 	return c;
 }
 
+// filterDefaultConfigValues removes the entries filled based on the default values
+// and selects only those the user explicitly specifies in their settings.
+// This assumes workspaceConfig is a non-null(undefined) object type.
+// Exported for testing.
+export function filterDefaultConfigValues(workspaceConfig: any, section: string, resource: vscode.Uri): any {
+	if (!workspaceConfig) {
+		return workspaceConfig;
+	}
+
+	const dot = section?.lastIndexOf('.') || -1;
+	const sectionKey = dot >= 0 ? section.substr(0, dot) : section;  // e.g. 'gopls'
+
+	const cfg = vscode.workspace.getConfiguration(sectionKey, resource);
+	const filtered = {} as { [key: string]: any };
+	for (const [key, value] of Object.entries(workspaceConfig)) {
+		if (typeof value === 'function') {
+			continue;
+		}
+		const c = cfg.inspect(key);
+		// select only the field whose current value comes from non-default setting.
+		if (!c || !deepEqual(c.defaultValue, value) ||
+			// c.defaultValue !== value would be most likely sufficient, except
+			// when gopls' default becomes different from extension's default.
+			// So, we also forward the key if ever explicitely stated in one of the
+			// settings layers.
+			c.globalLanguageValue !== undefined ||
+			c.globalValue !== undefined ||
+			c.workspaceFolderLanguageValue !== undefined ||
+			c.workspaceFolderValue !== undefined ||
+			c.workspaceLanguageValue !== undefined ||
+			c.workspaceValue !== undefined) {
+			filtered[key] = value;
+		}
+	}
+	return filtered;
+}
+
 // adjustGoplsWorkspaceConfiguration adds any extra options to the gopls
 // config. Right now, the only extra option is enabling experiments for the
 // Nightly extension.
@@ -591,7 +699,7 @@
 			documentLink: goConfig['languageServerExperimentalFeatures']['documentLink']
 		},
 		env: toolExecutionEnvironment(),
-		checkForUpdates: goConfig['useGoProxyToCheckForToolUpdates']
+		checkForUpdates: getCheckForToolsUpdatesConfig(goConfig),
 	};
 	// Don't look for the path if the server is not enabled.
 	if (!cfg.enabled) {
@@ -678,7 +786,7 @@
 	cfg: LanguageServerConfig,
 ): Promise<semver.SemVer> {
 	// Only support updating gopls for now.
-	if (tool.name !== 'gopls') {
+	if (tool.name !== 'gopls' || cfg.checkForUpdates === 'off') {
 		return null;
 	}
 
@@ -693,7 +801,7 @@
 	}
 
 	// Get the latest gopls version. If it is for nightly, using the prereleased version is ok.
-	let latestVersion = cfg.checkForUpdates ? await getLatestGoplsVersion(tool) : tool.latestVersion;
+	let latestVersion = cfg.checkForUpdates === 'local' ? tool.latestVersion : await getLatestGoplsVersion(tool);
 
 	// If we failed to get the gopls version, pick the one we know to be latest at the time of this extension's last update
 	if (!latestVersion) {
@@ -729,6 +837,50 @@
 	return semver.lt(usersVersionSemver, latestVersion) ? latestVersion : null;
 }
 
+/**
+ * forceUpdateGopls will make sure the user is using the latest version of `gopls`,
+ * when go.useLanguageServer is changed to true by default.
+ *
+ * @param tool	Object of type `Tool` for gopls tool.
+ * @param cfg	Object of type `Language Server Config` for the users language server
+ * 				configuration.
+ * @returns		true if the tool was updated
+ */
+async function forceUpdateGopls(
+	tool: Tool,
+	cfg: LanguageServerConfig,
+): Promise<boolean> {
+	const forceUpdatedGoplsKey = 'forceUpdateForGoplsOnDefault';
+	// forceUpdated is true when the process of updating has been succesfully completed.
+	const forceUpdated = getFromGlobalState(forceUpdatedGoplsKey, false);
+	// TODO: If we want to force update again, switch this to be a comparison for a newer version.
+	if (!!forceUpdated) {
+		return false;
+	}
+	// Update the state to the latest version to show the last version that was checked.
+	await updateGlobalState(forceUpdatedGoplsKey, tool.latestVersion);
+
+	const latestVersion = await shouldUpdateLanguageServer(tool, cfg);
+
+	if (!latestVersion) {
+		// The user is using a new enough version
+		return false;
+	}
+
+	const toolVersion = { ...tool, version: latestVersion }; // ToolWithVersion
+	const goVersion = await getGoVersion();
+	const failures = await installTools([toolVersion], goVersion);
+
+	// We successfully updated to the latest version.
+	if (failures.length === 0) {
+		return true;
+	}
+
+	// Failed to install the new version of gopls, warn the user.
+	vscode.window.showWarningMessage(`'gopls' is now enabled by default and you are using an old version. Please [update 'gopls'](https://github.com/golang/tools/blob/master/gopls/doc/user.md#installation) and restart the language server for the best experience.`);
+	return false;
+}
+
 // Copied from src/cmd/go/internal/modfetch.go.
 const pseudoVersionRE = /^v[0-9]+\.(0\.0-|\d+\.\d+-([^+]*\.)?0\.)\d{14}-[A-Za-z0-9]+(\+incompatible)?$/;
 
@@ -1066,8 +1218,8 @@
 
 export const goplsSurveyConfig = 'goplsSurveyConfig';
 
-function getSurveyConfig(): SurveyConfig {
-	const saved = getFromGlobalState(goplsSurveyConfig);
+function getSurveyConfig(surveyConfigKey = goplsSurveyConfig): SurveyConfig {
+	const saved = getFromGlobalState(surveyConfigKey);
 	if (saved === undefined) {
 		return {};
 	}
@@ -1180,22 +1332,26 @@
 			}
 			// Get the user's version in case the update prompt above failed.
 			const usersGoplsVersion = await getLocalGoplsVersion(latestConfig);
+			const extInfo = getExtensionInfo();
 			const settings = latestConfig.flags.join(' ');
 			const title = `gopls: automated issue report (${errKind})`;
-			const sanitizedLog = collectGoplsLog();
+			const { sanitizedLog, failureReason } = await collectGoplsLog();
 			const goplsLog = (sanitizedLog) ?
 				`<pre>${sanitizedLog}</pre>` :
-				`
-Please attach the stack trace from the crash.
+				`Please attach the stack trace from the crash.
 A window with the error message should have popped up in the lower half of your screen.
 Please copy the stack trace and error messages from that window and paste it in this issue.
 
 <PASTE STACK TRACE HERE>
+
+Failed to auto-collect gopls trace: ${failureReason}.
 `;
 
 			const body = `
 gopls version: ${usersGoplsVersion}
 gopls flags: ${settings}
+extension version: ${extInfo.version}
+environment: ${extInfo.appName}
 
 ATTENTION: PLEASE PROVIDE THE DETAILS REQUESTED BELOW.
 
@@ -1279,35 +1435,48 @@
 	}
 }
 
-function collectGoplsLog(): string {
+function sleep(ms: number) {
+	return new Promise((resolve) => setTimeout(resolve, ms));
+}
+
+async function collectGoplsLog(): Promise<{ sanitizedLog?: string; failureReason?: string; }> {
 	serverOutputChannel.show();
 	// Find the logs in the output channel. There is no way to read
 	// an output channel directly, but we can find the open text
 	// document, since we just surfaced the output channel to the user.
 	// See https://github.com/microsoft/vscode/issues/65108.
 	let logs: string;
-	for (const doc of vscode.workspace.textDocuments) {
-		if (doc.languageId !== 'Log') {
-			continue;
+	for (let i = 0; i < 3; i++) {
+		// try a couple of times until successfully finding the channel.
+		for (const doc of vscode.workspace.textDocuments) {
+			if (doc.languageId !== 'Log') {
+				continue;
+			}
+			if (doc.isDirty || doc.isClosed) {
+				continue;
+			}
+			// The document's name should look like 'extension-output-#X'.
+			if (doc.fileName.indexOf('extension-output-') === -1) {
+				continue;
+			}
+			logs = doc.getText();
+			break;
 		}
-		if (doc.isDirty || doc.isClosed) {
-			continue;
+		if (!!logs) {
+			break;
 		}
-		// The document's name should look like 'extension-output-#X'.
-		if (doc.fileName.indexOf('extension-output-') === -1) {
-			continue;
-		}
-		logs = doc.getText();
-		break;
+		// sleep a bit before the next try. The choice of the sleep time is arbitrary.
+		await sleep((i + 1) * 10);
 	}
+
 	return sanitizeGoplsTrace(logs);
 }
 
 // capture only panic stack trace and the initialization error message.
 // exported for testing.
-export function sanitizeGoplsTrace(logs?: string): string {
+export function sanitizeGoplsTrace(logs?: string): { sanitizedLog?: string, failureReason?: string } {
 	if (!logs) {
-		return '';
+		return { failureReason: 'no gopls log' };
 	}
 	const panicMsgBegin = logs.lastIndexOf('panic: ');
 	if (panicMsgBegin > -1) {  // panic message was found.
@@ -1330,31 +1499,41 @@
 				}
 			).join('\n');
 
-			return sanitized;
+			if (sanitized) {
+				return { sanitizedLog: sanitized };
+			}
+			return { failureReason: 'empty panic trace' };
 		}
+		return { failureReason: 'incomplete panic trace' };
 	}
 	const initFailMsgBegin = logs.lastIndexOf('Starting client failed');
 	if (initFailMsgBegin > -1) {  // client start failed. Capture up to the 'Code:' line.
 		const initFailMsgEnd = logs.indexOf('Code: ', initFailMsgBegin);
 		if (initFailMsgEnd > -1) {
 			const lineEnd = logs.indexOf('\n', initFailMsgEnd);
-			return lineEnd > -1 ? logs.substr(initFailMsgBegin, lineEnd - initFailMsgBegin) : logs.substr(initFailMsgBegin);
+			return { sanitizedLog: lineEnd > -1 ? logs.substr(initFailMsgBegin, lineEnd - initFailMsgBegin) : logs.substr(initFailMsgBegin) };
 		}
 	}
-	return '';
+	if (logs.lastIndexOf('Usage: gopls') > -1) {
+		return { failureReason: 'incorrect gopls command usage' };
+	}
+	return { failureReason: 'unrecognized crash pattern' };
 }
 
 export async function promptForLanguageServerDefaultChange(cfg: vscode.WorkspaceConfiguration) {
+	const useLanguageServer = cfg.inspect<boolean>('useLanguageServer');
+	if (!languageServerUsingDefault(cfg)) {
+		if (!cfg['useLanguageServer']) {  // ask users who explicitly disabled.
+			promptForLanguageServerOptOutSurvey();
+		}
+		return;  // user already explicitly set the field.
+	}
+
 	const promptedForLSDefaultChangeKey = `promptedForLSDefaultChange`;
 	if (getFromGlobalState(promptedForLSDefaultChangeKey, false)) {
 		return;
 	}
 
-	const useLanguageServer = cfg.inspect<boolean>('useLanguageServer');
-	if (useLanguageServer.globalValue !== undefined || useLanguageServer.workspaceValue !== undefined) {
-		return;  // user already explicitly set the field.
-	}
-
 	const selected = await vscode.window.showInformationMessage(
 		`"go.useLanguageServer" is enabled by default. If you need to disable it, please configure in the settings.`,
 		'Open Settings', 'OK');
@@ -1365,3 +1544,53 @@
 	}
 	updateGlobalState(promptedForLSDefaultChangeKey, true);
 }
+
+function languageServerUsingDefault(cfg: vscode.WorkspaceConfiguration): boolean {
+	const useLanguageServer = cfg.inspect<boolean>('useLanguageServer');
+	return useLanguageServer.globalValue === undefined && useLanguageServer.workspaceValue === undefined;
+}
+
+// Prompt users who disabled the language server and ask to file an issue.
+async function promptForLanguageServerOptOutSurvey() {
+	const promptedForLSOptOutSurveyKey = `promptedForLSOptOutSurvey`;
+	const value = getSurveyConfig(promptedForLSOptOutSurveyKey);  // We use only 'prompt' and 'lastDatePrompted' fields.
+
+	if (value?.prompt === false ||
+		(value?.lastDatePrompted && daysBetween(value.lastDatePrompted, new Date()) < 90)) {
+		return;
+	}
+
+	value.lastDatePrompted = new Date();
+
+	const selected = await vscode.window.showInformationMessage(
+		`Looks like you've disabled the language server. Would you be willing to file an issue and tell us why you had to disable it?`,
+		'Yes', 'Not now', 'Never');
+	switch (selected) {
+		case 'Yes':
+			const title = 'gopls: automated issue report (opt out)';
+			const body = `
+Please tell us why you had to disable the language server.
+
+`;
+			const url = `https://github.com/golang/vscode-go/issues/new?title=${title}&labels=upstream-tools&body=${body}`;
+			await vscode.env.openExternal(vscode.Uri.parse(url));
+			break;
+		case 'Never':
+			value.prompt = false;
+			break;
+		default:
+	}
+	updateGlobalState(promptedForLSOptOutSurveyKey, JSON.stringify(value));
+}
+
+interface ExtensionInfo {
+	version: string;  // Extension version
+	appName: string;  // The application name of the editor, like 'VS Code'
+}
+
+function getExtensionInfo(): ExtensionInfo {
+	const version = vscode.extensions.getExtension(extensionId)?.packageJSON?.version;
+	const appName = vscode.env.appName;
+	return { version, appName };
+
+}
diff --git a/src/goMain.ts b/src/goMain.ts
index bba7a75..278be4d 100644
--- a/src/goMain.ts
+++ b/src/goMain.ts
@@ -7,9 +7,7 @@
 'use strict';
 
 import * as path from 'path';
-import { commands } from 'vscode';
 import vscode = require('vscode');
-import { extensionId } from './const';
 import { browsePackages } from './goBrowsePackage';
 import { buildCode } from './goBuild';
 import { check, notifyIfGeneratedFile, removeTestStatus } from './goCheck';
@@ -111,15 +109,15 @@
 		suggestUpdates(ctx);
 		offerToInstallLatestGoVersion();
 		offerToInstallTools();
-		configureLanguageServer(ctx);
+		await configureLanguageServer(ctx);
 
 		if (
+			!languageServerIsRunning &&
 			vscode.window.activeTextEditor &&
 			vscode.window.activeTextEditor.document.languageId === 'go' &&
 			isGoPathSet()
 		) {
 			// Check mod status so that cache is updated and then run build/lint/vet
-			// TODO(hyangah): skip if the language server is used (it will run build too)
 			isModSupported(vscode.window.activeTextEditor.document.uri).then(() => {
 				runBuilds(vscode.window.activeTextEditor.document, getGoConfig());
 			});
@@ -711,8 +709,7 @@
 	};
 
 	// Start the language server, or fallback to the default language providers.
-	startLanguageServerWithFallback(ctx, true);
-
+	return startLanguageServerWithFallback(ctx, true);
 }
 
 function getCurrentGoPathCommand() {
@@ -753,7 +750,7 @@
 	outputChannel.appendLine('');
 
 	const goVersion = await getGoVersion();
-	const allTools = getConfiguredTools(goVersion);
+	const allTools = getConfiguredTools(goVersion, getGoConfig());
 
 	allTools.forEach((tool) => {
 		const toolPath = getBinPath(tool.name);
diff --git a/src/goStatus.ts b/src/goStatus.ts
index bcd4bbb..5e30ede 100644
--- a/src/goStatus.ts
+++ b/src/goStatus.ts
@@ -9,7 +9,7 @@
 import path = require('path');
 import vscode = require('vscode');
 import { formatGoVersion, GoEnvironmentOption, terminalCreationListener } from './goEnvironmentStatus';
-import { buildLanguageServerConfig, getLocalGoplsVersion, serverOutputChannel } from './goLanguageServer';
+import { buildLanguageServerConfig, getLocalGoplsVersion, languageServerIsRunning, serverOutputChannel } from './goLanguageServer';
 import { isGoFile } from './goMode';
 import { getModFolderPath, isModSupported } from './goModules';
 import { getGoConfig, getGoVersion } from './util';
@@ -48,7 +48,7 @@
 
 	// Get the gopls configuration
 	const cfg = buildLanguageServerConfig(getGoConfig());
-	if (cfg.serverName === 'gopls') {
+	if (languageServerIsRunning && cfg.serverName === 'gopls') {
 		const goplsVersion = await getLocalGoplsVersion(cfg);
 		options.push({label: `${languageServerIcon}Open 'gopls' trace`, description: `${goplsVersion}`});
 	}
@@ -102,7 +102,7 @@
 	// Assume if it is configured it is already running, since the
 	// icon will be updated on an attempt to start.
 	const cfg = buildLanguageServerConfig(getGoConfig());
-	updateLanguageServerIconGoStatusBar(true, cfg.serverName);
+	updateLanguageServerIconGoStatusBar(languageServerIsRunning, cfg.serverName);
 
 	showGoStatusBar();
 }
diff --git a/src/goTools.ts b/src/goTools.ts
index 9aad76c..e9387ba 100644
--- a/src/goTools.ts
+++ b/src/goTools.ts
@@ -18,6 +18,7 @@
 	name: string;
 	importPath: string;
 	isImportant: boolean;
+	replacedByGopls?: boolean;
 	description: string;
 
 	// latestVersion and latestVersionTimestamp are hardcoded default values
@@ -107,12 +108,20 @@
 	return tool.name === 'gocode' || tool.name === 'gocode-gomod';
 }
 
-export function getConfiguredTools(goVersion: GoVersion): Tool[] {
+export function getConfiguredTools(goVersion: GoVersion, goConfig: { [key: string]: any }): Tool[] {
+	// If language server is enabled, don't suggest tools that are replaced by gopls.
+	// TODO(github.com/golang/vscode-go/issues/388): decide what to do when
+	// the go version is no longer supported by gopls while the legacy tools are
+	// no longer working (or we remove the legacy language feature providers completely).
+	const useLanguageServer = goConfig['useLanguageServer'] && goVersion.gt('1.11');
+
 	const tools: Tool[] = [];
 	function maybeAddTool(name: string) {
 		const tool = allToolsInformation[name];
 		if (tool) {
-			tools.push(tool);
+			if (!useLanguageServer || !tool.replacedByGopls) {
+				tools.push(tool);
+			}
 		}
 	}
 
@@ -146,8 +155,6 @@
 		maybeAddTool('gocode-gomod');
 	}
 
-	const goConfig = getGoConfig();
-
 	// Add the doc/def tool that was chosen by the user.
 	switch (goConfig['docsTool']) {
 		case 'godoc':
@@ -164,8 +171,10 @@
 	// Add the linter that was chosen by the user.
 	maybeAddTool(goConfig['lintTool']);
 
-	// Add the language server for Go versions > 1.10 if user has choosen to do so.
-	if (goConfig['useLanguageServer'] && goVersion.gt('1.10')) {
+	// Add the language server if the user has chosen to do so.
+	// Even though we arranged this to run after the first attempt to start gopls
+	// this is still useful if we've fail to start gopls.
+	if (useLanguageServer) {
 		maybeAddTool('gopls');
 	}
 
@@ -181,6 +190,7 @@
 		name: 'gocode',
 		importPath: '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');
@@ -204,128 +214,150 @@
 		name: 'gocode-gomod',
 		importPath: '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',
+		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',
+		replacedByGopls: false,  // TODO(github.com/golang/vscode-go/issues/1020): replace with Gopls.
 		isImportant: true,
-		description: 'Go to symbol in file'
+		description: 'Go to symbol in file'  // GoDocumentSymbolProvider, used by 'run test' codelens
 	},
 	'go-symbols': {
 		name: 'go-symbols',
 		importPath: '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',
+		replacedByGopls: true,
 		isImportant: false,
 		description: 'Find all references and Go to implementation of symbols'
 	},
 	'gorename': {
 		name: 'gorename',
 		importPath: 'golang.org/x/tools/cmd/gorename',
+		replacedByGopls: true,
 		isImportant: false,
 		description: 'Rename symbols'
 	},
 	'gomodifytags': {
 		name: 'gomodifytags',
 		importPath: 'github.com/fatih/gomodifytags',
+		replacedByGopls: false,
 		isImportant: false,
 		description: 'Modify tags on structs'
 	},
 	'goplay': {
 		name: 'goplay',
 		importPath: 'github.com/haya14busa/goplay/cmd/goplay',
+		replacedByGopls: false,
 		isImportant: false,
 		description: 'The Go playground'
 	},
 	'impl': {
 		name: 'impl',
 		importPath: 'github.com/josharian/impl',
+		replacedByGopls: false,
 		isImportant: false,
 		description: 'Stubs for interfaces'
 	},
 	'gotype-live': {
 		name: 'gotype-live',
 		importPath: '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',
+		replacedByGopls: true,
 		isImportant: true,
 		description: 'Go to definition'
 	},
 	'gogetdoc': {
 		name: 'gogetdoc',
 		importPath: 'github.com/zmb3/gogetdoc',
+		replacedByGopls: true,
 		isImportant: true,
 		description: 'Go to definition & text shown on hover'
 	},
 	'gofumports': {
 		name: 'gofumports',
 		importPath: 'mvdan.cc/gofumpt/gofumports',
+		replacedByGopls: true,
 		isImportant: false,
 		description: 'Formatter'
 	},
 	'gofumpt': {
 		name: 'gofumpt',
 		importPath: 'mvdan.cc/gofumpt',
+		replacedByGopls: true,
 		isImportant: false,
 		description: 'Formatter'
 	},
 	'goimports': {
 		name: 'goimports',
 		importPath: 'golang.org/x/tools/cmd/goimports',
+		replacedByGopls: true,
 		isImportant: true,
 		description: 'Formatter'
 	},
 	'goreturns': {
 		name: 'goreturns',
 		importPath: 'github.com/sqs/goreturns',
+		replacedByGopls: true,
 		isImportant: true,
 		description: 'Formatter'
 	},
 	'goformat': {
 		name: 'goformat',
 		importPath: 'winterdrache.de/goformat/goformat',
+		replacedByGopls: true,
 		isImportant: false,
 		description: 'Formatter'
 	},
-	'golint': {
-		name: 'golint',
-		importPath: 'golang.org/x/lint/golint',
-		isImportant: true,
-		description: 'Linter',
-		minimumGoVersion: semver.coerce('1.9'),
-	},
 	'gotests': {
 		name: 'gotests',
 		importPath: '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',
+		replacedByGopls: false,
+		isImportant: true,
+		description: 'Linter',
+		minimumGoVersion: semver.coerce('1.9'),
+	},
 	'staticcheck': {
 		name: 'staticcheck',
 		importPath: 'honnef.co/go/tools/...',
+		replacedByGopls: false,
 		isImportant: true,
 		description: 'Linter'
 	},
 	'golangci-lint': {
 		name: 'golangci-lint',
 		importPath: 'github.com/golangci/golangci-lint/cmd/golangci-lint',
+		replacedByGopls: false,
 		isImportant: true,
 		description: 'Linter'
 	},
@@ -338,7 +370,8 @@
 	'gopls': {
 		name: 'gopls',
 		importPath: 'golang.org/x/tools/gopls',
-		isImportant: false,
+		replacedByGopls: false,  // lol
+		isImportant: true,
 		description: 'Language Server from Google',
 		minimumGoVersion: semver.coerce('1.12'),
 		latestVersion: semver.coerce('0.5.1'),
@@ -349,18 +382,21 @@
 	'dlv': {
 		name: 'dlv',
 		importPath: 'github.com/go-delve/delve/cmd/dlv',
-		isImportant: false,
+		replacedByGopls: false,
+		isImportant: true,
 		description: 'Debugging'
 	},
 	'fillstruct': {
 		name: 'fillstruct',
 		importPath: 'github.com/davidrjenni/reftools/cmd/fillstruct',
+		replacedByGopls: true,
 		isImportant: false,
 		description: 'Fill structs with defaults'
 	},
 	'godoctor': {
 		name: 'godoctor',
 		importPath: 'github.com/godoctor/godoctor',
+		replacedByGopls: true,
 		isImportant: false,
 		description: 'Extract to functions and variables'
 	}
diff --git a/src/util.ts b/src/util.ts
index 9f9476c..248d569 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -166,6 +166,22 @@
 	return getConfig('gopls');
 }
 
+// getCheckForToolsUpdatesConfig returns go.toolsManagement.checkForUpdates configuration.
+export function getCheckForToolsUpdatesConfig(gocfg: vscode.WorkspaceConfiguration) {
+	// useGoProxyToCheckForToolUpdates deprecation
+	// TODO: Step 1. mark as deprecated in Dec 2020 release, and update dev containers.
+	//       Step 2. prompt users to switch config. Jan 2020
+	//       Step 3. delete useGoProxyToCheckForToolUpdates support. Feb 2020
+	const legacyCfg = gocfg.get('useGoProxyToCheckForToolUpdates');
+	if (legacyCfg === false) {
+		const cfg = gocfg.inspect('toolsManagement.checkForUpdates');
+		if (cfg.globalValue === undefined && cfg.workspaceValue === undefined) {
+			return 'local';
+		}
+	}
+	return gocfg.get('toolsManagement.checkForUpdates') as string;
+}
+
 function getConfig(section: string, uri?: vscode.Uri) {
 	if (!uri) {
 		if (vscode.window.activeTextEditor) {
diff --git a/src/utils/pathUtils.ts b/src/utils/pathUtils.ts
index 0b93650..235ca8b 100644
--- a/src/utils/pathUtils.ts
+++ b/src/utils/pathUtils.ts
@@ -98,12 +98,14 @@
 		return {binPath: pathFromPath, why: found('path')};
 	}
 
-	// Check default path for go
+	// Check common paths for go
 	if (toolName === 'go') {
-		const defaultPathForGo = process.platform === 'win32' ? 'C:\\Go\\bin\\go.exe' : '/usr/local/go/bin/go';
-		if (executableFileExists(defaultPathForGo)) {
-			binPathCache[toolName] = defaultPathForGo;
-			return {binPath: defaultPathForGo, why: 'default'};
+		const defaultPathsForGo = process.platform === 'win32' ? ['C:\\Go\\bin\\go.exe'] : ['/usr/local/go/bin/go', '/usr/local/bin/go'];
+		for (const p of defaultPathsForGo) {
+			if (executableFileExists(p)) {
+				binPathCache[toolName] = p;
+			}
+			return {binPath: p, why: 'default'};
 		}
 		return {binPath: ''};
 	}
diff --git a/test/gopls/configuration.test.ts b/test/gopls/configuration.test.ts
new file mode 100644
index 0000000..e2adea4
--- /dev/null
+++ b/test/gopls/configuration.test.ts
@@ -0,0 +1,79 @@
+/*---------------------------------------------------------
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------*/
+
+import * as assert from 'assert';
+import moment = require('moment');
+import semver = require('semver');
+import sinon = require('sinon');
+import * as vscode from 'vscode';
+import * as lsp from '../../src/goLanguageServer';
+import { getTool, Tool } from '../../src/goTools';
+
+suite('gopls configuration tests', () => {
+	test('configuration', async () => {
+		const defaultGoplsConfig = vscode.workspace.getConfiguration('gopls');
+		const defaultGoplsAnalysesConfig = vscode.workspace.getConfiguration('gopls.analyses');
+
+		interface TestCase {
+			name: string;
+			section: string;
+			base: any;
+			want: any;
+		}
+		const testCases: TestCase[] = [
+			{
+				name: 'user set no gopls settings',
+				section: 'gopls',
+				base: defaultGoplsConfig,
+				want: {}
+			},
+			{
+				name: 'user set some gopls settings',
+				section: 'gopls',
+				base: defaultGoplsConfig,
+				want: {
+					buildFlags: ['-something'],
+					env: { foo: 'bar' },
+					hoverKind: 'NoDocumentation',
+					usePlaceholders: true,
+					linkTarget: 'godoc.org',
+				},
+			},
+			{
+				name: 'gopls asks analyses section, user set no analysis',
+				section: 'gopls.analyses',
+				base: defaultGoplsAnalysesConfig,
+				want: {},
+			},
+			{
+				name: 'gopls asks analyses section, user set some',
+				section: 'gopls.analyses',
+				base: defaultGoplsAnalysesConfig,
+				want: {
+					coolAnalysis: true,
+				},
+			},
+			{
+				name: 'user set extra gopls settings',
+				section: 'gopls',
+				base: defaultGoplsConfig,
+				want: {
+					undefinedGoplsSetting: true,
+				},
+			},
+			{
+				name: 'gopls asks undefined config section',
+				section: 'undefined.section',
+				base: undefined,
+				want: {},
+			}
+		];
+		testCases.map((tc: TestCase) => {
+			const input = Object.assign({}, tc.base, tc.want);
+			const actual = lsp.filterDefaultConfigValues(input, tc.section, undefined);
+			assert.deepStrictEqual(actual, tc.want, `Failed: ${tc.name}`);
+		});
+	});
+});
diff --git a/test/gopls/report.test.ts b/test/gopls/report.test.ts
index fc9cb36..7cdcc37 100644
--- a/test/gopls/report.test.ts
+++ b/test/gopls/report.test.ts
@@ -11,13 +11,14 @@
 		interface TestCase {
 			name: string;
 			in: string;
-			want: string;
+			want?: string;
+			wantReason?: string;
 		}
 		const testCases: TestCase[] = [
 			{
 				name: `panic trace`,
 				in: traceFromIssueGo41435,
-				want: sanitizedTraceFromIssueGo41435
+				want: sanitizedTraceFromIssueGo41435,
 			},
 			{
 				name: `initialization error message`,
@@ -27,18 +28,19 @@
 			{
 				name: `incomplete panic trace`,
 				in: `panic: \nsecret\n`,
-				want: ''
+				wantReason: `incomplete panic trace`
 			},
 			{
 				name: `incomplete initialization error message`,
 				in: `Secret Starting client failed.\nAnoter Secret\n`,
-				want: ''
+				wantReason: `unrecognized crash pattern`
 			}
 		];
 
 		testCases.map((tc: TestCase) => {
-			const out = sanitizeGoplsTrace(tc.in);
-			assert.strictEqual(out, tc.want, `sanitizeGoplsTrace(${tc.name}) returned unexpected results`);
+			const {sanitizedLog, failureReason}  = sanitizeGoplsTrace(tc.in);
+			assert.strictEqual(sanitizedLog, tc.want, `sanitizeGoplsTrace(${tc.name}) returned unexpected sanitizedLog result`);
+			assert.strictEqual(failureReason, tc.wantReason, `sanitizeGoplsTrace(${tc.name}) returned unexpected failureReason result`);
 		});
 
 	});
diff --git a/test/gopls/update.test.ts b/test/gopls/update.test.ts
index 2c838ff..dcfe27f 100644
--- a/test/gopls/update.test.ts
+++ b/test/gopls/update.test.ts
@@ -4,11 +4,78 @@
  *--------------------------------------------------------*/
 
 import * as assert from 'assert';
+import { defaultCipherList } from 'constants';
 import moment = require('moment');
 import semver = require('semver');
 import sinon = require('sinon');
+import * as vscode from 'vscode';
 import * as lsp from '../../src/goLanguageServer';
 import { getTool, Tool } from '../../src/goTools';
+import { getCheckForToolsUpdatesConfig as getCheckForToolUpdatesConfig, getGoConfig } from '../../src/util';
+
+suite('getCheckForToolUpdatesConfig tests', () => {
+	const CHECK_FOR_UPDATES = 'toolsManagement.checkForUpdates';
+	const LEGACY_CHECK_FOR_UPDATES = 'useGoProxyToCheckForToolUpdates';
+	const defaultConfigInspector = getGoConfig().inspect(CHECK_FOR_UPDATES);
+
+	test('default is as expected', () => {
+		const {key, defaultValue, globalValue, workspaceValue} = defaultConfigInspector;
+		assert.deepStrictEqual(
+			{ key, defaultValue, globalValue, workspaceValue },
+			{ key: `go.${CHECK_FOR_UPDATES}`, defaultValue : 'proxy', globalValue: undefined, workspaceValue: undefined},
+			CHECK_FOR_UPDATES);
+		assert.strictEqual(getGoConfig().get(LEGACY_CHECK_FOR_UPDATES), true, LEGACY_CHECK_FOR_UPDATES);
+	});
+
+	// wrapper class of vscode.WorkspaceConfiguration - the object returned by
+	// vscode.getConfiguration is read-only, and doesn't allow property modification
+	// so working with sinon directly doesn't seem possible.
+	class TestWorkspaceConfiguration implements vscode.WorkspaceConfiguration {
+		constructor(private _wrapped: vscode.WorkspaceConfiguration) {}
+		public get<T>(params: string) { return this._wrapped.get<T>(params); }
+		public has(params: string) { return this._wrapped.has(params); }
+		public inspect<T>(params: string) { return this._wrapped.inspect<T>(params); }
+		public update<T>(
+			section: string, value: any,
+			configurationTarget?: vscode.ConfigurationTarget | boolean, overrideInLanguage?: boolean) {
+				return this._wrapped.update(section, value, configurationTarget, overrideInLanguage);
+		}
+		[key: string]: any;
+	}
+
+	teardown(() => { sinon.restore(); });
+
+	test('default checkForUpdates returns proxy', () => {
+		const gocfg = getGoConfig();
+		assert.strictEqual(getCheckForToolUpdatesConfig(gocfg), 'proxy');
+	});
+	test('local when new config is not set and legacy config is set to false', () => {
+		const gocfg = new TestWorkspaceConfiguration(getGoConfig());
+		sinon.stub(gocfg, 'get')
+			.withArgs(LEGACY_CHECK_FOR_UPDATES).returns(false);
+
+		assert.strictEqual(getCheckForToolUpdatesConfig(gocfg), 'local');
+	});
+	test('proxy when new config is "proxy" and legacy config is set to false', () => {
+		const gocfg = new TestWorkspaceConfiguration(getGoConfig());
+		sinon.stub(gocfg, 'get')
+			.withArgs(LEGACY_CHECK_FOR_UPDATES).returns(false)
+			.withArgs(CHECK_FOR_UPDATES).returns('proxy');
+		sinon.stub(gocfg, 'inspect').withArgs(CHECK_FOR_UPDATES).returns(
+			Object.assign({}, defaultConfigInspector, {	globalValue: 'proxy' }));
+
+		assert.strictEqual(getCheckForToolUpdatesConfig(gocfg), 'proxy');
+	});
+	test('off when new config (workspace) is "off" and legacy config is set to false', () => {
+		const gocfg = new TestWorkspaceConfiguration(getGoConfig());
+		sinon.stub(gocfg, 'get')
+			.withArgs(LEGACY_CHECK_FOR_UPDATES).returns(false)
+			.withArgs(CHECK_FOR_UPDATES).returns('off');
+		sinon.stub(gocfg, 'inspect').withArgs(CHECK_FOR_UPDATES).returns(
+			Object.assign({}, defaultConfigInspector, {	workspaceValue: 'off' }));
+		assert.strictEqual(getCheckForToolUpdatesConfig(gocfg), 'off');
+	});
+});
 
 suite('gopls update tests', () => {
 	test('prompt for update', async () => {
@@ -105,7 +172,7 @@
 				enabled: true,
 				path: 'bad/path/to/gopls',
 				version: '',
-				checkForUpdates: true,
+				checkForUpdates: 'proxy',
 				env: {},
 				features: {
 					diagnostics: true,
diff --git a/test/integration/extension.test.ts b/test/integration/extension.test.ts
index 66dd9c3..3957b2e 100644
--- a/test/integration/extension.test.ts
+++ b/test/integration/extension.test.ts
@@ -42,7 +42,7 @@
 } from '../../src/util';
 
 suite('Go Extension Tests', function () {
-	this.timeout(15000);
+	this.timeout(20000);
 
 	const dummyCancellationSource = new vscode.CancellationTokenSource();
 
diff --git a/test/integration/goDebug.test.ts b/test/integration/goDebug.test.ts
index 08cc650..272e4d8 100644
--- a/test/integration/goDebug.test.ts
+++ b/test/integration/goDebug.test.ts
@@ -1,11 +1,12 @@
 import * as assert from 'assert';
-import { ChildProcess, spawn } from 'child_process';
-import { debug } from 'console';
+import * as cp from 'child_process';
 import * as fs from 'fs';
 import getPort = require('get-port');
 import * as http from 'http';
+import { tmpdir } from 'os';
 import * as path from 'path';
 import * as sinon from 'sinon';
+import util = require('util');
 import { DebugConfiguration } from 'vscode';
 import { DebugClient } from 'vscode-debugadapter-testsupport';
 import { ILocation } from 'vscode-debugadapter-testsupport/lib/debugClient';
@@ -18,7 +19,7 @@
 	RemoteSourcesAndPackages,
 } from '../../src/debugAdapter/goDebug';
 import { GoDebugConfigurationProvider } from '../../src/goDebugConfiguration';
-import { getBinPath } from '../../src/util';
+import { getBinPath, getGoVersion, rmdirRecursive } from '../../src/util';
 import { killProcessTree } from '../../src/utils/processUtils';
 
 suite('Path Manipulation Tests', () => {
@@ -323,7 +324,7 @@
 	 */
 	async function setUpRemoteProgram(
 			dlvPort: number, serverPort: number,
-			acceptMultiClient = true, continueOnStart = true): Promise<ChildProcess> {
+			acceptMultiClient = true, continueOnStart = true): Promise<cp.ChildProcess> {
 		const serverFolder = path.join(DATA_ROOT, 'helloWorldServer');
 		const toolPath = getBinPath('dlv');
 		const args = ['debug', '--api-version=2', '--headless', `--listen=127.0.0.1:${dlvPort}`];
@@ -333,7 +334,7 @@
 		if (continueOnStart) {
 			args.push('--continue');
 		}
-		const childProcess = spawn(toolPath, args,
+		const childProcess = cp.spawn(toolPath, args,
 			{cwd: serverFolder,  env: { PORT: `${serverPort}`, ...process.env}});
 
 		// Give dlv a few seconds to start.
@@ -514,7 +515,6 @@
 				request: 'launch',
 				mode: 'auto',
 				program: PROGRAM,
-				trace: 'verbose'
 			};
 
 			const debugConfig = debugConfigProvider.resolveDebugConfiguration(undefined, config);
@@ -745,7 +745,7 @@
 	});
 
 	suite('remote attach', () => {
-		let childProcess: ChildProcess;
+		let childProcess: cp.ChildProcess;
 		let server: number;
 		let debugConfig: DebugConfiguration;
 		setup(async () => {
@@ -842,7 +842,7 @@
 			const BREAKPOINT_LINE = 29;
 			const remoteProgram = await setUpRemoteProgram(remoteAttachConfig.port, server);
 
-			const breakpointLocation = getBreakpointLocation(FILE, BREAKPOINT_LINE, false);
+			const breakpointLocation = getBreakpointLocation(FILE, BREAKPOINT_LINE);
 
 			// Setup attach with a breakpoint.
 			await setUpRemoteAttach(remoteAttachDebugConfig, [breakpointLocation]);
@@ -858,7 +858,6 @@
 		});
 
 		test('stopped for a breakpoint set after initialization (remote attach)', async () => {
-			this.timeout(30_000);
 			const FILE = path.join(DATA_ROOT, 'helloWorldServer', 'main.go');
 			const BREAKPOINT_LINE = 29;
 			const remoteProgram = await setUpRemoteProgram(remoteAttachConfig.port, server);
@@ -867,7 +866,7 @@
 			await setUpRemoteAttach(remoteAttachDebugConfig);
 
 			// Now sets a breakpoint.
-			const breakpointLocation = getBreakpointLocation(FILE, BREAKPOINT_LINE, false);
+			const breakpointLocation = getBreakpointLocation(FILE, BREAKPOINT_LINE);
 			const breakpointsResult = await dc.setBreakpointsRequest(
 				{source: {path: breakpointLocation.path}, breakpoints: [breakpointLocation]});
 			assert.ok(breakpointsResult.success && breakpointsResult.body.breakpoints[0].verified);
@@ -881,27 +880,6 @@
 			await killProcessTree(remoteProgram);
 			await new Promise((resolve) => setTimeout(resolve, 2_000));
 		});
-
-		test('stopped for a breakpoint set during initialization (remote attach)', async () => {
-			const FILE = path.join(DATA_ROOT, 'helloWorldServer', 'main.go');
-			const BREAKPOINT_LINE = 29;
-			const remoteProgram = await setUpRemoteProgram(remoteAttachConfig.port, server);
-
-			const breakpointLocation = getBreakpointLocation(FILE, BREAKPOINT_LINE, false);
-
-			// Setup attach with a breakpoint.
-			await setUpRemoteAttach(remoteAttachDebugConfig, [breakpointLocation]);
-
-			// Calls the helloworld server to make the breakpoint hit.
-			await waitForBreakpoint(
-				() => http.get(`http://localhost:${server}`).on('error', (data) => console.log(data)),
-				breakpointLocation);
-
-			await dc.disconnectRequest({restart: false});
-			await killProcessTree(remoteProgram);
-			await new Promise((resolve) => setTimeout(resolve, 2_000));
-		});
-
 	});
 
 	suite('conditionalBreakpoints', () => {
@@ -1070,8 +1048,8 @@
 		// disconnectRequest is sent after it has already disconnected.
 
 		test('disconnect should work for remote attach', async () => {
-			this.timeout(30_000);
 			const server = await getPort();
+			remoteAttachConfig.port = await getPort();
 			const remoteProgram = await setUpRemoteProgram(remoteAttachConfig.port, server);
 
 			const debugConfig = debugConfigProvider.resolveDebugConfiguration(undefined, remoteAttachConfig);
@@ -1247,7 +1225,7 @@
 			};
 			const debugConfig = debugConfigProvider.resolveDebugConfiguration(undefined, config);
 
-			await dc.hitBreakpoint(debugConfig, { path: FILE, line: BREAKPOINT_LINE } );
+			await dc.hitBreakpoint(debugConfig, getBreakpointLocation(FILE, BREAKPOINT_LINE));
 
 			return Promise.all([
 				dc.disconnectRequest({restart: false}),
@@ -1306,4 +1284,151 @@
 			]);
 		});
 	});
+
+	suite('substitute path', () => {
+		// TODO(suzmue): add unit tests for substitutePath.
+		let tmpDir: string;
+
+		suiteSetup(() => {
+			tmpDir = fs.mkdtempSync(path.join(DATA_ROOT, 'substitutePathTest'));
+		});
+
+		suiteTeardown(() => {
+			rmdirRecursive(tmpDir);
+		});
+
+		function copyDirectory(name: string) {
+			const from = path.join(DATA_ROOT, name);
+			const to = path.join(tmpDir, name);
+			fs.mkdirSync(to);
+			fs.readdirSync(from).forEach((file) => {
+				fs.copyFileSync(path.join(from, file), path.join(to, file));
+			});
+			return to;
+		}
+
+		async function buildGoProgram(cwd: string, outputFile: string): Promise<string> {
+			const goRuntimePath = getBinPath('go');
+			const execFile = util.promisify(cp.execFile);
+			const child = await execFile(goRuntimePath,
+				['build', '-o', outputFile, `--gcflags='all=-N -l'`, '.'],
+				{cwd});
+			if (child.stderr.length > 0) {
+				throw Error(child.stderr);
+			}
+			return outputFile;
+		}
+
+		suite('substitutePath with missing files', () => {
+			let goBuildOutput: string;
+			suiteSetup(() => {
+				goBuildOutput = fs.mkdtempSync(path.join(tmpdir(), 'output'));
+			});
+
+			suiteTeardown(() => {
+				rmdirRecursive(goBuildOutput);
+			});
+
+			async function copyBuildDelete(program: string): Promise<{program: string, output: string}> {
+				const wd = copyDirectory(program);
+				const output = await buildGoProgram(wd, path.join(goBuildOutput, program));
+				rmdirRecursive(wd);
+				return {program: wd, output};
+			}
+
+			test('should stop on a breakpoint set in file with substituted path', async () => {
+				const {program, output} = await copyBuildDelete('baseTest');
+				const FILE = path.join(DATA_ROOT, 'baseTest', 'test.go');
+				const BREAKPOINT_LINE = 11;
+
+				const config = {
+					name: 'Launch',
+					type: 'go',
+					request: 'launch',
+					mode: 'exec',
+					program: output,
+					substitutePath: [
+						{
+							from: path.join(DATA_ROOT, 'baseTest'),
+							to: program
+						}
+					]
+				};
+				const debugConfig = debugConfigProvider.resolveDebugConfiguration(undefined, config);
+
+				return dc.hitBreakpoint(debugConfig, getBreakpointLocation(FILE, BREAKPOINT_LINE));
+			});
+		});
+
+		suite('substitutePath with remote program', () => {
+			let server: number;
+			let remoteAttachDebugConfig: DebugConfiguration;
+			let helloWorldLocal: string;
+			let helloWorldRemote: string;
+			setup(async () => {
+				server = await getPort();
+				remoteAttachConfig.port = await getPort();
+				remoteAttachDebugConfig = debugConfigProvider.resolveDebugConfiguration(undefined, remoteAttachConfig);
+			});
+
+			suiteSetup(() => {
+				helloWorldLocal = copyDirectory('helloWorldServer');
+				helloWorldRemote = path.join(DATA_ROOT, 'helloWorldServer');
+			});
+
+			suiteTeardown(() => {
+				rmdirRecursive(helloWorldLocal);
+			});
+
+			test('stopped for a breakpoint set during initialization using substitutePath (remote attach)', async () => {
+				const FILE = path.join(helloWorldLocal, 'main.go');
+				const BREAKPOINT_LINE = 29;
+				const remoteProgram = await setUpRemoteProgram(remoteAttachConfig.port, server);
+
+				const breakpointLocation = getBreakpointLocation(FILE, BREAKPOINT_LINE, false);
+				// Setup attach with a breakpoint.
+				remoteAttachDebugConfig.cwd = tmpDir;
+				remoteAttachDebugConfig.remotePath = '';
+				remoteAttachDebugConfig.substitutePath = [
+					{from: helloWorldLocal, to: helloWorldRemote}
+				];
+				await setUpRemoteAttach(remoteAttachDebugConfig, [breakpointLocation]);
+
+				// Calls the helloworld server to make the breakpoint hit.
+				await waitForBreakpoint(
+					() => http.get(`http://localhost:${server}`).on('error', (data) => console.log(data)),
+					breakpointLocation);
+
+				await dc.disconnectRequest({restart: false});
+				await killProcessTree(remoteProgram);
+				await new Promise((resolve) => setTimeout(resolve, 2_000));
+			});
+
+			test('stopped for a breakpoint set during initialization using remotePath (remote attach)', async () => {
+				const FILE = path.join(helloWorldLocal, 'main.go');
+				const BREAKPOINT_LINE = 29;
+				const remoteProgram = await setUpRemoteProgram(remoteAttachConfig.port, server);
+
+				const breakpointLocation = getBreakpointLocation(FILE, BREAKPOINT_LINE, false);
+				// Setup attach with a breakpoint.
+				remoteAttachDebugConfig.cwd = helloWorldLocal;
+				remoteAttachDebugConfig.remotePath = helloWorldRemote;
+				// This is a bad mapping, make sure that the remotePath config is used first.
+				remoteAttachDebugConfig.substitutePath = [
+					{from: helloWorldLocal, to: helloWorldLocal}
+				];
+				await setUpRemoteAttach(remoteAttachDebugConfig, [breakpointLocation]);
+
+				// Calls the helloworld server to make the breakpoint hit.
+				await waitForBreakpoint(
+					() => http.get(`http://localhost:${server}`).on('error', (data) => console.log(data)),
+					breakpointLocation);
+
+				await dc.disconnectRequest({restart: false});
+				await killProcessTree(remoteProgram);
+				await new Promise((resolve) => setTimeout(resolve, 2_000));
+			});
+		});
+	});
+
 });
diff --git a/test/integration/install.test.ts b/test/integration/install.test.ts
index 9a3732e..d73350d 100644
--- a/test/integration/install.test.ts
+++ b/test/integration/install.test.ts
@@ -15,8 +15,8 @@
 import vscode = require('vscode');
 import { toolInstallationEnvironment } from '../../src/goEnv';
 import { installTools } from '../../src/goInstallTools';
-import { allToolsInformation, getTool, getToolAtVersion } from '../../src/goTools';
-import { getBinPath, getGoVersion, rmdirRecursive } from '../../src/util';
+import { allToolsInformation, getConfiguredTools, getTool, getToolAtVersion } from '../../src/goTools';
+import { getBinPath, getGoVersion, GoVersion, rmdirRecursive } from '../../src/util';
 import { correctBinname } from '../../src/utils/pathUtils';
 
 suite('Installation Tests', function () {
@@ -166,3 +166,30 @@
 function shouldRunSlowTests(): boolean {
 	return !!process.env['VSCODEGO_BEFORE_RELEASE_TESTS'];
 }
+
+suite('getConfiguredTools', () => {
+	test('do not require legacy tools when using language server', async () => {
+		const configured = getConfiguredTools(fakeGoVersion('1.15.6'), { useLanguageServer: true });
+		const got = configured.map((tool) => tool.name) ?? [];
+		assert(got.includes('gopls'), `omitted 'gopls': ${JSON.stringify(got)}`);
+		assert(!got.includes('guru') && !got.includes('gocode'), `suggested legacy tools: ${JSON.stringify(got)}`);
+	});
+
+	test('do not require gopls when not using language server', async () => {
+		const configured = getConfiguredTools(fakeGoVersion('1.15.6'), { useLanguageServer: false });
+		const got = configured.map((tool) => tool.name) ?? [];
+		assert(!got.includes('gopls'), `suggested 'gopls': ${JSON.stringify(got)}`);
+		assert(got.includes('guru') && got.includes('gocode'), `omitted legacy tools: ${JSON.stringify(got)}`);
+	});
+
+	test('do not require gopls when the go version is old', async () => {
+		const configured = getConfiguredTools(fakeGoVersion('1.9'), { useLanguageServer: true });
+		const got = configured.map((tool) => tool.name) ?? [];
+		assert(!got.includes('gopls'), `suggested 'gopls' for old go: ${JSON.stringify(got)}`);
+		assert(got.includes('guru') && got.includes('gocode'), `omitted legacy tools: ${JSON.stringify(got)}`);
+	});
+});
+
+function fakeGoVersion(version: string) {
+	return new GoVersion('/path/to/go', `go version go${version} windows/amd64`);
+}
diff --git a/test/integration/test.test.ts b/test/integration/test.test.ts
index 2ef8415..902c923 100644
--- a/test/integration/test.test.ts
+++ b/test/integration/test.test.ts
@@ -96,7 +96,7 @@
 });
 
 suite('Test Go Test', function () {
-	this.timeout(10000);
+	this.timeout(50000);
 
 	const sourcePath = path.join(__dirname, '..', '..', '..', 'test', 'testdata', 'goTestTest');
 
@@ -134,8 +134,6 @@
 		input: { isMod: boolean, includeSubDirectories: boolean, testFlags?: string[], applyCodeCoverage?: boolean },
 		wantFiles: string[]) {
 
-		fs.copySync(sourcePath, repoPath, { recursive: true });
-
 		const config = Object.create(vscode.workspace.getConfiguration('go'));
 		const outputChannel = new FakeOutputChannel();
 
@@ -184,21 +182,21 @@
 	});
 
 	test('resolves file names in logs (GOPATH)', async () => {
-		setupRepo(true);
+		setupRepo(false);
 		await runTest(
-			{ isMod: true, includeSubDirectories: true },
+			{ isMod: false, includeSubDirectories: true },
 			[path.join(repoPath, 'a_test.go'), path.join(repoPath, 'b', 'b_test.go')]);
 		await runTest(
-			{ isMod: true, includeSubDirectories: false },
+			{ isMod: false, includeSubDirectories: false },
 			[path.join(repoPath, 'a_test.go')]);
 		await runTest(
-			{ isMod: true, includeSubDirectories: true, testFlags: ['-v'] },
+			{ isMod: false, includeSubDirectories: true, testFlags: ['-v'] },
 			[path.join(repoPath, 'a_test.go'), path.join(repoPath, 'b', 'b_test.go')]);
 		await runTest(
-			{ isMod: true, includeSubDirectories: true, testFlags: ['-race'], applyCodeCoverage: true },
+			{ isMod: false, includeSubDirectories: true, testFlags: ['-race'], applyCodeCoverage: true },
 			[path.join(repoPath, 'a_test.go'), path.join(repoPath, 'b', 'b_test.go')]);
 		await runTest(
-			{ isMod: true, includeSubDirectories: false, testFlags: ['-v'] },
+			{ isMod: false, includeSubDirectories: false, testFlags: ['-v'] },
 			[path.join(repoPath, 'a_test.go')]);
 	});
 });
diff --git a/tools/generate.go b/tools/generate.go
index 76b5ba5..940d1eb 100644
--- a/tools/generate.go
+++ b/tools/generate.go
@@ -38,14 +38,17 @@
 }
 
 type Property struct {
-	name string // Set by us.
+	name string `json:"name,omitempty"` // Set by us.
 
 	// Below are defined in package.json
-	Default             interface{} `json:"default,omitempty"`
-	MarkdownDescription string      `json:"markdownDescription,omitempty"`
-	Description         string      `json:"description,omitempty"`
-	Type                interface{} `json:"type,omitempty"`
-	Enum                []string    `json:"enum,omitempty"`
+	Properties                 map[string]interface{} `json:"properties,omitempty"`
+	Default                    interface{}            `json:"default,omitempty"`
+	MarkdownDescription        string                 `json:"markdownDescription,omitempty"`
+	Description                string                 `json:"description,omitempty"`
+	MarkdownDeprecationMessage string                 `json:"markdownDeprecationMessage,omitempty"`
+	DeprecationMessage         string                 `json:"deprecationMessage,omitempty"`
+	Type                       interface{}            `json:"type,omitempty"`
+	Enum                       []string               `json:"enum,omitempty"`
 }
 
 func main() {
@@ -127,7 +130,19 @@
 		if p.MarkdownDescription != "" {
 			desc = p.MarkdownDescription
 		}
-		b.WriteString(fmt.Sprintf("### `%s`\n\n%s", p.name, desc))
+		deprecation := p.DeprecationMessage
+		if p.MarkdownDeprecationMessage != "" {
+			deprecation = p.MarkdownDeprecationMessage
+		}
+
+		name := p.name
+		if deprecation != "" {
+			name += " (deprecated)"
+			desc = deprecation + "\n" + desc
+		}
+
+		b.WriteString(fmt.Sprintf("### `%s`\n\n%s", name, desc))
+
 		if p.Enum != nil {
 			b.WriteString(fmt.Sprintf("\n\nAllowed Values:`%v`", p.Enum))
 		}
@@ -155,6 +170,8 @@
 				}
 				b.WriteString("    }\n")
 			}
+			writeSettingsObjectProperties(&b, "####", p.Properties)
+
 		case "boolean", "string", "number":
 			b.WriteString(fmt.Sprintf("\n\nDefault: `%v`", p.Default))
 		case "array":
@@ -175,3 +192,40 @@
 	}
 	rewrite(filepath.Join(dir, "docs", "settings.md"), b.Bytes())
 }
+
+func writeSettingsObjectProperties(b *bytes.Buffer, heading string, properties map[string]interface{}) {
+	var names []string
+	for name := range properties {
+		names = append(names, name)
+	}
+	sort.Strings(names)
+
+	for _, name := range names {
+		p, ok := properties[name].(map[string]interface{})
+		if !ok {
+			b.WriteString(fmt.Sprintf("\n\n\n%s %s\n", heading, name))
+			continue
+		}
+
+		desc := ""
+		if d := p["description"]; d != nil {
+			desc = fmt.Sprintf("%v", d)
+		}
+		if d := p["markdownDescription"]; d != nil {
+			desc = fmt.Sprintf("%v", d)
+		}
+		deprecation := ""
+		if d := p["deprecationMessage"]; d != nil {
+			deprecation = fmt.Sprintf("%v", d)
+		}
+		if d := p["markdownDeprecationMessage"]; d != nil {
+			deprecation = fmt.Sprintf("%v", d)
+		}
+
+		if deprecation != "" {
+			name += " (deprecated)"
+			desc = deprecation + "\n" + desc
+		}
+		b.WriteString(fmt.Sprintf("\n\n%s `%s`\n%s", heading, name, desc))
+	}
+}
diff --git a/tools/goplssetting/main.go b/tools/goplssetting/main.go
index 5ce729b..868a027 100644
--- a/tools/goplssetting/main.go
+++ b/tools/goplssetting/main.go
@@ -127,8 +127,8 @@
 
 	opts := []*OptionJSON{}
 	for _, v := range options {
-		if emoji := sectionEmoji(v.section); emoji != "" {
-			v.OptionJSON.Doc = emoji + " " + v.OptionJSON.Doc
+		if name := sectionName(v.section); name != "" {
+			v.OptionJSON.Doc = name + " " + v.OptionJSON.Doc
 		}
 		opts = append(opts, v.OptionJSON)
 	}
@@ -147,12 +147,12 @@
 	return 1000
 }
 
-func sectionEmoji(section string) string {
+func sectionName(section string) string {
 	switch section {
 	case "Experimental":
-		return "๐Ÿงช"
+		return "(Experimental)"
 	case "Debugging":
-		return "๐Ÿ”"
+		return "(For Debugging)"
 	}
 	return ""
 }
@@ -181,14 +181,20 @@
 	}
 
 	line(`{`)
+	line(`"gopls": {`)
+	line(`    "type": "object",`)
+	line(`    "markdownDescription": "Configure the default Go language server ('gopls'). In most cases, configuring this section is unnecessary. See [the documentation](https://github.com/golang/tools/blob/master/gopls/doc/settings.md) for all available settings.",`)
+	line(`    "scope": "resource",`)
+	line(`    "additionalProperties": false,`)
+	line(`    "properties": {`)
 	for i, o := range options {
-		line(`  "gopls.%v" : {`, o.Name)
+		line(`    "%v" : {`, o.Name)
 
 		typ := propertyType(o.Type)
-		line(`    "type": %q,`, typ)
+		line(`      "type": %q,`, typ)
 		// TODO: consider 'additionalProperties' if gopls api-json outputs acceptable peoperties.
 
-		line(`    "markdownDescription": %q,`, o.Doc)
+		line(`      "markdownDescription": %q,`, o.Doc)
 
 		var enums, enumDocs []string
 		for _, v := range o.EnumValues {
@@ -196,25 +202,28 @@
 			enumDocs = append(enumDocs, fmt.Sprintf("%q", v.Doc))
 		}
 		if len(enums) > 0 {
-			line(`    "enum": [%v],`, strings.Join(enums, ","))
-			line(`    "markdownEnumDescriptions": [%v],`, strings.Join(enumDocs, ","))
+			line(`      "enum": [%v],`, strings.Join(enums, ","))
+			line(`      "markdownEnumDescriptions": [%v],`, strings.Join(enumDocs, ","))
 		}
 
 		if len(o.Default) > 0 {
-			line(`    "default": %v,`, o.Default)
+			line(`      "default": %v,`, o.Default)
 		}
 
 		// TODO: are all gopls settings in the resource scope?
-		line(`    "scope": "resource"`)
+		line(`      "scope": "resource"`)
 		// TODO: deprecation attribute
 
+		// "%v" : {
 		if i == len(options)-1 {
-			line(`  }`)
+			line(`    }`)
 		} else {
-			line(`  },`)
+			line(`    },`)
 		}
 	}
-	line(`}`)
+	line(`    }`) //  "properties": {
+	line(`  }`)   // "gopls": {
+	line(`}`)     // {
 }
 
 func propertyType(t string) string {
diff --git a/tools/goplssetting/main_test.go b/tools/goplssetting/main_test.go
index 368a693..a135f2c 100644
--- a/tools/goplssetting/main_test.go
+++ b/tools/goplssetting/main_test.go
@@ -44,7 +44,7 @@
 				Doc:     "verboseOutput enables additional debug logging.\n",
 				Default: "false",
 			},
-			out: `"gopls.verboseOutput": {
+			out: `"verboseOutput": {
 					"type": "boolean",
 					"markdownDescription": "verboseOutput enables additional debug logging.\n",
 					"default": false,
@@ -58,7 +58,7 @@
 				Type:    "time.Duration",
 				Default: "\"100ms\"",
 			},
-			out: `"gopls.completionBudget": {
+			out: `"completionBudget": {
 					"type": "string",
 					"markdownDescription": "",
 					"default": "100ms",
@@ -72,7 +72,7 @@
 				Type:    "map[string]bool",
 				Default: "{}",
 			},
-			out: `"gopls.analyses":{
+			out: `"analyses":{
 					"type": "object",
 					"markdownDescription": "",
 					"default": {},
@@ -100,7 +100,7 @@
 				},
 				Default: "\"Fuzzy\"",
 			},
-			out: `"gopls.matcher": {
+			out: `"matcher": {
  					"type": "string",
 					"markdownDescription": "",
 					"enum": [ "CaseInsensitive", "CaseSensitive", "Fuzzy" ],
@@ -116,7 +116,18 @@
 			options := []*OptionJSON{tc.in}
 			buf := &bytes.Buffer{}
 			writeAsVSCodeSettings(buf, options)
-			if got, want := normalize(t, buf.String()), normalize(t, "{ "+tc.out+" }"); got != want {
+			if got, want := normalize(t, buf.String()), normalize(t, `
+			{ 
+				"gopls": {
+					"type": "object",
+					"markdownDescription": "Configure the default Go language server ('gopls'). In most cases, configuring this section is unnecessary. See [the documentation](https://github.com/golang/tools/blob/master/gopls/doc/settings.md) for all available settings.",
+					"scope": "resource",
+					"additionalProperties": false,
+					"properties": {
+				       `+tc.out+`
+					}
+				}
+			}`); got != want {
 				t.Errorf("writeAsVSCodeSettings = %v, want %v", got, want)
 			}
 		})
@@ -127,9 +138,12 @@
 	t.Helper()
 	cmd := exec.Command("jq")
 	cmd.Stdin = strings.NewReader(in)
+	stderr := new(bytes.Buffer)
+	cmd.Stderr = stderr
+
 	out, err := cmd.Output()
 	if err != nil {
-		t.Fatalf("failed to run jq: %v", err)
+		t.Fatalf("%s\n%s\nfailed to run jq: %v", in, stderr, err)
 	}
 	return string(out)
 }