summaryrefslogtreecommitdiff
path: root/src/extension_loader.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/extension_loader.zig')
-rw-r--r--src/extension_loader.zig74
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);