[release.0.4] all: merge master into gopls-release-branch.0.4

Change-Id: I5dac68f22ca1cf574238b0b217658abf7cccf5f8
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..1502887
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,4 @@
+{
+  "singleQuote": true,
+  "trailingComma": "es5"
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index 20be9e1..8e7f941 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,15 @@
 The easiest way to install is to run `go get -u golang.org/x/tools/...`. You can
 also manually git clone the repository to `$GOPATH/src/golang.org/x/tools`.
 
+## JS/CSS Formatting
+
+This repository uses [prettier](https://prettier.io/) to format JS and CSS files.
+
+The version of `prettier` used is 1.18.2.
+
+It is encouraged that all JS and CSS code be run through this before submitting
+a change. However, it is not a strict requirement enforced by CI.
+
 ## Report Issues / Send Patches
 
 This repository uses Gerrit for code changes. To learn how to submit changes to
diff --git a/cmd/cover/README b/cmd/cover/README
deleted file mode 100644
index ff9523d..0000000
--- a/cmd/cover/README
+++ /dev/null
@@ -1,2 +0,0 @@
-NOTE: For Go releases 1.5 and later, this tool lives in the
-standard repository. The code here is not maintained.
diff --git a/cmd/cover/README.md b/cmd/cover/README.md
new file mode 100644
index 0000000..62e6027
--- /dev/null
+++ b/cmd/cover/README.md
@@ -0,0 +1,3 @@
+# Deprecated
+
+NOTE: For Go releases 1.5 and later, this tool lives in the standard repository. The code here is not maintained.
diff --git a/cmd/cover/doc.go b/cmd/cover/doc.go
index b74d5b3..f903d85 100644
--- a/cmd/cover/doc.go
+++ b/cmd/cover/doc.go
@@ -6,6 +6,9 @@
 Cover is a program for analyzing the coverage profiles generated by
 'go test -coverprofile=cover.out'.
 
+Deprecated: For Go releases 1.5 and later, this tool lives in the
+standard repository. The code here is not maintained.
+
 Cover is also used by 'go test -cover' to rewrite the source code with
 annotations to track which parts of each function are executed.
 It operates on one Go source file at a time, computing approximate
@@ -17,11 +20,5 @@
 For usage information, please see:
 	go help testflag
 	go tool cover -help
-
-No longer maintained:
-
-For Go releases 1.5 and later, this tool lives in the
-standard repository. The code here is not maintained.
-
 */
 package main // import "golang.org/x/tools/cmd/cover"
diff --git a/cmd/godoc/main.go b/cmd/godoc/main.go
index 48a658e..7dba4d2 100644
--- a/cmd/godoc/main.go
+++ b/cmd/godoc/main.go
@@ -19,6 +19,7 @@
 import (
 	"archive/zip"
 	"bytes"
+	"context"
 	"encoding/json"
 	_ "expvar" // to serve /debug/vars
 	"flag"
@@ -44,6 +45,7 @@
 	"golang.org/x/tools/godoc/vfs/gatefs"
 	"golang.org/x/tools/godoc/vfs/mapfs"
 	"golang.org/x/tools/godoc/vfs/zipfs"
+	"golang.org/x/tools/internal/gocommand"
 	"golang.org/x/xerrors"
 )
 
@@ -210,28 +212,48 @@
 			usage()
 		}
 
-		// Try to download dependencies that are not in the module cache in order to
-		// to show their documentation.
-		// This may fail if module downloading is disallowed (GOPROXY=off) or due to
-		// limited connectivity, in which case we print errors to stderr and show
-		// documentation only for packages that are available.
-		fillModuleCache(os.Stderr, goModFile)
-
-		// Determine modules in the build list.
-		mods, err := buildList(goModFile)
+		// Detect whether to use vendor mode or not.
+		mainMod, vendorEnabled, err := gocommand.VendorEnabled(context.Background(), gocommand.Invocation{}, &gocommand.Runner{})
 		if err != nil {
-			fmt.Fprintf(os.Stderr, "failed to determine the build list of the main module: %v", err)
+			fmt.Fprintf(os.Stderr, "failed to determine if vendoring is enabled: %v", err)
 			os.Exit(1)
 		}
+		if vendorEnabled {
+			// Bind the root directory of the main module.
+			fs.Bind(path.Join("/src", mainMod.Path), gatefs.New(vfs.OS(mainMod.Dir), fsGate), "/", vfs.BindAfter)
 
-		// Bind module trees into Go root.
-		for _, m := range mods {
-			if m.Dir == "" {
-				// Module is not available in the module cache, skip it.
-				continue
+			// Bind the vendor directory.
+			//
+			// Note that in module mode, vendor directories in locations
+			// other than the main module's root directory are ignored.
+			// See https://golang.org/ref/mod#vendoring.
+			vendorDir := filepath.Join(mainMod.Dir, "vendor")
+			fs.Bind("/src", gatefs.New(vfs.OS(vendorDir), fsGate), "/", vfs.BindAfter)
+
+		} else {
+			// Try to download dependencies that are not in the module cache in order to
+			// to show their documentation.
+			// This may fail if module downloading is disallowed (GOPROXY=off) or due to
+			// limited connectivity, in which case we print errors to stderr and show
+			// documentation only for packages that are available.
+			fillModuleCache(os.Stderr, goModFile)
+
+			// Determine modules in the build list.
+			mods, err := buildList(goModFile)
+			if err != nil {
+				fmt.Fprintf(os.Stderr, "failed to determine the build list of the main module: %v", err)
+				os.Exit(1)
 			}
-			dst := path.Join("/src", m.Path)
-			fs.Bind(dst, gatefs.New(vfs.OS(m.Dir), fsGate), "/", vfs.BindAfter)
+
+			// Bind module trees into Go root.
+			for _, m := range mods {
+				if m.Dir == "" {
+					// Module is not available in the module cache, skip it.
+					continue
+				}
+				dst := path.Join("/src", m.Path)
+				fs.Bind(dst, gatefs.New(vfs.OS(m.Dir), fsGate), "/", vfs.BindAfter)
+			}
 		}
 	} else {
 		fmt.Println("using GOPATH mode")
@@ -395,7 +417,7 @@
 // with all dependencies of the main module in the current directory
 // by invoking the go command. Module download logs are streamed to w.
 // If there are any problems encountered, they are also written to w.
-// It should only be used when operating in module mode.
+// It should only be used in module mode, when vendor mode isn't on.
 //
 // See https://golang.org/cmd/go/#hdr-Download_modules_to_local_cache.
 func fillModuleCache(w io.Writer, goMod string) {
@@ -436,9 +458,14 @@
 	}
 }
 
+type mod struct {
+	Path string // Module path.
+	Dir  string // Directory holding files for this module, if any.
+}
+
 // buildList determines the build list in the current directory
-// by invoking the go command. It should only be used when operating
-// in module mode.
+// by invoking the go command. It should only be used in module mode,
+// when vendor mode isn't on.
 //
 // See https://golang.org/cmd/go/#hdr-The_main_module_and_the_build_list.
 func buildList(goMod string) ([]mod, error) {
@@ -467,11 +494,6 @@
 	return mods, nil
 }
 
-type mod struct {
-	Path string // Module path.
-	Dir  string // Directory holding files for this module, if any.
-}
-
 // moduleFS is a vfs.FileSystem wrapper used when godoc is running
 // in module mode. It's needed so that packages inside modules are
 // considered to be third party.
diff --git a/cmd/gorename/cgo_test.go b/cmd/gorename/cgo_test.go
deleted file mode 100644
index fd07553..0000000
--- a/cmd/gorename/cgo_test.go
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2017 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build cgo
-
-package main_test
-
-func init() {
-	haveCGO = true
-}
diff --git a/cmd/gorename/gorename_test.go b/cmd/gorename/gorename_test.go
index 3351977..2928051 100644
--- a/cmd/gorename/gorename_test.go
+++ b/cmd/gorename/gorename_test.go
@@ -18,8 +18,6 @@
 	"golang.org/x/tools/internal/testenv"
 )
 
-var haveCGO bool
-
 type test struct {
 	offset, from, to string // specify the arguments
 	fileSpecified    bool   // true if the offset or from args specify a specific file
@@ -31,10 +29,8 @@
 
 // Test that renaming that would modify cgo files will produce an error and not modify the file.
 func TestGeneratedFiles(t *testing.T) {
-	if !haveCGO {
-		t.Skipf("skipping test: no cgo")
-	}
 	testenv.NeedsTool(t, "go")
+	testenv.NeedsTool(t, "cgo")
 
 	tmp, bin, cleanup := buildGorename(t)
 	defer cleanup()
diff --git a/cmd/present/static/article.css b/cmd/present/static/article.css
index 7b30455..52fd737 100644
--- a/cmd/present/static/article.css
+++ b/cmd/present/static/article.css
@@ -1,161 +1,179 @@
 body {
-	margin: 0;
-	font-family: Helvetica, Arial, sans-serif;
-	font-size: 16px;
+  margin: 0;
+  font-family: Helvetica, Arial, sans-serif;
+  font-size: 16px;
 }
 pre,
 code {
-	font-family: Menlo, monospace;
-	font-size: 14px;
+  font-family: Menlo, monospace;
+  font-size: 14px;
 }
 pre {
-	line-height: 18px;
-	margin: 0;
-	padding: 0;
+  line-height: 18px;
+  margin: 0;
+  padding: 0;
 }
 a {
-	color: #375EAB;
-	text-decoration: none;
+  color: #375eab;
+  text-decoration: none;
 }
 a:hover {
-	text-decoration: underline;
+  text-decoration: underline;
 }
-p, ul, ol {
-	margin: 20px;
+p,
+ul,
+ol {
+  margin: 20px;
 }
 
-h1, h2, h3, h4 {
-	margin: 20px 0;
-	padding: 0;
-	color: #375EAB;
-	font-weight: bold;
+h1,
+h2,
+h3,
+h4 {
+  margin: 20px 0;
+  padding: 0;
+  color: #375eab;
+  font-weight: bold;
 }
 h1 {
-	font-size: 18px;
-	padding: 2px 5px;
+  font-size: 18px;
+  padding: 2px 5px;
 }
 h2 {
-	font-size: 16px;
+  font-size: 16px;
 }
 h3 {
-	font-size: 16px;
+  font-size: 16px;
 }
-h3, h4 {
-	margin: 20px 5px;
+h3,
+h4 {
+  margin: 20px 5px;
 }
 h4 {
-	font-size: 16px;
+  font-size: 16px;
 }
 
 div#heading {
-	margin: 0 0 10px 0;
-	padding: 21px 0;
-	font-size: 20px;
-	font-weight: bold;
+  margin: 0 0 10px 0;
+  padding: 21px 0;
+  font-size: 20px;
+  font-weight: bold;
 }
 
 div#heading .author {
-	padding-top: 10px;
-	font-size: 14px;
-	font-weight: normal;
+  padding-top: 10px;
+  font-size: 14px;
+  font-weight: normal;
 }
 
 div#topbar {
 }
 
 body {
-	text-align: center;
+  text-align: center;
 }
 div#page {
-	width: 100%;
+  width: 100%;
 }
 div#page > .container,
 div#topbar > .container {
-	text-align: left;
-	margin-left: auto;
-	margin-right: auto;
-	padding: 0 20px;
-	width: 900px;
+  text-align: left;
+  margin-left: auto;
+  margin-right: auto;
+  padding: 0 20px;
+  width: 900px;
 }
 div#page.wide > .container,
 div#topbar.wide > .container {
-	width: auto;
+  width: auto;
 }
 
 div#footer {
-	text-align: center;
-	color: #666;
-	font-size: 14px;
-	margin: 40px 0;
+  text-align: center;
+  color: #666;
+  font-size: 14px;
+  margin: 40px 0;
 }
 
 .author p {
-	margin: 0;
-	padding: 0 20px;
+  margin: 0;
+  padding: 0 20px;
 }
 
 div.code,
 div.output {
-	margin: 20px 20px 20px 40px;
-	-webkit-border-radius: 5px;
-	-moz-border-radius: 5px;
-	border-radius: 5px;
+  margin: 20px 20px 20px 40px;
+  -webkit-border-radius: 5px;
+  -moz-border-radius: 5px;
+  border-radius: 5px;
 }
 
 div.output {
-	padding: 10px;
+  padding: 10px;
 }
 
-div.code { background: white; }
-div.output { background: black; }
-div.output .stdout { color: #e6e6e6; }
-div.output .stderr { color: rgb(244, 74, 63); }
-div.output .system { color: rgb(255, 209, 77) }
+div.code {
+  background: white;
+}
+div.output {
+  background: black;
+}
+div.output .stdout {
+  color: #e6e6e6;
+}
+div.output .stderr {
+  color: rgb(244, 74, 63);
+}
+div.output .system {
+  color: rgb(255, 209, 77);
+}
 
 .buttons {
-	margin-left: 20px;
+  margin-left: 20px;
 }
 div.output .buttons {
-	margin-left: 0;
-	margin-bottom: 10px;
+  margin-left: 0;
+  margin-bottom: 10px;
 }
 
 #toc {
-	float: right;
-	margin: 0px 10px;
-	padding: 10px;
-	border: 1px solid #e5ecf9; 
-	background-color: #eee;
-	box-shadow: 3px 3px 2px #888888;
+  float: right;
+  margin: 0px 10px;
+  padding: 10px;
+  border: 1px solid #e5ecf9;
+  background-color: #eee;
+  box-shadow: 3px 3px 2px #888888;
 
-	max-width: 33%;
+  max-width: 33%;
 
-	-webkit-border-radius: 5px;
-	-moz-border-radius: 5px;
-	border-radius: 5px;
+  -webkit-border-radius: 5px;
+  -moz-border-radius: 5px;
+  border-radius: 5px;
 }
 
 #tochead {
-	font-weight: bold;
-	font-variant: small-caps;
-	font-size: 100%;
-	text-align: center;
-	padding-bottom: 5px;
+  font-weight: bold;
+  font-variant: small-caps;
+  font-size: 100%;
+  text-align: center;
+  padding-bottom: 5px;
 }
 
-#toc ul, #toc a {
-	list-style-type: none;
-	padding-left: 0px;
-	color: black;
-	margin: 0px;
+#toc ul,
+#toc a {
+  list-style-type: none;
+  padding-left: 0px;
+  color: black;
+  margin: 0px;
 }
 
 ul.toc-inner a {
-	padding-left: 10px !important;
+  padding-left: 10px !important;
 }
 
 @media print {
-	.no-print, .no-print * {
-		display: none !important;
-	}
+  .no-print,
+  .no-print * {
+    display: none !important;
+  }
 }
diff --git a/cmd/present/static/dir.css b/cmd/present/static/dir.css
index 97587e6..22c160d 100644
--- a/cmd/present/static/dir.css
+++ b/cmd/present/static/dir.css
@@ -1,59 +1,59 @@
 /* copied from $GOROOT/doc/style.css */
 
 body {
-	margin: 0;
-	font-family: Helvetica, Arial, sans-serif;
-	font-size: 16px;
+  margin: 0;
+  font-family: Helvetica, Arial, sans-serif;
+  font-size: 16px;
 }
 pre,
 code {
-	font-family: Menlo, monospace;
-	font-size: 14px;
+  font-family: Menlo, monospace;
+  font-size: 14px;
 }
 pre {
-	line-height: 18px;
+  line-height: 18px;
 }
 pre .comment {
-	color: #375EAB;
+  color: #375eab;
 }
 pre .highlight,
 pre .highlight-comment,
 pre .selection-highlight,
 pre .selection-highlight-comment {
-	background: #FFFF00;
+  background: #ffff00;
 }
 pre .selection,
 pre .selection-comment {
-	background: #FF9632;
+  background: #ff9632;
 }
 pre .ln {
-	color: #999;
+  color: #999;
 }
 body {
-	color: #222;
+  color: #222;
 }
 a,
 .exampleHeading .text {
-	color: #375EAB;
-	text-decoration: none;
+  color: #375eab;
+  text-decoration: none;
 }
 a:hover,
 .exampleHeading .text:hover {
-	text-decoration: underline;
+  text-decoration: underline;
 }
 p,
 pre,
 ul,
 ol {
-	margin: 20px;
+  margin: 20px;
 }
 pre {
-	background: #e9e9e9;
-	padding: 10px;
+  background: #e9e9e9;
+  padding: 10px;
 
-	-webkit-border-radius: 5px;
-	-moz-border-radius: 5px;
-	border-radius: 5px;
+  -webkit-border-radius: 5px;
+  -moz-border-radius: 5px;
+  border-radius: 5px;
 }
 
 h1,
@@ -61,126 +61,126 @@
 h3,
 h4,
 .rootHeading {
-	margin: 20px 0;
-	padding: 0;
-	color: #375EAB;
-	font-weight: bold;
+  margin: 20px 0;
+  padding: 0;
+  color: #375eab;
+  font-weight: bold;
 }
 h1 {
-	font-size: 24px;
+  font-size: 24px;
 }
 h2 {
-	font-size: 20px;
-	background: #E0EBF5;
-	padding: 2px 5px;
+  font-size: 20px;
+  background: #e0ebf5;
+  padding: 2px 5px;
 }
 h3 {
-	font-size: 20px;
+  font-size: 20px;
 }
 h3,
 h4 {
-	margin: 20px 5px;
+  margin: 20px 5px;
 }
 h4 {
-	font-size: 16px;
+  font-size: 16px;
 }
 
 dl {
-	margin: 20px;
+  margin: 20px;
 }
 dd {
-	margin: 2px 20px;
+  margin: 2px 20px;
 }
 dl,
 dd {
-	font-size: 14px;
+  font-size: 14px;
 }
 div#nav table td {
-	vertical-align: top;
+  vertical-align: top;
 }
 
 div#heading {
-	float: left;
-	margin: 0 0 10px 0;
-	padding: 21px 0;
-	font-size: 20px;
-	font-weight: normal;
+  float: left;
+  margin: 0 0 10px 0;
+  padding: 21px 0;
+  font-size: 20px;
+  font-weight: normal;
 }
 div#heading a {
-	color: #222;
-	text-decoration: none;
+  color: #222;
+  text-decoration: none;
 }
 
 div#topbar {
-	background: #E0EBF5;
-	height: 64px;
+  background: #e0ebf5;
+  height: 64px;
 }
 
 body {
-	text-align: center;
+  text-align: center;
 }
 div#page,
 div#topbar > .container {
-	clear: both;
-	text-align: left;
-	margin-left: auto;
-	margin-right: auto;
-	padding: 0 20px;
-	width: 900px;
+  clear: both;
+  text-align: left;
+  margin-left: auto;
+  margin-right: auto;
+  padding: 0 20px;
+  width: 900px;
 }
 div#page.wide,
 div#topbar > .wide {
-	width: auto;
+  width: auto;
 }
 div#plusone {
-	float: right;
+  float: right;
 }
 
 div#footer {
-	color: #666;
-	font-size: 14px;
-	margin: 40px 0;
+  color: #666;
+  font-size: 14px;
+  margin: 40px 0;
 }
 
 div#menu > a,
 div#menu > input {
-	padding: 10px;
+  padding: 10px;
 
-	text-decoration: none;
-	font-size: 16px;
+  text-decoration: none;
+  font-size: 16px;
 
-	-webkit-border-radius: 5px;
-	-moz-border-radius: 5px;
-	border-radius: 5px;
+  -webkit-border-radius: 5px;
+  -moz-border-radius: 5px;
+  border-radius: 5px;
 }
 div#menu > a,
 div#menu > input {
-	border: 1px solid #375EAB;
+  border: 1px solid #375eab;
 }
 div#menu > a {
-	color: white;
-	background: #375EAB;
+  color: white;
+  background: #375eab;
 }
 
 div#menu {
-	float: right;
-	min-width: 590px;
-	padding: 10px 0;
-	text-align: right;
+  float: right;
+  min-width: 590px;
+  padding: 10px 0;
+  text-align: right;
 }
 div#menu > a {
-	margin-right: 5px;
-	margin-bottom: 10px;
+  margin-right: 5px;
+  margin-bottom: 10px;
 
-	padding: 10px;
+  padding: 10px;
 }
 div#menu > input {
-	position: relative;
-	top: 1px;
-	width: 60px;
-	background: white;
-	color: #222;
+  position: relative;
+  top: 1px;
+  width: 60px;
+  background: white;
+  color: #222;
 }
 div#menu > input.inactive {
-	color: #999;
+  color: #999;
 }
diff --git a/cmd/present/static/dir.js b/cmd/present/static/dir.js
index 5b0c37e..00ad22c 100644
--- a/cmd/present/static/dir.js
+++ b/cmd/present/static/dir.js
@@ -5,10 +5,10 @@
 // copied from $GOROOT/doc/godocs.js
 
 function bindEvent(el, e, fn) {
-  if (el.addEventListener){
+  if (el.addEventListener) {
     el.addEventListener(e, fn, false);
-  } else if (el.attachEvent){
-    el.attachEvent('on'+e, fn);
+  } else if (el.attachEvent) {
+    el.attachEvent('on' + e, fn);
   }
 }
 
@@ -19,19 +19,19 @@
     return;
   }
   function clearInactive() {
-    if (search.className == "inactive") {
-      search.value = "";
-      search.className = "";
+    if (search.className == 'inactive') {
+      search.value = '';
+      search.className = '';
     }
   }
   function restoreInactive() {
-    if (search.value !== "") {
+    if (search.value !== '') {
       return;
     }
-    if (search.type != "search") {
-      search.value = search.getAttribute("placeholder");
+    if (search.type != 'search') {
+      search.value = search.getAttribute('placeholder');
     }
-    search.className = "inactive";
+    search.className = 'inactive';
   }
   restoreInactive();
   bindEvent(search, 'focus', clearInactive);
diff --git a/cmd/present/static/notes.css b/cmd/present/static/notes.css
index 0b44583..9a897be 100644
--- a/cmd/present/static/notes.css
+++ b/cmd/present/static/notes.css
@@ -8,7 +8,7 @@
   margin-left: -17px;
   position: fixed;
   border: 0;
-  width : 146%;
+  width: 146%;
   height: 750px;
 
   transform: scale(0.7, 0.7);
diff --git a/cmd/present/static/notes.js b/cmd/present/static/notes.js
index 0144848..a6d327f 100644
--- a/cmd/present/static/notes.js
+++ b/cmd/present/static/notes.js
@@ -12,8 +12,8 @@
   window.onbeforeunload = function() {
     localStorage.clear();
     if (notesWindow) notesWindow.close();
-  }
-};
+  };
+}
 
 function toggleNotesWindow() {
   if (!isParentWindow) return;
@@ -24,7 +24,7 @@
   }
 
   initNotes();
-};
+}
 
 // Create an unique key for the local storage so we don't mix the
 // destSlide of different presentations. For golang.org/issue/24688.
@@ -91,7 +91,7 @@
   // Add listener on notesWindow to update notes when triggered from
   // parent window
   w.addEventListener('storage', updateNotes, false);
-};
+}
 
 function formatNotes(notes) {
   var formattedNotes = '';
@@ -101,7 +101,7 @@
     }
   }
   return formattedNotes;
-};
+}
 
 function updateNotes() {
   // When triggered from parent window, notesWindow is null
@@ -120,13 +120,13 @@
   } else {
     el.innerHTML = '';
   }
-};
+}
 
 /* Playground syncing */
 
 // When presenter notes are enabled, playground click handlers are
 // stored here to sync click events on the correct playground
-var playgroundHandlers = {onRun: [], onKill: [], onClose: []};
+var playgroundHandlers = { onRun: [], onKill: [], onClose: [] };
 
 function updatePlay(e) {
   var i = localStorage.getItem('play-index');
@@ -150,7 +150,7 @@
       out.style = localStorage.getItem('output-style');
       return;
   }
-};
+}
 
 // Reset 'run', 'kill', 'close' storage items when synced
 // so that successive actions can be synced correctly
@@ -172,4 +172,4 @@
       localStorage.setItem('play-shiftKey', e.shiftKey);
     }
   }
-};
+}
diff --git a/cmd/present/static/slides.js b/cmd/present/static/slides.js
index 9289465..7c1229e 100644
--- a/cmd/present/static/slides.js
+++ b/cmd/present/static/slides.js
@@ -14,121 +14,121 @@
 /* classList polyfill by Eli Grey
  * (http://purl.eligrey.com/github/classList.js/blob/master/classList.js) */
 
-if (typeof document !== 'undefined' && !('classList' in document.createElement('a'))) {
-
-(function (view) {
-
-var
-    classListProp = 'classList'
-  , protoProp = 'prototype'
-  , elemCtrProto = (view.HTMLElement || view.Element)[protoProp]
-  , objCtr = Object
-    strTrim = String[protoProp].trim || function () {
-    return this.replace(/^\s+|\s+$/g, '');
-  }
-  , arrIndexOf = Array[protoProp].indexOf || function (item) {
-    for (var i = 0, len = this.length; i < len; i++) {
-      if (i in this && this[i] === item) {
-        return i;
-      }
-    }
-    return -1;
-  }
-  // Vendors: please allow content code to instantiate DOMExceptions
-  , DOMEx = function (type, message) {
-    this.name = type;
-    this.code = DOMException[type];
-    this.message = message;
-  }
-  , checkTokenAndGetIndex = function (classList, token) {
-    if (token === '') {
-      throw new DOMEx(
-          'SYNTAX_ERR'
-        , 'An invalid or illegal string was specified'
-      );
-    }
-    if (/\s/.test(token)) {
-      throw new DOMEx(
-          'INVALID_CHARACTER_ERR'
-        , 'String contains an invalid character'
-      );
-    }
-    return arrIndexOf.call(classList, token);
-  }
-  , ClassList = function (elem) {
-    var
-        trimmedClasses = strTrim.call(elem.className)
-      , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : []
-    ;
-    for (var i = 0, len = classes.length; i < len; i++) {
-      this.push(classes[i]);
-    }
-    this._updateClassName = function () {
-      elem.className = this.toString();
+if (
+  typeof document !== 'undefined' &&
+  !('classList' in document.createElement('a'))
+) {
+  (function(view) {
+    var classListProp = 'classList',
+      protoProp = 'prototype',
+      elemCtrProto = (view.HTMLElement || view.Element)[protoProp],
+      objCtr = Object;
+    (strTrim =
+      String[protoProp].trim ||
+      function() {
+        return this.replace(/^\s+|\s+$/g, '');
+      }),
+      (arrIndexOf =
+        Array[protoProp].indexOf ||
+        function(item) {
+          for (var i = 0, len = this.length; i < len; i++) {
+            if (i in this && this[i] === item) {
+              return i;
+            }
+          }
+          return -1;
+        }),
+      // Vendors: please allow content code to instantiate DOMExceptions
+      (DOMEx = function(type, message) {
+        this.name = type;
+        this.code = DOMException[type];
+        this.message = message;
+      }),
+      (checkTokenAndGetIndex = function(classList, token) {
+        if (token === '') {
+          throw new DOMEx(
+            'SYNTAX_ERR',
+            'An invalid or illegal string was specified'
+          );
+        }
+        if (/\s/.test(token)) {
+          throw new DOMEx(
+            'INVALID_CHARACTER_ERR',
+            'String contains an invalid character'
+          );
+        }
+        return arrIndexOf.call(classList, token);
+      }),
+      (ClassList = function(elem) {
+        var trimmedClasses = strTrim.call(elem.className),
+          classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [];
+        for (var i = 0, len = classes.length; i < len; i++) {
+          this.push(classes[i]);
+        }
+        this._updateClassName = function() {
+          elem.className = this.toString();
+        };
+      }),
+      (classListProto = ClassList[protoProp] = []),
+      (classListGetter = function() {
+        return new ClassList(this);
+      });
+    // Most DOMException implementations don't allow calling DOMException's toString()
+    // on non-DOMExceptions. Error's toString() is sufficient here.
+    DOMEx[protoProp] = Error[protoProp];
+    classListProto.item = function(i) {
+      return this[i] || null;
     };
-  }
-  , classListProto = ClassList[protoProp] = []
-  , classListGetter = function () {
-    return new ClassList(this);
-  }
-;
-// Most DOMException implementations don't allow calling DOMException's toString()
-// on non-DOMExceptions. Error's toString() is sufficient here.
-DOMEx[protoProp] = Error[protoProp];
-classListProto.item = function (i) {
-  return this[i] || null;
-};
-classListProto.contains = function (token) {
-  token += '';
-  return checkTokenAndGetIndex(this, token) !== -1;
-};
-classListProto.add = function (token) {
-  token += '';
-  if (checkTokenAndGetIndex(this, token) === -1) {
-    this.push(token);
-    this._updateClassName();
-  }
-};
-classListProto.remove = function (token) {
-  token += '';
-  var index = checkTokenAndGetIndex(this, token);
-  if (index !== -1) {
-    this.splice(index, 1);
-    this._updateClassName();
-  }
-};
-classListProto.toggle = function (token) {
-  token += '';
-  if (checkTokenAndGetIndex(this, token) === -1) {
-    this.add(token);
-  } else {
-    this.remove(token);
-  }
-};
-classListProto.toString = function () {
-  return this.join(' ');
-};
+    classListProto.contains = function(token) {
+      token += '';
+      return checkTokenAndGetIndex(this, token) !== -1;
+    };
+    classListProto.add = function(token) {
+      token += '';
+      if (checkTokenAndGetIndex(this, token) === -1) {
+        this.push(token);
+        this._updateClassName();
+      }
+    };
+    classListProto.remove = function(token) {
+      token += '';
+      var index = checkTokenAndGetIndex(this, token);
+      if (index !== -1) {
+        this.splice(index, 1);
+        this._updateClassName();
+      }
+    };
+    classListProto.toggle = function(token) {
+      token += '';
+      if (checkTokenAndGetIndex(this, token) === -1) {
+        this.add(token);
+      } else {
+        this.remove(token);
+      }
+    };
+    classListProto.toString = function() {
+      return this.join(' ');
+    };
 
-if (objCtr.defineProperty) {
-  var classListPropDesc = {
-      get: classListGetter
-    , enumerable: true
-    , configurable: true
-  };
-  try {
-    objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
-  } catch (ex) { // IE 8 doesn't support enumerable:true
-    if (ex.number === -0x7FF5EC54) {
-      classListPropDesc.enumerable = false;
-      objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
+    if (objCtr.defineProperty) {
+      var classListPropDesc = {
+        get: classListGetter,
+        enumerable: true,
+        configurable: true,
+      };
+      try {
+        objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
+      } catch (ex) {
+        // IE 8 doesn't support enumerable:true
+        if (ex.number === -0x7ff5ec54) {
+          classListPropDesc.enumerable = false;
+          objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
+        }
+      }
+    } else if (objCtr[protoProp].__defineGetter__) {
+      elemCtrProto.__defineGetter__(classListProp, classListGetter);
     }
-  }
-} else if (objCtr[protoProp].__defineGetter__) {
-  elemCtrProto.__defineGetter__(classListProp, classListGetter);
-}
-
-}(self));
-
+  })(self);
 }
 /* ---------------------------------------------------------------------- */
 
@@ -136,15 +136,15 @@
 
 function hideHelpText() {
   document.getElementById('help').style.display = 'none';
-};
+}
 
 function getSlideEl(no) {
-  if ((no < 0) || (no >= slideEls.length)) {
+  if (no < 0 || no >= slideEls.length) {
     return null;
   } else {
     return slideEls[no];
   }
-};
+}
 
 function updateSlideClass(slideNo, className) {
   var el = getSlideEl(slideNo);
@@ -162,7 +162,7 @@
       el.classList.remove(SLIDE_CLASSES[i]);
     }
   }
-};
+}
 
 function updateSlides() {
   if (window.trackPageview) window.trackPageview();
@@ -202,7 +202,7 @@
   enableSlideFrames(curSlide + 2);
 
   updateHash();
-};
+}
 
 function prevSlide() {
   hideHelpText();
@@ -213,7 +213,7 @@
   }
 
   if (notesEnabled) localStorage.setItem(destSlideKey(), curSlide);
-};
+}
 
 function nextSlide() {
   hideHelpText();
@@ -224,7 +224,7 @@
   }
 
   if (notesEnabled) localStorage.setItem(destSlideKey(), curSlide);
-};
+}
 
 /* Slide events */
 
@@ -244,7 +244,7 @@
   evt.slideNumber = no + 1; // Make it readable
 
   el.dispatchEvent(evt);
-};
+}
 
 function triggerLeaveEvent(no) {
   var el = getSlideEl(no);
@@ -262,7 +262,7 @@
   evt.slideNumber = no + 1; // Make it readable
 
   el.dispatchEvent(evt);
-};
+}
 
 /* Touch events */
 
@@ -277,7 +277,7 @@
     document.body.addEventListener('touchmove', handleTouchMove, true);
     document.body.addEventListener('touchend', handleTouchEnd, true);
   }
-};
+}
 
 function handleTouchMove(event) {
   if (event.touches.length > 1) {
@@ -287,13 +287,13 @@
     touchDY = event.touches[0].pageY - touchStartY;
     event.preventDefault();
   }
-};
+}
 
 function handleTouchEnd(event) {
   var dx = Math.abs(touchDX);
   var dy = Math.abs(touchDY);
 
-  if ((dx > PM_TOUCH_SENSITIVITY) && (dy < (dx * 2 / 3))) {
+  if (dx > PM_TOUCH_SENSITIVITY && dy < (dx * 2) / 3) {
     if (touchDX > 0) {
       prevSlide();
     } else {
@@ -302,12 +302,12 @@
   }
 
   cancelTouch();
-};
+}
 
 function cancelTouch() {
   document.body.removeEventListener('touchmove', handleTouchMove, true);
   document.body.removeEventListener('touchend', handleTouchEnd, true);
-};
+}
 
 /* Preloading frames */
 
@@ -318,10 +318,10 @@
   }
 
   var frames = el.getElementsByTagName('iframe');
-  for (var i = 0, frame; frame = frames[i]; i++) {
+  for (var i = 0, frame; (frame = frames[i]); i++) {
     disableFrame(frame);
   }
-};
+}
 
 function enableSlideFrames(no) {
   var el = getSlideEl(no);
@@ -330,14 +330,14 @@
   }
 
   var frames = el.getElementsByTagName('iframe');
-  for (var i = 0, frame; frame = frames[i]; i++) {
+  for (var i = 0, frame; (frame = frames[i]); i++) {
     enableFrame(frame);
   }
-};
+}
 
 function disableFrame(frame) {
   frame.src = 'about:blank';
-};
+}
 
 function enableFrame(frame) {
   var src = frame._src;
@@ -345,11 +345,11 @@
   if (frame.src != src && src != 'about:blank') {
     frame.src = src;
   }
-};
+}
 
 function setupFrames() {
   var frames = document.querySelectorAll('iframe');
-  for (var i = 0, frame; frame = frames[i]; i++) {
+  for (var i = 0, frame; (frame = frames[i]); i++) {
     frame._src = frame.src;
     disableFrame(frame);
   }
@@ -357,7 +357,7 @@
   enableSlideFrames(curSlide);
   enableSlideFrames(curSlide + 1);
   enableSlideFrames(curSlide + 2);
-};
+}
 
 function setupInteraction() {
   /* Clicking and tapping */
@@ -389,11 +389,11 @@
   } else {
     curSlide = 0;
   }
-};
+}
 
 function updateHash() {
   location.replace('#' + (curSlide + 1));
-};
+}
 
 /* Event listeners */
 
@@ -439,7 +439,7 @@
       event.preventDefault();
       break;
   }
-};
+}
 
 function scaleSmallViewports() {
   var el = document.querySelector('section.slides');
@@ -490,11 +490,12 @@
   var el = document.createElement('link');
   el.rel = 'stylesheet';
   el.type = 'text/css';
-  el.href = '//fonts.googleapis.com/css?family=' +
-            'Open+Sans:regular,semibold,italic,italicsemibold|Droid+Sans+Mono';
+  el.href =
+    '//fonts.googleapis.com/css?family=' +
+    'Open+Sans:regular,semibold,italic,italicsemibold|Droid+Sans+Mono';
 
   document.body.appendChild(el);
-};
+}
 
 function addGeneralStyle() {
   var el = document.createElement('link');
@@ -514,7 +515,7 @@
   document.querySelector('head').appendChild(el);
 
   scaleSmallViewports();
-};
+}
 
 function handleDomLoaded() {
   slideEls = document.querySelectorAll('section.slides > article');
@@ -529,14 +530,18 @@
 
   setupInteraction();
 
-  if (window.location.hostname == 'localhost' || window.location.hostname == '127.0.0.1' || window.location.hostname == '::1') {
+  if (
+    window.location.hostname == 'localhost' ||
+    window.location.hostname == '127.0.0.1' ||
+    window.location.hostname == '::1'
+  ) {
     hideHelpText();
   }
 
   document.body.classList.add('loaded');
 
   setupNotesSync();
-};
+}
 
 function initialize() {
   getCurSlideFromHash();
@@ -554,10 +559,14 @@
 
 // If ?debug exists then load the script relative instead of absolute
 if (!window['_DEBUG'] && document.location.href.indexOf('?debug') !== -1) {
-  document.addEventListener('DOMContentLoaded', function() {
-    // Avoid missing the DomContentLoaded event
-    window['_DCL'] = true
-  }, false);
+  document.addEventListener(
+    'DOMContentLoaded',
+    function() {
+      // Avoid missing the DomContentLoaded event
+      window['_DCL'] = true;
+    },
+    false
+  );
 
   window['_DEBUG'] = true;
   var script = document.createElement('script');
@@ -585,9 +594,9 @@
           localStorage.setItem('play-index', i);
           localStorage.setItem('output-style', out[i].style.cssText);
         }
-      })
+      });
     }
-  };
+  }
   function setupPlayCodeSync() {
     var play = document.querySelectorAll('div.playground');
     for (var i = 0; i < play.length; i++) {
@@ -598,7 +607,7 @@
         localStorage.setItem('play-code', e.target.innerHTML);
       }
     }
-  };
+  }
 
   setupPlayCodeSync();
   setupPlayResizeSync();
diff --git a/cmd/present/static/styles.css b/cmd/present/static/styles.css
index bc52559..5edfde9 100644
--- a/cmd/present/static/styles.css
+++ b/cmd/present/static/styles.css
@@ -19,7 +19,15 @@
     background: -o-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
     background: -moz-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
     background: -webkit-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
-    background: -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 500, from(rgb(240, 240, 240)), to(rgb(190, 190, 190)));
+    background: -webkit-gradient(
+      radial,
+      50% 50%,
+      0,
+      50% 50%,
+      500,
+      from(rgb(240, 240, 240)),
+      to(rgb(190, 190, 190))
+    );
 
     -webkit-font-smoothing: antialiased;
   }
@@ -64,12 +72,12 @@
 
     background-color: white;
 
-    border: 1px solid rgba(0, 0, 0, .3);
+    border: 1px solid rgba(0, 0, 0, 0.3);
 
-    transition: transform .3s ease-out;
-    -o-transition: -o-transform .3s ease-out;
-    -moz-transition: -moz-transform .3s ease-out;
-    -webkit-transition: -webkit-transform .3s ease-out;
+    transition: transform 0.3s ease-out;
+    -o-transition: -o-transform 0.3s ease-out;
+    -moz-transition: -moz-transform 0.3s ease-out;
+    -webkit-transition: -webkit-transform 0.3s ease-out;
   }
   .slides.layout-widescreen > article {
     margin-left: -550px;
@@ -239,9 +247,10 @@
   }
 
   /* Add explicit links */
-  a:link:after, a:visited:after {
-   content: " (" attr(href) ") ";
-   font-size: 50%;
+  a:link:after,
+  a:visited:after {
+    content: ' (' attr(href) ') ';
+    font-size: 50%;
   }
 
   #help {
@@ -256,7 +265,7 @@
   font-family: 'Open Sans', Arial, sans-serif;
 
   color: black;
-  text-shadow: 0 1px 1px rgba(0, 0, 0, .1);
+  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
 
   font-size: 26px;
   line-height: 36px;
@@ -273,7 +282,7 @@
   text-decoration: none;
 }
 a:visited {
-  color: rgba(0, 102, 204, .75);
+  color: rgba(0, 102, 204, 0.75);
 }
 a:hover {
   color: black;
@@ -347,7 +356,7 @@
 }
 li {
   padding: 0;
-  margin: 0 0 .5em 0;
+  margin: 0 0 0.5em 0;
 }
 
 div.code {
@@ -444,7 +453,6 @@
   -o-border-radius: 10px;
   -moz-border-radius: 10px;
   -webkit-border-radius: 10px;
-
 }
 div.output pre {
   margin: 0;
@@ -455,14 +463,17 @@
   height: 100%;
   overflow: auto;
 }
-div.output .stdout, div.output pre {
+div.output .stdout,
+div.output pre {
   color: #e6e6e6;
 }
-div.output .stderr, div.output .error {
+div.output .stderr,
+div.output .error {
   color: rgb(255, 200, 200);
 }
-div.output .system, div.output .exit {
-  color: rgb(255, 230, 120)
+div.output .system,
+div.output .exit {
+  color: rgb(255, 230, 120);
 }
 .buttons {
   position: relative;
diff --git a/go/analysis/doc.go b/go/analysis/doc.go
index ea56b72..fb17a0e 100644
--- a/go/analysis/doc.go
+++ b/go/analysis/doc.go
@@ -170,6 +170,15 @@
 The optional Category field is a short identifier that classifies the
 kind of message when an analysis produces several kinds of diagnostic.
 
+Many analyses want to associate diagnostics with a severity level.
+Because Diagnostic does not have a severity level field, an Analyzer's
+diagnostics effectively all have the same severity level. To separate which
+diagnostics are high severity and which are low severity, expose multiple
+Analyzers instead. Analyzers should also be separated when their
+diagnostics belong in different groups, or could be tagged differently
+before being shown to the end user. Analyzers should document their severity
+level to help downstream tools surface diagnostics properly.
+
 Most Analyzers inspect typed Go syntax trees, but a few, such as asmdecl
 and buildtag, inspect the raw text of Go source files or even non-Go
 files such as assembly. To report a diagnostic against a line of a
diff --git a/go/analysis/internal/checker/checker.go b/go/analysis/internal/checker/checker.go
index a26f801..5ccfb16 100644
--- a/go/analysis/internal/checker/checker.go
+++ b/go/analysis/internal/checker/checker.go
@@ -800,8 +800,13 @@
 		return obj.Exported() && obj.Pkg() == pkg ||
 			obj.Type().(*types.Signature).Recv() != nil
 	case *types.Var:
-		return obj.Exported() && obj.Pkg() == pkg ||
-			obj.IsField()
+		if obj.IsField() {
+			return true
+		}
+		// we can't filter more aggressively than this because we need
+		// to consider function parameters exported, but have no way
+		// of telling apart function parameters from local variables.
+		return obj.Pkg() == pkg
 	case *types.TypeName, *types.Const:
 		return true
 	}
diff --git a/go/analysis/passes/printf/printf.go b/go/analysis/passes/printf/printf.go
index 14f3a47..ddad4c7 100644
--- a/go/analysis/passes/printf/printf.go
+++ b/go/analysis/passes/printf/printf.go
@@ -805,6 +805,7 @@
 	{'g', sharpNumFlag, argFloat | argComplex},
 	{'G', sharpNumFlag, argFloat | argComplex},
 	{'o', sharpNumFlag, argInt | argPointer},
+	{'O', sharpNumFlag, argInt | argPointer},
 	{'p', "-#", argPointer},
 	{'q', " -+.0#", argRune | argInt | argString},
 	{'s', " -+.0", argString},
diff --git a/go/analysis/passes/printf/testdata/src/a/a.go b/go/analysis/passes/printf/testdata/src/a/a.go
index 0f3dc8d..29f5cdc 100644
--- a/go/analysis/passes/printf/testdata/src/a/a.go
+++ b/go/analysis/passes/printf/testdata/src/a/a.go
@@ -77,6 +77,7 @@
 	fmt.Printf("%G %G %G %G", 3e9, x, fslice, c)
 	fmt.Printf("%b %b %b %b", 3e9, x, fslice, c)
 	fmt.Printf("%o %o", 3, i)
+	fmt.Printf("%O %O", 3, i)
 	fmt.Printf("%p", p)
 	fmt.Printf("%q %q %q %q", 3, i, 'x', r)
 	fmt.Printf("%s %s %s", "hi", s, []byte{65})
@@ -121,6 +122,7 @@
 	fmt.Printf("%g", imap)                      // want `Printf format %g has arg imap of wrong type map\[int\]int`
 	fmt.Printf("%G", i)                         // want "Printf format %G has arg i of wrong type int"
 	fmt.Printf("%o", x)                         // want "Printf format %o has arg x of wrong type float64"
+	fmt.Printf("%O", x)                         // want "Printf format %O has arg x of wrong type float64"
 	fmt.Printf("%p", nil)                       // want "Printf format %p has arg nil of wrong type untyped nil"
 	fmt.Printf("%p", 23)                        // want "Printf format %p has arg 23 of wrong type int"
 	fmt.Printf("%q", x)                         // want "Printf format %q has arg x of wrong type float64"
@@ -799,19 +801,21 @@
 	chan_ := make(chan bool)
 	func_ := func(bool) {}
 
-	// %p, %b, %d, %o, %x, and %X all support pointers.
+	// %p, %b, %d, %o, %O, %x, and %X all support pointers.
 	fmt.Printf("%p", ptr)
 	fmt.Printf("%b", ptr)
 	fmt.Printf("%d", ptr)
 	fmt.Printf("%o", ptr)
+	fmt.Printf("%O", ptr)
 	fmt.Printf("%x", ptr)
 	fmt.Printf("%X", ptr)
 
-	// %p, %b, %d, %o, %x, and %X all support channels.
+	// %p, %b, %d, %o, %O, %x, and %X all support channels.
 	fmt.Printf("%p", chan_)
 	fmt.Printf("%b", chan_)
 	fmt.Printf("%d", chan_)
 	fmt.Printf("%o", chan_)
+	fmt.Printf("%O", chan_)
 	fmt.Printf("%x", chan_)
 	fmt.Printf("%X", chan_)
 
@@ -820,6 +824,7 @@
 	fmt.Printf("%b", func_) // want `Printf format %b arg func_ is a func value, not called`
 	fmt.Printf("%d", func_) // want `Printf format %d arg func_ is a func value, not called`
 	fmt.Printf("%o", func_) // want `Printf format %o arg func_ is a func value, not called`
+	fmt.Printf("%O", func_) // want `Printf format %O arg func_ is a func value, not called`
 	fmt.Printf("%x", func_) // want `Printf format %x arg func_ is a func value, not called`
 	fmt.Printf("%X", func_) // want `Printf format %X arg func_ is a func value, not called`
 
@@ -831,6 +836,7 @@
 	fmt.Printf("%d", slice) // want `Printf format %d has arg slice of wrong type \[\]bool`
 
 	fmt.Printf("%o", slice) // want `Printf format %o has arg slice of wrong type \[\]bool`
+	fmt.Printf("%O", slice) // want `Printf format %O has arg slice of wrong type \[\]bool`
 
 	fmt.Printf("%x", slice) // want `Printf format %x has arg slice of wrong type \[\]bool`
 	fmt.Printf("%X", slice) // want `Printf format %X has arg slice of wrong type \[\]bool`
@@ -840,6 +846,7 @@
 	fmt.Printf("%b", array) // want `Printf format %b has arg array of wrong type \[3\]bool`
 	fmt.Printf("%d", array) // want `Printf format %d has arg array of wrong type \[3\]bool`
 	fmt.Printf("%o", array) // want `Printf format %o has arg array of wrong type \[3\]bool`
+	fmt.Printf("%O", array) // want `Printf format %O has arg array of wrong type \[3\]bool`
 	fmt.Printf("%x", array) // want `Printf format %x has arg array of wrong type \[3\]bool`
 	fmt.Printf("%X", array) // want `Printf format %X has arg array of wrong type \[3\]bool`
 
@@ -850,6 +857,7 @@
 	fmt.Printf("%d", map_) // want `Printf format %d has arg map_ of wrong type map\[bool\]bool`
 
 	fmt.Printf("%o", map_) // want `Printf format %o has arg map_ of wrong type map\[bool\]bool`
+	fmt.Printf("%O", map_) // want `Printf format %O has arg map_ of wrong type map\[bool\]bool`
 
 	fmt.Printf("%x", map_) // want `Printf format %x has arg map_ of wrong type map\[bool\]bool`
 	fmt.Printf("%X", map_) // want `Printf format %X has arg map_ of wrong type map\[bool\]bool`
diff --git a/go/analysis/passes/stringintconv/string.go b/go/analysis/passes/stringintconv/string.go
index ac2cd84..7a00590 100644
--- a/go/analysis/passes/stringintconv/string.go
+++ b/go/analysis/passes/stringintconv/string.go
@@ -101,7 +101,7 @@
 		}
 		diag := analysis.Diagnostic{
 			Pos:     n.Pos(),
-			Message: fmt.Sprintf("conversion from %s to %s yields a string of one rune", source, target),
+			Message: fmt.Sprintf("conversion from %s to %s yields a string of one rune, not a string of digits (did you mean fmt.Sprint(x)?)", source, target),
 			SuggestedFixes: []analysis.SuggestedFix{
 				{
 					Message: "Did you mean to convert a rune to a string?",
diff --git a/go/analysis/passes/stringintconv/testdata/src/a/a.go b/go/analysis/passes/stringintconv/testdata/src/a/a.go
index 72ceb97..837469c 100644
--- a/go/analysis/passes/stringintconv/testdata/src/a/a.go
+++ b/go/analysis/passes/stringintconv/testdata/src/a/a.go
@@ -25,12 +25,12 @@
 		o struct{ x int }
 	)
 	const p = 0
-	_ = string(i) // want `^conversion from int to string yields a string of one rune$`
+	_ = string(i) // want `^conversion from int to string yields a string of one rune, not a string of digits \(did you mean fmt\.Sprint\(x\)\?\)$`
 	_ = string(j)
 	_ = string(k)
-	_ = string(p)    // want `^conversion from untyped int to string yields a string of one rune$`
-	_ = A(l)         // want `^conversion from C \(int\) to A \(string\) yields a string of one rune$`
-	_ = B(m)         // want `^conversion from uintptr to B \(string\) yields a string of one rune$`
-	_ = string(n[1]) // want `^conversion from int to string yields a string of one rune$`
-	_ = string(o.x)  // want `^conversion from int to string yields a string of one rune$`
+	_ = string(p)    // want `^conversion from untyped int to string yields a string of one rune, not a string of digits \(did you mean fmt\.Sprint\(x\)\?\)$`
+	_ = A(l)         // want `^conversion from C \(int\) to A \(string\) yields a string of one rune, not a string of digits \(did you mean fmt\.Sprint\(x\)\?\)$`
+	_ = B(m)         // want `^conversion from uintptr to B \(string\) yields a string of one rune, not a string of digits \(did you mean fmt\.Sprint\(x\)\?\)$`
+	_ = string(n[1]) // want `^conversion from int to string yields a string of one rune, not a string of digits \(did you mean fmt\.Sprint\(x\)\?\)$`
+	_ = string(o.x)  // want `^conversion from int to string yields a string of one rune, not a string of digits \(did you mean fmt\.Sprint\(x\)\?\)$`
 }
diff --git a/go/analysis/passes/stringintconv/testdata/src/a/a.go.golden b/go/analysis/passes/stringintconv/testdata/src/a/a.go.golden
index 9538a01..593962d 100644
--- a/go/analysis/passes/stringintconv/testdata/src/a/a.go.golden
+++ b/go/analysis/passes/stringintconv/testdata/src/a/a.go.golden
@@ -25,12 +25,12 @@
 		o struct{ x int }
 	)
 	const p = 0
-	_ = string(rune(i)) // want `^conversion from int to string yields a string of one rune$`
+	_ = string(rune(i)) // want `^conversion from int to string yields a string of one rune, not a string of digits \(did you mean fmt\.Sprint\(x\)\?\)$`
 	_ = string(j)
 	_ = string(k)
-	_ = string(rune(p))    // want `^conversion from untyped int to string yields a string of one rune$`
-	_ = A(rune(l))         // want `^conversion from C \(int\) to A \(string\) yields a string of one rune$`
-	_ = B(rune(m))         // want `^conversion from uintptr to B \(string\) yields a string of one rune$`
-	_ = string(rune(n[1])) // want `^conversion from int to string yields a string of one rune$`
-	_ = string(rune(o.x))  // want `^conversion from int to string yields a string of one rune$`
+	_ = string(rune(p))    // want `^conversion from untyped int to string yields a string of one rune, not a string of digits \(did you mean fmt\.Sprint\(x\)\?\)$`
+	_ = A(rune(l))         // want `^conversion from C \(int\) to A \(string\) yields a string of one rune, not a string of digits \(did you mean fmt\.Sprint\(x\)\?\)$`
+	_ = B(rune(m))         // want `^conversion from uintptr to B \(string\) yields a string of one rune, not a string of digits \(did you mean fmt\.Sprint\(x\)\?\)$`
+	_ = string(rune(n[1])) // want `^conversion from int to string yields a string of one rune, not a string of digits \(did you mean fmt\.Sprint\(x\)\?\)$`
+	_ = string(rune(o.x))  // want `^conversion from int to string yields a string of one rune, not a string of digits \(did you mean fmt\.Sprint\(x\)\?\)$`
 }
diff --git a/go/gcexportdata/gcexportdata.go b/go/gcexportdata/gcexportdata.go
index 0d51aca..f8363d8 100644
--- a/go/gcexportdata/gcexportdata.go
+++ b/go/gcexportdata/gcexportdata.go
@@ -85,11 +85,15 @@
 		return gcimporter.ImportData(imports, path, path, bytes.NewReader(data))
 	}
 
-	// The indexed export format starts with an 'i'.
-	if len(data) == 0 || data[0] != 'i' {
-		return nil, fmt.Errorf("unknown export data format")
+	// The indexed export format starts with an 'i'; the older
+	// binary export format starts with a 'c', 'd', or 'v'
+	// (from "version"). Select appropriate importer.
+	if len(data) > 0 && data[0] == 'i' {
+		_, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path)
+		return pkg, err
 	}
-	_, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path)
+
+	_, pkg, err := gcimporter.BImportData(fset, imports, data, path)
 	return pkg, err
 }
 
diff --git a/go/internal/gcimporter/bexport.go b/go/internal/gcimporter/bexport.go
new file mode 100644
index 0000000..a807d0a
--- /dev/null
+++ b/go/internal/gcimporter/bexport.go
@@ -0,0 +1,852 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Binary package export.
+// This file was derived from $GOROOT/src/cmd/compile/internal/gc/bexport.go;
+// see that file for specification of the format.
+
+package gcimporter
+
+import (
+	"bytes"
+	"encoding/binary"
+	"fmt"
+	"go/ast"
+	"go/constant"
+	"go/token"
+	"go/types"
+	"math"
+	"math/big"
+	"sort"
+	"strings"
+)
+
+// If debugFormat is set, each integer and string value is preceded by a marker
+// and position information in the encoding. This mechanism permits an importer
+// to recognize immediately when it is out of sync. The importer recognizes this
+// mode automatically (i.e., it can import export data produced with debugging
+// support even if debugFormat is not set at the time of import). This mode will
+// lead to massively larger export data (by a factor of 2 to 3) and should only
+// be enabled during development and debugging.
+//
+// NOTE: This flag is the first flag to enable if importing dies because of
+// (suspected) format errors, and whenever a change is made to the format.
+const debugFormat = false // default: false
+
+// If trace is set, debugging output is printed to std out.
+const trace = false // default: false
+
+// Current export format version. Increase with each format change.
+// Note: The latest binary (non-indexed) export format is at version 6.
+//       This exporter is still at level 4, but it doesn't matter since
+//       the binary importer can handle older versions just fine.
+// 6: package height (CL 105038) -- NOT IMPLEMENTED HERE
+// 5: improved position encoding efficiency (issue 20080, CL 41619) -- NOT IMPLEMEMTED HERE
+// 4: type name objects support type aliases, uses aliasTag
+// 3: Go1.8 encoding (same as version 2, aliasTag defined but never used)
+// 2: removed unused bool in ODCL export (compiler only)
+// 1: header format change (more regular), export package for _ struct fields
+// 0: Go1.7 encoding
+const exportVersion = 4
+
+// trackAllTypes enables cycle tracking for all types, not just named
+// types. The existing compiler invariants assume that unnamed types
+// that are not completely set up are not used, or else there are spurious
+// errors.
+// If disabled, only named types are tracked, possibly leading to slightly
+// less efficient encoding in rare cases. It also prevents the export of
+// some corner-case type declarations (but those are not handled correctly
+// with with the textual export format either).
+// TODO(gri) enable and remove once issues caused by it are fixed
+const trackAllTypes = false
+
+type exporter struct {
+	fset *token.FileSet
+	out  bytes.Buffer
+
+	// object -> index maps, indexed in order of serialization
+	strIndex map[string]int
+	pkgIndex map[*types.Package]int
+	typIndex map[types.Type]int
+
+	// position encoding
+	posInfoFormat bool
+	prevFile      string
+	prevLine      int
+
+	// debugging support
+	written int // bytes written
+	indent  int // for trace
+}
+
+// internalError represents an error generated inside this package.
+type internalError string
+
+func (e internalError) Error() string { return "gcimporter: " + string(e) }
+
+func internalErrorf(format string, args ...interface{}) error {
+	return internalError(fmt.Sprintf(format, args...))
+}
+
+// BExportData returns binary export data for pkg.
+// If no file set is provided, position info will be missing.
+func BExportData(fset *token.FileSet, pkg *types.Package) (b []byte, err error) {
+	defer func() {
+		if e := recover(); e != nil {
+			if ierr, ok := e.(internalError); ok {
+				err = ierr
+				return
+			}
+			// Not an internal error; panic again.
+			panic(e)
+		}
+	}()
+
+	p := exporter{
+		fset:          fset,
+		strIndex:      map[string]int{"": 0}, // empty string is mapped to 0
+		pkgIndex:      make(map[*types.Package]int),
+		typIndex:      make(map[types.Type]int),
+		posInfoFormat: true, // TODO(gri) might become a flag, eventually
+	}
+
+	// write version info
+	// The version string must start with "version %d" where %d is the version
+	// number. Additional debugging information may follow after a blank; that
+	// text is ignored by the importer.
+	p.rawStringln(fmt.Sprintf("version %d", exportVersion))
+	var debug string
+	if debugFormat {
+		debug = "debug"
+	}
+	p.rawStringln(debug) // cannot use p.bool since it's affected by debugFormat; also want to see this clearly
+	p.bool(trackAllTypes)
+	p.bool(p.posInfoFormat)
+
+	// --- generic export data ---
+
+	// populate type map with predeclared "known" types
+	for index, typ := range predeclared() {
+		p.typIndex[typ] = index
+	}
+	if len(p.typIndex) != len(predeclared()) {
+		return nil, internalError("duplicate entries in type map?")
+	}
+
+	// write package data
+	p.pkg(pkg, true)
+	if trace {
+		p.tracef("\n")
+	}
+
+	// write objects
+	objcount := 0
+	scope := pkg.Scope()
+	for _, name := range scope.Names() {
+		if !ast.IsExported(name) {
+			continue
+		}
+		if trace {
+			p.tracef("\n")
+		}
+		p.obj(scope.Lookup(name))
+		objcount++
+	}
+
+	// indicate end of list
+	if trace {
+		p.tracef("\n")
+	}
+	p.tag(endTag)
+
+	// for self-verification only (redundant)
+	p.int(objcount)
+
+	if trace {
+		p.tracef("\n")
+	}
+
+	// --- end of export data ---
+
+	return p.out.Bytes(), nil
+}
+
+func (p *exporter) pkg(pkg *types.Package, emptypath bool) {
+	if pkg == nil {
+		panic(internalError("unexpected nil pkg"))
+	}
+
+	// if we saw the package before, write its index (>= 0)
+	if i, ok := p.pkgIndex[pkg]; ok {
+		p.index('P', i)
+		return
+	}
+
+	// otherwise, remember the package, write the package tag (< 0) and package data
+	if trace {
+		p.tracef("P%d = { ", len(p.pkgIndex))
+		defer p.tracef("} ")
+	}
+	p.pkgIndex[pkg] = len(p.pkgIndex)
+
+	p.tag(packageTag)
+	p.string(pkg.Name())
+	if emptypath {
+		p.string("")
+	} else {
+		p.string(pkg.Path())
+	}
+}
+
+func (p *exporter) obj(obj types.Object) {
+	switch obj := obj.(type) {
+	case *types.Const:
+		p.tag(constTag)
+		p.pos(obj)
+		p.qualifiedName(obj)
+		p.typ(obj.Type())
+		p.value(obj.Val())
+
+	case *types.TypeName:
+		if obj.IsAlias() {
+			p.tag(aliasTag)
+			p.pos(obj)
+			p.qualifiedName(obj)
+		} else {
+			p.tag(typeTag)
+		}
+		p.typ(obj.Type())
+
+	case *types.Var:
+		p.tag(varTag)
+		p.pos(obj)
+		p.qualifiedName(obj)
+		p.typ(obj.Type())
+
+	case *types.Func:
+		p.tag(funcTag)
+		p.pos(obj)
+		p.qualifiedName(obj)
+		sig := obj.Type().(*types.Signature)
+		p.paramList(sig.Params(), sig.Variadic())
+		p.paramList(sig.Results(), false)
+
+	default:
+		panic(internalErrorf("unexpected object %v (%T)", obj, obj))
+	}
+}
+
+func (p *exporter) pos(obj types.Object) {
+	if !p.posInfoFormat {
+		return
+	}
+
+	file, line := p.fileLine(obj)
+	if file == p.prevFile {
+		// common case: write line delta
+		// delta == 0 means different file or no line change
+		delta := line - p.prevLine
+		p.int(delta)
+		if delta == 0 {
+			p.int(-1) // -1 means no file change
+		}
+	} else {
+		// different file
+		p.int(0)
+		// Encode filename as length of common prefix with previous
+		// filename, followed by (possibly empty) suffix. Filenames
+		// frequently share path prefixes, so this can save a lot
+		// of space and make export data size less dependent on file
+		// path length. The suffix is unlikely to be empty because
+		// file names tend to end in ".go".
+		n := commonPrefixLen(p.prevFile, file)
+		p.int(n)           // n >= 0
+		p.string(file[n:]) // write suffix only
+		p.prevFile = file
+		p.int(line)
+	}
+	p.prevLine = line
+}
+
+func (p *exporter) fileLine(obj types.Object) (file string, line int) {
+	if p.fset != nil {
+		pos := p.fset.Position(obj.Pos())
+		file = pos.Filename
+		line = pos.Line
+	}
+	return
+}
+
+func commonPrefixLen(a, b string) int {
+	if len(a) > len(b) {
+		a, b = b, a
+	}
+	// len(a) <= len(b)
+	i := 0
+	for i < len(a) && a[i] == b[i] {
+		i++
+	}
+	return i
+}
+
+func (p *exporter) qualifiedName(obj types.Object) {
+	p.string(obj.Name())
+	p.pkg(obj.Pkg(), false)
+}
+
+func (p *exporter) typ(t types.Type) {
+	if t == nil {
+		panic(internalError("nil type"))
+	}
+
+	// Possible optimization: Anonymous pointer types *T where
+	// T is a named type are common. We could canonicalize all
+	// such types *T to a single type PT = *T. This would lead
+	// to at most one *T entry in typIndex, and all future *T's
+	// would be encoded as the respective index directly. Would
+	// save 1 byte (pointerTag) per *T and reduce the typIndex
+	// size (at the cost of a canonicalization map). We can do
+	// this later, without encoding format change.
+
+	// if we saw the type before, write its index (>= 0)
+	if i, ok := p.typIndex[t]; ok {
+		p.index('T', i)
+		return
+	}
+
+	// otherwise, remember the type, write the type tag (< 0) and type data
+	if trackAllTypes {
+		if trace {
+			p.tracef("T%d = {>\n", len(p.typIndex))
+			defer p.tracef("<\n} ")
+		}
+		p.typIndex[t] = len(p.typIndex)
+	}
+
+	switch t := t.(type) {
+	case *types.Named:
+		if !trackAllTypes {
+			// if we don't track all types, track named types now
+			p.typIndex[t] = len(p.typIndex)
+		}
+
+		p.tag(namedTag)
+		p.pos(t.Obj())
+		p.qualifiedName(t.Obj())
+		p.typ(t.Underlying())
+		if !types.IsInterface(t) {
+			p.assocMethods(t)
+		}
+
+	case *types.Array:
+		p.tag(arrayTag)
+		p.int64(t.Len())
+		p.typ(t.Elem())
+
+	case *types.Slice:
+		p.tag(sliceTag)
+		p.typ(t.Elem())
+
+	case *dddSlice:
+		p.tag(dddTag)
+		p.typ(t.elem)
+
+	case *types.Struct:
+		p.tag(structTag)
+		p.fieldList(t)
+
+	case *types.Pointer:
+		p.tag(pointerTag)
+		p.typ(t.Elem())
+
+	case *types.Signature:
+		p.tag(signatureTag)
+		p.paramList(t.Params(), t.Variadic())
+		p.paramList(t.Results(), false)
+
+	case *types.Interface:
+		p.tag(interfaceTag)
+		p.iface(t)
+
+	case *types.Map:
+		p.tag(mapTag)
+		p.typ(t.Key())
+		p.typ(t.Elem())
+
+	case *types.Chan:
+		p.tag(chanTag)
+		p.int(int(3 - t.Dir())) // hack
+		p.typ(t.Elem())
+
+	default:
+		panic(internalErrorf("unexpected type %T: %s", t, t))
+	}
+}
+
+func (p *exporter) assocMethods(named *types.Named) {
+	// Sort methods (for determinism).
+	var methods []*types.Func
+	for i := 0; i < named.NumMethods(); i++ {
+		methods = append(methods, named.Method(i))
+	}
+	sort.Sort(methodsByName(methods))
+
+	p.int(len(methods))
+
+	if trace && methods != nil {
+		p.tracef("associated methods {>\n")
+	}
+
+	for i, m := range methods {
+		if trace && i > 0 {
+			p.tracef("\n")
+		}
+
+		p.pos(m)
+		name := m.Name()
+		p.string(name)
+		if !exported(name) {
+			p.pkg(m.Pkg(), false)
+		}
+
+		sig := m.Type().(*types.Signature)
+		p.paramList(types.NewTuple(sig.Recv()), false)
+		p.paramList(sig.Params(), sig.Variadic())
+		p.paramList(sig.Results(), false)
+		p.int(0) // dummy value for go:nointerface pragma - ignored by importer
+	}
+
+	if trace && methods != nil {
+		p.tracef("<\n} ")
+	}
+}
+
+type methodsByName []*types.Func
+
+func (x methodsByName) Len() int           { return len(x) }
+func (x methodsByName) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
+func (x methodsByName) Less(i, j int) bool { return x[i].Name() < x[j].Name() }
+
+func (p *exporter) fieldList(t *types.Struct) {
+	if trace && t.NumFields() > 0 {
+		p.tracef("fields {>\n")
+		defer p.tracef("<\n} ")
+	}
+
+	p.int(t.NumFields())
+	for i := 0; i < t.NumFields(); i++ {
+		if trace && i > 0 {
+			p.tracef("\n")
+		}
+		p.field(t.Field(i))
+		p.string(t.Tag(i))
+	}
+}
+
+func (p *exporter) field(f *types.Var) {
+	if !f.IsField() {
+		panic(internalError("field expected"))
+	}
+
+	p.pos(f)
+	p.fieldName(f)
+	p.typ(f.Type())
+}
+
+func (p *exporter) iface(t *types.Interface) {
+	// TODO(gri): enable importer to load embedded interfaces,
+	// then emit Embeddeds and ExplicitMethods separately here.
+	p.int(0)
+
+	n := t.NumMethods()
+	if trace && n > 0 {
+		p.tracef("methods {>\n")
+		defer p.tracef("<\n} ")
+	}
+	p.int(n)
+	for i := 0; i < n; i++ {
+		if trace && i > 0 {
+			p.tracef("\n")
+		}
+		p.method(t.Method(i))
+	}
+}
+
+func (p *exporter) method(m *types.Func) {
+	sig := m.Type().(*types.Signature)
+	if sig.Recv() == nil {
+		panic(internalError("method expected"))
+	}
+
+	p.pos(m)
+	p.string(m.Name())
+	if m.Name() != "_" && !ast.IsExported(m.Name()) {
+		p.pkg(m.Pkg(), false)
+	}
+
+	// interface method; no need to encode receiver.
+	p.paramList(sig.Params(), sig.Variadic())
+	p.paramList(sig.Results(), false)
+}
+
+func (p *exporter) fieldName(f *types.Var) {
+	name := f.Name()
+
+	if f.Anonymous() {
+		// anonymous field - we distinguish between 3 cases:
+		// 1) field name matches base type name and is exported
+		// 2) field name matches base type name and is not exported
+		// 3) field name doesn't match base type name (alias name)
+		bname := basetypeName(f.Type())
+		if name == bname {
+			if ast.IsExported(name) {
+				name = "" // 1) we don't need to know the field name or package
+			} else {
+				name = "?" // 2) use unexported name "?" to force package export
+			}
+		} else {
+			// 3) indicate alias and export name as is
+			// (this requires an extra "@" but this is a rare case)
+			p.string("@")
+		}
+	}
+
+	p.string(name)
+	if name != "" && !ast.IsExported(name) {
+		p.pkg(f.Pkg(), false)
+	}
+}
+
+func basetypeName(typ types.Type) string {
+	switch typ := deref(typ).(type) {
+	case *types.Basic:
+		return typ.Name()
+	case *types.Named:
+		return typ.Obj().Name()
+	default:
+		return "" // unnamed type
+	}
+}
+
+func (p *exporter) paramList(params *types.Tuple, variadic bool) {
+	// use negative length to indicate unnamed parameters
+	// (look at the first parameter only since either all
+	// names are present or all are absent)
+	n := params.Len()
+	if n > 0 && params.At(0).Name() == "" {
+		n = -n
+	}
+	p.int(n)
+	for i := 0; i < params.Len(); i++ {
+		q := params.At(i)
+		t := q.Type()
+		if variadic && i == params.Len()-1 {
+			t = &dddSlice{t.(*types.Slice).Elem()}
+		}
+		p.typ(t)
+		if n > 0 {
+			name := q.Name()
+			p.string(name)
+			if name != "_" {
+				p.pkg(q.Pkg(), false)
+			}
+		}
+		p.string("") // no compiler-specific info
+	}
+}
+
+func (p *exporter) value(x constant.Value) {
+	if trace {
+		p.tracef("= ")
+	}
+
+	switch x.Kind() {
+	case constant.Bool:
+		tag := falseTag
+		if constant.BoolVal(x) {
+			tag = trueTag
+		}
+		p.tag(tag)
+
+	case constant.Int:
+		if v, exact := constant.Int64Val(x); exact {
+			// common case: x fits into an int64 - use compact encoding
+			p.tag(int64Tag)
+			p.int64(v)
+			return
+		}
+		// uncommon case: large x - use float encoding
+		// (powers of 2 will be encoded efficiently with exponent)
+		p.tag(floatTag)
+		p.float(constant.ToFloat(x))
+
+	case constant.Float:
+		p.tag(floatTag)
+		p.float(x)
+
+	case constant.Complex:
+		p.tag(complexTag)
+		p.float(constant.Real(x))
+		p.float(constant.Imag(x))
+
+	case constant.String:
+		p.tag(stringTag)
+		p.string(constant.StringVal(x))
+
+	case constant.Unknown:
+		// package contains type errors
+		p.tag(unknownTag)
+
+	default:
+		panic(internalErrorf("unexpected value %v (%T)", x, x))
+	}
+}
+
+func (p *exporter) float(x constant.Value) {
+	if x.Kind() != constant.Float {
+		panic(internalErrorf("unexpected constant %v, want float", x))
+	}
+	// extract sign (there is no -0)
+	sign := constant.Sign(x)
+	if sign == 0 {
+		// x == 0
+		p.int(0)
+		return
+	}
+	// x != 0
+
+	var f big.Float
+	if v, exact := constant.Float64Val(x); exact {
+		// float64
+		f.SetFloat64(v)
+	} else if num, denom := constant.Num(x), constant.Denom(x); num.Kind() == constant.Int {
+		// TODO(gri): add big.Rat accessor to constant.Value.
+		r := valueToRat(num)
+		f.SetRat(r.Quo(r, valueToRat(denom)))
+	} else {
+		// Value too large to represent as a fraction => inaccessible.
+		// TODO(gri): add big.Float accessor to constant.Value.
+		f.SetFloat64(math.MaxFloat64) // FIXME
+	}
+
+	// extract exponent such that 0.5 <= m < 1.0
+	var m big.Float
+	exp := f.MantExp(&m)
+
+	// extract mantissa as *big.Int
+	// - set exponent large enough so mant satisfies mant.IsInt()
+	// - get *big.Int from mant
+	m.SetMantExp(&m, int(m.MinPrec()))
+	mant, acc := m.Int(nil)
+	if acc != big.Exact {
+		panic(internalError("internal error"))
+	}
+
+	p.int(sign)
+	p.int(exp)
+	p.string(string(mant.Bytes()))
+}
+
+func valueToRat(x constant.Value) *big.Rat {
+	// Convert little-endian to big-endian.
+	// I can't believe this is necessary.
+	bytes := constant.Bytes(x)
+	for i := 0; i < len(bytes)/2; i++ {
+		bytes[i], bytes[len(bytes)-1-i] = bytes[len(bytes)-1-i], bytes[i]
+	}
+	return new(big.Rat).SetInt(new(big.Int).SetBytes(bytes))
+}
+
+func (p *exporter) bool(b bool) bool {
+	if trace {
+		p.tracef("[")
+		defer p.tracef("= %v] ", b)
+	}
+
+	x := 0
+	if b {
+		x = 1
+	}
+	p.int(x)
+	return b
+}
+
+// ----------------------------------------------------------------------------
+// Low-level encoders
+
+func (p *exporter) index(marker byte, index int) {
+	if index < 0 {
+		panic(internalError("invalid index < 0"))
+	}
+	if debugFormat {
+		p.marker('t')
+	}
+	if trace {
+		p.tracef("%c%d ", marker, index)
+	}
+	p.rawInt64(int64(index))
+}
+
+func (p *exporter) tag(tag int) {
+	if tag >= 0 {
+		panic(internalError("invalid tag >= 0"))
+	}
+	if debugFormat {
+		p.marker('t')
+	}
+	if trace {
+		p.tracef("%s ", tagString[-tag])
+	}
+	p.rawInt64(int64(tag))
+}
+
+func (p *exporter) int(x int) {
+	p.int64(int64(x))
+}
+
+func (p *exporter) int64(x int64) {
+	if debugFormat {
+		p.marker('i')
+	}
+	if trace {
+		p.tracef("%d ", x)
+	}
+	p.rawInt64(x)
+}
+
+func (p *exporter) string(s string) {
+	if debugFormat {
+		p.marker('s')
+	}
+	if trace {
+		p.tracef("%q ", s)
+	}
+	// if we saw the string before, write its index (>= 0)
+	// (the empty string is mapped to 0)
+	if i, ok := p.strIndex[s]; ok {
+		p.rawInt64(int64(i))
+		return
+	}
+	// otherwise, remember string and write its negative length and bytes
+	p.strIndex[s] = len(p.strIndex)
+	p.rawInt64(-int64(len(s)))
+	for i := 0; i < len(s); i++ {
+		p.rawByte(s[i])
+	}
+}
+
+// marker emits a marker byte and position information which makes
+// it easy for a reader to detect if it is "out of sync". Used for
+// debugFormat format only.
+func (p *exporter) marker(m byte) {
+	p.rawByte(m)
+	// Enable this for help tracking down the location
+	// of an incorrect marker when running in debugFormat.
+	if false && trace {
+		p.tracef("#%d ", p.written)
+	}
+	p.rawInt64(int64(p.written))
+}
+
+// rawInt64 should only be used by low-level encoders.
+func (p *exporter) rawInt64(x int64) {
+	var tmp [binary.MaxVarintLen64]byte
+	n := binary.PutVarint(tmp[:], x)
+	for i := 0; i < n; i++ {
+		p.rawByte(tmp[i])
+	}
+}
+
+// rawStringln should only be used to emit the initial version string.
+func (p *exporter) rawStringln(s string) {
+	for i := 0; i < len(s); i++ {
+		p.rawByte(s[i])
+	}
+	p.rawByte('\n')
+}
+
+// rawByte is the bottleneck interface to write to p.out.
+// rawByte escapes b as follows (any encoding does that
+// hides '$'):
+//
+//	'$'  => '|' 'S'
+//	'|'  => '|' '|'
+//
+// Necessary so other tools can find the end of the
+// export data by searching for "$$".
+// rawByte should only be used by low-level encoders.
+func (p *exporter) rawByte(b byte) {
+	switch b {
+	case '$':
+		// write '$' as '|' 'S'
+		b = 'S'
+		fallthrough
+	case '|':
+		// write '|' as '|' '|'
+		p.out.WriteByte('|')
+		p.written++
+	}
+	p.out.WriteByte(b)
+	p.written++
+}
+
+// tracef is like fmt.Printf but it rewrites the format string
+// to take care of indentation.
+func (p *exporter) tracef(format string, args ...interface{}) {
+	if strings.ContainsAny(format, "<>\n") {
+		var buf bytes.Buffer
+		for i := 0; i < len(format); i++ {
+			// no need to deal with runes
+			ch := format[i]
+			switch ch {
+			case '>':
+				p.indent++
+				continue
+			case '<':
+				p.indent--
+				continue
+			}
+			buf.WriteByte(ch)
+			if ch == '\n' {
+				for j := p.indent; j > 0; j-- {
+					buf.WriteString(".  ")
+				}
+			}
+		}
+		format = buf.String()
+	}
+	fmt.Printf(format, args...)
+}
+
+// Debugging support.
+// (tagString is only used when tracing is enabled)
+var tagString = [...]string{
+	// Packages
+	-packageTag: "package",
+
+	// Types
+	-namedTag:     "named type",
+	-arrayTag:     "array",
+	-sliceTag:     "slice",
+	-dddTag:       "ddd",
+	-structTag:    "struct",
+	-pointerTag:   "pointer",
+	-signatureTag: "signature",
+	-interfaceTag: "interface",
+	-mapTag:       "map",
+	-chanTag:      "chan",
+
+	// Values
+	-falseTag:    "false",
+	-trueTag:     "true",
+	-int64Tag:    "int64",
+	-floatTag:    "float",
+	-fractionTag: "fraction",
+	-complexTag:  "complex",
+	-stringTag:   "string",
+	-unknownTag:  "unknown",
+
+	// Type aliases
+	-aliasTag: "alias",
+}
diff --git a/go/internal/gcimporter/bexport_test.go b/go/internal/gcimporter/bexport_test.go
new file mode 100644
index 0000000..de7b921
--- /dev/null
+++ b/go/internal/gcimporter/bexport_test.go
@@ -0,0 +1,419 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gcimporter_test
+
+import (
+	"fmt"
+	"go/ast"
+	"go/build"
+	"go/constant"
+	"go/parser"
+	"go/token"
+	"go/types"
+	"reflect"
+	"runtime"
+	"strings"
+	"testing"
+
+	"golang.org/x/tools/go/buildutil"
+	"golang.org/x/tools/go/internal/gcimporter"
+	"golang.org/x/tools/go/loader"
+)
+
+var isRace = false
+
+func TestBExportData_stdlib(t *testing.T) {
+	if runtime.Compiler == "gccgo" {
+		t.Skip("gccgo standard library is inaccessible")
+	}
+	if runtime.GOOS == "android" {
+		t.Skipf("incomplete std lib on %s", runtime.GOOS)
+	}
+	if isRace {
+		t.Skipf("stdlib tests take too long in race mode and flake on builders")
+	}
+
+	// Load, parse and type-check the program.
+	ctxt := build.Default // copy
+	ctxt.GOPATH = ""      // disable GOPATH
+	conf := loader.Config{
+		Build:       &ctxt,
+		AllowErrors: true,
+	}
+	for _, path := range buildutil.AllPackages(conf.Build) {
+		conf.Import(path)
+	}
+
+	// Create a package containing type and value errors to ensure
+	// they are properly encoded/decoded.
+	f, err := conf.ParseFile("haserrors/haserrors.go", `package haserrors
+const UnknownValue = "" + 0
+type UnknownType undefined
+`)
+	if err != nil {
+		t.Fatal(err)
+	}
+	conf.CreateFromFiles("haserrors", f)
+
+	prog, err := conf.Load()
+	if err != nil {
+		t.Fatalf("Load failed: %v", err)
+	}
+
+	numPkgs := len(prog.AllPackages)
+	if want := 248; numPkgs < want {
+		t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want)
+	}
+
+	for pkg, info := range prog.AllPackages {
+		if info.Files == nil {
+			continue // empty directory
+		}
+		exportdata, err := gcimporter.BExportData(conf.Fset, pkg)
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		imports := make(map[string]*types.Package)
+		fset2 := token.NewFileSet()
+		n, pkg2, err := gcimporter.BImportData(fset2, imports, exportdata, pkg.Path())
+		if err != nil {
+			t.Errorf("BImportData(%s): %v", pkg.Path(), err)
+			continue
+		}
+		if n != len(exportdata) {
+			t.Errorf("BImportData(%s) decoded %d bytes, want %d",
+				pkg.Path(), n, len(exportdata))
+		}
+
+		// Compare the packages' corresponding members.
+		for _, name := range pkg.Scope().Names() {
+			if !ast.IsExported(name) {
+				continue
+			}
+			obj1 := pkg.Scope().Lookup(name)
+			obj2 := pkg2.Scope().Lookup(name)
+			if obj2 == nil {
+				t.Errorf("%s.%s not found, want %s", pkg.Path(), name, obj1)
+				continue
+			}
+
+			fl1 := fileLine(conf.Fset, obj1)
+			fl2 := fileLine(fset2, obj2)
+			if fl1 != fl2 {
+				t.Errorf("%s.%s: got posn %s, want %s",
+					pkg.Path(), name, fl2, fl1)
+			}
+
+			if err := equalObj(obj1, obj2); err != nil {
+				t.Errorf("%s.%s: %s\ngot:  %s\nwant: %s",
+					pkg.Path(), name, err, obj2, obj1)
+			}
+		}
+	}
+}
+
+func fileLine(fset *token.FileSet, obj types.Object) string {
+	posn := fset.Position(obj.Pos())
+	return fmt.Sprintf("%s:%d", posn.Filename, posn.Line)
+}
+
+// equalObj reports how x and y differ.  They are assumed to belong to
+// different universes so cannot be compared directly.
+func equalObj(x, y types.Object) error {
+	if reflect.TypeOf(x) != reflect.TypeOf(y) {
+		return fmt.Errorf("%T vs %T", x, y)
+	}
+	xt := x.Type()
+	yt := y.Type()
+	switch x.(type) {
+	case *types.Var, *types.Func:
+		// ok
+	case *types.Const:
+		xval := x.(*types.Const).Val()
+		yval := y.(*types.Const).Val()
+		// Use string comparison for floating-point values since rounding is permitted.
+		if constant.Compare(xval, token.NEQ, yval) &&
+			!(xval.Kind() == constant.Float && xval.String() == yval.String()) {
+			return fmt.Errorf("unequal constants %s vs %s", xval, yval)
+		}
+	case *types.TypeName:
+		xt = xt.Underlying()
+		yt = yt.Underlying()
+	default:
+		return fmt.Errorf("unexpected %T", x)
+	}
+	return equalType(xt, yt)
+}
+
+func equalType(x, y types.Type) error {
+	if reflect.TypeOf(x) != reflect.TypeOf(y) {
+		return fmt.Errorf("unequal kinds: %T vs %T", x, y)
+	}
+	switch x := x.(type) {
+	case *types.Interface:
+		y := y.(*types.Interface)
+		// TODO(gri): enable separate emission of Embedded interfaces
+		// and ExplicitMethods then use this logic.
+		// if x.NumEmbeddeds() != y.NumEmbeddeds() {
+		// 	return fmt.Errorf("unequal number of embedded interfaces: %d vs %d",
+		// 		x.NumEmbeddeds(), y.NumEmbeddeds())
+		// }
+		// for i := 0; i < x.NumEmbeddeds(); i++ {
+		// 	xi := x.Embedded(i)
+		// 	yi := y.Embedded(i)
+		// 	if xi.String() != yi.String() {
+		// 		return fmt.Errorf("mismatched %th embedded interface: %s vs %s",
+		// 			i, xi, yi)
+		// 	}
+		// }
+		// if x.NumExplicitMethods() != y.NumExplicitMethods() {
+		// 	return fmt.Errorf("unequal methods: %d vs %d",
+		// 		x.NumExplicitMethods(), y.NumExplicitMethods())
+		// }
+		// for i := 0; i < x.NumExplicitMethods(); i++ {
+		// 	xm := x.ExplicitMethod(i)
+		// 	ym := y.ExplicitMethod(i)
+		// 	if xm.Name() != ym.Name() {
+		// 		return fmt.Errorf("mismatched %th method: %s vs %s", i, xm, ym)
+		// 	}
+		// 	if err := equalType(xm.Type(), ym.Type()); err != nil {
+		// 		return fmt.Errorf("mismatched %s method: %s", xm.Name(), err)
+		// 	}
+		// }
+		if x.NumMethods() != y.NumMethods() {
+			return fmt.Errorf("unequal methods: %d vs %d",
+				x.NumMethods(), y.NumMethods())
+		}
+		for i := 0; i < x.NumMethods(); i++ {
+			xm := x.Method(i)
+			ym := y.Method(i)
+			if xm.Name() != ym.Name() {
+				return fmt.Errorf("mismatched %dth method: %s vs %s", i, xm, ym)
+			}
+			if err := equalType(xm.Type(), ym.Type()); err != nil {
+				return fmt.Errorf("mismatched %s method: %s", xm.Name(), err)
+			}
+		}
+	case *types.Array:
+		y := y.(*types.Array)
+		if x.Len() != y.Len() {
+			return fmt.Errorf("unequal array lengths: %d vs %d", x.Len(), y.Len())
+		}
+		if err := equalType(x.Elem(), y.Elem()); err != nil {
+			return fmt.Errorf("array elements: %s", err)
+		}
+	case *types.Basic:
+		y := y.(*types.Basic)
+		if x.Kind() != y.Kind() {
+			return fmt.Errorf("unequal basic types: %s vs %s", x, y)
+		}
+	case *types.Chan:
+		y := y.(*types.Chan)
+		if x.Dir() != y.Dir() {
+			return fmt.Errorf("unequal channel directions: %d vs %d", x.Dir(), y.Dir())
+		}
+		if err := equalType(x.Elem(), y.Elem()); err != nil {
+			return fmt.Errorf("channel elements: %s", err)
+		}
+	case *types.Map:
+		y := y.(*types.Map)
+		if err := equalType(x.Key(), y.Key()); err != nil {
+			return fmt.Errorf("map keys: %s", err)
+		}
+		if err := equalType(x.Elem(), y.Elem()); err != nil {
+			return fmt.Errorf("map values: %s", err)
+		}
+	case *types.Named:
+		y := y.(*types.Named)
+		if x.String() != y.String() {
+			return fmt.Errorf("unequal named types: %s vs %s", x, y)
+		}
+	case *types.Pointer:
+		y := y.(*types.Pointer)
+		if err := equalType(x.Elem(), y.Elem()); err != nil {
+			return fmt.Errorf("pointer elements: %s", err)
+		}
+	case *types.Signature:
+		y := y.(*types.Signature)
+		if err := equalType(x.Params(), y.Params()); err != nil {
+			return fmt.Errorf("parameters: %s", err)
+		}
+		if err := equalType(x.Results(), y.Results()); err != nil {
+			return fmt.Errorf("results: %s", err)
+		}
+		if x.Variadic() != y.Variadic() {
+			return fmt.Errorf("unequal variadicity: %t vs %t",
+				x.Variadic(), y.Variadic())
+		}
+		if (x.Recv() != nil) != (y.Recv() != nil) {
+			return fmt.Errorf("unequal receivers: %s vs %s", x.Recv(), y.Recv())
+		}
+		if x.Recv() != nil {
+			// TODO(adonovan): fix: this assertion fires for interface methods.
+			// The type of the receiver of an interface method is a named type
+			// if the Package was loaded from export data, or an unnamed (interface)
+			// type if the Package was produced by type-checking ASTs.
+			// if err := equalType(x.Recv().Type(), y.Recv().Type()); err != nil {
+			// 	return fmt.Errorf("receiver: %s", err)
+			// }
+		}
+	case *types.Slice:
+		y := y.(*types.Slice)
+		if err := equalType(x.Elem(), y.Elem()); err != nil {
+			return fmt.Errorf("slice elements: %s", err)
+		}
+	case *types.Struct:
+		y := y.(*types.Struct)
+		if x.NumFields() != y.NumFields() {
+			return fmt.Errorf("unequal struct fields: %d vs %d",
+				x.NumFields(), y.NumFields())
+		}
+		for i := 0; i < x.NumFields(); i++ {
+			xf := x.Field(i)
+			yf := y.Field(i)
+			if xf.Name() != yf.Name() {
+				return fmt.Errorf("mismatched fields: %s vs %s", xf, yf)
+			}
+			if err := equalType(xf.Type(), yf.Type()); err != nil {
+				return fmt.Errorf("struct field %s: %s", xf.Name(), err)
+			}
+			if x.Tag(i) != y.Tag(i) {
+				return fmt.Errorf("struct field %s has unequal tags: %q vs %q",
+					xf.Name(), x.Tag(i), y.Tag(i))
+			}
+		}
+	case *types.Tuple:
+		y := y.(*types.Tuple)
+		if x.Len() != y.Len() {
+			return fmt.Errorf("unequal tuple lengths: %d vs %d", x.Len(), y.Len())
+		}
+		for i := 0; i < x.Len(); i++ {
+			if err := equalType(x.At(i).Type(), y.At(i).Type()); err != nil {
+				return fmt.Errorf("tuple element %d: %s", i, err)
+			}
+		}
+	}
+	return nil
+}
+
+// TestVeryLongFile tests the position of an import object declared in
+// a very long input file.  Line numbers greater than maxlines are
+// reported as line 1, not garbage or token.NoPos.
+func TestVeryLongFile(t *testing.T) {
+	// parse and typecheck
+	longFile := "package foo" + strings.Repeat("\n", 123456) + "var X int"
+	fset1 := token.NewFileSet()
+	f, err := parser.ParseFile(fset1, "foo.go", longFile, 0)
+	if err != nil {
+		t.Fatal(err)
+	}
+	var conf types.Config
+	pkg, err := conf.Check("foo", fset1, []*ast.File{f}, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// export
+	exportdata, err := gcimporter.BExportData(fset1, pkg)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// import
+	imports := make(map[string]*types.Package)
+	fset2 := token.NewFileSet()
+	_, pkg2, err := gcimporter.BImportData(fset2, imports, exportdata, pkg.Path())
+	if err != nil {
+		t.Fatalf("BImportData(%s): %v", pkg.Path(), err)
+	}
+
+	// compare
+	posn1 := fset1.Position(pkg.Scope().Lookup("X").Pos())
+	posn2 := fset2.Position(pkg2.Scope().Lookup("X").Pos())
+	if want := "foo.go:1:1"; posn2.String() != want {
+		t.Errorf("X position = %s, want %s (orig was %s)",
+			posn2, want, posn1)
+	}
+}
+
+const src = `
+package p
+
+type (
+	T0 = int32
+	T1 = struct{}
+	T2 = struct{ T1 }
+	Invalid = foo // foo is undeclared
+)
+`
+
+func checkPkg(t *testing.T, pkg *types.Package, label string) {
+	T1 := types.NewStruct(nil, nil)
+	T2 := types.NewStruct([]*types.Var{types.NewField(0, pkg, "T1", T1, true)}, nil)
+
+	for _, test := range []struct {
+		name string
+		typ  types.Type
+	}{
+		{"T0", types.Typ[types.Int32]},
+		{"T1", T1},
+		{"T2", T2},
+		{"Invalid", types.Typ[types.Invalid]},
+	} {
+		obj := pkg.Scope().Lookup(test.name)
+		if obj == nil {
+			t.Errorf("%s: %s not found", label, test.name)
+			continue
+		}
+		tname, _ := obj.(*types.TypeName)
+		if tname == nil {
+			t.Errorf("%s: %v not a type name", label, obj)
+			continue
+		}
+		if !tname.IsAlias() {
+			t.Errorf("%s: %v: not marked as alias", label, tname)
+			continue
+		}
+		if got := tname.Type(); !types.Identical(got, test.typ) {
+			t.Errorf("%s: %v: got %v; want %v", label, tname, got, test.typ)
+		}
+	}
+}
+
+func TestTypeAliases(t *testing.T) {
+	// parse and typecheck
+	fset1 := token.NewFileSet()
+	f, err := parser.ParseFile(fset1, "p.go", src, 0)
+	if err != nil {
+		t.Fatal(err)
+	}
+	var conf types.Config
+	pkg1, err := conf.Check("p", fset1, []*ast.File{f}, nil)
+	if err == nil {
+		// foo in undeclared in src; we should see an error
+		t.Fatal("invalid source type-checked without error")
+	}
+	if pkg1 == nil {
+		// despite incorrect src we should see a (partially) type-checked package
+		t.Fatal("nil package returned")
+	}
+	checkPkg(t, pkg1, "export")
+
+	// export
+	exportdata, err := gcimporter.BExportData(fset1, pkg1)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// import
+	imports := make(map[string]*types.Package)
+	fset2 := token.NewFileSet()
+	_, pkg2, err := gcimporter.BImportData(fset2, imports, exportdata, pkg1.Path())
+	if err != nil {
+		t.Fatalf("BImportData(%s): %v", pkg1.Path(), err)
+	}
+	checkPkg(t, pkg2, "import")
+}
diff --git a/go/internal/gcimporter/bimport.go b/go/internal/gcimporter/bimport.go
new file mode 100644
index 0000000..e9f73d1
--- /dev/null
+++ b/go/internal/gcimporter/bimport.go
@@ -0,0 +1,1039 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file is a copy of $GOROOT/src/go/internal/gcimporter/bimport.go.
+
+package gcimporter
+
+import (
+	"encoding/binary"
+	"fmt"
+	"go/constant"
+	"go/token"
+	"go/types"
+	"sort"
+	"strconv"
+	"strings"
+	"sync"
+	"unicode"
+	"unicode/utf8"
+)
+
+type importer struct {
+	imports    map[string]*types.Package
+	data       []byte
+	importpath string
+	buf        []byte // for reading strings
+	version    int    // export format version
+
+	// object lists
+	strList       []string           // in order of appearance
+	pathList      []string           // in order of appearance
+	pkgList       []*types.Package   // in order of appearance
+	typList       []types.Type       // in order of appearance
+	interfaceList []*types.Interface // for delayed completion only
+	trackAllTypes bool
+
+	// position encoding
+	posInfoFormat bool
+	prevFile      string
+	prevLine      int
+	fake          fakeFileSet
+
+	// debugging support
+	debugFormat bool
+	read        int // bytes read
+}
+
+// BImportData imports a package from the serialized package data
+// and returns the number of bytes consumed and a reference to the package.
+// If the export data version is not recognized or the format is otherwise
+// compromised, an error is returned.
+func BImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) {
+	// catch panics and return them as errors
+	const currentVersion = 6
+	version := -1 // unknown version
+	defer func() {
+		if e := recover(); e != nil {
+			// Return a (possibly nil or incomplete) package unchanged (see #16088).
+			if version > currentVersion {
+				err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e)
+			} else {
+				err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e)
+			}
+		}
+	}()
+
+	p := importer{
+		imports:    imports,
+		data:       data,
+		importpath: path,
+		version:    version,
+		strList:    []string{""}, // empty string is mapped to 0
+		pathList:   []string{""}, // empty string is mapped to 0
+		fake: fakeFileSet{
+			fset:  fset,
+			files: make(map[string]*token.File),
+		},
+	}
+
+	// read version info
+	var versionstr string
+	if b := p.rawByte(); b == 'c' || b == 'd' {
+		// Go1.7 encoding; first byte encodes low-level
+		// encoding format (compact vs debug).
+		// For backward-compatibility only (avoid problems with
+		// old installed packages). Newly compiled packages use
+		// the extensible format string.
+		// TODO(gri) Remove this support eventually; after Go1.8.
+		if b == 'd' {
+			p.debugFormat = true
+		}
+		p.trackAllTypes = p.rawByte() == 'a'
+		p.posInfoFormat = p.int() != 0
+		versionstr = p.string()
+		if versionstr == "v1" {
+			version = 0
+		}
+	} else {
+		// Go1.8 extensible encoding
+		// read version string and extract version number (ignore anything after the version number)
+		versionstr = p.rawStringln(b)
+		if s := strings.SplitN(versionstr, " ", 3); len(s) >= 2 && s[0] == "version" {
+			if v, err := strconv.Atoi(s[1]); err == nil && v > 0 {
+				version = v
+			}
+		}
+	}
+	p.version = version
+
+	// read version specific flags - extend as necessary
+	switch p.version {
+	// case currentVersion:
+	// 	...
+	//	fallthrough
+	case currentVersion, 5, 4, 3, 2, 1:
+		p.debugFormat = p.rawStringln(p.rawByte()) == "debug"
+		p.trackAllTypes = p.int() != 0
+		p.posInfoFormat = p.int() != 0
+	case 0:
+		// Go1.7 encoding format - nothing to do here
+	default:
+		errorf("unknown bexport format version %d (%q)", p.version, versionstr)
+	}
+
+	// --- generic export data ---
+
+	// populate typList with predeclared "known" types
+	p.typList = append(p.typList, predeclared()...)
+
+	// read package data
+	pkg = p.pkg()
+
+	// read objects of phase 1 only (see cmd/compile/internal/gc/bexport.go)
+	objcount := 0
+	for {
+		tag := p.tagOrIndex()
+		if tag == endTag {
+			break
+		}
+		p.obj(tag)
+		objcount++
+	}
+
+	// self-verification
+	if count := p.int(); count != objcount {
+		errorf("got %d objects; want %d", objcount, count)
+	}
+
+	// ignore compiler-specific import data
+
+	// complete interfaces
+	// TODO(gri) re-investigate if we still need to do this in a delayed fashion
+	for _, typ := range p.interfaceList {
+		typ.Complete()
+	}
+
+	// record all referenced packages as imports
+	list := append(([]*types.Package)(nil), p.pkgList[1:]...)
+	sort.Sort(byPath(list))
+	pkg.SetImports(list)
+
+	// package was imported completely and without errors
+	pkg.MarkComplete()
+
+	return p.read, pkg, nil
+}
+
+func errorf(format string, args ...interface{}) {
+	panic(fmt.Sprintf(format, args...))
+}
+
+func (p *importer) pkg() *types.Package {
+	// if the package was seen before, i is its index (>= 0)
+	i := p.tagOrIndex()
+	if i >= 0 {
+		return p.pkgList[i]
+	}
+
+	// otherwise, i is the package tag (< 0)
+	if i != packageTag {
+		errorf("unexpected package tag %d version %d", i, p.version)
+	}
+
+	// read package data
+	name := p.string()
+	var path string
+	if p.version >= 5 {
+		path = p.path()
+	} else {
+		path = p.string()
+	}
+	if p.version >= 6 {
+		p.int() // package height; unused by go/types
+	}
+
+	// we should never see an empty package name
+	if name == "" {
+		errorf("empty package name in import")
+	}
+
+	// an empty path denotes the package we are currently importing;
+	// it must be the first package we see
+	if (path == "") != (len(p.pkgList) == 0) {
+		errorf("package path %q for pkg index %d", path, len(p.pkgList))
+	}
+
+	// if the package was imported before, use that one; otherwise create a new one
+	if path == "" {
+		path = p.importpath
+	}
+	pkg := p.imports[path]
+	if pkg == nil {
+		pkg = types.NewPackage(path, name)
+		p.imports[path] = pkg
+	} else if pkg.Name() != name {
+		errorf("conflicting names %s and %s for package %q", pkg.Name(), name, path)
+	}
+	p.pkgList = append(p.pkgList, pkg)
+
+	return pkg
+}
+
+// objTag returns the tag value for each object kind.
+func objTag(obj types.Object) int {
+	switch obj.(type) {
+	case *types.Const:
+		return constTag
+	case *types.TypeName:
+		return typeTag
+	case *types.Var:
+		return varTag
+	case *types.Func:
+		return funcTag
+	default:
+		errorf("unexpected object: %v (%T)", obj, obj) // panics
+		panic("unreachable")
+	}
+}
+
+func sameObj(a, b types.Object) bool {
+	// Because unnamed types are not canonicalized, we cannot simply compare types for
+	// (pointer) identity.
+	// Ideally we'd check equality of constant values as well, but this is good enough.
+	return objTag(a) == objTag(b) && types.Identical(a.Type(), b.Type())
+}
+
+func (p *importer) declare(obj types.Object) {
+	pkg := obj.Pkg()
+	if alt := pkg.Scope().Insert(obj); alt != nil {
+		// This can only trigger if we import a (non-type) object a second time.
+		// Excluding type aliases, this cannot happen because 1) we only import a package
+		// once; and b) we ignore compiler-specific export data which may contain
+		// functions whose inlined function bodies refer to other functions that
+		// were already imported.
+		// However, type aliases require reexporting the original type, so we need
+		// to allow it (see also the comment in cmd/compile/internal/gc/bimport.go,
+		// method importer.obj, switch case importing functions).
+		// TODO(gri) review/update this comment once the gc compiler handles type aliases.
+		if !sameObj(obj, alt) {
+			errorf("inconsistent import:\n\t%v\npreviously imported as:\n\t%v\n", obj, alt)
+		}
+	}
+}
+
+func (p *importer) obj(tag int) {
+	switch tag {
+	case constTag:
+		pos := p.pos()
+		pkg, name := p.qualifiedName()
+		typ := p.typ(nil, nil)
+		val := p.value()
+		p.declare(types.NewConst(pos, pkg, name, typ, val))
+
+	case aliasTag:
+		// TODO(gri) verify type alias hookup is correct
+		pos := p.pos()
+		pkg, name := p.qualifiedName()
+		typ := p.typ(nil, nil)
+		p.declare(types.NewTypeName(pos, pkg, name, typ))
+
+	case typeTag:
+		p.typ(nil, nil)
+
+	case varTag:
+		pos := p.pos()
+		pkg, name := p.qualifiedName()
+		typ := p.typ(nil, nil)
+		p.declare(types.NewVar(pos, pkg, name, typ))
+
+	case funcTag:
+		pos := p.pos()
+		pkg, name := p.qualifiedName()
+		params, isddd := p.paramList()
+		result, _ := p.paramList()
+		sig := types.NewSignature(nil, params, result, isddd)
+		p.declare(types.NewFunc(pos, pkg, name, sig))
+
+	default:
+		errorf("unexpected object tag %d", tag)
+	}
+}
+
+const deltaNewFile = -64 // see cmd/compile/internal/gc/bexport.go
+
+func (p *importer) pos() token.Pos {
+	if !p.posInfoFormat {
+		return token.NoPos
+	}
+
+	file := p.prevFile
+	line := p.prevLine
+	delta := p.int()
+	line += delta
+	if p.version >= 5 {
+		if delta == deltaNewFile {
+			if n := p.int(); n >= 0 {
+				// file changed
+				file = p.path()
+				line = n
+			}
+		}
+	} else {
+		if delta == 0 {
+			if n := p.int(); n >= 0 {
+				// file changed
+				file = p.prevFile[:n] + p.string()
+				line = p.int()
+			}
+		}
+	}
+	p.prevFile = file
+	p.prevLine = line
+
+	return p.fake.pos(file, line, 0)
+}
+
+// Synthesize a token.Pos
+type fakeFileSet struct {
+	fset  *token.FileSet
+	files map[string]*token.File
+}
+
+func (s *fakeFileSet) pos(file string, line, column int) token.Pos {
+	// TODO(mdempsky): Make use of column.
+
+	// Since we don't know the set of needed file positions, we
+	// reserve maxlines positions per file.
+	const maxlines = 64 * 1024
+	f := s.files[file]
+	if f == nil {
+		f = s.fset.AddFile(file, -1, maxlines)
+		s.files[file] = f
+		// Allocate the fake linebreak indices on first use.
+		// TODO(adonovan): opt: save ~512KB using a more complex scheme?
+		fakeLinesOnce.Do(func() {
+			fakeLines = make([]int, maxlines)
+			for i := range fakeLines {
+				fakeLines[i] = i
+			}
+		})
+		f.SetLines(fakeLines)
+	}
+
+	if line > maxlines {
+		line = 1
+	}
+
+	// Treat the file as if it contained only newlines
+	// and column=1: use the line number as the offset.
+	return f.Pos(line - 1)
+}
+
+var (
+	fakeLines     []int
+	fakeLinesOnce sync.Once
+)
+
+func (p *importer) qualifiedName() (pkg *types.Package, name string) {
+	name = p.string()
+	pkg = p.pkg()
+	return
+}
+
+func (p *importer) record(t types.Type) {
+	p.typList = append(p.typList, t)
+}
+
+// A dddSlice is a types.Type representing ...T parameters.
+// It only appears for parameter types and does not escape
+// the importer.
+type dddSlice struct {
+	elem types.Type
+}
+
+func (t *dddSlice) Underlying() types.Type { return t }
+func (t *dddSlice) String() string         { return "..." + t.elem.String() }
+
+// parent is the package which declared the type; parent == nil means
+// the package currently imported. The parent package is needed for
+// exported struct fields and interface methods which don't contain
+// explicit package information in the export data.
+//
+// A non-nil tname is used as the "owner" of the result type; i.e.,
+// the result type is the underlying type of tname. tname is used
+// to give interface methods a named receiver type where possible.
+func (p *importer) typ(parent *types.Package, tname *types.Named) types.Type {
+	// if the type was seen before, i is its index (>= 0)
+	i := p.tagOrIndex()
+	if i >= 0 {
+		return p.typList[i]
+	}
+
+	// otherwise, i is the type tag (< 0)
+	switch i {
+	case namedTag:
+		// read type object
+		pos := p.pos()
+		parent, name := p.qualifiedName()
+		scope := parent.Scope()
+		obj := scope.Lookup(name)
+
+		// if the object doesn't exist yet, create and insert it
+		if obj == nil {
+			obj = types.NewTypeName(pos, parent, name, nil)
+			scope.Insert(obj)
+		}
+
+		if _, ok := obj.(*types.TypeName); !ok {
+			errorf("pkg = %s, name = %s => %s", parent, name, obj)
+		}
+
+		// associate new named type with obj if it doesn't exist yet
+		t0 := types.NewNamed(obj.(*types.TypeName), nil, nil)
+
+		// but record the existing type, if any
+		tname := obj.Type().(*types.Named) // tname is either t0 or the existing type
+		p.record(tname)
+
+		// read underlying type
+		t0.SetUnderlying(p.typ(parent, t0))
+
+		// interfaces don't have associated methods
+		if types.IsInterface(t0) {
+			return tname
+		}
+
+		// read associated methods
+		for i := p.int(); i > 0; i-- {
+			// TODO(gri) replace this with something closer to fieldName
+			pos := p.pos()
+			name := p.string()
+			if !exported(name) {
+				p.pkg()
+			}
+
+			recv, _ := p.paramList() // TODO(gri) do we need a full param list for the receiver?
+			params, isddd := p.paramList()
+			result, _ := p.paramList()
+			p.int() // go:nointerface pragma - discarded
+
+			sig := types.NewSignature(recv.At(0), params, result, isddd)
+			t0.AddMethod(types.NewFunc(pos, parent, name, sig))
+		}
+
+		return tname
+
+	case arrayTag:
+		t := new(types.Array)
+		if p.trackAllTypes {
+			p.record(t)
+		}
+
+		n := p.int64()
+		*t = *types.NewArray(p.typ(parent, nil), n)
+		return t
+
+	case sliceTag:
+		t := new(types.Slice)
+		if p.trackAllTypes {
+			p.record(t)
+		}
+
+		*t = *types.NewSlice(p.typ(parent, nil))
+		return t
+
+	case dddTag:
+		t := new(dddSlice)
+		if p.trackAllTypes {
+			p.record(t)
+		}
+
+		t.elem = p.typ(parent, nil)
+		return t
+
+	case structTag:
+		t := new(types.Struct)
+		if p.trackAllTypes {
+			p.record(t)
+		}
+
+		*t = *types.NewStruct(p.fieldList(parent))
+		return t
+
+	case pointerTag:
+		t := new(types.Pointer)
+		if p.trackAllTypes {
+			p.record(t)
+		}
+
+		*t = *types.NewPointer(p.typ(parent, nil))
+		return t
+
+	case signatureTag:
+		t := new(types.Signature)
+		if p.trackAllTypes {
+			p.record(t)
+		}
+
+		params, isddd := p.paramList()
+		result, _ := p.paramList()
+		*t = *types.NewSignature(nil, params, result, isddd)
+		return t
+
+	case interfaceTag:
+		// Create a dummy entry in the type list. This is safe because we
+		// cannot expect the interface type to appear in a cycle, as any
+		// such cycle must contain a named type which would have been
+		// first defined earlier.
+		// TODO(gri) Is this still true now that we have type aliases?
+		// See issue #23225.
+		n := len(p.typList)
+		if p.trackAllTypes {
+			p.record(nil)
+		}
+
+		var embeddeds []types.Type
+		for n := p.int(); n > 0; n-- {
+			p.pos()
+			embeddeds = append(embeddeds, p.typ(parent, nil))
+		}
+
+		t := newInterface(p.methodList(parent, tname), embeddeds)
+		p.interfaceList = append(p.interfaceList, t)
+		if p.trackAllTypes {
+			p.typList[n] = t
+		}
+		return t
+
+	case mapTag:
+		t := new(types.Map)
+		if p.trackAllTypes {
+			p.record(t)
+		}
+
+		key := p.typ(parent, nil)
+		val := p.typ(parent, nil)
+		*t = *types.NewMap(key, val)
+		return t
+
+	case chanTag:
+		t := new(types.Chan)
+		if p.trackAllTypes {
+			p.record(t)
+		}
+
+		dir := chanDir(p.int())
+		val := p.typ(parent, nil)
+		*t = *types.NewChan(dir, val)
+		return t
+
+	default:
+		errorf("unexpected type tag %d", i) // panics
+		panic("unreachable")
+	}
+}
+
+func chanDir(d int) types.ChanDir {
+	// tag values must match the constants in cmd/compile/internal/gc/go.go
+	switch d {
+	case 1 /* Crecv */ :
+		return types.RecvOnly
+	case 2 /* Csend */ :
+		return types.SendOnly
+	case 3 /* Cboth */ :
+		return types.SendRecv
+	default:
+		errorf("unexpected channel dir %d", d)
+		return 0
+	}
+}
+
+func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags []string) {
+	if n := p.int(); n > 0 {
+		fields = make([]*types.Var, n)
+		tags = make([]string, n)
+		for i := range fields {
+			fields[i], tags[i] = p.field(parent)
+		}
+	}
+	return
+}
+
+func (p *importer) field(parent *types.Package) (*types.Var, string) {
+	pos := p.pos()
+	pkg, name, alias := p.fieldName(parent)
+	typ := p.typ(parent, nil)
+	tag := p.string()
+
+	anonymous := false
+	if name == "" {
+		// anonymous field - typ must be T or *T and T must be a type name
+		switch typ := deref(typ).(type) {
+		case *types.Basic: // basic types are named types
+			pkg = nil // // objects defined in Universe scope have no package
+			name = typ.Name()
+		case *types.Named:
+			name = typ.Obj().Name()
+		default:
+			errorf("named base type expected")
+		}
+		anonymous = true
+	} else if alias {
+		// anonymous field: we have an explicit name because it's an alias
+		anonymous = true
+	}
+
+	return types.NewField(pos, pkg, name, typ, anonymous), tag
+}
+
+func (p *importer) methodList(parent *types.Package, baseType *types.Named) (methods []*types.Func) {
+	if n := p.int(); n > 0 {
+		methods = make([]*types.Func, n)
+		for i := range methods {
+			methods[i] = p.method(parent, baseType)
+		}
+	}
+	return
+}
+
+func (p *importer) method(parent *types.Package, baseType *types.Named) *types.Func {
+	pos := p.pos()
+	pkg, name, _ := p.fieldName(parent)
+	// If we don't have a baseType, use a nil receiver.
+	// A receiver using the actual interface type (which
+	// we don't know yet) will be filled in when we call
+	// types.Interface.Complete.
+	var recv *types.Var
+	if baseType != nil {
+		recv = types.NewVar(token.NoPos, parent, "", baseType)
+	}
+	params, isddd := p.paramList()
+	result, _ := p.paramList()
+	sig := types.NewSignature(recv, params, result, isddd)
+	return types.NewFunc(pos, pkg, name, sig)
+}
+
+func (p *importer) fieldName(parent *types.Package) (pkg *types.Package, name string, alias bool) {
+	name = p.string()
+	pkg = parent
+	if pkg == nil {
+		// use the imported package instead
+		pkg = p.pkgList[0]
+	}
+	if p.version == 0 && name == "_" {
+		// version 0 didn't export a package for _ fields
+		return
+	}
+	switch name {
+	case "":
+		// 1) field name matches base type name and is exported: nothing to do
+	case "?":
+		// 2) field name matches base type name and is not exported: need package
+		name = ""
+		pkg = p.pkg()
+	case "@":
+		// 3) field name doesn't match type name (alias)
+		name = p.string()
+		alias = true
+		fallthrough
+	default:
+		if !exported(name) {
+			pkg = p.pkg()
+		}
+	}
+	return
+}
+
+func (p *importer) paramList() (*types.Tuple, bool) {
+	n := p.int()
+	if n == 0 {
+		return nil, false
+	}
+	// negative length indicates unnamed parameters
+	named := true
+	if n < 0 {
+		n = -n
+		named = false
+	}
+	// n > 0
+	params := make([]*types.Var, n)
+	isddd := false
+	for i := range params {
+		params[i], isddd = p.param(named)
+	}
+	return types.NewTuple(params...), isddd
+}
+
+func (p *importer) param(named bool) (*types.Var, bool) {
+	t := p.typ(nil, nil)
+	td, isddd := t.(*dddSlice)
+	if isddd {
+		t = types.NewSlice(td.elem)
+	}
+
+	var pkg *types.Package
+	var name string
+	if named {
+		name = p.string()
+		if name == "" {
+			errorf("expected named parameter")
+		}
+		if name != "_" {
+			pkg = p.pkg()
+		}
+		if i := strings.Index(name, "·"); i > 0 {
+			name = name[:i] // cut off gc-specific parameter numbering
+		}
+	}
+
+	// read and discard compiler-specific info
+	p.string()
+
+	return types.NewVar(token.NoPos, pkg, name, t), isddd
+}
+
+func exported(name string) bool {
+	ch, _ := utf8.DecodeRuneInString(name)
+	return unicode.IsUpper(ch)
+}
+
+func (p *importer) value() constant.Value {
+	switch tag := p.tagOrIndex(); tag {
+	case falseTag:
+		return constant.MakeBool(false)
+	case trueTag:
+		return constant.MakeBool(true)
+	case int64Tag:
+		return constant.MakeInt64(p.int64())
+	case floatTag:
+		return p.float()
+	case complexTag:
+		re := p.float()
+		im := p.float()
+		return constant.BinaryOp(re, token.ADD, constant.MakeImag(im))
+	case stringTag:
+		return constant.MakeString(p.string())
+	case unknownTag:
+		return constant.MakeUnknown()
+	default:
+		errorf("unexpected value tag %d", tag) // panics
+		panic("unreachable")
+	}
+}
+
+func (p *importer) float() constant.Value {
+	sign := p.int()
+	if sign == 0 {
+		return constant.MakeInt64(0)
+	}
+
+	exp := p.int()
+	mant := []byte(p.string()) // big endian
+
+	// remove leading 0's if any
+	for len(mant) > 0 && mant[0] == 0 {
+		mant = mant[1:]
+	}
+
+	// convert to little endian
+	// TODO(gri) go/constant should have a more direct conversion function
+	//           (e.g., once it supports a big.Float based implementation)
+	for i, j := 0, len(mant)-1; i < j; i, j = i+1, j-1 {
+		mant[i], mant[j] = mant[j], mant[i]
+	}
+
+	// adjust exponent (constant.MakeFromBytes creates an integer value,
+	// but mant represents the mantissa bits such that 0.5 <= mant < 1.0)
+	exp -= len(mant) << 3
+	if len(mant) > 0 {
+		for msd := mant[len(mant)-1]; msd&0x80 == 0; msd <<= 1 {
+			exp++
+		}
+	}
+
+	x := constant.MakeFromBytes(mant)
+	switch {
+	case exp < 0:
+		d := constant.Shift(constant.MakeInt64(1), token.SHL, uint(-exp))
+		x = constant.BinaryOp(x, token.QUO, d)
+	case exp > 0:
+		x = constant.Shift(x, token.SHL, uint(exp))
+	}
+
+	if sign < 0 {
+		x = constant.UnaryOp(token.SUB, x, 0)
+	}
+	return x
+}
+
+// ----------------------------------------------------------------------------
+// Low-level decoders
+
+func (p *importer) tagOrIndex() int {
+	if p.debugFormat {
+		p.marker('t')
+	}
+
+	return int(p.rawInt64())
+}
+
+func (p *importer) int() int {
+	x := p.int64()
+	if int64(int(x)) != x {
+		errorf("exported integer too large")
+	}
+	return int(x)
+}
+
+func (p *importer) int64() int64 {
+	if p.debugFormat {
+		p.marker('i')
+	}
+
+	return p.rawInt64()
+}
+
+func (p *importer) path() string {
+	if p.debugFormat {
+		p.marker('p')
+	}
+	// if the path was seen before, i is its index (>= 0)
+	// (the empty string is at index 0)
+	i := p.rawInt64()
+	if i >= 0 {
+		return p.pathList[i]
+	}
+	// otherwise, i is the negative path length (< 0)
+	a := make([]string, -i)
+	for n := range a {
+		a[n] = p.string()
+	}
+	s := strings.Join(a, "/")
+	p.pathList = append(p.pathList, s)
+	return s
+}
+
+func (p *importer) string() string {
+	if p.debugFormat {
+		p.marker('s')
+	}
+	// if the string was seen before, i is its index (>= 0)
+	// (the empty string is at index 0)
+	i := p.rawInt64()
+	if i >= 0 {
+		return p.strList[i]
+	}
+	// otherwise, i is the negative string length (< 0)
+	if n := int(-i); n <= cap(p.buf) {
+		p.buf = p.buf[:n]
+	} else {
+		p.buf = make([]byte, n)
+	}
+	for i := range p.buf {
+		p.buf[i] = p.rawByte()
+	}
+	s := string(p.buf)
+	p.strList = append(p.strList, s)
+	return s
+}
+
+func (p *importer) marker(want byte) {
+	if got := p.rawByte(); got != want {
+		errorf("incorrect marker: got %c; want %c (pos = %d)", got, want, p.read)
+	}
+
+	pos := p.read
+	if n := int(p.rawInt64()); n != pos {
+		errorf("incorrect position: got %d; want %d", n, pos)
+	}
+}
+
+// rawInt64 should only be used by low-level decoders.
+func (p *importer) rawInt64() int64 {
+	i, err := binary.ReadVarint(p)
+	if err != nil {
+		errorf("read error: %v", err)
+	}
+	return i
+}
+
+// rawStringln should only be used to read the initial version string.
+func (p *importer) rawStringln(b byte) string {
+	p.buf = p.buf[:0]
+	for b != '\n' {
+		p.buf = append(p.buf, b)
+		b = p.rawByte()
+	}
+	return string(p.buf)
+}
+
+// needed for binary.ReadVarint in rawInt64
+func (p *importer) ReadByte() (byte, error) {
+	return p.rawByte(), nil
+}
+
+// byte is the bottleneck interface for reading p.data.
+// It unescapes '|' 'S' to '$' and '|' '|' to '|'.
+// rawByte should only be used by low-level decoders.
+func (p *importer) rawByte() byte {
+	b := p.data[0]
+	r := 1
+	if b == '|' {
+		b = p.data[1]
+		r = 2
+		switch b {
+		case 'S':
+			b = '$'
+		case '|':
+			// nothing to do
+		default:
+			errorf("unexpected escape sequence in export data")
+		}
+	}
+	p.data = p.data[r:]
+	p.read += r
+	return b
+
+}
+
+// ----------------------------------------------------------------------------
+// Export format
+
+// Tags. Must be < 0.
+const (
+	// Objects
+	packageTag = -(iota + 1)
+	constTag
+	typeTag
+	varTag
+	funcTag
+	endTag
+
+	// Types
+	namedTag
+	arrayTag
+	sliceTag
+	dddTag
+	structTag
+	pointerTag
+	signatureTag
+	interfaceTag
+	mapTag
+	chanTag
+
+	// Values
+	falseTag
+	trueTag
+	int64Tag
+	floatTag
+	fractionTag // not used by gc
+	complexTag
+	stringTag
+	nilTag     // only used by gc (appears in exported inlined function bodies)
+	unknownTag // not used by gc (only appears in packages with errors)
+
+	// Type aliases
+	aliasTag
+)
+
+var predeclOnce sync.Once
+var predecl []types.Type // initialized lazily
+
+func predeclared() []types.Type {
+	predeclOnce.Do(func() {
+		// initialize lazily to be sure that all
+		// elements have been initialized before
+		predecl = []types.Type{ // basic types
+			types.Typ[types.Bool],
+			types.Typ[types.Int],
+			types.Typ[types.Int8],
+			types.Typ[types.Int16],
+			types.Typ[types.Int32],
+			types.Typ[types.Int64],
+			types.Typ[types.Uint],
+			types.Typ[types.Uint8],
+			types.Typ[types.Uint16],
+			types.Typ[types.Uint32],
+			types.Typ[types.Uint64],
+			types.Typ[types.Uintptr],
+			types.Typ[types.Float32],
+			types.Typ[types.Float64],
+			types.Typ[types.Complex64],
+			types.Typ[types.Complex128],
+			types.Typ[types.String],
+
+			// basic type aliases
+			types.Universe.Lookup("byte").Type(),
+			types.Universe.Lookup("rune").Type(),
+
+			// error
+			types.Universe.Lookup("error").Type(),
+
+			// untyped types
+			types.Typ[types.UntypedBool],
+			types.Typ[types.UntypedInt],
+			types.Typ[types.UntypedRune],
+			types.Typ[types.UntypedFloat],
+			types.Typ[types.UntypedComplex],
+			types.Typ[types.UntypedString],
+			types.Typ[types.UntypedNil],
+
+			// package unsafe
+			types.Typ[types.UnsafePointer],
+
+			// invalid type
+			types.Typ[types.Invalid], // only appears in packages with errors
+
+			// used internally by gc; never used by this package or in .a files
+			anyType{},
+		}
+	})
+	return predecl
+}
+
+type anyType struct{}
+
+func (t anyType) Underlying() types.Type { return t }
+func (t anyType) String() string         { return "any" }
diff --git a/go/internal/gcimporter/gcimporter.go b/go/internal/gcimporter/gcimporter.go
index 6a9265e..8dcd8bb 100644
--- a/go/internal/gcimporter/gcimporter.go
+++ b/go/internal/gcimporter/gcimporter.go
@@ -204,11 +204,14 @@
 		// Or, define a new standard go/types/gcexportdata package.
 		fset := token.NewFileSet()
 
-		// The indexed export format starts with an 'i'.
-		if len(data) == 0 || data[0] != 'i' {
-			return nil, fmt.Errorf("unknown export data format")
+		// The indexed export format starts with an 'i'; the older
+		// binary export format starts with a 'c', 'd', or 'v'
+		// (from "version"). Select appropriate importer.
+		if len(data) > 0 && data[0] == 'i' {
+			_, pkg, err = IImportData(fset, packages, data[1:], id)
+		} else {
+			_, pkg, err = BImportData(fset, packages, data, id)
 		}
-		_, pkg, err = IImportData(fset, packages, data[1:], id)
 
 	default:
 		err = fmt.Errorf("unknown export data header: %q", hdr)
diff --git a/go/internal/gcimporter/gcimporter11_test.go b/go/internal/gcimporter/gcimporter11_test.go
index 627300d..3f2f0a0 100644
--- a/go/internal/gcimporter/gcimporter11_test.go
+++ b/go/internal/gcimporter/gcimporter11_test.go
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build go1.11
-
 package gcimporter
 
 import (
@@ -11,6 +9,8 @@
 	"runtime"
 	"strings"
 	"testing"
+
+	"golang.org/x/tools/internal/testenv"
 )
 
 var importedObjectTests = []struct {
@@ -38,6 +38,7 @@
 }
 
 func TestImportedTypes(t *testing.T) {
+	testenv.NeedsGo1Point(t, 11)
 	skipSpecialPlatforms(t)
 
 	// This package only handles gc export data.
@@ -112,6 +113,7 @@
 	}
 }
 func TestIssue25301(t *testing.T) {
+	testenv.NeedsGo1Point(t, 11)
 	skipSpecialPlatforms(t)
 
 	// This package only handles gc export data.
diff --git a/go/internal/gcimporter/iexport.go b/go/internal/gcimporter/iexport.go
index 858eb9f..4be32a2 100644
--- a/go/internal/gcimporter/iexport.go
+++ b/go/internal/gcimporter/iexport.go
@@ -11,7 +11,6 @@
 import (
 	"bytes"
 	"encoding/binary"
-	"fmt"
 	"go/ast"
 	"go/constant"
 	"go/token"
@@ -26,15 +25,6 @@
 // 0: Go1.11 encoding
 const iexportVersion = 0
 
-// internalError represents an error generated inside this package.
-type internalError string
-
-func (e internalError) Error() string { return "gcimporter: " + string(e) }
-
-func internalErrorf(format string, args ...interface{}) error {
-	return internalError(fmt.Sprintf(format, args...))
-}
-
 // IExportData returns the binary export data for pkg.
 //
 // If no file set is provided, position info will be missing.
@@ -538,16 +528,6 @@
 	return &f
 }
 
-func valueToRat(x constant.Value) *big.Rat {
-	// Convert little-endian to big-endian.
-	// I can't believe this is necessary.
-	bytes := constant.Bytes(x)
-	for i := 0; i < len(bytes)/2; i++ {
-		bytes[i], bytes[len(bytes)-1-i] = bytes[len(bytes)-1-i], bytes[i]
-	}
-	return new(big.Rat).SetInt(new(big.Int).SetBytes(bytes))
-}
-
 // mpint exports a multi-precision integer.
 //
 // For unsigned types, small values are written out as a single
diff --git a/go/internal/gcimporter/iexport_test.go b/go/internal/gcimporter/iexport_test.go
index f66135d..5024570 100644
--- a/go/internal/gcimporter/iexport_test.go
+++ b/go/internal/gcimporter/iexport_test.go
@@ -28,8 +28,6 @@
 	"golang.org/x/tools/go/loader"
 )
 
-var isRace = false
-
 func TestIExportData_stdlib(t *testing.T) {
 	if runtime.Compiler == "gccgo" {
 		t.Skip("gccgo standard library is inaccessible")
@@ -180,16 +178,6 @@
 	}
 }
 
-const src = `
-package p
-type (
-	T0 = int32
-	T1 = struct{}
-	T2 = struct{ T1 }
-	Invalid = foo // foo is undeclared
-)
-`
-
 func TestIExportData_typealiases(t *testing.T) {
 	// parse and typecheck
 	fset1 := token.NewFileSet()
@@ -319,220 +307,3 @@
 	}
 	return new(big.Rat).SetInt(new(big.Int).SetBytes(bytes))
 }
-
-func fileLine(fset *token.FileSet, obj types.Object) string {
-	posn := fset.Position(obj.Pos())
-	return fmt.Sprintf("%s:%d", posn.Filename, posn.Line)
-}
-
-// equalObj reports how x and y differ.  They are assumed to belong to
-// different universes so cannot be compared directly.
-func equalObj(x, y types.Object) error {
-	if reflect.TypeOf(x) != reflect.TypeOf(y) {
-		return fmt.Errorf("%T vs %T", x, y)
-	}
-	xt := x.Type()
-	yt := y.Type()
-	switch x.(type) {
-	case *types.Var, *types.Func:
-		// ok
-	case *types.Const:
-		xval := x.(*types.Const).Val()
-		yval := y.(*types.Const).Val()
-		// Use string comparison for floating-point values since rounding is permitted.
-		if constant.Compare(xval, token.NEQ, yval) &&
-			!(xval.Kind() == constant.Float && xval.String() == yval.String()) {
-			return fmt.Errorf("unequal constants %s vs %s", xval, yval)
-		}
-	case *types.TypeName:
-		xt = xt.Underlying()
-		yt = yt.Underlying()
-	default:
-		return fmt.Errorf("unexpected %T", x)
-	}
-	return equalType(xt, yt)
-}
-
-func equalType(x, y types.Type) error {
-	if reflect.TypeOf(x) != reflect.TypeOf(y) {
-		return fmt.Errorf("unequal kinds: %T vs %T", x, y)
-	}
-	switch x := x.(type) {
-	case *types.Interface:
-		y := y.(*types.Interface)
-		// TODO(gri): enable separate emission of Embedded interfaces
-		// and ExplicitMethods then use this logic.
-		// if x.NumEmbeddeds() != y.NumEmbeddeds() {
-		// 	return fmt.Errorf("unequal number of embedded interfaces: %d vs %d",
-		// 		x.NumEmbeddeds(), y.NumEmbeddeds())
-		// }
-		// for i := 0; i < x.NumEmbeddeds(); i++ {
-		// 	xi := x.Embedded(i)
-		// 	yi := y.Embedded(i)
-		// 	if xi.String() != yi.String() {
-		// 		return fmt.Errorf("mismatched %th embedded interface: %s vs %s",
-		// 			i, xi, yi)
-		// 	}
-		// }
-		// if x.NumExplicitMethods() != y.NumExplicitMethods() {
-		// 	return fmt.Errorf("unequal methods: %d vs %d",
-		// 		x.NumExplicitMethods(), y.NumExplicitMethods())
-		// }
-		// for i := 0; i < x.NumExplicitMethods(); i++ {
-		// 	xm := x.ExplicitMethod(i)
-		// 	ym := y.ExplicitMethod(i)
-		// 	if xm.Name() != ym.Name() {
-		// 		return fmt.Errorf("mismatched %th method: %s vs %s", i, xm, ym)
-		// 	}
-		// 	if err := equalType(xm.Type(), ym.Type()); err != nil {
-		// 		return fmt.Errorf("mismatched %s method: %s", xm.Name(), err)
-		// 	}
-		// }
-		if x.NumMethods() != y.NumMethods() {
-			return fmt.Errorf("unequal methods: %d vs %d",
-				x.NumMethods(), y.NumMethods())
-		}
-		for i := 0; i < x.NumMethods(); i++ {
-			xm := x.Method(i)
-			ym := y.Method(i)
-			if xm.Name() != ym.Name() {
-				return fmt.Errorf("mismatched %dth method: %s vs %s", i, xm, ym)
-			}
-			if err := equalType(xm.Type(), ym.Type()); err != nil {
-				return fmt.Errorf("mismatched %s method: %s", xm.Name(), err)
-			}
-		}
-	case *types.Array:
-		y := y.(*types.Array)
-		if x.Len() != y.Len() {
-			return fmt.Errorf("unequal array lengths: %d vs %d", x.Len(), y.Len())
-		}
-		if err := equalType(x.Elem(), y.Elem()); err != nil {
-			return fmt.Errorf("array elements: %s", err)
-		}
-	case *types.Basic:
-		y := y.(*types.Basic)
-		if x.Kind() != y.Kind() {
-			return fmt.Errorf("unequal basic types: %s vs %s", x, y)
-		}
-	case *types.Chan:
-		y := y.(*types.Chan)
-		if x.Dir() != y.Dir() {
-			return fmt.Errorf("unequal channel directions: %d vs %d", x.Dir(), y.Dir())
-		}
-		if err := equalType(x.Elem(), y.Elem()); err != nil {
-			return fmt.Errorf("channel elements: %s", err)
-		}
-	case *types.Map:
-		y := y.(*types.Map)
-		if err := equalType(x.Key(), y.Key()); err != nil {
-			return fmt.Errorf("map keys: %s", err)
-		}
-		if err := equalType(x.Elem(), y.Elem()); err != nil {
-			return fmt.Errorf("map values: %s", err)
-		}
-	case *types.Named:
-		y := y.(*types.Named)
-		if x.String() != y.String() {
-			return fmt.Errorf("unequal named types: %s vs %s", x, y)
-		}
-	case *types.Pointer:
-		y := y.(*types.Pointer)
-		if err := equalType(x.Elem(), y.Elem()); err != nil {
-			return fmt.Errorf("pointer elements: %s", err)
-		}
-	case *types.Signature:
-		y := y.(*types.Signature)
-		if err := equalType(x.Params(), y.Params()); err != nil {
-			return fmt.Errorf("parameters: %s", err)
-		}
-		if err := equalType(x.Results(), y.Results()); err != nil {
-			return fmt.Errorf("results: %s", err)
-		}
-		if x.Variadic() != y.Variadic() {
-			return fmt.Errorf("unequal variadicity: %t vs %t",
-				x.Variadic(), y.Variadic())
-		}
-		if (x.Recv() != nil) != (y.Recv() != nil) {
-			return fmt.Errorf("unequal receivers: %s vs %s", x.Recv(), y.Recv())
-		}
-		if x.Recv() != nil {
-			// TODO(adonovan): fix: this assertion fires for interface methods.
-			// The type of the receiver of an interface method is a named type
-			// if the Package was loaded from export data, or an unnamed (interface)
-			// type if the Package was produced by type-checking ASTs.
-			// if err := equalType(x.Recv().Type(), y.Recv().Type()); err != nil {
-			// 	return fmt.Errorf("receiver: %s", err)
-			// }
-		}
-	case *types.Slice:
-		y := y.(*types.Slice)
-		if err := equalType(x.Elem(), y.Elem()); err != nil {
-			return fmt.Errorf("slice elements: %s", err)
-		}
-	case *types.Struct:
-		y := y.(*types.Struct)
-		if x.NumFields() != y.NumFields() {
-			return fmt.Errorf("unequal struct fields: %d vs %d",
-				x.NumFields(), y.NumFields())
-		}
-		for i := 0; i < x.NumFields(); i++ {
-			xf := x.Field(i)
-			yf := y.Field(i)
-			if xf.Name() != yf.Name() {
-				return fmt.Errorf("mismatched fields: %s vs %s", xf, yf)
-			}
-			if err := equalType(xf.Type(), yf.Type()); err != nil {
-				return fmt.Errorf("struct field %s: %s", xf.Name(), err)
-			}
-			if x.Tag(i) != y.Tag(i) {
-				return fmt.Errorf("struct field %s has unequal tags: %q vs %q",
-					xf.Name(), x.Tag(i), y.Tag(i))
-			}
-		}
-	case *types.Tuple:
-		y := y.(*types.Tuple)
-		if x.Len() != y.Len() {
-			return fmt.Errorf("unequal tuple lengths: %d vs %d", x.Len(), y.Len())
-		}
-		for i := 0; i < x.Len(); i++ {
-			if err := equalType(x.At(i).Type(), y.At(i).Type()); err != nil {
-				return fmt.Errorf("tuple element %d: %s", i, err)
-			}
-		}
-	}
-	return nil
-}
-
-func checkPkg(t *testing.T, pkg *types.Package, label string) {
-	T1 := types.NewStruct(nil, nil)
-	T2 := types.NewStruct([]*types.Var{types.NewField(0, pkg, "T1", T1, true)}, nil)
-
-	for _, test := range []struct {
-		name string
-		typ  types.Type
-	}{
-		{"T0", types.Typ[types.Int32]},
-		{"T1", T1},
-		{"T2", T2},
-		{"Invalid", types.Typ[types.Invalid]},
-	} {
-		obj := pkg.Scope().Lookup(test.name)
-		if obj == nil {
-			t.Errorf("%s: %s not found", label, test.name)
-			continue
-		}
-		tname, _ := obj.(*types.TypeName)
-		if tname == nil {
-			t.Errorf("%s: %v not a type name", label, obj)
-			continue
-		}
-		if !tname.IsAlias() {
-			t.Errorf("%s: %v: not marked as alias", label, tname)
-			continue
-		}
-		if got := tname.Type(); !types.Identical(got, test.typ) {
-			t.Errorf("%s: %v: got %v; want %v", label, tname, got, test.typ)
-		}
-	}
-}
diff --git a/go/internal/gcimporter/iimport.go b/go/internal/gcimporter/iimport.go
index fef8b30..a31a880 100644
--- a/go/internal/gcimporter/iimport.go
+++ b/go/internal/gcimporter/iimport.go
@@ -18,9 +18,6 @@
 	"go/types"
 	"io"
 	"sort"
-	"sync"
-	"unicode"
-	"unicode/utf8"
 )
 
 type intReader struct {
@@ -28,10 +25,6 @@
 	path string
 }
 
-func errorf(format string, args ...interface{}) {
-	panic(fmt.Sprintf(format, args...))
-}
-
 func (r *intReader) int64() int64 {
 	i, err := binary.ReadVarint(r.Reader)
 	if err != nil {
@@ -635,166 +628,3 @@
 	}
 	return x
 }
-
-const deltaNewFile = -64 // see cmd/compile/internal/gc/bexport.go
-
-// Synthesize a token.Pos
-type fakeFileSet struct {
-	fset  *token.FileSet
-	files map[string]*token.File
-}
-
-func (s *fakeFileSet) pos(file string, line, column int) token.Pos {
-	// TODO(mdempsky): Make use of column.
-
-	// Since we don't know the set of needed file positions, we
-	// reserve maxlines positions per file.
-	const maxlines = 64 * 1024
-	f := s.files[file]
-	if f == nil {
-		f = s.fset.AddFile(file, -1, maxlines)
-		s.files[file] = f
-		// Allocate the fake linebreak indices on first use.
-		// TODO(adonovan): opt: save ~512KB using a more complex scheme?
-		fakeLinesOnce.Do(func() {
-			fakeLines = make([]int, maxlines)
-			for i := range fakeLines {
-				fakeLines[i] = i
-			}
-		})
-		f.SetLines(fakeLines)
-	}
-
-	if line > maxlines {
-		line = 1
-	}
-
-	// Treat the file as if it contained only newlines
-	// and column=1: use the line number as the offset.
-	return f.Pos(line - 1)
-}
-
-var (
-	fakeLines     []int
-	fakeLinesOnce sync.Once
-)
-
-func chanDir(d int) types.ChanDir {
-	// tag values must match the constants in cmd/compile/internal/gc/go.go
-	switch d {
-	case 1 /* Crecv */ :
-		return types.RecvOnly
-	case 2 /* Csend */ :
-		return types.SendOnly
-	case 3 /* Cboth */ :
-		return types.SendRecv
-	default:
-		errorf("unexpected channel dir %d", d)
-		return 0
-	}
-}
-
-func exported(name string) bool {
-	ch, _ := utf8.DecodeRuneInString(name)
-	return unicode.IsUpper(ch)
-}
-
-// ----------------------------------------------------------------------------
-// Export format
-
-// Tags. Must be < 0.
-const (
-	// Objects
-	packageTag = -(iota + 1)
-	constTag
-	typeTag
-	varTag
-	funcTag
-	endTag
-
-	// Types
-	namedTag
-	arrayTag
-	sliceTag
-	dddTag
-	structTag
-	pointerTag
-	signatureTag
-	interfaceTag
-	mapTag
-	chanTag
-
-	// Values
-	falseTag
-	trueTag
-	int64Tag
-	floatTag
-	fractionTag // not used by gc
-	complexTag
-	stringTag
-	nilTag     // only used by gc (appears in exported inlined function bodies)
-	unknownTag // not used by gc (only appears in packages with errors)
-
-	// Type aliases
-	aliasTag
-)
-
-var predeclOnce sync.Once
-var predecl []types.Type // initialized lazily
-
-func predeclared() []types.Type {
-	predeclOnce.Do(func() {
-		// initialize lazily to be sure that all
-		// elements have been initialized before
-		predecl = []types.Type{ // basic types
-			types.Typ[types.Bool],
-			types.Typ[types.Int],
-			types.Typ[types.Int8],
-			types.Typ[types.Int16],
-			types.Typ[types.Int32],
-			types.Typ[types.Int64],
-			types.Typ[types.Uint],
-			types.Typ[types.Uint8],
-			types.Typ[types.Uint16],
-			types.Typ[types.Uint32],
-			types.Typ[types.Uint64],
-			types.Typ[types.Uintptr],
-			types.Typ[types.Float32],
-			types.Typ[types.Float64],
-			types.Typ[types.Complex64],
-			types.Typ[types.Complex128],
-			types.Typ[types.String],
-
-			// basic type aliases
-			types.Universe.Lookup("byte").Type(),
-			types.Universe.Lookup("rune").Type(),
-
-			// error
-			types.Universe.Lookup("error").Type(),
-
-			// untyped types
-			types.Typ[types.UntypedBool],
-			types.Typ[types.UntypedInt],
-			types.Typ[types.UntypedRune],
-			types.Typ[types.UntypedFloat],
-			types.Typ[types.UntypedComplex],
-			types.Typ[types.UntypedString],
-			types.Typ[types.UntypedNil],
-
-			// package unsafe
-			types.Typ[types.UnsafePointer],
-
-			// invalid type
-			types.Typ[types.Invalid], // only appears in packages with errors
-
-			// used internally by gc; never used by this package or in .a files
-			anyType{},
-		}
-	})
-	return predecl
-}
-
-type anyType struct{}
-
-func (t anyType) Underlying() types.Type { return t }
-func (t anyType) String() string         { return "any" }
diff --git a/go/internal/gcimporter/testdata/versions/test_go1.11_6b.a b/go/internal/gcimporter/testdata/versions/test_go1.11_6b.a
new file mode 100644
index 0000000..c0a211e
--- /dev/null
+++ b/go/internal/gcimporter/testdata/versions/test_go1.11_6b.a
Binary files differ
diff --git a/go/internal/gcimporter/testdata/versions/test_go1.11_999b.a b/go/internal/gcimporter/testdata/versions/test_go1.11_999b.a
new file mode 100644
index 0000000..c35d22d
--- /dev/null
+++ b/go/internal/gcimporter/testdata/versions/test_go1.11_999b.a
Binary files differ
diff --git a/go/internal/gcimporter/testdata/versions/test_go1.7_0.a b/go/internal/gcimporter/testdata/versions/test_go1.7_0.a
new file mode 100644
index 0000000..edb6c3f
--- /dev/null
+++ b/go/internal/gcimporter/testdata/versions/test_go1.7_0.a
Binary files differ
diff --git a/go/internal/gcimporter/testdata/versions/test_go1.7_1.a b/go/internal/gcimporter/testdata/versions/test_go1.7_1.a
new file mode 100644
index 0000000..554d04a
--- /dev/null
+++ b/go/internal/gcimporter/testdata/versions/test_go1.7_1.a
Binary files differ
diff --git a/go/internal/gcimporter/testdata/versions/test_go1.8_4.a b/go/internal/gcimporter/testdata/versions/test_go1.8_4.a
new file mode 100644
index 0000000..26b8531
--- /dev/null
+++ b/go/internal/gcimporter/testdata/versions/test_go1.8_4.a
Binary files differ
diff --git a/go/internal/gcimporter/testdata/versions/test_go1.8_5.a b/go/internal/gcimporter/testdata/versions/test_go1.8_5.a
new file mode 100644
index 0000000..60e52ef
--- /dev/null
+++ b/go/internal/gcimporter/testdata/versions/test_go1.8_5.a
Binary files differ
diff --git a/go/packages/golist.go b/go/packages/golist.go
index 88ca669..6e91391 100644
--- a/go/packages/golist.go
+++ b/go/packages/golist.go
@@ -24,7 +24,6 @@
 
 	"golang.org/x/tools/go/internal/packagesdriver"
 	"golang.org/x/tools/internal/gocommand"
-	"golang.org/x/tools/internal/packagesinternal"
 	"golang.org/x/xerrors"
 )
 
@@ -382,7 +381,7 @@
 	Imports         []string
 	ImportMap       map[string]string
 	Deps            []string
-	Module          *packagesinternal.Module
+	Module          *Module
 	TestGoFiles     []string
 	TestImports     []string
 	XTestGoFiles    []string
@@ -541,7 +540,26 @@
 			CompiledGoFiles: absJoin(p.Dir, p.CompiledGoFiles),
 			OtherFiles:      absJoin(p.Dir, otherFiles(p)...),
 			forTest:         p.ForTest,
-			module:          p.Module,
+			Module:          p.Module,
+		}
+
+		if (state.cfg.Mode&typecheckCgo) != 0 && len(p.CgoFiles) != 0 {
+			if len(p.CompiledGoFiles) > len(p.GoFiles) {
+				// We need the cgo definitions, which are in the first
+				// CompiledGoFile after the non-cgo ones. This is a hack but there
+				// isn't currently a better way to find it. We also need the pure
+				// Go files and unprocessed cgo files, all of which are already
+				// in pkg.GoFiles.
+				cgoTypes := p.CompiledGoFiles[len(p.GoFiles)]
+				pkg.CompiledGoFiles = append([]string{cgoTypes}, pkg.GoFiles...)
+			} else {
+				// golang/go#38990: go list silently fails to do cgo processing
+				pkg.CompiledGoFiles = nil
+				pkg.Errors = append(pkg.Errors, Error{
+					Msg:  "go list failed to return CompiledGoFiles; https://golang.org/issue/38990?",
+					Kind: ListError,
+				})
+			}
 		}
 
 		// Work around https://golang.org/issue/28749:
diff --git a/go/packages/golist_overlay.go b/go/packages/golist_overlay.go
index 3c99b6e..338d6f6 100644
--- a/go/packages/golist_overlay.go
+++ b/go/packages/golist_overlay.go
@@ -70,9 +70,9 @@
 			// to the overlay.
 			continue
 		}
-		// if all the overlay files belong to a different package, change the package
-		// name to that package. Otherwise leave it alone; there will be an error message.
-		maybeFixPackageName(pkgName, pkgOfDir, dir)
+		// If all the overlay files belong to a different package, change the
+		// package name to that package.
+		maybeFixPackageName(pkgName, isTestFile, pkgOfDir[dir])
 	nextPackage:
 		for _, p := range response.dr.Packages {
 			if pkgName != p.Name && p.ID != "command-line-arguments" {
@@ -113,24 +113,35 @@
 			if !ok {
 				break
 			}
+			var forTest string // only set for x tests
 			isXTest := strings.HasSuffix(pkgName, "_test")
 			if isXTest {
+				forTest = pkgPath
 				pkgPath += "_test"
 			}
 			id := pkgPath
-			if isTestFile && !isXTest {
-				id = fmt.Sprintf("%s [%s.test]", pkgPath, pkgPath)
+			if isTestFile {
+				if isXTest {
+					id = fmt.Sprintf("%s [%s.test]", pkgPath, forTest)
+				} else {
+					id = fmt.Sprintf("%s [%s.test]", pkgPath, pkgPath)
+				}
 			}
-			// Try to reclaim a package with the same id if it exists in the response.
+			// Try to reclaim a package with the same ID, if it exists in the response.
 			for _, p := range response.dr.Packages {
 				if reclaimPackage(p, id, opath, contents) {
 					pkg = p
 					break
 				}
 			}
-			// Otherwise, create a new package
+			// Otherwise, create a new package.
 			if pkg == nil {
-				pkg = &Package{PkgPath: pkgPath, ID: id, Name: pkgName, Imports: make(map[string]*Package)}
+				pkg = &Package{
+					PkgPath: pkgPath,
+					ID:      id,
+					Name:    pkgName,
+					Imports: make(map[string]*Package),
+				}
 				response.addPackage(pkg)
 				havePkgs[pkg.PkgPath] = id
 				// Add the production package's sources for a test variant.
@@ -143,6 +154,9 @@
 						pkg.Imports[k] = &Package{ID: v.ID}
 					}
 				}
+				if isXTest {
+					pkg.forTest = forTest
+				}
 			}
 		}
 		if !fileExists {
@@ -158,6 +172,8 @@
 			continue
 		}
 		for _, imp := range imports {
+			// TODO(rstambler): If the package is an x test and the import has
+			// a test variant, make sure to replace it.
 			if _, found := pkg.Imports[imp]; found {
 				continue
 			}
@@ -415,24 +431,35 @@
 // package name, and they all have the same package name, then that name becomes
 // the package name.
 // It returns true if it changes the package name, false otherwise.
-func maybeFixPackageName(newName string, pkgOfDir map[string][]*Package, dir string) bool {
+func maybeFixPackageName(newName string, isTestFile bool, pkgsOfDir []*Package) {
 	names := make(map[string]int)
-	for _, p := range pkgOfDir[dir] {
+	for _, p := range pkgsOfDir {
 		names[p.Name]++
 	}
 	if len(names) != 1 {
 		// some files are in different packages
-		return false
+		return
 	}
-	oldName := ""
+	var oldName string
 	for k := range names {
 		oldName = k
 	}
 	if newName == oldName {
-		return false
+		return
 	}
-	for _, p := range pkgOfDir[dir] {
+	// We might have a case where all of the package names in the directory are
+	// the same, but the overlay file is for an x test, which belongs to its
+	// own package. If the x test does not yet exist on disk, we may not yet
+	// have its package name on disk, but we should not rename the packages.
+	//
+	// We use a heuristic to determine if this file belongs to an x test:
+	// The test file should have a package name whose package name has a _test
+	// suffix or looks like "newName_test".
+	maybeXTest := strings.HasPrefix(oldName+"_test", newName) || strings.HasSuffix(newName, "_test")
+	if isTestFile && maybeXTest {
+		return
+	}
+	for _, p := range pkgsOfDir {
 		p.Name = newName
 	}
-	return true
 }
diff --git a/go/packages/overlay_test.go b/go/packages/overlay_test.go
index 7459f2b..618b508 100644
--- a/go/packages/overlay_test.go
+++ b/go/packages/overlay_test.go
@@ -1,8 +1,10 @@
 package packages_test
 
 import (
+	"fmt"
 	"log"
 	"path/filepath"
+	"reflect"
 	"testing"
 
 	"golang.org/x/tools/go/packages"
@@ -125,6 +127,91 @@
 	log.SetFlags(0)
 }
 
+func TestOverlayXTests(t *testing.T) {
+	packagestest.TestAll(t, testOverlayXTests)
+}
+
+// This test checks the behavior of go/packages.Load with an overlaid
+// x test. The source of truth is the go/packages.Load results for the
+// exact same package, just on-disk.
+func testOverlayXTests(t *testing.T, exporter packagestest.Exporter) {
+	const aFile = `package a; const C = "C"; func Hello() {}`
+	const aTestVariant = `package a
+
+import "testing"
+
+const TestC = "test" + C
+
+func TestHello(){
+	Hello()
+}`
+	const aXTest = `package a_test
+
+import (
+	"testing"
+
+	"golang.org/fake/a"
+)
+
+const xTestC = "x" + a.C
+
+func TestHello(t *testing.T) {
+	a.Hello()
+}`
+
+	// First, get the source of truth by loading the package, all on disk.
+	onDisk := packagestest.Export(t, exporter, []packagestest.Module{{
+		Name: "golang.org/fake",
+		Files: map[string]interface{}{
+			"a/a.go":        aFile,
+			"a/a_test.go":   aTestVariant,
+			"a/a_x_test.go": aXTest,
+		},
+	}})
+	defer onDisk.Cleanup()
+
+	onDisk.Config.Mode = commonMode
+	onDisk.Config.Tests = true
+	onDisk.Config.Mode = packages.LoadTypes
+	initial, err := packages.Load(onDisk.Config, fmt.Sprintf("file=%s", onDisk.File("golang.org/fake", "a/a_x_test.go")))
+	if err != nil {
+		t.Fatal(err)
+	}
+	wantPkg := initial[0]
+
+	exported := packagestest.Export(t, exporter, []packagestest.Module{{
+		Name: "golang.org/fake",
+		Files: map[string]interface{}{
+			"a/a.go":        aFile,
+			"a/a_test.go":   aTestVariant,
+			"a/a_x_test.go": ``, // empty x test on disk
+		},
+		Overlay: map[string][]byte{
+			"a/a_x_test.go": []byte(aXTest),
+		},
+	}})
+	defer exported.Cleanup()
+
+	if len(initial) != 1 {
+		t.Fatalf("expected 1 package, got %d", len(initial))
+	}
+	// Confirm that the overlaid package is identical to the on-disk version.
+	pkg := initial[0]
+	if !reflect.DeepEqual(wantPkg, pkg) {
+		t.Fatalf("mismatched packages: want %#v, got %#v", wantPkg, pkg)
+	}
+	xTestC := constant(pkg, "xTestC")
+	if xTestC == nil {
+		t.Fatalf("no value for xTestC")
+	}
+	got := xTestC.Val().String()
+	// TODO(rstambler): Ideally, this test would check that the test variant
+	// was imported, but that's pretty complicated.
+	if want := `"xC"`; got != want {
+		t.Errorf("got: %q, want %q", got, want)
+	}
+}
+
 func checkPkg(t *testing.T, p *packages.Package, id, name string, syntax int) bool {
 	t.Helper()
 	if p.ID == id && p.Name == name && len(p.Syntax) == syntax {
diff --git a/go/packages/packages.go b/go/packages/packages.go
index 03fd999..04053f1 100644
--- a/go/packages/packages.go
+++ b/go/packages/packages.go
@@ -21,10 +21,12 @@
 	"path/filepath"
 	"strings"
 	"sync"
+	"time"
 
 	"golang.org/x/tools/go/gcexportdata"
 	"golang.org/x/tools/internal/gocommand"
 	"golang.org/x/tools/internal/packagesinternal"
+	"golang.org/x/tools/internal/typesinternal"
 )
 
 // A LoadMode controls the amount of detail to return when loading.
@@ -70,6 +72,13 @@
 
 	// NeedTypesSizes adds TypesSizes.
 	NeedTypesSizes
+
+	// typecheckCgo enables full support for type checking cgo. Requires Go 1.15+.
+	// Modifies CompiledGoFiles and Types, and has no effect on its own.
+	typecheckCgo
+
+	// NeedModule adds Module.
+	NeedModule
 )
 
 const (
@@ -182,6 +191,13 @@
 
 // driverResponse contains the results for a driver query.
 type driverResponse struct {
+	// NotHandled is returned if the request can't be handled by the current
+	// driver. If an external driver returns a response with NotHandled, the
+	// rest of the driverResponse is ignored, and go/packages will fallback
+	// to the next driver. If go/packages is extended in the future to support
+	// lists of multiple drivers, go/packages will fall back to the next driver.
+	NotHandled bool
+
 	// Sizes, if not nil, is the types.Sizes to use when type checking.
 	Sizes *types.StdSizes
 
@@ -223,14 +239,22 @@
 	return l.refine(response.Roots, response.Packages...)
 }
 
-// defaultDriver is a driver that looks for an external driver binary, and if
-// it does not find it falls back to the built in go list driver.
+// defaultDriver is a driver that implements go/packages' fallback behavior.
+// It will try to request to an external driver, if one exists. If there's
+// no external driver, or the driver returns a response with NotHandled set,
+// defaultDriver will fall back to the go list driver.
 func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
 	driver := findExternalDriver(cfg)
 	if driver == nil {
 		driver = goListDriver
 	}
-	return driver(cfg, patterns...)
+	response, err := driver(cfg, patterns...)
+	if err != nil {
+		return response, err
+	} else if response.NotHandled {
+		return goListDriver(cfg, patterns...)
+	}
+	return response, nil
 }
 
 // A Package describes a loaded Go package.
@@ -257,7 +281,7 @@
 	GoFiles []string
 
 	// CompiledGoFiles lists the absolute file paths of the package's source
-	// files that were presented to the compiler.
+	// files that are suitable for type checking.
 	// This may differ from GoFiles if files are processed before compilation.
 	CompiledGoFiles []string
 
@@ -305,22 +329,39 @@
 	forTest string
 
 	// module is the module information for the package if it exists.
-	module *packagesinternal.Module
+	Module *Module
+}
+
+// Module provides module information for a package.
+type Module struct {
+	Path      string       // module path
+	Version   string       // module version
+	Replace   *Module      // replaced by this module
+	Time      *time.Time   // time version was created
+	Main      bool         // is this the main module?
+	Indirect  bool         // is this module only an indirect dependency of main module?
+	Dir       string       // directory holding files for this module, if any
+	GoMod     string       // path to go.mod file used when loading this module, if any
+	GoVersion string       // go version used in module
+	Error     *ModuleError // error loading module
+}
+
+// ModuleError holds errors loading a module.
+type ModuleError struct {
+	Err string // the error itself
 }
 
 func init() {
 	packagesinternal.GetForTest = func(p interface{}) string {
 		return p.(*Package).forTest
 	}
-	packagesinternal.GetModule = func(p interface{}) *packagesinternal.Module {
-		return p.(*Package).module
-	}
 	packagesinternal.GetGoCmdRunner = func(config interface{}) *gocommand.Runner {
 		return config.(*Config).gocmdRunner
 	}
 	packagesinternal.SetGoCmdRunner = func(config interface{}, runner *gocommand.Runner) {
 		config.(*Config).gocmdRunner = runner
 	}
+	packagesinternal.TypecheckCgo = int(typecheckCgo)
 }
 
 // An Error describes a problem with a package's metadata, syntax, or types.
@@ -703,6 +744,9 @@
 		if ld.requestedMode&NeedTypesSizes == 0 {
 			ld.pkgs[i].TypesSizes = nil
 		}
+		if ld.requestedMode&NeedModule == 0 {
+			ld.pkgs[i].Module = nil
+		}
 	}
 
 	return result, nil
@@ -878,6 +922,15 @@
 		Error: appendError,
 		Sizes: ld.sizes,
 	}
+	if (ld.Mode & typecheckCgo) != 0 {
+		if !typesinternal.SetUsesCgo(tc) {
+			appendError(Error{
+				Msg:  "typecheckCgo requires Go 1.15+",
+				Kind: ListError,
+			})
+			return
+		}
+	}
 	types.NewChecker(tc, ld.Fset, lpkg.Types, lpkg.TypesInfo).Files(lpkg.Syntax)
 
 	lpkg.importErrors = nil // no longer needed
diff --git a/go/packages/packages110_test.go b/go/packages/packages110_test.go
deleted file mode 100644
index d400568..0000000
--- a/go/packages/packages110_test.go
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build !go1.11
-
-package packages_test
-
-func init() {
-	usesOldGolist = true
-}
diff --git a/go/packages/packages114_test.go b/go/packages/packages114_test.go
deleted file mode 100644
index c85fcd7..0000000
--- a/go/packages/packages114_test.go
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build go1.14
-
-package packages_test
-
-import (
-	"fmt"
-	"path/filepath"
-	"testing"
-
-	"golang.org/x/tools/go/packages"
-	"golang.org/x/tools/go/packages/packagestest"
-)
-
-// These tests check fixes that are only available in Go 1.14.
-// They can be moved into packages_test.go when we no longer support 1.13.
-// See golang/go#35973 for more information.
-func TestInvalidFilesInOverlay(t *testing.T) { packagestest.TestAll(t, testInvalidFilesInOverlay) }
-func testInvalidFilesInOverlay(t *testing.T, exporter packagestest.Exporter) {
-	exported := packagestest.Export(t, exporter, []packagestest.Module{
-		{
-			Name: "golang.org/fake",
-			Files: map[string]interface{}{
-				"d/d.go":      `package d; import "net/http"; const d = http.MethodGet;`,
-				"d/util.go":   ``,
-				"d/d_test.go": ``,
-			},
-		},
-	})
-	defer exported.Cleanup()
-
-	dir := filepath.Dir(filepath.Dir(exported.File("golang.org/fake", "d/d.go")))
-
-	// Additional tests for test variants.
-	for i, tt := range []struct {
-		name    string
-		overlay map[string][]byte
-		want    string // expected value of d.D
-
-	}{
-		// Overlay with a test variant.
-		{"test_variant",
-			map[string][]byte{
-				filepath.Join(dir, "d", "d_test.go"): []byte(`package d; import "testing"; const D = d + "_test"; func TestD(t *testing.T) {};`)},
-			`"GET_test"`},
-		// Overlay in package.
-		{"second_file",
-			map[string][]byte{
-				filepath.Join(dir, "d", "util.go"): []byte(`package d; const D = d + "_util";`)},
-			`"GET_util"`},
-	} {
-		t.Run(tt.name, func(t *testing.T) {
-			exported.Config.Overlay = tt.overlay
-			exported.Config.Mode = packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles |
-				packages.NeedDeps | packages.NeedTypes | packages.NeedTypesSizes
-			exported.Config.Tests = true
-
-			for f := range tt.overlay {
-				initial, err := packages.Load(exported.Config, fmt.Sprintf("file=%s", f))
-				if err != nil {
-					t.Fatal(err)
-				}
-				d := initial[0]
-				var containsFile bool
-				for _, goFile := range d.CompiledGoFiles {
-					if f == goFile {
-						containsFile = true
-						break
-					}
-				}
-				if !containsFile {
-					t.Fatalf("expected %s in CompiledGoFiles, got %v", f, d.CompiledGoFiles)
-				}
-				// Check value of d.D.
-				dD := constant(d, "D")
-				if dD == nil {
-					t.Fatalf("%d. d.D: got nil", i)
-				}
-				got := dD.Val().String()
-				if got != tt.want {
-					t.Fatalf("%d. d.D: got %s, want %s", i, got, tt.want)
-				}
-			}
-		})
-	}
-}
diff --git a/go/packages/packages115_test.go b/go/packages/packages115_test.go
deleted file mode 100644
index 004de7c..0000000
--- a/go/packages/packages115_test.go
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build go1.15
-
-package packages_test
-
-import (
-	"testing"
-
-	"golang.org/x/tools/go/packages"
-	"golang.org/x/tools/go/packages/packagestest"
-)
-
-// TestInvalidFilesInXTest checks the fix for golang/go#37971.
-func TestInvalidFilesInXTest(t *testing.T) {
-	packagestest.TestAll(t, testInvalidFilesInXTest)
-}
-func testInvalidFilesInXTest(t *testing.T, exporter packagestest.Exporter) {
-	exported := packagestest.Export(t, exporter, []packagestest.Module{
-		{
-			Name: "golang.org/fake",
-			Files: map[string]interface{}{
-				"d/d.go":      `package d; import "net/http"; const d = http.MethodGet; func Get() string { return d; }`,
-				"d/d2.go":     ``, // invalid file
-				"d/d_test.go": `package d_test; import "testing"; import "golang.org/fake/d"; func TestD(t *testing.T) { d.Get(); }`,
-			},
-		},
-	})
-	defer exported.Cleanup()
-
-	exported.Config.Mode = packages.NeedName | packages.NeedFiles
-	exported.Config.Tests = true
-
-	initial, err := packages.Load(exported.Config, "golang.org/fake/d")
-	if err != nil {
-		t.Fatal(err)
-	}
-	if len(initial) != 3 {
-		t.Errorf("expected 3 packages, got %d", len(initial))
-	}
-}
diff --git a/go/packages/packages_test.go b/go/packages/packages_test.go
index f3bf68a..8eb87b5 100644
--- a/go/packages/packages_test.go
+++ b/go/packages/packages_test.go
@@ -17,6 +17,7 @@
 	"go/types"
 	"io/ioutil"
 	"os"
+	"os/exec"
 	"path/filepath"
 	"reflect"
 	"runtime"
@@ -1559,6 +1560,7 @@
 			"c/c.go": `package c`,
 		}}})
 	defer exported.Cleanup()
+
 	bOverlayXTestFile := filepath.Join(filepath.Dir(exported.File("golang.org/fake", "b/b.go")), "b_overlay_x_test.go")
 	exported.Config.Mode = packages.NeedName | packages.NeedFiles | packages.NeedImports
 	exported.Config.Overlay = map[string][]byte{bOverlayXTestFile: []byte(`package b_test; import "golang.org/fake/b"`)}
@@ -1570,15 +1572,14 @@
 	graph, _ := importGraph(initial)
 	wantGraph := `
   golang.org/fake/b
-* golang.org/fake/b_test
+* golang.org/fake/b_test [golang.org/fake/b.test]
   golang.org/fake/c
   golang.org/fake/b -> golang.org/fake/c
-  golang.org/fake/b_test -> golang.org/fake/b
+  golang.org/fake/b_test [golang.org/fake/b.test] -> golang.org/fake/b
 `[1:]
 	if graph != wantGraph {
 		t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph)
 	}
-
 }
 
 // This test ensures that the effective GOARCH variable in the
@@ -2175,6 +2176,7 @@
 
 func TestCgoNoCcompiler(t *testing.T) { packagestest.TestAll(t, testCgoNoCcompiler) }
 func testCgoNoCcompiler(t *testing.T, exporter packagestest.Exporter) {
+	testenv.NeedsTool(t, "cgo")
 	exported := packagestest.Export(t, exporter, []packagestest.Module{{
 		Name: "golang.org/fake",
 		Files: map[string]interface{}{
@@ -2208,6 +2210,7 @@
 
 func TestCgoMissingFile(t *testing.T) { packagestest.TestAll(t, testCgoMissingFile) }
 func testCgoMissingFile(t *testing.T, exporter packagestest.Exporter) {
+	testenv.NeedsTool(t, "cgo")
 	exported := packagestest.Export(t, exporter, []packagestest.Module{{
 		Name: "golang.org/fake",
 		Files: map[string]interface{}{
@@ -2242,6 +2245,168 @@
 	}
 }
 
+func TestLoadImportsC(t *testing.T) {
+	// This test checks that when a package depends on the
+	// test variant of "syscall", "unsafe", or "runtime/cgo", that dependency
+	// is not removed when those packages are added when it imports "C".
+	//
+	// For this test to work, the external test of syscall must have a dependency
+	// on net, and net must import "syscall" and "C".
+	if runtime.GOOS == "windows" {
+		t.Skipf("skipping on windows; packages on windows do not satisfy conditions for test.")
+	}
+	if runtime.GOOS == "plan9" {
+		// See https://golang.org/issue/27100.
+		t.Skip(`skipping on plan9; for some reason "net [syscall.test]" is not loaded`)
+	}
+	testenv.NeedsGoPackages(t)
+
+	cfg := &packages.Config{
+		Context: testCtx,
+		Mode:    packages.LoadImports,
+		Tests:   true,
+	}
+	initial, err := packages.Load(cfg, "syscall", "net")
+	if err != nil {
+		t.Fatalf("failed to load imports: %v", err)
+	}
+
+	_, all := importGraph(initial)
+
+	for _, test := range []struct {
+		pattern    string
+		wantImport string // an import to check for
+	}{
+		{"net", "syscall:syscall"},
+		{"net [syscall.test]", "syscall:syscall [syscall.test]"},
+		{"syscall_test [syscall.test]", "net:net [syscall.test]"},
+	} {
+		// Test the import paths.
+		pkg := all[test.pattern]
+		if pkg == nil {
+			t.Errorf("package %q not loaded", test.pattern)
+			continue
+		}
+		if imports := strings.Join(imports(pkg), " "); !strings.Contains(imports, test.wantImport) {
+			t.Errorf("package %q: got \n%s, \nwant to have %s", test.pattern, imports, test.wantImport)
+		}
+	}
+}
+
+func TestCgoNoSyntax(t *testing.T) {
+	packagestest.TestAll(t, testCgoNoSyntax)
+}
+func testCgoNoSyntax(t *testing.T, exporter packagestest.Exporter) {
+	testenv.NeedsTool(t, "cgo")
+
+	exported := packagestest.Export(t, exporter, []packagestest.Module{{
+		Name: "golang.org/fake",
+		Files: map[string]interface{}{
+			"c/c.go": `package c; import "C"`,
+		},
+	}})
+
+	// Explicitly enable cgo.
+	exported.Config.Env = append(exported.Config.Env, "CGO_ENABLED=1")
+
+	modes := []packages.LoadMode{
+		packages.NeedTypes,
+		packages.NeedName | packages.NeedTypes,
+		packages.NeedName | packages.NeedTypes | packages.NeedImports,
+		packages.NeedName | packages.NeedTypes | packages.NeedImports | packages.NeedDeps,
+		packages.NeedName | packages.NeedImports,
+	}
+	for _, mode := range modes {
+		t.Run(fmt.Sprint(mode), func(t *testing.T) {
+			exported.Config.Mode = mode
+			pkgs, err := packages.Load(exported.Config, "golang.org/fake/c")
+			if err != nil {
+				t.Fatal(err)
+			}
+			if len(pkgs) != 1 {
+				t.Fatalf("Expected 1 package, got %v", pkgs)
+			}
+			pkg := pkgs[0]
+			if len(pkg.Errors) != 0 {
+				t.Fatalf("Expected no errors in package, got %v", pkg.Errors)
+			}
+		})
+	}
+}
+
+func TestCgoBadPkgConfig(t *testing.T) {
+	packagestest.TestAll(t, testCgoBadPkgConfig)
+}
+func testCgoBadPkgConfig(t *testing.T, exporter packagestest.Exporter) {
+	testenv.NeedsTool(t, "cgo")
+
+	exported := packagestest.Export(t, exporter, []packagestest.Module{{
+		Name: "golang.org/fake",
+		Files: map[string]interface{}{
+			"c/c.go": `package c
+
+// #cgo pkg-config: --cflags --  foo
+import "C"`,
+		},
+	}})
+
+	dir := buildFakePkgconfig(t, exported.Config.Env)
+	defer os.RemoveAll(dir)
+	env := exported.Config.Env
+	for i, v := range env {
+		if strings.HasPrefix(v, "PATH=") {
+			env[i] = "PATH=" + dir + string(os.PathListSeparator) + v[len("PATH="):]
+		}
+	}
+
+	exported.Config.Env = append(exported.Config.Env, "CGO_ENABLED=1")
+
+	exported.Config.Mode = packages.NeedName | packages.NeedCompiledGoFiles
+	pkgs, err := packages.Load(exported.Config, "golang.org/fake/c")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(pkgs) != 1 {
+		t.Fatalf("Expected 1 package, got %v", pkgs)
+	}
+	if pkgs[0].Name != "c" {
+		t.Fatalf("Expected package to have name \"c\", got %q", pkgs[0].Name)
+	}
+}
+
+func buildFakePkgconfig(t *testing.T, env []string) string {
+	tmpdir, err := ioutil.TempDir("", "fakepkgconfig")
+	if err != nil {
+		t.Fatal(err)
+	}
+	err = ioutil.WriteFile(filepath.Join(tmpdir, "pkg-config.go"), []byte(`
+package main
+
+import "fmt"
+import "os"
+
+func main() {
+	fmt.Fprintln(os.Stderr, "bad")
+	os.Exit(2)
+}
+`), 0644)
+	if err != nil {
+		os.RemoveAll(tmpdir)
+		t.Fatal(err)
+	}
+	cmd := exec.Command("go", "build", "-o", "pkg-config", "pkg-config.go")
+	cmd.Dir = tmpdir
+	cmd.Env = env
+
+	if b, err := cmd.CombinedOutput(); err != nil {
+		os.RemoveAll(tmpdir)
+		fmt.Println(os.Environ())
+		t.Log(string(b))
+		t.Fatal(err)
+	}
+	return tmpdir
+}
+
 func TestIssue32814(t *testing.T) { packagestest.TestAll(t, testIssue32814) }
 func testIssue32814(t *testing.T, exporter packagestest.Exporter) {
 	exported := packagestest.Export(t, exporter, []packagestest.Module{{
@@ -2634,6 +2799,274 @@
 	}
 }
 
+// Tests golang/go#35973, fixed in Go 1.14.
+func TestInvalidFilesInOverlay(t *testing.T) { packagestest.TestAll(t, testInvalidFilesInOverlay) }
+func testInvalidFilesInOverlay(t *testing.T, exporter packagestest.Exporter) {
+	testenv.NeedsGo1Point(t, 14)
+	exported := packagestest.Export(t, exporter, []packagestest.Module{
+		{
+			Name: "golang.org/fake",
+			Files: map[string]interface{}{
+				"d/d.go":      `package d; import "net/http"; const d = http.MethodGet;`,
+				"d/util.go":   ``,
+				"d/d_test.go": ``,
+			},
+		},
+	})
+	defer exported.Cleanup()
+
+	dir := filepath.Dir(filepath.Dir(exported.File("golang.org/fake", "d/d.go")))
+
+	// Additional tests for test variants.
+	for i, tt := range []struct {
+		name    string
+		overlay map[string][]byte
+		want    string // expected value of d.D
+
+	}{
+		// Overlay with a test variant.
+		{"test_variant",
+			map[string][]byte{
+				filepath.Join(dir, "d", "d_test.go"): []byte(`package d; import "testing"; const D = d + "_test"; func TestD(t *testing.T) {};`)},
+			`"GET_test"`},
+		// Overlay in package.
+		{"second_file",
+			map[string][]byte{
+				filepath.Join(dir, "d", "util.go"): []byte(`package d; const D = d + "_util";`)},
+			`"GET_util"`},
+	} {
+		t.Run(tt.name, func(t *testing.T) {
+			exported.Config.Overlay = tt.overlay
+			exported.Config.Mode = packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles |
+				packages.NeedDeps | packages.NeedTypes | packages.NeedTypesSizes
+			exported.Config.Tests = true
+
+			for f := range tt.overlay {
+				initial, err := packages.Load(exported.Config, fmt.Sprintf("file=%s", f))
+				if err != nil {
+					t.Fatal(err)
+				}
+				d := initial[0]
+				var containsFile bool
+				for _, goFile := range d.CompiledGoFiles {
+					if f == goFile {
+						containsFile = true
+						break
+					}
+				}
+				if !containsFile {
+					t.Fatalf("expected %s in CompiledGoFiles, got %v", f, d.CompiledGoFiles)
+				}
+				// Check value of d.D.
+				dD := constant(d, "D")
+				if dD == nil {
+					t.Fatalf("%d. d.D: got nil", i)
+				}
+				got := dD.Val().String()
+				if got != tt.want {
+					t.Fatalf("%d. d.D: got %s, want %s", i, got, tt.want)
+				}
+			}
+		})
+	}
+}
+
+// TestInvalidFilesInXTest checks the fix for golang/go#37971 in Go 1.15.
+func TestInvalidFilesInXTest(t *testing.T) { packagestest.TestAll(t, testInvalidFilesInXTest) }
+func testInvalidFilesInXTest(t *testing.T, exporter packagestest.Exporter) {
+	testenv.NeedsGo1Point(t, 15)
+	exported := packagestest.Export(t, exporter, []packagestest.Module{
+		{
+			Name: "golang.org/fake",
+			Files: map[string]interface{}{
+				"d/d.go":      `package d; import "net/http"; const d = http.MethodGet; func Get() string { return d; }`,
+				"d/d2.go":     ``, // invalid file
+				"d/d_test.go": `package d_test; import "testing"; import "golang.org/fake/d"; func TestD(t *testing.T) { d.Get(); }`,
+			},
+		},
+	})
+	defer exported.Cleanup()
+
+	exported.Config.Mode = packages.NeedName | packages.NeedFiles
+	exported.Config.Tests = true
+
+	initial, err := packages.Load(exported.Config, "golang.org/fake/d")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(initial) != 3 {
+		t.Errorf("expected 3 packages, got %d", len(initial))
+	}
+}
+
+func TestTypecheckCgo(t *testing.T) { packagestest.TestAll(t, testTypecheckCgo) }
+func testTypecheckCgo(t *testing.T, exporter packagestest.Exporter) {
+	testenv.NeedsGo1Point(t, 15)
+	testenv.NeedsTool(t, "cgo")
+
+	const cgo = `package cgo
+		import "C"
+
+		func Example() {
+			C.CString("hi")
+		}
+	`
+	exported := packagestest.Export(t, exporter, []packagestest.Module{
+		{
+			Name: "golang.org/fake",
+			Files: map[string]interface{}{
+				"cgo/cgo.go": cgo,
+			},
+		},
+	})
+	defer exported.Cleanup()
+
+	exported.Config.Mode = packages.NeedFiles | packages.NeedCompiledGoFiles |
+		packages.NeedSyntax | packages.NeedDeps | packages.NeedTypes |
+		packages.LoadMode(packagesinternal.TypecheckCgo)
+
+	initial, err := packages.Load(exported.Config, "golang.org/fake/cgo")
+	if err != nil {
+		t.Fatal(err)
+	}
+	pkg := initial[0]
+	if len(pkg.Errors) != 0 {
+		t.Fatalf("package has errors: %v", pkg.Errors)
+	}
+
+	expos := pkg.Types.Scope().Lookup("Example").Pos()
+	fname := pkg.Fset.File(expos).Name()
+	if !strings.HasSuffix(fname, "cgo.go") {
+		t.Errorf("position for cgo package was loaded from %v, wanted cgo.go", fname)
+	}
+}
+
+func TestModule(t *testing.T) {
+	packagestest.TestAll(t, testModule)
+}
+func testModule(t *testing.T, exporter packagestest.Exporter) {
+	exported := packagestest.Export(t, exporter, []packagestest.Module{{
+		Name:  "golang.org/fake",
+		Files: map[string]interface{}{"a/a.go": `package a`}}})
+	exported.Config.Mode = packages.NeedModule
+	rootDir := filepath.Dir(filepath.Dir(exported.File("golang.org/fake", "a/a.go")))
+
+	initial, err := packages.Load(exported.Config, "golang.org/fake/a")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if len(initial) != 1 {
+		t.Fatal("want exactly one package, got ", initial)
+	}
+	a := initial[0]
+	switch exported.Exporter.Name() {
+	case "GOPATH":
+		if a.Module != nil {
+			t.Fatal("package.Module: want nil, got ", a.Module)
+		}
+	case "Modules":
+		// Make sure Modules field is set, and spot check a few of its fields.
+		if a.Module == nil {
+			t.Fatal("package.Module: want non-nil, got nil")
+		}
+		if a.Module.Path != "golang.org/fake" {
+			t.Fatalf("package.Modile.Path: want \"golang.org/fake\", got %q", a.Module.Path)
+		}
+		if a.Module.GoMod != filepath.Join(rootDir, "go.mod") {
+			t.Fatalf("package.Module.GoMod: want %q, got %q", filepath.Join(rootDir, "go.mod"), a.Module.GoMod)
+		}
+	default:
+		t.Fatalf("Expected exporter to be GOPATH or Modules, got %v", exported.Exporter.Name())
+	}
+}
+
+func TestExternal_NotHandled(t *testing.T) {
+	packagestest.TestAll(t, testExternal_NotHandled)
+}
+func testExternal_NotHandled(t *testing.T, exporter packagestest.Exporter) {
+	testenv.NeedsGoBuild(t)
+
+	tempdir, err := ioutil.TempDir("", "testexternal")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(tempdir)
+
+	exported := packagestest.Export(t, exporter, []packagestest.Module{{
+		Name: "golang.org/fake",
+		Files: map[string]interface{}{
+			"a/a.go": `package a`,
+			"empty_driver/main.go": `package main
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+)
+
+func main() {
+	ioutil.ReadAll(os.Stdin)
+	fmt.Println("{}")
+}
+`,
+			"nothandled_driver/main.go": `package main
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+)
+
+func main() {
+	ioutil.ReadAll(os.Stdin)
+	fmt.Println("{\"NotHandled\": true}")
+}
+`,
+		}}})
+	baseEnv := exported.Config.Env
+
+	// As a control, create a fake driver that always returns an empty response.
+	emptyDriverPath := filepath.Join(tempdir, "empty_driver.exe") // Add .exe because Windows expects it.
+	cmd := exec.Command("go", "build", "-o", emptyDriverPath, "golang.org/fake/empty_driver")
+	cmd.Env = baseEnv
+	cmd.Dir = exported.Config.Dir
+	if b, err := cmd.CombinedOutput(); err != nil {
+		t.Log(string(b))
+		t.Fatal(err)
+	}
+
+	exported.Config.Env = append(append([]string{}, baseEnv...), "GOPACKAGESDRIVER="+emptyDriverPath)
+	initial, err := packages.Load(exported.Config, "golang.org/fake/a")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if len(initial) != 0 {
+		t.Errorf("package.Load with empty driver: want [], got %v", initial)
+	}
+
+	// Create a fake driver that always returns a NotHandled response.
+	notHandledDriverPath := filepath.Join(tempdir, "nothandled_driver.exe")
+	cmd = exec.Command("go", "build", "-o", notHandledDriverPath, "golang.org/fake/nothandled_driver")
+	cmd.Env = baseEnv
+	cmd.Dir = exported.Config.Dir
+	if b, err := cmd.CombinedOutput(); err != nil {
+		t.Log(string(b))
+		t.Fatal(err)
+	}
+
+	exported.Config.Env = append(append([]string{}, baseEnv...), "GOPACKAGESDRIVER="+notHandledDriverPath)
+	initial, err = packages.Load(exported.Config, "golang.org/fake/a")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if len(initial) != 1 || initial[0].PkgPath != "golang.org/fake/a" {
+		t.Errorf("package.Load: want [golang.org/fake/a], got %v", initial)
+	}
+}
+
 func errorMessages(errors []packages.Error) []string {
 	var msgs []string
 	for _, err := range errors {
diff --git a/go/packages/packagescgo_test.go b/go/packages/packagescgo_test.go
deleted file mode 100644
index a149309..0000000
--- a/go/packages/packagescgo_test.go
+++ /dev/null
@@ -1,212 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build cgo
-
-package packages_test
-
-import (
-	"fmt"
-	"io/ioutil"
-	"os"
-	"os/exec"
-	"path/filepath"
-	"runtime"
-	"strings"
-	"testing"
-
-	"golang.org/x/tools/go/packages"
-	"golang.org/x/tools/go/packages/packagestest"
-	"golang.org/x/tools/internal/testenv"
-)
-
-func TestLoadImportsC(t *testing.T) {
-	// This test checks that when a package depends on the
-	// test variant of "syscall", "unsafe", or "runtime/cgo", that dependency
-	// is not removed when those packages are added when it imports "C".
-	//
-	// For this test to work, the external test of syscall must have a dependency
-	// on net, and net must import "syscall" and "C".
-	if runtime.GOOS == "windows" {
-		t.Skipf("skipping on windows; packages on windows do not satisfy conditions for test.")
-	}
-	if runtime.GOOS == "plan9" {
-		// See https://golang.org/issue/27100.
-		t.Skip(`skipping on plan9; for some reason "net [syscall.test]" is not loaded`)
-	}
-	testenv.NeedsGoPackages(t)
-
-	cfg := &packages.Config{
-		Context: testCtx,
-		Mode:    packages.LoadImports,
-		Tests:   true,
-	}
-	initial, err := packages.Load(cfg, "syscall", "net")
-	if err != nil {
-		t.Fatalf("failed to load imports: %v", err)
-	}
-
-	_, all := importGraph(initial)
-
-	for _, test := range []struct {
-		pattern    string
-		wantImport string // an import to check for
-	}{
-		{"net", "syscall:syscall"},
-		{"net [syscall.test]", "syscall:syscall [syscall.test]"},
-		{"syscall_test [syscall.test]", "net:net [syscall.test]"},
-	} {
-		// Test the import paths.
-		pkg := all[test.pattern]
-		if pkg == nil {
-			t.Errorf("package %q not loaded", test.pattern)
-			continue
-		}
-		if imports := strings.Join(imports(pkg), " "); !strings.Contains(imports, test.wantImport) {
-			t.Errorf("package %q: got \n%s, \nwant to have %s", test.pattern, imports, test.wantImport)
-		}
-	}
-}
-
-// Stolen from internal/testenv package in core.
-// hasGoBuild reports whether the current system can build programs with ``go build''
-// and then run them with os.StartProcess or exec.Command.
-func hasGoBuild() bool {
-	if os.Getenv("GO_GCFLAGS") != "" {
-		// It's too much work to require every caller of the go command
-		// to pass along "-gcflags="+os.Getenv("GO_GCFLAGS").
-		// For now, if $GO_GCFLAGS is set, report that we simply can't
-		// run go build.
-		return false
-	}
-	switch runtime.GOOS {
-	case "android", "js":
-		return false
-	case "darwin":
-		if strings.HasPrefix(runtime.GOARCH, "arm") {
-			return false
-		}
-	}
-	return true
-}
-
-func TestCgoNoSyntax(t *testing.T) {
-	packagestest.TestAll(t, testCgoNoSyntax)
-}
-func testCgoNoSyntax(t *testing.T, exporter packagestest.Exporter) {
-	// The android builders have a complex setup which causes this test to fail. See discussion on
-	// golang.org/cl/214943 for more details.
-	if !hasGoBuild() {
-		t.Skip("this test can't run on platforms without go build. See discussion on golang.org/cl/214943 for more details.")
-	}
-
-	exported := packagestest.Export(t, exporter, []packagestest.Module{{
-		Name: "golang.org/fake",
-		Files: map[string]interface{}{
-			"c/c.go": `package c; import "C"`,
-		},
-	}})
-
-	// Explicitly enable cgo.
-	exported.Config.Env = append(exported.Config.Env, "CGO_ENABLED=1")
-
-	modes := []packages.LoadMode{
-		packages.NeedTypes,
-		packages.NeedName | packages.NeedTypes,
-		packages.NeedName | packages.NeedTypes | packages.NeedImports,
-		packages.NeedName | packages.NeedTypes | packages.NeedImports | packages.NeedDeps,
-		packages.NeedName | packages.NeedImports,
-	}
-	for _, mode := range modes {
-		t.Run(fmt.Sprint(mode), func(t *testing.T) {
-			exported.Config.Mode = mode
-			pkgs, err := packages.Load(exported.Config, "golang.org/fake/c")
-			if err != nil {
-				t.Fatal(err)
-			}
-			if len(pkgs) != 1 {
-				t.Fatalf("Expected 1 package, got %v", pkgs)
-			}
-			pkg := pkgs[0]
-			if len(pkg.Errors) != 0 {
-				t.Fatalf("Expected no errors in package, got %v", pkg.Errors)
-			}
-		})
-	}
-}
-
-func TestCgoBadPkgConfig(t *testing.T) {
-	packagestest.TestAll(t, testCgoBadPkgConfig)
-}
-func testCgoBadPkgConfig(t *testing.T, exporter packagestest.Exporter) {
-	if !hasGoBuild() {
-		t.Skip("this test can't run on platforms without go build. See discussion on golang.org/cl/214943 for more details.")
-	}
-
-	exported := packagestest.Export(t, exporter, []packagestest.Module{{
-		Name: "golang.org/fake",
-		Files: map[string]interface{}{
-			"c/c.go": `package c
-
-// #cgo pkg-config: --cflags --  foo
-import "C"`,
-		},
-	}})
-
-	dir := buildFakePkgconfig(t, exported.Config.Env)
-	defer os.RemoveAll(dir)
-	env := exported.Config.Env
-	for i, v := range env {
-		if strings.HasPrefix(v, "PATH=") {
-			env[i] = "PATH=" + dir + string(os.PathListSeparator) + v[len("PATH="):]
-		}
-	}
-
-	exported.Config.Env = append(exported.Config.Env, "CGO_ENABLED=1")
-
-	exported.Config.Mode = packages.NeedName | packages.NeedCompiledGoFiles
-	pkgs, err := packages.Load(exported.Config, "golang.org/fake/c")
-	if err != nil {
-		t.Fatal(err)
-	}
-	if len(pkgs) != 1 {
-		t.Fatalf("Expected 1 package, got %v", pkgs)
-	}
-	if pkgs[0].Name != "c" {
-		t.Fatalf("Expected package to have name \"c\", got %q", pkgs[0].Name)
-	}
-}
-
-func buildFakePkgconfig(t *testing.T, env []string) string {
-	tmpdir, err := ioutil.TempDir("", "fakepkgconfig")
-	if err != nil {
-		t.Fatal(err)
-	}
-	err = ioutil.WriteFile(filepath.Join(tmpdir, "pkg-config.go"), []byte(`
-package main
-
-import "fmt"
-import "os"
-
-func main() {
-	fmt.Fprintln(os.Stderr, "bad")
-	os.Exit(2)
-}
-`), 0644)
-	if err != nil {
-		os.RemoveAll(tmpdir)
-		t.Fatal(err)
-	}
-	cmd := exec.Command("go", "build", "-o", "pkg-config", "pkg-config.go")
-	cmd.Dir = tmpdir
-	cmd.Env = env
-
-	if b, err := cmd.CombinedOutput(); err != nil {
-		os.RemoveAll(tmpdir)
-		fmt.Println(os.Environ())
-		t.Log(string(b))
-		t.Fatal(err)
-	}
-	return tmpdir
-}
diff --git a/go/packages/packagestest/modules_test.go b/go/packages/packagestest/modules_test.go
index 5d13176..6f627b1 100644
--- a/go/packages/packagestest/modules_test.go
+++ b/go/packages/packagestest/modules_test.go
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build go1.11
-
 package packagestest_test
 
 import (
@@ -11,9 +9,11 @@
 	"testing"
 
 	"golang.org/x/tools/go/packages/packagestest"
+	"golang.org/x/tools/internal/testenv"
 )
 
 func TestModulesExport(t *testing.T) {
+	testenv.NeedsGo1Point(t, 11)
 	exported := packagestest.Export(t, packagestest.Modules, testdata)
 	defer exported.Cleanup()
 	// Check that the cfg contains all the right bits
diff --git a/go/ssa/interp/ops.go b/go/ssa/interp/ops.go
index e04e451..90d9452 100644
--- a/go/ssa/interp/ops.go
+++ b/go/ssa/interp/ops.go
@@ -1195,7 +1195,7 @@
 		// TODO(adonovan): fix: test integer -> named alias of string.
 		if ut_src.Info()&types.IsInteger != 0 {
 			if ut_dst, ok := ut_dst.(*types.Basic); ok && ut_dst.Kind() == types.String {
-				return string(asInt(x))
+				return fmt.Sprintf("%c", x)
 			}
 		}
 
diff --git a/godoc/static/godocs.js b/godoc/static/godocs.js
index ca5bbea..7f02ba2 100644
--- a/godoc/static/godocs.js
+++ b/godoc/static/godocs.js
@@ -11,630 +11,678 @@
  */
 
 (function() {
-'use strict';
+  'use strict';
 
-// Mobile-friendly topbar menu
-$(function() {
-  var menu = $('#menu');
-  var menuButton = $('#menu-button');
-  var menuButtonArrow = $('#menu-button-arrow');
-  menuButton.click(function(event) {
-    menu.toggleClass('menu-visible');
-    menuButtonArrow.toggleClass('vertical-flip');
-    event.preventDefault();
-    return false;
-  });
-});
-
-/* Generates a table of contents: looks for h2 and h3 elements and generates
- * links. "Decorates" the element with id=="nav" with this table of contents.
- */
-function generateTOC() {
-  if ($('#manual-nav').length > 0) {
-    return;
-  }
-
-  // For search, we send the toc precomputed from server-side.
-  // TODO: Ideally, this should always be precomputed for all pages, but then
-  // we need to do HTML parsing on the server-side.
-  if (location.pathname === '/search') {
-    return;
-  }
-
-  var nav = $('#nav');
-  if (nav.length === 0) {
-    return;
-  }
-
-  var toc_items = [];
-  $(nav).nextAll('h2, h3').each(function() {
-    var node = this;
-    if (node.id == '')
-      node.id = 'tmp_' + toc_items.length;
-    var link = $('<a/>').attr('href', '#' + node.id).text($(node).text());
-    var item;
-    if ($(node).is('h2')) {
-      item = $('<dt/>');
-    } else { // h3
-      item = $('<dd class="indent"/>');
-    }
-    item.append(link);
-    toc_items.push(item);
-  });
-  if (toc_items.length <= 1) {
-    return;
-  }
-  var dl1 = $('<dl/>');
-  var dl2 = $('<dl/>');
-
-  var split_index = (toc_items.length / 2) + 1;
-  if (split_index < 8) {
-    split_index = toc_items.length;
-  }
-  for (var i = 0; i < split_index; i++) {
-    dl1.append(toc_items[i]);
-  }
-  for (/* keep using i */; i < toc_items.length; i++) {
-    dl2.append(toc_items[i]);
-  }
-
-  var tocTable = $('<table class="unruled"/>').appendTo(nav);
-  var tocBody = $('<tbody/>').appendTo(tocTable);
-  var tocRow = $('<tr/>').appendTo(tocBody);
-
-  // 1st column
-  $('<td class="first"/>').appendTo(tocRow).append(dl1);
-  // 2nd column
-  $('<td/>').appendTo(tocRow).append(dl2);
-}
-
-function bindToggle(el) {
-  $('.toggleButton', el).click(function() {
-    if ($(this).closest(".toggle, .toggleVisible")[0] != el) {
-      // Only trigger the closest toggle header.
-      return;
-    }
-
-    if ($(el).is('.toggle')) {
-      $(el).addClass('toggleVisible').removeClass('toggle');
-    } else {
-      $(el).addClass('toggle').removeClass('toggleVisible');
-    }
-  });
-}
-
-function bindToggles(selector) {
-  $(selector).each(function(i, el) {
-    bindToggle(el);
-  });
-}
-
-function bindToggleLink(el, prefix) {
-  $(el).click(function() {
-    var href = $(el).attr('href');
-    var i = href.indexOf('#'+prefix);
-    if (i < 0) {
-      return;
-    }
-    var id = '#' + prefix + href.slice(i+1+prefix.length);
-    if ($(id).is('.toggle')) {
-      $(id).find('.toggleButton').first().click();
-    }
-  });
-}
-function bindToggleLinks(selector, prefix) {
-  $(selector).each(function(i, el) {
-    bindToggleLink(el, prefix);
-  });
-}
-
-function setupDropdownPlayground() {
-  if (!$('#page').is('.wide')) {
-    return; // don't show on front page
-  }
-  var button = $('#playgroundButton');
-  var div = $('#playground');
-  var setup = false;
-  button.toggle(function() {
-    button.addClass('active');
-    div.show();
-    if (setup) {
-      return;
-    }
-    setup = true;
-    playground({
-      'codeEl': $('.code', div),
-      'outputEl': $('.output', div),
-      'runEl': $('.run', div),
-      'fmtEl': $('.fmt', div),
-      'shareEl': $('.share', div),
-      'shareRedirect': '//play.golang.org/p/'
+  // Mobile-friendly topbar menu
+  $(function() {
+    var menu = $('#menu');
+    var menuButton = $('#menu-button');
+    var menuButtonArrow = $('#menu-button-arrow');
+    menuButton.click(function(event) {
+      menu.toggleClass('menu-visible');
+      menuButtonArrow.toggleClass('vertical-flip');
+      event.preventDefault();
+      return false;
     });
-  },
-  function() {
-    button.removeClass('active');
-    div.hide();
   });
-  $('#menu').css('min-width', '+=60');
 
-  // Hide inline playground if we click somewhere on the page.
-  // This is needed in mobile devices, where the "Play" button
-  // is not clickable once the playground opens up.
-  $("#page").click(function() {
-    if (button.hasClass('active')) {
-      button.click();
+  /* Generates a table of contents: looks for h2 and h3 elements and generates
+   * links. "Decorates" the element with id=="nav" with this table of contents.
+   */
+  function generateTOC() {
+    if ($('#manual-nav').length > 0) {
+      return;
     }
-  });
-}
 
-function setupInlinePlayground() {
-	'use strict';
-	// Set up playground when each element is toggled.
-	$('div.play').each(function (i, el) {
-		// Set up playground for this example.
-		var setup = function() {
-			var code = $('.code', el);
-			playground({
-				'codeEl':   code,
-				'outputEl': $('.output', el),
-				'runEl':    $('.run', el),
-				'fmtEl':    $('.fmt', el),
-				'shareEl':  $('.share', el),
-				'shareRedirect': '//play.golang.org/p/'
-			});
-
-			// Make the code textarea resize to fit content.
-			var resize = function() {
-				code.height(0);
-				var h = code[0].scrollHeight;
-				code.height(h+20); // minimize bouncing.
-				code.closest('.input').height(h);
-			};
-			code.on('keydown', resize);
-			code.on('keyup', resize);
-			code.keyup(); // resize now.
-		};
-
-		// If example already visible, set up playground now.
-		if ($(el).is(':visible')) {
-			setup();
-			return;
-		}
-
-		// Otherwise, set up playground when example is expanded.
-		var built = false;
-		$(el).closest('.toggle').click(function() {
-			// Only set up once.
-			if (!built) {
-				setup();
-				built = true;
-			}
-		});
-	});
-}
-
-// fixFocus tries to put focus to div#page so that keyboard navigation works.
-function fixFocus() {
-  var page = $('div#page');
-  var topbar = $('div#topbar');
-  page.css('outline', 0); // disable outline when focused
-  page.attr('tabindex', -1); // and set tabindex so that it is focusable
-  $(window).resize(function (evt) {
-    // only focus page when the topbar is at fixed position (that is, it's in
-    // front of page, and keyboard event will go to the former by default.)
-    // by focusing page, keyboard event will go to page so that up/down arrow,
-    // space, etc. will work as expected.
-    if (topbar.css('position') == "fixed")
-      page.focus();
-  }).resize();
-}
-
-function toggleHash() {
-  var id = window.location.hash.substring(1);
-  // Open all of the toggles for a particular hash.
-  var els = $(
-    document.getElementById(id),
-    $('a[name]').filter(function() {
-      return $(this).attr('name') == id;
-    })
-  );
-
-  while (els.length) {
-    for (var i = 0; i < els.length; i++) {
-      var el = $(els[i]);
-      if (el.is('.toggle')) {
-        el.find('.toggleButton').first().click();
-      }
+    // For search, we send the toc precomputed from server-side.
+    // TODO: Ideally, this should always be precomputed for all pages, but then
+    // we need to do HTML parsing on the server-side.
+    if (location.pathname === '/search') {
+      return;
     }
-    els = el.parent();
+
+    var nav = $('#nav');
+    if (nav.length === 0) {
+      return;
+    }
+
+    var toc_items = [];
+    $(nav)
+      .nextAll('h2, h3')
+      .each(function() {
+        var node = this;
+        if (node.id == '') node.id = 'tmp_' + toc_items.length;
+        var link = $('<a/>')
+          .attr('href', '#' + node.id)
+          .text($(node).text());
+        var item;
+        if ($(node).is('h2')) {
+          item = $('<dt/>');
+        } else {
+          // h3
+          item = $('<dd class="indent"/>');
+        }
+        item.append(link);
+        toc_items.push(item);
+      });
+    if (toc_items.length <= 1) {
+      return;
+    }
+    var dl1 = $('<dl/>');
+    var dl2 = $('<dl/>');
+
+    var split_index = toc_items.length / 2 + 1;
+    if (split_index < 8) {
+      split_index = toc_items.length;
+    }
+    for (var i = 0; i < split_index; i++) {
+      dl1.append(toc_items[i]);
+    }
+    for (; /* keep using i */ i < toc_items.length; i++) {
+      dl2.append(toc_items[i]);
+    }
+
+    var tocTable = $('<table class="unruled"/>').appendTo(nav);
+    var tocBody = $('<tbody/>').appendTo(tocTable);
+    var tocRow = $('<tr/>').appendTo(tocBody);
+
+    // 1st column
+    $('<td class="first"/>')
+      .appendTo(tocRow)
+      .append(dl1);
+    // 2nd column
+    $('<td/>')
+      .appendTo(tocRow)
+      .append(dl2);
   }
-}
 
-function personalizeInstallInstructions() {
-  var prefix = '?download=';
-  var s = window.location.search;
-  if (s.indexOf(prefix) != 0) {
-    // No 'download' query string; detect "test" instructions from User Agent.
-    if (navigator.platform.indexOf('Win') != -1) {
-      $('.testUnix').hide();
-      $('.testWindows').show();
-    } else {
+  function bindToggle(el) {
+    $('.toggleButton', el).click(function() {
+      if ($(this).closest('.toggle, .toggleVisible')[0] != el) {
+        // Only trigger the closest toggle header.
+        return;
+      }
+
+      if ($(el).is('.toggle')) {
+        $(el)
+          .addClass('toggleVisible')
+          .removeClass('toggle');
+      } else {
+        $(el)
+          .addClass('toggle')
+          .removeClass('toggleVisible');
+      }
+    });
+  }
+
+  function bindToggles(selector) {
+    $(selector).each(function(i, el) {
+      bindToggle(el);
+    });
+  }
+
+  function bindToggleLink(el, prefix) {
+    $(el).click(function() {
+      var href = $(el).attr('href');
+      var i = href.indexOf('#' + prefix);
+      if (i < 0) {
+        return;
+      }
+      var id = '#' + prefix + href.slice(i + 1 + prefix.length);
+      if ($(id).is('.toggle')) {
+        $(id)
+          .find('.toggleButton')
+          .first()
+          .click();
+      }
+    });
+  }
+  function bindToggleLinks(selector, prefix) {
+    $(selector).each(function(i, el) {
+      bindToggleLink(el, prefix);
+    });
+  }
+
+  function setupDropdownPlayground() {
+    if (!$('#page').is('.wide')) {
+      return; // don't show on front page
+    }
+    var button = $('#playgroundButton');
+    var div = $('#playground');
+    var setup = false;
+    button.toggle(
+      function() {
+        button.addClass('active');
+        div.show();
+        if (setup) {
+          return;
+        }
+        setup = true;
+        playground({
+          codeEl: $('.code', div),
+          outputEl: $('.output', div),
+          runEl: $('.run', div),
+          fmtEl: $('.fmt', div),
+          shareEl: $('.share', div),
+          shareRedirect: '//play.golang.org/p/',
+        });
+      },
+      function() {
+        button.removeClass('active');
+        div.hide();
+      }
+    );
+    $('#menu').css('min-width', '+=60');
+
+    // Hide inline playground if we click somewhere on the page.
+    // This is needed in mobile devices, where the "Play" button
+    // is not clickable once the playground opens up.
+    $('#page').click(function() {
+      if (button.hasClass('active')) {
+        button.click();
+      }
+    });
+  }
+
+  function setupInlinePlayground() {
+    'use strict';
+    // Set up playground when each element is toggled.
+    $('div.play').each(function(i, el) {
+      // Set up playground for this example.
+      var setup = function() {
+        var code = $('.code', el);
+        playground({
+          codeEl: code,
+          outputEl: $('.output', el),
+          runEl: $('.run', el),
+          fmtEl: $('.fmt', el),
+          shareEl: $('.share', el),
+          shareRedirect: '//play.golang.org/p/',
+        });
+
+        // Make the code textarea resize to fit content.
+        var resize = function() {
+          code.height(0);
+          var h = code[0].scrollHeight;
+          code.height(h + 20); // minimize bouncing.
+          code.closest('.input').height(h);
+        };
+        code.on('keydown', resize);
+        code.on('keyup', resize);
+        code.keyup(); // resize now.
+      };
+
+      // If example already visible, set up playground now.
+      if ($(el).is(':visible')) {
+        setup();
+        return;
+      }
+
+      // Otherwise, set up playground when example is expanded.
+      var built = false;
+      $(el)
+        .closest('.toggle')
+        .click(function() {
+          // Only set up once.
+          if (!built) {
+            setup();
+            built = true;
+          }
+        });
+    });
+  }
+
+  // fixFocus tries to put focus to div#page so that keyboard navigation works.
+  function fixFocus() {
+    var page = $('div#page');
+    var topbar = $('div#topbar');
+    page.css('outline', 0); // disable outline when focused
+    page.attr('tabindex', -1); // and set tabindex so that it is focusable
+    $(window)
+      .resize(function(evt) {
+        // only focus page when the topbar is at fixed position (that is, it's in
+        // front of page, and keyboard event will go to the former by default.)
+        // by focusing page, keyboard event will go to page so that up/down arrow,
+        // space, etc. will work as expected.
+        if (topbar.css('position') == 'fixed') page.focus();
+      })
+      .resize();
+  }
+
+  function toggleHash() {
+    var id = window.location.hash.substring(1);
+    // Open all of the toggles for a particular hash.
+    var els = $(
+      document.getElementById(id),
+      $('a[name]').filter(function() {
+        return $(this).attr('name') == id;
+      })
+    );
+
+    while (els.length) {
+      for (var i = 0; i < els.length; i++) {
+        var el = $(els[i]);
+        if (el.is('.toggle')) {
+          el.find('.toggleButton')
+            .first()
+            .click();
+        }
+      }
+      els = el.parent();
+    }
+  }
+
+  function personalizeInstallInstructions() {
+    var prefix = '?download=';
+    var s = window.location.search;
+    if (s.indexOf(prefix) != 0) {
+      // No 'download' query string; detect "test" instructions from User Agent.
+      if (navigator.platform.indexOf('Win') != -1) {
+        $('.testUnix').hide();
+        $('.testWindows').show();
+      } else {
+        $('.testUnix').show();
+        $('.testWindows').hide();
+      }
+      return;
+    }
+
+    var filename = s.substr(prefix.length);
+    var filenameRE = /^go1\.\d+(\.\d+)?([a-z0-9]+)?\.([a-z0-9]+)(-[a-z0-9]+)?(-osx10\.[68])?\.([a-z.]+)$/;
+    var m = filenameRE.exec(filename);
+    if (!m) {
+      // Can't interpret file name; bail.
+      return;
+    }
+    $('.downloadFilename').text(filename);
+    $('.hideFromDownload').hide();
+
+    var os = m[3];
+    var ext = m[6];
+    if (ext != 'tar.gz') {
+      $('#tarballInstructions').hide();
+    }
+    if (os != 'darwin' || ext != 'pkg') {
+      $('#darwinPackageInstructions').hide();
+    }
+    if (os != 'windows') {
+      $('#windowsInstructions').hide();
       $('.testUnix').show();
       $('.testWindows').hide();
+    } else {
+      if (ext != 'msi') {
+        $('#windowsInstallerInstructions').hide();
+      }
+      if (ext != 'zip') {
+        $('#windowsZipInstructions').hide();
+      }
+      $('.testUnix').hide();
+      $('.testWindows').show();
     }
-    return;
+
+    var download = 'https://dl.google.com/go/' + filename;
+
+    var message = $(
+      '<p class="downloading">' +
+        'Your download should begin shortly. ' +
+        'If it does not, click <a>this link</a>.</p>'
+    );
+    message.find('a').attr('href', download);
+    message.insertAfter('#nav');
+
+    window.location = download;
   }
 
-  var filename = s.substr(prefix.length);
-  var filenameRE = /^go1\.\d+(\.\d+)?([a-z0-9]+)?\.([a-z0-9]+)(-[a-z0-9]+)?(-osx10\.[68])?\.([a-z.]+)$/;
-  var m = filenameRE.exec(filename);
-  if (!m) {
-    // Can't interpret file name; bail.
-    return;
-  }
-  $('.downloadFilename').text(filename);
-  $('.hideFromDownload').hide();
-
-  var os = m[3];
-  var ext = m[6];
-  if (ext != 'tar.gz') {
-    $('#tarballInstructions').hide();
-  }
-  if (os != 'darwin' || ext != 'pkg') {
-    $('#darwinPackageInstructions').hide();
-  }
-  if (os != 'windows') {
-    $('#windowsInstructions').hide();
-    $('.testUnix').show();
-    $('.testWindows').hide();
-  } else {
-    if (ext != 'msi') {
-      $('#windowsInstallerInstructions').hide();
+  function updateVersionTags() {
+    var v = window.goVersion;
+    if (/^go[0-9.]+$/.test(v)) {
+      $('.versionTag')
+        .empty()
+        .text(v);
+      $('.whereTag').hide();
     }
-    if (ext != 'zip') {
-      $('#windowsZipInstructions').hide();
+  }
+
+  function addPermalinks() {
+    function addPermalink(source, parent) {
+      var id = source.attr('id');
+      if (id == '' || id.indexOf('tmp_') === 0) {
+        // Auto-generated permalink.
+        return;
+      }
+      if (parent.find('> .permalink').length) {
+        // Already attached.
+        return;
+      }
+      parent
+        .append(' ')
+        .append($("<a class='permalink'>&#xb6;</a>").attr('href', '#' + id));
     }
-    $('.testUnix').hide();
-    $('.testWindows').show();
+
+    $('#page .container')
+      .find('h2[id], h3[id]')
+      .each(function() {
+        var el = $(this);
+        addPermalink(el, el);
+      });
+
+    $('#page .container')
+      .find('dl[id]')
+      .each(function() {
+        var el = $(this);
+        // Add the anchor to the "dt" element.
+        addPermalink(el, el.find('> dt').first());
+      });
   }
 
-  var download = "https://dl.google.com/go/" + filename;
+  $('.js-expandAll').click(function() {
+    if ($(this).hasClass('collapsed')) {
+      toggleExamples('toggle');
+      $(this).text('(Collapse All)');
+    } else {
+      toggleExamples('toggleVisible');
+      $(this).text('(Expand All)');
+    }
+    $(this).toggleClass('collapsed');
+  });
 
-  var message = $('<p class="downloading">'+
-    'Your download should begin shortly. '+
-    'If it does not, click <a>this link</a>.</p>');
-  message.find('a').attr('href', download);
-  message.insertAfter('#nav');
-
-  window.location = download;
-}
-
-function updateVersionTags() {
-  var v = window.goVersion;
-  if (/^go[0-9.]+$/.test(v)) {
-    $(".versionTag").empty().text(v);
-    $(".whereTag").hide();
+  function toggleExamples(className) {
+    // We need to explicitly iterate through divs starting with "example_"
+    // to avoid toggling Overview and Index collapsibles.
+    $("[id^='example_']").each(function() {
+      // Check for state and click it only if required.
+      if ($(this).hasClass(className)) {
+        $(this)
+          .find('.toggleButton')
+          .first()
+          .click();
+      }
+    });
   }
-}
 
-function addPermalinks() {
-  function addPermalink(source, parent) {
-    var id = source.attr("id");
-    if (id == "" || id.indexOf("tmp_") === 0) {
-      // Auto-generated permalink.
+  $(document).ready(function() {
+    generateTOC();
+    addPermalinks();
+    bindToggles('.toggle');
+    bindToggles('.toggleVisible');
+    bindToggleLinks('.exampleLink', 'example_');
+    bindToggleLinks('.overviewLink', '');
+    bindToggleLinks('.examplesLink', '');
+    bindToggleLinks('.indexLink', '');
+    setupDropdownPlayground();
+    setupInlinePlayground();
+    fixFocus();
+    setupTypeInfo();
+    setupCallgraphs();
+    toggleHash();
+    personalizeInstallInstructions();
+    updateVersionTags();
+
+    // godoc.html defines window.initFuncs in the <head> tag, and root.html and
+    // codewalk.js push their on-page-ready functions to the list.
+    // We execute those functions here, to avoid loading jQuery until the page
+    // content is loaded.
+    for (var i = 0; i < window.initFuncs.length; i++) window.initFuncs[i]();
+  });
+
+  // -- analysis ---------------------------------------------------------
+
+  // escapeHTML returns HTML for s, with metacharacters quoted.
+  // It is safe for use in both elements and attributes
+  // (unlike the "set innerText, read innerHTML" trick).
+  function escapeHTML(s) {
+    return s
+      .replace(/&/g, '&amp;')
+      .replace(/\"/g, '&quot;')
+      .replace(/\'/g, '&#39;')
+      .replace(/</g, '&lt;')
+      .replace(/>/g, '&gt;');
+  }
+
+  // makeAnchor returns HTML for an <a> element, given an anchorJSON object.
+  function makeAnchor(json) {
+    var html = escapeHTML(json.Text);
+    if (json.Href != '') {
+      html = "<a href='" + escapeHTML(json.Href) + "'>" + html + '</a>';
+    }
+    return html;
+  }
+
+  function showLowFrame(html) {
+    var lowframe = document.getElementById('lowframe');
+    lowframe.style.height = '200px';
+    lowframe.innerHTML =
+      "<p style='text-align: left;'>" +
+      html +
+      '</p>\n' +
+      "<div onclick='hideLowFrame()' style='position: absolute; top: 0; right: 0; cursor: pointer;'>✘</div>";
+  }
+
+  document.hideLowFrame = function() {
+    var lowframe = document.getElementById('lowframe');
+    lowframe.style.height = '0px';
+  };
+
+  // onClickCallers is the onclick action for the 'func' tokens of a
+  // function declaration.
+  document.onClickCallers = function(index) {
+    var data = document.ANALYSIS_DATA[index];
+    if (data.Callers.length == 1 && data.Callers[0].Sites.length == 1) {
+      document.location = data.Callers[0].Sites[0].Href; // jump to sole caller
       return;
     }
-    if (parent.find("> .permalink").length) {
-      // Already attached.
+
+    var html =
+      'Callers of <code>' + escapeHTML(data.Callee) + '</code>:<br/>\n';
+    for (var i = 0; i < data.Callers.length; i++) {
+      var caller = data.Callers[i];
+      html += '<code>' + escapeHTML(caller.Func) + '</code>';
+      var sites = caller.Sites;
+      if (sites != null && sites.length > 0) {
+        html += ' at line ';
+        for (var j = 0; j < sites.length; j++) {
+          if (j > 0) {
+            html += ', ';
+          }
+          html += '<code>' + makeAnchor(sites[j]) + '</code>';
+        }
+      }
+      html += '<br/>\n';
+    }
+    showLowFrame(html);
+  };
+
+  // onClickCallees is the onclick action for the '(' token of a function call.
+  document.onClickCallees = function(index) {
+    var data = document.ANALYSIS_DATA[index];
+    if (data.Callees.length == 1) {
+      document.location = data.Callees[0].Href; // jump to sole callee
       return;
     }
-    parent.append(" ").append($("<a class='permalink'>&#xb6;</a>").attr("href", "#" + id));
-  }
 
-  $("#page .container").find("h2[id], h3[id]").each(function() {
-    var el = $(this);
-    addPermalink(el, el);
-  });
-
-  $("#page .container").find("dl[id]").each(function() {
-    var el = $(this);
-    // Add the anchor to the "dt" element.
-    addPermalink(el, el.find("> dt").first());
-  });
-}
-
-$(".js-expandAll").click(function() {
-  if ($(this).hasClass("collapsed")) {
-    toggleExamples('toggle');
-    $(this).text("(Collapse All)");
-  } else {
-    toggleExamples('toggleVisible');
-    $(this).text("(Expand All)");
-  }
-  $(this).toggleClass("collapsed")
-});
-
-function toggleExamples(className) {
-  // We need to explicitly iterate through divs starting with "example_"
-  // to avoid toggling Overview and Index collapsibles.
-  $("[id^='example_']").each(function() {
-    // Check for state and click it only if required.
-    if ($(this).hasClass(className)) {
-      $(this).find('.toggleButton').first().click();
+    var html = 'Callees of this ' + escapeHTML(data.Descr) + ':<br/>\n';
+    for (var i = 0; i < data.Callees.length; i++) {
+      html += '<code>' + makeAnchor(data.Callees[i]) + '</code><br/>\n';
     }
-  });
-}
+    showLowFrame(html);
+  };
 
-$(document).ready(function() {
-  generateTOC();
-  addPermalinks();
-  bindToggles(".toggle");
-  bindToggles(".toggleVisible");
-  bindToggleLinks(".exampleLink", "example_");
-  bindToggleLinks(".overviewLink", "");
-  bindToggleLinks(".examplesLink", "");
-  bindToggleLinks(".indexLink", "");
-  setupDropdownPlayground();
-  setupInlinePlayground();
-  fixFocus();
-  setupTypeInfo();
-  setupCallgraphs();
-  toggleHash();
-  personalizeInstallInstructions();
-  updateVersionTags();
+  // onClickTypeInfo is the onclick action for identifiers declaring a named type.
+  document.onClickTypeInfo = function(index) {
+    var data = document.ANALYSIS_DATA[index];
+    var html =
+      'Type <code>' +
+      data.Name +
+      '</code>: ' +
+      '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<small>(size=' +
+      data.Size +
+      ', align=' +
+      data.Align +
+      ')</small><br/>\n';
+    html += implementsHTML(data);
+    html += methodsetHTML(data);
+    showLowFrame(html);
+  };
 
-  // godoc.html defines window.initFuncs in the <head> tag, and root.html and
-  // codewalk.js push their on-page-ready functions to the list.
-  // We execute those functions here, to avoid loading jQuery until the page
-  // content is loaded.
-  for (var i = 0; i < window.initFuncs.length; i++) window.initFuncs[i]();
-});
-
-// -- analysis ---------------------------------------------------------
-
-// escapeHTML returns HTML for s, with metacharacters quoted.
-// It is safe for use in both elements and attributes
-// (unlike the "set innerText, read innerHTML" trick).
-function escapeHTML(s) {
-    return s.replace(/&/g, '&amp;').
-             replace(/\"/g, '&quot;').
-             replace(/\'/g, '&#39;').
-             replace(/</g, '&lt;').
-             replace(/>/g, '&gt;');
-}
-
-// makeAnchor returns HTML for an <a> element, given an anchorJSON object.
-function makeAnchor(json) {
-  var html = escapeHTML(json.Text);
-  if (json.Href != "") {
-      html = "<a href='" + escapeHTML(json.Href) + "'>" + html + "</a>";
-  }
-  return html;
-}
-
-function showLowFrame(html) {
-  var lowframe = document.getElementById('lowframe');
-  lowframe.style.height = "200px";
-  lowframe.innerHTML = "<p style='text-align: left;'>" + html + "</p>\n" +
-      "<div onclick='hideLowFrame()' style='position: absolute; top: 0; right: 0; cursor: pointer;'>✘</div>"
-};
-
-document.hideLowFrame = function() {
-  var lowframe = document.getElementById('lowframe');
-  lowframe.style.height = "0px";
-}
-
-// onClickCallers is the onclick action for the 'func' tokens of a
-// function declaration.
-document.onClickCallers = function(index) {
-  var data = document.ANALYSIS_DATA[index]
-  if (data.Callers.length == 1 && data.Callers[0].Sites.length == 1) {
-    document.location = data.Callers[0].Sites[0].Href; // jump to sole caller
-    return;
-  }
-
-  var html = "Callers of <code>" + escapeHTML(data.Callee) + "</code>:<br/>\n";
-  for (var i = 0; i < data.Callers.length; i++) {
-    var caller = data.Callers[i];
-    html += "<code>" + escapeHTML(caller.Func) + "</code>";
-    var sites = caller.Sites;
-    if (sites != null && sites.length > 0) {
-      html += " at line ";
-      for (var j = 0; j < sites.length; j++) {
-        if (j > 0) {
-          html += ", ";
+  // implementsHTML returns HTML for the implements relation of the
+  // specified TypeInfoJSON value.
+  function implementsHTML(info) {
+    var html = '';
+    if (info.ImplGroups != null) {
+      for (var i = 0; i < info.ImplGroups.length; i++) {
+        var group = info.ImplGroups[i];
+        var x = '<code>' + escapeHTML(group.Descr) + '</code> ';
+        for (var j = 0; j < group.Facts.length; j++) {
+          var fact = group.Facts[j];
+          var y = '<code>' + makeAnchor(fact.Other) + '</code>';
+          if (fact.ByKind != null) {
+            html += escapeHTML(fact.ByKind) + ' type ' + y + ' implements ' + x;
+          } else {
+            html += x + ' implements ' + y;
+          }
+          html += '<br/>\n';
         }
-        html += "<code>" + makeAnchor(sites[j]) + "</code>";
       }
     }
-    html += "<br/>\n";
-  }
-  showLowFrame(html);
-};
-
-// onClickCallees is the onclick action for the '(' token of a function call.
-document.onClickCallees = function(index) {
-  var data = document.ANALYSIS_DATA[index]
-  if (data.Callees.length == 1) {
-    document.location = data.Callees[0].Href; // jump to sole callee
-    return;
+    return html;
   }
 
-  var html = "Callees of this " + escapeHTML(data.Descr) + ":<br/>\n";
-  for (var i = 0; i < data.Callees.length; i++) {
-    html += "<code>" + makeAnchor(data.Callees[i]) + "</code><br/>\n";
-  }
-  showLowFrame(html);
-};
-
-// onClickTypeInfo is the onclick action for identifiers declaring a named type.
-document.onClickTypeInfo = function(index) {
-  var data = document.ANALYSIS_DATA[index];
-  var html = "Type <code>" + data.Name + "</code>: " +
-  "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<small>(size=" + data.Size + ", align=" + data.Align + ")</small><br/>\n";
-  html += implementsHTML(data);
-  html += methodsetHTML(data);
-  showLowFrame(html);
-};
-
-// implementsHTML returns HTML for the implements relation of the
-// specified TypeInfoJSON value.
-function implementsHTML(info) {
-  var html = "";
-  if (info.ImplGroups != null) {
-    for (var i = 0; i < info.ImplGroups.length; i++) {
-      var group = info.ImplGroups[i];
-      var x = "<code>" + escapeHTML(group.Descr) + "</code> ";
-      for (var j = 0; j < group.Facts.length; j++) {
-        var fact = group.Facts[j];
-        var y = "<code>" + makeAnchor(fact.Other) + "</code>";
-        if (fact.ByKind != null) {
-          html += escapeHTML(fact.ByKind) + " type " + y + " implements " + x;
-        } else {
-          html += x + " implements " + y;
-        }
-        html += "<br/>\n";
+  // methodsetHTML returns HTML for the methodset of the specified
+  // TypeInfoJSON value.
+  function methodsetHTML(info) {
+    var html = '';
+    if (info.Methods != null) {
+      for (var i = 0; i < info.Methods.length; i++) {
+        html += '<code>' + makeAnchor(info.Methods[i]) + '</code><br/>\n';
       }
     }
+    return html;
   }
-  return html;
-}
 
-
-// methodsetHTML returns HTML for the methodset of the specified
-// TypeInfoJSON value.
-function methodsetHTML(info) {
-  var html = "";
-  if (info.Methods != null) {
-    for (var i = 0; i < info.Methods.length; i++) {
-      html += "<code>" + makeAnchor(info.Methods[i]) + "</code><br/>\n";
+  // onClickComm is the onclick action for channel "make" and "<-"
+  // send/receive tokens.
+  document.onClickComm = function(index) {
+    var ops = document.ANALYSIS_DATA[index].Ops;
+    if (ops.length == 1) {
+      document.location = ops[0].Op.Href; // jump to sole element
+      return;
     }
-  }
-  return html;
-}
 
-// onClickComm is the onclick action for channel "make" and "<-"
-// send/receive tokens.
-document.onClickComm = function(index) {
-  var ops = document.ANALYSIS_DATA[index].Ops
-  if (ops.length == 1) {
-    document.location = ops[0].Op.Href; // jump to sole element
-    return;
-  }
+    var html = 'Operations on this channel:<br/>\n';
+    for (var i = 0; i < ops.length; i++) {
+      html +=
+        makeAnchor(ops[i].Op) +
+        ' by <code>' +
+        escapeHTML(ops[i].Fn) +
+        '</code><br/>\n';
+    }
+    if (ops.length == 0) {
+      html += '(none)<br/>\n';
+    }
+    showLowFrame(html);
+  };
 
-  var html = "Operations on this channel:<br/>\n";
-  for (var i = 0; i < ops.length; i++) {
-    html += makeAnchor(ops[i].Op) + " by <code>" + escapeHTML(ops[i].Fn) + "</code><br/>\n";
-  }
-  if (ops.length == 0) {
-    html += "(none)<br/>\n";
-  }
-  showLowFrame(html);
-};
-
-$(window).load(function() {
+  $(window).load(function() {
     // Scroll window so that first selection is visible.
     // (This means we don't need to emit id='L%d' spans for each line.)
     // TODO(adonovan): ideally, scroll it so that it's under the pointer,
     // but I don't know how to get the pointer y coordinate.
-    var elts = document.getElementsByClassName("selection");
+    var elts = document.getElementsByClassName('selection');
     if (elts.length > 0) {
-	elts[0].scrollIntoView()
+      elts[0].scrollIntoView();
     }
-});
+  });
 
-// setupTypeInfo populates the "Implements" and "Method set" toggle for
-// each type in the package doc.
-function setupTypeInfo() {
-  for (var i in document.ANALYSIS_DATA) {
-    var data = document.ANALYSIS_DATA[i];
+  // setupTypeInfo populates the "Implements" and "Method set" toggle for
+  // each type in the package doc.
+  function setupTypeInfo() {
+    for (var i in document.ANALYSIS_DATA) {
+      var data = document.ANALYSIS_DATA[i];
 
-    var el = document.getElementById("implements-" + i);
-    if (el != null) {
-      // el != null => data is TypeInfoJSON.
-      if (data.ImplGroups != null) {
-        el.innerHTML = implementsHTML(data);
-        el.parentNode.parentNode.style.display = "block";
+      var el = document.getElementById('implements-' + i);
+      if (el != null) {
+        // el != null => data is TypeInfoJSON.
+        if (data.ImplGroups != null) {
+          el.innerHTML = implementsHTML(data);
+          el.parentNode.parentNode.style.display = 'block';
+        }
       }
-    }
 
-    var el = document.getElementById("methodset-" + i);
-    if (el != null) {
-      // el != null => data is TypeInfoJSON.
-      if (data.Methods != null) {
-        el.innerHTML = methodsetHTML(data);
-        el.parentNode.parentNode.style.display = "block";
+      var el = document.getElementById('methodset-' + i);
+      if (el != null) {
+        // el != null => data is TypeInfoJSON.
+        if (data.Methods != null) {
+          el.innerHTML = methodsetHTML(data);
+          el.parentNode.parentNode.style.display = 'block';
+        }
       }
     }
   }
-}
 
-function setupCallgraphs() {
-  if (document.CALLGRAPH == null) {
-    return
-  }
-  document.getElementById("pkg-callgraph").style.display = "block";
-
-  var treeviews = document.getElementsByClassName("treeview");
-  for (var i = 0; i < treeviews.length; i++) {
-    var tree = treeviews[i];
-    if (tree.id == null || tree.id.indexOf("callgraph-") != 0) {
-      continue;
+  function setupCallgraphs() {
+    if (document.CALLGRAPH == null) {
+      return;
     }
-    var id = tree.id.substring("callgraph-".length);
-    $(tree).treeview({collapsed: true, animated: "fast"});
-    document.cgAddChildren(tree, tree, [id]);
-    tree.parentNode.parentNode.style.display = "block";
-  }
-}
+    document.getElementById('pkg-callgraph').style.display = 'block';
 
-document.cgAddChildren = function(tree, ul, indices) {
-  if (indices != null) {
-    for (var i = 0; i < indices.length; i++) {
-      var li = cgAddChild(tree, ul, document.CALLGRAPH[indices[i]]);
-      if (i == indices.length - 1) {
-        $(li).addClass("last");
+    var treeviews = document.getElementsByClassName('treeview');
+    for (var i = 0; i < treeviews.length; i++) {
+      var tree = treeviews[i];
+      if (tree.id == null || tree.id.indexOf('callgraph-') != 0) {
+        continue;
+      }
+      var id = tree.id.substring('callgraph-'.length);
+      $(tree).treeview({ collapsed: true, animated: 'fast' });
+      document.cgAddChildren(tree, tree, [id]);
+      tree.parentNode.parentNode.style.display = 'block';
+    }
+  }
+
+  document.cgAddChildren = function(tree, ul, indices) {
+    if (indices != null) {
+      for (var i = 0; i < indices.length; i++) {
+        var li = cgAddChild(tree, ul, document.CALLGRAPH[indices[i]]);
+        if (i == indices.length - 1) {
+          $(li).addClass('last');
+        }
       }
     }
+    $(tree).treeview({ animated: 'fast', add: ul });
+  };
+
+  // cgAddChild adds an <li> element for document.CALLGRAPH node cgn to
+  // the parent <ul> element ul. tree is the tree's root <ul> element.
+  function cgAddChild(tree, ul, cgn) {
+    var li = document.createElement('li');
+    ul.appendChild(li);
+    li.className = 'closed';
+
+    var code = document.createElement('code');
+
+    if (cgn.Callees != null) {
+      $(li).addClass('expandable');
+
+      // Event handlers and innerHTML updates don't play nicely together,
+      // hence all this explicit DOM manipulation.
+      var hitarea = document.createElement('div');
+      hitarea.className = 'hitarea expandable-hitarea';
+      li.appendChild(hitarea);
+
+      li.appendChild(code);
+
+      var childUL = document.createElement('ul');
+      li.appendChild(childUL);
+      childUL.setAttribute('style', 'display: none;');
+
+      var onClick = function() {
+        document.cgAddChildren(tree, childUL, cgn.Callees);
+        hitarea.removeEventListener('click', onClick);
+      };
+      hitarea.addEventListener('click', onClick);
+    } else {
+      li.appendChild(code);
+    }
+    code.innerHTML += '&nbsp;' + makeAnchor(cgn.Func);
+    return li;
   }
-  $(tree).treeview({animated: "fast", add: ul});
-}
-
-// cgAddChild adds an <li> element for document.CALLGRAPH node cgn to
-// the parent <ul> element ul. tree is the tree's root <ul> element.
-function cgAddChild(tree, ul, cgn) {
-   var li = document.createElement("li");
-   ul.appendChild(li);
-   li.className = "closed";
-
-   var code = document.createElement("code");
-
-   if (cgn.Callees != null) {
-     $(li).addClass("expandable");
-
-     // Event handlers and innerHTML updates don't play nicely together,
-     // hence all this explicit DOM manipulation.
-     var hitarea = document.createElement("div");
-     hitarea.className = "hitarea expandable-hitarea";
-     li.appendChild(hitarea);
-
-     li.appendChild(code);
-
-     var childUL = document.createElement("ul");
-     li.appendChild(childUL);
-     childUL.setAttribute('style', "display: none;");
-
-     var onClick = function() {
-       document.cgAddChildren(tree, childUL, cgn.Callees);
-       hitarea.removeEventListener('click', onClick)
-     };
-     hitarea.addEventListener('click', onClick);
-
-   } else {
-     li.appendChild(code);
-   }
-   code.innerHTML += "&nbsp;" + makeAnchor(cgn.Func);
-   return li
-}
-
 })();
diff --git a/godoc/static/play.js b/godoc/static/play.js
index 6adbd6c..cde6d53 100644
--- a/godoc/static/play.js
+++ b/godoc/static/play.js
@@ -3,112 +3,112 @@
 // license that can be found in the LICENSE file.
 
 function initPlayground(transport) {
-	'use strict';
+  'use strict';
 
-	function text(node) {
-		var s = '';
-		for (var i = 0; i < node.childNodes.length; i++) {
-			var n = node.childNodes[i];
-			if (n.nodeType === 1) {
-				if (n.tagName === 'BUTTON') continue
-				if (n.tagName === 'SPAN' && n.className === 'number') continue;
-				if (n.tagName === 'DIV' || n.tagName == 'BR') {
-					s += "\n";
-				}
-				s += text(n);
-				continue;
-			}
-			if (n.nodeType === 3) {
-				s += n.nodeValue;
-			}
-		}
-		return s.replace('\xA0', ' '); // replace non-breaking spaces
-	}
+  function text(node) {
+    var s = '';
+    for (var i = 0; i < node.childNodes.length; i++) {
+      var n = node.childNodes[i];
+      if (n.nodeType === 1) {
+        if (n.tagName === 'BUTTON') continue;
+        if (n.tagName === 'SPAN' && n.className === 'number') continue;
+        if (n.tagName === 'DIV' || n.tagName == 'BR') {
+          s += '\n';
+        }
+        s += text(n);
+        continue;
+      }
+      if (n.nodeType === 3) {
+        s += n.nodeValue;
+      }
+    }
+    return s.replace('\xA0', ' '); // replace non-breaking spaces
+  }
 
-	// When presenter notes are enabled, the index passed
-	// here will identify the playground to be synced
-	function init(code, index) {
-		var output = document.createElement('div');
-		var outpre = document.createElement('pre');
-		var running;
+  // When presenter notes are enabled, the index passed
+  // here will identify the playground to be synced
+  function init(code, index) {
+    var output = document.createElement('div');
+    var outpre = document.createElement('pre');
+    var running;
 
-		if ($ && $(output).resizable) {
-			$(output).resizable({
-				handles: 	'n,w,nw',
-				minHeight:	27,
-				minWidth:	135,
-				maxHeight:	608,
-				maxWidth:	990
-			});
-		}
+    if ($ && $(output).resizable) {
+      $(output).resizable({
+        handles: 'n,w,nw',
+        minHeight: 27,
+        minWidth: 135,
+        maxHeight: 608,
+        maxWidth: 990,
+      });
+    }
 
-		function onKill() {
-			if (running) running.Kill();
-			if (window.notesEnabled) updatePlayStorage('onKill', index);
-		}
+    function onKill() {
+      if (running) running.Kill();
+      if (window.notesEnabled) updatePlayStorage('onKill', index);
+    }
 
-		function onRun(e) {
-			var sk = e.shiftKey || localStorage.getItem('play-shiftKey') === 'true';
-			if (running) running.Kill();
-			output.style.display = 'block';
-			outpre.innerHTML = '';
-			run1.style.display = 'none';
-			var options = {Race: sk};
-			running = transport.Run(text(code), PlaygroundOutput(outpre), options);
-			if (window.notesEnabled) updatePlayStorage('onRun', index, e);
-		}
+    function onRun(e) {
+      var sk = e.shiftKey || localStorage.getItem('play-shiftKey') === 'true';
+      if (running) running.Kill();
+      output.style.display = 'block';
+      outpre.textContent = '';
+      run1.style.display = 'none';
+      var options = { Race: sk };
+      running = transport.Run(text(code), PlaygroundOutput(outpre), options);
+      if (window.notesEnabled) updatePlayStorage('onRun', index, e);
+    }
 
-		function onClose() {
-			if (running) running.Kill();
-			output.style.display = 'none';
-			run1.style.display = 'inline-block';
-			if (window.notesEnabled) updatePlayStorage('onClose', index);
-		}
+    function onClose() {
+      if (running) running.Kill();
+      output.style.display = 'none';
+      run1.style.display = 'inline-block';
+      if (window.notesEnabled) updatePlayStorage('onClose', index);
+    }
 
-		if (window.notesEnabled) {
-			playgroundHandlers.onRun.push(onRun);
-			playgroundHandlers.onClose.push(onClose);
-			playgroundHandlers.onKill.push(onKill);
-		}
+    if (window.notesEnabled) {
+      playgroundHandlers.onRun.push(onRun);
+      playgroundHandlers.onClose.push(onClose);
+      playgroundHandlers.onKill.push(onKill);
+    }
 
-		var run1 = document.createElement('button');
-		run1.innerHTML = 'Run';
-		run1.className = 'run';
-		run1.addEventListener("click", onRun, false);
-		var run2 = document.createElement('button');
-		run2.className = 'run';
-		run2.innerHTML = 'Run';
-		run2.addEventListener("click", onRun, false);
-		var kill = document.createElement('button');
-		kill.className = 'kill';
-		kill.innerHTML = 'Kill';
-		kill.addEventListener("click", onKill, false);
-		var close = document.createElement('button');
-		close.className = 'close';
-		close.innerHTML = 'Close';
-		close.addEventListener("click", onClose, false);
+    var run1 = document.createElement('button');
+    run1.textContent = 'Run';
+    run1.className = 'run';
+    run1.addEventListener('click', onRun, false);
+    var run2 = document.createElement('button');
+    run2.className = 'run';
+    run2.textContent = 'Run';
+    run2.addEventListener('click', onRun, false);
+    var kill = document.createElement('button');
+    kill.className = 'kill';
+    kill.textContent = 'Kill';
+    kill.addEventListener('click', onKill, false);
+    var close = document.createElement('button');
+    close.className = 'close';
+    close.textContent = 'Close';
+    close.addEventListener('click', onClose, false);
 
-		var button = document.createElement('div');
-		button.classList.add('buttons');
-		button.appendChild(run1);
-		// Hack to simulate insertAfter
-		code.parentNode.insertBefore(button, code.nextSibling);
+    var button = document.createElement('div');
+    button.classList.add('buttons');
+    button.appendChild(run1);
+    // Hack to simulate insertAfter
+    code.parentNode.insertBefore(button, code.nextSibling);
 
-		var buttons = document.createElement('div');
-		buttons.classList.add('buttons');
-		buttons.appendChild(run2);
-		buttons.appendChild(kill);
-		buttons.appendChild(close);
+    var buttons = document.createElement('div');
+    buttons.classList.add('buttons');
+    buttons.appendChild(run2);
+    buttons.appendChild(kill);
+    buttons.appendChild(close);
 
-		output.classList.add('output');
-		output.appendChild(buttons);
-		output.appendChild(outpre);
-		output.style.display = 'none';
-		code.parentNode.insertBefore(output, button.nextSibling);
-	}
+    output.classList.add('output');
+    output.appendChild(buttons);
+    output.appendChild(outpre);
+    output.style.display = 'none';
+    code.parentNode.insertBefore(output, button.nextSibling);
+  }
 
-	var play = document.querySelectorAll('div.playground');
-	for (var i = 0; i < play.length; i++) {
-		init(play[i], i);
-	}
+  var play = document.querySelectorAll('div.playground');
+  for (var i = 0; i < play.length; i++) {
+    init(play[i], i);
+  }
 }
diff --git a/godoc/static/playground.js b/godoc/static/playground.js
index 0ab0b2f..f5278a0 100644
--- a/godoc/static/playground.js
+++ b/godoc/static/playground.js
@@ -44,240 +44,261 @@
 // enableVet enables running vet if a program was compiled and ran successfully.
 // If vet returned any errors, display them before the output of a program.
 function HTTPTransport(enableVet) {
-	'use strict';
+  'use strict';
 
-	function playback(output, data) {
-		// Backwards compatibility: default values do not affect the output.
-		var events = data.Events || [];
-		var errors = data.Errors || "";
-		var status = data.Status || 0;
-		var isTest = data.IsTest || false;
-		var testsFailed = data.TestsFailed || 0;
+  function playback(output, data) {
+    // Backwards compatibility: default values do not affect the output.
+    var events = data.Events || [];
+    var errors = data.Errors || '';
+    var status = data.Status || 0;
+    var isTest = data.IsTest || false;
+    var testsFailed = data.TestsFailed || 0;
 
-		var timeout;
-		output({Kind: 'start'});
-		function next() {
-			if (!events || events.length === 0) {
-				if (isTest) {
-					if (testsFailed > 0) {
-						output({Kind: 'system', Body: '\n'+testsFailed+' test'+(testsFailed>1?'s':'')+' failed.'});
-					} else {
-						output({Kind: 'system', Body: '\nAll tests passed.'});
-					}
-				} else {
-					if (status > 0) {
-						output({Kind: 'end', Body: 'status ' + status + '.'});
-					} else {
-						if (errors !== "") {
-							// errors are displayed only in the case of timeout.
-							output({Kind: 'end', Body: errors + '.'});
-						} else {
-							output({Kind: 'end'});
-						}
-					}
-				}
-				return;
-			}
-			var e = events.shift();
-			if (e.Delay === 0) {
-				output({Kind: e.Kind, Body: e.Message});
-				next();
-				return;
-			}
-			timeout = setTimeout(function() {
-				output({Kind: e.Kind, Body: e.Message});
-				next();
-			}, e.Delay / 1000000);
-		}
-		next();
-		return {
-			Stop: function() {
-				clearTimeout(timeout);
-			}
-		};
-	}
+    var timeout;
+    output({ Kind: 'start' });
+    function next() {
+      if (!events || events.length === 0) {
+        if (isTest) {
+          if (testsFailed > 0) {
+            output({
+              Kind: 'system',
+              Body:
+                '\n' +
+                testsFailed +
+                ' test' +
+                (testsFailed > 1 ? 's' : '') +
+                ' failed.',
+            });
+          } else {
+            output({ Kind: 'system', Body: '\nAll tests passed.' });
+          }
+        } else {
+          if (status > 0) {
+            output({ Kind: 'end', Body: 'status ' + status + '.' });
+          } else {
+            if (errors !== '') {
+              // errors are displayed only in the case of timeout.
+              output({ Kind: 'end', Body: errors + '.' });
+            } else {
+              output({ Kind: 'end' });
+            }
+          }
+        }
+        return;
+      }
+      var e = events.shift();
+      if (e.Delay === 0) {
+        output({ Kind: e.Kind, Body: e.Message });
+        next();
+        return;
+      }
+      timeout = setTimeout(function() {
+        output({ Kind: e.Kind, Body: e.Message });
+        next();
+      }, e.Delay / 1000000);
+    }
+    next();
+    return {
+      Stop: function() {
+        clearTimeout(timeout);
+      },
+    };
+  }
 
-	function error(output, msg) {
-		output({Kind: 'start'});
-		output({Kind: 'stderr', Body: msg});
-		output({Kind: 'end'});
-	}
+  function error(output, msg) {
+    output({ Kind: 'start' });
+    output({ Kind: 'stderr', Body: msg });
+    output({ Kind: 'end' });
+  }
 
-	function buildFailed(output, msg) {
-		output({Kind: 'start'});
-		output({Kind: 'stderr', Body: msg});
-		output({Kind: 'system', Body: '\nGo build failed.'});
-	}
+  function buildFailed(output, msg) {
+    output({ Kind: 'start' });
+    output({ Kind: 'stderr', Body: msg });
+    output({ Kind: 'system', Body: '\nGo build failed.' });
+  }
 
-	var seq = 0;
-	return {
-		Run: function(body, output, options) {
-			seq++;
-			var cur = seq;
-			var playing;
-			$.ajax('/compile', {
-				type: 'POST',
-				data: {'version': 2, 'body': body, 'withVet': enableVet},
-				dataType: 'json',
-				success: function(data) {
-					if (seq != cur) return;
-					if (!data) return;
-					if (playing != null) playing.Stop();
-					if (data.Errors) {
-						if (data.Errors === 'process took too long') {
-							// Playback the output that was captured before the timeout.
-							playing = playback(output, data);
-						} else {
-							buildFailed(output, data.Errors);
-						}
-						return;
-					}
-					if (!data.Events) {
-						data.Events = [];
-					}
-					if (data.VetErrors) {
-						// Inject errors from the vet as the first events in the output.
-						data.Events.unshift({Message: 'Go vet exited.\n\n', Kind: 'system', Delay: 0});
-						data.Events.unshift({Message: data.VetErrors, Kind: 'stderr', Delay: 0});
-					}
+  var seq = 0;
+  return {
+    Run: function(body, output, options) {
+      seq++;
+      var cur = seq;
+      var playing;
+      $.ajax('/compile', {
+        type: 'POST',
+        data: { version: 2, body: body, withVet: enableVet },
+        dataType: 'json',
+        success: function(data) {
+          if (seq != cur) return;
+          if (!data) return;
+          if (playing != null) playing.Stop();
+          if (data.Errors) {
+            if (data.Errors === 'process took too long') {
+              // Playback the output that was captured before the timeout.
+              playing = playback(output, data);
+            } else {
+              buildFailed(output, data.Errors);
+            }
+            return;
+          }
+          if (!data.Events) {
+            data.Events = [];
+          }
+          if (data.VetErrors) {
+            // Inject errors from the vet as the first events in the output.
+            data.Events.unshift({
+              Message: 'Go vet exited.\n\n',
+              Kind: 'system',
+              Delay: 0,
+            });
+            data.Events.unshift({
+              Message: data.VetErrors,
+              Kind: 'stderr',
+              Delay: 0,
+            });
+          }
 
-					if (!enableVet || data.VetOK || data.VetErrors) {
-						playing = playback(output, data);
-						return;
-					}
+          if (!enableVet || data.VetOK || data.VetErrors) {
+            playing = playback(output, data);
+            return;
+          }
 
-					// In case the server support doesn't support
-					// compile+vet in same request signaled by the
-					// 'withVet' parameter above, also try the old way.
-					// TODO: remove this when it falls out of use.
-					// It is 2019-05-13 now.
-					$.ajax("/vet", {
-						data: {"body": body},
-						type: "POST",
-						dataType: "json",
-						success: function(dataVet) {
-							if (dataVet.Errors) {
-								// inject errors from the vet as the first events in the output
-								data.Events.unshift({Message: 'Go vet exited.\n\n', Kind: 'system', Delay: 0});
-								data.Events.unshift({Message: dataVet.Errors, Kind: 'stderr', Delay: 0});
-							}
-							playing = playback(output, data);
-						},
-						error: function() {
-							playing = playback(output, data);
-						}
-					});
-				},
-				error: function() {
-					error(output, 'Error communicating with remote server.');
-				}
-			});
-			return {
-				Kill: function() {
-					if (playing != null) playing.Stop();
-					output({Kind: 'end', Body: 'killed'});
-				}
-			};
-		}
-	};
+          // In case the server support doesn't support
+          // compile+vet in same request signaled by the
+          // 'withVet' parameter above, also try the old way.
+          // TODO: remove this when it falls out of use.
+          // It is 2019-05-13 now.
+          $.ajax('/vet', {
+            data: { body: body },
+            type: 'POST',
+            dataType: 'json',
+            success: function(dataVet) {
+              if (dataVet.Errors) {
+                // inject errors from the vet as the first events in the output
+                data.Events.unshift({
+                  Message: 'Go vet exited.\n\n',
+                  Kind: 'system',
+                  Delay: 0,
+                });
+                data.Events.unshift({
+                  Message: dataVet.Errors,
+                  Kind: 'stderr',
+                  Delay: 0,
+                });
+              }
+              playing = playback(output, data);
+            },
+            error: function() {
+              playing = playback(output, data);
+            },
+          });
+        },
+        error: function() {
+          error(output, 'Error communicating with remote server.');
+        },
+      });
+      return {
+        Kill: function() {
+          if (playing != null) playing.Stop();
+          output({ Kind: 'end', Body: 'killed' });
+        },
+      };
+    },
+  };
 }
 
 function SocketTransport() {
-	'use strict';
+  'use strict';
 
-	var id = 0;
-	var outputs = {};
-	var started = {};
-	var websocket;
-	if (window.location.protocol == "http:") {
-		websocket = new WebSocket('ws://' + window.location.host + '/socket');
-	} else if (window.location.protocol == "https:") {
-		websocket = new WebSocket('wss://' + window.location.host + '/socket');
-	}
+  var id = 0;
+  var outputs = {};
+  var started = {};
+  var websocket;
+  if (window.location.protocol == 'http:') {
+    websocket = new WebSocket('ws://' + window.location.host + '/socket');
+  } else if (window.location.protocol == 'https:') {
+    websocket = new WebSocket('wss://' + window.location.host + '/socket');
+  }
 
-	websocket.onclose = function() {
-		console.log('websocket connection closed');
-	};
+  websocket.onclose = function() {
+    console.log('websocket connection closed');
+  };
 
-	websocket.onmessage = function(e) {
-		var m = JSON.parse(e.data);
-		var output = outputs[m.Id];
-		if (output === null)
-			return;
-		if (!started[m.Id]) {
-			output({Kind: 'start'});
-			started[m.Id] = true;
-		}
-		output({Kind: m.Kind, Body: m.Body});
-	};
+  websocket.onmessage = function(e) {
+    var m = JSON.parse(e.data);
+    var output = outputs[m.Id];
+    if (output === null) return;
+    if (!started[m.Id]) {
+      output({ Kind: 'start' });
+      started[m.Id] = true;
+    }
+    output({ Kind: m.Kind, Body: m.Body });
+  };
 
-	function send(m) {
-		websocket.send(JSON.stringify(m));
-	}
+  function send(m) {
+    websocket.send(JSON.stringify(m));
+  }
 
-	return {
-		Run: function(body, output, options) {
-			var thisID = id+'';
-			id++;
-			outputs[thisID] = output;
-			send({Id: thisID, Kind: 'run', Body: body, Options: options});
-			return {
-				Kill: function() {
-					send({Id: thisID, Kind: 'kill'});
-				}
-			};
-		}
-	};
+  return {
+    Run: function(body, output, options) {
+      var thisID = id + '';
+      id++;
+      outputs[thisID] = output;
+      send({ Id: thisID, Kind: 'run', Body: body, Options: options });
+      return {
+        Kill: function() {
+          send({ Id: thisID, Kind: 'kill' });
+        },
+      };
+    },
+  };
 }
 
 function PlaygroundOutput(el) {
-	'use strict';
+  'use strict';
 
-	return function(write) {
-		if (write.Kind == 'start') {
-			el.innerHTML = '';
-			return;
-		}
+  return function(write) {
+    if (write.Kind == 'start') {
+      el.innerHTML = '';
+      return;
+    }
 
-		var cl = 'system';
-		if (write.Kind == 'stdout' || write.Kind == 'stderr')
-			cl = write.Kind;
+    var cl = 'system';
+    if (write.Kind == 'stdout' || write.Kind == 'stderr') cl = write.Kind;
 
-		var m = write.Body;
-		if (write.Kind == 'end') {
-			m = '\nProgram exited' + (m?(': '+m):'.');
-		}
+    var m = write.Body;
+    if (write.Kind == 'end') {
+      m = '\nProgram exited' + (m ? ': ' + m : '.');
+    }
 
-		if (m.indexOf('IMAGE:') === 0) {
-			// TODO(adg): buffer all writes before creating image
-			var url = 'data:image/png;base64,' + m.substr(6);
-			var img = document.createElement('img');
-			img.src = url;
-			el.appendChild(img);
-			return;
-		}
+    if (m.indexOf('IMAGE:') === 0) {
+      // TODO(adg): buffer all writes before creating image
+      var url = 'data:image/png;base64,' + m.substr(6);
+      var img = document.createElement('img');
+      img.src = url;
+      el.appendChild(img);
+      return;
+    }
 
-		// ^L clears the screen.
-		var s = m.split('\x0c');
-		if (s.length > 1) {
-			el.innerHTML = '';
-			m = s.pop();
-		}
+    // ^L clears the screen.
+    var s = m.split('\x0c');
+    if (s.length > 1) {
+      el.innerHTML = '';
+      m = s.pop();
+    }
 
-		m = m.replace(/&/g, '&amp;');
-		m = m.replace(/</g, '&lt;');
-		m = m.replace(/>/g, '&gt;');
+    m = m.replace(/&/g, '&amp;');
+    m = m.replace(/</g, '&lt;');
+    m = m.replace(/>/g, '&gt;');
 
-		var needScroll = (el.scrollTop + el.offsetHeight) == el.scrollHeight;
+    var needScroll = el.scrollTop + el.offsetHeight == el.scrollHeight;
 
-		var span = document.createElement('span');
-		span.className = cl;
-		span.innerHTML = m;
-		el.appendChild(span);
+    var span = document.createElement('span');
+    span.className = cl;
+    span.innerHTML = m;
+    el.appendChild(span);
 
-		if (needScroll)
-			el.scrollTop = el.scrollHeight - el.offsetHeight;
-	};
+    if (needScroll) el.scrollTop = el.scrollHeight - el.offsetHeight;
+  };
 }
 
 (function() {
@@ -285,7 +306,9 @@
     var regex = /prog.go:([0-9]+)/g;
     var r = regex.exec(error);
     while (r) {
-      $(".lines div").eq(r[1]-1).addClass("lineerror");
+      $('.lines div')
+        .eq(r[1] - 1)
+        .addClass('lineerror');
       r = regex.exec(error);
     }
   }
@@ -296,7 +319,7 @@
     };
   }
   function lineClear() {
-    $(".lineerror").removeClass("lineerror");
+    $('.lineerror').removeClass('lineerror');
   }
 
   // opts is an object with these keys
@@ -322,28 +345,28 @@
     function insertTabs(n) {
       // find the selection start and end
       var start = code[0].selectionStart;
-      var end   = code[0].selectionEnd;
+      var end = code[0].selectionEnd;
       // split the textarea content into two, and insert n tabs
       var v = code[0].value;
       var u = v.substr(0, start);
-      for (var i=0; i<n; i++) {
-        u += "\t";
+      for (var i = 0; i < n; i++) {
+        u += '\t';
       }
       u += v.substr(end);
       // set revised content
       code[0].value = u;
       // reset caret position after inserted tabs
-      code[0].selectionStart = start+n;
-      code[0].selectionEnd = start+n;
+      code[0].selectionStart = start + n;
+      code[0].selectionEnd = start + n;
     }
     function autoindent(el) {
       var curpos = el.selectionStart;
       var tabs = 0;
       while (curpos > 0) {
         curpos--;
-        if (el.value[curpos] == "\t") {
+        if (el.value[curpos] == '\t') {
           tabs++;
-        } else if (tabs > 0 || el.value[curpos] == "\n") {
+        } else if (tabs > 0 || el.value[curpos] == '\n') {
           break;
         }
       }
@@ -356,13 +379,13 @@
     function handleSaveShortcut(e) {
       if (e.isDefaultPrevented()) return false;
       if (!e.metaKey && !e.ctrlKey) return false;
-      if (e.key != "S" && e.key != "s") return false;
+      if (e.key != 'S' && e.key != 's') return false;
 
       e.preventDefault();
 
       // Share and save
       share(function(url) {
-        window.location.href = url + ".go?download=true";
+        window.location.href = url + '.go?download=true';
       });
 
       return true;
@@ -371,17 +394,22 @@
     function keyHandler(e) {
       if (opts.enableShortcuts && handleSaveShortcut(e)) return;
 
-      if (e.keyCode == 9 && !e.ctrlKey) { // tab (but not ctrl-tab)
+      if (e.keyCode == 9 && !e.ctrlKey) {
+        // tab (but not ctrl-tab)
         insertTabs(1);
         e.preventDefault();
         return false;
       }
-      if (e.keyCode == 13) { // enter
-        if (e.shiftKey) { // +shift
+      if (e.keyCode == 13) {
+        // enter
+        if (e.shiftKey) {
+          // +shift
           run();
           e.preventDefault();
           return false;
-        } if (e.ctrlKey) { // +control
+        }
+        if (e.ctrlKey) {
+          // +control
           fmt();
           e.preventDefault();
         } else {
@@ -401,17 +429,20 @@
       $(opts.codeEl).val(text);
     }
     function origin(href) {
-      return (""+href).split("/").slice(0, 3).join("/");
+      return ('' + href)
+        .split('/')
+        .slice(0, 3)
+        .join('/');
     }
 
-    var pushedEmpty = (window.location.pathname == "/");
+    var pushedEmpty = window.location.pathname == '/';
     function inputChanged() {
       if (pushedEmpty) {
         return;
       }
       pushedEmpty = true;
       $(opts.shareURLEl).hide();
-      window.history.pushState(null, "", "/");
+      window.history.pushState(null, '', '/');
     }
     function popState(e) {
       if (e === null) {
@@ -422,7 +453,12 @@
       }
     }
     var rewriteHistory = false;
-    if (window.history && window.history.pushState && window.addEventListener && opts.enableHistory) {
+    if (
+      window.history &&
+      window.history.pushState &&
+      window.addEventListener &&
+      opts.enableHistory
+    ) {
       rewriteHistory = true;
       code[0].addEventListener('input', inputChanged);
       window.addEventListener('popstate', popState);
@@ -432,36 +468,42 @@
       if (running) running.Kill();
       lineClear();
       lineHighlight(error);
-      output.empty().addClass("error").text(error);
+      output
+        .empty()
+        .addClass('error')
+        .text(error);
     }
     function loading() {
       lineClear();
       if (running) running.Kill();
-      output.removeClass("error").text('Waiting for remote server...');
+      output.removeClass('error').text('Waiting for remote server...');
     }
     function run() {
       loading();
-      running = transport.Run(body(), highlightOutput(PlaygroundOutput(output[0])));
+      running = transport.Run(
+        body(),
+        highlightOutput(PlaygroundOutput(output[0]))
+      );
     }
 
     function fmt() {
       loading();
-      var data = {"body": body()};
-      if ($(opts.fmtImportEl).is(":checked")) {
-        data["imports"] = "true";
+      var data = { body: body() };
+      if ($(opts.fmtImportEl).is(':checked')) {
+        data['imports'] = 'true';
       }
-      $.ajax("/fmt", {
+      $.ajax('/fmt', {
         data: data,
-        type: "POST",
-        dataType: "json",
+        type: 'POST',
+        dataType: 'json',
         success: function(data) {
           if (data.Error) {
             setError(data.Error);
           } else {
             setBody(data.Body);
-            setError("");
+            setError('');
           }
-        }
+        },
       });
     }
 
@@ -475,21 +517,21 @@
       sharing = true;
 
       var sharingData = body();
-      $.ajax("/share", {
+      $.ajax('/share', {
         processData: false,
         data: sharingData,
-        type: "POST",
-        contentType: "text/plain; charset=utf-8",
+        type: 'POST',
+        contentType: 'text/plain; charset=utf-8',
         complete: function(xhr) {
           sharing = false;
           if (xhr.status != 200) {
-            alert("Server error; try again.");
+            alert('Server error; try again.');
             return;
           }
           if (opts.shareRedirect) {
             window.location = opts.shareRedirect + xhr.responseText;
           }
-          var path = "/p/" + xhr.responseText;
+          var path = '/p/' + xhr.responseText;
           var url = origin(window.location) + path;
 
           for (var i = 0; i < shareCallbacks.length; i++) {
@@ -498,22 +540,29 @@
           shareCallbacks = [];
 
           if (shareURL) {
-            shareURL.show().val(url).focus().select();
+            shareURL
+              .show()
+              .val(url)
+              .focus()
+              .select();
 
             if (rewriteHistory) {
-              var historyData = {"code": sharingData};
-              window.history.pushState(historyData, "", path);
+              var historyData = { code: sharingData };
+              window.history.pushState(historyData, '', path);
               pushedEmpty = false;
             }
           }
-        }
+        },
       });
     }
 
     $(opts.runEl).click(run);
     $(opts.fmtEl).click(fmt);
 
-    if (opts.shareEl !== null && (opts.shareURLEl !== null || opts.shareRedirect !== null)) {
+    if (
+      opts.shareEl !== null &&
+      (opts.shareURLEl !== null || opts.shareRedirect !== null)
+    ) {
       if (opts.shareURLEl) {
         shareURL = $(opts.shareURLEl).hide();
       }
@@ -525,16 +574,16 @@
     if (opts.toysEl !== null) {
       $(opts.toysEl).bind('change', function() {
         var toy = $(this).val();
-        $.ajax("/doc/play/"+toy, {
+        $.ajax('/doc/play/' + toy, {
           processData: false,
-          type: "GET",
+          type: 'GET',
           complete: function(xhr) {
             if (xhr.status != 200) {
-              alert("Server error; try again.");
+              alert('Server error; try again.');
               return;
             }
             setBody(xhr.responseText);
-          }
+          },
         });
       });
     }
diff --git a/godoc/static/static.go b/godoc/static/static.go
index b53e8c7..0f94143 100644
--- a/godoc/static/static.go
+++ b/godoc/static/static.go
@@ -49,7 +49,7 @@
 
 	"godoc.html": "<!DOCTYPE\x20html>\x0a<html>\x0a<head>\x0a<meta\x20http-equiv=\"Content-Type\"\x20content=\"text/html;\x20charset=utf-8\">\x0a<meta\x20name=\"viewport\"\x20content=\"width=device-width,\x20initial-scale=1\">\x0a<meta\x20name=\"theme-color\"\x20content=\"#375EAB\">\x0a{{with\x20.Tabtitle}}\x0a\x20\x20<title>{{html\x20.}}\x20-\x20Go\x20Documentation\x20Server</title>\x0a{{else}}\x0a\x20\x20<title>Go\x20Documentation\x20Server</title>\x0a{{end}}\x0a<link\x20type=\"text/css\"\x20rel=\"stylesheet\"\x20href=\"/lib/godoc/style.css\">\x0a{{if\x20.TreeView}}\x0a<link\x20rel=\"stylesheet\"\x20href=\"/lib/godoc/jquery.treeview.css\">\x0a{{end}}\x0a<script>window.initFuncs\x20=\x20[];</script>\x0a<script\x20src=\"/lib/godoc/jquery.js\"\x20defer></script>\x0a{{if\x20.TreeView}}\x0a<script\x20src=\"/lib/godoc/jquery.treeview.js\"\x20defer></script>\x0a<script\x20src=\"/lib/godoc/jquery.treeview.edit.js\"\x20defer></script>\x0a{{end}}\x0a\x0a{{if\x20.Playground}}\x0a<script\x20src=\"/lib/godoc/playground.js\"\x20defer></script>\x0a{{end}}\x0a{{with\x20.Version}}<script>var\x20goVersion\x20=\x20{{printf\x20\"%q\"\x20.}};</script>{{end}}\x0a<script\x20src=\"/lib/godoc/godocs.js\"\x20defer></script>\x0a</head>\x0a<body>\x0a\x0a<div\x20id='lowframe'\x20style=\"position:\x20fixed;\x20bottom:\x200;\x20left:\x200;\x20height:\x200;\x20width:\x20100%;\x20border-top:\x20thin\x20solid\x20grey;\x20background-color:\x20white;\x20overflow:\x20auto;\">\x0a...\x0a</div><!--\x20#lowframe\x20-->\x0a\x0a<div\x20id=\"topbar\"{{if\x20.Title}}\x20class=\"wide\"{{end}}><div\x20class=\"container\">\x0a<div\x20class=\"top-heading\"\x20id=\"heading-wide\"><a\x20href=\"/pkg/\">Go\x20Documentation\x20Server</a></div>\x0a<div\x20class=\"top-heading\"\x20id=\"heading-narrow\"><a\x20href=\"/pkg/\">GoDoc</a></div>\x0a<a\x20href=\"#\"\x20id=\"menu-button\"><span\x20id=\"menu-button-arrow\">&#9661;</span></a>\x0a<form\x20method=\"GET\"\x20action=\"/search\">\x0a<div\x20id=\"menu\">\x0a{{if\x20(and\x20.Playground\x20.Title)}}\x0a<a\x20id=\"playgroundButton\"\x20href=\"http://play.golang.org/\"\x20title=\"Show\x20Go\x20Playground\">Play</a>\x0a{{end}}\x0a<span\x20class=\"search-box\"><input\x20type=\"search\"\x20id=\"search\"\x20name=\"q\"\x20placeholder=\"Search\"\x20aria-label=\"Search\"\x20required><button\x20type=\"submit\"><span><!--\x20magnifying\x20glass:\x20--><svg\x20width=\"24\"\x20height=\"24\"\x20viewBox=\"0\x200\x2024\x2024\"><title>submit\x20search</title><path\x20d=\"M15.5\x2014h-.79l-.28-.27C15.41\x2012.59\x2016\x2011.11\x2016\x209.5\x2016\x205.91\x2013.09\x203\x209.5\x203S3\x205.91\x203\x209.5\x205.91\x2016\x209.5\x2016c1.61\x200\x203.09-.59\x204.23-1.57l.27.28v.79l5\x204.99L20.49\x2019l-4.99-5zm-6\x200C7.01\x2014\x205\x2011.99\x205\x209.5S7.01\x205\x209.5\x205\x2014\x207.01\x2014\x209.5\x2011.99\x2014\x209.5\x2014z\"/><path\x20d=\"M0\x200h24v24H0z\"\x20fill=\"none\"/></svg></span></button></span>\x0a</div>\x0a</form>\x0a\x0a</div></div>\x0a\x0a{{if\x20.Playground}}\x0a<div\x20id=\"playground\"\x20class=\"play\">\x0a\x09<div\x20class=\"input\"><textarea\x20class=\"code\"\x20spellcheck=\"false\">package\x20main\x0a\x0aimport\x20\"fmt\"\x0a\x0afunc\x20main()\x20{\x0a\x09fmt.Println(\"Hello,\x20\xe4\xb8\x96\xe7\x95\x8c\")\x0a}</textarea></div>\x0a\x09<div\x20class=\"output\"></div>\x0a\x09<div\x20class=\"buttons\">\x0a\x09\x09<a\x20class=\"run\"\x20title=\"Run\x20this\x20code\x20[shift-enter]\">Run</a>\x0a\x09\x09<a\x20class=\"fmt\"\x20title=\"Format\x20this\x20code\">Format</a>\x0a\x09\x09{{if\x20not\x20$.GoogleCN}}\x0a\x09\x09<a\x20class=\"share\"\x20title=\"Share\x20this\x20code\">Share</a>\x0a\x09\x09{{end}}\x0a\x09</div>\x0a</div>\x0a{{end}}\x0a\x0a<div\x20id=\"page\"{{if\x20.Title}}\x20class=\"wide\"{{end}}>\x0a<div\x20class=\"container\">\x0a\x0a{{if\x20or\x20.Title\x20.SrcPath}}\x0a\x20\x20<h1>\x0a\x20\x20\x20\x20{{html\x20.Title}}\x0a\x20\x20\x20\x20{{html\x20.SrcPath\x20|\x20srcBreadcrumb}}\x0a\x20\x20</h1>\x0a{{end}}\x0a\x0a{{with\x20.Subtitle}}\x0a\x20\x20<h2>{{html\x20.}}</h2>\x0a{{end}}\x0a\x0a{{with\x20.SrcPath}}\x0a\x20\x20<h2>\x0a\x20\x20\x20\x20Documentation:\x20{{html\x20.\x20|\x20srcToPkgLink}}\x0a\x20\x20</h2>\x0a{{end}}\x0a\x0a{{/*\x20The\x20Table\x20of\x20Contents\x20is\x20automatically\x20inserted\x20in\x20this\x20<div>.\x0a\x20\x20\x20\x20\x20Do\x20not\x20delete\x20this\x20<div>.\x20*/}}\x0a<div\x20id=\"nav\"></div>\x0a\x0a{{/*\x20Body\x20is\x20HTML-escaped\x20elsewhere\x20*/}}\x0a{{printf\x20\"%s\"\x20.Body}}\x0a\x0a<div\x20id=\"footer\">\x0aBuild\x20version\x20{{html\x20.Version}}.<br>\x0aExcept\x20as\x20<a\x20href=\"https://developers.google.com/site-policies#restrictions\">noted</a>,\x0athe\x20content\x20of\x20this\x20page\x20is\x20licensed\x20under\x20the\x0aCreative\x20Commons\x20Attribution\x203.0\x20License,\x0aand\x20code\x20is\x20licensed\x20under\x20a\x20<a\x20href=\"/LICENSE\">BSD\x20license</a>.<br>\x0a<a\x20href=\"/doc/tos.html\">Terms\x20of\x20Service</a>\x20|\x0a<a\x20href=\"http://www.google.com/intl/en/policies/privacy/\">Privacy\x20Policy</a>\x0a</div>\x0a\x0a</div><!--\x20.container\x20-->\x0a</div><!--\x20#page\x20-->\x0a</body>\x0a</html>\x0a",
 
-	"godocs.js": "//\x20Copyright\x202012\x20The\x20Go\x20Authors.\x20All\x20rights\x20reserved.\x0a//\x20Use\x20of\x20this\x20source\x20code\x20is\x20governed\x20by\x20a\x20BSD-style\x0a//\x20license\x20that\x20can\x20be\x20found\x20in\x20the\x20LICENSE\x20file.\x0a\x0a/*\x20A\x20little\x20code\x20to\x20ease\x20navigation\x20of\x20these\x20documents.\x0a\x20*\x0a\x20*\x20On\x20window\x20load\x20we:\x0a\x20*\x20\x20+\x20Generate\x20a\x20table\x20of\x20contents\x20(generateTOC)\x0a\x20*\x20\x20+\x20Bind\x20foldable\x20sections\x20(bindToggles)\x0a\x20*\x20\x20+\x20Bind\x20links\x20to\x20foldable\x20sections\x20(bindToggleLinks)\x0a\x20*/\x0a\x0a(function()\x20{\x0a'use\x20strict';\x0a\x0a//\x20Mobile-friendly\x20topbar\x20menu\x0a$(function()\x20{\x0a\x20\x20var\x20menu\x20=\x20$('#menu');\x0a\x20\x20var\x20menuButton\x20=\x20$('#menu-button');\x0a\x20\x20var\x20menuButtonArrow\x20=\x20$('#menu-button-arrow');\x0a\x20\x20menuButton.click(function(event)\x20{\x0a\x20\x20\x20\x20menu.toggleClass('menu-visible');\x0a\x20\x20\x20\x20menuButtonArrow.toggleClass('vertical-flip');\x0a\x20\x20\x20\x20event.preventDefault();\x0a\x20\x20\x20\x20return\x20false;\x0a\x20\x20});\x0a});\x0a\x0a/*\x20Generates\x20a\x20table\x20of\x20contents:\x20looks\x20for\x20h2\x20and\x20h3\x20elements\x20and\x20generates\x0a\x20*\x20links.\x20\"Decorates\"\x20the\x20element\x20with\x20id==\"nav\"\x20with\x20this\x20table\x20of\x20contents.\x0a\x20*/\x0afunction\x20generateTOC()\x20{\x0a\x20\x20if\x20($('#manual-nav').length\x20>\x200)\x20{\x0a\x20\x20\x20\x20return;\x0a\x20\x20}\x0a\x0a\x20\x20//\x20For\x20search,\x20we\x20send\x20the\x20toc\x20precomputed\x20from\x20server-side.\x0a\x20\x20//\x20TODO:\x20Ideally,\x20this\x20should\x20always\x20be\x20precomputed\x20for\x20all\x20pages,\x20but\x20then\x0a\x20\x20//\x20we\x20need\x20to\x20do\x20HTML\x20parsing\x20on\x20the\x20server-side.\x0a\x20\x20if\x20(location.pathname\x20===\x20'/search')\x20{\x0a\x20\x20\x20\x20return;\x0a\x20\x20}\x0a\x0a\x20\x20var\x20nav\x20=\x20$('#nav');\x0a\x20\x20if\x20(nav.length\x20===\x200)\x20{\x0a\x20\x20\x20\x20return;\x0a\x20\x20}\x0a\x0a\x20\x20var\x20toc_items\x20=\x20[];\x0a\x20\x20$(nav).nextAll('h2,\x20h3').each(function()\x20{\x0a\x20\x20\x20\x20var\x20node\x20=\x20this;\x0a\x20\x20\x20\x20if\x20(node.id\x20==\x20'')\x0a\x20\x20\x20\x20\x20\x20node.id\x20=\x20'tmp_'\x20+\x20toc_items.length;\x0a\x20\x20\x20\x20var\x20link\x20=\x20$('<a/>').attr('href',\x20'#'\x20+\x20node.id).text($(node).text());\x0a\x20\x20\x20\x20var\x20item;\x0a\x20\x20\x20\x20if\x20($(node).is('h2'))\x20{\x0a\x20\x20\x20\x20\x20\x20item\x20=\x20$('<dt/>');\x0a\x20\x20\x20\x20}\x20else\x20{\x20//\x20h3\x0a\x20\x20\x20\x20\x20\x20item\x20=\x20$('<dd\x20class=\"indent\"/>');\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20item.append(link);\x0a\x20\x20\x20\x20toc_items.push(item);\x0a\x20\x20});\x0a\x20\x20if\x20(toc_items.length\x20<=\x201)\x20{\x0a\x20\x20\x20\x20return;\x0a\x20\x20}\x0a\x20\x20var\x20dl1\x20=\x20$('<dl/>');\x0a\x20\x20var\x20dl2\x20=\x20$('<dl/>');\x0a\x0a\x20\x20var\x20split_index\x20=\x20(toc_items.length\x20/\x202)\x20+\x201;\x0a\x20\x20if\x20(split_index\x20<\x208)\x20{\x0a\x20\x20\x20\x20split_index\x20=\x20toc_items.length;\x0a\x20\x20}\x0a\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20split_index;\x20i++)\x20{\x0a\x20\x20\x20\x20dl1.append(toc_items[i]);\x0a\x20\x20}\x0a\x20\x20for\x20(/*\x20keep\x20using\x20i\x20*/;\x20i\x20<\x20toc_items.length;\x20i++)\x20{\x0a\x20\x20\x20\x20dl2.append(toc_items[i]);\x0a\x20\x20}\x0a\x0a\x20\x20var\x20tocTable\x20=\x20$('<table\x20class=\"unruled\"/>').appendTo(nav);\x0a\x20\x20var\x20tocBody\x20=\x20$('<tbody/>').appendTo(tocTable);\x0a\x20\x20var\x20tocRow\x20=\x20$('<tr/>').appendTo(tocBody);\x0a\x0a\x20\x20//\x201st\x20column\x0a\x20\x20$('<td\x20class=\"first\"/>').appendTo(tocRow).append(dl1);\x0a\x20\x20//\x202nd\x20column\x0a\x20\x20$('<td/>').appendTo(tocRow).append(dl2);\x0a}\x0a\x0afunction\x20bindToggle(el)\x20{\x0a\x20\x20$('.toggleButton',\x20el).click(function()\x20{\x0a\x20\x20\x20\x20if\x20($(this).closest(\".toggle,\x20.toggleVisible\")[0]\x20!=\x20el)\x20{\x0a\x20\x20\x20\x20\x20\x20//\x20Only\x20trigger\x20the\x20closest\x20toggle\x20header.\x0a\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20if\x20($(el).is('.toggle'))\x20{\x0a\x20\x20\x20\x20\x20\x20$(el).addClass('toggleVisible').removeClass('toggle');\x0a\x20\x20\x20\x20}\x20else\x20{\x0a\x20\x20\x20\x20\x20\x20$(el).addClass('toggle').removeClass('toggleVisible');\x0a\x20\x20\x20\x20}\x0a\x20\x20});\x0a}\x0a\x0afunction\x20bindToggles(selector)\x20{\x0a\x20\x20$(selector).each(function(i,\x20el)\x20{\x0a\x20\x20\x20\x20bindToggle(el);\x0a\x20\x20});\x0a}\x0a\x0afunction\x20bindToggleLink(el,\x20prefix)\x20{\x0a\x20\x20$(el).click(function()\x20{\x0a\x20\x20\x20\x20var\x20href\x20=\x20$(el).attr('href');\x0a\x20\x20\x20\x20var\x20i\x20=\x20href.indexOf('#'+prefix);\x0a\x20\x20\x20\x20if\x20(i\x20<\x200)\x20{\x0a\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20var\x20id\x20=\x20'#'\x20+\x20prefix\x20+\x20href.slice(i+1+prefix.length);\x0a\x20\x20\x20\x20if\x20($(id).is('.toggle'))\x20{\x0a\x20\x20\x20\x20\x20\x20$(id).find('.toggleButton').first().click();\x0a\x20\x20\x20\x20}\x0a\x20\x20});\x0a}\x0afunction\x20bindToggleLinks(selector,\x20prefix)\x20{\x0a\x20\x20$(selector).each(function(i,\x20el)\x20{\x0a\x20\x20\x20\x20bindToggleLink(el,\x20prefix);\x0a\x20\x20});\x0a}\x0a\x0afunction\x20setupDropdownPlayground()\x20{\x0a\x20\x20if\x20(!$('#page').is('.wide'))\x20{\x0a\x20\x20\x20\x20return;\x20//\x20don't\x20show\x20on\x20front\x20page\x0a\x20\x20}\x0a\x20\x20var\x20button\x20=\x20$('#playgroundButton');\x0a\x20\x20var\x20div\x20=\x20$('#playground');\x0a\x20\x20var\x20setup\x20=\x20false;\x0a\x20\x20button.toggle(function()\x20{\x0a\x20\x20\x20\x20button.addClass('active');\x0a\x20\x20\x20\x20div.show();\x0a\x20\x20\x20\x20if\x20(setup)\x20{\x0a\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20setup\x20=\x20true;\x0a\x20\x20\x20\x20playground({\x0a\x20\x20\x20\x20\x20\x20'codeEl':\x20$('.code',\x20div),\x0a\x20\x20\x20\x20\x20\x20'outputEl':\x20$('.output',\x20div),\x0a\x20\x20\x20\x20\x20\x20'runEl':\x20$('.run',\x20div),\x0a\x20\x20\x20\x20\x20\x20'fmtEl':\x20$('.fmt',\x20div),\x0a\x20\x20\x20\x20\x20\x20'shareEl':\x20$('.share',\x20div),\x0a\x20\x20\x20\x20\x20\x20'shareRedirect':\x20'//play.golang.org/p/'\x0a\x20\x20\x20\x20});\x0a\x20\x20},\x0a\x20\x20function()\x20{\x0a\x20\x20\x20\x20button.removeClass('active');\x0a\x20\x20\x20\x20div.hide();\x0a\x20\x20});\x0a\x20\x20$('#menu').css('min-width',\x20'+=60');\x0a\x0a\x20\x20//\x20Hide\x20inline\x20playground\x20if\x20we\x20click\x20somewhere\x20on\x20the\x20page.\x0a\x20\x20//\x20This\x20is\x20needed\x20in\x20mobile\x20devices,\x20where\x20the\x20\"Play\"\x20button\x0a\x20\x20//\x20is\x20not\x20clickable\x20once\x20the\x20playground\x20opens\x20up.\x0a\x20\x20$(\"#page\").click(function()\x20{\x0a\x20\x20\x20\x20if\x20(button.hasClass('active'))\x20{\x0a\x20\x20\x20\x20\x20\x20button.click();\x0a\x20\x20\x20\x20}\x0a\x20\x20});\x0a}\x0a\x0afunction\x20setupInlinePlayground()\x20{\x0a\x09'use\x20strict';\x0a\x09//\x20Set\x20up\x20playground\x20when\x20each\x20element\x20is\x20toggled.\x0a\x09$('div.play').each(function\x20(i,\x20el)\x20{\x0a\x09\x09//\x20Set\x20up\x20playground\x20for\x20this\x20example.\x0a\x09\x09var\x20setup\x20=\x20function()\x20{\x0a\x09\x09\x09var\x20code\x20=\x20$('.code',\x20el);\x0a\x09\x09\x09playground({\x0a\x09\x09\x09\x09'codeEl':\x20\x20\x20code,\x0a\x09\x09\x09\x09'outputEl':\x20$('.output',\x20el),\x0a\x09\x09\x09\x09'runEl':\x20\x20\x20\x20$('.run',\x20el),\x0a\x09\x09\x09\x09'fmtEl':\x20\x20\x20\x20$('.fmt',\x20el),\x0a\x09\x09\x09\x09'shareEl':\x20\x20$('.share',\x20el),\x0a\x09\x09\x09\x09'shareRedirect':\x20'//play.golang.org/p/'\x0a\x09\x09\x09});\x0a\x0a\x09\x09\x09//\x20Make\x20the\x20code\x20textarea\x20resize\x20to\x20fit\x20content.\x0a\x09\x09\x09var\x20resize\x20=\x20function()\x20{\x0a\x09\x09\x09\x09code.height(0);\x0a\x09\x09\x09\x09var\x20h\x20=\x20code[0].scrollHeight;\x0a\x09\x09\x09\x09code.height(h+20);\x20//\x20minimize\x20bouncing.\x0a\x09\x09\x09\x09code.closest('.input').height(h);\x0a\x09\x09\x09};\x0a\x09\x09\x09code.on('keydown',\x20resize);\x0a\x09\x09\x09code.on('keyup',\x20resize);\x0a\x09\x09\x09code.keyup();\x20//\x20resize\x20now.\x0a\x09\x09};\x0a\x0a\x09\x09//\x20If\x20example\x20already\x20visible,\x20set\x20up\x20playground\x20now.\x0a\x09\x09if\x20($(el).is(':visible'))\x20{\x0a\x09\x09\x09setup();\x0a\x09\x09\x09return;\x0a\x09\x09}\x0a\x0a\x09\x09//\x20Otherwise,\x20set\x20up\x20playground\x20when\x20example\x20is\x20expanded.\x0a\x09\x09var\x20built\x20=\x20false;\x0a\x09\x09$(el).closest('.toggle').click(function()\x20{\x0a\x09\x09\x09//\x20Only\x20set\x20up\x20once.\x0a\x09\x09\x09if\x20(!built)\x20{\x0a\x09\x09\x09\x09setup();\x0a\x09\x09\x09\x09built\x20=\x20true;\x0a\x09\x09\x09}\x0a\x09\x09});\x0a\x09});\x0a}\x0a\x0a//\x20fixFocus\x20tries\x20to\x20put\x20focus\x20to\x20div#page\x20so\x20that\x20keyboard\x20navigation\x20works.\x0afunction\x20fixFocus()\x20{\x0a\x20\x20var\x20page\x20=\x20$('div#page');\x0a\x20\x20var\x20topbar\x20=\x20$('div#topbar');\x0a\x20\x20page.css('outline',\x200);\x20//\x20disable\x20outline\x20when\x20focused\x0a\x20\x20page.attr('tabindex',\x20-1);\x20//\x20and\x20set\x20tabindex\x20so\x20that\x20it\x20is\x20focusable\x0a\x20\x20$(window).resize(function\x20(evt)\x20{\x0a\x20\x20\x20\x20//\x20only\x20focus\x20page\x20when\x20the\x20topbar\x20is\x20at\x20fixed\x20position\x20(that\x20is,\x20it's\x20in\x0a\x20\x20\x20\x20//\x20front\x20of\x20page,\x20and\x20keyboard\x20event\x20will\x20go\x20to\x20the\x20former\x20by\x20default.)\x0a\x20\x20\x20\x20//\x20by\x20focusing\x20page,\x20keyboard\x20event\x20will\x20go\x20to\x20page\x20so\x20that\x20up/down\x20arrow,\x0a\x20\x20\x20\x20//\x20space,\x20etc.\x20will\x20work\x20as\x20expected.\x0a\x20\x20\x20\x20if\x20(topbar.css('position')\x20==\x20\"fixed\")\x0a\x20\x20\x20\x20\x20\x20page.focus();\x0a\x20\x20}).resize();\x0a}\x0a\x0afunction\x20toggleHash()\x20{\x0a\x20\x20var\x20id\x20=\x20window.location.hash.substring(1);\x0a\x20\x20//\x20Open\x20all\x20of\x20the\x20toggles\x20for\x20a\x20particular\x20hash.\x0a\x20\x20var\x20els\x20=\x20$(\x0a\x20\x20\x20\x20document.getElementById(id),\x0a\x20\x20\x20\x20$('a[name]').filter(function()\x20{\x0a\x20\x20\x20\x20\x20\x20return\x20$(this).attr('name')\x20==\x20id;\x0a\x20\x20\x20\x20})\x0a\x20\x20);\x0a\x0a\x20\x20while\x20(els.length)\x20{\x0a\x20\x20\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20els.length;\x20i++)\x20{\x0a\x20\x20\x20\x20\x20\x20var\x20el\x20=\x20$(els[i]);\x0a\x20\x20\x20\x20\x20\x20if\x20(el.is('.toggle'))\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20el.find('.toggleButton').first().click();\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20els\x20=\x20el.parent();\x0a\x20\x20}\x0a}\x0a\x0afunction\x20personalizeInstallInstructions()\x20{\x0a\x20\x20var\x20prefix\x20=\x20'?download=';\x0a\x20\x20var\x20s\x20=\x20window.location.search;\x0a\x20\x20if\x20(s.indexOf(prefix)\x20!=\x200)\x20{\x0a\x20\x20\x20\x20//\x20No\x20'download'\x20query\x20string;\x20detect\x20\"test\"\x20instructions\x20from\x20User\x20Agent.\x0a\x20\x20\x20\x20if\x20(navigator.platform.indexOf('Win')\x20!=\x20-1)\x20{\x0a\x20\x20\x20\x20\x20\x20$('.testUnix').hide();\x0a\x20\x20\x20\x20\x20\x20$('.testWindows').show();\x0a\x20\x20\x20\x20}\x20else\x20{\x0a\x20\x20\x20\x20\x20\x20$('.testUnix').show();\x0a\x20\x20\x20\x20\x20\x20$('.testWindows').hide();\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20return;\x0a\x20\x20}\x0a\x0a\x20\x20var\x20filename\x20=\x20s.substr(prefix.length);\x0a\x20\x20var\x20filenameRE\x20=\x20/^go1\\.\\d+(\\.\\d+)?([a-z0-9]+)?\\.([a-z0-9]+)(-[a-z0-9]+)?(-osx10\\.[68])?\\.([a-z.]+)$/;\x0a\x20\x20var\x20m\x20=\x20filenameRE.exec(filename);\x0a\x20\x20if\x20(!m)\x20{\x0a\x20\x20\x20\x20//\x20Can't\x20interpret\x20file\x20name;\x20bail.\x0a\x20\x20\x20\x20return;\x0a\x20\x20}\x0a\x20\x20$('.downloadFilename').text(filename);\x0a\x20\x20$('.hideFromDownload').hide();\x0a\x0a\x20\x20var\x20os\x20=\x20m[3];\x0a\x20\x20var\x20ext\x20=\x20m[6];\x0a\x20\x20if\x20(ext\x20!=\x20'tar.gz')\x20{\x0a\x20\x20\x20\x20$('#tarballInstructions').hide();\x0a\x20\x20}\x0a\x20\x20if\x20(os\x20!=\x20'darwin'\x20||\x20ext\x20!=\x20'pkg')\x20{\x0a\x20\x20\x20\x20$('#darwinPackageInstructions').hide();\x0a\x20\x20}\x0a\x20\x20if\x20(os\x20!=\x20'windows')\x20{\x0a\x20\x20\x20\x20$('#windowsInstructions').hide();\x0a\x20\x20\x20\x20$('.testUnix').show();\x0a\x20\x20\x20\x20$('.testWindows').hide();\x0a\x20\x20}\x20else\x20{\x0a\x20\x20\x20\x20if\x20(ext\x20!=\x20'msi')\x20{\x0a\x20\x20\x20\x20\x20\x20$('#windowsInstallerInstructions').hide();\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20if\x20(ext\x20!=\x20'zip')\x20{\x0a\x20\x20\x20\x20\x20\x20$('#windowsZipInstructions').hide();\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20$('.testUnix').hide();\x0a\x20\x20\x20\x20$('.testWindows').show();\x0a\x20\x20}\x0a\x0a\x20\x20var\x20download\x20=\x20\"https://dl.google.com/go/\"\x20+\x20filename;\x0a\x0a\x20\x20var\x20message\x20=\x20$('<p\x20class=\"downloading\">'+\x0a\x20\x20\x20\x20'Your\x20download\x20should\x20begin\x20shortly.\x20'+\x0a\x20\x20\x20\x20'If\x20it\x20does\x20not,\x20click\x20<a>this\x20link</a>.</p>');\x0a\x20\x20message.find('a').attr('href',\x20download);\x0a\x20\x20message.insertAfter('#nav');\x0a\x0a\x20\x20window.location\x20=\x20download;\x0a}\x0a\x0afunction\x20updateVersionTags()\x20{\x0a\x20\x20var\x20v\x20=\x20window.goVersion;\x0a\x20\x20if\x20(/^go[0-9.]+$/.test(v))\x20{\x0a\x20\x20\x20\x20$(\".versionTag\").empty().text(v);\x0a\x20\x20\x20\x20$(\".whereTag\").hide();\x0a\x20\x20}\x0a}\x0a\x0afunction\x20addPermalinks()\x20{\x0a\x20\x20function\x20addPermalink(source,\x20parent)\x20{\x0a\x20\x20\x20\x20var\x20id\x20=\x20source.attr(\"id\");\x0a\x20\x20\x20\x20if\x20(id\x20==\x20\"\"\x20||\x20id.indexOf(\"tmp_\")\x20===\x200)\x20{\x0a\x20\x20\x20\x20\x20\x20//\x20Auto-generated\x20permalink.\x0a\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20if\x20(parent.find(\">\x20.permalink\").length)\x20{\x0a\x20\x20\x20\x20\x20\x20//\x20Already\x20attached.\x0a\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20parent.append(\"\x20\").append($(\"<a\x20class='permalink'>&#xb6;</a>\").attr(\"href\",\x20\"#\"\x20+\x20id));\x0a\x20\x20}\x0a\x0a\x20\x20$(\"#page\x20.container\").find(\"h2[id],\x20h3[id]\").each(function()\x20{\x0a\x20\x20\x20\x20var\x20el\x20=\x20$(this);\x0a\x20\x20\x20\x20addPermalink(el,\x20el);\x0a\x20\x20});\x0a\x0a\x20\x20$(\"#page\x20.container\").find(\"dl[id]\").each(function()\x20{\x0a\x20\x20\x20\x20var\x20el\x20=\x20$(this);\x0a\x20\x20\x20\x20//\x20Add\x20the\x20anchor\x20to\x20the\x20\"dt\"\x20element.\x0a\x20\x20\x20\x20addPermalink(el,\x20el.find(\">\x20dt\").first());\x0a\x20\x20});\x0a}\x0a\x0a$(\".js-expandAll\").click(function()\x20{\x0a\x20\x20if\x20($(this).hasClass(\"collapsed\"))\x20{\x0a\x20\x20\x20\x20toggleExamples('toggle');\x0a\x20\x20\x20\x20$(this).text(\"(Collapse\x20All)\");\x0a\x20\x20}\x20else\x20{\x0a\x20\x20\x20\x20toggleExamples('toggleVisible');\x0a\x20\x20\x20\x20$(this).text(\"(Expand\x20All)\");\x0a\x20\x20}\x0a\x20\x20$(this).toggleClass(\"collapsed\")\x0a});\x0a\x0afunction\x20toggleExamples(className)\x20{\x0a\x20\x20//\x20We\x20need\x20to\x20explicitly\x20iterate\x20through\x20divs\x20starting\x20with\x20\"example_\"\x0a\x20\x20//\x20to\x20avoid\x20toggling\x20Overview\x20and\x20Index\x20collapsibles.\x0a\x20\x20$(\"[id^='example_']\").each(function()\x20{\x0a\x20\x20\x20\x20//\x20Check\x20for\x20state\x20and\x20click\x20it\x20only\x20if\x20required.\x0a\x20\x20\x20\x20if\x20($(this).hasClass(className))\x20{\x0a\x20\x20\x20\x20\x20\x20$(this).find('.toggleButton').first().click();\x0a\x20\x20\x20\x20}\x0a\x20\x20});\x0a}\x0a\x0a$(document).ready(function()\x20{\x0a\x20\x20generateTOC();\x0a\x20\x20addPermalinks();\x0a\x20\x20bindToggles(\".toggle\");\x0a\x20\x20bindToggles(\".toggleVisible\");\x0a\x20\x20bindToggleLinks(\".exampleLink\",\x20\"example_\");\x0a\x20\x20bindToggleLinks(\".overviewLink\",\x20\"\");\x0a\x20\x20bindToggleLinks(\".examplesLink\",\x20\"\");\x0a\x20\x20bindToggleLinks(\".indexLink\",\x20\"\");\x0a\x20\x20setupDropdownPlayground();\x0a\x20\x20setupInlinePlayground();\x0a\x20\x20fixFocus();\x0a\x20\x20setupTypeInfo();\x0a\x20\x20setupCallgraphs();\x0a\x20\x20toggleHash();\x0a\x20\x20personalizeInstallInstructions();\x0a\x20\x20updateVersionTags();\x0a\x0a\x20\x20//\x20godoc.html\x20defines\x20window.initFuncs\x20in\x20the\x20<head>\x20tag,\x20and\x20root.html\x20and\x0a\x20\x20//\x20codewalk.js\x20push\x20their\x20on-page-ready\x20functions\x20to\x20the\x20list.\x0a\x20\x20//\x20We\x20execute\x20those\x20functions\x20here,\x20to\x20avoid\x20loading\x20jQuery\x20until\x20the\x20page\x0a\x20\x20//\x20content\x20is\x20loaded.\x0a\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20window.initFuncs.length;\x20i++)\x20window.initFuncs[i]();\x0a});\x0a\x0a//\x20--\x20analysis\x20---------------------------------------------------------\x0a\x0a//\x20escapeHTML\x20returns\x20HTML\x20for\x20s,\x20with\x20metacharacters\x20quoted.\x0a//\x20It\x20is\x20safe\x20for\x20use\x20in\x20both\x20elements\x20and\x20attributes\x0a//\x20(unlike\x20the\x20\"set\x20innerText,\x20read\x20innerHTML\"\x20trick).\x0afunction\x20escapeHTML(s)\x20{\x0a\x20\x20\x20\x20return\x20s.replace(/&/g,\x20'&amp;').\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20replace(/\\\"/g,\x20'&quot;').\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20replace(/\\'/g,\x20'&#39;').\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20replace(/</g,\x20'&lt;').\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20replace(/>/g,\x20'&gt;');\x0a}\x0a\x0a//\x20makeAnchor\x20returns\x20HTML\x20for\x20an\x20<a>\x20element,\x20given\x20an\x20anchorJSON\x20object.\x0afunction\x20makeAnchor(json)\x20{\x0a\x20\x20var\x20html\x20=\x20escapeHTML(json.Text);\x0a\x20\x20if\x20(json.Href\x20!=\x20\"\")\x20{\x0a\x20\x20\x20\x20\x20\x20html\x20=\x20\"<a\x20href='\"\x20+\x20escapeHTML(json.Href)\x20+\x20\"'>\"\x20+\x20html\x20+\x20\"</a>\";\x0a\x20\x20}\x0a\x20\x20return\x20html;\x0a}\x0a\x0afunction\x20showLowFrame(html)\x20{\x0a\x20\x20var\x20lowframe\x20=\x20document.getElementById('lowframe');\x0a\x20\x20lowframe.style.height\x20=\x20\"200px\";\x0a\x20\x20lowframe.innerHTML\x20=\x20\"<p\x20style='text-align:\x20left;'>\"\x20+\x20html\x20+\x20\"</p>\\n\"\x20+\x0a\x20\x20\x20\x20\x20\x20\"<div\x20onclick='hideLowFrame()'\x20style='position:\x20absolute;\x20top:\x200;\x20right:\x200;\x20cursor:\x20pointer;'>\xe2\x9c\x98</div>\"\x0a};\x0a\x0adocument.hideLowFrame\x20=\x20function()\x20{\x0a\x20\x20var\x20lowframe\x20=\x20document.getElementById('lowframe');\x0a\x20\x20lowframe.style.height\x20=\x20\"0px\";\x0a}\x0a\x0a//\x20onClickCallers\x20is\x20the\x20onclick\x20action\x20for\x20the\x20'func'\x20tokens\x20of\x20a\x0a//\x20function\x20declaration.\x0adocument.onClickCallers\x20=\x20function(index)\x20{\x0a\x20\x20var\x20data\x20=\x20document.ANALYSIS_DATA[index]\x0a\x20\x20if\x20(data.Callers.length\x20==\x201\x20&&\x20data.Callers[0].Sites.length\x20==\x201)\x20{\x0a\x20\x20\x20\x20document.location\x20=\x20data.Callers[0].Sites[0].Href;\x20//\x20jump\x20to\x20sole\x20caller\x0a\x20\x20\x20\x20return;\x0a\x20\x20}\x0a\x0a\x20\x20var\x20html\x20=\x20\"Callers\x20of\x20<code>\"\x20+\x20escapeHTML(data.Callee)\x20+\x20\"</code>:<br/>\\n\";\x0a\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20data.Callers.length;\x20i++)\x20{\x0a\x20\x20\x20\x20var\x20caller\x20=\x20data.Callers[i];\x0a\x20\x20\x20\x20html\x20+=\x20\"<code>\"\x20+\x20escapeHTML(caller.Func)\x20+\x20\"</code>\";\x0a\x20\x20\x20\x20var\x20sites\x20=\x20caller.Sites;\x0a\x20\x20\x20\x20if\x20(sites\x20!=\x20null\x20&&\x20sites.length\x20>\x200)\x20{\x0a\x20\x20\x20\x20\x20\x20html\x20+=\x20\"\x20at\x20line\x20\";\x0a\x20\x20\x20\x20\x20\x20for\x20(var\x20j\x20=\x200;\x20j\x20<\x20sites.length;\x20j++)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20if\x20(j\x20>\x200)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20html\x20+=\x20\",\x20\";\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20html\x20+=\x20\"<code>\"\x20+\x20makeAnchor(sites[j])\x20+\x20\"</code>\";\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20html\x20+=\x20\"<br/>\\n\";\x0a\x20\x20}\x0a\x20\x20showLowFrame(html);\x0a};\x0a\x0a//\x20onClickCallees\x20is\x20the\x20onclick\x20action\x20for\x20the\x20'('\x20token\x20of\x20a\x20function\x20call.\x0adocument.onClickCallees\x20=\x20function(index)\x20{\x0a\x20\x20var\x20data\x20=\x20document.ANALYSIS_DATA[index]\x0a\x20\x20if\x20(data.Callees.length\x20==\x201)\x20{\x0a\x20\x20\x20\x20document.location\x20=\x20data.Callees[0].Href;\x20//\x20jump\x20to\x20sole\x20callee\x0a\x20\x20\x20\x20return;\x0a\x20\x20}\x0a\x0a\x20\x20var\x20html\x20=\x20\"Callees\x20of\x20this\x20\"\x20+\x20escapeHTML(data.Descr)\x20+\x20\":<br/>\\n\";\x0a\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20data.Callees.length;\x20i++)\x20{\x0a\x20\x20\x20\x20html\x20+=\x20\"<code>\"\x20+\x20makeAnchor(data.Callees[i])\x20+\x20\"</code><br/>\\n\";\x0a\x20\x20}\x0a\x20\x20showLowFrame(html);\x0a};\x0a\x0a//\x20onClickTypeInfo\x20is\x20the\x20onclick\x20action\x20for\x20identifiers\x20declaring\x20a\x20named\x20type.\x0adocument.onClickTypeInfo\x20=\x20function(index)\x20{\x0a\x20\x20var\x20data\x20=\x20document.ANALYSIS_DATA[index];\x0a\x20\x20var\x20html\x20=\x20\"Type\x20<code>\"\x20+\x20data.Name\x20+\x20\"</code>:\x20\"\x20+\x0a\x20\x20\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<small>(size=\"\x20+\x20data.Size\x20+\x20\",\x20align=\"\x20+\x20data.Align\x20+\x20\")</small><br/>\\n\";\x0a\x20\x20html\x20+=\x20implementsHTML(data);\x0a\x20\x20html\x20+=\x20methodsetHTML(data);\x0a\x20\x20showLowFrame(html);\x0a};\x0a\x0a//\x20implementsHTML\x20returns\x20HTML\x20for\x20the\x20implements\x20relation\x20of\x20the\x0a//\x20specified\x20TypeInfoJSON\x20value.\x0afunction\x20implementsHTML(info)\x20{\x0a\x20\x20var\x20html\x20=\x20\"\";\x0a\x20\x20if\x20(info.ImplGroups\x20!=\x20null)\x20{\x0a\x20\x20\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20info.ImplGroups.length;\x20i++)\x20{\x0a\x20\x20\x20\x20\x20\x20var\x20group\x20=\x20info.ImplGroups[i];\x0a\x20\x20\x20\x20\x20\x20var\x20x\x20=\x20\"<code>\"\x20+\x20escapeHTML(group.Descr)\x20+\x20\"</code>\x20\";\x0a\x20\x20\x20\x20\x20\x20for\x20(var\x20j\x20=\x200;\x20j\x20<\x20group.Facts.length;\x20j++)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20var\x20fact\x20=\x20group.Facts[j];\x0a\x20\x20\x20\x20\x20\x20\x20\x20var\x20y\x20=\x20\"<code>\"\x20+\x20makeAnchor(fact.Other)\x20+\x20\"</code>\";\x0a\x20\x20\x20\x20\x20\x20\x20\x20if\x20(fact.ByKind\x20!=\x20null)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20html\x20+=\x20escapeHTML(fact.ByKind)\x20+\x20\"\x20type\x20\"\x20+\x20y\x20+\x20\"\x20implements\x20\"\x20+\x20x;\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x20else\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20html\x20+=\x20x\x20+\x20\"\x20implements\x20\"\x20+\x20y;\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20html\x20+=\x20\"<br/>\\n\";\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20}\x0a\x20\x20}\x0a\x20\x20return\x20html;\x0a}\x0a\x0a\x0a//\x20methodsetHTML\x20returns\x20HTML\x20for\x20the\x20methodset\x20of\x20the\x20specified\x0a//\x20TypeInfoJSON\x20value.\x0afunction\x20methodsetHTML(info)\x20{\x0a\x20\x20var\x20html\x20=\x20\"\";\x0a\x20\x20if\x20(info.Methods\x20!=\x20null)\x20{\x0a\x20\x20\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20info.Methods.length;\x20i++)\x20{\x0a\x20\x20\x20\x20\x20\x20html\x20+=\x20\"<code>\"\x20+\x20makeAnchor(info.Methods[i])\x20+\x20\"</code><br/>\\n\";\x0a\x20\x20\x20\x20}\x0a\x20\x20}\x0a\x20\x20return\x20html;\x0a}\x0a\x0a//\x20onClickComm\x20is\x20the\x20onclick\x20action\x20for\x20channel\x20\"make\"\x20and\x20\"<-\"\x0a//\x20send/receive\x20tokens.\x0adocument.onClickComm\x20=\x20function(index)\x20{\x0a\x20\x20var\x20ops\x20=\x20document.ANALYSIS_DATA[index].Ops\x0a\x20\x20if\x20(ops.length\x20==\x201)\x20{\x0a\x20\x20\x20\x20document.location\x20=\x20ops[0].Op.Href;\x20//\x20jump\x20to\x20sole\x20element\x0a\x20\x20\x20\x20return;\x0a\x20\x20}\x0a\x0a\x20\x20var\x20html\x20=\x20\"Operations\x20on\x20this\x20channel:<br/>\\n\";\x0a\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20ops.length;\x20i++)\x20{\x0a\x20\x20\x20\x20html\x20+=\x20makeAnchor(ops[i].Op)\x20+\x20\"\x20by\x20<code>\"\x20+\x20escapeHTML(ops[i].Fn)\x20+\x20\"</code><br/>\\n\";\x0a\x20\x20}\x0a\x20\x20if\x20(ops.length\x20==\x200)\x20{\x0a\x20\x20\x20\x20html\x20+=\x20\"(none)<br/>\\n\";\x0a\x20\x20}\x0a\x20\x20showLowFrame(html);\x0a};\x0a\x0a$(window).load(function()\x20{\x0a\x20\x20\x20\x20//\x20Scroll\x20window\x20so\x20that\x20first\x20selection\x20is\x20visible.\x0a\x20\x20\x20\x20//\x20(This\x20means\x20we\x20don't\x20need\x20to\x20emit\x20id='L%d'\x20spans\x20for\x20each\x20line.)\x0a\x20\x20\x20\x20//\x20TODO(adonovan):\x20ideally,\x20scroll\x20it\x20so\x20that\x20it's\x20under\x20the\x20pointer,\x0a\x20\x20\x20\x20//\x20but\x20I\x20don't\x20know\x20how\x20to\x20get\x20the\x20pointer\x20y\x20coordinate.\x0a\x20\x20\x20\x20var\x20elts\x20=\x20document.getElementsByClassName(\"selection\");\x0a\x20\x20\x20\x20if\x20(elts.length\x20>\x200)\x20{\x0a\x09elts[0].scrollIntoView()\x0a\x20\x20\x20\x20}\x0a});\x0a\x0a//\x20setupTypeInfo\x20populates\x20the\x20\"Implements\"\x20and\x20\"Method\x20set\"\x20toggle\x20for\x0a//\x20each\x20type\x20in\x20the\x20package\x20doc.\x0afunction\x20setupTypeInfo()\x20{\x0a\x20\x20for\x20(var\x20i\x20in\x20document.ANALYSIS_DATA)\x20{\x0a\x20\x20\x20\x20var\x20data\x20=\x20document.ANALYSIS_DATA[i];\x0a\x0a\x20\x20\x20\x20var\x20el\x20=\x20document.getElementById(\"implements-\"\x20+\x20i);\x0a\x20\x20\x20\x20if\x20(el\x20!=\x20null)\x20{\x0a\x20\x20\x20\x20\x20\x20//\x20el\x20!=\x20null\x20=>\x20data\x20is\x20TypeInfoJSON.\x0a\x20\x20\x20\x20\x20\x20if\x20(data.ImplGroups\x20!=\x20null)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20el.innerHTML\x20=\x20implementsHTML(data);\x0a\x20\x20\x20\x20\x20\x20\x20\x20el.parentNode.parentNode.style.display\x20=\x20\"block\";\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20var\x20el\x20=\x20document.getElementById(\"methodset-\"\x20+\x20i);\x0a\x20\x20\x20\x20if\x20(el\x20!=\x20null)\x20{\x0a\x20\x20\x20\x20\x20\x20//\x20el\x20!=\x20null\x20=>\x20data\x20is\x20TypeInfoJSON.\x0a\x20\x20\x20\x20\x20\x20if\x20(data.Methods\x20!=\x20null)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20el.innerHTML\x20=\x20methodsetHTML(data);\x0a\x20\x20\x20\x20\x20\x20\x20\x20el.parentNode.parentNode.style.display\x20=\x20\"block\";\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20}\x0a\x20\x20}\x0a}\x0a\x0afunction\x20setupCallgraphs()\x20{\x0a\x20\x20if\x20(document.CALLGRAPH\x20==\x20null)\x20{\x0a\x20\x20\x20\x20return\x0a\x20\x20}\x0a\x20\x20document.getElementById(\"pkg-callgraph\").style.display\x20=\x20\"block\";\x0a\x0a\x20\x20var\x20treeviews\x20=\x20document.getElementsByClassName(\"treeview\");\x0a\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20treeviews.length;\x20i++)\x20{\x0a\x20\x20\x20\x20var\x20tree\x20=\x20treeviews[i];\x0a\x20\x20\x20\x20if\x20(tree.id\x20==\x20null\x20||\x20tree.id.indexOf(\"callgraph-\")\x20!=\x200)\x20{\x0a\x20\x20\x20\x20\x20\x20continue;\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20var\x20id\x20=\x20tree.id.substring(\"callgraph-\".length);\x0a\x20\x20\x20\x20$(tree).treeview({collapsed:\x20true,\x20animated:\x20\"fast\"});\x0a\x20\x20\x20\x20document.cgAddChildren(tree,\x20tree,\x20[id]);\x0a\x20\x20\x20\x20tree.parentNode.parentNode.style.display\x20=\x20\"block\";\x0a\x20\x20}\x0a}\x0a\x0adocument.cgAddChildren\x20=\x20function(tree,\x20ul,\x20indices)\x20{\x0a\x20\x20if\x20(indices\x20!=\x20null)\x20{\x0a\x20\x20\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20indices.length;\x20i++)\x20{\x0a\x20\x20\x20\x20\x20\x20var\x20li\x20=\x20cgAddChild(tree,\x20ul,\x20document.CALLGRAPH[indices[i]]);\x0a\x20\x20\x20\x20\x20\x20if\x20(i\x20==\x20indices.length\x20-\x201)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20$(li).addClass(\"last\");\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20}\x0a\x20\x20}\x0a\x20\x20$(tree).treeview({animated:\x20\"fast\",\x20add:\x20ul});\x0a}\x0a\x0a//\x20cgAddChild\x20adds\x20an\x20<li>\x20element\x20for\x20document.CALLGRAPH\x20node\x20cgn\x20to\x0a//\x20the\x20parent\x20<ul>\x20element\x20ul.\x20tree\x20is\x20the\x20tree's\x20root\x20<ul>\x20element.\x0afunction\x20cgAddChild(tree,\x20ul,\x20cgn)\x20{\x0a\x20\x20\x20var\x20li\x20=\x20document.createElement(\"li\");\x0a\x20\x20\x20ul.appendChild(li);\x0a\x20\x20\x20li.className\x20=\x20\"closed\";\x0a\x0a\x20\x20\x20var\x20code\x20=\x20document.createElement(\"code\");\x0a\x0a\x20\x20\x20if\x20(cgn.Callees\x20!=\x20null)\x20{\x0a\x20\x20\x20\x20\x20$(li).addClass(\"expandable\");\x0a\x0a\x20\x20\x20\x20\x20//\x20Event\x20handlers\x20and\x20innerHTML\x20updates\x20don't\x20play\x20nicely\x20together,\x0a\x20\x20\x20\x20\x20//\x20hence\x20all\x20this\x20explicit\x20DOM\x20manipulation.\x0a\x20\x20\x20\x20\x20var\x20hitarea\x20=\x20document.createElement(\"div\");\x0a\x20\x20\x20\x20\x20hitarea.className\x20=\x20\"hitarea\x20expandable-hitarea\";\x0a\x20\x20\x20\x20\x20li.appendChild(hitarea);\x0a\x0a\x20\x20\x20\x20\x20li.appendChild(code);\x0a\x0a\x20\x20\x20\x20\x20var\x20childUL\x20=\x20document.createElement(\"ul\");\x0a\x20\x20\x20\x20\x20li.appendChild(childUL);\x0a\x20\x20\x20\x20\x20childUL.setAttribute('style',\x20\"display:\x20none;\");\x0a\x0a\x20\x20\x20\x20\x20var\x20onClick\x20=\x20function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20document.cgAddChildren(tree,\x20childUL,\x20cgn.Callees);\x0a\x20\x20\x20\x20\x20\x20\x20hitarea.removeEventListener('click',\x20onClick)\x0a\x20\x20\x20\x20\x20};\x0a\x20\x20\x20\x20\x20hitarea.addEventListener('click',\x20onClick);\x0a\x0a\x20\x20\x20}\x20else\x20{\x0a\x20\x20\x20\x20\x20li.appendChild(code);\x0a\x20\x20\x20}\x0a\x20\x20\x20code.innerHTML\x20+=\x20\"&nbsp;\"\x20+\x20makeAnchor(cgn.Func);\x0a\x20\x20\x20return\x20li\x0a}\x0a\x0a})();\x0a",
+	"godocs.js": "//\x20Copyright\x202012\x20The\x20Go\x20Authors.\x20All\x20rights\x20reserved.\x0a//\x20Use\x20of\x20this\x20source\x20code\x20is\x20governed\x20by\x20a\x20BSD-style\x0a//\x20license\x20that\x20can\x20be\x20found\x20in\x20the\x20LICENSE\x20file.\x0a\x0a/*\x20A\x20little\x20code\x20to\x20ease\x20navigation\x20of\x20these\x20documents.\x0a\x20*\x0a\x20*\x20On\x20window\x20load\x20we:\x0a\x20*\x20\x20+\x20Generate\x20a\x20table\x20of\x20contents\x20(generateTOC)\x0a\x20*\x20\x20+\x20Bind\x20foldable\x20sections\x20(bindToggles)\x0a\x20*\x20\x20+\x20Bind\x20links\x20to\x20foldable\x20sections\x20(bindToggleLinks)\x0a\x20*/\x0a\x0a(function()\x20{\x0a\x20\x20'use\x20strict';\x0a\x0a\x20\x20//\x20Mobile-friendly\x20topbar\x20menu\x0a\x20\x20$(function()\x20{\x0a\x20\x20\x20\x20var\x20menu\x20=\x20$('#menu');\x0a\x20\x20\x20\x20var\x20menuButton\x20=\x20$('#menu-button');\x0a\x20\x20\x20\x20var\x20menuButtonArrow\x20=\x20$('#menu-button-arrow');\x0a\x20\x20\x20\x20menuButton.click(function(event)\x20{\x0a\x20\x20\x20\x20\x20\x20menu.toggleClass('menu-visible');\x0a\x20\x20\x20\x20\x20\x20menuButtonArrow.toggleClass('vertical-flip');\x0a\x20\x20\x20\x20\x20\x20event.preventDefault();\x0a\x20\x20\x20\x20\x20\x20return\x20false;\x0a\x20\x20\x20\x20});\x0a\x20\x20});\x0a\x0a\x20\x20/*\x20Generates\x20a\x20table\x20of\x20contents:\x20looks\x20for\x20h2\x20and\x20h3\x20elements\x20and\x20generates\x0a\x20\x20\x20*\x20links.\x20\"Decorates\"\x20the\x20element\x20with\x20id==\"nav\"\x20with\x20this\x20table\x20of\x20contents.\x0a\x20\x20\x20*/\x0a\x20\x20function\x20generateTOC()\x20{\x0a\x20\x20\x20\x20if\x20($('#manual-nav').length\x20>\x200)\x20{\x0a\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20//\x20For\x20search,\x20we\x20send\x20the\x20toc\x20precomputed\x20from\x20server-side.\x0a\x20\x20\x20\x20//\x20TODO:\x20Ideally,\x20this\x20should\x20always\x20be\x20precomputed\x20for\x20all\x20pages,\x20but\x20then\x0a\x20\x20\x20\x20//\x20we\x20need\x20to\x20do\x20HTML\x20parsing\x20on\x20the\x20server-side.\x0a\x20\x20\x20\x20if\x20(location.pathname\x20===\x20'/search')\x20{\x0a\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20var\x20nav\x20=\x20$('#nav');\x0a\x20\x20\x20\x20if\x20(nav.length\x20===\x200)\x20{\x0a\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20var\x20toc_items\x20=\x20[];\x0a\x20\x20\x20\x20$(nav)\x0a\x20\x20\x20\x20\x20\x20.nextAll('h2,\x20h3')\x0a\x20\x20\x20\x20\x20\x20.each(function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20var\x20node\x20=\x20this;\x0a\x20\x20\x20\x20\x20\x20\x20\x20if\x20(node.id\x20==\x20'')\x20node.id\x20=\x20'tmp_'\x20+\x20toc_items.length;\x0a\x20\x20\x20\x20\x20\x20\x20\x20var\x20link\x20=\x20$('<a/>')\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20.attr('href',\x20'#'\x20+\x20node.id)\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20.text($(node).text());\x0a\x20\x20\x20\x20\x20\x20\x20\x20var\x20item;\x0a\x20\x20\x20\x20\x20\x20\x20\x20if\x20($(node).is('h2'))\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20item\x20=\x20$('<dt/>');\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x20else\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20//\x20h3\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20item\x20=\x20$('<dd\x20class=\"indent\"/>');\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20item.append(link);\x0a\x20\x20\x20\x20\x20\x20\x20\x20toc_items.push(item);\x0a\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20if\x20(toc_items.length\x20<=\x201)\x20{\x0a\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20var\x20dl1\x20=\x20$('<dl/>');\x0a\x20\x20\x20\x20var\x20dl2\x20=\x20$('<dl/>');\x0a\x0a\x20\x20\x20\x20var\x20split_index\x20=\x20toc_items.length\x20/\x202\x20+\x201;\x0a\x20\x20\x20\x20if\x20(split_index\x20<\x208)\x20{\x0a\x20\x20\x20\x20\x20\x20split_index\x20=\x20toc_items.length;\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20split_index;\x20i++)\x20{\x0a\x20\x20\x20\x20\x20\x20dl1.append(toc_items[i]);\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20for\x20(;\x20/*\x20keep\x20using\x20i\x20*/\x20i\x20<\x20toc_items.length;\x20i++)\x20{\x0a\x20\x20\x20\x20\x20\x20dl2.append(toc_items[i]);\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20var\x20tocTable\x20=\x20$('<table\x20class=\"unruled\"/>').appendTo(nav);\x0a\x20\x20\x20\x20var\x20tocBody\x20=\x20$('<tbody/>').appendTo(tocTable);\x0a\x20\x20\x20\x20var\x20tocRow\x20=\x20$('<tr/>').appendTo(tocBody);\x0a\x0a\x20\x20\x20\x20//\x201st\x20column\x0a\x20\x20\x20\x20$('<td\x20class=\"first\"/>')\x0a\x20\x20\x20\x20\x20\x20.appendTo(tocRow)\x0a\x20\x20\x20\x20\x20\x20.append(dl1);\x0a\x20\x20\x20\x20//\x202nd\x20column\x0a\x20\x20\x20\x20$('<td/>')\x0a\x20\x20\x20\x20\x20\x20.appendTo(tocRow)\x0a\x20\x20\x20\x20\x20\x20.append(dl2);\x0a\x20\x20}\x0a\x0a\x20\x20function\x20bindToggle(el)\x20{\x0a\x20\x20\x20\x20$('.toggleButton',\x20el).click(function()\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20($(this).closest('.toggle,\x20.toggleVisible')[0]\x20!=\x20el)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20//\x20Only\x20trigger\x20the\x20closest\x20toggle\x20header.\x0a\x20\x20\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20\x20\x20if\x20($(el).is('.toggle'))\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20$(el)\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20.addClass('toggleVisible')\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20.removeClass('toggle');\x0a\x20\x20\x20\x20\x20\x20}\x20else\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20$(el)\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20.addClass('toggle')\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20.removeClass('toggleVisible');\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20});\x0a\x20\x20}\x0a\x0a\x20\x20function\x20bindToggles(selector)\x20{\x0a\x20\x20\x20\x20$(selector).each(function(i,\x20el)\x20{\x0a\x20\x20\x20\x20\x20\x20bindToggle(el);\x0a\x20\x20\x20\x20});\x0a\x20\x20}\x0a\x0a\x20\x20function\x20bindToggleLink(el,\x20prefix)\x20{\x0a\x20\x20\x20\x20$(el).click(function()\x20{\x0a\x20\x20\x20\x20\x20\x20var\x20href\x20=\x20$(el).attr('href');\x0a\x20\x20\x20\x20\x20\x20var\x20i\x20=\x20href.indexOf('#'\x20+\x20prefix);\x0a\x20\x20\x20\x20\x20\x20if\x20(i\x20<\x200)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20var\x20id\x20=\x20'#'\x20+\x20prefix\x20+\x20href.slice(i\x20+\x201\x20+\x20prefix.length);\x0a\x20\x20\x20\x20\x20\x20if\x20($(id).is('.toggle'))\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20$(id)\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20.find('.toggleButton')\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20.first()\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20.click();\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20});\x0a\x20\x20}\x0a\x20\x20function\x20bindToggleLinks(selector,\x20prefix)\x20{\x0a\x20\x20\x20\x20$(selector).each(function(i,\x20el)\x20{\x0a\x20\x20\x20\x20\x20\x20bindToggleLink(el,\x20prefix);\x0a\x20\x20\x20\x20});\x0a\x20\x20}\x0a\x0a\x20\x20function\x20setupDropdownPlayground()\x20{\x0a\x20\x20\x20\x20if\x20(!$('#page').is('.wide'))\x20{\x0a\x20\x20\x20\x20\x20\x20return;\x20//\x20don't\x20show\x20on\x20front\x20page\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20var\x20button\x20=\x20$('#playgroundButton');\x0a\x20\x20\x20\x20var\x20div\x20=\x20$('#playground');\x0a\x20\x20\x20\x20var\x20setup\x20=\x20false;\x0a\x20\x20\x20\x20button.toggle(\x0a\x20\x20\x20\x20\x20\x20function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20button.addClass('active');\x0a\x20\x20\x20\x20\x20\x20\x20\x20div.show();\x0a\x20\x20\x20\x20\x20\x20\x20\x20if\x20(setup)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20setup\x20=\x20true;\x0a\x20\x20\x20\x20\x20\x20\x20\x20playground({\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20codeEl:\x20$('.code',\x20div),\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20outputEl:\x20$('.output',\x20div),\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20runEl:\x20$('.run',\x20div),\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20fmtEl:\x20$('.fmt',\x20div),\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20shareEl:\x20$('.share',\x20div),\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20shareRedirect:\x20'//play.golang.org/p/',\x0a\x20\x20\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20\x20\x20},\x0a\x20\x20\x20\x20\x20\x20function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20button.removeClass('active');\x0a\x20\x20\x20\x20\x20\x20\x20\x20div.hide();\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20);\x0a\x20\x20\x20\x20$('#menu').css('min-width',\x20'+=60');\x0a\x0a\x20\x20\x20\x20//\x20Hide\x20inline\x20playground\x20if\x20we\x20click\x20somewhere\x20on\x20the\x20page.\x0a\x20\x20\x20\x20//\x20This\x20is\x20needed\x20in\x20mobile\x20devices,\x20where\x20the\x20\"Play\"\x20button\x0a\x20\x20\x20\x20//\x20is\x20not\x20clickable\x20once\x20the\x20playground\x20opens\x20up.\x0a\x20\x20\x20\x20$('#page').click(function()\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(button.hasClass('active'))\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20button.click();\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20});\x0a\x20\x20}\x0a\x0a\x20\x20function\x20setupInlinePlayground()\x20{\x0a\x20\x20\x20\x20'use\x20strict';\x0a\x20\x20\x20\x20//\x20Set\x20up\x20playground\x20when\x20each\x20element\x20is\x20toggled.\x0a\x20\x20\x20\x20$('div.play').each(function(i,\x20el)\x20{\x0a\x20\x20\x20\x20\x20\x20//\x20Set\x20up\x20playground\x20for\x20this\x20example.\x0a\x20\x20\x20\x20\x20\x20var\x20setup\x20=\x20function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20var\x20code\x20=\x20$('.code',\x20el);\x0a\x20\x20\x20\x20\x20\x20\x20\x20playground({\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20codeEl:\x20code,\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20outputEl:\x20$('.output',\x20el),\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20runEl:\x20$('.run',\x20el),\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20fmtEl:\x20$('.fmt',\x20el),\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20shareEl:\x20$('.share',\x20el),\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20shareRedirect:\x20'//play.golang.org/p/',\x0a\x20\x20\x20\x20\x20\x20\x20\x20});\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20//\x20Make\x20the\x20code\x20textarea\x20resize\x20to\x20fit\x20content.\x0a\x20\x20\x20\x20\x20\x20\x20\x20var\x20resize\x20=\x20function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20code.height(0);\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20var\x20h\x20=\x20code[0].scrollHeight;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20code.height(h\x20+\x2020);\x20//\x20minimize\x20bouncing.\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20code.closest('.input').height(h);\x0a\x20\x20\x20\x20\x20\x20\x20\x20};\x0a\x20\x20\x20\x20\x20\x20\x20\x20code.on('keydown',\x20resize);\x0a\x20\x20\x20\x20\x20\x20\x20\x20code.on('keyup',\x20resize);\x0a\x20\x20\x20\x20\x20\x20\x20\x20code.keyup();\x20//\x20resize\x20now.\x0a\x20\x20\x20\x20\x20\x20};\x0a\x0a\x20\x20\x20\x20\x20\x20//\x20If\x20example\x20already\x20visible,\x20set\x20up\x20playground\x20now.\x0a\x20\x20\x20\x20\x20\x20if\x20($(el).is(':visible'))\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20setup();\x0a\x20\x20\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20\x20\x20//\x20Otherwise,\x20set\x20up\x20playground\x20when\x20example\x20is\x20expanded.\x0a\x20\x20\x20\x20\x20\x20var\x20built\x20=\x20false;\x0a\x20\x20\x20\x20\x20\x20$(el)\x0a\x20\x20\x20\x20\x20\x20\x20\x20.closest('.toggle')\x0a\x20\x20\x20\x20\x20\x20\x20\x20.click(function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20//\x20Only\x20set\x20up\x20once.\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(!built)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20setup();\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20built\x20=\x20true;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20});\x0a\x20\x20}\x0a\x0a\x20\x20//\x20fixFocus\x20tries\x20to\x20put\x20focus\x20to\x20div#page\x20so\x20that\x20keyboard\x20navigation\x20works.\x0a\x20\x20function\x20fixFocus()\x20{\x0a\x20\x20\x20\x20var\x20page\x20=\x20$('div#page');\x0a\x20\x20\x20\x20var\x20topbar\x20=\x20$('div#topbar');\x0a\x20\x20\x20\x20page.css('outline',\x200);\x20//\x20disable\x20outline\x20when\x20focused\x0a\x20\x20\x20\x20page.attr('tabindex',\x20-1);\x20//\x20and\x20set\x20tabindex\x20so\x20that\x20it\x20is\x20focusable\x0a\x20\x20\x20\x20$(window)\x0a\x20\x20\x20\x20\x20\x20.resize(function(evt)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20//\x20only\x20focus\x20page\x20when\x20the\x20topbar\x20is\x20at\x20fixed\x20position\x20(that\x20is,\x20it's\x20in\x0a\x20\x20\x20\x20\x20\x20\x20\x20//\x20front\x20of\x20page,\x20and\x20keyboard\x20event\x20will\x20go\x20to\x20the\x20former\x20by\x20default.)\x0a\x20\x20\x20\x20\x20\x20\x20\x20//\x20by\x20focusing\x20page,\x20keyboard\x20event\x20will\x20go\x20to\x20page\x20so\x20that\x20up/down\x20arrow,\x0a\x20\x20\x20\x20\x20\x20\x20\x20//\x20space,\x20etc.\x20will\x20work\x20as\x20expected.\x0a\x20\x20\x20\x20\x20\x20\x20\x20if\x20(topbar.css('position')\x20==\x20'fixed')\x20page.focus();\x0a\x20\x20\x20\x20\x20\x20})\x0a\x20\x20\x20\x20\x20\x20.resize();\x0a\x20\x20}\x0a\x0a\x20\x20function\x20toggleHash()\x20{\x0a\x20\x20\x20\x20var\x20id\x20=\x20window.location.hash.substring(1);\x0a\x20\x20\x20\x20//\x20Open\x20all\x20of\x20the\x20toggles\x20for\x20a\x20particular\x20hash.\x0a\x20\x20\x20\x20var\x20els\x20=\x20$(\x0a\x20\x20\x20\x20\x20\x20document.getElementById(id),\x0a\x20\x20\x20\x20\x20\x20$('a[name]').filter(function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20return\x20$(this).attr('name')\x20==\x20id;\x0a\x20\x20\x20\x20\x20\x20})\x0a\x20\x20\x20\x20);\x0a\x0a\x20\x20\x20\x20while\x20(els.length)\x20{\x0a\x20\x20\x20\x20\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20els.length;\x20i++)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20var\x20el\x20=\x20$(els[i]);\x0a\x20\x20\x20\x20\x20\x20\x20\x20if\x20(el.is('.toggle'))\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20el.find('.toggleButton')\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20.first()\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20.click();\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20els\x20=\x20el.parent();\x0a\x20\x20\x20\x20}\x0a\x20\x20}\x0a\x0a\x20\x20function\x20personalizeInstallInstructions()\x20{\x0a\x20\x20\x20\x20var\x20prefix\x20=\x20'?download=';\x0a\x20\x20\x20\x20var\x20s\x20=\x20window.location.search;\x0a\x20\x20\x20\x20if\x20(s.indexOf(prefix)\x20!=\x200)\x20{\x0a\x20\x20\x20\x20\x20\x20//\x20No\x20'download'\x20query\x20string;\x20detect\x20\"test\"\x20instructions\x20from\x20User\x20Agent.\x0a\x20\x20\x20\x20\x20\x20if\x20(navigator.platform.indexOf('Win')\x20!=\x20-1)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20$('.testUnix').hide();\x0a\x20\x20\x20\x20\x20\x20\x20\x20$('.testWindows').show();\x0a\x20\x20\x20\x20\x20\x20}\x20else\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20$('.testUnix').show();\x0a\x20\x20\x20\x20\x20\x20\x20\x20$('.testWindows').hide();\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20var\x20filename\x20=\x20s.substr(prefix.length);\x0a\x20\x20\x20\x20var\x20filenameRE\x20=\x20/^go1\\.\\d+(\\.\\d+)?([a-z0-9]+)?\\.([a-z0-9]+)(-[a-z0-9]+)?(-osx10\\.[68])?\\.([a-z.]+)$/;\x0a\x20\x20\x20\x20var\x20m\x20=\x20filenameRE.exec(filename);\x0a\x20\x20\x20\x20if\x20(!m)\x20{\x0a\x20\x20\x20\x20\x20\x20//\x20Can't\x20interpret\x20file\x20name;\x20bail.\x0a\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20$('.downloadFilename').text(filename);\x0a\x20\x20\x20\x20$('.hideFromDownload').hide();\x0a\x0a\x20\x20\x20\x20var\x20os\x20=\x20m[3];\x0a\x20\x20\x20\x20var\x20ext\x20=\x20m[6];\x0a\x20\x20\x20\x20if\x20(ext\x20!=\x20'tar.gz')\x20{\x0a\x20\x20\x20\x20\x20\x20$('#tarballInstructions').hide();\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20if\x20(os\x20!=\x20'darwin'\x20||\x20ext\x20!=\x20'pkg')\x20{\x0a\x20\x20\x20\x20\x20\x20$('#darwinPackageInstructions').hide();\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20if\x20(os\x20!=\x20'windows')\x20{\x0a\x20\x20\x20\x20\x20\x20$('#windowsInstructions').hide();\x0a\x20\x20\x20\x20\x20\x20$('.testUnix').show();\x0a\x20\x20\x20\x20\x20\x20$('.testWindows').hide();\x0a\x20\x20\x20\x20}\x20else\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(ext\x20!=\x20'msi')\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20$('#windowsInstallerInstructions').hide();\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20if\x20(ext\x20!=\x20'zip')\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20$('#windowsZipInstructions').hide();\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20$('.testUnix').hide();\x0a\x20\x20\x20\x20\x20\x20$('.testWindows').show();\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20var\x20download\x20=\x20'https://dl.google.com/go/'\x20+\x20filename;\x0a\x0a\x20\x20\x20\x20var\x20message\x20=\x20$(\x0a\x20\x20\x20\x20\x20\x20'<p\x20class=\"downloading\">'\x20+\x0a\x20\x20\x20\x20\x20\x20\x20\x20'Your\x20download\x20should\x20begin\x20shortly.\x20'\x20+\x0a\x20\x20\x20\x20\x20\x20\x20\x20'If\x20it\x20does\x20not,\x20click\x20<a>this\x20link</a>.</p>'\x0a\x20\x20\x20\x20);\x0a\x20\x20\x20\x20message.find('a').attr('href',\x20download);\x0a\x20\x20\x20\x20message.insertAfter('#nav');\x0a\x0a\x20\x20\x20\x20window.location\x20=\x20download;\x0a\x20\x20}\x0a\x0a\x20\x20function\x20updateVersionTags()\x20{\x0a\x20\x20\x20\x20var\x20v\x20=\x20window.goVersion;\x0a\x20\x20\x20\x20if\x20(/^go[0-9.]+$/.test(v))\x20{\x0a\x20\x20\x20\x20\x20\x20$('.versionTag')\x0a\x20\x20\x20\x20\x20\x20\x20\x20.empty()\x0a\x20\x20\x20\x20\x20\x20\x20\x20.text(v);\x0a\x20\x20\x20\x20\x20\x20$('.whereTag').hide();\x0a\x20\x20\x20\x20}\x0a\x20\x20}\x0a\x0a\x20\x20function\x20addPermalinks()\x20{\x0a\x20\x20\x20\x20function\x20addPermalink(source,\x20parent)\x20{\x0a\x20\x20\x20\x20\x20\x20var\x20id\x20=\x20source.attr('id');\x0a\x20\x20\x20\x20\x20\x20if\x20(id\x20==\x20''\x20||\x20id.indexOf('tmp_')\x20===\x200)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20//\x20Auto-generated\x20permalink.\x0a\x20\x20\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20if\x20(parent.find('>\x20.permalink').length)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20//\x20Already\x20attached.\x0a\x20\x20\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20parent\x0a\x20\x20\x20\x20\x20\x20\x20\x20.append('\x20')\x0a\x20\x20\x20\x20\x20\x20\x20\x20.append($(\"<a\x20class='permalink'>&#xb6;</a>\").attr('href',\x20'#'\x20+\x20id));\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20$('#page\x20.container')\x0a\x20\x20\x20\x20\x20\x20.find('h2[id],\x20h3[id]')\x0a\x20\x20\x20\x20\x20\x20.each(function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20var\x20el\x20=\x20$(this);\x0a\x20\x20\x20\x20\x20\x20\x20\x20addPermalink(el,\x20el);\x0a\x20\x20\x20\x20\x20\x20});\x0a\x0a\x20\x20\x20\x20$('#page\x20.container')\x0a\x20\x20\x20\x20\x20\x20.find('dl[id]')\x0a\x20\x20\x20\x20\x20\x20.each(function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20var\x20el\x20=\x20$(this);\x0a\x20\x20\x20\x20\x20\x20\x20\x20//\x20Add\x20the\x20anchor\x20to\x20the\x20\"dt\"\x20element.\x0a\x20\x20\x20\x20\x20\x20\x20\x20addPermalink(el,\x20el.find('>\x20dt').first());\x0a\x20\x20\x20\x20\x20\x20});\x0a\x20\x20}\x0a\x0a\x20\x20$('.js-expandAll').click(function()\x20{\x0a\x20\x20\x20\x20if\x20($(this).hasClass('collapsed'))\x20{\x0a\x20\x20\x20\x20\x20\x20toggleExamples('toggle');\x0a\x20\x20\x20\x20\x20\x20$(this).text('(Collapse\x20All)');\x0a\x20\x20\x20\x20}\x20else\x20{\x0a\x20\x20\x20\x20\x20\x20toggleExamples('toggleVisible');\x0a\x20\x20\x20\x20\x20\x20$(this).text('(Expand\x20All)');\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20$(this).toggleClass('collapsed');\x0a\x20\x20});\x0a\x0a\x20\x20function\x20toggleExamples(className)\x20{\x0a\x20\x20\x20\x20//\x20We\x20need\x20to\x20explicitly\x20iterate\x20through\x20divs\x20starting\x20with\x20\"example_\"\x0a\x20\x20\x20\x20//\x20to\x20avoid\x20toggling\x20Overview\x20and\x20Index\x20collapsibles.\x0a\x20\x20\x20\x20$(\"[id^='example_']\").each(function()\x20{\x0a\x20\x20\x20\x20\x20\x20//\x20Check\x20for\x20state\x20and\x20click\x20it\x20only\x20if\x20required.\x0a\x20\x20\x20\x20\x20\x20if\x20($(this).hasClass(className))\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20$(this)\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20.find('.toggleButton')\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20.first()\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20.click();\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20});\x0a\x20\x20}\x0a\x0a\x20\x20$(document).ready(function()\x20{\x0a\x20\x20\x20\x20generateTOC();\x0a\x20\x20\x20\x20addPermalinks();\x0a\x20\x20\x20\x20bindToggles('.toggle');\x0a\x20\x20\x20\x20bindToggles('.toggleVisible');\x0a\x20\x20\x20\x20bindToggleLinks('.exampleLink',\x20'example_');\x0a\x20\x20\x20\x20bindToggleLinks('.overviewLink',\x20'');\x0a\x20\x20\x20\x20bindToggleLinks('.examplesLink',\x20'');\x0a\x20\x20\x20\x20bindToggleLinks('.indexLink',\x20'');\x0a\x20\x20\x20\x20setupDropdownPlayground();\x0a\x20\x20\x20\x20setupInlinePlayground();\x0a\x20\x20\x20\x20fixFocus();\x0a\x20\x20\x20\x20setupTypeInfo();\x0a\x20\x20\x20\x20setupCallgraphs();\x0a\x20\x20\x20\x20toggleHash();\x0a\x20\x20\x20\x20personalizeInstallInstructions();\x0a\x20\x20\x20\x20updateVersionTags();\x0a\x0a\x20\x20\x20\x20//\x20godoc.html\x20defines\x20window.initFuncs\x20in\x20the\x20<head>\x20tag,\x20and\x20root.html\x20and\x0a\x20\x20\x20\x20//\x20codewalk.js\x20push\x20their\x20on-page-ready\x20functions\x20to\x20the\x20list.\x0a\x20\x20\x20\x20//\x20We\x20execute\x20those\x20functions\x20here,\x20to\x20avoid\x20loading\x20jQuery\x20until\x20the\x20page\x0a\x20\x20\x20\x20//\x20content\x20is\x20loaded.\x0a\x20\x20\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20window.initFuncs.length;\x20i++)\x20window.initFuncs[i]();\x0a\x20\x20});\x0a\x0a\x20\x20//\x20--\x20analysis\x20---------------------------------------------------------\x0a\x0a\x20\x20//\x20escapeHTML\x20returns\x20HTML\x20for\x20s,\x20with\x20metacharacters\x20quoted.\x0a\x20\x20//\x20It\x20is\x20safe\x20for\x20use\x20in\x20both\x20elements\x20and\x20attributes\x0a\x20\x20//\x20(unlike\x20the\x20\"set\x20innerText,\x20read\x20innerHTML\"\x20trick).\x0a\x20\x20function\x20escapeHTML(s)\x20{\x0a\x20\x20\x20\x20return\x20s\x0a\x20\x20\x20\x20\x20\x20.replace(/&/g,\x20'&amp;')\x0a\x20\x20\x20\x20\x20\x20.replace(/\\\"/g,\x20'&quot;')\x0a\x20\x20\x20\x20\x20\x20.replace(/\\'/g,\x20'&#39;')\x0a\x20\x20\x20\x20\x20\x20.replace(/</g,\x20'&lt;')\x0a\x20\x20\x20\x20\x20\x20.replace(/>/g,\x20'&gt;');\x0a\x20\x20}\x0a\x0a\x20\x20//\x20makeAnchor\x20returns\x20HTML\x20for\x20an\x20<a>\x20element,\x20given\x20an\x20anchorJSON\x20object.\x0a\x20\x20function\x20makeAnchor(json)\x20{\x0a\x20\x20\x20\x20var\x20html\x20=\x20escapeHTML(json.Text);\x0a\x20\x20\x20\x20if\x20(json.Href\x20!=\x20'')\x20{\x0a\x20\x20\x20\x20\x20\x20html\x20=\x20\"<a\x20href='\"\x20+\x20escapeHTML(json.Href)\x20+\x20\"'>\"\x20+\x20html\x20+\x20'</a>';\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20return\x20html;\x0a\x20\x20}\x0a\x0a\x20\x20function\x20showLowFrame(html)\x20{\x0a\x20\x20\x20\x20var\x20lowframe\x20=\x20document.getElementById('lowframe');\x0a\x20\x20\x20\x20lowframe.style.height\x20=\x20'200px';\x0a\x20\x20\x20\x20lowframe.innerHTML\x20=\x0a\x20\x20\x20\x20\x20\x20\"<p\x20style='text-align:\x20left;'>\"\x20+\x0a\x20\x20\x20\x20\x20\x20html\x20+\x0a\x20\x20\x20\x20\x20\x20'</p>\\n'\x20+\x0a\x20\x20\x20\x20\x20\x20\"<div\x20onclick='hideLowFrame()'\x20style='position:\x20absolute;\x20top:\x200;\x20right:\x200;\x20cursor:\x20pointer;'>\xe2\x9c\x98</div>\";\x0a\x20\x20}\x0a\x0a\x20\x20document.hideLowFrame\x20=\x20function()\x20{\x0a\x20\x20\x20\x20var\x20lowframe\x20=\x20document.getElementById('lowframe');\x0a\x20\x20\x20\x20lowframe.style.height\x20=\x20'0px';\x0a\x20\x20};\x0a\x0a\x20\x20//\x20onClickCallers\x20is\x20the\x20onclick\x20action\x20for\x20the\x20'func'\x20tokens\x20of\x20a\x0a\x20\x20//\x20function\x20declaration.\x0a\x20\x20document.onClickCallers\x20=\x20function(index)\x20{\x0a\x20\x20\x20\x20var\x20data\x20=\x20document.ANALYSIS_DATA[index];\x0a\x20\x20\x20\x20if\x20(data.Callers.length\x20==\x201\x20&&\x20data.Callers[0].Sites.length\x20==\x201)\x20{\x0a\x20\x20\x20\x20\x20\x20document.location\x20=\x20data.Callers[0].Sites[0].Href;\x20//\x20jump\x20to\x20sole\x20caller\x0a\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20var\x20html\x20=\x0a\x20\x20\x20\x20\x20\x20'Callers\x20of\x20<code>'\x20+\x20escapeHTML(data.Callee)\x20+\x20'</code>:<br/>\\n';\x0a\x20\x20\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20data.Callers.length;\x20i++)\x20{\x0a\x20\x20\x20\x20\x20\x20var\x20caller\x20=\x20data.Callers[i];\x0a\x20\x20\x20\x20\x20\x20html\x20+=\x20'<code>'\x20+\x20escapeHTML(caller.Func)\x20+\x20'</code>';\x0a\x20\x20\x20\x20\x20\x20var\x20sites\x20=\x20caller.Sites;\x0a\x20\x20\x20\x20\x20\x20if\x20(sites\x20!=\x20null\x20&&\x20sites.length\x20>\x200)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20html\x20+=\x20'\x20at\x20line\x20';\x0a\x20\x20\x20\x20\x20\x20\x20\x20for\x20(var\x20j\x20=\x200;\x20j\x20<\x20sites.length;\x20j++)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(j\x20>\x200)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20html\x20+=\x20',\x20';\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20html\x20+=\x20'<code>'\x20+\x20makeAnchor(sites[j])\x20+\x20'</code>';\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20html\x20+=\x20'<br/>\\n';\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20showLowFrame(html);\x0a\x20\x20};\x0a\x0a\x20\x20//\x20onClickCallees\x20is\x20the\x20onclick\x20action\x20for\x20the\x20'('\x20token\x20of\x20a\x20function\x20call.\x0a\x20\x20document.onClickCallees\x20=\x20function(index)\x20{\x0a\x20\x20\x20\x20var\x20data\x20=\x20document.ANALYSIS_DATA[index];\x0a\x20\x20\x20\x20if\x20(data.Callees.length\x20==\x201)\x20{\x0a\x20\x20\x20\x20\x20\x20document.location\x20=\x20data.Callees[0].Href;\x20//\x20jump\x20to\x20sole\x20callee\x0a\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20var\x20html\x20=\x20'Callees\x20of\x20this\x20'\x20+\x20escapeHTML(data.Descr)\x20+\x20':<br/>\\n';\x0a\x20\x20\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20data.Callees.length;\x20i++)\x20{\x0a\x20\x20\x20\x20\x20\x20html\x20+=\x20'<code>'\x20+\x20makeAnchor(data.Callees[i])\x20+\x20'</code><br/>\\n';\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20showLowFrame(html);\x0a\x20\x20};\x0a\x0a\x20\x20//\x20onClickTypeInfo\x20is\x20the\x20onclick\x20action\x20for\x20identifiers\x20declaring\x20a\x20named\x20type.\x0a\x20\x20document.onClickTypeInfo\x20=\x20function(index)\x20{\x0a\x20\x20\x20\x20var\x20data\x20=\x20document.ANALYSIS_DATA[index];\x0a\x20\x20\x20\x20var\x20html\x20=\x0a\x20\x20\x20\x20\x20\x20'Type\x20<code>'\x20+\x0a\x20\x20\x20\x20\x20\x20data.Name\x20+\x0a\x20\x20\x20\x20\x20\x20'</code>:\x20'\x20+\x0a\x20\x20\x20\x20\x20\x20'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<small>(size='\x20+\x0a\x20\x20\x20\x20\x20\x20data.Size\x20+\x0a\x20\x20\x20\x20\x20\x20',\x20align='\x20+\x0a\x20\x20\x20\x20\x20\x20data.Align\x20+\x0a\x20\x20\x20\x20\x20\x20')</small><br/>\\n';\x0a\x20\x20\x20\x20html\x20+=\x20implementsHTML(data);\x0a\x20\x20\x20\x20html\x20+=\x20methodsetHTML(data);\x0a\x20\x20\x20\x20showLowFrame(html);\x0a\x20\x20};\x0a\x0a\x20\x20//\x20implementsHTML\x20returns\x20HTML\x20for\x20the\x20implements\x20relation\x20of\x20the\x0a\x20\x20//\x20specified\x20TypeInfoJSON\x20value.\x0a\x20\x20function\x20implementsHTML(info)\x20{\x0a\x20\x20\x20\x20var\x20html\x20=\x20'';\x0a\x20\x20\x20\x20if\x20(info.ImplGroups\x20!=\x20null)\x20{\x0a\x20\x20\x20\x20\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20info.ImplGroups.length;\x20i++)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20var\x20group\x20=\x20info.ImplGroups[i];\x0a\x20\x20\x20\x20\x20\x20\x20\x20var\x20x\x20=\x20'<code>'\x20+\x20escapeHTML(group.Descr)\x20+\x20'</code>\x20';\x0a\x20\x20\x20\x20\x20\x20\x20\x20for\x20(var\x20j\x20=\x200;\x20j\x20<\x20group.Facts.length;\x20j++)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20var\x20fact\x20=\x20group.Facts[j];\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20var\x20y\x20=\x20'<code>'\x20+\x20makeAnchor(fact.Other)\x20+\x20'</code>';\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(fact.ByKind\x20!=\x20null)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20html\x20+=\x20escapeHTML(fact.ByKind)\x20+\x20'\x20type\x20'\x20+\x20y\x20+\x20'\x20implements\x20'\x20+\x20x;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x20else\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20html\x20+=\x20x\x20+\x20'\x20implements\x20'\x20+\x20y;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20html\x20+=\x20'<br/>\\n';\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20return\x20html;\x0a\x20\x20}\x0a\x0a\x20\x20//\x20methodsetHTML\x20returns\x20HTML\x20for\x20the\x20methodset\x20of\x20the\x20specified\x0a\x20\x20//\x20TypeInfoJSON\x20value.\x0a\x20\x20function\x20methodsetHTML(info)\x20{\x0a\x20\x20\x20\x20var\x20html\x20=\x20'';\x0a\x20\x20\x20\x20if\x20(info.Methods\x20!=\x20null)\x20{\x0a\x20\x20\x20\x20\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20info.Methods.length;\x20i++)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20html\x20+=\x20'<code>'\x20+\x20makeAnchor(info.Methods[i])\x20+\x20'</code><br/>\\n';\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20return\x20html;\x0a\x20\x20}\x0a\x0a\x20\x20//\x20onClickComm\x20is\x20the\x20onclick\x20action\x20for\x20channel\x20\"make\"\x20and\x20\"<-\"\x0a\x20\x20//\x20send/receive\x20tokens.\x0a\x20\x20document.onClickComm\x20=\x20function(index)\x20{\x0a\x20\x20\x20\x20var\x20ops\x20=\x20document.ANALYSIS_DATA[index].Ops;\x0a\x20\x20\x20\x20if\x20(ops.length\x20==\x201)\x20{\x0a\x20\x20\x20\x20\x20\x20document.location\x20=\x20ops[0].Op.Href;\x20//\x20jump\x20to\x20sole\x20element\x0a\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20var\x20html\x20=\x20'Operations\x20on\x20this\x20channel:<br/>\\n';\x0a\x20\x20\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20ops.length;\x20i++)\x20{\x0a\x20\x20\x20\x20\x20\x20html\x20+=\x0a\x20\x20\x20\x20\x20\x20\x20\x20makeAnchor(ops[i].Op)\x20+\x0a\x20\x20\x20\x20\x20\x20\x20\x20'\x20by\x20<code>'\x20+\x0a\x20\x20\x20\x20\x20\x20\x20\x20escapeHTML(ops[i].Fn)\x20+\x0a\x20\x20\x20\x20\x20\x20\x20\x20'</code><br/>\\n';\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20if\x20(ops.length\x20==\x200)\x20{\x0a\x20\x20\x20\x20\x20\x20html\x20+=\x20'(none)<br/>\\n';\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20showLowFrame(html);\x0a\x20\x20};\x0a\x0a\x20\x20$(window).load(function()\x20{\x0a\x20\x20\x20\x20//\x20Scroll\x20window\x20so\x20that\x20first\x20selection\x20is\x20visible.\x0a\x20\x20\x20\x20//\x20(This\x20means\x20we\x20don't\x20need\x20to\x20emit\x20id='L%d'\x20spans\x20for\x20each\x20line.)\x0a\x20\x20\x20\x20//\x20TODO(adonovan):\x20ideally,\x20scroll\x20it\x20so\x20that\x20it's\x20under\x20the\x20pointer,\x0a\x20\x20\x20\x20//\x20but\x20I\x20don't\x20know\x20how\x20to\x20get\x20the\x20pointer\x20y\x20coordinate.\x0a\x20\x20\x20\x20var\x20elts\x20=\x20document.getElementsByClassName('selection');\x0a\x20\x20\x20\x20if\x20(elts.length\x20>\x200)\x20{\x0a\x20\x20\x20\x20\x20\x20elts[0].scrollIntoView();\x0a\x20\x20\x20\x20}\x0a\x20\x20});\x0a\x0a\x20\x20//\x20setupTypeInfo\x20populates\x20the\x20\"Implements\"\x20and\x20\"Method\x20set\"\x20toggle\x20for\x0a\x20\x20//\x20each\x20type\x20in\x20the\x20package\x20doc.\x0a\x20\x20function\x20setupTypeInfo()\x20{\x0a\x20\x20\x20\x20for\x20(var\x20i\x20in\x20document.ANALYSIS_DATA)\x20{\x0a\x20\x20\x20\x20\x20\x20var\x20data\x20=\x20document.ANALYSIS_DATA[i];\x0a\x0a\x20\x20\x20\x20\x20\x20var\x20el\x20=\x20document.getElementById('implements-'\x20+\x20i);\x0a\x20\x20\x20\x20\x20\x20if\x20(el\x20!=\x20null)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20//\x20el\x20!=\x20null\x20=>\x20data\x20is\x20TypeInfoJSON.\x0a\x20\x20\x20\x20\x20\x20\x20\x20if\x20(data.ImplGroups\x20!=\x20null)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20el.innerHTML\x20=\x20implementsHTML(data);\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20el.parentNode.parentNode.style.display\x20=\x20'block';\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20\x20\x20var\x20el\x20=\x20document.getElementById('methodset-'\x20+\x20i);\x0a\x20\x20\x20\x20\x20\x20if\x20(el\x20!=\x20null)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20//\x20el\x20!=\x20null\x20=>\x20data\x20is\x20TypeInfoJSON.\x0a\x20\x20\x20\x20\x20\x20\x20\x20if\x20(data.Methods\x20!=\x20null)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20el.innerHTML\x20=\x20methodsetHTML(data);\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20el.parentNode.parentNode.style.display\x20=\x20'block';\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20}\x0a\x20\x20}\x0a\x0a\x20\x20function\x20setupCallgraphs()\x20{\x0a\x20\x20\x20\x20if\x20(document.CALLGRAPH\x20==\x20null)\x20{\x0a\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20document.getElementById('pkg-callgraph').style.display\x20=\x20'block';\x0a\x0a\x20\x20\x20\x20var\x20treeviews\x20=\x20document.getElementsByClassName('treeview');\x0a\x20\x20\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20treeviews.length;\x20i++)\x20{\x0a\x20\x20\x20\x20\x20\x20var\x20tree\x20=\x20treeviews[i];\x0a\x20\x20\x20\x20\x20\x20if\x20(tree.id\x20==\x20null\x20||\x20tree.id.indexOf('callgraph-')\x20!=\x200)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20continue;\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20var\x20id\x20=\x20tree.id.substring('callgraph-'.length);\x0a\x20\x20\x20\x20\x20\x20$(tree).treeview({\x20collapsed:\x20true,\x20animated:\x20'fast'\x20});\x0a\x20\x20\x20\x20\x20\x20document.cgAddChildren(tree,\x20tree,\x20[id]);\x0a\x20\x20\x20\x20\x20\x20tree.parentNode.parentNode.style.display\x20=\x20'block';\x0a\x20\x20\x20\x20}\x0a\x20\x20}\x0a\x0a\x20\x20document.cgAddChildren\x20=\x20function(tree,\x20ul,\x20indices)\x20{\x0a\x20\x20\x20\x20if\x20(indices\x20!=\x20null)\x20{\x0a\x20\x20\x20\x20\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20indices.length;\x20i++)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20var\x20li\x20=\x20cgAddChild(tree,\x20ul,\x20document.CALLGRAPH[indices[i]]);\x0a\x20\x20\x20\x20\x20\x20\x20\x20if\x20(i\x20==\x20indices.length\x20-\x201)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20$(li).addClass('last');\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20$(tree).treeview({\x20animated:\x20'fast',\x20add:\x20ul\x20});\x0a\x20\x20};\x0a\x0a\x20\x20//\x20cgAddChild\x20adds\x20an\x20<li>\x20element\x20for\x20document.CALLGRAPH\x20node\x20cgn\x20to\x0a\x20\x20//\x20the\x20parent\x20<ul>\x20element\x20ul.\x20tree\x20is\x20the\x20tree's\x20root\x20<ul>\x20element.\x0a\x20\x20function\x20cgAddChild(tree,\x20ul,\x20cgn)\x20{\x0a\x20\x20\x20\x20var\x20li\x20=\x20document.createElement('li');\x0a\x20\x20\x20\x20ul.appendChild(li);\x0a\x20\x20\x20\x20li.className\x20=\x20'closed';\x0a\x0a\x20\x20\x20\x20var\x20code\x20=\x20document.createElement('code');\x0a\x0a\x20\x20\x20\x20if\x20(cgn.Callees\x20!=\x20null)\x20{\x0a\x20\x20\x20\x20\x20\x20$(li).addClass('expandable');\x0a\x0a\x20\x20\x20\x20\x20\x20//\x20Event\x20handlers\x20and\x20innerHTML\x20updates\x20don't\x20play\x20nicely\x20together,\x0a\x20\x20\x20\x20\x20\x20//\x20hence\x20all\x20this\x20explicit\x20DOM\x20manipulation.\x0a\x20\x20\x20\x20\x20\x20var\x20hitarea\x20=\x20document.createElement('div');\x0a\x20\x20\x20\x20\x20\x20hitarea.className\x20=\x20'hitarea\x20expandable-hitarea';\x0a\x20\x20\x20\x20\x20\x20li.appendChild(hitarea);\x0a\x0a\x20\x20\x20\x20\x20\x20li.appendChild(code);\x0a\x0a\x20\x20\x20\x20\x20\x20var\x20childUL\x20=\x20document.createElement('ul');\x0a\x20\x20\x20\x20\x20\x20li.appendChild(childUL);\x0a\x20\x20\x20\x20\x20\x20childUL.setAttribute('style',\x20'display:\x20none;');\x0a\x0a\x20\x20\x20\x20\x20\x20var\x20onClick\x20=\x20function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20document.cgAddChildren(tree,\x20childUL,\x20cgn.Callees);\x0a\x20\x20\x20\x20\x20\x20\x20\x20hitarea.removeEventListener('click',\x20onClick);\x0a\x20\x20\x20\x20\x20\x20};\x0a\x20\x20\x20\x20\x20\x20hitarea.addEventListener('click',\x20onClick);\x0a\x20\x20\x20\x20}\x20else\x20{\x0a\x20\x20\x20\x20\x20\x20li.appendChild(code);\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20code.innerHTML\x20+=\x20'&nbsp;'\x20+\x20makeAnchor(cgn.Func);\x0a\x20\x20\x20\x20return\x20li;\x0a\x20\x20}\x0a})();\x0a",
 
 	"images/minus.gif": "GIF89a\x09\x00\x09\x00\xf0\x02\x00\x00\x00\x00\x80\x80\x80!\xf9\x04\x05\x00\x00\x02\x00,\x00\x00\x00\x00\x09\x00\x09\x00@\x02\x11\x8c\x8f\x89\x02\xddb\x84\x9c0\xd0\x19o\xd5[\xe7\x1b\x14\x00;",
 
@@ -83,9 +83,9 @@
 
 	"packageroot.html": "<!--\x0a\x09Copyright\x202018\x20The\x20Go\x20Authors.\x20All\x20rights\x20reserved.\x0a\x09Use\x20of\x20this\x20source\x20code\x20is\x20governed\x20by\x20a\x20BSD-style\x0a\x09license\x20that\x20can\x20be\x20found\x20in\x20the\x20LICENSE\x20file.\x0a-->\x0a<!--\x0a\x09Note:\x20Static\x20(i.e.,\x20not\x20template-generated)\x20href\x20and\x20id\x0a\x09attributes\x20start\x20with\x20\"pkg-\"\x20to\x20make\x20it\x20impossible\x20for\x0a\x09them\x20to\x20conflict\x20with\x20generated\x20attributes\x20(some\x20of\x20which\x0a\x09correspond\x20to\x20Go\x20identifiers).\x0a-->\x0a{{with\x20.PAst}}\x0a\x09{{range\x20$filename,\x20$ast\x20:=\x20.}}\x0a\x09\x09<a\x20href=\"{{$filename|srcLink|html}}\">{{$filename|filename|html}}</a>:<pre>{{node_html\x20$\x20$ast\x20false}}</pre>\x0a\x09{{end}}\x0a{{end}}\x0a\x0a{{with\x20.Dirs}}\x0a\x09{{/*\x20DirList\x20entries\x20are\x20numbers\x20and\x20strings\x20-\x20no\x20need\x20for\x20FSet\x20*/}}\x0a\x09{{if\x20$.PDoc}}\x0a\x09\x09<h2\x20id=\"pkg-subdirectories\">Subdirectories</h2>\x0a\x09{{end}}\x0a\x09\x09<div\x20id=\"manual-nav\">\x0a\x09\x09\x09<dl>\x0a\x09\x09\x09\x09<dt><a\x20href=\"#stdlib\">Standard\x20library</a></dt>\x0a\x09\x09\x09\x09{{if\x20hasThirdParty\x20.List\x20}}\x0a\x09\x09\x09\x09\x09<dt><a\x20href=\"#thirdparty\">Third\x20party</a></dt>\x0a\x09\x09\x09\x09{{end}}\x0a\x09\x09\x09\x09<dt><a\x20href=\"#other\">Other\x20packages</a></dt>\x0a\x09\x09\x09\x09<dd><a\x20href=\"#subrepo\">Sub-repositories</a></dd>\x0a\x09\x09\x09\x09<dd><a\x20href=\"#community\">Community</a></dd>\x0a\x09\x09\x09</dl>\x0a\x09\x09</div>\x0a\x0a\x09\x09<div\x20id=\"stdlib\"\x20class=\"toggleVisible\">\x0a\x09\x09\x09<div\x20class=\"collapsed\">\x0a\x09\x09\x09\x09<h2\x20class=\"toggleButton\"\x20title=\"Click\x20to\x20show\x20Standard\x20library\x20section\">Standard\x20library\x20\xe2\x96\xb9</h2>\x0a\x09\x09\x09</div>\x0a\x09\x09\x09<div\x20class=\"expanded\">\x0a\x09\x09\x09\x09<h2\x20class=\"toggleButton\"\x20title=\"Click\x20to\x20hide\x20Standard\x20library\x20section\">Standard\x20library\x20\xe2\x96\xbe</h2>\x0a\x09\x09\x09\x09<img\x20alt=\"\"\x20class=\"gopher\"\x20src=\"/doc/gopher/pkg.png\"/>\x0a\x09\x09\x09\x09<div\x20class=\"pkg-dir\">\x0a\x09\x09\x09\x09\x09<table>\x0a\x09\x09\x09\x09\x09\x09<tr>\x0a\x09\x09\x09\x09\x09\x09\x09<th\x20class=\"pkg-name\">Name</th>\x0a\x09\x09\x09\x09\x09\x09\x09<th\x20class=\"pkg-synopsis\">Synopsis</th>\x0a\x09\x09\x09\x09\x09\x09</tr>\x0a\x0a\x09\x09\x09\x09\x09\x09{{range\x20.List}}\x0a\x09\x09\x09\x09\x09\x09\x09<tr>\x0a\x09\x09\x09\x09\x09\x09\x09{{if\x20eq\x20.RootType\x20\"GOROOT\"}}\x0a\x09\x09\x09\x09\x09\x09\x09{{if\x20$.DirFlat}}\x0a\x09\x09\x09\x09\x09\x09\x09\x09{{if\x20.HasPkg}}\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09<td\x20class=\"pkg-name\">\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09<a\x20href=\"{{html\x20.Path}}/{{modeQueryString\x20$.Mode\x20|\x20html}}\">{{html\x20.Path}}</a>\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09</td>\x0a\x09\x09\x09\x09\x09\x09\x09\x09{{end}}\x0a\x09\x09\x09\x09\x09\x09\x09{{else}}\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09<td\x20class=\"pkg-name\"\x20style=\"padding-left:\x20{{multiply\x20.Depth\x2020}}px;\">\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09<a\x20href=\"{{html\x20.Path}}/{{modeQueryString\x20$.Mode\x20|\x20html}}\">{{html\x20.Name}}</a>\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09</td>\x0a\x09\x09\x09\x09\x09\x09\x09{{end}}\x0a\x09\x09\x09\x09\x09\x09\x09\x09<td\x20class=\"pkg-synopsis\">\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09{{html\x20.Synopsis}}\x0a\x09\x09\x09\x09\x09\x09\x09\x09</td>\x0a\x09\x09\x09\x09\x09\x09\x09{{end}}\x0a\x09\x09\x09\x09\x09\x09\x09</tr>\x0a\x09\x09\x09\x09\x09\x09{{end}}\x0a\x09\x09\x09\x09\x09</table>\x0a\x09\x09\x09\x09</div>\x20<!--\x20.pkg-dir\x20-->\x0a\x09\x09\x09</div>\x20<!--\x20.expanded\x20-->\x0a\x09\x09</div>\x20<!--\x20#stdlib\x20.toggleVisible\x20-->\x0a\x0a\x09{{if\x20hasThirdParty\x20.List\x20}}\x0a\x09\x09<div\x20id=\"thirdparty\"\x20class=\"toggleVisible\">\x0a\x09\x09\x09<div\x20class=\"collapsed\">\x0a\x09\x09\x09\x09<h2\x20class=\"toggleButton\"\x20title=\"Click\x20to\x20show\x20Third\x20party\x20section\">Third\x20party\x20\xe2\x96\xb9</h2>\x0a\x09\x09\x09</div>\x0a\x09\x09\x09<div\x20class=\"expanded\">\x0a\x09\x09\x09\x09<h2\x20class=\"toggleButton\"\x20title=\"Click\x20to\x20hide\x20Third\x20party\x20section\">Third\x20party\x20\xe2\x96\xbe</h2>\x0a\x09\x09\x09\x09<div\x20class=\"pkg-dir\">\x0a\x09\x09\x09\x09\x09<table>\x0a\x09\x09\x09\x09\x09\x09<tr>\x0a\x09\x09\x09\x09\x09\x09\x09<th\x20class=\"pkg-name\">Name</th>\x0a\x09\x09\x09\x09\x09\x09\x09<th\x20class=\"pkg-synopsis\">Synopsis</th>\x0a\x09\x09\x09\x09\x09\x09</tr>\x0a\x0a\x09\x09\x09\x09\x09\x09{{range\x20.List}}\x0a\x09\x09\x09\x09\x09\x09\x09<tr>\x0a\x09\x09\x09\x09\x09\x09\x09\x09{{if\x20eq\x20.RootType\x20\"GOPATH\"}}\x0a\x09\x09\x09\x09\x09\x09\x09\x09{{if\x20$.DirFlat}}\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09{{if\x20.HasPkg}}\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09<td\x20class=\"pkg-name\">\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09<a\x20href=\"{{html\x20.Path}}/{{modeQueryString\x20$.Mode\x20|\x20html}}\">{{html\x20.Path}}</a>\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09</td>\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09{{end}}\x0a\x09\x09\x09\x09\x09\x09\x09\x09{{else}}\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09<td\x20class=\"pkg-name\"\x20style=\"padding-left:\x20{{multiply\x20.Depth\x2020}}px;\">\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09<a\x20href=\"{{html\x20.Path}}/{{modeQueryString\x20$.Mode\x20|\x20html}}\">{{html\x20.Name}}</a>\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09</td>\x0a\x09\x09\x09\x09\x09\x09\x09\x09{{end}}\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09<td\x20class=\"pkg-synopsis\">\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09{{html\x20.Synopsis}}\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09</td>\x0a\x09\x09\x09\x09\x09\x09\x09\x09{{end}}\x0a\x09\x09\x09\x09\x09\x09\x09</tr>\x0a\x09\x09\x09\x09\x09\x09{{end}}\x0a\x09\x09\x09\x09\x09</table>\x0a\x09\x09\x09\x09</div>\x20<!--\x20.pkg-dir\x20-->\x0a\x09\x09\x09</div>\x20<!--\x20.expanded\x20-->\x0a\x09\x09</div>\x20<!--\x20#stdlib\x20.toggleVisible\x20-->\x0a\x09{{end}}\x0a\x0a\x09<h2\x20id=\"other\">Other\x20packages</h2>\x0a\x09<h3\x20id=\"subrepo\">Sub-repositories</h3>\x0a\x09<p>\x0a\x09These\x20packages\x20are\x20part\x20of\x20the\x20Go\x20Project\x20but\x20outside\x20the\x20main\x20Go\x20tree.\x0a\x09They\x20are\x20developed\x20under\x20looser\x20<a\x20href=\"/doc/go1compat\">compatibility\x20requirements</a>\x20than\x20the\x20Go\x20core.\x0a\x09Install\x20them\x20with\x20\"<a\x20href=\"/cmd/go/#hdr-Download_and_install_packages_and_dependencies\">go\x20get</a>\".\x0a\x09</p>\x0a\x09<ul>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/benchmarks\">benchmarks</a>\x20\xe2\x80\x94\x20benchmarks\x20to\x20measure\x20Go\x20as\x20it\x20is\x20developed.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/blog\">blog</a>\x20\xe2\x80\x94\x20<a\x20href=\"//blog.golang.org\">blog.golang.org</a>'s\x20implementation.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/build\">build</a>\x20\xe2\x80\x94\x20<a\x20href=\"//build.golang.org\">build.golang.org</a>'s\x20implementation.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/crypto\">crypto</a>\x20\xe2\x80\x94\x20additional\x20cryptography\x20packages.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/debug\">debug</a>\x20\xe2\x80\x94\x20an\x20experimental\x20debugger\x20for\x20Go.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/image\">image</a>\x20\xe2\x80\x94\x20additional\x20imaging\x20packages.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/mobile\">mobile</a>\x20\xe2\x80\x94\x20experimental\x20support\x20for\x20Go\x20on\x20mobile\x20platforms.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/net\">net</a>\x20\xe2\x80\x94\x20additional\x20networking\x20packages.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/perf\">perf</a>\x20\xe2\x80\x94\x20packages\x20and\x20tools\x20for\x20performance\x20measurement,\x20storage,\x20and\x20analysis.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/review\">review</a>\x20\xe2\x80\x94\x20a\x20tool\x20for\x20working\x20with\x20Gerrit\x20code\x20reviews.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/sync\">sync</a>\x20\xe2\x80\x94\x20additional\x20concurrency\x20primitives.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/sys\">sys</a>\x20\xe2\x80\x94\x20packages\x20for\x20making\x20system\x20calls.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/text\">text</a>\x20\xe2\x80\x94\x20packages\x20for\x20working\x20with\x20text.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/time\">time</a>\x20\xe2\x80\x94\x20additional\x20time\x20packages.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/tools\">tools</a>\x20\xe2\x80\x94\x20godoc,\x20goimports,\x20gorename,\x20and\x20other\x20tools.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/tour\">tour</a>\x20\xe2\x80\x94\x20<a\x20href=\"//tour.golang.org\">tour.golang.org</a>'s\x20implementation.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/exp\">exp</a>\x20\xe2\x80\x94\x20experimental\x20and\x20deprecated\x20packages\x20(handle\x20with\x20care;\x20may\x20change\x20without\x20warning).</li>\x0a\x09</ul>\x0a\x0a\x09<h3\x20id=\"community\">Community</h3>\x0a\x09<p>\x0a\x09These\x20services\x20can\x20help\x20you\x20find\x20Open\x20Source\x20packages\x20provided\x20by\x20the\x20community.\x0a\x09</p>\x0a\x09<ul>\x0a\x09\x09<li><a\x20href=\"//godoc.org\">GoDoc</a>\x20-\x20a\x20package\x20index\x20and\x20search\x20engine.</li>\x0a\x09\x09<li><a\x20href=\"http://go-search.org\">Go\x20Search</a>\x20-\x20a\x20code\x20search\x20engine.</li>\x0a\x09\x09<li><a\x20href=\"/wiki/Projects\">Projects\x20at\x20the\x20Go\x20Wiki</a>\x20-\x20a\x20curated\x20list\x20of\x20Go\x20projects.</li>\x0a\x09</ul>\x0a{{end}}\x0a",
 
-	"play.js": "//\x20Copyright\x202012\x20The\x20Go\x20Authors.\x20All\x20rights\x20reserved.\x0a//\x20Use\x20of\x20this\x20source\x20code\x20is\x20governed\x20by\x20a\x20BSD-style\x0a//\x20license\x20that\x20can\x20be\x20found\x20in\x20the\x20LICENSE\x20file.\x0a\x0afunction\x20initPlayground(transport)\x20{\x0a\x09'use\x20strict';\x0a\x0a\x09function\x20text(node)\x20{\x0a\x09\x09var\x20s\x20=\x20'';\x0a\x09\x09for\x20(var\x20i\x20=\x200;\x20i\x20<\x20node.childNodes.length;\x20i++)\x20{\x0a\x09\x09\x09var\x20n\x20=\x20node.childNodes[i];\x0a\x09\x09\x09if\x20(n.nodeType\x20===\x201)\x20{\x0a\x09\x09\x09\x09if\x20(n.tagName\x20===\x20'BUTTON')\x20continue\x0a\x09\x09\x09\x09if\x20(n.tagName\x20===\x20'SPAN'\x20&&\x20n.className\x20===\x20'number')\x20continue;\x0a\x09\x09\x09\x09if\x20(n.tagName\x20===\x20'DIV'\x20||\x20n.tagName\x20==\x20'BR')\x20{\x0a\x09\x09\x09\x09\x09s\x20+=\x20\"\\n\";\x0a\x09\x09\x09\x09}\x0a\x09\x09\x09\x09s\x20+=\x20text(n);\x0a\x09\x09\x09\x09continue;\x0a\x09\x09\x09}\x0a\x09\x09\x09if\x20(n.nodeType\x20===\x203)\x20{\x0a\x09\x09\x09\x09s\x20+=\x20n.nodeValue;\x0a\x09\x09\x09}\x0a\x09\x09}\x0a\x09\x09return\x20s.replace('\\xA0',\x20'\x20');\x20//\x20replace\x20non-breaking\x20spaces\x0a\x09}\x0a\x0a\x09//\x20When\x20presenter\x20notes\x20are\x20enabled,\x20the\x20index\x20passed\x0a\x09//\x20here\x20will\x20identify\x20the\x20playground\x20to\x20be\x20synced\x0a\x09function\x20init(code,\x20index)\x20{\x0a\x09\x09var\x20output\x20=\x20document.createElement('div');\x0a\x09\x09var\x20outpre\x20=\x20document.createElement('pre');\x0a\x09\x09var\x20running;\x0a\x0a\x09\x09if\x20($\x20&&\x20$(output).resizable)\x20{\x0a\x09\x09\x09$(output).resizable({\x0a\x09\x09\x09\x09handles:\x20\x09'n,w,nw',\x0a\x09\x09\x09\x09minHeight:\x0927,\x0a\x09\x09\x09\x09minWidth:\x09135,\x0a\x09\x09\x09\x09maxHeight:\x09608,\x0a\x09\x09\x09\x09maxWidth:\x09990\x0a\x09\x09\x09});\x0a\x09\x09}\x0a\x0a\x09\x09function\x20onKill()\x20{\x0a\x09\x09\x09if\x20(running)\x20running.Kill();\x0a\x09\x09\x09if\x20(window.notesEnabled)\x20updatePlayStorage('onKill',\x20index);\x0a\x09\x09}\x0a\x0a\x09\x09function\x20onRun(e)\x20{\x0a\x09\x09\x09var\x20sk\x20=\x20e.shiftKey\x20||\x20localStorage.getItem('play-shiftKey')\x20===\x20'true';\x0a\x09\x09\x09if\x20(running)\x20running.Kill();\x0a\x09\x09\x09output.style.display\x20=\x20'block';\x0a\x09\x09\x09outpre.innerHTML\x20=\x20'';\x0a\x09\x09\x09run1.style.display\x20=\x20'none';\x0a\x09\x09\x09var\x20options\x20=\x20{Race:\x20sk};\x0a\x09\x09\x09running\x20=\x20transport.Run(text(code),\x20PlaygroundOutput(outpre),\x20options);\x0a\x09\x09\x09if\x20(window.notesEnabled)\x20updatePlayStorage('onRun',\x20index,\x20e);\x0a\x09\x09}\x0a\x0a\x09\x09function\x20onClose()\x20{\x0a\x09\x09\x09if\x20(running)\x20running.Kill();\x0a\x09\x09\x09output.style.display\x20=\x20'none';\x0a\x09\x09\x09run1.style.display\x20=\x20'inline-block';\x0a\x09\x09\x09if\x20(window.notesEnabled)\x20updatePlayStorage('onClose',\x20index);\x0a\x09\x09}\x0a\x0a\x09\x09if\x20(window.notesEnabled)\x20{\x0a\x09\x09\x09playgroundHandlers.onRun.push(onRun);\x0a\x09\x09\x09playgroundHandlers.onClose.push(onClose);\x0a\x09\x09\x09playgroundHandlers.onKill.push(onKill);\x0a\x09\x09}\x0a\x0a\x09\x09var\x20run1\x20=\x20document.createElement('button');\x0a\x09\x09run1.innerHTML\x20=\x20'Run';\x0a\x09\x09run1.className\x20=\x20'run';\x0a\x09\x09run1.addEventListener(\"click\",\x20onRun,\x20false);\x0a\x09\x09var\x20run2\x20=\x20document.createElement('button');\x0a\x09\x09run2.className\x20=\x20'run';\x0a\x09\x09run2.innerHTML\x20=\x20'Run';\x0a\x09\x09run2.addEventListener(\"click\",\x20onRun,\x20false);\x0a\x09\x09var\x20kill\x20=\x20document.createElement('button');\x0a\x09\x09kill.className\x20=\x20'kill';\x0a\x09\x09kill.innerHTML\x20=\x20'Kill';\x0a\x09\x09kill.addEventListener(\"click\",\x20onKill,\x20false);\x0a\x09\x09var\x20close\x20=\x20document.createElement('button');\x0a\x09\x09close.className\x20=\x20'close';\x0a\x09\x09close.innerHTML\x20=\x20'Close';\x0a\x09\x09close.addEventListener(\"click\",\x20onClose,\x20false);\x0a\x0a\x09\x09var\x20button\x20=\x20document.createElement('div');\x0a\x09\x09button.classList.add('buttons');\x0a\x09\x09button.appendChild(run1);\x0a\x09\x09//\x20Hack\x20to\x20simulate\x20insertAfter\x0a\x09\x09code.parentNode.insertBefore(button,\x20code.nextSibling);\x0a\x0a\x09\x09var\x20buttons\x20=\x20document.createElement('div');\x0a\x09\x09buttons.classList.add('buttons');\x0a\x09\x09buttons.appendChild(run2);\x0a\x09\x09buttons.appendChild(kill);\x0a\x09\x09buttons.appendChild(close);\x0a\x0a\x09\x09output.classList.add('output');\x0a\x09\x09output.appendChild(buttons);\x0a\x09\x09output.appendChild(outpre);\x0a\x09\x09output.style.display\x20=\x20'none';\x0a\x09\x09code.parentNode.insertBefore(output,\x20button.nextSibling);\x0a\x09}\x0a\x0a\x09var\x20play\x20=\x20document.querySelectorAll('div.playground');\x0a\x09for\x20(var\x20i\x20=\x200;\x20i\x20<\x20play.length;\x20i++)\x20{\x0a\x09\x09init(play[i],\x20i);\x0a\x09}\x0a}\x0a",
+	"play.js": "//\x20Copyright\x202012\x20The\x20Go\x20Authors.\x20All\x20rights\x20reserved.\x0a//\x20Use\x20of\x20this\x20source\x20code\x20is\x20governed\x20by\x20a\x20BSD-style\x0a//\x20license\x20that\x20can\x20be\x20found\x20in\x20the\x20LICENSE\x20file.\x0a\x0afunction\x20initPlayground(transport)\x20{\x0a\x20\x20'use\x20strict';\x0a\x0a\x20\x20function\x20text(node)\x20{\x0a\x20\x20\x20\x20var\x20s\x20=\x20'';\x0a\x20\x20\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20node.childNodes.length;\x20i++)\x20{\x0a\x20\x20\x20\x20\x20\x20var\x20n\x20=\x20node.childNodes[i];\x0a\x20\x20\x20\x20\x20\x20if\x20(n.nodeType\x20===\x201)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20if\x20(n.tagName\x20===\x20'BUTTON')\x20continue;\x0a\x20\x20\x20\x20\x20\x20\x20\x20if\x20(n.tagName\x20===\x20'SPAN'\x20&&\x20n.className\x20===\x20'number')\x20continue;\x0a\x20\x20\x20\x20\x20\x20\x20\x20if\x20(n.tagName\x20===\x20'DIV'\x20||\x20n.tagName\x20==\x20'BR')\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20s\x20+=\x20'\\n';\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20s\x20+=\x20text(n);\x0a\x20\x20\x20\x20\x20\x20\x20\x20continue;\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20if\x20(n.nodeType\x20===\x203)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20s\x20+=\x20n.nodeValue;\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20return\x20s.replace('\\xA0',\x20'\x20');\x20//\x20replace\x20non-breaking\x20spaces\x0a\x20\x20}\x0a\x0a\x20\x20//\x20When\x20presenter\x20notes\x20are\x20enabled,\x20the\x20index\x20passed\x0a\x20\x20//\x20here\x20will\x20identify\x20the\x20playground\x20to\x20be\x20synced\x0a\x20\x20function\x20init(code,\x20index)\x20{\x0a\x20\x20\x20\x20var\x20output\x20=\x20document.createElement('div');\x0a\x20\x20\x20\x20var\x20outpre\x20=\x20document.createElement('pre');\x0a\x20\x20\x20\x20var\x20running;\x0a\x0a\x20\x20\x20\x20if\x20($\x20&&\x20$(output).resizable)\x20{\x0a\x20\x20\x20\x20\x20\x20$(output).resizable({\x0a\x20\x20\x20\x20\x20\x20\x20\x20handles:\x20'n,w,nw',\x0a\x20\x20\x20\x20\x20\x20\x20\x20minHeight:\x2027,\x0a\x20\x20\x20\x20\x20\x20\x20\x20minWidth:\x20135,\x0a\x20\x20\x20\x20\x20\x20\x20\x20maxHeight:\x20608,\x0a\x20\x20\x20\x20\x20\x20\x20\x20maxWidth:\x20990,\x0a\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20function\x20onKill()\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(running)\x20running.Kill();\x0a\x20\x20\x20\x20\x20\x20if\x20(window.notesEnabled)\x20updatePlayStorage('onKill',\x20index);\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20function\x20onRun(e)\x20{\x0a\x20\x20\x20\x20\x20\x20var\x20sk\x20=\x20e.shiftKey\x20||\x20localStorage.getItem('play-shiftKey')\x20===\x20'true';\x0a\x20\x20\x20\x20\x20\x20if\x20(running)\x20running.Kill();\x0a\x20\x20\x20\x20\x20\x20output.style.display\x20=\x20'block';\x0a\x20\x20\x20\x20\x20\x20outpre.textContent\x20=\x20'';\x0a\x20\x20\x20\x20\x20\x20run1.style.display\x20=\x20'none';\x0a\x20\x20\x20\x20\x20\x20var\x20options\x20=\x20{\x20Race:\x20sk\x20};\x0a\x20\x20\x20\x20\x20\x20running\x20=\x20transport.Run(text(code),\x20PlaygroundOutput(outpre),\x20options);\x0a\x20\x20\x20\x20\x20\x20if\x20(window.notesEnabled)\x20updatePlayStorage('onRun',\x20index,\x20e);\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20function\x20onClose()\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(running)\x20running.Kill();\x0a\x20\x20\x20\x20\x20\x20output.style.display\x20=\x20'none';\x0a\x20\x20\x20\x20\x20\x20run1.style.display\x20=\x20'inline-block';\x0a\x20\x20\x20\x20\x20\x20if\x20(window.notesEnabled)\x20updatePlayStorage('onClose',\x20index);\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20if\x20(window.notesEnabled)\x20{\x0a\x20\x20\x20\x20\x20\x20playgroundHandlers.onRun.push(onRun);\x0a\x20\x20\x20\x20\x20\x20playgroundHandlers.onClose.push(onClose);\x0a\x20\x20\x20\x20\x20\x20playgroundHandlers.onKill.push(onKill);\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20var\x20run1\x20=\x20document.createElement('button');\x0a\x20\x20\x20\x20run1.textContent\x20=\x20'Run';\x0a\x20\x20\x20\x20run1.className\x20=\x20'run';\x0a\x20\x20\x20\x20run1.addEventListener('click',\x20onRun,\x20false);\x0a\x20\x20\x20\x20var\x20run2\x20=\x20document.createElement('button');\x0a\x20\x20\x20\x20run2.className\x20=\x20'run';\x0a\x20\x20\x20\x20run2.textContent\x20=\x20'Run';\x0a\x20\x20\x20\x20run2.addEventListener('click',\x20onRun,\x20false);\x0a\x20\x20\x20\x20var\x20kill\x20=\x20document.createElement('button');\x0a\x20\x20\x20\x20kill.className\x20=\x20'kill';\x0a\x20\x20\x20\x20kill.textContent\x20=\x20'Kill';\x0a\x20\x20\x20\x20kill.addEventListener('click',\x20onKill,\x20false);\x0a\x20\x20\x20\x20var\x20close\x20=\x20document.createElement('button');\x0a\x20\x20\x20\x20close.className\x20=\x20'close';\x0a\x20\x20\x20\x20close.textContent\x20=\x20'Close';\x0a\x20\x20\x20\x20close.addEventListener('click',\x20onClose,\x20false);\x0a\x0a\x20\x20\x20\x20var\x20button\x20=\x20document.createElement('div');\x0a\x20\x20\x20\x20button.classList.add('buttons');\x0a\x20\x20\x20\x20button.appendChild(run1);\x0a\x20\x20\x20\x20//\x20Hack\x20to\x20simulate\x20insertAfter\x0a\x20\x20\x20\x20code.parentNode.insertBefore(button,\x20code.nextSibling);\x0a\x0a\x20\x20\x20\x20var\x20buttons\x20=\x20document.createElement('div');\x0a\x20\x20\x20\x20buttons.classList.add('buttons');\x0a\x20\x20\x20\x20buttons.appendChild(run2);\x0a\x20\x20\x20\x20buttons.appendChild(kill);\x0a\x20\x20\x20\x20buttons.appendChild(close);\x0a\x0a\x20\x20\x20\x20output.classList.add('output');\x0a\x20\x20\x20\x20output.appendChild(buttons);\x0a\x20\x20\x20\x20output.appendChild(outpre);\x0a\x20\x20\x20\x20output.style.display\x20=\x20'none';\x0a\x20\x20\x20\x20code.parentNode.insertBefore(output,\x20button.nextSibling);\x0a\x20\x20}\x0a\x0a\x20\x20var\x20play\x20=\x20document.querySelectorAll('div.playground');\x0a\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20play.length;\x20i++)\x20{\x0a\x20\x20\x20\x20init(play[i],\x20i);\x0a\x20\x20}\x0a}\x0a",
 
-	"playground.js": "//\x20Copyright\x202012\x20The\x20Go\x20Authors.\x20All\x20rights\x20reserved.\x0a//\x20Use\x20of\x20this\x20source\x20code\x20is\x20governed\x20by\x20a\x20BSD-style\x0a//\x20license\x20that\x20can\x20be\x20found\x20in\x20the\x20LICENSE\x20file.\x0a\x0a/*\x0aIn\x20the\x20absence\x20of\x20any\x20formal\x20way\x20to\x20specify\x20interfaces\x20in\x20JavaScript,\x0ahere's\x20a\x20skeleton\x20implementation\x20of\x20a\x20playground\x20transport.\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20function\x20Transport()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20//\x20Set\x20up\x20any\x20transport\x20state\x20(eg,\x20make\x20a\x20websocket\x20connection).\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20return\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Run:\x20function(body,\x20output,\x20options)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20//\x20Compile\x20and\x20run\x20the\x20program\x20'body'\x20with\x20'options'.\x0a\x09\x09\x09\x09//\x20Call\x20the\x20'output'\x20callback\x20to\x20display\x20program\x20output.\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20return\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Kill:\x20function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20//\x20Kill\x20the\x20running\x20program.\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20};\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20};\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x0a\x09//\x20The\x20output\x20callback\x20is\x20called\x20multiple\x20times,\x20and\x20each\x20time\x20it\x20is\x0a\x09//\x20passed\x20an\x20object\x20of\x20this\x20form.\x0a\x20\x20\x20\x20\x20\x20\x20\x20var\x20write\x20=\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Kind:\x20'string',\x20//\x20'start',\x20'stdout',\x20'stderr',\x20'end'\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Body:\x20'string'\x20\x20//\x20content\x20of\x20write\x20or\x20end\x20status\x20message\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x0a\x09//\x20The\x20first\x20call\x20must\x20be\x20of\x20Kind\x20'start'\x20with\x20no\x20body.\x0a\x09//\x20Subsequent\x20calls\x20may\x20be\x20of\x20Kind\x20'stdout'\x20or\x20'stderr'\x0a\x09//\x20and\x20must\x20have\x20a\x20non-null\x20Body\x20string.\x0a\x09//\x20The\x20final\x20call\x20should\x20be\x20of\x20Kind\x20'end'\x20with\x20an\x20optional\x0a\x09//\x20Body\x20string,\x20signifying\x20a\x20failure\x20(\"killed\",\x20for\x20example).\x0a\x0a\x09//\x20The\x20output\x20callback\x20must\x20be\x20of\x20this\x20form.\x0a\x09//\x20See\x20PlaygroundOutput\x20(below)\x20for\x20an\x20implementation.\x0a\x20\x20\x20\x20\x20\x20\x20\x20function\x20outputCallback(write)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a*/\x0a\x0a//\x20HTTPTransport\x20is\x20the\x20default\x20transport.\x0a//\x20enableVet\x20enables\x20running\x20vet\x20if\x20a\x20program\x20was\x20compiled\x20and\x20ran\x20successfully.\x0a//\x20If\x20vet\x20returned\x20any\x20errors,\x20display\x20them\x20before\x20the\x20output\x20of\x20a\x20program.\x0afunction\x20HTTPTransport(enableVet)\x20{\x0a\x09'use\x20strict';\x0a\x0a\x09function\x20playback(output,\x20data)\x20{\x0a\x09\x09//\x20Backwards\x20compatibility:\x20default\x20values\x20do\x20not\x20affect\x20the\x20output.\x0a\x09\x09var\x20events\x20=\x20data.Events\x20||\x20[];\x0a\x09\x09var\x20errors\x20=\x20data.Errors\x20||\x20\"\";\x0a\x09\x09var\x20status\x20=\x20data.Status\x20||\x200;\x0a\x09\x09var\x20isTest\x20=\x20data.IsTest\x20||\x20false;\x0a\x09\x09var\x20testsFailed\x20=\x20data.TestsFailed\x20||\x200;\x0a\x0a\x09\x09var\x20timeout;\x0a\x09\x09output({Kind:\x20'start'});\x0a\x09\x09function\x20next()\x20{\x0a\x09\x09\x09if\x20(!events\x20||\x20events.length\x20===\x200)\x20{\x0a\x09\x09\x09\x09if\x20(isTest)\x20{\x0a\x09\x09\x09\x09\x09if\x20(testsFailed\x20>\x200)\x20{\x0a\x09\x09\x09\x09\x09\x09output({Kind:\x20'system',\x20Body:\x20'\\n'+testsFailed+'\x20test'+(testsFailed>1?'s':'')+'\x20failed.'});\x0a\x09\x09\x09\x09\x09}\x20else\x20{\x0a\x09\x09\x09\x09\x09\x09output({Kind:\x20'system',\x20Body:\x20'\\nAll\x20tests\x20passed.'});\x0a\x09\x09\x09\x09\x09}\x0a\x09\x09\x09\x09}\x20else\x20{\x0a\x09\x09\x09\x09\x09if\x20(status\x20>\x200)\x20{\x0a\x09\x09\x09\x09\x09\x09output({Kind:\x20'end',\x20Body:\x20'status\x20'\x20+\x20status\x20+\x20'.'});\x0a\x09\x09\x09\x09\x09}\x20else\x20{\x0a\x09\x09\x09\x09\x09\x09if\x20(errors\x20!==\x20\"\")\x20{\x0a\x09\x09\x09\x09\x09\x09\x09//\x20errors\x20are\x20displayed\x20only\x20in\x20the\x20case\x20of\x20timeout.\x0a\x09\x09\x09\x09\x09\x09\x09output({Kind:\x20'end',\x20Body:\x20errors\x20+\x20'.'});\x0a\x09\x09\x09\x09\x09\x09}\x20else\x20{\x0a\x09\x09\x09\x09\x09\x09\x09output({Kind:\x20'end'});\x0a\x09\x09\x09\x09\x09\x09}\x0a\x09\x09\x09\x09\x09}\x0a\x09\x09\x09\x09}\x0a\x09\x09\x09\x09return;\x0a\x09\x09\x09}\x0a\x09\x09\x09var\x20e\x20=\x20events.shift();\x0a\x09\x09\x09if\x20(e.Delay\x20===\x200)\x20{\x0a\x09\x09\x09\x09output({Kind:\x20e.Kind,\x20Body:\x20e.Message});\x0a\x09\x09\x09\x09next();\x0a\x09\x09\x09\x09return;\x0a\x09\x09\x09}\x0a\x09\x09\x09timeout\x20=\x20setTimeout(function()\x20{\x0a\x09\x09\x09\x09output({Kind:\x20e.Kind,\x20Body:\x20e.Message});\x0a\x09\x09\x09\x09next();\x0a\x09\x09\x09},\x20e.Delay\x20/\x201000000);\x0a\x09\x09}\x0a\x09\x09next();\x0a\x09\x09return\x20{\x0a\x09\x09\x09Stop:\x20function()\x20{\x0a\x09\x09\x09\x09clearTimeout(timeout);\x0a\x09\x09\x09}\x0a\x09\x09};\x0a\x09}\x0a\x0a\x09function\x20error(output,\x20msg)\x20{\x0a\x09\x09output({Kind:\x20'start'});\x0a\x09\x09output({Kind:\x20'stderr',\x20Body:\x20msg});\x0a\x09\x09output({Kind:\x20'end'});\x0a\x09}\x0a\x0a\x09function\x20buildFailed(output,\x20msg)\x20{\x0a\x09\x09output({Kind:\x20'start'});\x0a\x09\x09output({Kind:\x20'stderr',\x20Body:\x20msg});\x0a\x09\x09output({Kind:\x20'system',\x20Body:\x20'\\nGo\x20build\x20failed.'});\x0a\x09}\x0a\x0a\x09var\x20seq\x20=\x200;\x0a\x09return\x20{\x0a\x09\x09Run:\x20function(body,\x20output,\x20options)\x20{\x0a\x09\x09\x09seq++;\x0a\x09\x09\x09var\x20cur\x20=\x20seq;\x0a\x09\x09\x09var\x20playing;\x0a\x09\x09\x09$.ajax('/compile',\x20{\x0a\x09\x09\x09\x09type:\x20'POST',\x0a\x09\x09\x09\x09data:\x20{'version':\x202,\x20'body':\x20body,\x20'withVet':\x20enableVet},\x0a\x09\x09\x09\x09dataType:\x20'json',\x0a\x09\x09\x09\x09success:\x20function(data)\x20{\x0a\x09\x09\x09\x09\x09if\x20(seq\x20!=\x20cur)\x20return;\x0a\x09\x09\x09\x09\x09if\x20(!data)\x20return;\x0a\x09\x09\x09\x09\x09if\x20(playing\x20!=\x20null)\x20playing.Stop();\x0a\x09\x09\x09\x09\x09if\x20(data.Errors)\x20{\x0a\x09\x09\x09\x09\x09\x09if\x20(data.Errors\x20===\x20'process\x20took\x20too\x20long')\x20{\x0a\x09\x09\x09\x09\x09\x09\x09//\x20Playback\x20the\x20output\x20that\x20was\x20captured\x20before\x20the\x20timeout.\x0a\x09\x09\x09\x09\x09\x09\x09playing\x20=\x20playback(output,\x20data);\x0a\x09\x09\x09\x09\x09\x09}\x20else\x20{\x0a\x09\x09\x09\x09\x09\x09\x09buildFailed(output,\x20data.Errors);\x0a\x09\x09\x09\x09\x09\x09}\x0a\x09\x09\x09\x09\x09\x09return;\x0a\x09\x09\x09\x09\x09}\x0a\x09\x09\x09\x09\x09if\x20(!data.Events)\x20{\x0a\x09\x09\x09\x09\x09\x09data.Events\x20=\x20[];\x0a\x09\x09\x09\x09\x09}\x0a\x09\x09\x09\x09\x09if\x20(data.VetErrors)\x20{\x0a\x09\x09\x09\x09\x09\x09//\x20Inject\x20errors\x20from\x20the\x20vet\x20as\x20the\x20first\x20events\x20in\x20the\x20output.\x0a\x09\x09\x09\x09\x09\x09data.Events.unshift({Message:\x20'Go\x20vet\x20exited.\\n\\n',\x20Kind:\x20'system',\x20Delay:\x200});\x0a\x09\x09\x09\x09\x09\x09data.Events.unshift({Message:\x20data.VetErrors,\x20Kind:\x20'stderr',\x20Delay:\x200});\x0a\x09\x09\x09\x09\x09}\x0a\x0a\x09\x09\x09\x09\x09if\x20(!enableVet\x20||\x20data.VetOK\x20||\x20data.VetErrors)\x20{\x0a\x09\x09\x09\x09\x09\x09playing\x20=\x20playback(output,\x20data);\x0a\x09\x09\x09\x09\x09\x09return;\x0a\x09\x09\x09\x09\x09}\x0a\x0a\x09\x09\x09\x09\x09//\x20In\x20case\x20the\x20server\x20support\x20doesn't\x20support\x0a\x09\x09\x09\x09\x09//\x20compile+vet\x20in\x20same\x20request\x20signaled\x20by\x20the\x0a\x09\x09\x09\x09\x09//\x20'withVet'\x20parameter\x20above,\x20also\x20try\x20the\x20old\x20way.\x0a\x09\x09\x09\x09\x09//\x20TODO:\x20remove\x20this\x20when\x20it\x20falls\x20out\x20of\x20use.\x0a\x09\x09\x09\x09\x09//\x20It\x20is\x202019-05-13\x20now.\x0a\x09\x09\x09\x09\x09$.ajax(\"/vet\",\x20{\x0a\x09\x09\x09\x09\x09\x09data:\x20{\"body\":\x20body},\x0a\x09\x09\x09\x09\x09\x09type:\x20\"POST\",\x0a\x09\x09\x09\x09\x09\x09dataType:\x20\"json\",\x0a\x09\x09\x09\x09\x09\x09success:\x20function(dataVet)\x20{\x0a\x09\x09\x09\x09\x09\x09\x09if\x20(dataVet.Errors)\x20{\x0a\x09\x09\x09\x09\x09\x09\x09\x09//\x20inject\x20errors\x20from\x20the\x20vet\x20as\x20the\x20first\x20events\x20in\x20the\x20output\x0a\x09\x09\x09\x09\x09\x09\x09\x09data.Events.unshift({Message:\x20'Go\x20vet\x20exited.\\n\\n',\x20Kind:\x20'system',\x20Delay:\x200});\x0a\x09\x09\x09\x09\x09\x09\x09\x09data.Events.unshift({Message:\x20dataVet.Errors,\x20Kind:\x20'stderr',\x20Delay:\x200});\x0a\x09\x09\x09\x09\x09\x09\x09}\x0a\x09\x09\x09\x09\x09\x09\x09playing\x20=\x20playback(output,\x20data);\x0a\x09\x09\x09\x09\x09\x09},\x0a\x09\x09\x09\x09\x09\x09error:\x20function()\x20{\x0a\x09\x09\x09\x09\x09\x09\x09playing\x20=\x20playback(output,\x20data);\x0a\x09\x09\x09\x09\x09\x09}\x0a\x09\x09\x09\x09\x09});\x0a\x09\x09\x09\x09},\x0a\x09\x09\x09\x09error:\x20function()\x20{\x0a\x09\x09\x09\x09\x09error(output,\x20'Error\x20communicating\x20with\x20remote\x20server.');\x0a\x09\x09\x09\x09}\x0a\x09\x09\x09});\x0a\x09\x09\x09return\x20{\x0a\x09\x09\x09\x09Kill:\x20function()\x20{\x0a\x09\x09\x09\x09\x09if\x20(playing\x20!=\x20null)\x20playing.Stop();\x0a\x09\x09\x09\x09\x09output({Kind:\x20'end',\x20Body:\x20'killed'});\x0a\x09\x09\x09\x09}\x0a\x09\x09\x09};\x0a\x09\x09}\x0a\x09};\x0a}\x0a\x0afunction\x20SocketTransport()\x20{\x0a\x09'use\x20strict';\x0a\x0a\x09var\x20id\x20=\x200;\x0a\x09var\x20outputs\x20=\x20{};\x0a\x09var\x20started\x20=\x20{};\x0a\x09var\x20websocket;\x0a\x09if\x20(window.location.protocol\x20==\x20\"http:\")\x20{\x0a\x09\x09websocket\x20=\x20new\x20WebSocket('ws://'\x20+\x20window.location.host\x20+\x20'/socket');\x0a\x09}\x20else\x20if\x20(window.location.protocol\x20==\x20\"https:\")\x20{\x0a\x09\x09websocket\x20=\x20new\x20WebSocket('wss://'\x20+\x20window.location.host\x20+\x20'/socket');\x0a\x09}\x0a\x0a\x09websocket.onclose\x20=\x20function()\x20{\x0a\x09\x09console.log('websocket\x20connection\x20closed');\x0a\x09};\x0a\x0a\x09websocket.onmessage\x20=\x20function(e)\x20{\x0a\x09\x09var\x20m\x20=\x20JSON.parse(e.data);\x0a\x09\x09var\x20output\x20=\x20outputs[m.Id];\x0a\x09\x09if\x20(output\x20===\x20null)\x0a\x09\x09\x09return;\x0a\x09\x09if\x20(!started[m.Id])\x20{\x0a\x09\x09\x09output({Kind:\x20'start'});\x0a\x09\x09\x09started[m.Id]\x20=\x20true;\x0a\x09\x09}\x0a\x09\x09output({Kind:\x20m.Kind,\x20Body:\x20m.Body});\x0a\x09};\x0a\x0a\x09function\x20send(m)\x20{\x0a\x09\x09websocket.send(JSON.stringify(m));\x0a\x09}\x0a\x0a\x09return\x20{\x0a\x09\x09Run:\x20function(body,\x20output,\x20options)\x20{\x0a\x09\x09\x09var\x20thisID\x20=\x20id+'';\x0a\x09\x09\x09id++;\x0a\x09\x09\x09outputs[thisID]\x20=\x20output;\x0a\x09\x09\x09send({Id:\x20thisID,\x20Kind:\x20'run',\x20Body:\x20body,\x20Options:\x20options});\x0a\x09\x09\x09return\x20{\x0a\x09\x09\x09\x09Kill:\x20function()\x20{\x0a\x09\x09\x09\x09\x09send({Id:\x20thisID,\x20Kind:\x20'kill'});\x0a\x09\x09\x09\x09}\x0a\x09\x09\x09};\x0a\x09\x09}\x0a\x09};\x0a}\x0a\x0afunction\x20PlaygroundOutput(el)\x20{\x0a\x09'use\x20strict';\x0a\x0a\x09return\x20function(write)\x20{\x0a\x09\x09if\x20(write.Kind\x20==\x20'start')\x20{\x0a\x09\x09\x09el.innerHTML\x20=\x20'';\x0a\x09\x09\x09return;\x0a\x09\x09}\x0a\x0a\x09\x09var\x20cl\x20=\x20'system';\x0a\x09\x09if\x20(write.Kind\x20==\x20'stdout'\x20||\x20write.Kind\x20==\x20'stderr')\x0a\x09\x09\x09cl\x20=\x20write.Kind;\x0a\x0a\x09\x09var\x20m\x20=\x20write.Body;\x0a\x09\x09if\x20(write.Kind\x20==\x20'end')\x20{\x0a\x09\x09\x09m\x20=\x20'\\nProgram\x20exited'\x20+\x20(m?(':\x20'+m):'.');\x0a\x09\x09}\x0a\x0a\x09\x09if\x20(m.indexOf('IMAGE:')\x20===\x200)\x20{\x0a\x09\x09\x09//\x20TODO(adg):\x20buffer\x20all\x20writes\x20before\x20creating\x20image\x0a\x09\x09\x09var\x20url\x20=\x20'data:image/png;base64,'\x20+\x20m.substr(6);\x0a\x09\x09\x09var\x20img\x20=\x20document.createElement('img');\x0a\x09\x09\x09img.src\x20=\x20url;\x0a\x09\x09\x09el.appendChild(img);\x0a\x09\x09\x09return;\x0a\x09\x09}\x0a\x0a\x09\x09//\x20^L\x20clears\x20the\x20screen.\x0a\x09\x09var\x20s\x20=\x20m.split('\\x0c');\x0a\x09\x09if\x20(s.length\x20>\x201)\x20{\x0a\x09\x09\x09el.innerHTML\x20=\x20'';\x0a\x09\x09\x09m\x20=\x20s.pop();\x0a\x09\x09}\x0a\x0a\x09\x09m\x20=\x20m.replace(/&/g,\x20'&amp;');\x0a\x09\x09m\x20=\x20m.replace(/</g,\x20'&lt;');\x0a\x09\x09m\x20=\x20m.replace(/>/g,\x20'&gt;');\x0a\x0a\x09\x09var\x20needScroll\x20=\x20(el.scrollTop\x20+\x20el.offsetHeight)\x20==\x20el.scrollHeight;\x0a\x0a\x09\x09var\x20span\x20=\x20document.createElement('span');\x0a\x09\x09span.className\x20=\x20cl;\x0a\x09\x09span.innerHTML\x20=\x20m;\x0a\x09\x09el.appendChild(span);\x0a\x0a\x09\x09if\x20(needScroll)\x0a\x09\x09\x09el.scrollTop\x20=\x20el.scrollHeight\x20-\x20el.offsetHeight;\x0a\x09};\x0a}\x0a\x0a(function()\x20{\x0a\x20\x20function\x20lineHighlight(error)\x20{\x0a\x20\x20\x20\x20var\x20regex\x20=\x20/prog.go:([0-9]+)/g;\x0a\x20\x20\x20\x20var\x20r\x20=\x20regex.exec(error);\x0a\x20\x20\x20\x20while\x20(r)\x20{\x0a\x20\x20\x20\x20\x20\x20$(\".lines\x20div\").eq(r[1]-1).addClass(\"lineerror\");\x0a\x20\x20\x20\x20\x20\x20r\x20=\x20regex.exec(error);\x0a\x20\x20\x20\x20}\x0a\x20\x20}\x0a\x20\x20function\x20highlightOutput(wrappedOutput)\x20{\x0a\x20\x20\x20\x20return\x20function(write)\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(write.Body)\x20lineHighlight(write.Body);\x0a\x20\x20\x20\x20\x20\x20wrappedOutput(write);\x0a\x20\x20\x20\x20};\x0a\x20\x20}\x0a\x20\x20function\x20lineClear()\x20{\x0a\x20\x20\x20\x20$(\".lineerror\").removeClass(\"lineerror\");\x0a\x20\x20}\x0a\x0a\x20\x20//\x20opts\x20is\x20an\x20object\x20with\x20these\x20keys\x0a\x20\x20//\x20\x20codeEl\x20-\x20code\x20editor\x20element\x0a\x20\x20//\x20\x20outputEl\x20-\x20program\x20output\x20element\x0a\x20\x20//\x20\x20runEl\x20-\x20run\x20button\x20element\x0a\x20\x20//\x20\x20fmtEl\x20-\x20fmt\x20button\x20element\x20(optional)\x0a\x20\x20//\x20\x20fmtImportEl\x20-\x20fmt\x20\"imports\"\x20checkbox\x20element\x20(optional)\x0a\x20\x20//\x20\x20shareEl\x20-\x20share\x20button\x20element\x20(optional)\x0a\x20\x20//\x20\x20shareURLEl\x20-\x20share\x20URL\x20text\x20input\x20element\x20(optional)\x0a\x20\x20//\x20\x20shareRedirect\x20-\x20base\x20URL\x20to\x20redirect\x20to\x20on\x20share\x20(optional)\x0a\x20\x20//\x20\x20toysEl\x20-\x20toys\x20select\x20element\x20(optional)\x0a\x20\x20//\x20\x20enableHistory\x20-\x20enable\x20using\x20HTML5\x20history\x20API\x20(optional)\x0a\x20\x20//\x20\x20transport\x20-\x20playground\x20transport\x20to\x20use\x20(default\x20is\x20HTTPTransport)\x0a\x20\x20//\x20\x20enableShortcuts\x20-\x20whether\x20to\x20enable\x20shortcuts\x20(Ctrl+S/Cmd+S\x20to\x20save)\x20(default\x20is\x20false)\x0a\x20\x20//\x20\x20enableVet\x20-\x20enable\x20running\x20vet\x20and\x20displaying\x20its\x20errors\x0a\x20\x20function\x20playground(opts)\x20{\x0a\x20\x20\x20\x20var\x20code\x20=\x20$(opts.codeEl);\x0a\x20\x20\x20\x20var\x20transport\x20=\x20opts['transport']\x20||\x20new\x20HTTPTransport(opts['enableVet']);\x0a\x20\x20\x20\x20var\x20running;\x0a\x0a\x20\x20\x20\x20//\x20autoindent\x20helpers.\x0a\x20\x20\x20\x20function\x20insertTabs(n)\x20{\x0a\x20\x20\x20\x20\x20\x20//\x20find\x20the\x20selection\x20start\x20and\x20end\x0a\x20\x20\x20\x20\x20\x20var\x20start\x20=\x20code[0].selectionStart;\x0a\x20\x20\x20\x20\x20\x20var\x20end\x20\x20\x20=\x20code[0].selectionEnd;\x0a\x20\x20\x20\x20\x20\x20//\x20split\x20the\x20textarea\x20content\x20into\x20two,\x20and\x20insert\x20n\x20tabs\x0a\x20\x20\x20\x20\x20\x20var\x20v\x20=\x20code[0].value;\x0a\x20\x20\x20\x20\x20\x20var\x20u\x20=\x20v.substr(0,\x20start);\x0a\x20\x20\x20\x20\x20\x20for\x20(var\x20i=0;\x20i<n;\x20i++)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20u\x20+=\x20\"\\t\";\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20u\x20+=\x20v.substr(end);\x0a\x20\x20\x20\x20\x20\x20//\x20set\x20revised\x20content\x0a\x20\x20\x20\x20\x20\x20code[0].value\x20=\x20u;\x0a\x20\x20\x20\x20\x20\x20//\x20reset\x20caret\x20position\x20after\x20inserted\x20tabs\x0a\x20\x20\x20\x20\x20\x20code[0].selectionStart\x20=\x20start+n;\x0a\x20\x20\x20\x20\x20\x20code[0].selectionEnd\x20=\x20start+n;\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20function\x20autoindent(el)\x20{\x0a\x20\x20\x20\x20\x20\x20var\x20curpos\x20=\x20el.selectionStart;\x0a\x20\x20\x20\x20\x20\x20var\x20tabs\x20=\x200;\x0a\x20\x20\x20\x20\x20\x20while\x20(curpos\x20>\x200)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20curpos--;\x0a\x20\x20\x20\x20\x20\x20\x20\x20if\x20(el.value[curpos]\x20==\x20\"\\t\")\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20tabs++;\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x20else\x20if\x20(tabs\x20>\x200\x20||\x20el.value[curpos]\x20==\x20\"\\n\")\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20break;\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20setTimeout(function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20insertTabs(tabs);\x0a\x20\x20\x20\x20\x20\x20},\x201);\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20//\x20NOTE(cbro):\x20e\x20is\x20a\x20jQuery\x20event,\x20not\x20a\x20DOM\x20event.\x0a\x20\x20\x20\x20function\x20handleSaveShortcut(e)\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(e.isDefaultPrevented())\x20return\x20false;\x0a\x20\x20\x20\x20\x20\x20if\x20(!e.metaKey\x20&&\x20!e.ctrlKey)\x20return\x20false;\x0a\x20\x20\x20\x20\x20\x20if\x20(e.key\x20!=\x20\"S\"\x20&&\x20e.key\x20!=\x20\"s\")\x20return\x20false;\x0a\x0a\x20\x20\x20\x20\x20\x20e.preventDefault();\x0a\x0a\x20\x20\x20\x20\x20\x20//\x20Share\x20and\x20save\x0a\x20\x20\x20\x20\x20\x20share(function(url)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20window.location.href\x20=\x20url\x20+\x20\".go?download=true\";\x0a\x20\x20\x20\x20\x20\x20});\x0a\x0a\x20\x20\x20\x20\x20\x20return\x20true;\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20function\x20keyHandler(e)\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(opts.enableShortcuts\x20&&\x20handleSaveShortcut(e))\x20return;\x0a\x0a\x20\x20\x20\x20\x20\x20if\x20(e.keyCode\x20==\x209\x20&&\x20!e.ctrlKey)\x20{\x20//\x20tab\x20(but\x20not\x20ctrl-tab)\x0a\x20\x20\x20\x20\x20\x20\x20\x20insertTabs(1);\x0a\x20\x20\x20\x20\x20\x20\x20\x20e.preventDefault();\x0a\x20\x20\x20\x20\x20\x20\x20\x20return\x20false;\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20if\x20(e.keyCode\x20==\x2013)\x20{\x20//\x20enter\x0a\x20\x20\x20\x20\x20\x20\x20\x20if\x20(e.shiftKey)\x20{\x20//\x20+shift\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20run();\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20e.preventDefault();\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20return\x20false;\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x20if\x20(e.ctrlKey)\x20{\x20//\x20+control\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20fmt();\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20e.preventDefault();\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x20else\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20autoindent(e.target);\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20return\x20true;\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20code.unbind('keydown').bind('keydown',\x20keyHandler);\x0a\x20\x20\x20\x20var\x20outdiv\x20=\x20$(opts.outputEl).empty();\x0a\x20\x20\x20\x20var\x20output\x20=\x20$('<pre/>').appendTo(outdiv);\x0a\x0a\x20\x20\x20\x20function\x20body()\x20{\x0a\x20\x20\x20\x20\x20\x20return\x20$(opts.codeEl).val();\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20function\x20setBody(text)\x20{\x0a\x20\x20\x20\x20\x20\x20$(opts.codeEl).val(text);\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20function\x20origin(href)\x20{\x0a\x20\x20\x20\x20\x20\x20return\x20(\"\"+href).split(\"/\").slice(0,\x203).join(\"/\");\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20var\x20pushedEmpty\x20=\x20(window.location.pathname\x20==\x20\"/\");\x0a\x20\x20\x20\x20function\x20inputChanged()\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(pushedEmpty)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20pushedEmpty\x20=\x20true;\x0a\x20\x20\x20\x20\x20\x20$(opts.shareURLEl).hide();\x0a\x20\x20\x20\x20\x20\x20window.history.pushState(null,\x20\"\",\x20\"/\");\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20function\x20popState(e)\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(e\x20===\x20null)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20if\x20(e\x20&&\x20e.state\x20&&\x20e.state.code)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20setBody(e.state.code);\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20var\x20rewriteHistory\x20=\x20false;\x0a\x20\x20\x20\x20if\x20(window.history\x20&&\x20window.history.pushState\x20&&\x20window.addEventListener\x20&&\x20opts.enableHistory)\x20{\x0a\x20\x20\x20\x20\x20\x20rewriteHistory\x20=\x20true;\x0a\x20\x20\x20\x20\x20\x20code[0].addEventListener('input',\x20inputChanged);\x0a\x20\x20\x20\x20\x20\x20window.addEventListener('popstate',\x20popState);\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20function\x20setError(error)\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(running)\x20running.Kill();\x0a\x20\x20\x20\x20\x20\x20lineClear();\x0a\x20\x20\x20\x20\x20\x20lineHighlight(error);\x0a\x20\x20\x20\x20\x20\x20output.empty().addClass(\"error\").text(error);\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20function\x20loading()\x20{\x0a\x20\x20\x20\x20\x20\x20lineClear();\x0a\x20\x20\x20\x20\x20\x20if\x20(running)\x20running.Kill();\x0a\x20\x20\x20\x20\x20\x20output.removeClass(\"error\").text('Waiting\x20for\x20remote\x20server...');\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20function\x20run()\x20{\x0a\x20\x20\x20\x20\x20\x20loading();\x0a\x20\x20\x20\x20\x20\x20running\x20=\x20transport.Run(body(),\x20highlightOutput(PlaygroundOutput(output[0])));\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20function\x20fmt()\x20{\x0a\x20\x20\x20\x20\x20\x20loading();\x0a\x20\x20\x20\x20\x20\x20var\x20data\x20=\x20{\"body\":\x20body()};\x0a\x20\x20\x20\x20\x20\x20if\x20($(opts.fmtImportEl).is(\":checked\"))\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20data[\"imports\"]\x20=\x20\"true\";\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20$.ajax(\"/fmt\",\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20data:\x20data,\x0a\x20\x20\x20\x20\x20\x20\x20\x20type:\x20\"POST\",\x0a\x20\x20\x20\x20\x20\x20\x20\x20dataType:\x20\"json\",\x0a\x20\x20\x20\x20\x20\x20\x20\x20success:\x20function(data)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(data.Error)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20setError(data.Error);\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x20else\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20setBody(data.Body);\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20setError(\"\");\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20var\x20shareURL;\x20//\x20jQuery\x20element\x20to\x20show\x20the\x20shared\x20URL.\x0a\x20\x20\x20\x20var\x20sharing\x20=\x20false;\x20//\x20true\x20if\x20there\x20is\x20a\x20pending\x20request.\x0a\x20\x20\x20\x20var\x20shareCallbacks\x20=\x20[];\x0a\x20\x20\x20\x20function\x20share(opt_callback)\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(opt_callback)\x20shareCallbacks.push(opt_callback);\x0a\x0a\x20\x20\x20\x20\x20\x20if\x20(sharing)\x20return;\x0a\x20\x20\x20\x20\x20\x20sharing\x20=\x20true;\x0a\x0a\x20\x20\x20\x20\x20\x20var\x20sharingData\x20=\x20body();\x0a\x20\x20\x20\x20\x20\x20$.ajax(\"/share\",\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20processData:\x20false,\x0a\x20\x20\x20\x20\x20\x20\x20\x20data:\x20sharingData,\x0a\x20\x20\x20\x20\x20\x20\x20\x20type:\x20\"POST\",\x0a\x20\x20\x20\x20\x20\x20\x20\x20contentType:\x20\"text/plain;\x20charset=utf-8\",\x0a\x20\x20\x20\x20\x20\x20\x20\x20complete:\x20function(xhr)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20sharing\x20=\x20false;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(xhr.status\x20!=\x20200)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20alert(\"Server\x20error;\x20try\x20again.\");\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(opts.shareRedirect)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20window.location\x20=\x20opts.shareRedirect\x20+\x20xhr.responseText;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20var\x20path\x20=\x20\"/p/\"\x20+\x20xhr.responseText;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20var\x20url\x20=\x20origin(window.location)\x20+\x20path;\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20shareCallbacks.length;\x20i++)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20shareCallbacks[i](url);\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20shareCallbacks\x20=\x20[];\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(shareURL)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20shareURL.show().val(url).focus().select();\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(rewriteHistory)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20var\x20historyData\x20=\x20{\"code\":\x20sharingData};\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20window.history.pushState(historyData,\x20\"\",\x20path);\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20pushedEmpty\x20=\x20false;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20$(opts.runEl).click(run);\x0a\x20\x20\x20\x20$(opts.fmtEl).click(fmt);\x0a\x0a\x20\x20\x20\x20if\x20(opts.shareEl\x20!==\x20null\x20&&\x20(opts.shareURLEl\x20!==\x20null\x20||\x20opts.shareRedirect\x20!==\x20null))\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(opts.shareURLEl)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20shareURL\x20=\x20$(opts.shareURLEl).hide();\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20$(opts.shareEl).click(function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20share();\x0a\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20if\x20(opts.toysEl\x20!==\x20null)\x20{\x0a\x20\x20\x20\x20\x20\x20$(opts.toysEl).bind('change',\x20function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20var\x20toy\x20=\x20$(this).val();\x0a\x20\x20\x20\x20\x20\x20\x20\x20$.ajax(\"/doc/play/\"+toy,\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20processData:\x20false,\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20type:\x20\"GET\",\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20complete:\x20function(xhr)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(xhr.status\x20!=\x20200)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20alert(\"Server\x20error;\x20try\x20again.\");\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20setBody(xhr.responseText);\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20}\x0a\x20\x20}\x0a\x0a\x20\x20window.playground\x20=\x20playground;\x0a})();\x0a",
+	"playground.js": "//\x20Copyright\x202012\x20The\x20Go\x20Authors.\x20All\x20rights\x20reserved.\x0a//\x20Use\x20of\x20this\x20source\x20code\x20is\x20governed\x20by\x20a\x20BSD-style\x0a//\x20license\x20that\x20can\x20be\x20found\x20in\x20the\x20LICENSE\x20file.\x0a\x0a/*\x0aIn\x20the\x20absence\x20of\x20any\x20formal\x20way\x20to\x20specify\x20interfaces\x20in\x20JavaScript,\x0ahere's\x20a\x20skeleton\x20implementation\x20of\x20a\x20playground\x20transport.\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20function\x20Transport()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20//\x20Set\x20up\x20any\x20transport\x20state\x20(eg,\x20make\x20a\x20websocket\x20connection).\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20return\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Run:\x20function(body,\x20output,\x20options)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20//\x20Compile\x20and\x20run\x20the\x20program\x20'body'\x20with\x20'options'.\x0a\x09\x09\x09\x09//\x20Call\x20the\x20'output'\x20callback\x20to\x20display\x20program\x20output.\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20return\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Kill:\x20function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20//\x20Kill\x20the\x20running\x20program.\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20};\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20};\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x0a\x09//\x20The\x20output\x20callback\x20is\x20called\x20multiple\x20times,\x20and\x20each\x20time\x20it\x20is\x0a\x09//\x20passed\x20an\x20object\x20of\x20this\x20form.\x0a\x20\x20\x20\x20\x20\x20\x20\x20var\x20write\x20=\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Kind:\x20'string',\x20//\x20'start',\x20'stdout',\x20'stderr',\x20'end'\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Body:\x20'string'\x20\x20//\x20content\x20of\x20write\x20or\x20end\x20status\x20message\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x0a\x09//\x20The\x20first\x20call\x20must\x20be\x20of\x20Kind\x20'start'\x20with\x20no\x20body.\x0a\x09//\x20Subsequent\x20calls\x20may\x20be\x20of\x20Kind\x20'stdout'\x20or\x20'stderr'\x0a\x09//\x20and\x20must\x20have\x20a\x20non-null\x20Body\x20string.\x0a\x09//\x20The\x20final\x20call\x20should\x20be\x20of\x20Kind\x20'end'\x20with\x20an\x20optional\x0a\x09//\x20Body\x20string,\x20signifying\x20a\x20failure\x20(\"killed\",\x20for\x20example).\x0a\x0a\x09//\x20The\x20output\x20callback\x20must\x20be\x20of\x20this\x20form.\x0a\x09//\x20See\x20PlaygroundOutput\x20(below)\x20for\x20an\x20implementation.\x0a\x20\x20\x20\x20\x20\x20\x20\x20function\x20outputCallback(write)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a*/\x0a\x0a//\x20HTTPTransport\x20is\x20the\x20default\x20transport.\x0a//\x20enableVet\x20enables\x20running\x20vet\x20if\x20a\x20program\x20was\x20compiled\x20and\x20ran\x20successfully.\x0a//\x20If\x20vet\x20returned\x20any\x20errors,\x20display\x20them\x20before\x20the\x20output\x20of\x20a\x20program.\x0afunction\x20HTTPTransport(enableVet)\x20{\x0a\x20\x20'use\x20strict';\x0a\x0a\x20\x20function\x20playback(output,\x20data)\x20{\x0a\x20\x20\x20\x20//\x20Backwards\x20compatibility:\x20default\x20values\x20do\x20not\x20affect\x20the\x20output.\x0a\x20\x20\x20\x20var\x20events\x20=\x20data.Events\x20||\x20[];\x0a\x20\x20\x20\x20var\x20errors\x20=\x20data.Errors\x20||\x20'';\x0a\x20\x20\x20\x20var\x20status\x20=\x20data.Status\x20||\x200;\x0a\x20\x20\x20\x20var\x20isTest\x20=\x20data.IsTest\x20||\x20false;\x0a\x20\x20\x20\x20var\x20testsFailed\x20=\x20data.TestsFailed\x20||\x200;\x0a\x0a\x20\x20\x20\x20var\x20timeout;\x0a\x20\x20\x20\x20output({\x20Kind:\x20'start'\x20});\x0a\x20\x20\x20\x20function\x20next()\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(!events\x20||\x20events.length\x20===\x200)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20if\x20(isTest)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(testsFailed\x20>\x200)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20output({\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Kind:\x20'system',\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Body:\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20'\\n'\x20+\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20testsFailed\x20+\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20'\x20test'\x20+\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20(testsFailed\x20>\x201\x20?\x20's'\x20:\x20'')\x20+\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20'\x20failed.',\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x20else\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20output({\x20Kind:\x20'system',\x20Body:\x20'\\nAll\x20tests\x20passed.'\x20});\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x20else\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(status\x20>\x200)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20output({\x20Kind:\x20'end',\x20Body:\x20'status\x20'\x20+\x20status\x20+\x20'.'\x20});\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x20else\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(errors\x20!==\x20'')\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20//\x20errors\x20are\x20displayed\x20only\x20in\x20the\x20case\x20of\x20timeout.\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20output({\x20Kind:\x20'end',\x20Body:\x20errors\x20+\x20'.'\x20});\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x20else\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20output({\x20Kind:\x20'end'\x20});\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20var\x20e\x20=\x20events.shift();\x0a\x20\x20\x20\x20\x20\x20if\x20(e.Delay\x20===\x200)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20output({\x20Kind:\x20e.Kind,\x20Body:\x20e.Message\x20});\x0a\x20\x20\x20\x20\x20\x20\x20\x20next();\x0a\x20\x20\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20timeout\x20=\x20setTimeout(function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20output({\x20Kind:\x20e.Kind,\x20Body:\x20e.Message\x20});\x0a\x20\x20\x20\x20\x20\x20\x20\x20next();\x0a\x20\x20\x20\x20\x20\x20},\x20e.Delay\x20/\x201000000);\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20next();\x0a\x20\x20\x20\x20return\x20{\x0a\x20\x20\x20\x20\x20\x20Stop:\x20function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20clearTimeout(timeout);\x0a\x20\x20\x20\x20\x20\x20},\x0a\x20\x20\x20\x20};\x0a\x20\x20}\x0a\x0a\x20\x20function\x20error(output,\x20msg)\x20{\x0a\x20\x20\x20\x20output({\x20Kind:\x20'start'\x20});\x0a\x20\x20\x20\x20output({\x20Kind:\x20'stderr',\x20Body:\x20msg\x20});\x0a\x20\x20\x20\x20output({\x20Kind:\x20'end'\x20});\x0a\x20\x20}\x0a\x0a\x20\x20function\x20buildFailed(output,\x20msg)\x20{\x0a\x20\x20\x20\x20output({\x20Kind:\x20'start'\x20});\x0a\x20\x20\x20\x20output({\x20Kind:\x20'stderr',\x20Body:\x20msg\x20});\x0a\x20\x20\x20\x20output({\x20Kind:\x20'system',\x20Body:\x20'\\nGo\x20build\x20failed.'\x20});\x0a\x20\x20}\x0a\x0a\x20\x20var\x20seq\x20=\x200;\x0a\x20\x20return\x20{\x0a\x20\x20\x20\x20Run:\x20function(body,\x20output,\x20options)\x20{\x0a\x20\x20\x20\x20\x20\x20seq++;\x0a\x20\x20\x20\x20\x20\x20var\x20cur\x20=\x20seq;\x0a\x20\x20\x20\x20\x20\x20var\x20playing;\x0a\x20\x20\x20\x20\x20\x20$.ajax('/compile',\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20type:\x20'POST',\x0a\x20\x20\x20\x20\x20\x20\x20\x20data:\x20{\x20version:\x202,\x20body:\x20body,\x20withVet:\x20enableVet\x20},\x0a\x20\x20\x20\x20\x20\x20\x20\x20dataType:\x20'json',\x0a\x20\x20\x20\x20\x20\x20\x20\x20success:\x20function(data)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(seq\x20!=\x20cur)\x20return;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(!data)\x20return;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(playing\x20!=\x20null)\x20playing.Stop();\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(data.Errors)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(data.Errors\x20===\x20'process\x20took\x20too\x20long')\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20//\x20Playback\x20the\x20output\x20that\x20was\x20captured\x20before\x20the\x20timeout.\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20playing\x20=\x20playback(output,\x20data);\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x20else\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20buildFailed(output,\x20data.Errors);\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(!data.Events)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20data.Events\x20=\x20[];\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(data.VetErrors)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20//\x20Inject\x20errors\x20from\x20the\x20vet\x20as\x20the\x20first\x20events\x20in\x20the\x20output.\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20data.Events.unshift({\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Message:\x20'Go\x20vet\x20exited.\\n\\n',\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Kind:\x20'system',\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Delay:\x200,\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20data.Events.unshift({\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Message:\x20data.VetErrors,\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Kind:\x20'stderr',\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Delay:\x200,\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(!enableVet\x20||\x20data.VetOK\x20||\x20data.VetErrors)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20playing\x20=\x20playback(output,\x20data);\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20//\x20In\x20case\x20the\x20server\x20support\x20doesn't\x20support\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20//\x20compile+vet\x20in\x20same\x20request\x20signaled\x20by\x20the\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20//\x20'withVet'\x20parameter\x20above,\x20also\x20try\x20the\x20old\x20way.\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20//\x20TODO:\x20remove\x20this\x20when\x20it\x20falls\x20out\x20of\x20use.\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20//\x20It\x20is\x202019-05-13\x20now.\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20$.ajax('/vet',\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20data:\x20{\x20body:\x20body\x20},\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20type:\x20'POST',\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20dataType:\x20'json',\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20success:\x20function(dataVet)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(dataVet.Errors)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20//\x20inject\x20errors\x20from\x20the\x20vet\x20as\x20the\x20first\x20events\x20in\x20the\x20output\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20data.Events.unshift({\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Message:\x20'Go\x20vet\x20exited.\\n\\n',\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Kind:\x20'system',\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Delay:\x200,\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20data.Events.unshift({\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Message:\x20dataVet.Errors,\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Kind:\x20'stderr',\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Delay:\x200,\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20playing\x20=\x20playback(output,\x20data);\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20},\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20error:\x20function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20playing\x20=\x20playback(output,\x20data);\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20},\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20\x20\x20\x20\x20},\x0a\x20\x20\x20\x20\x20\x20\x20\x20error:\x20function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20error(output,\x20'Error\x20communicating\x20with\x20remote\x20server.');\x0a\x20\x20\x20\x20\x20\x20\x20\x20},\x0a\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20\x20\x20return\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20Kill:\x20function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(playing\x20!=\x20null)\x20playing.Stop();\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20output({\x20Kind:\x20'end',\x20Body:\x20'killed'\x20});\x0a\x20\x20\x20\x20\x20\x20\x20\x20},\x0a\x20\x20\x20\x20\x20\x20};\x0a\x20\x20\x20\x20},\x0a\x20\x20};\x0a}\x0a\x0afunction\x20SocketTransport()\x20{\x0a\x20\x20'use\x20strict';\x0a\x0a\x20\x20var\x20id\x20=\x200;\x0a\x20\x20var\x20outputs\x20=\x20{};\x0a\x20\x20var\x20started\x20=\x20{};\x0a\x20\x20var\x20websocket;\x0a\x20\x20if\x20(window.location.protocol\x20==\x20'http:')\x20{\x0a\x20\x20\x20\x20websocket\x20=\x20new\x20WebSocket('ws://'\x20+\x20window.location.host\x20+\x20'/socket');\x0a\x20\x20}\x20else\x20if\x20(window.location.protocol\x20==\x20'https:')\x20{\x0a\x20\x20\x20\x20websocket\x20=\x20new\x20WebSocket('wss://'\x20+\x20window.location.host\x20+\x20'/socket');\x0a\x20\x20}\x0a\x0a\x20\x20websocket.onclose\x20=\x20function()\x20{\x0a\x20\x20\x20\x20console.log('websocket\x20connection\x20closed');\x0a\x20\x20};\x0a\x0a\x20\x20websocket.onmessage\x20=\x20function(e)\x20{\x0a\x20\x20\x20\x20var\x20m\x20=\x20JSON.parse(e.data);\x0a\x20\x20\x20\x20var\x20output\x20=\x20outputs[m.Id];\x0a\x20\x20\x20\x20if\x20(output\x20===\x20null)\x20return;\x0a\x20\x20\x20\x20if\x20(!started[m.Id])\x20{\x0a\x20\x20\x20\x20\x20\x20output({\x20Kind:\x20'start'\x20});\x0a\x20\x20\x20\x20\x20\x20started[m.Id]\x20=\x20true;\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20output({\x20Kind:\x20m.Kind,\x20Body:\x20m.Body\x20});\x0a\x20\x20};\x0a\x0a\x20\x20function\x20send(m)\x20{\x0a\x20\x20\x20\x20websocket.send(JSON.stringify(m));\x0a\x20\x20}\x0a\x0a\x20\x20return\x20{\x0a\x20\x20\x20\x20Run:\x20function(body,\x20output,\x20options)\x20{\x0a\x20\x20\x20\x20\x20\x20var\x20thisID\x20=\x20id\x20+\x20'';\x0a\x20\x20\x20\x20\x20\x20id++;\x0a\x20\x20\x20\x20\x20\x20outputs[thisID]\x20=\x20output;\x0a\x20\x20\x20\x20\x20\x20send({\x20Id:\x20thisID,\x20Kind:\x20'run',\x20Body:\x20body,\x20Options:\x20options\x20});\x0a\x20\x20\x20\x20\x20\x20return\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20Kill:\x20function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20send({\x20Id:\x20thisID,\x20Kind:\x20'kill'\x20});\x0a\x20\x20\x20\x20\x20\x20\x20\x20},\x0a\x20\x20\x20\x20\x20\x20};\x0a\x20\x20\x20\x20},\x0a\x20\x20};\x0a}\x0a\x0afunction\x20PlaygroundOutput(el)\x20{\x0a\x20\x20'use\x20strict';\x0a\x0a\x20\x20return\x20function(write)\x20{\x0a\x20\x20\x20\x20if\x20(write.Kind\x20==\x20'start')\x20{\x0a\x20\x20\x20\x20\x20\x20el.innerHTML\x20=\x20'';\x0a\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20var\x20cl\x20=\x20'system';\x0a\x20\x20\x20\x20if\x20(write.Kind\x20==\x20'stdout'\x20||\x20write.Kind\x20==\x20'stderr')\x20cl\x20=\x20write.Kind;\x0a\x0a\x20\x20\x20\x20var\x20m\x20=\x20write.Body;\x0a\x20\x20\x20\x20if\x20(write.Kind\x20==\x20'end')\x20{\x0a\x20\x20\x20\x20\x20\x20m\x20=\x20'\\nProgram\x20exited'\x20+\x20(m\x20?\x20':\x20'\x20+\x20m\x20:\x20'.');\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20if\x20(m.indexOf('IMAGE:')\x20===\x200)\x20{\x0a\x20\x20\x20\x20\x20\x20//\x20TODO(adg):\x20buffer\x20all\x20writes\x20before\x20creating\x20image\x0a\x20\x20\x20\x20\x20\x20var\x20url\x20=\x20'data:image/png;base64,'\x20+\x20m.substr(6);\x0a\x20\x20\x20\x20\x20\x20var\x20img\x20=\x20document.createElement('img');\x0a\x20\x20\x20\x20\x20\x20img.src\x20=\x20url;\x0a\x20\x20\x20\x20\x20\x20el.appendChild(img);\x0a\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20//\x20^L\x20clears\x20the\x20screen.\x0a\x20\x20\x20\x20var\x20s\x20=\x20m.split('\\x0c');\x0a\x20\x20\x20\x20if\x20(s.length\x20>\x201)\x20{\x0a\x20\x20\x20\x20\x20\x20el.innerHTML\x20=\x20'';\x0a\x20\x20\x20\x20\x20\x20m\x20=\x20s.pop();\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20m\x20=\x20m.replace(/&/g,\x20'&amp;');\x0a\x20\x20\x20\x20m\x20=\x20m.replace(/</g,\x20'&lt;');\x0a\x20\x20\x20\x20m\x20=\x20m.replace(/>/g,\x20'&gt;');\x0a\x0a\x20\x20\x20\x20var\x20needScroll\x20=\x20el.scrollTop\x20+\x20el.offsetHeight\x20==\x20el.scrollHeight;\x0a\x0a\x20\x20\x20\x20var\x20span\x20=\x20document.createElement('span');\x0a\x20\x20\x20\x20span.className\x20=\x20cl;\x0a\x20\x20\x20\x20span.innerHTML\x20=\x20m;\x0a\x20\x20\x20\x20el.appendChild(span);\x0a\x0a\x20\x20\x20\x20if\x20(needScroll)\x20el.scrollTop\x20=\x20el.scrollHeight\x20-\x20el.offsetHeight;\x0a\x20\x20};\x0a}\x0a\x0a(function()\x20{\x0a\x20\x20function\x20lineHighlight(error)\x20{\x0a\x20\x20\x20\x20var\x20regex\x20=\x20/prog.go:([0-9]+)/g;\x0a\x20\x20\x20\x20var\x20r\x20=\x20regex.exec(error);\x0a\x20\x20\x20\x20while\x20(r)\x20{\x0a\x20\x20\x20\x20\x20\x20$('.lines\x20div')\x0a\x20\x20\x20\x20\x20\x20\x20\x20.eq(r[1]\x20-\x201)\x0a\x20\x20\x20\x20\x20\x20\x20\x20.addClass('lineerror');\x0a\x20\x20\x20\x20\x20\x20r\x20=\x20regex.exec(error);\x0a\x20\x20\x20\x20}\x0a\x20\x20}\x0a\x20\x20function\x20highlightOutput(wrappedOutput)\x20{\x0a\x20\x20\x20\x20return\x20function(write)\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(write.Body)\x20lineHighlight(write.Body);\x0a\x20\x20\x20\x20\x20\x20wrappedOutput(write);\x0a\x20\x20\x20\x20};\x0a\x20\x20}\x0a\x20\x20function\x20lineClear()\x20{\x0a\x20\x20\x20\x20$('.lineerror').removeClass('lineerror');\x0a\x20\x20}\x0a\x0a\x20\x20//\x20opts\x20is\x20an\x20object\x20with\x20these\x20keys\x0a\x20\x20//\x20\x20codeEl\x20-\x20code\x20editor\x20element\x0a\x20\x20//\x20\x20outputEl\x20-\x20program\x20output\x20element\x0a\x20\x20//\x20\x20runEl\x20-\x20run\x20button\x20element\x0a\x20\x20//\x20\x20fmtEl\x20-\x20fmt\x20button\x20element\x20(optional)\x0a\x20\x20//\x20\x20fmtImportEl\x20-\x20fmt\x20\"imports\"\x20checkbox\x20element\x20(optional)\x0a\x20\x20//\x20\x20shareEl\x20-\x20share\x20button\x20element\x20(optional)\x0a\x20\x20//\x20\x20shareURLEl\x20-\x20share\x20URL\x20text\x20input\x20element\x20(optional)\x0a\x20\x20//\x20\x20shareRedirect\x20-\x20base\x20URL\x20to\x20redirect\x20to\x20on\x20share\x20(optional)\x0a\x20\x20//\x20\x20toysEl\x20-\x20toys\x20select\x20element\x20(optional)\x0a\x20\x20//\x20\x20enableHistory\x20-\x20enable\x20using\x20HTML5\x20history\x20API\x20(optional)\x0a\x20\x20//\x20\x20transport\x20-\x20playground\x20transport\x20to\x20use\x20(default\x20is\x20HTTPTransport)\x0a\x20\x20//\x20\x20enableShortcuts\x20-\x20whether\x20to\x20enable\x20shortcuts\x20(Ctrl+S/Cmd+S\x20to\x20save)\x20(default\x20is\x20false)\x0a\x20\x20//\x20\x20enableVet\x20-\x20enable\x20running\x20vet\x20and\x20displaying\x20its\x20errors\x0a\x20\x20function\x20playground(opts)\x20{\x0a\x20\x20\x20\x20var\x20code\x20=\x20$(opts.codeEl);\x0a\x20\x20\x20\x20var\x20transport\x20=\x20opts['transport']\x20||\x20new\x20HTTPTransport(opts['enableVet']);\x0a\x20\x20\x20\x20var\x20running;\x0a\x0a\x20\x20\x20\x20//\x20autoindent\x20helpers.\x0a\x20\x20\x20\x20function\x20insertTabs(n)\x20{\x0a\x20\x20\x20\x20\x20\x20//\x20find\x20the\x20selection\x20start\x20and\x20end\x0a\x20\x20\x20\x20\x20\x20var\x20start\x20=\x20code[0].selectionStart;\x0a\x20\x20\x20\x20\x20\x20var\x20end\x20=\x20code[0].selectionEnd;\x0a\x20\x20\x20\x20\x20\x20//\x20split\x20the\x20textarea\x20content\x20into\x20two,\x20and\x20insert\x20n\x20tabs\x0a\x20\x20\x20\x20\x20\x20var\x20v\x20=\x20code[0].value;\x0a\x20\x20\x20\x20\x20\x20var\x20u\x20=\x20v.substr(0,\x20start);\x0a\x20\x20\x20\x20\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20n;\x20i++)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20u\x20+=\x20'\\t';\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20u\x20+=\x20v.substr(end);\x0a\x20\x20\x20\x20\x20\x20//\x20set\x20revised\x20content\x0a\x20\x20\x20\x20\x20\x20code[0].value\x20=\x20u;\x0a\x20\x20\x20\x20\x20\x20//\x20reset\x20caret\x20position\x20after\x20inserted\x20tabs\x0a\x20\x20\x20\x20\x20\x20code[0].selectionStart\x20=\x20start\x20+\x20n;\x0a\x20\x20\x20\x20\x20\x20code[0].selectionEnd\x20=\x20start\x20+\x20n;\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20function\x20autoindent(el)\x20{\x0a\x20\x20\x20\x20\x20\x20var\x20curpos\x20=\x20el.selectionStart;\x0a\x20\x20\x20\x20\x20\x20var\x20tabs\x20=\x200;\x0a\x20\x20\x20\x20\x20\x20while\x20(curpos\x20>\x200)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20curpos--;\x0a\x20\x20\x20\x20\x20\x20\x20\x20if\x20(el.value[curpos]\x20==\x20'\\t')\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20tabs++;\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x20else\x20if\x20(tabs\x20>\x200\x20||\x20el.value[curpos]\x20==\x20'\\n')\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20break;\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20setTimeout(function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20insertTabs(tabs);\x0a\x20\x20\x20\x20\x20\x20},\x201);\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20//\x20NOTE(cbro):\x20e\x20is\x20a\x20jQuery\x20event,\x20not\x20a\x20DOM\x20event.\x0a\x20\x20\x20\x20function\x20handleSaveShortcut(e)\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(e.isDefaultPrevented())\x20return\x20false;\x0a\x20\x20\x20\x20\x20\x20if\x20(!e.metaKey\x20&&\x20!e.ctrlKey)\x20return\x20false;\x0a\x20\x20\x20\x20\x20\x20if\x20(e.key\x20!=\x20'S'\x20&&\x20e.key\x20!=\x20's')\x20return\x20false;\x0a\x0a\x20\x20\x20\x20\x20\x20e.preventDefault();\x0a\x0a\x20\x20\x20\x20\x20\x20//\x20Share\x20and\x20save\x0a\x20\x20\x20\x20\x20\x20share(function(url)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20window.location.href\x20=\x20url\x20+\x20'.go?download=true';\x0a\x20\x20\x20\x20\x20\x20});\x0a\x0a\x20\x20\x20\x20\x20\x20return\x20true;\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20function\x20keyHandler(e)\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(opts.enableShortcuts\x20&&\x20handleSaveShortcut(e))\x20return;\x0a\x0a\x20\x20\x20\x20\x20\x20if\x20(e.keyCode\x20==\x209\x20&&\x20!e.ctrlKey)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20//\x20tab\x20(but\x20not\x20ctrl-tab)\x0a\x20\x20\x20\x20\x20\x20\x20\x20insertTabs(1);\x0a\x20\x20\x20\x20\x20\x20\x20\x20e.preventDefault();\x0a\x20\x20\x20\x20\x20\x20\x20\x20return\x20false;\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20if\x20(e.keyCode\x20==\x2013)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20//\x20enter\x0a\x20\x20\x20\x20\x20\x20\x20\x20if\x20(e.shiftKey)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20//\x20+shift\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20run();\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20e.preventDefault();\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20return\x20false;\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20if\x20(e.ctrlKey)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20//\x20+control\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20fmt();\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20e.preventDefault();\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x20else\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20autoindent(e.target);\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20return\x20true;\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20code.unbind('keydown').bind('keydown',\x20keyHandler);\x0a\x20\x20\x20\x20var\x20outdiv\x20=\x20$(opts.outputEl).empty();\x0a\x20\x20\x20\x20var\x20output\x20=\x20$('<pre/>').appendTo(outdiv);\x0a\x0a\x20\x20\x20\x20function\x20body()\x20{\x0a\x20\x20\x20\x20\x20\x20return\x20$(opts.codeEl).val();\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20function\x20setBody(text)\x20{\x0a\x20\x20\x20\x20\x20\x20$(opts.codeEl).val(text);\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20function\x20origin(href)\x20{\x0a\x20\x20\x20\x20\x20\x20return\x20(''\x20+\x20href)\x0a\x20\x20\x20\x20\x20\x20\x20\x20.split('/')\x0a\x20\x20\x20\x20\x20\x20\x20\x20.slice(0,\x203)\x0a\x20\x20\x20\x20\x20\x20\x20\x20.join('/');\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20var\x20pushedEmpty\x20=\x20window.location.pathname\x20==\x20'/';\x0a\x20\x20\x20\x20function\x20inputChanged()\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(pushedEmpty)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20pushedEmpty\x20=\x20true;\x0a\x20\x20\x20\x20\x20\x20$(opts.shareURLEl).hide();\x0a\x20\x20\x20\x20\x20\x20window.history.pushState(null,\x20'',\x20'/');\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20function\x20popState(e)\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(e\x20===\x20null)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20if\x20(e\x20&&\x20e.state\x20&&\x20e.state.code)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20setBody(e.state.code);\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20var\x20rewriteHistory\x20=\x20false;\x0a\x20\x20\x20\x20if\x20(\x0a\x20\x20\x20\x20\x20\x20window.history\x20&&\x0a\x20\x20\x20\x20\x20\x20window.history.pushState\x20&&\x0a\x20\x20\x20\x20\x20\x20window.addEventListener\x20&&\x0a\x20\x20\x20\x20\x20\x20opts.enableHistory\x0a\x20\x20\x20\x20)\x20{\x0a\x20\x20\x20\x20\x20\x20rewriteHistory\x20=\x20true;\x0a\x20\x20\x20\x20\x20\x20code[0].addEventListener('input',\x20inputChanged);\x0a\x20\x20\x20\x20\x20\x20window.addEventListener('popstate',\x20popState);\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20function\x20setError(error)\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(running)\x20running.Kill();\x0a\x20\x20\x20\x20\x20\x20lineClear();\x0a\x20\x20\x20\x20\x20\x20lineHighlight(error);\x0a\x20\x20\x20\x20\x20\x20output\x0a\x20\x20\x20\x20\x20\x20\x20\x20.empty()\x0a\x20\x20\x20\x20\x20\x20\x20\x20.addClass('error')\x0a\x20\x20\x20\x20\x20\x20\x20\x20.text(error);\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20function\x20loading()\x20{\x0a\x20\x20\x20\x20\x20\x20lineClear();\x0a\x20\x20\x20\x20\x20\x20if\x20(running)\x20running.Kill();\x0a\x20\x20\x20\x20\x20\x20output.removeClass('error').text('Waiting\x20for\x20remote\x20server...');\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20function\x20run()\x20{\x0a\x20\x20\x20\x20\x20\x20loading();\x0a\x20\x20\x20\x20\x20\x20running\x20=\x20transport.Run(\x0a\x20\x20\x20\x20\x20\x20\x20\x20body(),\x0a\x20\x20\x20\x20\x20\x20\x20\x20highlightOutput(PlaygroundOutput(output[0]))\x0a\x20\x20\x20\x20\x20\x20);\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20function\x20fmt()\x20{\x0a\x20\x20\x20\x20\x20\x20loading();\x0a\x20\x20\x20\x20\x20\x20var\x20data\x20=\x20{\x20body:\x20body()\x20};\x0a\x20\x20\x20\x20\x20\x20if\x20($(opts.fmtImportEl).is(':checked'))\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20data['imports']\x20=\x20'true';\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20$.ajax('/fmt',\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20data:\x20data,\x0a\x20\x20\x20\x20\x20\x20\x20\x20type:\x20'POST',\x0a\x20\x20\x20\x20\x20\x20\x20\x20dataType:\x20'json',\x0a\x20\x20\x20\x20\x20\x20\x20\x20success:\x20function(data)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(data.Error)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20setError(data.Error);\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x20else\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20setBody(data.Body);\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20setError('');\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20},\x0a\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20var\x20shareURL;\x20//\x20jQuery\x20element\x20to\x20show\x20the\x20shared\x20URL.\x0a\x20\x20\x20\x20var\x20sharing\x20=\x20false;\x20//\x20true\x20if\x20there\x20is\x20a\x20pending\x20request.\x0a\x20\x20\x20\x20var\x20shareCallbacks\x20=\x20[];\x0a\x20\x20\x20\x20function\x20share(opt_callback)\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(opt_callback)\x20shareCallbacks.push(opt_callback);\x0a\x0a\x20\x20\x20\x20\x20\x20if\x20(sharing)\x20return;\x0a\x20\x20\x20\x20\x20\x20sharing\x20=\x20true;\x0a\x0a\x20\x20\x20\x20\x20\x20var\x20sharingData\x20=\x20body();\x0a\x20\x20\x20\x20\x20\x20$.ajax('/share',\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20processData:\x20false,\x0a\x20\x20\x20\x20\x20\x20\x20\x20data:\x20sharingData,\x0a\x20\x20\x20\x20\x20\x20\x20\x20type:\x20'POST',\x0a\x20\x20\x20\x20\x20\x20\x20\x20contentType:\x20'text/plain;\x20charset=utf-8',\x0a\x20\x20\x20\x20\x20\x20\x20\x20complete:\x20function(xhr)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20sharing\x20=\x20false;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(xhr.status\x20!=\x20200)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20alert('Server\x20error;\x20try\x20again.');\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(opts.shareRedirect)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20window.location\x20=\x20opts.shareRedirect\x20+\x20xhr.responseText;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20var\x20path\x20=\x20'/p/'\x20+\x20xhr.responseText;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20var\x20url\x20=\x20origin(window.location)\x20+\x20path;\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20shareCallbacks.length;\x20i++)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20shareCallbacks[i](url);\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20shareCallbacks\x20=\x20[];\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(shareURL)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20shareURL\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20.show()\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20.val(url)\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20.focus()\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20.select();\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(rewriteHistory)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20var\x20historyData\x20=\x20{\x20code:\x20sharingData\x20};\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20window.history.pushState(historyData,\x20'',\x20path);\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20pushedEmpty\x20=\x20false;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20},\x0a\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20$(opts.runEl).click(run);\x0a\x20\x20\x20\x20$(opts.fmtEl).click(fmt);\x0a\x0a\x20\x20\x20\x20if\x20(\x0a\x20\x20\x20\x20\x20\x20opts.shareEl\x20!==\x20null\x20&&\x0a\x20\x20\x20\x20\x20\x20(opts.shareURLEl\x20!==\x20null\x20||\x20opts.shareRedirect\x20!==\x20null)\x0a\x20\x20\x20\x20)\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(opts.shareURLEl)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20shareURL\x20=\x20$(opts.shareURLEl).hide();\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20$(opts.shareEl).click(function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20share();\x0a\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20if\x20(opts.toysEl\x20!==\x20null)\x20{\x0a\x20\x20\x20\x20\x20\x20$(opts.toysEl).bind('change',\x20function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20var\x20toy\x20=\x20$(this).val();\x0a\x20\x20\x20\x20\x20\x20\x20\x20$.ajax('/doc/play/'\x20+\x20toy,\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20processData:\x20false,\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20type:\x20'GET',\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20complete:\x20function(xhr)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(xhr.status\x20!=\x20200)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20alert('Server\x20error;\x20try\x20again.');\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20setBody(xhr.responseText);\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20},\x0a\x20\x20\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20}\x0a\x20\x20}\x0a\x0a\x20\x20window.playground\x20=\x20playground;\x0a})();\x0a",
 
 	"search.html": "<!--\x0a\x09Copyright\x202009\x20The\x20Go\x20Authors.\x20All\x20rights\x20reserved.\x0a\x09Use\x20of\x20this\x20source\x20code\x20is\x20governed\x20by\x20a\x20BSD-style\x0a\x09license\x20that\x20can\x20be\x20found\x20in\x20the\x20LICENSE\x20file.\x0a-->\x0a\x0a{{\x20$colCount\x20:=\x20tocColCount\x20.}}\x0a{{/*\x20Generate\x20the\x20TOC\x20*/}}\x0a<nav\x20class=\"search-nav\"\x20style=\"column-count:\x20{{$colCount}}\"\x20role=\"navigation\">\x0a{{range\x20$key,\x20$val\x20:=\x20.Idents}}\x0a\x09{{if\x20$val}}\x0a\x09\x09<a\x20href=\"#{{$key.Name}}\">{{$key.Name}}</a>\x0a\x09\x09<br\x20/>\x0a\x09{{end}}\x0a{{end}}\x0a\x0a{{if\x20not\x20.Idents}}\x0a\x09{{with\x20.Pak}}\x0a\x09\x09<a\x20href=\"#Packages\">Package\x20{{html\x20$.Query}}</a>\x0a\x09\x09<br\x20/>\x0a\x09{{end}}\x0a{{end}}\x0a\x0a{{with\x20.Hit}}\x0a\x09{{with\x20.Decls}}\x0a\x09\x09<a\x20href=\"#Global\">Package-level\x20declarations</a>\x0a\x09\x09<br\x20/>\x0a\x09\x09{{range\x20.}}\x0a\x09\x09\x09{{$pkg_html\x20:=\x20pkgLink\x20.Pak.Path\x20|\x20html}}\x0a\x09\x09\x09<a\x20href=\"#Global_{{$pkg_html}}\"\x20class=\"indent\">package\x20{{html\x20.Pak.Name}}</a>\x0a\x09\x09\x09<br\x20/>\x0a\x09\x09{{end}}\x0a\x09{{end}}\x0a\x09{{with\x20.Others}}\x0a\x09\x09<a\x20href=\"#Local\">Local\x20declarations\x20and\x20uses</a>\x0a\x09\x09<br\x20/>\x0a\x09\x09{{range\x20.}}\x0a\x09\x09\x09{{$pkg_html\x20:=\x20pkgLink\x20.Pak.Path\x20|\x20html}}\x0a\x09\x09\x09<a\x20href=\"#Local_{{$pkg_html}}\"\x20class=\"indent\">package\x20{{html\x20.Pak.Name}}</a>\x0a\x09\x09\x09<br\x20/>\x0a\x09\x09{{end}}\x0a\x09{{end}}\x0a{{end}}\x0a\x0a{{with\x20.Textual}}\x0a\x09{{if\x20$.Complete}}\x0a\x09\x09<a\x20href=\"#Textual\">{{html\x20$.Found}}\x20textual\x20occurrences</a>\x0a\x09{{else}}\x0a\x09\x09<a\x20href=\"#Textual\">More\x20than\x20{{html\x20$.Found}}\x20textual\x20occurrences</a>\x0a\x09{{end}}\x0a{{end}}\x0a</nav>\x0a\x0a{{with\x20.Alert}}\x0a\x09<p>\x0a\x09<span\x20class=\"alert\"\x20style=\"font-size:120%\">{{html\x20.}}</span>\x0a\x09</p>\x0a{{end}}\x0a{{with\x20.Alt}}\x0a\x09<p>\x0a\x09<span\x20class=\"alert\"\x20style=\"font-size:120%\">Did\x20you\x20mean:\x20</span>\x0a\x09{{range\x20.Alts}}\x0a\x09\x09<a\x20href=\"search?q={{urlquery\x20.}}\"\x20style=\"font-size:120%\">{{html\x20.}}</a>\x0a\x09{{end}}\x0a\x09</p>\x0a{{end}}\x0a",
 
@@ -95,5 +95,5 @@
 
 	"searchtxt.html": "<!--\x0a\x09Copyright\x202009\x20The\x20Go\x20Authors.\x20All\x20rights\x20reserved.\x0a\x09Use\x20of\x20this\x20source\x20code\x20is\x20governed\x20by\x20a\x20BSD-style\x0a\x09license\x20that\x20can\x20be\x20found\x20in\x20the\x20LICENSE\x20file.\x0a-->\x0a{{$query_url\x20:=\x20urlquery\x20.Query}}\x0a{{with\x20.Textual}}\x0a\x09{{if\x20$.Complete}}\x0a\x09\x09<h2\x20id=\"Textual\">{{html\x20$.Found}}\x20textual\x20occurrences</h2>\x0a\x09{{else}}\x0a\x09\x09<h2\x20id=\"Textual\">More\x20than\x20{{html\x20$.Found}}\x20textual\x20occurrences</h2>\x0a\x09\x09<p>\x0a\x09\x09<span\x20class=\"alert\"\x20style=\"font-size:120%\">Not\x20all\x20files\x20or\x20lines\x20containing\x20\"{{html\x20$.Query}}\"\x20are\x20shown.</span>\x0a\x09\x09</p>\x0a\x09{{end}}\x0a\x09<p>\x0a\x09<table\x20class=\"layout\">\x0a\x09{{range\x20.}}\x0a\x09\x09{{$file\x20:=\x20.Filename}}\x0a\x09\x09<tr>\x0a\x09\x09<td\x20align=\"left\"\x20valign=\"top\">\x0a\x09\x09<a\x20href=\"{{queryLink\x20$file\x20$query_url\x200}}\">{{$file}}</a>:\x0a\x09\x09</td>\x0a\x09\x09<td\x20align=\"left\"\x20width=\"4\"></td>\x0a\x09\x09<th\x20align=\"left\"\x20valign=\"top\">{{len\x20.Lines}}</th>\x0a\x09\x09<td\x20align=\"left\"\x20width=\"4\"></td>\x0a\x09\x09<td\x20align=\"left\">\x0a\x09\x09{{range\x20.Lines}}\x0a\x09\x09\x09<a\x20href=\"{{queryLink\x20$file\x20$query_url\x20.}}\">{{html\x20.}}</a>\x0a\x09\x09{{end}}\x0a\x09\x09{{if\x20not\x20$.Complete}}\x0a\x09\x09\x09...\x0a\x09\x09{{end}}\x0a\x09\x09</td>\x0a\x09\x09</tr>\x0a\x09{{end}}\x0a\x09{{if\x20not\x20$.Complete}}\x0a\x09\x09<tr><td\x20align=\"left\">...</td></tr>\x0a\x09{{end}}\x0a\x09</table>\x0a\x09</p>\x0a{{end}}\x0a",
 
-	"style.css": "body\x20{\x0a\x09margin:\x200;\x0a\x09font-family:\x20Arial,\x20sans-serif;\x0a\x09background-color:\x20#fff;\x0a\x09line-height:\x201.3;\x0a\x09text-align:\x20center;\x0a\x09color:\x20#222;\x0a}\x0atextarea\x20{\x0a\x09/*\x20Inherit\x20text\x20color\x20from\x20body\x20avoiding\x20illegible\x20text\x20in\x20the\x20case\x20where\x20the\x0a\x20\x09*\x20user\x20has\x20inverted\x20the\x20browsers\x20custom\x20text\x20and\x20background\x20colors.\x20*/\x0a\x09color:\x20inherit;\x0a}\x0apre,\x0acode\x20{\x0a\x09font-family:\x20Menlo,\x20monospace;\x0a\x09font-size:\x200.875rem;\x0a}\x0apre\x20{\x0a\x09line-height:\x201.4;\x0a\x09overflow-x:\x20auto;\x0a}\x0apre\x20.comment\x20{\x0a\x09color:\x20#006600;\x0a}\x0apre\x20.highlight,\x0apre\x20.highlight-comment,\x0apre\x20.selection-highlight,\x0apre\x20.selection-highlight-comment\x20{\x0a\x09background:\x20#FFFF00;\x0a}\x0apre\x20.selection,\x0apre\x20.selection-comment\x20{\x0a\x09background:\x20#FF9632;\x0a}\x0apre\x20.ln\x20{\x0a\x09color:\x20#999;\x0a\x09background:\x20#efefef;\x0a}\x0a.ln\x20{\x0a\x09-webkit-user-select:\x20none;\x0a\x09-moz-user-select:\x20none;\x0a\x09-ms-user-select:\x20none;\x0a\x09user-select:\x20none;\x0a\x0a\x09/*\x20Ensure\x208\x20characters\x20in\x20the\x20document\x20-\x20which\x20due\x20to\x20floating\x0a\x20\x20\x20*\x20point\x20rendering\x20issues,\x20might\x20have\x20a\x20width\x20of\x20less\x20than\x201\x20each\x20-\x20are\x208\x0a\x20\x20\x20*\x20characters\x20wide,\x20so\x20a\x20tab\x20in\x20the\x209th\x20position\x20indents\x20properly.\x20See\x0a\x20\x20\x20*\x20https://github.com/webcompat/web-bugs/issues/17530#issuecomment-402675091\x0a\x20\x20\x20*\x20for\x20more\x20information.\x20*/\x0a\x09display:\x20inline-block;\x0a\x09width:\x208ch;\x0a}\x0a\x0a.search-nav{\x0a\x09margin-left:\x201.25rem;\x0a\x09font-size:\x200.875rem;\x0a\x09column-gap:\x201.25rem;\x0a\x09column-fill:\x20auto;\x0a\x09column-width:\x2014rem;\x0a}\x0a\x0a.search-nav\x20.indent\x20{\x0a\x09margin-left:\x201.25rem;\x0a}\x0a\x0aa,\x0a.exampleHeading\x20.text,\x0a.expandAll\x20{\x0a\x09color:\x20#375EAB;\x0a\x09text-decoration:\x20none;\x0a}\x0aa:hover,\x0a.exampleHeading\x20.text:hover,\x0a.expandAll:hover\x20{\x0a\x09text-decoration:\x20underline;\x0a}\x0a.article\x20a\x20{\x0a\x09text-decoration:\x20underline;\x0a}\x0a.article\x20.title\x20a\x20{\x0a\x09text-decoration:\x20none;\x0a}\x0a\x0a.permalink\x20{\x0a\x09display:\x20none;\x0a}\x0a:hover\x20>\x20.permalink\x20{\x0a\x09display:\x20inline;\x0a}\x0a\x0ap,\x20li\x20{\x0a\x09max-width:\x2050rem;\x0a\x09word-wrap:\x20break-word;\x0a}\x0ap,\x0apre,\x0aul,\x0aol\x20{\x0a\x09margin:\x201.25rem;\x0a}\x0apre\x20{\x0a\x09background:\x20#EFEFEF;\x0a\x09padding:\x200.625rem;\x0a\x09border-radius:\x200.3125rem;\x0a}\x0a\x0ah1,\x0ah2,\x0ah3,\x0ah4,\x0a.rootHeading\x20{\x0a\x09margin:\x201.25rem\x200\x201.25rem;\x0a\x09padding:\x200;\x0a\x09color:\x20#375EAB;\x0a\x09font-weight:\x20bold;\x0a}\x0ah1\x20{\x0a\x09font-size:\x201.75rem;\x0a\x09line-height:\x201;\x0a}\x0ah1\x20.text-muted\x20{\x0a\x09color:#777;\x0a}\x0ah2\x20{\x0a\x09font-size:\x201.25rem;\x0a\x09background:\x20#E0EBF5;\x0a\x09padding:\x200.5rem;\x0a\x09line-height:\x201.25;\x0a\x09font-weight:\x20normal;\x0a\x09overflow:\x20auto;\x0a\x09overflow-wrap:\x20break-word;\x0a}\x0ah2\x20a\x20{\x0a\x09font-weight:\x20bold;\x0a}\x0ah3\x20{\x0a\x09font-size:\x201.25rem;\x0a\x09line-height:\x201.25;\x0a\x09overflow:\x20auto;\x0a\x09overflow-wrap:\x20break-word;\x0a}\x0ah3,\x0ah4\x20{\x0a\x09margin:\x201.25rem\x200.3125rem;\x0a}\x0ah4\x20{\x0a\x09font-size:\x201rem;\x0a}\x0a.rootHeading\x20{\x0a\x09font-size:\x201.25rem;\x0a\x09margin:\x200;\x0a}\x0a\x0ah2\x20>\x20span,\x0ah3\x20>\x20span\x20{\x0a\x09float:\x20right;\x0a\x09margin:\x200\x2025px\x200\x200;\x0a\x09font-weight:\x20normal;\x0a\x09color:\x20#5279C7;\x0a}\x0a\x0adl\x20{\x0a\x09margin:\x201.25rem;\x0a}\x0add\x20{\x0a\x09margin:\x200\x200\x200\x201.25rem;\x0a}\x0adl,\x0add\x20{\x0a\x09font-size:\x200.875rem;\x0a}\x0adiv#nav\x20table\x20td\x20{\x0a\x09vertical-align:\x20top;\x0a}\x0a\x0a#pkg-index\x20h3\x20{\x0a\x09font-size:\x201rem;\x0a}\x0a.pkg-dir\x20{\x0a\x09padding:\x200\x200.625rem;\x0a}\x0a.pkg-dir\x20table\x20{\x0a\x09border-collapse:\x20collapse;\x0a\x09border-spacing:\x200;\x0a}\x0a.pkg-name\x20{\x0a\x09padding-right:\x200.625rem;\x0a}\x0a.alert\x20{\x0a\x09color:\x20#AA0000;\x0a}\x0a\x0a.top-heading\x20{\x0a\x09float:\x20left;\x0a\x09padding:\x201.313rem\x200;\x0a\x09font-size:\x201.25rem;\x0a\x09font-weight:\x20normal;\x0a}\x0a.top-heading\x20a\x20{\x0a\x09color:\x20#222;\x0a\x09text-decoration:\x20none;\x0a}\x0a\x0a#pkg-examples\x20h3\x20{\x0a\x09float:\x20left;\x0a}\x0a\x0a#pkg-examples\x20dl\x20{\x0a\x09clear:\x20both;\x0a}\x0a\x0a.expandAll\x20{\x0a\x09cursor:\x20pointer;\x0a\x09float:\x20left;\x0a\x09margin:\x201.25rem\x200;\x0a}\x0a\x0adiv#topbar\x20{\x0a\x09background:\x20#E0EBF5;\x0a\x09height:\x204rem;\x0a\x09overflow:\x20hidden;\x0a}\x0a\x0adiv#page\x20{\x0a\x09width:\x20100%;\x0a}\x0adiv#page\x20>\x20.container,\x0adiv#topbar\x20>\x20.container\x20{\x0a\x09text-align:\x20left;\x0a\x09margin-left:\x20auto;\x0a\x09margin-right:\x20auto;\x0a\x09padding:\x200\x201.25rem;\x0a}\x0adiv#topbar\x20>\x20.container,\x0adiv#page\x20>\x20.container\x20{\x0a\x09max-width:\x2059.38rem;\x0a}\x0adiv#page.wide\x20>\x20.container,\x0adiv#topbar.wide\x20>\x20.container\x20{\x0a\x09max-width:\x20none;\x0a}\x0adiv#plusone\x20{\x0a\x09float:\x20right;\x0a\x09clear:\x20right;\x0a\x09margin-top:\x200.3125rem;\x0a}\x0a\x0adiv#footer\x20{\x0a\x09text-align:\x20center;\x0a\x09color:\x20#666;\x0a\x09font-size:\x200.875rem;\x0a\x09margin:\x202.5rem\x200;\x0a}\x0a\x0adiv#menu\x20>\x20a,\x0ainput#search,\x0adiv#learn\x20.buttons\x20a,\x0adiv.play\x20.buttons\x20a,\x0adiv#blog\x20.read\x20a,\x0a#menu-button\x20{\x0a\x09padding:\x200.625rem;\x0a\x0a\x09text-decoration:\x20none;\x0a\x09font-size:\x201rem;\x0a\x09border-radius:\x200.3125rem;\x0a}\x0adiv#playground\x20.buttons\x20a,\x0adiv#menu\x20>\x20a,\x0ainput#search,\x0a#menu-button\x20{\x0a\x09border:\x200.0625rem\x20solid\x20#375EAB;\x0a}\x0adiv#playground\x20.buttons\x20a,\x0adiv#menu\x20>\x20a,\x0a#menu-button\x20{\x0a\x09color:\x20white;\x0a\x09background:\x20#375EAB;\x0a}\x0a#playgroundButton.active\x20{\x0a\x09background:\x20white;\x0a\x09color:\x20#375EAB;\x0a}\x0aa#start,\x0adiv#learn\x20.buttons\x20a,\x0adiv.play\x20.buttons\x20a,\x0adiv#blog\x20.read\x20a\x20{\x0a\x09color:\x20#222;\x0a\x09border:\x200.0625rem\x20solid\x20#375EAB;\x0a\x09background:\x20#E0EBF5;\x0a}\x0a.download\x20{\x0a\x09width:\x209.375rem;\x0a}\x0a\x0adiv#menu\x20{\x0a\x09text-align:\x20right;\x0a\x09padding:\x200.625rem;\x0a\x09white-space:\x20nowrap;\x0a\x09max-height:\x200;\x0a\x09-moz-transition:\x20max-height\x20.25s\x20linear;\x0a\x09transition:\x20max-height\x20.25s\x20linear;\x0a\x09width:\x20100%;\x0a}\x0adiv#menu.menu-visible\x20{\x0a\x09max-height:\x2031.25rem;\x0a}\x0adiv#menu\x20>\x20a,\x0a#menu-button\x20{\x0a\x09margin:\x200.625rem\x200.125rem;\x0a\x09padding:\x200.625rem;\x0a}\x0a::-webkit-input-placeholder\x20{\x0a\x09color:\x20#7f7f7f;\x0a\x09opacity:\x201;\x0a}\x0a::placeholder\x20{\x0a\x09color:\x20#7f7f7f;\x0a\x09opacity:\x201;\x0a}\x0a#menu\x20.search-box\x20{\x0a\x09display:\x20inline-flex;\x0a\x09width:\x208.75rem;\x0a}\x0ainput#search\x20{\x0a\x09background:\x20white;\x0a\x09color:\x20#222;\x0a\x09box-sizing:\x20border-box;\x0a\x09-webkit-appearance:\x20none;\x0a\x09border-top-right-radius:\x200;\x0a\x09border-bottom-right-radius:\x200;\x0a\x09border-right:\x200;\x0a\x09margin-right:\x200;\x0a\x09flex-grow:\x201;\x0a\x09max-width:\x20100%;\x0a\x09min-width:\x205.625rem;\x0a}\x0ainput#search:-webkit-search-decoration\x20{\x0a\x09-webkit-appearance:\x20none;\x0a}\x0ainput#search:-moz-ui-invalid\x20{\x0a\x09box-shadow:\x20unset;\x0a}\x0ainput#search\x20+\x20button\x20{\x0a\x09display:\x20inline;\x0a\x09font-size:\x201em;\x0a\x09background-color:\x20#375EAB;\x0a\x09color:\x20white;\x0a\x09border:\x200.0625rem\x20solid\x20#375EAB;\x0a\x09border-top-left-radius:\x200;\x0a\x09border-top-right-radius:\x200.3125rem;\x0a\x09border-bottom-left-radius:\x200;\x0a\x09border-bottom-right-radius:\x200.3125rem;\x0a\x09margin-left:\x200;\x0a\x09cursor:\x20pointer;\x0a}\x0ainput#search\x20+\x20button\x20span\x20{\x0a\x09display:\x20flex;\x0a}\x0ainput#search\x20+\x20button\x20svg\x20{\x0a\x09fill:\x20white\x0a}\x0a\x0a#menu-button\x20{\x0a\x09display:\x20none;\x0a\x09position:\x20absolute;\x0a\x09right:\x200.3125rem;\x0a\x09top:\x200;\x0a\x09margin-right:\x200.3125rem;\x0a}\x0a#menu-button-arrow\x20{\x0a\x09display:\x20inline-block;\x0a}\x0a.vertical-flip\x20{\x0a\x09transform:\x20rotate(-180deg);\x0a}\x0a\x0adiv.left\x20{\x0a\x09float:\x20left;\x0a\x09clear:\x20left;\x0a\x09margin-right:\x202.5%;\x0a}\x0adiv.right\x20{\x0a\x09float:\x20right;\x0a\x09clear:\x20right;\x0a\x09margin-left:\x202.5%;\x0a}\x0adiv.left,\x0adiv.right\x20{\x0a\x09width:\x2045%;\x0a}\x0a\x0adiv#learn,\x0adiv#about\x20{\x0a\x09padding-top:\x201.25rem;\x0a}\x0adiv#learn\x20h2,\x0adiv#about\x20{\x0a\x09margin:\x200;\x0a}\x0adiv#about\x20{\x0a\x09font-size:\x201.25rem;\x0a\x09margin:\x200\x20auto\x201.875rem;\x0a}\x0adiv#gopher\x20{\x0a\x09background:\x20url(/doc/gopher/frontpage.png)\x20no-repeat;\x0a\x09background-position:\x20center\x20top;\x0a\x09height:\x209.688rem;\x0a\x09max-height:\x20200px;\x20/*\x20Setting\x20in\x20px\x20to\x20prevent\x20the\x20gopher\x20from\x20blowing\x20up\x20in\x20very\x20high\x20default\x20font-sizes\x20*/\x0a}\x0aa#start\x20{\x0a\x09display:\x20block;\x0a\x09padding:\x200.625rem;\x0a\x0a\x09text-align:\x20center;\x0a\x09text-decoration:\x20none;\x0a\x09border-radius:\x200.3125rem;\x0a}\x0aa#start\x20.big\x20{\x0a\x09display:\x20block;\x0a\x09font-weight:\x20bold;\x0a\x09font-size:\x201.25rem;\x0a}\x0aa#start\x20.desc\x20{\x0a\x09display:\x20block;\x0a\x09font-size:\x200.875rem;\x0a\x09font-weight:\x20normal;\x0a\x09margin-top:\x200.3125rem;\x0a}\x0a\x0adiv#learn\x20.popout\x20{\x0a\x09float:\x20right;\x0a\x09display:\x20block;\x0a\x09cursor:\x20pointer;\x0a\x09font-size:\x200.75rem;\x0a\x09background:\x20url(/doc/share.png)\x20no-repeat;\x0a\x09background-position:\x20right\x20center;\x0a\x09padding:\x200.375rem\x201.688rem;\x0a}\x0adiv#learn\x20pre,\x0adiv#learn\x20textarea\x20{\x0a\x09padding:\x200;\x0a\x09margin:\x200;\x0a\x09font-family:\x20Menlo,\x20monospace;\x0a\x09font-size:\x200.875rem;\x0a}\x0adiv#learn\x20.input\x20{\x0a\x09padding:\x200.625rem;\x0a\x09margin-top:\x200.625rem;\x0a\x09height:\x209.375rem;\x0a\x0a\x09border-top-left-radius:\x200.3125rem;\x0a\x09border-top-right-radius:\x200.3125rem;\x0a}\x0adiv#learn\x20.input\x20textarea\x20{\x0a\x09width:\x20100%;\x0a\x09height:\x20100%;\x0a\x09border:\x20none;\x0a\x09outline:\x20none;\x0a\x09resize:\x20none;\x0a}\x0adiv#learn\x20.output\x20{\x0a\x09border-top:\x20none\x20!important;\x0a\x0a\x09padding:\x200.625rem;\x0a\x09height:\x203.688rem;\x0a\x09overflow:\x20auto;\x0a\x0a\x09border-bottom-right-radius:\x200.3125rem;\x0a\x09border-bottom-left-radius:\x200.3125rem;\x0a}\x0adiv#learn\x20.output\x20pre\x20{\x0a\x09padding:\x200;\x0a\x09border-radius:\x200;\x0a}\x0adiv#learn\x20.input,\x0adiv#learn\x20.input\x20textarea,\x0adiv#learn\x20.output,\x0adiv#learn\x20.output\x20pre\x20{\x0a\x09background:\x20#FFFFD8;\x0a}\x0adiv#learn\x20.input,\x0adiv#learn\x20.output\x20{\x0a\x09border:\x200.0625rem\x20solid\x20#375EAB;\x0a}\x0adiv#learn\x20.buttons\x20{\x0a\x09float:\x20right;\x0a\x09padding:\x201.25rem\x200\x200.625rem\x200;\x0a\x09text-align:\x20right;\x0a}\x0adiv#learn\x20.buttons\x20a\x20{\x0a\x09height:\x201rem;\x0a\x09margin-left:\x200.3125rem;\x0a\x09padding:\x200.625rem;\x0a}\x0adiv#learn\x20.toys\x20{\x0a\x09margin-top:\x200.5rem;\x0a}\x0adiv#learn\x20.toys\x20select\x20{\x0a\x09font-size:\x200.875rem;\x0a\x09border:\x200.0625rem\x20solid\x20#375EAB;\x0a\x09margin:\x200;\x0a}\x0adiv#learn\x20.output\x20.exit\x20{\x0a\x09display:\x20none;\x0a}\x0a\x0adiv#video\x20{\x0a\x09max-width:\x20100%;\x0a}\x0adiv#blog,\x0adiv#video\x20{\x0a\x09margin-top:\x202.5rem;\x0a}\x0adiv#blog\x20>\x20a,\x0adiv#blog\x20>\x20div,\x0adiv#blog\x20>\x20h2,\x0adiv#video\x20>\x20a,\x0adiv#video\x20>\x20div,\x0adiv#video\x20>\x20h2\x20{\x0a\x09margin-bottom:\x200.625rem;\x0a}\x0adiv#blog\x20.title,\x0adiv#video\x20.title\x20{\x0a\x09display:\x20block;\x0a\x09font-size:\x201.25rem;\x0a}\x0adiv#blog\x20.when\x20{\x0a\x09color:\x20#666;\x0a\x09font-size:\x200.875rem;\x0a}\x0adiv#blog\x20.read\x20{\x0a\x09text-align:\x20right;\x0a}\x0a\x0a@supports\x20(--c:\x200)\x20{\x0a\x09[style*=\"--aspect-ratio-padding:\"]\x20{\x0a\x09\x09position:\x20relative;\x0a\x09\x09overflow:\x20hidden;\x0a\x09\x09padding-top:\x20var(--aspect-ratio-padding);\x0a\x09}\x0a\x0a\x09[style*=\"--aspect-ratio-padding:\"]>*\x20{\x0a\x09\x09position:\x20absolute;\x0a\x09\x09top:\x200;\x0a\x09\x09left:\x200;\x0a\x09\x09width:\x20100%;\x0a\x09\x09height:\x20100%;\x0a\x09}\x0a}\x0a\x0a.toggleButton\x20{\x20cursor:\x20pointer;\x20}\x0a.toggle\x20>\x20.collapsed\x20{\x20display:\x20block;\x20}\x0a.toggle\x20>\x20.expanded\x20{\x20display:\x20none;\x20}\x0a.toggleVisible\x20>\x20.collapsed\x20{\x20display:\x20none;\x20}\x0a.toggleVisible\x20>\x20.expanded\x20{\x20display:\x20block;\x20}\x0a\x0atable.codetable\x20{\x20margin-left:\x20auto;\x20margin-right:\x20auto;\x20border-style:\x20none;\x20}\x0atable.codetable\x20td\x20{\x20padding-right:\x200.625rem;\x20}\x0ahr\x20{\x20border-style:\x20none;\x20border-top:\x200.0625rem\x20solid\x20black;\x20}\x0a\x0aimg.gopher\x20{\x0a\x09float:\x20right;\x0a\x09margin-left:\x200.625rem;\x0a\x09margin-bottom:\x200.625rem;\x0a\x09z-index:\x20-1;\x0a}\x0ah2\x20{\x20clear:\x20right;\x20}\x0a\x0a/*\x20example\x20and\x20drop-down\x20playground\x20*/\x0adiv.play\x20{\x0a\x09padding:\x200\x201.25rem\x202.5rem\x201.25rem;\x0a}\x0adiv.play\x20pre,\x0adiv.play\x20textarea,\x0adiv.play\x20.lines\x20{\x0a\x09padding:\x200;\x0a\x09margin:\x200;\x0a\x09font-family:\x20Menlo,\x20monospace;\x0a\x09font-size:\x200.875rem;\x0a}\x0adiv.play\x20.input\x20{\x0a\x09padding:\x200.625rem;\x0a\x09margin-top:\x200.625rem;\x0a\x0a\x09border-top-left-radius:\x200.3125rem;\x0a\x09border-top-right-radius:\x200.3125rem;\x0a\x0a\x09overflow:\x20hidden;\x0a}\x0adiv.play\x20.input\x20textarea\x20{\x0a\x09width:\x20100%;\x0a\x09height:\x20100%;\x0a\x09border:\x20none;\x0a\x09outline:\x20none;\x0a\x09resize:\x20none;\x0a\x0a\x09overflow:\x20hidden;\x0a}\x0adiv#playground\x20.input\x20textarea\x20{\x0a\x09overflow:\x20auto;\x0a\x09resize:\x20auto;\x0a}\x0adiv.play\x20.output\x20{\x0a\x09border-top:\x20none\x20!important;\x0a\x0a\x09padding:\x200.625rem;\x0a\x09max-height:\x2012.5rem;\x0a\x09overflow:\x20auto;\x0a\x0a\x09border-bottom-right-radius:\x200.3125rem;\x0a\x09border-bottom-left-radius:\x200.3125rem;\x0a}\x0adiv.play\x20.output\x20pre\x20{\x0a\x09padding:\x200;\x0a\x09border-radius:\x200;\x0a}\x0adiv.play\x20.input,\x0adiv.play\x20.input\x20textarea,\x0adiv.play\x20.output,\x0adiv.play\x20.output\x20pre\x20{\x0a\x09background:\x20#FFFFD8;\x0a}\x0adiv.play\x20.input,\x0adiv.play\x20.output\x20{\x0a\x09border:\x200.0625rem\x20solid\x20#375EAB;\x0a}\x0adiv.play\x20.buttons\x20{\x0a\x09float:\x20right;\x0a\x09padding:\x201.25rem\x200\x200.625rem\x200;\x0a\x09text-align:\x20right;\x0a}\x0adiv.play\x20.buttons\x20a\x20{\x0a\x09height:\x201rem;\x0a\x09margin-left:\x200.3125rem;\x0a\x09padding:\x200.625rem;\x0a\x09cursor:\x20pointer;\x0a}\x0a.output\x20.stderr\x20{\x0a\x09color:\x20#933;\x0a}\x0a.output\x20.system\x20{\x0a\x09color:\x20#999;\x0a}\x0a\x0a/*\x20drop-down\x20playground\x20*/\x0adiv#playground\x20{\x0a\x09/*\x20start\x20hidden;\x20revealed\x20by\x20javascript\x20*/\x0a\x09display:\x20none;\x0a}\x0adiv#playground\x20{\x0a\x09position:\x20absolute;\x0a\x09top:\x203.938rem;\x0a\x09right:\x201.25rem;\x0a\x09padding:\x200\x200.625rem\x200.625rem\x200.625rem;\x0a\x09z-index:\x201;\x0a\x09text-align:\x20left;\x0a\x09background:\x20#E0EBF5;\x0a\x0a\x09border:\x200.0625rem\x20solid\x20#B0BBC5;\x0a\x09border-top:\x20none;\x0a\x0a\x09border-bottom-left-radius:\x200.3125rem;\x0a\x09border-bottom-right-radius:\x200.3125rem;\x0a}\x0adiv#playground\x20.code\x20{\x0a\x09width:\x2032.5rem;\x0a\x09height:\x2012.5rem;\x0a}\x0adiv#playground\x20.output\x20{\x0a\x09height:\x206.25rem;\x0a}\x0a\x0a/*\x20Inline\x20runnable\x20snippets\x20(play.js/initPlayground)\x20*/\x0a#content\x20.code\x20pre,\x20#content\x20.playground\x20pre,\x20#content\x20.output\x20pre\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20margin:\x200;\x0a\x20\x20\x20\x20\x20\x20\x20\x20padding:\x200;\x0a\x20\x20\x20\x20\x20\x20\x20\x20background:\x20none;\x0a\x20\x20\x20\x20\x20\x20\x20\x20border:\x20none;\x0a\x09outline:\x200\x20solid\x20transparent;\x0a\x20\x20\x20\x20\x20\x20\x20\x20overflow:\x20auto;\x0a}\x0a#content\x20.playground\x20.number,\x20#content\x20.code\x20.number\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20color:\x20#999;\x0a}\x0a#content\x20.code,\x20#content\x20.playground,\x20#content\x20.output\x20{\x0a\x09width:\x20auto;\x0a\x20\x20\x20\x20\x20\x20\x20\x20margin:\x201.25rem;\x0a\x20\x20\x20\x20\x20\x20\x20\x20padding:\x200.625rem;\x0a\x20\x20\x20\x20\x20\x20\x20\x20border-radius:\x200.3125rem;\x0a}\x0a#content\x20.code,\x20#content\x20.playground\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20background:\x20#e9e9e9;\x0a}\x0a#content\x20.output\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20background:\x20#202020;\x0a}\x0a#content\x20.output\x20.stdout,\x20#content\x20.output\x20pre\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20color:\x20#e6e6e6;\x0a}\x0a#content\x20.output\x20.stderr,\x20#content\x20.output\x20.error\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20color:\x20rgb(244,\x2074,\x2063);\x0a}\x0a#content\x20.output\x20.system,\x20#content\x20.output\x20.exit\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20color:\x20rgb(255,\x20209,\x2077)\x0a}\x0a#content\x20.buttons\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20position:\x20relative;\x0a\x20\x20\x20\x20\x20\x20\x20\x20float:\x20right;\x0a\x20\x20\x20\x20\x20\x20\x20\x20top:\x20-3.125rem;\x0a\x20\x20\x20\x20\x20\x20\x20\x20right:\x201.875rem;\x0a}\x0a#content\x20.output\x20.buttons\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20top:\x20-3.75rem;\x0a\x20\x20\x20\x20\x20\x20\x20\x20right:\x200;\x0a\x20\x20\x20\x20\x20\x20\x20\x20height:\x200;\x0a}\x0a#content\x20.buttons\x20.kill\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20display:\x20none;\x0a\x20\x20\x20\x20\x20\x20\x20\x20visibility:\x20hidden;\x0a}\x0aa.error\x20{\x0a\x09font-weight:\x20bold;\x0a\x20\x20\x20\x20\x20\x20\x20\x20color:\x20white;\x0a\x09background-color:\x20darkred;\x0a\x20\x20\x20\x20\x20\x20\x20\x20border-bottom-left-radius:\x200.25rem;\x0a\x20\x20\x20\x20\x20\x20\x20\x20border-bottom-right-radius:\x200.25rem;\x0a\x20\x20\x20\x20\x20\x20\x20\x20border-top-left-radius:\x200.25rem;\x0a\x20\x20\x20\x20\x20\x20\x20\x20border-top-right-radius:\x200.25rem;\x0a\x20\x20\x20\x20\x20\x20\x20\x20padding:\x200.125rem\x200.25rem\x200.125rem\x200.25rem;\x20/*\x20TRBL\x20*/\x0a}\x0a\x0a\x0a#heading-narrow\x20{\x0a\x09display:\x20none;\x0a}\x0a\x0a.downloading\x20{\x0a\x09background:\x20#F9F9BE;\x0a\x09padding:\x200.625rem;\x0a\x09text-align:\x20center;\x0a\x09border-radius:\x200.3125rem;\x0a}\x0a\x0a@media\x20(max-width:\x2058.125em)\x20{\x0a\x09#heading-wide\x20{\x0a\x09\x09display:\x20none;\x0a\x09}\x0a\x09#heading-narrow\x20{\x0a\x09\x09display:\x20block;\x0a\x09}\x0a}\x0a\x0a@media\x20(max-width:\x2047.5em)\x20{\x0a\x09.container\x20.left,\x0a\x09.container\x20.right\x20{\x0a\x09\x09width:\x20auto;\x0a\x09\x09float:\x20none;\x0a\x09}\x0a\x0a\x09div#about\x20{\x0a\x09\x09max-width:\x2031.25rem;\x0a\x09\x09text-align:\x20center;\x0a\x09}\x0a}\x0a\x0a@media\x20(min-width:\x2043.75em)\x20and\x20(max-width:\x2062.5em)\x20{\x0a\x09div#menu\x20>\x20a\x20{\x0a\x09\x09margin:\x200.3125rem\x200;\x0a\x09\x09font-size:\x200.875rem;\x0a\x09}\x0a\x0a\x09input#search\x20{\x0a\x09\x09font-size:\x200.875rem;\x0a\x09}\x0a}\x0a\x0a@media\x20(max-width:\x2043.75em)\x20{\x0a\x09body\x20{\x0a\x09\x09font-size:\x200.9375rem;\x0a\x09}\x0a\x0a\x09div#playground\x20{\x0a\x09\x09left:\x200;\x0a\x09\x09right:\x200;\x0a\x09}\x0a\x0a\x09pre,\x0a\x09code\x20{\x0a\x09\x09font-size:\x200.866rem;\x0a\x09}\x0a\x0a\x09div#page\x20>\x20.container\x20{\x0a\x09\x09padding:\x200\x200.625rem;\x0a\x09}\x0a\x0a\x09div#topbar\x20{\x0a\x09\x09height:\x20auto;\x0a\x09\x09padding:\x200.625rem;\x0a\x09}\x0a\x0a\x09div#topbar\x20>\x20.container\x20{\x0a\x09\x09padding:\x200;\x0a\x09}\x0a\x0a\x09#heading-wide\x20{\x0a\x09\x09display:\x20block;\x0a\x09}\x0a\x09#heading-narrow\x20{\x0a\x09\x09display:\x20none;\x0a\x09}\x0a\x0a\x09.top-heading\x20{\x0a\x09\x09float:\x20none;\x0a\x09\x09display:\x20inline-block;\x0a\x09\x09padding:\x200.75rem;\x0a\x09}\x0a\x0a\x09div#menu\x20{\x0a\x09\x09padding:\x200;\x0a\x09\x09min-width:\x200;\x0a\x09\x09text-align:\x20left;\x0a\x09\x09float:\x20left;\x0a\x09}\x0a\x0a\x09div#menu\x20>\x20a\x20{\x0a\x09\x09display:\x20block;\x0a\x09\x09margin-left:\x200;\x0a\x09\x09margin-right:\x200;\x0a\x09}\x0a\x0a\x09#menu\x20.search-box\x20{\x0a\x09\x09display:\x20flex;\x0a\x09\x09width:\x20100%;\x0a\x09}\x0a\x0a\x09#menu-button\x20{\x0a\x09\x09display:\x20inline-block;\x0a\x09}\x0a\x0a\x09p,\x0a\x09pre,\x0a\x09ul,\x0a\x09ol\x20{\x0a\x09\x09margin:\x200.625rem;\x0a\x09}\x0a\x0a\x09.pkg-synopsis\x20{\x0a\x09\x09display:\x20none;\x0a\x09}\x0a\x0a\x09img.gopher\x20{\x0a\x09\x09display:\x20none;\x0a\x09}\x0a}\x0a\x0a@media\x20(max-width:\x2030em)\x20{\x0a\x09#heading-wide\x20{\x0a\x09\x09display:\x20none;\x0a\x09}\x0a\x09#heading-narrow\x20{\x0a\x09\x09display:\x20block;\x0a\x09}\x0a}\x0a\x0a@media\x20print\x20{\x0a\x09pre\x20{\x0a\x09\x09background:\x20#FFF;\x0a\x09\x09border:\x200.0625rem\x20solid\x20#BBB;\x0a\x09\x09white-space:\x20pre-wrap;\x0a\x09}\x0a}\x0a",
+	"style.css": "body\x20{\x0a\x20\x20margin:\x200;\x0a\x20\x20font-family:\x20Arial,\x20sans-serif;\x0a\x20\x20background-color:\x20#fff;\x0a\x20\x20line-height:\x201.3;\x0a\x20\x20text-align:\x20center;\x0a\x20\x20color:\x20#222;\x0a}\x0atextarea\x20{\x0a\x20\x20/*\x20Inherit\x20text\x20color\x20from\x20body\x20avoiding\x20illegible\x20text\x20in\x20the\x20case\x20where\x20the\x0a\x20\x09*\x20user\x20has\x20inverted\x20the\x20browsers\x20custom\x20text\x20and\x20background\x20colors.\x20*/\x0a\x20\x20color:\x20inherit;\x0a}\x0apre,\x0acode\x20{\x0a\x20\x20font-family:\x20Menlo,\x20monospace;\x0a\x20\x20font-size:\x200.875rem;\x0a}\x0apre\x20{\x0a\x20\x20line-height:\x201.4;\x0a\x20\x20overflow-x:\x20auto;\x0a}\x0apre\x20.comment\x20{\x0a\x20\x20color:\x20#006600;\x0a}\x0apre\x20.highlight,\x0apre\x20.highlight-comment,\x0apre\x20.selection-highlight,\x0apre\x20.selection-highlight-comment\x20{\x0a\x20\x20background:\x20#ffff00;\x0a}\x0apre\x20.selection,\x0apre\x20.selection-comment\x20{\x0a\x20\x20background:\x20#ff9632;\x0a}\x0apre\x20.ln\x20{\x0a\x20\x20color:\x20#999;\x0a\x20\x20background:\x20#efefef;\x0a}\x0a.ln\x20{\x0a\x20\x20-webkit-user-select:\x20none;\x0a\x20\x20-moz-user-select:\x20none;\x0a\x20\x20-ms-user-select:\x20none;\x0a\x20\x20user-select:\x20none;\x0a\x0a\x20\x20/*\x20Ensure\x208\x20characters\x20in\x20the\x20document\x20-\x20which\x20due\x20to\x20floating\x0a\x20\x20\x20*\x20point\x20rendering\x20issues,\x20might\x20have\x20a\x20width\x20of\x20less\x20than\x201\x20each\x20-\x20are\x208\x0a\x20\x20\x20*\x20characters\x20wide,\x20so\x20a\x20tab\x20in\x20the\x209th\x20position\x20indents\x20properly.\x20See\x0a\x20\x20\x20*\x20https://github.com/webcompat/web-bugs/issues/17530#issuecomment-402675091\x0a\x20\x20\x20*\x20for\x20more\x20information.\x20*/\x0a\x20\x20display:\x20inline-block;\x0a\x20\x20width:\x208ch;\x0a}\x0a\x0a.search-nav\x20{\x0a\x20\x20margin-left:\x201.25rem;\x0a\x20\x20font-size:\x200.875rem;\x0a\x20\x20column-gap:\x201.25rem;\x0a\x20\x20column-fill:\x20auto;\x0a\x20\x20column-width:\x2014rem;\x0a}\x0a\x0a.search-nav\x20.indent\x20{\x0a\x20\x20margin-left:\x201.25rem;\x0a}\x0a\x0aa,\x0a.exampleHeading\x20.text,\x0a.expandAll\x20{\x0a\x20\x20color:\x20#375eab;\x0a\x20\x20text-decoration:\x20none;\x0a}\x0aa:hover,\x0a.exampleHeading\x20.text:hover,\x0a.expandAll:hover\x20{\x0a\x20\x20text-decoration:\x20underline;\x0a}\x0a.article\x20a\x20{\x0a\x20\x20text-decoration:\x20underline;\x0a}\x0a.article\x20.title\x20a\x20{\x0a\x20\x20text-decoration:\x20none;\x0a}\x0a\x0a.permalink\x20{\x0a\x20\x20display:\x20none;\x0a}\x0a:hover\x20>\x20.permalink\x20{\x0a\x20\x20display:\x20inline;\x0a}\x0a\x0ap,\x0ali\x20{\x0a\x20\x20max-width:\x2050rem;\x0a\x20\x20word-wrap:\x20break-word;\x0a}\x0ap,\x0apre,\x0aul,\x0aol\x20{\x0a\x20\x20margin:\x201.25rem;\x0a}\x0apre\x20{\x0a\x20\x20background:\x20#efefef;\x0a\x20\x20padding:\x200.625rem;\x0a\x20\x20border-radius:\x200.3125rem;\x0a}\x0a\x0ah1,\x0ah2,\x0ah3,\x0ah4,\x0a.rootHeading\x20{\x0a\x20\x20margin:\x201.25rem\x200\x201.25rem;\x0a\x20\x20padding:\x200;\x0a\x20\x20color:\x20#375eab;\x0a\x20\x20font-weight:\x20bold;\x0a}\x0ah1\x20{\x0a\x20\x20font-size:\x201.75rem;\x0a\x20\x20line-height:\x201;\x0a}\x0ah1\x20.text-muted\x20{\x0a\x20\x20color:\x20#777;\x0a}\x0ah2\x20{\x0a\x20\x20font-size:\x201.25rem;\x0a\x20\x20background:\x20#e0ebf5;\x0a\x20\x20padding:\x200.5rem;\x0a\x20\x20line-height:\x201.25;\x0a\x20\x20font-weight:\x20normal;\x0a\x20\x20overflow:\x20auto;\x0a\x20\x20overflow-wrap:\x20break-word;\x0a}\x0ah2\x20a\x20{\x0a\x20\x20font-weight:\x20bold;\x0a}\x0ah3\x20{\x0a\x20\x20font-size:\x201.25rem;\x0a\x20\x20line-height:\x201.25;\x0a\x20\x20overflow:\x20auto;\x0a\x20\x20overflow-wrap:\x20break-word;\x0a}\x0ah3,\x0ah4\x20{\x0a\x20\x20margin:\x201.25rem\x200.3125rem;\x0a}\x0ah4\x20{\x0a\x20\x20font-size:\x201rem;\x0a}\x0a.rootHeading\x20{\x0a\x20\x20font-size:\x201.25rem;\x0a\x20\x20margin:\x200;\x0a}\x0a\x0ah2\x20>\x20span,\x0ah3\x20>\x20span\x20{\x0a\x20\x20float:\x20right;\x0a\x20\x20margin:\x200\x2025px\x200\x200;\x0a\x20\x20font-weight:\x20normal;\x0a\x20\x20color:\x20#5279c7;\x0a}\x0a\x0adl\x20{\x0a\x20\x20margin:\x201.25rem;\x0a}\x0add\x20{\x0a\x20\x20margin:\x200\x200\x200\x201.25rem;\x0a}\x0adl,\x0add\x20{\x0a\x20\x20font-size:\x200.875rem;\x0a}\x0adiv#nav\x20table\x20td\x20{\x0a\x20\x20vertical-align:\x20top;\x0a}\x0a\x0a#pkg-index\x20h3\x20{\x0a\x20\x20font-size:\x201rem;\x0a}\x0a.pkg-dir\x20{\x0a\x20\x20padding:\x200\x200.625rem;\x0a}\x0a.pkg-dir\x20table\x20{\x0a\x20\x20border-collapse:\x20collapse;\x0a\x20\x20border-spacing:\x200;\x0a}\x0a.pkg-name\x20{\x0a\x20\x20padding-right:\x200.625rem;\x0a}\x0a.alert\x20{\x0a\x20\x20color:\x20#aa0000;\x0a}\x0a\x0a.top-heading\x20{\x0a\x20\x20float:\x20left;\x0a\x20\x20padding:\x201.313rem\x200;\x0a\x20\x20font-size:\x201.25rem;\x0a\x20\x20font-weight:\x20normal;\x0a}\x0a.top-heading\x20a\x20{\x0a\x20\x20color:\x20#222;\x0a\x20\x20text-decoration:\x20none;\x0a}\x0a\x0a#pkg-examples\x20h3\x20{\x0a\x20\x20float:\x20left;\x0a}\x0a\x0a#pkg-examples\x20dl\x20{\x0a\x20\x20clear:\x20both;\x0a}\x0a\x0a.expandAll\x20{\x0a\x20\x20cursor:\x20pointer;\x0a\x20\x20float:\x20left;\x0a\x20\x20margin:\x201.25rem\x200;\x0a}\x0a\x0adiv#topbar\x20{\x0a\x20\x20background:\x20#e0ebf5;\x0a\x20\x20height:\x204rem;\x0a\x20\x20overflow:\x20hidden;\x0a}\x0a\x0adiv#page\x20{\x0a\x20\x20width:\x20100%;\x0a}\x0adiv#page\x20>\x20.container,\x0adiv#topbar\x20>\x20.container\x20{\x0a\x20\x20text-align:\x20left;\x0a\x20\x20margin-left:\x20auto;\x0a\x20\x20margin-right:\x20auto;\x0a\x20\x20padding:\x200\x201.25rem;\x0a}\x0adiv#topbar\x20>\x20.container,\x0adiv#page\x20>\x20.container\x20{\x0a\x20\x20max-width:\x2059.38rem;\x0a}\x0adiv#page.wide\x20>\x20.container,\x0adiv#topbar.wide\x20>\x20.container\x20{\x0a\x20\x20max-width:\x20none;\x0a}\x0adiv#plusone\x20{\x0a\x20\x20float:\x20right;\x0a\x20\x20clear:\x20right;\x0a\x20\x20margin-top:\x200.3125rem;\x0a}\x0a\x0adiv#footer\x20{\x0a\x20\x20text-align:\x20center;\x0a\x20\x20color:\x20#666;\x0a\x20\x20font-size:\x200.875rem;\x0a\x20\x20margin:\x202.5rem\x200;\x0a}\x0a\x0adiv#menu\x20>\x20a,\x0ainput#search,\x0adiv#learn\x20.buttons\x20a,\x0adiv.play\x20.buttons\x20a,\x0adiv#blog\x20.read\x20a,\x0a#menu-button\x20{\x0a\x20\x20padding:\x200.625rem;\x0a\x0a\x20\x20text-decoration:\x20none;\x0a\x20\x20font-size:\x201rem;\x0a\x20\x20border-radius:\x200.3125rem;\x0a}\x0adiv#playground\x20.buttons\x20a,\x0adiv#menu\x20>\x20a,\x0ainput#search,\x0a#menu-button\x20{\x0a\x20\x20border:\x200.0625rem\x20solid\x20#375eab;\x0a}\x0adiv#playground\x20.buttons\x20a,\x0adiv#menu\x20>\x20a,\x0a#menu-button\x20{\x0a\x20\x20color:\x20white;\x0a\x20\x20background:\x20#375eab;\x0a}\x0a#playgroundButton.active\x20{\x0a\x20\x20background:\x20white;\x0a\x20\x20color:\x20#375eab;\x0a}\x0aa#start,\x0adiv#learn\x20.buttons\x20a,\x0adiv.play\x20.buttons\x20a,\x0adiv#blog\x20.read\x20a\x20{\x0a\x20\x20color:\x20#222;\x0a\x20\x20border:\x200.0625rem\x20solid\x20#375eab;\x0a\x20\x20background:\x20#e0ebf5;\x0a}\x0a.download\x20{\x0a\x20\x20width:\x209.375rem;\x0a}\x0a\x0adiv#menu\x20{\x0a\x20\x20text-align:\x20right;\x0a\x20\x20padding:\x200.625rem;\x0a\x20\x20white-space:\x20nowrap;\x0a\x20\x20max-height:\x200;\x0a\x20\x20-moz-transition:\x20max-height\x200.25s\x20linear;\x0a\x20\x20transition:\x20max-height\x200.25s\x20linear;\x0a\x20\x20width:\x20100%;\x0a}\x0adiv#menu.menu-visible\x20{\x0a\x20\x20max-height:\x2031.25rem;\x0a}\x0adiv#menu\x20>\x20a,\x0a#menu-button\x20{\x0a\x20\x20margin:\x200.625rem\x200.125rem;\x0a\x20\x20padding:\x200.625rem;\x0a}\x0a::-webkit-input-placeholder\x20{\x0a\x20\x20color:\x20#7f7f7f;\x0a\x20\x20opacity:\x201;\x0a}\x0a::placeholder\x20{\x0a\x20\x20color:\x20#7f7f7f;\x0a\x20\x20opacity:\x201;\x0a}\x0a#menu\x20.search-box\x20{\x0a\x20\x20display:\x20inline-flex;\x0a\x20\x20width:\x208.75rem;\x0a}\x0ainput#search\x20{\x0a\x20\x20background:\x20white;\x0a\x20\x20color:\x20#222;\x0a\x20\x20box-sizing:\x20border-box;\x0a\x20\x20-webkit-appearance:\x20none;\x0a\x20\x20border-top-right-radius:\x200;\x0a\x20\x20border-bottom-right-radius:\x200;\x0a\x20\x20border-right:\x200;\x0a\x20\x20margin-right:\x200;\x0a\x20\x20flex-grow:\x201;\x0a\x20\x20max-width:\x20100%;\x0a\x20\x20min-width:\x205.625rem;\x0a}\x0ainput#search:-webkit-search-decoration\x20{\x0a\x20\x20-webkit-appearance:\x20none;\x0a}\x0ainput#search:-moz-ui-invalid\x20{\x0a\x20\x20box-shadow:\x20unset;\x0a}\x0ainput#search\x20+\x20button\x20{\x0a\x20\x20display:\x20inline;\x0a\x20\x20font-size:\x201em;\x0a\x20\x20background-color:\x20#375eab;\x0a\x20\x20color:\x20white;\x0a\x20\x20border:\x200.0625rem\x20solid\x20#375eab;\x0a\x20\x20border-top-left-radius:\x200;\x0a\x20\x20border-top-right-radius:\x200.3125rem;\x0a\x20\x20border-bottom-left-radius:\x200;\x0a\x20\x20border-bottom-right-radius:\x200.3125rem;\x0a\x20\x20margin-left:\x200;\x0a\x20\x20cursor:\x20pointer;\x0a}\x0ainput#search\x20+\x20button\x20span\x20{\x0a\x20\x20display:\x20flex;\x0a}\x0ainput#search\x20+\x20button\x20svg\x20{\x0a\x20\x20fill:\x20white;\x0a}\x0a\x0a#menu-button\x20{\x0a\x20\x20display:\x20none;\x0a\x20\x20position:\x20absolute;\x0a\x20\x20right:\x200.3125rem;\x0a\x20\x20top:\x200;\x0a\x20\x20margin-right:\x200.3125rem;\x0a}\x0a#menu-button-arrow\x20{\x0a\x20\x20display:\x20inline-block;\x0a}\x0a.vertical-flip\x20{\x0a\x20\x20transform:\x20rotate(-180deg);\x0a}\x0a\x0adiv.left\x20{\x0a\x20\x20float:\x20left;\x0a\x20\x20clear:\x20left;\x0a\x20\x20margin-right:\x202.5%;\x0a}\x0adiv.right\x20{\x0a\x20\x20float:\x20right;\x0a\x20\x20clear:\x20right;\x0a\x20\x20margin-left:\x202.5%;\x0a}\x0adiv.left,\x0adiv.right\x20{\x0a\x20\x20width:\x2045%;\x0a}\x0a\x0adiv#learn,\x0adiv#about\x20{\x0a\x20\x20padding-top:\x201.25rem;\x0a}\x0adiv#learn\x20h2,\x0adiv#about\x20{\x0a\x20\x20margin:\x200;\x0a}\x0adiv#about\x20{\x0a\x20\x20font-size:\x201.25rem;\x0a\x20\x20margin:\x200\x20auto\x201.875rem;\x0a}\x0adiv#gopher\x20{\x0a\x20\x20background:\x20url(/doc/gopher/frontpage.png)\x20no-repeat;\x0a\x20\x20background-position:\x20center\x20top;\x0a\x20\x20height:\x209.688rem;\x0a\x20\x20max-height:\x20200px;\x20/*\x20Setting\x20in\x20px\x20to\x20prevent\x20the\x20gopher\x20from\x20blowing\x20up\x20in\x20very\x20high\x20default\x20font-sizes\x20*/\x0a}\x0aa#start\x20{\x0a\x20\x20display:\x20block;\x0a\x20\x20padding:\x200.625rem;\x0a\x0a\x20\x20text-align:\x20center;\x0a\x20\x20text-decoration:\x20none;\x0a\x20\x20border-radius:\x200.3125rem;\x0a}\x0aa#start\x20.big\x20{\x0a\x20\x20display:\x20block;\x0a\x20\x20font-weight:\x20bold;\x0a\x20\x20font-size:\x201.25rem;\x0a}\x0aa#start\x20.desc\x20{\x0a\x20\x20display:\x20block;\x0a\x20\x20font-size:\x200.875rem;\x0a\x20\x20font-weight:\x20normal;\x0a\x20\x20margin-top:\x200.3125rem;\x0a}\x0a\x0adiv#learn\x20.popout\x20{\x0a\x20\x20float:\x20right;\x0a\x20\x20display:\x20block;\x0a\x20\x20cursor:\x20pointer;\x0a\x20\x20font-size:\x200.75rem;\x0a\x20\x20background:\x20url(/doc/share.png)\x20no-repeat;\x0a\x20\x20background-position:\x20right\x20center;\x0a\x20\x20padding:\x200.375rem\x201.688rem;\x0a}\x0adiv#learn\x20pre,\x0adiv#learn\x20textarea\x20{\x0a\x20\x20padding:\x200;\x0a\x20\x20margin:\x200;\x0a\x20\x20font-family:\x20Menlo,\x20monospace;\x0a\x20\x20font-size:\x200.875rem;\x0a}\x0adiv#learn\x20.input\x20{\x0a\x20\x20padding:\x200.625rem;\x0a\x20\x20margin-top:\x200.625rem;\x0a\x20\x20height:\x209.375rem;\x0a\x0a\x20\x20border-top-left-radius:\x200.3125rem;\x0a\x20\x20border-top-right-radius:\x200.3125rem;\x0a}\x0adiv#learn\x20.input\x20textarea\x20{\x0a\x20\x20width:\x20100%;\x0a\x20\x20height:\x20100%;\x0a\x20\x20border:\x20none;\x0a\x20\x20outline:\x20none;\x0a\x20\x20resize:\x20none;\x0a}\x0adiv#learn\x20.output\x20{\x0a\x20\x20border-top:\x20none\x20!important;\x0a\x0a\x20\x20padding:\x200.625rem;\x0a\x20\x20height:\x203.688rem;\x0a\x20\x20overflow:\x20auto;\x0a\x0a\x20\x20border-bottom-right-radius:\x200.3125rem;\x0a\x20\x20border-bottom-left-radius:\x200.3125rem;\x0a}\x0adiv#learn\x20.output\x20pre\x20{\x0a\x20\x20padding:\x200;\x0a\x20\x20border-radius:\x200;\x0a}\x0adiv#learn\x20.input,\x0adiv#learn\x20.input\x20textarea,\x0adiv#learn\x20.output,\x0adiv#learn\x20.output\x20pre\x20{\x0a\x20\x20background:\x20#ffffd8;\x0a}\x0adiv#learn\x20.input,\x0adiv#learn\x20.output\x20{\x0a\x20\x20border:\x200.0625rem\x20solid\x20#375eab;\x0a}\x0adiv#learn\x20.buttons\x20{\x0a\x20\x20float:\x20right;\x0a\x20\x20padding:\x201.25rem\x200\x200.625rem\x200;\x0a\x20\x20text-align:\x20right;\x0a}\x0adiv#learn\x20.buttons\x20a\x20{\x0a\x20\x20height:\x201rem;\x0a\x20\x20margin-left:\x200.3125rem;\x0a\x20\x20padding:\x200.625rem;\x0a}\x0adiv#learn\x20.toys\x20{\x0a\x20\x20margin-top:\x200.5rem;\x0a}\x0adiv#learn\x20.toys\x20select\x20{\x0a\x20\x20font-size:\x200.875rem;\x0a\x20\x20border:\x200.0625rem\x20solid\x20#375eab;\x0a\x20\x20margin:\x200;\x0a}\x0adiv#learn\x20.output\x20.exit\x20{\x0a\x20\x20display:\x20none;\x0a}\x0a\x0adiv#video\x20{\x0a\x20\x20max-width:\x20100%;\x0a}\x0adiv#blog,\x0adiv#video\x20{\x0a\x20\x20margin-top:\x202.5rem;\x0a}\x0adiv#blog\x20>\x20a,\x0adiv#blog\x20>\x20div,\x0adiv#blog\x20>\x20h2,\x0adiv#video\x20>\x20a,\x0adiv#video\x20>\x20div,\x0adiv#video\x20>\x20h2\x20{\x0a\x20\x20margin-bottom:\x200.625rem;\x0a}\x0adiv#blog\x20.title,\x0adiv#video\x20.title\x20{\x0a\x20\x20display:\x20block;\x0a\x20\x20font-size:\x201.25rem;\x0a}\x0adiv#blog\x20.when\x20{\x0a\x20\x20color:\x20#666;\x0a\x20\x20font-size:\x200.875rem;\x0a}\x0adiv#blog\x20.read\x20{\x0a\x20\x20text-align:\x20right;\x0a}\x0a\x0a@supports\x20(--c:\x200)\x20{\x0a\x20\x20[style*='--aspect-ratio-padding:']\x20{\x0a\x20\x20\x20\x20position:\x20relative;\x0a\x20\x20\x20\x20overflow:\x20hidden;\x0a\x20\x20\x20\x20padding-top:\x20var(--aspect-ratio-padding);\x0a\x20\x20}\x0a\x0a\x20\x20[style*='--aspect-ratio-padding:']\x20>\x20*\x20{\x0a\x20\x20\x20\x20position:\x20absolute;\x0a\x20\x20\x20\x20top:\x200;\x0a\x20\x20\x20\x20left:\x200;\x0a\x20\x20\x20\x20width:\x20100%;\x0a\x20\x20\x20\x20height:\x20100%;\x0a\x20\x20}\x0a}\x0a\x0a.toggleButton\x20{\x0a\x20\x20cursor:\x20pointer;\x0a}\x0a.toggle\x20>\x20.collapsed\x20{\x0a\x20\x20display:\x20block;\x0a}\x0a.toggle\x20>\x20.expanded\x20{\x0a\x20\x20display:\x20none;\x0a}\x0a.toggleVisible\x20>\x20.collapsed\x20{\x0a\x20\x20display:\x20none;\x0a}\x0a.toggleVisible\x20>\x20.expanded\x20{\x0a\x20\x20display:\x20block;\x0a}\x0a\x0atable.codetable\x20{\x0a\x20\x20margin-left:\x20auto;\x0a\x20\x20margin-right:\x20auto;\x0a\x20\x20border-style:\x20none;\x0a}\x0atable.codetable\x20td\x20{\x0a\x20\x20padding-right:\x200.625rem;\x0a}\x0ahr\x20{\x0a\x20\x20border-style:\x20none;\x0a\x20\x20border-top:\x200.0625rem\x20solid\x20black;\x0a}\x0a\x0aimg.gopher\x20{\x0a\x20\x20float:\x20right;\x0a\x20\x20margin-left:\x200.625rem;\x0a\x20\x20margin-bottom:\x200.625rem;\x0a\x20\x20z-index:\x20-1;\x0a}\x0ah2\x20{\x0a\x20\x20clear:\x20right;\x0a}\x0a\x0a/*\x20example\x20and\x20drop-down\x20playground\x20*/\x0adiv.play\x20{\x0a\x20\x20padding:\x200\x201.25rem\x202.5rem\x201.25rem;\x0a}\x0adiv.play\x20pre,\x0adiv.play\x20textarea,\x0adiv.play\x20.lines\x20{\x0a\x20\x20padding:\x200;\x0a\x20\x20margin:\x200;\x0a\x20\x20font-family:\x20Menlo,\x20monospace;\x0a\x20\x20font-size:\x200.875rem;\x0a}\x0adiv.play\x20.input\x20{\x0a\x20\x20padding:\x200.625rem;\x0a\x20\x20margin-top:\x200.625rem;\x0a\x0a\x20\x20border-top-left-radius:\x200.3125rem;\x0a\x20\x20border-top-right-radius:\x200.3125rem;\x0a\x0a\x20\x20overflow:\x20hidden;\x0a}\x0adiv.play\x20.input\x20textarea\x20{\x0a\x20\x20width:\x20100%;\x0a\x20\x20height:\x20100%;\x0a\x20\x20border:\x20none;\x0a\x20\x20outline:\x20none;\x0a\x20\x20resize:\x20none;\x0a\x0a\x20\x20overflow:\x20hidden;\x0a}\x0adiv#playground\x20.input\x20textarea\x20{\x0a\x20\x20overflow:\x20auto;\x0a\x20\x20resize:\x20auto;\x0a}\x0adiv.play\x20.output\x20{\x0a\x20\x20border-top:\x20none\x20!important;\x0a\x0a\x20\x20padding:\x200.625rem;\x0a\x20\x20max-height:\x2012.5rem;\x0a\x20\x20overflow:\x20auto;\x0a\x0a\x20\x20border-bottom-right-radius:\x200.3125rem;\x0a\x20\x20border-bottom-left-radius:\x200.3125rem;\x0a}\x0adiv.play\x20.output\x20pre\x20{\x0a\x20\x20padding:\x200;\x0a\x20\x20border-radius:\x200;\x0a}\x0adiv.play\x20.input,\x0adiv.play\x20.input\x20textarea,\x0adiv.play\x20.output,\x0adiv.play\x20.output\x20pre\x20{\x0a\x20\x20background:\x20#ffffd8;\x0a}\x0adiv.play\x20.input,\x0adiv.play\x20.output\x20{\x0a\x20\x20border:\x200.0625rem\x20solid\x20#375eab;\x0a}\x0adiv.play\x20.buttons\x20{\x0a\x20\x20float:\x20right;\x0a\x20\x20padding:\x201.25rem\x200\x200.625rem\x200;\x0a\x20\x20text-align:\x20right;\x0a}\x0adiv.play\x20.buttons\x20a\x20{\x0a\x20\x20height:\x201rem;\x0a\x20\x20margin-left:\x200.3125rem;\x0a\x20\x20padding:\x200.625rem;\x0a\x20\x20cursor:\x20pointer;\x0a}\x0a.output\x20.stderr\x20{\x0a\x20\x20color:\x20#933;\x0a}\x0a.output\x20.system\x20{\x0a\x20\x20color:\x20#999;\x0a}\x0a\x0a/*\x20drop-down\x20playground\x20*/\x0adiv#playground\x20{\x0a\x20\x20/*\x20start\x20hidden;\x20revealed\x20by\x20javascript\x20*/\x0a\x20\x20display:\x20none;\x0a}\x0adiv#playground\x20{\x0a\x20\x20position:\x20absolute;\x0a\x20\x20top:\x203.938rem;\x0a\x20\x20right:\x201.25rem;\x0a\x20\x20padding:\x200\x200.625rem\x200.625rem\x200.625rem;\x0a\x20\x20z-index:\x201;\x0a\x20\x20text-align:\x20left;\x0a\x20\x20background:\x20#e0ebf5;\x0a\x0a\x20\x20border:\x200.0625rem\x20solid\x20#b0bbc5;\x0a\x20\x20border-top:\x20none;\x0a\x0a\x20\x20border-bottom-left-radius:\x200.3125rem;\x0a\x20\x20border-bottom-right-radius:\x200.3125rem;\x0a}\x0adiv#playground\x20.code\x20{\x0a\x20\x20width:\x2032.5rem;\x0a\x20\x20height:\x2012.5rem;\x0a}\x0adiv#playground\x20.output\x20{\x0a\x20\x20height:\x206.25rem;\x0a}\x0a\x0a/*\x20Inline\x20runnable\x20snippets\x20(play.js/initPlayground)\x20*/\x0a#content\x20.code\x20pre,\x0a#content\x20.playground\x20pre,\x0a#content\x20.output\x20pre\x20{\x0a\x20\x20margin:\x200;\x0a\x20\x20padding:\x200;\x0a\x20\x20background:\x20none;\x0a\x20\x20border:\x20none;\x0a\x20\x20outline:\x200\x20solid\x20transparent;\x0a\x20\x20overflow:\x20auto;\x0a}\x0a#content\x20.playground\x20.number,\x0a#content\x20.code\x20.number\x20{\x0a\x20\x20color:\x20#999;\x0a}\x0a#content\x20.code,\x0a#content\x20.playground,\x0a#content\x20.output\x20{\x0a\x20\x20width:\x20auto;\x0a\x20\x20margin:\x201.25rem;\x0a\x20\x20padding:\x200.625rem;\x0a\x20\x20border-radius:\x200.3125rem;\x0a}\x0a#content\x20.code,\x0a#content\x20.playground\x20{\x0a\x20\x20background:\x20#e9e9e9;\x0a}\x0a#content\x20.output\x20{\x0a\x20\x20background:\x20#202020;\x0a}\x0a#content\x20.output\x20.stdout,\x0a#content\x20.output\x20pre\x20{\x0a\x20\x20color:\x20#e6e6e6;\x0a}\x0a#content\x20.output\x20.stderr,\x0a#content\x20.output\x20.error\x20{\x0a\x20\x20color:\x20rgb(244,\x2074,\x2063);\x0a}\x0a#content\x20.output\x20.system,\x0a#content\x20.output\x20.exit\x20{\x0a\x20\x20color:\x20rgb(255,\x20209,\x2077);\x0a}\x0a#content\x20.buttons\x20{\x0a\x20\x20position:\x20relative;\x0a\x20\x20float:\x20right;\x0a\x20\x20top:\x20-3.125rem;\x0a\x20\x20right:\x201.875rem;\x0a}\x0a#content\x20.output\x20.buttons\x20{\x0a\x20\x20top:\x20-3.75rem;\x0a\x20\x20right:\x200;\x0a\x20\x20height:\x200;\x0a}\x0a#content\x20.buttons\x20.kill\x20{\x0a\x20\x20display:\x20none;\x0a\x20\x20visibility:\x20hidden;\x0a}\x0aa.error\x20{\x0a\x20\x20font-weight:\x20bold;\x0a\x20\x20color:\x20white;\x0a\x20\x20background-color:\x20darkred;\x0a\x20\x20border-bottom-left-radius:\x200.25rem;\x0a\x20\x20border-bottom-right-radius:\x200.25rem;\x0a\x20\x20border-top-left-radius:\x200.25rem;\x0a\x20\x20border-top-right-radius:\x200.25rem;\x0a\x20\x20padding:\x200.125rem\x200.25rem\x200.125rem\x200.25rem;\x20/*\x20TRBL\x20*/\x0a}\x0a\x0a#heading-narrow\x20{\x0a\x20\x20display:\x20none;\x0a}\x0a\x0a.downloading\x20{\x0a\x20\x20background:\x20#f9f9be;\x0a\x20\x20padding:\x200.625rem;\x0a\x20\x20text-align:\x20center;\x0a\x20\x20border-radius:\x200.3125rem;\x0a}\x0a\x0a@media\x20(max-width:\x2058.125em)\x20{\x0a\x20\x20#heading-wide\x20{\x0a\x20\x20\x20\x20display:\x20none;\x0a\x20\x20}\x0a\x20\x20#heading-narrow\x20{\x0a\x20\x20\x20\x20display:\x20block;\x0a\x20\x20}\x0a}\x0a\x0a@media\x20(max-width:\x2047.5em)\x20{\x0a\x20\x20.container\x20.left,\x0a\x20\x20.container\x20.right\x20{\x0a\x20\x20\x20\x20width:\x20auto;\x0a\x20\x20\x20\x20float:\x20none;\x0a\x20\x20}\x0a\x0a\x20\x20div#about\x20{\x0a\x20\x20\x20\x20max-width:\x2031.25rem;\x0a\x20\x20\x20\x20text-align:\x20center;\x0a\x20\x20}\x0a}\x0a\x0a@media\x20(min-width:\x2043.75em)\x20and\x20(max-width:\x2062.5em)\x20{\x0a\x20\x20div#menu\x20>\x20a\x20{\x0a\x20\x20\x20\x20margin:\x200.3125rem\x200;\x0a\x20\x20\x20\x20font-size:\x200.875rem;\x0a\x20\x20}\x0a\x0a\x20\x20input#search\x20{\x0a\x20\x20\x20\x20font-size:\x200.875rem;\x0a\x20\x20}\x0a}\x0a\x0a@media\x20(max-width:\x2043.75em)\x20{\x0a\x20\x20body\x20{\x0a\x20\x20\x20\x20font-size:\x200.9375rem;\x0a\x20\x20}\x0a\x0a\x20\x20div#playground\x20{\x0a\x20\x20\x20\x20left:\x200;\x0a\x20\x20\x20\x20right:\x200;\x0a\x20\x20}\x0a\x0a\x20\x20pre,\x0a\x20\x20code\x20{\x0a\x20\x20\x20\x20font-size:\x200.866rem;\x0a\x20\x20}\x0a\x0a\x20\x20div#page\x20>\x20.container\x20{\x0a\x20\x20\x20\x20padding:\x200\x200.625rem;\x0a\x20\x20}\x0a\x0a\x20\x20div#topbar\x20{\x0a\x20\x20\x20\x20height:\x20auto;\x0a\x20\x20\x20\x20padding:\x200.625rem;\x0a\x20\x20}\x0a\x0a\x20\x20div#topbar\x20>\x20.container\x20{\x0a\x20\x20\x20\x20padding:\x200;\x0a\x20\x20}\x0a\x0a\x20\x20#heading-wide\x20{\x0a\x20\x20\x20\x20display:\x20block;\x0a\x20\x20}\x0a\x20\x20#heading-narrow\x20{\x0a\x20\x20\x20\x20display:\x20none;\x0a\x20\x20}\x0a\x0a\x20\x20.top-heading\x20{\x0a\x20\x20\x20\x20float:\x20none;\x0a\x20\x20\x20\x20display:\x20inline-block;\x0a\x20\x20\x20\x20padding:\x200.75rem;\x0a\x20\x20}\x0a\x0a\x20\x20div#menu\x20{\x0a\x20\x20\x20\x20padding:\x200;\x0a\x20\x20\x20\x20min-width:\x200;\x0a\x20\x20\x20\x20text-align:\x20left;\x0a\x20\x20\x20\x20float:\x20left;\x0a\x20\x20}\x0a\x0a\x20\x20div#menu\x20>\x20a\x20{\x0a\x20\x20\x20\x20display:\x20block;\x0a\x20\x20\x20\x20margin-left:\x200;\x0a\x20\x20\x20\x20margin-right:\x200;\x0a\x20\x20}\x0a\x0a\x20\x20#menu\x20.search-box\x20{\x0a\x20\x20\x20\x20display:\x20flex;\x0a\x20\x20\x20\x20width:\x20100%;\x0a\x20\x20}\x0a\x0a\x20\x20#menu-button\x20{\x0a\x20\x20\x20\x20display:\x20inline-block;\x0a\x20\x20}\x0a\x0a\x20\x20p,\x0a\x20\x20pre,\x0a\x20\x20ul,\x0a\x20\x20ol\x20{\x0a\x20\x20\x20\x20margin:\x200.625rem;\x0a\x20\x20}\x0a\x0a\x20\x20.pkg-synopsis\x20{\x0a\x20\x20\x20\x20display:\x20none;\x0a\x20\x20}\x0a\x0a\x20\x20img.gopher\x20{\x0a\x20\x20\x20\x20display:\x20none;\x0a\x20\x20}\x0a}\x0a\x0a@media\x20(max-width:\x2030em)\x20{\x0a\x20\x20#heading-wide\x20{\x0a\x20\x20\x20\x20display:\x20none;\x0a\x20\x20}\x0a\x20\x20#heading-narrow\x20{\x0a\x20\x20\x20\x20display:\x20block;\x0a\x20\x20}\x0a}\x0a\x0a@media\x20print\x20{\x0a\x20\x20pre\x20{\x0a\x20\x20\x20\x20background:\x20#fff;\x0a\x20\x20\x20\x20border:\x200.0625rem\x20solid\x20#bbb;\x0a\x20\x20\x20\x20white-space:\x20pre-wrap;\x0a\x20\x20}\x0a}\x0a",
 }
diff --git a/godoc/static/style.css b/godoc/static/style.css
index b77d61e..c495bad 100644
--- a/godoc/static/style.css
+++ b/godoc/static/style.css
@@ -1,108 +1,109 @@
 body {
-	margin: 0;
-	font-family: Arial, sans-serif;
-	background-color: #fff;
-	line-height: 1.3;
-	text-align: center;
-	color: #222;
+  margin: 0;
+  font-family: Arial, sans-serif;
+  background-color: #fff;
+  line-height: 1.3;
+  text-align: center;
+  color: #222;
 }
 textarea {
-	/* Inherit text color from body avoiding illegible text in the case where the
+  /* Inherit text color from body avoiding illegible text in the case where the
  	* user has inverted the browsers custom text and background colors. */
-	color: inherit;
+  color: inherit;
 }
 pre,
 code {
-	font-family: Menlo, monospace;
-	font-size: 0.875rem;
+  font-family: Menlo, monospace;
+  font-size: 0.875rem;
 }
 pre {
-	line-height: 1.4;
-	overflow-x: auto;
+  line-height: 1.4;
+  overflow-x: auto;
 }
 pre .comment {
-	color: #006600;
+  color: #006600;
 }
 pre .highlight,
 pre .highlight-comment,
 pre .selection-highlight,
 pre .selection-highlight-comment {
-	background: #FFFF00;
+  background: #ffff00;
 }
 pre .selection,
 pre .selection-comment {
-	background: #FF9632;
+  background: #ff9632;
 }
 pre .ln {
-	color: #999;
-	background: #efefef;
+  color: #999;
+  background: #efefef;
 }
 .ln {
-	-webkit-user-select: none;
-	-moz-user-select: none;
-	-ms-user-select: none;
-	user-select: none;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
 
-	/* Ensure 8 characters in the document - which due to floating
+  /* Ensure 8 characters in the document - which due to floating
    * point rendering issues, might have a width of less than 1 each - are 8
    * characters wide, so a tab in the 9th position indents properly. See
    * https://github.com/webcompat/web-bugs/issues/17530#issuecomment-402675091
    * for more information. */
-	display: inline-block;
-	width: 8ch;
+  display: inline-block;
+  width: 8ch;
 }
 
-.search-nav{
-	margin-left: 1.25rem;
-	font-size: 0.875rem;
-	column-gap: 1.25rem;
-	column-fill: auto;
-	column-width: 14rem;
+.search-nav {
+  margin-left: 1.25rem;
+  font-size: 0.875rem;
+  column-gap: 1.25rem;
+  column-fill: auto;
+  column-width: 14rem;
 }
 
 .search-nav .indent {
-	margin-left: 1.25rem;
+  margin-left: 1.25rem;
 }
 
 a,
 .exampleHeading .text,
 .expandAll {
-	color: #375EAB;
-	text-decoration: none;
+  color: #375eab;
+  text-decoration: none;
 }
 a:hover,
 .exampleHeading .text:hover,
 .expandAll:hover {
-	text-decoration: underline;
+  text-decoration: underline;
 }
 .article a {
-	text-decoration: underline;
+  text-decoration: underline;
 }
 .article .title a {
-	text-decoration: none;
+  text-decoration: none;
 }
 
 .permalink {
-	display: none;
+  display: none;
 }
 :hover > .permalink {
-	display: inline;
+  display: inline;
 }
 
-p, li {
-	max-width: 50rem;
-	word-wrap: break-word;
+p,
+li {
+  max-width: 50rem;
+  word-wrap: break-word;
 }
 p,
 pre,
 ul,
 ol {
-	margin: 1.25rem;
+  margin: 1.25rem;
 }
 pre {
-	background: #EFEFEF;
-	padding: 0.625rem;
-	border-radius: 0.3125rem;
+  background: #efefef;
+  padding: 0.625rem;
+  border-radius: 0.3125rem;
 }
 
 h1,
@@ -110,147 +111,147 @@
 h3,
 h4,
 .rootHeading {
-	margin: 1.25rem 0 1.25rem;
-	padding: 0;
-	color: #375EAB;
-	font-weight: bold;
+  margin: 1.25rem 0 1.25rem;
+  padding: 0;
+  color: #375eab;
+  font-weight: bold;
 }
 h1 {
-	font-size: 1.75rem;
-	line-height: 1;
+  font-size: 1.75rem;
+  line-height: 1;
 }
 h1 .text-muted {
-	color:#777;
+  color: #777;
 }
 h2 {
-	font-size: 1.25rem;
-	background: #E0EBF5;
-	padding: 0.5rem;
-	line-height: 1.25;
-	font-weight: normal;
-	overflow: auto;
-	overflow-wrap: break-word;
+  font-size: 1.25rem;
+  background: #e0ebf5;
+  padding: 0.5rem;
+  line-height: 1.25;
+  font-weight: normal;
+  overflow: auto;
+  overflow-wrap: break-word;
 }
 h2 a {
-	font-weight: bold;
+  font-weight: bold;
 }
 h3 {
-	font-size: 1.25rem;
-	line-height: 1.25;
-	overflow: auto;
-	overflow-wrap: break-word;
+  font-size: 1.25rem;
+  line-height: 1.25;
+  overflow: auto;
+  overflow-wrap: break-word;
 }
 h3,
 h4 {
-	margin: 1.25rem 0.3125rem;
+  margin: 1.25rem 0.3125rem;
 }
 h4 {
-	font-size: 1rem;
+  font-size: 1rem;
 }
 .rootHeading {
-	font-size: 1.25rem;
-	margin: 0;
+  font-size: 1.25rem;
+  margin: 0;
 }
 
 h2 > span,
 h3 > span {
-	float: right;
-	margin: 0 25px 0 0;
-	font-weight: normal;
-	color: #5279C7;
+  float: right;
+  margin: 0 25px 0 0;
+  font-weight: normal;
+  color: #5279c7;
 }
 
 dl {
-	margin: 1.25rem;
+  margin: 1.25rem;
 }
 dd {
-	margin: 0 0 0 1.25rem;
+  margin: 0 0 0 1.25rem;
 }
 dl,
 dd {
-	font-size: 0.875rem;
+  font-size: 0.875rem;
 }
 div#nav table td {
-	vertical-align: top;
+  vertical-align: top;
 }
 
 #pkg-index h3 {
-	font-size: 1rem;
+  font-size: 1rem;
 }
 .pkg-dir {
-	padding: 0 0.625rem;
+  padding: 0 0.625rem;
 }
 .pkg-dir table {
-	border-collapse: collapse;
-	border-spacing: 0;
+  border-collapse: collapse;
+  border-spacing: 0;
 }
 .pkg-name {
-	padding-right: 0.625rem;
+  padding-right: 0.625rem;
 }
 .alert {
-	color: #AA0000;
+  color: #aa0000;
 }
 
 .top-heading {
-	float: left;
-	padding: 1.313rem 0;
-	font-size: 1.25rem;
-	font-weight: normal;
+  float: left;
+  padding: 1.313rem 0;
+  font-size: 1.25rem;
+  font-weight: normal;
 }
 .top-heading a {
-	color: #222;
-	text-decoration: none;
+  color: #222;
+  text-decoration: none;
 }
 
 #pkg-examples h3 {
-	float: left;
+  float: left;
 }
 
 #pkg-examples dl {
-	clear: both;
+  clear: both;
 }
 
 .expandAll {
-	cursor: pointer;
-	float: left;
-	margin: 1.25rem 0;
+  cursor: pointer;
+  float: left;
+  margin: 1.25rem 0;
 }
 
 div#topbar {
-	background: #E0EBF5;
-	height: 4rem;
-	overflow: hidden;
+  background: #e0ebf5;
+  height: 4rem;
+  overflow: hidden;
 }
 
 div#page {
-	width: 100%;
+  width: 100%;
 }
 div#page > .container,
 div#topbar > .container {
-	text-align: left;
-	margin-left: auto;
-	margin-right: auto;
-	padding: 0 1.25rem;
+  text-align: left;
+  margin-left: auto;
+  margin-right: auto;
+  padding: 0 1.25rem;
 }
 div#topbar > .container,
 div#page > .container {
-	max-width: 59.38rem;
+  max-width: 59.38rem;
 }
 div#page.wide > .container,
 div#topbar.wide > .container {
-	max-width: none;
+  max-width: none;
 }
 div#plusone {
-	float: right;
-	clear: right;
-	margin-top: 0.3125rem;
+  float: right;
+  clear: right;
+  margin-top: 0.3125rem;
 }
 
 div#footer {
-	text-align: center;
-	color: #666;
-	font-size: 0.875rem;
-	margin: 2.5rem 0;
+  text-align: center;
+  color: #666;
+  font-size: 0.875rem;
+  margin: 2.5rem 0;
 }
 
 div#menu > a,
@@ -259,258 +260,258 @@
 div.play .buttons a,
 div#blog .read a,
 #menu-button {
-	padding: 0.625rem;
+  padding: 0.625rem;
 
-	text-decoration: none;
-	font-size: 1rem;
-	border-radius: 0.3125rem;
+  text-decoration: none;
+  font-size: 1rem;
+  border-radius: 0.3125rem;
 }
 div#playground .buttons a,
 div#menu > a,
 input#search,
 #menu-button {
-	border: 0.0625rem solid #375EAB;
+  border: 0.0625rem solid #375eab;
 }
 div#playground .buttons a,
 div#menu > a,
 #menu-button {
-	color: white;
-	background: #375EAB;
+  color: white;
+  background: #375eab;
 }
 #playgroundButton.active {
-	background: white;
-	color: #375EAB;
+  background: white;
+  color: #375eab;
 }
 a#start,
 div#learn .buttons a,
 div.play .buttons a,
 div#blog .read a {
-	color: #222;
-	border: 0.0625rem solid #375EAB;
-	background: #E0EBF5;
+  color: #222;
+  border: 0.0625rem solid #375eab;
+  background: #e0ebf5;
 }
 .download {
-	width: 9.375rem;
+  width: 9.375rem;
 }
 
 div#menu {
-	text-align: right;
-	padding: 0.625rem;
-	white-space: nowrap;
-	max-height: 0;
-	-moz-transition: max-height .25s linear;
-	transition: max-height .25s linear;
-	width: 100%;
+  text-align: right;
+  padding: 0.625rem;
+  white-space: nowrap;
+  max-height: 0;
+  -moz-transition: max-height 0.25s linear;
+  transition: max-height 0.25s linear;
+  width: 100%;
 }
 div#menu.menu-visible {
-	max-height: 31.25rem;
+  max-height: 31.25rem;
 }
 div#menu > a,
 #menu-button {
-	margin: 0.625rem 0.125rem;
-	padding: 0.625rem;
+  margin: 0.625rem 0.125rem;
+  padding: 0.625rem;
 }
 ::-webkit-input-placeholder {
-	color: #7f7f7f;
-	opacity: 1;
+  color: #7f7f7f;
+  opacity: 1;
 }
 ::placeholder {
-	color: #7f7f7f;
-	opacity: 1;
+  color: #7f7f7f;
+  opacity: 1;
 }
 #menu .search-box {
-	display: inline-flex;
-	width: 8.75rem;
+  display: inline-flex;
+  width: 8.75rem;
 }
 input#search {
-	background: white;
-	color: #222;
-	box-sizing: border-box;
-	-webkit-appearance: none;
-	border-top-right-radius: 0;
-	border-bottom-right-radius: 0;
-	border-right: 0;
-	margin-right: 0;
-	flex-grow: 1;
-	max-width: 100%;
-	min-width: 5.625rem;
+  background: white;
+  color: #222;
+  box-sizing: border-box;
+  -webkit-appearance: none;
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
+  border-right: 0;
+  margin-right: 0;
+  flex-grow: 1;
+  max-width: 100%;
+  min-width: 5.625rem;
 }
 input#search:-webkit-search-decoration {
-	-webkit-appearance: none;
+  -webkit-appearance: none;
 }
 input#search:-moz-ui-invalid {
-	box-shadow: unset;
+  box-shadow: unset;
 }
 input#search + button {
-	display: inline;
-	font-size: 1em;
-	background-color: #375EAB;
-	color: white;
-	border: 0.0625rem solid #375EAB;
-	border-top-left-radius: 0;
-	border-top-right-radius: 0.3125rem;
-	border-bottom-left-radius: 0;
-	border-bottom-right-radius: 0.3125rem;
-	margin-left: 0;
-	cursor: pointer;
+  display: inline;
+  font-size: 1em;
+  background-color: #375eab;
+  color: white;
+  border: 0.0625rem solid #375eab;
+  border-top-left-radius: 0;
+  border-top-right-radius: 0.3125rem;
+  border-bottom-left-radius: 0;
+  border-bottom-right-radius: 0.3125rem;
+  margin-left: 0;
+  cursor: pointer;
 }
 input#search + button span {
-	display: flex;
+  display: flex;
 }
 input#search + button svg {
-	fill: white
+  fill: white;
 }
 
 #menu-button {
-	display: none;
-	position: absolute;
-	right: 0.3125rem;
-	top: 0;
-	margin-right: 0.3125rem;
+  display: none;
+  position: absolute;
+  right: 0.3125rem;
+  top: 0;
+  margin-right: 0.3125rem;
 }
 #menu-button-arrow {
-	display: inline-block;
+  display: inline-block;
 }
 .vertical-flip {
-	transform: rotate(-180deg);
+  transform: rotate(-180deg);
 }
 
 div.left {
-	float: left;
-	clear: left;
-	margin-right: 2.5%;
+  float: left;
+  clear: left;
+  margin-right: 2.5%;
 }
 div.right {
-	float: right;
-	clear: right;
-	margin-left: 2.5%;
+  float: right;
+  clear: right;
+  margin-left: 2.5%;
 }
 div.left,
 div.right {
-	width: 45%;
+  width: 45%;
 }
 
 div#learn,
 div#about {
-	padding-top: 1.25rem;
+  padding-top: 1.25rem;
 }
 div#learn h2,
 div#about {
-	margin: 0;
+  margin: 0;
 }
 div#about {
-	font-size: 1.25rem;
-	margin: 0 auto 1.875rem;
+  font-size: 1.25rem;
+  margin: 0 auto 1.875rem;
 }
 div#gopher {
-	background: url(/doc/gopher/frontpage.png) no-repeat;
-	background-position: center top;
-	height: 9.688rem;
-	max-height: 200px; /* Setting in px to prevent the gopher from blowing up in very high default font-sizes */
+  background: url(/doc/gopher/frontpage.png) no-repeat;
+  background-position: center top;
+  height: 9.688rem;
+  max-height: 200px; /* Setting in px to prevent the gopher from blowing up in very high default font-sizes */
 }
 a#start {
-	display: block;
-	padding: 0.625rem;
+  display: block;
+  padding: 0.625rem;
 
-	text-align: center;
-	text-decoration: none;
-	border-radius: 0.3125rem;
+  text-align: center;
+  text-decoration: none;
+  border-radius: 0.3125rem;
 }
 a#start .big {
-	display: block;
-	font-weight: bold;
-	font-size: 1.25rem;
+  display: block;
+  font-weight: bold;
+  font-size: 1.25rem;
 }
 a#start .desc {
-	display: block;
-	font-size: 0.875rem;
-	font-weight: normal;
-	margin-top: 0.3125rem;
+  display: block;
+  font-size: 0.875rem;
+  font-weight: normal;
+  margin-top: 0.3125rem;
 }
 
 div#learn .popout {
-	float: right;
-	display: block;
-	cursor: pointer;
-	font-size: 0.75rem;
-	background: url(/doc/share.png) no-repeat;
-	background-position: right center;
-	padding: 0.375rem 1.688rem;
+  float: right;
+  display: block;
+  cursor: pointer;
+  font-size: 0.75rem;
+  background: url(/doc/share.png) no-repeat;
+  background-position: right center;
+  padding: 0.375rem 1.688rem;
 }
 div#learn pre,
 div#learn textarea {
-	padding: 0;
-	margin: 0;
-	font-family: Menlo, monospace;
-	font-size: 0.875rem;
+  padding: 0;
+  margin: 0;
+  font-family: Menlo, monospace;
+  font-size: 0.875rem;
 }
 div#learn .input {
-	padding: 0.625rem;
-	margin-top: 0.625rem;
-	height: 9.375rem;
+  padding: 0.625rem;
+  margin-top: 0.625rem;
+  height: 9.375rem;
 
-	border-top-left-radius: 0.3125rem;
-	border-top-right-radius: 0.3125rem;
+  border-top-left-radius: 0.3125rem;
+  border-top-right-radius: 0.3125rem;
 }
 div#learn .input textarea {
-	width: 100%;
-	height: 100%;
-	border: none;
-	outline: none;
-	resize: none;
+  width: 100%;
+  height: 100%;
+  border: none;
+  outline: none;
+  resize: none;
 }
 div#learn .output {
-	border-top: none !important;
+  border-top: none !important;
 
-	padding: 0.625rem;
-	height: 3.688rem;
-	overflow: auto;
+  padding: 0.625rem;
+  height: 3.688rem;
+  overflow: auto;
 
-	border-bottom-right-radius: 0.3125rem;
-	border-bottom-left-radius: 0.3125rem;
+  border-bottom-right-radius: 0.3125rem;
+  border-bottom-left-radius: 0.3125rem;
 }
 div#learn .output pre {
-	padding: 0;
-	border-radius: 0;
+  padding: 0;
+  border-radius: 0;
 }
 div#learn .input,
 div#learn .input textarea,
 div#learn .output,
 div#learn .output pre {
-	background: #FFFFD8;
+  background: #ffffd8;
 }
 div#learn .input,
 div#learn .output {
-	border: 0.0625rem solid #375EAB;
+  border: 0.0625rem solid #375eab;
 }
 div#learn .buttons {
-	float: right;
-	padding: 1.25rem 0 0.625rem 0;
-	text-align: right;
+  float: right;
+  padding: 1.25rem 0 0.625rem 0;
+  text-align: right;
 }
 div#learn .buttons a {
-	height: 1rem;
-	margin-left: 0.3125rem;
-	padding: 0.625rem;
+  height: 1rem;
+  margin-left: 0.3125rem;
+  padding: 0.625rem;
 }
 div#learn .toys {
-	margin-top: 0.5rem;
+  margin-top: 0.5rem;
 }
 div#learn .toys select {
-	font-size: 0.875rem;
-	border: 0.0625rem solid #375EAB;
-	margin: 0;
+  font-size: 0.875rem;
+  border: 0.0625rem solid #375eab;
+  margin: 0;
 }
 div#learn .output .exit {
-	display: none;
+  display: none;
 }
 
 div#video {
-	max-width: 100%;
+  max-width: 100%;
 }
 div#blog,
 div#video {
-	margin-top: 2.5rem;
+  margin-top: 2.5rem;
 }
 div#blog > a,
 div#blog > div,
@@ -518,355 +519,384 @@
 div#video > a,
 div#video > div,
 div#video > h2 {
-	margin-bottom: 0.625rem;
+  margin-bottom: 0.625rem;
 }
 div#blog .title,
 div#video .title {
-	display: block;
-	font-size: 1.25rem;
+  display: block;
+  font-size: 1.25rem;
 }
 div#blog .when {
-	color: #666;
-	font-size: 0.875rem;
+  color: #666;
+  font-size: 0.875rem;
 }
 div#blog .read {
-	text-align: right;
+  text-align: right;
 }
 
 @supports (--c: 0) {
-	[style*="--aspect-ratio-padding:"] {
-		position: relative;
-		overflow: hidden;
-		padding-top: var(--aspect-ratio-padding);
-	}
+  [style*='--aspect-ratio-padding:'] {
+    position: relative;
+    overflow: hidden;
+    padding-top: var(--aspect-ratio-padding);
+  }
 
-	[style*="--aspect-ratio-padding:"]>* {
-		position: absolute;
-		top: 0;
-		left: 0;
-		width: 100%;
-		height: 100%;
-	}
+  [style*='--aspect-ratio-padding:'] > * {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+  }
 }
 
-.toggleButton { cursor: pointer; }
-.toggle > .collapsed { display: block; }
-.toggle > .expanded { display: none; }
-.toggleVisible > .collapsed { display: none; }
-.toggleVisible > .expanded { display: block; }
+.toggleButton {
+  cursor: pointer;
+}
+.toggle > .collapsed {
+  display: block;
+}
+.toggle > .expanded {
+  display: none;
+}
+.toggleVisible > .collapsed {
+  display: none;
+}
+.toggleVisible > .expanded {
+  display: block;
+}
 
-table.codetable { margin-left: auto; margin-right: auto; border-style: none; }
-table.codetable td { padding-right: 0.625rem; }
-hr { border-style: none; border-top: 0.0625rem solid black; }
+table.codetable {
+  margin-left: auto;
+  margin-right: auto;
+  border-style: none;
+}
+table.codetable td {
+  padding-right: 0.625rem;
+}
+hr {
+  border-style: none;
+  border-top: 0.0625rem solid black;
+}
 
 img.gopher {
-	float: right;
-	margin-left: 0.625rem;
-	margin-bottom: 0.625rem;
-	z-index: -1;
+  float: right;
+  margin-left: 0.625rem;
+  margin-bottom: 0.625rem;
+  z-index: -1;
 }
-h2 { clear: right; }
+h2 {
+  clear: right;
+}
 
 /* example and drop-down playground */
 div.play {
-	padding: 0 1.25rem 2.5rem 1.25rem;
+  padding: 0 1.25rem 2.5rem 1.25rem;
 }
 div.play pre,
 div.play textarea,
 div.play .lines {
-	padding: 0;
-	margin: 0;
-	font-family: Menlo, monospace;
-	font-size: 0.875rem;
+  padding: 0;
+  margin: 0;
+  font-family: Menlo, monospace;
+  font-size: 0.875rem;
 }
 div.play .input {
-	padding: 0.625rem;
-	margin-top: 0.625rem;
+  padding: 0.625rem;
+  margin-top: 0.625rem;
 
-	border-top-left-radius: 0.3125rem;
-	border-top-right-radius: 0.3125rem;
+  border-top-left-radius: 0.3125rem;
+  border-top-right-radius: 0.3125rem;
 
-	overflow: hidden;
+  overflow: hidden;
 }
 div.play .input textarea {
-	width: 100%;
-	height: 100%;
-	border: none;
-	outline: none;
-	resize: none;
+  width: 100%;
+  height: 100%;
+  border: none;
+  outline: none;
+  resize: none;
 
-	overflow: hidden;
+  overflow: hidden;
 }
 div#playground .input textarea {
-	overflow: auto;
-	resize: auto;
+  overflow: auto;
+  resize: auto;
 }
 div.play .output {
-	border-top: none !important;
+  border-top: none !important;
 
-	padding: 0.625rem;
-	max-height: 12.5rem;
-	overflow: auto;
+  padding: 0.625rem;
+  max-height: 12.5rem;
+  overflow: auto;
 
-	border-bottom-right-radius: 0.3125rem;
-	border-bottom-left-radius: 0.3125rem;
+  border-bottom-right-radius: 0.3125rem;
+  border-bottom-left-radius: 0.3125rem;
 }
 div.play .output pre {
-	padding: 0;
-	border-radius: 0;
+  padding: 0;
+  border-radius: 0;
 }
 div.play .input,
 div.play .input textarea,
 div.play .output,
 div.play .output pre {
-	background: #FFFFD8;
+  background: #ffffd8;
 }
 div.play .input,
 div.play .output {
-	border: 0.0625rem solid #375EAB;
+  border: 0.0625rem solid #375eab;
 }
 div.play .buttons {
-	float: right;
-	padding: 1.25rem 0 0.625rem 0;
-	text-align: right;
+  float: right;
+  padding: 1.25rem 0 0.625rem 0;
+  text-align: right;
 }
 div.play .buttons a {
-	height: 1rem;
-	margin-left: 0.3125rem;
-	padding: 0.625rem;
-	cursor: pointer;
+  height: 1rem;
+  margin-left: 0.3125rem;
+  padding: 0.625rem;
+  cursor: pointer;
 }
 .output .stderr {
-	color: #933;
+  color: #933;
 }
 .output .system {
-	color: #999;
+  color: #999;
 }
 
 /* drop-down playground */
 div#playground {
-	/* start hidden; revealed by javascript */
-	display: none;
+  /* start hidden; revealed by javascript */
+  display: none;
 }
 div#playground {
-	position: absolute;
-	top: 3.938rem;
-	right: 1.25rem;
-	padding: 0 0.625rem 0.625rem 0.625rem;
-	z-index: 1;
-	text-align: left;
-	background: #E0EBF5;
+  position: absolute;
+  top: 3.938rem;
+  right: 1.25rem;
+  padding: 0 0.625rem 0.625rem 0.625rem;
+  z-index: 1;
+  text-align: left;
+  background: #e0ebf5;
 
-	border: 0.0625rem solid #B0BBC5;
-	border-top: none;
+  border: 0.0625rem solid #b0bbc5;
+  border-top: none;
 
-	border-bottom-left-radius: 0.3125rem;
-	border-bottom-right-radius: 0.3125rem;
+  border-bottom-left-radius: 0.3125rem;
+  border-bottom-right-radius: 0.3125rem;
 }
 div#playground .code {
-	width: 32.5rem;
-	height: 12.5rem;
+  width: 32.5rem;
+  height: 12.5rem;
 }
 div#playground .output {
-	height: 6.25rem;
+  height: 6.25rem;
 }
 
 /* Inline runnable snippets (play.js/initPlayground) */
-#content .code pre, #content .playground pre, #content .output pre {
-        margin: 0;
-        padding: 0;
-        background: none;
-        border: none;
-	outline: 0 solid transparent;
-        overflow: auto;
+#content .code pre,
+#content .playground pre,
+#content .output pre {
+  margin: 0;
+  padding: 0;
+  background: none;
+  border: none;
+  outline: 0 solid transparent;
+  overflow: auto;
 }
-#content .playground .number, #content .code .number {
-        color: #999;
+#content .playground .number,
+#content .code .number {
+  color: #999;
 }
-#content .code, #content .playground, #content .output {
-	width: auto;
-        margin: 1.25rem;
-        padding: 0.625rem;
-        border-radius: 0.3125rem;
+#content .code,
+#content .playground,
+#content .output {
+  width: auto;
+  margin: 1.25rem;
+  padding: 0.625rem;
+  border-radius: 0.3125rem;
 }
-#content .code, #content .playground {
-        background: #e9e9e9;
+#content .code,
+#content .playground {
+  background: #e9e9e9;
 }
 #content .output {
-        background: #202020;
+  background: #202020;
 }
-#content .output .stdout, #content .output pre {
-        color: #e6e6e6;
+#content .output .stdout,
+#content .output pre {
+  color: #e6e6e6;
 }
-#content .output .stderr, #content .output .error {
-        color: rgb(244, 74, 63);
+#content .output .stderr,
+#content .output .error {
+  color: rgb(244, 74, 63);
 }
-#content .output .system, #content .output .exit {
-        color: rgb(255, 209, 77)
+#content .output .system,
+#content .output .exit {
+  color: rgb(255, 209, 77);
 }
 #content .buttons {
-        position: relative;
-        float: right;
-        top: -3.125rem;
-        right: 1.875rem;
+  position: relative;
+  float: right;
+  top: -3.125rem;
+  right: 1.875rem;
 }
 #content .output .buttons {
-        top: -3.75rem;
-        right: 0;
-        height: 0;
+  top: -3.75rem;
+  right: 0;
+  height: 0;
 }
 #content .buttons .kill {
-        display: none;
-        visibility: hidden;
+  display: none;
+  visibility: hidden;
 }
 a.error {
-	font-weight: bold;
-        color: white;
-	background-color: darkred;
-        border-bottom-left-radius: 0.25rem;
-        border-bottom-right-radius: 0.25rem;
-        border-top-left-radius: 0.25rem;
-        border-top-right-radius: 0.25rem;
-        padding: 0.125rem 0.25rem 0.125rem 0.25rem; /* TRBL */
+  font-weight: bold;
+  color: white;
+  background-color: darkred;
+  border-bottom-left-radius: 0.25rem;
+  border-bottom-right-radius: 0.25rem;
+  border-top-left-radius: 0.25rem;
+  border-top-right-radius: 0.25rem;
+  padding: 0.125rem 0.25rem 0.125rem 0.25rem; /* TRBL */
 }
 
-
 #heading-narrow {
-	display: none;
+  display: none;
 }
 
 .downloading {
-	background: #F9F9BE;
-	padding: 0.625rem;
-	text-align: center;
-	border-radius: 0.3125rem;
+  background: #f9f9be;
+  padding: 0.625rem;
+  text-align: center;
+  border-radius: 0.3125rem;
 }
 
 @media (max-width: 58.125em) {
-	#heading-wide {
-		display: none;
-	}
-	#heading-narrow {
-		display: block;
-	}
+  #heading-wide {
+    display: none;
+  }
+  #heading-narrow {
+    display: block;
+  }
 }
 
 @media (max-width: 47.5em) {
-	.container .left,
-	.container .right {
-		width: auto;
-		float: none;
-	}
+  .container .left,
+  .container .right {
+    width: auto;
+    float: none;
+  }
 
-	div#about {
-		max-width: 31.25rem;
-		text-align: center;
-	}
+  div#about {
+    max-width: 31.25rem;
+    text-align: center;
+  }
 }
 
 @media (min-width: 43.75em) and (max-width: 62.5em) {
-	div#menu > a {
-		margin: 0.3125rem 0;
-		font-size: 0.875rem;
-	}
+  div#menu > a {
+    margin: 0.3125rem 0;
+    font-size: 0.875rem;
+  }
 
-	input#search {
-		font-size: 0.875rem;
-	}
+  input#search {
+    font-size: 0.875rem;
+  }
 }
 
 @media (max-width: 43.75em) {
-	body {
-		font-size: 0.9375rem;
-	}
+  body {
+    font-size: 0.9375rem;
+  }
 
-	div#playground {
-		left: 0;
-		right: 0;
-	}
+  div#playground {
+    left: 0;
+    right: 0;
+  }
 
-	pre,
-	code {
-		font-size: 0.866rem;
-	}
+  pre,
+  code {
+    font-size: 0.866rem;
+  }
 
-	div#page > .container {
-		padding: 0 0.625rem;
-	}
+  div#page > .container {
+    padding: 0 0.625rem;
+  }
 
-	div#topbar {
-		height: auto;
-		padding: 0.625rem;
-	}
+  div#topbar {
+    height: auto;
+    padding: 0.625rem;
+  }
 
-	div#topbar > .container {
-		padding: 0;
-	}
+  div#topbar > .container {
+    padding: 0;
+  }
 
-	#heading-wide {
-		display: block;
-	}
-	#heading-narrow {
-		display: none;
-	}
+  #heading-wide {
+    display: block;
+  }
+  #heading-narrow {
+    display: none;
+  }
 
-	.top-heading {
-		float: none;
-		display: inline-block;
-		padding: 0.75rem;
-	}
+  .top-heading {
+    float: none;
+    display: inline-block;
+    padding: 0.75rem;
+  }
 
-	div#menu {
-		padding: 0;
-		min-width: 0;
-		text-align: left;
-		float: left;
-	}
+  div#menu {
+    padding: 0;
+    min-width: 0;
+    text-align: left;
+    float: left;
+  }
 
-	div#menu > a {
-		display: block;
-		margin-left: 0;
-		margin-right: 0;
-	}
+  div#menu > a {
+    display: block;
+    margin-left: 0;
+    margin-right: 0;
+  }
 
-	#menu .search-box {
-		display: flex;
-		width: 100%;
-	}
+  #menu .search-box {
+    display: flex;
+    width: 100%;
+  }
 
-	#menu-button {
-		display: inline-block;
-	}
+  #menu-button {
+    display: inline-block;
+  }
 
-	p,
-	pre,
-	ul,
-	ol {
-		margin: 0.625rem;
-	}
+  p,
+  pre,
+  ul,
+  ol {
+    margin: 0.625rem;
+  }
 
-	.pkg-synopsis {
-		display: none;
-	}
+  .pkg-synopsis {
+    display: none;
+  }
 
-	img.gopher {
-		display: none;
-	}
+  img.gopher {
+    display: none;
+  }
 }
 
 @media (max-width: 30em) {
-	#heading-wide {
-		display: none;
-	}
-	#heading-narrow {
-		display: block;
-	}
+  #heading-wide {
+    display: none;
+  }
+  #heading-narrow {
+    display: block;
+  }
 }
 
 @media print {
-	pre {
-		background: #FFF;
-		border: 0.0625rem solid #BBB;
-		white-space: pre-wrap;
-	}
+  pre {
+    background: #fff;
+    border: 0.0625rem solid #bbb;
+    white-space: pre-wrap;
+  }
 }
diff --git a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md
index f3e5eee..e56a52f 100644
--- a/gopls/doc/analyzers.md
+++ b/gopls/doc/analyzers.md
@@ -71,7 +71,7 @@
 
 Default value: `true`.
 
-### **composite**
+### **composites**
 
 check for unkeyed composite literals
 
diff --git a/gopls/doc/emacs.md b/gopls/doc/emacs.md
index bf836c8..ac92047 100644
--- a/gopls/doc/emacs.md
+++ b/gopls/doc/emacs.md
@@ -28,12 +28,6 @@
   (setq company-idle-delay 0)
   (setq company-minimum-prefix-length 1))
 
-;; company-lsp integrates company mode completion with lsp-mode.
-;; completion-at-point also works out of the box but doesn't support snippets.
-(use-package company-lsp
-  :ensure t
-  :commands company-lsp)
-
 ;; Optional - provides snippet support.
 (use-package yasnippet
   :ensure t
@@ -64,7 +58,7 @@
 Common errors:
 - When prompted by Emacs for your project folder, if you are using modules you must select the module's root folder (i.e. the directory with the "go.mod"). If you are using GOPATH, select your $GOPATH as your folder.
 - Emacs must have your environment set properly (PATH, GOPATH, etc). You can run `M-x getenv <RET> PATH <RET>` to see if your PATH is set in Emacs. If not, you can try starting Emacs from your terminal, using [this package][exec-path-from-shell], or moving your shell config from .bashrc into .bashenv (or .zshenv).
-- Make sure `lsp-mode`, `lsp-ui` and `company-lsp` are up-to-date, and make sure `lsp-go` is _not_ installed.
+- Make sure `lsp-mode` and `lsp-ui` are up-to-date, also make sure `lsp-go` and `company-lsp` are _not_ installed.
 - Look for errors in the `*lsp-log*` buffer.
 - Ask for help in the #emacs channel on the [Gophers slack].
 
diff --git a/gopls/doc/settings.md b/gopls/doc/settings.md
index 7f0c22e..7933696 100644
--- a/gopls/doc/settings.md
+++ b/gopls/doc/settings.md
@@ -88,8 +88,9 @@
 Overrides the enabled/disabled state of various code lenses. Currently, we
 support two code lenses:
 
-* `generate`: run `go generate` as specified by a `//go:generate` directive.
-* `upgrade.dependency`: upgrade a dependency listed in a `go.mod` file.
+* `generate`: [default: enabled] run `go generate` as specified by a `//go:generate` directive.
+* `upgrade.dependency`: [default: enabled] upgrade a dependency listed in a `go.mod` file.
+* `test`: [default: disabled] run `go test -run` for a test func.
 
 By default, both of these code lenses are enabled.
 
diff --git a/gopls/doc/vscode.md b/gopls/doc/vscode.md
index 60c196e..72be1ad 100644
--- a/gopls/doc/vscode.md
+++ b/gopls/doc/vscode.md
@@ -63,7 +63,7 @@
 ```
 
 
-[VSCode-Go]: https://github.com/microsoft/vscode-go
+[VSCode-Go]: https://github.com/golang/vscode-go
 
 # VSCode Remote Development with gopls
 
diff --git a/gopls/go.mod b/gopls/go.mod
index 7356e20..86f3e7b 100644
--- a/gopls/go.mod
+++ b/gopls/go.mod
@@ -1,12 +1,12 @@
 module golang.org/x/tools/gopls
 
-go 1.11
+go 1.13
 
 require (
 	github.com/sergi/go-diff v1.1.0
-	golang.org/x/tools v0.0.0-20200513154647-78b527d18275
-	honnef.co/go/tools v0.0.1-2020.1.3
-	mvdan.cc/xurls/v2 v2.1.0
+	golang.org/x/tools v0.0.0-20200515220128-d3bf790afa53
+	honnef.co/go/tools v0.0.1-2020.1.4
+	mvdan.cc/xurls/v2 v2.2.0
 )
 
 replace golang.org/x/tools => ../
diff --git a/gopls/go.sum b/gopls/go.sum
index e92fe0a..4c91be0 100644
--- a/gopls/go.sum
+++ b/gopls/go.sum
@@ -13,6 +13,7 @@
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
 github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -43,7 +44,7 @@
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
-honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-mvdan.cc/xurls/v2 v2.1.0 h1:KaMb5GLhlcSX+e+qhbRJODnUUBvlw01jt4yrjFIHAuA=
-mvdan.cc/xurls/v2 v2.1.0/go.mod h1:5GrSd9rOnKOpZaji1OZLYL/yeAAtGDlo/cFe+8K5n8E=
+honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8=
+honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+mvdan.cc/xurls/v2 v2.2.0 h1:NSZPykBXJFCetGZykLAxaL6SIpvbVy/UFEniIfHAa8A=
+mvdan.cc/xurls/v2 v2.2.0/go.mod h1:EV1RMtya9D6G5DMYPGD8zTQzaHet6Jh8gFlRgGRJeO8=
diff --git a/gopls/integration/replay/README.md b/gopls/integration/replay/README.md
index 189b5b1..7c32277 100644
--- a/gopls/integration/replay/README.md
+++ b/gopls/integration/replay/README.md
@@ -15,7 +15,7 @@
 
 The log for the replayed session is saved in `/tmp/seen`.
 
-There is aloso a boolean argument `-cmp` which compares the log file
+There is also a boolean argument `-cmp` which compares the log file
 with `/tmp/seen` without invoking gopls and rerunning the session.
 
 The output is fairly cryptic, and generated by logging. Ideas for better output would be welcome.
diff --git a/gopls/integration/replay/main.go b/gopls/integration/replay/main.go
index cea66f2..683ef81 100644
--- a/gopls/integration/replay/main.go
+++ b/gopls/integration/replay/main.go
@@ -18,6 +18,7 @@
 	"strings"
 
 	"golang.org/x/tools/gopls/integration/parse"
+	"golang.org/x/tools/internal/fakenet"
 	"golang.org/x/tools/internal/jsonrpc2"
 	p "golang.org/x/tools/internal/lsp/protocol"
 )
@@ -227,7 +228,8 @@
 	if err != nil {
 		log.Fatal(err)
 	}
-	stream := jsonrpc2.NewHeaderStream(fromServer, toServer)
+	conn := fakenet.NewConn("stdio", fromServer, toServer)
+	stream := jsonrpc2.NewHeaderStream(conn)
 	rchan := make(chan jsonrpc2.Message, 10) // do we need buffering?
 	rdr := func() {
 		for {
diff --git a/internal/analysisinternal/analysis.go b/internal/analysisinternal/analysis.go
index 2658681..14b96a7 100644
--- a/internal/analysisinternal/analysis.go
+++ b/internal/analysisinternal/analysis.go
@@ -48,7 +48,7 @@
 	case *types.Chan, *types.Interface, *types.Map, *types.Pointer, *types.Signature, *types.Slice:
 		return ast.NewIdent("nil")
 	case *types.Struct:
-		texpr := typeExpr(fset, f, pkg, typ) // typ because we want the name here.
+		texpr := TypeExpr(fset, f, pkg, typ) // typ because we want the name here.
 		if texpr == nil {
 			return nil
 		}
@@ -56,7 +56,7 @@
 			Type: texpr,
 		}
 	case *types.Array:
-		texpr := typeExpr(fset, f, pkg, u.Elem())
+		texpr := TypeExpr(fset, f, pkg, u.Elem())
 		if texpr == nil {
 			return nil
 		}
@@ -70,7 +70,7 @@
 	return nil
 }
 
-func typeExpr(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
+func TypeExpr(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
 	switch t := typ.(type) {
 	case *types.Basic:
 		switch t.Kind() {
@@ -101,6 +101,11 @@
 			X:   ast.NewIdent(pkgName),
 			Sel: ast.NewIdent(t.Obj().Name()),
 		}
+	case *types.Pointer:
+		return &ast.UnaryExpr{
+			Op: token.MUL,
+			X:  TypeExpr(fset, f, pkg, t.Elem()),
+		}
 	default:
 		return nil // TODO: anonymous structs, but who does that
 	}
diff --git a/internal/fakenet/conn.go b/internal/fakenet/conn.go
new file mode 100644
index 0000000..c9cdaf2
--- /dev/null
+++ b/internal/fakenet/conn.go
@@ -0,0 +1,129 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package fakenet
+
+import (
+	"io"
+	"net"
+	"sync"
+	"time"
+)
+
+// NewConn returns a net.Conn built on top of the supplied reader and writer.
+// It decouples the read and write on the conn from the underlying stream
+// to enable Close to abort ones that are in progress.
+// It's primary use is to fake a network connection from stdin and stdout.
+func NewConn(name string, in io.ReadCloser, out io.WriteCloser) net.Conn {
+	c := &fakeConn{
+		name:   name,
+		reader: newFeeder(in.Read),
+		writer: newFeeder(out.Write),
+		in:     in,
+		out:    out,
+	}
+	go c.reader.run()
+	go c.writer.run()
+	return c
+}
+
+type fakeConn struct {
+	name   string
+	reader *connFeeder
+	writer *connFeeder
+	in     io.ReadCloser
+	out    io.WriteCloser
+}
+
+type fakeAddr string
+
+// connFeeder serializes calls to the source function (io.Reader.Read or
+// io.Writer.Write) by delegating them to a channel. This also allows calls to
+// be intercepted when the connection is closed, and cancelled early if the
+// connection is closed while the calls are still outstanding.
+type connFeeder struct {
+	source func([]byte) (int, error)
+	input  chan []byte
+	result chan feedResult
+	mu     sync.Mutex
+	closed bool
+	done   chan struct{}
+}
+
+type feedResult struct {
+	n   int
+	err error
+}
+
+func (c *fakeConn) Close() error {
+	c.reader.close()
+	c.writer.close()
+	c.in.Close()
+	c.out.Close()
+	return nil
+}
+
+func (c *fakeConn) Read(b []byte) (n int, err error)   { return c.reader.do(b) }
+func (c *fakeConn) Write(b []byte) (n int, err error)  { return c.writer.do(b) }
+func (c *fakeConn) LocalAddr() net.Addr                { return fakeAddr(c.name) }
+func (c *fakeConn) RemoteAddr() net.Addr               { return fakeAddr(c.name) }
+func (c *fakeConn) SetDeadline(t time.Time) error      { return nil }
+func (c *fakeConn) SetReadDeadline(t time.Time) error  { return nil }
+func (c *fakeConn) SetWriteDeadline(t time.Time) error { return nil }
+func (a fakeAddr) Network() string                     { return "fake" }
+func (a fakeAddr) String() string                      { return string(a) }
+
+func newFeeder(source func([]byte) (int, error)) *connFeeder {
+	return &connFeeder{
+		source: source,
+		input:  make(chan []byte),
+		result: make(chan feedResult),
+		done:   make(chan struct{}),
+	}
+}
+
+func (f *connFeeder) close() {
+	f.mu.Lock()
+	if !f.closed {
+		f.closed = true
+		close(f.done)
+	}
+	f.mu.Unlock()
+}
+
+func (f *connFeeder) do(b []byte) (n int, err error) {
+	// send the request to the worker
+	select {
+	case f.input <- b:
+	case <-f.done:
+		return 0, io.EOF
+	}
+	// get the result from the worker
+	select {
+	case r := <-f.result:
+		return r.n, r.err
+	case <-f.done:
+		return 0, io.EOF
+	}
+}
+
+func (f *connFeeder) run() {
+	var b []byte
+	for {
+		// wait for an input request
+		select {
+		case b = <-f.input:
+		case <-f.done:
+			return
+		}
+		// invoke the underlying method
+		n, err := f.source(b)
+		// send the result back to the requester
+		select {
+		case f.result <- feedResult{n: n, err: err}:
+		case <-f.done:
+			return
+		}
+	}
+}
diff --git a/internal/fastwalk/fastwalk_test.go b/internal/fastwalk/fastwalk_test.go
index a6d9bea..d896aeb 100644
--- a/internal/fastwalk/fastwalk_test.go
+++ b/internal/fastwalk/fastwalk_test.go
@@ -40,6 +40,8 @@
 		t.Fatal(err)
 	}
 	defer os.RemoveAll(tempdir)
+
+	symlinks := map[string]string{}
 	for path, contents := range files {
 		file := filepath.Join(tempdir, "/src", path)
 		if err := os.MkdirAll(filepath.Dir(file), 0755); err != nil {
@@ -47,7 +49,7 @@
 		}
 		var err error
 		if strings.HasPrefix(contents, "LINK:") {
-			err = os.Symlink(strings.TrimPrefix(contents, "LINK:"), file)
+			symlinks[file] = filepath.FromSlash(strings.TrimPrefix(contents, "LINK:"))
 		} else {
 			err = ioutil.WriteFile(file, []byte(contents), 0644)
 		}
@@ -55,21 +57,38 @@
 			t.Fatal(err)
 		}
 	}
+
+	// Create symlinks after all other files. Otherwise, directory symlinks on
+	// Windows are unusable (see https://golang.org/issue/39183).
+	for file, dst := range symlinks {
+		err = os.Symlink(dst, file)
+		if err != nil {
+			if writeErr := ioutil.WriteFile(file, []byte(dst), 0644); writeErr == nil {
+				// Couldn't create symlink, but could write the file.
+				// Probably this filesystem doesn't support symlinks.
+				// (Perhaps we are on an older Windows and not running as administrator.)
+				t.Skipf("skipping because symlinks appear to be unsupported: %v", err)
+			}
+		}
+	}
+
 	got := map[string]os.FileMode{}
 	var mu sync.Mutex
-	if err := fastwalk.Walk(tempdir, func(path string, typ os.FileMode) error {
+	err = fastwalk.Walk(tempdir, func(path string, typ os.FileMode) error {
 		mu.Lock()
 		defer mu.Unlock()
 		if !strings.HasPrefix(path, tempdir) {
-			t.Fatalf("bogus prefix on %q, expect %q", path, tempdir)
+			t.Errorf("bogus prefix on %q, expect %q", path, tempdir)
 		}
 		key := filepath.ToSlash(strings.TrimPrefix(path, tempdir))
 		if old, dup := got[key]; dup {
-			t.Fatalf("callback called twice for key %q: %v -> %v", key, old, typ)
+			t.Errorf("callback called twice for key %q: %v -> %v", key, old, typ)
 		}
 		got[key] = typ
 		return callback(path, typ)
-	}); err != nil {
+	})
+
+	if err != nil {
 		t.Fatalf("callback returned: %v", err)
 	}
 	if !reflect.DeepEqual(got, want) {
@@ -116,26 +135,25 @@
 }
 
 func TestFastWalk_Symlink(t *testing.T) {
-	switch runtime.GOOS {
-	case "windows", "plan9":
-		t.Skipf("skipping on %s", runtime.GOOS)
-	}
 	testFastWalk(t, map[string]string{
-		"foo/foo.go": "one",
-		"bar/bar.go": "LINK:../foo.go",
-		"symdir":     "LINK:foo",
+		"foo/foo.go":       "one",
+		"bar/bar.go":       "LINK:../foo/foo.go",
+		"symdir":           "LINK:foo",
+		"broken/broken.go": "LINK:../nonexistent",
 	},
 		func(path string, typ os.FileMode) error {
 			return nil
 		},
 		map[string]os.FileMode{
-			"":                os.ModeDir,
-			"/src":            os.ModeDir,
-			"/src/bar":        os.ModeDir,
-			"/src/bar/bar.go": os.ModeSymlink,
-			"/src/foo":        os.ModeDir,
-			"/src/foo/foo.go": 0,
-			"/src/symdir":     os.ModeSymlink,
+			"":                      os.ModeDir,
+			"/src":                  os.ModeDir,
+			"/src/bar":              os.ModeDir,
+			"/src/bar/bar.go":       os.ModeSymlink,
+			"/src/foo":              os.ModeDir,
+			"/src/foo/foo.go":       0,
+			"/src/symdir":           os.ModeSymlink,
+			"/src/broken":           os.ModeDir,
+			"/src/broken/broken.go": os.ModeSymlink,
 		})
 }
 
@@ -195,11 +213,6 @@
 }
 
 func TestFastWalk_TraverseSymlink(t *testing.T) {
-	switch runtime.GOOS {
-	case "windows", "plan9":
-		t.Skipf("skipping on %s", runtime.GOOS)
-	}
-
 	testFastWalk(t, map[string]string{
 		"foo/foo.go":   "one",
 		"bar/bar.go":   "two",
diff --git a/internal/gocommand/invoke.go b/internal/gocommand/invoke.go
index 9aa7984..83b654b 100644
--- a/internal/gocommand/invoke.go
+++ b/internal/gocommand/invoke.go
@@ -23,57 +23,106 @@
 // An Runner will run go command invocations and serialize
 // them if it sees a concurrency error.
 type Runner struct {
-	// LoadMu guards packages.Load calls and associated state.
-	loadMu         sync.Mutex
-	serializeLoads int
+	// once guards the runner initialization.
+	once sync.Once
+
+	// inFlight tracks available workers.
+	inFlight chan struct{}
+
+	// serialized guards the ability to run a go command serially,
+	// to avoid deadlocks when claiming workers.
+	serialized chan struct{}
+}
+
+const maxInFlight = 10
+
+func (runner *Runner) initialize() {
+	runner.once.Do(func() {
+		runner.inFlight = make(chan struct{}, maxInFlight)
+		runner.serialized = make(chan struct{}, 1)
+	})
 }
 
 // 1.13: go: updates to go.mod needed, but contents have changed
 // 1.14: go: updating go.mod: existing contents have changed since last read
 var modConcurrencyError = regexp.MustCompile(`go:.*go.mod.*contents have changed`)
 
-// Run calls Runner.RunRaw, serializing requests if they fight over
-// go.mod changes.
+// Run is a convenience wrapper around RunRaw.
+// It returns only stdout and a "friendly" error.
 func (runner *Runner) Run(ctx context.Context, inv Invocation) (*bytes.Buffer, error) {
 	stdout, _, friendly, _ := runner.RunRaw(ctx, inv)
 	return stdout, friendly
 }
 
-// RunRaw calls Invocation.runRaw, serializing requests if they fight over
+// RunPiped runs the invocation serially, always waiting for any concurrent
+// invocations to complete first.
+func (runner *Runner) RunPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) error {
+	_, err := runner.runPiped(ctx, inv, stdout, stderr)
+	return err
+}
+
+// RunRaw runs the invocation, serializing requests only if they fight over
 // go.mod changes.
 func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) {
-	// We want to run invocations concurrently as much as possible. However,
-	// if go.mod updates are needed, only one can make them and the others will
-	// fail. We need to retry in those cases, but we don't want to thrash so
-	// badly we never recover. To avoid that, once we've seen one concurrency
-	// error, start serializing everything until the backlog has cleared out.
-	runner.loadMu.Lock()
-	var locked bool // If true, we hold the mutex and have incremented.
-	if runner.serializeLoads == 0 {
-		runner.loadMu.Unlock()
-	} else {
-		locked = true
-		runner.serializeLoads++
-	}
-	defer func() {
-		if locked {
-			runner.serializeLoads--
-			runner.loadMu.Unlock()
-		}
-	}()
+	// Make sure the runner is always initialized.
+	runner.initialize()
 
-	for {
-		stdout, stderr, friendlyErr, err := inv.runRaw(ctx)
-		if friendlyErr == nil || !modConcurrencyError.MatchString(friendlyErr.Error()) {
-			return stdout, stderr, friendlyErr, err
-		}
-		event.Error(ctx, "Load concurrency error, will retry serially", err)
-		if !locked {
-			runner.loadMu.Lock()
-			runner.serializeLoads++
-			locked = true
+	// First, try to run the go command concurrently.
+	stdout, stderr, friendlyErr, err := runner.runConcurrent(ctx, inv)
+
+	// If we encounter a load concurrency error, we need to retry serially.
+	if friendlyErr == nil || !modConcurrencyError.MatchString(friendlyErr.Error()) {
+		return stdout, stderr, friendlyErr, err
+	}
+	event.Error(ctx, "Load concurrency error, will retry serially", err)
+
+	// Run serially by calling runPiped.
+	stdout.Reset()
+	stderr.Reset()
+	friendlyErr, err = runner.runPiped(ctx, inv, stdout, stderr)
+	return stdout, stderr, friendlyErr, err
+}
+
+func (runner *Runner) runConcurrent(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) {
+	// Wait for 1 worker to become available.
+	select {
+	case <-ctx.Done():
+		return nil, nil, nil, ctx.Err()
+	case runner.inFlight <- struct{}{}:
+		defer func() { <-runner.inFlight }()
+	}
+
+	stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{}
+	friendlyErr, err := inv.runWithFriendlyError(ctx, stdout, stderr)
+	return stdout, stderr, friendlyErr, err
+}
+
+func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) (error, error) {
+	// Make sure the runner is always initialized.
+	runner.initialize()
+
+	// Acquire the serialization lock. This avoids deadlocks between two
+	// runPiped commands.
+	select {
+	case <-ctx.Done():
+		return nil, ctx.Err()
+	case runner.serialized <- struct{}{}:
+		defer func() { <-runner.serialized }()
+	}
+
+	// Wait for all in-progress go commands to return before proceeding,
+	// to avoid load concurrency errors.
+	for i := 0; i < maxInFlight; i++ {
+		select {
+		case <-ctx.Done():
+			return nil, ctx.Err()
+		case runner.inFlight <- struct{}{}:
+			// Make sure we always "return" any workers we took.
+			defer func() { <-runner.inFlight }()
 		}
 	}
+
+	return inv.runWithFriendlyError(ctx, stdout, stderr)
 }
 
 // An Invocation represents a call to the go command.
@@ -86,12 +135,8 @@
 	Logf       func(format string, args ...interface{})
 }
 
-// RunRaw is like RunPiped, but also returns the raw stderr and error for callers
-// that want to do low-level error handling/recovery.
-func (i *Invocation) runRaw(ctx context.Context) (stdout *bytes.Buffer, stderr *bytes.Buffer, friendlyError error, rawError error) {
-	stdout = &bytes.Buffer{}
-	stderr = &bytes.Buffer{}
-	rawError = i.RunPiped(ctx, stdout, stderr)
+func (i *Invocation) runWithFriendlyError(ctx context.Context, stdout, stderr io.Writer) (friendlyError error, rawError error) {
+	rawError = i.run(ctx, stdout, stderr)
 	if rawError != nil {
 		friendlyError = rawError
 		// Check for 'go' executable not being found.
@@ -106,8 +151,7 @@
 	return
 }
 
-// RunPiped is like Run, but relies on the given stdout/stderr
-func (i *Invocation) RunPiped(ctx context.Context, stdout, stderr io.Writer) error {
+func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error {
 	log := i.Logf
 	if log == nil {
 		log = func(string, ...interface{}) {}
diff --git a/internal/gocommand/vendor.go b/internal/gocommand/vendor.go
new file mode 100644
index 0000000..1cd8d84
--- /dev/null
+++ b/internal/gocommand/vendor.go
@@ -0,0 +1,102 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gocommand
+
+import (
+	"bytes"
+	"context"
+	"fmt"
+	"os"
+	"path/filepath"
+	"regexp"
+	"strings"
+
+	"golang.org/x/mod/semver"
+)
+
+// ModuleJSON holds information about a module.
+type ModuleJSON struct {
+	Path      string      // module path
+	Replace   *ModuleJSON // replaced by this module
+	Main      bool        // is this the main module?
+	Indirect  bool        // is this module only an indirect dependency of main module?
+	Dir       string      // directory holding files for this module, if any
+	GoMod     string      // path to go.mod file for this module, if any
+	GoVersion string      // go version used in module
+}
+
+var modFlagRegexp = regexp.MustCompile(`-mod[ =](\w+)`)
+
+// VendorEnabled reports whether vendoring is enabled. It takes a *Runner to execute Go commands
+// with the supplied context.Context and Invocation. The Invocation can contain pre-defined fields,
+// of which only Verb and Args are modified to run the appropriate Go command.
+// Inspired by setDefaultBuildMod in modload/init.go
+func VendorEnabled(ctx context.Context, inv Invocation, r *Runner) (*ModuleJSON, bool, error) {
+	mainMod, go114, err := getMainModuleAnd114(ctx, inv, r)
+	if err != nil {
+		return nil, false, err
+	}
+
+	// We check the GOFLAGS to see if there is anything overridden or not.
+	inv.Verb = "env"
+	inv.Args = []string{"GOFLAGS"}
+	stdout, err := r.Run(ctx, inv)
+	if err != nil {
+		return nil, false, err
+	}
+	goflags := string(bytes.TrimSpace(stdout.Bytes()))
+	matches := modFlagRegexp.FindStringSubmatch(goflags)
+	var modFlag string
+	if len(matches) != 0 {
+		modFlag = matches[1]
+	}
+	if modFlag != "" {
+		// Don't override an explicit '-mod=' argument.
+		return mainMod, modFlag == "vendor", nil
+	}
+	if mainMod == nil || !go114 {
+		return mainMod, false, nil
+	}
+	// Check 1.14's automatic vendor mode.
+	if fi, err := os.Stat(filepath.Join(mainMod.Dir, "vendor")); err == nil && fi.IsDir() {
+		if mainMod.GoVersion != "" && semver.Compare("v"+mainMod.GoVersion, "v1.14") >= 0 {
+			// The Go version is at least 1.14, and a vendor directory exists.
+			// Set -mod=vendor by default.
+			return mainMod, true, nil
+		}
+	}
+	return mainMod, false, nil
+}
+
+// getMainModuleAnd114 gets the main module's information and whether the
+// go command in use is 1.14+. This is the information needed to figure out
+// if vendoring should be enabled.
+func getMainModuleAnd114(ctx context.Context, inv Invocation, r *Runner) (*ModuleJSON, bool, error) {
+	const format = `{{.Path}}
+{{.Dir}}
+{{.GoMod}}
+{{.GoVersion}}
+{{range context.ReleaseTags}}{{if eq . "go1.14"}}{{.}}{{end}}{{end}}
+`
+	inv.Verb = "list"
+	inv.Args = []string{"-m", "-f", format}
+	stdout, err := r.Run(ctx, inv)
+	if err != nil {
+		return nil, false, err
+	}
+
+	lines := strings.Split(stdout.String(), "\n")
+	if len(lines) < 5 {
+		return nil, false, fmt.Errorf("unexpected stdout: %q", stdout.String())
+	}
+	mod := &ModuleJSON{
+		Path:      lines[0],
+		Dir:       lines[1],
+		GoMod:     lines[2],
+		GoVersion: lines[3],
+		Main:      true,
+	}
+	return mod, lines[4] == "go1.14", nil
+}
diff --git a/internal/imports/fix.go b/internal/imports/fix.go
index 264d001..36292d7 100644
--- a/internal/imports/fix.go
+++ b/internal/imports/fix.go
@@ -50,7 +50,8 @@
 		return
 	},
 	func(_ *ProcessEnv, importPath string) (num int, ok bool) {
-		if strings.Contains(importPath, ".") {
+		firstComponent := strings.Split(importPath, "/")[0]
+		if strings.Contains(firstComponent, ".") {
 			return 1, true
 		}
 		return
@@ -1324,7 +1325,10 @@
 		fullFile := filepath.Join(dir, fi.Name())
 		f, err := parser.ParseFile(fset, fullFile, nil, 0)
 		if err != nil {
-			return "", nil, fmt.Errorf("parsing %s: %v", fullFile, err)
+			if env.Logf != nil {
+				env.Logf("error parsing %v: %v", fullFile, err)
+			}
+			continue
 		}
 		if f.Name.Name == "documentation" {
 			// Special case from go/build.ImportDir, not
diff --git a/internal/imports/fix_test.go b/internal/imports/fix_test.go
index 68522b4..1e78687 100644
--- a/internal/imports/fix_test.go
+++ b/internal/imports/fix_test.go
@@ -846,7 +846,6 @@
 var _ = fmt.Sprintf
 `,
 	},
-
 	{
 		name: "import_grouping_not_path_dependent_no_groups",
 		in: `package main
@@ -2696,3 +2695,36 @@
 		wg.Wait()
 	})
 }
+
+func TestNonlocalDot(t *testing.T) {
+	const input = `package main
+import (
+	"fmt"
+)
+var _, _ = fmt.Sprintf, dot.Dot
+`
+	const want = `package main
+
+import (
+	"fmt"
+	"noninternet/dot.v1/dot"
+)
+
+var _, _ = fmt.Sprintf, dot.Dot
+`
+	testConfig{
+		modules: []packagestest.Module{
+			{
+				Name:  "golang.org/fake",
+				Files: fm{"x.go": input},
+			},
+			{
+				Name: "noninternet/dot.v1",
+				Files: fm{
+					"dot/dot.go": "package dot\nfunc Dot(){}\n",
+				},
+			},
+		},
+		gopathOnly: true, // our modules testing setup doesn't allow modules without dots.
+	}.processTest(t, "golang.org/fake", "x.go", nil, nil, want)
+}
diff --git a/internal/imports/mod.go b/internal/imports/mod.go
index 69e3eec..4e816e8 100644
--- a/internal/imports/mod.go
+++ b/internal/imports/mod.go
@@ -15,7 +15,7 @@
 	"strings"
 
 	"golang.org/x/mod/module"
-	"golang.org/x/mod/semver"
+	"golang.org/x/tools/internal/gocommand"
 	"golang.org/x/tools/internal/gopathwalk"
 )
 
@@ -24,31 +24,21 @@
 type ModuleResolver struct {
 	env            *ProcessEnv
 	moduleCacheDir string
-	dummyVendorMod *ModuleJSON // If vendoring is enabled, the pseudo-module that represents the /vendor directory.
+	dummyVendorMod *gocommand.ModuleJSON // If vendoring is enabled, the pseudo-module that represents the /vendor directory.
 	roots          []gopathwalk.Root
 	scanSema       chan struct{} // scanSema prevents concurrent scans and guards scannedRoots.
 	scannedRoots   map[gopathwalk.Root]bool
 
 	initialized   bool
-	main          *ModuleJSON
-	modsByModPath []*ModuleJSON // All modules, ordered by # of path components in module Path...
-	modsByDir     []*ModuleJSON // ...or Dir.
+	main          *gocommand.ModuleJSON
+	modsByModPath []*gocommand.ModuleJSON // All modules, ordered by # of path components in module Path...
+	modsByDir     []*gocommand.ModuleJSON // ...or Dir.
 
 	// moduleCacheCache stores information about the module cache.
 	moduleCacheCache *dirInfoCache
 	otherCache       *dirInfoCache
 }
 
-type ModuleJSON struct {
-	Path      string      // module path
-	Replace   *ModuleJSON // replaced by this module
-	Main      bool        // is this the main module?
-	Indirect  bool        // is this module only an indirect dependency of main module?
-	Dir       string      // directory holding files for this module, if any
-	GoMod     string      // path to go.mod file for this module, if any
-	GoVersion string      // go version used in module
-}
-
 func newModuleResolver(e *ProcessEnv) *ModuleResolver {
 	r := &ModuleResolver{
 		env:      e,
@@ -62,7 +52,14 @@
 	if r.initialized {
 		return nil
 	}
-	mainMod, vendorEnabled, err := vendorEnabled(r.env)
+
+	inv := gocommand.Invocation{
+		BuildFlags: r.env.BuildFlags,
+		Env:        r.env.env(),
+		Logf:       r.env.Logf,
+		WorkingDir: r.env.WorkingDir,
+	}
+	mainMod, vendorEnabled, err := gocommand.VendorEnabled(context.TODO(), inv, r.env.GocmdRunner)
 	if err != nil {
 		return err
 	}
@@ -71,12 +68,12 @@
 		// Vendor mode is on, so all the non-Main modules are irrelevant,
 		// and we need to search /vendor for everything.
 		r.main = mainMod
-		r.dummyVendorMod = &ModuleJSON{
+		r.dummyVendorMod = &gocommand.ModuleJSON{
 			Path: "",
 			Dir:  filepath.Join(mainMod.Dir, "vendor"),
 		}
-		r.modsByModPath = []*ModuleJSON{mainMod, r.dummyVendorMod}
-		r.modsByDir = []*ModuleJSON{mainMod, r.dummyVendorMod}
+		r.modsByModPath = []*gocommand.ModuleJSON{mainMod, r.dummyVendorMod}
+		r.modsByDir = []*gocommand.ModuleJSON{mainMod, r.dummyVendorMod}
 	} else {
 		// Vendor mode is off, so run go list -m ... to find everything.
 		r.initAllMods()
@@ -106,7 +103,7 @@
 	if vendorEnabled {
 		r.roots = append(r.roots, gopathwalk.Root{r.dummyVendorMod.Dir, gopathwalk.RootOther})
 	} else {
-		addDep := func(mod *ModuleJSON) {
+		addDep := func(mod *gocommand.ModuleJSON) {
 			if mod.Replace == nil {
 				// This is redundant with the cache, but we'll skip it cheaply enough.
 				r.roots = append(r.roots, gopathwalk.Root{mod.Dir, gopathwalk.RootModuleCache})
@@ -151,7 +148,7 @@
 		return err
 	}
 	for dec := json.NewDecoder(stdout); dec.More(); {
-		mod := &ModuleJSON{}
+		mod := &gocommand.ModuleJSON{}
 		if err := dec.Decode(mod); err != nil {
 			return err
 		}
@@ -197,7 +194,7 @@
 
 // findPackage returns the module and directory that contains the package at
 // the given import path, or returns nil, "" if no module is in scope.
-func (r *ModuleResolver) findPackage(importPath string) (*ModuleJSON, string) {
+func (r *ModuleResolver) findPackage(importPath string) (*gocommand.ModuleJSON, string) {
 	// This can't find packages in the stdlib, but that's harmless for all
 	// the existing code paths.
 	for _, m := range r.modsByModPath {
@@ -283,7 +280,7 @@
 
 // findModuleByDir returns the module that contains dir, or nil if no such
 // module is in scope.
-func (r *ModuleResolver) findModuleByDir(dir string) *ModuleJSON {
+func (r *ModuleResolver) findModuleByDir(dir string) *gocommand.ModuleJSON {
 	// This is quite tricky and may not be correct. dir could be:
 	// - a package in the main module.
 	// - a replace target underneath the main module's directory.
@@ -310,7 +307,7 @@
 
 // dirIsNestedModule reports if dir is contained in a nested module underneath
 // mod, not actually in mod.
-func (r *ModuleResolver) dirIsNestedModule(dir string, mod *ModuleJSON) bool {
+func (r *ModuleResolver) dirIsNestedModule(dir string, mod *gocommand.ModuleJSON) bool {
 	if !strings.HasPrefix(dir, mod.Dir) {
 		return false
 	}
@@ -490,7 +487,7 @@
 	return modRelevance(mod)
 }
 
-func modRelevance(mod *ModuleJSON) int {
+func modRelevance(mod *gocommand.ModuleJSON) int {
 	switch {
 	case mod == nil: // out of scope
 		return MaxRelevance - 4
@@ -656,63 +653,3 @@
 	}
 	return "" // missing module path
 }
-
-var modFlagRegexp = regexp.MustCompile(`-mod[ =](\w+)`)
-
-// vendorEnabled indicates if vendoring is enabled.
-// Inspired by setDefaultBuildMod in modload/init.go
-func vendorEnabled(env *ProcessEnv) (*ModuleJSON, bool, error) {
-	mainMod, go114, err := getMainModuleAnd114(env)
-	if err != nil {
-		return nil, false, err
-	}
-	matches := modFlagRegexp.FindStringSubmatch(env.GOFLAGS)
-	var modFlag string
-	if len(matches) != 0 {
-		modFlag = matches[1]
-	}
-	if modFlag != "" {
-		// Don't override an explicit '-mod=' argument.
-		return mainMod, modFlag == "vendor", nil
-	}
-	if mainMod == nil || !go114 {
-		return mainMod, false, nil
-	}
-	// Check 1.14's automatic vendor mode.
-	if fi, err := os.Stat(filepath.Join(mainMod.Dir, "vendor")); err == nil && fi.IsDir() {
-		if mainMod.GoVersion != "" && semver.Compare("v"+mainMod.GoVersion, "v1.14") >= 0 {
-			// The Go version is at least 1.14, and a vendor directory exists.
-			// Set -mod=vendor by default.
-			return mainMod, true, nil
-		}
-	}
-	return mainMod, false, nil
-}
-
-// getMainModuleAnd114 gets the main module's information and whether the
-// go command in use is 1.14+. This is the information needed to figure out
-// if vendoring should be enabled.
-func getMainModuleAnd114(env *ProcessEnv) (*ModuleJSON, bool, error) {
-	const format = `{{.Path}}
-{{.Dir}}
-{{.GoMod}}
-{{.GoVersion}}
-{{range context.ReleaseTags}}{{if eq . "go1.14"}}{{.}}{{end}}{{end}}
-`
-	stdout, err := env.invokeGo(context.TODO(), "list", "-m", "-f", format)
-	if err != nil {
-		return nil, false, nil
-	}
-	lines := strings.Split(stdout.String(), "\n")
-	if len(lines) < 5 {
-		return nil, false, fmt.Errorf("unexpected stdout: %q", stdout)
-	}
-	mod := &ModuleJSON{
-		Path:      lines[0],
-		Dir:       lines[1],
-		GoMod:     lines[2],
-		GoVersion: lines[3],
-		Main:      true,
-	}
-	return mod, lines[4] == "go1.14", nil
-}
diff --git a/internal/imports/mod_112_test.go b/internal/imports/mod_112_test.go
deleted file mode 100644
index 106db7c..0000000
--- a/internal/imports/mod_112_test.go
+++ /dev/null
@@ -1,22 +0,0 @@
-// +build go1.12
-
-package imports
-
-import (
-	"context"
-	"testing"
-)
-
-// Tests that we handle GO111MODULE=on with no go.mod file. See #30855.
-func TestNoMainModule(t *testing.T) {
-	mt := setup(t, `
--- x.go --
-package x
-`, "")
-	defer mt.cleanup()
-	if _, err := mt.env.invokeGo(context.Background(), "mod", "download", "rsc.io/quote@v1.5.1"); err != nil {
-		t.Fatal(err)
-	}
-
-	mt.assertScanFinds("rsc.io/quote", "quote")
-}
diff --git a/internal/imports/mod_114_test.go b/internal/imports/mod_114_test.go
deleted file mode 100644
index 10c1151..0000000
--- a/internal/imports/mod_114_test.go
+++ /dev/null
@@ -1,11 +0,0 @@
-// +build go1.14
-
-package imports
-
-import (
-	"testing"
-)
-
-func TestModVendorAuto_114(t *testing.T) {
-	testModVendorAuto(t, true)
-}
diff --git a/internal/imports/mod_pre114_test.go b/internal/imports/mod_pre114_test.go
deleted file mode 100644
index 8c307c1..0000000
--- a/internal/imports/mod_pre114_test.go
+++ /dev/null
@@ -1,11 +0,0 @@
-// +build !go1.14
-
-package imports
-
-import (
-	"testing"
-)
-
-func TestModVendorAuto_Pre114(t *testing.T) {
-	testModVendorAuto(t, false)
-}
diff --git a/internal/imports/mod_test.go b/internal/imports/mod_test.go
index 352fd20..fa1a9c6 100644
--- a/internal/imports/mod_test.go
+++ b/internal/imports/mod_test.go
@@ -1,5 +1,3 @@
-// +build go1.11
-
 package imports
 
 import (
@@ -21,6 +19,7 @@
 	"golang.org/x/mod/module"
 	"golang.org/x/tools/internal/gocommand"
 	"golang.org/x/tools/internal/gopathwalk"
+	"golang.org/x/tools/internal/proxydir"
 	"golang.org/x/tools/internal/testenv"
 	"golang.org/x/tools/txtar"
 )
@@ -247,7 +246,7 @@
 
 // Tests that -mod=vendor is auto-enabled only for go1.14 and higher.
 // Vaguely inspired by mod_vendor_auto.txt.
-func testModVendorAuto(t *testing.T, wantEnabled bool) {
+func TestModVendorAuto(t *testing.T) {
 	mt := setup(t, `
 -- go.mod --
 module m
@@ -265,7 +264,7 @@
 	}
 
 	wantDir := `pkg.*mod.*/sampler@.*$`
-	if wantEnabled {
+	if testenv.Go1Point() >= 14 {
 		wantDir = `/vendor/`
 	}
 	mt.assertModuleFoundInDir("rsc.io/sampler", "sampler", wantDir)
@@ -550,6 +549,21 @@
 	mt.assertModuleFoundInDir("example.com/vv", "v", `main/v12$`)
 }
 
+// Tests that we handle GO111MODULE=on with no go.mod file. See #30855.
+func TestNoMainModule(t *testing.T) {
+	testenv.NeedsGo1Point(t, 12)
+	mt := setup(t, `
+-- x.go --
+package x
+`, "")
+	defer mt.cleanup()
+	if _, err := mt.env.invokeGo(context.Background(), "mod", "download", "rsc.io/quote@v1.5.1"); err != nil {
+		t.Fatal(err)
+	}
+
+	mt.assertScanFinds("rsc.io/quote", "quote")
+}
+
 // assertFound asserts that the package at importPath is found to have pkgName,
 // and that scanning for pkgName finds it at importPath.
 func (t *modTest) assertFound(importPath, pkgName string) (string, *pkg) {
@@ -647,6 +661,7 @@
 // in testdata/mod, along the lines of TestScript in cmd/go.
 func setup(t *testing.T, main, wd string) *modTest {
 	t.Helper()
+	testenv.NeedsGo1Point(t, 11)
 	testenv.NeedsTool(t, "go")
 
 	proxyOnce.Do(func() {
@@ -674,7 +689,7 @@
 		GOROOT:      build.Default.GOROOT,
 		GOPATH:      filepath.Join(dir, "gopath"),
 		GO111MODULE: "on",
-		GOPROXY:     proxyDirToURL(proxyDir),
+		GOPROXY:     proxydir.ToURL(proxyDir),
 		GOSUMDB:     "off",
 		WorkingDir:  filepath.Join(mainDir, wd),
 		GocmdRunner: &gocommand.Runner{},
@@ -834,6 +849,7 @@
 
 // Tests that crud in the module cache is ignored.
 func TestInvalidModCache(t *testing.T) {
+	testenv.NeedsGo1Point(t, 11)
 	dir, err := ioutil.TempDir("", t.Name())
 	if err != nil {
 		t.Fatal(err)
@@ -920,6 +936,7 @@
 }
 
 func BenchmarkScanModCache(b *testing.B) {
+	testenv.NeedsGo1Point(b, 11)
 	env := &ProcessEnv{
 		GOPATH:      build.Default.GOPATH,
 		GOROOT:      build.Default.GOROOT,
diff --git a/internal/imports/proxy_112_test.go b/internal/imports/proxy_112_test.go
deleted file mode 100644
index 732879a..0000000
--- a/internal/imports/proxy_112_test.go
+++ /dev/null
@@ -1,20 +0,0 @@
-// +build !go1.13
-
-// Copyright 2019 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package imports
-
-import "path/filepath"
-
-// TODO: use proxy functionality in golang.org/x/tools/go/packages/packagestest
-// instead of copying it here.
-
-func proxyDirToURL(dir string) string {
-	// Prior to go1.13, the Go command on Windows only accepted GOPROXY file URLs
-	// of the form file://C:/path/to/proxy. This was incorrect: when parsed, "C:"
-	// is interpreted as the host. See golang.org/issue/6027. This has been
-	// fixed in go1.13, but we emit the old format for old releases.
-	return "file://" + filepath.ToSlash(dir)
-}
diff --git a/internal/imports/proxy_113_test.go b/internal/imports/proxy_113_test.go
deleted file mode 100644
index f6f6be5..0000000
--- a/internal/imports/proxy_113_test.go
+++ /dev/null
@@ -1,24 +0,0 @@
-// +build go1.13
-
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package imports
-
-import (
-	"path/filepath"
-	"strings"
-)
-
-// TODO: use proxy functionality in golang.org/x/tools/go/packages/packagestest
-// instead of copying it here.
-
-func proxyDirToURL(dir string) string {
-	// file URLs on Windows must start with file:///. See golang.org/issue/6027.
-	path := filepath.ToSlash(dir)
-	if !strings.HasPrefix(path, "/") {
-		path = "/" + path
-	}
-	return "file://" + path
-}
diff --git a/internal/jsonrpc2/conn.go b/internal/jsonrpc2/conn.go
new file mode 100644
index 0000000..ca7752d
--- /dev/null
+++ b/internal/jsonrpc2/conn.go
@@ -0,0 +1,262 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package jsonrpc2
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"sync"
+	"sync/atomic"
+
+	"golang.org/x/tools/internal/event"
+	"golang.org/x/tools/internal/event/label"
+	"golang.org/x/tools/internal/lsp/debug/tag"
+)
+
+// Conn is the common interface to jsonrpc clients and servers.
+// Conn is bidirectional; it does not have a designated server or client end.
+// It manages the jsonrpc2 protocol, connecting responses back to their calls.
+type Conn interface {
+	// Call invokes the target method and waits for a response.
+	// The params will be marshaled to JSON before sending over the wire, and will
+	// be handed to the method invoked.
+	// The response will be unmarshaled from JSON into the result.
+	// The id returned will be unique from this connection, and can be used for
+	// logging or tracking.
+	Call(ctx context.Context, method string, params, result interface{}) (ID, error)
+
+	// Notify invokes the target method but does not wait for a response.
+	// The params will be marshaled to JSON before sending over the wire, and will
+	// be handed to the method invoked.
+	Notify(ctx context.Context, method string, params interface{}) error
+
+	// Go starts a goroutine to handle the connection.
+	// It must be called exactly once for each Conn.
+	// It returns immediately.
+	// You must block on Done() to wait for the connection to shut down.
+	// This is a temporary measure, this should be started automatically in the
+	// future.
+	Go(ctx context.Context, handler Handler)
+
+	// Close closes the connection and it's underlying stream.
+	// It does not wait for the close to complete, use the Done() channel for
+	// that.
+	Close() error
+
+	// Done returns a channel that will be closed when the processing goroutine
+	// has terminated, which will happen if Close() is called or an underlying
+	// stream is closed.
+	Done() <-chan struct{}
+
+	// Err returns an error if there was one from within the processing goroutine.
+	// If err returns non nil, the connection will be already closed or closing.
+	Err() error
+}
+
+type conn struct {
+	seq       int64      // must only be accessed using atomic operations
+	writeMu   sync.Mutex // protects writes to the stream
+	stream    Stream
+	pendingMu sync.Mutex // protects the pending map
+	pending   map[ID]chan *Response
+
+	done chan struct{}
+	err  atomic.Value
+}
+
+// NewConn creates a new connection object around the supplied stream.
+func NewConn(s Stream) Conn {
+	conn := &conn{
+		stream:  s,
+		pending: make(map[ID]chan *Response),
+		done:    make(chan struct{}),
+	}
+	return conn
+}
+
+func (c *conn) Notify(ctx context.Context, method string, params interface{}) (err error) {
+	notify, err := NewNotification(method, params)
+	if err != nil {
+		return fmt.Errorf("marshaling notify parameters: %v", err)
+	}
+	ctx, done := event.Start(ctx, method,
+		tag.Method.Of(method),
+		tag.RPCDirection.Of(tag.Outbound),
+	)
+	defer func() {
+		recordStatus(ctx, err)
+		done()
+	}()
+
+	event.Metric(ctx, tag.Started.Of(1))
+	n, err := c.write(ctx, notify)
+	event.Metric(ctx, tag.SentBytes.Of(n))
+	return err
+}
+
+func (c *conn) Call(ctx context.Context, method string, params, result interface{}) (_ ID, err error) {
+	// generate a new request identifier
+	id := ID{number: atomic.AddInt64(&c.seq, 1)}
+	call, err := NewCall(id, method, params)
+	if err != nil {
+		return id, fmt.Errorf("marshaling call parameters: %v", err)
+	}
+	ctx, done := event.Start(ctx, method,
+		tag.Method.Of(method),
+		tag.RPCDirection.Of(tag.Outbound),
+		tag.RPCID.Of(fmt.Sprintf("%q", id)),
+	)
+	defer func() {
+		recordStatus(ctx, err)
+		done()
+	}()
+	event.Metric(ctx, tag.Started.Of(1))
+	// We have to add ourselves to the pending map before we send, otherwise we
+	// are racing the response. Also add a buffer to rchan, so that if we get a
+	// wire response between the time this call is cancelled and id is deleted
+	// from c.pending, the send to rchan will not block.
+	rchan := make(chan *Response, 1)
+	c.pendingMu.Lock()
+	c.pending[id] = rchan
+	c.pendingMu.Unlock()
+	defer func() {
+		c.pendingMu.Lock()
+		delete(c.pending, id)
+		c.pendingMu.Unlock()
+	}()
+	// now we are ready to send
+	n, err := c.write(ctx, call)
+	event.Metric(ctx, tag.SentBytes.Of(n))
+	if err != nil {
+		// sending failed, we will never get a response, so don't leave it pending
+		return id, err
+	}
+	// now wait for the response
+	select {
+	case response := <-rchan:
+		// is it an error response?
+		if response.err != nil {
+			return id, response.err
+		}
+		if result == nil || len(response.result) == 0 {
+			return id, nil
+		}
+		if err := json.Unmarshal(response.result, result); err != nil {
+			return id, fmt.Errorf("unmarshaling result: %v", err)
+		}
+		return id, nil
+	case <-ctx.Done():
+		return id, ctx.Err()
+	}
+}
+
+func (c *conn) replier(req Request, spanDone func()) Replier {
+	return func(ctx context.Context, result interface{}, err error) error {
+		defer func() {
+			recordStatus(ctx, err)
+			spanDone()
+		}()
+		call, ok := req.(*Call)
+		if !ok {
+			// request was a notify, no need to respond
+			return nil
+		}
+		response, err := NewResponse(call.id, result, err)
+		if err != nil {
+			return err
+		}
+		n, err := c.write(ctx, response)
+		event.Metric(ctx, tag.SentBytes.Of(n))
+		if err != nil {
+			// TODO(iancottrell): if a stream write fails, we really need to shut down
+			// the whole stream
+			return err
+		}
+		return nil
+	}
+}
+
+func (c *conn) write(ctx context.Context, msg Message) (int64, error) {
+	c.writeMu.Lock()
+	defer c.writeMu.Unlock()
+	return c.stream.Write(ctx, msg)
+}
+
+func (c *conn) Go(ctx context.Context, handler Handler) {
+	go c.run(ctx, handler)
+}
+
+func (c *conn) run(ctx context.Context, handler Handler) {
+	defer close(c.done)
+	for {
+		// get the next message
+		msg, n, err := c.stream.Read(ctx)
+		if err != nil {
+			// The stream failed, we cannot continue.
+			c.fail(err)
+			return
+		}
+		switch msg := msg.(type) {
+		case Request:
+			labels := []label.Label{
+				tag.Method.Of(msg.Method()),
+				tag.RPCDirection.Of(tag.Inbound),
+				{}, // reserved for ID if present
+			}
+			if call, ok := msg.(*Call); ok {
+				labels[len(labels)-1] = tag.RPCID.Of(fmt.Sprintf("%q", call.ID()))
+			} else {
+				labels = labels[:len(labels)-1]
+			}
+			reqCtx, spanDone := event.Start(ctx, msg.Method(), labels...)
+			event.Metric(reqCtx,
+				tag.Started.Of(1),
+				tag.ReceivedBytes.Of(n))
+			if err := handler(reqCtx, c.replier(msg, spanDone), msg); err != nil {
+				// delivery failed, not much we can do
+				event.Error(reqCtx, "jsonrpc2 message delivery failed", err)
+			}
+		case *Response:
+			// If method is not set, this should be a response, in which case we must
+			// have an id to send the response back to the caller.
+			c.pendingMu.Lock()
+			rchan, ok := c.pending[msg.id]
+			c.pendingMu.Unlock()
+			if ok {
+				rchan <- msg
+			}
+		}
+	}
+}
+
+func (c *conn) Close() error {
+	return c.stream.Close()
+}
+
+func (c *conn) Done() <-chan struct{} {
+	return c.done
+}
+
+func (c *conn) Err() error {
+	if err := c.err.Load(); err != nil {
+		return err.(error)
+	}
+	return nil
+}
+
+// fail sets a failure condition on the stream and closes it.
+func (c *conn) fail(err error) {
+	c.err.Store(err)
+	c.stream.Close()
+}
+
+func recordStatus(ctx context.Context, err error) {
+	if err != nil {
+		event.Label(ctx, tag.StatusCode.Of("ERROR"))
+	} else {
+		event.Label(ctx, tag.StatusCode.Of("OK"))
+	}
+}
diff --git a/internal/jsonrpc2/jsonrpc2.go b/internal/jsonrpc2/jsonrpc2.go
index c59d4ad..5a52995 100644
--- a/internal/jsonrpc2/jsonrpc2.go
+++ b/internal/jsonrpc2/jsonrpc2.go
@@ -7,212 +7,11 @@
 // It is intended to be compatible with other implementations at the wire level.
 package jsonrpc2
 
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"sync"
-	"sync/atomic"
-
-	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/label"
-	"golang.org/x/tools/internal/lsp/debug/tag"
-)
-
 const (
 	// ErrIdleTimeout is returned when serving timed out waiting for new connections.
 	ErrIdleTimeout = constError("timed out waiting for new connections")
 )
 
-// Conn is a JSON RPC 2 client server connection.
-// Conn is bidirectional; it does not have a designated server or client end.
-type Conn struct {
-	seq       int64 // must only be accessed using atomic operations
-	stream    Stream
-	pendingMu sync.Mutex // protects the pending map
-	pending   map[ID]chan *Response
-}
-
 type constError string
 
 func (e constError) Error() string { return string(e) }
-
-// NewConn creates a new connection object around the supplied stream.
-// You must call Run for the connection to be active.
-func NewConn(s Stream) *Conn {
-	conn := &Conn{
-		stream:  s,
-		pending: make(map[ID]chan *Response),
-	}
-	return conn
-}
-
-// Notify is called to send a notification request over the connection.
-// It will return as soon as the notification has been sent, as no response is
-// possible.
-func (c *Conn) Notify(ctx context.Context, method string, params interface{}) (err error) {
-	notify, err := NewNotification(method, params)
-	if err != nil {
-		return fmt.Errorf("marshaling notify parameters: %v", err)
-	}
-	ctx, done := event.Start(ctx, method,
-		tag.Method.Of(method),
-		tag.RPCDirection.Of(tag.Outbound),
-	)
-	defer func() {
-		recordStatus(ctx, err)
-		done()
-	}()
-
-	event.Metric(ctx, tag.Started.Of(1))
-	n, err := c.stream.Write(ctx, notify)
-	event.Metric(ctx, tag.SentBytes.Of(n))
-	return err
-}
-
-// Call sends a request over the connection and then waits for a response.
-// If the response is not an error, it will be decoded into result.
-// result must be of a type you an pass to json.Unmarshal.
-func (c *Conn) Call(ctx context.Context, method string, params, result interface{}) (_ ID, err error) {
-	// generate a new request identifier
-	id := ID{number: atomic.AddInt64(&c.seq, 1)}
-	call, err := NewCall(id, method, params)
-	if err != nil {
-		return id, fmt.Errorf("marshaling call parameters: %v", err)
-	}
-	ctx, done := event.Start(ctx, method,
-		tag.Method.Of(method),
-		tag.RPCDirection.Of(tag.Outbound),
-		tag.RPCID.Of(fmt.Sprintf("%q", id)),
-	)
-	defer func() {
-		recordStatus(ctx, err)
-		done()
-	}()
-	event.Metric(ctx, tag.Started.Of(1))
-	// We have to add ourselves to the pending map before we send, otherwise we
-	// are racing the response. Also add a buffer to rchan, so that if we get a
-	// wire response between the time this call is cancelled and id is deleted
-	// from c.pending, the send to rchan will not block.
-	rchan := make(chan *Response, 1)
-	c.pendingMu.Lock()
-	c.pending[id] = rchan
-	c.pendingMu.Unlock()
-	defer func() {
-		c.pendingMu.Lock()
-		delete(c.pending, id)
-		c.pendingMu.Unlock()
-	}()
-	// now we are ready to send
-	n, err := c.stream.Write(ctx, call)
-	event.Metric(ctx, tag.SentBytes.Of(n))
-	if err != nil {
-		// sending failed, we will never get a response, so don't leave it pending
-		return id, err
-	}
-	// now wait for the response
-	select {
-	case response := <-rchan:
-		// is it an error response?
-		if response.err != nil {
-			return id, response.err
-		}
-		if result == nil || len(response.result) == 0 {
-			return id, nil
-		}
-		if err := json.Unmarshal(response.result, result); err != nil {
-			return id, fmt.Errorf("unmarshaling result: %v", err)
-		}
-		return id, nil
-	case <-ctx.Done():
-		return id, ctx.Err()
-	}
-}
-
-func replier(conn *Conn, req Request, spanDone func()) Replier {
-	return func(ctx context.Context, result interface{}, err error) error {
-		defer func() {
-			recordStatus(ctx, err)
-			spanDone()
-		}()
-		call, ok := req.(*Call)
-		if !ok {
-			// request was a notify, no need to respond
-			return nil
-		}
-		response, err := NewResponse(call.id, result, err)
-		if err != nil {
-			return err
-		}
-		n, err := conn.stream.Write(ctx, response)
-		event.Metric(ctx, tag.SentBytes.Of(n))
-		if err != nil {
-			// TODO(iancottrell): if a stream write fails, we really need to shut down
-			// the whole stream
-			return err
-		}
-		return nil
-	}
-}
-
-// Run blocks until the connection is terminated, and returns any error that
-// caused the termination.
-// It must be called exactly once for each Conn.
-// It returns only when the reader is closed or there is an error in the stream.
-func (c *Conn) Run(runCtx context.Context, handler Handler) error {
-	for {
-		// get the next message
-		msg, n, err := c.stream.Read(runCtx)
-		if err != nil {
-			// The stream failed, we cannot continue. If the client disconnected
-			// normally, we should get ErrDisconnected here.
-			return err
-		}
-		switch msg := msg.(type) {
-		case Request:
-			labels := []label.Label{
-				tag.Method.Of(msg.Method()),
-				tag.RPCDirection.Of(tag.Inbound),
-				{}, // reserved for ID if present
-			}
-			if call, ok := msg.(*Call); ok {
-				labels[len(labels)-1] = tag.RPCID.Of(fmt.Sprintf("%q", call.ID()))
-			} else {
-				labels = labels[:len(labels)-1]
-			}
-			reqCtx, spanDone := event.Start(runCtx, msg.Method(), labels...)
-			event.Metric(reqCtx,
-				tag.Started.Of(1),
-				tag.ReceivedBytes.Of(n))
-			if err := handler(reqCtx, replier(c, msg, spanDone), msg); err != nil {
-				// delivery failed, not much we can do
-				event.Error(reqCtx, "jsonrpc2 message delivery failed", err)
-			}
-		case *Response:
-			// If method is not set, this should be a response, in which case we must
-			// have an id to send the response back to the caller.
-			c.pendingMu.Lock()
-			rchan, ok := c.pending[msg.id]
-			c.pendingMu.Unlock()
-			if ok {
-				rchan <- msg
-			}
-		}
-	}
-}
-
-func marshalToRaw(obj interface{}) (json.RawMessage, error) {
-	data, err := json.Marshal(obj)
-	if err != nil {
-		return json.RawMessage{}, err
-	}
-	return json.RawMessage(data), nil
-}
-
-func recordStatus(ctx context.Context, err error) {
-	if err != nil {
-		event.Label(ctx, tag.StatusCode.Of("ERROR"))
-	} else {
-		event.Label(ctx, tag.StatusCode.Of("OK"))
-	}
-}
diff --git a/internal/jsonrpc2/jsonrpc2_test.go b/internal/jsonrpc2/jsonrpc2_test.go
index 072f1d5..f62977e 100644
--- a/internal/jsonrpc2/jsonrpc2_test.go
+++ b/internal/jsonrpc2/jsonrpc2_test.go
@@ -7,17 +7,16 @@
 import (
 	"context"
 	"encoding/json"
-	"errors"
 	"flag"
 	"fmt"
-	"io"
+	"net"
 	"path"
 	"reflect"
-	"sync"
 	"testing"
 
 	"golang.org/x/tools/internal/event/export/eventtest"
 	"golang.org/x/tools/internal/jsonrpc2"
+	"golang.org/x/tools/internal/stack/stacktest"
 )
 
 var logRPC = flag.Bool("logrpc", false, "Enable jsonrpc2 communication logging")
@@ -62,6 +61,7 @@
 }
 
 func TestCall(t *testing.T) {
+	stacktest.NoLeak(t)
 	ctx := eventtest.NewContext(context.Background(), t)
 	for _, headers := range []bool{false, true} {
 		name := "Plain"
@@ -90,46 +90,28 @@
 	}
 }
 
-func prepare(ctx context.Context, t *testing.T, withHeaders bool) (*jsonrpc2.Conn, *jsonrpc2.Conn, func()) {
+func prepare(ctx context.Context, t *testing.T, withHeaders bool) (jsonrpc2.Conn, jsonrpc2.Conn, func()) {
 	// make a wait group that can be used to wait for the system to shut down
-	wg := &sync.WaitGroup{}
-	aR, bW := io.Pipe()
-	bR, aW := io.Pipe()
-	a := run(ctx, t, withHeaders, aR, aW, wg)
-	b := run(ctx, t, withHeaders, bR, bW, wg)
+	aPipe, bPipe := net.Pipe()
+	a := run(ctx, withHeaders, aPipe)
+	b := run(ctx, withHeaders, bPipe)
 	return a, b, func() {
-		// we close the main writer, this should cascade through the server and
-		// cause normal shutdown of the entire chain
-		aW.Close()
-		// this should then wait for that entire cascade,
-		wg.Wait()
+		a.Close()
+		b.Close()
+		<-a.Done()
+		<-b.Done()
 	}
 }
 
-func run(ctx context.Context, t *testing.T, withHeaders bool, r io.ReadCloser, w io.WriteCloser, wg *sync.WaitGroup) *jsonrpc2.Conn {
+func run(ctx context.Context, withHeaders bool, nc net.Conn) jsonrpc2.Conn {
 	var stream jsonrpc2.Stream
 	if withHeaders {
-		stream = jsonrpc2.NewHeaderStream(r, w)
+		stream = jsonrpc2.NewHeaderStream(nc)
 	} else {
-		stream = jsonrpc2.NewRawStream(r, w)
+		stream = jsonrpc2.NewRawStream(nc)
 	}
 	conn := jsonrpc2.NewConn(stream)
-	wg.Add(1)
-	go func() {
-		defer func() {
-			// this will happen when Run returns, which means at least one of the
-			// streams has already been closed
-			// we close both streams anyway, this may be redundant but is safe
-			r.Close()
-			w.Close()
-			// and then signal that this connection is done
-			wg.Done()
-		}()
-		err := conn.Run(ctx, testHandler(*logRPC))
-		if err != nil && !errors.Is(err, io.EOF) && !errors.Is(err, io.ErrClosedPipe) {
-			t.Errorf("Stream failed: %v", err)
-		}
-	}()
+	conn.Go(ctx, testHandler(*logRPC))
 	return conn
 }
 
diff --git a/internal/jsonrpc2/messages.go b/internal/jsonrpc2/messages.go
index 45efc53..58d285d 100644
--- a/internal/jsonrpc2/messages.go
+++ b/internal/jsonrpc2/messages.go
@@ -146,7 +146,10 @@
 func (msg *Response) isJSONRPC2Message()      {}
 
 func (r *Response) MarshalJSON() ([]byte, error) {
-	msg := &wireResponse{Result: &r.result, Error: toWireError(r.err), ID: &r.id}
+	msg := &wireResponse{Error: toWireError(r.err), ID: &r.id}
+	if msg.Error == nil {
+		msg.Result = &r.result
+	}
 	data, err := json.Marshal(msg)
 	if err != nil {
 		return data, fmt.Errorf("marshaling notification: %w", err)
@@ -225,3 +228,11 @@
 	}
 	return call, nil
 }
+
+func marshalToRaw(obj interface{}) (json.RawMessage, error) {
+	data, err := json.Marshal(obj)
+	if err != nil {
+		return json.RawMessage{}, err
+	}
+	return json.RawMessage(data), nil
+}
diff --git a/internal/jsonrpc2/serve.go b/internal/jsonrpc2/serve.go
index 1a84941..6589ed3 100644
--- a/internal/jsonrpc2/serve.go
+++ b/internal/jsonrpc2/serve.go
@@ -6,7 +6,9 @@
 
 import (
 	"context"
+	"errors"
 	"fmt"
+	"io"
 	"net"
 	"os"
 	"time"
@@ -20,26 +22,27 @@
 // semantics.
 
 // A StreamServer is used to serve incoming jsonrpc2 clients communicating over
-// a newly created stream.
+// a newly created connection.
 type StreamServer interface {
-	ServeStream(context.Context, Stream) error
+	ServeStream(context.Context, Conn) error
 }
 
 // The ServerFunc type is an adapter that implements the StreamServer interface
 // using an ordinary function.
-type ServerFunc func(context.Context, Stream) error
+type ServerFunc func(context.Context, Conn) error
 
 // ServeStream calls f(ctx, s).
-func (f ServerFunc) ServeStream(ctx context.Context, s Stream) error {
-	return f(ctx, s)
+func (f ServerFunc) ServeStream(ctx context.Context, c Conn) error {
+	return f(ctx, c)
 }
 
 // HandlerServer returns a StreamServer that handles incoming streams using the
 // provided handler.
 func HandlerServer(h Handler) StreamServer {
-	return ServerFunc(func(ctx context.Context, s Stream) error {
-		conn := NewConn(s)
-		return conn.Run(ctx, h)
+	return ServerFunc(func(ctx context.Context, conn Conn) error {
+		conn.Go(ctx, h)
+		<-conn.Done()
+		return conn.Err()
 	})
 }
 
@@ -80,7 +83,7 @@
 			nc, err := ln.Accept()
 			if err != nil {
 				select {
-				case doneListening <- fmt.Errorf("Accept(): %v", err):
+				case doneListening <- fmt.Errorf("Accept(): %w", err):
 				case <-ctx.Done():
 				}
 				return
@@ -95,14 +98,18 @@
 		case netConn := <-newConns:
 			activeConns++
 			connTimer.Stop()
-			stream := NewHeaderStream(netConn, netConn)
+			stream := NewHeaderStream(netConn)
 			go func() {
-				closedConns <- server.ServeStream(ctx, stream)
+				conn := NewConn(stream)
+				closedConns <- server.ServeStream(ctx, conn)
+				stream.Close()
 			}()
 		case err := <-doneListening:
 			return err
 		case err := <-closedConns:
-			event.Error(ctx, "closed a connection", err)
+			if !isClosingError(err) {
+				event.Error(ctx, "closed a connection", err)
+			}
 			activeConns--
 			if activeConns == 0 {
 				connTimer.Reset(idleTimeout)
@@ -114,3 +121,19 @@
 		}
 	}
 }
+
+// isClosingError reports if the error occurs normally during the process of
+// closing a network connection. It uses imperfect heuristics that err on the
+// side of false negatives, and should not be used for anything critical.
+func isClosingError(err error) bool {
+	if errors.Is(err, io.EOF) {
+		return true
+	}
+	// Per https://github.com/golang/go/issues/4373, this error string should not
+	// change. This is not ideal, but since the worst that could happen here is
+	// some superfluous logging, it is acceptable.
+	if err.Error() == "use of closed network connection" {
+		return true
+	}
+	return false
+}
diff --git a/internal/jsonrpc2/serve_test.go b/internal/jsonrpc2/serve_test.go
index 1b95610..c9c1fbd 100644
--- a/internal/jsonrpc2/serve_test.go
+++ b/internal/jsonrpc2/serve_test.go
@@ -10,9 +10,12 @@
 	"sync"
 	"testing"
 	"time"
+
+	"golang.org/x/tools/internal/stack/stacktest"
 )
 
 func TestIdleTimeout(t *testing.T) {
+	stacktest.NoLeak(t)
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	defer cancel()
 
diff --git a/internal/jsonrpc2/servertest/servertest.go b/internal/jsonrpc2/servertest/servertest.go
index ee27c8c..392e084 100644
--- a/internal/jsonrpc2/servertest/servertest.go
+++ b/internal/jsonrpc2/servertest/servertest.go
@@ -9,8 +9,8 @@
 import (
 	"context"
 	"fmt"
-	"io"
 	"net"
+	"strings"
 	"sync"
 
 	"golang.org/x/tools/internal/jsonrpc2"
@@ -18,105 +18,102 @@
 
 // Connector is the interface used to connect to a server.
 type Connector interface {
-	Connect(context.Context) *jsonrpc2.Conn
+	Connect(context.Context) jsonrpc2.Conn
 }
 
 // TCPServer is a helper for executing tests against a remote jsonrpc2
 // connection. Once initialized, its Addr field may be used to connect a
 // jsonrpc2 client.
 type TCPServer struct {
+	*connList
+
 	Addr string
 
-	ln  net.Listener
-	cls *closerList
+	ln     net.Listener
+	framer jsonrpc2.Framer
 }
 
 // NewTCPServer returns a new test server listening on local tcp port and
 // serving incoming jsonrpc2 streams using the provided stream server. It
 // panics on any error.
-func NewTCPServer(ctx context.Context, server jsonrpc2.StreamServer) *TCPServer {
+func NewTCPServer(ctx context.Context, server jsonrpc2.StreamServer, framer jsonrpc2.Framer) *TCPServer {
 	ln, err := net.Listen("tcp", "127.0.0.1:0")
 	if err != nil {
 		panic(fmt.Sprintf("servertest: failed to listen: %v", err))
 	}
+	if framer == nil {
+		framer = jsonrpc2.NewHeaderStream
+	}
 	go jsonrpc2.Serve(ctx, ln, server, 0)
-	return &TCPServer{Addr: ln.Addr().String(), ln: ln, cls: &closerList{}}
+	return &TCPServer{Addr: ln.Addr().String(), ln: ln, framer: framer, connList: &connList{}}
 }
 
 // Connect dials the test server and returns a jsonrpc2 Connection that is
 // ready for use.
-func (s *TCPServer) Connect(ctx context.Context) *jsonrpc2.Conn {
+func (s *TCPServer) Connect(ctx context.Context) jsonrpc2.Conn {
 	netConn, err := net.Dial("tcp", s.Addr)
 	if err != nil {
 		panic(fmt.Sprintf("servertest: failed to connect to test instance: %v", err))
 	}
-	s.cls.add(func() {
-		netConn.Close()
-	})
-	return jsonrpc2.NewConn(jsonrpc2.NewHeaderStream(netConn, netConn))
-}
-
-// Close closes all connected pipes.
-func (s *TCPServer) Close() error {
-	s.cls.closeAll()
-	return nil
+	conn := jsonrpc2.NewConn(s.framer(netConn))
+	s.add(conn)
+	return conn
 }
 
 // PipeServer is a test server that handles connections over io.Pipes.
 type PipeServer struct {
+	*connList
 	server jsonrpc2.StreamServer
-	cls    *closerList
+	framer jsonrpc2.Framer
 }
 
 // NewPipeServer returns a test server that can be connected to via io.Pipes.
-func NewPipeServer(ctx context.Context, server jsonrpc2.StreamServer) *PipeServer {
-	return &PipeServer{server: server, cls: &closerList{}}
+func NewPipeServer(ctx context.Context, server jsonrpc2.StreamServer, framer jsonrpc2.Framer) *PipeServer {
+	if framer == nil {
+		framer = jsonrpc2.NewRawStream
+	}
+	return &PipeServer{server: server, framer: framer, connList: &connList{}}
 }
 
 // Connect creates new io.Pipes and binds them to the underlying StreamServer.
-func (s *PipeServer) Connect(ctx context.Context) *jsonrpc2.Conn {
-	// Pipes connect like this:
-	// Client🡒(sWriter)🡒(sReader)🡒Server
-	//       🡔(cReader)🡐(cWriter)🡗
-	sReader, sWriter := io.Pipe()
-	cReader, cWriter := io.Pipe()
-	s.cls.add(func() {
-		sReader.Close()
-		sWriter.Close()
-		cReader.Close()
-		cWriter.Close()
-	})
-	serverStream := jsonrpc2.NewRawStream(sReader, cWriter)
-	go s.server.ServeStream(ctx, serverStream)
+func (s *PipeServer) Connect(ctx context.Context) jsonrpc2.Conn {
+	sPipe, cPipe := net.Pipe()
+	serverStream := s.framer(sPipe)
+	serverConn := jsonrpc2.NewConn(serverStream)
+	s.add(serverConn)
+	go s.server.ServeStream(ctx, serverConn)
 
-	clientStream := jsonrpc2.NewRawStream(cReader, sWriter)
-	return jsonrpc2.NewConn(clientStream)
+	clientStream := s.framer(cPipe)
+	clientConn := jsonrpc2.NewConn(clientStream)
+	s.add(clientConn)
+	return clientConn
 }
 
-// Close closes all connected pipes.
-func (s *PipeServer) Close() error {
-	s.cls.closeAll()
-	return nil
-}
-
-// closerList tracks closers to run when a testserver is closed.  This is a
+// connList tracks closers to run when a testserver is closed.  This is a
 // convenience, so that callers don't have to worry about closing each
 // connection.
-type closerList struct {
-	mu      sync.Mutex
-	closers []func()
+type connList struct {
+	mu    sync.Mutex
+	conns []jsonrpc2.Conn
 }
 
-func (l *closerList) add(closer func()) {
+func (l *connList) add(conn jsonrpc2.Conn) {
 	l.mu.Lock()
 	defer l.mu.Unlock()
-	l.closers = append(l.closers, closer)
+	l.conns = append(l.conns, conn)
 }
 
-func (l *closerList) closeAll() {
+func (l *connList) Close() error {
 	l.mu.Lock()
 	defer l.mu.Unlock()
-	for _, closer := range l.closers {
-		closer()
+	var errmsgs []string
+	for _, conn := range l.conns {
+		if err := conn.Close(); err != nil {
+			errmsgs = append(errmsgs, err.Error())
+		}
 	}
+	if len(errmsgs) > 0 {
+		return fmt.Errorf("closing errors:\n%s", strings.Join(errmsgs, "\n"))
+	}
+	return nil
 }
diff --git a/internal/jsonrpc2/servertest/servertest_test.go b/internal/jsonrpc2/servertest/servertest_test.go
index 75a0613..38fa21a 100644
--- a/internal/jsonrpc2/servertest/servertest_test.go
+++ b/internal/jsonrpc2/servertest/servertest_test.go
@@ -24,9 +24,9 @@
 	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
 	defer cancel()
 	server := jsonrpc2.HandlerServer(fakeHandler)
-	tcpTS := NewTCPServer(ctx, server)
+	tcpTS := NewTCPServer(ctx, server, nil)
 	defer tcpTS.Close()
-	pipeTS := NewPipeServer(ctx, server)
+	pipeTS := NewPipeServer(ctx, server, nil)
 	defer pipeTS.Close()
 
 	tests := []struct {
@@ -40,7 +40,7 @@
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
 			conn := test.connector.Connect(ctx)
-			go conn.Run(ctx, jsonrpc2.MethodNotFound)
+			conn.Go(ctx, jsonrpc2.MethodNotFound)
 			var got msg
 			if _, err := conn.Call(ctx, "ping", &msg{"ping"}, &got); err != nil {
 				t.Fatal(err)
diff --git a/internal/jsonrpc2/stream.go b/internal/jsonrpc2/stream.go
index c12eb00..b22be7e 100644
--- a/internal/jsonrpc2/stream.go
+++ b/internal/jsonrpc2/stream.go
@@ -10,38 +10,45 @@
 	"encoding/json"
 	"fmt"
 	"io"
+	"net"
 	"strconv"
 	"strings"
-	"sync"
 )
 
 // Stream abstracts the transport mechanics from the JSON RPC protocol.
 // A Conn reads and writes messages using the stream it was provided on
 // construction, and assumes that each call to Read or Write fully transfers
 // a single message, or returns an error.
+// A stream is not safe for concurrent use, it is expected it will be used by
+// a single Conn in a safe manner.
 type Stream interface {
 	// Read gets the next message from the stream.
-	// It is never called concurrently.
 	Read(context.Context) (Message, int64, error)
 	// Write sends a message to the stream.
-	// It must be safe for concurrent use.
 	Write(context.Context, Message) (int64, error)
+	// Close closes the connection.
+	// Any blocked Read or Write operations will be unblocked and return errors.
+	Close() error
 }
 
-// NewRawStream returns a Stream built on top of an io.Reader and io.Writer.
+// Framer wraps a network connection up into a Stream.
+// It is responsible for the framing and encoding of messages into wire form.
+// NewRawStream and NewHeaderStream are implementations of a Framer.
+type Framer func(conn net.Conn) Stream
+
+// NewRawStream returns a Stream built on top of a net.Conn.
 // The messages are sent with no wrapping, and rely on json decode consistency
 // to determine message boundaries.
-func NewRawStream(in io.Reader, out io.Writer) Stream {
+func NewRawStream(conn net.Conn) Stream {
 	return &rawStream{
-		in:  json.NewDecoder(in),
-		out: out,
+		conn: conn,
+		in:   json.NewDecoder(conn),
 	}
 }
 
 type rawStream struct {
-	in    *json.Decoder
-	outMu sync.Mutex
-	out   io.Writer
+	conn net.Conn
+	in   *json.Decoder
 }
 
 func (s *rawStream) Read(ctx context.Context) (Message, int64, error) {
@@ -68,26 +75,27 @@
 	if err != nil {
 		return 0, fmt.Errorf("marshaling message: %v", err)
 	}
-	s.outMu.Lock()
-	n, err := s.out.Write(data)
-	s.outMu.Unlock()
+	n, err := s.conn.Write(data)
 	return int64(n), err
 }
 
-// NewHeaderStream returns a Stream built on top of an io.Reader and io.Writer.
+func (s *rawStream) Close() error {
+	return s.conn.Close()
+}
+
+// NewHeaderStream returns a Stream built on top of a net.Conn.
 // The messages are sent with HTTP content length and MIME type headers.
 // This is the format used by LSP and others.
-func NewHeaderStream(in io.Reader, out io.Writer) Stream {
+func NewHeaderStream(conn net.Conn) Stream {
 	return &headerStream{
-		in:  bufio.NewReader(in),
-		out: out,
+		conn: conn,
+		in:   bufio.NewReader(conn),
 	}
 }
 
 type headerStream struct {
-	in    *bufio.Reader
-	outMu sync.Mutex
-	out   io.Writer
+	conn net.Conn
+	in   *bufio.Reader
 }
 
 func (s *headerStream) Read(ctx context.Context) (Message, int64, error) {
@@ -148,13 +156,15 @@
 	if err != nil {
 		return 0, fmt.Errorf("marshaling message: %v", err)
 	}
-	s.outMu.Lock()
-	defer s.outMu.Unlock()
-	n, err := fmt.Fprintf(s.out, "Content-Length: %v\r\n\r\n", len(data))
+	n, err := fmt.Fprintf(s.conn, "Content-Length: %v\r\n\r\n", len(data))
 	total := int64(n)
 	if err == nil {
-		n, err = s.out.Write(data)
+		n, err = s.conn.Write(data)
 		total += int64(n)
 	}
 	return total, err
 }
+
+func (s *headerStream) Close() error {
+	return s.conn.Close()
+}
diff --git a/internal/jsonrpc2/wire_test.go b/internal/jsonrpc2/wire_test.go
index 7cddb65..4d8832c 100644
--- a/internal/jsonrpc2/wire_test.go
+++ b/internal/jsonrpc2/wire_test.go
@@ -81,6 +81,25 @@
 	}
 }
 
+func TestErrorResponse(t *testing.T) {
+	// originally reported in #39719, this checks that result is not present if
+	// it is an error response
+	r, _ := jsonrpc2.NewResponse(jsonrpc2.NewIntID(3), nil, fmt.Errorf("computing fix edits"))
+	data, err := json.Marshal(r)
+	if err != nil {
+		t.Fatal(err)
+	}
+	checkJSON(t, data, []byte(`{
+		"jsonrpc":"2.0",
+		"error":{
+			"code":0,
+			"message":"computing fix edits",
+			"data":null
+		},
+		"id":3
+	}`))
+}
+
 func checkJSON(t *testing.T, got, want []byte) {
 	// compare the compact form, to allow for formatting differences
 	g := &bytes.Buffer{}
diff --git a/internal/lsp/analysis/fillreturns/fillreturns.go b/internal/lsp/analysis/fillreturns/fillreturns.go
index 7ed9653..4fd8877 100644
--- a/internal/lsp/analysis/fillreturns/fillreturns.go
+++ b/internal/lsp/analysis/fillreturns/fillreturns.go
@@ -9,6 +9,7 @@
 
 import (
 	"bytes"
+	"fmt"
 	"go/ast"
 	"go/format"
 	"go/types"
@@ -47,9 +48,15 @@
 var wrongReturnNumRegex = regexp.MustCompile(`wrong number of return values \(want (\d+), got (\d+)\)`)
 
 func run(pass *analysis.Pass) (interface{}, error) {
+	info := pass.TypesInfo
+	if info == nil {
+		return nil, fmt.Errorf("nil TypeInfo")
+	}
+
 	errors := analysisinternal.GetTypeErrors(pass)
-	// Filter out the errors that are not relevant to this analyzer.
+outer:
 	for _, typeErr := range errors {
+		// Filter out the errors that are not relevant to this analyzer.
 		if !FixesError(typeErr.Msg) {
 			continue
 		}
@@ -84,76 +91,75 @@
 
 		// Get the function that encloses the ReturnStmt.
 		var enclosingFunc *ast.FuncType
-	Outer:
 		for _, n := range path {
 			switch node := n.(type) {
 			case *ast.FuncLit:
 				enclosingFunc = node.Type
-				break Outer
 			case *ast.FuncDecl:
 				enclosingFunc = node.Type
-				break Outer
+			}
+			if enclosingFunc != nil {
+				break
 			}
 		}
 		if enclosingFunc == nil {
 			continue
 		}
-		numRetValues := len(ret.Results)
-		typeInfo := pass.TypesInfo
 
-		// skip if return value has a func call (whose multiple returns might be expanded)
+		// Skip any return statements that contain function calls with multiple return values.
 		for _, expr := range ret.Results {
 			e, ok := expr.(*ast.CallExpr)
 			if !ok {
 				continue
 			}
-			ident, ok := e.Fun.(*ast.Ident)
-			if !ok || ident.Obj == nil {
-				continue
-			}
-			fn, ok := ident.Obj.Decl.(*ast.FuncDecl)
-			if !ok {
-				continue
-			}
-			if len(fn.Type.Results.List) != 1 {
-				continue
-			}
-			if typeInfo == nil {
-				continue
-			}
-			if _, ok := typeInfo.TypeOf(e).(*types.Tuple); ok {
-				continue
+			if tup, ok := info.TypeOf(e).(*types.Tuple); ok && tup.Len() > 1 {
+				continue outer
 			}
 		}
 
-		// Fill in the missing arguments with zero-values.
-		returnCount := 0
-		zvs := make([]ast.Expr, len(enclosingFunc.Results.List))
+		// Duplicate the return values to track which values have been matched.
+		remaining := make([]ast.Expr, len(ret.Results))
+		copy(remaining, ret.Results)
+
+		fixed := make([]ast.Expr, len(enclosingFunc.Results.List))
+
+		// For each value in the return function declaration, find the leftmost element
+		// in the return statement that has the desired type. If no such element exits,
+		// fill in the missing value with the appropriate "zero" value.
 		for i, result := range enclosingFunc.Results.List {
-			zv := analysisinternal.ZeroValue(pass.Fset, file, pass.Pkg, typeInfo.TypeOf(result.Type))
-			if zv == nil {
-				return nil, nil
+			typ := info.TypeOf(result.Type)
+
+			var match ast.Expr
+			var idx int
+			for j, val := range remaining {
+				if !matchingTypes(info.TypeOf(val), typ) {
+					continue
+				}
+				match, idx = val, j
+				break
 			}
-			// We do not have any existing return values, fill in with zero-values.
-			if returnCount >= numRetValues {
-				zvs[i] = zv
-				continue
+
+			if match != nil {
+				fixed[i] = match
+				remaining = append(remaining[:idx], remaining[idx+1:]...)
+			} else {
+				zv := analysisinternal.ZeroValue(pass.Fset, file, pass.Pkg, info.TypeOf(result.Type))
+				if zv == nil {
+					return nil, nil
+				}
+				fixed[i] = zv
 			}
-			// Compare the types to see if they are the same.
-			current := ret.Results[returnCount]
-			if equalTypes(typeInfo.TypeOf(current), typeInfo.TypeOf(result.Type)) {
-				zvs[i] = current
-				returnCount += 1
-				continue
-			}
-			zvs[i] = zv
 		}
+
+		// Append leftover return values to end of new return statement.
+		fixed = append(fixed, remaining...)
+
 		newRet := &ast.ReturnStmt{
 			Return:  ret.Pos(),
-			Results: zvs,
+			Results: fixed,
 		}
 
-		// Convert the new return statement ast to text.
+		// Convert the new return statement AST to text.
 		var newBuf bytes.Buffer
 		if err := format.Node(&newBuf, pass.Fset, newRet); err != nil {
 			return nil, err
@@ -176,18 +182,17 @@
 	return nil, nil
 }
 
-func equalTypes(t1, t2 types.Type) bool {
-	if t1 == t2 || types.Identical(t1, t2) {
+func matchingTypes(want, got types.Type) bool {
+	if want == got || types.Identical(want, got) {
 		return true
 	}
 	// Code segment to help check for untyped equality from (golang/go#32146).
-	if rhs, ok := t1.(*types.Basic); ok && rhs.Info()&types.IsUntyped > 0 {
-		if lhs, ok := t2.Underlying().(*types.Basic); ok {
+	if rhs, ok := want.(*types.Basic); ok && rhs.Info()&types.IsUntyped > 0 {
+		if lhs, ok := got.Underlying().(*types.Basic); ok {
 			return rhs.Info()&types.IsConstType == lhs.Info()&types.IsConstType
 		}
 	}
-	// TODO: Figure out if we want to check for types.AssignableTo(t1, t2) || types.ConvertibleTo(t1, t2)
-	return false
+	return types.AssignableTo(want, got) || types.ConvertibleTo(want, got)
 }
 
 func FixesError(msg string) bool {
diff --git a/internal/lsp/analysis/fillreturns/testdata/src/a/a.go b/internal/lsp/analysis/fillreturns/testdata/src/a/a.go
index e65d42a..f80bbf6 100644
--- a/internal/lsp/analysis/fillreturns/testdata/src/a/a.go
+++ b/internal/lsp/analysis/fillreturns/testdata/src/a/a.go
@@ -11,6 +11,7 @@
 	"net/http"
 	. "net/http"
 	"net/url"
+	"strconv"
 )
 
 type T struct{}
@@ -39,9 +40,21 @@
 	return (z(http.ListenAndServe))("", nil) // want "wrong number of return values \\(want 3, got 1\\)"
 }
 
+func preserveLeft() (int, int, error) {
+	return 1, errors.New("foo") // want "wrong number of return values \\(want 3, got 2\\)"
+}
+
+func matchValues() (int, error, string) {
+	return errors.New("foo"), 3 // want "wrong number of return values \\(want 3, got 2\\)"
+}
+
+func preventDataOverwrite() (int, string) {
+	return errors.New("foo") // want "wrong number of return values \\(want 2, got 1\\)"
+}
+
 func closure() (string, error) {
 	_ = func() (int, error) {
-		return errors.New("foo") // want "wrong number of return values \\(want 2, got 1\\)"
+		return // want "wrong number of return values \\(want 2, got 0\\)"
 	}
 	return // want "wrong number of return values \\(want 2, got 0\\)"
 }
@@ -62,9 +75,39 @@
 	if 1 == 2 {
 		return // want "wrong number of return values \\(want 2, got 0\\)"
 	} else if 1 == 3 {
-		return // want "wrong number of return values \\(want 2, got 0\\)"
+		return errors.New("foo") // want "wrong number of return values \\(want 2, got 1\\)"
 	} else {
-		return // want "wrong number of return values \\(want 2, got 0\\)"
+		return 1 // want "wrong number of return values \\(want 2, got 1\\)"
 	}
 	return // want "wrong number of return values \\(want 2, got 0\\)"
 }
+
+func convertibleTypes() (ast2.Expr, int) {
+	return &ast2.ArrayType{} // want "wrong number of return values \\(want 2, got 1\\)"
+}
+
+func assignableTypes() (map[string]int, int) {
+	type X map[string]int
+	var x X
+	return x // want "wrong number of return values \\(want 2, got 1\\)"
+}
+
+func interfaceAndError() (I, int) {
+	return errors.New("foo") // want "wrong number of return values \\(want 2, got 1\\)"
+}
+
+func funcOneReturn() (string, error) {
+	return strconv.Itoa(1) // want "wrong number of return values \\(want 2, got 1\\)"
+}
+
+func funcMultipleReturn() (int, error, string) {
+	return strconv.Atoi("1")
+}
+
+func localFuncMultipleReturn() (string, int, error, string) {
+	return b()
+}
+
+func multipleUnused() (int, string, string, string) {
+	return 3, 4, 5 // want "wrong number of return values \\(want 4, got 3\\)"
+}
diff --git a/internal/lsp/analysis/fillreturns/testdata/src/a/a.go.golden b/internal/lsp/analysis/fillreturns/testdata/src/a/a.go.golden
index e5a4abb..15a82b1 100644
--- a/internal/lsp/analysis/fillreturns/testdata/src/a/a.go.golden
+++ b/internal/lsp/analysis/fillreturns/testdata/src/a/a.go.golden
@@ -11,6 +11,7 @@
 	"net/http"
 	. "net/http"
 	"net/url"
+	"strconv"
 )
 
 type T struct{}
@@ -39,9 +40,21 @@
 	return T{}, (z(http.ListenAndServe))("", nil), nil // want "wrong number of return values \\(want 3, got 1\\)"
 }
 
+func preserveLeft() (int, int, error) {
+	return 1, 0, errors.New("foo") // want "wrong number of return values \\(want 3, got 2\\)"
+}
+
+func matchValues() (int, error, string) {
+	return 3, errors.New("foo"), "" // want "wrong number of return values \\(want 3, got 2\\)"
+}
+
+func preventDataOverwrite() (int, string) {
+	return 0, "", errors.New("foo") // want "wrong number of return values \\(want 2, got 1\\)"
+}
+
 func closure() (string, error) {
 	_ = func() (int, error) {
-		return 0, errors.New("foo") // want "wrong number of return values \\(want 2, got 1\\)"
+		return 0, nil // want "wrong number of return values \\(want 2, got 0\\)"
 	}
 	return "", nil // want "wrong number of return values \\(want 2, got 0\\)"
 }
@@ -62,9 +75,39 @@
 	if 1 == 2 {
 		return 0, nil // want "wrong number of return values \\(want 2, got 0\\)"
 	} else if 1 == 3 {
-		return 0, nil // want "wrong number of return values \\(want 2, got 0\\)"
+		return 0, errors.New("foo") // want "wrong number of return values \\(want 2, got 1\\)"
 	} else {
-		return 0, nil // want "wrong number of return values \\(want 2, got 0\\)"
+		return 1, nil // want "wrong number of return values \\(want 2, got 1\\)"
 	}
 	return 0, nil // want "wrong number of return values \\(want 2, got 0\\)"
 }
+
+func convertibleTypes() (ast2.Expr, int) {
+	return &ast2.ArrayType{}, 0 // want "wrong number of return values \\(want 2, got 1\\)"
+}
+
+func assignableTypes() (map[string]int, int) {
+	type X map[string]int
+	var x X
+	return x, 0 // want "wrong number of return values \\(want 2, got 1\\)"
+}
+
+func interfaceAndError() (I, int) {
+	return errors.New("foo"), 0 // want "wrong number of return values \\(want 2, got 1\\)"
+}
+
+func funcOneReturn() (string, error) {
+	return strconv.Itoa(1), nil // want "wrong number of return values \\(want 2, got 1\\)"
+}
+
+func funcMultipleReturn() (int, error, string) {
+	return strconv.Atoi("1")
+}
+
+func localFuncMultipleReturn() (string, int, error, string) {
+	return b()
+}
+
+func multipleUnused() (int, string, string, string) {
+	return 3, "", "", "", 4, 5 // want "wrong number of return values \\(want 4, got 3\\)"
+}
diff --git a/internal/lsp/analysis/fillstruct/fillstruct.go b/internal/lsp/analysis/fillstruct/fillstruct.go
new file mode 100644
index 0000000..bbe7343
--- /dev/null
+++ b/internal/lsp/analysis/fillstruct/fillstruct.go
@@ -0,0 +1,349 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package fillstruct defines an Analyzer that automatically
+// fills in a struct declaration with zero value elements for each field.
+package fillstruct
+
+import (
+	"bytes"
+	"fmt"
+	"go/ast"
+	"go/format"
+	"go/printer"
+	"go/token"
+	"go/types"
+	"log"
+	"strings"
+	"unicode"
+
+	"golang.org/x/tools/go/analysis"
+	"golang.org/x/tools/go/analysis/passes/inspect"
+	"golang.org/x/tools/go/ast/inspector"
+	"golang.org/x/tools/internal/analysisinternal"
+)
+
+const Doc = `suggested input for incomplete struct initializations
+
+This analyzer provides the appropriate zero values for all
+uninitialized fields of an empty struct. For example, given the following struct:
+	type Foo struct {
+		ID   int64
+		Name string
+	}
+the initialization
+	var _ = Foo{}
+will turn into
+	var _ = Foo{
+		ID: 0,
+		Name: "",
+	}
+`
+
+var Analyzer = &analysis.Analyzer{
+	Name:             "fillstruct",
+	Doc:              Doc,
+	Requires:         []*analysis.Analyzer{inspect.Analyzer},
+	Run:              run,
+	RunDespiteErrors: true,
+}
+
+func run(pass *analysis.Pass) (interface{}, error) {
+	inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
+	nodeFilter := []ast.Node{(*ast.CompositeLit)(nil)}
+	inspect.Preorder(nodeFilter, func(n ast.Node) {
+		info := pass.TypesInfo
+		if info == nil {
+			return
+		}
+		expr := n.(*ast.CompositeLit)
+
+		// TODO: Handle partially-filled structs as well.
+		if len(expr.Elts) != 0 {
+			return
+		}
+
+		var file *ast.File
+		for _, f := range pass.Files {
+			if f.Pos() <= expr.Pos() && expr.Pos() <= f.End() {
+				file = f
+				break
+			}
+		}
+		if file == nil {
+			return
+		}
+
+		typ := info.TypeOf(expr)
+		if typ == nil {
+			return
+		}
+
+		// Find reference to the type declaration of the struct being initialized.
+		for {
+			p, ok := typ.Underlying().(*types.Pointer)
+			if !ok {
+				break
+			}
+			typ = p.Elem()
+		}
+		typ = typ.Underlying()
+
+		obj, ok := typ.(*types.Struct)
+		if !ok {
+			return
+		}
+		fieldCount := obj.NumFields()
+		// Skip any struct that is already populated or that has no fields.
+		if fieldCount == 0 || fieldCount == len(expr.Elts) {
+			return
+		}
+
+		var name string
+		switch typ := expr.Type.(type) {
+		case *ast.Ident:
+			name = typ.Name
+		case *ast.SelectorExpr:
+			name = fmt.Sprintf("%s.%s", typ.X, typ.Sel.Name)
+		default:
+			log.Printf("anonymous structs are not yet supported: %v (%T)", expr.Type, expr.Type)
+			return
+		}
+
+		// Use a new fileset to build up a token.File for the new composite
+		// literal. We need one line for foo{, one line for }, and one line for
+		// each field we're going to set. format.Node only cares about line
+		// numbers, so we don't need to set columns, and each line can be
+		// 1 byte long.
+		fset := token.NewFileSet()
+		tok := fset.AddFile("", -1, fieldCount+2)
+
+		var elts []ast.Expr
+		for i := 0; i < fieldCount; i++ {
+			field := obj.Field(i)
+
+			// Ignore fields that are not accessible in the current package.
+			if field.Pkg() != nil && field.Pkg() != pass.Pkg && !field.Exported() {
+				continue
+			}
+
+			value := populateValue(pass.Fset, file, pass.Pkg, field.Type())
+			if value == nil {
+				continue
+			}
+
+			line := i + 2      // account for 1-based lines and the left brace
+			tok.AddLine(i + 1) // add 1 byte per line
+			pos := tok.LineStart(line)
+
+			kv := &ast.KeyValueExpr{
+				Key: &ast.Ident{
+					NamePos: pos,
+					Name:    field.Name(),
+				},
+				Colon: pos,
+				Value: value,
+			}
+			elts = append(elts, kv)
+		}
+
+		// If all of the struct's fields are unexported, we have nothing to do.
+		if len(elts) == 0 {
+			return
+		}
+
+		// Add the final line for the right brace. Offset is the number of
+		// bytes already added plus 1.
+		tok.AddLine(len(elts) + 1)
+
+		cl := &ast.CompositeLit{
+			Type:   expr.Type,
+			Lbrace: tok.LineStart(1),
+			Elts:   elts,
+			Rbrace: tok.LineStart(len(elts) + 2),
+		}
+
+		// Print the AST to get the the original source code.
+		var b bytes.Buffer
+		if err := printer.Fprint(&b, pass.Fset, file); err != nil {
+			log.Printf("failed to print original file: %s", err)
+			return
+		}
+
+		// Find the line on which the composite literal is declared.
+		split := strings.Split(b.String(), "\n")
+		lineNumber := pass.Fset.Position(expr.Type.Pos()).Line
+		line := split[lineNumber-1] // lines are 1-indexed
+
+		// Trim the whitespace from the left of the line, and use the index
+		// to get the amount of whitespace on the left.
+		trimmed := strings.TrimLeftFunc(line, unicode.IsSpace)
+		i := strings.Index(line, trimmed)
+		whitespace := line[:i]
+
+		var newExpr bytes.Buffer
+		if err := format.Node(&newExpr, fset, cl); err != nil {
+			log.Printf("failed to format %s: %v", cl.Type, err)
+			return
+		}
+		split = strings.Split(newExpr.String(), "\n")
+		var newText strings.Builder
+		for i, s := range split {
+			// Don't add the extra indentation to the first line.
+			if i != 0 {
+				newText.WriteString(whitespace)
+			}
+			newText.WriteString(s)
+			if i < len(split)-1 {
+				newText.WriteByte('\n')
+			}
+		}
+		pass.Report(analysis.Diagnostic{
+			Pos: expr.Lbrace,
+			End: expr.Rbrace,
+			SuggestedFixes: []analysis.SuggestedFix{{
+				Message: fmt.Sprintf("Fill %s with default values", name),
+				TextEdits: []analysis.TextEdit{{
+					Pos:     expr.Pos(),
+					End:     expr.End(),
+					NewText: []byte(newText.String()),
+				}},
+			}},
+		})
+	})
+	return nil, nil
+}
+
+// populateValue constructs an expression to fill the value of a struct field.
+//
+// When the type of a struct field is a basic literal or interface, we return
+// default values. For other types, such as maps, slices, and channels, we create
+// expressions rather than using default values.
+//
+// The reasoning here is that users will call fillstruct with the intention of
+// initializing the struct, in which case setting these fields to nil has no effect.
+func populateValue(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
+	under := typ
+	if n, ok := typ.(*types.Named); ok {
+		under = n.Underlying()
+	}
+	switch u := under.(type) {
+	case *types.Basic:
+		switch {
+		case u.Info()&types.IsNumeric != 0:
+			return &ast.BasicLit{Kind: token.INT, Value: "0"}
+		case u.Info()&types.IsBoolean != 0:
+			return &ast.Ident{Name: "false"}
+		case u.Info()&types.IsString != 0:
+			return &ast.BasicLit{Kind: token.STRING, Value: `""`}
+		default:
+			panic("unknown basic type")
+		}
+	case *types.Map:
+		k := analysisinternal.TypeExpr(fset, f, pkg, u.Key())
+		v := analysisinternal.TypeExpr(fset, f, pkg, u.Elem())
+		if k == nil || v == nil {
+			return nil
+		}
+		return &ast.CompositeLit{
+			Type: &ast.MapType{
+				Key:   k,
+				Value: v,
+			},
+		}
+	case *types.Slice:
+		s := analysisinternal.TypeExpr(fset, f, pkg, u.Elem())
+		if s == nil {
+			return nil
+		}
+		return &ast.CompositeLit{
+			Type: &ast.ArrayType{
+				Elt: s,
+			},
+		}
+	case *types.Array:
+		a := analysisinternal.TypeExpr(fset, f, pkg, u.Elem())
+		if a == nil {
+			return nil
+		}
+		return &ast.CompositeLit{
+			Type: &ast.ArrayType{
+				Elt: a,
+				Len: &ast.BasicLit{
+					Kind: token.INT, Value: fmt.Sprintf("%v", u.Len())},
+			},
+		}
+	case *types.Chan:
+		v := analysisinternal.TypeExpr(fset, f, pkg, u.Elem())
+		if v == nil {
+			return nil
+		}
+		dir := ast.ChanDir(u.Dir())
+		if u.Dir() == types.SendRecv {
+			dir = ast.SEND | ast.RECV
+		}
+		return &ast.CallExpr{
+			Fun: ast.NewIdent("make"),
+			Args: []ast.Expr{
+				&ast.ChanType{
+					Dir:   dir,
+					Value: v,
+				},
+			},
+		}
+	case *types.Struct:
+		s := analysisinternal.TypeExpr(fset, f, pkg, typ)
+		if s == nil {
+			return nil
+		}
+		return &ast.CompositeLit{
+			Type: s,
+		}
+	case *types.Signature:
+		var params []*ast.Field
+		for i := 0; i < u.Params().Len(); i++ {
+			p := analysisinternal.TypeExpr(fset, f, pkg, u.Params().At(i).Type())
+			if p == nil {
+				return nil
+			}
+			params = append(params, &ast.Field{
+				Type: p,
+				Names: []*ast.Ident{
+					{
+						Name: u.Params().At(i).Name(),
+					},
+				},
+			})
+		}
+		var returns []*ast.Field
+		for i := 0; i < u.Results().Len(); i++ {
+			r := analysisinternal.TypeExpr(fset, f, pkg, u.Results().At(i).Type())
+			if r == nil {
+				return nil
+			}
+			returns = append(returns, &ast.Field{
+				Type: r,
+			})
+		}
+		return &ast.FuncLit{
+			Type: &ast.FuncType{
+				Params: &ast.FieldList{
+					List: params,
+				},
+				Results: &ast.FieldList{
+					List: returns,
+				},
+			},
+			Body: &ast.BlockStmt{},
+		}
+	case *types.Pointer:
+		return &ast.UnaryExpr{
+			Op: token.AND,
+			X:  populateValue(fset, f, pkg, u.Elem()),
+		}
+	case *types.Interface:
+		return ast.NewIdent("nil")
+	}
+	return nil
+}
diff --git a/internal/lsp/analysis/fillstruct/fillstruct_test.go b/internal/lsp/analysis/fillstruct/fillstruct_test.go
new file mode 100644
index 0000000..cdd1bde
--- /dev/null
+++ b/internal/lsp/analysis/fillstruct/fillstruct_test.go
@@ -0,0 +1,17 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package fillstruct_test
+
+import (
+	"testing"
+
+	"golang.org/x/tools/go/analysis/analysistest"
+	"golang.org/x/tools/internal/lsp/analysis/fillstruct"
+)
+
+func Test(t *testing.T) {
+	testdata := analysistest.TestData()
+	analysistest.RunWithSuggestedFixes(t, testdata, fillstruct.Analyzer, "a")
+}
diff --git a/internal/lsp/analysis/fillstruct/testdata/src/a/a.go b/internal/lsp/analysis/fillstruct/testdata/src/a/a.go
new file mode 100644
index 0000000..8358383
--- /dev/null
+++ b/internal/lsp/analysis/fillstruct/testdata/src/a/a.go
@@ -0,0 +1,91 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package fillstruct
+
+import (
+	data "b"
+	"go/ast"
+	"go/token"
+)
+
+type emptyStruct struct{}
+
+var _ = emptyStruct{}
+
+type basicStruct struct {
+	foo int
+}
+
+var _ = basicStruct{} // want ""
+
+type twoArgStruct struct {
+	foo int
+	bar string
+}
+
+var _ = twoArgStruct{} // want ""
+
+var _ = twoArgStruct{
+	bar: "bar",
+}
+
+type nestedStruct struct {
+	bar   string
+	basic basicStruct
+}
+
+var _ = nestedStruct{} // want ""
+
+var _ = data.B{} // want ""
+
+type typedStruct struct {
+	m  map[string]int
+	s  []int
+	c  chan int
+	c1 <-chan int
+	a  [2]string
+}
+
+var _ = typedStruct{} // want ""
+
+type funStruct struct {
+	fn func(i int) int
+}
+
+var _ = funStruct{} // want ""
+
+type funStructCompex struct {
+	fn func(i int, s string) (string, int)
+}
+
+var _ = funStructCompex{} // want ""
+
+type funStructEmpty struct {
+	fn func()
+}
+
+var _ = funStructEmpty{} // want ""
+
+type Foo struct {
+	A int
+}
+
+type Bar struct {
+	X *Foo
+	Y *Foo
+}
+
+var _ = Bar{} // want ""
+
+type importedStruct struct {
+	m  map[*ast.CompositeLit]ast.Field
+	s  []ast.BadExpr
+	a  [3]token.Token
+	c  chan ast.EmptyStmt
+	fn func(ast_decl ast.DeclStmt) ast.Ellipsis
+	st ast.CompositeLit
+}
+
+var _ = importedStruct{} // want ""
diff --git a/internal/lsp/analysis/fillstruct/testdata/src/a/a.go.golden b/internal/lsp/analysis/fillstruct/testdata/src/a/a.go.golden
new file mode 100644
index 0000000..2f52bf5
--- /dev/null
+++ b/internal/lsp/analysis/fillstruct/testdata/src/a/a.go.golden
@@ -0,0 +1,127 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package fillstruct
+
+import (
+	data "b"
+	"go/ast"
+	"go/token"
+)
+
+type emptyStruct struct{}
+
+var _ = emptyStruct{}
+
+type basicStruct struct {
+	foo int
+}
+
+var _ = basicStruct{
+	foo: 0,
+} // want ""
+
+type twoArgStruct struct {
+	foo int
+	bar string
+}
+
+var _ = twoArgStruct{
+	foo: 0,
+	bar: "",
+} // want ""
+
+var _ = twoArgStruct{
+	bar: "bar",
+}
+
+type nestedStruct struct {
+	bar   string
+	basic basicStruct
+}
+
+var _ = nestedStruct{
+	bar:   "",
+	basic: basicStruct{},
+} // want ""
+
+var _ = data.B{
+	ExportedInt: 0,
+} // want ""
+
+type typedStruct struct {
+	m  map[string]int
+	s  []int
+	c  chan int
+	c1 <-chan int
+	a  [2]string
+}
+
+var _ = typedStruct{
+	m:  map[string]int{},
+	s:  []int{},
+	c:  make(chan int),
+	c1: make(<-chan int),
+	a:  [2]string{},
+} // want ""
+
+type funStruct struct {
+	fn func(i int) int
+}
+
+var _ = funStruct{
+	fn: func(i int) int {
+	},
+} // want ""
+
+type funStructCompex struct {
+	fn func(i int, s string) (string, int)
+}
+
+var _ = funStructCompex{
+	fn: func(i int, s string) (string, int) {
+	},
+} // want ""
+
+type funStructEmpty struct {
+	fn func()
+}
+
+var _ = funStructEmpty{
+	fn: func() {
+	},
+} // want ""
+
+type Foo struct {
+	A int
+}
+
+type Bar struct {
+	X *Foo
+	Y *Foo
+}
+
+var _ = Bar{
+	X: &Foo{},
+	Y: &Foo{},
+} // want ""
+
+type importedStruct struct {
+	m  map[*ast.CompositeLit]ast.Field
+	s  []ast.BadExpr
+	a  [3]token.Token
+	c  chan ast.EmptyStmt
+	fn func(ast_decl ast.DeclStmt) ast.Ellipsis
+	st ast.CompositeLit
+}
+
+var _ = importedStruct{
+	m: map[*ast.CompositeLit]ast.Field{},
+	s: []ast.BadExpr{},
+	a: [3]token.Token{},
+	c: make(chan ast.EmptyStmt),
+	fn: func(ast_decl ast.DeclStmt) ast.Ellipsis {
+	},
+	st: ast.CompositeLit{},
+} // want ""
diff --git a/internal/lsp/analysis/fillstruct/testdata/src/b/b.go b/internal/lsp/analysis/fillstruct/testdata/src/b/b.go
new file mode 100644
index 0000000..a4b3946
--- /dev/null
+++ b/internal/lsp/analysis/fillstruct/testdata/src/b/b.go
@@ -0,0 +1,6 @@
+package fillstruct
+
+type B struct {
+	ExportedInt   int
+	unexportedInt int
+}
diff --git a/internal/lsp/analysis/undeclaredname/undeclared.go b/internal/lsp/analysis/undeclaredname/undeclared.go
index cf7c770..84a96d2 100644
--- a/internal/lsp/analysis/undeclaredname/undeclared.go
+++ b/internal/lsp/analysis/undeclaredname/undeclared.go
@@ -10,7 +10,7 @@
 	"bytes"
 	"fmt"
 	"go/ast"
-	"go/format"
+	"go/printer"
 	"strings"
 
 	"golang.org/x/tools/go/analysis"
@@ -89,7 +89,7 @@
 		}
 
 		var buf bytes.Buffer
-		if err := format.Node(&buf, pass.Fset, file); err != nil {
+		if err := printer.Fprint(&buf, pass.Fset, file); err != nil {
 			continue
 		}
 		old := buf.Bytes()
diff --git a/internal/lsp/cache/cache.go b/internal/lsp/cache/cache.go
index 75401d7..8a72479 100644
--- a/internal/lsp/cache/cache.go
+++ b/internal/lsp/cache/cache.go
@@ -8,33 +8,37 @@
 	"context"
 	"crypto/sha1"
 	"fmt"
+	"go/ast"
 	"go/token"
+	"go/types"
+	"html/template"
+	"io/ioutil"
+	"os"
 	"reflect"
+	"sort"
 	"strconv"
 	"sync/atomic"
+	"time"
 
-	"golang.org/x/tools/internal/lsp/debug"
+	"golang.org/x/tools/internal/event"
+	"golang.org/x/tools/internal/lsp/debug/tag"
 	"golang.org/x/tools/internal/lsp/source"
 	"golang.org/x/tools/internal/memoize"
 	"golang.org/x/tools/internal/span"
+	errors "golang.org/x/xerrors"
 )
 
 func New(ctx context.Context, options func(*source.Options)) *Cache {
 	index := atomic.AddInt64(&cacheIndex, 1)
 	c := &Cache{
-		fs:      &nativeFileSystem{},
 		id:      strconv.FormatInt(index, 10),
 		fset:    token.NewFileSet(),
 		options: options,
 	}
-	if di := debug.GetInstance(ctx); di != nil {
-		di.State.AddCache(debugCache{c})
-	}
 	return c
 }
 
 type Cache struct {
-	fs      source.FileSystem
 	id      string
 	fset    *token.FileSet
 	options func(*source.Options)
@@ -43,36 +47,65 @@
 }
 
 type fileKey struct {
-	identity source.FileIdentity
+	uri     span.URI
+	modTime time.Time
 }
 
 type fileHandle struct {
-	cache      *Cache
-	underlying source.FileHandle
-	handle     *memoize.Handle
-}
-
-type fileData struct {
+	uri span.URI
 	memoize.NoCopy
 	bytes []byte
 	hash  string
 	err   error
 }
 
-func (c *Cache) GetFile(uri span.URI) source.FileHandle {
-	underlying := c.fs.GetFile(uri)
+func (c *Cache) getFile(ctx context.Context, uri span.URI) (*fileHandle, error) {
+	var modTime time.Time
+	if fi, err := os.Stat(uri.Filename()); err == nil {
+		modTime = fi.ModTime()
+	}
+
 	key := fileKey{
-		identity: underlying.Identity(),
+		uri:     uri,
+		modTime: modTime,
 	}
 	h := c.store.Bind(key, func(ctx context.Context) interface{} {
-		data := &fileData{}
-		data.bytes, data.hash, data.err = underlying.Read(ctx)
-		return data
+		return readFile(ctx, uri, modTime)
 	})
+	v := h.Get(ctx)
+	if v == nil {
+		return nil, ctx.Err()
+	}
+	return v.(*fileHandle), nil
+}
+
+// ioLimit limits the number of parallel file reads per process.
+var ioLimit = make(chan struct{}, 128)
+
+func readFile(ctx context.Context, uri span.URI, origTime time.Time) *fileHandle {
+	ctx, done := event.Start(ctx, "cache.getFile", tag.File.Of(uri.Filename()))
+	_ = ctx
+	defer done()
+
+	ioLimit <- struct{}{}
+	defer func() { <-ioLimit }()
+
+	var modTime time.Time
+	if fi, err := os.Stat(uri.Filename()); err == nil {
+		modTime = fi.ModTime()
+	}
+
+	if modTime != origTime {
+		return &fileHandle{err: errors.Errorf("%s: file has been modified", uri.Filename())}
+	}
+	data, err := ioutil.ReadFile(uri.Filename())
+	if err != nil {
+		return &fileHandle{err: err}
+	}
 	return &fileHandle{
-		cache:      c,
-		underlying: underlying,
-		handle:     h,
+		uri:   uri,
+		bytes: data,
+		hash:  hashContents(data),
 	}
 }
 
@@ -84,9 +117,7 @@
 		options:  source.DefaultOptions(),
 		overlays: make(map[span.URI]*overlay),
 	}
-	if di := debug.GetInstance(ctx); di != nil {
-		di.State.AddSession(DebugSession{s})
-	}
+	event.Log(ctx, "New session", KeyCreateSession.Of(s))
 	return s
 }
 
@@ -94,21 +125,28 @@
 	return c.fset
 }
 
-func (h *fileHandle) FileSystem() source.FileSystem {
-	return h.cache
+func (h *fileHandle) URI() span.URI {
+	return h.uri
+}
+
+func (h *fileHandle) Kind() source.FileKind {
+	return source.DetectLanguage("", h.uri.Filename())
+}
+
+func (h *fileHandle) Version() float64 {
+	return 0
 }
 
 func (h *fileHandle) Identity() source.FileIdentity {
-	return h.underlying.Identity()
+	return source.FileIdentity{
+		URI:        h.uri,
+		Identifier: h.hash,
+		Kind:       h.Kind(),
+	}
 }
 
-func (h *fileHandle) Read(ctx context.Context) ([]byte, string, error) {
-	v := h.handle.Get(ctx)
-	if v == nil {
-		return nil, "", ctx.Err()
-	}
-	data := v.(*fileData)
-	return data.bytes, data.hash, data.err
+func (h *fileHandle) Read() ([]byte, error) {
+	return h.bytes, h.err
 }
 
 func hashContents(contents []byte) string {
@@ -119,8 +157,97 @@
 
 var cacheIndex, sessionIndex, viewIndex int64
 
-type debugCache struct{ *Cache }
+func (c *Cache) ID() string                     { return c.id }
+func (c *Cache) MemStats() map[reflect.Type]int { return c.store.Stats() }
 
-func (c *Cache) ID() string                         { return c.id }
-func (c debugCache) FileSet() *token.FileSet        { return c.fset }
-func (c debugCache) MemStats() map[reflect.Type]int { return c.store.Stats() }
+type packageStat struct {
+	id        packageID
+	mode      source.ParseMode
+	file      int64
+	ast       int64
+	types     int64
+	typesInfo int64
+	total     int64
+}
+
+func (c *Cache) PackageStats(withNames bool) template.HTML {
+	var packageStats []packageStat
+	c.store.DebugOnlyIterate(func(k, v interface{}) {
+		switch k.(type) {
+		case packageHandleKey:
+			v := v.(*packageData)
+			stat := packageStat{
+				id:        v.pkg.id,
+				mode:      v.pkg.mode,
+				types:     typesCost(v.pkg.types.Scope()),
+				typesInfo: typesInfoCost(v.pkg.typesInfo),
+			}
+			for _, f := range v.pkg.compiledGoFiles {
+				fvi := f.handle.Cached()
+				if fvi == nil {
+					continue
+				}
+				fv := fvi.(*parseGoData)
+				stat.file += int64(len(fv.src))
+				stat.ast += astCost(fv.ast)
+			}
+			stat.total = stat.file + stat.ast + stat.types + stat.typesInfo
+			packageStats = append(packageStats, stat)
+		}
+	})
+	var totalCost int64
+	for _, stat := range packageStats {
+		totalCost += stat.total
+	}
+	sort.Slice(packageStats, func(i, j int) bool {
+		return packageStats[i].total > packageStats[j].total
+	})
+	html := "<table><thead><td>Name</td><td>total = file + ast + types + types info</td></thead>\n"
+	human := func(n int64) string {
+		return fmt.Sprintf("%.2f", float64(n)/(1024*1024))
+	}
+	var printedCost int64
+	for _, stat := range packageStats {
+		name := stat.id
+		if !withNames {
+			name = "-"
+		}
+		html += fmt.Sprintf("<tr><td>%v (%v)</td><td>%v = %v + %v + %v + %v</td></tr>\n", name, stat.mode,
+			human(stat.total), human(stat.file), human(stat.ast), human(stat.types), human(stat.typesInfo))
+		printedCost += stat.total
+		if float64(printedCost) > float64(totalCost)*.9 {
+			break
+		}
+	}
+	html += "</table>\n"
+	return template.HTML(html)
+}
+
+func astCost(f *ast.File) int64 {
+	var count int64
+	ast.Inspect(f, func(n ast.Node) bool {
+		count += 32 // nodes are pretty small.
+		return true
+	})
+	return count
+}
+
+func typesCost(scope *types.Scope) int64 {
+	cost := 64 + int64(scope.Len())*128 // types.object looks pretty big
+	for i := 0; i < scope.NumChildren(); i++ {
+		cost += typesCost(scope.Child(i))
+	}
+	return cost
+}
+
+func typesInfoCost(info *types.Info) int64 {
+	// Most of these refer to existing objects, with the exception of InitOrder, Selections, and Types.
+	cost := 24*len(info.Defs) +
+		32*len(info.Implicits) +
+		256*len(info.InitOrder) + // these are big, but there aren't many of them.
+		32*len(info.Scopes) +
+		128*len(info.Selections) + // wild guess
+		128*len(info.Types) + // wild guess
+		32*len(info.Uses)
+	return int64(cost)
+}
diff --git a/internal/lsp/cache/check.go b/internal/lsp/cache/check.go
index 6d16b31..5c7d5f1 100644
--- a/internal/lsp/cache/check.go
+++ b/internal/lsp/cache/check.go
@@ -22,6 +22,7 @@
 	"golang.org/x/tools/internal/lsp/source"
 	"golang.org/x/tools/internal/memoize"
 	"golang.org/x/tools/internal/span"
+	"golang.org/x/tools/internal/typesinternal"
 	errors "golang.org/x/xerrors"
 )
 
@@ -155,11 +156,11 @@
 		deps[depHandle.m.pkgPath] = depHandle
 		depKeys = append(depKeys, depHandle.key)
 	}
-	ph.key = checkPackageKey(ph.m.id, ph.compiledGoFiles, m.config, depKeys)
+	ph.key = checkPackageKey(ctx, ph.m.id, ph.compiledGoFiles, m.config, depKeys)
 	return ph, deps, nil
 }
 
-func checkPackageKey(id packageID, pghs []*parseGoHandle, cfg *packages.Config, deps []packageHandleKey) packageHandleKey {
+func checkPackageKey(ctx context.Context, id packageID, pghs []*parseGoHandle, cfg *packages.Config, deps []packageHandleKey) packageHandleKey {
 	var depBytes []byte
 	for _, dep := range deps {
 		depBytes = append(depBytes, []byte(dep)...)
@@ -253,15 +254,15 @@
 }
 
 func (s *snapshot) parseGoHandles(ctx context.Context, files []span.URI, mode source.ParseMode) ([]*parseGoHandle, error) {
-	phs := make([]*parseGoHandle, 0, len(files))
+	pghs := make([]*parseGoHandle, 0, len(files))
 	for _, uri := range files {
-		fh, err := s.GetFile(uri)
+		fh, err := s.GetFile(ctx, uri)
 		if err != nil {
 			return nil, err
 		}
-		phs = append(phs, s.view.session.cache.parseGoHandle(fh, mode))
+		pghs = append(pghs, s.view.session.cache.parseGoHandle(ctx, fh, mode))
 	}
-	return phs, nil
+	return pghs, nil
 }
 
 func typeCheck(ctx context.Context, fset *token.FileSet, m *metadata, mode source.ParseMode, goFiles, compiledGoFiles []*parseGoHandle, deps map[packagePath]*packageHandle) (*pkg, error) {
@@ -368,25 +369,7 @@
 			if ctx.Err() != nil {
 				return nil, ctx.Err()
 			}
-			dep := deps[packagePath(pkgPath)]
-			if dep == nil {
-				// We may be in GOPATH mode, in which case we need to check vendor dirs.
-				searchDir := path.Dir(pkg.PkgPath())
-				for {
-					vdir := packagePath(path.Join(searchDir, "vendor", pkgPath))
-					if vdep := deps[vdir]; vdep != nil {
-						dep = vdep
-						break
-					}
-
-					// Search until Dir doesn't take us anywhere new, e.g. "." or "/".
-					next := path.Dir(searchDir)
-					if searchDir == next {
-						break
-					}
-					searchDir = next
-				}
-			}
+			dep := resolveImportPath(pkgPath, pkg, deps)
 			if dep == nil {
 				return nil, errors.Errorf("no package for import %s", pkgPath)
 			}
@@ -401,6 +384,10 @@
 			return depPkg.types, nil
 		}),
 	}
+	// We want to type check cgo code if go/types supports it.
+	// We passed typecheckCgo to go/packages when we Loaded.
+	typesinternal.SetUsesCgo(cfg)
+
 	check := types.NewChecker(cfg, fset, pkg.types, pkg.typesInfo)
 
 	// Type checking errors are handled via the config, so ignore them here.
@@ -428,6 +415,41 @@
 	return pkg, nil
 }
 
+// resolveImportPath resolves an import path in pkg to a package from deps.
+// It should produce the same results as resolveImportPath:
+// https://cs.opensource.google/go/go/+/master:src/cmd/go/internal/load/pkg.go;drc=641918ee09cb44d282a30ee8b66f99a0b63eaef9;l=990.
+func resolveImportPath(importPath string, pkg *pkg, deps map[packagePath]*packageHandle) *packageHandle {
+	if dep := deps[packagePath(importPath)]; dep != nil {
+		return dep
+	}
+	// We may be in GOPATH mode, in which case we need to check vendor dirs.
+	searchDir := path.Dir(pkg.PkgPath())
+	for {
+		vdir := packagePath(path.Join(searchDir, "vendor", importPath))
+		if vdep := deps[vdir]; vdep != nil {
+			return vdep
+		}
+
+		// Search until Dir doesn't take us anywhere new, e.g. "." or "/".
+		next := path.Dir(searchDir)
+		if searchDir == next {
+			break
+		}
+		searchDir = next
+	}
+
+	// Vendor didn't work. Let's try minimal module compatibility mode.
+	// In MMC, the packagePath is the canonical (.../vN/...) path, which
+	// is hard to calculate. But the go command has already resolved the ID
+	// to the non-versioned path, and we can take advantage of that.
+	for _, dep := range deps {
+		if dep.ID() == importPath {
+			return dep
+		}
+	}
+	return nil
+}
+
 func isValidImport(pkgPath, importPkgPath packagePath) bool {
 	i := strings.LastIndex(string(importPkgPath), "/internal/")
 	if i == -1 {
diff --git a/internal/lsp/cache/debug.go b/internal/lsp/cache/debug.go
deleted file mode 100644
index d4ea528..0000000
--- a/internal/lsp/cache/debug.go
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2019 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package cache
-
-import (
-	"sort"
-
-	"golang.org/x/tools/internal/lsp/debug"
-	"golang.org/x/tools/internal/span"
-)
-
-type debugView struct{ *view }
-
-func (v debugView) ID() string             { return v.id }
-func (v debugView) Session() debug.Session { return DebugSession{v.session} }
-func (v debugView) Env() []string          { return v.Options().Env }
-
-type DebugSession struct{ *Session }
-
-func (s DebugSession) ID() string         { return s.id }
-func (s DebugSession) Cache() debug.Cache { return debugCache{s.cache} }
-func (s DebugSession) Files() []*debug.File {
-	var files []*debug.File
-	seen := make(map[span.URI]*debug.File)
-	s.overlayMu.Lock()
-	defer s.overlayMu.Unlock()
-	for _, overlay := range s.overlays {
-		f, ok := seen[overlay.uri]
-		if !ok {
-			f = &debug.File{Session: s, URI: overlay.uri}
-			seen[overlay.uri] = f
-			files = append(files, f)
-		}
-		f.Data = string(overlay.text)
-		f.Error = nil
-		f.Hash = overlay.hash
-	}
-	sort.Slice(files, func(i int, j int) bool {
-		return files[i].URI < files[j].URI
-	})
-	return files
-}
-
-func (s DebugSession) File(hash string) *debug.File {
-	s.overlayMu.Lock()
-	defer s.overlayMu.Unlock()
-	for _, overlay := range s.overlays {
-		if overlay.hash == hash {
-			return &debug.File{
-				Session: s,
-				URI:     overlay.uri,
-				Data:    string(overlay.text),
-				Error:   nil,
-				Hash:    overlay.hash,
-			}
-		}
-	}
-	return &debug.File{
-		Session: s,
-		Hash:    hash,
-	}
-}
diff --git a/internal/lsp/cache/errors.go b/internal/lsp/cache/errors.go
index 14995b9..eff1c80 100644
--- a/internal/lsp/cache/errors.go
+++ b/internal/lsp/cache/errors.go
@@ -195,10 +195,7 @@
 	if err != nil {
 		return span.Span{}, err
 	}
-	if err != nil {
-		return span.Span{}, err
-	}
-	data, _, err := ph.File().Read(ctx)
+	data, err := ph.File().Read()
 	if err != nil {
 		return span.Span{}, err
 	}
@@ -221,7 +218,7 @@
 	}
 	tok := fset.File(file.Pos())
 	if tok == nil {
-		return span.Span{}, errors.Errorf("no token.File for %s", ph.File().Identity().URI)
+		return span.Span{}, errors.Errorf("no token.File for %s", ph.File().URI())
 	}
 	pos := tok.Pos(posn.Offset)
 	return span.NewRange(fset, pos, pos).Span()
diff --git a/internal/lsp/cache/external.go b/internal/lsp/cache/external.go
deleted file mode 100644
index 1f04d15..0000000
--- a/internal/lsp/cache/external.go
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2019 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package cache
-
-import (
-	"context"
-	"io/ioutil"
-	"os"
-
-	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/lsp/debug/tag"
-	"golang.org/x/tools/internal/lsp/source"
-	"golang.org/x/tools/internal/span"
-	errors "golang.org/x/xerrors"
-)
-
-// ioLimit limits the number of parallel file reads per process.
-var ioLimit = make(chan struct{}, 128)
-
-// nativeFileSystem implements FileSystem reading from the normal os file system.
-type nativeFileSystem struct{}
-
-// nativeFileHandle implements FileHandle for nativeFileSystem
-type nativeFileHandle struct {
-	fs       *nativeFileSystem
-	identity source.FileIdentity
-}
-
-func (fs *nativeFileSystem) GetFile(uri span.URI) source.FileHandle {
-	return &nativeFileHandle{
-		fs: fs,
-		identity: source.FileIdentity{
-			URI:        uri,
-			Identifier: identifier(uri.Filename()),
-			Kind:       source.DetectLanguage("", uri.Filename()),
-		},
-	}
-}
-
-func (h *nativeFileHandle) FileSystem() source.FileSystem {
-	return h.fs
-}
-
-func (h *nativeFileHandle) Identity() source.FileIdentity {
-	return h.identity
-}
-
-func (h *nativeFileHandle) Read(ctx context.Context) ([]byte, string, error) {
-	ctx, done := event.Start(ctx, "cache.nativeFileHandle.Read", tag.File.Of(h.identity.URI.Filename()))
-	_ = ctx
-	defer done()
-
-	ioLimit <- struct{}{}
-	defer func() { <-ioLimit }()
-
-	if id := identifier(h.identity.URI.Filename()); id != h.identity.Identifier {
-		return nil, "", errors.Errorf("%s: file has been modified", h.identity.URI.Filename())
-	}
-	data, err := ioutil.ReadFile(h.identity.URI.Filename())
-	if err != nil {
-		return nil, "", err
-	}
-	return data, hashContents(data), nil
-}
-
-func identifier(filename string) string {
-	if fi, err := os.Stat(filename); err == nil {
-		return fi.ModTime().String()
-	}
-	return "DOES NOT EXIST"
-}
diff --git a/internal/lsp/cache/keys.go b/internal/lsp/cache/keys.go
new file mode 100644
index 0000000..449daba
--- /dev/null
+++ b/internal/lsp/cache/keys.go
@@ -0,0 +1,52 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cache
+
+import (
+	"io"
+
+	"golang.org/x/tools/internal/event/label"
+)
+
+var (
+	KeyCreateSession   = NewSessionKey("create_session", "A new session was added")
+	KeyUpdateSession   = NewSessionKey("update_session", "Updated information about a session")
+	KeyShutdownSession = NewSessionKey("shutdown_session", "A session was shut down")
+)
+
+// SessionKey represents an event label key that has a *Session value.
+type SessionKey struct {
+	name        string
+	description string
+}
+
+// NewSessionKey creates a new Key for *Session values.
+func NewSessionKey(name, description string) *SessionKey {
+	return &SessionKey{name: name, description: description}
+}
+
+func (k *SessionKey) Name() string        { return k.name }
+func (k *SessionKey) Description() string { return k.description }
+
+func (k *SessionKey) Format(w io.Writer, buf []byte, l label.Label) {
+	io.WriteString(w, k.From(l).ID())
+}
+
+// Of creates a new Label with this key and the supplied session.
+func (k *SessionKey) Of(v *Session) label.Label { return label.OfValue(k, v) }
+
+// Get can be used to get the session for the key from a label.Map.
+func (k *SessionKey) Get(lm label.Map) *Session {
+	if t := lm.Find(k); t.Valid() {
+		return k.From(t)
+	}
+	return nil
+}
+
+// From can be used to get the session value from a Label.
+func (k *SessionKey) From(t label.Label) *Session {
+	err, _ := t.UnpackValue().(*Session)
+	return err
+}
diff --git a/internal/lsp/cache/load.go b/internal/lsp/cache/load.go
index b16485b..6b4d847 100644
--- a/internal/lsp/cache/load.go
+++ b/internal/lsp/cache/load.go
@@ -31,7 +31,7 @@
 	errors          []packages.Error
 	deps            []packageID
 	missingDeps     map[packagePath]struct{}
-	module          *packagesinternal.Module
+	module          *packages.Module
 
 	// config is the *packages.Config associated with the loaded package.
 	config *packages.Config
@@ -83,8 +83,29 @@
 	ctx, done := event.Start(ctx, "cache.view.load", tag.Query.Of(query))
 	defer done()
 
-	cfg := s.Config(ctx)
+	cfg := s.config(ctx)
+	cleanup := func() {}
+	if s.view.tmpMod {
+		modFH, err := s.GetFile(ctx, s.view.modURI)
+		if err != nil {
+			return err
+		}
+		var sumFH source.FileHandle
+		if s.view.sumURI != "" {
+			sumFH, err = s.GetFile(ctx, s.view.sumURI)
+			if err != nil {
+				return err
+			}
+		}
+		var tmpURI span.URI
+		tmpURI, cleanup, err = tempModFile(modFH, sumFH)
+		if err != nil {
+			return err
+		}
+		cfg.BuildFlags = append(cfg.BuildFlags, fmt.Sprintf("-modfile=%s", tmpURI.Filename()))
+	}
 	pkgs, err := packages.Load(cfg, query...)
+	cleanup()
 
 	// If the context was canceled, return early. Otherwise, we might be
 	// type-checking an incomplete result. Check the context directly,
@@ -93,6 +114,11 @@
 		return ctx.Err()
 	}
 	if err != nil {
+		// Match on common error messages. This is really hacky, but I'm not sure
+		// of any better way. This can be removed when golang/go#39164 is resolved.
+		if strings.Contains(err.Error(), "inconsistent vendoring") {
+			return source.InconsistentVendoring
+		}
 		event.Error(ctx, "go/packages.Load", err, tag.Snapshot.Of(s.ID()), tag.Directory.Of(cfg.Dir), tag.Query.Of(query), tag.PackageCount.Of(len(pkgs)))
 	} else {
 		event.Log(ctx, "go/packages.Load", tag.Snapshot.Of(s.ID()), tag.Directory.Of(cfg.Dir), tag.Query.Of(query), tag.PackageCount.Of(len(pkgs)))
@@ -100,6 +126,7 @@
 	if len(pkgs) == 0 {
 		return err
 	}
+
 	for _, pkg := range pkgs {
 		if !containsDir || s.view.Options().VerboseOutput {
 			event.Log(ctx, "go/packages.Load", tag.Snapshot.Of(s.ID()), tag.PackagePath.Of(pkg.PkgPath), tag.Files.Of(pkg.CompiledGoFiles))
@@ -149,7 +176,7 @@
 		typesSizes: pkg.TypesSizes,
 		errors:     pkg.Errors,
 		config:     cfg,
-		module:     packagesinternal.GetModule(pkg),
+		module:     pkg.Module,
 	}
 
 	for _, filename := range pkg.CompiledGoFiles {
@@ -204,18 +231,28 @@
 	}
 
 	// Set the workspace packages. If any of the package's files belong to the
-	// view, then the package is considered to be a workspace package.
+	// view, then the package may be a workspace package.
 	for _, uri := range append(m.compiledGoFiles, m.goFiles...) {
-		// If the package's files are in this view, mark it as a workspace package.
-		if s.view.contains(uri) {
-			// A test variant of a package can only be loaded directly by loading
-			// the non-test variant with -test. Track the import path of the non-test variant.
-			if m.forTest != "" {
-				s.workspacePackages[m.id] = m.forTest
-			} else {
-				s.workspacePackages[m.id] = pkgPath
-			}
-			break
+		if !s.view.contains(uri) {
+			continue
+		}
+
+		// The package's files are in this view. It may be a workspace package.
+		if strings.Contains(string(uri), "/vendor/") {
+			// Vendored packages are not likely to be interesting to the user.
+			continue
+		}
+
+		switch {
+		case m.forTest == "":
+			// A normal package.
+			s.workspacePackages[m.id] = pkgPath
+		case m.forTest == m.pkgPath, m.forTest+"_test" == m.pkgPath:
+			// The test variant of some workspace package or its x_test.
+			// To load it, we need to load the non-test variant with -test.
+			s.workspacePackages[m.id] = m.forTest
+		default:
+			// A test variant of some intermediate package. We don't care about it.
 		}
 	}
 	return m, nil
diff --git a/internal/lsp/cache/mod.go b/internal/lsp/cache/mod.go
index 9f01451..bcd04f5 100644
--- a/internal/lsp/cache/mod.go
+++ b/internal/lsp/cache/mod.go
@@ -7,453 +7,163 @@
 import (
 	"context"
 	"fmt"
-	"io/ioutil"
 	"os"
+	"path/filepath"
 	"regexp"
 	"strconv"
 	"strings"
 
 	"golang.org/x/mod/modfile"
-	"golang.org/x/tools/go/packages"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/gocommand"
 	"golang.org/x/tools/internal/lsp/debug/tag"
 	"golang.org/x/tools/internal/lsp/protocol"
 	"golang.org/x/tools/internal/lsp/source"
 	"golang.org/x/tools/internal/memoize"
-	"golang.org/x/tools/internal/packagesinternal"
 	"golang.org/x/tools/internal/span"
 	errors "golang.org/x/xerrors"
 )
 
 const (
-	ModTidyError = "go mod tidy"
-	SyntaxError  = "syntax"
+	SyntaxError = "syntax"
 )
 
-type modKey struct {
-	sessionID string
-	cfg       string
-	gomod     string
-	view      string
-}
-
-type modTidyKey struct {
-	sessionID       string
-	cfg             string
-	gomod           string
-	imports         string
-	unsavedOverlays string
-	view            string
-}
-
-type modHandle struct {
+type parseModHandle struct {
 	handle *memoize.Handle
-	file   source.FileHandle
-	cfg    *packages.Config
+
+	mod, sum source.FileHandle
 }
 
-type modData struct {
+type parseModData struct {
 	memoize.NoCopy
 
-	// origfh is the file handle for the original go.mod file.
-	origfh source.FileHandle
+	parsed *modfile.File
+	m      *protocol.ColumnMapper
 
-	// origParsedFile contains the parsed contents that are used to diff with
-	// the ideal contents.
-	origParsedFile *modfile.File
-
-	// origMapper is the column mapper for the original go.mod file.
-	origMapper *protocol.ColumnMapper
-
-	// idealParsedFile contains the parsed contents for the go.mod file
-	// after it has been "tidied".
-	idealParsedFile *modfile.File
-
-	// unusedDeps is the map containing the dependencies that are left after
-	// removing the ones that are identical in the original and ideal go.mods.
-	unusedDeps map[string]*modfile.Require
-
-	// missingDeps is the map containing the dependencies that are left after
-	// removing the ones that are identical in the original and ideal go.mods.
-	missingDeps map[string]*modfile.Require
-
-	// upgrades is a map of path->version that contains any upgrades for the go.mod.
-	upgrades map[string]string
-
-	// why is a map of path->explanation that contains all the "go mod why" contents
-	// for each require statement.
-	why map[string]string
-
-	// parseErrors are the errors that arise when we diff between a user's go.mod
-	// and the "tidied" go.mod.
+	// parseErrors refers to syntax errors found in the go.mod file.
 	parseErrors []source.Error
 
-	// err is any error that occurs while we are calculating the parseErrors.
+	// err is any error encountered while parsing the file.
 	err error
 }
 
-func (mh *modHandle) String() string {
-	return mh.File().Identity().URI.Filename()
+func (mh *parseModHandle) Mod() source.FileHandle {
+	return mh.mod
 }
 
-func (mh *modHandle) File() source.FileHandle {
-	return mh.file
+func (mh *parseModHandle) Sum() source.FileHandle {
+	return mh.sum
 }
 
-func (mh *modHandle) Parse(ctx context.Context) (*modfile.File, *protocol.ColumnMapper, error) {
+func (mh *parseModHandle) Parse(ctx context.Context) (*modfile.File, *protocol.ColumnMapper, []source.Error, error) {
 	v := mh.handle.Get(ctx)
 	if v == nil {
-		return nil, nil, errors.Errorf("no parsed file for %s", mh.File().Identity().URI)
+		return nil, nil, nil, ctx.Err()
 	}
-	data := v.(*modData)
-	return data.origParsedFile, data.origMapper, data.err
+	data := v.(*parseModData)
+	return data.parsed, data.m, data.parseErrors, data.err
 }
 
-func (mh *modHandle) Upgrades(ctx context.Context) (*modfile.File, *protocol.ColumnMapper, map[string]string, error) {
-	v := mh.handle.Get(ctx)
-	if v == nil {
-		return nil, nil, nil, errors.Errorf("no parsed file for %s", mh.File().Identity().URI)
-	}
-	data := v.(*modData)
-	return data.origParsedFile, data.origMapper, data.upgrades, data.err
-}
-
-func (mh *modHandle) Why(ctx context.Context) (*modfile.File, *protocol.ColumnMapper, map[string]string, error) {
-	v := mh.handle.Get(ctx)
-	if v == nil {
-		return nil, nil, nil, errors.Errorf("no parsed file for %s", mh.File().Identity().URI)
-	}
-	data := v.(*modData)
-	return data.origParsedFile, data.origMapper, data.why, data.err
-}
-
-func (s *snapshot) ModHandle(ctx context.Context, fh source.FileHandle) source.ModHandle {
-	uri := fh.Identity().URI
-	if handle := s.getModHandle(uri); handle != nil {
-		return handle
-	}
-
-	realURI, tempURI := s.view.ModFiles()
-	folder := s.View().Folder().Filename()
-	cfg := s.Config(ctx)
-
-	key := modKey{
-		sessionID: s.view.session.id,
-		cfg:       hashConfig(cfg),
-		gomod:     fh.Identity().String(),
-		view:      folder,
-	}
-	h := s.view.session.cache.store.Bind(key, func(ctx context.Context) interface{} {
-		ctx, done := event.Start(ctx, "cache.ModHandle", tag.URI.Of(uri))
-		defer done()
-
-		contents, _, err := fh.Read(ctx)
-		if err != nil {
-			return &modData{
-				err: err,
-			}
-		}
-		parsedFile, err := modfile.Parse(uri.Filename(), contents, nil)
-		if err != nil {
-			return &modData{
-				err: err,
-			}
-		}
-		data := &modData{
-			origfh:         fh,
-			origParsedFile: parsedFile,
-			origMapper: &protocol.ColumnMapper{
-				URI:       uri,
-				Converter: span.NewContentConverter(uri.Filename(), contents),
-				Content:   contents,
-			},
-		}
-		// If the go.mod file is not the view's go.mod file, then we just want to parse.
-		if uri != realURI {
-			return data
-		}
-
-		// If we have a tempModfile, copy the real go.mod file content into the temp go.mod file.
-		if tempURI != "" {
-			if err := ioutil.WriteFile(tempURI.Filename(), contents, os.ModePerm); err != nil {
-				return &modData{
-					err: err,
-				}
-			}
-		}
-		// Only get dependency upgrades if the go.mod file is the same as the view's.
-		if err := dependencyUpgrades(ctx, cfg, folder, data); err != nil {
-			return &modData{
-				err: err,
-			}
-		}
-		// Only run "go mod why" if the go.mod file is the same as the view's.
-		if err := goModWhy(ctx, cfg, folder, data); err != nil {
-			return &modData{
-				err: err,
-			}
-		}
-		return data
-	})
-	s.mu.Lock()
-	defer s.mu.Unlock()
-	s.modHandles[uri] = &modHandle{
-		handle: h,
-		file:   fh,
-		cfg:    cfg,
-	}
-	return s.modHandles[uri]
-}
-
-func goModWhy(ctx context.Context, cfg *packages.Config, folder string, data *modData) error {
-	if len(data.origParsedFile.Require) == 0 {
-		return nil
-	}
-	// Run "go mod why" on all the dependencies to get information about the usages.
-	inv := gocommand.Invocation{
-		Verb:       "mod",
-		Args:       []string{"why", "-m"},
-		BuildFlags: cfg.BuildFlags,
-		Env:        cfg.Env,
-		WorkingDir: folder,
-	}
-	for _, req := range data.origParsedFile.Require {
-		inv.Args = append(inv.Args, req.Mod.Path)
-	}
-	stdout, err := packagesinternal.GetGoCmdRunner(cfg).Run(ctx, inv)
-	if err != nil {
-		return err
-	}
-	whyList := strings.Split(stdout.String(), "\n\n")
-	if len(whyList) <= 1 || len(whyList) != len(data.origParsedFile.Require) {
-		return nil
-	}
-	data.why = make(map[string]string, len(data.origParsedFile.Require))
-	for i, req := range data.origParsedFile.Require {
-		data.why[req.Mod.Path] = whyList[i]
-	}
-	return nil
-}
-
-func dependencyUpgrades(ctx context.Context, cfg *packages.Config, folder string, data *modData) error {
-	if len(data.origParsedFile.Require) == 0 {
-		return nil
-	}
-	// Run "go list -u -m all" to be able to see which deps can be upgraded.
-	inv := gocommand.Invocation{
-		Verb:       "list",
-		Args:       []string{"-u", "-m", "all"},
-		BuildFlags: cfg.BuildFlags,
-		Env:        cfg.Env,
-		WorkingDir: folder,
-	}
-	stdout, err := packagesinternal.GetGoCmdRunner(cfg).Run(ctx, inv)
-	if err != nil {
-		return err
-	}
-	upgradesList := strings.Split(stdout.String(), "\n")
-	if len(upgradesList) <= 1 {
-		return nil
-	}
-	data.upgrades = make(map[string]string)
-	for _, upgrade := range upgradesList[1:] {
-		// Example: "github.com/x/tools v1.1.0 [v1.2.0]"
-		info := strings.Split(upgrade, " ")
-		if len(info) < 3 {
-			continue
-		}
-		dep, version := info[0], info[2]
-		latest := version[1:]                    // remove the "["
-		latest = strings.TrimSuffix(latest, "]") // remove the "]"
-		data.upgrades[dep] = latest
-	}
-	return nil
-}
-
-func (mh *modHandle) Tidy(ctx context.Context) (*modfile.File, *protocol.ColumnMapper, map[string]*modfile.Require, []source.Error, error) {
-	v := mh.handle.Get(ctx)
-	if v == nil {
-		return nil, nil, nil, nil, errors.Errorf("no parsed file for %s", mh.File().Identity().URI)
-	}
-	data := v.(*modData)
-	return data.origParsedFile, data.origMapper, data.missingDeps, data.parseErrors, data.err
-}
-
-func (s *snapshot) ModTidyHandle(ctx context.Context, realfh source.FileHandle) (source.ModTidyHandle, error) {
-	if handle := s.getModTidyHandle(); handle != nil {
+func (s *snapshot) ParseModHandle(ctx context.Context, modFH source.FileHandle) (source.ParseModHandle, error) {
+	if handle := s.getModHandle(modFH.URI()); handle != nil {
 		return handle, nil
 	}
-
-	realURI, tempURI := s.view.ModFiles()
-	cfg := s.Config(ctx)
-	options := s.View().Options()
-	folder := s.View().Folder().Filename()
-	gocmdRunner := s.view.gocmdRunner
-
-	wsPackages, err := s.WorkspacePackages(ctx)
-	if ctx.Err() != nil {
-		return nil, ctx.Err()
-	}
-	if err != nil {
-		return nil, err
-	}
-	imports, err := hashImports(ctx, wsPackages)
-	if err != nil {
-		return nil, err
-	}
-	s.mu.Lock()
-	overlayHash := hashUnsavedOverlays(s.files)
-	s.mu.Unlock()
-	key := modTidyKey{
-		sessionID:       s.view.session.id,
-		view:            folder,
-		imports:         imports,
-		unsavedOverlays: overlayHash,
-		gomod:           realfh.Identity().Identifier,
-		cfg:             hashConfig(cfg),
-	}
-	h := s.view.session.cache.store.Bind(key, func(ctx context.Context) interface{} {
-		// Check the case when the tempModfile flag is turned off.
-		if realURI == "" || tempURI == "" {
-			return &modData{}
-		}
-
-		ctx, done := event.Start(ctx, "cache.ModTidyHandle", tag.URI.Of(realURI))
+	h := s.view.session.cache.store.Bind(modFH.Identity().String(), func(ctx context.Context) interface{} {
+		_, done := event.Start(ctx, "cache.ParseModHandle", tag.URI.Of(modFH.URI()))
 		defer done()
 
-		realContents, _, err := realfh.Read(ctx)
+		contents, err := modFH.Read()
 		if err != nil {
-			return &modData{
-				err: err,
-			}
+			return &parseModData{err: err}
 		}
-		realMapper := &protocol.ColumnMapper{
-			URI:       realURI,
-			Converter: span.NewContentConverter(realURI.Filename(), realContents),
-			Content:   realContents,
+		m := &protocol.ColumnMapper{
+			URI:       modFH.URI(),
+			Converter: span.NewContentConverter(modFH.URI().Filename(), contents),
+			Content:   contents,
 		}
-		origParsedFile, err := modfile.Parse(realURI.Filename(), realContents, nil)
+		parsed, err := modfile.Parse(modFH.URI().Filename(), contents, nil)
 		if err != nil {
-			if parseErr, err := extractModParseErrors(ctx, realURI, realMapper, err, realContents); err == nil {
-				return &modData{
-					parseErrors: []source.Error{parseErr},
-				}
+			parseErr, _ := extractModParseErrors(modFH.URI(), m, err, contents)
+			var parseErrors []source.Error
+			if parseErr != nil {
+				parseErrors = append(parseErrors, *parseErr)
 			}
-			return &modData{
-				err: err,
+			return &parseModData{
+				parseErrors: parseErrors,
+				err:         err,
 			}
 		}
-
-		// Copy the real go.mod file content into the temp go.mod file.
-		if err := ioutil.WriteFile(tempURI.Filename(), realContents, os.ModePerm); err != nil {
-			return &modData{
-				err: err,
-			}
+		return &parseModData{
+			parsed: parsed,
+			m:      m,
 		}
-
-		// We want to run "go mod tidy" to be able to diff between the real and the temp files.
-		inv := gocommand.Invocation{
-			Verb:       "mod",
-			Args:       []string{"tidy"},
-			BuildFlags: cfg.BuildFlags,
-			Env:        cfg.Env,
-			WorkingDir: folder,
-		}
-		if _, err := gocmdRunner.Run(ctx, inv); err != nil {
-			return &modData{
-				err: err,
-			}
-		}
-
-		// Go directly to disk to get the temporary mod file, since it is always on disk.
-		tempContents, err := ioutil.ReadFile(tempURI.Filename())
-		if err != nil {
-			return &modData{
-				err: err,
-			}
-		}
-		idealParsedFile, err := modfile.Parse(tempURI.Filename(), tempContents, nil)
-		if err != nil {
-			// We do not need to worry about the temporary file's parse errors since it has been "tidied".
-			return &modData{
-				err: err,
-			}
-		}
-
-		data := &modData{
-			origfh:          realfh,
-			origParsedFile:  origParsedFile,
-			origMapper:      realMapper,
-			idealParsedFile: idealParsedFile,
-			unusedDeps:      make(map[string]*modfile.Require, len(origParsedFile.Require)),
-			missingDeps:     make(map[string]*modfile.Require, len(idealParsedFile.Require)),
-		}
-		// Get the dependencies that are different between the original and ideal mod files.
-		for _, req := range origParsedFile.Require {
-			data.unusedDeps[req.Mod.Path] = req
-		}
-		for _, req := range idealParsedFile.Require {
-			origDep := data.unusedDeps[req.Mod.Path]
-			if origDep != nil && origDep.Indirect == req.Indirect {
-				delete(data.unusedDeps, req.Mod.Path)
-			} else {
-				data.missingDeps[req.Mod.Path] = req
-			}
-		}
-		data.parseErrors, data.err = modRequireErrors(options, data)
-
-		for _, req := range data.missingDeps {
-			if data.unusedDeps[req.Mod.Path] != nil {
-				delete(data.missingDeps, req.Mod.Path)
-			}
-		}
-		return data
 	})
+	// Get the go.sum file, either from the snapshot or directly from the
+	// cache. Avoid (*snapshot).GetFile here, as we don't want to add
+	// nonexistent file handles to the snapshot if the file does not exist.
+	sumURI := span.URIFromPath(sumFilename(modFH.URI()))
+	sumFH := s.FindFile(sumURI)
+	if sumFH == nil {
+		fh, err := s.view.session.cache.getFile(ctx, sumURI)
+		if err != nil && !os.IsNotExist(err) {
+			return nil, err
+		}
+		if fh.err != nil && !os.IsNotExist(fh.err) {
+			return nil, fh.err
+		}
+		// If the file doesn't exist, we can just keep the go.sum nil.
+		if err != nil || fh.err != nil {
+			sumFH = nil
+		} else {
+			sumFH = fh
+		}
+	}
 	s.mu.Lock()
 	defer s.mu.Unlock()
-	s.modTidyHandle = &modHandle{
+	s.parseModHandles[modFH.URI()] = &parseModHandle{
 		handle: h,
-		file:   realfh,
-		cfg:    cfg,
+		mod:    modFH,
+		sum:    sumFH,
 	}
-	return s.modTidyHandle, nil
+	return s.parseModHandles[modFH.URI()], nil
+}
+
+func sumFilename(modURI span.URI) string {
+	return modURI.Filename()[:len(modURI.Filename())-len("mod")] + "sum"
 }
 
 // extractModParseErrors processes the raw errors returned by modfile.Parse,
 // extracting the filenames and line numbers that correspond to the errors.
-func extractModParseErrors(ctx context.Context, uri span.URI, m *protocol.ColumnMapper, parseErr error, content []byte) (source.Error, error) {
+func extractModParseErrors(uri span.URI, m *protocol.ColumnMapper, parseErr error, content []byte) (*source.Error, error) {
 	re := regexp.MustCompile(`.*:([\d]+): (.+)`)
 	matches := re.FindStringSubmatch(strings.TrimSpace(parseErr.Error()))
 	if len(matches) < 3 {
-		event.Error(ctx, "could not parse golang/x/mod error message", parseErr)
-		return source.Error{}, parseErr
+		return nil, errors.Errorf("could not parse go.mod error message: %s", parseErr)
 	}
 	line, err := strconv.Atoi(matches[1])
 	if err != nil {
-		return source.Error{}, parseErr
+		return nil, err
 	}
 	lines := strings.Split(string(content), "\n")
-	if len(lines) <= line {
-		return source.Error{}, errors.Errorf("could not parse goland/x/mod error message, line number out of range")
+	if line > len(lines) {
+		return nil, errors.Errorf("could not parse go.mod error message %q, line number %v out of range", content, line)
 	}
 	// The error returned from the modfile package only returns a line number,
 	// so we assume that the diagnostic should be for the entire line.
 	endOfLine := len(lines[line-1])
 	sOffset, err := m.Converter.ToOffset(line, 0)
 	if err != nil {
-		return source.Error{}, err
+		return nil, err
 	}
 	eOffset, err := m.Converter.ToOffset(line, endOfLine)
 	if err != nil {
-		return source.Error{}, err
+		return nil, err
 	}
 	spn := span.New(uri, span.NewPoint(line, 0, sOffset), span.NewPoint(line, endOfLine, eOffset))
 	rng, err := m.Range(spn)
 	if err != nil {
-		return source.Error{}, err
+		return nil, err
 	}
-	return source.Error{
+	return &source.Error{
 		Category: SyntaxError,
 		Message:  matches[2],
 		Range:    rng,
@@ -461,193 +171,212 @@
 	}, nil
 }
 
-// modRequireErrors extracts the errors that occur on the require directives.
-// It checks for directness issues and unused dependencies.
-func modRequireErrors(options source.Options, data *modData) ([]source.Error, error) {
-	var errors []source.Error
-	for dep, req := range data.unusedDeps {
-		if req.Syntax == nil {
-			continue
-		}
-		// Handle dependencies that are incorrectly labeled indirect and vice versa.
-		if data.missingDeps[dep] != nil && req.Indirect != data.missingDeps[dep].Indirect {
-			directErr, err := modDirectnessErrors(options, data, req)
-			if err != nil {
-				return nil, err
-			}
-			errors = append(errors, directErr)
-		}
-		// Handle unused dependencies.
-		if data.missingDeps[dep] == nil {
-			rng, err := rangeFromPositions(data.origfh.Identity().URI, data.origMapper, req.Syntax.Start, req.Syntax.End)
-			if err != nil {
-				return nil, err
-			}
-			edits, err := dropDependencyEdits(options, data, req)
-			if err != nil {
-				return nil, err
-			}
-			errors = append(errors, source.Error{
-				Category: ModTidyError,
-				Message:  fmt.Sprintf("%s is not used in this module.", dep),
-				Range:    rng,
-				URI:      data.origfh.Identity().URI,
-				SuggestedFixes: []source.SuggestedFix{{
-					Title: fmt.Sprintf("Remove dependency: %s", dep),
-					Edits: map[span.URI][]protocol.TextEdit{data.origfh.Identity().URI: edits},
-				}},
-			})
-		}
-	}
-	return errors, nil
+// modKey is uniquely identifies cached data for `go mod why` or dependencies
+// to upgrade.
+type modKey struct {
+	sessionID, cfg, mod, view string
+	verb                      modAction
 }
 
-// modDirectnessErrors extracts errors when a dependency is labeled indirect when it should be direct and vice versa.
-func modDirectnessErrors(options source.Options, data *modData, req *modfile.Require) (source.Error, error) {
-	rng, err := rangeFromPositions(data.origfh.Identity().URI, data.origMapper, req.Syntax.Start, req.Syntax.End)
-	if err != nil {
-		return source.Error{}, err
+type modAction int
+
+const (
+	why modAction = iota
+	upgrade
+)
+
+type modWhyHandle struct {
+	handle *memoize.Handle
+
+	pmh source.ParseModHandle
+}
+
+type modWhyData struct {
+	// why keeps track of the `go mod why` results for each require statement
+	// in the go.mod file.
+	why map[string]string
+
+	err error
+}
+
+func (mwh *modWhyHandle) Why(ctx context.Context) (map[string]string, error) {
+	v := mwh.handle.Get(ctx)
+	if v == nil {
+		return nil, ctx.Err()
 	}
-	if req.Indirect {
-		// If the dependency should be direct, just highlight the // indirect.
-		if comments := req.Syntax.Comment(); comments != nil && len(comments.Suffix) > 0 {
-			end := comments.Suffix[0].Start
-			end.LineRune += len(comments.Suffix[0].Token)
-			end.Byte += len([]byte(comments.Suffix[0].Token))
-			rng, err = rangeFromPositions(data.origfh.Identity().URI, data.origMapper, comments.Suffix[0].Start, end)
-			if err != nil {
-				return source.Error{}, err
-			}
-		}
-		edits, err := changeDirectnessEdits(options, data, req, false)
+	data := v.(*modWhyData)
+	return data.why, data.err
+}
+
+func (s *snapshot) ModWhyHandle(ctx context.Context) (source.ModWhyHandle, error) {
+	if err := s.awaitLoaded(ctx); err != nil {
+		return nil, err
+	}
+	fh, err := s.GetFile(ctx, s.view.modURI)
+	if err != nil {
+		return nil, err
+	}
+	pmh, err := s.ParseModHandle(ctx, fh)
+	if err != nil {
+		return nil, err
+	}
+	var (
+		cfg    = s.config(ctx)
+		tmpMod = s.view.tmpMod
+	)
+	key := modKey{
+		sessionID: s.view.session.id,
+		cfg:       hashConfig(cfg),
+		mod:       pmh.Mod().Identity().String(),
+		view:      s.view.folder.Filename(),
+		verb:      why,
+	}
+	h := s.view.session.cache.store.Bind(key, func(ctx context.Context) interface{} {
+		ctx, done := event.Start(ctx, "cache.ModHandle", tag.URI.Of(pmh.Mod().URI()))
+		defer done()
+
+		parsed, _, _, err := pmh.Parse(ctx)
 		if err != nil {
-			return source.Error{}, err
+			return &modWhyData{err: err}
 		}
-		return source.Error{
-			Category: ModTidyError,
-			Message:  fmt.Sprintf("%s should be a direct dependency.", req.Mod.Path),
-			Range:    rng,
-			URI:      data.origfh.Identity().URI,
-			SuggestedFixes: []source.SuggestedFix{{
-				Title: fmt.Sprintf("Make %s direct", req.Mod.Path),
-				Edits: map[span.URI][]protocol.TextEdit{data.origfh.Identity().URI: edits},
-			}},
-		}, nil
+		// No requires to explain.
+		if len(parsed.Require) == 0 {
+			return &modWhyData{}
+		}
+		// Run `go mod why` on all the dependencies.
+		args := []string{"why", "-m"}
+		for _, req := range parsed.Require {
+			args = append(args, req.Mod.Path)
+		}
+		_, stdout, err := runGoCommand(ctx, cfg, pmh, tmpMod, "mod", args)
+		if err != nil {
+			return &modWhyData{err: err}
+		}
+		whyList := strings.Split(stdout.String(), "\n\n")
+		if len(whyList) != len(parsed.Require) {
+			return &modWhyData{
+				err: fmt.Errorf("mismatched number of results: got %v, want %v", len(whyList), len(parsed.Require)),
+			}
+		}
+		why := make(map[string]string, len(parsed.Require))
+		for i, req := range parsed.Require {
+			why[req.Mod.Path] = whyList[i]
+		}
+		return &modWhyData{why: why}
+	})
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	s.modWhyHandle = &modWhyHandle{
+		handle: h,
+		pmh:    pmh,
 	}
-	// If the dependency should be indirect, add the // indirect.
-	edits, err := changeDirectnessEdits(options, data, req, true)
-	if err != nil {
-		return source.Error{}, err
-	}
-	return source.Error{
-		Category: ModTidyError,
-		Message:  fmt.Sprintf("%s should be an indirect dependency.", req.Mod.Path),
-		Range:    rng,
-		URI:      data.origfh.Identity().URI,
-		SuggestedFixes: []source.SuggestedFix{{
-			Title: fmt.Sprintf("Make %s indirect", req.Mod.Path),
-			Edits: map[span.URI][]protocol.TextEdit{data.origfh.Identity().URI: edits},
-		}},
-	}, nil
+	return s.modWhyHandle, nil
 }
 
-// dropDependencyEdits gets the edits needed to remove the dependency from the go.mod file.
-// As an example, this function will codify the edits needed to convert the before go.mod file to the after.
-// Before:
-// 	module t
-//
-// 	go 1.11
-//
-// 	require golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee
-// After:
-// 	module t
-//
-// 	go 1.11
-func dropDependencyEdits(options source.Options, data *modData, req *modfile.Require) ([]protocol.TextEdit, error) {
-	if err := data.origParsedFile.DropRequire(req.Mod.Path); err != nil {
-		return nil, err
-	}
-	data.origParsedFile.Cleanup()
-	newContents, err := data.origParsedFile.Format()
-	if err != nil {
-		return nil, err
-	}
-	// Reset the *modfile.File back to before we dropped the dependency.
-	data.origParsedFile.AddNewRequire(req.Mod.Path, req.Mod.Version, req.Indirect)
-	// Calculate the edits to be made due to the change.
-	diff := options.ComputeEdits(data.origfh.Identity().URI, string(data.origMapper.Content), string(newContents))
-	edits, err := source.ToProtocolEdits(data.origMapper, diff)
-	if err != nil {
-		return nil, err
-	}
-	return edits, nil
+type modUpgradeHandle struct {
+	handle *memoize.Handle
+
+	pmh source.ParseModHandle
 }
 
-// changeDirectnessEdits gets the edits needed to change an indirect dependency to direct and vice versa.
-// As an example, this function will codify the edits needed to convert the before go.mod file to the after.
-// Before:
-// 	module t
-//
-// 	go 1.11
-//
-// 	require golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee
-// After:
-// 	module t
-//
-// 	go 1.11
-//
-// 	require golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee // indirect
-func changeDirectnessEdits(options source.Options, data *modData, req *modfile.Require, indirect bool) ([]protocol.TextEdit, error) {
-	var newReq []*modfile.Require
-	prevIndirect := false
-	// Change the directness in the matching require statement.
-	for _, r := range data.origParsedFile.Require {
-		if req.Mod.Path == r.Mod.Path {
-			prevIndirect = req.Indirect
-			req.Indirect = indirect
-		}
-		newReq = append(newReq, r)
-	}
-	data.origParsedFile.SetRequire(newReq)
-	data.origParsedFile.Cleanup()
-	newContents, err := data.origParsedFile.Format()
-	if err != nil {
-		return nil, err
-	}
-	// Change the dependency back to the way it was before we got the newContents.
-	for _, r := range data.origParsedFile.Require {
-		if req.Mod.Path == r.Mod.Path {
-			req.Indirect = prevIndirect
-		}
-		newReq = append(newReq, r)
-	}
-	data.origParsedFile.SetRequire(newReq)
-	// Calculate the edits to be made due to the change.
-	diff := options.ComputeEdits(data.origfh.Identity().URI, string(data.origMapper.Content), string(newContents))
-	edits, err := source.ToProtocolEdits(data.origMapper, diff)
-	if err != nil {
-		return nil, err
-	}
-	return edits, nil
+type modUpgradeData struct {
+	// upgrades maps modules to their latest versions.
+	upgrades map[string]string
+
+	err error
 }
 
-func rangeFromPositions(uri span.URI, m *protocol.ColumnMapper, s, e modfile.Position) (protocol.Range, error) {
-	line, col, err := m.Converter.ToPosition(s.Byte)
-	if err != nil {
-		return protocol.Range{}, err
+func (muh *modUpgradeHandle) Upgrades(ctx context.Context) (map[string]string, error) {
+	v := muh.handle.Get(ctx)
+	if v == nil {
+		return nil, ctx.Err()
 	}
-	start := span.NewPoint(line, col, s.Byte)
+	data := v.(*modUpgradeData)
+	return data.upgrades, data.err
+}
 
-	line, col, err = m.Converter.ToPosition(e.Byte)
-	if err != nil {
-		return protocol.Range{}, err
+func (s *snapshot) ModUpgradeHandle(ctx context.Context) (source.ModUpgradeHandle, error) {
+	if err := s.awaitLoaded(ctx); err != nil {
+		return nil, err
 	}
-	end := span.NewPoint(line, col, e.Byte)
+	fh, err := s.GetFile(ctx, s.view.modURI)
+	if err != nil {
+		return nil, err
+	}
+	pmh, err := s.ParseModHandle(ctx, fh)
+	if err != nil {
+		return nil, err
+	}
+	var (
+		cfg    = s.config(ctx)
+		tmpMod = s.view.tmpMod
+	)
+	key := modKey{
+		sessionID: s.view.session.id,
+		cfg:       hashConfig(cfg),
+		mod:       pmh.Mod().Identity().String(),
+		view:      s.view.folder.Filename(),
+		verb:      upgrade,
+	}
+	h := s.view.session.cache.store.Bind(key, func(ctx context.Context) interface{} {
+		ctx, done := event.Start(ctx, "cache.ModUpgradeHandle", tag.URI.Of(pmh.Mod().URI()))
+		defer done()
 
-	spn := span.New(uri, start, end)
-	rng, err := m.Range(spn)
-	if err != nil {
-		return protocol.Range{}, err
+		parsed, _, _, err := pmh.Parse(ctx)
+		if err != nil {
+			return &modUpgradeData{err: err}
+		}
+		// No requires to upgrade.
+		if len(parsed.Require) == 0 {
+			return &modUpgradeData{}
+		}
+		// Run "go list -mod readonly -u -m all" to be able to see which deps can be
+		// upgraded without modifying mod file.
+		args := []string{"-u", "-m", "all"}
+		if !tmpMod || containsVendor(pmh.Mod().URI()) {
+			// Use -mod=readonly if the module contains a vendor directory
+			// (see golang/go#38711).
+			args = append([]string{"-mod", "readonly"}, args...)
+		}
+		_, stdout, err := runGoCommand(ctx, cfg, pmh, tmpMod, "list", args)
+		if err != nil {
+			return &modUpgradeData{err: err}
+		}
+		upgradesList := strings.Split(stdout.String(), "\n")
+		if len(upgradesList) <= 1 {
+			return nil
+		}
+		upgrades := make(map[string]string)
+		for _, upgrade := range upgradesList[1:] {
+			// Example: "github.com/x/tools v1.1.0 [v1.2.0]"
+			info := strings.Split(upgrade, " ")
+			if len(info) < 3 {
+				continue
+			}
+			dep, version := info[0], info[2]
+			latest := version[1:]                    // remove the "["
+			latest = strings.TrimSuffix(latest, "]") // remove the "]"
+			upgrades[dep] = latest
+		}
+		return &modUpgradeData{
+			upgrades: upgrades,
+		}
+	})
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	s.modUpgradeHandle = &modUpgradeHandle{
+		handle: h,
+		pmh:    pmh,
 	}
-	return rng, nil
+	return s.modUpgradeHandle, nil
+}
+
+// containsVendor reports whether the module has a vendor folder.
+func containsVendor(modURI span.URI) bool {
+	dir := filepath.Dir(modURI.Filename())
+	f, err := os.Stat(filepath.Join(dir, "vendor"))
+	if err != nil {
+		return false
+	}
+	return f.IsDir()
 }
diff --git a/internal/lsp/cache/mod_tidy.go b/internal/lsp/cache/mod_tidy.go
new file mode 100644
index 0000000..485b16b
--- /dev/null
+++ b/internal/lsp/cache/mod_tidy.go
@@ -0,0 +1,373 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cache
+
+import (
+	"context"
+	"fmt"
+	"io/ioutil"
+
+	"golang.org/x/mod/modfile"
+	"golang.org/x/tools/internal/event"
+	"golang.org/x/tools/internal/lsp/debug/tag"
+	"golang.org/x/tools/internal/lsp/protocol"
+	"golang.org/x/tools/internal/lsp/source"
+	"golang.org/x/tools/internal/memoize"
+	"golang.org/x/tools/internal/packagesinternal"
+	"golang.org/x/tools/internal/span"
+)
+
+type modTidyKey struct {
+	sessionID       string
+	cfg             string
+	gomod           string
+	imports         string
+	unsavedOverlays string
+	view            string
+}
+
+type modTidyHandle struct {
+	handle *memoize.Handle
+
+	pmh source.ParseModHandle
+}
+
+type modTidyData struct {
+	memoize.NoCopy
+
+	// missingDeps contains dependencies that should be added to the view's
+	// go.mod file.
+	missingDeps map[string]*modfile.Require
+
+	// diagnostics are any errors and associated suggested fixes for
+	// the go.mod file.
+	diagnostics []source.Error
+
+	err error
+}
+
+func (mth *modTidyHandle) Tidy(ctx context.Context) (map[string]*modfile.Require, []source.Error, error) {
+	v := mth.handle.Get(ctx)
+	if v == nil {
+		return nil, nil, ctx.Err()
+	}
+	data := v.(*modTidyData)
+	return data.missingDeps, data.diagnostics, data.err
+}
+
+func (s *snapshot) ModTidyHandle(ctx context.Context) (source.ModTidyHandle, error) {
+	if !s.view.tmpMod {
+		return nil, source.ErrTmpModfileUnsupported
+	}
+	if handle := s.getModTidyHandle(); handle != nil {
+		return handle, nil
+	}
+	fh, err := s.GetFile(ctx, s.view.modURI)
+	if err != nil {
+		return nil, err
+	}
+	pmh, err := s.ParseModHandle(ctx, fh)
+	if err != nil {
+		return nil, err
+	}
+	wsPackages, err := s.WorkspacePackages(ctx)
+	if ctx.Err() != nil {
+		return nil, ctx.Err()
+	}
+	if err != nil {
+		return nil, err
+	}
+	imports, err := hashImports(ctx, wsPackages)
+	if err != nil {
+		return nil, err
+	}
+
+	s.mu.Lock()
+	overlayHash := hashUnsavedOverlays(s.files)
+	s.mu.Unlock()
+
+	var (
+		folder  = s.View().Folder()
+		modURI  = s.view.modURI
+		cfg     = s.config(ctx)
+		options = s.view.Options()
+	)
+	key := modTidyKey{
+		sessionID:       s.view.session.id,
+		view:            folder.Filename(),
+		imports:         imports,
+		unsavedOverlays: overlayHash,
+		gomod:           pmh.Mod().Identity().String(),
+		cfg:             hashConfig(cfg),
+	}
+	h := s.view.session.cache.store.Bind(key, func(ctx context.Context) interface{} {
+		ctx, done := event.Start(ctx, "cache.ModTidyHandle", tag.URI.Of(modURI))
+		defer done()
+
+		original, m, parseErrors, err := pmh.Parse(ctx)
+		if err != nil || len(parseErrors) > 0 {
+			return &modTidyData{
+				diagnostics: parseErrors,
+				err:         err,
+			}
+		}
+		tmpURI, inv, cleanup, err := goCommandInvocation(ctx, cfg, pmh, "mod", []string{"tidy"})
+		if err != nil {
+			return &modTidyData{err: err}
+		}
+		// Keep the temporary go.mod file around long enough to parse it.
+		defer cleanup()
+
+		if _, err := packagesinternal.GetGoCmdRunner(cfg).Run(ctx, *inv); err != nil {
+			return &modTidyData{err: err}
+		}
+		// Go directly to disk to get the temporary mod file, since it is
+		// always on disk.
+		tempContents, err := ioutil.ReadFile(tmpURI.Filename())
+		if err != nil {
+			return &modTidyData{err: err}
+		}
+		ideal, err := modfile.Parse(tmpURI.Filename(), tempContents, nil)
+		if err != nil {
+			// We do not need to worry about the temporary file's parse errors
+			// since it has been "tidied".
+			return &modTidyData{err: err}
+		}
+		// Get the dependencies that are different between the original and
+		// ideal go.mod files.
+		unusedDeps := make(map[string]*modfile.Require, len(original.Require))
+		missingDeps := make(map[string]*modfile.Require, len(ideal.Require))
+		for _, req := range original.Require {
+			unusedDeps[req.Mod.Path] = req
+		}
+		for _, req := range ideal.Require {
+			origDep := unusedDeps[req.Mod.Path]
+			if origDep != nil && origDep.Indirect == req.Indirect {
+				delete(unusedDeps, req.Mod.Path)
+			} else {
+				missingDeps[req.Mod.Path] = req
+			}
+		}
+		diagnostics, err := modRequireErrors(pmh.Mod().URI(), original, m, missingDeps, unusedDeps, options)
+		if err != nil {
+			return &modTidyData{err: err}
+		}
+		for _, req := range missingDeps {
+			if unusedDeps[req.Mod.Path] != nil {
+				delete(missingDeps, req.Mod.Path)
+			}
+		}
+		return &modTidyData{
+			missingDeps: missingDeps,
+			diagnostics: diagnostics,
+		}
+	})
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	s.modTidyHandle = &modTidyHandle{
+		handle: h,
+		pmh:    pmh,
+	}
+	return s.modTidyHandle, nil
+}
+
+// modRequireErrors extracts the errors that occur on the require directives.
+// It checks for directness issues and unused dependencies.
+func modRequireErrors(uri span.URI, parsed *modfile.File, m *protocol.ColumnMapper, missingDeps, unusedDeps map[string]*modfile.Require, options source.Options) ([]source.Error, error) {
+	var errors []source.Error
+	for dep, req := range unusedDeps {
+		if req.Syntax == nil {
+			continue
+		}
+		// Handle dependencies that are incorrectly labeled indirect and vice versa.
+		if missingDeps[dep] != nil && req.Indirect != missingDeps[dep].Indirect {
+			directErr, err := modDirectnessErrors(uri, parsed, m, req, options)
+			if err != nil {
+				return nil, err
+			}
+			errors = append(errors, directErr)
+		}
+		// Handle unused dependencies.
+		if missingDeps[dep] == nil {
+			rng, err := rangeFromPositions(uri, m, req.Syntax.Start, req.Syntax.End)
+			if err != nil {
+				return nil, err
+			}
+			edits, err := dropDependencyEdits(uri, parsed, m, req, options)
+			if err != nil {
+				return nil, err
+			}
+			errors = append(errors, source.Error{
+				Category: ModTidyError,
+				Message:  fmt.Sprintf("%s is not used in this module.", dep),
+				Range:    rng,
+				URI:      uri,
+				SuggestedFixes: []source.SuggestedFix{{
+					Title: fmt.Sprintf("Remove dependency: %s", dep),
+					Edits: map[span.URI][]protocol.TextEdit{
+						uri: edits,
+					},
+				}},
+			})
+		}
+	}
+	return errors, nil
+}
+
+const ModTidyError = "go mod tidy"
+
+// modDirectnessErrors extracts errors when a dependency is labeled indirect when it should be direct and vice versa.
+func modDirectnessErrors(uri span.URI, parsed *modfile.File, m *protocol.ColumnMapper, req *modfile.Require, options source.Options) (source.Error, error) {
+	rng, err := rangeFromPositions(uri, m, req.Syntax.Start, req.Syntax.End)
+	if err != nil {
+		return source.Error{}, err
+	}
+	if req.Indirect {
+		// If the dependency should be direct, just highlight the // indirect.
+		if comments := req.Syntax.Comment(); comments != nil && len(comments.Suffix) > 0 {
+			end := comments.Suffix[0].Start
+			end.LineRune += len(comments.Suffix[0].Token)
+			end.Byte += len([]byte(comments.Suffix[0].Token))
+			rng, err = rangeFromPositions(uri, m, comments.Suffix[0].Start, end)
+			if err != nil {
+				return source.Error{}, err
+			}
+		}
+		edits, err := changeDirectnessEdits(uri, parsed, m, req, false, options)
+		if err != nil {
+			return source.Error{}, err
+		}
+		return source.Error{
+			Category: ModTidyError,
+			Message:  fmt.Sprintf("%s should be a direct dependency.", req.Mod.Path),
+			Range:    rng,
+			URI:      uri,
+			SuggestedFixes: []source.SuggestedFix{{
+				Title: fmt.Sprintf("Make %s direct", req.Mod.Path),
+				Edits: map[span.URI][]protocol.TextEdit{
+					uri: edits,
+				},
+			}},
+		}, nil
+	}
+	// If the dependency should be indirect, add the // indirect.
+	edits, err := changeDirectnessEdits(uri, parsed, m, req, true, options)
+	if err != nil {
+		return source.Error{}, err
+	}
+	return source.Error{
+		Category: ModTidyError,
+		Message:  fmt.Sprintf("%s should be an indirect dependency.", req.Mod.Path),
+		Range:    rng,
+		URI:      uri,
+		SuggestedFixes: []source.SuggestedFix{{
+			Title: fmt.Sprintf("Make %s indirect", req.Mod.Path),
+			Edits: map[span.URI][]protocol.TextEdit{
+				uri: edits,
+			},
+		}},
+	}, nil
+}
+
+// dropDependencyEdits gets the edits needed to remove the dependency from the go.mod file.
+// As an example, this function will codify the edits needed to convert the before go.mod file to the after.
+// Before:
+// 	module t
+//
+// 	go 1.11
+//
+// 	require golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee
+// After:
+// 	module t
+//
+// 	go 1.11
+func dropDependencyEdits(uri span.URI, parsed *modfile.File, m *protocol.ColumnMapper, req *modfile.Require, options source.Options) ([]protocol.TextEdit, error) {
+	if err := parsed.DropRequire(req.Mod.Path); err != nil {
+		return nil, err
+	}
+	parsed.Cleanup()
+	newContents, err := parsed.Format()
+	if err != nil {
+		return nil, err
+	}
+	// Reset the *modfile.File back to before we dropped the dependency.
+	parsed.AddNewRequire(req.Mod.Path, req.Mod.Version, req.Indirect)
+	// Calculate the edits to be made due to the change.
+	diff := options.ComputeEdits(uri, string(m.Content), string(newContents))
+	edits, err := source.ToProtocolEdits(m, diff)
+	if err != nil {
+		return nil, err
+	}
+	return edits, nil
+}
+
+// changeDirectnessEdits gets the edits needed to change an indirect dependency to direct and vice versa.
+// As an example, this function will codify the edits needed to convert the before go.mod file to the after.
+// Before:
+// 	module t
+//
+// 	go 1.11
+//
+// 	require golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee
+// After:
+// 	module t
+//
+// 	go 1.11
+//
+// 	require golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee // indirect
+func changeDirectnessEdits(uri span.URI, parsed *modfile.File, m *protocol.ColumnMapper, req *modfile.Require, indirect bool, options source.Options) ([]protocol.TextEdit, error) {
+	var newReq []*modfile.Require
+	prevIndirect := false
+	// Change the directness in the matching require statement.
+	for _, r := range parsed.Require {
+		if req.Mod.Path == r.Mod.Path {
+			prevIndirect = req.Indirect
+			req.Indirect = indirect
+		}
+		newReq = append(newReq, r)
+	}
+	parsed.SetRequire(newReq)
+	parsed.Cleanup()
+	newContents, err := parsed.Format()
+	if err != nil {
+		return nil, err
+	}
+	// Change the dependency back to the way it was before we got the newContents.
+	for _, r := range parsed.Require {
+		if req.Mod.Path == r.Mod.Path {
+			req.Indirect = prevIndirect
+		}
+		newReq = append(newReq, r)
+	}
+	parsed.SetRequire(newReq)
+	// Calculate the edits to be made due to the change.
+	diff := options.ComputeEdits(uri, string(m.Content), string(newContents))
+	edits, err := source.ToProtocolEdits(m, diff)
+	if err != nil {
+		return nil, err
+	}
+	return edits, nil
+}
+
+func rangeFromPositions(uri span.URI, m *protocol.ColumnMapper, s, e modfile.Position) (protocol.Range, error) {
+	line, col, err := m.Converter.ToPosition(s.Byte)
+	if err != nil {
+		return protocol.Range{}, err
+	}
+	start := span.NewPoint(line, col, s.Byte)
+
+	line, col, err = m.Converter.ToPosition(e.Byte)
+	if err != nil {
+		return protocol.Range{}, err
+	}
+	end := span.NewPoint(line, col, e.Byte)
+
+	spn := span.New(uri, start, end)
+	rng, err := m.Range(spn)
+	if err != nil {
+		return protocol.Range{}, err
+	}
+	return rng, nil
+}
diff --git a/internal/lsp/cache/parse.go b/internal/lsp/cache/parse.go
index 81ef3c7..46aea45 100644
--- a/internal/lsp/cache/parse.go
+++ b/internal/lsp/cache/parse.go
@@ -50,11 +50,11 @@
 	err        error // any other errors
 }
 
-func (c *Cache) ParseGoHandle(fh source.FileHandle, mode source.ParseMode) source.ParseGoHandle {
-	return c.parseGoHandle(fh, mode)
+func (c *Cache) ParseGoHandle(ctx context.Context, fh source.FileHandle, mode source.ParseMode) source.ParseGoHandle {
+	return c.parseGoHandle(ctx, fh, mode)
 }
 
-func (c *Cache) parseGoHandle(fh source.FileHandle, mode source.ParseMode) *parseGoHandle {
+func (c *Cache) parseGoHandle(ctx context.Context, fh source.FileHandle, mode source.ParseMode) *parseGoHandle {
 	key := parseKey{
 		file: fh.Identity(),
 		mode: mode,
@@ -71,7 +71,7 @@
 }
 
 func (pgh *parseGoHandle) String() string {
-	return pgh.File().Identity().URI.Filename()
+	return pgh.File().URI().Filename()
 }
 
 func (pgh *parseGoHandle) File() source.FileHandle {
@@ -94,7 +94,7 @@
 	v := pgh.handle.Get(ctx)
 	data, ok := v.(*parseGoData)
 	if !ok {
-		return nil, errors.Errorf("no parsed file for %s", pgh.File().Identity().URI)
+		return nil, errors.Errorf("no parsed file for %s", pgh.File().URI())
 	}
 	return data, nil
 }
@@ -102,35 +102,29 @@
 func (pgh *parseGoHandle) Cached() (*ast.File, []byte, *protocol.ColumnMapper, error, error) {
 	v := pgh.handle.Cached()
 	if v == nil {
-		return nil, nil, nil, nil, errors.Errorf("no cached AST for %s", pgh.file.Identity().URI)
+		return nil, nil, nil, nil, errors.Errorf("no cached AST for %s", pgh.file.URI())
 	}
 	data := v.(*parseGoData)
 	return data.ast, data.src, data.mapper, data.parseError, data.err
 }
 
-func hashParseKey(ph source.ParseGoHandle) string {
+func hashParseKeys(pghs []*parseGoHandle) string {
 	b := bytes.NewBuffer(nil)
-	b.WriteString(ph.File().Identity().String())
-	b.WriteString(string(rune(ph.Mode())))
-	return hashContents(b.Bytes())
-}
-
-func hashParseKeys(phs []*parseGoHandle) string {
-	b := bytes.NewBuffer(nil)
-	for _, ph := range phs {
-		b.WriteString(hashParseKey(ph))
+	for _, pgh := range pghs {
+		b.WriteString(pgh.file.Identity().String())
+		b.WriteByte(byte(pgh.Mode()))
 	}
 	return hashContents(b.Bytes())
 }
 
 func parseGo(ctx context.Context, fset *token.FileSet, fh source.FileHandle, mode source.ParseMode) *parseGoData {
-	ctx, done := event.Start(ctx, "cache.parseGo", tag.File.Of(fh.Identity().URI.Filename()))
+	ctx, done := event.Start(ctx, "cache.parseGo", tag.File.Of(fh.URI().Filename()))
 	defer done()
 
-	if fh.Identity().Kind != source.Go {
-		return &parseGoData{err: errors.Errorf("cannot parse non-Go file %s", fh.Identity().URI)}
+	if fh.Kind() != source.Go {
+		return &parseGoData{err: errors.Errorf("cannot parse non-Go file %s", fh.URI())}
 	}
-	buf, _, err := fh.Read(ctx)
+	buf, err := fh.Read()
 	if err != nil {
 		return &parseGoData{err: err}
 	}
@@ -139,13 +133,13 @@
 	if mode == source.ParseHeader {
 		parserMode = parser.ImportsOnly | parser.ParseComments
 	}
-	file, parseError := parser.ParseFile(fset, fh.Identity().URI.Filename(), buf, parserMode)
+	file, parseError := parser.ParseFile(fset, fh.URI().Filename(), buf, parserMode)
 	var tok *token.File
 	var fixed bool
 	if file != nil {
 		tok = fset.File(file.Pos())
 		if tok == nil {
-			return &parseGoData{err: errors.Errorf("successfully parsed but no token.File for %s (%v)", fh.Identity().URI, parseError)}
+			return &parseGoData{err: errors.Errorf("successfully parsed but no token.File for %s (%v)", fh.URI(), parseError)}
 		}
 
 		// Fix any badly parsed parts of the AST.
@@ -154,7 +148,7 @@
 		// Fix certain syntax errors that render the file unparseable.
 		newSrc := fixSrc(file, tok, buf)
 		if newSrc != nil {
-			newFile, _ := parser.ParseFile(fset, fh.Identity().URI.Filename(), newSrc, parserMode)
+			newFile, _ := parser.ParseFile(fset, fh.URI().Filename(), newSrc, parserMode)
 			if newFile != nil {
 				// Maintain the original parseError so we don't try formatting the doctored file.
 				file = newFile
@@ -174,12 +168,12 @@
 		// the parse errors are the actual errors.
 		err := parseError
 		if err == nil {
-			err = errors.Errorf("no AST for %s", fh.Identity().URI)
+			err = errors.Errorf("no AST for %s", fh.URI())
 		}
 		return &parseGoData{parseError: parseError, err: err}
 	}
 	m := &protocol.ColumnMapper{
-		URI:       fh.Identity().URI,
+		URI:       fh.URI(),
 		Converter: span.NewTokenConverter(fset, tok),
 		Content:   buf,
 	}
diff --git a/internal/lsp/cache/pkg.go b/internal/lsp/cache/pkg.go
index b75cd8a..bbfa04a1 100644
--- a/internal/lsp/cache/pkg.go
+++ b/internal/lsp/cache/pkg.go
@@ -8,8 +8,8 @@
 	"go/ast"
 	"go/types"
 
+	"golang.org/x/tools/go/packages"
 	"golang.org/x/tools/internal/lsp/source"
-	"golang.org/x/tools/internal/packagesinternal"
 	"golang.org/x/tools/internal/span"
 	errors "golang.org/x/xerrors"
 )
@@ -25,7 +25,7 @@
 	compiledGoFiles []*parseGoHandle
 	errors          []*source.Error
 	imports         map[packagePath]*pkg
-	module          *packagesinternal.Module
+	module          *packages.Module
 	typeErrors      []types.Error
 	types           *types.Package
 	typesInfo       *types.Info
@@ -61,12 +61,12 @@
 
 func (p *pkg) File(uri span.URI) (source.ParseGoHandle, error) {
 	for _, ph := range p.compiledGoFiles {
-		if ph.File().Identity().URI == uri {
+		if ph.File().URI() == uri {
 			return ph, nil
 		}
 	}
 	for _, ph := range p.goFiles {
-		if ph.File().Identity().URI == uri {
+		if ph.File().URI() == uri {
 			return ph, nil
 		}
 	}
@@ -124,6 +124,6 @@
 	return result
 }
 
-func (p *pkg) Module() *packagesinternal.Module {
+func (p *pkg) Module() *packages.Module {
 	return p.module
 }
diff --git a/internal/lsp/cache/session.go b/internal/lsp/cache/session.go
index 3da975d..527f54d 100644
--- a/internal/lsp/cache/session.go
+++ b/internal/lsp/cache/session.go
@@ -13,7 +13,6 @@
 
 	"golang.org/x/tools/internal/event"
 	"golang.org/x/tools/internal/gocommand"
-	"golang.org/x/tools/internal/lsp/debug"
 	"golang.org/x/tools/internal/lsp/source"
 	"golang.org/x/tools/internal/span"
 	"golang.org/x/tools/internal/xcontext"
@@ -27,8 +26,8 @@
 	options source.Options
 
 	viewMu  sync.Mutex
-	views   []*view
-	viewMap map[span.URI]*view
+	views   []*View
+	viewMap map[span.URI]*View
 
 	overlayMu sync.Mutex
 	overlays  map[span.URI]*overlay
@@ -47,8 +46,8 @@
 	saved bool
 }
 
-func (o *overlay) FileSystem() source.FileSystem {
-	return o.session
+func (o *overlay) Read() ([]byte, error) {
+	return o.text, nil
 }
 
 func (o *overlay) Identity() source.FileIdentity {
@@ -60,10 +59,26 @@
 		Kind:       o.kind,
 	}
 }
-func (o *overlay) Read(ctx context.Context) ([]byte, string, error) {
-	return o.text, o.hash, nil
+
+func (o *overlay) Kind() source.FileKind {
+	return o.kind
 }
 
+func (o *overlay) URI() span.URI {
+	return o.uri
+}
+
+func (o *overlay) Version() float64 {
+	return o.version
+}
+
+func (o *overlay) Session() source.Session { return o.session }
+func (o *overlay) Saved() bool             { return o.saved }
+func (o *overlay) Data() []byte            { return o.text }
+
+func (s *Session) ID() string     { return s.id }
+func (s *Session) String() string { return s.id }
+
 func (s *Session) Options() source.Options {
 	return s.options
 }
@@ -80,9 +95,7 @@
 	}
 	s.views = nil
 	s.viewMap = nil
-	if di := debug.GetInstance(ctx); di != nil {
-		di.State.DropSession(DebugSession{s})
-	}
+	event.Log(ctx, "Shutdown session", KeyShutdownSession.Of(s))
 }
 
 func (s *Session) Cache() source.Cache {
@@ -98,18 +111,18 @@
 	}
 	s.views = append(s.views, v)
 	// we always need to drop the view map
-	s.viewMap = make(map[span.URI]*view)
+	s.viewMap = make(map[span.URI]*View)
 	return v, snapshot, nil
 }
 
-func (s *Session) createView(ctx context.Context, name string, folder span.URI, options source.Options, snapshotID uint64) (*view, *snapshot, error) {
+func (s *Session) createView(ctx context.Context, name string, folder span.URI, options source.Options, snapshotID uint64) (*View, *snapshot, error) {
 	index := atomic.AddInt64(&viewIndex, 1)
 	// We want a true background context and not a detached context here
 	// the spans need to be unrelated and no tag values should pollute it.
 	baseCtx := event.Detach(xcontext.Detach(ctx))
 	backgroundCtx, cancel := context.WithCancel(baseCtx)
 
-	v := &view{
+	v := &View{
 		session:       s,
 		initialized:   make(chan struct{}),
 		id:            strconv.FormatInt(index, 10),
@@ -131,9 +144,8 @@
 			actions:           make(map[actionKey]*actionHandle),
 			workspacePackages: make(map[packageID]packagePath),
 			unloadableFiles:   make(map[span.URI]struct{}),
-			modHandles:        make(map[span.URI]*modHandle),
+			parseModHandles:   make(map[span.URI]*parseModHandle),
 		},
-		ignoredURIs: make(map[span.URI]struct{}),
 		gocmdRunner: &gocommand.Runner{},
 	}
 	v.snapshot.view = v
@@ -147,11 +159,9 @@
 	}
 
 	// Initialize the view without blocking.
-	go v.initialize(xcontext.Detach(ctx), v.snapshot)
-
-	if di := debug.GetInstance(ctx); di != nil {
-		di.State.AddView(debugView{v})
-	}
+	initCtx, initCancel := context.WithCancel(xcontext.Detach(ctx))
+	v.initCancel = initCancel
+	go v.initialize(initCtx, v.snapshot)
 	return v, v.snapshot, nil
 }
 
@@ -173,7 +183,7 @@
 	return s.viewOf(uri)
 }
 
-func (s *Session) viewOf(uri span.URI) (*view, error) {
+func (s *Session) viewOf(uri span.URI) (*View, error) {
 	s.viewMu.Lock()
 	defer s.viewMu.Unlock()
 
@@ -190,11 +200,11 @@
 	return v, nil
 }
 
-func (s *Session) viewsOf(uri span.URI) []*view {
+func (s *Session) viewsOf(uri span.URI) []*View {
 	s.viewMu.Lock()
 	defer s.viewMu.Unlock()
 
-	var views []*view
+	var views []*View
 	for _, view := range s.views {
 		if strings.HasPrefix(string(uri), string(view.Folder())) {
 			views = append(views, view)
@@ -215,12 +225,12 @@
 
 // bestView finds the best view to associate a given URI with.
 // viewMu must be held when calling this method.
-func (s *Session) bestView(uri span.URI) (*view, error) {
+func (s *Session) bestView(uri span.URI) (*View, error) {
 	if len(s.views) == 0 {
 		return nil, errors.Errorf("no views in the session")
 	}
 	// we need to find the best view for this file
-	var longest *view
+	var longest *View
 	for _, view := range s.views {
 		if longest != nil && len(longest.Folder()) > len(view.Folder()) {
 			continue
@@ -236,7 +246,7 @@
 	return s.views[0], nil
 }
 
-func (s *Session) removeView(ctx context.Context, view *view) error {
+func (s *Session) removeView(ctx context.Context, view *View) error {
 	s.viewMu.Lock()
 	defer s.viewMu.Unlock()
 	i, err := s.dropView(ctx, view)
@@ -251,7 +261,7 @@
 	return nil
 }
 
-func (s *Session) updateView(ctx context.Context, view *view, options source.Options) (*view, *snapshot, error) {
+func (s *Session) updateView(ctx context.Context, view *View, options source.Options) (*View, *snapshot, error) {
 	s.viewMu.Lock()
 	defer s.viewMu.Unlock()
 	i, err := s.dropView(ctx, view)
@@ -276,9 +286,9 @@
 	return v, snapshot, nil
 }
 
-func (s *Session) dropView(ctx context.Context, v *view) (int, error) {
+func (s *Session) dropView(ctx context.Context, v *View) (int, error) {
 	// we always need to drop the view map
-	s.viewMap = make(map[span.URI]*view)
+	s.viewMap = make(map[span.URI]*View)
 	for i := range s.views {
 		if v == s.views[i] {
 			// we found the view, drop it and return the index it was found at
@@ -291,13 +301,17 @@
 }
 
 func (s *Session) DidModifyFiles(ctx context.Context, changes []source.FileModification) ([]source.Snapshot, error) {
-	views := make(map[*view]map[span.URI]source.FileHandle)
+	views := make(map[*View]map[span.URI]source.FileHandle)
 
 	overlays, err := s.updateOverlays(ctx, changes)
 	if err != nil {
 		return nil, err
 	}
+	forceReloadMetadata := false
 	for _, c := range changes {
+		if c.Action == source.InvalidateMetadata {
+			forceReloadMetadata = true
+		}
 		// Do nothing if the file is open in the editor and we receive
 		// an on-disk action. The editor is the source of truth.
 		if s.isOpen(c.URI) && c.OnDisk {
@@ -306,9 +320,6 @@
 		// Look through all of the session's views, invalidating the file for
 		// all of the views to which it is known.
 		for _, view := range s.views {
-			if view.Ignore(c.URI) {
-				return nil, errors.Errorf("ignored file %v", c.URI)
-			}
 			// Don't propagate changes that are outside of the view's scope
 			// or knowledge.
 			if !view.relevantChange(c) {
@@ -324,13 +335,17 @@
 			if o, ok := overlays[c.URI]; ok {
 				views[view][c.URI] = o
 			} else {
-				views[view][c.URI] = s.cache.GetFile(c.URI)
+				fh, err := s.cache.getFile(ctx, c.URI)
+				if err != nil {
+					return nil, err
+				}
+				views[view][c.URI] = fh
 			}
 		}
 	}
 	var snapshots []source.Snapshot
 	for view, uris := range views {
-		snapshots = append(snapshots, view.invalidateContent(ctx, uris))
+		snapshots = append(snapshots, view.invalidateContent(ctx, uris, forceReloadMetadata))
 	}
 	return snapshots, nil
 }
@@ -348,8 +363,8 @@
 	defer s.overlayMu.Unlock()
 
 	for _, c := range changes {
-		// Don't update overlays for on-disk changes.
-		if c.OnDisk {
+		// Don't update overlays for on-disk changes or metadata invalidations.
+		if c.OnDisk || c.Action == source.InvalidateMetadata {
 			continue
 		}
 
@@ -385,8 +400,12 @@
 		var sameContentOnDisk bool
 		switch c.Action {
 		case source.Open:
-			_, h, err := s.cache.GetFile(c.URI).Read(ctx)
-			sameContentOnDisk = (err == nil && h == hash)
+			fh, err := s.cache.getFile(ctx, c.URI)
+			if err != nil {
+				return nil, err
+			}
+			_, readErr := fh.Read()
+			sameContentOnDisk = (readErr == nil && fh.Identity().Identifier == hash)
 		case source.Save:
 			// Make sure the version and content (if present) is the same.
 			if o.version != c.Version {
@@ -419,13 +438,12 @@
 	return overlays, nil
 }
 
-// GetFile implements the source.FileSystem interface.
-func (s *Session) GetFile(uri span.URI) source.FileHandle {
+func (s *Session) GetFile(ctx context.Context, uri span.URI) (source.FileHandle, error) {
 	if overlay := s.readOverlay(uri); overlay != nil {
-		return overlay
+		return overlay, nil
 	}
 	// Fall back to the cache-level file system.
-	return s.cache.GetFile(uri)
+	return s.cache.getFile(ctx, uri)
 }
 
 func (s *Session) readOverlay(uri span.URI) *overlay {
@@ -438,15 +456,13 @@
 	return nil
 }
 
-func (s *Session) UnsavedFiles() []span.URI {
+func (s *Session) Overlays() []source.Overlay {
 	s.overlayMu.Lock()
 	defer s.overlayMu.Unlock()
 
-	var unsaved []span.URI
-	for uri, overlay := range s.overlays {
-		if !overlay.saved {
-			unsaved = append(unsaved, uri)
-		}
+	overlays := make([]source.Overlay, 0, len(s.overlays))
+	for _, overlay := range s.overlays {
+		overlays = append(overlays, overlay)
 	}
-	return unsaved
+	return overlays
 }
diff --git a/internal/lsp/cache/snapshot.go b/internal/lsp/cache/snapshot.go
index df90d18..3faa7e1 100644
--- a/internal/lsp/cache/snapshot.go
+++ b/internal/lsp/cache/snapshot.go
@@ -5,10 +5,13 @@
 package cache
 
 import (
+	"bytes"
 	"context"
 	"fmt"
 	"go/ast"
 	"go/token"
+	"go/types"
+	"io"
 	"os"
 	"path/filepath"
 	"sort"
@@ -18,16 +21,18 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/packages"
 	"golang.org/x/tools/internal/event"
+	"golang.org/x/tools/internal/gocommand"
 	"golang.org/x/tools/internal/lsp/debug/tag"
 	"golang.org/x/tools/internal/lsp/source"
 	"golang.org/x/tools/internal/packagesinternal"
 	"golang.org/x/tools/internal/span"
+	"golang.org/x/tools/internal/typesinternal"
 	errors "golang.org/x/xerrors"
 )
 
 type snapshot struct {
 	id   uint64
-	view *view
+	view *View
 
 	// mu guards all of the maps in the snapshot.
 	mu sync.Mutex
@@ -61,12 +66,18 @@
 	// unloadableFiles keeps track of files that we've failed to load.
 	unloadableFiles map[span.URI]struct{}
 
-	// modHandles keeps track of any ParseModHandles for this snapshot.
-	modHandles map[span.URI]*modHandle
+	// parseModHandles keeps track of any ParseModHandles for the snapshot.
+	// The handles need not refer to only the view's go.mod file.
+	parseModHandles map[span.URI]*parseModHandle
 
-	// modTidyHandle is the saved modTidyHandle for this snapshot, it is attached to the
-	// snapshot so we can reuse it without having to call "go mod tidy" everytime.
-	modTidyHandle *modHandle
+	// Preserve go.mod-related handles to avoid garbage-collecting the results
+	// of various calls to the go command.
+	//
+	// TODO(rstambler): If we end up with any more such handles, we should
+	// consider creating a struct for them.
+	modTidyHandle    *modTidyHandle
+	modWhyHandle     *modWhyHandle
+	modUpgradeHandle *modUpgradeHandle
 }
 
 type packageKey struct {
@@ -87,9 +98,9 @@
 	return s.view
 }
 
-// Config returns the configuration used for the snapshot's interaction with the
+// config returns the configuration used for the snapshot's interaction with the
 // go/packages API.
-func (s *snapshot) Config(ctx context.Context) *packages.Config {
+func (s *snapshot) config(ctx context.Context) *packages.Config {
 	s.view.optionsMu.Lock()
 	env, buildFlags := s.view.envLocked()
 	verboseOutput := s.view.options.VerboseOutput
@@ -105,7 +116,8 @@
 			packages.NeedCompiledGoFiles |
 			packages.NeedImports |
 			packages.NeedDeps |
-			packages.NeedTypesSizes,
+			packages.NeedTypesSizes |
+			packages.NeedModule,
 		Fset:    s.view.session.cache.fset,
 		Overlay: s.buildOverlay(),
 		ParseFile: func(*token.FileSet, string, []byte) (*ast.File, error) {
@@ -118,11 +130,101 @@
 		},
 		Tests: true,
 	}
+	// We want to type check cgo code if go/types supports it.
+	if typesinternal.SetUsesCgo(&types.Config{}) {
+		cfg.Mode |= packages.LoadMode(packagesinternal.TypecheckCgo)
+	}
 	packagesinternal.SetGoCmdRunner(cfg, s.view.gocmdRunner)
 
 	return cfg
 }
 
+func (s *snapshot) RunGoCommandDirect(ctx context.Context, verb string, args []string) error {
+	cfg := s.config(ctx)
+	_, _, err := runGoCommand(ctx, cfg, nil, s.view.tmpMod, verb, args)
+	return err
+}
+
+func (s *snapshot) RunGoCommand(ctx context.Context, verb string, args []string) (*bytes.Buffer, error) {
+	cfg := s.config(ctx)
+	var pmh source.ParseModHandle
+	if s.view.tmpMod {
+		modFH, err := s.GetFile(ctx, s.view.modURI)
+		if err != nil {
+			return nil, err
+		}
+		pmh, err = s.ParseModHandle(ctx, modFH)
+		if err != nil {
+			return nil, err
+		}
+	}
+	_, stdout, err := runGoCommand(ctx, cfg, pmh, s.view.tmpMod, verb, args)
+	return stdout, err
+}
+
+func (s *snapshot) RunGoCommandPiped(ctx context.Context, verb string, args []string, stdout, stderr io.Writer) error {
+	cfg := s.config(ctx)
+	var pmh source.ParseModHandle
+	if s.view.tmpMod {
+		modFH, err := s.GetFile(ctx, s.view.modURI)
+		if err != nil {
+			return err
+		}
+		pmh, err = s.ParseModHandle(ctx, modFH)
+		if err != nil {
+			return err
+		}
+	}
+	_, inv, cleanup, err := goCommandInvocation(ctx, cfg, pmh, verb, args)
+	if err != nil {
+		return err
+	}
+	defer cleanup()
+
+	runner := packagesinternal.GetGoCmdRunner(cfg)
+	return runner.RunPiped(ctx, *inv, stdout, stderr)
+}
+
+// runGoCommand runs the given go command with the given config.
+// The given go.mod file is used to construct the temporary go.mod file, which
+// is then passed to the go command via the BuildFlags.
+// It assumes that modURI is only provided when the -modfile flag is enabled.
+func runGoCommand(ctx context.Context, cfg *packages.Config, pmh source.ParseModHandle, tmpMod bool, verb string, args []string) (span.URI, *bytes.Buffer, error) {
+	// Don't pass in the ParseModHandle if we are not using the -modfile flag.
+	var tmpPMH source.ParseModHandle
+	if tmpMod {
+		tmpPMH = pmh
+	}
+	tmpURI, inv, cleanup, err := goCommandInvocation(ctx, cfg, tmpPMH, verb, args)
+	if err != nil {
+		return "", nil, err
+	}
+	defer cleanup()
+
+	runner := packagesinternal.GetGoCmdRunner(cfg)
+	stdout, err := runner.Run(ctx, *inv)
+	return tmpURI, stdout, err
+}
+
+// Assumes that modURI is only provided when the -modfile flag is enabled.
+func goCommandInvocation(ctx context.Context, cfg *packages.Config, pmh source.ParseModHandle, verb string, args []string) (tmpURI span.URI, inv *gocommand.Invocation, cleanup func(), err error) {
+	cleanup = func() {} // fallback
+	if pmh != nil {
+		tmpURI, cleanup, err = tempModFile(pmh.Mod(), pmh.Sum())
+		if err != nil {
+			return "", nil, nil, err
+		}
+		cfg.BuildFlags = append(cfg.BuildFlags, fmt.Sprintf("-modfile=%s", tmpURI.Filename()))
+	}
+	return tmpURI, &gocommand.Invocation{
+		Verb:       verb,
+		Args:       args,
+		Env:        cfg.Env,
+		BuildFlags: cfg.BuildFlags,
+		WorkingDir: cfg.Dir,
+	}, cleanup, nil
+}
+
 func (s *snapshot) buildOverlay() map[string][]byte {
 	s.mu.Lock()
 	defer s.mu.Unlock()
@@ -154,16 +256,16 @@
 }
 
 func (s *snapshot) PackageHandles(ctx context.Context, fh source.FileHandle) ([]source.PackageHandle, error) {
-	if fh.Identity().Kind != source.Go {
+	if fh.Kind() != source.Go {
 		panic("called PackageHandles on a non-Go FileHandle")
 	}
 
-	ctx = event.Label(ctx, tag.URI.Of(fh.Identity().URI))
+	ctx = event.Label(ctx, tag.URI.Of(fh.URI()))
 
 	// Check if we should reload metadata for the file. We don't invalidate IDs
 	// (though we should), so the IDs will be a better source of truth than the
 	// metadata. If there are no IDs for the file, then we should also reload.
-	ids := s.getIDsForURI(fh.Identity().URI)
+	ids := s.getIDsForURI(fh.URI())
 	reload := len(ids) == 0
 	for _, id := range ids {
 		// Reload package metadata if any of the metadata has missing
@@ -178,13 +280,13 @@
 		// calls to packages.Load. Determine what we should do instead.
 	}
 	if reload {
-		if err := s.load(ctx, fileURI(fh.Identity().URI)); err != nil {
+		if err := s.load(ctx, fileURI(fh.URI())); err != nil {
 			return nil, err
 		}
 	}
 	// Get the list of IDs from the snapshot again, in case it has changed.
 	var phs []source.PackageHandle
-	for _, id := range s.getIDsForURI(fh.Identity().URI) {
+	for _, id := range s.getIDsForURI(fh.URI()) {
 		ph, err := s.packageHandle(ctx, id, source.ParseFull)
 		if err != nil {
 			return nil, err
@@ -248,13 +350,25 @@
 	}
 }
 
-func (s *snapshot) getModHandle(uri span.URI) *modHandle {
+func (s *snapshot) getModHandle(uri span.URI) *parseModHandle {
 	s.mu.Lock()
 	defer s.mu.Unlock()
-	return s.modHandles[uri]
+	return s.parseModHandles[uri]
 }
 
-func (s *snapshot) getModTidyHandle() *modHandle {
+func (s *snapshot) getModWhyHandle() *modWhyHandle {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	return s.modWhyHandle
+}
+
+func (s *snapshot) getModUpgradeHandle() *modUpgradeHandle {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	return s.modUpgradeHandle
+}
+
+func (s *snapshot) getModTidyHandle() *modTidyHandle {
 	s.mu.Lock()
 	defer s.mu.Unlock()
 	return s.modTidyHandle
@@ -515,7 +629,7 @@
 
 // GetFile returns a File for the given URI. It will always succeed because it
 // adds the file to the managed set if needed.
-func (s *snapshot) GetFile(uri span.URI) (source.FileHandle, error) {
+func (s *snapshot) GetFile(ctx context.Context, uri span.URI) (source.FileHandle, error) {
 	f, err := s.view.getFile(uri)
 	if err != nil {
 		return nil, err
@@ -524,10 +638,16 @@
 	s.mu.Lock()
 	defer s.mu.Unlock()
 
-	if _, ok := s.files[f.URI()]; !ok {
-		s.files[f.URI()] = s.view.session.cache.GetFile(uri)
+	if fh, ok := s.files[f.URI()]; ok {
+		return fh, nil
 	}
-	return s.files[f.URI()], nil
+
+	fh, err := s.view.session.cache.getFile(ctx, uri)
+	if err != nil {
+		return nil, err
+	}
+	s.files[f.URI()] = fh
+	return fh, nil
 }
 
 func (s *snapshot) IsOpen(uri span.URI) bool {
@@ -553,7 +673,18 @@
 	if err := s.reloadWorkspace(ctx); err != nil {
 		return err
 	}
-	return s.reloadOrphanedFiles(ctx)
+	if err := s.reloadOrphanedFiles(ctx); err != nil {
+		return err
+	}
+	// If we still have absolutely no metadata, check if the view failed to
+	// initialize and return any errors.
+	// TODO(rstambler): Should we clear the error after we return it?
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	if len(s.metadata) == 0 {
+		return s.view.initializedErr
+	}
+	return nil
 }
 
 // reloadWorkspace reloads the metadata for all invalidated workspace packages.
@@ -626,7 +757,7 @@
 	scopeSet := make(map[span.URI]struct{})
 	for uri, fh := range s.files {
 		// Don't try to reload metadata for go.mod files.
-		if fh.Identity().Kind != source.Go {
+		if fh.Kind() != source.Go {
 			continue
 		}
 		// If the URI doesn't belong to this view, then it's not in a workspace
@@ -649,7 +780,7 @@
 	return scopes
 }
 
-func contains(views []*view, view *view) bool {
+func contains(views []*View, view *View) bool {
 	for _, v := range views {
 		if v == view {
 			return true
@@ -658,7 +789,7 @@
 	return false
 }
 
-func (s *snapshot) clone(ctx context.Context, withoutURIs map[span.URI]source.FileHandle) *snapshot {
+func (s *snapshot) clone(ctx context.Context, withoutURIs map[span.URI]source.FileHandle, forceReloadMetadata bool) *snapshot {
 	s.mu.Lock()
 	defer s.mu.Unlock()
 
@@ -673,8 +804,10 @@
 		files:             make(map[span.URI]source.FileHandle),
 		workspacePackages: make(map[packageID]packagePath),
 		unloadableFiles:   make(map[span.URI]struct{}),
-		modHandles:        make(map[span.URI]*modHandle),
+		parseModHandles:   make(map[span.URI]*parseModHandle),
 		modTidyHandle:     s.modTidyHandle,
+		modUpgradeHandle:  s.modUpgradeHandle,
+		modWhyHandle:      s.modWhyHandle,
 	}
 
 	// Copy all of the FileHandles.
@@ -686,15 +819,14 @@
 		result.unloadableFiles[k] = v
 	}
 	// Copy all of the modHandles.
-	for k, v := range s.modHandles {
-		result.modHandles[k] = v
+	for k, v := range s.parseModHandles {
+		result.parseModHandles[k] = v
 	}
 
 	// transitiveIDs keeps track of transitive reverse dependencies.
 	// If an ID is present in the map, invalidate its types.
 	// If an ID's value is true, invalidate its metadata too.
 	transitiveIDs := make(map[packageID]bool)
-
 	for withoutURI, currentFH := range withoutURIs {
 		directIDs := map[packageID]struct{}{}
 
@@ -708,14 +840,16 @@
 
 		// Check if the file's package name or imports have changed,
 		// and if so, invalidate this file's packages' metadata.
-		invalidateMetadata := s.shouldInvalidateMetadata(ctx, originalFH, currentFH)
+		invalidateMetadata := forceReloadMetadata || s.shouldInvalidateMetadata(ctx, originalFH, currentFH)
 
 		// Invalidate the previous modTidyHandle if any of the files have been
 		// saved or if any of the metadata has been invalidated.
 		if invalidateMetadata || fileWasSaved(originalFH, currentFH) {
 			result.modTidyHandle = nil
+			result.modUpgradeHandle = nil
+			result.modWhyHandle = nil
 		}
-		if currentFH.Identity().Kind == source.Mod {
+		if currentFH.Kind() == source.Mod {
 			// If the view's go.mod file's contents have changed, invalidate the metadata
 			// for all of the packages in the workspace.
 			if invalidateMetadata {
@@ -723,7 +857,7 @@
 					directIDs[id] = struct{}{}
 				}
 			}
-			delete(result.modHandles, withoutURI)
+			delete(result.parseModHandles, withoutURI)
 		}
 
 		// If this is a file we don't yet know about,
@@ -762,7 +896,7 @@
 		}
 
 		// Handle the invalidated file; it may have new contents or not exist.
-		if _, _, err := currentFH.Read(ctx); os.IsNotExist(err) {
+		if _, err := currentFH.Read(); os.IsNotExist(err) {
 			delete(result.files, withoutURI)
 		} else {
 			result.files[withoutURI] = currentFH
@@ -794,25 +928,38 @@
 	}
 	// Copy the URI to package ID mappings, skipping only those URIs whose
 	// metadata will be reloaded in future calls to load.
-outer:
+copyIDs:
 	for k, ids := range s.ids {
 		for _, id := range ids {
 			if invalidateMetadata, ok := transitiveIDs[id]; invalidateMetadata && ok {
-				continue outer
+				continue copyIDs
 			}
 		}
 		result.ids[k] = ids
 	}
 	// Copy the set of initally loaded packages.
 	for id, pkgPath := range s.workspacePackages {
-		// TODO(rstambler): For now, we only invalidate "command-line-arguments"
-		// from workspace packages, but in general, we need to handle deletion
-		// of a package.
 		if id == "command-line-arguments" {
 			if invalidateMetadata, ok := transitiveIDs[id]; invalidateMetadata && ok {
 				continue
 			}
 		}
+
+		// If all the files we know about in a package have been deleted,
+		// the package is gone and we should no longer try to load it.
+		if m := s.metadata[id]; m != nil {
+			hasFiles := false
+			for _, uri := range s.metadata[id].goFiles {
+				if _, ok := result.files[uri]; ok {
+					hasFiles = true
+					break
+				}
+			}
+			if !hasFiles {
+				continue
+			}
+		}
+
 		result.workspacePackages[id] = pkgPath
 	}
 	// Don't bother copying the importedBy graph,
@@ -840,20 +987,19 @@
 // determine if the file requires a metadata reload.
 func (s *snapshot) shouldInvalidateMetadata(ctx context.Context, originalFH, currentFH source.FileHandle) bool {
 	if originalFH == nil {
-		return currentFH.Identity().Kind == source.Go
+		return currentFH.Kind() == source.Go
 	}
 	// If the file hasn't changed, there's no need to reload.
 	if originalFH.Identity().String() == currentFH.Identity().String() {
 		return false
 	}
 	// If a go.mod file's contents have changed, always invalidate metadata.
-	if kind := originalFH.Identity().Kind; kind == source.Mod {
-		modfile, _ := s.view.ModFiles()
-		return originalFH.Identity().URI == modfile
+	if kind := originalFH.Kind(); kind == source.Mod {
+		return originalFH.URI() == s.view.modURI
 	}
 	// Get the original and current parsed files in order to check package name and imports.
-	original, _, _, _, originalErr := s.view.session.cache.ParseGoHandle(originalFH, source.ParseHeader).Parse(ctx)
-	current, _, _, _, currentErr := s.view.session.cache.ParseGoHandle(currentFH, source.ParseHeader).Parse(ctx)
+	original, _, _, _, originalErr := s.view.session.cache.ParseGoHandle(ctx, originalFH, source.ParseHeader).Parse(ctx)
+	current, _, _, _, currentErr := s.view.session.cache.ParseGoHandle(ctx, currentFH, source.ParseHeader).Parse(ctx)
 	if originalErr != nil || currentErr != nil {
 		return (originalErr == nil) != (currentErr == nil)
 	}
diff --git a/internal/lsp/cache/view.go b/internal/lsp/cache/view.go
index be9e64a..f04a988 100644
--- a/internal/lsp/cache/view.go
+++ b/internal/lsp/cache/view.go
@@ -13,6 +13,7 @@
 	"io"
 	"io/ioutil"
 	"os"
+	"path"
 	"path/filepath"
 	"reflect"
 	"strings"
@@ -23,7 +24,6 @@
 	"golang.org/x/tools/internal/event/keys"
 	"golang.org/x/tools/internal/gocommand"
 	"golang.org/x/tools/internal/imports"
-	"golang.org/x/tools/internal/lsp/debug"
 	"golang.org/x/tools/internal/lsp/debug/tag"
 	"golang.org/x/tools/internal/lsp/source"
 	"golang.org/x/tools/internal/memoize"
@@ -32,7 +32,7 @@
 	errors "golang.org/x/xerrors"
 )
 
-type view struct {
+type View struct {
 	session *Session
 	id      string
 
@@ -62,7 +62,10 @@
 
 	// importsMu guards imports-related state, particularly the ProcessEnv.
 	importsMu sync.Mutex
-	// process is the process env for this view.
+
+	// processEnv is the process env for this view.
+	// Some of its fields can be changed dynamically by modifications to
+	// the view's options. These fields are repopulated for every use.
 	// Note: this contains cached module and filesystem state.
 	//
 	// TODO(suzmue): the state cached in the process env is specific to each view,
@@ -81,16 +84,17 @@
 	snapshotMu sync.Mutex
 	snapshot   *snapshot
 
-	// ignoredURIs is the set of URIs of files that we ignore.
-	ignoredURIsMu sync.Mutex
-	ignoredURIs   map[span.URI]struct{}
-
 	// initialized is closed when the view has been fully initialized.
 	// On initialization, the view's workspace packages are loaded.
 	// All of the fields below are set as part of initialization.
 	// If we failed to load, we don't re-try to avoid too many go/packages calls.
 	initializeOnce sync.Once
 	initialized    chan struct{}
+	initCancel     context.CancelFunc
+
+	// initializedErr needs no mutex, since any access to it happens after it
+	// has been set.
+	initializedErr error
 
 	// builtin pins the AST and package for builtin.go in memory.
 	builtin *builtinPackageHandle
@@ -99,16 +103,19 @@
 	// non go command build system.
 	hasValidBuildConfiguration bool
 
-	// The real and temporary go.mod files that are attributed to a view.
-	// The temporary go.mod is for use with the Go command's -modfile flag.
-	realMod, tempMod span.URI
+	// The real go.mod and go.sum files that are attributed to a view.
+	modURI, sumURI span.URI
+
+	// True if this view runs go commands using temporary mod files.
+	// Only possible with Go versions 1.14 and above.
+	tmpMod bool
 
 	// goCommand indicates if the user is using the go command or some other
 	// build system.
 	goCommand bool
 
 	// `go env` variables that need to be tracked.
-	gopath, gocache string
+	gopath, gocache, goprivate string
 
 	// gocmdRunner guards go command calls from concurrency errors.
 	gocmdRunner *gocommand.Runner
@@ -123,16 +130,25 @@
 	memoize.NoCopy
 
 	pkg *ast.Package
+	pgh *parseGoHandle
 	err error
 }
 
+func (d *builtinPackageData) Package() *ast.Package {
+	return d.pkg
+}
+
+func (d *builtinPackageData) ParseGoHandle() source.ParseGoHandle {
+	return d.pgh
+}
+
 // fileBase holds the common functionality for all files.
 // It is intended to be embedded in the file implementations
 type fileBase struct {
 	uris  []span.URI
 	fname string
 
-	view *view
+	view *View
 }
 
 func (f *fileBase) URI() span.URI {
@@ -148,29 +164,81 @@
 	return len(f.uris)
 }
 
-func (v *view) ValidBuildConfiguration() bool {
+func (v *View) ID() string { return v.id }
+
+func (v *View) ValidBuildConfiguration() bool {
 	return v.hasValidBuildConfiguration
 }
 
-func (v *view) ModFiles() (span.URI, span.URI) {
-	return v.realMod, v.tempMod
+func (v *View) ModFile() span.URI {
+	return v.modURI
 }
 
-func (v *view) Session() source.Session {
+// tempModFile creates a temporary go.mod file based on the contents of the
+// given go.mod file. It is the caller's responsibility to clean up the files
+// when they are done using them.
+func tempModFile(modFh, sumFH source.FileHandle) (tmpURI span.URI, cleanup func(), err error) {
+	filenameHash := hashContents([]byte(modFh.URI().Filename()))
+	tmpMod, err := ioutil.TempFile("", fmt.Sprintf("go.%s.*.mod", filenameHash))
+	if err != nil {
+		return "", nil, err
+	}
+	defer tmpMod.Close()
+
+	tmpURI = span.URIFromPath(tmpMod.Name())
+	tmpSumName := sumFilename(tmpURI)
+
+	content, err := modFh.Read()
+	if err != nil {
+		return "", nil, err
+	}
+
+	if _, err := tmpMod.Write(content); err != nil {
+		return "", nil, err
+	}
+
+	cleanup = func() {
+		_ = os.Remove(tmpSumName)
+		_ = os.Remove(tmpURI.Filename())
+	}
+
+	// Be careful to clean up if we return an error from this function.
+	defer func() {
+		if err != nil {
+			cleanup()
+			cleanup = nil
+		}
+	}()
+
+	// Create an analogous go.sum, if one exists.
+	if sumFH != nil {
+		sumContents, err := sumFH.Read()
+		if err != nil {
+			return "", nil, err
+		}
+		if err := ioutil.WriteFile(tmpSumName, sumContents, 0655); err != nil {
+			return "", nil, err
+		}
+	}
+
+	return tmpURI, cleanup, nil
+}
+
+func (v *View) Session() source.Session {
 	return v.session
 }
 
 // Name returns the user visible name of this view.
-func (v *view) Name() string {
+func (v *View) Name() string {
 	return v.name
 }
 
 // Folder returns the root of this view.
-func (v *view) Folder() span.URI {
+func (v *View) Folder() span.URI {
 	return v.folder
 }
 
-func (v *view) Options() source.Options {
+func (v *View) Options() source.Options {
 	v.optionsMu.Lock()
 	defer v.optionsMu.Unlock()
 	return v.options
@@ -188,7 +256,7 @@
 	return true
 }
 
-func (v *view) SetOptions(ctx context.Context, options source.Options) (source.View, error) {
+func (v *View) SetOptions(ctx context.Context, options source.Options) (source.View, error) {
 	// no need to rebuild the view if the options were not materially changed
 	v.optionsMu.Lock()
 	if minorOptionsChange(v.options, options) {
@@ -201,12 +269,12 @@
 	return newView, err
 }
 
-func (v *view) Rebuild(ctx context.Context) (source.Snapshot, error) {
+func (v *View) Rebuild(ctx context.Context) (source.Snapshot, error) {
 	_, snapshot, err := v.session.updateView(ctx, v, v.Options())
 	return snapshot, err
 }
 
-func (v *view) LookupBuiltin(ctx context.Context, name string) (*ast.Object, error) {
+func (v *View) BuiltinPackage(ctx context.Context) (source.BuiltinPackage, error) {
 	v.awaitInitialized(ctx)
 
 	if v.builtin == nil {
@@ -229,35 +297,38 @@
 	if d.pkg == nil || d.pkg.Scope == nil {
 		return nil, errors.Errorf("no builtin package")
 	}
-	astObj := d.pkg.Scope.Lookup(name)
-	if astObj == nil {
-		return nil, errors.Errorf("no builtin object for %s", name)
-	}
-	return astObj, nil
+	return d, nil
 }
 
-func (v *view) buildBuiltinPackage(ctx context.Context, goFiles []string) error {
+func (v *View) buildBuiltinPackage(ctx context.Context, goFiles []string) error {
 	if len(goFiles) != 1 {
 		return errors.Errorf("only expected 1 file, got %v", len(goFiles))
 	}
 	uri := span.URIFromPath(goFiles[0])
-	v.addIgnoredFile(uri) // to avoid showing diagnostics for builtin.go
 
 	// Get the FileHandle through the cache to avoid adding it to the snapshot
 	// and to get the file content from disk.
-	pgh := v.session.cache.ParseGoHandle(v.session.cache.GetFile(uri), source.ParseFull)
+	fh, err := v.session.cache.getFile(ctx, uri)
+	if err != nil {
+		return err
+	}
+	pgh := v.session.cache.parseGoHandle(ctx, fh, source.ParseFull)
 	fset := v.session.cache.fset
-	h := v.session.cache.store.Bind(pgh.File().Identity(), func(ctx context.Context) interface{} {
-		data := &builtinPackageData{}
+	h := v.session.cache.store.Bind(fh.Identity(), func(ctx context.Context) interface{} {
 		file, _, _, _, err := pgh.Parse(ctx)
 		if err != nil {
-			data.err = err
-			return data
+			return &builtinPackageData{err: err}
 		}
-		data.pkg, data.err = ast.NewPackage(fset, map[string]*ast.File{
-			pgh.File().Identity().URI.Filename(): file,
+		pkg, err := ast.NewPackage(fset, map[string]*ast.File{
+			pgh.File().URI().Filename(): file,
 		}, nil, nil)
-		return data
+		if err != nil {
+			return &builtinPackageData{err: err}
+		}
+		return &builtinPackageData{
+			pgh: pgh,
+			pkg: pkg,
+		}
 	})
 	v.builtin = &builtinPackageHandle{
 		handle: h,
@@ -266,10 +337,11 @@
 	return nil
 }
 
-func (v *view) WriteEnv(ctx context.Context, w io.Writer) error {
+func (v *View) WriteEnv(ctx context.Context, w io.Writer) error {
 	v.optionsMu.Lock()
 	env, buildFlags := v.envLocked()
 	v.optionsMu.Unlock()
+
 	// TODO(rstambler): We could probably avoid running this by saving the
 	// output on original create, but I'm not sure if it's worth it.
 	inv := gocommand.Invocation{
@@ -277,6 +349,8 @@
 		Env:        env,
 		WorkingDir: v.Folder().Filename(),
 	}
+	// Don't go through runGoCommand, as we don't need a temporary go.mod to
+	// run `go env`.
 	stdout, err := v.gocmdRunner.Run(ctx, inv)
 	if err != nil {
 		return err
@@ -286,22 +360,49 @@
 	return nil
 }
 
-func (v *view) RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options) error) error {
+func (v *View) RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options) error) error {
 	v.importsMu.Lock()
 	defer v.importsMu.Unlock()
 
+	// The resolver cached in the process env is reused, but some fields need
+	// to be repopulated for each use.
 	if v.processEnv == nil {
+		v.processEnv = &imports.ProcessEnv{}
+	}
+
+	var modFH, sumFH source.FileHandle
+	if v.tmpMod {
 		var err error
-		if v.processEnv, err = v.buildProcessEnv(ctx); err != nil {
+		// Use temporary go.mod files, but always go to disk for the contents.
+		// Rebuilding the cache is expensive, and we don't want to do it for
+		// transient changes.
+		modFH, err = v.session.cache.getFile(ctx, v.modURI)
+		if err != nil {
 			return err
 		}
+		if v.sumURI != "" {
+			sumFH, err = v.session.cache.getFile(ctx, v.sumURI)
+			if err != nil {
+				return err
+			}
+		}
 	}
 
-	// In module mode, check if the mod file has changed.
-	if v.realMod != "" {
-		if mod := v.session.cache.GetFile(v.realMod); mod.Identity() != v.cachedModFileVersion {
+	cleanup, err := v.populateProcessEnv(ctx, modFH, sumFH)
+	if err != nil {
+		return err
+	}
+	defer cleanup()
+
+	// If the go.mod file has changed, clear the cache.
+	if v.modURI != "" {
+		modFH, err := v.session.cache.getFile(ctx, v.modURI)
+		if err != nil {
+			return err
+		}
+		if modFH.Identity() != v.cachedModFileVersion {
 			v.processEnv.GetResolver().(*imports.ModuleResolver).ClearForNewMod()
-			v.cachedModFileVersion = mod.Identity()
+			v.cachedModFileVersion = modFH.Identity()
 		}
 	}
 
@@ -334,7 +435,7 @@
 	return nil
 }
 
-func (v *view) refreshProcessEnv() {
+func (v *View) refreshProcessEnv() {
 	start := time.Now()
 
 	v.importsMu.Lock()
@@ -356,19 +457,37 @@
 	v.importsMu.Unlock()
 }
 
-func (v *view) buildProcessEnv(ctx context.Context) (*imports.ProcessEnv, error) {
+// populateProcessEnv sets the dynamically configurable fields for the view's
+// process environment. It operates on a snapshot because it needs to access
+// file contents. Assumes that the caller is holding the s.view.importsMu.
+func (v *View) populateProcessEnv(ctx context.Context, modFH, sumFH source.FileHandle) (cleanup func(), err error) {
+	cleanup = func() {}
+
 	v.optionsMu.Lock()
 	env, buildFlags := v.envLocked()
 	localPrefix, verboseOutput := v.options.LocalPrefix, v.options.VerboseOutput
 	v.optionsMu.Unlock()
-	processEnv := &imports.ProcessEnv{
-		WorkingDir:  v.folder.Filename(),
-		BuildFlags:  buildFlags,
-		LocalPrefix: localPrefix,
-		GocmdRunner: v.gocmdRunner,
+
+	pe := v.processEnv
+
+	pe.BuildFlags = buildFlags
+
+	// Add -modfile to the build flags, if we are using it.
+	if modFH != nil {
+		var tmpURI span.URI
+		tmpURI, cleanup, err = tempModFile(modFH, sumFH)
+		if err != nil {
+			return nil, err
+		}
+		pe.BuildFlags = append(pe.BuildFlags, fmt.Sprintf("-modfile=%s", tmpURI.Filename()))
 	}
+
+	pe.WorkingDir = v.folder.Filename()
+	pe.LocalPrefix = localPrefix
+	pe.GocmdRunner = v.gocmdRunner
+
 	if verboseOutput {
-		processEnv.Logf = func(format string, args ...interface{}) {
+		pe.Logf = func(format string, args ...interface{}) {
 			event.Log(ctx, fmt.Sprintf(format, args...))
 		}
 	}
@@ -379,42 +498,39 @@
 		}
 		switch split[0] {
 		case "GOPATH":
-			processEnv.GOPATH = split[1]
+			pe.GOPATH = split[1]
 		case "GOROOT":
-			processEnv.GOROOT = split[1]
+			pe.GOROOT = split[1]
 		case "GO111MODULE":
-			processEnv.GO111MODULE = split[1]
+			pe.GO111MODULE = split[1]
 		case "GOPROXY":
-			processEnv.GOPROXY = split[1]
+			pe.GOPROXY = split[1]
 		case "GOFLAGS":
-			processEnv.GOFLAGS = split[1]
+			pe.GOFLAGS = split[1]
 		case "GOSUMDB":
-			processEnv.GOSUMDB = split[1]
+			pe.GOSUMDB = split[1]
 		}
 	}
-	if processEnv.GOPATH == "" {
+	if pe.GOPATH == "" {
 		return nil, fmt.Errorf("no GOPATH for view %s", v.folder)
 	}
-	return processEnv, nil
+	return cleanup, nil
 }
 
-func (v *view) envLocked() ([]string, []string) {
-	// We want to run the go commands with the -modfile flag if the version of go
-	// that we are using supports it.
-	buildFlags := v.options.BuildFlags
-	if v.tempMod != "" {
-		buildFlags = append(buildFlags, fmt.Sprintf("-modfile=%s", v.tempMod.Filename()))
-	}
+// envLocked returns the environment and build flags for the current view.
+// It assumes that the caller is holding the view's optionsMu.
+func (v *View) envLocked() ([]string, []string) {
 	env := []string{fmt.Sprintf("GOPATH=%s", v.gopath)}
 	env = append(env, v.options.Env...)
+	buildFlags := append([]string{}, v.options.BuildFlags...)
 	return env, buildFlags
 }
 
-func (v *view) contains(uri span.URI) bool {
+func (v *View) contains(uri span.URI) bool {
 	return strings.HasPrefix(string(uri), string(v.folder))
 }
 
-func (v *view) mapFile(uri span.URI, f *fileBase) {
+func (v *View) mapFile(uri span.URI, f *fileBase) {
 	v.filesByURI[uri] = f
 	if f.addURI(uri) == 1 {
 		basename := basename(f.filename())
@@ -426,7 +542,7 @@
 	return strings.ToLower(filepath.Base(filename))
 }
 
-func (v *view) relevantChange(c source.FileModification) bool {
+func (v *View) relevantChange(c source.FileModification) bool {
 	// If the file is known to the view, the change is relevant.
 	known := v.knownFile(c.URI)
 
@@ -439,7 +555,7 @@
 	return v.contains(c.URI) || known
 }
 
-func (v *view) knownFile(uri span.URI) bool {
+func (v *View) knownFile(uri span.URI) bool {
 	v.mu.Lock()
 	defer v.mu.Unlock()
 
@@ -449,7 +565,7 @@
 
 // getFile returns a file for the given URI. It will always succeed because it
 // adds the file to the managed set if needed.
-func (v *view) getFile(uri span.URI) (*fileBase, error) {
+func (v *View) getFile(uri span.URI) (*fileBase, error) {
 	v.mu.Lock()
 	defer v.mu.Unlock()
 
@@ -471,7 +587,7 @@
 //
 // An error is only returned for an irreparable failure, for example, if the
 // filename in question does not exist.
-func (v *view) findFile(uri span.URI) (*fileBase, error) {
+func (v *View) findFile(uri span.URI) (*fileBase, error) {
 	if f := v.filesByURI[uri]; f != nil {
 		// a perfect match
 		return f, nil
@@ -502,80 +618,91 @@
 	return nil, nil
 }
 
-func (v *view) Shutdown(ctx context.Context) {
+func (v *View) Shutdown(ctx context.Context) {
 	v.session.removeView(ctx, v)
 }
 
-func (v *view) shutdown(ctx context.Context) {
-	// TODO: Cancel the view's initialization.
+func (v *View) shutdown(ctx context.Context) {
+	// Cancel the initial workspace load if it is still running.
+	v.initCancel()
+
 	v.mu.Lock()
 	defer v.mu.Unlock()
 	if v.cancel != nil {
 		v.cancel()
 		v.cancel = nil
 	}
-	if v.tempMod != "" {
-		os.Remove(v.tempMod.Filename())
-		os.Remove(tempSumFile(v.tempMod.Filename()))
-	}
-	if di := debug.GetInstance(ctx); di != nil {
-		di.State.DropView(debugView{v})
-	}
 }
 
-// Ignore checks if the given URI is a URI we ignore.
-// As of right now, we only ignore files in the "builtin" package.
-func (v *view) Ignore(uri span.URI) bool {
-	v.ignoredURIsMu.Lock()
-	defer v.ignoredURIsMu.Unlock()
-
-	_, ok := v.ignoredURIs[uri]
-
-	// Files with _ prefixes are always ignored.
-	if !ok && strings.HasPrefix(filepath.Base(uri.Filename()), "_") {
-		v.ignoredURIs[uri] = struct{}{}
-		return true
-	}
-
-	return ok
-}
-
-func (v *view) addIgnoredFile(uri span.URI) {
-	v.ignoredURIsMu.Lock()
-	defer v.ignoredURIsMu.Unlock()
-
-	v.ignoredURIs[uri] = struct{}{}
-}
-
-func (v *view) BackgroundContext() context.Context {
+func (v *View) BackgroundContext() context.Context {
 	v.mu.Lock()
 	defer v.mu.Unlock()
 
 	return v.backgroundCtx
 }
 
-func (v *view) Snapshot() source.Snapshot {
+func (v *View) IgnoredFile(uri span.URI) bool {
+	filename := uri.Filename()
+	var prefixes []string
+	if v.modURI == "" {
+		for _, entry := range filepath.SplitList(v.gopath) {
+			prefixes = append(prefixes, filepath.Join(entry, "src"))
+		}
+	} else {
+		mainMod := filepath.Dir(v.modURI.Filename())
+		modCache := filepath.Join(filepath.SplitList(v.gopath)[0], "/pkg/mod")
+		prefixes = []string{mainMod, modCache}
+	}
+
+	for _, prefix := range prefixes {
+		if strings.HasPrefix(filename, prefix) {
+			return checkIgnored(filename[len(prefix):])
+		}
+	}
+	return false
+}
+
+// checkIgnored implements go list's exclusion rules. go help list:
+// 		Directory and file names that begin with "." or "_" are ignored
+// 		by the go tool, as are directories named "testdata".
+func checkIgnored(suffix string) bool {
+	for _, component := range strings.Split(suffix, string(filepath.Separator)) {
+		if len(component) == 0 {
+			continue
+		}
+		if component[0] == '.' || component[0] == '_' || component == "testdata" {
+			return true
+		}
+	}
+	return false
+}
+
+func (v *View) Snapshot() source.Snapshot {
 	return v.getSnapshot()
 }
 
-func (v *view) getSnapshot() *snapshot {
+func (v *View) getSnapshot() *snapshot {
 	v.snapshotMu.Lock()
 	defer v.snapshotMu.Unlock()
 
 	return v.snapshot
 }
 
-func (v *view) initialize(ctx context.Context, s *snapshot) {
+func (v *View) initialize(ctx context.Context, s *snapshot) {
 	v.initializeOnce.Do(func() {
 		defer close(v.initialized)
 
 		if err := s.load(ctx, viewLoadScope("LOAD_VIEW"), packagePath("builtin")); err != nil {
+			if ctx.Err() != nil {
+				return
+			}
+			v.initializedErr = err
 			event.Error(ctx, "initial workspace load failed", err)
 		}
 	})
 }
 
-func (v *view) awaitInitialized(ctx context.Context) {
+func (v *View) awaitInitialized(ctx context.Context) {
 	select {
 	case <-ctx.Done():
 	case <-v.initialized:
@@ -585,7 +712,7 @@
 // invalidateContent invalidates the content of a Go file,
 // including any position and type information that depends on it.
 // It returns true if we were already tracking the given file, false otherwise.
-func (v *view) invalidateContent(ctx context.Context, uris map[span.URI]source.FileHandle) source.Snapshot {
+func (v *View) invalidateContent(ctx context.Context, uris map[span.URI]source.FileHandle, forceReloadMetadata bool) source.Snapshot {
 	// Detach the context so that content invalidation cannot be canceled.
 	ctx = xcontext.Detach(ctx)
 
@@ -600,19 +727,22 @@
 	v.snapshotMu.Lock()
 	defer v.snapshotMu.Unlock()
 
-	v.snapshot = v.snapshot.clone(ctx, uris)
+	v.snapshot = v.snapshot.clone(ctx, uris, forceReloadMetadata)
 	return v.snapshot
 }
 
-func (v *view) cancelBackground() {
+func (v *View) cancelBackground() {
 	v.mu.Lock()
 	defer v.mu.Unlock()
-
+	if v.cancel == nil {
+		// this can happen during shutdown
+		return
+	}
 	v.cancel()
 	v.backgroundCtx, v.cancel = context.WithCancel(v.baseCtx)
 }
 
-func (v *view) setBuildInformation(ctx context.Context, folder span.URI, env []string, modfileFlagEnabled bool) error {
+func (v *View) setBuildInformation(ctx context.Context, folder span.URI, env []string, modfileFlagEnabled bool) error {
 	if err := checkPathCase(folder.Filename()); err != nil {
 		return fmt.Errorf("invalid workspace configuration: %w", err)
 	}
@@ -625,54 +755,25 @@
 	if modFile == os.DevNull {
 		return nil
 	}
-	v.realMod = span.URIFromPath(modFile)
+	v.modURI = span.URIFromPath(modFile)
+	// Set the sumURI, if the go.sum exists.
+	sumFilename := filepath.Join(filepath.Dir(modFile), "go.sum")
+	if stat, _ := os.Stat(sumFilename); stat != nil {
+		v.sumURI = span.URIFromPath(sumFilename)
+	}
 
 	// Now that we have set all required fields,
 	// check if the view has a valid build configuration.
-	v.hasValidBuildConfiguration = checkBuildConfiguration(v.goCommand, v.realMod, v.folder, v.gopath)
+	v.setBuildConfiguration()
 
 	// The user has disabled the use of the -modfile flag or has no go.mod file.
-	if !modfileFlagEnabled || v.realMod == "" {
+	if !modfileFlagEnabled || v.modURI == "" {
 		return nil
 	}
 	if modfileFlag, err := v.modfileFlagExists(ctx, v.Options().Env); err != nil {
 		return err
-	} else if !modfileFlag {
-		return nil
-	}
-	// Copy the current go.mod file into the temporary go.mod file.
-	// The file's name will be of the format go.directory.1234.mod.
-	// It's temporary go.sum file should have the corresponding format of go.directory.1234.sum.
-	tmpPattern := fmt.Sprintf("go.%s.*.mod", filepath.Base(folder.Filename()))
-	tempModFile, err := ioutil.TempFile("", tmpPattern)
-	if err != nil {
-		return err
-	}
-	defer tempModFile.Close()
-
-	origFile, err := os.Open(modFile)
-	if err != nil {
-		return err
-	}
-	defer origFile.Close()
-
-	if _, err := io.Copy(tempModFile, origFile); err != nil {
-		return err
-	}
-	v.tempMod = span.URIFromPath(tempModFile.Name())
-
-	// Copy go.sum file as well (if there is one).
-	sumFile := filepath.Join(filepath.Dir(modFile), "go.sum")
-	stat, err := os.Stat(sumFile)
-	if err != nil || !stat.Mode().IsRegular() {
-		return nil
-	}
-	contents, err := ioutil.ReadFile(sumFile)
-	if err != nil {
-		return err
-	}
-	if err := ioutil.WriteFile(tempSumFile(tempModFile.Name()), contents, stat.Mode()); err != nil {
-		return err
+	} else if modfileFlag {
+		v.tmpMod = true
 	}
 	return nil
 }
@@ -684,20 +785,23 @@
 	return nil
 }
 
-func checkBuildConfiguration(goCommand bool, mod, folder span.URI, gopath string) bool {
+func (v *View) setBuildConfiguration() (isValid bool) {
+	defer func() {
+		v.hasValidBuildConfiguration = isValid
+	}()
 	// Since we only really understand the `go` command, if the user is not
 	// using the go command, assume that their configuration is valid.
-	if !goCommand {
+	if !v.goCommand {
 		return true
 	}
 	// Check if the user is working within a module.
-	if mod != "" {
+	if v.modURI != "" {
 		return true
 	}
 	// The user may have a multiple directories in their GOPATH.
 	// Check if the workspace is within any of them.
-	for _, gp := range filepath.SplitList(gopath) {
-		if isSubdirectory(filepath.Join(gp, "src"), folder.Filename()) {
+	for _, gp := range filepath.SplitList(v.gopath) {
+		if isSubdirectory(filepath.Join(gp, "src"), v.folder.Filename()) {
 			return true
 		}
 	}
@@ -709,10 +813,11 @@
 	return err == nil && !strings.HasPrefix(rel, "..")
 }
 
-// getGoEnv sets the view's build information's GOPATH, GOCACHE, and GOPACKAGESDRIVER values.
-// It also returns the view's GOMOD value, which need not be cached.
-func (v *view) getGoEnv(ctx context.Context, env []string) (string, error) {
-	var gocache, gopath, gopackagesdriver bool
+// getGoEnv sets the view's build information's GOPATH, GOCACHE, GOPRIVATE, and
+// GOPACKAGESDRIVER values.  It also returns the view's GOMOD value, which need
+// not be cached.
+func (v *View) getGoEnv(ctx context.Context, env []string) (string, error) {
+	var gocache, gopath, gopackagesdriver, goprivate bool
 	isGoCommand := func(gopackagesdriver string) bool {
 		return gopackagesdriver == "" || gopackagesdriver == "off"
 	}
@@ -728,11 +833,16 @@
 		case "GOPATH":
 			v.gopath = split[1]
 			gopath = true
+		case "GOPRIVATE":
+			v.goprivate = split[1]
+			goprivate = true
 		case "GOPACKAGESDRIVER":
 			v.goCommand = isGoCommand(split[1])
 			gopackagesdriver = true
 		}
 	}
+	// Don't go through runGoCommand, as we don't need a temporary -modfile to
+	// run `go env`.
 	inv := gocommand.Invocation{
 		Verb:       "env",
 		Args:       []string{"-json"},
@@ -762,6 +872,12 @@
 			return "", errors.New("unable to determine GOCACHE")
 		}
 	}
+	if !goprivate {
+		if goprivate, ok := envMap["GOPRIVATE"]; ok {
+			v.goprivate = goprivate
+		}
+		// No error here: GOPRIVATE is not essential.
+	}
 	// The value of GOPACKAGESDRIVER is not returned through the go command.
 	if !gopackagesdriver {
 		v.goCommand = isGoCommand(os.Getenv("GOPACKAGESDRIVER"))
@@ -770,11 +886,58 @@
 		return gomod, nil
 	}
 	return "", nil
+
+}
+
+func (v *View) IsGoPrivatePath(target string) bool {
+	return globsMatchPath(v.goprivate, target)
+}
+
+// Copied from
+// https://cs.opensource.google/go/go/+/master:src/cmd/go/internal/str/path.go;l=58;drc=2910c5b4a01a573ebc97744890a07c1a3122c67a
+func globsMatchPath(globs, target string) bool {
+	for globs != "" {
+		// Extract next non-empty glob in comma-separated list.
+		var glob string
+		if i := strings.Index(globs, ","); i >= 0 {
+			glob, globs = globs[:i], globs[i+1:]
+		} else {
+			glob, globs = globs, ""
+		}
+		if glob == "" {
+			continue
+		}
+
+		// A glob with N+1 path elements (N slashes) needs to be matched
+		// against the first N+1 path elements of target,
+		// which end just before the N+1'th slash.
+		n := strings.Count(glob, "/")
+		prefix := target
+		// Walk target, counting slashes, truncating at the N+1'th slash.
+		for i := 0; i < len(target); i++ {
+			if target[i] == '/' {
+				if n == 0 {
+					prefix = target[:i]
+					break
+				}
+				n--
+			}
+		}
+		if n > 0 {
+			// Not enough prefix elements.
+			continue
+		}
+		matched, _ := path.Match(glob, prefix)
+		if matched {
+			return true
+		}
+	}
+	return false
 }
 
 // This function will return the main go.mod file for this folder if it exists and whether the -modfile
 // flag exists for this version of go.
-func (v *view) modfileFlagExists(ctx context.Context, env []string) (bool, error) {
+func (v *View) modfileFlagExists(ctx context.Context, env []string) (bool, error) {
 	// Check the go version by running "go list" with modules off.
 	// Borrowed from internal/imports/mod.go:620.
 	const format = `{{range context.ReleaseTags}}{{if eq . "go1.14"}}{{.}}{{end}}{{end}}`
@@ -797,12 +960,3 @@
 	}
 	return lines[0] == "go1.14", nil
 }
-
-// tempSumFile returns the path to the copied temporary go.sum file.
-// It simply replaces the extension of the temporary go.mod file with "sum".
-func tempSumFile(filename string) string {
-	if filename == "" {
-		return ""
-	}
-	return filename[:len(filename)-len("mod")] + "sum"
-}
diff --git a/internal/lsp/cmd/capabilities_test.go b/internal/lsp/cmd/capabilities_test.go
index aebed67..7b6d3bd 100644
--- a/internal/lsp/cmd/capabilities_test.go
+++ b/internal/lsp/cmd/capabilities_test.go
@@ -143,6 +143,12 @@
 			t.Errorf("textEdit is not a *protocol.TextEdit, instead it is %T", textEdit)
 		}
 	}
+	if err := c.Server.Shutdown(ctx); err != nil {
+		t.Fatal(err)
+	}
+	if err := c.Server.Exit(ctx); err != nil {
+		t.Fatal(err)
+	}
 }
 
 func validateCapabilities(result *protocol.InitializeResult) error {
diff --git a/internal/lsp/cmd/cmd.go b/internal/lsp/cmd/cmd.go
index 64011e1..28d6915 100644
--- a/internal/lsp/cmd/cmd.go
+++ b/internal/lsp/cmd/cmd.go
@@ -64,7 +64,7 @@
 	Verbose bool `flag:"v" help:"verbose output"`
 
 	// Control ocagent export of telemetry
-	OCAgent string `flag:"ocagent" help:"the address of the ocagent, or off"`
+	OCAgent string `flag:"ocagent" help:"the address of the ocagent (e.g. http://localhost:55678), or off"`
 
 	// PrepareOptions is called to update the options when a new view is built.
 	// It is primarily to allow the behavior of gopls to be modified by hooks.
@@ -224,17 +224,26 @@
 	}
 }
 
+// CloseTestConnections terminates shared connections used in command tests. It
+// should only be called from tests.
+func CloseTestConnections(ctx context.Context) {
+	for _, c := range internalConnections {
+		c.Shutdown(ctx)
+		c.Exit(ctx)
+	}
+}
+
 func (app *Application) connectRemote(ctx context.Context, remote string) (*connection, error) {
 	connection := newConnection(app)
 	conn, err := net.Dial("tcp", remote)
 	if err != nil {
 		return nil, err
 	}
-	stream := jsonrpc2.NewHeaderStream(conn, conn)
+	stream := jsonrpc2.NewHeaderStream(conn)
 	cc := jsonrpc2.NewConn(stream)
 	connection.Server = protocol.ServerDispatcher(cc)
 	ctx = protocol.WithClient(ctx, connection.Client)
-	go cc.Run(ctx,
+	cc.Go(ctx,
 		protocol.Handlers(
 			protocol.ClientHandler(connection.Client,
 				jsonrpc2.MethodNotFound)))
@@ -496,3 +505,8 @@
 	//TODO: right now calling exit terminates the process, we should rethink that
 	//server.Exit(ctx)
 }
+
+// Implement io.Closer.
+func (c *cmdClient) Close() error {
+	return nil
+}
diff --git a/internal/lsp/cmd/serve.go b/internal/lsp/cmd/serve.go
index 86cac2c..00157bd 100644
--- a/internal/lsp/cmd/serve.go
+++ b/internal/lsp/cmd/serve.go
@@ -12,6 +12,7 @@
 	"strings"
 	"time"
 
+	"golang.org/x/tools/internal/fakenet"
 	"golang.org/x/tools/internal/jsonrpc2"
 	"golang.org/x/tools/internal/lsp/cache"
 	"golang.org/x/tools/internal/lsp/debug"
@@ -31,9 +32,9 @@
 	Trace       bool          `flag:"rpc.trace" help:"print the full rpc trace in lsp inspector format"`
 	Debug       string        `flag:"debug" help:"serve debug information on the supplied address"`
 
-	RemoteListenTimeout time.Duration `flag:"remote.listen.timeout" help:"when used with -remote=auto, the listen.timeout used when auto-starting the remote"`
-	RemoteDebug         string        `flag:"remote.debug" help:"when used with -remote=auto, the debug address used when auto-starting the remote"`
-	RemoteLogfile       string        `flag:"remote.logfile" help:"when used with -remote=auto, the filename for the remote daemon to log to"`
+	RemoteListenTimeout time.Duration `flag:"remote.listen.timeout" help:"when used with -remote=auto, the -listen.timeout value used to start the daemon"`
+	RemoteDebug         string        `flag:"remote.debug" help:"when used with -remote=auto, the -debug value used to start the daemon"`
+	RemoteLogfile       string        `flag:"remote.logfile" help:"when used with -remote=auto, the -logfile value used to start the daemon"`
 
 	app *Application
 }
@@ -92,11 +93,12 @@
 		addr := fmt.Sprintf(":%v", s.Port)
 		return jsonrpc2.ListenAndServe(ctx, "tcp", addr, ss, s.IdleTimeout)
 	}
-	stream := jsonrpc2.NewHeaderStream(os.Stdin, os.Stdout)
+	stream := jsonrpc2.NewHeaderStream(fakenet.NewConn("stdio", os.Stdin, os.Stdout))
 	if s.Trace && di != nil {
 		stream = protocol.LoggingStream(stream, di.LogWriter)
 	}
-	return ss.ServeStream(ctx, stream)
+	conn := jsonrpc2.NewConn(stream)
+	return ss.ServeStream(ctx, conn)
 }
 
 // parseAddr parses the -listen flag in to a network, and address.
diff --git a/internal/lsp/cmd/symbols.go b/internal/lsp/cmd/symbols.go
index 60bebbc..b4a503b 100644
--- a/internal/lsp/cmd/symbols.go
+++ b/internal/lsp/cmd/symbols.go
@@ -53,32 +53,44 @@
 		return err
 	}
 	for _, s := range symbols {
-		s, ok := s.(map[string]interface{})
-		if !ok {
-			continue
-		}
-		bytes, err := json.Marshal(s)
-		if err != nil {
-			return err
-		}
-		if _, ok := s["selectionRange"]; ok {
-			if err := parseDocumentSymbol(bytes); err != nil {
+		if m, ok := s.(map[string]interface{}); ok {
+			s, err = mapToSymbol(m)
+			if err != nil {
 				return err
 			}
-			continue
 		}
-		if err := parseSymbolInformation(bytes); err != nil {
-			return err
+		switch t := s.(type) {
+		case protocol.DocumentSymbol:
+			printDocumentSymbol(t)
+		case protocol.SymbolInformation:
+			printSymbolInformation(t)
 		}
 	}
 	return nil
 }
 
-func parseDocumentSymbol(bytes []byte) error {
-	var s protocol.DocumentSymbol
-	if err := json.Unmarshal(bytes, &s); err != nil {
-		return err
+func mapToSymbol(m map[string]interface{}) (interface{}, error) {
+	b, err := json.Marshal(m)
+	if err != nil {
+		return nil, err
 	}
+
+	if _, ok := m["selectionRange"]; ok {
+		var s protocol.DocumentSymbol
+		if err := json.Unmarshal(b, &s); err != nil {
+			return nil, err
+		}
+		return s, nil
+	}
+
+	var s protocol.SymbolInformation
+	if err := json.Unmarshal(b, &s); err != nil {
+		return nil, err
+	}
+	return s, nil
+}
+
+func printDocumentSymbol(s protocol.DocumentSymbol) {
 	fmt.Printf("%s %s %s\n", s.Name, s.Kind, positionToString(s.SelectionRange))
 	// Sort children for consistency
 	sort.Slice(s.Children, func(i, j int) bool {
@@ -87,16 +99,10 @@
 	for _, c := range s.Children {
 		fmt.Printf("\t%s %s %s\n", c.Name, c.Kind, positionToString(c.SelectionRange))
 	}
-	return nil
 }
 
-func parseSymbolInformation(bytes []byte) error {
-	var s protocol.SymbolInformation
-	if err := json.Unmarshal(bytes, &s); err != nil {
-		return err
-	}
+func printSymbolInformation(s protocol.SymbolInformation) {
 	fmt.Printf("%s %s %s\n", s.Name, s.Kind, positionToString(s.Location.Range))
-	return nil
 }
 
 func positionToString(r protocol.Range) string {
diff --git a/internal/lsp/cmd/test/cmdtest.go b/internal/lsp/cmd/test/cmdtest.go
index 863e7ed..5adcc1c 100644
--- a/internal/lsp/cmd/test/cmdtest.go
+++ b/internal/lsp/cmd/test/cmdtest.go
@@ -61,6 +61,7 @@
 				tests.Run(t, NewRunner(exporter, datum, ctx, ts.Addr, options), datum)
 			})
 		}
+		cmd.CloseTestConnections(ctx)
 	}
 }
 
@@ -68,7 +69,7 @@
 	ctx = debug.WithInstance(ctx, "", "")
 	cache := cache.New(ctx, options)
 	ss := lsprpc.NewStreamServer(cache)
-	return servertest.NewTCPServer(ctx, ss)
+	return servertest.NewTCPServer(ctx, ss, nil)
 }
 
 func NewRunner(exporter packagestest.Exporter, data *tests.Data, ctx context.Context, remote string, options func(*source.Options)) *runner {
diff --git a/internal/lsp/cmd/test/suggested_fix.go b/internal/lsp/cmd/test/suggested_fix.go
index 0419e33..f1a478a 100644
--- a/internal/lsp/cmd/test/suggested_fix.go
+++ b/internal/lsp/cmd/test/suggested_fix.go
@@ -16,12 +16,17 @@
 	uri := spn.URI()
 	filename := uri.Filename()
 	args := []string{"fix", "-a", fmt.Sprintf("%s", spn)}
+	for _, kind := range actionKinds {
+		if kind == "refactor.rewrite" {
+			t.Skip("refactor.rewrite is not yet supported on the command line")
+		}
+	}
 	args = append(args, actionKinds...)
 	got, _ := r.NormalizeGoplsCmd(t, args...)
 	want := string(r.data.Golden("suggestedfix_"+tests.SpanName(spn), filename, func() ([]byte, error) {
 		return []byte(got), nil
 	}))
 	if want != got {
-		t.Errorf("suggested fixes failed for %s, expected:\n%v\ngot:\n%v", filename, want, got)
+		t.Errorf("suggested fixes failed for %s: %s", filename, tests.Diff(want, got))
 	}
 }
diff --git a/internal/lsp/code_action.go b/internal/lsp/code_action.go
index c226c1f..f611fc9 100644
--- a/internal/lsp/code_action.go
+++ b/internal/lsp/code_action.go
@@ -11,23 +11,25 @@
 	"sort"
 	"strings"
 
+	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/internal/imports"
 	"golang.org/x/tools/internal/lsp/mod"
 	"golang.org/x/tools/internal/lsp/protocol"
 	"golang.org/x/tools/internal/lsp/source"
+	"golang.org/x/tools/internal/span"
 )
 
 func (s *Server) codeAction(ctx context.Context, params *protocol.CodeActionParams) ([]protocol.CodeAction, error) {
-	snapshot, fh, ok, err := s.beginFileRequest(params.TextDocument.URI, source.UnknownKind)
+	snapshot, fh, ok, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.UnknownKind)
 	if !ok {
 		return nil, err
 	}
-	uri := fh.Identity().URI
+	uri := fh.URI()
 
 	// Determine the supported actions for this file kind.
-	supportedCodeActions, ok := snapshot.View().Options().SupportedCodeActions[fh.Identity().Kind]
+	supportedCodeActions, ok := snapshot.View().Options().SupportedCodeActions[fh.Kind()]
 	if !ok {
-		return nil, fmt.Errorf("no supported code actions for %v file kind", fh.Identity().Kind)
+		return nil, fmt.Errorf("no supported code actions for %v file kind", fh.Kind())
 	}
 
 	// The Only field of the context specifies which code actions the client wants.
@@ -46,7 +48,7 @@
 	}
 
 	var codeActions []protocol.CodeAction
-	switch fh.Identity().Kind {
+	switch fh.Kind() {
 	case source.Mod:
 		if diagnostics := params.Context.Diagnostics; len(diagnostics) > 0 {
 			modFixes, err := mod.SuggestedFixes(ctx, snapshot, fh, diagnostics)
@@ -62,7 +64,7 @@
 				Command: &protocol.Command{
 					Title:     "Tidy",
 					Command:   "tidy",
-					Arguments: []interface{}{fh.Identity().URI},
+					Arguments: []interface{}{fh.URI()},
 				},
 			})
 		}
@@ -111,13 +113,19 @@
 				})
 			}
 		}
-		// Check for context cancellation before processing analysis fixes.
 		if ctx.Err() != nil {
 			return nil, ctx.Err()
 		}
-		// Retrieve any necessary analysis fixes or edits.
+		phs, err := snapshot.PackageHandles(ctx, fh)
+		if err != nil {
+			return nil, err
+		}
+		ph, err := source.WidestPackageHandle(phs)
+		if err != nil {
+			return nil, err
+		}
 		if (wanted[protocol.QuickFix] || wanted[protocol.SourceFixAll]) && len(diagnostics) > 0 {
-			analysisQuickFixes, highConfidenceEdits, err := analysisFixes(ctx, snapshot, fh, diagnostics)
+			analysisQuickFixes, highConfidenceEdits, err := analysisFixes(ctx, snapshot, ph, diagnostics)
 			if err != nil {
 				return nil, err
 			}
@@ -143,6 +151,17 @@
 				})
 			}
 		}
+		if ctx.Err() != nil {
+			return nil, ctx.Err()
+		}
+		// Add any suggestions that do not necessarily fix any diagnostics.
+		if wanted[protocol.RefactorRewrite] {
+			fixes, err := convenienceFixes(ctx, snapshot, ph, uri, params.Range)
+			if err != nil {
+				return nil, err
+			}
+			codeActions = append(codeActions, fixes...)
+		}
 	default:
 		// Unsupported file kind for a code action.
 		return nil, nil
@@ -249,7 +268,7 @@
 	return results
 }
 
-func analysisFixes(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle, diagnostics []protocol.Diagnostic) ([]protocol.CodeAction, []protocol.TextDocumentEdit, error) {
+func analysisFixes(ctx context.Context, snapshot source.Snapshot, ph source.PackageHandle, diagnostics []protocol.Diagnostic) ([]protocol.CodeAction, []protocol.TextDocumentEdit, error) {
 	if len(diagnostics) == 0 {
 		return nil, nil, nil
 	}
@@ -257,16 +276,6 @@
 	var codeActions []protocol.CodeAction
 	var sourceFixAllEdits []protocol.TextDocumentEdit
 
-	phs, err := snapshot.PackageHandles(ctx, fh)
-	if err != nil {
-		return nil, nil, err
-	}
-	// We get the package that source.Diagnostics would've used. This is hack.
-	// TODO(golang/go#32443): The correct solution will be to cache diagnostics per-file per-snapshot.
-	ph, err := source.WidestPackageHandle(phs)
-	if err != nil {
-		return nil, nil, err
-	}
 	for _, diag := range diagnostics {
 		srcErr, analyzer, ok := findSourceError(ctx, snapshot, ph.ID(), diag)
 		if !ok {
@@ -280,7 +289,7 @@
 				Edit:        protocol.WorkspaceEdit{},
 			}
 			for uri, edits := range fix.Edits {
-				fh, err := snapshot.GetFile(uri)
+				fh, err := snapshot.GetFile(ctx, uri)
 				if err != nil {
 					return nil, nil, err
 				}
@@ -337,13 +346,52 @@
 	return nil, source.Analyzer{}, false
 }
 
+func convenienceFixes(ctx context.Context, snapshot source.Snapshot, ph source.PackageHandle, uri span.URI, rng protocol.Range) ([]protocol.CodeAction, error) {
+	var analyzers []*analysis.Analyzer
+	for _, a := range snapshot.View().Options().ConvenienceAnalyzers {
+		analyzers = append(analyzers, a.Analyzer)
+	}
+	diagnostics, err := snapshot.Analyze(ctx, ph.ID(), analyzers...)
+	if err != nil {
+		return nil, err
+	}
+	var codeActions []protocol.CodeAction
+	for _, d := range diagnostics {
+		// For now, only show diagnostics for matching lines. Maybe we should
+		// alter this behavior in the future, depending on the user experience.
+		if d.URI != uri {
+			continue
+		}
+		if d.Range.Start.Line != rng.Start.Line {
+			continue
+		}
+		for _, fix := range d.SuggestedFixes {
+			action := protocol.CodeAction{
+				Title: fix.Title,
+				Kind:  protocol.RefactorRewrite,
+				Edit:  protocol.WorkspaceEdit{},
+			}
+			for uri, edits := range fix.Edits {
+				fh, err := snapshot.GetFile(ctx, uri)
+				if err != nil {
+					return nil, err
+				}
+				docChanges := documentChanges(fh, edits)
+				action.Edit.DocumentChanges = append(action.Edit.DocumentChanges, docChanges...)
+			}
+			codeActions = append(codeActions, action)
+		}
+	}
+	return codeActions, nil
+}
+
 func documentChanges(fh source.FileHandle, edits []protocol.TextEdit) []protocol.TextDocumentEdit {
 	return []protocol.TextDocumentEdit{
 		{
 			TextDocument: protocol.VersionedTextDocumentIdentifier{
-				Version: fh.Identity().Version,
+				Version: fh.Version(),
 				TextDocumentIdentifier: protocol.TextDocumentIdentifier{
-					URI: protocol.URIFromSpanURI(fh.Identity().URI),
+					URI: protocol.URIFromSpanURI(fh.URI()),
 				},
 			},
 			Edits: edits,
diff --git a/internal/lsp/command.go b/internal/lsp/command.go
index cd80d6d..9826258 100644
--- a/internal/lsp/command.go
+++ b/internal/lsp/command.go
@@ -6,71 +6,128 @@
 
 import (
 	"context"
+	"io"
 	"strings"
 
-	"golang.org/x/tools/internal/gocommand"
 	"golang.org/x/tools/internal/lsp/protocol"
 	"golang.org/x/tools/internal/lsp/source"
-	"golang.org/x/tools/internal/packagesinternal"
+	"golang.org/x/tools/internal/span"
 	"golang.org/x/tools/internal/xcontext"
 	errors "golang.org/x/xerrors"
 )
 
 func (s *Server) executeCommand(ctx context.Context, params *protocol.ExecuteCommandParams) (interface{}, error) {
 	switch params.Command {
+	case source.CommandTest:
+		unsaved := false
+		for _, overlay := range s.session.Overlays() {
+			if !overlay.Saved() {
+				unsaved = true
+				break
+			}
+		}
+		if unsaved {
+			return nil, s.client.ShowMessage(ctx, &protocol.ShowMessageParams{
+				Type:    protocol.Error,
+				Message: "could not run tests, there are unsaved files in the view",
+			})
+		}
+		funcName, uri, err := getRunTestArguments(params.Arguments)
+		if err != nil {
+			return nil, err
+		}
+		view, err := s.session.ViewOf(uri)
+		if err != nil {
+			return nil, err
+		}
+		go s.runTest(ctx, view.Snapshot(), funcName)
 	case source.CommandGenerate:
 		dir, recursive, err := getGenerateRequest(params.Arguments)
 		if err != nil {
 			return nil, err
 		}
 		go s.runGenerate(xcontext.Detach(ctx), dir, recursive)
-	case source.CommandTidy:
+	case source.CommandRegenerateCgo:
+		mod := source.FileModification{
+			URI:    protocol.DocumentURI(params.Arguments[0].(string)).SpanURI(),
+			Action: source.InvalidateMetadata,
+		}
+		_, err := s.didModifyFiles(ctx, []source.FileModification{mod}, FromRegenerateCgo)
+		return nil, err
+	case source.CommandTidy, source.CommandVendor:
 		if len(params.Arguments) == 0 || len(params.Arguments) > 1 {
-			return nil, errors.Errorf("expected one file URI for call to `go mod tidy`, got %v", params.Arguments)
+			return nil, errors.Errorf("expected 1 argument, got %v", params.Arguments)
 		}
 		uri := protocol.DocumentURI(params.Arguments[0].(string))
-		snapshot, _, ok, err := s.beginFileRequest(uri, source.Mod)
-		if !ok {
-			return nil, err
+
+		// The flow for `go mod tidy` and `go mod vendor` is almost identical,
+		// so we combine them into one case for convenience.
+		arg := "tidy"
+		if params.Command == source.CommandVendor {
+			arg = "vendor"
 		}
-		cfg := snapshot.Config(ctx)
-		// Run go.mod tidy on the view.
-		inv := gocommand.Invocation{
-			Verb:       "mod",
-			Args:       []string{"tidy"},
-			Env:        cfg.Env,
-			WorkingDir: snapshot.View().Folder().Filename(),
-		}
-		gocmdRunner := packagesinternal.GetGoCmdRunner(cfg)
-		if _, err := gocmdRunner.Run(ctx, inv); err != nil {
-			return nil, err
-		}
+		err := s.directGoModCommand(ctx, uri, "mod", []string{arg}...)
+		return nil, err
 	case source.CommandUpgradeDependency:
 		if len(params.Arguments) < 2 {
-			return nil, errors.Errorf("expected one file URI and one dependency for call to `go get`, got %v", params.Arguments)
+			return nil, errors.Errorf("expected 2 arguments, got %v", params.Arguments)
 		}
 		uri := protocol.DocumentURI(params.Arguments[0].(string))
 		deps := params.Arguments[1].(string)
-		snapshot, _, ok, err := s.beginFileRequest(uri, source.UnknownKind)
-		if !ok {
-			return nil, err
-		}
-		cfg := snapshot.Config(ctx)
-		// Run "go get" on the dependency to upgrade it to the latest version.
-		inv := gocommand.Invocation{
-			Verb:       "get",
-			Args:       strings.Split(deps, " "),
-			Env:        cfg.Env,
-			WorkingDir: snapshot.View().Folder().Filename(),
-		}
-		gocmdRunner := packagesinternal.GetGoCmdRunner(cfg)
-		if _, err := gocmdRunner.Run(ctx, inv); err != nil {
-			return nil, err
-		}
+		err := s.directGoModCommand(ctx, uri, "get", strings.Split(deps, " ")...)
+		return nil, err
 	}
 	return nil, nil
 }
 
+func (s *Server) directGoModCommand(ctx context.Context, uri protocol.DocumentURI, verb string, args ...string) error {
+	view, err := s.session.ViewOf(uri.SpanURI())
+	if err != nil {
+		return err
+	}
+	return view.Snapshot().RunGoCommandDirect(ctx, verb, args)
+}
+
+func (s *Server) runTest(ctx context.Context, snapshot source.Snapshot, funcName string) error {
+	ctx, cancel := context.WithCancel(ctx)
+	defer cancel()
+
+	ew := &eventWriter{ctx: ctx, operation: "test"}
+	wc := s.newProgressWriter(ctx, "test", "running "+funcName, cancel)
+	defer wc.Close()
+
+	messageType := protocol.Info
+	message := "test passed"
+	stderr := io.MultiWriter(ew, wc)
+
+	if err := snapshot.RunGoCommandPiped(ctx, "test", []string{"-run", funcName}, ew, stderr); err != nil {
+		if errors.Is(err, context.Canceled) {
+			return err
+		}
+		messageType = protocol.Error
+		message = "test failed"
+	}
+	return s.client.ShowMessage(ctx, &protocol.ShowMessageParams{
+		Type:    messageType,
+		Message: message,
+	})
+}
+
+func getRunTestArguments(args []interface{}) (string, span.URI, error) {
+	if len(args) != 2 {
+		return "", "", errors.Errorf("expected one test func name and one file path, got %v", args)
+	}
+	funcName, ok := args[0].(string)
+	if !ok {
+		return "", "", errors.Errorf("expected func name to be a string, got %T", args[0])
+	}
+	filename, ok := args[1].(string)
+	if !ok {
+		return "", "", errors.Errorf("expected file to be a string, got %T", args[1])
+	}
+	return funcName, span.URIFromPath(filename), nil
+}
+
 func getGenerateRequest(args []interface{}) (string, bool, error) {
 	if len(args) != 2 {
 		return "", false, errors.Errorf("expected exactly 2 arguments but got %d", len(args))
diff --git a/internal/lsp/completion.go b/internal/lsp/completion.go
index f6d3049..bca9a83 100644
--- a/internal/lsp/completion.go
+++ b/internal/lsp/completion.go
@@ -16,13 +16,13 @@
 )
 
 func (s *Server) completion(ctx context.Context, params *protocol.CompletionParams) (*protocol.CompletionList, error) {
-	snapshot, fh, ok, err := s.beginFileRequest(params.TextDocument.URI, source.UnknownKind)
+	snapshot, fh, ok, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.UnknownKind)
 	if !ok {
 		return nil, err
 	}
 	var candidates []source.CompletionItem
 	var surrounding *source.Selection
-	switch fh.Identity().Kind {
+	switch fh.Kind() {
 	case source.Go:
 		candidates, surrounding, err = source.Completion(ctx, snapshot, fh, params.Position)
 	case source.Mod:
diff --git a/internal/lsp/completion_test.go b/internal/lsp/completion_test.go
index 04dc35e..340dd1c 100644
--- a/internal/lsp/completion_test.go
+++ b/internal/lsp/completion_test.go
@@ -15,13 +15,10 @@
 		opts.DeepCompletion = false
 		opts.Matcher = source.CaseInsensitive
 		opts.UnimportedCompletion = false
-		opts.InsertTextFormat = protocol.PlainTextTextFormat
-		// Only enable literal completions if in the completion literals tests.
-		// TODO(rstambler): Separate out literal completion tests.
-		if strings.Contains(string(src.URI()), "literal") {
-			opts.InsertTextFormat = protocol.SnippetTextFormat
+		opts.InsertTextFormat = protocol.SnippetTextFormat
+		if !strings.Contains(string(src.URI()), "literal") {
+			opts.LiteralCompletions = false
 		}
-
 	})
 	got = tests.FilterBuiltins(src, got)
 	want := expected(t, test, items)
diff --git a/internal/lsp/debug/info.go b/internal/lsp/debug/info.go
index d9077ad..3531353 100644
--- a/internal/lsp/debug/info.go
+++ b/internal/lsp/debug/info.go
@@ -23,7 +23,7 @@
 )
 
 // Version is a manually-updated mechanism for tracking versions.
-var Version = "0.4.1"
+var Version = "0.4.2"
 
 // PrintServerInfo writes HTML debug info to w for the Instance.
 func (i *Instance) PrintServerInfo(ctx context.Context, w io.Writer) {
diff --git a/internal/lsp/debug/serve.go b/internal/lsp/debug/serve.go
index 15c0d30..ea5a544 100644
--- a/internal/lsp/debug/serve.go
+++ b/internal/lsp/debug/serve.go
@@ -5,10 +5,10 @@
 package debug
 
 import (
+	"archive/zip"
 	"bytes"
 	"context"
 	"fmt"
-	"go/token"
 	"html/template"
 	"io"
 	"log"
@@ -18,7 +18,6 @@
 	"os"
 	"path"
 	"path/filepath"
-	"reflect"
 	"runtime"
 	rpprof "runtime/pprof"
 	"strconv"
@@ -34,9 +33,10 @@
 	"golang.org/x/tools/internal/event/export/prometheus"
 	"golang.org/x/tools/internal/event/keys"
 	"golang.org/x/tools/internal/event/label"
+	"golang.org/x/tools/internal/lsp/cache"
 	"golang.org/x/tools/internal/lsp/debug/tag"
 	"golang.org/x/tools/internal/lsp/protocol"
-	"golang.org/x/tools/internal/span"
+	"golang.org/x/tools/internal/lsp/source"
 	"golang.org/x/xerrors"
 )
 
@@ -67,312 +67,211 @@
 
 // State holds debugging information related to the server state.
 type State struct {
-	mu       sync.Mutex
-	caches   objset
-	sessions objset
-	views    objset
-	clients  objset
-	servers  objset
+	mu      sync.Mutex
+	clients []*Client
+	servers []*Server
 }
 
-type ider interface {
-	ID() string
-}
-
-type objset struct {
-	objs []ider
-}
-
-func (s *objset) add(elem ider) {
-	s.objs = append(s.objs, elem)
-}
-
-func (s *objset) drop(elem ider) {
-	var newobjs []ider
-	for _, obj := range s.objs {
-		if obj.ID() != elem.ID() {
-			newobjs = append(newobjs, obj)
+// Caches returns the set of Cache objects currently being served.
+func (st *State) Caches() []*cache.Cache {
+	var caches []*cache.Cache
+	seen := make(map[string]struct{})
+	for _, client := range st.Clients() {
+		cache, ok := client.Session.Cache().(*cache.Cache)
+		if !ok {
+			continue
 		}
+		if _, found := seen[cache.ID()]; found {
+			continue
+		}
+		seen[cache.ID()] = struct{}{}
+		caches = append(caches, cache)
 	}
-	s.objs = newobjs
+	return caches
 }
 
-func (s *objset) find(id string) ider {
-	for _, e := range s.objs {
-		if e.ID() == id {
-			return e
+// Cache returns the Cache that matches the supplied id.
+func (st *State) Cache(id string) *cache.Cache {
+	for _, c := range st.Caches() {
+		if c.ID() == id {
+			return c
 		}
 	}
 	return nil
 }
 
-// Caches returns the set of Cache objects currently being served.
-func (st *State) Caches() []Cache {
-	st.mu.Lock()
-	defer st.mu.Unlock()
-	caches := make([]Cache, len(st.caches.objs))
-	for i, c := range st.caches.objs {
-		caches[i] = c.(Cache)
-	}
-	return caches
-}
-
 // Sessions returns the set of Session objects currently being served.
-func (st *State) Sessions() []Session {
-	st.mu.Lock()
-	defer st.mu.Unlock()
-	sessions := make([]Session, len(st.sessions.objs))
-	for i, s := range st.sessions.objs {
-		sessions[i] = s.(Session)
+func (st *State) Sessions() []*cache.Session {
+	var sessions []*cache.Session
+	for _, client := range st.Clients() {
+		sessions = append(sessions, client.Session)
 	}
 	return sessions
 }
 
+// Session returns the Session that matches the supplied id.
+func (st *State) Session(id string) *cache.Session {
+	for _, s := range st.Sessions() {
+		if s.ID() == id {
+			return s
+		}
+	}
+	return nil
+}
+
 // Views returns the set of View objects currently being served.
-func (st *State) Views() []View {
-	st.mu.Lock()
-	defer st.mu.Unlock()
-	views := make([]View, len(st.views.objs))
-	for i, v := range st.views.objs {
-		views[i] = v.(View)
+func (st *State) Views() []*cache.View {
+	var views []*cache.View
+	for _, s := range st.Sessions() {
+		for _, v := range s.Views() {
+			if cv, ok := v.(*cache.View); ok {
+				views = append(views, cv)
+			}
+		}
 	}
 	return views
 }
 
+// View returns the View that matches the supplied id.
+func (st *State) View(id string) *cache.View {
+	for _, v := range st.Views() {
+		if v.ID() == id {
+			return v
+		}
+	}
+	return nil
+}
+
 // Clients returns the set of Clients currently being served.
-func (st *State) Clients() []Client {
+func (st *State) Clients() []*Client {
 	st.mu.Lock()
 	defer st.mu.Unlock()
-	clients := make([]Client, len(st.clients.objs))
-	for i, c := range st.clients.objs {
-		clients[i] = c.(Client)
-	}
+	clients := make([]*Client, len(st.clients))
+	copy(clients, st.clients)
 	return clients
 }
 
+// View returns the View that matches the supplied id.
+func (st *State) Client(id string) *Client {
+	for _, c := range st.Clients() {
+		if c.Session.ID() == id {
+			return c
+		}
+	}
+	return nil
+}
+
 // Servers returns the set of Servers the instance is currently connected to.
-func (st *State) Servers() []Server {
+func (st *State) Servers() []*Server {
 	st.mu.Lock()
 	defer st.mu.Unlock()
-	servers := make([]Server, len(st.servers.objs))
-	for i, s := range st.servers.objs {
-		servers[i] = s.(Server)
-	}
+	servers := make([]*Server, len(st.servers))
+	copy(servers, st.servers)
 	return servers
 }
 
 // A Client is an incoming connection from a remote client.
-type Client interface {
-	ID() string
-	Session() Session
-	DebugAddress() string
-	Logfile() string
-	ServerID() string
+type Client struct {
+	Session      *cache.Session
+	DebugAddress string
+	Logfile      string
+	GoplsPath    string
+	ServerID     string
 }
 
 // A Server is an outgoing connection to a remote LSP server.
-type Server interface {
-	ID() string
-	DebugAddress() string
-	Logfile() string
-	ClientID() string
-}
-
-// A Cache is an in-memory cache.
-type Cache interface {
-	ID() string
-	FileSet() *token.FileSet
-	MemStats() map[reflect.Type]int
-}
-
-// A Session is an LSP serving session.
-type Session interface {
-	ID() string
-	Cache() Cache
-	Files() []*File
-	File(hash string) *File
-}
-
-// A View is a root directory within a Session.
-type View interface {
-	ID() string
-	Name() string
-	Folder() span.URI
-	Session() Session
-}
-
-// A File is is a file within a session.
-type File struct {
-	Session Session
-	URI     span.URI
-	Data    string
-	Error   error
-	Hash    string
-}
-
-// AddCache adds a cache to the set being served.
-func (st *State) AddCache(cache Cache) {
-	st.mu.Lock()
-	defer st.mu.Unlock()
-	st.caches.add(cache)
-}
-
-// DropCache drops a cache from the set being served.
-func (st *State) DropCache(cache Cache) {
-	st.mu.Lock()
-	defer st.mu.Unlock()
-	st.caches.drop(cache)
-}
-
-// AddSession adds a session to the set being served.
-func (st *State) AddSession(session Session) {
-	st.mu.Lock()
-	defer st.mu.Unlock()
-	st.sessions.add(session)
-}
-
-// DropSession drops a session from the set being served.
-func (st *State) DropSession(session Session) {
-	st.mu.Lock()
-	defer st.mu.Unlock()
-	st.sessions.drop(session)
-}
-
-// AddView adds a view to the set being served.
-func (st *State) AddView(view View) {
-	st.mu.Lock()
-	defer st.mu.Unlock()
-	st.views.add(view)
-}
-
-// DropView drops a view from the set being served.
-func (st *State) DropView(view View) {
-	st.mu.Lock()
-	defer st.mu.Unlock()
-	st.views.drop(view)
+type Server struct {
+	ID           string
+	DebugAddress string
+	Logfile      string
+	GoplsPath    string
+	ClientID     string
 }
 
 // AddClient adds a client to the set being served.
-func (st *State) AddClient(client Client) {
+func (st *State) addClient(session *cache.Session) {
 	st.mu.Lock()
 	defer st.mu.Unlock()
-	st.clients.add(client)
+	st.clients = append(st.clients, &Client{Session: session})
 }
 
-// DropClient adds a client to the set being served.
-func (st *State) DropClient(client Client) {
+// DropClient removes a client from the set being served.
+func (st *State) dropClient(session source.Session) {
 	st.mu.Lock()
 	defer st.mu.Unlock()
-	st.clients.drop(client)
+	for i, c := range st.clients {
+		if c.Session == session {
+			copy(st.clients[i:], st.clients[i+1:])
+			st.clients[len(st.clients)-1] = nil
+			st.clients = st.clients[:len(st.clients)-1]
+			return
+		}
+	}
 }
 
 // AddServer adds a server to the set being queried. In practice, there should
 // be at most one remote server.
-func (st *State) AddServer(server Server) {
+func (st *State) addServer(server *Server) {
 	st.mu.Lock()
 	defer st.mu.Unlock()
-	st.servers.add(server)
+	st.servers = append(st.servers, server)
 }
 
-// DropServer drops a server to the set being queried.
-func (st *State) DropServer(server Server) {
+// DropServer drops a server from the set being queried.
+func (st *State) dropServer(id string) {
 	st.mu.Lock()
 	defer st.mu.Unlock()
-	st.servers.drop(server)
+	for i, s := range st.servers {
+		if s.ID == id {
+			copy(st.servers[i:], st.servers[i+1:])
+			st.servers[len(st.servers)-1] = nil
+			st.servers = st.servers[:len(st.servers)-1]
+			return
+		}
+	}
 }
 
 func (i *Instance) getCache(r *http.Request) interface{} {
-	i.State.mu.Lock()
-	defer i.State.mu.Unlock()
-	id := path.Base(r.URL.Path)
-	c, ok := i.State.caches.find(id).(Cache)
-	if !ok {
-		return nil
-	}
-	result := struct {
-		Cache
-		Sessions []Session
-	}{
-		Cache: c,
-	}
-
-	// now find all the views that belong to this session
-	for _, vd := range i.State.sessions.objs {
-		v := vd.(Session)
-		if v.Cache().ID() == id {
-			result.Sessions = append(result.Sessions, v)
-		}
-	}
-	return result
+	return i.State.Cache(path.Base(r.URL.Path))
 }
 
 func (i *Instance) getSession(r *http.Request) interface{} {
-	i.State.mu.Lock()
-	defer i.State.mu.Unlock()
-	id := path.Base(r.URL.Path)
-	s, ok := i.State.sessions.find(id).(Session)
-	if !ok {
-		return nil
-	}
-	result := struct {
-		Session
-		Views []View
-	}{
-		Session: s,
-	}
-	// now find all the views that belong to this session
-	for _, vd := range i.State.views.objs {
-		v := vd.(View)
-		if v.Session().ID() == id {
-			result.Views = append(result.Views, v)
-		}
-	}
-	return result
+	return i.State.Session(path.Base(r.URL.Path))
 }
 
 func (i Instance) getClient(r *http.Request) interface{} {
-	i.State.mu.Lock()
-	defer i.State.mu.Unlock()
-	id := path.Base(r.URL.Path)
-	c, ok := i.State.clients.find(id).(Client)
-	if !ok {
-		return nil
-	}
-	return c
+	return i.State.Client(path.Base(r.URL.Path))
 }
 
 func (i Instance) getServer(r *http.Request) interface{} {
 	i.State.mu.Lock()
 	defer i.State.mu.Unlock()
 	id := path.Base(r.URL.Path)
-	s, ok := i.State.servers.find(id).(Server)
-	if !ok {
-		return nil
+	for _, s := range i.State.servers {
+		if s.ID == id {
+			return s
+		}
 	}
-	return s
+	return nil
 }
 
 func (i Instance) getView(r *http.Request) interface{} {
-	i.State.mu.Lock()
-	defer i.State.mu.Unlock()
-	id := path.Base(r.URL.Path)
-	v, ok := i.State.views.find(id).(View)
-	if !ok {
-		return nil
-	}
-	return v
+	return i.State.View(path.Base(r.URL.Path))
 }
 
 func (i *Instance) getFile(r *http.Request) interface{} {
-	i.State.mu.Lock()
-	defer i.State.mu.Unlock()
-	hash := path.Base(r.URL.Path)
+	identifier := path.Base(r.URL.Path)
 	sid := path.Base(path.Dir(r.URL.Path))
-	s, ok := i.State.sessions.find(sid).(Session)
-	if !ok {
+	s := i.State.Session(sid)
+	if s == nil {
 		return nil
 	}
-	return s.File(hash)
+	for _, o := range s.Overlays() {
+		if o.Identity().Identifier == identifier {
+			return o
+		}
+	}
+	return nil
 }
 
 func (i *Instance) getInfo(r *http.Request) interface{} {
@@ -512,36 +411,61 @@
 			if mem.HeapAlloc < nextThresholdGiB*1<<30 {
 				continue
 			}
-			i.writeMemoryDebug(nextThresholdGiB)
+			if err := i.writeMemoryDebug(nextThresholdGiB, true); err != nil {
+				event.Error(ctx, "writing memory debug info", err)
+			}
+			if err := i.writeMemoryDebug(nextThresholdGiB, false); err != nil {
+				event.Error(ctx, "writing memory debug info", err)
+			}
 			event.Log(ctx, fmt.Sprintf("Wrote memory usage debug info to %v", os.TempDir()))
 			nextThresholdGiB++
 		}
 	}()
 }
 
-func (i *Instance) writeMemoryDebug(threshold uint64) error {
-	fname := func(t string) string {
-		return fmt.Sprintf("gopls.%d-%dGiB-%s", os.Getpid(), threshold, t)
+func (i *Instance) writeMemoryDebug(threshold uint64, withNames bool) error {
+	suffix := "withnames"
+	if !withNames {
+		suffix = "nonames"
 	}
 
-	f, err := os.Create(filepath.Join(os.TempDir(), fname("heap.pb.gz")))
+	filename := fmt.Sprintf("gopls.%d-%dGiB-%s.zip", os.Getpid(), threshold, suffix)
+	zipf, err := os.OpenFile(filepath.Join(os.TempDir(), filename), os.O_CREATE|os.O_RDWR, 0644)
 	if err != nil {
 		return err
 	}
-	defer f.Close()
+	zipw := zip.NewWriter(zipf)
+
+	f, err := zipw.Create("heap.pb.gz")
+	if err != nil {
+		return err
+	}
 	if err := rpprof.Lookup("heap").WriteTo(f, 0); err != nil {
 		return err
 	}
 
-	f, err = os.Create(filepath.Join(os.TempDir(), fname("goroutines.txt")))
+	f, err = zipw.Create("goroutines.txt")
 	if err != nil {
 		return err
 	}
-	defer f.Close()
 	if err := rpprof.Lookup("goroutine").WriteTo(f, 1); err != nil {
 		return err
 	}
-	return nil
+
+	for _, cache := range i.State.Caches() {
+		cf, err := zipw.Create(fmt.Sprintf("cache-%v.html", cache.ID()))
+		if err != nil {
+			return err
+		}
+		if _, err := cf.Write([]byte(cache.PackageStats(withNames))); err != nil {
+			return err
+		}
+	}
+
+	if err := zipw.Close(); err != nil {
+		return err
+	}
+	return zipf.Close()
 }
 
 func makeGlobalExporter(stderr io.Writer) event.Exporter {
@@ -584,6 +508,34 @@
 		if i.traces != nil {
 			ctx = i.traces.ProcessEvent(ctx, ev, lm)
 		}
+		if event.IsLog(ev) {
+			if s := cache.KeyCreateSession.Get(ev); s != nil {
+				i.State.addClient(s)
+			}
+			if sid := tag.NewServer.Get(ev); sid != "" {
+				i.State.addServer(&Server{
+					ID:           sid,
+					Logfile:      tag.Logfile.Get(ev),
+					DebugAddress: tag.DebugAddress.Get(ev),
+					GoplsPath:    tag.GoplsPath.Get(ev),
+					ClientID:     tag.ClientID.Get(ev),
+				})
+			}
+			if s := cache.KeyShutdownSession.Get(ev); s != nil {
+				i.State.dropClient(s)
+			}
+			if sid := tag.EndServer.Get(ev); sid != "" {
+				i.State.dropServer(sid)
+			}
+			if s := cache.KeyUpdateSession.Get(ev); s != nil {
+				if c := i.State.Client(s.ID()); c != nil {
+					c.DebugAddress = tag.DebugAddress.Get(ev)
+					c.Logfile = tag.Logfile.Get(ev)
+					c.ServerID = tag.ServerID.Get(ev)
+					c.GoplsPath = tag.GoplsPath.Get(ev)
+				}
+			}
+		}
 		return ctx
 	}
 	metrics := metric.Config{}
@@ -625,6 +577,10 @@
 	return commas(strconv.FormatUint(uint64(v), 10))
 }
 
+func fcontent(v []byte) string {
+	return string(v)
+}
+
 var baseTemplate = template.Must(template.New("").Parse(`
 <html>
 <head>
@@ -664,10 +620,11 @@
 {{define "serverlink"}}<a href="/server/{{.}}">Server {{.}}</a>{{end}}
 {{define "sessionlink"}}<a href="/session/{{.}}">Session {{.}}</a>{{end}}
 {{define "viewlink"}}<a href="/view/{{.}}">View {{.}}</a>{{end}}
-{{define "filelink"}}<a href="/file/{{.Session.ID}}/{{.Hash}}">{{.URI}}</a>{{end}}
+{{define "filelink"}}<a href="/file/{{.SessionID}}/{{.Identifier}}">{{.URI}}</a>{{end}}
 `)).Funcs(template.FuncMap{
-	"fuint64": fuint64,
-	"fuint32": fuint32,
+	"fuint64":  fuint64,
+	"fuint32":  fuint32,
+	"fcontent": fcontent,
 	"localAddress": func(s string) string {
 		// Try to translate loopback addresses to localhost, both for cosmetics and
 		// because unspecified ipv6 addresses can break links on Windows.
@@ -700,7 +657,7 @@
 <h2>Views</h2>
 <ul>{{range .State.Views}}<li>{{.Name}} is {{template "viewlink" .ID}} from {{template "sessionlink" .Session.ID}} in {{.Folder}}</li>{{end}}</ul>
 <h2>Clients</h2>
-<ul>{{range .State.Clients}}<li>{{template "clientlink" .ID}}</li>{{end}}</ul>
+<ul>{{range .State.Clients}}<li>{{template "clientlink" .Session.ID}}</li>{{end}}</ul>
 <h2>Servers</h2>
 <ul>{{range .State.Servers}}<li>{{template "serverlink" .ID}}</li>{{end}}</ul>
 {{end}}
@@ -753,15 +710,15 @@
 var cacheTmpl = template.Must(template.Must(baseTemplate.Clone()).Parse(`
 {{define "title"}}Cache {{.ID}}{{end}}
 {{define "body"}}
-<h2>Sessions</h2>
-<ul>{{range .Sessions}}<li>{{template "sessionlink" .ID}}</li>{{end}}</ul>
 <h2>memoize.Store entries</h2>
 <ul>{{range $k,$v := .MemStats}}<li>{{$k}} - {{$v}}</li>{{end}}</ul>
+<h2>Per-package usage - not accurate, for guidance only</h2>
+{{.PackageStats true}}
 {{end}}
 `))
 
 var clientTmpl = template.Must(template.Must(baseTemplate.Clone()).Parse(`
-{{define "title"}}Client {{.ID}}{{end}}
+{{define "title"}}Client {{.Session.ID}}{{end}}
 {{define "body"}}
 Using session: <b>{{template "sessionlink" .Session.ID}}</b><br>
 {{if .DebugAddress}}Debug this client at: <a href="http://{{localAddress .DebugAddress}}">{{localAddress .DebugAddress}}</a><br>{{end}}
@@ -785,8 +742,8 @@
 From: <b>{{template "cachelink" .Cache.ID}}</b><br>
 <h2>Views</h2>
 <ul>{{range .Views}}<li>{{.Name}} is {{template "viewlink" .ID}} in {{.Folder}}</li>{{end}}</ul>
-<h2>Files</h2>
-<ul>{{range .Files}}<li>{{template "filelink" .}}</li>{{end}}</ul>
+<h2>Overlays</h2>
+<ul>{{range .Overlays}}<li>{{template "filelink" .Identity}}</li>{{end}}</ul>
 {{end}}
 `))
 
@@ -797,18 +754,21 @@
 Folder: <b>{{.Folder}}</b><br>
 From: <b>{{template "sessionlink" .Session.ID}}</b><br>
 <h2>Environment</h2>
-<ul>{{range .Env}}<li>{{.}}</li>{{end}}</ul>
+<ul>{{range .Options.Env}}<li>{{.}}</li>{{end}}</ul>
 {{end}}
 `))
 
 var fileTmpl = template.Must(template.Must(baseTemplate.Clone()).Parse(`
-{{define "title"}}File {{.Hash}}{{end}}
+{{define "title"}}Overlay {{.Identity.Identifier}}{{end}}
 {{define "body"}}
-From: <b>{{template "sessionlink" .Session.ID}}</b><br>
-URI: <b>{{.URI}}</b><br>
-Hash: <b>{{.Hash}}</b><br>
-Error: <b>{{.Error}}</b><br>
+{{with .Identity}}
+	From: <b>{{template "sessionlink" .SessionID}}</b><br>
+	URI: <b>{{.URI}}</b><br>
+	Identifier: <b>{{.Identifier}}</b><br>
+	Version: <b>{{.Version}}</b><br>
+	Kind: <b>{{.Kind}}</b><br>
+{{end}}
 <h3>Contents</h3>
-<pre>{{.Data}}</pre>
+<pre>{{fcontent .Data}}</pre>
 {{end}}
 `))
diff --git a/internal/lsp/debug/serve_test.go b/internal/lsp/debug/serve_test.go
deleted file mode 100644
index ff981e9..0000000
--- a/internal/lsp/debug/serve_test.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package debug
-
-import "testing"
-
-type fakeCache struct {
-	Cache
-
-	id string
-}
-
-func (c fakeCache) ID() string {
-	return c.id
-}
-
-func TestState(t *testing.T) {
-	c1 := fakeCache{id: "1"}
-	c2 := fakeCache{id: "2"}
-	c3 := fakeCache{id: "3"}
-
-	var s State
-	s.AddCache(c1)
-	s.AddCache(c2)
-	s.AddCache(c3)
-
-	compareCaches := func(desc string, want []fakeCache) {
-		t.Run(desc, func(t *testing.T) {
-			caches := s.Caches()
-			if gotLen, wantLen := len(caches), len(want); gotLen != wantLen {
-				t.Fatalf("len(Caches) = %d, want %d", gotLen, wantLen)
-			}
-			for i, got := range caches {
-				if got != want[i] {
-					t.Errorf("Caches[%d] = %v, want %v", i, got, want[i])
-				}
-			}
-		})
-	}
-
-	compareCaches("initial load", []fakeCache{c1, c2, c3})
-	s.DropCache(c2)
-	compareCaches("dropped cache 2", []fakeCache{c1, c3})
-	s.DropCache(c2)
-	compareCaches("duplicate drop", []fakeCache{c1, c3})
-	s.AddCache(c2)
-	compareCaches("re-add cache 2", []fakeCache{c1, c3, c2})
-	s.DropCache(c1)
-	s.DropCache(c2)
-	s.DropCache(c3)
-	compareCaches("drop all", []fakeCache{})
-}
diff --git a/internal/lsp/debug/tag/tag.go b/internal/lsp/debug/tag/tag.go
index 7222f5a..e37419e 100644
--- a/internal/lsp/debug/tag/tag.go
+++ b/internal/lsp/debug/tag/tag.go
@@ -32,6 +32,15 @@
 	Port         = keys.NewInt("port", "")
 	Type         = keys.New("type", "")
 	HoverKind    = keys.NewString("hoverkind", "")
+
+	NewServer = keys.NewString("new_server", "A new server was added")
+	EndServer = keys.NewString("end_server", "A server was shut down")
+
+	ServerID     = keys.NewString("server", "The server ID an event is related to")
+	Logfile      = keys.NewString("logfile", "")
+	DebugAddress = keys.NewString("debug_address", "")
+	GoplsPath    = keys.NewString("gopls_path", "")
+	ClientID     = keys.NewString("client_id", "")
 )
 
 var (
diff --git a/internal/lsp/definition.go b/internal/lsp/definition.go
index 3943877..427501b 100644
--- a/internal/lsp/definition.go
+++ b/internal/lsp/definition.go
@@ -12,7 +12,7 @@
 )
 
 func (s *Server) definition(ctx context.Context, params *protocol.DefinitionParams) ([]protocol.Location, error) {
-	snapshot, fh, ok, err := s.beginFileRequest(params.TextDocument.URI, source.Go)
+	snapshot, fh, ok, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.Go)
 	if !ok {
 		return nil, err
 	}
@@ -38,7 +38,7 @@
 }
 
 func (s *Server) typeDefinition(ctx context.Context, params *protocol.TypeDefinitionParams) ([]protocol.Location, error) {
-	snapshot, fh, ok, err := s.beginFileRequest(params.TextDocument.URI, source.Go)
+	snapshot, fh, ok, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.Go)
 	if !ok {
 		return nil, err
 	}
diff --git a/internal/lsp/diagnostics.go b/internal/lsp/diagnostics.go
index 3ec045e..a2e653f 100644
--- a/internal/lsp/diagnostics.go
+++ b/internal/lsp/diagnostics.go
@@ -6,6 +6,7 @@
 
 import (
 	"context"
+	"fmt"
 	"strings"
 	"sync"
 
@@ -69,12 +70,16 @@
 	// Ensure that the reports returned from mod.Diagnostics are only related
 	// to the go.mod file for the module.
 	if len(reports) > 1 {
-		panic("unexpected reports from mod.Diagnostics")
+		panic(fmt.Sprintf("expected 1 report from mod.Diagnostics, got %v: %v", len(reports), reports))
 	}
-	modURI, _ := snapshot.View().ModFiles()
+	modURI := snapshot.View().ModFile()
 	for id, diags := range reports {
+		if id.URI == "" {
+			event.Error(ctx, "missing URI for module diagnostics", fmt.Errorf("empty URI"), tag.Directory.Of(snapshot.View().Folder().Filename()))
+			continue
+		}
 		if id.URI != modURI {
-			panic("unexpected reports from mod.Diagnostics")
+			panic(fmt.Sprintf("expected module diagnostics report for %q, got %q", modURI, id.URI))
 		}
 		key := diagnosticKey{
 			id: id,
@@ -84,7 +89,26 @@
 
 	// Diagnose all of the packages in the workspace.
 	wsPackages, err := snapshot.WorkspacePackages(ctx)
-	if err != nil {
+	if err == source.InconsistentVendoring {
+		item, err := s.client.ShowMessageRequest(ctx, &protocol.ShowMessageRequestParams{
+			Type: protocol.Error,
+			Message: `Inconsistent vendoring detected. Please re-run "go mod vendor".
+See https://github.com/golang/go/issues/39164 for more detail on this issue.`,
+			Actions: []protocol.MessageActionItem{
+				{Title: "go mod vendor"},
+			},
+		})
+		if item == nil || err != nil {
+			return nil, nil
+		}
+		if err := s.directGoModCommand(ctx, protocol.URIFromSpanURI(modURI), "mod", []string{"vendor"}...); err != nil {
+			return nil, &protocol.ShowMessageParams{
+				Type:    protocol.Error,
+				Message: fmt.Sprintf(`"go mod vendor" failed with %v`, err),
+			}
+		}
+		return nil, nil
+	} else if err != nil {
 		event.Error(ctx, "failed to load workspace packages, skipping diagnostics", err, tag.Snapshot.Of(snapshot.ID()), tag.Directory.Of(snapshot.View().Folder()))
 		return nil, nil
 	}
@@ -95,8 +119,8 @@
 			defer wg.Done()
 			// Only run analyses for packages with open files.
 			withAnalyses := alwaysAnalyze
-			for _, fh := range ph.CompiledGoFiles() {
-				if snapshot.IsOpen(fh.File().Identity().URI) {
+			for _, pgh := range ph.CompiledGoFiles() {
+				if snapshot.IsOpen(pgh.File().URI()) {
 					withAnalyses = true
 				}
 			}
diff --git a/internal/lsp/fake/client.go b/internal/lsp/fake/client.go
index 9a2d8c9..4a4c9f2 100644
--- a/internal/lsp/fake/client.go
+++ b/internal/lsp/fake/client.go
@@ -6,6 +6,7 @@
 
 import (
 	"context"
+	"fmt"
 
 	"golang.org/x/tools/internal/lsp/protocol"
 )
@@ -17,6 +18,7 @@
 	OnWorkDoneProgressCreate func(context.Context, *protocol.WorkDoneProgressCreateParams) error
 	OnProgress               func(context.Context, *protocol.ProgressParams) error
 	OnShowMessage            func(context.Context, *protocol.ShowMessageParams) error
+	OnShowMessageRequest     func(context.Context, *protocol.ShowMessageRequestParams) error
 }
 
 // Client is an adapter that converts an *Editor into an LSP Client. It mosly
@@ -34,7 +36,15 @@
 }
 
 func (c *Client) ShowMessageRequest(ctx context.Context, params *protocol.ShowMessageRequestParams) (*protocol.MessageActionItem, error) {
-	return nil, nil
+	if c.hooks.OnShowMessageRequest != nil {
+		if err := c.hooks.OnShowMessageRequest(ctx, params); err != nil {
+			return nil, err
+		}
+	}
+	if len(params.Actions) == 0 || len(params.Actions) > 1 {
+		return nil, fmt.Errorf("fake editor cannot handle multiple action items")
+	}
+	return &params.Actions[0], nil
 }
 
 func (c *Client) LogMessage(ctx context.Context, params *protocol.LogMessageParams) error {
diff --git a/internal/lsp/fake/edit.go b/internal/lsp/fake/edit.go
index 819f1e0..fb28841 100644
--- a/internal/lsp/fake/edit.go
+++ b/internal/lsp/fake/edit.go
@@ -18,6 +18,13 @@
 	Line, Column int
 }
 
+// Range corresponds to protocol.Range, but uses the editor friend Pos
+// instead of UTF-16 oriented protocol.Position
+type Range struct {
+	Start Pos
+	End   Pos
+}
+
 func (p Pos) toProtocolPosition() protocol.Position {
 	return protocol.Position{
 		Line:      float64(p.Line),
@@ -38,6 +45,21 @@
 	Text       string
 }
 
+// Location is the editor friendly equivalent of protocol.Location
+type Location struct {
+	Path  string
+	Range Range
+}
+
+// SymbolInformation is an editor friendly version of
+// protocol.SymbolInformation, with location information transformed to byte
+// offsets. Field names correspond to the protocol type.
+type SymbolInformation struct {
+	Name     string
+	Kind     protocol.SymbolKind
+	Location Location
+}
+
 // NewEdit creates an edit replacing all content between
 // (startLine, startColumn) and (endLine, endColumn) with text.
 func NewEdit(startLine, startColumn, endLine, endColumn int, text string) Edit {
diff --git a/internal/lsp/fake/editor.go b/internal/lsp/fake/editor.go
index 9ae5b84..96a94cc 100644
--- a/internal/lsp/fake/editor.go
+++ b/internal/lsp/fake/editor.go
@@ -22,11 +22,12 @@
 type Editor struct {
 	Config EditorConfig
 
-	// server, client, and sandbox are concurrency safe and written only
+	// Server, client, and sandbox are concurrency safe and written only
 	// at construction time, so do not require synchronization.
-	server  protocol.Server
-	client  *Client
-	sandbox *Sandbox
+	Server     protocol.Server
+	serverConn jsonrpc2.Conn
+	client     *Client
+	sandbox    *Sandbox
 
 	// Since this editor is intended just for testing, we use very coarse
 	// locking.
@@ -80,10 +81,11 @@
 //
 // It returns the editor, so that it may be called as follows:
 //   editor, err := NewEditor(s).Connect(ctx, conn)
-func (e *Editor) Connect(ctx context.Context, conn *jsonrpc2.Conn, hooks ClientHooks) (*Editor, error) {
-	e.server = protocol.ServerDispatcher(conn)
+func (e *Editor) Connect(ctx context.Context, conn jsonrpc2.Conn, hooks ClientHooks) (*Editor, error) {
+	e.serverConn = conn
+	e.Server = protocol.ServerDispatcher(conn)
 	e.client = &Client{editor: e, hooks: hooks}
-	go conn.Run(ctx,
+	conn.Go(ctx,
 		protocol.Handlers(
 			protocol.ClientHandler(e.client,
 				jsonrpc2.MethodNotFound)))
@@ -96,8 +98,8 @@
 
 // Shutdown issues the 'shutdown' LSP notification.
 func (e *Editor) Shutdown(ctx context.Context) error {
-	if e.server != nil {
-		if err := e.server.Shutdown(ctx); err != nil {
+	if e.Server != nil {
+		if err := e.Server.Shutdown(ctx); err != nil {
 			return fmt.Errorf("Shutdown: %w", err)
 		}
 	}
@@ -106,16 +108,34 @@
 
 // Exit issues the 'exit' LSP notification.
 func (e *Editor) Exit(ctx context.Context) error {
-	if e.server != nil {
+	if e.Server != nil {
 		// Not all LSP clients issue the exit RPC, but we do so here to ensure that
 		// we gracefully handle it on multi-session servers.
-		if err := e.server.Exit(ctx); err != nil {
+		if err := e.Server.Exit(ctx); err != nil {
 			return fmt.Errorf("Exit: %w", err)
 		}
 	}
 	return nil
 }
 
+// Close issues the shutdown and exit sequence an editor should.
+func (e *Editor) Close(ctx context.Context) error {
+	if err := e.Shutdown(ctx); err != nil {
+		return err
+	}
+	if err := e.Exit(ctx); err != nil {
+		return err
+	}
+	// called close on the editor should result in the connection closing
+	select {
+	case <-e.serverConn.Done():
+		// connection closed itself
+		return nil
+	case <-ctx.Done():
+		return fmt.Errorf("connection not closed: %w", ctx.Err())
+	}
+}
+
 // Client returns the LSP client for this editor.
 func (e *Editor) Client() *Client {
 	return e.client
@@ -158,8 +178,8 @@
 
 	params.Trace = "messages"
 	// TODO: support workspace folders.
-	if e.server != nil {
-		resp, err := e.server.Initialize(ctx, params)
+	if e.Server != nil {
+		resp, err := e.Server.Initialize(ctx, params)
 		if err != nil {
 			return fmt.Errorf("initialize: %w", err)
 		}
@@ -167,7 +187,7 @@
 		e.serverCapabilities = resp.Capabilities
 		e.mu.Unlock()
 
-		if err := e.server.Initialized(ctx, &protocol.InitializedParams{}); err != nil {
+		if err := e.Server.Initialized(ctx, &protocol.InitializedParams{}); err != nil {
 			return fmt.Errorf("initialized: %w", err)
 		}
 	}
@@ -176,14 +196,14 @@
 }
 
 func (e *Editor) onFileChanges(ctx context.Context, evts []FileEvent) {
-	if e.server == nil {
+	if e.Server == nil {
 		return
 	}
 	var lspevts []protocol.FileEvent
 	for _, evt := range evts {
 		lspevts = append(lspevts, evt.ProtocolEvent)
 	}
-	e.server.DidChangeWatchedFiles(ctx, &protocol.DidChangeWatchedFilesParams{
+	e.Server.DidChangeWatchedFiles(ctx, &protocol.DidChangeWatchedFilesParams{
 		Changes: lspevts,
 	})
 }
@@ -200,8 +220,8 @@
 	item := textDocumentItem(e.sandbox.Workdir, buf)
 	e.mu.Unlock()
 
-	if e.server != nil {
-		if err := e.server.DidOpen(ctx, &protocol.DidOpenTextDocumentParams{
+	if e.Server != nil {
+		if err := e.Server.DidOpen(ctx, &protocol.DidOpenTextDocumentParams{
 			TextDocument: item,
 		}); err != nil {
 			return fmt.Errorf("DidOpen: %w", err)
@@ -242,8 +262,8 @@
 	item := textDocumentItem(e.sandbox.Workdir, buf)
 	e.mu.Unlock()
 
-	if e.server != nil {
-		if err := e.server.DidOpen(ctx, &protocol.DidOpenTextDocumentParams{
+	if e.Server != nil {
+		if err := e.Server.DidOpen(ctx, &protocol.DidOpenTextDocumentParams{
 			TextDocument: item,
 		}); err != nil {
 			return fmt.Errorf("DidOpen: %w", err)
@@ -263,8 +283,8 @@
 	delete(e.buffers, path)
 	e.mu.Unlock()
 
-	if e.server != nil {
-		if err := e.server.DidClose(ctx, &protocol.DidCloseTextDocumentParams{
+	if e.Server != nil {
+		if err := e.Server.DidClose(ctx, &protocol.DidCloseTextDocumentParams{
 			TextDocument: e.textDocumentIdentifier(path),
 		}); err != nil {
 			return fmt.Errorf("DidClose: %w", err)
@@ -288,7 +308,10 @@
 	if err := e.FormatBuffer(ctx, path); err != nil {
 		return fmt.Errorf("formatting before save: %w", err)
 	}
+	return e.SaveBufferWithoutActions(ctx, path)
+}
 
+func (e *Editor) SaveBufferWithoutActions(ctx context.Context, path string) error {
 	e.mu.Lock()
 	buf, ok := e.buffers[path]
 	if !ok {
@@ -304,8 +327,8 @@
 	e.mu.Unlock()
 
 	docID := e.textDocumentIdentifier(buf.path)
-	if e.server != nil {
-		if err := e.server.WillSave(ctx, &protocol.WillSaveTextDocumentParams{
+	if e.Server != nil {
+		if err := e.Server.WillSave(ctx, &protocol.WillSaveTextDocumentParams{
 			TextDocument: docID,
 			Reason:       protocol.Manual,
 		}); err != nil {
@@ -315,7 +338,7 @@
 	if err := e.sandbox.Workdir.WriteFile(ctx, path, content); err != nil {
 		return fmt.Errorf("writing %q: %w", path, err)
 	}
-	if e.server != nil {
+	if e.Server != nil {
 		params := &protocol.DidSaveTextDocumentParams{
 			TextDocument: protocol.VersionedTextDocumentIdentifier{
 				Version:                float64(buf.version),
@@ -325,7 +348,7 @@
 		if includeText {
 			params.Text = &content
 		}
-		if err := e.server.DidSave(ctx, params); err != nil {
+		if err := e.Server.DidSave(ctx, params); err != nil {
 			return fmt.Errorf("DidSave: %w", err)
 		}
 	}
@@ -493,8 +516,8 @@
 		},
 		ContentChanges: evts,
 	}
-	if e.server != nil {
-		if err := e.server.DidChange(ctx, params); err != nil {
+	if e.Server != nil {
+		if err := e.Server.DidChange(ctx, params); err != nil {
 			return fmt.Errorf("DidChange: %w", err)
 		}
 	}
@@ -511,7 +534,7 @@
 	params.TextDocument.URI = e.sandbox.Workdir.URI(path)
 	params.Position = pos.toProtocolPosition()
 
-	resp, err := e.server.Definition(ctx, params)
+	resp, err := e.Server.Definition(ctx, params)
 	if err != nil {
 		return "", Pos{}, fmt.Errorf("definition: %w", err)
 	}
@@ -526,6 +549,38 @@
 	return newPath, newPos, nil
 }
 
+// Symbol performs a workspace symbol search using query
+func (e *Editor) Symbol(ctx context.Context, query string) ([]SymbolInformation, error) {
+	params := &protocol.WorkspaceSymbolParams{}
+	params.Query = query
+
+	resp, err := e.Server.Symbol(ctx, params)
+	if err != nil {
+		return nil, fmt.Errorf("symbol: %w", err)
+	}
+	var res []SymbolInformation
+	for _, si := range resp {
+		ploc := si.Location
+		path := e.sandbox.Workdir.URIToPath(ploc.URI)
+		start := fromProtocolPosition(ploc.Range.Start)
+		end := fromProtocolPosition(ploc.Range.End)
+		rnge := Range{
+			Start: start,
+			End:   end,
+		}
+		loc := Location{
+			Path:  path,
+			Range: rnge,
+		}
+		res = append(res, SymbolInformation{
+			Name:     si.Name,
+			Kind:     si.Kind,
+			Location: loc,
+		})
+	}
+	return res, nil
+}
+
 // OrganizeImports requests and performs the source.organizeImports codeAction.
 func (e *Editor) OrganizeImports(ctx context.Context, path string) error {
 	return e.codeAction(ctx, path, nil, protocol.SourceOrganizeImports)
@@ -537,7 +592,7 @@
 }
 
 func (e *Editor) codeAction(ctx context.Context, path string, diagnostics []protocol.Diagnostic, only ...protocol.CodeActionKind) error {
-	if e.server == nil {
+	if e.Server == nil {
 		return nil
 	}
 	params := &protocol.CodeActionParams{}
@@ -546,7 +601,7 @@
 	if diagnostics != nil {
 		params.Context.Diagnostics = diagnostics
 	}
-	actions, err := e.server.CodeAction(ctx, params)
+	actions, err := e.Server.CodeAction(ctx, params)
 	if err != nil {
 		return fmt.Errorf("textDocument/codeAction: %w", err)
 	}
@@ -588,7 +643,7 @@
 
 // FormatBuffer gofmts a Go file.
 func (e *Editor) FormatBuffer(ctx context.Context, path string) error {
-	if e.server == nil {
+	if e.Server == nil {
 		return nil
 	}
 	e.mu.Lock()
@@ -596,7 +651,7 @@
 	e.mu.Unlock()
 	params := &protocol.DocumentFormattingParams{}
 	params.TextDocument.URI = e.sandbox.Workdir.URI(path)
-	resp, err := e.server.Formatting(ctx, params)
+	resp, err := e.Server.Formatting(ctx, params)
 	if err != nil {
 		return fmt.Errorf("textDocument/formatting: %w", err)
 	}
@@ -627,7 +682,7 @@
 // change, so must be followed by a call to Workdir.CheckForFileChanges once
 // the generate command has completed.
 func (e *Editor) RunGenerate(ctx context.Context, dir string) error {
-	if e.server == nil {
+	if e.Server == nil {
 		return nil
 	}
 	absDir := e.sandbox.Workdir.filePath(dir)
@@ -635,7 +690,7 @@
 		Command:   "generate",
 		Arguments: []interface{}{absDir, false},
 	}
-	if _, err := e.server.ExecuteCommand(ctx, params); err != nil {
+	if _, err := e.Server.ExecuteCommand(ctx, params); err != nil {
 		return fmt.Errorf("running generate: %v", err)
 	}
 	// Unfortunately we can't simply poll the workdir for file changes here,
@@ -647,7 +702,7 @@
 
 // CodeLens execute a codelens request on the server.
 func (e *Editor) CodeLens(ctx context.Context, path string) ([]protocol.CodeLens, error) {
-	if e.server == nil {
+	if e.Server == nil {
 		return nil, nil
 	}
 	e.mu.Lock()
@@ -659,9 +714,58 @@
 	params := &protocol.CodeLensParams{
 		TextDocument: e.textDocumentIdentifier(path),
 	}
-	lens, err := e.server.CodeLens(ctx, params)
+	lens, err := e.Server.CodeLens(ctx, params)
 	if err != nil {
 		return nil, err
 	}
 	return lens, nil
 }
+
+// CodeAction executes a codeAction request on the server.
+func (e *Editor) CodeAction(ctx context.Context, path string) ([]protocol.CodeAction, error) {
+	if e.Server == nil {
+		return nil, nil
+	}
+	e.mu.Lock()
+	_, ok := e.buffers[path]
+	e.mu.Unlock()
+	if !ok {
+		return nil, fmt.Errorf("buffer %q is not open", path)
+	}
+	params := &protocol.CodeActionParams{
+		TextDocument: e.textDocumentIdentifier(path),
+	}
+	lens, err := e.Server.CodeAction(ctx, params)
+	if err != nil {
+		return nil, err
+	}
+	return lens, nil
+}
+
+// Hover triggers a hover at the given position in an open buffer.
+func (e *Editor) Hover(ctx context.Context, path string, pos Pos) (*protocol.MarkupContent, Pos, error) {
+	if err := e.checkBufferPosition(path, pos); err != nil {
+		return nil, Pos{}, err
+	}
+	params := &protocol.HoverParams{}
+	params.TextDocument.URI = e.sandbox.Workdir.URI(path)
+	params.Position = pos.toProtocolPosition()
+
+	resp, err := e.Server.Hover(ctx, params)
+	if err != nil {
+		return nil, Pos{}, fmt.Errorf("hover: %w", err)
+	}
+	if resp == nil {
+		return nil, Pos{}, nil
+	}
+	return &resp.Contents, fromProtocolPosition(resp.Range.Start), nil
+}
+
+func (e *Editor) DocumentLink(ctx context.Context, path string) ([]protocol.DocumentLink, error) {
+	if e.Server == nil {
+		return nil, nil
+	}
+	params := &protocol.DocumentLinkParams{}
+	params.TextDocument.URI = e.sandbox.Workdir.URI(path)
+	return e.Server.DocumentLink(ctx, params)
+}
diff --git a/internal/lsp/fake/sandbox.go b/internal/lsp/fake/sandbox.go
index cee2993..a96c957 100644
--- a/internal/lsp/fake/sandbox.go
+++ b/internal/lsp/fake/sandbox.go
@@ -13,6 +13,7 @@
 	"strings"
 
 	"golang.org/x/tools/internal/gocommand"
+	"golang.org/x/tools/internal/testenv"
 	"golang.org/x/tools/txtar"
 )
 
@@ -101,12 +102,16 @@
 // GoEnv returns the default environment variables that can be used for
 // invoking Go commands in the sandbox.
 func (sb *Sandbox) GoEnv() []string {
-	return []string{
+	vars := []string{
 		"GOPATH=" + sb.GOPATH(),
 		"GOPROXY=" + sb.Proxy.GOPROXY(),
 		"GO111MODULE=",
 		"GOSUMDB=off",
 	}
+	if testenv.Go1Point() >= 5 {
+		vars = append(vars, "GOMODCACHE=")
+	}
+	return vars
 }
 
 // RunGoCommand executes a go command in the sandbox.
diff --git a/internal/lsp/folding_range.go b/internal/lsp/folding_range.go
index 5bae8f5..2de472e 100644
--- a/internal/lsp/folding_range.go
+++ b/internal/lsp/folding_range.go
@@ -8,7 +8,7 @@
 )
 
 func (s *Server) foldingRange(ctx context.Context, params *protocol.FoldingRangeParams) ([]protocol.FoldingRange, error) {
-	snapshot, fh, ok, err := s.beginFileRequest(params.TextDocument.URI, source.Go)
+	snapshot, fh, ok, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.Go)
 	if !ok {
 		return nil, err
 	}
diff --git a/internal/lsp/format.go b/internal/lsp/format.go
index f9bf5aa..5251d59 100644
--- a/internal/lsp/format.go
+++ b/internal/lsp/format.go
@@ -13,11 +13,11 @@
 )
 
 func (s *Server) formatting(ctx context.Context, params *protocol.DocumentFormattingParams) ([]protocol.TextEdit, error) {
-	snapshot, fh, ok, err := s.beginFileRequest(params.TextDocument.URI, source.UnknownKind)
+	snapshot, fh, ok, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.UnknownKind)
 	if !ok {
 		return nil, err
 	}
-	switch fh.Identity().Kind {
+	switch fh.Kind() {
 	case source.Mod:
 		return mod.Format(ctx, snapshot, fh)
 	case source.Go:
diff --git a/internal/lsp/general.go b/internal/lsp/general.go
index 36f7ba3..c2051ce 100644
--- a/internal/lsp/general.go
+++ b/internal/lsp/general.go
@@ -9,6 +9,7 @@
 	"context"
 	"errors"
 	"fmt"
+	"io"
 	"os"
 	"path"
 
@@ -268,7 +269,7 @@
 // it to a snapshot.
 // We don't want to return errors for benign conditions like wrong file type,
 // so callers should do if !ok { return err } rather than if err != nil.
-func (s *Server) beginFileRequest(pURI protocol.DocumentURI, expectKind source.FileKind) (source.Snapshot, source.FileHandle, bool, error) {
+func (s *Server) beginFileRequest(ctx context.Context, pURI protocol.DocumentURI, expectKind source.FileKind) (source.Snapshot, source.FileHandle, bool, error) {
 	uri := pURI.SpanURI()
 	if !uri.IsFile() {
 		// Not a file URI. Stop processing the request, but don't return an error.
@@ -279,11 +280,11 @@
 		return nil, nil, false, err
 	}
 	snapshot := view.Snapshot()
-	fh, err := snapshot.GetFile(uri)
+	fh, err := snapshot.GetFile(ctx, uri)
 	if err != nil {
 		return nil, nil, false, err
 	}
-	if expectKind != source.UnknownKind && fh.Identity().Kind != expectKind {
+	if expectKind != source.UnknownKind && fh.Kind() != expectKind {
 		// Wrong kind of file. Nothing to do.
 		return nil, nil, false, nil
 	}
@@ -304,16 +305,18 @@
 	return nil
 }
 
-// ServerExitFunc is used to exit when requested by the client. It is mutable
-// for testing purposes.
-var ServerExitFunc = os.Exit
-
 func (s *Server) exit(ctx context.Context) error {
 	s.stateMu.Lock()
 	defer s.stateMu.Unlock()
+
+	// TODO: We need a better way to find the conn close method.
+	s.client.(io.Closer).Close()
+
 	if s.state != serverShutDown {
-		ServerExitFunc(1)
+		// TODO: We should be able to do better than this.
+		os.Exit(1)
 	}
-	ServerExitFunc(0)
+	// we don't terminate the process on a normal exit, we just allow it to
+	// close naturally if needed after the connection is closed.
 	return nil
 }
diff --git a/internal/lsp/generate.go b/internal/lsp/generate.go
index f434bc3..d850dec 100644
--- a/internal/lsp/generate.go
+++ b/internal/lsp/generate.go
@@ -9,64 +9,66 @@
 	"io"
 
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/gocommand"
 	"golang.org/x/tools/internal/lsp/debug/tag"
 	"golang.org/x/tools/internal/lsp/protocol"
-	"golang.org/x/xerrors"
+	"golang.org/x/tools/internal/span"
+	errors "golang.org/x/xerrors"
 )
 
 // GenerateWorkDoneTitle is the title used in progress reporting for go
 // generate commands. It is exported for testing purposes.
 const GenerateWorkDoneTitle = "generate"
 
-func (s *Server) runGenerate(ctx context.Context, dir string, recursive bool) {
+func (s *Server) runGenerate(ctx context.Context, dir string, recursive bool) error {
 	ctx, cancel := context.WithCancel(ctx)
 	defer cancel()
 
-	er := &eventWriter{ctx: ctx}
-	wc := s.newProgressWriter(ctx, cancel)
+	er := &eventWriter{ctx: ctx, operation: "generate"}
+	wc := s.newProgressWriter(ctx, GenerateWorkDoneTitle, "running go generate", cancel)
 	defer wc.Close()
 	args := []string{"-x"}
 	if recursive {
 		args = append(args, "./...")
 	}
-	inv := &gocommand.Invocation{
-		Verb:       "generate",
-		Args:       args,
-		Env:        s.session.Options().Env,
-		WorkingDir: dir,
-	}
+
 	stderr := io.MultiWriter(er, wc)
-	err := inv.RunPiped(ctx, er, stderr)
+	uri := span.URIFromPath(dir)
+	view, err := s.session.ViewOf(uri)
 	if err != nil {
-		event.Error(ctx, "generate: command error", err, tag.Directory.Of(dir))
-		if !xerrors.Is(err, context.Canceled) {
-			s.client.ShowMessage(ctx, &protocol.ShowMessageParams{
-				Type:    protocol.Error,
-				Message: "go generate exited with an error, check gopls logs",
-			})
-		}
+		return err
 	}
+	snapshot := view.Snapshot()
+	if err := snapshot.RunGoCommandPiped(ctx, "generate", args, er, stderr); err != nil {
+		if errors.Is(err, context.Canceled) {
+			return nil
+		}
+		event.Error(ctx, "generate: command error", err, tag.Directory.Of(dir))
+		return s.client.ShowMessage(ctx, &protocol.ShowMessageParams{
+			Type:    protocol.Error,
+			Message: "go generate exited with an error, check gopls logs",
+		})
+	}
+	return nil
 }
 
 // eventWriter writes every incoming []byte to
 // event.Print with the operation=generate tag
 // to distinguish its logs from others.
 type eventWriter struct {
-	ctx context.Context
+	ctx       context.Context
+	operation string
 }
 
 func (ew *eventWriter) Write(p []byte) (n int, err error) {
-	event.Log(ew.ctx, string(p), tag.Operation.Of("generate"))
+	event.Log(ew.ctx, string(p), tag.Operation.Of(ew.operation))
 	return len(p), nil
 }
 
 // newProgressWriter returns an io.WriterCloser that can be used
-// to report progress on the "go generate" command based on the
-// client capabilities.
-func (s *Server) newProgressWriter(ctx context.Context, cancel func()) io.WriteCloser {
+// to report progress on a command based on the client capabilities.
+func (s *Server) newProgressWriter(ctx context.Context, title, message string, cancel func()) io.WriteCloser {
 	if s.supportsWorkDoneProgress {
-		wd := s.StartWork(ctx, GenerateWorkDoneTitle, "running go generate", cancel)
+		wd := s.StartWork(ctx, title, message, cancel)
 		return &workDoneWriter{ctx, wd}
 	}
 	mw := &messageWriter{ctx, cancel, s.client}
diff --git a/internal/lsp/highlight.go b/internal/lsp/highlight.go
index ef72c54..5e5a985 100644
--- a/internal/lsp/highlight.go
+++ b/internal/lsp/highlight.go
@@ -14,7 +14,7 @@
 )
 
 func (s *Server) documentHighlight(ctx context.Context, params *protocol.DocumentHighlightParams) ([]protocol.DocumentHighlight, error) {
-	snapshot, fh, ok, err := s.beginFileRequest(params.TextDocument.URI, source.Go)
+	snapshot, fh, ok, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.Go)
 	if !ok {
 		return nil, err
 	}
diff --git a/internal/lsp/hover.go b/internal/lsp/hover.go
index 32af7e2..e9eaddf 100644
--- a/internal/lsp/hover.go
+++ b/internal/lsp/hover.go
@@ -13,11 +13,11 @@
 )
 
 func (s *Server) hover(ctx context.Context, params *protocol.HoverParams) (*protocol.Hover, error) {
-	snapshot, fh, ok, err := s.beginFileRequest(params.TextDocument.URI, source.UnknownKind)
+	snapshot, fh, ok, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.UnknownKind)
 	if !ok {
 		return nil, err
 	}
-	switch fh.Identity().Kind {
+	switch fh.Kind() {
 	case source.Mod:
 		return mod.Hover(ctx, snapshot, fh, params.Position)
 	case source.Go:
diff --git a/internal/lsp/implementation.go b/internal/lsp/implementation.go
index e4b3650..9250232 100644
--- a/internal/lsp/implementation.go
+++ b/internal/lsp/implementation.go
@@ -12,7 +12,7 @@
 )
 
 func (s *Server) implementation(ctx context.Context, params *protocol.ImplementationParams) ([]protocol.Location, error) {
-	snapshot, fh, ok, err := s.beginFileRequest(params.TextDocument.URI, source.Go)
+	snapshot, fh, ok, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.Go)
 	if !ok {
 		return nil, err
 	}
diff --git a/internal/lsp/link.go b/internal/lsp/link.go
index ddb4e76..7218b3c 100644
--- a/internal/lsp/link.go
+++ b/internal/lsp/link.go
@@ -25,11 +25,11 @@
 )
 
 func (s *Server) documentLink(ctx context.Context, params *protocol.DocumentLinkParams) (links []protocol.DocumentLink, err error) {
-	snapshot, fh, ok, err := s.beginFileRequest(params.TextDocument.URI, source.UnknownKind)
+	snapshot, fh, ok, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.UnknownKind)
 	if !ok {
 		return nil, err
 	}
-	switch fh.Identity().Kind {
+	switch fh.Kind() {
 	case source.Mod:
 		links, err = modLinks(ctx, snapshot, fh)
 	case source.Go:
@@ -37,7 +37,7 @@
 	}
 	// Don't return errors for document links.
 	if err != nil {
-		event.Error(ctx, "failed to compute document links", err, tag.URI.Of(fh.Identity().URI))
+		event.Error(ctx, "failed to compute document links", err, tag.URI.Of(fh.URI()))
 		return nil, nil
 	}
 	return links, nil
@@ -46,12 +46,20 @@
 func modLinks(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle) ([]protocol.DocumentLink, error) {
 	view := snapshot.View()
 
-	file, m, err := snapshot.ModHandle(ctx, fh).Parse(ctx)
+	pmh, err := snapshot.ParseModHandle(ctx, fh)
+	if err != nil {
+		return nil, err
+	}
+	file, m, _, err := pmh.Parse(ctx)
 	if err != nil {
 		return nil, err
 	}
 	var links []protocol.DocumentLink
 	for _, req := range file.Require {
+		// See golang/go#36998: don't link to modules matching GOPRIVATE.
+		if snapshot.View().IsGoPrivatePath(req.Mod.Path) {
+			continue
+		}
 		dep := []byte(req.Mod.Path)
 		s, e := req.Syntax.Start.Byte, req.Syntax.End.Byte
 		i := bytes.Index(m.Content[s:e], dep)
@@ -100,7 +108,8 @@
 	if err != nil {
 		return nil, err
 	}
-	file, _, m, _, err := view.Session().Cache().ParseGoHandle(fh, source.ParseFull).Parse(ctx)
+	pgh := view.Session().Cache().ParseGoHandle(ctx, fh, source.ParseFull)
+	file, _, m, _, err := pgh.Parse(ctx)
 	if err != nil {
 		return nil, err
 	}
@@ -127,6 +136,10 @@
 		if err != nil {
 			continue
 		}
+		// See golang/go#36998: don't link to modules matching GOPRIVATE.
+		if view.IsGoPrivatePath(target) {
+			continue
+		}
 		if mod, version, ok := moduleAtVersion(ctx, target, ph); ok && strings.ToLower(view.Options().LinkTarget) == "pkg.go.dev" {
 			target = strings.Replace(target, mod, mod+"@"+version, 1)
 		}
diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go
index ff42115..d168076 100644
--- a/internal/lsp/lsp_test.go
+++ b/internal/lsp/lsp_test.go
@@ -12,7 +12,6 @@
 	"os/exec"
 	"path/filepath"
 	"sort"
-	"strings"
 	"testing"
 
 	"golang.org/x/tools/go/packages/packagestest"
@@ -55,25 +54,21 @@
 		options := tests.DefaultOptions()
 		session.SetOptions(options)
 		options.Env = datum.Config.Env
-		v, snapshot, err := session.NewView(ctx, datum.Config.Dir, span.URIFromPath(datum.Config.Dir), options)
+		view, snapshot, err := session.NewView(ctx, datum.Config.Dir, span.URIFromPath(datum.Config.Dir), options)
 		if err != nil {
 			t.Fatal(err)
 		}
 
+		defer view.Shutdown(ctx)
+
 		// Enable type error analyses for tests.
 		// TODO(golang/go#38212): Delete this once they are enabled by default.
 		tests.EnableAllAnalyzers(snapshot, &options)
-		v.SetOptions(ctx, options)
+		view.SetOptions(ctx, options)
 
-		// Check to see if the -modfile flag is available, this is basically a check
-		// to see if the go version >= 1.14. Otherwise, the modfile specific tests
-		// will always fail if this flag is not available.
-		for _, flag := range v.Snapshot().Config(ctx).BuildFlags {
-			if strings.Contains(flag, "-modfile=") {
-				datum.ModfileFlagAvailable = true
-				break
-			}
-		}
+		// Only run the -modfile specific tests in module mode with Go 1.14 or above.
+		datum.ModfileFlagAvailable = view.ModFile() != "" && testenv.Go1Point() >= 14
+
 		var modifications []source.FileModification
 		for filename, content := range datum.Config.Overlay {
 			kind := source.DetectLanguage("", filename)
@@ -392,19 +387,16 @@
 			r.diagnostics[key.id.URI] = diags
 		}
 	}
-	var diag *source.Diagnostic
+	var diagnostics []protocol.Diagnostic
 	for _, d := range r.diagnostics[uri] {
 		// Compare the start positions rather than the entire range because
 		// some diagnostics have a range with the same start and end position (8:1-8:1).
 		// The current marker functionality prevents us from having a range of 0 length.
 		if protocol.ComparePosition(d.Range.Start, rng.Start) == 0 {
-			diag = d
+			diagnostics = append(diagnostics, toProtocolDiagnostics([]*source.Diagnostic{d})...)
 			break
 		}
 	}
-	if diag == nil {
-		t.Fatalf("could not get any suggested fixes for %v", spn)
-	}
 	codeActionKinds := []protocol.CodeActionKind{}
 	for _, k := range actionKinds {
 		codeActionKinds = append(codeActionKinds, protocol.CodeActionKind(k))
@@ -413,28 +405,30 @@
 		TextDocument: protocol.TextDocumentIdentifier{
 			URI: protocol.URIFromSpanURI(uri),
 		},
+		Range: rng,
 		Context: protocol.CodeActionContext{
 			Only:        codeActionKinds,
-			Diagnostics: toProtocolDiagnostics([]*source.Diagnostic{diag}),
+			Diagnostics: diagnostics,
 		},
 	})
 	if err != nil {
 		t.Fatal(err)
 	}
-	// TODO: This test should probably be able to handle multiple code actions.
-	if len(actions) == 0 {
-		t.Fatal("no code actions returned")
+	// Hack: We assume that we only get one code action per range.
+	// TODO(rstambler): Support multiple code actions per test.
+	if len(actions) == 0 || len(actions) > 1 {
+		t.Fatalf("unexpected number of code actions, want 1, got %v", len(actions))
 	}
 	res, err := applyWorkspaceEdits(r, actions[0].Edit)
 	if err != nil {
 		t.Fatal(err)
 	}
 	for u, got := range res {
-		fixed := string(r.data.Golden("suggestedfix_"+tests.SpanName(spn), u.Filename(), func() ([]byte, error) {
+		want := string(r.data.Golden("suggestedfix_"+tests.SpanName(spn), u.Filename(), func() ([]byte, error) {
 			return []byte(got), nil
 		}))
-		if fixed != got {
-			t.Errorf("suggested fixes failed for %s, expected:\n%#v\ngot:\n%#v", u.Filename(), fixed, got)
+		if want != got {
+			t.Errorf("suggested fixes failed for %s:\n%s", u.Filename(), tests.Diff(want, got))
 		}
 	}
 }
diff --git a/internal/lsp/lsprpc/lsprpc.go b/internal/lsp/lsprpc/lsprpc.go
index 03721cd..b43e6e3 100644
--- a/internal/lsp/lsprpc/lsprpc.go
+++ b/internal/lsp/lsprpc/lsprpc.go
@@ -17,12 +17,13 @@
 	"sync/atomic"
 	"time"
 
-	"golang.org/x/sync/errgroup"
 	"golang.org/x/tools/internal/event"
+	"golang.org/x/tools/internal/gocommand"
 	"golang.org/x/tools/internal/jsonrpc2"
 	"golang.org/x/tools/internal/lsp"
 	"golang.org/x/tools/internal/lsp/cache"
 	"golang.org/x/tools/internal/lsp/debug"
+	"golang.org/x/tools/internal/lsp/debug/tag"
 	"golang.org/x/tools/internal/lsp/protocol"
 )
 
@@ -31,7 +32,7 @@
 const AutoNetwork = "auto"
 
 // Unique identifiers for client/server.
-var clientIndex, serverIndex int64
+var serverIndex int64
 
 // The StreamServer type is a jsonrpc2.StreamServer that handles incoming
 // streams as a new LSP session, using a shared cache.
@@ -49,79 +50,11 @@
 	return &StreamServer{cache: cache}
 }
 
-// debugInstance is the common functionality shared between client and server
-// gopls instances.
-type debugInstance struct {
-	id           string
-	debugAddress string
-	logfile      string
-	goplsPath    string
-}
-
-func (d debugInstance) ID() string {
-	return d.id
-}
-
-func (d debugInstance) DebugAddress() string {
-	return d.debugAddress
-}
-
-func (d debugInstance) Logfile() string {
-	return d.logfile
-}
-
-func (d debugInstance) GoplsPath() string {
-	return d.goplsPath
-}
-
-// A debugServer is held by the client to identity the remove server to which
-// it is connected.
-type debugServer struct {
-	debugInstance
-	// clientID is the id of this client on the server.
-	clientID string
-}
-
-func (s debugServer) ClientID() string {
-	return s.clientID
-}
-
-// A debugClient is held by the server to identify an incoming client
-// connection.
-type debugClient struct {
-	debugInstance
-	// session is the session serving this client.
-	session *cache.Session
-	// serverID is this id of this server on the client.
-	serverID string
-}
-
-func (c debugClient) Session() debug.Session {
-	return cache.DebugSession{Session: c.session}
-}
-
-func (c debugClient) ServerID() string {
-	return c.serverID
-}
-
 // ServeStream implements the jsonrpc2.StreamServer interface, by handling
 // incoming streams using a new lsp server.
-func (s *StreamServer) ServeStream(ctx context.Context, stream jsonrpc2.Stream) error {
-	index := atomic.AddInt64(&clientIndex, 1)
-
-	conn := jsonrpc2.NewConn(stream)
+func (s *StreamServer) ServeStream(ctx context.Context, conn jsonrpc2.Conn) error {
 	client := protocol.ClientDispatcher(conn)
 	session := s.cache.NewSession(ctx)
-	dc := &debugClient{
-		debugInstance: debugInstance{
-			id: strconv.FormatInt(index, 10),
-		},
-		session: session,
-	}
-	if di := debug.GetInstance(ctx); di != nil {
-		di.State.AddClient(dc)
-		defer di.State.DropClient(dc)
-	}
 	server := s.serverForTest
 	if server == nil {
 		server = lsp.NewServer(session, client)
@@ -140,11 +73,13 @@
 		executable = ""
 	}
 	ctx = protocol.WithClient(ctx, client)
-	return conn.Run(ctx,
+	conn.Go(ctx,
 		protocol.Handlers(
-			handshaker(dc, executable,
+			handshaker(session, executable,
 				protocol.ServerHandler(server,
 					jsonrpc2.MethodNotFound))))
+	<-conn.Done()
+	return conn.Err()
 }
 
 // A Forwarder is a jsonrpc2.StreamServer that handles an LSP stream by
@@ -233,8 +168,8 @@
 	if err != nil {
 		return nil, fmt.Errorf("dialing remote: %w", err)
 	}
-	serverConn := jsonrpc2.NewConn(jsonrpc2.NewHeaderStream(netConn, netConn))
-	go serverConn.Run(ctx, jsonrpc2.MethodNotFound)
+	serverConn := jsonrpc2.NewConn(jsonrpc2.NewHeaderStream(netConn))
+	serverConn.Go(ctx, jsonrpc2.MethodNotFound)
 	var state ServerState
 	if err := protocol.Call(ctx, serverConn, sessionsMethod, nil, &state); err != nil {
 		return nil, fmt.Errorf("querying server state: %w", err)
@@ -244,32 +179,27 @@
 
 // ServeStream dials the forwarder remote and binds the remote to serve the LSP
 // on the incoming stream.
-func (f *Forwarder) ServeStream(ctx context.Context, stream jsonrpc2.Stream) error {
-	clientConn := jsonrpc2.NewConn(stream)
+func (f *Forwarder) ServeStream(ctx context.Context, clientConn jsonrpc2.Conn) error {
 	client := protocol.ClientDispatcher(clientConn)
 
 	netConn, err := f.connectToRemote(ctx)
 	if err != nil {
 		return fmt.Errorf("forwarder: connecting to remote: %w", err)
 	}
-	serverConn := jsonrpc2.NewConn(jsonrpc2.NewHeaderStream(netConn, netConn))
+	serverConn := jsonrpc2.NewConn(jsonrpc2.NewHeaderStream(netConn))
 	server := protocol.ServerDispatcher(serverConn)
 
 	// Forward between connections.
-	g, ctx := errgroup.WithContext(ctx)
-	g.Go(func() error {
-		return serverConn.Run(ctx,
-			protocol.Handlers(
-				protocol.ClientHandler(client,
-					jsonrpc2.MethodNotFound)))
-	})
+	serverConn.Go(ctx,
+		protocol.Handlers(
+			protocol.ClientHandler(client,
+				jsonrpc2.MethodNotFound)))
 	// Don't run the clientConn yet, so that we can complete the handshake before
 	// processing any client messages.
 
 	// Do a handshake with the server instance to exchange debug information.
 	index := atomic.AddInt64(&serverIndex, 1)
 	serverID := strconv.FormatInt(index, 10)
-	di := debug.GetInstance(ctx)
 	var (
 		hreq = handshakeRequest{
 			ServerID:  serverID,
@@ -277,7 +207,7 @@
 		}
 		hresp handshakeResponse
 	)
-	if di != nil {
+	if di := debug.GetInstance(ctx); di != nil {
 		hreq.Logfile = di.Logfile
 		hreq.DebugAddr = di.ListenedDebugAddress
 	}
@@ -287,26 +217,31 @@
 	if hresp.GoplsPath != f.goplsPath {
 		event.Error(ctx, "", fmt.Errorf("forwarder: gopls path mismatch: forwarder is %q, remote is %q", f.goplsPath, hresp.GoplsPath))
 	}
-	if di != nil {
-		di.State.AddServer(debugServer{
-			debugInstance: debugInstance{
-				id:           serverID,
-				logfile:      hresp.Logfile,
-				debugAddress: hresp.DebugAddr,
-				goplsPath:    hresp.GoplsPath,
-			},
-			clientID: hresp.ClientID,
-		})
-	}
-	g.Go(func() error {
-		return clientConn.Run(ctx,
-			protocol.Handlers(
-				forwarderHandler(
-					protocol.ServerHandler(server,
-						jsonrpc2.MethodNotFound))))
-	})
+	event.Log(ctx, "New server",
+		tag.NewServer.Of(serverID),
+		tag.Logfile.Of(hresp.Logfile),
+		tag.DebugAddress.Of(hresp.DebugAddr),
+		tag.GoplsPath.Of(hresp.GoplsPath),
+		tag.ClientID.Of(hresp.SessionID),
+	)
+	clientConn.Go(ctx,
+		protocol.Handlers(
+			forwarderHandler(
+				protocol.ServerHandler(server,
+					jsonrpc2.MethodNotFound))))
 
-	return g.Wait()
+	select {
+	case <-serverConn.Done():
+		clientConn.Close()
+	case <-clientConn.Done():
+		serverConn.Close()
+	}
+
+	err = serverConn.Err()
+	if err == nil {
+		err = clientConn.Err()
+	}
+	return err
 }
 
 func (f *Forwarder) connectToRemote(ctx context.Context) (net.Conn, error) {
@@ -388,48 +323,93 @@
 	return nil, fmt.Errorf("dialing remote: %w", err)
 }
 
-// ForwarderExitFunc is used to exit the forwarder process. It is mutable for
-// testing purposes.
-var ForwarderExitFunc = os.Exit
-
-// OverrideExitFuncsForTest can be used from test code to prevent the test
-// process from exiting on server shutdown. The returned func reverts the exit
-// funcs to their previous state.
-func OverrideExitFuncsForTest() func() {
-	// Override functions that would shut down the test process
-	cleanup := func(lspExit, forwarderExit func(code int)) func() {
-		return func() {
-			lsp.ServerExitFunc = lspExit
-			ForwarderExitFunc = forwarderExit
-		}
-	}(lsp.ServerExitFunc, ForwarderExitFunc)
-	// It is an error for a test to shutdown a server process.
-	lsp.ServerExitFunc = func(code int) {
-		panic(fmt.Sprintf("LSP server exited with code %d", code))
-	}
-	// We don't want our forwarders to exit, but it's OK if they would have.
-	ForwarderExitFunc = func(code int) {}
-	return cleanup
-}
-
 // forwarderHandler intercepts 'exit' messages to prevent the shared gopls
 // instance from exiting. In the future it may also intercept 'shutdown' to
 // provide more graceful shutdown of the client connection.
 func forwarderHandler(handler jsonrpc2.Handler) jsonrpc2.Handler {
 	return func(ctx context.Context, reply jsonrpc2.Replier, r jsonrpc2.Request) error {
-		// TODO(golang.org/issues/34111): we should more gracefully disconnect here,
-		// once that process exists.
-		if r.Method() == "exit" {
-			ForwarderExitFunc(0)
-			// reply nil here to consume the message: in
-			// tests, ForwarderExitFunc may be overridden to something that doesn't
-			// exit the process.
-			return reply(ctx, nil, nil)
+		// The gopls workspace environment defaults to the process environment in
+		// which gopls daemon was started. To avoid discrepancies in Go environment
+		// between the editor and daemon, inject any unset variables in `go env`
+		// into the options sent by initialize.
+		//
+		// See also golang.org/issue/37830.
+		if r.Method() == "initialize" {
+			if newr, err := addGoEnvToInitializeRequest(ctx, r); err == nil {
+				r = newr
+			} else {
+				log.Printf("unable to add local env to initialize request: %v", err)
+			}
 		}
 		return handler(ctx, reply, r)
 	}
 }
 
+// addGoEnvToInitializeRequest builds a new initialize request in which we set
+// any environment variables output by `go env` and not already present in the
+// request.
+//
+// It returns an error if r is not an initialize requst, or is otherwise
+// malformed.
+func addGoEnvToInitializeRequest(ctx context.Context, r jsonrpc2.Request) (jsonrpc2.Request, error) {
+	var params protocol.ParamInitialize
+	if err := json.Unmarshal(r.Params(), &params); err != nil {
+		return nil, err
+	}
+	var opts map[string]interface{}
+	switch v := params.InitializationOptions.(type) {
+	case nil:
+		opts = make(map[string]interface{})
+	case map[string]interface{}:
+		opts = v
+	default:
+		return nil, fmt.Errorf("unexpected type for InitializationOptions: %T", v)
+	}
+	envOpt, ok := opts["env"]
+	if !ok {
+		envOpt = make(map[string]interface{})
+	}
+	env, ok := envOpt.(map[string]interface{})
+	if !ok {
+		return nil, fmt.Errorf(`env option is %T, expected a map`, envOpt)
+	}
+	goenv, err := getGoEnv(ctx, env)
+	if err != nil {
+		return nil, err
+	}
+	for govar, value := range goenv {
+		env[govar] = value
+	}
+	opts["env"] = env
+	params.InitializationOptions = opts
+	call, ok := r.(*jsonrpc2.Call)
+	if !ok {
+		return nil, fmt.Errorf("%T is not a *jsonrpc2.Call", r)
+	}
+	return jsonrpc2.NewCall(call.ID(), "initialize", params)
+}
+
+func getGoEnv(ctx context.Context, env map[string]interface{}) (map[string]string, error) {
+	var runEnv []string
+	for k, v := range env {
+		runEnv = append(runEnv, fmt.Sprintf("%s=%s", k, v))
+	}
+	runner := gocommand.Runner{}
+	output, err := runner.Run(ctx, gocommand.Invocation{
+		Verb: "env",
+		Args: []string{"-json"},
+		Env:  runEnv,
+	})
+	if err != nil {
+		return nil, err
+	}
+	envmap := make(map[string]string)
+	if err := json.Unmarshal(output.Bytes(), &envmap); err != nil {
+		return nil, err
+	}
+	return envmap, nil
+}
+
 // A handshakeRequest identifies a client to the LSP server.
 type handshakeRequest struct {
 	// ServerID is the ID of the server on the client. This should usually be 0.
@@ -446,8 +426,6 @@
 // A handshakeResponse is returned by the LSP server to tell the LSP client
 // information about its session.
 type handshakeResponse struct {
-	// ClientID is the ID of the client as seen on the server.
-	ClientID string `json:"clientID"`
 	// SessionID is the server session associated with the client.
 	SessionID string `json:"sessionID"`
 	// Logfile is the location of the server logs.
@@ -463,7 +441,6 @@
 // that it looks similar to handshakeResposne, but in fact 'Logfile' and
 // 'DebugAddr' now refer to the client.
 type ClientSession struct {
-	ClientID  string `json:"clientID"`
 	SessionID string `json:"sessionID"`
 	Logfile   string `json:"logfile"`
 	DebugAddr string `json:"debugAddr"`
@@ -485,7 +462,7 @@
 	sessionsMethod  = "gopls/sessions"
 )
 
-func handshaker(client *debugClient, goplsPath string, handler jsonrpc2.Handler) jsonrpc2.Handler {
+func handshaker(session *cache.Session, goplsPath string, handler jsonrpc2.Handler) jsonrpc2.Handler {
 	return func(ctx context.Context, reply jsonrpc2.Replier, r jsonrpc2.Request) error {
 		switch r.Method() {
 		case handshakeMethod:
@@ -494,13 +471,15 @@
 				sendError(ctx, reply, err)
 				return nil
 			}
-			client.debugAddress = req.DebugAddr
-			client.logfile = req.Logfile
-			client.serverID = req.ServerID
-			client.goplsPath = req.GoplsPath
+			event.Log(ctx, "Handshake session update",
+				cache.KeyUpdateSession.Of(session),
+				tag.DebugAddress.Of(req.DebugAddr),
+				tag.Logfile.Of(req.Logfile),
+				tag.ServerID.Of(req.ServerID),
+				tag.GoplsPath.Of(req.GoplsPath),
+			)
 			resp := handshakeResponse{
-				ClientID:  client.id,
-				SessionID: cache.DebugSession{Session: client.session}.ID(),
+				SessionID: session.ID(),
 				GoplsPath: goplsPath,
 			}
 			if di := debug.GetInstance(ctx); di != nil {
@@ -512,17 +491,16 @@
 		case sessionsMethod:
 			resp := ServerState{
 				GoplsPath:       goplsPath,
-				CurrentClientID: client.ID(),
+				CurrentClientID: session.ID(),
 			}
 			if di := debug.GetInstance(ctx); di != nil {
 				resp.Logfile = di.Logfile
 				resp.DebugAddr = di.ListenedDebugAddress
 				for _, c := range di.State.Clients() {
 					resp.Clients = append(resp.Clients, ClientSession{
-						ClientID:  c.ID(),
-						SessionID: c.Session().ID(),
-						Logfile:   c.Logfile(),
-						DebugAddr: c.DebugAddress(),
+						SessionID: c.Session.ID(),
+						Logfile:   c.Logfile,
+						DebugAddr: c.DebugAddress,
 					})
 				}
 			}
diff --git a/internal/lsp/lsprpc/lsprpc_test.go b/internal/lsp/lsprpc/lsprpc_test.go
index c89e54c..042767d 100644
--- a/internal/lsp/lsprpc/lsprpc_test.go
+++ b/internal/lsp/lsprpc/lsprpc_test.go
@@ -31,14 +31,20 @@
 	return nil
 }
 
-type pingServer struct{ protocol.Server }
+// fakeServer is intended to be embedded in the test fakes below, to trivially
+// implement Shutdown.
+type fakeServer struct {
+	protocol.Server
+}
 
-func (s pingServer) DidOpen(ctx context.Context, params *protocol.DidOpenTextDocumentParams) error {
-	event.Log(ctx, "ping")
+func (fakeServer) Shutdown(ctx context.Context) error {
 	return nil
 }
 
-func (s pingServer) Shutdown(ctx context.Context) error {
+type pingServer struct{ fakeServer }
+
+func (s pingServer) DidOpen(ctx context.Context, params *protocol.DidOpenTextDocumentParams) error {
+	event.Log(ctx, "ping")
 	return nil
 }
 
@@ -52,10 +58,10 @@
 	ctx = debug.WithInstance(ctx, "", "")
 	ss := NewStreamServer(cache.New(ctx, nil))
 	ss.serverForTest = server
-	ts := servertest.NewPipeServer(ctx, ss)
+	ts := servertest.NewPipeServer(ctx, ss, nil)
 	defer checkClose(t, ts.Close)
 	cc := ts.Connect(ctx)
-	go cc.Run(ctx, protocol.ClientHandler(client, jsonrpc2.MethodNotFound))
+	cc.Go(ctx, protocol.ClientHandler(client, jsonrpc2.MethodNotFound))
 
 	protocol.ServerDispatcher(cc).DidOpen(ctx, &protocol.DidOpenTextDocumentParams{})
 
@@ -78,7 +84,7 @@
 // The requests chosen are arbitrary: we simply needed one that blocks, and
 // another that doesn't.
 type waitableServer struct {
-	protocol.Server
+	fakeServer
 
 	started chan struct{}
 }
@@ -97,10 +103,6 @@
 	return item, nil
 }
 
-func (s waitableServer) Shutdown(ctx context.Context) error {
-	return nil
-}
-
 func checkClose(t *testing.T, closer func() error) {
 	t.Helper()
 	if err := closer(); err != nil {
@@ -108,22 +110,29 @@
 	}
 }
 
+func setupForwarding(ctx context.Context, t *testing.T, s protocol.Server) (direct, forwarded servertest.Connector, cleanup func()) {
+	t.Helper()
+	serveCtx := debug.WithInstance(ctx, "", "")
+	ss := NewStreamServer(cache.New(serveCtx, nil))
+	ss.serverForTest = s
+	tsDirect := servertest.NewTCPServer(serveCtx, ss, nil)
+
+	forwarderCtx := debug.WithInstance(ctx, "", "")
+	forwarder := NewForwarder("tcp", tsDirect.Addr)
+	tsForwarded := servertest.NewPipeServer(forwarderCtx, forwarder, nil)
+	return tsDirect, tsForwarded, func() {
+		checkClose(t, tsDirect.Close)
+		checkClose(t, tsForwarded.Close)
+	}
+}
+
 func TestRequestCancellation(t *testing.T) {
+	ctx := context.Background()
 	server := waitableServer{
 		started: make(chan struct{}),
 	}
-	baseCtx := context.Background()
-	serveCtx := debug.WithInstance(baseCtx, "", "")
-	ss := NewStreamServer(cache.New(serveCtx, nil))
-	ss.serverForTest = server
-	tsDirect := servertest.NewTCPServer(serveCtx, ss)
-	defer checkClose(t, tsDirect.Close)
-
-	forwarderCtx := debug.WithInstance(baseCtx, "", "")
-	forwarder := NewForwarder("tcp", tsDirect.Addr)
-	tsForwarded := servertest.NewPipeServer(forwarderCtx, forwarder)
-	defer checkClose(t, tsForwarded.Close)
-
+	tsDirect, tsForwarded, cleanup := setupForwarding(ctx, t, server)
+	defer cleanup()
 	tests := []struct {
 		serverType string
 		ts         servertest.Connector
@@ -134,9 +143,9 @@
 
 	for _, test := range tests {
 		t.Run(test.serverType, func(t *testing.T) {
-			cc := test.ts.Connect(baseCtx)
+			cc := test.ts.Connect(ctx)
 			sd := protocol.ServerDispatcher(cc)
-			go cc.Run(baseCtx,
+			cc.Go(ctx,
 				protocol.Handlers(
 					jsonrpc2.MethodNotFound))
 
@@ -187,9 +196,6 @@
 }`
 
 func TestDebugInfoLifecycle(t *testing.T) {
-	resetExitFuncs := OverrideExitFuncsForTest()
-	defer resetExitFuncs()
-
 	sb, err := fake.NewSandbox("gopls-lsprpc-test", exampleProgram, "", false)
 	if err != nil {
 		t.Fatal(err)
@@ -212,23 +218,23 @@
 
 	cache := cache.New(serverCtx, nil)
 	ss := NewStreamServer(cache)
-	tsBackend := servertest.NewTCPServer(serverCtx, ss)
+	tsBackend := servertest.NewTCPServer(serverCtx, ss, nil)
 
 	forwarder := NewForwarder("tcp", tsBackend.Addr)
-	tsForwarder := servertest.NewPipeServer(clientCtx, forwarder)
+	tsForwarder := servertest.NewPipeServer(clientCtx, forwarder, nil)
 
 	conn1 := tsForwarder.Connect(clientCtx)
 	ed1, err := fake.NewEditor(sb, fake.EditorConfig{}).Connect(clientCtx, conn1, fake.ClientHooks{})
 	if err != nil {
 		t.Fatal(err)
 	}
-	defer ed1.Shutdown(clientCtx)
+	defer ed1.Close(clientCtx)
 	conn2 := tsBackend.Connect(baseCtx)
 	ed2, err := fake.NewEditor(sb, fake.EditorConfig{}).Connect(baseCtx, conn2, fake.ClientHooks{})
 	if err != nil {
 		t.Fatal(err)
 	}
-	defer ed2.Shutdown(baseCtx)
+	defer ed2.Close(baseCtx)
 
 	serverDebug := debug.GetInstance(serverCtx)
 	if got, want := len(serverDebug.State.Clients()), 2; got != want {
@@ -243,14 +249,75 @@
 	}
 	// Close one of the connections to verify that the client and session were
 	// dropped.
-	if err := ed1.Shutdown(clientCtx); err != nil {
+	if err := ed1.Close(clientCtx); err != nil {
 		t.Fatal(err)
 	}
+	/*TODO: at this point we have verified the editor is closed
+	However there is no way currently to wait for all associated go routines to
+	go away, and we need to wait for those to trigger the client drop
+	for now we just give it a little bit of time, but we need to fix this
+	in a principled way
+	*/
+	start := time.Now()
+	delay := time.Millisecond
+	const maxWait = time.Second
+	for len(serverDebug.State.Clients()) > 1 {
+		if time.Since(start) > maxWait {
+			break
+		}
+		time.Sleep(delay)
+		delay *= 2
+	}
+	if got, want := len(serverDebug.State.Clients()), 1; got != want {
+		t.Errorf("len(server:Clients) = %d, want %d", got, want)
+	}
 	if got, want := len(serverDebug.State.Sessions()), 1; got != want {
 		t.Errorf("len(server:Sessions()) = %d, want %d", got, want)
 	}
-	// TODO(rfindley): once disconnection works, assert that len(Clients) == 1
-	// (as of writing, it is still 2)
 }
 
-// TODO: add a test for telemetry.
+type initServer struct {
+	fakeServer
+
+	params *protocol.ParamInitialize
+}
+
+func (s *initServer) Initialize(ctx context.Context, params *protocol.ParamInitialize) (*protocol.InitializeResult, error) {
+	s.params = params
+	return &protocol.InitializeResult{}, nil
+}
+
+func TestEnvForwarding(t *testing.T) {
+	server := &initServer{}
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
+	_, tsForwarded, cleanup := setupForwarding(ctx, t, server)
+	defer cleanup()
+
+	conn := tsForwarded.Connect(ctx)
+	conn.Go(ctx, jsonrpc2.MethodNotFound)
+	dispatch := protocol.ServerDispatcher(conn)
+	initParams := &protocol.ParamInitialize{}
+	initParams.InitializationOptions = map[string]interface{}{
+		"env": map[string]interface{}{
+			"GONOPROXY": "example.com",
+		},
+	}
+	_, err := dispatch.Initialize(ctx, initParams)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if server.params == nil {
+		t.Fatalf("initialize params are unset")
+	}
+	env := server.params.InitializationOptions.(map[string]interface{})["env"].(map[string]interface{})
+
+	// Check for an arbitrary Go variable. It should be set.
+	if _, ok := env["GOPRIVATE"]; !ok {
+		t.Errorf("Go environment variable GOPRIVATE unset in initialization options")
+	}
+	// Check that the variable present in our user config was not overwritten.
+	if v := env["GONOPROXY"]; v != "example.com" {
+		t.Errorf("GONOPROXY environment variable was overwritten")
+	}
+}
diff --git a/internal/lsp/mod/code_lens.go b/internal/lsp/mod/code_lens.go
index 39ca193..b72dcad 100644
--- a/internal/lsp/mod/code_lens.go
+++ b/internal/lsp/mod/code_lens.go
@@ -18,28 +18,38 @@
 	if !snapshot.View().Options().EnabledCodeLens[source.CommandUpgradeDependency] {
 		return nil, nil
 	}
-	realURI, _ := snapshot.View().ModFiles()
-	if realURI == "" {
-		return nil, nil
-	}
-	// Only get code lens on the go.mod for the view.
-	if uri != realURI {
-		return nil, nil
-	}
-	ctx, done := event.Start(ctx, "mod.CodeLens", tag.URI.Of(realURI))
+	ctx, done := event.Start(ctx, "mod.CodeLens", tag.URI.Of(uri))
 	defer done()
 
-	fh, err := snapshot.GetFile(realURI)
+	// Only show go.mod code lenses in module mode, for the view's go.mod.
+	if modURI := snapshot.View().ModFile(); modURI == "" || modURI != uri {
+		return nil, nil
+	}
+	fh, err := snapshot.GetFile(ctx, uri)
 	if err != nil {
 		return nil, err
 	}
-	f, m, upgrades, err := snapshot.ModHandle(ctx, fh).Upgrades(ctx)
+	pmh, err := snapshot.ParseModHandle(ctx, fh)
 	if err != nil {
 		return nil, err
 	}
-	var codelens []protocol.CodeLens
-	var allUpgrades []string
-	for _, req := range f.Require {
+	file, m, _, err := pmh.Parse(ctx)
+	if err != nil {
+		return nil, err
+	}
+	muh, err := snapshot.ModUpgradeHandle(ctx)
+	if err != nil {
+		return nil, err
+	}
+	upgrades, err := muh.Upgrades(ctx)
+	if err != nil {
+		return nil, err
+	}
+	var (
+		codelens    []protocol.CodeLens
+		allUpgrades []string
+	)
+	for _, req := range file.Require {
 		dep := req.Mod.Path
 		latest, ok := upgrades[dep]
 		if !ok {
@@ -61,7 +71,7 @@
 		allUpgrades = append(allUpgrades, dep)
 	}
 	// If there is at least 1 upgrade, add an "Upgrade all dependencies" to the module statement.
-	if module := f.Module; len(allUpgrades) > 0 && module != nil && module.Syntax != nil {
+	if module := file.Module; len(allUpgrades) > 0 && module != nil && module.Syntax != nil {
 		// Get the range of the module directive.
 		rng, err := positionsToRange(uri, m, module.Syntax.Start, module.Syntax.End)
 		if err != nil {
diff --git a/internal/lsp/mod/diagnostics.go b/internal/lsp/mod/diagnostics.go
index 0b39f83..b07d7b2 100644
--- a/internal/lsp/mod/diagnostics.go
+++ b/internal/lsp/mod/diagnostics.go
@@ -17,32 +17,33 @@
 )
 
 func Diagnostics(ctx context.Context, snapshot source.Snapshot) (map[source.FileIdentity][]*source.Diagnostic, map[string]*modfile.Require, error) {
-	// TODO: We will want to support diagnostics for go.mod files even when the -modfile flag is turned off.
-	realURI, tempURI := snapshot.View().ModFiles()
-
-	// Check the case when the tempModfile flag is turned off.
-	if realURI == "" || tempURI == "" {
+	uri := snapshot.View().ModFile()
+	if uri == "" {
 		return nil, nil, nil
 	}
-	ctx, done := event.Start(ctx, "mod.Diagnostics", tag.URI.Of(realURI))
+
+	ctx, done := event.Start(ctx, "mod.Diagnostics", tag.URI.Of(uri))
 	defer done()
 
-	realfh, err := snapshot.GetFile(realURI)
+	fh, err := snapshot.GetFile(ctx, uri)
 	if err != nil {
 		return nil, nil, err
 	}
-	mth, err := snapshot.ModTidyHandle(ctx, realfh)
+	mth, err := snapshot.ModTidyHandle(ctx)
+	if err == source.ErrTmpModfileUnsupported {
+		return nil, nil, nil
+	}
 	if err != nil {
 		return nil, nil, err
 	}
-	_, _, missingDeps, parseErrors, err := mth.Tidy(ctx)
+	missingDeps, diagnostics, err := mth.Tidy(ctx)
 	if err != nil {
 		return nil, nil, err
 	}
 	reports := map[source.FileIdentity][]*source.Diagnostic{
-		realfh.Identity(): {},
+		fh.Identity(): {},
 	}
-	for _, e := range parseErrors {
+	for _, e := range diagnostics {
 		diag := &source.Diagnostic{
 			Message: e.Message,
 			Range:   e.Range,
@@ -53,22 +54,25 @@
 		} else {
 			diag.Severity = protocol.SeverityWarning
 		}
-		reports[realfh.Identity()] = append(reports[realfh.Identity()], diag)
+		reports[fh.Identity()] = append(reports[fh.Identity()], diag)
 	}
 	return reports, missingDeps, nil
 }
 
-func SuggestedFixes(ctx context.Context, snapshot source.Snapshot, realfh source.FileHandle, diags []protocol.Diagnostic) ([]protocol.CodeAction, error) {
-	mth, err := snapshot.ModTidyHandle(ctx, realfh)
+func SuggestedFixes(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle, diags []protocol.Diagnostic) ([]protocol.CodeAction, error) {
+	mth, err := snapshot.ModTidyHandle(ctx)
+	if err == source.ErrTmpModfileUnsupported {
+		return nil, nil
+	}
 	if err != nil {
 		return nil, err
 	}
-	_, _, _, parseErrors, err := mth.Tidy(ctx)
+	_, diagnostics, err := mth.Tidy(ctx)
 	if err != nil {
 		return nil, err
 	}
 	errorsMap := make(map[string][]source.Error)
-	for _, e := range parseErrors {
+	for _, e := range diagnostics {
 		if errorsMap[e.Message] == nil {
 			errorsMap[e.Message] = []source.Error{}
 		}
@@ -88,15 +92,15 @@
 					Edit:        protocol.WorkspaceEdit{},
 				}
 				for uri, edits := range fix.Edits {
-					fh, err := snapshot.GetFile(uri)
+					fh, err := snapshot.GetFile(ctx, uri)
 					if err != nil {
 						return nil, err
 					}
 					action.Edit.DocumentChanges = append(action.Edit.DocumentChanges, protocol.TextDocumentEdit{
 						TextDocument: protocol.VersionedTextDocumentIdentifier{
-							Version: fh.Identity().Version,
+							Version: fh.Version(),
 							TextDocumentIdentifier: protocol.TextDocumentIdentifier{
-								URI: protocol.URIFromSpanURI(fh.Identity().URI),
+								URI: protocol.URIFromSpanURI(fh.URI()),
 							},
 						},
 						Edits: edits,
@@ -110,62 +114,70 @@
 }
 
 func SuggestedGoFixes(ctx context.Context, snapshot source.Snapshot) (map[string]protocol.TextDocumentEdit, error) {
-	// TODO(rstambler): Support diagnostics for go.mod files even when the
-	// -modfile flag is turned off.
-	realURI, tempURI := snapshot.View().ModFiles()
-	if realURI == "" || tempURI == "" {
+	uri := snapshot.View().ModFile()
+	if uri == "" {
 		return nil, nil
 	}
-
-	ctx, done := event.Start(ctx, "mod.SuggestedGoFixes", tag.URI.Of(realURI))
+	ctx, done := event.Start(ctx, "mod.SuggestedGoFixes", tag.URI.Of(uri))
 	defer done()
 
-	realfh, err := snapshot.GetFile(realURI)
+	fh, err := snapshot.GetFile(ctx, uri)
 	if err != nil {
 		return nil, err
 	}
-	mth, err := snapshot.ModTidyHandle(ctx, realfh)
+	mth, err := snapshot.ModTidyHandle(ctx)
+	if err == source.ErrTmpModfileUnsupported {
+		return nil, nil
+	}
 	if err != nil {
 		return nil, err
 	}
-	realFile, realMapper, missingDeps, _, err := mth.Tidy(ctx)
+	missingDeps, _, err := mth.Tidy(ctx)
 	if err != nil {
 		return nil, err
 	}
 	if len(missingDeps) == 0 {
 		return nil, nil
 	}
+	pmh, err := snapshot.ParseModHandle(ctx, fh)
+	if err != nil {
+		return nil, err
+	}
+	file, m, _, err := pmh.Parse(ctx)
+	if err != nil {
+		return nil, err
+	}
 	// Get the contents of the go.mod file before we make any changes.
-	oldContents, _, err := realfh.Read(ctx)
+	oldContents, err := fh.Read()
 	if err != nil {
 		return nil, err
 	}
 	textDocumentEdits := make(map[string]protocol.TextDocumentEdit)
 	for dep, req := range missingDeps {
 		// Calculate the quick fix edits that need to be made to the go.mod file.
-		if err := realFile.AddRequire(req.Mod.Path, req.Mod.Version); err != nil {
+		if err := file.AddRequire(req.Mod.Path, req.Mod.Version); err != nil {
 			return nil, err
 		}
-		realFile.Cleanup()
-		newContents, err := realFile.Format()
+		file.Cleanup()
+		newContents, err := file.Format()
 		if err != nil {
 			return nil, err
 		}
 		// Reset the *modfile.File back to before we added the dependency.
-		if err := realFile.DropRequire(req.Mod.Path); err != nil {
+		if err := file.DropRequire(req.Mod.Path); err != nil {
 			return nil, err
 		}
 		// Calculate the edits to be made due to the change.
-		diff := snapshot.View().Options().ComputeEdits(realfh.Identity().URI, string(oldContents), string(newContents))
-		edits, err := source.ToProtocolEdits(realMapper, diff)
+		diff := snapshot.View().Options().ComputeEdits(fh.URI(), string(oldContents), string(newContents))
+		edits, err := source.ToProtocolEdits(m, diff)
 		if err != nil {
 			return nil, err
 		}
 		textDocumentEdits[dep] = protocol.TextDocumentEdit{
 			TextDocument: protocol.VersionedTextDocumentIdentifier{
-				Version: realfh.Identity().Version,
+				Version: fh.Version(),
 				TextDocumentIdentifier: protocol.TextDocumentIdentifier{
-					URI: protocol.URIFromSpanURI(realfh.Identity().URI),
+					URI: protocol.URIFromSpanURI(fh.URI()),
 				},
 			},
 			Edits: edits,
diff --git a/internal/lsp/mod/format.go b/internal/lsp/mod/format.go
index d1c567e..fdb52e4 100644
--- a/internal/lsp/mod/format.go
+++ b/internal/lsp/mod/format.go
@@ -12,7 +12,11 @@
 	ctx, done := event.Start(ctx, "mod.Format")
 	defer done()
 
-	file, m, err := snapshot.ModHandle(ctx, fh).Parse(ctx)
+	pmh, err := snapshot.ParseModHandle(ctx, fh)
+	if err != nil {
+		return nil, err
+	}
+	file, m, _, err := pmh.Parse(ctx)
 	if err != nil {
 		return nil, err
 	}
@@ -21,6 +25,6 @@
 		return nil, err
 	}
 	// Calculate the edits to be made due to the change.
-	diff := snapshot.View().Options().ComputeEdits(fh.Identity().URI, string(m.Content), string(formatted))
+	diff := snapshot.View().Options().ComputeEdits(fh.URI(), string(m.Content), string(formatted))
 	return source.ToProtocolEdits(m, diff)
 }
diff --git a/internal/lsp/mod/hover.go b/internal/lsp/mod/hover.go
index 2439002..98b9cee 100644
--- a/internal/lsp/mod/hover.go
+++ b/internal/lsp/mod/hover.go
@@ -15,28 +15,35 @@
 )
 
 func Hover(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle, position protocol.Position) (*protocol.Hover, error) {
-	realURI, _ := snapshot.View().ModFiles()
-	// Only get hover information on the go.mod for the view.
-	if realURI == "" || fh.Identity().URI != realURI {
+	uri := snapshot.View().ModFile()
+
+	// For now, we only provide hover information for the view's go.mod file.
+	if uri == "" || fh.URI() != uri {
 		return nil, nil
 	}
+
 	ctx, done := event.Start(ctx, "mod.Hover")
 	defer done()
 
-	file, m, why, err := snapshot.ModHandle(ctx, fh).Why(ctx)
+	// Get the position of the cursor.
+	pmh, err := snapshot.ParseModHandle(ctx, fh)
+	if err != nil {
+		return nil, fmt.Errorf("getting modfile handle: %w", err)
+	}
+	file, m, _, err := pmh.Parse(ctx)
 	if err != nil {
 		return nil, err
 	}
-	// Get the position of the cursor.
 	spn, err := m.PointSpan(position)
 	if err != nil {
-		return nil, err
+		return nil, fmt.Errorf("computing cursor position: %w", err)
 	}
 	hoverRng, err := spn.Range(m.Converter)
 	if err != nil {
-		return nil, err
+		return nil, fmt.Errorf("computing hover range: %w", err)
 	}
 
+	// Confirm that the cursor is at the position of a require statement.
 	var req *modfile.Require
 	var startPos, endPos int
 	for _, r := range file.Require {
@@ -54,13 +61,29 @@
 			break
 		}
 	}
-	if req == nil || why == nil {
+
+	// The cursor position is not on a require statement.
+	if req == nil {
+		return nil, nil
+	}
+
+	// Get the `go mod why` results for the given file.
+	mwh, err := snapshot.ModWhyHandle(ctx)
+	if err != nil {
+		return nil, err
+	}
+	why, err := mwh.Why(ctx)
+	if err != nil {
+		return nil, fmt.Errorf("running go mod why: %w", err)
+	}
+	if why == nil {
 		return nil, nil
 	}
 	explanation, ok := why[req.Mod.Path]
 	if !ok {
 		return nil, nil
 	}
+
 	// Get the range to highlight for the hover.
 	line, col, err := m.Converter.ToPosition(startPos)
 	if err != nil {
@@ -74,13 +97,14 @@
 	}
 	end := span.NewPoint(line, col, endPos)
 
-	spn = span.New(fh.Identity().URI, start, end)
+	spn = span.New(fh.URI(), start, end)
 	rng, err := m.Range(spn)
 	if err != nil {
 		return nil, err
 	}
 	options := snapshot.View().Options()
-	explanation = formatExplanation(explanation, req, options)
+	isPrivate := snapshot.View().IsGoPrivatePath(req.Mod.Path)
+	explanation = formatExplanation(explanation, req, options, isPrivate)
 	return &protocol.Hover{
 		Contents: protocol.MarkupContent{
 			Kind:  options.PreferredContentFormat,
@@ -90,7 +114,7 @@
 	}, nil
 }
 
-func formatExplanation(text string, req *modfile.Require, options source.Options) string {
+func formatExplanation(text string, req *modfile.Require, options source.Options, isPrivate bool) string {
 	text = strings.TrimSuffix(text, "\n")
 	splt := strings.Split(text, "\n")
 	length := len(splt)
@@ -112,16 +136,17 @@
 		return b.String()
 	}
 
-	imp := splt[length-1]
-	target := imp
-	if strings.ToLower(options.LinkTarget) == "pkg.go.dev" {
-		target = strings.Replace(target, req.Mod.Path, req.Mod.String(), 1)
+	imp := splt[length-1] // import path
+	reference := imp
+	// See golang/go#36998: don't link to modules matching GOPRIVATE.
+	if !isPrivate && options.PreferredContentFormat == protocol.Markdown {
+		target := imp
+		if strings.ToLower(options.LinkTarget) == "pkg.go.dev" {
+			target = strings.Replace(target, req.Mod.Path, req.Mod.String(), 1)
+		}
+		reference = fmt.Sprintf("[%s](https://%s/%s)", imp, options.LinkTarget, target)
 	}
-	target = fmt.Sprintf("https://%s/%s", options.LinkTarget, target)
-
-	b.WriteString("This module is necessary because ")
-	msg := fmt.Sprintf("[%s](%s) is imported in", imp, target)
-	b.WriteString(msg)
+	b.WriteString("This module is necessary because " + reference + " is imported in")
 
 	// If the explanation is 3 lines, then it is of the form:
 	// # golang.org/x/tools
diff --git a/internal/lsp/mod/mod_test.go b/internal/lsp/mod/mod_test.go
index 72c1f53..d58a05d 100644
--- a/internal/lsp/mod/mod_test.go
+++ b/internal/lsp/mod/mod_test.go
@@ -22,6 +22,8 @@
 }
 
 func TestModfileRemainsUnchanged(t *testing.T) {
+	testenv.NeedsGo1Point(t, 14)
+
 	ctx := tests.Context(t)
 	cache := cache.New(ctx, nil)
 	session := cache.NewSession(ctx)
@@ -41,13 +43,9 @@
 	if err != nil {
 		t.Fatal(err)
 	}
-	_, snapshot, err := session.NewView(ctx, "diagnostics_test", span.URIFromPath(folder), options)
-	if err != nil {
+	if _, _, err := session.NewView(ctx, "diagnostics_test", span.URIFromPath(folder), options); err != nil {
 		t.Fatal(err)
 	}
-	if _, t := snapshot.View().ModFiles(); t == "" {
-		return
-	}
 	after, err := ioutil.ReadFile(filepath.Join(folder, "go.mod"))
 	if err != nil {
 		t.Fatal(err)
diff --git a/internal/lsp/protocol/log.go b/internal/lsp/protocol/log.go
index 597553b..2c82c64 100644
--- a/internal/lsp/protocol/log.go
+++ b/internal/lsp/protocol/log.go
@@ -36,6 +36,10 @@
 	return count, err
 }
 
+func (s *loggingStream) Close() error {
+	return s.stream.Close()
+}
+
 type req struct {
 	method string
 	start  time.Time
diff --git a/internal/lsp/protocol/protocol.go b/internal/lsp/protocol/protocol.go
index 969ab7e..398e6f4 100644
--- a/internal/lsp/protocol/protocol.go
+++ b/internal/lsp/protocol/protocol.go
@@ -21,16 +21,60 @@
 
 // ClientDispatcher returns a Client that dispatches LSP requests across the
 // given jsonrpc2 connection.
-func ClientDispatcher(conn *jsonrpc2.Conn) Client {
+func ClientDispatcher(conn jsonrpc2.Conn) Client {
 	return &clientDispatcher{Conn: conn}
 }
 
+type clientDispatcher struct {
+	jsonrpc2.Conn
+}
+
 // ServerDispatcher returns a Server that dispatches LSP requests across the
 // given jsonrpc2 connection.
-func ServerDispatcher(conn *jsonrpc2.Conn) Server {
+func ServerDispatcher(conn jsonrpc2.Conn) Server {
 	return &serverDispatcher{Conn: conn}
 }
 
+type serverDispatcher struct {
+	jsonrpc2.Conn
+}
+
+func ClientHandler(client Client, handler jsonrpc2.Handler) jsonrpc2.Handler {
+	return func(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error {
+		if ctx.Err() != nil {
+			ctx := xcontext.Detach(ctx)
+			return reply(ctx, nil, RequestCancelledError)
+		}
+		handled, err := clientDispatch(ctx, client, reply, req)
+		if handled || err != nil {
+			return err
+		}
+		return handler(ctx, reply, req)
+	}
+}
+
+func ServerHandler(server Server, handler jsonrpc2.Handler) jsonrpc2.Handler {
+	return func(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error {
+		if ctx.Err() != nil {
+			ctx := xcontext.Detach(ctx)
+			return reply(ctx, nil, RequestCancelledError)
+		}
+		handled, err := serverDispatch(ctx, server, reply, req)
+		if handled || err != nil {
+			return err
+		}
+		//TODO: This code is wrong, it ignores handler and assumes non standard
+		// request handles everything
+		// non standard request should just be a layered handler.
+		var params interface{}
+		if err := json.Unmarshal(req.Params(), &params); err != nil {
+			return sendParseError(ctx, reply, err)
+		}
+		resp, err := server.NonstandardRequest(ctx, req.Method(), params)
+		return reply(ctx, resp, err)
+
+	}
+}
 func Handlers(handler jsonrpc2.Handler) jsonrpc2.Handler {
 	return CancelHandler(
 		jsonrpc2.AsyncHandler(
@@ -72,7 +116,7 @@
 	}
 }
 
-func Call(ctx context.Context, conn *jsonrpc2.Conn, method string, params interface{}, result interface{}) error {
+func Call(ctx context.Context, conn jsonrpc2.Conn, method string, params interface{}, result interface{}) error {
 	id, err := conn.Call(ctx, method, params, result)
 	if ctx.Err() != nil {
 		cancelCall(ctx, conn, id)
@@ -80,7 +124,7 @@
 	return err
 }
 
-func cancelCall(ctx context.Context, conn *jsonrpc2.Conn, id jsonrpc2.ID) {
+func cancelCall(ctx context.Context, conn jsonrpc2.Conn, id jsonrpc2.ID) {
 	ctx = xcontext.Detach(ctx)
 	ctx, done := event.Start(ctx, "protocol.canceller")
 	defer done()
diff --git a/internal/lsp/protocol/tsclient.go b/internal/lsp/protocol/tsclient.go
index fbb7cfc..836a127 100644
--- a/internal/lsp/protocol/tsclient.go
+++ b/internal/lsp/protocol/tsclient.go
@@ -2,8 +2,8 @@
 
 // Package protocol contains data types and code for LSP jsonrpcs
 // generated automatically from vscode-languageserver-node
-// commit: 151b520c995ee3d76729b5c46258ab273d989726
-// last fetched Mon Mar 30 2020 21:01:17 GMT-0400 (Eastern Daylight Time)
+// commit: 1f688e2f65f3a6fc9ba395380cd7b059667a9ecf
+// last fetched Tue Jun 09 2020 11:22:02 GMT-0400 (Eastern Daylight Time)
 
 // Code generated (see typescript/README.md) DO NOT EDIT.
 
@@ -13,7 +13,6 @@
 	"fmt"
 
 	"golang.org/x/tools/internal/jsonrpc2"
-	"golang.org/x/tools/internal/xcontext"
 )
 
 type Client interface {
@@ -31,107 +30,97 @@
 	ApplyEdit(context.Context, *ApplyWorkspaceEditParams) (*ApplyWorkspaceEditResponse, error)
 }
 
-func ClientHandler(client Client, handler jsonrpc2.Handler) jsonrpc2.Handler {
-	return func(ctx context.Context, reply jsonrpc2.Replier, r jsonrpc2.Request) error {
-		if ctx.Err() != nil {
-			ctx := xcontext.Detach(ctx)
-			return reply(ctx, nil, RequestCancelledError)
+func clientDispatch(ctx context.Context, client Client, reply jsonrpc2.Replier, r jsonrpc2.Request) (bool, error) {
+	switch r.Method() {
+	case "window/showMessage": // notif
+		var params ShowMessageParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
 		}
-		switch r.Method() {
-		case "window/showMessage": // notif
-			var params ShowMessageParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			err := client.ShowMessage(ctx, &params)
-			return reply(ctx, nil, err)
-		case "window/logMessage": // notif
-			var params LogMessageParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			err := client.LogMessage(ctx, &params)
-			return reply(ctx, nil, err)
-		case "telemetry/event": // notif
-			var params interface{}
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			err := client.Event(ctx, &params)
-			return reply(ctx, nil, err)
-		case "textDocument/publishDiagnostics": // notif
-			var params PublishDiagnosticsParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			err := client.PublishDiagnostics(ctx, &params)
-			return reply(ctx, nil, err)
-		case "$/progress": // notif
-			var params ProgressParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			err := client.Progress(ctx, &params)
-			return reply(ctx, nil, err)
-		case "workspace/workspaceFolders": // req
-			if len(r.Params()) > 0 {
-				return reply(ctx, nil, fmt.Errorf("%w: expected no params", jsonrpc2.ErrInvalidParams))
-			}
-			resp, err := client.WorkspaceFolders(ctx)
-			return reply(ctx, resp, err)
-		case "workspace/configuration": // req
-			var params ParamConfiguration
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := client.Configuration(ctx, &params)
-			return reply(ctx, resp, err)
-		case "window/workDoneProgress/create": // req
-			var params WorkDoneProgressCreateParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			err := client.WorkDoneProgressCreate(ctx, &params)
-			return reply(ctx, nil, err)
-		case "client/registerCapability": // req
-			var params RegistrationParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			err := client.RegisterCapability(ctx, &params)
-			return reply(ctx, nil, err)
-		case "client/unregisterCapability": // req
-			var params UnregistrationParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			err := client.UnregisterCapability(ctx, &params)
-			return reply(ctx, nil, err)
-		case "window/showMessageRequest": // req
-			var params ShowMessageRequestParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := client.ShowMessageRequest(ctx, &params)
-			return reply(ctx, resp, err)
-		case "workspace/applyEdit": // req
-			var params ApplyWorkspaceEditParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := client.ApplyEdit(ctx, &params)
-			return reply(ctx, resp, err)
-		default:
-			return handler(ctx, reply, r)
+		err := client.ShowMessage(ctx, &params)
+		return true, reply(ctx, nil, err)
+	case "window/logMessage": // notif
+		var params LogMessageParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		err := client.LogMessage(ctx, &params)
+		return true, reply(ctx, nil, err)
+	case "telemetry/event": // notif
+		var params interface{}
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		err := client.Event(ctx, &params)
+		return true, reply(ctx, nil, err)
+	case "textDocument/publishDiagnostics": // notif
+		var params PublishDiagnosticsParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		err := client.PublishDiagnostics(ctx, &params)
+		return true, reply(ctx, nil, err)
+	case "$/progress": // notif
+		var params ProgressParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		err := client.Progress(ctx, &params)
+		return true, reply(ctx, nil, err)
+	case "workspace/workspaceFolders": // req
+		if len(r.Params()) > 0 {
+			return true, reply(ctx, nil, fmt.Errorf("%w: expected no params", jsonrpc2.ErrInvalidParams))
+		}
+		resp, err := client.WorkspaceFolders(ctx)
+		return true, reply(ctx, resp, err)
+	case "workspace/configuration": // req
+		var params ParamConfiguration
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := client.Configuration(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "window/workDoneProgress/create": // req
+		var params WorkDoneProgressCreateParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		err := client.WorkDoneProgressCreate(ctx, &params)
+		return true, reply(ctx, nil, err)
+	case "client/registerCapability": // req
+		var params RegistrationParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		err := client.RegisterCapability(ctx, &params)
+		return true, reply(ctx, nil, err)
+	case "client/unregisterCapability": // req
+		var params UnregistrationParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		err := client.UnregisterCapability(ctx, &params)
+		return true, reply(ctx, nil, err)
+	case "window/showMessageRequest": // req
+		var params ShowMessageRequestParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := client.ShowMessageRequest(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "workspace/applyEdit": // req
+		var params ApplyWorkspaceEditParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := client.ApplyEdit(ctx, &params)
+		return true, reply(ctx, resp, err)
 
-		}
+	default:
+		return false, nil
 	}
 }
 
-type clientDispatcher struct {
-	*jsonrpc2.Conn
-}
-
 func (s *clientDispatcher) ShowMessage(ctx context.Context, params *ShowMessageParams) error {
 	return s.Conn.Notify(ctx, "window/showMessage", params)
 }
diff --git a/internal/lsp/protocol/tsprotocol.go b/internal/lsp/protocol/tsprotocol.go
index 488cb78..63afa03 100644
--- a/internal/lsp/protocol/tsprotocol.go
+++ b/internal/lsp/protocol/tsprotocol.go
@@ -1,7 +1,7 @@
 // Package protocol contains data types and code for LSP jsonrpcs
 // generated automatically from vscode-languageserver-node
-// commit: 151b520c995ee3d76729b5c46258ab273d989726
-// last fetched Mon Mar 30 2020 21:01:17 GMT-0400 (Eastern Daylight Time)
+// commit: 1f688e2f65f3a6fc9ba395380cd7b059667a9ecf
+// last fetched Tue Jun 09 2020 11:22:02 GMT-0400 (Eastern Daylight Time)
 package protocol
 
 // Code generated (see typescript/README.md) DO NOT EDIT.
@@ -45,9 +45,21 @@
 }
 
 /**
+ * @since 3.16.0
+ */
+type CallHierarchyClientCapabilities struct {
+	/**
+	 * Whether implementation supports dynamic registration. If this is set to `true`
+	 * the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)`
+	 * return value for the corresponding server capability as well.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+}
+
+/**
  * Represents an incoming call, e.g. a caller of a method or constructor.
  *
- * @since 3.16.0 - Proposed state
+ * @since 3.16.0
  */
 type CallHierarchyIncomingCall struct {
 	/**
@@ -55,7 +67,7 @@
 	 */
 	From CallHierarchyItem `json:"from"`
 	/**
-	 * The range at which at which the calls appears. This is relative to the caller
+	 * The ranges at which the calls appear. This is relative to the caller
 	 * denoted by [`this.from`](#CallHierarchyIncomingCall.from).
 	 */
 	FromRanges []Range `json:"fromRanges"`
@@ -64,7 +76,7 @@
 /**
  * The parameter of a `callHierarchy/incomingCalls` request.
  *
- * @since 3.16.0 - Proposed state
+ * @since 3.16.0
  */
 type CallHierarchyIncomingCallsParams struct {
 	Item CallHierarchyItem `json:"item"`
@@ -76,7 +88,7 @@
  * Represents programming constructs like functions or constructors in the context
  * of call hierarchy.
  *
- * @since 3.16.0 - Proposed state
+ * @since 3.16.0
  */
 type CallHierarchyItem struct {
 	/**
@@ -111,9 +123,18 @@
 }
 
 /**
+ * Call hierarchy options used during static registration.
+ *
+ * @since 3.16.0
+ */
+type CallHierarchyOptions struct {
+	WorkDoneProgressOptions
+}
+
+/**
  * Represents an outgoing call, e.g. calling a getter from a method or a method from a constructor etc.
  *
- * @since 3.16.0 - Proposed state
+ * @since 3.16.0
  */
 type CallHierarchyOutgoingCall struct {
 	/**
@@ -131,7 +152,7 @@
 /**
  * The parameter of a `callHierarchy/outgoingCalls` request.
  *
- * @since 3.16.0 - Proposed state
+ * @since 3.16.0
  */
 type CallHierarchyOutgoingCallsParams struct {
 	Item CallHierarchyItem `json:"item"`
@@ -142,13 +163,24 @@
 /**
  * The parameter of a `textDocument/prepareCallHierarchy` request.
  *
- * @since 3.16.0 - Proposed state
+ * @since 3.16.0
  */
 type CallHierarchyPrepareParams struct {
 	TextDocumentPositionParams
 	WorkDoneProgressParams
 }
 
+/**
+ * Call hierarchy options used during static or dynamic registration.
+ *
+ * @since 3.16.0
+ */
+type CallHierarchyRegistrationOptions struct {
+	TextDocumentRegistrationOptions
+	CallHierarchyOptions
+	StaticRegistrationOptions
+}
+
 type CancelParams struct {
 	/**
 	 * The request id to cancel.
@@ -250,8 +282,9 @@
 	 */
 	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
 	/**
-	 * The client support code action literals as a valid
-	 * response of the `textDocument/codeAction` request.
+	 * The client support code action literals of type `CodeAction` as a valid
+	 * response of the `textDocument/codeAction` request. If the property is not
+	 * set the request can only return `Command` literals.
 	 *
 	 * @since 3.8.0
 	 */
@@ -669,8 +702,16 @@
 	 * this completion. When an edit is provided the value of
 	 * [insertText](#CompletionItem.insertText) is ignored.
 	 *
-	 * *Note:* The text edit's range as well as both ranges from a insert replace edit must be a
+	 * Most editors support two different operation when accepting a completion item. One is to insert a
+	 * completion text and the other is to replace an existing text with a competion text. Since this can
+	 * usually not predetermend by a server it can report both ranges. Clients need to signal support for
+	 * `InsertReplaceEdits` via the `textDocument.completion.insertReplaceSupport` client capability
+	 * property.
+	 *
+	 * *Note 1:* The text edit's range as well as both ranges from a insert replace edit must be a
 	 * [single line] and they must contain the position at which completion has been requested.
+	 * *Note 2:* If an `InsertReplaceEdit` is returned the edit's insert range must be a prefix of
+	 * the edit's replace range, that means it must be contained and starting at the same position.
 	 *
 	 * @since 3.16.0 additional type `InsertReplaceEdit` - Proposed state
 	 */
@@ -2062,13 +2103,17 @@
 	 */
 	ExecuteCommandProvider ExecuteCommandOptions `json:"executeCommandProvider,omitempty"`
 	/**
+	 * The server provides Call Hierarchy support.
+	 */
+	CallHierarchyProvider interface{}/* bool | CallHierarchyOptions | CallHierarchyRegistrationOptions*/ `json:"callHierarchyProvider,omitempty"`
+	/**
 	 * Experimental server capabilities.
 	 */
 	Experimental interface{} `json:"experimental,omitempty"`
 }
 
 /**
- * A special text edit to provide a insert or a replace operation.
+ * A special text edit to provide an insert and a replace operation.
  *
  * @since 3.16.0 - Proposed state
  */
@@ -2614,9 +2659,18 @@
  * @since 3.16.0 - Proposed state
  */
 type SemanticTokensEdit struct {
-	Start       float64   `json:"start"`
-	DeleteCount float64   `json:"deleteCount"`
-	Data        []float64 `json:"data,omitempty"`
+	/**
+	 * The start offset of the edit.
+	 */
+	Start float64 `json:"start"`
+	/**
+	 * The count of elements to remove.
+	 */
+	DeleteCount float64 `json:"deleteCount"`
+	/**
+	 * The elements to insert.
+	 */
+	Data []float64 `json:"data,omitempty"`
 }
 
 /**
@@ -2774,6 +2828,10 @@
 	 */
 	ExecuteCommandProvider ExecuteCommandOptions `json:"executeCommandProvider,omitempty"`
 	/**
+	 * The server provides Call Hierarchy support.
+	 */
+	CallHierarchyProvider interface{}/* bool | CallHierarchyOptions | CallHierarchyRegistrationOptions*/ `json:"callHierarchyProvider,omitempty"`
+	/**
 	 * Experimental server capabilities.
 	 */
 	Experimental interface{} `json:"experimental,omitempty"`
@@ -2868,6 +2926,13 @@
 			 */
 			LabelOffsetSupport bool `json:"labelOffsetSupport,omitempty"`
 		} `json:"parameterInformation,omitempty"`
+		/**
+		 * The client support the `activeParameter` property on `SignatureInformation`
+		 * literal.
+		 *
+		 * @since 3.16.0 - proposed state
+		 */
+		ActiveParameterSupport bool `json:"activeParameterSupport,omitempty"`
 	} `json:"signatureInformation,omitempty"`
 	/**
 	 * The client supports to send additional context information for a
@@ -2974,6 +3039,14 @@
 	 * The parameters of this signature.
 	 */
 	Parameters []ParameterInformation `json:"parameters,omitempty"`
+	/**
+	 * The index of the active parameter.
+	 *
+	 * If provided, this is used in place of `SignatureHelp.activeParameter`.
+	 *
+	 * @since 3.16.0 - proposed state
+	 */
+	ActiveParameter float64 `json:"activeParameter,omitempty"`
 }
 
 /**
@@ -3147,6 +3220,12 @@
 	 * Capabilities specific to `textDocument/publishDiagnostics`.
 	 */
 	PublishDiagnostics PublishDiagnosticsClientCapabilities `json:"publishDiagnostics,omitempty"`
+	/**
+	 * Capabilities specific to the `textDocument/callHierarchy`.
+	 *
+	 * @since 3.16.0
+	 */
+	CallHierarchy CallHierarchyClientCapabilities `json:"callHierarchy,omitempty"`
 }
 
 /**
@@ -3304,7 +3383,7 @@
 	 * If present save notifications are sent to the server. If omitted the notification should not be
 	 * sent.
 	 */
-	Save SaveOptions `json:"save,omitempty"`
+	Save SaveOptions/*boolean | SaveOptions*/ `json:"save,omitempty"`
 }
 
 /**
@@ -3502,11 +3581,10 @@
 type WorkDoneProgressReport struct {
 	Kind string `json:"kind"`
 	/**
-	 * Controls enablement state of a cancel button. This property is only valid if a cancel
-	 * button got requested in the `WorkDoneProgressStart` payload.
+	 * Controls enablement state of a cancel button.
 	 *
-	 * Clients that don't support cancellation or don't support control the button's
-	 * enablement state are allowed to ignore the setting.
+	 * Clients that don't support cancellation or don't support controlling the button's
+	 * enablement state are allowed to ignore the property.
 	 */
 	Cancellable bool `json:"cancellable,omitempty"`
 	/**
@@ -3957,7 +4035,7 @@
 	 * the end of the snippet. Placeholders with equal identifiers are linked,
 	 * that is typing in one will update others too.
 	 *
-	 * See also: https://github.com/Microsoft/vscode/blob/master/src/vs/editor/contrib/snippet/common/snippet.md
+	 * See also: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#snippet_syntax
 	 */
 
 	SnippetTextFormat InsertTextFormat = 2
diff --git a/internal/lsp/protocol/tsserver.go b/internal/lsp/protocol/tsserver.go
index 4c808bd..433e536 100644
--- a/internal/lsp/protocol/tsserver.go
+++ b/internal/lsp/protocol/tsserver.go
@@ -2,8 +2,8 @@
 
 // Package protocol contains data types and code for LSP jsonrpcs
 // generated automatically from vscode-languageserver-node
-// commit: 151b520c995ee3d76729b5c46258ab273d989726
-// last fetched Mon Mar 30 2020 21:01:17 GMT-0400 (Eastern Daylight Time)
+// commit: 1f688e2f65f3a6fc9ba395380cd7b059667a9ecf
+// last fetched Tue Jun 09 2020 11:22:02 GMT-0400 (Eastern Daylight Time)
 
 // Code generated (see typescript/README.md) DO NOT EDIT.
 
@@ -13,7 +13,6 @@
 	"fmt"
 
 	"golang.org/x/tools/internal/jsonrpc2"
-	"golang.org/x/tools/internal/xcontext"
 )
 
 type Server interface {
@@ -37,6 +36,9 @@
 	FoldingRange(context.Context, *FoldingRangeParams) ([]FoldingRange /*FoldingRange[] | null*/, error)
 	Declaration(context.Context, *DeclarationParams) (Declaration /*Declaration | DeclarationLink[] | null*/, error)
 	SelectionRange(context.Context, *SelectionRangeParams) ([]SelectionRange /*SelectionRange[] | null*/, error)
+	PrepareCallHierarchy(context.Context, *CallHierarchyPrepareParams) ([]CallHierarchyItem /*CallHierarchyItem[] | null*/, error)
+	IncomingCalls(context.Context, *CallHierarchyIncomingCallsParams) ([]CallHierarchyIncomingCall /*CallHierarchyIncomingCall[] | null*/, error)
+	OutgoingCalls(context.Context, *CallHierarchyOutgoingCallsParams) ([]CallHierarchyOutgoingCall /*CallHierarchyOutgoingCall[] | null*/, error)
 	Initialize(context.Context, *ParamInitialize) (*InitializeResult, error)
 	Shutdown(context.Context) error
 	WillSaveWaitUntil(context.Context, *WillSaveTextDocumentParams) ([]TextEdit /*TextEdit[] | null*/, error)
@@ -60,375 +62,358 @@
 	Rename(context.Context, *RenameParams) (*WorkspaceEdit /*WorkspaceEdit | null*/, error)
 	PrepareRename(context.Context, *PrepareRenameParams) (*Range /*Range | { range: Range, placeholder: string } | null*/, error)
 	ExecuteCommand(context.Context, *ExecuteCommandParams) (interface{} /*any | null*/, error)
-	PrepareCallHierarchy(context.Context, *CallHierarchyPrepareParams) ([]CallHierarchyItem /*CallHierarchyItem[] | null*/, error)
-	IncomingCalls(context.Context, *CallHierarchyIncomingCallsParams) ([]CallHierarchyIncomingCall /*CallHierarchyIncomingCall[] | null*/, error)
-	OutgoingCalls(context.Context, *CallHierarchyOutgoingCallsParams) ([]CallHierarchyOutgoingCall /*CallHierarchyOutgoingCall[] | null*/, error)
 	SemanticTokens(context.Context, *SemanticTokensParams) (*SemanticTokens /*SemanticTokens | null*/, error)
 	SemanticTokensEdits(context.Context, *SemanticTokensEditsParams) (interface{} /* SemanticTokens | SemanticTokensEdits | nil*/, error)
 	SemanticTokensRange(context.Context, *SemanticTokensRangeParams) (*SemanticTokens /*SemanticTokens | null*/, error)
 	NonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error)
 }
 
-func ServerHandler(server Server, handler jsonrpc2.Handler) jsonrpc2.Handler {
-	return func(ctx context.Context, reply jsonrpc2.Replier, r jsonrpc2.Request) error {
-		if ctx.Err() != nil {
-			ctx := xcontext.Detach(ctx)
-			return reply(ctx, nil, RequestCancelledError)
+func serverDispatch(ctx context.Context, server Server, reply jsonrpc2.Replier, r jsonrpc2.Request) (bool, error) {
+	switch r.Method() {
+	case "workspace/didChangeWorkspaceFolders": // notif
+		var params DidChangeWorkspaceFoldersParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
 		}
-		switch r.Method() {
-		case "workspace/didChangeWorkspaceFolders": // notif
-			var params DidChangeWorkspaceFoldersParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			err := server.DidChangeWorkspaceFolders(ctx, &params)
-			return reply(ctx, nil, err)
-		case "window/workDoneProgress/cancel": // notif
-			var params WorkDoneProgressCancelParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			err := server.WorkDoneProgressCancel(ctx, &params)
-			return reply(ctx, nil, err)
-		case "initialized": // notif
-			var params InitializedParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			err := server.Initialized(ctx, &params)
-			return reply(ctx, nil, err)
-		case "exit": // notif
-			return server.Exit(ctx)
-		case "workspace/didChangeConfiguration": // notif
-			var params DidChangeConfigurationParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			err := server.DidChangeConfiguration(ctx, &params)
-			return reply(ctx, nil, err)
-		case "textDocument/didOpen": // notif
-			var params DidOpenTextDocumentParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			err := server.DidOpen(ctx, &params)
-			return reply(ctx, nil, err)
-		case "textDocument/didChange": // notif
-			var params DidChangeTextDocumentParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			err := server.DidChange(ctx, &params)
-			return reply(ctx, nil, err)
-		case "textDocument/didClose": // notif
-			var params DidCloseTextDocumentParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			err := server.DidClose(ctx, &params)
-			return reply(ctx, nil, err)
-		case "textDocument/didSave": // notif
-			var params DidSaveTextDocumentParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			err := server.DidSave(ctx, &params)
-			return reply(ctx, nil, err)
-		case "textDocument/willSave": // notif
-			var params WillSaveTextDocumentParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			err := server.WillSave(ctx, &params)
-			return reply(ctx, nil, err)
-		case "workspace/didChangeWatchedFiles": // notif
-			var params DidChangeWatchedFilesParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			err := server.DidChangeWatchedFiles(ctx, &params)
-			return reply(ctx, nil, err)
-		case "$/setTraceNotification": // notif
-			var params SetTraceParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			err := server.SetTraceNotification(ctx, &params)
-			return reply(ctx, nil, err)
-		case "$/logTraceNotification": // notif
-			var params LogTraceParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			err := server.LogTraceNotification(ctx, &params)
-			return reply(ctx, nil, err)
-		case "textDocument/implementation": // req
-			var params ImplementationParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.Implementation(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/typeDefinition": // req
-			var params TypeDefinitionParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.TypeDefinition(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/documentColor": // req
-			var params DocumentColorParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.DocumentColor(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/colorPresentation": // req
-			var params ColorPresentationParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.ColorPresentation(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/foldingRange": // req
-			var params FoldingRangeParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.FoldingRange(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/declaration": // req
-			var params DeclarationParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.Declaration(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/selectionRange": // req
-			var params SelectionRangeParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.SelectionRange(ctx, &params)
-			return reply(ctx, resp, err)
-		case "initialize": // req
-			var params ParamInitialize
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.Initialize(ctx, &params)
-			return reply(ctx, resp, err)
-		case "shutdown": // req
-			if len(r.Params()) > 0 {
-				return reply(ctx, nil, fmt.Errorf("%w: expected no params", jsonrpc2.ErrInvalidParams))
-			}
-			err := server.Shutdown(ctx)
-			return reply(ctx, nil, err)
-		case "textDocument/willSaveWaitUntil": // req
-			var params WillSaveTextDocumentParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.WillSaveWaitUntil(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/completion": // req
-			var params CompletionParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.Completion(ctx, &params)
-			return reply(ctx, resp, err)
-		case "completionItem/resolve": // req
-			var params CompletionItem
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.Resolve(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/hover": // req
-			var params HoverParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.Hover(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/signatureHelp": // req
-			var params SignatureHelpParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.SignatureHelp(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/definition": // req
-			var params DefinitionParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.Definition(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/references": // req
-			var params ReferenceParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.References(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/documentHighlight": // req
-			var params DocumentHighlightParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.DocumentHighlight(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/documentSymbol": // req
-			var params DocumentSymbolParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.DocumentSymbol(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/codeAction": // req
-			var params CodeActionParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.CodeAction(ctx, &params)
-			return reply(ctx, resp, err)
-		case "workspace/symbol": // req
-			var params WorkspaceSymbolParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.Symbol(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/codeLens": // req
-			var params CodeLensParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.CodeLens(ctx, &params)
-			return reply(ctx, resp, err)
-		case "codeLens/resolve": // req
-			var params CodeLens
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.ResolveCodeLens(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/documentLink": // req
-			var params DocumentLinkParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.DocumentLink(ctx, &params)
-			return reply(ctx, resp, err)
-		case "documentLink/resolve": // req
-			var params DocumentLink
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.ResolveDocumentLink(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/formatting": // req
-			var params DocumentFormattingParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.Formatting(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/rangeFormatting": // req
-			var params DocumentRangeFormattingParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.RangeFormatting(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/onTypeFormatting": // req
-			var params DocumentOnTypeFormattingParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.OnTypeFormatting(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/rename": // req
-			var params RenameParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.Rename(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/prepareRename": // req
-			var params PrepareRenameParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.PrepareRename(ctx, &params)
-			return reply(ctx, resp, err)
-		case "workspace/executeCommand": // req
-			var params ExecuteCommandParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.ExecuteCommand(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/prepareCallHierarchy": // req
-			var params CallHierarchyPrepareParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.PrepareCallHierarchy(ctx, &params)
-			return reply(ctx, resp, err)
-		case "callHierarchy/incomingCalls": // req
-			var params CallHierarchyIncomingCallsParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.IncomingCalls(ctx, &params)
-			return reply(ctx, resp, err)
-		case "callHierarchy/outgoingCalls": // req
-			var params CallHierarchyOutgoingCallsParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.OutgoingCalls(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/semanticTokens": // req
-			var params SemanticTokensParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.SemanticTokens(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/semanticTokens/edits": // req
-			var params SemanticTokensEditsParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.SemanticTokensEdits(ctx, &params)
-			return reply(ctx, resp, err)
-		case "textDocument/semanticTokens/range": // req
-			var params SemanticTokensRangeParams
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.SemanticTokensRange(ctx, &params)
-			return reply(ctx, resp, err)
-		default:
-			var params interface{}
-			if err := json.Unmarshal(r.Params(), &params); err != nil {
-				return sendParseError(ctx, reply, err)
-			}
-			resp, err := server.NonstandardRequest(ctx, r.Method(), params)
-			return reply(ctx, resp, err)
+		err := server.DidChangeWorkspaceFolders(ctx, &params)
+		return true, reply(ctx, nil, err)
+	case "window/workDoneProgress/cancel": // notif
+		var params WorkDoneProgressCancelParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		err := server.WorkDoneProgressCancel(ctx, &params)
+		return true, reply(ctx, nil, err)
+	case "initialized": // notif
+		var params InitializedParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		err := server.Initialized(ctx, &params)
+		return true, reply(ctx, nil, err)
+	case "exit": // notif
+		err := server.Exit(ctx)
+		return true, reply(ctx, nil, err)
+	case "workspace/didChangeConfiguration": // notif
+		var params DidChangeConfigurationParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		err := server.DidChangeConfiguration(ctx, &params)
+		return true, reply(ctx, nil, err)
+	case "textDocument/didOpen": // notif
+		var params DidOpenTextDocumentParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		err := server.DidOpen(ctx, &params)
+		return true, reply(ctx, nil, err)
+	case "textDocument/didChange": // notif
+		var params DidChangeTextDocumentParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		err := server.DidChange(ctx, &params)
+		return true, reply(ctx, nil, err)
+	case "textDocument/didClose": // notif
+		var params DidCloseTextDocumentParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		err := server.DidClose(ctx, &params)
+		return true, reply(ctx, nil, err)
+	case "textDocument/didSave": // notif
+		var params DidSaveTextDocumentParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		err := server.DidSave(ctx, &params)
+		return true, reply(ctx, nil, err)
+	case "textDocument/willSave": // notif
+		var params WillSaveTextDocumentParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		err := server.WillSave(ctx, &params)
+		return true, reply(ctx, nil, err)
+	case "workspace/didChangeWatchedFiles": // notif
+		var params DidChangeWatchedFilesParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		err := server.DidChangeWatchedFiles(ctx, &params)
+		return true, reply(ctx, nil, err)
+	case "$/setTraceNotification": // notif
+		var params SetTraceParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		err := server.SetTraceNotification(ctx, &params)
+		return true, reply(ctx, nil, err)
+	case "$/logTraceNotification": // notif
+		var params LogTraceParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		err := server.LogTraceNotification(ctx, &params)
+		return true, reply(ctx, nil, err)
+	case "textDocument/implementation": // req
+		var params ImplementationParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.Implementation(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/typeDefinition": // req
+		var params TypeDefinitionParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.TypeDefinition(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/documentColor": // req
+		var params DocumentColorParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.DocumentColor(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/colorPresentation": // req
+		var params ColorPresentationParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.ColorPresentation(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/foldingRange": // req
+		var params FoldingRangeParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.FoldingRange(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/declaration": // req
+		var params DeclarationParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.Declaration(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/selectionRange": // req
+		var params SelectionRangeParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.SelectionRange(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/prepareCallHierarchy": // req
+		var params CallHierarchyPrepareParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.PrepareCallHierarchy(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "callHierarchy/incomingCalls": // req
+		var params CallHierarchyIncomingCallsParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.IncomingCalls(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "callHierarchy/outgoingCalls": // req
+		var params CallHierarchyOutgoingCallsParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.OutgoingCalls(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "initialize": // req
+		var params ParamInitialize
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.Initialize(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "shutdown": // req
+		if len(r.Params()) > 0 {
+			return true, reply(ctx, nil, fmt.Errorf("%w: expected no params", jsonrpc2.ErrInvalidParams))
+		}
+		err := server.Shutdown(ctx)
+		return true, reply(ctx, nil, err)
+	case "textDocument/willSaveWaitUntil": // req
+		var params WillSaveTextDocumentParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.WillSaveWaitUntil(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/completion": // req
+		var params CompletionParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.Completion(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "completionItem/resolve": // req
+		var params CompletionItem
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.Resolve(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/hover": // req
+		var params HoverParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.Hover(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/signatureHelp": // req
+		var params SignatureHelpParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.SignatureHelp(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/definition": // req
+		var params DefinitionParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.Definition(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/references": // req
+		var params ReferenceParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.References(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/documentHighlight": // req
+		var params DocumentHighlightParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.DocumentHighlight(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/documentSymbol": // req
+		var params DocumentSymbolParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.DocumentSymbol(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/codeAction": // req
+		var params CodeActionParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.CodeAction(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "workspace/symbol": // req
+		var params WorkspaceSymbolParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.Symbol(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/codeLens": // req
+		var params CodeLensParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.CodeLens(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "codeLens/resolve": // req
+		var params CodeLens
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.ResolveCodeLens(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/documentLink": // req
+		var params DocumentLinkParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.DocumentLink(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "documentLink/resolve": // req
+		var params DocumentLink
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.ResolveDocumentLink(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/formatting": // req
+		var params DocumentFormattingParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.Formatting(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/rangeFormatting": // req
+		var params DocumentRangeFormattingParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.RangeFormatting(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/onTypeFormatting": // req
+		var params DocumentOnTypeFormattingParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.OnTypeFormatting(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/rename": // req
+		var params RenameParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.Rename(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/prepareRename": // req
+		var params PrepareRenameParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.PrepareRename(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "workspace/executeCommand": // req
+		var params ExecuteCommandParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.ExecuteCommand(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/semanticTokens": // req
+		var params SemanticTokensParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.SemanticTokens(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/semanticTokens/edits": // req
+		var params SemanticTokensEditsParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.SemanticTokensEdits(ctx, &params)
+		return true, reply(ctx, resp, err)
+	case "textDocument/semanticTokens/range": // req
+		var params SemanticTokensRangeParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.SemanticTokensRange(ctx, &params)
+		return true, reply(ctx, resp, err)
 
-		}
+	default:
+		return false, nil
 	}
 }
 
-type serverDispatcher struct {
-	*jsonrpc2.Conn
-}
-
 func (s *serverDispatcher) DidChangeWorkspaceFolders(ctx context.Context, params *DidChangeWorkspaceFoldersParams) error {
 	return s.Conn.Notify(ctx, "workspace/didChangeWorkspaceFolders", params)
 }
@@ -536,6 +521,30 @@
 	return result, nil
 }
 
+func (s *serverDispatcher) PrepareCallHierarchy(ctx context.Context, params *CallHierarchyPrepareParams) ([]CallHierarchyItem /*CallHierarchyItem[] | null*/, error) {
+	var result []CallHierarchyItem /*CallHierarchyItem[] | null*/
+	if err := Call(ctx, s.Conn, "textDocument/prepareCallHierarchy", params, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func (s *serverDispatcher) IncomingCalls(ctx context.Context, params *CallHierarchyIncomingCallsParams) ([]CallHierarchyIncomingCall /*CallHierarchyIncomingCall[] | null*/, error) {
+	var result []CallHierarchyIncomingCall /*CallHierarchyIncomingCall[] | null*/
+	if err := Call(ctx, s.Conn, "callHierarchy/incomingCalls", params, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func (s *serverDispatcher) OutgoingCalls(ctx context.Context, params *CallHierarchyOutgoingCallsParams) ([]CallHierarchyOutgoingCall /*CallHierarchyOutgoingCall[] | null*/, error) {
+	var result []CallHierarchyOutgoingCall /*CallHierarchyOutgoingCall[] | null*/
+	if err := Call(ctx, s.Conn, "callHierarchy/outgoingCalls", params, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
 func (s *serverDispatcher) Initialize(ctx context.Context, params *ParamInitialize) (*InitializeResult, error) {
 	var result *InitializeResult
 	if err := Call(ctx, s.Conn, "initialize", params, &result); err != nil {
@@ -716,30 +725,6 @@
 	return result, nil
 }
 
-func (s *serverDispatcher) PrepareCallHierarchy(ctx context.Context, params *CallHierarchyPrepareParams) ([]CallHierarchyItem /*CallHierarchyItem[] | null*/, error) {
-	var result []CallHierarchyItem /*CallHierarchyItem[] | null*/
-	if err := Call(ctx, s.Conn, "textDocument/prepareCallHierarchy", params, &result); err != nil {
-		return nil, err
-	}
-	return result, nil
-}
-
-func (s *serverDispatcher) IncomingCalls(ctx context.Context, params *CallHierarchyIncomingCallsParams) ([]CallHierarchyIncomingCall /*CallHierarchyIncomingCall[] | null*/, error) {
-	var result []CallHierarchyIncomingCall /*CallHierarchyIncomingCall[] | null*/
-	if err := Call(ctx, s.Conn, "callHierarchy/incomingCalls", params, &result); err != nil {
-		return nil, err
-	}
-	return result, nil
-}
-
-func (s *serverDispatcher) OutgoingCalls(ctx context.Context, params *CallHierarchyOutgoingCallsParams) ([]CallHierarchyOutgoingCall /*CallHierarchyOutgoingCall[] | null*/, error) {
-	var result []CallHierarchyOutgoingCall /*CallHierarchyOutgoingCall[] | null*/
-	if err := Call(ctx, s.Conn, "callHierarchy/outgoingCalls", params, &result); err != nil {
-		return nil, err
-	}
-	return result, nil
-}
-
 func (s *serverDispatcher) SemanticTokens(ctx context.Context, params *SemanticTokensParams) (*SemanticTokens /*SemanticTokens | null*/, error) {
 	var result *SemanticTokens /*SemanticTokens | null*/
 	if err := Call(ctx, s.Conn, "textDocument/semanticTokens", params, &result); err != nil {
diff --git a/internal/lsp/protocol/typescript/code.ts b/internal/lsp/protocol/typescript/code.ts
index 54d808f..9baacc9 100644
--- a/internal/lsp/protocol/typescript/code.ts
+++ b/internal/lsp/protocol/typescript/code.ts
@@ -196,7 +196,8 @@
   if (ts.isExpressionStatement(node) || ts.isFunctionDeclaration(node) ||
     ts.isImportDeclaration(node) || ts.isVariableStatement(node) ||
     ts.isExportDeclaration(node) || ts.isEmptyStatement(node) ||
-    node.kind == ts.SyntaxKind.EndOfFileToken) {
+    ts.isExportAssignment(node) || ts.isImportEqualsDeclaration(node) ||
+    ts.isBlock(node) || node.kind == ts.SyntaxKind.EndOfFileToken) {
     return;
   }
   if (ts.isInterfaceDeclaration(node)) {
@@ -214,7 +215,7 @@
         // and InitializeResult: [custom: string]: any;]
         return
       } else
-        throw new Error(`unexpected ${strKind(t)}`)
+        throw new Error(`217 unexpected ${strKind(t)}`)
     };
     v.members.forEach(f);
     if (mems.length == 0 && !v.heritageClauses &&
@@ -334,7 +335,7 @@
       throw new Error(`Class dup ${loc(c.me)} and ${loc(data.get(c.name).me)}`);
     data.set(c.name, c);
   } else {
-    throw new Error(`unexpected ${strKind(node)} ${loc(node)} `)
+    throw new Error(`338 unexpected ${strKind(node)} ${loc(node)} `)
   }
 }
 
@@ -347,8 +348,6 @@
   }
   const ax = `(${a.statements.length},${a.properties.length})`
   const bx = `(${b.statements.length},${b.properties.length})`
-  // console.log(`397
-  // ${a.name}${ax}${bx}\n${a.me.getText()}\n${b.me.getText()}\n`)
   switch (a.name) {
     case 'InitializeError':
     case 'MessageType':
@@ -358,13 +357,14 @@
       // want the Module
       return a.statements.length > 0 ? a : b;
     case 'CancellationToken':
+    case 'CancellationStrategy':
       // want the Interface
       return a.properties.length > 0 ? a : b;
     case 'TextDocumentContentChangeEvent':  // almost the same
       return a;
   }
   console.log(
-    `${strKind(a.me)} ${strKind(b.me)} ${a.name} ${loc(a.me)} ${loc(b.me)}`)
+    `367 ${strKind(a.me)} ${strKind(b.me)} ${a.name} ${loc(a.me)} ${loc(b.me)}`)
   throw new Error(`Fix dataMerge for ${a.name}`)
 }
 
@@ -676,6 +676,7 @@
       if (a == 'BooleanKeyword') {  // usually want bool
         if (nm == 'codeActionProvider') return `interface{} ${help}`;
         if (nm == 'renameProvider') return `interface{} ${help}`;
+        if (nm == 'save') return `${goType(n.types[1], '680')} ${help}`;
         return `${goType(n.types[0], 'b')} ${help}`
       }
       if (b == 'ArrayType') return `${goType(n.types[1], 'c')} ${help}`;
@@ -687,7 +688,7 @@
       if (a == 'TypeLiteral' && nm == 'TextDocumentContentChangeEvent') {
         return `${goType(n.types[0], nm)}`
       }
-      throw new Error(`724 ${a} ${b} ${n.getText()} ${loc(n)}`);
+      throw new Error(`691 ${a} ${b} ${n.getText()} ${loc(n)}`);
     case 3:
       const aa = strKind(n.types[0])
       const bb = strKind(n.types[1])
@@ -726,7 +727,7 @@
   // Result will be interface{} with a comment
   let isLiteral = true;
   let literal = 'string';
-  let res = `interface{ } /* `
+  let res = `interface{} /* `
   n.types.forEach((v: ts.TypeNode, i: number) => {
     // might get an interface inside:
     //  (Command | CodeAction)[] | null
@@ -908,7 +909,7 @@
 
 // commonly used output
 const notNil = `if len(r.Params()) > 0 {
-  return reply(ctx, nil, fmt.Errorf("%w: expected no params", jsonrpc2.ErrInvalidParams))
+  return true, reply(ctx, nil, fmt.Errorf("%w: expected no params", jsonrpc2.ErrInvalidParams))
 }`;
 
 // Go code for notifications. Side is client or server, m is the request
@@ -924,12 +925,13 @@
   if (a != '' && a != 'void') {
     case1 = `var params ${a}
     if err := json.Unmarshal(r.Params(), &params); err != nil {
-      return sendParseError(ctx, reply, err)
+      return true, sendParseError(ctx, reply, err)
     }
     err:= ${side.name}.${nm}(ctx, &params)
-    return reply(ctx, nil, err)`
+    return true, reply(ctx, nil, err)`
   } else {
-    case1 = `return ${side.name}.${nm}(ctx)`;
+    case1 = `err := ${side.name}.${nm}(ctx)
+    return true, reply(ctx, nil, err)`;
   }
   side.cases.push(`${caseHdr}\n${case1}`);
 
@@ -959,7 +961,7 @@
     if (extraTypes.has('Param' + nm)) a = 'Param' + nm
     case1 = `var params ${a}
     if err := json.Unmarshal(r.Params(), &params); err != nil {
-      return sendParseError(ctx, reply, err)
+      return true, sendParseError(ctx, reply, err)
     }`;
   }
   const arg2 = a == '' ? '' : ', &params';
@@ -968,10 +970,10 @@
   }`;
   if (b != '' && b != 'void') {
     case2 = `resp, err := ${side.name}.${nm}(ctx${arg2})
-    return reply(ctx, resp, err)`;
+    return true, reply(ctx, resp, err)`;
   } else {  // response is nil
     case2 = `err := ${side.name}.${nm}(ctx${arg2})
-    return reply(ctx, nil, err)`
+    return true, reply(ctx, nil, err)`
   }
 
   side.cases.push(`${caseHdr}\n${case1}\n${case2}`);
@@ -1074,30 +1076,20 @@
           "fmt"
 
           "golang.org/x/tools/internal/jsonrpc2"
-          "golang.org/x/tools/internal/xcontext"
         )
         `);
   const a = side.name[0].toUpperCase() + side.name.substring(1)
   f(`type ${a} interface {`);
   side.methods.forEach((v) => {f(v)});
   f('}\n');
-  f(`func ${a}Handler(${side.name} ${a}, handler jsonrpc2.Handler) jsonrpc2.Handler {
-        return func(ctx context.Context, reply jsonrpc2.Replier, r jsonrpc2.Request) error {
-            if ctx.Err() != nil {
-              ctx := xcontext.Detach(ctx)
-              return reply(ctx, nil, RequestCancelledError)
-            }
-            switch r.Method() {`);
+  f(`func ${side.name}Dispatch(ctx context.Context, ${side.name} ${a}, reply jsonrpc2.Replier, r jsonrpc2.Request) (bool, error) {
+          switch r.Method() {`);
   side.cases.forEach((v) => {f(v)});
   f(`
-          }
+        default:
+          return false, nil
         }
       }`);
-  f(`
-        type ${side.name}Dispatcher struct {
-          *jsonrpc2.Conn
-        }
-        `);
   side.calls.forEach((v) => {f(v)});
 }
 
@@ -1114,16 +1106,6 @@
       return result, nil
     }
   `)
-  client.cases.push(`default:
-    return handler(ctx, reply, r)`)
-  server.cases.push(`default:
-  var params interface{}
-  if err := json.Unmarshal(r.Params(), &params); err != nil {
-    return sendParseError(ctx, reply, err)
-  }
-  resp, err := server.NonstandardRequest(ctx, r.Method(), params)
-  return reply(ctx, resp, err)
-`)
 }
 
 // ----- remember it's a scripting language
diff --git a/internal/lsp/protocol/typescript/util.ts b/internal/lsp/protocol/typescript/util.ts
index 51d2439..77fbfed 100644
--- a/internal/lsp/protocol/typescript/util.ts
+++ b/internal/lsp/protocol/typescript/util.ts
@@ -10,11 +10,11 @@
 let dir = process.env['HOME'];
 const srcDir = '/vscode-languageserver-node'
 export const fnames = [
-  //`${dir}${srcDir}/protocol/src/protocol.ts`, // why isn't this main.ts?
-  `${dir}/${srcDir}/protocol/src/main.ts`, `${dir}${srcDir}/types/src/main.ts`,
-  `${dir}${srcDir}/jsonrpc/src/main.ts`
+  `${dir}${srcDir}/protocol/src/common/protocol.ts`,
+  `${dir}/${srcDir}/protocol/src/browser/main.ts`, `${dir}${srcDir}/types/src/main.ts`,
+  `${dir}${srcDir}/jsonrpc/src/node/main.ts`
 ];
-export const gitHash = '151b520c995ee3d76729b5c46258ab273d989726'
+export const gitHash = '1f688e2f65f3a6fc9ba395380cd7b059667a9ecf'
 let outFname = 'tsprotocol.go';
 let fda: number, fdb: number, fde: number;  // file descriptors
 
@@ -66,10 +66,10 @@
     }
   }
   const a =
-      `// Package protocol contains data types and code for LSP jsonrpcs\n` +
-      `// generated automatically from vscode-languageserver-node\n` +
-      `// commit: ${gitHash}\n` +
-      `// last fetched ${lastDate}\n`
+    `// Package protocol contains data types and code for LSP jsonrpcs\n` +
+    `// generated automatically from vscode-languageserver-node\n` +
+    `// commit: ${gitHash}\n` +
+    `// last fetched ${lastDate}\n`
   const b = 'package protocol\n'
   const c = `\n// Code generated (see typescript/README.md) DO NOT EDIT.\n\n`
   if (pkgDoc) {
@@ -95,7 +95,7 @@
 // Generate JSON tag for a struct field
 export function JSON(n: ts.PropertySignature): string {
   const json = `\`json:"${n.name.getText()}${
-      n.questionToken != undefined ? ',omitempty' : ''}"\``;
+    n.questionToken != undefined ? ',omitempty' : ''}"\``;
   return json
 }
 
@@ -114,7 +114,7 @@
   let ans = nm;
   if (pref.get(type)) ans = pref.get(type) + ans;
   if (suff.has(type)) ans = ans + suff.get(type)
-    return ans
+  return ans
 }
 
 // Find the comments associated with an AST node
@@ -193,7 +193,7 @@
   const n = fn.search(/-node./)
   fn = fn.substring(n + 6)
   return `${fn} ${x.line + 1}: ${x.character + 1} (${y.line + 1}: ${
-      y.character + 1})`
+    y.character + 1})`
 }
 // --- various string stuff
 
@@ -201,7 +201,7 @@
 // as part of printing the AST tree
 function kinds(n: ts.Node): string {
   let res = 'Seen ' + strKind(n);
-  function f(n: ts.Node): void{res += ' ' + strKind(n)};
+  function f(n: ts.Node): void {res += ' ' + strKind(n)};
   ts.forEachChild(n, f)
   return res
 }
diff --git a/internal/lsp/references.go b/internal/lsp/references.go
index 8a5700d..8d65bf9 100644
--- a/internal/lsp/references.go
+++ b/internal/lsp/references.go
@@ -12,7 +12,7 @@
 )
 
 func (s *Server) references(ctx context.Context, params *protocol.ReferenceParams) ([]protocol.Location, error) {
-	snapshot, fh, ok, err := s.beginFileRequest(params.TextDocument.URI, source.Go)
+	snapshot, fh, ok, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.Go)
 	if !ok {
 		return nil, err
 	}
diff --git a/internal/lsp/regtest/codelens_test.go b/internal/lsp/regtest/codelens_test.go
index 9bc913f..14f5bef 100644
--- a/internal/lsp/regtest/codelens_test.go
+++ b/internal/lsp/regtest/codelens_test.go
@@ -8,7 +8,9 @@
 	"testing"
 
 	"golang.org/x/tools/internal/lsp/fake"
+	"golang.org/x/tools/internal/lsp/protocol"
 	"golang.org/x/tools/internal/lsp/source"
+	"golang.org/x/tools/internal/testenv"
 )
 
 func TestDisablingCodeLens(t *testing.T) {
@@ -55,3 +57,117 @@
 		})
 	}
 }
+
+// This test confirms the full functionality of the code lenses for updating
+// dependencies in a go.mod file. It checks for the code lens that suggests an
+// update and then executes the command associated with that code lens.
+// A regression test for golang/go#39446.
+func TestUpdateCodelens(t *testing.T) {
+	const proxyWithLatest = `
+-- golang.org/x/hello@v1.3.3/go.mod --
+module golang.org/x/hello
+
+go 1.14
+-- golang.org/x/hello@v1.3.3/hi/hi.go --
+package hi
+
+var Goodbye error
+	-- golang.org/x/hello@v1.2.3/go.mod --
+module golang.org/x/hello
+
+go 1.14
+-- golang.org/x/hello@v1.2.3/hi/hi.go --
+package hi
+
+var Goodbye error
+`
+
+	const shouldUpdateDep = `
+-- go.mod --
+module mod.com
+
+go 1.14
+
+require golang.org/x/hello v1.2.3
+-- main.go --
+package main
+
+import "golang.org/x/hello/hi"
+
+func main() {
+	_ = hi.Goodbye
+}
+`
+	runner.Run(t, shouldUpdateDep, func(t *testing.T, env *Env) {
+		env.OpenFile("go.mod")
+		before := env.ReadWorkspaceFile("go.mod")
+		lenses := env.CodeLens("go.mod")
+		want := "Upgrade dependency to v1.3.3"
+		var found *protocol.CodeLens
+		for _, lens := range lenses {
+			if lens.Command.Title == want {
+				found = &lens
+			}
+		}
+		if found == nil {
+			t.Fatalf("did not find lens %q, got %v", want, lenses)
+		}
+		if _, err := env.Editor.Server.ExecuteCommand(env.Ctx, &protocol.ExecuteCommandParams{
+			Command:   found.Command.Command,
+			Arguments: found.Command.Arguments,
+		}); err != nil {
+			t.Fatal(err)
+		}
+		after := env.ReadWorkspaceFile("go.mod")
+		if before == after {
+			t.Fatalf("go.mod file was unchanged by upgrade command")
+		}
+	}, WithProxy(proxyWithLatest))
+}
+
+func TestRegenerateCgo(t *testing.T) {
+	testenv.NeedsTool(t, "cgo")
+	testenv.NeedsGo1Point(t, 15)
+
+	const workspace = `
+-- go.mod --
+module example.com
+-- cgo.go --
+package x
+
+/*
+int fortythree() { return 42; }
+*/
+import "C"
+
+func Foo() {
+	print(C.fortytwo())
+}
+`
+	runner.Run(t, workspace, func(t *testing.T, env *Env) {
+		// Open the file. We should have a nonexistant symbol.
+		env.OpenFile("cgo.go")
+		env.Await(env.DiagnosticAtRegexp("cgo.go", `C\.(fortytwo)`)) // could not determine kind of name for C.fortytwo
+
+		// Fix the C function name. We haven't regenerated cgo, so nothing should be fixed.
+		env.RegexpReplace("cgo.go", `int fortythree`, "int fortytwo")
+		env.SaveBuffer("cgo.go")
+		env.Await(env.DiagnosticAtRegexp("cgo.go", `C\.(fortytwo)`))
+
+		// Regenerate cgo, fixing the diagnostic.
+		lenses := env.CodeLens("cgo.go")
+		var lens protocol.CodeLens
+		for _, l := range lenses {
+			if l.Command.Command == source.CommandRegenerateCgo {
+				lens = l
+			}
+		}
+		if _, err := env.Editor.Server.ExecuteCommand(env.Ctx, &protocol.ExecuteCommandParams{
+			Command:   lens.Command.Command,
+			Arguments: lens.Command.Arguments,
+		}); err != nil {
+			t.Fatal(err)
+		}
+		env.Await(EmptyDiagnostics("cgo.go"))
+	})
+}
diff --git a/internal/lsp/regtest/diagnostics_114_test.go b/internal/lsp/regtest/diagnostics_114_test.go
deleted file mode 100644
index 39f5667..0000000
--- a/internal/lsp/regtest/diagnostics_114_test.go
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build 1.14
-
-package regtest
-
-import (
-	"testing"
-
-	"golang.org/x/tools/internal/lsp/fake"
-	"golang.org/x/tools/internal/lsp/protocol"
-)
-
-const ardanLabsProxy = `
--- github.com/ardanlabs/conf@v1.2.3/go.mod --
-module github.com/ardanlabs/conf
-
-go 1.12
--- github.com/ardanlabs/conf@v1.2.3/conf.go --
-package conf
-
-var ErrHelpWanted error
-`
-
-// -modfile flag that is used to provide modfile diagnostics is only available
-// with 1.14.
-func Test_Issue38211(t *testing.T) {
-	const ardanLabs = `
--- go.mod --
-module mod.com
-
-go 1.14
--- main.go --
-package main
-
-import "github.com/ardanlabs/conf"
-
-func main() {
-	_ = conf.ErrHelpWanted
-}
-`
-	runner.Run(t, ardanLabs, func(t *testing.T, env *Env) {
-		// Expect a diagnostic with a suggested fix to add
-		// "github.com/ardanlabs/conf" to the go.mod file.
-		env.OpenFile("go.mod")
-		env.OpenFile("main.go")
-		metBy := env.Await(
-			env.DiagnosticAtRegexp("main.go", `"github.com/ardanlabs/conf"`),
-		)
-		d, ok := metBy[0].(*protocol.PublishDiagnosticsParams)
-		if !ok {
-			t.Fatalf("unexpected type for metBy (%T)", metBy)
-		}
-		env.ApplyQuickFixes("main.go", d.Diagnostics)
-		env.SaveBuffer("go.mod")
-		env.Await(
-			EmptyDiagnostics("main.go"),
-		)
-		// Comment out the line that depends on conf and expect a
-		// diagnostic and a fix to remove the import.
-		env.RegexpReplace("main.go", "_ = conf.ErrHelpWanted", "//_ = conf.ErrHelpWanted")
-		env.Await(
-			env.DiagnosticAtRegexp("main.go", `"github.com/ardanlabs/conf"`),
-		)
-		env.SaveBuffer("main.go")
-		// Expect a diagnostic and fix to remove the dependency in the go.mod.
-		metBy = env.Await(
-			EmptyDiagnostics("main.go"),
-			env.DiagnosticAtRegexp("go.mod", "require github.com/ardanlabs/conf"),
-		)
-		d, ok = metBy[1].(*protocol.PublishDiagnosticsParams)
-		if !ok {
-			t.Fatalf("unexpected type for metBy (%T)", metBy)
-		}
-		env.ApplyQuickFixes("go.mod", d.Diagnostics)
-		env.SaveBuffer("go.mod")
-		env.Await(
-			EmptyDiagnostics("go.mod"),
-		)
-		// Uncomment the lines and expect a new diagnostic for the import.
-		env.RegexpReplace("main.go", "//_ = conf.ErrHelpWanted", "_ = conf.ErrHelpWanted")
-		env.SaveBuffer("main.go")
-		env.Await(
-			env.DiagnosticAtRegexp("main.go", `"github.com/ardanlabs/conf"`),
-		)
-	}, WithProxy(ardanLabsProxy))
-}
-
-// Test for golang/go#38207.
-func TestNewModule_Issue38207(t *testing.T) {
-	const emptyFile = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-`
-	runner.Run(t, emptyFile, func(t *testing.T, env *Env) {
-		env.OpenFile("main.go")
-		env.OpenFile("go.mod")
-		env.EditBuffer("main.go", fake.NewEdit(0, 0, 0, 0, `package main
-
-import "github.com/ardanlabs/conf"
-
-func main() {
-	_ = conf.ErrHelpWanted
-}
-`))
-		env.SaveBuffer("main.go")
-		metBy := env.Await(
-			env.DiagnosticAtRegexp("main.go", `"github.com/ardanlabs/conf"`),
-		)
-		d, ok := metBy[0].(*protocol.PublishDiagnosticsParams)
-		if !ok {
-			t.Fatalf("unexpected type for diagnostics (%T)", d)
-		}
-		env.ApplyQuickFixes("main.go", d.Diagnostics)
-		env.Await(
-			EmptyDiagnostics("main.go"),
-		)
-	}, WithProxy(ardanLabsProxy))
-}
diff --git a/internal/lsp/regtest/diagnostics_test.go b/internal/lsp/regtest/diagnostics_test.go
index 6a5f016..39eb67c 100644
--- a/internal/lsp/regtest/diagnostics_test.go
+++ b/internal/lsp/regtest/diagnostics_test.go
@@ -5,14 +5,18 @@
 package regtest
 
 import (
+	"context"
 	"fmt"
+	"log"
 	"os"
 	"testing"
+	"time"
 
 	"golang.org/x/tools/internal/lsp"
 	"golang.org/x/tools/internal/lsp/fake"
 	"golang.org/x/tools/internal/lsp/protocol"
 	"golang.org/x/tools/internal/lsp/tests"
+	"golang.org/x/tools/internal/testenv"
 )
 
 // Use mod.com for all go.mod files due to golang/go#35230.
@@ -127,7 +131,7 @@
 	runner.Run(t, badPackage, func(t *testing.T, env *Env) {
 		env.OpenFile("a.go")
 		env.Await(env.DiagnosticAtRegexp("a.go", "a = 1"), env.DiagnosticAtRegexp("b.go", "a = 2"))
-		env.RemoveFileFromWorkspace("b.go")
+		env.RemoveWorkspaceFile("b.go")
 
 		env.Await(EmptyDiagnostics("a.go"), EmptyDiagnostics("b.go"))
 	})
@@ -175,6 +179,52 @@
 	})
 }
 
+// Tests golang/go#38878: good a.go, bad a_test.go, remove a_test.go but its errors remain
+// If the file is open in the editor, this is working as intended
+// If the file is not open in the editor, the errors go away
+const test38878 = `
+-- go.mod --
+module foo
+
+-- a.go --
+package x
+
+func f() {}
+
+-- a_test.go --
+package x
+
+import "testing"
+
+func TestA(t *testing.T) {
+	f(3)
+}
+`
+
+func TestRmTest38878Close(t *testing.T) {
+	runner.Run(t, test38878, func(t *testing.T, env *Env) {
+		env.OpenFile("a_test.go")
+		env.Await(DiagnosticAt("a_test.go", 5, 3))
+		env.CloseBuffer("a_test.go")
+		env.RemoveWorkspaceFile("a_test.go")
+		// diagnostics go away
+		env.Await(EmptyDiagnostics("a_test.go"))
+	})
+}
+func TestRmTest38878(t *testing.T) {
+	log.SetFlags(log.Lshortfile)
+	runner.Run(t, test38878, func(t *testing.T, env *Env) {
+		env.OpenFile("a_test.go")
+		env.Await(DiagnosticAt("a_test.go", 5, 3))
+		env.Sandbox.Workdir.RemoveFile(context.Background(), "a_test.go")
+		// diagnostics remain after giving gopls a chance to do something
+		// (there is not yet a better way to decide gopls isn't going
+		// to do anything)
+		time.Sleep(time.Second)
+		env.Await(DiagnosticAt("a_test.go", 5, 3))
+	})
+}
+
 // TestNoMod confirms that gopls continues to work when a user adds a go.mod
 // file to their workspace.
 func TestNoMod(t *testing.T) {
@@ -293,8 +343,10 @@
 			// package renaming was fully processed. Therefore, in order for this
 			// test to actually exercise the bug, we must wait until that work has
 			// completed.
-			NoDiagnostics("a.go"),
-			CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1),
+			OnceMet(
+				CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1),
+				NoDiagnostics("a.go"),
+			),
 		)
 	})
 }
@@ -473,7 +525,13 @@
 `
 	runner.Run(t, noModule, func(t *testing.T, env *Env) {
 		env.OpenFile("a.go")
-		env.Await(NoDiagnostics("a.go"), EmptyShowMessage(""))
+		env.Await(
+			OnceMet(
+				CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1),
+				NoDiagnostics("a.go"),
+			),
+			EmptyShowMessage(""),
+		)
 		// introduce an error, expect no Show Message
 		env.RegexpReplace("a.go", "func", "fun")
 		env.Await(env.DiagnosticAtRegexp("a.go", "fun"), EmptyShowMessage(""))
@@ -514,8 +572,293 @@
 		}
 		badFile := fmt.Sprintf("%s/found packages main (main.go) and x (x.go) in %s/src/x", dir, env.Sandbox.GOPATH())
 		env.Await(
-			EmptyDiagnostics("x/main.go"),
+			OnceMet(
+				CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1),
+				EmptyDiagnostics("x/main.go"),
+			),
 			NoDiagnostics(badFile),
 		)
 	}, InGOPATH())
 }
+
+const ardanLabsProxy = `
+-- github.com/ardanlabs/conf@v1.2.3/go.mod --
+module github.com/ardanlabs/conf
+
+go 1.12
+-- github.com/ardanlabs/conf@v1.2.3/conf.go --
+package conf
+
+var ErrHelpWanted error
+`
+
+// Test for golang/go#38211.
+func Test_Issue38211(t *testing.T) {
+	testenv.NeedsGo1Point(t, 14)
+	const ardanLabs = `
+-- go.mod --
+module mod.com
+
+go 1.14
+-- main.go --
+package main
+
+import "github.com/ardanlabs/conf"
+
+func main() {
+	_ = conf.ErrHelpWanted
+}
+`
+	runner.Run(t, ardanLabs, func(t *testing.T, env *Env) {
+		// Expect a diagnostic with a suggested fix to add
+		// "github.com/ardanlabs/conf" to the go.mod file.
+		env.OpenFile("go.mod")
+		env.OpenFile("main.go")
+		metBy := env.Await(
+			env.DiagnosticAtRegexp("main.go", `"github.com/ardanlabs/conf"`),
+		)
+		d, ok := metBy[0].(*protocol.PublishDiagnosticsParams)
+		if !ok {
+			t.Fatalf("unexpected type for metBy (%T)", metBy)
+		}
+		env.ApplyQuickFixes("main.go", d.Diagnostics)
+		env.SaveBuffer("go.mod")
+		env.Await(
+			EmptyDiagnostics("main.go"),
+		)
+		// Comment out the line that depends on conf and expect a
+		// diagnostic and a fix to remove the import.
+		env.RegexpReplace("main.go", "_ = conf.ErrHelpWanted", "//_ = conf.ErrHelpWanted")
+		env.Await(
+			env.DiagnosticAtRegexp("main.go", `"github.com/ardanlabs/conf"`),
+		)
+		env.SaveBuffer("main.go")
+		// Expect a diagnostic and fix to remove the dependency in the go.mod.
+		metBy = env.Await(
+			EmptyDiagnostics("main.go"),
+			env.DiagnosticAtRegexp("go.mod", "require github.com/ardanlabs/conf"),
+		)
+		d, ok = metBy[1].(*protocol.PublishDiagnosticsParams)
+		if !ok {
+			t.Fatalf("unexpected type for metBy (%T)", metBy)
+		}
+		env.ApplyQuickFixes("go.mod", d.Diagnostics)
+		env.SaveBuffer("go.mod")
+		env.Await(
+			EmptyDiagnostics("go.mod"),
+		)
+		// Uncomment the lines and expect a new diagnostic for the import.
+		env.RegexpReplace("main.go", "//_ = conf.ErrHelpWanted", "_ = conf.ErrHelpWanted")
+		env.SaveBuffer("main.go")
+		env.Await(
+			env.DiagnosticAtRegexp("main.go", `"github.com/ardanlabs/conf"`),
+		)
+	}, WithProxy(ardanLabsProxy))
+}
+
+// Test for golang/go#38207.
+func TestNewModule_Issue38207(t *testing.T) {
+	testenv.NeedsGo1Point(t, 14)
+	const emptyFile = `
+-- go.mod --
+module mod.com
+
+go 1.12
+-- main.go --
+`
+	runner.Run(t, emptyFile, func(t *testing.T, env *Env) {
+		env.OpenFile("main.go")
+		env.OpenFile("go.mod")
+		env.EditBuffer("main.go", fake.NewEdit(0, 0, 0, 0, `package main
+
+import "github.com/ardanlabs/conf"
+
+func main() {
+	_ = conf.ErrHelpWanted
+}
+`))
+		env.SaveBuffer("main.go")
+		metBy := env.Await(
+			env.DiagnosticAtRegexp("main.go", `"github.com/ardanlabs/conf"`),
+		)
+		d, ok := metBy[0].(*protocol.PublishDiagnosticsParams)
+		if !ok {
+			t.Fatalf("unexpected type for diagnostics (%T)", d)
+		}
+		env.ApplyQuickFixes("main.go", d.Diagnostics)
+		env.Await(
+			EmptyDiagnostics("main.go"),
+		)
+	}, WithProxy(ardanLabsProxy))
+}
+
+// Test for golang/go#36960.
+func TestNewFileBadImports_Issue36960(t *testing.T) {
+	testenv.NeedsGo1Point(t, 14)
+	const simplePackage = `
+-- go.mod --
+module mod.com
+
+go 1.14
+-- a/a1.go --
+package a
+
+import "fmt"
+
+func _() {
+	fmt.Println("hi")
+}
+`
+	runner.Run(t, simplePackage, func(t *testing.T, env *Env) {
+		env.OpenFile("a/a1.go")
+		env.CreateBuffer("a/a2.go", ``)
+		if err := env.Editor.SaveBufferWithoutActions(env.Ctx, "a/a2.go"); err != nil {
+			t.Fatal(err)
+		}
+		env.Await(
+			OnceMet(
+				CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidSave), 1),
+				NoDiagnostics("a/a1.go"),
+			),
+		)
+		env.EditBuffer("a/a2.go", fake.NewEdit(0, 0, 0, 0, `package a`))
+		env.Await(
+			OnceMet(
+				CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1),
+				NoDiagnostics("a/a1.go"),
+			),
+		)
+	})
+}
+
+// This test tries to replicate the workflow of a user creating a new x test.
+// It also tests golang/go#39315.
+func TestManuallyCreatingXTest(t *testing.T) {
+	// Only for 1.15 because of golang/go#37971.
+	testenv.NeedsGo1Point(t, 15)
+
+	// Create a package that already has a test variant (in-package test).
+	const testVariant = `
+-- go.mod --
+module mod.com
+
+go 1.15
+-- hello/hello.go --
+package hello
+
+func Hello() {
+	var x int
+}
+-- hello/hello_test.go --
+package hello
+
+import "testing"
+
+func TestHello(t *testing.T) {
+	var x int
+	Hello()
+}
+`
+	runner.Run(t, testVariant, func(t *testing.T, env *Env) {
+		// Open the file, triggering the workspace load.
+		// There are errors in the code to ensure all is working as expected.
+		env.OpenFile("hello/hello.go")
+		env.Await(
+			env.DiagnosticAtRegexp("hello/hello.go", "x"),
+			env.DiagnosticAtRegexp("hello/hello_test.go", "x"),
+		)
+
+		// Create an empty file with the intention of making it an x test.
+		// This resembles a typical flow in an editor like VS Code, in which
+		// a user would create an empty file and add content, saving
+		// intermittently.
+		// TODO(rstambler): There might be more edge cases here, as file
+		// content can be added incrementally.
+		env.CreateBuffer("hello/hello_x_test.go", ``)
+
+		// Save the empty file (no actions since formatting will fail).
+		env.Editor.SaveBufferWithoutActions(env.Ctx, "hello/hello_x_test.go")
+
+		// Add the content. The missing import is for the package under test.
+		env.EditBuffer("hello/hello_x_test.go", fake.NewEdit(0, 0, 0, 0, `package hello_test
+
+import (
+	"testing"
+)
+
+func TestHello(t *testing.T) {
+	hello.Hello()
+}
+`))
+		// Expect a diagnostic for the missing import. Save, which should
+		// trigger import organization. The diagnostic should clear.
+		env.Await(
+			env.DiagnosticAtRegexp("hello/hello_x_test.go", "hello.Hello"),
+		)
+		env.SaveBuffer("hello/hello_x_test.go")
+		env.Await(
+			EmptyDiagnostics("hello/hello_x_test.go"),
+		)
+	})
+}
+
+func TestIgnoredFiles(t *testing.T) {
+	const ws = `
+-- go.mod --
+module mod.com
+
+go 1.15
+-- _foo/x.go --
+package x
+
+var _ = foo.Bar
+`
+	runner.Run(t, ws, func(t *testing.T, env *Env) {
+		env.OpenFile("_foo/x.go")
+		env.Await(
+			OnceMet(
+				CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1),
+				NoDiagnostics("_foo/x.go"),
+			))
+	})
+}
+
+// Partially reproduces golang/go#38977, moving a file between packages.
+// It also gets hit by some go command bug fixed in 1.15, but we don't
+// care about that so much here.
+func TestDeletePackage(t *testing.T) {
+	const ws = `
+-- go.mod --
+module mod.com
+
+go 1.15
+-- a/a.go --
+package a
+
+const A = 1
+
+-- b/b.go --
+package b
+
+import "mod.com/a"
+
+const B = a.A
+
+-- c/c.go --
+package c
+
+import "mod.com/a"
+
+const C = a.A
+`
+	runner.Run(t, ws, func(t *testing.T, env *Env) {
+		env.OpenFile("b/b.go")
+		env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1))
+		// Delete c/c.go, the only file in package c.
+		env.RemoveWorkspaceFile("c/c.go")
+
+		// We should still get diagnostics for files that exist.
+		env.RegexpReplace("b/b.go", `a.A`, "a.Nonexistant")
+		env.Await(env.DiagnosticAtRegexp("b/b.go", `Nonexistant`))
+	})
+}
diff --git a/internal/lsp/regtest/env.go b/internal/lsp/regtest/env.go
index 08fc842..d86fe27 100644
--- a/internal/lsp/regtest/env.go
+++ b/internal/lsp/regtest/env.go
@@ -12,7 +12,6 @@
 	"sync"
 	"testing"
 
-	"golang.org/x/tools/internal/jsonrpc2"
 	"golang.org/x/tools/internal/jsonrpc2/servertest"
 	"golang.org/x/tools/internal/lsp/fake"
 	"golang.org/x/tools/internal/lsp/protocol"
@@ -31,7 +30,6 @@
 	Sandbox *fake.Sandbox
 	Editor  *fake.Editor
 	Server  servertest.Connector
-	Conn    *jsonrpc2.Conn
 
 	// mu guards the fields below, for the purpose of checking conditions on
 	// every change to diagnostics.
@@ -45,9 +43,10 @@
 // State encapsulates the server state TODO: explain more
 type State struct {
 	// diagnostics are a map of relative path->diagnostics params
-	diagnostics map[string]*protocol.PublishDiagnosticsParams
-	logs        []*protocol.LogMessageParams
-	showMessage []*protocol.ShowMessageParams
+	diagnostics        map[string]*protocol.PublishDiagnosticsParams
+	logs               []*protocol.LogMessageParams
+	showMessage        []*protocol.ShowMessageParams
+	showMessageRequest []*protocol.ShowMessageRequestParams
 	// outstandingWork is a map of token->work summary. All tokens are assumed to
 	// be string, though the spec allows for numeric tokens as well.  When work
 	// completes, it is deleted from this map.
@@ -87,7 +86,11 @@
 		if name == "" {
 			name = fmt.Sprintf("!NO NAME(token: %s)", token)
 		}
-		fmt.Fprintf(&b, "\t%s: %.2f", name, state.percent)
+		fmt.Fprintf(&b, "\t%s: %.2f\n", name, state.percent)
+	}
+	b.WriteString("#### completed work:\n")
+	for name, count := range s.completedWork {
+		fmt.Fprintf(&b, "\t%s: %d\n", name, count)
 	}
 	return b.String()
 }
@@ -110,7 +113,6 @@
 		Ctx:     ctx,
 		Sandbox: scratch,
 		Server:  ts,
-		Conn:    conn,
 		state: State{
 			diagnostics:     make(map[string]*protocol.PublishDiagnosticsParams),
 			outstandingWork: make(map[string]*workProgress),
@@ -124,6 +126,7 @@
 		OnWorkDoneProgressCreate: env.onWorkDoneProgressCreate,
 		OnProgress:               env.onProgress,
 		OnShowMessage:            env.onShowMessage,
+		OnShowMessageRequest:     env.onShowMessageRequest,
 	}
 	editor, err := fake.NewEditor(scratch, editorConfig).Connect(ctx, conn, hooks)
 	if err != nil {
@@ -152,6 +155,15 @@
 	return nil
 }
 
+func (e *Env) onShowMessageRequest(_ context.Context, m *protocol.ShowMessageRequestParams) error {
+	e.mu.Lock()
+	defer e.mu.Unlock()
+
+	e.state.showMessageRequest = append(e.state.showMessageRequest, m)
+	e.checkConditionsLocked()
+	return nil
+}
+
 func (e *Env) onLogMessage(_ context.Context, m *protocol.LogMessageParams) error {
 	e.mu.Lock()
 	defer e.mu.Unlock()
@@ -366,6 +378,29 @@
 	}
 }
 
+// ShowMessageRequest asserts that the editor has received a ShowMessageRequest
+// with an action item that has the given title.
+func ShowMessageRequest(title string) SimpleExpectation {
+	check := func(s State) (Verdict, interface{}) {
+		if len(s.showMessageRequest) == 0 {
+			return Unmet, nil
+		}
+		// Only check the most recent one.
+		m := s.showMessageRequest[len(s.showMessageRequest)-1]
+		if len(m.Actions) == 0 || len(m.Actions) > 1 {
+			return Unmet, nil
+		}
+		if m.Actions[0].Title == title {
+			return Met, m.Actions[0]
+		}
+		return Unmet, nil
+	}
+	return SimpleExpectation{
+		check:       check,
+		description: "received ShowMessageRequest",
+	}
+}
+
 // CompletedWork expects a work item to have been completed >= atLeast times.
 //
 // Since the Progress API doesn't include any hidden metadata, we must use the
@@ -479,7 +514,9 @@
 }
 
 // NoDiagnostics asserts that no diagnostics are sent for the
-// workspace-relative path name.
+// workspace-relative path name. It should be used primarily in conjunction
+// with a OnceMet, as it has to check that all outstanding diagnostics have
+// already been delivered.
 func NoDiagnostics(name string) Expectation {
 	check := func(s State) (Verdict, interface{}) {
 		if _, ok := s.diagnostics[name]; !ok {
@@ -489,7 +526,7 @@
 	}
 	return SimpleExpectation{
 		check:       check,
-		description: "empty diagnostics",
+		description: "no diagnostics",
 	}
 }
 
@@ -512,6 +549,7 @@
 // position matching the regexp search string re in the buffer specified by
 // name. Note that this currently ignores the end position.
 func (e *Env) DiagnosticAtRegexp(name, re string) DiagnosticExpectation {
+	e.T.Helper()
 	pos := e.RegexpSearch(name, re)
 	expectation := DiagnosticAt(name, pos.Line, pos.Column)
 	expectation.description += fmt.Sprintf(" (location of %q)", re)
@@ -536,6 +574,15 @@
 	}
 }
 
+// DiagnosticsFor returns the current diagnostics for the file. It is useful
+// after waiting on AnyDiagnosticAtCurrentVersion, when the desired diagnostic
+// is not simply described by DiagnosticAt.
+func (e *Env) DiagnosticsFor(name string) *protocol.PublishDiagnosticsParams {
+	e.mu.Lock()
+	defer e.mu.Unlock()
+	return e.state.diagnostics[name]
+}
+
 // Await waits for all expectations to simultaneously be met. It should only be
 // called from the main test goroutine.
 func (e *Env) Await(expectations ...Expectation) []interface{} {
diff --git a/internal/lsp/regtest/formatting_test.go b/internal/lsp/regtest/formatting_test.go
index a2e83b3..4f200fc 100644
--- a/internal/lsp/regtest/formatting_test.go
+++ b/internal/lsp/regtest/formatting_test.go
@@ -2,6 +2,8 @@
 
 import (
 	"testing"
+
+	"golang.org/x/tools/internal/lsp/tests"
 )
 
 const unformattedProgram = `
@@ -33,8 +35,10 @@
 	})
 }
 
-// this is the fixed case from #36824
-const onelineProgram = `
+// Tests golang/go#36824.
+func TestFormattingOneLine36824(t *testing.T) {
+
+	const onelineProgram = `
 -- a.go --
 package main; func f() {}
 
@@ -43,8 +47,6 @@
 
 func f() {}
 `
-
-func TestFormattingOneLine36824(t *testing.T) {
 	runner.Run(t, onelineProgram, func(t *testing.T, env *Env) {
 		env.OpenFile("a.go")
 		env.FormatBuffer("a.go")
@@ -56,7 +58,10 @@
 	})
 }
 
-const onelineProgramA = `
+// Tests golang/go#36824.
+func TestFormattingOneLineImports36824(t *testing.T) {
+
+	const onelineProgramA = `
 -- a.go --
 package x; func f() {fmt.Println()}
 
@@ -67,18 +72,34 @@
 
 func f() { fmt.Println() }
 `
-
-// this is the example from #36824 done properly
-// but gopls does not reformat before fixing the imports
-func TestFormattingOneLineImports36824(t *testing.T) {
 	runner.Run(t, onelineProgramA, func(t *testing.T, env *Env) {
 		env.OpenFile("a.go")
-		env.FormatBuffer("a.go")
 		env.OrganizeImports("a.go")
 		got := env.Editor.BufferText("a.go")
 		want := env.ReadWorkspaceFile("a.go.imported")
 		if got != want {
-			t.Errorf("OneLineImports go\n%q wanted\n%q", got, want)
+			t.Errorf("OneLineImports3824:\n%s", tests.Diff(want, got))
+		}
+	})
+}
+
+func TestFormattingOneLineRmImports36824(t *testing.T) {
+	const onelineProgramB = `
+-- a.go --
+package x; import "os"; func f() {}
+
+-- a.go.imported --
+package x
+
+func f() {}
+`
+	runner.Run(t, onelineProgramB, func(t *testing.T, env *Env) {
+		env.OpenFile("a.go")
+		env.OrganizeImports("a.go")
+		got := env.Editor.BufferText("a.go")
+		want := env.ReadWorkspaceFile("a.go.imported")
+		if got != want {
+			t.Errorf("OneLineRmImports:\n%s", tests.Diff(want, got))
 		}
 	})
 }
diff --git a/internal/lsp/regtest/imports_test.go b/internal/lsp/regtest/imports_test.go
new file mode 100644
index 0000000..fd35f34
--- /dev/null
+++ b/internal/lsp/regtest/imports_test.go
@@ -0,0 +1,100 @@
+package regtest
+
+import (
+	"testing"
+)
+
+const needs = `
+-- go.mod --
+module foo
+
+-- a.go --
+package main
+func f() {}
+`
+const ntest = `package main
+func TestZ(t *testing.T) {
+	f()
+}
+`
+const want = `package main
+
+import "testing"
+
+func TestZ(t *testing.T) {
+	f()
+}
+`
+
+func TestIssue38815(t *testing.T) {
+	// it was returning
+	// "package main\nimport \"testing\"\npackage main..."
+	runner.Run(t, needs, func(t *testing.T, env *Env) {
+		env.CreateBuffer("a_test.go", ntest)
+		env.SaveBuffer("a_test.go")
+		got := env.Editor.BufferText("a_test.go")
+		if want != got {
+			t.Errorf("got\n%q, wanted\n%q", got, want)
+		}
+	})
+}
+
+const vim1 = `package main
+
+import "fmt"
+
+var foo = 1
+var bar = 2
+
+func main() {
+	fmt.Printf("This is a test %v\n", foo)
+	fmt.Printf("This is another test %v\n", foo)
+	fmt.Printf("This is also a test %v\n", foo)
+}
+`
+
+func TestVim1(t *testing.T) {
+	// The file remains unchanged, but if there are any CodeActions returned, they confuse vim.
+	// Therefore check for no CodeActions
+	runner.Run(t, vim1, func(t *testing.T, env *Env) {
+		env.CreateBuffer("main.go", vim1)
+		env.OrganizeImports("main.go")
+		actions := env.CodeAction("main.go")
+		if len(actions) > 0 {
+			got := env.Editor.BufferText("main.go")
+			t.Errorf("unexpected actions %#v", actions)
+			if got == vim1 {
+				t.Errorf("no changes")
+			} else {
+				t.Errorf("got\n%q", got)
+				t.Errorf("was\n%q", vim1)
+			}
+		}
+	})
+}
+
+const vim2 = `package main
+
+import (
+	"fmt"
+
+	"example.com/blah"
+
+	"rubbish.com/useless"
+)
+
+func main() {
+	fmt.Println(blah.Name, useless.Name)
+}
+`
+
+func TestVim2(t *testing.T) {
+	runner.Run(t, vim1, func(t *testing.T, env *Env) {
+		env.CreateBuffer("main.go", vim2)
+		env.OrganizeImports("main.go")
+		actions := env.CodeAction("main.go")
+		if len(actions) > 0 {
+			t.Errorf("unexpected actions %#v", actions)
+		}
+	})
+}
diff --git a/internal/lsp/regtest/link_test.go b/internal/lsp/regtest/link_test.go
new file mode 100644
index 0000000..913abf4
--- /dev/null
+++ b/internal/lsp/regtest/link_test.go
@@ -0,0 +1,85 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package regtest
+
+import (
+	"strings"
+	"testing"
+)
+
+func TestHoverAndDocumentLink(t *testing.T) {
+	const program = `
+-- go.mod --
+module mod.test
+
+go 1.12
+
+require import.test v1.2.3
+-- main.go --
+package main
+
+import "import.test/pkg"
+
+func main() {
+	println(pkg.Hello)
+}`
+
+	const proxy = `
+-- import.test@v1.2.3/go.mod --
+module import.test
+
+go 1.12
+-- import.test@v1.2.3/pkg/const.go --
+package pkg
+
+const Hello = "Hello"
+`
+	runner.Run(t, program, func(t *testing.T, env *Env) {
+		env.OpenFile("main.go")
+		env.OpenFile("go.mod")
+
+		modLink := "https://pkg.go.dev/mod/import.test@v1.2.3"
+		pkgLink := "https://pkg.go.dev/import.test@v1.2.3/pkg"
+
+		// First, check that we get the expected links via hover and documentLink.
+		content, _ := env.Hover("main.go", env.RegexpSearch("main.go", "pkg.Hello"))
+		if content == nil || !strings.Contains(content.Value, pkgLink) {
+			t.Errorf("hover: got %v in main.go, want contains %q", content, pkgLink)
+		}
+		content, _ = env.Hover("go.mod", env.RegexpSearch("go.mod", "import.test"))
+		if content == nil || !strings.Contains(content.Value, pkgLink) {
+			t.Errorf("hover: got %v in go.mod, want contains %q", content, pkgLink)
+		}
+		links := env.DocumentLink("main.go")
+		if len(links) != 1 || links[0].Target != pkgLink {
+			t.Errorf("documentLink: got %v for main.go, want link to %q", links, pkgLink)
+		}
+		links = env.DocumentLink("go.mod")
+		if len(links) != 1 || links[0].Target != modLink {
+			t.Errorf("documentLink: got %v for go.mod, want link to %q", links, modLink)
+		}
+
+		// Then change the environment to make these links private.
+		env.ChangeEnv("GOPRIVATE=import.test")
+
+		// Finally, verify that the links are gone.
+		content, _ = env.Hover("main.go", env.RegexpSearch("main.go", "pkg.Hello"))
+		if content == nil || strings.Contains(content.Value, pkgLink) {
+			t.Errorf("hover: got %v in main.go, want non-empty hover without %q", content, pkgLink)
+		}
+		content, _ = env.Hover("go.mod", env.RegexpSearch("go.mod", "import.test"))
+		if content == nil || strings.Contains(content.Value, modLink) {
+			t.Errorf("hover: got %v in go.mod, want contains %q", content, modLink)
+		}
+		links = env.DocumentLink("main.go")
+		if len(links) != 0 {
+			t.Errorf("documentLink: got %d document links for main.go, want 0\nlinks: %v", len(links), links)
+		}
+		links = env.DocumentLink("go.mod")
+		if len(links) != 0 {
+			t.Errorf("documentLink: got %d document links for go.mod, want 0\nlinks: %v", len(links), links)
+		}
+	}, WithProxy(proxy))
+}
diff --git a/internal/lsp/regtest/modfile_test.go b/internal/lsp/regtest/modfile_test.go
new file mode 100644
index 0000000..47a0ed2
--- /dev/null
+++ b/internal/lsp/regtest/modfile_test.go
@@ -0,0 +1,60 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package regtest
+
+import (
+	"testing"
+
+	"golang.org/x/tools/internal/lsp/tests"
+	"golang.org/x/tools/internal/testenv"
+)
+
+func TestModFileModification(t *testing.T) {
+	testenv.NeedsGo1Point(t, 14)
+
+	const proxy = `
+-- example.com@v1.2.3/go.mod --
+module example.com
+
+go 1.12
+-- example.com@v1.2.3/blah/blah.go --
+package blah
+
+const Name = "Blah"
+`
+	const untidyModule = `
+-- go.mod --
+module mod.com
+
+-- main.go --
+package main
+
+import "example.com/blah"
+
+func main() {
+	fmt.Println(blah.Name)
+}`
+	runner.Run(t, untidyModule, func(t *testing.T, env *Env) {
+		// Open the file and make sure that the initial workspace load does not
+		// modify the go.mod file.
+		goModContent := env.ReadWorkspaceFile("go.mod")
+		env.OpenFile("main.go")
+		env.Await(
+			env.DiagnosticAtRegexp("main.go", "\"example.com/blah\""),
+		)
+		if got := env.ReadWorkspaceFile("go.mod"); got != goModContent {
+			t.Fatalf("go.mod changed on disk:\n%s", tests.Diff(want, got))
+		}
+		// Save the buffer, which will format and organize imports.
+		// Confirm that the go.mod file still does not change.
+		env.SaveBuffer("main.go")
+		env.Await(
+			env.DiagnosticAtRegexp("main.go", "\"example.com/blah\""),
+		)
+		if got := env.ReadWorkspaceFile("go.mod"); got != goModContent {
+			t.Fatalf("go.mod changed on disk:\n%s", tests.Diff(want, got))
+		}
+	}, WithProxy(proxy))
+}
diff --git a/internal/lsp/regtest/reg_test.go b/internal/lsp/regtest/reg_test.go
index e4bd925..9f0e283 100644
--- a/internal/lsp/regtest/reg_test.go
+++ b/internal/lsp/regtest/reg_test.go
@@ -13,16 +13,14 @@
 	"time"
 
 	"golang.org/x/tools/internal/lsp/cmd"
-	"golang.org/x/tools/internal/lsp/lsprpc"
 	"golang.org/x/tools/internal/tool"
 )
 
 var (
 	runSubprocessTests       = flag.Bool("enable_gopls_subprocess_tests", false, "run regtests against a gopls subprocess")
-	goplsBinaryPath          = flag.String("gopls_test_binary", "", "path to the gopls binary for use as a remote, for use with the -gopls_subprocess_testmode flag")
-	alwaysPrintLogs          = flag.Bool("regtest_print_rpc_logs", false, "whether to always print RPC logs")
+	goplsBinaryPath          = flag.String("gopls_test_binary", "", "path to the gopls binary for use as a remote, for use with the -enable_gopls_subprocess_tests flag")
 	regtestTimeout           = flag.Duration("regtest_timeout", 60*time.Second, "default timeout for each regtest")
-	printGoroutinesOnFailure = flag.Bool("regtest_print_goroutines", false, "whether to print goroutine info on failure")
+	printGoroutinesOnFailure = flag.Bool("regtest_print_goroutines", false, "whether to print goroutines info on failure")
 )
 
 var runner *Runner
@@ -33,13 +31,10 @@
 		tool.Main(context.Background(), cmd.New("gopls", "", nil, nil), os.Args[1:])
 		os.Exit(0)
 	}
-	resetExitFuncs := lsprpc.OverrideExitFuncsForTest()
-	defer resetExitFuncs()
 
 	runner = &Runner{
 		DefaultModes:             NormalModes,
 		Timeout:                  *regtestTimeout,
-		AlwaysPrintLogs:          *alwaysPrintLogs,
 		PrintGoroutinesOnFailure: *printGoroutinesOnFailure,
 	}
 	if *runSubprocessTests {
diff --git a/internal/lsp/regtest/runner.go b/internal/lsp/regtest/runner.go
index 3d838fa..77b0270 100644
--- a/internal/lsp/regtest/runner.go
+++ b/internal/lsp/regtest/runner.go
@@ -10,6 +10,7 @@
 	"fmt"
 	"io"
 	"io/ioutil"
+	"net"
 	"os"
 	"os/exec"
 	"path/filepath"
@@ -54,7 +55,6 @@
 	DefaultModes             Mode
 	Timeout                  time.Duration
 	GoplsPath                string
-	AlwaysPrintLogs          bool
 	PrintGoroutinesOnFailure bool
 
 	mu        sync.Mutex
@@ -185,51 +185,49 @@
 				r.AddCloser(sandbox)
 			}
 			ss := tc.getServer(ctx, t)
-			ls := &loggingServer{delegate: ss}
-			ts := servertest.NewPipeServer(ctx, ls)
-			defer func() {
-				ts.Close()
-			}()
+			ls := &loggingFramer{}
+			framer := ls.framer(jsonrpc2.NewRawStream)
+			ts := servertest.NewPipeServer(ctx, ss, framer)
 			env := NewEnv(ctx, t, sandbox, ts, config.editorConfig)
 			defer func() {
 				if t.Failed() && r.PrintGoroutinesOnFailure {
 					pprof.Lookup("goroutine").WriteTo(os.Stderr, 1)
 				}
-				if t.Failed() || r.AlwaysPrintLogs {
+				if t.Failed() || testing.Verbose() {
 					ls.printBuffers(t.Name(), os.Stderr)
 				}
-				if err := env.Editor.Shutdown(ctx); err != nil {
-					panic(err)
-				}
+				env.CloseEditor()
 			}()
 			test(t, env)
 		})
 	}
 }
 
-type loggingServer struct {
-	delegate jsonrpc2.StreamServer
-
+type loggingFramer struct {
 	mu      sync.Mutex
 	buffers []*bytes.Buffer
 }
 
-func (s *loggingServer) ServeStream(ctx context.Context, stream jsonrpc2.Stream) error {
-	s.mu.Lock()
-	var buf bytes.Buffer
-	s.buffers = append(s.buffers, &buf)
-	s.mu.Unlock()
-	logStream := protocol.LoggingStream(stream, &buf)
-	return s.delegate.ServeStream(ctx, logStream)
+func (s *loggingFramer) framer(f jsonrpc2.Framer) jsonrpc2.Framer {
+	return func(nc net.Conn) jsonrpc2.Stream {
+		s.mu.Lock()
+		var buf bytes.Buffer
+		s.buffers = append(s.buffers, &buf)
+		s.mu.Unlock()
+		stream := f(nc)
+		return protocol.LoggingStream(stream, &buf)
+	}
 }
 
-func (s *loggingServer) printBuffers(testname string, w io.Writer) {
+func (s *loggingFramer) printBuffers(testname string, w io.Writer) {
 	s.mu.Lock()
 	defer s.mu.Unlock()
 
 	for i, buf := range s.buffers {
 		fmt.Fprintf(os.Stderr, "#### Start Gopls Test Logs %d of %d for %q\n", i+1, len(s.buffers), testname)
-		io.Copy(w, buf)
+		// Re-buffer buf to avoid a data rate (io.Copy mutates src).
+		writeBuf := bytes.NewBuffer(buf.Bytes())
+		io.Copy(w, writeBuf)
 		fmt.Fprintf(os.Stderr, "#### End Gopls Test Logs %d of %d for %q\n", i+1, len(s.buffers), testname)
 	}
 }
@@ -252,7 +250,7 @@
 		ctx := context.Background()
 		ctx = debug.WithInstance(ctx, "", "")
 		ss := lsprpc.NewStreamServer(cache.New(ctx, nil))
-		r.ts = servertest.NewTCPServer(context.Background(), ss)
+		r.ts = servertest.NewTCPServer(ctx, ss, nil)
 	}
 	return r.ts
 }
diff --git a/internal/lsp/regtest/serialization_test.go b/internal/lsp/regtest/serialization_test.go
deleted file mode 100644
index 571c376..0000000
--- a/internal/lsp/regtest/serialization_test.go
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package regtest
-
-import (
-	"encoding/json"
-	"testing"
-
-	"golang.org/x/tools/internal/lsp/protocol"
-)
-
-const simpleProgram = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-import "fmt"
-
-func main() {
-	fmt.Println("Hello World.")
-}`
-
-func TestHoverSerialization(t *testing.T) {
-	runner.Run(t, simpleProgram, func(t *testing.T, env *Env) {
-		// Hover on an empty line.
-		params := protocol.HoverParams{}
-		params.TextDocument.URI = env.Sandbox.Workdir.URI("main.go")
-		params.Position.Line = 3
-		params.Position.Character = 0
-		var resp json.RawMessage
-		if err := protocol.Call(env.Ctx, env.Conn, "textDocument/hover", &params, &resp); err != nil {
-			t.Fatal(err)
-		}
-		if len(string(resp)) > 0 {
-			t.Errorf("got non-empty response for empty hover: %v", string(resp))
-		}
-	})
-}
diff --git a/internal/lsp/regtest/symbol_helper_test.go b/internal/lsp/regtest/symbol_helper_test.go
new file mode 100644
index 0000000..c4ece70
--- /dev/null
+++ b/internal/lsp/regtest/symbol_helper_test.go
@@ -0,0 +1,114 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package regtest
+
+import (
+	"encoding/json"
+	"fmt"
+
+	"golang.org/x/tools/internal/lsp/fake"
+	"golang.org/x/tools/internal/lsp/protocol"
+)
+
+// expSymbolInformation and the types it references are pointer-based versions
+// of fake.SymbolInformation, used to make it easier to partially assert
+// against values of type fake.SymbolInformation
+
+// expSymbolInformation is a pointer-based version of fake.SymbolInformation
+type expSymbolInformation struct {
+	Name     *string
+	Kind     *protocol.SymbolKind
+	Location *expLocation
+}
+
+func (e *expSymbolInformation) matchAgainst(sis []fake.SymbolInformation) bool {
+	for _, si := range sis {
+		if e.match(si) {
+			return true
+		}
+	}
+	return false
+}
+
+func (e *expSymbolInformation) match(si fake.SymbolInformation) bool {
+	if e.Name != nil && *e.Name != si.Name {
+		return false
+	}
+	if e.Kind != nil && *e.Kind != si.Kind {
+		return false
+	}
+	if e.Location != nil && !e.Location.match(si.Location) {
+		return false
+	}
+	return true
+}
+
+func (e *expSymbolInformation) String() string {
+	byts, err := json.MarshalIndent(e, "", "  ")
+	if err != nil {
+		panic(fmt.Errorf("failed to json.Marshal *expSymbolInformation: %v", err))
+	}
+	return string(byts)
+}
+
+// expLocation is a pointer-based version of fake.Location
+type expLocation struct {
+	Path  *string
+	Range *expRange
+}
+
+func (e *expLocation) match(l fake.Location) bool {
+	if e.Path != nil && *e.Path != l.Path {
+		return false
+	}
+	if e.Range != nil && !e.Range.match(l.Range) {
+		return false
+	}
+	return true
+}
+
+// expRange is a pointer-based version of fake.Range
+type expRange struct {
+	Start *expPos
+	End   *expPos
+}
+
+func (e *expRange) match(l fake.Range) bool {
+	if e.Start != nil && !e.Start.match(l.Start) {
+		return false
+	}
+	if e.End != nil && !e.End.match(l.End) {
+		return false
+	}
+	return true
+}
+
+// expPos is a pointer-based version of fake.Pos
+type expPos struct {
+	Line   *int
+	Column *int
+}
+
+func (e *expPos) match(l fake.Pos) bool {
+	if e.Line != nil && *e.Line != l.Line {
+		return false
+	}
+	if e.Column != nil && *e.Column != l.Column {
+		return false
+	}
+	return true
+}
+
+func pString(s string) *string {
+	return &s
+}
+
+func pInt(i int) *int {
+	return &i
+}
+
+func pKind(k protocol.SymbolKind) *protocol.SymbolKind {
+	return &k
+}
diff --git a/internal/lsp/regtest/symbol_test.go b/internal/lsp/regtest/symbol_test.go
new file mode 100644
index 0000000..094c49f
--- /dev/null
+++ b/internal/lsp/regtest/symbol_test.go
@@ -0,0 +1,58 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package regtest
+
+import (
+	"testing"
+
+	"golang.org/x/tools/internal/lsp/fake"
+)
+
+const symbolSetup = `
+-- go.mod --
+module mod.com
+
+go 1.12
+-- main.go --
+package main
+
+import "fmt"
+
+func main() {
+	fmt.Println(Message)
+}
+-- const.go --
+package main
+
+const Message = "Hello World."
+`
+
+// TestSymbolPos tests that, at a basic level, we get the correct position
+// information for symbols matches that are returned.
+func TestSymbolPos(t *testing.T) {
+	matcher := "caseSensitive"
+	opts := []RunOption{
+		WithEditorConfig(fake.EditorConfig{SymbolMatcher: &matcher}),
+	}
+
+	runner.Run(t, symbolSetup, func(t *testing.T, env *Env) {
+		res := env.Symbol("main")
+		exp := &expSymbolInformation{
+			Name: pString("main"),
+			Location: &expLocation{
+				Path: pString("main.go"),
+				Range: &expRange{
+					Start: &expPos{
+						Line:   pInt(4),
+						Column: pInt(5),
+					},
+				},
+			},
+		}
+		if !exp.matchAgainst(res) {
+			t.Fatalf("failed to find match for main function")
+		}
+	}, opts...)
+}
diff --git a/internal/lsp/regtest/unix_test.go b/internal/lsp/regtest/unix_test.go
index 955bb5a..c75bc4e 100644
--- a/internal/lsp/regtest/unix_test.go
+++ b/internal/lsp/regtest/unix_test.go
@@ -2,12 +2,11 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !windows
+// +build !windows,!plan9
 
 package regtest
 
 import (
-	"fmt"
 	"testing"
 
 	"golang.org/x/tools/internal/lsp/fake"
@@ -31,6 +30,6 @@
 			t.Fatal(err)
 		}
 	}, WithEditorConfig(fake.EditorConfig{
-		Env: []string{fmt.Sprintf("GOPATH=:/path/to/gopath")},
+		Env: []string{"GOPATH=:/path/to/gopath"},
 	}))
 }
diff --git a/internal/lsp/regtest/vendor_test.go b/internal/lsp/regtest/vendor_test.go
new file mode 100644
index 0000000..72cc937
--- /dev/null
+++ b/internal/lsp/regtest/vendor_test.go
@@ -0,0 +1,62 @@
+package regtest
+
+import (
+	"testing"
+
+	"golang.org/x/tools/internal/lsp"
+	"golang.org/x/tools/internal/testenv"
+)
+
+const basicProxy = `
+-- golang.org/x/hello@v1.2.3/go.mod --
+module golang.org/x/hello
+
+go 1.14
+-- golang.org/x/hello@v1.2.3/hi/hi.go --
+package hi
+
+var Goodbye error
+`
+
+func TestInconsistentVendoring(t *testing.T) {
+	testenv.NeedsGo1Point(t, 14)
+	const pkgThatUsesVendoring = `
+-- go.mod --
+module mod.com
+
+go 1.14
+
+require golang.org/x/hello v1.2.3
+-- vendor/modules.txt --
+-- a/a1.go --
+package a
+
+import "golang.org/x/hello/hi"
+
+func _() {
+	_ = hi.Goodbye
+	var q int // hardcode a diagnostic
+}
+`
+	runner.Run(t, pkgThatUsesVendoring, func(t *testing.T, env *Env) {
+		env.OpenFile("a/a1.go")
+		env.Await(
+			// The editor should pop up a message suggesting that the user
+			// run `go mod vendor`, along with a button to do so.
+			// By default, the fake editor always accepts such suggestions,
+			// so once we see the request, we can assume that `go mod vendor`
+			// will be executed.
+			OnceMet(
+				CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1),
+				ShowMessageRequest("go mod vendor"),
+			),
+		)
+		env.CheckForFileChanges()
+		env.Await(
+			OnceMet(
+				CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 1),
+				DiagnosticAt("a/a1.go", 6, 5),
+			),
+		)
+	}, WithProxy(basicProxy))
+}
diff --git a/internal/lsp/regtest/wrappers.go b/internal/lsp/regtest/wrappers.go
index 4464f43..827dcdb 100644
--- a/internal/lsp/regtest/wrappers.go
+++ b/internal/lsp/regtest/wrappers.go
@@ -5,14 +5,18 @@
 package regtest
 
 import (
+	"errors"
+	"io"
+	"testing"
+
 	"golang.org/x/tools/internal/lsp"
 	"golang.org/x/tools/internal/lsp/fake"
 	"golang.org/x/tools/internal/lsp/protocol"
 )
 
-// RemoveFileFromWorkspace deletes a file on disk but does nothing in the
+// RemoveWorkspaceFile deletes a file on disk but does nothing in the
 // editor. It calls t.Fatal on any error.
-func (e *Env) RemoveFileFromWorkspace(name string) {
+func (e *Env) RemoveWorkspaceFile(name string) {
 	e.T.Helper()
 	if err := e.Sandbox.Workdir.RemoveFile(e.Ctx, name); err != nil {
 		e.T.Fatal(err)
@@ -30,6 +34,15 @@
 	return content
 }
 
+// WriteWorkspaceFile writes a file to disk but does nothing in the editor.
+// It calls t.Fatal on any error.
+func (e *Env) WriteWorkspaceFile(name, content string) {
+	e.T.Helper()
+	if err := e.Sandbox.Workdir.WriteFile(e.Ctx, name, content); err != nil {
+		e.T.Fatal(err)
+	}
+}
+
 // OpenFile opens a file in the editor, calling t.Fatal on any error.
 func (e *Env) OpenFile(name string) {
 	e.T.Helper()
@@ -106,6 +119,16 @@
 	return n, p
 }
 
+// Symbol returns symbols matching query
+func (e *Env) Symbol(query string) []fake.SymbolInformation {
+	e.T.Helper()
+	r, err := e.Editor.Symbol(e.Ctx, query)
+	if err != nil {
+		e.T.Fatal(err)
+	}
+	return r
+}
+
 // FormatBuffer formats the editor buffer, calling t.Fatal on any error.
 func (e *Env) FormatBuffer(name string) {
 	e.T.Helper()
@@ -131,15 +154,36 @@
 	}
 }
 
+// Hover in the editor, calling t.Fatal on any error.
+func (e *Env) Hover(name string, pos fake.Pos) (*protocol.MarkupContent, fake.Pos) {
+	e.T.Helper()
+	c, p, err := e.Editor.Hover(e.Ctx, name, pos)
+	if err != nil {
+		e.T.Fatal(err)
+	}
+	return c, p
+}
+
+func (e *Env) DocumentLink(name string) []protocol.DocumentLink {
+	e.T.Helper()
+	links, err := e.Editor.DocumentLink(e.Ctx, name)
+	if err != nil {
+		e.T.Fatal(err)
+	}
+	return links
+}
+
+func checkIsFatal(t *testing.T, err error) {
+	t.Helper()
+	if err != nil && !errors.Is(err, io.EOF) && !errors.Is(err, io.ErrClosedPipe) {
+		t.Fatal(err)
+	}
+}
+
 // CloseEditor shuts down the editor, calling t.Fatal on any error.
 func (e *Env) CloseEditor() {
 	e.T.Helper()
-	if err := e.Editor.Shutdown(e.Ctx); err != nil {
-		e.T.Fatal(err)
-	}
-	if err := e.Editor.Exit(e.Ctx); err != nil {
-		e.T.Fatal(err)
-	}
+	checkIsFatal(e.T, e.Editor.Close(e.Ctx))
 }
 
 // RunGenerate runs go:generate on the given dir, calling t.Fatal on any error.
@@ -177,3 +221,29 @@
 	}
 	return lens
 }
+
+// CodeAction calls testDocument/codeAction for the given path, and calls
+// t.Fatal if there are errors.
+func (e *Env) CodeAction(path string) []protocol.CodeAction {
+	e.T.Helper()
+	actions, err := e.Editor.CodeAction(e.Ctx, path)
+	if err != nil {
+		e.T.Fatal(err)
+	}
+	return actions
+}
+
+// ChangeEnv modifies the editor environment and reconfigures the LSP client.
+// TODO: extend this to "ChangeConfiguration", once we refactor the way editor
+// configuration is defined.
+func (e *Env) ChangeEnv(envvars ...string) {
+	e.T.Helper()
+	// TODO: to be correct, this should probably be synchronized, but right now
+	// configuration is only ever modified synchronously in a regtest, so this
+	// correctness can wait for the previously mentioned refactoring.
+	e.Editor.Config.Env = append(e.Editor.Config.Env, envvars...)
+	var params protocol.DidChangeConfigurationParams
+	if err := e.Editor.Server.DidChangeConfiguration(e.Ctx, &params); err != nil {
+		e.T.Fatal(err)
+	}
+}
diff --git a/internal/lsp/rename.go b/internal/lsp/rename.go
index 9fe59e9..cbd06ec 100644
--- a/internal/lsp/rename.go
+++ b/internal/lsp/rename.go
@@ -12,7 +12,7 @@
 )
 
 func (s *Server) rename(ctx context.Context, params *protocol.RenameParams) (*protocol.WorkspaceEdit, error) {
-	snapshot, fh, ok, err := s.beginFileRequest(params.TextDocument.URI, source.Go)
+	snapshot, fh, ok, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.Go)
 	if !ok {
 		return nil, err
 	}
@@ -23,7 +23,7 @@
 
 	var docChanges []protocol.TextDocumentEdit
 	for uri, e := range edits {
-		fh, err := snapshot.GetFile(uri)
+		fh, err := snapshot.GetFile(ctx, uri)
 		if err != nil {
 			return nil, err
 		}
@@ -35,7 +35,7 @@
 }
 
 func (s *Server) prepareRename(ctx context.Context, params *protocol.PrepareRenameParams) (*protocol.Range, error) {
-	snapshot, fh, ok, err := s.beginFileRequest(params.TextDocument.URI, source.Go)
+	snapshot, fh, ok, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.Go)
 	if !ok {
 		return nil, err
 	}
diff --git a/internal/lsp/server.go b/internal/lsp/server.go
index 77d6bc7..5366303 100644
--- a/internal/lsp/server.go
+++ b/internal/lsp/server.go
@@ -94,13 +94,13 @@
 }
 
 func (s *Server) codeLens(ctx context.Context, params *protocol.CodeLensParams) ([]protocol.CodeLens, error) {
-	snapshot, fh, ok, err := s.beginFileRequest(params.TextDocument.URI, source.UnknownKind)
+	snapshot, fh, ok, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.UnknownKind)
 	if !ok {
 		return nil, err
 	}
-	switch fh.Identity().Kind {
+	switch fh.Kind() {
 	case source.Mod:
-		return mod.CodeLens(ctx, snapshot, fh.Identity().URI)
+		return mod.CodeLens(ctx, snapshot, fh.URI())
 	case source.Go:
 		return source.CodeLens(ctx, snapshot, fh)
 	}
@@ -112,17 +112,17 @@
 	paramMap := params.(map[string]interface{})
 	if method == "gopls/diagnoseFiles" {
 		for _, file := range paramMap["files"].([]interface{}) {
-			snapshot, fh, ok, err := s.beginFileRequest(protocol.DocumentURI(file.(string)), source.UnknownKind)
+			snapshot, fh, ok, err := s.beginFileRequest(ctx, protocol.DocumentURI(file.(string)), source.UnknownKind)
 			if !ok {
 				return nil, err
 			}
 
-			fileID, diagnostics, err := source.FileDiagnostics(ctx, snapshot, fh.Identity().URI)
+			fileID, diagnostics, err := source.FileDiagnostics(ctx, snapshot, fh.URI())
 			if err != nil {
 				return nil, err
 			}
 			if err := s.client.PublishDiagnostics(ctx, &protocol.PublishDiagnosticsParams{
-				URI:         protocol.URIFromSpanURI(fh.Identity().URI),
+				URI:         protocol.URIFromSpanURI(fh.URI()),
 				Diagnostics: toProtocolDiagnostics(diagnostics),
 				Version:     fileID.Version,
 			}); err != nil {
diff --git a/internal/lsp/signature_help.go b/internal/lsp/signature_help.go
index 4739548..87dfeb0 100644
--- a/internal/lsp/signature_help.go
+++ b/internal/lsp/signature_help.go
@@ -14,7 +14,7 @@
 )
 
 func (s *Server) signatureHelp(ctx context.Context, params *protocol.SignatureHelpParams) (*protocol.SignatureHelp, error) {
-	snapshot, fh, ok, err := s.beginFileRequest(params.TextDocument.URI, source.Go)
+	snapshot, fh, ok, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.Go)
 	if !ok {
 		return nil, err
 	}
diff --git a/internal/lsp/source/code_lens.go b/internal/lsp/source/code_lens.go
index 689a6fe..3de0647 100644
--- a/internal/lsp/source/code_lens.go
+++ b/internal/lsp/source/code_lens.go
@@ -6,22 +6,127 @@
 
 import (
 	"context"
+	"go/ast"
 	"go/token"
+	"go/types"
 	"path/filepath"
+	"regexp"
 	"strings"
 
 	"golang.org/x/tools/internal/lsp/protocol"
 )
 
+type lensFunc func(context.Context, Snapshot, FileHandle, *ast.File, *protocol.ColumnMapper) ([]protocol.CodeLens, error)
+
+var lensFuncs = map[string]lensFunc{
+	CommandGenerate:      goGenerateCodeLens,
+	CommandTest:          runTestCodeLens,
+	CommandRegenerateCgo: regenerateCgoLens,
+}
+
 // CodeLens computes code lens for Go source code.
 func CodeLens(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]protocol.CodeLens, error) {
-	if !snapshot.View().Options().EnabledCodeLens[CommandGenerate] {
-		return nil, nil
-	}
-	f, _, m, _, err := snapshot.View().Session().Cache().ParseGoHandle(fh, ParseFull).Parse(ctx)
+	pgh := snapshot.View().Session().Cache().ParseGoHandle(ctx, fh, ParseFull)
+	f, _, m, _, err := pgh.Parse(ctx)
 	if err != nil {
 		return nil, err
 	}
+
+	var result []protocol.CodeLens
+	for lens, lf := range lensFuncs {
+		if !snapshot.View().Options().EnabledCodeLens[lens] {
+			continue
+		}
+		added, err := lf(ctx, snapshot, fh, f, m)
+
+		if err != nil {
+			return nil, err
+		}
+		result = append(result, added...)
+	}
+	return result, nil
+}
+
+var testMatcher = regexp.MustCompile("^Test[^a-z]")
+var benchMatcher = regexp.MustCompile("^Benchmark[^a-z]")
+
+func runTestCodeLens(ctx context.Context, snapshot Snapshot, fh FileHandle, f *ast.File, m *protocol.ColumnMapper) ([]protocol.CodeLens, error) {
+	codeLens := make([]protocol.CodeLens, 0)
+
+	pkg, _, err := getParsedFile(ctx, snapshot, fh, WidestPackageHandle)
+	if err != nil {
+		return nil, err
+	}
+
+	if !strings.HasSuffix(fh.URI().Filename(), "_test.go") {
+		return nil, nil
+	}
+
+	for _, d := range f.Decls {
+		fn, ok := d.(*ast.FuncDecl)
+		if !ok {
+			continue
+		}
+
+		if isTestFunc(fn, pkg) {
+			fset := snapshot.View().Session().Cache().FileSet()
+			rng, err := newMappedRange(fset, m, d.Pos(), d.Pos()).Range()
+			if err != nil {
+				return nil, err
+			}
+
+			uri := fh.URI()
+			codeLens = append(codeLens, protocol.CodeLens{
+				Range: rng,
+				Command: protocol.Command{
+					Title:     "run test",
+					Command:   "test",
+					Arguments: []interface{}{fn.Name.Name, uri},
+				},
+			})
+		}
+	}
+
+	return codeLens, nil
+}
+
+func isTestFunc(fn *ast.FuncDecl, pkg Package) bool {
+	typesInfo := pkg.GetTypesInfo()
+	if typesInfo == nil {
+		return false
+	}
+
+	sig, ok := typesInfo.ObjectOf(fn.Name).Type().(*types.Signature)
+	if !ok {
+		return false
+	}
+
+	// test funcs should have a single parameter, so we can exit early if that's not the case.
+	if sig.Params().Len() != 1 {
+		return false
+	}
+
+	firstParam, ok := sig.Params().At(0).Type().(*types.Pointer)
+	if !ok {
+		return false
+	}
+
+	firstParamElem, ok := firstParam.Elem().(*types.Named)
+	if !ok {
+		return false
+	}
+
+	firstParamObj := firstParamElem.Obj()
+	if firstParamObj.Pkg().Path() != "testing" {
+		return false
+	}
+
+	firstParamName := firstParamObj.Id()
+	return (firstParamName == "T" && testMatcher.MatchString(fn.Name.Name)) ||
+		(firstParamName == "B" && benchMatcher.MatchString(fn.Name.Name))
+}
+
+func goGenerateCodeLens(ctx context.Context, snapshot Snapshot, fh FileHandle, f *ast.File, m *protocol.ColumnMapper) ([]protocol.CodeLens, error) {
 	const ggDirective = "//go:generate"
 	for _, c := range f.Comments {
 		for _, l := range c.List {
@@ -33,7 +138,7 @@
 			if err != nil {
 				return nil, err
 			}
-			dir := filepath.Dir(fh.Identity().URI.Filename())
+			dir := filepath.Dir(fh.URI().Filename())
 			return []protocol.CodeLens{
 				{
 					Range: rng,
@@ -57,3 +162,30 @@
 	}
 	return nil, nil
 }
+
+func regenerateCgoLens(ctx context.Context, snapshot Snapshot, fh FileHandle, f *ast.File, m *protocol.ColumnMapper) ([]protocol.CodeLens, error) {
+	var c *ast.ImportSpec
+	for _, imp := range f.Imports {
+		if imp.Path.Value == `"C"` {
+			c = imp
+		}
+	}
+	if c == nil {
+		return nil, nil
+	}
+	fset := snapshot.View().Session().Cache().FileSet()
+	rng, err := newMappedRange(fset, m, c.Pos(), c.EndPos).Range()
+	if err != nil {
+		return nil, err
+	}
+	return []protocol.CodeLens{
+		{
+			Range: rng,
+			Command: protocol.Command{
+				Title:     "regenerate cgo definitions",
+				Command:   CommandRegenerateCgo,
+				Arguments: []interface{}{fh.URI()},
+			},
+		},
+	}, nil
+}
diff --git a/internal/lsp/source/completion.go b/internal/lsp/source/completion.go
index fe3ba16..2c7a3af 100644
--- a/internal/lsp/source/completion.go
+++ b/internal/lsp/source/completion.go
@@ -78,6 +78,10 @@
 
 	// Documentation is the documentation for the completion item.
 	Documentation string
+
+	// obj is the object from which this candidate was derived, if any.
+	// obj is for internal use only.
+	obj types.Object
 }
 
 // Snippet is a convenience returns the snippet if available, otherwise
@@ -487,7 +491,7 @@
 		pkg:                       pkg,
 		snapshot:                  snapshot,
 		qf:                        qualifier(file, pkg.GetTypes(), pkg.GetTypesInfo()),
-		filename:                  fh.Identity().URI.Filename(),
+		filename:                  fh.URI().Filename(),
 		file:                      file,
 		path:                      path,
 		pos:                       pos,
@@ -501,7 +505,7 @@
 			documentation:     opts.CompletionDocumentation,
 			fullDocumentation: opts.HoverKind == FullDocumentation,
 			placeholders:      opts.Placeholders,
-			literal:           opts.InsertTextFormat == protocol.SnippetTextFormat,
+			literal:           opts.LiteralCompletions && opts.InsertTextFormat == protocol.SnippetTextFormat,
 			budget:            opts.CompletionBudget,
 		},
 		// default to a matcher that always matches
@@ -553,10 +557,6 @@
 		return c.items, c.getSurrounding(), nil
 	}
 
-	// Statement candidates offer an entire statement in certain
-	// contexts, as opposed to a single object.
-	c.addStatementCandidates()
-
 	if c.emptySwitchStmt() {
 		// Empty switch statements only admit "default" and "case" keywords.
 		c.addKeywordItems(map[string]bool{}, highScore, CASE, DEFAULT)
@@ -570,9 +570,20 @@
 			if err := c.selector(ctx, sel); err != nil {
 				return nil, nil, err
 			}
-			return c.items, c.getSurrounding(), nil
-		}
-		if err := c.lexical(ctx); err != nil {
+		} else if obj, ok := pkg.GetTypesInfo().Defs[n]; ok {
+			// reject defining identifiers
+
+			if v, ok := obj.(*types.Var); ok && v.IsField() && v.Embedded() {
+				// An anonymous field is also a reference to a type.
+			} else {
+				objStr := ""
+				if obj != nil {
+					qual := types.RelativeTo(pkg.GetTypes())
+					objStr = types.ObjectString(obj, qual)
+				}
+				return nil, nil, ErrIsDefinition{objStr: objStr}
+			}
+		} else if err := c.lexical(ctx); err != nil {
 			return nil, nil, err
 		}
 	// The function name hasn't been typed yet, but the parens are there:
@@ -599,6 +610,12 @@
 		}
 	}
 
+	// Statement candidates offer an entire statement in certain
+	// contexts, as opposed to a single object. Add statement candidates
+	// last because they depend on other candidates having already been
+	// collected.
+	c.addStatementCandidates()
+
 	return c.items, c.getSurrounding(), nil
 }
 
@@ -714,31 +731,20 @@
 		// handle const, vars, and types
 		case *ast.GenDecl:
 			for _, spec := range node.Specs {
-				switch spec.(type) {
+				switch spec := spec.(type) {
 				case *ast.ValueSpec:
-					valueSpec, ok := spec.(*ast.ValueSpec)
-					if !ok {
-						continue
-					}
-					for _, name := range valueSpec.Names {
+					for _, name := range spec.Names {
 						if name.String() == "_" || !name.IsExported() {
 							continue
 						}
-
 						obj := c.pkg.GetTypesInfo().ObjectOf(name)
 						c.found(ctx, candidate{obj: obj, score: stdScore})
 					}
 				case *ast.TypeSpec:
-					typeSpec, ok := spec.(*ast.TypeSpec)
-					if !ok {
+					if spec.Name.String() == "_" || !spec.Name.IsExported() {
 						continue
 					}
-
-					if typeSpec.Name.String() == "_" || !typeSpec.Name.IsExported() {
-						continue
-					}
-
-					obj := c.pkg.GetTypesInfo().ObjectOf(typeSpec.Name)
+					obj := c.pkg.GetTypesInfo().ObjectOf(spec.Name)
 					c.found(ctx, candidate{obj: obj, score: stdScore})
 				}
 			}
@@ -832,8 +838,11 @@
 			return nil
 		})
 	}
+	sort.Slice(paths, func(i, j int) bool {
+		return relevances[paths[i]] > relevances[paths[j]]
+	})
 
-	for path, relevance := range relevances {
+	for _, path := range paths {
 		pkg := known[path]
 		if pkg.GetTypes().Name() != id.Name {
 			continue
@@ -845,7 +854,7 @@
 		if imports.ImportPathToAssumedName(path) != pkg.GetTypes().Name() {
 			imp.name = pkg.GetTypes().Name()
 		}
-		c.packageMembers(ctx, pkg.GetTypes(), stdScore+.1*float64(relevance), imp)
+		c.packageMembers(ctx, pkg.GetTypes(), unimportedScore(relevances[path]), imp)
 		if len(c.items) >= unimportedMemberTarget {
 			return nil
 		}
@@ -865,7 +874,7 @@
 		// Continue with untyped proposals.
 		pkg := types.NewPackage(pkgExport.Fix.StmtInfo.ImportPath, pkgExport.Fix.IdentName)
 		for _, export := range pkgExport.Exports {
-			score := stdScore + 0.1*float64(pkgExport.Fix.Relevance)
+			score := unimportedScore(pkgExport.Fix.Relevance)
 			c.found(ctx, candidate{
 				obj:   types.NewVar(0, pkg, export, nil),
 				score: score,
@@ -884,6 +893,12 @@
 	})
 }
 
+// unimportedScore returns a score for an unimported package that is generally
+// lower than other candidates.
+func unimportedScore(relevance int) float64 {
+	return (stdScore + .1*float64(relevance)) / 2
+}
+
 func (c *completer) packageMembers(ctx context.Context, pkg *types.Package, score float64, imp *importInfo) {
 	scope := pkg.Scope()
 	for _, name := range scope.Names() {
@@ -1090,15 +1105,15 @@
 	if c.surrounding != nil {
 		prefix = c.surrounding.Prefix()
 	}
-	initialItemCount := len(c.items)
+	count := 0
 
 	known, err := c.snapshot.CachedImportPaths(ctx)
 	if err != nil {
 		return err
 	}
 	var paths []string
-	for path := range known {
-		if !strings.HasPrefix(path, prefix) {
+	for path, pkg := range known {
+		if !strings.HasPrefix(pkg.GetTypes().Name(), prefix) {
 			continue
 		}
 		paths = append(paths, path)
@@ -1111,9 +1126,15 @@
 			return nil
 		})
 	}
+	sort.Slice(paths, func(i, j int) bool {
+		return relevances[paths[i]] > relevances[paths[j]]
+	})
 
-	for path, relevance := range relevances {
+	for _, path := range paths {
 		pkg := known[path]
+		if _, ok := seen[pkg.GetTypes().Name()]; ok {
+			continue
+		}
 		imp := &importInfo{
 			importPath: path,
 			pkg:        pkg,
@@ -1121,15 +1142,15 @@
 		if imports.ImportPathToAssumedName(path) != pkg.GetTypes().Name() {
 			imp.name = pkg.GetTypes().Name()
 		}
-		score := 0.01 * float64(relevance)
-		c.found(ctx, candidate{
-			obj:   types.NewPkgName(0, nil, pkg.GetTypes().Name(), pkg.GetTypes()),
-			score: score,
-			imp:   imp,
-		})
-		if len(c.items)-initialItemCount >= maxUnimportedPackageNames {
+		if count >= maxUnimportedPackageNames {
 			return nil
 		}
+		c.found(ctx, candidate{
+			obj:   types.NewPkgName(0, nil, pkg.GetTypes().Name(), pkg.GetTypes()),
+			score: unimportedScore(relevances[path]),
+			imp:   imp,
+		})
+		count++
 	}
 
 	ctx, cancel := context.WithCancel(ctx)
@@ -1142,13 +1163,14 @@
 		if _, ok := seen[pkg.IdentName]; ok {
 			return
 		}
+		if _, ok := relevances[pkg.StmtInfo.ImportPath]; ok {
+			return
+		}
 
-		if len(c.items)-initialItemCount >= maxUnimportedPackageNames {
+		if count >= maxUnimportedPackageNames {
 			cancel()
 			return
 		}
-		// Rank unimported packages significantly lower than other results.
-		score := 0.01 * float64(pkg.Relevance)
 
 		// Do not add the unimported packages to seen, since we can have
 		// multiple packages of the same name as completion suggestions, since
@@ -1156,12 +1178,13 @@
 		obj := types.NewPkgName(0, nil, pkg.IdentName, types.NewPackage(pkg.StmtInfo.ImportPath, pkg.IdentName))
 		c.found(ctx, candidate{
 			obj:   obj,
-			score: score,
+			score: unimportedScore(pkg.Relevance),
 			imp: &importInfo{
 				importPath: pkg.StmtInfo.ImportPath,
 				name:       pkg.StmtInfo.Name,
 			},
 		})
+		count++
 	}
 	return c.snapshot.View().RunProcessEnvFunc(ctx, func(opts *imports.Options) error {
 		return imports.GetAllCandidates(ctx, add, prefix, c.filename, c.pkg.GetTypes().Name(), opts)
diff --git a/internal/lsp/source/completion_builtin.go b/internal/lsp/source/completion_builtin.go
index f314146..e67efb0 100644
--- a/internal/lsp/source/completion_builtin.go
+++ b/internal/lsp/source/completion_builtin.go
@@ -14,13 +14,13 @@
 // argument. It attempts to use the AST hints from builtin.go where
 // possible.
 func (c *completer) builtinArgKind(ctx context.Context, obj types.Object, call *ast.CallExpr) objKind {
-	astObj, err := c.snapshot.View().LookupBuiltin(ctx, obj.Name())
+	builtin, err := c.snapshot.View().BuiltinPackage(ctx)
 	if err != nil {
 		return 0
 	}
 	exprIdx := exprAtPos(c.pos, call.Args)
 
-	decl, ok := astObj.Decl.(*ast.FuncDecl)
+	decl, ok := builtin.Package().Scope.Lookup(obj.Name()).Decl.(*ast.FuncDecl)
 	if !ok || exprIdx >= len(decl.Type.Params.List) {
 		return 0
 	}
diff --git a/internal/lsp/source/completion_format.go b/internal/lsp/source/completion_format.go
index 2535432..ffa85be 100644
--- a/internal/lsp/source/completion_format.go
+++ b/internal/lsp/source/completion_format.go
@@ -166,6 +166,7 @@
 		Score:               cand.score,
 		Depth:               len(c.deepState.chain),
 		snippet:             snip,
+		obj:                 obj,
 	}
 	// If the user doesn't want documentation for completion items.
 	if !c.opts.documentation {
@@ -194,7 +195,7 @@
 	if err != nil {
 		return item, nil
 	}
-	hover, err := ident.Hover(ctx)
+	hover, err := HoverIdentifier(ctx, ident)
 	if err != nil {
 		event.Error(ctx, "failed to find Hover", err, tag.URI.Of(uri))
 		return item, nil
@@ -215,7 +216,7 @@
 	uri := span.URIFromPath(c.filename)
 	var ph ParseGoHandle
 	for _, h := range c.pkg.CompiledGoFiles() {
-		if h.File().Identity().URI == uri {
+		if h.File().URI() == uri {
 			ph = h
 		}
 	}
diff --git a/internal/lsp/source/completion_statements.go b/internal/lsp/source/completion_statements.go
index 7806168..8f72c37 100644
--- a/internal/lsp/source/completion_statements.go
+++ b/internal/lsp/source/completion_statements.go
@@ -18,6 +18,147 @@
 // appropriate for the current context.
 func (c *completer) addStatementCandidates() {
 	c.addErrCheckAndReturn()
+	c.addAssignAppend()
+}
+
+// addAssignAppend offers a completion candidate of the form:
+//
+//     someSlice = append(someSlice, )
+//
+// It will offer the "append" completion in two situations:
+//
+// 1. Position is in RHS of assign, prefix matches "append", and
+//    corresponding LHS object is a slice. For example,
+//    "foo = ap<>" completes to "foo = append(foo, )".
+//
+// Or
+//
+// 2. Prefix is an ident or selector in an *ast.ExprStmt (i.e.
+//    beginning of statement), and our best matching candidate is a
+//    slice. For example: "foo.ba" completes to "foo.bar = append(foo.bar, )".
+func (c *completer) addAssignAppend() {
+	if len(c.path) < 3 {
+		return
+	}
+
+	ident, _ := c.path[0].(*ast.Ident)
+	if ident == nil {
+		return
+	}
+
+	var (
+		// sliceText is the full name of our slice object, e.g. "s.abc" in
+		// "s.abc = app<>".
+		sliceText string
+		// needsLHS is true if we need to prepend the LHS slice name and
+		// "=" to our candidate.
+		needsLHS = false
+		fset     = c.snapshot.View().Session().Cache().FileSet()
+	)
+
+	switch n := c.path[1].(type) {
+	case *ast.AssignStmt:
+		// We are already in an assignment. Make sure our prefix matches "append".
+		if c.matcher.Score("append") <= 0 {
+			return
+		}
+
+		exprIdx := exprAtPos(c.pos, n.Rhs)
+		if exprIdx == len(n.Rhs) || exprIdx > len(n.Lhs)-1 {
+			return
+		}
+
+		lhsType := c.pkg.GetTypesInfo().TypeOf(n.Lhs[exprIdx])
+		if lhsType == nil {
+			return
+		}
+
+		// Make sure our corresponding LHS object is a slice.
+		if _, isSlice := lhsType.Underlying().(*types.Slice); !isSlice {
+			return
+		}
+
+		// The name or our slice is whatever's in the LHS expression.
+		sliceText = formatNode(fset, n.Lhs[exprIdx])
+	case *ast.SelectorExpr:
+		// Make sure we are a selector at the beginning of a statement.
+		if _, parentIsExprtStmt := c.path[2].(*ast.ExprStmt); !parentIsExprtStmt {
+			return
+		}
+
+		// So far we only know the first part of our slice name. For
+		// example in "s.a<>" we only know our slice begins with "s."
+		// since the user could still be typing.
+		sliceText = formatNode(fset, n.X) + "."
+		needsLHS = true
+	case *ast.ExprStmt:
+		needsLHS = true
+	default:
+		return
+	}
+
+	var (
+		label string
+		snip  snippet.Builder
+		score = highScore
+	)
+
+	if needsLHS {
+		// Offer the long form assign + append candidate if our best
+		// candidate is a slice.
+		bestItem := c.topCandidate()
+		if bestItem == nil || bestItem.obj == nil || bestItem.obj.Type() == nil {
+			return
+		}
+
+		if _, isSlice := bestItem.obj.Type().Underlying().(*types.Slice); !isSlice {
+			return
+		}
+
+		// Don't rank the full form assign + append candidate above the
+		// slice itself.
+		score = bestItem.Score - 0.01
+
+		// Fill in rest of sliceText now that we have the object name.
+		sliceText += bestItem.Label
+
+		// Fill in the candidate's LHS bits.
+		label = fmt.Sprintf("%s = ", bestItem.Label)
+		snip.WriteText(label)
+	}
+
+	snip.WriteText(fmt.Sprintf("append(%s, ", sliceText))
+	snip.WritePlaceholder(nil)
+	snip.WriteText(")")
+
+	c.items = append(c.items, CompletionItem{
+		Label:   label + fmt.Sprintf("append(%s, )", sliceText),
+		Kind:    protocol.FunctionCompletion,
+		Score:   score,
+		snippet: &snip,
+	})
+}
+
+// topCandidate returns the strictly highest scoring candidate
+// collected so far. If the top two candidates have the same score,
+// nil is returned.
+func (c *completer) topCandidate() *CompletionItem {
+	var bestItem, secondBestItem *CompletionItem
+	for i := range c.items {
+		if bestItem == nil || c.items[i].Score > bestItem.Score {
+			bestItem = &c.items[i]
+		} else if secondBestItem == nil || c.items[i].Score > secondBestItem.Score {
+			secondBestItem = &c.items[i]
+		}
+	}
+
+	// If secondBestItem has the same score, bestItem isn't
+	// the strict best.
+	if secondBestItem != nil && secondBestItem.Score == bestItem.Score {
+		return nil
+	}
+
+	return bestItem
 }
 
 // addErrCheckAndReturn offers a completion candidate of the form:
diff --git a/internal/lsp/source/diagnostics.go b/internal/lsp/source/diagnostics.go
index d1ba85c..b244959 100644
--- a/internal/lsp/source/diagnostics.go
+++ b/internal/lsp/source/diagnostics.go
@@ -42,6 +42,14 @@
 }
 
 func Diagnostics(ctx context.Context, snapshot Snapshot, ph PackageHandle, missingModules map[string]*modfile.Require, withAnalysis bool) (map[FileIdentity][]*Diagnostic, bool, error) {
+	onlyIgnoredFiles := true
+	for _, pgh := range ph.CompiledGoFiles() {
+		onlyIgnoredFiles = onlyIgnoredFiles && snapshot.View().IgnoredFile(pgh.File().URI())
+	}
+	if onlyIgnoredFiles {
+		return nil, false, nil
+	}
+
 	// If we are missing dependencies, it may because the user's workspace is
 	// not correctly configured. Report errors, if possible.
 	var warn bool
@@ -83,10 +91,10 @@
 
 	// Prepare the reports we will send for the files in this package.
 	reports := make(map[FileIdentity][]*Diagnostic)
-	for _, fh := range pkg.CompiledGoFiles() {
-		clearReports(snapshot, reports, fh.File().Identity().URI)
+	for _, pgh := range pkg.CompiledGoFiles() {
+		clearReports(ctx, snapshot, reports, pgh.File().URI())
 		if len(missing) > 0 {
-			if err := missingModulesDiagnostics(ctx, snapshot, reports, missing, fh.File().Identity().URI); err != nil {
+			if err := missingModulesDiagnostics(ctx, snapshot, reports, missing, pgh.File().URI()); err != nil {
 				return nil, warn, err
 			}
 		}
@@ -99,13 +107,13 @@
 		}
 		// If no file is associated with the error, pick an open file from the package.
 		if e.URI.Filename() == "" {
-			for _, ph := range pkg.CompiledGoFiles() {
-				if snapshot.IsOpen(ph.File().Identity().URI) {
-					e.URI = ph.File().Identity().URI
+			for _, pgh := range pkg.CompiledGoFiles() {
+				if snapshot.IsOpen(pgh.File().URI()) {
+					e.URI = pgh.File().URI()
 				}
 			}
 		}
-		clearReports(snapshot, reports, e.URI)
+		clearReports(ctx, snapshot, reports, e.URI)
 	}
 	// Run diagnostics for the package that this URI belongs to.
 	hadDiagnostics, hadTypeErrors, err := diagnostics(ctx, snapshot, reports, pkg, len(ph.MissingDependencies()) > 0)
@@ -121,10 +129,7 @@
 		return nil, warn, ctx.Err()
 	}
 	// If we don't have any list or parse errors, run analyses.
-	analyzers := snapshot.View().Options().DefaultAnalyzers
-	if hadTypeErrors {
-		analyzers = snapshot.View().Options().TypeErrorAnalyzers
-	}
+	analyzers := pickAnalyzers(snapshot, hadTypeErrors)
 	if err := analyses(ctx, snapshot, reports, ph, analyzers); err != nil {
 		event.Error(ctx, "analyses failed", err, tag.Snapshot.Of(snapshot.ID()), tag.Package.Of(ph.ID()))
 		if ctx.Err() != nil {
@@ -134,8 +139,28 @@
 	return reports, warn, nil
 }
 
+func pickAnalyzers(snapshot Snapshot, hadTypeErrors bool) map[string]Analyzer {
+	analyzers := make(map[string]Analyzer)
+
+	// Always run convenience analyzers.
+	for k, v := range snapshot.View().Options().ConvenienceAnalyzers {
+		analyzers[k] = v
+	}
+	// If we had type errors, only run type error analyzers.
+	if hadTypeErrors {
+		for k, v := range snapshot.View().Options().TypeErrorAnalyzers {
+			analyzers[k] = v
+		}
+		return analyzers
+	}
+	for k, v := range snapshot.View().Options().DefaultAnalyzers {
+		analyzers[k] = v
+	}
+	return analyzers
+}
+
 func FileDiagnostics(ctx context.Context, snapshot Snapshot, uri span.URI) (FileIdentity, []*Diagnostic, error) {
-	fh, err := snapshot.GetFile(uri)
+	fh, err := snapshot.GetFile(ctx, uri)
 	if err != nil {
 		return FileIdentity{}, nil, err
 	}
@@ -205,7 +230,7 @@
 		} else if len(set.typeErrors) > 0 {
 			hasTypeErrors = true
 		}
-		if err := addReports(snapshot, reports, uri, diags...); err != nil {
+		if err := addReports(ctx, snapshot, reports, uri, diags...); err != nil {
 			return false, false, err
 		}
 	}
@@ -213,14 +238,15 @@
 }
 
 func missingModulesDiagnostics(ctx context.Context, snapshot Snapshot, reports map[FileIdentity][]*Diagnostic, missingModules map[string]*modfile.Require, uri span.URI) error {
-	if snapshot.View().Ignore(uri) || len(missingModules) == 0 {
+	if len(missingModules) == 0 {
 		return nil
 	}
-	fh, err := snapshot.GetFile(uri)
+	fh, err := snapshot.GetFile(ctx, uri)
 	if err != nil {
 		return err
 	}
-	file, _, m, _, err := snapshot.View().Session().Cache().ParseGoHandle(fh, ParseHeader).Parse(ctx)
+	pgh := snapshot.View().Session().Cache().ParseGoHandle(ctx, fh, ParseHeader)
+	file, _, m, _, err := pgh.Parse(ctx)
 	if err != nil {
 		return err
 	}
@@ -282,6 +308,13 @@
 
 	// Report diagnostics and errors from root analyzers.
 	for _, e := range analysisErrors {
+		// If the diagnostic comes from a "convenience" analyzer, it is not
+		// meant to provide diagnostics, but rather only suggested fixes.
+		// Skip these types of errors in diagnostics; we will use their
+		// suggested fixes when providing code actions.
+		if isConvenienceAnalyzer(e.Category) {
+			continue
+		}
 		// This is a bit of a hack, but clients > 3.15 will be able to grey out unnecessary code.
 		// If we are deleting code as part of all of our suggested fixes, assume that this is dead code.
 		// TODO(golang/go#34508): Return these codes from the diagnostics themselves.
@@ -289,7 +322,7 @@
 		if onlyDeletions(e.SuggestedFixes) {
 			tags = append(tags, protocol.Unnecessary)
 		}
-		if err := addReports(snapshot, reports, e.URI, &Diagnostic{
+		if err := addReports(ctx, snapshot, reports, e.URI, &Diagnostic{
 			Range:    e.Range,
 			Message:  e.Message,
 			Source:   e.Category,
@@ -303,10 +336,7 @@
 	return nil
 }
 
-func clearReports(snapshot Snapshot, reports map[FileIdentity][]*Diagnostic, uri span.URI) {
-	if snapshot.View().Ignore(uri) {
-		return
-	}
+func clearReports(ctx context.Context, snapshot Snapshot, reports map[FileIdentity][]*Diagnostic, uri span.URI) {
 	fh := snapshot.FindFile(uri)
 	if fh == nil {
 		return
@@ -314,16 +344,12 @@
 	reports[fh.Identity()] = []*Diagnostic{}
 }
 
-func addReports(snapshot Snapshot, reports map[FileIdentity][]*Diagnostic, uri span.URI, diagnostics ...*Diagnostic) error {
-	if snapshot.View().Ignore(uri) {
-		return nil
-	}
+func addReports(ctx context.Context, snapshot Snapshot, reports map[FileIdentity][]*Diagnostic, uri span.URI, diagnostics ...*Diagnostic) error {
 	fh := snapshot.FindFile(uri)
 	if fh == nil {
 		return nil
 	}
-	identity := fh.Identity()
-	existingDiagnostics, ok := reports[identity]
+	existingDiagnostics, ok := reports[fh.Identity()]
 	if !ok {
 		return fmt.Errorf("diagnostics for unexpected file %s", uri)
 	}
@@ -337,7 +363,7 @@
 				if d1.Message != d2.Message {
 					continue
 				}
-				reports[identity][i].Tags = append(reports[identity][i].Tags, d1.Tags...)
+				reports[fh.Identity()][i].Tags = append(reports[fh.Identity()][i].Tags, d1.Tags...)
 			}
 			return nil
 		}
@@ -376,3 +402,12 @@
 	}
 	return false
 }
+
+func isConvenienceAnalyzer(category string) bool {
+	for _, a := range DefaultOptions().ConvenienceAnalyzers {
+		if category == a.Analyzer.Name {
+			return true
+		}
+	}
+	return false
+}
diff --git a/internal/lsp/source/folding_range.go b/internal/lsp/source/folding_range.go
index 21cfda1..74ad3e6 100644
--- a/internal/lsp/source/folding_range.go
+++ b/internal/lsp/source/folding_range.go
@@ -18,7 +18,7 @@
 func FoldingRange(ctx context.Context, snapshot Snapshot, fh FileHandle, lineFoldingOnly bool) (ranges []*FoldingRangeInfo, err error) {
 	// TODO(suzmue): consider limiting the number of folding ranges returned, and
 	// implement a way to prioritize folding ranges in that case.
-	pgh := snapshot.View().Session().Cache().ParseGoHandle(fh, ParseFull)
+	pgh := snapshot.View().Session().Cache().ParseGoHandle(ctx, fh, ParseFull)
 	file, _, m, _, err := pgh.Parse(ctx)
 	if err != nil {
 		return nil, err
@@ -76,6 +76,9 @@
 			kind = protocol.Imports
 		}
 		start, end = n.Lparen+1, n.Rparen
+	case *ast.CompositeLit:
+		// Fold from position of "{" to position of "}".
+		start, end = n.Lbrace+1, n.Rbrace
 	}
 	if !start.IsValid() || !end.IsValid() {
 		return nil
@@ -155,6 +158,15 @@
 			break
 		}
 		start, end = n.Lparen+1, n.Specs[nSpecs-1].End()
+	case *ast.CompositeLit:
+		// Fold lines between "{" and "}".
+		if !n.Lbrace.IsValid() || !n.Rbrace.IsValid() {
+			break
+		}
+		if len(n.Elts) == 0 {
+			break
+		}
+		start, end = n.Lbrace+1, n.Elts[len(n.Elts)-1].End()
 	}
 
 	// Check that folding positions are valid.
diff --git a/internal/lsp/source/format.go b/internal/lsp/source/format.go
index 17ed0a6..183fd8e 100644
--- a/internal/lsp/source/format.go
+++ b/internal/lsp/source/format.go
@@ -11,14 +11,13 @@
 	"go/ast"
 	"go/format"
 	"go/parser"
-	"go/scanner"
 	"go/token"
+	"strings"
 
 	"golang.org/x/tools/internal/event"
 	"golang.org/x/tools/internal/imports"
 	"golang.org/x/tools/internal/lsp/diff"
 	"golang.org/x/tools/internal/lsp/protocol"
-	"golang.org/x/tools/internal/span"
 	errors "golang.org/x/xerrors"
 )
 
@@ -27,7 +26,7 @@
 	ctx, done := event.Start(ctx, "source.Format")
 	defer done()
 
-	pgh := snapshot.View().Session().Cache().ParseGoHandle(fh, ParseFull)
+	pgh := snapshot.View().Session().Cache().ParseGoHandle(ctx, fh, ParseFull)
 	file, _, m, parseErrors, err := pgh.Parse(ctx)
 	if err != nil {
 		return nil, err
@@ -57,10 +56,10 @@
 }
 
 func formatSource(ctx context.Context, fh FileHandle) ([]byte, error) {
-	ctx, done := event.Start(ctx, "source.formatSource")
+	_, done := event.Start(ctx, "source.formatSource")
 	defer done()
 
-	data, _, err := fh.Read(ctx)
+	data, err := fh.Read()
 	if err != nil {
 		return nil, err
 	}
@@ -80,7 +79,7 @@
 	ctx, done := event.Start(ctx, "source.AllImportsFixes")
 	defer done()
 
-	pgh := snapshot.View().Session().Cache().ParseGoHandle(fh, ParseFull)
+	pgh := snapshot.View().Session().Cache().ParseGoHandle(ctx, fh, ParseFull)
 	if err := snapshot.View().RunProcessEnvFunc(ctx, func(opts *imports.Options) error {
 		allFixEdits, editsPerFix, err = computeImportEdits(ctx, snapshot.View(), pgh, opts)
 		return err
@@ -93,14 +92,14 @@
 // computeImportEdits computes a set of edits that perform one or all of the
 // necessary import fixes.
 func computeImportEdits(ctx context.Context, view View, ph ParseGoHandle, options *imports.Options) (allFixEdits []protocol.TextEdit, editsPerFix []*ImportFix, err error) {
-	filename := ph.File().Identity().URI.Filename()
+	filename := ph.File().URI().Filename()
 
 	// Build up basic information about the original file.
-	origData, _, err := ph.File().Read(ctx)
+	origData, err := ph.File().Read()
 	if err != nil {
 		return nil, nil, err
 	}
-	origAST, _, origMapper, _, err := ph.Parse(ctx)
+	_, _, origMapper, _, err := ph.Parse(ctx)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -110,8 +109,7 @@
 		return nil, nil, err
 	}
 
-	origImports, origImportOffset := trimToImports(view.Session().Cache().FileSet(), origAST, origData)
-	allFixEdits, err = computeFixEdits(view, ph, options, origData, origAST, origMapper, origImports, origImportOffset, allFixes)
+	allFixEdits, err = computeFixEdits(view, ph, options, origData, origMapper, allFixes)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -119,7 +117,7 @@
 	// Apply all of the import fixes to the file.
 	// Add the edits for each fix to the result.
 	for _, fix := range allFixes {
-		edits, err := computeFixEdits(view, ph, options, origData, origAST, origMapper, origImports, origImportOffset, []*imports.ImportFix{fix})
+		edits, err := computeFixEdits(view, ph, options, origData, origMapper, []*imports.ImportFix{fix})
 		if err != nil {
 			return nil, nil, err
 		}
@@ -132,15 +130,14 @@
 }
 
 func computeOneImportFixEdits(ctx context.Context, view View, ph ParseGoHandle, fix *imports.ImportFix) ([]protocol.TextEdit, error) {
-	origData, _, err := ph.File().Read(ctx)
+	origData, err := ph.File().Read()
 	if err != nil {
 		return nil, err
 	}
-	origAST, _, origMapper, _, err := ph.Parse(ctx)
+	_, _, origMapper, _, err := ph.Parse(ctx) // ph.Parse returns values never used
 	if err != nil {
 		return nil, err
 	}
-	origImports, origImportOffset := trimToImports(view.Session().Cache().FileSet(), origAST, origData)
 
 	options := &imports.Options{
 		// Defaults.
@@ -151,165 +148,78 @@
 		TabIndent:  true,
 		TabWidth:   8,
 	}
-	return computeFixEdits(view, ph, options, origData, origAST, origMapper, origImports, origImportOffset, []*imports.ImportFix{fix})
+	return computeFixEdits(view, ph, options, origData, origMapper, []*imports.ImportFix{fix})
 }
 
-func computeFixEdits(view View, ph ParseGoHandle, options *imports.Options, origData []byte, origAST *ast.File, origMapper *protocol.ColumnMapper, origImports []byte, origImportOffset int, fixes []*imports.ImportFix) ([]protocol.TextEdit, error) {
-	filename := ph.File().Identity().URI.Filename()
+func computeFixEdits(view View, ph ParseGoHandle, options *imports.Options, origData []byte, origMapper *protocol.ColumnMapper, fixes []*imports.ImportFix) ([]protocol.TextEdit, error) {
+	// trim the original data to match fixedData
+	left := importPrefix(origData)
+	extra := !strings.Contains(left, "\n") // one line may have more than imports
+	if extra {
+		left = string(origData)
+	}
+	if len(left) > 0 && left[len(left)-1] != '\n' {
+		left += "\n"
+	}
 	// Apply the fixes and re-parse the file so that we can locate the
 	// new imports.
-	fixedData, err := imports.ApplyFixes(fixes, filename, origData, options, parser.ImportsOnly)
-	fixedData = append(fixedData, '\n') // ApplyFixes comes out missing the newline, go figure.
+	flags := parser.ImportsOnly
+	if extra {
+		// used all of origData above, use all of it here too
+		flags = 0
+	}
+	fixedData, err := imports.ApplyFixes(fixes, "", origData, options, flags)
 	if err != nil {
 		return nil, err
 	}
-	fixedFset := token.NewFileSet()
-	fixedAST, err := parser.ParseFile(fixedFset, filename, fixedData, parser.ImportsOnly)
-	// Any error here prevents us from computing the edits.
-	if err != nil {
-		return nil, err
+	if fixedData == nil || fixedData[len(fixedData)-1] != '\n' {
+		fixedData = append(fixedData, '\n') // ApplyFixes may miss the newline, go figure.
 	}
-	fixedImports, fixedImportsOffset := trimToImports(fixedFset, fixedAST, fixedData)
-
-	// Prepare the diff. If both sides had import statements, we can diff
-	// just those sections against each other, then shift the resulting
-	// edits to the right lines in the original file.
-	left, right := origImports, fixedImports
-
-	// If there is no diff, return early, as there's no need to compute edits.
-	// imports.ApplyFixes also formats the file, and this way we avoid
-	// unnecessary formatting, which may cause further issues if we can't
-	// find an import block on which to anchor the diffs.
-	if len(left) == 0 && len(right) == 0 {
-		return nil, nil
-	}
-
-	converter := span.NewContentConverter(filename, origImports)
-	offset := origImportOffset
-
-	// If one side or the other has no imports, we won't know where to
-	// anchor the diffs. Instead, use the beginning of the file, up to its
-	// first non-imports decl. We know the imports code will insert
-	// somewhere before that.
-	if origImportOffset == 0 || fixedImportsOffset == 0 {
-		left, _ = trimToFirstNonImport(view.Session().Cache().FileSet(), origAST, origData, nil)
-		fixedData, err = imports.ApplyFixes(fixes, filename, origData, options, 0)
-		if err != nil {
-			return nil, err
-		}
-		// We need the whole file here, not just the ImportsOnly versions we made above.
-		fixedAST, err = parser.ParseFile(fixedFset, filename, fixedData, 0)
-		if fixedAST == nil {
-			return nil, err
-		}
-		var ok bool
-		right, ok = trimToFirstNonImport(fixedFset, fixedAST, fixedData, err)
-		if !ok {
-			return nil, errors.Errorf("error %v detected in the import block", err)
-		}
-		// We're now working with a prefix of the original file, so we can
-		// use the original converter, and there is no offset on the edits.
-		converter = origMapper.Converter
-		offset = 0
-	}
-
-	// Perform the diff and adjust the results for the trimming, if any.
-	edits := view.Options().ComputeEdits(ph.File().Identity().URI, string(left), string(right))
-	for i := range edits {
-		s, err := edits[i].Span.WithPosition(converter)
-		if err != nil {
-			return nil, err
-		}
-		start := span.NewPoint(s.Start().Line()+offset, s.Start().Column(), -1)
-		end := span.NewPoint(s.End().Line()+offset, s.End().Column(), -1)
-		edits[i].Span = span.New(s.URI(), start, end)
-	}
+	uri := ph.File().URI()
+	edits := view.Options().ComputeEdits(uri, left, string(fixedData))
 	return ToProtocolEdits(origMapper, edits)
 }
 
-// trimToImports returns a section of the source file that covers all of the
-// import declarations, and the line offset into the file that section starts at.
-func trimToImports(fset *token.FileSet, f *ast.File, src []byte) ([]byte, int) {
-	var firstImport, lastImport ast.Decl
-	for _, decl := range f.Decls {
-		if gen, ok := decl.(*ast.GenDecl); ok && gen.Tok == token.IMPORT {
-			if firstImport == nil {
-				firstImport = decl
-			}
-			lastImport = decl
-		}
+// return the prefix of the src through the last imports, or if there are
+// no imports, through the package statement (and a subsequent comment group)
+func importPrefix(src []byte) string {
+	fset := token.NewFileSet()
+	// do as little parsing as possible
+	f, err := parser.ParseFile(fset, "", src, parser.ImportsOnly|parser.ParseComments)
+	if err != nil { // This can happen if 'package' is misspelled
+		return ""
 	}
-
-	if firstImport == nil {
-		return nil, 0
-	}
-	tok := fset.File(f.Pos())
-	start := firstImport.Pos()
-	end := lastImport.End()
-	// The parser will happily feed us nonsense. See golang/go#36610.
-	tokStart, tokEnd := token.Pos(tok.Base()), token.Pos(tok.Base()+tok.Size())
-	if start < tokStart || start > tokEnd || end < tokStart || end > tokEnd {
-		return nil, 0
-	}
-	if nextLine := fset.Position(end).Line + 1; tok.LineCount() >= nextLine {
-		end = fset.File(f.Pos()).LineStart(nextLine)
-	}
-	if start > end {
-		return nil, 0
-	}
-
-	startLineOffset := fset.Position(start).Line - 1 // lines are 1-indexed.
-	return src[fset.Position(start).Offset:fset.Position(end).Offset], startLineOffset
-}
-
-// trimToFirstNonImport returns src from the beginning to the first non-import
-// declaration, or the end of the file if there is no such decl.
-func trimToFirstNonImport(fset *token.FileSet, f *ast.File, src []byte, err error) ([]byte, bool) {
-	var firstDecl ast.Decl
-	for _, decl := range f.Decls {
-		if gen, ok := decl.(*ast.GenDecl); ok && gen.Tok == token.IMPORT {
-			continue
-		}
-		firstDecl = decl
-		break
-	}
-	tok := fset.File(f.Pos())
-	if tok == nil {
-		return nil, false
-	}
-	end := f.End()
-	if firstDecl != nil {
-		if firstDeclLine := fset.Position(firstDecl.Pos()).Line; firstDeclLine > 1 {
-			end = tok.LineStart(firstDeclLine - 1)
-		}
-	}
-	// Any errors in the file must be after the part of the file that we care about.
-	switch err := err.(type) {
-	case *scanner.Error:
-		pos := tok.Pos(err.Pos.Offset)
-		if pos <= end {
-			return nil, false
-		}
-	case scanner.ErrorList:
-		if err.Len() > 0 {
-			pos := tok.Pos(err[0].Pos.Offset)
-			if pos <= end {
-				return nil, false
+	myStart := fset.File(f.Pos()).Base() // 1, but the generality costs little
+	pkgEnd := int(f.Name.NamePos) + len(f.Name.Name)
+	var importEnd int
+	for _, d := range f.Decls {
+		if x, ok := d.(*ast.GenDecl); ok && x.Tok == token.IMPORT {
+			e := int(d.End()) - myStart
+			if e > importEnd {
+				importEnd = e
 			}
 		}
 	}
-	return src[0:fset.Position(end).Offset], true
+	if importEnd == 0 {
+		importEnd = pkgEnd
+	}
+	for _, c := range f.Comments {
+		if int(c.End()) > importEnd {
+			importEnd = int(c.End())
+		}
+	}
+	return string(src[:importEnd])
 }
 
 func computeTextEdits(ctx context.Context, view View, fh FileHandle, m *protocol.ColumnMapper, formatted string) ([]protocol.TextEdit, error) {
-	ctx, done := event.Start(ctx, "source.computeTextEdits")
+	_, done := event.Start(ctx, "source.computeTextEdits")
 	defer done()
 
-	data, _, err := fh.Read(ctx)
+	data, err := fh.Read()
 	if err != nil {
 		return nil, err
 	}
-	edits := view.Options().ComputeEdits(fh.Identity().URI, string(data), formatted)
+	edits := view.Options().ComputeEdits(fh.URI(), string(data), formatted)
 	return ToProtocolEdits(m, edits)
 }
 
diff --git a/internal/lsp/source/format_test.go b/internal/lsp/source/format_test.go
index 678a672..845c87c 100644
--- a/internal/lsp/source/format_test.go
+++ b/internal/lsp/source/format_test.go
@@ -1,25 +1,30 @@
 package source
 
 import (
-	"go/parser"
-	"go/token"
 	"testing"
 )
 
-func TestTrimToImports(t *testing.T) {
-	const input = `package source
-
-import (
-	m
-	"fmt"
-)
-
-func foo() {
-	fmt.Println("hi")
+type data struct {
+	input, want string
 }
-`
 
-	fs := token.NewFileSet()
-	f, _ := parser.ParseFile(fs, "foo.go", input, parser.ImportsOnly)
-	trimToImports(fs, f, []byte(input))
+func TestImportPrefix(t *testing.T) {
+	var tdata = []data{
+		{"package foo\n", "package foo\n"},
+		{"package foo\n\nfunc f(){}\n", "package foo\n"},
+		{"package foo\n\nimport \"fmt\"\n", "package foo\n\nimport \"fmt\""},
+		{"package foo\nimport (\n\"fmt\"\n)\n", "package foo\nimport (\n\"fmt\"\n)"},
+		{"\n\n\npackage foo\n", "\n\n\npackage foo\n"},
+		{"// hi \n\npackage foo //xx\nfunc _(){}\n", "// hi \n\npackage foo //xx\n"},
+		{"package foo //hi\n", "package foo //hi\n"},
+		{"//hi\npackage foo\n//a\n\n//b\n", "//hi\npackage foo\n//a\n\n//b\n"},
+		{"package a\n\nimport (\n  \"fmt\"\n)\n//hi\n",
+			"package a\n\nimport (\n  \"fmt\"\n)\n//hi\n"},
+	}
+	for i, x := range tdata {
+		got := importPrefix([]byte(x.input))
+		if got != x.want {
+			t.Errorf("%d: got\n%q, wanted\n%q", i, got, x.want)
+		}
+	}
 }
diff --git a/internal/lsp/source/highlight.go b/internal/lsp/source/highlight.go
index dc791be..fe91217 100644
--- a/internal/lsp/source/highlight.go
+++ b/internal/lsp/source/highlight.go
@@ -42,9 +42,10 @@
 	if len(path) == 0 {
 		return nil, fmt.Errorf("no enclosing position found for %v:%v", int(pos.Line), int(pos.Character))
 	}
-	// If start==end for astutil.PathEnclosingInterval, the 1-char interval following start is used instead.
-	// As a result, we might not get an exact match so we should check the 1-char interval to the left of the
-	// passed in position to see if that is an exact match.
+	// If start == end for astutil.PathEnclosingInterval, the 1-char interval
+	// following start is used instead. As a result, we might not get an exact
+	// match so we should check the 1-char interval to the left of the passed
+	// in position to see if that is an exact match.
 	if _, ok := path[0].(*ast.Ident); !ok {
 		if p, _ := astutil.PathEnclosingInterval(file, rng.Start-1, rng.Start-1); p != nil {
 			switch p[0].(type) {
@@ -53,44 +54,89 @@
 			}
 		}
 	}
+	result, err := highlightPath(pkg, path)
+	if err != nil {
+		return nil, err
+	}
+	var ranges []protocol.Range
+	for rng := range result {
+		mRng, err := posToMappedRange(snapshot.View(), pkg, rng.start, rng.end)
+		if err != nil {
+			return nil, err
+		}
+		pRng, err := mRng.Range()
+		if err != nil {
+			return nil, err
+		}
+		ranges = append(ranges, pRng)
+	}
+	return ranges, nil
+}
 
-	switch path[0].(type) {
+func highlightPath(pkg Package, path []ast.Node) (map[posRange]struct{}, error) {
+	result := make(map[posRange]struct{})
+	switch node := path[0].(type) {
 	case *ast.BasicLit:
 		if len(path) > 1 {
 			if _, ok := path[1].(*ast.ImportSpec); ok {
-				return highlightImportUses(ctx, snapshot.View(), pkg, path)
+				err := highlightImportUses(pkg, path, result)
+				return result, err
 			}
 		}
-		return highlightFuncControlFlow(ctx, snapshot.View(), pkg, path)
+		highlightFuncControlFlow(path, result)
 	case *ast.ReturnStmt, *ast.FuncDecl, *ast.FuncType:
-		return highlightFuncControlFlow(ctx, snapshot.View(), pkg, path)
+		highlightFuncControlFlow(path, result)
 	case *ast.Ident:
-		return highlightIdentifiers(ctx, snapshot.View(), pkg, path)
-	case *ast.BranchStmt, *ast.ForStmt, *ast.RangeStmt:
-		return highlightLoopControlFlow(ctx, snapshot.View(), pkg, path)
+		highlightIdentifiers(pkg, path, result)
+	case *ast.ForStmt, *ast.RangeStmt:
+		highlightLoopControlFlow(path, result)
+	case *ast.SwitchStmt:
+		highlightSwitchFlow(path, result)
+	case *ast.BranchStmt:
+		// BREAK can exit a loop, switch or select, while CONTINUE exit a loop so
+		// these need to be handled separately. They can also be embedded in any
+		// other loop/switch/select if they have a label. TODO: add support for
+		// GOTO and FALLTHROUGH as well.
+		if node.Label != nil {
+			highlightLabeledFlow(node, result)
+		} else {
+			switch node.Tok {
+			case token.BREAK:
+				highlightUnlabeledBreakFlow(path, result)
+			case token.CONTINUE:
+				highlightLoopControlFlow(path, result)
+			}
+		}
+	default:
+		// If the cursor is in an unidentified area, return empty results.
+		return nil, nil
 	}
-	// If the cursor is in an unidentified area, return empty results.
-	return nil, nil
+	return result, nil
 }
 
-func highlightFuncControlFlow(ctx context.Context, view View, pkg Package, path []ast.Node) ([]protocol.Range, error) {
+type posRange struct {
+	start, end token.Pos
+}
+
+func highlightFuncControlFlow(path []ast.Node, result map[posRange]struct{}) {
 	var enclosingFunc ast.Node
 	var returnStmt *ast.ReturnStmt
 	var resultsList *ast.FieldList
 	inReturnList := false
+
 Outer:
 	// Reverse walk the path till we get to the func block.
 	for i, n := range path {
 		switch node := n.(type) {
 		case *ast.KeyValueExpr:
 			// If cursor is in a key: value expr, we don't want control flow highlighting
-			return nil, nil
+			return
 		case *ast.CallExpr:
 			// If cusor is an arg in a callExpr, we don't want control flow highlighting.
 			if i > 0 {
 				for _, arg := range node.Args {
 					if arg == path[i-1] {
-						return nil, nil
+						return
 					}
 				}
 			}
@@ -113,7 +159,7 @@
 	}
 	// Cursor is not in a function.
 	if enclosingFunc == nil {
-		return nil, nil
+		return
 	}
 	// If the cursor is on a "return" or "func" keyword, we should highlight all of the exit
 	// points of the function, including the "return" and "func" keywords.
@@ -122,7 +168,7 @@
 	case *ast.Ident, *ast.BasicLit:
 		// Cursor is in an identifier and not in a return statement or in the results list.
 		if returnStmt == nil && !inReturnList {
-			return nil, nil
+			return
 		}
 	case *ast.FuncType:
 		highlightAllReturnsAndFunc = true
@@ -142,119 +188,263 @@
 	}
 	_, index := nodeAtPos(nodes, path[0].Pos())
 
-	result := make(map[protocol.Range]bool)
 	// Highlight the correct argument in the function declaration return types.
 	if resultsList != nil && -1 < index && index < len(resultsList.List) {
-		rng, err := nodeToProtocolRange(view, pkg, resultsList.List[index])
-		if err != nil {
-			return nil, err
+		rng := posRange{
+			start: resultsList.List[index].Pos(),
+			end:   resultsList.List[index].End(),
 		}
-		result[rng] = true
+		result[rng] = struct{}{}
 	}
 	// Add the "func" part of the func declaration.
 	if highlightAllReturnsAndFunc {
-		funcStmt, err := posToMappedRange(view, pkg, enclosingFunc.Pos(), enclosingFunc.Pos()+token.Pos(len("func")))
-		if err != nil {
-			return nil, err
+		r := posRange{
+			start: enclosingFunc.Pos(),
+			end:   enclosingFunc.Pos() + token.Pos(len("func")),
 		}
-		rng, err := funcStmt.Range()
-		if err != nil {
-			return nil, err
-		}
-		result[rng] = true
+		result[r] = struct{}{}
 	}
-	// Traverse the AST to highlight the other relevant return statements in the function.
 	ast.Inspect(enclosingFunc, func(n ast.Node) bool {
 		// Don't traverse any other functions.
 		switch n.(type) {
 		case *ast.FuncDecl, *ast.FuncLit:
 			return enclosingFunc == n
 		}
-		if n, ok := n.(*ast.ReturnStmt); ok {
-			var toAdd ast.Node
-			// Add the entire return statement, applies when highlight the word "return" or "func".
-			if highlightAllReturnsAndFunc {
-				toAdd = n
-			}
-			// Add the relevant field within the entire return statement.
-			if -1 < index && index < len(n.Results) {
-				toAdd = n.Results[index]
-			}
-			if toAdd != nil {
-				rng, err := nodeToProtocolRange(view, pkg, toAdd)
-				if err != nil {
-					event.Error(ctx, "Error getting range for node", err)
-					return false
-				}
-				result[rng] = true
-				return false
-			}
+		ret, ok := n.(*ast.ReturnStmt)
+		if !ok {
+			return true
 		}
-		return true
+		var toAdd ast.Node
+		// Add the entire return statement, applies when highlight the word "return" or "func".
+		if highlightAllReturnsAndFunc {
+			toAdd = n
+		}
+		// Add the relevant field within the entire return statement.
+		if -1 < index && index < len(ret.Results) {
+			toAdd = ret.Results[index]
+		}
+		if toAdd != nil {
+			result[posRange{start: toAdd.Pos(), end: toAdd.End()}] = struct{}{}
+		}
+		return false
 	})
-	return rangeMapToSlice(result), nil
 }
 
-func highlightLoopControlFlow(ctx context.Context, view View, pkg Package, path []ast.Node) ([]protocol.Range, error) {
-	var loop ast.Node
-Outer:
-	// Reverse walk the path till we get to the for loop.
+func highlightUnlabeledBreakFlow(path []ast.Node, result map[posRange]struct{}) {
+	// Reverse walk the path until we find closest loop, select, or switch.
 	for _, n := range path {
 		switch n.(type) {
 		case *ast.ForStmt, *ast.RangeStmt:
-			loop = n
-			break Outer
+			highlightLoopControlFlow(path, result)
+			return // only highlight the innermost statement
+		case *ast.SwitchStmt:
+			highlightSwitchFlow(path, result)
+			return
+		case *ast.SelectStmt:
+			// TODO: add highlight when breaking a select.
+			return
 		}
 	}
-	// Cursor is not in a for loop.
-	if loop == nil {
-		return nil, nil
-	}
-	result := make(map[protocol.Range]bool)
-	// Add the for statement.
-	forStmt, err := posToMappedRange(view, pkg, loop.Pos(), loop.Pos()+token.Pos(len("for")))
-	if err != nil {
-		return nil, err
-	}
-	rng, err := forStmt.Range()
-	if err != nil {
-		return nil, err
-	}
-	result[rng] = true
+}
 
+func highlightLabeledFlow(node *ast.BranchStmt, result map[posRange]struct{}) {
+	obj := node.Label.Obj
+	if obj == nil || obj.Decl == nil {
+		return
+	}
+	label, ok := obj.Decl.(*ast.LabeledStmt)
+	if !ok {
+		return
+	}
+	switch label.Stmt.(type) {
+	case *ast.ForStmt, *ast.RangeStmt:
+		highlightLoopControlFlow([]ast.Node{label.Stmt, label}, result)
+	case *ast.SwitchStmt:
+		highlightSwitchFlow([]ast.Node{label.Stmt, label}, result)
+	}
+}
+
+func labelFor(path []ast.Node) *ast.Ident {
+	if len(path) > 1 {
+		if n, ok := path[1].(*ast.LabeledStmt); ok {
+			return n.Label
+		}
+	}
+	return nil
+}
+
+func highlightLoopControlFlow(path []ast.Node, result map[posRange]struct{}) {
+	var loop ast.Node
+	var loopLabel *ast.Ident
+	stmtLabel := labelFor(path)
+Outer:
+	// Reverse walk the path till we get to the for loop.
+	for i := range path {
+		switch n := path[i].(type) {
+		case *ast.ForStmt, *ast.RangeStmt:
+			loopLabel = labelFor(path[i:])
+
+			if stmtLabel == nil || loopLabel == stmtLabel {
+				loop = n
+				break Outer
+			}
+		}
+	}
+	if loop == nil {
+		return
+	}
+
+	// Add the for statement.
+	rng := posRange{
+		start: loop.Pos(),
+		end:   loop.Pos() + token.Pos(len("for")),
+	}
+	result[rng] = struct{}{}
+
+	// Traverse AST to find branch statements within the same for-loop.
 	ast.Inspect(loop, func(n ast.Node) bool {
-		// Don't traverse any other for loops.
+		switch n.(type) {
+		case *ast.ForStmt, *ast.RangeStmt:
+			return loop == n
+		case *ast.SwitchStmt, *ast.SelectStmt:
+			return false
+		}
+		b, ok := n.(*ast.BranchStmt)
+		if !ok {
+			return true
+		}
+		if b.Label == nil || labelDecl(b.Label) == loopLabel {
+			result[posRange{start: b.Pos(), end: b.End()}] = struct{}{}
+		}
+		return true
+	})
+
+	// Find continue statements in the same loop or switches/selects.
+	ast.Inspect(loop, func(n ast.Node) bool {
 		switch n.(type) {
 		case *ast.ForStmt, *ast.RangeStmt:
 			return loop == n
 		}
-		// Add all branch statements in same scope as the identified one.
-		if n, ok := n.(*ast.BranchStmt); ok {
-			rng, err := nodeToProtocolRange(view, pkg, n)
-			if err != nil {
-				event.Error(ctx, "Error getting range for node", err)
-				return false
-			}
-			result[rng] = true
+
+		if n, ok := n.(*ast.BranchStmt); ok && n.Tok == token.CONTINUE {
+			result[posRange{start: n.Pos(), end: n.End()}] = struct{}{}
 		}
 		return true
 	})
-	return rangeMapToSlice(result), nil
-}
 
-func highlightImportUses(ctx context.Context, view View, pkg Package, path []ast.Node) ([]protocol.Range, error) {
-	result := make(map[protocol.Range]bool)
-	basicLit, ok := path[0].(*ast.BasicLit)
-	if !ok {
-		return nil, errors.Errorf("highlightImportUses called with an ast.Node of type %T", basicLit)
+	// We don't need to check other for loops if we aren't looking for labeled statements.
+	if loopLabel == nil {
+		return
 	}
 
+	// Find labeled branch statements in any loop
+	ast.Inspect(loop, func(n ast.Node) bool {
+		b, ok := n.(*ast.BranchStmt)
+		if !ok {
+			return true
+		}
+		// Statment with labels that matches the loop.
+		if b.Label != nil && labelDecl(b.Label) == loopLabel {
+			result[posRange{start: b.Pos(), end: b.End()}] = struct{}{}
+		}
+		return true
+	})
+}
+
+func highlightSwitchFlow(path []ast.Node, result map[posRange]struct{}) {
+	var switchNode ast.Node
+	var switchNodeLabel *ast.Ident
+	stmtLabel := labelFor(path)
+Outer:
+	// Reverse walk the path till we get to the switch statement.
+	for i := range path {
+		switch n := path[i].(type) {
+		case *ast.SwitchStmt:
+			switchNodeLabel = labelFor(path[i:])
+			if stmtLabel == nil || switchNodeLabel == stmtLabel {
+				switchNode = n
+				break Outer
+			}
+		}
+	}
+	// Cursor is not in a switch statement
+	if switchNode == nil {
+		return
+	}
+
+	// Add the switch statement.
+	rng := posRange{
+		start: switchNode.Pos(),
+		end:   switchNode.Pos() + token.Pos(len("switch")),
+	}
+	result[rng] = struct{}{}
+
+	// Traverse AST to find break statements within the same switch.
+	ast.Inspect(switchNode, func(n ast.Node) bool {
+		switch n.(type) {
+		case *ast.SwitchStmt:
+			return switchNode == n
+		case *ast.ForStmt, *ast.RangeStmt, *ast.SelectStmt:
+			return false
+		}
+
+		b, ok := n.(*ast.BranchStmt)
+		if !ok || b.Tok != token.BREAK {
+			return true
+		}
+
+		if b.Label == nil || labelDecl(b.Label) == switchNodeLabel {
+			result[posRange{start: b.Pos(), end: b.End()}] = struct{}{}
+		}
+		return true
+	})
+
+	// We don't need to check other switches if we aren't looking for labeled statements.
+	if switchNodeLabel == nil {
+		return
+	}
+
+	// Find labeled break statements in any switch
+	ast.Inspect(switchNode, func(n ast.Node) bool {
+		b, ok := n.(*ast.BranchStmt)
+		if !ok || b.Tok != token.BREAK {
+			return true
+		}
+
+		if b.Label != nil && labelDecl(b.Label) == switchNodeLabel {
+			result[posRange{start: b.Pos(), end: b.End()}] = struct{}{}
+		}
+
+		return true
+	})
+}
+
+func labelDecl(n *ast.Ident) *ast.Ident {
+	if n == nil {
+		return nil
+	}
+	if n.Obj == nil {
+		return nil
+	}
+	if n.Obj.Decl == nil {
+		return nil
+	}
+	stmt, ok := n.Obj.Decl.(*ast.LabeledStmt)
+	if !ok {
+		return nil
+	}
+	return stmt.Label
+}
+
+func highlightImportUses(pkg Package, path []ast.Node, result map[posRange]struct{}) error {
+	basicLit, ok := path[0].(*ast.BasicLit)
+	if !ok {
+		return errors.Errorf("highlightImportUses called with an ast.Node of type %T", basicLit)
+	}
 	ast.Inspect(path[len(path)-1], func(node ast.Node) bool {
 		if imp, ok := node.(*ast.ImportSpec); ok && imp.Path == basicLit {
-			if rng, err := nodeToProtocolRange(view, pkg, node); err == nil {
-				result[rng] = true
-				return false
-			}
+			result[posRange{start: node.Pos(), end: node.End()}] = struct{}{}
+			return false
 		}
 		n, ok := node.(*ast.Ident)
 		if !ok {
@@ -267,29 +457,19 @@
 		if !strings.Contains(basicLit.Value, obj.Name()) {
 			return true
 		}
-		rng, err := nodeToProtocolRange(view, pkg, n)
-		if err != nil {
-			event.Error(ctx, "Error getting range for node", err)
-			return false
-		}
-		result[rng] = true
+		result[posRange{start: n.Pos(), end: n.End()}] = struct{}{}
 		return false
 	})
-	return rangeMapToSlice(result), nil
+	return nil
 }
 
-func highlightIdentifiers(ctx context.Context, view View, pkg Package, path []ast.Node) ([]protocol.Range, error) {
-	result := make(map[protocol.Range]bool)
+func highlightIdentifiers(pkg Package, path []ast.Node, result map[posRange]struct{}) error {
 	id, ok := path[0].(*ast.Ident)
 	if !ok {
-		return nil, errors.Errorf("highlightIdentifiers called with an ast.Node of type %T", id)
+		return errors.Errorf("highlightIdentifiers called with an ast.Node of type %T", id)
 	}
 	// Check if ident is inside return or func decl.
-	if toAdd, err := highlightFuncControlFlow(ctx, view, pkg, path); toAdd != nil && err == nil {
-		for _, r := range toAdd {
-			result[r] = true
-		}
-	}
+	highlightFuncControlFlow(path, result)
 
 	// TODO: maybe check if ident is a reserved word, if true then don't continue and return results.
 
@@ -297,9 +477,7 @@
 	pkgObj, isImported := idObj.(*types.PkgName)
 	ast.Inspect(path[len(path)-1], func(node ast.Node) bool {
 		if imp, ok := node.(*ast.ImportSpec); ok && isImported {
-			if rng, err := highlightImport(view, pkg, pkgObj, imp); rng != nil && err == nil {
-				result[*rng] = true
-			}
+			highlightImport(pkgObj, imp, result)
 		}
 		n, ok := node.(*ast.Ident)
 		if !ok {
@@ -308,38 +486,20 @@
 		if n.Name != id.Name {
 			return false
 		}
-		if nObj := pkg.GetTypesInfo().ObjectOf(n); nObj != idObj {
-			return false
+		if nObj := pkg.GetTypesInfo().ObjectOf(n); nObj == idObj {
+			result[posRange{start: n.Pos(), end: n.End()}] = struct{}{}
 		}
-		rng, err := nodeToProtocolRange(view, pkg, n)
-		if err != nil {
-			event.Error(ctx, "Error getting range for node", err)
-			return false
-		}
-		result[rng] = true
 		return false
 	})
-	return rangeMapToSlice(result), nil
+	return nil
 }
 
-func highlightImport(view View, pkg Package, obj *types.PkgName, imp *ast.ImportSpec) (*protocol.Range, error) {
+func highlightImport(obj *types.PkgName, imp *ast.ImportSpec, result map[posRange]struct{}) {
 	if imp.Name != nil || imp.Path == nil {
-		return nil, nil
+		return
 	}
 	if !strings.Contains(imp.Path.Value, obj.Name()) {
-		return nil, nil
+		return
 	}
-	rng, err := nodeToProtocolRange(view, pkg, imp.Path)
-	if err != nil {
-		return nil, err
-	}
-	return &rng, nil
-}
-
-func rangeMapToSlice(rangeMap map[protocol.Range]bool) []protocol.Range {
-	var list []protocol.Range
-	for i := range rangeMap {
-		list = append(list, i)
-	}
-	return list
+	result[posRange{start: imp.Path.Pos(), end: imp.Path.End()}] = struct{}{}
 }
diff --git a/internal/lsp/source/hover.go b/internal/lsp/source/hover.go
index 78f3721..8fc6974 100644
--- a/internal/lsp/source/hover.go
+++ b/internal/lsp/source/hover.go
@@ -34,6 +34,9 @@
 	// FullDocumentation is the symbol's full documentation.
 	FullDocumentation string `json:"fullDocumentation"`
 
+	// ImportPath is the import path for the package containing the given symbol.
+	ImportPath string
+
 	// Link is the pkg.go.dev anchor for the given symbol.
 	// For example, "go/ast#Node".
 	Link string `json:"link"`
@@ -50,7 +53,7 @@
 	if err != nil {
 		return nil, nil
 	}
-	h, err := ident.Hover(ctx)
+	h, err := HoverIdentifier(ctx, ident)
 	if err != nil {
 		return nil, err
 	}
@@ -58,6 +61,10 @@
 	if err != nil {
 		return nil, err
 	}
+	// See golang/go#36998: don't link to modules matching GOPRIVATE.
+	if snapshot.View().IsGoPrivatePath(h.ImportPath) {
+		h.Link = ""
+	}
 	hover, err := FormatHover(h, snapshot.View().Options())
 	if err != nil {
 		return nil, err
@@ -71,7 +78,7 @@
 	}, nil
 }
 
-func (i *IdentifierInfo) Hover(ctx context.Context) (*HoverInformation, error) {
+func HoverIdentifier(ctx context.Context, i *IdentifierInfo) (*HoverInformation, error) {
 	ctx, done := event.Start(ctx, "source.Hover")
 	defer done()
 
@@ -94,7 +101,7 @@
 	if obj := i.Declaration.obj; obj != nil {
 		h.SingleLine = objectString(obj, i.qf)
 	}
-	h.Link, h.SymbolName = i.linkAndSymbolName()
+	h.ImportPath, h.Link, h.SymbolName = pathLinkAndSymbolName(i)
 	if h.comment != nil {
 		h.FullDocumentation = h.comment.Text()
 		h.Synopsis = doc.Synopsis(h.FullDocumentation)
@@ -102,32 +109,33 @@
 	return h, nil
 }
 
-func (i *IdentifierInfo) linkAndSymbolName() (string, string) {
+func pathLinkAndSymbolName(i *IdentifierInfo) (string, string, string) {
 	obj := i.Declaration.obj
 	if obj == nil {
-		return "", ""
+		return "", "", ""
 	}
 	switch obj := obj.(type) {
 	case *types.PkgName:
 		path := obj.Imported().Path()
+		link := path
 		if mod, version, ok := moduleAtVersion(path, i); ok {
-			path = strings.Replace(path, mod, mod+"@"+version, 1)
+			link = strings.Replace(path, mod, mod+"@"+version, 1)
 		}
-		return path, obj.Name()
+		return path, link, obj.Name()
 	case *types.Builtin:
-		return fmt.Sprintf("builtin#%s", obj.Name()), obj.Name()
+		return "builtin", fmt.Sprintf("builtin#%s", obj.Name()), obj.Name()
 	}
 	// Check if the identifier is test-only (and is therefore not part of a
 	// package's API). This is true if the request originated in a test package,
 	// and if the declaration is also found in the same test package.
 	if i.pkg != nil && obj.Pkg() != nil && i.pkg.ForTest() != "" {
 		if _, pkg, _ := FindFileInPackage(i.pkg, i.Declaration.MappedRange[0].URI()); i.pkg == pkg {
-			return "", ""
+			return "", "", ""
 		}
 	}
 	// Don't return links for other unexported types.
 	if !obj.Exported() {
-		return "", ""
+		return "", "", ""
 	}
 	var rTypeName string
 	switch obj := obj.(type) {
@@ -143,7 +151,7 @@
 	case *types.Func:
 		typ, ok := obj.Type().(*types.Signature)
 		if !ok {
-			return "", ""
+			return "", "", ""
 		}
 		if r := typ.Recv(); r != nil {
 			switch rtyp := deref(r.Type()).(type) {
@@ -153,7 +161,7 @@
 				if named, ok := i.enclosing.(*types.Named); ok {
 					rTypeName = named.Obj().Name()
 				} else if !rtyp.Obj().Exported() {
-					return "", ""
+					return "", "", ""
 				} else {
 					rTypeName = rtyp.Obj().Name()
 				}
@@ -161,16 +169,19 @@
 		}
 	}
 	path := obj.Pkg().Path()
+	link := path
 	if mod, version, ok := moduleAtVersion(path, i); ok {
-		path = strings.Replace(path, mod, mod+"@"+version, 1)
+		link = strings.Replace(path, mod, mod+"@"+version, 1)
 	}
 	if rTypeName != "" {
-		link := fmt.Sprintf("%s#%s.%s", path, rTypeName, obj.Name())
+		link = fmt.Sprintf("%s#%s.%s", link, rTypeName, obj.Name())
 		symbol := fmt.Sprintf("(%s.%s).%s", obj.Pkg().Name(), rTypeName, obj.Name())
-		return link, symbol
+		return path, link, symbol
 	}
 	// For most cases, the link is "package/path#symbol".
-	return fmt.Sprintf("%s#%s", path, obj.Name()), fmt.Sprintf("%s.%s", obj.Pkg().Name(), obj.Name())
+	link = fmt.Sprintf("%s#%s", link, obj.Name())
+	symbolName := fmt.Sprintf("%s.%s", obj.Pkg().Name(), obj.Name())
+	return path, link, symbolName
 }
 
 func moduleAtVersion(path string, i *IdentifierInfo) (string, string, bool) {
@@ -305,9 +316,9 @@
 			fieldList = t.Methods
 		}
 	case *ast.ValueSpec:
-		comment := decl.Doc
+		comment := spec.Doc
 		if comment == nil {
-			comment = spec.Doc
+			comment = decl.Doc
 		}
 		if comment == nil {
 			comment = spec.Comment
@@ -374,7 +385,6 @@
 		return plainLink
 	}
 }
-
 func formatDoc(doc string, options Options) string {
 	if options.PreferredContentFormat == protocol.Markdown {
 		return CommentToMarkdown(doc)
diff --git a/internal/lsp/source/identifier.go b/internal/lsp/source/identifier.go
index 85ca19f..0c6d288 100644
--- a/internal/lsp/source/identifier.go
+++ b/internal/lsp/source/identifier.go
@@ -165,20 +165,23 @@
 
 	// Handle builtins separately.
 	if result.Declaration.obj.Parent() == types.Universe {
-		astObj, err := view.LookupBuiltin(ctx, result.Name)
+		builtin, err := view.BuiltinPackage(ctx)
 		if err != nil {
 			return nil, err
 		}
-		decl, ok := astObj.Decl.(ast.Node)
+		decl, ok := builtin.Package().Scope.Lookup(result.Name).Decl.(ast.Node)
 		if !ok {
 			return nil, errors.Errorf("no declaration for %s", result.Name)
 		}
 		result.Declaration.node = decl
 
-		rng, err := nameToMappedRange(view, pkg, decl.Pos(), result.Name)
+		// The builtin package isn't in the dependency graph, so the usual utilities
+		// won't work here.
+		_, _, m, _, err := builtin.ParseGoHandle().Cached()
 		if err != nil {
 			return nil, err
 		}
+		rng := newMappedRange(view.Session().Cache().FileSet(), m, decl.Pos(), decl.Pos()+token.Pos(len(result.Name)))
 		result.Declaration.MappedRange = append(result.Declaration.MappedRange, rng)
 
 		return result, nil
diff --git a/internal/lsp/source/implementation.go b/internal/lsp/source/implementation.go
index 9826d23..527c8e6 100644
--- a/internal/lsp/source/implementation.go
+++ b/internal/lsp/source/implementation.go
@@ -285,7 +285,7 @@
 }
 
 func getASTFile(pkg Package, f FileHandle, pos protocol.Position) (*ast.File, token.Pos, error) {
-	pgh, err := pkg.File(f.Identity().URI)
+	pgh, err := pkg.File(f.URI())
 	if err != nil {
 		return nil, 0, err
 	}
diff --git a/internal/lsp/source/options.go b/internal/lsp/source/options.go
index 393880e..daf4b4b 100644
--- a/internal/lsp/source/options.go
+++ b/internal/lsp/source/options.go
@@ -38,6 +38,7 @@
 	"golang.org/x/tools/go/analysis/passes/unsafeptr"
 	"golang.org/x/tools/go/analysis/passes/unusedresult"
 	"golang.org/x/tools/internal/lsp/analysis/fillreturns"
+	"golang.org/x/tools/internal/lsp/analysis/fillstruct"
 	"golang.org/x/tools/internal/lsp/analysis/nonewvars"
 	"golang.org/x/tools/internal/lsp/analysis/noresultvalues"
 	"golang.org/x/tools/internal/lsp/analysis/simplifycompositelit"
@@ -53,12 +54,23 @@
 )
 
 const (
+	// CommandGenerate is a gopls command to run `go test` for a specific test function.
+	CommandTest = "test"
+
 	// CommandGenerate is a gopls command to run `go generate` for a directory.
 	CommandGenerate = "generate"
+
 	// CommandTidy is a gopls command to run `go mod tidy` for a module.
 	CommandTidy = "tidy"
+
+	// CommandVendor is a gopls command to run `go mod vendor` for a module.
+	CommandVendor = "vendor"
+
 	// CommandUpgradeDependency is a gopls command to upgrade a dependency.
-	CommandUpgradeDependency = "upgrade.dependency"
+	CommandUpgradeDependency = "upgrade_dependency"
+
+	// CommandRegenerateCfgo is a gopls command to regenerate cgo definitions.
+	CommandRegenerateCgo = "regenerate_cgo"
 )
 
 // DefaultOptions is the options that are used for Gopls execution independent
@@ -81,6 +93,7 @@
 					protocol.SourceFixAll:          true,
 					protocol.SourceOrganizeImports: true,
 					protocol.QuickFix:              true,
+					protocol.RefactorRewrite:       true,
 				},
 				Mod: {
 					protocol.SourceOrganizeImports: true,
@@ -88,9 +101,11 @@
 				Sum: {},
 			},
 			SupportedCommands: []string{
-				CommandTidy,              // for go.mod files
-				CommandUpgradeDependency, // for go.mod dependency upgrades
-				CommandGenerate,          // for "go generate" commands
+				CommandTest,
+				CommandTidy,
+				CommandUpgradeDependency,
+				CommandGenerate,
+				CommandRegenerateCgo,
 			},
 		},
 		UserOptions: UserOptions{
@@ -105,20 +120,23 @@
 			EnabledCodeLens: map[string]bool{
 				CommandGenerate:          true,
 				CommandUpgradeDependency: true,
+				CommandRegenerateCgo:     true,
 			},
 		},
 		DebuggingOptions: DebuggingOptions{
-			CompletionBudget: 100 * time.Millisecond,
+			CompletionBudget:   100 * time.Millisecond,
+			LiteralCompletions: true,
 		},
 		ExperimentalOptions: ExperimentalOptions{
 			TempModfile: true,
 		},
 		Hooks: Hooks{
-			ComputeEdits:       myers.ComputeEdits,
-			URLRegexp:          regexp.MustCompile(`(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?`),
-			DefaultAnalyzers:   defaultAnalyzers(),
-			TypeErrorAnalyzers: typeErrorAnalyzers(),
-			GoDiff:             true,
+			ComputeEdits:         myers.ComputeEdits,
+			URLRegexp:            regexp.MustCompile(`(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?`),
+			DefaultAnalyzers:     defaultAnalyzers(),
+			TypeErrorAnalyzers:   typeErrorAnalyzers(),
+			ConvenienceAnalyzers: convenienceAnalyzers(),
+			GoDiff:               true,
 		},
 	}
 }
@@ -227,11 +245,12 @@
 // Hooks contains configuration that is provided to the Gopls command by the
 // main package.
 type Hooks struct {
-	GoDiff             bool
-	ComputeEdits       diff.ComputeEdits
-	URLRegexp          *regexp.Regexp
-	DefaultAnalyzers   map[string]Analyzer
-	TypeErrorAnalyzers map[string]Analyzer
+	GoDiff               bool
+	ComputeEdits         diff.ComputeEdits
+	URLRegexp            *regexp.Regexp
+	DefaultAnalyzers     map[string]Analyzer
+	TypeErrorAnalyzers   map[string]Analyzer
+	ConvenienceAnalyzers map[string]Analyzer
 }
 
 func (o Options) AddDefaultAnalyzer(a *analysis.Analyzer) {
@@ -261,6 +280,11 @@
 	// dynamically reduce the search scope to ensure we return timely
 	// results. Zero means unlimited.
 	CompletionBudget time.Duration
+
+	// LiteralCompletions controls whether literal candidates such as
+	// "&someStruct{}" are offered. Tests disable this flag to simplify
+	// their expected values.
+	LiteralCompletions bool
 }
 
 type Matcher int
@@ -604,6 +628,15 @@
 	}
 }
 
+func convenienceAnalyzers() map[string]Analyzer {
+	return map[string]Analyzer{
+		fillstruct.Analyzer.Name: {
+			Analyzer: fillstruct.Analyzer,
+			enabled:  true,
+		},
+	}
+}
+
 func defaultAnalyzers() map[string]Analyzer {
 	return map[string]Analyzer{
 		// The traditional vet suite:
@@ -631,7 +664,7 @@
 		unsafeptr.Analyzer.Name:    {Analyzer: unsafeptr.Analyzer, enabled: true},
 		unusedresult.Analyzer.Name: {Analyzer: unusedresult.Analyzer, enabled: true},
 
-		// Non-vet analyzers
+		// Non-vet analyzers:
 		deepequalerrors.Analyzer.Name:  {Analyzer: deepequalerrors.Analyzer, enabled: true},
 		sortslice.Analyzer.Name:        {Analyzer: sortslice.Analyzer, enabled: true},
 		testinggoroutine.Analyzer.Name: {Analyzer: testinggoroutine.Analyzer, enabled: true},
diff --git a/internal/lsp/source/rename.go b/internal/lsp/source/rename.go
index 5d8068f..beba1ad 100644
--- a/internal/lsp/source/rename.go
+++ b/internal/lsp/source/rename.go
@@ -88,7 +88,7 @@
 		return nil, errors.Errorf("invalid identifier to rename: %q", newName)
 	}
 	if pkg == nil || pkg.IsIllTyped() {
-		return nil, errors.Errorf("package for %s is ill typed", f.Identity().URI)
+		return nil, errors.Errorf("package for %s is ill typed", f.URI())
 	}
 	refs, err := references(ctx, s, qos, true)
 	if err != nil {
@@ -126,11 +126,11 @@
 	for uri, edits := range changes {
 		// These edits should really be associated with FileHandles for maximal correctness.
 		// For now, this is good enough.
-		fh, err := s.GetFile(uri)
+		fh, err := s.GetFile(ctx, uri)
 		if err != nil {
 			return nil, err
 		}
-		data, _, err := fh.Read(ctx)
+		data, err := fh.Read()
 		if err != nil {
 			return nil, err
 		}
diff --git a/internal/lsp/source/rename_check.go b/internal/lsp/source/rename_check.go
index 8ff2879..1541f0e 100644
--- a/internal/lsp/source/rename_check.go
+++ b/internal/lsp/source/rename_check.go
@@ -315,7 +315,10 @@
 		case *ast.CompositeLit:
 			// Handle recursion ourselves for struct literals
 			// so we don't visit field identifiers.
-			tv := pkg.GetTypesInfo().Types[n]
+			tv, ok := pkg.GetTypesInfo().Types[n]
+			if !ok {
+				return visit(nil) // pop stack, don't descend
+			}
 			if _, ok := deref(tv.Type).Underlying().(*types.Struct); ok {
 				if n.Type != nil {
 					ast.Inspect(n.Type, visit)
diff --git a/internal/lsp/source/source_test.go b/internal/lsp/source/source_test.go
index 219388f..e08260e 100644
--- a/internal/lsp/source/source_test.go
+++ b/internal/lsp/source/source_test.go
@@ -56,6 +56,8 @@
 		if err != nil {
 			t.Fatal(err)
 		}
+		defer view.Shutdown(ctx)
+
 		// Enable type error analyses for tests.
 		// TODO(golang/go#38212): Delete this once they are enabled by default.
 		tests.EnableAllAnalyzers(snapshot, &options)
@@ -117,11 +119,9 @@
 		opts.Matcher = source.CaseInsensitive
 		opts.DeepCompletion = false
 		opts.UnimportedCompletion = false
-		opts.InsertTextFormat = protocol.PlainTextTextFormat
-		// Only enable literal completions if in the completion literals tests.
-		// TODO(rstambler): Separate out literal completion tests.
-		if strings.Contains(string(src.URI()), "literal") {
-			opts.InsertTextFormat = protocol.SnippetTextFormat
+		opts.InsertTextFormat = protocol.SnippetTextFormat
+		if !strings.Contains(string(src.URI()), "literal") {
+			opts.LiteralCompletions = false
 		}
 	})
 	got = tests.FilterBuiltins(src, got)
@@ -228,7 +228,7 @@
 }
 
 func (r *runner) callCompletion(t *testing.T, src span.Span, options func(*source.Options)) (string, []protocol.CompletionItem) {
-	fh, err := r.view.Snapshot().GetFile(src.URI())
+	fh, err := r.view.Snapshot().GetFile(r.ctx, src.URI())
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -274,11 +274,11 @@
 func (r *runner) FoldingRanges(t *testing.T, spn span.Span) {
 	uri := spn.URI()
 
-	fh, err := r.view.Snapshot().GetFile(spn.URI())
+	fh, err := r.view.Snapshot().GetFile(r.ctx, spn.URI())
 	if err != nil {
 		t.Fatal(err)
 	}
-	data, _, err := fh.Read(r.ctx)
+	data, err := fh.Read()
 	if err != nil {
 		t.Error(err)
 		return
@@ -414,7 +414,7 @@
 		out, _ := cmd.Output() // ignore error, sometimes we have intentionally ungofmt-able files
 		return out, nil
 	}))
-	fh, err := r.view.Snapshot().GetFile(spn.URI())
+	fh, err := r.view.Snapshot().GetFile(r.ctx, spn.URI())
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -425,7 +425,7 @@
 		}
 		return
 	}
-	data, _, err := fh.Read(r.ctx)
+	data, err := fh.Read()
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -444,7 +444,7 @@
 }
 
 func (r *runner) Import(t *testing.T, spn span.Span) {
-	fh, err := r.view.Snapshot().GetFile(spn.URI())
+	fh, err := r.view.Snapshot().GetFile(r.ctx, spn.URI())
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -452,11 +452,11 @@
 	if err != nil {
 		t.Error(err)
 	}
-	data, _, err := fh.Read(r.ctx)
+	data, err := fh.Read()
 	if err != nil {
 		t.Fatal(err)
 	}
-	m, err := r.data.Mapper(fh.Identity().URI)
+	m, err := r.data.Mapper(fh.URI())
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -481,7 +481,7 @@
 	if err != nil {
 		t.Fatal(err)
 	}
-	fh, err := r.view.Snapshot().GetFile(spn.URI())
+	fh, err := r.view.Snapshot().GetFile(r.ctx, spn.URI())
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -489,7 +489,7 @@
 	if err != nil {
 		t.Fatalf("failed for %v: %v", d.Src, err)
 	}
-	h, err := ident.Hover(r.ctx)
+	h, err := source.HoverIdentifier(r.ctx, ident)
 	if err != nil {
 		t.Fatalf("failed for %v: %v", d.Src, err)
 	}
@@ -541,7 +541,7 @@
 	if err != nil {
 		t.Fatalf("failed for %v: %v", spn, err)
 	}
-	fh, err := r.view.Snapshot().GetFile(spn.URI())
+	fh, err := r.view.Snapshot().GetFile(r.ctx, spn.URI())
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -585,7 +585,7 @@
 	if err != nil {
 		t.Fatal(err)
 	}
-	fh, err := r.view.Snapshot().GetFile(src.URI())
+	fh, err := r.view.Snapshot().GetFile(r.ctx, src.URI())
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -594,7 +594,7 @@
 		t.Errorf("highlight failed for %s: %v", src.URI(), err)
 	}
 	if len(highlights) != len(locations) {
-		t.Errorf("got %d highlights for highlight at %v:%v:%v, expected %d", len(highlights), src.URI().Filename(), src.Start().Line(), src.Start().Column(), len(locations))
+		t.Fatalf("got %d highlights for highlight at %v:%v:%v, expected %d", len(highlights), src.URI().Filename(), src.Start().Line(), src.Start().Column(), len(locations))
 	}
 	// Check to make sure highlights have a valid range.
 	var results []span.Span
@@ -624,7 +624,7 @@
 		t.Fatal(err)
 	}
 	snapshot := r.view.Snapshot()
-	fh, err := snapshot.GetFile(src.URI())
+	fh, err := snapshot.GetFile(r.ctx, src.URI())
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -663,14 +663,13 @@
 }
 
 func (r *runner) Rename(t *testing.T, spn span.Span, newText string) {
-	ctx := r.ctx
 	tag := fmt.Sprintf("%s-rename", newText)
 
 	_, srcRng, err := spanToRange(r.data, spn)
 	if err != nil {
 		t.Fatal(err)
 	}
-	fh, err := r.view.Snapshot().GetFile(spn.URI())
+	fh, err := r.view.Snapshot().GetFile(r.ctx, spn.URI())
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -687,15 +686,15 @@
 
 	var res []string
 	for editURI, edits := range changes {
-		fh, err := r.view.Snapshot().GetFile(editURI)
+		fh, err := r.view.Snapshot().GetFile(r.ctx, editURI)
 		if err != nil {
 			t.Fatal(err)
 		}
-		data, _, err := fh.Read(ctx)
+		data, err := fh.Read()
 		if err != nil {
 			t.Fatal(err)
 		}
-		m, err := r.data.Mapper(fh.Identity().URI)
+		m, err := r.data.Mapper(fh.URI())
 		if err != nil {
 			t.Fatal(err)
 		}
@@ -752,7 +751,7 @@
 		t.Fatal(err)
 	}
 	// Find the identifier at the position.
-	fh, err := r.view.Snapshot().GetFile(src.URI())
+	fh, err := r.view.Snapshot().GetFile(r.ctx, src.URI())
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -787,7 +786,7 @@
 }
 
 func (r *runner) Symbols(t *testing.T, uri span.URI, expectedSymbols []protocol.DocumentSymbol) {
-	fh, err := r.view.Snapshot().GetFile(uri)
+	fh, err := r.view.Snapshot().GetFile(r.ctx, uri)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -837,7 +836,7 @@
 	if err != nil {
 		t.Fatal(err)
 	}
-	fh, err := r.view.Snapshot().GetFile(spn.URI())
+	fh, err := r.view.Snapshot().GetFile(r.ctx, spn.URI())
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -869,7 +868,7 @@
 }
 
 func (r *runner) CodeLens(t *testing.T, uri span.URI, want []protocol.CodeLens) {
-	fh, err := r.view.Snapshot().GetFile(uri)
+	fh, err := r.view.Snapshot().GetFile(r.ctx, uri)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/internal/lsp/source/types_format.go b/internal/lsp/source/types_format.go
index b9e8955..53323f9 100644
--- a/internal/lsp/source/types_format.go
+++ b/internal/lsp/source/types_format.go
@@ -75,11 +75,11 @@
 }
 
 func newBuiltinSignature(ctx context.Context, view View, name string) (*signature, error) {
-	astObj, err := view.LookupBuiltin(ctx, name)
+	builtin, err := view.BuiltinPackage(ctx)
 	if err != nil {
 		return nil, err
 	}
-	decl, ok := astObj.Decl.(*ast.FuncDecl)
+	decl, ok := builtin.Package().Scope.Lookup(name).Decl.(*ast.FuncDecl)
 	if !ok {
 		return nil, fmt.Errorf("no function declaration for builtin: %s", name)
 	}
diff --git a/internal/lsp/source/util.go b/internal/lsp/source/util.go
index 69cfba9..ca0df19 100644
--- a/internal/lsp/source/util.go
+++ b/internal/lsp/source/util.go
@@ -80,7 +80,7 @@
 	if err != nil {
 		return nil, nil, err
 	}
-	pgh, err := pkg.File(fh.Identity().URI)
+	pgh, err := pkg.File(fh.URI())
 	return pkg, pgh, err
 }
 
@@ -142,11 +142,11 @@
 }
 
 func IsGenerated(ctx context.Context, snapshot Snapshot, uri span.URI) bool {
-	fh, err := snapshot.GetFile(uri)
+	fh, err := snapshot.GetFile(ctx, uri)
 	if err != nil {
 		return false
 	}
-	ph := snapshot.View().Session().Cache().ParseGoHandle(fh, ParseHeader)
+	ph := snapshot.View().Session().Cache().ParseGoHandle(ctx, fh, ParseHeader)
 	parsed, _, _, _, err := ph.Parse(ctx)
 	if err != nil {
 		return false
@@ -536,17 +536,7 @@
 	}
 	uri := span.URIFromPath(tok.Name())
 
-	var (
-		ph  ParseGoHandle
-		pkg Package
-		err error
-	)
-	// Special case for ignored files.
-	if v.Ignore(uri) {
-		ph, err = findIgnoredFile(v, uri)
-	} else {
-		ph, pkg, err = FindFileInPackage(searchpkg, uri)
-	}
+	ph, pkg, err := FindFileInPackage(searchpkg, uri)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -555,22 +545,13 @@
 		return nil, nil, err
 	}
 	if !(file.Pos() <= pos && pos <= file.End()) {
-		return nil, nil, fmt.Errorf("pos %v, apparently in file %q, is not between %v and %v", pos, ph.File().Identity().URI, file.Pos(), file.End())
+		return nil, nil, fmt.Errorf("pos %v, apparently in file %q, is not between %v and %v", pos, ph.File().URI(), file.Pos(), file.End())
 	}
 	return file, pkg, nil
 }
 
 func findMapperInPackage(v View, searchpkg Package, uri span.URI) (*protocol.ColumnMapper, error) {
-	var (
-		ph  ParseGoHandle
-		err error
-	)
-	// Special case for ignored files.
-	if v.Ignore(uri) {
-		ph, err = findIgnoredFile(v, uri)
-	} else {
-		ph, _, err = FindFileInPackage(searchpkg, uri)
-	}
+	ph, _, err := FindFileInPackage(searchpkg, uri)
 	if err != nil {
 		return nil, err
 	}
@@ -581,14 +562,6 @@
 	return m, nil
 }
 
-func findIgnoredFile(v View, uri span.URI) (ParseGoHandle, error) {
-	fh, err := v.Snapshot().GetFile(uri)
-	if err != nil {
-		return nil, err
-	}
-	return v.Session().Cache().ParseGoHandle(fh, ParseFull), nil
-}
-
 // FindFileInPackage finds uri in pkg or its dependencies.
 func FindFileInPackage(pkg Package, uri span.URI) (ParseGoHandle, Package, error) {
 	queue := []Package{pkg}
diff --git a/internal/lsp/source/view.go b/internal/lsp/source/view.go
index e204bd1..1854bc8 100644
--- a/internal/lsp/source/view.go
+++ b/internal/lsp/source/view.go
@@ -5,6 +5,7 @@
 package source
 
 import (
+	"bytes"
 	"context"
 	"fmt"
 	"go/ast"
@@ -17,8 +18,8 @@
 	"golang.org/x/tools/go/packages"
 	"golang.org/x/tools/internal/imports"
 	"golang.org/x/tools/internal/lsp/protocol"
-	"golang.org/x/tools/internal/packagesinternal"
 	"golang.org/x/tools/internal/span"
+	errors "golang.org/x/xerrors"
 )
 
 // Snapshot represents the current state for the given view.
@@ -28,16 +29,13 @@
 	// View returns the View associated with this snapshot.
 	View() View
 
-	// Config returns the configuration for the view.
-	Config(ctx context.Context) *packages.Config
-
 	// FindFile returns the FileHandle for the given URI, if it is already
 	// in the given snapshot.
 	FindFile(uri span.URI) FileHandle
 
 	// GetFile returns the FileHandle for a given URI, initializing it
 	// if it is not already part of the snapshot.
-	GetFile(uri span.URI) (FileHandle, error)
+	GetFile(ctx context.Context, uri span.URI) (FileHandle, error)
 
 	// IsOpen returns whether the editor currently has a file open.
 	IsOpen(uri span.URI) bool
@@ -48,13 +46,35 @@
 	// Analyze runs the analyses for the given package at this snapshot.
 	Analyze(ctx context.Context, pkgID string, analyzers ...*analysis.Analyzer) ([]*Error, error)
 
-	// ModTidyHandle returns a ModTidyHandle for the given go.mod file handle.
-	// This function can have no data or error if there is no modfile detected.
-	ModTidyHandle(ctx context.Context, fh FileHandle) (ModTidyHandle, error)
+	// RunGoCommandPiped runs the given `go` command in the view, using the
+	// provided stdout and stderr. It will use the -modfile flag, if possible.
+	RunGoCommandPiped(ctx context.Context, verb string, args []string, stdout, stderr io.Writer) error
 
-	// ModHandle returns a ModHandle for the passed in go.mod file handle.
-	// This function can have no data if there is no modfile detected.
-	ModHandle(ctx context.Context, fh FileHandle) ModHandle
+	// RunGoCommand runs the given `go` command in the view. It will use the
+	// -modfile flag, if possible.
+	RunGoCommand(ctx context.Context, verb string, args []string) (*bytes.Buffer, error)
+
+	// RunGoCommandDirect runs the given `go` command, never using the
+	// -modfile flag.
+	RunGoCommandDirect(ctx context.Context, verb string, args []string) error
+
+	// ParseModHandle is used to parse go.mod files.
+	ParseModHandle(ctx context.Context, fh FileHandle) (ParseModHandle, error)
+
+	// ModWhyHandle is used get the results of `go mod why` for a given module.
+	// It only works for go.mod files that can be parsed, hence it takes a
+	// ParseModHandle.
+	ModWhyHandle(ctx context.Context) (ModWhyHandle, error)
+
+	// ModWhyHandle is used get the possible upgrades for the dependencies of
+	// a given module. It only works for go.mod files that can be parsed, hence
+	// it takes a ParseModHandle.
+	ModUpgradeHandle(ctx context.Context) (ModUpgradeHandle, error)
+
+	// ModWhyHandle is used get the results of `go mod tidy` for a given
+	// module. It only works for go.mod files that can be parsed, hence it
+	// takes a ParseModHandle.
+	ModTidyHandle(ctx context.Context) (ModTidyHandle, error)
 
 	// PackageHandles returns the PackageHandles for the packages that this file
 	// belongs to.
@@ -110,11 +130,11 @@
 	// Folder returns the root folder for this view.
 	Folder() span.URI
 
-	// ModFiles returns the URIs of the go.mod files attached to the view associated with this snapshot.
-	ModFiles() (span.URI, span.URI)
+	// ModFile is the go.mod file at the root of this view. It may not exist.
+	ModFile() span.URI
 
-	// LookupBuiltin returns the go/ast.Object for the given name in the builtin package.
-	LookupBuiltin(ctx context.Context, name string) (*ast.Object, error)
+	// BuiltinPackage returns the go/ast.Object for the given name in the builtin package.
+	BuiltinPackage(ctx context.Context) (BuiltinPackage, error)
 
 	// BackgroundContext returns a context used for all background processing
 	// on behalf of this view.
@@ -123,9 +143,6 @@
 	// Shutdown closes this view, and detaches it from it's session.
 	Shutdown(ctx context.Context)
 
-	// Ignore returns true if this file should be ignored by this view.
-	Ignore(span.URI) bool
-
 	// WriteEnv writes the view-specific environment to the io.Writer.
 	WriteEnv(ctx context.Context, w io.Writer) error
 
@@ -152,6 +169,19 @@
 	// user's workspace. In particular, if they are both outside of a module
 	// and their GOPATH.
 	ValidBuildConfiguration() bool
+
+	// IsGoPrivatePath reports whether target is a private import path, as identified
+	// by the GOPRIVATE environment variable.
+	IsGoPrivatePath(path string) bool
+
+	// IgnoredFile reports if a file would be ignored by a `go list` of the whole
+	// workspace.
+	IgnoredFile(uri span.URI) bool
+}
+
+type BuiltinPackage interface {
+	Package() *ast.Package
+	ParseGoHandle() ParseGoHandle
 }
 
 // Session represents a single connection from a client.
@@ -177,16 +207,15 @@
 	// Shutdown the session and all views it has created.
 	Shutdown(ctx context.Context)
 
-	// A FileSystem prefers the contents from overlays, and falls back to the
-	// content from the underlying cache if no overlay is present.
-	FileSystem
+	// GetFile returns a handle for the specified file.
+	GetFile(ctx context.Context, uri span.URI) (FileHandle, error)
 
 	// DidModifyFile reports a file modification to the session.
 	// It returns the resulting snapshots, a guaranteed one per view.
 	DidModifyFiles(ctx context.Context, changes []FileModification) ([]Snapshot, error)
 
-	// UnsavedFiles returns a slice of open but unsaved files in the session.
-	UnsavedFiles() []span.URI
+	// Overlays returns a slice of file overlays for the session.
+	Overlays() []Overlay
 
 	// Options returns a copy of the SessionOptions for this session.
 	Options() Options
@@ -195,6 +224,21 @@
 	SetOptions(Options)
 }
 
+// Overlay is the type for a file held in memory on a session.
+type Overlay interface {
+	// Session returns the session this overlay belongs to.
+	Session() Session
+
+	// Identity returns the FileIdentity for the overlay.
+	Identity() FileIdentity
+
+	// Saved returns whether this overlay has been saved to disk.
+	Saved() bool
+
+	// Data is the contents of the overlay held in memory.
+	Data() []byte
+}
+
 // FileModification represents a modification to a file.
 type FileModification struct {
 	URI    span.URI
@@ -216,13 +260,14 @@
 type FileAction int
 
 const (
-	Open = FileAction(iota)
+	UnknownFileAction = FileAction(iota)
+	Open
 	Change
 	Close
 	Save
 	Create
 	Delete
-	UnknownFileAction
+	InvalidateMetadata
 )
 
 // Cache abstracts the core logic of dealing with the environment from the
@@ -233,20 +278,11 @@
 // sharing between all consumers.
 // A cache may have many active sessions at any given time.
 type Cache interface {
-	// A FileSystem that reads file contents from external storage.
-	FileSystem
-
 	// FileSet returns the shared fileset used by all files in the system.
 	FileSet() *token.FileSet
 
 	// ParseGoHandle returns a ParseGoHandle for the given file handle.
-	ParseGoHandle(fh FileHandle, mode ParseMode) ParseGoHandle
-}
-
-// FileSystem is the interface to something that provides file contents.
-type FileSystem interface {
-	// GetFile returns a handle for the specified file.
-	GetFile(uri span.URI) FileHandle
+	ParseGoHandle(ctx context.Context, fh FileHandle, mode ParseMode) ParseGoHandle
 }
 
 // ParseGoHandle represents a handle to the AST for a file.
@@ -265,37 +301,37 @@
 	Cached() (file *ast.File, src []byte, m *protocol.ColumnMapper, parseErr error, err error)
 }
 
-// ModHandle represents a handle to the modfile for a go.mod.
-type ModHandle interface {
-	// File returns a file handle for which to get the modfile.
-	File() FileHandle
+type ParseModHandle interface {
+	// Mod returns the file handle for the go.mod file.
+	Mod() FileHandle
 
-	// Parse returns the parsed modfile and a mapper for the go.mod file.
-	// If the file is not available, returns nil and an error.
-	Parse(ctx context.Context) (*modfile.File, *protocol.ColumnMapper, error)
+	// Sum returns the file handle for the analogous go.sum file. It may be nil.
+	Sum() FileHandle
 
-	// Upgrades returns the parsed modfile, a mapper, and any dependency upgrades
-	// for the go.mod file. Note that this will only work if the go.mod is the view's go.mod.
-	// If the file is not available, returns nil and an error.
-	Upgrades(ctx context.Context) (*modfile.File, *protocol.ColumnMapper, map[string]string, error)
-
-	// Why returns the parsed modfile, a mapper, and any explanations why a dependency should be
-	// in the go.mod file. Note that this will only work if the go.mod is the view's go.mod.
-	// If the file is not available, returns nil and an error.
-	Why(ctx context.Context) (*modfile.File, *protocol.ColumnMapper, map[string]string, error)
+	// Parse returns the parsed go.mod file, a column mapper, and a list of
+	// parse for the go.mod file.
+	Parse(ctx context.Context) (*modfile.File, *protocol.ColumnMapper, []Error, error)
 }
 
-// ModTidyHandle represents a handle to the modfile for the view.
-// Specifically for the purpose of getting diagnostics by running "go mod tidy".
+type ModUpgradeHandle interface {
+	// Upgrades returns the latest versions for each of the module's
+	// dependencies.
+	Upgrades(ctx context.Context) (map[string]string, error)
+}
+
+type ModWhyHandle interface {
+	// Why returns the results of `go mod why` for every dependency of the
+	// module.
+	Why(ctx context.Context) (map[string]string, error)
+}
+
 type ModTidyHandle interface {
-	// File returns a file handle for which to get the modfile.
-	File() FileHandle
-
-	// Tidy returns the parsed modfile, a mapper, and "go mod tidy" errors
-	// for the go.mod file. If the file is not available, returns nil and an error.
-	Tidy(ctx context.Context) (*modfile.File, *protocol.ColumnMapper, map[string]*modfile.Require, []Error, error)
+	// Tidy returns the results of `go mod tidy` for the module.
+	Tidy(ctx context.Context) (map[string]*modfile.Require, []Error, error)
 }
 
+var ErrTmpModfileUnsupported = errors.New("-modfile is unsupported for this Go version")
+
 // ParseMode controls the content of the AST produced when parsing a source file.
 type ParseMode int
 
@@ -316,18 +352,20 @@
 	ParseFull
 )
 
-// FileHandle represents a handle to a specific version of a single file from
-// a specific file system.
+// FileHandle represents a handle to a specific version of a single file.
 type FileHandle interface {
-	// FileSystem returns the file system this handle was acquired from.
-	FileSystem() FileSystem
+	URI() span.URI
+	Kind() FileKind
+	Version() float64
 
-	// Identity returns the FileIdentity for the file.
+	// Identity returns a FileIdentity for the file, even if there was an error
+	// reading it.
+	// It is a fatal error to call Identity on a file that has not yet been read.
 	Identity() FileIdentity
 
-	// Read reads the contents of a file and returns it along with its hash value.
+	// Read reads the contents of a file.
 	// If the file is not available, returns a nil slice and an error.
-	Read(ctx context.Context) ([]byte, string, error)
+	Read() ([]byte, error)
 }
 
 // FileIdentity uniquely identifies a file at a version from a FileSystem.
@@ -360,14 +398,15 @@
 type FileKind int
 
 const (
+	// UnknownKind is a file type we don't know about.
+	UnknownKind = FileKind(iota)
+
 	// Go is a normal go source file.
-	Go = FileKind(iota)
+	Go
 	// Mod is a go.mod file.
 	Mod
 	// Sum is a go.sum file.
 	Sum
-	// UnknownKind is a file type we don't know about.
-	UnknownKind
 )
 
 // Analyzer represents a go/analysis analyzer with some boolean properties
@@ -409,7 +448,7 @@
 	ForTest() string
 	GetImport(pkgPath string) (Package, error)
 	Imports() []Package
-	Module() *packagesinternal.Module
+	Module() *packages.Module
 }
 
 type Error struct {
@@ -435,3 +474,5 @@
 func (e *Error) Error() string {
 	return fmt.Sprintf("%s:%s: %s", e.URI, e.Range, e.Message)
 }
+
+var InconsistentVendoring = errors.New("inconsistent vendoring")
diff --git a/internal/lsp/symbols.go b/internal/lsp/symbols.go
index 7848eee..0f101a6 100644
--- a/internal/lsp/symbols.go
+++ b/internal/lsp/symbols.go
@@ -17,13 +17,13 @@
 	ctx, done := event.Start(ctx, "lsp.Server.documentSymbol")
 	defer done()
 
-	snapshot, fh, ok, err := s.beginFileRequest(params.TextDocument.URI, source.Go)
+	snapshot, fh, ok, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.Go)
 	if !ok {
 		return []interface{}{}, err
 	}
 	docSymbols, err := source.DocumentSymbols(ctx, snapshot, fh)
 	if err != nil {
-		event.Error(ctx, "DocumentSymbols failed", err, tag.URI.Of(fh.Identity().URI))
+		event.Error(ctx, "DocumentSymbols failed", err, tag.URI.Of(fh.URI()))
 		return []interface{}{}, nil
 	}
 	// Convert the symbols to an interface array.
diff --git a/internal/lsp/testdata/lsp/primarymod/cgo/declarecgo.go b/internal/lsp/testdata/lsp/primarymod/cgo/declarecgo.go
index d96582e..c283cdf 100644
--- a/internal/lsp/testdata/lsp/primarymod/cgo/declarecgo.go
+++ b/internal/lsp/testdata/lsp/primarymod/cgo/declarecgo.go
@@ -10,9 +10,10 @@
 */
 import "C"
 
-import "fmt"
-
-import "unsafe"
+import (
+	"fmt"
+	"unsafe"
+)
 
 func Example() { //@mark(funccgoexample, "Example"),item(funccgoexample, "Example", "func()", "func")
 	fmt.Println()
@@ -22,5 +23,5 @@
 }
 
 func _() {
-	Example()
+	Example() //@godef("ample", funccgoexample),complete("ample", funccgoexample)
 }
diff --git a/internal/lsp/testdata/lsp/primarymod/cgo/declarecgo.go.golden b/internal/lsp/testdata/lsp/primarymod/cgo/declarecgo.go.golden
new file mode 100644
index 0000000..e7fc967
--- /dev/null
+++ b/internal/lsp/testdata/lsp/primarymod/cgo/declarecgo.go.golden
@@ -0,0 +1,30 @@
+-- funccgoexample-definition --
+cgo/declarecgo.go:18:6-13: defined here as ```go
+func Example()
+```
+
+[`cgo.Example` on pkg.go.dev](https://pkg.go.dev/golang.org/x/tools/internal/lsp/cgo#Example)
+-- funccgoexample-definition-json --
+{
+	"span": {
+		"uri": "file://cgo/declarecgo.go",
+		"start": {
+			"line": 18,
+			"column": 6,
+			"offset": 151
+		},
+		"end": {
+			"line": 18,
+			"column": 13,
+			"offset": 158
+		}
+	},
+	"description": "```go\nfunc Example()\n```\n\n[`cgo.Example` on pkg.go.dev](https://pkg.go.dev/golang.org/x/tools/internal/lsp/cgo#Example)"
+}
+
+-- funccgoexample-hover --
+[`cgo.Example` on pkg.go.dev](https://pkg.go.dev/golang.org/x/tools/internal/lsp/cgo#Example)
+
+```go
+func Example()
+```
diff --git a/internal/lsp/testdata/lsp/primarymod/cgo/declarecgo_nocgo.go b/internal/lsp/testdata/lsp/primarymod/cgo/declarecgo_nocgo.go
index efc8934..a05c012 100644
--- a/internal/lsp/testdata/lsp/primarymod/cgo/declarecgo_nocgo.go
+++ b/internal/lsp/testdata/lsp/primarymod/cgo/declarecgo_nocgo.go
@@ -3,4 +3,4 @@
 package cgo
 
 // Set a dummy marker to keep the test framework happy. The tests should be skipped.
-var _ = "Example" //@mark(funccgoexample, "Example")
+var _ = "Example" //@mark(funccgoexample, "Example"),godef("ample", funccgoexample),complete("ample", funccgoexample)
diff --git a/internal/lsp/testdata/lsp/primarymod/cgoimport/usecgo.go.golden b/internal/lsp/testdata/lsp/primarymod/cgoimport/usecgo.go.golden
index 35937f1..cf683f3 100644
--- a/internal/lsp/testdata/lsp/primarymod/cgoimport/usecgo.go.golden
+++ b/internal/lsp/testdata/lsp/primarymod/cgoimport/usecgo.go.golden
@@ -1,5 +1,5 @@
 -- funccgoexample-definition --
-cgo/declarecgo.go:17:6-13: defined here as ```go
+cgo/declarecgo.go:18:6-13: defined here as ```go
 func cgo.Example()
 ```
 
@@ -9,14 +9,14 @@
 	"span": {
 		"uri": "file://cgo/declarecgo.go",
 		"start": {
-			"line": 17,
+			"line": 18,
 			"column": 6,
-			"offset": 153
+			"offset": 151
 		},
 		"end": {
-			"line": 17,
+			"line": 18,
 			"column": 13,
-			"offset": 160
+			"offset": 158
 		}
 	},
 	"description": "```go\nfunc cgo.Example()\n```\n\n[`cgo.Example` on pkg.go.dev](https://pkg.go.dev/golang.org/x/tools/internal/lsp/cgo#Example)"
diff --git a/internal/lsp/testdata/lsp/primarymod/codelens/codelens_test.go b/internal/lsp/testdata/lsp/primarymod/codelens/codelens_test.go
new file mode 100644
index 0000000..604d237
--- /dev/null
+++ b/internal/lsp/testdata/lsp/primarymod/codelens/codelens_test.go
@@ -0,0 +1,16 @@
+package codelens
+
+import "testing"
+
+// no code lens for TestMain
+func TestMain(m *testing.M) {
+}
+
+func TestFuncWithCodeLens(t *testing.T) { //@ codelens("func", "run test", "test")
+}
+
+func thisShouldNotHaveACodeLens(t *testing.T) {
+}
+
+func BenchmarkFuncWithCodeLens(b *testing.B) { //@ codelens("func", "run test", "test")
+}
diff --git a/internal/lsp/testdata/lsp/primarymod/fillstruct/data/a.go b/internal/lsp/testdata/lsp/primarymod/fillstruct/data/a.go
new file mode 100644
index 0000000..2860da9
--- /dev/null
+++ b/internal/lsp/testdata/lsp/primarymod/fillstruct/data/a.go
@@ -0,0 +1,6 @@
+package data

+

+type A struct {

+	ExportedInt   int

+	unexportedInt int

+}

diff --git a/internal/lsp/testdata/lsp/primarymod/fillstruct/fill_struct.go b/internal/lsp/testdata/lsp/primarymod/fillstruct/fill_struct.go
new file mode 100644
index 0000000..fccec13
--- /dev/null
+++ b/internal/lsp/testdata/lsp/primarymod/fillstruct/fill_struct.go
@@ -0,0 +1,26 @@
+package fillstruct
+
+type StructA struct {
+	unexportedIntField int
+	ExportedIntField   int
+	MapA               map[int]string
+	Array              []int
+	StructB
+}
+
+type StructA2 struct {
+	B *StructB
+}
+
+type StructA3 struct {
+	B StructB
+}
+
+func fill() {
+	a := StructA{}  //@suggestedfix("}", "refactor.rewrite")
+	b := StructA2{} //@suggestedfix("}", "refactor.rewrite")
+	c := StructA3{} //@suggestedfix("}", "refactor.rewrite")
+	if true {
+		_ = StructA3{} //@suggestedfix("}", "refactor.rewrite")
+	}
+}
diff --git a/internal/lsp/testdata/lsp/primarymod/fillstruct/fill_struct.go.golden b/internal/lsp/testdata/lsp/primarymod/fillstruct/fill_struct.go.golden
new file mode 100644
index 0000000..8d99703
--- /dev/null
+++ b/internal/lsp/testdata/lsp/primarymod/fillstruct/fill_struct.go.golden
@@ -0,0 +1,124 @@
+-- suggestedfix_fill_struct_20_15 --
+package fillstruct
+
+type StructA struct {
+	unexportedIntField int
+	ExportedIntField   int
+	MapA               map[int]string
+	Array              []int
+	StructB
+}
+
+type StructA2 struct {
+	B *StructB
+}
+
+type StructA3 struct {
+	B StructB
+}
+
+func fill() {
+	a := StructA{
+		unexportedIntField: 0,
+		ExportedIntField:   0,
+		MapA:               map[int]string{},
+		Array:              []int{},
+		StructB:            StructB{},
+	}  //@suggestedfix("}", "refactor.rewrite")
+	b := StructA2{} //@suggestedfix("}", "refactor.rewrite")
+	c := StructA3{} //@suggestedfix("}", "refactor.rewrite")
+	if true {
+		_ = StructA3{} //@suggestedfix("}", "refactor.rewrite")
+	}
+}
+
+-- suggestedfix_fill_struct_21_16 --
+package fillstruct
+
+type StructA struct {
+	unexportedIntField int
+	ExportedIntField   int
+	MapA               map[int]string
+	Array              []int
+	StructB
+}
+
+type StructA2 struct {
+	B *StructB
+}
+
+type StructA3 struct {
+	B StructB
+}
+
+func fill() {
+	a := StructA{}  //@suggestedfix("}", "refactor.rewrite")
+	b := StructA2{
+		B: &StructB{},
+	} //@suggestedfix("}", "refactor.rewrite")
+	c := StructA3{} //@suggestedfix("}", "refactor.rewrite")
+	if true {
+		_ = StructA3{} //@suggestedfix("}", "refactor.rewrite")
+	}
+}
+
+-- suggestedfix_fill_struct_22_16 --
+package fillstruct
+
+type StructA struct {
+	unexportedIntField int
+	ExportedIntField   int
+	MapA               map[int]string
+	Array              []int
+	StructB
+}
+
+type StructA2 struct {
+	B *StructB
+}
+
+type StructA3 struct {
+	B StructB
+}
+
+func fill() {
+	a := StructA{}  //@suggestedfix("}", "refactor.rewrite")
+	b := StructA2{} //@suggestedfix("}", "refactor.rewrite")
+	c := StructA3{
+		B: StructB{},
+	} //@suggestedfix("}", "refactor.rewrite")
+	if true {
+		_ = StructA3{} //@suggestedfix("}", "refactor.rewrite")
+	}
+}
+
+-- suggestedfix_fill_struct_24_16 --
+package fillstruct
+
+type StructA struct {
+	unexportedIntField int
+	ExportedIntField   int
+	MapA               map[int]string
+	Array              []int
+	StructB
+}
+
+type StructA2 struct {
+	B *StructB
+}
+
+type StructA3 struct {
+	B StructB
+}
+
+func fill() {
+	a := StructA{}  //@suggestedfix("}", "refactor.rewrite")
+	b := StructA2{} //@suggestedfix("}", "refactor.rewrite")
+	c := StructA3{} //@suggestedfix("}", "refactor.rewrite")
+	if true {
+		_ = StructA3{
+			B: StructB{},
+		} //@suggestedfix("}", "refactor.rewrite")
+	}
+}
+
diff --git a/internal/lsp/testdata/lsp/primarymod/fillstruct/fill_struct_nested.go b/internal/lsp/testdata/lsp/primarymod/fillstruct/fill_struct_nested.go
new file mode 100644
index 0000000..79eb84b
--- /dev/null
+++ b/internal/lsp/testdata/lsp/primarymod/fillstruct/fill_struct_nested.go
@@ -0,0 +1,15 @@
+package fillstruct
+
+type StructB struct {
+	StructC
+}
+
+type StructC struct {
+	unexportedInt int
+}
+
+func nested() {
+	c := StructB{
+		StructC: StructC{}, //@suggestedfix("}", "refactor.rewrite")
+	}
+}
diff --git a/internal/lsp/testdata/lsp/primarymod/fillstruct/fill_struct_nested.go.golden b/internal/lsp/testdata/lsp/primarymod/fillstruct/fill_struct_nested.go.golden
new file mode 100644
index 0000000..30061a5
--- /dev/null
+++ b/internal/lsp/testdata/lsp/primarymod/fillstruct/fill_struct_nested.go.golden
@@ -0,0 +1,19 @@
+-- suggestedfix_fill_struct_nested_13_20 --
+package fillstruct
+
+type StructB struct {
+	StructC
+}
+
+type StructC struct {
+	unexportedInt int
+}
+
+func nested() {
+	c := StructB{
+		StructC: StructC{
+			unexportedInt: 0,
+		}, //@suggestedfix("}", "refactor.rewrite")
+	}
+}
+
diff --git a/internal/lsp/testdata/lsp/primarymod/fillstruct/fill_struct_package.go b/internal/lsp/testdata/lsp/primarymod/fillstruct/fill_struct_package.go
new file mode 100644
index 0000000..336a375
--- /dev/null
+++ b/internal/lsp/testdata/lsp/primarymod/fillstruct/fill_struct_package.go
@@ -0,0 +1,9 @@
+package fillstruct
+
+import (
+	"golang.org/x/tools/internal/lsp/fillstruct/data"
+)
+
+func unexported() {
+	a := data.A{} //@suggestedfix("}", "refactor.rewrite")
+}
diff --git a/internal/lsp/testdata/lsp/primarymod/fillstruct/fill_struct_package.go.golden b/internal/lsp/testdata/lsp/primarymod/fillstruct/fill_struct_package.go.golden
new file mode 100644
index 0000000..d4cefe5
--- /dev/null
+++ b/internal/lsp/testdata/lsp/primarymod/fillstruct/fill_struct_package.go.golden
@@ -0,0 +1,13 @@
+-- suggestedfix_fill_struct_package_8_14 --
+package fillstruct
+
+import (
+	"golang.org/x/tools/internal/lsp/fillstruct/data"
+)
+
+func unexported() {
+	a := data.A{
+		ExportedInt: 0,
+	} //@suggestedfix("}", "refactor.rewrite")
+}
+
diff --git a/internal/lsp/testdata/lsp/primarymod/fillstruct/fill_struct_spaces.go b/internal/lsp/testdata/lsp/primarymod/fillstruct/fill_struct_spaces.go
new file mode 100644
index 0000000..d5d1bbb
--- /dev/null
+++ b/internal/lsp/testdata/lsp/primarymod/fillstruct/fill_struct_spaces.go
@@ -0,0 +1,9 @@
+package fillstruct
+
+type StructD struct {
+	ExportedIntField int
+}
+
+func spaces() {
+	d := StructD{} //@suggestedfix("}", "refactor.rewrite")
+}
diff --git a/internal/lsp/testdata/lsp/primarymod/fillstruct/fill_struct_spaces.go.golden b/internal/lsp/testdata/lsp/primarymod/fillstruct/fill_struct_spaces.go.golden
new file mode 100644
index 0000000..0d75533
--- /dev/null
+++ b/internal/lsp/testdata/lsp/primarymod/fillstruct/fill_struct_spaces.go.golden
@@ -0,0 +1,13 @@
+-- suggestedfix_fill_struct_spaces_8_15 --
+package fillstruct
+
+type StructD struct {
+	ExportedIntField int
+}
+
+func spaces() {
+	d := StructD{
+		ExportedIntField: 0,
+	} //@suggestedfix("}", "refactor.rewrite")
+}
+
diff --git a/internal/lsp/testdata/lsp/primarymod/folding/a.go b/internal/lsp/testdata/lsp/primarymod/folding/a.go
index 388a373..43f54d5 100644
--- a/internal/lsp/testdata/lsp/primarymod/folding/a.go
+++ b/internal/lsp/testdata/lsp/primarymod/folding/a.go
@@ -22,6 +22,28 @@
 	default:
 		fmt.Println("default")
 	}
+	_ = []int{
+		1,
+		2,
+		3,
+	}
+	_ = [2]string{"d",
+		"e"}
+	_ = map[string]int{
+		"a": 1,
+		"b": 2,
+		"c": 3,
+	}
+	type T struct {
+		f string
+		g int
+		h string
+	}
+	_ = T{
+		f: "j",
+		g: 4,
+		h: "i",
+	}
 	// This is a multiline comment
 	// that is not a doc comment.
 	return `
diff --git a/internal/lsp/testdata/lsp/primarymod/folding/a.go.golden b/internal/lsp/testdata/lsp/primarymod/folding/a.go.golden
index d5e4838..2b97014 100644
--- a/internal/lsp/testdata/lsp/primarymod/folding/a.go.golden
+++ b/internal/lsp/testdata/lsp/primarymod/folding/a.go.golden
@@ -22,6 +22,11 @@
 // With a multiline doc comment.
 func bar() string {
 	switch {<>}
+	_ = []int{<>}
+	_ = [2]string{<>}
+	_ = map[string]int{<>}
+	type T struct {<>}
+	_ = T{<>}
 	// This is a multiline comment<>
 	return `
 this string
@@ -46,6 +51,28 @@
 	case false:<>
 	default:<>
 	}
+	_ = []int{
+		1,
+		2,
+		3,
+	}
+	_ = [2]string{"d",
+		"e"}
+	_ = map[string]int{
+		"a": 1,
+		"b": 2,
+		"c": 3,
+	}
+	type T struct {
+		f string
+		g int
+		h string
+	}
+	_ = T{
+		f: "j",
+		g: 4,
+		h: "i",
+	}
 	// This is a multiline comment
 	// that is not a doc comment.
 	return `
@@ -74,6 +101,28 @@
 	default:
 		fmt.Println(<>)
 	}
+	_ = []int{
+		1,
+		2,
+		3,
+	}
+	_ = [2]string{"d",
+		"e"}
+	_ = map[string]int{
+		"a": 1,
+		"b": 2,
+		"c": 3,
+	}
+	type T struct {
+		f string
+		g int
+		h string
+	}
+	_ = T{
+		f: "j",
+		g: 4,
+		h: "i",
+	}
 	// This is a multiline comment
 	// that is not a doc comment.
 	return `
@@ -106,6 +155,28 @@
 	default:
 		fmt.Println("default")
 	}
+	_ = []int{
+		1,
+		2,
+		3,
+	}
+	_ = [2]string{"d",
+		"e"}
+	_ = map[string]int{
+		"a": 1,
+		"b": 2,
+		"c": 3,
+	}
+	type T struct {
+		f string
+		g int
+		h string
+	}
+	_ = T{
+		f: "j",
+		g: 4,
+		h: "i",
+	}
 	// This is a multiline comment
 	// that is not a doc comment.
 	return `
@@ -117,7 +188,7 @@
 3:9-6:0
 10:22-11:32
 12:10-12:9
-12:20-30:0
+12:20-52:0
 13:10-24:1
 14:12-19:3
 15:12-17:2
@@ -128,7 +199,12 @@
 21:15-21:21
 22:10-23:24
 23:15-23:23
-25:32-26:30
+25:12-29:1
+30:16-31:5
+32:21-36:1
+37:17-41:1
+42:8-46:1
+47:32-48:30
 
 -- foldingRange-comment-0 --
 package folding //@fold("package")
@@ -154,6 +230,28 @@
 	default:
 		fmt.Println("default")
 	}
+	_ = []int{
+		1,
+		2,
+		3,
+	}
+	_ = [2]string{"d",
+		"e"}
+	_ = map[string]int{
+		"a": 1,
+		"b": 2,
+		"c": 3,
+	}
+	type T struct {
+		f string
+		g int
+		h string
+	}
+	_ = T{
+		f: "j",
+		g: 4,
+		h: "i",
+	}
 	// This is a multiline comment<>
 	return `
 this string
@@ -182,6 +280,28 @@
 	default:
 		fmt.Println("default")
 	}
+	_ = []int{
+		1,
+		2,
+		3,
+	}
+	_ = [2]string{"d",
+		"e"}
+	_ = map[string]int{
+		"a": 1,
+		"b": 2,
+		"c": 3,
+	}
+	type T struct {
+		f string
+		g int
+		h string
+	}
+	_ = T{
+		f: "j",
+		g: 4,
+		h: "i",
+	}
 	// This is a multiline comment
 	// that is not a doc comment.
 	return `
@@ -216,6 +336,15 @@
 func bar() string {
 	switch {<>
 	}
+	_ = []int{<>,
+	}
+	_ = [2]string{<>}
+	_ = map[string]int{<>,
+	}
+	type T struct {<>
+	}
+	_ = T{<>,
+	}
 	// This is a multiline comment<>
 	return `
 this string
@@ -240,6 +369,28 @@
 	case false:<>
 	default:<>
 	}
+	_ = []int{
+		1,
+		2,
+		3,
+	}
+	_ = [2]string{"d",
+		"e"}
+	_ = map[string]int{
+		"a": 1,
+		"b": 2,
+		"c": 3,
+	}
+	type T struct {
+		f string
+		g int
+		h string
+	}
+	_ = T{
+		f: "j",
+		g: 4,
+		h: "i",
+	}
 	// This is a multiline comment
 	// that is not a doc comment.
 	return `
@@ -270,6 +421,28 @@
 	default:
 		fmt.Println("default")
 	}
+	_ = []int{
+		1,
+		2,
+		3,
+	}
+	_ = [2]string{"d",
+		"e"}
+	_ = map[string]int{
+		"a": 1,
+		"b": 2,
+		"c": 3,
+	}
+	type T struct {
+		f string
+		g int
+		h string
+	}
+	_ = T{
+		f: "j",
+		g: 4,
+		h: "i",
+	}
 	// This is a multiline comment
 	// that is not a doc comment.
 	return `
@@ -301,6 +474,28 @@
 	default:
 		fmt.Println("default")
 	}
+	_ = []int{
+		1,
+		2,
+		3,
+	}
+	_ = [2]string{"d",
+		"e"}
+	_ = map[string]int{
+		"a": 1,
+		"b": 2,
+		"c": 3,
+	}
+	type T struct {
+		f string
+		g int
+		h string
+	}
+	_ = T{
+		f: "j",
+		g: 4,
+		h: "i",
+	}
 	// This is a multiline comment<>
 	return `
 this string
@@ -330,6 +525,28 @@
 	default:
 		fmt.Println("default")
 	}
+	_ = []int{
+		1,
+		2,
+		3,
+	}
+	_ = [2]string{"d",
+		"e"}
+	_ = map[string]int{
+		"a": 1,
+		"b": 2,
+		"c": 3,
+	}
+	type T struct {
+		f string
+		g int
+		h string
+	}
+	_ = T{
+		f: "j",
+		g: 4,
+		h: "i",
+	}
 	// This is a multiline comment
 	// that is not a doc comment.
 	return `
diff --git a/internal/lsp/testdata/lsp/primarymod/godef/a/a.go b/internal/lsp/testdata/lsp/primarymod/godef/a/a.go
index f957c19..f818acc 100644
--- a/internal/lsp/testdata/lsp/primarymod/godef/a/a.go
+++ b/internal/lsp/testdata/lsp/primarymod/godef/a/a.go
@@ -12,6 +12,14 @@
 	x string //@x,hover("x", x)
 )
 
+// Constant block. When I hover on h, I should see this comment.
+const (
+	// When I hover on g, I should see this comment.
+	g = 1 //@g,hover("g", g)
+
+	h = 2 //@h,hover("h", h)
+)
+
 // z is a variable too.
 var z string //@z,hover("z", z)
 
diff --git a/internal/lsp/testdata/lsp/primarymod/godef/a/a.go.golden b/internal/lsp/testdata/lsp/primarymod/godef/a/a.go.golden
index 60b3bbd..e21d537 100644
--- a/internal/lsp/testdata/lsp/primarymod/godef/a/a.go.golden
+++ b/internal/lsp/testdata/lsp/primarymod/godef/a/a.go.golden
@@ -81,7 +81,7 @@
 -- aPackage-hover --
 Package a is a package for testing go to definition\.
 -- err-definition --
-godef/a/a.go:25:6-9: defined here as ```go
+godef/a/a.go:33:6-9: defined here as ```go
 var err error
 ```
 
@@ -91,14 +91,14 @@
 	"span": {
 		"uri": "file://godef/a/a.go",
 		"start": {
-			"line": 25,
+			"line": 33,
 			"column": 6,
-			"offset": 418
+			"offset": 597
 		},
 		"end": {
-			"line": 25,
+			"line": 33,
 			"column": 9,
-			"offset": 421
+			"offset": 600
 		}
 	},
 	"description": "```go\nvar err error\n```\n\n\\@err"
@@ -110,6 +110,18 @@
 ```go
 var err error
 ```
+-- g-hover --
+When I hover on g, I should see this comment\.
+
+```go
+const g untyped int = 1
+```
+-- h-hover --
+Constant block\.
+
+```go
+const h untyped int = 2
+```
 -- make-hover --
 The make built\-in function allocates and initializes an object of type slice, map, or chan \(only\)\.
 
diff --git a/internal/lsp/testdata/lsp/primarymod/godef/a/a_x_test.go b/internal/lsp/testdata/lsp/primarymod/godef/a/a_x_test.go
index 85f21cc..4631eba 100644
--- a/internal/lsp/testdata/lsp/primarymod/godef/a/a_x_test.go
+++ b/internal/lsp/testdata/lsp/primarymod/godef/a/a_x_test.go
@@ -5,4 +5,5 @@
 )
 
 func TestA2(t *testing.T) { //@TestA2,godef(TestA2, TestA2)
+	Nonexistant() //@diag("Nonexistant", "compiler", "undeclared name: Nonexistant", "error")
 }
diff --git a/internal/lsp/testdata/lsp/primarymod/godef/b/b.go.golden b/internal/lsp/testdata/lsp/primarymod/godef/b/b.go.golden
index c2eefff..7f14091 100644
--- a/internal/lsp/testdata/lsp/primarymod/godef/b/b.go.golden
+++ b/internal/lsp/testdata/lsp/primarymod/godef/b/b.go.golden
@@ -29,7 +29,7 @@
 package a ("golang.org/x/tools/internal/lsp/godef/a")
 ```
 -- AString-definition --
-godef/a/a.go:18:6-7: defined here as ```go
+godef/a/a.go:26:6-7: defined here as ```go
 A string //@mark(AString, "A")
 
 ```
@@ -40,14 +40,14 @@
 	"span": {
 		"uri": "file://godef/a/a.go",
 		"start": {
-			"line": 18,
+			"line": 26,
 			"column": 6,
-			"offset": 273
+			"offset": 452
 		},
 		"end": {
-			"line": 18,
+			"line": 26,
 			"column": 7,
-			"offset": 274
+			"offset": 453
 		}
 	},
 	"description": "```go\nA string //@mark(AString, \"A\")\n\n```\n\n[`a.A` on pkg.go.dev](https://pkg.go.dev/golang.org/x/tools/internal/lsp/godef/a#A)"
@@ -61,7 +61,7 @@
 
 ```
 -- AStuff-definition --
-godef/a/a.go:20:6-12: defined here as ```go
+godef/a/a.go:28:6-12: defined here as ```go
 func a.AStuff()
 ```
 
@@ -71,14 +71,14 @@
 	"span": {
 		"uri": "file://godef/a/a.go",
 		"start": {
-			"line": 20,
+			"line": 28,
 			"column": 6,
-			"offset": 310
+			"offset": 489
 		},
 		"end": {
-			"line": 20,
+			"line": 28,
 			"column": 12,
-			"offset": 316
+			"offset": 495
 		}
 	},
 	"description": "```go\nfunc a.AStuff()\n```\n\n[`a.AStuff` on pkg.go.dev](https://pkg.go.dev/golang.org/x/tools/internal/lsp/godef/a#AStuff)"
diff --git a/internal/lsp/testdata/lsp/primarymod/highlights/highlights.go b/internal/lsp/testdata/lsp/primarymod/highlights/highlights.go
index db09b56..55ae68a 100644
--- a/internal/lsp/testdata/lsp/primarymod/highlights/highlights.go
+++ b/internal/lsp/testdata/lsp/primarymod/highlights/highlights.go
@@ -72,6 +72,50 @@
 			continue //@mark(cont4, "continue"),highlight(cont4, forDecl4, brk4, cont4)
 		}
 	}
+
+Outer:
+	for i := 0; i < 10; i++ { //@mark(forDecl5, "for"),highlight(forDecl5, forDecl5, brk5, brk6, brk8)
+		break //@mark(brk5, "break"),highlight(brk5, forDecl5, brk5, brk6, brk8)
+		for { //@mark(forDecl6, "for"),highlight(forDecl6, forDecl6, cont5)
+			if i == 1 {
+				break Outer //@mark(brk6, "break Outer"),highlight(brk6, forDecl5, brk5, brk6, brk8)
+			}
+			switch i { //@mark(switch1, "switch"),highlight(switch1, switch1, brk7)
+			case 5:
+				break //@mark(brk7, "break"),highlight(brk7, switch1, brk7)
+			case 6:
+				continue //@mark(cont5, "continue"),highlight(cont5, forDecl6, cont5)
+			case 7:
+				break Outer //@mark(brk8, "break Outer"),highlight(brk8, forDecl5, brk5, brk6, brk8)
+			}
+		}
+	}
+}
+
+func testSwitch() {
+	var i, j int
+
+L1:
+	for { //@mark(forDecl7, "for"),highlight(forDecl7, forDecl7, brk10, cont6)
+	L2:
+		switch i { //@mark(switch2, "switch"),highlight(switch2, switch2, brk11, brk12, brk13)
+		case 1:
+			switch j { //@mark(switch3, "switch"),highlight(switch3, switch3, brk9)
+			case 1:
+				break //@mark(brk9, "break"),highlight(brk9, switch3, brk9)
+			case 2:
+				break L1 //@mark(brk10, "break L1"),highlight(brk10, forDecl7, brk10, cont6)
+			case 3:
+				break L2 //@mark(brk11, "break L2"),highlight(brk11, switch2, brk11, brk12, brk13)
+			default:
+				continue //@mark(cont6, "continue"),highlight(cont6, forDecl7, brk10, cont6)
+			}
+		case 2:
+			break //@mark(brk12, "break"),highlight(brk12, switch2, brk11, brk12, brk13)
+		default:
+			break L2 //@mark(brk13, "break L2"),highlight(brk13, switch2, brk11, brk12, brk13)
+		}
+	}
 }
 
 func testReturn() bool { //@mark(func1, "func"),mark(bool1, "bool"),highlight(func1, func1, fullRet11, fullRet12),highlight(bool1, bool1, false1, bool2, true1)
diff --git a/internal/lsp/testdata/lsp/primarymod/imports/issue35458.go.golden b/internal/lsp/testdata/lsp/primarymod/imports/issue35458.go.golden
index 60e5081..f077260 100644
--- a/internal/lsp/testdata/lsp/primarymod/imports/issue35458.go.golden
+++ b/internal/lsp/testdata/lsp/primarymod/imports/issue35458.go.golden
@@ -1,9 +1,5 @@
 -- goimports --
-
-
-
-
-
+// package doc
 package imports //@import("package")
 
 
diff --git a/internal/lsp/testdata/lsp/primarymod/imports/issue35458.go.in b/internal/lsp/testdata/lsp/primarymod/imports/issue35458.go.in
index c4e3bd5..7420c21 100644
--- a/internal/lsp/testdata/lsp/primarymod/imports/issue35458.go.in
+++ b/internal/lsp/testdata/lsp/primarymod/imports/issue35458.go.in
@@ -3,6 +3,7 @@
 
 
 
+// package doc
 package imports //@import("package")
 
 
diff --git a/internal/lsp/testdata/lsp/primarymod/rename/issue39614/issue39614.go.golden b/internal/lsp/testdata/lsp/primarymod/rename/issue39614/issue39614.go.golden
new file mode 100644
index 0000000..d87c58e
--- /dev/null
+++ b/internal/lsp/testdata/lsp/primarymod/rename/issue39614/issue39614.go.golden
@@ -0,0 +1,10 @@
+-- bar-rename --
+package issue39614
+
+func fn() {
+	var bar bool //@rename("foo","bar")
+	make(map[string]bool
+	if true {
+	}
+}
+
diff --git a/internal/lsp/testdata/lsp/primarymod/rename/issue39614/issue39614.go.in b/internal/lsp/testdata/lsp/primarymod/rename/issue39614/issue39614.go.in
new file mode 100644
index 0000000..8222db2
--- /dev/null
+++ b/internal/lsp/testdata/lsp/primarymod/rename/issue39614/issue39614.go.in
@@ -0,0 +1,8 @@
+package issue39614
+
+func fn() {
+	var foo bool //@rename("foo","bar")
+	make(map[string]bool
+	if true {
+	}
+}
diff --git a/internal/lsp/testdata/lsp/primarymod/statements/append.go b/internal/lsp/testdata/lsp/primarymod/statements/append.go
new file mode 100644
index 0000000..0eea85a
--- /dev/null
+++ b/internal/lsp/testdata/lsp/primarymod/statements/append.go
@@ -0,0 +1,42 @@
+package statements
+
+func _() {
+	type mySlice []int
+
+	var (
+		abc    []int   //@item(stmtABC, "abc", "[]int", "var")
+		abcdef mySlice //@item(stmtABCDEF, "abcdef", "mySlice", "var")
+	)
+
+	/* abcdef = append(abcdef, ) */ //@item(stmtABCDEFAssignAppend, "abcdef = append(abcdef, )", "", "func")
+
+	// don't offer "abc = append(abc, )" because "abc" isn't necessarily
+	// better than "abcdef".
+	abc //@complete(" //", stmtABC, stmtABCDEF)
+
+	abcdef //@complete(" //", stmtABCDEF, stmtABCDEFAssignAppend)
+
+	/* append(abc, ) */ //@item(stmtABCAppend, "append(abc, )", "", "func")
+
+	abc = app //@snippet(" //", stmtABCAppend, "append(abc, ${1:})", "append(abc, ${1:})")
+}
+
+func _() {
+	var s struct{ xyz []int }
+
+	/* xyz = append(s.xyz, ) */ //@item(stmtXYZAppend, "xyz = append(s.xyz, )", "", "func")
+
+	s.x //@snippet(" //", stmtXYZAppend, "xyz = append(s.xyz, ${1:})", "xyz = append(s.xyz, ${1:})")
+
+	/* s.xyz = append(s.xyz, ) */ //@item(stmtDeepXYZAppend, "s.xyz = append(s.xyz, )", "", "func")
+
+	sx //@snippet(" //", stmtDeepXYZAppend, "s.xyz = append(s.xyz, ${1:})", "s.xyz = append(s.xyz, ${1:})")
+}
+
+func _() {
+	var foo [][]int
+
+	/* append(foo[0], ) */ //@item(stmtFooAppend, "append(foo[0], )", "", "func")
+
+	foo[0] = app //@complete(" //"),snippet(" //", stmtFooAppend, "append(foo[0], ${1:})", "append(foo[0], ${1:})")
+}
diff --git a/internal/lsp/testdata/lsp/primarymod/unimported/unimported.go.in b/internal/lsp/testdata/lsp/primarymod/unimported/unimported.go.in
index 3c5ab11..91a11fa 100644
--- a/internal/lsp/testdata/lsp/primarymod/unimported/unimported.go.in
+++ b/internal/lsp/testdata/lsp/primarymod/unimported/unimported.go.in
@@ -1,7 +1,7 @@
 package unimported
 
 func _() {
-	http //@unimported("p", nethttp, nethttptest)
+	http //@unimported("p", nethttp)
 	pkg  //@unimported("g", externalpackage)
 	// container/ring is extremely unlikely to be imported by anything, so shouldn't have type information.
 	ring.Ring     //@unimported("Ring", ringring)
@@ -12,7 +12,7 @@
 
 // Create markers for unimported std lib packages. Only for use by this test.
 /* http */ //@item(nethttp, "http", "\"net/http\"", "package")
-/* httptest */ //@item(nethttptest, "httptest", "\"net/http/httptest\"", "package")
+
 /* pkg */ //@item(externalpackage, "pkg", "\"example.com/extramodule/pkg\"", "package")
 
 /* ring.Ring */ //@item(ringring, "Ring", "(from \"container/ring\")", "var")
diff --git a/internal/lsp/testdata/lsp/primarymod/workspacesymbol/a/a.go b/internal/lsp/testdata/lsp/primarymod/workspacesymbol/a/a.go
index 7ff01b2..072f4cb 100644
--- a/internal/lsp/testdata/lsp/primarymod/workspacesymbol/a/a.go
+++ b/internal/lsp/testdata/lsp/primarymod/workspacesymbol/a/a.go
@@ -1,9 +1,9 @@
 package a
 
-var WorkspaceSymbolVariableA = "a" //@symbol("WorkspaceSymbolVariableA", "WorkspaceSymbolVariableA", "Variable", "", "WorkspaceSymbolVariableA")
+var RandomGopherVariableA = "a" //@symbol("RandomGopherVariableA", "RandomGopherVariableA", "Variable", "", "RandomGopherVariableA")
 
-const WorkspaceSymbolConstantA = "a" //@symbol("WorkspaceSymbolConstantA", "WorkspaceSymbolConstantA", "Constant", "", "WorkspaceSymbolConstantA")
+const RandomGopherConstantA = "a" //@symbol("RandomGopherConstantA", "RandomGopherConstantA", "Constant", "", "RandomGopherConstantA")
 
 const (
-	workspacesymbolinvariable = iota //@symbol("workspacesymbolinvariable", "workspacesymbolinvariable", "Constant", "", "workspacesymbolinvariable")
+	randomgopherinvariable = iota //@symbol("randomgopherinvariable", "randomgopherinvariable", "Constant", "", "randomgopherinvariable")
 )
diff --git a/internal/lsp/testdata/lsp/primarymod/workspacesymbol/a/a.go.golden b/internal/lsp/testdata/lsp/primarymod/workspacesymbol/a/a.go.golden
index 2a8788b..c3f0885 100644
--- a/internal/lsp/testdata/lsp/primarymod/workspacesymbol/a/a.go.golden
+++ b/internal/lsp/testdata/lsp/primarymod/workspacesymbol/a/a.go.golden
@@ -1,5 +1,5 @@
 -- symbols --
-WorkspaceSymbolVariableA Variable 3:5-3:29
-WorkspaceSymbolConstantA Constant 5:7-5:31
-workspacesymbolinvariable Constant 8:2-8:27
+RandomGopherVariableA Variable 3:5-3:26
+RandomGopherConstantA Constant 5:7-5:28
+randomgopherinvariable Constant 8:2-8:24
 
diff --git a/internal/lsp/testdata/lsp/primarymod/workspacesymbol/b/b.go b/internal/lsp/testdata/lsp/primarymod/workspacesymbol/b/b.go
index cbe98ab..b90b3a9 100644
--- a/internal/lsp/testdata/lsp/primarymod/workspacesymbol/b/b.go
+++ b/internal/lsp/testdata/lsp/primarymod/workspacesymbol/b/b.go
@@ -1,7 +1,7 @@
 package b
 
-var WorkspaceSymbolVariableB = "b" //@symbol("WorkspaceSymbolVariableB", "WorkspaceSymbolVariableB", "Variable", "", "WorkspaceSymbolVariableB")
+var RandomGopherVariableB = "b" //@symbol("RandomGopherVariableB", "RandomGopherVariableB", "Variable", "", "RandomGopherVariableB")
 
-type WorkspaceSymbolStructB struct { //@symbol("WorkspaceSymbolStructB", "WorkspaceSymbolStructB", "Struct", "", "WorkspaceSymbolStructB")
-	Bar int //@mark(bBar, "Bar"), symbol("Bar", "Bar", "Field", "WorkspaceSymbolStructB", "Bar")
+type RandomGopherStructB struct { //@symbol("RandomGopherStructB", "RandomGopherStructB", "Struct", "", "RandomGopherStructB")
+	Bar int //@mark(bBar, "Bar"), symbol("Bar", "Bar", "Field", "RandomGopherStructB", "Bar")
 }
diff --git a/internal/lsp/testdata/lsp/primarymod/workspacesymbol/b/b.go.golden b/internal/lsp/testdata/lsp/primarymod/workspacesymbol/b/b.go.golden
index ecc8781..4711c9d 100644
--- a/internal/lsp/testdata/lsp/primarymod/workspacesymbol/b/b.go.golden
+++ b/internal/lsp/testdata/lsp/primarymod/workspacesymbol/b/b.go.golden
@@ -1,5 +1,5 @@
 -- symbols --
-WorkspaceSymbolVariableB Variable 3:5-3:29
-WorkspaceSymbolStructB Struct 5:6-5:28
+RandomGopherVariableB Variable 3:5-3:26
+RandomGopherStructB Struct 5:6-5:25
 	Bar Field 6:2-6:5
 
diff --git a/internal/lsp/testdata/lsp/primarymod/workspacesymbol/fuzzy/fuzzy.go b/internal/lsp/testdata/lsp/primarymod/workspacesymbol/fuzzy/fuzzy.go
index de03d36..4bc8b54 100644
--- a/internal/lsp/testdata/lsp/primarymod/workspacesymbol/fuzzy/fuzzy.go
+++ b/internal/lsp/testdata/lsp/primarymod/workspacesymbol/fuzzy/fuzzy.go
@@ -1,23 +1,23 @@
 package fuzzy
 
 /*@
-workspacesymbolfuzzy("wsym",
-	WorkspaceSymbolVariableA,
-	WorkspaceSymbolConstantA,
-	workspacesymbolinvariable,
-	WorkspaceSymbolVariableB,
-	WorkspaceSymbolStructB,
+workspacesymbolfuzzy("rgop",
+	RandomGopherVariableA,
+	RandomGopherConstantA,
+	randomgopherinvariable,
+	RandomGopherVariableB,
+	RandomGopherStructB,
 )
-workspacesymbolfuzzy("symbola",
-	WorkspaceSymbolVariableA,
-	WorkspaceSymbolConstantA,
-	workspacesymbolinvariable,
-	WorkspaceSymbolVariableB,
+workspacesymbolfuzzy("randoma",
+	RandomGopherVariableA,
+	RandomGopherConstantA,
+	randomgopherinvariable,
+	RandomGopherVariableB,
 )
-workspacesymbolfuzzy("symbolb",
-	WorkspaceSymbolVariableA,
-	workspacesymbolinvariable,
-	WorkspaceSymbolVariableB,
-	WorkspaceSymbolStructB,
+workspacesymbolfuzzy("randomb",
+	RandomGopherVariableA,
+	randomgopherinvariable,
+	RandomGopherVariableB,
+	RandomGopherStructB,
 )
 */
diff --git a/internal/lsp/testdata/lsp/primarymod/workspacesymbol/fuzzy/randoma.golden b/internal/lsp/testdata/lsp/primarymod/workspacesymbol/fuzzy/randoma.golden
new file mode 100644
index 0000000..bfdcb21
--- /dev/null
+++ b/internal/lsp/testdata/lsp/primarymod/workspacesymbol/fuzzy/randoma.golden
@@ -0,0 +1,5 @@
+-- workspace_symbol --
+workspacesymbol/a/a.go:3:5-26 RandomGopherVariableA Variable
+workspacesymbol/a/a.go:5:7-28 RandomGopherConstantA Constant
+workspacesymbol/a/a.go:8:2-24 randomgopherinvariable Constant
+workspacesymbol/b/b.go:3:5-26 RandomGopherVariableB Variable
diff --git a/internal/lsp/testdata/lsp/primarymod/workspacesymbol/fuzzy/randomb.golden b/internal/lsp/testdata/lsp/primarymod/workspacesymbol/fuzzy/randomb.golden
new file mode 100644
index 0000000..d0fef3e
--- /dev/null
+++ b/internal/lsp/testdata/lsp/primarymod/workspacesymbol/fuzzy/randomb.golden
@@ -0,0 +1,5 @@
+-- workspace_symbol --
+workspacesymbol/a/a.go:3:5-26 RandomGopherVariableA Variable
+workspacesymbol/a/a.go:8:2-24 randomgopherinvariable Constant
+workspacesymbol/b/b.go:3:5-26 RandomGopherVariableB Variable
+workspacesymbol/b/b.go:5:6-25 RandomGopherStructB Struct
diff --git a/internal/lsp/testdata/lsp/primarymod/workspacesymbol/fuzzy/rgop.golden b/internal/lsp/testdata/lsp/primarymod/workspacesymbol/fuzzy/rgop.golden
new file mode 100644
index 0000000..0c5fde3
--- /dev/null
+++ b/internal/lsp/testdata/lsp/primarymod/workspacesymbol/fuzzy/rgop.golden
@@ -0,0 +1,6 @@
+-- workspace_symbol --
+workspacesymbol/a/a.go:3:5-26 RandomGopherVariableA Variable
+workspacesymbol/a/a.go:5:7-28 RandomGopherConstantA Constant
+workspacesymbol/a/a.go:8:2-24 randomgopherinvariable Constant
+workspacesymbol/b/b.go:3:5-26 RandomGopherVariableB Variable
+workspacesymbol/b/b.go:5:6-25 RandomGopherStructB Struct
diff --git a/internal/lsp/testdata/lsp/primarymod/workspacesymbol/fuzzy/symbola.golden b/internal/lsp/testdata/lsp/primarymod/workspacesymbol/fuzzy/symbola.golden
deleted file mode 100644
index ccfa859..0000000
--- a/internal/lsp/testdata/lsp/primarymod/workspacesymbol/fuzzy/symbola.golden
+++ /dev/null
@@ -1,5 +0,0 @@
--- workspace_symbol --
-workspacesymbol/a/a.go:3:5-29 WorkspaceSymbolVariableA Variable
-workspacesymbol/a/a.go:5:7-31 WorkspaceSymbolConstantA Constant
-workspacesymbol/a/a.go:8:2-27 workspacesymbolinvariable Constant
-workspacesymbol/b/b.go:3:5-29 WorkspaceSymbolVariableB Variable
diff --git a/internal/lsp/testdata/lsp/primarymod/workspacesymbol/fuzzy/symbolb.golden b/internal/lsp/testdata/lsp/primarymod/workspacesymbol/fuzzy/symbolb.golden
deleted file mode 100644
index aa2601b..0000000
--- a/internal/lsp/testdata/lsp/primarymod/workspacesymbol/fuzzy/symbolb.golden
+++ /dev/null
@@ -1,5 +0,0 @@
--- workspace_symbol --
-workspacesymbol/a/a.go:3:5-29 WorkspaceSymbolVariableA Variable
-workspacesymbol/a/a.go:8:2-27 workspacesymbolinvariable Constant
-workspacesymbol/b/b.go:3:5-29 WorkspaceSymbolVariableB Variable
-workspacesymbol/b/b.go:5:6-28 WorkspaceSymbolStructB Struct
diff --git a/internal/lsp/testdata/lsp/primarymod/workspacesymbol/fuzzy/wsym.golden b/internal/lsp/testdata/lsp/primarymod/workspacesymbol/fuzzy/wsym.golden
deleted file mode 100644
index b0b69c5..0000000
--- a/internal/lsp/testdata/lsp/primarymod/workspacesymbol/fuzzy/wsym.golden
+++ /dev/null
@@ -1,6 +0,0 @@
--- workspace_symbol --
-workspacesymbol/a/a.go:3:5-29 WorkspaceSymbolVariableA Variable
-workspacesymbol/a/a.go:5:7-31 WorkspaceSymbolConstantA Constant
-workspacesymbol/a/a.go:8:2-27 workspacesymbolinvariable Constant
-workspacesymbol/b/b.go:3:5-29 WorkspaceSymbolVariableB Variable
-workspacesymbol/b/b.go:5:6-28 WorkspaceSymbolStructB Struct
diff --git a/internal/lsp/testdata/lsp/primarymod/workspacesymbol/randomgophervar.golden b/internal/lsp/testdata/lsp/primarymod/workspacesymbol/randomgophervar.golden
new file mode 100644
index 0000000..02e9954
--- /dev/null
+++ b/internal/lsp/testdata/lsp/primarymod/workspacesymbol/randomgophervar.golden
@@ -0,0 +1,3 @@
+-- workspace_symbol --
+workspacesymbol/a/a.go:3:5-26 RandomGopherVariableA Variable
+workspacesymbol/b/b.go:3:5-26 RandomGopherVariableB Variable
diff --git a/internal/lsp/testdata/lsp/primarymod/workspacesymbol/workspacesymbol.go b/internal/lsp/testdata/lsp/primarymod/workspacesymbol/workspacesymbol.go
index 7ab2290..bffb1dd 100644
--- a/internal/lsp/testdata/lsp/primarymod/workspacesymbol/workspacesymbol.go
+++ b/internal/lsp/testdata/lsp/primarymod/workspacesymbol/workspacesymbol.go
@@ -2,8 +2,8 @@
 
 /*@
 workspacesymbol("") // The result is 0 symbols due to the limit(golang.org/cl/220939).
-workspacesymbol("workspacesymbolvar",
-	WorkspaceSymbolVariableA,
-	WorkspaceSymbolVariableB,
+workspacesymbol("randomgophervar",
+	RandomGopherVariableA,
+	RandomGopherVariableB,
 )
 */
diff --git a/internal/lsp/testdata/lsp/primarymod/workspacesymbol/workspacesymbolvar.golden b/internal/lsp/testdata/lsp/primarymod/workspacesymbol/workspacesymbolvar.golden
deleted file mode 100644
index 8650fad..0000000
--- a/internal/lsp/testdata/lsp/primarymod/workspacesymbol/workspacesymbolvar.golden
+++ /dev/null
@@ -1,3 +0,0 @@
--- workspace_symbol --
-workspacesymbol/a/a.go:3:5-29 WorkspaceSymbolVariableA Variable
-workspacesymbol/b/b.go:3:5-29 WorkspaceSymbolVariableB Variable
diff --git a/internal/lsp/testdata/lsp/summary.txt.golden b/internal/lsp/testdata/lsp/summary.txt.golden
index 5296bd9..9c3986d4 100644
--- a/internal/lsp/testdata/lsp/summary.txt.golden
+++ b/internal/lsp/testdata/lsp/summary.txt.golden
@@ -1,22 +1,22 @@
 -- summary --
-CodeLensCount = 2
-CompletionsCount = 240
-CompletionSnippetCount = 76
+CodeLensCount = 4
+CompletionsCount = 244
+CompletionSnippetCount = 80
 UnimportedCompletionsCount = 6
 DeepCompletionsCount = 5
 FuzzyCompletionsCount = 8
 RankedCompletionsCount = 120
 CaseSensitiveCompletionsCount = 4
-DiagnosticsCount = 43
+DiagnosticsCount = 44
 FoldingRangesCount = 2
 FormatCount = 6
 ImportCount = 8
-SuggestedFixCount = 6
-DefinitionsCount = 50
+SuggestedFixCount = 13
+DefinitionsCount = 53
 TypeDefinitionsCount = 2
-HighlightsCount = 52
+HighlightsCount = 69
 ReferencesCount = 11
-RenamesCount = 24
+RenamesCount = 25
 PrepareRenamesCount = 7
 SymbolsCount = 3
 WorkspaceSymbolsCount = 2
diff --git a/internal/lsp/testdata/upgradedep/primarymod/go.mod b/internal/lsp/testdata/upgradedep/primarymod/go.mod
index 7aacfb2..638cb09 100644
--- a/internal/lsp/testdata/upgradedep/primarymod/go.mod
+++ b/internal/lsp/testdata/upgradedep/primarymod/go.mod
@@ -1,4 +1,4 @@
-module upgradedep //@codelens("module upgradedep", "Upgrade all dependencies", "upgrade.dependency")
+module upgradedep //@codelens("module upgradedep", "Upgrade all dependencies", "upgrade_dependency")
 
 // TODO(microsoft/vscode-go#12): Another issue. //@link(`microsoft/vscode-go#12`, `https://github.com/microsoft/vscode-go/issues/12`)
 
@@ -6,6 +6,6 @@
 
 // TODO(golang/go#1234): Link the relevant issue. //@link(`golang/go#1234`, `https://github.com/golang/go/issues/1234`)
 
-require example.com/extramodule v1.0.0 //@link(`example.com/extramodule`, `https://pkg.go.dev/mod/example.com/extramodule@v1.0.0`),codelens("require example.com/extramodule v1.0.0", "Upgrade dependency to v1.1.0", "upgrade.dependency")
+require example.com/extramodule v1.0.0 //@link(`example.com/extramodule`, `https://pkg.go.dev/mod/example.com/extramodule@v1.0.0`),codelens("require example.com/extramodule v1.0.0", "Upgrade dependency to v1.1.0", "upgrade_dependency")
 
 // https://example.com/comment: Another issue. //@link(`https://example.com/comment`,`https://example.com/comment`)
diff --git a/internal/lsp/tests/tests.go b/internal/lsp/tests/tests.go
index 3af5292..850ec22 100644
--- a/internal/lsp/tests/tests.go
+++ b/internal/lsp/tests/tests.go
@@ -16,7 +16,6 @@
 	"os"
 	"path/filepath"
 	"regexp"
-	"runtime"
 	"sort"
 	"strconv"
 	"strings"
@@ -30,6 +29,7 @@
 	"golang.org/x/tools/internal/lsp/protocol"
 	"golang.org/x/tools/internal/lsp/source"
 	"golang.org/x/tools/internal/span"
+	"golang.org/x/tools/internal/testenv"
 	"golang.org/x/tools/txtar"
 )
 
@@ -216,12 +216,15 @@
 		source.Go: {
 			protocol.SourceOrganizeImports: true,
 			protocol.QuickFix:              true,
+			protocol.RefactorRewrite:       true,
+			protocol.SourceFixAll:          true,
 		},
 		source.Mod: {
 			protocol.SourceOrganizeImports: true,
 		},
 		source.Sum: {},
 	}
+	o.UserOptions.EnabledCodeLens[source.CommandTest] = true
 	o.HoverKind = source.SynopsisDocumentation
 	o.InsertTextFormat = protocol.SnippetTextFormat
 	o.CompletionBudget = time.Minute
@@ -229,7 +232,9 @@
 	return o
 }
 
-var haveCgo = false
+var (
+	go115 = false
+)
 
 // Load creates the folder structure required when testing with modules.
 // The directory structure of a test needs to look like the example below:
@@ -449,8 +454,11 @@
 			for i, e := range exp {
 				t.Run(SpanName(src)+"_"+strconv.Itoa(i), func(t *testing.T) {
 					t.Helper()
-					if (!haveCgo || runtime.GOOS == "android") && strings.Contains(t.Name(), "cgo") {
-						t.Skip("test requires cgo, not supported")
+					if strings.Contains(t.Name(), "cgo") {
+						testenv.NeedsTool(t, "cgo")
+					}
+					if !go115 && strings.Contains(t.Name(), "declarecgo") {
+						t.Skip("test requires Go 1.15")
 					}
 					test(t, src, e, data.CompletionItems)
 				})
@@ -607,8 +615,11 @@
 		for spn, d := range data.Definitions {
 			t.Run(SpanName(spn), func(t *testing.T) {
 				t.Helper()
-				if (!haveCgo || runtime.GOOS == "android") && strings.Contains(t.Name(), "cgo") {
-					t.Skip("test requires cgo, not supported")
+				if strings.Contains(t.Name(), "cgo") {
+					testenv.NeedsTool(t, "cgo")
+				}
+				if !go115 && strings.Contains(t.Name(), "declarecgo") {
+					t.Skip("test requires Go 1.15")
 				}
 				tests.Definition(t, spn, d)
 			})
@@ -808,7 +819,7 @@
 	}))
 	got := buf.String()
 	if want != got {
-		t.Errorf("test summary does not match, want\n%s\ngot:\n%s", want, got)
+		t.Errorf("test summary does not match: %v", Diff(want, got))
 	}
 }
 
@@ -877,6 +888,7 @@
 		}
 		file.Data = append(contents, '\n') // add trailing \n for txtar
 		golden.Modified = true
+
 	}
 	if file == nil {
 		data.t.Fatalf("could not find golden contents %v: %v", fragment, tag)
diff --git a/internal/lsp/tests/tests_cgo.go b/internal/lsp/tests/tests_cgo.go
deleted file mode 100644
index 0f823a5..0000000
--- a/internal/lsp/tests/tests_cgo.go
+++ /dev/null
@@ -1,7 +0,0 @@
-// +build cgo
-
-package tests
-
-func init() {
-	haveCgo = true
-}
diff --git a/internal/lsp/tests/tests_go115.go b/internal/lsp/tests/tests_go115.go
new file mode 100644
index 0000000..6c5f9bb
--- /dev/null
+++ b/internal/lsp/tests/tests_go115.go
@@ -0,0 +1,7 @@
+// +build go1.15
+
+package tests
+
+func init() {
+	go115 = true
+}
diff --git a/internal/lsp/text_synchronization.go b/internal/lsp/text_synchronization.go
index bb79cbd..52bc46f 100644
--- a/internal/lsp/text_synchronization.go
+++ b/internal/lsp/text_synchronization.go
@@ -8,6 +8,7 @@
 	"bytes"
 	"context"
 	"fmt"
+	"sync"
 
 	"golang.org/x/tools/internal/jsonrpc2"
 	"golang.org/x/tools/internal/lsp/protocol"
@@ -30,6 +31,7 @@
 	FromDidSave
 	// FromDidClose is a file modification caused by closing a file.
 	FromDidClose
+	FromRegenerateCgo
 )
 
 func (m ModificationSource) String() string {
@@ -42,6 +44,8 @@
 		return "files changed on disk"
 	case FromDidSave:
 		return "saved files"
+	case FromRegenerateCgo:
+		return "regenerate cgo"
 	default:
 		return "unknown file modification"
 	}
@@ -131,7 +135,7 @@
 	if err != nil {
 		return err
 	}
-	// Clear the diagnostics for any deleted files.
+	// Clear the diagnostics for any deleted files that are not open in the editor.
 	for uri := range deletions {
 		if snapshot := snapshots[uri]; snapshot == nil || snapshot.IsOpen(uri) {
 			continue
@@ -184,12 +188,12 @@
 	if snapshot == nil {
 		return errors.Errorf("no snapshot for %s", uri)
 	}
-	fh, err := snapshot.GetFile(uri)
+	fh, err := snapshot.GetFile(ctx, uri)
 	if err != nil {
 		return err
 	}
 	// If a file has been closed and is not on disk, clear its diagnostics.
-	if _, _, err := fh.Read(ctx); err != nil {
+	if _, err := fh.Read(); err != nil {
 		return s.client.PublishDiagnostics(ctx, &protocol.PublishDiagnosticsParams{
 			URI:         protocol.URIFromSpanURI(uri),
 			Diagnostics: []protocol.Diagnostic{},
@@ -200,6 +204,18 @@
 }
 
 func (s *Server) didModifyFiles(ctx context.Context, modifications []source.FileModification, cause ModificationSource) (map[span.URI]source.Snapshot, error) {
+	// diagnosticWG tracks outstanding diagnostic work as a result of this file
+	// modification.
+	var diagnosticWG sync.WaitGroup
+	if s.session.Options().VerboseWorkDoneProgress {
+		work := s.StartWork(ctx, DiagnosticWorkTitle(cause), "Calculating file diagnostics...", nil)
+		defer func() {
+			go func() {
+				diagnosticWG.Wait()
+				work.End(ctx, "Done.")
+			}()
+		}()
+	}
 	snapshots, err := s.session.DidModifyFiles(ctx, modifications)
 	if err != nil {
 		return nil, err
@@ -237,17 +253,17 @@
 		// If a modification comes in for the view's go.mod file and the view
 		// was never properly initialized, or the view does not have
 		// a go.mod file, try to recreate the associated view.
-		if modfile, _ := snapshot.View().ModFiles(); modfile == "" {
+		if modfile := snapshot.View().ModFile(); modfile == "" {
 			for _, uri := range uris {
 				// Don't rebuild the view until the go.mod is on disk.
 				if !snapshot.IsSaved(uri) {
 					continue
 				}
-				fh, err := snapshot.GetFile(uri)
+				fh, err := snapshot.GetFile(ctx, uri)
 				if err != nil {
 					return nil, err
 				}
-				switch fh.Identity().Kind {
+				switch fh.Kind() {
 				case source.Mod:
 					newSnapshot, err := snapshot.View().Rebuild(ctx)
 					if err != nil {
@@ -259,11 +275,9 @@
 				}
 			}
 		}
+		diagnosticWG.Add(1)
 		go func(snapshot source.Snapshot) {
-			if s.session.Options().VerboseWorkDoneProgress {
-				work := s.StartWork(ctx, DiagnosticWorkTitle(cause), "Calculating file diagnostics...", nil)
-				defer work.End(ctx, "Done.")
-			}
+			defer diagnosticWG.Done()
 			s.diagnoseSnapshot(snapshot)
 		}(snapshot)
 	}
@@ -298,7 +312,11 @@
 }
 
 func (s *Server) applyIncrementalChanges(ctx context.Context, uri span.URI, changes []protocol.TextDocumentContentChangeEvent) ([]byte, error) {
-	content, _, err := s.session.GetFile(uri).Read(ctx)
+	fh, err := s.session.GetFile(ctx, uri)
+	if err != nil {
+		return nil, err
+	}
+	content, err := fh.Read()
 	if err != nil {
 		return nil, fmt.Errorf("%w: file not found (%v)", jsonrpc2.ErrInternal, err)
 	}
diff --git a/internal/memoize/memoize.go b/internal/memoize/memoize.go
index b05a216..30f4c0c 100644
--- a/internal/memoize/memoize.go
+++ b/internal/memoize/memoize.go
@@ -164,6 +164,27 @@
 	return result
 }
 
+// DebugOnlyIterate iterates through all live cache entries and calls f on them.
+// It should only be used for debugging purposes.
+func (s *Store) DebugOnlyIterate(f func(k, v interface{})) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+
+	for k, e := range s.entries {
+		h := (*Handle)(unsafe.Pointer(e))
+		var v interface{}
+		h.mu.Lock()
+		if h.state == stateCompleted {
+			v = h.value
+		}
+		h.mu.Unlock()
+		if v == nil {
+			continue
+		}
+		f(k, v)
+	}
+}
+
 // Cached returns the value associated with a handle.
 //
 // It will never cause the value to be generated.
diff --git a/internal/packagesinternal/packages.go b/internal/packagesinternal/packages.go
index a88750b..2c4527f 100644
--- a/internal/packagesinternal/packages.go
+++ b/internal/packagesinternal/packages.go
@@ -2,34 +2,13 @@
 package packagesinternal
 
 import (
-	"time"
-
 	"golang.org/x/tools/internal/gocommand"
 )
 
-// Fields must match go list;
-type Module struct {
-	Path      string       // module path
-	Version   string       // module version
-	Versions  []string     // available module versions (with -versions)
-	Replace   *Module      // replaced by this module
-	Time      *time.Time   // time version was created
-	Update    *Module      // available update, if any (with -u)
-	Main      bool         // is this the main module?
-	Indirect  bool         // is this module only an indirect dependency of main module?
-	Dir       string       // directory holding files for this module, if any
-	GoMod     string       // path to go.mod file used when loading this module, if any
-	GoVersion string       // go version used in module
-	Error     *ModuleError // error loading module
-}
-type ModuleError struct {
-	Err string // the error itself
-}
-
 var GetForTest = func(p interface{}) string { return "" }
 
-var GetModule = func(p interface{}) *Module { return nil }
-
 var GetGoCmdRunner = func(config interface{}) *gocommand.Runner { return nil }
 
 var SetGoCmdRunner = func(config interface{}, runner *gocommand.Runner) {}
+
+var TypecheckCgo int
diff --git a/internal/proxydir/proxydir.go b/internal/proxydir/proxydir.go
index 52e5b58..5180204 100644
--- a/internal/proxydir/proxydir.go
+++ b/internal/proxydir/proxydir.go
@@ -14,6 +14,9 @@
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"strings"
+
+	"golang.org/x/tools/internal/testenv"
 )
 
 // WriteModuleVersion creates a directory in the proxy dir for a module.
@@ -77,3 +80,21 @@
 		*err = fmt.Errorf("closing %s: %v", name, cerr)
 	}
 }
+
+// ToURL returns the file uri for a proxy directory.
+func ToURL(dir string) string {
+	if testenv.Go1Point() >= 13 {
+		// file URLs on Windows must start with file:///. See golang.org/issue/6027.
+		path := filepath.ToSlash(dir)
+		if !strings.HasPrefix(path, "/") {
+			path = "/" + path
+		}
+		return "file://" + path
+	} else {
+		// Prior to go1.13, the Go command on Windows only accepted GOPROXY file URLs
+		// of the form file://C:/path/to/proxy. This was incorrect: when parsed, "C:"
+		// is interpreted as the host. See golang.org/issue/6027. This has been
+		// fixed in go1.13, but we emit the old format for old releases.
+		return "file://" + filepath.ToSlash(dir)
+	}
+}
diff --git a/internal/proxydir/proxydir_112.go b/internal/proxydir/proxydir_112.go
deleted file mode 100644
index c87fe1f..0000000
--- a/internal/proxydir/proxydir_112.go
+++ /dev/null
@@ -1,18 +0,0 @@
-// +build !go1.13
-
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package proxydir
-
-import "path/filepath"
-
-// ToURL returns the file uri for a proxy directory.
-func ToURL(dir string) string {
-	// Prior to go1.13, the Go command on Windows only accepted GOPROXY file URLs
-	// of the form file://C:/path/to/proxy. This was incorrect: when parsed, "C:"
-	// is interpreted as the host. See golang.org/issue/6027. This has been
-	// fixed in go1.13, but we emit the old format for old releases.
-	return "file://" + filepath.ToSlash(dir)
-}
diff --git a/internal/proxydir/proxydir_113.go b/internal/proxydir/proxydir_113.go
deleted file mode 100644
index ec9925d..0000000
--- a/internal/proxydir/proxydir_113.go
+++ /dev/null
@@ -1,22 +0,0 @@
-// +build go1.13
-
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package proxydir
-
-import (
-	"path/filepath"
-	"strings"
-)
-
-// ToURL returns the file uri for a proxy directory.
-func ToURL(dir string) string {
-	// file URLs on Windows must start with file:///. See golang.org/issue/6027.
-	path := filepath.ToSlash(dir)
-	if !strings.HasPrefix(path, "/") {
-		path = "/" + path
-	}
-	return "file://" + path
-}
diff --git a/internal/stack/gostacks/gostacks.go b/internal/stack/gostacks/gostacks.go
new file mode 100644
index 0000000..699471f
--- /dev/null
+++ b/internal/stack/gostacks/gostacks.go
@@ -0,0 +1,23 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// The gostacks command processes stdin looking for things that look like
+// stack traces and simplifying them to make the log more readable.
+// It collates stack traces that have the same path as well as simplifying the
+// individual lines of the trace.
+// The processed log is printed to stdout.
+package main
+
+import (
+	"fmt"
+	"os"
+
+	"golang.org/x/tools/internal/stack"
+)
+
+func main() {
+	if err := stack.Process(os.Stdout, os.Stdin); err != nil {
+		fmt.Fprintln(os.Stderr, err)
+	}
+}
diff --git a/internal/stack/parse.go b/internal/stack/parse.go
new file mode 100644
index 0000000..e01da8f
--- /dev/null
+++ b/internal/stack/parse.go
@@ -0,0 +1,175 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package stack
+
+import (
+	"bufio"
+	"errors"
+	"io"
+	"regexp"
+	"strconv"
+)
+
+var (
+	reBlank     = regexp.MustCompile(`^\s*$`)
+	reGoroutine = regexp.MustCompile(`^\s*goroutine (\d+) \[([^\]]*)\]:\s*$`)
+	reCall      = regexp.MustCompile(`^\s*` +
+		`(created by )?` + //marker
+		`(([\w/.]+/)?[\w]+)\.` + //package
+		`(\(([^:.)]*)\)\.)?` + //optional type
+		`([\w\.]+)` + //function
+		`(\(.*\))?` + // args
+		`\s*$`)
+	rePos = regexp.MustCompile(`^\s*(.*):(\d+)( .*)?$`)
+
+	errBreakParse = errors.New("break parse")
+)
+
+// Scanner splits an input stream into lines in a way that is consumable by
+// the parser.
+type Scanner struct {
+	lines *bufio.Scanner
+	done  bool
+}
+
+// NewScanner creates a scanner on top of a reader.
+func NewScanner(r io.Reader) *Scanner {
+	s := &Scanner{
+		lines: bufio.NewScanner(r),
+	}
+	s.Skip() // prefill
+	return s
+}
+
+// Peek returns the next line without consuming it.
+func (s *Scanner) Peek() string {
+	if s.done {
+		return ""
+	}
+	return s.lines.Text()
+}
+
+// Skip consumes the next line without looking at it.
+// Normally used after it has already been looked at using Peek.
+func (s *Scanner) Skip() {
+	if !s.lines.Scan() {
+		s.done = true
+	}
+}
+
+// Next consumes and returns the next line.
+func (s *Scanner) Next() string {
+	line := s.Peek()
+	s.Skip()
+	return line
+}
+
+// Done returns true if the scanner has reached the end of the underlying
+// stream.
+func (s *Scanner) Done() bool {
+	return s.done
+}
+
+// Err returns true if the scanner has reached the end of the underlying
+// stream.
+func (s *Scanner) Err() error {
+	return s.lines.Err()
+}
+
+// Match returns the submatchs of the regular expression against the next line.
+// If it matched the line is also consumed.
+func (s *Scanner) Match(re *regexp.Regexp) []string {
+	if s.done {
+		return nil
+	}
+	match := re.FindStringSubmatch(s.Peek())
+	if match != nil {
+		s.Skip()
+	}
+	return match
+}
+
+// SkipBlank skips any number of pure whitespace lines.
+func (s *Scanner) SkipBlank() {
+	for !s.done {
+		line := s.Peek()
+		if len(line) != 0 && !reBlank.MatchString(line) {
+			return
+		}
+		s.Skip()
+	}
+}
+
+// Parse the current contiguous block of goroutine stack traces until the
+// scanned content no longer matches.
+func Parse(scanner *Scanner) (Dump, error) {
+	dump := Dump{}
+	for {
+		gr, ok := parseGoroutine(scanner)
+		if !ok {
+			return dump, nil
+		}
+		dump = append(dump, gr)
+	}
+}
+
+func parseGoroutine(scanner *Scanner) (Goroutine, bool) {
+	match := scanner.Match(reGoroutine)
+	if match == nil {
+		return Goroutine{}, false
+	}
+	id, _ := strconv.ParseInt(match[1], 0, 32)
+	gr := Goroutine{
+		ID:    int(id),
+		State: match[2],
+	}
+	for {
+		frame, ok := parseFrame(scanner)
+		if !ok {
+			scanner.SkipBlank()
+			return gr, true
+		}
+		if frame.Position.Filename != "" {
+			gr.Stack = append(gr.Stack, frame)
+		}
+	}
+}
+
+func parseFrame(scanner *Scanner) (Frame, bool) {
+	fun, ok := parseFunction(scanner)
+	if !ok {
+		return Frame{}, false
+	}
+	frame := Frame{
+		Function: fun,
+	}
+	frame.Position, ok = parsePosition(scanner)
+	// if ok is false, then this is a broken state.
+	// we got the func but not the file that must follow
+	// the consumed line can be recovered from the frame
+	//TODO: push back the fun raw
+	return frame, ok
+}
+
+func parseFunction(scanner *Scanner) (Function, bool) {
+	match := scanner.Match(reCall)
+	if match == nil {
+		return Function{}, false
+	}
+	return Function{
+		Package: match[2],
+		Type:    match[5],
+		Name:    match[6],
+	}, true
+}
+
+func parsePosition(scanner *Scanner) (Position, bool) {
+	match := scanner.Match(rePos)
+	if match == nil {
+		return Position{}, false
+	}
+	line, _ := strconv.ParseInt(match[2], 0, 32)
+	return Position{Filename: match[1], Line: int(line)}, true
+}
diff --git a/internal/stack/process.go b/internal/stack/process.go
new file mode 100644
index 0000000..ac19366
--- /dev/null
+++ b/internal/stack/process.go
@@ -0,0 +1,112 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package stack
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"runtime"
+	"sort"
+)
+
+// Capture get the current stack traces from the runtime.
+func Capture() Dump {
+	buf := make([]byte, 2<<20)
+	buf = buf[:runtime.Stack(buf, true)]
+	scanner := NewScanner(bytes.NewReader(buf))
+	dump, _ := Parse(scanner)
+	return dump
+}
+
+// Summarize a dump for easier consumption.
+// This collates goroutines with equivalent stacks.
+func Summarize(dump Dump) Summary {
+	s := Summary{
+		Total: len(dump),
+	}
+	for _, gr := range dump {
+		s.addGoroutine(gr)
+	}
+	return s
+}
+
+// Process and input stream to an output stream, summarizing any stacks that
+// are detected in place.
+func Process(out io.Writer, in io.Reader) error {
+	scanner := NewScanner(in)
+	for {
+		dump, err := Parse(scanner)
+		summary := Summarize(dump)
+		switch {
+		case len(dump) > 0:
+			fmt.Fprintf(out, "%+v\n\n", summary)
+		case err != nil:
+			return err
+		case scanner.Done():
+			return scanner.Err()
+		default:
+			// must have been a line that is not part of a dump
+			fmt.Fprintln(out, scanner.Next())
+		}
+	}
+}
+
+// Diff calculates the delta between two dumps.
+func Diff(before, after Dump) Delta {
+	result := Delta{}
+	processed := make(map[int]bool)
+	for _, gr := range before {
+		processed[gr.ID] = false
+	}
+	for _, gr := range after {
+		if _, found := processed[gr.ID]; found {
+			result.Shared = append(result.Shared, gr)
+		} else {
+			result.After = append(result.After, gr)
+		}
+		processed[gr.ID] = true
+	}
+	for _, gr := range before {
+		if done := processed[gr.ID]; !done {
+			result.Before = append(result.Before, gr)
+		}
+	}
+	return result
+}
+
+// TODO: do we want to allow contraction of stacks before comparison?
+func (s *Summary) addGoroutine(gr Goroutine) {
+	index := sort.Search(len(s.Calls), func(i int) bool {
+		return !s.Calls[i].Stack.less(gr.Stack)
+	})
+	if index >= len(s.Calls) || !s.Calls[index].Stack.equal(gr.Stack) {
+		// insert new stack, first increase the length
+		s.Calls = append(s.Calls, Call{})
+		// move the top part upward to make space
+		copy(s.Calls[index+1:], s.Calls[index:])
+		// insert the new call
+		s.Calls[index] = Call{
+			Stack: gr.Stack,
+		}
+	}
+	// merge the goroutine into the matched call
+	s.Calls[index].merge(gr)
+}
+
+//TODO: do we want other grouping strategies?
+func (c *Call) merge(gr Goroutine) {
+	for i := range c.Groups {
+		canditate := &c.Groups[i]
+		if canditate.State == gr.State {
+			canditate.Goroutines = append(canditate.Goroutines, gr)
+			return
+		}
+	}
+	c.Groups = append(c.Groups, Group{
+		State:      gr.State,
+		Goroutines: []Goroutine{gr},
+	})
+}
diff --git a/internal/stack/stack.go b/internal/stack/stack.go
new file mode 100644
index 0000000..479301a
--- /dev/null
+++ b/internal/stack/stack.go
@@ -0,0 +1,170 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package stack provides support for parsing standard goroutine stack traces.
+package stack
+
+import (
+	"fmt"
+	"text/tabwriter"
+)
+
+// Dump is a raw set of goroutines and their stacks.
+type Dump []Goroutine
+
+// Goroutine is a single parsed goroutine dump.
+type Goroutine struct {
+	State string // state that the goroutine is in.
+	ID    int    // id of the goroutine.
+	Stack Stack  // call frames that make up the stack
+}
+
+// Stack is a set of frames in a callstack.
+type Stack []Frame
+
+// Frame is a point in a call stack.
+type Frame struct {
+	Function Function
+	Position Position
+}
+
+// Function is the function called at a frame.
+type Function struct {
+	Package string // package name of function if known
+	Type    string // if set function is a method of this type
+	Name    string // function name of the frame
+}
+
+// Position is the file position for a frame.
+type Position struct {
+	Filename string // source filename
+	Line     int    // line number within file
+}
+
+// Summary is a set of stacks processed and collated into Calls.
+type Summary struct {
+	Total int    // the total count of goroutines in the summary
+	Calls []Call // the collated stack traces
+}
+
+// Call is set of goroutines that all share the same callstack.
+// They will be grouped by state.
+type Call struct {
+	Stack  Stack   // the shared callstack information
+	Groups []Group // the sets of goroutines with the same state
+}
+
+// Group is a set of goroutines with the same stack that are in the same state.
+type Group struct {
+	State      string      // the shared state of the goroutines
+	Goroutines []Goroutine // the set of goroutines in this group
+}
+
+// Delta represents the difference between two stack dumps.
+type Delta struct {
+	Before Dump // The goroutines that were only in the before set.
+	Shared Dump // The goroutines that were in both sets.
+	After  Dump // The goroutines that were only in the after set.
+}
+
+func (s Stack) equal(other Stack) bool {
+	if len(s) != len(other) {
+		return false
+	}
+	for i, frame := range s {
+		if !frame.equal(other[i]) {
+			return false
+		}
+	}
+	return true
+}
+
+func (s Stack) less(other Stack) bool {
+	for i, frame := range s {
+		if i >= len(other) {
+			return false
+		}
+		if frame.less(other[i]) {
+			return true
+		}
+		if !frame.equal(other[i]) {
+			return false
+		}
+	}
+	return len(s) < len(other)
+}
+
+func (f Frame) equal(other Frame) bool {
+	return f.Position.equal(other.Position)
+}
+
+func (f Frame) less(other Frame) bool {
+	return f.Position.less(other.Position)
+}
+
+func (p Position) equal(other Position) bool {
+	return p.Filename == other.Filename && p.Line == other.Line
+}
+
+func (p Position) less(other Position) bool {
+	if p.Filename < other.Filename {
+		return true
+	}
+	if p.Filename > other.Filename {
+		return false
+	}
+	return p.Line < other.Line
+}
+
+func (s Summary) Format(w fmt.State, r rune) {
+	tw := tabwriter.NewWriter(w, 0, 0, 1, ' ', 0)
+	for i, c := range s.Calls {
+		if i > 0 {
+			fmt.Fprintf(tw, "\n\n")
+			tw.Flush()
+		}
+		fmt.Fprint(tw, c)
+	}
+	tw.Flush()
+	if s.Total > 0 && w.Flag('+') {
+		fmt.Fprintf(w, "\n\n%d goroutines, %d unique", s.Total, len(s.Calls))
+	}
+}
+
+func (c Call) Format(w fmt.State, r rune) {
+	for i, g := range c.Groups {
+		if i > 0 {
+			fmt.Fprint(w, " ")
+		}
+		fmt.Fprint(w, g)
+	}
+	for _, f := range c.Stack {
+		fmt.Fprintf(w, "\n%v", f)
+	}
+}
+
+func (g Group) Format(w fmt.State, r rune) {
+	fmt.Fprintf(w, "[%v]: ", g.State)
+	for i, gr := range g.Goroutines {
+		if i > 0 {
+			fmt.Fprint(w, ", ")
+		}
+		fmt.Fprintf(w, "$%d", gr.ID)
+	}
+}
+
+func (f Frame) Format(w fmt.State, c rune) {
+	fmt.Fprintf(w, "%v:\t%v", f.Position, f.Function)
+}
+
+func (f Function) Format(w fmt.State, c rune) {
+	if f.Type != "" {
+		fmt.Fprintf(w, "(%v).", f.Type)
+	}
+	fmt.Fprintf(w, "%v", f.Name)
+}
+
+func (p Position) Format(w fmt.State, c rune) {
+	fmt.Fprintf(w, "%v:%v", p.Filename, p.Line)
+}
diff --git a/internal/stack/stack_test.go b/internal/stack/stack_test.go
new file mode 100644
index 0000000..371492a
--- /dev/null
+++ b/internal/stack/stack_test.go
@@ -0,0 +1,193 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package stack_test
+
+import (
+	"bytes"
+	"strings"
+	"testing"
+
+	"golang.org/x/tools/internal/stack"
+)
+
+func TestProcess(t *testing.T) {
+	for _, test := range []struct{ name, input, expect string }{{
+		name:   `empty`,
+		input:  ``,
+		expect: ``,
+	}, {
+		name:  `no_frame`,
+		input: `goroutine 1 [running]:`,
+		expect: `
+[running]: $1
+
+1 goroutines, 1 unique
+`,
+	}, {
+		name: `one_frame`,
+		input: `
+goroutine 1 [running]:
+package.function(args)
+	file.go:10
+`,
+		expect: `
+[running]: $1
+file.go:10: function
+
+1 goroutines, 1 unique
+`,
+	}, {
+		name: `one_call`,
+		input: `
+goroutine 1 [running]:
+package1.functionA(args)
+	file1.go:10
+package2.functionB(args)
+	file2.go:20
+package3.functionC(args)
+	file3.go:30
+`,
+		expect: `
+[running]: $1
+file1.go:10: functionA
+file2.go:20: functionB
+file3.go:30: functionC
+
+1 goroutines, 1 unique
+`,
+	}, {
+		name: `two_call`,
+		input: `
+goroutine 1 [running]:
+package1.functionA(args)
+	file1.go:10
+goroutine 2 [running]:
+package2.functionB(args)
+	file2.go:20
+`,
+		expect: `
+[running]: $1
+file1.go:10: functionA
+
+[running]: $2
+file2.go:20: functionB
+
+2 goroutines, 2 unique
+`,
+	}, {
+		name: `merge_call`,
+		input: `
+goroutine 1 [running]:
+package1.functionA(args)
+	file1.go:10
+goroutine 2 [running]:
+package1.functionA(args)
+	file1.go:10
+`,
+		expect: `
+[running]: $1, $2
+file1.go:10: functionA
+
+2 goroutines, 1 unique
+`,
+	}, {
+		name: `alternating_call`,
+		input: `
+goroutine 1 [running]:
+package1.functionA(args)
+	file1.go:10
+goroutine 2 [running]:
+package2.functionB(args)
+	file2.go:20
+goroutine 3 [running]:
+package1.functionA(args)
+	file1.go:10
+goroutine 4 [running]:
+package2.functionB(args)
+	file2.go:20
+goroutine 5 [running]:
+package1.functionA(args)
+	file1.go:10
+goroutine 6 [running]:
+package2.functionB(args)
+	file2.go:20
+`,
+		expect: `
+[running]: $1, $3, $5
+file1.go:10: functionA
+
+[running]: $2, $4, $6
+file2.go:20: functionB
+
+6 goroutines, 2 unique
+`,
+	}, {
+		name: `sort_calls`,
+		input: `
+goroutine 1 [running]:
+package3.functionC(args)
+	file3.go:30
+goroutine 2 [running]:
+package2.functionB(args)
+	file2.go:20
+goroutine 3 [running]:
+package1.functionA(args)
+	file1.go:10
+`,
+		expect: `
+[running]: $3
+file1.go:10: functionA
+
+[running]: $2
+file2.go:20: functionB
+
+[running]: $1
+file3.go:30: functionC
+
+3 goroutines, 3 unique
+`,
+	}, {
+		name: `real_single`,
+		input: `
+panic: oops
+
+goroutine 53 [running]:
+golang.org/x/tools/internal/jsonrpc2_test.testHandler.func1(0x1240c20, 0xc000013350, 0xc0000133b0, 0x1240ca0, 0xc00002ab00, 0x3, 0x3)
+	/work/tools/internal/jsonrpc2/jsonrpc2_test.go:160 +0x74c
+golang.org/x/tools/internal/jsonrpc2.(*Conn).Run(0xc000204330, 0x1240c20, 0xc000204270, 0x1209570, 0xc000212120, 0x1242700)
+	/work/tools/internal/jsonrpc2/jsonrpc2.go:187 +0x777
+golang.org/x/tools/internal/jsonrpc2_test.run.func1(0x123ebe0, 0xc000206018, 0x123ec20, 0xc000206010, 0xc0002080a0, 0xc000204330, 0x1240c20, 0xc000204270, 0xc000212120)
+	/work/tools/internal/jsonrpc2/jsonrpc2_test.go:131 +0xe2
+created by golang.org/x/tools/internal/jsonrpc2_test.run
+	/work/tools/internal/jsonrpc2/jsonrpc2_test.go:121 +0x263
+FAIL    golang.org/x/tools/internal/jsonrpc2    0.252s
+FAIL
+`,
+		expect: `
+panic: oops
+
+[running]: $53
+/work/tools/internal/jsonrpc2/jsonrpc2_test.go:160: testHandler.func1
+/work/tools/internal/jsonrpc2/jsonrpc2.go:187:      (*Conn).Run
+/work/tools/internal/jsonrpc2/jsonrpc2_test.go:131: run.func1
+/work/tools/internal/jsonrpc2/jsonrpc2_test.go:121: run
+
+1 goroutines, 1 unique
+
+FAIL    golang.org/x/tools/internal/jsonrpc2    0.252s
+FAIL
+`,
+	}} {
+		t.Run(test.name, func(t *testing.T) {
+			buf := &bytes.Buffer{}
+			stack.Process(buf, strings.NewReader(test.input))
+			expect := strings.TrimSpace(test.expect)
+			got := strings.TrimSpace(buf.String())
+			if got != expect {
+				t.Errorf("got:\n%s\nexpect:\n%s", got, expect)
+			}
+		})
+	}
+}
diff --git a/internal/stack/stacktest/stacktest.go b/internal/stack/stacktest/stacktest.go
new file mode 100644
index 0000000..e23f03e
--- /dev/null
+++ b/internal/stack/stacktest/stacktest.go
@@ -0,0 +1,50 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package stacktest
+
+import (
+	"testing"
+	"time"
+
+	"golang.org/x/tools/internal/stack"
+)
+
+//this is only needed to support pre 1.14 when testing.TB did not have Cleanup
+type withCleanup interface {
+	Cleanup(func())
+}
+
+// the maximum amount of time to wait for goroutines to clean themselves up.
+const maxWait = time.Second
+
+// NoLeak checks that a test (or benchmark) does not leak any goroutines.
+func NoLeak(t testing.TB) {
+	c, ok := t.(withCleanup)
+	if !ok {
+		return
+	}
+	before := stack.Capture()
+	c.Cleanup(func() {
+		var delta stack.Delta
+		start := time.Now()
+		delay := time.Millisecond
+		for {
+			after := stack.Capture()
+			delta = stack.Diff(before, after)
+			if len(delta.After) == 0 {
+				// no leaks
+				return
+			}
+			if time.Since(start) > maxWait {
+				break
+			}
+			time.Sleep(delay)
+			delay *= 2
+		}
+		// it's been long enough, and leaks are still present
+		summary := stack.Summarize(delta.After)
+		t.Errorf("goroutine leak detected:\n%+v", summary)
+	})
+}
diff --git a/internal/testenv/testenv.go b/internal/testenv/testenv.go
index 6c5fb98..f725b95 100644
--- a/internal/testenv/testenv.go
+++ b/internal/testenv/testenv.go
@@ -9,6 +9,7 @@
 import (
 	"bytes"
 	"fmt"
+	"go/build"
 	"io/ioutil"
 	"os"
 	"os/exec"
@@ -40,6 +41,17 @@
 }
 
 func hasTool(tool string) error {
+	if tool == "cgo" {
+		enabled, err := cgoEnabled(false)
+		if err != nil {
+			return fmt.Errorf("checking cgo: %v", err)
+		}
+		if !enabled {
+			return fmt.Errorf("cgo not enabled")
+		}
+		return nil
+	}
+
 	_, err := exec.LookPath(tool)
 	if err != nil {
 		return err
@@ -94,6 +106,19 @@
 	return nil
 }
 
+func cgoEnabled(bypassEnvironment bool) (bool, error) {
+	cmd := exec.Command("go", "env", "CGO_ENABLED")
+	if bypassEnvironment {
+		cmd.Env = append(append([]string(nil), os.Environ()...), "CGO_ENABLED=")
+	}
+	out, err := cmd.CombinedOutput()
+	if err != nil {
+		return false, err
+	}
+	enabled := strings.TrimSpace(string(out))
+	return enabled == "1", nil
+}
+
 func allowMissingTool(tool string) bool {
 	if runtime.GOOS == "android" {
 		// Android builds generally run tests on a separate machine from the build,
@@ -102,6 +127,15 @@
 	}
 
 	switch tool {
+	case "cgo":
+		if strings.HasSuffix(os.Getenv("GO_BUILDER_NAME"), "-nocgo") {
+			// Explicitly disabled on -nocgo builders.
+			return true
+		}
+		if enabled, err := cgoEnabled(true); err == nil && !enabled {
+			// No platform support.
+			return true
+		}
 	case "go":
 		if os.Getenv("GO_BUILDER_NAME") == "illumos-amd64-joyent" {
 			// Work around a misconfigured builder (see https://golang.org/issue/33950).
@@ -125,6 +159,7 @@
 }
 
 // NeedsTool skips t if the named tool is not present in the path.
+// As a special case, "cgo" means "go" is present and can compile cgo programs.
 func NeedsTool(t Testing, tool string) {
 	if t, ok := t.(helperer); ok {
 		t.Helper()
@@ -185,6 +220,27 @@
 	NeedsGoPackages(t)
 }
 
+// NeedsGoBuild skips t if the current system can't build programs with ``go build''
+// and then run them with os.StartProcess or exec.Command.
+// android, and darwin/arm systems don't have the userspace go build needs to run,
+// and js/wasm doesn't support running subprocesses.
+func NeedsGoBuild(t Testing) {
+	if t, ok := t.(helperer); ok {
+		t.Helper()
+	}
+
+	NeedsTool(t, "go")
+
+	switch runtime.GOOS {
+	case "android", "js":
+		t.Skipf("skipping test: %v can't build and run Go binaries", runtime.GOOS)
+	case "darwin":
+		if strings.HasPrefix(runtime.GOARCH, "arm") {
+			t.Skipf("skipping test: darwin/arm can't build and run Go binaries")
+		}
+	}
+}
+
 // ExitIfSmallMachine emits a helpful diagnostic and calls os.Exit(0) if the
 // current machine is a builder known to have scarce resources.
 //
@@ -199,3 +255,37 @@
 		os.Exit(0)
 	}
 }
+
+// Go1Point returns the x in Go 1.x.
+func Go1Point() int {
+	for i := len(build.Default.ReleaseTags) - 1; i >= 0; i-- {
+		var version int
+		if _, err := fmt.Sscanf(build.Default.ReleaseTags[i], "go1.%d", &version); err != nil {
+			continue
+		}
+		return version
+	}
+	panic("bad release tags")
+}
+
+// NeedsGo1Point skips t if the Go version used to run the test is older than
+// 1.x.
+func NeedsGo1Point(t Testing, x int) {
+	if t, ok := t.(helperer); ok {
+		t.Helper()
+	}
+	if Go1Point() < x {
+		t.Skipf("running Go version %q is version 1.%d, older than required 1.%d", runtime.Version(), Go1Point(), x)
+	}
+}
+
+// SkipAfterGo1Point skips t if the Go version used to run the test is newer than
+// 1.x.
+func SkipAfterGo1Point(t Testing, x int) {
+	if t, ok := t.(helperer); ok {
+		t.Helper()
+	}
+	if Go1Point() > x {
+		t.Skipf("running Go version %q is version 1.%d, newer than maximum 1.%d", runtime.Version(), Go1Point(), x)
+	}
+}
diff --git a/internal/typesinternal/types.go b/internal/typesinternal/types.go
new file mode 100644
index 0000000..a5bb408
--- /dev/null
+++ b/internal/typesinternal/types.go
@@ -0,0 +1,28 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package typesinternal
+
+import (
+	"go/types"
+	"reflect"
+	"unsafe"
+)
+
+func SetUsesCgo(conf *types.Config) bool {
+	v := reflect.ValueOf(conf).Elem()
+
+	f := v.FieldByName("go115UsesCgo")
+	if !f.IsValid() {
+		f = v.FieldByName("UsesCgo")
+		if !f.IsValid() {
+			return false
+		}
+	}
+
+	addr := unsafe.Pointer(f.UnsafeAddr())
+	*(*bool)(addr) = true
+
+	return true
+}