From 4d18923f060e1e78b64cc09f3d2aaa73d669e565 Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Fri, 16 Sep 2022 17:18:39 -0400 Subject: [PATCH] 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 TryBot-Result: Gopher Robot Reviewed-by: Dylan Le Run-TryBot: Robert Findley --- gopls/internal/lsp/fake/edit.go | 37 ++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/gopls/internal/lsp/fake/edit.go b/gopls/internal/lsp/fake/edit.go index b45f23f9e8..7c833383d0 100644 --- a/gopls/internal/lsp/fake/edit.go +++ b/gopls/internal/lsp/fake/edit.go @@ -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 +}