summaryrefslogtreecommitdiff
path: root/src/edit.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/edit.zig')
-rw-r--r--src/edit.zig80
1 files changed, 80 insertions, 0 deletions
diff --git a/src/edit.zig b/src/edit.zig
index 5ba46ca7..24354df8 100644
--- a/src/edit.zig
+++ b/src/edit.zig
@@ -1354,6 +1354,86 @@ pub fn normalRotateContents(p: *Pardes, t: *Text, forward: bool, cnt: usize) voi
t.setRanges(t2, rs[0..n], &.{}, if (forward) (got.pri + by) % n else (got.pri + n - by) % n, expl);
}
+/// `&`: pad the selections into columns, in one edit (helix
+/// align_selections). The k-th range of every line is a column; spaces go in
+/// before each range so its head lands on the column's widest head, measured
+/// in display cells. A range over several lines aligns nothing.
+pub fn normalAlign(p: *Pardes, t: *Text) 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;
+ var row: [Text.max_selections]usize = undefined;
+ var col: [Text.max_selections]usize = undefined;
+ var widths: [Text.max_selections]usize = @splat(0);
+ var line: usize = std.math.maxInt(usize);
+ var k: usize = 0;
+ var run: usize = 0;
+ for (rs[0..n], 0..) |r, i| {
+ row[i] = modal.lineAtOffset(text, r.head);
+ if (modal.lineAtOffset(text, r.anchor) != row[i]) return;
+ const ls = modal.lineStartOffset(text, row[i]);
+ col[i] = panes.File.rawDisplayCol(text[ls..modal.lineEndOffset(text, row[i])], r.head - ls);
+ if (row[i] != line) {
+ line = row[i];
+ k = 0;
+ run = 0;
+ }
+ widths[k] = @max(widths[k], col[i] - run);
+ run = col[i];
+ k += 1;
+ }
+ t.select = false; // helix exit_select_mode
+ // each column's head position, left to right
+ for (1..n) |i| widths[i] += widths[i - 1];
+ const arena = p.scratch.allocator();
+ var chs: [Text.max_selections]TextChange = undefined;
+ var m: usize = 0;
+ line = std.math.maxInt(usize);
+ for (rs[0..n], 0..) |r, i| {
+ if (row[i] != line) {
+ line = row[i];
+ k = 0;
+ run = 0;
+ }
+ const pad = widths[k] - col[i] - run;
+ k += 1;
+ run += pad;
+ if (pad == 0) continue;
+ const spaces = arena.alloc(u8, pad) catch return;
+ @memset(spaces, ' ');
+ const at = @min(r.anchor, r.head);
+ chs[m] = .{ .from = at, .to = at, .ins = spaces };
+ m += 1;
+ }
+ if (m == 0) return;
+ pushUndo(p, t);
+ const new = applyChanges(p, text, chs[0..m]) catch return;
+ var cells: [Text.max_selections]Text.SelRange = undefined;
+ for (rs[0..n], 0..) |r, i| {
+ const c = Text.rangeCells(text, r);
+ const cc = modal.positionAt(new, mapThroughChanges(chs[0..m], c.cur));
+ const ac = modal.positionAt(new, mapThroughChanges(chs[0..m], 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], &.{}, got.pri, expl);
+}
+
pub fn normalIndent(p: *Pardes, t: *Text, cnt: usize, add: bool) void {
t.select = false;
const sr = selRows(t);