protected async completion()

in Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts [478:714]


  protected async completion(params: TextDocumentPositionParams): Promise<CompletionList | null> {
    const document = this.documents.get(params.textDocument.uri);
    if (!document) {
      return Promise.resolve(null);
    }

    const position = params.position;
    const range = Range.create(position.line, 0, position.line, position.character);
    const curLineContent = document.getText(range);
    const luDoc = this.getLUDocument(document);
    const text = (await luDoc?.index()).content || document.getText();
    const lines = text.split('\n');
    const curLineNumber = params.position.line;
    //const textBeforeCurLine = lines.slice(0, curLineNumber).join('\n');
    const textExceptCurLine = lines
      .slice(0, curLineNumber)
      .concat(lines.slice(curLineNumber + 1))
      .join('\n');
    const completionList: CompletionItem[] = [];
    if (util.isEntityType(curLineContent)) {
      const triggerChar = curLineContent[position.character - 1];
      const extraWhiteSpace = triggerChar === '@' ? ' ' : '';
      const entityTypes: string[] = EntityTypesObj.EntityType;
      entityTypes.forEach((entity) => {
        const item = {
          label: entity,
          kind: CompletionItemKind.Keyword,
          insertText: `${extraWhiteSpace}${entity}`,
          documentation: `Enitity type: ${entity}`,
        };

        completionList.push(item);
      });
    }

    if (util.isPrebuiltEntity(curLineContent)) {
      const prebuiltTypes: string[] = EntityTypesObj.Prebuilt;
      const triggerChar = curLineContent[position.character - 1];
      const extraWhiteSpace = triggerChar !== ' ' ? ' ' : '';
      prebuiltTypes.forEach((entity) => {
        const item = {
          label: entity,
          kind: CompletionItemKind.Keyword,
          insertText: `${extraWhiteSpace}${entity}`,
          documentation: `Prebuilt enitity: ${entity}`,
        };

        completionList.push(item);
      });
    }

    if (util.isRegexEntity(curLineContent)) {
      const item = {
        label: 'RegExp Entity',
        kind: CompletionItemKind.Keyword,
        insertText: `//`,
        documentation: `regex enitity`,
      };

      completionList.push(item);
    }

    if (util.isEntityName(curLineContent)) {
      const item = {
        label: 'hasRoles?',
        kind: CompletionItemKind.Keyword,
        insertText: `hasRoles`,
        documentation: `Entity name hasRole?`,
      };

      completionList.push(item);
      const item2 = {
        label: 'usesFeature?',
        kind: CompletionItemKind.Keyword,
        insertText: `usesFeature`,
        documentation: `Entity name usesFeature?`,
      };

      completionList.push(item2);
    }

    if (util.isPhraseListEntity(curLineContent)) {
      const item = {
        label: 'interchangeable synonyms?',
        kind: CompletionItemKind.Keyword,
        insertText: `interchangeable`,
        documentation: `interchangeable synonyms as part of the entity definition`,
      };

      completionList.push(item);
    }

    // completion for entities and patterns, use the text without current line due to usually it will cause parser errors, the luisjson will be undefined

    let luisJson = await this.extractLUISContent(text);
    if (!luisJson) {
      luisJson = await this.extractLUISContent(textExceptCurLine);
    }

    const suggestionEntityList = uniq(
      util.getSuggestionEntities(luisJson, util.suggestionAllEntityTypes).concat(this._importedEntities)
    );
    const regexEntityList = util.getRegexEntities(luisJson);

    //suggest a regex pattern for seperated line definition
    if (util.isSeperatedEntityDef(curLineContent)) {
      const seperatedEntityDef = /^\s*@\s*([\w._]+|"[\w._\s]+")+\s*=\s*$/; //lgtm [js/redos]
      let entityName = '';
      const matchGroup = curLineContent.match(seperatedEntityDef);
      if (matchGroup && matchGroup.length >= 2) {
        entityName = matchGroup[1].trim();
      }

      if (regexEntityList.includes(entityName)) {
        const item = {
          label: 'RegExp Entity',
          kind: CompletionItemKind.Keyword,
          insertText: `//`,
          documentation: `regex enitity`,
        };

        completionList.push(item);
      }
    }

    // auto suggest pattern
    if (util.matchedEnterPattern(curLineContent)) {
      suggestionEntityList.forEach((name) => {
        const item = {
          label: `Entity: ${name}`,
          kind: CompletionItemKind.Property,
          insertText: `${name}`,
          documentation: `pattern suggestion for entity: ${name}`,
        };

        completionList.push(item);
      });
    }

    // suggestions for entities in a seperated line
    if (util.isEntityType(curLineContent)) {
      suggestionEntityList.forEach((entity) => {
        const item = {
          label: entity,
          kind: CompletionItemKind.Property,
          insertText: `${entity}`,
          documentation: `Enitity type: ${entity}`,
        };

        completionList.push(item);
      });
    }

    if (util.isCompositeEntity(curLineContent)) {
      util.getSuggestionEntities(luisJson, util.suggestionNoCompositeEntityTypes).forEach((entity) => {
        const item = {
          label: entity,
          kind: CompletionItemKind.Property,
          insertText: `${entity}`,
          documentation: `Enitity type: ${entity}`,
        };

        completionList.push(item);
      });
    }

    const suggestionRolesList = util.getSuggestionRoles(luisJson, util.suggestionAllEntityTypes);
    // auto suggest roles
    if (util.matchedRolesPattern(curLineContent)) {
      suggestionRolesList.forEach((name) => {
        const item = {
          label: `Role: ${name}`,
          kind: CompletionItemKind.Property,
          insertText: `${name}`,
          documentation: `roles suggestion for entity name: ${name}`,
        };

        completionList.push(item);
      });
    }

    if (util.matchedEntityPattern(curLineContent)) {
      suggestionEntityList.forEach((name) => {
        const item = {
          label: `Entity: ${name}`,
          kind: CompletionItemKind.Property,
          insertText: ` ${name}`,
          documentation: `pattern suggestion for entity: ${name}`,
        };
        completionList.push(item);
      });
    }

    if (util.matchedEntityCanUsesFeature(curLineContent, text, luisJson)) {
      const enitityName = util.extractEntityNameInUseFeature(curLineContent);
      const suggestionFeatureList = util.getSuggestionEntities(luisJson, util.suggestionNoPatternAnyEntityTypes);
      suggestionFeatureList.forEach((name) => {
        if (name !== enitityName) {
          const item = {
            label: `Entity: ${name}`,
            kind: CompletionItemKind.Method,
            insertText: `${name}`,
            documentation: `Feature suggestion for current entity: ${name}`,
          };

          completionList.push(item);
        }
      });
    }

    if (util.matchIntentInEntityDef(curLineContent)) {
      const item = {
        label: 'usesFeature?',
        kind: CompletionItemKind.Keyword,
        insertText: `usesFeature`,
        documentation: `Does this intent usesFeature?`,
      };

      completionList.push(item);
    }

    if (util.matchIntentUsesFeatures(curLineContent)) {
      const suggestionFeatureList = util.getSuggestionEntities(luisJson, util.suggestionNoPatternAnyEntityTypes);
      suggestionFeatureList.forEach((name) => {
        const item = {
          label: `Entity: ${name}`,
          kind: CompletionItemKind.Method,
          insertText: `${name}`,
          documentation: `Feature suggestion for current entity: ${name}`,
        };

        completionList.push(item);
      });
    }

    return Promise.resolve({ isIncomplete: false, items: completionList });
  }