internal/lsp/source: cap number of unimported completions
Building unimported completions requires re-parsing and formatting at least
some of the file for each one, which adds up. Limit it to 20; I expect
people will just type more rather than scroll through a giant list.
Updates golang/go#36001.
Change-Id: Ib41232b91c327d4b824e6176e30306abf356f5b4
Reviewed-on: https://go-review.googlesource.com/c/tools/+/210198
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/source/completion.go b/internal/lsp/source/completion.go
index 52d0bbe..72d3805 100644
--- a/internal/lsp/source/completion.go
+++ b/internal/lsp/source/completion.go
@@ -547,6 +547,9 @@
return c.expectedType.typeName.wantTypeName
}
+// See https://golang.org/issue/36001. Unimported completions are expensive.
+const maxUnimported = 20
+
// selector finds completions for the specified selector expression.
func (c *completer) selector(sel *ast.SelectorExpr) error {
// Is sel a qualified identifier?
@@ -570,7 +573,11 @@
return err
}
known := c.snapshot.KnownImportPaths()
+ startingItems := len(c.items)
for _, pkgExport := range pkgExports {
+ if len(c.items)-startingItems >= maxUnimported {
+ break
+ }
// If we've seen this import path, use the fully-typed version.
if knownPkg, ok := known[pkgExport.Fix.StmtInfo.ImportPath]; ok {
c.packageMembers(knownPkg.GetTypes(), &importInfo{
@@ -726,7 +733,12 @@
score := stdScore
// Rank unimported packages significantly lower than other results.
score *= 0.07
+
+ startingItems := len(c.items)
for _, pkg := range pkgs {
+ if len(c.items)-startingItems >= maxUnimported {
+ break
+ }
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
diff --git a/internal/lsp/testdata/summary.txt.golden b/internal/lsp/testdata/summary.txt.golden
index 3c952c0..ec01935 100644
--- a/internal/lsp/testdata/summary.txt.golden
+++ b/internal/lsp/testdata/summary.txt.golden
@@ -1,7 +1,7 @@
-- summary --
CompletionsCount = 219
CompletionSnippetCount = 51
-UnimportedCompletionsCount = 3
+UnimportedCompletionsCount = 4
DeepCompletionsCount = 5
FuzzyCompletionsCount = 7
RankedCompletionsCount = 26
diff --git a/internal/lsp/testdata/unimported/unimported.go.in b/internal/lsp/testdata/unimported/unimported.go.in
index 3b78f72..d4ce8b3 100644
--- a/internal/lsp/testdata/unimported/unimported.go.in
+++ b/internal/lsp/testdata/unimported/unimported.go.in
@@ -1,18 +1,18 @@
package unimported
func _() {
- //@unimported("", bytes, context, cryptoslashrand, time, unsafe, externalpackage)
+ //@unimported("", hashslashadler32, goslashast, encodingslashbase64, bytes)
+ pkg //@unimported("g", externalpackage)
// container/ring is extremely unlikely to be imported by anything, so shouldn't have type information.
ring.Ring //@unimported("Ring", ringring)
signature.Foo //@unimported("Foo", signaturefoo)
}
// Create markers for unimported std lib packages. Only for use by this test.
+/* adler32 */ //@item(hashslashadler32, "adler32", "\"hash/adler32\"", "package")
+/* ast */ //@item(goslashast, "ast", "\"go/ast\"", "package")
+/* base64 */ //@item(encodingslashbase64, "base64", "\"encoding/base64\"", "package")
/* bytes */ //@item(bytes, "bytes", "\"bytes\"", "package")
-/* context */ //@item(context, "context", "\"context\"", "package")
-/* rand */ //@item(cryptoslashrand, "rand", "\"crypto/rand\"", "package")
-/* time */ //@item(time, "time", "\"time\"", "package")
-/* unsafe */ //@item(unsafe, "unsafe", "\"unsafe\"", "package")
/* pkg */ //@item(externalpackage, "pkg", "\"example.com/extramodule/pkg\"", "package")
/* ring.Ring */ //@item(ringring, "Ring", "(from \"container/ring\")", "var")