@player-lang/json-language-service 0.0.2-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/cjs/index.cjs +2314 -0
  2. package/dist/cjs/index.cjs.map +1 -0
  3. package/dist/index.legacy-esm.js +2249 -0
  4. package/dist/index.mjs +2249 -0
  5. package/dist/index.mjs.map +1 -0
  6. package/package.json +40 -0
  7. package/src/__tests__/__snapshots__/service.test.ts.snap +213 -0
  8. package/src/__tests__/service.test.ts +298 -0
  9. package/src/constants.ts +38 -0
  10. package/src/index.ts +490 -0
  11. package/src/parser/__tests__/parse.test.ts +18 -0
  12. package/src/parser/document.ts +456 -0
  13. package/src/parser/edits.ts +31 -0
  14. package/src/parser/index.ts +38 -0
  15. package/src/parser/jsonParseErrors.ts +69 -0
  16. package/src/parser/types.ts +314 -0
  17. package/src/parser/utils.ts +94 -0
  18. package/src/plugins/__tests__/asset-wrapper-array-plugin.test.ts +112 -0
  19. package/src/plugins/__tests__/binding-schema-plugin.test.ts +62 -0
  20. package/src/plugins/__tests__/duplicate-id-plugin.test.ts +195 -0
  21. package/src/plugins/__tests__/missing-asset-wrapper-plugin.test.ts +190 -0
  22. package/src/plugins/__tests__/nav-state-plugin.test.ts +136 -0
  23. package/src/plugins/__tests__/view-node-plugin.test.ts +154 -0
  24. package/src/plugins/asset-wrapper-array-plugin.ts +123 -0
  25. package/src/plugins/binding-schema-plugin.ts +289 -0
  26. package/src/plugins/duplicate-id-plugin.ts +158 -0
  27. package/src/plugins/missing-asset-wrapper-plugin.ts +96 -0
  28. package/src/plugins/nav-state-plugin.ts +139 -0
  29. package/src/plugins/view-node-plugin.ts +225 -0
  30. package/src/plugins/xlr-plugin.ts +371 -0
  31. package/src/types.ts +119 -0
  32. package/src/utils.ts +143 -0
  33. package/src/xlr/__tests__/__snapshots__/transform.test.ts.snap +390 -0
  34. package/src/xlr/__tests__/transform.test.ts +108 -0
  35. package/src/xlr/index.ts +3 -0
  36. package/src/xlr/registry.ts +99 -0
  37. package/src/xlr/service.ts +190 -0
  38. package/src/xlr/transforms.ts +169 -0
  39. package/types/constants.d.ts +7 -0
  40. package/types/index.d.ts +69 -0
  41. package/types/parser/document.d.ts +25 -0
  42. package/types/parser/edits.d.ts +10 -0
  43. package/types/parser/index.d.ts +16 -0
  44. package/types/parser/jsonParseErrors.d.ts +27 -0
  45. package/types/parser/types.d.ts +188 -0
  46. package/types/parser/utils.d.ts +26 -0
  47. package/types/plugins/asset-wrapper-array-plugin.d.ts +9 -0
  48. package/types/plugins/binding-schema-plugin.d.ts +15 -0
  49. package/types/plugins/duplicate-id-plugin.d.ts +7 -0
  50. package/types/plugins/missing-asset-wrapper-plugin.d.ts +9 -0
  51. package/types/plugins/nav-state-plugin.d.ts +9 -0
  52. package/types/plugins/view-node-plugin.d.ts +9 -0
  53. package/types/plugins/xlr-plugin.d.ts +7 -0
  54. package/types/types.d.ts +81 -0
  55. package/types/utils.d.ts +24 -0
  56. package/types/xlr/index.d.ts +4 -0
  57. package/types/xlr/registry.d.ts +17 -0
  58. package/types/xlr/service.d.ts +22 -0
  59. package/types/xlr/transforms.d.ts +18 -0
@@ -0,0 +1,456 @@
1
+ import { parseTree, findNodeAtOffset } from "jsonc-parser";
2
+ import type { Node, ParseError } from "jsonc-parser";
3
+ import type { Diagnostic } from "vscode-languageserver-types";
4
+ import type { TextDocument } from "vscode-languageserver-textdocument";
5
+ import type {
6
+ ASTNode,
7
+ ContentASTNode,
8
+ PropertyASTNode,
9
+ ViewASTNode,
10
+ AssetASTNode,
11
+ StringASTNode,
12
+ ArrayASTNode,
13
+ NavigationASTNode,
14
+ FlowASTNode,
15
+ FlowStateASTNode,
16
+ } from "./types";
17
+ import {
18
+ ViewASTNodeImpl,
19
+ StringASTNodeImpl,
20
+ PropertyASTNodeImpl,
21
+ NullASTNodeImpl,
22
+ BooleanASTNodeImpl,
23
+ NumberASTNodeImpl,
24
+ ArrayASTNodeImpl,
25
+ AssetASTNodeImpl,
26
+ ObjectASTNodeImpl,
27
+ ContentASTNodeImpl,
28
+ NavigationASTNodeImpl,
29
+ FlowASTNodeImpl,
30
+ FlowStateASTNodeImpl,
31
+ } from "./types";
32
+ import { convertErrorsToDiags } from "./jsonParseErrors";
33
+
34
+ /** Check if the property is a string */
35
+ function isStringProperty(
36
+ node: PropertyASTNode,
37
+ ): node is PropertyASTNode<StringASTNode> {
38
+ return node.valueNode?.type === "string";
39
+ }
40
+
41
+ /**
42
+ * The top level document
43
+ */
44
+ export class PlayerContent {
45
+ public readonly root: ASTNode;
46
+ public readonly syntaxErrors: Array<Diagnostic>;
47
+ private readonly jsonNodeToNode: Map<Node, ASTNode>;
48
+
49
+ constructor(
50
+ root: ASTNode,
51
+ errors: Array<Diagnostic>,
52
+ jsonToNodeMap: Map<Node, ASTNode>,
53
+ ) {
54
+ this.root = root;
55
+ this.jsonNodeToNode = jsonToNodeMap;
56
+ this.syntaxErrors = errors;
57
+ }
58
+
59
+ getNodeFromOffset(offset: number): ASTNode | undefined {
60
+ const jsonNode = findNodeAtOffset(this.root.jsonNode, offset);
61
+ if (!jsonNode) {
62
+ return;
63
+ }
64
+
65
+ if (this.jsonNodeToNode.has(jsonNode)) {
66
+ return this.jsonNodeToNode.get(jsonNode);
67
+ }
68
+
69
+ // walk up one level to see if you're at an empty property line
70
+ const parentNode = jsonNode.parent as Node;
71
+ if (this.jsonNodeToNode.has(parentNode)) {
72
+ return this.jsonNodeToNode.get(parentNode);
73
+ }
74
+
75
+ // Check for a (great)-grand-parent match
76
+ // This means we're completing a property on one of these nodes
77
+ const grandparentNode = parentNode.parent as Node;
78
+ if (this.jsonNodeToNode.has(grandparentNode)) {
79
+ return this.jsonNodeToNode.get(grandparentNode);
80
+ }
81
+
82
+ const greatGrandparentNode = grandparentNode.parent as Node;
83
+ if (this.jsonNodeToNode.has(greatGrandparentNode)) {
84
+ return this.jsonNodeToNode.get(greatGrandparentNode);
85
+ }
86
+ }
87
+ }
88
+
89
+ export enum ObjType {
90
+ FLOW,
91
+ ASSET,
92
+ ASSET_WRAPPER,
93
+ UNKNOWN,
94
+ }
95
+
96
+ /** Try to identify any object as an Asset or Flow */
97
+ export default function identify(node: Node): ObjType {
98
+ if (node === undefined || node.type !== "object") {
99
+ return ObjType.UNKNOWN;
100
+ }
101
+
102
+ const knownProps = node.children?.reduce((props, childNode) => {
103
+ if (childNode.type === "property" && childNode.children) {
104
+ const [key] = childNode.children;
105
+ props.add(key.value);
106
+ }
107
+
108
+ return props;
109
+ }, new Set<string>());
110
+
111
+ if (knownProps?.has("type")) {
112
+ return ObjType.ASSET;
113
+ }
114
+
115
+ return ObjType.FLOW;
116
+ }
117
+
118
+ /** parse a text document into a player one */
119
+ export function parse(document: TextDocument): PlayerContent {
120
+ const errors: Array<ParseError> = [];
121
+ const jsonToNode = new Map<Node, ASTNode>();
122
+ const root = parseTree(document.getText(), errors, {
123
+ disallowComments: true,
124
+ });
125
+
126
+ // TODO: convert runtime errors to diags
127
+ const diags: Diagnostic[] = convertErrorsToDiags(document, errors);
128
+
129
+ /** parse an asset */
130
+ function parseAsset(node: Node, parent?: ASTNode): AssetASTNode {
131
+ const assetNode = new AssetASTNodeImpl(node, parent);
132
+ node.children?.forEach((prop) => {
133
+ if (prop.type === "property" && prop.children?.length) {
134
+ const [key, val] = prop.children;
135
+
136
+ const keyNode = new StringASTNodeImpl(key);
137
+ const property = new PropertyASTNodeImpl(prop, assetNode, keyNode);
138
+ property.keyNode = new StringASTNodeImpl(key, property);
139
+
140
+ property.valueNode = parseUnknownNode(val, property);
141
+
142
+ if (key.value === "id" && isStringProperty(property)) {
143
+ assetNode.id = property;
144
+ } else if (key.value === "type" && isStringProperty(property)) {
145
+ assetNode.assetType = property;
146
+ }
147
+
148
+ assetNode.properties.push(property);
149
+
150
+ jsonToNode.set(prop, property);
151
+ jsonToNode.set(key, property.keyNode);
152
+ if (property.valueNode) {
153
+ jsonToNode.set(val, property.valueNode);
154
+ }
155
+ }
156
+ });
157
+ return assetNode;
158
+ }
159
+
160
+ /** parse a node that we don't know about */
161
+ function parseUnknownNode(
162
+ node?: Node,
163
+ parent?: ASTNode,
164
+ ): ASTNode | undefined {
165
+ switch (node?.type) {
166
+ case "string": {
167
+ const newNode = new StringASTNodeImpl(node, parent);
168
+ jsonToNode.set(node, newNode);
169
+ return newNode;
170
+ }
171
+
172
+ case "null": {
173
+ const newNode = new NullASTNodeImpl(node, parent);
174
+ jsonToNode.set(node, newNode);
175
+ return newNode;
176
+ }
177
+
178
+ case "boolean": {
179
+ const newNode = new BooleanASTNodeImpl(node, parent);
180
+ jsonToNode.set(node, newNode);
181
+ return newNode;
182
+ }
183
+
184
+ case "number": {
185
+ const newNode = new NumberASTNodeImpl(node, parent);
186
+ jsonToNode.set(node, newNode);
187
+ return newNode;
188
+ }
189
+
190
+ case "array": {
191
+ const arr = new ArrayASTNodeImpl(node, parent);
192
+ node.children?.forEach((arrChild) => {
193
+ const child = parseUnknownNode(arrChild, arr);
194
+ if (child) {
195
+ jsonToNode.set(arrChild, child);
196
+ arr.items.push(child);
197
+ }
198
+ });
199
+ jsonToNode.set(node, arr);
200
+ return arr;
201
+ }
202
+
203
+ case "object": {
204
+ const obj = new ObjectASTNodeImpl(node, parent);
205
+ jsonToNode.set(node, obj);
206
+ node.children?.forEach((prop) => {
207
+ if (prop.type === "property" && prop.children?.length) {
208
+ const [key, val] = prop.children;
209
+ const keyNode = new StringASTNodeImpl(key);
210
+ const propNode = new PropertyASTNodeImpl(prop, obj, keyNode);
211
+ propNode.keyNode = new StringASTNodeImpl(key, propNode);
212
+
213
+ if (val) {
214
+ if (keyNode.value === "asset") {
215
+ propNode.valueNode = parseAsset(val, propNode);
216
+ } else {
217
+ propNode.valueNode = parseUnknownNode(val, propNode);
218
+ }
219
+ }
220
+
221
+ jsonToNode.set(prop, propNode);
222
+ jsonToNode.set(key, propNode.keyNode);
223
+ if (propNode.valueNode) {
224
+ jsonToNode.set(val, propNode.valueNode);
225
+ }
226
+
227
+ obj.properties.push(propNode);
228
+ }
229
+ });
230
+ return obj;
231
+ }
232
+
233
+ default:
234
+ }
235
+ }
236
+
237
+ /** parse a view */
238
+ function parseView(node: Node, parent?: ASTNode): ViewASTNode {
239
+ const viewNode = new ViewASTNodeImpl(node, parent);
240
+
241
+ node.children?.forEach((prop) => {
242
+ if (prop.type === "property" && (prop.children?.length ?? 0) > 0) {
243
+ const [key, val] = prop.children ?? [];
244
+
245
+ const keyNode = new StringASTNodeImpl(key);
246
+ const property = new PropertyASTNodeImpl(prop, viewNode, keyNode);
247
+ property.keyNode = new StringASTNodeImpl(key, property);
248
+
249
+ if (val) {
250
+ property.valueNode = parseUnknownNode(val, property);
251
+ if (key.value === "id" && isStringProperty(property)) {
252
+ viewNode.id = property;
253
+ } else if (key.value === "type" && isStringProperty(property)) {
254
+ viewNode.viewType = property;
255
+ }
256
+ }
257
+
258
+ jsonToNode.set(prop, property);
259
+ jsonToNode.set(key, property.keyNode);
260
+ if (property.valueNode) {
261
+ jsonToNode.set(val, property.valueNode);
262
+ }
263
+
264
+ viewNode.properties.push(property);
265
+ }
266
+ });
267
+
268
+ jsonToNode.set(node, viewNode);
269
+ return viewNode;
270
+ }
271
+
272
+ /** parse a flow state */
273
+ function parseFlowState(node: Node, parent?: ASTNode): FlowStateASTNode {
274
+ const state = new FlowStateASTNodeImpl(node, parent);
275
+ jsonToNode.set(node, state);
276
+
277
+ if (node.type === "object") {
278
+ node.children?.forEach((prop) => {
279
+ if (prop.type === "property" && prop.children?.length) {
280
+ const [key, val] = prop.children;
281
+
282
+ const keyNode = new StringASTNodeImpl(key);
283
+ const property = new PropertyASTNodeImpl(prop, state, keyNode);
284
+ property.keyNode = new StringASTNodeImpl(key, property);
285
+
286
+ if (key.value === "state_type" && val?.type === "string") {
287
+ property.valueNode = parseUnknownNode(val, property);
288
+ state.stateType = property as PropertyASTNode<StringASTNode>;
289
+ } else {
290
+ property.valueNode = parseUnknownNode(val, property);
291
+ }
292
+
293
+ jsonToNode.set(prop, property);
294
+ jsonToNode.set(key, property.keyNode);
295
+ if (property.valueNode) {
296
+ jsonToNode.set(val, property.valueNode);
297
+ }
298
+
299
+ state.properties.push(property);
300
+ }
301
+ });
302
+ }
303
+
304
+ return state;
305
+ }
306
+
307
+ /** parse a flow */
308
+ function parseFlow(node: Node, parent?: ASTNode): FlowASTNode {
309
+ const flow = new FlowASTNodeImpl(node, parent);
310
+ jsonToNode.set(node, flow);
311
+
312
+ if (node.type === "object") {
313
+ node.children?.forEach((prop) => {
314
+ if (prop.type === "property" && prop.children?.length) {
315
+ const [key, val] = prop.children;
316
+
317
+ const keyNode = new StringASTNodeImpl(key);
318
+ const property = new PropertyASTNodeImpl(prop, flow, keyNode);
319
+ property.keyNode = new StringASTNodeImpl(key, property);
320
+
321
+ if (key.value === "startState" && val?.type === "string") {
322
+ property.valueNode = parseUnknownNode(val, property);
323
+ flow.start = property as PropertyASTNode<StringASTNode>;
324
+ } else if (
325
+ val?.type === "object" &&
326
+ property.keyNode.value !== "onStart" &&
327
+ property.keyNode.value !== "onEnd"
328
+ ) {
329
+ // Anything else in here is a state-type
330
+ property.valueNode = parseFlowState(val, property);
331
+ flow.states.push(property as PropertyASTNode<FlowStateASTNode>);
332
+ } else {
333
+ property.valueNode = parseUnknownNode(val, property);
334
+ }
335
+
336
+ jsonToNode.set(prop, property);
337
+ jsonToNode.set(key, property.keyNode);
338
+ if (property.valueNode) {
339
+ jsonToNode.set(val, property.valueNode);
340
+ }
341
+
342
+ flow.properties.push(property);
343
+ }
344
+ });
345
+ }
346
+
347
+ return flow;
348
+ }
349
+
350
+ /** parse a nav node */
351
+ function parseNavigation(node: Node, parent?: ASTNode): NavigationASTNode {
352
+ const navNode = new NavigationASTNodeImpl(node, parent);
353
+ jsonToNode.set(node, navNode);
354
+ if (node.type === "object") {
355
+ node.children?.forEach((prop) => {
356
+ if (prop.type === "property" && prop.children?.length) {
357
+ const [key, val] = prop.children;
358
+
359
+ const keyNode = new StringASTNodeImpl(key);
360
+ const property = new PropertyASTNodeImpl(prop, navNode, keyNode);
361
+ property.keyNode = new StringASTNodeImpl(key, property);
362
+
363
+ if (key.value === "BEGIN" && val?.type === "string") {
364
+ property.valueNode = parseUnknownNode(val, property);
365
+ navNode.begin = property as PropertyASTNode<StringASTNode>;
366
+ } else if (val?.type === "object") {
367
+ // Anything else in here is a state-type
368
+ property.valueNode = parseFlow(val, property);
369
+ navNode.flows.push(property as PropertyASTNode<FlowASTNode>);
370
+ }
371
+
372
+ jsonToNode.set(prop, property);
373
+ jsonToNode.set(key, property.keyNode);
374
+ if (property.valueNode) {
375
+ jsonToNode.set(val, property.valueNode);
376
+ }
377
+
378
+ navNode.properties.push(property);
379
+ }
380
+ });
381
+ }
382
+
383
+ return navNode;
384
+ }
385
+
386
+ /** parse a full doc */
387
+ function parseContent(node: Node): ContentASTNode {
388
+ const contentNode = new ContentASTNodeImpl(node, undefined);
389
+
390
+ if (node.type === "object") {
391
+ node.children?.forEach((childProp) => {
392
+ if (childProp.type === "property" && childProp.children?.length) {
393
+ const [key, val] = childProp.children;
394
+
395
+ const keyNode = new StringASTNodeImpl(key);
396
+ const property = new PropertyASTNodeImpl(
397
+ childProp,
398
+ contentNode,
399
+ keyNode,
400
+ );
401
+ property.keyNode = new StringASTNodeImpl(key, property);
402
+
403
+ if (key.value === "views" && val?.type === "array") {
404
+ const views = new ArrayASTNodeImpl(val, property);
405
+ val.children?.forEach((view) => {
406
+ const parsedV = parseView(view, views);
407
+ jsonToNode.set(view, parsedV);
408
+ views.items.push(parsedV);
409
+ });
410
+ property.valueNode = views;
411
+ contentNode.views = property as PropertyASTNode<ArrayASTNode>;
412
+ } else if (key.value === "navigation" && val) {
413
+ const nav = parseNavigation(val, property);
414
+ contentNode.navigation =
415
+ property as PropertyASTNode<NavigationASTNode>;
416
+ property.valueNode = nav;
417
+ } else if (val) {
418
+ property.valueNode = parseUnknownNode(val, property);
419
+ }
420
+
421
+ jsonToNode.set(childProp, property);
422
+ jsonToNode.set(key, property.keyNode);
423
+ if (property.valueNode) {
424
+ jsonToNode.set(val, property.valueNode);
425
+ }
426
+
427
+ contentNode.properties.push(property);
428
+ }
429
+ });
430
+ }
431
+
432
+ jsonToNode.set(node, contentNode);
433
+ return contentNode;
434
+ }
435
+
436
+ let rootASTNode: ASTNode = {
437
+ type: "empty",
438
+ value: undefined,
439
+ jsonNode: root,
440
+ };
441
+
442
+ const objType = identify(root);
443
+
444
+ switch (objType) {
445
+ case ObjType.ASSET:
446
+ rootASTNode = parseAsset(root);
447
+ break;
448
+ case ObjType.FLOW:
449
+ rootASTNode = parseContent(root);
450
+ break;
451
+ default:
452
+ break;
453
+ }
454
+
455
+ return new PlayerContent(rootASTNode, diags, jsonToNode);
456
+ }
@@ -0,0 +1,31 @@
1
+ import type { TextDocument } from "vscode-languageserver-textdocument";
2
+ import { TextEdit, Range } from "vscode-languageserver-types";
3
+ import type { StringASTNode, NodeEdit, ASTNode } from "./types";
4
+
5
+ /** Create a NodeEdit by replacing a string */
6
+ export function replaceString(node: StringASTNode, newText: string): NodeEdit {
7
+ return {
8
+ type: "replace",
9
+ node,
10
+ value: newText,
11
+ };
12
+ }
13
+
14
+ /** Get the range for a given node */
15
+ export function toRange(document: TextDocument, node: ASTNode): Range {
16
+ return Range.create(
17
+ document.positionAt(node.jsonNode.offset),
18
+ document.positionAt(node.jsonNode.offset + node.jsonNode.length),
19
+ );
20
+ }
21
+
22
+ /** Convert a NodeEdit to a TextEdit */
23
+ export function toTextEdit(document: TextDocument, edit: NodeEdit): TextEdit {
24
+ switch (edit.type) {
25
+ case "replace":
26
+ return TextEdit.replace(toRange(document, edit.node), edit.value);
27
+
28
+ default:
29
+ throw new Error("Dont know how to convert this to a TextEdit");
30
+ }
31
+ }
@@ -0,0 +1,38 @@
1
+ import type { TextDocument } from "vscode-languageserver-textdocument";
2
+ import { getNodeValue as getJSONNodeValue } from "jsonc-parser";
3
+ import type { PlayerContent } from "./document";
4
+ import type { ASTNode } from "./types";
5
+
6
+ export * from "./utils";
7
+ export { parse } from "./document";
8
+ export * from "./types";
9
+ export * from "./document";
10
+
11
+ export interface PlayerContentProvider {
12
+ parse(document: TextDocument): PlayerContent;
13
+ }
14
+
15
+ /** traverse a node and call the visitor for each nested item */
16
+ export async function walk(
17
+ node: ASTNode,
18
+ visitor: (n: ASTNode) => Promise<boolean>,
19
+ ): Promise<void> {
20
+ const queue: ASTNode[] = [node];
21
+ let stop = false;
22
+
23
+ while (queue.length > 0 && !stop) {
24
+ const nodeToVisit = queue.shift();
25
+ if (nodeToVisit?.children) {
26
+ queue.push(...nodeToVisit.children);
27
+ }
28
+
29
+ stop = nodeToVisit ? await visitor(nodeToVisit) : true;
30
+ }
31
+ }
32
+
33
+ /** Get the JSON value for a node */
34
+ export function getNodeValue(node: ASTNode): any {
35
+ return getJSONNodeValue(node.jsonNode);
36
+ }
37
+
38
+ export * from "./edits";
@@ -0,0 +1,69 @@
1
+ import { printParseErrorCode } from "jsonc-parser";
2
+ import {
3
+ Diagnostic,
4
+ Range,
5
+ DiagnosticSeverity,
6
+ } from "vscode-languageserver-types";
7
+
8
+ import type { ParseError } from "jsonc-parser";
9
+ import type { TextDocument } from "vscode-languageserver-textdocument";
10
+
11
+ enum ParseErrorCode {
12
+ InvalidSymbol = 1,
13
+ InvalidNumberFormat = 2,
14
+ PropertyNameExpected = 3,
15
+ ValueExpected = 4,
16
+ ColonExpected = 5,
17
+ CommaExpected = 6,
18
+ CloseBraceExpected = 7,
19
+ CloseBracketExpected = 8,
20
+ EndOfFileExpected = 9,
21
+ InvalidCommentToken = 10,
22
+ UnexpectedEndOfComment = 11,
23
+ UnexpectedEndOfString = 12,
24
+ UnexpectedEndOfNumber = 13,
25
+ InvalidUnicode = 14,
26
+ InvalidEscapeCharacter = 15,
27
+ InvalidCharacter = 16,
28
+ }
29
+
30
+ /** Just what the function says */
31
+ export function prettyPrintParseErrorCode(code: ParseErrorCode): string {
32
+ switch (code) {
33
+ case ParseErrorCode.CommaExpected:
34
+ return `Expected comma`;
35
+ case ParseErrorCode.ColonExpected:
36
+ return `Expected colon`;
37
+ case ParseErrorCode.PropertyNameExpected:
38
+ return `Expected property name`;
39
+ case ParseErrorCode.ValueExpected:
40
+ return `Expected value`;
41
+ case ParseErrorCode.CloseBraceExpected:
42
+ return `Expected }`;
43
+ case ParseErrorCode.EndOfFileExpected:
44
+ return `Expected end of file`;
45
+ case ParseErrorCode.CloseBracketExpected:
46
+ return `Expected ]`;
47
+ case ParseErrorCode.UnexpectedEndOfString:
48
+ return `Expected "`;
49
+ default:
50
+ return printParseErrorCode(code as any);
51
+ }
52
+ }
53
+
54
+ /** Convert any JSON parsing errors to LSP diagnostics */
55
+ export function convertErrorsToDiags(
56
+ document: TextDocument,
57
+ errors: Array<ParseError>,
58
+ ): Array<Diagnostic> {
59
+ return errors.map((parseError) => {
60
+ return Diagnostic.create(
61
+ Range.create(
62
+ document.positionAt(parseError.offset),
63
+ document.positionAt(parseError.offset + parseError.length),
64
+ ),
65
+ prettyPrintParseErrorCode(parseError.error as any as ParseErrorCode),
66
+ DiagnosticSeverity.Error,
67
+ );
68
+ });
69
+ }