diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-29 16:57:32 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:17 -0300 |
| commit | 9e78acbfeee714ae5a4ce152c2be8c6a7abccf3e (patch) | |
| tree | effc19711bb5a419318633dc50d663c6fa89f336 /src/ninep/ctl.zig | |
| parent | 916615648fbef6998244f23a2b1045de7edb3a75 (diff) | |
| download | pardes-9e78acbfeee714ae5a4ce152c2be8c6a7abccf3e.tar.gz pardes-9e78acbfeee714ae5a4ce152c2be8c6a7abccf3e.zip | |
A pane's ctl reads acme's fields in acme's order, the undo and redo flags included, then pardes's own
Where plan9port's line has whether Undo and Redo have a step, pardes's had current, so a client reading acme's fields by position read a word for a flag. The two flags now follow the tab width, as in plan9port's winctlprint, and current|notcurrent and a REPL's id come after them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/ninep/ctl.zig')
| -rw-r--r-- | src/ninep/ctl.zig | 49 |
1 files changed, 39 insertions, 10 deletions
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 403100f2..e7274c5e 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -331,13 +331,13 @@ fn writeQuoted(w: *std.Io.Writer, s: []const u8) !void { try w.writeByte('\''); } -pub const pane_text_cap = 7 * 12 + 2 * 256 + 4 + "notcurrent ".len + 48; +pub const pane_text_cap = 9 * 12 + 2 * 256 + 4 + "notcurrent ".len + 48; -/// acme's winctlprint: serial, tag length, body length, a reserved zero, the -/// dirty flag, then the width in cells, the font and the tab width. Then -/// rio's word for whether the window has the keyboard, `current` or -/// `notcurrent` (rio(4), wctl), which acme's line has no room for because -/// acme has one window for input at a time only by the mouse. +/// acme's winctlprint (plan9port wind.c), field for field: serial, tag +/// length, body length, isdir (0), the dirty flag, the width in cells, the +/// font, the tab width, whether Undo has a step, whether Redo has one. Then +/// pardes's own: rio's word for whether the window has the keyboard, +/// `current` or `notcurrent` (rio(4), wctl), and a REPL's id. pub fn paneText(p: *Pardes, pane: *Pane, buf: []u8) []const u8 { var w = std.Io.Writer.fixed(buf); w.print("{d:>11} {d:>11} {d:>11} {d:>11} {d:>11} {d:>11} ", .{ @@ -353,7 +353,10 @@ pub fn paneText(p: *Pardes, pane: *Pane, buf: []u8) []const u8 { writeQuoted(&w, if (font.len == 0) "default" else font) catch return w.buffered(); // While a column's or the workspace's tag has the keyboard, no pane does. const current = p.panes[p.active] == pane and !p.header_focus; - w.print(" {d:>11} {s} ", .{ config.tab_width, if (current) "current" else "notcurrent" }) catch {}; + const f = pane_files.fileOf(pane); + const undo = if (f) |file| file.history.undo_len > 0 else false; + const redo = if (f) |file| file.history.redo_len > 0 else false; + w.print(" {d:>11} {d:>11} {d:>11} {s} ", .{ config.tab_width, @intFromBool(undo), @intFromBool(redo), if (current) "current" else "notcurrent" }) catch {}; // A terminal bound as a REPL ends with its id, `python-a`. var idbuf: [48]u8 = undefined; const id = exec_line.replId(&idbuf, pane); @@ -1004,7 +1007,7 @@ const root_status = @intFromEnum(tree.TopFile.status); const root_look = @intFromEnum(tree.TopFile.look); const root_exec = @intFromEnum(tree.TopFile.exec); -test "pane ctl read is index's five fields plus width in cells, font, tab width and whether it is current" { +test "pane ctl read is acme's fields -- index's five, width in cells, font, tab width, undo, redo -- then whether it is current" { const gpa = testing.allocator; const p = try withFile(gpa, "x\n"); defer p.deinit(); @@ -1014,8 +1017,8 @@ test "pane ctl read is index's five fields plus width in cells, font, tab width try testing.expectEqual(Status.ok, a.reply.status); var want: std.ArrayList(u8) = .empty; defer want.deinit(gpa); - try want.print(gpa, "{d:>11} {d:>11} {d:>11} {d:>11} {d:>11} {d:>11} {s} {d:>11} current ", .{ - pane.serial, pane_files.tagOf(p, pane).len, @as(usize, 2), 0, 0, pane.cols, "default", config.tab_width, + try want.print(gpa, "{d:>11} {d:>11} {d:>11} {d:>11} {d:>11} {d:>11} {s} {d:>11} {d:>11} {d:>11} current ", .{ + pane.serial, pane_files.tagOf(p, pane).len, @as(usize, 2), 0, 0, pane.cols, "default", config.tab_width, 0, 0, }); try testing.expectEqualStrings(want.items, a.bytes); try testing.expectEqual(@as(u64, a.bytes.len), call(p, .{ .tag = 1, .op = .getattr, .node = Node.of(pane.serial, .ctl) }).reply.attr.size); @@ -2399,6 +2402,32 @@ test "a pane's ctl takes acme's words: done by the files that replace them, or r try testing.expectEqualStrings("0", std.mem.trimEnd(u8, rd(p, Node.of(serial, .dirty), 0, 8).bytes, "\n")); } +test "ctl reads acme's fields in acme's order, undo and redo flags included, then pardes's own" { + const p = try withFile(testing.allocator, "one\n"); + defer p.deinit(); + const serial = serialOf(p); + const ctl_node = Node.of(serial, .ctl); + const fields = struct { + fn of(text: []const u8, buf: *[16][]const u8) [][]const u8 { + var it = std.mem.tokenizeScalar(u8, text, ' '); + var n: usize = 0; + while (it.next()) |t| : (n += 1) buf[n] = t; + return buf[0..n]; + } + }; + var buf: [16][]const u8 = undefined; + var f = fields.of(rd(p, ctl_node, 0, 4096).bytes, &buf); + try testing.expectEqualStrings("0", f[8]); // nothing to undo + try testing.expectEqualStrings("0", f[9]); // nothing to redo + try testing.expect(std.mem.eql(u8, f[10], "current") or std.mem.eql(u8, f[10], "notcurrent")); + _ = wr(p, Node.of(serial, .body), "two\n"); + f = fields.of(rd(p, ctl_node, 0, 4096).bytes, &buf); + try testing.expectEqualStrings("1", f[8]); + _ = wr(p, ctl_node, "Undo\n"); + f = fields.of(rd(p, ctl_node, 0, 4096).bytes, &buf); + try testing.expectEqualStrings("1", f[9]); +} + test "every EINVAL a write gets says why, in its err record too; DEL is a control character in a line" { const p = try withFile(testing.allocator, "x\n"); defer p.deinit(); |
