1
0
mirror of https://github.com/golang/go synced 2024-11-18 16:54:43 -07:00

internal/lsp: add completions of unimported std lib pkgs

Unimported packages may be suggested as completion items. Since these
are not yet imported, they should be ranked lower than other candidates.

They also require an additional import statement to be valid, which is
provided as an AdditionalTextEdit.

Adding this import does not use astutil.AddNamedImport, to avoid
editing the current ast and work even if there are errors. Additionally,
it can be hard to determine what changes need to be made to the source
document from the ast, as astutil.AddNamedImport includes a merging
pass. Instead, the completion item simply adds another import
declaration.

Change-Id: Icbde226d843bd49ee3713cafcbd5299d51530695
Reviewed-on: https://go-review.googlesource.com/c/tools/+/190338
Run-TryBot: Suzy Mueller <suzmue@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Suzy Mueller 2019-08-14 17:25:47 -04:00
parent a857023c21
commit caa95bb40b
13 changed files with 822 additions and 17 deletions

View File

@ -35,6 +35,7 @@ func (s *Server) completion(ctx context.Context, params *protocol.CompletionPara
DeepComplete: s.useDeepCompletions,
WantDocumentaton: s.wantCompletionDocumentation,
WantFullDocumentation: s.hoverKind == fullDocumentation,
WantUnimported: s.wantUnimportedCompletions,
})
if err != nil {
log.Print(ctx, "no completions found", tag.Of("At", rng), tag.Of("Failure", err))
@ -96,7 +97,11 @@ func (s *Server) toProtocolCompletionItems(ctx context.Context, view source.View
if s.insertTextFormat == protocol.SnippetTextFormat {
insertText = candidate.Snippet(s.usePlaceholders)
}
addlEdits, err := ToProtocolEdits(m, candidate.AdditionalTextEdits)
if err != nil {
log.Error(ctx, "failed to convert to protocol edits", err)
continue
}
item := protocol.CompletionItem{
Label: candidate.Label,
Detail: candidate.Detail,
@ -105,7 +110,8 @@ func (s *Server) toProtocolCompletionItems(ctx context.Context, view source.View
NewText: insertText,
Range: insertionRange,
},
InsertTextFormat: s.insertTextFormat,
InsertTextFormat: s.insertTextFormat,
AdditionalTextEdits: addlEdits,
// This is a hack so that the client sorts completion results in the order
// according to their score. This can be removed upon the resolution of
// https://github.com/Microsoft/language-server-protocol/issues/348.

View File

@ -261,6 +261,10 @@ func (s *Server) processConfig(ctx context.Context, view source.View, config int
if useDeepCompletions, ok := c["useDeepCompletions"].(bool); ok {
s.useDeepCompletions = useDeepCompletions
}
// Check if want unimported package completions.
if wantUnimportedCompletions, ok := c["wantUnimportedCompletions"].(bool); ok {
s.wantUnimportedCompletions = wantUnimportedCompletions
}
return nil
}

View File

@ -100,6 +100,7 @@ func (r *runner) Diagnostics(t *testing.T, data tests.Diagnostics) {
func (r *runner) Completion(t *testing.T, data tests.Completions, snippets tests.CompletionSnippets, items tests.CompletionItems) {
defer func() {
r.server.useDeepCompletions = false
r.server.wantUnimportedCompletions = false
r.server.wantCompletionDocumentation = false
}()
@ -112,6 +113,7 @@ func (r *runner) Completion(t *testing.T, data tests.Completions, snippets tests
}
r.server.useDeepCompletions = strings.Contains(string(src.URI()), "deepcomplete")
r.server.wantUnimportedCompletions = strings.Contains(string(src.URI()), "unimported")
list := r.runCompletion(t, src)
@ -141,6 +143,7 @@ func (r *runner) Completion(t *testing.T, data tests.Completions, snippets tests
for src, want := range snippets {
r.server.useDeepCompletions = strings.Contains(string(src.URI()), "deepcomplete")
r.server.wantUnimportedCompletions = strings.Contains(string(src.URI()), "unimported")
list := r.runCompletion(t, src)

View File

@ -82,6 +82,7 @@ type Server struct {
hoverKind hoverKind
useDeepCompletions bool
wantCompletionDocumentation bool
wantUnimportedCompletions bool
insertTextFormat protocol.InsertTextFormat
configurationSupported bool
dynamicConfigurationSupported bool

View File

@ -12,6 +12,7 @@ import (
"strings"
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/internal/imports"
"golang.org/x/tools/internal/lsp/fuzzy"
"golang.org/x/tools/internal/lsp/snippet"
"golang.org/x/tools/internal/span"
@ -34,6 +35,14 @@ type CompletionItem struct {
Kind CompletionItemKind
// An optional array of additional TextEdits that are applied when
// selecting this completion.
//
// Additional text edits should be used to change text unrelated to the current cursor position
// (for example adding an import statement at the top of the file if the completion item will
// insert an unqualified type).
AdditionalTextEdits []TextEdit
// Depth is how many levels were searched to find this completion.
// For example when completing "foo<>", "fooBar" is depth 0, and
// "fooBar.Baz" is depth 1.
@ -146,6 +155,12 @@ type completer struct {
// ctx is the context associated with this completion request.
ctx context.Context
// filename is the name of the file associated with this completion request.
filename string
// file is the AST of the file associated with this completion request.
file *ast.File
// pos is the position at which the request was triggered.
pos token.Pos
@ -237,7 +252,7 @@ func (c *completer) setSurrounding(ident *ast.Ident) {
// found adds a candidate completion. We will also search through the object's
// members for more candidates.
func (c *completer) found(obj types.Object, score float64) error {
func (c *completer) found(obj types.Object, score float64, imp *imports.ImportInfo) error {
if obj.Pkg() != nil && obj.Pkg() != c.types && !obj.Exported() {
return errors.Errorf("%s is inaccessible from %s", obj.Name(), c.types.Path())
}
@ -262,6 +277,7 @@ func (c *completer) found(obj types.Object, score float64) error {
cand := candidate{
obj: obj,
score: score,
imp: imp,
}
if c.matchingType(&cand) {
@ -301,12 +317,17 @@ type candidate struct {
// expandFuncCall is true if obj should be invoked in the completion.
// For example, expandFuncCall=true yields "foo()", expandFuncCall=false yields "foo".
expandFuncCall bool
// imp is the import that needs to be added to this package in order
// for this candidate to be valid. nil if no import needed.
imp *imports.ImportInfo
}
type CompletionOptions struct {
DeepComplete bool
WantDocumentaton bool
WantFullDocumentation bool
WantUnimported bool
}
// Completion returns a list of possible candidates for completion, given a
@ -351,6 +372,8 @@ func Completion(ctx context.Context, view View, f GoFile, pos token.Pos, opts Co
qf: qualifier(file, pkg.GetTypes(), pkg.GetTypesInfo()),
view: view,
ctx: ctx,
filename: f.URI().Filename(),
file: file,
path: path,
pos: pos,
seen: make(map[types.Object]bool),
@ -469,7 +492,7 @@ func (c *completer) selector(sel *ast.SelectorExpr) error {
func (c *completer) packageMembers(pkg *types.PkgName) {
scope := pkg.Imported().Scope()
for _, name := range scope.Names() {
c.found(scope.Lookup(name), stdScore)
c.found(scope.Lookup(name), stdScore, nil)
}
}
@ -485,12 +508,12 @@ func (c *completer) methodsAndFields(typ types.Type, addressable bool) error {
}
for i := 0; i < mset.Len(); i++ {
c.found(mset.At(i).Obj(), stdScore)
c.found(mset.At(i).Obj(), stdScore, nil)
}
// Add fields of T.
for _, f := range fieldSelections(typ) {
c.found(f, stdScore)
c.found(f, stdScore, nil)
}
return nil
}
@ -550,7 +573,27 @@ func (c *completer) lexical() error {
// If we haven't already added a candidate for an object with this name.
if _, ok := seen[obj.Name()]; !ok {
seen[obj.Name()] = struct{}{}
c.found(obj, score)
c.found(obj, score, nil)
}
}
}
if c.opts.WantUnimported {
// Suggest packages that have not been imported yet.
pkgs, err := CandidateImports(c.ctx, c.view, c.filename)
if err != nil {
return err
}
score := stdScore
// Rank unimported packages significantly lower than other results.
score *= 0.07
for _, pkg := range pkgs {
if _, ok := seen[pkg.IdentName]; !ok {
// Do not add the unimported packages to seen, since we can have
// multiple packages of the same name as completion suggestions, since
// only one will be chosen.
obj := types.NewPkgName(0, nil, pkg.IdentName, types.NewPackage(pkg.StmtInfo.ImportPath, pkg.IdentName))
c.found(obj, score, &pkg.StmtInfo)
}
}
}
@ -585,7 +628,7 @@ func (c *completer) structLiteralFieldName() error {
for i := 0; i < t.NumFields(); i++ {
field := t.Field(i)
if !addedFields[field] {
c.found(field, highScore)
c.found(field, highScore, nil)
}
}

View File

@ -35,6 +35,7 @@ func (c *completer) item(cand candidate) (CompletionItem, error) {
kind CompletionItemKind
plainSnippet *snippet.Builder
placeholderSnippet *snippet.Builder
addlEdits []TextEdit
)
// expandFuncCall mutates the completion label, detail, and snippets
@ -85,16 +86,27 @@ func (c *completer) item(cand candidate) (CompletionItem, error) {
detail = fmt.Sprintf("%q", obj.Imported().Path())
}
// If this candidate needs an additional import statement,
// add the additional text edits needed.
if cand.imp != nil {
edit, err := AddNamedImport(c.view.Session().Cache().FileSet(), c.file, cand.imp.Name, cand.imp.ImportPath)
if err != nil {
return CompletionItem{}, err
}
addlEdits = append(addlEdits, edit...)
}
detail = strings.TrimPrefix(detail, "untyped ")
item := CompletionItem{
Label: label,
InsertText: insert,
Detail: detail,
Kind: kind,
Score: cand.score,
Depth: len(c.deepState.chain),
plainSnippet: plainSnippet,
placeholderSnippet: placeholderSnippet,
Label: label,
InsertText: insert,
AdditionalTextEdits: addlEdits,
Detail: detail,
Kind: kind,
Score: cand.score,
Depth: len(c.deepState.chain),
plainSnippet: plainSnippet,
placeholderSnippet: placeholderSnippet,
}
// TODO(rstambler): Log errors when this feature is enabled.
if c.opts.WantDocumentaton {

View File

@ -175,6 +175,35 @@ func AllImportsFixes(ctx context.Context, view View, f GoFile, rng span.Range) (
return edits, editsPerFix, nil
}
// AllImportsFixes formats f for each possible fix to the imports.
// In addition to returning the result of applying all edits,
// it returns a list of fixes that could be applied to the file, with the
// corresponding TextEdits that would be needed to apply that fix.
func CandidateImports(ctx context.Context, view View, filename string) (pkgs []imports.ImportFix, err error) {
ctx, done := trace.StartSpan(ctx, "source.CandidateImports")
defer done()
options := &imports.Options{
// Defaults.
AllErrors: true,
Comments: true,
Fragment: true,
FormatOnly: false,
TabIndent: true,
TabWidth: 8,
}
importFn := func(opts *imports.Options) error {
pkgs, err = imports.GetAllCandidates(filename, opts)
return err
}
err = view.RunProcessEnvFunc(ctx, importFn, options)
if err != nil {
return nil, err
}
return pkgs, nil
}
// hasParseErrors returns true if the given file has parse errors.
func hasParseErrors(pkg Package, uri span.URI) bool {
for _, err := range pkg.GetErrors() {

View File

@ -0,0 +1,133 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package source
import (
"bytes"
"go/ast"
"go/format"
"go/token"
"strconv"
"golang.org/x/tools/internal/span"
)
// Taken and then modified from golang.org/x/tools/go/ast/astutil.
//
// We currently choose to create our own version of AddNamedImport for the following reasons:
// 1. We do not want to edit the current ast. This is so that we can use the same ast
// to get the changes from multiple distinct modifications.
// 2. We need the changes that *only* affect the import declarations, because the edits
// are not allowed to overlap with the position in the source that is being edited.
// astutil.AddNamedImport makes it hard to determine what changes need to be made
// to the source document from the ast, as astutil.AddNamedImport includes a merging pass.
// AddNamedImport adds the import with the given name and path to the file f, if absent.
// If name is not empty, it is used to rename the import.
//
// For example, calling
// AddNamedImport(fset, f, "pathpkg", "path")
// adds
// import pathpkg "path"
//
// AddNamedImport only returns edits that affect the import declarations.
func AddNamedImport(fset *token.FileSet, f *ast.File, name, path string) (edits []TextEdit, err error) {
if alreadyImports(f, name, path) {
return nil, nil
}
newImport := &ast.ImportSpec{
Path: &ast.BasicLit{
Kind: token.STRING,
Value: strconv.Quote(path),
},
}
if name != "" {
newImport.Name = &ast.Ident{Name: name}
}
// TODO(suzmue): insert the import statement in the location that would be chosen
// by astutil.AddNamedImport
// Find the last import decl.
var lastImport = -1 // index in f.Decls of the file's final import decl
for i, decl := range f.Decls {
gen, ok := decl.(*ast.GenDecl)
if ok && gen.Tok == token.IMPORT {
lastImport = i
}
}
// Add an import decl after the last import.
impDecl := &ast.GenDecl{
Tok: token.IMPORT,
}
impDecl.Specs = append(impDecl.Specs, newImport)
var insertPos token.Pos
if lastImport >= 0 {
insertPos = f.Decls[lastImport].End()
} else {
// There are no existing imports.
// Our new import, preceded by a blank line, goes after the package declaration
// and after the comment, if any, that starts on the same line as the
// package declaration.
insertPos = f.Name.End()
file := fset.File(f.Package)
pkgLine := file.Line(f.Package)
for _, c := range f.Comments {
if file.Line(c.Pos()) > pkgLine {
break
}
insertPos = c.End()
}
}
// Print this import declaration.
var buf bytes.Buffer
format.Node(&buf, fset, impDecl)
newText := "\n\n" + buf.String() + "\n"
rng := span.NewRange(fset, insertPos, insertPos)
spn, err := rng.Span()
if err != nil {
return nil, err
}
edits = append(edits, TextEdit{
Span: spn,
NewText: newText,
})
return edits, nil
}
// alreadyImports reports whether f has an import with the specified name and path.
func alreadyImports(f *ast.File, name, path string) bool {
for _, s := range f.Imports {
if importName(s) == name && importPath(s) == path {
return true
}
}
return false
}
// importName returns the name of s,
// or "" if the import is not named.
func importName(s *ast.ImportSpec) string {
if s.Name == nil {
return ""
}
return s.Name.Name
}
// importPath returns the unquoted import path of s,
// or "" if the path is not properly quoted.
func importPath(s *ast.ImportSpec) string {
t, err := strconv.Unquote(s.Path.Value)
if err != nil {
return ""
}
return t
}

View File

@ -0,0 +1,306 @@
package source
import (
"bytes"
"fmt"
"go/ast"
"go/format"
"go/parser"
"go/token"
"log"
"testing"
)
var fset = token.NewFileSet()
func parse(t *testing.T, name, in string) *ast.File {
file, err := parser.ParseFile(fset, name, in, parser.ParseComments)
if err != nil {
t.Fatalf("%s parse: %v", name, err)
}
return file
}
func print(t *testing.T, name string, f *ast.File) string {
var buf bytes.Buffer
if err := format.Node(&buf, fset, f); err != nil {
t.Fatalf("%s gofmt: %v", name, err)
}
return buf.String()
}
type test struct {
name string
renamedPkg string
pkg string
in string
want []importInfo
unchanged bool // Expect added/deleted return value to be false.
}
type importInfo struct {
name string
path string
}
var addTests = []test{
{
name: "leave os alone",
pkg: "os",
in: `package main
import (
"os"
)
`,
want: []importInfo{
importInfo{
name: "",
path: "os",
},
},
unchanged: true,
},
{
name: "package statement only",
pkg: "os",
in: `package main
`,
want: []importInfo{
importInfo{
name: "",
path: "os",
},
},
},
{
name: "package statement no new line",
pkg: "os",
in: `package main`,
want: []importInfo{
importInfo{
name: "",
path: "os",
},
},
},
{
name: "package statement comments",
pkg: "os",
in: `// This is a comment
package main // This too`,
want: []importInfo{
importInfo{
name: "",
path: "os",
},
},
},
{
name: "package statement multiline comments",
pkg: "os",
in: `package main /* This is a multiline comment
and it extends
further down*/`,
want: []importInfo{
importInfo{
name: "",
path: "os",
},
},
},
{
name: "import c",
pkg: "os",
in: `package main
import "C"
`,
want: []importInfo{
importInfo{
name: "",
path: "os",
},
importInfo{
name: "",
path: "C",
},
},
},
{
name: "existing imports",
pkg: "os",
in: `package main
import "io"
`,
want: []importInfo{
importInfo{
name: "",
path: "os",
},
importInfo{
name: "",
path: "io",
},
},
},
{
name: "existing imports with comment",
pkg: "os",
in: `package main
import "io" // A comment
`,
want: []importInfo{
importInfo{
name: "",
path: "os",
},
importInfo{
name: "",
path: "io",
},
},
},
{
name: "existing imports multiline comment",
pkg: "os",
in: `package main
import "io" /* A comment
that
extends */
`,
want: []importInfo{
importInfo{
name: "",
path: "os",
},
importInfo{
name: "",
path: "io",
},
},
},
{
name: "renamed import",
renamedPkg: "o",
pkg: "os",
in: `package main
`,
want: []importInfo{
importInfo{
name: "o",
path: "os",
},
},
},
}
func TestAddImport(t *testing.T) {
for _, test := range addTests {
file := parse(t, test.name, test.in)
var before bytes.Buffer
ast.Fprint(&before, fset, file, nil)
edits, err := AddNamedImport(fset, file, test.renamedPkg, test.pkg)
if err != nil && !test.unchanged {
t.Errorf("error adding import: %s", err)
continue
}
// Apply the edits and parse the file.
got := applyEdits(test.in, edits)
gotFile := parse(t, test.name, got)
compareImports(t, fmt.Sprintf("first run: %s:\n", test.name), gotFile.Imports, test.want)
// AddNamedImport should be idempotent. Verify that by calling it again,
// expecting no change to the AST, and the returned added value to always be false.
edits, err = AddNamedImport(fset, gotFile, test.renamedPkg, test.pkg)
if err != nil && !test.unchanged {
t.Errorf("error adding import: %s", err)
continue
}
// Apply the edits and parse the file.
got = applyEdits(got, edits)
gotFile = parse(t, test.name, got)
compareImports(t, test.name, gotFile.Imports, test.want)
}
}
func TestDoubleAddNamedImport(t *testing.T) {
name := "doublenamedimport"
in := "package main\n"
file := parse(t, name, in)
// Add a named import
edits, err := AddNamedImport(fset, file, "o", "os")
if err != nil {
t.Errorf("error adding import: %s", err)
return
}
got := applyEdits(in, edits)
log.Println(got)
gotFile := parse(t, name, got)
// Add a second named import
edits, err = AddNamedImport(fset, gotFile, "i", "io")
if err != nil {
t.Errorf("error adding import: %s", err)
return
}
got = applyEdits(got, edits)
gotFile = parse(t, name, got)
want := []importInfo{
importInfo{
name: "o",
path: "os",
},
importInfo{
name: "i",
path: "io",
},
}
compareImports(t, "", gotFile.Imports, want)
}
func compareImports(t *testing.T, prefix string, got []*ast.ImportSpec, want []importInfo) {
if len(got) != len(want) {
t.Errorf("%s\ngot %d imports\nwant %d", prefix, len(got), len(want))
return
}
for _, imp := range got {
name := importName(imp)
path := importPath(imp)
found := false
for _, want := range want {
if want.name == name && want.path == path {
found = true
break
}
}
if !found {
t.Errorf("%s\n\ngot unexpected import: name: %q,path: %q", prefix, name, path)
continue
}
}
}
func applyEdits(contents string, edits []TextEdit) string {
res := contents
// Apply the edits from the end of the file forward
// to preserve the offsets
for i := len(edits) - 1; i >= 0; i-- {
edit := edits[i]
start := edit.Span.Start().Offset()
end := edit.Span.End().Offset()
tmp := res[0:start] + edit.NewText
res = tmp + res[end:]
}
return res
}

View File

@ -93,9 +93,11 @@ func (r *runner) Completion(t *testing.T, data tests.Completions, snippets tests
}
pos := tok.Pos(src.Start().Offset())
deepComplete := strings.Contains(string(src.URI()), "deepcomplete")
unimported := strings.Contains(string(src.URI()), "unimported")
list, surrounding, err := source.Completion(ctx, r.view, f.(source.GoFile), pos, source.CompletionOptions{
DeepComplete: deepComplete,
WantDocumentaton: true,
WantUnimported: unimported,
})
if err != nil {
t.Fatalf("failed for %v: %v", src, err)

View File

@ -0,0 +1,115 @@
// +build ignore
// mkunimported generates the unimported.go file, containing the Go standard
// library packages.
// The completion items from the std library are computed in the same way as in the
// golang.org/x/tools/internal/imports.
package main
import (
"bufio"
"bytes"
"fmt"
"go/format"
"io"
"io/ioutil"
"log"
"os"
pkgpath "path"
"path/filepath"
"regexp"
"runtime"
"sort"
"strings"
)
func mustOpen(name string) io.Reader {
f, err := os.Open(name)
if err != nil {
log.Fatal(err)
}
return f
}
func api(base string) string {
return filepath.Join(runtime.GOROOT(), "api", base)
}
var sym = regexp.MustCompile(`^pkg (\S+).*?, (?:var|func|type|const) ([A-Z]\w*)`)
var unsafeSyms = map[string]bool{"Alignof": true, "ArbitraryType": true, "Offsetof": true, "Pointer": true, "Sizeof": true}
func main() {
var buf bytes.Buffer
outf := func(format string, args ...interface{}) {
fmt.Fprintf(&buf, format, args...)
}
outf("// Code generated by mkstdlib.go. DO NOT EDIT.\n\n")
outf("package unimported\n")
outf("func _() {\n")
f := io.MultiReader(
mustOpen(api("go1.txt")),
mustOpen(api("go1.1.txt")),
mustOpen(api("go1.2.txt")),
mustOpen(api("go1.3.txt")),
mustOpen(api("go1.4.txt")),
mustOpen(api("go1.5.txt")),
mustOpen(api("go1.6.txt")),
mustOpen(api("go1.7.txt")),
mustOpen(api("go1.8.txt")),
mustOpen(api("go1.9.txt")),
mustOpen(api("go1.10.txt")),
mustOpen(api("go1.11.txt")),
mustOpen(api("go1.12.txt")),
)
sc := bufio.NewScanner(f)
pkgs := map[string]bool{
"unsafe": true,
"syscall/js": true,
}
paths := []string{"unsafe", "syscall/js"}
for sc.Scan() {
l := sc.Text()
has := func(v string) bool { return strings.Contains(l, v) }
if has("struct, ") || has("interface, ") || has(", method (") {
continue
}
if m := sym.FindStringSubmatch(l); m != nil {
path, _ := m[1], m[2]
if _, ok := pkgs[path]; !ok {
pkgs[path] = true
paths = append(paths, path)
}
}
}
if err := sc.Err(); err != nil {
log.Fatal(err)
}
sort.Strings(paths)
var markers []string
for _, path := range paths {
marker := strings.ReplaceAll(path, "/", "slash")
markers = append(markers, marker)
}
outf(" //@complete(\"\", %s)\n", strings.Join(markers, ", "))
outf("}\n")
outf("// Create markers for unimported std lib packages. Only for use by this test.\n")
for i, path := range paths {
name := pkgpath.Base(path)
marker := markers[i]
outf("/* %s *///@item(%s, \"%s\", \"\\\"%s\\\"\", \"package\")\n", name, marker, name, path)
}
fmtbuf, err := format.Source(buf.Bytes())
if err != nil {
log.Fatal(err)
}
err = ioutil.WriteFile("unimported.go", fmtbuf, 0666)
if err != nil {
log.Fatal(err)
}
}

View File

@ -0,0 +1,151 @@
// Code generated by mkstdlib.go. DO NOT EDIT.
package unimported
func _() {
//@complete("", archiveslashtar, archiveslashzip, bufio, bytes, compressslashbzip2, compressslashflate, compressslashgzip, compressslashlzw, compressslashzlib, containerslashheap, containerslashlist, containerslashring, context, crypto, cryptoslashaes, cryptoslashcipher, cryptoslashdes, cryptoslashdsa, cryptoslashecdsa, cryptoslashelliptic, cryptoslashhmac, cryptoslashmd5, cryptoslashrand, cryptoslashrc4, cryptoslashrsa, cryptoslashsha1, cryptoslashsha256, cryptoslashsha512, cryptoslashsubtle, cryptoslashtls, cryptoslashx509, cryptoslashx509slashpkix, databaseslashsql, databaseslashsqlslashdriver, debugslashdwarf, debugslashelf, debugslashgosym, debugslashmacho, debugslashpe, debugslashplan9obj, encoding, encodingslashascii85, encodingslashasn1, encodingslashbase32, encodingslashbase64, encodingslashbinary, encodingslashcsv, encodingslashgob, encodingslashhex, encodingslashjson, encodingslashpem, encodingslashxml, errors, expvar, flag, fmt, goslashast, goslashbuild, goslashconstant, goslashdoc, goslashformat, goslashimporter, goslashparser, goslashprinter, goslashscanner, goslashtoken, goslashtypes, hash, hashslashadler32, hashslashcrc32, hashslashcrc64, hashslashfnv, html, htmlslashtemplate, image, imageslashcolor, imageslashcolorslashpalette, imageslashdraw, imageslashgif, imageslashjpeg, imageslashpng, indexslashsuffixarray, io, ioslashioutil, log, logslashsyslog, math, mathslashbig, mathslashbits, mathslashcmplx, mathslashrand, mime, mimeslashmultipart, mimeslashquotedprintable, net, netslashhttp, netslashhttpslashcgi, netslashhttpslashcookiejar, netslashhttpslashfcgi, netslashhttpslashhttptest, netslashhttpslashhttptrace, netslashhttpslashhttputil, netslashhttpslashpprof, netslashmail, netslashrpc, netslashrpcslashjsonrpc, netslashsmtp, netslashtextproto, netslashurl, os, osslashexec, osslashsignal, osslashuser, path, pathslashfilepath, plugin, reflect, regexp, regexpslashsyntax, runtime, runtimeslashdebug, runtimeslashpprof, runtimeslashtrace, sort, strconv, strings, sync, syncslashatomic, syscall, syscallslashjs, testing, testingslashiotest, testingslashquick, textslashscanner, textslashtabwriter, textslashtemplate, textslashtemplateslashparse, time, unicode, unicodeslashutf16, unicodeslashutf8, unsafe)
}
// Create markers for unimported std lib packages. Only for use by this test.
/* tar */ //@item(archiveslashtar, "tar", "\"archive/tar\"", "package")
/* zip */ //@item(archiveslashzip, "zip", "\"archive/zip\"", "package")
/* bufio */ //@item(bufio, "bufio", "\"bufio\"", "package")
/* bytes */ //@item(bytes, "bytes", "\"bytes\"", "package")
/* bzip2 */ //@item(compressslashbzip2, "bzip2", "\"compress/bzip2\"", "package")
/* flate */ //@item(compressslashflate, "flate", "\"compress/flate\"", "package")
/* gzip */ //@item(compressslashgzip, "gzip", "\"compress/gzip\"", "package")
/* lzw */ //@item(compressslashlzw, "lzw", "\"compress/lzw\"", "package")
/* zlib */ //@item(compressslashzlib, "zlib", "\"compress/zlib\"", "package")
/* heap */ //@item(containerslashheap, "heap", "\"container/heap\"", "package")
/* list */ //@item(containerslashlist, "list", "\"container/list\"", "package")
/* ring */ //@item(containerslashring, "ring", "\"container/ring\"", "package")
/* context */ //@item(context, "context", "\"context\"", "package")
/* crypto */ //@item(crypto, "crypto", "\"crypto\"", "package")
/* aes */ //@item(cryptoslashaes, "aes", "\"crypto/aes\"", "package")
/* cipher */ //@item(cryptoslashcipher, "cipher", "\"crypto/cipher\"", "package")
/* des */ //@item(cryptoslashdes, "des", "\"crypto/des\"", "package")
/* dsa */ //@item(cryptoslashdsa, "dsa", "\"crypto/dsa\"", "package")
/* ecdsa */ //@item(cryptoslashecdsa, "ecdsa", "\"crypto/ecdsa\"", "package")
/* elliptic */ //@item(cryptoslashelliptic, "elliptic", "\"crypto/elliptic\"", "package")
/* hmac */ //@item(cryptoslashhmac, "hmac", "\"crypto/hmac\"", "package")
/* md5 */ //@item(cryptoslashmd5, "md5", "\"crypto/md5\"", "package")
/* rand */ //@item(cryptoslashrand, "rand", "\"crypto/rand\"", "package")
/* rc4 */ //@item(cryptoslashrc4, "rc4", "\"crypto/rc4\"", "package")
/* rsa */ //@item(cryptoslashrsa, "rsa", "\"crypto/rsa\"", "package")
/* sha1 */ //@item(cryptoslashsha1, "sha1", "\"crypto/sha1\"", "package")
/* sha256 */ //@item(cryptoslashsha256, "sha256", "\"crypto/sha256\"", "package")
/* sha512 */ //@item(cryptoslashsha512, "sha512", "\"crypto/sha512\"", "package")
/* subtle */ //@item(cryptoslashsubtle, "subtle", "\"crypto/subtle\"", "package")
/* tls */ //@item(cryptoslashtls, "tls", "\"crypto/tls\"", "package")
/* x509 */ //@item(cryptoslashx509, "x509", "\"crypto/x509\"", "package")
/* pkix */ //@item(cryptoslashx509slashpkix, "pkix", "\"crypto/x509/pkix\"", "package")
/* sql */ //@item(databaseslashsql, "sql", "\"database/sql\"", "package")
/* driver */ //@item(databaseslashsqlslashdriver, "driver", "\"database/sql/driver\"", "package")
/* dwarf */ //@item(debugslashdwarf, "dwarf", "\"debug/dwarf\"", "package")
/* elf */ //@item(debugslashelf, "elf", "\"debug/elf\"", "package")
/* gosym */ //@item(debugslashgosym, "gosym", "\"debug/gosym\"", "package")
/* macho */ //@item(debugslashmacho, "macho", "\"debug/macho\"", "package")
/* pe */ //@item(debugslashpe, "pe", "\"debug/pe\"", "package")
/* plan9obj */ //@item(debugslashplan9obj, "plan9obj", "\"debug/plan9obj\"", "package")
/* encoding */ //@item(encoding, "encoding", "\"encoding\"", "package")
/* ascii85 */ //@item(encodingslashascii85, "ascii85", "\"encoding/ascii85\"", "package")
/* asn1 */ //@item(encodingslashasn1, "asn1", "\"encoding/asn1\"", "package")
/* base32 */ //@item(encodingslashbase32, "base32", "\"encoding/base32\"", "package")
/* base64 */ //@item(encodingslashbase64, "base64", "\"encoding/base64\"", "package")
/* binary */ //@item(encodingslashbinary, "binary", "\"encoding/binary\"", "package")
/* csv */ //@item(encodingslashcsv, "csv", "\"encoding/csv\"", "package")
/* gob */ //@item(encodingslashgob, "gob", "\"encoding/gob\"", "package")
/* hex */ //@item(encodingslashhex, "hex", "\"encoding/hex\"", "package")
/* json */ //@item(encodingslashjson, "json", "\"encoding/json\"", "package")
/* pem */ //@item(encodingslashpem, "pem", "\"encoding/pem\"", "package")
/* xml */ //@item(encodingslashxml, "xml", "\"encoding/xml\"", "package")
/* errors */ //@item(errors, "errors", "\"errors\"", "package")
/* expvar */ //@item(expvar, "expvar", "\"expvar\"", "package")
/* flag */ //@item(flag, "flag", "\"flag\"", "package")
/* fmt */ //@item(fmt, "fmt", "\"fmt\"", "package")
/* ast */ //@item(goslashast, "ast", "\"go/ast\"", "package")
/* build */ //@item(goslashbuild, "build", "\"go/build\"", "package")
/* constant */ //@item(goslashconstant, "constant", "\"go/constant\"", "package")
/* doc */ //@item(goslashdoc, "doc", "\"go/doc\"", "package")
/* format */ //@item(goslashformat, "format", "\"go/format\"", "package")
/* importer */ //@item(goslashimporter, "importer", "\"go/importer\"", "package")
/* parser */ //@item(goslashparser, "parser", "\"go/parser\"", "package")
/* printer */ //@item(goslashprinter, "printer", "\"go/printer\"", "package")
/* scanner */ //@item(goslashscanner, "scanner", "\"go/scanner\"", "package")
/* token */ //@item(goslashtoken, "token", "\"go/token\"", "package")
/* types */ //@item(goslashtypes, "types", "\"go/types\"", "package")
/* hash */ //@item(hash, "hash", "\"hash\"", "package")
/* adler32 */ //@item(hashslashadler32, "adler32", "\"hash/adler32\"", "package")
/* crc32 */ //@item(hashslashcrc32, "crc32", "\"hash/crc32\"", "package")
/* crc64 */ //@item(hashslashcrc64, "crc64", "\"hash/crc64\"", "package")
/* fnv */ //@item(hashslashfnv, "fnv", "\"hash/fnv\"", "package")
/* html */ //@item(html, "html", "\"html\"", "package")
/* template */ //@item(htmlslashtemplate, "template", "\"html/template\"", "package")
/* image */ //@item(image, "image", "\"image\"", "package")
/* color */ //@item(imageslashcolor, "color", "\"image/color\"", "package")
/* palette */ //@item(imageslashcolorslashpalette, "palette", "\"image/color/palette\"", "package")
/* draw */ //@item(imageslashdraw, "draw", "\"image/draw\"", "package")
/* gif */ //@item(imageslashgif, "gif", "\"image/gif\"", "package")
/* jpeg */ //@item(imageslashjpeg, "jpeg", "\"image/jpeg\"", "package")
/* png */ //@item(imageslashpng, "png", "\"image/png\"", "package")
/* suffixarray */ //@item(indexslashsuffixarray, "suffixarray", "\"index/suffixarray\"", "package")
/* io */ //@item(io, "io", "\"io\"", "package")
/* ioutil */ //@item(ioslashioutil, "ioutil", "\"io/ioutil\"", "package")
/* log */ //@item(log, "log", "\"log\"", "package")
/* syslog */ //@item(logslashsyslog, "syslog", "\"log/syslog\"", "package")
/* math */ //@item(math, "math", "\"math\"", "package")
/* big */ //@item(mathslashbig, "big", "\"math/big\"", "package")
/* bits */ //@item(mathslashbits, "bits", "\"math/bits\"", "package")
/* cmplx */ //@item(mathslashcmplx, "cmplx", "\"math/cmplx\"", "package")
/* rand */ //@item(mathslashrand, "rand", "\"math/rand\"", "package")
/* mime */ //@item(mime, "mime", "\"mime\"", "package")
/* multipart */ //@item(mimeslashmultipart, "multipart", "\"mime/multipart\"", "package")
/* quotedprintable */ //@item(mimeslashquotedprintable, "quotedprintable", "\"mime/quotedprintable\"", "package")
/* net */ //@item(net, "net", "\"net\"", "package")
/* http */ //@item(netslashhttp, "http", "\"net/http\"", "package")
/* cgi */ //@item(netslashhttpslashcgi, "cgi", "\"net/http/cgi\"", "package")
/* cookiejar */ //@item(netslashhttpslashcookiejar, "cookiejar", "\"net/http/cookiejar\"", "package")
/* fcgi */ //@item(netslashhttpslashfcgi, "fcgi", "\"net/http/fcgi\"", "package")
/* httptest */ //@item(netslashhttpslashhttptest, "httptest", "\"net/http/httptest\"", "package")
/* httptrace */ //@item(netslashhttpslashhttptrace, "httptrace", "\"net/http/httptrace\"", "package")
/* httputil */ //@item(netslashhttpslashhttputil, "httputil", "\"net/http/httputil\"", "package")
/* pprof */ //@item(netslashhttpslashpprof, "pprof", "\"net/http/pprof\"", "package")
/* mail */ //@item(netslashmail, "mail", "\"net/mail\"", "package")
/* rpc */ //@item(netslashrpc, "rpc", "\"net/rpc\"", "package")
/* jsonrpc */ //@item(netslashrpcslashjsonrpc, "jsonrpc", "\"net/rpc/jsonrpc\"", "package")
/* smtp */ //@item(netslashsmtp, "smtp", "\"net/smtp\"", "package")
/* textproto */ //@item(netslashtextproto, "textproto", "\"net/textproto\"", "package")
/* url */ //@item(netslashurl, "url", "\"net/url\"", "package")
/* os */ //@item(os, "os", "\"os\"", "package")
/* exec */ //@item(osslashexec, "exec", "\"os/exec\"", "package")
/* signal */ //@item(osslashsignal, "signal", "\"os/signal\"", "package")
/* user */ //@item(osslashuser, "user", "\"os/user\"", "package")
/* path */ //@item(path, "path", "\"path\"", "package")
/* filepath */ //@item(pathslashfilepath, "filepath", "\"path/filepath\"", "package")
/* plugin */ //@item(plugin, "plugin", "\"plugin\"", "package")
/* reflect */ //@item(reflect, "reflect", "\"reflect\"", "package")
/* regexp */ //@item(regexp, "regexp", "\"regexp\"", "package")
/* syntax */ //@item(regexpslashsyntax, "syntax", "\"regexp/syntax\"", "package")
/* runtime */ //@item(runtime, "runtime", "\"runtime\"", "package")
/* debug */ //@item(runtimeslashdebug, "debug", "\"runtime/debug\"", "package")
/* pprof */ //@item(runtimeslashpprof, "pprof", "\"runtime/pprof\"", "package")
/* trace */ //@item(runtimeslashtrace, "trace", "\"runtime/trace\"", "package")
/* sort */ //@item(sort, "sort", "\"sort\"", "package")
/* strconv */ //@item(strconv, "strconv", "\"strconv\"", "package")
/* strings */ //@item(strings, "strings", "\"strings\"", "package")
/* sync */ //@item(sync, "sync", "\"sync\"", "package")
/* atomic */ //@item(syncslashatomic, "atomic", "\"sync/atomic\"", "package")
/* syscall */ //@item(syscall, "syscall", "\"syscall\"", "package")
/* js */ //@item(syscallslashjs, "js", "\"syscall/js\"", "package")
/* testing */ //@item(testing, "testing", "\"testing\"", "package")
/* iotest */ //@item(testingslashiotest, "iotest", "\"testing/iotest\"", "package")
/* quick */ //@item(testingslashquick, "quick", "\"testing/quick\"", "package")
/* scanner */ //@item(textslashscanner, "scanner", "\"text/scanner\"", "package")
/* tabwriter */ //@item(textslashtabwriter, "tabwriter", "\"text/tabwriter\"", "package")
/* template */ //@item(textslashtemplate, "template", "\"text/template\"", "package")
/* parse */ //@item(textslashtemplateslashparse, "parse", "\"text/template/parse\"", "package")
/* time */ //@item(time, "time", "\"time\"", "package")
/* unicode */ //@item(unicode, "unicode", "\"unicode\"", "package")
/* utf16 */ //@item(unicodeslashutf16, "utf16", "\"unicode/utf16\"", "package")
/* utf8 */ //@item(unicodeslashutf8, "utf8", "\"unicode/utf8\"", "package")
/* unsafe */ //@item(unsafe, "unsafe", "\"unsafe\"", "package")

View File

@ -29,7 +29,7 @@ import (
// We hardcode the expected number of test cases to ensure that all tests
// are being executed. If a test is added, this number must be changed.
const (
ExpectedCompletionsCount = 145
ExpectedCompletionsCount = 146
ExpectedCompletionSnippetCount = 15
ExpectedDiagnosticsCount = 21
ExpectedFormatCount = 6