spec: Shift+Click extends terminal text selection (#9963)#10026
spec: Shift+Click extends terminal text selection (#9963)#10026lonexreb wants to merge 1 commit intowarpdotdev:masterfrom
Conversation
Adds product.md and tech.md for issue warpdotdev#9963: support the standard terminal interaction where Shift+Click extends an existing selection from its original anchor to the clicked point. Today, Shift+Click clears or restarts the selection — a regression from every other terminal emulator. V1 scope: - Holding Shift while clicking inside terminal output extends from the existing selection's anchor (not its head). Forward and backward extension both supported via the existing `Selection::start_anchor` / `end_anchor` getters' orientation-agnostic resolution. - Degenerate cases handled: Shift+Click with no existing selection starts a single-point selection (equivalent to a non-Shift click). - Anchor persistence across scroll: BlockAnchor encodes buffer position, not viewport position — invariant 9 holds for free. - One small mutator on Selection (set_head_to) if not already public, plus a single new branch in the terminal-view mouse-down handler. Out of V1 (tracked as follow-ups): - Shift+Drag to extend an in-progress selection - Shift+Click parity in editor / Markdown viewer / settings surfaces - Shift+Click in rectangular-selection mode - Keyboard-only selection extension (Shift+Arrow in terminal output) 8 testable behavior invariants. Implementation surface is genuinely small: one branch in the mouse-down handler, one mutator on Selection if missing, no new infrastructure. Reuses existing model events so shared-session broadcasting and "Copy on Select" interactions ride the existing path.
|
I'm starting a first review of this spec-only pull request. You can view the conversation on Warp. I completed the review and no human review was requested for this pull request. Comment Powered by Oz |
There was a problem hiding this comment.
Overview
This PR adds product and tech specs for terminal Shift+Click selection extension. The desired user behavior is clear, but the tech spec currently directs implementers to mutate the wrong selection endpoint and has several internal inconsistencies around Shift+Drag scope, endpoint side handling, and scroll-persistence validation.
Concerns
- The implementation guidance inverts the existing
head/tailsemantics, which would preserve the wrong anchor and break the core feature. - V1 scope is inconsistent for Shift+Drag/active-drag behavior.
- Anchor persistence across scroll is claimed as an invariant but is not actually listed or tested.
Verdict
Found: 1 critical, 3 important, 0 suggestions
Request changes
Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).
Powered by Oz
| } | ||
| ``` | ||
|
|
||
| The "original anchor" is the `tail` of the existing selection in today's model — the field that wasn't moved by the most recent mouse drag. If the existing selection's `head` is currently before `tail` in document order (i.e. the user dragged backward last time), `set_head_to(click_point)` may flip the orientation; the existing `Selection`'s `start_anchor()` / `end_anchor()` getters resolve the user-visible "from" and "to" points correctly regardless. |
There was a problem hiding this comment.
🚨 [CRITICAL] The current BlockListSelection model treats head as the original click and tail as the endpoint moved by update_selection, so this directs implementers to move the wrong endpoint. Rewrite the spec to preserve head and update tail for Shift+Click.
|
|
||
| ## Non-goals (V1 — explicitly deferred to follow-ups) | ||
|
|
||
| - **Shift+Click during an active drag.** V1 handles the discrete click case; a drag started with Shift held continues to extend until release, but the spec does not formalise drag-extension behavior. Existing drag-select continues to work as today. |
There was a problem hiding this comment.
| } | ||
| ``` | ||
|
|
||
| The `side` field on `BlockAnchor` (left vs right of the column) is preserved; for click-to-extend, keeping the existing head side is the right default — Shift+Click at a column doesn't change the half-column the selection ends on. |
There was a problem hiding this comment.
|
|
||
| ### 3. Anchor persistence across scroll | ||
|
|
||
| The `BlockAnchor` already encodes a buffer position (block index + grid point), not a viewport position. Scrolling the buffer between the original click-drag and the Shift+Click does NOT invalidate the anchor — invariant 9 in product.md (anchor persistence across scroll) holds for free. |
There was a problem hiding this comment.
product.md has only eight invariants and anchor persistence is listed as an open question, so this V1 requirement has no mapped verification path. Add it as a numbered invariant and a test row, or remove the invariant claim.
Adds a product+tech spec for #9963: support the standard terminal interaction where Shift+Click extends an existing selection from its original anchor to the clicked point.
Files
Total: 234 insertions. No code changes — this is a spec PR.
Why this is small
The selection model in `app/src/terminal/model/blocks/selection.rs` already represents selections as `head`/`tail` anchor pairs with orientation-agnostic `start_anchor()` / `end_anchor()` getters. Extension is mechanically just "move the head to the click point" — the existing struct handles the orientation-flip case (when the click is before the original anchor) without any new logic.
The implementation surface:
V1 scope
Out of V1 (tracked as follow-ups)
Why this spec
Open questions for maintainers
Happy to iterate further.