gopls/internal: implement Packages command

Implements the `gopls.packages` command.

Updates golang/go#59445

Change-Id: Ia72a971b7aac9baa964c8cf5eee8b332b3125fa4
Reviewed-on: https://go-review.googlesource.com/c/tools/+/600355
Reviewed-by: Cherry Mui <cherryyz@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Ethan Reesor 2024-08-07 17:21:57 -05:00 коммит произвёл Robert Findley
Родитель e5e8aa8472
Коммит f5c7449850
2 изменённых файлов: 218 добавлений и 2 удалений

Просмотреть файл

@ -143,10 +143,87 @@ func (h *commandHandler) Modules(ctx context.Context, args command.ModulesArgs)
return result, nil
}
func (h *commandHandler) Packages(context.Context, command.PackagesArgs) (command.PackagesResult, error) {
func (h *commandHandler) Packages(ctx context.Context, args command.PackagesArgs) (command.PackagesResult, error) {
wantTests := args.Mode&command.NeedTests != 0
result := command.PackagesResult{
Module: make(map[string]command.Module),
}
keepPackage := func(pkg *metadata.Package) bool {
for _, file := range pkg.GoFiles {
for _, arg := range args.Files {
if file == arg || file.Dir() == arg {
return true
}
if args.Recursive && arg.Encloses(file) {
return true
}
}
}
return false
}
buildPackage := func(snapshot *cache.Snapshot, meta *metadata.Package) (command.Package, command.Module) {
if wantTests {
// These will be used in the next CL to query tests
_, _ = ctx, snapshot
panic("unimplemented")
}
pkg := command.Package{
Path: string(meta.PkgPath),
}
if meta.Module == nil {
return pkg, command.Module{}
}
mod := command.Module{
Path: meta.Module.Path,
Version: meta.Module.Version,
GoMod: protocol.URIFromPath(meta.Module.GoMod),
}
pkg.ModulePath = mod.Path
return pkg, mod
}
err := h.run(ctx, commandConfig{
progress: "Packages",
}, func(ctx context.Context, _ commandDeps) error {
for _, view := range h.s.session.Views() {
snapshot, release, err := view.Snapshot()
if err != nil {
return err
}
defer release()
metas, err := snapshot.WorkspaceMetadata(ctx)
if err != nil {
return err
}
for _, meta := range metas {
if meta.IsIntermediateTestVariant() {
continue
}
if !keepPackage(meta) {
continue
}
pkg, mod := buildPackage(snapshot, meta)
result.Packages = append(result.Packages, pkg)
// Overwriting is ok
if mod.Path != "" {
result.Module[mod.Path] = mod
}
}
}
return nil
})
return result, err
}
func (h *commandHandler) MaybePromptForTelemetry(ctx context.Context) error {
go h.s.maybePromptForTelemetry(ctx, true)
return nil

Просмотреть файл

@ -0,0 +1,139 @@
// Copyright 2024 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 workspace
import (
"sort"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"golang.org/x/tools/gopls/internal/protocol"
"golang.org/x/tools/gopls/internal/protocol/command"
. "golang.org/x/tools/gopls/internal/test/integration"
)
func TestPackages(t *testing.T) {
const goModView = `
-- go.mod --
module foo
-- foo.go --
package foo
func Foo()
-- bar/bar.go --
package bar
func Bar()
-- baz/go.mod --
module baz
-- baz/baz.go --
package baz
func Baz()
`
t.Run("file", func(t *testing.T) {
Run(t, goModView, func(t *testing.T, env *Env) {
checkPackages(t, env, []protocol.DocumentURI{env.Editor.DocumentURI("foo.go")}, false, []command.Package{
{
Path: "foo",
ModulePath: "foo",
},
}, map[string]command.Module{
"foo": {
Path: "foo",
GoMod: env.Editor.DocumentURI("go.mod"),
},
})
})
})
t.Run("package", func(t *testing.T) {
Run(t, goModView, func(t *testing.T, env *Env) {
checkPackages(t, env, []protocol.DocumentURI{env.Editor.DocumentURI("bar")}, false, []command.Package{
{
Path: "foo/bar",
ModulePath: "foo",
},
}, map[string]command.Module{
"foo": {
Path: "foo",
GoMod: env.Editor.DocumentURI("go.mod"),
},
})
})
})
t.Run("workspace", func(t *testing.T) {
Run(t, goModView, func(t *testing.T, env *Env) {
checkPackages(t, env, []protocol.DocumentURI{env.Editor.DocumentURI("")}, true, []command.Package{
{
Path: "foo",
ModulePath: "foo",
},
{
Path: "foo/bar",
ModulePath: "foo",
},
}, map[string]command.Module{
"foo": {
Path: "foo",
GoMod: env.Editor.DocumentURI("go.mod"),
},
})
})
})
t.Run("nested module", func(t *testing.T) {
Run(t, goModView, func(t *testing.T, env *Env) {
// Load the nested module
env.OpenFile("baz/baz.go")
// Request packages using the URI of the nested module _directory_
checkPackages(t, env, []protocol.DocumentURI{env.Editor.DocumentURI("baz")}, true, []command.Package{
{
Path: "baz",
ModulePath: "baz",
},
}, map[string]command.Module{
"baz": {
Path: "baz",
GoMod: env.Editor.DocumentURI("baz/go.mod"),
},
})
})
})
}
func checkPackages(t testing.TB, env *Env, files []protocol.DocumentURI, recursive bool, wantPkg []command.Package, wantModule map[string]command.Module) {
t.Helper()
cmd, err := command.NewPackagesCommand("Packages", command.PackagesArgs{Files: files, Recursive: recursive})
if err != nil {
t.Fatal(err)
}
var result command.PackagesResult
env.ExecuteCommand(&protocol.ExecuteCommandParams{
Command: command.Packages.String(),
Arguments: cmd.Arguments,
}, &result)
// The ordering of packages is undefined so sort the results to ensure
// consistency
sort.Slice(result.Packages, func(i, j int) bool {
a, b := result.Packages[i], result.Packages[j]
return strings.Compare(a.Path, b.Path) < 0
})
if diff := cmp.Diff(wantPkg, result.Packages); diff != "" {
t.Errorf("Packages(%v) returned unexpected packages (-want +got):\n%s", files, diff)
}
if diff := cmp.Diff(wantModule, result.Module); diff != "" {
t.Errorf("Packages(%v) returned unexpected modules (-want +got):\n%s", files, diff)
}
}