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  }