Thin the app's comments

The same pass the server had, on the Kotlin side: comments restating what
the code says are gone, and the ones recording a measurement, a constraint
or an incident are kept but cut to a few lines each. 6540 comment lines to
5674, and 920 lines off the app.

Two doc comments had drifted onto the item above the one they describe --
`contextAfter`'s onto `sessionWorking` in Events.kt, and `UsageMonitor`'s
equivalent on the server was fixed in the previous commit. Each is back on
its own item, which is the only non-comment line this diff moves.

The comments are reflowed to the column limit at their own indentation:
several were written wide, and ktfmt re-wrapped them into lines holding a
single orphan word. `/tmp` script, not kept -- ktfmt is idempotent over the
result, which is the check.

Left alone deliberately: this codebase's remaining comment density is high
because the comments carry things the code cannot say -- what a null means,
what a number was measured against, which bug a guard exists for. Of the
238 one-line doc comments in the app, five were pure restatement of the
name and were removed; the rest each say something the signature does not.

ktfmtFormat, compileDebugKotlin, lintDebug and testDebugUnitTest pass;
cargo test (127), clippy --all-targets and fmt still clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Opus 5 committed 2026-09-04 16:20:16 -04:00
1 parent 79682f03a7
commit edc39c7371
68 files changed
+2077 -2997

No files matched your search

@@ -50,15 +50,12 @@ fun codeStyle(): TextStyle =
/**
* [content] scanned off the main thread, then drawn.
*
* Measured on the emulator on 2026-09-04: [FileLines.of] takes **460ms** on a 1 MiB Rust file
* (28,660 lines) and 11ms on 32 kB. Called from a `remember` inside the composition, as it was
* first written, that is 460ms of frozen screen at the size the server is willing to send -- long
* enough that the accessibility tree cannot be read, which is what "the app has stopped" looks like
* from outside. So it runs on [Dispatchers.Default] and the spinner is what the reader sees
* meanwhile, in the place the file will appear.
* Measured on the emulator 2026-09-04: [FileLines.of] takes **460ms** on a 1 MiB Rust file (28,660
* lines) and 11ms on 32 kB. Called from a `remember` inside the composition, as it was first
* written, that is 460ms of frozen screen at the size the server is willing to send -- long enough
* that the accessibility tree cannot be read, which is what "the app has stopped" looks like.
*
* Keyed on the text and the language, so re-reading the same file does not rescan it and a file
* that changed does.
* Keyed on the text and the language, so re-reading the same file does not rescan it.
*/
@Composable
fun ScannedFile(content: String, language: Language?, modifier: Modifier = Modifier) {
@@ -76,39 +73,31 @@ fun ScannedFile(content: String, language: Language?, modifier: Modifier = Modif
* A file, one line per row, coloured by the same scanner that colours a reply's code fences.
*
* A `LazyColumn` of lines rather than one `Text`, because text layout is linear in the text: a
* twenty-thousand-line file in a single `Text` measures all of it to draw a screenful, and the
* scroll never recovers. The cost of the choice is that each row needs its own colours, which is
* what [FileLines] works out once and off this thread.
* twenty-thousand-line file in a single `Text` measures all of it to draw a screenful. The cost is
* that each row needs its own colours, which is what [FileLines] works out once and off this
* thread.
*
* Lines do not wrap. They share one horizontal scroll state, so the whole file moves sideways as a
* block and a long line does not silently become three -- which would put the gutter's numbers
* against the wrong text, the one thing a numbered listing must never do. Because nothing wraps, a
* logical line is one visual line and the two cannot drift.
* against the wrong text.
*
* **Every row is given the same content width**, and that is what makes the shared scroll state
* behave. `Modifier.horizontalScroll` is a node per row, and each one coerces the shared offset
* into *its own* range -- `content width - viewport` -- so with rows of their natural widths a
* short line's range is zero and it never moves at all while a long one beside it does. Each row
* also writes `maxValue` on the shared state as it measures, so how far the file could be dragged
* was decided by whichever row happened to measure last and changed as the list scrolled. Both
* disappear once every row is [FileLines.columns] wide: one range, one maximum, and the file moves
* as the block this comment always claimed it was. Reported by Iris on 2026-09-04 as "it seems to
* affect different rows differently", which is exactly what a per-row range looks like.
* short line's range is zero and it never moves while a long one beside it does. Each row also
* writes `maxValue` as it measures, so how far the file could be dragged was decided by whichever
* row measured last. Both disappear once every row is [FileLines.columns] wide. Reported by Iris on
* 2026-09-04 as "it seems to affect different rows differently", which is what a per-row range
* looks like.
*
* The stretch at the ends of the travel is **one** effect for the whole file, rendered on the box
* around the list rather than by each row. `horizontalScroll` makes its own per node otherwise, so
* only the line under the finger stretched and the rest of the file sat still beside it -- the same
* complaint as the offsets above, one layer further out. Handing every row the same effect and
* rendering it once is what makes the file bend as the block it scrolls as. Only possible because
* every row now has the same range: rows that disagreed about where the end was would disagree
* about when to stretch.
* around the list rather than by each row -- `horizontalScroll` makes its own per node otherwise,
* so only the line under the finger stretched. Only possible because every row now has the same
* range.
*
* The gutter is **beside** the scrolling box rather than inside its rows, which is what keeps the
* numbers out of both effects: they do not travel with the text and they do not bend with it. The
* rows leave a spacer where the numbers will go and [LineGutter] draws them there. Its width is
* measured from the digit count of the line count in the very style it is drawn in, so a nine-line
* file and a twelve-thousand-line file each get exactly what they need and nothing is nudged by
* hand.
* numbers out of both effects. The rows leave a spacer and [LineGutter] draws them there; its width
* is measured from the digit count of the line count in the style it is drawn in.
*
* Moving them out also takes them out of the [SelectionContainer], so selecting part of a file and
* copying it gives the code rather than the code with a number in front of every line.
@@ -139,8 +128,8 @@ fun FileViewer(lines: FileLines, modifier: Modifier = Modifier) {
softWrap = false,
// The scroll outside the width: the scrolling node's viewport is
// what the row has room for, and its content is the whole file's
// widest line. The shared effect is given to every row and
// rendered by none of them -- see the box above.
// widest line. The shared effect is given to every row and rendered
// by none of them -- see the box above.
modifier =
Modifier.horizontalScroll(scroll, overscroll).width(content),
)
@@ -157,24 +146,20 @@ fun FileViewer(lines: FileLines, modifier: Modifier = Modifier) {
* The line numbers, drawn beside the file rather than in it.
*
* They have to be outside the box the stretch is rendered on, or they bend with the text; and they
* have to stay exactly level with the lines they number, which is the one thing a numbered listing
* may never get wrong. Those two pull in opposite directions -- out of the list, but pinned to it.
* have to stay exactly level with the lines they number. Those two pull in opposite directions.
*
* A [SubcomposeLayout] is what settles it. *Which* numbers exist and *where* each goes both come
* from the list's own `layoutInfo`, read in the measure block -- and subcomposition happens during
* measurement, so this is not composing from a value it read a frame ago, it is composing from the
* answer the list has just produced. A `Column` translated by the scroll position could not do
* that: the translation would be a layout read and current while the set of numbers would be a
* composition behind it, so during a fling the numbers would slide against their lines.
* measurement, so this composes from the answer the list has just produced rather than one it read
* a frame ago. A `Column` translated by the scroll position could not: the translation would be
* current while the set of numbers was a composition behind, so during a fling the numbers would
* slide against their lines.
*
* The list is measured before this is -- they are siblings in a `Box` and it is declared first --
* and a scroll that remeasures the list on its own does so synchronously, ahead of the layout pass,
* which is the same reason a lazy list does not lag its own content.
* The list is measured before this is -- they are siblings in a `Box` and it is declared first.
*
* `onSurfaceVariant`, because a number is not part of the file: it is this app numbering it, and
* the text's own colour would put it in the same voice as the code. The background is painted
* because the stretch can carry the text sideways under this column, and a digit with a smear of
* code behind it reads as a rendering fault.
* `onSurfaceVariant`, because a number is not part of the file. The background is painted because
* the stretch can carry the text sideways under this column, and a digit with a smear of code
* behind it reads as a rendering fault.
*/
@Composable
private fun LineGutter(rows: LazyListState, width: Dp, style: TextStyle) {
@@ -205,10 +190,9 @@ private fun LineGutter(rows: LazyListState, width: Dp, style: TextStyle) {
/**
* How wide the widest line number is, measured rather than guessed.
*
* `9` repeated, because digits in a monospace face are all one width and the count's own digits
* would measure the same -- what matters is how many there are. Measuring in the style the numbers
* are drawn in is what makes this survive a font size, a density or a display scale nobody here
* chose.
* `9` repeated, because digits in a monospace face are all one width -- what matters is how many
* there are. Measuring in the style the numbers are drawn in is what makes this survive a font
* size, a density or a display scale nobody here chose.
*/
@Composable
fun gutterWidth(lineCount: Int, style: TextStyle): Dp {
@@ -225,15 +209,15 @@ fun gutterWidth(lineCount: Int, style: TextStyle): Dp {
/**
* How wide to make every row: the widest line in the file, in this style.
*
* One character measured rather than the line itself, because the face is monospace -- every
* advance is the same -- and measuring the actual widest line of a twenty-thousand-line file is
* work for an answer arithmetic already has. Sixty-four of them, divided, so the answer does not
* carry a whole character's worth of rounding.
* One character measured rather than the line itself, because the face is monospace and measuring
* the actual widest line of a twenty-thousand-line file is work for an answer arithmetic already
* has. Sixty-four of them, divided, so the answer does not carry a whole character's worth of
* rounding.
*
* Capped, because this becomes a fixed width in a layout and Compose cannot represent an arbitrary
* one: a minified file is a single line of a hundred thousand characters, and asking to lay that
* out as one row is a crash rather than a slow scroll. Past the cap the far end of such a line
* cannot be reached, which is the tolerable half of that trade.
* one: a minified file is a single line of a hundred thousand characters, and laying that out as
* one row is a crash rather than a slow scroll. Past the cap the far end of such a line cannot be
* reached, which is the tolerable half of that trade.
*/
@Composable
private fun contentWidth(columns: Int, style: TextStyle): Dp {
@@ -252,9 +236,7 @@ private fun contentWidth(columns: Int, style: TextStyle): Dp {
private const val MAX_CONTENT_PX = 100_000f
/**
* The space between the numbers and the code.
*
* A gap, not an alignment: the two are already aligned by the row, and this is only so the digits
* and the first character of the line are not touching.
* The space between the numbers and the code. A gap, not an alignment: the two are already aligned
* by the row, and this is only so the digits and the first character are not touching.
*/
val GUTTER_GAP = 8.dp