Merge pull request #2606 from ghostty-org/push-ovrqrlvyrkoo

config: add "initial-command" config, "-e" sets that
This commit is contained in:
Mitchell Hashimoto
2024-11-05 17:20:17 -08:00
committed by GitHub
3 changed files with 43 additions and 10 deletions

View File

@ -66,6 +66,11 @@ font_grid_set: font.SharedGridSet,
last_notification_time: ?std.time.Instant = null, last_notification_time: ?std.time.Instant = null,
last_notification_digest: u64 = 0, last_notification_digest: u64 = 0,
/// Set to false once we've created at least one surface. This
/// never goes true again. This can be used by surfaces to determine
/// if they are the first surface.
first: bool = true,
pub const CreateError = Allocator.Error || font.SharedGridSet.InitError; pub const CreateError = Allocator.Error || font.SharedGridSet.InitError;
/// Initialize the main app instance. This creates the main window, sets /// Initialize the main app instance. This creates the main window, sets

View File

@ -474,13 +474,19 @@ pub fn init(
.config = derived_config, .config = derived_config,
}; };
// The command we're going to execute
const command: ?[]const u8 = if (app.first)
config.@"initial-command" orelse config.command
else
config.command;
// Start our IO implementation // Start our IO implementation
// This separate block ({}) is important because our errdefers must // This separate block ({}) is important because our errdefers must
// be scoped here to be valid. // be scoped here to be valid.
{ {
// Initialize our IO backend // Initialize our IO backend
var io_exec = try termio.Exec.init(alloc, .{ var io_exec = try termio.Exec.init(alloc, .{
.command = config.command, .command = command,
.shell_integration = config.@"shell-integration", .shell_integration = config.@"shell-integration",
.shell_integration_features = config.@"shell-integration-features", .shell_integration_features = config.@"shell-integration-features",
.working_directory = config.@"working-directory", .working_directory = config.@"working-directory",
@ -618,9 +624,9 @@ pub fn init(
// For xdg-terminal-exec execution we special-case and set the window // For xdg-terminal-exec execution we special-case and set the window
// title to the command being executed. This allows window managers // title to the command being executed. This allows window managers
// to set custom styling based on the command being executed. // to set custom styling based on the command being executed.
const command = config.command orelse break :xdg; const v = command orelse break :xdg;
if (command.len > 0) { if (v.len > 0) {
const title = alloc.dupeZ(u8, command) catch |err| { const title = alloc.dupeZ(u8, v) catch |err| {
log.warn( log.warn(
"error copying command for title, title will not be set err={}", "error copying command for title, title will not be set err={}",
.{err}, .{err},
@ -635,6 +641,9 @@ pub fn init(
); );
} }
} }
// We are no longer the first surface
app.first = false;
} }
pub fn deinit(self: *Surface) void { pub fn deinit(self: *Surface) void {

View File

@ -513,7 +513,26 @@ palette: Palette = .{},
/// arguments are provided, the command will be executed using `/bin/sh -c`. /// arguments are provided, the command will be executed using `/bin/sh -c`.
/// Ghostty does not do any shell command parsing. /// Ghostty does not do any shell command parsing.
/// ///
/// If you're using the `ghostty` CLI there is also a shortcut to run a command /// This command will be used for all new terminal surfaces, i.e. new windows,
/// tabs, etc. If you want to run a command only for the first terminal surface
/// created when Ghostty starts, use the `initial-command` configuration.
///
/// Ghostty supports the common `-e` flag for executing a command with
/// arguments. For example, `ghostty -e fish --with --custom --args`.
/// This flag sets the `initial-command` configuration, see that for more
/// information.
command: ?[]const u8 = null,
/// This is the same as "command", but only applies to the first terminal
/// surface created when Ghostty starts. Subsequent terminal surfaces will use
/// the `command` configuration.
///
/// After the first terminal surface is created (or closed), there is no
/// way to run this initial command again automatically. As such, setting
/// this at runtime works but will only affect the next terminal surface
/// if it is the first one ever created.
///
/// If you're using the `ghostty` CLI there is also a shortcut to set this
/// with arguments directly: you can use the `-e` flag. For example: `ghostty -e /// with arguments directly: you can use the `-e` flag. For example: `ghostty -e
/// fish --with --custom --args`. The `-e` flag automatically forces some /// fish --with --custom --args`. The `-e` flag automatically forces some
/// other behaviors as well: /// other behaviors as well:
@ -525,7 +544,7 @@ palette: Palette = .{},
/// process will exit when the command exits. Additionally, the /// process will exit when the command exits. Additionally, the
/// `quit-after-last-window-closed-delay` is unset. /// `quit-after-last-window-closed-delay` is unset.
/// ///
command: ?[]const u8 = null, @"initial-command": ?[]const u8 = null,
/// If true, keep the terminal open after the command exits. Normally, the /// If true, keep the terminal open after the command exits. Normally, the
/// terminal window closes when the running command (such as a shell) exits. /// terminal window closes when the running command (such as a shell) exits.
@ -2356,7 +2375,7 @@ pub fn loadCliArgs(self: *Config, alloc_gpa: Allocator) !void {
} }
self.@"_xdg-terminal-exec" = true; self.@"_xdg-terminal-exec" = true;
self.command = command.items[0 .. command.items.len - 1]; self.@"initial-command" = command.items[0 .. command.items.len - 1];
return; return;
} }
} }
@ -2755,7 +2774,7 @@ pub fn parseManuallyHook(
return false; return false;
} }
self.command = command.items[0 .. command.items.len - 1]; self.@"initial-command" = command.items[0 .. command.items.len - 1];
// See "command" docs for the implied configurations and why. // See "command" docs for the implied configurations and why.
self.@"gtk-single-instance" = .false; self.@"gtk-single-instance" = .false;
@ -2945,7 +2964,7 @@ test "parse e: command only" {
var it: TestIterator = .{ .data = &.{"foo"} }; var it: TestIterator = .{ .data = &.{"foo"} };
try testing.expect(!try cfg.parseManuallyHook(alloc, "-e", &it)); try testing.expect(!try cfg.parseManuallyHook(alloc, "-e", &it));
try testing.expectEqualStrings("foo", cfg.command.?); try testing.expectEqualStrings("foo", cfg.@"initial-command".?);
} }
test "parse e: command and args" { test "parse e: command and args" {
@ -2956,7 +2975,7 @@ test "parse e: command and args" {
var it: TestIterator = .{ .data = &.{ "echo", "foo", "bar baz" } }; var it: TestIterator = .{ .data = &.{ "echo", "foo", "bar baz" } };
try testing.expect(!try cfg.parseManuallyHook(alloc, "-e", &it)); try testing.expect(!try cfg.parseManuallyHook(alloc, "-e", &it));
try testing.expectEqualStrings("echo foo bar baz", cfg.command.?); try testing.expectEqualStrings("echo foo bar baz", cfg.@"initial-command".?);
} }
test "clone default" { test "clone default" {