internal/lsp: clean up ApplyEdits
This should be a faster but equivalent implementation.
Change-Id: I7bc756644c601b953ba7715e093bfa10ca5ea97b
Reviewed-on: https://go-review.googlesource.com/c/tools/+/198878
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/diff/diff.go b/internal/lsp/diff/diff.go
index eb4fa6d..a3560e8 100644
--- a/internal/lsp/diff/diff.go
+++ b/internal/lsp/diff/diff.go
@@ -6,8 +6,8 @@
package diff
import (
- "bytes"
"sort"
+ "strings"
"golang.org/x/tools/internal/span"
)
@@ -41,44 +41,33 @@
// 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
+ if len(edits) == 0 {
+ return before
}
-
- // 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
+ edits = prepareEdits(edits)
+ c := span.NewContentConverter("", []byte(before))
+ after := strings.Builder{}
+ last := 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())
+ spn, _ := edit.Span.WithAll(c)
+ start := spn.Start().Offset()
+ if start > last {
+ after.WriteString(before[last:start])
+ last = start
}
- // offset(edit.Span.Start) is now equal to beforeOffset
after.WriteString(edit.NewText)
- beforeOffset += offset(edit.Span.End()) - offset(edit.Span.Start())
+ last = spn.End().Offset()
}
- if beforeOffset < len(before) {
- after.WriteString(before[beforeOffset:])
- beforeOffset = len(before[beforeOffset:]) // just to preserve invariants
+ if last < len(before) {
+ after.WriteString(before[last:])
}
return after.String()
}
+
+// prepareEdits returns a sorted copy of the edits
+func prepareEdits(edits []TextEdit) []TextEdit {
+ copied := make([]TextEdit, len(edits))
+ copy(copied, edits)
+ SortTextEdits(copied)
+ return copied
+}