Skip to content

Commit 54f7e1f

Browse files
authored
Merge pull request #3 from midasdf/cursor/fix-cli-s-session-name-f5d6
fix(cli): -s セッション名を attach / kill-session で解釈する
2 parents 939fa7e + d9a30a0 commit 54f7e1f

1 file changed

Lines changed: 91 additions & 22 deletions

File tree

src/main.zig

Lines changed: 91 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,42 @@ pub const client = @import("client.zig");
1919
pub const server = @import("server.zig");
2020
pub const session = @import("session.zig");
2121

22+
// ─── CLI parsing ──────────────────────────────────────────────────────────────
23+
24+
const ParsedCli = struct {
25+
/// From `-s <name>` (last wins if repeated). Not appended to `filtered`.
26+
session_name: ?[]const u8,
27+
/// argv with program path stripped, `-s` and its value removed.
28+
filtered: []const []const u8,
29+
};
30+
31+
/// Strip the executable path, remove `-s <name>` pairs, and record the session name.
32+
/// Caller must `allocator.free(parsed.filtered)` when done.
33+
fn parseUserArgs(allocator: std.mem.Allocator, args: []const []const u8) error{ OutOfMemory, MissingSessionNameAfterS }!ParsedCli {
34+
if (args.len == 0) return .{ .session_name = null, .filtered = try allocator.alloc([]const u8, 0) };
35+
36+
var session_name: ?[]const u8 = null;
37+
var list: std.ArrayListUnmanaged([]const u8) = .{};
38+
errdefer list.deinit(allocator);
39+
40+
var i: usize = 1;
41+
while (i < args.len) : (i += 1) {
42+
const arg = args[i];
43+
if (std.mem.eql(u8, arg, "-s")) {
44+
if (i + 1 >= args.len) return error.MissingSessionNameAfterS;
45+
session_name = args[i + 1];
46+
i += 1;
47+
continue;
48+
}
49+
try list.append(allocator, arg);
50+
}
51+
52+
return .{
53+
.session_name = session_name,
54+
.filtered = try list.toOwnedSlice(allocator),
55+
};
56+
}
57+
2258
// ─── Socket path helpers ──────────────────────────────────────────────────────
2359

2460
/// Resolve `socket_dir` from config, replacing `{uid}` with the real UID.
@@ -403,27 +439,23 @@ pub fn main() !void {
403439
var cfg = try config.loadFromFile(allocator);
404440
defer cfg.deinit();
405441

406-
// Parse args.
442+
// Parse args (handles `-s <name>` anywhere; value is not treated as a subcommand arg).
407443
const args = try std.process.argsAlloc(allocator);
408444
defer std.process.argsFree(allocator, args);
409445

410-
// Scan for -s <name> flag anywhere in args.
411-
var session_name: ?[]const u8 = null;
412-
var filtered_args: std.ArrayListUnmanaged([]const u8) = .{};
413-
defer filtered_args.deinit(allocator);
414-
415-
for (args, 0..) |arg, i| {
416-
if (std.mem.eql(u8, arg, "-s") and i + 1 < args.len) {
417-
session_name = args[i + 1];
418-
} else if (i > 0 and i < args.len) {
419-
// Check if this arg is the value for -s (skip it).
420-
if (i >= 2 and std.mem.eql(u8, args[i - 1], "-s")) continue;
421-
try filtered_args.append(allocator, arg);
422-
}
423-
}
446+
const parsed = parseUserArgs(allocator, args) catch |err| switch (err) {
447+
error.MissingSessionNameAfterS => {
448+
std.debug.print("error: -s requires a session name\n", .{});
449+
return;
450+
},
451+
else => |e| return e,
452+
};
453+
defer allocator.free(parsed.filtered);
454+
const session_name = parsed.session_name;
455+
const filtered_args = parsed.filtered;
424456

425-
const has_subcmd = filtered_args.items.len > 0;
426-
const subcmd = if (has_subcmd) filtered_args.items[0] else "";
457+
const has_subcmd = filtered_args.len > 0;
458+
const subcmd = if (has_subcmd) filtered_args[0] else "";
427459

428460
// No subcommand: create a new session.
429461
if (!has_subcmd) {
@@ -437,14 +469,18 @@ pub fn main() !void {
437469

438470
// ── Internal ──
439471
if (std.mem.eql(u8, subcmd, "--server")) {
440-
const name = if (filtered_args.items.len > 1) filtered_args.items[1] else "0";
472+
const name = if (filtered_args.len > 1) filtered_args[1] else "0";
441473
return runServer(allocator, &cfg, name);
442474
}
443475

444476
// ── attach / a ──
445477
if (std.mem.eql(u8, subcmd, "attach") or std.mem.eql(u8, subcmd, "a")) {
446-
if (filtered_args.items.len > 1) {
447-
return attachSession(allocator, &cfg, filtered_args.items[1]);
478+
const name_arg: ?[]const u8 = if (filtered_args.len > 1)
479+
filtered_args[1]
480+
else
481+
session_name;
482+
if (name_arg) |n| {
483+
return attachSession(allocator, &cfg, n);
448484
}
449485
return attachAuto(allocator, &cfg);
450486
}
@@ -459,8 +495,12 @@ pub fn main() !void {
459495

460496
// ── kill-session / k ──
461497
if (std.mem.eql(u8, subcmd, "kill-session") or std.mem.eql(u8, subcmd, "k")) {
462-
if (filtered_args.items.len > 1) {
463-
return killSession(allocator, &cfg, filtered_args.items[1]);
498+
const name_arg: ?[]const u8 = if (filtered_args.len > 1)
499+
filtered_args[1]
500+
else
501+
session_name;
502+
if (name_arg) |n| {
503+
return killSession(allocator, &cfg, n);
464504
}
465505
std.debug.print("Usage: zplit kill-session <name>\n", .{});
466506
return;
@@ -553,3 +593,32 @@ test "findNextSessionName returns 0 when no sockets exist" {
553593

554594
try std.testing.expectEqualStrings("0", name);
555595
}
596+
597+
test "parseUserArgs strips -s and keeps subcommand args" {
598+
const allocator = std.testing.allocator;
599+
const argv = [_][]const u8{ "/bin/zplit", "attach", "-s", "foo", "extra" };
600+
const p = try parseUserArgs(allocator, &argv);
601+
defer allocator.free(p.filtered);
602+
603+
try std.testing.expectEqualStrings("foo", p.session_name.?);
604+
try std.testing.expectEqual(@as(usize, 2), p.filtered.len);
605+
try std.testing.expectEqualStrings("attach", p.filtered[0]);
606+
try std.testing.expectEqualStrings("extra", p.filtered[1]);
607+
}
608+
609+
test "parseUserArgs attach -s name has positional name for attach" {
610+
const allocator = std.testing.allocator;
611+
const argv = [_][]const u8{ "zplit", "attach", "-s", "mysession" };
612+
const p = try parseUserArgs(allocator, &argv);
613+
defer allocator.free(p.filtered);
614+
615+
try std.testing.expectEqualStrings("mysession", p.session_name.?);
616+
try std.testing.expectEqual(@as(usize, 1), p.filtered.len);
617+
try std.testing.expectEqualStrings("attach", p.filtered[0]);
618+
}
619+
620+
test "parseUserArgs trailing -s without value errors" {
621+
const allocator = std.testing.allocator;
622+
const argv = [_][]const u8{ "zplit", "list", "-s" };
623+
try std.testing.expectError(error.MissingSessionNameAfterS, parseUserArgs(allocator, &argv));
624+
}

0 commit comments

Comments
 (0)