Skip to content

Commit

Permalink
gopls/internal/golang: add "Browse gopls features" code action
Browse files Browse the repository at this point in the history
This command opens the Index of Features doc page:

  $ gopls codeaction -kind=gopls.doc.features -exec ./gopls/main.go

VS Code exposes this new code action through the Quick Fix
menu (Command-.) under the section "More actions...".
It should probably also be given a top-level command similar
to "Go: Add Import", etc.

Other editors seem to treat code actions
more uniformly, so special handling is unnecessary.

Change-Id: I633dd34cdb9005009a098bcd7bb50d0db06044c7
Reviewed-on: https://go-review.googlesource.com/c/tools/+/595557
Commit-Queue: Alan Donovan <adonovan@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Alan Donovan <adonovan@google.com>
  • Loading branch information
adonovan committed Aug 1, 2024
1 parent ead76ab commit 2154cbf
Show file tree
Hide file tree
Showing 15 changed files with 111 additions and 43 deletions.
11 changes: 11 additions & 0 deletions gopls/doc/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,17 @@ Args:
}
```

<a id='gopls.client_open_url'></a>
## `gopls.client_open_url`: **Request that the client open a URL in a browser.**



Args:

```
string
```

<a id='gopls.diagnose_files'></a>
## `gopls.diagnose_files`: **Cause server to publish diagnostics for the specified files.**

Expand Down
7 changes: 6 additions & 1 deletion gopls/doc/features/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ when making significant changes to existing features or when adding new ones.
- [Type Definition](navigation.md#type-definition): go to definition of type of selected symbol
- [References](navigation.md#references): list references to selected symbol
- [Implementation](navigation.md#implementation): show "implements" relationships of selected type
- [Document Symbol](passive.md#document-symbol): outline of symbols defined in current file
- [Document Symbol](navigation.md#document-symbol): outline of symbols defined in current file
- [Symbol](navigation.md#symbol): fuzzy search for symbol by name
- [Selection Range](navigation.md#selection-range): select enclosing unit of syntax
- [Call Hierarchy](navigation.md#call-hierarchy): show outgoing/incoming calls to the current function
Expand All @@ -59,3 +59,8 @@ when making significant changes to existing features or when adding new ones.
- [go.mod and go.work files](modfiles.md): Go module and workspace manifests
- [Command-line interface](../command-line.md): CLI for debugging and scripting (unstable)
- [Non-standard commands](../commands.md): gopls-specific RPC protocol extensions (unstable)

You can find this page from within your editor by executing the
`gopls.doc.features` [code action](transformation.md#code-actions),
which opens it in a web browser.
In VS Code, you can find it on the Quick fix menu.
1 change: 1 addition & 0 deletions gopls/internal/cmd/codeaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Valid kinds include:
source.doc
source.freesymbols
goTest
gopls.doc.features
Kinds are hierarchical, so "refactor" includes "refactor.inline".
(Note: actions of kind "goTest" are not returned unless explicitly
Expand Down
7 changes: 5 additions & 2 deletions gopls/internal/cmd/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -977,10 +977,13 @@ type C struct{}
}
// list code actions in file, filtering by title
{
res := gopls(t, tree, "codeaction", "-title=Br.wse", "a.go")
res := gopls(t, tree, "codeaction", "-title=Browse.*doc", "a.go")
res.checkExit(true)
got := res.stdout
want := `command "Browse documentation for package a" [source.doc]` + "\n"
want := `command "Browse gopls feature documentation" [gopls.doc.features]` +
"\n" +
`command "Browse documentation for package a" [source.doc]` +
"\n"
if got != want {
t.Errorf("codeaction: got <<%s>>, want <<%s>>\nstderr:\n%s", got, want, res.stderr)
}
Expand Down
1 change: 1 addition & 0 deletions gopls/internal/cmd/usage/codeaction.hlp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Valid kinds include:
source.doc
source.freesymbols
goTest
gopls.doc.features

Kinds are hierarchical, so "refactor" includes "refactor.inline".
(Note: actions of kind "goTest" are not returned unless explicitly
Expand Down
7 changes: 7 additions & 0 deletions gopls/internal/doc/api.json
Original file line number Diff line number Diff line change
Expand Up @@ -989,6 +989,13 @@
"ArgDoc": "{\n\t// The go.mod file URI.\n\t\"URI\": string,\n\t// The modules to check.\n\t\"Modules\": []string,\n}",
"ResultDoc": ""
},
{
"Command": "gopls.client_open_url",
"Title": "Request that the client open a URL in a browser.",
"Doc": "",
"ArgDoc": "string",
"ResultDoc": ""
},
{
"Command": "gopls.diagnose_files",
"Title": "Cause server to publish diagnostics for the specified files.",
Expand Down
19 changes: 18 additions & 1 deletion gopls/internal/golang/codeaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ func CodeActions(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle,
if wantQuickFixes ||
want[protocol.SourceOrganizeImports] ||
want[protocol.RefactorExtract] ||
want[settings.GoFreeSymbols] {
want[settings.GoFreeSymbols] ||
want[settings.GoplsDocFeatures] {

pgf, err := snapshot.ParseGo(ctx, fh, parsego.Full)
if err != nil {
Expand Down Expand Up @@ -115,6 +116,22 @@ func CodeActions(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle,
Command: &cmd,
})
}

if want[settings.GoplsDocFeatures] {
// TODO(adonovan): after the docs are published in gopls/v0.17.0,
// use the gopls release tag instead of master.
cmd, err := command.NewClientOpenURLCommand(
"Browse gopls feature documentation",
"https://github.com/golang/tools/blob/master/gopls/doc/features/README.md")
if err != nil {
return nil, err
}
actions = append(actions, protocol.CodeAction{
Title: cmd.Title,
Kind: settings.GoplsDocFeatures,
Command: &cmd,
})
}
}

// Code actions requiring type information.
Expand Down
20 changes: 20 additions & 0 deletions gopls/internal/protocol/command/command_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions gopls/internal/protocol/command/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,9 @@ type Interface interface {
// The machine architecture is determined by the view.
Assembly(_ context.Context, viewID, packageID, symbol string) error

// ClientOpenURL: Request that the client open a URL in a browser.
ClientOpenURL(_ context.Context, url string) error

// ScanImports: force a sychronous scan of the imports cache.
//
// This command is intended for use by gopls tests only.
Expand Down
3 changes: 2 additions & 1 deletion gopls/internal/server/code_action.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ func (s *server) CodeAction(ctx context.Context, params *protocol.CodeActionPara
case settings.GoTest,
settings.GoDoc,
settings.GoFreeSymbols,
settings.GoAssembly:
settings.GoAssembly,
settings.GoplsDocFeatures:
return false // read-only query
}
return true // potential write operation
Expand Down
5 changes: 5 additions & 0 deletions gopls/internal/server/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -1462,6 +1462,11 @@ func (c *commandHandler) Assembly(ctx context.Context, viewID, packageID, symbol
return nil
}

func (c *commandHandler) ClientOpenURL(ctx context.Context, url string) error {
openClientBrowser(ctx, c.s.client, url)
return nil
}

func (c *commandHandler) ScanImports(ctx context.Context) error {
for _, v := range c.s.session.Views() {
v.ScanImports()
Expand Down
12 changes: 7 additions & 5 deletions gopls/internal/settings/codeactionkind.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ import "golang.org/x/tools/gopls/internal/protocol"
// actions with kind="source.*". A lightbulb appears in both cases.
// A third menu, "Quick fix...", not found on the usual context
// menu but accessible through the command palette or "⌘.",
// displays code actions of kind "quickfix.*" and "refactor.*".
// displays code actions of kind "quickfix.*" and "refactor.*",
// and ad hoc ones ("More actions...") such as "gopls.*".
// All of these CodeAction requests have triggerkind=Invoked.
//
// Cursor motion also performs a CodeAction request, but with
Expand Down Expand Up @@ -76,8 +77,9 @@ import "golang.org/x/tools/gopls/internal/protocol"
// instead of == for CodeActionKinds throughout gopls.
// See golang/go#40438 for related discussion.
const (
GoAssembly protocol.CodeActionKind = "source.assembly"
GoDoc protocol.CodeActionKind = "source.doc"
GoFreeSymbols protocol.CodeActionKind = "source.freesymbols"
GoTest protocol.CodeActionKind = "goTest" // TODO(adonovan): rename "source.test"
GoAssembly protocol.CodeActionKind = "source.assembly"
GoDoc protocol.CodeActionKind = "source.doc"
GoFreeSymbols protocol.CodeActionKind = "source.freesymbols"
GoTest protocol.CodeActionKind = "goTest" // TODO(adonovan): rename "source.test"
GoplsDocFeatures protocol.CodeActionKind = "gopls.doc.features"
)
1 change: 1 addition & 0 deletions gopls/internal/settings/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ func DefaultOptions(overrides ...func(*Options)) *Options {
GoDoc: true,
GoFreeSymbols: true,
// Not GoTest: it must be explicit in CodeActionParams.Context.Only
GoplsDocFeatures: true,
},
file.Mod: {
protocol.SourceOrganizeImports: true,
Expand Down
4 changes: 3 additions & 1 deletion gopls/internal/test/integration/misc/codeactions_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

53 changes: 21 additions & 32 deletions gopls/internal/test/integration/misc/webserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package misc

import (
"fmt"
"html"
"io"
"net/http"
Expand All @@ -15,6 +16,7 @@ import (

"golang.org/x/tools/gopls/internal/protocol"
"golang.org/x/tools/gopls/internal/protocol/command"
"golang.org/x/tools/gopls/internal/settings"
. "golang.org/x/tools/gopls/internal/test/integration"
"golang.org/x/tools/internal/testenv"
)
Expand Down Expand Up @@ -271,18 +273,10 @@ func (*T) M() { /*in T.M*/}
func viewPkgDoc(t *testing.T, env *Env, loc protocol.Location) protocol.URI {
// Invoke the "Browse package documentation" code
// action to start the server.
var docAction *protocol.CodeAction
actions := env.CodeAction(loc, nil, 0)
for _, act := range actions {
if strings.HasPrefix(act.Title, "Browse ") &&
strings.Contains(act.Title, "documentation") {
docAction = &act
break
}
}
if docAction == nil {
t.Fatalf("can't find action with Title 'Browse...documentation', only %#v",
actions)
docAction, err := codeActionByKind(actions, settings.GoDoc)
if err != nil {
t.Fatal(err)
}

// Execute the command.
Expand Down Expand Up @@ -335,16 +329,9 @@ func f(buf bytes.Buffer, greeting string) {
if err != nil {
t.Fatalf("CodeAction: %v", err)
}
var action *protocol.CodeAction
for _, a := range actions {
if a.Title == "Browse free symbols" {
action = &a
break
}
}
if action == nil {
t.Fatalf("can't find action with Title 'Browse free symbols', only %#v",
actions)
action, err := codeActionByKind(actions, settings.GoFreeSymbols)
if err != nil {
t.Fatal(err)
}

// Execute the command.
Expand Down Expand Up @@ -401,17 +388,9 @@ func g() {
if err != nil {
t.Fatalf("CodeAction: %v", err)
}
const wantTitle = "Browse " + runtime.GOARCH + " assembly for f"
var action *protocol.CodeAction
for _, a := range actions {
if a.Title == wantTitle {
action = &a
break
}
}
if action == nil {
t.Fatalf("can't find action with Title %s, only %#v",
wantTitle, actions)
action, err := codeActionByKind(actions, settings.GoAssembly)
if err != nil {
t.Fatal(err)
}

// Execute the command.
Expand Down Expand Up @@ -504,3 +483,13 @@ func checkMatch(t *testing.T, want bool, got []byte, pattern string) {
}
}
}

// codeActionByKind returns the first action of the specified kind, or an error.
func codeActionByKind(actions []protocol.CodeAction, kind protocol.CodeActionKind) (*protocol.CodeAction, error) {
for _, act := range actions {
if act.Kind == kind {
return &act, nil
}
}
return nil, fmt.Errorf("can't find action with kind %s, only %#v", kind, actions)
}

0 comments on commit 2154cbf

Please sign in to comment.