Skip to content

Commit

Permalink
Merge pull request #41136 from malinthar/fix-#35507
Browse files Browse the repository at this point in the history
Add spread member completion item in list constructor context
  • Loading branch information
KavinduZoysa authored Aug 8, 2023
2 parents 8da2231 + 9eb973a commit e76b3b1
Show file tree
Hide file tree
Showing 42 changed files with 5,125 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,6 @@ enum CompletionItemType {
TYPE,
FUNCTION_POINTER,
NAMED_ARG,
SPREAD
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ public static List<CodeAction> getAvailableCodeActions(CodeActionContext ctx) {
" Syntax tree is empty for file " + ctx.fileUri());
return Collections.emptyList();
}
if (ctx.currentSemanticModel().isEmpty()) {
clientLogger.logTrace(LSContextOperation.TXT_CODE_ACTION.getName() + " " +
" Semantic model is empty for module " + ctx.currentModule()
.map(module -> module.moduleName().toString()).orElse(""));
return Collections.emptyList();
}
Range highlightedRange = ctx.range();
// Run code action node analyzer
CodeActionNodeAnalyzer analyzer = CodeActionNodeAnalyzer.analyze(highlightedRange, syntaxTree.get());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2023, WSO2 LLC. (http://wso2.com) All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ballerinalang.langserver.completions;

import io.ballerina.compiler.api.symbols.Symbol;
import org.ballerinalang.langserver.commons.BallerinaCompletionContext;
import org.ballerinalang.langserver.commons.completion.AbstractLSCompletionItem;
import org.eclipse.lsp4j.CompletionItem;

import java.util.Optional;

/**
* Represents a Spread Completion Item.
* Eg: ...varName
*
* @since 2201.8.0
*/
public class SpreadCompletionItem extends AbstractLSCompletionItem {
private final Symbol expression;

public SpreadCompletionItem(BallerinaCompletionContext context, CompletionItem completionItem, Symbol expression) {
super(context, completionItem, CompletionItemType.SPREAD);
this.expression = expression;
}

public Optional<Symbol> getExpression() {
return Optional.ofNullable(expression);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@
*
* @since 2201.1.1
*/
public class SpreadFieldCompletionItemBuilder {
public class SpreadCompletionItemBuilder {

private static final String SPREAD_OPERATOR = "...";
public static final String SPREAD_OPERATOR = "...";

/**
* Build the constant {@link CompletionItem}.
Expand All @@ -46,7 +46,7 @@ public class SpreadFieldCompletionItemBuilder {
*/
public static CompletionItem build(Symbol symbol, String typeName, BallerinaCompletionContext context) {
if (symbol.kind() == FUNCTION) {
return SpreadFieldCompletionItemBuilder.build((FunctionSymbol) symbol, typeName, context);
return SpreadCompletionItemBuilder.build((FunctionSymbol) symbol, typeName, context);
}
String symbolName = symbol.getName().orElseThrow();
String insertText = SPREAD_OPERATOR + symbolName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,37 @@
*/
package org.ballerinalang.langserver.completions.providers.context;

import io.ballerina.compiler.api.SemanticModel;
import io.ballerina.compiler.api.symbols.ArrayTypeSymbol;
import io.ballerina.compiler.api.symbols.FunctionSymbol;
import io.ballerina.compiler.api.symbols.Symbol;
import io.ballerina.compiler.api.symbols.SymbolKind;
import io.ballerina.compiler.api.symbols.TypeDescKind;
import io.ballerina.compiler.api.symbols.TypeSymbol;
import io.ballerina.compiler.api.symbols.VariableSymbol;
import io.ballerina.compiler.syntax.tree.ListConstructorExpressionNode;
import io.ballerina.compiler.syntax.tree.NonTerminalNode;
import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode;
import io.ballerina.compiler.syntax.tree.SyntaxKind;
import io.ballerina.projects.Document;
import org.ballerinalang.annotation.JavaSPIService;
import org.ballerinalang.langserver.common.utils.NameUtil;
import org.ballerinalang.langserver.common.utils.PositionUtil;
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.SpreadCompletionItem;
import org.ballerinalang.langserver.completions.builder.SpreadCompletionItemBuilder;
import org.ballerinalang.langserver.completions.providers.AbstractCompletionProvider;
import org.ballerinalang.langserver.completions.util.QNameRefCompletionUtil;
import org.ballerinalang.langserver.completions.util.SortingUtil;
import org.eclipse.lsp4j.CompletionItem;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
* Completion provider for {@link ListConstructorExpressionNode} context.
Expand All @@ -55,15 +70,66 @@ public List<LSCompletionItem> getCompletions(BallerinaCompletionContext context,
completionItems.addAll(this.getCompletionItemList(entries, context));
} else {
completionItems.addAll(this.expressionCompletions(context));
if (context.getNodeAtCursor().kind() != SyntaxKind.SPREAD_MEMBER) {
completionItems.addAll(this.spreadOperatorCompletions(context));
}
}
this.sort(context, node, completionItems);

return completionItems;
}

private List<LSCompletionItem> spreadOperatorCompletions(BallerinaCompletionContext context) {
Optional<SemanticModel> semanticModel = context.currentSemanticModel();
Optional<Document> document = context.currentDocument();
if (semanticModel.isEmpty() || document.isEmpty()) {
return Collections.emptyList();
}
Optional<TypeSymbol> expectedType = semanticModel.get().expectedType(document.get(),
PositionUtil.getLinePosition(context.getCursorPosition()));
if (expectedType.isEmpty()) {
return Collections.emptyList();
}
return context.visibleSymbols(context.getCursorPosition()).stream()
.filter(symbol -> {
if (symbol.getName().isEmpty()) {
return false;
}
TypeSymbol typeDescriptor;
if (symbol.kind() == SymbolKind.VARIABLE) {
typeDescriptor = ((VariableSymbol) symbol).typeDescriptor();
if (typeDescriptor.typeKind() != TypeDescKind.ARRAY) {
return false;
}
} else if (symbol.kind() == SymbolKind.FUNCTION) {
Optional<TypeSymbol> typeSymbol = ((FunctionSymbol) symbol).typeDescriptor()
.returnTypeDescriptor();
if (typeSymbol.isEmpty()) {
return false;
}
typeDescriptor = typeSymbol.get();
} else {
return false;
}
return typeDescriptor.typeKind() == TypeDescKind.ARRAY &&
((ArrayTypeSymbol) typeDescriptor).memberTypeDescriptor().subtypeOf(expectedType.get());
}).map(symbol -> {
TypeSymbol typeDescriptor;
if (symbol.kind() == SymbolKind.VARIABLE) {
typeDescriptor = ((VariableSymbol) symbol).typeDescriptor();
} else {
typeDescriptor = ((FunctionSymbol) symbol).typeDescriptor().returnTypeDescriptor().get();
}
String typeName = NameUtil.getModifiedTypeName(context, typeDescriptor);
CompletionItem completionItem =
SpreadCompletionItemBuilder.build(symbol, typeName, context);
return new SpreadCompletionItem(context, completionItem, symbol);
}).collect(Collectors.toList());
}

@Override
public boolean onPreValidation(BallerinaCompletionContext context, ListConstructorExpressionNode node) {
return node.textRange().startOffset() <= context.getCursorPositionInTree()
return node.textRange().startOffset() <= context.getCursorPositionInTree()
&& context.getCursorPositionInTree() <= node.textRange().endOffset();
}

Expand All @@ -76,6 +142,15 @@ public void sort(BallerinaCompletionContext context, ListConstructorExpressionNo
if (contextType.isEmpty()) {
// Added for safety.
sortText = SortingUtil.genSortText(SortingUtil.toRank(context, lsCItem, 2));
} else if (lsCItem.getType() == LSCompletionItem.CompletionItemType.SPREAD) {
Optional<Symbol> expression = ((SpreadCompletionItem) lsCItem).getExpression();
//Default sort text of variable or function symbols
int lastRank = expression.map(expr -> expr.kind() == SymbolKind.FUNCTION ? 4 : 3)
.orElse(3);
//Set spread completion item sort text as the same of variable or function symbols
sortText = SortingUtil.genSortText(1)
+ SortingUtil.genSortText(1) //Assignable
+ SortingUtil.genSortText(lastRank);
} else if (!SortingUtil.isTypeCompletionItem(lsCItem)) {
/*
Here the sort text is three-fold.
Expand All @@ -89,7 +164,6 @@ Then we again append the sorting among the symbols (ex: functions over variable)
} else {
sortText = SortingUtil.genSortText(2) + SortingUtil.genSortText(SortingUtil.toRank(context, lsCItem));
}

lsCItem.getCompletionItem().setSortText(sortText);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,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.SymbolCompletionItem;
import org.ballerinalang.langserver.completions.SpreadCompletionItem;
import org.ballerinalang.langserver.completions.util.QNameRefCompletionUtil;
import org.ballerinalang.langserver.completions.util.SortingUtil;

Expand Down Expand Up @@ -104,33 +104,33 @@ public void sort(BallerinaCompletionContext context, MappingConstructorExpressio
} else {
scope = Scope.OTHER;
}

Optional<TypeSymbol> contextType = context.getContextType();
if (contextType.isEmpty()) {
super.sort(context, node, completionItems);
return;
}

completionItems.forEach(lsCItem -> {
// In the field name context, we have to give a special consideration to the map type variables
// suggested with the spread operator (...map1).
if (scope == Scope.FIELD_NAME && lsCItem.getType() == LSCompletionItem.CompletionItemType.SYMBOL) {
SymbolCompletionItem symbolCItem = (SymbolCompletionItem) lsCItem;
Optional<TypeSymbol> mapTypeParam = symbolCItem.getSymbol()
if (scope == Scope.FIELD_NAME && lsCItem.getType() == LSCompletionItem.CompletionItemType.SPREAD) {
Optional<Symbol> expression = ((SpreadCompletionItem) lsCItem).getExpression();

Optional<TypeSymbol> mapTypeParam = expression
.flatMap(SymbolUtil::getTypeDescriptor)
.filter(typeDesc -> typeDesc.typeKind() == TypeDescKind.MAP)
.map(typeDesc -> (MapTypeSymbol) typeDesc)
.map(MapTypeSymbol::typeParam);

// If the completion item is a map type variable and is the spread operator, we give it priority
if (mapTypeParam.isPresent() && mapTypeParam.get().subtypeOf(contextType.get())
&& lsCItem.getCompletionItem().getLabel().startsWith("...")) {
int rank = SortingUtil.toRank(context, lsCItem);
String sortText = SortingUtil.genSortText(1) + SortingUtil.genSortText(rank);
if ((mapTypeParam.isPresent() && mapTypeParam.get().subtypeOf(contextType.get()))
|| expression.isPresent()) {
int lastRank = expression.map(expr -> expr.kind() == SymbolKind.FUNCTION ? 4 : 3)
.orElse(3);
String sortText = SortingUtil.genSortText(1) + SortingUtil.genSortText(lastRank);
lsCItem.getCompletionItem().setSortText(sortText);
return;
}
}

String sortText = SortingUtil.genSortTextByAssignability(context, lsCItem, contextType.get());
lsCItem.getCompletionItem().setSortText(sortText);
});
Expand Down Expand Up @@ -168,7 +168,7 @@ protected List<String> getFields(MappingConstructorExpressionNode node) {
.map(field -> ((IdentifierToken) ((SpecificFieldNode) field).fieldName()).text())
.collect(Collectors.toList());
}

private enum Scope {
VALUE_EXPR,
FIELD_NAME,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@
import org.ballerinalang.langserver.commons.BallerinaCompletionContext;
import org.ballerinalang.langserver.commons.completion.LSCompletionItem;
import org.ballerinalang.langserver.completions.SnippetCompletionItem;
import org.ballerinalang.langserver.completions.SymbolCompletionItem;
import org.ballerinalang.langserver.completions.builder.SpreadFieldCompletionItemBuilder;
import org.ballerinalang.langserver.completions.SpreadCompletionItem;
import org.ballerinalang.langserver.completions.builder.SpreadCompletionItemBuilder;
import org.ballerinalang.langserver.completions.providers.AbstractCompletionProvider;
import org.ballerinalang.langserver.completions.util.QNameRefCompletionUtil;
import org.ballerinalang.langserver.completions.util.Snippet;
Expand Down Expand Up @@ -337,8 +337,8 @@ private List<LSCompletionItem> getSpreadFieldCompletionItemList(List<Symbol> sym
String typeName = (typeDescriptor.isEmpty() || typeDescriptor.get().typeKind() == null) ? "" :
NameUtil.getModifiedTypeName(ctx, typeDescriptor.get());
CompletionItem cItem;
cItem = SpreadFieldCompletionItemBuilder.build(symbol, typeName, ctx);
completionItems.add(new SymbolCompletionItem(ctx, symbol, cItem));
cItem = SpreadCompletionItemBuilder.build(symbol, typeName, ctx);
completionItems.add(new SpreadCompletionItem(ctx, cItem, symbol));
processedSymbols.add(symbol);
});
return completionItems;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,15 @@
"filterText": "xml ``",
"insertText": "xml `${1}`",
"insertTextFormat": "Snippet"
},
{
"label": "...numberList",
"kind": "Variable",
"detail": "int[]",
"sortText": "AAC",
"filterText": "numberList",
"insertText": "...numberList",
"insertTextFormat": "Snippet"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,15 @@
"filterText": "xml ``",
"insertText": "xml `${1}`",
"insertTextFormat": "Snippet"
},
{
"label": "...numberList",
"kind": "Variable",
"detail": "int[]",
"sortText": "AAC",
"filterText": "numberList",
"insertText": "...numberList",
"insertTextFormat": "Snippet"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,15 @@
"filterText": "xml ``",
"insertText": "xml `${1}`",
"insertTextFormat": "Snippet"
},
{
"label": "...numberList",
"kind": "Variable",
"detail": "int[]",
"sortText": "AAC",
"filterText": "numberList",
"insertText": "...numberList",
"insertTextFormat": "Snippet"
}
]
}
Loading

0 comments on commit e76b3b1

Please sign in to comment.