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

11e8f6b85 internal/lsp: refactor codeAction
1523bb47d internal/lsp: fix time.Duration hover name check
bcb2d7b23 internal/lsp: fix bad completion for variadic functions
7ee29554c internal/lsp/cache: refactor and improve go get quick fixes
1e524e26b internal/lsp/source: add the nilness analyzer
d34cf35d9 internal/lsp/source: return nil for foldingRange in case of parse error
c5f5f4bed gopls/doc: clarify how to set remote.listen.timeout
3e1cb9523 internal/lsp/source: correct workspace symbol logic for unpacking receivers
397283d20 internal/lsp/protocol/typescript: fix lint errors in .ts code
d19d8cffc internal/lsp/protocol/typecript: fix type merging
50ca8d007 all: recognize new error from go command when no go.mod is found
2cde57b5a internal/lsp: remove redundant didChange notifications
376db5724 internal/lsp: use pre-existing quick fixes for analysis diagnostics
144d5ced6 internal/lsp: run type error analyzers as part of diagnostics
24439e3c7 internal/lsp/source: eliminate GetTypeCheckDiagnostics
dafbee503 internal/lsp: show human-readable const time.Duration as a comment
9c452d857 internal/lsp/cache: don't rely on related diagnostics if unsupported
2ac05c832 internal/lsp: key GC details off package ID
303d8bbb5 gopls/internal/hooks: compile URL regexp once
94327d32c internal/lsp/cache: show type errors on parse errors in other files
16b2c8703 gopls/internal/regtest: add a failing test for issue 44736
78002535c internal/lsp/cache: split up sourceDiagnostics
47985cf3c internal/lsp/cache: refactor Go file parsing
6422c5c8c internal/lsp/cache: invalidate metadata on magic comment changes
f9c628b18 gopls/internal/regtest: add test for bad embed rules
89a9cb6e0 internal/lsp/cache: parse filenames from go list errors correctly
eb48d3f60 internal/lsp/cache: refactor diagnostic suppression
7a079fcd7 internal/lsp/cache: fix related error processing
f5a4005dd cmd/eg: don't do rewrites within the template file itself
28e7a3b5f cmd/eg: use go/packages
54dc8c5ed playground: remove /share registration, add Proxy
24aca17f6 cmd/guru: adjust describe qualifier function (fix describe test)
0150491f5 x/tools/internal/fastwalk: fixes "interrupted system call" error
b4639ccb8 internal/lsp/protocol: fix vet error in tsprotocol.go
a43f69b1f go/expect: use parser.AllErrors when extracting Notes
f48e60bd8 add comment and check action type neovim goimports
b8d1a33f7 internal/lsp/source: add the unusedwrite analyzer

Change-Id: I3bf12fb1e4a6431ea32e879774dd6c5e525b1ff1
diff --git a/cmd/eg/eg.go b/cmd/eg/eg.go
index a5473ad..6463ac4 100644
--- a/cmd/eg/eg.go
+++ b/cmd/eg/eg.go
@@ -10,16 +10,18 @@
 import (
 	"flag"
 	"fmt"
-	"go/build"
+	"go/ast"
 	"go/format"
 	"go/parser"
 	"go/token"
-	exec "golang.org/x/sys/execabs"
+	"go/types"
+	"io/ioutil"
 	"os"
+	"path/filepath"
 	"strings"
 
-	"golang.org/x/tools/go/buildutil"
-	"golang.org/x/tools/go/loader"
+	exec "golang.org/x/sys/execabs"
+	"golang.org/x/tools/go/packages"
 	"golang.org/x/tools/refactor/eg"
 )
 
@@ -32,13 +34,9 @@
 	verboseFlag    = flag.Bool("v", false, "show verbose matcher diagnostics")
 )
 
-func init() {
-	flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
-}
-
 const usage = `eg: an example-based refactoring tool.
 
-Usage: eg -t template.go [-w] [-transitive] <args>...
+Usage: eg -t template.go [-w] [-transitive] <packages>
 
 -help            show detailed help message
 -t template.go	 specifies the template file (use -help to see explanation)
@@ -47,7 +45,7 @@
 -v               show verbose matcher diagnostics
 -beforeedit cmd  a command to exec before each file is modified.
                  "{}" represents the name of the file.
-` + loader.FromArgsUsage
+`
 
 func main() {
 	if err := doMain(); err != nil {
@@ -74,51 +72,73 @@
 		return fmt.Errorf("no -t template.go file specified")
 	}
 
-	conf := loader.Config{
-		Fset:       token.NewFileSet(),
-		ParserMode: parser.ParseComments,
+	tAbs, err := filepath.Abs(*templateFlag)
+	if err != nil {
+		return err
 	}
-
-	// The first Created package is the template.
-	conf.CreateFromFilenames("template", *templateFlag)
-
-	if _, err := conf.FromArgs(args, true); err != nil {
+	template, err := ioutil.ReadFile(tAbs)
+	if err != nil {
 		return err
 	}
 
-	// Load, parse and type-check the whole program.
-	iprog, err := conf.Load()
+	cfg := &packages.Config{
+		Fset:  token.NewFileSet(),
+		Mode:  packages.NeedTypesInfo | packages.NeedName | packages.NeedTypes | packages.NeedSyntax | packages.NeedImports | packages.NeedDeps | packages.NeedCompiledGoFiles,
+		Tests: true,
+	}
+
+	pkgs, err := packages.Load(cfg, args...)
+	if err != nil {
+		return err
+	}
+
+	tFile, err := parser.ParseFile(cfg.Fset, tAbs, template, parser.ParseComments)
+	if err != nil {
+		return err
+	}
+
+	// Type-check the template.
+	tInfo := types.Info{
+		Types:      make(map[ast.Expr]types.TypeAndValue),
+		Defs:       make(map[*ast.Ident]types.Object),
+		Uses:       make(map[*ast.Ident]types.Object),
+		Implicits:  make(map[ast.Node]types.Object),
+		Selections: make(map[*ast.SelectorExpr]*types.Selection),
+		Scopes:     make(map[ast.Node]*types.Scope),
+	}
+	conf := types.Config{
+		Importer: pkgsImporter(pkgs),
+	}
+	tPkg, err := conf.Check("egtemplate", cfg.Fset, []*ast.File{tFile}, &tInfo)
 	if err != nil {
 		return err
 	}
 
 	// Analyze the template.
-	template := iprog.Created[0]
-	xform, err := eg.NewTransformer(iprog.Fset, template.Pkg, template.Files[0], &template.Info, *verboseFlag)
+	xform, err := eg.NewTransformer(cfg.Fset, tPkg, tFile, &tInfo, *verboseFlag)
 	if err != nil {
 		return err
 	}
 
 	// Apply it to the input packages.
-	var pkgs []*loader.PackageInfo
+	var all []*packages.Package
 	if *transitiveFlag {
-		for _, info := range iprog.AllPackages {
-			pkgs = append(pkgs, info)
-		}
+		packages.Visit(pkgs, nil, func(p *packages.Package) { all = append(all, p) })
 	} else {
-		pkgs = iprog.InitialPackages()
+		all = pkgs
 	}
 	var hadErrors bool
 	for _, pkg := range pkgs {
-		if pkg == template {
-			continue
-		}
-		for _, file := range pkg.Files {
-			n := xform.Transform(&pkg.Info, pkg.Pkg, file)
+		for i, filename := range pkg.CompiledGoFiles {
+			if filename == tAbs {
+				// Don't rewrite the template file.
+				continue
+			}
+			file := pkg.Syntax[i]
+			n := xform.Transform(pkg.TypesInfo, pkg.Types, file)
 			if n == 0 {
 				continue
 			}
-			filename := iprog.Fset.File(file.Pos()).Name()
 			fmt.Fprintf(os.Stderr, "=== %s (%d matches)\n", filename, n)
 			if *writeFlag {
 				// Run the before-edit command (e.g. "chmod +w",  "checkout") if any.
@@ -138,17 +158,34 @@
 							args, err)
 					}
 				}
-				if err := eg.WriteAST(iprog.Fset, filename, file); err != nil {
+				if err := eg.WriteAST(cfg.Fset, filename, file); err != nil {
 					fmt.Fprintf(os.Stderr, "eg: %s\n", err)
 					hadErrors = true
 				}
 			} else {
-				format.Node(os.Stdout, iprog.Fset, file)
+				format.Node(os.Stdout, cfg.Fset, file)
 			}
 		}
 	}
 	if hadErrors {
 		os.Exit(1)
 	}
+
 	return nil
 }
+
+type pkgsImporter []*packages.Package
+
+func (p pkgsImporter) Import(path string) (tpkg *types.Package, err error) {
+	packages.Visit([]*packages.Package(p), func(pkg *packages.Package) bool {
+		if pkg.PkgPath == path {
+			tpkg = pkg.Types
+			return false
+		}
+		return true
+	}, nil)
+	if tpkg != nil {
+		return tpkg, nil
+	}
+	return nil, fmt.Errorf("package %q not found", path)
+}
diff --git a/cmd/godoc/handlers.go b/cmd/godoc/handlers.go
index a59301b..86a1243 100644
--- a/cmd/godoc/handlers.go
+++ b/cmd/godoc/handlers.go
@@ -14,11 +14,9 @@
 	"golang.org/x/tools/godoc"
 	"golang.org/x/tools/godoc/redirect"
 	"golang.org/x/tools/godoc/vfs"
-)
 
-// This package registers "/compile" and "/share" handlers
-// that redirect to the golang.org playground.
-import _ "golang.org/x/tools/playground"
+	_ "golang.org/x/tools/playground" // register "/compile" playground redirect
+)
 
 var (
 	pres *godoc.Presentation
diff --git a/cmd/guru/describe.go b/cmd/guru/describe.go
index 125e409..41189f6 100644
--- a/cmd/guru/describe.go
+++ b/cmd/guru/describe.go
@@ -738,6 +738,22 @@
 			}
 		}
 		if typestr == "" {
+			// The fix for #44515 changed the printing of unsafe.Pointer
+			// such that it uses a qualifier if one is provided. Using
+			// the types.RelativeTo qualifier provided here, the output
+			// is just "Pointer" rather than "unsafe.Pointer". This is
+			// consistent with the printing of non-type objects but it
+			// breaks an existing test which needs to work with older
+			// versions of Go. Re-establish the original output by not
+			// using a qualifier at all if we're printing a type from
+			// package unsafe - there's only unsafe.Pointer (#44596).
+			// NOTE: This correction can be removed (and the test's
+			// golden file adjusted) once we only run against go1.17
+			// or bigger.
+			qualifier := qualifier
+			if obj.Pkg() == types.Unsafe {
+				qualifier = nil
+			}
 			typestr = types.TypeString(typ, qualifier)
 		}
 		buf.WriteString(typestr)
diff --git a/cmd/present/play.go b/cmd/present/play.go
index 80627f2..2e53f14 100644
--- a/cmd/present/play.go
+++ b/cmd/present/play.go
@@ -18,7 +18,7 @@
 	"golang.org/x/tools/playground/socket"
 	"golang.org/x/tools/present"
 
-	// This will register handlers at /compile and /share that will proxy to the
+	// This will register a handler at /compile that will proxy to the
 	// respective endpoints at play.golang.org. This allows the frontend to call
 	// these endpoints without needing cross-origin request sharing (CORS).
 	// Note that this is imported regardless of whether the endpoints are used or
diff --git a/go/expect/extract.go b/go/expect/extract.go
index 4862b76..a01b8ce 100644
--- a/go/expect/extract.go
+++ b/go/expect/extract.go
@@ -42,7 +42,7 @@
 		// there are ways you can break the parser such that it will not add all the
 		// comments to the ast, which may result in files where the tests are silently
 		// not run.
-		file, err := parser.ParseFile(fset, filename, src, parser.ParseComments)
+		file, err := parser.ParseFile(fset, filename, src, parser.ParseComments|parser.AllErrors)
 		if file == nil {
 			return nil, err
 		}
diff --git a/go/internal/packagesdriver/sizes.go b/go/internal/packagesdriver/sizes.go
index f4d73b2..18a002f 100644
--- a/go/internal/packagesdriver/sizes.go
+++ b/go/internal/packagesdriver/sizes.go
@@ -22,7 +22,7 @@
 	stdout, stderr, friendlyErr, rawErr := gocmdRunner.RunRaw(ctx, inv)
 	var goarch, compiler string
 	if rawErr != nil {
-		if strings.Contains(rawErr.Error(), "cannot find main module") {
+		if rawErrMsg := rawErr.Error(); strings.Contains(rawErrMsg, "cannot find main module") || strings.Contains(rawErrMsg, "go.mod file not found") {
 			// User's running outside of a module. All bets are off. Get GOARCH and guess compiler is gc.
 			// TODO(matloob): Is this a problem in practice?
 			inv.Verb = "env"
diff --git a/go/packages/golist.go b/go/packages/golist.go
index d6ff75d..0e1e7f1 100644
--- a/go/packages/golist.go
+++ b/go/packages/golist.go
@@ -584,7 +584,7 @@
 				// 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?",
+					Msg:  "go list failed to return CompiledGoFiles. This may indicate failure to perform cgo processing; try building at the command line. See https://golang.org/issue/38990.",
 					Kind: ListError,
 				})
 			}
diff --git a/godoc/static/playground.js b/godoc/static/playground.js
index f5278a0..2dd1753 100644
--- a/godoc/static/playground.js
+++ b/godoc/static/playground.js
@@ -517,7 +517,7 @@
       sharing = true;
 
       var sharingData = body();
-      $.ajax('/share', {
+      $.ajax('https://play.golang.org/share', {
         processData: false,
         data: sharingData,
         type: 'POST',
diff --git a/godoc/static/static.go b/godoc/static/static.go
index e2a6452..3dab9ea 100644
--- a/godoc/static/static.go
+++ b/godoc/static/static.go
@@ -85,7 +85,7 @@
 
 	"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||\x20n.tagName\x20===\x20'PRE')\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\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",
+	"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('https://play.golang.org/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",
 
diff --git a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md
index 747b17e..e067d0c 100644
--- a/gopls/doc/analyzers.md
+++ b/gopls/doc/analyzers.md
@@ -208,6 +208,46 @@
 
 **Enabled by default.**
 
+## **nilness**
+
+check for redundant or impossible nil comparisons
+
+The nilness checker inspects the control-flow graph of each function in
+a package and reports nil pointer dereferences, degenerate nil
+pointers, and panics with nil values. A degenerate comparison is of the form
+x==nil or x!=nil where x is statically known to be nil or non-nil. These are
+often a mistake, especially in control flow related to errors. Panics with nil
+values are checked because they are not detectable by
+
+	if r := recover(); r != nil {
+
+This check reports conditions such as:
+
+	if f == nil { // impossible condition (f is a function)
+	}
+
+and:
+
+	p := &v
+	...
+	if p != nil { // tautological condition
+	}
+
+and:
+
+	if p == nil {
+		print(*p) // nil dereference
+	}
+
+and:
+
+	if p == nil {
+		panic(p)
+	}
+
+
+**Disabled by default. Enable it by setting `"analyses": {"nilness": true}`.**
+
 ## **printf**
 
 check consistency of Printf format strings and arguments
@@ -468,6 +508,35 @@
 
 **Enabled by default.**
 
+## **unusedwrite**
+
+checks for unused writes
+
+The analyzer reports instances of writes to struct fields and
+arrays that are never read. Specifically, when a struct object
+or an array is copied, its elements are copied implicitly by
+the compiler, and any element write to this copy does nothing
+with the original object.
+
+For example:
+
+	type T struct { x int }
+	func f(input []T) {
+		for i, v := range input {  // v is a copy
+			v.x = i  // unused write to field x
+		}
+	}
+
+Another example is about non-pointer receiver:
+
+	type T struct { x int }
+	func (t T) f() {  // t is a copy
+		t.x = i  // unused write to field x
+	}
+
+
+**Disabled by default. Enable it by setting `"analyses": {"unusedwrite": true}`.**
+
 ## **fillreturns**
 
 suggested fixes for "wrong number of return values (want %d, got %d)"
diff --git a/gopls/doc/daemon.md b/gopls/doc/daemon.md
index f54099c..86356da 100644
--- a/gopls/doc/daemon.md
+++ b/gopls/doc/daemon.md
@@ -174,8 +174,9 @@
 * `-remote.logfile`: the location of the daemon logfile
 * `-remote.debug`: the daemon's debug address
 * `-remote.listen.timeout`: the amount of time the daemon should wait for new
-  connections while there are no current connections, before shutting down. If
-  `0`, listen indefinitely.
+  connections while there are no current connections, before shutting down.
+  Must be set to a valid `time.Duration` (e.g. `30s` or `5m`). If `0`, listen
+  indefinitely. Default: `1m`.
 
 Note that once the daemon is already running, setting these flags will not
 change its configuration. These flags only matter for the forwarder process
diff --git a/gopls/doc/generate.go b/gopls/doc/generate.go
index d6bd1ab..ed42647 100644
--- a/gopls/doc/generate.go
+++ b/gopls/doc/generate.go
@@ -91,7 +91,7 @@
 	for _, c := range api.Commands {
 		c.Command = command.ID(c.Command)
 	}
-	for _, m := range []map[string]source.Analyzer{
+	for _, m := range []map[string]*source.Analyzer{
 		defaults.DefaultAnalyzers,
 		defaults.TypeErrorAnalyzers,
 		defaults.ConvenienceAnalyzers,
@@ -464,7 +464,7 @@
 	return lenses
 }
 
-func loadAnalyzers(m map[string]source.Analyzer) []*source.AnalyzerJSON {
+func loadAnalyzers(m map[string]*source.Analyzer) []*source.AnalyzerJSON {
 	var sorted []string
 	for _, a := range m {
 		sorted = append(sorted, a.Analyzer.Name)
diff --git a/gopls/doc/vim.md b/gopls/doc/vim.md
index 41c01e9..8a49a4d 100644
--- a/gopls/doc/vim.md
+++ b/gopls/doc/vim.md
@@ -171,17 +171,27 @@
     local params = vim.lsp.util.make_range_params()
     params.context = context
 
-    local method = "textDocument/codeAction"
-    local resp = vim.lsp.buf_request_sync(0, method, params, timeoutms)
-    if resp and resp[1] then
-      local result = resp[1].result
-      if result and result[1] then
-        local edit = result[1].edit
-        vim.lsp.util.apply_workspace_edit(edit)
-      end
-    end
+    -- See the implementation of the textDocument/codeAction callback
+    -- (lua/vim/lsp/handler.lua) for how to do this properly.
+    local result = vim.lsp.buf_request_sync(0, "textDocument/codeAction", params, timeout_ms)
+    if not result or next(result) == nil then return end
+    local actions = result[1].result
+    if not actions then return end
+    local action = actions[1]
 
-    vim.lsp.buf.formatting()
+    -- textDocument/codeAction can return either Command[] or CodeAction[]. If it
+    -- is a CodeAction, it can have either an edit, a command or both. Edits
+    -- should be executed first.
+    if action.edit or type(action.command) == "table" then
+      if action.edit then
+        vim.lsp.util.apply_workspace_edit(action.edit)
+      end
+      if type(action.command) == "table" then
+        vim.lsp.buf.execute_command(action.command)
+      end
+    else
+      vim.lsp.buf.execute_command(action)
+    end
   end
 EOF
 
diff --git a/gopls/go.mod b/gopls/go.mod
index b6a86e3..3565e0d 100644
--- a/gopls/go.mod
+++ b/gopls/go.mod
@@ -14,3 +14,5 @@
 	mvdan.cc/gofumpt v0.1.0
 	mvdan.cc/xurls/v2 v2.2.0
 )
+
+replace golang.org/x/tools => ../
diff --git a/gopls/internal/hooks/hooks.go b/gopls/internal/hooks/hooks.go
index 50e8f71..390967d 100644
--- a/gopls/internal/hooks/hooks.go
+++ b/gopls/internal/hooks/hooks.go
@@ -21,16 +21,17 @@
 	if options.GoDiff {
 		options.ComputeEdits = ComputeEdits
 	}
-	options.URLRegexp = urlRegexp()
+	options.URLRegexp = relaxedFullWord
 	options.GofumptFormat = func(ctx context.Context, src []byte) ([]byte, error) {
 		return format.Source(src, format.Options{})
 	}
 	updateAnalyzers(options)
 }
 
-func urlRegexp() *regexp.Regexp {
-	// Ensure links are matched as full words, not anywhere.
-	re := regexp.MustCompile(`\b(` + xurls.Relaxed().String() + `)\b`)
-	re.Longest()
-	return re
+var relaxedFullWord *regexp.Regexp
+
+// Ensure links are matched as full words, not anywhere.
+func init() {
+	relaxedFullWord = regexp.MustCompile(`\b(` + xurls.Relaxed().String() + `)\b`)
+	relaxedFullWord.Longest()
 }
diff --git a/gopls/internal/regtest/codelens/codelens_test.go b/gopls/internal/regtest/codelens/codelens_test.go
index 9a2cce8..88d0e04 100644
--- a/gopls/internal/regtest/codelens/codelens_test.go
+++ b/gopls/internal/regtest/codelens/codelens_test.go
@@ -256,14 +256,17 @@
 }
 `
 	Run(t, workspace, func(t *testing.T, env *Env) {
-		// Open the file. We should have a nonexistant symbol.
+		// Open the file. We have a nonexistant symbol that will break cgo processing.
 		env.OpenFile("cgo.go")
-		env.Await(env.DiagnosticAtRegexp("cgo.go", `C\.(fortytwo)`)) // could not determine kind of name for C.fortytwo
+		env.Await(env.DiagnosticAtRegexpWithMessage("cgo.go", ``, "go list failed to return CompiledGoFiles"))
 
 		// 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)`))
+		env.Await(OnceMet(
+			env.DoneWithSave(),
+			env.DiagnosticAtRegexpWithMessage("cgo.go", ``, "go list failed to return CompiledGoFiles"),
+		))
 
 		// Regenerate cgo, fixing the diagnostic.
 		env.ExecuteCodeLensCommand("cgo.go", command.RegenerateCgo)
diff --git a/gopls/internal/regtest/diagnostics/diagnostics_test.go b/gopls/internal/regtest/diagnostics/diagnostics_test.go
index 84b8b3d..75ba7de 100644
--- a/gopls/internal/regtest/diagnostics/diagnostics_test.go
+++ b/gopls/internal/regtest/diagnostics/diagnostics_test.go
@@ -8,7 +8,6 @@
 	"context"
 	"fmt"
 	"log"
-	"os"
 	"testing"
 
 	. "golang.org/x/tools/gopls/internal/regtest"
@@ -585,8 +584,9 @@
 	}
 }
 
-// Tests golang/go#38602.
-func TestNonexistentFileDiagnostics_Issue38602(t *testing.T) {
+// Tests the repro case from golang/go#38602. Diagnostics are now handled properly,
+// which blocks type checking.
+func TestConflictingMainPackageErrors(t *testing.T) {
 	const collision = `
 -- x/x.go --
 package x
@@ -604,27 +604,19 @@
 }
 `
 	WithOptions(InGOPATH()).Run(t, collision, func(t *testing.T, env *Env) {
-		env.OpenFile("x/main.go")
+		env.OpenFile("x/x.go")
 		env.Await(
-			env.DiagnosticAtRegexp("x/main.go", "fmt.Println"),
+			env.DiagnosticAtRegexpWithMessage("x/x.go", `^`, "found packages main (main.go) and x (x.go)"),
+			env.DiagnosticAtRegexpWithMessage("x/main.go", `^`, "found packages main (main.go) and x (x.go)"),
 		)
-		env.OrganizeImports("x/main.go")
-		// span.Parse misparses the error message when multiple packages are
-		// defined in the same directory, creating a garbage filename.
-		// Previously, we would send diagnostics for this nonexistent file.
-		// This test checks that we don't send diagnostics for this file.
-		dir, err := os.Getwd()
-		if err != nil {
-			t.Fatal(err)
-		}
-		badFile := fmt.Sprintf("%s/found packages main (main.go) and x (x.go) in %s/src/x", dir, env.Sandbox.GOPATH())
-		env.Await(
-			OnceMet(
+
+		// We don't recover cleanly from the errors without good overlay support.
+		if testenv.Go1Point() >= 16 {
+			env.RegexpReplace("x/x.go", `package x`, `package main`)
+			env.Await(OnceMet(
 				env.DoneWithChange(),
-				EmptyDiagnostics("x/main.go"),
-			),
-			NoDiagnostics(badFile),
-		)
+				env.DiagnosticAtRegexpWithMessage("x/main.go", `fmt`, "undeclared name")))
+		}
 	})
 }
 
@@ -717,15 +709,14 @@
 	WithOptions(
 		ProxyFiles(ardanLabsProxy),
 	).Run(t, emptyFile, func(t *testing.T, env *Env) {
-		env.OpenFile("main.go")
-		env.EditBuffer("main.go", fake.NewEdit(0, 0, 0, 0, `package main
+		env.CreateBuffer("main.go", `package main
 
 import "github.com/ardanlabs/conf"
 
 func main() {
 	_ = conf.ErrHelpWanted
 }
-`))
+`)
 		env.SaveBuffer("main.go")
 		var d protocol.PublishDiagnosticsParams
 		env.Await(
@@ -1116,7 +1107,7 @@
 }
 `
 	Run(t, basic, func(t *testing.T, env *Env) {
-		testenv.NeedsGo1Point(t, 15)
+		testenv.NeedsGo1Point(t, 16) // We can't recover cleanly from this case without good overlay support.
 
 		env.WriteWorkspaceFile("foo/foo_test.go", `package main
 
@@ -1219,7 +1210,7 @@
 	Run(t, pkgDefault, func(t *testing.T, env *Env) {
 		env.OpenFile("main.go")
 		env.Await(
-			env.DiagnosticAtRegexp("main.go", "default"),
+			env.DiagnosticAtRegexpWithMessage("main.go", "default", "expected 'IDENT'"),
 		)
 	})
 }
@@ -1261,7 +1252,7 @@
 	})
 }
 
-func TestStaticcheckDiagnostic(t *testing.T) {
+func TestSimplifyCompositeLitDiagnostic(t *testing.T) {
 	const files = `
 -- go.mod --
 module mod.com
@@ -1286,8 +1277,16 @@
 		EditorConfig{EnableStaticcheck: true},
 	).Run(t, files, func(t *testing.T, env *Env) {
 		env.OpenFile("main.go")
-		// Staticcheck should generate a diagnostic to simplify this literal.
-		env.Await(env.DiagnosticAtRegexp("main.go", `t{"msg"}`))
+		var d protocol.PublishDiagnosticsParams
+		env.Await(OnceMet(
+			env.DiagnosticAtRegexpWithMessage("main.go", `t{"msg"}`, "redundant type"),
+			ReadDiagnostics("main.go", &d),
+		))
+		if tags := d.Diagnostics[0].Tags; len(tags) == 0 || tags[0] != protocol.Unnecessary {
+			t.Errorf("wanted Unnecessary tag on diagnostic, got %v", tags)
+		}
+		env.ApplyQuickFixes("main.go", d.Diagnostics)
+		env.Await(EmptyDiagnostics("main.go"))
 	})
 }
 
@@ -1798,3 +1797,68 @@
 		)
 	})
 }
+
+func TestBuildTagChange(t *testing.T) {
+	const files = `
+-- go.mod --
+module mod.com
+
+go 1.12
+-- foo.go --
+// decoy comment
+// +build hidden
+// decoy comment
+
+package foo
+var Foo = 1
+-- bar.go --
+package foo
+var Bar = Foo
+`
+
+	Run(t, files, func(t *testing.T, env *Env) {
+		env.OpenFile("foo.go")
+		env.Await(env.DiagnosticAtRegexpWithMessage("bar.go", `Foo`, "undeclared name"))
+		env.RegexpReplace("foo.go", `\+build`, "")
+		env.Await(EmptyDiagnostics("bar.go"))
+	})
+
+}
+
+func TestIssue44736(t *testing.T) {
+	const files = `
+	-- go.mod --
+module blah.com
+
+go 1.16
+-- main.go --
+package main
+
+import "fmt"
+
+func main() {
+	asdf
+	fmt.Printf("This is a test %v")
+	fdas
+}
+-- other.go --
+package main
+
+`
+	Run(t, files, func(t *testing.T, env *Env) {
+		env.OpenFile("main.go")
+		env.OpenFile("other.go")
+		env.Await(
+			env.DiagnosticAtRegexpWithMessage("main.go", "asdf", "undeclared name"),
+			env.DiagnosticAtRegexpWithMessage("main.go", "fdas", "undeclared name"),
+		)
+		env.SetBufferContent("other.go", "package main\n\nasdf")
+		// The new diagnostic in other.go should not suppress diagnostics in main.go.
+		env.Await(
+			OnceMet(
+				env.DiagnosticAtRegexpWithMessage("other.go", "asdf", "expected declaration"),
+				env.DiagnosticAtRegexpWithMessage("main.go", "asdf", "undeclared name"),
+			),
+		)
+	})
+}
diff --git a/gopls/internal/regtest/misc/embed_test.go b/gopls/internal/regtest/misc/embed_test.go
new file mode 100644
index 0000000..76d1225
--- /dev/null
+++ b/gopls/internal/regtest/misc/embed_test.go
@@ -0,0 +1,34 @@
+// Copyright 2021 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 misc
+
+import (
+	"testing"
+
+	. "golang.org/x/tools/gopls/internal/regtest"
+	"golang.org/x/tools/internal/testenv"
+)
+
+func TestMissingPatternDiagnostic(t *testing.T) {
+	testenv.NeedsGo1Point(t, 16)
+	const files = `
+-- go.mod --
+module example.com
+-- x.go --
+package x
+
+import (
+	_ "embed"
+)
+
+//go:embed NONEXISTENT
+var foo string
+`
+	Run(t, files, func(t *testing.T, env *Env) {
+		env.OpenFile("x.go")
+		env.Await(env.DiagnosticAtRegexpWithMessage("x.go", `NONEXISTENT`, "no matching files found"))
+		env.RegexpReplace("x.go", `NONEXISTENT`, "x.go")
+		env.Await(EmptyDiagnostics("x.go"))
+	})
+}
diff --git a/gopls/internal/regtest/misc/failures_test.go b/gopls/internal/regtest/misc/failures_test.go
index 68bbded..41a833e 100644
--- a/gopls/internal/regtest/misc/failures_test.go
+++ b/gopls/internal/regtest/misc/failures_test.go
@@ -5,7 +5,6 @@
 package misc
 
 import (
-	"log"
 	"testing"
 
 	. "golang.org/x/tools/gopls/internal/regtest"
@@ -59,18 +58,13 @@
 
 func TestFailingDiagnosticClearingOnEdit(t *testing.T) {
 	Run(t, badPackageDup, func(t *testing.T, env *Env) {
-		log.SetFlags(log.Lshortfile)
 		env.OpenFile("b.go")
-		env.Await(env.AnyDiagnosticAtCurrentVersion("a.go"))
-		// no diagnostics for either b.go or 'gen.go', but there should be
-		env.Await(NoDiagnostics("b.go"))
+		// no diagnostics for any files, but there should be
+		env.Await(NoDiagnostics("a.go"), NoDiagnostics("b.go"))
 
 		// Fix the error by editing the const name in b.go to `b`.
 		env.RegexpReplace("b.go", "(a) = 2", "b")
-		env.Await(
-			EmptyDiagnostics("a.go"),
-			// expected, as there have never been any diagnostics for b.go
-			NoDiagnostics("b.go"),
-		)
+
+		// The diagnostics that weren't sent above should now be cleared.
 	})
 }
diff --git a/gopls/internal/regtest/misc/fix_test.go b/gopls/internal/regtest/misc/fix_test.go
index 4f97c01..395bfd8 100644
--- a/gopls/internal/regtest/misc/fix_test.go
+++ b/gopls/internal/regtest/misc/fix_test.go
@@ -60,3 +60,28 @@
 		}
 	})
 }
+
+func TestFillReturns(t *testing.T) {
+	const files = `
+-- go.mod --
+module mod.com
+
+go 1.12
+-- main.go --
+package main
+
+func Foo() error {
+	return
+}
+`
+	Run(t, files, func(t *testing.T, env *Env) {
+		env.OpenFile("main.go")
+		var d protocol.PublishDiagnosticsParams
+		env.Await(OnceMet(
+			env.DiagnosticAtRegexpWithMessage("main.go", `return`, "wrong number of return values"),
+			ReadDiagnostics("main.go", &d),
+		))
+		env.ApplyQuickFixes("main.go", d.Diagnostics)
+		env.Await(EmptyDiagnostics("main.go"))
+	})
+}
diff --git a/gopls/internal/regtest/watch/watch_test.go b/gopls/internal/regtest/watch/watch_test.go
index 436c091..ae43bea 100644
--- a/gopls/internal/regtest/watch/watch_test.go
+++ b/gopls/internal/regtest/watch/watch_test.go
@@ -370,6 +370,7 @@
 // Tests golang/go#38498. Delete a file and then force a reload.
 // Assert that we no longer try to load the file.
 func TestDeleteFiles(t *testing.T) {
+	testenv.NeedsGo1Point(t, 13) // Poor overlay support causes problems on 1.12.
 	const pkg = `
 -- go.mod --
 module mod.com
diff --git a/internal/fastwalk/fastwalk_unix.go b/internal/fastwalk/fastwalk_unix.go
index e4edb5c..58bd878 100644
--- a/internal/fastwalk/fastwalk_unix.go
+++ b/internal/fastwalk/fastwalk_unix.go
@@ -22,7 +22,7 @@
 const unknownFileMode os.FileMode = os.ModeNamedPipe | os.ModeSocket | os.ModeDevice
 
 func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error {
-	fd, err := syscall.Open(dirName, 0, 0)
+	fd, err := open(dirName, 0, 0)
 	if err != nil {
 		return &os.PathError{Op: "open", Path: dirName, Err: err}
 	}
@@ -36,7 +36,7 @@
 	for {
 		if bufp >= nbuf {
 			bufp = 0
-			nbuf, err = syscall.ReadDirent(fd, buf)
+			nbuf, err = readDirent(fd, buf)
 			if err != nil {
 				return os.NewSyscallError("readdirent", err)
 			}
@@ -127,3 +127,27 @@
 	}
 	return
 }
+
+// According to https://golang.org/doc/go1.14#runtime
+// A consequence of the implementation of preemption is that on Unix systems, including Linux and macOS
+// systems, programs built with Go 1.14 will receive more signals than programs built with earlier releases.
+//
+// This causes syscall.Open and syscall.ReadDirent sometimes fail with EINTR errors.
+// We need to retry in this case.
+func open(path string, mode int, perm uint32) (fd int, err error) {
+	for {
+		fd, err := syscall.Open(path, mode, perm)
+		if err != syscall.EINTR {
+			return fd, err
+		}
+	}
+}
+
+func readDirent(fd int, buf []byte) (n int, err error) {
+	for {
+		nbuf, err := syscall.ReadDirent(fd, buf)
+		if err != syscall.EINTR {
+			return nbuf, err
+		}
+	}
+}
diff --git a/internal/imports/mod.go b/internal/imports/mod.go
index 65e0b94..dff6d55 100644
--- a/internal/imports/mod.go
+++ b/internal/imports/mod.go
@@ -89,8 +89,10 @@
 		err := r.initAllMods()
 		// We expect an error when running outside of a module with
 		// GO111MODULE=on. Other errors are fatal.
-		if err != nil && !strings.Contains(err.Error(), "working directory is not part of a module") {
-			return err
+		if err != nil {
+			if errMsg := err.Error(); !strings.Contains(errMsg, "working directory is not part of a module") && !strings.Contains(errMsg, "go.mod file not found") {
+				return err
+			}
 		}
 	}
 
diff --git a/internal/lsp/cache/analysis.go b/internal/lsp/cache/analysis.go
index 2b537e0..92af4f1 100644
--- a/internal/lsp/cache/analysis.go
+++ b/internal/lsp/cache/analysis.go
@@ -18,17 +18,21 @@
 	"golang.org/x/tools/internal/analysisinternal"
 	"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/span"
 	errors "golang.org/x/xerrors"
 )
 
-func (s *snapshot) Analyze(ctx context.Context, id string, analyzers ...*analysis.Analyzer) ([]*source.Diagnostic, error) {
+func (s *snapshot) Analyze(ctx context.Context, id string, analyzers []*source.Analyzer) ([]*source.Diagnostic, error) {
 	var roots []*actionHandle
 
 	for _, a := range analyzers {
-		ah, err := s.actionHandle(ctx, packageID(id), a)
+
+		if !a.IsEnabled(s.view) {
+			continue
+		}
+		ah, err := s.actionHandle(ctx, packageID(id), a.Analyzer)
 		if err != nil {
 			return nil, err
 		}
@@ -324,7 +328,7 @@
 	analysisinternal.SetTypeErrors(pass, pkg.typeErrors)
 
 	if pkg.IsIllTyped() {
-		data.err = errors.Errorf("analysis skipped due to errors in package: %v", pkg.GetDiagnostics())
+		data.err = errors.Errorf("analysis skipped due to errors in package")
 		return data
 	}
 	data.result, data.err = pass.Analyzer.Run(pass)
@@ -348,7 +352,7 @@
 	}
 
 	for _, diag := range diagnostics {
-		srcDiags, err := sourceDiagnostics(ctx, snapshot, pkg, protocol.SeverityWarning, diag)
+		srcDiags, err := analysisDiagnosticDiagnostics(ctx, snapshot, pkg, analyzer, diag)
 		if err != nil {
 			event.Error(ctx, "unable to compute analysis error position", err, tag.Category.Of(diag.Category), tag.Package.Of(pkg.ID()))
 			continue
@@ -391,3 +395,38 @@
 	}
 	return t
 }
+
+func (s *snapshot) DiagnosePackage(ctx context.Context, spkg source.Package) (map[span.URI][]*source.Diagnostic, error) {
+	pkg := spkg.(*pkg)
+	// Apply type error analyzers. They augment type error diagnostics with their own fixes.
+	var analyzers []*source.Analyzer
+	for _, a := range s.View().Options().TypeErrorAnalyzers {
+		analyzers = append(analyzers, a)
+	}
+	var errorAnalyzerDiag []*source.Diagnostic
+	if pkg.hasTypeErrors {
+		var err error
+		errorAnalyzerDiag, err = s.Analyze(ctx, pkg.ID(), analyzers)
+		if err != nil {
+			return nil, err
+		}
+	}
+	diags := map[span.URI][]*source.Diagnostic{}
+	for _, diag := range pkg.diagnostics {
+		for _, eaDiag := range errorAnalyzerDiag {
+			if eaDiag.URI == diag.URI && eaDiag.Range == diag.Range && eaDiag.Message == diag.Message {
+				// Type error analyzers just add fixes and tags. Make a copy,
+				// since we don't own either, and overwrite.
+				// The analyzer itself can't do this merge because
+				// analysis.Diagnostic doesn't have all the fields, and Analyze
+				// can't because it doesn't have the type error, notably its code.
+				clone := *diag
+				clone.SuggestedFixes = eaDiag.SuggestedFixes
+				clone.Tags = eaDiag.Tags
+				diag = &clone
+			}
+		}
+		diags[diag.URI] = append(diags[diag.URI], diag)
+	}
+	return diags, nil
+}
diff --git a/internal/lsp/cache/check.go b/internal/lsp/cache/check.go
index 5940651..9df626b 100644
--- a/internal/lsp/cache/check.go
+++ b/internal/lsp/cache/check.go
@@ -9,10 +9,10 @@
 	"context"
 	"fmt"
 	"go/ast"
+	"go/scanner"
 	"go/types"
 	"path"
 	"path/filepath"
-	"regexp"
 	"sort"
 	"strings"
 	"sync"
@@ -21,7 +21,6 @@
 	"golang.org/x/tools/go/ast/astutil"
 	"golang.org/x/tools/go/packages"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/lsp/command"
 	"golang.org/x/tools/internal/lsp/debug/tag"
 	"golang.org/x/tools/internal/lsp/protocol"
 	"golang.org/x/tools/internal/lsp/source"
@@ -271,11 +270,6 @@
 	ctx, done := event.Start(ctx, "cache.importer.typeCheck", tag.Package.Of(string(m.id)))
 	defer done()
 
-	var rawErrors []error
-	for _, err := range m.errors {
-		rawErrors = append(rawErrors, err)
-	}
-
 	fset := snapshot.view.session.cache.fset
 	pkg := &pkg{
 		m:               m,
@@ -307,12 +301,12 @@
 	}
 	var (
 		files        = make([]*ast.File, len(m.compiledGoFiles))
-		parseErrors  = make([]error, len(m.compiledGoFiles))
+		parseErrors  = make([]scanner.ErrorList, len(m.compiledGoFiles))
 		actualErrors = make([]error, len(m.compiledGoFiles))
 		wg           sync.WaitGroup
 
 		mu             sync.Mutex
-		skipTypeErrors bool
+		haveFixedFiles bool
 	)
 	for i, cgf := range m.compiledGoFiles {
 		wg.Add(1)
@@ -332,8 +326,10 @@
 			pkg.compiledGoFiles[i] = pgf
 			files[i], parseErrors[i], actualErrors[i] = pgf.File, pgf.ParseErr, err
 
+			// If we have fixed parse errors in any of the files, we should hide type
+			// errors, as they may be completely nonsensical.
 			mu.Lock()
-			skipTypeErrors = skipTypeErrors || fixed
+			haveFixedFiles = haveFixedFiles || fixed
 			mu.Unlock()
 		}(i, cgf)
 	}
@@ -357,13 +353,16 @@
 		}
 	}
 
+	var i int
 	for _, e := range parseErrors {
 		if e != nil {
-			rawErrors = append(rawErrors, e)
+			parseErrors[i] = e
+			i++
 		}
 	}
+	parseErrors = parseErrors[:i]
 
-	var i int
+	i = 0
 	for _, f := range files {
 		if f != nil {
 			files[i] = f
@@ -379,10 +378,10 @@
 		// race to Unsafe.completed.
 		return pkg, nil
 	} else if len(files) == 0 { // not the unsafe package, no parsed files
-		// Try to attach errors messages to the file as much as possible.
+		// Try to attach error messages to the file as much as possible.
 		var found bool
-		for _, e := range rawErrors {
-			srcDiags, err := sourceDiagnostics(ctx, snapshot, pkg, protocol.SeverityError, e)
+		for _, e := range m.errors {
+			srcDiags, err := goPackagesErrorDiagnostics(ctx, snapshot, pkg, e)
 			if err != nil {
 				continue
 			}
@@ -392,19 +391,15 @@
 		if found {
 			return pkg, nil
 		}
-		return nil, errors.Errorf("no parsed files for package %s, expected: %v, list errors: %v", pkg.m.pkgPath, pkg.compiledGoFiles, rawErrors)
+		return nil, errors.Errorf("no parsed files for package %s, expected: %v, errors: %v", pkg.m.pkgPath, pkg.compiledGoFiles, m.errors)
 	} else {
 		pkg.types = types.NewPackage(string(m.pkgPath), string(m.name))
 	}
 
+	var typeErrors []types.Error
 	cfg := &types.Config{
 		Error: func(e error) {
-			// If we have fixed parse errors in any of the files,
-			// we should hide type errors, as they may be completely nonsensical.
-			if skipTypeErrors {
-				return
-			}
-			rawErrors = append(rawErrors, e)
+			typeErrors = append(typeErrors, e.(types.Error))
 		},
 		Importer: importerFunc(func(pkgPath string) (*types.Package, error) {
 			// If the context was cancelled, we should abort.
@@ -441,63 +436,79 @@
 	}
 
 	// We don't care about a package's errors unless we have parsed it in full.
-	if mode == source.ParseFull {
-		expandErrors(rawErrors)
-		for _, e := range rawErrors {
-			srcDiags, err := sourceDiagnostics(ctx, snapshot, pkg, protocol.SeverityError, e)
+	if mode != source.ParseFull {
+		return pkg, nil
+	}
+
+	if len(m.errors) != 0 {
+		pkg.hasListOrParseErrors = true
+		for _, e := range m.errors {
+			diags, err := goPackagesErrorDiagnostics(ctx, snapshot, pkg, e)
 			if err != nil {
-				event.Error(ctx, "unable to compute error positions", err, tag.Package.Of(pkg.ID()))
+				event.Error(ctx, "unable to compute positions for list errors", err, tag.Package.Of(pkg.ID()))
 				continue
 			}
-			pkg.diagnostics = append(pkg.diagnostics, srcDiags...)
+			pkg.diagnostics = append(pkg.diagnostics, diags...)
+		}
+	}
 
-			if err, ok := e.(extendedError); ok {
-				pkg.typeErrors = append(pkg.typeErrors, err.primary)
+	// Our heuristic for whether to show type checking errors is:
+	//  + If any file was 'fixed', don't show type checking errors as we
+	//    can't guarantee that they reference accurate locations in the source.
+	//  + If there is a parse error _in the current file_, suppress type
+	//    errors in that file.
+	//  + Otherwise, show type errors even in the presence of parse errors in
+	//    other package files. go/types attempts to suppress follow-on errors
+	//    due to bad syntax, so on balance type checking errors still provide
+	//    a decent signal/noise ratio as long as the file in question parses.
+
+	// Track URIs with parse errors so that we can suppress type errors for these
+	// files.
+	unparseable := map[span.URI]bool{}
+	if len(parseErrors) != 0 {
+		pkg.hasListOrParseErrors = true
+		for _, e := range parseErrors {
+			diags, err := parseErrorDiagnostics(ctx, snapshot, pkg, e)
+			if err != nil {
+				event.Error(ctx, "unable to compute positions for parse errors", err, tag.Package.Of(pkg.ID()))
+				continue
+			}
+			for _, diag := range diags {
+				unparseable[diag.URI] = true
+				pkg.diagnostics = append(pkg.diagnostics, diag)
 			}
 		}
+	}
 
-		depsErrors, err := snapshot.depsErrors(ctx, pkg)
+	if haveFixedFiles {
+		return pkg, nil
+	}
+
+	for _, e := range expandErrors(typeErrors, snapshot.View().Options().RelatedInformationSupported) {
+		pkg.hasTypeErrors = true
+		diags, err := typeErrorDiagnostics(ctx, snapshot, pkg, e)
 		if err != nil {
-			return nil, err
-		}
-		pkg.diagnostics = append(pkg.diagnostics, depsErrors...)
-		if err := addGoGetFixes(ctx, snapshot, pkg); err != nil {
-			return nil, err
-		}
-	}
-
-	return pkg, nil
-}
-
-var importErrorRe = regexp.MustCompile(`could not import ([^\s]+)`)
-var missingModuleErrorRe = regexp.MustCompile(`cannot find module providing package ([^\s:]+)`)
-
-func addGoGetFixes(ctx context.Context, snapshot source.Snapshot, pkg *pkg) error {
-	if len(pkg.compiledGoFiles) == 0 || snapshot.GoModForFile(pkg.compiledGoFiles[0].URI) == "" {
-		// Go get only supports module mode for now.
-		return nil
-	}
-	for _, diag := range pkg.diagnostics {
-		matches := importErrorRe.FindStringSubmatch(diag.Message)
-		if len(matches) == 0 {
-			matches = missingModuleErrorRe.FindStringSubmatch(diag.Message)
-		}
-		if len(matches) == 0 {
+			event.Error(ctx, "unable to compute positions for type errors", err, tag.Package.Of(pkg.ID()))
 			continue
 		}
-		direct := !strings.Contains(diag.Message, "error while importing")
-		title := fmt.Sprintf("go get package %v", matches[1])
-		cmd, err := command.NewGoGetPackageCommand(title, command.GoGetPackageArgs{
-			URI:        protocol.URIFromSpanURI(pkg.compiledGoFiles[0].URI),
-			AddRequire: direct,
-			Pkg:        matches[1],
-		})
-		if err != nil {
-			return err
+		pkg.typeErrors = append(pkg.typeErrors, e.primary)
+		for _, diag := range diags {
+			// If the file didn't parse cleanly, it is highly likely that type
+			// checking errors will be confusing or redundant. But otherwise, type
+			// checking usually provides a good enough signal to include.
+			if !unparseable[diag.URI] {
+				pkg.diagnostics = append(pkg.diagnostics, diag)
+			}
 		}
-		diag.SuggestedFixes = append(diag.SuggestedFixes, source.SuggestedFixFromCommand(cmd))
 	}
-	return nil
+
+	depsErrors, err := snapshot.depsErrors(ctx, pkg)
+	if err != nil {
+		return nil, err
+	}
+	pkg.diagnostics = append(pkg.diagnostics, depsErrors...)
+
+	return pkg, nil
 }
 
 func (s *snapshot) depsErrors(ctx context.Context, pkg *pkg) ([]*source.Diagnostic, error) {
@@ -545,28 +556,34 @@
 		}
 	}
 
-	// Apply a diagnostic to any import involved in the error, stopping after
+	// Apply a diagnostic to any import involved in the error, stopping once
 	// we reach the workspace.
 	var errors []*source.Diagnostic
 	for _, depErr := range relevantErrors {
 		for i := len(depErr.ImportStack) - 1; i >= 0; i-- {
 			item := depErr.ImportStack[i]
+			if _, ok := s.isWorkspacePackage(packageID(item)); ok {
+				break
+			}
+
 			for _, imp := range allImports[item] {
 				rng, err := source.NewMappedRange(s.FileSet(), imp.cgf.Mapper, imp.imp.Pos(), imp.imp.End()).Range()
 				if err != nil {
 					return nil, err
 				}
+				fixes, err := goGetQuickFixes(s, imp.cgf.URI, item)
+				if err != nil {
+					return nil, err
+				}
 				errors = append(errors, &source.Diagnostic{
-					URI:      imp.cgf.URI,
-					Range:    rng,
-					Severity: protocol.SeverityError,
-					Source:   source.TypeError,
-					Message:  fmt.Sprintf("error while importing %v: %v", item, depErr.Err),
+					URI:            imp.cgf.URI,
+					Range:          rng,
+					Severity:       protocol.SeverityError,
+					Source:         source.TypeError,
+					Message:        fmt.Sprintf("error while importing %v: %v", item, depErr.Err),
+					SuggestedFixes: fixes,
 				})
 			}
-			if _, ok := s.isWorkspacePackage(packageID(item)); ok {
-				break
-			}
 		}
 	}
 
@@ -604,12 +621,17 @@
 			if err != nil {
 				return nil, err
 			}
+			fixes, err := goGetQuickFixes(s, pm.URI, item)
+			if err != nil {
+				return nil, err
+			}
 			errors = append(errors, &source.Diagnostic{
-				URI:      pm.URI,
-				Range:    rng,
-				Severity: protocol.SeverityError,
-				Source:   source.TypeError,
-				Message:  fmt.Sprintf("error while importing %v: %v", item, depErr.Err),
+				URI:            pm.URI,
+				Range:          rng,
+				Severity:       protocol.SeverityError,
+				Source:         source.TypeError,
+				Message:        fmt.Sprintf("error while importing %v: %v", item, depErr.Err),
+				SuggestedFixes: fixes,
 			})
 			break
 		}
@@ -651,30 +673,55 @@
 // there is a multiply-defined function, the secondary error points back to the
 // definition first noticed.
 //
-// This code associates the secondary error with its primary error, which can
+// This function associates the secondary error with its primary error, which can
 // then be used as RelatedInformation when the error becomes a diagnostic.
-func expandErrors(errs []error) []error {
+//
+// If supportsRelatedInformation is false, the secondary is instead embedded as
+// additional context in the primary error.
+func expandErrors(errs []types.Error, supportsRelatedInformation bool) []extendedError {
+	var result []extendedError
 	for i := 0; i < len(errs); {
-		e, ok := errs[i].(types.Error)
-		if !ok {
-			i++
-			continue
+		original := extendedError{
+			primary: errs[i],
 		}
-		enew := extendedError{
-			primary: e,
-		}
-		j := i + 1
-		for ; j < len(errs); j++ {
-			spl, ok := errs[j].(types.Error)
-			if !ok || len(spl.Msg) == 0 || spl.Msg[0] != '\t' {
+		for i++; i < len(errs); i++ {
+			spl := errs[i]
+			if len(spl.Msg) == 0 || spl.Msg[0] != '\t' {
 				break
 			}
-			enew.secondaries = append(enew.secondaries, spl)
+			spl.Msg = spl.Msg[1:]
+			original.secondaries = append(original.secondaries, spl)
 		}
-		errs[i] = enew
-		i = j
+
+		// Clone the error to all its related locations -- VS Code, at least,
+		// doesn't do it for us.
+		result = append(result, original)
+		for i, mainSecondary := range original.secondaries {
+			// Create the new primary error, with a tweaked message, in the
+			// secondary's location. We need to start from the secondary to
+			// capture its unexported location fields.
+			relocatedSecondary := mainSecondary
+			if supportsRelatedInformation {
+				relocatedSecondary.Msg = fmt.Sprintf("%v (see details)", original.primary.Msg)
+			} else {
+				relocatedSecondary.Msg = fmt.Sprintf("%v (this error: %v)", original.primary.Msg, mainSecondary.Msg)
+			}
+			relocatedSecondary.Soft = original.primary.Soft
+
+			// Copy over the secondary errors, noting the location of the
+			// current error we're cloning.
+			clonedError := extendedError{primary: relocatedSecondary, secondaries: []types.Error{original.primary}}
+			for j, secondary := range original.secondaries {
+				if i == j {
+					secondary.Msg += " (this error)"
+				}
+				clonedError.secondaries = append(clonedError.secondaries, secondary)
+			}
+			result = append(result, clonedError)
+		}
+
 	}
-	return errs
+	return result
 }
 
 // resolveImportPath resolves an import path in pkg to a package from deps.
diff --git a/internal/lsp/cache/error_test.go b/internal/lsp/cache/error_test.go
index d361e03..43cc03f 100644
--- a/internal/lsp/cache/error_test.go
+++ b/internal/lsp/cache/error_test.go
@@ -28,7 +28,7 @@
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			spn := parseGoListError(tt.in)
+			spn := parseGoListError(tt.in, ".")
 			fn := spn.URI().Filename()
 
 			if !strings.HasSuffix(fn, tt.expectedFileName) {
diff --git a/internal/lsp/cache/errors.go b/internal/lsp/cache/errors.go
index 148fbda..f18d5ed 100644
--- a/internal/lsp/cache/errors.go
+++ b/internal/lsp/cache/errors.go
@@ -14,156 +14,227 @@
 	"strconv"
 	"strings"
 
-	errors "golang.org/x/xerrors"
-
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/packages"
 	"golang.org/x/tools/internal/analysisinternal"
-	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/lsp/debug/tag"
+	"golang.org/x/tools/internal/lsp/command"
 	"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/typesinternal"
+	errors "golang.org/x/xerrors"
 )
 
-func sourceDiagnostics(ctx context.Context, snapshot *snapshot, pkg *pkg, severity protocol.DiagnosticSeverity, e interface{}) ([]*source.Diagnostic, error) {
-	fset := snapshot.view.session.cache.fset
-	var (
-		spn     span.Span
-		err     error
-		msg     string
-		code    typesinternal.ErrorCode
-		diagSrc source.DiagnosticSource
-		fixes   []source.SuggestedFix
-		related []source.RelatedInformation
-	)
-	switch e := e.(type) {
-	case packages.Error:
-		diagSrc = toDiagnosticSource(e.Kind)
-		var ok bool
-		if msg, spn, ok = parseGoListImportCycleError(ctx, snapshot, e, pkg); ok {
-			diagSrc = source.TypeError
-			break
+func goPackagesErrorDiagnostics(ctx context.Context, snapshot *snapshot, pkg *pkg, e packages.Error) ([]*source.Diagnostic, error) {
+	if msg, spn, ok := parseGoListImportCycleError(ctx, snapshot, e, pkg); ok {
+		rng, err := spanToRange(snapshot, pkg, spn)
+		if err != nil {
+			return nil, err
 		}
-		if e.Pos == "" {
-			spn = parseGoListError(e.Msg)
+		return []*source.Diagnostic{{
+			URI:      spn.URI(),
+			Range:    rng,
+			Severity: protocol.SeverityError,
+			Source:   source.TypeError,
+			Message:  msg,
+		}}, nil
+	}
 
-			// We may not have been able to parse a valid span.
-			if _, err := spanToRange(snapshot, pkg, spn); err != nil {
-				var diags []*source.Diagnostic
-				for _, cgf := range pkg.compiledGoFiles {
-					diags = append(diags, &source.Diagnostic{
-						URI:      cgf.URI,
-						Severity: severity,
-						Source:   diagSrc,
-						Message:  msg,
-					})
-				}
-				return diags, nil
+	var spn span.Span
+	if e.Pos == "" {
+		spn = parseGoListError(e.Msg, pkg.m.config.Dir)
+		// We may not have been able to parse a valid span. Apply the errors to all files.
+		if _, err := spanToRange(snapshot, pkg, spn); err != nil {
+			var diags []*source.Diagnostic
+			for _, cgf := range pkg.compiledGoFiles {
+				diags = append(diags, &source.Diagnostic{
+					URI:      cgf.URI,
+					Severity: protocol.SeverityError,
+					Source:   source.ListError,
+					Message:  e.Msg,
+				})
 			}
-		} else {
-			spn = span.Parse(e.Pos)
+			return diags, nil
 		}
-	case *scanner.Error:
-		msg = e.Msg
-		diagSrc = source.ParseError
-		spn, err = scannerErrorRange(snapshot, pkg, e.Pos)
-		if err != nil {
-			if ctx.Err() != nil {
-				return nil, ctx.Err()
-			}
-			event.Error(ctx, "no span for scanner.Error pos", err, tag.Package.Of(pkg.ID()))
-			spn = span.Parse(e.Pos.String())
-		}
+	} else {
+		spn = span.ParseInDir(e.Pos, pkg.m.config.Dir)
+	}
 
-	case scanner.ErrorList:
-		// The first parser error is likely the root cause of the problem.
-		if e.Len() <= 0 {
-			return nil, errors.Errorf("no errors in %v", e)
-		}
-		msg = e[0].Msg
-		diagSrc = source.ParseError
-		spn, err = scannerErrorRange(snapshot, pkg, e[0].Pos)
-		if err != nil {
-			if ctx.Err() != nil {
-				return nil, ctx.Err()
-			}
-			event.Error(ctx, "no span for scanner.Error pos", err, tag.Package.Of(pkg.ID()))
-			spn = span.Parse(e[0].Pos.String())
-		}
-	case types.Error:
-		msg = e.Msg
-		diagSrc = source.TypeError
-		if !e.Pos.IsValid() {
-			return nil, fmt.Errorf("invalid position for type error %v", e)
-		}
-		code, spn, err = typeErrorData(fset, pkg, e)
-		if err != nil {
-			return nil, err
-		}
-	case extendedError:
-		perr := e.primary
-		msg = perr.Msg
-		diagSrc = source.TypeError
-		if !perr.Pos.IsValid() {
-			return nil, fmt.Errorf("invalid position for type error %v", e)
-		}
-		code, spn, err = typeErrorData(fset, pkg, e.primary)
-		if err != nil {
-			return nil, err
-		}
-		for _, s := range e.secondaries {
-			var x source.RelatedInformation
-			x.Message = s.Msg
-			_, xspn, err := typeErrorData(fset, pkg, s)
-			if err != nil {
-				return nil, fmt.Errorf("invalid position for type error %v", s)
-			}
-			x.URI = xspn.URI()
-			rng, err := spanToRange(snapshot, pkg, xspn)
-			if err != nil {
-				return nil, err
-			}
-			x.Range = rng
-			related = append(related, x)
-		}
-	case *analysis.Diagnostic:
-		spn, err = span.NewRange(fset, e.Pos, e.End).Span()
-		if err != nil {
-			return nil, err
-		}
-		msg = e.Message
-		diagSrc = source.AnalyzerErrorKind(e.Category)
-		fixes, err = suggestedAnalysisFixes(snapshot, pkg, e)
-		if err != nil {
-			return nil, err
-		}
-		related, err = relatedInformation(snapshot, pkg, e)
-		if err != nil {
-			return nil, err
-		}
-	default:
-		panic(fmt.Sprintf("%T unexpected", e))
+	rng, err := spanToRange(snapshot, pkg, spn)
+	if err != nil {
+		return nil, err
+	}
+	return []*source.Diagnostic{{
+		URI:      spn.URI(),
+		Range:    rng,
+		Severity: protocol.SeverityError,
+		Source:   source.ListError,
+		Message:  e.Msg,
+	}}, nil
+}
+
+func parseErrorDiagnostics(ctx context.Context, snapshot *snapshot, pkg *pkg, errList scanner.ErrorList) ([]*source.Diagnostic, error) {
+	// The first parser error is likely the root cause of the problem.
+	if errList.Len() <= 0 {
+		return nil, errors.Errorf("no errors in %v", errList)
+	}
+	e := errList[0]
+	pgf, err := pkg.File(span.URIFromPath(e.Pos.Filename))
+	if err != nil {
+		return nil, err
+	}
+	pos := pgf.Tok.Pos(e.Pos.Offset)
+	spn, err := span.NewRange(snapshot.FileSet(), pos, pos).Span()
+	if err != nil {
+		return nil, err
 	}
 	rng, err := spanToRange(snapshot, pkg, spn)
 	if err != nil {
 		return nil, err
 	}
-	sd := &source.Diagnostic{
-		URI:            spn.URI(),
-		Range:          rng,
-		Severity:       severity,
-		Source:         diagSrc,
-		Message:        msg,
-		Related:        related,
-		SuggestedFixes: fixes,
+	return []*source.Diagnostic{{
+		URI:      spn.URI(),
+		Range:    rng,
+		Severity: protocol.SeverityError,
+		Source:   source.ParseError,
+		Message:  e.Msg,
+	}}, nil
+}
+
+var importErrorRe = regexp.MustCompile(`could not import ([^\s]+)`)
+
+func typeErrorDiagnostics(ctx context.Context, snapshot *snapshot, pkg *pkg, e extendedError) ([]*source.Diagnostic, error) {
+	code, spn, err := typeErrorData(snapshot.FileSet(), pkg, e.primary)
+	if err != nil {
+		return nil, err
+	}
+	rng, err := spanToRange(snapshot, pkg, spn)
+	if err != nil {
+		return nil, err
+	}
+	diag := &source.Diagnostic{
+		URI:      spn.URI(),
+		Range:    rng,
+		Severity: protocol.SeverityError,
+		Source:   source.TypeError,
+		Message:  e.primary.Msg,
 	}
 	if code != 0 {
-		sd.Code = code.String()
-		sd.CodeHref = typesCodeHref(snapshot, code)
+		diag.Code = code.String()
+		diag.CodeHref = typesCodeHref(snapshot, code)
 	}
-	return []*source.Diagnostic{sd}, nil
+
+	for _, secondary := range e.secondaries {
+		_, secondarySpan, err := typeErrorData(snapshot.FileSet(), pkg, secondary)
+		if err != nil {
+			return nil, err
+		}
+		rng, err := spanToRange(snapshot, pkg, secondarySpan)
+		if err != nil {
+			return nil, err
+		}
+		diag.Related = append(diag.Related, source.RelatedInformation{
+			URI:     secondarySpan.URI(),
+			Range:   rng,
+			Message: secondary.Msg,
+		})
+	}
+
+	if match := importErrorRe.FindStringSubmatch(e.primary.Msg); match != nil {
+		diag.SuggestedFixes, err = goGetQuickFixes(snapshot, spn.URI(), match[1])
+		if err != nil {
+			return nil, err
+		}
+	}
+	return []*source.Diagnostic{diag}, nil
+}
+
+func goGetQuickFixes(snapshot *snapshot, uri span.URI, pkg string) ([]source.SuggestedFix, error) {
+	// Go get only supports module mode for now.
+	if snapshot.workspaceMode()&moduleMode == 0 {
+		return nil, nil
+	}
+	title := fmt.Sprintf("go get package %v", pkg)
+	cmd, err := command.NewGoGetPackageCommand(title, command.GoGetPackageArgs{
+		URI:        protocol.URIFromSpanURI(uri),
+		AddRequire: true,
+		Pkg:        pkg,
+	})
+	if err != nil {
+		return nil, err
+	}
+	return []source.SuggestedFix{source.SuggestedFixFromCommand(cmd)}, nil
+}
+
+func analysisDiagnosticDiagnostics(ctx context.Context, snapshot *snapshot, pkg *pkg, a *analysis.Analyzer, e *analysis.Diagnostic) ([]*source.Diagnostic, error) {
+	var srcAnalyzer *source.Analyzer
+	// Find the analyzer that generated this diagnostic.
+	for _, sa := range source.EnabledAnalyzers(snapshot) {
+		if a == sa.Analyzer {
+			srcAnalyzer = sa
+			break
+		}
+	}
+
+	spn, err := span.NewRange(snapshot.FileSet(), e.Pos, e.End).Span()
+	if err != nil {
+		return nil, err
+	}
+	rng, err := spanToRange(snapshot, pkg, spn)
+	if err != nil {
+		return nil, err
+	}
+	fixes, err := suggestedAnalysisFixes(snapshot, pkg, e)
+	if err != nil {
+		return nil, err
+	}
+	if srcAnalyzer.Fix != "" {
+		cmd, err := command.NewApplyFixCommand(e.Message, command.ApplyFixArgs{
+			URI:   protocol.URIFromSpanURI(spn.URI()),
+			Range: rng,
+			Fix:   srcAnalyzer.Fix,
+		})
+		if err != nil {
+			return nil, err
+		}
+		fixes = append(fixes, source.SuggestedFixFromCommand(cmd))
+	}
+	related, err := relatedInformation(snapshot, pkg, e)
+	if err != nil {
+		return nil, err
+	}
+	diag := &source.Diagnostic{
+		URI:            spn.URI(),
+		Range:          rng,
+		Severity:       protocol.SeverityWarning,
+		Source:         source.AnalyzerErrorKind(e.Category),
+		Message:        e.Message,
+		Related:        related,
+		SuggestedFixes: fixes,
+		Analyzer:       srcAnalyzer,
+	}
+	// If the fixes only delete code, assume that the diagnostic is reporting dead code.
+	if onlyDeletions(fixes) {
+		diag.Tags = []protocol.DiagnosticTag{protocol.Unnecessary}
+	}
+	return []*source.Diagnostic{diag}, nil
+}
+
+// onlyDeletions returns true if all of the suggested fixes are deletions.
+func onlyDeletions(fixes []source.SuggestedFix) bool {
+	for _, fix := range fixes {
+		for _, edits := range fix.Edits {
+			for _, edit := range edits {
+				if edit.NewText != "" {
+					return false
+				}
+				if protocol.ComparePosition(edit.Range.Start, edit.Range.End) == 0 {
+					return false
+				}
+			}
+		}
+	}
+	return len(fixes) > 0
 }
 
 func typesCodeHref(snapshot *snapshot, code typesinternal.ErrorCode) string {
@@ -217,19 +288,6 @@
 	return out, nil
 }
 
-func toDiagnosticSource(kind packages.ErrorKind) source.DiagnosticSource {
-	switch kind {
-	case packages.ListError:
-		return source.ListError
-	case packages.ParseError:
-		return source.ParseError
-	case packages.TypeError:
-		return source.TypeError
-	default:
-		return source.UnknownError
-	}
-}
-
 func typeErrorData(fset *token.FileSet, pkg *pkg, terr types.Error) (typesinternal.ErrorCode, span.Span, error) {
 	ecode, start, end, ok := typesinternal.ReadGo116ErrorData(terr)
 	if !ok {
@@ -255,16 +313,6 @@
 	return span.FileSpan(pgf.Tok, pgf.Mapper.Converter, start, end)
 }
 
-func scannerErrorRange(snapshot *snapshot, pkg *pkg, posn token.Position) (span.Span, error) {
-	fset := snapshot.view.session.cache.fset
-	pgf, err := pkg.File(span.URIFromPath(posn.Filename))
-	if err != nil {
-		return span.Span{}, err
-	}
-	pos := pgf.Tok.Pos(posn.Offset)
-	return span.NewRange(fset, pos, pos).Span()
-}
-
 // spanToRange converts a span.Span to a protocol.Range,
 // assuming that the span belongs to the package whose diagnostics are being computed.
 func spanToRange(snapshot *snapshot, pkg *pkg, spn span.Span) (protocol.Range, error) {
@@ -283,13 +331,13 @@
 //
 //   attributes.go:13:1: expected 'package', found 'type'
 //
-func parseGoListError(input string) span.Span {
+func parseGoListError(input, wd string) span.Span {
 	input = strings.TrimSpace(input)
 	msgIndex := strings.Index(input, ": ")
 	if msgIndex < 0 {
 		return span.Parse(input)
 	}
-	return span.Parse(input[:msgIndex])
+	return span.ParseInDir(input[:msgIndex], wd)
 }
 
 func parseGoListImportCycleError(ctx context.Context, snapshot *snapshot, e packages.Error, pkg *pkg) (string, span.Span, bool) {
diff --git a/internal/lsp/cache/load.go b/internal/lsp/cache/load.go
index 8914d0d..f958a56 100644
--- a/internal/lsp/cache/load.go
+++ b/internal/lsp/cache/load.go
@@ -372,12 +372,21 @@
 		name:       packageName(pkg.Name),
 		forTest:    packagePath(packagesinternal.GetForTest(pkg)),
 		typesSizes: pkg.TypesSizes,
-		errors:     pkg.Errors,
 		config:     cfg,
 		module:     pkg.Module,
 		depsErrors: packagesinternal.GetDepsErrors(pkg),
 	}
 
+	for _, err := range pkg.Errors {
+		// Filter out parse errors from go list. We'll get them when we
+		// actually parse, and buggy overlay support may generate spurious
+		// errors. (See TestNewModule_Issue38207.)
+		if strings.Contains(err.Msg, "expected '") {
+			continue
+		}
+		m.errors = append(m.errors, err)
+	}
+
 	for _, filename := range pkg.CompiledGoFiles {
 		uri := span.URIFromPath(filename)
 		m.compiledGoFiles = append(m.compiledGoFiles, uri)
diff --git a/internal/lsp/cache/parse.go b/internal/lsp/cache/parse.go
index edbd519..e133235 100644
--- a/internal/lsp/cache/parse.go
+++ b/internal/lsp/cache/parse.go
@@ -246,7 +246,7 @@
 	if fh.Kind() != source.Go {
 		return &parseGoData{err: errors.Errorf("cannot parse non-Go file %s", fh.URI())}
 	}
-	buf, err := fh.Read()
+	src, err := fh.Read()
 	if err != nil {
 		return &parseGoData{err: err}
 	}
@@ -255,37 +255,32 @@
 	if mode == source.ParseHeader {
 		parserMode = parser.ImportsOnly | parser.ParseComments
 	}
-	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 {
-			tok = fset.AddFile(fh.URI().Filename(), -1, len(buf))
-			tok.SetLinesForContent(buf)
-			return &parseGoData{
-				parsed: &source.ParsedGoFile{
-					URI:  fh.URI(),
-					Mode: mode,
-					Src:  buf,
-					File: file,
-					Tok:  tok,
-					Mapper: &protocol.ColumnMapper{
-						URI:       fh.URI(),
-						Content:   buf,
-						Converter: span.NewTokenConverter(fset, tok),
-					},
-					ParseErr: parseError,
-				},
-			}
-		}
 
+	file, err := parser.ParseFile(fset, fh.URI().Filename(), src, parserMode)
+	var parseErr scanner.ErrorList
+	if err != nil {
+		// We passed a byte slice, so the only possible error is a parse error.
+		parseErr = err.(scanner.ErrorList)
+	}
+
+	tok := fset.File(file.Pos())
+	if tok == nil {
+		// file.Pos is the location of the package declaration. If there was
+		// none, we can't find the token.File that ParseFile created, and we
+		// have no choice but to recreate it.
+		tok = fset.AddFile(fh.URI().Filename(), -1, len(src))
+		tok.SetLinesForContent(src)
+	}
+
+	fixed := false
+	// If there were parse errors, attempt to fix them up.
+	if parseErr != nil {
 		// Fix any badly parsed parts of the AST.
-		fixed = fixAST(ctx, file, tok, buf)
+		fixed = fixAST(ctx, file, tok, src)
 
 		for i := 0; i < 10; i++ {
 			// Fix certain syntax errors that render the file unparseable.
-			newSrc := fixSrc(file, tok, buf)
+			newSrc := fixSrc(file, tok, src)
 			if newSrc == nil {
 				break
 			}
@@ -294,11 +289,11 @@
 			// it is likely we got stuck in a loop somehow. Log out a diff
 			// of the last changes we made to aid in debugging.
 			if i == 9 {
-				edits, err := myers.ComputeEdits(fh.URI(), string(buf), string(newSrc))
+				edits, err := myers.ComputeEdits(fh.URI(), string(src), string(newSrc))
 				if err != nil {
 					event.Error(ctx, "error generating fixSrc diff", err, tag.File.Of(tok.Name()))
 				} else {
-					unified := diff.ToUnified("before", "after", string(buf), edits)
+					unified := diff.ToUnified("before", "after", string(src), edits)
 					event.Log(ctx, fmt.Sprintf("fixSrc loop - last diff:\n%v", unified), tag.File.Of(tok.Name()))
 				}
 			}
@@ -307,44 +302,33 @@
 			if newFile != nil {
 				// Maintain the original parseError so we don't try formatting the doctored file.
 				file = newFile
-				buf = newSrc
+				src = newSrc
 				tok = fset.File(file.Pos())
 
-				fixed = fixAST(ctx, file, tok, buf)
+				fixed = fixAST(ctx, file, tok, src)
 			}
-
-		}
-
-		if mode == source.ParseExported {
-			trimAST(file)
 		}
 	}
 
-	// A missing file is always an error; use the parse error or make one up if there isn't one.
-	if file == nil {
-		if parseError == nil {
-			parseError = errors.Errorf("parsing %s failed with no parse error reported", fh.URI())
-		}
-		err = parseError
-	}
-	m := &protocol.ColumnMapper{
-		URI:       fh.URI(),
-		Converter: span.NewTokenConverter(fset, tok),
-		Content:   buf,
+	if mode == source.ParseExported {
+		trimAST(file)
 	}
 
 	return &parseGoData{
 		parsed: &source.ParsedGoFile{
-			URI:      fh.URI(),
-			Mode:     mode,
-			Src:      buf,
-			File:     file,
-			Tok:      tok,
-			Mapper:   m,
-			ParseErr: parseError,
+			URI:  fh.URI(),
+			Mode: mode,
+			Src:  src,
+			File: file,
+			Tok:  tok,
+			Mapper: &protocol.ColumnMapper{
+				URI:       fh.URI(),
+				Converter: span.NewTokenConverter(fset, tok),
+				Content:   src,
+			},
+			ParseErr: parseErr,
 		},
 		fixed: fixed,
-		err:   err,
 	}
 }
 
diff --git a/internal/lsp/cache/pkg.go b/internal/lsp/cache/pkg.go
index 66f3e1a..69b3e3f 100644
--- a/internal/lsp/cache/pkg.go
+++ b/internal/lsp/cache/pkg.go
@@ -16,17 +16,19 @@
 
 // pkg contains the type information needed by the source package.
 type pkg struct {
-	m               *metadata
-	mode            source.ParseMode
-	goFiles         []*source.ParsedGoFile
-	compiledGoFiles []*source.ParsedGoFile
-	diagnostics     []*source.Diagnostic
-	imports         map[packagePath]*pkg
-	version         *module.Version
-	typeErrors      []types.Error
-	types           *types.Package
-	typesInfo       *types.Info
-	typesSizes      types.Sizes
+	m                    *metadata
+	mode                 source.ParseMode
+	goFiles              []*source.ParsedGoFile
+	compiledGoFiles      []*source.ParsedGoFile
+	diagnostics          []*source.Diagnostic
+	imports              map[packagePath]*pkg
+	version              *module.Version
+	typeErrors           []types.Error
+	types                *types.Package
+	typesInfo            *types.Info
+	typesSizes           types.Sizes
+	hasListOrParseErrors bool
+	hasTypeErrors        bool
 }
 
 // Declare explicit types for package paths, names, and IDs to ensure that we
@@ -84,10 +86,6 @@
 	return syntax
 }
 
-func (p *pkg) GetDiagnostics() []*source.Diagnostic {
-	return p.diagnostics
-}
-
 func (p *pkg) GetTypes() *types.Package {
 	return p.types
 }
@@ -146,3 +144,11 @@
 func (p *pkg) Version() *module.Version {
 	return p.version
 }
+
+func (p *pkg) HasListOrParseErrors() bool {
+	return p.hasListOrParseErrors
+}
+
+func (p *pkg) HasTypeErrors() bool {
+	return p.hasTypeErrors
+}
diff --git a/internal/lsp/cache/snapshot.go b/internal/lsp/cache/snapshot.go
index ddfefc7..b80d518 100644
--- a/internal/lsp/cache/snapshot.go
+++ b/internal/lsp/cache/snapshot.go
@@ -15,6 +15,7 @@
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"regexp"
 	"sort"
 	"strconv"
 	"strings"
@@ -1060,7 +1061,7 @@
 		return nil
 	}
 
-	if strings.Contains(loadErr.MainError.Error(), "cannot find main module") {
+	if errMsg := loadErr.MainError.Error(); strings.Contains(errMsg, "cannot find main module") || strings.Contains(errMsg, "go.mod file not found") {
 		return s.workspaceLayoutError(ctx)
 	}
 	return loadErr
@@ -1680,9 +1681,38 @@
 		}
 		return true, false
 	}
+
+	// Re-evaluate build constraints and embed patterns. It would be preferable
+	// to only do this on save, but we don't have the prior versions accessible.
+	oldComments := extractMagicComments(original.File)
+	newComments := extractMagicComments(current.File)
+	if len(oldComments) != len(newComments) {
+		return true, false
+	}
+	for i := range oldComments {
+		if oldComments[i] != newComments[i] {
+			return true, false
+		}
+	}
+
 	return false, false
 }
 
+var buildConstraintOrEmbedRe = regexp.MustCompile(`^//(go:embed|go:build|\s*\+build).*`)
+
+// extractMagicComments finds magic comments that affect metadata in f.
+func extractMagicComments(f *ast.File) []string {
+	var results []string
+	for _, cg := range f.Comments {
+		for _, c := range cg.List {
+			if buildConstraintOrEmbedRe.MatchString(c.Text) {
+				results = append(results, c.Text)
+			}
+		}
+	}
+	return results
+}
+
 func (s *snapshot) BuiltinPackage(ctx context.Context) (*source.BuiltinPackage, error) {
 	s.AwaitInitialized(ctx)
 
diff --git a/internal/lsp/cmd/cmd.go b/internal/lsp/cmd/cmd.go
index fd9d6f9..228a713 100644
--- a/internal/lsp/cmd/cmd.go
+++ b/internal/lsp/cmd/cmd.go
@@ -218,7 +218,7 @@
 		if app.options != nil {
 			app.options(opts)
 		}
-		key := fmt.Sprintf("%s %v", app.wd, opts)
+		key := fmt.Sprintf("%s %v %v %v", app.wd, opts.PreferredContentFormat, opts.HierarchicalDocumentSymbolSupport, opts.SymbolMatcher)
 		if c := internalConnections[key]; c != nil {
 			return c, nil
 		}
@@ -278,6 +278,7 @@
 	if options != nil {
 		options(opts)
 	}
+	// If you add an additional option here, you must update the map key in connect.
 	params.Capabilities.TextDocument.Hover = protocol.HoverClientCapabilities{
 		ContentFormat: []protocol.MarkupKind{opts.PreferredContentFormat},
 	}
diff --git a/internal/lsp/code_action.go b/internal/lsp/code_action.go
index 6c41dc3..9b76f3f 100644
--- a/internal/lsp/code_action.go
+++ b/internal/lsp/code_action.go
@@ -10,7 +10,6 @@
 	"sort"
 	"strings"
 
-	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/internal/event"
 	"golang.org/x/tools/internal/imports"
 	"golang.org/x/tools/internal/lsp/command"
@@ -63,14 +62,18 @@
 	switch fh.Kind() {
 	case source.Mod:
 		if diagnostics := params.Context.Diagnostics; len(diagnostics) > 0 {
-			modQuickFixes, err := moduleQuickFixes(ctx, snapshot, fh, diagnostics)
+			diags, err := mod.DiagnosticsForMod(ctx, snapshot, fh)
 			if source.IsNonFatalGoModError(err) {
 				return nil, nil
 			}
 			if err != nil {
 				return nil, err
 			}
-			codeActions = append(codeActions, modQuickFixes...)
+			quickFixes, err := codeActionsMatchingDiagnostics(ctx, snapshot, diagnostics, diags)
+			if err != nil {
+				return nil, err
+			}
+			codeActions = append(codeActions, quickFixes...)
 		}
 	case source.Go:
 		// Don't suggest fixes for generated files, since they are generally
@@ -125,50 +128,68 @@
 		if err != nil {
 			return nil, err
 		}
-		if (wanted[protocol.QuickFix] || wanted[protocol.SourceFixAll]) && len(diagnostics) > 0 {
-			pkgQuickFixes, err := quickFixesForDiagnostics(ctx, snapshot, diagnostics, pkg.GetDiagnostics())
-			if err != nil {
-				return nil, err
-			}
-			codeActions = append(codeActions, pkgQuickFixes...)
-			analysisQuickFixes, highConfidenceEdits, err := analysisFixes(ctx, snapshot, pkg, diagnostics)
-			if err != nil {
-				return nil, err
-			}
-			if wanted[protocol.QuickFix] {
-				// Add the quick fixes reported by go/analysis.
-				codeActions = append(codeActions, analysisQuickFixes...)
 
-				// If there are any diagnostics relating to the go.mod file,
-				// add their corresponding quick fixes.
-				modQuickFixes, err := moduleQuickFixes(ctx, snapshot, fh, diagnostics)
-				if source.IsNonFatalGoModError(err) {
-					// Not a fatal error.
-					event.Error(ctx, "module suggested fixes failed", err, tag.Directory.Of(snapshot.View().Folder()))
-				}
-				codeActions = append(codeActions, modQuickFixes...)
-			}
-			if wanted[protocol.SourceFixAll] && len(highConfidenceEdits) > 0 {
-				codeActions = append(codeActions, protocol.CodeAction{
-					Title: "Simplifications",
-					Kind:  protocol.SourceFixAll,
-					Edit: protocol.WorkspaceEdit{
-						DocumentChanges: highConfidenceEdits,
-					},
-				})
-			}
+		pkgDiagnostics, err := snapshot.DiagnosePackage(ctx, pkg)
+		if err != nil {
+			return nil, err
 		}
-		if ctx.Err() != nil {
-			return nil, ctx.Err()
+		analysisDiags, err := source.Analyze(ctx, snapshot, pkg, true)
+		if err != nil {
+			return nil, err
 		}
-		// Add any suggestions that do not necessarily fix any diagnostics.
-		if wanted[protocol.RefactorRewrite] {
-			fixes, err := convenienceFixes(ctx, snapshot, pkg, uri, params.Range)
+		fileDiags := append(pkgDiagnostics[uri], analysisDiags[uri]...)
+		modURI := snapshot.GoModForFile(fh.URI())
+		if modURI != "" {
+			modFH, err := snapshot.GetVersionedFile(ctx, modURI)
 			if err != nil {
 				return nil, err
 			}
-			codeActions = append(codeActions, fixes...)
+			modDiags, err := mod.DiagnosticsForMod(ctx, snapshot, modFH)
+			if err != nil && !source.IsNonFatalGoModError(err) {
+				// Not a fatal error.
+				event.Error(ctx, "module suggested fixes failed", err, tag.Directory.Of(snapshot.View().Folder()))
+			}
+			fileDiags = append(fileDiags, modDiags...)
 		}
+
+		// Split diagnostics into fixes, which must match incoming diagnostics,
+		// and non-fixes, which must match the requested range. Build actions
+		// for all of them.
+		var fixDiags, nonFixDiags []*source.Diagnostic
+		for _, d := range fileDiags {
+			if len(d.SuggestedFixes) == 0 {
+				continue
+			}
+			kind := protocol.QuickFix
+			if d.Analyzer != nil && d.Analyzer.ActionKind != "" {
+				kind = d.Analyzer.ActionKind
+			}
+			if kind == protocol.QuickFix || kind == protocol.SourceFixAll {
+				fixDiags = append(fixDiags, d)
+			} else {
+				nonFixDiags = append(nonFixDiags, d)
+			}
+		}
+
+		fixActions, err := codeActionsMatchingDiagnostics(ctx, snapshot, diagnostics, fixDiags)
+		if err != nil {
+			return nil, err
+		}
+		codeActions = append(codeActions, fixActions...)
+
+		for _, nonfix := range nonFixDiags {
+			// For now, only show diagnostics for matching lines. Maybe we should
+			// alter this behavior in the future, depending on the user experience.
+			if !protocol.Intersect(nonfix.Range, params.Range) {
+				continue
+			}
+			actions, err := codeActionsForDiagnostic(ctx, snapshot, nonfix, nil)
+			if err != nil {
+				return nil, err
+			}
+			codeActions = append(codeActions, actions...)
+		}
+
 		if wanted[protocol.RefactorExtract] {
 			fixes, err := extractionFixes(ctx, snapshot, pkg, uri, params.Range)
 			if err != nil {
@@ -189,7 +210,14 @@
 		// Unsupported file kind for a code action.
 		return nil, nil
 	}
-	return codeActions, nil
+
+	var filtered []protocol.CodeAction
+	for _, action := range codeActions {
+		if wanted[action.Kind] {
+			filtered = append(filtered, action)
+		}
+	}
+	return filtered, nil
 }
 
 func (s *Server) getSupportedCodeActions() []protocol.CodeActionKind {
@@ -250,179 +278,6 @@
 	return results
 }
 
-func analysisFixes(ctx context.Context, snapshot source.Snapshot, pkg source.Package, diagnostics []protocol.Diagnostic) ([]protocol.CodeAction, []protocol.TextDocumentEdit, error) {
-	if len(diagnostics) == 0 {
-		return nil, nil, nil
-	}
-	var (
-		codeActions       []protocol.CodeAction
-		sourceFixAllEdits []protocol.TextDocumentEdit
-	)
-	for _, diag := range diagnostics {
-		srcErr, analyzer, ok := findDiagnostic(ctx, snapshot, pkg.ID(), diag)
-		if !ok {
-			continue
-		}
-		// If the suggested fix for the diagnostic is expected to be separate,
-		// see if there are any supported commands available.
-		if analyzer.Fix != "" {
-			action, err := diagnosticToCommandCodeAction(ctx, snapshot, srcErr, &diag, protocol.QuickFix)
-			if err != nil {
-				return nil, nil, err
-			}
-			codeActions = append(codeActions, *action)
-			continue
-		}
-		for _, fix := range srcErr.SuggestedFixes {
-			action := protocol.CodeAction{
-				Title:       fix.Title,
-				Kind:        protocol.QuickFix,
-				Diagnostics: []protocol.Diagnostic{diag},
-				Edit:        protocol.WorkspaceEdit{},
-			}
-			for uri, edits := range fix.Edits {
-				fh, err := snapshot.GetVersionedFile(ctx, uri)
-				if err != nil {
-					return nil, nil, err
-				}
-				docChanges := documentChanges(fh, edits)
-				if analyzer.HighConfidence {
-					sourceFixAllEdits = append(sourceFixAllEdits, docChanges...)
-				}
-				action.Edit.DocumentChanges = append(action.Edit.DocumentChanges, docChanges...)
-			}
-			codeActions = append(codeActions, action)
-		}
-	}
-	return codeActions, sourceFixAllEdits, nil
-}
-
-func findDiagnostic(ctx context.Context, snapshot source.Snapshot, pkgID string, diag protocol.Diagnostic) (*source.Diagnostic, source.Analyzer, bool) {
-	analyzer := diagnosticToAnalyzer(snapshot, diag.Source, diag.Message)
-	if analyzer == nil {
-		return nil, source.Analyzer{}, false
-	}
-	analysisErrors, err := snapshot.Analyze(ctx, pkgID, analyzer.Analyzer)
-	if err != nil {
-		return nil, source.Analyzer{}, false
-	}
-	for _, err := range analysisErrors {
-		if err.Message != diag.Message {
-			continue
-		}
-		if protocol.CompareRange(err.Range, diag.Range) != 0 {
-			continue
-		}
-		if string(err.Source) != analyzer.Analyzer.Name {
-			continue
-		}
-		// The error matches.
-		return err, *analyzer, true
-	}
-	return nil, source.Analyzer{}, false
-}
-
-// diagnosticToAnalyzer return the analyzer associated with a given diagnostic.
-// It assumes that the diagnostic's source will be the name of the analyzer.
-// If this changes, this approach will need to be reworked.
-func diagnosticToAnalyzer(snapshot source.Snapshot, src, msg string) (analyzer *source.Analyzer) {
-	// Make sure that the analyzer we found is enabled.
-	defer func() {
-		if analyzer != nil && !analyzer.IsEnabled(snapshot.View()) {
-			analyzer = nil
-		}
-	}()
-	if a, ok := snapshot.View().Options().DefaultAnalyzers[src]; ok {
-		return &a
-	}
-	if a, ok := snapshot.View().Options().StaticcheckAnalyzers[src]; ok {
-		return &a
-	}
-	if a, ok := snapshot.View().Options().ConvenienceAnalyzers[src]; ok {
-		return &a
-	}
-	// Hack: We publish diagnostics with the source "compiler" for type errors,
-	// but these analyzers have different names. Try both possibilities.
-	if a, ok := snapshot.View().Options().TypeErrorAnalyzers[src]; ok {
-		return &a
-	}
-	if src != "compiler" {
-		return nil
-	}
-	for _, a := range snapshot.View().Options().TypeErrorAnalyzers {
-		if a.FixesError(msg) {
-			return &a
-		}
-	}
-	return nil
-}
-
-func convenienceFixes(ctx context.Context, snapshot source.Snapshot, pkg source.Package, uri span.URI, rng protocol.Range) ([]protocol.CodeAction, error) {
-	var analyzers []*analysis.Analyzer
-	for _, a := range snapshot.View().Options().ConvenienceAnalyzers {
-		if !a.IsEnabled(snapshot.View()) {
-			continue
-		}
-		if a.Fix == "" {
-			event.Error(ctx, "convenienceFixes", fmt.Errorf("no suggested fixes for convenience analyzer %s", a.Analyzer.Name))
-			continue
-		}
-		analyzers = append(analyzers, a.Analyzer)
-	}
-	diagnostics, err := snapshot.Analyze(ctx, pkg.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 !protocol.Intersect(d.Range, rng) {
-			continue
-		}
-		action, err := diagnosticToCommandCodeAction(ctx, snapshot, d, nil, protocol.RefactorRewrite)
-		if err != nil {
-			return nil, err
-		}
-		codeActions = append(codeActions, *action)
-	}
-	return codeActions, nil
-}
-
-func diagnosticToCommandCodeAction(ctx context.Context, snapshot source.Snapshot, sd *source.Diagnostic, pd *protocol.Diagnostic, kind protocol.CodeActionKind) (*protocol.CodeAction, error) {
-	// The fix depends on the category of the analyzer. The diagnostic may be
-	// nil, so use the error's category.
-	analyzer := diagnosticToAnalyzer(snapshot, string(sd.Source), sd.Message)
-	if analyzer == nil {
-		return nil, fmt.Errorf("no convenience analyzer for source %s", sd.Source)
-	}
-	if analyzer.Fix == "" {
-		return nil, fmt.Errorf("no fix for convenience analyzer %s", analyzer.Analyzer.Name)
-	}
-	cmd, err := command.NewApplyFixCommand(sd.Message, command.ApplyFixArgs{
-		URI:   protocol.URIFromSpanURI(sd.URI),
-		Range: sd.Range,
-		Fix:   analyzer.Fix,
-	})
-	if err != nil {
-		return nil, err
-	}
-	var diagnostics []protocol.Diagnostic
-	if pd != nil {
-		diagnostics = append(diagnostics, *pd)
-	}
-	return &protocol.CodeAction{
-		Title:       sd.Message,
-		Kind:        kind,
-		Diagnostics: diagnostics,
-		Command:     &cmd,
-	}, nil
-}
-
 func extractionFixes(ctx context.Context, snapshot source.Snapshot, pkg source.Package, uri span.URI, rng protocol.Range) ([]protocol.CodeAction, error) {
 	if rng.Start == rng.End {
 		return nil, nil
@@ -488,70 +343,63 @@
 	}
 }
 
-func moduleQuickFixes(ctx context.Context, snapshot source.Snapshot, fh source.VersionedFileHandle, pdiags []protocol.Diagnostic) ([]protocol.CodeAction, error) {
-	var modFH source.VersionedFileHandle
-	switch fh.Kind() {
-	case source.Mod:
-		modFH = fh
-	case source.Go:
-		modURI := snapshot.GoModForFile(fh.URI())
-		if modURI == "" {
-			return nil, nil
-		}
-		var err error
-		modFH, err = snapshot.GetVersionedFile(ctx, modURI)
-		if err != nil {
-			return nil, err
-		}
-	}
-	diags, err := mod.DiagnosticsForMod(ctx, snapshot, modFH)
-	if err != nil {
-		return nil, err
-	}
-	return quickFixesForDiagnostics(ctx, snapshot, pdiags, diags)
-}
-
-func quickFixesForDiagnostics(ctx context.Context, snapshot source.Snapshot, pdiags []protocol.Diagnostic, sdiags []*source.Diagnostic) ([]protocol.CodeAction, error) {
-	var quickFixes []protocol.CodeAction
-	for _, e := range sdiags {
+func codeActionsMatchingDiagnostics(ctx context.Context, snapshot source.Snapshot, pdiags []protocol.Diagnostic, sdiags []*source.Diagnostic) ([]protocol.CodeAction, error) {
+	var actions []protocol.CodeAction
+	for _, sd := range sdiags {
 		var diag *protocol.Diagnostic
-		for _, d := range pdiags {
-			if sameDiagnostic(d, e) {
-				diag = &d
+		for _, pd := range pdiags {
+			if sameDiagnostic(pd, sd) {
+				diag = &pd
 				break
 			}
 		}
 		if diag == nil {
 			continue
 		}
-		for _, fix := range e.SuggestedFixes {
-			action := protocol.CodeAction{
-				Title:       fix.Title,
-				Kind:        protocol.QuickFix,
-				Diagnostics: []protocol.Diagnostic{*diag},
-				Edit:        protocol.WorkspaceEdit{},
-				Command:     fix.Command,
-			}
-
-			for uri, edits := range fix.Edits {
-				fh, err := snapshot.GetVersionedFile(ctx, uri)
-				if err != nil {
-					return nil, err
-				}
-				action.Edit.DocumentChanges = append(action.Edit.DocumentChanges, protocol.TextDocumentEdit{
-					TextDocument: protocol.OptionalVersionedTextDocumentIdentifier{
-						Version: fh.Version(),
-						TextDocumentIdentifier: protocol.TextDocumentIdentifier{
-							URI: protocol.URIFromSpanURI(uri),
-						},
-					},
-					Edits: edits,
-				})
-			}
-			quickFixes = append(quickFixes, action)
+		diagActions, err := codeActionsForDiagnostic(ctx, snapshot, sd, diag)
+		if err != nil {
+			return nil, err
 		}
+		actions = append(actions, diagActions...)
+
 	}
-	return quickFixes, nil
+	return actions, nil
+}
+
+func codeActionsForDiagnostic(ctx context.Context, snapshot source.Snapshot, sd *source.Diagnostic, pd *protocol.Diagnostic) ([]protocol.CodeAction, error) {
+	var actions []protocol.CodeAction
+	for _, fix := range sd.SuggestedFixes {
+		action := protocol.CodeAction{
+			Title:   fix.Title,
+			Kind:    protocol.QuickFix,
+			Edit:    protocol.WorkspaceEdit{},
+			Command: fix.Command,
+		}
+		if pd != nil {
+			action.Diagnostics = []protocol.Diagnostic{*pd}
+		}
+		if sd.Analyzer != nil && sd.Analyzer.ActionKind != "" {
+			action.Kind = sd.Analyzer.ActionKind
+		}
+
+		for uri, edits := range fix.Edits {
+			fh, err := snapshot.GetVersionedFile(ctx, uri)
+			if err != nil {
+				return nil, err
+			}
+			action.Edit.DocumentChanges = append(action.Edit.DocumentChanges, protocol.TextDocumentEdit{
+				TextDocument: protocol.OptionalVersionedTextDocumentIdentifier{
+					Version: fh.Version(),
+					TextDocumentIdentifier: protocol.TextDocumentIdentifier{
+						URI: protocol.URIFromSpanURI(uri),
+					},
+				},
+				Edits: edits,
+			})
+		}
+		actions = append(actions, action)
+	}
+	return actions, nil
 }
 
 func sameDiagnostic(pd protocol.Diagnostic, sd *source.Diagnostic) bool {
diff --git a/internal/lsp/command.go b/internal/lsp/command.go
index edcff12..1960270 100644
--- a/internal/lsp/command.go
+++ b/internal/lsp/command.go
@@ -613,13 +613,16 @@
 		progress:    "Toggling GC Details",
 		forURI:      args.URI,
 	}, func(ctx context.Context, deps commandDeps) error {
-		pkgDir := span.URIFromPath(filepath.Dir(args.URI.SpanURI().Filename()))
+		pkg, err := deps.snapshot.PackageForFile(ctx, deps.fh.URI(), source.TypecheckWorkspace, source.NarrowestPackage)
+		if err != nil {
+			return err
+		}
 		c.s.gcOptimizationDetailsMu.Lock()
-		if _, ok := c.s.gcOptimizationDetails[pkgDir]; ok {
-			delete(c.s.gcOptimizationDetails, pkgDir)
+		if _, ok := c.s.gcOptimizationDetails[pkg.ID()]; ok {
+			delete(c.s.gcOptimizationDetails, pkg.ID())
 			c.s.clearDiagnosticSource(gcDetailsSource)
 		} else {
-			c.s.gcOptimizationDetails[pkgDir] = struct{}{}
+			c.s.gcOptimizationDetails[pkg.ID()] = struct{}{}
 		}
 		c.s.gcOptimizationDetailsMu.Unlock()
 		c.s.diagnoseSnapshot(deps.snapshot, nil, false)
diff --git a/internal/lsp/debug/info.go b/internal/lsp/debug/info.go
index 7aee82f..018c754 100644
--- a/internal/lsp/debug/info.go
+++ b/internal/lsp/debug/info.go
@@ -26,7 +26,7 @@
 )
 
 // Version is a manually-updated mechanism for tracking versions.
-const Version = "v0.6.6"
+const Version = "v0.6.7"
 
 // ServerVersion is the format used by gopls to report its version to the
 // client. This format is structured so that the client can parse it easily.
diff --git a/internal/lsp/diagnostics.go b/internal/lsp/diagnostics.go
index 59e6070..6b75feb 100644
--- a/internal/lsp/diagnostics.go
+++ b/internal/lsp/diagnostics.go
@@ -245,41 +245,43 @@
 func (s *Server) diagnosePkg(ctx context.Context, snapshot source.Snapshot, pkg source.Package, alwaysAnalyze bool) {
 	ctx, done := event.Start(ctx, "Server.diagnosePkg", tag.Snapshot.Of(snapshot.ID()), tag.Package.Of(pkg.ID()))
 	defer done()
+	enableDiagnostics := false
 	includeAnalysis := alwaysAnalyze // only run analyses for packages with open files
-	var gcDetailsDir span.URI        // find the package's optimization details, if available
 	for _, pgf := range pkg.CompiledGoFiles() {
-		if snapshot.IsOpen(pgf.URI) {
-			includeAnalysis = true
+		enableDiagnostics = enableDiagnostics || !snapshot.IgnoredFile(pgf.URI)
+		includeAnalysis = includeAnalysis || snapshot.IsOpen(pgf.URI)
+	}
+	// Don't show any diagnostics on ignored files.
+	if !enableDiagnostics {
+		return
+	}
+
+	pkgDiagnostics, err := snapshot.DiagnosePackage(ctx, pkg)
+	if err != nil {
+		event.Error(ctx, "warning: diagnosing package", err, tag.Snapshot.Of(snapshot.ID()), tag.Package.Of(pkg.ID()))
+		return
+	}
+	for _, cgf := range pkg.CompiledGoFiles() {
+		s.storeDiagnostics(snapshot, cgf.URI, typeCheckSource, pkgDiagnostics[cgf.URI])
+	}
+	if includeAnalysis && !pkg.HasListOrParseErrors() {
+		reports, err := source.Analyze(ctx, snapshot, pkg, false)
+		if err != nil {
+			event.Error(ctx, "warning: analyzing package", err, tag.Snapshot.Of(snapshot.ID()), tag.Package.Of(pkg.ID()))
+			return
 		}
-		if gcDetailsDir == "" {
-			dirURI := span.URIFromPath(filepath.Dir(pgf.URI.Filename()))
-			s.gcOptimizationDetailsMu.Lock()
-			_, ok := s.gcOptimizationDetails[dirURI]
-			s.gcOptimizationDetailsMu.Unlock()
-			if ok {
-				gcDetailsDir = dirURI
-			}
+		for _, cgf := range pkg.CompiledGoFiles() {
+			s.storeDiagnostics(snapshot, cgf.URI, analysisSource, reports[cgf.URI])
 		}
 	}
 
-	typeCheckResults := source.GetTypeCheckDiagnostics(ctx, snapshot, pkg)
-	for uri, diags := range typeCheckResults.Diagnostics {
-		s.storeDiagnostics(snapshot, uri, typeCheckSource, diags)
-	}
-	if includeAnalysis && !typeCheckResults.HasParseOrListErrors {
-		reports, err := source.Analyze(ctx, snapshot, pkg, typeCheckResults)
-		if err != nil {
-			event.Error(ctx, "warning: diagnose package", err, tag.Snapshot.Of(snapshot.ID()), tag.Package.Of(pkg.ID()))
-			return
-		}
-		for uri, diags := range reports {
-			s.storeDiagnostics(snapshot, uri, analysisSource, diags)
-		}
-	}
-	// If gc optimization details are available, add them to the
+	// If gc optimization details are requested, add them to the
 	// diagnostic reports.
-	if gcDetailsDir != "" {
-		gcReports, err := source.GCOptimizationDetails(ctx, snapshot, gcDetailsDir)
+	s.gcOptimizationDetailsMu.Lock()
+	_, enableGCDetails := s.gcOptimizationDetails[pkg.ID()]
+	s.gcOptimizationDetailsMu.Unlock()
+	if enableGCDetails {
+		gcReports, err := source.GCOptimizationDetails(ctx, snapshot, pkg)
 		if err != nil {
 			event.Error(ctx, "warning: gc details", err, tag.Snapshot.Of(snapshot.ID()), tag.Package.Of(pkg.ID()))
 		}
diff --git a/internal/lsp/fake/editor.go b/internal/lsp/fake/editor.go
index 58a13ec..725c596 100644
--- a/internal/lsp/fake/editor.go
+++ b/internal/lsp/fake/editor.go
@@ -57,12 +57,12 @@
 type buffer struct {
 	version int
 	path    string
-	content []string
+	lines   []string
 	dirty   bool
 }
 
 func (b buffer) text() string {
-	return strings.Join(b.content, "\n")
+	return strings.Join(b.lines, "\n")
 }
 
 // EditorConfig configures the editor's LSP session. This is similar to
@@ -334,6 +334,10 @@
 				if err != nil {
 					continue // A race with some other operation.
 				}
+				// No need to update if the buffer content hasn't changed.
+				if content == strings.Join(buf.lines, "\n") {
+					continue
+				}
 				// During shutdown, this call will fail. Ignore the error.
 				_ = e.setBufferContentLocked(ctx, evt.Path, false, strings.Split(content, "\n"), nil)
 			}
@@ -378,7 +382,7 @@
 	buf := buffer{
 		version: 1,
 		path:    path,
-		content: strings.Split(content, "\n"),
+		lines:   strings.Split(content, "\n"),
 		dirty:   dirty,
 	}
 	e.mu.Lock()
@@ -640,8 +644,8 @@
 	if !ok {
 		return fmt.Errorf("unknown buffer %q", path)
 	}
-	content := make([]string, len(buf.content))
-	copy(content, buf.content)
+	content := make([]string, len(buf.lines))
+	copy(content, buf.lines)
 	content, err := editContent(content, edits)
 	if err != nil {
 		return err
@@ -654,7 +658,7 @@
 	if !ok {
 		return fmt.Errorf("unknown buffer %q", path)
 	}
-	buf.content = content
+	buf.lines = content
 	buf.version++
 	buf.dirty = dirty
 	e.buffers[path] = buf
@@ -908,7 +912,7 @@
 	if !ok {
 		return fmt.Errorf("buffer %q is not open", path)
 	}
-	if !inText(pos, buf.content) {
+	if !inText(pos, buf.lines) {
 		return fmt.Errorf("position %v is invalid in buffer %q", pos, path)
 	}
 	return nil
diff --git a/internal/lsp/general.go b/internal/lsp/general.go
index beec225..c87107f 100644
--- a/internal/lsp/general.go
+++ b/internal/lsp/general.go
@@ -90,42 +90,40 @@
 
 	return &protocol.InitializeResult{
 		Capabilities: protocol.ServerCapabilities{
-			InnerServerCapabilities: protocol.InnerServerCapabilities{
-				CallHierarchyProvider: true,
-				CodeActionProvider:    codeActionProvider,
-				CompletionProvider: protocol.CompletionOptions{
-					TriggerCharacters: []string{"."},
+			CallHierarchyProvider: true,
+			CodeActionProvider:    codeActionProvider,
+			CompletionProvider: protocol.CompletionOptions{
+				TriggerCharacters: []string{"."},
+			},
+			DefinitionProvider:         true,
+			TypeDefinitionProvider:     true,
+			ImplementationProvider:     true,
+			DocumentFormattingProvider: true,
+			DocumentSymbolProvider:     true,
+			WorkspaceSymbolProvider:    true,
+			ExecuteCommandProvider: protocol.ExecuteCommandOptions{
+				Commands: options.SupportedCommands,
+			},
+			FoldingRangeProvider:      true,
+			HoverProvider:             true,
+			DocumentHighlightProvider: true,
+			DocumentLinkProvider:      protocol.DocumentLinkOptions{},
+			ReferencesProvider:        true,
+			RenameProvider:            renameOpts,
+			SignatureHelpProvider: protocol.SignatureHelpOptions{
+				TriggerCharacters: []string{"(", ","},
+			},
+			TextDocumentSync: &protocol.TextDocumentSyncOptions{
+				Change:    protocol.Incremental,
+				OpenClose: true,
+				Save: protocol.SaveOptions{
+					IncludeText: false,
 				},
-				DefinitionProvider:         true,
-				TypeDefinitionProvider:     true,
-				ImplementationProvider:     true,
-				DocumentFormattingProvider: true,
-				DocumentSymbolProvider:     true,
-				WorkspaceSymbolProvider:    true,
-				ExecuteCommandProvider: protocol.ExecuteCommandOptions{
-					Commands: options.SupportedCommands,
-				},
-				FoldingRangeProvider:      true,
-				HoverProvider:             true,
-				DocumentHighlightProvider: true,
-				DocumentLinkProvider:      protocol.DocumentLinkOptions{},
-				ReferencesProvider:        true,
-				RenameProvider:            renameOpts,
-				SignatureHelpProvider: protocol.SignatureHelpOptions{
-					TriggerCharacters: []string{"(", ","},
-				},
-				TextDocumentSync: &protocol.TextDocumentSyncOptions{
-					Change:    protocol.Incremental,
-					OpenClose: true,
-					Save: protocol.SaveOptions{
-						IncludeText: false,
-					},
-				},
-				Workspace: protocol.WorkspaceGn{
-					WorkspaceFolders: protocol.WorkspaceFoldersGn{
-						Supported:           true,
-						ChangeNotifications: "workspace/didChangeWorkspaceFolders",
-					},
+			},
+			Workspace: protocol.Workspace5Gn{
+				WorkspaceFolders: protocol.WorkspaceFolders4Gn{
+					Supported:           true,
+					ChangeNotifications: "workspace/didChangeWorkspaceFolders",
 				},
 			},
 		},
diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go
index 2b82891..2b71029 100644
--- a/internal/lsp/lsp_test.go
+++ b/internal/lsp/lsp_test.go
@@ -521,7 +521,7 @@
 		// Hack: We assume that we only get one code action per range.
 		var cmds []string
 		for _, a := range actions {
-			cmds = append(cmds, fmt.Sprintf("%s (%s)", a.Command.Command, a.Title))
+			cmds = append(cmds, fmt.Sprintf("%s (%s)", a.Command, a.Title))
 		}
 		t.Fatalf("unexpected number of code actions, want %d, got %d: %v", expectedActions, len(actions), cmds)
 	}
diff --git a/internal/lsp/mod/diagnostics.go b/internal/lsp/mod/diagnostics.go
index e515ada..0aa8caf 100644
--- a/internal/lsp/mod/diagnostics.go
+++ b/internal/lsp/mod/diagnostics.go
@@ -92,11 +92,11 @@
 	}
 	if err == nil {
 		for _, pkg := range wspkgs {
-			for _, diag := range pkg.GetDiagnostics() {
-				if diag.URI == fh.URI() {
-					diagnostics = append(diagnostics, diag)
-				}
+			pkgDiagnostics, err := snapshot.DiagnosePackage(ctx, pkg)
+			if err != nil {
+				return nil, err
 			}
+			diagnostics = append(diagnostics, pkgDiagnostics[fh.URI()]...)
 		}
 	}
 
diff --git a/internal/lsp/protocol/tsclient.go b/internal/lsp/protocol/tsclient.go
index 2746580..0287aca 100644
--- a/internal/lsp/protocol/tsclient.go
+++ b/internal/lsp/protocol/tsclient.go
@@ -7,7 +7,7 @@
 // Package protocol contains data types and code for LSP jsonrpcs
 // generated automatically from vscode-languageserver-node
 // commit: dae62de921d25964e8732411ca09e532dde992f5
-// last fetched Sat Jan 23 2021 16:14:55 GMT-0500 (Eastern Standard Time)
+// last fetched Thu Feb 04 2021 11:11:02 GMT-0500 (Eastern Standard Time)
 
 // Code generated (see typescript/README.md) DO NOT EDIT.
 
diff --git a/internal/lsp/protocol/tsprotocol.go b/internal/lsp/protocol/tsprotocol.go
index 3650d2f..0a2590b 100644
--- a/internal/lsp/protocol/tsprotocol.go
+++ b/internal/lsp/protocol/tsprotocol.go
@@ -5,7 +5,7 @@
 // Package protocol contains data types and code for LSP jsonrpcs
 // generated automatically from vscode-languageserver-node
 // commit: dae62de921d25964e8732411ca09e532dde992f5
-// last fetched Sat Jan 23 2021 16:14:55 GMT-0500 (Eastern Standard Time)
+// last fetched Thu Feb 04 2021 11:11:02 GMT-0500 (Eastern Standard Time)
 package protocol
 
 // Code generated (see typescript/README.md) DO NOT EDIT.
@@ -240,34 +240,19 @@
  */
 type ChangeAnnotationIdentifier = string
 
-type ClientCapabilities = struct {
-	Workspace struct {
-		/**
-		 * Workspace specific client capabilities.
-		 */
-		WorkspaceClientCapabilities
-		/**
-		 * The client has support for workspace folders
-		 *
-		 * @since 3.6.0
-		 */
-		WorkspaceFolders bool `json:"workspaceFolders,omitempty"`
-		/**
-		 * The client supports `workspace/configuration` requests.
-		 *
-		 * @since 3.6.0
-		 */
-		Configuration bool `json:"configuration,omitempty"`
-	}
+type ClientCapabilities struct {
+	/**
+	 * The workspace client capabilities
+	 */
+	Workspace Workspace2Gn `json:"workspace,omitempty"`
 	/**
 	 * Text document specific client capabilities.
 	 */
 	TextDocument TextDocumentClientCapabilities `json:"textDocument,omitempty"`
-	Window       struct {
-		/**
-		 * Window specific client capabilities.
-		 */
-		WindowClientCapabilities
+	/**
+	 * Window specific client capabilities.
+	 */
+	Window struct {
 		/**
 		 * Whether client supports server initiated progress using the
 		 * `window/workDoneProgress/create` request.
@@ -275,7 +260,19 @@
 		 * Since 3.15.0
 		 */
 		WorkDoneProgress bool `json:"workDoneProgress,omitempty"`
-	}
+		/**
+		 * Capabilities specific to the showMessage request.
+		 *
+		 * @since 3.16.0
+		 */
+		ShowMessage ShowMessageRequestClientCapabilities `json:"showMessage,omitempty"`
+		/**
+		 * Capabilities specific to the showDocument request.
+		 *
+		 * @since 3.16.0
+		 */
+		ShowDocument ShowDocumentClientCapabilities `json:"showDocument,omitempty"`
+	} `json:"window,omitempty"`
 	/**
 	 * General client capabilities.
 	 *
@@ -1036,7 +1033,7 @@
 	/**
 	 * The workspace client capabilities
 	 */
-	Workspace WorkspaceGn `json:"workspace,omitempty"`
+	Workspace Workspace3Gn `json:"workspace,omitempty"`
 }
 
 type ConfigurationItem struct {
@@ -2372,71 +2369,7 @@
  */
 type InitializeError float64
 
-type InitializeParams = struct {
-	InnerInitializeParams
-	WorkspaceFoldersInitializeParams
-}
-
-/**
- * The result returned from an initialize request.
- */
-type InitializeResult struct {
-	/**
-	 * The capabilities the language server provides.
-	 */
-	Capabilities ServerCapabilities `json:"capabilities"`
-	/**
-	 * Information about the server.
-	 *
-	 * @since 3.15.0
-	 */
-	ServerInfo struct {
-		/**
-		 * The name of the server as defined by the server.
-		 */
-		Name string `json:"name"`
-		/**
-		 * The server's version as defined by the server.
-		 */
-		Version string `json:"version,omitempty"`
-	} `json:"serverInfo,omitempty"`
-}
-
-type InitializedParams struct {
-}
-
-/**
- * Defines the capabilities provided by the client.
- */
-type InnerClientCapabilities struct {
-	/**
-	 * Workspace specific client capabilities.
-	 */
-	Workspace WorkspaceClientCapabilities `json:"workspace,omitempty"`
-	/**
-	 * Text document specific client capabilities.
-	 */
-	TextDocument TextDocumentClientCapabilities `json:"textDocument,omitempty"`
-	/**
-	 * Window specific client capabilities.
-	 */
-	Window WindowClientCapabilities `json:"window,omitempty"`
-	/**
-	 * General client capabilities.
-	 *
-	 * @since 3.16.0
-	 */
-	General GeneralClientCapabilities `json:"general,omitempty"`
-	/**
-	 * Experimental client capabilities.
-	 */
-	Experimental interface{} `json:"experimental,omitempty"`
-}
-
-/**
- * The initialize parameters
- */
-type InnerInitializeParams struct {
+type InitializeParams struct {
 	/**
 	 * The process Id of the parent process that started
 	 * the server.
@@ -2495,143 +2428,38 @@
 	 * The initial trace setting. If omitted trace is disabled ('off').
 	 */
 	Trace string/*'off' | 'messages' | 'verbose'*/ `json:"trace,omitempty"`
-	WorkDoneProgressParams
+	/**
+	 * The actual configured workspace folders.
+	 */
+	WorkspaceFolders []WorkspaceFolder/*WorkspaceFolder[] | null*/ `json:"workspaceFolders"`
 }
 
 /**
- * Defines the capabilities provided by a language
- * server.
+ * The result returned from an initialize request.
  */
-type InnerServerCapabilities struct {
+type InitializeResult struct {
 	/**
-	 * Defines how text documents are synced. Is either a detailed structure defining each notification or
-	 * for backwards compatibility the TextDocumentSyncKind number.
+	 * The capabilities the language server provides.
 	 */
-	TextDocumentSync interface{}/*TextDocumentSyncOptions | TextDocumentSyncKind*/ `json:"textDocumentSync,omitempty"`
+	Capabilities ServerCapabilities `json:"capabilities"`
 	/**
-	 * The server provides completion support.
-	 */
-	CompletionProvider CompletionOptions `json:"completionProvider,omitempty"`
-	/**
-	 * The server provides hover support.
-	 */
-	HoverProvider bool/*boolean | HoverOptions*/ `json:"hoverProvider,omitempty"`
-	/**
-	 * The server provides signature help support.
-	 */
-	SignatureHelpProvider SignatureHelpOptions `json:"signatureHelpProvider,omitempty"`
-	/**
-	 * The server provides Goto Declaration support.
-	 */
-	DeclarationProvider interface{}/* bool | DeclarationOptions | DeclarationRegistrationOptions*/ `json:"declarationProvider,omitempty"`
-	/**
-	 * The server provides goto definition support.
-	 */
-	DefinitionProvider bool/*boolean | DefinitionOptions*/ `json:"definitionProvider,omitempty"`
-	/**
-	 * The server provides Goto Type Definition support.
-	 */
-	TypeDefinitionProvider interface{}/* bool | TypeDefinitionOptions | TypeDefinitionRegistrationOptions*/ `json:"typeDefinitionProvider,omitempty"`
-	/**
-	 * The server provides Goto Implementation support.
-	 */
-	ImplementationProvider interface{}/* bool | ImplementationOptions | ImplementationRegistrationOptions*/ `json:"implementationProvider,omitempty"`
-	/**
-	 * The server provides find references support.
-	 */
-	ReferencesProvider bool/*boolean | ReferenceOptions*/ `json:"referencesProvider,omitempty"`
-	/**
-	 * The server provides document highlight support.
-	 */
-	DocumentHighlightProvider bool/*boolean | DocumentHighlightOptions*/ `json:"documentHighlightProvider,omitempty"`
-	/**
-	 * The server provides document symbol support.
-	 */
-	DocumentSymbolProvider bool/*boolean | DocumentSymbolOptions*/ `json:"documentSymbolProvider,omitempty"`
-	/**
-	 * The server provides code actions. CodeActionOptions may only be
-	 * specified if the client states that it supports
-	 * `codeActionLiteralSupport` in its initial `initialize` request.
-	 */
-	CodeActionProvider interface{}/*boolean | CodeActionOptions*/ `json:"codeActionProvider,omitempty"`
-	/**
-	 * The server provides code lens.
-	 */
-	CodeLensProvider CodeLensOptions `json:"codeLensProvider,omitempty"`
-	/**
-	 * The server provides document link support.
-	 */
-	DocumentLinkProvider DocumentLinkOptions `json:"documentLinkProvider,omitempty"`
-	/**
-	 * The server provides color provider support.
-	 */
-	ColorProvider interface{}/* bool | DocumentColorOptions | DocumentColorRegistrationOptions*/ `json:"colorProvider,omitempty"`
-	/**
-	 * The server provides workspace symbol support.
-	 */
-	WorkspaceSymbolProvider bool/*boolean | WorkspaceSymbolOptions*/ `json:"workspaceSymbolProvider,omitempty"`
-	/**
-	 * The server provides document formatting.
-	 */
-	DocumentFormattingProvider bool/*boolean | DocumentFormattingOptions*/ `json:"documentFormattingProvider,omitempty"`
-	/**
-	 * The server provides document range formatting.
-	 */
-	DocumentRangeFormattingProvider bool/*boolean | DocumentRangeFormattingOptions*/ `json:"documentRangeFormattingProvider,omitempty"`
-	/**
-	 * The server provides document formatting on typing.
-	 */
-	DocumentOnTypeFormattingProvider DocumentOnTypeFormattingOptions `json:"documentOnTypeFormattingProvider,omitempty"`
-	/**
-	 * The server provides rename support. RenameOptions may only be
-	 * specified if the client states that it supports
-	 * `prepareSupport` in its initial `initialize` request.
-	 */
-	RenameProvider interface{}/*boolean | RenameOptions*/ `json:"renameProvider,omitempty"`
-	/**
-	 * The server provides folding provider support.
-	 */
-	FoldingRangeProvider interface{}/* bool | FoldingRangeOptions | FoldingRangeRegistrationOptions*/ `json:"foldingRangeProvider,omitempty"`
-	/**
-	 * The server provides selection range support.
-	 */
-	SelectionRangeProvider interface{}/* bool | SelectionRangeOptions | SelectionRangeRegistrationOptions*/ `json:"selectionRangeProvider,omitempty"`
-	/**
-	 * The server provides execute command support.
-	 */
-	ExecuteCommandProvider ExecuteCommandOptions `json:"executeCommandProvider,omitempty"`
-	/**
-	 * The server provides call hierarchy support.
+	 * Information about the server.
 	 *
-	 * @since 3.16.0
+	 * @since 3.15.0
 	 */
-	CallHierarchyProvider interface{}/* bool | CallHierarchyOptions | CallHierarchyRegistrationOptions*/ `json:"callHierarchyProvider,omitempty"`
-	/**
-	 * The server provides linked editing range support.
-	 *
-	 * @since 3.16.0
-	 */
-	LinkedEditingRangeProvider interface{}/* bool | LinkedEditingRangeOptions | LinkedEditingRangeRegistrationOptions*/ `json:"linkedEditingRangeProvider,omitempty"`
-	/**
-	 * The server provides semantic tokens support.
-	 *
-	 * @since 3.16.0
-	 */
-	SemanticTokensProvider interface{}/*SemanticTokensOptions | SemanticTokensRegistrationOptions*/ `json:"semanticTokensProvider,omitempty"`
-	/**
-	 * Window specific server capabilities.
-	 */
-	Workspace WorkspaceGn `json:"workspace,omitempty"`
-	/**
-	 * The server provides moniker support.
-	 *
-	 * @since 3.16.0
-	 */
-	MonikerProvider interface{}/* bool | MonikerOptions | MonikerRegistrationOptions*/ `json:"monikerProvider,omitempty"`
-	/**
-	 * Experimental server capabilities.
-	 */
-	Experimental interface{} `json:"experimental,omitempty"`
+	ServerInfo struct {
+		/**
+		 * The name of the server as defined by the server.
+		 */
+		Name string `json:"name"`
+		/**
+		 * The server's version as defined by the server.
+		 */
+		Version string `json:"version,omitempty"`
+	} `json:"serverInfo,omitempty"`
+}
+
+type InitializedParams struct {
 }
 
 /**
@@ -2668,8 +2496,6 @@
  */
 type InsertTextMode float64
 
-type Integer float64
-
 /**
  * Client capabilities for the linked editing range request.
  *
@@ -3576,9 +3402,136 @@
 	RefreshSupport bool `json:"refreshSupport,omitempty"`
 }
 
-type ServerCapabilities = struct {
-	InnerServerCapabilities
-	WorkspaceFoldersServerCapabilities
+type ServerCapabilities struct {
+	/**
+	 * Defines how text documents are synced. Is either a detailed structure defining each notification or
+	 * for backwards compatibility the TextDocumentSyncKind number.
+	 */
+	TextDocumentSync interface{}/*TextDocumentSyncOptions | TextDocumentSyncKind*/ `json:"textDocumentSync,omitempty"`
+	/**
+	 * The server provides completion support.
+	 */
+	CompletionProvider CompletionOptions `json:"completionProvider,omitempty"`
+	/**
+	 * The server provides hover support.
+	 */
+	HoverProvider bool/*boolean | HoverOptions*/ `json:"hoverProvider,omitempty"`
+	/**
+	 * The server provides signature help support.
+	 */
+	SignatureHelpProvider SignatureHelpOptions `json:"signatureHelpProvider,omitempty"`
+	/**
+	 * The server provides Goto Declaration support.
+	 */
+	DeclarationProvider interface{}/* bool | DeclarationOptions | DeclarationRegistrationOptions*/ `json:"declarationProvider,omitempty"`
+	/**
+	 * The server provides goto definition support.
+	 */
+	DefinitionProvider bool/*boolean | DefinitionOptions*/ `json:"definitionProvider,omitempty"`
+	/**
+	 * The server provides Goto Type Definition support.
+	 */
+	TypeDefinitionProvider interface{}/* bool | TypeDefinitionOptions | TypeDefinitionRegistrationOptions*/ `json:"typeDefinitionProvider,omitempty"`
+	/**
+	 * The server provides Goto Implementation support.
+	 */
+	ImplementationProvider interface{}/* bool | ImplementationOptions | ImplementationRegistrationOptions*/ `json:"implementationProvider,omitempty"`
+	/**
+	 * The server provides find references support.
+	 */
+	ReferencesProvider bool/*boolean | ReferenceOptions*/ `json:"referencesProvider,omitempty"`
+	/**
+	 * The server provides document highlight support.
+	 */
+	DocumentHighlightProvider bool/*boolean | DocumentHighlightOptions*/ `json:"documentHighlightProvider,omitempty"`
+	/**
+	 * The server provides document symbol support.
+	 */
+	DocumentSymbolProvider bool/*boolean | DocumentSymbolOptions*/ `json:"documentSymbolProvider,omitempty"`
+	/**
+	 * The server provides code actions. CodeActionOptions may only be
+	 * specified if the client states that it supports
+	 * `codeActionLiteralSupport` in its initial `initialize` request.
+	 */
+	CodeActionProvider interface{}/*boolean | CodeActionOptions*/ `json:"codeActionProvider,omitempty"`
+	/**
+	 * The server provides code lens.
+	 */
+	CodeLensProvider CodeLensOptions `json:"codeLensProvider,omitempty"`
+	/**
+	 * The server provides document link support.
+	 */
+	DocumentLinkProvider DocumentLinkOptions `json:"documentLinkProvider,omitempty"`
+	/**
+	 * The server provides color provider support.
+	 */
+	ColorProvider interface{}/* bool | DocumentColorOptions | DocumentColorRegistrationOptions*/ `json:"colorProvider,omitempty"`
+	/**
+	 * The server provides workspace symbol support.
+	 */
+	WorkspaceSymbolProvider bool/*boolean | WorkspaceSymbolOptions*/ `json:"workspaceSymbolProvider,omitempty"`
+	/**
+	 * The server provides document formatting.
+	 */
+	DocumentFormattingProvider bool/*boolean | DocumentFormattingOptions*/ `json:"documentFormattingProvider,omitempty"`
+	/**
+	 * The server provides document range formatting.
+	 */
+	DocumentRangeFormattingProvider bool/*boolean | DocumentRangeFormattingOptions*/ `json:"documentRangeFormattingProvider,omitempty"`
+	/**
+	 * The server provides document formatting on typing.
+	 */
+	DocumentOnTypeFormattingProvider DocumentOnTypeFormattingOptions `json:"documentOnTypeFormattingProvider,omitempty"`
+	/**
+	 * The server provides rename support. RenameOptions may only be
+	 * specified if the client states that it supports
+	 * `prepareSupport` in its initial `initialize` request.
+	 */
+	RenameProvider interface{}/*boolean | RenameOptions*/ `json:"renameProvider,omitempty"`
+	/**
+	 * The server provides folding provider support.
+	 */
+	FoldingRangeProvider interface{}/* bool | FoldingRangeOptions | FoldingRangeRegistrationOptions*/ `json:"foldingRangeProvider,omitempty"`
+	/**
+	 * The server provides selection range support.
+	 */
+	SelectionRangeProvider interface{}/* bool | SelectionRangeOptions | SelectionRangeRegistrationOptions*/ `json:"selectionRangeProvider,omitempty"`
+	/**
+	 * The server provides execute command support.
+	 */
+	ExecuteCommandProvider ExecuteCommandOptions `json:"executeCommandProvider,omitempty"`
+	/**
+	 * The server provides call hierarchy support.
+	 *
+	 * @since 3.16.0
+	 */
+	CallHierarchyProvider interface{}/* bool | CallHierarchyOptions | CallHierarchyRegistrationOptions*/ `json:"callHierarchyProvider,omitempty"`
+	/**
+	 * The server provides linked editing range support.
+	 *
+	 * @since 3.16.0
+	 */
+	LinkedEditingRangeProvider interface{}/* bool | LinkedEditingRangeOptions | LinkedEditingRangeRegistrationOptions*/ `json:"linkedEditingRangeProvider,omitempty"`
+	/**
+	 * The server provides semantic tokens support.
+	 *
+	 * @since 3.16.0
+	 */
+	SemanticTokensProvider interface{}/*SemanticTokensOptions | SemanticTokensRegistrationOptions*/ `json:"semanticTokensProvider,omitempty"`
+	/**
+	 * The workspace server capabilities
+	 */
+	Workspace Workspace5Gn `json:"workspace,omitempty"`
+	/**
+	 * The server provides moniker support.
+	 *
+	 * @since 3.16.0
+	 */
+	MonikerProvider interface{}/* bool | MonikerOptions | MonikerRegistrationOptions*/ `json:"monikerProvider,omitempty"`
+	/**
+	 * Experimental server capabilities.
+	 */
+	Experimental interface{} `json:"experimental,omitempty"`
 }
 
 type SetTraceParams struct {
@@ -4063,8 +4016,6 @@
 /**
  * An event describing a change to a text document. If range and rangeLength are omitted
  * the new text is considered to be the full content of the document.
- *
- * @deprecated Use the text document from the new vscode-languageserver-textdocument package.
  */
 type TextDocumentContentChangeEvent = struct {
 	/**
@@ -4284,8 +4235,6 @@
  */
 type URI = string
 
-type Uinteger float64
-
 /**
  * Moniker uniqueness level to define scope of the moniker.
  *
@@ -4415,6 +4364,18 @@
 		 * Since 3.15.0
 		 */
 		WorkDoneProgress bool `json:"workDoneProgress,omitempty"`
+		/**
+		 * Capabilities specific to the showMessage request.
+		 *
+		 * @since 3.16.0
+		 */
+		ShowMessage ShowMessageRequestClientCapabilities `json:"showMessage,omitempty"`
+		/**
+		 * Capabilities specific to the showDocument request.
+		 *
+		 * @since 3.16.0
+		 */
+		ShowDocument ShowDocumentClientCapabilities `json:"showDocument,omitempty"`
 	} `json:"window,omitempty"`
 }
 
@@ -4634,7 +4595,7 @@
 	/**
 	 * The workspace client capabilities
 	 */
-	Workspace WorkspaceGn `json:"workspace,omitempty"`
+	Workspace Workspace6Gn `json:"workspace,omitempty"`
 }
 
 type WorkspaceFoldersInitializeParams struct {
@@ -4648,7 +4609,7 @@
 	/**
 	 * The workspace server capabilities
 	 */
-	Workspace WorkspaceGn `json:"workspace,omitempty"`
+	Workspace Workspace8Gn `json:"workspace,omitempty"`
 }
 
 /**
@@ -4989,8 +4950,6 @@
 	 */
 
 	AdjustIndentation InsertTextMode = 2
-	INT_MIN_VALUE     Integer        = -2147483648
-	INT_MAX_VALUE     Integer        = 2147483647
 	/**
 	 * Plain text is supported as a content format
 	 */
@@ -5128,9 +5087,7 @@
 	 * send.
 	 */
 
-	Incremental    TextDocumentSyncKind = 2
-	UINT_MIN_VALUE Uinteger             = 0
-	UINT_MAX_VALUE Uinteger             = 2147483647
+	Incremental TextDocumentSyncKind = 2
 	/**
 	 * The moniker is only unique inside a document
 	 */
@@ -5177,10 +5134,147 @@
 	InitializeParams
 	WorkDoneProgressParams
 }
-type WorkspaceGn struct {
-	WorkspaceFolders WorkspaceFoldersGn `json:"workspaceFolders,omitempty"`
+type Workspace2Gn struct {
+	/**
+	 * The client supports applying batch edits
+	 * to the workspace by supporting the request
+	 * 'workspace/applyEdit'
+	 */
+	ApplyEdit bool `json:"applyEdit,omitempty"`
+
+	/**
+	 * Capabilities specific to `WorkspaceEdit`s
+	 */
+	WorkspaceEdit *WorkspaceEditClientCapabilities `json:"workspaceEdit,omitempty"`
+
+	/**
+	 * Capabilities specific to the `workspace/didChangeConfiguration` notification.
+	 */
+	DidChangeConfiguration DidChangeConfigurationClientCapabilities `json:"didChangeConfiguration,omitempty"`
+
+	/**
+	 * Capabilities specific to the `workspace/didChangeWatchedFiles` notification.
+	 */
+	DidChangeWatchedFiles DidChangeWatchedFilesClientCapabilities `json:"didChangeWatchedFiles,omitempty"`
+
+	/**
+	 * Capabilities specific to the `workspace/symbol` request.
+	 */
+	Symbol *WorkspaceSymbolClientCapabilities `json:"symbol,omitempty"`
+
+	/**
+	 * Capabilities specific to the `workspace/executeCommand` request.
+	 */
+	ExecuteCommand ExecuteCommandClientCapabilities `json:"executeCommand,omitempty"`
+
+	/**
+	 * Capabilities specific to the semantic token requests scoped to the
+	 * workspace.
+	 *
+	 * @since 3.16.0.
+	 */
+	SemanticTokens SemanticTokensWorkspaceClientCapabilities `json:"semanticTokens,omitempty"`
+
+	/**
+	 * Capabilities specific to the code lens requests scoped to the
+	 * workspace.
+	 *
+	 * @since 3.16.0.
+	 */
+	CodeLens CodeLensWorkspaceClientCapabilities `json:"codeLens,omitempty"`
+
+	/**
+	 * The client has support for file notifications/requests for user operations on files.
+	 *
+	 * Since 3.16.0
+	 */
+	FileOperations *FileOperationClientCapabilities `json:"fileOperations,omitempty"`
+
+	/**
+	 * The client has support for workspace folders
+	 *
+	 * @since 3.6.0
+	 */
+	WorkspaceFolders bool `json:"workspaceFolders,omitempty"`
+
+	/**
+	 * The client supports `workspace/configuration` requests.
+	 *
+	 * @since 3.6.0
+	 */
+	Configuration bool `json:"configuration,omitempty"`
 }
-type WorkspaceFoldersGn struct {
+type Workspace3Gn struct {
+	/**
+	 * The client supports applying batch edits
+	 * to the workspace by supporting the request
+	 * 'workspace/applyEdit'
+	 */
+	ApplyEdit bool `json:"applyEdit,omitempty"`
+
+	/**
+	 * Capabilities specific to `WorkspaceEdit`s
+	 */
+	WorkspaceEdit *WorkspaceEditClientCapabilities `json:"workspaceEdit,omitempty"`
+
+	/**
+	 * Capabilities specific to the `workspace/didChangeConfiguration` notification.
+	 */
+	DidChangeConfiguration DidChangeConfigurationClientCapabilities `json:"didChangeConfiguration,omitempty"`
+
+	/**
+	 * Capabilities specific to the `workspace/didChangeWatchedFiles` notification.
+	 */
+	DidChangeWatchedFiles DidChangeWatchedFilesClientCapabilities `json:"didChangeWatchedFiles,omitempty"`
+
+	/**
+	 * Capabilities specific to the `workspace/symbol` request.
+	 */
+	Symbol *WorkspaceSymbolClientCapabilities `json:"symbol,omitempty"`
+
+	/**
+	 * Capabilities specific to the `workspace/executeCommand` request.
+	 */
+	ExecuteCommand ExecuteCommandClientCapabilities `json:"executeCommand,omitempty"`
+
+	/**
+	 * Capabilities specific to the semantic token requests scoped to the
+	 * workspace.
+	 *
+	 * @since 3.16.0.
+	 */
+	SemanticTokens SemanticTokensWorkspaceClientCapabilities `json:"semanticTokens,omitempty"`
+
+	/**
+	 * Capabilities specific to the code lens requests scoped to the
+	 * workspace.
+	 *
+	 * @since 3.16.0.
+	 */
+	CodeLens CodeLensWorkspaceClientCapabilities `json:"codeLens,omitempty"`
+
+	/**
+	 * The client has support for file notifications/requests for user operations on files.
+	 *
+	 * Since 3.16.0
+	 */
+	FileOperations *FileOperationClientCapabilities `json:"fileOperations,omitempty"`
+
+	/**
+	 * The client has support for workspace folders
+	 *
+	 * @since 3.6.0
+	 */
+	WorkspaceFolders bool `json:"workspaceFolders,omitempty"`
+
+	/**
+	 * The client supports `workspace/configuration` requests.
+	 *
+	 * @since 3.6.0
+	 */
+	Configuration bool `json:"configuration,omitempty"`
+}
+type WorkspaceFolders4Gn struct {
 	/**
 	 * The Server has support for workspace folders
 	 */
@@ -5197,3 +5291,110 @@
 	 */
 	ChangeNotifications string/*string | boolean*/ `json:"changeNotifications,omitempty"`
 }
+type Workspace5Gn struct {
+	/**
+	* The server is interested in notifications/requests for operations on files.
+	*
+	* @since 3.16.0
+	 */
+	FileOperations *FileOperationOptions `json:"fileOperations,omitempty"`
+
+	WorkspaceFolders WorkspaceFolders4Gn `json:"workspaceFolders,omitempty"`
+}
+type Workspace6Gn struct {
+	/**
+	 * The client supports applying batch edits
+	 * to the workspace by supporting the request
+	 * 'workspace/applyEdit'
+	 */
+	ApplyEdit bool `json:"applyEdit,omitempty"`
+
+	/**
+	 * Capabilities specific to `WorkspaceEdit`s
+	 */
+	WorkspaceEdit *WorkspaceEditClientCapabilities `json:"workspaceEdit,omitempty"`
+
+	/**
+	 * Capabilities specific to the `workspace/didChangeConfiguration` notification.
+	 */
+	DidChangeConfiguration DidChangeConfigurationClientCapabilities `json:"didChangeConfiguration,omitempty"`
+
+	/**
+	 * Capabilities specific to the `workspace/didChangeWatchedFiles` notification.
+	 */
+	DidChangeWatchedFiles DidChangeWatchedFilesClientCapabilities `json:"didChangeWatchedFiles,omitempty"`
+
+	/**
+	 * Capabilities specific to the `workspace/symbol` request.
+	 */
+	Symbol *WorkspaceSymbolClientCapabilities `json:"symbol,omitempty"`
+
+	/**
+	 * Capabilities specific to the `workspace/executeCommand` request.
+	 */
+	ExecuteCommand ExecuteCommandClientCapabilities `json:"executeCommand,omitempty"`
+
+	/**
+	 * Capabilities specific to the semantic token requests scoped to the
+	 * workspace.
+	 *
+	 * @since 3.16.0.
+	 */
+	SemanticTokens SemanticTokensWorkspaceClientCapabilities `json:"semanticTokens,omitempty"`
+
+	/**
+	 * Capabilities specific to the code lens requests scoped to the
+	 * workspace.
+	 *
+	 * @since 3.16.0.
+	 */
+	CodeLens CodeLensWorkspaceClientCapabilities `json:"codeLens,omitempty"`
+
+	/**
+	 * The client has support for file notifications/requests for user operations on files.
+	 *
+	 * Since 3.16.0
+	 */
+	FileOperations *FileOperationClientCapabilities `json:"fileOperations,omitempty"`
+
+	/**
+	 * The client has support for workspace folders
+	 *
+	 * @since 3.6.0
+	 */
+	WorkspaceFolders bool `json:"workspaceFolders,omitempty"`
+
+	/**
+	 * The client supports `workspace/configuration` requests.
+	 *
+	 * @since 3.6.0
+	 */
+	Configuration bool `json:"configuration,omitempty"`
+}
+type WorkspaceFolders7Gn struct {
+	/**
+	 * The Server has support for workspace folders
+	 */
+	Supported bool `json:"supported,omitempty"`
+
+	/**
+	 * Whether the server wants to receive workspace folder
+	 * change notifications.
+	 *
+	 * If a strings is provided the string is treated as a ID
+	 * under which the notification is registered on the client
+	 * side. The ID can be used to unregister for these events
+	 * using the `client/unregisterCapability` request.
+	 */
+	ChangeNotifications string/*string | boolean*/ `json:"changeNotifications,omitempty"`
+}
+type Workspace8Gn struct {
+	/**
+	* The server is interested in notifications/requests for operations on files.
+	*
+	* @since 3.16.0
+	 */
+	FileOperations *FileOperationOptions `json:"fileOperations,omitempty"`
+
+	WorkspaceFolders WorkspaceFolders7Gn `json:"workspaceFolders,omitempty"`
+}
diff --git a/internal/lsp/protocol/tsserver.go b/internal/lsp/protocol/tsserver.go
index e021772..c93c8ef 100644
--- a/internal/lsp/protocol/tsserver.go
+++ b/internal/lsp/protocol/tsserver.go
@@ -7,7 +7,7 @@
 // Package protocol contains data types and code for LSP jsonrpcs
 // generated automatically from vscode-languageserver-node
 // commit: dae62de921d25964e8732411ca09e532dde992f5
-// last fetched Sat Jan 23 2021 16:14:55 GMT-0500 (Eastern Standard Time)
+// last fetched Thu Feb 04 2021 11:11:02 GMT-0500 (Eastern Standard Time)
 
 // Code generated (see typescript/README.md) DO NOT EDIT.
 
diff --git a/internal/lsp/protocol/typescript/README.md b/internal/lsp/protocol/typescript/README.md
index af813d9..456cc85 100644
--- a/internal/lsp/protocol/typescript/README.md
+++ b/internal/lsp/protocol/typescript/README.md
@@ -23,7 +23,7 @@
 
 ## Notes
 
-1. `go.ts` and `requests.ts` use the Typescript compiler's API, which is [introduced](https://github.com/Microsoft/TypeScript/wiki/Architectural-Overview) in their wiki.
+1. `code.ts` and `util.ts` use the Typescript compiler's API, which is [introduced](https://github.com/Microsoft/TypeScript/wiki/Architectural-Overview) in their wiki.
 2. Because the Typescript and Go type systems are incompatible, `code.ts` and `util.ts` are filled with heuristics and special cases. Therefore they are tied to a specific commit of `vscode-languageserver-node`. The hash code of the commit is included in the header of
 the generated files and stored in the variable `gitHash` in `go.ts`. It is checked (see `git()` in `util.ts`) on every execution.
 3. Generating the `ts*.go` files is only semi-automated. Please file an issue if the released version is too far behind.
diff --git a/internal/lsp/protocol/typescript/code.ts b/internal/lsp/protocol/typescript/code.ts
index 095c52a..bc6c643 100644
--- a/internal/lsp/protocol/typescript/code.ts
+++ b/internal/lsp/protocol/typescript/code.ts
@@ -1,3 +1,4 @@
+/* eslint-disable no-useless-return */
 // read files from vscode-languageserver-node, and generate Go rpc stubs
 // and data definitions. (and maybe someday unmarshaling code)
 
@@ -22,13 +23,15 @@
 import { constName, getComments, goName, loc, strKind } from './util';
 
 var program: ts.Program;
+// eslint-disable-next-line no-unused-vars
+var checker: ts.TypeChecker;
 
 function parse() {
   // this won't complain if some fnames don't exist
   program = ts.createProgram(
     u.fnames,
     { target: ts.ScriptTarget.ES2018, module: ts.ModuleKind.CommonJS });
-  program.getTypeChecker();  // finish type checking and assignment
+  checker = program.getTypeChecker();  // finish type checking and assignment
 }
 
 // ----- collecting information for RPCs
@@ -40,22 +43,22 @@
 
 function findRPCs(node: ts.Node) {
   if (!ts.isModuleDeclaration(node)) {
-    return
+    return;
   }
   if (!ts.isIdentifier(node.name)) {
     throw new Error(
-      `expected Identifier, got ${strKind(node.name)} at ${loc(node)}`)
+      `expected Identifier, got ${strKind(node.name)} at ${loc(node)}`);
   }
-  let reqnot = req
-  let v = node.name.getText()
+  let reqnot = req;
+  let v = node.name.getText();
   if (v.endsWith('Notification')) reqnot = not;
   else if (!v.endsWith('Request')) return;
 
   if (!ts.isModuleBlock(node.body)) {
     throw new Error(
-      `expected ModuleBody got ${strKind(node.body)} at ${loc(node)}`)
+      `expected ModuleBody got ${strKind(node.body)} at ${loc(node)}`);
   }
-  let x: ts.ModuleBlock = node.body
+  let x: ts.ModuleBlock = node.body;
   // The story is to expect const method = 'textDocument/implementation'
   // const type = new ProtocolRequestType<...>(method)
   // but the method may be an explicit string
@@ -68,52 +71,52 @@
     if (dl.declarations.length != 1)
       throw new Error(`expected a single decl at ${loc(dl)}`);
     const decl: ts.VariableDeclaration = dl.declarations[0];
-    const name = decl.name.getText()
+    const name = decl.name.getText();
     // we want the initializers
     if (name == 'method') {  // mostly StringLiteral but NoSubstitutionTemplateLiteral in protocol.semanticTokens.ts
       if (!ts.isStringLiteral(decl.initializer)) {
         if (!ts.isNoSubstitutionTemplateLiteral(decl.initializer)) {
-          console.log(`${decl.initializer.getText()}`);
+          console.log(`81: ${decl.initializer.getText()}`);
           throw new Error(`expect StringLiteral at ${loc(decl)} got ${strKind(decl.initializer)}`);
         }
       }
-      rpc = decl.initializer.getText()
+      rpc = decl.initializer.getText();
     }
     else if (name == 'type') {  // NewExpression
       if (!ts.isNewExpression(decl.initializer))
-        throw new Error(`expecte new at ${loc(decl)}`);
-      const nn: ts.NewExpression = decl.initializer
-      newNode = nn
+        throw new Error(`89 expected new at ${loc(decl)}`);
+      const nn: ts.NewExpression = decl.initializer;
+      newNode = nn;
       const mtd = nn.arguments[0];
       if (ts.isStringLiteral(mtd)) rpc = mtd.getText();
       switch (nn.typeArguments.length) {
         case 1:  // exit
-          ptypes.set(rpc, [nn.typeArguments[0], null])
+          ptypes.set(rpc, [nn.typeArguments[0], null]);
           break;
         case 2:  // notifications
-          ptypes.set(rpc, [nn.typeArguments[0], null])
+          ptypes.set(rpc, [nn.typeArguments[0], null]);
           break;
         case 4:  // request with no parameters
-          ptypes.set(rpc, [null, nn.typeArguments[0]])
+          ptypes.set(rpc, [null, nn.typeArguments[0]]);
           break;
         case 5:  // request req, resp, partial(?)
-          ptypes.set(rpc, [nn.typeArguments[0], nn.typeArguments[1]])
+          ptypes.set(rpc, [nn.typeArguments[0], nn.typeArguments[1]]);
           break;
         default:
-          throw new Error(`${nn.typeArguments.length} at ${loc(nn)}`)
+          throw new Error(`${nn.typeArguments.length} at ${loc(nn)}`);
       }
     }
   }
-  if (rpc == '') throw new Error(`no name found at ${loc(x)}`);
+  if (rpc == '') throw new Error(`112 no name found at ${loc(x)}`);
   // remember the implied types
   const [a, b] = ptypes.get(rpc);
   const add = function (n: ts.Node) {
-    rpcTypes.add(goName(n.getText()))
+    rpcTypes.add(goName(n.getText()));
   };
   underlying(a, add);
   underlying(b, add);
   rpc = rpc.substring(1, rpc.length - 1);  // 'exit'
-  reqnot.set(rpc, newNode)
+  reqnot.set(rpc, newNode);
 }
 
 function setReceives() {
@@ -121,8 +124,8 @@
   // it would be nice to have some independent check on this
   // (this logic fails if the server ever sends $/canceRequest
   //  or $/progress)
-  req.forEach((_, k) => { receives.set(k, 'server') });
-  not.forEach((_, k) => { receives.set(k, 'server') });
+  req.forEach((_, k) => { receives.set(k, 'server'); });
+  not.forEach((_, k) => { receives.set(k, 'server'); });
   receives.set('window/showMessage', 'client');
   receives.set('window/showMessageRequest', 'client');
   receives.set('window/logMessage', 'client');
@@ -137,18 +140,22 @@
   receives.set('$/progress', 'client');
   // a small check
   receives.forEach((_, k) => {
-    if (!req.get(k) && !not.get(k)) throw new Error(`missing ${k}}`);
-    if (req.get(k) && not.get(k)) throw new Error(`dup ${k}`);
-  })
+    if (!req.get(k) && !not.get(k)) throw new Error(`145 missing ${k}}`);
+    if (req.get(k) && not.get(k)) throw new Error(`146 dup ${k}`);
+  });
 }
 
+type DataKind = 'module' | 'interface' | 'alias' | 'enum' | 'class';
+
 interface Data {
+  kind: DataKind;
   me: ts.Node;   // root node for this type
   name: string;  // Go name
+  origname: string; // their name
   generics: ts.NodeArray<ts.TypeParameterDeclaration>;
   as: ts.NodeArray<ts.HeritageClause>;  // inheritance
   // Interface
-  properties: ts.NodeArray<ts.TypeElement>;  // ts.PropertySignature
+  properties: ts.NodeArray<ts.PropertySignature>
   alias: ts.TypeNode;                        // type alias
   // module
   statements: ts.NodeArray<ts.Statement>;
@@ -156,29 +163,73 @@
   // class
   members: ts.NodeArray<ts.PropertyDeclaration>;
 }
-function newData(n: ts.Node, nm: string): Data {
+function newData(n: ts.Node, nm: string, k: DataKind, origname: string): Data {
   return {
-    me: n, name: goName(nm),
-    generics: ts.createNodeArray<ts.TypeParameterDeclaration>(), as: ts.createNodeArray<ts.HeritageClause>(),
-    properties: ts.createNodeArray<ts.TypeElement>(), alias: undefined,
-    statements: ts.createNodeArray<ts.Statement>(),
-    enums: ts.createNodeArray<ts.EnumMember>(),
-    members: ts.createNodeArray<ts.PropertyDeclaration>(),
-  }
+    kind: k,
+    me: n, name: goName(nm), origname: origname,
+    generics: ts.factory.createNodeArray<ts.TypeParameterDeclaration>(),
+    as: ts.factory.createNodeArray<ts.HeritageClause>(),
+    properties: ts.factory.createNodeArray<ts.PropertySignature>(), alias: undefined,
+    statements: ts.factory.createNodeArray<ts.Statement>(),
+    enums: ts.factory.createNodeArray<ts.EnumMember>(),
+    members: ts.factory.createNodeArray<ts.PropertyDeclaration>(),
+  };
 }
 
 // for debugging, produce a skeleton description
 function strData(d: Data): string {
+  if (!d) { return 'nil'; }
   const f = function (na: ts.NodeArray<any>): number {
-    return na.length
+    return na.length;
   };
-  return `D(${d.name}) g;${f(d.generics)} a:${f(d.as)} p:${f(d.properties)} s:${f(d.statements)} e:${f(d.enums)} m:${f(d.members)} ${d.alias != undefined}`
+  const nm = d.name == d.origname ? `${d.name}` : `${d.name}/${d.origname}`;
+  return `g:${f(d.generics)} a:${f(d.as)} p:${f(d.properties)} s:${f(d.statements)} e:${f(d.enums)} m:${f(d.members)} a:${d.alias !== undefined} D(${nm}) k:${d.kind}`;
 }
 
 let data = new Map<string, Data>();            // parsed data types
 let seenTypes = new Map<string, Data>();       // type names we've seen
 let extraTypes = new Map<string, string[]>();  // to avoid struct params
 
+function setData(nm: string, d: Data) {
+  const v = data.get(nm);
+  if (!v) {
+    data.set(nm, d);
+    return;
+  }
+  // if there are multiple definitions of the same name, decide what to do.
+  // For now the choices are only aliases and modules
+  // alias is preferred unless the constant values are needed
+  if (nm === 'PrepareSupportDefaultBehavior') {
+    // want the alias, as we're going to change the type and can't afford a constant
+    if (d.kind === 'alias') data.set(nm, d);
+    else if (v.kind == 'alias') data.set(nm, v);
+    else throw new Error(`208 ${d.kind} ${v.kind}`);
+    return;
+  }
+  if (nm === 'CodeActionKind') {
+    // want the module, need the constants
+    if (d.kind === 'module') data.set(nm, d);
+    else if (v.kind === 'module') data.set(nm, v);
+    else throw new Error(`215 ${d.kind} ${v.kind}`);
+  }
+  if (v.kind === 'alias' && d.kind !== 'alias') return;
+  if (d.kind === 'alias' && v.kind !== 'alias') {
+    data.set(nm, d);
+    return;
+  }
+  if (v.kind === 'alias' && d.kind === 'alias') return;
+  // protocol/src/common/protocol.foldingRange.ts 44: 1 (39: 2) and
+  // types/src/main.ts 397: 1 (392: 2)
+  // for FoldingRangeKind
+  if (d.me.getText() === v.me.getText()) return;
+  // error messages for an unexpected case
+  console.log(`228 ${strData(v)} ${loc(v.me)} for`);
+  console.log(`229 ${v.me.getText().replace(/\n/g, '\\n')}`);
+  console.log(`230 ${strData(d)} ${loc(d.me)}`);
+  console.log(`231 ${d.me.getText().replace(/\n/g, '\\n')}`);
+  throw new Error(`232 setData found ${v.kind} for ${d.kind}`);
+}
+
 // look at top level data definitions
 function genTypes(node: ts.Node) {
   // Ignore top-level items that can't produce output
@@ -192,7 +243,7 @@
   if (ts.isInterfaceDeclaration(node)) {
     const v: ts.InterfaceDeclaration = node;
     // need to check the members, many of which are disruptive
-    let mems: ts.TypeElement[] = [];
+    let mems: ts.PropertySignature[] = [];
     const f = function (t: ts.TypeElement) {
       if (ts.isPropertySignature(t)) {
         mems.push(t);
@@ -203,29 +254,29 @@
         // [key: string]: boolean | number | string | undefined;
         // and InitializeResult: [custom: string]: any;]
       } else
-        throw new Error(`206 unexpected ${strKind(t)}`);
+        throw new Error(`259 unexpected ${strKind(t)}`);
     };
     v.members.forEach(f);
     if (mems.length == 0 && !v.heritageClauses &&
       v.name.getText() != 'InitializedParams') {
-      return  // Don't seem to need any of these [Logger, PipTransport, ...]
+      return;  // Don't seem to need any of these [Logger, PipTransport, ...]
     }
     // Found one we want
-    let x = newData(v, goName(v.name.getText()));
-    x.properties = ts.createNodeArray<ts.TypeElement>(mems);
+    let x = newData(v, goName(v.name.getText()), 'interface', v.name.getText());
+    x.properties = ts.factory.createNodeArray<ts.PropertySignature>(mems);
     if (v.typeParameters) x.generics = v.typeParameters;
     if (v.heritageClauses) x.as = v.heritageClauses;
     if (x.generics.length > 1) {  // Unneeded
       // Item interface Item<K, V>...
-      return
-    };
-    if (data.has(x.name)) {  // modifying one we've seen
-      x = dataMerge(x, data.get(x.name));
+      return;
     }
-    data.set(x.name, x);
+    if (data.has(x.name)) {  // modifying one we've seen
+      x = dataChoose(x, data.get(x.name));
+    }
+    setData(x.name, x);
   } else if (ts.isTypeAliasDeclaration(node)) {
     const v: ts.TypeAliasDeclaration = node;
-    let x = newData(v, v.name.getText());
+    let x = newData(v, v.name.getText(), 'alias', v.name.getText());
     x.alias = v.type;
     // if type is a union of constants, we (mostly) don't want it
     // (at the top level)
@@ -237,105 +288,104 @@
     if (v.typeParameters) {
       x.generics = v.typeParameters;
     }
-    if (data.has(x.name)) x = dataMerge(x, data.get(x.name));
+    if (data.has(x.name)) x = dataChoose(x, data.get(x.name));
     if (x.generics.length > 1) {
-      return
-    };
-    data.set(x.name, x);
+      return;
+    }
+    setData(x.name, x);
   } else if (ts.isModuleDeclaration(node)) {
     const v: ts.ModuleDeclaration = node;
     if (!ts.isModuleBlock(v.body)) {
-      throw new Error(`${loc(v)} not ModuleBlock, but ${strKind(v.body)}`)
+      throw new Error(`${loc(v)} not ModuleBlock, but ${strKind(v.body)}`);
     }
     const b: ts.ModuleBlock = v.body;
     var s: ts.Statement[] = [];
     // we don't want most of these
     const fx = function (x: ts.Statement) {
       if (ts.isFunctionDeclaration(x)) {
-        return
-      };
+        return;
+      }
       if (ts.isTypeAliasDeclaration(x) || ts.isModuleDeclaration(x)) {
-        return
+        return;
       }
       if (!ts.isVariableStatement(x))
         throw new Error(
-          `expected VariableStatment ${loc(x)} ${strKind(x)} ${x.getText()}`);
+          `315 expected VariableStatment ${loc(x)} ${strKind(x)} ${x.getText()}`);
       if (hasNewExpression(x)) {
-        return
-      };
+        return;
+      }
       s.push(x);
     };
-    b.statements.forEach(fx)
+    b.statements.forEach(fx);
     if (s.length == 0) {
-      return
-    };
-    let m = newData(node, v.name.getText());
-    m.statements = ts.createNodeArray<ts.Statement>(s);
-    if (data.has(m.name)) m = dataMerge(m, data.get(m.name));
-    data.set(m.name, m);
+      return;
+    }
+    let m = newData(node, v.name.getText(), 'module', v.name.getText());
+    m.statements = ts.factory.createNodeArray<ts.Statement>(s);
+    if (data.has(m.name)) m = dataChoose(m, data.get(m.name));
+    setData(m.name, m);
   } else if (ts.isEnumDeclaration(node)) {
     const nm = node.name.getText();
-    let v = newData(node, nm);
+    let v = newData(node, nm, 'enum', node.name.getText());
     v.enums = node.members;
     if (data.has(nm)) {
-      v = dataMerge(v, data.get(nm));
+      v = dataChoose(v, data.get(nm));
     }
-    data.set(nm, v);
+    setData(nm, v);
   } else if (ts.isClassDeclaration(node)) {
     const v: ts.ClassDeclaration = node;
     var d: ts.PropertyDeclaration[] = [];
     const wanted = function (c: ts.ClassElement): string {
       if (ts.isConstructorDeclaration(c)) {
-        return ''
-      };
+        return '';
+      }
       if (ts.isMethodDeclaration(c)) {
-        return ''
-      };
+        return '';
+      }
       if (ts.isGetAccessor(c)) {
-        return ''
-      };
+        return '';
+      }
       if (ts.isSetAccessor(c)) {
-        return ''
-      };
+        return '';
+      }
       if (ts.isPropertyDeclaration(c)) {
         d.push(c);
-        return strKind(c)
-      };
-      throw new Error(`Class decl ${strKind(c)} `)
+        return strKind(c);
+      }
+      throw new Error(`Class decl ${strKind(c)} `);
     };
     v.members.forEach((c) => wanted(c));
     if (d.length == 0) {
-      return
+      return;
     }  // don't need it
-    let c = newData(v, v.name.getText());
-    c.members = ts.createNodeArray<ts.PropertyDeclaration>(d);
+    let c = newData(v, v.name.getText(), 'class', v.name.getText());
+    c.members = ts.factory.createNodeArray<ts.PropertyDeclaration>(d);
     if (v.typeParameters) {
-      c.generics = v.typeParameters
+      c.generics = v.typeParameters;
     }
     if (c.generics.length > 1) {
-      return
+      return;
     }
     if (v.heritageClauses) {
-      c.as = v.heritageClauses
+      c.as = v.heritageClauses;
     }
     if (data.has(c.name))
       throw new Error(`Class dup ${loc(c.me)} and ${loc(data.get(c.name).me)}`);
-    data.set(c.name, c);
+    setData(c.name, c);
   } else {
-    throw new Error(`325 unexpected ${strKind(node)} ${loc(node)} `)
+    throw new Error(`378 unexpected ${strKind(node)} ${loc(node)} `);
   }
 }
 
-// Typescript can accumulate
-function dataMerge(a: Data, b: Data): Data {
-  // maybe they are textually identical? (it happens)
+// Typescript can accumulate, but this chooses one or the other
+function dataChoose(a: Data, b: Data): Data {
+  // maybe they are textually identical? (e.g., FoldingRangeKind)
   const [at, bt] = [a.me.getText(), b.me.getText()];
   if (at == bt) {
     return a;
   }
   switch (a.name) {
     case 'InitializeError':
-    case 'MessageType':
     case 'CompletionItemTag':
     case 'SymbolTag':
     case 'CodeActionKind':
@@ -354,8 +404,8 @@
       return a;
   }
   console.log(
-    `357 ${strKind(a.me)} ${strKind(b.me)} ${a.name} ${loc(a.me)} ${loc(b.me)}`)
-  throw new Error(`Fix dataMerge for ${a.name}`);
+    `409 ${strKind(a.me)} ${strKind(b.me)} ${a.name} ${loc(a.me)} ${loc(b.me)}`);
+  throw new Error(`410 Fix dataChoose for ${a.name}`);
 }
 
 // is a node an ancestor of a NewExpression
@@ -363,25 +413,26 @@
   let ans = false;
   n.forEachChild((n: ts.Node) => {
     if (ts.isNewExpression(n)) ans = true;
-  })
-  return ans
+  });
+  return ans;
 }
 
 function checkOnce() {
   // Data for all the rpc types?
   rpcTypes.forEach(s => {
-    if (!data.has(s)) throw new Error(`checkOnce, ${s}?`)
+    if (!data.has(s)) throw new Error(`checkOnce, ${s}?`);
   });
 }
 
 // helper function to find underlying types
+// eslint-disable-next-line no-unused-vars
 function underlying(n: ts.Node, f: (n: ts.Node) => void) {
   if (!n) return;
   const ff = function (n: ts.Node) {
-    underlying(n, f)
+    underlying(n, f);
   };
   if (ts.isIdentifier(n)) {
-    f(n)
+    f(n);
   } else if (
     n.kind == ts.SyntaxKind.StringKeyword ||
     n.kind == ts.SyntaxKind.NumberKeyword ||
@@ -393,32 +444,32 @@
     n.kind == ts.SyntaxKind.VoidKeyword) {
     // nothing to do
   } else if (ts.isTypeReferenceNode(n)) {
-    f(n.typeName)
+    f(n.typeName);
   } else if (ts.isArrayTypeNode(n)) {
-    underlying(n.elementType, f)
+    underlying(n.elementType, f);
   } else if (ts.isHeritageClause(n)) {
     n.types.forEach(ff);
   } else if (ts.isExpressionWithTypeArguments(n)) {
-    underlying(n.expression, f)
+    underlying(n.expression, f);
   } else if (ts.isPropertySignature(n)) {
-    underlying(n.type, f)
+    underlying(n.type, f);
   } else if (ts.isTypeLiteralNode(n)) {
-    n.members.forEach(ff)
+    n.members.forEach(ff);
   } else if (ts.isUnionTypeNode(n) || ts.isIntersectionTypeNode(n)) {
-    n.types.forEach(ff)
+    n.types.forEach(ff);
   } else if (ts.isIndexSignatureDeclaration(n)) {
-    underlying(n.type, f)
+    underlying(n.type, f);
   } else if (ts.isParenthesizedTypeNode(n)) {
-    underlying(n.type, f)
+    underlying(n.type, f);
   } else if (
     ts.isLiteralTypeNode(n) || ts.isVariableStatement(n) ||
     ts.isTupleTypeNode(n)) {
     // we only see these in moreTypes, but they are handled elsewhere
   } else if (ts.isEnumMember(n)) {
     if (ts.isStringLiteral(n.initializer)) return;
-    throw new Error(`419 EnumMember ${strKind(n.initializer)} ${n.name.getText()}`)
+    throw new Error(`472 EnumMember ${strKind(n.initializer)} ${n.name.getText()}`);
   } else {
-    throw new Error(`421 saw ${strKind(n)} in underlying. ${n.getText()} at ${loc(n)}`)
+    throw new Error(`474 saw ${strKind(n)} in underlying. ${n.getText()} at ${loc(n)}`);
   }
 }
 
@@ -428,86 +479,212 @@
 function moreTypes() {
   const extra = function (s: string) {
     if (!data.has(s)) throw new Error(`moreTypes needs ${s}`);
-    seenTypes.set(s, data.get(s))
+    seenTypes.set(s, data.get(s));
   };
   rpcTypes.forEach(extra);  // all the types needed by the rpcs
   // needed in enums.go (or elsewhere)
-  extra('InitializeError')
-  extra('WatchKind')
-  extra('FoldingRangeKind')
+  extra('InitializeError');
+  extra('WatchKind');
+  extra('FoldingRangeKind');
   // not sure why these weren't picked up
-  extra('FileSystemWatcher')
-  extra('DidChangeWatchedFilesRegistrationOptions')
-  extra('WorkDoneProgressBegin')
-  extra('WorkDoneProgressReport')
-  extra('WorkDoneProgressEnd')
-  let old = 0
+  extra('DidChangeWatchedFilesRegistrationOptions');
+  extra('WorkDoneProgressBegin');
+  extra('WorkDoneProgressReport');
+  extra('WorkDoneProgressEnd');
+  let old = 0;
   do {
-    old = seenTypes.size
+    old = seenTypes.size;
 
     const m = new Map<string, Data>();
     const add = function (n: ts.Node) {
       const nm = goName(n.getText());
       if (seenTypes.has(nm) || m.has(nm)) return;
-      // For generic parameters, this might set it to undefined
-      m.set(nm, data.get(nm));
+      if (data.get(nm)) {
+        m.set(nm, data.get(nm));
+      }
     };
     // expect all the heritage clauses have single Identifiers
     const h = function (n: ts.Node) {
       underlying(n, add);
     };
     const f = function (x: ts.NodeArray<ts.Node>) {
-      x.forEach(h)
+      x.forEach(h);
     };
-    seenTypes.forEach((d: Data) => d && f(d.as))
+    seenTypes.forEach((d: Data) => d && f(d.as));
     // find the types in the properties
-    seenTypes.forEach((d: Data) => d && f(d.properties))
+    seenTypes.forEach((d: Data) => d && f(d.properties));
     // and in the alias and in the statements and in the enums
-    seenTypes.forEach((d: Data) => d && underlying(d.alias, add))
-    seenTypes.forEach((d: Data) => d && f(d.statements))
-    seenTypes.forEach((d: Data) => d && f(d.enums))
-    m.forEach((d, k) => seenTypes.set(k, d))
+    seenTypes.forEach((d: Data) => d && underlying(d.alias, add));
+    seenTypes.forEach((d: Data) => d && f(d.statements));
+    seenTypes.forEach((d: Data) => d && f(d.enums));
+    m.forEach((d, k) => seenTypes.set(k, d));
   }
   while (seenTypes.size != old)
     ;
 }
 
+function cleanData() { // middle pass
+  // seenTypes contains all the top-level types.
+  seenTypes.forEach((d) => {
+    if (d.kind == 'alias') mergeAlias(d);
+  });
+}
+
+function sameType(a: ts.TypeNode, b: ts.TypeNode): boolean {
+  if (a.kind !== b.kind) return false;
+  if (a.kind === ts.SyntaxKind.BooleanKeyword) return true;
+  if (ts.isTypeReferenceNode(a) && ts.isTypeReferenceNode(b) &&
+    a.typeName.getText() === b.typeName.getText()) return true;
+  if (ts.isArrayTypeNode(a) && ts.isArrayTypeNode(b)) return sameType(a.elementType, b.elementType);
+  if (ts.isTypeLiteralNode(a) && ts.isTypeLiteralNode(b)) {
+    if (a.members.length !== b.members.length) return false;
+    if (a.members.length === 1) return a.members[0].name.getText() === b.members[0].name.getText();
+    if (loc(a) === loc(b)) return true;
+  }
+  throw new Error(`546 sameType? ${strKind(a)} ${strKind(b)}`);
+}
+
+type propMap = Map<string, ts.PropertySignature>;
+function propMapSet(pm: propMap, name: string, v: ts.PropertySignature) {
+  if (!pm.get(name)) {
+    try { getComments(v); } catch (e) { console.log(`552 ${name} ${e}`); }
+    pm.set(name, v);
+    return;
+  }
+  const a = pm.get(name).type;
+  const b = v.type;
+  if (sameType(a, b)) {
+    return;
+  }
+  if (ts.isTypeReferenceNode(a) && ts.isTypeLiteralNode(b)) {
+    const x = mergeTypeRefLit(name, a, b);
+    const fake: Object = v;
+    fake['type'] = x;
+    check(fake as ts.PropertySignature, '565');
+    pm.set(name, fake as ts.PropertySignature);
+    return;
+  }
+  if (ts.isTypeLiteralNode(a) && ts.isTypeLiteralNode(b)) {
+    const x = mergeTypeLitLit(name, a, b);
+    const fake: Object = v;
+    fake['type'] = x;
+    check(fake as ts.PropertySignature, '578');
+    pm.set(name, fake as ts.PropertySignature);
+    return;
+  }
+  console.log(`577 ${pm.get(name).getText()}\n${v.getText()}`);
+  throw new Error(`578 should merge ${strKind(a)} and ${strKind(b)} for ${name}`);
+}
+function addToProperties(pm: propMap, tn: ts.TypeNode, prefix = '') {
+  if (ts.isTypeReferenceNode(tn)) {
+    const d = seenTypes.get(goName(tn.typeName.getText()));
+    if (tn.typeName.getText() === 'T') return;
+    if (!d) throw new Error(`584 ${tn.typeName.getText()} not found`);
+    if (d.properties.length === 0 && d.alias === undefined) return;
+    if (d.alias !== undefined) {
+      if (ts.isIntersectionTypeNode(d.alias)) {
+        d.alias.types.forEach((tn) => addToProperties(pm, tn, prefix)); // prefix?
+        return;
+      }
+    }
+    d.properties.forEach((ps) => {
+      const name = `${prefix}.${ps.name.getText()}`;
+      propMapSet(pm, name, ps);
+      addToProperties(pm, ps.type, name);
+    });
+  } else if (strKind(tn) === 'TypeLiteral') {
+    if (!ts.isTypeLiteralNode(tn)) new Error(`598 ${strKind(tn)}`);
+    tn.forEachChild((child: ts.Node) => {
+      if (!ts.isPropertySignature(child)) throw new Error(`600 ${strKind(child)}`);
+      const name = `${prefix}.${child.name.getText()}`;
+      propMapSet(pm, name, child);
+      addToProperties(pm, child.type, name);
+    });
+  }
+}
+function deepProperties(d: Data): propMap {
+  let properties: propMap = new Map<string, ts.PropertySignature>();
+  if (!d.alias || !ts.isIntersectionTypeNode(d.alias)) return;
+  d.alias.types.forEach((ts) => addToProperties(properties, ts));
+  return properties;
+}
+
+function mergeAlias(d: Data) {
+  const props = deepProperties(d);
+  if (!props) return; // nothing merged
+  // now each element of props should have length 1
+  // change d to merged, toss its alias field, fill in its properties
+  const v: ts.PropertySignature[] = [];
+  props.forEach((ps, nm) => {
+    const xlen = nm.split('.').length;
+    if (xlen !== 2) return; // not top-level
+    v.push(ps);
+  });
+  d.kind = 'interface';
+  d.alias = undefined;
+  d.properties = ts.factory.createNodeArray(v);
+}
+
+function mergeTypeLitLit(name: string, a: ts.TypeLiteralNode, b: ts.TypeLiteralNode): ts.TypeLiteralNode {
+  const v = new Map<string, ts.TypeElement>(); // avoid duplicates
+  a.members.forEach((te) => v.set(te.name.getText(), te));
+  b.members.forEach((te) => v.set(te.name.getText(), te));
+  const x: ts.TypeElement[] = [];
+  v.forEach((te) => x.push(te));
+  const fake: Object = a;
+  fake['members'] = x;
+  check(fake as ts.TypeLiteralNode, '643');
+  return fake as ts.TypeLiteralNode;
+}
+
+function mergeTypeRefLit(name: string, a: ts.TypeReferenceNode, b: ts.TypeLiteralNode): ts.TypeLiteralNode {
+  const d = seenTypes.get(goName(a.typeName.getText()));
+  if (!d) throw new Error(`644 name ${a.typeName.getText()} not found`);
+  const typ = d.me;
+  if (!ts.isInterfaceDeclaration(typ)) throw new Error(`646 got ${strKind(typ)} not InterfaceDecl`);
+  const v = new Map<string, ts.TypeElement>(); // avoid duplicates
+  typ.members.forEach((te) => v.set(te.name.getText(), te));
+  b.members.forEach((te) => v.set(te.name.getText(), te));
+  const x: ts.TypeElement[] = [];
+  v.forEach((te) => x.push(te));
+
+  const w = ts.factory.createNodeArray(x);
+  const fk: Object = b;
+  fk['members'] = w;
+  fk['members']['pos'] = b.members.pos;
+  fk['members']['end'] = b.members.end;
+  check(fk as ts.TypeLiteralNode, '662');
+  return fk as ts.TypeLiteralNode;
+}
+
+// check that constructed nodes still have associated text
+function check(n: ts.Node, loc: string) {
+  try { getComments(n); } catch (e) { console.log(`check at ${loc} ${e}`); }
+  try { n.getText(); } catch (e) { console.log(`text check at ${loc}`); }
+}
+
 let typesOut = new Array<string>();
 let constsOut = new Array<string>();
 
 // generate Go types
 function toGo(d: Data, nm: string) {
   if (!d) return;  // this is probably a generic T
-  if (d.alias) {
-    goTypeAlias(d, nm);
-  } else if (d.statements.length > 0) {
-    goModule(d, nm);
-  } else if (d.enums.length > 0) {
-    goEnum(d, nm);
-  } else if (
-    d.properties.length > 0 || d.as.length > 0 || nm == 'InitializedParams') {
-    goInterface(d, nm);
-  } else
-    throw new Error(
-      `492 more cases in toGo ${nm} ${d.as.length} ${d.generics.length} `)
+  if (d.name.startsWith('Inner') || d.name === 'WindowClientCapabilities') return; // removed by alias processing
+  if (d.name === 'Integer' || d.name === 'Uinteger') return; // unneeded
+  switch (d.kind) {
+    case 'alias':
+      goTypeAlias(d, nm); break;
+    case 'module': goModule(d, nm); break;
+    case 'enum': goEnum(d, nm); break;
+    case 'interface': goInterface(d, nm); break;
+    default:
+      throw new Error(
+        `672: more cases in toGo ${nm} ${d.kind}`);
+  }
 }
 
-// these fields need a *. (making every optional struct indirect led to very
-// complex literals in gopls.)
-// As of Jan 2021 (3.16.0) consider (sent by server)
-// LocationLink.originSelectionRange // unused by gopls
-// Diagnostics.codeDescription
-// CreateFile.createFileOptions and .annotationID // unused by gopls
-// same for RenameFile and DeleteFile
-// InitializeResult.serverInfo // gopls always sets
-// InnerServerCapabilites.completionProvider, .signatureHelpProvider, .documentLinkProvider,
-//   .executeCommandProvider, .Workspace  // always set
-// InnerserverCapabilities.codeLensProvier, .DocumentOnTypeFormattingProvider // unused(?)
-// FileOperationPattern.options // unused
-// CompletionItem.command
-// Hover.Range (?)
-// CodeAction.disabled, .command
-// CodeLens.command
+// these fields need a * and are not covered by the code
+// that calls isStructType.
 var starred: [string, string][] = [
   ['TextDocumentContentChangeEvent', 'range'], ['CodeAction', 'command'],
   ['CodeAction', 'disabled'],
@@ -520,42 +697,42 @@
   let ans = `type ${goName(nm)} struct {\n`;
 
   // generate the code for each member
-  const g = function (n: ts.TypeElement) {
+  const g = function (n: ts.PropertySignature) {
     if (!ts.isPropertySignature(n))
       throw new Error(`expected PropertySignature got ${strKind(n)} `);
     ans = ans.concat(getComments(n));
     const json = u.JSON(n);
-    // SelectionRange is a recursive type
     let gt = goType(n.type, n.name.getText());
     if (gt == d.name) gt = '*' + gt; // avoid recursive types (SelectionRange)
     // there are several cases where a * is needed
+    // (putting * in front of too many things breaks uses of CodeActionKind)
     starred.forEach(([a, b]) => {
       if (d.name == a && n.name.getText() == b) {
         gt = '*' + gt;
       }
-    })
+    });
     ans = ans.concat(`${goName(n.name.getText())} ${gt}`, json, '\n');
   };
-  d.properties.forEach(g)
+  d.properties.forEach(g);
   // heritage clauses become embedded types
   // check they are all Identifiers
   const f = function (n: ts.ExpressionWithTypeArguments) {
     if (!ts.isIdentifier(n.expression))
       throw new Error(`Interface ${nm} heritage ${strKind(n.expression)} `);
-    ans = ans.concat(goName(n.expression.getText()), '\n')
+    ans = ans.concat(goName(n.expression.getText()), '\n');
   };
-  d.as.forEach((n: ts.HeritageClause) => n.types.forEach(f))
-  ans = ans.concat('}\n')
-  typesOut.push(getComments(d.me))
-  typesOut.push(ans)
+  d.as.forEach((n: ts.HeritageClause) => n.types.forEach(f));
+  ans = ans.concat('}\n');
+  typesOut.push(getComments(d.me));
+  typesOut.push(ans);
 }
 
 // generate Go code for a module (const declarations)
 // Generates type definitions, and named constants
 function goModule(d: Data, nm: string) {
   if (d.generics.length > 0 || d.as.length > 0) {
-    throw new Error(`557 goModule: unexpected for ${nm}
-  `)
+    throw new Error(`743 goModule: unexpected for ${nm}
+  `);
   }
   // all the statements should be export const <id>: value
   //   or value = value
@@ -564,45 +741,45 @@
   let isNumeric = false;
   const f = function (n: ts.Statement, i: number) {
     if (!ts.isVariableStatement(n)) {
-      throw new Error(`567 ${nm} ${i} expected VariableStatement,
+      throw new Error(`753 ${nm} ${i} expected VariableStatement,
       got ${strKind(n)}`);
     }
-    const c = getComments(n)
+    const c = getComments(n);
     const v = n.declarationList.declarations[0];  // only one
 
     if (!v.initializer)
-      throw new Error(`574 no initializer ${nm} ${i} ${v.name.getText()}`)
+      throw new Error(`760 no initializer ${nm} ${i} ${v.name.getText()}`);
     isNumeric = strKind(v.initializer) == 'NumericLiteral';
     if (c != '') constsOut.push(c);  // no point if there are no comments
     // There are duplicates.
     const cname = constName(goName(v.name.getText()), nm);
-    let val = v.initializer.getText()
-    val = val.split('\'').join('"')  // useless work for numbers
-    constsOut.push(`${cname} ${nm} = ${val}`)
+    let val = v.initializer.getText();
+    val = val.split('\'').join('"');  // useless work for numbers
+    constsOut.push(`${cname} ${nm} = ${val}`);
   };
-  d.statements.forEach(f)
-  typesOut.push(getComments(d.me))
+  d.statements.forEach(f);
+  typesOut.push(getComments(d.me));
   // Or should they be type aliases?
-  typesOut.push(`type ${nm} ${isNumeric ? 'float64' : 'string'}`) // PJW: superfluous Integer and Uinteger
+  typesOut.push(`type ${nm} ${isNumeric ? 'float64' : 'string'}`);
 }
 
 // generate Go code for an enum. Both types and named constants
 function goEnum(d: Data, nm: string) {
-  let isNumeric = false
+  let isNumeric = false;
   const f = function (v: ts.EnumMember, j: number) {  // same as goModule
     if (!v.initializer)
       throw new Error(`goEnum no initializer ${nm} ${j} ${v.name.getText()}`);
     isNumeric = strKind(v.initializer) == 'NumericLiteral';
     const c = getComments(v);
     const cname = constName(goName(v.name.getText()), nm);
-    let val = v.initializer.getText()
-    val = val.split('\'').join('"')  // replace quotes. useless work for numbers
-    constsOut.push(`${c}${cname} ${nm} = ${val}`)
+    let val = v.initializer.getText();
+    val = val.split('\'').join('"');  // replace quotes. useless work for numbers
+    constsOut.push(`${c}${cname} ${nm} = ${val}`);
   };
-  d.enums.forEach(f)
-  typesOut.push(getComments(d.me))
+  d.enums.forEach(f);
+  typesOut.push(getComments(d.me));
   // Or should they be type aliases?
-  typesOut.push(`type ${nm} ${isNumeric ? 'float64' : 'string'}`)
+  typesOut.push(`type ${nm} ${isNumeric ? 'float64' : 'string'}`);
 }
 
 // generate code for a type alias
@@ -615,12 +792,12 @@
   // d.alias doesn't seem to have comments
   let aliasStr = goName(nm) == 'DocumentURI' ? ' ' : ' = ';
   if (nm == 'PrepareSupportDefaultBehavior') {
-    // code-insiders is sending a bool, not a number. PJW: check this after Jan/2021
+    // code-insiders is sending a bool, not a number. PJW: check this after Feb/2021
     // (and gopls never looks at it anyway)
     typesOut.push(`type ${goName(nm)}${aliasStr}interface{}\n`);
     return;
   }
-  typesOut.push(`type ${goName(nm)}${aliasStr}${goType(d.alias, nm)}\n`)
+  typesOut.push(`type ${goName(nm)}${aliasStr}${goType(d.alias, nm)}\n`);
 }
 
 // return a go type and maybe an assocated javascript tag
@@ -645,37 +822,37 @@
   } else if (strKind(n) == 'AnyKeyword' || strKind(n) == 'UnknownKeyword') {
     return 'interface{}';
   } else if (strKind(n) == 'NullKeyword') {
-    return 'nil'
+    return 'nil';
   } else if (strKind(n) == 'VoidKeyword' || strKind(n) == 'NeverKeyword') {
-    return 'void'
+    return 'void';
   } else if (strKind(n) == 'ObjectKeyword') {
-    return 'interface{}'
+    return 'interface{}';
   } else if (ts.isArrayTypeNode(n)) {
     if (nm === 'arguments') {
       // Command and ExecuteCommandParams
       return '[]json.RawMessage';
     }
-    return `[]${goType(n.elementType, nm)}`
+    return `[]${goType(n.elementType, nm)}`;
   } else if (ts.isParenthesizedTypeNode(n)) {
-    return goType(n.type, nm)
+    return goType(n.type, nm);
   } else if (ts.isLiteralTypeNode(n)) {
     return strKind(n.literal) == 'StringLiteral' ? 'string' : 'float64';
   } else if (ts.isTypeLiteralNode(n)) {
     // these are anonymous structs
     const v = goTypeLiteral(n, nm);
-    return v
+    return v;
   } else if (ts.isTupleTypeNode(n)) {
     if (n.getText() == '[number, number]') return '[]float64';
-    throw new Error(`goType unexpected Tuple ${n.getText()}`)
+    throw new Error(`goType unexpected Tuple ${n.getText()}`);
   }
-  throw new Error(`${strKind(n)} goType unexpected ${n.getText()} for ${nm}`)
+  throw new Error(`${strKind(n)} goType unexpected ${n.getText()} for ${nm}`);
 }
 
 // The choice is uniform interface{}, or some heuristically assigned choice,
 // or some better sytematic idea I haven't thought of. Using interface{}
 // is, in practice, impossibly complex in the existing code.
 function goUnionType(n: ts.UnionTypeNode, nm: string): string {
-  let help = `/*${n.getText()}*/`  // show the original as a comment
+  let help = `/*${n.getText()}*/`;  // show the original as a comment
   // There are some bad cases with newlines:
   // range?: boolean | {\n	};
   // full?: boolean | {\n		/**\n		 * The server supports deltas for full documents.\n		 */\n		delta?: boolean;\n	}
@@ -687,25 +864,27 @@
   // handle all the special cases
   switch (n.types.length) {
     case 2: {
-      const a = strKind(n.types[0])
-      const b = strKind(n.types[1])
+      const a = strKind(n.types[0]);
+      const b = strKind(n.types[1]);
       if (a == 'NumberKeyword' && b == 'StringKeyword') {  // ID
-        return `interface{} ${help}`
+        return `interface{} ${help}`;
       }
-      if (b == 'NullKeyword') {
+      if (b == 'NullKeyword' || n.types[1].getText() === 'null') {
+        // PJW: fix this. it looks like 'null' is now being parsed as LiteralType
+        // and check the other keyword cases
         if (nm == 'textDocument/codeAction') {
           // (Command | CodeAction)[] | null
-          return `[]CodeAction ${help}`
+          return `[]CodeAction ${help}`;
         }
-        let v = goType(n.types[0], 'a')
-        return `${v} ${help}`
+        let v = goType(n.types[0], 'a');
+        return `${v} ${help}`;
       }
       if (a == 'BooleanKeyword') {  // usually want bool
         if (nm == 'codeActionProvider') return `interface{} ${help}`;
         if (nm == 'renameProvider') return `interface{} ${help}`;
         if (nm == 'full') return `interface{} ${help}`; // there's a struct
         if (nm == 'save') return `${goType(n.types[1], '680')} ${help}`;
-        return `${goType(n.types[0], 'b')} ${help}`
+        return `${goType(n.types[0], 'b')} ${help}`;
       }
       if (b == 'ArrayType') return `${goType(n.types[1], 'c')} ${help}`;
       if (help.includes('InsertReplaceEdit') && n.types[0].getText() == 'TextEdit') {
@@ -718,32 +897,33 @@
       }
       if (a == 'StringKeyword') return `string ${help}`;
       if (a == 'TypeLiteral' && nm == 'TextDocumentContentChangeEvent') {
-        return `${goType(n.types[0], nm)}`
+        return `${goType(n.types[0], nm)}`;
       }
-      throw new Error(`709 ${a} ${b} ${n.getText()} ${loc(n)}`);
+      console.log(`911 ${n.types[1].getText()} ${loc(n.types[1])}`);
+      throw new Error(`912 ${nm}: a:${a} b:${b} ${n.getText()} ${loc(n)}`);
     }
     case 3: {
-      const aa = strKind(n.types[0])
-      const bb = strKind(n.types[1])
-      const cc = strKind(n.types[2])
+      const aa = strKind(n.types[0]);
+      const bb = strKind(n.types[1]);
+      const cc = strKind(n.types[2]);
       if (nm == 'DocumentFilter') {
         // not really a union. the first is enough, up to a missing
         // omitempty but avoid repetitious comments
-        return `${goType(n.types[0], 'g')}`
+        return `${goType(n.types[0], 'g')}`;
       }
       if (nm == 'textDocument/documentSymbol') {
         return `[]interface{} ${help}`;
       }
       if (aa == 'TypeReference' && bb == 'ArrayType' && cc == 'NullKeyword') {
-        return `${goType(n.types[0], 'd')} ${help}`
+        return `${goType(n.types[0], 'd')} ${help}`;
       }
       if (aa == 'TypeReference' && bb == aa && cc == 'ArrayType') {
         // should check that this is Hover.Contents
-        return `${goType(n.types[0], 'e')} ${help}`
+        return `${goType(n.types[0], 'e')} ${help}`;
       }
       if (aa == 'ArrayType' && bb == 'TypeReference' && cc == 'NullKeyword') {
         // check this is nm == 'textDocument/completion'
-        return `${goType(n.types[1], 'f')} ${help}`
+        return `${goType(n.types[1], 'f')} ${help}`;
       }
       if (aa == 'LiteralType' && bb == aa && cc == aa) return `string ${help}`;
       break;
@@ -751,6 +931,7 @@
     case 4:
       if (nm == 'documentChanges') return `TextDocumentEdit ${help} `;
       if (nm == 'textDocument/prepareRename') return `Range ${help} `;
+    // eslint-disable-next-line no-fallthrough
     default:
       throw new Error(`goUnionType len=${n.types.length} nm=${nm}`);
   }
@@ -758,7 +939,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
@@ -767,8 +948,8 @@
       // avoid nested comments
       m = m.split(' ')[0];
     }
-    m = m.split('\n').join('; ')  // sloppy: struct{;
-    res = res.concat(`${i == 0 ? '' : ' | '}`, m)
+    m = m.split('\n').join('; ');  // sloppy: struct{;
+    res = res.concat(`${i == 0 ? '' : ' | '}`, m);
     if (!ts.isLiteralTypeNode(v)) isLiteral = false;
     else literal = strKind(v.literal) == 'StringLiteral' ? 'string' : 'number';
   });
@@ -777,7 +958,7 @@
   }
   // I don't think we get here
   // trace?: 'off' | 'messages' | 'verbose' should get string
-  return `${literal} /* ${n.getText()} */`
+  return `${literal} /* ${n.getText()} */`;
 }
 
 // some of the intersection types A&B are ok as struct{A;B;} and some
@@ -789,8 +970,8 @@
   //if (nm == 'ServerCapabilities') return expandIntersection(n); // save for later consideration
   let inner = '';
   n.types.forEach(
-    (t: ts.TypeNode) => { inner = inner.concat(goType(t, nm), '\n'); })
-  return `struct{ \n${inner}} `
+    (t: ts.TypeNode) => { inner = inner.concat(goType(t, nm), '\n'); });
+  return `struct{ \n${inner}} `;
 }
 
 // for each of the intersected types, extract its components (each will
@@ -799,7 +980,7 @@
 // that occur more than once need to be combined.
 function expandIntersection(n: ts.IntersectionTypeNode): string {
   const bad = function (n: ts.Node, s: string) {
-    return new Error(`expandIntersection ${strKind(n)} ${s}`)
+    return new Error(`expandIntersection ${strKind(n)} ${s}`);
   };
   let props = new Map<string, ts.PropertySignature[]>();
   for (const tp of n.types) {
@@ -817,14 +998,14 @@
     if (v.length == 1) {
       const a = v[0];
       ans = ans.concat(getComments(a));
-      ans = ans.concat(`${goName(k)} ${goType(a.type, k)} ${u.JSON(a)}\n`)
-      continue
+      ans = ans.concat(`${goName(k)} ${goType(a.type, k)} ${u.JSON(a)}\n`);
+      continue;
     }
-    ans = ans.concat(`${goName(k)} struct {\n`)
+    ans = ans.concat(`${goName(k)} struct {\n`);
     for (let i = 0; i < v.length; i++) {
       const a = v[i];
       if (ts.isTypeReferenceNode(a.type)) {
-        ans = ans.concat(getComments(a))
+        ans = ans.concat(getComments(a));
         ans = ans.concat(goName(a.type.typeName.getText()), '\n');
       } else if (ts.isTypeLiteralNode(a.type)) {
         if (a.type.members.length != 1) throw bad(a.type, 'C');
@@ -832,15 +1013,38 @@
         if (!ts.isPropertySignature(b)) throw bad(b, 'D');
         ans = ans.concat(getComments(b));
         ans = ans.concat(
-          goName(b.name.getText()), ' ', goType(b.type, 'a'), u.JSON(b), '\n')
+          goName(b.name.getText()), ' ', goType(b.type, 'a'), u.JSON(b), '\n');
       } else {
-        throw bad(a.type, `E ${a.getText()} in ${goName(k)} at ${loc(a)}`)
+        throw bad(a.type, `E ${a.getText()} in ${goName(k)} at ${loc(a)}`);
       }
     }
     ans = ans.concat('}\n');
   }
   ans = ans.concat('}\n');
-  return ans
+  return ans;
+}
+
+// Does it make sense to use a pointer?
+function isStructType(te: ts.TypeNode): boolean {
+  switch (strKind(te)) {
+    case 'UnionType': // really need to know which type will be chosen
+    case 'BooleanKeyword':
+    case 'StringKeyword':
+    case 'ArrayType':
+      return false;
+    case 'TypeLiteral': return false; // true makes for difficult compound constants
+    // but think more carefully to understands why starred is needed.
+    case 'TypeReference': {
+      if (!ts.isTypeReferenceNode(te)) throw new Error(`1047 impossible ${strKind(te)}`);
+      const d = seenTypes.get(goName(te.typeName.getText()));
+      if (d.properties.length > 1) return true;
+      // alias or interface with a single property (The alias is Uinteger, which we ignore later)
+      if (d.alias) return false;
+      const x = d.properties[0].type;
+      return isStructType(x);
+    }
+    default: throw new Error(`1055 indirectable> ${strKind(te)}`);
+  }
 }
 
 function goTypeLiteral(n: ts.TypeLiteralNode, nm: string): string {
@@ -850,38 +1054,41 @@
     // add the json, as in goInterface(). Strange inside union types.
     if (ts.isPropertySignature(nx)) {
       let json = u.JSON(nx);
-      let typ = goType(nx.type, nx.name.getText())
+      let typ = goType(nx.type, nx.name.getText());
       const v = getComments(nx) || '';
       starred.forEach(([a, b]) => {
         if (a != nm || b != typ.toLowerCase()) return;
         typ = '*' + typ;
-        json = json.substring(0, json.length - 2) + ',omitempty"`'
-      })
-      res = res.concat(`${v} ${goName(nx.name.getText())} ${typ}`, json, '\n')
-      ans.push(`${v}${goName(nx.name.getText())} ${typ} ${json}\n`)
+        json = json.substring(0, json.length - 2) + ',omitempty"`';
+      });
+      if (typ[0] !== '*' && isStructType(nx.type)) typ = '*' + typ;
+      res = res.concat(`${v} ${goName(nx.name.getText())} ${typ}`, json, '\n');
+      ans.push(`${v}${goName(nx.name.getText())} ${typ} ${json}\n`);
     } else if (ts.isIndexSignatureDeclaration(nx)) {
       if (nx.getText() == '[uri: string]: TextEdit[];') {
         res = 'map[string][]TextEdit';
-        ans.push(`map[string][]TextEdit`);  // this is never used
+        ans.push('map[string][]TextEdit');  // this is never used
         return;
       }
       if (nx.getText() == '[id: string /* ChangeAnnotationIdentifier */]: ChangeAnnotation;') {
         res = 'map[string]ChangeAnnotationIdentifier';
         ans.push(res);
-        return
+        return;
       }
-      throw new Error(`873 handle ${nx.getText()} ${loc(nx)}`);
+      throw new Error(`1087 handle ${nx.getText()} ${loc(nx)}`);
     } else
-      throw new Error(`TypeLiteral had ${strKind(nx)}`)
+      throw new Error(`TypeLiteral had ${strKind(nx)}`);
   };
   n.members.forEach(g);
   // for some the generated type is wanted, for others it's not needed
   if (!nm.startsWith('workspace')) {
     if (res.startsWith('struct')) return res + '}';  // map[] is special
-    return res
+    return res;
   }
-  extraTypes.set(goName(nm) + 'Gn', ans)
-  return goName(nm) + 'Gn'
+  // these names have to be made unique
+  const genName = `${goName(nm)}${extraTypes.size}Gn`;
+  extraTypes.set(genName, ans);
+  return genName;
 }
 
 // print all the types and constants and extra types
@@ -889,29 +1096,29 @@
   // generate go types alphabeticaly
   let v = Array.from(seenTypes.keys());
   v.sort();
-  v.forEach((x) => toGo(seenTypes.get(x), x))
-  u.prgo(u.computeHeader(true))
+  v.forEach((x) => toGo(seenTypes.get(x), x));
+  u.prgo(u.computeHeader(true));
   u.prgo('import "encoding/json"\n\n');
   typesOut.forEach((s) => {
     u.prgo(s);
     // it's more convenient not to have to think about trailing newlines
     // when generating types, but doc comments can't have an extra \n
     if (s.indexOf('/**') < 0) u.prgo('\n');
-  })
+  });
   u.prgo('\nconst (\n');
   constsOut.forEach((s) => {
     u.prgo(s);
-    u.prgo('\n')
-  })
+    u.prgo('\n');
+  });
   u.prgo(')\n');
-  u.prgo('// Types created to name formal parameters and embedded structs\n')
+  u.prgo('// Types created to name formal parameters and embedded structs\n');
   extraTypes.forEach((v, k) => {
-    u.prgo(` type ${k} struct {\n`)
+    u.prgo(` type ${k} struct {\n`);
     v.forEach((s) => {
       u.prgo(s);
-      u.prgo('\n')
+      u.prgo('\n');
     });
-    u.prgo('}\n')
+    u.prgo('}\n');
   });
 }
 
@@ -962,7 +1169,7 @@
       return true, sendParseError(ctx, reply, err)
     }
     err:= ${side.name}.${nm}(ctx, &params)
-    return true, reply(ctx, nil, err)`
+    return true, reply(ctx, nil, err)`;
   } else {
     case1 = `err := ${side.name}.${nm}(ctx)
     return true, reply(ctx, nil, err)`;
@@ -986,20 +1193,21 @@
     b = a;
     a = '';  // workspace/workspaceFolders and shutdown
   }
-  u.prb(`${side.name} req ${a != ''}, ${b != ''} ${nm} ${m} ${loc(n)} `)
+  u.prb(`${side.name} req ${a != ''}, ${b != ''} ${nm} ${m} ${loc(n)} `);
   side.methods.push(sig(nm, a, b));
 
   const caseHdr = `case "${m}": // req`;
   let case1 = notNil;
   if (a != '') {
-    if (extraTypes.has('Param' + nm)) a = 'Param' + nm
+    if (extraTypes.has('Param' + nm)) a = 'Param' + nm;
     case1 = `var params ${a}
     if err := json.Unmarshal(r.Params(), &params); err != nil {
       return true, sendParseError(ctx, reply, err)
     }`;
   }
   const arg2 = a == '' ? '' : ', &params';
-  let case2 = `if err := ${side.name}.${nm}(ctx${arg2}); err != nil {
+  // if case2 is not explicitly typed string, typescript makes it a union of strings
+  let case2: string = `if err := ${side.name}.${nm}(ctx${arg2}); err != nil {
     event.Error(ctx, "", err)
   }`;
   if (b != '' && b != 'void') {
@@ -1007,7 +1215,7 @@
     return true, reply(ctx, resp, err)`;
   } else {  // response is nil
     case2 = `err := ${side.name}.${nm}(ctx${arg2})
-    return true, reply(ctx, nil, err)`
+    return true, reply(ctx, nil, err)`;
   }
 
   side.cases.push(`${caseHdr}\n${case1}\n${case2}`);
@@ -1025,7 +1233,7 @@
     }`;
   } else if (a != '') {
     callBody = `return Call(ctx, s.Conn, "${m}", params, nil) // Call, not Notify
-  }`
+  }`;
   }
   side.calls.push(`${callHdr}\n${callBody}\n`);
 }
@@ -1037,15 +1245,15 @@
   let s = m.substring(i + 1);
   let x = s[0].toUpperCase() + s.substring(1);
   for (let j = x.indexOf('/'); j >= 0; j = x.indexOf('/')) {
-    let suffix = x.substring(j + 1)
-    suffix = suffix[0].toUpperCase() + suffix.substring(1)
-    let prefix = x.substring(0, j)
-    x = prefix + suffix
+    let suffix = x.substring(j + 1);
+    suffix = suffix[0].toUpperCase() + suffix.substring(1);
+    let prefix = x.substring(0, j);
+    x = prefix + suffix;
   }
   if (seenNames.has(x)) {
     // Resolve, ResolveCodeLens, ResolveDocumentLink
-    if (!x.startsWith('Resolve')) throw new Error(`expected Resolve, not ${x}`)
-    x += m[0].toUpperCase() + m.substring(1, i)
+    if (!x.startsWith('Resolve')) throw new Error(`expected Resolve, not ${x}`);
+    x += m[0].toUpperCase() + m.substring(1, i);
   }
   seenNames.add(x);
   return x;
@@ -1058,15 +1266,15 @@
   if (skip('[]') || skip('interface') || skip('Declaration') ||
     skip('Definition') || skip('DocumentSelector'))
     return false;
-  return true
+  return true;
 }
 
 // Go signatures for methods.
 function sig(nm: string, a: string, b: string, names?: boolean): string {
   if (a.indexOf('struct') != -1) {
-    const v = a.split('\n')
-    extraTypes.set(`Param${nm}`, v.slice(1, v.length - 1))
-    a = 'Param' + nm
+    const v = a.split('\n');
+    extraTypes.set(`Param${nm}`, v.slice(1, v.length - 1));
+    a = 'Param' + nm;
   }
   if (a == 'void')
     a = '';
@@ -1110,25 +1318,25 @@
           errors "golang.org/x/xerrors"
         )
         `);
-  const a = side.name[0].toUpperCase() + side.name.substring(1)
+  const a = side.name[0].toUpperCase() + side.name.substring(1);
   f(`type ${a} interface {`);
-  side.methods.forEach((v) => { f(v) })
+  side.methods.forEach((v) => { f(v); });
   f('}\n');
   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) })
+  side.cases.forEach((v) => { f(v); });
   f(`
         default:
           return false, nil
         }
       }`);
-  side.calls.forEach((v) => { f(v) });
+  side.calls.forEach((v) => { f(v); });
 }
 
 // Handling of non-standard requests, so we can add gopls-specific calls.
 function nonstandardRequests() {
   server.methods.push(
-    'NonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error)')
+    'NonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error)');
   server.calls.push(
     `func (s *serverDispatcher) NonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error) {
       var result interface{}
@@ -1137,7 +1345,7 @@
       }
       return result, nil
     }
-  `)
+  `);
 }
 
 // ----- remember it's a scripting language
@@ -1146,13 +1354,13 @@
     throw new Error(
       `git hash mismatch, wanted\n${u.gitHash} but source is at\n${u.git()}`);
   }
-  u.createOutputFiles()
-  parse()
-  u.printAST(program)
+  u.createOutputFiles();
+  parse();
+  u.printAST(program);
   // find the Requests and Nofificatations
   for (const sourceFile of program.getSourceFiles()) {
     if (!sourceFile.isDeclarationFile) {
-      ts.forEachChild(sourceFile, findRPCs)
+      ts.forEachChild(sourceFile, findRPCs);
     }
   }
   // separate RPCs into client and server
@@ -1160,7 +1368,7 @@
   // visit every sourceFile collecting top-level type definitions
   for (const sourceFile of program.getSourceFiles()) {
     if (!sourceFile.isDeclarationFile) {
-      ts.forEachChild(sourceFile, genTypes)
+      ts.forEachChild(sourceFile, genTypes);
     }
   }
   // check that each thing occurs exactly once, and put pointers into
@@ -1172,21 +1380,23 @@
   // 3. func (x *xDispatcher) Method(ctx, parm)
   not.forEach(  // notifications
     (v, k) => {
-      receives.get(k) == 'client' ? goNot(client, k) : goNot(server, k)
+      receives.get(k) == 'client' ? goNot(client, k) : goNot(server, k);
     });
   req.forEach(  // requests
     (v, k) => {
-      receives.get(k) == 'client' ? goReq(client, k) : goReq(server, k)
+      receives.get(k) == 'client' ? goReq(client, k) : goReq(server, k);
     });
   nonstandardRequests();
   // find all the types implied by seenTypes and rpcs to try to avoid
   // generating types that aren't used
   moreTypes();
+  // do merging
+  cleanData();
   // and print the Go code
-  outputTypes()
-  console.log(`seen ${seenTypes.size + extraTypes.size}`)
+  outputTypes();
+  console.log(`seen ${seenTypes.size + extraTypes.size}`);
   output(client);
   output(server);
 }
 
-main()
+main();
diff --git a/internal/lsp/protocol/typescript/util.ts b/internal/lsp/protocol/typescript/util.ts
index 5e0756a..09c8aae 100644
--- a/internal/lsp/protocol/typescript/util.ts
+++ b/internal/lsp/protocol/typescript/util.ts
@@ -1,6 +1,7 @@
 
 // for us typescript ignorati, having an import makes this file a module
 import * as fs from 'fs';
+import * as process from 'process';
 import * as ts from 'typescript';
 
 // This file contains various utilities having to do with producing strings
@@ -8,29 +9,29 @@
 
 // ------ create files
 let dir = process.env['HOME'];
-const srcDir = '/vscode-languageserver-node'
+const srcDir = '/vscode-languageserver-node';
 export const fnames = [
   `${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 = 'dae62de921d25964e8732411ca09e532dde992f5'
+export const gitHash = 'dae62de921d25964e8732411ca09e532dde992f5';
 let outFname = 'tsprotocol.go';
 let fda: number, fdb: number, fde: number;  // file descriptors
 
 export function createOutputFiles() {
-  fda = fs.openSync('/tmp/ts-a', 'w')  // dump of AST
-  fdb = fs.openSync('/tmp/ts-b', 'w')  // unused, for debugging
-  fde = fs.openSync(outFname, 'w')     // generated Go
+  fda = fs.openSync('/tmp/ts-a', 'w');  // dump of AST
+  fdb = fs.openSync('/tmp/ts-b', 'w');  // unused, for debugging
+  fde = fs.openSync(outFname, 'w');     // generated Go
 }
 export function pra(s: string) {
-  return (fs.writeSync(fda, s))
+  return (fs.writeSync(fda, s));
 }
 export function prb(s: string) {
-  return (fs.writeSync(fdb, s))
+  return (fs.writeSync(fdb, s));
 }
 export function prgo(s: string) {
-  return (fs.writeSync(fde, s))
+  return (fs.writeSync(fde, s));
 }
 
 // Get the hash value of the git commit
@@ -42,66 +43,66 @@
     a = a.substring(0, a.length - 1);
   }
   if (a.length == 40) {
-    return a  // a hash
+    return a;  // a hash
   }
   if (a.substring(0, 5) == 'ref: ') {
     const fname = `${dir}${srcDir}/.git/` + a.substring(5);
-    let b = fs.readFileSync(fname).toString()
+    let b = fs.readFileSync(fname).toString();
     if (b.length == 41) {
       return b.substring(0, 40);
     }
   }
-  throw new Error('failed to find the git commit hash')
+  throw new Error('failed to find the git commit hash');
 }
 
 // Produce a header for Go output files
 export function computeHeader(pkgDoc: boolean): string {
-  let lastMod = 0
-  let lastDate: Date
+  let lastMod = 0;
+  let lastDate: Date;
   for (const f of fnames) {
-    const st = fs.statSync(f)
+    const st = fs.statSync(f);
     if (st.mtimeMs > lastMod) {
-      lastMod = st.mtimeMs
-      lastDate = st.mtime
+      lastMod = st.mtimeMs;
+      lastDate = st.mtime;
     }
   }
   const cp = `// 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.
 
-  `
+  `;
   const a =
-    `// Package protocol contains data types and code for LSP jsonrpcs\n` +
-    `// generated automatically from vscode-languageserver-node\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`
+    `// 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) {
-    return cp + a + b + c
+    return cp + a + b + c;
   }
   else {
-    return cp + b + a + c
+    return cp + b + a + c;
   }
-};
+}
 
 // Turn a typescript name into an exportable Go name, and appease lint
 export function goName(s: string): string {
-  let ans = s
+  let ans = s;
   if (s.charAt(0) == '_') {
-    ans = 'Inner' + s.substring(1)
+    ans = 'Inner' + s.substring(1);
   }
-  else { ans = s.substring(0, 1).toUpperCase() + s.substring(1) };
-  ans = ans.replace(/Uri$/, 'URI')
-  ans = ans.replace(/Id$/, 'ID')
-  return ans
+  else { ans = s.substring(0, 1).toUpperCase() + s.substring(1); }
+  ans = ans.replace(/Uri$/, 'URI');
+  ans = ans.replace(/Id$/, 'ID');
+  return ans;
 }
 
 // Generate JSON tag for a struct field
 export function JSON(n: ts.PropertySignature): string {
   const json = `\`json:"${n.name.getText()}${
     n.questionToken != undefined ? ',omitempty' : ''}"\``;
-  return json
+  return json;
 }
 
 // Generate modifying prefixes and suffixes to ensure
@@ -112,24 +113,24 @@
     ['DiagnosticSeverity', 'Severity'], ['WatchKind', 'Watch'],
     ['SignatureHelpTriggerKind', 'Sig'], ['CompletionItemTag', 'Compl'],
     ['Integer', 'INT_'], ['Uinteger', 'UINT_']
-  ])  // typeName->prefix
+  ]);  // typeName->prefix
   let suff = new Map<string, string>([
     ['CompletionItemKind', 'Completion'], ['InsertTextFormat', 'TextFormat'],
     ['SymbolTag', 'Symbol'], ['FileOperationPatternKind', 'Op'],
-  ])
+  ]);
   let ans = nm;
   if (pref.get(type)) ans = pref.get(type) + ans;
-  if (suff.has(type)) ans = ans + suff.get(type)
-  return ans
+  if (suff.has(type)) ans = ans + suff.get(type);
+  return ans;
 }
 
 // Find the comments associated with an AST node
 export function getComments(node: ts.Node): string {
   const sf = node.getSourceFile();
-  const start = node.getStart(sf, false)
-  const starta = node.getStart(sf, true)
-  const x = sf.text.substring(starta, start)
-  return x
+  const start = node.getStart(sf, false);
+  const starta = node.getStart(sf, true);
+  const x = sf.text.substring(starta, start);
+  return x;
 }
 
 
@@ -138,7 +139,7 @@
 export function printAST(program: ts.Program) {
   // dump the ast, for debugging
   const f = function (n: ts.Node) {
-    describe(n, pra)
+    describe(n, pra);
   };
   for (const sourceFile of program.getSourceFiles()) {
     if (!sourceFile.isDeclarationFile) {
@@ -146,60 +147,61 @@
       ts.forEachChild(sourceFile, f);
     }
   }
-  pra('\n')
+  pra('\n');
   for (const key of Object.keys(seenThings).sort()) {
-    pra(`${key}: ${seenThings[key]} \n`)
+    pra(`${key}: ${seenThings[key]} \n`);
   }
 }
 
 // Used in printing the AST
 let seenThings = new Map<string, number>();
 function seenAdd(x: string) {
-  seenThings[x] = (seenThings[x] === undefined ? 1 : seenThings[x] + 1)
+  seenThings[x] = (seenThings[x] === undefined ? 1 : seenThings[x] + 1);
 }
 
+// eslint-disable-next-line no-unused-vars
 function describe(node: ts.Node, pr: (s: string) => any) {
   if (node === undefined) {
-    return
+    return;
   }
   let indent = '';
 
   function f(n: ts.Node) {
-    seenAdd(kinds(n))
+    seenAdd(kinds(n));
     if (ts.isIdentifier(n)) {
-      pr(`${indent} ${loc(n)} ${strKind(n)} ${n.text} \n`)
+      pr(`${indent} ${loc(n)} ${strKind(n)} ${n.text} \n`);
     }
     else if (ts.isPropertySignature(n) || ts.isEnumMember(n)) {
-      pra(`${indent} ${loc(n)} ${strKind(n)} \n`)
+      pra(`${indent} ${loc(n)} ${strKind(n)} \n`);
     }
     else if (ts.isTypeLiteralNode(n)) {
-      let m = n.members
-      pr(`${indent} ${loc(n)} ${strKind(n)} ${m.length} \n`)
+      let m = n.members;
+      pr(`${indent} ${loc(n)} ${strKind(n)} ${m.length} \n`);
     }
     else if (ts.isStringLiteral(n)) {
-      pr(`${indent} ${loc(n)} ${strKind(n)} ${n.text} \n`)
+      pr(`${indent} ${loc(n)} ${strKind(n)} ${n.text} \n`);
     }
-    else { pr(`${indent} ${loc(n)} ${strKind(n)} \n`) };
-    indent += ' .'
-    ts.forEachChild(n, f)
-    indent = indent.slice(0, indent.length - 2)
+    else { pr(`${indent} ${loc(n)} ${strKind(n)} \n`); }
+    indent += ' .';
+    ts.forEachChild(n, f);
+    indent = indent.slice(0, indent.length - 2);
   }
-  f(node)
+  f(node);
 }
 
 
 // For debugging, say where an AST node is in a file
 export function loc(node: ts.Node): string {
   const sf = node.getSourceFile();
-  const start = node.getStart()
-  const x = sf.getLineAndCharacterOfPosition(start)
-  const full = node.getFullStart()
-  const y = sf.getLineAndCharacterOfPosition(full)
-  let fn = sf.fileName
-  const n = fn.search(/-node./)
-  fn = fn.substring(n + 6)
+  const start = node.getStart();
+  const x = sf.getLineAndCharacterOfPosition(start);
+  const full = node.getFullStart();
+  const y = sf.getLineAndCharacterOfPosition(full);
+  let fn = sf.fileName;
+  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
 
@@ -207,9 +209,9 @@
 // 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) };
-  ts.forEachChild(n, f)
-  return res
+  function f(n: ts.Node): void { res += ' ' + strKind(n); }
+  ts.forEachChild(n, f);
+  return res;
 }
 
 // What kind of AST node is it? This would just be typescript's
@@ -217,9 +219,14 @@
 // are misleading
 export function strKind(n: ts.Node): string {
   if (n == null || n == undefined) {
-    return 'null'
+    return 'null';
   }
-  const x = ts.SyntaxKind[n.kind];
+   return kindToStr(n.kind);
+}
+
+export function kindToStr(k: ts.SyntaxKind): string {
+  if (k === undefined) return 'unDefined';
+  const x = ts.SyntaxKind[k];
   // some of these have two names
   switch (x) {
     default:
diff --git a/internal/lsp/server.go b/internal/lsp/server.go
index df1da8c..793ee5d 100644
--- a/internal/lsp/server.go
+++ b/internal/lsp/server.go
@@ -24,7 +24,7 @@
 func NewServer(session source.Session, client protocol.Client) *Server {
 	return &Server{
 		diagnostics:           map[span.URI]*fileReports{},
-		gcOptimizationDetails: make(map[span.URI]struct{}),
+		gcOptimizationDetails: make(map[string]struct{}),
 		watchedGlobPatterns:   make(map[string]struct{}),
 		changedFiles:          make(map[span.URI]struct{}),
 		session:               session,
@@ -91,9 +91,9 @@
 
 	// gcOptimizationDetails describes the packages for which we want
 	// optimization details to be included in the diagnostics. The key is the
-	// directory of the package.
+	// ID of the package.
 	gcOptimizationDetailsMu sync.Mutex
-	gcOptimizationDetails   map[span.URI]struct{}
+	gcOptimizationDetails   map[string]struct{}
 
 	// diagnosticsSema limits the concurrency of diagnostics runs, which can be
 	// expensive.
diff --git a/internal/lsp/source/api_json.go b/internal/lsp/source/api_json.go
index 7352130..8f72405 100755
--- a/internal/lsp/source/api_json.go
+++ b/internal/lsp/source/api_json.go
@@ -392,6 +392,11 @@
 							Default: "true",
 						},
 						{
+							Name:    "\"nilness\"",
+							Doc:     "check for redundant or impossible nil comparisons\n\nThe nilness checker inspects the control-flow graph of each function in\na package and reports nil pointer dereferences, degenerate nil\npointers, and panics with nil values. A degenerate comparison is of the form\nx==nil or x!=nil where x is statically known to be nil or non-nil. These are\noften a mistake, especially in control flow related to errors. Panics with nil\nvalues are checked because they are not detectable by\n\n\tif r := recover(); r != nil {\n\nThis check reports conditions such as:\n\n\tif f == nil { // impossible condition (f is a function)\n\t}\n\nand:\n\n\tp := &v\n\t...\n\tif p != nil { // tautological condition\n\t}\n\nand:\n\n\tif p == nil {\n\t\tprint(*p) // nil dereference\n\t}\n\nand:\n\n\tif p == nil {\n\t\tpanic(p)\n\t}\n",
+							Default: "false",
+						},
+						{
 							Name:    "\"printf\"",
 							Doc:     "check consistency of Printf format strings and arguments\n\nThe check applies to known functions (for example, those in package fmt)\nas well as any detected wrappers of known functions.\n\nA function that wants to avail itself of printf checking but is not\nfound by this analyzer's heuristics (for example, due to use of\ndynamic calls) can insert a bogus call:\n\n\tif false {\n\t\t_ = fmt.Sprintf(format, args...) // enable printf checking\n\t}\n\nThe -funcs flag specifies a comma-separated list of names of additional\nknown formatting functions or methods. If the name contains a period,\nit must denote a specific function using one of the following forms:\n\n\tdir/pkg.Function\n\tdir/pkg.Type.Method\n\t(*dir/pkg.Type).Method\n\nOtherwise the name is interpreted as a case-insensitive unqualified\nidentifier such as \"errorf\". Either way, if a listed name ends in f, the\nfunction is assumed to be Printf-like, taking a format string before the\nargument list. Otherwise it is assumed to be Print-like, taking a list\nof arguments with no format string.\n",
 							Default: "true",
@@ -477,6 +482,11 @@
 							Default: "true",
 						},
 						{
+							Name:    "\"unusedwrite\"",
+							Doc:     "checks for unused writes\n\nThe analyzer reports instances of writes to struct fields and\narrays that are never read. Specifically, when a struct object\nor an array is copied, its elements are copied implicitly by\nthe compiler, and any element write to this copy does nothing\nwith the original object.\n\nFor example:\n\n\ttype T struct { x int }\n\tfunc f(input []T) {\n\t\tfor i, v := range input {  // v is a copy\n\t\t\tv.x = i  // unused write to field x\n\t\t}\n\t}\n\nAnother example is about non-pointer receiver:\n\n\ttype T struct { x int }\n\tfunc (t T) f() {  // t is a copy\n\t\tt.x = i  // unused write to field x\n\t}\n",
+							Default: "false",
+						},
+						{
 							Name:    "\"fillreturns\"",
 							Doc:     "suggested fixes for \"wrong number of return values (want %d, got %d)\"\n\nThis checker provides suggested fixes for type errors of the\ntype \"wrong number of return values (want %d, got %d)\". For example:\n\tfunc m() (int, string, *bool, error) {\n\t\treturn\n\t}\nwill turn into\n\tfunc m() (int, string, *bool, error) {\n\t\treturn 0, \"\", nil, nil\n\t}\n\nThis functionality is similar to https://github.com/sqs/goreturns.\n",
 							Default: "true",
@@ -905,6 +915,11 @@
 			Default: true,
 		},
 		{
+			Name:    "nilness",
+			Doc:     "check for redundant or impossible nil comparisons\n\nThe nilness checker inspects the control-flow graph of each function in\na package and reports nil pointer dereferences, degenerate nil\npointers, and panics with nil values. A degenerate comparison is of the form\nx==nil or x!=nil where x is statically known to be nil or non-nil. These are\noften a mistake, especially in control flow related to errors. Panics with nil\nvalues are checked because they are not detectable by\n\n\tif r := recover(); r != nil {\n\nThis check reports conditions such as:\n\n\tif f == nil { // impossible condition (f is a function)\n\t}\n\nand:\n\n\tp := &v\n\t...\n\tif p != nil { // tautological condition\n\t}\n\nand:\n\n\tif p == nil {\n\t\tprint(*p) // nil dereference\n\t}\n\nand:\n\n\tif p == nil {\n\t\tpanic(p)\n\t}\n",
+			Default: false,
+		},
+		{
 			Name:    "printf",
 			Doc:     "check consistency of Printf format strings and arguments\n\nThe check applies to known functions (for example, those in package fmt)\nas well as any detected wrappers of known functions.\n\nA function that wants to avail itself of printf checking but is not\nfound by this analyzer's heuristics (for example, due to use of\ndynamic calls) can insert a bogus call:\n\n\tif false {\n\t\t_ = fmt.Sprintf(format, args...) // enable printf checking\n\t}\n\nThe -funcs flag specifies a comma-separated list of names of additional\nknown formatting functions or methods. If the name contains a period,\nit must denote a specific function using one of the following forms:\n\n\tdir/pkg.Function\n\tdir/pkg.Type.Method\n\t(*dir/pkg.Type).Method\n\nOtherwise the name is interpreted as a case-insensitive unqualified\nidentifier such as \"errorf\". Either way, if a listed name ends in f, the\nfunction is assumed to be Printf-like, taking a format string before the\nargument list. Otherwise it is assumed to be Print-like, taking a list\nof arguments with no format string.\n",
 			Default: true,
@@ -990,6 +1005,11 @@
 			Default: true,
 		},
 		{
+			Name:    "unusedwrite",
+			Doc:     "checks for unused writes\n\nThe analyzer reports instances of writes to struct fields and\narrays that are never read. Specifically, when a struct object\nor an array is copied, its elements are copied implicitly by\nthe compiler, and any element write to this copy does nothing\nwith the original object.\n\nFor example:\n\n\ttype T struct { x int }\n\tfunc f(input []T) {\n\t\tfor i, v := range input {  // v is a copy\n\t\t\tv.x = i  // unused write to field x\n\t\t}\n\t}\n\nAnother example is about non-pointer receiver:\n\n\ttype T struct { x int }\n\tfunc (t T) f() {  // t is a copy\n\t\tt.x = i  // unused write to field x\n\t}\n",
+			Default: false,
+		},
+		{
 			Name:    "fillreturns",
 			Doc:     "suggested fixes for \"wrong number of return values (want %d, got %d)\"\n\nThis checker provides suggested fixes for type errors of the\ntype \"wrong number of return values (want %d, got %d)\". For example:\n\tfunc m() (int, string, *bool, error) {\n\t\treturn\n\t}\nwill turn into\n\tfunc m() (int, string, *bool, error) {\n\t\treturn 0, \"\", nil, nil\n\t}\n\nThis functionality is similar to https://github.com/sqs/goreturns.\n",
 			Default: true,
diff --git a/internal/lsp/source/completion/completion.go b/internal/lsp/source/completion/completion.go
index 4689b76..de6e53f 100644
--- a/internal/lsp/source/completion/completion.go
+++ b/internal/lsp/source/completion/completion.go
@@ -95,6 +95,7 @@
 	fullDocumentation bool
 	placeholders      bool
 	literal           bool
+	snippets          bool
 	matcher           source.Matcher
 	budget            time.Duration
 }
@@ -519,6 +520,7 @@
 			placeholders:      opts.UsePlaceholders,
 			literal:           opts.LiteralCompletions && opts.InsertTextFormat == protocol.SnippetTextFormat,
 			budget:            opts.CompletionBudget,
+			snippets:          opts.InsertTextFormat == protocol.SnippetTextFormat,
 		},
 		// default to a matcher that always matches
 		matcher:        prefixMatcher(""),
diff --git a/internal/lsp/source/completion/format.go b/internal/lsp/source/completion/format.go
index bf47a1d..ec67060 100644
--- a/internal/lsp/source/completion/format.go
+++ b/internal/lsp/source/completion/format.go
@@ -150,9 +150,8 @@
 		prefix = typeName + "(" + prefix
 		suffix = ")"
 	}
-
-	// Add variadic "..." if we are filling in a variadic param.
-	if cand.variadic {
+	// Add variadic "..." only if snippets if enabled or cand is not a function
+	if cand.variadic && (c.opts.snippets || !cand.expandFuncCall) {
 		suffix += "..."
 	}
 
diff --git a/internal/lsp/source/diagnostics.go b/internal/lsp/source/diagnostics.go
index f2b382d..1948381 100644
--- a/internal/lsp/source/diagnostics.go
+++ b/internal/lsp/source/diagnostics.go
@@ -7,9 +7,6 @@
 import (
 	"context"
 
-	"golang.org/x/tools/go/analysis"
-	"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/span"
 )
@@ -26,99 +23,41 @@
 	Message string
 }
 
-func GetTypeCheckDiagnostics(ctx context.Context, snapshot Snapshot, pkg Package) TypeCheckDiagnostics {
-	onlyIgnoredFiles := true
-	for _, pgf := range pkg.CompiledGoFiles() {
-		onlyIgnoredFiles = onlyIgnoredFiles && snapshot.IgnoredFile(pgf.URI)
-	}
-	if onlyIgnoredFiles {
-		return TypeCheckDiagnostics{}
-	}
-	return typeCheckDiagnostics(ctx, snapshot, pkg)
-}
-
-func Analyze(ctx context.Context, snapshot Snapshot, pkg Package, typeCheckResult TypeCheckDiagnostics) (map[span.URI][]*Diagnostic, error) {
+func Analyze(ctx context.Context, snapshot Snapshot, pkg Package, includeConvenience bool) (map[span.URI][]*Diagnostic, error) {
 	// Exit early if the context has been canceled. This also protects us
 	// from a race on Options, see golang/go#36699.
 	if ctx.Err() != nil {
 		return nil, ctx.Err()
 	}
-	// If we don't have any list or parse errors, run analyses.
-	analyzers := pickAnalyzers(snapshot, typeCheckResult.HasTypeErrors)
-	analysisDiagnostics, err := snapshot.Analyze(ctx, pkg.ID(), analyzers...)
+
+	categories := []map[string]*Analyzer{}
+	if includeConvenience {
+		categories = append(categories, snapshot.View().Options().ConvenienceAnalyzers)
+	}
+	// If we had type errors, don't run any other analyzers.
+	if !pkg.HasTypeErrors() {
+		categories = append(categories, snapshot.View().Options().DefaultAnalyzers, snapshot.View().Options().StaticcheckAnalyzers)
+	}
+	var analyzers []*Analyzer
+	for _, cat := range categories {
+		for _, a := range cat {
+			analyzers = append(analyzers, a)
+		}
+	}
+
+	analysisDiagnostics, err := snapshot.Analyze(ctx, pkg.ID(), analyzers)
 	if err != nil {
 		return nil, err
 	}
-	analysisDiagnostics = cloneDiagnostics(analysisDiagnostics)
 
-	reports := emptyDiagnostics(pkg)
+	reports := map[span.URI][]*Diagnostic{}
 	// Report diagnostics and errors from root analyzers.
 	for _, diag := range analysisDiagnostics {
-		// 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(string(diag.Source)) {
-			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.
-		var tags []protocol.DiagnosticTag
-		if onlyDeletions(diag.SuggestedFixes) {
-			tags = append(tags, protocol.Unnecessary)
-		}
-		// Type error analyzers only alter the tags for existing type errors.
-		if _, ok := snapshot.View().Options().TypeErrorAnalyzers[string(diag.Source)]; ok {
-			existingDiagnostics := typeCheckResult.Diagnostics[diag.URI]
-			for _, existing := range existingDiagnostics {
-				if r := protocol.CompareRange(diag.Range, existing.Range); r != 0 {
-					continue
-				}
-				if diag.Message != existing.Message {
-					continue
-				}
-				existing.Tags = append(existing.Tags, tags...)
-			}
-		} else {
-			diag.Tags = append(diag.Tags, tags...)
-			reports[diag.URI] = append(reports[diag.URI], diag)
-		}
+		reports[diag.URI] = append(reports[diag.URI], diag)
 	}
 	return reports, nil
 }
 
-// cloneDiagnostics makes a shallow copy of diagnostics so that Analyze
-// can add tags to them without affecting the cached diagnostics.
-func cloneDiagnostics(diags []*Diagnostic) []*Diagnostic {
-	result := []*Diagnostic{}
-	for _, d := range diags {
-		clone := *d
-		result = append(result, &clone)
-	}
-	return result
-}
-
-func pickAnalyzers(snapshot Snapshot, hadTypeErrors bool) []*analysis.Analyzer {
-	// Always run convenience analyzers.
-	categories := []map[string]Analyzer{snapshot.View().Options().ConvenienceAnalyzers}
-	// If we had type errors, only run type error analyzers.
-	if hadTypeErrors {
-		categories = append(categories, snapshot.View().Options().TypeErrorAnalyzers)
-	} else {
-		categories = append(categories, snapshot.View().Options().DefaultAnalyzers, snapshot.View().Options().StaticcheckAnalyzers)
-	}
-	var analyzers []*analysis.Analyzer
-	for _, m := range categories {
-		for _, a := range m {
-			if a.IsEnabled(snapshot.View()) {
-				analyzers = append(analyzers, a.Analyzer)
-			}
-		}
-	}
-	return analyzers
-}
-
 func FileDiagnostics(ctx context.Context, snapshot Snapshot, uri span.URI) (VersionedFileIdentity, []*Diagnostic, error) {
 	fh, err := snapshot.GetVersionedFile(ctx, uri)
 	if err != nil {
@@ -128,97 +67,19 @@
 	if err != nil {
 		return VersionedFileIdentity{}, nil, err
 	}
-	typeCheckResults := GetTypeCheckDiagnostics(ctx, snapshot, pkg)
-	diagnostics := typeCheckResults.Diagnostics[fh.URI()]
-	if !typeCheckResults.HasParseOrListErrors {
-		reports, err := Analyze(ctx, snapshot, pkg, typeCheckResults)
+	diagnostics, err := snapshot.DiagnosePackage(ctx, pkg)
+	if err != nil {
+		return VersionedFileIdentity{}, nil, err
+	}
+	fileDiags := diagnostics[fh.URI()]
+	if !pkg.HasListOrParseErrors() {
+		analysisDiags, err := Analyze(ctx, snapshot, pkg, false)
 		if err != nil {
 			return VersionedFileIdentity{}, nil, err
 		}
-		diagnostics = append(diagnostics, reports[fh.URI()]...)
+		fileDiags = append(fileDiags, analysisDiags[fh.URI()]...)
 	}
-	return fh.VersionedFileIdentity(), diagnostics, nil
-}
-
-type TypeCheckDiagnostics struct {
-	HasTypeErrors        bool
-	HasParseOrListErrors bool
-	Diagnostics          map[span.URI][]*Diagnostic
-}
-
-type diagnosticSet struct {
-	listErrors, parseErrors, typeErrors []*Diagnostic
-}
-
-func typeCheckDiagnostics(ctx context.Context, snapshot Snapshot, pkg Package) TypeCheckDiagnostics {
-	ctx, done := event.Start(ctx, "source.typeCheckDiagnostics", tag.Package.Of(pkg.ID()))
-	_ = ctx // circumvent SA4006
-	defer done()
-
-	diagSets := make(map[span.URI]*diagnosticSet)
-	for _, diag := range pkg.GetDiagnostics() {
-		set, ok := diagSets[diag.URI]
-		if !ok {
-			set = &diagnosticSet{}
-			diagSets[diag.URI] = set
-		}
-		switch diag.Source {
-		case ParseError:
-			set.parseErrors = append(set.parseErrors, diag)
-		case TypeError:
-			set.typeErrors = append(set.typeErrors, diag)
-		case ListError:
-			set.listErrors = append(set.listErrors, diag)
-		}
-	}
-	typecheck := TypeCheckDiagnostics{
-		Diagnostics: emptyDiagnostics(pkg),
-	}
-	for uri, set := range diagSets {
-		// Don't report type errors if there are parse errors or list errors.
-		diags := set.typeErrors
-		switch {
-		case len(set.parseErrors) > 0:
-			typecheck.HasParseOrListErrors = true
-			diags = set.parseErrors
-		case len(set.listErrors) > 0:
-			typecheck.HasParseOrListErrors = true
-			if len(pkg.MissingDependencies()) > 0 {
-				diags = set.listErrors
-			}
-		case len(set.typeErrors) > 0:
-			typecheck.HasTypeErrors = true
-		}
-		typecheck.Diagnostics[uri] = cloneDiagnostics(diags)
-	}
-	return typecheck
-}
-
-func emptyDiagnostics(pkg Package) map[span.URI][]*Diagnostic {
-	diags := map[span.URI][]*Diagnostic{}
-	for _, pgf := range pkg.CompiledGoFiles() {
-		if _, ok := diags[pgf.URI]; !ok {
-			diags[pgf.URI] = nil
-		}
-	}
-	return diags
-}
-
-// onlyDeletions returns true if all of the suggested fixes are deletions.
-func onlyDeletions(fixes []SuggestedFix) bool {
-	for _, fix := range fixes {
-		for _, edits := range fix.Edits {
-			for _, edit := range edits {
-				if edit.NewText != "" {
-					return false
-				}
-				if protocol.ComparePosition(edit.Range.Start, edit.Range.End) == 0 {
-					return false
-				}
-			}
-		}
-	}
-	return len(fixes) > 0
+	return fh.VersionedFileIdentity(), fileDiags, nil
 }
 
 func isConvenienceAnalyzer(category string) bool {
diff --git a/internal/lsp/source/folding_range.go b/internal/lsp/source/folding_range.go
index 05b1d60..00e6ba0 100644
--- a/internal/lsp/source/folding_range.go
+++ b/internal/lsp/source/folding_range.go
@@ -27,6 +27,19 @@
 	if err != nil {
 		return nil, err
 	}
+
+	// With parse errors, we wouldn't be able to produce accurate folding info.
+	// LSP protocol (3.16) currently does not have a way to handle this case
+	// (https://github.com/microsoft/language-server-protocol/issues/1200).
+	// We cannot return an error either because we are afraid some editors
+	// may not handle errors nicely. As a workaround, we now return an empty
+	// result and let the client handle this case by double check the file
+	// contents (i.e. if the file is not empty and the folding range result
+	// is empty, raise an internal error).
+	if pgf.ParseErr != nil {
+		return nil, nil
+	}
+
 	fset := snapshot.FileSet()
 
 	// Get folding ranges for comments separately as they are not walked by ast.Inspect.
diff --git a/internal/lsp/source/gc_annotations.go b/internal/lsp/source/gc_annotations.go
index f2a0e67..3616bbf 100644
--- a/internal/lsp/source/gc_annotations.go
+++ b/internal/lsp/source/gc_annotations.go
@@ -35,7 +35,11 @@
 	Bounds Annotation = "bounds"
 )
 
-func GCOptimizationDetails(ctx context.Context, snapshot Snapshot, pkgDir span.URI) (map[VersionedFileIdentity][]*Diagnostic, error) {
+func GCOptimizationDetails(ctx context.Context, snapshot Snapshot, pkg Package) (map[VersionedFileIdentity][]*Diagnostic, error) {
+	if len(pkg.CompiledGoFiles()) == 0 {
+		return nil, nil
+	}
+	pkgDir := filepath.Dir(pkg.CompiledGoFiles()[0].URI.Filename())
 	outDir := filepath.Join(os.TempDir(), fmt.Sprintf("gopls-%d.details", os.Getpid()))
 
 	if err := os.MkdirAll(outDir, 0700); err != nil {
@@ -60,7 +64,7 @@
 			fmt.Sprintf("-o=%s", tmpFile.Name()),
 			".",
 		},
-		WorkingDir: pkgDir.Filename(),
+		WorkingDir: pkgDir,
 	}
 	_, err = snapshot.RunGoCommandDirect(ctx, Normal, inv)
 	if err != nil {
@@ -83,7 +87,7 @@
 		if fh == nil {
 			continue
 		}
-		if pkgDir.Filename() != filepath.Dir(fh.URI().Filename()) {
+		if pkgDir != filepath.Dir(fh.URI().Filename()) {
 			// https://github.com/golang/go/issues/42198
 			// sometimes the detail diagnostics generated for files
 			// outside the package can never be taken back.
diff --git a/internal/lsp/source/hover.go b/internal/lsp/source/hover.go
index 21e1c57..8f23d1d 100644
--- a/internal/lsp/source/hover.go
+++ b/internal/lsp/source/hover.go
@@ -9,10 +9,12 @@
 	"encoding/json"
 	"fmt"
 	"go/ast"
+	"go/constant"
 	"go/doc"
 	"go/format"
 	"go/types"
 	"strings"
+	"time"
 
 	"golang.org/x/tools/internal/event"
 	"golang.org/x/tools/internal/lsp/protocol"
@@ -232,6 +234,18 @@
 	switch obj := obj.(type) {
 	case *types.Const:
 		str = fmt.Sprintf("%s = %s", str, obj.Val())
+
+		// Try to add a formatted duration as an inline comment
+		typ, ok := obj.Type().(*types.Named)
+		if !ok {
+			break
+		}
+		pkg := typ.Obj().Pkg()
+		if pkg.Path() == "time" && typ.Obj().Name() == "Duration" {
+			if d, ok := constant.Int64Val(obj.Val()); ok {
+				str += " // " + time.Duration(d).String()
+			}
+		}
 	}
 	return str
 }
diff --git a/internal/lsp/source/options.go b/internal/lsp/source/options.go
index 05d3c9a..0f8dda1 100644
--- a/internal/lsp/source/options.go
+++ b/internal/lsp/source/options.go
@@ -31,6 +31,7 @@
 	"golang.org/x/tools/go/analysis/passes/loopclosure"
 	"golang.org/x/tools/go/analysis/passes/lostcancel"
 	"golang.org/x/tools/go/analysis/passes/nilfunc"
+	"golang.org/x/tools/go/analysis/passes/nilness"
 	"golang.org/x/tools/go/analysis/passes/printf"
 	"golang.org/x/tools/go/analysis/passes/shadow"
 	"golang.org/x/tools/go/analysis/passes/shift"
@@ -44,6 +45,7 @@
 	"golang.org/x/tools/go/analysis/passes/unreachable"
 	"golang.org/x/tools/go/analysis/passes/unsafeptr"
 	"golang.org/x/tools/go/analysis/passes/unusedresult"
+	"golang.org/x/tools/go/analysis/passes/unusedwrite"
 	"golang.org/x/tools/internal/lsp/analysis/fillreturns"
 	"golang.org/x/tools/internal/lsp/analysis/fillstruct"
 	"golang.org/x/tools/internal/lsp/analysis/nonewvars"
@@ -95,6 +97,7 @@
 					},
 					Mod: {
 						protocol.SourceOrganizeImports: true,
+						protocol.QuickFix:              true,
 					},
 					Sum: {},
 				},
@@ -152,7 +155,7 @@
 				DefaultAnalyzers:     defaultAnalyzers(),
 				TypeErrorAnalyzers:   typeErrorAnalyzers(),
 				ConvenienceAnalyzers: convenienceAnalyzers(),
-				StaticcheckAnalyzers: map[string]Analyzer{},
+				StaticcheckAnalyzers: map[string]*Analyzer{},
 				GoDiff:               true,
 			},
 		}
@@ -182,6 +185,7 @@
 	HierarchicalDocumentSymbolSupport bool
 	SemanticTypes                     []string
 	SemanticMods                      []string
+	RelatedInformationSupported       bool
 }
 
 // ServerOptions holds LSP-specific configuration that is provided by the
@@ -416,10 +420,10 @@
 	ComputeEdits         diff.ComputeEdits
 	URLRegexp            *regexp.Regexp
 	GofumptFormat        func(ctx context.Context, src []byte) ([]byte, error)
-	DefaultAnalyzers     map[string]Analyzer
-	TypeErrorAnalyzers   map[string]Analyzer
-	ConvenienceAnalyzers map[string]Analyzer
-	StaticcheckAnalyzers map[string]Analyzer
+	DefaultAnalyzers     map[string]*Analyzer
+	TypeErrorAnalyzers   map[string]*Analyzer
+	ConvenienceAnalyzers map[string]*Analyzer
+	StaticcheckAnalyzers map[string]*Analyzer
 }
 
 // InternalOptions contains settings that are not intended for use by the
@@ -615,6 +619,9 @@
 	o.SemanticMods = caps.TextDocument.SemanticTokens.TokenModifiers
 	// we don't need Requests, as we support full functionality
 	// we don't need Formats, as there is only one, for now
+
+	// Check if the client supports diagnostic related information.
+	o.RelatedInformationSupported = caps.TextDocument.PublishDiagnostics.RelatedInformation
 }
 
 func (o *Options) Clone() *Options {
@@ -651,8 +658,8 @@
 	result.BuildFlags = copySlice(o.BuildFlags)
 	result.DirectoryFilters = copySlice(o.DirectoryFilters)
 
-	copyAnalyzerMap := func(src map[string]Analyzer) map[string]Analyzer {
-		dst := make(map[string]Analyzer)
+	copyAnalyzerMap := func(src map[string]*Analyzer) map[string]*Analyzer {
+		dst := make(map[string]*Analyzer)
 		for k, v := range src {
 			dst[k] = v
 		}
@@ -666,7 +673,7 @@
 }
 
 func (o *Options) AddStaticcheckAnalyzer(a *analysis.Analyzer) {
-	o.StaticcheckAnalyzers[a.Name] = Analyzer{Analyzer: a, Enabled: true}
+	o.StaticcheckAnalyzers[a.Name] = &Analyzer{Analyzer: a, Enabled: true}
 }
 
 // enableAllExperiments turns on all of the experimental "off-by-default"
@@ -1045,7 +1052,7 @@
 
 // EnabledAnalyzers returns all of the analyzers enabled for the given
 // snapshot.
-func EnabledAnalyzers(snapshot Snapshot) (analyzers []Analyzer) {
+func EnabledAnalyzers(snapshot Snapshot) (analyzers []*Analyzer) {
 	for _, a := range snapshot.View().Options().DefaultAnalyzers {
 		if a.IsEnabled(snapshot.View()) {
 			analyzers = append(analyzers, a)
@@ -1069,45 +1076,42 @@
 	return analyzers
 }
 
-func typeErrorAnalyzers() map[string]Analyzer {
-	return map[string]Analyzer{
+func typeErrorAnalyzers() map[string]*Analyzer {
+	return map[string]*Analyzer{
 		fillreturns.Analyzer.Name: {
-			Analyzer:       fillreturns.Analyzer,
-			FixesError:     fillreturns.FixesError,
-			HighConfidence: true,
-			Enabled:        true,
+			Analyzer:   fillreturns.Analyzer,
+			ActionKind: protocol.SourceFixAll,
+			Enabled:    true,
 		},
 		nonewvars.Analyzer.Name: {
-			Analyzer:   nonewvars.Analyzer,
-			FixesError: nonewvars.FixesError,
-			Enabled:    true,
+			Analyzer: nonewvars.Analyzer,
+			Enabled:  true,
 		},
 		noresultvalues.Analyzer.Name: {
-			Analyzer:   noresultvalues.Analyzer,
-			FixesError: noresultvalues.FixesError,
-			Enabled:    true,
+			Analyzer: noresultvalues.Analyzer,
+			Enabled:  true,
 		},
 		undeclaredname.Analyzer.Name: {
-			Analyzer:   undeclaredname.Analyzer,
-			FixesError: undeclaredname.FixesError,
-			Fix:        UndeclaredName,
-			Enabled:    true,
-		},
-	}
-}
-
-func convenienceAnalyzers() map[string]Analyzer {
-	return map[string]Analyzer{
-		fillstruct.Analyzer.Name: {
-			Analyzer: fillstruct.Analyzer,
-			Fix:      FillStruct,
+			Analyzer: undeclaredname.Analyzer,
+			Fix:      UndeclaredName,
 			Enabled:  true,
 		},
 	}
 }
 
-func defaultAnalyzers() map[string]Analyzer {
-	return map[string]Analyzer{
+func convenienceAnalyzers() map[string]*Analyzer {
+	return map[string]*Analyzer{
+		fillstruct.Analyzer.Name: {
+			Analyzer:   fillstruct.Analyzer,
+			Fix:        FillStruct,
+			Enabled:    true,
+			ActionKind: protocol.RefactorRewrite,
+		},
+	}
+}
+
+func defaultAnalyzers() map[string]*Analyzer {
+	return map[string]*Analyzer{
 		// The traditional vet suite:
 		asmdecl.Analyzer.Name:       {Analyzer: asmdecl.Analyzer, Enabled: true},
 		assign.Analyzer.Name:        {Analyzer: assign.Analyzer, Enabled: true},
@@ -1138,15 +1142,17 @@
 		atomicalign.Analyzer.Name:      {Analyzer: atomicalign.Analyzer, Enabled: true},
 		deepequalerrors.Analyzer.Name:  {Analyzer: deepequalerrors.Analyzer, Enabled: true},
 		fieldalignment.Analyzer.Name:   {Analyzer: fieldalignment.Analyzer, Enabled: false},
+		nilness.Analyzer.Name:          {Analyzer: nilness.Analyzer, Enabled: false},
 		shadow.Analyzer.Name:           {Analyzer: shadow.Analyzer, Enabled: false},
 		sortslice.Analyzer.Name:        {Analyzer: sortslice.Analyzer, Enabled: true},
 		testinggoroutine.Analyzer.Name: {Analyzer: testinggoroutine.Analyzer, Enabled: true},
 		unusedparams.Analyzer.Name:     {Analyzer: unusedparams.Analyzer, Enabled: false},
+		unusedwrite.Analyzer.Name:      {Analyzer: unusedwrite.Analyzer, Enabled: false},
 
 		// gofmt -s suite:
-		simplifycompositelit.Analyzer.Name: {Analyzer: simplifycompositelit.Analyzer, Enabled: true, HighConfidence: true},
-		simplifyrange.Analyzer.Name:        {Analyzer: simplifyrange.Analyzer, Enabled: true, HighConfidence: true},
-		simplifyslice.Analyzer.Name:        {Analyzer: simplifyslice.Analyzer, Enabled: true, HighConfidence: true},
+		simplifycompositelit.Analyzer.Name: {Analyzer: simplifycompositelit.Analyzer, Enabled: true, ActionKind: protocol.SourceFixAll},
+		simplifyrange.Analyzer.Name:        {Analyzer: simplifyrange.Analyzer, Enabled: true, ActionKind: protocol.SourceFixAll},
+		simplifyslice.Analyzer.Name:        {Analyzer: simplifyslice.Analyzer, Enabled: true, ActionKind: protocol.SourceFixAll},
 	}
 }
 
diff --git a/internal/lsp/source/rename_check.go b/internal/lsp/source/rename_check.go
index 0f1bf92..a46254c 100644
--- a/internal/lsp/source/rename_check.go
+++ b/internal/lsp/source/rename_check.go
@@ -812,7 +812,7 @@
 			// type-checker.
 			//
 			// Only proceed if all packages have no errors.
-			if diags := pkg.GetDiagnostics(); len(diags) > 0 {
+			if pkg.HasListOrParseErrors() || pkg.HasTypeErrors() {
 				r.errorf(token.NoPos, // we don't have a position for this error.
 					"renaming %q to %q not possible because %q has errors",
 					r.from, r.to, pkg.PkgPath())
diff --git a/internal/lsp/source/source_test.go b/internal/lsp/source/source_test.go
index deb6f6e..8064b0d 100644
--- a/internal/lsp/source/source_test.go
+++ b/internal/lsp/source/source_test.go
@@ -376,8 +376,8 @@
 			return []byte(got), nil
 		}))
 
-		if want != got {
-			t.Errorf("%s: foldingRanges failed for %s, expected:\n%v\ngot:\n%v", tag, uri.Filename(), want, got)
+		if diff := tests.Diff(t, want, got); diff != "" {
+			t.Errorf("%s: foldingRanges failed for %s, diff:\n%v", tag, uri.Filename(), diff)
 		}
 	}
 
@@ -403,8 +403,8 @@
 				return []byte(got), nil
 			}))
 
-			if want != got {
-				t.Errorf("%s: failed for %s, expected:\n%v\ngot:\n%v", tag, uri.Filename(), want, got)
+			if diff := tests.Diff(t, want, got); diff != "" {
+				t.Errorf("%s: failed for %s, diff:\n%v", tag, uri.Filename(), diff)
 			}
 		}
 
diff --git a/internal/lsp/source/view.go b/internal/lsp/source/view.go
index 5c96908..9bc5eca 100644
--- a/internal/lsp/source/view.go
+++ b/internal/lsp/source/view.go
@@ -9,6 +9,7 @@
 	"context"
 	"fmt"
 	"go/ast"
+	"go/scanner"
 	"go/token"
 	"go/types"
 	"io"
@@ -83,8 +84,12 @@
 	// string for the objects.
 	PosToDecl(ctx context.Context, pgf *ParsedGoFile) (map[token.Pos]ast.Decl, error)
 
+	// DiagnosePackage returns basic diagnostics, including list, parse, and type errors
+	// for pkg, grouped by file.
+	DiagnosePackage(ctx context.Context, pkg Package) (map[span.URI][]*Diagnostic, error)
+
 	// Analyze runs the analyses for the given package at this snapshot.
-	Analyze(ctx context.Context, pkgID string, analyzers ...*analysis.Analyzer) ([]*Diagnostic, error)
+	Analyze(ctx context.Context, pkgID string, analyzers []*Analyzer) ([]*Diagnostic, error)
 
 	// RunGoCommandPiped runs the given `go` command, writing its output
 	// to stdout and stderr. Verb, Args, and WorkingDir must be specified.
@@ -262,7 +267,7 @@
 	// actual content of the file if we have fixed the AST.
 	Src      []byte
 	Mapper   *protocol.ColumnMapper
-	ParseErr error
+	ParseErr scanner.ErrorList
 }
 
 // A ParsedModule contains the results of parsing a go.mod file.
@@ -515,14 +520,9 @@
 	// the analyzer's suggested fixes through a Command, not a TextEdit.
 	Fix string
 
-	// If this is true, then we can apply the suggested fixes
-	// as part of a source.FixAll codeaction.
-	HighConfidence bool
-
-	// FixesError is only set for type-error analyzers.
-	// It reports true if the message provided indicates an error that could be
-	// fixed by the analyzer.
-	FixesError func(msg string) bool
+	// ActionKind is the kind of code action this analyzer produces. If
+	// unspecified the type defaults to quickfix.
+	ActionKind protocol.CodeActionKind
 }
 
 func (a Analyzer) IsEnabled(view View) bool {
@@ -547,7 +547,6 @@
 	CompiledGoFiles() []*ParsedGoFile
 	File(uri span.URI) (*ParsedGoFile, error)
 	GetSyntax() []*ast.File
-	GetDiagnostics() []*Diagnostic
 	GetTypes() *types.Package
 	GetTypesInfo() *types.Info
 	GetTypesSizes() types.Sizes
@@ -557,6 +556,8 @@
 	MissingDependencies() []string
 	Imports() []Package
 	Version() *module.Version
+	HasListOrParseErrors() bool
+	HasTypeErrors() bool
 }
 
 type CriticalError struct {
@@ -584,9 +585,10 @@
 	Tags    []protocol.DiagnosticTag
 	Related []RelatedInformation
 
-	// SuggestedFixes is used to generate quick fixes for a CodeAction request.
-	// It isn't part of the Diagnostic type.
+	// Fields below are used internally to generate quick fixes. They aren't
+	// part of the LSP spec and don't leave the server.
 	SuggestedFixes []SuggestedFix
+	Analyzer       *Analyzer
 }
 
 type DiagnosticSource string
diff --git a/internal/lsp/source/workspace_symbol.go b/internal/lsp/source/workspace_symbol.go
index fb40e7d..c0aabf2 100644
--- a/internal/lsp/source/workspace_symbol.go
+++ b/internal/lsp/source/workspace_symbol.go
@@ -351,14 +351,9 @@
 		case *ast.FuncDecl:
 			kind := protocol.Function
 			var recv *ast.Ident
-			if decl.Recv != nil {
+			if decl.Recv.NumFields() > 0 {
 				kind = protocol.Method
-				switch typ := decl.Recv.List[0].Type.(type) {
-				case *ast.StarExpr:
-					recv = typ.X.(*ast.Ident)
-				case *ast.Ident:
-					recv = typ
-				}
+				recv = unpackRecv(decl.Recv.List[0].Type)
 			}
 			if recv != nil {
 				sc.match(decl.Name.Name, kind, decl.Name, recv)
@@ -385,6 +380,25 @@
 	}
 }
 
+func unpackRecv(rtyp ast.Expr) *ast.Ident {
+	// Extract the receiver identifier. Lifted from go/types/resolver.go
+L:
+	for {
+		switch t := rtyp.(type) {
+		case *ast.ParenExpr:
+			rtyp = t.X
+		case *ast.StarExpr:
+			rtyp = t.X
+		default:
+			break L
+		}
+	}
+	if name, _ := rtyp.(*ast.Ident); name != nil {
+		return name
+	}
+	return nil
+}
+
 // walkType processes symbols related to a type expression. path is path of
 // nested type identifiers to the type expression.
 func (sc *symbolCollector) walkType(typ ast.Expr, path ...*ast.Ident) {
diff --git a/internal/lsp/testdata/folding/a.go b/internal/lsp/testdata/folding/a.go
index ffdc2a5..76b26c1 100644
--- a/internal/lsp/testdata/folding/a.go
+++ b/internal/lsp/testdata/folding/a.go
@@ -28,7 +28,7 @@
 		3,
 	}
 	_ = [2]string{"d",
-		"e"
+		"e",
 	}
 	_ = map[string]int{
 		"a": 1,
diff --git a/internal/lsp/testdata/folding/a.go.golden b/internal/lsp/testdata/folding/a.go.golden
index f822736..d8341f7 100644
--- a/internal/lsp/testdata/folding/a.go.golden
+++ b/internal/lsp/testdata/folding/a.go.golden
@@ -59,7 +59,7 @@
 		3,
 	}
 	_ = [2]string{"d",
-		"e"
+		"e",
 	}
 	_ = map[string]int{
 		"a": 1,
@@ -116,7 +116,7 @@
 		3,
 	}
 	_ = [2]string{"d",
-		"e"
+		"e",
 	}
 	_ = map[string]int{
 		"a": 1,
@@ -180,7 +180,7 @@
 		3,
 	}
 	_ = [2]string{"d",
-		"e"
+		"e",
 	}
 	_ = map[string]int{
 		"a": 1,
@@ -281,7 +281,7 @@
 		3,
 	}
 	_ = [2]string{"d",
-		"e"
+		"e",
 	}
 	_ = map[string]int{
 		"a": 1,
@@ -345,7 +345,7 @@
 		3,
 	}
 	_ = [2]string{"d",
-		"e"
+		"e",
 	}
 	_ = map[string]int{
 		"a": 1,
@@ -412,7 +412,7 @@
 	_ = []int{<>,
 	}
 	_ = [2]string{"d",
-		"e"
+		"e",
 	}
 	_ = map[string]int{<>,
 	}
@@ -453,7 +453,7 @@
 		3,
 	}
 	_ = [2]string{"d",
-		"e"
+		"e",
 	}
 	_ = map[string]int{
 		"a": 1,
@@ -512,7 +512,7 @@
 		3,
 	}
 	_ = [2]string{"d",
-		"e"
+		"e",
 	}
 	_ = map[string]int{
 		"a": 1,
@@ -577,7 +577,7 @@
 		3,
 	}
 	_ = [2]string{"d",
-		"e"
+		"e",
 	}
 	_ = map[string]int{
 		"a": 1,
@@ -642,7 +642,7 @@
 		3,
 	}
 	_ = [2]string{"d",
-		"e"
+		"e",
 	}
 	_ = map[string]int{
 		"a": 1,
diff --git a/internal/lsp/testdata/godef/a/g.go b/internal/lsp/testdata/godef/a/g.go
new file mode 100644
index 0000000..4f31857
--- /dev/null
+++ b/internal/lsp/testdata/godef/a/g.go
@@ -0,0 +1,6 @@
+package a
+
+import "time"
+
+// dur is a constant of type time.Duration.
+const dur = 15*time.Minute + 10*time.Second + 350*time.Millisecond //@dur,hover("dur", dur)
diff --git a/internal/lsp/testdata/godef/a/g.go.golden b/internal/lsp/testdata/godef/a/g.go.golden
new file mode 100644
index 0000000..d46ff04
--- /dev/null
+++ b/internal/lsp/testdata/godef/a/g.go.golden
@@ -0,0 +1,6 @@
+-- dur-hover --
+```go
+const dur time.Duration = 910350000000 // 15m10.35s
+```
+
+dur is a constant of type time\.Duration\.
diff --git a/internal/lsp/testdata/summary.txt.golden b/internal/lsp/testdata/summary.txt.golden
index f9b6c38..e0b6366 100644
--- a/internal/lsp/testdata/summary.txt.golden
+++ b/internal/lsp/testdata/summary.txt.golden
@@ -15,7 +15,7 @@
 SemanticTokenCount = 3
 SuggestedFixCount = 40
 FunctionExtractionCount = 12
-DefinitionsCount = 64
+DefinitionsCount = 65
 TypeDefinitionsCount = 2
 HighlightsCount = 69
 ReferencesCount = 25
diff --git a/internal/lsp/testdata/workspacesymbol/issue44806.go b/internal/lsp/testdata/workspacesymbol/issue44806.go
new file mode 100644
index 0000000..6a6e03a
--- /dev/null
+++ b/internal/lsp/testdata/workspacesymbol/issue44806.go
@@ -0,0 +1,10 @@
+package main
+
+type T struct{}
+
+// We should accept all valid receiver syntax when scanning symbols.
+func (*(T)) m1() {}
+func (*T) m2()   {}
+func (T) m3()    {}
+func ((T)) m4()    {}
+func ((*T)) m5()   {}
diff --git a/internal/span/parse.go b/internal/span/parse.go
index aa17c84..c4cec16 100644
--- a/internal/span/parse.go
+++ b/internal/span/parse.go
@@ -5,6 +5,7 @@
 package span
 
 import (
+	"path/filepath"
 	"strconv"
 	"strings"
 	"unicode/utf8"
@@ -15,6 +16,17 @@
 // The returned span will be normalized, and thus if printed may produce a
 // different string.
 func Parse(input string) Span {
+	return ParseInDir(input, ".")
+}
+
+// ParseInDir is like Parse, but interprets paths relative to wd.
+func ParseInDir(input, wd string) Span {
+	uri := func(path string) URI {
+		if !filepath.IsAbs(path) {
+			path = filepath.Join(wd, path)
+		}
+		return URIFromPath(path)
+	}
 	// :0:0#0-0:0#0
 	valid := input
 	var hold, offset int
@@ -32,12 +44,12 @@
 	}
 	switch {
 	case suf.sep == ":":
-		return New(URIFromPath(suf.remains), NewPoint(suf.num, hold, offset), Point{})
+		return New(uri(suf.remains), NewPoint(suf.num, hold, offset), Point{})
 	case suf.sep == "-":
 		// we have a span, fall out of the case to continue
 	default:
 		// separator not valid, rewind to either the : or the start
-		return New(URIFromPath(valid), NewPoint(hold, 0, offset), Point{})
+		return New(uri(valid), NewPoint(hold, 0, offset), Point{})
 	}
 	// only the span form can get here
 	// at this point we still don't know what the numbers we have mean
@@ -53,20 +65,20 @@
 	}
 	if suf.sep != ":" {
 		// turns out we don't have a span after all, rewind
-		return New(URIFromPath(valid), end, Point{})
+		return New(uri(valid), end, Point{})
 	}
 	valid = suf.remains
 	hold = suf.num
 	suf = rstripSuffix(suf.remains)
 	if suf.sep != ":" {
 		// line#offset only
-		return New(URIFromPath(valid), NewPoint(hold, 0, offset), end)
+		return New(uri(valid), NewPoint(hold, 0, offset), end)
 	}
 	// we have a column, so if end only had one number, it is also the column
 	if !hadCol {
 		end = NewPoint(suf.num, end.v.Line, end.v.Offset)
 	}
-	return New(URIFromPath(suf.remains), NewPoint(suf.num, hold, offset), end)
+	return New(uri(suf.remains), NewPoint(suf.num, hold, offset), end)
 }
 
 type suffix struct {
diff --git a/playground/playground.go b/playground/playground.go
index 64f1519..e3a7a31 100644
--- a/playground/playground.go
+++ b/playground/playground.go
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Package playground registers HTTP handlers at "/compile" and "/share" that
-// proxy requests to the golang.org playground service.
+// Package playground registers an HTTP handler at "/compile" that
+// proxies requests to the golang.org playground service.
 // This package may be used unaltered on App Engine Standard with Go 1.11+ runtime.
 package playground // import "golang.org/x/tools/playground"
 
@@ -14,27 +14,31 @@
 	"io"
 	"log"
 	"net/http"
-	"os"
-	"strings"
 	"time"
-
-	"golang.org/x/tools/godoc/golangorgenv"
 )
 
 const baseURL = "https://play.golang.org"
 
 func init() {
-	http.HandleFunc("/compile", bounce)
-	http.HandleFunc("/share", bounce)
+	http.Handle("/compile", Proxy())
+}
+
+// Proxy returns a handler that can be registered on /compile to proxy requests to the Go playground.
+//
+// This package already contains a func init that does:
+//
+//	func init() {
+//		http.Handle("/compile", Proxy())
+//	}
+//
+// Proxy may be useful for servers that use HTTP muxes other than the default mux.
+func Proxy() http.Handler {
+	return http.HandlerFunc(bounce)
 }
 
 func bounce(w http.ResponseWriter, r *http.Request) {
 	b := new(bytes.Buffer)
-	if err := passThru(b, r); os.IsPermission(err) {
-		http.Error(w, "403 Forbidden", http.StatusForbidden)
-		log.Println(err)
-		return
-	} else if err != nil {
+	if err := passThru(b, r); err != nil {
 		http.Error(w, "500 Internal Server Error", http.StatusInternalServerError)
 		log.Println(err)
 		return
@@ -43,9 +47,6 @@
 }
 
 func passThru(w io.Writer, req *http.Request) error {
-	if req.URL.Path == "/share" && googleCN(req) {
-		return os.ErrPermission
-	}
 	defer req.Body.Close()
 	url := baseURL + req.URL.Path
 	ctx, cancel := context.WithTimeout(req.Context(), 60*time.Second)
@@ -69,22 +70,3 @@
 	req.Header.Set("Content-Type", contentType)
 	return http.DefaultClient.Do(req.WithContext(ctx))
 }
-
-// googleCN reports whether request r is considered
-// to be served from golang.google.cn.
-func googleCN(r *http.Request) bool {
-	if r.FormValue("googlecn") != "" {
-		return true
-	}
-	if strings.HasSuffix(r.Host, ".cn") {
-		return true
-	}
-	if !golangorgenv.CheckCountry() {
-		return false
-	}
-	switch r.Header.Get("X-Appengine-Country") {
-	case "", "ZZ", "CN":
-		return true
-	}
-	return false
-}