internal/lsp: invert the diff dependencies so myers depends on diff

This makes it so the diff package is depended on by all implementations, rather
than the diff package having to depend on the default myers implementation.

Change-Id: I04b9caee6ff1017fa8e5476a7434e4b0e17753c3
Reviewed-on: https://go-review.googlesource.com/c/tools/+/198379
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Ian Cottrell 2019-10-01 18:08:04 -04:00 committed by Brad Fitzpatrick
parent 6e0078a899
commit b917058f11
7 changed files with 89 additions and 89 deletions

View File

@ -24,6 +24,30 @@ var (
ToUnified func(from, to string, before string, edits []TextEdit) string
)
type OpKind int
const (
Delete OpKind = iota
Insert
Equal
)
func (k OpKind) String() string {
switch k {
case Delete:
return "delete"
case Insert:
return "insert"
case Equal:
return "equal"
default:
panic("unknown operation kind")
}
}
// SortTextEdits attempts to order all edits by their starting points.
// The sort is stable so that edits with the same starting point will not
// be reordered.
func SortTextEdits(d []TextEdit) {
// Use a stable sort to maintain the order of edits inserted at the same position.
sort.SliceStable(d, func(i int, j int) bool {

View File

@ -1,16 +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 diff_test
import (
"testing"
"golang.org/x/tools/internal/lsp/diff"
"golang.org/x/tools/internal/lsp/diff/difftest"
)
func TestDiff(t *testing.T) {
difftest.DiffTest(t, diff.MyersComputeEdits)
}

View File

@ -5,40 +5,23 @@
// Package myers implements the Myers diff algorithm.
package myers
import "strings"
import (
"strings"
"golang.org/x/tools/internal/lsp/diff"
)
// Sources:
// 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
type Op struct {
Kind OpKind
Kind diff.OpKind
Content []string // content from b
I1, I2 int // indices of the line in a
J1 int // indices of the line in b, J2 implied by len(Content)
}
type OpKind int
const (
Delete OpKind = iota
Insert
Equal
)
func (k OpKind) String() string {
switch k {
case Delete:
return "delete"
case Insert:
return "insert"
case Equal:
return "equal"
default:
panic("unknown operation kind")
}
}
func ApplyEdits(a []string, operations []*Op) []string {
var b []string
var prevI2 int
@ -50,7 +33,7 @@ func ApplyEdits(a []string, operations []*Op) []string {
}
}
switch op.Kind {
case Equal, Insert:
case diff.Equal, diff.Insert:
b = append(b, op.Content...)
}
prevI2 = op.I2
@ -84,7 +67,7 @@ func Operations(a, b []string) []*Op {
return
}
op.I2 = i2
if op.Kind == Insert {
if op.Kind == diff.Insert {
op.Content = b[op.J1:j2]
}
solution[i] = op
@ -100,7 +83,7 @@ func Operations(a, b []string) []*Op {
for snake[0]-snake[1] > x-y {
if op == nil {
op = &Op{
Kind: Delete,
Kind: diff.Delete,
I1: x,
J1: y,
}
@ -116,7 +99,7 @@ func Operations(a, b []string) []*Op {
for snake[0]-snake[1] < x-y {
if op == nil {
op = &Op{
Kind: Insert,
Kind: diff.Insert,
I1: x,
J1: y,
}

View File

@ -14,6 +14,8 @@ import (
"strings"
"testing"
"golang.org/x/tools/internal/lsp/diff"
"golang.org/x/tools/internal/lsp/diff/difftest"
"golang.org/x/tools/internal/lsp/diff/myers"
)
@ -26,6 +28,10 @@ const (
var verifyDiff = flag.Bool("verify-diff", false, "Check that the unified diff output matches `diff -u`")
func TestDiff(t *testing.T) {
difftest.DiffTest(t, myers.ComputeEdits)
}
func TestMyersDiff(t *testing.T) {
for _, test := range []struct {
a, b string
lines []*myers.Op
@ -42,8 +48,8 @@ func TestDiff(t *testing.T) {
a: "A\n",
b: "B\n",
operations: []*myers.Op{
&myers.Op{Kind: myers.Delete, I1: 0, I2: 1, J1: 0},
&myers.Op{Kind: myers.Insert, Content: []string{"B\n"}, I1: 1, I2: 1, J1: 0},
&myers.Op{Kind: diff.Delete, I1: 0, I2: 1, J1: 0},
&myers.Op{Kind: diff.Insert, Content: []string{"B\n"}, I1: 1, I2: 1, J1: 0},
},
unified: `
@@ -1 +1 @@
@ -53,8 +59,8 @@ func TestDiff(t *testing.T) {
a: "A",
b: "B",
operations: []*myers.Op{
&myers.Op{Kind: myers.Delete, I1: 0, I2: 1, J1: 0},
&myers.Op{Kind: myers.Insert, Content: []string{"B"}, I1: 1, I2: 1, J1: 0},
&myers.Op{Kind: diff.Delete, I1: 0, I2: 1, J1: 0},
&myers.Op{Kind: diff.Insert, Content: []string{"B"}, I1: 1, I2: 1, J1: 0},
},
unified: `
@@ -1 +1 @@
@ -66,11 +72,11 @@ func TestDiff(t *testing.T) {
a: "A\nB\nC\nA\nB\nB\nA\n",
b: "C\nB\nA\nB\nA\nC\n",
operations: []*myers.Op{
&myers.Op{Kind: myers.Delete, I1: 0, I2: 1, J1: 0},
&myers.Op{Kind: myers.Delete, I1: 1, I2: 2, J1: 0},
&myers.Op{Kind: myers.Insert, Content: []string{"B\n"}, I1: 3, I2: 3, J1: 1},
&myers.Op{Kind: myers.Delete, I1: 5, I2: 6, J1: 4},
&myers.Op{Kind: myers.Insert, Content: []string{"C\n"}, I1: 7, I2: 7, J1: 5},
&myers.Op{Kind: diff.Delete, I1: 0, I2: 1, J1: 0},
&myers.Op{Kind: diff.Delete, I1: 1, I2: 2, J1: 0},
&myers.Op{Kind: diff.Insert, Content: []string{"B\n"}, I1: 3, I2: 3, J1: 1},
&myers.Op{Kind: diff.Delete, I1: 5, I2: 6, J1: 4},
&myers.Op{Kind: diff.Insert, Content: []string{"C\n"}, I1: 7, I2: 7, J1: 5},
},
unified: `
@@ -1,7 +1,6 @@
@ -90,9 +96,9 @@ func TestDiff(t *testing.T) {
a: "A\nB\n",
b: "A\nC\n\n",
operations: []*myers.Op{
&myers.Op{Kind: myers.Delete, I1: 1, I2: 2, J1: 1},
&myers.Op{Kind: myers.Insert, Content: []string{"C\n"}, I1: 2, I2: 2, J1: 1},
&myers.Op{Kind: myers.Insert, Content: []string{"\n"}, I1: 2, I2: 2, J1: 2},
&myers.Op{Kind: diff.Delete, I1: 1, I2: 2, J1: 1},
&myers.Op{Kind: diff.Insert, Content: []string{"C\n"}, I1: 2, I2: 2, J1: 1},
&myers.Op{Kind: diff.Insert, Content: []string{"\n"}, I1: 2, I2: 2, J1: 2},
},
unified: `
@@ -1,2 +1,3 @@

View File

@ -2,68 +2,68 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package diff
package myers
import (
"fmt"
"strings"
"golang.org/x/tools/internal/lsp/diff/myers"
"golang.org/x/tools/internal/lsp/diff"
"golang.org/x/tools/internal/span"
)
func init() {
ToUnified = myersToUnified
diff.ToUnified = myersToUnified
}
func MyersComputeEdits(uri span.URI, before, after string) []TextEdit {
u := myers.SplitLines(before)
f := myers.SplitLines(after)
return myersDiffToEdits(uri, myers.Operations(u, f))
func ComputeEdits(uri span.URI, before, after string) []diff.TextEdit {
u := SplitLines(before)
f := SplitLines(after)
return myersDiffToEdits(uri, Operations(u, f))
}
func myersToUnified(from, to string, before string, edits []TextEdit) string {
u := myers.SplitLines(before)
func myersToUnified(from, to string, before string, edits []diff.TextEdit) string {
u := SplitLines(before)
ops := myersEditsToDiff(edits)
return fmt.Sprint(myers.ToUnified(from, to, u, ops))
return fmt.Sprint(ToUnified(from, to, u, ops))
}
func myersDiffToEdits(uri span.URI, ops []*myers.Op) []TextEdit {
edits := make([]TextEdit, 0, len(ops))
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 myers.Delete:
case diff.Delete:
// Delete: unformatted[i1:i2] is deleted.
edits = append(edits, TextEdit{Span: s})
case myers.Insert:
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, TextEdit{Span: s, NewText: content})
edits = append(edits, diff.TextEdit{Span: s, NewText: content})
}
}
}
return edits
}
func myersEditsToDiff(edits []TextEdit) []*myers.Op {
func myersEditsToDiff(edits []diff.TextEdit) []*Op {
iToJ := 0
ops := make([]*myers.Op, len(edits))
ops := make([]*Op, len(edits))
for i, edit := range edits {
i1 := edit.Span.Start().Line() - 1
i2 := edit.Span.End().Line() - 1
kind := myers.Insert
kind := diff.Insert
if edit.NewText == "" {
kind = myers.Delete
kind = diff.Delete
}
ops[i] = &myers.Op{
ops[i] = &Op{
Kind: kind,
Content: myers.SplitLines(edit.NewText),
Content: SplitLines(edit.NewText),
I1: i1,
I2: i2,
J1: i1 + iToJ,
}
if kind == myers.Insert {
if kind == diff.Insert {
iToJ += len(ops[i].Content)
} else {
iToJ -= i2 - i1

View File

@ -7,6 +7,8 @@ package myers
import (
"fmt"
"strings"
"golang.org/x/tools/internal/lsp/diff"
)
type Unified struct {
@ -21,7 +23,7 @@ type Hunk struct {
}
type Line struct {
Kind OpKind
Kind diff.OpKind
Content string
}
@ -67,14 +69,14 @@ func ToUnified(from, to string, lines []string, ops []*Op) Unified {
}
last = op.I1
switch op.Kind {
case Delete:
case diff.Delete:
for i := op.I1; i < op.I2; i++ {
h.Lines = append(h.Lines, Line{Kind: Delete, Content: lines[i]})
h.Lines = append(h.Lines, Line{Kind: diff.Delete, Content: lines[i]})
last++
}
case Insert:
case diff.Insert:
for _, c := range op.Content {
h.Lines = append(h.Lines, Line{Kind: Insert, Content: c})
h.Lines = append(h.Lines, Line{Kind: diff.Insert, Content: c})
}
default:
// all other op types ignored
@ -97,7 +99,7 @@ func addEqualLines(h *Hunk, lines []string, start, end int) int {
if i >= len(lines) {
return delta
}
h.Lines = append(h.Lines, Line{Kind: Equal, Content: lines[i]})
h.Lines = append(h.Lines, Line{Kind: diff.Equal, Content: lines[i]})
delta++
}
return delta
@ -113,9 +115,9 @@ func (u Unified) Format(f fmt.State, r rune) {
fromCount, toCount := 0, 0
for _, l := range hunk.Lines {
switch l.Kind {
case Delete:
case diff.Delete:
fromCount++
case Insert:
case diff.Insert:
toCount++
default:
fromCount++
@ -136,9 +138,9 @@ func (u Unified) Format(f fmt.State, r rune) {
fmt.Fprint(f, " @@\n")
for _, l := range hunk.Lines {
switch l.Kind {
case Delete:
case diff.Delete:
fmt.Fprintf(f, "-%s", l.Content)
case Insert:
case diff.Insert:
fmt.Fprintf(f, "+%s", l.Content)
default:
fmt.Fprintf(f, " %s", l.Content)

View File

@ -10,6 +10,7 @@ import (
"time"
"golang.org/x/tools/internal/lsp/diff"
"golang.org/x/tools/internal/lsp/diff/myers"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/telemetry/tag"
errors "golang.org/x/xerrors"
@ -41,7 +42,7 @@ var (
FuzzyMatching: true,
Budget: 100 * time.Millisecond,
},
ComputeEdits: diff.MyersComputeEdits,
ComputeEdits: myers.ComputeEdits,
}
)