Skip to content

Add bundled schema support to LSP server and VS Code client#310

Open
holodorum wants to merge 18 commits intokson-org:mainfrom
holodorum:schema-vscode-resource
Open

Add bundled schema support to LSP server and VS Code client#310
holodorum wants to merge 18 commits intokson-org:mainfrom
holodorum:schema-vscode-resource

Conversation

@holodorum
Copy link
Collaborator

Summary

  • Add BundledSchemaProvider for schemas shipped with the extension, matched by file extension — works in both desktop and browser (unlike FileSystemSchemaProvider which requires Node.js fs)
    • Add metaschema support: documents declaring '$schema': 'http://json-schema.org/draft-07/schema#' automatically get hover, completion, and go-to-definition from the bundled Draft 7
      metaschema
    • Add CompositeSchemaProvider to chain providers by priority (file system > bundled > metaschema)
    • Add TextDocumentContentProvider for bundled:// URIs so go-to-definition can navigate into bundled schema content
    • Fix duplicate diagnostics when a schema is present
    • Extension authors can bundle schemas for their KSON dialect by adding a bundledSchema field to the language contribution in package.json

How it works

Schema resolution has two layers:

  1. By file extension — a bundledSchema path in package.json maps file-extensions to a schema file shipped in the extension. This is the bundled equivalent of the existing .kson-schema.kson file system approach.
  2. By $schema content — if no extension match is found, the LSP parses the document's $schema field and matches it against bundled metaschemas. This is how the Draft 7 metaschema kicks in for any .schema.kson file.

Schemas are loaded client-side via vscode.workspace.fs (cross-platform), serialized through LSP initializationOptions, and reconstructed server-side by BundledSchemaProvider.

Introduce BundledSchemaProvider and CompositeSchemaProvider to enable
schemas to be shipped with the extension. CompositeSchemaProvider chains
multiple providers with priority ordering, ensuring user-configured
schemas take precedence over bundled ones.

- Add BundledSchemaProvider for extension-bundled schemas
- Add CompositeSchemaProvider with priority-based provider chaining
- Extend SchemaProvider interface with optional languageId parameter
- Add unit and integration tests for both providers

# Conflicts:
#	tooling/language-server-protocol/src/core/document/KsonDocumentsManager.ts
- Add bundledSchemaLoader utility to load schemas from extension
- Update languageConfig to extract bundledSchema from package.json
- Update clientOptions to pass bundled schemas via initializationOptions
- Update both node and browser clients to load and pass bundled schemas
- Update StatusBarManager to show (bundled) indicator for bundled schemas
- Add enableBundledSchemas setting to package.json
- Update esbuild to copy schemas directory to dist
VS Code failed to navigate to bundled schemas with error:
"Unable to resolve resource bundled://schema/xxx"

The LSP server creates schema documents with bundled:// URIs for schemas
shipped with the extension. VS Code doesn't natively understand this
scheme, so "Go to Definition" failed when pointing to these URIs.

Fix: Register a TextDocumentContentProvider for the bundled:// scheme
that maps bundled://schema/{languageId} URIs to the pre-loaded schema
content.

The bundled:// approach was chosen over file:// URIs because:
- Browser environments may not have access to extension files via file://
- The server doesn't need to know the extension's installation path
- Schema content is loaded once at startup and kept in memory

Added integration tests for bundled schema navigation by bundling the
json draft 7 metaschema.
Change bundled schema URIs from bundled://schema/{languageId} to
bundled://schema/{languageId}.schema.kson. This enables VS Code to
recognize bundled schemas as KSON files and apply syntax highlighting
when navigating to definitions.

- Match schemas by file extension instead of languageId
- Deduplicate BundledSchemaConfig interface across packages
The deactivate() function was being called immediately at module load
instead of being exported for VS Code to call during extension deactivation.
This caused "command already exists" errors when files with bundled schemas
were opened, as the language client tried to re-register commands without
the previous client being properly cleaned up.
By adding it to the subscriptions this deactivation is handled by VSCode
Schema validation already includes parse errors, so use it exclusively
when available instead of combining both sources.
Extract duplicated schema resolution logic in KsonDocumentsManager into
a resolveSchema() helper. Remove unused context parameter from
registerBundledSchemaContentProvider.
Add KsonSchemaDocument extending KsonDocument to encode the domain rule
that only schema files get metaschema resolution. Move extractSchemaId
into KsonDocument, replace resolveSchema with type-aware resolveDocument,
and update all services to explicitly check document type at each call site.
The same 3-line pattern (refreshDocumentSchemas, sendNotification,
sendRequest) was duplicated in onDidChangeWatchedFiles,
onDidChangeConfiguration, and onNotification handlers. Extract into
a single notifySchemaChange() helper.
The onNotification handler duplicated the onDidChangeConfiguration
handler's behavior for the enableBundledSchemas setting, and no client
code sends this notification. Remove the redundant code path.
Replace fragile positional parameters with a named options object.
The old constructor had metaSchemas as 4th parameter after an optional
logger, requiring callers to pass logger explicitly to reach it.
Remove isBundled from the kson/getDocumentSchema server response.
The client already has the schema URI and can derive the bundled
status from the bundled:// scheme directly.
- Move .gitkeep instructions to schemas/README.md (conventionally empty)
- Add trailing newlines to both ksonClientMain.ts files
- Fix brittle regex key normalization in BundledSchemaContentProvider
  with explicit path handling
- Remove backslash handling from findMatchingExtension (LSP URIs always
  use forward slashes)
- Add validation warning for common fileExtension format mistakes
  (leading dot, glob patterns)
Add exact definition.length assertions to tests that use .find() to
locate results. This catches regressions where one source unexpectedly
returns too many or too few results.
Replace fixed setTimeout delays with waitForDiagnostics and
waitForDefinitions polling helpers. This eliminates timing-dependent
false passes (the Go-to-Definition test had an early return that
silently passed when definitions weren't ready yet).
Deduplicate waitForDiagnostics, waitForDefinitions, waitForHover, and
waitForCompletions from 5 test files into common.ts, backed by a
generic pollUntil helper.
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.

1 participant