summaryrefslogtreecommitdiff
path: root/src/edit.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-28 15:28:36 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:15 -0300
commitf59785670a2ff12cec33894add6dc1590c7b77b1 (patch)
tree30f61973ee24fd952d922a587d5893f9f2de1184 /src/edit.zig
parentff29fd633e979d24a191e7ecd0c26541e3b597ac (diff)
downloadpardes-f59785670a2ff12cec33894add6dc1590c7b77b1.tar.gz
pardes-f59785670a2ff12cec33894add6dc1590c7b77b1.zip
Replacing a selection with the register keeps its direction
R always left the head on the pasted text's last char. helix maps the replaced range through the change, so a backward selection comes back backward, its head on the first char: helix-golf's reverse_golf_example (xbR) is where it showed. SPC R shares the path. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/edit.zig')
-rw-r--r--src/edit.zig29
1 files changed, 21 insertions, 8 deletions
diff --git a/src/edit.zig b/src/edit.zig
index 56d537bf..fb8681d8 100644
--- a/src/edit.zig
+++ b/src/edit.zig
@@ -1125,27 +1125,40 @@ pub fn normalReplaceYank(p: *Pardes, t: *Text) void {
replaceWithText(p, t, p.yank orelse return);
}
-/// The pasted text becomes the selection, head on its last char (which for
-/// a trailing-newline `y` is the '\n' cell of the last full line).
+/// The pasted text becomes the selection, in the direction the replaced one
+/// had: a forward head lands on its last char (which for a trailing-newline
+/// `y` is the '\n' cell of the last full line), a backward one on its first.
fn replaceWithText(p: *Pardes, t: *Text, y: []const u8) void {
if (y.len == 0) return;
t.select = false;
const eb = editTextEol(p, t, selRows(t)) orelse return;
const r = selRange(t, eb.text, eb.row0);
+ 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 new = modal.replaceRange(p.gpa, eb.text, r.a, r.b, y) catch return;
setEditText(p, t, new);
t.msel.active = false;
- t.vsel = .{ .active = modal.nextGrapheme(y, 0) < y.len, .row = @as(i32, @intCast(r.a.row)) + eb.row0, .col = @intCast(r.a.col), .explicit = false };
const end = modal.advanceBy(r.a, y);
+ var last_row: i32 = undefined;
+ var last_col: i32 = undefined;
if (end.col > 0) {
- t.cur_row = @as(i32, @intCast(end.row)) + eb.row0;
- t.cur_col = @intCast(modal.prevGrapheme(modal.lineSlice(new, end.row), end.col));
+ last_row = @as(i32, @intCast(end.row)) + eb.row0;
+ last_col = @intCast(modal.prevGrapheme(modal.lineSlice(new, end.row), end.col));
} else {
- // the yank ended in '\n': the cursor lands ON that newline
- t.cur_row = @as(i32, @intCast(end.row -| 1)) + eb.row0;
- t.cur_col = @intCast(modal.lineSlice(new, end.row -| 1).len);
+ // the yank ended in '\n': its last cell is that newline
+ last_row = @as(i32, @intCast(end.row -| 1)) + eb.row0;
+ last_col = @intCast(modal.lineSlice(new, end.row -| 1).len);
}
+ const first_row = @as(i32, @intCast(r.a.row)) + eb.row0;
+ const first_col: i32 = @intCast(r.a.col);
+ t.vsel = .{
+ .active = modal.nextGrapheme(y, 0) < y.len,
+ .row = if (back) last_row else first_row,
+ .col = if (back) last_col else first_col,
+ .explicit = false,
+ };
+ t.cur_row = if (back) first_row else last_row;
+ t.cur_col = if (back) first_col else last_col;
t.cur_pinned = true;
t.sticky_col = -1;
t.show();