Skip to content

Commit 2cc4b84

Browse files
committed
Build type completion proposal directly from match
Delegating to IType may require to open (parse) the underlying file. Directly build the proposal from the search match which contains enough information.
1 parent 19231d9 commit 2cc4b84

File tree

1 file changed

+179
-3
lines changed

1 file changed

+179
-3
lines changed

org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/codeassist/DOMCompletionEngine.java

Lines changed: 179 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4812,8 +4812,184 @@ private boolean staticOnly() {
48124812
return false;
48134813
}
48144814

4815-
private CompletionProposal toProposal(TypeNameMatch typeNameMatch) {
4816-
return toProposal(typeNameMatch.getType(), typeNameMatch.getAccessibility(), typeNameMatch.getModifiers());
4815+
/// In this method, we avoid retrieving the IType, because most of IType methods require re-parsing the source
4816+
private CompletionProposal toProposal(TypeNameMatch type) {
4817+
DOMInternalCompletionProposal res = createProposal(CompletionProposal.TYPE_REF);
4818+
char[] simpleName = type.getSimpleTypeName().toCharArray();
4819+
char[] signature = Signature.createTypeSignature(type.getFullyQualifiedName(), true).toCharArray();
4820+
res.setSignature(signature);
4821+
res.setDeclarationSignature(type.getPackageName().toCharArray());
4822+
4823+
// set completion, considering nested types
4824+
StringBuilder completion = new StringBuilder();
4825+
if (completionContext.getCurrentTypeBinding() != null
4826+
&& completionContext.getCurrentTypeBinding().getJavaElement() != null
4827+
&& type.getFullyQualifiedName().equals(((IType)completionContext.getCurrentTypeBinding().getJavaElement()).getFullyQualifiedName())) {
4828+
completion.insert(0, type.getSimpleTypeName());
4829+
} else {
4830+
completion.insert(0, type.getFullyQualifiedName());
4831+
// ASTNode currentName = this.toComplete instanceof QualifiedName qn && FAKE_IDENTIFIER.equals(qn.getName().toString()) ? qn.getName() : this.toComplete instanceof Name ? this.toComplete : null;
4832+
// while (cursor instanceof IType currentType && (completion.isEmpty() || currentName == null || (!Objects.equals(currentName.toString(), currentType.getElementName()) && !Objects.equals(currentName.toString(), currentType.getFullyQualifiedName())))) {
4833+
// if (!completion.isEmpty()) {
4834+
// completion.insert(0, '.');
4835+
// }
4836+
// completion.insert(0, cursor.getElementName());
4837+
// cursor = cursor.getParent();
4838+
// if (currentName != null && currentName.getLocationInParent() == QualifiedName.NAME_PROPERTY) {
4839+
// currentName = ((QualifiedName)currentName.getParent()).getQualifier();
4840+
// } else {
4841+
// currentName = null;
4842+
// }
4843+
// }
4844+
}
4845+
4846+
boolean isMember = false;
4847+
// try {
4848+
// isMember = type.isMember();
4849+
// } catch (JavaModelException e) {
4850+
// // do nothing
4851+
// }
4852+
Javadoc javadoc = (Javadoc) DOMCompletionUtils.findParent(this.toComplete, new int[] { ASTNode.JAVADOC });
4853+
ASTNode parentTypeDeclaration = DOMCompletionUtils.findParentTypeDeclaration(this.toComplete);
4854+
if (parentTypeDeclaration != null || javadoc != null) {
4855+
String fullTypeName = type.getFullyQualifiedName();
4856+
boolean inImports = ((List<ImportDeclaration>)this.unit.imports()).stream().anyMatch(improt -> fullTypeName.equals(improt.getName().toString()));
4857+
boolean isInJavaLang = type.getFullyQualifiedName().startsWith("java.lang.") && !type.getFullyQualifiedName().substring("java.lang.".length()).contains(".");
4858+
IPackageBinding currentPackageBinding = completionContext.getCurrentTypeBinding() == null ? null : completionContext.getCurrentTypeBinding().getPackage();
4859+
// TODO: what about qualified references to inner classes?
4860+
if (!type.getPackageName().isEmpty() && (currentPackageBinding == null
4861+
|| (this.qualifiedPrefix.startsWith(type.getPackageName()) && javadoc != null)
4862+
|| ((!type.getPackageName().equals(currentPackageBinding.getName()) || (isMember && this.qualifiedPrefix.equals(this.prefix) && !type.getFullyQualifiedName().equals(this.modelUnit)))
4863+
&& !type.getPackageName().equals("java.lang"))) && !inImports && !isInJavaLang) { //$NON-NLS-1$
4864+
completion.insert(0, '.');
4865+
completion.insert(0, type.getPackageName());
4866+
}
4867+
} else {
4868+
// in imports list
4869+
int lastOffset = this.toComplete.getStartPosition() + this.toComplete.getLength();
4870+
if (lastOffset >= this.textContent.length() || this.textContent.charAt(lastOffset) != ';') {
4871+
completion.append(';');
4872+
}
4873+
}
4874+
res.setCompletion(completion.toString().toCharArray());
4875+
4876+
if (this.toComplete instanceof FieldAccess || this.prefix.isEmpty()) {
4877+
res.setReplaceRange(this.offset, this.offset);
4878+
} else if (this.completionContext.isInJavadoc()) {
4879+
if (this.qualifiedPrefix.equals(this.prefix)) {
4880+
setRange(res);
4881+
} else {
4882+
setQualifiedRange(res);
4883+
}
4884+
} else if (this.toComplete instanceof MarkerAnnotation) {
4885+
res.setReplaceRange(this.toComplete.getStartPosition() + 1, this.toComplete.getStartPosition() + this.toComplete.getLength());
4886+
} else if (this.toComplete instanceof SimpleName currentName && FAKE_IDENTIFIER.equals(currentName.toString())) {
4887+
res.setReplaceRange(this.offset, this.offset);
4888+
} else if (this.toComplete instanceof SimpleName) {
4889+
res.setReplaceRange(this.toComplete.getStartPosition(), this.toComplete.getStartPosition() + this.toComplete.getLength());
4890+
} else if (this.toComplete instanceof ThisExpression thisExpression
4891+
&& thisExpression.getQualifier() != null
4892+
&& this.offset > (thisExpression.getQualifier().getStartPosition() + thisExpression.getQualifier().getLength())) {
4893+
setRange(res);
4894+
} else if (this.toComplete instanceof MethodRefParameter || this.toComplete instanceof MethodRef || this.toComplete instanceof TextElement){
4895+
if (this.qualifiedPrefix.equals(this.prefix)) {
4896+
setRange(res);
4897+
} else {
4898+
setQualifiedRange(res);
4899+
}
4900+
} else {
4901+
res.setReplaceRange(this.toComplete.getStartPosition(), this.offset);
4902+
}
4903+
res.setFlags(type.getModifiers());
4904+
if (this.toComplete instanceof SimpleName) {
4905+
res.setTokenRange(this.toComplete.getStartPosition(), this.toComplete.getStartPosition() + this.toComplete.getLength());
4906+
} else if (this.toComplete instanceof MarkerAnnotation) {
4907+
res.setTokenRange(this.offset, this.offset);
4908+
} else {
4909+
setTokenRange(res);
4910+
}
4911+
boolean nodeInImports = DOMCompletionUtils.findParent(this.toComplete, new int[] { ASTNode.IMPORT_DECLARATION }) != null;
4912+
4913+
boolean fromCurrentCU = ((List<AbstractTypeDeclaration>)this.unit.types()).stream().map(decl -> decl.resolveBinding().getQualifiedName()).anyMatch(qualified -> type.getFullyQualifiedName().equals(qualified));
4914+
boolean inSamePackage = false;
4915+
boolean typeIsImported = ((List<ImportDeclaration>)this.unit.imports()).stream().anyMatch(importDecl -> {
4916+
return importDecl.getName().toString().equals(type.getFullyQualifiedName());
4917+
});
4918+
PackageDeclaration packageDeclaration = this.unit.getPackage();
4919+
if (packageDeclaration != null) {
4920+
inSamePackage = packageDeclaration.getName().toString().equals(type.getPackageName());
4921+
} else {
4922+
inSamePackage = type.getPackageName().isEmpty();
4923+
}
4924+
boolean isExceptionExpected = DOMCompletionUtils.findParent(this.toComplete, new int[] { ASTNode.CATCH_CLAUSE }) != null //
4925+
|| (DOMCompletionUtils.findParent(this.toComplete, new int[] { ASTNode.TAG_ELEMENT }) instanceof TagElement te && TagElement.TAG_THROWS.equals(te.getTagName()));
4926+
int relevance = RelevanceConstants.R_DEFAULT;
4927+
relevance += RelevanceConstants.R_RESOLVED;
4928+
// relevance += RelevanceUtils.computeRelevanceForInteresting(type, expectedTypes);
4929+
relevance += RelevanceUtils.computeRelevanceForRestrictions(type.getAccessibility(), this.settings);
4930+
// relevance += (isExceptionExpected && DOMCompletionUtils.findInSupers(type, "Ljava/lang/Exception;", this.workingCopyOwner, this.typeHierarchyCache) ? RelevanceConstants.R_EXCEPTION : 0);
4931+
// relevance += RelevanceUtils.computeRelevanceForInheritance(this.qualifyingType, type);
4932+
relevance += RelevanceUtils.computeRelevanceForQualification(!"java.lang".equals(type.getPackageName()) && !nodeInImports && !fromCurrentCU && !inSamePackage && !typeIsImported, this.prefix, this.qualifiedPrefix);
4933+
if (type.getFullyQualifiedName().startsWith("java.")
4934+
&& !(DOMCompletionUtils.findParent(this.toComplete, new int[] { ASTNode.CATCH_CLAUSE }) != null && this.prefix.isEmpty())) {
4935+
relevance += RelevanceConstants.R_JAVA_LIBRARY;
4936+
}
4937+
// sometimes subclasses and superclasses are considered, sometimes they aren't
4938+
// relevance += (isExceptionExpected ? RelevanceUtils.computeRelevanceForExpectingType(type, expectedTypes, this.workingCopyOwner, this.typeHierarchyCache) : RelevanceUtils.simpleComputeRelevanceForExpectingType(type, expectedTypes));
4939+
relevance += RelevanceUtils.computeRelevanceForCaseMatching(this.prefix.toCharArray(), simpleName, this.assistOptions);
4940+
try {
4941+
if ((type.getModifiers() & Flags.AccAnnotation) != 0) {
4942+
ASTNode current = this.toComplete;
4943+
while (current instanceof Name) {
4944+
current = current.getParent();
4945+
}
4946+
if (current instanceof Annotation annotation) {
4947+
relevance += RelevanceConstants.R_ANNOTATION;
4948+
IAnnotation targetAnnotation = null; //type.getAnnotation(Target.class.getName());
4949+
if (targetAnnotation == null || !targetAnnotation.exists()) {
4950+
// On Javadoc for @Target: "If a Target meta-annotation is not present on an annotation type declaration,
4951+
// the declared type may be used on any program element."
4952+
relevance += RelevanceConstants.R_TARGET;
4953+
} else {
4954+
var memberValuePairs = targetAnnotation.getMemberValuePairs();
4955+
if (memberValuePairs != null) {
4956+
if (Stream.of(memberValuePairs)
4957+
.filter(memberValue -> "value".equals(memberValue.getMemberName()))
4958+
.map(IMemberValuePair::getValue)
4959+
.anyMatch(target -> matchHostType(annotation.getParent(), target))) {
4960+
relevance += RelevanceConstants.R_TARGET;
4961+
}
4962+
}
4963+
}
4964+
}
4965+
}
4966+
} catch (JavaModelException ex) {
4967+
ILog.get().warn(ex.getMessage(), ex);
4968+
}
4969+
if (isInExtendsOrImplements(this.toComplete) != null) {
4970+
if ((type.getModifiers() & Flags.AccAnnotation) != 0) {
4971+
relevance += RelevanceConstants.R_ANNOTATION;
4972+
}
4973+
if ((type.getModifiers() & Flags.AccInterface) != 0) {
4974+
relevance += RelevanceConstants.R_INTERFACE;
4975+
} else {
4976+
relevance += RelevanceConstants.R_CLASS;
4977+
}
4978+
}
4979+
res.setRelevance(relevance);
4980+
if (parentTypeDeclaration != null) {
4981+
String packageName = ""; //$NON-NLS-1$
4982+
PackageDeclaration packageDecl = this.unit.getPackage();
4983+
if (packageDecl != null) {
4984+
packageName = packageDecl.getName().toString();
4985+
}
4986+
if (!packageName.equals(type.getPackageName()) && !new String(res.getCompletion()).equals(type.getFullyQualifiedName())) {
4987+
// propose importing the type
4988+
res.setRequiredProposals(new CompletionProposal[] { toImportProposal(simpleName, signature, type.getPackageName().toCharArray()) });
4989+
}
4990+
}
4991+
return res;
4992+
48174993
}
48184994

48194995
private CompletionProposal toProposal(IType type, int access) {
@@ -4965,7 +5141,7 @@ private CompletionProposal toProposal(IType type, int access, int modifiers) {
49655141
relevance += (isExceptionExpected ? RelevanceUtils.computeRelevanceForExpectingType(type, expectedTypes, this.workingCopyOwner, this.typeHierarchyCache) : RelevanceUtils.simpleComputeRelevanceForExpectingType(type, expectedTypes));
49665142
relevance += RelevanceUtils.computeRelevanceForCaseMatching(this.prefix.toCharArray(), simpleName, this.assistOptions);
49675143
try {
4968-
if (type.isAnnotation()) {
5144+
if ((type.getFlags() & Flags.AccAnnotation) != 0) {
49695145
ASTNode current = this.toComplete;
49705146
while (current instanceof Name) {
49715147
current = current.getParent();

0 commit comments

Comments
 (0)