internal/imports: support completing import paths

Add a new autocomplete function that completes based on import path
prefix rather than package name prefix.

Updates golang/go#35877.

Change-Id: Ib768080ee99debfff1c8c870d22dc7b7459deadd
Reviewed-on: https://go-review.googlesource.com/c/tools/+/249419
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Danish Dua <danishdua@google.com>
diff --git a/internal/imports/fix.go b/internal/imports/fix.go
index 0cb09e2..62d9fe8 100644
--- a/internal/imports/fix.go
+++ b/internal/imports/fix.go
@@ -693,8 +693,8 @@
 	return ""
 }
 
-// GetAllCandidates gets all of the packages starting with prefix that can be
-// imported by filename, sorted by import path.
+// GetAllCandidates calls wrapped for each package whose name starts with
+// searchPrefix, and can be imported from filename with the package name filePkg.
 func GetAllCandidates(ctx context.Context, wrapped func(ImportFix), searchPrefix, filename, filePkg string, env *ProcessEnv) error {
 	callback := &scanCallback{
 		rootFound: func(gopathwalk.Root) bool {
@@ -728,6 +728,35 @@
 	return getCandidatePkgs(ctx, callback, filename, filePkg, env)
 }
 
+// GetImportPaths calls wrapped for each package whose import path starts with
+// searchPrefix, and can be imported from filename with the package name filePkg.
+func GetImportPaths(ctx context.Context, wrapped func(ImportFix), searchPrefix, filename, filePkg string, env *ProcessEnv) error {
+	callback := &scanCallback{
+		rootFound: func(gopathwalk.Root) bool {
+			return true
+		},
+		dirFound: func(pkg *pkg) bool {
+			if !canUse(filename, pkg.dir) {
+				return false
+			}
+			return strings.HasPrefix(pkg.importPathShort, searchPrefix)
+		},
+		packageNameLoaded: func(pkg *pkg) bool {
+			wrapped(ImportFix{
+				StmtInfo: ImportInfo{
+					ImportPath: pkg.importPathShort,
+					Name:       candidateImportName(pkg),
+				},
+				IdentName: pkg.packageName,
+				FixType:   AddImport,
+				Relevance: pkg.relevance,
+			})
+			return false
+		},
+	}
+	return getCandidatePkgs(ctx, callback, filename, filePkg, env)
+}
+
 // A PackageExport is a package and its exports.
 type PackageExport struct {
 	Fix     *ImportFix
diff --git a/internal/imports/fix_test.go b/internal/imports/fix_test.go
index e14f1d3..0bcbb88 100644
--- a/internal/imports/fix_test.go
+++ b/internal/imports/fix_test.go
@@ -2560,7 +2560,7 @@
 	}.processTest(t, "foo.com/a", "a_test.go", nil, nil, want)
 }
 
-// TestStdLibGetCandidates tests that get packages finds std library packages
+// TestGetCandidates tests that get packages finds packages
 // with correct priorities.
 func TestGetCandidates(t *testing.T) {
 	type res struct {
@@ -2613,7 +2613,57 @@
 			got[i].relevance = 0
 		}
 		if !reflect.DeepEqual(want, got) {
-			t.Errorf("wanted stdlib results in order %v, got %v", want, got)
+			t.Errorf("wanted results in order %v, got %v", want, got)
+		}
+	})
+}
+
+func TestGetImportPaths(t *testing.T) {
+	type res struct {
+		relevance  int
+		name, path string
+	}
+	want := []res{
+		{0, "http", "net/http"},
+		{0, "net", "net"},
+		{0, "neta", "neta.com/neta"},
+	}
+
+	testConfig{
+		modules: []packagestest.Module{
+			{
+				Name:  "neta.com",
+				Files: fm{"neta/neta.go": "package neta\n"},
+			},
+		},
+	}.test(t, func(t *goimportTest) {
+		var mu sync.Mutex
+		var got []res
+		add := func(c ImportFix) {
+			mu.Lock()
+			defer mu.Unlock()
+			for _, w := range want {
+				if c.StmtInfo.ImportPath == w.path {
+					got = append(got, res{c.Relevance, c.IdentName, c.StmtInfo.ImportPath})
+				}
+			}
+		}
+		if err := GetImportPaths(context.Background(), add, "ne", "x.go", "x", t.env); err != nil {
+			t.Fatalf("GetImportPaths() = %v", err)
+		}
+		// Sort, then clear out relevance so it doesn't mess up the DeepEqual.
+		sort.Slice(got, func(i, j int) bool {
+			ri, rj := got[i], got[j]
+			if ri.relevance != rj.relevance {
+				return ri.relevance > rj.relevance // Highest first.
+			}
+			return ri.name < rj.name
+		})
+		for i := range got {
+			got[i].relevance = 0
+		}
+		if !reflect.DeepEqual(want, got) {
+			t.Errorf("wanted results in order %v, got %v", want, got)
 		}
 	})
 }
@@ -2664,7 +2714,7 @@
 			got[i].relevance = 0
 		}
 		if !reflect.DeepEqual(want, got) {
-			t.Errorf("wanted stdlib results in order %v, got %v", want, got)
+			t.Errorf("wanted results in order %v, got %v", want, got)
 		}
 	})
 }