Skip to content
This repository was archived by the owner on Oct 18, 2024. It is now read-only.

Commit bdd119d

Browse files
committed
fix: completion becomes unresponsive while completing class names inside an anonymous class
This bug is caused when the user requests completions for class names inside an anonymous class (`new View.OnClickListener() { ... } ` for example). The completion provider succeeds in finding the completion candidates. However, for creating CompletionItem instance for classes, it needs to know if the class that is being suggested is a nested class or not. This is checked by `IJavaCompletionProvider.findTopLevelDeclaration()` which however, fails the check the nullability of the parent elements while traversing. This results in an infinite loop causing the `SynchronizedTask` to hold on to the `CompileTask` instance which further prevents any compilation related tasks. As the `CompileTask` is held due to the infinite loop, the Java completion provider simply returns empty results on further completion requests.
1 parent 66b7f57 commit bdd119d

File tree

4 files changed

+22
-12
lines changed

4 files changed

+22
-12
lines changed
367 Bytes
Binary file not shown.

lsp/java/src/main/java/com/itsaky/androidide/lsp/java/compiler/SynchronizedTask.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,15 @@ void setTask(CompileTask task) {
118118
this.task = task;
119119
}
120120

121-
public synchronized boolean isCompiling() {
122-
return isCompiling || semaphore.hasQueuedThreads();
121+
public synchronized boolean isBusy() {
122+
return isCompiling || semaphore.availablePermits() == 0;
123+
}
124+
125+
/**
126+
* <b>FOR INTERNAL USE ONLY!</b>
127+
*/
128+
public void logStats() {
129+
LOG.warn("[SynchronizedTask] isCompiling =", isCompiling);
130+
LOG.warn("[SynchronizedTask] queuedLength =", semaphore.getQueueLength());
123131
}
124132
}

lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/CompletionProvider.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,10 @@ public boolean canComplete(Path file) {
9696
@NonNull
9797
@Override
9898
public CompletionResult complete(@NonNull CompletionParams params) {
99-
if (compiler.getSynchronizedTask().isCompiling()) {
99+
final var synchronizedTask = compiler.getSynchronizedTask();
100+
if (synchronizedTask.isBusy()) {
101+
LOG.error("Cannot complete, a compilation task is already in progress");
102+
synchronizedTask.logStats();
100103
return CompletionResult.EMPTY;
101104
}
102105

lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/completion/IJavaCompletionProvider.kt

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ import com.itsaky.androidide.lsp.models.MatchLevel
4343
import com.itsaky.androidide.lsp.models.MethodCompletionData
4444
import com.itsaky.androidide.progress.ProgressManager.Companion.abortIfCancelled
4545
import com.itsaky.androidide.utils.ILogger
46-
import openjdk.source.tree.Tree
47-
import openjdk.source.util.TreePath
4846
import java.nio.file.Path
4947
import jdkx.lang.model.element.Element
5048
import jdkx.lang.model.element.ElementKind.ANNOTATION_TYPE
@@ -67,6 +65,8 @@ import jdkx.lang.model.element.ElementKind.TYPE_PARAMETER
6765
import jdkx.lang.model.element.ExecutableElement
6866
import jdkx.lang.model.element.TypeElement
6967
import jdkx.lang.model.element.VariableElement
68+
import openjdk.source.tree.Tree
69+
import openjdk.source.util.TreePath
7070

7171
/**
7272
* Completion provider for Java source code.
@@ -83,7 +83,6 @@ abstract class IJavaCompletionProvider(
8383
protected lateinit var filePackage: String
8484
protected lateinit var fileImports: Set<String>
8585

86-
@Suppress("Since15")
8786
open fun complete(
8887
task: CompileTask,
8988
path: TreePath,
@@ -102,11 +101,11 @@ abstract class IJavaCompletionProvider(
102101
* Provide completions with the given data.
103102
*
104103
* @param task The compilation task. Subclasses are expected to use this compile task instead of
105-
* starting another compilation process.
104+
* starting another compilation process.
106105
* @param path The [TreePath] defining the [Tree] at the current position.
107106
* @param partial The partial identifier.
108107
* @param endsWithParen `true` if the statement at cursor ends with a parenthesis. `false`
109-
* otherwise.
108+
* otherwise.
110109
*/
111110
protected abstract fun doComplete(
112111
task: CompileTask,
@@ -337,13 +336,13 @@ abstract class IJavaCompletionProvider(
337336
val parameterTypes = Array(element.parameters.size) { "" }
338337
val erasedParameterTypes = Array(parameterTypes.size) { "" }
339338
val plusOverloads = overloads - 1
340-
339+
341340
for (i in element.parameters.indices) {
342341
val p = element.parameters[i].asType()
343342
parameterTypes[i] = p.toString()
344343
erasedParameterTypes[i] = types.erasure(p).toString()
345344
}
346-
345+
347346
return MethodCompletionData(
348347
element.simpleName.toString(),
349348
getClassCompletionData(type),
@@ -373,11 +372,11 @@ abstract class IJavaCompletionProvider(
373372

374373
var element: TypeElement? = this
375374
while (true) {
376-
if (element?.enclosingElement?.kind == PACKAGE) {
375+
if (element == null || element.enclosingElement?.kind == PACKAGE) {
377376
break
378377
}
379378

380-
element = element?.enclosingElement as? TypeElement
379+
element = element.enclosingElement as? TypeElement
381380
}
382381

383382
return element!!

0 commit comments

Comments
 (0)