golang.org/x/tools/gopls@v0.15.3/internal/protocol/edits.go (about) 1 // Copyright 2023 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 protocol 6 7 import ( 8 "fmt" 9 10 "golang.org/x/tools/internal/diff" 11 ) 12 13 // EditsFromDiffEdits converts diff.Edits to a non-nil slice of LSP TextEdits. 14 // See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textEditArray 15 func EditsFromDiffEdits(m *Mapper, edits []diff.Edit) ([]TextEdit, error) { 16 // LSP doesn't require TextEditArray to be sorted: 17 // this is the receiver's concern. But govim, and perhaps 18 // other clients have historically relied on the order. 19 edits = append([]diff.Edit(nil), edits...) 20 diff.SortEdits(edits) 21 22 result := make([]TextEdit, len(edits)) 23 for i, edit := range edits { 24 rng, err := m.OffsetRange(edit.Start, edit.End) 25 if err != nil { 26 return nil, err 27 } 28 result[i] = TextEdit{ 29 Range: rng, 30 NewText: edit.New, 31 } 32 } 33 return result, nil 34 } 35 36 // EditsToDiffEdits converts LSP TextEdits to diff.Edits. 37 // See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textEditArray 38 func EditsToDiffEdits(m *Mapper, edits []TextEdit) ([]diff.Edit, error) { 39 if edits == nil { 40 return nil, nil 41 } 42 result := make([]diff.Edit, len(edits)) 43 for i, edit := range edits { 44 start, end, err := m.RangeOffsets(edit.Range) 45 if err != nil { 46 return nil, err 47 } 48 result[i] = diff.Edit{ 49 Start: start, 50 End: end, 51 New: edit.NewText, 52 } 53 } 54 return result, nil 55 } 56 57 // ApplyEdits applies the patch (edits) to m.Content and returns the result. 58 // It also returns the edits converted to diff-package form. 59 func ApplyEdits(m *Mapper, edits []TextEdit) ([]byte, []diff.Edit, error) { 60 diffEdits, err := EditsToDiffEdits(m, edits) 61 if err != nil { 62 return nil, nil, err 63 } 64 out, err := diff.ApplyBytes(m.Content, diffEdits) 65 return out, diffEdits, err 66 } 67 68 // AsTextEdits converts a slice possibly containing AnnotatedTextEdits 69 // to a slice of TextEdits. 70 func AsTextEdits(edits []Or_TextDocumentEdit_edits_Elem) []TextEdit { 71 var result []TextEdit 72 for _, e := range edits { 73 var te TextEdit 74 if x, ok := e.Value.(AnnotatedTextEdit); ok { 75 te = x.TextEdit 76 } else if x, ok := e.Value.(TextEdit); ok { 77 te = x 78 } else { 79 panic(fmt.Sprintf("unexpected type %T, expected AnnotatedTextEdit or TextEdit", e.Value)) 80 } 81 result = append(result, te) 82 } 83 return result 84 } 85 86 // AsAnnotatedTextEdits converts a slice of TextEdits 87 // to a slice of Or_TextDocumentEdit_edits_Elem. 88 // (returning a typed nil is required in server: in code_action.go and command.go)) 89 func AsAnnotatedTextEdits(edits []TextEdit) []Or_TextDocumentEdit_edits_Elem { 90 if edits == nil { 91 return []Or_TextDocumentEdit_edits_Elem{} 92 } 93 var result []Or_TextDocumentEdit_edits_Elem 94 for _, e := range edits { 95 result = append(result, Or_TextDocumentEdit_edits_Elem{ 96 Value: TextEdit{ 97 Range: e.Range, 98 NewText: e.NewText, 99 }, 100 }) 101 } 102 return result 103 } 104 105 // TextEditsToDocumentChanges converts a set of edits within the 106 // specified (versioned) file to a singleton list of DocumentChanges 107 // (as required for a WorkspaceEdit). 108 func TextEditsToDocumentChanges(uri DocumentURI, version int32, edits []TextEdit) []DocumentChanges { 109 return []DocumentChanges{{ 110 TextDocumentEdit: &TextDocumentEdit{ 111 TextDocument: OptionalVersionedTextDocumentIdentifier{ 112 Version: version, 113 TextDocumentIdentifier: TextDocumentIdentifier{URI: uri}, 114 }, 115 Edits: AsAnnotatedTextEdits(edits), 116 }, 117 }} 118 } 119 120 // TextDocumentEditsToDocumentChanges wraps each TextDocumentEdit in a DocumentChange. 121 func TextDocumentEditsToDocumentChanges(edits []TextDocumentEdit) []DocumentChanges { 122 changes := []DocumentChanges{} // non-nil 123 for _, edit := range edits { 124 edit := edit 125 changes = append(changes, DocumentChanges{TextDocumentEdit: &edit}) 126 } 127 return changes 128 }