Files
ARCADIA-Studio/harness/experiments/text-workspace-initial-review-20260913/selection-snapshot-research.md
T
sorokin.oleg.edu 2df7f580aa Make the Text workspace compact and preserve editing context
Replace the oversized header and reference rail with stable editor slots and a retained supporting drawer. Bind diagnostic, result and menu actions to their original publication and visit; preserve effective selections on navigation and dismiss Actions, Find and the drawer one surface per Escape.

Retain exact Windows source captures, eight final gates (871 desktop passes), independent reviews, native selection/rename/recovery trials and the original failures. All 526 tested inputs and staged evidence were byte-checked before commit. Combined preview integration and packaging remain with issue 27; Linux remains paused.
2026-09-12 22:00:36 -07:00

74 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Back/file selection snapshot research
Read-only source research, Codex-9e8r6t /root/native_archive, 2026-09-13. No source edits, compilation, application/UI execution or vendoring. Native Find Close failure is established separately by trial-text-1. **Back and file-switch defects below are source-derived, not new native observations.**
## Stable input and requirement
Inspected the immutable attempt-4 source ZIP `C:/ar-text-9e8r6t/.local/text-workspace-gates-20260912/attempt-4/inputs.zip`, SHA `e1e566295bbbd6c1d571f7c8908b7344397293496481790f0f8dacab7eba5366`. Relevant member hashes:
| Member | SHA-256 |
| --- | --- |
| `crates/oa-desktop/src/app/source_symbols.rs` | `1fb3709bb973e7dcfbcde56054d028e47d770a62f3b7c8b6fac411543a234b7b` |
| `crates/oa-desktop/src/source_workspace.rs` | `388c71f63829c0c76526a1700ebf5f240c86c467a65aae4fca4cd6c6a88c468b` |
| `crates/oa-desktop/src/app.rs` | `f3a61976c1ddbd3a80ae082858dfaa28652156c3af98c6b8fd5d9d6ce294b2b0` |
| `crates/oa-desktop/src/source_navigation/controller.rs` | `7003533fbc3f1d31bf667555faaf81015f6a5d8fffd869e34e15395ce94835fe` |
`harness/release-text-navigation-design-20260911.md`, interaction rule 7, requires an unchanged Back origin to restore its exact selection. Changed origins intentionally use the current retained cursor with a notice, rather than pretending the historical offset is exact. This is separate from the current focus-only Find correction and must not weaken its no-caret-move behavior.
## Concrete failure chain
`source_symbol_jump` stores `self.source_editor.cursor()` in `HistoryEntry`; `Workspace::switch` stores the same representation in `Buffer.cursor`. `source_symbol_back` selects that saved cursor for an unchanged origin. File restoration and Back reconstruct `Content` then queue a reveal that calls `move_to`.
Iced graphics `Editor::cursor` maps all Cosmic `Normal`, `Word` and `Line` selections to the same two raw positions. For a double-click word, those raw positions can coincide inside the word even though the effective selected byte range is the whole word. `Editor::move_to` restores any `Some(selection)` as `Normal`. Consequently, the saved representation cannot restore the effective Word/Line selection on unchanged Back or a file round-trip. The equality of raw `Cursor` structs is not a sufficient test oracle; typing can insert inside the token instead of replacing the original selection. Normal explicit endpoint selections do not suffer this particular loss.
## Public API boundary
The cached primary implementations are:
| Source under `.local/toolchain/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/` | SHA-256 |
| --- | --- |
| `iced_widget-0.14.2/src/text_editor.rs` | `cecf8686c90aa92066d55075beba545dfb7dd5a44bad0c1519fcb110a56070e1` |
| `iced_graphics-0.14.0/src/text/editor.rs` | `b12c4e645a4db26c79e421a88c2127c8c20b7ff0f5a6fae9a8e050f40bf39567` |
| `iced_core-0.14.0/src/text/editor.rs` | `717158d90d372183ee07483ad950c648fe79acdaa3c61db4d006b924426d0eee` |
| `cosmic-text-0.15.0/src/edit/editor.rs` | `9d61a742fea99db9044fce28a253f790f6b48369c24ec9646a3209a6a69fa1cb` |
`Content::selection()` returns copied selected text. The public `Editor::selection()` returns visual caret/rectangles, not logical bounds or selection mode. `Content::clone()` recreates text with `with_text`, losing selection and scroll; it cannot make a selection probe. Cosmic logical selection bounds are inside the graphics editor, whose internal edit state is private. Inferring ranges from painted rectangles or searching copied text in the source is unnecessary and unsound for repeated text.
## Small supported candidate: canonical range at committed departure
Use the existing `Cursor` storage as a **canonical effective range**, rather than introduce whole-file snapshots or a new history authority. A helper needs mutable Content only at the successful departure boundary:
1. Read the original raw cursor and `Content::selection()` before modifying selection. If there is no effective nonempty selection, retain the caret representation; do not issue Left on an unselected caret.
2. Perform public `Action::Move(Motion::Left)`. Graphics `Editor::perform`, lines 315–339, explicitly obtains effective `selection_bounds`, clears selection and sets the caret to its start. It does not edit text. This works for Normal, Word and Line selections.
3. Read that start from `Content::cursor().position`. Derive the end by **position arithmetic on the already copied selection**, not searching: Cosmic `copy_selection`, lines 495–520, joins selected editor lines with one LF regardless of physical line endings. No LF means `end.column = start.column + selected.len()` on the same editor line. Otherwise `end.line = start.line + LF count`, and `end.column` is the UTF-8 byte length after the last LF. These are editor byte columns; inspect `Content::line` boundaries if validating. Do not rewrite or normalize the source itself.
4. Store the resulting endpoint pair as a Normal `Cursor`. Preserve forward/reverse direction for unequal original anchor/head positions. When Word/Line raw positions coincide inside an expanded range, choose a documented canonical active endpoint, e.g. the end. Restore this explicit range on the fresh Content used by Back/file return.
This retains only constant-size coordinates and temporarily copies O(selected bytes), not O(file size) when the selection is small. Performing Left followed by Right does **not** obtain both old boundaries: Left already clears the selection, so Right would move normally. The copied-selection arithmetic is what avoids a second selected editor snapshot.
Capture only after all target, session, snapshot and admission checks have succeeded and immediately before an actual jump or different-file switch. Do not normalize during query capture, pending work, failed/stale completion, invalid file selection, a no-op click on the active file, or focus restoration. Keep raw query-request cursors unchanged for their existing freshness comparisons. Reuse one departure capture for both history and the outgoing buffer cursor. There must be no source edit, Apply, Save or Undo-history mutation from capture.
**Explicit semantic boundary:** this preserves exact selected bytes and subsequent replacement behavior. It does not preserve the Word/Line gesture-extension mode or the clicked interior caret as a distinct hidden coordinate. If the requirement is exact continued word-drag/line-drag behavior after returning, canonical Normal ranges are insufficient; that stronger requirement needs a richer supported editor API or bounded retained editor state. Do not silently claim that property. For the existing Back rule's selected-byte equivalence, canonical ranges are a proportionate candidate to verify.
Keeping the actual Content object per history entry would retain richer state but can hold whole files/shaping data across 64 entries; it requires an explicit memory policy and is not the smallest change. Tracking guessed selection modes from input, reconstructing words with custom lexical/string-search heuristics, unsafe access and broad dependency vendoring are rejected for this bounded correction.
## Required placement and guards before capture
| Boundary | Checks that must precede the mutable helper |
| --- | --- |
| `source_symbol_jump` | Preserve caller's query/result/session/interaction/raw-input admission. The target file must exist and `nav::exact_cursor` must accept the intended target range. Only then capture the outgoing effective selection and use that same cursor for `HistoryEntry` and `Workspace::switch`. A valid same-file symbol jump is a real departure and may capture; a failed target must not. |
| `source_symbol_back` | Existing Back admission first; pop/skip foreign sessions, require the origin file, choose unchanged generation vs changed-current fallback, and validate the eventual destination before normalizing the outgoing editor. Empty history or invalid origin returns without capture. If the changed-origin fallback is the currently active editor, reuse its newly canonical captured range rather than the lossy raw cursor. Preserve the changed-position notice and no-history-push behavior. |
| `Message::SourceFile(path)` | Preserve existing outer input/modal admission, then explicitly require `path != active` and loaded membership **before** helper invocation; those conditions currently live inside `Workspace::switch`. Moving capture before an unchecked `switch` would mutate selection on invalid/no-op requests. After checks, capture once, switch, and restore the destination buffer's canonical cursor. |
| `jump_to_source` | Preserve each caller's authority: current diagnostic publication/snapshot/visit/path/span or the separately authorized model-to-source action. Require a loaded target and establish the destination coordinates before capturing outgoing selection. The old function calls `switch` before its missing-file failure check; do not insert capture before that check. Keep its existing line-selection/reveal semantics. Do not add a blanket Text-only admission that breaks authorized model-to-source entry from another page. |
The attempt-4 `source_navigation_available` predicate requires Text page, no rename dialog, no busy/pending/file/delete/workflow/hosted/model-menu/Text-menu/architecture/behavior modal context. Continue checking that predicate at existing lexical navigation entry points. A new helper is not an authority bypass and must not replace captured-message freshness guards. File creation, load/recovery replacement, source edits and pending query snapshots should not acquire departure normalization as a side effect of a broad call-site search/replace.
## Required discriminating checks
- Actual mounted Word and Line selections, including word-drag/line-drag if canonical byte-range coverage is intended, must have identical `selection()` before departure and after restoration; subsequent typing must replace exactly those bytes. Compare exact final source bytes, not merely cursor structs.
- Cover forward/reverse Normal ranges, multiline LF/CRLF/mixed endings, non-ASCII text, repeated identical aliases, trailing newline, empty line and no-selection caret. Verify source bytes remain unchanged by capture and stored history remains bounded.
- Exercise real successful definition/reference jump→Back with unchanged origin and a real different-file round-trip. Retain the existing changed-generation fallback and notice; do not restore stale historical ranges into edited sources.
- Failed query, stale completion, invalid/no-op file selection and Find focus-only round-trips must not normalize or move the current selection. Their subsequent native/mounted replacement behavior remains the original behavior.
- Source/model revisions, complete bundle, text Undo and accepted-model history must be unchanged until an explicit user edit. No filesystem action belongs to capture.
These are proposed evaluators, not executed results. The parent owns the correction decision, implementation partition and serialized gates/native confirmation.