Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions model/high/v3/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ func (s *Schema) Walk(ctx context.Context, schema *base.Schema, depth int) {
// Restore original NodeParent after potential corruption
s.NodeParent = originalNodeParent
drCtx.ObjectChan <- s
s.publishHydratedChildren(drCtx)
return
}
}
Expand Down
93 changes: 93 additions & 0 deletions model/high/v3/schema_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,99 @@ func (s *Schema) hydrateMaterializedFromCache(cached *Schema, drCtx *DrContext)
s.ExternalDocs = copyExternalDoc(cached.ExternalDocs, s)
}

func (s *Schema) publishHydratedChildren(drCtx *DrContext) {
if s == nil || drCtx == nil || drCtx.ObjectChan == nil {
return
}
visited := map[*Schema]struct{}{s: {}}
s.publishChildSchemas(drCtx, visited)
}

func (s *Schema) publishChildSchemas(drCtx *DrContext, visited map[*Schema]struct{}) {
if s == nil {
return
}
s.EnsureChildrenForRead()
if s.XML != nil {
drCtx.ObjectChan <- s.XML
}
if s.ExternalDocs != nil {
drCtx.ObjectChan <- s.ExternalDocs
}
if s.Discriminator != nil {
drCtx.ObjectChan <- s.Discriminator
}

publishProxy := func(proxy *SchemaProxy) {
publishSchemaProxy(proxy, drCtx, visited)
}
for _, proxy := range s.AllOf {
publishProxy(proxy)
}
for _, proxy := range s.OneOf {
publishProxy(proxy)
}
for _, proxy := range s.AnyOf {
publishProxy(proxy)
}
for _, proxy := range s.PrefixItems {
publishProxy(proxy)
}
publishProxy(s.Contains)
publishProxy(s.If)
publishProxy(s.Else)
publishProxy(s.Then)
publishProxy(s.PropertyNames)
publishProxy(s.UnevaluatedItems)
publishProxy(s.Not)

if s.DependentSchemas != nil {
for pair := s.DependentSchemas.First(); pair != nil; pair = pair.Next() {
publishProxy(pair.Value())
}
}
if s.PatternProperties != nil {
for pair := s.PatternProperties.First(); pair != nil; pair = pair.Next() {
publishProxy(pair.Value())
}
}
if s.Properties != nil {
for pair := s.Properties.First(); pair != nil; pair = pair.Next() {
publishProxy(pair.Value())
}
}
publishDynamicSchemaProxy(s.UnevaluatedProperties, drCtx, visited)
publishDynamicSchemaProxy(s.Items, drCtx, visited)
if s.AdditionalProperties != nil {
drCtx.ObjectChan <- s.AdditionalProperties
}
publishDynamicSchemaProxy(s.AdditionalProperties, drCtx, visited)
}

func publishDynamicSchemaProxy(
value *DynamicValue[*base.SchemaProxy, bool, *SchemaProxy, bool],
drCtx *DrContext,
visited map[*Schema]struct{},
) {
if value == nil {
return
}
publishSchemaProxy(value.A, drCtx, visited)
}

func publishSchemaProxy(proxy *SchemaProxy, drCtx *DrContext, visited map[*Schema]struct{}) {
if proxy == nil || proxy.Schema == nil {
return
}
schema := proxy.Schema
if _, ok := visited[schema]; ok {
return
}
visited[schema] = struct{}{}
drCtx.ObjectChan <- schema
schema.publishChildSchemas(drCtx, visited)
}

func schemaFromCache(cached *Schema, parent any, nodeParent any, options schemaCacheCopyOptions) *Schema {
if cached == nil {
return nil
Expand Down
89 changes: 63 additions & 26 deletions model/walk_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,11 +223,8 @@ func (w *DrDocument) LocateModelsByKeyAndValue(key, value *yaml.Node) ([]drV3.Fo
}
}
}
sort.Slice(filteredObjects, func(i, j int) bool {
return filteredObjects[i].GenerateJSONPath() < filteredObjects[j].GenerateJSONPath()
})
if len(filteredObjects) > 0 {
return filteredObjects, nil
return stableLocatedModels(filteredObjects), nil
}
}
return nil, fmt.Errorf("model not found at line %d", origin.LineValue)
Expand Down Expand Up @@ -275,14 +272,8 @@ func (w *DrDocument) LocateModelsByKeyAndValue(key, value *yaml.Node) ([]drV3.Fo
}
}
}
foLen := len(filteredObjects)
if foLen > 0 {
sort.Slice(filteredObjects, func(i, j int) bool {
return filteredObjects[i].GenerateJSONPath() < filteredObjects[j].GenerateJSONPath()
})
}
if foLen > 0 {
return filteredObjects, nil
if len(filteredObjects) > 0 {
return stableLocatedModels(filteredObjects), nil
}
}
}
Expand Down Expand Up @@ -311,7 +302,7 @@ func (w *DrDocument) LocateModel(node *yaml.Node) ([]drV3.Foundational, error) {
result = append(result, f)
}
}
return result, nil
return stableLocatedModels(result), nil
}
return nil, fmt.Errorf("model not found at line %d", node.Line)
}
Expand All @@ -335,23 +326,69 @@ func (w *DrDocument) LocateModelByLine(line int) ([]drV3.Foundational, error) {
result = append(result, f)
}
}
// order by line number
sort.Slice(result, func(i, j int) bool {
knA := result[i].GetKeyNode()
knB := result[j].GetKeyNode()
if knA != nil && knB != nil {
return result[i].GetKeyNode().Line < result[j].GetKeyNode().Line
}
if knA == nil && knB != nil {
return false
}
return true
})
return result, nil
return stableLocatedModels(result), nil
}
return nil, fmt.Errorf("model not found at line %d", line)
}

func stableLocatedModels(models []drV3.Foundational) []drV3.Foundational {
if len(models) < 2 {
return models
}

seen := make(map[string]struct{}, len(models))
stable := make([]drV3.Foundational, 0, len(models))
for _, model := range models {
if model == nil {
continue
}
path := model.GenerateJSONPath()
if path != "" {
if _, ok := seen[path]; ok {
continue
}
seen[path] = struct{}{}
}
stable = append(stable, model)
}

sort.Slice(stable, func(i, j int) bool {
return locatedModelSortKey(stable[i]) < locatedModelSortKey(stable[j])
})
return stable
}

func locatedModelSortKey(model drV3.Foundational) string {
if model == nil {
return ""
}
path := model.GenerateJSONPath()
keyNode := model.GetKeyNode()
valueNode := model.GetValueNode()
return fmt.Sprintf("%s|%08d:%08d|%08d:%08d|%T",
path,
nodeLine(keyNode),
nodeColumn(keyNode),
nodeLine(valueNode),
nodeColumn(valueNode),
model,
)
}

func nodeLine(node *yaml.Node) int {
if node == nil {
return 0
}
return node.Line
}

func nodeColumn(node *yaml.Node) int {
if node == nil {
return 0
}
return node.Column
}

// BuildObjectLocationMap builds a map of line numbers to models in the document.
func (w *DrDocument) BuildObjectLocationMap() map[int]any {
objectMap := make(map[int]any)
Expand Down
Loading
Loading