private getFunctionCallCompletions()

in src/documents/positionContexts/TemplatePositionContext.ts [671:788]


    private getFunctionCallCompletions(tleValue: TLE.FunctionCallValue, parentStringToken: Json.Token, tleCharacterIndex: number, scope: TemplateScope): Completion.Item[] {
        assert(tleValue.getSpan().contains(tleCharacterIndex, ContainsBehavior.extended), "Position should be inside the function call, or right after it");

        const completions: Completion.Item[] = [];

        const namespaceName: string | undefined = tleValue.namespaceToken ? tleValue.namespaceToken.stringValue : undefined;
        // tslint:disable-next-line: strict-boolean-expressions
        const namespace: UserFunctionNamespaceDefinition | undefined = (namespaceName && scope.getFunctionNamespaceDefinition(namespaceName)) || undefined;

        // The token (namespace or name) that the user is completing and will be replaced with the user's selection
        // If undefined, we're just inserting at the current position, not replacing anything
        let tleTokenToComplete: TLE.Token | undefined;

        let completeNamespaces: boolean;
        let completeBuiltinFunctions: boolean;
        let completeUserFunctions: boolean;

        if (tleValue.nameToken && tleValue.nameToken.span.contains(tleCharacterIndex, ContainsBehavior.extended)) {
            // The caret is inside the function's name (or a namespace before the period has been typed), so one of
            // three possibilities.
            tleTokenToComplete = tleValue.nameToken;

            if (namespace) {
                // 1) "namespace.func<CURSOR>tion"
                //   Complete only UDF functions
                completeUserFunctions = true;
                completeNamespaces = false;
                completeBuiltinFunctions = false;
            } else {
                // 2) "name<CURSOR>space"
                // 3) "func<CURSOR>tion"
                //   Complete built-ins and namespaces
                completeNamespaces = true;
                completeBuiltinFunctions = true;
                completeUserFunctions = false;
            }
        } else if (namespaceName && tleValue.periodToken && tleValue.periodToken.span.afterEndIndex === tleCharacterIndex) {
            // "namespace.<CURSOR>function"
            //   The caret is right after the period between a namespace and a function name, so we will be looking for UDF function completions

            if (!namespace) {
                // The given namespace is not defined, so no completions
                return [];
            }

            tleTokenToComplete = tleValue.nameToken;
            completeNamespaces = false;
            completeBuiltinFunctions = false;
            completeUserFunctions = true;
        } else if (tleValue.namespaceToken && tleValue.periodToken && tleValue.namespaceToken.span.contains(tleCharacterIndex, ContainsBehavior.extended)) {
            // "name<CURSOR>space.function"
            //   The caret is inside the UDF's namespace (e.g., the namespace and at least a period already exist in the call).
            //
            // So we want built-in functions or namespaces only

            tleTokenToComplete = tleValue.namespaceToken;
            completeNamespaces = true;
            completeBuiltinFunctions = true;
            completeUserFunctions = false;

        } else if (tleValue.isCallToBuiltinWithName(templateKeys.parameters) && tleValue.argumentExpressions.length === 0) {
            // "parameters<CURSOR>" or "parameters(<CURSOR>)" or similar
            // Don't bother bringing up any other completions
            return this.getParameterCompletions(tleValue, tleCharacterIndex, scope);
        } else if (tleValue.isCallToBuiltinWithName(templateKeys.variables) && tleValue.argumentExpressions.length === 0) {
            // "variables<CURSOR>" or "variables(<CURSOR>)" or similar
            // Don't bother bringing up any other completions
            return this.getVariableCompletions(tleValue, tleCharacterIndex, scope);
        } else {
            // Anywhere else (e.g. whitespace after function name, or inside the arguments list).
            //
            //   "function <CURSOR>()"
            //   "function(<CURSOR>)"
            //   etc.
            //
            // Assume the user is starting a new function call and provide all completions at that location;

            tleTokenToComplete = undefined;
            completeNamespaces = true;
            completeBuiltinFunctions = true;
            completeUserFunctions = false;
        }

        let replaceSpan: Span;

        // Figure out the span which will be replaced by the completion
        if (tleTokenToComplete) {
            const tokenToCompleteStartIndex: number = tleTokenToComplete.span.startIndex;
            const completionLength = tleCharacterIndex - tokenToCompleteStartIndex;
            assert(completionLength >= 0);
            replaceSpan = new Span(this.jsonTokenStartIndex, completionLength).translate(tleTokenToComplete.span.startIndex);
        } else {
            // Nothing getting completed, completion selection will be inserted at current location
            replaceSpan = this.emptySpanAtDocumentCharacterIndex;
        }

        assert(completeBuiltinFunctions || completeUserFunctions || completeNamespaces, "Should be completing something");
        if (completeBuiltinFunctions || completeUserFunctions) {
            if (completeUserFunctions && namespace) {
                const userFunctionCompletions = TemplatePositionContext.getFunctionCompletions(scope, namespace, replaceSpan);
                completions.push(...userFunctionCompletions);
            }
            if (completeBuiltinFunctions) {
                const builtinCompletions = TemplatePositionContext.getFunctionCompletions(scope, undefined, replaceSpan);
                completions.push(...builtinCompletions);
            }
        }
        if (completeNamespaces) {
            const namespaceCompletions = TemplatePositionContext.getNamespaceCompletions(scope, replaceSpan);
            completions.push(...namespaceCompletions);
        }

        // If the completion is for 'resourceId' or related function, then in addition
        // to the regular completions, also add special completions for resourceId
        completions.push(...getResourceIdCompletions(this, tleValue, parentStringToken));

        return completions;
    }