From c1990b3e6e196ad41379aa432bf3ccca8a65d9f0 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Sun, 20 Sep 2026 00:36:50 -0300 Subject: Flatten the 9P control tree and move it out of fs.zig The served tree loses the self/ level: /index /ctl /new /log /screen /listeners /pane//... /os, with /src only in -Dembed-sources=true builds (default off, on for esp32p4). ctl speaks the editor's own language with two lowercase verbs, look TEXT and exec TEXT, plus acme's addr verbs; the new/ factory directory becomes one clone file; cons is gone (exec Msg); name and sel are files; stats report real lengths, modes and mtimes; /log streams pane new/del/rename/save events. The tree code lives in src/ninep/ (tree, pane, ctl, addr, pty, events, screen, sources); fs.zig keeps host access, mounts, resolution and find/grep. Same engine and transports. README (fs-help.txt) and docs rewritten; tests updated and extended. Co-Authored-By: Claude Fable 5.1 --- src/pardes.zig | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) (limited to 'src/pardes.zig') diff --git a/src/pardes.zig b/src/pardes.zig index 5c0ac263..88442542 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -6,6 +6,7 @@ const mvzr = @import("mvzr"); pub const modal = @import("modal.zig"); pub const look = @import("look.zig"); pub const filesystem = @import("fs.zig"); +pub const ctlfs = @import("ninep/tree.zig"); pub const syntax = @import("syntax.zig"); pub const locations_config = @import("locations_config.zig"); const tracy = @import("tracy.zig"); @@ -4711,7 +4712,7 @@ pub const Event = union(enum) { /// this must not clamp onto and preview the final grid cell. pointer_leave, tick, - fs_req: filesystem.Req, + fs_req: ctlfs.Req, }; pub const attach_name_max = 256; @@ -4749,7 +4750,7 @@ pub const Effect = union(enum) { /// Write the build-time theme ring below the per-user config directory. /// The pane receives the completion/error message from the native host. dump_themes: struct { pane: u8 }, - fs_reply: filesystem.Reply, + fs_reply: ctlfs.Reply, attach: struct { pane: u8, name: Buf(attach_name_max) }, detach: struct { pane: u8 }, quit, @@ -6109,6 +6110,7 @@ pub const Pardes = struct { .fallback = .{ .gpa = gpa }, }; errdefer p.deinit(); + p.fs.started = ctlfs.events.now(); for (opts.mounts) |mount| try p.fs.mount(gpa, mount.name, mount.dial); p.opts.mounts = &.{}; p.opts.ninep_identity = .{}; @@ -6335,6 +6337,7 @@ pub const Pardes = struct { const watched = (if (pane.file) |f| f.output == null else false) or pane.hasPdf(); for (p.panes, 0..) |slot, id| if (slot == pane) { if (watched) p.emit(.{ .watch = .{ .pane = @intCast(id), .on = false } }); + ctlfs.events.noteRetire(p, id, pane); p.fs.forget(p.gpa, id); // ...and so do bytes still queued for the pty it no longer has. p.dropPendingWrite(id); @@ -6380,6 +6383,7 @@ pub const Pardes = struct { pane.serial = p.next_serial; p.rects[id] = .{}; p.panes[id] = pane; + ctlfs.events.noteInstall(p, id); } pub fn newShell(p: *Pardes, id: usize, cwd: []const u8) !*Pane { @@ -6924,7 +6928,7 @@ pub const Pardes = struct { return pane.pdfPath(); } - pub fn fsPayload(p: *const Pardes, r: filesystem.Reply) []const u8 { + pub fn fsPayload(p: *const Pardes, r: ctlfs.Reply) []const u8 { return switch (r.payload) { .none => &.{}, .staged => |n| p.fs.out.items[0..@min(n, p.fs.out.items.len)], @@ -6961,6 +6965,7 @@ pub const Pardes = struct { p.hostWriteFile(sf.pane, f.path, f.content); const saved_pane = p.panes[sf.pane] orelse return; if (saved_pane.serial != serial) return; + ctlfs.events.noteLog(p, .save, saved_pane); const saved = if (saved_pane.file) |*file| file else return; if (saved.saved_revision != saved.revision or !saved.watch_after_save) return; saved.watch_after_save = false; @@ -7116,7 +7121,7 @@ pub const Pardes = struct { }, .output => |o| { const pane = p.panes[o.pane] orelse return; - filesystem.notePtyOutput(p, o.pane, o.bytes); + ctlfs.events.notePtyOutput(p, o.pane, o.bytes); panes.Terminal.feedOutput(p, pane, o.bytes); }, .eof => |e| p.removePane(e.pane) catch |err| { @@ -7146,7 +7151,7 @@ pub const Pardes = struct { }, .paste => |bytes| p.applyPaste(bytes), .command => |line| _ = p.executeBuiltinLine(p.active, line), - .fs_req => |r| p.emit(.{ .fs_reply = filesystem.handle(p, r) }), + .fs_req => |r| p.emit(.{ .fs_reply = ctlfs.handle(p, r) }), .pinch => |scale| p.ov_pinch_scale = scale, .touch_scroll => |delta| p.ov_touch_scroll_delta = delta, .pointer_leave => { @@ -7163,6 +7168,7 @@ pub const Pardes = struct { _ = p.scratch.reset(.retain_capacity); p.sync(); p.fsReport(); + ctlfs.events.announce(p); } fn fsReport(p: *Pardes) void { @@ -7174,7 +7180,7 @@ pub const Pardes = struct { const snap = &p.fs.panes[id].tag_snap; if (std.mem.eql(u8, snap.items, tag)) continue; if (snap.capacity != 0 or snap.items.len != 0) - filesystem.noteReplace(p, id, true, snap.items, tag); + ctlfs.events.noteReplace(p, id, true, snap.items, tag); snap.clearRetainingCapacity(); snap.appendSlice(p.gpa, tag) catch {}; } @@ -7451,7 +7457,7 @@ pub const Pardes = struct { p.reportError(id, "name", err); return false; }; - filesystem.nameBuffer(p, id, full, true) catch |err| { + ctlfs.pane.nameBuffer(p, id, full, true) catch |err| { p.reportError(id, "name", err); return false; }; @@ -11673,21 +11679,21 @@ pub const Pardes = struct { ) bool { if (!p.fs.scripted(id)) return false; const is_look = cmd == config.look_cmd; - const action: filesystem.Action = if (is_look) + const action: ctlfs.events.Action = if (is_look) (if (on_tag) .tag_look else .body_look) else (if (on_tag) .tag_exec else .body_exec); const named = std.meta.stringToEnum(Builtin, commandText(text)) != null; const range = p.gestureRange(id, text, on_tag, operand); - var flag: u32 = if (named) filesystem.flag_builtin else 0; - if (range.q0 == range.q1 and text.len > 0) flag |= filesystem.flag_expansion; + var flag: u32 = if (named) ctlfs.events.flag_builtin else 0; + if (range.q0 == range.q1 and text.len > 0) flag |= ctlfs.events.flag_expansion; if (is_look) { - if (!named and std.mem.indexOfAny(u8, text, "/.:") != null) flag |= filesystem.flag_filename; - } else if (chorded) flag |= filesystem.flag_chorded; - return filesystem.noteAction(p, id, action, range.q0, range.q1, flag, text); + if (!named and std.mem.indexOfAny(u8, text, "/.:") != null) flag |= ctlfs.events.flag_filename; + } else if (chorded) flag |= ctlfs.events.flag_chorded; + return ctlfs.events.noteAction(p, id, action, range.q0, range.q1, flag, text); } - fn gestureRange(p: *Pardes, id: usize, text: []const u8, on_tag: bool, operand: PointerOperand) filesystem.PaneState.Range { + fn gestureRange(p: *Pardes, id: usize, text: []const u8, on_tag: bool, operand: PointerOperand) ctlfs.pane.State.Range { const pane = p.panes[id] orelse return .{}; if (on_tag) { const tag = p.tagText(p.scratch.allocator(), pane) catch return .{}; -- cgit v1.3