all: merge master@bfaff19 into release branch

Changed package.json version to 0.15.0

Change-Id: I35ae570805ca6a35072a4d8096789357ea1f036e
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 2850819..064afa8 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -2,10 +2,4 @@
 contact_links:
   - name: Question
     url: https://invite.slack.golangbridge.org/
-    about: Ask or answer questions in the \#vscode Gopher slack channel
-  - name: Bug Report
-    url: https://github.com/golang/vscode-go/issues/new?template=bug_report.md
-    about: Report a bug in the VS Code Go extension
-  - name: Feature Request
-    url: https://github.com/golang/vscode-go/issues/new?template=feature_request.md
-    about: Suggest ideas for improvement
+    about: Ask and answer questions on the `#vscode` channel in Gophers Slacks.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..e315bfc
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,22 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+If you have a question, please ask it on the `#vscode` or `#vscode-go` channels in Gophers Slack](https://invite.slack.golangbridge.org/messages/vscode).
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE
new file mode 100644
index 0000000..d8a6f89
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE
@@ -0,0 +1,24 @@
+This PR will be imported into Gerrit with the title and first
+comment (this text) used to generate the subject and body of
+the Gerrit change.
+
+**Please ensure you adhere to every item in this list.**
+
+More info can be found at https://github.com/golang/go/wiki/CommitMessage
+
++ The PR title is formatted as follows: `frob the quux before blarfing`
+  + The part after the colon uses the verb tense + phrase that completes the blank in,
+    "This change modifies Go to ___________"
+  + Lowercase verb after the colon
+  + No trailing period
+  + Keep the title as short as possible. ideally under 76 characters or shorter
++ No Markdown
++ The first PR comment (this one) is wrapped at 76 characters, unless it's
+  really needed (ASCII art, table, or long link)
++ If there is a corresponding issue, add either `Fixes golang/vscode-go#1234` or `Updates golang/vscode-go#1234`
+  (the latter if this is not a complete fix) to this comment
++ If referring to a repo, you can use the `owner/repo#issue_number` syntax:
+  `Fixes golang/tools#1234`
++ We do not use Signed-off-by lines in Go. Please don't add them.
+  Our Gerrit server & GitHub bots enforce CLA compliance instead.
++ Delete these instructions once you have read and applied them
diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md
deleted file mode 100644
index 9da7f28..0000000
--- a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md
+++ /dev/null
@@ -1,2 +0,0 @@
-We are moving to a new home! See https://github.com/microsoft/vscode-go/issues/3247.
-Please send your pull request to https://github.com/golang/vscode-go instead.
diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md
new file mode 100644
index 0000000..914c4f4
--- /dev/null
+++ b/.github/SUPPORT.md
@@ -0,0 +1,6 @@
+For asking questions, visit:
+
+* `#vscode` channel in [Gophers Slack](https://gophers.slack.com) for general questions.
+* `#vscode-dev` channel in [Gophers Slack](https://gophers.slack.com) for extension development-related questions.
+
+Use the [invite app](https://invite.slack.golangbridge.org/) for access.
diff --git a/.github/locker.yml b/.github/locker.yml
deleted file mode 100644
index 763c8aa..0000000
--- a/.github/locker.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-{

-    daysAfterClose: 45,

-    daysSinceLastUpdate: 3,

-    perform: true

-}
\ No newline at end of file
diff --git a/.github/needs_more_info.yml b/.github/needs_more_info.yml
deleted file mode 100644
index e7de8ac..0000000
--- a/.github/needs_more_info.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-{

-    daysUntilClose: 7,

-    needsMoreInfoLabel: 'needs more info',

-    perform: true,

-    closeComment: 'This issue has been closed automatically because it needs more information and has not had recent activity. Thank you for your contributions.'

-}
\ No newline at end of file
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 0000000..79139db
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,41 @@
+name: "Code scanning - action"
+
+on:
+  push:
+  schedule:
+    - cron: '0 22 * * 1'
+
+jobs:
+  CodeQL-Build:
+
+    # CodeQL runs on ubuntu-latest and windows-latest
+    runs-on: ubuntu-latest
+
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v2
+
+    # Initializes the CodeQL tools for scanning.
+    - name: Initialize CodeQL
+      uses: github/codeql-action/init@v1
+      with:
+        languages: go, javascript
+
+    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
+    # If this step fails, then you should remove it and run the build manually (see below)
+    - name: Autobuild
+      uses: github/codeql-action/autobuild@v1
+
+    # ℹī¸ Command-line programs to run using the OS shell.
+    # 📚 https://git.io/JvXDl
+
+    # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines
+    #    and modify them (or add more) to build your code if your project
+    #    uses a compiled language
+
+    #- run: |
+    #   make bootstrap
+    #   make release
+
+    - name: Perform CodeQL Analysis
+      uses: github/codeql-action/analyze@v1
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..1a8a449
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,87 @@
+name: release
+
+# Daily release on 15:00 UTC, monday-thursday.
+# Or, force to release by triggering repository_dispatch events by using
+#   curl -v -H "Accept: application/vnd.github.everest-preview+json" -H "Authorization: token ${GITHUB_TOKEN}" https://api.github.com/repos/golang/vscode-go/dispatches -d '{ "event_type": "force-release" }'
+on:
+  schedule:
+    - cron: "0 15 * * MON-THU"  # 15 UTC, monday-thursday daily
+  repository_dispatch:
+    types: [force-release]
+
+jobs:
+  release:
+    name: Release Nightly
+    runs-on: ubuntu-latest
+    timeout-minutes: 20
+
+    steps:
+      - name: Clone repository
+        uses: actions/checkout@v2
+
+      - name: Setup Node
+        uses: actions/setup-node@v1
+        with:
+          node-version: '10.x'
+
+      - name: Setup Go
+        uses: actions/setup-go@v1
+        with:
+         go-version: '1.14'
+
+      - name: Install dependencies
+        run: npm ci
+
+      - name: Prepare Release
+        run: build/all.bash prepare_nightly
+
+      - name: Compile
+        run: npm run vscode:prepublish
+
+      - name: Install Go tools (Modules mode)
+        run: |
+            go version
+            go get github.com/acroca/go-symbols \
+               github.com/davidrjenni/reftools/cmd/fillstruct \
+               github.com/haya14busa/goplay/cmd/goplay \
+               github.com/mdempsky/gocode \
+               github.com/sqs/goreturns \
+               github.com/uudashr/gopkgs/v2/cmd/gopkgs \
+               github.com/zmb3/gogetdoc \
+               golang.org/x/lint/golint \
+               golang.org/x/tools/cmd/gorename \
+               golang.org/x/tools/gopls
+
+        env:
+          GO111MODULE: on
+
+      - name: Install Go tools (GOPATH mode)
+        run: |
+          go version
+          go get github.com/cweill/gotests/... \
+            github.com/rogpeppe/godef \
+            github.com/ramya-rao-a/go-outline
+            # Because some tests depend on the source code checked in GOPATH. TODO: FIX THEM.
+        env:
+          GO111MODULE: off
+
+
+      - name: Run unit tests
+        run: npm run unit-test
+        continue-on-error: true
+
+      - name: Run tests
+        uses: GabrielBB/xvfb-action@v1.0
+        with:
+          run: npm run test
+        env:
+          CODE_VERSION: 'insiders'
+          VSCODEGO_BEFORE_RELEASE_TESTS: true
+
+      - name: Publish
+        if: github.ref == 'refs/heads/master' && github.repository == 'golang/vscode-go'
+        uses: lannonbr/vsce-action@704da577da0f27de5cdb4ae018374c2f08b5f523
+        with:
+          args: "publish -p $VSCE_TOKEN"
+        env:
+          VSCE_TOKEN: ${{ secrets.VSCE_TOKEN }}
diff --git a/.github/workflows/test-long.yml b/.github/workflows/test-long.yml
new file mode 100644
index 0000000..9cc67d4
--- /dev/null
+++ b/.github/workflows/test-long.yml
@@ -0,0 +1,83 @@
+name: Long Tests
+
+on:
+  push:
+    branches-ignore:
+      - 'latest'
+      - 'upstream'
+
+jobs:
+  build:
+    name: ${{ matrix.os }} ${{ matrix.version }}
+    runs-on: ${{ matrix.os }}
+    
+    # Not containing 'SKIP CI' in the commit message AND
+    # (Either non-Windows OR triggered on 'push' (if triggered by 'pull_request', github.base_ref is not empty)
+    if: "!contains(github.event.head_commit.message, 'SKIP CI')"
+    timeout-minutes: 20
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-latest, windows-latest, macos-latest]
+        version: ['stable']
+
+    steps:
+      - name: Clone repository
+        uses: actions/checkout@v2
+
+      - name: Setup Node
+        uses: actions/setup-node@v1
+        with:
+          node-version: '10.x'
+
+      - name: Setup Go
+        uses: actions/setup-go@v1
+        with:
+         go-version: '1.14'
+
+      - name: Install dependencies
+        run: npm ci
+      
+      - name: Compile
+        run: npm run vscode:prepublish
+
+      - name: Install Go tools (Modules mode)
+        run: |
+            go version
+            go get github.com/acroca/go-symbols
+            go get github.com/davidrjenni/reftools/cmd/fillstruct
+            go get github.com/haya14busa/goplay/cmd/goplay
+            go get github.com/mdempsky/gocode
+            go get github.com/sqs/goreturns
+            go get github.com/uudashr/gopkgs/v2/cmd/gopkgs
+            go get github.com/zmb3/gogetdoc
+            go get golang.org/x/lint/golint
+            go get golang.org/x/tools/cmd/gorename
+            go get golang.org/x/tools/gopls
+        env:
+          GO111MODULE: on   
+          
+      - name: Install Go tools (GOPATH mode)
+        run: |
+          go version
+          go get github.com/cweill/gotests/...
+          go get github.com/rogpeppe/godef
+          go get github.com/ramya-rao-a/go-outline
+            # Because some tests depend on the source code checked in GOPATH. TODO: FIX THEM.
+        env:
+          GO111MODULE: off
+      
+      - name: Run unit tests
+        run: npm run unit-test
+        continue-on-error: true
+        
+      - name: Run tests
+        uses: GabrielBB/xvfb-action@v1.0
+        with:
+          run: npm run test
+        env:
+          CODE_VERSION: ${{ matrix.version }}
+      
+      - name: Lint check
+        run: npm run lint
+        if: ${{ matrix.os }} == 'ubuntu-latest'
diff --git a/.github/workflows/test-smoke.yml b/.github/workflows/test-smoke.yml
new file mode 100644
index 0000000..96e517f
--- /dev/null
+++ b/.github/workflows/test-smoke.yml
@@ -0,0 +1,79 @@
+name: Smoke Tests
+
+on: [pull_request]
+
+jobs:
+  build:
+    name: ${{ matrix.os }} ${{ matrix.version }}
+    runs-on: ${{ matrix.os }}
+    
+    # Not containing 'SKIP CI' in the commit message AND
+    # (Either non-Windows OR triggered on 'push' (if triggered by 'pull_request', github.base_ref is not empty)
+    if: "!contains(github.event.head_commit.message, 'SKIP CI')"
+    timeout-minutes: 20
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-latest]
+        version: ['stable']
+
+    steps:
+      - name: Clone repository
+        uses: actions/checkout@v2
+
+      - name: Setup Node
+        uses: actions/setup-node@v1
+        with:
+          node-version: '10.x'
+
+      - name: Setup Go
+        uses: actions/setup-go@v1
+        with:
+         go-version: '1.14'
+
+      - name: Install dependencies
+        run: npm ci
+      
+      - name: Compile
+        run: npm run vscode:prepublish
+
+      - name: Install Go tools (Modules mode)
+        run: |
+            go version
+            go get github.com/acroca/go-symbols
+            go get github.com/davidrjenni/reftools/cmd/fillstruct
+            go get github.com/haya14busa/goplay/cmd/goplay
+            go get github.com/mdempsky/gocode
+            go get github.com/sqs/goreturns
+            go get github.com/uudashr/gopkgs/v2/cmd/gopkgs
+            go get github.com/zmb3/gogetdoc
+            go get golang.org/x/lint/golint
+            go get golang.org/x/tools/cmd/gorename
+            go get golang.org/x/tools/gopls
+        env:
+          GO111MODULE: on   
+          
+      - name: Install Go tools (GOPATH mode)
+        run: |
+          go version
+          go get github.com/cweill/gotests/...
+          go get github.com/rogpeppe/godef
+          go get github.com/ramya-rao-a/go-outline
+            # Because some tests depend on the source code checked in GOPATH. TODO: FIX THEM.
+        env:
+          GO111MODULE: off
+      
+      - name: Run unit tests
+        run: npm run unit-test
+        continue-on-error: true
+        
+      - name: Run tests
+        uses: GabrielBB/xvfb-action@v1.0
+        with:
+          run: npm run test
+        env:
+          CODE_VERSION: ${{ matrix.version }}
+
+      - name: Lint check
+        run: npm run lint
+        if: ${{ matrix.os }} == 'ubuntu-latest'
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 27028e9..68d1f87 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
 out
 node_modules
 .vscode-test
-.DS_Store
\ No newline at end of file
+.DS_Store
diff --git a/.travis.yml b/.travis.yml
index 54cf743..c6761bb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,7 +3,6 @@
 dist: bionic
 
 go:
-  - 1.12.x
   - 1.13.x
   - 1.14.x
   - tip
@@ -29,7 +28,7 @@
     fi
 
 install:
-  - TRAVIS_NODE_VERSION="8";
+  - TRAVIS_NODE_VERSION="12";
   # Clear out whatever version of NVM Travis has as it is old.
   - rm -rf ~/.nvm;
     # Grab NVM.
@@ -40,7 +39,7 @@
     # Install the desired version of Node
   - source ~/.nvm/nvm.sh;
   - nvm install $TRAVIS_NODE_VERSION;
-  - npm install
+  - npm ci
   - npm run vscode:prepublish
   - go get -u -v github.com/acroca/go-symbols
   - go get -u -v github.com/cweill/gotests/...
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 49e3b5e..94310ec 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -9,7 +9,8 @@
 			// path to VSCode executable
 			"runtimeExecutable": "${execPath}",
 			"args": [
-				"--extensionDevelopmentPath=${workspaceFolder}"
+				"--extensionDevelopmentPath=${workspaceFolder}",
+				"--disable-extensions"
 			],
 			"stopOnEntry": false,
 			"sourceMaps": true,
@@ -63,7 +64,6 @@
 				"--disable-extensions",
 				"--extensionDevelopmentPath=${workspaceFolder}",
 				"--extensionTestsPath=${workspaceFolder}/out/test/gopls/index",
-				"--user-data-dir", "${workspaceFolder}/test/gopls/testfixtures/src/workspace",
 				"--timeout", "999999",
 				"${workspaceFolder}/test/gopls/testfixtures/src/workspace"  // gopls requires a workspace to work with.
 			],
diff --git a/.vscodeignore b/.vscodeignore
index 5f7ffd2..529c5c2 100644
--- a/.vscodeignore
+++ b/.vscodeignore
@@ -1,11 +1,18 @@
 src/**/*
+test/
+third_party/
 typings/**/*
 .vscode/**/*
 tsconfig.json
 .gitignore
 node_modules/fs-extra
-test/
 **/*.map
 **/tslint.json
 build/**/*
 docs/
+*.md.nightly
+.github/**/*
+.prettierrc.json
+out/test/**
+.vscode-test/**
+SECURITY.md
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index e05c977..0000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,119 +0,0 @@
-# Contributing to Go plugin for VS Code

-

-Welcome, and thank you for your interest in contributing to the Go plugin for VS Code!

-

-There are many ways that you can contribute, beyond writing code. The goal of this document is to provide a high-level overview of how you can get involved.

-

-## Contributing Fixes

-

-If you are interested in writing code to fix issues, first look at the issues with the [help-wanted](https://github.com/Microsoft/vscode-go/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) label. They should have the context and code pointers needed to get started. If not, then feel free to ask for some, and we will be happy to provide any guidance you need.

-

-To get started with the code, please see [Building, Debugging and Sideloading the extension in Visual Studio Code](https://github.com/Microsoft/vscode-go/wiki/Building,-Debugging-and-Sideloading-the-extension-in-Visual-Studio-Code) in the wiki.

-

-A few labels other than [help-wanted](https://github.com/Microsoft/vscode-go/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) that might be of interest to you are

-- [good-first-issue](https://github.com/microsoft/vscode-go/issues?q=is%3Aopen+is%3Aissue+label%3Agood-first-issue)

-   These are issues that have fixes with the lowest barrier of entry.

-- [engineering-debt](https://github.com/microsoft/vscode-go/issues?utf8=✓&q=is%3Aopen+is%3Aissue+label%3Aengineering-debt+)

-   These are issues tracking the work that would improve the project structure, coding standards, test coverage etc. If you have any Javascript or Typescript background, then these are the issues you can jump in and start contributing immediately.

-- [under-discussion](https://github.com/microsoft/vscode-go/issues?utf8=✓&q=is%3Aopen+is%3Aissue+label%3Aunder-discussion)

-   These are issues where you as a Go developer can jump in and provide your insights on the discussion regarding how a problem should be solved or help in replicating the problem.

-

-### Contributor License Agreement

-

-You must sign a Contribution License Agreement (CLA) before your PR will be merged. This a one-time requirement for Microsoft projects in GitHub. 

-You can read more about the Microsoft Contribution License Agreement (CLA) at https://cla.microsoft.com.

-

-However, you don't have to do this up-front. You can simply clone, fork, and submit your pull-request as usual.

-

-When your pull-request is created, a bot will evaluate whether you have signed the CLA. If required, the bot will comment on the pull request, including a link to accept the agreement.

-

-Signing the CLA might sound scary but it's actually very simple and can be done in less than a minute :)

-

-### Contributing to snippets

-

-The `go.json` in this repo contains all the Code Snippets that is provided by this plugin. If you want to add more snippets, please create an issue first. If this issue gets atleast 10 upvotes, we will then consider adding your snippets to the plugin.

-

-We have this policy in place because excessive snippets will hinder the normal coding by adding unexpected entries in the auto-completion widget. Therefore, we want to add a snippet only after atleast 10 users from the community have upvoted it.

-

-## Asking Questions

-

-Have a question? First, ask away on [Gitter](https://gitter.im/Microsoft/vscode-go) or on the `vscode` channel in the [Gophers Slack](https://invite.slack.golangbridge.org/).

-

-The active community will be eager to assist you. If you don't get answers there, then feel free to ask your question by opening an issue here.

-Your well-worded question will serve as a resource to others searching for help.

-

-## Reporting Issues

-

-Have you identified a reproducible problem? Have a feature request? We want to hear about it! Here's how you can make reporting your issue as effective as possible.

-

-### Look For an Existing Issue

-

-Before you create a new issue, please do a search in [open issues](https://github.com/Microsoft/vscode-go/issues) to see if the issue or feature request has already been filed.

-

-Be sure to scan through the [most popular](https://github.com/Microsoft/vscode-go/issues?q=is%3Aopen+is%3Aissue+sort%3Areactions-%2B1-desc) issues.

-

-If you find your issue already exists, make relevant comments and add your [reaction](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments). Use a reaction in place of a "+1" comment:

-

-* 👍 - upvote

-* 👎 - downvote

-

-Subscribe to such issues so that you are kept up to date about the progress.

-

-If you cannot find an existing issue that describes your bug or feature, create a new issue using the guidelines below.

-

-### Writing Good Bug Reports and Feature Requests

-

-File a single issue per problem and feature request. Do not enumerate multiple bugs or feature requests in the same issue.

-

-Do not add your issue as a comment to an existing issue unless it's for the identical input. Many issues look similar, but have different causes.

-

-The more information you can provide, the more likely someone will be successful reproducing the issue and finding a fix.

-

-Please include the following with each issue:

-

-* Version of VS Code, the Go plugin and Go

-

-* OS/Platform you are working on

-

-* Run the command `Preferences: Open Settings (JSON) to see the entire list of settings you have added/edited. Share any Go related settings from here.

-

-* Reproducible steps (1... 2... 3...) that cause the issue

-

-* What you expected to see, versus what you actually saw

-

-* Images, animations, or a link to a video showing the issue occurring

-

-* A code snippet that demonstrates the issue or a link to a code repository the developers can easily pull down to recreate the issue locally

-

-  * **Note:** Because the developers need to copy and paste the code snippet, including a code snippet as a media file (i.e. .gif) is not sufficient.

-

-* Errors from the Dev Tools Console (open from the menu: Help > Toggle Developer Tools -> Console)

-

-* If you have enabled the use of language server, then share any errors from the logs (open from the menu: View > Output -> select language server from the drop down on the to right corner of the output panel)

-

-### Final Checklist

-

-Please remember to do the following:

-

-* [ ] Search the issue repository to ensure your report is a new issue

-

-* [ ] Simplify your code around the issue to better isolate the problem

-

-Don't feel bad if others can't reproduce the issue right away. They will simply ask for more information!

-

-### Follow Your Issue

-

-Once submitted, your report will go into the [issue tracking](https://github.com/Microsoft/vscode/wiki/Issue-Tracking) workflow. Be sure to understand what will happen next, so you know what to expect, and how to continue to assist throughout the process.

-

-## Automated Issue Management

-

-We use a bot to help us manage issues. This bot currently:

-

-* Automatically closes any issue marked `needs-more-info` if there has been no response in past 7 days.

-* Automatically locks issues 45 days after they are closed.

-

-If you believe the bot got something wrong, please open a new issue and let us know.

-

-# Thank You!

-

-Your contributions to open source, large or small, make great projects like this possible. Thank you for taking the time to contribute.

diff --git a/LICENSE b/LICENSE
index 6341057..54e5303 100644
--- a/LICENSE
+++ b/LICENSE
@@ -23,7 +23,6 @@
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 
------
 
 THE FOLLOWING SETS FORTH ATTRIBUTION NOTICES FOR THIRD PARTY SOFTWARE THAT MAY BE CONTAINED IN PORTIONS OF THE GO PRODUCT.
 
@@ -215,6 +214,32 @@
 
 -----
 
+The following software may be included in this product: balanced-match. A copy of the source code may be downloaded from git://github.com/juliangruber/balanced-match.git. This software contains the following license and notice below:
+
+(MIT)
+
+Copyright (c) 2013 Julian Gruber <julian@juliangruber.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+-----
+
 The following software may be included in this product: bcrypt-pbkdf. A copy of the source code may be downloaded from git://github.com/joyent/node-bcrypt-pbkdf.git. This software contains the following license and notice below:
 
 The Blowfish portions are under the following license:
@@ -286,6 +311,32 @@
 
 -----
 
+The following software may be included in this product: brace-expansion, isarray. A copy of the source code may be downloaded from git://github.com/juliangruber/brace-expansion.git (brace-expansion), git://github.com/juliangruber/isarray.git (isarray). This software contains the following license and notice below:
+
+MIT License
+
+Copyright (c) 2013 Julian Gruber <julian@juliangruber.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+-----
+
 The following software may be included in this product: caseless. A copy of the source code may be downloaded from https://github.com/mikeal/caseless. This software contains the following license and notice below:
 
 Apache License
@@ -343,6 +394,29 @@
 
 -----
 
+The following software may be included in this product: concat-map, is-typedarray, minimist. A copy of the source code may be downloaded from git://github.com/substack/node-concat-map.git (concat-map), git://github.com/hughsk/is-typedarray.git (is-typedarray), git://github.com/substack/minimist.git (minimist). This software contains the following license and notice below:
+
+This software is released under the MIT license:
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-----
+
 The following software may be included in this product: core-util-is. A copy of the source code may be downloaded from git://github.com/isaacs/core-util-is. This software contains the following license and notice below:
 
 Copyright Node.js contributors. All rights reserved.
@@ -558,7 +632,7 @@
 
 -----
 
-The following software may be included in this product: es-to-primitive, is-boolean-object, is-callable, is-date-object, is-number-object, is-string, is-symbol, is-typed-array, string.prototype.trimleft, string.prototype.trimright, which-typed-array. A copy of the source code may be downloaded from git://github.com/ljharb/es-to-primitive.git (es-to-primitive), git://github.com/ljharb/is-boolean-object.git (is-boolean-object), git://github.com/ljharb/is-callable.git (is-callable), git://github.com/ljharb/is-date-object.git (is-date-object), git://github.com/inspect-js/is-number-object.git (is-number-object), git://github.com/ljharb/is-string.git (is-string), git://github.com/inspect-js/is-symbol.git (is-symbol), git://github.com/ljharb/is-typed-array.git (is-typed-array), git://github.com/es-shims/String.prototype.trimLeft.git (string.prototype.trimleft), git://github.com/es-shims/String.prototype.trimRight.git (string.prototype.trimright), git://github.com/inspect-js/which-typed-array.git (which-typed-array). This software contains the following license and notice below:
+The following software may be included in this product: es-to-primitive, is-boolean-object, is-callable, is-date-object, is-number-object, is-string, is-symbol, is-typed-array, which-typed-array. A copy of the source code may be downloaded from git://github.com/ljharb/es-to-primitive.git (es-to-primitive), git://github.com/ljharb/is-boolean-object.git (is-boolean-object), git://github.com/ljharb/is-callable.git (is-callable), git://github.com/ljharb/is-date-object.git (is-date-object), git://github.com/inspect-js/is-number-object.git (is-number-object), git://github.com/ljharb/is-string.git (is-string), git://github.com/inspect-js/is-symbol.git (is-symbol), git://github.com/ljharb/is-typed-array.git (is-typed-array), git://github.com/inspect-js/which-typed-array.git (which-typed-array). This software contains the following license and notice below:
 
 The MIT License (MIT)
 
@@ -740,7 +814,7 @@
 
 -----
 
-The following software may be included in this product: faye-websocket, websocket-driver. A copy of the source code may be downloaded from git://github.com/faye/faye-websocket-node.git (faye-websocket), git://github.com/faye/websocket-driver-node.git (websocket-driver). This software contains the following license and notice below:
+The following software may be included in this product: faye-websocket. A copy of the source code may be downloaded from git://github.com/faye/faye-websocket-node.git. This software contains the following license and notice below:
 
 Copyright 2010-2019 James Coglan
 
@@ -810,6 +884,54 @@
 
 -----
 
+The following software may be included in this product: fs.realpath. A copy of the source code may be downloaded from git+https://github.com/isaacs/fs.realpath.git. This software contains the following license and notice below:
+
+The ISC License
+
+Copyright (c) Isaac Z. Schlueter and Contributors
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+----
+
+This library bundles a version of the `fs.realpath` and `fs.realpathSync`
+methods from Node.js v0.10 under the terms of the Node.js MIT license.
+
+Node's license follows, also included at the header of `old.js` which contains
+the licensed code:
+
+  Copyright Joyent, Inc. and other Node contributors.
+
+  Permission is hereby granted, free of charge, to any person obtaining a
+  copy of this software and associated documentation files (the "Software"),
+  to deal in the Software without restriction, including without limitation
+  the rights to use, copy, modify, merge, publish, distribute, sublicense,
+  and/or sell copies of the Software, and to permit persons to whom the
+  Software is furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+  DEALINGS IN THE SOFTWARE.
+
+-----
+
 The following software may be included in this product: function-bind. A copy of the source code may be downloaded from git://github.com/Raynos/function-bind.git. This software contains the following license and notice below:
 
 Copyright (c) 2013 Raynos.
@@ -857,6 +979,32 @@
 
 -----
 
+The following software may be included in this product: glob. A copy of the source code may be downloaded from git://github.com/isaacs/node-glob.git. This software contains the following license and notice below:
+
+The ISC License
+
+Copyright (c) Isaac Z. Schlueter and Contributors
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+## Glob Logo
+
+Glob's logo created by Tanya Brassie <http://tanyabrassie.com/>, licensed
+under a Creative Commons Attribution-ShareAlike 4.0 International License
+https://creativecommons.org/licenses/by-sa/4.0/
+
+-----
+
 The following software may be included in this product: har-schema. A copy of the source code may be downloaded from https://github.com/ahmadnassri/har-schema.git. This software contains the following license and notice below:
 
 Copyright (c) 2015, Ahmad Nassri <ahmad@ahmadnassri.com>
@@ -1029,6 +1177,46 @@
 
 -----
 
+The following software may be included in this product: inflight. A copy of the source code may be downloaded from https://github.com/npm/inflight.git. This software contains the following license and notice below:
+
+The ISC License
+
+Copyright (c) Isaac Z. Schlueter
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+-----
+
+The following software may be included in this product: inherits. A copy of the source code may be downloaded from git://github.com/isaacs/inherits. This software contains the following license and notice below:
+
+The ISC License
+
+Copyright (c) Isaac Z. Schlueter
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+-----
+
 The following software may be included in this product: is-arguments, is-regex, object-is. A copy of the source code may be downloaded from git://github.com/ljharb/is-arguments.git (is-arguments), git://github.com/ljharb/is-regex.git (is-regex), git://github.com/es-shims/object-is.git (object-is). This software contains the following license and notice below:
 
 The MIT License (MIT)
@@ -1106,55 +1294,6 @@
 
 -----
 
-The following software may be included in this product: is-typedarray, minimist. A copy of the source code may be downloaded from git://github.com/hughsk/is-typedarray.git (is-typedarray), git://github.com/substack/minimist.git (minimist). This software contains the following license and notice below:
-
-This software is released under the MIT license:
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
------
-
-The following software may be included in this product: isarray. A copy of the source code may be downloaded from git://github.com/juliangruber/isarray.git. This software contains the following license and notice below:
-
-MIT License
-
-Copyright (c) 2013 Julian Gruber <julian@juliangruber.com>
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
------
-
 The following software may be included in this product: isstream. A copy of the source code may be downloaded from https://github.com/rvagg/isstream.git. This software contains the following license and notice below:
 
 The MIT License (MIT)
@@ -1243,7 +1382,7 @@
 
 -----
 
-The following software may be included in this product: json-stringify-safe, semver. A copy of the source code may be downloaded from git://github.com/isaacs/json-stringify-safe (json-stringify-safe), https://github.com/npm/node-semver (semver). This software contains the following license and notice below:
+The following software may be included in this product: json-stringify-safe, minimatch, once, semver, wrappy. A copy of the source code may be downloaded from git://github.com/isaacs/json-stringify-safe (json-stringify-safe), git://github.com/isaacs/minimatch.git (minimatch), git://github.com/isaacs/once (once), https://github.com/npm/node-semver (semver), https://github.com/npm/wrappy (wrappy). This software contains the following license and notice below:
 
 The ISC License
 
@@ -1452,7 +1591,7 @@
 
 -----
 
-The following software may be included in this product: object-assign. A copy of the source code may be downloaded from https://github.com/sindresorhus/object-assign.git. This software contains the following license and notice below:
+The following software may be included in this product: object-assign, path-is-absolute. A copy of the source code may be downloaded from https://github.com/sindresorhus/object-assign.git (object-assign), https://github.com/sindresorhus/path-is-absolute.git (path-is-absolute). This software contains the following license and notice below:
 
 The MIT License (MIT)
 
@@ -1478,7 +1617,7 @@
 
 -----
 
-The following software may be included in this product: object-inspect. A copy of the source code may be downloaded from git://github.com/substack/object-inspect.git. This software contains the following license and notice below:
+The following software may be included in this product: object-inspect. A copy of the source code may be downloaded from git://github.com/inspect-js/object-inspect.git. This software contains the following license and notice below:
 
 MIT License
 
@@ -1899,26 +2038,35 @@
 
 -----
 
+The following software may be included in this product: websocket-driver. A copy of the source code may be downloaded from git://github.com/faye/websocket-driver-node.git. This software contains the following license and notice below:
+
+Copyright 2010-2020 James Coglan
+
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed
+under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+CONDITIONS OF ANY KIND, either express or implied. See the License for the
+specific language governing permissions and limitations under the License.
+
+-----
+
 The following software may be included in this product: websocket-extensions. A copy of the source code may be downloaded from git://github.com/faye/websocket-extensions-node.git. This software contains the following license and notice below:
 
-# The MIT License
+Copyright 2014-2020 James Coglan
 
-Copyright (c) 2014-2017 James Coglan
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at
 
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the 'Software'), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
+    http://www.apache.org/licenses/LICENSE-2.0
 
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+Unless required by applicable law or agreed to in writing, software distributed
+under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+CONDITIONS OF ANY KIND, either express or implied. See the License for the
+specific language governing permissions and limitations under the License.
 
diff --git a/README.md b/README.md
index 4faea7f..11874ba 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
 
 Take a look at the [Changelog](CHANGELOG.md) to learn about new features.
 
-> This is the **new** home for the VS Code Go extension. We just migrated from [Microsoft/vscode-go](https://github.com/Microsoft/vscode-go). Learn more our move on the [Go blog](https://golang.blog/the-vs-code-go-extension-joins-the-go-project).
+> This is the **new** home for the VS Code Go extension. We just migrated from [Microsoft/vscode-go](https://github.com/Microsoft/vscode-go). Learn more about our move on the [Go blog](https://blog.golang.org/vscode-go).
 
 ## Overview
 
@@ -125,7 +125,11 @@
 
 ## [Contributing](docs/contributing.md)
 
+<<<<<<< HEAD
 We welcome your contributions and thank you for working to improve the Go development experience in VS Code. If you would like to help work on the VS Code Go extension, please see our [contribution cuide](docs/contributing.md). It explains how to build and run the extension locally, and it describes the process of sending a contribution.
+=======
+We welcome your contributions and thank you for working to improve the Go development experience in VS Code. If you would like to help work on the VS Code Go extension, please see our [contribution guide](docs/contributing.md). It explains how to build and run the extension locally, and it describes the process of sending a contribution.
+>>>>>>> master
 
 ## [Code of Conduct](CODE_OF_CONDUCT.md)
 
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..7f8a58b
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,5 @@
+# Security
+
+## Reporting a Vulnerability
+
+See [golang.org/security](https://golang.org/security) to report a vulnerability.
diff --git a/build/all.bash b/build/all.bash
index 19ef780..10c3110 100755
--- a/build/all.bash
+++ b/build/all.bash
@@ -1,6 +1,7 @@
 #!/bin/bash -e
 
 # Copyright (C) Microsoft Corporation. All rights reserved.
+# Modification copyright 2020 The Go Authors. All rights reserved.
 # Licensed under the MIT License. See LICENSE in the project root for license information.
 
 usage() {
@@ -8,7 +9,7 @@
 Usage: $0 [subcommand]
 Available subcommands:
   help      - display this help message.
-  test      - build and test locally. Some tests may fail if vscode is alreay in use.
+  test      - build and test locally. Some tests may fail if vscode is already in use.
   testlocal - build and test in a locally built container.
   ci        - build and test with headless vscode. Requires Xvfb.
 EOUSAGE
@@ -44,9 +45,12 @@
   echo "**** Run test ****"
   npm ci
   npm run compile
-  npm run lint
   npm run unit-test
   npm test --silent
+  npm run lint
+
+  echo "**** Run settings generator ****"
+  go run tools/generate.go -w=false
 }
 
 run_test_in_docker() {
@@ -55,6 +59,35 @@
   docker run --workdir=/workspace -v "$(pwd):/workspace" vscode-test-env ci
 }
 
+prepare_nightly() {
+  # Version format: YYYY.MM.DDHH based on the latest commit timestamp.
+  # e.g. 2020.1.510 is the version built based on a commit that was made
+  #      on 2020/01/05 10:00
+  local VER=`git log -1 --format=%cd --date="format:%Y.%-m.%-d%H"`
+  local COMMIT=`git log -1 --format=%H`
+  echo "**** Preparing nightly release : $VER ***"
+
+  # Update package.json
+  (cat package.json | jq --arg VER "${VER}" '
+.version=$VER |
+.preview=true |
+.name="go-nightly" |
+.displayName="Go Nightly" |
+.publisher="golang" |
+.description="Rich Go language support for Visual Studio Code (Nightly)" |
+.author.name="Go Team at Google" |
+.repository.url="https://github.com/golang/vscode-go" |
+.bugs.url="https://github.com/golang/vscode-go/issues"
+') > /tmp/package.json && mv /tmp/package.json package.json
+
+  # Replace CHANGELOG.md with CHANGELOG.md + Release commit info.
+  printf "**Release ${VER} @ ${COMMIT}** \n\n" | cat - CHANGELOG.md > /tmp/CHANGELOG.md.new && mv /tmp/CHANGELOG.md.new CHANGELOG.md
+  # Replace the heading of README.md with the heading for Go Nightly.
+  sed '/^# Go for Visual Studio Code$/d' README.md | cat build/nightly/README.md - > /tmp/README.md.new && mv /tmp/README.md.new README.md
+  # Replace src/const.ts with build/nightly/const.ts.
+  cp build/nightly/const.ts src/const.ts
+}
+
 main() {
   cd "$(root_dir)"  # always run from the script root.
   case "$1" in
@@ -74,9 +107,12 @@
       setup_virtual_display
       run_test
       ;;
+    "prepare_nightly")
+      prepare_nightly
+      ;;
     *)
       usage
       exit 2
   esac
 }
-main $@
\ No newline at end of file
+main $@
diff --git a/build/cloudbuild.container.yaml b/build/cloudbuild.container.yaml
new file mode 100644
index 0000000..12d8ad7
--- /dev/null
+++ b/build/cloudbuild.container.yaml
@@ -0,0 +1,5 @@
+steps:
+- name: 'gcr.io/cloud-builders/docker'
+  args: ['build', '-t', 'gcr.io/$PROJECT_ID/vscode-test-env', '-f', 'build/Dockerfile', '.']
+images:
+    - 'gcr.io/$PROJECT_ID/vscode-test-env'
diff --git a/build/cloudbuild.yaml b/build/cloudbuild.yaml
new file mode 100644
index 0000000..a678121
--- /dev/null
+++ b/build/cloudbuild.yaml
@@ -0,0 +1,9 @@
+steps:
+- name: 'gcr.io/$PROJECT_ID/vscode-test-env'
+  entrypoint: "./build/all.bash"
+  args: ['ci']
+  env:
+    - 'BUILD=$BUILD_ID'
+    - 'PROJECT=$PROJECT_ID'
+    - 'REV=$REVISION_ID'
+timeout: 600s
diff --git a/build/nightly/README.md b/build/nightly/README.md
new file mode 100644
index 0000000..13a0c43
--- /dev/null
+++ b/build/nightly/README.md
@@ -0,0 +1,14 @@
+# Go Nightly for VS Code
+
+> ### **ATTENTION: This is the *preview* version of the [VS Code Go extension](https://github.com/golang/vscode-go), used for early feedback and testing.**
+> It is published nightly and contains previews of new features and bug fixes that are still under review or test. Therefore, this extension can be broken or unstable at times. If you are looking for the stable version,
+please install [the default VS Code Go extension](https://marketplace.visualstudio.com/items?itemName=golang.go) instead.
+>
+> **NOTE: The stable Go extension (`golang.go`) cannot be used at the same time as the preview Go Nightly extension (`golang.go-nightly`)**. If you have installed both extensions, you **must disable or uninstall** one of them. For further guidance, read the [documentation on how to disable an extension](https://code.visualstudio.com/docs/editor/extension-gallery#_disable-an-extension).
+
+> ### **Differences between Go and Go Nightly**
+>
+> * Go Nightly is released more frequently than the stable version (once per weekday).
+> * Go Nightly includes features and bug fixes that are not yet finalized.
+> * Go Nightly may use pre-release versions of tools (such as `gopls`) instead of released versions.
+> * For more information about Go Nightly, read the [Go Nightly](../nightly.md) documentation.
diff --git a/build/nightly/const.ts b/build/nightly/const.ts
new file mode 100644
index 0000000..7cb042b
--- /dev/null
+++ b/build/nightly/const.ts
@@ -0,0 +1,9 @@
+/*---------------------------------------------------------
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ * Modification copyright 2020 The Go Authors. All rights reserved.
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
+ *--------------------------------------------------------*/
+
+// This file contains constants that replace constants defined in src/const.ts when building Nightly
+
+export const extensionId: string = 'golang.go-nightly';
diff --git a/docs/contributing.md b/docs/contributing.md
index f318b81..752b32d 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -4,6 +4,8 @@
 
 This guide will explain the process of setting up your development environment to work on the VS Code Go extension, as well as the process of sending out your change for review. If you're interested in testing the master branch or pre-releases of the extension, please see the [Go Nightly documentation](nightly.md).
 
+Our canonical Git repository is located at https://go.googlesource.com/vscode-go and https://github.com/golang/vscode-go is a mirror.
+
 * [Before you start coding](#before-you-start-coding)
   * [Ask for help](#ask-for-help)
   * [Debug Adapter](#debug-adapter)
@@ -36,7 +38,7 @@
 2) Clone the repository, run `npm install`, and open VS Code:
 
     ```bash
-    git clone https://github.com/golang/vscode-go
+    git clone https://go.googlesource.com/vscode-go
     cd vscode-go
     npm install
     code .
@@ -44,6 +46,10 @@
 
 3) Make sure the `window.openFoldersInNewWindow` setting is not `"on"`.
 
+#### Lint
+
+You can run `npm run lint` on the command-line to check for lint errors in your program. You can also use the [TSLint](https://marketplace.visualstudio.com/items?itemName=ms-vscode.vscode-typescript-tslint-plugin) plugin to see errors as you code.
+
 ### Run
 
 To run the extension with your patch, open the Run view (`Ctrl+Shift+D`), select `Launch Extension`, and click the Play button (`F5`).
diff --git a/docs/testing.md b/docs/testing.md
index cf66d5d..2a8abf2 100644
--- a/docs/testing.md
+++ b/docs/testing.md
@@ -23,6 +23,7 @@
 GCB project members can manually trigger a build and test their local changes. Follow the [GCB instructions](https://cloud.google.com/cloud-build/docs/running-builds/start-build-manually) to set up the environment and tools, and then run:
 
 ```bash
+gcloud config set project vscode-go # this assumes access to vscode-go GCB project. If you encounter an access related error, please file an issue.
 gcloud builds submit --config=build/cloudbuild.yaml
 ```
 
diff --git a/images/goIcon.png b/images/goIcon.png
deleted file mode 100644
index a9eb5ad..0000000
--- a/images/goIcon.png
+++ /dev/null
Binary files differ
diff --git a/package-lock.json b/package-lock.json
index 89985c7..9c46b90 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,31 +1,31 @@
 {
-  "name": "Go",
-  "version": "0.14.2",
+  "name": "go",
+  "version": "0.15.0-dev",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
     "@babel/code-frame": {
-      "version": "7.8.3",
-      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
-      "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
+      "version": "7.10.1",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.1.tgz",
+      "integrity": "sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw==",
       "dev": true,
       "requires": {
-        "@babel/highlight": "^7.8.3"
+        "@babel/highlight": "^7.10.1"
       }
     },
     "@babel/helper-validator-identifier": {
-      "version": "7.9.5",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz",
-      "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==",
+      "version": "7.10.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz",
+      "integrity": "sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==",
       "dev": true
     },
     "@babel/highlight": {
-      "version": "7.9.0",
-      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz",
-      "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==",
+      "version": "7.10.1",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.1.tgz",
+      "integrity": "sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg==",
       "dev": true,
       "requires": {
-        "@babel/helper-validator-identifier": "^7.9.0",
+        "@babel/helper-validator-identifier": "^7.10.1",
         "chalk": "^2.0.0",
         "js-tokens": "^4.0.0"
       }
@@ -75,34 +75,36 @@
       "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==",
       "dev": true
     },
+    "@types/adm-zip": {
+      "version": "0.4.33",
+      "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.4.33.tgz",
+      "integrity": "sha512-WM0DCWFLjXtddl0fu0+iN2ZF+qz8RF9RddG5OSy/S90AQz01Fu8lHn/3oTIZDxvG8gVcnBLAHMHOdBLbV6m6Mw==",
+      "dev": true,
+      "requires": {
+        "@types/node": "*"
+      }
+    },
     "@types/deep-equal": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/@types/deep-equal/-/deep-equal-1.0.1.tgz",
       "integrity": "sha512-mMUu4nWHLBlHtxXY17Fg6+ucS/MnndyOWyOe7MmwkoMYxvfQU2ajtRaEvqSUv+aVkMqH/C0NCI8UoVfRNQ10yg==",
       "dev": true
     },
-    "@types/events": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
-      "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==",
-      "dev": true
-    },
     "@types/fs-extra": {
-      "version": "8.1.0",
-      "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.0.tgz",
-      "integrity": "sha512-UoOfVEzAUpeSPmjm7h1uk5MH6KZma2z2O7a75onTGjnNvAvMVrPzPL/vBbT65iIGHWj6rokwfmYcmxmlSf2uwg==",
+      "version": "8.1.1",
+      "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.1.tgz",
+      "integrity": "sha512-TcUlBem321DFQzBNuz8p0CLLKp0VvF/XH9E4KHNmgwyp4E3AfgI5cjiIVZWlbfThBop2qxFIh4+LeY6hVWWZ2w==",
       "dev": true,
       "requires": {
         "@types/node": "*"
       }
     },
     "@types/glob": {
-      "version": "7.1.1",
-      "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz",
-      "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==",
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.2.tgz",
+      "integrity": "sha512-VgNIkxK+j7Nz5P7jvUZlRvhuPSmsEfS03b0alKcq5V/STUKAa3Plemsn5mrQUO7am6OErJ4rhGEGJbACclrtRA==",
       "dev": true,
       "requires": {
-        "@types/events": "*",
         "@types/minimatch": "*",
         "@types/node": "*"
       }
@@ -120,24 +122,24 @@
       "dev": true
     },
     "@types/node": {
-      "version": "13.13.0",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.0.tgz",
-      "integrity": "sha512-WE4IOAC6r/yBZss1oQGM5zs2D7RuKR6Q+w+X2SouPofnWn+LbCqClRyhO3ZE7Ix8nmFgo/oVuuE01cJT2XB13A==",
+      "version": "13.13.11",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.11.tgz",
+      "integrity": "sha512-FX7mIFKfnGCfq10DGWNhfCNxhACEeqH5uulT6wRRA1KEt7zgLe0HdrAd9/QQkObDqp2Z0KEV3OAmNgs0lTx5tQ==",
       "dev": true
     },
     "@types/semver": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.1.0.tgz",
-      "integrity": "sha512-pOKLaubrAEMUItGNpgwl0HMFPrSAFic8oSVIvfu1UwcgGNmNyK9gyhBHKmBnUTwwVvpZfkzUC0GaMgnL6P86uA==",
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.2.0.tgz",
+      "integrity": "sha512-TbB0A8ACUWZt3Y6bQPstW9QNbhNeebdgLX4T/ZfkrswAfUzRiXrgd9seol+X379Wa589Pu4UEx9Uok0D4RjRCQ==",
       "dev": true,
       "requires": {
         "@types/node": "*"
       }
     },
     "@types/sinon": {
-      "version": "9.0.0",
-      "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-9.0.0.tgz",
-      "integrity": "sha512-v2TkYHkts4VXshMkcmot/H+ERZ2SevKa10saGaJPGCJ8vh3lKrC4u663zYEeRZxep+VbG6YRDtQ6gVqw9dYzPA==",
+      "version": "9.0.4",
+      "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-9.0.4.tgz",
+      "integrity": "sha512-sJmb32asJZY6Z2u09bl0G2wglSxDlROlAejCjsnor+LzBMz17gu8IU7vKC/vWDnv9zEq2wqADHVXFjf4eE8Gdw==",
       "dev": true,
       "requires": {
         "@types/sinonjs__fake-timers": "*"
@@ -150,9 +152,15 @@
       "dev": true
     },
     "@types/vscode": {
-      "version": "1.44.0",
-      "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.44.0.tgz",
-      "integrity": "sha512-WJZtZlinE3meRdH+I7wTsIhpz/GLhqEQwmPGeh4s1irWLwMzCeTV8WZ+pgPTwrDXoafVUWwo1LiZ9HJVHFlJSQ==",
+      "version": "1.45.1",
+      "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.45.1.tgz",
+      "integrity": "sha512-0NO9qrrEJBO8FsqHCrFMgR2suKnwCsKBWvRSb2OzH5gs4i3QO5AhEMQYrSzDbU/wLPt7N617/rN9lPY213gmwg==",
+      "dev": true
+    },
+    "adm-zip": {
+      "version": "0.4.14",
+      "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.14.tgz",
+      "integrity": "sha512-/9aQCnQHF+0IiCl0qhXoK7qs//SwYE7zX8lsr/DNk1BRAHYxeLZPL4pguwK29gUEqasYQjqPtEpDRSWEkdHn9g==",
       "dev": true
     },
     "agent-base": {
@@ -265,8 +273,7 @@
     "balanced-match": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
-      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
-      "dev": true
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
     },
     "bcrypt-pbkdf": {
       "version": "1.0.2",
@@ -291,7 +298,6 @@
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "dev": true,
       "requires": {
         "balanced-match": "^1.0.0",
         "concat-map": "0.0.1"
@@ -438,8 +444,7 @@
     "concat-map": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
-      "dev": true
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
     },
     "core-util-is": {
       "version": "1.0.2",
@@ -469,9 +474,9 @@
       "dev": true
     },
     "deep-equal": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.2.tgz",
-      "integrity": "sha512-kX0bjV7tdMuhrhzKPEnVwqfQCuf+IEfN+4Xqv4eKd75xGRyn8yzdQ9ujPY6a221rgJKyQC4KBu1PibDTpa6m9w==",
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.3.tgz",
+      "integrity": "sha512-Spqdl4H+ky45I9ByyJtXteOm9CaIrPmnIPmOhrkKGNYWeDgCvJ8jNYVCTjChxW4FqGuZnLHADc8EKRMX6+CgvA==",
       "requires": {
         "es-abstract": "^1.17.5",
         "es-get-iterator": "^1.1.0",
@@ -479,13 +484,14 @@
         "is-date-object": "^1.0.2",
         "is-regex": "^1.0.5",
         "isarray": "^2.0.5",
-        "object-is": "^1.0.2",
+        "object-is": "^1.1.2",
         "object-keys": "^1.1.1",
+        "object.assign": "^4.1.0",
         "regexp.prototype.flags": "^1.3.0",
         "side-channel": "^1.0.2",
         "which-boxed-primitive": "^1.0.1",
         "which-collection": "^1.0.1",
-        "which-typed-array": "^1.1.1"
+        "which-typed-array": "^1.1.2"
       },
       "dependencies": {
         "isarray": {
@@ -693,9 +699,9 @@
       }
     },
     "fs-extra": {
-      "version": "9.0.0",
-      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.0.tgz",
-      "integrity": "sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g==",
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz",
+      "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==",
       "dev": true,
       "requires": {
         "at-least-node": "^1.0.0",
@@ -707,13 +713,12 @@
     "fs.realpath": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
-      "dev": true
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
     },
     "fsevents": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz",
-      "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==",
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+      "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
       "dev": true,
       "optional": true
     },
@@ -740,7 +745,6 @@
       "version": "7.1.6",
       "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
       "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
-      "dev": true,
       "requires": {
         "fs.realpath": "^1.0.0",
         "inflight": "^1.0.4",
@@ -760,9 +764,9 @@
       }
     },
     "graceful-fs": {
-      "version": "4.2.3",
-      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
-      "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
+      "version": "4.2.4",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+      "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
       "dev": true
     },
     "growl": {
@@ -877,7 +881,6 @@
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
       "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
-      "dev": true,
       "requires": {
         "once": "^1.3.0",
         "wrappy": "1"
@@ -886,8 +889,7 @@
     "inherits": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
-      "dev": true
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
     },
     "is-arguments": {
       "version": "1.0.4",
@@ -920,9 +922,9 @@
       "dev": true
     },
     "is-callable": {
-      "version": "1.1.5",
-      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
-      "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q=="
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz",
+      "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw=="
     },
     "is-date-object": {
       "version": "1.0.2",
@@ -967,11 +969,11 @@
       "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw=="
     },
     "is-regex": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
-      "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz",
+      "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==",
       "requires": {
-        "has": "^1.0.3"
+        "has-symbols": "^1.0.1"
       }
     },
     "is-set": {
@@ -1172,7 +1174,6 @@
       "version": "3.0.4",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
       "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
-      "dev": true,
       "requires": {
         "brace-expansion": "^1.1.7"
       }
@@ -1191,9 +1192,9 @@
       }
     },
     "mocha": {
-      "version": "7.1.1",
-      "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.1.tgz",
-      "integrity": "sha512-3qQsu3ijNS3GkWcccT5Zw0hf/rWvu1fTN9sPvEd81hlwsr30GX2GcDSSoBxo24IR8FelmrAydGC6/1J5QQP4WA==",
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.2.0.tgz",
+      "integrity": "sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ==",
       "dev": true,
       "requires": {
         "ansi-colors": "3.2.3",
@@ -1209,7 +1210,7 @@
         "js-yaml": "3.13.1",
         "log-symbols": "3.0.0",
         "minimatch": "3.0.4",
-        "mkdirp": "0.5.3",
+        "mkdirp": "0.5.5",
         "ms": "2.1.1",
         "node-environment-flags": "1.0.6",
         "object.assign": "4.1.0",
@@ -1251,15 +1252,6 @@
             "path-is-absolute": "^1.0.0"
           }
         },
-        "mkdirp": {
-          "version": "0.5.3",
-          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz",
-          "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==",
-          "dev": true,
-          "requires": {
-            "minimist": "^1.2.5"
-          }
-        },
         "ms": {
           "version": "2.1.1",
           "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
@@ -1269,9 +1261,9 @@
       }
     },
     "moment": {
-      "version": "2.24.0",
-      "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
-      "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
+      "version": "2.26.0",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.26.0.tgz",
+      "integrity": "sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw=="
     },
     "ms": {
       "version": "2.0.0",
@@ -1369,7 +1361,6 @@
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
       "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
-      "dev": true,
       "requires": {
         "wrappy": "1"
       }
@@ -1407,8 +1398,7 @@
     "path-is-absolute": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
-      "dev": true
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
     },
     "path-parse": {
       "version": "1.0.6",
@@ -1437,9 +1427,9 @@
       "dev": true
     },
     "prettier": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.4.tgz",
-      "integrity": "sha512-SVJIQ51spzFDvh4fIbCLvciiDMCrRhlN3mbZvv/+ycjvmF5E73bKdGfU8QDLNmjYJf+lsGnDBC4UUnvTe5OO0w==",
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz",
+      "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==",
       "dev": true
     },
     "psl": {
@@ -1515,9 +1505,9 @@
       "dev": true
     },
     "resolve": {
-      "version": "1.16.1",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.16.1.tgz",
-      "integrity": "sha512-rmAglCSqWWMrrBv/XM6sW0NuRFiKViw/W4d9EbC4pt+49H8JwHy+mcGmALTEg504AUDcLTvb1T2q3E9AnmY+ig==",
+      "version": "1.17.0",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+      "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
       "dev": true,
       "requires": {
         "path-parse": "^1.0.6"
@@ -1707,20 +1697,19 @@
       }
     },
     "tree-kill": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+      "version": "file:third_party/tree-kill",
       "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="
     },
     "tslib": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz",
-      "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==",
+      "version": "1.13.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
+      "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==",
       "dev": true
     },
     "tslint": {
-      "version": "6.1.1",
-      "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.1.tgz",
-      "integrity": "sha512-kd6AQ/IgPRpLn6g5TozqzPdGNZ0q0jtXW4//hRcj10qLYBaa3mTUU2y2MCG+RXZm8Zx+KZi0eA+YCrMyNlF4UA==",
+      "version": "6.1.2",
+      "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.2.tgz",
+      "integrity": "sha512-UyNrLdK3E0fQG/xWNqAFAC5ugtFyPO4JJR1KyyfQAyzR8W0fTRrC91A8Wej4BntFzcvETdCSDa/4PnNYJQLYiA==",
       "dev": true,
       "requires": {
         "@babel/code-frame": "^7.0.0",
@@ -1775,9 +1764,9 @@
       "dev": true
     },
     "typescript": {
-      "version": "3.8.3",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz",
-      "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==",
+      "version": "3.9.5",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.5.tgz",
+      "integrity": "sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==",
       "dev": true
     },
     "universalify": {
@@ -1810,18 +1799,18 @@
       }
     },
     "vscode-debugadapter": {
-      "version": "1.40.0",
-      "resolved": "https://registry.npmjs.org/vscode-debugadapter/-/vscode-debugadapter-1.40.0.tgz",
-      "integrity": "sha512-cudm9ROtFRxiBgcM+B8cQXA1DfsRKaOfEYDMh9upxbYxN3v0c40SHCPmNivIYp7LDzcG60UGaIYD1vsUfC1Qcg==",
+      "version": "1.41.0",
+      "resolved": "https://registry.npmjs.org/vscode-debugadapter/-/vscode-debugadapter-1.41.0.tgz",
+      "integrity": "sha512-b+J8wmsa3NCxJ+L9DAMpRfPM+8bmp4gFBoFp9lhkpwqn3UMs3sYvdcwugQr/T4lDaCCEr807HKMppRsD1EHhPQ==",
       "requires": {
         "mkdirp": "^0.5.1",
-        "vscode-debugprotocol": "1.40.0"
+        "vscode-debugprotocol": "1.41.0"
       }
     },
     "vscode-debugprotocol": {
-      "version": "1.40.0",
-      "resolved": "https://registry.npmjs.org/vscode-debugprotocol/-/vscode-debugprotocol-1.40.0.tgz",
-      "integrity": "sha512-Fwze+9qbLDPuQUhtITJSu/Vk6zIuakNM1iR2ZiZRgRaMEgBpMs2JSKaT0chrhJHCOy6/UbpsUbUBIseF6msV+g=="
+      "version": "1.41.0",
+      "resolved": "https://registry.npmjs.org/vscode-debugprotocol/-/vscode-debugprotocol-1.41.0.tgz",
+      "integrity": "sha512-Sxp7kDDuhpEZiDaIfhM0jLF3RtMqvc6CpoESANE77t351uezsd/oDoqALLcOnmmsDzTgQ3W0sCvM4gErnjDFpA=="
     },
     "vscode-jsonrpc": {
       "version": "5.0.1",
@@ -1859,9 +1848,9 @@
       "integrity": "sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ=="
     },
     "vscode-test": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-1.3.0.tgz",
-      "integrity": "sha512-LddukcBiSU2FVTDr3c1D8lwkiOvwlJdDL2hqVbn6gIz+rpTqUCkMZSKYm94Y1v0WXlHSDQBsXyY+tchWQgGVsw==",
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-1.4.0.tgz",
+      "integrity": "sha512-Jt7HNGvSE0+++Tvtq5wc4hiXLIr2OjDShz/gbAfM/mahQpy4rKBnmOK33D+MR67ATWviQhl+vpmU3p/qwSH/Pg==",
       "dev": true,
       "requires": {
         "http-proxy-agent": "^2.1.0",
@@ -1888,9 +1877,9 @@
       }
     },
     "websocket-extensions": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz",
-      "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg=="
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+      "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg=="
     },
     "which": {
       "version": "1.3.1",
@@ -1994,8 +1983,7 @@
     "wrappy": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
-      "dev": true
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
     },
     "y18n": {
       "version": "4.0.0",
@@ -2069,6 +2057,12 @@
         "lodash": "^4.17.15",
         "yargs": "^13.3.0"
       }
+    },
+    "yarn": {
+      "version": "1.22.4",
+      "resolved": "https://registry.npmjs.org/yarn/-/yarn-1.22.4.tgz",
+      "integrity": "sha512-oYM7hi/lIWm9bCoDMEWgffW8aiNZXCWeZ1/tGy0DWrN6vmzjCXIKu2Y21o8DYVBUtiktwKcNoxyGl/2iKLUNGA==",
+      "dev": true
     }
   }
 }
diff --git a/package.json b/package.json
index 4e9673c..0a4b81f 100644
--- a/package.json
+++ b/package.json
@@ -1,12 +1,13 @@
 {
-  "name": "Go",
+  "name": "go",
   "displayName": "Go",
-  "version": "0.14.4",
+  "version": "0.15.0",
   "publisher": "golang",
   "description": "Rich Go language support for Visual Studio Code",
   "author": {
     "name": "Go Team at Google"
   },
+  "preview": true,
   "license": "MIT",
   "icon": "images/go-logo-blue.png",
   "categories": [
@@ -44,18 +45,21 @@
   },
   "extensionDependencies": [],
   "dependencies": {
+    "deep-equal": "^2.0.2",
     "diff": "^4.0.2",
+    "glob": "^7.1.6",
     "json-rpc2": "^1.0.2",
     "moment": "^2.24.0",
     "semver": "^7.3.2",
-    "tree-kill": "^1.2.2",
+    "tree-kill": "file:third_party/tree-kill",
     "vscode-debugadapter": "^1.40.0",
     "vscode-debugprotocol": "^1.40.0",
     "vscode-languageclient": "6.1.0",
-    "web-request": "^1.0.7",
-    "deep-equal": "^2.0.2"
+    "web-request": "^1.0.7"
   },
   "devDependencies": {
+    "@types/adm-zip": "^0.4.33",
+    "@types/deep-equal": "^1.0.1",
     "@types/fs-extra": "^8.1.0",
     "@types/glob": "^7.1.1",
     "@types/mocha": "^7.0.2",
@@ -63,15 +67,15 @@
     "@types/semver": "^7.1.0",
     "@types/sinon": "^9.0.0",
     "@types/vscode": "^1.41.0",
+    "adm-zip": "^0.4.14",
     "fs-extra": "^9.0.0",
-    "glob": "^7.1.6",
     "mocha": "^7.1.1",
     "prettier": "^2.0.4",
     "sinon": "^9.0.2",
     "tslint": "^6.1.1",
     "typescript": "^3.8.3",
     "vscode-test": "^1.3.0",
-    "@types/deep-equal": "^1.0.1"
+    "yarn": "^1.22.4"
   },
   "engines": {
     "vscode": "^1.41.0"
@@ -159,6 +163,11 @@
         "description": "Runs a unit test at the cursor."
       },
       {
+        "command": "go.subtest.cursor",
+        "title": "Go: Subtest At Cursor",
+        "description": "Runs a sub test at the cursor."
+      },
+      {
         "command": "go.benchmark.cursor",
         "title": "Go: Benchmark Function At Cursor",
         "description": "Runs a benchmark at the cursor."
@@ -337,6 +346,11 @@
         "command": "go.languageserver.restart",
         "title": "Go: Restart Language Server",
         "description": "Restart the running instance of the language server"
+      },
+      {
+        "command": "go.environment.choose",
+        "title": "Go: Choose Go Environment",
+        "description": "Choose a different Go version or binary for this project. (WIP)"
       }
     ],
     "breakpoints": [
@@ -871,13 +885,13 @@
           ],
           "default": null,
           "description": "Specify GOPATH here to override the one that is set as environment variable. The inferred GOPATH from workspace root overrides this, if go.inferGopath is set to true.",
-          "scope": "resource"
+          "scope": "machine-overridable"
         },
         "go.toolsGopath": {
           "type": "string",
           "default": "",
           "description": "Location to install the Go tools that the extension depends on if you don't want them in your GOPATH.",
-          "scope": "resource"
+          "scope": "machine-overridable"
         },
         "go.goroot": {
           "type": [
@@ -886,7 +900,7 @@
           ],
           "default": null,
           "description": "Specifies the GOROOT to use when no environment variable is set.",
-          "scope": "resource"
+          "scope": "machine-overridable"
         },
         "go.testOnSave": {
           "type": "boolean",
@@ -1130,6 +1144,16 @@
           },
           "description": "Use this setting to enable/disable experimental features from the language server."
         },
+        "go.trace.server": {
+          "type": "string",
+          "enum": [
+            "off",
+            "messages",
+            "verbose"
+          ],
+          "default": "off",
+          "description": "Trace the communication between VS Code and the Go language server."
+        },
         "go.useGoProxyToCheckForToolUpdates": {
           "type": "boolean",
           "default": true,
diff --git a/src/avlTree.ts b/src/avlTree.ts
index 632ad90..7833a42 100644
--- a/src/avlTree.ts
+++ b/src/avlTree.ts
@@ -32,16 +32,16 @@
  * Represents a node in the binary tree, which has a key and a value, as well as left and right subtrees
  */
 export class Node<K, V> {
-	public left: Node<K, V> = null;
-	public right: Node<K, V> = null;
-	public height: number = null;
+	public left: Node<K, V> | null = null;
+	public right: Node<K, V> | null = null;
+	public height: number = 0;
 
 	/**
 	 * Creates a new AVL Tree node.
 	 * @param key The key of the new node.
 	 * @param value The value of the new node.
 	 */
-	constructor(public key: K, public value: V) {}
+	constructor(public key: K, public value: V | undefined) {}
 
 	/**
 	 * Convenience function to get the height of the left child of the node,
@@ -77,7 +77,7 @@
 		//   a   e -> b.rotateRight() -> c   b
 		//  / \                             / \
 		// c   d                           d   e
-		const other = this.left;
+		const other = <Node<K, V>>this.left;
 		this.left = other.right;
 		other.right = this;
 		this.height = Math.max(this.leftHeight, this.rightHeight) + 1;
@@ -95,7 +95,7 @@
 		// c   b   -> a.rotateLeft() ->   a   e
 		//    / \                        / \
 		//   d   e                      c   d
-		const other = this.right;
+		const other = <Node<K, V>>this.right;
 		this.right = other.left;
 		other.left = this;
 		this.height = Math.max(this.leftHeight, this.rightHeight) + 1;
@@ -127,7 +127,7 @@
 	public static NUMERIC_DISTANCE_FUNCTION = (a: number, b: number) => (a > b ? a - b : b - a);
 	public static DEFAULT_COMPARE_FUNCTION = (a: any, b: any) => (a > b ? 1 : a < b ? -1 : 0);
 
-	protected root: Node<K, V> = null;
+	protected root: Node<K, V>|null = null;
 
 	/**
 	 * Creates a new AVL Tree.
@@ -141,7 +141,7 @@
 	}
 
 	public height() {
-		return this.root.height;
+		return this.root ? this.root.height : 0;
 	}
 
 	/**
@@ -159,7 +159,7 @@
 	 * @return The (key, value) pair of the node with key nearest the given key in value.
 	 */
 	public getNearest(key: K): Node<K, V> {
-		return this._getNearest(key, this.root, this.root);
+		return this._getNearest(key, this.root!, this.root!);
 	}
 
 	/**
@@ -168,7 +168,7 @@
 	 * @param root The root of the tree to insert in.
 	 * @return The new tree root.
 	 */
-	private _insert(key: K, value: V, root: Node<K, V>): Node<K, V> {
+	private _insert(key: K, value: V | undefined, root: Node<K, V> | null): Node<K, V> {
 		// Perform regular BST insertion
 		if (root === null) {
 			return new Node(key, value);
@@ -187,23 +187,23 @@
 		const balanceState = this._getBalanceState(root);
 
 		if (balanceState === BalanceState.UNBALANCED_LEFT) {
-			if (this.compare(key, root.left.key) < 0) {
+			if (this.compare(key, root.left!.key) < 0) {
 				// Left left case
 				root = root.rotateRight();
 			} else {
 				// Left right case
-				root.left = root.left.rotateLeft();
+				root.left = root.left!.rotateLeft();
 				return root.rotateRight();
 			}
 		}
 
 		if (balanceState === BalanceState.UNBALANCED_RIGHT) {
-			if (this.compare(key, root.right.key) > 0) {
+			if (this.compare(key, root.right!.key) > 0) {
 				// Right right case
 				root = root.rotateLeft();
 			} else {
 				// Right left case
-				root.right = root.right.rotateRight();
+				root.right = root.right!.rotateRight();
 				return root.rotateLeft();
 			}
 		}
@@ -259,9 +259,7 @@
 				if (heightDifference > 0) {
 					return BalanceState.UNBALANCED_LEFT;
 				}
-				if (heightDifference < 0) {
-					return BalanceState.UNBALANCED_RIGHT;
-				}
+				return BalanceState.UNBALANCED_RIGHT;  // heightDifference can't be 0
 			}
 		}
 	}
diff --git a/src/const.ts b/src/const.ts
new file mode 100644
index 0000000..3783d8b
--- /dev/null
+++ b/src/const.ts
@@ -0,0 +1,12 @@
+/*---------------------------------------------------------
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ * Modification copyright 2020 The Go Authors. All rights reserved.
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
+ *--------------------------------------------------------*/
+
+// This file contains constants that need to change when building Nightly or Go2Go.
+//
+// For example, we replace this file with the version in the build/nightly.
+// Make sure to reflect any changes in this file to build/nightly/const.ts.
+
+export const extensionId: string = 'golang.go';
diff --git a/src/debugAdapter/.vscode/launch.json b/src/debugAdapter/.vscode/launch.json
index 45b3ae4..3362173 100644
--- a/src/debugAdapter/.vscode/launch.json
+++ b/src/debugAdapter/.vscode/launch.json
@@ -2,7 +2,7 @@
 	"version": "0.2.0",
 	"configurations": [
 		{
-			"name": "debug-debugger",
+			"name": "Launch as server",
 			"type": "node",
 			"request": "launch",
 			"program": "${workspaceFolder}/../../out/src/debugAdapter/goDebug.js",
diff --git a/src/debugAdapter/goDebug.ts b/src/debugAdapter/goDebug.ts
index ec745cf..a2b3c49 100644
--- a/src/debugAdapter/goDebug.ts
+++ b/src/debugAdapter/goDebug.ts
@@ -3,9 +3,11 @@
  * Licensed under the MIT License. See LICENSE in the project root for license information.
  *--------------------------------------------------------*/
 
-import { ChildProcess, execFile, execSync, spawn, spawnSync } from 'child_process';
+import { ChildProcess, execFile, spawn } from 'child_process';
+import { EventEmitter } from 'events';
 import * as fs from 'fs';
 import { existsSync, lstatSync } from 'fs';
+import * as glob from 'glob';
 import { Client, RPCConnection } from 'json-rpc2';
 import * as os from 'os';
 import * as path from 'path';
@@ -13,6 +15,7 @@
 import * as util from 'util';
 import {
 	DebugSession,
+	ErrorDestination,
 	Handles,
 	InitializedEvent,
 	logger,
@@ -88,6 +91,20 @@
 	Running: boolean;
 }
 
+export interface PackageBuildInfo {
+	ImportPath: string;
+	DirectoryPath: string;
+	Files: string[];
+}
+
+export interface ListPackagesBuildInfoOut {
+	List: PackageBuildInfo[];
+}
+
+export interface ListSourcesOut {
+	Sources: string[];
+}
+
 interface CreateBreakpointOut {
 	Breakpoint: DebugBreakpoint;
 }
@@ -308,6 +325,10 @@
 	return filePath.includes('/') ? '/' : '\\';
 }
 
+export function escapeGoModPath(filePath: string) {
+	return filePath.replace(/[A-Z]/g, (match: string) => `!${match.toLocaleLowerCase()}`);
+}
+
 function normalizePath(filePath: string) {
 	if (process.platform === 'win32') {
 		const pathSeparator = findPathSeparator(filePath);
@@ -319,7 +340,11 @@
 	return filePath;
 }
 
-class Delve {
+function getBaseName(filePath: string) {
+	return filePath.includes('/') ? path.basename(filePath) : path.win32.basename(filePath);
+}
+
+export class Delve {
 	public program: string;
 	public remotePath: string;
 	public loadConfig: LoadConfig;
@@ -332,6 +357,7 @@
 	public dlvEnv: any;
 	public stackTraceDepth: number;
 	public isRemoteDebugging: boolean;
+	public goroot: string;
 	private localDebugeePath: string | undefined;
 	private debugProcess: ChildProcess;
 	private request: 'attach' | 'launch';
@@ -345,7 +371,7 @@
 			this.isApiV1 = launchArgs.apiVersion === 1;
 		}
 		this.stackTraceDepth = typeof launchArgs.stackTraceDepth === 'number' ? launchArgs.stackTraceDepth : 50;
-		this.connection = new Promise((resolve, reject) => {
+		this.connection = new Promise(async (resolve, reject) => {
 			const mode = launchArgs.mode;
 			let dlvCwd = path.dirname(program);
 			let serverRunning = false;
@@ -363,8 +389,10 @@
 			};
 
 			if (mode === 'remote') {
+				log(`Start remote debugging: connecting ${launchArgs.host}:${launchArgs.port}`);
 				this.debugProcess = null;
 				this.isRemoteDebugging = true;
+				this.goroot = await queryGOROOT(dlvCwd, process.env);
 				serverRunning = true; // assume server is running when in remote mode
 				connectClient(launchArgs.port, launchArgs.host);
 				return;
@@ -420,7 +448,11 @@
 					env['GOPATH'] = getInferredGopath(dirname) || env['GOPATH'];
 				}
 				this.dlvEnv = env;
+				this.goroot = await queryGOROOT(dlvCwd, env);
+
 				log(`Using GOPATH: ${env['GOPATH']}`);
+				log(`Using GOROOT: ${this.goroot}`);
+				log(`Using PATH: ${env['PATH']}`);
 
 				if (!!launchArgs.noDebug) {
 					if (mode === 'debug') {
@@ -439,7 +471,11 @@
 							runArgs.push(...launchArgs.args);
 						}
 
-						this.debugProcess = spawn(getBinPathWithPreferredGopath('go', []), runArgs, runOptions);
+						const goExe = getBinPathWithPreferredGopath('go', []);
+						log(`Current working directory: ${dirname}`);
+						log(`Running: ${goExe} ${runArgs.join(' ')}`);
+
+						this.debugProcess = spawn(goExe, runArgs, runOptions);
 						this.debugProcess.stderr.on('data', (chunk) => {
 							const str = chunk.toString();
 							if (this.onstderr) {
@@ -453,7 +489,11 @@
 							}
 						});
 						this.debugProcess.on('close', (code) => {
-							logError('Process exiting with code: ' + code);
+							if (code) {
+								logError(`Process exiting with code: ${code} signal: ${this.debugProcess.killed}`);
+							} else {
+								log(`Process exiting normally ${this.debugProcess.killed}`);
+							}
 							if (this.onclose) {
 								this.onclose(code);
 							}
@@ -470,7 +510,7 @@
 				if (!existsSync(launchArgs.dlvToolPath)) {
 					log(
 						`Couldn't find dlv at the Go tools path, ${process.env['GOPATH']}${
-							env['GOPATH'] ? ', ' + env['GOPATH'] : ''
+						env['GOPATH'] ? ', ' + env['GOPATH'] : ''
 						} or ${envPath}`
 					);
 					return reject(
@@ -565,6 +605,7 @@
 						}
 						return resolve(conn);
 					});
+					client.on('error', reject);
 				}, 200);
 			}
 
@@ -638,6 +679,8 @@
 	/**
 	 * Closing a debugging session follows different approaches for launch vs attach debugging.
 	 *
+	 * For launch without debugging, we kill the process since the extension started the `go run` process.
+	 *
 	 * For launch debugging, since the extension starts the delve process, the extension should close it as well.
 	 * To gracefully clean up the assets created by delve, we send the Detach request with kill option set to true.
 	 *
@@ -649,18 +692,21 @@
 	 * Since the Halt request might sometimes take too long to complete, we have a timer in place to forcefully kill
 	 * the debug process and clean up the assets in case of local debugging
 	 */
-	public close(): Thenable<void> {
+	public async close(): Promise<void> {
+		const forceCleanup = async () => {
+			log(`killing debugee (pid: ${this.debugProcess.pid})...`);
+			await killProcessTree(this.debugProcess);
+			await removeFile(this.localDebugeePath);
+		};
+
 		if (this.noDebug) {
 			// delve isn't running so no need to halt
+			await forceCleanup();
 			return Promise.resolve();
 		}
 		log('HaltRequest');
-
 		const isLocalDebugging: boolean = this.request === 'launch' && !!this.debugProcess;
-		const forceCleanup = async () => {
-			kill(this.debugProcess.pid, (err) => console.log('Error killing debug process: ' + err));
-			await removeFile(this.localDebugeePath);
-		};
+
 		return new Promise(async (resolve) => {
 			// For remote debugging, closing the connection would terminate the
 			// program as well so we just want to disconnect.
@@ -715,7 +761,7 @@
 	}
 }
 
-class GoDebugSession extends LoggingDebugSession {
+export class GoDebugSession extends LoggingDebugSession {
 	private variableHandles: Handles<DebugVariable>;
 	private breakpoints: Map<string, DebugBreakpoint[]>;
 	// Editing breakpoints requires halting delve, skip sending Stop Event to VS Code in such cases
@@ -729,12 +775,18 @@
 	private stopOnEntry: boolean;
 	private logLevel: Logger.LogLevel = Logger.LogLevel.Error;
 	private readonly initdone = 'initdone·';
+	private remoteSourcesAndPackages = new RemoteSourcesAndPackages();
+	private localToRemotePathMapping = new Map<string, string>();
+	private remoteToLocalPathMapping = new Map<string, string>();
 
 	private showGlobalVariables: boolean = false;
 
 	private continueEpoch = 0;
 	private continueRequestRunning = false;
-	public constructor(debuggerLinesStartAt1: boolean, isServer: boolean = false) {
+	public constructor(
+		debuggerLinesStartAt1: boolean,
+		isServer: boolean = false,
+		readonly fileSystem = fs) {
 		super('', debuggerLinesStartAt1, isServer);
 		this.variableHandles = new Handles<DebugVariable>();
 		this.skipStopEventOnce = false;
@@ -758,6 +810,7 @@
 	}
 
 	protected launchRequest(response: DebugProtocol.LaunchResponse, args: LaunchRequestArguments): void {
+		log('LaunchRequest');
 		if (!args.program) {
 			this.sendErrorResponse(
 				response,
@@ -770,6 +823,7 @@
 	}
 
 	protected attachRequest(response: DebugProtocol.AttachResponse, args: AttachRequestArguments): void {
+		log('AttachRequest');
 		if (args.mode === 'local' && !args.processId) {
 			this.sendErrorResponse(
 				response,
@@ -827,10 +881,76 @@
 		}
 	}
 
-	protected toDebuggerPath(filePath: string): string {
+	/**
+	 * Given a potential list of paths in potentialPaths array, we will
+	 * find the path that has the longest suffix matching filePath.
+	 * For example, if filePath is /usr/local/foo/bar/main.go
+	 * and potentialPaths are abc/xyz/main.go, bar/main.go
+	 * then bar/main.go will be the result.
+	 * NOTE: This function assumes that potentialPaths array only contains
+	 * files with the same base names as filePath.
+	 */
+	protected findPathWithBestMatchingSuffix(filePath: string, potentialPaths: string[]): string | undefined {
+		if (!potentialPaths.length) {
+			return;
+		}
+
+		if (potentialPaths.length === 1) {
+			return potentialPaths[0];
+		}
+
+		const filePathSegments = filePath.split(/\/|\\/).reverse();
+		let bestPathSoFar = potentialPaths[0];
+		let bestSegmentsCount = 0;
+		for (const potentialPath of potentialPaths) {
+			const potentialPathSegments = potentialPath.split(/\/|\\/).reverse();
+			let i = 0;
+			for (; i < filePathSegments.length
+				&& i < potentialPathSegments.length
+				&& filePathSegments[i] === potentialPathSegments[i]; i++) {
+				if (i > bestSegmentsCount) {
+					bestSegmentsCount = i;
+					bestPathSoFar = potentialPath;
+				}
+			}
+		}
+		return bestPathSoFar;
+	}
+
+	/**
+	 * Given a local path, try to find matching file in the remote machine
+	 * using remote sources and remote packages info that we get from Delve.
+	 * The result would be cached in localToRemotePathMapping.
+	 */
+	protected inferRemotePathFromLocalPath(localPath: string): string | undefined {
+		if (this.localToRemotePathMapping.has(localPath)) {
+			return this.localToRemotePathMapping.get(localPath);
+		}
+
+		const fileName = getBaseName(localPath);
+		const potentialMatchingRemoteFiles = this.remoteSourcesAndPackages.remoteSourceFilesNameGrouping.get(fileName);
+		const bestMatchingRemoteFile = this.findPathWithBestMatchingSuffix(localPath, potentialMatchingRemoteFiles);
+		if (!bestMatchingRemoteFile) {
+			return;
+		}
+
+		this.localToRemotePathMapping.set(localPath, bestMatchingRemoteFile);
+		return bestMatchingRemoteFile;
+	}
+
+	protected async toDebuggerPath(filePath: string): Promise<string> {
 		if (this.delve.remotePath.length === 0) {
+			if (this.delve.isRemoteDebugging) {
+				// The user trusts us to infer the remote path mapping!
+				await this.initializeRemotePackagesAndSources();
+				const matchedRemoteFile = this.inferRemotePathFromLocalPath(filePath);
+				if (matchedRemoteFile) {
+					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
@@ -838,8 +958,160 @@
 		return filePath.replace(this.delve.program.replace(/\/|\\/g, this.remotePathSeparator), this.delve.remotePath);
 	}
 
+	/**
+	 * Given a remote path, try to infer the matching local path.
+	 * We attempt to find the path in local Go packages as well as workspaceFolder.
+	 * Cache the result in remoteToLocalPathMapping.
+	 */
+	protected inferLocalPathFromRemotePath(remotePath: string): string | undefined {
+		if (this.remoteToLocalPathMapping.has(remotePath)) {
+			return this.remoteToLocalPathMapping.get(remotePath);
+		}
+
+		const convertedLocalPackageFile = this.inferLocalPathFromRemoteGoPackage(remotePath);
+		if (convertedLocalPackageFile) {
+			this.remoteToLocalPathMapping.set(remotePath, convertedLocalPackageFile);
+			return convertedLocalPackageFile;
+		}
+
+		// If we cannot find the path in packages, most likely it will be in the current directory.
+		const fileName = getBaseName(remotePath);
+		const globSync = glob.sync(fileName, {
+			matchBase: true,
+			cwd: this.delve.program
+		});
+		const bestMatchingLocalPath = this.findPathWithBestMatchingSuffix(remotePath, globSync);
+		if (bestMatchingLocalPath) {
+			const fullLocalPath = path.join(this.delve.program, bestMatchingLocalPath);
+			this.remoteToLocalPathMapping.set(remotePath, fullLocalPath);
+			return fullLocalPath;
+		}
+	}
+
+	/**
+	 * Given a remote path, we attempt to infer the local path by first checking
+	 * if it is in any remote packages. If so, then we attempt to find the matching
+	 * local package and find the local path from there.
+	 */
+	protected inferLocalPathFromRemoteGoPackage(remotePath: string): string | undefined {
+		const remotePackage = this.remoteSourcesAndPackages.remotePackagesBuildInfo.find(
+			(buildInfo) => remotePath.startsWith(buildInfo.DirectoryPath));
+		// Since we know pathToConvert exists in a remote package, we can try to find
+		// that same package in the local client. We can use import path to search for the package.
+		if (!remotePackage) {
+			return;
+		}
+
+		if (!this.remotePathSeparator) {
+			this.remotePathSeparator = findPathSeparator(remotePackage.DirectoryPath);
+		}
+
+		// Escaping package path.
+		// It seems like sometimes Delve don't escape the path properly
+		// so we should do it.
+		remotePath = escapeGoModPath(remotePath);
+		const escapedImportPath = escapeGoModPath(remotePackage.ImportPath);
+
+		// The remotePackage.DirectoryPath should be something like
+		// <gopath|goroot|source>/<import-path>/xyz...
+		// Directory Path can be like "/go/pkg/mod/github.com/google/go-cmp@v0.4.0/cmp"
+		// and Import Path can be like "github.com/google/go-cmp/cmp"
+		// and Remote Path "/go/pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/blah.go"
+		const importPathIndex = remotePath.replace(/@v\d+\.\d+\.\d+[^\/]*/, '')
+			.indexOf(escapedImportPath);
+		if (importPathIndex < 0) {
+			return;
+		}
+
+		const relativeRemotePath = remotePath
+			.substr(importPathIndex)
+			.split(this.remotePathSeparator)
+			.join(this.localPathSeparator);
+		const pathToConvertWithLocalSeparator = remotePath.split(this.remotePathSeparator).join(this.localPathSeparator);
+
+		// Scenario 1: The package is inside the current working directory.
+		const localWorkspacePath = path.join(this.delve.program, relativeRemotePath);
+		if (this.fileSystem.existsSync(localWorkspacePath)) {
+			return localWorkspacePath;
+		}
+
+		// Scenario 2: The package is inside GOPATH.
+		const localGoPathImportPath = this.inferLocalPathInGoPathFromRemoteGoPackage(
+			pathToConvertWithLocalSeparator, relativeRemotePath);
+		if (localGoPathImportPath) {
+			return localGoPathImportPath;
+		}
+
+		// Scenario 3: The package is inside GOROOT.
+		return this.inferLocalPathInGoRootFromRemoteGoPackage(pathToConvertWithLocalSeparator, relativeRemotePath);
+	}
+
+	/**
+	 * Given a remotePath, check whether the file path exists in $GOROOT/src.
+	 * Return the path if it exists.
+	 * We are assuming that remotePath is of the form <prefix>/src/<suffix>.
+	 */
+	protected inferLocalPathInGoRootFromRemoteGoPackage(
+		remotePathWithLocalSeparator: string, relativeRemotePath: string): string | undefined {
+		const srcIndex = remotePathWithLocalSeparator.indexOf(`${this.localPathSeparator}src${this.localPathSeparator}`);
+		const goroot = this.getGOROOT();
+		const localGoRootImportPath = path.join(
+			goroot,
+			srcIndex >= 0
+				? remotePathWithLocalSeparator.substr(srcIndex)
+				: path.join('src', relativeRemotePath));
+		if (this.fileSystem.existsSync(localGoRootImportPath)) {
+			return localGoRootImportPath;
+		}
+	}
+
+	/**
+	 * Given a remotePath, check whether the file path exists in $GOPATH.
+	 * This can be either in $GOPATH/pkg/mod or $GOPATH/src. If so, return that path.
+	 * remotePath can be something like /usr/local/gopath/src/hello-world/main.go
+	 * and relativeRemotePath should be hello-world/main.go. In other words,
+	 * relativeRemotePath is a relative version of remotePath starting
+	 * from the import path of the module.
+	 */
+	protected inferLocalPathInGoPathFromRemoteGoPackage(
+		remotePathWithLocalSeparator: string, relativeRemotePath: string): string | undefined {
+		// Scenario 1: The package is inside $GOPATH/pkg/mod.
+		const gopath = (process.env['GOPATH'] || '').split(path.delimiter)[0];
+
+		const indexGoModCache = remotePathWithLocalSeparator.indexOf(
+			`${this.localPathSeparator}pkg${this.localPathSeparator}mod${this.localPathSeparator}`
+		);
+		const localGoPathImportPath = path.join(
+			gopath,
+			indexGoModCache >= 0
+				? remotePathWithLocalSeparator.substr(indexGoModCache)
+				: path.join('pkg', 'mod', relativeRemotePath));
+		if (this.fileSystem.existsSync(localGoPathImportPath)) {
+			return localGoPathImportPath;
+		}
+
+		// Scenario 2: The file is in a package in $GOPATH/src.
+		const localGoPathSrcPath = path.join(
+			gopath, 'src',
+			relativeRemotePath.split(this.remotePathSeparator).join(this.localPathSeparator));
+		if (this.fileSystem.existsSync(localGoPathSrcPath)) {
+			return localGoPathSrcPath;
+		}
+	}
+
+	/**
+	 * This functions assumes that remote packages and paths information
+	 * have been initialized.
+	 */
 	protected toLocalPath(pathToConvert: string): string {
 		if (this.delve.remotePath.length === 0) {
+			// User trusts use to infer the path
+			if (this.delve.isRemoteDebugging) {
+				const inferredPath = this.inferLocalPathFromRemotePath(pathToConvert);
+				if (inferredPath) {
+					return inferredPath;
+				}
+			}
 			return this.convertDebuggerPathToClient(pathToConvert);
 		}
 
@@ -847,7 +1119,7 @@
 		if (!pathToConvert.startsWith(this.delve.remotePath)) {
 			// Fix for https://github.com/Microsoft/vscode-go/issues/1178
 			const index = pathToConvert.indexOf(`${this.remotePathSeparator}src${this.remotePathSeparator}`);
-			const goroot = process.env['GOROOT'];
+			const goroot = this.getGOROOT();
 			if (goroot && index > 0) {
 				return path.join(goroot, pathToConvert.substr(index));
 			}
@@ -968,7 +1240,7 @@
 		this.delve.call<DebugLocation[] | StacktraceOut>(
 			this.delve.isApiV1 ? 'StacktraceGoroutine' : 'Stacktrace',
 			[stackTraceIn],
-			(err, out) => {
+			async (err, out) => {
 				if (err) {
 					this.logDelveError(err, 'Failed to produce stacktrace');
 					return this.sendErrorResponse(response, 2004, 'Unable to produce stack trace: "{e}"', {
@@ -977,6 +1249,11 @@
 				}
 				const locations = this.delve.isApiV1 ? <DebugLocation[]>out : (<StacktraceOut>out).Locations;
 				log('locations', locations);
+
+				if (this.delve.isRemoteDebugging) {
+					await this.initializeRemotePackagesAndSources();
+				}
+
 				let stackFrames = locations.map((location, frameId) => {
 					const uniqueStackFrameId = this.stackFrameHandles.create([goroutineId, frameId]);
 					return new StackFrame(
@@ -1329,9 +1606,18 @@
 				log('EvaluateResponse');
 			},
 			(err) => {
+				let dest: ErrorDestination;
+				// No need to repeatedly show the error pop-up when expressions
+				// are continiously reevaluated in the Watch panel, which
+				// already displays errors.
+				if (args.context === 'watch') {
+					dest = null;
+				} else {
+					dest = ErrorDestination.User;
+				}
 				this.sendErrorResponse(response, 2009, 'Unable to eval expression: "{e}"', {
 					e: err.toString()
-				});
+				}, dest);
 			}
 		);
 	}
@@ -1361,6 +1647,15 @@
 		});
 	}
 
+	private getGOROOT(): string {
+		if (this.delve && this.delve.goroot) {
+			return this.delve.goroot;
+		}
+		return process.env['GOROOT'] || '';
+		// this is a workaround to keep the tests in integration/goDebug.test.ts running.
+		// The tests synthesize a bogus Delve instance.
+	}
+
 	// contains common code for launch and attach debugging initialization
 	private initLaunchAttachRequest(
 		response: DebugProtocol.LaunchResponse,
@@ -1370,8 +1665,8 @@
 			args.trace === 'verbose'
 				? Logger.LogLevel.Verbose
 				: args.trace === 'log'
-				? Logger.LogLevel.Log
-				: Logger.LogLevel.Error;
+					? Logger.LogLevel.Log
+					: Logger.LogLevel.Error;
 		const logPath =
 			this.logLevel !== Logger.LogLevel.Error ? path.join(os.tmpdir(), 'vscode-go-debug.txt') : undefined;
 		logger.setup(this.logLevel, logPath);
@@ -1400,8 +1695,8 @@
 			args.remotePath = '';
 		}
 
+		this.localPathSeparator = findPathSeparator(localPath);
 		if (args.remotePath.length > 0) {
-			this.localPathSeparator = findPathSeparator(localPath);
 			this.remotePathSeparator = findPathSeparator(args.remotePath);
 
 			const llist = localPath.split(/\/|\\/).reverse();
@@ -1476,15 +1771,46 @@
 		);
 	}
 
-	private setBreakPoints(
+	/**
+	 * Initializing remote packages and sources.
+	 * We use event model to prevent race conditions.
+	 */
+	private async initializeRemotePackagesAndSources(): Promise<void> {
+		if (this.remoteSourcesAndPackages.initializedRemoteSourceFiles) {
+			return;
+		}
+
+		if (!this.remoteSourcesAndPackages.initializingRemoteSourceFiles) {
+			try {
+				await this.remoteSourcesAndPackages.initializeRemotePackagesAndSources(this.delve);
+			} catch (error) {
+				log(`Failing to initialize remote sources: ${error}`);
+			}
+			return;
+		}
+
+		if (this.remoteSourcesAndPackages.initializingRemoteSourceFiles) {
+			try {
+				await new Promise((resolve) => {
+					this.remoteSourcesAndPackages.on(RemoteSourcesAndPackages.INITIALIZED, () => {
+						resolve();
+					});
+				});
+			} catch (error) {
+				log(`Failing to initialize remote sources: ${error}`);
+			}
+		}
+	}
+
+	private async setBreakPoints(
 		response: DebugProtocol.SetBreakpointsResponse,
 		args: DebugProtocol.SetBreakpointsArguments
-	): Thenable<void> {
+	): Promise<void> {
 		const file = normalizePath(args.source.path);
 		if (!this.breakpoints.get(file)) {
 			this.breakpoints.set(file, []);
 		}
-		const remoteFile = this.toDebuggerPath(file);
+		const remoteFile = await this.toDebuggerPath(file);
 
 		return Promise.all(
 			this.breakpoints.get(file).map((existingBP) => {
@@ -1591,12 +1917,15 @@
 			);
 	}
 
-	private getPackageInfo(debugState: DebuggerState): Thenable<string> {
+	private async getPackageInfo(debugState: DebuggerState): Promise<string> {
 		if (!debugState.currentThread || !debugState.currentThread.file) {
 			return Promise.resolve(null);
 		}
+		if (this.delve.isRemoteDebugging) {
+			await this.initializeRemotePackagesAndSources();
+		}
 		const dir = path.dirname(
-			this.delve.remotePath.length
+			this.delve.remotePath.length || this.delve.isRemoteDebugging
 				? this.toLocalPath(debugState.currentThread.file)
 				: debugState.currentThread.file
 		);
@@ -1909,6 +2238,65 @@
 	}
 }
 
+// Class for fetching remote sources and packages
+// in the remote program using Delve.
+// tslint:disable-next-line:max-classes-per-file
+export class RemoteSourcesAndPackages extends EventEmitter {
+	public static readonly INITIALIZED = 'INITIALIZED';
+
+	public initializingRemoteSourceFiles = false;
+	public initializedRemoteSourceFiles = false;
+
+	public remotePackagesBuildInfo: PackageBuildInfo[] = [];
+	public remoteSourceFiles: string[] = [];
+	public remoteSourceFilesNameGrouping = new Map<string, string[]>();
+
+	/**
+	 * Initialize and fill out remote packages build info and remote source files.
+	 * Emits the INITIALIZED event once initialization is complete.
+	 */
+	public async initializeRemotePackagesAndSources(delve: Delve): Promise<void> {
+		this.initializingRemoteSourceFiles = true;
+
+		try {
+			// ListPackagesBuildInfo is not available on V1.
+			if (!delve.isApiV1 && this.remotePackagesBuildInfo.length === 0) {
+				const packagesBuildInfoResponse: ListPackagesBuildInfoOut = await delve.callPromise(
+					'ListPackagesBuildInfo', [{ IncludeFiles: true }]
+				);
+				if (packagesBuildInfoResponse && packagesBuildInfoResponse.List) {
+					this.remotePackagesBuildInfo = packagesBuildInfoResponse.List;
+				}
+			}
+
+			// List sources will return all the source files used by Delve.
+			if (delve.isApiV1) {
+				this.remoteSourceFiles = await delve.callPromise('ListSources', []);
+			} else {
+				const listSourcesResponse: ListSourcesOut = await delve.callPromise('ListSources', [{}]);
+				if (listSourcesResponse && listSourcesResponse.Sources) {
+					this.remoteSourceFiles = listSourcesResponse.Sources;
+				}
+			}
+
+			// Group the source files by name for easy searching later.
+			this.remoteSourceFiles = this.remoteSourceFiles.filter((sourceFile) => !sourceFile.startsWith('<'));
+			this.remoteSourceFiles.forEach((sourceFile) => {
+				const fileName = getBaseName(sourceFile);
+				if (!this.remoteSourceFilesNameGrouping.has(fileName)) {
+					this.remoteSourceFilesNameGrouping.set(fileName, []);
+				}
+				this.remoteSourceFilesNameGrouping.get(fileName).push(sourceFile);
+			});
+		} catch (error) {
+			logError(`Failed to initialize remote sources and packages: ${error && error.message}`);
+		} finally {
+			this.emit(RemoteSourcesAndPackages.INITIALIZED);
+			this.initializedRemoteSourceFiles = true;
+		}
+	}
+}
+
 function random(low: number, high: number): number {
 	return Math.floor(Math.random() * (high - low) + low);
 }
@@ -1926,4 +2314,37 @@
 	}
 }
 
+function killProcessTree(p: ChildProcess): Promise<void> {
+	if (!p || !p.pid) {
+		log(`no process to kill`);
+		return Promise.resolve();
+	}
+	return new Promise((resolve) => {
+		kill(p.pid, (err) => {
+			if (err) {
+				logError(`Error killing process ${p.pid}: ${err}`);
+			} else {
+				log(`killed process ${p.pid}`);
+			}
+			resolve();
+		});
+	});
+}
+
+// queryGOROOT returns `go env GOROOT`.
+function queryGOROOT(cwd: any, env: any): Promise<string> {
+	return new Promise<string>((resolve) => {
+		execFile(
+			getBinPathWithPreferredGopath('go', []),
+			['env', 'GOROOT'],
+			{ cwd, env },
+			(err, stdout, stderr) => {
+				if (err) {
+					return resolve('');
+				}
+				return resolve(stdout.trim());
+			});
+	});
+}
+
 DebugSession.run(GoDebugSession);
diff --git a/src/goBrowsePackage.ts b/src/goBrowsePackage.ts
index c2b041a..f27b107 100644
--- a/src/goBrowsePackage.ts
+++ b/src/goBrowsePackage.ts
@@ -9,7 +9,7 @@
 import path = require('path');
 import vscode = require('vscode');
 import { getAllPackages } from './goPackages';
-import { envPath } from './goPath';
+import { envPath, getCurrentGoRoot } from './goPath';
 import { getBinPath, getCurrentGoPath, getImportPath } from './util';
 
 export function browsePackages() {
@@ -40,7 +40,7 @@
 	const goRuntimePath = getBinPath('go');
 	if (!goRuntimePath) {
 		return vscode.window.showErrorMessage(
-			`Failed to run "go list" to fetch packages as the "go" binary cannot be found in either GOROOT(${process.env['GOROOT']}) or PATH(${envPath})`
+			`Failed to run "go list" to fetch packages as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath})`
 		);
 	}
 
diff --git a/src/goBuild.ts b/src/goBuild.ts
index 9b8129d..0aaee96 100644
--- a/src/goBuild.ts
+++ b/src/goBuild.ts
@@ -5,6 +5,7 @@
 
 import path = require('path');
 import vscode = require('vscode');
+import { toolExecutionEnvironment } from './goEnv';
 import { buildDiagnosticCollection } from './goMain';
 import { isModSupported } from './goModules';
 import { getNonVendorPackages } from './goPackages';
@@ -16,7 +17,6 @@
 	getGoConfig,
 	getModuleCache,
 	getTempFilePath,
-	getToolsEnvVars,
 	getWorkspaceFolderPath,
 	handleDiagnosticErrors,
 	ICheckResult,
@@ -100,14 +100,14 @@
 		return [];
 	}
 
-	const buildEnv = Object.assign({}, getToolsEnvVars());
+	const buildEnv = toolExecutionEnvironment();
 	const tmpPath = getTempFilePath('go-code-check');
 	const isTestFile = fileUri && fileUri.fsPath.endsWith('_test.go');
 	const buildFlags: string[] = isTestFile
 		? getTestFlags(goConfig)
 		: Array.isArray(goConfig['buildFlags'])
-		? [...goConfig['buildFlags']]
-		: [];
+			? [...goConfig['buildFlags']]
+			: [];
 	const buildArgs: string[] = isTestFile ? ['test', '-c'] : ['build'];
 
 	if (goConfig['installDependenciesWhenBuilding'] === true && !isMod) {
diff --git a/src/goCheck.ts b/src/goCheck.ts
index 2cc110a..597e018 100644
--- a/src/goCheck.ts
+++ b/src/goCheck.ts
@@ -8,7 +8,7 @@
 import path = require('path');
 import vscode = require('vscode');
 import { goBuild } from './goBuild';
-import { parseLanguageServerConfig } from './goLanguageServer';
+import { buildLanguageServerConfig } from './goLanguageServer';
 import { goLint } from './goLint';
 import { buildDiagnosticCollection, lintDiagnosticCollection, vetDiagnosticCollection } from './goMain';
 import { isModSupported } from './goModules';
@@ -59,7 +59,7 @@
 
 	// If a user has enabled diagnostics via a language server,
 	// then we disable running build or vet to avoid duplicate errors and warnings.
-	const lspConfig = parseLanguageServerConfig();
+	const lspConfig = buildLanguageServerConfig();
 	const disableBuildAndVet = lspConfig.enabled && lspConfig.features.diagnostics;
 
 	let testPromise: Thenable<boolean>;
diff --git a/src/goCover.ts b/src/goCover.ts
index 6185838..6ebda30 100644
--- a/src/goCover.ts
+++ b/src/goCover.ts
@@ -11,7 +11,7 @@
 import vscode = require('vscode');
 import { isModSupported } from './goModules';
 import { getTestFlags, goTest, showTestOutput, TestConfig } from './testUtils';
-import { getGoConfig, getTempFilePath } from './util';
+import { getGoConfig } from './util';
 
 let gutterSvgs: { [key: string]: string };
 let decorators: {
diff --git a/src/goDebugConfiguration.ts b/src/goDebugConfiguration.ts
index d09f2b1..a69087a 100644
--- a/src/goDebugConfiguration.ts
+++ b/src/goDebugConfiguration.ts
@@ -7,10 +7,11 @@
 
 import path = require('path');
 import vscode = require('vscode');
+import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool } from './goInstallTools';
 import { packagePathToGoModPathMap } from './goModules';
 import { getFromGlobalState, updateGlobalState } from './stateUtils';
-import { getBinPath, getCurrentGoPath, getGoConfig, getToolsEnvVars } from './util';
+import { getBinPath, getCurrentGoPath, getGoConfig } from './util';
 
 export class GoDebugConfigurationProvider implements vscode.DebugConfigurationProvider {
 	public provideDebugConfigurations(
@@ -61,7 +62,7 @@
 		}
 
 		const goConfig = getGoConfig(folder && folder.uri);
-		const goToolsEnvVars = getToolsEnvVars();
+		const goToolsEnvVars = toolExecutionEnvironment();
 		Object.keys(goToolsEnvVars).forEach((key) => {
 			if (!debugConfiguration['env'].hasOwnProperty(key)) {
 				debugConfiguration['env'][key] = goToolsEnvVars[key];
diff --git a/src/goDeclaration.ts b/src/goDeclaration.ts
index 798b014..7eb03ee 100644
--- a/src/goDeclaration.ts
+++ b/src/goDeclaration.ts
@@ -8,6 +8,7 @@
 import cp = require('child_process');
 import path = require('path');
 import vscode = require('vscode');
+import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool, promptForUpdatingTool } from './goInstallTools';
 import { getModFolderPath, promptToUpdateToolForModules } from './goModules';
 import {
@@ -16,7 +17,6 @@
 	getFileArchive,
 	getGoConfig,
 	getModuleCache,
-	getToolsEnvVars,
 	getWorkspaceFolderPath,
 	goKeywords,
 	isPositionInString,
@@ -132,7 +132,7 @@
 		return Promise.reject(missingToolMsg + godefTool);
 	}
 	const offset = byteOffsetAt(input.document, input.position);
-	const env = getToolsEnvVars();
+	const env = toolExecutionEnvironment();
 	let p: cp.ChildProcess;
 	if (token) {
 		token.onCancellationRequested(() => killTree(p.pid));
@@ -223,7 +223,7 @@
 		return Promise.reject(missingToolMsg + 'gogetdoc');
 	}
 	const offset = byteOffsetAt(input.document, input.position);
-	const env = getToolsEnvVars();
+	const env = toolExecutionEnvironment();
 	let p: cp.ChildProcess;
 	if (token) {
 		token.onCancellationRequested(() => killTree(p.pid));
@@ -297,7 +297,7 @@
 		return Promise.reject(missingToolMsg + 'guru');
 	}
 	const offset = byteOffsetAt(input.document, input.position);
-	const env = getToolsEnvVars();
+	const env = toolExecutionEnvironment();
 	let p: cp.ChildProcess;
 	if (token) {
 		token.onCancellationRequested(() => killTree(p.pid));
diff --git a/src/goDoctor.ts b/src/goDoctor.ts
index 9833596..b8a396b 100644
--- a/src/goDoctor.ts
+++ b/src/goDoctor.ts
@@ -8,8 +8,9 @@
 import cp = require('child_process');
 import { dirname, isAbsolute } from 'path';
 import vscode = require('vscode');
+import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool } from './goInstallTools';
-import { getBinPath, getToolsEnvVars } from './util';
+import { getBinPath } from './util';
 
 /**
  * Extracts function out of current selection and replaces the current selection with a call to the extracted function.
@@ -78,7 +79,7 @@
 				'-w',
 				'-pos',
 				`${selection.start.line + 1},${selection.start.character + 1}:${selection.end.line + 1},${
-					selection.end.character
+				selection.end.character
 				}`,
 				'-file',
 				fileName,
@@ -86,7 +87,7 @@
 				newName
 			],
 			{
-				env: getToolsEnvVars(),
+				env: toolExecutionEnvironment(),
 				cwd: dirname(fileName)
 			},
 			(err, stdout, stderr) => {
diff --git a/src/goEnv.ts b/src/goEnv.ts
new file mode 100644
index 0000000..b872319
--- /dev/null
+++ b/src/goEnv.ts
@@ -0,0 +1,69 @@
+/*---------------------------------------------------------
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
+ *--------------------------------------------------------*/
+
+'use strict';
+
+import path = require('path');
+import vscode = require('vscode');
+import { getCurrentGoPath, getGoConfig, getToolsGopath } from './util';
+
+// toolInstallationEnvironment returns the environment in which tools should
+// be installed. It always returns a new object.
+export function toolInstallationEnvironment(): NodeJS.Dict<string> {
+	const env = newEnvironment();
+
+	// If the go.toolsGopath is set, use its value as the GOPATH for `go` processes.
+	// Else use the Current Gopath
+	let toolsGopath = getToolsGopath();
+	if (toolsGopath) {
+		// User has explicitly chosen to use toolsGopath, so ignore GOBIN.
+		env['GOBIN'] = '';
+	} else {
+		toolsGopath = getCurrentGoPath();
+	}
+	if (!toolsGopath) {
+		const msg = 'Cannot install Go tools. Set either go.gopath or go.toolsGopath in settings.';
+		vscode.window.showInformationMessage(msg, 'Open User Settings', 'Open Workspace Settings').then((selected) => {
+			switch (selected) {
+				case 'Open User Settings':
+					vscode.commands.executeCommand('workbench.action.openGlobalSettings');
+					break;
+				case 'Open Workspace Settings':
+					vscode.commands.executeCommand('workbench.action.openWorkspaceSettings');
+					break;
+			}
+		});
+		return;
+	}
+	env['GOPATH'] = toolsGopath;
+
+	return env;
+}
+
+// toolExecutionEnvironment returns the environment in which tools should
+// be executed. It always returns a new object.
+export function toolExecutionEnvironment(): NodeJS.Dict<string> {
+	const env = newEnvironment();
+	const gopath = getCurrentGoPath();
+	if (gopath) {
+		env['GOPATH'] = gopath;
+	}
+	return env;
+}
+
+function newEnvironment(): NodeJS.Dict<string> {
+	const toolsEnvVars = getGoConfig()['toolsEnvVars'];
+	const env = Object.assign({}, process.env, toolsEnvVars);
+
+	// The http.proxy setting takes precedence over environment variables.
+	const httpProxy = vscode.workspace.getConfiguration('http', null).get('proxy');
+	if (httpProxy && typeof httpProxy === 'string') {
+		env['http_proxy'] = httpProxy;
+		env['HTTP_PROXY'] = httpProxy;
+		env['https_proxy'] = httpProxy;
+		env['HTTPS_PROXY'] = httpProxy;
+	}
+	return env;
+}
diff --git a/src/goEnvironmentStatus.ts b/src/goEnvironmentStatus.ts
new file mode 100644
index 0000000..ee6f9d7
--- /dev/null
+++ b/src/goEnvironmentStatus.ts
@@ -0,0 +1,102 @@
+/*---------------------------------------------------------
+ * Copyright 2020 The Go Authors. All rights reserved.
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
+ *--------------------------------------------------------*/
+
+'use strict';
+
+import vscode = require('vscode');
+
+import { updateGoVarsFromConfig } from './goInstallTools';
+import { getCurrentGoRoot } from './goPath';
+import { getGoVersion } from './util';
+
+// statusbar item for switching the Go environment
+let goEnvStatusbarItem: vscode.StatusBarItem;
+
+/**
+ * Initialize the status bar item with current Go binary
+ */
+export async function initGoStatusBar() {
+	goEnvStatusbarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 50);
+
+	// make goroot default to go.goroot and fallback on $PATH
+	const goroot = await getActiveGoRoot();
+	if (!goroot) {
+		// TODO: prompt user to install Go
+		vscode.window.showErrorMessage('No Go command could be found.');
+	}
+
+	// set Go version and command
+	const version = await getGoVersion();
+	goEnvStatusbarItem.text = formatGoVersion(version.format());
+	goEnvStatusbarItem.command = 'go.environment.choose';
+
+	showGoStatusBar();
+}
+
+/**
+ * disable the Go environment status bar item
+ */
+export function disposeGoStatusBar() {
+	if (!!goEnvStatusbarItem) {
+		goEnvStatusbarItem.dispose();
+	}
+}
+
+/**
+ * Show the Go Environment statusbar item on the statusbar
+ */
+export function showGoStatusBar() {
+	if (!!goEnvStatusbarItem) {
+		goEnvStatusbarItem.show();
+	}
+}
+
+/**
+ * Hide the Go Environment statusbar item from the statusbar
+ */
+export function hideGoStatusBar() {
+	if (!!goEnvStatusbarItem) {
+		goEnvStatusbarItem.hide();
+	}
+}
+
+/**
+ * Present a command palette menu to the user to select their go binary
+ * TODO: remove temporary alert and implement correct functionality
+ */
+export function chooseGoEnvironment() {
+	vscode.window.showInformationMessage(`Current GOROOT: ${getCurrentGoRoot()}`);
+}
+
+/**
+ * return reference to the statusbar item
+ */
+export function getGoEnvironmentStatusbarItem(): vscode.StatusBarItem {
+	return goEnvStatusbarItem;
+}
+
+export async function getActiveGoRoot(): Promise<string | undefined> {
+	// look for current current go binary
+	let goroot = getCurrentGoRoot();
+	if (!goroot) {
+		await updateGoVarsFromConfig();
+		goroot = getCurrentGoRoot();
+	}
+	return goroot || undefined;
+}
+
+export function formatGoVersion(version: string): string {
+	const versionWords = version.split(' ');
+	if (versionWords[0] === 'devel') {
+		// Go devel +hash
+		return `Go ${versionWords[0]} ${versionWords[4]}`;
+	} else if (versionWords.length > 0) {
+		// some other version format
+		return `Go ${version.substr(0, 8)}`;
+	} else {
+		// default semantic version format
+		return `Go ${versionWords[0]}`;
+	}
+}
diff --git a/src/goFillStruct.ts b/src/goFillStruct.ts
index beb12aa..4e9fba6 100644
--- a/src/goFillStruct.ts
+++ b/src/goFillStruct.ts
@@ -7,8 +7,9 @@
 
 import cp = require('child_process');
 import vscode = require('vscode');
+import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool } from './goInstallTools';
-import { byteOffsetAt, getBinPath, getFileArchive, getToolsEnvVars, makeMemoizedByteOffsetConverter } from './util';
+import { byteOffsetAt, getBinPath, getFileArchive, makeMemoizedByteOffsetConverter } from './util';
 
 // Interface for the output from fillstruct
 interface GoFillStructOutput {
@@ -59,7 +60,7 @@
 	const tabsCount = getTabsCount(editor);
 
 	return new Promise<void>((resolve, reject) => {
-		const p = cp.execFile(fillstruct, args, { env: getToolsEnvVars() }, (err, stdout, stderr) => {
+		const p = cp.execFile(fillstruct, args, { env: toolExecutionEnvironment() }, (err, stdout, stderr) => {
 			try {
 				if (err && (<any>err).code === 'ENOENT') {
 					promptForMissingTool('fillstruct');
diff --git a/src/goFormat.ts b/src/goFormat.ts
index 78a52a3..6321e66 100644
--- a/src/goFormat.ts
+++ b/src/goFormat.ts
@@ -8,8 +8,9 @@
 import cp = require('child_process');
 import path = require('path');
 import vscode = require('vscode');
+import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool, promptForUpdatingTool } from './goInstallTools';
-import { getBinPath, getGoConfig, getToolsEnvVars, killTree } from './util';
+import { getBinPath, getGoConfig, killTree } from './util';
 
 export class GoDocumentFormattingEditProvider implements vscode.DocumentFormattingEditProvider {
 	public provideDocumentFormattingEdits(
@@ -70,7 +71,7 @@
 				return reject();
 			}
 
-			const env = getToolsEnvVars();
+			const env = toolExecutionEnvironment();
 			const cwd = path.dirname(document.fileName);
 			let stdout = '';
 			let stderr = '';
diff --git a/src/goGenerateTests.ts b/src/goGenerateTests.ts
index 9521e7e..a9e71c9 100644
--- a/src/goGenerateTests.ts
+++ b/src/goGenerateTests.ts
@@ -9,10 +9,11 @@
 import path = require('path');
 import vscode = require('vscode');
 
+import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool } from './goInstallTools';
 import { GoDocumentSymbolProvider } from './goOutline';
 import { outputChannel } from './goStatus';
-import { getBinPath, getGoConfig, getToolsEnvVars } from './util';
+import { getBinPath, getGoConfig } from './util';
 
 const generatedWord = 'Generated ';
 
@@ -172,7 +173,7 @@
 			args = args.concat(['-all', conf.dir]);
 		}
 
-		cp.execFile(cmd, args, { env: getToolsEnvVars() }, (err, stdout, stderr) => {
+		cp.execFile(cmd, args, { env: toolExecutionEnvironment() }, (err, stdout, stderr) => {
 			outputChannel.appendLine('Generating Tests: ' + cmd + ' ' + args.join(' '));
 
 			try {
diff --git a/src/goGetPackage.ts b/src/goGetPackage.ts
index 80dda27..272a995 100644
--- a/src/goGetPackage.ts
+++ b/src/goGetPackage.ts
@@ -8,7 +8,7 @@
 import cp = require('child_process');
 import vscode = require('vscode');
 import { buildCode } from './goBuild';
-import { envPath } from './goPath';
+import { envPath, getCurrentGoRoot } from './goPath';
 import { outputChannel } from './goStatus';
 import { getBinPath, getCurrentGoPath, getImportPath } from './util';
 
@@ -26,7 +26,7 @@
 	const goRuntimePath = getBinPath('go');
 	if (!goRuntimePath) {
 		return vscode.window.showErrorMessage(
-			`Failed to run "go get" to get package as the "go" binary cannot be found in either GOROOT(${process.env['GOROOT']}) or PATH(${envPath})`
+			`Failed to run "go get" to get package as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath})`
 		);
 	}
 
diff --git a/src/goImpl.ts b/src/goImpl.ts
index dcaf4f0..3672315 100644
--- a/src/goImpl.ts
+++ b/src/goImpl.ts
@@ -8,8 +8,9 @@
 import cp = require('child_process');
 import { dirname } from 'path';
 import vscode = require('vscode');
+import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool } from './goInstallTools';
-import { getBinPath, getToolsEnvVars } from './util';
+import { getBinPath } from './util';
 
 // Supports only passing interface, see TODO in implCursor to finish
 const inputRegex = /^(\w+\ \*?\w+\ )?([\w./]+)$/;
@@ -49,7 +50,7 @@
 	const p = cp.execFile(
 		goimpl,
 		args,
-		{ env: getToolsEnvVars(), cwd: dirname(editor.document.fileName) },
+		{ env: toolExecutionEnvironment(), cwd: dirname(editor.document.fileName) },
 		(err, stdout, stderr) => {
 			if (err && (<any>err).code === 'ENOENT') {
 				promptForMissingTool('impl');
diff --git a/src/goImplementations.ts b/src/goImplementations.ts
index 2bc45d5..da39a51 100644
--- a/src/goImplementations.ts
+++ b/src/goImplementations.ts
@@ -8,16 +8,16 @@
 import cp = require('child_process');
 import path = require('path');
 import vscode = require('vscode');
+import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool } from './goInstallTools';
-import { envPath } from './goPath';
+import { envPath, getCurrentGoRoot } from './goPath';
 import {
 	byteOffsetAt,
 	canonicalizeGOPATHPrefix,
 	getBinPath,
 	getGoConfig,
-	getToolsEnvVars,
 	getWorkspaceFolderPath,
-	killTree
+	killTree,
 } from './util';
 
 interface GoListOutput {
@@ -57,7 +57,7 @@
 		const goRuntimePath = getBinPath('go');
 		if (!goRuntimePath) {
 			vscode.window.showErrorMessage(
-				`Failed to run "go list" to get the scope to find implementations as the "go" binary cannot be found in either GOROOT(${process.env['GOROOT']}) or PATH(${envPath})`
+				`Failed to run "go list" to get the scope to find implementations as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath})`
 			);
 			return;
 		}
@@ -66,7 +66,7 @@
 			if (token.isCancellationRequested) {
 				return resolve(null);
 			}
-			const env = getToolsEnvVars();
+			const env = toolExecutionEnvironment();
 			const listProcess = cp.execFile(
 				goRuntimePath,
 				['list', '-e', '-json'],
diff --git a/src/goImport.ts b/src/goImport.ts
index c8445c7..5a9df7e 100644
--- a/src/goImport.ts
+++ b/src/goImport.ts
@@ -7,11 +7,12 @@
 
 import cp = require('child_process');
 import vscode = require('vscode');
+import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool } from './goInstallTools';
 import { documentSymbols, GoOutlineImportsOptions } from './goOutline';
 import { getImportablePackages } from './goPackages';
-import { envPath } from './goPath';
-import { getBinPath, getImportPath, getToolsEnvVars, parseFilePrelude } from './util';
+import { envPath, getCurrentGoRoot } from './goPath';
+import { getBinPath, getImportPath, parseFilePrelude } from './util';
 
 const missingToolMsg = 'Missing tool: ';
 
@@ -179,11 +180,11 @@
 	const goRuntimePath = getBinPath('go');
 	if (!goRuntimePath) {
 		vscode.window.showErrorMessage(
-			`Failed to run "go list" to find the package as the "go" binary cannot be found in either GOROOT(${process.env['GOROOT']}) or PATH(${envPath})`
+			`Failed to run "go list" to find the package as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath})`
 		);
 		return;
 	}
-	const env = getToolsEnvVars();
+	const env = toolExecutionEnvironment();
 
 	cp.execFile(goRuntimePath, ['list', '-f', '{{.Dir}}', importPath], { env }, (err, stdout, stderr) => {
 		const dirs = (stdout || '').split('\n');
diff --git a/src/goInstall.ts b/src/goInstall.ts
index 32aaa9c..859da2a 100644
--- a/src/goInstall.ts
+++ b/src/goInstall.ts
@@ -6,10 +6,11 @@
 import cp = require('child_process');
 import path = require('path');
 import vscode = require('vscode');
+import { toolExecutionEnvironment } from './goEnv';
 import { isModSupported } from './goModules';
-import { envPath, getCurrentGoWorkspaceFromGOPATH } from './goPath';
+import { envPath, getCurrentGoRoot, getCurrentGoWorkspaceFromGOPATH } from './goPath';
 import { outputChannel } from './goStatus';
-import { getBinPath, getCurrentGoPath, getGoConfig, getModuleCache, getToolsEnvVars } from './util';
+import { getBinPath, getCurrentGoPath, getGoConfig, getModuleCache } from './util';
 
 export async function installCurrentPackage(): Promise<void> {
 	const editor = vscode.window.activeTextEditor;
@@ -27,12 +28,12 @@
 	const goRuntimePath = getBinPath('go');
 	if (!goRuntimePath) {
 		vscode.window.showErrorMessage(
-			`Failed to run "go install" to install the package as the "go" binary cannot be found in either GOROOT(${process.env['GOROOT']}) or PATH(${envPath})`
+			`Failed to run "go install" to install the package as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath})`
 		);
 		return;
 	}
 
-	const env = Object.assign({}, getToolsEnvVars());
+	const env = toolExecutionEnvironment();
 	const cwd = path.dirname(editor.document.uri.fsPath);
 	const isMod = await isModSupported(editor.document.uri);
 
diff --git a/src/goInstallTools.ts b/src/goInstallTools.ts
index 495913d..a72ad34 100644
--- a/src/goInstallTools.ts
+++ b/src/goInstallTools.ts
@@ -9,12 +9,14 @@
 import fs = require('fs');
 import path = require('path');
 import { SemVer } from 'semver';
+import util = require('util');
 import vscode = require('vscode');
+import { toolInstallationEnvironment } from './goEnv';
 import { getLanguageServerToolPath } from './goLanguageServer';
-import { envPath, getToolFromToolPath } from './goPath';
+import { restartLanguageServer } from './goMain';
+import { envPath, getCurrentGoRoot, getToolFromToolPath, setCurrentGoRoot } from './goPath';
 import { hideGoStatus, outputChannel, showGoStatus } from './goStatus';
 import {
-	containsString,
 	containsTool,
 	disableModulesForWildcard,
 	getConfiguredTools,
@@ -22,18 +24,16 @@
 	getImportPathWithVersion,
 	getTool,
 	hasModSuffix,
-	isGocode,
-	Tool
+	Tool,
+	ToolAtVersion,
 } from './goTools';
 import {
 	getBinPath,
-	getCurrentGoPath,
 	getGoConfig,
 	getGoVersion,
 	getTempFilePath,
-	getToolsGopath,
 	GoVersion,
-	resolvePath
+	rmdirRecursive,
 } from './util';
 
 // declinedUpdates tracks the tools that the user has declined to update.
@@ -54,7 +54,7 @@
 
 	// Update existing tools by finding all tools the user has already installed.
 	if (updateExistingToolsOnly) {
-		installTools(
+		await installTools(
 			allTools.filter((tool) => {
 				const toolPath = getBinPath(tool.name);
 				return toolPath && path.isAbsolute(toolPath);
@@ -65,37 +65,23 @@
 	}
 
 	// Otherwise, allow the user to select which tools to install or update.
-	vscode.window
-		.showQuickPick(
-			allTools.map((x) => {
-				const item: vscode.QuickPickItem = {
-					label: x.name,
-					description: x.description
-				};
-				return item;
-			}),
-			{
-				canPickMany: true,
-				placeHolder: 'Select the tools to install/update.'
-			}
-		)
-		.then((selectedTools) => {
-			if (!selectedTools) {
-				return;
-			}
-			installTools(
-				selectedTools.map((x) => getTool(x.label)),
-				goVersion
-			);
-		});
-}
-
-/**
- * ToolAtVersion is a Tool with version annotation.
- * Lack of version implies the latest version
- */
-export interface ToolAtVersion extends Tool {
-	version?: SemVer;
+	const selected = await vscode.window.showQuickPick(
+		allTools.map((x) => {
+			const item: vscode.QuickPickItem = {
+				label: x.name,
+				description: x.description
+			};
+			return item;
+		}),
+		{
+			canPickMany: true,
+			placeHolder: 'Select the tools to install/update.'
+		}
+	);
+	if (!selected) {
+		return;
+	}
+	await installTools(selected.map((x) => getTool(x.label)), goVersion);
 }
 
 /**
@@ -105,68 +91,27 @@
  *                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)
  */
-export function installTools(missing: ToolAtVersion[], goVersion: GoVersion): Promise<void> {
-	const goRuntimePath = getBinPath('go');
-	if (!goRuntimePath) {
-		vscode.window.showErrorMessage(
-			`Failed to run "go get" to install the packages as the "go" binary cannot be found in either GOROOT(${process.env['GOROOT']}) or PATH(${envPath})`
-		);
-		return;
-	}
+export async function installTools(missing: ToolAtVersion[], goVersion: GoVersion): Promise<void> {
 	if (!missing) {
 		return;
 	}
 
-	// http.proxy setting takes precedence over environment variables
-	const httpProxy = vscode.workspace.getConfiguration('http', null).get('proxy');
-	let envForTools = Object.assign({}, process.env);
-	if (httpProxy) {
-		envForTools = Object.assign({}, process.env, {
-			http_proxy: httpProxy,
-			HTTP_PROXY: httpProxy,
-			https_proxy: httpProxy,
-			HTTPS_PROXY: httpProxy
-		});
-	}
-
 	outputChannel.show();
 	outputChannel.clear();
 
-	// If the go.toolsGopath is set, use its value as the GOPATH for the "go get" child process.
-	// Else use the Current Gopath
-	let toolsGopath = getToolsGopath();
-	if (toolsGopath) {
-		// User has explicitly chosen to use toolsGopath, so ignore GOBIN
-		envForTools['GOBIN'] = '';
-		outputChannel.appendLine(`Using the value ${toolsGopath} from the go.toolsGopath setting.`);
-	} else {
-		toolsGopath = getCurrentGoPath();
-		outputChannel.appendLine(`go.toolsGopath setting is not set. Using GOPATH ${toolsGopath}`);
+	const envForTools = toolInstallationEnvironment();
+	const toolsGopath = envForTools['GOPATH'];
+	let envMsg = `Tools environment: GOPATH=${toolsGopath}`;
+	if (envForTools['GOBIN']) {
+		envMsg += `, GOBIN=${envForTools['GOBIN']}`;
 	}
-	if (toolsGopath) {
-		const paths = toolsGopath.split(path.delimiter);
-		toolsGopath = paths[0];
-		envForTools['GOPATH'] = toolsGopath;
-	} else {
-		const msg = 'Cannot install Go tools. Set either go.gopath or go.toolsGopath in settings.';
-		vscode.window.showInformationMessage(msg, 'Open User Settings', 'Open Workspace Settings').then((selected) => {
-			switch (selected) {
-				case 'Open User Settings':
-					vscode.commands.executeCommand('workbench.action.openGlobalSettings');
-					break;
-				case 'Open Workspace Settings':
-					vscode.commands.executeCommand('workbench.action.openWorkspaceSettings');
-					break;
-			}
-		});
-		return;
-	}
+	outputChannel.appendLine(envMsg);
 
 	let installingMsg = `Installing ${missing.length} ${missing.length > 1 ? 'tools' : 'tool'} at `;
 	if (envForTools['GOBIN']) {
 		installingMsg += `the configured GOBIN: ${envForTools['GOBIN']}`;
 	} else {
-		installingMsg += toolsGopath + path.sep + 'bin';
+		installingMsg += `${toolsGopath}${path.sep}bin`;
 	}
 
 	// If the user is on Go >= 1.11, tools should be installed with modules enabled.
@@ -190,129 +135,118 @@
 
 	outputChannel.appendLine(''); // Blank line for spacing.
 
-	// Install tools in a temporary directory, to avoid altering go.mod files.
-	const toolsTmpDir = fs.mkdtempSync(getTempFilePath('go-tools-'));
+	const toInstall: Promise<{ tool: Tool, reason: string }>[] = [];
+	for (const tool of missing) {
+		// Disable modules for tools which are installed with the "..." wildcard.
+		const modulesOffForTool = modulesOff || disableModulesForWildcard(tool, goVersion);
 
-	return missing
-		.reduce((res: Promise<string[]>, tool: ToolAtVersion) => {
-			return res.then(
-				(sofar) =>
-					new Promise<string[]>((resolve, reject) => {
-						// Disable modules for tools which are installed with the "..." wildcard.
-						// TODO: ... will be supported in Go 1.13, so enable these tools to use modules then.
-						const modulesOffForTool = modulesOff || disableModulesForWildcard(tool, goVersion);
-						let tmpGoModFile: string;
-						if (modulesOffForTool) {
-							envForTools['GO111MODULE'] = 'off';
-						} else {
-							envForTools['GO111MODULE'] = 'on';
-							// Write a temporary go.mod file to avoid version conflicts.
-							tmpGoModFile = path.join(toolsTmpDir, 'go.mod');
-							fs.writeFileSync(tmpGoModFile, 'module tools');
-						}
+		const reason = installTool(tool, goVersion, envForTools, !modulesOffForTool);
+		toInstall.push(Promise.resolve({ tool, reason: await reason }));
+	}
 
-						const opts = {
-							env: envForTools,
-							cwd: toolsTmpDir
-						};
-						const callback = (err: Error, stdout: string, stderr: string) => {
-							// Make sure to delete the temporary go.mod file, if it exists.
-							if (tmpGoModFile && fs.existsSync(tmpGoModFile)) {
-								fs.unlinkSync(tmpGoModFile);
-							}
-							const importPath = getImportPathWithVersion(tool, tool.version, goVersion);
-							if (err) {
-								outputChannel.appendLine('Installing ' + importPath + ' FAILED');
-								const failureReason = tool.name + ';;' + err + stdout.toString() + stderr.toString();
-								resolve([...sofar, failureReason]);
-							} else {
-								outputChannel.appendLine('Installing ' + importPath + ' SUCCEEDED');
-								resolve([...sofar, null]);
-							}
-						};
+	const results = await Promise.all(toInstall);
 
-						let closeToolPromise = Promise.resolve(true);
-						const toolBinPath = getBinPath(tool.name);
-						if (path.isAbsolute(toolBinPath) && isGocode(tool)) {
-							closeToolPromise = new Promise<boolean>((innerResolve) => {
-								cp.execFile(toolBinPath, ['close'], {}, (err, stdout, stderr) => {
-									if (stderr && stderr.indexOf(`rpc: can't find service Server.`) > -1) {
-										outputChannel.appendLine(
-											'Installing gocode aborted as existing process cannot be closed. Please kill the running process for gocode and try again.'
-										);
-										return innerResolve(false);
-									}
-									innerResolve(true);
-								});
-							});
-						}
-
-						closeToolPromise.then((success) => {
-							if (!success) {
-								resolve([...sofar, null]);
-								return;
-							}
-							const args = ['get', '-v'];
-							// Only get tools at master if we are not using modules.
-							if (modulesOffForTool) {
-								args.push('-u');
-							}
-							// Tools with a "mod" suffix should not be installed,
-							// instead we run "go build -o" to rename them.
-							if (hasModSuffix(tool)) {
-								args.push('-d');
-							}
-							let importPath: string;
-							if (modulesOffForTool) {
-								importPath = getImportPath(tool, goVersion);
-							} else {
-								importPath = getImportPathWithVersion(tool, tool.version, goVersion);
-							}
-							args.push(importPath);
-							cp.execFile(goRuntimePath, args, opts, (err, stdout, stderr) => {
-								if (stderr.indexOf('unexpected directory layout:') > -1) {
-									outputChannel.appendLine(
-										`Installing ${importPath} failed with error "unexpected directory layout". Retrying...`
-									);
-									cp.execFile(goRuntimePath, args, opts, callback);
-								} else if (!err && hasModSuffix(tool)) {
-									const outputFile = path.join(
-										toolsGopath,
-										'bin',
-										process.platform === 'win32' ? `${tool.name}.exe` : tool.name
-									);
-									cp.execFile(
-										goRuntimePath,
-										['build', '-o', outputFile, getImportPath(tool, goVersion)],
-										opts,
-										callback
-									);
-								} else {
-									callback(err, stdout, stderr);
-								}
-							});
-						});
-					})
-			);
-		}, Promise.resolve([]))
-		.then((res) => {
-			outputChannel.appendLine(''); // Blank line for spacing
-			const failures = res.filter((x) => x != null);
-			if (failures.length === 0) {
-				if (containsString(missing, 'gopls')) {
-					outputChannel.appendLine('Reload VS Code window to use the Go language server');
-				}
-				outputChannel.appendLine('All tools successfully installed. You are ready to Go :).');
-				return;
+	const failures: { tool: ToolAtVersion, reason: string }[] = [];
+	for (const result of results) {
+		if (result.reason === '') {
+			// Restart the language server if a new binary has been installed.
+			if (result.tool.name === 'gopls') {
+				restartLanguageServer();
 			}
+		} else {
+			failures.push(result);
+		}
+	}
 
-			outputChannel.appendLine(failures.length + ' tools failed to install.\n');
-			failures.forEach((failure) => {
-				const reason = failure.split(';;');
-				outputChannel.appendLine(reason[0] + ':');
-				outputChannel.appendLine(reason[1]);
-			});
-		});
+	// Report detailed information about any failures.
+	outputChannel.appendLine(''); // blank line for spacing
+	if (failures.length === 0) {
+		outputChannel.appendLine('All tools successfully installed. You are ready to Go :).');
+	} else {
+		outputChannel.appendLine(failures.length + ' tools failed to install.\n');
+		for (const failure of failures) {
+			outputChannel.appendLine(`${failure.tool.name}: ${failure.reason} `);
+		}
+	}
+}
+
+export async function installTool(
+	tool: ToolAtVersion, goVersion: GoVersion,
+	envForTools: NodeJS.Dict<string>, modulesOn: boolean): Promise<string> {
+	// Some tools may have to be closed before we reinstall them.
+	if (tool.close) {
+		const reason = await tool.close();
+		if (reason) {
+			return reason;
+		}
+	}
+	// Install tools in a temporary directory, to avoid altering go.mod files.
+	const mkdtemp = util.promisify(fs.mkdtemp);
+	const toolsTmpDir = await mkdtemp(getTempFilePath('go-tools-'));
+	const env = Object.assign({}, envForTools);
+	let tmpGoModFile: string;
+	if (modulesOn) {
+		env['GO111MODULE'] = 'on';
+
+		// Write a temporary go.mod file to avoid version conflicts.
+		tmpGoModFile = path.join(toolsTmpDir, 'go.mod');
+		const writeFile = util.promisify(fs.writeFile);
+		await writeFile(tmpGoModFile, 'module tools');
+	} else {
+		envForTools['GO111MODULE'] = 'off';
+	}
+
+	// Build the arguments list for the tool installation.
+	const args = ['get', '-v'];
+	// Only get tools at master if we are not using modules.
+	if (!modulesOn) {
+		args.push('-u');
+	}
+	// Tools with a "mod" suffix should not be installed,
+	// instead we run "go build -o" to rename them.
+	if (hasModSuffix(tool)) {
+		args.push('-d');
+	}
+	let importPath: string;
+	if (!modulesOn) {
+		importPath = getImportPath(tool, goVersion);
+	} else {
+		importPath = getImportPathWithVersion(tool, tool.version, goVersion);
+	}
+	args.push(importPath);
+
+	let output: string;
+	let result: string = '';
+	try {
+		const opts = {
+			env,
+			cwd: toolsTmpDir,
+		};
+		const execFile = util.promisify(cp.execFile);
+		const { stdout, stderr } = await execFile(goVersion.binaryPath, args, opts);
+		output = `${stdout} ${stderr}`;
+
+		// TODO(rstambler): Figure out why this happens and maybe delete it.
+		if (stderr.indexOf('unexpected directory layout:') > -1) {
+			await execFile(goVersion.binaryPath, args, opts);
+		} else if (hasModSuffix(tool)) {
+			const gopath = env['GOPATH'];
+			if (!gopath) {
+				return `GOPATH not configured in environment`;
+			}
+			const outputFile = path.join(gopath, 'bin', process.platform === 'win32' ? `${tool.name}.exe` : tool.name);
+			await execFile(goVersion.binaryPath, ['build', '-o', outputFile, importPath], opts);
+		}
+		outputChannel.appendLine(`Installing ${importPath} SUCCEEDED`);
+	} catch (e) {
+		outputChannel.appendLine(`Installing ${importPath} FAILED`);
+		result = `failed to install ${tool}: ${e} ${output} `;
+	}
+
+	// Delete the temporary installation directory.
+	rmdirRecursive(toolsTmpDir);
+
+	return result;
 }
 
 export async function promptForMissingTool(toolName: string) {
@@ -324,23 +258,18 @@
 	}
 
 	const goVersion = await getGoVersion();
-	// Show error messages for outdated tools.
-	if (goVersion.lt('1.9')) {
-		let outdatedErrorMsg;
-		switch (tool.name) {
-			case 'golint':
-				outdatedErrorMsg =
-					'golint no longer supports go1.8 or below, update your settings to use golangci-lint as go.lintTool and install golangci-lint';
-				break;
-			case 'gotests':
-				outdatedErrorMsg =
-					'Generate unit tests feature is not supported as gotests tool needs go1.9 or higher.';
-				break;
-		}
-		if (outdatedErrorMsg) {
-			vscode.window.showInformationMessage(outdatedErrorMsg);
-			return;
-		}
+	if (!goVersion) {
+		return;
+	}
+
+	// Show error messages for outdated tools or outdated Go versions.
+	if (tool.minimumGoVersion && goVersion.lt(tool.minimumGoVersion.format())) {
+		vscode.window.showInformationMessage(`You are using go${goVersion.format()}, but ${tool.name} requires at least go${tool.minimumGoVersion.format()}.`);
+		return;
+	}
+	if (tool.maximumGoVersion && goVersion.gt(tool.maximumGoVersion.format())) {
+		vscode.window.showInformationMessage(`You are using go${goVersion.format()}, but ${tool.name} only supports go${tool.maximumGoVersion.format()} and below.`);
+		return;
 	}
 
 	const installOptions = ['Install'];
@@ -353,25 +282,22 @@
 		// Offer the option to install all tools.
 		installOptions.push('Install All');
 	}
-	const msg = `The "${tool.name}" command is not available. Run "go get -v ${getImportPath(
-		tool,
-		goVersion
-	)}" to install.`;
-	vscode.window.showInformationMessage(msg, ...installOptions).then((selected) => {
-		switch (selected) {
-			case 'Install':
-				installTools([tool], goVersion);
-				break;
-			case 'Install All':
-				installTools(missing, goVersion);
-				hideGoStatus();
-				break;
-			default:
-				// The user has declined to install this tool.
-				declinedInstalls.push(tool);
-				break;
-		}
-	});
+	const msg = `The "${tool.name}" command is not available.
+Run "go get -v ${getImportPath(tool, goVersion)}" to install.`;
+	const selected = await vscode.window.showInformationMessage(msg, ...installOptions);
+	switch (selected) {
+		case 'Install':
+			await installTools([tool], goVersion);
+			break;
+		case 'Install All':
+			await installTools(missing, goVersion);
+			hideGoStatus();
+			break;
+		default:
+			// The user has declined to install this tool.
+			declinedInstalls.push(tool);
+			break;
+	}
 }
 
 export async function promptForUpdatingTool(toolName: string, newVersion?: SemVer) {
@@ -389,33 +315,27 @@
 		choices.push('Release Notes');
 	}
 	if (newVersion) {
-		updateMsg = `New version of ${tool.name} (${newVersion}) is available. Please update for an improved experience.`;
+		updateMsg = `A new version of ${tool.name} (v${newVersion}) is available. Please update for an improved experience.`;
 	}
-	vscode.window.showInformationMessage(updateMsg, ...choices).then((selected) => {
-		switch (selected) {
-			case 'Update':
-				installTools([toolVersion], goVersion);
-				break;
-			case 'Release Notes':
-				vscode.commands.executeCommand(
-					'vscode.open',
-					vscode.Uri.parse('https://github.com/golang/go/issues/33030#issuecomment-510151934')
-				);
-				break;
-			default:
-				declinedUpdates.push(tool);
-				break;
-		}
-	});
+	const selected = await vscode.window.showInformationMessage(updateMsg, ...choices);
+	switch (selected) {
+		case 'Update':
+			await installTools([toolVersion], goVersion);
+			break;
+		case 'Release Notes':
+			vscode.commands.executeCommand(
+				'vscode.open',
+				vscode.Uri.parse('https://github.com/golang/go/issues/33030#issuecomment-510151934')
+			);
+			break;
+		default:
+			declinedUpdates.push(tool);
+			break;
+	}
 }
 
-export function updateGoPathGoRootFromConfig(): Promise<void> {
-	const goroot = getGoConfig()['goroot'];
-	if (goroot) {
-		process.env['GOROOT'] = resolvePath(goroot);
-	}
-
-	if (process.env['GOPATH'] && process.env['GOROOT'] && process.env['GOPROXY']) {
+export function updateGoVarsFromConfig(): Promise<void> {
+	if (getCurrentGoRoot() && process.env['GOPATH'] && process.env['GOPROXY'] && process.env['GOBIN']) {
 		return Promise.resolve();
 	}
 
@@ -423,7 +343,7 @@
 	const goRuntimePath = getBinPath('go');
 	if (!goRuntimePath) {
 		vscode.window.showErrorMessage(
-			`Failed to run "go env" to find GOPATH as the "go" binary cannot be found in either GOROOT(${process.env['GOROOT']}) or PATH(${envPath})`
+			`Failed to run "go env" to find GOPATH as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath})`
 		);
 		return;
 	}
@@ -447,7 +367,7 @@
 	}
 
 	return new Promise<void>((resolve, reject) => {
-		cp.execFile(goRuntimePath, ['env', 'GOPATH', 'GOROOT', 'GOPROXY'], (err, stdout, stderr) => {
+		cp.execFile(goRuntimePath, ['env', 'GOPATH', 'GOROOT', 'GOPROXY', 'GOBIN'], (err, stdout, stderr) => {
 			if (err) {
 				return reject();
 			}
@@ -455,12 +375,15 @@
 			if (!process.env['GOPATH'] && envOutput[0].trim()) {
 				process.env['GOPATH'] = envOutput[0].trim();
 			}
-			if (!process.env['GOROOT'] && envOutput[1] && envOutput[1].trim()) {
-				process.env['GOROOT'] = envOutput[1].trim();
+			if (!getCurrentGoRoot() && envOutput[1] && envOutput[1].trim()) {
+				setCurrentGoRoot(envOutput[1].trim());
 			}
 			if (!process.env['GOPROXY'] && envOutput[2] && envOutput[2].trim()) {
 				process.env['GOPROXY'] = envOutput[2].trim();
 			}
+			if (!process.env['GOBIN'] && envOutput[3] && envOutput[3].trim()) {
+				process.env['GOBIN'] = envOutput[3].trim();
+			}
 			return resolve();
 		});
 	});
@@ -482,9 +405,9 @@
 		vscode.commands.registerCommand('go.promptforinstall', () => {
 			const installItem = {
 				title: 'Install',
-				command() {
+				async command() {
 					hideGoStatus();
-					installTools(missing, goVersion);
+					await installTools(missing, goVersion);
 				}
 			};
 			const showItem = {
@@ -517,23 +440,18 @@
 			'The language server from Sourcegraph is no longer under active development and it does not support Go modules as well. Please install and use the language server from Google or disable the use of language servers altogether.';
 		const disableLabel = 'Disable language server';
 		const installLabel = 'Install';
-		vscode.window.showInformationMessage(promptMsg, installLabel, disableLabel).then((selected) => {
-			if (selected === installLabel) {
-				installTools([getTool('gopls')], goVersion).then(() => {
-					vscode.window.showInformationMessage(
-						'Reload VS Code window to enable the use of Go language server'
-					);
-				});
-			} else if (selected === disableLabel) {
-				const goConfig = getGoConfig();
-				const inspectLanguageServerSetting = goConfig.inspect('useLanguageServer');
-				if (inspectLanguageServerSetting.globalValue === true) {
-					goConfig.update('useLanguageServer', false, vscode.ConfigurationTarget.Global);
-				} else if (inspectLanguageServerSetting.workspaceFolderValue === true) {
-					goConfig.update('useLanguageServer', false, vscode.ConfigurationTarget.WorkspaceFolder);
-				}
+		const selected = await vscode.window.showInformationMessage(promptMsg, installLabel, disableLabel);
+		if (selected === installLabel) {
+			await installTools([getTool('gopls')], goVersion);
+		} else if (selected === disableLabel) {
+			const goConfig = getGoConfig();
+			const inspectLanguageServerSetting = goConfig.inspect('useLanguageServer');
+			if (inspectLanguageServerSetting.globalValue === true) {
+				goConfig.update('useLanguageServer', false, vscode.ConfigurationTarget.Global);
+			} else if (inspectLanguageServerSetting.workspaceFolderValue === true) {
+				goConfig.update('useLanguageServer', false, vscode.ConfigurationTarget.WorkspaceFolder);
 			}
-		});
+		}
 	}
 }
 
diff --git a/src/goLanguageServer.ts b/src/goLanguageServer.ts
index 8a5ecdc..e2afaca 100644
--- a/src/goLanguageServer.ts
+++ b/src/goLanguageServer.ts
@@ -1,5 +1,6 @@
 /*---------------------------------------------------------
  * Copyright (C) Microsoft Corporation. All rights reserved.
+ * Modification copyright 2020 The Go Authors. All rights reserved.
  * Licensed under the MIT License. See LICENSE in the project root for license information.
  *--------------------------------------------------------*/
 
@@ -7,28 +8,44 @@
 
 import cp = require('child_process');
 import deepEqual = require('deep-equal');
+import fs = require('fs');
 import moment = require('moment');
 import path = require('path');
 import semver = require('semver');
 import util = require('util');
 import vscode = require('vscode');
 import {
-	Command,
-	HandleDiagnosticsSignature,
-	LanguageClient,
-	ProvideCompletionItemsSignature,
-	ProvideDocumentLinksSignature,
-	RevealOutputChannelOn
+	CloseAction, CompletionItemKind, ErrorAction, HandleDiagnosticsSignature, InitializeError,
+	LanguageClient, Message, ProvideCompletionItemsSignature, ProvideDocumentLinksSignature,
+	RevealOutputChannelOn,
 } from 'vscode-languageclient';
 import WebRequest = require('web-request');
+import { extensionId } from './const';
+import { GoDefinitionProvider } from './goDeclaration';
+import { toolExecutionEnvironment } from './goEnv';
+import { GoHoverProvider } from './goExtraInfo';
+import { GoDocumentFormattingEditProvider } from './goFormat';
+import { GoImplementationProvider } from './goImplementations';
 import { promptForMissingTool, promptForUpdatingTool } from './goInstallTools';
+import { parseLiveFile } from './goLiveErrors';
+import { restartLanguageServer } from './goMain';
+import { GO_MODE } from './goMode';
+import { GoDocumentSymbolProvider } from './goOutline';
 import { getToolFromToolPath } from './goPath';
+import { GoReferenceProvider } from './goReferences';
+import { GoRenameProvider } from './goRename';
+import { GoSignatureHelpProvider } from './goSignature';
+import { GoCompletionItemProvider } from './goSuggest';
+import { GoWorkspaceSymbolProvider } from './goSymbol';
 import { getTool, Tool } from './goTools';
-import { getBinPath, getCurrentGoPath, getGoConfig, getToolsEnvVars } from './util';
+import { GoTypeDefinitionProvider } from './goTypeDefinition';
+import { getFromGlobalState, updateGlobalState } from './stateUtils';
+import { getBinPath, getCurrentGoPath, getGoConfig } from './util';
 
 interface LanguageServerConfig {
 	serverName: string;
 	path: string;
+	modtime: Date;
 	enabled: boolean;
 	flags: string[];
 	env: any;
@@ -46,39 +63,53 @@
 let languageServerDisposable: vscode.Disposable;
 let latestConfig: LanguageServerConfig;
 let serverOutputChannel: vscode.OutputChannel;
+let serverTraceChannel: vscode.OutputChannel;
+let crashCount = 0;
 
-// startLanguageServer starts the language server (if enabled), returning
-// true on success.
-export async function registerLanguageFeatures(ctx: vscode.ExtensionContext): Promise<boolean> {
-	// Subscribe to notifications for changes to the configuration of the language server.
-	ctx.subscriptions.push(vscode.workspace.onDidChangeConfiguration((e) => watchLanguageServerConfiguration(e)));
+// defaultLanguageProviders is the list of providers currently registered.
+let defaultLanguageProviders: vscode.Disposable[] = [];
 
-	const config = parseLanguageServerConfig();
-	if (!config.enabled) {
-		return false;
-	}
+// restartCommand is the command used by the user to restart the language
+// server.
+let restartCommand: vscode.Disposable;
 
-	// Support a command to restart the language server, if it's enabled.
-	ctx.subscriptions.push(vscode.commands.registerCommand('go.languageserver.restart', () => {
-		return startLanguageServer(ctx, parseLanguageServerConfig());
-	}));
+// When enabled, users may be prompted to fill out the gopls survey.
+const goplsSurveyOn: boolean = false;
+
+// 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();
 
 	// If the language server is gopls, we can check if the user needs to
-	// update their gopls version.
-	if (config.serverName === 'gopls') {
-		const tool = getTool(config.serverName);
-		if (!tool) {
-			return false;
-		}
-		const versionToUpdate = await shouldUpdateLanguageServer(tool, config.path, config.checkForUpdates);
-		if (versionToUpdate) {
-			promptForUpdatingTool(tool.name, versionToUpdate);
+	// update their gopls version. We do this only once per VS Code
+	// activation to avoid inundating the user.
+	if (activation && cfg.enabled && cfg.serverName === 'gopls') {
+		const tool = getTool(cfg.serverName);
+		if (tool) {
+			const versionToUpdate = await shouldUpdateLanguageServer(tool, cfg.path, cfg.checkForUpdates);
+			if (versionToUpdate) {
+				promptForUpdatingTool(tool.name, versionToUpdate);
+			} else if (goplsSurveyOn) {
+				// Only prompt users to fill out the gopls survey if we are not
+				// also prompting them to update (both would be too much).
+				const timeout = 1000 * 60 * 60; // 1 hour
+				setTimeout(async () => {
+					const surveyCfg = await maybePromptForGoplsSurvey();
+					flushSurveyConfig(surveyCfg);
+				}, timeout);
+			}
 		}
 	}
 
-	// This function handles the case when the server isn't started yet,
-	// so we can call it to start the language server.
-	return startLanguageServer(ctx, config);
+	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);
+	}
 }
 
 async function startLanguageServer(ctx: vscode.ExtensionContext, config: LanguageServerConfig): Promise<boolean> {
@@ -97,29 +128,53 @@
 	// Check if we should recreate the language client. This may be necessary
 	// if the user has changed settings in their config.
 	if (!deepEqual(latestConfig, config)) {
-		// Track the latest config used to start the language server.
+		// Track the latest config used to start the language server,
+		// and rebuild the language client.
 		latestConfig = config;
-
-		// If the user has not enabled or installed the language server, return.
-		if (!config.enabled || !config.path) {
-			return false;
-		}
-		buildLanguageClient(config);
+		languageClient = await buildLanguageClient(config);
+		crashCount = 0;
 	}
 
+	// If the user has not enabled the language server, return early.
+	if (!config.enabled) {
+		return false;
+	}
+
+	// Set up the command to allow the user to manually restart the
+	// language server.
+	if (!restartCommand) {
+		restartCommand = vscode.commands.registerCommand('go.languageserver.restart', async () => {
+			// TODO(rstambler): Enable this behavior when gopls reaches v1.0.
+			if (false) {
+				await suggestGoplsIssueReport(`Looks like you're about to manually restart the language server.`);
+			}
+			restartLanguageServer();
+		});
+		ctx.subscriptions.push(restartCommand);
+	}
+
+	// Before starting the language server, make sure to deregister any
+	// currently registered language providers.
+	disposeDefaultProviders();
+
 	languageServerDisposable = languageClient.start();
 	ctx.subscriptions.push(languageServerDisposable);
-
 	return true;
 }
 
-function buildLanguageClient(config: LanguageServerConfig) {
+async function buildLanguageClient(config: LanguageServerConfig): Promise<LanguageClient> {
 	// Reuse the same output channel for each instance of the server.
-	if (!serverOutputChannel) {
-		serverOutputChannel = vscode.window.createOutputChannel(config.serverName);
+	if (config.enabled) {
+		if (!serverOutputChannel) {
+			serverOutputChannel = vscode.window.createOutputChannel(config.serverName + ' (server)');
+		}
+		if (!serverTraceChannel) {
+			serverTraceChannel = vscode.window.createOutputChannel(config.serverName);
+		}
 	}
-	languageClient = new LanguageClient(
-		config.serverName,
+	const c = new LanguageClient(
+		'go',  // id
+		config.serverName,  // name
 		{
 			command: config.path,
 			args: ['-mode=stdio', ...config.flags],
@@ -135,7 +190,38 @@
 				protocol2Code: (uri: string) => vscode.Uri.parse(uri)
 			},
 			outputChannel: serverOutputChannel,
+			traceOutputChannel: serverTraceChannel,
 			revealOutputChannelOn: RevealOutputChannelOn.Never,
+			initializationFailedHandler: (error: WebRequest.ResponseError<InitializeError>): boolean => {
+				vscode.window.showErrorMessage(
+					`The language server is not able to serve any features. Initialization failed: ${error}. `
+				);
+				serverOutputChannel.show();
+				suggestGoplsIssueReport(`The gopls server failed to initialize.`);
+				return false;
+			},
+			errorHandler: {
+				error: (error: Error, message: Message, count: number): ErrorAction => {
+					vscode.window.showErrorMessage(
+						`Error communicating with the language server: ${error}: ${message}.`
+					);
+					// Allow 5 crashes before shutdown.
+					if (count < 5) {
+						return ErrorAction.Continue;
+					}
+					return ErrorAction.Shutdown;
+				},
+				closed: (): CloseAction => {
+					// Allow 5 crashes before shutdown.
+					crashCount++;
+					if (crashCount < 5) {
+						return CloseAction.Restart;
+					}
+					serverOutputChannel.show();
+					suggestGoplsIssueReport(`The connection to gopls has been closed. The gopls server may have crashed.`);
+					return CloseAction.DoNotRestart;
+				},
+			},
 			middleware: {
 				handleDiagnostics: (
 					uri: vscode.Uri,
@@ -157,20 +243,41 @@
 					}
 					return next(document, token);
 				},
-				provideCompletionItem: (
+				provideCompletionItem: async (
 					document: vscode.TextDocument,
 					position: vscode.Position,
 					context: vscode.CompletionContext,
 					token: vscode.CancellationToken,
 					next: ProvideCompletionItemsSignature
 				) => {
+					const list = await next(document, position, context, token);
+					if (!list) {
+						return list;
+					}
+					const items = Array.isArray(list) ? list : list.items;
+
+					// Give all the candidates the same filterText to trick VSCode
+					// into not reordering our candidates. All the candidates will
+					// appear to be equally good matches, so VSCode's fuzzy
+					// matching/ranking just maintains the natural "sortText"
+					// ordering. We can only do this in tandem with
+					// "incompleteResults" since otherwise client side filtering is
+					// important.
+					if (!Array.isArray(list) && list.isIncomplete && list.items.length > 1) {
+						let hardcodedFilterText = items[0].filterText;
+						if (!hardcodedFilterText) {
+							hardcodedFilterText = '';
+						}
+						for (const item of items) {
+							item.filterText = hardcodedFilterText;
+						}
+					}
 					// TODO(hyangah): when v1.42+ api is available, we can simplify
 					// language-specific configuration lookup using the new
 					// ConfigurationScope.
 					//    const paramHintsEnabled = vscode.workspace.getConfiguration(
 					//          'editor.parameterHints',
 					//          { languageId: 'go', uri: document.uri });
-
 					const editorParamHintsEnabled = vscode.workspace.getConfiguration(
 						'editor.parameterHints',
 						document.uri
@@ -178,94 +285,90 @@
 					const goParamHintsEnabled = vscode.workspace.getConfiguration('[go]', document.uri)[
 						'editor.parameterHints.enabled'
 					];
-
 					let paramHintsEnabled: boolean = false;
 					if (typeof goParamHintsEnabled === 'undefined') {
 						paramHintsEnabled = editorParamHintsEnabled;
 					} else {
 						paramHintsEnabled = goParamHintsEnabled;
 					}
-					let cmd: Command;
+					// If the user has parameterHints (signature help) enabled,
+					// trigger it for function or method completion items.
 					if (paramHintsEnabled) {
-						cmd = { title: 'triggerParameterHints', command: 'editor.action.triggerParameterHints' };
-					}
-
-					function configureCommands(
-						r: vscode.CompletionItem[] | vscode.CompletionList | null | undefined
-					): vscode.CompletionItem[] | vscode.CompletionList | null | undefined {
-						if (r) {
-							(Array.isArray(r) ? r : r.items).forEach((i: vscode.CompletionItem) => {
-								i.command = cmd;
-							});
+						for (const item of items) {
+							if (item.kind === CompletionItemKind.Method || item.kind === CompletionItemKind.Function) {
+								item.command = { title: 'triggerParameterHints', command: 'editor.action.triggerParameterHints' };
+							}
 						}
-						return r;
 					}
-					const ret = next(document, position, context, token);
-
-					const isThenable = <T>(obj: vscode.ProviderResult<T>): obj is Thenable<T> =>
-						obj && (<any>obj)['then'];
-					if (isThenable<vscode.CompletionItem[] | vscode.CompletionList | null | undefined>(ret)) {
-						return ret.then(configureCommands);
-					}
-					return configureCommands(ret);
+					return list;
 				}
 			}
 		}
 	);
-	languageClient.onReady().then(() => {
-		const capabilities = languageClient.initializeResult && languageClient.initializeResult.capabilities;
-		if (!capabilities) {
-			return vscode.window.showErrorMessage(
-				'The language server is not able to serve any features at the moment.'
-			);
-		}
-	});
+	return c;
 }
 
-function watchLanguageServerConfiguration(e: vscode.ConfigurationChangeEvent) {
+// registerUsualProviders registers the language feature providers if the language server is not enabled.
+function registerDefaultProviders(ctx: vscode.ExtensionContext) {
+	const completionProvider = new GoCompletionItemProvider(ctx.globalState);
+	defaultLanguageProviders.push(completionProvider);
+	defaultLanguageProviders.push(vscode.languages.registerCompletionItemProvider(GO_MODE, completionProvider, '.', '"'));
+	defaultLanguageProviders.push(vscode.languages.registerHoverProvider(GO_MODE, new GoHoverProvider()));
+	defaultLanguageProviders.push(vscode.languages.registerDefinitionProvider(GO_MODE, new GoDefinitionProvider()));
+	defaultLanguageProviders.push(vscode.languages.registerReferenceProvider(GO_MODE, new GoReferenceProvider()));
+	defaultLanguageProviders.push(
+		vscode.languages.registerDocumentSymbolProvider(GO_MODE, new GoDocumentSymbolProvider())
+	);
+	defaultLanguageProviders.push(vscode.languages.registerWorkspaceSymbolProvider(new GoWorkspaceSymbolProvider()));
+	defaultLanguageProviders.push(
+		vscode.languages.registerSignatureHelpProvider(GO_MODE, new GoSignatureHelpProvider(), '(', ',')
+	);
+	defaultLanguageProviders.push(
+		vscode.languages.registerImplementationProvider(GO_MODE, new GoImplementationProvider())
+	);
+	defaultLanguageProviders.push(
+		vscode.languages.registerDocumentFormattingEditProvider(GO_MODE, new GoDocumentFormattingEditProvider())
+	);
+	defaultLanguageProviders.push(
+		vscode.languages.registerTypeDefinitionProvider(GO_MODE, new GoTypeDefinitionProvider())
+	);
+	defaultLanguageProviders.push(vscode.languages.registerRenameProvider(GO_MODE, new GoRenameProvider()));
+	defaultLanguageProviders.push(vscode.workspace.onDidChangeTextDocument(parseLiveFile, null, ctx.subscriptions));
+
+	for (const provider of defaultLanguageProviders) {
+		ctx.subscriptions.push(provider);
+	}
+}
+
+function disposeDefaultProviders() {
+	for (const disposable of defaultLanguageProviders) {
+		disposable.dispose();
+	}
+	defaultLanguageProviders = [];
+}
+
+export function watchLanguageServerConfiguration(e: vscode.ConfigurationChangeEvent) {
 	if (!e.affectsConfiguration('go')) {
 		return;
 	}
 
-	const config = parseLanguageServerConfig();
-	let reloadMessage: string;
-
-	// If the user has disabled or enabled the language server.
-	if (e.affectsConfiguration('go.useLanguageServer')) {
-		if (config.enabled) {
-			reloadMessage = 'Reload VS Code window to enable the use of language server';
-		} else {
-			reloadMessage = 'Reload VS Code window to disable the use of language server';
-		}
-	}
-
 	if (
+		e.affectsConfiguration('go.useLanguageServer') ||
 		e.affectsConfiguration('go.languageServerFlags') ||
-		e.affectsConfiguration('go.languageServerExperimentalFeatures')
+		e.affectsConfiguration('go.languageServerExperimentalFeatures') ||
+		e.affectsConfiguration('go.alternateTools')
 	) {
-		reloadMessage = 'Reload VS Code window for the changes in language server settings to take effect';
-	}
-
-	// If there was a change in the configuration of the language server,
-	// then ask the user to reload VS Code.
-	if (reloadMessage) {
-		vscode.window.showInformationMessage(reloadMessage, 'Reload').then((selected) => {
-			if (selected === 'Reload') {
-				vscode.commands.executeCommand('workbench.action.reloadWindow');
-			}
-		});
+		restartLanguageServer();
 	}
 }
 
-export function parseLanguageServerConfig(): LanguageServerConfig {
+export function buildLanguageServerConfig(): LanguageServerConfig {
 	const goConfig = getGoConfig();
-	const toolsEnv = getToolsEnvVars();
-	const languageServerPath = getLanguageServerToolPath();
-	const languageServerName = getToolFromToolPath(languageServerPath);
-	return {
-		serverName: languageServerName,
-		path: languageServerPath,
-		enabled: goConfig['useLanguageServer'],
+	const cfg: LanguageServerConfig = {
+		serverName: '',
+		path: '',
+		modtime: null,
+		enabled: goConfig['useLanguageServer'] === true,
 		flags: goConfig['languageServerFlags'] || [],
 		features: {
 			// TODO: We should have configs that match these names.
@@ -273,24 +376,48 @@
 			diagnostics: goConfig['languageServerExperimentalFeatures']['diagnostics'],
 			documentLink: goConfig['languageServerExperimentalFeatures']['documentLink']
 		},
-		env: toolsEnv,
+		env: toolExecutionEnvironment(),
 		checkForUpdates: goConfig['useGoProxyToCheckForToolUpdates']
 	};
+	// Don't look for the path if the server is not enabled.
+	if (!cfg.enabled) {
+		return cfg;
+	}
+	const languageServerPath = getLanguageServerToolPath();
+	if (!languageServerPath) {
+		// Assume the getLanguageServerToolPath will show the relevant
+		// errors to the user. Disable the language server.
+		cfg.enabled = false;
+		return cfg;
+	}
+	cfg.path = languageServerPath;
+	cfg.serverName = getToolFromToolPath(cfg.path);
+
+	// Get the mtime of the language server binary so that we always pick up
+	// the right version.
+	const stats = fs.statSync(languageServerPath);
+	if (!stats) {
+		vscode.window.showErrorMessage(`Unable to stat path to language server binary: ${languageServerPath}.
+Please try reinstalling it.`);
+		// Disable the language server.
+		cfg.enabled = false;
+		return cfg;
+	}
+	cfg.modtime = stats.mtime;
+
+	return cfg;
 }
 
 /**
  *
- * If the user has enabled the language server, return the absolute path to the
- * correct binary. If the required tool is not available, prompt the user to
- * install it. Only gopls is officially supported.
+ * Return the absolute path to the correct binary. If the required tool is not available,
+ * prompt the user to install it. Only gopls is officially supported.
  */
 export function getLanguageServerToolPath(): string {
-	// If language server is not enabled, return
 	const goConfig = getGoConfig();
 	if (!goConfig['useLanguageServer']) {
 		return;
 	}
-
 	// Check that all workspace folders are configured with the same GOPATH.
 	if (!allFoldersHaveSameGopath()) {
 		vscode.window.showInformationMessage(
@@ -335,10 +462,7 @@
 	return vscode.workspace.workspaceFolders.find((x) => tempGopath !== getCurrentGoPath(x.uri)) ? false : true;
 }
 
-const acceptGoplsPrerelease = false;
-const defaultLatestVersion = semver.coerce('0.4.0');
-const defaultLatestVersionTime = moment('2020-04-08', 'YYYY-MM-DD');
-async function shouldUpdateLanguageServer(
+export async function shouldUpdateLanguageServer(
 	tool: Tool,
 	languageServerToolPath: string,
 	makeProxyCall: boolean
@@ -349,52 +473,53 @@
 	}
 
 	// First, run the "gopls version" command and parse its results.
-	const usersVersion = await goplsVersion(languageServerToolPath);
+	const usersVersion = await getLocalGoplsVersion(languageServerToolPath);
 
 	// We might have a developer version. Don't make the user update.
 	if (usersVersion === '(devel)') {
 		return null;
 	}
 
-	// Get the latest gopls version.
-	let latestVersion = makeProxyCall ? await latestGopls(tool) : defaultLatestVersion;
+	// Get the latest gopls version. If it is for nightly, using the prereleased version is ok.
+	let latestVersion = makeProxyCall ? await getLatestGoplsVersion(tool) : tool.latestVersion;
 
 	// 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) {
-		latestVersion = defaultLatestVersion;
+		latestVersion = tool.latestVersion;
 	}
 
 	// If "gopls" is so old that it doesn't have the "gopls version" command,
-	// or its version doesn't match our expectations, usersVersion will be empty.
+	// or its version doesn't match our expectations, usersVersion will be empty or invalid.
 	// Suggest the latestVersion.
-	if (!usersVersion) {
+	if (!usersVersion || !semver.valid(usersVersion)) {
 		return latestVersion;
 	}
 
 	// The user may have downloaded golang.org/x/tools/gopls@master,
 	// which means that they have a pseudoversion.
-	const usersTime = parsePseudoversionTimestamp(usersVersion);
+	const usersTime = parseTimestampFromPseudoversion(usersVersion);
 	// If the user has a pseudoversion, get the timestamp for the latest gopls version and compare.
 	if (usersTime) {
-		let latestTime = makeProxyCall ? await goplsVersionTimestamp(tool, latestVersion) : defaultLatestVersionTime;
+		let latestTime = makeProxyCall ? await getTimestampForVersion(tool, latestVersion) : tool.latestVersionTimestamp;
 		if (!latestTime) {
-			latestTime = defaultLatestVersionTime;
+			latestTime = tool.latestVersionTimestamp;
 		}
 		return usersTime.isBefore(latestTime) ? latestVersion : null;
 	}
 
 	// If the user's version does not contain a timestamp,
 	// default to a semver comparison of the two versions.
-	return semver.lt(usersVersion, latestVersion) ? latestVersion : null;
+	const usersVersionSemver = semver.coerce(usersVersion, { includePrerelease: true, loose: true });
+	return semver.lt(usersVersionSemver, latestVersion) ? latestVersion : null;
 }
 
-// Copied from src/cmd/go/internal/modfetch.
+// 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)?$/;
 
-// parsePseudoVersion reports whether v is a pseudo-version.
-// The timestamp is the center component, and it has the format "YYYYMMDDHHmmss".
-
-function parsePseudoversionTimestamp(version: string): moment.Moment {
+// parseTimestampFromPseudoversion returns the timestamp for the given
+// pseudoversion. The timestamp is the center component, and it has the
+// format "YYYYMMDDHHmmss".
+function parseTimestampFromPseudoversion(version: string): moment.Moment {
 	const split = version.split('-');
 	if (split.length < 2) {
 		return null;
@@ -430,16 +555,18 @@
 	return moment.utc(timestamp, 'YYYYMMDDHHmmss');
 }
 
-async function goplsVersionTimestamp(tool: Tool, version: semver.SemVer): Promise<moment.Moment> {
+export const getTimestampForVersion = async (tool: Tool, version: semver.SemVer) => {
 	const data = await goProxyRequest(tool, `v${version.format()}.info`);
 	if (!data) {
 		return null;
 	}
 	const time = moment(data['Time']);
 	return time;
-}
+};
 
-async function latestGopls(tool: Tool): Promise<semver.SemVer> {
+const acceptGoplsPrerelease = (extensionId === 'golang.go-nightly');
+
+export const getLatestGoplsVersion = async (tool: Tool) => {
 	// If the user has a version of gopls that we understand,
 	// ask the proxy for the latest version, and if the user's version is older,
 	// prompt them to update.
@@ -468,14 +595,16 @@
 	}
 	// The first version in the sorted list without a prerelease tag.
 	return versions.find((version) => !version.prerelease || !version.prerelease.length);
-}
+};
 
-async function goplsVersion(goplsPath: string): Promise<string> {
-	const env = getToolsEnvVars();
+// getLocalGoplsVersion returns the version of gopls that is currently
+// installed on the user's machine. This is determined by running the
+// `gopls version` command.
+export const getLocalGoplsVersion = async (goplsPath: string) => {
 	const execFile = util.promisify(cp.execFile);
 	let output: any;
 	try {
-		const { stdout } = await execFile(goplsPath, ['version'], { env });
+		const { stdout } = await execFile(goplsPath, ['version'], { env: toolExecutionEnvironment() });
 		output = stdout;
 	} catch (e) {
 		// The "gopls version" command is not supported, or something else went wrong.
@@ -506,7 +635,7 @@
 	//
 	//    golang.org/x/tools/gopls@v0.1.3 h1:CB5ECiPysqZrwxcyRjN+exyZpY0gODTZvNiqQi3lpeo=
 	//
-	// TODO: We should use a regex to match this, but for now, we split on the @ symbol.
+	// TODO(stamblerre): We should use a regex to match this, but for now, we split on the @ symbol.
 	// The reasoning for this is that gopls still has a golang.org/x/tools/cmd/gopls binary,
 	// so users may have a developer version that looks like "golang.org/x/tools@(devel)".
 	const moduleVersion = lines[1].trim().split(' ')[0];
@@ -524,12 +653,18 @@
 	//    v0.1.3
 	//
 	return split[1];
-}
+};
 
 async function goProxyRequest(tool: Tool, endpoint: string): Promise<any> {
-	const proxies = goProxy();
+	// Get the user's value of GOPROXY.
+	// If it is not set, we cannot make the request.
+	const output: string = process.env['GOPROXY'];
+	if (!output || !output.trim()) {
+		return null;
+	}
 	// Try each URL set in the user's GOPROXY environment variable.
 	// If none is set, don't make the request.
+	const proxies = output.trim().split(',|');
 	for (const proxy of proxies) {
 		if (proxy === 'direct') {
 			continue;
@@ -548,11 +683,184 @@
 	return null;
 }
 
-function goProxy(): string[] {
-	const output: string = process.env['GOPROXY'];
-	if (!output || !output.trim()) {
-		return [];
+// SurveyConfig is the set of global properties used to determine if
+// we should prompt a user to take the gopls survey.
+export interface SurveyConfig {
+	// prompt is true if the user can be prompted to take the survey.
+	// It is false if the user has responded "Never" to the prompt.
+	prompt?: boolean;
+
+	// promptThisMonth is true if we have used a random number generator
+	// to determine if the user should be prompted this month.
+	// It is undefined if we have not yet made the determination.
+	promptThisMonth?: boolean;
+
+	// promptThisMonthTimestamp is the date on which we determined if the user
+	// should be prompted this month.
+	promptThisMonthTimestamp?: Date;
+
+	// lastDatePrompted is the most recent date that the user has been prompted.
+	lastDatePrompted?: Date;
+
+	// lastDateAccepted is the most recent date that the user responded "Yes"
+	// to the survey prompt. The user need not have completed the survey.
+	lastDateAccepted?: Date;
+}
+
+async function maybePromptForGoplsSurvey(): Promise<SurveyConfig> {
+	const now = new Date();
+	const cfg = getSurveyConfig();
+	const prompt = shouldPromptForGoplsSurvey(now, cfg);
+	if (!prompt) {
+		return cfg;
 	}
-	const split = output.trim().split(',');
-	return split;
+	const selected = await vscode.window.showInformationMessage(`Looks like you're using gopls, the Go language server.
+Would you be willing to fill out a quick survey about your experience with gopls?`, 'Yes', 'Not now', 'Never');
+
+	// Update the time last asked.
+	cfg.lastDatePrompted = now;
+
+	switch (selected) {
+		case 'Yes':
+			cfg.lastDateAccepted = now;
+			cfg.prompt = true;
+
+			// Open the link to the survey.
+			vscode.env.openExternal(vscode.Uri.parse('https://www.whattimeisitrightnow.com/'));
+			break;
+		case 'Not now':
+			cfg.prompt = true;
+
+			vscode.window.showInformationMessage(`No problem! We'll ask you again another time.`);
+			break;
+		case 'Never':
+			cfg.prompt = false;
+
+			vscode.window.showInformationMessage(`No problem! We won't ask again.`);
+			break;
+	}
+	return cfg;
+}
+
+export function shouldPromptForGoplsSurvey(now: Date, cfg: SurveyConfig): boolean {
+	// If the prompt value is not set, assume we haven't prompted the user
+	// and should do so.
+	if (cfg.prompt === undefined) {
+		cfg.prompt = true;
+	}
+	if (!cfg.prompt) {
+		return false;
+	}
+
+	// Check if the user has taken the survey in the last year.
+	// Don't prompt them if they have been.
+	if (cfg.lastDateAccepted) {
+		if (daysBetween(now, cfg.lastDateAccepted) < 365) {
+			return false;
+		}
+	}
+
+	// Check if the user has been prompted for the survey in the last 90 days.
+	// Don't prompt them if they have been.
+	if (cfg.lastDatePrompted) {
+		if (daysBetween(now, cfg.lastDatePrompted) < 90) {
+			return false;
+		}
+	}
+
+	// Check if the extension has been activated this month.
+	if (cfg.promptThisMonthTimestamp) {
+		// The extension has been activated this month, so we should have already
+		// decided if the user should be prompted.
+		if (daysBetween(now, cfg.promptThisMonthTimestamp) < 30) {
+			return cfg.promptThisMonth;
+		}
+	}
+	// This is the first activation this month (or ever), so decide if we
+	// should prompt the user. This is done by generating a random number
+	// and % 20 to get a 5% chance.
+	const r = Math.floor(Math.random() * 20);
+	cfg.promptThisMonth = (r % 20 === 0);
+	cfg.promptThisMonthTimestamp = now;
+
+	return cfg.promptThisMonth;
+}
+
+export const goplsSurveyConfig = 'goplsSurveyConfig';
+
+function getSurveyConfig(): SurveyConfig {
+	const saved = getFromGlobalState(goplsSurveyConfig);
+	if (saved === undefined) {
+		return {};
+	}
+	try {
+		const cfg = JSON.parse(saved, (key: string, value: any) => {
+			// Make sure values that should be dates are correctly converted.
+			if (key.includes('Date')) {
+				return new Date(value);
+			}
+			return value;
+		});
+		return cfg;
+	} catch (err) {
+		console.log(`Error parsing JSON from ${saved}: ${err}`);
+		return {};
+	}
+}
+
+function flushSurveyConfig(cfg: SurveyConfig) {
+	updateGlobalState(goplsSurveyConfig, JSON.stringify(cfg));
+}
+
+// suggestGoplsIssueReport prompts users to file an issue with gopls.
+async function suggestGoplsIssueReport(msg: string) {
+	if (latestConfig.serverName !== 'gopls') {
+		return;
+	}
+	const promptForIssueOnGoplsRestartKey = `promptForIssueOnGoplsRestart`;
+	let saved: any;
+	try {
+		saved = JSON.parse(getFromGlobalState(promptForIssueOnGoplsRestartKey, false));
+	} catch (err) {
+		console.log(`Failed to parse as JSON ${getFromGlobalState(promptForIssueOnGoplsRestartKey, true)}: ${err}`);
+		return;
+	}
+	// If the user has already seen this prompt, they may have opted-out for
+	// the future. Only prompt again if it's been more than a year since.
+	if (saved) {
+		const dateSaved = new Date(saved['date']);
+		const prompt = <boolean>saved['prompt'];
+		if (!prompt && daysBetween(new Date(), dateSaved) <= 365) {
+			return;
+		}
+	}
+	const selected = await vscode.window.showInformationMessage(`${msg} Would you like to report a gopls issue ? `, 'Yes', 'Next time', 'Never');
+	switch (selected) {
+		case 'Yes':
+			// Run the `gopls bug` command directly for now. When
+			// https://github.com/golang/go/issues/38942 is
+			// resolved, we'll be able to do this through the
+			// language client.
+
+			// Wait for the command to finish before restarting the
+			// server, but don't bother handling errors.
+			const execFile = util.promisify(cp.execFile);
+			await execFile(latestConfig.path, ['bug'], { env: toolExecutionEnvironment() });
+			break;
+		case 'Next time':
+			break;
+		case 'Never':
+			updateGlobalState(promptForIssueOnGoplsRestartKey, JSON.stringify({
+				prompt: false,
+				date: new Date(),
+			}));
+			break;
+	}
+}
+
+// daysBetween returns the number of days between a and b,
+// assuming that a occurs after b.
+function daysBetween(a: Date, b: Date) {
+	const ms = a.getTime() - b.getTime();
+	return ms / (1000 * 60 * 60 * 24);
 }
diff --git a/src/goLint.ts b/src/goLint.ts
index eca0101..2eb80ea 100644
--- a/src/goLint.ts
+++ b/src/goLint.ts
@@ -5,11 +5,11 @@
 
 import path = require('path');
 import vscode = require('vscode');
+import { toolExecutionEnvironment } from './goEnv';
 import { lintDiagnosticCollection } from './goMain';
 import { diagnosticsStatusBarItem, outputChannel } from './goStatus';
 import {
 	getGoConfig,
-	getToolsEnvVars,
 	getToolsGopath,
 	getWorkspaceFolderPath,
 	handleDiagnosticErrors,
@@ -83,7 +83,7 @@
 
 	const lintTool = goConfig['lintTool'] || 'golint';
 	const lintFlags: string[] = goConfig['lintFlags'] || [];
-	const lintEnv = Object.assign({}, getToolsEnvVars());
+	const lintEnv = toolExecutionEnvironment();
 	const args: string[] = [];
 
 	lintFlags.forEach((flag) => {
diff --git a/src/goLiveErrors.ts b/src/goLiveErrors.ts
index 8c18dd4..e6b6cb8 100644
--- a/src/goLiveErrors.ts
+++ b/src/goLiveErrors.ts
@@ -8,10 +8,11 @@
 import cp = require('child_process');
 import path = require('path');
 import vscode = require('vscode');
+import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool } from './goInstallTools';
 import { buildDiagnosticCollection } from './goMain';
 import { isModSupported } from './goModules';
-import { getBinPath, getGoConfig, getToolsEnvVars } from './util';
+import { getBinPath, getGoConfig } from './util';
 
 // Interface for settings configuration for adding and removing tags
 interface GoLiveErrorsConfig {
@@ -77,7 +78,7 @@
 	const fileContents = e.document.getText();
 	const fileName = e.document.fileName;
 	const args = ['-e', '-a', '-lf=' + fileName, path.dirname(fileName)];
-	const env = getToolsEnvVars();
+	const env = toolExecutionEnvironment();
 	const p = cp.execFile(gotypeLive, args, { env }, (err, stdout, stderr) => {
 		if (err && (<any>err).code === 'ENOENT') {
 			promptForMissingTool('gotype-live');
diff --git a/src/goMain.ts b/src/goMain.ts
index 3d9b78a..ac4fc8e 100644
--- a/src/goMain.ts
+++ b/src/goMain.ts
@@ -1,11 +1,11 @@
 /*---------------------------------------------------------
  * Copyright (C) Microsoft Corporation. All rights reserved.
+ * Modification copyright 2020 The Go Authors. All rights reserved.
  * Licensed under the MIT License. See LICENSE in the project root for license information.
  *--------------------------------------------------------*/
 
 'use strict';
 
-import fs = require('fs');
 import * as path from 'path';
 import vscode = require('vscode');
 import { browsePackages } from './goBrowsePackage';
@@ -13,60 +13,38 @@
 import { check, notifyIfGeneratedFile, removeTestStatus } from './goCheck';
 import { GoCodeActionProvider } from './goCodeAction';
 import {
-	applyCodeCoverage,
-	applyCodeCoverageToAllEditors,
-	initCoverageDecorators,
-	removeCodeCoverageOnFileSave,
-	toggleCoverageCurrentPackage,
-	trackCodeCoverageRemovalOnFileChange,
-	updateCodeCoverageDecorators
+	applyCodeCoverage, applyCodeCoverageToAllEditors, initCoverageDecorators, removeCodeCoverageOnFileSave,
+	toggleCoverageCurrentPackage, trackCodeCoverageRemovalOnFileChange, updateCodeCoverageDecorators
 } from './goCover';
 import { GoDebugConfigurationProvider } from './goDebugConfiguration';
-import { GoDefinitionProvider } from './goDeclaration';
 import { extractFunction, extractVariable } from './goDoctor';
-import { GoHoverProvider } from './goExtraInfo';
+import { toolExecutionEnvironment } from './goEnv';
+import { chooseGoEnvironment, initGoStatusBar } from './goEnvironmentStatus';
 import { runFillStruct } from './goFillStruct';
-import { GoDocumentFormattingEditProvider } from './goFormat';
 import * as goGenerateTests from './goGenerateTests';
 import { goGetPackage } from './goGetPackage';
 import { implCursor } from './goImpl';
-import { GoImplementationProvider } from './goImplementations';
 import { addImport, addImportToWorkspace } from './goImport';
 import { installCurrentPackage } from './goInstall';
 import {
-	installAllTools,
-	installTools,
-	offerToInstallTools,
-	promptForMissingTool,
-	updateGoPathGoRootFromConfig
+	installAllTools, installTools, offerToInstallTools, promptForMissingTool,
+	updateGoVarsFromConfig
 } from './goInstallTools';
-import { registerLanguageFeatures } from './goLanguageServer';
+import { startLanguageServerWithFallback, watchLanguageServerConfiguration } from './goLanguageServer';
 import { lintCode } from './goLint';
-import { parseLiveFile } from './goLiveErrors';
 import { GO_MODE } from './goMode';
 import { addTags, removeTags } from './goModifytags';
 import { GO111MODULE, isModSupported } from './goModules';
-import { GoDocumentSymbolProvider } from './goOutline';
-import { clearCacheForTools, fileExists } from './goPath';
+import { clearCacheForTools, fileExists, getCurrentGoRoot, setCurrentGoRoot } from './goPath';
 import { playgroundCommand } from './goPlayground';
-import { GoReferenceProvider } from './goReferences';
 import { GoReferencesCodeLensProvider } from './goReferencesCodelens';
-import { GoRenameProvider } from './goRename';
 import { GoRunTestCodeLensProvider } from './goRunTestCodelens';
-import { GoSignatureHelpProvider } from './goSignature';
 import { outputChannel, showHideStatus } from './goStatus';
-import { GoCompletionItemProvider } from './goSuggest';
-import { GoWorkspaceSymbolProvider } from './goSymbol';
-import { testAtCursor, testCurrentFile, testCurrentPackage, testPrevious, testWorkspace } from './goTest';
+import { subTestAtCursor, testAtCursor, testCurrentFile, testCurrentPackage, testPrevious, testWorkspace } from './goTest';
 import { getConfiguredTools } from './goTools';
-import { GoTypeDefinitionProvider } from './goTypeDefinition';
 import { vetCode } from './goVet';
 import {
-	getFromGlobalState,
-	getFromWorkspaceState,
-	setGlobalState,
-	setWorkspaceState,
-	updateGlobalState,
+	getFromGlobalState, getFromWorkspaceState, setGlobalState, setWorkspaceState, updateGlobalState,
 	updateWorkspaceState
 } from './stateUtils';
 import { cancelRunningTests, showTestOutput } from './testUtils';
@@ -77,22 +55,30 @@
 	getExtensionCommands,
 	getGoConfig,
 	getGoVersion,
-	getToolsEnvVars,
 	getToolsGopath,
 	getWorkspaceFolderPath,
 	handleDiagnosticErrors,
-	isGoPathSet
+	isGoPathSet,
 } from './util';
 
 export let buildDiagnosticCollection: vscode.DiagnosticCollection;
 export let lintDiagnosticCollection: vscode.DiagnosticCollection;
 export let vetDiagnosticCollection: vscode.DiagnosticCollection;
 
+// restartLanguageServer wraps all of the logic needed to restart the
+// language server. It can be used to enable, disable, or otherwise change
+// the configuration of the server.
+export let restartLanguageServer = () => { return; };
+
 export function activate(ctx: vscode.ExtensionContext): void {
 	setGlobalState(ctx.globalState);
 	setWorkspaceState(ctx.workspaceState);
+	const configGOROOT = getGoConfig()['goroot'];
+	if (!!configGOROOT) {
+		setCurrentGoRoot(configGOROOT);
+	}
 
-	updateGoPathGoRootFromConfig().then(async () => {
+	updateGoVarsFromConfig().then(async () => {
 		const updateToolsCmdText = 'Update tools';
 		interface GoInfo {
 			goroot: string;
@@ -104,7 +90,7 @@
 			toolsGoInfo[toolsGopath] = { goroot: null, version: null };
 		}
 		const prevGoroot = toolsGoInfo[toolsGopath].goroot;
-		const currentGoroot: string = process.env['GOROOT'] && process.env['GOROOT'].toLowerCase();
+		const currentGoroot: string = getCurrentGoRoot().toLowerCase();
 		if (prevGoroot && prevGoroot.toLowerCase() !== currentGoroot) {
 			vscode.window
 				.showInformationMessage(
@@ -126,7 +112,7 @@
 					if (prevVersion) {
 						vscode.window
 							.showInformationMessage(
-								'Your Go version is different than before, few Go tools may need re-compiling',
+								'Your Go version is different than before, a few Go tools may need re-compiling',
 								updateToolsCmdText
 							)
 							.then((selected) => {
@@ -144,12 +130,21 @@
 
 		offerToInstallTools();
 
-		// This handles all of the configurations and registrations for the language server.
-		// It also registers the necessary language feature providers that the language server may not support.
-		const ok = await registerLanguageFeatures(ctx);
-		if (!ok) {
-			registerUsualProviders(ctx);
-		}
+		// Subscribe to notifications for changes to the configuration
+		// of the language server, even if it's not currently in use.
+		ctx.subscriptions.push(vscode.workspace.onDidChangeConfiguration(
+			(e) => watchLanguageServerConfiguration(e)
+		));
+
+		// Set the function that is used to restart the language server.
+		// This is necessary, even if the language server is not currently
+		// in use.
+		restartLanguageServer = async () => {
+			startLanguageServerWithFallback(ctx, false);
+		};
+
+		// Start the language server, or fallback to the default language providers.
+		startLanguageServerWithFallback(ctx, true);
 
 		if (
 			vscode.window.activeTextEditor &&
@@ -166,15 +161,18 @@
 	initCoverageDecorators(ctx);
 
 	ctx.subscriptions.push(
-		vscode.commands.registerCommand('go.open.modulewiki', async () => {
+		vscode.commands.registerCommand('go.open.modulesdoc', async () => {
 			vscode.commands.executeCommand(
 				'vscode.open',
-				vscode.Uri.parse('https://github.com/microsoft/vscode-go/wiki/Go-modules-support-in-Visual-Studio-Code')
+				vscode.Uri.parse('https://github.com/golang/vscode-go/blob/master/docs/modules.md')
 			);
 		})
 	);
 	showHideStatus(vscode.window.activeTextEditor);
 
+	// show the go environment status bar item
+	initGoStatusBar();
+
 	const testCodeLensProvider = new GoRunTestCodeLensProvider();
 	const referencesCodeLensProvider = new GoReferencesCodeLensProvider();
 
@@ -230,7 +228,7 @@
 			outputChannel.appendLine('GOBIN: ' + process.env['GOBIN']);
 			outputChannel.appendLine('toolsGopath: ' + getToolsGopath());
 			outputChannel.appendLine('gopath: ' + getCurrentGoPath());
-			outputChannel.appendLine('GOROOT: ' + process.env['GOROOT']);
+			outputChannel.appendLine('GOROOT: ' + getCurrentGoRoot());
 			outputChannel.appendLine('PATH: ' + process.env['PATH']);
 			outputChannel.appendLine('');
 
@@ -293,6 +291,13 @@
 	);
 
 	ctx.subscriptions.push(
+		vscode.commands.registerCommand('go.subtest.cursor', (args) => {
+			const goConfig = getGoConfig();
+			subTestAtCursor(goConfig, args);
+		})
+	);
+
+	ctx.subscriptions.push(
 		vscode.commands.registerCommand('go.debug.cursor', (args) => {
 			const goConfig = getGoConfig();
 			testAtCursor(goConfig, 'debug', args);
@@ -385,7 +390,7 @@
 		vscode.commands.registerCommand('go.tools.install', async (args) => {
 			if (Array.isArray(args) && args.length) {
 				const goVersion = await getGoVersion();
-				installTools(args, goVersion);
+				await installTools(args, goVersion);
 				return;
 			}
 			installAllTools();
@@ -404,7 +409,7 @@
 				return;
 			}
 			const updatedGoConfig = getGoConfig();
-			updateGoPathGoRootFromConfig();
+			updateGoVarsFromConfig();
 
 			// If there was a change in "toolsGopath" setting, then clear cache for go tools
 			if (getToolsGopath() !== getToolsGopath(false)) {
@@ -429,7 +434,7 @@
 				updateCodeCoverageDecorators(updatedGoConfig['coverageDecorator']);
 			}
 			if (e.affectsConfiguration('go.toolsEnvVars')) {
-				const env = getToolsEnvVars();
+				const env = toolExecutionEnvironment();
 				if (GO111MODULE !== env['GO111MODULE']) {
 					const reloadMsg =
 						'Reload VS Code window so that the Go tools can respect the change to GO111MODULE';
@@ -558,6 +563,13 @@
 		})
 	);
 
+	// Go Enviornment switching commands
+	ctx.subscriptions.push(
+		vscode.commands.registerCommand('go.environment.choose', () => {
+			chooseGoEnvironment();
+		})
+	);
+
 	vscode.languages.setLanguageConfiguration(GO_MODE.language, {
 		wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g
 	});
@@ -619,28 +631,6 @@
 	);
 }
 
-// registerUsualProviders registers the language feature providers if the language server is not enabled.
-function registerUsualProviders(ctx: vscode.ExtensionContext) {
-	const provider = new GoCompletionItemProvider(ctx.globalState);
-	ctx.subscriptions.push(provider);
-	ctx.subscriptions.push(vscode.languages.registerCompletionItemProvider(GO_MODE, provider, '.', '"'));
-	ctx.subscriptions.push(vscode.languages.registerHoverProvider(GO_MODE, new GoHoverProvider()));
-	ctx.subscriptions.push(vscode.languages.registerDefinitionProvider(GO_MODE, new GoDefinitionProvider()));
-	ctx.subscriptions.push(vscode.languages.registerReferenceProvider(GO_MODE, new GoReferenceProvider()));
-	ctx.subscriptions.push(vscode.languages.registerDocumentSymbolProvider(GO_MODE, new GoDocumentSymbolProvider()));
-	ctx.subscriptions.push(vscode.languages.registerWorkspaceSymbolProvider(new GoWorkspaceSymbolProvider()));
-	ctx.subscriptions.push(
-		vscode.languages.registerSignatureHelpProvider(GO_MODE, new GoSignatureHelpProvider(), '(', ',')
-	);
-	ctx.subscriptions.push(vscode.languages.registerImplementationProvider(GO_MODE, new GoImplementationProvider()));
-	ctx.subscriptions.push(
-		vscode.languages.registerDocumentFormattingEditProvider(GO_MODE, new GoDocumentFormattingEditProvider())
-	);
-	ctx.subscriptions.push(vscode.languages.registerTypeDefinitionProvider(GO_MODE, new GoTypeDefinitionProvider()));
-	ctx.subscriptions.push(vscode.languages.registerRenameProvider(GO_MODE, new GoRenameProvider()));
-	vscode.workspace.onDidChangeTextDocument(parseLiveFile, null, ctx.subscriptions);
-}
-
 function addOnChangeTextDocumentListeners(ctx: vscode.ExtensionContext) {
 	vscode.workspace.onDidChangeTextDocument(trackCodeCoverageRemovalOnFileChange, null, ctx.subscriptions);
 	vscode.workspace.onDidChangeTextDocument(removeTestStatus, null, ctx.subscriptions);
diff --git a/src/goModifytags.ts b/src/goModifytags.ts
index 707ae5b..bfaa5b2 100644
--- a/src/goModifytags.ts
+++ b/src/goModifytags.ts
@@ -7,8 +7,9 @@
 
 import cp = require('child_process');
 import vscode = require('vscode');
+import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool } from './goInstallTools';
-import { byteOffsetAt, getBinPath, getFileArchive, getGoConfig, getToolsEnvVars } from './util';
+import { byteOffsetAt, getBinPath, getFileArchive, getGoConfig } from './util';
 
 // Interface for the output from gomodifytags
 interface GomodifytagsOutput {
@@ -128,7 +129,14 @@
 					prompt: 'Enter comma separated options'
 				})
 				.then((inputOptions) => {
-					return [inputTags, inputOptions, transformValue];
+					return vscode.window
+						.showInputBox({
+							value: transformValue,
+							prompt: 'Enter transform value'
+						})
+						.then((transformOption) => {
+							return [inputTags, inputOptions, transformOption];
+						});
 				});
 		});
 }
@@ -137,7 +145,7 @@
 	const gomodifytags = getBinPath('gomodifytags');
 	const editor = vscode.window.activeTextEditor;
 	const input = getFileArchive(editor.document);
-	const p = cp.execFile(gomodifytags, args, { env: getToolsEnvVars() }, (err, stdout, stderr) => {
+	const p = cp.execFile(gomodifytags, args, { env: toolExecutionEnvironment() }, (err, stdout, stderr) => {
 		if (err && (<any>err).code === 'ENOENT') {
 			promptForMissingTool('gomodifytags');
 			return;
diff --git a/src/goModules.ts b/src/goModules.ts
index 612d82e..2a41455 100644
--- a/src/goModules.ts
+++ b/src/goModules.ts
@@ -6,11 +6,12 @@
 import cp = require('child_process');
 import path = require('path');
 import vscode = require('vscode');
+import { toolExecutionEnvironment } from './goEnv';
 import { installTools } from './goInstallTools';
-import { envPath, fixDriveCasingInWindows } from './goPath';
+import { envPath, fixDriveCasingInWindows, getCurrentGoRoot } from './goPath';
 import { getTool } from './goTools';
 import { getFromGlobalState, updateGlobalState } from './stateUtils';
-import { getBinPath, getGoConfig, getGoVersion, getModuleCache, getToolsEnvVars } from './util';
+import { getBinPath, getGoConfig, getGoVersion, getModuleCache } from './util';
 
 export let GO111MODULE: string;
 
@@ -18,14 +19,14 @@
 	const goExecutable = getBinPath('go');
 	if (!goExecutable) {
 		console.warn(
-			`Failed to run "go env GOMOD" to find mod file as the "go" binary cannot be found in either GOROOT(${process.env['GOROOT']}) or PATH(${envPath})`
+			`Failed to run "go env GOMOD" to find mod file as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath})`
 		);
 		return;
 	}
-	const env = getToolsEnvVars();
+	const env = toolExecutionEnvironment();
 	GO111MODULE = env['GO111MODULE'];
 	return new Promise((resolve) => {
-		cp.execFile(goExecutable, ['env', 'GOMOD'], { cwd: folderPath, env: getToolsEnvVars() }, (err, stdout) => {
+		cp.execFile(goExecutable, ['env', 'GOMOD'], { cwd: folderPath, env }, (err, stdout) => {
 			if (err) {
 				console.warn(`Error when running go env GOMOD: ${err}`);
 				return resolve();
@@ -64,7 +65,6 @@
 		logModuleUsage();
 		goModEnvResult = path.dirname(goModEnvResult);
 		const goConfig = getGoConfig(fileuri);
-		let promptFormatTool = goConfig['formatTool'] === 'goreturns';
 
 		if (goConfig['inferGopath'] === true) {
 			goConfig.update('inferGopath', false, vscode.ConfigurationTarget.WorkspaceFolder);
@@ -72,20 +72,19 @@
 				'The "inferGopath" setting is disabled for this workspace because Go modules are being used.'
 			);
 		}
+
+		// TODO(rstambler): This will offer multiple prompts to the user, but
+		// it's still better than waiting for user input. Ideally, this should
+		// be combined into one prompt.
 		if (goConfig['useLanguageServer'] === false) {
 			const promptMsg =
 				'For better performance using Go modules, you can try the experimental Go language server, gopls.';
-			const choseToUpdateLS = await promptToUpdateToolForModules('gopls', promptMsg, goConfig);
-			promptFormatTool = promptFormatTool && !choseToUpdateLS;
-		} else if (promptFormatTool) {
-			const languageServerExperimentalFeatures: any = goConfig.get('languageServerExperimentalFeatures');
-			promptFormatTool = languageServerExperimentalFeatures['format'] === false;
-		}
+			promptToUpdateToolForModules('gopls', promptMsg, goConfig);
 
-		if (promptFormatTool) {
-			const promptMsgForFormatTool =
-				'`goreturns` doesnt support auto-importing missing imports when using Go modules yet. Please update the "formatTool" setting to `goimports`.';
-			await promptToUpdateToolForModules('switchFormatToolToGoimports', promptMsgForFormatTool, goConfig);
+			if (goConfig['formatTool'] === 'goreturns') {
+				const promptMsgForFormatTool = `The goreturns tool does not support Go modules. Please update the "formatTool" setting to goimports.`;
+				promptToUpdateToolForModules('switchFormatToolToGoimports', promptMsgForFormatTool, goConfig);
+			}
 		}
 	}
 	packagePathToGoModPathMap[pkgPath] = goModEnvResult;
@@ -125,22 +124,7 @@
 			if (tool === 'switchFormatToolToGoimports') {
 				goConfig.update('formatTool', 'goimports', vscode.ConfigurationTarget.Global);
 			} else {
-				installTools([getTool(tool)], goVersion).then(() => {
-					if (tool === 'gopls') {
-						if (goConfig.get('useLanguageServer') === false) {
-							goConfig.update('useLanguageServer', true, vscode.ConfigurationTarget.Global);
-						}
-						if (goConfig.inspect('useLanguageServer').workspaceFolderValue === false) {
-							goConfig.update('useLanguageServer', true, vscode.ConfigurationTarget.WorkspaceFolder);
-						}
-						const reloadMsg = 'Reload VS Code window to enable the use of Go language server';
-						vscode.window.showInformationMessage(reloadMsg, 'Reload').then((selectedForReload) => {
-							if (selectedForReload === 'Reload') {
-								vscode.commands.executeCommand('workbench.action.reloadWindow');
-							}
-						});
-					}
-				});
+				await installTools([getTool(tool)], goVersion);
 			}
 			promptedToolsForModules[tool] = true;
 			updateGlobalState('promptedToolsForModules', promptedToolsForModules);
@@ -178,12 +162,12 @@
 	const goRuntimePath = getBinPath('go');
 	if (!goRuntimePath) {
 		console.warn(
-			`Failed to run "go list" to find current package as the "go" binary cannot be found in either GOROOT(${process.env['GOROOT']}) or PATH(${envPath})`
+			`Failed to run "go list" to find current package as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath})`
 		);
 		return;
 	}
 	return new Promise<string>((resolve) => {
-		const childProcess = cp.spawn(goRuntimePath, ['list'], { cwd, env: getToolsEnvVars() });
+		const childProcess = cp.spawn(goRuntimePath, ['list'], { cwd, env: toolExecutionEnvironment() });
 		const chunks: any[] = [];
 		childProcess.stdout.on('data', (stdout) => {
 			chunks.push(stdout);
diff --git a/src/goOutline.ts b/src/goOutline.ts
index 15ad345..97af953 100644
--- a/src/goOutline.ts
+++ b/src/goOutline.ts
@@ -7,12 +7,12 @@
 
 import cp = require('child_process');
 import vscode = require('vscode');
+import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool, promptForUpdatingTool } from './goInstallTools';
 import {
 	getBinPath,
 	getFileArchive,
 	getGoConfig,
-	getToolsEnvVars,
 	killProcess,
 	makeMemoizedByteOffsetConverter
 } from './util';
@@ -92,7 +92,7 @@
 		}
 
 		// Spawn `go-outline` process
-		p = cp.execFile(gooutline, gooutlineFlags, { env: getToolsEnvVars() }, (err, stdout, stderr) => {
+		p = cp.execFile(gooutline, gooutlineFlags, { env: toolExecutionEnvironment() }, (err, stdout, stderr) => {
 			try {
 				if (err && (<any>err).code === 'ENOENT') {
 					promptForMissingTool('go-outline');
@@ -193,7 +193,7 @@
 }
 
 export class GoDocumentSymbolProvider implements vscode.DocumentSymbolProvider {
-	constructor(private includeImports?: boolean) {}
+	constructor(private includeImports?: boolean) { }
 
 	public provideDocumentSymbols(
 		document: vscode.TextDocument,
diff --git a/src/goPackages.ts b/src/goPackages.ts
index 208d2d2..5c7fce7 100644
--- a/src/goPackages.ts
+++ b/src/goPackages.ts
@@ -6,9 +6,10 @@
 import cp = require('child_process');
 import path = require('path');
 import vscode = require('vscode');
+import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool, promptForUpdatingTool } from './goInstallTools';
-import { envPath, fixDriveCasingInWindows, getCurrentGoWorkspaceFromGOPATH } from './goPath';
-import { getBinPath, getCurrentGoPath, getGoVersion, getToolsEnvVars, isVendorSupported } from './util';
+import { envPath, fixDriveCasingInWindows, getCurrentGoRoot, getCurrentGoWorkspaceFromGOPATH } from './goPath';
+import { getBinPath, getCurrentGoPath, getGoVersion, isVendorSupported } from './util';
 
 type GopkgsDone = (res: Map<string, PackageInfo>) => void;
 interface Cache {
@@ -45,7 +46,7 @@
 			args.push('-workDir', workDir);
 		}
 
-		const cmd = cp.spawn(gopkgsBinPath, args, { env: getToolsEnvVars() });
+		const cmd = cp.spawn(gopkgsBinPath, args, { env: toolExecutionEnvironment() });
 		const chunks: any[] = [];
 		const errchunks: any[] = [];
 		let err: any;
@@ -71,7 +72,7 @@
 				);
 				return resolve(pkgs);
 			}
-			const goroot = process.env['GOROOT'];
+			const goroot = getCurrentGoRoot();
 			const output = chunks.join('');
 			if (output.indexOf(';') === -1) {
 				// User might be using the old gopkgs tool, prompt to update
@@ -261,7 +262,7 @@
 	const goRuntimePath = getBinPath('go');
 	if (!goRuntimePath) {
 		console.warn(
-			`Failed to run "go list" to find packages as the "go" binary cannot be found in either GOROOT(${process.env['GOROOT']}) or PATH(${envPath})`
+			`Failed to run "go list" to find packages as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) PATH(${envPath})`
 		);
 		return;
 	}
@@ -269,7 +270,7 @@
 		const childProcess = cp.spawn(
 			goRuntimePath,
 			['list', '-f', 'ImportPath: {{.ImportPath}} FolderPath: {{.Dir}}', './...'],
-			{ cwd: currentFolderPath, env: getToolsEnvVars() }
+			{ cwd: currentFolderPath, env: toolExecutionEnvironment() }
 		);
 		const chunks: any[] = [];
 		childProcess.stdout.on('data', (stdout) => {
diff --git a/src/goPath.ts b/src/goPath.ts
index 06cbacf..6f4d742 100644
--- a/src/goPath.ts
+++ b/src/goPath.ts
@@ -1,5 +1,6 @@
 /*---------------------------------------------------------
  * Copyright (C) Microsoft Corporation. All rights reserved.
+ * Modification copyright 2020 The Go Authors. All rights reserved.
  * Licensed under the MIT License. See LICENSE in the project root for license information.
  *--------------------------------------------------------*/
 
@@ -30,16 +31,20 @@
 	return null;
 }
 
-export function getBinPathWithPreferredGopath(toolName: string, preferredGopaths: string[], alternateTool?: string) {
-	if (binPathCache[toolName]) {
-		return binPathCache[toolName];
-	}
-
+export function getBinPathWithPreferredGopath(
+	toolName: string,
+	preferredGopaths: string[],
+	alternateTool?: string
+) {
 	if (alternateTool && path.isAbsolute(alternateTool) && executableFileExists(alternateTool)) {
 		binPathCache[toolName] = alternateTool;
 		return alternateTool;
 	}
 
+	if (binPathCache[toolName]) {
+		return binPathCache[toolName];
+	}
+
 	const binname = alternateTool && !path.isAbsolute(alternateTool) ? alternateTool : toolName;
 	const pathFromGoBin = getBinPathFromEnvVar(binname, process.env['GOBIN'], false);
 	if (pathFromGoBin) {
@@ -59,7 +64,7 @@
 	}
 
 	// Check GOROOT (go, gofmt, godoc would be found here)
-	const pathFromGoRoot = getBinPathFromEnvVar(binname, process.env['GOROOT'], true);
+	const pathFromGoRoot = getBinPathFromEnvVar(binname, getCurrentGoRoot(), true);
 	if (pathFromGoRoot) {
 		binPathCache[toolName] = pathFromGoRoot;
 		return pathFromGoRoot;
@@ -86,6 +91,18 @@
 	return toolName;
 }
 
+/**
+ * Returns the goroot path if it exists, otherwise returns an empty string
+ */
+let currentGoRoot = '';
+export function getCurrentGoRoot(): string {
+	return currentGoRoot || process.env['GOROOT'] || '';
+}
+
+export function setCurrentGoRoot(goroot: string) {
+	currentGoRoot = goroot;
+}
+
 function correctBinname(toolName: string) {
 	if (process.platform === 'win32') {
 		return toolName + '.exe';
diff --git a/src/goReferences.ts b/src/goReferences.ts
index bb7acfb..44a86fc 100644
--- a/src/goReferences.ts
+++ b/src/goReferences.ts
@@ -8,6 +8,7 @@
 import cp = require('child_process');
 import path = require('path');
 import vscode = require('vscode');
+import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool } from './goInstallTools';
 import {
 	byteOffsetAt,
@@ -15,7 +16,6 @@
 	getBinPath,
 	getFileArchive,
 	getGoConfig,
-	getToolsEnvVars,
 	killTree
 } from './util';
 
@@ -51,7 +51,7 @@
 			const filename = canonicalizeGOPATHPrefix(document.fileName);
 			const cwd = path.dirname(filename);
 			const offset = byteOffsetAt(document, wordRange.start);
-			const env = getToolsEnvVars();
+			const env = toolExecutionEnvironment();
 			const buildTags = getGoConfig(document.uri)['buildTags'];
 			const args = buildTags ? ['-tags', buildTags] : [];
 			args.push('-modified', 'referrers', `${filename}:#${offset.toString()}`);
diff --git a/src/goRename.ts b/src/goRename.ts
index bab3e25..55a97f7 100644
--- a/src/goRename.ts
+++ b/src/goRename.ts
@@ -8,9 +8,10 @@
 import cp = require('child_process');
 import vscode = require('vscode');
 import { Edit, FilePatch, getEditsFromUnifiedDiffStr, isDiffToolAvailable } from './diffUtils';
+import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool } from './goInstallTools';
 import { outputChannel } from './goStatus';
-import { byteOffsetAt, canonicalizeGOPATHPrefix, getBinPath, getGoConfig, getToolsEnvVars, killTree } from './util';
+import { byteOffsetAt, canonicalizeGOPATHPrefix, getBinPath, getGoConfig, killTree } from './util';
 
 export class GoRenameProvider implements vscode.RenameProvider {
 	public provideRenameEdits(
@@ -35,7 +36,7 @@
 			const range = document.getWordRangeAtPosition(position);
 			const pos = range ? range.start : position;
 			const offset = byteOffsetAt(document, pos);
-			const env = getToolsEnvVars();
+			const env = toolExecutionEnvironment();
 			const gorename = getBinPath('gorename');
 			const buildTags = getGoConfig(document.uri)['buildTags'];
 			const gorenameArgs = ['-offset', filename + ':#' + offset, '-to', newName];
diff --git a/src/goRunTestCodelens.ts b/src/goRunTestCodelens.ts
index f53388b..587450a 100644
--- a/src/goRunTestCodelens.ts
+++ b/src/goRunTestCodelens.ts
@@ -10,21 +10,12 @@
 import { GoBaseCodeLensProvider } from './goBaseCodelens';
 import { GoDocumentSymbolProvider } from './goOutline';
 import { getBenchmarkFunctions, getTestFunctions } from './testUtils';
-import { getCurrentGoPath, getGoConfig } from './util';
+import { getGoConfig } from './util';
 
 export class GoRunTestCodeLensProvider extends GoBaseCodeLensProvider {
 	private readonly benchmarkRegex = /^Benchmark.+/;
-	private readonly debugConfig: any = {
-		name: 'Launch',
-		type: 'go',
-		request: 'launch',
-		mode: 'test',
-		env: {
-			GOPATH: getCurrentGoPath() // Passing current GOPATH to Delve as it runs in another process
-		}
-	};
 
-	public provideCodeLenses(document: TextDocument, token: CancellationToken): CodeLens[] | Thenable<CodeLens[]> {
+	public async provideCodeLenses(document: TextDocument, token: CancellationToken): Promise<CodeLens[]> {
 		if (!this.enabled) {
 			return [];
 		}
@@ -35,24 +26,19 @@
 			return [];
 		}
 
-		return Promise.all([
+		const codelenses = await Promise.all([
 			this.getCodeLensForPackage(document, token),
-			this.getCodeLensForFunctions(config, document, token)
-		]).then(([pkg, fns]) => {
-			let res: any[] = [];
-			if (pkg && Array.isArray(pkg)) {
-				res = res.concat(pkg);
-			}
-			if (fns && Array.isArray(fns)) {
-				res = res.concat(fns);
-			}
-			return res;
-		});
+			this.getCodeLensForFunctions(document, token)
+		]);
+		return ([] as CodeLens[]).concat(...codelenses);
 	}
 
 	private async getCodeLensForPackage(document: TextDocument, token: CancellationToken): Promise<CodeLens[]> {
 		const documentSymbolProvider = new GoDocumentSymbolProvider();
 		const symbols = await documentSymbolProvider.provideDocumentSymbols(document, token);
+		if (!symbols || symbols.length === 0) {
+			return [];
+		}
 		const pkg = symbols[0];
 		if (!pkg) {
 			return [];
@@ -69,7 +55,7 @@
 			})
 		];
 		if (
-			symbols[0].children.some(
+			pkg.children.some(
 				(sym) => sym.kind === vscode.SymbolKind.Function && this.benchmarkRegex.test(sym.name)
 			)
 		) {
@@ -87,54 +73,50 @@
 		return packageCodeLens;
 	}
 
-	private async getCodeLensForFunctions(
-		vsConfig: vscode.WorkspaceConfiguration,
-		document: TextDocument,
-		token: CancellationToken
-	): Promise<CodeLens[]> {
-		const codelens: CodeLens[] = [];
-
-		const testPromise = getTestFunctions(document, token).then((testFunctions) => {
-			testFunctions.forEach((func) => {
-				const runTestCmd: Command = {
+	private async getCodeLensForFunctions(document: TextDocument, token: CancellationToken): Promise<CodeLens[]> {
+		const testPromise = async (): Promise<CodeLens[]> => {
+			const testFunctions = await getTestFunctions(document, token);
+			if (!testFunctions) {
+				return [];
+			}
+			const codelens: CodeLens[] = [];
+			for (const f of testFunctions) {
+				codelens.push(new CodeLens(f.range, {
 					title: 'run test',
 					command: 'go.test.cursor',
-					arguments: [{ functionName: func.name }]
-				};
-
-				codelens.push(new CodeLens(func.range, runTestCmd));
-
-				const debugTestCmd: Command = {
+					arguments: [{ functionName: f.name }]
+				}));
+				codelens.push(new CodeLens(f.range, {
 					title: 'debug test',
 					command: 'go.debug.cursor',
-					arguments: [{ functionName: func.name }]
-				};
+					arguments: [{ functionName: f.name }]
+				}));
+			}
+			return codelens;
+		};
 
-				codelens.push(new CodeLens(func.range, debugTestCmd));
-			});
-		});
-
-		const benchmarkPromise = getBenchmarkFunctions(document, token).then((benchmarkFunctions) => {
-			benchmarkFunctions.forEach((func) => {
-				const runBenchmarkCmd: Command = {
+		const benchmarkPromise = (async (): Promise<CodeLens[]> => {
+			const benchmarkFunctions = await getBenchmarkFunctions(document, token);
+			if (!benchmarkFunctions) {
+				return [];
+			}
+			const codelens: CodeLens[] = [];
+			for (const f of benchmarkFunctions) {
+				codelens.push(new CodeLens(f.range, {
 					title: 'run benchmark',
 					command: 'go.benchmark.cursor',
-					arguments: [{ functionName: func.name }]
-				};
-
-				codelens.push(new CodeLens(func.range, runBenchmarkCmd));
-
-				const debugTestCmd: Command = {
+					arguments: [{ functionName: f.name }]
+				}));
+				codelens.push(new CodeLens(f.range, {
 					title: 'debug benchmark',
 					command: 'go.debug.cursor',
-					arguments: [{ functionName: func.name }]
-				};
-
-				codelens.push(new CodeLens(func.range, debugTestCmd));
-			});
+					arguments: [{ functionName: f.name }]
+				}));
+			}
+			return codelens;
 		});
 
-		await Promise.all([testPromise, benchmarkPromise]);
-		return codelens;
+		const codelenses = await Promise.all([testPromise(), benchmarkPromise()]);
+		return ([] as CodeLens[]).concat(...codelenses);
 	}
 }
diff --git a/src/goStatus.ts b/src/goStatus.ts
index 69a1184..c13cd4f 100644
--- a/src/goStatus.ts
+++ b/src/goStatus.ts
@@ -18,7 +18,7 @@
 statusBarItemModule.text = '$(megaphone) Go Modules';
 statusBarItemModule.tooltip =
 	'Modules is enabled for this project. Click to learn more about Modules support in VS Code.';
-statusBarItemModule.command = 'go.open.modulewiki';
+statusBarItemModule.command = 'go.open.modulesdoc';
 
 export function showHideStatus(editor: vscode.TextEditor) {
 	if (statusBarEntry) {
diff --git a/src/goSuggest.ts b/src/goSuggest.ts
index 139fbc1..e676ca0 100644
--- a/src/goSuggest.ts
+++ b/src/goSuggest.ts
@@ -8,6 +8,7 @@
 import cp = require('child_process');
 import path = require('path');
 import vscode = require('vscode');
+import { toolExecutionEnvironment } from './goEnv';
 import { getTextEditForAddImport } from './goImport';
 import { promptForMissingTool, promptForUpdatingTool } from './goInstallTools';
 import { isModSupported } from './goModules';
@@ -19,7 +20,6 @@
 	getCurrentGoPath,
 	getGoConfig,
 	getParametersAndReturnType,
-	getToolsEnvVars,
 	goBuiltinTypes,
 	goKeywords,
 	guessPackageNameFromFile,
@@ -269,7 +269,7 @@
 		const gocodeName = this.isGoMod ? 'gocode-gomod' : 'gocode';
 		const gocode = getBinPath(gocodeName);
 		if (path.isAbsolute(gocode)) {
-			cp.spawn(gocode, ['close'], { env: getToolsEnvVars() });
+			cp.spawn(gocode, ['close'], { env: toolExecutionEnvironment() });
 		}
 	}
 
@@ -293,7 +293,7 @@
 				return reject();
 			}
 
-			const env = getToolsEnvVars();
+			const env = toolExecutionEnvironment();
 			let stdout = '';
 			let stderr = '';
 
@@ -380,8 +380,8 @@
 									config['useCodeSnippetsOnFunctionSuggestWithoutType']) &&
 								((suggest.class === 'func' && lineText.substr(position.character, 2) !== '()') || // Avoids met() -> method()()
 									(suggest.class === 'var' &&
-									suggest.type.startsWith('func(') &&
-									lineText.substr(position.character, 1) !== ')' && // Avoids snippets when typing params in a func call
+										suggest.type.startsWith('func(') &&
+										lineText.substr(position.character, 1) !== ')' && // Avoids snippets when typing params in a func call
 										lineText.substr(position.character, 1) !== ',')) // Avoids snippets when typing params in a func call
 							) {
 								const { params, returnType } = getParametersAndReturnType(suggest.type.substring(4));
@@ -421,22 +421,22 @@
 										const arg = param.substr(0, param.indexOf(' '));
 										paramSnippets.push(
 											'${' +
-												(i + 1) +
-												':' +
-												arg +
-												'}' +
-												param.substr(param.indexOf(' '), param.length)
+											(i + 1) +
+											':' +
+											arg +
+											'}' +
+											param.substr(param.indexOf(' '), param.length)
 										);
 									}
 								}
 								item.insertText = new vscode.SnippetString(
 									suggest.name +
-										'(func(' +
-										paramSnippets.join(', ') +
-										') {\n	$' +
-										(params.length + 1) +
-										'\n})' +
-										returnType
+									'(func(' +
+									paramSnippets.join(', ') +
+									') {\n	$' +
+									(params.length + 1) +
+									'\n})' +
+									returnType
 								);
 							}
 
@@ -504,7 +504,7 @@
 
 		const setGocodeProps = new Promise<void>((resolve, reject) => {
 			const gocode = getBinPath('gocode');
-			const env = getToolsEnvVars();
+			const env = toolExecutionEnvironment();
 
 			cp.execFile(gocode, ['set'], { env }, (err, stdout, stderr) => {
 				if (err && stdout.startsWith('gocode: unknown subcommand:')) {
@@ -696,48 +696,3 @@
 	});
 	return suggestions;
 }
-
-export async function getCompletionsWithoutGoCode(
-	document: vscode.TextDocument,
-	position: vscode.Position
-): Promise<vscode.CompletionItem[]> {
-	// Completions for the package statement based on the file name
-	const pkgStatementCompletions = await getPackageStatementCompletions(document);
-	if (pkgStatementCompletions && pkgStatementCompletions.length) {
-		return pkgStatementCompletions;
-	}
-
-	const lineText = document.lineAt(position.line).text;
-	const config = getGoConfig(document.uri);
-	const autocompleteUnimportedPackages =
-		config['autocompleteUnimportedPackages'] === true && !lineText.match(/^(\s)*(import|package)(\s)+/);
-
-	const commentCompletion = getCommentCompletion(document, position);
-	if (commentCompletion) {
-		return [commentCompletion];
-	}
-
-	if (isPositionInComment(document, position)) {
-		return [];
-	}
-
-	const currentWord = getCurrentWord(document, position);
-	if (!currentWord.length) {
-		return [];
-	}
-
-	// gocode does not suggest keywords, so we have to do it
-	const completionItems: any[] = getKeywordCompletions(currentWord);
-	if (!autocompleteUnimportedPackages) {
-		return completionItems;
-	}
-
-	const isMod = await isModSupported(document.uri);
-	if (isMod) {
-		return completionItems;
-	}
-
-	const pkgMap = await getImportablePackages(document.fileName, true);
-	const packageCompletions = getPackageCompletions(document, currentWord, pkgMap);
-	return packageCompletions.concat(completionItems);
-}
diff --git a/src/goSymbol.ts b/src/goSymbol.ts
index 2c03f6d..b97e018 100644
--- a/src/goSymbol.ts
+++ b/src/goSymbol.ts
@@ -7,8 +7,10 @@
 
 import cp = require('child_process');
 import vscode = require('vscode');
+import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool, promptForUpdatingTool } from './goInstallTools';
-import { getBinPath, getGoConfig, getToolsEnvVars, getWorkspaceFolderPath, killTree } from './util';
+import { getCurrentGoRoot } from './goPath';
+import { getBinPath, getGoConfig, getWorkspaceFolderPath, killTree } from './util';
 
 // Keep in sync with github.com/acroca/go-symbols'
 interface GoSymbolDeclaration {
@@ -92,7 +94,7 @@
 	calls.push(callGoSymbols([...baseArgs, workspacePath, query], token));
 
 	if (gotoSymbolConfig.includeGoroot) {
-		const goRoot = process.env['GOROOT'];
+		const goRoot = getCurrentGoRoot();
 		const gorootCall = callGoSymbols([...baseArgs, goRoot, query], token);
 		calls.push(gorootCall);
 	}
@@ -112,7 +114,7 @@
 
 function callGoSymbols(args: string[], token: vscode.CancellationToken): Promise<GoSymbolDeclaration[]> {
 	const gosyms = getBinPath('go-symbols');
-	const env = getToolsEnvVars();
+	const env = toolExecutionEnvironment();
 	let p: cp.ChildProcess;
 
 	if (token) {
diff --git a/src/goTest.ts b/src/goTest.ts
index cc76650..b25c78a 100644
--- a/src/goTest.ts
+++ b/src/goTest.ts
@@ -6,7 +6,6 @@
 
 import path = require('path');
 import vscode = require('vscode');
-import { applyCodeCoverageToAllEditors } from './goCover';
 import { isModSupported } from './goModules';
 import {
 	extractInstanceTestName,
@@ -19,7 +18,6 @@
 	goTest,
 	TestConfig
 } from './testUtils';
-import { getTempFilePath } from './util';
 
 // lastTestConfig holds a reference to the last executed TestConfig which allows
 // the last test to be easily re-executed.
@@ -108,6 +106,66 @@
 }
 
 /**
+ * Executes the sub unit test at the primary cursor using `go test`. Output
+ * is sent to the 'Go' channel.
+ *
+ * @param goConfig Configuration for the Go extension.
+ */
+export async function subTestAtCursor(goConfig: vscode.WorkspaceConfiguration, args: any) {
+	const editor = vscode.window.activeTextEditor;
+	if (!editor) {
+		vscode.window.showInformationMessage('No editor is active.');
+		return;
+	}
+	if (!editor.document.fileName.endsWith('_test.go')) {
+		vscode.window.showInformationMessage('No tests found. Current file is not a test file.');
+		return;
+	}
+
+	await editor.document.save();
+	try {
+		const testFunctions = await getTestFunctions(editor.document, null);
+		// We use functionName if it was provided as argument
+		// Otherwise find any test function containing the cursor.
+		const currentTestFunctions = testFunctions.filter((func) => func.range.contains(editor.selection.start));
+		const testFunctionName =
+			args && args.functionName ? args.functionName : currentTestFunctions.map((el) => el.name)[0];
+
+		if (!testFunctionName || currentTestFunctions.length === 0) {
+			vscode.window.showInformationMessage('No test function found at cursor.');
+			return;
+		}
+
+		const testFunction = currentTestFunctions[0];
+		const simpleRunRegex = /t.Run\("([^"]+)",/;
+		const runRegex = /t.Run\(/;
+		let lineText: string;
+		let runMatch: RegExpMatchArray | null;
+		let simpleMatch: RegExpMatchArray | null;
+		for (let i = editor.selection.start.line; i >= testFunction.range.start.line; i--) {
+			lineText = editor.document.lineAt(i).text;
+			simpleMatch = lineText.match(simpleRunRegex);
+			runMatch = lineText.match(runRegex);
+			if (simpleMatch || (runMatch && !simpleMatch)) {
+				break;
+			}
+		}
+
+		if (!simpleMatch) {
+			vscode.window.showInformationMessage('No subtest function with a simple subtest name found at cursor.');
+			return;
+		}
+
+		const subTestName = testFunctionName + '/' + simpleMatch[1];
+
+		return await runTestAtCursor(editor, subTestName, testFunctions, goConfig, 'test', args);
+	} catch (err) {
+		vscode.window.showInformationMessage('Unable to run subtest: ' + err.toString());
+		console.error(err);
+	}
+}
+
+/**
  * Debugs the test at cursor.
  */
 async function debugTestAtCursor(
diff --git a/src/goTools.ts b/src/goTools.ts
index 7ef0a9b..eaa2b87 100644
--- a/src/goTools.ts
+++ b/src/goTools.ts
@@ -5,15 +5,46 @@
 
 'use strict';
 
-import { SemVer } from 'semver';
+import cp = require('child_process');
+import fs = require('fs');
+import moment = require('moment');
+import path = require('path');
+import semver = require('semver');
+import util = require('util');
 import { goLiveErrorsEnabled } from './goLiveErrors';
-import { getGoConfig, GoVersion } from './util';
+import { getBinPath, getGoConfig, GoVersion } from './util';
 
 export interface Tool {
 	name: string;
 	importPath: string;
 	isImportant: boolean;
 	description: string;
+
+	// latestVersion and latestVersionTimestamp are hardcoded default values
+	// for the last known version of the given tool. We also hardcode values
+	// for the latest known pre-release of the tool for the Nightly extension.
+	latestVersion?: semver.SemVer;
+	latestVersionTimestamp?: moment.Moment;
+	latestPrereleaseVersion?: semver.SemVer;
+	latestPrereleaseVersionTimestamp?: moment.Moment;
+
+	// minimumGoVersion and maximumGoVersion set the range for the versions of
+	// Go with which this tool can be used.
+	minimumGoVersion?: semver.SemVer;
+	maximumGoVersion?: semver.SemVer;
+
+	// close performs any shutdown tasks that a tool must execute before a new
+	// version is installed. It returns a string containing an error message on
+	// failure.
+	close?: () => Promise<string>;
+}
+
+/**
+ * ToolAtVersion is a Tool at a specific version.
+ * Lack of version implies the latest version.
+ */
+export interface ToolAtVersion extends Tool {
+	version?: semver.SemVer;
 }
 
 /**
@@ -29,7 +60,7 @@
 	return tool.importPath;
 }
 
-export function getImportPathWithVersion(tool: Tool, version: SemVer, goVersion: GoVersion): string {
+export function getImportPathWithVersion(tool: Tool, version: semver.SemVer, goVersion: GoVersion): string {
 	const importPath = getImportPath(tool, goVersion);
 	if (version) {
 		return importPath + '@v' + version;
@@ -62,6 +93,10 @@
 	return allToolsInformation[name];
 }
 
+export function getToolAtVersion(name: string, version?: semver.SemVer): ToolAtVersion {
+	return { ...allToolsInformation[name], version };
+}
+
 // hasModSuffix returns true if the given tool has a different, module-specific
 // name to avoid conflicts.
 export function hasModSuffix(tool: Tool): boolean {
@@ -141,18 +176,35 @@
 	return tools;
 }
 
-const allToolsInformation: { [key: string]: Tool } = {
+export const allToolsInformation: { [key: string]: Tool } = {
 	'gocode': {
 		name: 'gocode',
 		importPath: 'github.com/mdempsky/gocode',
 		isImportant: true,
-		description: 'Auto-completion, does not work with modules'
+		description: 'Auto-completion, does not work with modules',
+		close: async (): Promise<string> => {
+			const toolBinPath = getBinPath('gocode');
+			if (!path.isAbsolute(toolBinPath)) {
+				return '';
+			}
+			try {
+				const execFile = util.promisify(cp.execFile);
+				const { stderr } = await execFile(toolBinPath, ['close']);
+				if (stderr.indexOf(`rpc: can't find service Server.`) > -1) {
+					return `Installing gocode aborted as existing process cannot be closed. Please kill the running process for gocode and try again.`;
+				}
+			} catch (err) {
+				return `Failed to close gocode process: ${err}.`;
+			}
+			return '';
+		},
 	},
 	'gocode-gomod': {
 		name: 'gocode-gomod',
 		importPath: 'github.com/stamblerre/gocode',
 		isImportant: true,
-		description: 'Auto-completion, works with modules'
+		description: 'Auto-completion, works with modules',
+		minimumGoVersion: semver.coerce('1.11'),
 	},
 	'gopkgs': {
 		name: 'gopkgs',
@@ -242,13 +294,15 @@
 		name: 'golint',
 		importPath: 'golang.org/x/lint/golint',
 		isImportant: true,
-		description: 'Linter'
+		description: 'Linter',
+		minimumGoVersion: semver.coerce('1.9'),
 	},
 	'gotests': {
 		name: 'gotests',
 		importPath: 'github.com/cweill/gotests/...',
 		isImportant: false,
-		description: 'Generate unit tests'
+		description: 'Generate unit tests',
+		minimumGoVersion: semver.coerce('1.9'),
 	},
 	'staticcheck': {
 		name: 'staticcheck',
@@ -272,7 +326,12 @@
 		name: 'gopls',
 		importPath: 'golang.org/x/tools/gopls',
 		isImportant: false,
-		description: 'Language Server from Google'
+		description: 'Language Server from Google',
+		minimumGoVersion: semver.coerce('1.12'),
+		latestVersion: semver.coerce('0.4.1'),
+		latestVersionTimestamp: moment('2020-05-13', 'YYYY-MM-DD'),
+		latestPrereleaseVersion: semver.coerce('0.4.1'),
+		latestPrereleaseVersionTimestamp: moment('2020-05-13', 'YYYY-MM-DD'),
 	},
 	'dlv': {
 		name: 'dlv',
diff --git a/src/goTypeDefinition.ts b/src/goTypeDefinition.ts
index d414559..100deb1 100644
--- a/src/goTypeDefinition.ts
+++ b/src/goTypeDefinition.ts
@@ -9,6 +9,7 @@
 import path = require('path');
 import vscode = require('vscode');
 import { adjustWordPosition, definitionLocation, parseMissingError } from './goDeclaration';
+import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool } from './goInstallTools';
 import {
 	byteOffsetAt,
@@ -16,7 +17,6 @@
 	getBinPath,
 	getFileArchive,
 	getGoConfig,
-	getToolsEnvVars,
 	goBuiltinTypes,
 	killTree
 } from './util';
@@ -61,7 +61,7 @@
 
 			const filename = canonicalizeGOPATHPrefix(document.fileName);
 			const offset = byteOffsetAt(document, position);
-			const env = getToolsEnvVars();
+			const env = toolExecutionEnvironment();
 			const buildTags = getGoConfig(document.uri)['buildTags'];
 			const args = buildTags ? ['-tags', buildTags] : [];
 			args.push('-json', '-modified', 'describe', `${filename}:#${offset.toString()}`);
diff --git a/src/goVet.ts b/src/goVet.ts
index 1de766b..210213a 100644
--- a/src/goVet.ts
+++ b/src/goVet.ts
@@ -5,12 +5,12 @@
 
 import path = require('path');
 import vscode = require('vscode');
+import { toolExecutionEnvironment } from './goEnv';
 import { vetDiagnosticCollection } from './goMain';
 import { diagnosticsStatusBarItem, outputChannel } from './goStatus';
 import {
 	getGoConfig,
 	getGoVersion,
-	getToolsEnvVars,
 	getWorkspaceFolderPath,
 	handleDiagnosticErrors,
 	ICheckResult,
@@ -81,7 +81,7 @@
 	}
 
 	const vetFlags: string[] = goConfig['vetFlags'] || [];
-	const vetEnv = Object.assign({}, getToolsEnvVars());
+	const vetEnv = toolExecutionEnvironment();
 	const args: string[] = [];
 
 	vetFlags.forEach((flag) => {
diff --git a/src/stateUtils.ts b/src/stateUtils.ts
index 72402ac..c7b49c2 100644
--- a/src/stateUtils.ts
+++ b/src/stateUtils.ts
@@ -8,7 +8,7 @@
 let globalState: vscode.Memento;
 let workspaceState: vscode.Memento;
 
-export function getFromGlobalState(key: string, defaultValue?: any) {
+export function getFromGlobalState(key: string, defaultValue?: any): any {
 	if (!globalState) {
 		return defaultValue;
 	}
diff --git a/src/telemetry.ts b/src/telemetry.ts
deleted file mode 100644
index 88f733a..0000000
--- a/src/telemetry.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-/*---------------------------------------------------------
- * Copyright (C) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See LICENSE in the project root for license information.
- *--------------------------------------------------------*/
-
-// TODO(hyangah): move this somewhere else (where easily modifiable during release process)
-
-export const extensionId: string = 'golang.Go';
diff --git a/src/testUtils.ts b/src/testUtils.ts
index cfeb493..092dc45 100644
--- a/src/testUtils.ts
+++ b/src/testUtils.ts
@@ -8,19 +8,19 @@
 import vscode = require('vscode');
 
 import { applyCodeCoverageToAllEditors } from './goCover';
+import { toolExecutionEnvironment } from './goEnv';
 import { getCurrentPackage } from './goModules';
 import { GoDocumentSymbolProvider } from './goOutline';
 import { getNonVendorPackages } from './goPackages';
-import { envPath, getCurrentGoWorkspaceFromGOPATH, parseEnvFile } from './goPath';
+import { envPath, getCurrentGoRoot, getCurrentGoWorkspaceFromGOPATH, parseEnvFile } from './goPath';
 import {
 	getBinPath,
 	getCurrentGoPath,
 	getGoVersion,
 	getTempFilePath,
-	getToolsEnvVars,
 	killTree,
 	LineBuffer,
-	resolvePath
+	resolvePath,
 } from './util';
 
 const outputChannel = vscode.window.createOutputChannel('Go Tests');
@@ -80,7 +80,7 @@
 }
 
 export function getTestEnvVars(config: vscode.WorkspaceConfiguration): any {
-	const envVars = getToolsEnvVars();
+	const envVars = toolExecutionEnvironment();
 	const testEnvConfig = config['testEnvVars'] || {};
 
 	let fileEnv: { [key: string]: any } = {};
@@ -122,24 +122,28 @@
  * @param the URI of a Go source file.
  * @return test function symbols for the source file.
  */
-export function getTestFunctions(
+export async function getTestFunctions(
 	doc: vscode.TextDocument,
 	token: vscode.CancellationToken
-): Thenable<vscode.DocumentSymbol[]> {
+): Promise<vscode.DocumentSymbol[] | undefined> {
 	const documentSymbolProvider = new GoDocumentSymbolProvider(true);
-	return documentSymbolProvider
-		.provideDocumentSymbols(doc, token)
-		.then((symbols) => symbols[0].children)
-		.then((symbols) => {
-			const testify = symbols.some(
-				(sym) => sym.kind === vscode.SymbolKind.Namespace && sym.name === '"github.com/stretchr/testify/suite"'
-			);
-			return symbols.filter(
-				(sym) =>
-					sym.kind === vscode.SymbolKind.Function &&
-					(testFuncRegex.test(sym.name) || (testify && testMethodRegex.test(sym.name)))
-			);
-		});
+	const symbols = await documentSymbolProvider.provideDocumentSymbols(doc, token);
+	if (!symbols || symbols.length === 0) {
+		return;
+	}
+	const symbol = symbols[0];
+	if (!symbol) {
+		return;
+	}
+	const children = symbol.children;
+	const testify = children.some(
+		(sym) => sym.kind === vscode.SymbolKind.Namespace && sym.name === '"github.com/stretchr/testify/suite"'
+	);
+	return children.filter(
+		(sym) =>
+			sym.kind === vscode.SymbolKind.Function &&
+			(testFuncRegex.test(sym.name) || (testify && testMethodRegex.test(sym.name)))
+	);
 }
 
 /**
@@ -202,17 +206,21 @@
  * @param the URI of a Go source file.
  * @return benchmark function symbols for the source file.
  */
-export function getBenchmarkFunctions(
+export async function getBenchmarkFunctions(
 	doc: vscode.TextDocument,
 	token: vscode.CancellationToken
-): Thenable<vscode.DocumentSymbol[]> {
+): Promise<vscode.DocumentSymbol[] | undefined> {
 	const documentSymbolProvider = new GoDocumentSymbolProvider();
-	return documentSymbolProvider
-		.provideDocumentSymbols(doc, token)
-		.then((symbols) => symbols[0].children)
-		.then((symbols) =>
-			symbols.filter((sym) => sym.kind === vscode.SymbolKind.Function && benchmarkRegex.test(sym.name))
-		);
+	const symbols = await documentSymbolProvider.provideDocumentSymbols(doc, token);
+	if (!symbols || symbols.length === 0) {
+		return;
+	}
+	const symbol = symbols[0];
+	if (!symbol) {
+		return;
+	}
+	const children = symbol.children;
+	return children.filter((sym) => sym.kind === vscode.SymbolKind.Function && benchmarkRegex.test(sym.name));
 }
 
 /**
@@ -254,7 +262,7 @@
 
 		if (!goRuntimePath) {
 			vscode.window.showErrorMessage(
-				`Failed to run "go test" as the "go" binary cannot be found in either GOROOT(${process.env['GOROOT']}) or PATH(${envPath})`
+				`Failed to run "go test" as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath})`
 			);
 			return Promise.resolve();
 		}
@@ -455,7 +463,11 @@
 			// in running all the test methods, but one of them should call testify's `suite.Run(...)`
 			// which will result in the correct thing to happen
 			if (testFunctions.length > 0) {
-				params = params.concat(['-run', util.format('^(%s)$', testFunctions.join('|'))]);
+				if (testFunctions.length === 1) {
+					params = params.concat(['-run', util.format('^%s$', testFunctions.pop())]);
+				} else {
+					params = params.concat(['-run', util.format('^(%s)$', testFunctions.join('|'))]);
+				}
 			}
 			if (testifyMethods.length > 0) {
 				params = params.concat(['-testify.m', util.format('^(%s)$', testifyMethods.join('|'))]);
diff --git a/src/util.ts b/src/util.ts
index 47e152d..556e52f 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -9,19 +9,22 @@
 import path = require('path');
 import semver = require('semver');
 import kill = require('tree-kill');
+import util = require('util');
 import vscode = require('vscode');
 import { NearestNeighborDict, Node } from './avlTree';
+import { extensionId } from './const';
+import { toolExecutionEnvironment } from './goEnv';
 import { buildDiagnosticCollection, lintDiagnosticCollection, vetDiagnosticCollection } from './goMain';
 import { getCurrentPackage } from './goModules';
 import {
 	envPath,
 	fixDriveCasingInWindows,
 	getBinPathWithPreferredGopath,
+	getCurrentGoRoot,
 	getInferredGopath,
-	resolveHomeDir
+	resolveHomeDir,
 } from './goPath';
 import { outputChannel } from './goStatus';
-import { extensionId } from './telemetry';
 
 let userNameHash: number = 0;
 
@@ -77,21 +80,28 @@
 ]);
 
 export class GoVersion {
-	public sv: semver.SemVer;
-	public isDevel: boolean;
-	private commit: string;
+	public sv?: semver.SemVer;
+	public isDevel?: boolean;
+	private commit?: string;
 
-	constructor(version: string) {
+	constructor(public binaryPath: string, version: string) {
 		const matchesRelease = /go version go(\d.\d+).*/.exec(version);
 		const matchesDevel = /go version devel \+(.[a-zA-Z0-9]+).*/.exec(version);
 		if (matchesRelease) {
-			this.sv = semver.coerce(matchesRelease[0]);
+			const sv = semver.coerce(matchesRelease[0]);
+			if (sv) {
+				this.sv = sv;
+			}
 		} else if (matchesDevel) {
 			this.isDevel = true;
 			this.commit = matchesDevel[0];
 		}
 	}
 
+	public isValid(): boolean {
+		return !!this.sv || !!this.isDevel;
+	}
+
 	public format(): string {
 		if (this.sv) {
 			return this.sv.format();
@@ -105,7 +115,11 @@
 		if (this.isDevel || !this.sv) {
 			return false;
 		}
-		return semver.lt(this.sv, semver.coerce(version));
+		const v = semver.coerce(version);
+		if (!v) {
+			return false;
+		}
+		return semver.lt(this.sv, v);
 	}
 
 	public gt(version: string): boolean {
@@ -114,12 +128,16 @@
 		if (this.isDevel || !this.sv) {
 			return true;
 		}
-		return semver.gt(this.sv, semver.coerce(version));
+		const v = semver.coerce(version);
+		if (!v) {
+			return false;
+		}
+		return semver.gt(this.sv, v);
 	}
 }
 
-let cachedGoVersion: GoVersion = null;
-let vendorSupport: boolean = null;
+let cachedGoVersion: GoVersion | undefined;
+let vendorSupport: boolean | undefined;
 let toolsGopath: string;
 
 export function getGoConfig(uri?: vscode.Uri): vscode.WorkspaceConfiguration {
@@ -281,33 +299,37 @@
  * Gets version of Go based on the output of the command `go version`.
  * Returns null if go is being used from source/tip in which case `go version` will not return release tag like go1.6.3
  */
-export async function getGoVersion(): Promise<GoVersion> {
+export async function getGoVersion(): Promise<GoVersion | undefined> {
 	const goRuntimePath = getBinPath('go');
 
+	const warn = (msg: string) => {
+		outputChannel.appendLine(msg);
+		console.warn(msg);
+	};
+
 	if (!goRuntimePath) {
-		console.warn(
-			`Failed to run "go version" as the "go" binary cannot be found in either GOROOT(${process.env['GOROOT']}) or PATH(${envPath})`
-		);
-		return Promise.resolve(null);
+		warn(`unable to locate "go" binary in GOROOT (${getCurrentGoRoot()}) or PATH (${envPath})`);
+		return;
 	}
-	if (cachedGoVersion && (cachedGoVersion.sv || cachedGoVersion.isDevel)) {
-		return Promise.resolve(cachedGoVersion);
+	if (cachedGoVersion) {
+		if (cachedGoVersion.isValid()) {
+			return Promise.resolve(cachedGoVersion);
+		}
+		warn(`cached Go version (${cachedGoVersion}) is invalid, recomputing`);
 	}
-	return new Promise<GoVersion>((resolve) => {
-		cp.execFile(goRuntimePath, ['version'], {}, (err, stdout, stderr) => {
-			cachedGoVersion = new GoVersion(stdout);
-			if (!cachedGoVersion.sv && !cachedGoVersion.isDevel) {
-				if (err || stderr) {
-					console.log(`Error when running the command "${goRuntimePath} version": `, err || stderr);
-				} else {
-					console.log(
-						`Not able to determine version from the output of the command "${goRuntimePath} version": ${stdout}`
-					);
-				}
-			}
-			return resolve(cachedGoVersion);
-		});
-	});
+	try {
+		const execFile = util.promisify(cp.execFile);
+		const { stdout, stderr } = await execFile(goRuntimePath, ['version']);
+		if (stderr) {
+			warn(`failed to run "${goRuntimePath} version": stdout: ${stdout}, stderr: ${stderr}`);
+			return;
+		}
+		cachedGoVersion = new GoVersion(goRuntimePath, stdout);
+	} catch (err) {
+		warn(`failed to run "${goRuntimePath} version": ${err}`);
+		return;
+	}
+	return cachedGoVersion;
 }
 
 /**
@@ -328,7 +350,7 @@
 		case 1:
 			vendorSupport =
 				goVersion.sv.minor > 6 ||
-				((goVersion.sv.minor === 5 || goVersion.sv.minor === 6) && process.env['GO15VENDOREXPERIMENT'] === '1')
+					((goVersion.sv.minor === 5 || goVersion.sv.minor === 6) && process.env['GO15VENDOREXPERIMENT'] === '1')
 					? true
 					: false;
 			break;
@@ -417,7 +439,7 @@
 	return getBinPathWithPreferredGopath(
 		tool,
 		tool === 'go' ? [] : [getToolsGopath(), getCurrentGoPath()],
-		resolvePath(alternateToolPath)
+		resolvePath(alternateToolPath),
 	);
 }
 
@@ -426,24 +448,6 @@
 	return document.fileName + '\n' + Buffer.byteLength(fileContents, 'utf8') + '\n' + fileContents;
 }
 
-export function getToolsEnvVars(): any {
-	const config = getGoConfig();
-	const toolsEnvVars = config['toolsEnvVars'];
-
-	const gopath = getCurrentGoPath();
-	const envVars = Object.assign({}, process.env, gopath ? { GOPATH: gopath } : {});
-
-	if (toolsEnvVars && typeof toolsEnvVars === 'object') {
-		Object.keys(toolsEnvVars).forEach(
-			(key) =>
-				(envVars[key] =
-					typeof toolsEnvVars[key] === 'string' ? resolvePath(toolsEnvVars[key]) : toolsEnvVars[key])
-		);
-	}
-
-	return envVars;
-}
-
 export function substituteEnv(input: string): string {
 	return input.replace(/\${env:([^}]+)}/g, (match, capture) => {
 		return process.env[capture.trim()] || '';
@@ -852,11 +856,15 @@
 }
 
 export const killTree = (processId: number): void => {
-	kill(processId, (err) => {
-		if (err) {
-			console.log('Error killing process tree: ' + err);
-		}
-	});
+	try {
+		kill(processId, (err) => {
+			if (err) {
+				console.log(`Error killing process tree: ${err}`);
+			}
+		});
+	} catch (err) {
+		console.log(`Error killing process tree: ${err}`);
+	}
 };
 
 export function killProcess(p: cp.ChildProcess) {
@@ -897,9 +905,13 @@
 		fs.readdirSync(dir).forEach((file) => {
 			const relPath = path.join(dir, file);
 			if (fs.lstatSync(relPath).isDirectory()) {
-				rmdirRecursive(dir);
+				rmdirRecursive(relPath);
 			} else {
-				fs.unlinkSync(relPath);
+				try {
+					fs.unlinkSync(relPath);
+				} catch (err) {
+					console.log(`failed to remove ${relPath}: ${err}`);
+				}
 			}
 		});
 		fs.rmdirSync(dir);
@@ -967,7 +979,7 @@
 				symbol = receiver + '.' + symbol;
 			}
 
-			const env = getToolsEnvVars();
+			const env = toolExecutionEnvironment();
 			const args = ['doc', '-c', '-cmd', '-u', packageImportPath, symbol];
 			const p = cp.execFile(goRuntimePath, args, { env, cwd }, (err, stdout, stderr) => {
 				if (err) {
diff --git a/test/fixtures/codelens/codelens_benchmark_test.go b/test/fixtures/codelens/codelens_benchmark_test.go
new file mode 100644
index 0000000..ecd6ed6
--- /dev/null
+++ b/test/fixtures/codelens/codelens_benchmark_test.go
@@ -0,0 +1,20 @@
+package main
+
+import (
+	"testing"
+)
+
+func BenchmarkSample(b *testing.B) {
+	b.Run("sample test passing", func(t *testing.B) {
+
+	})
+
+	b.Run("sample test failing", func(t *testing.B) {
+		t.FailNow()
+	})
+
+	testName := "dynamic test name"
+	b.Run(testName, func(t *testing.B) {
+		t.FailNow()
+	})
+}
diff --git a/test/fixtures/codelens/codelens_test.go b/test/fixtures/codelens/codelens_test.go
new file mode 100644
index 0000000..3481139
--- /dev/null
+++ b/test/fixtures/codelens/codelens_test.go
@@ -0,0 +1,20 @@
+package main
+
+import (
+	"testing"
+)
+
+func TestSample(t *testing.T) {
+	t.Run("sample test passing", func(t *testing.T) {
+
+	})
+
+	t.Run("sample test failing", func(t *testing.T) {
+		t.FailNow()
+	})
+
+	testName := "dynamic test name"
+	t.Run(testName, func(t *testing.T) {
+		t.FailNow()
+	})
+}
diff --git a/test/fixtures/codelens/go.mod b/test/fixtures/codelens/go.mod
new file mode 100644
index 0000000..1455032
--- /dev/null
+++ b/test/fixtures/codelens/go.mod
@@ -0,0 +1,3 @@
+module github.com/microsoft/vscode-go/gofixtures/subtests
+
+go 1.14
diff --git a/test/gopls/extension.test.ts b/test/gopls/extension.test.ts
index bde1fd8..64cddfc 100644
--- a/test/gopls/extension.test.ts
+++ b/test/gopls/extension.test.ts
@@ -7,9 +7,7 @@
 import * as fs from 'fs-extra';
 import * as path from 'path';
 import * as vscode from 'vscode';
-import { updateGoPathGoRootFromConfig } from '../../src/goInstallTools';
-import { extensionId } from '../../src/telemetry';
-import { getCurrentGoPath } from '../../src/util';
+import { extensionId } from '../../src/const';
 
 // Env is a collection of test related variables
 // that define the test environment such as vscode workspace.
@@ -46,11 +44,6 @@
 	}
 
 	public async setup() {
-		const wscfg = vscode.workspace.getConfiguration('go');
-		if (!wscfg.get('useLanguageServer')) {
-			wscfg.update('useLanguageServer', true, vscode.ConfigurationTarget.Workspace);
-		}
-
 		await this.reset();
 		await this.extension.activate();
 		await sleep(2000);  // allow extension host + gopls to start.
@@ -62,10 +55,10 @@
 			// needed to keep the empty directory in vcs.
 			await fs.readdir(this.workspaceDir).then((files) => {
 				return Promise.all(
-					files.filter((filename) => filename !== '.gitignore').map((file) => {
+					files.filter((filename) => filename !== '.gitignore' && filename !== '.vscode').map((file) => {
 						fs.remove(path.resolve(this.workspaceDir, file));
 					}));
-				});
+			});
 
 			if (!fixtureDirName) {
 				return;
@@ -92,7 +85,7 @@
 }
 
 suite('Go Extension Tests With Gopls', function () {
-	this.timeout(1000000);
+	this.timeout(300000);
 	const projectDir = path.join(__dirname, '..', '..', '..');
 	const env = new Env(projectDir);
 
@@ -136,4 +129,34 @@
 		});
 		return Promise.all(promises);
 	});
+
+	test('Completion middleware', async () => {
+		await env.reset('gogetdocTestData');
+		const { uri } = await env.openDoc('test.go');
+		const testCases: [string, vscode.Position, string][] = [
+			['fmt.<>', new vscode.Position(19, 5), 'Formatter'],
+		];
+		for (const [name, position, wantFilterText] of testCases) {
+			const list = await vscode.commands.executeCommand(
+				'vscode.executeCompletionItemProvider', uri, position) as vscode.CompletionList;
+
+			// Confirm that the hardcoded filter text hack has been applied.
+			if (!list.isIncomplete) {
+				assert.fail(`gopls should provide an incomplete list by default`);
+			}
+			// TODO(rstambler): For some reason, the filter text gets deleted
+			// from the first item. I can't reproduce this outside of the test
+			// suite.
+			for (let i = 1; i < list.items.length; i++) {
+				const item = list.items[i];
+				assert.equal(item.filterText, wantFilterText, `${uri}:${name} failed, unexpected filter text (got ${item.filterText}, want ${wantFilterText})`);
+			}
+			for (const item of list.items) {
+				if (item.kind === vscode.CompletionItemKind.Method || item.kind === vscode.CompletionItemKind.Function) {
+					assert.ok(item.command, `${uri}:${name}: expected command associated with ${item.label}, found none`);
+				}
+			}
+		}
+
+	});
 });
diff --git a/test/gopls/survey.test.ts b/test/gopls/survey.test.ts
new file mode 100644
index 0000000..bccd3d6
--- /dev/null
+++ b/test/gopls/survey.test.ts
@@ -0,0 +1,81 @@
+/*---------------------------------------------------------
+ * 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 sinon = require('sinon');
+import { shouldPromptForGoplsSurvey, SurveyConfig } from '../../src/goLanguageServer';
+
+suite('gopls survey tests', () => {
+	test('prompt for survey', () => {
+		// global state -> offer survey
+		const testCases: [SurveyConfig, boolean][] = [
+			// User who is activating the extension for the first time.
+			[
+				{},
+				true,
+			],
+			// User who has already taken the survey.
+			[
+				{
+					lastDateAccepted: new Date('2020-04-02'),
+					promptThisMonthTimestamp: new Date('2020-04-10'),
+					lastDatePrompted: new Date('2020-04-02'),
+					prompt: true,
+					promptThisMonth: false,
+				},
+				false,
+			],
+			// User who has declined survey prompting.
+			[
+				{
+					promptThisMonthTimestamp: new Date('2020-04-10'),
+					lastDatePrompted: new Date('2020-04-02'),
+					prompt: false,
+				},
+				false,
+			],
+			// User who hasn't activated the extension in a while, but has opted in to prompting.
+			[
+				{
+					promptThisMonthTimestamp: new Date('2019-04-10'),
+					lastDatePrompted: new Date('2019-01-02'),
+					prompt: true,
+				},
+				true,
+			],
+			// User who hasn't activated the extension in a while, and has never been prompted.
+			[
+				{
+					promptThisMonthTimestamp: new Date('2019-04-10'),
+					lastDatePrompted: new Date('2019-01-02'),
+				},
+				true,
+			],
+			// User who should get prompted this month, but hasn't been yet.
+			[
+				{
+					lastDateAccepted: undefined,
+					promptThisMonthTimestamp: new Date('2020-04-10'),
+					lastDatePrompted: new Date('2019-01-02'),
+					prompt: true,
+					promptThisMonth: true,
+				},
+				true,
+			],
+		];
+		testCases.map(([testConfig, wantPrompt], i) => {
+			// Replace Math.Random so that it always returns 1. This means
+			// that we will always choose to prompt, in the event that the
+			// user can be prompted that month.
+			sinon.replace(Math, 'random', () => 1);
+
+			const now = new Date('2020-04-29');
+			const gotPrompt = shouldPromptForGoplsSurvey(now, testConfig);
+			assert.equal(wantPrompt, gotPrompt, `prompt determination failed for ${i}`);
+
+			sinon.restore();
+		});
+	});
+});
diff --git a/test/gopls/testfixtures/src/workspace/.gitignore b/test/gopls/testfixtures/src/workspace/.gitignore
index d6b7ef3..a6821b0 100644
--- a/test/gopls/testfixtures/src/workspace/.gitignore
+++ b/test/gopls/testfixtures/src/workspace/.gitignore
@@ -1,2 +1,3 @@
-*
+/*
 !.gitignore
+!.vscode/
\ No newline at end of file
diff --git a/test/gopls/testfixtures/src/workspace/.vscode/settings.json b/test/gopls/testfixtures/src/workspace/.vscode/settings.json
new file mode 100644
index 0000000..2474ded
--- /dev/null
+++ b/test/gopls/testfixtures/src/workspace/.vscode/settings.json
@@ -0,0 +1,5 @@
+{
+	"go.useLanguageServer": true,
+	"go.languageServerFlags": ["-rpc.trace", "serve"],
+	"go.useGoProxyToCheckForToolUpdates": false,
+}
\ No newline at end of file
diff --git a/test/gopls/update.test.ts b/test/gopls/update.test.ts
new file mode 100644
index 0000000..dbe5312
--- /dev/null
+++ b/test/gopls/update.test.ts
@@ -0,0 +1,109 @@
+/*---------------------------------------------------------
+ * 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 lsp from '../../src/goLanguageServer';
+import { getTool, Tool } from '../../src/goTools';
+
+suite('gopls update tests', () => {
+	test('prompt for update', async () => {
+		const tool = getTool('gopls');
+
+		const toSemver = (v: string) => semver.coerce(v, {includePrerelease: true, loose: true});
+
+		// Fake data stubbed functions will serve.
+		const latestVersion = toSemver('0.4.1');
+		const latestVersionTimestamp = moment('2020-05-13', 'YYYY-MM-DD');
+		const latestPrereleaseVersion = toSemver('0.4.2-pre1');
+		const latestPrereleaseVersionTimestamp = moment('2020-05-20', 'YYYY-MM-DD');
+
+		// name, usersVersion, acceptPrerelease, want
+		const testCases: [string, string, boolean, semver.SemVer][] = [
+			['outdated, tagged', 'v0.3.1', false, latestVersion],
+			['outdated, tagged (pre-release)', '0.3.1', true, latestPrereleaseVersion],
+			['up-to-date, tagged', latestVersion.format(), false, null],
+			['up-to-date tagged (pre-release)', 'v0.4.0', true, latestPrereleaseVersion],
+			['developer version', '(devel)', false, null],
+			['developer version (pre-release)', '(devel)', true, null],
+			['nonsense version', 'nosuchversion', false, latestVersion],
+			['nonsense version (pre-release)', 'nosuchversion', true, latestPrereleaseVersion],
+			[
+				'latest pre-release',
+				'v0.4.2-pre1',
+				false, null,
+			],
+			[
+				'latest pre-release (pre-release)',
+				'v0.4.2-pre1',
+				true, null,
+			],
+			[
+				'outdated pre-release version',
+				'v0.3.1-pre1',
+				false, latestVersion,
+			],
+			[
+				'outdated pre-release version (pre-release)',
+				'v0.3.1-pre1',
+				true, latestPrereleaseVersion,
+			],
+			[
+				'recent pseudoversion after pre-release, 2020-05-20',
+				'v0.0.0-20200521000000-2212a7e161a5',
+				false, null,
+			],
+			[
+				'recent pseudoversion before pre-release, 2020-05-20',
+				'v0.0.0-20200515000000-2212a7e161a5',
+				false, null,
+			],
+			[
+				'recent pseudoversion after pre-release (pre-release)',
+				'v0.0.0-20200521000000-2212a7e161a5',
+				true, null,
+			],
+			[
+				'recent pseudoversion before pre-release (pre-release)',
+				'v0.0.0-20200515000000-2212a7e161a5',
+				true, latestPrereleaseVersion,
+			],
+			[
+				'outdated pseudoversion',
+				'v0.0.0-20200309030707-2212a7e161a5',
+				false, latestVersion,
+			],
+			[
+				'outdated pseudoversion (pre-release)',
+				'v0.0.0-20200309030707-2212a7e161a5',
+				true, latestPrereleaseVersion,
+			],
+		];
+		for (const [name, usersVersion, acceptPrerelease, want] of testCases) {
+			sinon.replace(lsp, 'getLocalGoplsVersion', async () => {
+				return usersVersion;
+			});
+			sinon.replace(lsp, 'getLatestGoplsVersion', async () => {
+				if (acceptPrerelease) {
+					return latestPrereleaseVersion;
+				}
+				return latestVersion;
+			});
+			sinon.replace(lsp, 'getTimestampForVersion', async (_: Tool, version: semver.SemVer) => {
+				if (version === latestVersion) {
+					return latestVersionTimestamp;
+				}
+				if (version === latestPrereleaseVersion) {
+					return latestPrereleaseVersionTimestamp;
+				}
+			});
+			const got = await lsp.shouldUpdateLanguageServer(tool, 'bad/path/to/gopls', true);
+			assert.deepEqual(got, want, `${name}: failed (got: '${got}' ${typeof got} want: '${want}' ${typeof want})`);
+			sinon.restore();
+		}
+	});
+});
diff --git a/test/integration/codelens.test.ts b/test/integration/codelens.test.ts
new file mode 100644
index 0000000..f28d81d
--- /dev/null
+++ b/test/integration/codelens.test.ts
@@ -0,0 +1,133 @@
+/*---------------------------------------------------------
+ * Copyright 2020 The Go Authors. All rights reserved.
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
+ *--------------------------------------------------------*/
+
+'use strict';
+
+import * as assert from 'assert';
+import fs = require('fs-extra');
+import path = require('path');
+import sinon = require('sinon');
+import vscode = require('vscode');
+import { updateGoVarsFromConfig } from '../../src/goInstallTools';
+import { GoRunTestCodeLensProvider } from '../../src/goRunTestCodelens';
+import { subTestAtCursor } from '../../src/goTest';
+import { getCurrentGoPath } from '../../src/util';
+
+suite('Code lenses for testing and benchmarking', function () {
+	this.timeout(20000);
+
+	let gopath: string;
+	let repoPath: string;
+	let fixturePath: string;
+	let fixtureSourcePath: string;
+
+	let goConfig: vscode.WorkspaceConfiguration;
+	let document: vscode.TextDocument;
+
+	const cancellationTokenSource = new vscode.CancellationTokenSource();
+	const codeLensProvider = new GoRunTestCodeLensProvider();
+
+	suiteSetup(async () => {
+		await updateGoVarsFromConfig();
+
+		gopath = getCurrentGoPath();
+		if (!gopath) {
+			assert.fail('Cannot run tests without a configured GOPATH');
+		}
+		console.log(`Using GOPATH: ${gopath}`);
+
+		// Set up the test fixtures.
+		repoPath = path.join(gopath, 'src', 'test');
+		fixturePath = path.join(repoPath, 'testfixture');
+		fixtureSourcePath = path.join(__dirname, '..', '..', '..', 'test', 'fixtures', 'codelens');
+
+		fs.removeSync(repoPath);
+		fs.copySync(fixtureSourcePath, fixturePath, {
+			recursive: true,
+			// All of the tests run in GOPATH mode for now.
+			// TODO(rstambler): Run tests in GOPATH and module mode.
+			filter: (src: string): boolean => {
+				if (path.basename(src) === 'go.mod') {
+					return false;
+				}
+				return true;
+			},
+		});
+		goConfig = vscode.workspace.getConfiguration('go');
+		const uri = vscode.Uri.file(path.join(fixturePath, 'codelens_test.go'));
+		document = await vscode.workspace.openTextDocument(uri);
+	});
+
+	suiteTeardown(() => {
+		fs.removeSync(repoPath);
+	});
+
+	teardown(() => {
+		sinon.restore();
+	});
+
+	test('Subtests - runs a test with cursor on t.Run line', async () => {
+		const editor = await vscode.window.showTextDocument(document);
+		editor.selection = new vscode.Selection(7, 4, 7, 4);
+		const result = await subTestAtCursor(goConfig, []);
+		assert.equal(result, true);
+	});
+
+	test('Subtests - runs a test with cursor within t.Run function', async () => {
+		const editor = await vscode.window.showTextDocument(document);
+		editor.selection = new vscode.Selection(8, 4, 8, 4);
+		const result = await subTestAtCursor(goConfig, []);
+		assert.equal(result, true);
+	});
+
+	test('Subtests - returns false for a failing test', async () => {
+		const editor = await vscode.window.showTextDocument(document);
+		editor.selection = new vscode.Selection(11, 4, 11, 4);
+		const result = await subTestAtCursor(goConfig, []);
+		assert.equal(result, false);
+	});
+
+	test('Subtests - does nothing for a dynamically defined subtest', async () => {
+		const editor = await vscode.window.showTextDocument(document);
+		editor.selection = new vscode.Selection(17, 4, 17, 4);
+		const result = await subTestAtCursor(goConfig, []);
+		assert.equal(result, undefined);
+	});
+
+	test('Subtests - does nothing when cursor outside of a test function', async () => {
+		const editor = await vscode.window.showTextDocument(document);
+		editor.selection = new vscode.Selection(5, 0, 5, 0);
+		const result = await subTestAtCursor(goConfig, []);
+		assert.equal(result, undefined);
+	});
+
+	test('Subtests - does nothing when no test function covers the cursor and a function name is passed in', async () => {
+		const editor = await vscode.window.showTextDocument(document);
+		editor.selection = new vscode.Selection(5, 0, 5, 0);
+		const result = await subTestAtCursor(goConfig, { functionName: 'TestMyFunction' });
+		assert.equal(result, undefined);
+	});
+
+	test('Test codelenses', async () => {
+		const codeLenses = await codeLensProvider.provideCodeLenses(document, cancellationTokenSource.token);
+		assert.equal(codeLenses.length, 4);
+		const wantCommands = ['go.test.package', 'go.test.file', 'go.test.cursor', 'go.debug.cursor'];
+		for (let i = 0; i < codeLenses.length; i++) {
+			assert.equal(codeLenses[i].command.command, wantCommands[i]);
+		}
+	});
+
+	test('Benchmark codelenses', async () => {
+		const uri = vscode.Uri.file(path.join(fixturePath, 'codelens_benchmark_test.go'));
+		const benchmarkDocument = await vscode.workspace.openTextDocument(uri);
+		const codeLenses = await codeLensProvider.provideCodeLenses(benchmarkDocument, cancellationTokenSource.token);
+		assert.equal(codeLenses.length, 6);
+		const wantCommands = ['go.test.package', 'go.test.file', 'go.benchmark.package',
+			'go.benchmark.file', 'go.benchmark.cursor', 'go.debug.cursor'];
+		for (let i = 0; i < codeLenses.length; i++) {
+			assert.equal(codeLenses[i].command.command, wantCommands[i]);
+		}
+	});
+});
diff --git a/test/integration/extension.test.ts b/test/integration/extension.test.ts
index 34569ef..b8c752d 100644
--- a/test/integration/extension.test.ts
+++ b/test/integration/extension.test.ts
@@ -20,7 +20,7 @@
 	generateTestCurrentPackage
 } from '../../src/goGenerateTests';
 import { getTextEditForAddImport, listPackages } from '../../src/goImport';
-import { updateGoPathGoRootFromConfig } from '../../src/goInstallTools';
+import { updateGoVarsFromConfig } from '../../src/goInstallTools';
 import { goLint } from '../../src/goLint';
 import { documentSymbols, GoDocumentSymbolProvider, GoOutlineImportsOptions } from '../../src/goOutline';
 import { getAllPackages } from '../../src/goPackages';
@@ -32,7 +32,7 @@
 import {
 	getBinPath,
 	getCurrentGoPath,
-	getGoVersion,
+	getGoConfig,
 	getImportPath,
 	getToolsGopath,
 	ICheckResult,
@@ -55,7 +55,7 @@
 	let toolsGopath: string;
 
 	suiteSetup(async () => {
-		await updateGoPathGoRootFromConfig();
+		await updateGoVarsFromConfig();
 
 		gopath = getCurrentGoPath();
 		if (!gopath) {
@@ -73,19 +73,17 @@
 		toolsGopath = getToolsGopath() || gopath;
 
 		fs.removeSync(repoPath);
-		fs.copySync(path.join(fixtureSourcePath, 'baseTest', 'test.go'), path.join(fixturePath, 'baseTest', 'test.go'));
-		fs.copySync(
-			path.join(fixtureSourcePath, 'baseTest', 'sample_test.go'),
-			path.join(fixturePath, 'baseTest', 'sample_test.go')
-		);
-		fs.copySync(
-			path.join(fixtureSourcePath, 'errorsTest', 'errors.go'),
-			path.join(fixturePath, 'errorsTest', 'errors.go')
-		);
-		fs.copySync(
-			path.join(fixtureSourcePath, 'gogetdocTestData', 'test.go'),
-			path.join(fixturePath, 'gogetdocTestData', 'test.go')
-		);
+		fs.copySync(fixtureSourcePath, fixturePath, {
+			recursive: true,
+			// All of the tests run in GOPATH mode for now.
+			// TODO(rstambler): Run tests in GOPATH and module mode.
+			filter: (src: string): boolean => {
+				if (path.basename(src) === 'go.mod') {
+					return false;
+				}
+				return true;
+			},
+		});
 		fs.copySync(
 			path.join(fixtureSourcePath, 'generatetests', 'generatetests.go'),
 			path.join(generateTestsSourcePath, 'generatetests.go')
@@ -114,82 +112,6 @@
 			path.join(fixtureSourcePath, 'diffTestData', 'file2.go'),
 			path.join(fixturePath, 'diffTest2Data', 'file2.go')
 		);
-		fs.copySync(
-			path.join(fixtureSourcePath, 'linterTest', 'linter_1.go'),
-			path.join(fixturePath, 'linterTest', 'linter_1.go')
-		);
-		fs.copySync(
-			path.join(fixtureSourcePath, 'linterTest', 'linter_2.go'),
-			path.join(fixturePath, 'linterTest', 'linter_2.go')
-		);
-		fs.copySync(
-			path.join(fixtureSourcePath, 'buildTags', 'hello.go'),
-			path.join(fixturePath, 'buildTags', 'hello.go')
-		);
-		fs.copySync(
-			path.join(fixtureSourcePath, 'testTags', 'hello_test.go'),
-			path.join(fixturePath, 'testTags', 'hello_test.go')
-		);
-		fs.copySync(
-			path.join(fixtureSourcePath, 'completions', 'unimportedPkgs.go'),
-			path.join(fixturePath, 'completions', 'unimportedPkgs.go')
-		);
-		fs.copySync(
-			path.join(fixtureSourcePath, 'completions', 'unimportedMultiplePkgs.go'),
-			path.join(fixturePath, 'completions', 'unimportedMultiplePkgs.go')
-		);
-		fs.copySync(
-			path.join(fixtureSourcePath, 'completions', 'snippets.go'),
-			path.join(fixturePath, 'completions', 'snippets.go')
-		);
-		fs.copySync(
-			path.join(fixtureSourcePath, 'completions', 'nosnippets.go'),
-			path.join(fixturePath, 'completions', 'nosnippets.go')
-		);
-		fs.copySync(
-			path.join(fixtureSourcePath, 'completions', 'exportedMemberDocs.go'),
-			path.join(fixturePath, 'completions', 'exportedMemberDocs.go')
-		);
-		fs.copySync(
-			path.join(fixtureSourcePath, 'importTest', 'noimports.go'),
-			path.join(fixturePath, 'importTest', 'noimports.go')
-		);
-		fs.copySync(
-			path.join(fixtureSourcePath, 'importTest', 'groupImports.go'),
-			path.join(fixturePath, 'importTest', 'groupImports.go')
-		);
-		fs.copySync(
-			path.join(fixtureSourcePath, 'importTest', 'singleImports.go'),
-			path.join(fixturePath, 'importTest', 'singleImports.go')
-		);
-		fs.copySync(
-			path.join(fixtureSourcePath, 'importTest', 'cgoImports.go'),
-			path.join(fixturePath, 'importTest', 'cgoImports.go')
-		);
-		fs.copySync(
-			path.join(fixtureSourcePath, 'fillStruct', 'input_1.go'),
-			path.join(fixturePath, 'fillStruct', 'input_1.go')
-		);
-		fs.copySync(
-			path.join(fixtureSourcePath, 'fillStruct', 'golden_1.go'),
-			path.join(fixturePath, 'fillStruct', 'golden_1.go')
-		);
-		fs.copySync(
-			path.join(fixtureSourcePath, 'fillStruct', 'input_2.go'),
-			path.join(fixturePath, 'fillStruct', 'input_2.go')
-		);
-		fs.copySync(
-			path.join(fixtureSourcePath, 'fillStruct', 'golden_2.go'),
-			path.join(fixturePath, 'fillStruct', 'golden_2.go')
-		);
-		fs.copySync(
-			path.join(fixtureSourcePath, 'fillStruct', 'input_2.go'),
-			path.join(fixturePath, 'fillStruct', 'input_3.go')
-		);
-		fs.copySync(
-			path.join(fixtureSourcePath, 'outlineTest', 'test.go'),
-			path.join(fixturePath, 'outlineTest', 'test.go')
-		);
 	});
 
 	suiteTeardown(() => {
@@ -1310,7 +1232,7 @@
 					expected.length,
 					labels.length,
 					`expected number of completions: ${expected.length} Actual: ${labels.length} at position(${
-						position.line + 1
+					position.line + 1
 					},${position.character + 1}) ${labels}`
 				);
 				expected.forEach((entry, index) => {
@@ -1435,67 +1357,44 @@
 	});
 
 	test('Build Tags checking', async () => {
-		const config1 = Object.create(vscode.workspace.getConfiguration('go'), {
-			vetOnSave: { value: 'off' },
-			lintOnSave: { value: 'off' },
-			buildOnSave: { value: 'package' },
-			buildTags: { value: 'randomtag' }
-		});
+		// Note: The following checks can't be parallelized because the underlying go build command
+		// runner (goBuild) will cancel any outstanding go build commands.
 
-		const checkWithTags = check(vscode.Uri.file(path.join(fixturePath, 'buildTags', 'hello.go')), config1).then(
-			(diagnostics) => {
-				assert.equal(1, diagnostics.length, 'check with buildtag failed. Unexpected errors found');
-				assert.equal(1, diagnostics[0].errors.length, 'check with buildtag failed. Unexpected errors found');
-				assert.equal(diagnostics[0].errors[0].msg, 'undefined: fmt.Prinln');
-			}
+		const checkWithTags = async (tags: string) => {
+			const fileUri = vscode.Uri.file(path.join(fixturePath, 'buildTags', 'hello.go'));
+			const defaultGoCfg = getGoConfig(fileUri);
+			const cfg = Object.create(defaultGoCfg, {
+				vetOnSave: { value: 'off' },
+				lintOnSave: { value: 'off' },
+				buildOnSave: { value: 'package' },
+				buildTags: { value: tags }
+			}) as vscode.WorkspaceConfiguration;
+
+			const diagnostics = await check(fileUri, cfg);
+			return ([] as string[]).concat(...diagnostics.map<string[]>((d) => {
+				return d.errors.map((e) => e.msg) as string[];
+			}));
+		};
+
+		const errors1 = await checkWithTags('randomtag');
+		assert.deepEqual(errors1, ['undefined: fmt.Prinln'], 'check with buildtag "randomtag" failed. Unexpected errors found.');
+
+		// TODO(hyangah): after go1.13, -tags expects a comma-separated tag list.
+		// For backwards compatibility, space-separated tag lists are still recognized,
+		// but change to a space-separated list once we stop testing with go1.12.
+		const errors2 = await checkWithTags('randomtag other');
+		assert.deepEqual(errors2, ['undefined: fmt.Prinln'],
+			'check with multiple buildtags "randomtag,other" failed. Unexpected errors found.');
+
+		const errors3 = await checkWithTags('');
+		assert.equal(errors3.length, 1,
+			'check without buildtag failed. Unexpected number of errors found' + JSON.stringify(errors3));
+		const errMsg = errors3[0];
+		assert.ok(
+			errMsg.includes(`can't load package: package test/testfixture/buildTags`) ||
+			errMsg.includes(`build constraints exclude all Go files`),
+			`check without buildtags failed. Go files not excluded. ${errMsg}`
 		);
-
-		const config2 = Object.create(vscode.workspace.getConfiguration('go'), {
-			vetOnSave: { value: 'off' },
-			lintOnSave: { value: 'off' },
-			buildOnSave: { value: 'package' },
-			buildTags: { value: 'randomtag othertag' }
-		});
-
-		const checkWithMultipleTags = check(
-			vscode.Uri.file(path.join(fixturePath, 'buildTags', 'hello.go')),
-			config2
-		).then((diagnostics) => {
-			assert.equal(1, diagnostics.length, 'check with multiple buildtags failed. Unexpected errors found');
-			assert.equal(
-				1,
-				diagnostics[0].errors.length,
-				'check with multiple buildtags failed. Unexpected errors found'
-			);
-			assert.equal(diagnostics[0].errors[0].msg, 'undefined: fmt.Prinln');
-		});
-
-		const config3 = Object.create(vscode.workspace.getConfiguration('go'), {
-			vetOnSave: { value: 'off' },
-			lintOnSave: { value: 'off' },
-			buildOnSave: { value: 'package' },
-			buildTags: { value: '' }
-		});
-
-		const checkWithoutTags = check(vscode.Uri.file(path.join(fixturePath, 'buildTags', 'hello.go')), config3).then(
-			(diagnostics) => {
-				assert.equal(1, diagnostics.length, 'check without buildtags failed. Unexpected errors found');
-				assert.equal(
-					1,
-					diagnostics[0].errors.length,
-					'check without buildtags failed. Unexpected errors found'
-				);
-				const errMsg = diagnostics[0].errors[0].msg;
-				assert.equal(
-					errMsg.includes(`can't load package: package test/testfixture/buildTags`) ||
-						errMsg.includes(`build constraints exclude all Go files`),
-					true,
-					`check without buildtags failed. Go files not excluded. ${diagnostics[0].errors[0].msg}`
-				);
-			}
-		);
-
-		return Promise.all([checkWithTags, checkWithMultipleTags, checkWithoutTags]);
 	});
 
 	test('Test Tags checking', async () => {
diff --git a/test/integration/goDebug.test.ts b/test/integration/goDebug.test.ts
new file mode 100644
index 0000000..618087f
--- /dev/null
+++ b/test/integration/goDebug.test.ts
@@ -0,0 +1,269 @@
+import * as assert from 'assert';
+import * as fs from 'fs';
+import * as path from 'path';
+import * as sinon from 'sinon';
+import {
+	Delve,
+	escapeGoModPath,
+	GoDebugSession,
+	PackageBuildInfo,
+	RemoteSourcesAndPackages,
+} from '../../src/debugAdapter/goDebug';
+
+suite('Path Manipulation Tests', () => {
+	test('escapeGoModPath works', () => {
+		assert.strictEqual(escapeGoModPath('BurnSushi/test.go'), '!burn!sushi/test.go');
+	});
+});
+
+suite('GoDebugSession Tests', async () => {
+	const workspaceFolder = '/usr/workspacefolder';
+	const delve: Delve = {} as Delve;
+	let goDebugSession: GoDebugSession;
+	let remoteSourcesAndPackages: RemoteSourcesAndPackages;
+	let fileSystem: typeof fs;
+
+	let previousEnv: any;
+
+	setup(() => {
+		previousEnv = Object.assign({}, process.env);
+
+		process.env.GOPATH = '/usr/gopath';
+		process.env.GOROOT = '/usr/goroot';
+		remoteSourcesAndPackages = new RemoteSourcesAndPackages();
+		fileSystem = { existsSync: () => false } as unknown as typeof fs;
+		delve.program = workspaceFolder;
+		delve.isApiV1 = false;
+		goDebugSession = new GoDebugSession(true, false, fileSystem);
+		goDebugSession['delve'] = delve;
+		goDebugSession['remoteSourcesAndPackages'] = remoteSourcesAndPackages;
+	});
+
+	teardown(() => {
+		process.env = previousEnv;
+		sinon.restore();
+	});
+
+	test('inferRemotePathFromLocalPath works', () => {
+		const sourceFileMapping = new Map<string, string[]>();
+		sourceFileMapping.set('main.go', ['/app/hello-world/main.go', '/app/main.go']);
+		sourceFileMapping.set('blah.go', ['/app/blah.go']);
+
+		remoteSourcesAndPackages.remoteSourceFilesNameGrouping = sourceFileMapping;
+
+		const inferredPath = goDebugSession['inferRemotePathFromLocalPath'](
+			'C:\\Users\\Documents\\src\\hello-world\\main.go');
+		assert.strictEqual(inferredPath, '/app/hello-world/main.go');
+	});
+
+	test('inferLocalPathFromRemoteGoPackage works for package in workspaceFolder', () => {
+		const remotePath = '/src/hello-world/morestrings/morestrings.go';
+		const helloPackage: PackageBuildInfo = {
+			ImportPath: 'hello-world/morestrings',
+			DirectoryPath: '/src/hello-world/morestrings',
+			Files: ['/src/hello-world/morestrings/lessstrings.go', '/src/hello-world/morestrings/morestrings.go']
+		};
+
+		const testPackage: PackageBuildInfo = {
+			ImportPath: 'FooBar/test',
+			DirectoryPath: 'remote/pkg/mod/!foo!bar/test@v1.0.2',
+			Files: ['remote/pkg/mod/!foo!bar/test@v1.0.2/test.go']
+		};
+
+		const localPath = path.join(workspaceFolder, 'hello-world/morestrings/morestrings.go');
+		const existsSyncStub = sinon.stub(fileSystem, 'existsSync');
+		existsSyncStub.withArgs(localPath).returns(true);
+
+		remoteSourcesAndPackages.remotePackagesBuildInfo = [helloPackage, testPackage];
+
+		goDebugSession['localPathSeparator'] = '/';
+		const inferredLocalPath = goDebugSession['inferLocalPathFromRemoteGoPackage'](remotePath);
+		assert.strictEqual(inferredLocalPath, localPath);
+	});
+
+	test('inferLocalPathFromRemoteGoPackage works for package in GOPATH/pkg/mod', () => {
+		const remotePath = 'remote/pkg/mod/!foo!bar/test@v1.0.2/test.go';
+		const helloPackage: PackageBuildInfo = {
+			ImportPath: 'hello-world',
+			DirectoryPath: '/src/hello-world',
+			Files: ['src/hello-world/hello.go', 'src/hello-world/world.go']
+		};
+
+		const testPackage: PackageBuildInfo = {
+			ImportPath: 'FooBar/test',
+			DirectoryPath: 'remote/pkg/mod/!foo!bar/test@v1.0.2',
+			Files: ['remote/pkg/mod/!foo!bar/test@v1.0.2/test.go']
+		};
+
+		const localPath = path.join(process.env.GOPATH, 'pkg/mod/!foo!bar/test@v1.0.2/test.go');
+		const existsSyncStub = sinon.stub(fileSystem, 'existsSync');
+		existsSyncStub.withArgs(localPath).returns(true);
+
+		remoteSourcesAndPackages.remotePackagesBuildInfo = [helloPackage, testPackage];
+
+		goDebugSession['localPathSeparator'] = '/';
+		const inferredLocalPath = goDebugSession['inferLocalPathFromRemoteGoPackage'](remotePath);
+		assert.strictEqual(inferredLocalPath, localPath);
+	});
+
+	test('inferLocalPathFromRemoteGoPackage works for package in GOPATH/pkg/mod with relative path', () => {
+		const remotePath = '!foo!bar/test@v1.0.2/test.go';
+		const helloPackage: PackageBuildInfo = {
+			ImportPath: 'hello-world',
+			DirectoryPath: '/src/hello-world',
+			Files: ['src/hello-world/hello.go', 'src/hello-world/world.go']
+		};
+
+		const testPackage: PackageBuildInfo = {
+			ImportPath: 'FooBar/test',
+			DirectoryPath: '!foo!bar/test@v1.0.2',
+			Files: ['!foo!bar/test@v1.0.2/test.go']
+		};
+
+		const localPath = path.join(process.env.GOPATH, 'pkg/mod/!foo!bar/test@v1.0.2/test.go');
+		const existsSyncStub = sinon.stub(fileSystem, 'existsSync');
+		existsSyncStub.withArgs(localPath).returns(true);
+
+		remoteSourcesAndPackages.remotePackagesBuildInfo = [helloPackage, testPackage];
+
+		goDebugSession['localPathSeparator'] = '/';
+		const inferredLocalPath = goDebugSession['inferLocalPathFromRemoteGoPackage'](remotePath);
+		assert.strictEqual(inferredLocalPath, localPath);
+	});
+
+	test('inferLocalPathFromRemoteGoPackage works for package in GOPATH/src', () => {
+		const remotePath = 'remote/gopath/src/foobar/test@v1.0.2-abcde-34/test.go';
+		const helloPackage: PackageBuildInfo = {
+			ImportPath: 'hello-world',
+			DirectoryPath: '/src/hello-world',
+			Files: ['src/hello-world/hello.go', 'src/hello-world/world.go']
+		};
+
+		const testPackage: PackageBuildInfo = {
+			ImportPath: 'foobar/test',
+			DirectoryPath: 'remote/gopath/src/foobar/test@v1.0.2-abcde-34',
+			Files: ['remote/gopath/src/foobar/test@v1.0.2-abcde-34/test.go']
+		};
+
+		const localPath = path.join(process.env.GOPATH, 'src', 'foobar/test@v1.0.2-abcde-34/test.go');
+		const existsSyncStub = sinon.stub(fileSystem, 'existsSync');
+		existsSyncStub.withArgs(localPath).returns(true);
+
+		remoteSourcesAndPackages.remotePackagesBuildInfo = [helloPackage, testPackage];
+
+		goDebugSession['localPathSeparator'] = '/';
+		const inferredLocalPath = goDebugSession['inferLocalPathFromRemoteGoPackage'](remotePath);
+		assert.strictEqual(inferredLocalPath, localPath);
+	});
+
+	test('inferLocalPathFromRemoteGoPackage works for package in GOPATH/src with relative path', () => {
+		const remotePath = 'foobar/test@v1.0.2/test.go';
+		const helloPackage: PackageBuildInfo = {
+			ImportPath: 'hello-world',
+			DirectoryPath: '/src/hello-world',
+			Files: ['src/hello-world/hello.go', 'src/hello-world/world.go']
+		};
+
+		const testPackage: PackageBuildInfo = {
+			ImportPath: 'foobar/test',
+			DirectoryPath: 'foobar/test@v1.0.2',
+			Files: ['foobar/test@v1.0.2/test.go']
+		};
+
+		const localPath = path.join(process.env.GOPATH, 'src', 'foobar/test@v1.0.2/test.go');
+		const existsSyncStub = sinon.stub(fileSystem, 'existsSync');
+		existsSyncStub.withArgs(localPath).returns(true);
+
+		remoteSourcesAndPackages.remotePackagesBuildInfo = [helloPackage, testPackage];
+
+		goDebugSession['localPathSeparator'] = '/';
+		const inferredLocalPath = goDebugSession['inferLocalPathFromRemoteGoPackage'](remotePath);
+		assert.strictEqual(inferredLocalPath, localPath);
+	});
+
+	test('inferLocalPathFromRemoteGoPackage works for package in GOROOT/src', () => {
+		const remotePath = 'remote/goroot/src/foobar/test@v1.0.2/test.go';
+		const helloPackage: PackageBuildInfo = {
+			ImportPath: 'hello-world',
+			DirectoryPath: '/src/hello-world',
+			Files: ['src/hello-world/hello.go', 'src/hello-world/world.go']
+		};
+
+		const testPackage: PackageBuildInfo = {
+			ImportPath: 'foobar/test',
+			DirectoryPath: 'remote/goroot/src/foobar/test@v1.0.2',
+			Files: ['remote/goroot/src/foobar/test@v1.0.2/test.go']
+		};
+
+		const localPath = path.join(process.env.GOROOT, 'src', 'foobar/test@v1.0.2/test.go');
+		const existsSyncStub = sinon.stub(fileSystem, 'existsSync');
+		existsSyncStub.withArgs(localPath).returns(true);
+
+		remoteSourcesAndPackages.remotePackagesBuildInfo = [helloPackage, testPackage];
+
+		goDebugSession['localPathSeparator'] = '/';
+		const inferredLocalPath = goDebugSession['inferLocalPathFromRemoteGoPackage'](remotePath);
+		assert.strictEqual(inferredLocalPath, localPath);
+	});
+
+	test('inferLocalPathFromRemoteGoPackage works for package in GOROOT/src with relative path', () => {
+		const remotePath = 'foobar/test@v1.0.2/test.go';
+		const helloPackage: PackageBuildInfo = {
+			ImportPath: 'hello-world',
+			DirectoryPath: '/src/hello-world',
+			Files: ['src/hello-world/hello.go', 'src/hello-world/world.go']
+		};
+
+		const testPackage: PackageBuildInfo = {
+			ImportPath: 'foobar/test',
+			DirectoryPath: 'foobar/test@v1.0.2',
+			Files: ['foobar/test@v1.0.2/test.go']
+		};
+
+		const localPath = path.join(process.env.GOROOT, 'src', 'foobar/test@v1.0.2/test.go');
+		const existsSyncStub = sinon.stub(fileSystem, 'existsSync');
+		existsSyncStub.withArgs(localPath).returns(true);
+
+		remoteSourcesAndPackages.remotePackagesBuildInfo = [helloPackage, testPackage];
+
+		goDebugSession['localPathSeparator'] = '/';
+		const inferredLocalPath = goDebugSession['inferLocalPathFromRemoteGoPackage'](remotePath);
+		assert.strictEqual(inferredLocalPath, localPath);
+	});
+});
+
+suite('RemoteSourcesAndPackages Tests', () => {
+	const helloPackage: PackageBuildInfo = {
+		ImportPath: 'hello-world',
+		DirectoryPath: '/src/hello-world',
+		Files: ['src/hello-world/hello.go', 'src/hello-world/world.go']
+	};
+	const testPackage: PackageBuildInfo = {
+		ImportPath: 'test',
+		DirectoryPath: '/src/test',
+		Files: ['src/test/test.go']
+	};
+	const sources = ['src/hello-world/hello.go', 'src/hello-world/world.go', 'src/test/test.go'];
+	let remoteSourcesAndPackages: RemoteSourcesAndPackages;
+	let delve: Delve;
+	setup(() => {
+		delve = { callPromise: () => ({}), isApiV1: false } as unknown as Delve;
+		remoteSourcesAndPackages = new RemoteSourcesAndPackages();
+	});
+
+	teardown(() => {
+		sinon.restore();
+	});
+
+	test('initializeRemotePackagesAndSources retrieves remote packages and sources', async () => {
+		const stub = sinon.stub(delve, 'callPromise');
+		stub.withArgs('ListPackagesBuildInfo', [{ IncludeFiles: true }])
+			.returns(Promise.resolve({ List: [helloPackage, testPackage] }));
+		stub.withArgs('ListSources', [{}])
+			.returns(Promise.resolve({ Sources: sources }));
+
+		await remoteSourcesAndPackages.initializeRemotePackagesAndSources(delve);
+		assert.deepEqual(remoteSourcesAndPackages.remoteSourceFiles, sources);
+		assert.deepEqual(remoteSourcesAndPackages.remotePackagesBuildInfo, [helloPackage, testPackage]);
+	});
+});
diff --git a/test/integration/index.ts b/test/integration/index.ts
index 1184be9..2d964a1 100644
--- a/test/integration/index.ts
+++ b/test/integration/index.ts
@@ -6,9 +6,8 @@
 import * as Mocha from 'mocha';
 import * as path from 'path';
 export function run(): Promise<void> {
-	// Create the mocha test
 	const mocha = new Mocha({
-		ui: 'tdd'
+		ui: 'tdd',
 	});
 	mocha.useColors(true);
 
diff --git a/test/integration/install.test.ts b/test/integration/install.test.ts
new file mode 100644
index 0000000..2c663f5
--- /dev/null
+++ b/test/integration/install.test.ts
@@ -0,0 +1,159 @@
+/*---------------------------------------------------------
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------*/
+
+import AdmZip = require('adm-zip');
+import * as assert from 'assert';
+import cp = require('child_process');
+import fs = require('fs');
+import os = require('os');
+import path = require('path');
+import sinon = require('sinon');
+import util = require('util');
+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';
+
+suite('Installation Tests', function () {
+	// Disable timeout when we are running slow tests.
+	let timeout = 10000;
+	if (shouldRunSlowTests()) {
+		timeout = 0;
+	}
+	this.timeout(timeout);
+
+	let tmpToolsGopath: string;
+	let sandbox: sinon.SinonSandbox;
+	let toolsGopathStub: sinon.SinonStub;
+
+	setup(() => {
+		// Create a temporary directory in which to install tools.
+		tmpToolsGopath = fs.mkdtempSync(path.join(os.tmpdir(), 'install-test'));
+		fs.mkdirSync(path.join(tmpToolsGopath, 'bin'));
+		fs.mkdirSync(path.join(tmpToolsGopath, 'src'));
+
+		sandbox = sinon.createSandbox();
+		const utils = require('../../src/util');
+		toolsGopathStub = sandbox.stub(utils, 'getToolsGopath').returns(tmpToolsGopath);
+	});
+
+	teardown(async () => {
+		sandbox.restore();
+
+		// Clean up the temporary GOPATH. To delete the module cache, run `go clean -modcache`.
+		const goRuntimePath = getBinPath('go');
+		const envForTest = Object.assign({}, process.env);
+		envForTest['GOPATH'] = tmpToolsGopath;
+		const execFile = util.promisify(cp.execFile);
+		await execFile(goRuntimePath, ['clean', '-modcache'], {
+			env: envForTest,
+		});
+		rmdirRecursive(tmpToolsGopath);
+	});
+
+	// runTest actually executes the logic of the test.
+	// If withLocalProxy is true, the test does not require internet.
+	async function runTest(testCases: string[], withLocalProxy?: boolean) {
+		let proxyDir: string;
+		let configStub: sinon.SinonStub;
+		if (withLocalProxy) {
+			proxyDir = buildFakeProxy([].concat(...testCases));
+			const goConfig = Object.create(vscode.workspace.getConfiguration('go'), {
+				toolsEnvVars: {
+					value: {
+						GOPROXY: `file://${proxyDir}`,
+						GOSUMDB: 'off',
+					}
+				},
+			});
+			configStub = sandbox.stub(vscode.workspace, 'getConfiguration').returns(goConfig);
+		} else {
+			const env = toolInstallationEnvironment();
+			console.log(`Installing tools using GOPROXY=${env['GOPROXY']}`);
+		}
+
+		// TODO(rstambler): Test with versions as well.
+		const missingTools = testCases.map((tool) => getToolAtVersion(tool));
+		const goVersion = await getGoVersion();
+		await installTools(missingTools, goVersion);
+
+		// Confirm that each expected tool has been installed.
+		const checks: Promise<void>[] = [];
+		const exists = util.promisify(fs.exists);
+		for (const tool of testCases) {
+			checks.push(new Promise<void>(async (resolve) => {
+				// Check that the expect tool has been installed to $GOPATH/bin.
+				const ok = await exists(path.join(tmpToolsGopath, 'bin', tool));
+				if (!ok) {
+					assert.fail(`expected ${tmpToolsGopath}/bin/${tool}, not found`);
+				}
+				return resolve();
+			}));
+		}
+		await Promise.all(checks);
+
+		sandbox.assert.calledWith(toolsGopathStub);
+
+		if (withLocalProxy) {
+			sandbox.assert.calledWith(configStub);
+			rmdirRecursive(proxyDir);
+		}
+	}
+
+	test('Install one tool with a local proxy', async () => {
+		await runTest(['gopls'], true);
+	});
+
+	test('Install multiple tools with a local proxy', async () => {
+		await runTest(['gopls', 'guru'], true);
+	});
+
+	test('Install all tools via GOPROXY', async () => {
+		// Only run this test if we are in CI before a Nightly release.
+		if (!shouldRunSlowTests()) {
+			return;
+		}
+		const tools = Object.keys(allToolsInformation);
+		await runTest(tools);
+	});
+
+});
+
+// buildFakeProxy creates a fake file-based proxy used for testing. The code is
+// mostly adapted from golang.org/x/tools/internal/proxydir/proxydir.go.
+function buildFakeProxy(tools: string[]) {
+	const proxyDir = fs.mkdtempSync(path.join(os.tmpdir(), 'proxydir'));
+	for (const toolName of tools) {
+		const tool = getTool(toolName);
+		const module = tool.importPath;
+		const version = `v1.0.0`; // hardcoded for now
+		const dir = path.join(proxyDir, module, '@v');
+		fs.mkdirSync(dir, { recursive: true });
+
+		// Write the list file.
+		fs.writeFileSync(path.join(dir, 'list'), `${version}\n`);
+
+		// Write the go.mod file.
+		fs.writeFileSync(path.join(dir, `${version}.mod`), `module ${module}\n`);
+
+		// Write the info file.
+		fs.writeFileSync(path.join(dir, `${version}.info`), `{ "Version": "${version}", "Time": "2020-04-07T14:45:07Z" } `);
+
+		// Write the zip file.
+		const zip = new AdmZip();
+		const content = `package main; func main() {};`;
+		zip.addFile(path.join(`${module}@${version}`, 'main.go'), Buffer.alloc(content.length, content));
+		zip.writeZip(path.join(dir, `${version}.zip`));
+	}
+	return proxyDir;
+}
+
+// Check if VSCODEGO_BEFORE_RELEASE_TESTS is set to true. This environment
+// variable is set by the CI system that releases the Nightly extension,
+// allowing us to opt-in to more rigorous testing only before releases.
+function shouldRunSlowTests(): boolean {
+	return !!process.env['VSCODEGO_BEFORE_RELEASE_TESTS'];
+}
diff --git a/test/integration/statusbar.test.ts b/test/integration/statusbar.test.ts
new file mode 100644
index 0000000..f55d34e
--- /dev/null
+++ b/test/integration/statusbar.test.ts
@@ -0,0 +1,35 @@
+/*---------------------------------------------------------
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ * Modification copyright 2020 The Go Authors. All rights reserved.
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
+ *--------------------------------------------------------*/
+
+import * as assert from 'assert';
+import { describe, it } from 'mocha';
+
+import { disposeGoStatusBar, formatGoVersion, getGoEnvironmentStatusbarItem, initGoStatusBar } from '../../src/goEnvironmentStatus';
+import { getGoVersion } from '../../src/util';
+
+describe('#initGoStatusBar()', function () {
+	this.beforeAll(() => {
+		initGoStatusBar();
+	});
+
+	this.afterAll(() => {
+		disposeGoStatusBar();
+	});
+
+	it('should create a status bar item', () => {
+		assert.notEqual(getGoEnvironmentStatusbarItem(), undefined);
+	});
+
+	it('should create a status bar item with a label matching go.goroot version', async () =>  {
+		const version = await getGoVersion();
+		const versionLabel = formatGoVersion(version.format());
+		assert.equal(
+			getGoEnvironmentStatusbarItem().text,
+			versionLabel,
+			'goroot version does not match status bar item text'
+		);
+	});
+});
diff --git a/test/runTest.ts b/test/runTest.ts
index ee29d48..d7656e6 100644
--- a/test/runTest.ts
+++ b/test/runTest.ts
@@ -1,23 +1,29 @@
 import * as fs from 'fs-extra';
 import * as path from 'path';
 import { runTests } from 'vscode-test';
-import { extensionId } from '../src/telemetry';
+import { extensionId } from '../src/const';
 
 async function main() {
 		// The folder containing the Extension Manifest package.json
 		// Passed to `--extensionDevelopmentPath`
 	const extensionDevelopmentPath = path.resolve(__dirname, '../../');
 
+	let failed = false;
+
 	try {
 		// The path to the extension test script
 		// Passed to --extensionTestsPath
 		const extensionTestsPath = path.resolve(__dirname, './integration/index');
 
 		// Download VS Code, unzip it and run the integration test
-		await runTests({ extensionDevelopmentPath, extensionTestsPath });
+		await runTests({
+			extensionDevelopmentPath,
+			extensionTestsPath,
+			launchArgs: ['--disable-extensions'],  // disable all other extensions
+		});
 	} catch (err) {
 		console.error('Failed to run integration tests' + err);
-		process.exit(1);
+		failed = true;
 	}
 
 	// Integration tests using gopls.
@@ -40,6 +46,11 @@
 		});
 	} catch (err) {
 		console.error('Failed to run gopls tests' + err);
+		failed = true;
+	}
+
+	if (failed) {
+		process.exit(1);
 	}
 }
 
diff --git a/third_party/README.md b/third_party/README.md
new file mode 100644
index 0000000..07a562b
--- /dev/null
+++ b/third_party/README.md
@@ -0,0 +1,26 @@
+# Vendored dependencies
+
+third_party directory contains code from the third party including
+vendored modules that need local modifications (e.g. bug fixes or
+necessary enhancement before they are incorporated and released
+in the upstream). Every directory must contain LICENSE files.
+
+The vendored node modules still need to be specified in the dependencies.
+For example, after copying the `tree-kill` module to this directory
+and applying necessary local modification, run from the root of this
+project directory:
+
+```
+$ npm install --save ./third_party/tree-kill
+
+```
+
+This will update `package.json` and `package-lock.json` to point to
+the local dependency.
+
+Note: We didn't test vendoring platform-dependent modules yet.
+
+
+## List of local modification
+
+`tree-kill`: vendored 1.2.2 with a fix for https://github.com/golang/vscode-go/issues/90 
diff --git a/third_party/tree-kill/LICENSE b/third_party/tree-kill/LICENSE
new file mode 100644
index 0000000..aa86c2d
--- /dev/null
+++ b/third_party/tree-kill/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 Peter Krumins
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/third_party/tree-kill/README.md b/third_party/tree-kill/README.md
new file mode 100644
index 0000000..59a00ea
--- /dev/null
+++ b/third_party/tree-kill/README.md
@@ -0,0 +1,89 @@
+Tree Kill
+=========
+
+Kill all processes in the process tree, including the root process.
+
+Examples
+=======
+
+Kill all the descendent processes of the process with pid `1`, including the process with pid `1` itself:
+```js
+var kill = require('tree-kill');
+kill(1);
+```
+
+Send a signal other than SIGTERM.:
+```js
+var kill = require('tree-kill');
+kill(1, 'SIGKILL');
+```
+
+Run a callback when done killing the processes. Passes an error argument if there was an error.
+```js
+var kill = require('tree-kill');
+kill(1, 'SIGKILL', function(err) {
+    // Do things
+});
+```
+
+You can also install tree-kill globally and use it as a command:
+```sh
+tree-kill 1          # sends SIGTERM to process 1 and its descendents
+tree-kill 1 SIGTERM  # same
+tree-kill 1 SIGKILL  # sends KILL instead of TERMINATE
+```
+
+Methods
+=======
+
+## require('tree-kill')(pid, [signal], [callback]);
+
+Sends signal `signal` to all children processes of the process with pid `pid`, including `pid`. Signal defaults to `SIGTERM`.
+
+For Linux, this uses `ps -o pid --no-headers --ppid PID` to find the parent pids of `PID`.
+
+For Darwin/OSX, this uses `pgrep -P PID` to find the parent pids of `PID`.
+
+For Windows, this uses `'taskkill /pid PID /T /F'` to kill the process tree. Note that on Windows, sending the different kinds of POSIX signals is not possible.
+
+Install
+=======
+
+With [npm](https://npmjs.org) do:
+
+```
+npm install tree-kill
+```
+
+License
+=======
+
+MIT
+
+Changelog
+=========
+
+
+## [1.2.2] - 2019-12-11
+### Changed
+- security fix: sanitize `pid` parameter to fix arbitrary code execution vulnerability
+
+## [1.2.1] - 2018-11-05
+### Changed
+- added missing LICENSE file
+- updated TypeScript definitions
+
+## [1.2.0] - 2017-09-19
+### Added
+- TypeScript definitions
+### Changed
+- `kill(pid, callback)` works. Before you had to use `kill(pid, signal, callback)`
+
+## [1.1.0] - 2016-05-13
+### Added
+- A `tree-kill` CLI
+
+## [1.0.0] - 2015-09-17
+### Added
+- optional callback
+- Darwin support
diff --git a/third_party/tree-kill/cli.js b/third_party/tree-kill/cli.js
new file mode 100755
index 0000000..1acb815
--- /dev/null
+++ b/third_party/tree-kill/cli.js
@@ -0,0 +1,14 @@
+#!/usr/bin/env node
+kill = require('.')
+try {
+  kill(process.argv[2], process.argv[3], function(err){
+    if (err) {
+      console.log(err.message)
+      process.exit(1)
+    }
+  })
+}
+catch (err) {
+  console.log(err.message)
+  process.exit(1)
+}
diff --git a/third_party/tree-kill/index.d.ts b/third_party/tree-kill/index.d.ts
new file mode 100644
index 0000000..e0b1302
--- /dev/null
+++ b/third_party/tree-kill/index.d.ts
@@ -0,0 +1,13 @@
+/**
+ * Kills process identified by `pid` and all its children
+ *
+ * @param pid
+ * @param signal 'SIGTERM' by default
+ * @param callback
+ */
+declare function treeKill(pid: number, callback?: (error?: Error) => void): void;
+declare function treeKill(pid: number, signal?: string | number, callback?: (error?: Error) => void): void;
+
+declare namespace treeKill {}
+
+export = treeKill;
diff --git a/third_party/tree-kill/index.js b/third_party/tree-kill/index.js
new file mode 100755
index 0000000..9499a89
--- /dev/null
+++ b/third_party/tree-kill/index.js
@@ -0,0 +1,136 @@
+'use strict';
+
+var childProcess = require('child_process');
+const { existsSync } = require('fs');
+var spawn = childProcess.spawn;
+var exec = childProcess.exec;
+
+module.exports = function (pid, signal, callback) {
+    if (typeof signal === 'function' && callback === undefined) {
+        callback = signal;
+        signal = undefined;
+    }
+
+    pid = parseInt(pid);
+    if (Number.isNaN(pid)) {
+        if (callback) {
+            return callback(new Error("pid must be a number"));
+        } else {
+            throw new Error("pid must be a number");
+        }
+    }
+
+    var tree = {};
+    var pidsToProcess = {};
+    tree[pid] = [];
+    pidsToProcess[pid] = 1;
+
+    switch (process.platform) {
+    case 'win32':
+        exec('taskkill /pid ' + pid + ' /T /F', callback);
+        break;
+    case 'darwin':
+        buildProcessTree(pid, tree, pidsToProcess, function (parentPid) {
+            return spawn(pathToPgrep(), ['-P', parentPid]);
+        }, function () {
+            killAll(tree, signal, callback);
+        });
+        break;
+    // case 'sunos':
+    //     buildProcessTreeSunOS(pid, tree, pidsToProcess, function () {
+    //         killAll(tree, signal, callback);
+    //     });
+    //     break;
+    default: // Linux
+        buildProcessTree(pid, tree, pidsToProcess, function (parentPid) {
+          return spawn('ps', ['-o', 'pid', '--no-headers', '--ppid', parentPid]);
+        }, function () {
+            killAll(tree, signal, callback);
+        });
+        break;
+    }
+};
+
+function killAll (tree, signal, callback) {
+    var killed = {};
+    try {
+        Object.keys(tree).forEach(function (pid) {
+            tree[pid].forEach(function (pidpid) {
+                if (!killed[pidpid]) {
+                    killPid(pidpid, signal);
+                    killed[pidpid] = 1;
+                }
+            });
+            if (!killed[pid]) {
+                killPid(pid, signal);
+                killed[pid] = 1;
+            }
+        });
+    } catch (err) {
+        if (callback) {
+            return callback(err);
+        } else {
+            throw err;
+        }
+    }
+    if (callback) {
+        return callback();
+    }
+}
+
+function killPid(pid, signal) {
+    try {
+        process.kill(parseInt(pid, 10), signal);
+    }
+    catch (err) {
+        if (err.code !== 'ESRCH') throw err;
+    }
+}
+
+function buildProcessTree (parentPid, tree, pidsToProcess, spawnChildProcessesList, cb) {
+    var ps = spawnChildProcessesList(parentPid);
+    var allData = '';
+    ps.stdout.on('data', function (data) {
+        var data = data.toString('ascii');
+        allData += data;
+    });
+
+    var onClose = function (code) {
+        delete pidsToProcess[parentPid];
+
+        if (code != 0) {
+            // no more parent processes
+            if (Object.keys(pidsToProcess).length == 0) {
+                cb();
+            }
+            return;
+        }
+
+        allData.match(/\d+/g).forEach(function (pid) {
+          pid = parseInt(pid, 10);
+          tree[parentPid].push(pid);
+          tree[pid] = [];
+          pidsToProcess[pid] = 1;
+          buildProcessTree(pid, tree, pidsToProcess, spawnChildProcessesList, cb);
+        });
+    };
+
+    ps.on('close', onClose);
+}
+
+var pgrep = '';
+function pathToPgrep () {
+    if (pgrep) {
+        return pgrep;
+    }
+    // Use the default pgrep, available since os x mountain lion.
+    // proctools' pgrep does not implement `-P` correctly and returns
+    // unrelated processes.
+    // https://github.com/golang/vscode-go/issues/90#issuecomment-634430428
+    try {
+        pgrep = existsSync('/usr/bin/pgrep') ? '/usr/bin/pgrep' : 'pgrep';
+    } catch (e) {
+        pgrep = 'pgrep';
+    }
+    return pgrep;
+}
\ No newline at end of file
diff --git a/third_party/tree-kill/package.json b/third_party/tree-kill/package.json
new file mode 100644
index 0000000..68f0c49
--- /dev/null
+++ b/third_party/tree-kill/package.json
@@ -0,0 +1,82 @@
+{
+  "_args": [
+    [
+      "tree-kill@1.2.2",
+      "/Users/hakim/projects/google/vscode-go"
+    ]
+  ],
+  "_from": "tree-kill@1.2.2",
+  "_id": "tree-kill@1.2.2",
+  "_inBundle": false,
+  "_integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
+  "_location": "/tree-kill",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "version",
+    "registry": true,
+    "raw": "tree-kill@1.2.2",
+    "name": "tree-kill",
+    "escapedName": "tree-kill",
+    "rawSpec": "1.2.2",
+    "saveSpec": null,
+    "fetchSpec": "1.2.2"
+  },
+  "_requiredBy": [
+    "/"
+  ],
+  "_resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+  "_spec": "1.2.2",
+  "_where": "/Users/hakim/projects/google/vscode-go",
+  "author": {
+    "name": "Peteris Krumins",
+    "email": "peteris.krumins@gmail.com",
+    "url": "http://www.catonmat.net"
+  },
+  "bin": {
+    "tree-kill": "cli.js"
+  },
+  "bugs": {
+    "url": "https://github.com/pkrumins/node-tree-kill/issues"
+  },
+  "contributors": [
+    {
+      "name": "Todd Wolfson",
+      "email": "todd@twolfson.com",
+      "url": "http://twolfson.com/"
+    },
+    {
+      "name": "William Hilton",
+      "email": "wmhilton@gmail.com",
+      "url": "http://wmhilton.com/"
+    },
+    {
+      "name": "Fabrício Matté",
+      "url": "http://ultcombo.js.org/"
+    }
+  ],
+  "description": "kill trees of processes",
+  "devDependencies": {
+    "mocha": "^2.2.5"
+  },
+  "homepage": "https://github.com/pkrumins/node-tree-kill",
+  "keywords": [
+    "tree",
+    "trees",
+    "process",
+    "processes",
+    "kill",
+    "signal"
+  ],
+  "license": "MIT",
+  "main": "index.js",
+  "name": "tree-kill",
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/pkrumins/node-tree-kill.git"
+  },
+  "scripts": {
+    "test": "mocha"
+  },
+  "types": "index.d.ts",
+  "version": "1.2.2"
+}
diff --git a/thirdpartynotices.txt b/thirdpartynotices.txt
deleted file mode 100644
index d00316e..0000000
--- a/thirdpartynotices.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-THIRD-PARTY SOFTWARE NOTICES AND INFORMATION
-For Microsoft vscode-go
- 
-This project incorporates material from the projects listed below.  The original copyright notice 
-and the license under which Microsoft received such material are set out below. Microsoft reserves 
-all other rights not expressly granted, whether by implication, estoppel or otherwise.
-
-
-1. json-rpc2 version 1.0.2 (https://github.com/pocesar/node-jsonrpc2)
-
-The MIT License
-
-       Copyright (C) 2009 Eric Florenzano <eflorenzano.com/aboutme/> and
-                          Ryan Tomayko <tomayko.com/about>
- 
-Permission  is  hereby granted, free of charge, to any person ob-
-taining a copy of  this  software  and  associated  documentation
-files  (the "Software"), to deal in the Software without restric-
-tion, including without limitation the rights to use, copy, modi-
-fy, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is  fur-
-nished to do so, subject to the following conditions:
- 
-The  above  copyright  notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
- 
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF  ANY  KIND,
-EXPRESS  OR  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE  AND  NONIN-
-FRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER  IN  AN
-ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN  THE
-SOFTWARE.
-
diff --git a/tools/generate.go b/tools/generate.go
index 6dc6745..f93ce88 100644
--- a/tools/generate.go
+++ b/tools/generate.go
@@ -8,12 +8,18 @@
 import (
 	"bytes"
 	"encoding/json"
+	"flag"
 	"fmt"
 	"io/ioutil"
 	"log"
 	"os"
 	"path/filepath"
 	"sort"
+	"strings"
+)
+
+var (
+	writeFlag = flag.Bool("w", true, "Write new file contents to disk.")
 )
 
 type PackageJSON struct {
@@ -40,14 +46,13 @@
 }
 
 func main() {
+	flag.Parse()
+
 	// Assume this is running from the vscode-go directory.
 	dir, err := os.Getwd()
 	if err != nil {
 		log.Fatal(err)
 	}
-	if filepath.Base(dir) != "vscode-go" {
-		log.Fatalf("run this script from the vscode-go root directory")
-	}
 	// Find the package.json file.
 	data, err := ioutil.ReadFile(filepath.Join(dir, "package.json"))
 	if err != nil {
@@ -58,24 +63,39 @@
 		log.Fatal(err)
 	}
 	rewrite := func(filename string, toAdd []byte) {
-		content, err := ioutil.ReadFile(filename)
+		oldContent, err := ioutil.ReadFile(filename)
 		if err != nil {
 			log.Fatal(err)
 		}
 		gen := []byte(`<!-- Everything below this line is generated. DO NOT EDIT. -->`)
-		split := bytes.Split(content, gen)
+		split := bytes.Split(oldContent, gen)
 		if len(split) == 1 {
-			log.Fatalf("expected to find %q in %s, not found", filename, gen)
+			log.Fatalf("expected to find %q in %s, not found", gen, filename)
 		}
 		s := bytes.Join([][]byte{
 			bytes.TrimSpace(split[0]),
 			gen,
 			toAdd,
 		}, []byte("\n\n"))
-		if err := ioutil.WriteFile(filename, append(s, '\n'), 0644); err != nil {
-			log.Fatal(err)
+		newContent := append(s, '\n')
+
+		// Return early if the contents are unchanged.
+		if bytes.Equal(oldContent, newContent) {
+			return
 		}
-		fmt.Printf("regenerated %s\n", filename)
+
+		// Either write out new contents or report an error (if in CI).
+		if *writeFlag {
+			if err := ioutil.WriteFile(filename, newContent, 0644); err != nil {
+				log.Fatal(err)
+			}
+			fmt.Printf("updated %s\n", filename)
+		} else {
+			base := filepath.Join("docs", filepath.Base(filename))
+			fmt.Printf(`%s have changed in the package.json, but documentation in %s was not updated.
+`, strings.TrimSuffix(base, ".md"), base)
+			os.Exit(1)
+		}
 	}
 	var b bytes.Buffer
 	for i, c := range pkgJSON.Contributes.Commands {
diff --git a/tools/license.sh b/tools/license.sh
new file mode 100755
index 0000000..9647e1f
--- /dev/null
+++ b/tools/license.sh
@@ -0,0 +1,70 @@
+#!/bin/bash
+
+# Copyright 2020 The Go Authors. All rights reserved.
+# Licensed under the MIT License. See LICENSE in the project root for license information.
+
+set -euo pipefail
+
+root_dir() {
+  local script_name="${0}"
+  local script_dir=$(dirname "${script_name}")
+  local parent_dir=$(cd "${script_dir}/.." && pwd)
+  echo "${parent_dir}"
+}
+
+ROOT="$(root_dir)"
+cd "${ROOT}"  # always run from the root directory.
+
+WORKTREE="$(mktemp -d)"
+BRANCH="license-gen-$(date +%Y%m%d%H%M%S)"
+
+git fetch
+git worktree add --track -b "${BRANCH}" "${WORKTREE}" origin/master
+
+cd "${WORKTREE}"
+export GIT_GOFMT_HOOK=off
+
+YARN="${ROOT}/node_modules/.bin/yarn"
+
+ALL_LICENSES=$(
+  $YARN licenses list --json --no-progress 2>/dev/null|
+  jq 'select(.type == "table") | .data.body | map( {name: .[0], version: .[1], license: .[2], url: .[3], verndor: .[4], vendorName: .[5]} )')
+
+NG=$(echo "${ALL_LICENSES}" | jq '
+{
+  "Apache-2.0": 1,
+  "BSD-2-Clause": 1,
+  "BSD-3-Clause": 1,
+  "ISC": 1,
+  "MIT": 1,
+  "Unlicense": 1,
+  "0BSD": 1,
+  "(Unlicense OR Apache-2.0)": 1,
+} as $allowed_licenses |
+{
+  "json-schema@0.2.3": 1,
+} as $allow_list |
+.[] | select(.license | in($allowed_licenses) | not)
+| select((.name+"@"+.version) | in($allow_list) | not) ')
+
+if [ -z "${NG}" ];
+then
+  echo "PASSED license check"
+else
+  echo "FAILED license check. The following dependencies need manual check: ${NG}" &&
+  echo "WORKTREE=${WORKTREE}" &&
+  exit 1
+fi
+
+LICENSEFILE="LICENSE.prod"
+cat LICENSE > "${LICENSEFILE}"
+printf "\n\n" >> "${LICENSEFILE}"
+"${YARN}" licenses generate-disclaimer --prod >> "${LICENSEFILE}"
+
+if [[ -f thirdpartynotices.txt ]]
+then
+  printf "\n" >> "${LICENSEFILE}"
+  cat thirdpartynotices.txt >> "${LICENSEFILE}"
+fi
+
+cd - && mv "${WORKTREE}/${LICENSEFILE}" . && git worktree remove "${WORKTREE}" -f
diff --git a/tsconfig.json b/tsconfig.json
index 4960e62..6045bef 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -17,6 +17,7 @@
 		//"strictPropertyInitialization": true,
 	},
 	"exclude": [
-		"node_modules"
+		"node_modules",
+		"third_party"
 	]
 }
\ No newline at end of file
diff --git a/typings/json-rpc2.d.ts b/typings/json-rpc2.d.ts
index aa02e73..3bee3d0 100644
--- a/typings/json-rpc2.d.ts
+++ b/typings/json-rpc2.d.ts
@@ -6,5 +6,6 @@
 	export class Client {
 		static $create(port: number, addr: string): Client;
 		connectSocket(callback: (err: Error, conn: RPCConnection) => void): void;
+		on(handler: 'error', callback: (err: Error) => void): void;
 	}
 }
\ No newline at end of file