Repository navigation
Build completions lazily and cut evaluation overhead - #161
Open
jdesrosiers wants to merge 7 commits into
Open
jdesrosiers wants to merge 7 commits into
jdesrosiers wants to merge 7 commits into
Conversation
The value sets for each instance location were built during every evaluation, so diagnostics and hover paid for completions on every edit. They're now memoized thunks that are composed during evaluation and built the first time completions or annotations at the location are requested. Validating against the oxlint configuration schema went from about 87ms to 6ms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every keyword that doesn't constrain the value, such as description, was intersected as an unconstrained set, which allocated a full set and copied the accumulated one for no effect. They're now skipped, and the schema's own annotations are added to the accumulated set directly. Property completions for the rules of the oxlint configuration schema went from about 40ms to 18ms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Building a schema's value set started from an unconstrained set and intersected every constraining keyword into it, which allocated a full set and copied the first keyword's set for no effect. The same was true of allOf. They now start from the first set instead. Intersecting also put the members in type order, which completions relied on. The iterator now yields members in type order no matter what order they were added in. Property completions for the rules of the oxlint configuration schema went from about 19ms to 12ms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After every keyword, the schema's completions were replaced with a copy that included the keyword's subschema completions, even for keywords without subschemas. A schema's completions aren't shared until the schema is done, so they're now combined in place. Validating against the oxlint configuration schema went from about 10.5ms to 3.6ms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Completions were merged into every schema that applied on the way up, so the completions for an object's properties were copied once for each $ref and allOf between the schema that declared them and the root. Each schema evaluation now records what it contributes and how its subschemas combine, and the completions at a location are combined when they're requested, visiting only the schemas whose instance location contains it. Validating against the oxlint configuration schema went from about 3.5ms to 1.8ms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The properties keyword added completions for every property it declared each time it was evaluated, which dominated validation for schemas that declare a lot of properties. It now records the declared schemas, and a property's completions are only created when they're requested. Validating against the oxlint configuration schema went from about 1.8ms to 0.4ms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Schemas commonly reference the same subschemas from many places, such as the oxlint configuration schema, where 868 rules reference the same definition. Its value set was built again for each one. Value sets are now cached by schema location for as long as the compiled schema is in use. A set that depends on the dynamic scope is cached for that scope, and one that depends on where a cycle was entered isn't cached. Property completions for the rules of the oxlint configuration schema went from about 15ms to 3ms. Because cached sets share annotation objects, a subschema reached more than once at a location that evaluation doesn't reach now contributes its annotations once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Completion value sets were built for every instance location during every evaluation, so diagnostics, hover and colors paid for completions on every edit. Evaluation now only records what each schema contributes, and completions are built and combined when they're requested.
$refandallOfbetween the schema that declared them and the root. Each schema evaluation now records its own contributions and how its subschemas combine (intersect, union, exclusive union, negate). The completions at a location are combined on request, visiting only the schemas whose instance location contains it. An intermediate commit that combined them in place is superseded by this.propertieskeyword records its subschemas instead of adding completions for every declared property each time it's evaluated. A property's completions are created only when it's requested.description, are skipped instead of being intersected as unconstrained sets. Building starts from the first constraining keyword instead of an unconstrained set, and the same goes forallOf. The value set iterator now yields members in type order, which completions had relied on the extra intersection for.Measured against the oxlint configuration schema (765 KB, 871 rules):
rulesBehavior change
Cached value sets share annotation objects, and annotations are deduplicated by identity. So a subschema reached more than once at a location that evaluation doesn't reach now contributes its annotations once instead of once per reference. For example, in
{ "properties": { "foo": { "allOf": [{ "$ref": "#/$defs/a" }, { "$ref": "#/$defs/a" }] } } }, an absentfoonow hasa's description once instead of twice. Locations that evaluation reaches are unaffected.Testing
Apart from that, behavior isn't intended to change, so there are no new tests. The existing tests pass. For each step, I compared annotations and completions before and after, and they were identical. The comparisons covered the oxlint schema's objects and their properties, including the order of property names, array items and incomplete locations. For the caching step, they also covered a schema document validated against the 2020-12 meta-schema, which exercises
$dynamicRefand recursion. Each comparison was run both on the first query and with the cache warm.🤖 Generated with Claude Code