mirror of
https://github.com/regclient/regclient.git
synced 2025-04-18 22:44:00 +03:00
When context overlaps with previous diff entry, a line of context is lost in the output. Signed-off-by: Brandon Mitchell <git@bmitch.net>
113 lines
2.6 KiB
Go
113 lines
2.6 KiB
Go
// Package diff computes the efficient set of changes (insert/delete) between two arrays of strings
|
|
package diff
|
|
|
|
import "fmt"
|
|
|
|
// opKind is used to denote the type of operation a line represents.
|
|
type opKind int
|
|
|
|
const (
|
|
// OpDelete is the operation kind for a line that is present in the input
|
|
// but not in the output.
|
|
OpDelete opKind = iota
|
|
// OpInsert is the operation kind for a line that is new in the output.
|
|
OpInsert
|
|
)
|
|
|
|
type operation struct {
|
|
Kind opKind
|
|
X1, X2 int // indices of the line in a
|
|
Y1, Y2 int // indices of the line in b
|
|
}
|
|
|
|
type Opt func(*conf)
|
|
|
|
type conf struct {
|
|
contextA int
|
|
contextB int
|
|
contextFull bool
|
|
}
|
|
|
|
func WithContext(a, b int) func(*conf) {
|
|
return func(c *conf) {
|
|
c.contextA = a
|
|
c.contextB = b
|
|
}
|
|
}
|
|
|
|
func WithFullContext() func(*conf) {
|
|
return func(c *conf) {
|
|
c.contextFull = true
|
|
}
|
|
}
|
|
|
|
// Diff returns the difference between two strings
|
|
func Diff(a, b []string, opts ...Opt) []string {
|
|
c := conf{}
|
|
for _, fn := range opts {
|
|
fn(&c)
|
|
}
|
|
|
|
diffLines := []string{}
|
|
setLines := []string{}
|
|
ops := myersOperations(a, b)
|
|
sX1, sX2, sY1, sY2 := 0, 0, 0, 0
|
|
addSet := func() {
|
|
if len(setLines) == 0 {
|
|
return
|
|
}
|
|
// calculate how many lines of context to add
|
|
cA, cB := c.contextA, c.contextB
|
|
if sX1-cA < 0 || c.contextFull {
|
|
cA = sX1
|
|
}
|
|
if sX2+cB > len(a) || c.contextFull {
|
|
cB = len(a) - sX2
|
|
}
|
|
// add header
|
|
diffLines = append(diffLines, fmt.Sprintf("@@ -%d,%d +%d,%d @@", sX1-cA+1, sX2+cA+cB-sX1, sY1-cA+1, sY2+cA+cB-sY1))
|
|
// add context before, the change set, and context after
|
|
if cA > 0 {
|
|
for _, line := range a[sX1-cA : sX1] {
|
|
diffLines = append(diffLines, " "+line)
|
|
}
|
|
}
|
|
diffLines = append(diffLines, setLines...)
|
|
setLines = []string{} // reset the setLines to a new array
|
|
if cB > 0 {
|
|
for _, line := range a[sX2 : sX2+cB] {
|
|
diffLines = append(diffLines, " "+line)
|
|
}
|
|
}
|
|
}
|
|
for _, op := range ops {
|
|
// compare from last set
|
|
dX, dY := op.X1-sX2, op.Y1-sY2
|
|
if dX != dY || (dX > c.contextA && dX > c.contextB && !c.contextFull) {
|
|
// unexpected diff lines or gap exceeds context limits, create a new set
|
|
addSet()
|
|
sX1, sY1 = op.X1, op.Y1
|
|
} else if dX > 0 {
|
|
// add common lines between two diffs
|
|
for _, line := range a[sX2:op.X1] {
|
|
setLines = append(setLines, " "+line)
|
|
}
|
|
}
|
|
// add entries to this set, either delete or add
|
|
switch op.Kind {
|
|
case OpDelete:
|
|
for _, line := range a[op.X1:op.X2] {
|
|
setLines = append(setLines, "- "+line)
|
|
}
|
|
case OpInsert:
|
|
for _, line := range b[op.Y1:op.Y2] {
|
|
setLines = append(setLines, "+ "+line)
|
|
}
|
|
}
|
|
// update end of set
|
|
sX2, sY2 = op.X2, op.Y2
|
|
}
|
|
addSet()
|
|
return diffLines
|
|
}
|