vscode-json-languageservice 5.7.2 → 6.0.0-next.2

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 (52) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/lib/esm/jsonContributions.d.ts +1 -1
  3. package/lib/esm/jsonLanguageService.d.ts +2 -2
  4. package/lib/esm/jsonLanguageService.js +13 -13
  5. package/lib/esm/jsonLanguageTypes.d.ts +10 -2
  6. package/lib/esm/jsonSchema.d.ts +71 -2
  7. package/lib/esm/parser/jsonParser.js +224 -93
  8. package/lib/esm/services/configuration.js +2 -2
  9. package/lib/esm/services/jsonCompletion.js +5 -5
  10. package/lib/esm/services/jsonDocumentSymbols.js +4 -4
  11. package/lib/esm/services/jsonFolding.js +1 -1
  12. package/lib/esm/services/jsonHover.js +2 -2
  13. package/lib/esm/services/jsonLinks.js +94 -3
  14. package/lib/esm/services/jsonSchemaService.js +639 -100
  15. package/lib/esm/services/jsonSelectionRanges.js +1 -1
  16. package/lib/esm/services/jsonValidation.js +4 -4
  17. package/lib/esm/services/schemas/draft-2019-09-flat.d.ts +1 -1
  18. package/lib/esm/services/schemas/draft-2020-12-flat.d.ts +1 -1
  19. package/lib/esm/services/vocabularies.js +139 -0
  20. package/lib/esm/utils/format.js +1 -1
  21. package/lib/esm/utils/sort.js +3 -3
  22. package/package.json +23 -20
  23. package/lib/umd/jsonContributions.d.ts +0 -21
  24. package/lib/umd/jsonContributions.js +0 -12
  25. package/lib/umd/jsonLanguageService.d.ts +0 -30
  26. package/lib/umd/jsonLanguageService.js +0 -79
  27. package/lib/umd/jsonLanguageTypes.d.ts +0 -305
  28. package/lib/umd/jsonLanguageTypes.js +0 -109
  29. package/lib/umd/jsonSchema.d.ts +0 -92
  30. package/lib/umd/jsonSchema.js +0 -12
  31. package/lib/umd/parser/jsonParser.js +0 -1365
  32. package/lib/umd/services/configuration.js +0 -536
  33. package/lib/umd/services/jsonCompletion.js +0 -982
  34. package/lib/umd/services/jsonDocumentSymbols.js +0 -285
  35. package/lib/umd/services/jsonFolding.js +0 -133
  36. package/lib/umd/services/jsonHover.js +0 -125
  37. package/lib/umd/services/jsonLinks.js +0 -85
  38. package/lib/umd/services/jsonSchemaService.js +0 -631
  39. package/lib/umd/services/jsonSelectionRanges.js +0 -74
  40. package/lib/umd/services/jsonValidation.js +0 -165
  41. package/lib/umd/services/schemas/draft-2019-09-flat.d.ts +0 -278
  42. package/lib/umd/services/schemas/draft-2019-09-flat.js +0 -314
  43. package/lib/umd/services/schemas/draft-2020-12-flat.d.ts +0 -276
  44. package/lib/umd/services/schemas/draft-2020-12-flat.js +0 -307
  45. package/lib/umd/utils/colors.js +0 -83
  46. package/lib/umd/utils/format.js +0 -33
  47. package/lib/umd/utils/glob.js +0 -137
  48. package/lib/umd/utils/json.js +0 -55
  49. package/lib/umd/utils/objects.js +0 -86
  50. package/lib/umd/utils/propertyTree.js +0 -90
  51. package/lib/umd/utils/sort.js +0 -384
  52. package/lib/umd/utils/strings.js +0 -97
@@ -1,982 +0,0 @@
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
- (function (factory) {
6
- if (typeof module === "object" && typeof module.exports === "object") {
7
- var v = factory(require, exports);
8
- if (v !== undefined) module.exports = v;
9
- }
10
- else if (typeof define === "function" && define.amd) {
11
- define(["require", "exports", "../parser/jsonParser", "jsonc-parser", "../utils/json", "../utils/strings", "../utils/objects", "../jsonLanguageTypes", "@vscode/l10n"], factory);
12
- }
13
- })(function (require, exports) {
14
- "use strict";
15
- Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.JSONCompletion = void 0;
17
- const Parser = require("../parser/jsonParser");
18
- const Json = require("jsonc-parser");
19
- const json_1 = require("../utils/json");
20
- const strings_1 = require("../utils/strings");
21
- const objects_1 = require("../utils/objects");
22
- const jsonLanguageTypes_1 = require("../jsonLanguageTypes");
23
- const l10n = require("@vscode/l10n");
24
- const valueCommitCharacters = [',', '}', ']'];
25
- const propertyCommitCharacters = [':'];
26
- class JSONCompletion {
27
- constructor(schemaService, contributions = [], promiseConstructor = Promise, clientCapabilities = {}) {
28
- this.schemaService = schemaService;
29
- this.contributions = contributions;
30
- this.promiseConstructor = promiseConstructor;
31
- this.clientCapabilities = clientCapabilities;
32
- }
33
- doResolve(item) {
34
- for (let i = this.contributions.length - 1; i >= 0; i--) {
35
- const resolveCompletion = this.contributions[i].resolveCompletion;
36
- if (resolveCompletion) {
37
- const resolver = resolveCompletion(item);
38
- if (resolver) {
39
- return resolver;
40
- }
41
- }
42
- }
43
- return this.promiseConstructor.resolve(item);
44
- }
45
- doComplete(document, position, doc) {
46
- const result = {
47
- items: [],
48
- isIncomplete: false
49
- };
50
- const text = document.getText();
51
- const offset = document.offsetAt(position);
52
- let node = doc.getNodeFromOffset(offset, true);
53
- if (this.isInComment(document, node ? node.offset : 0, offset)) {
54
- return Promise.resolve(result);
55
- }
56
- if (node && (offset === node.offset + node.length) && offset > 0) {
57
- const ch = text[offset - 1];
58
- if (node.type === 'object' && ch === '}' || node.type === 'array' && ch === ']') {
59
- // after ] or }
60
- node = node.parent;
61
- }
62
- }
63
- const currentWord = this.getCurrentWord(document, offset);
64
- let overwriteRange;
65
- if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) {
66
- overwriteRange = jsonLanguageTypes_1.Range.create(document.positionAt(node.offset), document.positionAt(node.offset + node.length));
67
- }
68
- else {
69
- let overwriteStart = offset - currentWord.length;
70
- if (overwriteStart > 0 && text[overwriteStart - 1] === '"') {
71
- overwriteStart--;
72
- }
73
- overwriteRange = jsonLanguageTypes_1.Range.create(document.positionAt(overwriteStart), position);
74
- }
75
- const supportsCommitCharacters = false; //this.doesSupportsCommitCharacters(); disabled for now, waiting for new API: https://github.com/microsoft/vscode/issues/42544
76
- const proposed = new Map();
77
- const collector = {
78
- add: (suggestion) => {
79
- let label = suggestion.label;
80
- const existing = proposed.get(label);
81
- if (!existing) {
82
- label = label.replace(/[\n]/g, '↵');
83
- if (label.length > 60) {
84
- const shortendedLabel = label.substr(0, 57).trim() + '...';
85
- if (!proposed.has(shortendedLabel)) {
86
- label = shortendedLabel;
87
- }
88
- }
89
- suggestion.textEdit = jsonLanguageTypes_1.TextEdit.replace(overwriteRange, suggestion.insertText);
90
- if (supportsCommitCharacters) {
91
- suggestion.commitCharacters = suggestion.kind === jsonLanguageTypes_1.CompletionItemKind.Property ? propertyCommitCharacters : valueCommitCharacters;
92
- }
93
- suggestion.label = label;
94
- proposed.set(label, suggestion);
95
- result.items.push(suggestion);
96
- }
97
- else {
98
- if (!existing.documentation) {
99
- existing.documentation = suggestion.documentation;
100
- }
101
- if (!existing.detail) {
102
- existing.detail = suggestion.detail;
103
- }
104
- if (!existing.labelDetails) {
105
- existing.labelDetails = suggestion.labelDetails;
106
- }
107
- }
108
- },
109
- setAsIncomplete: () => {
110
- result.isIncomplete = true;
111
- },
112
- error: (message) => {
113
- console.error(message);
114
- },
115
- getNumberOfProposals: () => {
116
- return result.items.length;
117
- }
118
- };
119
- return this.schemaService.getSchemaForResource(document.uri, doc).then((schema) => {
120
- const collectionPromises = [];
121
- let addValue = true;
122
- let currentKey = '';
123
- let currentProperty = undefined;
124
- if (node) {
125
- if (node.type === 'string') {
126
- const parent = node.parent;
127
- if (parent && parent.type === 'property' && parent.keyNode === node) {
128
- addValue = !parent.valueNode;
129
- currentProperty = parent;
130
- currentKey = text.substr(node.offset + 1, node.length - 2);
131
- if (parent) {
132
- node = parent.parent;
133
- }
134
- }
135
- }
136
- }
137
- // proposals for properties
138
- if (node && node.type === 'object') {
139
- // don't suggest keys when the cursor is just before the opening curly brace
140
- if (node.offset === offset) {
141
- return result;
142
- }
143
- // don't suggest properties that are already present
144
- const properties = node.properties;
145
- properties.forEach(p => {
146
- if (!currentProperty || currentProperty !== p) {
147
- proposed.set(p.keyNode.value, jsonLanguageTypes_1.CompletionItem.create('__'));
148
- }
149
- });
150
- let separatorAfter = '';
151
- if (addValue) {
152
- separatorAfter = this.evaluateSeparatorAfter(document, document.offsetAt(overwriteRange.end));
153
- }
154
- if (schema) {
155
- // property proposals with schema
156
- this.getPropertyCompletions(schema, doc, node, addValue, separatorAfter, collector);
157
- }
158
- else {
159
- // property proposals without schema
160
- this.getSchemaLessPropertyCompletions(doc, node, currentKey, collector);
161
- }
162
- const location = Parser.getNodePath(node);
163
- this.contributions.forEach((contribution) => {
164
- const collectPromise = contribution.collectPropertyCompletions(document.uri, location, currentWord, addValue, separatorAfter === '', collector);
165
- if (collectPromise) {
166
- collectionPromises.push(collectPromise);
167
- }
168
- });
169
- if ((!schema && currentWord.length > 0 && text.charAt(offset - currentWord.length - 1) !== '"')) {
170
- collector.add({
171
- kind: jsonLanguageTypes_1.CompletionItemKind.Property,
172
- label: this.getLabelForValue(currentWord),
173
- insertText: this.getInsertTextForProperty(currentWord, undefined, false, separatorAfter),
174
- insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet, documentation: '',
175
- });
176
- collector.setAsIncomplete();
177
- }
178
- }
179
- // proposals for values
180
- const types = {};
181
- if (schema) {
182
- // value proposals with schema
183
- this.getValueCompletions(schema, doc, node, offset, document, collector, types);
184
- }
185
- else {
186
- // value proposals without schema
187
- this.getSchemaLessValueCompletions(doc, node, offset, document, collector);
188
- }
189
- if (this.contributions.length > 0) {
190
- this.getContributedValueCompletions(doc, node, offset, document, collector, collectionPromises);
191
- }
192
- return this.promiseConstructor.all(collectionPromises).then(() => {
193
- if (collector.getNumberOfProposals() === 0) {
194
- let offsetForSeparator = offset;
195
- if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) {
196
- offsetForSeparator = node.offset + node.length;
197
- }
198
- const separatorAfter = this.evaluateSeparatorAfter(document, offsetForSeparator);
199
- this.addFillerValueCompletions(types, separatorAfter, collector);
200
- }
201
- return result;
202
- });
203
- });
204
- }
205
- getPropertyCompletions(schema, doc, node, addValue, separatorAfter, collector) {
206
- const matchingSchemas = doc.getMatchingSchemas(schema.schema, node.offset);
207
- matchingSchemas.forEach((s) => {
208
- if (s.node === node && !s.inverted) {
209
- const schemaProperties = s.schema.properties;
210
- if (schemaProperties) {
211
- Object.keys(schemaProperties).forEach((key) => {
212
- const propertySchema = schemaProperties[key];
213
- if (typeof propertySchema === 'object' && !propertySchema.deprecationMessage && !propertySchema.doNotSuggest) {
214
- const proposal = {
215
- kind: jsonLanguageTypes_1.CompletionItemKind.Property,
216
- label: key,
217
- insertText: this.getInsertTextForProperty(key, propertySchema, addValue, separatorAfter),
218
- insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
219
- filterText: this.getFilterTextForValue(key),
220
- documentation: this.fromMarkup(propertySchema.markdownDescription) || propertySchema.description || ''
221
- };
222
- if (propertySchema.completionDetail !== undefined) {
223
- proposal.detail = propertySchema.completionDetail;
224
- }
225
- if (propertySchema.suggestSortText !== undefined) {
226
- proposal.sortText = propertySchema.suggestSortText;
227
- }
228
- if (proposal.insertText && (0, strings_1.endsWith)(proposal.insertText, `$1${separatorAfter}`)) {
229
- proposal.command = {
230
- title: 'Suggest',
231
- command: 'editor.action.triggerSuggest'
232
- };
233
- }
234
- collector.add(proposal);
235
- }
236
- });
237
- }
238
- const schemaPropertyNames = s.schema.propertyNames;
239
- if (typeof schemaPropertyNames === 'object' && !schemaPropertyNames.deprecationMessage && !schemaPropertyNames.doNotSuggest) {
240
- const propertyNameCompletionItem = (name, documentation, detail, sortText) => {
241
- const proposal = {
242
- kind: jsonLanguageTypes_1.CompletionItemKind.Property,
243
- label: name,
244
- insertText: this.getInsertTextForProperty(name, undefined, addValue, separatorAfter),
245
- insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
246
- filterText: this.getFilterTextForValue(name),
247
- documentation: documentation || this.fromMarkup(schemaPropertyNames.markdownDescription) || schemaPropertyNames.description || '',
248
- sortText,
249
- detail
250
- };
251
- if (proposal.insertText && (0, strings_1.endsWith)(proposal.insertText, `$1${separatorAfter}`)) {
252
- proposal.command = {
253
- title: 'Suggest',
254
- command: 'editor.action.triggerSuggest'
255
- };
256
- }
257
- collector.add(proposal);
258
- };
259
- if (schemaPropertyNames.enum) {
260
- for (let i = 0; i < schemaPropertyNames.enum.length; i++) {
261
- let enumDescription = undefined;
262
- if (schemaPropertyNames.markdownEnumDescriptions && i < schemaPropertyNames.markdownEnumDescriptions.length) {
263
- enumDescription = this.fromMarkup(schemaPropertyNames.markdownEnumDescriptions[i]);
264
- }
265
- else if (schemaPropertyNames.enumDescriptions && i < schemaPropertyNames.enumDescriptions.length) {
266
- enumDescription = schemaPropertyNames.enumDescriptions[i];
267
- }
268
- const enumSortText = schemaPropertyNames.enumSortTexts?.[i];
269
- const enumDetails = schemaPropertyNames.enumDetails?.[i];
270
- propertyNameCompletionItem(schemaPropertyNames.enum[i], enumDescription, enumDetails, enumSortText);
271
- }
272
- }
273
- if (schemaPropertyNames.examples) {
274
- for (let i = 0; i < schemaPropertyNames.examples.length; i++) {
275
- propertyNameCompletionItem(schemaPropertyNames.examples[i], undefined, undefined, undefined);
276
- }
277
- }
278
- if (schemaPropertyNames.const) {
279
- propertyNameCompletionItem(schemaPropertyNames.const, undefined, schemaPropertyNames.completionDetail, schemaPropertyNames.suggestSortText);
280
- }
281
- }
282
- }
283
- });
284
- }
285
- getSchemaLessPropertyCompletions(doc, node, currentKey, collector) {
286
- const collectCompletionsForSimilarObject = (obj) => {
287
- obj.properties.forEach((p) => {
288
- const key = p.keyNode.value;
289
- collector.add({
290
- kind: jsonLanguageTypes_1.CompletionItemKind.Property,
291
- label: key,
292
- insertText: this.getInsertTextForValue(key, ''),
293
- insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
294
- filterText: this.getFilterTextForValue(key),
295
- documentation: ''
296
- });
297
- });
298
- };
299
- if (node.parent) {
300
- if (node.parent.type === 'property') {
301
- // if the object is a property value, check the tree for other objects that hang under a property of the same name
302
- const parentKey = node.parent.keyNode.value;
303
- doc.visit(n => {
304
- if (n.type === 'property' && n !== node.parent && n.keyNode.value === parentKey && n.valueNode && n.valueNode.type === 'object') {
305
- collectCompletionsForSimilarObject(n.valueNode);
306
- }
307
- return true;
308
- });
309
- }
310
- else if (node.parent.type === 'array') {
311
- // if the object is in an array, use all other array elements as similar objects
312
- node.parent.items.forEach(n => {
313
- if (n.type === 'object' && n !== node) {
314
- collectCompletionsForSimilarObject(n);
315
- }
316
- });
317
- }
318
- }
319
- else if (node.type === 'object') {
320
- collector.add({
321
- kind: jsonLanguageTypes_1.CompletionItemKind.Property,
322
- label: '$schema',
323
- insertText: this.getInsertTextForProperty('$schema', undefined, true, ''),
324
- insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet, documentation: '',
325
- filterText: this.getFilterTextForValue("$schema")
326
- });
327
- }
328
- }
329
- getSchemaLessValueCompletions(doc, node, offset, document, collector) {
330
- let offsetForSeparator = offset;
331
- if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) {
332
- offsetForSeparator = node.offset + node.length;
333
- node = node.parent;
334
- }
335
- if (!node) {
336
- collector.add({
337
- kind: this.getSuggestionKind('object'),
338
- label: 'Empty object',
339
- insertText: this.getInsertTextForValue({}, ''),
340
- insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
341
- documentation: ''
342
- });
343
- collector.add({
344
- kind: this.getSuggestionKind('array'),
345
- label: 'Empty array',
346
- insertText: this.getInsertTextForValue([], ''),
347
- insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
348
- documentation: ''
349
- });
350
- return;
351
- }
352
- const separatorAfter = this.evaluateSeparatorAfter(document, offsetForSeparator);
353
- const collectSuggestionsForValues = (value) => {
354
- if (value.parent && !Parser.contains(value.parent, offset, true)) {
355
- collector.add({
356
- kind: this.getSuggestionKind(value.type),
357
- label: this.getLabelTextForMatchingNode(value, document),
358
- insertText: this.getInsertTextForMatchingNode(value, document, separatorAfter),
359
- insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet, documentation: ''
360
- });
361
- }
362
- if (value.type === 'boolean') {
363
- this.addBooleanValueCompletion(!value.value, separatorAfter, collector);
364
- }
365
- };
366
- if (node.type === 'property') {
367
- if (offset > (node.colonOffset || 0)) {
368
- const valueNode = node.valueNode;
369
- if (valueNode && (offset > (valueNode.offset + valueNode.length) || valueNode.type === 'object' || valueNode.type === 'array')) {
370
- return;
371
- }
372
- // suggest values at the same key
373
- const parentKey = node.keyNode.value;
374
- doc.visit(n => {
375
- if (n.type === 'property' && n.keyNode.value === parentKey && n.valueNode) {
376
- collectSuggestionsForValues(n.valueNode);
377
- }
378
- return true;
379
- });
380
- if (parentKey === '$schema' && node.parent && !node.parent.parent) {
381
- this.addDollarSchemaCompletions(separatorAfter, collector);
382
- }
383
- }
384
- }
385
- if (node.type === 'array') {
386
- if (node.parent && node.parent.type === 'property') {
387
- // suggest items of an array at the same key
388
- const parentKey = node.parent.keyNode.value;
389
- doc.visit((n) => {
390
- if (n.type === 'property' && n.keyNode.value === parentKey && n.valueNode && n.valueNode.type === 'array') {
391
- n.valueNode.items.forEach(collectSuggestionsForValues);
392
- }
393
- return true;
394
- });
395
- }
396
- else {
397
- // suggest items in the same array
398
- node.items.forEach(collectSuggestionsForValues);
399
- }
400
- }
401
- }
402
- getValueCompletions(schema, doc, node, offset, document, collector, types) {
403
- let offsetForSeparator = offset;
404
- let parentKey = undefined;
405
- let valueNode = undefined;
406
- if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) {
407
- offsetForSeparator = node.offset + node.length;
408
- valueNode = node;
409
- node = node.parent;
410
- }
411
- if (!node) {
412
- this.addSchemaValueCompletions(schema.schema, '', collector, types);
413
- return;
414
- }
415
- if ((node.type === 'property') && offset > (node.colonOffset || 0)) {
416
- const valueNode = node.valueNode;
417
- if (valueNode && offset > (valueNode.offset + valueNode.length)) {
418
- return; // we are past the value node
419
- }
420
- parentKey = node.keyNode.value;
421
- node = node.parent;
422
- }
423
- if (node && (parentKey !== undefined || node.type === 'array')) {
424
- const separatorAfter = this.evaluateSeparatorAfter(document, offsetForSeparator);
425
- const matchingSchemas = doc.getMatchingSchemas(schema.schema, node.offset, valueNode);
426
- for (const s of matchingSchemas) {
427
- if (s.node === node && !s.inverted && s.schema) {
428
- if (node.type === 'array' && s.schema.items) {
429
- let c = collector;
430
- if (s.schema.uniqueItems) {
431
- const existingValues = new Set();
432
- node.children.forEach(n => {
433
- if (n.type !== 'array' && n.type !== 'object') {
434
- existingValues.add(this.getLabelForValue(Parser.getNodeValue(n)));
435
- }
436
- });
437
- c = {
438
- ...collector,
439
- add(suggestion) {
440
- if (!existingValues.has(suggestion.label)) {
441
- collector.add(suggestion);
442
- }
443
- }
444
- };
445
- }
446
- if (Array.isArray(s.schema.items)) {
447
- const index = this.findItemAtOffset(node, document, offset);
448
- if (index < s.schema.items.length) {
449
- this.addSchemaValueCompletions(s.schema.items[index], separatorAfter, c, types);
450
- }
451
- }
452
- else {
453
- this.addSchemaValueCompletions(s.schema.items, separatorAfter, c, types);
454
- }
455
- }
456
- if (parentKey !== undefined) {
457
- let propertyMatched = false;
458
- if (s.schema.properties) {
459
- const propertySchema = s.schema.properties[parentKey];
460
- if (propertySchema) {
461
- propertyMatched = true;
462
- this.addSchemaValueCompletions(propertySchema, separatorAfter, collector, types);
463
- }
464
- }
465
- if (s.schema.patternProperties && !propertyMatched) {
466
- for (const pattern of Object.keys(s.schema.patternProperties)) {
467
- const regex = (0, strings_1.extendedRegExp)(pattern);
468
- if (regex?.test(parentKey)) {
469
- propertyMatched = true;
470
- const propertySchema = s.schema.patternProperties[pattern];
471
- this.addSchemaValueCompletions(propertySchema, separatorAfter, collector, types);
472
- }
473
- }
474
- }
475
- if (s.schema.additionalProperties && !propertyMatched) {
476
- const propertySchema = s.schema.additionalProperties;
477
- this.addSchemaValueCompletions(propertySchema, separatorAfter, collector, types);
478
- }
479
- }
480
- }
481
- }
482
- if (parentKey === '$schema' && !node.parent) {
483
- this.addDollarSchemaCompletions(separatorAfter, collector);
484
- }
485
- if (types['boolean']) {
486
- this.addBooleanValueCompletion(true, separatorAfter, collector);
487
- this.addBooleanValueCompletion(false, separatorAfter, collector);
488
- }
489
- if (types['null']) {
490
- this.addNullValueCompletion(separatorAfter, collector);
491
- }
492
- }
493
- }
494
- getContributedValueCompletions(doc, node, offset, document, collector, collectionPromises) {
495
- if (!node) {
496
- this.contributions.forEach((contribution) => {
497
- const collectPromise = contribution.collectDefaultCompletions(document.uri, collector);
498
- if (collectPromise) {
499
- collectionPromises.push(collectPromise);
500
- }
501
- });
502
- }
503
- else {
504
- if (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null') {
505
- node = node.parent;
506
- }
507
- if (node && (node.type === 'property') && offset > (node.colonOffset || 0)) {
508
- const parentKey = node.keyNode.value;
509
- const valueNode = node.valueNode;
510
- if ((!valueNode || offset <= (valueNode.offset + valueNode.length)) && node.parent) {
511
- const location = Parser.getNodePath(node.parent);
512
- this.contributions.forEach((contribution) => {
513
- const collectPromise = contribution.collectValueCompletions(document.uri, location, parentKey, collector);
514
- if (collectPromise) {
515
- collectionPromises.push(collectPromise);
516
- }
517
- });
518
- }
519
- }
520
- }
521
- }
522
- addSchemaValueCompletions(schema, separatorAfter, collector, types) {
523
- if (typeof schema === 'object') {
524
- this.addEnumValueCompletions(schema, separatorAfter, collector);
525
- this.addDefaultValueCompletions(schema, separatorAfter, collector);
526
- this.collectTypes(schema, types);
527
- if (Array.isArray(schema.allOf)) {
528
- schema.allOf.forEach(s => this.addSchemaValueCompletions(s, separatorAfter, collector, types));
529
- }
530
- if (Array.isArray(schema.anyOf)) {
531
- schema.anyOf.forEach(s => this.addSchemaValueCompletions(s, separatorAfter, collector, types));
532
- }
533
- if (Array.isArray(schema.oneOf)) {
534
- schema.oneOf.forEach(s => this.addSchemaValueCompletions(s, separatorAfter, collector, types));
535
- }
536
- }
537
- }
538
- addDefaultValueCompletions(schema, separatorAfter, collector, arrayDepth = 0) {
539
- let hasProposals = false;
540
- if ((0, objects_1.isDefined)(schema.default)) {
541
- let type = schema.type;
542
- let value = schema.default;
543
- for (let i = arrayDepth; i > 0; i--) {
544
- value = [value];
545
- type = 'array';
546
- }
547
- const completionItem = {
548
- kind: this.getSuggestionKind(type),
549
- label: this.getLabelForValue(value),
550
- insertText: this.getInsertTextForValue(value, separatorAfter),
551
- insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet
552
- };
553
- if (this.doesSupportsLabelDetails()) {
554
- completionItem.labelDetails = { description: l10n.t('Default value') };
555
- }
556
- else {
557
- completionItem.detail = l10n.t('Default value');
558
- }
559
- collector.add(completionItem);
560
- hasProposals = true;
561
- }
562
- if (Array.isArray(schema.examples)) {
563
- schema.examples.forEach(example => {
564
- let type = schema.type;
565
- let value = example;
566
- for (let i = arrayDepth; i > 0; i--) {
567
- value = [value];
568
- type = 'array';
569
- }
570
- collector.add({
571
- kind: this.getSuggestionKind(type),
572
- label: this.getLabelForValue(value),
573
- insertText: this.getInsertTextForValue(value, separatorAfter),
574
- insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet
575
- });
576
- hasProposals = true;
577
- });
578
- }
579
- if (Array.isArray(schema.defaultSnippets)) {
580
- schema.defaultSnippets.forEach(s => {
581
- let type = schema.type;
582
- let value = s.body;
583
- let label = s.label;
584
- let insertText;
585
- let filterText;
586
- if ((0, objects_1.isDefined)(value)) {
587
- let type = schema.type;
588
- for (let i = arrayDepth; i > 0; i--) {
589
- value = [value];
590
- type = 'array';
591
- }
592
- insertText = this.getInsertTextForSnippetValue(value, separatorAfter);
593
- filterText = this.getFilterTextForSnippetValue(value);
594
- label = label || this.getLabelForSnippetValue(value);
595
- }
596
- else if (typeof s.bodyText === 'string') {
597
- let prefix = '', suffix = '', indent = '';
598
- for (let i = arrayDepth; i > 0; i--) {
599
- prefix = prefix + indent + '[\n';
600
- suffix = suffix + '\n' + indent + ']';
601
- indent += '\t';
602
- type = 'array';
603
- }
604
- insertText = prefix + indent + s.bodyText.split('\n').join('\n' + indent) + suffix + separatorAfter;
605
- label = label || insertText,
606
- filterText = insertText.replace(/[\n]/g, ''); // remove new lines
607
- }
608
- else {
609
- return;
610
- }
611
- collector.add({
612
- kind: this.getSuggestionKind(type),
613
- label,
614
- documentation: this.fromMarkup(s.markdownDescription) || s.description,
615
- insertText,
616
- insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
617
- filterText
618
- });
619
- hasProposals = true;
620
- });
621
- }
622
- if (!hasProposals && typeof schema.items === 'object' && !Array.isArray(schema.items) && arrayDepth < 5 /* beware of recursion */) {
623
- this.addDefaultValueCompletions(schema.items, separatorAfter, collector, arrayDepth + 1);
624
- }
625
- }
626
- addEnumValueCompletions(schema, separatorAfter, collector) {
627
- if ((0, objects_1.isDefined)(schema.const)) {
628
- collector.add({
629
- kind: this.getSuggestionKind(schema.type),
630
- label: this.getLabelForValue(schema.const),
631
- insertText: this.getInsertTextForValue(schema.const, separatorAfter),
632
- insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
633
- documentation: this.fromMarkup(schema.markdownDescription) || schema.description
634
- });
635
- }
636
- if (Array.isArray(schema.enum)) {
637
- for (let i = 0, length = schema.enum.length; i < length; i++) {
638
- const enm = schema.enum[i];
639
- let documentation = this.fromMarkup(schema.markdownDescription) || schema.description;
640
- if (schema.markdownEnumDescriptions && i < schema.markdownEnumDescriptions.length && this.doesSupportMarkdown()) {
641
- documentation = this.fromMarkup(schema.markdownEnumDescriptions[i]);
642
- }
643
- else if (schema.enumDescriptions && i < schema.enumDescriptions.length) {
644
- documentation = schema.enumDescriptions[i];
645
- }
646
- collector.add({
647
- kind: this.getSuggestionKind(schema.type),
648
- label: this.getLabelForValue(enm),
649
- insertText: this.getInsertTextForValue(enm, separatorAfter),
650
- insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
651
- sortText: schema.enumSortTexts?.[i],
652
- detail: schema.enumDetails?.[i],
653
- documentation
654
- });
655
- }
656
- }
657
- }
658
- collectTypes(schema, types) {
659
- if (Array.isArray(schema.enum) || (0, objects_1.isDefined)(schema.const)) {
660
- return;
661
- }
662
- const type = schema.type;
663
- if (Array.isArray(type)) {
664
- type.forEach(t => types[t] = true);
665
- }
666
- else if (type) {
667
- types[type] = true;
668
- }
669
- }
670
- addFillerValueCompletions(types, separatorAfter, collector) {
671
- if (types['object']) {
672
- collector.add({
673
- kind: this.getSuggestionKind('object'),
674
- label: '{}',
675
- insertText: this.getInsertTextForGuessedValue({}, separatorAfter),
676
- insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
677
- detail: l10n.t('New object'),
678
- documentation: ''
679
- });
680
- }
681
- if (types['array']) {
682
- collector.add({
683
- kind: this.getSuggestionKind('array'),
684
- label: '[]',
685
- insertText: this.getInsertTextForGuessedValue([], separatorAfter),
686
- insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
687
- detail: l10n.t('New array'),
688
- documentation: ''
689
- });
690
- }
691
- }
692
- addBooleanValueCompletion(value, separatorAfter, collector) {
693
- collector.add({
694
- kind: this.getSuggestionKind('boolean'),
695
- label: value ? 'true' : 'false',
696
- insertText: this.getInsertTextForValue(value, separatorAfter),
697
- insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
698
- documentation: ''
699
- });
700
- }
701
- addNullValueCompletion(separatorAfter, collector) {
702
- collector.add({
703
- kind: this.getSuggestionKind('null'),
704
- label: 'null',
705
- insertText: 'null' + separatorAfter,
706
- insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
707
- documentation: ''
708
- });
709
- }
710
- addDollarSchemaCompletions(separatorAfter, collector) {
711
- const schemaIds = this.schemaService.getRegisteredSchemaIds(schema => schema === 'http' || schema === 'https');
712
- schemaIds.forEach(schemaId => {
713
- if (schemaId.startsWith('https://json-schema.org/draft-')) {
714
- schemaId = schemaId + '#';
715
- }
716
- collector.add({
717
- kind: jsonLanguageTypes_1.CompletionItemKind.Module,
718
- label: this.getLabelForValue(schemaId),
719
- filterText: this.getFilterTextForValue(schemaId),
720
- insertText: this.getInsertTextForValue(schemaId, separatorAfter),
721
- insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet, documentation: ''
722
- });
723
- });
724
- }
725
- getLabelForValue(value) {
726
- return JSON.stringify(value);
727
- }
728
- getValueFromLabel(value) {
729
- return JSON.parse(value);
730
- }
731
- getFilterTextForValue(value) {
732
- return JSON.stringify(value);
733
- }
734
- getFilterTextForSnippetValue(value) {
735
- return JSON.stringify(value).replace(/\$\{\d+:([^}]+)\}|\$\d+/g, '$1');
736
- }
737
- getLabelForSnippetValue(value) {
738
- const label = JSON.stringify(value);
739
- return label.replace(/\$\{\d+:([^}]+)\}|\$\d+/g, '$1');
740
- }
741
- getInsertTextForPlainText(text) {
742
- return text.replace(/[\\\$\}]/g, '\\$&'); // escape $, \ and }
743
- }
744
- getInsertTextForValue(value, separatorAfter) {
745
- const text = JSON.stringify(value, null, '\t');
746
- if (text === '{}') {
747
- return '{$1}' + separatorAfter;
748
- }
749
- else if (text === '[]') {
750
- return '[$1]' + separatorAfter;
751
- }
752
- return this.getInsertTextForPlainText(text + separatorAfter);
753
- }
754
- getInsertTextForSnippetValue(value, separatorAfter) {
755
- const replacer = (value) => {
756
- if (typeof value === 'string') {
757
- if (value[0] === '^') {
758
- return value.substr(1);
759
- }
760
- }
761
- return JSON.stringify(value);
762
- };
763
- return (0, json_1.stringifyObject)(value, '', replacer) + separatorAfter;
764
- }
765
- getInsertTextForGuessedValue(value, separatorAfter) {
766
- switch (typeof value) {
767
- case 'object':
768
- if (value === null) {
769
- return '${1:null}' + separatorAfter;
770
- }
771
- return this.getInsertTextForValue(value, separatorAfter);
772
- case 'string':
773
- let snippetValue = JSON.stringify(value);
774
- snippetValue = snippetValue.substr(1, snippetValue.length - 2); // remove quotes
775
- snippetValue = this.getInsertTextForPlainText(snippetValue); // escape \ and }
776
- return '"${1:' + snippetValue + '}"' + separatorAfter;
777
- case 'number':
778
- case 'boolean':
779
- return '${1:' + JSON.stringify(value) + '}' + separatorAfter;
780
- }
781
- return this.getInsertTextForValue(value, separatorAfter);
782
- }
783
- getSuggestionKind(type) {
784
- if (Array.isArray(type)) {
785
- const array = type;
786
- type = array.length > 0 ? array[0] : undefined;
787
- }
788
- if (!type) {
789
- return jsonLanguageTypes_1.CompletionItemKind.Value;
790
- }
791
- switch (type) {
792
- case 'string': return jsonLanguageTypes_1.CompletionItemKind.Value;
793
- case 'object': return jsonLanguageTypes_1.CompletionItemKind.Module;
794
- case 'property': return jsonLanguageTypes_1.CompletionItemKind.Property;
795
- default: return jsonLanguageTypes_1.CompletionItemKind.Value;
796
- }
797
- }
798
- getLabelTextForMatchingNode(node, document) {
799
- switch (node.type) {
800
- case 'array':
801
- return '[]';
802
- case 'object':
803
- return '{}';
804
- default:
805
- const content = document.getText().substr(node.offset, node.length);
806
- return content;
807
- }
808
- }
809
- getInsertTextForMatchingNode(node, document, separatorAfter) {
810
- switch (node.type) {
811
- case 'array':
812
- return this.getInsertTextForValue([], separatorAfter);
813
- case 'object':
814
- return this.getInsertTextForValue({}, separatorAfter);
815
- default:
816
- const content = document.getText().substr(node.offset, node.length) + separatorAfter;
817
- return this.getInsertTextForPlainText(content);
818
- }
819
- }
820
- getInsertTextForProperty(key, propertySchema, addValue, separatorAfter) {
821
- const propertyText = this.getInsertTextForValue(key, '');
822
- if (!addValue) {
823
- return propertyText;
824
- }
825
- const resultText = propertyText + ': ';
826
- let value;
827
- let nValueProposals = 0;
828
- if (propertySchema) {
829
- if (Array.isArray(propertySchema.defaultSnippets)) {
830
- if (propertySchema.defaultSnippets.length === 1) {
831
- const body = propertySchema.defaultSnippets[0].body;
832
- if ((0, objects_1.isDefined)(body)) {
833
- value = this.getInsertTextForSnippetValue(body, '');
834
- }
835
- }
836
- nValueProposals += propertySchema.defaultSnippets.length;
837
- }
838
- if (propertySchema.enum) {
839
- if (!value && propertySchema.enum.length === 1) {
840
- value = this.getInsertTextForGuessedValue(propertySchema.enum[0], '');
841
- }
842
- nValueProposals += propertySchema.enum.length;
843
- }
844
- if ((0, objects_1.isDefined)(propertySchema.const)) {
845
- if (!value) {
846
- value = this.getInsertTextForGuessedValue(propertySchema.const, '');
847
- }
848
- nValueProposals++;
849
- }
850
- if ((0, objects_1.isDefined)(propertySchema.default)) {
851
- if (!value) {
852
- value = this.getInsertTextForGuessedValue(propertySchema.default, '');
853
- }
854
- nValueProposals++;
855
- }
856
- if (Array.isArray(propertySchema.examples) && propertySchema.examples.length) {
857
- if (!value) {
858
- value = this.getInsertTextForGuessedValue(propertySchema.examples[0], '');
859
- }
860
- nValueProposals += propertySchema.examples.length;
861
- }
862
- if (nValueProposals === 0) {
863
- let type = Array.isArray(propertySchema.type) ? propertySchema.type[0] : propertySchema.type;
864
- if (!type) {
865
- if (propertySchema.properties) {
866
- type = 'object';
867
- }
868
- else if (propertySchema.items) {
869
- type = 'array';
870
- }
871
- }
872
- switch (type) {
873
- case 'boolean':
874
- value = '$1';
875
- break;
876
- case 'string':
877
- value = '"$1"';
878
- break;
879
- case 'object':
880
- value = '{$1}';
881
- break;
882
- case 'array':
883
- value = '[$1]';
884
- break;
885
- case 'number':
886
- case 'integer':
887
- value = '${1:0}';
888
- break;
889
- case 'null':
890
- value = '${1:null}';
891
- break;
892
- default:
893
- return propertyText;
894
- }
895
- }
896
- }
897
- if (!value || nValueProposals > 1) {
898
- value = '$1';
899
- }
900
- return resultText + value + separatorAfter;
901
- }
902
- getCurrentWord(document, offset) {
903
- let i = offset - 1;
904
- const text = document.getText();
905
- while (i >= 0 && ' \t\n\r\v":{[,]}'.indexOf(text.charAt(i)) === -1) {
906
- i--;
907
- }
908
- return text.substring(i + 1, offset);
909
- }
910
- evaluateSeparatorAfter(document, offset) {
911
- const scanner = Json.createScanner(document.getText(), true);
912
- scanner.setPosition(offset);
913
- const token = scanner.scan();
914
- switch (token) {
915
- case 5 /* Json.SyntaxKind.CommaToken */:
916
- case 2 /* Json.SyntaxKind.CloseBraceToken */:
917
- case 4 /* Json.SyntaxKind.CloseBracketToken */:
918
- case 17 /* Json.SyntaxKind.EOF */:
919
- return '';
920
- default:
921
- return ',';
922
- }
923
- }
924
- findItemAtOffset(node, document, offset) {
925
- const scanner = Json.createScanner(document.getText(), true);
926
- const children = node.items;
927
- for (let i = children.length - 1; i >= 0; i--) {
928
- const child = children[i];
929
- if (offset > child.offset + child.length) {
930
- scanner.setPosition(child.offset + child.length);
931
- const token = scanner.scan();
932
- if (token === 5 /* Json.SyntaxKind.CommaToken */ && offset >= scanner.getTokenOffset() + scanner.getTokenLength()) {
933
- return i + 1;
934
- }
935
- return i;
936
- }
937
- else if (offset >= child.offset) {
938
- return i;
939
- }
940
- }
941
- return 0;
942
- }
943
- isInComment(document, start, offset) {
944
- const scanner = Json.createScanner(document.getText(), false);
945
- scanner.setPosition(start);
946
- let token = scanner.scan();
947
- while (token !== 17 /* Json.SyntaxKind.EOF */ && (scanner.getTokenOffset() + scanner.getTokenLength() < offset)) {
948
- token = scanner.scan();
949
- }
950
- return (token === 12 /* Json.SyntaxKind.LineCommentTrivia */ || token === 13 /* Json.SyntaxKind.BlockCommentTrivia */) && scanner.getTokenOffset() <= offset;
951
- }
952
- fromMarkup(markupString) {
953
- if (markupString && this.doesSupportMarkdown()) {
954
- return {
955
- kind: jsonLanguageTypes_1.MarkupKind.Markdown,
956
- value: markupString
957
- };
958
- }
959
- return undefined;
960
- }
961
- doesSupportMarkdown() {
962
- if (!(0, objects_1.isDefined)(this.supportsMarkdown)) {
963
- const documentationFormat = this.clientCapabilities.textDocument?.completion?.completionItem?.documentationFormat;
964
- this.supportsMarkdown = Array.isArray(documentationFormat) && documentationFormat.indexOf(jsonLanguageTypes_1.MarkupKind.Markdown) !== -1;
965
- }
966
- return this.supportsMarkdown;
967
- }
968
- doesSupportsCommitCharacters() {
969
- if (!(0, objects_1.isDefined)(this.supportsCommitCharacters)) {
970
- this.labelDetailsSupport = this.clientCapabilities.textDocument?.completion?.completionItem?.commitCharactersSupport;
971
- }
972
- return this.supportsCommitCharacters;
973
- }
974
- doesSupportsLabelDetails() {
975
- if (!(0, objects_1.isDefined)(this.labelDetailsSupport)) {
976
- this.labelDetailsSupport = this.clientCapabilities.textDocument?.completion?.completionItem?.labelDetailsSupport;
977
- }
978
- return this.labelDetailsSupport;
979
- }
980
- }
981
- exports.JSONCompletion = JSONCompletion;
982
- });