public doComplete()

in src/services/jsonCompletion.ts [52:225]


	public doComplete(document: TextDocument, position: Position, doc: Parser.JSONDocument): Thenable<CompletionList> {

		const result: CompletionList = {
			items: [],
			isIncomplete: false
		};

		const text = document.getText();

		const offset = document.offsetAt(position);
		let 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) {
			const ch = text[offset - 1];
			if (node.type === 'object' && ch === '}' || node.type === 'array' && ch === ']') {
				// after ] or }
				node = node.parent;
			}
		}

		const currentWord = this.getCurrentWord(document, offset);
		let overwriteRange: Range;

		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 {
			let overwriteStart = offset - currentWord.length;
			if (overwriteStart > 0 && text[overwriteStart - 1] === '"') {
				overwriteStart--;
			}
			overwriteRange = Range.create(document.positionAt(overwriteStart), position);
		}

		const supportsCommitCharacters = false; //this.doesSupportsCommitCharacters(); disabled for now, waiting for new API: https://github.com/microsoft/vscode/issues/42544

		const proposed: { [key: string]: CompletionItem } = {};
		const collector: CompletionsCollector = {
			add: (suggestion: CompletionItem) => {
				let label = suggestion.label;
				const existing = proposed[label];
				if (!existing) {
					label = label.replace(/[\n]/g, '↵');
					if (label.length > 60) {
						const 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: () => {
				result.isIncomplete = true;
			},
			error: (message: string) => {
				console.error(message);
			},
			log: (message: string) => {
				console.log(message);
			},
			getNumberOfProposals: () => {
				return result.items.length;
			}
		};

		return this.schemaService.getSchemaForResource(document.uri, doc).then((schema) => {
			const collectionPromises: Thenable<any>[] = [];

			let addValue = true;
			let currentKey = '';

			let currentProperty: PropertyASTNode | undefined = undefined;
			if (node) {

				if (node.type === 'string') {
					const 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
				const properties = node.properties;
				properties.forEach(p => {
					if (!currentProperty || currentProperty !== p) {
						proposed[p.keyNode.value] = CompletionItem.create('__');
					}
				});
				let separatorAfter = '';
				if (addValue) {
					separatorAfter = this.evaluateSeparatorAfter(document, document.offsetAt(overwriteRange.end));
				}

				if (schema) {
					// property proposals with schema
					this.getPropertyCompletions(schema, doc, node, addValue, separatorAfter, collector);
				} else {
					// property proposals without schema
					this.getSchemaLessPropertyCompletions(doc, node, currentKey, collector);
				}

				const location = Parser.getNodePath(node);
				this.contributions.forEach((contribution) => {
					const collectPromise = contribution.collectPropertyCompletions(document.uri, location, currentWord, addValue, separatorAfter === '', 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),
						insertTextFormat: InsertTextFormat.Snippet, documentation: '',
					});
					collector.setAsIncomplete();
				}
			}

			// proposals for values
			const types: { [type: string]: boolean } = {};
			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(() => {
				if (collector.getNumberOfProposals() === 0) {
					let offsetForSeparator = offset;
					if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) {
						offsetForSeparator = node.offset + node.length;
					}
					const separatorAfter = this.evaluateSeparatorAfter(document, offsetForSeparator);
					this.addFillerValueCompletions(types, separatorAfter, collector);
				}
				return result;
			});
		});
	}