diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-29 20:56:14 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:17 -0300 |
| commit | 04b047fb366ea2c595708fb62467f690fce4b9ec (patch) | |
| tree | b2b765cbca96bcfd1785a39f60f0e0398a6aabb2 /src | |
| parent | 9554cc4c544632b01f740f895fd6db9f9952c02e (diff) | |
| download | pardes-04b047fb366ea2c595708fb62467f690fce4b9ec.tar.gz pardes-04b047fb366ea2c595708fb62467f690fce4b9ec.zip | |
Terminal.zig is terminal.zig: a file of functions and no fields takes a namespace's lowercase name
The last of the deferred renames, now that the tty and theme agents have landed: panes.terminal at its importers, the alias lines in Text.zig and File.zig and panes.zig's own uses following. dump.zig's Terminal struct, a dump record, is not this. The served sources list and docs/design.typ name the new file. test/perf.zig's references change, so the three perf baselines take its new harness id with their numbers as recorded. No behaviour changes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src')
| -rw-r--r-- | src/File.zig | 6 | ||||
| -rw-r--r-- | src/Text.zig | 18 | ||||
| -rw-r--r-- | src/body_layer.zig | 16 | ||||
| -rw-r--r-- | src/builtins.zig | 2 | ||||
| -rw-r--r-- | src/draw.zig | 8 | ||||
| -rw-r--r-- | src/dump.zig | 4 | ||||
| -rw-r--r-- | src/edit.zig | 26 | ||||
| -rw-r--r-- | src/exec.zig | 34 | ||||
| -rw-r--r-- | src/fs.zig | 2 | ||||
| -rw-r--r-- | src/host_io.zig | 2 | ||||
| -rw-r--r-- | src/layout.zig | 4 | ||||
| -rw-r--r-- | src/mouse.zig | 6 | ||||
| -rw-r--r-- | src/ninep/ctl.zig | 2 | ||||
| -rw-r--r-- | src/ninep/pane.zig | 4 | ||||
| -rw-r--r-- | src/ninep/pty.zig | 24 | ||||
| -rw-r--r-- | src/ninep/screen.zig | 4 | ||||
| -rw-r--r-- | src/panes.zig | 20 | ||||
| -rw-r--r-- | src/pardes.zig | 106 | ||||
| -rw-r--r-- | src/terminal.zig (renamed from src/Terminal.zig) | 0 | ||||
| -rw-r--r-- | src/tty/tty.zig | 2 |
20 files changed, 145 insertions, 145 deletions
diff --git a/src/File.zig b/src/File.zig index c4040863..8babb27d 100644 --- a/src/File.zig +++ b/src/File.zig @@ -17,7 +17,7 @@ const locations = @import("locations.zig"); const Pane = panes.Pane; const Output = panes.Output; const Mini = panes.mini; -const Terminal = panes.Terminal; +const terminal = panes.terminal; const Text = panes.Text; const SYNTAX_CONTEXT_AFTER_ROWS: usize = 2; @@ -782,7 +782,7 @@ pub fn open(p: *Pardes, id: usize, path: []const u8, line: usize) !*Pane { std.debug.assert(p.panes[id] == null and !p.reserved_slots[id]); const path_copy = try p.gpa.dupe(u8, path); errdefer p.gpa.free(path_copy); - const pane = try Terminal.createDoc(p.gpa, p.screen_w, p.screen_h); + const pane = try terminal.createDoc(p.gpa, p.screen_w, p.screen_h); errdefer p.gpa.destroy(pane); const history = try History.create(p.gpa); errdefer p.gpa.destroy(history); @@ -1239,7 +1239,7 @@ fn fillBody(dst: ?[]u8, pane: *Pane, f: *State, width: usize, record_wrap: bool) pub fn drawGutter(p: *Pardes, s: *Surface, pane: *Pane, r: pardes.Rect, tx: u16, tw: u16, body_h: u16, active: bool) void { const prefix_width = gutterWidth(pane); const goff = pane.scroll(); - const gcur = Terminal.gridCursor(pane); + const gcur = terminal.gridCursor(pane); const gcrow = if (pane.body.cur_pinned) pane.body.cur_row else @as(i32, gcur.y) + goff; const typing_body = pane.focus == .body and (pane.prompt == .none or pane.prompt == .del_side or pane.prompt == .repl_choice); const cur_line: i32 = if (active and typing_body) gcrow else std.math.minInt(i32); diff --git a/src/Text.zig b/src/Text.zig index ab23bf8e..cef76b3b 100644 --- a/src/Text.zig +++ b/src/Text.zig @@ -14,7 +14,7 @@ const config = @import("config.zig"); const memory = @import("memory.zig"); const Pane = panes.Pane; const File = panes.File; -const Terminal = panes.Terminal; +const terminal = panes.terminal; const Text = @This(); pub const Mode = enum { normal, insert, tty }; @@ -84,9 +84,9 @@ normal: modal.Normal.State = .{}, /// last f/F/t/T motion, for Alt-. repeat find_op: u8 = 0, find_ch: u21 = 0, -ed_undo: [Terminal.history_max]Terminal.Snapshot = undefined, +ed_undo: [terminal.history_max]terminal.Snapshot = undefined, ed_undo_len: usize = 0, -ed_redo: [Terminal.history_max]Terminal.Snapshot = undefined, +ed_redo: [terminal.history_max]terminal.Snapshot = undefined, ed_redo_len: usize = 0, /// Set when an edit would have changed characters this text does not own /// (a tag's computed prefix) and was refused; the key that tried it puts @@ -706,10 +706,10 @@ pub fn clampCursor(t: *Text, text: []const u8, row0: i32) void { t.show(); } -fn pushHistory(gpa: std.mem.Allocator, slots: []Terminal.Snapshot, len: *usize, value: Terminal.Snapshot) void { +fn pushHistory(gpa: std.mem.Allocator, slots: []terminal.Snapshot, len: *usize, value: terminal.Snapshot) void { if (len.* == slots.len) { if (slots[0].ovl) |overlay| gpa.free(overlay.text); - std.mem.copyForwards(Terminal.Snapshot, slots[0 .. slots.len - 1], slots[1..]); + std.mem.copyForwards(terminal.Snapshot, slots[0 .. slots.len - 1], slots[1..]); len.* -= 1; } slots[len.*] = value; @@ -719,14 +719,14 @@ fn pushHistory(gpa: std.mem.Allocator, slots: []Terminal.Snapshot, len: *usize, /// Record an edit buffer (a terminal's overlay, a tag's own text) as it /// stands before an edit, once per change of it, and forget what could have /// been redone. Null is a buffer not made yet. -pub fn remember(t: *Text, gpa: std.mem.Allocator, current: ?Terminal.EditBuffer) void { +pub fn remember(t: *Text, gpa: std.mem.Allocator, current: ?terminal.EditBuffer) void { if (t.ed_undo_len > 0) { const top = t.ed_undo[t.ed_undo_len - 1]; const same = if (top.ovl) |overlay| if (current) |now| overlay.row == now.row and overlay.rows == now.rows and std.mem.eql(u8, overlay.text, now.text) else false else current == null; if (same) return; } - const copy: ?Terminal.EditBuffer = if (current) |now| .{ .row = now.row, .rows = now.rows, .text = gpa.dupe(u8, now.text) catch return } else null; + const copy: ?terminal.EditBuffer = if (current) |now| .{ .row = now.row, .rows = now.rows, .text = gpa.dupe(u8, now.text) catch return } else null; pushHistory(gpa, &t.ed_undo, &t.ed_undo_len, .{ .ovl = copy, .cur_row = t.cur_row, .cur_col = t.cur_col, .vsel = t.vsel }); for (t.ed_redo[0..t.ed_redo_len]) |item| if (item.ovl) |overlay| gpa.free(overlay.text); t.ed_redo_len = 0; @@ -736,13 +736,13 @@ pub fn remember(t: *Text, gpa: std.mem.Allocator, current: ?Terminal.EditBuffer) /// other history and the state to return to comes back, its cursor and /// selection already restored here. The caller installs its buffer, which /// it then owns. Null when there is nothing to step to. -pub fn step(t: *Text, gpa: std.mem.Allocator, current: ?Terminal.EditBuffer, back: bool) ?Terminal.Snapshot { +pub fn step(t: *Text, gpa: std.mem.Allocator, current: ?terminal.EditBuffer, back: bool) ?terminal.Snapshot { const from, const from_len, const to, const to_len = if (back) .{ &t.ed_undo, &t.ed_undo_len, &t.ed_redo, &t.ed_redo_len } else .{ &t.ed_redo, &t.ed_redo_len, &t.ed_undo, &t.ed_undo_len }; if (from_len.* == 0) return null; - const copy: ?Terminal.EditBuffer = if (current) |now| .{ .row = now.row, .rows = now.rows, .text = gpa.dupe(u8, now.text) catch return null } else null; + const copy: ?terminal.EditBuffer = if (current) |now| .{ .row = now.row, .rows = now.rows, .text = gpa.dupe(u8, now.text) catch return null } else null; pushHistory(gpa, to, to_len, .{ .ovl = copy, .cur_row = t.cur_row, .cur_col = t.cur_col, .vsel = t.vsel }); from_len.* -= 1; const back_to = from[from_len.*]; diff --git a/src/body_layer.zig b/src/body_layer.zig index 6e1fe8f1..cb26c1f2 100644 --- a/src/body_layer.zig +++ b/src/body_layer.zig @@ -51,7 +51,7 @@ fn paintTerminalSelection(p: *Pardes, s: *Surface, pane: *Pane, r: Rect, rows: [ var visible: i32 = 0; while (lines.next()) |line| : (visible += 1) { if (visible >= r.h -| pane.tag_rows) break; - const source_row = if (raw) panes.Terminal.gridOffset(pane) + visible else pane.wrapAt(visible).line; + const source_row = if (raw) panes.terminal.gridOffset(pane) + visible else pane.wrapAt(visible).line; for (rows) |row| { const target = if (row.raw_terminal) (if (raw) row.row else pane.surfRow(row.row)) @@ -64,7 +64,7 @@ fn paintTerminalSelection(p: *Pardes, s: *Surface, pane: *Pane, r: Rect, rows: [ lo -|= row.prompt_bytes; hi -|= row.prompt_bytes; } else if (!row.raw_terminal and raw) { - const prefix = panes.Terminal.promptPrefixBytes(pane, source_row, line); + const prefix = panes.terminal.promptPrefixBytes(pane, source_row, line); lo += prefix; hi += prefix; } @@ -85,7 +85,7 @@ fn paintTerminalSelection(p: *Pardes, s: *Surface, pane: *Pane, r: Rect, rows: [ fn paintSourceSelection(p: *Pardes, s: *Surface, pane: *Pane, r: Rect, rows: []const Pane.PointerRow, bg: [3]u8, fg: ?[3]u8) void { if (pane.isTerminal()) return paintTerminalSelection(p, s, pane, r, rows, bg, fg); - const terminal_lines = if (pane.file == null) panes.Terminal.cursorLines(p, pane) catch return else &.{}; + const terminal_lines = if (pane.file == null) panes.terminal.cursorLines(p, pane) catch return else &.{}; const tx = r.x + config.GUTTER; const width = r.w -| config.GUTTER; const body_y = p.bodyTop(pane, r); @@ -291,7 +291,7 @@ pub fn renderBody(p: *Pardes, s: *Surface, arena: std.mem.Allocator, pane: *Pane panes.File.recolorSyntax(p, s, pane, f, r, tx, tw, body_h); panes.File.drawWrapMarkers(p, s, pane, r, tx, tw, body_h, pane_bg); } else if (pane.isTerminal() and p.settings.colors) { - panes.Terminal.recolorAnsi(p, s, pane, r, tx, tw, body_h, body); + panes.terminal.recolorAnsi(p, s, pane, r, tx, tw, body_h, body); } tz_color.end(); @@ -440,8 +440,8 @@ pub fn renderBody(p: *Pardes, s: *Surface, arena: std.mem.Allocator, pane: *Pane // (the tag's or a prompt's cursor wins while that is being typed into) if (active and pane.focus == .body and (pane.prompt == .none or pane.prompt == .del_side or pane.prompt == .repl_choice)) { if (pane.body.mode != .tty) { - const cur = panes.Terminal.gridCursor(pane); - const goff = panes.Terminal.gridOffset(pane); + const cur = panes.terminal.gridCursor(pane); + const goff = panes.terminal.gridOffset(pane); const crow = if (pane.body.cur_pinned) pane.body.cur_row else pane.surfRow(@as(i32, @intCast(cur.y)) + goff); const ccol = if (pane.body.cur_pinned) pane.body.cur_col else @as(i32, @intCast(cur.x)); const cwp = pane.wrapRow(crow, ccol); @@ -460,7 +460,7 @@ pub fn renderBody(p: *Pardes, s: *Surface, arena: std.mem.Allocator, pane: *Pane ccol; if (prow >= pane.tag_rows and cx >= 0 and prow < body_bottom and cx < tw) s.cursor = .{ .x = tx + @as(u16, @intCast(cx)), .y = body_y + @as(u16, @intCast(prow - pane.tag_rows)), .bar = pane.body.mode == .insert }; - } else if (panes.Terminal.visibleCursor(pane)) |cur| { + } else if (panes.terminal.visibleCursor(pane)) |cur| { if (cur.y + pane.tag_rows < body_bottom and cur.x < tw) s.cursor = .{ .x = tx + cur.x, .y = body_y + cur.y }; } @@ -492,5 +492,5 @@ pub fn bodyText(p: *Pardes, arena: std.mem.Allocator, pane: *Pane) ![]const u8 { unreachable; } if (pane.file) |*f| return panes.File.bodyText(arena, pane, f, p.settings.wrap); - return panes.Terminal.bodyText(arena, pane); + return panes.terminal.bodyText(arena, pane); } diff --git a/src/builtins.zig b/src/builtins.zig index cc86067e..c549b17f 100644 --- a/src/builtins.zig +++ b/src/builtins.zig @@ -625,7 +625,7 @@ pub const Kill = struct { stopped = true; continue; }; - if (!panes.Terminal.commandRunning(pane)) continue; + if (!panes.terminal.commandRunning(pane)) continue; const sent = pane.sent_command.?; if (names.len > 0) { var words = std.mem.tokenizeAny(u8, names, " \t"); diff --git a/src/draw.zig b/src/draw.zig index 10303117..ad24153f 100644 --- a/src/draw.zig +++ b/src/draw.zig @@ -63,7 +63,7 @@ pub fn place(p: *Pardes, s: *Surface) void { // A folded pane is its tag rows and nothing else. if (pane.collapsed) continue; const body_h = r.h -| pane.tag_rows; - const line: i32 = if (pane.file) |f| @intCast(@min(f.scroll, std.math.maxInt(i32))) else if (pane.isTerminal()) panes.Terminal.gridOffset(pane) else 0; + const line: i32 = if (pane.file) |f| @intCast(@min(f.scroll, std.math.maxInt(i32))) else if (pane.isTerminal()) panes.terminal.gridOffset(pane) else 0; s.addRegion(.{ .kind = .body, .owner = owner, .serial = pane.serial, .active = active, .line = line, .rect = .{ .x = r.x + config.GUTTER, .y = body_y, .w = r.w - config.GUTTER, .h = body_h } }); var rail: Region = .{ .kind = .rail, .owner = owner, .serial = pane.serial, .active = active, .rect = .{ .x = r.x, .y = body_y, .w = config.GUTTER, .h = body_h } }; // An image's rail has no thumb; a native PDF's is measured as its @@ -78,7 +78,7 @@ pub fn place(p: *Pardes, s: *Surface) void { .offset = page, .len = 1, } else blk: { - const gsb = panes.Terminal.scrollbar(pane); + const gsb = panes.terminal.scrollbar(pane); break :blk .{ .total = gsb.total, .offset = gsb.offset, .len = gsb.len }; }; const track_h: usize = body_h; @@ -241,7 +241,7 @@ fn glideCursor(p: *Pardes, s: *Surface) void { if (inside) frame = paneFrame(s, pane.serial, rect); const origin: [2]f32 = .{ @floatFromInt(rect.x), @floatFromInt(rect.y) }; const view = p.cursor_view; - const line: i64 = if (pane.file) |f| @intCast(f.scroll) else if (pane.isTerminal()) panes.Terminal.gridOffset(pane) else 0; + const line: i64 = if (pane.file) |f| @intCast(f.scroll) else if (pane.isTerminal()) panes.terminal.gridOffset(pane) else 0; if (view.serial == pane.serial and view.inside == inside) { // The pane moved in the layout: the springs move with it (its // own cursor's; one in a column's tag stays). @@ -719,7 +719,7 @@ pub fn render(p: *Pardes, arena: std.mem.Allocator) !*Surface { sb_off = page; sb_total = if (comptime pdf_enabled) at.pdf.?.page_count else 0; } else { - const sb = panes.Terminal.scrollbar(at); + const sb = panes.terminal.scrollbar(at); sb_off = sb.offset; sb_total = sb.total; } diff --git a/src/dump.zig b/src/dump.zig index 1eabdae8..73d4e4ed 100644 --- a/src/dump.zig +++ b/src/dump.zig @@ -731,7 +731,7 @@ pub fn dumpState(p: *Pardes) !void { } else if (pane.image) |iv| try pardes.panes.Image.dumpPane(p, arena, pane, tag, body, scroll, iv.path, iv.raw) else - try pardes.panes.Terminal.dumpPane(pane, arena, tag, body, scroll); + try pardes.panes.terminal.dumpPane(pane, arena, tag, body, scroll); dp.tag_tail = pane.tag.own; if (dp.file) |*df| { const dot = ctlfs.pane.dotOf(pane); @@ -914,7 +914,7 @@ fn initDump(gpa: std.mem.Allocator, opts: Options, zon_bytes: []const u8, previo // and how it ended: its command is not run again. const live = comptime (terminal_panes and platform != .web and !isolated); const revive = live and t.cwd.len <= effect_path_cap and t.command.len == 0; - const restored = try pardes.panes.Terminal.restore(p, src, revive); + const restored = try pardes.panes.terminal.restore(p, src, revive); p.installPane(i, restored); try restored.setOwnedCwd(t.cwd); if (t.shell.len > 0) restored.shell = try gpa.dupe(u8, t.shell); diff --git a/src/edit.zig b/src/edit.zig index 26062029..98c60efb 100644 --- a/src/edit.zig +++ b/src/edit.zig @@ -113,7 +113,7 @@ pub fn clipRequest(p: *Pardes, id: usize, mode: @FieldType(ClipRequest, "mode")) pub fn typeToTty(p: *Pardes, id: usize, pane: *const Pane, text: []const u8) void { if (text.len == 0) return; - if (panes.Terminal.bracketedPaste(pane)) { + if (panes.terminal.bracketedPaste(pane)) { p.emitWrite(id, "\x1b[200~"); p.emitWrite(id, text); p.emitWrite(id, "\x1b[201~"); @@ -225,7 +225,7 @@ pub fn capturePointerSelection(p: *Pardes, pane: *Pane, slot: usize) !void { // Refresh the source map before translating the rendered rectangle. const raw = pane.isTerminal() and pane.body.mode == .tty; const body = try body_layer.bodyText(p, p.scratch.allocator(), pane); - const terminal_lines = if (pane.file == null and !raw) try panes.Terminal.cursorLines(p, pane) else &.{}; + const terminal_lines = if (pane.file == null and !raw) try panes.terminal.cursorLines(p, pane) else &.{}; const first = @max(0, @min(gesture.r0, gesture.r1) - pane.tag_rows); const logical_rows = if (pane.body_rows > 0) pane.body_rows else pane.rows; const last = @min(@as(i32, logical_rows) - 1, @max(gesture.r0, gesture.r1) - pane.tag_rows); @@ -242,7 +242,7 @@ pub fn capturePointerSelection(p: *Pardes, pane: *Pane, slot: usize) !void { const left: i32 = if (cols.first) cols.lo else prefix; const right: i32 = cols.hi orelse std.math.maxInt(i32) / 2; var source = pane.wrapAt(visible); - if (raw) source = .{ .line = panes.Terminal.gridOffset(pane) + visible, .at = 0 }; + if (raw) source = .{ .line = panes.terminal.gridOffset(pane) + visible, .at = 0 }; const line = if (raw) modal.lineSlice(body, @intCast(visible)) else pointerSourceLine(pane, terminal_lines, source.line); var next = pane.wrapAt(visible + 1); if (raw) next = .{ .line = source.line + 1, .at = 0 }; @@ -256,7 +256,7 @@ pub fn capturePointerSelection(p: *Pardes, pane: *Pane, slot: usize) !void { .hi = if (right < prefix) lo else if (cols.hi == null) end else @min(end, modal.nextGrapheme(line, at)), .to_edge = cols.hi == null, .raw_terminal = raw, - .prompt_bytes = if (raw) panes.Terminal.promptPrefixBytes(pane, source.line, line) else 0, + .prompt_bytes = if (raw) panes.terminal.promptPrefixBytes(pane, source.line, line) else 0, }; const rendered = modal.lineSlice(body, @intCast(visible)); const prefix_end = @min(rendered.len, @as(usize, @intCast(@min(prefix, right + 1)))); @@ -276,7 +276,7 @@ pub fn selectionText(p: *Pardes, pane: *Pane, sl: Pane.Sel) ![]const u8 { const arena = p.scratch.allocator(); if (pane.rawPointerText(sl)) |text| return arena.dupe(u8, text); if (pane.pointerSelection(sl)) |rows| { - const terminal_lines = if (pane.file == null) try panes.Terminal.cursorLines(p, pane) else &.{}; + const terminal_lines = if (pane.file == null) try panes.terminal.cursorLines(p, pane) else &.{}; var total: usize = rows.len -| 1; for (rows) |row| { const line = pointerSourceLine(pane, terminal_lines, row.row); @@ -402,7 +402,7 @@ pub fn paneCursorLines(p: *Pardes, t: *Text) ![]const []const u8 { if (comptime pdf_enabled) return panes.Pdf.textLines(&pane.pdf.?, p.pdf_gpa, arena); unreachable; } - return panes.Terminal.cursorLines(p, pane); + return panes.terminal.cursorLines(p, pane); } pub fn paneByteAtDisplay(p: *Pardes, t: *Text, row: i32, from_raw: i32, display_col: i32) i32 { @@ -448,8 +448,8 @@ pub fn flatSurface(p: *Pardes, t: *Text) ![]const u8 { if (comptime pdf_enabled) return pane.pdf.?.ensureText(p.pdf_gpa); unreachable; } - const lines = try panes.Terminal.cursorLines(p, pane); - return panes.Terminal.flatSurface(p, pane, lines); + const lines = try panes.terminal.cursorLines(p, pane); + return panes.terminal.flatSurface(p, pane, lines); } pub fn paneWrapWidth(p: *const Pardes, t: *Text) usize { @@ -474,7 +474,7 @@ fn editText(p: *Pardes, t: *Text, lo: i32, hi: i32, col: i32) ?panes.EditText { const pane = t.pane() orelse return null; if (pane.file) |f| return .{ .text = f.content, .row0 = 0 }; if (pane.image != null or pane.hasPdf()) return null; - return panes.Terminal.editText(p, pane, lo, hi, col); + return panes.terminal.editText(p, pane, lo, hi, col); } fn editTextEol(p: *Pardes, t: *Text, b: Bounds) ?panes.EditText { @@ -535,7 +535,7 @@ pub fn setEditTextEnd(p: *Pardes, t: *Text, new: []u8, known_end: ?modal.Cursor) }; t.last_edit = .{ .row = @as(i32, @intCast(end.row)) + row0, .col = @intCast(end.col) }; if (pane.file) |*f| return panes.File.setContent(p, f, new); - panes.Terminal.setEditText(p, pane, new); + panes.terminal.setEditText(p, pane, new); } /// Take a text an edit made that nothing keeps, holding it until the next @@ -2057,7 +2057,7 @@ pub fn pushUndo(p: *Pardes, t: *Text) void { } const pane = t.pane() orelse return; if (pane.file != null) return panes.File.pushUndo(p, pane); - panes.Terminal.pushUndo(p, pane); + panes.terminal.pushUndo(p, pane); } pub fn doUndo(p: *Pardes, t: *Text) void { @@ -2065,7 +2065,7 @@ pub fn doUndo(p: *Pardes, t: *Text) void { if (t.what != .body) return; const pane = t.pane() orelse return; if (pane.file != null) return panes.File.undo(p, pane); - panes.Terminal.undo(p, pane); + panes.terminal.undo(p, pane); } pub fn doRedo(p: *Pardes, t: *Text) void { @@ -2073,7 +2073,7 @@ pub fn doRedo(p: *Pardes, t: *Text) void { if (t.what != .body) return; const pane = t.pane() orelse return; if (pane.file != null) return panes.File.redo(p, pane); - panes.Terminal.redo(p, pane); + panes.terminal.redo(p, pane); } /// Undo or redo an edit of a text's own characters; stepping back past its diff --git a/src/exec.zig b/src/exec.zig index 2c42389c..221de105 100644 --- a/src/exec.zig +++ b/src/exec.zig @@ -42,11 +42,11 @@ pub fn takesCommandLine(p: *const Pardes, id: usize) bool { // idle, EMPTY prompt is typed at; otherwise a new pane runs the line. // (Text in pardes's own edit buffer is not the shell's yet: an exec of // it, or of anything, types only what it runs.) - if (panes.Terminal.promptInputReady(pane) and !panes.Terminal.promptInputEmpty(pane)) return false; + if (panes.terminal.promptInputReady(pane) and !panes.terminal.promptInputEmpty(pane)) return false; // A mounted shell sits behind sudo's process supervisor. Its OSC 133 // prompt marks identify input readiness; the launcher's executable is // not the shell executable used by the ordinary process heuristic. - if (pane.v9fs_on_spawn) return panes.Terminal.promptInputReady(pane); + if (pane.v9fs_on_spawn) return panes.terminal.promptInputReady(pane); return !p.hostTtyTaken(id); } @@ -111,7 +111,7 @@ pub fn pointerOperand(p: *Pardes, pane: *Pane, clicked: Pane.Sel) PointerOperand const visible = clicked.r0 - @as(i32, pane.tag_rows); const wrapped = pane.wrapAt(visible); const raw = pane.isTerminal() and pane.body.mode == .tty and clicked.r0 >= pane.tag_rows; - const row = if (raw) panes.Terminal.gridOffset(pane) + visible else wrapped.line; + const row = if (raw) panes.terminal.gridOffset(pane) + visible else wrapped.line; const raw_line = if (raw) modal.lineSlice(body_layer.bodyText(p, p.scratch.allocator(), pane) catch "", @intCast(@max(0, visible))) else ""; const col = if (raw) @as(i32, @intCast(panes.File.rawAtDisplay(raw_line, @intCast(@max(0, clicked.c0))))) @@ -143,7 +143,7 @@ pub fn pointerOperand(p: *Pardes, pane: *Pane, clicked: Pane.Sel) PointerOperand lo -|= span.prompt_bytes; hi -|= span.prompt_bytes; } else if (!span.raw_terminal and raw) { - const prefix = panes.Terminal.promptPrefixBytes(pane, row, raw_line); + const prefix = panes.terminal.promptPrefixBytes(pane, row, raw_line); lo += prefix; hi += prefix; } @@ -429,8 +429,8 @@ pub fn evictLonePristineTty(p: *Pardes, col: usize, keep_id: usize) void { const tt = p.panes[tty_id] orelse return; // No typing, cursor on the first prompt line, no scrollback: this is // the throwaway boot placeholder a document may replace. - if (tt.ovl != null or panes.Terminal.gridCursor(tt).y != 0 or - panes.Terminal.scrollbar(tt).total > tt.rows) return; + if (tt.ovl != null or panes.terminal.gridCursor(tt).y != 0 or + panes.terminal.scrollbar(tt).total > tt.rows) return; p.deinitPane(tt) catch |err| return p.reportError(tty_id, "close", err); layout.compute(p); // a just-stacked doc has no rect yet; absorb snaps to rows _ = layout.absorbVWeight(p, tty_id, null); @@ -588,7 +588,7 @@ pub fn sendToRepl(p: *Pardes, from: usize, to: usize, text: []const u8) void { var said: [128]u8 = undefined; return p.setMessage(from, std.fmt.bufPrint(&said, "Repl: {d} bytes is more than can be sent at once; send less", .{n}) catch "Repl: too long to send"); } - const pasted = panes.Terminal.bracketedPaste(dst); + const pasted = panes.terminal.bracketedPaste(dst); edit.typeToTty(p, to, dst, clean[0..n]); // A Python block whose last line is indented, or a bare header, is not // over at one Enter, pasted into 3.13's REPL or typed into the old one: @@ -611,7 +611,7 @@ pub fn sendToRepl(p: *Pardes, from: usize, to: usize, text: []const u8) void { dst.repl_enter_due_ns = clockNow(p) + repl_enter_wait_ns; } else for (0..enters) |_| p.emitWrite(to, "\r"); // Line by line, a blank line inside a Python block ends the block. - if (!panes.Terminal.bracketedPaste(dst) and !dst.repl_warned and (if (dst.repl) |r| r.lang == python_lang else false) and + if (!panes.terminal.bracketedPaste(dst) and !dst.repl_warned and (if (dst.repl) |r| r.lang == python_lang else false) and std.mem.indexOfScalar(u8, text, '\n') != null) { dst.repl_warned = true; @@ -671,9 +671,9 @@ pub fn executeFrom(p: *Pardes, id: usize, txt: []const u8, from_body: bool) ?usi return null; } if (!takesCommandLine(p, id)) return runCommand(p, id, cmd); - panes.Terminal.padOutputBelowEdits(p, id); - panes.Terminal.noteCommand(pane, cmd); - if (panes.Terminal.queuePendingCommand(pane, cmd) catch |err| { + panes.terminal.padOutputBelowEdits(p, id); + panes.terminal.noteCommand(pane, cmd); + if (panes.terminal.queuePendingCommand(pane, cmd) catch |err| { p.reportError(id, "queue command", err); return id; }) return id; @@ -733,8 +733,8 @@ fn runCommand(p: *Pardes, from: usize, line: []const u8) ?usize { // saved cursor), mouse reports and bracketed paste off, the cursor // shown, colours reset. (Not DECSTR: ghostty's stream does not // implement it.) - if (panes.Terminal.onAlternateScreen(pane)) panes.Terminal.feedOutput(p, pane, "\x1b[?1049l"); - panes.Terminal.feedOutput(p, pane, "\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1006l\x1b[?2004l\x1b[?25h\x1b[0m"); + if (panes.terminal.onAlternateScreen(pane)) panes.terminal.feedOutput(p, pane, "\x1b[?1049l"); + panes.terminal.feedOutput(p, pane, "\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1006l\x1b[?2004l\x1b[?25h\x1b[0m"); echoCommand(p, pane, line); p.emitSpawn(id, pane.serial, pane.cwdSlice()); noteRun(p, pane, "run", line); @@ -760,10 +760,10 @@ fn runCommand(p: *Pardes, from: usize, line: []const u8) ?usize { fn echoCommand(p: *Pardes, pane: *Pane, line: []const u8) void { var buf: [2 * command_max + 8]u8 = undefined; var w = std.Io.Writer.fixed(&buf); - w.writeAll(if (panes.Terminal.gridCursor(pane).x != 0) "\r\n% " else "% ") catch {}; + w.writeAll(if (panes.terminal.gridCursor(pane).x != 0) "\r\n% " else "% ") catch {}; for (line) |c| (if (c == '\n') w.writeAll("\r\n") else w.writeByte(c)) catch {}; w.writeAll("\r\n") catch {}; - panes.Terminal.feedOutput(p, pane, w.buffered()); + panes.terminal.feedOutput(p, pane, w.buffered()); } /// `run <serial> <word>` or `exit <serial> <N|?>` in the log. @@ -788,8 +788,8 @@ pub fn commandDone(p: *Pardes, id: usize, status: ?u8) void { var buf: [16]u8 = undefined; const code = if (status) |n| std.fmt.bufPrint(&buf, "{d}", .{n}) catch "?" else "?"; var said: [32]u8 = undefined; - const lead = if (panes.Terminal.gridCursor(pane).x != 0) "\r\n" else ""; - panes.Terminal.feedOutput(p, pane, std.fmt.bufPrint(&said, "{s}exit {s}\r\n", .{ lead, code }) catch "exit ?\r\n"); + const lead = if (panes.terminal.gridCursor(pane).x != 0) "\r\n" else ""; + panes.terminal.feedOutput(p, pane, std.fmt.bufPrint(&said, "{s}exit {s}\r\n", .{ lead, code }) catch "exit ?\r\n"); noteRun(p, pane, "exit", code); p.needs_frame = true; } @@ -57,7 +57,7 @@ const source_files = [_]Source{ .{ .path = "src/Output.zig", .contents = @embedFile("Output.zig") }, .{ .path = "src/mini.zig", .contents = @embedFile("mini.zig") }, .{ .path = "src/image.zig", .contents = @embedFile("image.zig") }, - .{ .path = "src/Terminal.zig", .contents = @embedFile("Terminal.zig") }, + .{ .path = "src/terminal.zig", .contents = @embedFile("terminal.zig") }, .{ .path = "src/pdf_view.zig", .contents = @embedFile("pdf_view.zig") }, .{ .path = "src/layout.zig", .contents = @embedFile("layout.zig") }, .{ .path = "src/fs.zig", .contents = @embedFile("fs.zig") }, diff --git a/src/host_io.zig b/src/host_io.zig index 9e3fbf79..e7be28cd 100644 --- a/src/host_io.zig +++ b/src/host_io.zig @@ -1293,7 +1293,7 @@ pub fn forkShell( var command: std.Io.Writer.Allocating = .init(c.gpa); defer command.deinit(); try @import("linux/v9fs.zig").writeLaunchCommand(&command.writer, path, std.mem.span(socket), &spawn.argv); - if (!try pardes.panes.Terminal.queuePendingCommand(pn, command.written())) return error.ShellAlreadyStarted; + if (!try pardes.panes.terminal.queuePendingCommand(pn, command.written())) return error.ShellAlreadyStarted; } // Built here and not after the fork: between fork and exec the child may // not call setenv, whose malloc can deadlock against a pty reader thread diff --git a/src/layout.zig b/src/layout.zig index 28f567bc..28f3d270 100644 --- a/src/layout.zig +++ b/src/layout.zig @@ -607,7 +607,7 @@ fn blankRows(p: *const Pardes, id: usize) u16 { // The empty line after a final newline shows nothing. (panes.File.nlines(p.gpa, f) -| @intFromBool(std.mem.endsWith(u8, f.content, "\n"))) -| f.scroll else if (pane.isTerminal()) - @as(usize, panes.Terminal.gridCursor(pane).y) + 1 + @as(usize, panes.terminal.gridCursor(pane).y) + 1 else rows; return rows -| @as(u16, @intCast(@min(used, rows))); @@ -742,7 +742,7 @@ pub fn splitBelow(p: *Pardes, src_id: usize, nw: *Pane) void { // Its tag may wrap to more than one row: the rows under it are its body. const tag_h = @max(BOX_H, src.tag_rows); const body: u16 = if (src_h > tag_h) src_h - tag_h else 1; - const cur: u16 = if (!src.isTerminal()) body / 2 else panes.Terminal.gridCursor(src).y + 1; + const cur: u16 = if (!src.isTerminal()) body / 2 else panes.terminal.gridCursor(src).y + 1; // cap keep so a content-full source still leaves the new pane a tag + // a few body rows (an Alt-n from a full shell was born 0 rows tall) // ...and the source min_body_rows of its own (exec.placeNew), when it diff --git a/src/mouse.zig b/src/mouse.zig index 1f2648b0..cb0378de 100644 --- a/src/mouse.zig +++ b/src/mouse.zig @@ -821,7 +821,7 @@ fn mirrorTtySelection(p: *Pardes, pane: *Pane) void { const forward = gesture.r1 > gesture.r0 or (gesture.r1 == gesture.r0 and gesture.c1 >= gesture.c0); const endpoint = rows[if (forward) rows.len - 1 else 0]; const body = body_layer.bodyText(p, p.scratch.allocator(), pane) catch return; - const line = modal.lineSlice(body, @intCast(@max(0, endpoint.row - panes.Terminal.gridOffset(pane)))); + const line = modal.lineSlice(body, @intCast(@max(0, endpoint.row - panes.terminal.gridOffset(pane)))); const byte = if (forward) modal.prevGrapheme(line, @min(endpoint.hi, line.len)) else endpoint.lo; pane.body.cur_row = pane.surfRow(endpoint.row); var shown = std.mem.trimEnd(u8, line[@min(endpoint.prompt_bytes, line.len)..], " \t"); @@ -1137,11 +1137,11 @@ fn chordCutPaste(p: *Pardes, cut: bool) void { if (cut) return; const y = p.registers.text(p.gpa, pardes.Registers.default) orelse return; if (y.len == 0) return; - if (panes.Terminal.reportsMouse(pane)) { + if (panes.terminal.reportsMouse(pane)) { const col: u16 = @intCast(std.math.clamp(pane.sel[sel_slot].c1 + 1, 1, 9999)); const row: u16 = @intCast(std.math.clamp(pane.sel[sel_slot].r1 - @as(i32, pane.tag_rows) + 1, 1, 9999)); var mb: [32]u8 = undefined; - if (panes.Terminal.mouseFormatSgr(pane)) { + if (panes.terminal.mouseFormatSgr(pane)) { p.emitWrite(s.id, std.fmt.bufPrint(&mb, "\x1b[<0;{d};{d}M\x1b[<0;{d};{d}m", .{ col, row, col, row }) catch return); } else { // ponytail: legacy X10 bytes; add utf8/urxvt formats if an app ever wants them diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 5bf354eb..060e0d6c 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -344,7 +344,7 @@ pub fn paneText(p: *Pardes, pane: *Pane, buf: []u8) []const u8 { pane.serial, pane_files.tagOf(p, pane).len, // A terminal's body is its history, read whole on an open's first read. - if (pane.isTerminal()) panes.Terminal.screenTextLen(pane) else pane_files.bodyOf(pane).len, + if (pane.isTerminal()) panes.terminal.screenTextLen(pane) else pane_files.bodyOf(pane).len, @as(u32, 0), @intFromBool(pane_files.dirtyOf(pane)), pane.cols, diff --git a/src/ninep/pane.zig b/src/ninep/pane.zig index 3c1e181e..3fad1fae 100644 --- a/src/ninep/pane.zig +++ b/src/ninep/pane.zig @@ -258,7 +258,7 @@ pub fn fileSize(p: *Pardes, id: usize, f: PaneFile) u64 { const pf = &pane.fs; return switch (f) { // A terminal's body reads its screen and history, as text. - .body => if (pane.file == null and pane.isTerminal()) panes.Terminal.screenTextLen(pane) else if (pdfText(p, pane)) |t| t.len else bodyOf(pane).len, + .body => if (pane.file == null and pane.isTerminal()) panes.terminal.screenTextLen(pane) else if (pdfText(p, pane)) |t| t.len else bodyOf(pane).len, .data, .xdata => bodyOf(pane).len, .tag => tagOf(p, pane).len, .name => nameOf(pane).len + 1, @@ -399,7 +399,7 @@ fn readBody(p: *Pardes, req: Req, id: usize, pane: *Pane) Reply { return tree.stagedReply(p, req); } if (req.handle != 0 and pane.isTerminal()) return screen.readSnapshot(p, req, pane); - const text = panes.Terminal.screenTextAlloc(pane, p.gpa) catch + const text = panes.terminal.screenTextAlloc(pane, p.gpa) catch return Reply.fail(req.tag, E.NOMEM); defer p.gpa.free(text); const out = p.fs.stage(p.gpa); diff --git a/src/ninep/pty.zig b/src/ninep/pty.zig index 8ba9f4df..0f5202cc 100644 --- a/src/ninep/pty.zig +++ b/src/ninep/pty.zig @@ -139,9 +139,9 @@ pub fn readStatus(p: *Pardes, req: Req, id: usize, pane: *Pane) Reply { // or a line is typed at the prompt. Before the shell's first prompt a // run waits for it rather than answer busy, so that is not busy. // A build with no terminal panes (the board) has no marks to read. - const marks = if (comptime !pardes.panes.Terminal.enabled) null else if (pane.terminal) |state| &state.stream.handler else null; + const marks = if (comptime !pardes.panes.terminal.enabled) null else if (pane.terminal) |state| &state.stream.handler else null; const busy = p.hostTtyTaken(id) or (!pane.fs.unmarked and (waitingRun(p, pane) != null or - (if (marks) |m| m.prompts > 0 and (m.phase != .input or !pardes.panes.Terminal.promptInputEmpty(pane)) else false))); + (if (marks) |m| m.prompts > 0 and (m.phase != .input or !pardes.panes.terminal.promptInputEmpty(pane)) else false))); const out = p.fs.stage(p.gpa); const size = winsize(pane); out.print(p.gpa, "{d:>11} {d:>11} {d:>11}\n", .{ size[0], size[1], @intFromBool(busy) }) catch {}; @@ -222,7 +222,7 @@ pub fn writeRun(p: *Pardes, req: Req, id: usize, pane: *Pane) Reply { if (std.mem.trim(u8, line, " \t").len == 0) return tree.failText(req.tag, E.INVAL, e_bad_line); if (line.len == 0) return tree.failText(req.tag, E.INVAL, e_bad_line); for (line) |c| if (c < ' ' and c != '\t') return tree.failText(req.tag, E.INVAL, e_bad_line); - if (comptime !pardes.panes.Terminal.enabled) return tree.failText(req.tag, E.INVAL, e_bad_line); + if (comptime !pardes.panes.terminal.enabled) return tree.failText(req.tag, E.INVAL, e_bad_line); const pf = &pane.fs; const state = pane.terminal orelse return tree.failText(req.tag, E.INVAL, e_bad_line); const marks = &state.stream.handler; @@ -235,7 +235,7 @@ pub fn writeRun(p: *Pardes, req: Req, id: usize, pane: *Pane) Reply { } else if (pf.unmarked) { answer(p, slot, "error no prompt marks", .{}); } else if (waitingRun(p, pane) != null or (marks.prompts > 0 and (marks.phase != .input or - !pardes.panes.Terminal.promptInputEmpty(pane) or p.hostTtyTaken(id)))) + !pardes.panes.terminal.promptInputEmpty(pane) or p.hostTtyTaken(id)))) { // Something is running or someone has typed at the prompt: sending // now would type into the middle of it. The phase is pardes's own @@ -288,7 +288,7 @@ pub fn readRun(p: *Pardes, req: Req) Reply { /// for the prompt means whoever reads the answer can send the next command /// at once instead of finding the shell still drawing it. pub fn noteMarks(p: *Pardes, id: usize, pane: *Pane) void { - if (comptime !pardes.panes.Terminal.enabled) return; + if (comptime !pardes.panes.terminal.enabled) return; const state = pane.terminal orelse return; const pf = &pane.fs; const slot = waitingRun(p, pane) orelse return; @@ -299,13 +299,13 @@ pub fn noteMarks(p: *Pardes, id: usize, pane: *Pane) void { pf.run = null; return; } - if (marks.phase != .input or !pardes.panes.Terminal.promptInputEmpty(pane) or p.hostTtyTaken(id)) return; + if (marks.phase != .input or !pardes.panes.terminal.promptInputEmpty(pane) or p.hostTtyTaken(id)) return; slot.want = marks.started +% 1; slot.prompts = marks.prompts; slot.declined = marks.declined; p.emitWrite(id, slot.line); p.emitWrite(id, "\r"); - pardes.panes.Terminal.noteCommand(pane, slot.line); + pardes.panes.terminal.noteCommand(pane, slot.line); slot.phase = .sent; return; } @@ -319,7 +319,7 @@ pub fn noteMarks(p: *Pardes, id: usize, pane: *Pane) void { // typed into an empty prompt, so throw it away with Ctrl-C, which // drops the whole pending command, and answer at the prompt after // that, so the next line finds the shell clear. - const empty = pardes.panes.Terminal.promptInputEmpty(pane); + const empty = pardes.panes.terminal.promptInputEmpty(pane); switch (slot.cleared) { .no => { if (marks.continuation) { @@ -360,7 +360,7 @@ pub fn noteMarks(p: *Pardes, id: usize, pane: *Pane) void { /// Answers a run whose command ended with `status`: the header and the /// output it printed. fn finish(p: *Pardes, slot: *Run, pane: *Pane, status_code: ?i32) void { - const printed = pardes.panes.Terminal.commandOutput(pane, p.gpa, output_rows) catch + const printed = pardes.panes.terminal.commandOutput(pane, p.gpa, output_rows) catch return answer(p, slot, "error out of memory", .{}); defer p.gpa.free(printed.text); // Keep the tail, cut at a line start so the first line kept is whole. @@ -400,7 +400,7 @@ pub fn shellExited(p: *Pardes, pane: *Pane, status: ?u8) void { const code = status orelse return; if (waitingRun(p, pane)) |slot| if (slot.phase == .sent) { pane.fs.run = null; - pardes.panes.Terminal.endOutputHere(pane); + pardes.panes.terminal.endOutputHere(pane); finish(p, slot, pane, code); }; var buf: [4]u8 = undefined; @@ -963,10 +963,10 @@ test "a run's answer says cut when its output's start is gone, and reads a bound // A resize between its start and end reflows the pins with the text. h = sh.send(p, node); sh.put(p, c ++ "one\r\ntwo\r\n"); - pardes.panes.Terminal.resizeGrid(pane, gpa, 50, 20); + pardes.panes.terminal.resizeGrid(pane, gpa, 50, 20); sh.put(p, d ++ prompt); try testing.expectEqualStrings("exit 0\none\ntwo\n", sh.answered(p, node, h, into)); - pardes.panes.Terminal.resizeGrid(pane, gpa, pane.cols, pane.rows); + pardes.panes.terminal.resizeGrid(pane, gpa, pane.cols, pane.rows); // A full-screen program leaves nothing on the primary screen, and a D // with no status is not a success. diff --git a/src/ninep/screen.zig b/src/ninep/screen.zig index f496da03..b991c3e4 100644 --- a/src/ninep/screen.zig +++ b/src/ninep/screen.zig @@ -47,7 +47,7 @@ pub fn readSnapshot(p: *Pardes, req: Req, pane: ?*Pane) Reply { const snapshot = &(tree.openOf(p, req) orelse return Reply.fail(req.tag, E.INVAL)).what.snapshot; if (snapshot.* == null) { const terminal = pane orelse return Reply.fail(req.tag, E.INVAL); - snapshot.* = panes.Terminal.screenTextAlloc(terminal, p.gpa) catch return Reply.fail(req.tag, E.NOMEM); + snapshot.* = panes.terminal.screenTextAlloc(terminal, p.gpa) catch return Reply.fail(req.tag, E.NOMEM); } const bytes = snapshot.*.?; const off = @min(req.off, bytes.len); @@ -129,7 +129,7 @@ test "terminal body handles keep one history snapshot across fragmented reads" { const node = Node.of(serialOf(p), .body); p.update(.{ .output = .{ .pane = 0, .bytes = "old caf\xc3\xa9\r\nold tail" } }); while (p.nextEffect()) |_| {} - const original = try panes.Terminal.screenTextAlloc(p.panes[0].?, gpa); + const original = try panes.terminal.screenTextAlloc(p.panes[0].?, gpa); defer gpa.free(original); const opened = call(p, .{ .tag = 1, .op = .open, .node = node }); try testing.expectEqual(Status.ok, opened.reply.status); diff --git a/src/panes.zig b/src/panes.zig index b6fdc31a..0380f89f 100644 --- a/src/panes.zig +++ b/src/panes.zig @@ -89,7 +89,7 @@ pub const Pane = struct { pub const Cwd = union(enum) { none, inherited: *Pane, owned: []u8 }; pub const Focus = enum { body, tag }; - terminal: ?*Terminal.State = null, + terminal: ?*terminal.State = null, gpa: std.mem.Allocator, serial: u32 = 0, /// What the control filesystem keeps of this pane -- addr and limit, the @@ -116,7 +116,7 @@ pub const Pane = struct { /// A shell has run in it (Pardes.acknowledgeShell): a Tty whose first /// shell fails to start closes, one started again keeps its pane. had_shell: bool = false, - pending_command: Terminal.PendingCommand = .{}, + pending_command: terminal.PendingCommand = .{}, /// The last command line pardes typed into this terminal (an exec, a /// middle click, a pty/run): its first word, which Kill matches, and /// the shell's command count its start (C mark) reaches. @@ -227,7 +227,7 @@ pub const Pane = struct { next_pointer_selection: u64 = 0, /// terminals only: the typed-text buffer standing in for shell rows - ovl: ?Terminal.EditBuffer = null, + ovl: ?terminal.EditBuffer = null, tty_filter: bool = false, msg: [256]u8 = undefined, msg_len: u16 = 0, @@ -486,7 +486,7 @@ pub const Pane = struct { pub fn scroll(pane: *Pane) i32 { if (pane.file) |f| return @intCast(f.scroll); if (comptime Pdf.enabled) if (pane.pdf) |pv| return @intCast(pv.text_scroll); - return pane.surfRow(Terminal.gridOffset(pane)); + return pane.surfRow(terminal.gridOffset(pane)); } pub fn wrapAt(pane: *Pane, vr: i32) struct { line: i32, at: i32 } { @@ -531,8 +531,8 @@ pub const Pane = struct { } } else { // the vt scrolls in SHELL rows; convert through the edit buffer - const off = Terminal.gridOffset(pane); - Terminal.scrollGrid(pane, pane.gridRow(pane.surfRow(off) + delta) - off); + const off = terminal.gridOffset(pane); + terminal.scrollGrid(pane, pane.gridRow(pane.surfRow(off) + delta) - off); } } @@ -611,8 +611,8 @@ pub const Pane = struct { pane.body.cur_row = pane.scroll(); pane.body.cur_col = 0; } else { - const cur = Terminal.gridCursor(pane); - pane.body.cur_row = pane.surfRow(@as(i32, cur.y) + Terminal.gridOffset(pane)); + const cur = terminal.gridCursor(pane); + pane.body.cur_row = pane.surfRow(@as(i32, cur.y) + terminal.gridOffset(pane)); pane.body.cur_col = @intCast(cur.x); } pane.body.cur_pinned = true; @@ -642,7 +642,7 @@ pub const Image = @import("image.zig"); pub const Pdf = @import("pdf_view.zig"); -pub const Terminal = @import("Terminal.zig"); +pub const terminal = @import("terminal.zig"); test { _ = Pane; @@ -652,5 +652,5 @@ test { _ = mini; _ = Image; _ = Pdf; - _ = Terminal; + _ = terminal; } diff --git a/src/pardes.zig b/src/pardes.zig index 141b214e..623af3c9 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -1195,9 +1195,9 @@ test "Tty9p marks only the new Linux terminal for a mounted shell" { try std.testing.expect(pane.v9fs_on_spawn); try std.testing.expect(!pane.greet); try std.testing.expect(!exec.takesCommandLine(p, p.active)); - panes.Terminal.feedOutput(p, pane, "\x1b]133;A\x07$ \x1b]133;B\x07"); + panes.terminal.feedOutput(p, pane, "\x1b]133;A\x07$ \x1b]133;B\x07"); try std.testing.expect(exec.takesCommandLine(p, p.active)); - panes.Terminal.feedOutput(p, pane, "\x1b]133;C\x07"); + panes.terminal.feedOutput(p, pane, "\x1b]133;C\x07"); try std.testing.expect(!exec.takesCommandLine(p, p.active)); } @@ -1558,7 +1558,7 @@ test "a command of several lines keeps its tag one row, and its echo does not st _ = exec.execute(p, 1, two); while (p.nextEffect()) |_| {} _ = try p.render(frame.allocator()); - const body = try panes.Terminal.screenTextAlloc(pane, gpa); + const body = try panes.terminal.screenTextAlloc(pane, gpa); defer gpa.free(body); try std.testing.expect(std.mem.indexOf(u8, body, "% def greet(name):\n return name") != null); } @@ -1878,7 +1878,7 @@ test "a command pane shows how its command ended, and the next command there run const pane = p.panes[dst].?; // Its first command is echoed as every later one is. { - const first = try panes.Terminal.screenTextAlloc(pane, gpa); + const first = try panes.terminal.screenTextAlloc(pane, gpa); defer gpa.free(first); try std.testing.expect(std.mem.startsWith(u8, first, "% make -j8")); } @@ -1909,16 +1909,16 @@ test "a command pane shows how its command ended, and the next command there run // A program that crashed out of its full screen leaves the emulator // in its modes; the next command starts clear of them. p.update(.{ .output = .{ .pane = @intCast(dst), .bytes = "\x1b[?1049h\x1b[?2004h\x1b[?1000h" } }); - try std.testing.expect(panes.Terminal.bracketedPaste(pane)); + try std.testing.expect(panes.terminal.bracketedPaste(pane)); // Done, the directory's next command runs there, below what it showed. try std.testing.expectEqual(@as(?usize, dst), exec.execute(p, 1, "make test")); - try std.testing.expect(!panes.Terminal.bracketedPaste(pane)); + try std.testing.expect(!panes.terminal.bracketedPaste(pane)); var respawned = false; while (p.nextEffect()) |effect| if (effect == .spawn and effect.spawn.pane == dst) { respawned = true; }; try std.testing.expect(respawned); - const body = try panes.Terminal.screenTextAlloc(pane, gpa); + const body = try panes.terminal.screenTextAlloc(pane, gpa); defer gpa.free(body); const said = std.mem.indexOf(u8, body, "compiled") orelse return error.OutputLost; const ended = std.mem.indexOf(u8, body, "exit 2") orelse return error.ExitLost; @@ -4394,7 +4394,7 @@ pub const Pardes = struct { settings: config.Runtime = .{ .font = .{ .tagline_percent = config.gui_tagline_font_percent } }, locations_config: locations.Config = .{}, locations_cache: locations.Cache = .{}, - tty_filter_palette: panes.Terminal.FilterPalette = .{}, + tty_filter_palette: panes.terminal.FilterPalette = .{}, font_request_taken: bool = false, custom_theme: ?Theme = null, /// bodyChrome's: the theme it was worked out for, and it. @@ -4580,8 +4580,8 @@ pub const Pardes = struct { /// that still own their loop pass their own arena to `render` instead. frame_arena: std.heap.ArenaAllocator, /// the terminal motion surface, memoized against the pane it was built - /// for — see panes.Terminal.RowsCache for the lifetime rule - shell_rows: panes.Terminal.RowsCache = .{}, + /// for — see panes.terminal.RowsCache for the lifetime rule + shell_rows: panes.terminal.RowsCache = .{}, /// The message row's entry points, called from everywhere as /// `p.setMessage(...)`; they live with the rest of it in Messages.zig. @@ -4944,7 +4944,7 @@ pub const Pardes = struct { } p.shell_rows.dropPane(pane); for (0..pane.pointer_selections.len) |slot| pane.clearPointerSelection(slot); - panes.Terminal.deinitPendingCommand(pane); + panes.terminal.deinitPendingCommand(pane); if (pane.command) |line| p.gpa.free(line); if (pane.shell) |bin| p.gpa.free(bin); if (pane.image) |*iv| { @@ -4957,7 +4957,7 @@ pub const Pardes = struct { pane.tag.deinit(p.gpa); pane.input.deinit(p.gpa); pane.jump.deinit(p.gpa); - panes.Terminal.deinitEmulator(pane, p.gpa); + panes.terminal.deinitEmulator(pane, p.gpa); pane.clearCwd(); pane.fs.deinit(p.gpa); p.gpa.destroy(pane); @@ -4986,12 +4986,12 @@ pub const Pardes = struct { pub fn newShell(p: *Pardes, id: usize, cwd: []const u8) !*Pane { std.debug.assert(p.panes[id] == null); if (cwd.len > limits.host_path_cap) return error.PathTooLong; - const pane = try panes.Terminal.create(p.gpa, p.screen_w, p.screen_h); + const pane = try panes.terminal.create(p.gpa, p.screen_w, p.screen_h); // Named by the directory it is started in from the first, so the // shell saying it is there is no rename (`new N /` then `rename`); // out of memory for it, the shell names it when it says. if (cwd.len > 0) pane.setOwnedCwd(cwd) catch {}; - panes.Terminal.armShellSpawn(pane); + panes.terminal.armShellSpawn(pane); p.installPane(id, pane); p.emitSpawn(id, pane.serial, cwd); return pane; @@ -5017,7 +5017,7 @@ pub const Pardes = struct { if (cwd.len > limits.host_path_cap) return error.PathTooLong; const owned = try p.gpa.dupe(u8, line); errdefer p.gpa.free(owned); - const pane = try panes.Terminal.create(p.gpa, p.screen_w, p.screen_h); + const pane = try panes.terminal.create(p.gpa, p.screen_w, p.screen_h); pane.command = owned; pane.command_pty = true; pane.body.mode = .tty; @@ -5031,7 +5031,7 @@ pub const Pardes = struct { pub fn newDocPane(p: *Pardes, id: usize) !*Pane { std.debug.assert(p.panes[id] == null); - const pane = try panes.Terminal.createDoc(p.gpa, p.screen_w, p.screen_h); + const pane = try panes.terminal.createDoc(p.gpa, p.screen_w, p.screen_h); p.installPane(id, pane); return pane; } @@ -5223,7 +5223,7 @@ pub const Pardes = struct { } else |_| {} } }; - if (id < MAX_PANES) panes.Terminal.shellSpawned(p, id, prompt_marks); + if (id < MAX_PANES) panes.terminal.shellSpawned(p, id, prompt_marks); if (p.settings.shell.effective.set(executable)) p.settings.shell.pending = false; } @@ -5358,7 +5358,7 @@ pub const Pardes = struct { /// The pane a session word runs with in an empty window. pub fn standIn(p: *Pardes) ?*Pane { - if (p.stand_in == null) p.stand_in = panes.Terminal.createDoc(p.gpa, p.screen_w, p.screen_h) catch null; + if (p.stand_in == null) p.stand_in = panes.terminal.createDoc(p.gpa, p.screen_w, p.screen_h) catch null; return p.stand_in; } @@ -5627,7 +5627,7 @@ pub const Pardes = struct { return; } if (!pane.isTerminal()) return; - const text = panes.Terminal.screenTextAlloc(pane, p.gpa) catch return; + const text = panes.terminal.screenTextAlloc(pane, p.gpa) catch return; defer p.gpa.free(text); p.hostWriteFile(st.pane, st.path.slice(), text); }, @@ -5839,7 +5839,7 @@ pub const Pardes = struct { const pane = p.panes[o.pane] orelse return; pane.shell_spoke = true; ctlfs.events.notePtyOutput(p, o.pane, o.bytes); - panes.Terminal.feedOutput(p, pane, o.bytes); + panes.terminal.feedOutput(p, pane, o.bytes); ctlfs.pty.noteMarks(p, o.pane, pane); exec.flushReplEnter(p, o.pane, true); }, @@ -5944,7 +5944,7 @@ pub const Pardes = struct { .normal => edit.enterInsert(p, &pane.body, .at, 1), .insert => { edit.exitInsert(p, &pane.body); - if (pane.isTerminal()) panes.Terminal.enterTty(p, id); + if (pane.isTerminal()) panes.terminal.enterTty(p, id); }, } } @@ -5960,7 +5960,7 @@ pub const Pardes = struct { if (pane.body.mode == .tty) { pane.body.mode = .normal; pane.body.normal.clear(); - } else panes.Terminal.enterTty(p, id); + } else panes.terminal.enterTty(p, id); } /// Type `keys` again (a macro, `.`): through the modal handling as @@ -6079,7 +6079,7 @@ pub const Pardes = struct { return exec.runBuiltin(p, .Last, p.active, "", null); if (key.cp == Key.escape and !key.ctrl and !key.alt and !key.shift and exec.takesCommandLine(p, p.active)) return exec.runBuiltin(p, .Last, p.active, "", null); - panes.Terminal.followOutput(pane); // typing snaps back to live output + panes.terminal.followOutput(pane); // typing snaps back to live output // Paste stays the window's, the one exception to forwarding a raw // tty's keys: Ctrl-V types the register at the program and // Ctrl-Shift-V asks the desktop for its clipboard first. Forwarded, @@ -6094,7 +6094,7 @@ pub const Pardes = struct { // Ctrl-V a black hole in every full-screen application. if (p.registers.text(p.gpa, Registers.default)) |text| return edit.typeToTty(p, p.active, pane, text); } - return panes.Terminal.forwardKey(p, p.active, key); + return panes.terminal.forwardKey(p, p.active, key); } if (p.leader_on) return p.leaderKey(key); if (p.ctrl_w_pending) { @@ -6247,7 +6247,7 @@ pub const Pardes = struct { edit.exitInsert(p, t) else edit.handleInsert(p, t, key), - .tty => panes.Terminal.forwardKey(p, p.active, key), + .tty => panes.terminal.forwardKey(p, p.active, key), } // `.`'s log: the keys of the normal command that entered insert // mode, then everything typed until it is left @@ -6738,7 +6738,7 @@ pub const Pardes = struct { errdefer p.gpa.free(path); const history = try panes.File.History.create(p.gpa); errdefer p.gpa.destroy(history); - const pane = try panes.Terminal.createDoc(p.gpa, p.screen_w, p.screen_h); + const pane = try panes.terminal.createDoc(p.gpa, p.screen_w, p.screen_h); errdefer p.gpa.destroy(pane); try p.deinitPane(p.panes[0].?); p.panes[0] = null; @@ -7086,7 +7086,7 @@ pub const Pardes = struct { // doc panes have no pty/emulator grid to reflow; just record // the size so bodyText renders the right number of rows if (pane.isTerminal()) { - panes.Terminal.resizeGrid(pane, p.gpa, cols, rows); + panes.terminal.resizeGrid(pane, p.gpa, cols, rows); p.shell_rows.markStale(pane); // reflow moved every row p.emit(.{ .resize_pty = .{ .pane = @intCast(id), .cols = cols, .rows = rows } }); pane.fs.winsize = null; @@ -7095,12 +7095,12 @@ pub const Pardes = struct { pane.rows = rows; if (pane.file) |*file| file.syntax_dirty = true; } - panes.Terminal.releasePendingCommandIfReady(p, id, pane); + panes.terminal.releasePendingCommandIfReady(p, id, pane); // The greeting `ls` of a new terminal in a directory is typed at // its shell's FIRST prompt, or never: typed later it would land // in whatever runs then, or in the middle of a line someone - // typed (panes.Terminal.greeting). - if (pane.greet and pane.isTerminal() and p.resize_count > 0) switch (panes.Terminal.greeting(pane)) { + // typed (panes.terminal.greeting). + if (pane.greet and pane.isTerminal() and p.resize_count > 0) switch (panes.terminal.greeting(pane)) { .wait => {}, .never => pane.greet = false, .now => { @@ -7381,7 +7381,7 @@ test "argv naming nothing boots an errors pane rather than failing the launch" { } test "raw terminal cursor obeys visibility without hiding modal and tag cursors" { - if (comptime !panes.Terminal.enabled) return error.SkipZigTest; + if (comptime !panes.terminal.enabled) return error.SkipZigTest; const gpa = std.testing.allocator; const p = try Pardes.init(gpa, .{ .tty_only = true, .cols = 40, .rows = 12 }); defer p.deinit(); @@ -7426,7 +7426,7 @@ test "raw terminal cursor obeys visibility without hiding modal and tag cursors" } test "raw terminal cursor follows its active row only while that row is visible" { - if (comptime !panes.Terminal.enabled) return error.SkipZigTest; + if (comptime !panes.terminal.enabled) return error.SkipZigTest; const gpa = std.testing.allocator; for ([_]bool{ false, true }) |tag_bottom| { const p = try Pardes.init(gpa, .{ .tty_only = true, .cols = 40, .rows = 12 }); @@ -7445,13 +7445,13 @@ test "raw terminal cursor follows its active row only while that row is visible" try std.testing.expectEqual(rect.x + config.GUTTER + 3, live.x); try std.testing.expectEqual(body_y + 2, live.y); - panes.Terminal.scrollGrid(pane, -1); + panes.terminal.scrollGrid(pane, -1); const scrolled = (try p.render(frame.allocator())).cursor orelse return error.MissingCursor; try std.testing.expectEqual(live.x, scrolled.x); try std.testing.expectEqual(live.y + 1, scrolled.y); - panes.Terminal.scrollGrid(pane, -@as(i32, pane.rows)); + panes.terminal.scrollGrid(pane, -@as(i32, pane.rows)); try std.testing.expect((try p.render(frame.allocator())).cursor == null); - panes.Terminal.followOutput(pane); + panes.terminal.followOutput(pane); const returned = (try p.render(frame.allocator())).cursor orelse return error.MissingCursor; try std.testing.expectEqual(live.x, returned.x); try std.testing.expectEqual(live.y, returned.y); @@ -7895,31 +7895,31 @@ test "tty output follows only from the bottom" { p.update(.{ .output = .{ .pane = @intCast(shell), .bytes = std.fmt.bufPrint(&buf, "line {d}\r\n", .{i}) catch unreachable } }); } p.sync(); - const bottom = panes.Terminal.scrollbar(sp).offset; + const bottom = panes.terminal.scrollbar(sp).offset; try std.testing.expect(bottom > 0); // Scrolled back, the reader stays put however much the shell prints. sp.scrollBy(-10); p.sync(); - const parked = panes.Terminal.scrollbar(sp).offset; + const parked = panes.terminal.scrollbar(sp).offset; try std.testing.expect(parked < bottom); for (60..70) |i| { var buf: [32]u8 = undefined; p.update(.{ .output = .{ .pane = @intCast(shell), .bytes = std.fmt.bufPrint(&buf, "line {d}\r\n", .{i}) catch unreachable } }); } p.sync(); - try std.testing.expectEqual(parked, panes.Terminal.scrollbar(sp).offset); + try std.testing.expectEqual(parked, panes.terminal.scrollbar(sp).offset); // Typing snaps back to live output, so nobody types blind. p.update(.{ .key = .{ .cp = 'x', .text = "x" } }); p.sync(); - try std.testing.expect(panes.Terminal.scrollbar(sp).offset > parked); + try std.testing.expect(panes.terminal.scrollbar(sp).offset > parked); // ...and from there output follows again. - const live = panes.Terminal.scrollbar(sp).offset; + const live = panes.terminal.scrollbar(sp).offset; p.update(.{ .output = .{ .pane = @intCast(shell), .bytes = "tail\r\n" } }); p.sync(); - try std.testing.expect(panes.Terminal.scrollbar(sp).offset > live); + try std.testing.expect(panes.terminal.scrollbar(sp).offset > live); } test "Esc back into a file leaves its view where it was" { @@ -8135,7 +8135,7 @@ test "an unasked desktop paste reaches a tty pane's program, not its buffer" { _ = drainWrites(p, &buf); const pane = p.panes[0].?; - panes.Terminal.enterTty(p, 0); + panes.terminal.enterTty(p, 0); // No request behind it: the window manager's own paste, or SDL answering a // Ctrl-Shift-V the desktop handled. It still has to reach the shell. p.update(.{ .paste = "ls -la" }); @@ -8153,7 +8153,7 @@ test "a paste larger than the effect ring reaches the program whole and in order const buf = try gpa.alloc(u8, 1 << 20); defer gpa.free(buf); _ = drainWrites(p, buf); - panes.Terminal.enterTty(p, 0); + panes.terminal.enterTty(p, 0); const text = try gpa.alloc(u8, 300 * 1024); defer gpa.free(text); @@ -8166,7 +8166,7 @@ test "a paste larger than the effect ring reaches the program whole and in order test "a bracketed paste larger than the ring still closes its bracket" { if (platform == .web) return; - if (comptime !panes.Terminal.enabled) return; + if (comptime !panes.terminal.enabled) return; const gpa = std.testing.allocator; const p = try Pardes.init(gpa, .{ .tty_only = true, .cols = 80, .rows = 24 }); defer p.deinit(); @@ -8174,10 +8174,10 @@ test "a bracketed paste larger than the ring still closes its bracket" { const buf = try gpa.alloc(u8, 1 << 20); defer gpa.free(buf); _ = drainWrites(p, buf); - panes.Terminal.enterTty(p, 0); + panes.terminal.enterTty(p, 0); p.update(.{ .output = .{ .pane = 0, .bytes = "\x1b[?2004h" } }); - try std.testing.expect(panes.Terminal.bracketedPaste(p.panes[0].?)); + try std.testing.expect(panes.terminal.bracketedPaste(p.panes[0].?)); const text = try gpa.alloc(u8, 300 * 1024); defer gpa.free(text); @memset(text, 'z'); @@ -8197,7 +8197,7 @@ test "pasted bytes still queued at shutdown are freed, not leaked" { const buf = try gpa.alloc(u8, 1 << 20); defer gpa.free(buf); _ = drainWrites(p, buf); - panes.Terminal.enterTty(p, 0); + panes.terminal.enterTty(p, 0); const text = try gpa.alloc(u8, 300 * 1024); defer gpa.free(text); @@ -8227,14 +8227,14 @@ test "leaving tty hides the prompt and keeps the command typed at it" { const rowOf = struct { fn at(pp: *Pardes, pane: *Pane, needle: []const u8) ?[]const u8 { - const rows = panes.Terminal.shellRows(pp, pane) catch return null; + const rows = panes.terminal.shellRows(pp, pane) catch return null; for (rows) |r| if (std.mem.indexOf(u8, r, needle) != null) return r; return null; } }.at; const bodyRowOf = struct { fn at(pp: *Pardes, pane: *Pane, needle: []const u8) ?[]const u8 { - const body = panes.Terminal.bodyText(pp.scratch.allocator(), pane) catch return null; + const body = panes.terminal.bodyText(pp.scratch.allocator(), pane) catch return null; var it = std.mem.splitScalar(u8, body, '\n'); while (it.next()) |r| if (std.mem.indexOf(u8, r, needle) != null) return r; return null; @@ -8290,7 +8290,7 @@ test "entering tty walks the shell cursor to the column clicked past the prompt" p.shell_rows.stale = true; // The command shows LEFT-HUGGED, so its column 3 is the '3'... - const rows = try panes.Terminal.shellRows(p, sp); + const rows = try panes.terminal.shellRows(p, sp); var row: i32 = 0; const at = for (rows, 0..) |r, i| { if (std.mem.indexOf(u8, r, "0123456789") != null) break i; @@ -8304,7 +8304,7 @@ test "entering tty walks the shell cursor to the column clicked past the prompt" sp.body.cur_col = 3; sp.body.cur_pinned = true; while (p.nextEffect()) |_| {} - panes.Terminal.enterTty(p, shell); + panes.terminal.enterTty(p, shell); // The walk is arrow keys the shell understands. Seven lefts: readline's // cursor sits past the '9' and the click was on the '3'. @@ -8347,7 +8347,7 @@ test "board heap: every inline ring the board pays for still fits its budget" { const effect_ring = 128 * @sizeOf(Effect); // ...and the undo history, the largest in `Pane` once the terminal is out: // 16 snapshots on the board against 256 on a desktop. - const undo_history = 2 * 16 * @sizeOf(panes.Terminal.Snapshot); + const undo_history = 2 * 16 * @sizeOf(panes.terminal.Snapshot); const grid = @as(usize, board_cols) * board_rows * (2 * @sizeOf(Cell) + @sizeOf(PanelCellDiff)); // Each of the three separately, so a failure names the one that grew @@ -8475,7 +8475,7 @@ test "jump history terminal rectangle follows output and clears on reflow" { try std.testing.expectEqualStrings("alpha", try edit.selectionText(p, pane, pane.sel[0])); for (0..40) |_| p.update(.{ .output = .{ .pane = 0, .bytes = "more output\r\n" } }); try std.testing.expectEqualStrings("alpha", try edit.selectionText(p, pane, pane.sel[0])); - panes.Terminal.resizeGrid(pane, p.gpa, pane.cols - 1, pane.rows); + panes.terminal.resizeGrid(pane, p.gpa, pane.cols - 1, pane.rows); try std.testing.expect(pane.pointerSelection(pane.sel[0]) == null); try std.testing.expectEqual(.none, pane.sel[0].state); } diff --git a/src/Terminal.zig b/src/terminal.zig index c99a92a6..c99a92a6 100644 --- a/src/Terminal.zig +++ b/src/terminal.zig diff --git a/src/tty/tty.zig b/src/tty/tty.zig index f5ae078a..3902271c 100644 --- a/src/tty/tty.zig +++ b/src/tty/tty.zig @@ -1915,7 +1915,7 @@ test "TTY queued results reject old PTY generations and LSP request IDs" { shell.gens[0] = 2; _ = shell.apply(.{ .pty_read = .{ .id = 0, .gen = 1, .bytes = try gpa.dupe(u8, "old session\n") } }); _ = shell.apply(.{ .pty_read = .{ .id = 0, .gen = 2, .bytes = try gpa.dupe(u8, "current session\n") } }); - const text = try pardes.panes.Terminal.screenTextAlloc(core.panes[0].?, gpa); + const text = try pardes.panes.terminal.screenTextAlloc(core.panes[0].?, gpa); defer gpa.free(text); try std.testing.expect(std.mem.indexOf(u8, text, "old session") == null); try std.testing.expect(std.mem.indexOf(u8, text, "current session") != null); |
