editor: Speed up multi cursor editing (#58510)

Early draft for #32051 (multi-cursor editing is very slow, and basically
hangs at high cursor counts). Opening it early like @Anthony-Eid
suggested so we can agree on direction before I go further.

## Root cause

Typing with a cursor on every line is mostly O(N)-per-keystroke work
spread across a few places. I profiled it with `sample` and per-phase
timers around `handle_input`. At 1000 cursors (~24ms before this PR):
the post-edit display-map sync is ~11.8ms, the CRDT edit
(`apply_local_edit`) ~3.7ms, resolving all N selections plus the
per-cursor input loop ~3.5ms, the post-edit selection round-trip ~2.6ms,
and `change_selections` plus transact machinery ~2.4ms. The display sync
re-runs per-edit `SumTree` work through every layer even when nothing
transforms (the plain-text case), and selections get re-resolved through
the full display round-trip several times per keystroke.

## What this does

- Display fast paths: `InlayMap::sync` (no inlays) and
`WrapMap::interpolate` (no soft-wrap) skip the O(edits) transform-tree
rebuild and return the passthrough snapshot, gated so a pending inlay
splice or existing wraps still take the slow path.
- Selection fast path: when nothing collapses buffer content
(`!has_folds() && !has_replacement_blocks()`), resolve `Anchor` to
`Point` to `Offset` batched and skip the per-selection display
round-trip (the `todo(lw)`).
- Render and autoscroll resolve only the first/last/newest selections
instead of all N per frame.
- A parameterized `Multi-cursor input/cursors/{1000,10000,100000}`
benchmark.

## Results

- Typing (`handle_input`): ~2.1x faster (24ms to 11.7ms at 1k).
- Type plus two word-deletes (criterion): 29% faster (89.9 to 63.8ms) at
1k, 37% (959 to 606ms) at 10k.
- Post-edit display sync alone: 11.8 to 3.8ms at 1k, 133 to 45ms at 10k.

All `editor` (759), `display_map` (67), `multi_buffer` (58), and `text`
(36) tests pass, and `./script/clippy` is clean.

## Direction

This is ~2x, and I can get the current architecture to roughly 3.5-4.5x
with a few more safe changes (hoisting the per-cursor
language/editability checks, cheaper snapshot clones). Genuine VS Code
numbers (~1-2us/cursor) aren't reachable while the buffer is a CRDT rope
of fragments with anchor selections and a 5-layer transform stack. That
would need a plain-offset cursor model and/or decoupling display layout
from the edit path, which is a bigger effort I'd want to design with
you. One thing I'd need your call on: `set_active_selections` sends an
`UpdateSelections` collab op every keystroke (building N anchors); can
that be debounced to transaction end, or do presence/follow-mode
features rely on per-keystroke cursor broadcast?

Release Notes:

- editor: Improved multi cursor editing performance

---------

Co-authored-by: Anthony Eid <anthony@zed.dev>
1722fe63bcRani committed on 6/17/2026, 7:57:05 AM· committed by GitHubparent45afbac
14 files changedLine totals unavailable