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