Diff returns an anchored diff of the two texts old and new in the “unified diff” format. If old and new are identical, Diff returns a nil slice (no output). Unix diff implementations typically look for a diff with the smallest number of lines inserted and removed, which can in the worst case take t
(oldName string, old []byte, newName string, new []byte)
| 44 | // to wait longer (to be patient) for the diff, meaning that it is a slower algorithm, |
| 45 | // when in fact the algorithm is faster than the standard one. |
| 46 | func Diff(oldName string, old []byte, newName string, new []byte) []byte { |
| 47 | if bytes.Equal(old, new) { |
| 48 | return nil |
| 49 | } |
| 50 | x := lines(old) |
| 51 | y := lines(new) |
| 52 | |
| 53 | // Print diff header. |
| 54 | var out bytes.Buffer |
| 55 | fmt.Fprintf(&out, "diff %s %s\n", oldName, newName) |
| 56 | fmt.Fprintf(&out, "--- %s\n", oldName) |
| 57 | fmt.Fprintf(&out, "+++ %s\n", newName) |
| 58 | |
| 59 | // Loop over matches to consider, |
| 60 | // expanding each match to include surrounding lines, |
| 61 | // and then printing diff chunks. |
| 62 | // To avoid setup/teardown cases outside the loop, |
| 63 | // tgs returns a leading {0,0} and trailing {len(x), len(y)} pair |
| 64 | // in the sequence of matches. |
| 65 | var ( |
| 66 | done pair // printed up to x[:done.x] and y[:done.y] |
| 67 | chunk pair // start lines of current chunk |
| 68 | count pair // number of lines from each side in current chunk |
| 69 | ctext []string // lines for current chunk |
| 70 | ) |
| 71 | for _, m := range tgs(x, y) { |
| 72 | if m.x < done.x { |
| 73 | // Already handled scanning forward from earlier match. |
| 74 | continue |
| 75 | } |
| 76 | |
| 77 | // Expand matching lines as far as possible, |
| 78 | // establishing that x[start.x:end.x] == y[start.y:end.y]. |
| 79 | // Note that on the first (or last) iteration we may (or definitely do) |
| 80 | // have an empty match: start.x==end.x and start.y==end.y. |
| 81 | start := m |
| 82 | for start.x > done.x && start.y > done.y && x[start.x-1] == y[start.y-1] { |
| 83 | start.x-- |
| 84 | start.y-- |
| 85 | } |
| 86 | end := m |
| 87 | for end.x < len(x) && end.y < len(y) && x[end.x] == y[end.y] { |
| 88 | end.x++ |
| 89 | end.y++ |
| 90 | } |
| 91 | |
| 92 | // Emit the mismatched lines before start into this chunk. |
| 93 | // (No effect on first sentinel iteration, when start = {0,0}.) |
| 94 | for _, s := range x[done.x:start.x] { |
| 95 | ctext = append(ctext, "-"+s) |
| 96 | count.x++ |
| 97 | } |
| 98 | for _, s := range y[done.y:start.y] { |
| 99 | ctext = append(ctext, "+"+s) |
| 100 | count.y++ |
| 101 | } |
| 102 | |
| 103 | // If we're not at EOF and have too few common lines, |