Preserve inherited source origins and transfer evidence
This commit is contained in:
@@ -24,9 +24,35 @@ type inheritanceControl struct {
|
||||
}
|
||||
|
||||
type inheritanceScope struct {
|
||||
root *document.Node
|
||||
transfers []inheritanceTransfer
|
||||
controls []inheritanceControl
|
||||
root *document.Node
|
||||
origins map[string]Origin
|
||||
transfers []inheritanceTransfer
|
||||
controls []inheritanceControl
|
||||
replacements []inheritanceReplacement
|
||||
}
|
||||
|
||||
// A replacement retains the displaced source/derivation record while the
|
||||
// effective origin belongs to the child. Value trees are borrowed immutable
|
||||
// inputs or detached templates and are budgeted with retained history later.
|
||||
type inheritanceReplacement struct {
|
||||
path Path
|
||||
before *document.Node
|
||||
after *document.Node
|
||||
previous []displacedDefinition
|
||||
effective Origin
|
||||
}
|
||||
|
||||
// A link represents one shared relationship edge. Each contributor retains
|
||||
// its original semantic path independently of source line/column coordinates.
|
||||
type inheritanceTrace struct {
|
||||
previous *inheritanceTrace
|
||||
transfer *inheritanceTransfer
|
||||
}
|
||||
|
||||
type inheritanceContributor struct {
|
||||
source Path
|
||||
location Location
|
||||
trace *inheritanceTrace
|
||||
}
|
||||
|
||||
type siblingFrame struct {
|
||||
@@ -44,6 +70,10 @@ func expandSiblingDefinitions(scope *document.Node, scopePath Path) (*inheritanc
|
||||
}
|
||||
|
||||
func expandSiblingDefinitionsWithLimits(scope *document.Node, scopePath Path, limits projectionLimits) (*inheritanceScope, []Diagnostic) {
|
||||
return expandSiblingDefinitionsWithOrigins(scope, scopePath, limits, nil)
|
||||
}
|
||||
|
||||
func expandSiblingDefinitionsWithOrigins(scope *document.Node, scopePath Path, limits projectionLimits, inputOrigins map[string]Origin) (*inheritanceScope, []Diagnostic) {
|
||||
if scope == nil || scope.Kind != document.Mapping {
|
||||
return nil, []Diagnostic{{
|
||||
Code: CodeInheritanceInvalid, Severity: SeverityError, Path: scopePath,
|
||||
@@ -60,6 +90,7 @@ func expandSiblingDefinitionsWithLimits(scope *document.Node, scopePath Path, li
|
||||
slices.Sort(names)
|
||||
|
||||
resolved := make(map[string]*document.Node, len(byName))
|
||||
resolvedOrigins := make(map[string]map[string]Origin, len(byName))
|
||||
state := make(map[string]uint8, len(byName))
|
||||
result := &inheritanceScope{}
|
||||
retained := 1 // scope mapping node
|
||||
@@ -128,6 +159,7 @@ func expandSiblingDefinitionsWithLimits(scope *document.Node, scopePath Path, li
|
||||
}
|
||||
|
||||
payload := withoutInheritanceDirective(entry.Value)
|
||||
childOrigins := directInheritanceOrigins(payload, path, inputOrigins)
|
||||
if limits.nodes >= 0 {
|
||||
remaining := limits.nodes - retained - 1 // the sibling key
|
||||
within := false
|
||||
@@ -143,17 +175,37 @@ func expandSiblingDefinitionsWithLimits(scope *document.Node, scopePath Path, li
|
||||
if frame.hasParent {
|
||||
parent := resolved[frame.parent]
|
||||
merged := document.Clone(parent)
|
||||
mergeRecursiveMappings(merged, payload, path, new(mappingIndex), recursiveMergeHooks{})
|
||||
control := inheritanceDirectiveLocation(entry.Value)
|
||||
transfer := inheritanceTransfer{
|
||||
from: scopePath.Key(frame.parent), to: path,
|
||||
reference: path.Key(inheritanceKey), locations: []Location{control},
|
||||
}
|
||||
mergedOrigins := transferredInheritanceOrigins(resolvedOrigins[frame.parent], transfer.from, path, transfer)
|
||||
mergeInheritanceOrigin(mergedOrigins, path, childOrigins[path.String()])
|
||||
mergeRecursiveMappings(merged, payload, path, new(mappingIndex), recursiveMergeHooks{
|
||||
added: func(childPath Path, incoming *document.Node) {
|
||||
copyInheritanceSubtree(mergedOrigins, childOrigins, incoming, childPath)
|
||||
},
|
||||
combined: func(childPath Path, _ *document.Node) {
|
||||
mergeInheritanceOrigin(mergedOrigins, childPath, childOrigins[childPath.String()])
|
||||
},
|
||||
replaced: func(childPath Path, existing, incoming *document.Node) {
|
||||
previous := displacedOrigins(existing, mergedOrigins, childPath)
|
||||
result.replacements = append(result.replacements, inheritanceReplacement{
|
||||
path: childPath.clone(), before: existing, after: incoming,
|
||||
previous: previous, effective: cloneOrigin(childOrigins[childPath.String()]),
|
||||
})
|
||||
deleteOriginSubtree(mergedOrigins, childPath, existing)
|
||||
copyInheritanceSubtree(mergedOrigins, childOrigins, incoming, childPath)
|
||||
},
|
||||
})
|
||||
merged.Location = entry.Value.Location
|
||||
resolved[frame.name] = merged
|
||||
reference := path.Key(inheritanceKey)
|
||||
control := inheritanceDirectiveLocation(entry.Value)
|
||||
result.transfers = append(result.transfers, inheritanceTransfer{
|
||||
from: scopePath.Key(frame.parent), to: path,
|
||||
reference: reference, locations: []Location{control},
|
||||
})
|
||||
resolvedOrigins[frame.name] = mergedOrigins
|
||||
result.transfers = append(result.transfers, transfer)
|
||||
} else {
|
||||
resolved[frame.name] = document.Clone(payload)
|
||||
resolvedOrigins[frame.name] = childOrigins
|
||||
}
|
||||
count, _ := inheritanceLowerBoundCount(resolved[frame.name], limits.nodes)
|
||||
retained += 1 + count
|
||||
@@ -164,12 +216,20 @@ func expandSiblingDefinitionsWithLimits(scope *document.Node, scopePath Path, li
|
||||
|
||||
root := *scope
|
||||
root.Mapping = make([]document.Entry, len(scope.Mapping))
|
||||
rootOrigin := directInheritanceOrigins(&document.Node{Kind: document.Mapping, Location: scope.Location}, scopePath, inputOrigins)
|
||||
result.origins = rootOrigin
|
||||
for index, entry := range scope.Mapping {
|
||||
root.Mapping[index] = entry
|
||||
if resolved[entry.Key] != nil {
|
||||
root.Mapping[index].Value = resolved[entry.Key]
|
||||
for pointer, origin := range resolvedOrigins[entry.Key] {
|
||||
result.origins[pointer] = origin
|
||||
}
|
||||
} else {
|
||||
root.Mapping[index].Value = document.Clone(entry.Value)
|
||||
for pointer, origin := range directInheritanceOrigins(entry.Value, scopePath.Key(entry.Key), inputOrigins) {
|
||||
result.origins[pointer] = origin
|
||||
}
|
||||
}
|
||||
}
|
||||
result.root = &root
|
||||
|
||||
Reference in New Issue
Block a user