1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
Selection anchors through insert-mode edits, and the transaction model
=======================================================================
The one place the helix differential suite shows a real structural gap
(waiver "wiX-edit-drops-sel" in test/hxcases/waivers.jsonl): helix keeps a
selection ALIVE across insert mode, pardes drops it on the first edit.
What helix does
---------------
A selection doesn't die when you enter insert mode. Every edit is a
Transaction — a structured delta (retain N, insert "...", delete N) — and
all stored positions are MAPPED through it, the selection anchor included.
Insert a char before the anchor and it slides right by one; delete a line
above it and its row shifts up. Esc back to normal mode and the old
selection is still there, coordinates adjusted to cover the same logical
text.
Example ("alpha beta" + keys w i X <esc>):
after w: anchor (0,0), cursor on the space (0,5), "alpha " selected
i, type X: buffer is "Xalpha beta"
after esc: helix — anchor (0,1), selection still spans "alpha " (remapped)
pardes — selection gone, anchor == cursor
Final text, cursor, and mode agree on both sides; only the anchor differs.
What pardes does today
----------------------
No transaction layer. handleInsert mutates the buffer directly at ~23
distinct sites (char inserts, newline, backspace incl. the line-join case,
Ctrl-w/Alt-d word kills, Ctrl-u/Ctrl-k line kills, multi-line paste, and
the terminal run-splice variants). Keeping the anchor alive would mean
hand-written row/col shift arithmetic at every one of those sites — 23
error-prone edit paths to maintain for state nothing reads: d/c/y take a
fresh selection from the next motion, and the acme Enter/Tab chords only
act on EXPLICIT selections (v/x/n/N), which never enter insert mode. Hence
the waiver instead of a fix.
Why we might adopt the transaction model anyway
-----------------------------------------------
A small transaction layer (edits as deltas + one mapThroughEdit(pos, edit)
helper applied at each mutation site) would buy, in rough order of value:
- "." repeat-last-insert (currently deferred in docs/helix-keys.md for
exactly this reason: no edit log to replay)
- delta-based undo/redo instead of whole-buffer snapshots (pushUndo
dupes f.content per edit op today — fine for small files, O(file) per op)
- the waived anchor parity for free (selections survive insert mode,
remapped like helix)
- any future position-carrying state (marks, jumplist, multiple
selections) gets remapping for free instead of per-site arithmetic
Upgrade path, if/when
---------------------
1. Define the delta type in modal.zig (pure, unit-testable): a list of
(retain, insert, delete) spans, plus mapThroughEdit for a (row,col).
2. Make the ~23 handleInsert mutation sites EMIT a delta and apply it in
one place, instead of splicing content inline.
3. Undo becomes a log of inverse deltas; "." becomes a replay of the last
insert session's deltas; drop the y-anchor waiver and its harness case.
Adopt only when one of the consumers above is actually wanted — the delta
layer alone is machinery without a user.
|