Preserve raw include diagnostic provenance and derive bands from the existing shaped editor. Retain deferred tooltip paragraphs through rendering. Include source, regression, failure and native-trial evidence; corrected native acceptance remains pending.
10 KiB
Diagnostic editor geometry seam — independent critique
Read-only review by Codex-9e8r6t /root/publication_review, 2026-09-13. No dependency/source edits, builds, GUI, or network. Recommendation: first test the single read-only Content accessor in a private, pinned iced_widget patch, with the actual editor and a permanent delegating wrapper. A source-only copy is feasible but has a larger behavioral/type integration surface than the proposed accessor. This is an experiment recommendation, not acceptance of a dependency fork or diagnostic UX.
Concrete correction and boundaries
Draw-only hover still needs redraw scheduling. Pinned iced_widget text_editor.rs:906–935 requests redraw for a changed widget status; movement between two annotated words inside the same focused/hovered editor need not change that status. An annotation wrapper that merely delegates events can therefore retain the last tooltip until another redraw, including an incidental caret blink. Observe CursorMoved/CursorLeft and request redraw without capturing the event, publishing an application action, or changing Content. Hide based on the current cursor/window/modal admission. Test pointer A→B→blank→outside with an unfocused editor and no blink-driven redraw. Parent acknowledged this correction.
The accessor itself is a reasonable narrow seam: with_editor<T>(&self, f: impl FnOnce(&R::Editor) -> T) -> T reads the existing associated editor while its internal RefCell borrow is held. Compute owned bands inside that closure and release the borrow before calling child methods. No mutation, cloned Content, temporary selection, or alternate buffer is necessary. Geometry requires the concrete graphics Editor; a generic core Editor bound alone does not expose arbitrary ranges.
Read after the child draw: text_editor.rs:952–975 applies its highlighter, then :987–1016 uses the padded text bounds and passes the actual editor to fill_editor. A wrapper needs the same explicitly configured padding, rather than inferring text origin from outer bounds. Parent/child must share that configuration. The child's draw ignores its _viewport; wrapper annotations must independently intersect text bounds with its current ancestor viewport and use the normal renderer layer/transform protocol. A geometrically correct band outside a modal's permitted surface must still be suppressed by parent admission.
A tooltip drawn in that same draw pass from the current clipped bands avoids stale hover caches. It must remain in the intended clip, use bounded wrapping/placement, and not create a new overlay/input surface. If later requirements demand a tooltip outside the editor clip, do not quietly reuse this reasoning: overlay ordering, current geometry availability and event routing need a separate small design. Draw cannot emit application messages or request a later layout through Shell; do not derive retained tooltip state by pretending those phases are interchangeable.
Alternatives and actual integration cost
| Candidate | Assessment |
|---|---|
| Whole pinned iced_widget source, one Content accessor addition | More vendored bytes, but stock Content/Binding/Catalog identities and existing widget implementation remain continuous. The most conservative behavioral experiment for the already tested source editor. Freeze exact upstream files and a mechanically reviewable accessor diff; no registry-cache mutation. Production adoption also needs source capture, dependency/lock and notice provenance that identify the patched bytes. |
| Copy only text_editor.rs into a locally owned module/small crate | Feasible: the pinned file is 48,281 bytes / 1,526 lines and has no private sibling widget dependency. Most imports use public core APIs; line 626's iced_renderer::core return type can use the same public layout type. Optional highlighter support and the advanced class method need deliberate feature handling. A small crate could depend directly on the pinned iced_widget and reexport its public core/Theme/Renderer; it need not copy all widgets or introduce another renderer version. Preserve verified upstream MIT copyright/license and original-file hash. |
| Renderer interception | fill_editor supplies exact editor/origin/clip (iced_core text.rs:358), but the child Widget and its Content are parameterized by the renderer type. Matching associated Editor types does not make Content interchangeable with Content. A global adapter would require broader renderer/runtime trait plumbing; a temporary borrowing proxy cannot simply be passed to the existing child. No narrower safe drop-in adapter is established by these APIs. |
| Duplicate Editor/Paragraph or selection-based measurement | Reject for this task: duplicates layout authority or mutates the user's editor state, respectively. Existing selection regressions make the latter particularly unsuitable. |
The source-only copy is not just a smaller accessor patch: its Content, Binding, KeyPress, Status, Style and Catalog are new nominal types. Its private Tree State also differs. App.source_editor and its reconstruction points, source_binding, rename editor reuse, departure-selection helpers, and graphics-backed source editor tests would need consistent migration. Action/Cursor and other core reexports can retain their original identity. Unrelated form/hosted/rename-details editors can remain stock; no whole-application conversion is intrinsically required. Never switch between the two widget implementations in a live tree or convert by Content::with_text, which would discard selection/scroll state. The local copy also assumes maintenance of clipboard, IME, focus, gesture and platform-key code even when unchanged initially.
Thus module copying is a viable fallback if maintaining a whole-crate patch proves materially worse, not a reason to rewrite tested editor interactions now. Parent favors the pinned one-accessor experiment; this review agrees with that bounded choice. Avoid extending the patch beyond read-only access before the experiment resolves geometry.
Bounded experiment and falsifiers
Use the existing real graphics-backed editor/highlighter and one retained child Tree. A recording renderer should independently observe actual fill_editor origin/clip/buffer and annotation primitives. Compare covered cluster identities/visible advances, not the same rectangle helper against itself. Include an intentionally wrong padding or double-scroll offset as an in-process negative control, proving the evaluator detects misalignment.
- Source and frame authority: with a stationary pointer, edit the diagnosed token, switch to another file containing the same token, and publish a newer diagnostic result. Old bands/tooltips must disappear immediately unless the publication matches current displayed bytes. No first-text-match remapping. Drawing the new child with old publication data is a failure even if its rectangles look plausible.
- Actual layout: one diagnostic across wraps at nonzero scroll, partial first/last runs, then narrow/wide resize and font/highlighter change. Every admitted visible cluster must be represented within the actual text/ancestor clip; no extra scroll subtraction, DPI multiplication, or single rectangle spanning unrelated bidi glyphs. Use tabs, combining marks, mixed RTL/LTR and repeated tokens. Cluster expansion describes advance bands, not exact ink coverage or altered source spans.
- Zero and absent geometry: empty buffer, EOF, newline-only range, whitespace and zero-advance cluster. Define the marker explicitly or suppress it with accessible details elsewhere; never manufacture a nearby word's tooltip via snapping hit(). A pointer outside the actual clipped annotation band must not activate that diagnostic.
- Input continuity: hover redraws must publish no edit/navigation messages and leave exact bytes, focus, Word/Line selected text and cursor state, scroll, model/history unchanged. Delegate normal update/operate/mouse interaction/layout/draw and any child overlay behavior. Repeat Find close/Escape, drag, wheel, clipboard and IME preedit/commit paths on the integrated widget. Checking only raw cursor equality is insufficient.
- Occlusion and redraw: move between bands while widget status is unchanged; leave the editor/window; open and dismiss a modal while the pointer is stationary. Tooltip must update/hide without depending on caret blinking, and never overpaint or activate through the modal. No retained draw geometry may become an actionable navigation token.
- Work bounds: index diagnostics by logical lines/ranges; do not scan all diagnostics on each frame. Measure a large file and one very long wrapped line scrolled far down. Cosmic LayoutRunIter (buffer.rs:99–176) begins at the scrolled logical line but iterates wrapped layout lines to reach the vertical offset: 'visible runs only' is not proof of constant work independent of hidden wraps. Do not claim the wrapper's second traversal is negligible without measurement. Start with observed frame/hover cost rather than a new cache or layout engine.
CPU success establishes the pinned renderer path only. Native minimum/wide allocation, DPI, hover/scroll and selected-byte probes remain necessary before acceptance. A private patch should retain original crate hashes and exact patch contents; production capture must include patched source and notices, not just the old registry version string.
Reviewed bindings
Primary cache root: main repo .local/toolchain/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/.
| File | SHA-256 |
|---|---|
| .local/text-diagnostics-20260913/editor-geometry-research.md | 48b726bc87842e0da7695209f9b2292822192cdcecf9bea59baf1294c3625f36 |
| iced_widget-0.14.2/src/text_editor.rs | cecf8686c90aa92066d55075beba545dfb7dd5a44bad0c1519fcb110a56070e1 |
| iced_widget-0.14.2/src/lib.rs | 4da7f1a83cd8edc30bd9983e59ba3537f44a475814d0b9c80dcf7e97873badb4 |
| iced_widget-0.14.2/Cargo.toml | e8e6b9a72fc0f97c84b8fb2703fe6cdccd738c6c01d2fadf018a012182de93b5 |
| iced_core-0.14.0/src/renderer.rs | bba3595f0b1487e64243087de3af17cd5ba11b7f7c485521bd0a7229e2b1e3b9 |
| iced_core-0.14.0/src/text.rs | 38d58babceba650855c1ddeaf599548cac3f57db94ef4131a0571126d88166d6 |
| iced_graphics-0.14.0/src/text/editor.rs | b12c4e645a4db26c79e421a88c2127c8c20b7ff0f5a6fae9a8e050f40bf39567 |
| cosmic-text-0.15.0/src/buffer.rs | c715f18a4288cfc15c1d919a1998146d4d44072704bd6d24dfc30811149647d8 |