in Composer/packages/tools/language-servers/language-generation/src/LGServer.ts [667:824]
protected async completion(params: TextDocumentPositionParams): Promise<Thenable<CompletionList | null>> {
const document = this.documents.get(params.textDocument.uri);
if (!document) {
return Promise.resolve(null);
}
const position = params.position;
const range = getRangeAtPosition(document, position, true);
const wordAtCurRange = document.getText(range);
const endWithDot = wordAtCurRange.endsWith('.');
const includesDot = wordAtCurRange.includes('.');
const lgDoc = this.getLGDocument(document);
const lgFile = await lgDoc?.index();
const templateId = lgDoc?.templateId;
const lines = document.getText(range).split('\n');
if (!lgFile) {
return Promise.resolve(null);
}
const wordRange = getEntityRangeAtPosition(document, params.position);
const word = document.getText(wordRange);
const startWithAt = word.startsWith('@');
const completionEntityList = this._luisEntities.map((entity: string) => {
return {
label: entity,
kind: CompletionItemKind.Property,
insertText: entity,
documentation: formatMessage('Entity defined in lu files: { entity }', { entity: entity }),
};
});
const { allTemplates } = lgFile;
const completionTemplateList: CompletionItem[] = allTemplates.map((template) => {
return {
label: template.name,
kind: CompletionItemKind.Reference,
insertText:
template.parameters.length > 0
? template.name + '(' + template.parameters.join(', ') + ')'
: template.name + '()',
documentation: template.body,
};
});
const completionFunctionList: CompletionItem[] = Array.from(buildInFunctionsMap).map((item) => {
const [key, value] = item;
return {
label: key,
kind: CompletionItemKind.Function,
insertText: key + '(' + this.removeParamFormat(value.Params.toString()) + ')',
documentation: value.Introduction,
};
});
const completionPropertyResult = this.findValidMemoryVariables(params);
const isStructuredLG = this.matchStructuredLG(params, templateId);
//sugegst card types if an initial [ line after template line
if (isStructuredLG === STRUCTURELG) {
const cardTypesSuggestions: CompletionItem[] = cardTypes.map((type) => {
return {
label: type,
kind: CompletionItemKind.Keyword,
insertText: type,
documentation: formatMessage('Suggestion for Card or Activity: { type }', { type: type }),
};
});
return Promise.resolve({
isIncomplete: false,
items: cardTypesSuggestions,
});
}
const curLineState = this.matchCurLineState(params, templateId);
const curPosInLineState = this.matchState(params, curLineState);
// if the current editing line is in a structured LG and cur postion is not in an expression, returns the missing property fields
if (curLineState === STRUCTURELG && curPosInLineState !== EXPRESSION) {
const cardType = this.matchCardTypeState(params, templateId);
const propsList = this.findLastStructureLGProps(params, templateId);
const cardNameRegex = /^\s*\[[\w]+/;
const lastLine = lines[lines.length - 2];
const paddingIndent = cardNameRegex.test(lastLine) ? '\t' : '';
const normalCardTypes = ['CardAction', 'Suggestions', 'Attachment', 'Activity'];
if (cardType && cardTypes.includes(cardType)) {
const items: CompletionItem[] = [];
if (normalCardTypes.includes(cardType)) {
cardPropDict[cardType].forEach((u) => {
if (!propsList?.includes(u)) {
const item = {
label: `${u}: ${cardPropPossibleValueType[u]}`,
kind: CompletionItemKind.Snippet,
insertText: `${paddingIndent}${u} = ${cardPropPossibleValueType[u]}`,
documentation: formatMessage('Suggested propertiy { u } in { cardType }', { u: u, cardType: cardType }),
};
items.push(item);
}
});
} else if (cardType.endsWith('Card')) {
cardPropDict.Cards.forEach((u) => {
if (!propsList?.includes(u)) {
const item = {
label: `${u}: ${cardPropPossibleValueType[u]}`,
kind: CompletionItemKind.Snippet,
insertText: `${paddingIndent}${u} = ${cardPropPossibleValueType[u]}`,
documentation: formatMessage('Suggested propertiy { u } in { cardType }', { u: u, cardType: cardType }),
};
items.push(item);
}
});
} else {
cardPropDict.Others.forEach((u) => {
if (!propsList?.includes(u)) {
const item = {
label: `${u}: ${cardPropPossibleValueType[u]}`,
kind: CompletionItemKind.Snippet,
insertText: `${paddingIndent}${u} = ${cardPropPossibleValueType[u]}`,
documentation: formatMessage('Suggested propertiy { u } in { cardType }', { u: u, cardType: cardType }),
};
items.push(item);
}
});
}
if (items.length > 0) {
return Promise.resolve({
isIncomplete: false,
items: items,
});
}
}
}
if (curPosInLineState === EXPRESSION) {
if (endWithDot) {
return Promise.resolve({
isIncomplete: false,
items: completionPropertyResult,
});
} else if (startWithAt) {
return Promise.resolve({
isIncomplete: false,
items: completionEntityList,
});
} else if (!includesDot) {
return Promise.resolve({
isIncomplete: false,
items: [...completionTemplateList, ...completionFunctionList, ...completionPropertyResult],
});
} else {
return Promise.resolve(null);
}
} else {
return Promise.resolve(null);
}
}