Skip to content

Resolve $refs in schema navigation the way validation does - #449

Merged
dmarcotte merged 3 commits into
kson-org:mainfrom
holodorum:fix/schema-navigation-refs
Oct 1, 2026
Merged

dmarcotte merged 3 commits into
kson-org:mainfrom
holodorum:fix/schema-navigation-refs

Conversation

@holodorum

Copy link
Copy Markdown
Collaborator

Schema navigation, which drives hover, go-to-definition and completion, resolved $refs differently from the validator in two cases. Both are fixed so tooling shows the schema validation actually applies.

A schema's own $id was skipped when flattening it. Refs inside a schema's allOf, anyOf, oneOf and if/then/else resolved under the base URI the schema was reached under, so #/$defs/x could pick the root's x instead of the schema's own. Flattening now applies the $id first, as stepping into properties and items already did.

Only one $ref hop was followed. Where a ref led to another ref, navigation stopped at the schema holding it and read the keywords beside it, which the validator ignores. The lookup now follows the chain to its end, ignoring sibling keywords ($id included) and stopping at a ref that fails to resolve or leads back into the chain.

Flattening resolved the $refs in a schema's allOf, anyOf, oneOf and
if/then/else under the base URI the schema was reached under, skipping
its own $id, which only stepping into properties and items applied.
With p a $ref to a schema whose $id gives its anyOf's #/$defs/x its
own object, navigation took the root's x, a string, instead.

Flattening now applies the $id first, as stepping does. The fix goes
there rather than in the id lookup because the $id went missing
however the schema was reached: by its $id, by a JSON pointer, or as
an anyOf branch no $ref leads to. The lookup leaves a node's own $id
to whatever reads the node, as the validator's parser does, and
ResolvedRef's KDoc now says so.

Hover, go-to-definition and the completion list change with it: they
now use the schemas such $refs resolve to, so completing inside p
offers the properties of the target's own x, not the root's.
Resolving a $ref followed only one hop. Where that led to another
$ref, navigation stopped at the schema holding it and read the other
keywords beside it, which validation ignores: with p a $ref to a, and
a holding a $ref to b beside a string type, navigating to p reached
a's string rather than b, the schema validation applies.

The id lookup now follows the chain as the validator does: each $ref
resolves under the base URI its schema is read under, ignoring that
schema's own $id. A $ref that doesn't resolve, or leads back into the
chain, ends it at the schema holding it. Only schema navigation calls
it, so hover, go-to-definition and the completion list change with it:
they now use the schema at the end of the chain.

@dmarcotte dmarcotte left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Good ol' $refs. I left a few notes that hopefully make sense, but the core changes look sound to me

Comment thread src/commonMain/kotlin/org/kson/schema/SchemaIdLookup.kt Outdated
Comment thread src/commonTest/kotlin/org/kson/schema/SchemaIdLookupTest.kt Outdated
@holodorum

Copy link
Copy Markdown
Collaborator Author

Thanks for the review! I've pushed a new commit that clarifies some of the comments, they are more concise and readable now. In that commit I've also updated the SchemaIdLookupTest.

@dmarcotte
dmarcotte merged commit cb6fc5e into kson-org:main Oct 1, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants