From f8c6d45755acb5abf9ac68931268b7945da0fae0 Mon Sep 17 00:00:00 2001 From: t Date: Mon, 15 Jun 2026 22:11:53 -0600 Subject: display fixes: markdown rendering, tool-specific components --- src/lua_event_bridge.zig | 82 ++++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 41 deletions(-) (limited to 'src/lua_event_bridge.zig') diff --git a/src/lua_event_bridge.zig b/src/lua_event_bridge.zig index c4acb1d..24bc6fc 100644 --- a/src/lua_event_bridge.zig +++ b/src/lua_event_bridge.zig @@ -3,7 +3,7 @@ //! This module bridges the native event machinery in `tui_event.zig` to //! Lua: it lets a Lua `panto.ext.on(name, handler)` callback participate in //! the SAME `EventBus` the native side fires, receive a bridged `event` -//! object (`getComponent`/`setComponent` + payload fields), and either +//! object (`get_component`/`set_component` + payload fields), and either //! pass through a native default component, wrap it, or install a //! brand-new component DEFINED IN LUA. //! @@ -401,7 +401,7 @@ pub const EventBridge = struct { /// The native `Handler.callback` for a bridged Lua handler. Builds the /// `event` userdata, calls the Lua function with it (synchronously, under /// a traceback errfunc), and lets the Lua side read/replace the component -/// via `event:getComponent()` / `event:setComponent()`. Errors in the Lua +/// via `event:get_component()` / `event:set_component()`. Errors in the Lua /// handler are logged and swallowed — a broken handler must not abort the /// event dispatch or the render loop. fn nativeHandlerCallback(ctx: *anyopaque, event: *Event) void { @@ -444,7 +444,7 @@ fn nativeHandlerCallback(ctx: *anyopaque, event: *Event) void { /// Push an `event` userdata onto the stack, carrying a pointer to the /// bridge (which reaches the active `*Event`). Its metatable exposes -/// `getComponent`, `setComponent`, and read-only payload fields via +/// `get_component`, `set_component`, and read-only payload fields via /// `__index`. fn pushEventObject(bridge: *EventBridge) !void { const L = bridge.L; @@ -456,7 +456,7 @@ fn pushEventObject(bridge: *EventBridge) !void { /// Lazily create the `event` userdata metatable (by name, so /// `luaL_checkudata` recognizes it) and leave it on the stack. Its -/// `__index` is a function resolving `getComponent`/`setComponent` and +/// `__index` is a function resolving `get_component`/`set_component` and /// payload fields. fn ensureEventMetatable(L: *c.lua_State) void { // luaL_newmetatable pushes the metatable; returns 1 if freshly created. @@ -476,11 +476,11 @@ fn eventIndexThunk(L_opt: ?*c.lua_State) callconv(.c) c_int { if (kptr == null) return 0; const key = kptr[0..klen]; - if (std.mem.eql(u8, key, "getComponent")) { + if (std.mem.eql(u8, key, "get_component")) { c.lua_pushcclosure(L, eventGetComponentThunk, 0); return 1; } - if (std.mem.eql(u8, key, "setComponent")) { + if (std.mem.eql(u8, key, "set_component")) { c.lua_pushcclosure(L, eventSetComponentThunk, 0); return 1; } @@ -579,13 +579,13 @@ fn pushPayloadField(L: *c.lua_State, ev: *Event, key: []const u8) void { c.lua_pushnil(L); } -/// `event:getComponent()` -> the current component as a native-passthrough +/// `event:get_component()` -> the current component as a native-passthrough /// userdata (or nil). The userdata wraps the `Component` (vtable+ptr) so -/// Lua can pass it straight back to `setComponent` unchanged (§7.5 wrap +/// Lua can pass it straight back to `set_component` unchanged (§7.5 wrap /// pattern: the inner is opaque to Lua but re-settable / wrappable). fn eventGetComponentThunk(L_opt: ?*c.lua_State) callconv(.c) c_int { const L = L_opt.?; - // arg 1 is the event userdata (method call `event:getComponent()`). + // arg 1 is the event userdata (method call `event:get_component()`). const ud: **EventBridge = @ptrCast(@alignCast(c.luaL_checkudata(L, 1, eventMtName) orelse return 0)); const bridge = ud.*; const ev = bridge.active_event orelse { @@ -600,8 +600,8 @@ fn eventGetComponentThunk(L_opt: ?*c.lua_State) callconv(.c) c_int { return 1; } -/// `event:setComponent(c)` where `c` is EITHER a native-passthrough -/// userdata (from `getComponent`) OR a Lua component table. A table is +/// `event:set_component(c)` where `c` is EITHER a native-passthrough +/// userdata (from `get_component`) OR a Lua component table. A table is /// bridged into a native `Component` (§7.6); a userdata passes the native /// component straight through. fn eventSetComponentThunk(L_opt: ?*c.lua_State) callconv(.c) c_int { @@ -618,20 +618,20 @@ fn eventSetComponentThunk(L_opt: ?*c.lua_State) callconv(.c) c_int { ev.setComponent(cud.*); return 0; } - return c.luaL_error(L, "setComponent: unknown userdata (expected a component)"); + return c.luaL_error(L, "set_component: unknown userdata (expected a component)"); } if (ty == lua_bridge.T_TABLE) { const comp = bridge.makeBridgedComponent(2) catch { - return c.luaL_error(L, "setComponent: failed to bridge Lua component"); + return c.luaL_error(L, "set_component: failed to bridge Lua component"); }; ev.setComponent(comp); return 0; } - return c.luaL_error(L, "setComponent: argument must be a component (table or native handle)"); + return c.luaL_error(L, "set_component: argument must be a component (table or native handle)"); } /// Push a native `Component` as a passthrough userdata with the -/// native-component metatable (so `setComponent` can recover it). +/// native-component metatable (so `set_component` can recover it). fn pushNativeComponent(L: *c.lua_State, comp: Component) void { const ud: *Component = @ptrCast(@alignCast(c.lua_newuserdatauv(L, @sizeOf(Component), 0).?)); ud.* = comp; @@ -781,7 +781,7 @@ test "Lua-defined component renders lines through the bridged vtable" { // A handler that sets a Lua component whose render returns two lines. try runScript(L, \\panto.ext.on("tool", function(e) - \\ e:setComponent({ + \\ e:set_component({ \\ render = function(self, width) return { "hello", "world" } end, \\ }) \\end) @@ -820,7 +820,7 @@ test "bridged component render truncates to the width contract" { try runScript(L, \\panto.ext.on("tool", function(e) - \\ e:setComponent({ + \\ e:set_component({ \\ render = function(self, width) return { "abcdefghij" } end, \\ }) \\end) @@ -849,7 +849,7 @@ test "bridged component render error yields a safe fallback line, not a crash" { try runScript(L, \\panto.ext.on("tool", function(e) - \\ e:setComponent({ + \\ e:set_component({ \\ render = function(self, width) error("boom") end, \\ }) \\end) @@ -903,12 +903,12 @@ test "wrap pattern: Lua reads the native default, wraps it, and renders through var nd = NativeDefault{ .cache = RenderCache.init(testing.allocator) }; defer nd.cache.deinit(); - // Handler: read the native default via getComponent, store it, set a Lua + // Handler: read the native default via get_component, store it, set a Lua // component that renders "[" .. inner_first_line .. "]". try runScript(L, \\panto.ext.on("tool", function(e) - \\ local inner = e:getComponent() -- native passthrough handle - \\ e:setComponent({ + \\ local inner = e:get_component() -- native passthrough handle + \\ e:set_component({ \\ inner = inner, \\ render = function(self, width) \\ -- We cannot call the native inner's render from Lua (it has no @@ -933,7 +933,7 @@ test "wrap pattern: Lua reads the native default, wraps it, and renders through test "skills-style claim-by-name (§7.5) works end-to-end through the Lua bridge" { // The canonical extension pattern: a Lua handler subscribes to `tool`, // returns early UNLESS event.tool_name matches its tool, and otherwise - // setComponent's a Lua-defined component. We fire two tool events through + // set_component's a Lua-defined component. We fire two tool events through // the NATIVE bus and assert only the matching name is claimed (its Lua // component renders), while a non-matching name keeps the native default. const L = c.luaL_newstate() orelse return error.LuaInitFailed; @@ -955,7 +955,7 @@ test "skills-style claim-by-name (§7.5) works end-to-end through the Lua bridge \\panto.ext.on("tool", function(e) \\ if e.tool_name ~= "skill" then return end -- claim-by-name \\ local name = e.tool_name -- snapshot at handler time - \\ e:setComponent({ + \\ e:set_component({ \\ render = function(self, width) return { "SKILL:" .. name } end, \\ }) \\end) @@ -1011,7 +1011,7 @@ test "skills-style claim-by-name (§7.5) works end-to-end through the Lua bridge } } -test "native-passthrough get/set round-trip: setComponent(getComponent()) keeps the native default" { +test "native-passthrough get/set round-trip: set_component(get_component()) keeps the native default" { const L = c.luaL_newstate() orelse return error.LuaInitFailed; defer c.lua_close(L); c.luaL_openlibs(L); @@ -1049,7 +1049,7 @@ test "native-passthrough get/set round-trip: setComponent(getComponent()) keeps // Handler passes the native default straight back through. try runScript(L, \\panto.ext.on("tool", function(e) - \\ e:setComponent(e:getComponent()) + \\ e:set_component(e:get_component()) \\end) ); try harvestOnInto(&bridge); @@ -1112,7 +1112,7 @@ test "bridged render: a long error on a NARROW width still satisfies the width c try runScript(L, \\panto.ext.on("tool", function(e) - \\ e:setComponent({ + \\ e:set_component({ \\ render = function(self, width) error("a very long error message that definitely exceeds a narrow terminal width") end, \\ }) \\end) @@ -1153,7 +1153,7 @@ test "bridged render: non-array / nil / non-string returns each yield a safe fal \\idx = 0 \\panto.ext.on("tool", function(e) \\ idx = idx + 1 - \\ e:setComponent({ render = components[idx] }) + \\ e:set_component({ render = components[idx] }) \\end) ); try harvestOnInto(&bridge); @@ -1183,7 +1183,7 @@ test "bridged render: empty array renders zero lines (no fallback)" { try runScript(L, \\panto.ext.on("tool", function(e) - \\ e:setComponent({ render = function(self, width) return {} end }) + \\ e:set_component({ render = function(self, width) return {} end }) \\end) ); try harvestOnInto(&bridge); @@ -1213,7 +1213,7 @@ test "bridged render: UTF-8 line truncates on codepoint boundaries to the width" // exactly 3 codepoints (6 bytes), not split a 2-byte sequence. try runScript(L, \\panto.ext.on("tool", function(e) - \\ e:setComponent({ render = function(self, width) return { "\195\169\195\169\195\169\195\169\195\169" } end }) + \\ e:set_component({ render = function(self, width) return { "\195\169\195\169\195\169\195\169\195\169" } end }) \\end) ); try harvestOnInto(&bridge); @@ -1247,7 +1247,7 @@ test "bridged firstLineChanged is cache-derived: append stays near the tail" { try runScript(L, \\n = 2 \\panto.ext.on("tool", function(e) - \\ e:setComponent({ + \\ e:set_component({ \\ render = function(self, width) \\ local t = {} \\ for i = 1, n do t[i] = "line" .. i end @@ -1296,7 +1296,7 @@ test "bridged firstLineChanged: a mid-line replace reports the changed line, shr try runScript(L, \\lines = { "a", "b", "c" } \\panto.ext.on("tool", function(e) - \\ e:setComponent({ render = function(self, width) return lines end }) + \\ e:set_component({ render = function(self, width) return lines end }) \\end) ); try harvestOnInto(&bridge); @@ -1343,7 +1343,7 @@ test "bridged handleInput round-trips: a Lua method mutates state the next rende try runScript(L, \\panto.ext.on("tool", function(e) - \\ e:setComponent({ + \\ e:set_component({ \\ buf = "", \\ handleInput = function(self, data) self.buf = self.buf .. data end, \\ render = function(self, width) return { self.buf } end, @@ -1390,7 +1390,7 @@ test "bridged component: invalidate frees the ref+cache eagerly; teardown is lea try runScript(L, \\panto.ext.on("tool", function(e) - \\ e:setComponent({ render = function(self, width) return { "x", "y" } end }) + \\ e:set_component({ render = function(self, width) return { "x", "y" } end }) \\end) ); try harvestOnInto(&bridge); @@ -1537,12 +1537,12 @@ test "claim-by-name at tool_details swaps over the start default; releasing the try runScript(L, \\-- At block_start the name is unknown; set a placeholder Lua component. \\panto.ext.on("tool", function(e) - \\ e:setComponent({ render = function(self, w) return { "tool (?)" } end }) + \\ e:set_component({ render = function(self, w) return { "tool (?)" } end }) \\end) \\-- At tool_details, claim by name and swap in the real component. \\panto.ext.on("tool_details", function(e) \\ if e.tool_name ~= "skill" then return end - \\ e:setComponent({ render = function(self, w) return { "SKILL" } end }) + \\ e:set_component({ render = function(self, w) return { "SKILL" } end }) \\end) ); try harvestOnInto(&bridge); @@ -1560,7 +1560,7 @@ test "claim-by-name at tool_details swaps over the start default; releasing the try testing.expectEqual(@as(usize, 1), bridge.components.items.len); // tool_details: seed the event with the CURRENT override (the placeholder), - // mirroring how the App seeds getComponent with the current component. The + // mirroring how the App seeds get_component with the current component. The // handler claims "skill" and swaps in "SKILL". const details = bus.fire("tool_details", start, .{ .tool = .{ .index = 0, .tool_name = "skill", .id = "c0" } }).?; try testing.expect(details.ptr != start.ptr); // a NEW component @@ -1614,7 +1614,7 @@ test "claim-by-name at tool_details swaps over the start default; releasing the test "intra-emit clobber: two handlers each mint a Lua component in ONE emit; both are tracked and freed at teardown (no true leak)" { // §7.3 "last-wins-blind": when two handlers for the SAME event each call - // setComponent within a single emit, the bus keeps only the last as + // set_component within a single emit, the bus keeps only the last as // `current`. The App records only that last one as the entry's override, // so the FIRST handler's freshly-minted Lua component is never handed to // `releaseOverride` (the App never sees it). It is therefore NOT released @@ -1626,8 +1626,8 @@ test "intra-emit clobber: two handlers each mint a Lua component in ONE emit; bo // - but it IS per-emit accumulation: a clobbering handler chain grows // `bridge.components` by one orphan per clobber for the runtime's life. // - // The documented mitigation is the §7.3 wrap pattern (getComponent -> - // wrap -> setComponent), under which no component is orphaned because each + // The documented mitigation is the §7.3 wrap pattern (get_component -> + // wrap -> set_component), under which no component is orphaned because each // handler decorates the current one instead of minting a rival. A handler // that clobbers blind is at fault per the plan; the resource is still // reclaimed at teardown, so correctness (no UAF / no true leak) holds. @@ -1646,10 +1646,10 @@ test "intra-emit clobber: two handlers each mint a Lua component in ONE emit; bo // component, ignoring the current one. try runScript(L, \\panto.ext.on("tool", function(e) - \\ e:setComponent({ render = function(self, w) return { "FIRST" } end }) + \\ e:set_component({ render = function(self, w) return { "FIRST" } end }) \\end) \\panto.ext.on("tool", function(e) - \\ e:setComponent({ render = function(self, w) return { "SECOND" } end }) + \\ e:set_component({ render = function(self, w) return { "SECOND" } end }) \\end) ); try harvestOnInto(&bridge); -- cgit v1.3