123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934 |
- /*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
- import * as Parser from '../parser/jsonParser';
- import * as Json from 'jsonc-parser';
- import { stringifyObject } from '../utils/json';
- import { endsWith, extendedRegExp } from '../utils/strings';
- import { isDefined } from '../utils/objects';
- import { CompletionItem, CompletionItemKind, Range, TextEdit, InsertTextFormat, MarkupKind } from '../jsonLanguageTypes';
- import * as nls from 'vscode-nls';
- var localize = nls.loadMessageBundle();
- var valueCommitCharacters = [',', '}', ']'];
- var propertyCommitCharacters = [':'];
- var JSONCompletion = /** @class */ (function () {
- function JSONCompletion(schemaService, contributions, promiseConstructor, clientCapabilities) {
- if (contributions === void 0) { contributions = []; }
- if (promiseConstructor === void 0) { promiseConstructor = Promise; }
- if (clientCapabilities === void 0) { clientCapabilities = {}; }
- this.schemaService = schemaService;
- this.contributions = contributions;
- this.promiseConstructor = promiseConstructor;
- this.clientCapabilities = clientCapabilities;
- }
- JSONCompletion.prototype.doResolve = function (item) {
- for (var i = this.contributions.length - 1; i >= 0; i--) {
- var resolveCompletion = this.contributions[i].resolveCompletion;
- if (resolveCompletion) {
- var resolver = resolveCompletion(item);
- if (resolver) {
- return resolver;
- }
- }
- }
- return this.promiseConstructor.resolve(item);
- };
- JSONCompletion.prototype.doComplete = function (document, position, doc) {
- var _this = this;
- var result = {
- items: [],
- isIncomplete: false
- };
- var text = document.getText();
- var offset = document.offsetAt(position);
- var node = doc.getNodeFromOffset(offset, true);
- if (this.isInComment(document, node ? node.offset : 0, offset)) {
- return Promise.resolve(result);
- }
- if (node && (offset === node.offset + node.length) && offset > 0) {
- var ch = text[offset - 1];
- if (node.type === 'object' && ch === '}' || node.type === 'array' && ch === ']') {
- // after ] or }
- node = node.parent;
- }
- }
- var currentWord = this.getCurrentWord(document, offset);
- var overwriteRange;
- if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) {
- overwriteRange = Range.create(document.positionAt(node.offset), document.positionAt(node.offset + node.length));
- }
- else {
- var overwriteStart = offset - currentWord.length;
- if (overwriteStart > 0 && text[overwriteStart - 1] === '"') {
- overwriteStart--;
- }
- overwriteRange = Range.create(document.positionAt(overwriteStart), position);
- }
- var supportsCommitCharacters = false; //this.doesSupportsCommitCharacters(); disabled for now, waiting for new API: https://github.com/microsoft/vscode/issues/42544
- var proposed = {};
- var collector = {
- add: function (suggestion) {
- var label = suggestion.label;
- var existing = proposed[label];
- if (!existing) {
- label = label.replace(/[\n]/g, '↵');
- if (label.length > 60) {
- var shortendedLabel = label.substr(0, 57).trim() + '...';
- if (!proposed[shortendedLabel]) {
- label = shortendedLabel;
- }
- }
- if (overwriteRange && suggestion.insertText !== undefined) {
- suggestion.textEdit = TextEdit.replace(overwriteRange, suggestion.insertText);
- }
- if (supportsCommitCharacters) {
- suggestion.commitCharacters = suggestion.kind === CompletionItemKind.Property ? propertyCommitCharacters : valueCommitCharacters;
- }
- suggestion.label = label;
- proposed[label] = suggestion;
- result.items.push(suggestion);
- }
- else {
- if (!existing.documentation) {
- existing.documentation = suggestion.documentation;
- }
- if (!existing.detail) {
- existing.detail = suggestion.detail;
- }
- }
- },
- setAsIncomplete: function () {
- result.isIncomplete = true;
- },
- error: function (message) {
- console.error(message);
- },
- log: function (message) {
- console.log(message);
- },
- getNumberOfProposals: function () {
- return result.items.length;
- }
- };
- return this.schemaService.getSchemaForResource(document.uri, doc).then(function (schema) {
- var collectionPromises = [];
- var addValue = true;
- var currentKey = '';
- var currentProperty = undefined;
- if (node) {
- if (node.type === 'string') {
- var parent = node.parent;
- if (parent && parent.type === 'property' && parent.keyNode === node) {
- addValue = !parent.valueNode;
- currentProperty = parent;
- currentKey = text.substr(node.offset + 1, node.length - 2);
- if (parent) {
- node = parent.parent;
- }
- }
- }
- }
- // proposals for properties
- if (node && node.type === 'object') {
- // don't suggest keys when the cursor is just before the opening curly brace
- if (node.offset === offset) {
- return result;
- }
- // don't suggest properties that are already present
- var properties = node.properties;
- properties.forEach(function (p) {
- if (!currentProperty || currentProperty !== p) {
- proposed[p.keyNode.value] = CompletionItem.create('__');
- }
- });
- var separatorAfter_1 = '';
- if (addValue) {
- separatorAfter_1 = _this.evaluateSeparatorAfter(document, document.offsetAt(overwriteRange.end));
- }
- if (schema) {
- // property proposals with schema
- _this.getPropertyCompletions(schema, doc, node, addValue, separatorAfter_1, collector);
- }
- else {
- // property proposals without schema
- _this.getSchemaLessPropertyCompletions(doc, node, currentKey, collector);
- }
- var location_1 = Parser.getNodePath(node);
- _this.contributions.forEach(function (contribution) {
- var collectPromise = contribution.collectPropertyCompletions(document.uri, location_1, currentWord, addValue, separatorAfter_1 === '', collector);
- if (collectPromise) {
- collectionPromises.push(collectPromise);
- }
- });
- if ((!schema && currentWord.length > 0 && text.charAt(offset - currentWord.length - 1) !== '"')) {
- collector.add({
- kind: CompletionItemKind.Property,
- label: _this.getLabelForValue(currentWord),
- insertText: _this.getInsertTextForProperty(currentWord, undefined, false, separatorAfter_1),
- insertTextFormat: InsertTextFormat.Snippet, documentation: '',
- });
- collector.setAsIncomplete();
- }
- }
- // proposals for values
- var types = {};
- if (schema) {
- // value proposals with schema
- _this.getValueCompletions(schema, doc, node, offset, document, collector, types);
- }
- else {
- // value proposals without schema
- _this.getSchemaLessValueCompletions(doc, node, offset, document, collector);
- }
- if (_this.contributions.length > 0) {
- _this.getContributedValueCompletions(doc, node, offset, document, collector, collectionPromises);
- }
- return _this.promiseConstructor.all(collectionPromises).then(function () {
- if (collector.getNumberOfProposals() === 0) {
- var offsetForSeparator = offset;
- if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) {
- offsetForSeparator = node.offset + node.length;
- }
- var separatorAfter = _this.evaluateSeparatorAfter(document, offsetForSeparator);
- _this.addFillerValueCompletions(types, separatorAfter, collector);
- }
- return result;
- });
- });
- };
- JSONCompletion.prototype.getPropertyCompletions = function (schema, doc, node, addValue, separatorAfter, collector) {
- var _this = this;
- var matchingSchemas = doc.getMatchingSchemas(schema.schema, node.offset);
- matchingSchemas.forEach(function (s) {
- if (s.node === node && !s.inverted) {
- var schemaProperties_1 = s.schema.properties;
- if (schemaProperties_1) {
- Object.keys(schemaProperties_1).forEach(function (key) {
- var propertySchema = schemaProperties_1[key];
- if (typeof propertySchema === 'object' && !propertySchema.deprecationMessage && !propertySchema.doNotSuggest) {
- var proposal = {
- kind: CompletionItemKind.Property,
- label: key,
- insertText: _this.getInsertTextForProperty(key, propertySchema, addValue, separatorAfter),
- insertTextFormat: InsertTextFormat.Snippet,
- filterText: _this.getFilterTextForValue(key),
- documentation: _this.fromMarkup(propertySchema.markdownDescription) || propertySchema.description || '',
- };
- if (propertySchema.suggestSortText !== undefined) {
- proposal.sortText = propertySchema.suggestSortText;
- }
- if (proposal.insertText && endsWith(proposal.insertText, "$1" + separatorAfter)) {
- proposal.command = {
- title: 'Suggest',
- command: 'editor.action.triggerSuggest'
- };
- }
- collector.add(proposal);
- }
- });
- }
- var schemaPropertyNames_1 = s.schema.propertyNames;
- if (typeof schemaPropertyNames_1 === 'object' && !schemaPropertyNames_1.deprecationMessage && !schemaPropertyNames_1.doNotSuggest) {
- var propertyNameCompletionItem = function (name, enumDescription) {
- if (enumDescription === void 0) { enumDescription = undefined; }
- var proposal = {
- kind: CompletionItemKind.Property,
- label: name,
- insertText: _this.getInsertTextForProperty(name, undefined, addValue, separatorAfter),
- insertTextFormat: InsertTextFormat.Snippet,
- filterText: _this.getFilterTextForValue(name),
- documentation: enumDescription || _this.fromMarkup(schemaPropertyNames_1.markdownDescription) || schemaPropertyNames_1.description || '',
- };
- if (schemaPropertyNames_1.suggestSortText !== undefined) {
- proposal.sortText = schemaPropertyNames_1.suggestSortText;
- }
- if (proposal.insertText && endsWith(proposal.insertText, "$1" + separatorAfter)) {
- proposal.command = {
- title: 'Suggest',
- command: 'editor.action.triggerSuggest'
- };
- }
- collector.add(proposal);
- };
- if (schemaPropertyNames_1.enum) {
- for (var i = 0; i < schemaPropertyNames_1.enum.length; i++) {
- var enumDescription = undefined;
- if (schemaPropertyNames_1.markdownEnumDescriptions && i < schemaPropertyNames_1.markdownEnumDescriptions.length) {
- enumDescription = _this.fromMarkup(schemaPropertyNames_1.markdownEnumDescriptions[i]);
- }
- else if (schemaPropertyNames_1.enumDescriptions && i < schemaPropertyNames_1.enumDescriptions.length) {
- enumDescription = schemaPropertyNames_1.enumDescriptions[i];
- }
- propertyNameCompletionItem(schemaPropertyNames_1.enum[i], enumDescription);
- }
- }
- if (schemaPropertyNames_1.const) {
- propertyNameCompletionItem(schemaPropertyNames_1.const);
- }
- }
- }
- });
- };
- JSONCompletion.prototype.getSchemaLessPropertyCompletions = function (doc, node, currentKey, collector) {
- var _this = this;
- var collectCompletionsForSimilarObject = function (obj) {
- obj.properties.forEach(function (p) {
- var key = p.keyNode.value;
- collector.add({
- kind: CompletionItemKind.Property,
- label: key,
- insertText: _this.getInsertTextForValue(key, ''),
- insertTextFormat: InsertTextFormat.Snippet,
- filterText: _this.getFilterTextForValue(key),
- documentation: ''
- });
- });
- };
- if (node.parent) {
- if (node.parent.type === 'property') {
- // if the object is a property value, check the tree for other objects that hang under a property of the same name
- var parentKey_1 = node.parent.keyNode.value;
- doc.visit(function (n) {
- if (n.type === 'property' && n !== node.parent && n.keyNode.value === parentKey_1 && n.valueNode && n.valueNode.type === 'object') {
- collectCompletionsForSimilarObject(n.valueNode);
- }
- return true;
- });
- }
- else if (node.parent.type === 'array') {
- // if the object is in an array, use all other array elements as similar objects
- node.parent.items.forEach(function (n) {
- if (n.type === 'object' && n !== node) {
- collectCompletionsForSimilarObject(n);
- }
- });
- }
- }
- else if (node.type === 'object') {
- collector.add({
- kind: CompletionItemKind.Property,
- label: '$schema',
- insertText: this.getInsertTextForProperty('$schema', undefined, true, ''),
- insertTextFormat: InsertTextFormat.Snippet, documentation: '',
- filterText: this.getFilterTextForValue("$schema")
- });
- }
- };
- JSONCompletion.prototype.getSchemaLessValueCompletions = function (doc, node, offset, document, collector) {
- var _this = this;
- var offsetForSeparator = offset;
- if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) {
- offsetForSeparator = node.offset + node.length;
- node = node.parent;
- }
- if (!node) {
- collector.add({
- kind: this.getSuggestionKind('object'),
- label: 'Empty object',
- insertText: this.getInsertTextForValue({}, ''),
- insertTextFormat: InsertTextFormat.Snippet,
- documentation: ''
- });
- collector.add({
- kind: this.getSuggestionKind('array'),
- label: 'Empty array',
- insertText: this.getInsertTextForValue([], ''),
- insertTextFormat: InsertTextFormat.Snippet,
- documentation: ''
- });
- return;
- }
- var separatorAfter = this.evaluateSeparatorAfter(document, offsetForSeparator);
- var collectSuggestionsForValues = function (value) {
- if (value.parent && !Parser.contains(value.parent, offset, true)) {
- collector.add({
- kind: _this.getSuggestionKind(value.type),
- label: _this.getLabelTextForMatchingNode(value, document),
- insertText: _this.getInsertTextForMatchingNode(value, document, separatorAfter),
- insertTextFormat: InsertTextFormat.Snippet, documentation: ''
- });
- }
- if (value.type === 'boolean') {
- _this.addBooleanValueCompletion(!value.value, separatorAfter, collector);
- }
- };
- if (node.type === 'property') {
- if (offset > (node.colonOffset || 0)) {
- var valueNode = node.valueNode;
- if (valueNode && (offset > (valueNode.offset + valueNode.length) || valueNode.type === 'object' || valueNode.type === 'array')) {
- return;
- }
- // suggest values at the same key
- var parentKey_2 = node.keyNode.value;
- doc.visit(function (n) {
- if (n.type === 'property' && n.keyNode.value === parentKey_2 && n.valueNode) {
- collectSuggestionsForValues(n.valueNode);
- }
- return true;
- });
- if (parentKey_2 === '$schema' && node.parent && !node.parent.parent) {
- this.addDollarSchemaCompletions(separatorAfter, collector);
- }
- }
- }
- if (node.type === 'array') {
- if (node.parent && node.parent.type === 'property') {
- // suggest items of an array at the same key
- var parentKey_3 = node.parent.keyNode.value;
- doc.visit(function (n) {
- if (n.type === 'property' && n.keyNode.value === parentKey_3 && n.valueNode && n.valueNode.type === 'array') {
- n.valueNode.items.forEach(collectSuggestionsForValues);
- }
- return true;
- });
- }
- else {
- // suggest items in the same array
- node.items.forEach(collectSuggestionsForValues);
- }
- }
- };
- JSONCompletion.prototype.getValueCompletions = function (schema, doc, node, offset, document, collector, types) {
- var offsetForSeparator = offset;
- var parentKey = undefined;
- var valueNode = undefined;
- if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) {
- offsetForSeparator = node.offset + node.length;
- valueNode = node;
- node = node.parent;
- }
- if (!node) {
- this.addSchemaValueCompletions(schema.schema, '', collector, types);
- return;
- }
- if ((node.type === 'property') && offset > (node.colonOffset || 0)) {
- var valueNode_1 = node.valueNode;
- if (valueNode_1 && offset > (valueNode_1.offset + valueNode_1.length)) {
- return; // we are past the value node
- }
- parentKey = node.keyNode.value;
- node = node.parent;
- }
- if (node && (parentKey !== undefined || node.type === 'array')) {
- var separatorAfter = this.evaluateSeparatorAfter(document, offsetForSeparator);
- var matchingSchemas = doc.getMatchingSchemas(schema.schema, node.offset, valueNode);
- for (var _i = 0, matchingSchemas_1 = matchingSchemas; _i < matchingSchemas_1.length; _i++) {
- var s = matchingSchemas_1[_i];
- if (s.node === node && !s.inverted && s.schema) {
- if (node.type === 'array' && s.schema.items) {
- if (Array.isArray(s.schema.items)) {
- var index = this.findItemAtOffset(node, document, offset);
- if (index < s.schema.items.length) {
- this.addSchemaValueCompletions(s.schema.items[index], separatorAfter, collector, types);
- }
- }
- else {
- this.addSchemaValueCompletions(s.schema.items, separatorAfter, collector, types);
- }
- }
- if (parentKey !== undefined) {
- var propertyMatched = false;
- if (s.schema.properties) {
- var propertySchema = s.schema.properties[parentKey];
- if (propertySchema) {
- propertyMatched = true;
- this.addSchemaValueCompletions(propertySchema, separatorAfter, collector, types);
- }
- }
- if (s.schema.patternProperties && !propertyMatched) {
- for (var _a = 0, _b = Object.keys(s.schema.patternProperties); _a < _b.length; _a++) {
- var pattern = _b[_a];
- var regex = extendedRegExp(pattern);
- if (regex.test(parentKey)) {
- propertyMatched = true;
- var propertySchema = s.schema.patternProperties[pattern];
- this.addSchemaValueCompletions(propertySchema, separatorAfter, collector, types);
- }
- }
- }
- if (s.schema.additionalProperties && !propertyMatched) {
- var propertySchema = s.schema.additionalProperties;
- this.addSchemaValueCompletions(propertySchema, separatorAfter, collector, types);
- }
- }
- }
- }
- if (parentKey === '$schema' && !node.parent) {
- this.addDollarSchemaCompletions(separatorAfter, collector);
- }
- if (types['boolean']) {
- this.addBooleanValueCompletion(true, separatorAfter, collector);
- this.addBooleanValueCompletion(false, separatorAfter, collector);
- }
- if (types['null']) {
- this.addNullValueCompletion(separatorAfter, collector);
- }
- }
- };
- JSONCompletion.prototype.getContributedValueCompletions = function (doc, node, offset, document, collector, collectionPromises) {
- if (!node) {
- this.contributions.forEach(function (contribution) {
- var collectPromise = contribution.collectDefaultCompletions(document.uri, collector);
- if (collectPromise) {
- collectionPromises.push(collectPromise);
- }
- });
- }
- else {
- if (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null') {
- node = node.parent;
- }
- if (node && (node.type === 'property') && offset > (node.colonOffset || 0)) {
- var parentKey_4 = node.keyNode.value;
- var valueNode = node.valueNode;
- if ((!valueNode || offset <= (valueNode.offset + valueNode.length)) && node.parent) {
- var location_2 = Parser.getNodePath(node.parent);
- this.contributions.forEach(function (contribution) {
- var collectPromise = contribution.collectValueCompletions(document.uri, location_2, parentKey_4, collector);
- if (collectPromise) {
- collectionPromises.push(collectPromise);
- }
- });
- }
- }
- }
- };
- JSONCompletion.prototype.addSchemaValueCompletions = function (schema, separatorAfter, collector, types) {
- var _this = this;
- if (typeof schema === 'object') {
- this.addEnumValueCompletions(schema, separatorAfter, collector);
- this.addDefaultValueCompletions(schema, separatorAfter, collector);
- this.collectTypes(schema, types);
- if (Array.isArray(schema.allOf)) {
- schema.allOf.forEach(function (s) { return _this.addSchemaValueCompletions(s, separatorAfter, collector, types); });
- }
- if (Array.isArray(schema.anyOf)) {
- schema.anyOf.forEach(function (s) { return _this.addSchemaValueCompletions(s, separatorAfter, collector, types); });
- }
- if (Array.isArray(schema.oneOf)) {
- schema.oneOf.forEach(function (s) { return _this.addSchemaValueCompletions(s, separatorAfter, collector, types); });
- }
- }
- };
- JSONCompletion.prototype.addDefaultValueCompletions = function (schema, separatorAfter, collector, arrayDepth) {
- var _this = this;
- if (arrayDepth === void 0) { arrayDepth = 0; }
- var hasProposals = false;
- if (isDefined(schema.default)) {
- var type = schema.type;
- var value = schema.default;
- for (var i = arrayDepth; i > 0; i--) {
- value = [value];
- type = 'array';
- }
- collector.add({
- kind: this.getSuggestionKind(type),
- label: this.getLabelForValue(value),
- insertText: this.getInsertTextForValue(value, separatorAfter),
- insertTextFormat: InsertTextFormat.Snippet,
- detail: localize('json.suggest.default', 'Default value')
- });
- hasProposals = true;
- }
- if (Array.isArray(schema.examples)) {
- schema.examples.forEach(function (example) {
- var type = schema.type;
- var value = example;
- for (var i = arrayDepth; i > 0; i--) {
- value = [value];
- type = 'array';
- }
- collector.add({
- kind: _this.getSuggestionKind(type),
- label: _this.getLabelForValue(value),
- insertText: _this.getInsertTextForValue(value, separatorAfter),
- insertTextFormat: InsertTextFormat.Snippet
- });
- hasProposals = true;
- });
- }
- if (Array.isArray(schema.defaultSnippets)) {
- schema.defaultSnippets.forEach(function (s) {
- var type = schema.type;
- var value = s.body;
- var label = s.label;
- var insertText;
- var filterText;
- if (isDefined(value)) {
- var type_1 = schema.type;
- for (var i = arrayDepth; i > 0; i--) {
- value = [value];
- type_1 = 'array';
- }
- insertText = _this.getInsertTextForSnippetValue(value, separatorAfter);
- filterText = _this.getFilterTextForSnippetValue(value);
- label = label || _this.getLabelForSnippetValue(value);
- }
- else if (typeof s.bodyText === 'string') {
- var prefix = '', suffix = '', indent = '';
- for (var i = arrayDepth; i > 0; i--) {
- prefix = prefix + indent + '[\n';
- suffix = suffix + '\n' + indent + ']';
- indent += '\t';
- type = 'array';
- }
- insertText = prefix + indent + s.bodyText.split('\n').join('\n' + indent) + suffix + separatorAfter;
- label = label || insertText,
- filterText = insertText.replace(/[\n]/g, ''); // remove new lines
- }
- else {
- return;
- }
- collector.add({
- kind: _this.getSuggestionKind(type),
- label: label,
- documentation: _this.fromMarkup(s.markdownDescription) || s.description,
- insertText: insertText,
- insertTextFormat: InsertTextFormat.Snippet,
- filterText: filterText
- });
- hasProposals = true;
- });
- }
- if (!hasProposals && typeof schema.items === 'object' && !Array.isArray(schema.items) && arrayDepth < 5 /* beware of recursion */) {
- this.addDefaultValueCompletions(schema.items, separatorAfter, collector, arrayDepth + 1);
- }
- };
- JSONCompletion.prototype.addEnumValueCompletions = function (schema, separatorAfter, collector) {
- if (isDefined(schema.const)) {
- collector.add({
- kind: this.getSuggestionKind(schema.type),
- label: this.getLabelForValue(schema.const),
- insertText: this.getInsertTextForValue(schema.const, separatorAfter),
- insertTextFormat: InsertTextFormat.Snippet,
- documentation: this.fromMarkup(schema.markdownDescription) || schema.description
- });
- }
- if (Array.isArray(schema.enum)) {
- for (var i = 0, length = schema.enum.length; i < length; i++) {
- var enm = schema.enum[i];
- var documentation = this.fromMarkup(schema.markdownDescription) || schema.description;
- if (schema.markdownEnumDescriptions && i < schema.markdownEnumDescriptions.length && this.doesSupportMarkdown()) {
- documentation = this.fromMarkup(schema.markdownEnumDescriptions[i]);
- }
- else if (schema.enumDescriptions && i < schema.enumDescriptions.length) {
- documentation = schema.enumDescriptions[i];
- }
- collector.add({
- kind: this.getSuggestionKind(schema.type),
- label: this.getLabelForValue(enm),
- insertText: this.getInsertTextForValue(enm, separatorAfter),
- insertTextFormat: InsertTextFormat.Snippet,
- documentation: documentation
- });
- }
- }
- };
- JSONCompletion.prototype.collectTypes = function (schema, types) {
- if (Array.isArray(schema.enum) || isDefined(schema.const)) {
- return;
- }
- var type = schema.type;
- if (Array.isArray(type)) {
- type.forEach(function (t) { return types[t] = true; });
- }
- else if (type) {
- types[type] = true;
- }
- };
- JSONCompletion.prototype.addFillerValueCompletions = function (types, separatorAfter, collector) {
- if (types['object']) {
- collector.add({
- kind: this.getSuggestionKind('object'),
- label: '{}',
- insertText: this.getInsertTextForGuessedValue({}, separatorAfter),
- insertTextFormat: InsertTextFormat.Snippet,
- detail: localize('defaults.object', 'New object'),
- documentation: ''
- });
- }
- if (types['array']) {
- collector.add({
- kind: this.getSuggestionKind('array'),
- label: '[]',
- insertText: this.getInsertTextForGuessedValue([], separatorAfter),
- insertTextFormat: InsertTextFormat.Snippet,
- detail: localize('defaults.array', 'New array'),
- documentation: ''
- });
- }
- };
- JSONCompletion.prototype.addBooleanValueCompletion = function (value, separatorAfter, collector) {
- collector.add({
- kind: this.getSuggestionKind('boolean'),
- label: value ? 'true' : 'false',
- insertText: this.getInsertTextForValue(value, separatorAfter),
- insertTextFormat: InsertTextFormat.Snippet,
- documentation: ''
- });
- };
- JSONCompletion.prototype.addNullValueCompletion = function (separatorAfter, collector) {
- collector.add({
- kind: this.getSuggestionKind('null'),
- label: 'null',
- insertText: 'null' + separatorAfter,
- insertTextFormat: InsertTextFormat.Snippet,
- documentation: ''
- });
- };
- JSONCompletion.prototype.addDollarSchemaCompletions = function (separatorAfter, collector) {
- var _this = this;
- var schemaIds = this.schemaService.getRegisteredSchemaIds(function (schema) { return schema === 'http' || schema === 'https'; });
- schemaIds.forEach(function (schemaId) { return collector.add({
- kind: CompletionItemKind.Module,
- label: _this.getLabelForValue(schemaId),
- filterText: _this.getFilterTextForValue(schemaId),
- insertText: _this.getInsertTextForValue(schemaId, separatorAfter),
- insertTextFormat: InsertTextFormat.Snippet, documentation: ''
- }); });
- };
- JSONCompletion.prototype.getLabelForValue = function (value) {
- return JSON.stringify(value);
- };
- JSONCompletion.prototype.getFilterTextForValue = function (value) {
- return JSON.stringify(value);
- };
- JSONCompletion.prototype.getFilterTextForSnippetValue = function (value) {
- return JSON.stringify(value).replace(/\$\{\d+:([^}]+)\}|\$\d+/g, '$1');
- };
- JSONCompletion.prototype.getLabelForSnippetValue = function (value) {
- var label = JSON.stringify(value);
- return label.replace(/\$\{\d+:([^}]+)\}|\$\d+/g, '$1');
- };
- JSONCompletion.prototype.getInsertTextForPlainText = function (text) {
- return text.replace(/[\\\$\}]/g, '\\$&'); // escape $, \ and }
- };
- JSONCompletion.prototype.getInsertTextForValue = function (value, separatorAfter) {
- var text = JSON.stringify(value, null, '\t');
- if (text === '{}') {
- return '{$1}' + separatorAfter;
- }
- else if (text === '[]') {
- return '[$1]' + separatorAfter;
- }
- return this.getInsertTextForPlainText(text + separatorAfter);
- };
- JSONCompletion.prototype.getInsertTextForSnippetValue = function (value, separatorAfter) {
- var replacer = function (value) {
- if (typeof value === 'string') {
- if (value[0] === '^') {
- return value.substr(1);
- }
- }
- return JSON.stringify(value);
- };
- return stringifyObject(value, '', replacer) + separatorAfter;
- };
- JSONCompletion.prototype.getInsertTextForGuessedValue = function (value, separatorAfter) {
- switch (typeof value) {
- case 'object':
- if (value === null) {
- return '${1:null}' + separatorAfter;
- }
- return this.getInsertTextForValue(value, separatorAfter);
- case 'string':
- var snippetValue = JSON.stringify(value);
- snippetValue = snippetValue.substr(1, snippetValue.length - 2); // remove quotes
- snippetValue = this.getInsertTextForPlainText(snippetValue); // escape \ and }
- return '"${1:' + snippetValue + '}"' + separatorAfter;
- case 'number':
- case 'boolean':
- return '${1:' + JSON.stringify(value) + '}' + separatorAfter;
- }
- return this.getInsertTextForValue(value, separatorAfter);
- };
- JSONCompletion.prototype.getSuggestionKind = function (type) {
- if (Array.isArray(type)) {
- var array = type;
- type = array.length > 0 ? array[0] : undefined;
- }
- if (!type) {
- return CompletionItemKind.Value;
- }
- switch (type) {
- case 'string': return CompletionItemKind.Value;
- case 'object': return CompletionItemKind.Module;
- case 'property': return CompletionItemKind.Property;
- default: return CompletionItemKind.Value;
- }
- };
- JSONCompletion.prototype.getLabelTextForMatchingNode = function (node, document) {
- switch (node.type) {
- case 'array':
- return '[]';
- case 'object':
- return '{}';
- default:
- var content = document.getText().substr(node.offset, node.length);
- return content;
- }
- };
- JSONCompletion.prototype.getInsertTextForMatchingNode = function (node, document, separatorAfter) {
- switch (node.type) {
- case 'array':
- return this.getInsertTextForValue([], separatorAfter);
- case 'object':
- return this.getInsertTextForValue({}, separatorAfter);
- default:
- var content = document.getText().substr(node.offset, node.length) + separatorAfter;
- return this.getInsertTextForPlainText(content);
- }
- };
- JSONCompletion.prototype.getInsertTextForProperty = function (key, propertySchema, addValue, separatorAfter) {
- var propertyText = this.getInsertTextForValue(key, '');
- if (!addValue) {
- return propertyText;
- }
- var resultText = propertyText + ': ';
- var value;
- var nValueProposals = 0;
- if (propertySchema) {
- if (Array.isArray(propertySchema.defaultSnippets)) {
- if (propertySchema.defaultSnippets.length === 1) {
- var body = propertySchema.defaultSnippets[0].body;
- if (isDefined(body)) {
- value = this.getInsertTextForSnippetValue(body, '');
- }
- }
- nValueProposals += propertySchema.defaultSnippets.length;
- }
- if (propertySchema.enum) {
- if (!value && propertySchema.enum.length === 1) {
- value = this.getInsertTextForGuessedValue(propertySchema.enum[0], '');
- }
- nValueProposals += propertySchema.enum.length;
- }
- if (isDefined(propertySchema.default)) {
- if (!value) {
- value = this.getInsertTextForGuessedValue(propertySchema.default, '');
- }
- nValueProposals++;
- }
- if (Array.isArray(propertySchema.examples) && propertySchema.examples.length) {
- if (!value) {
- value = this.getInsertTextForGuessedValue(propertySchema.examples[0], '');
- }
- nValueProposals += propertySchema.examples.length;
- }
- if (nValueProposals === 0) {
- var type = Array.isArray(propertySchema.type) ? propertySchema.type[0] : propertySchema.type;
- if (!type) {
- if (propertySchema.properties) {
- type = 'object';
- }
- else if (propertySchema.items) {
- type = 'array';
- }
- }
- switch (type) {
- case 'boolean':
- value = '$1';
- break;
- case 'string':
- value = '"$1"';
- break;
- case 'object':
- value = '{$1}';
- break;
- case 'array':
- value = '[$1]';
- break;
- case 'number':
- case 'integer':
- value = '${1:0}';
- break;
- case 'null':
- value = '${1:null}';
- break;
- default:
- return propertyText;
- }
- }
- }
- if (!value || nValueProposals > 1) {
- value = '$1';
- }
- return resultText + value + separatorAfter;
- };
- JSONCompletion.prototype.getCurrentWord = function (document, offset) {
- var i = offset - 1;
- var text = document.getText();
- while (i >= 0 && ' \t\n\r\v":{[,]}'.indexOf(text.charAt(i)) === -1) {
- i--;
- }
- return text.substring(i + 1, offset);
- };
- JSONCompletion.prototype.evaluateSeparatorAfter = function (document, offset) {
- var scanner = Json.createScanner(document.getText(), true);
- scanner.setPosition(offset);
- var token = scanner.scan();
- switch (token) {
- case 5 /* CommaToken */:
- case 2 /* CloseBraceToken */:
- case 4 /* CloseBracketToken */:
- case 17 /* EOF */:
- return '';
- default:
- return ',';
- }
- };
- JSONCompletion.prototype.findItemAtOffset = function (node, document, offset) {
- var scanner = Json.createScanner(document.getText(), true);
- var children = node.items;
- for (var i = children.length - 1; i >= 0; i--) {
- var child = children[i];
- if (offset > child.offset + child.length) {
- scanner.setPosition(child.offset + child.length);
- var token = scanner.scan();
- if (token === 5 /* CommaToken */ && offset >= scanner.getTokenOffset() + scanner.getTokenLength()) {
- return i + 1;
- }
- return i;
- }
- else if (offset >= child.offset) {
- return i;
- }
- }
- return 0;
- };
- JSONCompletion.prototype.isInComment = function (document, start, offset) {
- var scanner = Json.createScanner(document.getText(), false);
- scanner.setPosition(start);
- var token = scanner.scan();
- while (token !== 17 /* EOF */ && (scanner.getTokenOffset() + scanner.getTokenLength() < offset)) {
- token = scanner.scan();
- }
- return (token === 12 /* LineCommentTrivia */ || token === 13 /* BlockCommentTrivia */) && scanner.getTokenOffset() <= offset;
- };
- JSONCompletion.prototype.fromMarkup = function (markupString) {
- if (markupString && this.doesSupportMarkdown()) {
- return {
- kind: MarkupKind.Markdown,
- value: markupString
- };
- }
- return undefined;
- };
- JSONCompletion.prototype.doesSupportMarkdown = function () {
- if (!isDefined(this.supportsMarkdown)) {
- var completion = this.clientCapabilities.textDocument && this.clientCapabilities.textDocument.completion;
- this.supportsMarkdown = completion && completion.completionItem && Array.isArray(completion.completionItem.documentationFormat) && completion.completionItem.documentationFormat.indexOf(MarkupKind.Markdown) !== -1;
- }
- return this.supportsMarkdown;
- };
- JSONCompletion.prototype.doesSupportsCommitCharacters = function () {
- if (!isDefined(this.supportsCommitCharacters)) {
- var completion = this.clientCapabilities.textDocument && this.clientCapabilities.textDocument.completion;
- this.supportsCommitCharacters = completion && completion.completionItem && !!completion.completionItem.commitCharactersSupport;
- }
- return this.supportsCommitCharacters;
- };
- return JSONCompletion;
- }());
- export { JSONCompletion };
|