Ctrl K
ComunidadGuías…

docs/bidi-selection-direction.md

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Bidi (RTL) selection direction: discussion, open questions, next steps

Working notes from reviewing PR #709 ("Right-to-left (Persian/Arabic/Hebrew)
rendering, selection, and copy", author @nuved). Recorded 2026-08-06.

This file captures the design discussion so it is not lost while we wait on the
author. It is not a spec; it records where we are and why.

## Context

PR #709 adds RTL rendering (mirror pairs, guillemets as-typed on both the
CoreGraphics and Metal renderers), scrollback/scrollRect propagation of the
per-line RTL annotation, and a rework of selection and copy so that the
selection is stored and reasoned about in logical (source-cell) coordinates.
Everything is gated behind the existing Bidi preference and is a no-op with it
off.

The rendering and copy-order work is good and worth keeping. The open question
is the **mouse selection model**.

## The core question: visual vs logical mouse selection

The distinguishing characteristic of logical selection in mixed-direction text
is **discontiguousness**: a logical range can render as several disjoint
rectangles on screen, and its copy includes everything logically between the
endpoints. Visual selection follows the pointer, stays contiguous on screen,
and never goes discontiguous (word and line selection included, since each is a
single contiguous span).

We wanted to "match OS convention unless the OS is lazily doing the wrong
thing." The complication we discovered: macOS is **not internally consistent**,
so there is no single convention to defer to.

## What we tested (empirical, not assumed)

Test line: `سلام Hello دنیا` (on-screen order left to right: `donya Hello
salaam`). Probe: drag-select from the middle of `Hello` (starting at the second
`l`) rightward into the Persian word to its right, then copy.

- **TextEdit (native NSTextView): VISUAL selection + logical-order copy.**
  Highlight follows the pointer and stays contiguous. Copy serializes the
  glyphs actually swept into reading order, giving `سلام llo`. The `He` that was
  not dragged over is excluded.
- **Safari, Chrome, Firefox (all three tested): LOGICAL selection.**
  Selection is a logical range between the endpoints, highlight can jump and go
  discontiguous, and copy returns the whole logical interval, giving `سلام
  Hello` (includes the `He` that was not swept).

So the split is: native macOS text (TextEdit) on one side, the entire browser
platform on the other. **PR #709 currently implements the browser/logical
model.** Both are legitimate, deliberate conventions; the PR's choice is
defensible, not a bug.

Two earlier claims were corrected by testing: browsers do NOT do visual
selection (all three do logical), so any "every browser does X" framing was
wrong. Only assert what was tested.

## Where the maintainer (non-RTL) currently leans

Lean, held loosely: **visual for the mouse**, because iTerm is a native Mac app
whose selection has always been visual/column-based, its closest analog is the
native text view rather than a web document, and "what I dragged is what I get"
is least surprising for terminal use (commands, paths, output). Personal
reaction: the browser/logical behavior is hard to use because the highlight
stops tracking the mouse.

Important caveat: this is a non-expert opinion. The author reads and writes RTL;
we asked for their analysis rather than imposing a preference.

## copy mode as the home for logical selection

Regardless of the mouse decision: iTerm's **copy mode** is our keyboard-driven
selection and the natural analog of shift-arrow, where macOS itself does
logical, discontiguous selection even in native text views. So if we land on
visual for the mouse, logical selection still has a clean home in copy mode
rather than being lost.

## Open questions (posted to the author)

Posted at https://github.com/gnachman/iTerm2/pull/709 (issuecomment-5198952840):

1. How did they analyze the tradeoff? Was the browser/logical model chosen
   deliberately, or did it fall out of storing the selection logically for the
   copy fix?
2. As an RTL user, which do they reach for and expect day to day, and does it
   change by context (prose vs a command or path in a terminal)?
3. Is there something better about the browser/logical model for RTL
   specifically that a non-RTL user would miss?

## Implementation issues parked until direction is settled

These came out of the review and are independent of the visual-vs-logical
decision. Held back so we do not bury the author before aligning on direction.

- **Metal renderer selected fg/bg coordinate mismatch (real bug, fix
  regardless).** In `sources/MetalRenderer/Glue/iTermMetalPerFrameState.m` the
  selected background at line 1424 tests the logical cell index
  (`containsIndex:logicalX`, correct), but the selected foreground at line 1702
  still tests the visual column (`containsIndex:visualX`). On a reordered line
  these are different cells, so a glyph can get the selected background with the
  unselected foreground color and effectively disappear (observed on screen).
  Lines 1720 (annotations, `visualX`) and 1983 (`bgrle->origin`, visual) have
  the same mismatch. The CoreGraphics path is consistent (derives both from the
  same logical `run->selected`), so toggling off the GPU renderer makes the
  glyphs reappear, which confirms it is Metal-specific.
- **Missed visual-to-logical conversions feeding the logical mouse selection.**
  Several selection entry points still pass raw visual coordinates into the now
  logical selection model: shift-click-extend and three-finger drag in
  `PTYMouseHandler.m`, `findOnPageSelectRange:` in `PTYTextView.m` (stores a
  visual range via `visualRangeForLogical:` into a logical selection),
  context-menu copy of a smart-selection action in
  `iTermTextViewContextMenuHelper.m` (passes `action.visualRange` to the now
  logical extractor), Look Up / force-touch (`showDefinitionForWordAt:` in
  `PTYTextView+ARC.m`), and right-click / pointer-gesture smart selection in
  `iTermURLActionHelper.m`. **Most of these dissolve if the mouse path stays
  visual**, since they only exist to feed a logical mouse selection.
- **Supplementary-plane strong-RTL detection.** `firstStrongIsRTL` in
  `sources/Drawing/BidiDisplayInfo.swift` walks UTF-16 units and skips surrogate
  halves, so non-BMP strong-RTL characters (Adlam, Hanifi Rohingya, etc.) are
  ignored and paragraph direction is misdetected. Gated behind the default-off
  `detectParagraphDirection` advanced setting, so low impact, but a regression
  vs the `rangeOfCharacter(from:)` code it replaced.
- **Pre-existing typo (not introduced by this PR).**
  `iTermAttributedStringBuilder.m` has `bidiLUT[i - i]` (always index 0) where
  `bidiLUT[i - 1]` was intended, misplacing spacing combining marks on bidi
  lines. From the original GPU-RTL commit, worth a one-character fix while in
  the area.
- **Conventions.** The diff adds em dashes to comments and at least one
  user-visible string (the `stripZeroWidthFormatCharactersOnPaste` advanced
  setting description), which the em-dash rule prohibits.

## Next steps

1. Wait for the author's analysis on the mouse selection model.
2. Fix the Metal selected fg/bg coordinate mismatch regardless of direction (it
   is a bug under any model).
3. If we land on **visual** for the mouse: keep the reading-order copy
   serialization, revert the mouse selection/highlight to visual columns (where
   iTerm already was), and let copy mode be where logical selection lives. This
   also removes the need for most of the missed conversions above.
4. If we land on **logical** for the mouse: finish the conversions at every
   selection entry point so the model is consistent.
5. Keep regardless of direction: the mirror-pair rendering, guillemets as-typed,
   reading-order copy extraction, and scrollback/scrollRect RTL propagation.

## Manual test artifacts (local, not committed)

- `~/bidi-test.txt`: mixed-direction lines to `cat` in a terminal build with the
  Bidi pref on.
- `~/bidi-safari-test.html`: same lines in a browser with a live readout of what
  Cmd-C would copy (selection string plus code-point breakdown), used to compare
  TextEdit against the browsers.