| // Copyright 2025 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 |
| |
| import ( |
| "slices" |
| ) |
| |
| // Merge merges two valid, ordered lists of edits. |
| // It returns zero if there was a conflict. |
| // |
| // If corresponding edits in x and y are identical, |
| // they are coalesced in the result. |
| // |
| // If x and y both provide different insertions at the same point, |
| // the insertions from x will be first in the result. |
| // |
| // TODO(adonovan): this algorithm could be improved, for example by |
| // working harder to coalesce non-identical edits that share a common |
| // deletion or common prefix of insertion (see the tests). |
| // Survey the academic literature for insights. |
| func Merge(x, y []Edit) ([]Edit, bool) { |
| // Make a defensive (premature) copy of the arrays. |
| x = slices.Clone(x) |
| y = slices.Clone(y) |
| |
| var merged []Edit |
| add := func(edit Edit) { |
| merged = append(merged, edit) |
| } |
| var xi, yi int |
| for xi < len(x) && yi < len(y) { |
| px := &x[xi] |
| py := &y[yi] |
| |
| if *px == *py { |
| // x and y are identical: coalesce. |
| add(*px) |
| xi++ |
| yi++ |
| |
| } else if px.End <= py.Start { |
| // x is entirely before y, |
| // or an insertion at start of y. |
| add(*px) |
| xi++ |
| |
| } else if py.End <= px.Start { |
| // y is entirely before x, |
| // or an insertion at start of x. |
| add(*py) |
| yi++ |
| |
| } else if px.Start < py.Start { |
| // x is partly before y: |
| // split it into a deletion and an edit. |
| add(Edit{px.Start, py.Start, ""}) |
| px.Start = py.Start |
| |
| } else if py.Start < px.Start { |
| // y is partly before x: |
| // split it into a deletion and an edit. |
| add(Edit{py.Start, px.Start, ""}) |
| py.Start = px.Start |
| |
| } else { |
| // x and y are unequal non-insertions |
| // at the same point: conflict. |
| return nil, false |
| } |
| } |
| for ; xi < len(x); xi++ { |
| add(x[xi]) |
| } |
| for ; yi < len(y); yi++ { |
| add(y[yi]) |
| } |
| return merged, true |
| } |