diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-28 15:28:03 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:15 -0300 |
| commit | ff29fd633e979d24a191e7ecd0c26541e3b597ac (patch) | |
| tree | d323e552f83c11c18dc281262caba83fa8bfb181 | |
| parent | 05ca060fc91930f0e931f39dcf56729ec71543dd (diff) | |
| download | pardes-ff29fd633e979d24a191e7ecd0c26541e3b597ac.tar.gz pardes-ff29fd633e979d24a191e7ecd0c26541e3b597ac.zip | |
Surround keeps the direction of the selection it wraps
ms<ch> always left the wrapped range pointing forward. helix gives each range
back with_direction(range.direction()), so a selection made backward (b, or v
moving up) keeps its head on the open, which is where the next motion starts
from. helix-golf's csv_to_sql (bms`) is where it showed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
| -rw-r--r-- | docs/helix-keys.md | 2 | ||||
| -rw-r--r-- | src/edit.zig | 12 | ||||
| -rw-r--r-- | test/hxcases/cases.jsonl | 3 | ||||
| -rw-r--r-- | test/hxcases/goldens.jsonl | 3 |
4 files changed, 16 insertions, 4 deletions
diff --git a/docs/helix-keys.md b/docs/helix-keys.md index 84ee5124..0b0da2ae 100644 --- a/docs/helix-keys.md +++ b/docs/helix-keys.md @@ -283,7 +283,7 @@ keeps the matches it found so far. | --- | --- | --- | --- | | `mm` | goto matching bracket | dumb text scan with nesting for `()[]{}<>`; ON a bracket only (no TS "nearest pair"); works on terminals via the motion surface | helix-verified (phase 5) | | `mi<pair>` / `ma<pair>` | select inside / around textobject | pairs `( ) [ ] { } < >` nesting-aware multi-line; quotes `' " `` ` `` ` **line-scoped** (plain-text strings don't span lines); `w`/`W` word run (+trailing ws around, leading if none); `p` blank-line block (+trailing blanks around). Empty inside (`()`) = no-op. Selections work on terminals; `Pane.pending2` holds the i/a/s/r/d sub-key | helix-verified (phase 5) | -| `ms<ch>` | surround selection (or cursor char) with the `<ch>` pair; wrap incl. pair becomes the selection | either bracket names its pair; any other ASCII char wraps with itself; file panes only | helix-verified (phase 5) | +| `ms<ch>` | surround selection (or cursor char) with the `<ch>` pair; wrap incl. pair becomes the selection, keeping the direction it had | either bracket names its pair; any other ASCII char wraps with itself; file panes only | helix-verified (phase 5) | | `mr<from><to>` | replace enclosing `<from>` pair chars with `<to>`'s | `Pane.pending_ch` holds `<from>` while `<to>` pends; file panes only | helix-verified (phase 5) | | `md<ch>` | delete the enclosing `<ch>` pair chars | file panes only | helix-verified (phase 5) | diff --git a/src/edit.zig b/src/edit.zig index 3cbbc28c..56d537bf 100644 --- a/src/edit.zig +++ b/src/edit.zig @@ -1512,16 +1512,22 @@ pub fn surroundAdd(p: *Pardes, t: *Text, ch: u21) void { t.select = false; const eb = editTextEol(p, t, selRows(t)) orelse return; const r = selRange(t, eb.text, eb.row0); + // the wrapped range keeps the direction it had (helix with_direction) + const back = t.vsel.active and (t.cur_row < t.vsel.row or (t.cur_row == t.vsel.row and t.cur_col < t.vsel.col)); pushUndo(p, t); const close_col = modal.nextGrapheme(modal.lineSlice(eb.text, r.b.row), r.b.col); var new = modal.insertAt(p.gpa, eb.text, .{ .row = r.b.row, .col = close_col }, &[1]u8{pr.c}) catch return; setEditText(p, t, new); new = modal.insertAt(p.gpa, new, .{ .row = r.a.row, .col = r.a.col }, &[1]u8{pr.o}) catch return; setEditText(p, t, new); + const open_row = @as(i32, @intCast(r.a.row)) + eb.row0; + const open_col: i32 = @intCast(r.a.col); + const close_row = @as(i32, @intCast(r.b.row)) + eb.row0; + const close_at: i32 = @intCast(close_col + @as(usize, if (r.a.row == r.b.row) 1 else 0)); t.msel.active = false; - t.vsel = .{ .active = true, .row = @as(i32, @intCast(r.a.row)) + eb.row0, .col = @intCast(r.a.col), .explicit = false }; - t.cur_row = @as(i32, @intCast(r.b.row)) + eb.row0; - t.cur_col = @intCast(close_col + @as(usize, if (r.a.row == r.b.row) 1 else 0)); + t.vsel = .{ .active = true, .row = if (back) close_row else open_row, .col = if (back) close_at else open_col, .explicit = false }; + t.cur_row = if (back) open_row else close_row; + t.cur_col = if (back) open_col else close_at; t.cur_pinned = true; t.sticky_col = -1; t.show(); diff --git a/test/hxcases/cases.jsonl b/test/hxcases/cases.jsonl index b0a3aad4..d7f76912 100644 --- a/test/hxcases/cases.jsonl +++ b/test/hxcases/cases.jsonl @@ -316,6 +316,9 @@ {"name": "ms-sel", "pane": "file", "text": "hello\n", "keys": "vllms("} {"name": "ms-bare", "pane": "file", "text": "ab\n", "keys": "ms("} {"name": "ms-quote-self", "pane": "file", "text": "ab cd\n", "keys": "wems'"} +{"name": "ms-backward", "pane": "file", "text": "ab cd\n", "keys": "wbms("} +{"name": "ms-backward-lines", "pane": "file", "text": "ab\ncd\nef\n", "keys": "jjlvkkms["} +{"name": "msel-ms-backward", "pane": "file", "text": "id 1,x 2\n", "keys": "%s\\d<ret>dhbms`"} {"name": "md-basic", "pane": "file", "text": "a(bcd)e\n", "keys": "3lmd("} {"name": "md-quote", "pane": "file", "text": "a 'bc' d\n", "keys": "3lmd'"} {"name": "mr-basic", "pane": "file", "text": "a(bcd)e\n", "keys": "3lmr(["} diff --git a/test/hxcases/goldens.jsonl b/test/hxcases/goldens.jsonl index eee7294b..cab7393b 100644 --- a/test/hxcases/goldens.jsonl +++ b/test/hxcases/goldens.jsonl @@ -316,6 +316,9 @@ {"name":"ms-sel","text":"(hel)lo\n","mode":"normal","cursor":{"row":0,"col":4},"anchor":{"row":0,"col":0}} {"name":"ms-bare","text":"(a)b\n","mode":"normal","cursor":{"row":0,"col":2},"anchor":{"row":0,"col":0}} {"name":"ms-quote-self","text":"ab' cd'\n","mode":"normal","cursor":{"row":0,"col":6},"anchor":{"row":0,"col":2}} +{"name":"ms-backward","text":"(ab )cd\n","mode":"normal","cursor":{"row":0,"col":0},"anchor":{"row":0,"col":4}} +{"name":"ms-backward-lines","text":"a[b\ncd\nef]\n","mode":"normal","cursor":{"row":0,"col":1},"anchor":{"row":2,"col":2}} +{"name":"msel-ms-backward","text":"`id `,`x `\n","mode":"normal","cursor":{"row":0,"col":0},"anchor":{"row":0,"col":4},"sels":[{"cursor":{"row":0,"col":0},"anchor":{"row":0,"col":4}},{"cursor":{"row":0,"col":6},"anchor":{"row":0,"col":9}}],"primary":0} {"name":"md-basic","text":"abcde\n","mode":"normal","cursor":{"row":0,"col":2},"anchor":{"row":0,"col":2}} {"name":"md-quote","text":"a bc d\n","mode":"normal","cursor":{"row":0,"col":2},"anchor":{"row":0,"col":2}} {"name":"mr-basic","text":"a[bcd]e\n","mode":"normal","cursor":{"row":0,"col":3},"anchor":{"row":0,"col":3}} |
