summaryrefslogtreecommitdiff
path: root/src/main.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-08-09 02:34:49 -0300
committerGabriel Schneider <[email protected]>2026-08-10 09:17:07 -0300
commit0a15af8d98771180e32402e68ea844f9f377cefb (patch)
tree8f919ba30dd304c4654e4abf127c238f05458000 /src/main.zig
parent09c35b22793153d201fa202b52671a6442e5e2e1 (diff)
downloadpardes-0a15af8d98771180e32402e68ea844f9f377cefb.tar.gz
pardes-0a15af8d98771180e32402e68ea844f9f377cefb.zip
a pardes launched inside pardes hands its file to the outer one
Diffstat (limited to 'src/main.zig')
-rw-r--r--src/main.zig85
1 files changed, 66 insertions, 19 deletions
diff --git a/src/main.zig b/src/main.zig
index 343742e3..472dda6e 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -1,6 +1,7 @@
const std = @import("std");
const builtin = @import("builtin");
const pardes = @import("pardes.zig");
+const nested = @import("nested.zig");
const is_emscripten = builtin.os.tag == .emscripten;
@@ -75,10 +76,21 @@ const help_text =
\\ A FILE argument boots just that file instead.
\\ --tty-toggle <key> use Ctrl-<key> to enter/leave tty mode
\\ -l <dump.zon> load a dump of another instance (see Dump)
+ \\ --nested run a full session even inside another pardes.
+ \\ Without it, a pardes started inside a pardes
+ \\ hands its FILE argument to the outer one. This
+ \\ session will not serve its own children either.
\\ -h, --help show this help and exit
\\
;
+const nested_text =
+ \\pardes: this shell is already inside pardes, and a pardes inside a pardes
+ \\is spicy. Name a file or a directory and the outer session opens it, or
+ \\pass --nested if you really want a second one in here.
+ \\
+;
+
// The browser runtime calls a C main (exported below); everything else keeps
// the std.process.Init entry.
pub const main = if (is_emscripten) webMain else nativeMain;
@@ -108,8 +120,15 @@ fn nativeMain(init: std.process.Init) !void {
var opts: pardes.Options = .{};
const arena = init.arena.allocator();
const args = try init.minimal.args.toSlice(arena);
- if (args.len == 1) opts.tty_only = true; // bare `pardes` boots straight into tty mode
- var positional = false;
+ // bare `pardes` boots straight into tty mode — and `pardes --nested` still
+ // counts as bare, because --nested says something about the session's
+ // relationship to its parent and nothing about its layout
+ if (args.len == 1 or (args.len == 2 and std.mem.eql(u8, args[1], "--nested")))
+ opts.tty_only = true;
+ // Kept RAW until every flag is parsed: classifying it means chdir'ing into
+ // a directory and recording nothing, and the nested client below still
+ // needs the word itself to resolve.
+ var positional: ?[:0]const u8 = null;
var i: usize = 1;
while (i < args.len) : (i += 1) {
const a = args[i];
@@ -129,30 +148,58 @@ fn nativeMain(init: std.process.Init) !void {
i += 1;
if (i >= args.len) return error.BadArgs;
opts.load_path = args[i];
+ } else if (std.mem.eql(u8, a, "--nested")) {
+ opts.nested = true;
} else if (std.mem.eql(u8, a, "-h") or std.mem.eql(u8, a, "--help")) {
try std.Io.File.stdout().writeStreamingAll(init.io, help_text);
return;
- } else if (a.len > 0 and a[0] != '-' and !positional) {
- // one positional: a directory becomes the cwd shells spawn in
- // (chdir succeeds only on dirs); anything else resolves as a file
- positional = true;
- if (std.c.chdir(a.ptr) != 0) {
- var cwdbuf: [4096]u8 = undefined;
- const cwd = std.c.getcwd(&cwdbuf, cwdbuf.len) orelse return error.BadArgs;
- var realbuf: [4096]u8 = undefined;
- switch (@import("look.zig").resolve(a, std.mem.span(@as([*:0]u8, @ptrCast(cwd))), &realbuf)) {
- .file => |t| {
- opts.file = try arena.dupe(u8, t.path);
- opts.file_line = t.at.line;
- },
- .image => |t| opts.file = try arena.dupe(u8, t.path),
- else => return error.BadArgs,
- }
- }
+ } else if (a.len > 0 and a[0] != '-' and positional == null) {
+ positional = a;
} else {
return error.BadArgs;
}
}
+ // Started INSIDE another pardes: hand it the file and get out of the way
+ // rather than stacking a second full-screen UI inside one of its panes.
+ // The word is resolved here rather than sent raw because the outer
+ // instance resolves against ITS panes' directories, which are not ours.
+ // A word naming nothing on disk sends nothing and falls through to the
+ // classification below, which already refuses it — no second UI either way.
+ if (!opts.nested) if (nested.outer()) |outer_pid| {
+ const word = positional orelse {
+ try std.Io.File.stderr().writeStreamingAll(init.io, nested_text);
+ std.process.exit(1);
+ };
+ var cwdbuf: [4096]u8 = undefined;
+ const cwd = std.c.getcwd(&cwdbuf, cwdbuf.len) orelse return error.BadArgs;
+ var realbuf: [4096]u8 = undefined;
+ const sent = switch (@import("look.zig").resolve(word, std.mem.span(@as([*:0]u8, @ptrCast(cwd))), &realbuf)) {
+ .dir => |d| nested.sendLook(outer_pid, d, 0),
+ .file => |t| nested.sendLook(outer_pid, t.path, t.at.line),
+ .image => |t| nested.sendLook(outer_pid, t.path, 0),
+ // an unreachable outer instance (an older build, a stale socket
+ // path) is not worth failing a launch over: run normally instead
+ else => false,
+ };
+ if (sent) return;
+ };
+ if (positional) |a| {
+ // a directory becomes the cwd shells spawn in (chdir succeeds only on
+ // dirs); anything else resolves as a file
+ if (std.c.chdir(a.ptr) != 0) {
+ var cwdbuf: [4096]u8 = undefined;
+ const cwd = std.c.getcwd(&cwdbuf, cwdbuf.len) orelse return error.BadArgs;
+ var realbuf: [4096]u8 = undefined;
+ switch (@import("look.zig").resolve(a, std.mem.span(@as([*:0]u8, @ptrCast(cwd))), &realbuf)) {
+ .file => |t| {
+ opts.file = try arena.dupe(u8, t.path);
+ opts.file_line = t.at.line;
+ },
+ .image => |t| opts.file = try arena.dupe(u8, t.path),
+ else => return error.BadArgs,
+ }
+ }
+ }
// Native shells opt into the user config; the sans-IO core and web keep
// Options' null default. Read it before entering either frontend so every
// builtin has run before that frontend can render its first frame.