1
0
mirror of https://github.com/regclient/regclient.git synced 2025-04-18 22:44:00 +03:00
Brandon Mitchell d5807529a5
Fix missing lines from diff
When context overlaps with previous diff entry, a line of context is lost in the output.

Signed-off-by: Brandon Mitchell <git@bmitch.net>
2024-03-06 16:14:33 -05:00

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
}