vscode-json-languageservice 4.2.1 → 5.0.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.
- package/CHANGELOG.md +7 -1
- package/SECURITY.md +41 -0
- package/lib/esm/jsonContributions.d.ts +17 -17
- package/lib/esm/jsonContributions.js +1 -1
- package/lib/esm/jsonLanguageService.d.ts +29 -29
- package/lib/esm/jsonLanguageService.js +66 -66
- package/lib/esm/jsonLanguageTypes.d.ts +279 -278
- package/lib/esm/jsonLanguageTypes.js +46 -45
- package/lib/esm/jsonSchema.d.ts +89 -70
- package/lib/esm/jsonSchema.js +1 -1
- package/lib/esm/parser/jsonParser.js +1214 -1218
- package/lib/esm/services/configuration.js +528 -528
- package/lib/esm/services/jsonCompletion.js +918 -934
- package/lib/esm/services/jsonDocumentSymbols.js +267 -278
- package/lib/esm/services/jsonFolding.js +120 -121
- package/lib/esm/services/jsonHover.js +109 -112
- package/lib/esm/services/jsonLinks.js +72 -73
- package/lib/esm/services/jsonSchemaService.js +586 -605
- package/lib/esm/services/jsonSelectionRanges.js +61 -61
- package/lib/esm/services/jsonValidation.js +151 -149
- package/lib/esm/utils/colors.js +68 -69
- package/lib/esm/utils/glob.js +124 -124
- package/lib/esm/utils/json.js +42 -42
- package/lib/esm/utils/objects.js +68 -65
- package/lib/esm/utils/strings.js +64 -64
- package/lib/umd/jsonContributions.d.ts +17 -17
- package/lib/umd/jsonContributions.js +12 -12
- package/lib/umd/jsonLanguageService.d.ts +29 -29
- package/lib/umd/jsonLanguageService.js +90 -90
- package/lib/umd/jsonLanguageTypes.d.ts +279 -278
- package/lib/umd/jsonLanguageTypes.js +93 -92
- package/lib/umd/jsonSchema.d.ts +89 -70
- package/lib/umd/jsonSchema.js +12 -12
- package/lib/umd/parser/jsonParser.js +1243 -1237
- package/lib/umd/services/configuration.js +541 -541
- package/lib/umd/services/jsonCompletion.js +932 -947
- package/lib/umd/services/jsonDocumentSymbols.js +281 -291
- package/lib/umd/services/jsonFolding.js +134 -135
- package/lib/umd/services/jsonHover.js +123 -125
- package/lib/umd/services/jsonLinks.js +86 -87
- package/lib/umd/services/jsonSchemaService.js +602 -618
- package/lib/umd/services/jsonSelectionRanges.js +75 -75
- package/lib/umd/services/jsonValidation.js +165 -162
- package/lib/umd/utils/colors.js +84 -85
- package/lib/umd/utils/glob.js +138 -138
- package/lib/umd/utils/json.js +56 -56
- package/lib/umd/utils/objects.js +87 -83
- package/lib/umd/utils/strings.js +82 -82
- package/package.json +10 -10
|
@@ -1,1218 +1,1214 @@
|
|
|
1
|
-
/*---------------------------------------------------------------------------------------------
|
|
2
|
-
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
-
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
|
-
*--------------------------------------------------------------------------------------------*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
})()
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
export
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
return
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
this.
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
this.
|
|
209
|
-
this.
|
|
210
|
-
this.
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
if (
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
if (
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
if (
|
|
361
|
-
validationResult.
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
if (
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
}
|
|
732
|
-
if (
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
if (
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
if (prop) {
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
export function parse(textDocument, config) {
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
function _scanNext() {
|
|
940
|
-
while (true) {
|
|
941
|
-
|
|
942
|
-
_checkScanError();
|
|
943
|
-
switch (
|
|
944
|
-
case 12 /* LineCommentTrivia */:
|
|
945
|
-
case 13 /* BlockCommentTrivia */:
|
|
946
|
-
if (Array.isArray(commentRanges)) {
|
|
947
|
-
commentRanges.push(Range.create(textDocument.positionAt(scanner.getTokenOffset()), textDocument.positionAt(scanner.getTokenOffset() + scanner.getTokenLength())));
|
|
948
|
-
}
|
|
949
|
-
break;
|
|
950
|
-
case 15 /* Trivia */:
|
|
951
|
-
case 14 /* LineBreakTrivia */:
|
|
952
|
-
break;
|
|
953
|
-
default:
|
|
954
|
-
return
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
function _accept(token) {
|
|
959
|
-
if (scanner.getToken() === token) {
|
|
960
|
-
_scanNext();
|
|
961
|
-
return true;
|
|
962
|
-
}
|
|
963
|
-
return false;
|
|
964
|
-
}
|
|
965
|
-
function _errorAtRange(message, code, startOffset, endOffset, severity) {
|
|
966
|
-
if (
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
if (
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
start
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
}
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
if (
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
_error(localize('
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
}
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
_error(localize('
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
if (scanner.
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
case
|
|
1194
|
-
return _finalize(new
|
|
1195
|
-
|
|
1196
|
-
return
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
}
|
|
1216
|
-
}
|
|
1217
|
-
return new JSONDocument(_root, problems, commentRanges);
|
|
1218
|
-
}
|
|
1
|
+
/*---------------------------------------------------------------------------------------------
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
import * as Json from 'jsonc-parser';
|
|
6
|
+
import { isNumber, equals, isBoolean, isString, isDefined, isObject } from '../utils/objects';
|
|
7
|
+
import { extendedRegExp } from '../utils/strings';
|
|
8
|
+
import { ErrorCode, Diagnostic, DiagnosticSeverity, Range } from '../jsonLanguageTypes';
|
|
9
|
+
import * as nls from 'vscode-nls';
|
|
10
|
+
const localize = nls.loadMessageBundle();
|
|
11
|
+
const formats = {
|
|
12
|
+
'color-hex': { errorMessage: localize('colorHexFormatWarning', 'Invalid color format. Use #RGB, #RGBA, #RRGGBB or #RRGGBBAA.'), pattern: /^#([0-9A-Fa-f]{3,4}|([0-9A-Fa-f]{2}){3,4})$/ },
|
|
13
|
+
'date-time': { errorMessage: localize('dateTimeFormatWarning', 'String is not a RFC3339 date-time.'), pattern: /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(Z|(\+|-)([01][0-9]|2[0-3]):([0-5][0-9]))$/i },
|
|
14
|
+
'date': { errorMessage: localize('dateFormatWarning', 'String is not a RFC3339 date.'), pattern: /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/i },
|
|
15
|
+
'time': { errorMessage: localize('timeFormatWarning', 'String is not a RFC3339 time.'), pattern: /^([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(Z|(\+|-)([01][0-9]|2[0-3]):([0-5][0-9]))$/i },
|
|
16
|
+
'email': { errorMessage: localize('emailFormatWarning', 'String is not an e-mail address.'), pattern: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}))$/ },
|
|
17
|
+
'hostname': { errorMessage: localize('hostnameFormatWarning', 'String is not a hostname.'), pattern: /^(?=.{1,253}\.?$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*\.?$/i },
|
|
18
|
+
'ipv4': { errorMessage: localize('ipv4FormatWarning', 'String is not an IPv4 address.'), pattern: /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/ },
|
|
19
|
+
'ipv6': { errorMessage: localize('ipv6FormatWarning', 'String is not an IPv6 address.'), pattern: /^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i },
|
|
20
|
+
};
|
|
21
|
+
export class ASTNodeImpl {
|
|
22
|
+
constructor(parent, offset, length = 0) {
|
|
23
|
+
this.offset = offset;
|
|
24
|
+
this.length = length;
|
|
25
|
+
this.parent = parent;
|
|
26
|
+
}
|
|
27
|
+
get children() {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
toString() {
|
|
31
|
+
return 'type: ' + this.type + ' (' + this.offset + '/' + this.length + ')' + (this.parent ? ' parent: {' + this.parent.toString() + '}' : '');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export class NullASTNodeImpl extends ASTNodeImpl {
|
|
35
|
+
constructor(parent, offset) {
|
|
36
|
+
super(parent, offset);
|
|
37
|
+
this.type = 'null';
|
|
38
|
+
this.value = null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export class BooleanASTNodeImpl extends ASTNodeImpl {
|
|
42
|
+
constructor(parent, boolValue, offset) {
|
|
43
|
+
super(parent, offset);
|
|
44
|
+
this.type = 'boolean';
|
|
45
|
+
this.value = boolValue;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export class ArrayASTNodeImpl extends ASTNodeImpl {
|
|
49
|
+
constructor(parent, offset) {
|
|
50
|
+
super(parent, offset);
|
|
51
|
+
this.type = 'array';
|
|
52
|
+
this.items = [];
|
|
53
|
+
}
|
|
54
|
+
get children() {
|
|
55
|
+
return this.items;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export class NumberASTNodeImpl extends ASTNodeImpl {
|
|
59
|
+
constructor(parent, offset) {
|
|
60
|
+
super(parent, offset);
|
|
61
|
+
this.type = 'number';
|
|
62
|
+
this.isInteger = true;
|
|
63
|
+
this.value = Number.NaN;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export class StringASTNodeImpl extends ASTNodeImpl {
|
|
67
|
+
constructor(parent, offset, length) {
|
|
68
|
+
super(parent, offset, length);
|
|
69
|
+
this.type = 'string';
|
|
70
|
+
this.value = '';
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export class PropertyASTNodeImpl extends ASTNodeImpl {
|
|
74
|
+
constructor(parent, offset, keyNode) {
|
|
75
|
+
super(parent, offset);
|
|
76
|
+
this.type = 'property';
|
|
77
|
+
this.colonOffset = -1;
|
|
78
|
+
this.keyNode = keyNode;
|
|
79
|
+
}
|
|
80
|
+
get children() {
|
|
81
|
+
return this.valueNode ? [this.keyNode, this.valueNode] : [this.keyNode];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export class ObjectASTNodeImpl extends ASTNodeImpl {
|
|
85
|
+
constructor(parent, offset) {
|
|
86
|
+
super(parent, offset);
|
|
87
|
+
this.type = 'object';
|
|
88
|
+
this.properties = [];
|
|
89
|
+
}
|
|
90
|
+
get children() {
|
|
91
|
+
return this.properties;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export function asSchema(schema) {
|
|
95
|
+
if (isBoolean(schema)) {
|
|
96
|
+
return schema ? {} : { "not": {} };
|
|
97
|
+
}
|
|
98
|
+
return schema;
|
|
99
|
+
}
|
|
100
|
+
export var EnumMatch;
|
|
101
|
+
(function (EnumMatch) {
|
|
102
|
+
EnumMatch[EnumMatch["Key"] = 0] = "Key";
|
|
103
|
+
EnumMatch[EnumMatch["Enum"] = 1] = "Enum";
|
|
104
|
+
})(EnumMatch || (EnumMatch = {}));
|
|
105
|
+
class SchemaCollector {
|
|
106
|
+
constructor(focusOffset = -1, exclude) {
|
|
107
|
+
this.focusOffset = focusOffset;
|
|
108
|
+
this.exclude = exclude;
|
|
109
|
+
this.schemas = [];
|
|
110
|
+
}
|
|
111
|
+
add(schema) {
|
|
112
|
+
this.schemas.push(schema);
|
|
113
|
+
}
|
|
114
|
+
merge(other) {
|
|
115
|
+
Array.prototype.push.apply(this.schemas, other.schemas);
|
|
116
|
+
}
|
|
117
|
+
include(node) {
|
|
118
|
+
return (this.focusOffset === -1 || contains(node, this.focusOffset)) && (node !== this.exclude);
|
|
119
|
+
}
|
|
120
|
+
newSub() {
|
|
121
|
+
return new SchemaCollector(-1, this.exclude);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
class NoOpSchemaCollector {
|
|
125
|
+
constructor() { }
|
|
126
|
+
get schemas() { return []; }
|
|
127
|
+
add(schema) { }
|
|
128
|
+
merge(other) { }
|
|
129
|
+
include(node) { return true; }
|
|
130
|
+
newSub() { return this; }
|
|
131
|
+
}
|
|
132
|
+
NoOpSchemaCollector.instance = new NoOpSchemaCollector();
|
|
133
|
+
export class ValidationResult {
|
|
134
|
+
constructor() {
|
|
135
|
+
this.problems = [];
|
|
136
|
+
this.propertiesMatches = 0;
|
|
137
|
+
this.processedProperties = new Set();
|
|
138
|
+
this.propertiesValueMatches = 0;
|
|
139
|
+
this.primaryValueMatches = 0;
|
|
140
|
+
this.enumValueMatch = false;
|
|
141
|
+
this.enumValues = undefined;
|
|
142
|
+
}
|
|
143
|
+
hasProblems() {
|
|
144
|
+
return !!this.problems.length;
|
|
145
|
+
}
|
|
146
|
+
mergeAll(validationResults) {
|
|
147
|
+
for (const validationResult of validationResults) {
|
|
148
|
+
this.merge(validationResult);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
merge(validationResult) {
|
|
152
|
+
this.problems = this.problems.concat(validationResult.problems);
|
|
153
|
+
}
|
|
154
|
+
mergeEnumValues(validationResult) {
|
|
155
|
+
if (!this.enumValueMatch && !validationResult.enumValueMatch && this.enumValues && validationResult.enumValues) {
|
|
156
|
+
this.enumValues = this.enumValues.concat(validationResult.enumValues);
|
|
157
|
+
for (const error of this.problems) {
|
|
158
|
+
if (error.code === ErrorCode.EnumValueMismatch) {
|
|
159
|
+
error.message = localize('enumWarning', 'Value is not accepted. Valid values: {0}.', this.enumValues.map(v => JSON.stringify(v)).join(', '));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
mergePropertyMatch(propertyValidationResult) {
|
|
165
|
+
this.merge(propertyValidationResult);
|
|
166
|
+
this.propertiesMatches++;
|
|
167
|
+
if (propertyValidationResult.enumValueMatch || !propertyValidationResult.hasProblems() && propertyValidationResult.propertiesMatches) {
|
|
168
|
+
this.propertiesValueMatches++;
|
|
169
|
+
}
|
|
170
|
+
if (propertyValidationResult.enumValueMatch && propertyValidationResult.enumValues && propertyValidationResult.enumValues.length === 1) {
|
|
171
|
+
this.primaryValueMatches++;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
mergeProcessedProperties(validationResult) {
|
|
175
|
+
validationResult.processedProperties.forEach(p => this.processedProperties.add(p));
|
|
176
|
+
}
|
|
177
|
+
compare(other) {
|
|
178
|
+
const hasProblems = this.hasProblems();
|
|
179
|
+
if (hasProblems !== other.hasProblems()) {
|
|
180
|
+
return hasProblems ? -1 : 1;
|
|
181
|
+
}
|
|
182
|
+
if (this.enumValueMatch !== other.enumValueMatch) {
|
|
183
|
+
return other.enumValueMatch ? -1 : 1;
|
|
184
|
+
}
|
|
185
|
+
if (this.primaryValueMatches !== other.primaryValueMatches) {
|
|
186
|
+
return this.primaryValueMatches - other.primaryValueMatches;
|
|
187
|
+
}
|
|
188
|
+
if (this.propertiesValueMatches !== other.propertiesValueMatches) {
|
|
189
|
+
return this.propertiesValueMatches - other.propertiesValueMatches;
|
|
190
|
+
}
|
|
191
|
+
return this.propertiesMatches - other.propertiesMatches;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
export function newJSONDocument(root, diagnostics = []) {
|
|
195
|
+
return new JSONDocument(root, diagnostics, []);
|
|
196
|
+
}
|
|
197
|
+
export function getNodeValue(node) {
|
|
198
|
+
return Json.getNodeValue(node);
|
|
199
|
+
}
|
|
200
|
+
export function getNodePath(node) {
|
|
201
|
+
return Json.getNodePath(node);
|
|
202
|
+
}
|
|
203
|
+
export function contains(node, offset, includeRightBound = false) {
|
|
204
|
+
return offset >= node.offset && offset < (node.offset + node.length) || includeRightBound && offset === (node.offset + node.length);
|
|
205
|
+
}
|
|
206
|
+
export class JSONDocument {
|
|
207
|
+
constructor(root, syntaxErrors = [], comments = []) {
|
|
208
|
+
this.root = root;
|
|
209
|
+
this.syntaxErrors = syntaxErrors;
|
|
210
|
+
this.comments = comments;
|
|
211
|
+
}
|
|
212
|
+
getNodeFromOffset(offset, includeRightBound = false) {
|
|
213
|
+
if (this.root) {
|
|
214
|
+
return Json.findNodeAtOffset(this.root, offset, includeRightBound);
|
|
215
|
+
}
|
|
216
|
+
return undefined;
|
|
217
|
+
}
|
|
218
|
+
visit(visitor) {
|
|
219
|
+
if (this.root) {
|
|
220
|
+
const doVisit = (node) => {
|
|
221
|
+
let ctn = visitor(node);
|
|
222
|
+
const children = node.children;
|
|
223
|
+
if (Array.isArray(children)) {
|
|
224
|
+
for (let i = 0; i < children.length && ctn; i++) {
|
|
225
|
+
ctn = doVisit(children[i]);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return ctn;
|
|
229
|
+
};
|
|
230
|
+
doVisit(this.root);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
validate(textDocument, schema, severity = DiagnosticSeverity.Warning) {
|
|
234
|
+
if (this.root && schema) {
|
|
235
|
+
const validationResult = new ValidationResult();
|
|
236
|
+
validate(this.root, schema, validationResult, NoOpSchemaCollector.instance);
|
|
237
|
+
return validationResult.problems.map(p => {
|
|
238
|
+
const range = Range.create(textDocument.positionAt(p.location.offset), textDocument.positionAt(p.location.offset + p.location.length));
|
|
239
|
+
return Diagnostic.create(range, p.message, p.severity ?? severity, p.code);
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
244
|
+
getMatchingSchemas(schema, focusOffset = -1, exclude) {
|
|
245
|
+
const matchingSchemas = new SchemaCollector(focusOffset, exclude);
|
|
246
|
+
if (this.root && schema) {
|
|
247
|
+
validate(this.root, schema, new ValidationResult(), matchingSchemas);
|
|
248
|
+
}
|
|
249
|
+
return matchingSchemas.schemas;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
function validate(n, schema, validationResult, matchingSchemas) {
|
|
253
|
+
if (!n || !matchingSchemas.include(n)) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (n.type === 'property') {
|
|
257
|
+
return validate(n.valueNode, schema, validationResult, matchingSchemas);
|
|
258
|
+
}
|
|
259
|
+
const node = n;
|
|
260
|
+
_validateNode();
|
|
261
|
+
switch (node.type) {
|
|
262
|
+
case 'object':
|
|
263
|
+
_validateObjectNode(node);
|
|
264
|
+
break;
|
|
265
|
+
case 'array':
|
|
266
|
+
_validateArrayNode(node);
|
|
267
|
+
break;
|
|
268
|
+
case 'string':
|
|
269
|
+
_validateStringNode(node);
|
|
270
|
+
break;
|
|
271
|
+
case 'number':
|
|
272
|
+
_validateNumberNode(node);
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
matchingSchemas.add({ node: node, schema: schema });
|
|
276
|
+
function _validateNode() {
|
|
277
|
+
function matchesType(type) {
|
|
278
|
+
return node.type === type || (type === 'integer' && node.type === 'number' && node.isInteger);
|
|
279
|
+
}
|
|
280
|
+
if (Array.isArray(schema.type)) {
|
|
281
|
+
if (!schema.type.some(matchesType)) {
|
|
282
|
+
validationResult.problems.push({
|
|
283
|
+
location: { offset: node.offset, length: node.length },
|
|
284
|
+
message: schema.errorMessage || localize('typeArrayMismatchWarning', 'Incorrect type. Expected one of {0}.', schema.type.join(', '))
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
else if (schema.type) {
|
|
289
|
+
if (!matchesType(schema.type)) {
|
|
290
|
+
validationResult.problems.push({
|
|
291
|
+
location: { offset: node.offset, length: node.length },
|
|
292
|
+
message: schema.errorMessage || localize('typeMismatchWarning', 'Incorrect type. Expected "{0}".', schema.type)
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (Array.isArray(schema.allOf)) {
|
|
297
|
+
for (const subSchemaRef of schema.allOf) {
|
|
298
|
+
validate(node, asSchema(subSchemaRef), validationResult, matchingSchemas);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
const notSchema = asSchema(schema.not);
|
|
302
|
+
if (notSchema) {
|
|
303
|
+
const subValidationResult = new ValidationResult();
|
|
304
|
+
const subMatchingSchemas = matchingSchemas.newSub();
|
|
305
|
+
validate(node, notSchema, subValidationResult, subMatchingSchemas);
|
|
306
|
+
if (!subValidationResult.hasProblems()) {
|
|
307
|
+
validationResult.problems.push({
|
|
308
|
+
location: { offset: node.offset, length: node.length },
|
|
309
|
+
message: localize('notSchemaWarning', "Matches a schema that is not allowed.")
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
for (const ms of subMatchingSchemas.schemas) {
|
|
313
|
+
ms.inverted = !ms.inverted;
|
|
314
|
+
matchingSchemas.add(ms);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
const testAlternatives = (alternatives, maxOneMatch) => {
|
|
318
|
+
const matches = [];
|
|
319
|
+
// remember the best match that is used for error messages
|
|
320
|
+
let bestMatch = undefined;
|
|
321
|
+
for (const subSchemaRef of alternatives) {
|
|
322
|
+
const subSchema = asSchema(subSchemaRef);
|
|
323
|
+
const subValidationResult = new ValidationResult();
|
|
324
|
+
const subMatchingSchemas = matchingSchemas.newSub();
|
|
325
|
+
validate(node, subSchema, subValidationResult, subMatchingSchemas);
|
|
326
|
+
if (!subValidationResult.hasProblems()) {
|
|
327
|
+
matches.push(subSchema);
|
|
328
|
+
}
|
|
329
|
+
if (!bestMatch) {
|
|
330
|
+
bestMatch = { schema: subSchema, validationResult: subValidationResult, matchingSchemas: subMatchingSchemas };
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
if (!maxOneMatch && !subValidationResult.hasProblems() && !bestMatch.validationResult.hasProblems()) {
|
|
334
|
+
// no errors, both are equally good matches
|
|
335
|
+
bestMatch.matchingSchemas.merge(subMatchingSchemas);
|
|
336
|
+
bestMatch.validationResult.propertiesMatches += subValidationResult.propertiesMatches;
|
|
337
|
+
bestMatch.validationResult.propertiesValueMatches += subValidationResult.propertiesValueMatches;
|
|
338
|
+
bestMatch.validationResult.mergeProcessedProperties(subValidationResult);
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
const compareResult = subValidationResult.compare(bestMatch.validationResult);
|
|
342
|
+
if (compareResult > 0) {
|
|
343
|
+
// our node is the best matching so far
|
|
344
|
+
bestMatch = { schema: subSchema, validationResult: subValidationResult, matchingSchemas: subMatchingSchemas };
|
|
345
|
+
}
|
|
346
|
+
else if (compareResult === 0) {
|
|
347
|
+
// there's already a best matching but we are as good
|
|
348
|
+
bestMatch.matchingSchemas.merge(subMatchingSchemas);
|
|
349
|
+
bestMatch.validationResult.mergeEnumValues(subValidationResult);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (matches.length > 1 && maxOneMatch) {
|
|
355
|
+
validationResult.problems.push({
|
|
356
|
+
location: { offset: node.offset, length: 1 },
|
|
357
|
+
message: localize('oneOfWarning', "Matches multiple schemas when only one must validate.")
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
if (bestMatch) {
|
|
361
|
+
validationResult.merge(bestMatch.validationResult);
|
|
362
|
+
validationResult.propertiesMatches += bestMatch.validationResult.propertiesMatches;
|
|
363
|
+
validationResult.propertiesValueMatches += bestMatch.validationResult.propertiesValueMatches;
|
|
364
|
+
validationResult.mergeProcessedProperties(bestMatch.validationResult);
|
|
365
|
+
matchingSchemas.merge(bestMatch.matchingSchemas);
|
|
366
|
+
}
|
|
367
|
+
return matches.length;
|
|
368
|
+
};
|
|
369
|
+
if (Array.isArray(schema.anyOf)) {
|
|
370
|
+
testAlternatives(schema.anyOf, false);
|
|
371
|
+
}
|
|
372
|
+
if (Array.isArray(schema.oneOf)) {
|
|
373
|
+
testAlternatives(schema.oneOf, true);
|
|
374
|
+
}
|
|
375
|
+
const testBranch = (schema) => {
|
|
376
|
+
const subValidationResult = new ValidationResult();
|
|
377
|
+
const subMatchingSchemas = matchingSchemas.newSub();
|
|
378
|
+
validate(node, asSchema(schema), subValidationResult, subMatchingSchemas);
|
|
379
|
+
validationResult.merge(subValidationResult);
|
|
380
|
+
validationResult.propertiesMatches += subValidationResult.propertiesMatches;
|
|
381
|
+
validationResult.propertiesValueMatches += subValidationResult.propertiesValueMatches;
|
|
382
|
+
validationResult.mergeProcessedProperties(subValidationResult);
|
|
383
|
+
matchingSchemas.merge(subMatchingSchemas);
|
|
384
|
+
};
|
|
385
|
+
const testCondition = (ifSchema, thenSchema, elseSchema) => {
|
|
386
|
+
const subSchema = asSchema(ifSchema);
|
|
387
|
+
const subValidationResult = new ValidationResult();
|
|
388
|
+
const subMatchingSchemas = matchingSchemas.newSub();
|
|
389
|
+
validate(node, subSchema, subValidationResult, subMatchingSchemas);
|
|
390
|
+
matchingSchemas.merge(subMatchingSchemas);
|
|
391
|
+
validationResult.mergeProcessedProperties(subValidationResult);
|
|
392
|
+
if (!subValidationResult.hasProblems()) {
|
|
393
|
+
if (thenSchema) {
|
|
394
|
+
testBranch(thenSchema);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
else if (elseSchema) {
|
|
398
|
+
testBranch(elseSchema);
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
const ifSchema = asSchema(schema.if);
|
|
402
|
+
if (ifSchema) {
|
|
403
|
+
testCondition(ifSchema, asSchema(schema.then), asSchema(schema.else));
|
|
404
|
+
}
|
|
405
|
+
if (Array.isArray(schema.enum)) {
|
|
406
|
+
const val = getNodeValue(node);
|
|
407
|
+
let enumValueMatch = false;
|
|
408
|
+
for (const e of schema.enum) {
|
|
409
|
+
if (equals(val, e)) {
|
|
410
|
+
enumValueMatch = true;
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
validationResult.enumValues = schema.enum;
|
|
415
|
+
validationResult.enumValueMatch = enumValueMatch;
|
|
416
|
+
if (!enumValueMatch) {
|
|
417
|
+
validationResult.problems.push({
|
|
418
|
+
location: { offset: node.offset, length: node.length },
|
|
419
|
+
code: ErrorCode.EnumValueMismatch,
|
|
420
|
+
message: schema.errorMessage || localize('enumWarning', 'Value is not accepted. Valid values: {0}.', schema.enum.map(v => JSON.stringify(v)).join(', '))
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
if (isDefined(schema.const)) {
|
|
425
|
+
const val = getNodeValue(node);
|
|
426
|
+
if (!equals(val, schema.const)) {
|
|
427
|
+
validationResult.problems.push({
|
|
428
|
+
location: { offset: node.offset, length: node.length },
|
|
429
|
+
code: ErrorCode.EnumValueMismatch,
|
|
430
|
+
message: schema.errorMessage || localize('constWarning', 'Value must be {0}.', JSON.stringify(schema.const))
|
|
431
|
+
});
|
|
432
|
+
validationResult.enumValueMatch = false;
|
|
433
|
+
}
|
|
434
|
+
else {
|
|
435
|
+
validationResult.enumValueMatch = true;
|
|
436
|
+
}
|
|
437
|
+
validationResult.enumValues = [schema.const];
|
|
438
|
+
}
|
|
439
|
+
let deprecationMessage = schema.deprecationMessage;
|
|
440
|
+
if ((deprecationMessage || schema.deprecated) && node.parent) {
|
|
441
|
+
deprecationMessage = deprecationMessage || localize('deprecated', 'Value is deprecated');
|
|
442
|
+
validationResult.problems.push({
|
|
443
|
+
location: { offset: node.parent.offset, length: node.parent.length },
|
|
444
|
+
severity: DiagnosticSeverity.Warning,
|
|
445
|
+
message: deprecationMessage,
|
|
446
|
+
code: ErrorCode.Deprecated
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
function _validateNumberNode(node) {
|
|
451
|
+
const val = node.value;
|
|
452
|
+
function normalizeFloats(float) {
|
|
453
|
+
const parts = /^(-?\d+)(?:\.(\d+))?(?:e([-+]\d+))?$/.exec(float.toString());
|
|
454
|
+
return parts && {
|
|
455
|
+
value: Number(parts[1] + (parts[2] || '')),
|
|
456
|
+
multiplier: (parts[2]?.length || 0) - (parseInt(parts[3]) || 0)
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
;
|
|
460
|
+
if (isNumber(schema.multipleOf)) {
|
|
461
|
+
let remainder = -1;
|
|
462
|
+
if (Number.isInteger(schema.multipleOf)) {
|
|
463
|
+
remainder = val % schema.multipleOf;
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
let normMultipleOf = normalizeFloats(schema.multipleOf);
|
|
467
|
+
let normValue = normalizeFloats(val);
|
|
468
|
+
if (normMultipleOf && normValue) {
|
|
469
|
+
const multiplier = 10 ** Math.abs(normValue.multiplier - normMultipleOf.multiplier);
|
|
470
|
+
if (normValue.multiplier < normMultipleOf.multiplier) {
|
|
471
|
+
normValue.value *= multiplier;
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
normMultipleOf.value *= multiplier;
|
|
475
|
+
}
|
|
476
|
+
remainder = normValue.value % normMultipleOf.value;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
if (remainder !== 0) {
|
|
480
|
+
validationResult.problems.push({
|
|
481
|
+
location: { offset: node.offset, length: node.length },
|
|
482
|
+
message: localize('multipleOfWarning', 'Value is not divisible by {0}.', schema.multipleOf)
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
function getExclusiveLimit(limit, exclusive) {
|
|
487
|
+
if (isNumber(exclusive)) {
|
|
488
|
+
return exclusive;
|
|
489
|
+
}
|
|
490
|
+
if (isBoolean(exclusive) && exclusive) {
|
|
491
|
+
return limit;
|
|
492
|
+
}
|
|
493
|
+
return undefined;
|
|
494
|
+
}
|
|
495
|
+
function getLimit(limit, exclusive) {
|
|
496
|
+
if (!isBoolean(exclusive) || !exclusive) {
|
|
497
|
+
return limit;
|
|
498
|
+
}
|
|
499
|
+
return undefined;
|
|
500
|
+
}
|
|
501
|
+
const exclusiveMinimum = getExclusiveLimit(schema.minimum, schema.exclusiveMinimum);
|
|
502
|
+
if (isNumber(exclusiveMinimum) && val <= exclusiveMinimum) {
|
|
503
|
+
validationResult.problems.push({
|
|
504
|
+
location: { offset: node.offset, length: node.length },
|
|
505
|
+
message: localize('exclusiveMinimumWarning', 'Value is below the exclusive minimum of {0}.', exclusiveMinimum)
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
const exclusiveMaximum = getExclusiveLimit(schema.maximum, schema.exclusiveMaximum);
|
|
509
|
+
if (isNumber(exclusiveMaximum) && val >= exclusiveMaximum) {
|
|
510
|
+
validationResult.problems.push({
|
|
511
|
+
location: { offset: node.offset, length: node.length },
|
|
512
|
+
message: localize('exclusiveMaximumWarning', 'Value is above the exclusive maximum of {0}.', exclusiveMaximum)
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
const minimum = getLimit(schema.minimum, schema.exclusiveMinimum);
|
|
516
|
+
if (isNumber(minimum) && val < minimum) {
|
|
517
|
+
validationResult.problems.push({
|
|
518
|
+
location: { offset: node.offset, length: node.length },
|
|
519
|
+
message: localize('minimumWarning', 'Value is below the minimum of {0}.', minimum)
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
const maximum = getLimit(schema.maximum, schema.exclusiveMaximum);
|
|
523
|
+
if (isNumber(maximum) && val > maximum) {
|
|
524
|
+
validationResult.problems.push({
|
|
525
|
+
location: { offset: node.offset, length: node.length },
|
|
526
|
+
message: localize('maximumWarning', 'Value is above the maximum of {0}.', maximum)
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
function _validateStringNode(node) {
|
|
531
|
+
if (isNumber(schema.minLength) && node.value.length < schema.minLength) {
|
|
532
|
+
validationResult.problems.push({
|
|
533
|
+
location: { offset: node.offset, length: node.length },
|
|
534
|
+
message: localize('minLengthWarning', 'String is shorter than the minimum length of {0}.', schema.minLength)
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
if (isNumber(schema.maxLength) && node.value.length > schema.maxLength) {
|
|
538
|
+
validationResult.problems.push({
|
|
539
|
+
location: { offset: node.offset, length: node.length },
|
|
540
|
+
message: localize('maxLengthWarning', 'String is longer than the maximum length of {0}.', schema.maxLength)
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
if (isString(schema.pattern)) {
|
|
544
|
+
const regex = extendedRegExp(schema.pattern);
|
|
545
|
+
if (!(regex?.test(node.value))) {
|
|
546
|
+
validationResult.problems.push({
|
|
547
|
+
location: { offset: node.offset, length: node.length },
|
|
548
|
+
message: schema.patternErrorMessage || schema.errorMessage || localize('patternWarning', 'String does not match the pattern of "{0}".', schema.pattern)
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
if (schema.format) {
|
|
553
|
+
switch (schema.format) {
|
|
554
|
+
case 'uri':
|
|
555
|
+
case 'uri-reference':
|
|
556
|
+
{
|
|
557
|
+
let errorMessage;
|
|
558
|
+
if (!node.value) {
|
|
559
|
+
errorMessage = localize('uriEmpty', 'URI expected.');
|
|
560
|
+
}
|
|
561
|
+
else {
|
|
562
|
+
const match = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/.exec(node.value);
|
|
563
|
+
if (!match) {
|
|
564
|
+
errorMessage = localize('uriMissing', 'URI is expected.');
|
|
565
|
+
}
|
|
566
|
+
else if (!match[2] && schema.format === 'uri') {
|
|
567
|
+
errorMessage = localize('uriSchemeMissing', 'URI with a scheme is expected.');
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
if (errorMessage) {
|
|
571
|
+
validationResult.problems.push({
|
|
572
|
+
location: { offset: node.offset, length: node.length },
|
|
573
|
+
message: schema.patternErrorMessage || schema.errorMessage || localize('uriFormatWarning', 'String is not a URI: {0}', errorMessage)
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
break;
|
|
578
|
+
case 'color-hex':
|
|
579
|
+
case 'date-time':
|
|
580
|
+
case 'date':
|
|
581
|
+
case 'time':
|
|
582
|
+
case 'email':
|
|
583
|
+
case 'hostname':
|
|
584
|
+
case 'ipv4':
|
|
585
|
+
case 'ipv6':
|
|
586
|
+
const format = formats[schema.format];
|
|
587
|
+
if (!node.value || !format.pattern.exec(node.value)) {
|
|
588
|
+
validationResult.problems.push({
|
|
589
|
+
location: { offset: node.offset, length: node.length },
|
|
590
|
+
message: schema.patternErrorMessage || schema.errorMessage || format.errorMessage
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
default:
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
function _validateArrayNode(node) {
|
|
598
|
+
let prefixItemsSchemas;
|
|
599
|
+
let additionalItemSchema;
|
|
600
|
+
let isSchema_2020_12 = Array.isArray(schema.prefixItems) || (schema.items !== undefined && !Array.isArray(schema.items) && schema.additionalItems === undefined);
|
|
601
|
+
if (isSchema_2020_12) {
|
|
602
|
+
prefixItemsSchemas = schema.prefixItems;
|
|
603
|
+
additionalItemSchema = !Array.isArray(schema.items) ? schema.items : undefined;
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
prefixItemsSchemas = Array.isArray(schema.items) ? schema.items : undefined;
|
|
607
|
+
additionalItemSchema = !Array.isArray(schema.items) ? schema.items : schema.additionalItems;
|
|
608
|
+
}
|
|
609
|
+
let index = 0;
|
|
610
|
+
if (prefixItemsSchemas !== undefined) {
|
|
611
|
+
const max = Math.min(prefixItemsSchemas.length, node.items.length);
|
|
612
|
+
for (; index < max; index++) {
|
|
613
|
+
const subSchemaRef = prefixItemsSchemas[index];
|
|
614
|
+
const subSchema = asSchema(subSchemaRef);
|
|
615
|
+
const itemValidationResult = new ValidationResult();
|
|
616
|
+
const item = node.items[index];
|
|
617
|
+
if (item) {
|
|
618
|
+
validate(item, subSchema, itemValidationResult, matchingSchemas);
|
|
619
|
+
validationResult.mergePropertyMatch(itemValidationResult);
|
|
620
|
+
}
|
|
621
|
+
validationResult.processedProperties.add(String(index));
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
if (additionalItemSchema !== undefined && index < node.items.length) {
|
|
625
|
+
if (typeof additionalItemSchema === 'boolean') {
|
|
626
|
+
if (additionalItemSchema === false) {
|
|
627
|
+
validationResult.problems.push({
|
|
628
|
+
location: { offset: node.offset, length: node.length },
|
|
629
|
+
message: localize('additionalItemsWarning', 'Array has too many items according to schema. Expected {0} or fewer.', index)
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
for (; index < node.items.length; index++) {
|
|
633
|
+
validationResult.processedProperties.add(String(index));
|
|
634
|
+
validationResult.propertiesValueMatches++;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
else {
|
|
638
|
+
for (; index < node.items.length; index++) {
|
|
639
|
+
const itemValidationResult = new ValidationResult();
|
|
640
|
+
validate(node.items[index], additionalItemSchema, itemValidationResult, matchingSchemas);
|
|
641
|
+
validationResult.mergePropertyMatch(itemValidationResult);
|
|
642
|
+
validationResult.processedProperties.add(String(index));
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
const containsSchema = asSchema(schema.contains);
|
|
647
|
+
if (containsSchema) {
|
|
648
|
+
let containsCount = 0;
|
|
649
|
+
for (let index = 0; index < node.items.length; index++) {
|
|
650
|
+
const item = node.items[index];
|
|
651
|
+
const itemValidationResult = new ValidationResult();
|
|
652
|
+
validate(item, containsSchema, itemValidationResult, NoOpSchemaCollector.instance);
|
|
653
|
+
if (!itemValidationResult.hasProblems()) {
|
|
654
|
+
containsCount++;
|
|
655
|
+
if (isSchema_2020_12) {
|
|
656
|
+
validationResult.processedProperties.add(String(index));
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
if (containsCount === 0 && !isNumber(schema.minContains)) {
|
|
661
|
+
validationResult.problems.push({
|
|
662
|
+
location: { offset: node.offset, length: node.length },
|
|
663
|
+
message: schema.errorMessage || localize('requiredItemMissingWarning', 'Array does not contain required item.')
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
if (isNumber(schema.minContains) && containsCount < schema.minContains) {
|
|
667
|
+
validationResult.problems.push({
|
|
668
|
+
location: { offset: node.offset, length: node.length },
|
|
669
|
+
message: localize('minContainsWarning', 'Array has too few items that match the contains contraint. Expected {0} or more.', schema.minContains)
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
if (isNumber(schema.maxContains) && containsCount > schema.maxContains) {
|
|
673
|
+
validationResult.problems.push({
|
|
674
|
+
location: { offset: node.offset, length: node.length },
|
|
675
|
+
message: localize('maxContainsWarning', 'Array has too many items that match the contains contraint. Expected {0} or less.', schema.maxContains)
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
const unevaluatedItems = schema.unevaluatedItems;
|
|
680
|
+
if (unevaluatedItems !== undefined) {
|
|
681
|
+
for (let i = 0; i < node.items.length; i++) {
|
|
682
|
+
if (!validationResult.processedProperties.has(String(i))) {
|
|
683
|
+
if (unevaluatedItems === false) {
|
|
684
|
+
validationResult.problems.push({
|
|
685
|
+
location: { offset: node.offset, length: node.length },
|
|
686
|
+
message: localize('unevaluatedItemsWarning', 'Item does not match any validation rule from the array.')
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
const itemValidationResult = new ValidationResult();
|
|
691
|
+
validate(node.items[i], schema.additionalItems, itemValidationResult, matchingSchemas);
|
|
692
|
+
validationResult.mergePropertyMatch(itemValidationResult);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
validationResult.processedProperties.add(String(i));
|
|
696
|
+
validationResult.propertiesValueMatches++;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
if (isNumber(schema.minItems) && node.items.length < schema.minItems) {
|
|
700
|
+
validationResult.problems.push({
|
|
701
|
+
location: { offset: node.offset, length: node.length },
|
|
702
|
+
message: localize('minItemsWarning', 'Array has too few items. Expected {0} or more.', schema.minItems)
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
if (isNumber(schema.maxItems) && node.items.length > schema.maxItems) {
|
|
706
|
+
validationResult.problems.push({
|
|
707
|
+
location: { offset: node.offset, length: node.length },
|
|
708
|
+
message: localize('maxItemsWarning', 'Array has too many items. Expected {0} or fewer.', schema.maxItems)
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
if (schema.uniqueItems === true) {
|
|
712
|
+
const values = getNodeValue(node);
|
|
713
|
+
const duplicates = values.some((value, index) => {
|
|
714
|
+
return index !== values.lastIndexOf(value);
|
|
715
|
+
});
|
|
716
|
+
if (duplicates) {
|
|
717
|
+
validationResult.problems.push({
|
|
718
|
+
location: { offset: node.offset, length: node.length },
|
|
719
|
+
message: localize('uniqueItemsWarning', 'Array has duplicate items.')
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
function _validateObjectNode(node) {
|
|
725
|
+
const seenKeys = Object.create(null);
|
|
726
|
+
const unprocessedProperties = new Set();
|
|
727
|
+
for (const propertyNode of node.properties) {
|
|
728
|
+
const key = propertyNode.keyNode.value;
|
|
729
|
+
seenKeys[key] = propertyNode.valueNode;
|
|
730
|
+
unprocessedProperties.add(key);
|
|
731
|
+
}
|
|
732
|
+
if (Array.isArray(schema.required)) {
|
|
733
|
+
for (const propertyName of schema.required) {
|
|
734
|
+
if (!seenKeys[propertyName]) {
|
|
735
|
+
const keyNode = node.parent && node.parent.type === 'property' && node.parent.keyNode;
|
|
736
|
+
const location = keyNode ? { offset: keyNode.offset, length: keyNode.length } : { offset: node.offset, length: 1 };
|
|
737
|
+
validationResult.problems.push({
|
|
738
|
+
location: location,
|
|
739
|
+
message: localize('MissingRequiredPropWarning', 'Missing property "{0}".', propertyName)
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
const propertyProcessed = (prop) => {
|
|
745
|
+
unprocessedProperties.delete(prop);
|
|
746
|
+
validationResult.processedProperties.add(prop);
|
|
747
|
+
};
|
|
748
|
+
if (schema.properties) {
|
|
749
|
+
for (const propertyName of Object.keys(schema.properties)) {
|
|
750
|
+
propertyProcessed(propertyName);
|
|
751
|
+
const propertySchema = schema.properties[propertyName];
|
|
752
|
+
const child = seenKeys[propertyName];
|
|
753
|
+
if (child) {
|
|
754
|
+
if (isBoolean(propertySchema)) {
|
|
755
|
+
if (!propertySchema) {
|
|
756
|
+
const propertyNode = child.parent;
|
|
757
|
+
validationResult.problems.push({
|
|
758
|
+
location: { offset: propertyNode.keyNode.offset, length: propertyNode.keyNode.length },
|
|
759
|
+
message: schema.errorMessage || localize('DisallowedExtraPropWarning', 'Property {0} is not allowed.', propertyName)
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
else {
|
|
763
|
+
validationResult.propertiesMatches++;
|
|
764
|
+
validationResult.propertiesValueMatches++;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
else {
|
|
768
|
+
const propertyValidationResult = new ValidationResult();
|
|
769
|
+
validate(child, propertySchema, propertyValidationResult, matchingSchemas);
|
|
770
|
+
validationResult.mergePropertyMatch(propertyValidationResult);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
if (schema.patternProperties) {
|
|
776
|
+
for (const propertyPattern of Object.keys(schema.patternProperties)) {
|
|
777
|
+
const regex = extendedRegExp(propertyPattern);
|
|
778
|
+
if (regex) {
|
|
779
|
+
const processed = [];
|
|
780
|
+
for (const propertyName of unprocessedProperties) {
|
|
781
|
+
if (regex.test(propertyName)) {
|
|
782
|
+
processed.push(propertyName);
|
|
783
|
+
const child = seenKeys[propertyName];
|
|
784
|
+
if (child) {
|
|
785
|
+
const propertySchema = schema.patternProperties[propertyPattern];
|
|
786
|
+
if (isBoolean(propertySchema)) {
|
|
787
|
+
if (!propertySchema) {
|
|
788
|
+
const propertyNode = child.parent;
|
|
789
|
+
validationResult.problems.push({
|
|
790
|
+
location: { offset: propertyNode.keyNode.offset, length: propertyNode.keyNode.length },
|
|
791
|
+
message: schema.errorMessage || localize('DisallowedExtraPropWarning', 'Property {0} is not allowed.', propertyName)
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
else {
|
|
795
|
+
validationResult.propertiesMatches++;
|
|
796
|
+
validationResult.propertiesValueMatches++;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
else {
|
|
800
|
+
const propertyValidationResult = new ValidationResult();
|
|
801
|
+
validate(child, propertySchema, propertyValidationResult, matchingSchemas);
|
|
802
|
+
validationResult.mergePropertyMatch(propertyValidationResult);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
processed.forEach(propertyProcessed);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
const additionalProperties = schema.additionalProperties;
|
|
812
|
+
if (additionalProperties !== undefined && additionalProperties !== true) {
|
|
813
|
+
for (const propertyName of unprocessedProperties) {
|
|
814
|
+
propertyProcessed(propertyName);
|
|
815
|
+
const child = seenKeys[propertyName];
|
|
816
|
+
if (child) {
|
|
817
|
+
if (additionalProperties === false) {
|
|
818
|
+
const propertyNode = child.parent;
|
|
819
|
+
validationResult.problems.push({
|
|
820
|
+
location: { offset: propertyNode.keyNode.offset, length: propertyNode.keyNode.length },
|
|
821
|
+
message: schema.errorMessage || localize('DisallowedExtraPropWarning', 'Property {0} is not allowed.', propertyName)
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
else {
|
|
825
|
+
const propertyValidationResult = new ValidationResult();
|
|
826
|
+
validate(child, additionalProperties, propertyValidationResult, matchingSchemas);
|
|
827
|
+
validationResult.mergePropertyMatch(propertyValidationResult);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
const unevaluatedProperties = schema.unevaluatedProperties;
|
|
833
|
+
if (unevaluatedProperties !== undefined && unevaluatedProperties !== true) {
|
|
834
|
+
const processed = [];
|
|
835
|
+
for (const propertyName of unprocessedProperties) {
|
|
836
|
+
if (!validationResult.processedProperties.has(propertyName)) {
|
|
837
|
+
processed.push(propertyName);
|
|
838
|
+
const child = seenKeys[propertyName];
|
|
839
|
+
if (child) {
|
|
840
|
+
if (unevaluatedProperties === false) {
|
|
841
|
+
const propertyNode = child.parent;
|
|
842
|
+
validationResult.problems.push({
|
|
843
|
+
location: { offset: propertyNode.keyNode.offset, length: propertyNode.keyNode.length },
|
|
844
|
+
message: schema.errorMessage || localize('DisallowedExtraPropWarning', 'Property {0} is not allowed.', propertyName)
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
else {
|
|
848
|
+
const propertyValidationResult = new ValidationResult();
|
|
849
|
+
validate(child, unevaluatedProperties, propertyValidationResult, matchingSchemas);
|
|
850
|
+
validationResult.mergePropertyMatch(propertyValidationResult);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
processed.forEach(propertyProcessed);
|
|
856
|
+
}
|
|
857
|
+
if (isNumber(schema.maxProperties)) {
|
|
858
|
+
if (node.properties.length > schema.maxProperties) {
|
|
859
|
+
validationResult.problems.push({
|
|
860
|
+
location: { offset: node.offset, length: node.length },
|
|
861
|
+
message: localize('MaxPropWarning', 'Object has more properties than limit of {0}.', schema.maxProperties)
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
if (isNumber(schema.minProperties)) {
|
|
866
|
+
if (node.properties.length < schema.minProperties) {
|
|
867
|
+
validationResult.problems.push({
|
|
868
|
+
location: { offset: node.offset, length: node.length },
|
|
869
|
+
message: localize('MinPropWarning', 'Object has fewer properties than the required number of {0}', schema.minProperties)
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
if (schema.dependentRequired) {
|
|
874
|
+
for (const key in schema.dependentRequired) {
|
|
875
|
+
const prop = seenKeys[key];
|
|
876
|
+
const propertyDeps = schema.dependentRequired[key];
|
|
877
|
+
if (prop && Array.isArray(propertyDeps)) {
|
|
878
|
+
_validatePropertyDependencies(key, propertyDeps);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
if (schema.dependentSchemas) {
|
|
883
|
+
for (const key in schema.dependentSchemas) {
|
|
884
|
+
const prop = seenKeys[key];
|
|
885
|
+
const propertyDeps = schema.dependentSchemas[key];
|
|
886
|
+
if (prop && isObject(propertyDeps)) {
|
|
887
|
+
_validatePropertyDependencies(key, propertyDeps);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
if (schema.dependencies) {
|
|
892
|
+
for (const key in schema.dependencies) {
|
|
893
|
+
const prop = seenKeys[key];
|
|
894
|
+
if (prop) {
|
|
895
|
+
_validatePropertyDependencies(key, schema.dependencies[key]);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
const propertyNames = asSchema(schema.propertyNames);
|
|
900
|
+
if (propertyNames) {
|
|
901
|
+
for (const f of node.properties) {
|
|
902
|
+
const key = f.keyNode;
|
|
903
|
+
if (key) {
|
|
904
|
+
validate(key, propertyNames, validationResult, NoOpSchemaCollector.instance);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
function _validatePropertyDependencies(key, propertyDep) {
|
|
909
|
+
if (Array.isArray(propertyDep)) {
|
|
910
|
+
for (const requiredProp of propertyDep) {
|
|
911
|
+
if (!seenKeys[requiredProp]) {
|
|
912
|
+
validationResult.problems.push({
|
|
913
|
+
location: { offset: node.offset, length: node.length },
|
|
914
|
+
message: localize('RequiredDependentPropWarning', 'Object is missing property {0} required by property {1}.', requiredProp, key)
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
else {
|
|
918
|
+
validationResult.propertiesValueMatches++;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
else {
|
|
923
|
+
const propertySchema = asSchema(propertyDep);
|
|
924
|
+
if (propertySchema) {
|
|
925
|
+
const propertyValidationResult = new ValidationResult();
|
|
926
|
+
validate(node, propertySchema, propertyValidationResult, matchingSchemas);
|
|
927
|
+
validationResult.mergePropertyMatch(propertyValidationResult);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
export function parse(textDocument, config) {
|
|
934
|
+
const problems = [];
|
|
935
|
+
let lastProblemOffset = -1;
|
|
936
|
+
const text = textDocument.getText();
|
|
937
|
+
const scanner = Json.createScanner(text, false);
|
|
938
|
+
const commentRanges = config && config.collectComments ? [] : undefined;
|
|
939
|
+
function _scanNext() {
|
|
940
|
+
while (true) {
|
|
941
|
+
const token = scanner.scan();
|
|
942
|
+
_checkScanError();
|
|
943
|
+
switch (token) {
|
|
944
|
+
case 12 /* LineCommentTrivia */:
|
|
945
|
+
case 13 /* BlockCommentTrivia */:
|
|
946
|
+
if (Array.isArray(commentRanges)) {
|
|
947
|
+
commentRanges.push(Range.create(textDocument.positionAt(scanner.getTokenOffset()), textDocument.positionAt(scanner.getTokenOffset() + scanner.getTokenLength())));
|
|
948
|
+
}
|
|
949
|
+
break;
|
|
950
|
+
case 15 /* Trivia */:
|
|
951
|
+
case 14 /* LineBreakTrivia */:
|
|
952
|
+
break;
|
|
953
|
+
default:
|
|
954
|
+
return token;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
function _accept(token) {
|
|
959
|
+
if (scanner.getToken() === token) {
|
|
960
|
+
_scanNext();
|
|
961
|
+
return true;
|
|
962
|
+
}
|
|
963
|
+
return false;
|
|
964
|
+
}
|
|
965
|
+
function _errorAtRange(message, code, startOffset, endOffset, severity = DiagnosticSeverity.Error) {
|
|
966
|
+
if (problems.length === 0 || startOffset !== lastProblemOffset) {
|
|
967
|
+
const range = Range.create(textDocument.positionAt(startOffset), textDocument.positionAt(endOffset));
|
|
968
|
+
problems.push(Diagnostic.create(range, message, severity, code, textDocument.languageId));
|
|
969
|
+
lastProblemOffset = startOffset;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
function _error(message, code, node = undefined, skipUntilAfter = [], skipUntil = []) {
|
|
973
|
+
let start = scanner.getTokenOffset();
|
|
974
|
+
let end = scanner.getTokenOffset() + scanner.getTokenLength();
|
|
975
|
+
if (start === end && start > 0) {
|
|
976
|
+
start--;
|
|
977
|
+
while (start > 0 && /\s/.test(text.charAt(start))) {
|
|
978
|
+
start--;
|
|
979
|
+
}
|
|
980
|
+
end = start + 1;
|
|
981
|
+
}
|
|
982
|
+
_errorAtRange(message, code, start, end);
|
|
983
|
+
if (node) {
|
|
984
|
+
_finalize(node, false);
|
|
985
|
+
}
|
|
986
|
+
if (skipUntilAfter.length + skipUntil.length > 0) {
|
|
987
|
+
let token = scanner.getToken();
|
|
988
|
+
while (token !== 17 /* EOF */) {
|
|
989
|
+
if (skipUntilAfter.indexOf(token) !== -1) {
|
|
990
|
+
_scanNext();
|
|
991
|
+
break;
|
|
992
|
+
}
|
|
993
|
+
else if (skipUntil.indexOf(token) !== -1) {
|
|
994
|
+
break;
|
|
995
|
+
}
|
|
996
|
+
token = _scanNext();
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
return node;
|
|
1000
|
+
}
|
|
1001
|
+
function _checkScanError() {
|
|
1002
|
+
switch (scanner.getTokenError()) {
|
|
1003
|
+
case 4 /* InvalidUnicode */:
|
|
1004
|
+
_error(localize('InvalidUnicode', 'Invalid unicode sequence in string.'), ErrorCode.InvalidUnicode);
|
|
1005
|
+
return true;
|
|
1006
|
+
case 5 /* InvalidEscapeCharacter */:
|
|
1007
|
+
_error(localize('InvalidEscapeCharacter', 'Invalid escape character in string.'), ErrorCode.InvalidEscapeCharacter);
|
|
1008
|
+
return true;
|
|
1009
|
+
case 3 /* UnexpectedEndOfNumber */:
|
|
1010
|
+
_error(localize('UnexpectedEndOfNumber', 'Unexpected end of number.'), ErrorCode.UnexpectedEndOfNumber);
|
|
1011
|
+
return true;
|
|
1012
|
+
case 1 /* UnexpectedEndOfComment */:
|
|
1013
|
+
_error(localize('UnexpectedEndOfComment', 'Unexpected end of comment.'), ErrorCode.UnexpectedEndOfComment);
|
|
1014
|
+
return true;
|
|
1015
|
+
case 2 /* UnexpectedEndOfString */:
|
|
1016
|
+
_error(localize('UnexpectedEndOfString', 'Unexpected end of string.'), ErrorCode.UnexpectedEndOfString);
|
|
1017
|
+
return true;
|
|
1018
|
+
case 6 /* InvalidCharacter */:
|
|
1019
|
+
_error(localize('InvalidCharacter', 'Invalid characters in string. Control characters must be escaped.'), ErrorCode.InvalidCharacter);
|
|
1020
|
+
return true;
|
|
1021
|
+
}
|
|
1022
|
+
return false;
|
|
1023
|
+
}
|
|
1024
|
+
function _finalize(node, scanNext) {
|
|
1025
|
+
node.length = scanner.getTokenOffset() + scanner.getTokenLength() - node.offset;
|
|
1026
|
+
if (scanNext) {
|
|
1027
|
+
_scanNext();
|
|
1028
|
+
}
|
|
1029
|
+
return node;
|
|
1030
|
+
}
|
|
1031
|
+
function _parseArray(parent) {
|
|
1032
|
+
if (scanner.getToken() !== 3 /* OpenBracketToken */) {
|
|
1033
|
+
return undefined;
|
|
1034
|
+
}
|
|
1035
|
+
const node = new ArrayASTNodeImpl(parent, scanner.getTokenOffset());
|
|
1036
|
+
_scanNext(); // consume OpenBracketToken
|
|
1037
|
+
const count = 0;
|
|
1038
|
+
let needsComma = false;
|
|
1039
|
+
while (scanner.getToken() !== 4 /* CloseBracketToken */ && scanner.getToken() !== 17 /* EOF */) {
|
|
1040
|
+
if (scanner.getToken() === 5 /* CommaToken */) {
|
|
1041
|
+
if (!needsComma) {
|
|
1042
|
+
_error(localize('ValueExpected', 'Value expected'), ErrorCode.ValueExpected);
|
|
1043
|
+
}
|
|
1044
|
+
const commaOffset = scanner.getTokenOffset();
|
|
1045
|
+
_scanNext(); // consume comma
|
|
1046
|
+
if (scanner.getToken() === 4 /* CloseBracketToken */) {
|
|
1047
|
+
if (needsComma) {
|
|
1048
|
+
_errorAtRange(localize('TrailingComma', 'Trailing comma'), ErrorCode.TrailingComma, commaOffset, commaOffset + 1);
|
|
1049
|
+
}
|
|
1050
|
+
continue;
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
else if (needsComma) {
|
|
1054
|
+
_error(localize('ExpectedComma', 'Expected comma'), ErrorCode.CommaExpected);
|
|
1055
|
+
}
|
|
1056
|
+
const item = _parseValue(node);
|
|
1057
|
+
if (!item) {
|
|
1058
|
+
_error(localize('PropertyExpected', 'Value expected'), ErrorCode.ValueExpected, undefined, [], [4 /* CloseBracketToken */, 5 /* CommaToken */]);
|
|
1059
|
+
}
|
|
1060
|
+
else {
|
|
1061
|
+
node.items.push(item);
|
|
1062
|
+
}
|
|
1063
|
+
needsComma = true;
|
|
1064
|
+
}
|
|
1065
|
+
if (scanner.getToken() !== 4 /* CloseBracketToken */) {
|
|
1066
|
+
return _error(localize('ExpectedCloseBracket', 'Expected comma or closing bracket'), ErrorCode.CommaOrCloseBacketExpected, node);
|
|
1067
|
+
}
|
|
1068
|
+
return _finalize(node, true);
|
|
1069
|
+
}
|
|
1070
|
+
const keyPlaceholder = new StringASTNodeImpl(undefined, 0, 0);
|
|
1071
|
+
function _parseProperty(parent, keysSeen) {
|
|
1072
|
+
const node = new PropertyASTNodeImpl(parent, scanner.getTokenOffset(), keyPlaceholder);
|
|
1073
|
+
let key = _parseString(node);
|
|
1074
|
+
if (!key) {
|
|
1075
|
+
if (scanner.getToken() === 16 /* Unknown */) {
|
|
1076
|
+
// give a more helpful error message
|
|
1077
|
+
_error(localize('DoubleQuotesExpected', 'Property keys must be doublequoted'), ErrorCode.Undefined);
|
|
1078
|
+
const keyNode = new StringASTNodeImpl(node, scanner.getTokenOffset(), scanner.getTokenLength());
|
|
1079
|
+
keyNode.value = scanner.getTokenValue();
|
|
1080
|
+
key = keyNode;
|
|
1081
|
+
_scanNext(); // consume Unknown
|
|
1082
|
+
}
|
|
1083
|
+
else {
|
|
1084
|
+
return undefined;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
node.keyNode = key;
|
|
1088
|
+
const seen = keysSeen[key.value];
|
|
1089
|
+
if (seen) {
|
|
1090
|
+
_errorAtRange(localize('DuplicateKeyWarning', "Duplicate object key"), ErrorCode.DuplicateKey, node.keyNode.offset, node.keyNode.offset + node.keyNode.length, DiagnosticSeverity.Warning);
|
|
1091
|
+
if (isObject(seen)) {
|
|
1092
|
+
_errorAtRange(localize('DuplicateKeyWarning', "Duplicate object key"), ErrorCode.DuplicateKey, seen.keyNode.offset, seen.keyNode.offset + seen.keyNode.length, DiagnosticSeverity.Warning);
|
|
1093
|
+
}
|
|
1094
|
+
keysSeen[key.value] = true; // if the same key is duplicate again, avoid duplicate error reporting
|
|
1095
|
+
}
|
|
1096
|
+
else {
|
|
1097
|
+
keysSeen[key.value] = node;
|
|
1098
|
+
}
|
|
1099
|
+
if (scanner.getToken() === 6 /* ColonToken */) {
|
|
1100
|
+
node.colonOffset = scanner.getTokenOffset();
|
|
1101
|
+
_scanNext(); // consume ColonToken
|
|
1102
|
+
}
|
|
1103
|
+
else {
|
|
1104
|
+
_error(localize('ColonExpected', 'Colon expected'), ErrorCode.ColonExpected);
|
|
1105
|
+
if (scanner.getToken() === 10 /* StringLiteral */ && textDocument.positionAt(key.offset + key.length).line < textDocument.positionAt(scanner.getTokenOffset()).line) {
|
|
1106
|
+
node.length = key.length;
|
|
1107
|
+
return node;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
const value = _parseValue(node);
|
|
1111
|
+
if (!value) {
|
|
1112
|
+
return _error(localize('ValueExpected', 'Value expected'), ErrorCode.ValueExpected, node, [], [2 /* CloseBraceToken */, 5 /* CommaToken */]);
|
|
1113
|
+
}
|
|
1114
|
+
node.valueNode = value;
|
|
1115
|
+
node.length = value.offset + value.length - node.offset;
|
|
1116
|
+
return node;
|
|
1117
|
+
}
|
|
1118
|
+
function _parseObject(parent) {
|
|
1119
|
+
if (scanner.getToken() !== 1 /* OpenBraceToken */) {
|
|
1120
|
+
return undefined;
|
|
1121
|
+
}
|
|
1122
|
+
const node = new ObjectASTNodeImpl(parent, scanner.getTokenOffset());
|
|
1123
|
+
const keysSeen = Object.create(null);
|
|
1124
|
+
_scanNext(); // consume OpenBraceToken
|
|
1125
|
+
let needsComma = false;
|
|
1126
|
+
while (scanner.getToken() !== 2 /* CloseBraceToken */ && scanner.getToken() !== 17 /* EOF */) {
|
|
1127
|
+
if (scanner.getToken() === 5 /* CommaToken */) {
|
|
1128
|
+
if (!needsComma) {
|
|
1129
|
+
_error(localize('PropertyExpected', 'Property expected'), ErrorCode.PropertyExpected);
|
|
1130
|
+
}
|
|
1131
|
+
const commaOffset = scanner.getTokenOffset();
|
|
1132
|
+
_scanNext(); // consume comma
|
|
1133
|
+
if (scanner.getToken() === 2 /* CloseBraceToken */) {
|
|
1134
|
+
if (needsComma) {
|
|
1135
|
+
_errorAtRange(localize('TrailingComma', 'Trailing comma'), ErrorCode.TrailingComma, commaOffset, commaOffset + 1);
|
|
1136
|
+
}
|
|
1137
|
+
continue;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
else if (needsComma) {
|
|
1141
|
+
_error(localize('ExpectedComma', 'Expected comma'), ErrorCode.CommaExpected);
|
|
1142
|
+
}
|
|
1143
|
+
const property = _parseProperty(node, keysSeen);
|
|
1144
|
+
if (!property) {
|
|
1145
|
+
_error(localize('PropertyExpected', 'Property expected'), ErrorCode.PropertyExpected, undefined, [], [2 /* CloseBraceToken */, 5 /* CommaToken */]);
|
|
1146
|
+
}
|
|
1147
|
+
else {
|
|
1148
|
+
node.properties.push(property);
|
|
1149
|
+
}
|
|
1150
|
+
needsComma = true;
|
|
1151
|
+
}
|
|
1152
|
+
if (scanner.getToken() !== 2 /* CloseBraceToken */) {
|
|
1153
|
+
return _error(localize('ExpectedCloseBrace', 'Expected comma or closing brace'), ErrorCode.CommaOrCloseBraceExpected, node);
|
|
1154
|
+
}
|
|
1155
|
+
return _finalize(node, true);
|
|
1156
|
+
}
|
|
1157
|
+
function _parseString(parent) {
|
|
1158
|
+
if (scanner.getToken() !== 10 /* StringLiteral */) {
|
|
1159
|
+
return undefined;
|
|
1160
|
+
}
|
|
1161
|
+
const node = new StringASTNodeImpl(parent, scanner.getTokenOffset());
|
|
1162
|
+
node.value = scanner.getTokenValue();
|
|
1163
|
+
return _finalize(node, true);
|
|
1164
|
+
}
|
|
1165
|
+
function _parseNumber(parent) {
|
|
1166
|
+
if (scanner.getToken() !== 11 /* NumericLiteral */) {
|
|
1167
|
+
return undefined;
|
|
1168
|
+
}
|
|
1169
|
+
const node = new NumberASTNodeImpl(parent, scanner.getTokenOffset());
|
|
1170
|
+
if (scanner.getTokenError() === 0 /* None */) {
|
|
1171
|
+
const tokenValue = scanner.getTokenValue();
|
|
1172
|
+
try {
|
|
1173
|
+
const numberValue = JSON.parse(tokenValue);
|
|
1174
|
+
if (!isNumber(numberValue)) {
|
|
1175
|
+
return _error(localize('InvalidNumberFormat', 'Invalid number format.'), ErrorCode.Undefined, node);
|
|
1176
|
+
}
|
|
1177
|
+
node.value = numberValue;
|
|
1178
|
+
}
|
|
1179
|
+
catch (e) {
|
|
1180
|
+
return _error(localize('InvalidNumberFormat', 'Invalid number format.'), ErrorCode.Undefined, node);
|
|
1181
|
+
}
|
|
1182
|
+
node.isInteger = tokenValue.indexOf('.') === -1;
|
|
1183
|
+
}
|
|
1184
|
+
return _finalize(node, true);
|
|
1185
|
+
}
|
|
1186
|
+
function _parseLiteral(parent) {
|
|
1187
|
+
let node;
|
|
1188
|
+
switch (scanner.getToken()) {
|
|
1189
|
+
case 7 /* NullKeyword */:
|
|
1190
|
+
return _finalize(new NullASTNodeImpl(parent, scanner.getTokenOffset()), true);
|
|
1191
|
+
case 8 /* TrueKeyword */:
|
|
1192
|
+
return _finalize(new BooleanASTNodeImpl(parent, true, scanner.getTokenOffset()), true);
|
|
1193
|
+
case 9 /* FalseKeyword */:
|
|
1194
|
+
return _finalize(new BooleanASTNodeImpl(parent, false, scanner.getTokenOffset()), true);
|
|
1195
|
+
default:
|
|
1196
|
+
return undefined;
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
function _parseValue(parent) {
|
|
1200
|
+
return _parseArray(parent) || _parseObject(parent) || _parseString(parent) || _parseNumber(parent) || _parseLiteral(parent);
|
|
1201
|
+
}
|
|
1202
|
+
let _root = undefined;
|
|
1203
|
+
const token = _scanNext();
|
|
1204
|
+
if (token !== 17 /* EOF */) {
|
|
1205
|
+
_root = _parseValue(_root);
|
|
1206
|
+
if (!_root) {
|
|
1207
|
+
_error(localize('Invalid symbol', 'Expected a JSON object, array or literal.'), ErrorCode.Undefined);
|
|
1208
|
+
}
|
|
1209
|
+
else if (scanner.getToken() !== 17 /* EOF */) {
|
|
1210
|
+
_error(localize('End of file expected', 'End of file expected.'), ErrorCode.Undefined);
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
return new JSONDocument(_root, problems, commentRanges);
|
|
1214
|
+
}
|