From dcb222060e5fc5099e02182d12ec26c83d512559 Mon Sep 17 00:00:00 2001 From: BrookJeynes Date: Tue, 27 May 2025 20:19:26 +1000 Subject: [PATCH] feat: add `--choose-dir` arg This arg treats jido like a directory chooser writing the last visited directory to STDOUT on exit. --- src/main.zig | 123 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 86 insertions(+), 37 deletions(-) diff --git a/src/main.zig b/src/main.zig index fdcf62c..457637e 100644 --- a/src/main.zig +++ b/src/main.zig @@ -8,6 +8,20 @@ const config = &@import("./config.zig").config; const resolvePath = @import("./commands.zig").resolvePath; pub const panic = vaxis.panic_handler; +const help_menu = + \\Usage: jido + \\ + \\a lightweight Unix TUI file explorer + \\ + \\Flags: + \\ -h, --help Show help information and exit. + \\ -v, --version Print version information and exit. + \\ --entry-dir=PATH Open jido at chosen dir. + \\ --choose-dir Makes jido act like a directory chooser. When jido + \\ quits, it will write the name of the last visited + \\ directory to STDOUT. + \\ +; pub const std_options: std.Options = .{ .log_scope_levels = &.{ @@ -16,6 +30,19 @@ pub const std_options: std.Options = .{ }, }; +const Options = struct { + help: bool = false, + version: bool = false, + @"choose-dir": bool = false, + @"entry-path": []const u8 = ".", + + fn optKind(a: []const u8) enum { short, long, positional } { + if (std.mem.startsWith(u8, a, "--")) return .long; + if (std.mem.startsWith(u8, a, "-")) return .short; + return .positional; + } +}; + pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer { @@ -26,54 +53,65 @@ pub fn main() !void { } const alloc = gpa.allocator(); - var entry_buf: [std.fs.max_path_bytes]u8 = undefined; - var entry_dir_path: ?[]const u8 = null; + var last_dir: ?[]const u8 = null; + var entry_path_buf: [std.fs.max_path_bytes]u8 = undefined; + var opts = Options{}; var args = std.process.args(); _ = args.skip(); while (args.next()) |arg| { - if (std.mem.eql(u8, arg, "--version") or std.mem.eql(u8, arg, "-v")) { - std.debug.print("jido v{}\n", .{options.version}); - return; + switch (Options.optKind(arg)) { + .short => { + const str = arg[1..]; + for (str) |b| { + switch (b) { + 'v' => opts.version = true, + 'h' => opts.help = true, + else => { + std.log.err("Invalid opt: '{c}'", .{b}); + std.process.exit(1); + }, + } + } + }, + .long => { + var split = std.mem.splitScalar(u8, arg[2..], '='); + const opt = split.first(); + const val = split.rest(); + if (std.mem.eql(u8, opt, "version")) { + opts.version = true; + } else if (std.mem.eql(u8, opt, "help")) { + opts.help = true; + } else if (std.mem.eql(u8, opt, "choose-dir")) { + opts.@"choose-dir" = true; + } else if (std.mem.eql(u8, opt, "entry-dir")) { + const path = if (std.mem.eql(u8, val, "")) "." else val; + var dir = try std.fs.cwd().openDir(".", .{ .iterate = true }); + defer dir.close(); + opts.@"entry-path" = resolvePath(&entry_path_buf, path, dir); + } + }, + .positional => { + std.log.err("Invalid opt: '{s}'. Jido does not take positional arguments.", .{arg}); + std.process.exit(1); + }, } + } - if (std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h")) { - std.debug.print( - \\Usage: jido - \\ - \\a lightweight Unix TUI file explorer - \\ - \\Flags: - \\ -h, --help Show help information and exit. - \\ -v, --version Print version information and exit. - \\ --entry-dir=PATH Open jido at chosen dir. - \\ - , .{}); - return; - } + if (opts.help) { + std.debug.print(help_menu, .{}); + return; + } - if (std.mem.startsWith(u8, arg, "--entry-dir")) { - var path: ?[]const u8 = null; - if (arg.len > 11 and arg[11] == '=') { - var iter = std.mem.tokenizeAny(u8, arg, "="); - _ = iter.next(); - if (iter.next()) |p| path = p; - } else { - if (args.next()) |p| path = p; - } - - if (path) |p| { - var dir = try std.fs.cwd().openDir(".", .{ .iterate = true }); - defer dir.close(); - entry_dir_path = resolvePath(&entry_buf, p, dir); - } - } + if (opts.version) { + std.debug.print("jido v{}\n", .{options.version}); + return; } { - var app = App.init(alloc, entry_dir_path) catch { + var app = App.init(alloc, opts.@"entry-path") catch { vaxis.recover(); - std.posix.exit(1); + std.process.exit(1); }; defer app.deinit(); @@ -104,6 +142,17 @@ pub fn main() !void { app.file_logger = if (config.config_dir) |dir| FileLogger.init(dir) else null; try app.run(); + + if (opts.@"choose-dir") { + last_dir = alloc.dupe(u8, try app.directories.fullPath(".")) catch null; + } } + // Must be printed after app has deinit as part of that process clears + // the screen. + if (last_dir) |path| { + const stdout = std.io.getStdOut().writer(); + stdout.print("{s}\n", .{path}) catch {}; + alloc.free(path); + } } -- 2.51.2