Keep the running tool call outside its group
A run of adjacent calls is drawn as one collapsed card, which hid the one thing worth seeing without opening anything: the command the session is running right now. It is a row of its own while it runs and folds back into the run when it ends. Grouping stays a display decision, so the pieces a running call cuts a run into are keyed there. The first piece keeps the run's name -- that name is what survives a page of history landing in front of it -- and later pieces take their own first call's id behind it, since the call a run was named after can itself be the one running. The echo rig's /tools gap now runs between a call's start and its end rather than between one call and the next, which is where a real session's time goes and what makes the running state observable at all. Checked with ktfmtFormat, compileDebugKotlin, testDebugUnitTest (new ToolRowsTest) and lintDebug, cargo fmt/clippy/test, and on the emulator against the sandbox: "Called 2 tools" with the live Bash card beneath it.
This commit is contained in:
1 parent
b9b777acaf
commit
036eb375aa
5 files changed
+172
-45
No files matched your search
@@ -61,7 +61,9 @@ sealed class TranscriptRow {
|
||||
*
|
||||
* A tool row therefore keys on [TranscriptItem.ToolRun.runId] rather than on a sequence number,
|
||||
* and it is the *same* value whether the run is drawn as one card or as a group. Which value
|
||||
* that is belongs to the item ([TranscriptItem.key]), not to a `when` here.
|
||||
* that is belongs to the item ([TranscriptItem.key]) everywhere a row is one thing; where
|
||||
* [groupRuns] cuts a run into several rows it is the one deciding, and it says so by handing
|
||||
* each piece its key.
|
||||
*/
|
||||
abstract val key: Any
|
||||
|
||||
@@ -75,23 +77,15 @@ sealed class TranscriptRow {
|
||||
*/
|
||||
abstract val startSeq: Long
|
||||
|
||||
data class Single(val item: TranscriptItem) : TranscriptRow() {
|
||||
override val key: Any
|
||||
get() = item.key
|
||||
|
||||
data class Single(val item: TranscriptItem, override val key: Any = item.key) :
|
||||
TranscriptRow() {
|
||||
override val startSeq: Long
|
||||
get() = item.seq
|
||||
}
|
||||
|
||||
/** Two or more calls with nothing between them; drawn as one collapsed card. */
|
||||
data class Tools(val calls: List<TranscriptItem.ToolRun>) : TranscriptRow() {
|
||||
/** The run's own name, which every call in it already carries. */
|
||||
val id: String
|
||||
get() = calls.first().runId
|
||||
|
||||
override val key: Any
|
||||
get() = id
|
||||
|
||||
data class Tools(val calls: List<TranscriptItem.ToolRun>, override val key: String) :
|
||||
TranscriptRow() {
|
||||
override val startSeq: Long
|
||||
get() = calls.first().seq
|
||||
}
|
||||
@@ -102,6 +96,12 @@ sealed class TranscriptRow {
|
||||
*
|
||||
* A single call is left alone: "Called 1 tool" hides a card to say the same thing in more words,
|
||||
* and the run this exists for is the burst of five greps nobody wants to scroll past.
|
||||
*
|
||||
* A call that has not finished is left alone too, wherever in its run it sits. What the session is
|
||||
* doing *now* is the one thing worth seeing without opening anything, and grouping it hides the
|
||||
* running command behind a heading that counts it. The call rejoins its run when it ends, which is
|
||||
* the moment it stops being what is happening and becomes history -- and it is a row at the live
|
||||
* end of the transcript, so nothing above the reader moves when it does.
|
||||
*/
|
||||
fun groupToolRuns(items: List<TranscriptItem>): List<TranscriptRow> =
|
||||
DebugStats.timed("grouped tool runs") { groupRuns(items) }
|
||||
@@ -109,13 +109,24 @@ fun groupToolRuns(items: List<TranscriptItem>): List<TranscriptRow> =
|
||||
private fun groupRuns(items: List<TranscriptItem>): List<TranscriptRow> {
|
||||
val rows = mutableListOf<TranscriptRow>()
|
||||
var run = mutableListOf<TranscriptItem.ToolRun>()
|
||||
// The run being walked, and how many rows it has produced so far -- which is what decides the
|
||||
// keys below.
|
||||
var runId: String? = null
|
||||
var emitted = 0
|
||||
|
||||
fun flush() {
|
||||
when (run.size) {
|
||||
0 -> {}
|
||||
1 -> rows += TranscriptRow.Single(run.first())
|
||||
else -> rows += TranscriptRow.Tools(run.toList())
|
||||
}
|
||||
val first = run.firstOrNull() ?: return
|
||||
// The first row a run produces keeps the run's name, which is the name that survives a page
|
||||
// of history landing in front of it ([adoptRun]); losing it is the transcript stepping
|
||||
// under whoever is reading. The pieces a running call cuts off the back of the run have no
|
||||
// such name, so each takes its own first call's id, which is unique because a call id is.
|
||||
// The run's name goes in front of it because the two can otherwise be the same string: the
|
||||
// call a run was named after can itself be the one still running.
|
||||
val key = if (emitted == 0) first.runId else "${first.runId}/${first.id}"
|
||||
rows +=
|
||||
if (run.size == 1) TranscriptRow.Single(first, key)
|
||||
else TranscriptRow.Tools(run.toList(), key)
|
||||
emitted++
|
||||
run = mutableListOf()
|
||||
}
|
||||
|
||||
@@ -124,11 +135,20 @@ private fun groupRuns(items: List<TranscriptItem>): List<TranscriptRow> {
|
||||
// Adjacency is the same answer most of the time and a worse one at the edges: a call
|
||||
// arriving next to an existing run, or a page of history arriving in front of one, both
|
||||
// change which call is *first*.
|
||||
if (item is TranscriptItem.ToolRun && (run.isEmpty() || run.first().runId == item.runId)) {
|
||||
run += item
|
||||
} else {
|
||||
val call = item as? TranscriptItem.ToolRun
|
||||
if (call == null || call.runId != runId) {
|
||||
flush()
|
||||
if (item is TranscriptItem.ToolRun) run += item else rows += TranscriptRow.Single(item)
|
||||
runId = call?.runId
|
||||
emitted = 0
|
||||
}
|
||||
when {
|
||||
call == null -> rows += TranscriptRow.Single(item)
|
||||
call.done -> run += call
|
||||
else -> {
|
||||
flush()
|
||||
run += call
|
||||
flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
flush()
|
||||
|
||||
Reference in new issue
Block a user