Skip to content

Commit

Permalink
feat: import upstream package (#7)
Browse files Browse the repository at this point in the history
Co-authored-by: aymanbagabas <[email protected]>
  • Loading branch information
github-actions[bot] and aymanbagabas authored Jun 20, 2023
1 parent a9c2e43 commit 54452e1
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 39 deletions.
2 changes: 1 addition & 1 deletion .github/UPSTREAM
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1c9fe3f82c363b929ef7239ca0ad8a5dafbbcf05
a4ed05f16747f9958a65c5c279f69e30b6477c9e
27 changes: 17 additions & 10 deletions diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type Edit struct {
}

func (e Edit) String() string {
return fmt.Sprintf("{Start:%d,End:%d,New:%s}", e.Start, e.End, e.New)
return fmt.Sprintf("{Start:%d,End:%d,New:%q}", e.Start, e.End, e.New)
}

// Apply applies a sequence of edits to the src buffer and returns the
Expand Down Expand Up @@ -114,19 +114,23 @@ func lineEdits(src string, edits []Edit) ([]Edit, error) {
return nil, err
}

// Do all edits begin and end at the start of a line?
// TODO(adonovan, pjw): why does omitting this 'optimization'
// cause tests to fail? (TestDiff/insert-line,extra_newline)
// Do all deletions begin and end at the start of a line,
// and all insertions end with a newline?
// (This is merely a fast path.)
for _, edit := range edits {
if edit.Start >= len(src) || // insertion at EOF
edit.Start > 0 && src[edit.Start-1] != '\n' || // not at line start
edit.End > 0 && src[edit.End-1] != '\n' { // not at line start
goto expand
edit.End > 0 && src[edit.End-1] != '\n' || // not at line start
edit.New != "" && edit.New[len(edit.New)-1] != '\n' { // partial insert
goto expand // slow path
}
}
return edits, nil // aligned

expand:
if len(edits) == 0 {
return edits, nil // no edits (unreachable due to fast path)
}
expanded := make([]Edit, 0, len(edits)) // a guess
prev := edits[0]
// TODO(adonovan): opt: start from the first misaligned edit.
Expand Down Expand Up @@ -158,10 +162,13 @@ func expandEdit(edit Edit, src string) Edit {

// Expand end right to end of line.
end := edit.End
if nl := strings.IndexByte(src[end:], '\n'); nl < 0 {
edit.End = len(src) // extend to EOF
} else {
edit.End = end + nl + 1 // extend beyond \n
if end > 0 && src[end-1] != '\n' ||
edit.New != "" && edit.New[len(edit.New)-1] != '\n' {
if nl := strings.IndexByte(src[end:], '\n'); nl < 0 {
edit.End = len(src) // extend to EOF
} else {
edit.End = end + nl + 1 // extend beyond \n
}
}
edit.New += src[end:edit.End]

Expand Down
20 changes: 14 additions & 6 deletions diff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,17 +94,25 @@ func FuzzRoundTrip(f *testing.F) {
func TestLineEdits(t *testing.T) {
for _, tc := range difftest.TestCases {
t.Run(tc.Name, func(t *testing.T) {
// if line edits not specified, it is the same as edits
edits := tc.LineEdits
if edits == nil {
edits = tc.Edits
want := tc.LineEdits
if want == nil {
want = tc.Edits // already line-aligned
}
got, err := diff.LineEdits(tc.In, tc.Edits)
if err != nil {
t.Fatalf("LineEdits: %v", err)
}
if !reflect.DeepEqual(got, edits) {
t.Errorf("LineEdits got\n%q, want\n%q\n%#v", got, edits, tc)
if !reflect.DeepEqual(got, want) {
t.Errorf("in=<<%s>>\nout=<<%s>>\nraw edits=%s\nline edits=%s\nwant: %s",
tc.In, tc.Out, tc.Edits, got, want)
}
// make sure that applying the edits gives the expected result
fixed, err := diff.Apply(tc.In, got)
if err != nil {
t.Error(err)
}
if fixed != tc.Out {
t.Errorf("Apply(LineEdits): got %q, want %q", fixed, tc.Out)
}
})
}
Expand Down
25 changes: 21 additions & 4 deletions difftest/difftest.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const (

var TestCases = []struct {
Name, In, Out, Unified string
Edits, LineEdits []diff.Edit
Edits, LineEdits []diff.Edit // expectation (LineEdits=nil => already line-aligned)
NoDiff bool
}{{
Name: "empty",
Expand Down Expand Up @@ -220,9 +220,9 @@ var TestCases = []struct {
{Start: 14, End: 14, New: "C\n"},
},
LineEdits: []diff.Edit{
{Start: 0, End: 6, New: "C\n"},
{Start: 6, End: 8, New: "B\nA\n"},
{Start: 10, End: 14, New: "A\n"},
{Start: 0, End: 4, New: ""},
{Start: 6, End: 6, New: "B\n"},
{Start: 10, End: 12, New: ""},
{Start: 14, End: 14, New: "C\n"},
},
}, {
Expand Down Expand Up @@ -279,6 +279,23 @@ var TestCases = []struct {
Edits: []diff.Edit{{Start: 3, End: 3, New: "\nbbb"}},
LineEdits: []diff.Edit{{Start: 0, End: 4, New: "aaa\nbbb\n"}},
Unified: UnifiedPrefix + "@@ -1,2 +1,3 @@\n aaa\n+bbb\n ccc\n",
}, {
Name: "60379",
In: `package a
type S struct {
s fmt.Stringer
}
`,
Out: `package a
type S struct {
s fmt.Stringer
}
`,
Edits: []diff.Edit{{Start: 27, End: 27, New: "\t"}},
LineEdits: []diff.Edit{{Start: 27, End: 42, New: "\ts fmt.Stringer\n"}},
Unified: UnifiedPrefix + "@@ -1,5 +1,5 @@\n package a\n \n type S struct {\n-s fmt.Stringer\n+\ts fmt.Stringer\n }\n",
},
}

Expand Down
34 changes: 28 additions & 6 deletions myers/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ func ComputeEdits(before, after string) []diff.Edit {
for _, op := range ops {
start, end := lineOffsets[op.I1], lineOffsets[op.I2]
switch op.Kind {
case diff.Delete:
case opDelete:
// Delete: before[I1:I2] is deleted.
edits = append(edits, diff.Edit{Start: start, End: end})
case diff.Insert:
case opInsert:
// Insert: after[J1:J2] is inserted at before[I1:I1].
if content := strings.Join(op.Content, ""); content != "" {
edits = append(edits, diff.Edit{Start: start, End: end, New: content})
Expand All @@ -45,8 +45,30 @@ func ComputeEdits(before, after string) []diff.Edit {
return edits
}

// opKind is used to denote the type of operation a line represents.
type opKind int

const (
opDelete opKind = iota // line deleted from input (-)
opInsert // line inserted into output (+)
opEqual // line present in input and output
)

func (kind opKind) String() string {
switch kind {
case opDelete:
return "delete"
case opInsert:
return "insert"
case opEqual:
return "equal"
default:
panic("unknown opKind")
}
}

type operation struct {
Kind diff.OpKind
Kind 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)
Expand All @@ -72,7 +94,7 @@ func operations(a, b []string) []*operation {
return
}
op.I2 = i2
if op.Kind == diff.Insert {
if op.Kind == opInsert {
op.Content = b[op.J1:j2]
}
solution[i] = op
Expand All @@ -88,7 +110,7 @@ func operations(a, b []string) []*operation {
for snake[0]-snake[1] > x-y {
if op == nil {
op = &operation{
Kind: diff.Delete,
Kind: opDelete,
I1: x,
J1: y,
}
Expand All @@ -104,7 +126,7 @@ func operations(a, b []string) []*operation {
for snake[0]-snake[1] < x-y {
if op == nil {
op = &operation{
Kind: diff.Insert,
Kind: opInsert,
I1: x,
J1: y,
}
Expand Down
14 changes: 2 additions & 12 deletions unified.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ type line struct {
}

// OpKind is used to denote the type of operation a line represents.
// TODO(adonovan): hide this once the myers package no longer references it.
type OpKind int

const (
Expand Down Expand Up @@ -156,18 +155,9 @@ func toUnified(fromName, toName string, content string, edits []Edit) (unified,
last++
}
if edit.New != "" {
for i, content := range splitLines(edit.New) {
toLine++
// Merge identical Delete+Insert.
// This is an unwanted output of converting diffs to line diffs
// that is easiest to fix by postprocessing.
// e.g. issue #59232: ("aaa\nccc\n", "aaa\nbbb\nccc")
// -> [Delete "aaa\n", Insert "aaa\n", Insert "bbb\n", ...].
if i == 0 && last > start && h.Lines[len(h.Lines)-1].Content == content {
h.Lines[len(h.Lines)-1].Kind = Equal
continue
}
for _, content := range splitLines(edit.New) {
h.Lines = append(h.Lines, line{Kind: Insert, Content: content})
toLine++
}
}
}
Expand Down

0 comments on commit 54452e1

Please sign in to comment.