mirror of
https://github.com/golang/go.git
synced 2025-05-05 23:53:05 +00:00
internal/lsp: cleanup the diff/myers package
The only exposed symbol is now the ComputeEdits function, all other functionality is now moved up to the diff layer and using edits instead of operations. Change-Id: I149e4f3276592e1a7c2c52e6eaffc826cc22a9fa Reviewed-on: https://go-review.googlesource.com/c/tools/+/198518 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
cc9bfb3eec
commit
88641d98b3
@ -9,47 +9,42 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/tools/internal/lsp/diff"
|
"golang.org/x/tools/internal/lsp/diff"
|
||||||
|
"golang.org/x/tools/internal/span"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Sources:
|
// Sources:
|
||||||
// https://blog.jcoglan.com/2017/02/17/the-myers-diff-algorithm-part-3/
|
// https://blog.jcoglan.com/2017/02/17/the-myers-diff-algorithm-part-3/
|
||||||
// https://www.codeproject.com/Articles/42279/%2FArticles%2F42279%2FInvestigating-Myers-diff-algorithm-Part-1-of-2
|
// https://www.codeproject.com/Articles/42279/%2FArticles%2F42279%2FInvestigating-Myers-diff-algorithm-Part-1-of-2
|
||||||
|
|
||||||
type Op struct {
|
func ComputeEdits(uri span.URI, before, after string) []diff.TextEdit {
|
||||||
|
ops := operations(splitLines(before), splitLines(after))
|
||||||
|
edits := make([]diff.TextEdit, 0, len(ops))
|
||||||
|
for _, op := range ops {
|
||||||
|
s := span.New(uri, span.NewPoint(op.I1+1, 1, 0), span.NewPoint(op.I2+1, 1, 0))
|
||||||
|
switch op.Kind {
|
||||||
|
case diff.Delete:
|
||||||
|
// Delete: unformatted[i1:i2] is deleted.
|
||||||
|
edits = append(edits, diff.TextEdit{Span: s})
|
||||||
|
case diff.Insert:
|
||||||
|
// Insert: formatted[j1:j2] is inserted at unformatted[i1:i1].
|
||||||
|
if content := strings.Join(op.Content, ""); content != "" {
|
||||||
|
edits = append(edits, diff.TextEdit{Span: s, NewText: content})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return edits
|
||||||
|
}
|
||||||
|
|
||||||
|
type operation struct {
|
||||||
Kind diff.OpKind
|
Kind diff.OpKind
|
||||||
Content []string // content from b
|
Content []string // content from b
|
||||||
I1, I2 int // indices of the line in a
|
I1, I2 int // indices of the line in a
|
||||||
J1 int // indices of the line in b, J2 implied by len(Content)
|
J1 int // indices of the line in b, J2 implied by len(Content)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ApplyEdits(a []string, operations []*Op) []string {
|
// operations returns the list of operations to convert a into b, consolidating
|
||||||
var b []string
|
|
||||||
var prevI2 int
|
|
||||||
for _, op := range operations {
|
|
||||||
// catch up to latest indices
|
|
||||||
if op.I1-prevI2 > 0 {
|
|
||||||
for _, c := range a[prevI2:op.I1] {
|
|
||||||
b = append(b, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch op.Kind {
|
|
||||||
case diff.Equal, diff.Insert:
|
|
||||||
b = append(b, op.Content...)
|
|
||||||
}
|
|
||||||
prevI2 = op.I2
|
|
||||||
}
|
|
||||||
// final catch up
|
|
||||||
if len(a)-prevI2 > 0 {
|
|
||||||
for _, c := range a[prevI2:len(a)] {
|
|
||||||
b = append(b, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// Operations returns the list of operations to convert a into b, consolidating
|
|
||||||
// operations for multiple lines and not including equal lines.
|
// operations for multiple lines and not including equal lines.
|
||||||
func Operations(a, b []string) []*Op {
|
func operations(a, b []string) []*operation {
|
||||||
if len(a) == 0 && len(b) == 0 {
|
if len(a) == 0 && len(b) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -60,9 +55,9 @@ func Operations(a, b []string) []*Op {
|
|||||||
M, N := len(a), len(b)
|
M, N := len(a), len(b)
|
||||||
|
|
||||||
var i int
|
var i int
|
||||||
solution := make([]*Op, len(a)+len(b))
|
solution := make([]*operation, len(a)+len(b))
|
||||||
|
|
||||||
add := func(op *Op, i2, j2 int) {
|
add := func(op *operation, i2, j2 int) {
|
||||||
if op == nil {
|
if op == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -78,11 +73,11 @@ func Operations(a, b []string) []*Op {
|
|||||||
if len(snake) < 2 {
|
if len(snake) < 2 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var op *Op
|
var op *operation
|
||||||
// delete (horizontal)
|
// delete (horizontal)
|
||||||
for snake[0]-snake[1] > x-y {
|
for snake[0]-snake[1] > x-y {
|
||||||
if op == nil {
|
if op == nil {
|
||||||
op = &Op{
|
op = &operation{
|
||||||
Kind: diff.Delete,
|
Kind: diff.Delete,
|
||||||
I1: x,
|
I1: x,
|
||||||
J1: y,
|
J1: y,
|
||||||
@ -98,7 +93,7 @@ func Operations(a, b []string) []*Op {
|
|||||||
// insert (vertical)
|
// insert (vertical)
|
||||||
for snake[0]-snake[1] < x-y {
|
for snake[0]-snake[1] < x-y {
|
||||||
if op == nil {
|
if op == nil {
|
||||||
op = &Op{
|
op = &operation{
|
||||||
Kind: diff.Insert,
|
Kind: diff.Insert,
|
||||||
I1: x,
|
I1: x,
|
||||||
J1: y,
|
J1: y,
|
||||||
@ -201,7 +196,7 @@ func shortestEditSequence(a, b []string) ([][]int, int) {
|
|||||||
return nil, 0
|
return nil, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func SplitLines(text string) []string {
|
func splitLines(text string) []string {
|
||||||
lines := strings.SplitAfter(text, "\n")
|
lines := strings.SplitAfter(text, "\n")
|
||||||
if lines[len(lines)-1] == "" {
|
if lines[len(lines)-1] == "" {
|
||||||
lines = lines[:len(lines)-1]
|
lines = lines[:len(lines)-1]
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
// Copyright 2019 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package myers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/tools/internal/lsp/diff"
|
|
||||||
"golang.org/x/tools/internal/span"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ComputeEdits(uri span.URI, before, after string) []diff.TextEdit {
|
|
||||||
u := SplitLines(before)
|
|
||||||
f := SplitLines(after)
|
|
||||||
return myersDiffToEdits(uri, Operations(u, f))
|
|
||||||
}
|
|
||||||
|
|
||||||
func myersDiffToEdits(uri span.URI, ops []*Op) []diff.TextEdit {
|
|
||||||
edits := make([]diff.TextEdit, 0, len(ops))
|
|
||||||
for _, op := range ops {
|
|
||||||
s := span.New(uri, span.NewPoint(op.I1+1, 1, 0), span.NewPoint(op.I2+1, 1, 0))
|
|
||||||
switch op.Kind {
|
|
||||||
case diff.Delete:
|
|
||||||
// Delete: unformatted[i1:i2] is deleted.
|
|
||||||
edits = append(edits, diff.TextEdit{Span: s})
|
|
||||||
case diff.Insert:
|
|
||||||
// Insert: formatted[j1:j2] is inserted at unformatted[i1:i1].
|
|
||||||
if content := strings.Join(op.Content, ""); content != "" {
|
|
||||||
edits = append(edits, diff.TextEdit{Span: s, NewText: content})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return edits
|
|
||||||
}
|
|
||||||
|
|
||||||
func myersEditsToDiff(edits []diff.TextEdit) []*Op {
|
|
||||||
iToJ := 0
|
|
||||||
ops := make([]*Op, len(edits))
|
|
||||||
for i, edit := range edits {
|
|
||||||
i1 := edit.Span.Start().Line() - 1
|
|
||||||
i2 := edit.Span.End().Line() - 1
|
|
||||||
kind := diff.Insert
|
|
||||||
if edit.NewText == "" {
|
|
||||||
kind = diff.Delete
|
|
||||||
}
|
|
||||||
ops[i] = &Op{
|
|
||||||
Kind: kind,
|
|
||||||
Content: SplitLines(edit.NewText),
|
|
||||||
I1: i1,
|
|
||||||
I2: i2,
|
|
||||||
J1: i1 + iToJ,
|
|
||||||
}
|
|
||||||
if kind == diff.Insert {
|
|
||||||
iToJ += len(ops[i].Content)
|
|
||||||
} else {
|
|
||||||
iToJ -= i2 - i1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ops
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user