summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/config.zig2
-rw-r--r--src/edit.zig51
-rw-r--r--src/modal.zig15
-rw-r--r--src/normal.zig2
4 files changed, 69 insertions, 1 deletions
diff --git a/src/config.zig b/src/config.zig
index 372c4884..edc73837 100644
--- a/src/config.zig
+++ b/src/config.zig
@@ -539,6 +539,8 @@ pub const to_lowercase: []const Chord = &.{.{ .cp = '`' }};
pub const to_uppercase: []const Chord = &.{.{ .cp = '`', .alt = true }};
pub const join_lines: []const Chord = &.{.{ .cp = 'J' }};
pub const join_select_space: []const Chord = &.{.{ .cp = 'J', .alt = true }};
+pub const rotate_contents_fwd: []const Chord = &.{.{ .cp = ')', .alt = true }};
+pub const rotate_contents_back: []const Chord = &.{.{ .cp = '(', .alt = true }};
pub const indent: []const Chord = &.{.{ .cp = '>' }};
pub const unindent: []const Chord = &.{.{ .cp = '<' }};
pub const format: []const Chord = &.{.{ .cp = '=' }};
diff --git a/src/edit.zig b/src/edit.zig
index 09c3ffe7..2dc79ab5 100644
--- a/src/edit.zig
+++ b/src/edit.zig
@@ -1292,6 +1292,57 @@ pub fn normalJoin(p: *Pardes, t: *Text, select_space: bool) void {
t.setRanges(t2, rs[0..n], &.{}, pri, expl);
}
+/// `Alt-)` / `Alt-(`: rotate the TEXT of the selections forward / back by
+/// one range (count ranges), in one edit (helix reorder_selection_contents).
+/// Each range comes back over the text it now holds, in its own direction,
+/// and the primary moves with the text it had.
+pub fn normalRotateContents(p: *Pardes, t: *Text, forward: bool, cnt: usize) void {
+ const expl = (t.vsel.active and t.vsel.explicit) or t.msel.active;
+ var b = selRows(t);
+ for (t.sels[0..t.nsel]) |s| {
+ b.lo_row = @min(b.lo_row, @min(s.row, s.arow));
+ b.hi_row = @max(b.hi_row, @max(s.row, s.arow));
+ }
+ const eb = editTextEol(p, t, b) orelse return;
+ const text = eb.text;
+ var rs: [Text.max_selections]modal.Selection = undefined;
+ const got = t.ranges(text, eb.row0, &rs);
+ const n = got.n;
+ if (n < 2) return;
+ const by = @min(cnt, n);
+ var chs: [Text.max_selections]TextChange = undefined;
+ for (rs[0..n], 0..) |r, i| {
+ const src = rs[if (forward) (i + n - by) % n else (i + by) % n];
+ chs[i] = .{
+ .from = @min(r.anchor, r.head),
+ .to = @max(r.anchor, r.head),
+ .ins = text[@min(src.anchor, src.head)..@max(src.anchor, src.head)],
+ };
+ }
+ pushUndo(p, t);
+ const new = applyChanges(p, text, chs[0..n]) catch return;
+ var cells: [Text.max_selections]Text.SelRange = undefined;
+ var delta: i64 = 0;
+ for (chs[0..n], rs[0..n], 0..) |ch, r, i| {
+ const from: usize = @intCast(@as(i64, @intCast(ch.from)) + delta);
+ delta += @as(i64, @intCast(ch.ins.len)) - @as(i64, @intCast(ch.to - ch.from));
+ const to = from + ch.ins.len;
+ const c = Text.rangeCells(new, if (r.anchor > r.head) .{ .anchor = to, .head = from } else .{ .anchor = from, .head = to });
+ const cc = modal.positionAt(new, c.cur);
+ const ac = modal.positionAt(new, c.anc);
+ cells[i] = .{
+ .row = @as(i32, @intCast(cc.row)) + eb.row0,
+ .col = @intCast(cc.col),
+ .arow = @as(i32, @intCast(ac.row)) + eb.row0,
+ .acol = @intCast(ac.col),
+ };
+ }
+ setEditText(p, t, new);
+ const t2 = flatSurface(p, t) catch return;
+ for (cells[0..n], 0..) |s, i| rs[i] = Text.cellRange(t2, s.arow, s.acol, s.row, s.col);
+ t.setRanges(t2, rs[0..n], &.{}, if (forward) (got.pri + by) % n else (got.pri + n - by) % n, expl);
+}
+
pub fn normalIndent(p: *Pardes, t: *Text, cnt: usize, add: bool) void {
t.select = false;
const sr = selRows(t);
diff --git a/src/modal.zig b/src/modal.zig
index 0e6aba61..6bf7918f 100644
--- a/src/modal.zig
+++ b/src/modal.zig
@@ -109,6 +109,8 @@ pub const Normal = struct {
to_uppercase,
join_lines,
join_select_space,
+ rotate_contents_fwd,
+ rotate_contents_back,
indent,
unindent,
format,
@@ -262,6 +264,8 @@ pub const Normal = struct {
uppercase,
join_lines,
join_select_space,
+ rotate_contents_forward,
+ rotate_contents_backward,
indent,
unindent,
comment_toggle,
@@ -319,7 +323,14 @@ pub const Normal = struct {
.search_step,
=> .once,
.edit => |edit| switch (edit.kind) {
- .join_lines, .join_select_space, .comment_toggle, .undo, .redo => .once,
+ .join_lines,
+ .join_select_space,
+ .rotate_contents_forward,
+ .rotate_contents_backward,
+ .comment_toggle,
+ .undo,
+ .redo,
+ => .once,
else => .per_selection,
},
else => .per_selection,
@@ -552,6 +563,8 @@ pub const Normal = struct {
if (key.has(.to_uppercase)) return resultAction(.{ .edit = .{ .kind = .uppercase, .count = count } });
if (key.has(.join_lines)) return resultAction(.{ .edit = .{ .kind = .join_lines, .count = count } });
if (key.has(.join_select_space)) return resultAction(.{ .edit = .{ .kind = .join_select_space, .count = count } });
+ if (key.has(.rotate_contents_fwd)) return resultAction(.{ .edit = .{ .kind = .rotate_contents_forward, .count = count } });
+ if (key.has(.rotate_contents_back)) return resultAction(.{ .edit = .{ .kind = .rotate_contents_backward, .count = count } });
if (key.has(.indent)) return resultAction(.{ .edit = .{ .kind = .indent, .count = count } });
if (key.has(.unindent)) return resultAction(.{ .edit = .{ .kind = .unindent, .count = count } });
if (key.has(.format)) return resultAction(.{ .lsp = .format });
diff --git a/src/normal.zig b/src/normal.zig
index c976823c..9c6f2acd 100644
--- a/src/normal.zig
+++ b/src/normal.zig
@@ -542,6 +542,8 @@ fn executeNormalAction(p: *Pardes, t: *Text, semantic: modal.Normal.Action) void
.uppercase => return edit.normalCase(p, t, .upper),
.join_lines => return edit.normalJoin(p, t, false),
.join_select_space => return edit.normalJoin(p, t, true),
+ .rotate_contents_forward => return edit.normalRotateContents(p, t, true, op.count),
+ .rotate_contents_backward => return edit.normalRotateContents(p, t, false, op.count),
.indent => return edit.normalIndent(p, t, op.count, true),
.unindent => return edit.normalIndent(p, t, op.count, false),
.comment_toggle => return edit.normalToggleComment(p, t),