func parseSequence()

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
}