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