github.com/v2fly/tools@v0.100.0/internal/lsp/source/fix.go (about) 1 // Copyright 2020 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package source 6 7 import ( 8 "context" 9 "fmt" 10 "go/ast" 11 "go/token" 12 "go/types" 13 14 "github.com/v2fly/tools/go/analysis" 15 "github.com/v2fly/tools/internal/lsp/analysis/fillstruct" 16 "github.com/v2fly/tools/internal/lsp/analysis/undeclaredname" 17 "github.com/v2fly/tools/internal/lsp/protocol" 18 "github.com/v2fly/tools/internal/span" 19 errors "golang.org/x/xerrors" 20 ) 21 22 // SuggestedFixFunc is a function used to get the suggested fixes for a given 23 // gopls command, some of which are provided by go/analysis.Analyzers. Some of 24 // the analyzers in internal/lsp/analysis are not efficient enough to include 25 // suggested fixes with their diagnostics, so we have to compute them 26 // separately. Such analyzers should provide a function with a signature of 27 // SuggestedFixFunc. 28 type SuggestedFixFunc func(fset *token.FileSet, rng span.Range, src []byte, file *ast.File, pkg *types.Package, info *types.Info) (*analysis.SuggestedFix, error) 29 30 const ( 31 FillStruct = "fill_struct" 32 UndeclaredName = "undeclared_name" 33 ExtractVariable = "extract_variable" 34 ExtractFunction = "extract_function" 35 ) 36 37 // suggestedFixes maps a suggested fix command id to its handler. 38 var suggestedFixes = map[string]SuggestedFixFunc{ 39 FillStruct: fillstruct.SuggestedFix, 40 UndeclaredName: undeclaredname.SuggestedFix, 41 ExtractVariable: extractVariable, 42 ExtractFunction: extractFunction, 43 } 44 45 func SuggestedFixFromCommand(cmd protocol.Command, kind protocol.CodeActionKind) SuggestedFix { 46 return SuggestedFix{ 47 Title: cmd.Title, 48 Command: &cmd, 49 ActionKind: kind, 50 } 51 } 52 53 // ApplyFix applies the command's suggested fix to the given file and 54 // range, returning the resulting edits. 55 func ApplyFix(ctx context.Context, fix string, snapshot Snapshot, fh VersionedFileHandle, pRng protocol.Range) ([]protocol.TextDocumentEdit, error) { 56 handler, ok := suggestedFixes[fix] 57 if !ok { 58 return nil, fmt.Errorf("no suggested fix function for %s", fix) 59 } 60 fset, rng, src, file, m, pkg, info, err := getAllSuggestedFixInputs(ctx, snapshot, fh, pRng) 61 if err != nil { 62 return nil, err 63 } 64 suggestion, err := handler(fset, rng, src, file, pkg, info) 65 if err != nil { 66 return nil, err 67 } 68 if suggestion == nil { 69 return nil, nil 70 } 71 72 var edits []protocol.TextDocumentEdit 73 for _, edit := range suggestion.TextEdits { 74 rng := span.NewRange(fset, edit.Pos, edit.End) 75 spn, err := rng.Span() 76 if err != nil { 77 return nil, err 78 } 79 clRng, err := m.Range(spn) 80 if err != nil { 81 return nil, err 82 } 83 edits = append(edits, protocol.TextDocumentEdit{ 84 TextDocument: protocol.OptionalVersionedTextDocumentIdentifier{ 85 Version: fh.Version(), 86 TextDocumentIdentifier: protocol.TextDocumentIdentifier{ 87 URI: protocol.URIFromSpanURI(fh.URI()), 88 }, 89 }, 90 Edits: []protocol.TextEdit{ 91 { 92 Range: clRng, 93 NewText: string(edit.NewText), 94 }, 95 }, 96 }) 97 } 98 return edits, nil 99 } 100 101 // getAllSuggestedFixInputs is a helper function to collect all possible needed 102 // inputs for an AppliesFunc or SuggestedFixFunc. 103 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) { 104 pkg, pgf, err := GetParsedFile(ctx, snapshot, fh, NarrowestPackage) 105 if err != nil { 106 return nil, span.Range{}, nil, nil, nil, nil, nil, errors.Errorf("getting file for Identifier: %w", err) 107 } 108 rng, err := pgf.Mapper.RangeToSpanRange(pRng) 109 if err != nil { 110 return nil, span.Range{}, nil, nil, nil, nil, nil, err 111 } 112 return snapshot.FileSet(), rng, pgf.Src, pgf.File, pgf.Mapper, pkg.GetTypes(), pkg.GetTypesInfo(), nil 113 }