summaryrefslogtreecommitdiff
path: root/src/term_pane.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/term_pane.zig')
-rw-r--r--src/term_pane.zig55
1 files changed, 42 insertions, 13 deletions
diff --git a/src/term_pane.zig b/src/term_pane.zig
index 47123ceb..5e50e15d 100644
--- a/src/term_pane.zig
+++ b/src/term_pane.zig
@@ -35,14 +35,18 @@ pub fn shellRows(p: *Pardes, pane: *Pane) ![]const []const u8 {
const arena = p.scratch.allocator();
const full = try pane.vt.screens.active.dumpStringAlloc(arena, .{ .screen = .{} });
var pit = pane.vt.screens.active.pages.rowIterator(.right_down, .{ .screen = .{} }, null);
- var ls: std.ArrayList([]const u8) = .empty;
- var it = std.mem.splitAny(u8, full, "\n");
+ // split yields one more item than delimiters; the extra final slot is the
+ // cursor row retained below.
+ const rows = try arena.alloc([]const u8, std.mem.count(u8, full, "\n") + 2);
+ var n: usize = 0;
+ var it = std.mem.splitScalar(u8, full, '\n');
while (it.next()) |raw| {
const is_prompt = if (pit.next()) |pin| pin.rowAndCell().row.semantic_prompt != .none else false;
- try ls.append(arena, if (is_prompt) "" else raw);
+ rows[n] = if (is_prompt) "" else raw;
+ n += 1;
}
- try ls.append(arena, "");
- return ls.items;
+ rows[n] = "";
+ return rows;
}
/// The body a terminal renders: the viewport's shell rows (prompt rows blanked
@@ -51,21 +55,35 @@ pub fn shellRows(p: *Pardes, pane: *Pane) ![]const []const u8 {
pub fn bodyText(arena: std.mem.Allocator, pane: *Pane) ![]const u8 {
const raw = try pane.vt.plainString(arena);
var prompts = pane.vt.screens.active.pages.rowIterator(.right_down, .{ .viewport = .{} }, null);
- var vp: std.ArrayList([]const u8) = .empty;
+ const vp = try arena.alloc([]const u8, std.mem.count(u8, raw, "\n") + 1);
var lines = std.mem.splitScalar(u8, raw, '\n');
+ var n: usize = 0;
while (lines.next()) |ln| {
const is_prompt = if (pane.mode != .tty)
if (prompts.next()) |pin| pin.rowAndCell().row.semantic_prompt != .none else false
else
false;
- try vp.append(arena, if (is_prompt) "" else ln);
+ vp[n] = if (is_prompt) "" else ln;
+ n += 1;
}
+ std.debug.assert(n == vp.len);
+
+ const len = fillBody(null, pane, vp);
+ const out = try arena.alloc(u8, len);
+ const filled = fillBody(out, pane, vp);
+ std.debug.assert(filled == out.len);
+ return out;
+}
+
+/// Run the terminal body row walk. A null destination counts bytes; a slice
+/// fills the exact allocation made from that count.
+fn fillBody(dst: ?[]u8, pane: *Pane, viewport: []const []const u8) usize {
const goff: i32 = @intCast(pane.vt.screens.active.pages.scrollbar().offset);
const off = pane.scroll();
var g: i32 = pane.gridRow(off);
// the buffer can start above the viewport: drop the lines scrolled past
var skip: usize = if (pane.ovl) |o| @intCast(@max(0, off - pane.surfRow(o.row))) else 0;
- var out: std.ArrayList(u8) = .empty;
+ var written: usize = 0;
var n: usize = 0;
while (n < pane.rows) {
if (pane.mode != .tty) if (pane.ovl) |o| if (g == o.row) {
@@ -74,21 +92,32 @@ pub fn bodyText(arena: std.mem.Allocator, pane: *Pane) ![]const u8 {
while (bit.next()) |ln| : (k += 1) {
if (k < skip) continue;
if (n >= pane.rows) break;
- if (n > 0) try out.append(arena, '\n');
- try out.appendSlice(arena, ln);
+ if (n > 0) {
+ if (dst) |out| out[written] = '\n';
+ written += 1;
+ }
+ if (dst) |out| @memcpy(out[written..][0..ln.len], ln);
+ written += ln.len;
n += 1;
}
skip = 0;
g += o.rows;
continue;
};
- if (n > 0) try out.append(arena, '\n');
+ if (n > 0) {
+ if (dst) |out| out[written] = '\n';
+ written += 1;
+ }
const vi = g - goff;
- if (vi >= 0 and @as(usize, @intCast(vi)) < vp.items.len) try out.appendSlice(arena, vp.items[@intCast(vi)]);
+ if (vi >= 0 and @as(usize, @intCast(vi)) < viewport.len) {
+ const line = viewport[@intCast(vi)];
+ if (dst) |out| @memcpy(out[written..][0..line.len], line);
+ written += line.len;
+ }
n += 1;
g += 1;
}
- return out.items;
+ return written;
}
/// tty colors: recolor each body cell from the emulator's own style so shell