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:
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
|
||||
Reference in new issue
Block a user