summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/hxcases/cases.jsonl121
-rw-r--r--test/hxcases/goldens.jsonl123
-rw-r--r--test/hxcases/parity-waivers.jsonl3
-rw-r--r--test/hxcases/waivers.jsonl4
-rw-r--r--test/hxdiff.zig99
-rw-r--r--test/snapshots/lspdebug.golden14
-rw-r--r--test/snapshots/multicursor.golden217
-rw-r--r--test/snapshots/multicursor.snap46
-rw-r--r--test/snapshots/selregex.golden275
-rw-r--r--test/snapshots/selregex.snap56
10 files changed, 947 insertions, 11 deletions
diff --git a/test/hxcases/cases.jsonl b/test/hxcases/cases.jsonl
index 386013bf..b0a3aad4 100644
--- a/test/hxcases/cases.jsonl
+++ b/test/hxcases/cases.jsonl
@@ -358,3 +358,124 @@
{"name": "ins-arrows", "pane": "file", "text": "abc\ndef\n", "keys": "i<down><right>x<esc>"}
{"name": "ins-tab-col0", "pane": "file", "text": "ab\n", "keys": "i<tab><esc>"}
{"name": "ins-tab-mid", "pane": "file", "text": "ab\n", "keys": "li<tab><esc>"}
+{"name": "msel-C-basic", "pane": "file", "text": "aaa\nbbb\nccc\n", "keys": "lC"}
+{"name": "msel-C-basic-tty", "pane": "tty", "text": "aaa\nbbb\nccc\n", "keys": "lC"}
+{"name": "msel-C-count", "pane": "file", "text": "aaa\nbbb\nccc\nddd\n", "keys": "2C"}
+{"name": "msel-C-count-tty", "pane": "tty", "text": "aaa\nbbb\nccc\nddd\n", "keys": "2C"}
+{"name": "msel-C-skips-short", "pane": "file", "text": "abcdef\nab\nabcdef\n", "keys": "4lC"}
+{"name": "msel-C-stops-at-eof", "pane": "file", "text": "aa\nbb\n", "keys": "CCC"}
+{"name": "msel-C-of-selection", "pane": "file", "text": "abcd\nabcd\nabcd\n", "keys": "vllC"}
+{"name": "msel-C-above", "pane": "file", "text": "aaa\nbbb\nccc\n", "keys": "jjl<A-C>"}
+{"name": "msel-C-above-tty", "pane": "tty", "text": "aaa\nbbb\nccc\n", "keys": "jjl<A-C>"}
+{"name": "msel-C-above-at-top", "pane": "file", "text": "aaa\nbbb\n", "keys": "<A-C>"}
+{"name": "msel-C-empty-line", "pane": "file", "text": "ab\n\ncd\n", "keys": "C"}
+{"name": "msel-move-l", "pane": "file", "text": "aaa\nbbb\nccc\n", "keys": "Cl"}
+{"name": "msel-move-l-tty", "pane": "tty", "text": "aaa\nbbb\nccc\n", "keys": "Cl"}
+{"name": "msel-move-jj", "pane": "file", "text": "aaaa\nbbbb\ncccc\ndddd\n", "keys": "2lCkk"}
+{"name": "msel-word", "pane": "file", "text": "one two\nsix seven\n", "keys": "Cw"}
+{"name": "msel-goto-line-end", "pane": "file", "text": "aaa\nbbbbb\n", "keys": "Cgl"}
+{"name": "msel-keep-primary", "pane": "file", "text": "aaa\nbbb\nccc\n", "keys": "CC,"}
+{"name": "msel-keep-primary-tty", "pane": "tty", "text": "aaa\nbbb\nccc\n", "keys": "CC,"}
+{"name": "msel-remove-primary", "pane": "file", "text": "aaa\nbbb\nccc\n", "keys": "CC<A-,>"}
+{"name": "msel-remove-primary-last", "pane": "file", "text": "aaa\nbbb\nccc\n", "keys": "CC)<A-,>"}
+{"name": "msel-rotate-fwd", "pane": "file", "text": "aaa\nbbb\nccc\n", "keys": "CC)"}
+{"name": "msel-rotate-fwd-tty", "pane": "tty", "text": "aaa\nbbb\nccc\n", "keys": "CC)"}
+{"name": "msel-rotate-back", "pane": "file", "text": "aaa\nbbb\nccc\n", "keys": "CC("}
+{"name": "msel-rotate-wrap", "pane": "file", "text": "aaa\nbbb\n", "keys": "C))"}
+{"name": "msel-split-newline", "pane": "file", "text": "aa\nbb\ncc\n", "keys": "%<A-s>"}
+{"name": "msel-split-partial", "pane": "file", "text": "abcd\nefgh\n", "keys": "vjl<A-s>"}
+{"name": "msel-split-partial-tty", "pane": "tty", "text": "abcd\nefgh\n", "keys": "vjl<A-s>"}
+{"name": "msel-merge", "pane": "file", "text": "aaa\nbbb\nccc\n", "keys": "CC<A-minus>"}
+{"name": "msel-merge-consecutive", "pane": "file", "text": "aa\nbb\ncc\n", "keys": "xC<A-_>"}
+{"name": "msel-trim", "pane": "file", "text": " ab \n", "keys": "%_"}
+{"name": "msel-trim-all-ws", "pane": "file", "text": " \n", "keys": "%_"}
+{"name": "msel-trim-multi", "pane": "file", "text": " ab \n cd \n", "keys": "x<A-x>C_"}
+{"name": "msel-insert", "pane": "file", "text": "aa\nbb\n", "keys": "Cix<esc>"}
+{"name": "msel-insert-tty", "pane": "tty", "text": "aa\nbb\n", "keys": "Cix<esc>"}
+{"name": "msel-append", "pane": "file", "text": "aa\nbb\n", "keys": "Caz<esc>"}
+{"name": "msel-insert-two-chars", "pane": "file", "text": "aa\nbb\ncc\n", "keys": "2Cixy<esc>"}
+{"name": "msel-delete", "pane": "file", "text": "abc\nabc\n", "keys": "Cd"}
+{"name": "msel-delete-tty", "pane": "tty", "text": "abc\nabc\n", "keys": "Cd"}
+{"name": "msel-delete-selection", "pane": "file", "text": "abcd\nabcd\n", "keys": "vlCd"}
+{"name": "msel-change", "pane": "file", "text": "abc\nabc\n", "keys": "Ccz<esc>"}
+{"name": "msel-switch-case", "pane": "file", "text": "abc\nabc\n", "keys": "C~"}
+{"name": "msel-switch-case-tty", "pane": "tty", "text": "abc\nabc\n", "keys": "C~"}
+{"name": "msel-uppercase", "pane": "file", "text": "abc\nabc\n", "keys": "vlC<A-`>"}
+{"name": "msel-indent", "pane": "file", "text": "aa\nbb\n", "keys": "C<gt>"}
+{"name": "msel-replace-char", "pane": "file", "text": "abc\nabc\n", "keys": "Crz"}
+{"name": "msel-undo", "pane": "file", "text": "abc\nabc\n", "keys": "Cdu"}
+{"name": "msel-open-below", "pane": "file", "text": "aa\nbb\n", "keys": "Coz<esc>"}
+{"name": "msel-line-select", "pane": "file", "text": "aa\nbb\ncc\n", "keys": "Cx"}
+{"name": "msel-collapse", "pane": "file", "text": "abcd\nabcd\n", "keys": "vllC;"}
+{"name": "msel-flip", "pane": "file", "text": "abcd\nabcd\n", "keys": "vllC<A-;>"}
+{"name": "msel-yank-paste", "pane": "file", "text": "ab\ncd\n", "keys": "Cyp"}
+{"name": "msel-esc-keeps", "pane": "file", "text": "aa\nbb\n", "keys": "C<esc>"}
+{"name": "msel-select-mode", "pane": "file", "text": "abcd\nabcd\n", "keys": "Cvl"}
+{"name": "msel-open-count", "pane": "file", "text": "ab\n", "keys": "2O"}
+{"name": "msel-backspace", "pane": "file", "text": "abc\nabc\n", "keys": "Cla<backspace><esc>"}
+{"name": "msel-backspace-tty", "pane": "tty", "text": "abc\nabc\n", "keys": "Cla<backspace><esc>"}
+{"name": "sel-regex-basic", "pane": "file", "text": "alpha beta alpha\n", "keys": "%sa<ret>"}
+{"name": "sel-regex-basic-tty", "pane": "tty", "text": "alpha beta alpha\n", "keys": "%sa<ret>"}
+{"name": "sel-regex-word", "pane": "file", "text": "alpha beta alpha\n", "keys": "%salpha<ret>"}
+{"name": "sel-regex-class", "pane": "file", "text": "a1b22c333\n", "keys": "%s[0-9]+<ret>"}
+{"name": "sel-regex-class-tty", "pane": "tty", "text": "a1b22c333\n", "keys": "%s[0-9]+<ret>"}
+{"name": "sel-regex-alt", "pane": "file", "text": "cat dog cat\n", "keys": "%scat|dog<ret>"}
+{"name": "sel-regex-plus", "pane": "file", "text": "aa b aaa\n", "keys": "%sa+<ret>"}
+{"name": "sel-regex-star-empty", "pane": "file", "text": "ab\n", "keys": "%sx*<ret>"}
+{"name": "sel-regex-nomatch", "pane": "file", "text": "alpha beta\n", "keys": "%szzz<ret>"}
+{"name": "sel-regex-esc", "pane": "file", "text": "alpha beta alpha\n", "keys": "%sa<esc>"}
+{"name": "sel-regex-empty", "pane": "file", "text": "abc\n", "keys": "%s<ret>"}
+{"name": "sel-regex-invalid", "pane": "file", "text": "abc\n", "keys": "%s[<ret>"}
+{"name": "sel-regex-within-sels", "pane": "file", "text": "aXa\nbXb\n", "keys": "xxsX<ret>"}
+{"name": "sel-regex-within-sels-tty", "pane": "tty", "text": "aXa\nbXb\n", "keys": "xxsX<ret>"}
+{"name": "sel-regex-at-cursors", "pane": "file", "text": "foo bar\nfoo baz\n", "keys": "xxsfoo<ret>"}
+{"name": "sel-regex-narrow", "pane": "file", "text": "alpha beta alpha\n", "keys": "%salpha<ret>sa<ret>"}
+{"name": "sel-regex-select-mode", "pane": "file", "text": "alpha beta\n", "keys": "v5lsa<ret>"}
+{"name": "sel-regex-multiline", "pane": "file", "text": "one\ntwo\nthree\n", "keys": "%so<ret>"}
+{"name": "sel-regex-smart-case", "pane": "file", "text": "Alpha alpha ALPHA\n", "keys": "%salpha<ret>"}
+{"name": "sel-regex-cased", "pane": "file", "text": "Alpha alpha ALPHA\n", "keys": "%sAlpha<ret>"}
+{"name": "sel-regex-boundary", "pane": "file", "text": "cat cats\n", "keys": "%s\\bcat\\b<ret>"}
+{"name": "sel-regex-whole", "pane": "file", "text": "abc\n", "keys": "%sabc<ret>"}
+{"name": "sel-regex-then-motion", "pane": "file", "text": "foo bar foo baz\n", "keys": "%sfoo<ret>w"}
+{"name": "sel-regex-then-delete", "pane": "file", "text": "a1b2c3\n", "keys": "%s[0-9]<ret>d"}
+{"name": "sel-regex-dot-newline", "pane": "file", "text": "ab\n", "keys": "%s.<ret>"}
+{"name": "sel-regex-caret", "pane": "file", "text": "ab\nab\n", "keys": "%s^a<ret>"}
+{"name": "split-regex-comma", "pane": "file", "text": "a,b,c\n", "keys": "%S,<ret>"}
+{"name": "split-regex-comma-tty", "pane": "tty", "text": "a,b,c\n", "keys": "%S,<ret>"}
+{"name": "split-regex-lead", "pane": "file", "text": ",a,b\n", "keys": "%S,<ret>"}
+{"name": "split-regex-trail", "pane": "file", "text": "a,b,\n", "keys": "%S,<ret>"}
+{"name": "split-regex-adjacent", "pane": "file", "text": "a,,b\n", "keys": "%S,<ret>"}
+{"name": "split-regex-ws", "pane": "file", "text": "one two three\n", "keys": "%S\\s+<ret>"}
+{"name": "split-regex-ws-tty", "pane": "tty", "text": "one two three\n", "keys": "%S\\s+<ret>"}
+{"name": "split-regex-nomatch", "pane": "file", "text": "abc\n", "keys": "%Sz<ret>"}
+{"name": "split-regex-newline", "pane": "file", "text": "one\ntwo\nthree\n", "keys": "%S\\n<ret>"}
+{"name": "split-regex-esc", "pane": "file", "text": "a,b,c\n", "keys": "%S,<esc>"}
+{"name": "split-regex-within-sels", "pane": "file", "text": "aXa\nbXb\n", "keys": "xxSX<ret>"}
+{"name": "split-regex-end", "pane": "file", "text": "abc\n", "keys": "%Sc<ret>"}
+{"name": "comment-line", "pane": "file", "text": "alpha\nbeta\n", "keys": "<C-c>"}
+{"name": "comment-toggle-back", "pane": "file", "text": "alpha\nbeta\n", "keys": "<C-c><C-c>"}
+{"name": "comment-selection", "pane": "file", "text": "alpha\nbeta\ngamma\n", "keys": "xx<C-c>"}
+{"name": "comment-indent", "pane": "file", "text": " alpha\n beta\n", "keys": "%<C-c>"}
+{"name": "comment-indent-round-trip", "pane": "file", "text": " alpha\n beta\n", "keys": "%<C-c>%<C-c>"}
+{"name": "comment-blank-skipped", "pane": "file", "text": "alpha\n\nbeta\n", "keys": "%<C-c>"}
+{"name": "comment-partial", "pane": "file", "text": "# alpha\nbeta\ngamma\n", "keys": "%<C-c>"}
+{"name": "comment-uncomment", "pane": "file", "text": "# alpha\n# beta\n", "keys": "%<C-c>"}
+{"name": "comment-margin0", "pane": "file", "text": "#alpha\n# beta\n", "keys": "%<C-c>"}
+{"name": "comment-token-only", "pane": "file", "text": "#\n#\n", "keys": "%<C-c>"}
+{"name": "comment-uncomment-deep", "pane": "file", "text": "# alpha\n # beta\n", "keys": "%<C-c>"}
+{"name": "comment-tab-indent", "pane": "file", "text": "\talpha\n\tbeta\n", "keys": "%<C-c>"}
+{"name": "comment-other-token", "pane": "file", "text": "// alpha\n// beta\n", "keys": "%<C-c>"}
+{"name": "comment-noeol", "pane": "file", "text": "alpha", "keys": "<C-c>"}
+{"name": "comment-empty", "pane": "file", "text": "\n", "keys": "<C-c>"}
+{"name": "comment-only-blank", "pane": "file", "text": " \n\t\n", "keys": "%<C-c>"}
+{"name": "comment-select-mode", "pane": "file", "text": "alpha\nbeta\n", "keys": "vj<C-c>"}
+{"name": "comment-count-ignored", "pane": "file", "text": "alpha\nbeta\ngamma\n", "keys": "3<C-c>"}
+{"name": "comment-undo", "pane": "file", "text": "alpha\nbeta\n", "keys": "%<C-c>u"}
+{"name": "comment-multi-cursor", "pane": "file", "text": "alpha\nbeta\ngamma\n", "keys": "C<C-c>"}
+{"name": "comment-same-line-cursors", "pane": "file", "text": "alpha beta\n", "keys": "wC<C-c>"}
+{"name": "comment-cursor-gap", "pane": "file", "text": "a\nb\nc\nd\n", "keys": "2C<C-c>"}
+{"name": "comment-split-cursors", "pane": "file", "text": "alpha\nbeta\ngamma\n", "keys": "%<A-s><C-c>"}
+{"name": "comment-last-line", "pane": "file", "text": "alpha\nbeta\n", "keys": "jj<C-c>"}
+{"name": "comment-then-insert", "pane": "file", "text": "alpha\n", "keys": "<C-c>iz<esc>"}
+{"name": "comment-after-sel-regex", "pane": "file", "text": "alpha\nbeta\ngamma\n", "keys": "%sa<ret><C-c>"}
+{"name": "comment-after-split-regex", "pane": "file", "text": "a,b\nc,d\n", "keys": "%S,<ret><C-c>"}
diff --git a/test/hxcases/goldens.jsonl b/test/hxcases/goldens.jsonl
index baa45d34..eee7294b 100644
--- a/test/hxcases/goldens.jsonl
+++ b/test/hxcases/goldens.jsonl
@@ -323,7 +323,7 @@
{"name":"o-basic","text":"ab\nx\n","mode":"normal","cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1}}
{"name":"o-copies-indent-level","text":" foo\n \n","mode":"insert","cursor":{"row":1,"col":4},"anchor":{"row":1,"col":4}}
{"name":"o-partial-indent-drops","text":" foo\n\n","mode":"insert","cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0}}
-{"name":"o-count","text":"ab\n\n\n","mode":"normal","cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0}}
+{"name":"o-count","text":"ab\n\n\n","mode":"normal","cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0},"sels":[{"cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0}},{"cursor":{"row":2,"col":0},"anchor":{"row":2,"col":0}}],"primary":0}
{"name":"O-basic","text":"aa\nx\nbb\n","mode":"normal","cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1}}
{"name":"O-copies-indent","text":" \n foo\n","mode":"insert","cursor":{"row":0,"col":4},"anchor":{"row":0,"col":4}}
{"name":"i-type-esc","text":"hello alpha\n","mode":"normal","cursor":{"row":0,"col":6},"anchor":{"row":0,"col":6}}
@@ -358,3 +358,124 @@
{"name":"ins-arrows","text":"abc\ndxef\n","mode":"normal","cursor":{"row":1,"col":2},"anchor":{"row":1,"col":2}}
{"name":"ins-tab-col0","text":" ab\n","mode":"normal","cursor":{"row":0,"col":4},"anchor":{"row":0,"col":4}}
{"name":"ins-tab-mid","text":"a b\n","mode":"normal","cursor":{"row":0,"col":4},"anchor":{"row":0,"col":4}}
+{"name":"msel-C-basic","text":"aaa\nbbb\nccc\n","mode":"normal","cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1},"sels":[{"cursor":{"row":0,"col":1},"anchor":{"row":0,"col":1}},{"cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1}}],"primary":1}
+{"name":"msel-C-basic-tty","text":"aaa\nbbb\nccc\n","mode":"normal","cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1},"sels":[{"cursor":{"row":0,"col":1},"anchor":{"row":0,"col":1}},{"cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1}}],"primary":1}
+{"name":"msel-C-count","text":"aaa\nbbb\nccc\nddd\n","mode":"normal","cursor":{"row":2,"col":0},"anchor":{"row":2,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0}},{"cursor":{"row":2,"col":0},"anchor":{"row":2,"col":0}}],"primary":2}
+{"name":"msel-C-count-tty","text":"aaa\nbbb\nccc\nddd\n","mode":"normal","cursor":{"row":2,"col":0},"anchor":{"row":2,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0}},{"cursor":{"row":2,"col":0},"anchor":{"row":2,"col":0}}],"primary":2}
+{"name":"msel-C-skips-short","text":"abcdef\nab\nabcdef\n","mode":"normal","cursor":{"row":2,"col":4},"anchor":{"row":2,"col":4},"sels":[{"cursor":{"row":0,"col":4},"anchor":{"row":0,"col":4}},{"cursor":{"row":2,"col":4},"anchor":{"row":2,"col":4}}],"primary":1}
+{"name":"msel-C-stops-at-eof","text":"aa\nbb\n","mode":"normal","cursor":{"row":2,"col":0},"anchor":{"row":2,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0}},{"cursor":{"row":2,"col":0},"anchor":{"row":2,"col":0}}],"primary":2}
+{"name":"msel-C-of-selection","text":"abcd\nabcd\nabcd\n","mode":"select","cursor":{"row":1,"col":2},"anchor":{"row":1,"col":0},"sels":[{"cursor":{"row":0,"col":2},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":2},"anchor":{"row":1,"col":0}}],"primary":1}
+{"name":"msel-C-above","text":"aaa\nbbb\nccc\n","mode":"normal","cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1},"sels":[{"cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1}},{"cursor":{"row":2,"col":1},"anchor":{"row":2,"col":1}}],"primary":0}
+{"name":"msel-C-above-tty","text":"aaa\nbbb\nccc\n","mode":"normal","cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1},"sels":[{"cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1}},{"cursor":{"row":2,"col":1},"anchor":{"row":2,"col":1}}],"primary":0}
+{"name":"msel-C-above-at-top","text":"aaa\nbbb\n","mode":"normal","cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}}
+{"name":"msel-C-empty-line","text":"ab\n\ncd\n","mode":"normal","cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0}}],"primary":1}
+{"name":"msel-move-l","text":"aaa\nbbb\nccc\n","mode":"normal","cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1},"sels":[{"cursor":{"row":0,"col":1},"anchor":{"row":0,"col":1}},{"cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1}}],"primary":1}
+{"name":"msel-move-l-tty","text":"aaa\nbbb\nccc\n","mode":"normal","cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1},"sels":[{"cursor":{"row":0,"col":1},"anchor":{"row":0,"col":1}},{"cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1}}],"primary":1}
+{"name":"msel-move-jj","text":"aaaa\nbbbb\ncccc\ndddd\n","mode":"normal","cursor":{"row":0,"col":2},"anchor":{"row":0,"col":2}}
+{"name":"msel-word","text":"one two\nsix seven\n","mode":"normal","cursor":{"row":1,"col":3},"anchor":{"row":1,"col":0},"sels":[{"cursor":{"row":0,"col":3},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":3},"anchor":{"row":1,"col":0}}],"primary":1}
+{"name":"msel-goto-line-end","text":"aaa\nbbbbb\n","mode":"normal","cursor":{"row":1,"col":4},"anchor":{"row":1,"col":4},"sels":[{"cursor":{"row":0,"col":2},"anchor":{"row":0,"col":2}},{"cursor":{"row":1,"col":4},"anchor":{"row":1,"col":4}}],"primary":1}
+{"name":"msel-keep-primary","text":"aaa\nbbb\nccc\n","mode":"normal","cursor":{"row":2,"col":0},"anchor":{"row":2,"col":0}}
+{"name":"msel-keep-primary-tty","text":"aaa\nbbb\nccc\n","mode":"normal","cursor":{"row":2,"col":0},"anchor":{"row":2,"col":0}}
+{"name":"msel-remove-primary","text":"aaa\nbbb\nccc\n","mode":"normal","cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0}}],"primary":1}
+{"name":"msel-remove-primary-last","text":"aaa\nbbb\nccc\n","mode":"normal","cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0},"sels":[{"cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0}},{"cursor":{"row":2,"col":0},"anchor":{"row":2,"col":0}}],"primary":0}
+{"name":"msel-rotate-fwd","text":"aaa\nbbb\nccc\n","mode":"normal","cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0}},{"cursor":{"row":2,"col":0},"anchor":{"row":2,"col":0}}],"primary":0}
+{"name":"msel-rotate-fwd-tty","text":"aaa\nbbb\nccc\n","mode":"normal","cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0}},{"cursor":{"row":2,"col":0},"anchor":{"row":2,"col":0}}],"primary":0}
+{"name":"msel-rotate-back","text":"aaa\nbbb\nccc\n","mode":"normal","cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0}},{"cursor":{"row":2,"col":0},"anchor":{"row":2,"col":0}}],"primary":1}
+{"name":"msel-rotate-wrap","text":"aaa\nbbb\n","mode":"normal","cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0}}],"primary":1}
+{"name":"msel-split-newline","text":"aa\nbb\ncc\n","mode":"normal","cursor":{"row":0,"col":1},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":1},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":1},"anchor":{"row":1,"col":0}},{"cursor":{"row":2,"col":1},"anchor":{"row":2,"col":0}}],"primary":0}
+{"name":"msel-split-partial","text":"abcd\nefgh\n","mode":"select","cursor":{"row":0,"col":3},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":3},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":1},"anchor":{"row":1,"col":0}}],"primary":0}
+{"name":"msel-split-partial-tty","text":"abcd\nefgh\n","mode":"select","cursor":{"row":0,"col":3},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":3},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":1},"anchor":{"row":1,"col":0}}],"primary":0}
+{"name":"msel-merge","text":"aaa\nbbb\nccc\n","mode":"normal","cursor":{"row":2,"col":0},"anchor":{"row":0,"col":0}}
+{"name":"msel-merge-consecutive","text":"aa\nbb\ncc\n","mode":"normal","cursor":{"row":1,"col":2},"anchor":{"row":0,"col":0}}
+{"name":"msel-trim","text":" ab \n","mode":"normal","cursor":{"row":0,"col":3},"anchor":{"row":0,"col":2}}
+{"name":"msel-trim-all-ws","text":" \n","mode":"normal","cursor":{"row":0,"col":3},"anchor":{"row":0,"col":3}}
+{"name":"msel-trim-multi","text":" ab \n cd \n","mode":"normal","cursor":{"row":1,"col":2},"anchor":{"row":1,"col":1},"sels":[{"cursor":{"row":0,"col":2},"anchor":{"row":0,"col":1}},{"cursor":{"row":1,"col":2},"anchor":{"row":1,"col":1}}],"primary":1}
+{"name":"msel-insert","text":"xaa\nxbb\n","mode":"normal","cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1},"sels":[{"cursor":{"row":0,"col":1},"anchor":{"row":0,"col":1}},{"cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1}}],"primary":1}
+{"name":"msel-insert-tty","text":"xaa\nxbb\n","mode":"normal","cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1},"sels":[{"cursor":{"row":0,"col":1},"anchor":{"row":0,"col":1}},{"cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1}}],"primary":1}
+{"name":"msel-append","text":"aza\nbzb\n","mode":"normal","cursor":{"row":1,"col":1},"anchor":{"row":1,"col":0},"sels":[{"cursor":{"row":0,"col":1},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":1},"anchor":{"row":1,"col":0}}],"primary":1}
+{"name":"msel-insert-two-chars","text":"xyaa\nxybb\nxycc\n","mode":"normal","cursor":{"row":2,"col":2},"anchor":{"row":2,"col":2},"sels":[{"cursor":{"row":0,"col":2},"anchor":{"row":0,"col":2}},{"cursor":{"row":1,"col":2},"anchor":{"row":1,"col":2}},{"cursor":{"row":2,"col":2},"anchor":{"row":2,"col":2}}],"primary":2}
+{"name":"msel-delete","text":"bc\nbc\n","mode":"normal","cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0}}],"primary":1}
+{"name":"msel-delete-tty","text":"bc\nbc\n","mode":"normal","cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0}}],"primary":1}
+{"name":"msel-delete-selection","text":"cd\ncd\n","mode":"normal","cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0}}],"primary":1}
+{"name":"msel-change","text":"zbc\nzbc\n","mode":"normal","cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1},"sels":[{"cursor":{"row":0,"col":1},"anchor":{"row":0,"col":1}},{"cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1}}],"primary":1}
+{"name":"msel-switch-case","text":"Abc\nAbc\n","mode":"normal","cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0}}],"primary":1}
+{"name":"msel-switch-case-tty","text":"Abc\nAbc\n","mode":"normal","cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0}}],"primary":1}
+{"name":"msel-uppercase","text":"ABc\nABc\n","mode":"normal","cursor":{"row":1,"col":1},"anchor":{"row":1,"col":0},"sels":[{"cursor":{"row":0,"col":1},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":1},"anchor":{"row":1,"col":0}}],"primary":1}
+{"name":"msel-indent","text":" aa\n bb\n","mode":"normal","cursor":{"row":1,"col":4},"anchor":{"row":1,"col":4},"sels":[{"cursor":{"row":0,"col":4},"anchor":{"row":0,"col":4}},{"cursor":{"row":1,"col":4},"anchor":{"row":1,"col":4}}],"primary":1}
+{"name":"msel-replace-char","text":"zbc\nzbc\n","mode":"normal","cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0}}],"primary":1}
+{"name":"msel-undo","text":"abc\nabc\n","mode":"normal","cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0}}],"primary":1}
+{"name":"msel-open-below","text":"aa\nz\nbb\nz\n","mode":"normal","cursor":{"row":3,"col":1},"anchor":{"row":3,"col":1},"sels":[{"cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1}},{"cursor":{"row":3,"col":1},"anchor":{"row":3,"col":1}}],"primary":1}
+{"name":"msel-line-select","text":"aa\nbb\ncc\n","mode":"normal","cursor":{"row":1,"col":2},"anchor":{"row":1,"col":0},"sels":[{"cursor":{"row":0,"col":2},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":2},"anchor":{"row":1,"col":0}}],"primary":1}
+{"name":"msel-collapse","text":"abcd\nabcd\n","mode":"select","cursor":{"row":1,"col":2},"anchor":{"row":1,"col":2},"sels":[{"cursor":{"row":0,"col":2},"anchor":{"row":0,"col":2}},{"cursor":{"row":1,"col":2},"anchor":{"row":1,"col":2}}],"primary":1}
+{"name":"msel-flip","text":"abcd\nabcd\n","mode":"select","cursor":{"row":1,"col":0},"anchor":{"row":1,"col":2},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":2}},{"cursor":{"row":1,"col":0},"anchor":{"row":1,"col":2}}],"primary":1}
+{"name":"msel-yank-paste","text":"aab\nccd\n","mode":"normal","cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1},"sels":[{"cursor":{"row":0,"col":1},"anchor":{"row":0,"col":1}},{"cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1}}],"primary":1}
+{"name":"msel-esc-keeps","text":"aa\nbb\n","mode":"normal","cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0}}],"primary":1}
+{"name":"msel-select-mode","text":"abcd\nabcd\n","mode":"select","cursor":{"row":1,"col":1},"anchor":{"row":1,"col":0},"sels":[{"cursor":{"row":0,"col":1},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":1},"anchor":{"row":1,"col":0}}],"primary":1}
+{"name":"msel-open-count","text":"\n\nab\n","mode":"insert","cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0}}],"primary":0}
+{"name":"msel-backspace","text":"ac\nac\n","mode":"normal","cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1},"sels":[{"cursor":{"row":0,"col":1},"anchor":{"row":0,"col":1}},{"cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1}}],"primary":1}
+{"name":"msel-backspace-tty","text":"ac\nac\n","mode":"normal","cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1},"sels":[{"cursor":{"row":0,"col":1},"anchor":{"row":0,"col":1}},{"cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1}}],"primary":1}
+{"name":"sel-regex-basic","text":"alpha beta alpha\n","mode":"normal","cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":0,"col":4},"anchor":{"row":0,"col":4}},{"cursor":{"row":0,"col":9},"anchor":{"row":0,"col":9}},{"cursor":{"row":0,"col":11},"anchor":{"row":0,"col":11}},{"cursor":{"row":0,"col":15},"anchor":{"row":0,"col":15}}],"primary":0}
+{"name":"sel-regex-basic-tty","text":"alpha beta alpha\n","mode":"normal","cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":0,"col":4},"anchor":{"row":0,"col":4}},{"cursor":{"row":0,"col":9},"anchor":{"row":0,"col":9}},{"cursor":{"row":0,"col":11},"anchor":{"row":0,"col":11}},{"cursor":{"row":0,"col":15},"anchor":{"row":0,"col":15}}],"primary":0}
+{"name":"sel-regex-word","text":"alpha beta alpha\n","mode":"normal","cursor":{"row":0,"col":4},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":4},"anchor":{"row":0,"col":0}},{"cursor":{"row":0,"col":15},"anchor":{"row":0,"col":11}}],"primary":0}
+{"name":"sel-regex-class","text":"a1b22c333\n","mode":"normal","cursor":{"row":0,"col":1},"anchor":{"row":0,"col":1},"sels":[{"cursor":{"row":0,"col":1},"anchor":{"row":0,"col":1}},{"cursor":{"row":0,"col":4},"anchor":{"row":0,"col":3}},{"cursor":{"row":0,"col":8},"anchor":{"row":0,"col":6}}],"primary":0}
+{"name":"sel-regex-class-tty","text":"a1b22c333\n","mode":"normal","cursor":{"row":0,"col":1},"anchor":{"row":0,"col":1},"sels":[{"cursor":{"row":0,"col":1},"anchor":{"row":0,"col":1}},{"cursor":{"row":0,"col":4},"anchor":{"row":0,"col":3}},{"cursor":{"row":0,"col":8},"anchor":{"row":0,"col":6}}],"primary":0}
+{"name":"sel-regex-alt","text":"cat dog cat\n","mode":"normal","cursor":{"row":0,"col":2},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":2},"anchor":{"row":0,"col":0}},{"cursor":{"row":0,"col":6},"anchor":{"row":0,"col":4}},{"cursor":{"row":0,"col":10},"anchor":{"row":0,"col":8}}],"primary":0}
+{"name":"sel-regex-plus","text":"aa b aaa\n","mode":"normal","cursor":{"row":0,"col":1},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":1},"anchor":{"row":0,"col":0}},{"cursor":{"row":0,"col":7},"anchor":{"row":0,"col":5}}],"primary":0}
+{"name":"sel-regex-star-empty","text":"ab\n","mode":"normal","cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":0,"col":1},"anchor":{"row":0,"col":1}},{"cursor":{"row":0,"col":2},"anchor":{"row":0,"col":2}}],"primary":0}
+{"name":"sel-regex-nomatch","text":"alpha beta\n","mode":"normal","cursor":{"row":0,"col":10},"anchor":{"row":0,"col":0}}
+{"name":"sel-regex-esc","text":"alpha beta alpha\n","mode":"normal","cursor":{"row":0,"col":16},"anchor":{"row":0,"col":0}}
+{"name":"sel-regex-empty","text":"abc\n","mode":"normal","cursor":{"row":0,"col":3},"anchor":{"row":0,"col":0}}
+{"name":"sel-regex-invalid","text":"abc\n","mode":"normal","cursor":{"row":0,"col":3},"anchor":{"row":0,"col":0}}
+{"name":"sel-regex-within-sels","text":"aXa\nbXb\n","mode":"normal","cursor":{"row":0,"col":1},"anchor":{"row":0,"col":1},"sels":[{"cursor":{"row":0,"col":1},"anchor":{"row":0,"col":1}},{"cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1}}],"primary":0}
+{"name":"sel-regex-within-sels-tty","text":"aXa\nbXb\n","mode":"normal","cursor":{"row":0,"col":1},"anchor":{"row":0,"col":1},"sels":[{"cursor":{"row":0,"col":1},"anchor":{"row":0,"col":1}},{"cursor":{"row":1,"col":1},"anchor":{"row":1,"col":1}}],"primary":0}
+{"name":"sel-regex-at-cursors","text":"foo bar\nfoo baz\n","mode":"normal","cursor":{"row":0,"col":2},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":2},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":2},"anchor":{"row":1,"col":0}}],"primary":0}
+{"name":"sel-regex-narrow","text":"alpha beta alpha\n","mode":"normal","cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":0,"col":4},"anchor":{"row":0,"col":4}},{"cursor":{"row":0,"col":11},"anchor":{"row":0,"col":11}},{"cursor":{"row":0,"col":15},"anchor":{"row":0,"col":15}}],"primary":0}
+{"name":"sel-regex-select-mode","text":"alpha beta\n","mode":"select","cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":0,"col":4},"anchor":{"row":0,"col":4}}],"primary":0}
+{"name":"sel-regex-multiline","text":"one\ntwo\nthree\n","mode":"normal","cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":2},"anchor":{"row":1,"col":2}}],"primary":0}
+{"name":"sel-regex-smart-case","text":"Alpha alpha ALPHA\n","mode":"normal","cursor":{"row":0,"col":4},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":4},"anchor":{"row":0,"col":0}},{"cursor":{"row":0,"col":10},"anchor":{"row":0,"col":6}},{"cursor":{"row":0,"col":16},"anchor":{"row":0,"col":12}}],"primary":0}
+{"name":"sel-regex-cased","text":"Alpha alpha ALPHA\n","mode":"normal","cursor":{"row":0,"col":4},"anchor":{"row":0,"col":0}}
+{"name":"sel-regex-boundary","text":"cat cats\n","mode":"normal","cursor":{"row":0,"col":2},"anchor":{"row":0,"col":0}}
+{"name":"sel-regex-whole","text":"abc\n","mode":"normal","cursor":{"row":0,"col":2},"anchor":{"row":0,"col":0}}
+{"name":"sel-regex-then-motion","text":"foo bar foo baz\n","mode":"normal","cursor":{"row":0,"col":3},"anchor":{"row":0,"col":2},"sels":[{"cursor":{"row":0,"col":3},"anchor":{"row":0,"col":2}},{"cursor":{"row":0,"col":11},"anchor":{"row":0,"col":10}}],"primary":0}
+{"name":"sel-regex-then-delete","text":"abc\n","mode":"normal","cursor":{"row":0,"col":1},"anchor":{"row":0,"col":1},"sels":[{"cursor":{"row":0,"col":1},"anchor":{"row":0,"col":1}},{"cursor":{"row":0,"col":2},"anchor":{"row":0,"col":2}},{"cursor":{"row":0,"col":3},"anchor":{"row":0,"col":3}}],"primary":0}
+{"name":"sel-regex-dot-newline","text":"ab\n","mode":"normal","cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":0,"col":1},"anchor":{"row":0,"col":1}}],"primary":0}
+{"name":"sel-regex-caret","text":"ab\nab\n","mode":"normal","cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":0},"anchor":{"row":1,"col":0}}],"primary":0}
+{"name":"split-regex-comma","text":"a,b,c\n","mode":"normal","cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":0,"col":2},"anchor":{"row":0,"col":2}},{"cursor":{"row":0,"col":5},"anchor":{"row":0,"col":4}}],"primary":0}
+{"name":"split-regex-comma-tty","text":"a,b,c\n","mode":"normal","cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":0,"col":2},"anchor":{"row":0,"col":2}},{"cursor":{"row":0,"col":5},"anchor":{"row":0,"col":4}}],"primary":0}
+{"name":"split-regex-lead","text":",a,b\n","mode":"normal","cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":0,"col":1},"anchor":{"row":0,"col":1}},{"cursor":{"row":0,"col":4},"anchor":{"row":0,"col":3}}],"primary":0}
+{"name":"split-regex-trail","text":"a,b,\n","mode":"normal","cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":0,"col":2},"anchor":{"row":0,"col":2}},{"cursor":{"row":0,"col":4},"anchor":{"row":0,"col":4}}],"primary":0}
+{"name":"split-regex-adjacent","text":"a,,b\n","mode":"normal","cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":0,"col":2},"anchor":{"row":0,"col":2}},{"cursor":{"row":0,"col":4},"anchor":{"row":0,"col":3}}],"primary":0}
+{"name":"split-regex-ws","text":"one two three\n","mode":"normal","cursor":{"row":0,"col":2},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":2},"anchor":{"row":0,"col":0}},{"cursor":{"row":0,"col":6},"anchor":{"row":0,"col":4}},{"cursor":{"row":0,"col":13},"anchor":{"row":0,"col":9}}],"primary":0}
+{"name":"split-regex-ws-tty","text":"one two three\n","mode":"normal","cursor":{"row":0,"col":2},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":2},"anchor":{"row":0,"col":0}},{"cursor":{"row":0,"col":6},"anchor":{"row":0,"col":4}},{"cursor":{"row":0,"col":13},"anchor":{"row":0,"col":9}}],"primary":0}
+{"name":"split-regex-nomatch","text":"abc\n","mode":"normal","cursor":{"row":0,"col":3},"anchor":{"row":0,"col":0}}
+{"name":"split-regex-newline","text":"one\ntwo\nthree\n","mode":"normal","cursor":{"row":0,"col":2},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":2},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":2},"anchor":{"row":1,"col":0}},{"cursor":{"row":2,"col":4},"anchor":{"row":2,"col":0}}],"primary":0}
+{"name":"split-regex-esc","text":"a,b,c\n","mode":"normal","cursor":{"row":0,"col":5},"anchor":{"row":0,"col":0}}
+{"name":"split-regex-within-sels","text":"aXa\nbXb\n","mode":"normal","cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}},{"cursor":{"row":1,"col":0},"anchor":{"row":0,"col":2}},{"cursor":{"row":1,"col":3},"anchor":{"row":1,"col":2}}],"primary":0}
+{"name":"split-regex-end","text":"abc\n","mode":"normal","cursor":{"row":0,"col":1},"anchor":{"row":0,"col":0},"sels":[{"cursor":{"row":0,"col":1},"anchor":{"row":0,"col":0}},{"cursor":{"row":0,"col":3},"anchor":{"row":0,"col":3}}],"primary":0}
+{"name":"comment-line","text":"# alpha\nbeta\n","mode":"normal","cursor":{"row":0,"col":2},"anchor":{"row":0,"col":2}}
+{"name":"comment-toggle-back","text":"alpha\nbeta\n","mode":"normal","cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}}
+{"name":"comment-selection","text":"# alpha\n# beta\ngamma\n","mode":"normal","cursor":{"row":1,"col":6},"anchor":{"row":0,"col":2}}
+{"name":"comment-indent","text":" # alpha\n # beta\n","mode":"normal","cursor":{"row":1,"col":10},"anchor":{"row":0,"col":0}}
+{"name":"comment-indent-round-trip","text":" alpha\n beta\n","mode":"normal","cursor":{"row":1,"col":8},"anchor":{"row":0,"col":0}}
+{"name":"comment-blank-skipped","text":"# alpha\n\n# beta\n","mode":"normal","cursor":{"row":2,"col":6},"anchor":{"row":0,"col":2}}
+{"name":"comment-partial","text":"# # alpha\n# beta\n# gamma\n","mode":"normal","cursor":{"row":2,"col":7},"anchor":{"row":0,"col":2}}
+{"name":"comment-uncomment","text":"alpha\nbeta\n","mode":"normal","cursor":{"row":1,"col":4},"anchor":{"row":0,"col":0}}
+{"name":"comment-margin0","text":"alpha\n beta\n","mode":"normal","cursor":{"row":1,"col":5},"anchor":{"row":0,"col":0}}
+{"name":"comment-token-only","text":"\n\n","mode":"normal","cursor":{"row":1,"col":0},"anchor":{"row":0,"col":0}}
+{"name":"comment-uncomment-deep","text":"alpha\n # beta\n","mode":"normal","cursor":{"row":1,"col":8},"anchor":{"row":0,"col":0}}
+{"name":"comment-tab-indent","text":"\t# alpha\n\t# beta\n","mode":"normal","cursor":{"row":1,"col":7},"anchor":{"row":0,"col":0}}
+{"name":"comment-other-token","text":"# // alpha\n# // beta\n","mode":"normal","cursor":{"row":1,"col":9},"anchor":{"row":0,"col":2}}
+{"name":"comment-noeol","text":"# alpha","mode":"normal","cursor":{"row":0,"col":2},"anchor":{"row":0,"col":2}}
+{"name":"comment-empty","text":"\n","mode":"normal","cursor":{"row":0,"col":0},"anchor":{"row":0,"col":0}}
+{"name":"comment-only-blank","text":" \n\t\n","mode":"normal","cursor":{"row":1,"col":1},"anchor":{"row":0,"col":0}}
+{"name":"comment-select-mode","text":"# alpha\n# beta\n","mode":"normal","cursor":{"row":1,"col":2},"anchor":{"row":0,"col":2}}
+{"name":"comment-count-ignored","text":"# alpha\nbeta\ngamma\n","mode":"normal","cursor":{"row":0,"col":2},"anchor":{"row":0,"col":2}}
+{"name":"comment-undo","text":"alpha\nbeta\n","mode":"normal","cursor":{"row":1,"col":4},"anchor":{"row":0,"col":0}}
+{"name":"comment-multi-cursor","text":"# alpha\n# beta\ngamma\n","mode":"normal","cursor":{"row":1,"col":2},"anchor":{"row":1,"col":2},"sels":[{"cursor":{"row":0,"col":2},"anchor":{"row":0,"col":2}},{"cursor":{"row":1,"col":2},"anchor":{"row":1,"col":2}}],"primary":1}
+{"name":"comment-same-line-cursors","text":"# alpha beta\n","mode":"normal","cursor":{"row":0,"col":7},"anchor":{"row":0,"col":2}}
+{"name":"comment-cursor-gap","text":"# a\n# b\n# c\nd\n","mode":"normal","cursor":{"row":2,"col":2},"anchor":{"row":2,"col":2},"sels":[{"cursor":{"row":0,"col":2},"anchor":{"row":0,"col":2}},{"cursor":{"row":1,"col":2},"anchor":{"row":1,"col":2}},{"cursor":{"row":2,"col":2},"anchor":{"row":2,"col":2}}],"primary":2}
+{"name":"comment-split-cursors","text":"# alpha\n# beta\n# gamma\n","mode":"normal","cursor":{"row":0,"col":6},"anchor":{"row":0,"col":2},"sels":[{"cursor":{"row":0,"col":6},"anchor":{"row":0,"col":2}},{"cursor":{"row":1,"col":5},"anchor":{"row":1,"col":2}},{"cursor":{"row":2,"col":6},"anchor":{"row":2,"col":2}}],"primary":0}
+{"name":"comment-last-line","text":"alpha\nbeta\n","mode":"normal","cursor":{"row":2,"col":0},"anchor":{"row":2,"col":0}}
+{"name":"comment-then-insert","text":"# zalpha\n","mode":"normal","cursor":{"row":0,"col":3},"anchor":{"row":0,"col":3}}
+{"name":"comment-after-sel-regex","text":"# alpha\n# beta\n# gamma\n","mode":"normal","cursor":{"row":0,"col":2},"anchor":{"row":0,"col":2},"sels":[{"cursor":{"row":0,"col":2},"anchor":{"row":0,"col":2}},{"cursor":{"row":0,"col":6},"anchor":{"row":0,"col":6}},{"cursor":{"row":1,"col":5},"anchor":{"row":1,"col":5}},{"cursor":{"row":2,"col":3},"anchor":{"row":2,"col":3}},{"cursor":{"row":2,"col":6},"anchor":{"row":2,"col":6}}],"primary":0}
+{"name":"comment-after-split-regex","text":"# a,b\n# c,d\n","mode":"normal","cursor":{"row":0,"col":2},"anchor":{"row":0,"col":2},"sels":[{"cursor":{"row":0,"col":2},"anchor":{"row":0,"col":2}},{"cursor":{"row":1,"col":2},"anchor":{"row":0,"col":4}},{"cursor":{"row":1,"col":5},"anchor":{"row":1,"col":4}}],"primary":0}
diff --git a/test/hxcases/parity-waivers.jsonl b/test/hxcases/parity-waivers.jsonl
index d48f4670..1a6fd269 100644
--- a/test/hxcases/parity-waivers.jsonl
+++ b/test/hxcases/parity-waivers.jsonl
@@ -8,3 +8,6 @@
{"name":"zj-noop-in-band","reason":"viewport geometry: same as zj-snaps-cursor - the terminal's view cannot scroll past the live grid bottom, so the cursor's band differs by one row."}
{"name":"zk-bottom-snap","reason":"viewport geometry: zk scrolls the view up one row and snaps the cursor; the terminal's starting view offset differs because it could not scroll as far down in the first place."}
{"name":"unindent-tab","reason":"the case text is not representable in a pty pane: a literal TAB fed through the terminal is expanded to the next tab stop by the emulator, so the shell rows hold spaces and never the tab the file pane unindents. Not an editing divergence - unindent-basic, unindent-partial, unindent-realign and unindent-cursor all match; only a byte a terminal cannot hold verbatim."}
+{"name": "comment-tab-indent", "reason": "the case text is not representable in a pty pane: a literal TAB fed through the terminal is expanded to the next tab stop by the emulator, so the shell rows hold spaces and the comment token goes in at a different column. Exactly the unindent-tab waiver's class - not an editing divergence; comment-indent, comment-uncomment-deep and the rest of the comment-* family match."}
+{"name": "comment-noeol", "reason": "the case text is not representable in a pty pane: it is the one case in the corpus with NO trailing newline (that is the point - it pins helix's last-line handling), and the tty harness re-adds the newline ghostty's dump trims off the cursor row. The comment itself is identical on both sides; only the missing final byte differs."}
+{"name": "comment-only-blank", "reason": "the case text is not representable in a pty pane: a literal TAB (unindent-tab's class) plus an all-whitespace last row, which the terminal dump trims off entirely. Both panes correctly change nothing - toggle_comments skips blank lines - so the only difference is what the pty could hold in the first place."}
diff --git a/test/hxcases/waivers.jsonl b/test/hxcases/waivers.jsonl
index 8110a84c..6b2f612c 100644
--- a/test/hxcases/waivers.jsonl
+++ b/test/hxcases/waivers.jsonl
@@ -1,2 +1,6 @@
{"name":"wiX-edit-drops-sel","reason":"helix maps the selection through every insert-mode edit; pardes drops it on the first edit instead (anchor-only divergence, text+cursor+mode match). Re-judged phase 5 and kept: mapping the anchor through all ~23 mutation sites in handleInsert (multi-line inserts, EOL joins, kill runs) is genuinely invasive, and nothing in pardes consumes an implicit selection after insert-mode typing - the acme chords need explicit selections, which never enter insert."}
{"name":"alt-c-window-op","reason":"deliberate pardes binding: Alt-c is the move-pane-to-fresh-column window op in ANY mode (do-not-touch contract); helix Alt-c is change-noyank. Alt-d + i covers the helix behavior; the buffer is untouched on the pardes side."}
+{"name":"msel-append","reason":"multiple cursors + `a`: pardes restores the appended-over span for the PRIMARY only, because Pane.append_at (where the append session began) is a single field and the other ranges have nowhere to keep their own origin. Every cursor lands on the right cell and the text matches; only the secondaries' anchors differ, so they read as bare cursors instead of spanning what was typed. Same family as wiX-edit-drops-sel: pardes does not map selections through insert-mode edits. Fix = an origin per SelRange."}
+{"name":"msel-yank-paste","reason":"pardes has ONE yank register; helix's holds one VALUE PER RANGE and pastes value[i] back at range[i]. A multi-range `y` in pardes therefore joins the ranges' text with newlines and `p` puts the whole newline-joined blob at every cursor (which also makes it linewise). Deliberate: a per-range register is its own feature, not part of the selection model. Single-range yank/paste is unaffected and covered by the yank-* / paste-* cases."}
+{"name": "sel-regex-dot-newline", "reason": "mvzr's `.` matches ANY byte, newlines included; helix builds its pattern with the Rust regex crate, where `.` excludes \\n unless asked. So `%s.` selects one range per byte in pardes and one per non-newline byte in helix. Deliberate: closing it means rewriting `.` to `[^\\n]` inside the pattern, and doing that correctly needs a real parse (`\\.` is a literal dot, a `.` inside `[...]` is already literal) - a regex parser written to work around the regex engine. `[^\\n]` typed at the prompt is the workaround, and it behaves identically in both editors."}
+{"name": "sel-regex-caret", "reason": "helix compiles the `s`/`S` pattern with multi_line(true), so `^` and `$` are LINE anchors; mvzr has no such flag and asserts them against the START OF THE SLICE it is handed, which pardes advances to each match's end. `%s^a` therefore finds one match in pardes and one per line in helix. Same root cause for `$`. Fixing it means either searching line by line - which changes which ranges come out for every OTHER pattern, since a match may not span a line then - or an engine with multi-line assertions. Every other anchor-free pattern agrees, including \\b (sel-regex-boundary)."}
diff --git a/test/hxdiff.zig b/test/hxdiff.zig
index b04cc0b5..f6ab5c6f 100644
--- a/test/hxdiff.zig
+++ b/test/hxdiff.zig
@@ -8,6 +8,16 @@
// result: {"name":"w-basic","text":"alpha beta\n","mode":"normal",
// "cursor":{"row":0,"col":5},"anchor":{"row":0,"col":0}}
//
+// MULTIPLE CURSORS extend that line with two more fields, and only when there
+// is more than one selection:
+//
+// ...,"sels":[{"cursor":…,"anchor":…},…],"primary":1
+//
+// `sels` is EVERY range in document order, each end measured exactly like the
+// `cursor`/`anchor` pair above, and `primary` is the index of the one those
+// two fields describe. Omitted entirely at one selection — which is why every
+// golden written before multiple cursors existed is still byte-for-byte valid.
+//
// zig build hxdiff (diff the checked-in corpus)
// zig build hxdiff -- <cases.jsonl> (results to stdout, no diff)
// zig build hxparity (file-vs-pty parity, both corpora)
@@ -53,7 +63,21 @@ const pardes = @import("pardes");
// drives feeds every case's text through ghostty-vt, which narrates each
// sequence it does not implement. Cut the libraries back to errors; the
// per-case report goes through std.debug, so a mismatch stays loud.
-pub const std_options: std.Options = .{ .log_level = .err };
+// The regex engine is dropped outright rather than cut back: an `s`/`S` case
+// types its pattern one key at a time, so mvzr is HANDED every incomplete
+// prefix of it and logs each one at error level. src/main.zig drops the same
+// scope for the same reason.
+pub const std_options: std.Options = .{ .log_level = .err, .logFn = logFn };
+
+fn logFn(
+ comptime level: std.log.Level,
+ comptime scope: @EnumLiteral(),
+ comptime format: []const u8,
+ args: anytype,
+) void {
+ if (scope == .mvzr) return;
+ std.log.defaultLog(level, scope, format, args);
+}
const gpa = std.heap.page_allocator;
@@ -152,7 +176,9 @@ pub fn main(init: std.process.Init) !void {
}
const res = runCase(arena, case, case.pane) catch |e| fatal("case {s}: {s}", .{ case.name, @errorName(e) });
if (goldens_path == null) {
- const jl = std.json.Stringify.valueAlloc(arena, res, .{}) catch fatal("oom", .{});
+ // null sels/primary are DROPPED, not written: that is what
+ // keeps a one-selection line identical to the old contract
+ const jl = std.json.Stringify.valueAlloc(arena, res, .{ .emit_null_optional_fields = false }) catch fatal("oom", .{});
out.appendSlice(arena, jl) catch fatal("oom", .{});
out.append(arena, '\n') catch fatal("oom", .{});
continue;
@@ -185,14 +211,20 @@ const Case = struct {
};
const Pos = struct { row: i32, col: i32 };
+const Sel = struct { cursor: Pos, anchor: Pos };
-// field order is the contract's emit order (Stringify writes declaration order)
+// field order is the contract's emit order (Stringify writes declaration
+// order). The two multi-cursor fields are OPTIONAL and emitted with
+// .emit_null_optional_fields = false, so a single selection prints the
+// original five-field line and nothing else.
const Result = struct {
name: []const u8,
text: []const u8,
mode: []const u8,
cursor: Pos,
anchor: Pos,
+ sels: ?[]const Sel = null,
+ primary: ?usize = null,
};
const Waiver = struct {
@@ -216,6 +248,35 @@ fn diffResult(arena: std.mem.Allocator, want: Result, got: Result, wl: []const u
if (want.anchor.row != got.anchor.row or want.anchor.col != got.anchor.col) {
appendf(arena, &buf, " anchor: {s} ({d},{d}) != {s} ({d},{d})\n", .{ wl, @as(i64, want.anchor.row), @as(i64, want.anchor.col), gl, @as(i64, got.anchor.row), @as(i64, got.anchor.col) });
}
+ // the whole selection: absent on both sides = one range, and the two
+ // fields above already said everything about it
+ const ws = want.sels orelse &.{};
+ const gs = got.sels orelse &.{};
+ var sel_diff = ws.len != gs.len or (want.primary orelse 0) != (got.primary orelse 0);
+ if (!sel_diff) for (ws, gs) |a, b| {
+ if (a.cursor.row != b.cursor.row or a.cursor.col != b.cursor.col or
+ a.anchor.row != b.anchor.row or a.anchor.col != b.anchor.col) sel_diff = true;
+ };
+ if (sel_diff) {
+ appendf(arena, &buf, " sels: {s} {s} != {s} {s}\n", .{ wl, fmtSels(arena, ws, want.primary), gl, fmtSels(arena, gs, got.primary) });
+ }
+ return buf.items;
+}
+
+/// "2 of [(0,1)|(1,1) *(2,1)]" — every range as cursor|anchor (collapsed to
+/// one pair when they are the same cell), the primary starred
+fn fmtSels(arena: std.mem.Allocator, sels: []const Sel, primary: ?usize) []const u8 {
+ if (sels.len == 0) return "<one>";
+ var buf: std.ArrayList(u8) = .empty;
+ appendf(arena, &buf, "{d} of [", .{sels.len});
+ for (sels, 0..) |s, i| {
+ if (i > 0) appendf(arena, &buf, " ", .{});
+ if (i == (primary orelse 0)) appendf(arena, &buf, "*", .{});
+ appendf(arena, &buf, "({d},{d})", .{ @as(i64, s.cursor.row), @as(i64, s.cursor.col) });
+ if (s.anchor.row != s.cursor.row or s.anchor.col != s.cursor.col)
+ appendf(arena, &buf, "|({d},{d})", .{ @as(i64, s.anchor.row), @as(i64, s.anchor.col) });
+ }
+ appendf(arena, &buf, "]", .{});
return buf.items;
}
@@ -296,6 +357,11 @@ fn runCase(arena: std.mem.Allocator, case: Case, pane_kind: []const u8) !Result
} else if (std.mem.eql(u8, name, "gt")) {
key.cp = '>';
if (!key.ctrl and !key.alt) key.text = ">";
+ } else if (std.mem.eql(u8, name, "minus")) {
+ // helix spells Alt-'-' this way: a bare `-` after a modifier
+ // is a parse error in its own macro notation
+ key.cp = '-';
+ if (!key.ctrl and !key.alt) key.text = "-";
} else if (std.mem.eql(u8, name, "up")) {
key.cp = pardes.Key.up;
} else if (std.mem.eql(u8, name, "down")) {
@@ -351,12 +417,39 @@ fn runCase(arena: std.mem.Allocator, case: Case, pane_kind: []const u8) !Result
// mode "select" ONLY while v-extend is on (helix keeps x/%/mi selections
// in normal mode); pane.mode itself never holds a select state
const mode = if (pane.mode == .normal and pane.select) "select" else @tagName(pane.mode);
+ // multiple cursors: the primary (cur_row/cur_col + the anchor above) and
+ // pane.sels, slotted together in document order. pane.sels is already
+ // ordered, so the primary is the only one that needs placing.
+ var sels: ?[]const Sel = null;
+ var primary: ?usize = null;
+ if (pane.nsel > 0) {
+ var out: std.ArrayList(Sel) = .empty;
+ const prim: Sel = .{ .cursor = .{ .row = pane.cur_row, .col = pane.cur_col }, .anchor = anchor };
+ const pr_row = @min(prim.cursor.row, prim.anchor.row);
+ const pr_col = if (prim.cursor.row == prim.anchor.row) @min(prim.cursor.col, prim.anchor.col) else if (prim.cursor.row < prim.anchor.row) prim.cursor.col else prim.anchor.col;
+ for (pane.sels[0..pane.nsel]) |s| {
+ const s_row = @min(s.row, s.arow);
+ const s_col = if (s.row == s.arow) @min(s.col, s.acol) else if (s.row < s.arow) s.col else s.acol;
+ if (primary == null and (pr_row < s_row or (pr_row == s_row and pr_col <= s_col))) {
+ primary = out.items.len;
+ try out.append(arena, prim);
+ }
+ try out.append(arena, .{ .cursor = .{ .row = s.row, .col = s.col }, .anchor = .{ .row = s.arow, .col = s.acol } });
+ }
+ if (primary == null) {
+ primary = out.items.len;
+ try out.append(arena, prim);
+ }
+ sels = out.items;
+ }
return .{
.name = case.name,
.text = try arena.dupe(u8, text),
.mode = mode,
.cursor = .{ .row = pane.cur_row, .col = pane.cur_col },
.anchor = anchor,
+ .sels = sels,
+ .primary = primary,
};
}
diff --git a/test/snapshots/lspdebug.golden b/test/snapshots/lspdebug.golden
index 2bce4aa6..bb0fee41 100644
--- a/test/snapshots/lspdebug.golden
+++ b/test/snapshots/lspdebug.golden
@@ -203,16 +203,16 @@
| 3 zig lib dir: /usr/lib/zig [OK]
| 4 offsets: utf-8 walk caps: 512 files, 2000 rows
| 5
-| 6 dependency imports gd can follow (6):
+| 6 dependency imports gd can follow (7):
| 7 zls /home/goblin/00-projects/0x4200.cafe
| 8 tree-sitter /home/goblin/00-projects/0x4200.cafe
| 9 zstbi /home/goblin/00-projects/0x4200.cafe
-| 10 ghostty-vt /home/goblin/00-projects/0x4200.cafe
-| 11 vaxis /home/goblin/00-projects/0x4200.cafe
-| 12 uucode /home/goblin/00-projects/0x4200.cafe
-| 13 asked from: /tmp/pardes-snap/lspdebug/cwd/z.zig
-| 14
-| 15 answers: definition, declaration, type_definition, im
+| 10 mvzr /home/goblin/00-projects/0x4200.cafe
+| 11 ghostty-vt /home/goblin/00-projects/0x4200.cafe
+| 12 vaxis /home/goblin/00-projects/0x4200.cafe
+| 13 uucode /home/goblin/00-projects/0x4200.cafe
+| 14 asked from: /tmp/pardes-snap/lspdebug/cwd/z.zig
+| 15
| NOR /tmp/pardes-snap/lspdebug/cwd/+Lsp Del
| 1 lsp explain — the definition query at byte 29 of z.zi
| 2
diff --git a/test/snapshots/multicursor.golden b/test/snapshots/multicursor.golden
new file mode 100644
index 00000000..2be37dc2
--- /dev/null
+++ b/test/snapshots/multicursor.golden
@@ -0,0 +1,217 @@
+== snap three-cursors grid=150x30 cursor=7,4
+|Kill Newcol Tutor Debug NextColor Dump Find Grep Help
+| NOR /tmp/pardes-snap/multicursor/cwd/f.txt Save NOR /tmp/pardes-snap/multicursor/cwd Del NOR /tmp/pardes-snap/multicursor/cwd Del
+| 1 line 1
+| 2 line 2 f.txt f.txt
+| 3 line 3
+| 4 line 4
+| 5 line 5
+| 6 line 6 f.txt
+| 7 line 7
+| 8 line 8
+| 9 line 9
+| 10 line 10
+| 11 line 11
+| 12 line 12
+| 13
+|
+| NOR /tmp/pardes-snap/multicursor/cwd Del
+|
+| f.txt
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+== style three-cursors
+|0: 0-52 #949494,#121212, 53-149 d,#121212,
+|1: 0-1 d,#626262, 2-49 #949494,#121212, 50-51 d,#262626, 52-91 #949494,#121212, 92-99 d,#121212, 100-101 d,#262626, 102-141 #949494,#121212, 142-149 d,#121212,
+|2: 0-1 d,#444444, 2-6 #626262,#080808, 7-7 #4a3a1e,#d8c8a8, 8-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|3: 0-1 d,#444444, 2-6 #626262,#080808, 7-7 #4a3a1e,#d8c8a8, 8-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|4: 0-1 d,#444444, 2-6 #949494,#121212, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|5: 0-1 d,#444444, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|6: 0-1 d,#444444, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|7: 0-1 d,#444444, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-58 #bdbdbd,#080808, 59-99 d,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|8: 0-1 d,#444444, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|9: 0-1 d,#444444, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|10: 0-1 d,#444444, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|11: 0-1 d,#444444, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|12: 0-1 d,#444444, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|13: 0-1 d,#444444, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|14: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|15: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|16: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#262626, 52-91 #949494,#121212, 92-99 d,#121212, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|17: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|18: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|19: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|20: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|21: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|22: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|23: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|24: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|25: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|26: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|27: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|28: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|29: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+== snap edited-at-three grid=150x30 cursor=9,4
+|Kill Newcol Tutor Debug NextColor Dump Find Grep Help
+| NOR /tmp/pardes-snap/multicursor/cwd/f.txt Save NOR /tmp/pardes-snap/multicursor/cwd Del NOR /tmp/pardes-snap/multicursor/cwd Del
+| 1 >>line 1
+| 2 >>line 2 f.txt f.txt
+| 3 >>line 3
+| 4 line 4
+| 5 line 5
+| 6 line 6 f.txt
+| 7 line 7
+| 8 line 8
+| 9 line 9
+| 10 line 10
+| 11 line 11
+| 12 line 12
+| 13
+|
+| NOR /tmp/pardes-snap/multicursor/cwd Del
+|
+| f.txt
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+== snap three-selections grid=150x30 cursor=8,4
+|Kill Newcol Tutor Debug NextColor Dump Find Grep Help
+| NOR /tmp/pardes-snap/multicursor/cwd/f.txt Save NOR /tmp/pardes-snap/multicursor/cwd Del NOR /tmp/pardes-snap/multicursor/cwd Del
+| 1 >>line 1
+| 2 >>line 2 f.txt f.txt
+| 3 >>line 3
+| 4 line 4
+| 5 line 5
+| 6 line 6 f.txt
+| 7 line 7
+| 8 line 8
+| 9 line 9
+| 10 line 10
+| 11 line 11
+| 12 line 12
+| 13
+|
+| NOR /tmp/pardes-snap/multicursor/cwd Del
+|
+| f.txt
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+== style three-selections
+|0: 0-52 #949494,#121212, 53-149 d,#121212,
+|1: 0-1 d,#626262, 2-49 #949494,#121212, 50-51 d,#262626, 52-91 #949494,#121212, 92-99 d,#121212, 100-101 d,#262626, 102-141 #949494,#121212, 142-149 d,#121212,
+|2: 0-1 d,#444444, 2-6 #626262,#080808, 7-7 #a89c84,#2e2616, 8-8 #4a3a1e,#d8c8a8, 9-14 #bdbdbd,#080808, 15-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|3: 0-1 d,#444444, 2-6 #626262,#080808, 7-7 #a89c84,#2e2616, 8-8 #4a3a1e,#d8c8a8, 9-14 #bdbdbd,#080808, 15-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|4: 0-1 d,#444444, 2-6 #949494,#121212, 7-8 #d8c8a8,#4a3a1e, 9-14 #bdbdbd,#080808, 15-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|5: 0-1 d,#444444, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|6: 0-1 d,#444444, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|7: 0-1 d,#444444, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-58 #bdbdbd,#080808, 59-99 d,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|8: 0-1 d,#444444, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|9: 0-1 d,#444444, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|10: 0-1 d,#444444, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|11: 0-1 d,#444444, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|12: 0-1 d,#444444, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|13: 0-1 d,#444444, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|14: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|15: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|16: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#262626, 52-91 #949494,#121212, 92-99 d,#121212, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|17: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|18: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|19: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|20: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|21: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|22: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|23: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|24: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|25: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|26: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|27: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|28: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|29: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+== snap keep-primary grid=150x30 cursor=8,4
+|Kill Newcol Tutor Debug NextColor Dump Find Grep Help
+| NOR /tmp/pardes-snap/multicursor/cwd/f.txt Save NOR /tmp/pardes-snap/multicursor/cwd Del NOR /tmp/pardes-snap/multicursor/cwd Del
+| 1 >>line 1
+| 2 >>line 2 f.txt f.txt
+| 3 >>line 3
+| 4 line 4
+| 5 line 5
+| 6 line 6 f.txt
+| 7 line 7
+| 8 line 8
+| 9 line 9
+| 10 line 10
+| 11 line 11
+| 12 line 12
+| 13
+|
+| NOR /tmp/pardes-snap/multicursor/cwd Del
+|
+| f.txt
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+== style keep-primary
+|0: 0-52 #949494,#121212, 53-149 d,#121212,
+|1: 0-1 d,#626262, 2-49 #949494,#121212, 50-51 d,#262626, 52-91 #949494,#121212, 92-99 d,#121212, 100-101 d,#262626, 102-141 #949494,#121212, 142-149 d,#121212,
+|2: 0-1 d,#444444, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|3: 0-1 d,#444444, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|4: 0-1 d,#444444, 2-6 #949494,#121212, 7-8 #d8c8a8,#4a3a1e, 9-14 #bdbdbd,#080808, 15-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|5: 0-1 d,#444444, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|6: 0-1 d,#444444, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|7: 0-1 d,#444444, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-58 #bdbdbd,#080808, 59-99 d,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|8: 0-1 d,#444444, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|9: 0-1 d,#444444, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|10: 0-1 d,#444444, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|11: 0-1 d,#444444, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|12: 0-1 d,#444444, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|13: 0-1 d,#444444, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|14: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|15: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|16: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#262626, 52-91 #949494,#121212, 92-99 d,#121212, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|17: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|18: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|19: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|20: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|21: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|22: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|23: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|24: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|25: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|26: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|27: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|28: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
+|29: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808, 100-101 d,#444444, 102-149 #bdbdbd,#080808,
diff --git a/test/snapshots/multicursor.snap b/test/snapshots/multicursor.snap
new file mode 100644
index 00000000..2bfccb93
--- /dev/null
+++ b/test/snapshots/multicursor.snap
@@ -0,0 +1,46 @@
+# MULTIPLE CURSORS on a real file pane. The differential suite (hxdiff) proves
+# the model against a real helix but is headless, so this is the part it cannot
+# see: that every range is actually on screen, that the primary reads
+# differently from the others, and that ONE keystroke edits at all of them.
+lines f.txt 12
+start 30 150 -n 3
+wait 8000 f.txt
+stable 700 20000
+# open f.txt in the left pane (type its name, right-click to Look)
+press left 5 8
+release left 5 8
+stable 400 5000
+key i
+text f.txt
+key esc
+settle 100
+press right 6 8
+release right 6 8
+wait 10000 line 3
+stable 700 15000
+# top of the file, then C C: three cursors, one per line, primary on line 3
+key g g
+stable 400 5000
+key C C
+stable 400 5000
+snap three-cursors
+# the colours ARE the feature here: the primary's cell against the two dimmer
+# ones, each with its own bright cursor cell
+snapstyle three-cursors
+# one keystroke, three edits
+key i
+text >>
+key esc
+settle 100
+stable 400 5000
+snap edited-at-three
+# a selection per cursor: e selects to the end of the first word at each
+key g h w
+stable 400 5000
+snap three-selections
+snapstyle three-selections
+# `,` drops back to the primary alone; the two dim highlights must vanish
+key ,
+stable 400 5000
+snap keep-primary
+snapstyle keep-primary
diff --git a/test/snapshots/selregex.golden b/test/snapshots/selregex.golden
new file mode 100644
index 00000000..9a1d58d6
--- /dev/null
+++ b/test/snapshots/selregex.golden
@@ -0,0 +1,275 @@
+== snap prompt-armed grid=160x24 cursor=59,1
+|Kill Newcol Tutor Debug NextColor Dump Find Grep Help
+| INS /tmp/pardes-snap/selregex/cwd/m.zig Save Del Select / NOR /tmp/pardes-snap/selregex/cwd Del
+| 1 const one = 1;
+| 2 const two = 2; m.zig
+| 3 const three = 3;
+| 4
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+== snap preview grid=160x24 cursor=64,1
+|Kill Newcol Tutor Debug NextColor Dump Find Grep Help
+| INS /tmp/pardes-snap/selregex/cwd/m.zig Save Del Select /const NOR /tmp/pardes-snap/selregex/cwd Del
+| 1 const one = 1;
+| 2 const two = 2; m.zig
+| 3 const three = 3;
+| 4
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+== style preview
+|0: 0-52 #949494,#121212, 53-159 d,#121212,
+|1: 0-1 d,#626262, 2-63 #949494,#121212, 64-79 d,#121212, 80-81 d,#262626, 82-118 #949494,#121212, 119-159 d,#121212,
+|2: 0-1 d,#444444, 2-6 #626262,#080808, 7-10 #d8c8a8,#4a3a1e, 11-11 #4a3a1e,#d8c8a8, 12-18 #bdbdbd,#080808, 19-19 #e3c78a,#080808, 20-20 #bdbdbd,#080808, 21-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|3: 0-1 d,#444444, 2-6 #626262,#080808, 7-10 #a89c84,#2e2616, 11-11 #4a3a1e,#d8c8a8, 12-18 #bdbdbd,#080808, 19-19 #e3c78a,#080808, 20-20 #bdbdbd,#080808, 21-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|4: 0-1 d,#444444, 2-6 #626262,#080808, 7-10 #a89c84,#2e2616, 11-11 #4a3a1e,#d8c8a8, 12-20 #bdbdbd,#080808, 21-21 #e3c78a,#080808, 22-22 #bdbdbd,#080808, 23-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|5: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|6: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|7: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|8: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|9: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|10: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|11: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|12: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|13: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|14: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|15: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|16: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|17: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|18: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|19: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|20: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|21: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|22: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|23: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+== snap three-selected grid=160x24 cursor=11,2
+|Kill Newcol Tutor Debug NextColor Dump Find Grep Help
+| NOR /tmp/pardes-snap/selregex/cwd/m.zig Save Del NOR /tmp/pardes-snap/selregex/cwd Del
+| 1 const one = 1;
+| 2 const two = 2; m.zig
+| 3 const three = 3;
+| 4
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+== style three-selected
+|0: 0-52 #949494,#121212, 53-159 d,#121212,
+|1: 0-1 d,#626262, 2-49 #949494,#121212, 50-79 d,#121212, 80-81 d,#262626, 82-118 #949494,#121212, 119-159 d,#121212,
+|2: 0-1 d,#444444, 2-6 #949494,#121212, 7-11 #d8c8a8,#4a3a1e, 12-18 #bdbdbd,#080808, 19-19 #e3c78a,#080808, 20-20 #bdbdbd,#080808, 21-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|3: 0-1 d,#444444, 2-6 #626262,#080808, 7-10 #a89c84,#2e2616, 11-11 #4a3a1e,#d8c8a8, 12-18 #bdbdbd,#080808, 19-19 #e3c78a,#080808, 20-20 #bdbdbd,#080808, 21-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|4: 0-1 d,#444444, 2-6 #626262,#080808, 7-10 #a89c84,#2e2616, 11-11 #4a3a1e,#d8c8a8, 12-20 #bdbdbd,#080808, 21-21 #e3c78a,#080808, 22-22 #bdbdbd,#080808, 23-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|5: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|6: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|7: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|8: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|9: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|10: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|11: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|12: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|13: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|14: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|15: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|16: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|17: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|18: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|19: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|20: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|21: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|22: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|23: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+== snap narrowed grid=160x24 cursor=60,1
+|Kill Newcol Tutor Debug NextColor Dump Find Grep Help
+| INS /tmp/pardes-snap/selregex/cwd/m.zig Save Del Select /t NOR /tmp/pardes-snap/selregex/cwd Del
+| 1 const one = 1;
+| 2 const two = 2; m.zig
+| 3 const three = 3;
+| 4
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+== style narrowed
+|0: 0-52 #949494,#121212, 53-159 d,#121212,
+|1: 0-1 d,#626262, 2-59 #949494,#121212, 60-79 d,#121212, 80-81 d,#262626, 82-118 #949494,#121212, 119-159 d,#121212,
+|2: 0-1 d,#444444, 2-6 #626262,#080808, 7-10 #bdbdbd,#080808, 11-11 #4a3a1e,#d8c8a8, 12-18 #bdbdbd,#080808, 19-19 #e3c78a,#080808, 20-20 #bdbdbd,#080808, 21-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|3: 0-1 d,#444444, 2-6 #626262,#080808, 7-10 #bdbdbd,#080808, 11-11 #4a3a1e,#d8c8a8, 12-18 #bdbdbd,#080808, 19-19 #e3c78a,#080808, 20-20 #bdbdbd,#080808, 21-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|4: 0-1 d,#444444, 2-6 #626262,#080808, 7-10 #bdbdbd,#080808, 11-11 #4a3a1e,#d8c8a8, 12-20 #bdbdbd,#080808, 21-21 #e3c78a,#080808, 22-22 #bdbdbd,#080808, 23-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|5: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|6: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|7: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|8: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|9: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|10: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|11: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|12: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|13: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|14: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|15: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|16: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|17: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|18: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|19: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|20: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|21: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|22: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|23: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+== snap esc-restores grid=160x24 cursor=11,2
+|Kill Newcol Tutor Debug NextColor Dump Find Grep Help
+| NOR /tmp/pardes-snap/selregex/cwd/m.zig Save Del NOR /tmp/pardes-snap/selregex/cwd Del
+| 1 const one = 1;
+| 2 const two = 2; m.zig
+| 3 const three = 3;
+| 4
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+== style esc-restores
+|0: 0-52 #949494,#121212, 53-159 d,#121212,
+|1: 0-1 d,#626262, 2-49 #949494,#121212, 50-79 d,#121212, 80-81 d,#262626, 82-118 #949494,#121212, 119-159 d,#121212,
+|2: 0-1 d,#444444, 2-6 #949494,#121212, 7-11 #d8c8a8,#4a3a1e, 12-18 #bdbdbd,#080808, 19-19 #e3c78a,#080808, 20-20 #bdbdbd,#080808, 21-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|3: 0-1 d,#444444, 2-6 #626262,#080808, 7-10 #a89c84,#2e2616, 11-11 #4a3a1e,#d8c8a8, 12-18 #bdbdbd,#080808, 19-19 #e3c78a,#080808, 20-20 #bdbdbd,#080808, 21-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|4: 0-1 d,#444444, 2-6 #626262,#080808, 7-10 #a89c84,#2e2616, 11-11 #4a3a1e,#d8c8a8, 12-20 #bdbdbd,#080808, 21-21 #e3c78a,#080808, 22-22 #bdbdbd,#080808, 23-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|5: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|6: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|7: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|8: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|9: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|10: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|11: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|12: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|13: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|14: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|15: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|16: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|17: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|18: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|19: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|20: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|21: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|22: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+|23: 0-1 d,#444444, 2-6 #626262,#080808, 7-79 d,#080808, 80-81 d,#444444, 82-159 #bdbdbd,#080808,
+== snap commented grid=160x24 cursor=14,2
+|Kill Newcol Tutor Debug NextColor Dump Find Grep Help
+| NOR /tmp/pardes-snap/selregex/cwd/m.zig Save Del NOR /tmp/pardes-snap/selregex/cwd Del
+| 1 // const one = 1;
+| 2 // const two = 2; m.zig
+| 3 // const three = 3;
+| 4
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+== snap uncommented grid=160x24 cursor=11,2
+|Kill Newcol Tutor Debug NextColor Dump Find Grep Help
+| NOR /tmp/pardes-snap/selregex/cwd/m.zig Save Del NOR /tmp/pardes-snap/selregex/cwd Del
+| 1 const one = 1;
+| 2 const two = 2; m.zig
+| 3 const three = 3;
+| 4
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
diff --git a/test/snapshots/selregex.snap b/test/snapshots/selregex.snap
new file mode 100644
index 00000000..193ab314
--- /dev/null
+++ b/test/snapshots/selregex.snap
@@ -0,0 +1,56 @@
+# Interactive regex selection (`s` / `S`) and the comment toggle, on a real
+# file pane. hxdiff pins what they DO against a real helix, but it is headless,
+# so this is the half it cannot see: the prompt living in the TAG rather than a
+# popup, the matches lighting up under a pattern as it is typed, Esc putting
+# back what was there, and Ctrl-c using the token this file's EXTENSION asks
+# for — `//`, where a buffer with no language would get `#`.
+file m.zig const one = 1;\nconst two = 2;\nconst three = 3;\n
+# wide on purpose: the whole point of this script is a tag long enough to hold
+# the marker AND the pattern, and a narrow pane simply truncates it away
+start 24 160 m.zig
+wait 8000 m.zig
+stable 700 20000
+key g g
+stable 400 5000
+# `%` takes the whole file, `s` arms the regex input: the marker goes into the
+# tag tail after the path, and the pane is in insert mode ON THE TAG — the
+# same one-line editor `/` and Find use, and nothing has moved in the body yet.
+key %
+key s
+stable 400 5000
+snap prompt-armed
+# ...and now the point: the selection follows the pattern as it is typed. Three
+# `const` runs light up with Enter nowhere near, and the colours ARE the
+# feature — the primary bright, the other two dim, each with its own cursor.
+text const
+stable 400 5000
+snap preview
+snapstyle preview
+key enter
+stable 400 5000
+snap three-selected
+snapstyle three-selected
+# a second `s` narrows the three to one `t` apiece...
+key s
+text t
+stable 400 5000
+snap narrowed
+# the grid text cannot show a selection, so this is where the styles carry the
+# whole point: three one-cell ranges here, three five-cell ones after the Esc
+snapstyle narrowed
+# ...and Esc throws that away for the selection the prompt opened on, which is
+# free: the preview is re-derived from that snapshot on every keystroke, so
+# cancelling is just applying the empty pattern.
+key esc
+settle 100
+stable 400 5000
+snap esc-restores
+snapstyle esc-restores
+# Ctrl-c comments every line the three cursors touch — one decision for the
+# whole selection, not one per cursor — and again to take it back off.
+key c-c
+stable 400 5000
+snap commented
+key c-c
+stable 400 5000
+snap uncommented