eb7b738c1707e25938cd01b00a07eaa49ed85254
2
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6caa82597e |
fix dangling tail anchors in reflow on partially-filled lines (T-92)
test / unit + widget + golden + a11y (push) Failing after 29s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 1m0s
`_LineReflow._addPart`'s post-loop block reparents anchors past the
source line's trimmed content onto whatever `_builder._result` was
active at that moment. When no further content lands in the builder
(non-wrapped lines, or the last logical line of a wrapped run),
`finish()` was emitting only when `_builder.isNotEmpty` — leaving
the empty result line with the reparented anchor unappended. The
anchor then pointed to a `BufferLine` that the reflow output never
included, `lines.replaceWith(reflowResult)` discarded it, and
`CellAnchor.attached` returned false. The selection controller's
`extent.attached` null-check then dropped the selection silently
on resize.
The fix adds a `_LineBuilder.hasAnchors` getter and uses it in
`finish()` so the builder line is also emitted when it's carrying
an anchor — even when otherwise empty. Trade-off: an extra trailing
line in the reflow output when (and only when) a tail anchor would
have dangled. `Buffer.resize` already pads the result to `newHeight`
afterward, so for the common case (resize fits inside view height)
the total ring length is unchanged; only when the result already
meets / exceeds `newHeight` does the buffer grow by one. Acceptable
in exchange for selections surviving a width change.
User-visible trigger paths:
- `SelectAllTextIntent` (Ctrl+A) creates an end anchor at
`x = viewWidth` on the last buffer line — exactly the past-
trimmed-length position. Resizing narrower while the selection
was active dropped it.
- Mouse drag selections past the end of a partially-filled line
hit the same shape.
Tests:
- The pre-existing `reflow anchors on the source line tail (past
trimmedLength) get reparented` test was originally written to
document the buggy behaviour ("anchor moves off the source onto
a dangling builder line"). Updated to assert the post-fix
contract: `out.contains(tail.line)` is true.
- New `SelectAllTextIntent-shaped end anchor survives shrink`
regression test that mirrors the actual production trigger
(anchor at `x = viewWidth` on a partially-filled line, narrower
reflow).
reflow.dart 71/71 → 72/72 (the new getter is a one-liner). Project
coverage 54.62% unchanged within rounding.
Co-Authored-By: Claude <noreply@anthropic.com>
|
||
|
|
3196c4957f |
test sweep: cover core/(root) — cell, cursor, charset, tabs, reflow (T-91)
Adds test/terminal/core/core_test.dart — 36 unit tests across the small standalone files that sit directly under `lib/src/terminal/src/core/*.dart`: - CellData (constructor + empty + getHash + toString), - CursorStyle (default ctor, every set/unset attr getter pair, all three colour-mode setters per channel, reset, the .empty singleton) + CursorPosition, - Charset (translate, designate/use, save/restore, asciiTranslator, decSpecGraphicsTranslator with in-table, out-of-table, and high- codepoint paths), - TabStops (default 8-column grid, find with empty-range / out-of- bounds / no-stop-in-range cases, setAt/clearAt/clearAll/reset), - reflow (empty input, single-line passthrough, grow, shrink-with- split, wrapped-run continuation, wide-char boundary on the new width, inner wide-char clamp during _addPart, anchor reparent on the main path, anchor reparent past trimmedLength). Two source-side cleanups folded in: - `CursorStyle.isItalis` was a defined-but-never-called getter with a typo. No external callers reference it; renamed to `isItalic` in the same change as the test that exercises it. - `_LineBuilder.isEmpty` in reflow.dart was dead — the only callers use `isNotEmpty` or check `_lines.isNotEmpty` separately. Removed. Coverage delta: - cell.dart: 3/7 → 7/7. - charset.dart: 12/25 → 25/25. - cursor.dart: 2/62 → 62/62. - tabs.dart: 0/23 → 23/23. - reflow.dart: 24/72 → 71/71 (file shrank by one line after the isEmpty getter removal). - Total project: 52.72% → 54.62%; coverage_floor bumped 52 → 54. Note for follow-up (not blocking): the post-loop "anchor.x >= to" branch in reflow's `_addPart` reparents anchors past trimmedLength onto whatever builder line is active at that moment. If no subsequent content is added (no wrapped continuations after the last shrink iteration), that builder line is never emitted by `finish()` and the anchor lands on a dangling reference. The path is exercised by the new test, but the contract it implements is arguably broken — anchors that should follow the source content end up off the visible buffer. Worth a separate ticket if real terminals trip it. Co-Authored-By: Claude <noreply@anthropic.com> |