sync: merge microsoft/vscode-go@a66e3b5 into master

Change-Id: Ie13560096a7ba7661bfde398cd161bbf7da1acc3
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index de792dc..4d6fb1b 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -1,2 +1,48 @@
-We are moving to a new home! See https://github.com/microsoft/vscode-go/issues/3247.
-Please file your issue in https://github.com/golang/vscode-go instead.
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+Please direct general questions to:
+- [`#vscode` channel in Gophers Slack](https://invite.slack.golangbridge.org/messages/vscode)
+
+Please review the [documentation](https://github.com/golang/vscode-go/tree/master/docs) before filing an issue.
+Helpful pages include:
+
+- [GOPATH](https://github.com/golang/vscode-go/tree/master/docs/gopath.md)
+- [Module Support](https://github.com/golang/vscode-go/tree/master/docs/modules.md)
+- [Debugging](https://github.com/golang/vscode-go/tree/master/docs/debugging.md)
+  - Set "trace": "log" and share the resulting logs in the debug console when logging an issue.
+
+Please answer these questions before submitting your issue. Thanks!
+
+### What version of Go, VS Code & VS Code Go extension are you using?
+- Run `go version` to get version of Go
+	- <Paste go version here>
+- Run `code -v` or `code-insiders -v` to get version of VS Code or VS Code Insiders
+	- <Paste VS Code version here>
+- Check your installed extensions to get the version of the VS Code Go extension 
+	- <Paste Go extension version here>
+- Run `go env` to get the go development environment details
+	- <Paste the output here>
+
+### Share the Go related settings you have added/edited
+
+Run `Preferences: Open Settings (JSON)` command to open your settings.json file.
+Share all the settings with the `go.` or `["go"]` or `gopls` prefixes.
+
+### Describe the bug
+A clear and concise description of what the bug.
+A clear and concise description of what you expected to happen.
+
+### Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. See error
+
+### Screenshots or recordings
+If applicable, add screenshots or recordings to help explain your problem.
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index f667801..d2cd362 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -2,7 +2,4 @@
 contact_links:
   - name: Question
     url: https://invite.slack.golangbridge.org/
-    about: Ask and answer questions in the #vscode Gopher slack channel
-  - name: Feature Request
-    url: https://github.com/golang/vscode-go/issues/new?template=feature_request.md
-    about: Suggest ideas for improvement in the new repo
\ No newline at end of file
+    about: Ask and answer questions on the `#vscode` channel in Gophers Slacks.
\ No newline at end of file
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/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..05009b8
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,93 @@
+name: build
+
+on: [push, pull_request]
+
+jobs:
+  build:
+    name: ${{ matrix.os }} ${{ matrix.version }}
+    runs-on: ${{ matrix.os }}
+    
+    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 install
+      
+      - 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: ${{ matrix.version }}
+
+  eslint:
+    runs-on: ubuntu-latest
+    if: "!contains(github.event.head_commit.message, 'SKIP CI')"
+
+    steps:
+      - name: Clone repository
+        uses: actions/checkout@v1
+
+      - name: Setup Node
+        uses: actions/setup-node@v1
+        with:
+          node-version: '10.x'
+
+      - name: Install Dependencies
+        run: 'npm install --frozen-lockfile'
+        shell: bash
+
+      - name: Lint check
+        run: npm run lint
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..eff44f1
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,84 @@
+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]
+
+env:
+  GOPATH: /tmp/go
+  # Because some tests require explicit setting of GOPATH. TODO: FIX THEM.
+
+jobs:
+  release:
+    name: Release Nightly
+    runs-on: ubuntu-latest
+    timeout-minutes: 20
+
+    steps:
+      - name: Clone repository
+        uses: actions/checkout@v1
+
+      - 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 install
+
+      - 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
+        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: Prepare Release
+        run: build/all.bash prepare_nightly
+
+      - 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'
+
+      - 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/.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/.vscodeignore b/.vscodeignore
index 5f7ffd2..9d45b80 100644
--- a/.vscodeignore
+++ b/.vscodeignore
@@ -1,11 +1,13 @@
 src/**/*
+test/
 typings/**/*
 .vscode/**/*
 tsconfig.json
 .gitignore
 node_modules/fs-extra
-test/
 **/*.map
 **/tslint.json
 build/**/*
 docs/
+*.md.nightly
+.github/**/*
diff --git a/CHANGELOG.md.nightly b/CHANGELOG.md.nightly
new file mode 100644
index 0000000..f93a946
--- /dev/null
+++ b/CHANGELOG.md.nightly
@@ -0,0 +1,4 @@
+## 2020.3.x
+* Set the extension name for VS Code Go Nightly(`go-nightly`).
+* Pick up the pre-release version of `gopls` if available.
+* Disabled the telemetry report for VS Code Go.
diff --git a/README.md b/README.md
index e4cd7a8..2441386 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,14 @@
 # Go for Visual Studio Code
 
-[![Slack](https://img.shields.io/badge/slack-gophers-green.svg?style=flat)](https://gophers.slack.com/messages/vscode/) [![Build Status](https://travis-ci.org/Microsoft/vscode-go.svg?branch=master)](https://travis-ci.org/Microsoft/vscode-go)
-
-> Read the [We are moving 🏡 🚚 🏡](#we-are-moving---) section to learn about the new home for this extension and subscribe to [Microsoft/vscode-go#3247](https://github.com/microsoft/vscode-go/issues/3247) for updates.
+[![Slack](https://img.shields.io/badge/slack-gophers-green.svg?style=flat)](https://gophers.slack.com/messages/vscode/)
 
 This extension adds rich language support for the [Go language](https://golang.org/) to VS Code.
 
 See the [Changelog](https://github.com/Microsoft/vscode-go/blob/master/CHANGELOG.md) to know what has changed over the last few versions of this extension.
 
-## Table of Contents
+> This is the future home for the VS Code Go extension and migration is in progress. Read [the announcement in the old repo](https://github.com/microsoft/vscode-go/blob/master/README.md#we-are-moving---) section to learn about the transition and subscribe to [Microsoft/vscode-go#3247](https://github.com/microsoft/vscode-go/issues/3247) for updates.
 
-[**We are moving!**](#we-are-moving---)
+## Table of Contents
 
 - [Language Features](#language-features)
 	- [IntelliSense](#intellisense)
@@ -36,27 +34,6 @@
 - [Code of Conduct](#code-of-conduct)
 - [License](#license)
 
-## We are moving! 🏡 🚚 🏡 
-
-Our new home will be https://github.com/golang/vscode-go.
-
-As the extension grows in features and popularity, it requires more maintenance and support.
-Since the introduction of Go modules, we have been collaborating with the Go team, particularly on support
-for `gopls`, the [Go language server](https://golang.org/s/gopls/README.md). More exciting work is on the horizon with the
-[debug adapter service](https://github.com/go-delve/delve/tree/master/service/dap) in delve which will help
-improve the debugging experience.
-
-Moving to the [golang GitHub organization](https://github.com/golang) brings us closer to the home of other tools
-and libraries that the Go extension depends on. We hope this will help keep the extension tightly coupled with the Go 
-language and encourage more Gophers to get involved.
-
-As of May 2020, this repository is effectively frozen. We are currently focused on migration tasks, such as resolving
-and transferring open issues and PRs and updating documentation. Critical bug fixes and PRs related
-to migration will still be accepted. You can open new feature requests, bug reports, and PRs in 
-the new repository: https://github.com/golang/vscode-go.
-
-For questions and discussion, please join us in the [`#vscode-dev`](https://gophers.slack.com/messages/vscode-dev) Gophers Slack channel.
-
 ## Language Features
 
 ### IntelliSense
@@ -112,11 +89,11 @@
 
 Install and open [Visual Studio Code](https://code.visualstudio.com). Press `Ctrl+Shift+X` or `Cmd+Shift+X` to open the Extensions pane. Find and install the Go extension. You can also install the extension from the [Marketplace](https://marketplace.visualstudio.com/items?itemName=ms-vscode.Go). Open any `.go` file in VS Code. The extension is now activated.
 
-This extension uses a set of Go tools to provide the various rich features. These tools are installed in your GOPATH by default. If you wish to have these tools in a separate location, provide the desired location in the setting `go.toolsGopath`. Read more about this and the tools at [Go tools that the Go extension depends on](https://github.com/microsoft/vscode-go/blob/master/docs/Go-tools-that-the-Go-extension-depends-on.md).
+This extension uses a set of Go tools to provide the various rich features. These tools are installed in your GOPATH by default. If you wish to have these tools in a separate location, provide the desired location in the setting `go.toolsGopath`. Read more about this and the tools at [Go tools that the Go extension depends on](docs/tools.md).
 
 You will see `Analysis Tools Missing` in the bottom right, clicking this will offer to install all of the dependent Go tools. You can also run the [command](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette) `Go: Install/Update tools` to install/update the same. You need to have git installed for these tool installations to work.
 
-**Note 1**: Read [GOPATH in the VS Code Go extension](https://github.com/Microsoft/vscode-go/blob/master/docs/GOPATH-in-the-VS-Code-Go-extension.md) to learn about the different ways you can get the extension to set GOPATH.
+**Note 1**: Read [GOPATH in the VS Code Go extension](docs/gopath.md) to learn about the different ways you can get the extension to set GOPATH.
 
 **Note 2**: The `Format on save` feature has a timeout of 750ms after which the formatting is aborted. You can change this timeout using the setting `editor.formatOnSaveTimeout`. This feature gets disabled when you have enabled the `Auto Save` feature in Visual Studio Code.
 
@@ -124,12 +101,11 @@
 
 ### Customizing the Go extension features
 
-The Go extension is ready to use on the get go. If you want to customize the features, you can edit the settings in your User or Workspace settings. Read [All Settings & Commands in Visual Studio Code Go extension](https://github.com/Microsoft/vscode-go/blob/master/docs/All-Settings-&-Commands-in-Visual-Studio-Code-Go-extension.md) for the full list of options and their descriptions.
-
+The Go extension is ready to use on the get go. If you want to customize the features, you can edit the settings in your User or Workspace settings. Read [All Settings & Commands in Visual Studio Code Go extension](docs/commands.md) for the full list of options and their descriptions.
 
 ### Go Language Server
 
-The Go extension uses a host of [Go tools](https://github.com/Microsoft/vscode-go/blob/master/docs/Go-tools-that-the-Go-extension-depends-on.md) to provide the various language features. An alternative is to use a single language server that provides the same features using the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/). 
+The Go extension uses a host of [Go tools](docs/tools.md) to provide the various language features. An alternative is to use a single language server that provides the same features using the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/). 
 
 [`gopls`](https://golang.org/s/gopls/README.md) from Google is the official language server for
 the Go language and is currently in active development. You can choose to use this by setting
@@ -233,7 +209,7 @@
 
 ### _Optional_: Debugging
 
-To use the debugger, you must currently manually [install delve](https://github.com/derekparker/delve/tree/master/Documentation/installation). For more read [Debugging Go Code Using VS Code](https://github.com/microsoft/vscode-go/blob/master/docs/Debugging-Go-code-using-VS-Code.md).
+To use the debugger, you must currently manually [install delve](https://github.com/derekparker/delve/tree/master/Documentation/installation). For more read [Debugging Go Code Using VS Code](docs/debugging.md).
 
 #### Debugging in WSL
 
@@ -241,24 +217,24 @@
 
 #### Remote Debugging
 
-To remote debug using VS Code, read [Remote Debugging](https://github.com/microsoft/vscode-go/blob/master/docs/Debugging-Go-code-using-VS-Code.md#remote-debugging).
+To remote debug using VS Code, read [Remote Debugging](docs/debugging.md#remote-debugging).
 
 ## Install or update all dependencies
 
-To quickly get all dependencies installed (or updated) see the [Go Tools wiki page](https://github.com/microsoft/vscode-go/blob/master/docs/Go-tools-that-the-Go-extension-depends-on.md).
+To quickly get all dependencies installed (or updated) see the [Go Tools wiki page](docs/tools.md).
 
 ## Building and Debugging the Extension
 
 You can set up a development environment for debugging the extension during extension development.
-Read more at [Building, Debugging and Sideloading the extension in Visual Studio Code](https://github.com/microsoft/vscode-go/blob/master/docs/Building,-Debugging-and-Sideloading-the-extension-in-Visual-Studio-Code.md).
+Read more at [Building, Debugging and Sideloading the extension in Visual Studio Code](docs/contributing.md).
 
 ## Tools this extension depends on
 
-This extension uses a host of Go tools to provide the various rich features. These tools are installed in your GOPATH by default. If you wish to have the extension use a separate GOPATH for its tools, provide the desired location in the setting `go.toolsGopath`. Read more about this and the tools at [Go tools that the Go extension depends on](https://github.com/microsoft/vscode-go/blob/master/docs/Go-tools-that-the-Go-extension-depends-on.md).
+This extension uses a host of Go tools to provide the various rich features. These tools are installed in your GOPATH by default. If you wish to have the extension use a separate GOPATH for its tools, provide the desired location in the setting `go.toolsGopath`. Read more about this and the tools at [Go tools that the Go extension depends on](docs/tools.md).
 
 ## Frequently Asked Questions (FAQ)
 
-Please see our wiki on [Frequently Asked Questions](https://github.com/microsoft/vscode-go/blob/master/docs/Go-with-VS-Code-FAQ-and-Troubleshooting.md) to get answers to your questions or get started with troubleshooting.
+Please see our wiki on [Frequently Asked Questions](docs/troubleshooting.md) to get answers to your questions or get started with troubleshooting.
 
 ## Contributing
 
@@ -272,4 +248,5 @@
 contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
 
 ## License
+
 [MIT](LICENSE)
diff --git a/README.md.nightly b/README.md.nightly
new file mode 100644
index 0000000..0064e7e
--- /dev/null
+++ b/README.md.nightly
@@ -0,0 +1,27 @@
+# Go Nightly for VS Code
+
+> ### **ATTENTION**
+>**Go Nightly for VS Code** is the insider version of
+[VS Code Go extension](https://github.com/microsoft/vscode-go)
+for early feedback and testing. This extension works best with
+[VS Code Insiders](https://code.visualstudio.com/insiders).
+Go Nightly contains previews of new features and bug fixes that are still
+under review or testing, so can be unstable. If you are looking for the stable version,
+please use [the stable version](https://marketplace.visualstudio.com/items?itemName=ms-vscode.go) instead.
+>
+> **NOTE:**
+If you have both stable (aka "Go") and nightly version (aka "Go Nightly") installed,
+you MUST DISABLE one of them. Docs on how to disable an extension can be found
+[here](https://code.visualstudio.com/docs/editor/extension-gallery#_disable-an-extension).
+
+> ### Difference between VS Code Go and VS Code Go Nightly
+> - Go Nightly is maintained and released by Go Tools team at Google.
+> - Go Nightly is released more frequently than the stable version.
+> - Go Nightly includes features and bug fixes that are still under testing or not finalized yet.
+> - Go Nightly may use the latest pre-release versions of tools (e.g. `gopls`) instead of release versions.
+> - For now, Go and Go Nightly maintain separate repositories. Both repositories
+>   welcome all contributors. For contribution to Go Nightly repo, see the Go
+>   project's [contribution guide](https://golang.org/doc/contribute.html).
+>   Go team members who has signed the Microsoft CLA will send a syncing PR upstream to
+>   https://github.com/microsoft/vscode-go every two weeks.
+> - [Here](https://github.com/microsoft/vscode-go/compare/master...golang:master) is the full list of local modifications.
diff --git a/build/README.md b/build/README.md
new file mode 100644
index 0000000..cacccb2
--- /dev/null
+++ b/build/README.md
@@ -0,0 +1,88 @@
+## Continuous Integration Testing
+
+Currently we are using two separate CI systems to test all changes and pushed commits:
+Tests running in Google Cloud Build (GCB) and tests running with GitHub Action.
+It is a temporary setup; once GCB fully supports our desired workflow that works
+with the Go Git repository, we plan to use the GCB-based setup for CI.
+
+### Testing via GCB
+
+This workflow is triggered for Gerrit CLs (chosen by project members) and all
+the commits merged into the master branch.
+Note that our main repository is in `go.googlesource.com/vscode-go` and
+`github.com/golang/vscode-go` is a mirror of the Go Git repository.
+All PRs sent to `github.com/golang/vscode-go` will be converted as Gerrit CLs.
+Currently, the results of the CI Run are visible to only project members.
+We are working on improving this workflow - making the results visible to
+public and easily accessible through our Gerrit review UI.
+
+- `build/cloudbuild.yaml`, `build/all.bash` - define the GCB workflow.
+- `build/cloudbuild.container.yaml`, `build/Dockerfile` - define the Docker container used for CI.
+
+Project members (currently restricted to our GCP project members) can manually
+trigger cloud build and test their locally made changes. 
+Follow the [GCB instruction](https://cloud.google.com/cloud-build/docs/running-builds/start-build-manually)
+to set up the environment and tools, and then run
+
+```
+$ gcloud builds submit --config=build/cloudbuild.yaml
+```
+
+In order to modify and rebuild the docker container image, run
+
+```
+$ gcloud builds submit --config=build/cloudbuild.container.yaml
+```
+
+### Testing via GitHub Action
+
+This is the workflow triggered for every PR and commit made to our mirror repository in github.com/golang/vscode-go. We are using this CI to run tests
+in the platforms which GCB does not support yet, and allow contributors 
+to see the test results for their PRs. This workflow is not triggered by
+CLs sent via Gerrit yet.
+
+Until GCB-based CI is ready for general use, we recommend contributors
+to send PRs to github.com/golang/vscode-go as described in
+[the Go project contribution guide](https://golang.org/doc/contribute.html#sending_a_change_github). The results will be posted to the PR request.
+
+- `.github/workflows/ci.yml` - define the github action based CI workflow.
+
+## Nightly Release
+
+A new version is released based on what is committed on the `master` branch,
+at least once a day between Monday and Thursday. If there is no new commit,
+release does not happen. This nightly extension is a separate extension from
+the official Go extension, and is available at [the VS Code market place](https://marketplace.visualstudio.com/items?itemName=golang.go-nightly).
+
+The version number encodes the last commit timestamp of the master branch
+in the format of `YYYY.[M]M.[D]DHH`. For example, version 2020.3.702 indicates
+the extension is built with the last commit committed at ~2AM 2020/03/07 (UTC).
+
+- `.github/workflows/release.yml, build/all.bash` - define the daily release process.
+
+## Sync with upstream
+
+### Merging commits from upstream
+
+This is done manually by project members, probably before each nightly release.
+
+Once we consolidate the two repositories, this process becomes unnecessary.
+
+The merge script will create a Gerrit CL for merge and issue the GCB based test workflow. 
+The remote `origin` should be set to `https://go.googlesource.com/vscode-go`.
+Make sure you have access to the GCB project and `gcloud` tool 
+is available.
+
+```
+$ build/merge.sh
+```
+
+In case of conflicts, you will need to check out the cl, fix, and upload the
+updated cl again following the usual Gerrit CL workflow.
+
+### Reflecting commits to upstream
+
+Once the feature or bug fix tested with Nightly extension is stablized, create
+a PR to the upstream (github.com/microsoft/vscode-go).
+Please make sure to include all the gerrit CL numbers so the upstream code
+reviewers can find reference to all prior discussion.
diff --git a/build/all.bash b/build/all.bash
index 19ef780..6b72279 100755
--- a/build/all.bash
+++ b/build/all.bash
@@ -55,6 +55,33 @@
   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.nightly + Release commit info.
+  printf "**Release ${VER} @ ${COMMIT}** \n\n" | cat - CHANGELOG.md.nightly > /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 README.md.nightly - > /tmp/README.md.new && mv /tmp/README.md.new README.md
+}
+
 main() {
   cd "$(root_dir)"  # always run from the script root.
   case "$1" in
@@ -74,9 +101,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/merge.bash b/build/merge.bash
new file mode 100755
index 0000000..971f10a
--- /dev/null
+++ b/build/merge.bash
@@ -0,0 +1,34 @@
+#! /bin/bash
+set -euo pipefail
+
+# In order to sync with upstream, run merge.bash
+
+# TODO(hyangah): commands for building docker container and running tests locally with docker run.
+root_dir() {
+  local script_name=$(readlink -f "${0}")
+  local script_dir=$(dirname "${script_name}")
+  local parent_dir=$(dirname "${script_dir}")
+  echo "${parent_dir}"
+}
+
+ROOT="$(root_dir)"
+cd "${ROOT}"  # always run from the root directory.
+
+WORKTREE="$(mktemp -d)"
+BRANCH="sync/merge-upstream-$(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
+git merge --no-commit "origin/upstream" || echo "Ignoring conflict..."
+
+COMMIT=`git log --format=%h -n 1 "origin/upstream"`
+
+gcloud builds submit --config=build/cloudbuild.yaml || echo "Build failed. Please address the issue..."
+
+git commit -m "sync: merge microsoft/vscode-go@${COMMIT} into master"
+
+git codereview mail HEAD
+cd - && git worktree remove "${WORKTREE}"
diff --git a/docs/All-Settings-&-Commands-in-Visual-Studio-Code-Go-extension.md b/docs/All-Settings-&-Commands-in-Visual-Studio-Code-Go-extension.md
deleted file mode 100644
index 7723246..0000000
--- a/docs/All-Settings-&-Commands-in-Visual-Studio-Code-Go-extension.md
+++ /dev/null
@@ -1,12 +0,0 @@
-You can view the Visual Studio Code settings for the Go extension along with their *default* values and description in the product itself
-
-1. Press `Ctrl+Shift+X` or `Cmd+Shift+X` to open the Extensions viewlet.
-2. Find the Go extension, click on it to open the Extension Editor.
-3. Click on the `Contributions` tab.
-4. Scroll away.
-
-![ext](https://user-images.githubusercontent.com/16890566/30246497-9d6cc588-95b0-11e7-87dd-4bd1b18b139f.gif)
-
-If you want to change any of these, you can do so in user preferences (`cmd+,` or `ctrl+,`) or workspace settings (`.vscode/settings.json`). You don't have to copy these if you don't intend to change them.
-
-Scroll down and you will see list of all available commands with description too
\ No newline at end of file
diff --git a/docs/Building,-Debugging-and-Sideloading-the-extension-in-Visual-Studio-Code.md b/docs/Building,-Debugging-and-Sideloading-the-extension-in-Visual-Studio-Code.md
deleted file mode 100644
index d0067d1..0000000
--- a/docs/Building,-Debugging-and-Sideloading-the-extension-in-Visual-Studio-Code.md
+++ /dev/null
@@ -1,42 +0,0 @@
-You can set up a development environment for debugging the extension during extension development.
-
-## Building and Debugging the extension
-
-Ensure you have [node](https://nodejs.org/en/) installed.
-Clone the repo, run `npm install` and open a development instance of Code.
-
-```bash
-git clone https://github.com/Microsoft/vscode-go
-cd vscode-go
-npm install
-code .
-```
-
-Make sure the `window.openFoldersInNewWindow` setting is not `"on"`.
-
-You can now go to the Debug viewlet (`Ctrl+Shift+D`) and select `Launch Extension` then hit run (`F5`).
-
-This will open a new VS Code window which will have the title `[Extension Development Host]`. In this window, open any folder with Go code. 
-
-In the original VS Code window, you can now add breakpoints which will be hit when you use any of the the plugin's features in the second window.
-
-If you make edits in the extension `.ts` files, just reload (`cmd-r`) the `[Extension Development Host]` instance of Code to load in the new extension code.  The debugging instance will automatically reattach.
-
-To debug the debugger, see [the debugAdapter readme](/Microsoft/vscode-go/tree/master/src/debugAdapter).
-
-## Running the tests
-To run the tests locally, open the Debug viewlet (`Ctrl+Shift+D`), select `Launch Tests`, then hit run (`F5`)
-
-## Sideloading the extension
-After making changes to the extension, you might want to test it end to end instead of running it in debug mode. To do this, you can sideload the extension. This can be done by preparing the extension and loading it directly.
-
-1. `npm install -g vsce` to make sure you have vsce installed globally
-2. `git clone https://github.com/Microsoft/vscode-go` to clone the repo if you havent already done so
-3. `cd vscode-go`
-4. `npm install` to install dependencies if you havent already done so
-5. `vsce package` to build the package. This will generate a file with extension `vsix`
-6. Run the command `Extensions: Install from VSIX...`, choose the vsix file generated in the previous step
-
-## Use the beta version of this extension
-
-If you want to help with testing the next update to this extension or you want to use the latest features that arent released yet, its easy to do so. Please see [Use the beta version of the Go extension](Use-the-beta-version-of-the-latest-Go-extension.md)
\ No newline at end of file
diff --git a/docs/README.md b/docs/README.md
index 9ec8d70..112dd45 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,10 +1,9 @@
-- How to contribute? [Build, Debug and/or SideLoad the Go extension](Building,-Debugging-and-Sideloading-the-extension-in-Visual-Studio-Code.md)
-
-- [Go tools that the Go extension depends on](Go-tools-that-the-Go-extension-depends-on.md)
-- [GOPATH in the VS Code Go extension](GOPATH-in-the-VS-Code-Go-extension.md)
-- [Settings & Commands in Visual Studio Code Go extension](Settings-for-Visual-Studio-Code-Go-extension.md) 
-- [Debugging Go Code Using VS Code](Debugging-Go-code-using-VS-Code.md)
-- [Release Notes](https://github.com/Microsoft/vscode-go/blob/master/CHANGELOG.md)
+- How to contribute? [Build, Debug and/or SideLoad the Go extension](contributing.md)
+- [Go tools that the Go extension depends on](tools.md)
+- [GOPATH in the VS Code Go extension](gopath.md)
+- [Settings & Commands in Visual Studio Code Go extension](commands.md) 
+- [Debugging Go Code Using VS Code](debugging.md)
+- [Release Notes](../CHANGELOG.md)
 
 
-See the [FAQ](Go-with-VS-Code-FAQ-and-Troubleshooting.md).
\ No newline at end of file
+See the [FAQ](troubleshooting.md).
\ No newline at end of file
diff --git a/docs/Use-the-beta-version-of-the-latest-Go-extension.md b/docs/Use-the-beta-version-of-the-latest-Go-extension.md
deleted file mode 100644
index 36c2842..0000000
--- a/docs/Use-the-beta-version-of-the-latest-Go-extension.md
+++ /dev/null
@@ -1,7 +0,0 @@
-If you ever want to try the beta version of the Go extension which is built from the master, follow the below instructions:
-
-- Download the vsix file from https://github.com/Microsoft/vscode-go/releases/tag/latest
-- Run `code --install-extension Go-latest.vsix` 
-- Reload VS Code
-
-**Note**: When when you install a beta version, there is no auto-update mechanism where a newer beta version will get installed automatically.
diff --git a/docs/commands.md b/docs/commands.md
new file mode 100644
index 0000000..5647d8f
--- /dev/null
+++ b/docs/commands.md
@@ -0,0 +1,15 @@
+# Settings and Commands
+
+To view a complete list of the commands settings for this extension:
+
+1. Navigate to the Extensions view (Ctrl+Shift+X).
+2. Find the Go extension, click on it to open the Extension Editor.
+3. Click on the `Feature Contributions` tab.
+4. Scroll away.
+
+<!--TODO(rstambler): This image needs to be updated.-->
+![ext](https://user-images.githubusercontent.com/16890566/30246497-9d6cc588-95b0-11e7-87dd-4bd1b18b139f.gif)
+
+## Settings
+
+You can configure your settings by modifying your [User or Workspace Settings](https://code.visualstudio.com/docs/getstarted/settings). To navigate to your settings, open the Command Palette (Ctrl+Shift+P) and search for "settings". The simplest way to modify your settings is through "Preferences: Open Settings (UI)".
diff --git a/docs/contributing.md b/docs/contributing.md
new file mode 100644
index 0000000..115f4a2
--- /dev/null
+++ b/docs/contributing.md
@@ -0,0 +1,96 @@
+# Contributing
+
+We welcome your contributions and thank you for working to improve the Go development experience in VS Code.
+
+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).
+
+* [Before you start coding](#before-you-start-coding)
+  * [Ask for help](#ask-for-help)
+* [Developing](#developing)
+  * [Setup](#setup)
+  * [Run](#run)
+  * [Test](#test)
+  * [Sideload](#sideload)
+* [Mail your change for review](#mail-your-change-for-review)
+
+## Before you start coding
+
+If you are interested in fixing a bug or contributing a feature, please [file an issue](https://github.com/golang/vscode-go/issues/new/choose) first. Wait for a project maintainer to respond before you spend time coding.
+
+If you wish to work on an existing issue, please add a comment saying so, as someone may already be working on it. A project maintainer may respond with advice on how to get started. If you're not sure which issues are available, search from issues with the [help wanted label](https://github.com/golang/vscode-go/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22).
+
+### Ask for help
+
+The VS Code Go maintainers are reachable via the issue tracker and the [#vscode-dev] channel on the [Gophers Slack]. Please reach out on Slack with questions, suggestions, or ideas. If you have trouble getting started on an issue, we'd be happy to give pointers and advice.
+
+## Developing
+
+### Setup
+
+1) Install [node](https://nodejs.org/en/).
+2) Clone the repository, run `npm install` and open VS Code:
+
+    ```bash
+    git clone https://github.com/golang/vscode-go
+    cd vscode-go
+    npm install
+    code .
+    ```
+
+3) Make sure the `window.ope
+nFoldersInNewWindow` setting is not `"on"`. <!--TODO(rstambler): Confirm that this is still required.-->
+
+### Run
+
+To run the extension with your patch, open the Run view (`Ctrl+Shift+D`), select `Launch Extension`, and click the Play button (`F5`).
+
+This will open a new VS Code window with the title `[Extension Development Host]`. You can then open a folder that contains Go code and try out your changes.
+
+You can also set breakpoints, which will work as you run the extension.
+
+If you make further edits in the codebase, you can reload (`Ctrl+R`) the `[Extension Development Host]` instance of VS Code, which will load the new code. The debugging instance will automatically reattach.
+
+To debug the Go debugger, see the [debugAdapter README](../src/debugAdapter/README.md).
+
+## Test
+
+There are currently three test launch configurations: (1) Launch Extension Tests, (2) Launch Extension Tests with Gopls, and (3) Launch Unit Tests. To run the tests locally, open the Run view (`Ctrl+Shift+D`), select the relevant launch configuration, and hit the Play button (`F5`).
+
+## Sideload
+
+After making changes to the extension, you may want to test it end-to-end instead of running it in debug mode. To do this, you can sideload the extension.
+
+1. Install the [vsce](https://code.visualstudio.com/api/working-with-extensions/publishing-extension#vsce) tool for packaging extensions (`npm install -g vsce`).
+2. `cd` into your `vscode-go` directory.
+3. Install all dependencies by running `npm install`.
+4. Run `vsce package` to build the package. This will generate a file with a `.vsix` extension in your current directory.
+
+    ```bash
+    npm install -g vsce
+    cd vscode-go
+    npm install
+    vsce package
+    ```
+
+5. Open a VS Code window, navigate to the Extensions view, and disable or uninstall the default Go extension.
+6. Click on the "..." in the top-right corner, select "Install
+from VSIX...", and choose the generated VSIX file. Alternatively, you can run `code --install-extension path/to/go.vsix` or open the Command Palette and run the "Extensions: Install from VSIX..." command.
+
+## Mail your change for review
+
+Once you have coded, built, and tested your change, it's ready for review! There are two ways to contribute your change: (1) [a GitHub pull request (PR)](https://golang.org/doc/contribute.html#sending_a_change_github), or (2) a [Gerrit code review](https://golang.org/doc/contribute.html#sending_a_change_gerrit).
+
+<!--TODO(rstambler): The content on https://golang.org/doc/contribute.html needs to be generalized to all x/ repos.-->
+
+In either case, code review will happen in [Gerrit](https://www.gerritcodereview.com/), which is used for all repositories in the Go project. GitHub pull requests will be mirrored into Gerrit, so you can follow a more traditional GitHub workflow, but you will still have to look at Gerrit to read comments.
+
+The easiest way to start is by reading this [detailed guide for contributing to the Go project](https://golang.org/doc/contribute.html). Important things to note are:
+
+* You will need to sign the [Google CLA](https://golang.org/doc/contribute.html#cla).
+* Your commit message should follow the standards described on the [Commit Message Wiki page](https://github.com/golang/go/wiki/CommitMessage).<!--TODO(rstambler): What should the prefix be for vscode-go CLs? I feel like we still haven't figured this out.-->
+* Your change should include tests (if possible).
+
+Once you've sent out your change, a maintainer will take a look at your contribution within a few weeks. If you don't hear back in that time, feel free to ping the issue or send a message to the [#vscode-dev] channel of the [Gophers Slack].
+
+[#vscode-dev]: https://gophers.slack.com/archives/CUWGEKH5Z
+[Gophers Slack]: https://invite.slack.golangbridge.org/
diff --git a/docs/Debugging-Go-code-using-VS-Code.md b/docs/debugging.md
similarity index 97%
rename from docs/Debugging-Go-code-using-VS-Code.md
rename to docs/debugging.md
index e36c61f..5170e01 100644
--- a/docs/Debugging-Go-code-using-VS-Code.md
+++ b/docs/debugging.md
@@ -7,8 +7,8 @@
 ## Set up configurations in your settings
 
 The below settings are used by the debugger. You may not need to add/change any of them to have debugging working in simple cases, but do give them a read sometime
-- `go.gopath`. See [GOPATH in VS Code](GOPATH-in-the-VS-Code-Go-extension.md)
-- `go.inferGopath`. See [GOPATH in VS Code](GOPATH-in-the-VS-Code-Go-extension.md)
+- `go.gopath`. See [GOPATH in VS Code](gopath.md)
+- `go.inferGopath`. See [GOPATH in VS Code](gopath.md)
 - `go.delveConfig`
      - `apiVersion`: Controls the version of delve apis to be used when launching the delve headless server. Default is 2.
      - `dlvLoadConfig`: Not applicable when `apiVersion` is 1. The configuration passed to delve. Controls [various features of delve](https://github.com/Microsoft/vscode-go/blob/0.6.85/package.json#L431-L468) that affects the variables shown in the debug pane.
@@ -205,7 +205,7 @@
 
 ### Debug the debugger using source code
 
-If you want to dig deeper and debug the debugger using source code of this extension, see [building-and-debugging-the-extension](Building,-Debugging-and-Sideloading-the-extension-in-Visual-Studio-Code#building-and-debugging-the-extension.md)
+If you want to dig deeper and debug the debugger using source code of this extension, see [building-and-debugging-the-extension](contributing.md#building-and-debugging-the-extension)
 
 ### Common issues
 
diff --git a/docs/Features-for-Go-in-VS-Code.md b/docs/features.md
similarity index 100%
rename from docs/Features-for-Go-in-VS-Code.md
rename to docs/features.md
diff --git a/docs/GOPATH-in-the-VS-Code-Go-extension.md b/docs/gopath.md
similarity index 100%
rename from docs/GOPATH-in-the-VS-Code-Go-extension.md
rename to docs/gopath.md
diff --git a/docs/Go-modules-support-in-Visual-Studio-Code.md b/docs/modules.md
similarity index 82%
rename from docs/Go-modules-support-in-Visual-Studio-Code.md
rename to docs/modules.md
index 3ce4da8..4eeb686 100644
--- a/docs/Go-modules-support-in-Visual-Studio-Code.md
+++ b/docs/modules.md
@@ -14,10 +14,10 @@
 
 * [Troubleshooting and reporting issues](https://golang.org/s/gopls/doc/troubleshooting.md)
 
-In addition to the Go language server, VS Code extension may use additional [Go tools](Go-tools-that-the-Go-extension-depends-on.md) to provide features like code navigation, code completion, build, lint etc. Some tools may not have a good support for Go modules yet. Please report [an issue](https://github.com/microsoft/vscode-go/issues/new) if you encounter problems.
+In addition to the Go language server, VS Code extension may use additional [Go tools](tools.md) to provide features like code navigation, code completion, build, lint etc. Some tools may not have a good support for Go modules yet. Please report [an issue](https://github.com/microsoft/vscode-go/issues/new) if you encounter problems.
 
 
-If you don't want to use the language server for any reason, then please know that not all the [Go tools](Go-tools-that-the-Go-extension-depends-on.md) that this extension depends on supports Go modules. https://golang.org/issues/24661 is the issue used by the Go tools team to track the update of Go modules support in various Go tools.
+If you don't want to use the language server for any reason, then please know that not all the [Go tools](tools.md) that this extension depends on supports Go modules. https://golang.org/issues/24661 is the issue used by the Go tools team to track the update of Go modules support in various Go tools.
 
 ## FAQ
 
diff --git a/docs/nightly.md b/docs/nightly.md
new file mode 100644
index 0000000..776b2f4
--- /dev/null
+++ b/docs/nightly.md
@@ -0,0 +1,21 @@
+# Go Nightly
+
+This master branch of this extension is built and published nightly through the [Go Nightly](https://marketplace.visualstudio.com/items?itemName=golang.go-nightly). If you're interested in testing new features and bug fixes, you may be interested in switching to the Go Nightly extension. Also, if you file an issue, we may suggest trying out the fix in Go Nightly.
+
+If you try out Go Nightly, please file issues when you notice bugs. You can also join the maintainers in the [#vscode-dev](https://gophers.slack.com/archives/CUWGEKH5Z) channel on the [Gophers Slack](https://invite.slack.golangbridge.org/).
+
+## Installation
+
+To use the Go Nightly extension, you must first disable the standard Go extension. The two are not compatible and will cause conflicts if enabled simultaneously. If you'd like to make a permanent switch, you can uninstall the Go extension. Otherwise, you can disable it temporarily. To do so, open the Extensions view in VS Code, click on the gear icon next to the Go extension, and select Disable. Then, search for Go Nightly in the VS Code Marketplace and install it instead.
+
+## Testing pre-releases
+
+**Note**: Pre-releases are not yet available.
+
+Pre-releases of the Go extension will be made available on the [Releases page](https://github.com/golang/vscode-go/releases/tag/latest) on GitHub. If you would like to try a pre-release, follow these instructions:
+
+1) Download the `.vsix` file from the [Releases page](https://github.com/golang/vscode-go/releases/tag/latest).
+2) Navigate to the Extensions view in VS Code (Ctrl+Shift+X). Click on the "..." in the top-right corner, select "Install from VSIX", and select `Go-latest.vsix`. Alternatively, you can run `code --install-extension Go-latest.vsix` or open the Command Palette and run the "Extensions: Install from VSIX..." command.
+3) If prompted, reload VS Code.
+
+**Note**: If you install an extension from a VSIX file, you will stop receiving automatic prompts when updates are released.
diff --git a/docs/On-Save-features.md b/docs/on-save.md
similarity index 100%
rename from docs/On-Save-features.md
rename to docs/on-save.md
diff --git a/docs/Tasks-with-Go.md b/docs/tasks.md
similarity index 100%
rename from docs/Tasks-with-Go.md
rename to docs/tasks.md
diff --git a/docs/Go-tools-that-the-Go-extension-depends-on.md b/docs/tools.md
similarity index 100%
rename from docs/Go-tools-that-the-Go-extension-depends-on.md
rename to docs/tools.md
diff --git a/docs/Go-with-VS-Code-FAQ-and-Troubleshooting.md b/docs/troubleshooting.md
similarity index 93%
rename from docs/Go-with-VS-Code-FAQ-and-Troubleshooting.md
rename to docs/troubleshooting.md
index ad43f2c..85836ab 100644
--- a/docs/Go-with-VS-Code-FAQ-and-Troubleshooting.md
+++ b/docs/troubleshooting.md
@@ -2,7 +2,7 @@
 
 **Q: I installed the plugin, but none of the features are working. Why?**
 
-**A:** Make sure to install all the dependent Go tools. Run `Go: Install/Update Tools`. If you want to install only selected tools, then go through the [Go tools that this plugin depends on](Go-tools-that-the-Go-extension-depends-on) and install the ones you need manually
+**A:** Make sure to install all the dependent Go tools. Run `Go: Install/Update Tools`. If you want to install only selected tools, then go through the [Go tools that this plugin depends on](tools.md) and install the ones you need manually
 
 If you see an error of the form `command <command-name-here> not found`, it means that the extension has failed to activate and register its commands. Please try to uninstall and re-install the extension.
 
@@ -43,15 +43,15 @@
 
 **Q: How does the plugin determine the GOPATH to use?**
 
-**A:** See [GOPATH in the VS Code Go extension](GOPATH-in-the-VS-Code-Go-extension.md)
+**A:** See [GOPATH in the VS Code Go extension](gopath.md)
 
 **Q: Does VS Code support Go modules?**
 
-**A:** See [Go modules support in VS Code](Go-modules-support-in-Visual-Studio-Code.md)
+**A:** See [Go modules support in VS Code](modules.md)
 
 **Q: Why is code navigation and code completion slow when using Go modules?**
 
-Please see [Go modules support in VS Code](Go-modules-support-in-Visual-Studio-Code.md)
+Please see [Go modules support in VS Code](modules.md)
 
 **Q: Can I use language server when using Go modules?**
 
@@ -118,4 +118,4 @@
 
 **Q: How do I get the features/bug fixes that are implemented but not released yet? How do I get the beta version of the Go extension?**
 
-**A:** See [Install the beta version](Use-the-beta-version-of-the-latest-Go-extension.md)
\ No newline at end of file
+**A:** Install the [Go Nightly](nightly.md) extension.
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 a3fbee8..ea435d4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
-  "name": "Go",
-  "version": "0.14.2",
+  "name": "go-nightly",
+  "version": "0.0.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
@@ -75,6 +75,15 @@
       "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",
@@ -155,6 +164,12 @@
       "integrity": "sha512-WJZtZlinE3meRdH+I7wTsIhpz/GLhqEQwmPGeh4s1irWLwMzCeTV8WZ+pgPTwrDXoafVUWwo1LiZ9HJVHFlJSQ==",
       "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": {
       "version": "4.3.0",
       "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
@@ -206,17 +221,6 @@
         "picomatch": "^2.0.4"
       }
     },
-    "applicationinsights": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-1.4.0.tgz",
-      "integrity": "sha512-TV8MYb0Kw9uE2cdu4V/UvTKdOABkX2+Fga9iDz0zqV7FLrNXfmAugWZmmdTx4JoynYkln3d5CUHY3oVSUEbfFw==",
-      "requires": {
-        "cls-hooked": "^4.2.2",
-        "continuation-local-storage": "^3.2.1",
-        "diagnostic-channel": "0.2.0",
-        "diagnostic-channel-publishers": "^0.3.2"
-      }
-    },
     "argparse": {
       "version": "1.0.10",
       "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@@ -244,30 +248,6 @@
       "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
       "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
     },
-    "async-hook-jl": {
-      "version": "1.7.6",
-      "resolved": "https://registry.npmjs.org/async-hook-jl/-/async-hook-jl-1.7.6.tgz",
-      "integrity": "sha512-gFaHkFfSxTjvoxDMYqDuGHlcRyUuamF8s+ZTtJdDzqjws4mCt7v0vuV79/E2Wr2/riMQgtG4/yUtXWs1gZ7JMg==",
-      "requires": {
-        "stack-chain": "^1.3.7"
-      }
-    },
-    "async-listener": {
-      "version": "0.6.10",
-      "resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.10.tgz",
-      "integrity": "sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw==",
-      "requires": {
-        "semver": "^5.3.0",
-        "shimmer": "^1.1.0"
-      },
-      "dependencies": {
-        "semver": {
-          "version": "5.7.1",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
-          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
-        }
-      }
-    },
     "asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -441,23 +421,6 @@
         }
       }
     },
-    "cls-hooked": {
-      "version": "4.2.2",
-      "resolved": "https://registry.npmjs.org/cls-hooked/-/cls-hooked-4.2.2.tgz",
-      "integrity": "sha512-J4Xj5f5wq/4jAvcdgoGsL3G103BtWpZrMo8NEinRltN+xpTZdI+M38pyQqhuFU/P792xkMFvnKSf+Lm81U1bxw==",
-      "requires": {
-        "async-hook-jl": "^1.7.6",
-        "emitter-listener": "^1.0.1",
-        "semver": "^5.4.1"
-      },
-      "dependencies": {
-        "semver": {
-          "version": "5.7.1",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
-          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
-        }
-      }
-    },
     "color-convert": {
       "version": "1.9.3",
       "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -493,15 +456,6 @@
       "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
       "dev": true
     },
-    "continuation-local-storage": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz",
-      "integrity": "sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA==",
-      "requires": {
-        "async-listener": "^0.6.0",
-        "emitter-listener": "^1.1.1"
-      }
-    },
     "core-util-is": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@@ -569,26 +523,6 @@
       "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
       "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
     },
-    "diagnostic-channel": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/diagnostic-channel/-/diagnostic-channel-0.2.0.tgz",
-      "integrity": "sha1-zJmvlhLCP7H/8TYSxy8sv6qNWhc=",
-      "requires": {
-        "semver": "^5.3.0"
-      },
-      "dependencies": {
-        "semver": {
-          "version": "5.7.1",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
-          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
-        }
-      }
-    },
-    "diagnostic-channel-publishers": {
-      "version": "0.3.3",
-      "resolved": "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.3.3.tgz",
-      "integrity": "sha512-qIocRYU5TrGUkBlDDxaziAK1+squ8Yf2Ls4HldL3xxb/jzmWO2Enux7CvevNKYmF2kDXZ9HiRqwjPsjk8L+i2Q=="
-    },
     "diff": {
       "version": "4.0.2",
       "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
@@ -603,14 +537,6 @@
         "safer-buffer": "^2.1.0"
       }
     },
-    "emitter-listener": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz",
-      "integrity": "sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==",
-      "requires": {
-        "shimmer": "^1.2.0"
-      }
-    },
     "emoji-regex": {
       "version": "7.0.3",
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
@@ -1642,11 +1568,6 @@
       "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
       "dev": true
     },
-    "shimmer": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz",
-      "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw=="
-    },
     "side-channel": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.2.tgz",
@@ -1710,11 +1631,6 @@
         "tweetnacl": "~0.14.0"
       }
     },
-    "stack-chain": {
-      "version": "1.3.7",
-      "resolved": "https://registry.npmjs.org/stack-chain/-/stack-chain-1.3.7.tgz",
-      "integrity": "sha1-0ZLJ/06moiyUxN1FkXHj8AzqEoU="
-    },
     "string-width": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
@@ -1922,14 +1838,6 @@
       "resolved": "https://registry.npmjs.org/vscode-debugprotocol/-/vscode-debugprotocol-1.40.0.tgz",
       "integrity": "sha512-Fwze+9qbLDPuQUhtITJSu/Vk6zIuakNM1iR2ZiZRgRaMEgBpMs2JSKaT0chrhJHCOy6/UbpsUbUBIseF6msV+g=="
     },
-    "vscode-extension-telemetry": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.2.tgz",
-      "integrity": "sha512-FSbaZKlIH3VKvBJsKw7v5bESWHXzltji2rtjaJeJglpQH4tfClzwHMzlMXUZGiblV++djEzb1gW8mb5E+wxFsg==",
-      "requires": {
-        "applicationinsights": "1.4.0"
-      }
-    },
     "vscode-jsonrpc": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz",
diff --git a/package.json b/package.json
index 5b5e66b..18413d0 100644
--- a/package.json
+++ b/package.json
@@ -1,11 +1,13 @@
 {
-  "name": "Go",
-  "version": "0.14.3",
-  "publisher": "ms-vscode",
-  "description": "Rich Go language support for Visual Studio Code",
+  "name": "go-nightly",
+  "displayName": "Go Nightly",
+  "version": "0.0.0",
+  "publisher": "golang",
+  "description": "Rich Go language support for Visual Studio Code (Nightly)",
   "author": {
-    "name": "Microsoft Corporation - Development Labs"
+    "name": "Go Team at Google"
   },
+  "preview": true,
   "license": "MIT",
   "icon": "images/go-logo-blue.png",
   "categories": [
@@ -22,7 +24,10 @@
   "private": true,
   "repository": {
     "type": "git",
-    "url": "https://github.com/Microsoft/vscode-go.git"
+    "url": "https://github.com/golang/vscode-go"
+  },
+  "bugs": {
+    "url": "https://github.com/golang/vscode-go/issues"
   },
   "keywords": [
     "multi-root ready"
@@ -40,6 +45,7 @@
   },
   "extensionDependencies": [],
   "dependencies": {
+    "deep-equal": "^2.0.2",
     "diff": "^4.0.2",
     "json-rpc2": "^1.0.2",
     "moment": "^2.24.0",
@@ -47,12 +53,13 @@
     "tree-kill": "^1.2.2",
     "vscode-debugadapter": "^1.40.0",
     "vscode-debugprotocol": "^1.40.0",
-    "vscode-extension-telemetry": "^0.1.2",
     "vscode-languageclient": "6.1.0",
-    "web-request": "^1.0.7",
-    "deep-equal": "^2.0.2"
+    "web-request": "^1.0.7"
   },
   "devDependencies": {
+    "adm-zip": "^0.4.14",
+    "@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",
@@ -67,8 +74,7 @@
     "sinon": "^9.0.2",
     "tslint": "^6.1.1",
     "typescript": "^3.8.3",
-    "vscode-test": "^1.3.0",
-    "@types/deep-equal": "^1.0.1"
+    "vscode-test": "^1.3.0"
   },
   "engines": {
     "vscode": "^1.41.0"
@@ -1127,6 +1133,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/debugAdapter/goDebug.ts b/src/debugAdapter/goDebug.ts
index ec745cf..09075ba 100644
--- a/src/debugAdapter/goDebug.ts
+++ b/src/debugAdapter/goDebug.ts
@@ -4,8 +4,10 @@
  *--------------------------------------------------------*/
 
 import { ChildProcess, execFile, execSync, spawn, spawnSync } 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';
@@ -88,6 +90,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 +324,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 +339,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;
@@ -715,7 +739,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 +753,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;
@@ -827,10 +857,80 @@
 		}
 	}
 
-	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!
+				try {
+					await this.initializeRemotePackagesAndSources();
+					const matchedRemoteFile = this.inferRemotePathFromLocalPath(filePath);
+					if (matchedRemoteFile) {
+						return matchedRemoteFile;
+					}
+				} catch (error) {
+					log(`Failing to initialize remote sources: ${error}`);
+				}
+			}
 			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 +938,158 @@
 		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 = process.env['GOROOT'] || '';
+		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);
 		}
 
@@ -968,7 +1218,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 +1227,9 @@
 				}
 				const locations = this.delve.isApiV1 ? <DebugLocation[]>out : (<StacktraceOut>out).Locations;
 				log('locations', locations);
+
+				await this.initializeRemotePackagesAndSources();
+
 				let stackFrames = locations.map((location, frameId) => {
 					const uniqueStackFrameId = this.stackFrameHandles.create([goroutineId, frameId]);
 					return new StackFrame(
@@ -1400,8 +1653,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 +1729,38 @@
 		);
 	}
 
-	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) {
+			await this.remoteSourcesAndPackages.initializeRemotePackagesAndSources(this.delve);
+			return;
+		}
+
+		if (this.remoteSourcesAndPackages.initializingRemoteSourceFiles) {
+			await new Promise((resolve) => {
+				this.remoteSourcesAndPackages.on(RemoteSourcesAndPackages.INITIALIZED, () => {
+					resolve();
+				});
+			});
+		}
+	}
+
+	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 +1867,13 @@
 			);
 	}
 
-	private getPackageInfo(debugState: DebuggerState): Thenable<string> {
+	private async getPackageInfo(debugState: DebuggerState): Promise<string> {
 		if (!debugState.currentThread || !debugState.currentThread.file) {
 			return Promise.resolve(null);
 		}
+		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 +2186,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);
 }
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/goDebugConfiguration.ts b/src/goDebugConfiguration.ts
index f087e33..d09f2b1 100644
--- a/src/goDebugConfiguration.ts
+++ b/src/goDebugConfiguration.ts
@@ -10,7 +10,6 @@
 import { promptForMissingTool } from './goInstallTools';
 import { packagePathToGoModPathMap } from './goModules';
 import { getFromGlobalState, updateGlobalState } from './stateUtils';
-import { sendTelemetryEventForDebugConfiguration } from './telemetry';
 import { getBinPath, getCurrentGoPath, getGoConfig, getToolsEnvVars } from './util';
 
 export class GoDebugConfigurationProvider implements vscode.DebugConfigurationProvider {
@@ -36,10 +35,6 @@
 		debugConfiguration: vscode.DebugConfiguration,
 		token?: vscode.CancellationToken
 	): vscode.DebugConfiguration {
-		if (debugConfiguration) {
-			sendTelemetryEventForDebugConfiguration(debugConfiguration);
-		}
-
 		const activeEditor = vscode.window.activeTextEditor;
 		if (!debugConfiguration || !debugConfiguration.request) {
 			// if 'request' is missing interpret this as a missing launch.json
diff --git a/src/goFormat.ts b/src/goFormat.ts
index ed8a965..78a52a3 100644
--- a/src/goFormat.ts
+++ b/src/goFormat.ts
@@ -9,7 +9,6 @@
 import path = require('path');
 import vscode = require('vscode');
 import { promptForMissingTool, promptForUpdatingTool } from './goInstallTools';
-import { sendTelemetryEventForFormatting } from './telemetry';
 import { getBinPath, getGoConfig, getToolsEnvVars, killTree } from './util';
 
 export class GoDocumentFormattingEditProvider implements vscode.DocumentFormattingEditProvider {
@@ -71,7 +70,6 @@
 				return reject();
 			}
 
-			const t0 = Date.now();
 			const env = getToolsEnvVars();
 			const cwd = path.dirname(document.fileName);
 			let stdout = '';
@@ -101,12 +99,6 @@
 				const textEdits: vscode.TextEdit[] = [
 					new vscode.TextEdit(new vscode.Range(fileStart, fileEnd), stdout)
 				];
-
-				const timeTaken = Date.now() - t0;
-				sendTelemetryEventForFormatting(formatTool, timeTaken);
-				if (timeTaken > 750) {
-					console.log(`Formatting took too long(${timeTaken}ms). Format On Save feature could be aborted.`);
-				}
 				return resolve(textEdits);
 			});
 			if (p.pid) {
diff --git a/src/goImport.ts b/src/goImport.ts
index dd9a6de..c8445c7 100644
--- a/src/goImport.ts
+++ b/src/goImport.ts
@@ -11,7 +11,6 @@
 import { documentSymbols, GoOutlineImportsOptions } from './goOutline';
 import { getImportablePackages } from './goPackages';
 import { envPath } from './goPath';
-import { sendTelemetryEventForAddImportCmd } from './telemetry';
 import { getBinPath, getImportPath, getToolsEnvVars, parseFilePrelude } from './util';
 
 const missingToolMsg = 'Missing tool: ';
@@ -132,7 +131,6 @@
 		if (!imp) {
 			return;
 		}
-		sendTelemetryEventForAddImportCmd(arg);
 		const edits = getTextEditForAddImport(imp);
 		if (edits && edits.length > 0) {
 			const edit = new vscode.WorkspaceEdit();
diff --git a/src/goInstallTools.ts b/src/goInstallTools.ts
index 495913d..3d8508f 100644
--- a/src/goInstallTools.ts
+++ b/src/goInstallTools.ts
@@ -11,6 +11,7 @@
 import { SemVer } from 'semver';
 import vscode = require('vscode');
 import { getLanguageServerToolPath } from './goLanguageServer';
+import { restartLanguageServer } from './goMain';
 import { envPath, getToolFromToolPath } from './goPath';
 import { hideGoStatus, outputChannel, showGoStatus } from './goStatus';
 import {
@@ -23,7 +24,8 @@
 	getTool,
 	hasModSuffix,
 	isGocode,
-	Tool
+	Tool,
+	ToolAtVersion
 } from './goTools';
 import {
 	getBinPath,
@@ -31,6 +33,7 @@
 	getGoConfig,
 	getGoVersion,
 	getTempFilePath,
+	getToolsEnvVars,
 	getToolsGopath,
 	GoVersion,
 	resolvePath
@@ -54,7 +57,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 +68,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,7 +94,7 @@
  *                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> {
+export async function installTools(missing: ToolAtVersion[], goVersion: GoVersion): Promise<void> {
 	const goRuntimePath = getBinPath('go');
 	if (!goRuntimePath) {
 		vscode.window.showErrorMessage(
@@ -119,14 +108,12 @@
 
 	// http.proxy setting takes precedence over environment variables
 	const httpProxy = vscode.workspace.getConfiguration('http', null).get('proxy');
-	let envForTools = Object.assign({}, process.env);
+	const envForTools = Object.assign({}, process.env, getToolsEnvVars());
 	if (httpProxy) {
-		envForTools = Object.assign({}, process.env, {
-			http_proxy: httpProxy,
-			HTTP_PROXY: httpProxy,
-			https_proxy: httpProxy,
-			HTTPS_PROXY: httpProxy
-		});
+		envForTools['http_proxy'] = httpProxy;
+		envForTools['HTTP_PROXY'] = httpProxy;
+		envForTools['https_proxy'] = httpProxy;
+		envForTools['HTTPS_PROXY'] = httpProxy;
 	}
 
 	outputChannel.show();
@@ -197,7 +184,7 @@
 		.reduce((res: Promise<string[]>, tool: ToolAtVersion) => {
 			return res.then(
 				(sofar) =>
-					new Promise<string[]>((resolve, reject) => {
+					new Promise<string[]>(async (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);
@@ -210,17 +197,18 @@
 							tmpGoModFile = path.join(toolsTmpDir, 'go.mod');
 							fs.writeFileSync(tmpGoModFile, 'module tools');
 						}
+						let importPath: string;
+						if (modulesOffForTool) {
+							importPath = getImportPath(tool, goVersion);
+						} else {
+							importPath = getImportPathWithVersion(tool, tool.version, goVersion);
+						}
 
-						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();
@@ -231,66 +219,51 @@
 							}
 						};
 
-						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) {
+						// Perform any on-close actions before reinstalling the tool.
+						if (tool.close) {
+							const errMsg = await tool.close();
+							if (errMsg) {
+								outputChannel.appendLine(errMsg);
 								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);
+						}
+						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');
+						}
+						args.push(importPath);
+						const opts = {
+							env: envForTools,
+							cwd: toolsTmpDir
+						};
+						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 {
-								importPath = getImportPathWithVersion(tool, tool.version, goVersion);
+								callback(err, stdout, stderr);
 							}
-							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);
-								}
-							});
 						});
 					})
 			);
@@ -299,10 +272,12 @@
 			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 :).');
+
+				// Restart the language server since a new binary has been installed.
+				if (containsString(missing, 'gopls')) {
+					restartLanguageServer();
+				}
 				return;
 			}
 
@@ -324,23 +299,15 @@
 	}
 
 	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;
-		}
+
+	// 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 +320,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,24 +353,23 @@
 		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> {
@@ -482,9 +445,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 +480,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..257ffdb 100644
--- a/src/goLanguageServer.ts
+++ b/src/goLanguageServer.ts
@@ -7,28 +7,42 @@
 
 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 { GoDefinitionProvider } from './goDeclaration';
+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 { GoTypeDefinitionProvider } from './goTypeDefinition';
+import { getFromGlobalState, updateGlobalState } from './stateUtils';
 import { getBinPath, getCurrentGoPath, getGoConfig, getToolsEnvVars } from './util';
 
 interface LanguageServerConfig {
 	serverName: string;
 	path: string;
+	modtime: Date;
 	enabled: boolean;
 	flags: string[];
 	env: any;
@@ -47,38 +61,50 @@
 let latestConfig: LanguageServerConfig;
 let serverOutputChannel: vscode.OutputChannel;
 
-// 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 +123,47 @@
 	// 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);
 	}
 
+	// 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) {
+	if (config.enabled && !serverOutputChannel) {
 		serverOutputChannel = 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],
@@ -136,6 +180,31 @@
 			},
 			outputChannel: serverOutputChannel,
 			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}.`
+					);
+					// Stick with the default number of 5 crashes before shutdown.
+					if (count >= 5) {
+						return ErrorAction.Shutdown;
+					}
+					return ErrorAction.Continue;
+				},
+				closed: (): CloseAction => {
+					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 +226,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 +268,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')
 	) {
-		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.
@@ -274,23 +360,47 @@
 			documentLink: goConfig['languageServerExperimentalFeatures']['documentLink']
 		},
 		env: toolsEnv,
-		checkForUpdates: goConfig['useGoProxyToCheckForToolUpdates']
+		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 +445,8 @@
 	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(
+const acceptGoplsPrerelease = true;  // For nightly, we accept the prerelease version.
+export async function shouldUpdateLanguageServer(
 	tool: Tool,
 	languageServerToolPath: string,
 	makeProxyCall: boolean
@@ -349,19 +457,19 @@
 	}
 
 	// 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,
@@ -373,12 +481,12 @@
 
 	// 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;
 	}
@@ -388,13 +496,13 @@
 	return semver.lt(usersVersion, 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,7 +538,7 @@
 	return moment.utc(timestamp, 'YYYYMMDDHHmmss');
 }
 
-async function goplsVersionTimestamp(tool: Tool, version: semver.SemVer): Promise<moment.Moment> {
+export async function getTimestampForVersion(tool: Tool, version: semver.SemVer): Promise<moment.Moment> {
 	const data = await goProxyRequest(tool, `v${version.format()}.info`);
 	if (!data) {
 		return null;
@@ -439,7 +547,7 @@
 	return time;
 }
 
-async function latestGopls(tool: Tool): Promise<semver.SemVer> {
+export async function getLatestGoplsVersion(tool: Tool): Promise<semver.SemVer> {
 	// 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.
@@ -470,7 +578,10 @@
 	return versions.find((version) => !version.prerelease || !version.prerelease.length);
 }
 
-async function goplsVersion(goplsPath: string): Promise<string> {
+// 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 async function getLocalGoplsVersion(goplsPath: string): Promise<string> {
 	const env = getToolsEnvVars();
 	const execFile = util.promisify(cp.execFile);
 	let output: any;
@@ -527,9 +638,15 @@
 }
 
 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 +665,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, true));
+	} 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['date'] && saved['prompt']) {
+		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: getToolsEnvVars() });
+			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/goMain.ts b/src/goMain.ts
index 7fda993..ee62766 100644
--- a/src/goMain.ts
+++ b/src/goMain.ts
@@ -5,7 +5,6 @@
 
 'use strict';
 
-import fs = require('fs');
 import * as path from 'path';
 import vscode = require('vscode');
 import { browsePackages } from './goBrowsePackage';
@@ -13,82 +12,53 @@
 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 { 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,
+	installAllTools, installTools, offerToInstallTools, promptForMissingTool,
 	updateGoPathGoRootFromConfig
 } 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 { 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 { 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 { disposeTelemetryReporter, sendTelemetryEventForConfig } from './telemetry';
 import { cancelRunningTests, showTestOutput } from './testUtils';
 import {
-	cleanupTempDir,
-	getBinPath,
-	getCurrentGoPath,
-	getExtensionCommands,
-	getGoConfig,
-	getGoVersion,
-	getToolsEnvVars,
-	getToolsGopath,
-	getWorkspaceFolderPath,
-	handleDiagnosticErrors,
-	isGoPathSet
+	cleanupTempDir, getBinPath, getCurrentGoPath, getExtensionCommands, getGoConfig,
+	getGoVersion, getToolsEnvVars, getToolsGopath, getWorkspaceFolderPath, handleDiagnosticErrors, 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);
@@ -145,12 +115,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 &&
@@ -386,7 +365,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();
@@ -405,7 +384,6 @@
 				return;
 			}
 			const updatedGoConfig = getGoConfig();
-			sendTelemetryEventForConfig(updatedGoConfig);
 			updateGoPathGoRootFromConfig();
 
 			// If there was a change in "toolsGopath" setting, then clear cache for go tools
@@ -563,12 +541,10 @@
 	vscode.languages.setLanguageConfiguration(GO_MODE.language, {
 		wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g
 	});
-
-	sendTelemetryEventForConfig(getGoConfig());
 }
 
 export function deactivate() {
-	return Promise.all([disposeTelemetryReporter(), cancelRunningTests(), Promise.resolve(cleanupTempDir())]);
+	return Promise.all([cancelRunningTests(), Promise.resolve(cleanupTempDir())]);
 }
 
 function runBuilds(document: vscode.TextDocument, goConfig: vscode.WorkspaceConfiguration) {
@@ -623,28 +599,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/goModules.ts b/src/goModules.ts
index 35caeb7..65d8b78 100644
--- a/src/goModules.ts
+++ b/src/goModules.ts
@@ -7,10 +7,10 @@
 import path = require('path');
 import vscode = require('vscode');
 import { installTools } from './goInstallTools';
+import { restartLanguageServer } from './goMain';
 import { envPath, fixDriveCasingInWindows } from './goPath';
 import { getTool } from './goTools';
 import { getFromGlobalState, updateGlobalState } from './stateUtils';
-import { sendTelemetryEventForModulesUsage } from './telemetry';
 import { getBinPath, getGoConfig, getGoVersion, getModuleCache, getToolsEnvVars } from './util';
 
 export let GO111MODULE: string;
@@ -99,7 +99,6 @@
 		return;
 	}
 	moduleUsageLogged = true;
-	sendTelemetryEventForModulesUsage();
 }
 
 const promptedToolsForCurrentSession = new Set<string>();
@@ -127,22 +126,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);
diff --git a/src/goPackages.ts b/src/goPackages.ts
index 8af7878..208d2d2 100644
--- a/src/goPackages.ts
+++ b/src/goPackages.ts
@@ -8,7 +8,6 @@
 import vscode = require('vscode');
 import { promptForMissingTool, promptForUpdatingTool } from './goInstallTools';
 import { envPath, fixDriveCasingInWindows, getCurrentGoWorkspaceFromGOPATH } from './goPath';
-import { sendTelemetryEventForGopkgs } from './telemetry';
 import { getBinPath, getCurrentGoPath, getGoVersion, getToolsEnvVars, isVendorSupported } from './util';
 
 type GopkgsDone = (res: Map<string, PackageInfo>) => void;
@@ -101,7 +100,6 @@
 				});
 			});
 			const timeTaken = Date.now() - t0;
-			sendTelemetryEventForGopkgs(timeTaken);
 			cacheTimeout = timeTaken > 5000 ? timeTaken : 5000;
 			return resolve(pkgs);
 		});
diff --git a/src/goTools.ts b/src/goTools.ts
index 7ef0a9b..2a9bb8d 100644
--- a/src/goTools.ts
+++ b/src/goTools.ts
@@ -5,15 +5,45 @@
 
 'use strict';
 
-import { SemVer } from 'semver';
+import cp = require('child_process');
+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 +59,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 +92,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 {
@@ -146,13 +180,30 @@
 		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 +293,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 +325,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.0'),
+		latestVersionTimestamp: moment('2020-04-08', 'YYYY-MM-DD'),
+		latestPrereleaseVersion: semver.coerce('0.4.1-pre2'),
+		latestPrereleaseVersionTimestamp: moment('2020-05-11', 'YYYY-MM-DD'),
 	},
 	'dlv': {
 		name: 'dlv',
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
index ad66384..7bf6d49 100644
--- a/src/telemetry.ts
+++ b/src/telemetry.ts
@@ -3,191 +3,6 @@
  * Licensed under the MIT License. See LICENSE in the project root for license information.
  *--------------------------------------------------------*/
 
-import vscode = require('vscode');
-import TelemetryReporter from 'vscode-extension-telemetry';
+// TODO(hyangah): move this somewhere else (where easily modifiable during release process)
 
-export const extensionId: string = 'ms-vscode.Go';
-const extension = vscode.extensions.getExtension(extensionId);
-const extensionVersion: string = extension ? extension.packageJSON.version : '';
-const aiKey: string = 'AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217';
-
-export function sendTelemetryEventForModulesUsage() {
-	/* __GDPR__
-		"modules" : {}
-	*/
-	sendTelemetryEvent('modules');
-}
-
-export function sendTelemetryEventForAddImportCmd(arg: { importPath: string; from: string }) {
-	/* __GDPR__
-		"addImportCmd" : {
-			"from" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
-		}
-	*/
-	sendTelemetryEvent('addImportCmd', { from: (arg && arg.from) || 'cmd' });
-}
-
-export function sendTelemetryEventForGopkgs(timeTaken: number) {
-	/* __GDPR__
-		"gopkgs" : {
-			"tool" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"timeTaken": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }
-		}
-	*/
-	sendTelemetryEvent('gopkgs', {}, { timeTaken });
-}
-
-export function sendTelemetryEventForFormatting(formatTool: string, timeTaken: number) {
-	/* __GDPR__
-		"format" : {
-			"tool" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"timeTaken": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }
-		}
-	*/
-	sendTelemetryEvent('format', { tool: formatTool }, { timeTaken });
-}
-
-export function sendTelemetryEventForDebugConfiguration(debugConfiguration: vscode.DebugConfiguration) {
-	/* __GDPR__
-		"debugConfiguration" : {
-			"request" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"mode" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"useApiV<NUMBER>": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"apiVersion": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"stopOnEntry": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
-		}
-	*/
-	sendTelemetryEvent('debugConfiguration', {
-		request: debugConfiguration.request,
-		mode: debugConfiguration.mode,
-		useApiV1: debugConfiguration.useApiV1,
-		apiVersion: debugConfiguration.apiVersion,
-		stopOnEntry: debugConfiguration.stopOnEntry
-	});
-}
-
-export function sendTelemetryEventForConfig(goConfig: vscode.WorkspaceConfiguration) {
-	/* __GDPR__
-		"goConfig" : {
-			"buildOnSave" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"buildFlags": { "classification": "CustomerContent", "purpose": "FeatureInsight" },
-			"buildTags": { "classification": "CustomerContent", "purpose": "FeatureInsight" },
-			"formatTool": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"formatFlags": { "classification": "CustomerContent", "purpose": "FeatureInsight" },
-			"generateTestsFlags": { "classification": "CustomerContent", "purpose": "FeatureInsight" },
-			"lintOnSave": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"lintFlags": { "classification": "CustomerContent", "purpose": "FeatureInsight" },
-			"lintTool": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"vetOnSave": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"vetFlags": { "classification": "CustomerContent", "purpose": "FeatureInsight" },
-			"testOnSave": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"testFlags": { "classification": "CustomerContent", "purpose": "FeatureInsight" },
-			"coverOnSave": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"coverOnTestPackage": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"coverageDecorator": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"coverageOptions": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"gopath": { "classification": "CustomerContent", "purpose": "FeatureInsight" },
-			"goroot": { "classification": "CustomerContent", "purpose": "FeatureInsight" },
-			"inferGopath": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"toolsGopath": { "classification": "CustomerContent", "purpose": "FeatureInsight" },
-			"gocodeAutoBuild": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"gocodePackageLookupMode": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"useCodeSnippetsOnFunctionSuggest": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"useCodeSnippetsOnFunctionSuggestWithoutType": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"autocompleteUnimportedPackages": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"docsTool": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"useLanguageServer": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"languageServerExperimentalFeatures": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"includeImports": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"addTags": { "classification": "CustomerContent", "purpose": "FeatureInsight" },
-			"removeTags": { "classification": "CustomerContent", "purpose": "FeatureInsight" },
-			"editorContextMenuCommands": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"liveErrors": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"codeLens": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
-			"alternateTools": { "classification": "CustomerContent", "purpose": "FeatureInsight" },
-			"useGoProxyToCheckForToolUpdates": { "classification": "CustomerContent", "purpose": "FeatureInsight" }
-		}
-	*/
-	sendTelemetryEvent('goConfig', {
-		buildOnSave: goConfig['buildOnSave'] + '',
-		buildFlags: goConfig['buildFlags'],
-		buildTags: goConfig['buildTags'],
-		formatOnSave: goConfig['formatOnSave'] + '',
-		formatTool: goConfig['formatTool'],
-		formatFlags: goConfig['formatFlags'],
-		lintOnSave: goConfig['lintOnSave'] + '',
-		lintFlags: goConfig['lintFlags'],
-		lintTool: goConfig['lintTool'],
-		generateTestsFlags: goConfig['generateTestsFlags'],
-		vetOnSave: goConfig['vetOnSave'] + '',
-		vetFlags: goConfig['vetFlags'],
-		testOnSave: goConfig['testOnSave'] + '',
-		testFlags: goConfig['testFlags'],
-		coverOnSave: goConfig['coverOnSave'] + '',
-		coverOnTestPackage: goConfig['coverOnTestPackage'] + '',
-		coverageDecorator: goConfig['coverageDecorator'],
-		coverageOptions: goConfig['coverageOptions'],
-		gopath: goConfig['gopath'] ? 'set' : '',
-		goroot: goConfig['goroot'] ? 'set' : '',
-		inferGopath: goConfig['inferGopath'] + '',
-		toolsGopath: goConfig['toolsGopath'] ? 'set' : '',
-		gocodeAutoBuild: goConfig['gocodeAutoBuild'] + '',
-		gocodePackageLookupMode: goConfig['gocodePackageLookupMode'] + '',
-		useCodeSnippetsOnFunctionSuggest: goConfig['useCodeSnippetsOnFunctionSuggest'] + '',
-		useCodeSnippetsOnFunctionSuggestWithoutType: goConfig['useCodeSnippetsOnFunctionSuggestWithoutType'] + '',
-		autocompleteUnimportedPackages: goConfig['autocompleteUnimportedPackages'] + '',
-		docsTool: goConfig['docsTool'],
-		useLanguageServer: goConfig['useLanguageServer'] + '',
-		languageServerExperimentalFeatures: JSON.stringify(goConfig['languageServerExperimentalFeatures']),
-		includeImports: goConfig['gotoSymbol'] && goConfig['gotoSymbol']['includeImports'] + '',
-		addTags: JSON.stringify(goConfig['addTags']),
-		removeTags: JSON.stringify(goConfig['removeTags']),
-		editorContextMenuCommands: JSON.stringify(goConfig['editorContextMenuCommands']),
-		liveErrors: JSON.stringify(goConfig['liveErrors']),
-		codeLens: JSON.stringify(goConfig['enableCodeLens']),
-		alternateTools: JSON.stringify(goConfig['alternateTools']),
-		useGoProxyToCheckForToolUpdates: goConfig['useGoProxyToCheckForToolUpdates'] + ''
-	});
-}
-
-export function sendTelemetryEventForKillingProcess(msg: any, matches: any) {
-	/* __GDPR__
-		"errorKillingProcess" : {
-			"message" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
-			"stack": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }
-		}
-	*/
-	sendTelemetryEvent('errorKillingProcess', { message: msg, stack: matches });
-}
-
-export function sendTelemetryEventForGoVersion(goVersion: string) {
-	/* __GDPR__
-		"getGoVersion" : {
-			"version" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
-		}
-	*/
-	sendTelemetryEvent('getGoVersion', { version: `${goVersion}` });
-}
-
-export function disposeTelemetryReporter(): Promise<any> {
-	if (telemtryReporter) {
-		return telemtryReporter.dispose();
-	}
-	return Promise.resolve(null);
-}
-
-let telemtryReporter: TelemetryReporter;
-
-function sendTelemetryEvent(
-	eventName: string,
-	properties?: { [key: string]: string },
-	measures?: { [key: string]: number }
-): void {
-	if (!aiKey) {
-		return; // cannot enable telemetry
-	}
-	telemtryReporter = telemtryReporter
-		? telemtryReporter
-		: new TelemetryReporter(extensionId, extensionVersion, aiKey);
-	telemtryReporter.sendTelemetryEvent(eventName, properties, measures);
-}
+export const extensionId: string = 'golang.go-nightly';
diff --git a/src/testUtils.ts b/src/testUtils.ts
index cfeb493..4bba6f2 100644
--- a/src/testUtils.ts
+++ b/src/testUtils.ts
@@ -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));
 }
 
 /**
diff --git a/src/util.ts b/src/util.ts
index 25ce19e..19e87a0 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -21,7 +21,7 @@
 	resolveHomeDir
 } from './goPath';
 import { outputChannel } from './goStatus';
-import { extensionId, sendTelemetryEventForGoVersion, sendTelemetryEventForKillingProcess } from './telemetry';
+import { extensionId } from './telemetry';
 
 let userNameHash: number = 0;
 
@@ -90,7 +90,6 @@
 			this.isDevel = true;
 			this.commit = matchesDevel[0];
 		}
-		sendTelemetryEventForGoVersion(this.format());
 	}
 
 	public format(): string {
@@ -292,7 +291,6 @@
 		return Promise.resolve(null);
 	}
 	if (cachedGoVersion && (cachedGoVersion.sv || cachedGoVersion.isDevel)) {
-		sendTelemetryEventForGoVersion(cachedGoVersion.format());
 		return Promise.resolve(cachedGoVersion);
 	}
 	return new Promise<GoVersion>((resolve) => {
@@ -330,7 +328,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;
@@ -867,12 +865,6 @@
 			p.kill();
 		} catch (e) {
 			console.log('Error killing process: ' + e);
-			if (e && e.message && e.stack) {
-				const matches = e.stack.match(/(src.go[a-z,A-Z]+\.js)/g);
-				if (matches) {
-					sendTelemetryEventForKillingProcess(e.message, matches);
-				}
-			}
 		}
 	}
 }
@@ -905,9 +897,14 @@
 		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(err);
+				}
+
 			}
 		});
 		fs.rmdirSync(dir);
diff --git a/test/gopls/extension.test.ts b/test/gopls/extension.test.ts
index bde1fd8..2e3705d 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';
 
 // Env is a collection of test related variables
 // that define the test environment such as vscode workspace.
@@ -65,7 +63,7 @@
 					files.filter((filename) => filename !== '.gitignore').map((file) => {
 						fs.remove(path.resolve(this.workspaceDir, file));
 					}));
-				});
+			});
 
 			if (!fixtureDirName) {
 				return;
@@ -136,4 +134,33 @@
 		});
 		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/update.test.ts b/test/gopls/update.test.ts
new file mode 100644
index 0000000..31f9ed9
--- /dev/null
+++ b/test/gopls/update.test.ts
@@ -0,0 +1,93 @@
+/*---------------------------------------------------------
+ * 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 semver = require('semver');
+import sinon = require('sinon');
+import lsp = require('../../src/goLanguageServer');
+import { getTool, Tool } from '../../src/goTools';
+
+suite('gopls update tests', () => {
+	test('prompt for update', () => {
+		const tool = getTool('gopls');
+		const testCases: [string, string, boolean, semver.SemVer][] = [
+			['outdated, tagged', 'v0.3.1', false, tool.latestVersion],
+			['outdated, tagged (pre-release)', '0.3.1', true, tool.latestPrereleaseVersion],
+			['up-to-date, tagged', 'v0.4.0', false, null],
+			['up-to-date tagged (pre-release)', 'v0.4.0', true, tool.latestPrereleaseVersion],
+			['developer version', '(devel)', false, null],
+			['developer version (pre-release)', '(devel)', true, null],
+			['nonsense version', 'nosuchversion', false, null],
+			['nonsense version (pre-release)', 'nosuchversion', true, null],
+			[
+				'latest pre-release',
+				'v0.4.1-pre1 h1:w6e4AmFe6sDSVrgaRkf4WqLyVAlByUrr9QM5xH7z1e4=',
+				false, null,
+			],
+			[
+				'latest pre-release (pre-release)',
+				'v0.4.1-pre1 h1:w6e4AmFe6sDSVrgaRkf4WqLyVAlByUrr9QM5xH7z1e4=',
+				true, null,
+			],
+			[
+				'outdated pre-release version',
+				'v0.3.1-pre1 h1:pBnJjmdcHy5AiRJleOWaakxFHykf8uXzSZKQMd0EA0Q=',
+				false, tool.latestVersion,
+			],
+			[
+				'outdated pre-release version (pre-release)',
+				'v0.3.1-pre1 h1:pBnJjmdcHy5AiRJleOWaakxFHykf8uXzSZKQMd0EA0Q=',
+				true, tool.latestPrereleaseVersion,
+			],
+			[
+				'recent pseudoversion after pre-release',
+				'v0.0.0-20200509030707-2212a7e161a5 h1:0gSpZ0Z2URJoo3oilGRq9ViMLDTlmNSDCyeZNHHrvd4=',
+				false, null,
+			],
+			[
+				'recent pseudoversion before pre-release',
+				'v0.0.0-20200501030707-2212a7e161a5 h1:0gSpZ0Z2URJoo3oilGRq9ViMLDTlmNSDCyeZNHHrvd4=',
+				false, null,
+			],
+			[
+				'recent pseudoversion before pre-release (pre-release)',
+				'v0.0.0-20200501030707-2212a7e161a5 h1:0gSpZ0Z2URJoo3oilGRq9ViMLDTlmNSDCyeZNHHrvd4=',
+				true, tool.latestPrereleaseVersion,
+			],
+			[
+				'outdated pseudoversion',
+				'v0.0.0-20200309030707-2212a7e161a5 h1:0gSpZ0Z2URJoo3oilGRq9ViMLDTlmNSDCyeZNHHrvd4=',
+				false, tool.latestVersion,
+			],
+			[
+				'outdated pseudoversion (pre-release)',
+				'v0.0.0-20200309030707-2212a7e161a5 h1:0gSpZ0Z2URJoo3oilGRq9ViMLDTlmNSDCyeZNHHrvd4=',
+				true, tool.latestPrereleaseVersion,
+			],
+		];
+		testCases.map(async ([name, usersVersion, acceptPrerelease, want], i) => {
+			sinon.replace(lsp, 'getLocalGoplsVersion', async () => {
+				return usersVersion;
+			});
+			sinon.replace(lsp, 'getLatestGoplsVersion', async () => {
+				if (acceptPrerelease) {
+					return tool.latestPrereleaseVersion;
+				}
+				return tool.latestVersion;
+			});
+			sinon.replace(lsp, 'getTimestampForVersion', async (_: Tool, version: semver.SemVer) => {
+				if (version === tool.latestVersion) {
+					return tool.latestVersionTimestamp;
+				}
+				if (version === tool.latestPrereleaseVersion) {
+					return tool.latestPrereleaseVersionTimestamp;
+				}
+			});
+			const got = await lsp.shouldUpdateLanguageServer(tool, 'bad/path/to/gopls', true);
+			assert.equal(got, want, `${name}@${i} failed`);
+			sinon.restore();
+		});
+	});
+});
diff --git a/test/integration/extension.test.ts b/test/integration/extension.test.ts
index 34569ef..63043d5 100644
--- a/test/integration/extension.test.ts
+++ b/test/integration/extension.test.ts
@@ -32,6 +32,7 @@
 import {
 	getBinPath,
 	getCurrentGoPath,
+	getGoConfig,
 	getGoVersion,
 	getImportPath,
 	getToolsGopath,
@@ -1310,7 +1311,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 +1436,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..3fae910
--- /dev/null
+++ b/test/integration/goDebug.test.ts
@@ -0,0 +1,263 @@
+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', () => {
+	const workspaceFolder = '/usr/workspacefolder';
+	const delve: Delve = {} as Delve;
+	const previousGoPath = process.env.GOPATH;
+	const previousGoRoot = process.env.GOROOT;
+
+	let goDebugSession: GoDebugSession;
+	let remoteSourcesAndPackages: RemoteSourcesAndPackages;
+	let fileSystem: typeof fs;
+	setup(() => {
+		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.GOPATH = previousGoPath;
+		process.env.GOROOT = previousGoRoot;
+		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..12e0292 100644
--- a/test/integration/index.ts
+++ b/test/integration/index.ts
@@ -8,7 +8,7 @@
 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..4c73e47
--- /dev/null
+++ b/test/integration/install.test.ts
@@ -0,0 +1,102 @@
+/*---------------------------------------------------------
+ * 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 { installTools } from '../../src/goInstallTools';
+import { getTool, getToolAtVersion } from '../../src/goTools';
+import { getBinPath, getGoVersion, rmdirRecursive } from '../../src/util';
+
+suite('Installation Tests', () => {
+	test('install tools', async () => {
+		const goVersion = await getGoVersion();
+		const testCases: string[][] = [
+			['gopls'],
+			['gopls', 'guru'],
+		];
+		const proxyDir = buildFakeProxy([].concat(...testCases));
+
+		for (const missing of testCases) {
+			// Create a temporary directory in which to install tools.
+			const tmpToolsGopath = fs.mkdtempSync(path.join(os.tmpdir(), 'install-test'));
+			fs.mkdirSync(path.join(tmpToolsGopath, 'bin'));
+			fs.mkdirSync(path.join(tmpToolsGopath, 'src'));
+
+			const sandbox = sinon.createSandbox();
+			const utils = require('../../src/util');
+			const toolsGopathStub = sandbox.stub(utils, 'getToolsGopath').returns(tmpToolsGopath);
+			const goConfig = Object.create(vscode.workspace.getConfiguration('go'), {
+				toolsEnvVars: {
+					value: {
+						GOPROXY: `file://${proxyDir}`,
+						GOSUMDB: 'off',
+					}
+				},
+			});
+			const configStub = sandbox.stub(vscode.workspace, 'getConfiguration').returns(goConfig);
+			// TODO(rstambler): Test with versions as well.
+			const missingTools = missing.map((tool) => getToolAtVersion(tool));
+			await installTools(missingTools, goVersion);
+
+			sinon.assert.calledWith(toolsGopathStub);
+			sinon.assert.calledWith(configStub);
+			sandbox.restore();
+
+			// Read the $GOPATH/bin to confirm that the expected tools were
+			// installed.
+			const readdir = util.promisify(fs.readdir);
+			const files = await readdir(path.join(tmpToolsGopath, 'bin'));
+			assert.deepEqual(files, missing, `tool installation failed for ${missing}`);
+
+			// 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);
+		}
+
+		rmdirRecursive(proxyDir);
+	});
+});
+
+// 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;
+}
diff --git a/test/runTest.ts b/test/runTest.ts
index ee29d48..b6dcd8a 100644
--- a/test/runTest.ts
+++ b/test/runTest.ts
@@ -8,6 +8,8 @@
 		// Passed to `--extensionDevelopmentPath`
 	const extensionDevelopmentPath = path.resolve(__dirname, '../../');
 
+	let failed = false;
+
 	try {
 		// The path to the extension test script
 		// Passed to --extensionTestsPath
@@ -17,7 +19,7 @@
 		await runTests({ extensionDevelopmentPath, extensionTestsPath });
 	} catch (err) {
 		console.error('Failed to run integration tests' + err);
-		process.exit(1);
+		failed = true;
 	}
 
 	// Integration tests using gopls.
@@ -40,6 +42,11 @@
 		});
 	} catch (err) {
 		console.error('Failed to run gopls tests' + err);
+		// failed = true; TODO(hyangah): reenable this after golang.org/cl/233517
+	}
+
+	if (failed) {
+		process.exit(1);
 	}
 }