summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-28 15:40:03 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:15 -0300
commit68b0fe7600987587ed0c27cf38a3fca8216c45b4 (patch)
treece25c1adaa366413e85f68b38c2a765aeb715f70 /docs
parent9bade7130bc0aa8cbcea82c446fc5747453b1360 (diff)
downloadpardes-68b0fe7600987587ed0c27cf38a3fca8216c45b4.tar.gz
pardes-68b0fe7600987587ed0c27cf38a3fca8216c45b4.zip
docs/selections.md says how normal mode holds its selections
The primary and the others, cells against helix's gaps, how setRanges normalizes, which commands replay per range and which make one edit, and what undo, the register and repeat do with several ranges, with the places helix differs listed at the end. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'docs')
-rw-r--r--docs/selections.md103
1 files changed, 103 insertions, 0 deletions
diff --git a/docs/selections.md b/docs/selections.md
new file mode 100644
index 00000000..5b992cc4
--- /dev/null
+++ b/docs/selections.md
@@ -0,0 +1,103 @@
+# Selections
+
+How normal mode holds and changes its selections, and where that differs from
+helix's `Selection` (a list of ranges over gap offsets, with a primary index).
+Key-by-key behaviour is in docs/helix-keys.md; this is the model underneath.
+
+## What a selection is
+
+Every `Text` (src/Text.zig) has its own: a pane's body, its tag, a prompt's
+answer, a column's or the workspace's tag. A selection is one primary range
+plus up to 63 others.
+
+- The PRIMARY is where the cursor always was: `cur_row`/`cur_col` is the
+ head, `vsel.row`/`vsel.col` the anchor while `vsel.active`, and a bare
+ cursor (inactive `vsel`) is the one-character range under it. `msel` is
+ the older whole-line form (`r0`..`r1`), kept for search-result highlights.
+- The others are `sels[0..nsel]`, each a `SelRange` of head and anchor plus
+ its own `j`/`k` goal column, in document order. The primary is not in the
+ list; `Text.ranges` slots it in and returns its index.
+- Ends are CELLS, the characters a block cursor sits on, where helix's are
+ gaps between characters. `Text.cellRange`, `cellOffRange` and `rangeCells`
+ convert. A range covers at least one character, as in helix (min width 1),
+ so there are no empty ranges.
+- `vsel.explicit` says the selection was made on purpose (`v`, `x`, `%`,
+ `s`, `n`/`N`, ...) rather than left behind by a motion. Only pardes reads
+ it: the look (Enter) and execute (Tab) chords act on explicit selections
+ and on the word under the cursor otherwise.
+- `select` is `v` extend mode. `append_at` remembers where an `a` began, so
+ Esc can give back the range it appended over.
+
+`Text.setRanges` is the one writer of a whole selection: it sorts by start,
+merges ranges that overlap or share a start (helix `normalize`), follows the
+primary through the merges, and keeps the first 64. `setRange` writes one
+range and drops the others.
+
+## Making, splitting, merging, rotating, removing
+
+- Made several by `C`/`Alt-C` (copy to the next/previous line), `s` (every
+ match inside each range), `S` (the pieces between matches), `Alt-s` (one
+ per line), `<n>o`/`<n>O`, and `Alt-J` (the spaces a join put in). `s`/`S`
+ re-run on every keystroke from the selection the prompt opened on
+ (`Pane.sel_snap`); Esc is that snapshot restored.
+- `Alt-minus` merges all into one, `Alt-_` the consecutive ones, `_` trims
+ whitespace (dropping all-blank ranges).
+- `)`/`(` rotate which range is primary; `Alt-)`/`Alt-(` rotate the TEXT
+ between ranges, the primary moving with its text.
+- `,` keeps only the primary, `Alt-,` removes it (the next range takes over).
+- After `s`, `S` and `Alt-s` the primary is the first range, which is
+ helix's behaviour (its own TODO), not a choice.
+
+## How a command reaches every range
+
+A parsed action has a scope (`modal.Normal.Action.scope`).
+
+- `per_selection` (motions, `d`, `c`, `y`, `p`, `r`, `~`, `>`, `ms`, ...):
+ `normal.replaySels` runs the ONE-range handler once per range, last range
+ first, each pass seeing a single selection. A pass's result is stored as
+ distances from the END of the text, which an edit at an earlier range
+ cannot move; `setRanges` then puts them back together. The mode, prefix
+ and select state that survive are the primary's pass. Insert-mode keys are
+ replayed the same way.
+- `once` (the `multi` family, `s`/`S`, `J`/`Alt-J`, `Alt-(`/`Alt-)`, `&`,
+ `Ctrl-c`, undo/redo, builtins, search): one function takes `Text.ranges`,
+ makes one edit and hands the mapped ranges to `setRanges`, the way a helix
+ command builds one transaction.
+- Anything that reaches outside the text (a builtin, a language query, a
+ look) runs once from the primary and drops back to one cursor
+ (`normal.multiOnce`).
+
+## Undo, registers, repeat
+
+- One keystroke is one undo step however many ranges it edited: only the
+ first replay pass records (`pushUndo`).
+- An undo snapshot (`File.Snapshot`) holds the content and the PRIMARY
+ range. Undo puts the primary back and leaves the other ranges where they
+ were; helix restores the whole selection of that revision.
+- There is ONE register, `Pardes.yank`, written by `y`, `d` and `c` and read
+ by `p`, `P` and `R`. A yank at several ranges joins their texts with
+ newlines into it, and a paste puts that whole value at every range.
+- `Alt-.` repeats the last `f`/`t`/`F`/`T`. There is no `.` and there are no
+ macros.
+
+## Where it differs from helix
+
+- Registers: helix keeps one value PER RANGE and pastes value `i` at range
+ `i`; it has named registers (`"a`), special ones (`#` range indices,
+ `.` the selection, `/` the search, `_` the black hole) and inserts a
+ register in insert mode with `Ctrl-r`. None exist here (waiver
+ `msel-yank-paste`).
+- Insert mode: helix maps every range through each keystroke's edit, so a
+ range an `i`/`a` started from stretches over what is typed and keeps its
+ anchor. Here only the cursor cells move, and after Esc only the primary's
+ `append_at` span comes back (waivers `wiX-edit-drops-sel`, `msel-append`).
+- Direction of a one-character range: both of its cells are the same cell,
+ so it has none. helix can flip one (`Alt-;`) and the commands that read
+ the head (`&`, the next extend) see the difference.
+- Count: at most 64 ranges; matches past that are dropped silently.
+- `n`/`N` walk the look ring (acme), not regex search hits, and there is no
+ `*`; helix-golf's `*`, `""N` and `n` do not apply.
+- `s`/`S` match as sam does (docs/helix-keys.md, "Regex selection"): each
+ line is its own haystack, so `^`/`$` hold at every line and a class like
+ `\s` does not reach the newline unless the pattern names `\n`.
+- Undo restores only the primary (above).