@@ -62,6 +62,7 @@ import (
62
62
63
63
"golang.org/x/mod/modfile"
64
64
"golang.org/x/tools/go/ast/astutil"
65
+ "golang.org/x/tools/go/ast/inspector"
65
66
"golang.org/x/tools/go/types/objectpath"
66
67
"golang.org/x/tools/go/types/typeutil"
67
68
"golang.org/x/tools/gopls/internal/cache"
@@ -135,17 +136,34 @@ func PrepareRename(ctx context.Context, snapshot *cache.Snapshot, f file.Handle,
135
136
return nil , nil , err
136
137
}
137
138
139
+ cur , ok := pgf .Cursor .FindByPos (pos , pos )
140
+ if ! ok {
141
+ return nil , nil , fmt .Errorf ("can't find cursor for selection" )
142
+ }
143
+
138
144
// Check if we're in a 'func' keyword. If so, we hijack the renaming to
139
145
// change the function signature.
140
- if item , err := prepareRenameFuncSignature (pgf , pos ); err != nil {
146
+ if item , err := prepareRenameFuncSignature (pgf , pos , cur ); err != nil {
141
147
return nil , nil , err
142
148
} else if item != nil {
143
149
return item , nil , nil
144
150
}
145
151
146
152
targets , node , err := objectsAt (pkg .TypesInfo (), pgf .File , pos )
147
153
if err != nil {
148
- return nil , nil , err
154
+ // Check if we are renaming an ident inside its doc comment. The call to
155
+ // objectsAt will have returned an error in this case.
156
+ id := docCommentPosToIdent (pgf , pos , cur )
157
+ if id == nil {
158
+ return nil , nil , err
159
+ }
160
+ obj := pkg .TypesInfo ().Defs [id ]
161
+ if obj == nil {
162
+ return nil , nil , fmt .Errorf ("error fetching Object for ident %q" , id .Name )
163
+ }
164
+ // Change rename target to the ident.
165
+ targets = map [types.Object ]ast.Node {obj : id }
166
+ node = id
149
167
}
150
168
var obj types.Object
151
169
for obj = range targets {
@@ -209,8 +227,8 @@ func prepareRenamePackageName(ctx context.Context, snapshot *cache.Snapshot, pgf
209
227
//
210
228
// The resulting text is the signature of the function, which may be edited to
211
229
// the new signature.
212
- func prepareRenameFuncSignature (pgf * parsego.File , pos token.Pos ) (* PrepareItem , error ) {
213
- fdecl := funcKeywordDecl (pgf , pos )
230
+ func prepareRenameFuncSignature (pgf * parsego.File , pos token.Pos , cursor inspector. Cursor ) (* PrepareItem , error ) {
231
+ fdecl := funcKeywordDecl (pos , cursor )
214
232
if fdecl == nil {
215
233
return nil , nil
216
234
}
@@ -264,17 +282,8 @@ func nameBlankParams(ftype *ast.FuncType) *ast.FuncType {
264
282
265
283
// renameFuncSignature computes and applies the effective change signature
266
284
// operation resulting from a 'renamed' (=rewritten) signature.
267
- func renameFuncSignature (ctx context.Context , snapshot * cache.Snapshot , f file.Handle , pp protocol.Position , newName string ) (map [protocol.DocumentURI ][]protocol.TextEdit , error ) {
268
- // Find the renamed signature.
269
- pkg , pgf , err := NarrowestPackageForFile (ctx , snapshot , f .URI ())
270
- if err != nil {
271
- return nil , err
272
- }
273
- pos , err := pgf .PositionPos (pp )
274
- if err != nil {
275
- return nil , err
276
- }
277
- fdecl := funcKeywordDecl (pgf , pos )
285
+ func renameFuncSignature (ctx context.Context , pkg * cache.Package , pgf * parsego.File , pos token.Pos , snapshot * cache.Snapshot , cursor inspector.Cursor , f file.Handle , pp protocol.Position , newName string ) (map [protocol.DocumentURI ][]protocol.TextEdit , error ) {
286
+ fdecl := funcKeywordDecl (pos , cursor )
278
287
if fdecl == nil {
279
288
return nil , nil
280
289
}
@@ -350,15 +359,12 @@ func renameFuncSignature(ctx context.Context, snapshot *cache.Snapshot, f file.H
350
359
351
360
// funcKeywordDecl returns the FuncDecl for which pos is in the 'func' keyword,
352
361
// if any.
353
- func funcKeywordDecl (pgf * parsego.File , pos token.Pos ) * ast.FuncDecl {
354
- path , _ := astutil .PathEnclosingInterval (pgf .File , pos , pos )
355
- if len (path ) < 1 {
356
- return nil
357
- }
358
- fdecl , _ := path [0 ].(* ast.FuncDecl )
359
- if fdecl == nil {
362
+ func funcKeywordDecl (pos token.Pos , cursor inspector.Cursor ) * ast.FuncDecl {
363
+ curDecl , ok := moreiters .First (cursor .Enclosing ((* ast .FuncDecl )(nil )))
364
+ if ! ok {
360
365
return nil
361
366
}
367
+ fdecl := curDecl .Node ().(* ast.FuncDecl )
362
368
ftyp := fdecl .Type
363
369
if pos < ftyp .Func || pos > ftyp .Func + token .Pos (len ("func" )) { // tolerate renaming immediately after 'func'
364
370
return nil
@@ -396,7 +402,21 @@ func Rename(ctx context.Context, snapshot *cache.Snapshot, f file.Handle, pp pro
396
402
ctx , done := event .Start (ctx , "golang.Rename" )
397
403
defer done ()
398
404
399
- if edits , err := renameFuncSignature (ctx , snapshot , f , pp , newName ); err != nil {
405
+ pkg , pgf , err := NarrowestPackageForFile (ctx , snapshot , f .URI ())
406
+ if err != nil {
407
+ return nil , false , err
408
+ }
409
+ pos , err := pgf .PositionPos (pp )
410
+ if err != nil {
411
+ return nil , false , err
412
+ }
413
+
414
+ cur , ok := pgf .Cursor .FindByPos (pos , pos )
415
+ if ! ok {
416
+ return nil , false , fmt .Errorf ("can't find cursor for selection" )
417
+ }
418
+
419
+ if edits , err := renameFuncSignature (ctx , pkg , pgf , pos , snapshot , cur , f , pp , newName ); err != nil {
400
420
return nil , false , err
401
421
} else if edits != nil {
402
422
return edits , false , nil
@@ -503,7 +523,18 @@ func renameOrdinary(ctx context.Context, snapshot *cache.Snapshot, uri protocol.
503
523
}
504
524
targets , node , err := objectsAt (pkg .TypesInfo (), pgf .File , pos )
505
525
if err != nil {
506
- return nil , err
526
+ // Check if we are renaming an ident inside its doc comment. The call to
527
+ // objectsAt will have returned an error in this case.
528
+ id := docCommentPosToIdent (pgf , pos , cur )
529
+ if id == nil {
530
+ return nil , err
531
+ }
532
+ obj := pkg .TypesInfo ().Defs [id ]
533
+ if obj == nil {
534
+ return nil , fmt .Errorf ("error fetching types.Object for ident %q" , id .Name )
535
+ }
536
+ // Change rename target to the ident.
537
+ targets = map [types.Object ]ast.Node {obj : id }
507
538
}
508
539
509
540
// Pick a representative object arbitrarily.
@@ -1633,6 +1664,41 @@ func docComment(pgf *parsego.File, id *ast.Ident) *ast.CommentGroup {
1633
1664
return nil
1634
1665
}
1635
1666
1667
+ // docCommentPosToIdent returns the node whose doc comment contains pos, if any.
1668
+ // The pos must be within an occurrence of the identifier's name, otherwise it returns nil.
1669
+ func docCommentPosToIdent (pgf * parsego.File , pos token.Pos , cur inspector.Cursor ) * ast.Ident {
1670
+ for curId := range cur .Preorder ((* ast .Ident )(nil )) {
1671
+ id := curId .Node ().(* ast.Ident )
1672
+ if pos > id .Pos () {
1673
+ continue // Doc comments are not located after an ident.
1674
+ }
1675
+ doc := docComment (pgf , id )
1676
+ if doc == nil || ! (doc .Pos () <= pos && pos < doc .End ()) {
1677
+ continue
1678
+ }
1679
+
1680
+ docRegexp := regexp .MustCompile (`\b` + id .Name + `\b` )
1681
+ for _ , comment := range doc .List {
1682
+ if isDirective (comment .Text ) || ! (comment .Pos () <= pos && pos < comment .End ()) {
1683
+ continue
1684
+ }
1685
+ start := comment .Pos ()
1686
+ text , err := pgf .NodeText (comment )
1687
+ if err != nil {
1688
+ return nil
1689
+ }
1690
+ for _ , locs := range docRegexp .FindAllIndex (text , - 1 ) {
1691
+ matchStart := start + token .Pos (locs [0 ])
1692
+ matchEnd := start + token .Pos (locs [1 ])
1693
+ if matchStart <= pos && pos <= matchEnd {
1694
+ return id
1695
+ }
1696
+ }
1697
+ }
1698
+ }
1699
+ return nil
1700
+ }
1701
+
1636
1702
// updatePkgName returns the updates to rename a pkgName in the import spec by
1637
1703
// only modifying the package name portion of the import declaration.
1638
1704
func (r * renamer ) updatePkgName (pgf * parsego.File , pkgName * types.PkgName ) (diff.Edit , error ) {
0 commit comments