massive update, probably broken
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-json / node_modules / vscode-json-languageservice / lib / esm / services / jsonSchemaService.js
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 { Minimatch } from 'minimatch';
10 import * as nls from 'vscode-nls';
11 var localize = nls.loadMessageBundle();
12 var BANG = '!';
13 var PATH_SEP = '/';
14 var FilePatternAssociation = /** @class */ (function () {
15     function FilePatternAssociation(pattern, uris) {
16         this.minimatchWrappers = [];
17         try {
18             for (var _i = 0, pattern_1 = pattern; _i < pattern_1.length; _i++) {
19                 var patternString = pattern_1[_i];
20                 var 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.minimatchWrappers.push({
29                         minimatch: new Minimatch("**/" + patternString),
30                         include: include,
31                     });
32                 }
33             }
34             ;
35             this.uris = uris;
36         }
37         catch (e) {
38             this.minimatchWrappers.length = 0;
39             this.uris = [];
40         }
41     }
42     FilePatternAssociation.prototype.matchesPattern = function (fileName) {
43         var match = false;
44         for (var _i = 0, _a = this.minimatchWrappers; _i < _a.length; _i++) {
45             var _b = _a[_i], minimatch = _b.minimatch, include = _b.include;
46             if (minimatch.match(fileName)) {
47                 match = include;
48             }
49         }
50         return match;
51     };
52     FilePatternAssociation.prototype.getURIs = function () {
53         return this.uris;
54     };
55     return FilePatternAssociation;
56 }());
57 var SchemaHandle = /** @class */ (function () {
58     function SchemaHandle(service, url, unresolvedSchemaContent) {
59         this.service = service;
60         this.url = url;
61         this.dependencies = {};
62         if (unresolvedSchemaContent) {
63             this.unresolvedSchema = this.service.promise.resolve(new UnresolvedSchema(unresolvedSchemaContent));
64         }
65     }
66     SchemaHandle.prototype.getUnresolvedSchema = function () {
67         if (!this.unresolvedSchema) {
68             this.unresolvedSchema = this.service.loadSchema(this.url);
69         }
70         return this.unresolvedSchema;
71     };
72     SchemaHandle.prototype.getResolvedSchema = function () {
73         var _this = this;
74         if (!this.resolvedSchema) {
75             this.resolvedSchema = this.getUnresolvedSchema().then(function (unresolved) {
76                 return _this.service.resolveSchemaContent(unresolved, _this.url, _this.dependencies);
77             });
78         }
79         return this.resolvedSchema;
80     };
81     SchemaHandle.prototype.clearSchema = function () {
82         this.resolvedSchema = undefined;
83         this.unresolvedSchema = undefined;
84         this.dependencies = {};
85     };
86     return SchemaHandle;
87 }());
88 var UnresolvedSchema = /** @class */ (function () {
89     function UnresolvedSchema(schema, errors) {
90         if (errors === void 0) { errors = []; }
91         this.schema = schema;
92         this.errors = errors;
93     }
94     return UnresolvedSchema;
95 }());
96 export { UnresolvedSchema };
97 var ResolvedSchema = /** @class */ (function () {
98     function ResolvedSchema(schema, errors) {
99         if (errors === void 0) { errors = []; }
100         this.schema = schema;
101         this.errors = errors;
102     }
103     ResolvedSchema.prototype.getSection = function (path) {
104         var schemaRef = this.getSectionRecursive(path, this.schema);
105         if (schemaRef) {
106             return Parser.asSchema(schemaRef);
107         }
108         return undefined;
109     };
110     ResolvedSchema.prototype.getSectionRecursive = function (path, schema) {
111         if (!schema || typeof schema === 'boolean' || path.length === 0) {
112             return schema;
113         }
114         var next = path.shift();
115         if (schema.properties && typeof schema.properties[next]) {
116             return this.getSectionRecursive(path, schema.properties[next]);
117         }
118         else if (schema.patternProperties) {
119             for (var _i = 0, _a = Object.keys(schema.patternProperties); _i < _a.length; _i++) {
120                 var pattern = _a[_i];
121                 var regex = Strings.extendedRegExp(pattern);
122                 if (regex.test(next)) {
123                     return this.getSectionRecursive(path, schema.patternProperties[pattern]);
124                 }
125             }
126         }
127         else if (typeof schema.additionalProperties === 'object') {
128             return this.getSectionRecursive(path, schema.additionalProperties);
129         }
130         else if (next.match('[0-9]+')) {
131             if (Array.isArray(schema.items)) {
132                 var index = parseInt(next, 10);
133                 if (!isNaN(index) && schema.items[index]) {
134                     return this.getSectionRecursive(path, schema.items[index]);
135                 }
136             }
137             else if (schema.items) {
138                 return this.getSectionRecursive(path, schema.items);
139             }
140         }
141         return undefined;
142     };
143     return ResolvedSchema;
144 }());
145 export { ResolvedSchema };
146 var JSONSchemaService = /** @class */ (function () {
147     function JSONSchemaService(requestService, contextService, promiseConstructor) {
148         this.contextService = contextService;
149         this.requestService = requestService;
150         this.promiseConstructor = promiseConstructor || Promise;
151         this.callOnDispose = [];
152         this.contributionSchemas = {};
153         this.contributionAssociations = [];
154         this.schemasById = {};
155         this.filePatternAssociations = [];
156         this.registeredSchemasIds = {};
157     }
158     JSONSchemaService.prototype.getRegisteredSchemaIds = function (filter) {
159         return Object.keys(this.registeredSchemasIds).filter(function (id) {
160             var scheme = URI.parse(id).scheme;
161             return scheme !== 'schemaservice' && (!filter || filter(scheme));
162         });
163     };
164     Object.defineProperty(JSONSchemaService.prototype, "promise", {
165         get: function () {
166             return this.promiseConstructor;
167         },
168         enumerable: false,
169         configurable: true
170     });
171     JSONSchemaService.prototype.dispose = function () {
172         while (this.callOnDispose.length > 0) {
173             this.callOnDispose.pop()();
174         }
175     };
176     JSONSchemaService.prototype.onResourceChange = function (uri) {
177         var _this = this;
178         var hasChanges = false;
179         uri = normalizeId(uri);
180         var toWalk = [uri];
181         var all = Object.keys(this.schemasById).map(function (key) { return _this.schemasById[key]; });
182         while (toWalk.length) {
183             var curr = toWalk.pop();
184             for (var i = 0; i < all.length; i++) {
185                 var handle = all[i];
186                 if (handle && (handle.url === curr || handle.dependencies[curr])) {
187                     if (handle.url !== curr) {
188                         toWalk.push(handle.url);
189                     }
190                     handle.clearSchema();
191                     all[i] = undefined;
192                     hasChanges = true;
193                 }
194             }
195         }
196         return hasChanges;
197     };
198     JSONSchemaService.prototype.setSchemaContributions = function (schemaContributions) {
199         if (schemaContributions.schemas) {
200             var schemas = schemaContributions.schemas;
201             for (var id in schemas) {
202                 var normalizedId = normalizeId(id);
203                 this.contributionSchemas[normalizedId] = this.addSchemaHandle(normalizedId, schemas[id]);
204             }
205         }
206         if (Array.isArray(schemaContributions.schemaAssociations)) {
207             var schemaAssociations = schemaContributions.schemaAssociations;
208             for (var _i = 0, schemaAssociations_1 = schemaAssociations; _i < schemaAssociations_1.length; _i++) {
209                 var schemaAssociation = schemaAssociations_1[_i];
210                 var uris = schemaAssociation.uris.map(normalizeId);
211                 var association = this.addFilePatternAssociation(schemaAssociation.pattern, uris);
212                 this.contributionAssociations.push(association);
213             }
214         }
215     };
216     JSONSchemaService.prototype.addSchemaHandle = function (id, unresolvedSchemaContent) {
217         var schemaHandle = new SchemaHandle(this, id, unresolvedSchemaContent);
218         this.schemasById[id] = schemaHandle;
219         return schemaHandle;
220     };
221     JSONSchemaService.prototype.getOrAddSchemaHandle = function (id, unresolvedSchemaContent) {
222         return this.schemasById[id] || this.addSchemaHandle(id, unresolvedSchemaContent);
223     };
224     JSONSchemaService.prototype.addFilePatternAssociation = function (pattern, uris) {
225         var fpa = new FilePatternAssociation(pattern, uris);
226         this.filePatternAssociations.push(fpa);
227         return fpa;
228     };
229     JSONSchemaService.prototype.registerExternalSchema = function (uri, filePatterns, unresolvedSchemaContent) {
230         var id = normalizeId(uri);
231         this.registeredSchemasIds[id] = true;
232         this.cachedSchemaForResource = undefined;
233         if (filePatterns) {
234             this.addFilePatternAssociation(filePatterns, [uri]);
235         }
236         return unresolvedSchemaContent ? this.addSchemaHandle(id, unresolvedSchemaContent) : this.getOrAddSchemaHandle(id);
237     };
238     JSONSchemaService.prototype.clearExternalSchemas = function () {
239         this.schemasById = {};
240         this.filePatternAssociations = [];
241         this.registeredSchemasIds = {};
242         this.cachedSchemaForResource = undefined;
243         for (var id in this.contributionSchemas) {
244             this.schemasById[id] = this.contributionSchemas[id];
245             this.registeredSchemasIds[id] = true;
246         }
247         for (var _i = 0, _a = this.contributionAssociations; _i < _a.length; _i++) {
248             var contributionAssociation = _a[_i];
249             this.filePatternAssociations.push(contributionAssociation);
250         }
251     };
252     JSONSchemaService.prototype.getResolvedSchema = function (schemaId) {
253         var id = normalizeId(schemaId);
254         var schemaHandle = this.schemasById[id];
255         if (schemaHandle) {
256             return schemaHandle.getResolvedSchema();
257         }
258         return this.promise.resolve(undefined);
259     };
260     JSONSchemaService.prototype.loadSchema = function (url) {
261         if (!this.requestService) {
262             var errorMessage = localize('json.schema.norequestservice', 'Unable to load schema from \'{0}\'. No schema request service available', toDisplayString(url));
263             return this.promise.resolve(new UnresolvedSchema({}, [errorMessage]));
264         }
265         return this.requestService(url).then(function (content) {
266             if (!content) {
267                 var errorMessage = localize('json.schema.nocontent', 'Unable to load schema from \'{0}\': No content.', toDisplayString(url));
268                 return new UnresolvedSchema({}, [errorMessage]);
269             }
270             var schemaContent = {};
271             var jsonErrors = [];
272             schemaContent = Json.parse(content, jsonErrors);
273             var errors = jsonErrors.length ? [localize('json.schema.invalidFormat', 'Unable to parse content from \'{0}\': Parse error at offset {1}.', toDisplayString(url), jsonErrors[0].offset)] : [];
274             return new UnresolvedSchema(schemaContent, errors);
275         }, function (error) {
276             var errorMessage = error.toString();
277             var errorSplit = error.toString().split('Error: ');
278             if (errorSplit.length > 1) {
279                 // more concise error message, URL and context are attached by caller anyways
280                 errorMessage = errorSplit[1];
281             }
282             if (Strings.endsWith(errorMessage, '.')) {
283                 errorMessage = errorMessage.substr(0, errorMessage.length - 1);
284             }
285             return new UnresolvedSchema({}, [localize('json.schema.nocontent', 'Unable to load schema from \'{0}\': {1}.', toDisplayString(url), errorMessage)]);
286         });
287     };
288     JSONSchemaService.prototype.resolveSchemaContent = function (schemaToResolve, schemaURL, dependencies) {
289         var _this = this;
290         var resolveErrors = schemaToResolve.errors.slice(0);
291         var schema = schemaToResolve.schema;
292         if (schema.$schema) {
293             var id = normalizeId(schema.$schema);
294             if (id === 'http://json-schema.org/draft-03/schema') {
295                 return this.promise.resolve(new ResolvedSchema({}, [localize('json.schema.draft03.notsupported', "Draft-03 schemas are not supported.")]));
296             }
297             else if (id === 'https://json-schema.org/draft/2019-09/schema') {
298                 resolveErrors.push(localize('json.schema.draft201909.notsupported', "Draft 2019-09 schemas are not yet fully supported."));
299             }
300         }
301         var contextService = this.contextService;
302         var findSection = function (schema, path) {
303             if (!path) {
304                 return schema;
305             }
306             var current = schema;
307             if (path[0] === '/') {
308                 path = path.substr(1);
309             }
310             path.split('/').some(function (part) {
311                 part = part.replace(/~1/g, '/').replace(/~0/g, '~');
312                 current = current[part];
313                 return !current;
314             });
315             return current;
316         };
317         var merge = function (target, sourceRoot, sourceURI, refSegment) {
318             var path = refSegment ? decodeURIComponent(refSegment) : undefined;
319             var section = findSection(sourceRoot, path);
320             if (section) {
321                 for (var key in section) {
322                     if (section.hasOwnProperty(key) && !target.hasOwnProperty(key)) {
323                         target[key] = section[key];
324                     }
325                 }
326             }
327             else {
328                 resolveErrors.push(localize('json.schema.invalidref', '$ref \'{0}\' in \'{1}\' can not be resolved.', path, sourceURI));
329             }
330         };
331         var resolveExternalLink = function (node, uri, refSegment, parentSchemaURL, parentSchemaDependencies) {
332             if (contextService && !/^\w+:\/\/.*/.test(uri)) {
333                 uri = contextService.resolveRelativePath(uri, parentSchemaURL);
334             }
335             uri = normalizeId(uri);
336             var referencedHandle = _this.getOrAddSchemaHandle(uri);
337             return referencedHandle.getUnresolvedSchema().then(function (unresolvedSchema) {
338                 parentSchemaDependencies[uri] = true;
339                 if (unresolvedSchema.errors.length) {
340                     var loc = refSegment ? uri + '#' + refSegment : uri;
341                     resolveErrors.push(localize('json.schema.problemloadingref', 'Problems loading reference \'{0}\': {1}', loc, unresolvedSchema.errors[0]));
342                 }
343                 merge(node, unresolvedSchema.schema, uri, refSegment);
344                 return resolveRefs(node, unresolvedSchema.schema, uri, referencedHandle.dependencies);
345             });
346         };
347         var resolveRefs = function (node, parentSchema, parentSchemaURL, parentSchemaDependencies) {
348             if (!node || typeof node !== 'object') {
349                 return Promise.resolve(null);
350             }
351             var toWalk = [node];
352             var seen = [];
353             var openPromises = [];
354             var collectEntries = function () {
355                 var entries = [];
356                 for (var _i = 0; _i < arguments.length; _i++) {
357                     entries[_i] = arguments[_i];
358                 }
359                 for (var _a = 0, entries_1 = entries; _a < entries_1.length; _a++) {
360                     var entry = entries_1[_a];
361                     if (typeof entry === 'object') {
362                         toWalk.push(entry);
363                     }
364                 }
365             };
366             var collectMapEntries = function () {
367                 var maps = [];
368                 for (var _i = 0; _i < arguments.length; _i++) {
369                     maps[_i] = arguments[_i];
370                 }
371                 for (var _a = 0, maps_1 = maps; _a < maps_1.length; _a++) {
372                     var map = maps_1[_a];
373                     if (typeof map === 'object') {
374                         for (var k in map) {
375                             var key = k;
376                             var entry = map[key];
377                             if (typeof entry === 'object') {
378                                 toWalk.push(entry);
379                             }
380                         }
381                     }
382                 }
383             };
384             var collectArrayEntries = function () {
385                 var arrays = [];
386                 for (var _i = 0; _i < arguments.length; _i++) {
387                     arrays[_i] = arguments[_i];
388                 }
389                 for (var _a = 0, arrays_1 = arrays; _a < arrays_1.length; _a++) {
390                     var array = arrays_1[_a];
391                     if (Array.isArray(array)) {
392                         for (var _b = 0, array_1 = array; _b < array_1.length; _b++) {
393                             var entry = array_1[_b];
394                             if (typeof entry === 'object') {
395                                 toWalk.push(entry);
396                             }
397                         }
398                     }
399                 }
400             };
401             var handleRef = function (next) {
402                 var seenRefs = [];
403                 while (next.$ref) {
404                     var ref = next.$ref;
405                     var segments = ref.split('#', 2);
406                     delete next.$ref;
407                     if (segments[0].length > 0) {
408                         openPromises.push(resolveExternalLink(next, segments[0], segments[1], parentSchemaURL, parentSchemaDependencies));
409                         return;
410                     }
411                     else {
412                         if (seenRefs.indexOf(ref) === -1) {
413                             merge(next, parentSchema, parentSchemaURL, segments[1]); // can set next.$ref again, use seenRefs to avoid circle
414                             seenRefs.push(ref);
415                         }
416                     }
417                 }
418                 collectEntries(next.items, next.additionalItems, next.additionalProperties, next.not, next.contains, next.propertyNames, next.if, next.then, next.else);
419                 collectMapEntries(next.definitions, next.properties, next.patternProperties, next.dependencies);
420                 collectArrayEntries(next.anyOf, next.allOf, next.oneOf, next.items);
421             };
422             while (toWalk.length) {
423                 var next = toWalk.pop();
424                 if (seen.indexOf(next) >= 0) {
425                     continue;
426                 }
427                 seen.push(next);
428                 handleRef(next);
429             }
430             return _this.promise.all(openPromises);
431         };
432         return resolveRefs(schema, schema, schemaURL, dependencies).then(function (_) { return new ResolvedSchema(schema, resolveErrors); });
433     };
434     JSONSchemaService.prototype.getSchemaForResource = function (resource, document) {
435         // first use $schema if present
436         if (document && document.root && document.root.type === 'object') {
437             var schemaProperties = document.root.properties.filter(function (p) { return (p.keyNode.value === '$schema') && p.valueNode && p.valueNode.type === 'string'; });
438             if (schemaProperties.length > 0) {
439                 var valueNode = schemaProperties[0].valueNode;
440                 if (valueNode && valueNode.type === 'string') {
441                     var schemeId = Parser.getNodeValue(valueNode);
442                     if (schemeId && Strings.startsWith(schemeId, '.') && this.contextService) {
443                         schemeId = this.contextService.resolveRelativePath(schemeId, resource);
444                     }
445                     if (schemeId) {
446                         var id = normalizeId(schemeId);
447                         return this.getOrAddSchemaHandle(id).getResolvedSchema();
448                     }
449                 }
450             }
451         }
452         if (this.cachedSchemaForResource && this.cachedSchemaForResource.resource === resource) {
453             return this.cachedSchemaForResource.resolvedSchema;
454         }
455         var seen = Object.create(null);
456         var schemas = [];
457         var normalizedResource = normalizeResourceForMatching(resource);
458         for (var _i = 0, _a = this.filePatternAssociations; _i < _a.length; _i++) {
459             var entry = _a[_i];
460             if (entry.matchesPattern(normalizedResource)) {
461                 for (var _b = 0, _c = entry.getURIs(); _b < _c.length; _b++) {
462                     var schemaId = _c[_b];
463                     if (!seen[schemaId]) {
464                         schemas.push(schemaId);
465                         seen[schemaId] = true;
466                     }
467                 }
468             }
469         }
470         var resolvedSchema = schemas.length > 0 ? this.createCombinedSchema(resource, schemas).getResolvedSchema() : this.promise.resolve(undefined);
471         this.cachedSchemaForResource = { resource: resource, resolvedSchema: resolvedSchema };
472         return resolvedSchema;
473     };
474     JSONSchemaService.prototype.createCombinedSchema = function (resource, schemaIds) {
475         if (schemaIds.length === 1) {
476             return this.getOrAddSchemaHandle(schemaIds[0]);
477         }
478         else {
479             var combinedSchemaId = 'schemaservice://combinedSchema/' + encodeURIComponent(resource);
480             var combinedSchema = {
481                 allOf: schemaIds.map(function (schemaId) { return ({ $ref: schemaId }); })
482             };
483             return this.addSchemaHandle(combinedSchemaId, combinedSchema);
484         }
485     };
486     JSONSchemaService.prototype.getMatchingSchemas = function (document, jsonDocument, schema) {
487         if (schema) {
488             var id = schema.id || ('schemaservice://untitled/matchingSchemas/' + idCounter++);
489             return this.resolveSchemaContent(new UnresolvedSchema(schema), id, {}).then(function (resolvedSchema) {
490                 return jsonDocument.getMatchingSchemas(resolvedSchema.schema).filter(function (s) { return !s.inverted; });
491             });
492         }
493         return this.getSchemaForResource(document.uri, jsonDocument).then(function (schema) {
494             if (schema) {
495                 return jsonDocument.getMatchingSchemas(schema.schema).filter(function (s) { return !s.inverted; });
496             }
497             return [];
498         });
499     };
500     return JSONSchemaService;
501 }());
502 export { JSONSchemaService };
503 var idCounter = 0;
504 function normalizeId(id) {
505     // remove trailing '#', normalize drive capitalization
506     try {
507         return URI.parse(id).toString();
508     }
509     catch (e) {
510         return id;
511     }
512 }
513 function normalizeResourceForMatching(resource) {
514     // remove queries and fragments, normalize drive capitalization
515     try {
516         return URI.parse(resource).with({ fragment: null, query: null }).toString();
517     }
518     catch (e) {
519         return resource;
520     }
521 }
522 function toDisplayString(url) {
523     try {
524         var uri = URI.parse(url);
525         if (uri.scheme === 'file') {
526             return uri.fsPath;
527         }
528     }
529     catch (e) {
530         // ignore
531     }
532     return url;
533 }