159 lines
5.2 KiB
Go
159 lines
5.2 KiB
Go
package engine
|
|
|
|
import (
|
|
"slices"
|
|
"strconv"
|
|
|
|
"gitea.maximumdirect.net/eric/ConfigKit/internal/document"
|
|
)
|
|
|
|
func directInheritanceOrigins(node *document.Node, root Path, input map[string]Origin) map[string]Origin {
|
|
result := make(map[string]Origin)
|
|
walkDocument(node, root, func(path Path, value *document.Node) {
|
|
origin, found := input[path.String()]
|
|
if !found {
|
|
origin = Origin{Layer: LayerBase, Locations: []Location{publicLocation(value.Location)}}
|
|
}
|
|
origin = cloneOrigin(origin)
|
|
if len(origin.contributors) == 0 {
|
|
origin.contributors = make([]inheritanceContributor, len(origin.Locations))
|
|
for index, location := range origin.Locations {
|
|
origin.contributors[index] = inheritanceContributor{
|
|
source: path.clone(), location: location,
|
|
}
|
|
}
|
|
}
|
|
result[path.String()] = origin
|
|
})
|
|
return result
|
|
}
|
|
|
|
func transferredInheritanceOrigins(parent map[string]Origin, from, to Path, control inheritanceTransfer) map[string]Origin {
|
|
result := make(map[string]Origin, len(parent))
|
|
transfer := control // one immutable edge shared by every matching contributor
|
|
links := make(map[*inheritanceTrace]*inheritanceTrace)
|
|
for pointer, origin := range parent {
|
|
path, err := ParsePath(pointer)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
relative, ok := relativeHistoryPath(from, path)
|
|
if !ok {
|
|
continue
|
|
}
|
|
destination := appendInheritancePath(to, relative)
|
|
origin = cloneOrigin(origin)
|
|
for index, contributor := range origin.contributors {
|
|
link, found := links[contributor.trace]
|
|
if !found {
|
|
link = &inheritanceTrace{previous: contributor.trace, transfer: &transfer}
|
|
links[contributor.trace] = link
|
|
}
|
|
origin.contributors[index].trace = link
|
|
}
|
|
result[destination.String()] = origin
|
|
}
|
|
return result
|
|
}
|
|
|
|
func appendInheritancePath(root, relative Path) Path {
|
|
return Path{segments: append(clonePathSegments(root.segments, len(relative.segments)), relative.segments...)}
|
|
}
|
|
|
|
func mergeInheritanceOrigin(origins map[string]Origin, path Path, incoming Origin) {
|
|
pointer := path.String()
|
|
current := origins[pointer]
|
|
current.Layer = incoming.Layer
|
|
current.Locations = append(current.Locations, incoming.Locations...)
|
|
current.contributors = append(current.contributors, incoming.contributors...)
|
|
origins[pointer] = current
|
|
}
|
|
|
|
func copyInheritanceSubtree(target, incoming map[string]Origin, node *document.Node, root Path) {
|
|
walkDocument(node, root, func(path Path, _ *document.Node) {
|
|
target[path.String()] = cloneOrigin(incoming[path.String()])
|
|
})
|
|
}
|
|
|
|
type inheritanceStepKey struct {
|
|
from string
|
|
to string
|
|
reference string
|
|
locations string
|
|
}
|
|
|
|
func inheritanceStepKeyForPaths(from, to, reference Path, locations []Location) inheritanceStepKey {
|
|
return inheritanceStepKey{
|
|
from: from.String(), to: to.String(), reference: reference.String(),
|
|
locations: inheritanceLocationsKey(locations),
|
|
}
|
|
}
|
|
|
|
func inheritanceLocationsKey(locations []Location) string {
|
|
encoded := strconv.AppendInt(nil, int64(len(locations)), 10)
|
|
encoded = append(encoded, ';')
|
|
for _, location := range locations {
|
|
encoded = strconv.AppendInt(encoded, int64(len(location.SourceID)), 10)
|
|
encoded = append(encoded, ':')
|
|
encoded = append(encoded, location.SourceID...)
|
|
encoded = append(encoded, ';')
|
|
encoded = strconv.AppendInt(encoded, int64(location.Line), 10)
|
|
encoded = append(encoded, ';')
|
|
encoded = strconv.AppendInt(encoded, int64(location.Column), 10)
|
|
encoded = append(encoded, ';')
|
|
}
|
|
return string(encoded)
|
|
}
|
|
|
|
// Materialize concrete steps only for an inspection record. The linked private
|
|
// traces remain compact when one parent supplies many descendant values.
|
|
func projectInheritanceSteps(origin Origin) []InheritanceStep {
|
|
steps := cloneInheritanceSteps(origin.Inheritance)
|
|
seen := make(map[inheritanceStepKey]struct{}, len(steps))
|
|
for _, step := range steps {
|
|
seen[inheritanceStepKeyForPaths(step.From, step.To, step.Reference, step.Locations)] = struct{}{}
|
|
}
|
|
for _, contributor := range origin.contributors {
|
|
var reverse []*inheritanceTrace
|
|
for link := contributor.trace; link != nil; link = link.previous {
|
|
reverse = append(reverse, link)
|
|
}
|
|
current := contributor.source
|
|
for index := len(reverse) - 1; index >= 0; index-- {
|
|
transfer := reverse[index].transfer
|
|
relative, ok := relativeHistoryPath(transfer.from, current)
|
|
if !ok {
|
|
continue
|
|
}
|
|
next := appendInheritancePath(transfer.to, relative)
|
|
key := inheritanceStepKeyForPaths(current, next, transfer.reference, transfer.locations)
|
|
if _, found := seen[key]; !found {
|
|
seen[key] = struct{}{}
|
|
steps = append(steps, InheritanceStep{
|
|
From: current.clone(), To: next.clone(), Reference: transfer.reference.clone(),
|
|
Locations: cloneLocations(transfer.locations),
|
|
})
|
|
}
|
|
current = next
|
|
}
|
|
}
|
|
return steps
|
|
}
|
|
|
|
func sameInheritanceTrace(left, right *inheritanceTrace) bool {
|
|
for left != nil && right != nil {
|
|
if left == right {
|
|
return true
|
|
}
|
|
l, r := left.transfer, right.transfer
|
|
if !slices.Equal(l.from.segments, r.from.segments) ||
|
|
!slices.Equal(l.to.segments, r.to.segments) ||
|
|
!slices.Equal(l.reference.segments, r.reference.segments) ||
|
|
!slices.Equal(l.locations, r.locations) {
|
|
return false
|
|
}
|
|
left, right = left.previous, right.previous
|
|
}
|
|
return left == nil && right == nil
|
|
}
|