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