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

add support for method parameter completions #396

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -45,15 +45,19 @@
import org.eclipse.jdt.core.dom.NodeFinder;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.VariableDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.TypeNameMatchRequestor;
import org.eclipse.jdt.internal.codeassist.impl.AssistOptions;
import org.eclipse.jdt.internal.codeassist.impl.Engine;
import org.eclipse.jdt.internal.compiler.env.AccessRestriction;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.core.JavaProject;
import org.eclipse.jdt.internal.core.SearchableEnvironment;

Expand All @@ -75,8 +79,9 @@ public class DOMCompletionEngine implements Runnable {
private ExpectedTypes expectedTypes;
private String prefix;
private ASTNode toComplete;
private DOMCompletionEngineVariableDeclHandler variableDeclHandler;

private static class Bindings {
static class Bindings {
private HashSet<IMethodBinding> methods = new HashSet<>();
private HashSet<IBinding> others = new HashSet<>();

Expand Down Expand Up @@ -132,6 +137,7 @@ public DOMCompletionEngine(int offset, CompilationUnit domUnit, ICompilationUnit
// TODO sorting/relevance: closest/prefix match should go first
// ...
this.nestedEngine = new CompletionEngine(this.nameEnvironment, this.requestor, this.modelUnit.getOptions(true), this.modelUnit.getJavaProject(), workingCopyOwner, monitor);
this.variableDeclHandler = new DOMCompletionEngineVariableDeclHandler();
}

private static Collection<? extends IBinding> visibleBindings(ASTNode node, int offset) {
Expand Down Expand Up @@ -170,7 +176,8 @@ public void run() {
if (this.toComplete instanceof SimpleName simpleName) {
int charCount = this.offset - simpleName.getStartPosition();
completeAfter = simpleName.getIdentifier().substring(0, charCount);
if (simpleName.getParent() instanceof FieldAccess || simpleName.getParent() instanceof MethodInvocation) {
if (simpleName.getParent() instanceof FieldAccess || simpleName.getParent() instanceof MethodInvocation
|| simpleName.getParent() instanceof VariableDeclaration) {
context = this.toComplete.getParent();
}
}
Expand Down Expand Up @@ -249,6 +256,13 @@ public void acceptConstructor(int modifiers, char[] simpleTypeName, int paramete
}
// else complete parameters, get back to default
}
if (context instanceof VariableDeclaration declaration) {
var binding = declaration.resolveBinding();
if (binding != null) {
this.variableDeclHandler.findVariableNames(binding, completeAfter, scope).stream()
.map(name -> toProposal(binding, name)).forEach(this.requestor::accept);
}
}

ASTNode current = this.toComplete;
ASTNode parent = current;
Expand Down Expand Up @@ -323,8 +337,11 @@ private void processMembers(ITypeBinding typeBinding, Bindings scope) {
}
processMembers(typeBinding.getSuperclass(), scope);
}

private CompletionProposal toProposal(IBinding binding) {
return toProposal(binding, binding.getName());
}

private CompletionProposal toProposal(IBinding binding, String completion) {
if (binding instanceof ITypeBinding && binding.getJavaElement() instanceof IType type) {
return toProposal(type);
}
Expand All @@ -334,7 +351,6 @@ private CompletionProposal toProposal(IBinding binding) {
binding instanceof IVariableBinding variableBinding ? CompletionProposal.LOCAL_VARIABLE_REF :
-1, this.offset);
res.setName(binding.getName().toCharArray());
String completion = binding.getName();
if (binding instanceof IMethodBinding) {
completion += "()"; //$NON-NLS-1$
}
Expand Down Expand Up @@ -412,13 +428,46 @@ private CompletionProposal toPackageProposal(String packageName, ASTNode complet
InternalCompletionProposal res = new InternalCompletionProposal(CompletionProposal.PACKAGE_REF, this.offset);
res.setName(packageName.toCharArray());
res.setCompletion(packageName.toCharArray());
res.setReplaceRange(completing.getStartPosition(), this.offset);
res.setDeclarationSignature(packageName.toCharArray());
res.completionEngine = this.nestedEngine;
res.nameLookup = this.nameEnvironment.nameLookup;
configureProposal(res, completing);
return res;
}

private CompletionProposal toVariableNameProposal(String name, VariableDeclaration variable, ASTNode completing) {
InternalCompletionProposal res = new InternalCompletionProposal(CompletionProposal.VARIABLE_DECLARATION,
this.offset);
res.setName(name.toCharArray());
res.setCompletion(name.toCharArray());

if (variable instanceof SingleVariableDeclaration sv) {
var binding = sv.resolveBinding();
if (binding == null) {
return res;
}
if (binding.getType().getPackage() != null) {
res.setPackageName(binding.getType().getPackage().getName().toCharArray());
}
if (binding.getType() instanceof TypeBinding tb) {
res.setSignature(Engine.getSignature(tb));
res.setRelevance(
CompletionEngine.computeBaseRelevance() + CompletionEngine.computeRelevanceForResolution()
+ this.nestedEngine.computeRelevanceForInterestingProposal()
+ CompletionEngine.computeRelevanceForCaseMatching(this.prefix.toCharArray(),
binding.getName().toCharArray(), this.assistOptions)
+ computeRelevanceForExpectingType((ITypeBinding) tb)
+ CompletionEngine.computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE)
+ RelevanceConstants.R_NON_INHERITED);
}
}
return res;
}

private void configureProposal(InternalCompletionProposal proposal, ASTNode completing) {
proposal.setReplaceRange(completing.getStartPosition(), this.offset);
proposal.completionEngine = this.nestedEngine;
proposal.nameLookup = this.nameEnvironment.nameLookup;
}

private int computeRelevanceForExpectingType(ITypeBinding proposalType){
if (proposalType != null) {
int relevance = 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*******************************************************************************
* Copyright (c) 2024 Red Hat, Inc. and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Gayan Perera - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.codeassist;

import java.util.List;

import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.internal.codeassist.DOMCompletionEngine.Bindings;

/**
* This class define methods which are used for handling dom based completions for variable declarations.
*/
gayanper marked this conversation as resolved.
Show resolved Hide resolved
public final class DOMCompletionEngineVariableDeclHandler {
gayanper marked this conversation as resolved.
Show resolved Hide resolved

/**
* Find variable names for given variable binding.
*/
public List<String> findVariableNames(IVariableBinding binding, String token, Bindings scope) {
// todo: add more variable names suggestions and also consider the visible variables to avoid conflicting names.
var typeName = binding.getType().getName();
if (token != null && !token.isEmpty() && !typeName.startsWith(token)) {
typeName = token.concat(typeName);
} else {
typeName = typeName.length() > 1 ? typeName.substring(0, 1).toLowerCase().concat(typeName.substring(1))
: typeName;
}
return List.of(typeName);
}
}
Loading