aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/cli/action.zig
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-04-04 19:22:04 +0300
committerCan Celik <ogulcancelik@gmail.com>2026-04-05 01:23:06 +0300
commit41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch)
tree3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/src/cli/action.zig
parent55c138263e190a1e79d4b997bfb9397306c0ec1f (diff)
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/src/cli/action.zig')
-rw-r--r--vendor/libghostty-vt/src/cli/action.zig277
1 files changed, 277 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/cli/action.zig b/vendor/libghostty-vt/src/cli/action.zig
new file mode 100644
index 00000000..41173a9f
--- /dev/null
+++ b/vendor/libghostty-vt/src/cli/action.zig
@@ -0,0 +1,277 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+
+pub const DetectError = error{
+ /// Multiple actions were detected. You can specify at most one
+ /// action on the CLI otherwise the behavior desired is ambiguous.
+ MultipleActions,
+
+ /// An unknown action was specified.
+ InvalidAction,
+};
+
+/// Detect the action from CLI args.
+pub fn detectArgs(comptime E: type, alloc: Allocator) !?E {
+ var iter = try std.process.argsWithAllocator(alloc);
+ defer iter.deinit();
+ return try detectIter(E, &iter);
+}
+
+/// Detect the action from any iterator. Each iterator value should yield
+/// a CLI argument such as "--foo".
+///
+/// The comptime type E must be an enum with the available actions.
+/// If the type E has a decl `detectSpecialCase`, then it will be called
+/// for each argument to allow handling of special cases. The function
+/// signature for `detectSpecialCase` should be:
+///
+/// fn detectSpecialCase(arg: []const u8) ?SpecialCase(E)
+///
+pub fn detectIter(
+ comptime E: type,
+ iter: anytype,
+) DetectError!?E {
+ var fallback: ?E = null;
+ var pending: ?E = null;
+ while (iter.next()) |arg| {
+ // Allow handling of special cases.
+ if (@hasDecl(E, "detectSpecialCase")) special: {
+ const special = E.detectSpecialCase(arg) orelse break :special;
+ switch (special) {
+ .action => |a| return a,
+ .fallback => |a| fallback = a,
+ .abort_if_no_action => if (pending == null) return null,
+ }
+ }
+
+ // Commands must start with "+"
+ if (arg.len == 0 or arg[0] != '+') continue;
+ if (pending != null) return DetectError.MultipleActions;
+ pending = std.meta.stringToEnum(E, arg[1..]) orelse
+ return DetectError.InvalidAction;
+ }
+
+ // If we have an action, we always return that action, even if we've
+ // seen "--help" or "-h" because the action may have its own help text.
+ if (pending != null) return pending;
+
+ // If we have no action but we have a fallback, then we return that.
+ if (fallback) |a| return a;
+
+ return null;
+}
+
+/// The action enum E can implement the decl `detectSpecialCase` to
+/// return this enum in order to perform various special case actions.
+pub fn SpecialCase(comptime E: type) type {
+ return union(enum) {
+ /// Immediately return this action.
+ action: E,
+
+ /// Return this action if no other action is found.
+ fallback: E,
+
+ /// If there is no pending action (we haven't seen an action yet)
+ /// then we should return no action. This is kind of weird but is
+ /// a special case to allow "-e" in Ghostty.
+ abort_if_no_action,
+ };
+}
+
+test "detect direct match" {
+ const testing = std.testing;
+ const alloc = testing.allocator;
+ const Enum = enum { foo, bar, baz };
+
+ var iter = try std.process.ArgIteratorGeneral(.{}).init(
+ alloc,
+ "+foo",
+ );
+ defer iter.deinit();
+ const result = try detectIter(Enum, &iter);
+ try testing.expectEqual(Enum.foo, result.?);
+}
+
+test "detect invalid match" {
+ const testing = std.testing;
+ const alloc = testing.allocator;
+ const Enum = enum { foo, bar, baz };
+
+ var iter = try std.process.ArgIteratorGeneral(.{}).init(
+ alloc,
+ "+invalid",
+ );
+ defer iter.deinit();
+ try testing.expectError(
+ DetectError.InvalidAction,
+ detectIter(Enum, &iter),
+ );
+}
+
+test "detect multiple actions" {
+ const testing = std.testing;
+ const alloc = testing.allocator;
+ const Enum = enum { foo, bar, baz };
+
+ var iter = try std.process.ArgIteratorGeneral(.{}).init(
+ alloc,
+ "+foo +bar",
+ );
+ defer iter.deinit();
+ try testing.expectError(
+ DetectError.MultipleActions,
+ detectIter(Enum, &iter),
+ );
+}
+
+test "detect no match" {
+ const testing = std.testing;
+ const alloc = testing.allocator;
+ const Enum = enum { foo, bar, baz };
+
+ var iter = try std.process.ArgIteratorGeneral(.{}).init(
+ alloc,
+ "--some-flag",
+ );
+ defer iter.deinit();
+ const result = try detectIter(Enum, &iter);
+ try testing.expect(result == null);
+}
+
+test "detect special case action" {
+ const testing = std.testing;
+ const alloc = testing.allocator;
+ const Enum = enum {
+ foo,
+ bar,
+
+ fn detectSpecialCase(arg: []const u8) ?SpecialCase(@This()) {
+ return if (std.mem.eql(u8, arg, "--special"))
+ .{ .action = .foo }
+ else
+ null;
+ }
+ };
+
+ {
+ var iter = try std.process.ArgIteratorGeneral(.{}).init(
+ alloc,
+ "--special +bar",
+ );
+ defer iter.deinit();
+ const result = try detectIter(Enum, &iter);
+ try testing.expectEqual(Enum.foo, result.?);
+ }
+
+ {
+ var iter = try std.process.ArgIteratorGeneral(.{}).init(
+ alloc,
+ "+bar --special",
+ );
+ defer iter.deinit();
+ const result = try detectIter(Enum, &iter);
+ try testing.expectEqual(Enum.foo, result.?);
+ }
+
+ {
+ var iter = try std.process.ArgIteratorGeneral(.{}).init(
+ alloc,
+ "+bar",
+ );
+ defer iter.deinit();
+ const result = try detectIter(Enum, &iter);
+ try testing.expectEqual(Enum.bar, result.?);
+ }
+}
+
+test "detect special case fallback" {
+ const testing = std.testing;
+ const alloc = testing.allocator;
+ const Enum = enum {
+ foo,
+ bar,
+
+ fn detectSpecialCase(arg: []const u8) ?SpecialCase(@This()) {
+ return if (std.mem.eql(u8, arg, "--special"))
+ .{ .fallback = .foo }
+ else
+ null;
+ }
+ };
+
+ {
+ var iter = try std.process.ArgIteratorGeneral(.{}).init(
+ alloc,
+ "--special",
+ );
+ defer iter.deinit();
+ const result = try detectIter(Enum, &iter);
+ try testing.expectEqual(Enum.foo, result.?);
+ }
+
+ {
+ var iter = try std.process.ArgIteratorGeneral(.{}).init(
+ alloc,
+ "+bar --special",
+ );
+ defer iter.deinit();
+ const result = try detectIter(Enum, &iter);
+ try testing.expectEqual(Enum.bar, result.?);
+ }
+
+ {
+ var iter = try std.process.ArgIteratorGeneral(.{}).init(
+ alloc,
+ "--special +bar",
+ );
+ defer iter.deinit();
+ const result = try detectIter(Enum, &iter);
+ try testing.expectEqual(Enum.bar, result.?);
+ }
+}
+
+test "detect special case abort_if_no_action" {
+ const testing = std.testing;
+ const alloc = testing.allocator;
+ const Enum = enum {
+ foo,
+ bar,
+
+ fn detectSpecialCase(arg: []const u8) ?SpecialCase(@This()) {
+ return if (std.mem.eql(u8, arg, "-e"))
+ .abort_if_no_action
+ else
+ null;
+ }
+ };
+
+ {
+ var iter = try std.process.ArgIteratorGeneral(.{}).init(
+ alloc,
+ "-e",
+ );
+ defer iter.deinit();
+ const result = try detectIter(Enum, &iter);
+ try testing.expect(result == null);
+ }
+
+ {
+ var iter = try std.process.ArgIteratorGeneral(.{}).init(
+ alloc,
+ "+foo -e",
+ );
+ defer iter.deinit();
+ const result = try detectIter(Enum, &iter);
+ try testing.expectEqual(Enum.foo, result.?);
+ }
+
+ {
+ var iter = try std.process.ArgIteratorGeneral(.{}).init(
+ alloc,
+ "-e +bar",
+ );
+ defer iter.deinit();
+ const result = try detectIter(Enum, &iter);
+ try testing.expect(result == null);
+ }
+}