Skip to content

Commit 3b30816

Browse files
committed
Don't use a class for SchemaDocument
1 parent 900a092 commit 3b30816

File tree

4 files changed

+150
-148
lines changed

4 files changed

+150
-148
lines changed

language-server/src/features/hover.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { MarkupKind } from "vscode-languageserver";
2+
import * as SchemaDocument from "../json-schema-document.js";
23
import * as JsonNode from "../json-node.js";
34
import { getSchemaDocument } from "./schema-registry.js";
45

@@ -17,7 +18,7 @@ export default {
1718
const document = documents.get(textDocument.uri);
1819
const schemaDocument = await getSchemaDocument(connection, document);
1920
const offset = document.offsetAt(position);
20-
const keyword = schemaDocument.findNodeAtOffset(offset);
21+
const keyword = SchemaDocument.findNodeAtOffset(schemaDocument, offset);
2122

2223
// This is a little wierd because we want the hover to be on the keyword, but
2324
// the annotation is actually on the value not the keyword.

language-server/src/features/schema-completion.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { getDialectIds } from "@hyperjump/json-schema/experimental";
21
import { CompletionItemKind } from "vscode-languageserver";
2+
import { getDialectIds } from "@hyperjump/json-schema/experimental";
3+
import * as SchemaDocument from "../json-schema-document.js";
34
import { subscribe } from "../pubsub.js";
45

56

@@ -17,7 +18,7 @@ export default {
1718
const shouldHaveTrailingHash = (uri) => trailingHashDialects.has(uri);
1819

1920
subscribe("completions", async (_message, { schemaDocument, offset, completions }) => {
20-
const currentProperty = schemaDocument.findNodeAtOffset(offset);
21+
const currentProperty = SchemaDocument.findNodeAtOffset(schemaDocument, offset);
2122
if (currentProperty && currentProperty.pointer.endsWith("/$schema")) {
2223
completions.push(...getDialectIds().map((uri) => ({
2324
label: shouldHaveTrailingHash(uri) ? `${uri}#` : uri,

language-server/src/features/schema-registry.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { getDocumentSettings } from "./document-settings.js";
2-
import { JsonSchemaDocument } from "../json-schema-document.js";
2+
import * as SchemaDocument from "../json-schema-document.js";
33

44

55
export default {
@@ -21,7 +21,7 @@ export const getSchemaDocument = async (connection, textDocument) => {
2121

2222
if (version === -1 || version !== textDocument.version) {
2323
const settings = await getDocumentSettings(connection, textDocument.uri);
24-
schemaDocument = await JsonSchemaDocument.fromTextDocument(textDocument, settings.defaultDialect);
24+
schemaDocument = await SchemaDocument.fromTextDocument(textDocument, settings.defaultDialect);
2525

2626
schemaDocuments.set(textDocument.uri, { version: textDocument.version, schemaDocument });
2727
}

language-server/src/json-schema-document.js

Lines changed: 143 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -6,177 +6,168 @@ import * as JsonNode from "./json-node.js";
66
import { uriFragment } from "./util.js";
77

88

9-
export class JsonSchemaDocument {
10-
constructor(textDocument) {
11-
this.textDocument = textDocument;
12-
this.schemaResources = [];
13-
this.errors = [];
14-
}
15-
16-
static async fromTextDocument(textDocument, contextDialectUri) {
17-
const document = new JsonSchemaDocument(textDocument);
18-
19-
const json = textDocument.getText();
20-
if (json) {
21-
const root = parseTree(json, [], {
22-
disallowComments: false,
23-
allowTrailingComma: true,
24-
allowEmptyContent: true
25-
});
26-
27-
document.#buildSchemaResources(root, textDocument.uri, contextDialectUri);
28-
29-
for (const { dialectUri, schemaResource } of document.schemaResources) {
30-
if (!hasDialect(dialectUri)) {
31-
const $schema = JsonNode.get("#/$schema", schemaResource);
32-
if ($schema && JsonNode.typeOf($schema) === "string") {
33-
document.errors.push({
34-
keyword: "https://json-schema.org/keyword/schema",
35-
instanceNode: $schema,
36-
message: "Unknown dialect"
37-
});
38-
} else if (dialectUri) {
39-
document.errors.push({
40-
keyword: "https://json-schema.org/keyword/schema",
41-
instanceNode: schemaResource,
42-
message: "Unknown dialect"
43-
});
44-
} else {
45-
document.errors.push({
46-
keyword: "https://json-schema.org/keyword/schema",
47-
instanceNode: schemaResource,
48-
message: "No dialect"
49-
});
50-
}
9+
const cons = (textDocument) => {
10+
return {
11+
textDocument: textDocument,
12+
schemaResources: [],
13+
errors: []
14+
};
15+
};
5116

52-
continue;
17+
export const fromTextDocument = async (textDocument, contextDialectUri) => {
18+
const document = cons(textDocument);
19+
20+
const json = textDocument.getText();
21+
if (json) {
22+
const root = parseTree(json, [], {
23+
disallowComments: false,
24+
allowTrailingComma: true,
25+
allowEmptyContent: true
26+
});
27+
28+
buildSchemaResources(document, root, textDocument.uri, contextDialectUri);
29+
30+
for (const { dialectUri, schemaResource } of document.schemaResources) {
31+
if (!hasDialect(dialectUri)) {
32+
const $schema = JsonNode.get("#/$schema", schemaResource);
33+
if ($schema && JsonNode.typeOf($schema) === "string") {
34+
document.errors.push({
35+
keyword: "https://json-schema.org/keyword/schema",
36+
instanceNode: $schema,
37+
message: "Unknown dialect"
38+
});
39+
} else if (dialectUri) {
40+
document.errors.push({
41+
keyword: "https://json-schema.org/keyword/schema",
42+
instanceNode: schemaResource,
43+
message: "Unknown dialect"
44+
});
45+
} else {
46+
document.errors.push({
47+
keyword: "https://json-schema.org/keyword/schema",
48+
instanceNode: schemaResource,
49+
message: "No dialect"
50+
});
5351
}
5452

55-
const schema = await getSchema(dialectUri);
56-
const compiled = await compile(schema);
57-
const output = interpret(compiled, schemaResource, BASIC);
58-
if (!output.valid) {
59-
for (const error of output.errors) {
60-
document.errors.push({
61-
keyword: error.keyword,
62-
keywordNode: await getSchema(error.absoluteKeywordLocation),
63-
instanceNode: JsonNode.get(error.instanceLocation, schemaResource)
64-
});
65-
}
53+
continue;
54+
}
55+
56+
const schema = await getSchema(dialectUri);
57+
const compiled = await compile(schema);
58+
const output = interpret(compiled, schemaResource, BASIC);
59+
if (!output.valid) {
60+
for (const error of output.errors) {
61+
document.errors.push({
62+
keyword: error.keyword,
63+
keywordNode: await getSchema(error.absoluteKeywordLocation),
64+
instanceNode: JsonNode.get(error.instanceLocation, schemaResource)
65+
});
6666
}
6767
}
6868
}
69-
70-
return document;
7169
}
7270

73-
#buildSchemaResources(node, uri = "", dialectUri = "", pointer = "", parent = undefined, anchors = {}) {
74-
const jsonNode = JsonNode.cons(uri, pointer, getNodeValue(node), node.type, [], parent, node.offset, node.length);
75-
76-
switch (node.type) {
77-
case "array":
78-
jsonNode.children = node.children.map((child, index) => {
79-
const itemPointer = JsonPointer.append(index, pointer);
80-
return this.#buildSchemaResources(child, uri, dialectUri, itemPointer, jsonNode, anchors);
81-
});
82-
break;
83-
84-
case "object":
85-
if (pointer === "") {
86-
// Resource root
87-
const $schema = nodeStep(node, "$schema");
88-
if ($schema?.type === "string") {
89-
try {
90-
dialectUri = toAbsoluteIri(getNodeValue($schema));
91-
} catch (error) {
92-
// Ignore
93-
}
94-
}
95-
96-
const idToken = keywordNameFor("https://json-schema.org/keyword/id", dialectUri);
97-
const $idNode = idToken && nodeStep(node, idToken);
98-
if ($idNode) {
99-
uri = toAbsoluteIri(resolveIri(getNodeValue($idNode), uri));
100-
jsonNode.baseUri = uri;
101-
}
71+
return document;
72+
};
10273

103-
const legacyIdToken = keywordNameFor("https://json-schema.org/keyword/draft-04/id", dialectUri);
104-
const legacy$idNode = legacyIdToken && nodeStep(node, legacyIdToken);
105-
if (legacy$idNode?.type === "string") {
106-
const legacy$id = getNodeValue(legacy$idNode);
107-
if (legacy$id[0] !== "#") {
108-
uri = toAbsoluteIri(resolveIri(legacy$id, uri));
109-
jsonNode.baseUri = uri;
110-
}
111-
}
112-
} else {
113-
// Check for embedded schema
114-
const embeddedDialectUri = getEmbeddedDialectUri(node, dialectUri);
115-
if (embeddedDialectUri) {
116-
this.#buildSchemaResources(node, uri, embeddedDialectUri);
74+
const buildSchemaResources = (document, node, uri = "", dialectUri = "", pointer = "", parent = undefined, anchors = {}) => {
75+
const jsonNode = JsonNode.cons(uri, pointer, getNodeValue(node), node.type, [], parent, node.offset, node.length);
11776

118-
return JsonNode.cons(uri, pointer, true, "boolean", [], parent, node.offset, node.length);
77+
switch (node.type) {
78+
case "array":
79+
jsonNode.children = node.children.map((child, index) => {
80+
const itemPointer = JsonPointer.append(index, pointer);
81+
return buildSchemaResources(document, child, uri, dialectUri, itemPointer, jsonNode, anchors);
82+
});
83+
break;
84+
85+
case "object":
86+
if (pointer === "") {
87+
// Resource root
88+
const $schema = nodeStep(node, "$schema");
89+
if ($schema?.type === "string") {
90+
try {
91+
dialectUri = toAbsoluteIri(getNodeValue($schema));
92+
} catch (error) {
93+
// Ignore
11994
}
12095
}
12196

122-
const anchorToken = keywordNameFor("https://json-schema.org/keyword/anchor", dialectUri);
123-
const $anchorNode = anchorToken && nodeStep(node, anchorToken);
124-
if ($anchorNode) {
125-
const anchor = getNodeValue($anchorNode);
126-
anchors[anchor] = pointer;
97+
const idToken = keywordNameFor("https://json-schema.org/keyword/id", dialectUri);
98+
const $idNode = idToken && nodeStep(node, idToken);
99+
if ($idNode) {
100+
uri = toAbsoluteIri(resolveIri(getNodeValue($idNode), uri));
101+
jsonNode.baseUri = uri;
127102
}
128103

129-
const legacyAnchorToken = keywordNameFor("https://json-schema.org/keyword/draft-04/id", dialectUri);
130-
const legacyAnchorNode = legacyAnchorToken && nodeStep(node, legacyAnchorToken);
131-
if (legacyAnchorNode) {
132-
const anchor = getNodeValue(legacyAnchorNode);
133-
if (anchor[0] === "#") {
134-
anchors[uriFragment(anchor)] = pointer;
104+
const legacyIdToken = keywordNameFor("https://json-schema.org/keyword/draft-04/id", dialectUri);
105+
const legacy$idNode = legacyIdToken && nodeStep(node, legacyIdToken);
106+
if (legacy$idNode?.type === "string") {
107+
const legacy$id = getNodeValue(legacy$idNode);
108+
if (legacy$id[0] !== "#") {
109+
uri = toAbsoluteIri(resolveIri(legacy$id, uri));
110+
jsonNode.baseUri = uri;
135111
}
136112
}
113+
} else {
114+
// Check for embedded schema
115+
const embeddedDialectUri = getEmbeddedDialectUri(node, dialectUri);
116+
if (embeddedDialectUri) {
117+
buildSchemaResources(document, node, uri, embeddedDialectUri);
137118

138-
for (const child of node.children) {
139-
const propertyPointer = JsonPointer.append(getNodeValue(child.children[0]), pointer);
140-
const propertyNode = this.#buildSchemaResources(child, uri, dialectUri, propertyPointer, jsonNode, anchors);
119+
return JsonNode.cons(uri, pointer, true, "boolean", [], parent, node.offset, node.length);
120+
}
121+
}
141122

142-
if (propertyNode) {
143-
jsonNode.children.push(propertyNode);
144-
}
123+
const anchorToken = keywordNameFor("https://json-schema.org/keyword/anchor", dialectUri);
124+
const $anchorNode = anchorToken && nodeStep(node, anchorToken);
125+
if ($anchorNode) {
126+
const anchor = getNodeValue($anchorNode);
127+
anchors[anchor] = pointer;
128+
}
129+
130+
const legacyAnchorToken = keywordNameFor("https://json-schema.org/keyword/draft-04/id", dialectUri);
131+
const legacyAnchorNode = legacyAnchorToken && nodeStep(node, legacyAnchorToken);
132+
if (legacyAnchorNode) {
133+
const anchor = getNodeValue(legacyAnchorNode);
134+
if (anchor[0] === "#") {
135+
anchors[uriFragment(anchor)] = pointer;
145136
}
146-
break;
137+
}
138+
139+
for (const child of node.children) {
140+
const propertyPointer = JsonPointer.append(getNodeValue(child.children[0]), pointer);
141+
const propertyNode = buildSchemaResources(document, child, uri, dialectUri, propertyPointer, jsonNode, anchors);
147142

148-
case "property":
149-
if (node.children.length !== 2) {
150-
return;
143+
if (propertyNode) {
144+
jsonNode.children.push(propertyNode);
151145
}
146+
}
147+
break;
152148

153-
jsonNode.children = node.children.map((child) => {
154-
return this.#buildSchemaResources(child, uri, dialectUri, pointer, jsonNode, anchors);
155-
});
156-
break;
157-
}
149+
case "property":
150+
if (node.children.length !== 2) {
151+
return;
152+
}
158153

159-
if (jsonNode.pointer === "") {
160-
this.schemaResources.push({
161-
schemaResource: jsonNode,
162-
dialectUri: dialectUri,
163-
baseUri: uri,
164-
anchors: anchors
154+
jsonNode.children = node.children.map((child) => {
155+
return buildSchemaResources(document, child, uri, dialectUri, pointer, jsonNode, anchors);
165156
});
166-
}
167-
168-
return jsonNode;
157+
break;
169158
}
170159

171-
findNodeAtOffset(offset) {
172-
for (const { schemaResource } of this.schemaResources) {
173-
const node = _findNodeAtOffset(schemaResource, offset);
174-
if (node) {
175-
return node;
176-
}
177-
}
160+
if (jsonNode.pointer === "") {
161+
document.schemaResources.push({
162+
schemaResource: jsonNode,
163+
dialectUri: dialectUri,
164+
baseUri: uri,
165+
anchors: anchors
166+
});
178167
}
179-
}
168+
169+
return jsonNode;
170+
};
180171

181172
const getEmbeddedDialectUri = (node, dialectUri) => {
182173
const $schema = nodeStep(node, "$schema");
@@ -202,6 +193,15 @@ const getEmbeddedDialectUri = (node, dialectUri) => {
202193
}
203194
};
204195

196+
export const findNodeAtOffset = (document, offset) => {
197+
for (const { schemaResource } of document.schemaResources) {
198+
const node = _findNodeAtOffset(schemaResource, offset);
199+
if (node) {
200+
return node;
201+
}
202+
}
203+
};
204+
205205
const _findNodeAtOffset = (node, offset, includeRightBound = false) => {
206206
if (contains(node, offset, includeRightBound)) {
207207
for (let i = 0; i < node.children.length && node.children[i].offset <= offset; i++) {

0 commit comments

Comments
 (0)