Skip to content

Commit 788d39e

Browse files
committed
gopls/internal/golang: "Show free symbols" code action
This change adds a new "Show free symbols" (source.freesymbols) code action that reports the set of free symbols referenced by the selected region of Go source. The HTML report, produced by the /freerefs endpoint of gopls' web server, includes an itemized list of symbols, and a color-annotated source listing. Symbols are presented in three groups: imported symbols (grouped by package); package-level symbols and local symbols. Each symbol is a link to either the integrated doc viewer (for imported symbols) or the declaration, for others. The feature is visible in editors as: - VS Code: Source actions... > Show free references - Emacs+eglot: M-x go-freesymbols (Requires (eglot--code-action go-freerefs "source.freesymbols") until dominikh/go-mode.el#436 is resolved.) There are a number of opportunities for factoring in common with RenderPackageDoc; they will be dealt with in a follow-up. Also: - a unit test of the freeRefs algorithm; - an integration test of the web interaction. - release notes. Change-Id: I97de76686fcc28e445a72e7c611673c47e467dfd Reviewed-on: https://go-review.googlesource.com/c/tools/+/539663 LUCI-TryBot-Result: Go LUCI <[email protected]> Auto-Submit: Alan Donovan <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent f73683e commit 788d39e

File tree

16 files changed

+893
-29
lines changed

16 files changed

+893
-29
lines changed

gopls/doc/commands.md

+29
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,35 @@ Result:
306306
map[golang.org/x/tools/gopls/internal/protocol.DocumentURI]*golang.org/x/tools/gopls/internal/vulncheck.Result
307307
```
308308

309+
### **report free symbols referenced by the selection.**
310+
Identifier: `gopls.free_symbols`
311+
312+
This command is a query over a selected range of Go source
313+
code. It reports the set of "free" symbols of the
314+
selection: the set of symbols that are referenced within
315+
the selection but are declared outside of it. This
316+
information is useful for understanding at a glance what a
317+
block of code depends on, perhaps as a precursor to
318+
extracting it into a separate function.
319+
320+
Args:
321+
322+
```
323+
string,
324+
{
325+
// The range's start position.
326+
"start": {
327+
"line": uint32,
328+
"character": uint32,
329+
},
330+
// The range's end position.
331+
"end": {
332+
"line": uint32,
333+
"character": uint32,
334+
},
335+
}
336+
```
337+
309338
### **Toggle gc_details**
310339
Identifier: `gopls.gc_details`
311340

gopls/doc/release/v0.16.0.md

+38
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,44 @@ Editor support:
5454

5555
- TODO: test in vim, neovim, sublime, helix.
5656

57+
### Free symbols
58+
59+
Gopls offers another web-based code action, "Show free symbols",
60+
which displays the free symbols referenced by the selected code.
61+
62+
A symbol is "free" if it is referenced within the selection but
63+
declared outside of it. The free symbols that are variables are, in
64+
effect, the set of parameters that would be needed if the block were
65+
extracted into its own function in the same package.
66+
67+
Even when you don't intend to extract a block into a new function,
68+
this information can help you to tell at a glance what names a block
69+
of code depends on.
70+
71+
Each dotted path of identifiers (such as `file.Name.Pos`) is reported
72+
as a separate item, so that you can see which parts of a complex
73+
type are actually needed.
74+
75+
Viewing the free symbols of the body of a function may reveal that
76+
only a small part (a single field of a struct, say) of one of the
77+
function's parameters is used, allowing you to simplify and generalize
78+
the function by choosing a different type for that parameter.
79+
80+
- TODO screenshot
81+
82+
- VS Code: use the `Source action > View free symbols` menu item.
83+
84+
- Emacs: requires eglot v1.17. You may find this `go-doc` function a
85+
useful shortcut:
86+
87+
```lisp
88+
(eglot--code-action eglot-code-action-freesymbols "source.freesymbols")
89+
90+
(defalias 'go-freesymbols #'eglot-code-action-freesymbols
91+
"View free symbols referred to by the current selection.")
92+
```
93+
TODO(dominikh/go-mode.el#436): add both of these to go-mode.el.
94+
5795
### `unusedwrite` analyzer
5896

5997
The new

gopls/internal/doc/api.json

+7
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,13 @@
10021002
"ArgDoc": "{\n\t// The file URI.\n\t\"URI\": string,\n}",
10031003
"ResultDoc": "map[golang.org/x/tools/gopls/internal/protocol.DocumentURI]*golang.org/x/tools/gopls/internal/vulncheck.Result"
10041004
},
1005+
{
1006+
"Command": "gopls.free_symbols",
1007+
"Title": "report free symbols referenced by the selection.",
1008+
"Doc": "This command is a query over a selected range of Go source\ncode. It reports the set of \"free\" symbols of the\nselection: the set of symbols that are referenced within\nthe selection but are declared outside of it. This\ninformation is useful for understanding at a glance what a\nblock of code depends on, perhaps as a precursor to\nextracting it into a separate function.",
1009+
"ArgDoc": "string,\n{\n\t// The range's start position.\n\t\"start\": {\n\t\t\"line\": uint32,\n\t\t\"character\": uint32,\n\t},\n\t// The range's end position.\n\t\"end\": {\n\t\t\"line\": uint32,\n\t\t\"character\": uint32,\n\t},\n}",
1010+
"ResultDoc": ""
1011+
},
10051012
{
10061013
"Command": "gopls.gc_details",
10071014
"Title": "Toggle gc_details",

gopls/internal/golang/codeaction.go

+34-17
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,13 @@ func CodeActions(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle,
3636
// when adding new query operations like GoTest and GoDoc that
3737
// are permitted even in generated source files
3838

39-
// Code actions requiring syntax information alone.
40-
if wantQuickFixes || want[protocol.SourceOrganizeImports] || want[protocol.RefactorExtract] {
39+
// Code actions that can be offered based on syntax information alone.
40+
if wantQuickFixes ||
41+
want[protocol.SourceOrganizeImports] ||
42+
want[protocol.RefactorExtract] ||
43+
want[protocol.GoDoc] ||
44+
want[protocol.GoFreeSymbols] {
45+
4146
pgf, err := snapshot.ParseGo(ctx, fh, parsego.Full)
4247
if err != nil {
4348
return nil, err
@@ -89,13 +94,38 @@ func CodeActions(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle,
8994
}
9095
actions = append(actions, extractions...)
9196
}
97+
98+
if want[protocol.GoDoc] {
99+
loc := protocol.Location{URI: pgf.URI, Range: rng}
100+
cmd, err := command.NewDocCommand("View package documentation", loc)
101+
if err != nil {
102+
return nil, err
103+
}
104+
actions = append(actions, protocol.CodeAction{
105+
Title: cmd.Title,
106+
Kind: protocol.GoDoc,
107+
Command: &cmd,
108+
})
109+
}
110+
111+
if want[protocol.GoFreeSymbols] && rng.End != rng.Start {
112+
cmd, err := command.NewFreeSymbolsCommand("Show free symbols", pgf.URI, rng)
113+
if err != nil {
114+
return nil, err
115+
}
116+
// For implementation, see commandHandler.showFreeSymbols.
117+
actions = append(actions, protocol.CodeAction{
118+
Title: cmd.Title,
119+
Kind: protocol.GoFreeSymbols,
120+
Command: &cmd,
121+
})
122+
}
92123
}
93124

94125
// Code actions requiring type information.
95126
if want[protocol.RefactorRewrite] ||
96127
want[protocol.RefactorInline] ||
97-
want[protocol.GoTest] ||
98-
want[protocol.GoDoc] {
128+
want[protocol.GoTest] {
99129
pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI())
100130
if err != nil {
101131
return nil, err
@@ -123,19 +153,6 @@ func CodeActions(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle,
123153
}
124154
actions = append(actions, fixes...)
125155
}
126-
127-
if want[protocol.GoDoc] {
128-
loc := protocol.Location{URI: pgf.URI, Range: rng}
129-
cmd, err := command.NewDocCommand("View package documentation", loc)
130-
if err != nil {
131-
return nil, err
132-
}
133-
actions = append(actions, protocol.CodeAction{
134-
Title: cmd.Title,
135-
Kind: protocol.GoDoc,
136-
Command: &cmd,
137-
})
138-
}
139156
}
140157
return actions, nil
141158
}

0 commit comments

Comments
 (0)