Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix completion issue for optional field access expression #31215

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,21 @@
import io.ballerina.compiler.api.symbols.ObjectFieldSymbol;
import io.ballerina.compiler.api.symbols.RecordFieldSymbol;
import io.ballerina.compiler.api.symbols.Symbol;
import io.ballerina.compiler.syntax.tree.FieldAccessExpressionNode;
import io.ballerina.compiler.syntax.tree.MethodCallExpressionNode;
import io.ballerina.compiler.syntax.tree.NonTerminalNode;
import io.ballerina.compiler.syntax.tree.SyntaxKind;
import io.ballerina.tools.text.LinePosition;
import io.ballerina.tools.text.LineRange;
import org.ballerinalang.langserver.commons.BallerinaCompletionContext;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionItemKind;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextEdit;

import java.util.Collections;
import java.util.Optional;

/**
* Completion item builder for the object fields and for record fields.
Expand Down Expand Up @@ -52,10 +65,26 @@ private static CompletionItem getCompletionItem(Symbol symbol) {
* Build the constant {@link CompletionItem}.
*
* @param symbol {@link RecordFieldSymbol}
* @param context Completion Context
* @return {@link CompletionItem} generated completion item
*/
public static CompletionItem build(RecordFieldSymbol symbol) {
return getCompletionItem(symbol);
public static CompletionItem build(RecordFieldSymbol symbol, BallerinaCompletionContext context) {
String recordFieldName = symbol.getName().orElseThrow();
String insertText;
CompletionItem completionItem = new CompletionItem();
completionItem.setLabel(recordFieldName);
completionItem.setKind(CompletionItemKind.Field);

Optional<TextEdit> recordFieldAdditionalTextEdit = getRecordFieldAdditionalTextEdit(symbol, context);
if (recordFieldAdditionalTextEdit.isPresent()) {
insertText = "?." + recordFieldName;
completionItem.setAdditionalTextEdits(Collections.singletonList(recordFieldAdditionalTextEdit.get()));
} else {
insertText = recordFieldName;
}
completionItem.setInsertText(insertText);

return completionItem;
}

/**
Expand All @@ -67,4 +96,37 @@ public static CompletionItem build(RecordFieldSymbol symbol) {
public static CompletionItem build(ObjectFieldSymbol symbol) {
return getCompletionItem(symbol);
}

private static Optional<TextEdit> getRecordFieldAdditionalTextEdit(RecordFieldSymbol recordFieldSymbol,
BallerinaCompletionContext context) {
if (!recordFieldSymbol.isOptional()) {
return Optional.empty();
}

NonTerminalNode evalNode = context.getNodeAtCursor();
if (evalNode.kind() == SyntaxKind.SIMPLE_NAME_REFERENCE) {
evalNode = evalNode.parent();
}

LineRange dotTokenLineRange;
if (evalNode.kind() == SyntaxKind.FIELD_ACCESS) {
dotTokenLineRange = ((FieldAccessExpressionNode) evalNode).dotToken().lineRange();
} else if (evalNode.kind() == SyntaxKind.METHOD_CALL) {
// Added for safety
dotTokenLineRange = ((MethodCallExpressionNode) evalNode).dotToken().lineRange();
} else {
return Optional.empty();
}

LinePosition startLine = dotTokenLineRange.startLine();
LinePosition endLine = dotTokenLineRange.endLine();
TextEdit textEdit = new TextEdit();
Range range = new Range();
range.setStart(new Position(startLine.line(), startLine.offset()));
range.setEnd(new Position(endLine.line(), endLine.offset()));
textEdit.setRange(range);
textEdit.setNewText("");

return Optional.of(textEdit);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ protected List<LSCompletionItem> getCompletionItemList(List<? extends Symbol> sc
completionItems.add(new SymbolCompletionItem(ctx, symbol, xmlItem));
} else if (symbol.kind() == RECORD_FIELD) {
RecordFieldSymbol recordFieldSymbol = (RecordFieldSymbol) symbol;
CompletionItem recFieldItem = FieldCompletionItemBuilder.build(recordFieldSymbol);
CompletionItem recFieldItem = FieldCompletionItemBuilder.build(recordFieldSymbol, ctx);
completionItems.add(new RecordFieldCompletionItem(ctx, recordFieldSymbol, recFieldItem));
} else if (symbol.kind() == OBJECT_FIELD || symbol.kind() == CLASS_FIELD) {
ObjectFieldSymbol objectFieldSymbol = (ObjectFieldSymbol) symbol;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,8 @@ public FieldAccessContext(Class<T> attachmentPoint) {
* @return {@link List} of filtered scope entries
*/
protected List<LSCompletionItem> getEntries(BallerinaCompletionContext ctx,
ExpressionNode expr,
boolean optionalFieldAccess) {
FieldAccessCompletionResolver resolver = new FieldAccessCompletionResolver(ctx, optionalFieldAccess);
ExpressionNode expr) {
FieldAccessCompletionResolver resolver = new FieldAccessCompletionResolver(ctx);
List<Symbol> symbolList = resolver.getVisibleEntries(expr);

return this.getCompletionItemList(symbolList, ctx);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public FieldAccessExpressionNodeContext() {
public List<LSCompletionItem> getCompletions(BallerinaCompletionContext context, FieldAccessExpressionNode node)
throws LSCompletionException {
ExpressionNode expression = node.expression();
List<LSCompletionItem> completionItems = getEntries(context, expression, false);
List<LSCompletionItem> completionItems = getEntries(context, expression);
this.sort(context, node, completionItems);

return completionItems;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public MethodCallExpressionNodeContext() {
public List<LSCompletionItem> getCompletions(BallerinaCompletionContext context, MethodCallExpressionNode node)
throws LSCompletionException {
ExpressionNode expression = node.expression();
List<LSCompletionItem> completionItems = getEntries(context, expression, false);
List<LSCompletionItem> completionItems = getEntries(context, expression);
this.sort(context, node, completionItems);

return completionItems;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public OptionalFieldAccessExpressionNodeContext() {
@Override
public List<LSCompletionItem> getCompletions(BallerinaCompletionContext ctx, OptionalFieldAccessExpressionNode node)
throws LSCompletionException {
List<LSCompletionItem> completionItems = getEntries(ctx, node.expression(), true);
List<LSCompletionItem> completionItems = getEntries(ctx, node.expression());
this.sort(ctx, node, completionItems);

return completionItems;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ public Optional<TypeSymbol> transform(FunctionDefinitionNode node) {

@Override
public Optional<TypeSymbol> transform(FieldAccessExpressionNode node) {
FieldAccessCompletionResolver resolver = new FieldAccessCompletionResolver(context, false);
FieldAccessCompletionResolver resolver = new FieldAccessCompletionResolver(context);
List<Symbol> visibleEntries = resolver.getVisibleEntries(node.expression());
NameReferenceNode nameRef = node.fieldName();
if (nameRef.kind() != SyntaxKind.SIMPLE_NAME_REFERENCE) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import io.ballerina.compiler.syntax.tree.NameReferenceNode;
import io.ballerina.compiler.syntax.tree.Node;
import io.ballerina.compiler.syntax.tree.NodeTransformer;
import io.ballerina.compiler.syntax.tree.OptionalFieldAccessExpressionNode;
import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode;
import io.ballerina.compiler.syntax.tree.SimpleNameReferenceNode;
import io.ballerina.compiler.syntax.tree.SyntaxKind;
Expand Down Expand Up @@ -72,11 +73,9 @@
*/
public class FieldAccessCompletionResolver extends NodeTransformer<Optional<TypeSymbol>> {
private final PositionedOperationContext context;
private final boolean optionalFieldAccess;

public FieldAccessCompletionResolver(PositionedOperationContext context, boolean optionalFieldAccess) {
public FieldAccessCompletionResolver(PositionedOperationContext context) {
this.context = context;
this.optionalFieldAccess = optionalFieldAccess;
}

@Override
Expand Down Expand Up @@ -111,6 +110,31 @@ public Optional<TypeSymbol> transform(FieldAccessExpressionNode node) {
return SymbolUtil.getTypeDescriptor(filteredSymbol.get());
}

@Override
public Optional<TypeSymbol> transform(OptionalFieldAccessExpressionNode node) {
// First capture the expression and the respective symbols
// In future we should use the following approach and get rid of this resolver.
Optional<TypeSymbol> resolvedType = this.context.currentSemanticModel().get().type(node);
if (resolvedType.isPresent() && resolvedType.get().typeKind() != TypeDescKind.COMPILATION_ERROR) {
return SymbolUtil.getTypeDescriptor(resolvedType.get());
}

Optional<TypeSymbol> typeSymbol = node.expression().apply(this);
NameReferenceNode fieldName = node.fieldName();
if (fieldName.kind() != SyntaxKind.SIMPLE_NAME_REFERENCE || typeSymbol.isEmpty()) {
return Optional.empty();
}
String name = ((SimpleNameReferenceNode) fieldName).name().text();
List<Symbol> visibleEntries = this.getVisibleEntries(typeSymbol.get(), node.expression());
Optional<Symbol> filteredSymbol = this.getSymbolByName(visibleEntries, name);

if (filteredSymbol.isEmpty()) {
return Optional.empty();
}

return SymbolUtil.getTypeDescriptor(filteredSymbol.get());
}

@Override
public Optional<TypeSymbol> transform(MethodCallExpressionNode node) {
Optional<TypeSymbol> exprTypeSymbol = node.expression().apply(this);
Expand Down Expand Up @@ -222,10 +246,7 @@ private List<Symbol> getVisibleEntries(TypeSymbol typeSymbol, Node node) {
case RECORD:
// If the invoked for field access expression, then avoid suggesting the optional fields
List<RecordFieldSymbol> filteredEntries =
((RecordTypeSymbol) rawType).fieldDescriptors().values().stream()
.filter(recordFieldSymbol -> this.optionalFieldAccess
|| !recordFieldSymbol.isOptional())
.collect(Collectors.toList());
new ArrayList<>(((RecordTypeSymbol) rawType).fieldDescriptors().values());
visibleEntries.addAll(filteredEntries);
break;
case OBJECT:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,29 @@
"insertText": "testField",
"insertTextFormat": "Snippet"
},
{
"label": "testOptional",
"kind": "Field",
"detail": "int",
"sortText": "A",
"insertText": "?.testOptional",
"insertTextFormat": "Snippet",
"additionalTextEdits": [
{
"range": {
"start": {
"line": 3,
"character": 24
},
"end": {
"line": 3,
"character": 25
}
},
"newText": ""
}
]
},
{
"label": "reduce(function () func, any|error initial)(any|error)",
"kind": "Function",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,29 @@
"insertText": "testField",
"insertTextFormat": "Snippet"
},
{
"label": "testOptional",
"kind": "Field",
"detail": "int",
"sortText": "A",
"insertText": "?.testOptional",
"insertTextFormat": "Snippet",
"additionalTextEdits": [
{
"range": {
"start": {
"line": 3,
"character": 24
},
"end": {
"line": 3,
"character": 25
}
},
"newText": ""
}
]
},
{
"label": "reduce(function () func, any|error initial)(any|error)",
"kind": "Function",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,29 @@
"insertText": "rec1Field2",
"insertTextFormat": "Snippet"
},
{
"label": "optionalInt",
"kind": "Field",
"detail": "int",
"sortText": "A",
"insertText": "?.optionalInt",
"insertTextFormat": "Snippet",
"additionalTextEdits": [
{
"range": {
"start": {
"line": 3,
"character": 34
},
"end": {
"line": 3,
"character": 35
}
},
"newText": ""
}
]
},
{
"label": "reduce(function () func, any|error initial)(any|error)",
"kind": "Function",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,29 @@
"insertText": "testField",
"insertTextFormat": "Snippet"
},
{
"label": "testOptional",
"kind": "Field",
"detail": "int",
"sortText": "A",
"insertText": "?.testOptional",
"insertTextFormat": "Snippet",
"additionalTextEdits": [
{
"range": {
"start": {
"line": 4,
"character": 20
},
"end": {
"line": 4,
"character": 21
}
},
"newText": ""
}
]
},
{
"label": "reduce(function () func, any|error initial)(any|error)",
"kind": "Function",
Expand Down