Skip to content

Commit 2154cbf

Browse files
committed
gopls/internal/golang: add "Browse gopls features" code action
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 <[email protected]> Reviewed-by: Robert Findley <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Auto-Submit: Alan Donovan <[email protected]>
1 parent ead76ab commit 2154cbf

File tree

15 files changed

+111
-43
lines changed

15 files changed

+111
-43
lines changed

gopls/doc/commands.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,17 @@ Args:
262262
}
263263
```
264264

265+
<a id='gopls.client_open_url'></a>
266+
## `gopls.client_open_url`: **Request that the client open a URL in a browser.**
267+
268+
269+
270+
Args:
271+
272+
```
273+
string
274+
```
275+
265276
<a id='gopls.diagnose_files'></a>
266277
## `gopls.diagnose_files`: **Cause server to publish diagnostics for the specified files.**
267278

gopls/doc/features/README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ when making significant changes to existing features or when adding new ones.
3838
- [Type Definition](navigation.md#type-definition): go to definition of type of selected symbol
3939
- [References](navigation.md#references): list references to selected symbol
4040
- [Implementation](navigation.md#implementation): show "implements" relationships of selected type
41-
- [Document Symbol](passive.md#document-symbol): outline of symbols defined in current file
41+
- [Document Symbol](navigation.md#document-symbol): outline of symbols defined in current file
4242
- [Symbol](navigation.md#symbol): fuzzy search for symbol by name
4343
- [Selection Range](navigation.md#selection-range): select enclosing unit of syntax
4444
- [Call Hierarchy](navigation.md#call-hierarchy): show outgoing/incoming calls to the current function
@@ -59,3 +59,8 @@ when making significant changes to existing features or when adding new ones.
5959
- [go.mod and go.work files](modfiles.md): Go module and workspace manifests
6060
- [Command-line interface](../command-line.md): CLI for debugging and scripting (unstable)
6161
- [Non-standard commands](../commands.md): gopls-specific RPC protocol extensions (unstable)
62+
63+
You can find this page from within your editor by executing the
64+
`gopls.doc.features` [code action](transformation.md#code-actions),
65+
which opens it in a web browser.
66+
In VS Code, you can find it on the Quick fix menu.

gopls/internal/cmd/codeaction.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ Valid kinds include:
5858
source.doc
5959
source.freesymbols
6060
goTest
61+
gopls.doc.features
6162
6263
Kinds are hierarchical, so "refactor" includes "refactor.inline".
6364
(Note: actions of kind "goTest" are not returned unless explicitly

gopls/internal/cmd/integration_test.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -977,10 +977,13 @@ type C struct{}
977977
}
978978
// list code actions in file, filtering by title
979979
{
980-
res := gopls(t, tree, "codeaction", "-title=Br.wse", "a.go")
980+
res := gopls(t, tree, "codeaction", "-title=Browse.*doc", "a.go")
981981
res.checkExit(true)
982982
got := res.stdout
983-
want := `command "Browse documentation for package a" [source.doc]` + "\n"
983+
want := `command "Browse gopls feature documentation" [gopls.doc.features]` +
984+
"\n" +
985+
`command "Browse documentation for package a" [source.doc]` +
986+
"\n"
984987
if got != want {
985988
t.Errorf("codeaction: got <<%s>>, want <<%s>>\nstderr:\n%s", got, want, res.stderr)
986989
}

gopls/internal/cmd/usage/codeaction.hlp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Valid kinds include:
2929
source.doc
3030
source.freesymbols
3131
goTest
32+
gopls.doc.features
3233

3334
Kinds are hierarchical, so "refactor" includes "refactor.inline".
3435
(Note: actions of kind "goTest" are not returned unless explicitly

gopls/internal/doc/api.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,13 @@
989989
"ArgDoc": "{\n\t// The go.mod file URI.\n\t\"URI\": string,\n\t// The modules to check.\n\t\"Modules\": []string,\n}",
990990
"ResultDoc": ""
991991
},
992+
{
993+
"Command": "gopls.client_open_url",
994+
"Title": "Request that the client open a URL in a browser.",
995+
"Doc": "",
996+
"ArgDoc": "string",
997+
"ResultDoc": ""
998+
},
992999
{
9931000
"Command": "gopls.diagnose_files",
9941001
"Title": "Cause server to publish diagnostics for the specified files.",

gopls/internal/golang/codeaction.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ func CodeActions(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle,
4848
if wantQuickFixes ||
4949
want[protocol.SourceOrganizeImports] ||
5050
want[protocol.RefactorExtract] ||
51-
want[settings.GoFreeSymbols] {
51+
want[settings.GoFreeSymbols] ||
52+
want[settings.GoplsDocFeatures] {
5253

5354
pgf, err := snapshot.ParseGo(ctx, fh, parsego.Full)
5455
if err != nil {
@@ -115,6 +116,22 @@ func CodeActions(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle,
115116
Command: &cmd,
116117
})
117118
}
119+
120+
if want[settings.GoplsDocFeatures] {
121+
// TODO(adonovan): after the docs are published in gopls/v0.17.0,
122+
// use the gopls release tag instead of master.
123+
cmd, err := command.NewClientOpenURLCommand(
124+
"Browse gopls feature documentation",
125+
"https://github.com/golang/tools/blob/master/gopls/doc/features/README.md")
126+
if err != nil {
127+
return nil, err
128+
}
129+
actions = append(actions, protocol.CodeAction{
130+
Title: cmd.Title,
131+
Kind: settings.GoplsDocFeatures,
132+
Command: &cmd,
133+
})
134+
}
118135
}
119136

120137
// Code actions requiring type information.

gopls/internal/protocol/command/command_gen.go

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gopls/internal/protocol/command/interface.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,9 @@ type Interface interface {
265265
// The machine architecture is determined by the view.
266266
Assembly(_ context.Context, viewID, packageID, symbol string) error
267267

268+
// ClientOpenURL: Request that the client open a URL in a browser.
269+
ClientOpenURL(_ context.Context, url string) error
270+
268271
// ScanImports: force a sychronous scan of the imports cache.
269272
//
270273
// This command is intended for use by gopls tests only.

gopls/internal/server/code_action.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ func (s *server) CodeAction(ctx context.Context, params *protocol.CodeActionPara
143143
case settings.GoTest,
144144
settings.GoDoc,
145145
settings.GoFreeSymbols,
146-
settings.GoAssembly:
146+
settings.GoAssembly,
147+
settings.GoplsDocFeatures:
147148
return false // read-only query
148149
}
149150
return true // potential write operation

gopls/internal/server/command.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,6 +1462,11 @@ func (c *commandHandler) Assembly(ctx context.Context, viewID, packageID, symbol
14621462
return nil
14631463
}
14641464

1465+
func (c *commandHandler) ClientOpenURL(ctx context.Context, url string) error {
1466+
openClientBrowser(ctx, c.s.client, url)
1467+
return nil
1468+
}
1469+
14651470
func (c *commandHandler) ScanImports(ctx context.Context) error {
14661471
for _, v := range c.s.session.Views() {
14671472
v.ScanImports()

gopls/internal/settings/codeactionkind.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ import "golang.org/x/tools/gopls/internal/protocol"
3131
// actions with kind="source.*". A lightbulb appears in both cases.
3232
// A third menu, "Quick fix...", not found on the usual context
3333
// menu but accessible through the command palette or "⌘.",
34-
// displays code actions of kind "quickfix.*" and "refactor.*".
34+
// displays code actions of kind "quickfix.*" and "refactor.*",
35+
// and ad hoc ones ("More actions...") such as "gopls.*".
3536
// All of these CodeAction requests have triggerkind=Invoked.
3637
//
3738
// Cursor motion also performs a CodeAction request, but with
@@ -76,8 +77,9 @@ import "golang.org/x/tools/gopls/internal/protocol"
7677
// instead of == for CodeActionKinds throughout gopls.
7778
// See golang/go#40438 for related discussion.
7879
const (
79-
GoAssembly protocol.CodeActionKind = "source.assembly"
80-
GoDoc protocol.CodeActionKind = "source.doc"
81-
GoFreeSymbols protocol.CodeActionKind = "source.freesymbols"
82-
GoTest protocol.CodeActionKind = "goTest" // TODO(adonovan): rename "source.test"
80+
GoAssembly protocol.CodeActionKind = "source.assembly"
81+
GoDoc protocol.CodeActionKind = "source.doc"
82+
GoFreeSymbols protocol.CodeActionKind = "source.freesymbols"
83+
GoTest protocol.CodeActionKind = "goTest" // TODO(adonovan): rename "source.test"
84+
GoplsDocFeatures protocol.CodeActionKind = "gopls.doc.features"
8385
)

gopls/internal/settings/default.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ func DefaultOptions(overrides ...func(*Options)) *Options {
5353
GoDoc: true,
5454
GoFreeSymbols: true,
5555
// Not GoTest: it must be explicit in CodeActionParams.Context.Only
56+
GoplsDocFeatures: true,
5657
},
5758
file.Mod: {
5859
protocol.SourceOrganizeImports: true,

gopls/internal/test/integration/misc/codeactions_test.go

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gopls/internal/test/integration/misc/webserver_test.go

Lines changed: 21 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package misc
66

77
import (
8+
"fmt"
89
"html"
910
"io"
1011
"net/http"
@@ -15,6 +16,7 @@ import (
1516

1617
"golang.org/x/tools/gopls/internal/protocol"
1718
"golang.org/x/tools/gopls/internal/protocol/command"
19+
"golang.org/x/tools/gopls/internal/settings"
1820
. "golang.org/x/tools/gopls/internal/test/integration"
1921
"golang.org/x/tools/internal/testenv"
2022
)
@@ -271,18 +273,10 @@ func (*T) M() { /*in T.M*/}
271273
func viewPkgDoc(t *testing.T, env *Env, loc protocol.Location) protocol.URI {
272274
// Invoke the "Browse package documentation" code
273275
// action to start the server.
274-
var docAction *protocol.CodeAction
275276
actions := env.CodeAction(loc, nil, 0)
276-
for _, act := range actions {
277-
if strings.HasPrefix(act.Title, "Browse ") &&
278-
strings.Contains(act.Title, "documentation") {
279-
docAction = &act
280-
break
281-
}
282-
}
283-
if docAction == nil {
284-
t.Fatalf("can't find action with Title 'Browse...documentation', only %#v",
285-
actions)
277+
docAction, err := codeActionByKind(actions, settings.GoDoc)
278+
if err != nil {
279+
t.Fatal(err)
286280
}
287281

288282
// Execute the command.
@@ -335,16 +329,9 @@ func f(buf bytes.Buffer, greeting string) {
335329
if err != nil {
336330
t.Fatalf("CodeAction: %v", err)
337331
}
338-
var action *protocol.CodeAction
339-
for _, a := range actions {
340-
if a.Title == "Browse free symbols" {
341-
action = &a
342-
break
343-
}
344-
}
345-
if action == nil {
346-
t.Fatalf("can't find action with Title 'Browse free symbols', only %#v",
347-
actions)
332+
action, err := codeActionByKind(actions, settings.GoFreeSymbols)
333+
if err != nil {
334+
t.Fatal(err)
348335
}
349336

350337
// Execute the command.
@@ -401,17 +388,9 @@ func g() {
401388
if err != nil {
402389
t.Fatalf("CodeAction: %v", err)
403390
}
404-
const wantTitle = "Browse " + runtime.GOARCH + " assembly for f"
405-
var action *protocol.CodeAction
406-
for _, a := range actions {
407-
if a.Title == wantTitle {
408-
action = &a
409-
break
410-
}
411-
}
412-
if action == nil {
413-
t.Fatalf("can't find action with Title %s, only %#v",
414-
wantTitle, actions)
391+
action, err := codeActionByKind(actions, settings.GoAssembly)
392+
if err != nil {
393+
t.Fatal(err)
415394
}
416395

417396
// Execute the command.
@@ -504,3 +483,13 @@ func checkMatch(t *testing.T, want bool, got []byte, pattern string) {
504483
}
505484
}
506485
}
486+
487+
// codeActionByKind returns the first action of the specified kind, or an error.
488+
func codeActionByKind(actions []protocol.CodeAction, kind protocol.CodeActionKind) (*protocol.CodeAction, error) {
489+
for _, act := range actions {
490+
if act.Kind == kind {
491+
return &act, nil
492+
}
493+
}
494+
return nil, fmt.Errorf("can't find action with kind %s, only %#v", kind, actions)
495+
}

0 commit comments

Comments
 (0)