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

11 KiB
Raw Blame History

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.