public doComplete()

in language-service/src/services/yamlCompletion.ts [54:213]


    public doComplete(document: TextDocument, position: Position, yamlDocument: YAMLDocument): Thenable<CompletionList> {

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

        const offset = document.offsetAt(position);
        if (document.getText()[offset] === ":") {
            return Promise.resolve(result);
        }

        const jsonDocument: SingleYAMLDocument = yamlDocument.documents.length > 0 ? yamlDocument.documents[0] : null;
        if (jsonDocument === null) {
            return Promise.resolve(result);
        }
        let node = jsonDocument.getNodeFromOffsetEndInclusive(offset);
        if (this.isInComment(document, node ? node.start : 0, offset)) {
            return Promise.resolve(result);
        }

        //console.log(JSON.pruned(node));

        let currentWord = this.getCurrentWord(document, offset);

        let overwriteRange: Range = null;
        if (node && node.type === 'null') {
            //console.log('type = null');
            let nodeStartPos = document.positionAt(node.start);
            nodeStartPos.character += 1;
            let nodeEndPos = document.positionAt(node.end);
            nodeEndPos.character += 1;
            overwriteRange = Range.create(nodeStartPos, nodeEndPos);
        } else if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean')) {
            //console.log('type = string | nuber | boolean');      
            overwriteRange = this.getRangeForLiteralProperties(document, node);
        } else {
            //console.log('else');
            let overwriteStart = offset - currentWord.length;
            if (overwriteStart > 0 && document.getText()[overwriteStart - 1] === '"') {
                overwriteStart--;
            }
            overwriteRange = Range.create(document.positionAt(overwriteStart), position);
        }

        //console.log('overwriteRange: ' + JSON.stringify(overwriteRange));

        let proposed: { [key: string]: CompletionItem } = {};
        let collector: CompletionsCollector = {
            add: (suggestion: CompletionItem) => {
                let existing = proposed[suggestion.label];
                if (!existing) {
                    proposed[suggestion.label] = suggestion;
                    if (overwriteRange) {
                        suggestion.textEdit = TextEdit.replace(overwriteRange, suggestion.insertText);
                    }
                    result.items.push(suggestion);
                } else if (!existing.documentation) {
                    existing.documentation = suggestion.documentation;
                }
            },
            setAsIncomplete: () => {
                result.isIncomplete = true;
            },
            error: (message: string) => {
                console.error(message);
            },
            log: (message: string) => {
                console.log(message);
            },
            getNumberOfProposals: () => {
                return result.items.length;
            }
        };

        //console.log('document.uri: ' + JSON.stringify(document.uri));
        return this.schemaService.getSchemaForResource(document.uri).then((schema) => {
            //console.log('start');
            //console.log('schema: ' + JSON.stringify(schema));
            if (!schema) {
                return Promise.resolve(result);
            }

            let collectionPromises: Thenable<any>[] = [];

            let addValue = true;

            let currentProperty: Parser.PropertyASTNode = null;
            if (node) {

                if (node.type === 'string') {
                    let stringNode = <Parser.StringASTNode>node;
                    if (stringNode.isKey) {
                        addValue = !(node.parent && ((<Parser.PropertyASTNode>node.parent).value));
                        currentProperty = node.parent ? <Parser.PropertyASTNode>node.parent : null;
                        // currentKey = document.getText().substring(node.start + 1, node.end - 1);
                        if (node.parent) {
                            node = node.parent.parent;
                        }
                    }
                }
            }

            // proposals for properties
            //console.log('node and node object');
            if (node && node.type === 'object') {
                // don't suggest properties that are already present
                const properties: Parser.PropertyASTNode[] = (<Parser.ObjectASTNode>node).properties;
                properties.forEach(p => {
                    if (!currentProperty || currentProperty !== p) {
                        proposed[p.key.value] = CompletionItem.create('__');
                    }
                });

                let separatorAfter = '';
                if (addValue) {
                    separatorAfter = this.evaluateSeparatorAfter(document, document.offsetAt(overwriteRange.end));
                }

                if (schema) {
                    // property proposals with schema
                    this.getPropertyCompletions(schema, jsonDocument, node, addValue, collector, separatorAfter);
                }

                let location = node.getPath();
                this.contributions.forEach((contribution) => {
                    let collectPromise = contribution.collectPropertyCompletions(document.uri, location, currentWord, addValue, false, collector);
                    if (collectPromise) {
                        collectionPromises.push(collectPromise);
                    }
                });
                if ((!schema && currentWord.length > 0 && document.getText().charAt(offset - currentWord.length - 1) !== '"')) {
                    collector.add({
                        kind: CompletionItemKind.Property,
                        label: this.getLabelForValue(currentWord),
                        insertText: this.getInsertTextForProperty(currentWord, null, false, separatorAfter),
                        insertTextFormat: InsertTextFormat.Snippet,
                        documentation: ''
                    });
                }
            }

            // proposals for values
            let types: { [type: string]: boolean } = {};
            //console.log('schema: ' + JSON.stringify(schema));
            if (schema) {
                this.getValueCompletions(schema, jsonDocument, node, offset, document, collector, types);
            }
            if (this.contributions.length > 0) {
                this.getContributedValueCompletions(jsonDocument, node, offset, document, collector, collectionPromises);
            }
            if (this.customTags.length > 0) {
                this.getCustomTagValueCompletions(collector);
            }

            return this.promise.all(collectionPromises).then(() => {
                return result;
            });
        });
    }