protected async completion()

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);
    }
  }