Skip to content

Commit 0054e2a

Browse files
committed
chore: simplify comments
Signed-off-by: Akash Yadav <akashyadav@appdevforall.org>
1 parent 6c68d93 commit 0054e2a

16 files changed

Lines changed: 126 additions & 188 deletions

File tree

‎build.gradle.kts‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,9 @@ subprojects {
9898
// - java.base/java.io, java.util: needed by Robolectric/Gradle worker
9999
// reflection in the same test JVM.
100100
// - java.base/java.util.concurrent: the embedded IntelliJ scheduler
101-
// (BoundedTaskExecutor.info -> AppDelayQueue "Periodic tasks thread")
102-
// reflectively reads FutureTask.callable. Without this the periodic
103-
// thread dies, disabling CoreProgressManager's cancellation poll that
104-
// makes the Kotlin Analysis API interruptible mid-`analyze`.
101+
// reflectively reads FutureTask.callable; without this its periodic
102+
// thread dies, disabling the cancellation poll that makes the Kotlin
103+
// Analysis API interruptible mid-`analyze`.
105104
jvmArgs(
106105
"--add-opens=java.base/java.lang=ALL-UNNAMED",
107106
"--add-opens=java.base/java.lang.reflect=ALL-UNNAMED",

‎editor/src/main/java/com/itsaky/androidide/editor/language/IDELanguage.kt‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,9 @@ abstract class IDELanguage : Language {
7272
try {
7373
val cancelChecker = CompletionCancelChecker(publisher)
7474
Lookup.getDefault().update(ICancelChecker::class.java, cancelChecker)
75-
// Bind the checker to this completion thread. EditorCompletionWindow.cancelCompletion()
76-
// cancels the (old) thread via ProgressManager.cancel(thread); routing that to *this*
77-
// checker's cancel() lets the LSP's invokeOnCancel listener abort the running analysis
78-
// mid-`analyze` immediately, instead of only at coarse checkpoints.
75+
// Bind the checker to this thread so cancelCompletion()'s ProgressManager.cancel(thread)
76+
// routes here, letting the LSP's invokeOnCancel abort the running analysis mid-`analyze`
77+
// immediately rather than only at coarse checkpoints.
7978
ProgressManager.instance.register(completionThread, cancelChecker)
8079
doComplete(content, position, publisher, cancelChecker, extraArguments)
8180
} finally {

‎editor/src/main/java/com/itsaky/androidide/editor/ui/EditorCompletionWindow.kt‎

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,7 @@ class EditorCompletionWindow(val editor: IDEEditor) : EditorAutoCompletion(edito
5151

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

54-
/**
55-
* Quiet period used to coalesce a burst of keystrokes into a single completion request. Rapid
56-
* typing (re)schedules the start; only after the user pauses this long does one analysis run,
57-
* for the latest cursor position. Keeps at most one completion in flight.
58-
*/
54+
/** Quiet period for coalescing a keystroke burst: analysis runs only after typing pauses this long. */
5955
private const val COMPLETION_DEBOUNCE_MS = 80L
6056
}
6157

@@ -141,10 +137,7 @@ class EditorCompletionWindow(val editor: IDEEditor) : EditorAutoCompletion(edito
141137
super.cancelCompletion()
142138
}
143139

144-
/**
145-
* Whether a completion may be shown for the current editor state. Hides the window (matching the
146-
* prior inline behaviour) when the cursor is selected or completion is otherwise not applicable.
147-
*/
140+
/** Whether completion may run now; hides the window when the cursor is selected or otherwise not applicable. */
148141
private fun canStartCompletion(): Boolean {
149142
if (cancelShowUp || !isEnabled || !editor.isAttachedToWindow) {
150143
return false
@@ -156,29 +149,29 @@ class EditorCompletionWindow(val editor: IDEEditor) : EditorAutoCompletion(edito
156149
return true
157150
}
158151

152+
/**
153+
* Coalesces a keystroke burst into one completion for the latest cursor position, keeping at most one
154+
* analysis in flight. This prevents the CompletionThread/allocation pile-up that saturated the heap and
155+
* froze the editor during fast typing.
156+
*/
159157
override fun requireCompletion() {
160158
if (!canStartCompletion()) {
161159
return
162160
}
163161

164-
// Coalesce a burst of keystrokes into a single completion. Cancel the in-flight completion and
165-
// any pending (not-yet-started) one, then (re)schedule one start after a short quiet period.
166-
// This guarantees at most one completion analysis in flight and that only the latest cursor
167-
// position is computed — preventing the CompletionThread/allocation pile-up that saturated the
168-
// heap and froze the editor during fast typing. cancelCompletion() clears any pending request,
169-
// so we always schedule exactly one.
162+
// cancelCompletion() clears any in-flight and pending request, so we then schedule exactly one.
170163
cancelCompletion()
171164

172165
val request = Runnable { startCompletion() }
173166
pendingCompletion = request
174167
editor.handler.postDelayed(request, COMPLETION_DEBOUNCE_MS)
175168
}
176169

177-
/** Starts a single completion for the current cursor position. Runs on the UI thread. */
170+
/** Runs on the UI thread. */
178171
private fun startCompletion() {
179172
pendingCompletion = null
180173

181-
// The editor state may have changed during the debounce delay; re-check the guards.
174+
// Editor state may have changed during the debounce delay; re-check the guards.
182175
if (!canStartCompletion()) {
183176
return
184177
}

‎lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/compiler/CompilationEnvironment.kt‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,7 @@ internal class CompilationEnvironment(
186186
languageClient?.publishDiagnostics(result)
187187
}
188188
} catch (e: AnalysisPreemptedException) {
189-
// A higher-priority analysis (completion) preempted this diagnostics run.
190-
// Re-schedule so diagnostics still run once the higher-priority work finishes.
189+
// Preempted by completion; re-schedule so diagnostics still run once it finishes.
191190
logger.debug("diagnostics for {} preempted; rescheduling", path)
192191
fileAnalyzer.schedule(path)
193192
}

‎lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/compiler/index/SourceFileIndexer.kt‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,9 @@ internal suspend fun indexSourceFile(
7676
symbolsIndex: JvmSymbolIndex,
7777
cancelChecker: ICancelChecker,
7878
) {
79-
// Indexing runs at the lowest (INDEXING) priority: it yields to both completion and diagnostics.
80-
// Wrapping the checker lets the scheduler preempt an in-progress index pass; the preemption
81-
// surfaces as AnalysisPreemptedException at the abortIfCancelled() checkpoints below, which the
82-
// IndexWorker catches to re-queue the file.
79+
// Indexing runs at the lowest priority, yielding to completion and diagnostics. Wrapping the checker
80+
// lets the scheduler preempt an in-progress pass; the preemption surfaces as AnalysisPreemptedException
81+
// at the abortIfCancelled() checkpoints below, which IndexWorker catches to re-queue the file.
8382
val checker = cancelChecker as? ScheduledCancelChecker ?: ScheduledCancelChecker(cancelChecker)
8483

8584
// Defensive backstop: this runs on the debounced/async index scope, so a disposal path that

‎lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/compiler/modules/AnalysisScheduler.kt‎

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,11 @@ import kotlin.concurrent.withLock
1515
* Order: [INDEXING] < [DIAGNOSTICS] < [COMPLETION] — interactive completion beats background
1616
* diagnostics, which beats bulk indexing.
1717
*
18-
* [supersedesSamePriority] additionally lets a *newer* request preempt an in-flight request of the
19-
* **same** priority. This is enabled only for [COMPLETION]: when the user types fast, several
20-
* completion requests fire in a row and the in-flight one is computing results for a now-stale cursor
21-
* position, so the newer request cancels it and the fresh position is analysed immediately. A
22-
* superseded completion is simply *discarded* — nothing reschedules it (see
23-
* `KotlinCompletions.codeComplete`). It is intentionally off for [DIAGNOSTICS] and [INDEXING], whose
24-
* preempted work is re-queued rather than dropped; same-priority preemption there would livelock, as
25-
* two contenders would endlessly re-queue and re-preempt each other.
18+
* [supersedesSamePriority] additionally lets a *newer* request preempt an in-flight one of the
19+
* **same** priority. On for [COMPLETION] only: rapid typing makes the in-flight completion stale, so
20+
* the newer one cancels it and the superseded work is *discarded* (nothing reschedules it). Off for
21+
* [DIAGNOSTICS]/[INDEXING], whose preempted work is re-queued — there same-priority preemption would
22+
* livelock, two contenders endlessly re-queuing and re-preempting each other.
2623
*/
2724
internal enum class AnalysisPriority(val supersedesSamePriority: Boolean) {
2825
INDEXING(supersedesSamePriority = false),
@@ -62,7 +59,7 @@ internal class ScheduledCancelChecker(
6259
/** Marks this analysis as preempted; the next [abortIfCancelled] will throw. */
6360
fun preempt() {
6461
preempted = true
65-
// Push: preemption is a cancellation too, so notify [invokeOnCancel] listeners immediately.
62+
// Preemption is a cancellation too: fire invokeOnCancel listeners now, don't wait for a poll.
6663
onCancelListeners.forEach { it() }
6764
onCancelListeners.clear()
6865
}
@@ -131,11 +128,10 @@ internal object AnalysisScheduler {
131128
* priority is [AnalysisPriority.supersedesSamePriority] — [onPreempt] of *that* holder is invoked so
132129
* it yields; [onPreempt] passed here is stored and used if this acquisition is later preempted.
133130
*
134-
* [cancelChecker] is *this* requester's checker: while waiting for the lock, the wait is re-checked on
135-
* a short timer, and if the requester has been cancelled — e.g. the editor superseded
136-
* this completion — [acquire] throws instead of parking until the lock frees. This is what stops
137-
* superseded completions from piling up holding heavy state (KtFile copies, symbol lists) while they
138-
* wait, which on-device saturated the heap and triggered multi-second GC stalls.
131+
* [cancelChecker] is *this* requester's checker: a queued requester re-checks it on a short timer and
132+
* [acquire] throws (rather than park until the lock frees) once cancelled — e.g. the editor superseded
133+
* this completion. This stops superseded completions from piling up holding heavy state (KtFile copies,
134+
* symbol lists), which on-device saturated the heap and triggered multi-second GC stalls.
139135
*/
140136
fun acquire(priority: AnalysisPriority, cancelChecker: ICancelChecker, onPreempt: () -> Unit) {
141137
mutex.withLock {

‎lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/compiler/modules/AnalysisThreadContext.java‎

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,11 @@
66
import org.jetbrains.kotlin.com.intellij.openapi.application.AccessToken;
77

88
/**
9-
* Bridge to the embeddable IntelliJ {@link ThreadContext} coroutine-context API.
9+
* Java bridge to the embeddable IntelliJ {@link ThreadContext} coroutine-context API.
1010
*
11-
* <p>{@code currentThreadContext}/{@code installThreadContext} live in a Kotlin file facade whose
12-
* metadata the Kotlin compiler cannot resolve against from this module (they surface as
13-
* "unresolved reference" from Kotlin). At the bytecode level, though, they are plain
14-
* {@code public static} methods, so Java can call them directly.
15-
*
16-
* <p>This is used by {@code withAnalysisLock} to install a cancellable {@link Job} into the analysis
17-
* thread's context: the embeddable {@code CoreProgressManager.checkCanceled()} routes through
18-
* {@code Cancellation.checkCancelled()}, which throws as soon as that Job is cancelled, aborting the
19-
* running analysis mid-{@code analyze}.
11+
* <p>Exists in Java because {@code currentThreadContext}/{@code installThreadContext} live in a
12+
* Kotlin file-facade whose metadata this module's Kotlin compiler cannot resolve ("unresolved
13+
* reference"), yet at the bytecode level they are plain {@code public static} methods Java can call.
2014
*/
2115
public final class AnalysisThreadContext {
2216

@@ -25,10 +19,12 @@ private AnalysisThreadContext() {
2519

2620
/**
2721
* Installs {@code job} into the current thread's IntelliJ coroutine context (preserving any
28-
* context already present) and returns a token that restores the previous context when closed.
22+
* existing context) and returns a token that restores the previous context when closed. Cancelling
23+
* {@code job} then aborts the running analysis mid-{@code analyze}, since the embeddable
24+
* {@code CoreProgressManager.checkCanceled()} throws once the installed Job is cancelled.
2925
*
30-
* <p>Public because it is referenced from the {@code internal inline} {@code withAnalysisLock};
31-
* an inline function may only reference declarations at least as accessible as itself.
26+
* <p>Public (not package-private) because the {@code internal inline} {@code withAnalysisLock}
27+
* references it: an inline function may only reference declarations at least as accessible as itself.
3228
*/
3329
public static AccessToken installJob(Job job) {
3430
CoroutineContext context = ThreadContext.currentThreadContext().plus(job);

‎lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/compiler/modules/CancelCheckerProgressIndicator.kt‎

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,15 @@ import org.jetbrains.kotlin.com.intellij.openapi.progress.ProcessCanceledExcepti
55
import org.jetbrains.kotlin.com.intellij.openapi.progress.util.AbstractProgressIndicatorBase
66

77
/**
8-
* A [com.intellij.openapi.progress.ProgressIndicator] whose cancellation state is driven by an
9-
* [ICancelChecker]. Installing it as the analysis thread's indicator (via
10-
* [withAnalysisLock]) is what makes the Kotlin Analysis API actually interruptible *mid*-`analyze`.
8+
* A progress indicator whose cancellation is driven by an [ICancelChecker]. Installing it as the
9+
* analysis thread's indicator (via [withAnalysisLock]) makes the Kotlin Analysis API interruptible
10+
* *mid*-`analyze`: the embeddable `CoreProgressManager.checkCanceled()` (called densely throughout
11+
* FIR resolution) rethrows this indicator's [checkCanceled], so a preemption or cancellation aborts
12+
* at the compiler's next internal checkpoint, not only at the coarse [ICancelChecker.abortIfCancelled].
1113
*
12-
* The embeddable analysis API ships the full IntelliJ `CoreProgressManager`, whose
13-
* `ProgressManager.checkCanceled()` (called densely throughout FIR resolution) re-fetches the
14-
* current thread's indicator and rethrows its [checkCanceled]. By bridging that to [checker], a
15-
* preemption or ordinary cancellation aborts the running analysis at the compiler's next internal
16-
* checkpoint instead of only at the coarse LSP-level [ICancelChecker.abortIfCancelled] checks.
17-
*
18-
* This extends [AbstractProgressIndicatorBase] rather than `EmptyProgressIndicator` on purpose:
19-
* the base is a *non-standard* indicator, so `CoreProgressManager` runs a background task that
20-
* polls [checkCanceled] every ~10ms. That poll flips the manager's internal "should check
21-
* cancelled" flag to active once [checker] reports cancellation, which is what arms the in-`analyze`
22-
* checks. [cancel] (invoked synchronously when this analysis is preempted) flips the same flag
23-
* immediately, so preemption does not have to wait for the poll.
14+
* Extends [AbstractProgressIndicatorBase] (a *non-standard* indicator) on purpose: `CoreProgressManager`
15+
* then polls [checkCanceled] every ~10ms, arming its internal "check cancelled" flag once [checker]
16+
* reports cancellation. `cancel()` flips that flag immediately so preemption need not wait for the poll.
2417
*/
2518
internal class CancelCheckerProgressIndicator(
2619
private val checker: ICancelChecker,

0 commit comments

Comments
 (0)