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) { copyInheritanceSubtreeAt(target, incoming, node, root, root) } func copyInheritanceSubtreeAt(target, incoming map[string]Origin, node *document.Node, from, to Path) { walkDocument(node, Path{}, func(relative Path, _ *document.Node) { source := appendDestination(from, relative) destination := appendDestination(to, relative) target[destination.String()] = cloneOrigin(incoming[source.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 }