From 23f430573efe4f6ef55b0180aa0d742ff4eca1d9 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Mon, 28 Sep 2026 00:01:57 -0300 Subject: Make a pane's tag a Text edited with the body's own keys The tag was a fixed buffer with its own one-line editor, a cursor measured across the path, and a hijack of the body's mode while it was typed into. It is now the pane's second Text: its own characters after the computed prefix (path, dirty marker, PDF page), which is never stored in it, so a rename or a dirty marker never moves its cursor. Normal and insert mode are the body's, undo included; `:` moves the keyboard between body and tag, the tag remembers its cursor and starts on Save, and executing a word from it hands the keyboard back. The mouse still sees the prefix: a sweep selects across it and a click on the path drafts a new name. Co-Authored-By: Claude Opus 5.5 --- src/Terminal.zig | 63 +++++++++----------------------------------------------- 1 file changed, 10 insertions(+), 53 deletions(-) (limited to 'src/Terminal.zig') diff --git a/src/Terminal.zig b/src/Terminal.zig index fbe7bfc0..7b5af696 100644 --- a/src/Terminal.zig +++ b/src/Terminal.zig @@ -686,8 +686,7 @@ pub fn feedOutput(p: *Pardes, pane: *Pane, bytes: []const u8) void { const at_bottom = atBottom(pane); if (has_positions) ingestWithPositions(pane, bytes) else ingest(pane, bytes); p.shell_rows.markStale(pane); - const body_mode = if (pane.tag_edit) pane.tag_mode else pane.body.mode; - if (body_mode == .tty and at_bottom) followOutput(pane); + if (pane.body.mode == .tty and at_bottom) followOutput(pane); } const RowPin = struct { pin: ?*ghostty_vt.Pin = null, offset: i32 = 0 }; @@ -2463,64 +2462,22 @@ pub fn padOutputBelowEdits(p: *Pardes, id: usize) void { while (i < pad) : (i += 1) p.emitWrite(id, "\r"); } -/// the snapshot takes ownership of a COPY of the edit buffer's text -pub fn snap(p: *Pardes, pane: *Pane) ?Snapshot { - var ovl: ?EditBuffer = null; - if (pane.ovl) |o| ovl = .{ .row = o.row, .rows = o.rows, .text = p.gpa.dupe(u8, o.text) catch return null }; - return .{ .ovl = ovl, .cur_row = pane.body.cur_row, .cur_col = pane.body.cur_col, .vsel = pane.body.vsel }; -} - -/// undo/redo restores the selection recorded with the snapshot (helix -/// keeps selections in its history transactions) -pub fn restoreSnap(p: *Pardes, pane: *Pane, s: Snapshot) void { - if (pane.ovl) |o| p.gpa.free(o.text); - pane.ovl = s.ovl; - pane.body.cur_row = s.cur_row; - pane.body.cur_col = s.cur_col; - pane.body.cur_pinned = true; - pane.body.vsel = s.vsel; - pane.body.msel.active = false; - pane.ensureCursorVisible(); -} - -fn pushHistory(gpa: std.mem.Allocator, slots: []Snapshot, len: *usize, value: Snapshot) void { - if (len.* == slots.len) { - if (slots[0].ovl) |overlay| gpa.free(overlay.text); - std.mem.copyForwards(Snapshot, slots[0 .. slots.len - 1], slots[1..]); - len.* -= 1; - } - slots[len.*] = value; - len.* += 1; -} - pub fn pushUndo(p: *Pardes, pane: *Pane) void { - const current = pane.ovl orelse EditBuffer{ .rows = 0 }; - if (pane.body.ed_undo_len > 0) { - const top = pane.body.ed_undo[pane.body.ed_undo_len - 1]; - const same = if (top.ovl) |overlay| pane.ovl != null and overlay.row == current.row and - overlay.rows == current.rows and std.mem.eql(u8, overlay.text, current.text) else pane.ovl == null; - if (same) return; - } - const value = snap(p, pane) orelse return; - pushHistory(p.gpa, &pane.body.ed_undo, &pane.body.ed_undo_len, value); - for (pane.body.ed_redo[0..pane.body.ed_redo_len]) |item| if (item.ovl) |overlay| p.gpa.free(overlay.text); - pane.body.ed_redo_len = 0; + pane.body.remember(p.gpa, pane.ovl); } pub fn undo(p: *Pardes, pane: *Pane) void { - if (pane.body.ed_undo_len == 0) return; - const current = snap(p, pane) orelse return; - pushHistory(p.gpa, &pane.body.ed_redo, &pane.body.ed_redo_len, current); - pane.body.ed_undo_len -= 1; - restoreSnap(p, pane, pane.body.ed_undo[pane.body.ed_undo_len]); + const back_to = pane.body.step(p.gpa, pane.ovl, true) orelse return; + if (pane.ovl) |o| p.gpa.free(o.text); + pane.ovl = back_to.ovl; + pane.ensureCursorVisible(); } pub fn redo(p: *Pardes, pane: *Pane) void { - if (pane.body.ed_redo_len == 0) return; - const current = snap(p, pane) orelse return; - pushHistory(p.gpa, &pane.body.ed_undo, &pane.body.ed_undo_len, current); - pane.body.ed_redo_len -= 1; - restoreSnap(p, pane, pane.body.ed_redo[pane.body.ed_redo_len]); + const forward_to = pane.body.step(p.gpa, pane.ovl, false) orelse return; + if (pane.ovl) |o| p.gpa.free(o.text); + pane.ovl = forward_to.ovl; + pane.ensureCursorVisible(); } pub fn ptyReport(handler: *ghostty_vt.TerminalStream.Handler, data: [:0]const u8) void { -- cgit v1.3