Project history and sensitivity paths
This commit is contained in:
@@ -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)}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user