mirror of https://github.com/golang/go.git
internal/lsp: add ListImports
The VS Code extension uses information about imports to figure out whether a given function is a Testify test. As of writing, it asks: - Does the file import Testify? - Does the package it's in do so? To answer these questions, add ListImports, which tells you about the packages imported by the current file, including their import name, plus the import paths of all imports in the entire package. I suspect the latter may be wrong in the presence of GOPATH vendoring, but that should be a relatively rare situation at this point so I didn't bother testing. Fixes golang/go#40514. Change-Id: I4c69e1db80dce6e594bdb595a81aade1ddec4d29 Reviewed-on: https://go-review.googlesource.com/c/tools/+/383354 Trust: Heschi Kreinick <heschi@google.com> Run-TryBot: Heschi Kreinick <heschi@google.com> Reviewed-by: Robert Findley <rfindley@google.com> gopls-CI: kokoro <noreply+kokoro@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
parent
d55d8929fe
commit
414ec9c3f0
|
|
@ -142,6 +142,37 @@ Args:
|
|||
}
|
||||
```
|
||||
|
||||
### **List imports of a file and its package**
|
||||
Identifier: `gopls.list_imports`
|
||||
|
||||
Retrieve a list of imports in the given Go file, and the package it
|
||||
belongs to.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
// The file URI.
|
||||
"URI": string,
|
||||
}
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```
|
||||
{
|
||||
// Imports is a list of imports in the requested file.
|
||||
"Imports": []{
|
||||
"Path": string,
|
||||
"Name": string,
|
||||
},
|
||||
// PackageImports is a list of all imports in the requested file's package.
|
||||
"PackageImports": []{
|
||||
"Path": string,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### **List known packages**
|
||||
Identifier: `gopls.list_known_packages`
|
||||
|
||||
|
|
|
|||
|
|
@ -1,59 +0,0 @@
|
|||
// 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/internal/lsp/command"
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
. "golang.org/x/tools/internal/lsp/regtest"
|
||||
"golang.org/x/tools/internal/lsp/tests"
|
||||
)
|
||||
|
||||
func TestAddImport(t *testing.T) {
|
||||
const before = `package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
`
|
||||
|
||||
const want = `package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
`
|
||||
|
||||
Run(t, "", func(t *testing.T, env *Env) {
|
||||
env.CreateBuffer("main.go", before)
|
||||
cmd, err := command.NewAddImportCommand("Add Import", command.AddImportArgs{
|
||||
URI: protocol.URIFromSpanURI(env.Sandbox.Workdir.URI("main.go").SpanURI()),
|
||||
ImportPath: "bytes",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = env.Editor.ExecuteCommand(env.Ctx, &protocol.ExecuteCommandParams{
|
||||
Command: "gopls.add_import",
|
||||
Arguments: cmd.Arguments,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got := env.Editor.BufferText("main.go")
|
||||
if got != want {
|
||||
t.Fatalf("gopls.add_import failed\n%s", tests.Diff(t, want, got))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
// 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"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"golang.org/x/tools/internal/lsp/command"
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
. "golang.org/x/tools/internal/lsp/regtest"
|
||||
"golang.org/x/tools/internal/lsp/tests"
|
||||
)
|
||||
|
||||
func TestAddImport(t *testing.T) {
|
||||
const before = `package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
`
|
||||
|
||||
const want = `package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
`
|
||||
|
||||
Run(t, "", func(t *testing.T, env *Env) {
|
||||
env.CreateBuffer("main.go", before)
|
||||
cmd, err := command.NewAddImportCommand("Add Import", command.AddImportArgs{
|
||||
URI: env.Sandbox.Workdir.URI("main.go"),
|
||||
ImportPath: "bytes",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
env.ExecuteCommand(&protocol.ExecuteCommandParams{
|
||||
Command: "gopls.add_import",
|
||||
Arguments: cmd.Arguments,
|
||||
}, nil)
|
||||
got := env.Editor.BufferText("main.go")
|
||||
if got != want {
|
||||
t.Fatalf("gopls.add_import failed\n%s", tests.Diff(t, want, got))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestListImports(t *testing.T) {
|
||||
const files = `
|
||||
-- go.mod --
|
||||
module mod.com
|
||||
|
||||
go 1.12
|
||||
-- foo.go --
|
||||
package foo
|
||||
const C = 1
|
||||
-- import_strings_test.go --
|
||||
package foo
|
||||
import (
|
||||
x "strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFoo(t *testing.T) {}
|
||||
-- import_testing_test.go --
|
||||
package foo
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestFoo2(t *testing.T) {}
|
||||
`
|
||||
tests := []struct {
|
||||
filename string
|
||||
want command.ListImportsResult
|
||||
}{
|
||||
{
|
||||
filename: "import_strings_test.go",
|
||||
want: command.ListImportsResult{
|
||||
Imports: []command.FileImport{
|
||||
{Name: "x", Path: "strings"},
|
||||
{Path: "testing"},
|
||||
},
|
||||
PackageImports: []command.PackageImport{
|
||||
{Path: "strings"},
|
||||
{Path: "testing"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
filename: "import_testing_test.go",
|
||||
want: command.ListImportsResult{
|
||||
Imports: []command.FileImport{
|
||||
{Path: "testing"},
|
||||
},
|
||||
PackageImports: []command.PackageImport{
|
||||
{Path: "strings"},
|
||||
{Path: "testing"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Run(t, files, func(t *testing.T, env *Env) {
|
||||
for _, tt := range tests {
|
||||
cmd, err := command.NewListImportsCommand("List Imports", command.URIArg{
|
||||
URI: env.Sandbox.Workdir.URI(tt.filename),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var result command.ListImportsResult
|
||||
env.ExecuteCommand(&protocol.ExecuteCommandParams{
|
||||
Command: command.ListImports.ID(),
|
||||
Arguments: cmd.Arguments,
|
||||
}, &result)
|
||||
if diff := cmp.Diff(tt.want, result); diff != "" {
|
||||
t.Errorf("unexpected list imports result for %q (-want +got):\n%s", tt.filename, diff)
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
|
@ -13,9 +13,11 @@ import (
|
|||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
"golang.org/x/tools/internal/event"
|
||||
"golang.org/x/tools/internal/gocommand"
|
||||
"golang.org/x/tools/internal/lsp/command"
|
||||
|
|
@ -681,6 +683,48 @@ func (c *commandHandler) ListKnownPackages(ctx context.Context, args command.URI
|
|||
})
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (c *commandHandler) ListImports(ctx context.Context, args command.URIArg) (command.ListImportsResult, error) {
|
||||
var result command.ListImportsResult
|
||||
err := c.run(ctx, commandConfig{
|
||||
forURI: args.URI,
|
||||
}, func(ctx context.Context, deps commandDeps) error {
|
||||
pkg, err := deps.snapshot.PackageForFile(ctx, args.URI.SpanURI(), source.TypecheckWorkspace, source.NarrowestPackage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pgf, err := pkg.File(args.URI.SpanURI())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, group := range astutil.Imports(deps.snapshot.FileSet(), pgf.File) {
|
||||
for _, imp := range group {
|
||||
if imp.Path == nil {
|
||||
continue
|
||||
}
|
||||
var name string
|
||||
if imp.Name != nil {
|
||||
name = imp.Name.Name
|
||||
}
|
||||
result.Imports = append(result.Imports, command.FileImport{
|
||||
Path: source.ImportPath(imp),
|
||||
Name: name,
|
||||
})
|
||||
}
|
||||
}
|
||||
for _, imp := range pkg.Imports() {
|
||||
result.PackageImports = append(result.PackageImports, command.PackageImport{
|
||||
Path: imp.PkgPath(), // This might be the vendored path under GOPATH vendoring, in which case it's a bug.
|
||||
})
|
||||
}
|
||||
sort.Slice(result.PackageImports, func(i, j int) bool {
|
||||
return result.PackageImports[i].Path < result.PackageImports[j].Path
|
||||
})
|
||||
return nil
|
||||
})
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (c *commandHandler) AddImport(ctx context.Context, args command.AddImportArgs) error {
|
||||
return c.run(ctx, commandConfig{
|
||||
progress: "Adding import",
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ const (
|
|||
Generate Command = "generate"
|
||||
GenerateGoplsMod Command = "generate_gopls_mod"
|
||||
GoGetPackage Command = "go_get_package"
|
||||
ListImports Command = "list_imports"
|
||||
ListKnownPackages Command = "list_known_packages"
|
||||
RegenerateCgo Command = "regenerate_cgo"
|
||||
RemoveDependency Command = "remove_dependency"
|
||||
|
|
@ -49,6 +50,7 @@ var Commands = []Command{
|
|||
Generate,
|
||||
GenerateGoplsMod,
|
||||
GoGetPackage,
|
||||
ListImports,
|
||||
ListKnownPackages,
|
||||
RegenerateCgo,
|
||||
RemoveDependency,
|
||||
|
|
@ -112,6 +114,12 @@ func Dispatch(ctx context.Context, params *protocol.ExecuteCommandParams, s Inte
|
|||
return nil, err
|
||||
}
|
||||
return nil, s.GoGetPackage(ctx, a0)
|
||||
case "gopls.list_imports":
|
||||
var a0 URIArg
|
||||
if err := UnmarshalArgs(params.Arguments, &a0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.ListImports(ctx, a0)
|
||||
case "gopls.list_known_packages":
|
||||
var a0 URIArg
|
||||
if err := UnmarshalArgs(params.Arguments, &a0); err != nil {
|
||||
|
|
@ -280,6 +288,18 @@ func NewGoGetPackageCommand(title string, a0 GoGetPackageArgs) (protocol.Command
|
|||
}, nil
|
||||
}
|
||||
|
||||
func NewListImportsCommand(title string, a0 URIArg) (protocol.Command, error) {
|
||||
args, err := MarshalArgs(a0)
|
||||
if err != nil {
|
||||
return protocol.Command{}, err
|
||||
}
|
||||
return protocol.Command{
|
||||
Title: title,
|
||||
Command: "gopls.list_imports",
|
||||
Arguments: args,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewListKnownPackagesCommand(title string, a0 URIArg) (protocol.Command, error) {
|
||||
args, err := MarshalArgs(a0)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -120,6 +120,12 @@ type Interface interface {
|
|||
// Retrieve a list of packages that are importable from the given URI.
|
||||
ListKnownPackages(context.Context, URIArg) (ListKnownPackagesResult, error)
|
||||
|
||||
// ListImports: List imports of a file and its package
|
||||
//
|
||||
// Retrieve a list of imports in the given Go file, and the package it
|
||||
// belongs to.
|
||||
ListImports(context.Context, URIArg) (ListImportsResult, error)
|
||||
|
||||
// AddImport: Add an import
|
||||
//
|
||||
// Ask the server to add an import path to a given Go file. The method will
|
||||
|
|
@ -224,6 +230,26 @@ type ListKnownPackagesResult struct {
|
|||
Packages []string
|
||||
}
|
||||
|
||||
type ListImportsResult struct {
|
||||
// Imports is a list of imports in the requested file.
|
||||
Imports []FileImport
|
||||
|
||||
// PackageImports is a list of all imports in the requested file's package.
|
||||
PackageImports []PackageImport
|
||||
}
|
||||
|
||||
type FileImport struct {
|
||||
// Path is the import path of the import.
|
||||
Path string
|
||||
// Name is the name of the import, e.g. `foo` in `import foo "strings"`.
|
||||
Name string
|
||||
}
|
||||
|
||||
type PackageImport struct {
|
||||
// Path is the import path of the import.
|
||||
Path string
|
||||
}
|
||||
|
||||
type WorkspaceMetadataArgs struct {
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -622,6 +622,13 @@ var GeneratedAPIJSON = &APIJSON{
|
|||
Doc: "Runs `go get` to fetch a package.",
|
||||
ArgDoc: "{\n\t// Any document URI within the relevant module.\n\t\"URI\": string,\n\t// The package to go get.\n\t\"Pkg\": string,\n\t\"AddRequire\": bool,\n}",
|
||||
},
|
||||
{
|
||||
Command: "gopls.list_imports",
|
||||
Title: "List imports of a file and its package",
|
||||
Doc: "Retrieve a list of imports in the given Go file, and the package it\nbelongs to.",
|
||||
ArgDoc: "{\n\t// The file URI.\n\t\"URI\": string,\n}",
|
||||
ResultDoc: "{\n\t// Imports is a list of imports in the requested file.\n\t\"Imports\": []{\n\t\t\"Path\": string,\n\t\t\"Name\": string,\n\t},\n\t// PackageImports is a list of all imports in the requested file's package.\n\t\"PackageImports\": []{\n\t\t\"Path\": string,\n\t},\n}",
|
||||
},
|
||||
{
|
||||
Command: "gopls.list_known_packages",
|
||||
Title: "List known packages",
|
||||
|
|
|
|||
Loading…
Reference in New Issue