@@ -27,8 +27,6 @@ import (
27
27
"golang.org/x/tools/gopls/internal/util/typesutil"
28
28
)
29
29
30
- var ErrDotImport = errors .New ("\" extract to new file\" does not support files containing dot imports" )
31
-
32
30
// canExtractToNewFile reports whether the code in the given range can be extracted to a new file.
33
31
func canExtractToNewFile (pgf * parsego.File , start , end token.Pos ) bool {
34
32
_ , _ , _ , ok := selectedToplevelDecls (pgf , start , end )
@@ -39,7 +37,7 @@ func canExtractToNewFile(pgf *parsego.File, start, end token.Pos) bool {
39
37
// or deleted from the old file if the range is extracted to a new file.
40
38
//
41
39
// 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 ) {
43
41
// make a map from a pkgName to its references
44
42
pkgNameReferences := make (map [* types.PkgName ][]* ast.Ident )
45
43
for ident , use := range info .Uses {
@@ -54,7 +52,8 @@ func findImportEdits(file *ast.File, info *types.Info, start, end token.Pos) (ad
54
52
// deleted from the original file.
55
53
for _ , spec := range file .Imports {
56
54
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" )
58
57
}
59
58
pkgName , ok := typesutil .ImportedPkgName (info , spec )
60
59
if ! ok {
@@ -96,7 +95,7 @@ func ExtractToNewFile(ctx context.Context, snapshot *cache.Snapshot, fh file.Han
96
95
97
96
start , end , firstSymbol , ok := selectedToplevelDecls (pgf , start , end )
98
97
if ! ok {
99
- return nil , bug .Errorf ("precondition unmet " )
98
+ return nil , bug .Errorf ("invalid selection " )
100
99
}
101
100
102
101
// select trailing empty lines
@@ -105,7 +104,7 @@ func ExtractToNewFile(ctx context.Context, snapshot *cache.Snapshot, fh file.Han
105
104
106
105
replaceRange , err := pgf .PosRange (start , end )
107
106
if err != nil {
108
- return nil , bug .Errorf ("findRangeAndFilename returned invalid range: %v" , err )
107
+ return nil , bug .Errorf ("invalid range: %v" , err )
109
108
}
110
109
111
110
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
120
119
// For parenthesised declarations like `import ("fmt"\n "log")`
121
120
// we only remove the ImportSpec, because removing the whole declaration
122
121
// might remove other ImportsSpecs we don't want to touch.
123
- parenthesisFreeImports := findParenthesisFreeImports (pgf )
122
+ unparenthesizedImports := unparenthesizedImports (pgf )
124
123
for _ , importSpec := range deletes {
125
- if decl := parenthesisFreeImports [importSpec ]; decl != nil {
124
+ if decl := unparenthesizedImports [importSpec ]; decl != nil {
126
125
importDeletes = append (importDeletes , removeNode (pgf , decl ))
127
126
} else {
128
127
importDeletes = append (importDeletes , removeNode (pgf , importSpec ))
@@ -143,7 +142,7 @@ func ExtractToNewFile(ctx context.Context, snapshot *cache.Snapshot, fh file.Han
143
142
buf .WriteString (")\n " )
144
143
}
145
144
146
- newFile , err := chooseNewFileURI (ctx , snapshot , pgf .URI .Dir ().Path (), firstSymbol )
145
+ newFile , err := chooseNewFile (ctx , snapshot , pgf .URI .Dir ().Path (), firstSymbol )
147
146
if err != nil {
148
147
return nil , fmt .Errorf ("%s: %w" , errorPrefix , err )
149
148
}
@@ -162,27 +161,14 @@ func ExtractToNewFile(ctx context.Context, snapshot *cache.Snapshot, fh file.Han
162
161
// create a new file
163
162
protocol .DocumentChangeCreate (newFile .URI ()),
164
163
// edit the created file
165
- protocol .DocumentChangeEdit (& uriVersion { uri : newFile . URI (), version : 0 } , []protocol.TextEdit {
164
+ protocol .DocumentChangeEdit (newFile , []protocol.TextEdit {
166
165
{Range : protocol.Range {}, NewText : string (newFileContent )},
167
166
})), nil
168
167
}
169
168
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
184
170
// 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 ) {
186
172
basename := strings .ToLower (firstSymbol )
187
173
newPath := protocol .URIFromPath (filepath .Join (dir , basename + ".go" ))
188
174
for count := 1 ; count < 5 ; count ++ {
@@ -248,6 +234,7 @@ func selectedToplevelDecls(pgf *parsego.File, start, end token.Pos) (token.Pos,
248
234
return 0 , 0 , "" , false
249
235
}
250
236
if id != nil && firstName == "" {
237
+ // may be "_"
251
238
firstName = id .Name
252
239
}
253
240
// extends selection to docs comments
@@ -277,23 +264,21 @@ func selectedToplevelDecls(pgf *parsego.File, start, end token.Pos) (token.Pos,
277
264
return start , end , firstName , true
278
265
}
279
266
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 {
281
270
decls := make (map [* ast.ImportSpec ]* ast.GenDecl )
282
271
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
289
274
}
290
275
}
291
276
return decls
292
277
}
293
278
294
279
// removeNode returns a TextEdit that removes the node.
295
280
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 )
297
282
if err != nil {
298
283
bug .Reportf ("removeNode: %v" , err )
299
284
}
0 commit comments