- commit
- 98995bd
- parent
- 79fce5d
- author
- Eric Bower
- date
- 2026-02-01 20:14:33 -0500 EST
chore: short should be session name only
2 files changed,
+110,
-109
+1,
-0
1@@ -12,6 +12,7 @@ Use spec: https://common-changelog.org/
2 - New command `zmx [c]ompletions <shell>` that outputs auto-completion scripts for a given shell
3 - List command `zmx list` now shows `started_at` showing working directory when creating session
4 - List command `zmx list` now shows `cmd` showing command provided when creating session
5+- List command `zmx list` now shows `→` arrow indicating the current session
6
7 ### Fixed
8
+109,
-109
1@@ -505,44 +505,6 @@ const SessionEntry = struct {
2
3 const current_arrow = "→";
4
5-/// Formats a session entry for list output (only the name when `short` is
6-/// true), adding a prefix to indicate the current session, if there is one.
7-fn writeSessionLine(writer: *std.Io.Writer, session: SessionEntry, short: bool, current_session: ?[]const u8) !void {
8- const prefix = if (current_session) |current|
9- if (std.mem.eql(u8, current, session.name)) current_arrow ++ " " else " "
10- else
11- "";
12-
13- if (short) {
14- if (session.is_error) return;
15- try writer.print("{s}{s}\n", .{ prefix, session.name });
16- return;
17- }
18-
19- if (session.is_error) {
20- try writer.print("{s}session_name={s}\tstatus={s}\t(cleaning up)\n", .{
21- prefix,
22- session.name,
23- session.error_name.?,
24- });
25- return;
26- }
27-
28- try writer.print("{s}session_name={s}\tpid={d}\tclients={d}", .{
29- prefix,
30- session.name,
31- session.pid.?,
32- session.clients_len.?,
33- });
34- if (session.cwd) |cwd| {
35- try writer.print("\tstarted_in={s}", .{cwd});
36- }
37- if (session.cmd) |cmd| {
38- try writer.print("\tcmd={s}", .{cmd});
39- }
40- try writer.print("\n", .{});
41-}
42-
43 fn list(cfg: *Cfg, short: bool) !void {
44 var gpa = std.heap.GeneralPurposeAllocator(.{}){};
45 defer _ = gpa.deinit();
46@@ -1495,6 +1457,44 @@ fn getTerminalSize(fd: i32) ipc.Resize {
47 return .{ .rows = 24, .cols = 80 };
48 }
49
50+/// Formats a session entry for list output (only the name when `short` is
51+/// true), adding a prefix to indicate the current session, if there is one.
52+fn writeSessionLine(writer: *std.Io.Writer, session: SessionEntry, short: bool, current_session: ?[]const u8) !void {
53+ const prefix = if (current_session) |current|
54+ if (std.mem.eql(u8, current, session.name)) current_arrow ++ " " else " "
55+ else
56+ "";
57+
58+ if (short) {
59+ if (session.is_error) return;
60+ try writer.print("{s}\n", .{session.name});
61+ return;
62+ }
63+
64+ if (session.is_error) {
65+ try writer.print("{s}session_name={s}\tstatus={s}\t(cleaning up)\n", .{
66+ prefix,
67+ session.name,
68+ session.error_name.?,
69+ });
70+ return;
71+ }
72+
73+ try writer.print("{s}session_name={s}\tpid={d}\tclients={d}", .{
74+ prefix,
75+ session.name,
76+ session.pid.?,
77+ session.clients_len.?,
78+ });
79+ if (session.cwd) |cwd| {
80+ try writer.print("\tstarted_in={s}", .{cwd});
81+ }
82+ if (session.cmd) |cmd| {
83+ try writer.print("\tcmd={s}", .{cmd});
84+ }
85+ try writer.print("\n", .{});
86+}
87+
88 /// Detects Kitty keyboard protocol escape sequence for Ctrl+\
89 /// 92 = backslash, 5 = ctrl modifier, :1 = key press event
90 fn isKittyCtrlBackslash(buf: []const u8) bool {
91@@ -1502,6 +1502,75 @@ fn isKittyCtrlBackslash(buf: []const u8) bool {
92 std.mem.indexOf(u8, buf, "\x1b[92;5:1u") != null;
93 }
94
95+fn serializeTerminalState(alloc: std.mem.Allocator, term: *ghostty_vt.Terminal) ?[]const u8 {
96+ var builder: std.Io.Writer.Allocating = .init(alloc);
97+ defer builder.deinit();
98+
99+ var term_formatter = ghostty_vt.formatter.TerminalFormatter.init(term, .vt);
100+ term_formatter.content = .{ .selection = null };
101+ term_formatter.extra = .{
102+ .palette = false,
103+ .modes = true,
104+ .scrolling_region = true,
105+ .tabstops = false, // tabstop restoration moves cursor after CUP, corrupting position
106+ .pwd = true,
107+ .keyboard = true,
108+ .screen = .all,
109+ };
110+
111+ term_formatter.format(&builder.writer) catch |err| {
112+ std.log.warn("failed to format terminal state err={s}", .{@errorName(err)});
113+ return null;
114+ };
115+
116+ const output = builder.writer.buffered();
117+ if (output.len == 0) return null;
118+
119+ return alloc.dupe(u8, output) catch |err| {
120+ std.log.warn("failed to allocate terminal state err={s}", .{@errorName(err)});
121+ return null;
122+ };
123+}
124+
125+fn serializeTerminal(alloc: std.mem.Allocator, term: *ghostty_vt.Terminal, format: HistoryFormat) ?[]const u8 {
126+ var builder: std.Io.Writer.Allocating = .init(alloc);
127+ defer builder.deinit();
128+
129+ const opts: ghostty_vt.formatter.Options = switch (format) {
130+ .plain => .plain,
131+ .vt => .vt,
132+ .html => .html,
133+ };
134+ var term_formatter = ghostty_vt.formatter.TerminalFormatter.init(term, opts);
135+ term_formatter.content = .{ .selection = null };
136+ term_formatter.extra = switch (format) {
137+ .plain => .none,
138+ .vt => .{
139+ .palette = false,
140+ .modes = true,
141+ .scrolling_region = true,
142+ .tabstops = false,
143+ .pwd = true,
144+ .keyboard = true,
145+ .screen = .all,
146+ },
147+ .html => .styles,
148+ };
149+
150+ term_formatter.format(&builder.writer) catch |err| {
151+ std.log.warn("failed to format terminal err={s}", .{@errorName(err)});
152+ return null;
153+ };
154+
155+ const output = builder.writer.buffered();
156+ if (output.len == 0) return null;
157+
158+ return alloc.dupe(u8, output) catch |err| {
159+ std.log.warn("failed to allocate terminal output err={s}", .{@errorName(err)});
160+ return null;
161+ };
162+}
163+
164 test "isKittyCtrlBackslash" {
165 try std.testing.expect(isKittyCtrlBackslash("\x1b[92;5u"));
166 try std.testing.expect(isKittyCtrlBackslash("\x1b[92;5:1u"));
167@@ -1551,13 +1620,13 @@ test "writeSessionLine formats output for current session and short output" {
168 .session = session,
169 .short = true,
170 .current_session = "dev",
171- .expected = "→ dev\n",
172+ .expected = "dev\n",
173 },
174 .{
175 .session = session,
176 .short = true,
177 .current_session = "other",
178- .expected = " dev\n",
179+ .expected = "dev\n",
180 },
181 .{
182 .session = session,
183@@ -1575,72 +1644,3 @@ test "writeSessionLine formats output for current session and short output" {
184 try std.testing.expectEqualStrings(case.expected, builder.writer.buffered());
185 }
186 }
187-
188-fn serializeTerminalState(alloc: std.mem.Allocator, term: *ghostty_vt.Terminal) ?[]const u8 {
189- var builder: std.Io.Writer.Allocating = .init(alloc);
190- defer builder.deinit();
191-
192- var term_formatter = ghostty_vt.formatter.TerminalFormatter.init(term, .vt);
193- term_formatter.content = .{ .selection = null };
194- term_formatter.extra = .{
195- .palette = false,
196- .modes = true,
197- .scrolling_region = true,
198- .tabstops = false, // tabstop restoration moves cursor after CUP, corrupting position
199- .pwd = true,
200- .keyboard = true,
201- .screen = .all,
202- };
203-
204- term_formatter.format(&builder.writer) catch |err| {
205- std.log.warn("failed to format terminal state err={s}", .{@errorName(err)});
206- return null;
207- };
208-
209- const output = builder.writer.buffered();
210- if (output.len == 0) return null;
211-
212- return alloc.dupe(u8, output) catch |err| {
213- std.log.warn("failed to allocate terminal state err={s}", .{@errorName(err)});
214- return null;
215- };
216-}
217-
218-fn serializeTerminal(alloc: std.mem.Allocator, term: *ghostty_vt.Terminal, format: HistoryFormat) ?[]const u8 {
219- var builder: std.Io.Writer.Allocating = .init(alloc);
220- defer builder.deinit();
221-
222- const opts: ghostty_vt.formatter.Options = switch (format) {
223- .plain => .plain,
224- .vt => .vt,
225- .html => .html,
226- };
227- var term_formatter = ghostty_vt.formatter.TerminalFormatter.init(term, opts);
228- term_formatter.content = .{ .selection = null };
229- term_formatter.extra = switch (format) {
230- .plain => .none,
231- .vt => .{
232- .palette = false,
233- .modes = true,
234- .scrolling_region = true,
235- .tabstops = false,
236- .pwd = true,
237- .keyboard = true,
238- .screen = .all,
239- },
240- .html => .styles,
241- };
242-
243- term_formatter.format(&builder.writer) catch |err| {
244- std.log.warn("failed to format terminal err={s}", .{@errorName(err)});
245- return null;
246- };
247-
248- const output = builder.writer.buffered();
249- if (output.len == 0) return null;
250-
251- return alloc.dupe(u8, output) catch |err| {
252- std.log.warn("failed to allocate terminal output err={s}", .{@errorName(err)});
253- return null;
254- };
255-}