mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
shell-integration: automatic bash integration
This change adds automatic bash shell detection and integration. Unlike our other shell integrations, bash doesn't provide a built-in mechanism for injecting our ghostty.bash script into the new shell environment. Instead, we start bash in POSIX mode and use the ENV environment variable to load our integration script, and the rest of the bash startup sequence becomes the responsibility of our script to emulate (along with disabling POSIX mode).
This commit is contained in:
@ -408,7 +408,7 @@ palette: Palette = .{},
|
||||
/// Ghostty does not do any shell command parsing.
|
||||
///
|
||||
/// If you're using the `ghostty` CLI there is also a shortcut to run a command
|
||||
/// with argumens 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`.
|
||||
command: ?[]const u8 = null,
|
||||
|
||||
@ -841,7 +841,7 @@ keybind: Keybinds = .{},
|
||||
///
|
||||
/// * `detect` - Detect the shell based on the filename.
|
||||
///
|
||||
/// * `fish`, `zsh` - Use this specific shell injection scheme.
|
||||
/// * `bash`, `fish`, `zsh` - Use this specific shell injection scheme.
|
||||
///
|
||||
/// The default value is `detect`.
|
||||
@"shell-integration": ShellIntegration = .detect,
|
||||
@ -3402,6 +3402,7 @@ pub const CopyOnSelect = enum {
|
||||
pub const ShellIntegration = enum {
|
||||
none,
|
||||
detect,
|
||||
bash,
|
||||
fish,
|
||||
zsh,
|
||||
};
|
||||
|
@ -13,6 +13,66 @@
|
||||
if [[ "$-" != *i* ]] ; then builtin return; fi
|
||||
if [ -z "$GHOSTTY_RESOURCES_DIR" ]; then builtin return; fi
|
||||
|
||||
# When automatic shell integration is active, we need to manually
|
||||
# load the normal bash startup files based on the injected state.
|
||||
if [ -n "$GHOSTTY_BASH_INJECT" ]; then
|
||||
builtin declare ghostty_bash_inject="$GHOSTTY_BASH_INJECT"
|
||||
builtin unset GHOSTTY_BASH_INJECT ENV
|
||||
|
||||
# At this point, we're in POSIX mode and rely on the injected
|
||||
# flags to guide is through the rest of the startup sequence.
|
||||
|
||||
# POSIX mode was requested by the user so there's nothing
|
||||
# more to do that optionally source their original $ENV.
|
||||
# No other startup files are read, per the standard.
|
||||
if [[ "$ghostty_bash_inject" == *"--posix"* ]]; then
|
||||
if [ -n "$GHOSTTY_BASH_ENV" ]; then
|
||||
builtin source "$GHOSTTY_BASH_ENV"
|
||||
builtin export ENV="$GHOSTTY_BASH_ENV"
|
||||
fi
|
||||
else
|
||||
# Restore bash's default 'posix' behavior. Also reset 'inherit_errexit',
|
||||
# which doesn't happen as part of the 'posix' reset.
|
||||
builtin set +o posix
|
||||
builtin shopt -u inherit_errexit 2>/dev/null
|
||||
|
||||
# Unexport HISTFILE if it was set by the shell integration code.
|
||||
if [[ -n "$GHOSTTY_BASH_UNEXPORT_HISTFILE" ]]; then
|
||||
builtin export -n HISTFILE
|
||||
builtin unset GHOSTTY_BASH_UNEXPORT_HISTFILE
|
||||
fi
|
||||
|
||||
# Manually source the startup files, respecting the injected flags like
|
||||
# --norc and --noprofile that we parsed with the shell integration code.
|
||||
#
|
||||
# See also: run_startup_files() in shell.c in the Bash source code
|
||||
if builtin shopt -q login_shell; then
|
||||
if [[ $ghostty_bash_inject != *"--noprofile"* ]]; then
|
||||
[ -r /etc/profile ] && builtin source "/etc/profile"
|
||||
for rcfile in "$HOME/.bash_profile" "$HOME/.bash_login" "$HOME/.profile"; do
|
||||
[ -r "$rcfile" ] && { builtin source "$rcfile"; break; }
|
||||
done
|
||||
fi
|
||||
else
|
||||
if [[ $ghostty_bash_inject != *"--norc"* ]]; then
|
||||
# The location of the system bashrc is determined at bash build
|
||||
# time via -DSYS_BASHRC and can therefore vary across distros:
|
||||
# Arch, Debian, Ubuntu use /etc/bash.bashrc
|
||||
# Fedora uses /etc/bashrc sourced from ~/.bashrc instead of SYS_BASHRC
|
||||
# Void Linux uses /etc/bash/bashrc
|
||||
for rcfile in /etc/bash.bashrc /etc/bash/bashrc ; do
|
||||
[ -r "$rcfile" ] && { builtin source "$rcfile"; break; }
|
||||
done
|
||||
if [[ -z "$GHOSTTY_BASH_RCFILE" ]]; then GHOSTTY_BASH_RCFILE="$HOME/.bashrc"; fi
|
||||
[ -r "$GHOSTTY_BASH_RCFILE" ] && builtin source "$GHOSTTY_BASH_RCFILE"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
builtin unset GHOSTTY_BASH_ENV GHOSTTY_BASH_RCFILE
|
||||
builtin unset ghostty_bash_inject rcfile
|
||||
fi
|
||||
|
||||
# Import bash-preexec, safe to do multiple times
|
||||
builtin source "$GHOSTTY_RESOURCES_DIR/shell-integration/bash/bash-preexec.sh"
|
||||
|
||||
|
@ -908,7 +908,7 @@ const Subprocess = struct {
|
||||
const alloc = arena.allocator();
|
||||
|
||||
// Determine the shell command we're executing
|
||||
const shell_command = opts.full_config.command orelse switch (builtin.os.tag) {
|
||||
var shell_command = opts.full_config.command orelse switch (builtin.os.tag) {
|
||||
.windows => "cmd.exe",
|
||||
else => "sh",
|
||||
};
|
||||
@ -1019,6 +1019,41 @@ const Subprocess = struct {
|
||||
env.remove("GHOSTTY_MAC_APP");
|
||||
}
|
||||
|
||||
// Setup our shell integration, if we can.
|
||||
const integrated_shell: ?shell_integration.ShellIntegration = shell: {
|
||||
const force: ?shell_integration.Shell = switch (opts.full_config.@"shell-integration") {
|
||||
.none => break :shell null,
|
||||
.detect => null,
|
||||
.bash => .bash,
|
||||
.fish => .fish,
|
||||
.zsh => .zsh,
|
||||
};
|
||||
|
||||
const dir = opts.resources_dir orelse break :shell null;
|
||||
|
||||
break :shell try shell_integration.setup(
|
||||
gpa,
|
||||
dir,
|
||||
shell_command,
|
||||
&env,
|
||||
force,
|
||||
opts.full_config.@"shell-integration-features",
|
||||
);
|
||||
};
|
||||
defer if (integrated_shell) |shell| shell.deinit(gpa);
|
||||
|
||||
if (integrated_shell) |shell| {
|
||||
log.info(
|
||||
"shell integration automatically injected shell={}",
|
||||
.{shell.shell},
|
||||
);
|
||||
if (shell.command) |command| {
|
||||
shell_command = command;
|
||||
}
|
||||
} else if (opts.full_config.@"shell-integration" != .none) {
|
||||
log.warn("shell could not be detected, no automatic shell integration will be injected", .{});
|
||||
}
|
||||
|
||||
// Build our args list
|
||||
const args = args: {
|
||||
const cap = 9; // the most we'll ever use
|
||||
@ -1157,35 +1192,6 @@ const Subprocess = struct {
|
||||
else
|
||||
null;
|
||||
|
||||
// Setup our shell integration, if we can.
|
||||
const shell_integrated: ?shell_integration.Shell = shell: {
|
||||
const force: ?shell_integration.Shell = switch (opts.full_config.@"shell-integration") {
|
||||
.none => break :shell null,
|
||||
.detect => null,
|
||||
.fish => .fish,
|
||||
.zsh => .zsh,
|
||||
};
|
||||
|
||||
const dir = opts.resources_dir orelse break :shell null;
|
||||
|
||||
break :shell try shell_integration.setup(
|
||||
gpa,
|
||||
dir,
|
||||
shell_command,
|
||||
&env,
|
||||
force,
|
||||
opts.full_config.@"shell-integration-features",
|
||||
);
|
||||
};
|
||||
if (shell_integrated) |shell| {
|
||||
log.info(
|
||||
"shell integration automatically injected shell={}",
|
||||
.{shell},
|
||||
);
|
||||
} else if (opts.full_config.@"shell-integration" != .none) {
|
||||
log.warn("shell could not be detected, no automatic shell integration will be injected", .{});
|
||||
}
|
||||
|
||||
// Our screen size should be our padded size
|
||||
const padded_size = opts.screen_size.subPadding(opts.padding);
|
||||
|
||||
|
@ -7,19 +7,34 @@ const log = std.log.scoped(.shell_integration);
|
||||
|
||||
/// Shell types we support
|
||||
pub const Shell = enum {
|
||||
bash,
|
||||
fish,
|
||||
zsh,
|
||||
};
|
||||
|
||||
pub const ShellIntegration = struct {
|
||||
/// The successfully-integrated shell.
|
||||
shell: Shell,
|
||||
|
||||
/// A revised shell command. This value will be allocated
|
||||
/// with the setup() function's allocator and becomes the
|
||||
/// caller's responsibility to free it.
|
||||
command: ?[]const u8 = null,
|
||||
|
||||
pub fn deinit(self: ShellIntegration, alloc: Allocator) void {
|
||||
if (self.command) |command| {
|
||||
alloc.free(command);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Setup the command execution environment for automatic
|
||||
/// integrated shell integration. This returns true if shell
|
||||
/// integration was successful. False could mean many things:
|
||||
/// the shell type wasn't detected, etc.
|
||||
/// integrated shell integration and return a ShellIntegration
|
||||
/// struct describing the integration. If integration fails
|
||||
/// (shell type couldn't be detected, etc.), this will return null.
|
||||
///
|
||||
/// The allocator is only used for temporary values, so it should
|
||||
/// be given a general purpose allocator. No allocated memory remains
|
||||
/// after this function returns except anything allocated by the
|
||||
/// EnvMap.
|
||||
/// The allocator is used for temporary values and to allocate values
|
||||
/// in the ShellIntegration result.
|
||||
pub fn setup(
|
||||
alloc: Allocator,
|
||||
resource_dir: []const u8,
|
||||
@ -27,8 +42,9 @@ pub fn setup(
|
||||
env: *EnvMap,
|
||||
force_shell: ?Shell,
|
||||
features: config.ShellIntegrationFeatures,
|
||||
) !?Shell {
|
||||
) !?ShellIntegration {
|
||||
const exe = if (force_shell) |shell| switch (shell) {
|
||||
.bash => "bash",
|
||||
.fish => "fish",
|
||||
.zsh => "zsh",
|
||||
} else exe: {
|
||||
@ -38,7 +54,14 @@ pub fn setup(
|
||||
break :exe std.fs.path.basename(command[0..idx]);
|
||||
};
|
||||
|
||||
var new_command: ?[]const u8 = null;
|
||||
const shell: Shell = shell: {
|
||||
if (std.mem.eql(u8, "bash", exe)) {
|
||||
new_command = try setupBash(alloc, command, resource_dir, env);
|
||||
if (new_command == null) return null;
|
||||
break :shell .bash;
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, "fish", exe)) {
|
||||
try setupFish(alloc, resource_dir, env);
|
||||
break :shell .fish;
|
||||
@ -57,7 +80,297 @@ pub fn setup(
|
||||
if (!features.sudo) try env.put("GHOSTTY_SHELL_INTEGRATION_NO_SUDO", "1");
|
||||
if (!features.title) try env.put("GHOSTTY_SHELL_INTEGRATION_NO_TITLE", "1");
|
||||
|
||||
return shell;
|
||||
return .{
|
||||
.shell = shell,
|
||||
.command = new_command,
|
||||
};
|
||||
}
|
||||
|
||||
test "force shell" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var env = EnvMap.init(alloc);
|
||||
defer env.deinit();
|
||||
|
||||
inline for (@typeInfo(Shell).Enum.fields) |field| {
|
||||
const shell = @field(Shell, field.name);
|
||||
const result = try setup(alloc, ".", "sh", &env, shell, .{});
|
||||
|
||||
try testing.expect(result != null);
|
||||
if (result) |r| {
|
||||
try testing.expectEqual(shell, r.shell);
|
||||
r.deinit(alloc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Setup the bash automatic shell integration. This works by
|
||||
/// starting bash in POSIX mode and using the ENV environment
|
||||
/// variable to load our bash integration script. This prevents
|
||||
/// bash from loading its normal startup files, which becomes
|
||||
/// our script's responsibility (along with disabling POSIX
|
||||
/// mode).
|
||||
///
|
||||
/// This returns a new (allocated) shell command string that
|
||||
/// enables the integration or null if integration failed.
|
||||
fn setupBash(
|
||||
alloc: Allocator,
|
||||
command: []const u8,
|
||||
resource_dir: []const u8,
|
||||
env: *EnvMap,
|
||||
) !?[]const u8 {
|
||||
// Accumulates the arguments that will form the final shell command line.
|
||||
// We can build this list on the stack because we're just temporarily
|
||||
// referencing other slices, but we can fall back to heap in extreme cases.
|
||||
var args_alloc = std.heap.stackFallback(1024, alloc);
|
||||
var args = try std.ArrayList([]const u8).initCapacity(args_alloc.get(), 2);
|
||||
defer args.deinit();
|
||||
|
||||
// Iterator that yields each argument in the original command line.
|
||||
// This will allocate once proportionate to the command line length.
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(alloc, command);
|
||||
defer iter.deinit();
|
||||
|
||||
// Start accumulating arguments with the executable and `--posix` mode flag.
|
||||
if (iter.next()) |exe| {
|
||||
try args.append(exe);
|
||||
} else return null;
|
||||
try args.append("--posix");
|
||||
|
||||
// Stores the list of intercepted command line flags that will be passed
|
||||
// to our shell integration script: --posix --norc --noprofile
|
||||
// We always include at least "1" so the script can differentiate between
|
||||
// being manually sourced or automatically injected (from here).
|
||||
var inject = try std.BoundedArray(u8, 32).init(0);
|
||||
try inject.appendSlice("1");
|
||||
|
||||
var posix = false;
|
||||
|
||||
// Some additional cases we don't yet cover:
|
||||
//
|
||||
// - If the `c` shell option is set, interactive mode is disabled, so skip
|
||||
// loading our shell integration.
|
||||
// - If additional file arguments are provided (after a `-` or `--` flag),
|
||||
// and the `i` shell option isn't being explicitly set, we can assume a
|
||||
// non-interactive shell session and skip loading our shell integration.
|
||||
while (iter.next()) |arg| {
|
||||
if (std.mem.eql(u8, arg, "--posix")) {
|
||||
try inject.appendSlice(" --posix");
|
||||
posix = true;
|
||||
} else if (std.mem.eql(u8, arg, "--norc")) {
|
||||
try inject.appendSlice(" --norc");
|
||||
} else if (std.mem.eql(u8, arg, "--noprofile")) {
|
||||
try inject.appendSlice(" --noprofile");
|
||||
} else if (std.mem.eql(u8, arg, "--rcfile") or std.mem.eql(u8, arg, "--init-file")) {
|
||||
if (iter.next()) |rcfile| {
|
||||
try env.put("GHOSTTY_BASH_RCFILE", rcfile);
|
||||
}
|
||||
} else {
|
||||
try args.append(arg);
|
||||
}
|
||||
}
|
||||
try env.put("GHOSTTY_BASH_INJECT", inject.slice());
|
||||
|
||||
// In POSIX mode, HISTFILE defaults to ~/.sh_history.
|
||||
if (!posix and env.get("HISTFILE") == null) {
|
||||
try env.put("HISTFILE", "~/.bash_history");
|
||||
try env.put("GHOSTTY_BASH_UNEXPORT_HISTFILE", "1");
|
||||
}
|
||||
|
||||
// Preserve the existing ENV value in POSIX mode.
|
||||
if (env.get("ENV")) |old| {
|
||||
if (posix) {
|
||||
try env.put("GHOSTTY_BASH_ENV", old);
|
||||
}
|
||||
}
|
||||
|
||||
// Set our new ENV to point to our integration script.
|
||||
var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
|
||||
const integ_dir = try std.fmt.bufPrint(
|
||||
&path_buf,
|
||||
"{s}/shell-integration/bash/ghostty.bash",
|
||||
.{resource_dir},
|
||||
);
|
||||
try env.put("ENV", integ_dir);
|
||||
|
||||
// Join the acculumated arguments to form the final command string.
|
||||
return try std.mem.join(alloc, " ", args.items);
|
||||
}
|
||||
|
||||
test "bash" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var env = EnvMap.init(alloc);
|
||||
defer env.deinit();
|
||||
|
||||
const command = try setupBash(alloc, "bash", ".", &env);
|
||||
defer if (command) |c| alloc.free(c);
|
||||
|
||||
try testing.expectEqualStrings("bash --posix", command.?);
|
||||
try testing.expectEqualStrings("./shell-integration/bash/ghostty.bash", env.get("ENV").?);
|
||||
try testing.expectEqualStrings("1", env.get("GHOSTTY_BASH_INJECT").?);
|
||||
}
|
||||
|
||||
test "bash: inject flags" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
// bash --posix
|
||||
{
|
||||
var env = EnvMap.init(alloc);
|
||||
defer env.deinit();
|
||||
|
||||
const command = try setupBash(alloc, "bash --posix", ".", &env);
|
||||
defer if (command) |c| alloc.free(c);
|
||||
|
||||
try testing.expectEqualStrings("bash --posix", command.?);
|
||||
try testing.expectEqualStrings("1 --posix", env.get("GHOSTTY_BASH_INJECT").?);
|
||||
}
|
||||
|
||||
// bash --norc
|
||||
{
|
||||
var env = EnvMap.init(alloc);
|
||||
defer env.deinit();
|
||||
|
||||
const command = try setupBash(alloc, "bash --norc", ".", &env);
|
||||
defer if (command) |c| alloc.free(c);
|
||||
|
||||
try testing.expectEqualStrings("bash --posix", command.?);
|
||||
try testing.expectEqualStrings("1 --norc", env.get("GHOSTTY_BASH_INJECT").?);
|
||||
}
|
||||
|
||||
// bash --noprofile
|
||||
{
|
||||
var env = EnvMap.init(alloc);
|
||||
defer env.deinit();
|
||||
|
||||
const command = try setupBash(alloc, "bash --noprofile", ".", &env);
|
||||
defer if (command) |c| alloc.free(c);
|
||||
|
||||
try testing.expectEqualStrings("bash --posix", command.?);
|
||||
try testing.expectEqualStrings("1 --noprofile", env.get("GHOSTTY_BASH_INJECT").?);
|
||||
}
|
||||
}
|
||||
|
||||
test "bash: rcfile" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var env = EnvMap.init(alloc);
|
||||
defer env.deinit();
|
||||
|
||||
// bash --rcfile
|
||||
{
|
||||
const command = try setupBash(alloc, "bash --rcfile profile.sh", ".", &env);
|
||||
defer if (command) |c| alloc.free(c);
|
||||
|
||||
try testing.expectEqualStrings("bash --posix", command.?);
|
||||
try testing.expectEqualStrings("profile.sh", env.get("GHOSTTY_BASH_RCFILE").?);
|
||||
}
|
||||
|
||||
// bash --init-file
|
||||
{
|
||||
const command = try setupBash(alloc, "bash --init-file profile.sh", ".", &env);
|
||||
defer if (command) |c| alloc.free(c);
|
||||
|
||||
try testing.expectEqualStrings("bash --posix", command.?);
|
||||
try testing.expectEqualStrings("profile.sh", env.get("GHOSTTY_BASH_RCFILE").?);
|
||||
}
|
||||
}
|
||||
|
||||
test "bash: HISTFILE" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
// HISTFILE unset
|
||||
{
|
||||
var env = EnvMap.init(alloc);
|
||||
defer env.deinit();
|
||||
|
||||
const command = try setupBash(alloc, "bash", ".", &env);
|
||||
defer if (command) |c| alloc.free(c);
|
||||
|
||||
try testing.expectEqualStrings("~/.bash_history", env.get("HISTFILE").?);
|
||||
try testing.expectEqualStrings("1", env.get("GHOSTTY_BASH_UNEXPORT_HISTFILE").?);
|
||||
}
|
||||
|
||||
// HISTFILE set
|
||||
{
|
||||
var env = EnvMap.init(alloc);
|
||||
defer env.deinit();
|
||||
|
||||
try env.put("HISTFILE", "my_history");
|
||||
|
||||
const command = try setupBash(alloc, "bash", ".", &env);
|
||||
defer if (command) |c| alloc.free(c);
|
||||
|
||||
try testing.expectEqualStrings("my_history", env.get("HISTFILE").?);
|
||||
try testing.expect(env.get("GHOSTTY_BASH_UNEXPORT_HISTFILE") == null);
|
||||
}
|
||||
|
||||
// HISTFILE unset (POSIX mode)
|
||||
{
|
||||
var env = EnvMap.init(alloc);
|
||||
defer env.deinit();
|
||||
|
||||
const command = try setupBash(alloc, "bash --posix", ".", &env);
|
||||
defer if (command) |c| alloc.free(c);
|
||||
|
||||
try testing.expect(env.get("HISTFILE") == null);
|
||||
try testing.expect(env.get("GHOSTTY_BASH_UNEXPORT_HISTFILE") == null);
|
||||
}
|
||||
|
||||
// HISTFILE set (POSIX mode)
|
||||
{
|
||||
var env = EnvMap.init(alloc);
|
||||
defer env.deinit();
|
||||
|
||||
try env.put("HISTFILE", "my_history");
|
||||
|
||||
const command = try setupBash(alloc, "bash --posix", ".", &env);
|
||||
defer if (command) |c| alloc.free(c);
|
||||
|
||||
try testing.expectEqualStrings("my_history", env.get("HISTFILE").?);
|
||||
try testing.expect(env.get("GHOSTTY_BASH_UNEXPORT_HISTFILE") == null);
|
||||
}
|
||||
}
|
||||
|
||||
test "bash: preserve ENV" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var env = EnvMap.init(alloc);
|
||||
defer env.deinit();
|
||||
|
||||
const original_env = "original-env.bash";
|
||||
|
||||
// POSIX mode
|
||||
{
|
||||
try env.put("ENV", original_env);
|
||||
const command = try setupBash(alloc, "bash --posix", ".", &env);
|
||||
defer if (command) |c| alloc.free(c);
|
||||
|
||||
try testing.expect(std.mem.indexOf(u8, command.?, "--posix") != null);
|
||||
try testing.expect(std.mem.indexOf(u8, env.get("GHOSTTY_BASH_INJECT").?, "posix") != null);
|
||||
try testing.expectEqualStrings(original_env, env.get("GHOSTTY_BASH_ENV").?);
|
||||
try testing.expectEqualStrings("./shell-integration/bash/ghostty.bash", env.get("ENV").?);
|
||||
}
|
||||
|
||||
env.remove("GHOSTTY_BASH_ENV");
|
||||
|
||||
// Not POSIX mode
|
||||
{
|
||||
try env.put("ENV", original_env);
|
||||
const command = try setupBash(alloc, "bash", ".", &env);
|
||||
defer if (command) |c| alloc.free(c);
|
||||
|
||||
try testing.expect(std.mem.indexOf(u8, command.?, "--posix") != null);
|
||||
try testing.expect(std.mem.indexOf(u8, env.get("GHOSTTY_BASH_INJECT").?, "posix") == null);
|
||||
try testing.expect(env.get("GHOSTTY_BASH_ENV") == null);
|
||||
try testing.expectEqualStrings("./shell-integration/bash/ghostty.bash", env.get("ENV").?);
|
||||
}
|
||||
}
|
||||
|
||||
/// Setup the fish automatic shell integration. This works by
|
||||
@ -128,16 +441,3 @@ fn setupZsh(
|
||||
);
|
||||
try env.put("ZDOTDIR", integ_dir);
|
||||
}
|
||||
|
||||
test "force shell" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var env = EnvMap.init(alloc);
|
||||
defer env.deinit();
|
||||
|
||||
inline for (@typeInfo(Shell).Enum.fields) |field| {
|
||||
const shell = @field(Shell, field.name);
|
||||
try testing.expectEqual(shell, setup(alloc, ".", "sh", &env, shell, .{}));
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user