Skip to content

Commit aacfc85

Browse files
committed
refactors
1 parent 1a5533f commit aacfc85

File tree

8 files changed

+30
-68
lines changed

8 files changed

+30
-68
lines changed

gopls/doc/release/v0.16.0.md

+5-11
Original file line numberDiff line numberDiff line change
@@ -94,18 +94,12 @@ TODO(dominikh/go-mode.el#436): add both of these to go-mode.el.
9494

9595
### Extract declarations to new file
9696
Gopls now offers another code action, "Extract declarations to new file",
97-
which moves selected code sections to a newly created file within the
98-
same package. The created filename is chosen as the first {function, type,
99-
const, var} name encountered. In addition, import declarations are added or
100-
removed as needed.
101-
102-
The user can invoke this code action by selecting a function name, the keywords
103-
`func`, `const`, `var`, `type`, or by placing the caret on them without selecting,
104-
or by selecting a whole declaration or multiple declrations.
105-
106-
In order to avoid ambiguity and surprise about what to extract, some kinds
107-
of paritial selection of a declration cannot invoke this code action.
97+
which moves the selected declarations to a newly created file within the
98+
same package. The created file's name is based on the first symbol
99+
name encountered. Import declarations are added or removed as needed.
108100

101+
You can find this action by selecting one or more complete declarations,
102+
or just the func, const, var, or type token of any top-level declaration.
109103

110104
### `unusedwrite` analyzer
111105

gopls/internal/golang/codeaction.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ func getExtractCodeActions(pgf *parsego.File, rng protocol.Range, options *setti
258258
if canExtractToNewFile(pgf, start, end) {
259259
cmd, err := command.NewExtractToNewFileCommand(
260260
"Extract declarations to new file",
261-
command.ExtractToNewFileArgs{URI: pgf.URI, Range: rng},
261+
protocol.Location{URI: pgf.URI, Range: rng},
262262
)
263263
if err != nil {
264264
return nil, err

gopls/internal/golang/extracttofile.go

+18-33
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ import (
2727
"golang.org/x/tools/gopls/internal/util/typesutil"
2828
)
2929

30-
var ErrDotImport = errors.New("\"extract to new file\" does not support files containing dot imports")
31-
3230
// canExtractToNewFile reports whether the code in the given range can be extracted to a new file.
3331
func canExtractToNewFile(pgf *parsego.File, start, end token.Pos) bool {
3432
_, _, _, ok := selectedToplevelDecls(pgf, start, end)
@@ -39,7 +37,7 @@ func canExtractToNewFile(pgf *parsego.File, start, end token.Pos) bool {
3937
// or deleted from the old file if the range is extracted to a new file.
4038
//
4139
// TODO: handle dot imports.
42-
func findImportEdits(file *ast.File, info *types.Info, start, end token.Pos) (adds []*ast.ImportSpec, deletes []*ast.ImportSpec, _ error) {
40+
func findImportEdits(file *ast.File, info *types.Info, start, end token.Pos) (adds, deletes []*ast.ImportSpec, _ error) {
4341
// make a map from a pkgName to its references
4442
pkgNameReferences := make(map[*types.PkgName][]*ast.Ident)
4543
for ident, use := range info.Uses {
@@ -54,7 +52,8 @@ func findImportEdits(file *ast.File, info *types.Info, start, end token.Pos) (ad
5452
// deleted from the original file.
5553
for _, spec := range file.Imports {
5654
if spec.Name != nil && spec.Name.Name == "." {
57-
return nil, nil, ErrDotImport
55+
// TODO: support dot imports.
56+
return nil, nil, errors.New("\"extract to new file\" does not support files containing dot imports")
5857
}
5958
pkgName, ok := typesutil.ImportedPkgName(info, spec)
6059
if !ok {
@@ -96,7 +95,7 @@ func ExtractToNewFile(ctx context.Context, snapshot *cache.Snapshot, fh file.Han
9695

9796
start, end, firstSymbol, ok := selectedToplevelDecls(pgf, start, end)
9897
if !ok {
99-
return nil, bug.Errorf("precondition unmet")
98+
return nil, bug.Errorf("invalid selection")
10099
}
101100

102101
// select trailing empty lines
@@ -105,7 +104,7 @@ func ExtractToNewFile(ctx context.Context, snapshot *cache.Snapshot, fh file.Han
105104

106105
replaceRange, err := pgf.PosRange(start, end)
107106
if err != nil {
108-
return nil, bug.Errorf("findRangeAndFilename returned invalid range: %v", err)
107+
return nil, bug.Errorf("invalid range: %v", err)
109108
}
110109

111110
adds, deletes, err := findImportEdits(pgf.File, pkg.TypesInfo(), start, end)
@@ -120,9 +119,9 @@ func ExtractToNewFile(ctx context.Context, snapshot *cache.Snapshot, fh file.Han
120119
// For parenthesised declarations like `import ("fmt"\n "log")`
121120
// we only remove the ImportSpec, because removing the whole declaration
122121
// might remove other ImportsSpecs we don't want to touch.
123-
parenthesisFreeImports := findParenthesisFreeImports(pgf)
122+
unparenthesizedImports := unparenthesizedImports(pgf)
124123
for _, importSpec := range deletes {
125-
if decl := parenthesisFreeImports[importSpec]; decl != nil {
124+
if decl := unparenthesizedImports[importSpec]; decl != nil {
126125
importDeletes = append(importDeletes, removeNode(pgf, decl))
127126
} else {
128127
importDeletes = append(importDeletes, removeNode(pgf, importSpec))
@@ -143,7 +142,7 @@ func ExtractToNewFile(ctx context.Context, snapshot *cache.Snapshot, fh file.Han
143142
buf.WriteString(")\n")
144143
}
145144

146-
newFile, err := chooseNewFileURI(ctx, snapshot, pgf.URI.Dir().Path(), firstSymbol)
145+
newFile, err := chooseNewFile(ctx, snapshot, pgf.URI.Dir().Path(), firstSymbol)
147146
if err != nil {
148147
return nil, fmt.Errorf("%s: %w", errorPrefix, err)
149148
}
@@ -162,27 +161,14 @@ func ExtractToNewFile(ctx context.Context, snapshot *cache.Snapshot, fh file.Han
162161
// create a new file
163162
protocol.DocumentChangeCreate(newFile.URI()),
164163
// edit the created file
165-
protocol.DocumentChangeEdit(&uriVersion{uri: newFile.URI(), version: 0}, []protocol.TextEdit{
164+
protocol.DocumentChangeEdit(newFile, []protocol.TextEdit{
166165
{Range: protocol.Range{}, NewText: string(newFileContent)},
167166
})), nil
168167
}
169168

170-
// uriVersion is the simplest struct that implements protocol.fileHandle.
171-
type uriVersion struct {
172-
uri protocol.DocumentURI
173-
version int32
174-
}
175-
176-
func (fh *uriVersion) URI() protocol.DocumentURI {
177-
return fh.uri
178-
}
179-
func (fh *uriVersion) Version() int32 {
180-
return fh.version
181-
}
182-
183-
// chooseNewFileURI chooses a new filename in dir, based on the name of the
169+
// chooseNewFile chooses a new filename in dir, based on the name of the
184170
// first extracted symbol, and if necessary to disambiguate, a numeric suffix.
185-
func chooseNewFileURI(ctx context.Context, snapshot *cache.Snapshot, dir string, firstSymbol string) (file.Handle, error) {
171+
func chooseNewFile(ctx context.Context, snapshot *cache.Snapshot, dir string, firstSymbol string) (file.Handle, error) {
186172
basename := strings.ToLower(firstSymbol)
187173
newPath := protocol.URIFromPath(filepath.Join(dir, basename+".go"))
188174
for count := 1; count < 5; count++ {
@@ -248,6 +234,7 @@ func selectedToplevelDecls(pgf *parsego.File, start, end token.Pos) (token.Pos,
248234
return 0, 0, "", false
249235
}
250236
if id != nil && firstName == "" {
237+
// may be "_"
251238
firstName = id.Name
252239
}
253240
// extends selection to docs comments
@@ -277,23 +264,21 @@ func selectedToplevelDecls(pgf *parsego.File, start, end token.Pos) (token.Pos,
277264
return start, end, firstName, true
278265
}
279266

280-
func findParenthesisFreeImports(pgf *parsego.File) map[*ast.ImportSpec]*ast.GenDecl {
267+
// unparenthesizedImports returns a map from each unparenthesized ImportSpec
268+
// to its enclosing declaration (which may need to be deleted too).
269+
func unparenthesizedImports(pgf *parsego.File) map[*ast.ImportSpec]*ast.GenDecl {
281270
decls := make(map[*ast.ImportSpec]*ast.GenDecl)
282271
for _, decl := range pgf.File.Decls {
283-
if g, ok := decl.(*ast.GenDecl); ok {
284-
if !g.Lparen.IsValid() && len(g.Specs) > 0 {
285-
if v, ok := g.Specs[0].(*ast.ImportSpec); ok {
286-
decls[v] = g
287-
}
288-
}
272+
if decl, ok := decl.(*ast.GenDecl); ok && decl.Tok == token.IMPORT && !decl.Lparen.IsValid() {
273+
decls[decl.Specs[0].(*ast.ImportSpec)] = decl
289274
}
290275
}
291276
return decls
292277
}
293278

294279
// removeNode returns a TextEdit that removes the node.
295280
func removeNode(pgf *parsego.File, node ast.Node) protocol.TextEdit {
296-
rng, err := pgf.PosRange(node.Pos(), node.End())
281+
rng, err := pgf.NodeRange(node)
297282
if err != nil {
298283
bug.Reportf("removeNode: %v", err)
299284
}

gopls/internal/protocol/command/command_gen.go

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gopls/internal/protocol/command/interface.go

+1-7
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ type Interface interface {
157157
// ExtractToNewFile: Move selected declarations to a new file
158158
//
159159
// Used by the code action of the same name.
160-
ExtractToNewFile(context.Context, ExtractToNewFileArgs) error
160+
ExtractToNewFile(context.Context, protocol.Location) error
161161

162162
// StartDebugging: Start the gopls debug server
163163
//
@@ -352,12 +352,6 @@ type AddImportArgs struct {
352352
URI protocol.DocumentURI
353353
}
354354

355-
type ExtractToNewFileArgs struct {
356-
// URI of the file
357-
URI protocol.DocumentURI
358-
Range protocol.Range
359-
}
360-
361355
type ListKnownPackagesResult struct {
362356
// Packages is a list of packages relative
363357
// to the URIArg passed by the command request.

gopls/internal/server/command.go

+1-5
Original file line numberDiff line numberDiff line change
@@ -995,17 +995,13 @@ func (c *commandHandler) AddImport(ctx context.Context, args command.AddImportAr
995995
})
996996
}
997997

998-
func (c *commandHandler) ExtractToNewFile(ctx context.Context, args command.ExtractToNewFileArgs) error {
998+
func (c *commandHandler) ExtractToNewFile(ctx context.Context, args protocol.Location) error {
999999
return c.run(ctx, commandConfig{
10001000
progress: "Extract to a new file",
10011001
forURI: args.URI,
10021002
}, func(ctx context.Context, deps commandDeps) error {
10031003
edit, err := golang.ExtractToNewFile(ctx, deps.snapshot, deps.fh, args.Range)
10041004
if err != nil {
1005-
if errors.Is(err, golang.ErrDotImport) {
1006-
showMessage(ctx, c.s.client, protocol.Info, err.Error())
1007-
return nil
1008-
}
10091005
return err
10101006
}
10111007
resp, err := c.s.client.ApplyEdit(ctx, &protocol.ApplyWorkspaceEditParams{Edit: *edit})

gopls/internal/test/integration/wrappers.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,8 @@ func (e *Env) SetBufferContent(name string, content string) {
117117
}
118118

119119
// FileContent returns the file content for name that applies to the current
120-
// editing session: if the file is open, it returns its buffer content,
121-
// otherwise it returns on disk content, if the file does not exist,
122-
// it returns an empty string.
120+
// editing session: it returns the buffer content for an open file, the
121+
// on-disk content for an unopened file, or "" for a non-existent file.
123122
func (e *Env) FileContent(name string) string {
124123
e.T.Helper()
125124
text, ok := e.Editor.BufferText(name)

gopls/internal/test/marker/testdata/codeaction/extracttofile.txt

-6
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,6 @@ package main
7171
import _ "fmt"
7272
func F() {} //@codeactionedit("func", "refactor.extract", blank_import)
7373

74-
-- dot_import.go --
75-
// This case is not yet implemented, it aborts and shows a message to the client.
76-
package main
77-
import . "fmt"
78-
func F() { Println() } //@codeactionedit("func", "refactor.extract", dot_import)
79-
8074

8175

8276
-- @blank_import/blank_import.go --

0 commit comments

Comments
 (0)