mirror of
https://github.com/golang/go.git
synced 2025-05-05 23:53:05 +00: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
|
package diff
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"golang.org/x/tools/internal/span"
|
"golang.org/x/tools/internal/span"
|
||||||
@ -18,29 +19,10 @@ type TextEdit struct {
|
|||||||
NewText string
|
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 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.
|
// SortTextEdits attempts to order all edits by their starting points.
|
||||||
// The sort is stable so that edits with the same starting point will not
|
// The sort is stable so that edits with the same starting point will not
|
||||||
// be reordered.
|
// be reordered.
|
||||||
@ -50,3 +32,53 @@ func SortTextEdits(d []TextEdit) {
|
|||||||
return span.Compare(d[i].Span, d[j].Span) < 0
|
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"
|
"golang.org/x/tools/internal/span"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Unified represents a set of edits as a unified diff.
|
||||||
type Unified struct {
|
type Unified struct {
|
||||||
From, To string
|
// From is the name of the original file.
|
||||||
Hunks []*Hunk
|
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 {
|
type Hunk struct {
|
||||||
|
// The line in the original source where the hunk starts.
|
||||||
FromLine int
|
FromLine int
|
||||||
ToLine int
|
// The line in the original source where the hunk finishes.
|
||||||
Lines []Line
|
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 {
|
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
|
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 (
|
const (
|
||||||
edge = 3
|
edge = 3
|
||||||
gap = edge * 2
|
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 {
|
func ToUnified(from, to string, content string, edits []TextEdit) Unified {
|
||||||
u := Unified{
|
u := Unified{
|
||||||
From: from,
|
From: from,
|
||||||
@ -121,6 +166,8 @@ func addEqualLines(h *Hunk, lines []string, start, end int) int {
|
|||||||
return delta
|
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) {
|
func (u Unified) Format(f fmt.State, r rune) {
|
||||||
if len(u.Hunks) == 0 {
|
if len(u.Hunks) == 0 {
|
||||||
return
|
return
|
||||||
|
Loading…
x
Reference in New Issue
Block a user