diff --git a/src/config/Config.zig b/src/config/Config.zig index 85d140492..809eb427d 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -387,10 +387,14 @@ palette: Palette = .{}, /// flag. For example: `ghostty -e fish --with --custom --args`. command: ?[]const u8 = null, -/// The number of milliseconds below which we consider a process -/// exit to be abnormal. This is used to show an error message -/// when the process exits too quickly. -@"abnormal-runtime-threshold-ms": u64 = 250, +/// The number of milliseconds of runtime below which we consider a process +/// exit to be abnormal. This is used to show an error message when the +/// process exits too quickly. +/// +/// On Linux, this must be paired with a non-zero exit code. On macOS, +/// we allow any exit code because of the way shell processes are launched +/// via the login command. +@"abnormal-command-exit-runtime": u32 = 250, /// Match a regular expression against the terminal text and associate /// clicking it with an action. This can be used to match URLs, file paths, diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index 0db1c1e95..15ed97b3f 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -41,7 +41,7 @@ const disable_kitty_keyboard_protocol = apprt.runtime == apprt.glfw; /// The number of milliseconds below which we consider a process /// exit to be abnormal. This is used to show an error message /// when the process exits too quickly. -abnormal_runtime_threshold_ms: u64, +abnormal_runtime_threshold_ms: u32, /// Allocator alloc: Allocator, @@ -111,7 +111,7 @@ pub const DerivedConfig = struct { osc_color_report_format: configpkg.Config.OSCColorReportFormat, term: []const u8, grapheme_width_method: configpkg.Config.GraphemeWidthMethod, - abnormal_runtime_threshold_ms: u64, + abnormal_runtime_threshold_ms: u32, pub fn init( alloc_gpa: Allocator, @@ -130,7 +130,7 @@ pub const DerivedConfig = struct { .osc_color_report_format = config.@"osc-color-report-format", .term = config.term, .grapheme_width_method = config.@"grapheme-width-method", - .abnormal_runtime_threshold_ms = config.@"abnormal-runtime-threshold-ms", + .abnormal_runtime_threshold_ms = config.@"abnormal-command-exit-runtime", }; } @@ -551,18 +551,13 @@ pub fn jumpToPrompt(self: *Exec, delta: isize) !void { /// Called when the child process exited abnormally but before /// the surface is notified. pub fn childExitedAbnormally(self: *Exec, exit_code: u32, runtime_ms: u64) !void { - // Build up our command for the error message - const command = try std.mem.join( - self.alloc, - " ", - self.subprocess.args, - ); - defer self.alloc.free(command); + var arena = ArenaAllocator.init(self.alloc); + defer arena.deinit(); + const alloc = arena.allocator(); - const runtime_str = try std.fmt.allocPrint(self.alloc, "{d} ms", .{runtime_ms}); - defer self.alloc.free(runtime_str); - const exit_code_str = try std.fmt.allocPrint(self.alloc, "{d}", .{exit_code}); - defer self.alloc.free(exit_code_str); + // Build up our command for the error message + const command = try std.mem.join(alloc, " ", self.subprocess.args); + const runtime_str = try std.fmt.allocPrint(alloc, "{d} ms", .{runtime_ms}); // Modify the terminal to show our error message. This // requires grabbing the renderer state lock. @@ -580,8 +575,7 @@ pub fn childExitedAbnormally(self: *Exec, exit_code: u32, runtime_ms: u64) !void // a little bit and write a horizontal rule before writing // our message. This lets the use see the error message the // command may have output. - const viewport_str = try t.plainString(self.alloc); - defer self.alloc.free(viewport_str); + const viewport_str = try t.plainString(alloc); if (viewport_str.len > 0) { try t.linefeed(); for (0..t.cols) |_| try t.print(0x2501); @@ -593,27 +587,36 @@ pub fn childExitedAbnormally(self: *Exec, exit_code: u32, runtime_ms: u64) !void // Output our error message try t.setAttribute(.{ .@"8_fg" = .bright_red }); try t.setAttribute(.{ .bold = {} }); - try t.printString("Ghostty failed to launch the requested command:"); + try t.printString("Ghostty failed to launch the requested command."); try t.setAttribute(.{ .unset = {} }); + t.carriageReturn(); try t.linefeed(); try t.linefeed(); - try t.setAttribute(.{ .bold = {} }); + try t.printString("Command: "); try t.printString(command); try t.setAttribute(.{ .unset = {} }); + t.carriageReturn(); try t.linefeed(); try t.linefeed(); - try t.printString("Runtime: "); + try t.printString("Runtime: "); try t.setAttribute(.{ .@"8_fg" = .bright_red }); try t.printString(runtime_str); try t.setAttribute(.{ .unset = {} }); - t.carriageReturn(); - try t.linefeed(); - try t.printString("Exit code: "); - try t.setAttribute(.{ .@"8_fg" = .bright_red }); - try t.printString(exit_code_str); - try t.setAttribute(.{ .unset = {} }); + + // We don't print this on macOS because the exit code is always 0 + // due to the way we launch the process. + if (comptime !builtin.target.isDarwin()) { + const exit_code_str = try std.fmt.allocPrint(alloc, "{d}", .{exit_code}); + t.carriageReturn(); + try t.linefeed(); + try t.printString("Exit Code: "); + try t.setAttribute(.{ .@"8_fg" = .bright_red }); + try t.printString(exit_code_str); + try t.setAttribute(.{ .unset = {} }); + } + t.carriageReturn(); try t.linefeed(); try t.linefeed(); @@ -771,7 +774,7 @@ const EventData = struct { /// The number of milliseconds below which we consider a process /// exit to be abnormal. This is used to show an error message /// when the process exits too quickly. - abnormal_runtime_threshold_ms: u64, + abnormal_runtime_threshold_ms: u32, pub fn deinit(self: *EventData, alloc: Allocator) void { // Clear our write pools. We know we aren't ever going to do