diff options
Diffstat (limited to 'src/extension_loader.zig')
| -rw-r--r-- | src/extension_loader.zig | 74 |
1 files changed, 38 insertions, 36 deletions
diff --git a/src/extension_loader.zig b/src/extension_loader.zig index 0ab8091..87eb609 100644 --- a/src/extension_loader.zig +++ b/src/extension_loader.zig @@ -6,13 +6,13 @@ //! //! Extensions (full-featured; call `panto.ext.register_tool` from a script //! that may register many tools): -//! 1. `$PANTO_HOME/agent/extensions/` ("base") +//! 1. `<data home>/agent/extensions/` ("base") //! 2. `${XDG_CONFIG_HOME:-$HOME/.config}/panto/extensions/` ("user") //! 3. `./.panto/extensions/` ("project") //! //! Tools (ergonomic single-tool form; the script returns one table //! shaped like the argument to `panto.ext.register_tool`): -//! 1. `$PANTO_HOME/agent/tools/` ("base") +//! 1. `<data home>/agent/tools/` ("base") //! 2. `${XDG_CONFIG_HOME:-$HOME/.config}/panto/tools/` ("user") //! 3. `./.panto/tools/` ("project") //! @@ -124,7 +124,7 @@ pub const Kind = enum { /// /// `base_agent_dir`, when non-null, is the path under which embedded /// base tools/extensions have been staged — typically -/// `$PANTO_HOME/agent/`. Pass `null` to skip the base layer entirely +/// `<data home>/agent/`. Pass `null` to skip the base layer entirely /// (mostly useful for tests). /// /// `environ_map` is consulted for `HOME` and `XDG_CONFIG_HOME`. The @@ -372,16 +372,19 @@ fn classifyFile( if (!std.mem.endsWith(u8, entry_name, ".lua")) return null; const base = entry_name[0 .. entry_name.len - ".lua".len]; if (base.len == 0) return null; + if (base[0] == '_') return null; const script_path = try std.fs.path.join(allocator, &.{ dir_path, entry_name }); errdefer allocator.free(script_path); const name = try allocator.dupe(u8, base); errdefer allocator.free(name); + const package_root = try allocator.dupe(u8, dir_path); + errdefer allocator.free(package_root); return Found{ .name = name, .script_path = script_path, - .package_root = null, + .package_root = package_root, .source = source, .kind = kind, }; @@ -445,44 +448,42 @@ const ShadowKeyCtx = struct { }; fn applyShadowing(allocator: Allocator, list: *std.array_list.Managed(Found)) !void { - var latest: std.HashMap(ShadowKey, usize, ShadowKeyCtx, std.hash_map.default_max_load_percentage) = .init(allocator); + const keep = try allocator.alloc(bool, list.items.len); + defer allocator.free(keep); + @memset(keep, false); - for (list.items, 0..) |f, i| { - try latest.put(.{ .kind = f.kind, .name = f.name }, i); - } + { + var latest: std.HashMap(ShadowKey, usize, ShadowKeyCtx, std.hash_map.default_max_load_percentage) = .init(allocator); + defer latest.deinit(); - var keep: std.array_list.Managed(Found) = .init(allocator); - var drop: std.array_list.Managed(Found) = .init(allocator); - errdefer { - latest.deinit(); - for (keep.items) |*f| f.deinit(allocator); - keep.deinit(); - for (drop.items) |*f| f.deinit(allocator); - drop.deinit(); + for (list.items, 0..) |f, i| { + try latest.put(.{ .kind = f.kind, .name = f.name }, i); + } + + for (list.items, 0..) |f, i| { + const winner = latest.get(.{ .kind = f.kind, .name = f.name }).?; + if (winner == i) { + keep[i] = true; + } else { + std.log.debug( + "{s}: '{s}' from {s} shadowed by {s}", + .{ f.kind.label(), f.name, f.source.label(), list.items[winner].source.label() }, + ); + } + } } - try keep.ensureTotalCapacity(list.items.len); - try drop.ensureTotalCapacity(list.items.len); - for (list.items, 0..) |f, i| { - const winner = latest.get(.{ .kind = f.kind, .name = f.name }).?; - if (winner == i) { - keep.appendAssumeCapacity(f); + var write: usize = 0; + for (list.items, keep) |f, k| { + if (k) { + list.items[write] = f; + write += 1; } else { - std.log.debug( - "{s}: '{s}' from {s} shadowed by {s}", - .{ f.kind.label(), f.name, f.source.label(), list.items[winner].source.label() }, - ); - drop.appendAssumeCapacity(f); + var dropped = f; + dropped.deinit(allocator); } } - - latest.deinit(); - for (drop.items) |*f| f.deinit(allocator); - drop.deinit(); - - list.clearRetainingCapacity(); - list.appendSlice(keep.items) catch unreachable; - keep.deinit(); + list.shrinkRetainingCapacity(write); } // --------------------------------------------------------------------------- @@ -527,6 +528,7 @@ test "scanDir picks up single-file and directory-style extensions" { try makeDir(tmp.dir, "ext_root"); try makeDir(tmp.dir, "ext_root/beta"); try writeFile(tmp.dir, "ext_root/alpha.lua", "-- alpha\n"); + try writeFile(tmp.dir, "ext_root/_helper.lua", "-- helper module\n"); try writeFile(tmp.dir, "ext_root/beta/init.lua", "-- beta init\n"); try writeFile(tmp.dir, "ext_root/beta/helper.lua", "-- helper\n"); try writeFile(tmp.dir, "ext_root/.ignored.lua", "-- hidden\n"); @@ -552,7 +554,7 @@ test "scanDir picks up single-file and directory-style extensions" { }.lt); try testing.expectEqualStrings("alpha", list.items[0].name); - try testing.expect(list.items[0].package_root == null); + try testing.expect(list.items[0].package_root != null); try testing.expect(std.mem.endsWith(u8, list.items[0].script_path, "alpha.lua")); try testing.expectEqualStrings("beta", list.items[1].name); |
