From c9f9432ec4b21a28c4d47f172513698febb68e9c Mon Sep 17 00:00:00 2001 From: Ian Cottrell Date: Thu, 3 Oct 2019 14:44:07 -0400 Subject: [PATCH] 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 TryBot-Result: Gobot Gobot Reviewed-by: Rebecca Stambler --- internal/lsp/diff/diff.go | 57 ++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/internal/lsp/diff/diff.go b/internal/lsp/diff/diff.go index eb4fa6d8c5..a3560e886e 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 @@ 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 + 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 +}