vscode-json-languageservice 5.0.0 → 5.1.1

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 (48) hide show
  1. package/CHANGELOG.md +10 -2
  2. package/lib/esm/jsonContributions.d.ts +17 -17
  3. package/lib/esm/jsonContributions.js +1 -1
  4. package/lib/esm/jsonLanguageService.d.ts +29 -29
  5. package/lib/esm/jsonLanguageService.js +66 -66
  6. package/lib/esm/jsonLanguageTypes.d.ts +292 -279
  7. package/lib/esm/jsonLanguageTypes.js +55 -46
  8. package/lib/esm/jsonSchema.d.ts +89 -89
  9. package/lib/esm/jsonSchema.js +1 -1
  10. package/lib/esm/parser/jsonParser.js +1236 -1214
  11. package/lib/esm/services/configuration.js +528 -528
  12. package/lib/esm/services/jsonCompletion.js +924 -918
  13. package/lib/esm/services/jsonDocumentSymbols.js +267 -267
  14. package/lib/esm/services/jsonFolding.js +120 -120
  15. package/lib/esm/services/jsonHover.js +109 -109
  16. package/lib/esm/services/jsonLinks.js +72 -72
  17. package/lib/esm/services/jsonSchemaService.js +593 -586
  18. package/lib/esm/services/jsonSelectionRanges.js +61 -61
  19. package/lib/esm/services/jsonValidation.js +151 -151
  20. package/lib/esm/utils/colors.js +68 -68
  21. package/lib/esm/utils/glob.js +124 -124
  22. package/lib/esm/utils/json.js +42 -42
  23. package/lib/esm/utils/objects.js +68 -68
  24. package/lib/esm/utils/strings.js +79 -64
  25. package/lib/umd/jsonContributions.d.ts +17 -17
  26. package/lib/umd/jsonContributions.js +12 -12
  27. package/lib/umd/jsonLanguageService.d.ts +29 -29
  28. package/lib/umd/jsonLanguageService.js +94 -90
  29. package/lib/umd/jsonLanguageTypes.d.ts +292 -279
  30. package/lib/umd/jsonLanguageTypes.js +103 -93
  31. package/lib/umd/jsonSchema.d.ts +89 -89
  32. package/lib/umd/jsonSchema.js +12 -12
  33. package/lib/umd/parser/jsonParser.js +1265 -1243
  34. package/lib/umd/services/configuration.js +541 -541
  35. package/lib/umd/services/jsonCompletion.js +938 -932
  36. package/lib/umd/services/jsonDocumentSymbols.js +281 -281
  37. package/lib/umd/services/jsonFolding.js +134 -134
  38. package/lib/umd/services/jsonHover.js +123 -123
  39. package/lib/umd/services/jsonLinks.js +86 -86
  40. package/lib/umd/services/jsonSchemaService.js +609 -602
  41. package/lib/umd/services/jsonSelectionRanges.js +75 -75
  42. package/lib/umd/services/jsonValidation.js +165 -165
  43. package/lib/umd/utils/colors.js +84 -84
  44. package/lib/umd/utils/glob.js +138 -138
  45. package/lib/umd/utils/json.js +56 -56
  46. package/lib/umd/utils/objects.js +87 -87
  47. package/lib/umd/utils/strings.js +98 -82
  48. package/package.json +11 -10
@@ -1,586 +1,593 @@
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 { URI } from 'vscode-uri';
7
- import * as Strings from '../utils/strings';
8
- import * as Parser from '../parser/jsonParser';
9
- import * as nls from 'vscode-nls';
10
- import { createRegex } from '../utils/glob';
11
- import { isObject, isString } from '../utils/objects';
12
- const localize = nls.loadMessageBundle();
13
- const BANG = '!';
14
- const PATH_SEP = '/';
15
- class FilePatternAssociation {
16
- constructor(pattern, uris) {
17
- this.globWrappers = [];
18
- try {
19
- for (let patternString of pattern) {
20
- const include = patternString[0] !== BANG;
21
- if (!include) {
22
- patternString = patternString.substring(1);
23
- }
24
- if (patternString.length > 0) {
25
- if (patternString[0] === PATH_SEP) {
26
- patternString = patternString.substring(1);
27
- }
28
- this.globWrappers.push({
29
- regexp: createRegex('**/' + patternString, { extended: true, globstar: true }),
30
- include: include,
31
- });
32
- }
33
- }
34
- ;
35
- this.uris = uris;
36
- }
37
- catch (e) {
38
- this.globWrappers.length = 0;
39
- this.uris = [];
40
- }
41
- }
42
- matchesPattern(fileName) {
43
- let match = false;
44
- for (const { regexp, include } of this.globWrappers) {
45
- if (regexp.test(fileName)) {
46
- match = include;
47
- }
48
- }
49
- return match;
50
- }
51
- getURIs() {
52
- return this.uris;
53
- }
54
- }
55
- class SchemaHandle {
56
- constructor(service, uri, unresolvedSchemaContent) {
57
- this.service = service;
58
- this.uri = uri;
59
- this.dependencies = new Set();
60
- this.anchors = undefined;
61
- if (unresolvedSchemaContent) {
62
- this.unresolvedSchema = this.service.promise.resolve(new UnresolvedSchema(unresolvedSchemaContent));
63
- }
64
- }
65
- getUnresolvedSchema() {
66
- if (!this.unresolvedSchema) {
67
- this.unresolvedSchema = this.service.loadSchema(this.uri);
68
- }
69
- return this.unresolvedSchema;
70
- }
71
- getResolvedSchema() {
72
- if (!this.resolvedSchema) {
73
- this.resolvedSchema = this.getUnresolvedSchema().then(unresolved => {
74
- return this.service.resolveSchemaContent(unresolved, this);
75
- });
76
- }
77
- return this.resolvedSchema;
78
- }
79
- clearSchema() {
80
- const hasChanges = !!this.unresolvedSchema;
81
- this.resolvedSchema = undefined;
82
- this.unresolvedSchema = undefined;
83
- this.dependencies.clear();
84
- this.anchors = undefined;
85
- return hasChanges;
86
- }
87
- }
88
- export class UnresolvedSchema {
89
- constructor(schema, errors = []) {
90
- this.schema = schema;
91
- this.errors = errors;
92
- }
93
- }
94
- export class ResolvedSchema {
95
- constructor(schema, errors = [], warnings = [], schemaDraft) {
96
- this.schema = schema;
97
- this.errors = errors;
98
- this.warnings = warnings;
99
- this.schemaDraft = schemaDraft;
100
- }
101
- getSection(path) {
102
- const schemaRef = this.getSectionRecursive(path, this.schema);
103
- if (schemaRef) {
104
- return Parser.asSchema(schemaRef);
105
- }
106
- return undefined;
107
- }
108
- getSectionRecursive(path, schema) {
109
- if (!schema || typeof schema === 'boolean' || path.length === 0) {
110
- return schema;
111
- }
112
- const next = path.shift();
113
- if (schema.properties && typeof schema.properties[next]) {
114
- return this.getSectionRecursive(path, schema.properties[next]);
115
- }
116
- else if (schema.patternProperties) {
117
- for (const pattern of Object.keys(schema.patternProperties)) {
118
- const regex = Strings.extendedRegExp(pattern);
119
- if (regex?.test(next)) {
120
- return this.getSectionRecursive(path, schema.patternProperties[pattern]);
121
- }
122
- }
123
- }
124
- else if (typeof schema.additionalProperties === 'object') {
125
- return this.getSectionRecursive(path, schema.additionalProperties);
126
- }
127
- else if (next.match('[0-9]+')) {
128
- if (Array.isArray(schema.items)) {
129
- const index = parseInt(next, 10);
130
- if (!isNaN(index) && schema.items[index]) {
131
- return this.getSectionRecursive(path, schema.items[index]);
132
- }
133
- }
134
- else if (schema.items) {
135
- return this.getSectionRecursive(path, schema.items);
136
- }
137
- }
138
- return undefined;
139
- }
140
- }
141
- export class JSONSchemaService {
142
- constructor(requestService, contextService, promiseConstructor) {
143
- this.contextService = contextService;
144
- this.requestService = requestService;
145
- this.promiseConstructor = promiseConstructor || Promise;
146
- this.callOnDispose = [];
147
- this.contributionSchemas = {};
148
- this.contributionAssociations = [];
149
- this.schemasById = {};
150
- this.filePatternAssociations = [];
151
- this.registeredSchemasIds = {};
152
- }
153
- getRegisteredSchemaIds(filter) {
154
- return Object.keys(this.registeredSchemasIds).filter(id => {
155
- const scheme = URI.parse(id).scheme;
156
- return scheme !== 'schemaservice' && (!filter || filter(scheme));
157
- });
158
- }
159
- get promise() {
160
- return this.promiseConstructor;
161
- }
162
- dispose() {
163
- while (this.callOnDispose.length > 0) {
164
- this.callOnDispose.pop()();
165
- }
166
- }
167
- onResourceChange(uri) {
168
- // always clear this local cache when a resource changes
169
- this.cachedSchemaForResource = undefined;
170
- let hasChanges = false;
171
- uri = normalizeId(uri);
172
- const toWalk = [uri];
173
- const all = Object.keys(this.schemasById).map(key => this.schemasById[key]);
174
- while (toWalk.length) {
175
- const curr = toWalk.pop();
176
- for (let i = 0; i < all.length; i++) {
177
- const handle = all[i];
178
- if (handle && (handle.uri === curr || handle.dependencies.has(curr))) {
179
- if (handle.uri !== curr) {
180
- toWalk.push(handle.uri);
181
- }
182
- if (handle.clearSchema()) {
183
- hasChanges = true;
184
- }
185
- all[i] = undefined;
186
- }
187
- }
188
- }
189
- return hasChanges;
190
- }
191
- setSchemaContributions(schemaContributions) {
192
- if (schemaContributions.schemas) {
193
- const schemas = schemaContributions.schemas;
194
- for (const id in schemas) {
195
- const normalizedId = normalizeId(id);
196
- this.contributionSchemas[normalizedId] = this.addSchemaHandle(normalizedId, schemas[id]);
197
- }
198
- }
199
- if (Array.isArray(schemaContributions.schemaAssociations)) {
200
- const schemaAssociations = schemaContributions.schemaAssociations;
201
- for (let schemaAssociation of schemaAssociations) {
202
- const uris = schemaAssociation.uris.map(normalizeId);
203
- const association = this.addFilePatternAssociation(schemaAssociation.pattern, uris);
204
- this.contributionAssociations.push(association);
205
- }
206
- }
207
- }
208
- addSchemaHandle(id, unresolvedSchemaContent) {
209
- const schemaHandle = new SchemaHandle(this, id, unresolvedSchemaContent);
210
- this.schemasById[id] = schemaHandle;
211
- return schemaHandle;
212
- }
213
- getOrAddSchemaHandle(id, unresolvedSchemaContent) {
214
- return this.schemasById[id] || this.addSchemaHandle(id, unresolvedSchemaContent);
215
- }
216
- addFilePatternAssociation(pattern, uris) {
217
- const fpa = new FilePatternAssociation(pattern, uris);
218
- this.filePatternAssociations.push(fpa);
219
- return fpa;
220
- }
221
- registerExternalSchema(uri, filePatterns, unresolvedSchemaContent) {
222
- const id = normalizeId(uri);
223
- this.registeredSchemasIds[id] = true;
224
- this.cachedSchemaForResource = undefined;
225
- if (filePatterns) {
226
- this.addFilePatternAssociation(filePatterns, [id]);
227
- }
228
- return unresolvedSchemaContent ? this.addSchemaHandle(id, unresolvedSchemaContent) : this.getOrAddSchemaHandle(id);
229
- }
230
- clearExternalSchemas() {
231
- this.schemasById = {};
232
- this.filePatternAssociations = [];
233
- this.registeredSchemasIds = {};
234
- this.cachedSchemaForResource = undefined;
235
- for (const id in this.contributionSchemas) {
236
- this.schemasById[id] = this.contributionSchemas[id];
237
- this.registeredSchemasIds[id] = true;
238
- }
239
- for (const contributionAssociation of this.contributionAssociations) {
240
- this.filePatternAssociations.push(contributionAssociation);
241
- }
242
- }
243
- getResolvedSchema(schemaId) {
244
- const id = normalizeId(schemaId);
245
- const schemaHandle = this.schemasById[id];
246
- if (schemaHandle) {
247
- return schemaHandle.getResolvedSchema();
248
- }
249
- return this.promise.resolve(undefined);
250
- }
251
- loadSchema(url) {
252
- if (!this.requestService) {
253
- const errorMessage = localize('json.schema.norequestservice', 'Unable to load schema from \'{0}\'. No schema request service available', toDisplayString(url));
254
- return this.promise.resolve(new UnresolvedSchema({}, [errorMessage]));
255
- }
256
- return this.requestService(url).then(content => {
257
- if (!content) {
258
- const errorMessage = localize('json.schema.nocontent', 'Unable to load schema from \'{0}\': No content.', toDisplayString(url));
259
- return new UnresolvedSchema({}, [errorMessage]);
260
- }
261
- let schemaContent = {};
262
- const jsonErrors = [];
263
- schemaContent = Json.parse(content, jsonErrors);
264
- const errors = jsonErrors.length ? [localize('json.schema.invalidFormat', 'Unable to parse content from \'{0}\': Parse error at offset {1}.', toDisplayString(url), jsonErrors[0].offset)] : [];
265
- return new UnresolvedSchema(schemaContent, errors);
266
- }, (error) => {
267
- let errorMessage = error.toString();
268
- const errorSplit = error.toString().split('Error: ');
269
- if (errorSplit.length > 1) {
270
- // more concise error message, URL and context are attached by caller anyways
271
- errorMessage = errorSplit[1];
272
- }
273
- if (Strings.endsWith(errorMessage, '.')) {
274
- errorMessage = errorMessage.substr(0, errorMessage.length - 1);
275
- }
276
- return new UnresolvedSchema({}, [localize('json.schema.nocontent', 'Unable to load schema from \'{0}\': {1}.', toDisplayString(url), errorMessage)]);
277
- });
278
- }
279
- resolveSchemaContent(schemaToResolve, handle) {
280
- const resolveErrors = schemaToResolve.errors.slice(0);
281
- const schema = schemaToResolve.schema;
282
- let schemaDraft = schema.$schema ? normalizeId(schema.$schema) : undefined;
283
- if (schemaDraft === 'http://json-schema.org/draft-03/schema') {
284
- return this.promise.resolve(new ResolvedSchema({}, [localize('json.schema.draft03.notsupported', "Draft-03 schemas are not supported.")], [], schemaDraft));
285
- }
286
- let usesUnsupportedFeatures = new Set();
287
- const contextService = this.contextService;
288
- const findSectionByJSONPointer = (schema, path) => {
289
- path = decodeURIComponent(path);
290
- let current = schema;
291
- if (path[0] === '/') {
292
- path = path.substring(1);
293
- }
294
- path.split('/').some((part) => {
295
- part = part.replace(/~1/g, '/').replace(/~0/g, '~');
296
- current = current[part];
297
- return !current;
298
- });
299
- return current;
300
- };
301
- const findSchemaById = (schema, handle, id) => {
302
- if (!handle.anchors) {
303
- handle.anchors = collectAnchors(schema);
304
- }
305
- return handle.anchors.get(id);
306
- };
307
- const merge = (target, section) => {
308
- for (const key in section) {
309
- if (section.hasOwnProperty(key) && !target.hasOwnProperty(key) && key !== 'id' && key !== '$id') {
310
- target[key] = section[key];
311
- }
312
- }
313
- };
314
- const mergeRef = (target, sourceRoot, sourceHandle, refSegment) => {
315
- let section;
316
- if (refSegment === undefined || refSegment.length === 0) {
317
- section = sourceRoot;
318
- }
319
- else if (refSegment.charAt(0) === '/') {
320
- // A $ref to a JSON Pointer (i.e #/definitions/foo)
321
- section = findSectionByJSONPointer(sourceRoot, refSegment);
322
- }
323
- else {
324
- // A $ref to a sub-schema with an $id (i.e #hello)
325
- section = findSchemaById(sourceRoot, sourceHandle, refSegment);
326
- }
327
- if (section) {
328
- merge(target, section);
329
- }
330
- else {
331
- resolveErrors.push(localize('json.schema.invalidid', '$ref \'{0}\' in \'{1}\' can not be resolved.', refSegment, sourceHandle.uri));
332
- }
333
- };
334
- const resolveExternalLink = (node, uri, refSegment, parentHandle) => {
335
- if (contextService && !/^[A-Za-z][A-Za-z0-9+\-.+]*:\/\/.*/.test(uri)) {
336
- uri = contextService.resolveRelativePath(uri, parentHandle.uri);
337
- }
338
- uri = normalizeId(uri);
339
- const referencedHandle = this.getOrAddSchemaHandle(uri);
340
- return referencedHandle.getUnresolvedSchema().then(unresolvedSchema => {
341
- parentHandle.dependencies.add(uri);
342
- if (unresolvedSchema.errors.length) {
343
- const loc = refSegment ? uri + '#' + refSegment : uri;
344
- resolveErrors.push(localize('json.schema.problemloadingref', 'Problems loading reference \'{0}\': {1}', loc, unresolvedSchema.errors[0]));
345
- }
346
- mergeRef(node, unresolvedSchema.schema, referencedHandle, refSegment);
347
- return resolveRefs(node, unresolvedSchema.schema, referencedHandle);
348
- });
349
- };
350
- const resolveRefs = (node, parentSchema, parentHandle) => {
351
- const openPromises = [];
352
- this.traverseNodes(node, next => {
353
- const seenRefs = new Set();
354
- while (next.$ref) {
355
- const ref = next.$ref;
356
- const segments = ref.split('#', 2);
357
- delete next.$ref;
358
- if (segments[0].length > 0) {
359
- // This is a reference to an external schema
360
- openPromises.push(resolveExternalLink(next, segments[0], segments[1], parentHandle));
361
- return;
362
- }
363
- else {
364
- // This is a reference inside the current schema
365
- if (!seenRefs.has(ref)) {
366
- const id = segments[1];
367
- mergeRef(next, parentSchema, parentHandle, id);
368
- seenRefs.add(ref);
369
- }
370
- }
371
- }
372
- if (next.$recursiveRef) {
373
- usesUnsupportedFeatures.add('$recursiveRef');
374
- }
375
- if (next.$dynamicRef) {
376
- usesUnsupportedFeatures.add('$dynamicRef');
377
- }
378
- });
379
- return this.promise.all(openPromises);
380
- };
381
- const collectAnchors = (root) => {
382
- const result = new Map();
383
- this.traverseNodes(root, next => {
384
- const id = next.$id || next.id;
385
- const anchor = isString(id) && id.charAt(0) === '#' ? id.substring(1) : next.$anchor;
386
- if (anchor) {
387
- if (result.has(anchor)) {
388
- resolveErrors.push(localize('json.schema.duplicateid', 'Duplicate anchor declaration: \'{0}\'', anchor));
389
- }
390
- else {
391
- result.set(anchor, next);
392
- }
393
- }
394
- if (next.$recursiveAnchor) {
395
- usesUnsupportedFeatures.add('$recursiveAnchor');
396
- }
397
- if (next.$dynamicAnchor) {
398
- usesUnsupportedFeatures.add('$dynamicAnchor');
399
- }
400
- });
401
- return result;
402
- };
403
- return resolveRefs(schema, schema, handle).then(_ => {
404
- let resolveWarnings = [];
405
- if (usesUnsupportedFeatures.size) {
406
- resolveWarnings.push(localize('json.schema.warnings', 'The schema uses meta-schema features ({0}) that are not yet supported by the validator.', Array.from(usesUnsupportedFeatures.keys()).join(', ')));
407
- }
408
- return new ResolvedSchema(schema, resolveErrors, resolveWarnings, schemaDraft);
409
- });
410
- }
411
- traverseNodes(root, handle) {
412
- if (!root || typeof root !== 'object') {
413
- return Promise.resolve(null);
414
- }
415
- const seen = new Set();
416
- const collectEntries = (...entries) => {
417
- for (const entry of entries) {
418
- if (isObject(entry)) {
419
- toWalk.push(entry);
420
- }
421
- }
422
- };
423
- const collectMapEntries = (...maps) => {
424
- for (const map of maps) {
425
- if (isObject(map)) {
426
- for (const k in map) {
427
- const key = k;
428
- const entry = map[key];
429
- if (isObject(entry)) {
430
- toWalk.push(entry);
431
- }
432
- }
433
- }
434
- }
435
- };
436
- const collectArrayEntries = (...arrays) => {
437
- for (const array of arrays) {
438
- if (Array.isArray(array)) {
439
- for (const entry of array) {
440
- if (isObject(entry)) {
441
- toWalk.push(entry);
442
- }
443
- }
444
- }
445
- }
446
- };
447
- const collectEntryOrArrayEntries = (items) => {
448
- if (Array.isArray(items)) {
449
- for (const entry of items) {
450
- if (isObject(entry)) {
451
- toWalk.push(entry);
452
- }
453
- }
454
- }
455
- else if (isObject(items)) {
456
- toWalk.push(items);
457
- }
458
- };
459
- const toWalk = [root];
460
- let next = toWalk.pop();
461
- while (next) {
462
- if (!seen.has(next)) {
463
- seen.add(next);
464
- handle(next);
465
- collectEntries(next.additionalItems, next.additionalProperties, next.not, next.contains, next.propertyNames, next.if, next.then, next.else, next.unevaluatedItems, next.unevaluatedProperties);
466
- collectMapEntries(next.definitions, next.$defs, next.properties, next.patternProperties, next.dependencies, next.dependentSchemas);
467
- collectArrayEntries(next.anyOf, next.allOf, next.oneOf, next.prefixItems);
468
- collectEntryOrArrayEntries(next.items);
469
- }
470
- next = toWalk.pop();
471
- }
472
- }
473
- ;
474
- getSchemaFromProperty(resource, document) {
475
- if (document.root?.type === 'object') {
476
- for (const p of document.root.properties) {
477
- if (p.keyNode.value === '$schema' && p.valueNode?.type === 'string') {
478
- let schemaId = p.valueNode.value;
479
- if (this.contextService && !/^\w[\w\d+.-]*:/.test(schemaId)) { // has scheme
480
- schemaId = this.contextService.resolveRelativePath(schemaId, resource);
481
- }
482
- return schemaId;
483
- }
484
- }
485
- }
486
- return undefined;
487
- }
488
- getAssociatedSchemas(resource) {
489
- const seen = Object.create(null);
490
- const schemas = [];
491
- const normalizedResource = normalizeResourceForMatching(resource);
492
- for (const entry of this.filePatternAssociations) {
493
- if (entry.matchesPattern(normalizedResource)) {
494
- for (const schemaId of entry.getURIs()) {
495
- if (!seen[schemaId]) {
496
- schemas.push(schemaId);
497
- seen[schemaId] = true;
498
- }
499
- }
500
- }
501
- }
502
- return schemas;
503
- }
504
- getSchemaURIsForResource(resource, document) {
505
- let schemeId = document && this.getSchemaFromProperty(resource, document);
506
- if (schemeId) {
507
- return [schemeId];
508
- }
509
- return this.getAssociatedSchemas(resource);
510
- }
511
- getSchemaForResource(resource, document) {
512
- if (document) {
513
- // first use $schema if present
514
- let schemeId = this.getSchemaFromProperty(resource, document);
515
- if (schemeId) {
516
- const id = normalizeId(schemeId);
517
- return this.getOrAddSchemaHandle(id).getResolvedSchema();
518
- }
519
- }
520
- if (this.cachedSchemaForResource && this.cachedSchemaForResource.resource === resource) {
521
- return this.cachedSchemaForResource.resolvedSchema;
522
- }
523
- const schemas = this.getAssociatedSchemas(resource);
524
- const resolvedSchema = schemas.length > 0 ? this.createCombinedSchema(resource, schemas).getResolvedSchema() : this.promise.resolve(undefined);
525
- this.cachedSchemaForResource = { resource, resolvedSchema };
526
- return resolvedSchema;
527
- }
528
- createCombinedSchema(resource, schemaIds) {
529
- if (schemaIds.length === 1) {
530
- return this.getOrAddSchemaHandle(schemaIds[0]);
531
- }
532
- else {
533
- const combinedSchemaId = 'schemaservice://combinedSchema/' + encodeURIComponent(resource);
534
- const combinedSchema = {
535
- allOf: schemaIds.map(schemaId => ({ $ref: schemaId }))
536
- };
537
- return this.addSchemaHandle(combinedSchemaId, combinedSchema);
538
- }
539
- }
540
- getMatchingSchemas(document, jsonDocument, schema) {
541
- if (schema) {
542
- const id = schema.id || ('schemaservice://untitled/matchingSchemas/' + idCounter++);
543
- const handle = this.addSchemaHandle(id, schema);
544
- return handle.getResolvedSchema().then(resolvedSchema => {
545
- return jsonDocument.getMatchingSchemas(resolvedSchema.schema).filter(s => !s.inverted);
546
- });
547
- }
548
- return this.getSchemaForResource(document.uri, jsonDocument).then(schema => {
549
- if (schema) {
550
- return jsonDocument.getMatchingSchemas(schema.schema).filter(s => !s.inverted);
551
- }
552
- return [];
553
- });
554
- }
555
- }
556
- let idCounter = 0;
557
- function normalizeId(id) {
558
- // remove trailing '#', normalize drive capitalization
559
- try {
560
- return URI.parse(id).toString(true);
561
- }
562
- catch (e) {
563
- return id;
564
- }
565
- }
566
- function normalizeResourceForMatching(resource) {
567
- // remove queries and fragments, normalize drive capitalization
568
- try {
569
- return URI.parse(resource).with({ fragment: null, query: null }).toString(true);
570
- }
571
- catch (e) {
572
- return resource;
573
- }
574
- }
575
- function toDisplayString(url) {
576
- try {
577
- const uri = URI.parse(url);
578
- if (uri.scheme === 'file') {
579
- return uri.fsPath;
580
- }
581
- }
582
- catch (e) {
583
- // ignore
584
- }
585
- return url;
586
- }
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 { URI } from 'vscode-uri';
7
+ import * as Strings from '../utils/strings';
8
+ import * as Parser from '../parser/jsonParser';
9
+ import * as nls from 'vscode-nls';
10
+ import { createRegex } from '../utils/glob';
11
+ import { isObject, isString } from '../utils/objects';
12
+ const localize = nls.loadMessageBundle();
13
+ const BANG = '!';
14
+ const PATH_SEP = '/';
15
+ class FilePatternAssociation {
16
+ constructor(pattern, uris) {
17
+ this.globWrappers = [];
18
+ try {
19
+ for (let patternString of pattern) {
20
+ const include = patternString[0] !== BANG;
21
+ if (!include) {
22
+ patternString = patternString.substring(1);
23
+ }
24
+ if (patternString.length > 0) {
25
+ if (patternString[0] === PATH_SEP) {
26
+ patternString = patternString.substring(1);
27
+ }
28
+ this.globWrappers.push({
29
+ regexp: createRegex('**/' + patternString, { extended: true, globstar: true }),
30
+ include: include,
31
+ });
32
+ }
33
+ }
34
+ ;
35
+ this.uris = uris;
36
+ }
37
+ catch (e) {
38
+ this.globWrappers.length = 0;
39
+ this.uris = [];
40
+ }
41
+ }
42
+ matchesPattern(fileName) {
43
+ let match = false;
44
+ for (const { regexp, include } of this.globWrappers) {
45
+ if (regexp.test(fileName)) {
46
+ match = include;
47
+ }
48
+ }
49
+ return match;
50
+ }
51
+ getURIs() {
52
+ return this.uris;
53
+ }
54
+ }
55
+ class SchemaHandle {
56
+ constructor(service, uri, unresolvedSchemaContent) {
57
+ this.service = service;
58
+ this.uri = uri;
59
+ this.dependencies = new Set();
60
+ this.anchors = undefined;
61
+ if (unresolvedSchemaContent) {
62
+ this.unresolvedSchema = this.service.promise.resolve(new UnresolvedSchema(unresolvedSchemaContent));
63
+ }
64
+ }
65
+ getUnresolvedSchema() {
66
+ if (!this.unresolvedSchema) {
67
+ this.unresolvedSchema = this.service.loadSchema(this.uri);
68
+ }
69
+ return this.unresolvedSchema;
70
+ }
71
+ getResolvedSchema() {
72
+ if (!this.resolvedSchema) {
73
+ this.resolvedSchema = this.getUnresolvedSchema().then(unresolved => {
74
+ return this.service.resolveSchemaContent(unresolved, this);
75
+ });
76
+ }
77
+ return this.resolvedSchema;
78
+ }
79
+ clearSchema() {
80
+ const hasChanges = !!this.unresolvedSchema;
81
+ this.resolvedSchema = undefined;
82
+ this.unresolvedSchema = undefined;
83
+ this.dependencies.clear();
84
+ this.anchors = undefined;
85
+ return hasChanges;
86
+ }
87
+ }
88
+ export class UnresolvedSchema {
89
+ constructor(schema, errors = []) {
90
+ this.schema = schema;
91
+ this.errors = errors;
92
+ }
93
+ }
94
+ export class ResolvedSchema {
95
+ constructor(schema, errors = [], warnings = [], schemaDraft) {
96
+ this.schema = schema;
97
+ this.errors = errors;
98
+ this.warnings = warnings;
99
+ this.schemaDraft = schemaDraft;
100
+ }
101
+ getSection(path) {
102
+ const schemaRef = this.getSectionRecursive(path, this.schema);
103
+ if (schemaRef) {
104
+ return Parser.asSchema(schemaRef);
105
+ }
106
+ return undefined;
107
+ }
108
+ getSectionRecursive(path, schema) {
109
+ if (!schema || typeof schema === 'boolean' || path.length === 0) {
110
+ return schema;
111
+ }
112
+ const next = path.shift();
113
+ if (schema.properties && typeof schema.properties[next]) {
114
+ return this.getSectionRecursive(path, schema.properties[next]);
115
+ }
116
+ else if (schema.patternProperties) {
117
+ for (const pattern of Object.keys(schema.patternProperties)) {
118
+ const regex = Strings.extendedRegExp(pattern);
119
+ if (regex?.test(next)) {
120
+ return this.getSectionRecursive(path, schema.patternProperties[pattern]);
121
+ }
122
+ }
123
+ }
124
+ else if (typeof schema.additionalProperties === 'object') {
125
+ return this.getSectionRecursive(path, schema.additionalProperties);
126
+ }
127
+ else if (next.match('[0-9]+')) {
128
+ if (Array.isArray(schema.items)) {
129
+ const index = parseInt(next, 10);
130
+ if (!isNaN(index) && schema.items[index]) {
131
+ return this.getSectionRecursive(path, schema.items[index]);
132
+ }
133
+ }
134
+ else if (schema.items) {
135
+ return this.getSectionRecursive(path, schema.items);
136
+ }
137
+ }
138
+ return undefined;
139
+ }
140
+ }
141
+ export class JSONSchemaService {
142
+ constructor(requestService, contextService, promiseConstructor) {
143
+ this.contextService = contextService;
144
+ this.requestService = requestService;
145
+ this.promiseConstructor = promiseConstructor || Promise;
146
+ this.callOnDispose = [];
147
+ this.contributionSchemas = {};
148
+ this.contributionAssociations = [];
149
+ this.schemasById = {};
150
+ this.filePatternAssociations = [];
151
+ this.registeredSchemasIds = {};
152
+ }
153
+ getRegisteredSchemaIds(filter) {
154
+ return Object.keys(this.registeredSchemasIds).filter(id => {
155
+ const scheme = URI.parse(id).scheme;
156
+ return scheme !== 'schemaservice' && (!filter || filter(scheme));
157
+ });
158
+ }
159
+ get promise() {
160
+ return this.promiseConstructor;
161
+ }
162
+ dispose() {
163
+ while (this.callOnDispose.length > 0) {
164
+ this.callOnDispose.pop()();
165
+ }
166
+ }
167
+ onResourceChange(uri) {
168
+ // always clear this local cache when a resource changes
169
+ this.cachedSchemaForResource = undefined;
170
+ let hasChanges = false;
171
+ uri = normalizeId(uri);
172
+ const toWalk = [uri];
173
+ const all = Object.keys(this.schemasById).map(key => this.schemasById[key]);
174
+ while (toWalk.length) {
175
+ const curr = toWalk.pop();
176
+ for (let i = 0; i < all.length; i++) {
177
+ const handle = all[i];
178
+ if (handle && (handle.uri === curr || handle.dependencies.has(curr))) {
179
+ if (handle.uri !== curr) {
180
+ toWalk.push(handle.uri);
181
+ }
182
+ if (handle.clearSchema()) {
183
+ hasChanges = true;
184
+ }
185
+ all[i] = undefined;
186
+ }
187
+ }
188
+ }
189
+ return hasChanges;
190
+ }
191
+ setSchemaContributions(schemaContributions) {
192
+ if (schemaContributions.schemas) {
193
+ const schemas = schemaContributions.schemas;
194
+ for (const id in schemas) {
195
+ const normalizedId = normalizeId(id);
196
+ this.contributionSchemas[normalizedId] = this.addSchemaHandle(normalizedId, schemas[id]);
197
+ }
198
+ }
199
+ if (Array.isArray(schemaContributions.schemaAssociations)) {
200
+ const schemaAssociations = schemaContributions.schemaAssociations;
201
+ for (let schemaAssociation of schemaAssociations) {
202
+ const uris = schemaAssociation.uris.map(normalizeId);
203
+ const association = this.addFilePatternAssociation(schemaAssociation.pattern, uris);
204
+ this.contributionAssociations.push(association);
205
+ }
206
+ }
207
+ }
208
+ addSchemaHandle(id, unresolvedSchemaContent) {
209
+ const schemaHandle = new SchemaHandle(this, id, unresolvedSchemaContent);
210
+ this.schemasById[id] = schemaHandle;
211
+ return schemaHandle;
212
+ }
213
+ getOrAddSchemaHandle(id, unresolvedSchemaContent) {
214
+ return this.schemasById[id] || this.addSchemaHandle(id, unresolvedSchemaContent);
215
+ }
216
+ addFilePatternAssociation(pattern, uris) {
217
+ const fpa = new FilePatternAssociation(pattern, uris);
218
+ this.filePatternAssociations.push(fpa);
219
+ return fpa;
220
+ }
221
+ registerExternalSchema(uri, filePatterns, unresolvedSchemaContent) {
222
+ const id = normalizeId(uri);
223
+ this.registeredSchemasIds[id] = true;
224
+ this.cachedSchemaForResource = undefined;
225
+ if (filePatterns) {
226
+ this.addFilePatternAssociation(filePatterns, [id]);
227
+ }
228
+ return unresolvedSchemaContent ? this.addSchemaHandle(id, unresolvedSchemaContent) : this.getOrAddSchemaHandle(id);
229
+ }
230
+ clearExternalSchemas() {
231
+ this.schemasById = {};
232
+ this.filePatternAssociations = [];
233
+ this.registeredSchemasIds = {};
234
+ this.cachedSchemaForResource = undefined;
235
+ for (const id in this.contributionSchemas) {
236
+ this.schemasById[id] = this.contributionSchemas[id];
237
+ this.registeredSchemasIds[id] = true;
238
+ }
239
+ for (const contributionAssociation of this.contributionAssociations) {
240
+ this.filePatternAssociations.push(contributionAssociation);
241
+ }
242
+ }
243
+ getResolvedSchema(schemaId) {
244
+ const id = normalizeId(schemaId);
245
+ const schemaHandle = this.schemasById[id];
246
+ if (schemaHandle) {
247
+ return schemaHandle.getResolvedSchema();
248
+ }
249
+ return this.promise.resolve(undefined);
250
+ }
251
+ loadSchema(url) {
252
+ if (!this.requestService) {
253
+ const errorMessage = localize('json.schema.norequestservice', 'Unable to load schema from \'{0}\'. No schema request service available', toDisplayString(url));
254
+ return this.promise.resolve(new UnresolvedSchema({}, [errorMessage]));
255
+ }
256
+ return this.requestService(url).then(content => {
257
+ if (!content) {
258
+ const errorMessage = localize('json.schema.nocontent', 'Unable to load schema from \'{0}\': No content.', toDisplayString(url));
259
+ return new UnresolvedSchema({}, [errorMessage]);
260
+ }
261
+ const errors = [];
262
+ if (content.charCodeAt(0) === 65279) {
263
+ errors.push(localize('json.schema.encodingWithBOM', 'Problem reading content from \'{0}\': UTF-8 with BOM detected, only UTF 8 is allowed.', toDisplayString(url)));
264
+ content = content.trimStart();
265
+ }
266
+ let schemaContent = {};
267
+ const jsonErrors = [];
268
+ schemaContent = Json.parse(content, jsonErrors);
269
+ if (jsonErrors.length) {
270
+ errors.push(localize('json.schema.invalidFormat', 'Unable to parse content from \'{0}\': Parse error at offset {1}.', toDisplayString(url), jsonErrors[0].offset));
271
+ }
272
+ return new UnresolvedSchema(schemaContent, errors);
273
+ }, (error) => {
274
+ let errorMessage = error.toString();
275
+ const errorSplit = error.toString().split('Error: ');
276
+ if (errorSplit.length > 1) {
277
+ // more concise error message, URL and context are attached by caller anyways
278
+ errorMessage = errorSplit[1];
279
+ }
280
+ if (Strings.endsWith(errorMessage, '.')) {
281
+ errorMessage = errorMessage.substr(0, errorMessage.length - 1);
282
+ }
283
+ return new UnresolvedSchema({}, [localize('json.schema.nocontent', 'Unable to load schema from \'{0}\': {1}.', toDisplayString(url), errorMessage)]);
284
+ });
285
+ }
286
+ resolveSchemaContent(schemaToResolve, handle) {
287
+ const resolveErrors = schemaToResolve.errors.slice(0);
288
+ const schema = schemaToResolve.schema;
289
+ let schemaDraft = schema.$schema ? normalizeId(schema.$schema) : undefined;
290
+ if (schemaDraft === 'http://json-schema.org/draft-03/schema') {
291
+ return this.promise.resolve(new ResolvedSchema({}, [localize('json.schema.draft03.notsupported', "Draft-03 schemas are not supported.")], [], schemaDraft));
292
+ }
293
+ let usesUnsupportedFeatures = new Set();
294
+ const contextService = this.contextService;
295
+ const findSectionByJSONPointer = (schema, path) => {
296
+ path = decodeURIComponent(path);
297
+ let current = schema;
298
+ if (path[0] === '/') {
299
+ path = path.substring(1);
300
+ }
301
+ path.split('/').some((part) => {
302
+ part = part.replace(/~1/g, '/').replace(/~0/g, '~');
303
+ current = current[part];
304
+ return !current;
305
+ });
306
+ return current;
307
+ };
308
+ const findSchemaById = (schema, handle, id) => {
309
+ if (!handle.anchors) {
310
+ handle.anchors = collectAnchors(schema);
311
+ }
312
+ return handle.anchors.get(id);
313
+ };
314
+ const merge = (target, section) => {
315
+ for (const key in section) {
316
+ if (section.hasOwnProperty(key) && key !== 'id' && key !== '$id') {
317
+ target[key] = section[key];
318
+ }
319
+ }
320
+ };
321
+ const mergeRef = (target, sourceRoot, sourceHandle, refSegment) => {
322
+ let section;
323
+ if (refSegment === undefined || refSegment.length === 0) {
324
+ section = sourceRoot;
325
+ }
326
+ else if (refSegment.charAt(0) === '/') {
327
+ // A $ref to a JSON Pointer (i.e #/definitions/foo)
328
+ section = findSectionByJSONPointer(sourceRoot, refSegment);
329
+ }
330
+ else {
331
+ // A $ref to a sub-schema with an $id (i.e #hello)
332
+ section = findSchemaById(sourceRoot, sourceHandle, refSegment);
333
+ }
334
+ if (section) {
335
+ merge(target, section);
336
+ }
337
+ else {
338
+ resolveErrors.push(localize('json.schema.invalidid', '$ref \'{0}\' in \'{1}\' can not be resolved.', refSegment, sourceHandle.uri));
339
+ }
340
+ };
341
+ const resolveExternalLink = (node, uri, refSegment, parentHandle) => {
342
+ if (contextService && !/^[A-Za-z][A-Za-z0-9+\-.+]*:\/\/.*/.test(uri)) {
343
+ uri = contextService.resolveRelativePath(uri, parentHandle.uri);
344
+ }
345
+ uri = normalizeId(uri);
346
+ const referencedHandle = this.getOrAddSchemaHandle(uri);
347
+ return referencedHandle.getUnresolvedSchema().then(unresolvedSchema => {
348
+ parentHandle.dependencies.add(uri);
349
+ if (unresolvedSchema.errors.length) {
350
+ const loc = refSegment ? uri + '#' + refSegment : uri;
351
+ resolveErrors.push(localize('json.schema.problemloadingref', 'Problems loading reference \'{0}\': {1}', loc, unresolvedSchema.errors[0]));
352
+ }
353
+ mergeRef(node, unresolvedSchema.schema, referencedHandle, refSegment);
354
+ return resolveRefs(node, unresolvedSchema.schema, referencedHandle);
355
+ });
356
+ };
357
+ const resolveRefs = (node, parentSchema, parentHandle) => {
358
+ const openPromises = [];
359
+ this.traverseNodes(node, next => {
360
+ const seenRefs = new Set();
361
+ while (next.$ref) {
362
+ const ref = next.$ref;
363
+ const segments = ref.split('#', 2);
364
+ delete next.$ref;
365
+ if (segments[0].length > 0) {
366
+ // This is a reference to an external schema
367
+ openPromises.push(resolveExternalLink(next, segments[0], segments[1], parentHandle));
368
+ return;
369
+ }
370
+ else {
371
+ // This is a reference inside the current schema
372
+ if (!seenRefs.has(ref)) {
373
+ const id = segments[1];
374
+ mergeRef(next, parentSchema, parentHandle, id);
375
+ seenRefs.add(ref);
376
+ }
377
+ }
378
+ }
379
+ if (next.$recursiveRef) {
380
+ usesUnsupportedFeatures.add('$recursiveRef');
381
+ }
382
+ if (next.$dynamicRef) {
383
+ usesUnsupportedFeatures.add('$dynamicRef');
384
+ }
385
+ });
386
+ return this.promise.all(openPromises);
387
+ };
388
+ const collectAnchors = (root) => {
389
+ const result = new Map();
390
+ this.traverseNodes(root, next => {
391
+ const id = next.$id || next.id;
392
+ const anchor = isString(id) && id.charAt(0) === '#' ? id.substring(1) : next.$anchor;
393
+ if (anchor) {
394
+ if (result.has(anchor)) {
395
+ resolveErrors.push(localize('json.schema.duplicateid', 'Duplicate anchor declaration: \'{0}\'', anchor));
396
+ }
397
+ else {
398
+ result.set(anchor, next);
399
+ }
400
+ }
401
+ if (next.$recursiveAnchor) {
402
+ usesUnsupportedFeatures.add('$recursiveAnchor');
403
+ }
404
+ if (next.$dynamicAnchor) {
405
+ usesUnsupportedFeatures.add('$dynamicAnchor');
406
+ }
407
+ });
408
+ return result;
409
+ };
410
+ return resolveRefs(schema, schema, handle).then(_ => {
411
+ let resolveWarnings = [];
412
+ if (usesUnsupportedFeatures.size) {
413
+ resolveWarnings.push(localize('json.schema.warnings', 'The schema uses meta-schema features ({0}) that are not yet supported by the validator.', Array.from(usesUnsupportedFeatures.keys()).join(', ')));
414
+ }
415
+ return new ResolvedSchema(schema, resolveErrors, resolveWarnings, schemaDraft);
416
+ });
417
+ }
418
+ traverseNodes(root, handle) {
419
+ if (!root || typeof root !== 'object') {
420
+ return Promise.resolve(null);
421
+ }
422
+ const seen = new Set();
423
+ const collectEntries = (...entries) => {
424
+ for (const entry of entries) {
425
+ if (isObject(entry)) {
426
+ toWalk.push(entry);
427
+ }
428
+ }
429
+ };
430
+ const collectMapEntries = (...maps) => {
431
+ for (const map of maps) {
432
+ if (isObject(map)) {
433
+ for (const k in map) {
434
+ const key = k;
435
+ const entry = map[key];
436
+ if (isObject(entry)) {
437
+ toWalk.push(entry);
438
+ }
439
+ }
440
+ }
441
+ }
442
+ };
443
+ const collectArrayEntries = (...arrays) => {
444
+ for (const array of arrays) {
445
+ if (Array.isArray(array)) {
446
+ for (const entry of array) {
447
+ if (isObject(entry)) {
448
+ toWalk.push(entry);
449
+ }
450
+ }
451
+ }
452
+ }
453
+ };
454
+ const collectEntryOrArrayEntries = (items) => {
455
+ if (Array.isArray(items)) {
456
+ for (const entry of items) {
457
+ if (isObject(entry)) {
458
+ toWalk.push(entry);
459
+ }
460
+ }
461
+ }
462
+ else if (isObject(items)) {
463
+ toWalk.push(items);
464
+ }
465
+ };
466
+ const toWalk = [root];
467
+ let next = toWalk.pop();
468
+ while (next) {
469
+ if (!seen.has(next)) {
470
+ seen.add(next);
471
+ handle(next);
472
+ collectEntries(next.additionalItems, next.additionalProperties, next.not, next.contains, next.propertyNames, next.if, next.then, next.else, next.unevaluatedItems, next.unevaluatedProperties);
473
+ collectMapEntries(next.definitions, next.$defs, next.properties, next.patternProperties, next.dependencies, next.dependentSchemas);
474
+ collectArrayEntries(next.anyOf, next.allOf, next.oneOf, next.prefixItems);
475
+ collectEntryOrArrayEntries(next.items);
476
+ }
477
+ next = toWalk.pop();
478
+ }
479
+ }
480
+ ;
481
+ getSchemaFromProperty(resource, document) {
482
+ if (document.root?.type === 'object') {
483
+ for (const p of document.root.properties) {
484
+ if (p.keyNode.value === '$schema' && p.valueNode?.type === 'string') {
485
+ let schemaId = p.valueNode.value;
486
+ if (this.contextService && !/^\w[\w\d+.-]*:/.test(schemaId)) { // has scheme
487
+ schemaId = this.contextService.resolveRelativePath(schemaId, resource);
488
+ }
489
+ return schemaId;
490
+ }
491
+ }
492
+ }
493
+ return undefined;
494
+ }
495
+ getAssociatedSchemas(resource) {
496
+ const seen = Object.create(null);
497
+ const schemas = [];
498
+ const normalizedResource = normalizeResourceForMatching(resource);
499
+ for (const entry of this.filePatternAssociations) {
500
+ if (entry.matchesPattern(normalizedResource)) {
501
+ for (const schemaId of entry.getURIs()) {
502
+ if (!seen[schemaId]) {
503
+ schemas.push(schemaId);
504
+ seen[schemaId] = true;
505
+ }
506
+ }
507
+ }
508
+ }
509
+ return schemas;
510
+ }
511
+ getSchemaURIsForResource(resource, document) {
512
+ let schemeId = document && this.getSchemaFromProperty(resource, document);
513
+ if (schemeId) {
514
+ return [schemeId];
515
+ }
516
+ return this.getAssociatedSchemas(resource);
517
+ }
518
+ getSchemaForResource(resource, document) {
519
+ if (document) {
520
+ // first use $schema if present
521
+ let schemeId = this.getSchemaFromProperty(resource, document);
522
+ if (schemeId) {
523
+ const id = normalizeId(schemeId);
524
+ return this.getOrAddSchemaHandle(id).getResolvedSchema();
525
+ }
526
+ }
527
+ if (this.cachedSchemaForResource && this.cachedSchemaForResource.resource === resource) {
528
+ return this.cachedSchemaForResource.resolvedSchema;
529
+ }
530
+ const schemas = this.getAssociatedSchemas(resource);
531
+ const resolvedSchema = schemas.length > 0 ? this.createCombinedSchema(resource, schemas).getResolvedSchema() : this.promise.resolve(undefined);
532
+ this.cachedSchemaForResource = { resource, resolvedSchema };
533
+ return resolvedSchema;
534
+ }
535
+ createCombinedSchema(resource, schemaIds) {
536
+ if (schemaIds.length === 1) {
537
+ return this.getOrAddSchemaHandle(schemaIds[0]);
538
+ }
539
+ else {
540
+ const combinedSchemaId = 'schemaservice://combinedSchema/' + encodeURIComponent(resource);
541
+ const combinedSchema = {
542
+ allOf: schemaIds.map(schemaId => ({ $ref: schemaId }))
543
+ };
544
+ return this.addSchemaHandle(combinedSchemaId, combinedSchema);
545
+ }
546
+ }
547
+ getMatchingSchemas(document, jsonDocument, schema) {
548
+ if (schema) {
549
+ const id = schema.id || ('schemaservice://untitled/matchingSchemas/' + idCounter++);
550
+ const handle = this.addSchemaHandle(id, schema);
551
+ return handle.getResolvedSchema().then(resolvedSchema => {
552
+ return jsonDocument.getMatchingSchemas(resolvedSchema.schema).filter(s => !s.inverted);
553
+ });
554
+ }
555
+ return this.getSchemaForResource(document.uri, jsonDocument).then(schema => {
556
+ if (schema) {
557
+ return jsonDocument.getMatchingSchemas(schema.schema).filter(s => !s.inverted);
558
+ }
559
+ return [];
560
+ });
561
+ }
562
+ }
563
+ let idCounter = 0;
564
+ function normalizeId(id) {
565
+ // remove trailing '#', normalize drive capitalization
566
+ try {
567
+ return URI.parse(id).toString(true);
568
+ }
569
+ catch (e) {
570
+ return id;
571
+ }
572
+ }
573
+ function normalizeResourceForMatching(resource) {
574
+ // remove queries and fragments, normalize drive capitalization
575
+ try {
576
+ return URI.parse(resource).with({ fragment: null, query: null }).toString(true);
577
+ }
578
+ catch (e) {
579
+ return resource;
580
+ }
581
+ }
582
+ function toDisplayString(url) {
583
+ try {
584
+ const uri = URI.parse(url);
585
+ if (uri.scheme === 'file') {
586
+ return uri.fsPath;
587
+ }
588
+ }
589
+ catch (e) {
590
+ // ignore
591
+ }
592
+ return url;
593
+ }