Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d8c32bd
fix: add global analysis lock for Kotlin analysis
itsaky Jun 19, 2026
dc471d3
tests: add test case
itsaky Jun 19, 2026
206c0c1
fix: address review findings (lifetime owner escape, write-lock race,…
itsaky-adfa Jun 23, 2026
f281d0e
feat(ADFA-4174): priority-aware preemptive analysis scheduler
itsaky-adfa Jun 23, 2026
c9349b0
fix: integrate cancel checker with ProgressManager
itsaky-adfa Jul 2, 2026
148144b
Merge branch 'stage' into fix/ADFA-4174
itsaky-adfa Jul 2, 2026
959e0f0
fix: allow same-priority pre-emption for code completion requests
itsaky-adfa Jul 2, 2026
b17aabd
feat: add push cancellation and thread registration to shared cancel …
itsaky-adfa Jul 3, 2026
fdb4e5e
fix: abort in-flight Kotlin analysis promptly on completion cancellation
itsaky-adfa Jul 3, 2026
9b3de6a
fix: coalesce Kotlin completion requests to a single in-flight analysis
itsaky-adfa Jul 3, 2026
e2f579b
Merge branch 'stage' into fix/ADFA-4174
itsaky-adfa Jul 3, 2026
4efbf83
Merge branch 'stage' into fix/ADFA-4174
itsaky-adfa Jul 8, 2026
6c68d93
fix: fix CodeRabbit comments
itsaky-adfa Jul 8, 2026
0054e2a
chore: simplify comments
itsaky-adfa Jul 8, 2026
a403efc
Merge branch 'stage' into fix/ADFA-4174
itsaky-adfa Jul 13, 2026
c035d6f
ADFA-4174: Rename AnalysisPriority.COMPLETION to INTERACTIVE
itsaky-adfa Jul 13, 2026
3b68867
ADFA-4174: Extract shared isAnalysisCancellation helper
itsaky-adfa Jul 13, 2026
78d1a3c
ADFA-4174: Run signature help at INTERACTIVE priority with cancellation
itsaky-adfa Jul 13, 2026
66da411
ADFA-4174: Migrate signature-help test fixture to priority-aware anal…
itsaky-adfa Jul 13, 2026
3616f37
fix: resolve review comments
itsaky-adfa Jul 14, 2026
9b6e926
Merge branch 'stage' into fix/ADFA-4174
itsaky-adfa Jul 14, 2026
c4e3b67
Merge branch 'stage' into fix/ADFA-4174
itsaky-adfa Jul 14, 2026
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
12 changes: 12 additions & 0 deletions .claude/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Local, machine-specific settings (not shared)
settings.local.json

# Claude Code worktrees
worktrees/

# Session/runtime artifacts
projects/
todos/
shell-snapshots/
statsig/
.credentials.json
5 changes: 5 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,16 @@ subprojects {
// setAccessible on java.lang.Class fields.
// - java.base/java.io, java.util: needed by Robolectric/Gradle worker
// reflection in the same test JVM.
// - java.base/java.util.concurrent: the embedded IntelliJ scheduler
// reflectively reads FutureTask.callable; without this its periodic
// thread dies, disabling the cancellation poll that makes the Kotlin
// Analysis API interruptible mid-`analyze`.
jvmArgs(
"--add-opens=java.base/java.lang=ALL-UNNAMED",
"--add-opens=java.base/java.lang.reflect=ALL-UNNAMED",
"--add-opens=java.base/java.io=ALL-UNNAMED",
"--add-opens=java.base/java.util=ALL-UNNAMED",
"--add-opens=java.base/java.util.concurrent=ALL-UNNAMED",
"--add-opens=jdk.unsupported/sun.misc=ALL-UNNAMED",
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.itsaky.androidide.lsp.debug.model.BreakpointDefinition
import com.itsaky.androidide.lsp.debug.model.BreakpointRequest
import com.itsaky.androidide.preferences.internal.EditorPreferences
import com.itsaky.androidide.progress.ICancelChecker
import com.itsaky.androidide.progress.ProgressManager
import io.github.rosemoe.sora.lang.Language
import io.github.rosemoe.sora.lang.completion.CompletionCancelledException
import io.github.rosemoe.sora.lang.completion.CompletionPublisher
Expand Down Expand Up @@ -67,11 +68,17 @@ abstract class IDELanguage : Language {
publisher: CompletionPublisher,
extraArguments: Bundle
) {
val completionThread = Thread.currentThread()
try {
val cancelChecker = CompletionCancelChecker(publisher)
Lookup.getDefault().update(ICancelChecker::class.java, cancelChecker)
// Bind the checker to this thread so cancelCompletion()'s ProgressManager.cancel(thread)
// routes here, letting the LSP's invokeOnCancel abort the running analysis mid-`analyze`
// immediately rather than only at coarse checkpoints.
ProgressManager.instance.register(completionThread, cancelChecker)
doComplete(content, position, publisher, cancelChecker, extraArguments)
} finally {
ProgressManager.instance.unregister(completionThread)
Lookup.getDefault().unregister(
ICancelChecker::class.java
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,18 @@ class EditorCompletionWindow(val editor: IDEEditor) : EditorAutoCompletion(edito
private var listView: ListView? = null
private val items: MutableList<CompletionItem> = mutableListOf()

/**
* A scheduled-but-not-yet-started completion request, kept so a newer keystroke can cancel it.
* See [requireCompletion].
*/
private var pendingCompletion: Runnable? = null

companion object {

private val log = LoggerFactory.getLogger(EditorCompletionWindow::class.java)

/** Quiet period for coalescing a keystroke burst: analysis runs only after typing pauses this long. */
private const val COMPLETION_DEBOUNCE_MS = 80L
}

init {
Expand Down Expand Up @@ -119,29 +128,54 @@ class EditorCompletionWindow(val editor: IDEEditor) : EditorAutoCompletion(edito
}

override fun cancelCompletion() {
// Drop any request that was scheduled but hasn't started yet.
pendingCompletion?.let { editor.handler.removeCallbacks(it) }
pendingCompletion = null
if (completionThread != null) {
ProgressManager.instance.cancel(completionThread)
}
super.cancelCompletion()
}

override fun requireCompletion() {
/** Whether completion may run now; hides the window when the cursor is selected or otherwise not applicable. */
private fun canStartCompletion(): Boolean {
if (cancelShowUp || !isEnabled || !editor.isAttachedToWindow) {
return
return false
}

val text = editor.text
if (text.cursor.isSelected || checkNoCompletion()) {
if (editor.text.cursor.isSelected || checkNoCompletion()) {
hide()
return
return false
}
return true
}

if (System.nanoTime() - requestTime < editor.props.cancelCompletionNs) {
requestTime = System.nanoTime()
/**
* Coalesces a keystroke burst into one completion for the latest cursor position, keeping at most one
* analysis in flight. This prevents the CompletionThread/allocation pile-up that saturated the heap and
* froze the editor during fast typing.
*/
override fun requireCompletion() {
if (!canStartCompletion()) {
return
}

// cancelCompletion() clears any in-flight and pending request, so we then schedule exactly one.
cancelCompletion()

val request = Runnable { startCompletion() }
pendingCompletion = request
editor.handler.postDelayed(request, COMPLETION_DEBOUNCE_MS)
}

/** Runs on the UI thread. */
private fun startCompletion() {
pendingCompletion = null

// Editor state may have changed during the debounce delay; re-check the guards.
if (!canStartCompletion()) {
return
}

requestTime = System.nanoTime()
currentSelection = -1

Expand Down
Loading
Loading