ADFA-2593 | Fix initialization crash in BuildOutputFragment#866
ADFA-2593 | Fix initialization crash in BuildOutputFragment#866
Conversation
📝 Walkthrough
WalkthroughBuildOutputFragment now buffers log lines into an unbounded coroutine Channel and runs a background coroutine to batch and flush them to IDEEditor; IDEEditor gained awaitLayout, isReadyToAppend, and appendBatch helpers to safely coordinate UI appends. Changes
Sequence Diagram(s)sequenceDiagram
participant Producer as Log Producer\n(BuildOutputFragment)
participant Channel as logChannel
participant Processor as processLogs()\n(Background)
participant Editor as IDEEditor\n(UI/Main)
Producer->>Channel: appendOutput(line)
Channel-->>Processor: receive lines / drainTo(batch)
Processor->>Processor: accumulate batch
Processor->>Editor: flushToEditor(batch)
Editor->>Editor: awaitLayout(onForceVisible)
Editor->>Editor: appendBatch(batch)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt`:
- Around line 68-78: flushPendingOutputIfReady currently appends raw
unsavedLines causing missing newline normalization that appendOutput() enforces;
update flushPendingOutputIfReady (or extract a shared helper used by
appendOutput and flushPendingOutputIfReady) to normalize each line to end with a
newline before calling editor.append, e.g., reuse the same newline logic applied
in appendOutput for each item in unsavedLines, then clear unsavedLines and call
emptyStateViewModel.setEmpty(false) as before.
app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt (1)
39-76: Append can still run before layout; queue until ready.Line 47‑61 appends as soon as
editor != null. If output arrives between view creation and first layout, the same crash can still occur. Also,unsavedLinescan be mutated off the UI thread whileflushPendingOutputIfReady()iterates. Consider gatingappendOutput()onisReadyToAppendand synchronizingunsavedLines.🛠️ Proposed fix
fun appendOutput(output: String?) { - if (editor == null) { - unsavedLines.add(output) - return - } + val currentEditor = editor + if (currentEditor == null || !currentEditor.isReadyToAppend) { + synchronized(unsavedLines) { unsavedLines.add(output) } + currentEditor?.post { flushPendingOutputIfReady() } + return + } ThreadUtils.runOnUiThread { val message = if (output == null || output.endsWith("\n")) { output } else { "${output}\n" } - editor?.append(message).also { + currentEditor.append(message).also { emptyStateViewModel.setEmpty(false) } } } private fun flushPendingOutputIfReady() { editor?.run { - if (!isReadyToAppend || unsavedLines.isEmpty()) return - - unsavedLines.forEach { line -> - line?.let { - append(if (it.endsWith("\n")) it else "$it\n") - } - } - unsavedLines.clear() + if (!isReadyToAppend) return + val lines = + synchronized(unsavedLines) { + if (unsavedLines.isEmpty()) null + else unsavedLines.toList().also { unsavedLines.clear() } + } ?: return + + lines.forEach { line -> + line?.let { append(if (it.endsWith("\n")) it else "$it\n") } + } emptyStateViewModel.setEmpty(false) } }
89349fe to
cc33a83
Compare
|
Caution Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted. Error details |
7966702 to
03a22ba
Compare
7dd38e4 to
d3b88ea
Compare
02df537 to
ab33f3a
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt`:
- Around line 112-123: The flushToEditor coroutine can lose logs when
awaitLayout times out because appendBatch is skipped; update flushToEditor (the
function named flushToEditor) to detect whether append actually happened and
return a Boolean success flag instead of void: after awaitLayout/or its timeout,
check the editor readiness (use the existing readiness flag or isReadyToAppend
equivalent) before calling appendBatch, only call
emptyStateViewModel.setEmpty(false) on successful append, and return false on
timeout/no-append so callers can requeue; then update processLogs to re-enqueue
the batch (with optional short backoff) when flushToEditor returns false to
prevent data loss.
- Line 39: The logChannel currently uses Channel.UNLIMITED which can grow
unbounded; change its creation to a bounded channel (e.g., Channel(capacity) or
Channel(Channel.BUFFERED) with a defined onBufferOverflow policy like
DROP_OLDEST or DROP_LATEST) to provide backpressure and avoid OOM during heavy
logging; update any producers that call logChannel.trySend(...) to handle
dropped messages or backpressure (check send/trySend result) and keep the rest
of the UI consumer logic for reading from logChannel unchanged (refer to the
logChannel variable and places that call trySend/send).
app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt
Show resolved
Hide resolved
app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt
Show resolved
Hide resolved
… crash Ensure the editor is attached and laid out before flushing pending output to avoid ArrayIndexOutOfBoundsException.
- Implement Coroutine Channel to process logs off the main thread. - Add batching logic to reduce UI layout passes and prevent ANRs. - Add `awaitLayout` to ensure editor readiness, fixing ArrayIndexOutOfBoundsException.
3bd91ec to
c371b55
Compare
Description
Fixed a race condition causing an
ArrayIndexOutOfBoundsExceptionwhen opening the Build Output fragment with pending logs.The crash occurred because the code attempted to append text to the
IDEEditorbefore its internal layout components (specificallyLineBreakLayout) were fully initialized and measured.Changes:
isReadyToAppendto verify if the editor is attached to the window and laid out.postrunnable to ensure the view cycle is complete before modification.flushPendingOutputIfReadyusing Kotlin scope functions for better null safety.Details
Logic update to prevent the following crash reported in Sentry:
java.lang.ArrayIndexOutOfBoundsException: index = 0, length = 0 at io.github.rosemoe.sora.util.BlockIntList.setBefore changes
Screen.Recording.2026-01-20.at.3.53.57.PM.mov
After changes
Screen.Recording.2026-01-21.at.11.17.03.AM.mov
Ticket
ADFA-2593
Observation
The
isReadyToAppendcheck specifically looks forisLaidOutto guarantee that the editor knows its dimensions before calculating line breaks.