Project history and sensitivity paths

This commit is contained in:
2026-10-16 15:44:44 +00:00
parent b1b5163cc3
commit 66dced0823
5 changed files with 207 additions and 34 deletions

View File

@@ -66,6 +66,61 @@ func cloneHistoryApplications(values []historyApplication) []historyApplication
return cloned
}
// walkHistoryApplication visits a retained source tree with both its local
// semantic path and its effective projected path. Sequence fragments keep
// their original tree indexes, while the effective path uses the assigned
// range recorded by strict composition.
func walkHistoryApplication(application historyApplication, visit func(local, effective Path, node *document.Node)) {
if application.value == nil {
return
}
var walk func(*document.Node, Path, Path)
walk = func(node *document.Node, local, effective Path) {
if node == nil {
return
}
visit(local, appendDestination(application.path, effective), node)
switch node.Kind {
case document.Mapping:
for _, entry := range node.Mapping {
walk(entry.Value, local.Key(entry.Key), effective.Key(entry.Key))
}
case document.Sequence:
contribution, projected := sequenceHistoryAt(application.sequences, local)
for index, child := range node.Sequence {
effectiveIndex := index
if projected {
effectiveIndex += contribution.offset
}
walk(child, local.Index(index), effective.Index(effectiveIndex))
}
}
}
walk(application.value, Path{}, Path{})
}
// historyApplicationLocalPath translates an effective projected path back to
// the retained source tree, including indexes shifted by enclosing appends.
func historyApplicationLocalPath(application historyApplication, effective Path) (Path, bool) {
relative, ok := relativeHistoryPath(application.path, effective)
if !ok {
return Path{}, false
}
local := Path{}
for _, segment := range relative.segments {
if contribution, found := sequenceHistoryAt(application.sequences, local); found {
index, valid := schemaIndex(segment)
if !valid || segment.kind == pathSegmentKey || index < contribution.offset || index >= contribution.offset+contribution.length {
return Path{}, false
}
local = local.Index(index - contribution.offset)
continue
}
local.segments = append(local.segments, segment)
}
return local, true
}
func applicationOperationPath(application historyApplication, query Path, lookup *documentLookup) (Path, bool) {
if !application.path.valid() || application.value == nil {
return Path{}, false
@@ -78,14 +133,13 @@ func applicationOperationPath(application historyApplication, query Path, lookup
}
sequencePathMatched := false
for _, contribution := range application.sequences {
if !pathContains(contribution.path, query) && !pathContains(query, contribution.path) {
container := appendDestination(application.path, contribution.path)
if !pathContains(container, query) && !pathContains(query, container) {
continue
}
sequencePathMatched = true
if pathContains(contribution.path, query) && len(query.segments) > len(contribution.path.segments) {
segment := query.segments[len(contribution.path.segments)]
itemIndex, ok := schemaIndex(segment)
if !ok || itemIndex < contribution.offset || itemIndex >= contribution.offset+contribution.length {
if pathContains(container, query) && len(query.segments) > len(container.segments) {
if _, ok := historyApplicationLocalPath(application, query); !ok {
continue
}
}
@@ -115,26 +169,10 @@ func historyApplicationOrigin(application historyApplication, operationPath Path
return cloneOrigin(application.origin)
}
origin := Origin{Layer: application.layer}
relative, ok := relativeHistoryPath(application.path, operationPath)
relative, ok := historyApplicationLocalPath(application, operationPath)
if !ok {
return origin
}
for _, contribution := range application.sequences {
if !pathContains(contribution.path, operationPath) {
continue
}
relative, ok = relativeHistoryPath(Path{}, operationPath)
if !ok || len(relative.segments) < len(contribution.path.segments)+1 {
break
}
itemPosition := len(contribution.path.segments)
itemIndex, valid := schemaIndex(relative.segments[itemPosition])
if !valid || itemIndex < contribution.offset || itemIndex >= contribution.offset+contribution.length {
break
}
relative.segments[itemPosition] = pathSegment{kind: pathSegmentIndex, index: itemIndex - contribution.offset}
break
}
if node, present := documentAtPathWithLookup(application.value, relative, lookup); present {
origin.Locations = []Location{publicLocation(node.Location)}
}