gopls/internal/fake: sort edits by both start and end position

When processing edits, they must be non-overlapping but they may be
adjacent: we must allow for insertions adjacent to replacements, e.g. an
edit of 3:1-3:1 followed by an edit of 3:1-3:15. Achieve this in the
fake editor by sorting edits by end position after start position.

Longer term, we should eliminate this ad-hoc editing in favor of
diff.ApplyEdit.

Change-Id: I72a252952585d0a652d97287486aa61c167db485
Reviewed-on: https://go-review.googlesource.com/c/tools/+/431219
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Dylan Le <dungtuanle@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
This commit is contained in:
Robert Findley 2022-09-16 17:18:39 -04:00 committed by Dylan Le
parent 45ad958c90
commit 4d18923f06
1 changed files with 30 additions and 7 deletions

View File

@ -110,17 +110,23 @@ func inText(p Pos, content []string) bool {
// invalid for the current content.
//
// TODO(rfindley): this function does not handle non-ascii text correctly.
// TODO(rfindley): replace this with diff.ApplyEdits: we should not be
// maintaining an additional representation of edits.
func editContent(content []string, edits []Edit) ([]string, error) {
newEdits := make([]Edit, len(edits))
copy(newEdits, edits)
sort.Slice(newEdits, func(i, j int) bool {
if newEdits[i].Start.Line < newEdits[j].Start.Line {
return true
sort.SliceStable(newEdits, func(i, j int) bool {
ei := newEdits[i]
ej := newEdits[j]
// Sort by edit start position followed by end position. Given an edit
// 3:1-3:1 followed by an edit 3:1-3:15, we must process the empty edit
// first.
if cmp := comparePos(ei.Start, ej.Start); cmp != 0 {
return cmp < 0
}
if newEdits[i].Start.Line > newEdits[j].Start.Line {
return false
}
return newEdits[i].Start.Column < newEdits[j].Start.Column
return comparePos(ei.End, ej.End) < 0
})
// Validate edits.
@ -157,3 +163,20 @@ func editContent(content []string, edits []Edit) ([]string, error) {
advance(len(content)-1, len([]rune(content[len(content)-1])))
return strings.Split(b.String(), "\n"), nil
}
// comparePos returns -1 if left < right, 0 if left == right, and 1 if left > right.
func comparePos(left, right Pos) int {
if left.Line < right.Line {
return -1
}
if left.Line > right.Line {
return 1
}
if left.Column < right.Column {
return -1
}
if left.Column > right.Column {
return 1
}
return 0
}