Skip to content

Commit 83c79ae

Browse files
datho7561rgrunber
authored andcommitted
Another round of keyword completions
- Completion for `instanceof` keyword - Since it's sort of like an infix operator, I needed to write some more involved logic to get it to work properly. - Suggest all type-declaration like keywords instead of just `class` - eg. `enum`, `interface`, `record` - Complete the `import` keyword - See eclipse-jdt#3799, I won't fix that test case - fix module import completion (only show it when `import module` is used) - fix replace range on package completion - It uses the nested engine currently and the root cause was us not modifying the internal state correctly. I think using nested engine is bad, but I did the lazy hack instead of replacing the nested engine with our own `ISearchRequestor` implementation Signed-off-by: David Thompson <[email protected]>
1 parent 083f424 commit 83c79ae

File tree

3 files changed

+93
-26
lines changed

3 files changed

+93
-26
lines changed

org.eclipse.jdt.core.javac/src/org/eclipse/jdt/core/dom/JavacBindingResolver.java

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1594,16 +1594,19 @@ private IBinding resolveImportImpl(ImportDeclaration importDeclaration) {
15941594
if (importDeclaration.isStatic()) {
15951595
com.sun.tools.javac.code.Type type = fieldAccess.getExpression().type;
15961596
if (type != null) {
1597-
IBinding binding = Arrays.stream(this.bindings.getTypeBinding(type).getDeclaredMethods())
1598-
.filter(method -> Objects.equals(fieldAccess.getIdentifier().toString(), method.getName()))
1599-
.findAny()
1600-
.orElse(null);
1601-
if (binding == null) {
1602-
binding = Arrays.stream(this.bindings.getTypeBinding(type).getDeclaredFields()).filter(
1603-
field -> Objects.equals(fieldAccess.getIdentifier().toString(), field.getName()))
1604-
.findAny().orElse(null);
1597+
ITypeBinding typeBinding = this.bindings.getTypeBinding(type);
1598+
if (typeBinding != null) {
1599+
IBinding binding = Arrays.stream(typeBinding.getDeclaredMethods())
1600+
.filter(method -> Objects.equals(fieldAccess.getIdentifier().toString(), method.getName()))
1601+
.findAny()
1602+
.orElse(null);
1603+
if (binding == null) {
1604+
binding = Arrays.stream(typeBinding.getDeclaredFields()).filter(
1605+
field -> Objects.equals(fieldAccess.getIdentifier().toString(), field.getName()))
1606+
.findAny().orElse(null);
1607+
}
1608+
return binding;
16051609
}
1606-
return binding;
16071610
}
16081611
}
16091612
}

org.eclipse.jdt.core.javac/src/org/eclipse/jdt/core/dom/JavacConverter.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,9 @@ private ImportDeclaration convert(JCImport javac) {
407407
} else {
408408
res.setName(toName(select));
409409
}
410+
if (res.getName().toString().contains(JavacConverter.FAKE_IDENTIFIER)) {
411+
res.setFlags(res.getFlags() | ASTNode.MALFORMED);
412+
}
410413
if (javac.isStatic() || javac.isModule()) {
411414
if (this.ast.apiLevel < AST.JLS23_INTERNAL) {
412415
if (!javac.isStatic()) {

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

Lines changed: 78 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
111111
import org.eclipse.jdt.core.dom.ModuleDeclaration;
112112
import org.eclipse.jdt.core.dom.Name;
113+
import org.eclipse.jdt.core.dom.NodeFinder;
113114
import org.eclipse.jdt.core.dom.NormalAnnotation;
114115
import org.eclipse.jdt.core.dom.NumberLiteral;
115116
import org.eclipse.jdt.core.dom.PackageDeclaration;
@@ -156,6 +157,7 @@
156157
import org.eclipse.jdt.core.search.TypeNameMatchRequestor;
157158
import org.eclipse.jdt.internal.codeassist.impl.AssistOptions;
158159
import org.eclipse.jdt.internal.codeassist.impl.Keywords;
160+
import org.eclipse.jdt.internal.codeassist.impl.RestrictedIdentifiers;
159161
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
160162
import org.eclipse.jdt.internal.compiler.parser.RecoveryScanner;
161163
import org.eclipse.jdt.internal.core.JarPackageFragmentRoot;
@@ -646,6 +648,8 @@ public void complete(org.eclipse.jdt.internal.compiler.env.ICompilationUnit sour
646648
}
647649
} else if (this.toComplete instanceof QualifiedName && this.toComplete.getParent() instanceof TagElement tagElement) {
648650
context = tagElement;
651+
} else if (this.toComplete instanceof Modifier && this.toComplete.getParent() instanceof ImportDeclaration) {
652+
context = this.toComplete.getParent();
649653
}
650654
this.prefix = token == null ? new String() : new String(token);
651655
if (this.toComplete instanceof MethodInvocation methodInvocation && this.offset == (methodInvocation.getName().getStartPosition() + methodInvocation.getName().getLength()) + 1) {
@@ -792,9 +796,7 @@ public void complete(org.eclipse.jdt.internal.compiler.env.ICompilationUnit sour
792796
// }
793797
ITypeBinding typeDeclBinding = typeDecl.resolveBinding();
794798
findOverridableMethods(typeDeclBinding, this.javaProject, context);
795-
if (!isFailedMatch(this.prefix.toCharArray(), Keywords.CLASS)) {
796-
this.requestor.accept(createKeywordProposal(Keywords.CLASS, this.toComplete.getStartPosition(), this.offset));
797-
}
799+
suggestClassDeclarationLikeKeywords();
798800
suggestTypeKeywords(true);
799801
suggestModifierKeywords(fieldDeclaration.getModifiers());
800802
if (!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
@@ -1563,19 +1565,39 @@ public void complete(org.eclipse.jdt.internal.compiler.env.ICompilationUnit sour
15631565
}
15641566
}
15651567
}
1566-
if (context instanceof ImportDeclaration) {
1568+
if (context instanceof ImportDeclaration importDeclaration) {
15671569
if (context.getAST().apiLevel() >= AST.JLS23
1568-
&& this.javaProject.getOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, true).equals(JavaCore.ENABLED)) {
1570+
&& this.javaProject.getOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, true).equals(JavaCore.ENABLED)
1571+
&& Modifier.isModule(importDeclaration.getModifiers())) {
15691572
findModules(this.qualifiedPrefix.toCharArray(), this.javaProject, this.assistOptions, Collections.emptySet());
15701573
suggestDefaultCompletions = false;
15711574
}
15721575
}
1573-
if (context instanceof CompilationUnit unit &&
1574-
CharOperation.prefixEquals(completionContext.getToken(), Keywords.PACKAGE) &&
1575-
unit.getPackage() == null &&
1576-
this.offset <= ((Collection<ASTNode>)unit.imports()).stream().mapToInt(ASTNode::getStartPosition).filter(n -> n >= 0).min().orElse(Integer.MAX_VALUE) &&
1577-
this.offset <= ((Collection<ASTNode>)unit.types()).stream().mapToInt(ASTNode::getStartPosition).filter(n -> n >= 0).min().orElse(Integer.MAX_VALUE)) {
1578-
this.requestor.accept(createKeywordProposal(Keywords.PACKAGE, completionContext.getTokenStart(), completionContext.getTokenEnd()));
1576+
if (context instanceof CompilationUnit unit) {
1577+
if (CharOperation.prefixEquals(completionContext.getToken(), Keywords.PACKAGE) &&
1578+
unit.getPackage() == null &&
1579+
this.offset <= ((Collection<ASTNode>)unit.imports()).stream().mapToInt(ASTNode::getStartPosition).filter(n -> n >= 0).min().orElse(Integer.MAX_VALUE) &&
1580+
this.offset <= ((Collection<ASTNode>)unit.types()).stream().mapToInt(ASTNode::getStartPosition).filter(n -> n >= 0).min().orElse(Integer.MAX_VALUE)) {
1581+
this.requestor.accept(createKeywordProposal(Keywords.PACKAGE, completionContext.getTokenStart(), completionContext.getTokenEnd()));
1582+
}
1583+
if (CharOperation.prefixEquals(completionContext.getToken(), Keywords.IMPORT)
1584+
&& (unit.getPackage() == null
1585+
|| this.offset >= unit.getPackage().getStartPosition() + unit.getPackage().getLength())
1586+
&& (unit.types().isEmpty() || this.offset < ((ASTNode)unit.types().get(0)).getStartPosition())) {
1587+
if (unit.imports().isEmpty()) {
1588+
this.requestor.accept(createKeywordProposal(Keywords.IMPORT, -1, -1));
1589+
} else {
1590+
for (int i = unit.imports().size() - 1; i >= 0; i--) {
1591+
ImportDeclaration importDeclaration = (ImportDeclaration)unit.imports().get(i);
1592+
if (this.offset > importDeclaration.getStartPosition() + importDeclaration.getLength()) {
1593+
if ((importDeclaration.getFlags() & ASTNode.MALFORMED) == 0) {
1594+
this.requestor.accept(createKeywordProposal(Keywords.IMPORT, -1, -1));
1595+
}
1596+
break;
1597+
}
1598+
}
1599+
}
1600+
}
15791601
}
15801602
if (context instanceof MethodRef methodRef) {
15811603
JavadocMethodReferenceParseState state = JavadocMethodReferenceParseState.BEFORE_IDENTIFIER;
@@ -1879,9 +1901,7 @@ public void complete(org.eclipse.jdt.internal.compiler.env.ICompilationUnit sour
18791901
}
18801902

18811903
suggestModifierKeywords(existingModifiers);
1882-
if (!this.isFailedMatch(this.prefix.toCharArray(), Keywords.CLASS)) {
1883-
this.requestor.accept(createKeywordProposal(Keywords.CLASS, -1, -1));
1884-
}
1904+
suggestClassDeclarationLikeKeywords();
18851905
}
18861906
suggestDefaultCompletions = false;
18871907
}
@@ -2051,6 +2071,23 @@ public void complete(org.eclipse.jdt.internal.compiler.env.ICompilationUnit sour
20512071
}
20522072
}
20532073
}
2074+
2075+
private void suggestClassDeclarationLikeKeywords() {
2076+
if (!this.isFailedMatch(this.prefix.toCharArray(), Keywords.CLASS)) {
2077+
this.requestor.accept(createKeywordProposal(Keywords.CLASS, -1, -1));
2078+
}
2079+
if (!this.isFailedMatch(this.prefix.toCharArray(), Keywords.INTERFACE)) {
2080+
this.requestor.accept(createKeywordProposal(Keywords.INTERFACE, -1, -1));
2081+
}
2082+
if (!this.isFailedMatch(this.prefix.toCharArray(), Keywords.ENUM)) {
2083+
this.requestor.accept(createKeywordProposal(Keywords.ENUM, -1, -1));
2084+
}
2085+
if (this.unit.getAST().apiLevel() >= AST.JLS14) {
2086+
if (!this.isFailedMatch(this.prefix.toCharArray(), RestrictedIdentifiers.RECORD)) {
2087+
this.requestor.accept(createKeywordProposal(RestrictedIdentifiers.RECORD, -1, -1));
2088+
}
2089+
}
2090+
}
20542091

20552092
private void suggestUndeclaredVariableNames(ITypeBinding typeBinding, Set<String> alreadySuggested) {
20562093
ASTNode pastCursor = this.toComplete;
@@ -2981,7 +3018,22 @@ private void statementLikeKeywords() {
29813018
}
29823019
keywords.add(Keywords.SUPER);
29833020
keywords.add(Keywords.NEW);
2984-
3021+
3022+
{
3023+
// instanceof must be preceeded by an expression
3024+
int instanceofCursor = this.toComplete.getStartPosition();
3025+
while (instanceofCursor > 0 && Character.isWhitespace(this.textContent.charAt(instanceofCursor - 1))) {
3026+
instanceofCursor--;
3027+
}
3028+
ASTNode preceedingNode = NodeFinder.perform(this.unit, instanceofCursor, 0);
3029+
while (preceedingNode != null && !(preceedingNode instanceof Expression)) {
3030+
preceedingNode = preceedingNode.getParent();
3031+
}
3032+
if (preceedingNode != null) {
3033+
keywords.add(Keywords.INSTANCEOF);
3034+
}
3035+
}
3036+
29853037
if (!this.expectedTypes.getExpectedTypes().isEmpty()) {
29863038
keywords.add(Keywords.NULL);
29873039
}
@@ -3097,8 +3149,17 @@ private void suggestPackages(ASTNode context) {
30973149
context = context.getParent();
30983150
}
30993151
String prefix = context instanceof Name name ? name.toString() : this.prefix;
3100-
if (prefix != null && !prefix.isBlank()) {
3101-
this.nameEnvironment.findPackages(prefix.toCharArray(), this.nestedEngine);
3152+
if (prefix != null) {
3153+
prefix = prefix.replace(FAKE_IDENTIFIER, "");
3154+
if (!prefix.isBlank()) {
3155+
// IMO we should provide our own ISearchRequestor so that we don't have to mess with CompletionEngine internal state
3156+
// but this hack should hopefully fix the range
3157+
if (context instanceof Name name) {
3158+
this.nestedEngine.startPosition = name.getStartPosition();
3159+
this.nestedEngine.endPosition = name.getStartPosition() + name.getLength();
3160+
}
3161+
this.nameEnvironment.findPackages(prefix.toCharArray(), this.nestedEngine);
3162+
}
31023163
}
31033164
}
31043165

0 commit comments

Comments
 (0)