Skip to content

Commit

Permalink
Merge pull request #32796 from nadeeshaan/dev-branch
Browse files Browse the repository at this point in the history
Fix completions within method call expr args
  • Loading branch information
nadeeshaan committed Sep 22, 2021
2 parents 1ea697c + ab0b86e commit 704ddcc
Show file tree
Hide file tree
Showing 46 changed files with 6,814 additions and 85 deletions.
Expand Up @@ -36,16 +36,19 @@
import io.ballerina.compiler.api.symbols.TypeSymbol;
import io.ballerina.compiler.api.symbols.UnionTypeSymbol;
import io.ballerina.compiler.api.symbols.VariableSymbol;
import io.ballerina.compiler.syntax.tree.FunctionArgumentNode;
import io.ballerina.compiler.syntax.tree.FunctionCallExpressionNode;
import io.ballerina.compiler.syntax.tree.FunctionTypeDescriptorNode;
import io.ballerina.compiler.syntax.tree.IdentifierToken;
import io.ballerina.compiler.syntax.tree.ImportDeclarationNode;
import io.ballerina.compiler.syntax.tree.ImportPrefixNode;
import io.ballerina.compiler.syntax.tree.MethodCallExpressionNode;
import io.ballerina.compiler.syntax.tree.ModuleMemberDeclarationNode;
import io.ballerina.compiler.syntax.tree.ModulePartNode;
import io.ballerina.compiler.syntax.tree.ModuleVariableDeclarationNode;
import io.ballerina.compiler.syntax.tree.Node;
import io.ballerina.compiler.syntax.tree.NonTerminalNode;
import io.ballerina.compiler.syntax.tree.RemoteMethodCallActionNode;
import io.ballerina.compiler.syntax.tree.SeparatedNodeList;
import io.ballerina.compiler.syntax.tree.SyntaxKind;
import io.ballerina.compiler.syntax.tree.SyntaxTree;
Expand Down Expand Up @@ -156,7 +159,7 @@ public class CommonUtil {
private static final String SELF_KW = "self";

private static final Pattern TYPE_NAME_DECOMPOSE_PATTERN = Pattern.compile("([\\w_.]*)/([\\w._]*):([\\w.-]*)");

private static final int MAX_DEPTH = 1;

static {
Expand Down Expand Up @@ -253,10 +256,10 @@ public static List<TextEdit> getAutoImportTextEdits(@Nonnull String orgName, Str
public static Optional<String> getDefaultValueForType(TypeSymbol bType) {
return getDefaultValueForType(bType, 1);
}

private static Optional<String> getDefaultValueForType(TypeSymbol bType, int depth) {
String typeString;

if (bType == null) {
return Optional.empty();
}
Expand All @@ -281,7 +284,7 @@ private static Optional<String> getDefaultValueForType(TypeSymbol bType, int dep
// Filler value of an array is []
ArrayTypeSymbol arrayType = (ArrayTypeSymbol) rawType;
if (arrayType.memberTypeDescriptor().typeKind() == TypeDescKind.ARRAY) {
typeString = "[" + getDefaultValueForType(arrayType.memberTypeDescriptor(), depth + 1).orElse("")
typeString = "[" + getDefaultValueForType(arrayType.memberTypeDescriptor(), depth + 1).orElse("")
+ "]";
} else {
typeString = "[]";
Expand Down Expand Up @@ -1342,18 +1345,37 @@ private static List<String> getBallerinaKeywords() {
public static Optional<ParameterSymbol> resolveFunctionParameterSymbol(FunctionTypeSymbol functionTypeSymbol,
PositionedOperationContext ctx,
FunctionCallExpressionNode node) {
int cursorPosition = ctx.getCursorPositionInTree();
int argIndex = -1;
for (Node child : node.arguments()) {
if (child.textRange().endOffset() < cursorPosition) {
argIndex += 1;
}
}
Optional<List<ParameterSymbol>> params = functionTypeSymbol.params();
if (params.isEmpty() || params.get().size() < argIndex + 2) {
return Optional.empty();
}
return Optional.of(params.get().get(argIndex + 1));
return resolveParameterSymbol(functionTypeSymbol, ctx, node.arguments());
}

/**
* Given the cursor position information, returns the expected ParameterSymbol
* information corresponding to the FunctionTypeSymbol instance.
*
* @param functionTypeSymbol Referenced FunctionTypeSymbol
* @param ctx Positioned operation context information.
* @param node Remote method call action node.
* @return {@link Optional<ParameterSymbol>} Expected Parameter Symbol.
*/
public static Optional<ParameterSymbol> resolveFunctionParameterSymbol(FunctionTypeSymbol functionTypeSymbol,
PositionedOperationContext ctx,
RemoteMethodCallActionNode node) {
return resolveParameterSymbol(functionTypeSymbol, ctx, node.arguments());
}

/**
* Given the cursor position information, returns the expected ParameterSymbol
* information corresponding to the FunctionTypeSymbol instance.
*
* @param functionTypeSymbol Referenced FunctionTypeSymbol
* @param ctx Positioned operation context information.
* @param node Method call expression node.
* @return {@link Optional<ParameterSymbol>} Expected Parameter Symbol.
*/
public static Optional<ParameterSymbol> resolveFunctionParameterSymbol(FunctionTypeSymbol functionTypeSymbol,
PositionedOperationContext ctx,
MethodCallExpressionNode node) {
return resolveParameterSymbol(functionTypeSymbol, ctx, node.arguments());
}

/**
Expand All @@ -1365,11 +1387,31 @@ public static Optional<ParameterSymbol> resolveFunctionParameterSymbol(FunctionT
*/
public static Boolean isInFunctionCallParameterContext(PositionedOperationContext ctx,
FunctionCallExpressionNode node) {
int cursorPosition = ctx.getCursorPositionInTree();
return (!node.openParenToken().isMissing())
&& (node.openParenToken().textRange().endOffset() <= cursorPosition)
&& (!node.closeParenToken().isMissing())
&& (cursorPosition <= node.closeParenToken().textRange().startOffset());
return isWithinParenthesis(ctx, node.openParenToken(), node.closeParenToken());
}

/**
* Check if the cursor is positioned in a method call expression parameter context.
*
* @param ctx PositionedOperationContext
* @param node MethodCallExpressionNode
* @return {@link Boolean} whether the cursor is in parameter context.
*/
public static Boolean isInMethodCallParameterContext(PositionedOperationContext ctx,
MethodCallExpressionNode node) {
return isWithinParenthesis(ctx, node.openParenToken(), node.closeParenToken());
}

/**
* Check if the cursor is positioned in a method call expression parameter context.
*
* @param ctx PositionedOperationContext
* @param node RemoteMethodCallActionNode
* @return {@link Boolean} whether the cursor is in parameter context.
*/
public static Boolean isInMethodCallParameterContext(PositionedOperationContext ctx,
RemoteMethodCallActionNode node) {
return isWithinParenthesis(ctx, node.openParenToken(), node.closeParenToken());
}

/**
Expand Down Expand Up @@ -1480,4 +1522,29 @@ public static List<Token> getQualifiersOfNode(Node node) {
}
return qualifiers;
}

private static boolean isWithinParenthesis(PositionedOperationContext ctx, Token openParen, Token closedParen) {
int cursorPosition = ctx.getCursorPositionInTree();
return (!openParen.isMissing())
&& (openParen.textRange().endOffset() <= cursorPosition)
&& (!closedParen.isMissing())
&& (cursorPosition <= closedParen.textRange().startOffset());
}

private static Optional<ParameterSymbol> resolveParameterSymbol(FunctionTypeSymbol functionTypeSymbol,
PositionedOperationContext ctx,
SeparatedNodeList<FunctionArgumentNode> arguments) {
int cursorPosition = ctx.getCursorPositionInTree();
int argIndex = -1;
for (Node child : arguments) {
if (child.textRange().endOffset() < cursorPosition) {
argIndex += 1;
}
}
Optional<List<ParameterSymbol>> params = functionTypeSymbol.params();
if (params.isEmpty() || params.get().size() < argIndex + 2) {
return Optional.empty();
}
return Optional.of(params.get().get(argIndex + 1));
}
}
Expand Up @@ -25,6 +25,7 @@
import org.ballerinalang.langserver.commons.BallerinaCompletionContext;
import org.ballerinalang.langserver.commons.completion.LSCompletionException;
import org.ballerinalang.langserver.commons.completion.LSCompletionItem;
import org.ballerinalang.langserver.completions.providers.AbstractCompletionProvider;
import org.ballerinalang.langserver.completions.util.SortingUtil;

import java.util.ArrayList;
Expand All @@ -37,7 +38,7 @@
* @since 2.0.0
*/
@JavaSPIService("org.ballerinalang.langserver.commons.completion.spi.BallerinaCompletionProvider")
public class FunctionCallExpressionNodeContext extends BlockNodeContextProvider<FunctionCallExpressionNode> {
public class FunctionCallExpressionNodeContext extends AbstractCompletionProvider<FunctionCallExpressionNode> {

public FunctionCallExpressionNodeContext() {
super(FunctionCallExpressionNode.class);
Expand All @@ -53,8 +54,6 @@ public List<LSCompletionItem> getCompletions(BallerinaCompletionContext ctx, Fun
List<LSCompletionItem> completionItems = new ArrayList<>();
completionItems.addAll(this.actionKWCompletions(ctx));
completionItems.addAll(this.expressionCompletions(ctx));
// TODO: implement the following
// completionItems.addAll(this.getNewExprCompletionItems(ctx, node));
this.sort(ctx, node, completionItems);
return completionItems;
}
Expand All @@ -72,7 +71,6 @@ public boolean onPreValidation(BallerinaCompletionContext context, FunctionCallE
public void sort(BallerinaCompletionContext context,
FunctionCallExpressionNode node,
List<LSCompletionItem> completionItems) {

Optional<TypeSymbol> parameterSymbol = context.getContextType();
if (parameterSymbol.isEmpty() || !CommonUtil.isInFunctionCallParameterContext(context, node)) {
super.sort(context, node, completionItems);
Expand Down
Expand Up @@ -15,16 +15,23 @@
*/
package org.ballerinalang.langserver.completions.providers.context;

import io.ballerina.compiler.api.symbols.Symbol;
import io.ballerina.compiler.api.symbols.TypeSymbol;
import io.ballerina.compiler.syntax.tree.ExpressionNode;
import io.ballerina.compiler.syntax.tree.MethodCallExpressionNode;
import io.ballerina.compiler.syntax.tree.NameReferenceNode;
import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode;
import io.ballerina.compiler.syntax.tree.Token;
import org.ballerinalang.annotation.JavaSPIService;
import org.ballerinalang.langserver.common.utils.completion.QNameReferenceUtil;
import org.ballerinalang.langserver.commons.BallerinaCompletionContext;
import org.ballerinalang.langserver.commons.completion.LSCompletionException;
import org.ballerinalang.langserver.commons.completion.LSCompletionItem;
import org.ballerinalang.langserver.completions.util.SortingUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
* Completion provider for {@link MethodCallExpressionNode} context.
Expand All @@ -41,8 +48,21 @@ public MethodCallExpressionNodeContext() {
@Override
public List<LSCompletionItem> getCompletions(BallerinaCompletionContext context, MethodCallExpressionNode node)
throws LSCompletionException {
ExpressionNode expression = node.expression();
List<LSCompletionItem> completionItems = getEntries(context, expression);
List<LSCompletionItem> completionItems = new ArrayList<>();
if (this.withinParameterContext(context, node)) {
if (QNameReferenceUtil.onQualifiedNameIdentifier(context, context.getNodeAtCursor())) {
QualifiedNameReferenceNode qNameRef = (QualifiedNameReferenceNode) context.getNodeAtCursor();
List<Symbol> exprEntries = QNameReferenceUtil.getExpressionContextEntries(context, qNameRef);
List<LSCompletionItem> items = this.getCompletionItemList(exprEntries, context);
completionItems.addAll(items);
} else {
completionItems.addAll(this.actionKWCompletions(context));
completionItems.addAll(this.expressionCompletions(context));
}
} else {
ExpressionNode expression = node.expression();
completionItems = getEntries(context, expression);
}
this.sort(context, node, completionItems);

return completionItems;
Expand All @@ -54,11 +74,12 @@ public boolean onPreValidation(BallerinaCompletionContext context, MethodCallExp
Supports the following only
eg:
(1) abc.def.test<cursor>Method()
(2) abc.def.testMethod(<cursor>)
With this check, the following example also will come to the methodCall navigating through the parent
hierarchy and skip properly
eg:
(2) s<cursor>abc.def.testMethod()
(3) self.<cursor>Method()
(3) s<cursor>abc.def.testMethod()
(4) self.<cursor>Method()
*/
int cursor = context.getCursorPositionInTree();
NameReferenceNode nameRef = node.methodName();
Expand All @@ -67,4 +88,32 @@ public boolean onPreValidation(BallerinaCompletionContext context, MethodCallExp
return ((cursor >= nameRef.textRange().startOffset() && cursor <= nameRef.textRange().endOffset())
|| (!dotToken.isMissing() && cursor > dotToken.textRange().startOffset()));
}

private boolean withinParameterContext(BallerinaCompletionContext context, MethodCallExpressionNode node) {
int cursor = context.getCursorPositionInTree();
Token openParen = node.openParenToken();
Token closeParen = node.closeParenToken();

return cursor > openParen.textRange().startOffset() && cursor < closeParen.textRange().endOffset();
}

@Override
public void sort(BallerinaCompletionContext context,
MethodCallExpressionNode node,
List<LSCompletionItem> completionItems) {
if (!withinParameterContext(context, node)) {
super.sort(context, node, completionItems);
return;
}
Optional<TypeSymbol> parameterSymbol = context.getContextType();
if (parameterSymbol.isEmpty()) {
SortingUtil.toDefaultSorting(context, completionItems);
return;
}
TypeSymbol symbol = parameterSymbol.get();
for (LSCompletionItem completionItem : completionItems) {
completionItem.getCompletionItem()
.setSortText(SortingUtil.genSortTextByAssignability(context, completionItem, symbol));
}
}
}
Expand Up @@ -26,6 +26,7 @@
import org.ballerinalang.langserver.commons.completion.LSCompletionException;
import org.ballerinalang.langserver.commons.completion.LSCompletionItem;
import org.ballerinalang.langserver.completions.util.ContextTypeResolver;
import org.ballerinalang.langserver.completions.util.SortingUtil;

import java.util.ArrayList;
import java.util.Collections;
Expand Down Expand Up @@ -62,17 +63,19 @@ public List<LSCompletionItem> getCompletions(BallerinaCompletionContext context,
*/
List<Symbol> clientActions = this.getClientActions(expressionType.get());
completionItems.addAll(this.getCompletionItemList(clientActions, context));
} else if (onSuggestFunctionParameters(node, context)) {
} else if (this.withinParameterContext(node, context)) {
/*
* Covers the following cases:
* 1. a->func(<cursor>)
* 2. a->func(mod1:<cursor>)
*/
if (QNameReferenceUtil.onQualifiedNameIdentifier(context, context.getNodeAtCursor())) {
QualifiedNameReferenceNode qualifiedNameRef = (QualifiedNameReferenceNode) context.getNodeAtCursor();
List<Symbol> exprCtxEntries = QNameReferenceUtil.getExpressionContextEntries(context, qualifiedNameRef);
completionItems.addAll(this.getCompletionItemList(exprCtxEntries, context));
QualifiedNameReferenceNode qNameRef = (QualifiedNameReferenceNode) context.getNodeAtCursor();
List<Symbol> exprEntries = QNameReferenceUtil.getExpressionContextEntries(context, qNameRef);
List<LSCompletionItem> items = this.getCompletionItemList(exprEntries, context);
completionItems.addAll(items);
} else {
completionItems.addAll(this.actionKWCompletions(context));
completionItems.addAll(this.expressionCompletions(context));
}
}
Expand All @@ -88,9 +91,29 @@ private boolean onSuggestClientActions(RemoteMethodCallActionNode node, Ballerin
(node.openParenToken().isMissing() || cursor <= node.openParenToken().textRange().startOffset());
}

private boolean onSuggestFunctionParameters(RemoteMethodCallActionNode node, BallerinaCompletionContext context) {
private boolean withinParameterContext(RemoteMethodCallActionNode node, BallerinaCompletionContext context) {
int cursor = context.getCursorPositionInTree();
return !node.openParenToken().isMissing() && node.openParenToken().textRange().endOffset() <= cursor &&
(node.closeParenToken().isMissing() || cursor <= node.closeParenToken().textRange().startOffset());
}

@Override
public void sort(BallerinaCompletionContext context,
RemoteMethodCallActionNode node,
List<LSCompletionItem> completionItems) {
if (!withinParameterContext(node, context)) {
super.sort(context, node, completionItems);
return;
}
Optional<TypeSymbol> parameterSymbol = context.getContextType();
if (parameterSymbol.isEmpty()) {
SortingUtil.toDefaultSorting(context, completionItems);
return;
}
TypeSymbol symbol = parameterSymbol.get();
for (LSCompletionItem completionItem : completionItems) {
completionItem.getCompletionItem()
.setSortText(SortingUtil.genSortTextByAssignability(context, completionItem, symbol));
}
}
}

0 comments on commit 704ddcc

Please sign in to comment.