in internal/pkg/template/diff/diff.go [193:269]
func parseSequence(fromNode, toNode *yaml.Node, overriders ...overrider) ([]diffNode, error) {
fromSeq, toSeq := make([]yaml.Node, len(fromNode.Content)), make([]yaml.Node, len(toNode.Content)) // NOTE: should be the same as calling `Decode`.
for idx, v := range fromNode.Content {
fromSeq[idx] = *v
}
for idx, v := range toNode.Content {
toSeq[idx] = *v
}
type cachedEntry struct {
node diffNode
err error
}
cachedDiff := make(map[string]cachedEntry)
lcsIndices := longestCommonSubsequence(fromSeq, toSeq, func(idxFrom, idxTo int) bool {
// Note: This function passed as `eq` should be a pure function. Therefore, its output is the same
// given the same `idxFrom` and `idxTo`. Hence, it is not necessary to parse the nodes again.
// In `lcs.go`, `eq` can be called twice on the same indices: once when computing LCS length, and
// once when back-tracing to construct the LCS.
if diff, ok := cachedDiff[cacheKey(idxFrom, idxTo)]; ok {
return diff.err == nil && diff.node == nil
}
diff, err := parse(&(fromSeq[idxFrom]), &(toSeq[idxTo]), "", overriders...)
if diff != nil { // NOTE: cache the diff only if a modification could have happened at this position.
cachedDiff[cacheKey(idxFrom, idxTo)] = cachedEntry{
node: diff,
err: err,
}
}
return err == nil && diff == nil
})
// No difference if the two sequences have the same size and the LCS is the entire sequence.
if len(fromSeq) == len(toSeq) && len(lcsIndices) == len(fromSeq) {
return nil, nil
}
var children []diffNode
var matchCount int
inspector := newLCSStateMachine(fromSeq, toSeq, lcsIndices)
for action := inspector.action(); action != actionDone; action = inspector.action() {
switch action {
case actionMatch:
matchCount++
if action := inspector.peek(); action != actionMatch {
children = append(children, &unchangedNode{count: matchCount})
matchCount = 0
}
case actionMod:
diff := cachedDiff[cacheKey(inspector.fromIndex(), inspector.toIndex())]
if diff.err != nil {
return nil, diff.err
}
children = append(children, &seqItemNode{
keyNode{
keyValue: diff.node.key(),
childNodes: diff.node.children(),
oldV: diff.node.oldYAML(),
newV: diff.node.newYAML(),
},
})
case actionDel:
item := inspector.fromItem()
children = append(children, &seqItemNode{
keyNode{
oldV: &item,
},
})
case actionInsert:
item := inspector.toItem()
children = append(children, &seqItemNode{
keyNode{
newV: &item,
},
})
}
inspector.next()
}
return children, nil
}