internal/lsp/source: update SuggestedFixFunc to accept source.Snapshot
As part of the work for implementing method-stub code generation,
this CL updates the function signature of source/command.go's SuggestedFixFunc
so that a command can operate on the entire source.Snapshot to analyze
and change multiple packages and their files.
Updates golang/go#37537
Change-Id: I8430b2189ce7d91d37ab991f87ba368245293e56
Reviewed-on: https://go-review.googlesource.com/c/tools/+/279412
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
Trust: Rebecca Stambler <rstambler@golang.org>
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
diff --git a/internal/lsp/source/fix.go b/internal/lsp/source/fix.go
index 4cf270f..e0046ee 100644
--- a/internal/lsp/source/fix.go
+++ b/internal/lsp/source/fix.go
@@ -19,13 +19,16 @@
errors "golang.org/x/xerrors"
)
-// SuggestedFixFunc is a function used to get the suggested fixes for a given
-// gopls command, some of which are provided by go/analysis.Analyzers. Some of
-// the analyzers in internal/lsp/analysis are not efficient enough to include
-// suggested fixes with their diagnostics, so we have to compute them
-// separately. Such analyzers should provide a function with a signature of
-// SuggestedFixFunc.
-type SuggestedFixFunc func(fset *token.FileSet, rng span.Range, src []byte, file *ast.File, pkg *types.Package, info *types.Info) (*analysis.SuggestedFix, error)
+type (
+ // SuggestedFixFunc is a function used to get the suggested fixes for a given
+ // gopls command, some of which are provided by go/analysis.Analyzers. Some of
+ // the analyzers in internal/lsp/analysis are not efficient enough to include
+ // suggested fixes with their diagnostics, so we have to compute them
+ // separately. Such analyzers should provide a function with a signature of
+ // SuggestedFixFunc.
+ SuggestedFixFunc func(ctx context.Context, snapshot Snapshot, fh VersionedFileHandle, pRng protocol.Range) (*analysis.SuggestedFix, error)
+ singleFileFixFunc func(fset *token.FileSet, rng span.Range, src []byte, file *ast.File, pkg *types.Package, info *types.Info) (*analysis.SuggestedFix, error)
+)
const (
FillStruct = "fill_struct"
@@ -37,11 +40,22 @@
// suggestedFixes maps a suggested fix command id to its handler.
var suggestedFixes = map[string]SuggestedFixFunc{
- FillStruct: fillstruct.SuggestedFix,
- UndeclaredName: undeclaredname.SuggestedFix,
- ExtractVariable: extractVariable,
- ExtractFunction: extractFunction,
- ExtractMethod: extractMethod,
+ FillStruct: singleFile(fillstruct.SuggestedFix),
+ UndeclaredName: singleFile(undeclaredname.SuggestedFix),
+ ExtractVariable: singleFile(extractVariable),
+ ExtractFunction: singleFile(extractFunction),
+ ExtractMethod: singleFile(extractMethod),
+}
+
+// singleFile calls analyzers that expect inputs for a single file
+func singleFile(sf singleFileFixFunc) SuggestedFixFunc {
+ return func(ctx context.Context, snapshot Snapshot, fh VersionedFileHandle, pRng protocol.Range) (*analysis.SuggestedFix, error) {
+ fset, rng, src, file, pkg, info, err := getAllSuggestedFixInputs(ctx, snapshot, fh, pRng)
+ if err != nil {
+ return nil, err
+ }
+ return sf(fset, rng, src, file, pkg, info)
+ }
}
func SuggestedFixFromCommand(cmd protocol.Command, kind protocol.CodeActionKind) SuggestedFix {
@@ -59,55 +73,66 @@
if !ok {
return nil, fmt.Errorf("no suggested fix function for %s", fix)
}
- fset, rng, src, file, m, pkg, info, err := getAllSuggestedFixInputs(ctx, snapshot, fh, pRng)
- if err != nil {
- return nil, err
- }
- suggestion, err := handler(fset, rng, src, file, pkg, info)
+ suggestion, err := handler(ctx, snapshot, fh, pRng)
if err != nil {
return nil, err
}
if suggestion == nil {
return nil, nil
}
-
- var edits []protocol.TextEdit
+ fset := snapshot.FileSet()
+ editsPerFile := map[span.URI]*protocol.TextDocumentEdit{}
for _, edit := range suggestion.TextEdits {
- rng := span.NewRange(fset, edit.Pos, edit.End)
- spn, err := rng.Span()
+ spn, err := span.NewRange(fset, edit.Pos, edit.End).Span()
if err != nil {
return nil, err
}
- clRng, err := m.Range(spn)
+ fh, err := snapshot.GetVersionedFile(ctx, spn.URI())
if err != nil {
return nil, err
}
- edits = append(edits, protocol.TextEdit{
- Range: clRng,
+ te, ok := editsPerFile[spn.URI()]
+ if !ok {
+ te = &protocol.TextDocumentEdit{
+ TextDocument: protocol.OptionalVersionedTextDocumentIdentifier{
+ Version: fh.Version(),
+ TextDocumentIdentifier: protocol.TextDocumentIdentifier{
+ URI: protocol.URIFromSpanURI(fh.URI()),
+ },
+ },
+ }
+ editsPerFile[spn.URI()] = te
+ }
+ _, pgf, err := GetParsedFile(ctx, snapshot, fh, NarrowestPackage)
+ if err != nil {
+ return nil, err
+ }
+ rng, err := pgf.Mapper.Range(spn)
+ if err != nil {
+ return nil, err
+ }
+ te.Edits = append(te.Edits, protocol.TextEdit{
+ Range: rng,
NewText: string(edit.NewText),
})
}
- return []protocol.TextDocumentEdit{{
- TextDocument: protocol.OptionalVersionedTextDocumentIdentifier{
- Version: fh.Version(),
- TextDocumentIdentifier: protocol.TextDocumentIdentifier{
- URI: protocol.URIFromSpanURI(fh.URI()),
- },
- },
- Edits: edits,
- }}, nil
+ var edits []protocol.TextDocumentEdit
+ for _, edit := range editsPerFile {
+ edits = append(edits, *edit)
+ }
+ return edits, nil
}
// getAllSuggestedFixInputs is a helper function to collect all possible needed
// inputs for an AppliesFunc or SuggestedFixFunc.
-func getAllSuggestedFixInputs(ctx context.Context, snapshot Snapshot, fh FileHandle, pRng protocol.Range) (*token.FileSet, span.Range, []byte, *ast.File, *protocol.ColumnMapper, *types.Package, *types.Info, error) {
+func getAllSuggestedFixInputs(ctx context.Context, snapshot Snapshot, fh FileHandle, pRng protocol.Range) (*token.FileSet, span.Range, []byte, *ast.File, *types.Package, *types.Info, error) {
pkg, pgf, err := GetParsedFile(ctx, snapshot, fh, NarrowestPackage)
if err != nil {
- return nil, span.Range{}, nil, nil, nil, nil, nil, errors.Errorf("getting file for Identifier: %w", err)
+ return nil, span.Range{}, nil, nil, nil, nil, errors.Errorf("getting file for Identifier: %w", err)
}
rng, err := pgf.Mapper.RangeToSpanRange(pRng)
if err != nil {
- return nil, span.Range{}, nil, nil, nil, nil, nil, err
+ return nil, span.Range{}, nil, nil, nil, nil, err
}
- return snapshot.FileSet(), rng, pgf.Src, pgf.File, pgf.Mapper, pkg.GetTypes(), pkg.GetTypesInfo(), nil
+ return snapshot.FileSet(), rng, pgf.Src, pgf.File, pkg.GetTypes(), pkg.GetTypesInfo(), nil
}