mirror of
https://github.com/golang/go
synced 2024-11-05 14:56:10 -07:00
internal/lsp: cleanup the diff package
Make sure everything is documented and move things to sensible files now all the cross package shuffling is done Change-Id: I884053a207d6741cda066afa5da91b00f1dfd31c Reviewed-on: https://go-review.googlesource.com/c/tools/+/198877 Run-TryBot: Ian Cottrell <iancottrell@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
parent
43d3a2ca2a
commit
5bee6a6eb8
@ -1,54 +0,0 @@
|
||||
package diff
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sort"
|
||||
|
||||
"golang.org/x/tools/internal/span"
|
||||
)
|
||||
|
||||
func ApplyEdits(before string, edits []TextEdit) string {
|
||||
// Preconditions:
|
||||
// - all of the edits apply to before
|
||||
// - and all the spans for each TextEdit have the same URI
|
||||
|
||||
// copy edits so we don't make a mess of the caller's slice
|
||||
s := make([]TextEdit, len(edits))
|
||||
copy(s, edits)
|
||||
edits = s
|
||||
|
||||
// TODO(matloob): Initialize the Converter Once?
|
||||
var conv span.Converter = span.NewContentConverter("", []byte(before))
|
||||
offset := func(point span.Point) int {
|
||||
if point.HasOffset() {
|
||||
return point.Offset()
|
||||
}
|
||||
offset, err := conv.ToOffset(point.Line(), point.Column())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return offset
|
||||
}
|
||||
|
||||
// sort the copy
|
||||
sort.Slice(edits, func(i, j int) bool { return offset(edits[i].Span.Start()) < offset(edits[j].Span.Start()) })
|
||||
|
||||
var after bytes.Buffer
|
||||
beforeOffset := 0
|
||||
for _, edit := range edits {
|
||||
if offset(edit.Span.Start()) < beforeOffset {
|
||||
panic("overlapping edits") // TODO(matloob): ApplyEdits doesn't return an error. What do we do?
|
||||
} else if offset(edit.Span.Start()) > beforeOffset {
|
||||
after.WriteString(before[beforeOffset:offset(edit.Span.Start())])
|
||||
beforeOffset = offset(edit.Span.Start())
|
||||
}
|
||||
// offset(edit.Span.Start) is now equal to beforeOffset
|
||||
after.WriteString(edit.NewText)
|
||||
beforeOffset += offset(edit.Span.End()) - offset(edit.Span.Start())
|
||||
}
|
||||
if beforeOffset < len(before) {
|
||||
after.WriteString(before[beforeOffset:])
|
||||
beforeOffset = len(before[beforeOffset:]) // just to preserve invariants
|
||||
}
|
||||
return after.String()
|
||||
}
|
@ -6,6 +6,7 @@
|
||||
package diff
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sort"
|
||||
|
||||
"golang.org/x/tools/internal/span"
|
||||
@ -18,29 +19,10 @@ type TextEdit struct {
|
||||
NewText string
|
||||
}
|
||||
|
||||
// ComputeEdits is the type for a function that produces a set of edits that
|
||||
// convert from the before content to the after content.
|
||||
type ComputeEdits func(uri span.URI, before, after string) []TextEdit
|
||||
|
||||
type OpKind int
|
||||
|
||||
const (
|
||||
Delete OpKind = iota
|
||||
Insert
|
||||
Equal
|
||||
)
|
||||
|
||||
func (k OpKind) String() string {
|
||||
switch k {
|
||||
case Delete:
|
||||
return "delete"
|
||||
case Insert:
|
||||
return "insert"
|
||||
case Equal:
|
||||
return "equal"
|
||||
default:
|
||||
panic("unknown operation kind")
|
||||
}
|
||||
}
|
||||
|
||||
// SortTextEdits attempts to order all edits by their starting points.
|
||||
// The sort is stable so that edits with the same starting point will not
|
||||
// be reordered.
|
||||
@ -50,3 +32,53 @@ func SortTextEdits(d []TextEdit) {
|
||||
return span.Compare(d[i].Span, d[j].Span) < 0
|
||||
})
|
||||
}
|
||||
|
||||
// ApplyEdits applies the set of edits to the before and returns the resulting
|
||||
// content.
|
||||
// It may panic or produce garbage if the edits are not valid for the provided
|
||||
// before content.
|
||||
func ApplyEdits(before string, edits []TextEdit) string {
|
||||
// Preconditions:
|
||||
// - all of the edits apply to before
|
||||
// - and all the spans for each TextEdit have the same URI
|
||||
|
||||
// copy edits so we don't make a mess of the caller's slice
|
||||
s := make([]TextEdit, len(edits))
|
||||
copy(s, edits)
|
||||
edits = s
|
||||
|
||||
// TODO(matloob): Initialize the Converter Once?
|
||||
var conv span.Converter = span.NewContentConverter("", []byte(before))
|
||||
offset := func(point span.Point) int {
|
||||
if point.HasOffset() {
|
||||
return point.Offset()
|
||||
}
|
||||
offset, err := conv.ToOffset(point.Line(), point.Column())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return offset
|
||||
}
|
||||
|
||||
// sort the copy
|
||||
sort.Slice(edits, func(i, j int) bool { return offset(edits[i].Span.Start()) < offset(edits[j].Span.Start()) })
|
||||
|
||||
var after bytes.Buffer
|
||||
beforeOffset := 0
|
||||
for _, edit := range edits {
|
||||
if offset(edit.Span.Start()) < beforeOffset {
|
||||
panic("overlapping edits") // TODO(matloob): ApplyEdits doesn't return an error. What do we do?
|
||||
} else if offset(edit.Span.Start()) > beforeOffset {
|
||||
after.WriteString(before[beforeOffset:offset(edit.Span.Start())])
|
||||
beforeOffset = offset(edit.Span.Start())
|
||||
}
|
||||
// offset(edit.Span.Start) is now equal to beforeOffset
|
||||
after.WriteString(edit.NewText)
|
||||
beforeOffset += offset(edit.Span.End()) - offset(edit.Span.Start())
|
||||
}
|
||||
if beforeOffset < len(before) {
|
||||
after.WriteString(before[beforeOffset:])
|
||||
beforeOffset = len(before[beforeOffset:]) // just to preserve invariants
|
||||
}
|
||||
return after.String()
|
||||
}
|
||||
|
@ -11,27 +11,72 @@ import (
|
||||
"golang.org/x/tools/internal/span"
|
||||
)
|
||||
|
||||
// Unified represents a set of edits as a unified diff.
|
||||
type Unified struct {
|
||||
From, To string
|
||||
Hunks []*Hunk
|
||||
// From is the name of the original file.
|
||||
From string
|
||||
// To is the name of the modified file.
|
||||
To string
|
||||
// Hunks is the set of edit hunks needed to transform the file content.
|
||||
Hunks []*Hunk
|
||||
}
|
||||
|
||||
// Hunk represents a contiguous set of line edits to apply.
|
||||
type Hunk struct {
|
||||
// The line in the original source where the hunk starts.
|
||||
FromLine int
|
||||
ToLine int
|
||||
Lines []Line
|
||||
// The line in the original source where the hunk finishes.
|
||||
ToLine int
|
||||
// The set of line based edits to apply.
|
||||
Lines []Line
|
||||
}
|
||||
|
||||
// Line represents a single line operation to apply as part of a Hunk.
|
||||
type Line struct {
|
||||
Kind OpKind
|
||||
// Kind is the type of line this represents, deletion, insertion or copy.
|
||||
Kind OpKind
|
||||
// Content is the content of this line.
|
||||
// For deletion it is the line being removed, for all others it is the line
|
||||
// to put in the output.
|
||||
Content string
|
||||
}
|
||||
|
||||
// OpKind is used to denote the type of operation a line represents.
|
||||
type OpKind int
|
||||
|
||||
const (
|
||||
// Delete is the operation kind for a line that is present in the input
|
||||
// but not in the output.
|
||||
Delete OpKind = iota
|
||||
// Insert is the operation kind for a line that is new in the output.
|
||||
Insert
|
||||
// Equal is the operation kind for a line that is the same in the input and
|
||||
// output, often used to provide context around edited lines.
|
||||
Equal
|
||||
)
|
||||
|
||||
// String returns a human readable representation of an OpKind. It is not
|
||||
// intended for machine processing.
|
||||
func (k OpKind) String() string {
|
||||
switch k {
|
||||
case Delete:
|
||||
return "delete"
|
||||
case Insert:
|
||||
return "insert"
|
||||
case Equal:
|
||||
return "equal"
|
||||
default:
|
||||
panic("unknown operation kind")
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
edge = 3
|
||||
gap = edge * 2
|
||||
)
|
||||
|
||||
// ToUnified takes a file contents and a sequence of edits, and calculates
|
||||
// a unified diff that represents those edits.
|
||||
func ToUnified(from, to string, content string, edits []TextEdit) Unified {
|
||||
u := Unified{
|
||||
From: from,
|
||||
@ -121,6 +166,8 @@ func addEqualLines(h *Hunk, lines []string, start, end int) int {
|
||||
return delta
|
||||
}
|
||||
|
||||
// Format converts a unified diff to the standard textual form for that diff.
|
||||
// The output of this function can be passed to tools like patch.
|
||||
func (u Unified) Format(f fmt.State, r rune) {
|
||||
if len(u.Hunks) == 0 {
|
||||
return
|
||||
|
Loading…
Reference in New Issue
Block a user