summaryrefslogtreecommitdiff
path: root/src/Text.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/Text.zig')
-rw-r--r--src/Text.zig42
1 files changed, 39 insertions, 3 deletions
diff --git a/src/Text.zig b/src/Text.zig
index 70ec5369..a44241ca 100644
--- a/src/Text.zig
+++ b/src/Text.zig
@@ -60,8 +60,14 @@ cur_row: i32 = 0,
cur_col: i32 = 0,
msel: LineSel = .{},
vsel: CharSel = .{},
-sels: [max_selections - 1]SelRange = undefined,
+/// The ranges beside the primary, `nsel` of them: room for max_selections - 1
+/// made when a second range appears and given back when the selection is one
+/// range again, so a single cursor holds nothing here.
+sels: []SelRange = &.{},
nsel: u16 = 0,
+/// What a column's or the workspace's tag allocates its ranges from; a text
+/// with a pane uses the pane's.
+gpa: ?std.mem.Allocator = null,
select: bool = false,
/// sticky goal column for j/k runs (helix old_visual_position): any
/// non-vertical range write resets it to -1.
@@ -101,10 +107,39 @@ pub fn pane(t: *Text) ?*Pane {
/// Free what a text owns: its characters and the buffers its undo keeps.
pub fn deinit(t: *Text, gpa: std.mem.Allocator) void {
+ if (t.sels.len > 0) gpa.free(t.sels);
if (t.own) |own| gpa.free(own);
for (t.ed_undo[0..t.ed_undo_len]) |item| if (item.ovl) |overlay| gpa.free(overlay.text);
for (t.ed_redo[0..t.ed_redo_len]) |item| if (item.ovl) |overlay| gpa.free(overlay.text);
- t.* = .{ .what = t.what };
+ t.* = .{ .what = t.what, .gpa = t.gpa };
+}
+
+fn allocator(t: *Text) ?std.mem.Allocator {
+ return if (t.pane()) |w| w.gpa else t.gpa;
+}
+
+/// Room for the ranges beside the primary; false when there is none to be had.
+pub fn reserveSels(t: *Text) bool {
+ if (t.sels.len > 0) return true;
+ const gpa = t.allocator() orelse return false;
+ t.sels = gpa.alloc(SelRange, max_selections - 1) catch return false;
+ return true;
+}
+
+/// The ranges beside the primary as they were (a refused edit's undoing).
+pub fn restoreSels(t: *Text, kept: []const SelRange) void {
+ if (kept.len == 0) return t.dropSels();
+ if (!t.reserveSels()) return;
+ @memcpy(t.sels[0..kept.len], kept);
+ t.nsel = @intCast(kept.len);
+}
+
+/// Back to one range: the room for the others goes.
+pub fn dropSels(t: *Text) void {
+ t.nsel = 0;
+ if (t.sels.len == 0) return;
+ if (t.allocator()) |gpa| gpa.free(t.sels);
+ t.sels = &.{};
}
/// Bring the cursor into view (acme's textshow). Only a body scrolls to it;
@@ -185,7 +220,7 @@ pub fn setRange(t: *Text, text: []const u8, row0: i32, r0: modal.Selection, expl
.explicit = explicit or t.select,
};
t.msel.active = false;
- t.nsel = 0; // writing ONE range means the selection IS that range
+ t.dropSels(); // writing ONE range means the selection IS that range
t.cur_pinned = true;
t.sticky_col = -1;
t.normal.clear();
@@ -262,6 +297,7 @@ pub fn setRanges(t: *Text, text: []const u8, in: []const modal.Selection, sticky
}
n = k + 1;
setRange(t, text, 0, r[pri], explicit);
+ if (n > 1 and !t.reserveSels()) return; // no room: the primary alone
t.sticky_col = st[pri];
var w: usize = 0;
for (r[0..n], 0..) |x, idx| {