mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-10-08 10:56:34 +00:00
termio: shell integration uses arena
This commit is contained in:
@@ -907,12 +907,6 @@ const Subprocess = struct {
|
|||||||
errdefer arena.deinit();
|
errdefer arena.deinit();
|
||||||
const alloc = arena.allocator();
|
const alloc = arena.allocator();
|
||||||
|
|
||||||
// Determine the shell command we're executing
|
|
||||||
var shell_command = opts.full_config.command orelse switch (builtin.os.tag) {
|
|
||||||
.windows => "cmd.exe",
|
|
||||||
else => "sh",
|
|
||||||
};
|
|
||||||
|
|
||||||
// Set our env vars. For Flatpak builds running in Flatpak we don't
|
// Set our env vars. For Flatpak builds running in Flatpak we don't
|
||||||
// inherit our environment because the login shell on the host side
|
// inherit our environment because the login shell on the host side
|
||||||
// will get it.
|
// will get it.
|
||||||
@@ -1020,36 +1014,42 @@ const Subprocess = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Setup our shell integration, if we can.
|
// Setup our shell integration, if we can.
|
||||||
const integrated_shell: ?shell_integration.ShellIntegration = shell: {
|
const integrated_shell: ?shell_integration.Shell, const shell_command: []const u8 = shell: {
|
||||||
|
const default_shell_command = opts.full_config.command orelse switch (builtin.os.tag) {
|
||||||
|
.windows => "cmd.exe",
|
||||||
|
else => "sh",
|
||||||
|
};
|
||||||
|
|
||||||
const force: ?shell_integration.Shell = switch (opts.full_config.@"shell-integration") {
|
const force: ?shell_integration.Shell = switch (opts.full_config.@"shell-integration") {
|
||||||
.none => break :shell null,
|
.none => break :shell .{ null, default_shell_command },
|
||||||
.detect => null,
|
.detect => null,
|
||||||
.bash => .bash,
|
.bash => .bash,
|
||||||
.fish => .fish,
|
.fish => .fish,
|
||||||
.zsh => .zsh,
|
.zsh => .zsh,
|
||||||
};
|
};
|
||||||
|
|
||||||
const dir = opts.resources_dir orelse break :shell null;
|
const dir = opts.resources_dir orelse break :shell .{
|
||||||
|
null,
|
||||||
|
default_shell_command,
|
||||||
|
};
|
||||||
|
|
||||||
break :shell try shell_integration.setup(
|
const integration = try shell_integration.setup(
|
||||||
gpa,
|
alloc,
|
||||||
dir,
|
dir,
|
||||||
shell_command,
|
default_shell_command,
|
||||||
&env,
|
&env,
|
||||||
force,
|
force,
|
||||||
opts.full_config.@"shell-integration-features",
|
opts.full_config.@"shell-integration-features",
|
||||||
);
|
) orelse break :shell .{ null, default_shell_command };
|
||||||
|
|
||||||
|
break :shell .{ integration.shell, integration.command };
|
||||||
};
|
};
|
||||||
defer if (integrated_shell) |shell| shell.deinit(gpa);
|
|
||||||
|
|
||||||
if (integrated_shell) |shell| {
|
if (integrated_shell) |shell| {
|
||||||
log.info(
|
log.info(
|
||||||
"shell integration automatically injected shell={}",
|
"shell integration automatically injected shell={}",
|
||||||
.{shell.shell},
|
.{shell},
|
||||||
);
|
);
|
||||||
if (shell.command) |command| {
|
|
||||||
shell_command = command;
|
|
||||||
}
|
|
||||||
} else if (opts.full_config.@"shell-integration" != .none) {
|
} else if (opts.full_config.@"shell-integration" != .none) {
|
||||||
log.warn("shell could not be detected, no automatic shell integration will be injected", .{});
|
log.warn("shell could not be detected, no automatic shell integration will be injected", .{});
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||||
const EnvMap = std.process.EnvMap;
|
const EnvMap = std.process.EnvMap;
|
||||||
const config = @import("../config.zig");
|
const config = @import("../config.zig");
|
||||||
|
|
||||||
@@ -12,18 +13,17 @@ pub const Shell = enum {
|
|||||||
zsh,
|
zsh,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// The result of setting up a shell integration.
|
||||||
pub const ShellIntegration = struct {
|
pub const ShellIntegration = struct {
|
||||||
/// The successfully-integrated shell.
|
/// The successfully-integrated shell.
|
||||||
shell: Shell,
|
shell: Shell,
|
||||||
|
|
||||||
/// A revised, integration-aware shell command.
|
/// The command to use to start the shell with the integration.
|
||||||
command: ?[]const u8 = null,
|
/// In most cases this is identical to the command given but for
|
||||||
|
/// bash in particular it may be different.
|
||||||
pub fn deinit(self: ShellIntegration, alloc: Allocator) void {
|
///
|
||||||
if (self.command) |command| {
|
/// The memory is allocated in the arena given to setup.
|
||||||
alloc.free(command);
|
command: []const u8,
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Setup the command execution environment for automatic
|
/// Setup the command execution environment for automatic
|
||||||
@@ -32,9 +32,10 @@ pub const ShellIntegration = struct {
|
|||||||
/// (shell type couldn't be detected, etc.), this will return null.
|
/// (shell type couldn't be detected, etc.), this will return null.
|
||||||
///
|
///
|
||||||
/// The allocator is used for temporary values and to allocate values
|
/// The allocator is used for temporary values and to allocate values
|
||||||
/// in the ShellIntegration result.
|
/// in the ShellIntegration result. It is expected to be an arena to
|
||||||
|
/// simplify cleanup.
|
||||||
pub fn setup(
|
pub fn setup(
|
||||||
alloc: Allocator,
|
alloc_arena: Allocator,
|
||||||
resource_dir: []const u8,
|
resource_dir: []const u8,
|
||||||
command: []const u8,
|
command: []const u8,
|
||||||
env: *EnvMap,
|
env: *EnvMap,
|
||||||
@@ -52,22 +53,34 @@ pub fn setup(
|
|||||||
break :exe std.fs.path.basename(command[0..idx]);
|
break :exe std.fs.path.basename(command[0..idx]);
|
||||||
};
|
};
|
||||||
|
|
||||||
var new_command: ?[]const u8 = null;
|
const result: ShellIntegration = shell: {
|
||||||
const shell: Shell = shell: {
|
|
||||||
if (std.mem.eql(u8, "bash", exe)) {
|
if (std.mem.eql(u8, "bash", exe)) {
|
||||||
new_command = try setupBash(alloc, command, resource_dir, env);
|
const new_command = try setupBash(
|
||||||
if (new_command == null) return null;
|
alloc_arena,
|
||||||
break :shell .bash;
|
command,
|
||||||
|
resource_dir,
|
||||||
|
env,
|
||||||
|
) orelse return null;
|
||||||
|
break :shell .{
|
||||||
|
.shell = .bash,
|
||||||
|
.command = new_command,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std.mem.eql(u8, "fish", exe)) {
|
if (std.mem.eql(u8, "fish", exe)) {
|
||||||
try setupFish(alloc, resource_dir, env);
|
try setupFish(alloc_arena, resource_dir, env);
|
||||||
break :shell .fish;
|
break :shell .{
|
||||||
|
.shell = .fish,
|
||||||
|
.command = command,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std.mem.eql(u8, "zsh", exe)) {
|
if (std.mem.eql(u8, "zsh", exe)) {
|
||||||
try setupZsh(resource_dir, env);
|
try setupZsh(resource_dir, env);
|
||||||
break :shell .zsh;
|
break :shell .{
|
||||||
|
.shell = .zsh,
|
||||||
|
.command = command,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -78,15 +91,15 @@ pub fn setup(
|
|||||||
if (!features.sudo) try env.put("GHOSTTY_SHELL_INTEGRATION_NO_SUDO", "1");
|
if (!features.sudo) try env.put("GHOSTTY_SHELL_INTEGRATION_NO_SUDO", "1");
|
||||||
if (!features.title) try env.put("GHOSTTY_SHELL_INTEGRATION_NO_TITLE", "1");
|
if (!features.title) try env.put("GHOSTTY_SHELL_INTEGRATION_NO_TITLE", "1");
|
||||||
|
|
||||||
return .{
|
return result;
|
||||||
.shell = shell,
|
|
||||||
.command = new_command,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test "force shell" {
|
test "force shell" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const alloc = testing.allocator;
|
|
||||||
|
var arena = ArenaAllocator.init(testing.allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
const alloc = arena.allocator();
|
||||||
|
|
||||||
var env = EnvMap.init(alloc);
|
var env = EnvMap.init(alloc);
|
||||||
defer env.deinit();
|
defer env.deinit();
|
||||||
@@ -94,12 +107,7 @@ test "force shell" {
|
|||||||
inline for (@typeInfo(Shell).Enum.fields) |field| {
|
inline for (@typeInfo(Shell).Enum.fields) |field| {
|
||||||
const shell = @field(Shell, field.name);
|
const shell = @field(Shell, field.name);
|
||||||
const result = try setup(alloc, ".", "sh", &env, shell, .{});
|
const result = try setup(alloc, ".", "sh", &env, shell, .{});
|
||||||
|
try testing.expectEqual(shell, result.?.shell);
|
||||||
try testing.expect(result != null);
|
|
||||||
if (result) |r| {
|
|
||||||
try testing.expectEqual(shell, r.shell);
|
|
||||||
r.deinit(alloc);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,7 +384,7 @@ test "bash: preserve ENV" {
|
|||||||
/// Fish will automatically load configuration in XDG_DATA_DIRS
|
/// Fish will automatically load configuration in XDG_DATA_DIRS
|
||||||
/// "fish/vendor_conf.d/*.fish".
|
/// "fish/vendor_conf.d/*.fish".
|
||||||
fn setupFish(
|
fn setupFish(
|
||||||
alloc_gpa: Allocator,
|
alloc_arena: Allocator,
|
||||||
resource_dir: []const u8,
|
resource_dir: []const u8,
|
||||||
env: *EnvMap,
|
env: *EnvMap,
|
||||||
) !void {
|
) !void {
|
||||||
@@ -402,7 +410,7 @@ fn setupFish(
|
|||||||
// 4K is a reasonable size for this for most cases. However, env
|
// 4K is a reasonable size for this for most cases. However, env
|
||||||
// vars can be significantly larger so if we have to we fall
|
// vars can be significantly larger so if we have to we fall
|
||||||
// back to a heap allocated value.
|
// back to a heap allocated value.
|
||||||
var stack_alloc = std.heap.stackFallback(4096, alloc_gpa);
|
var stack_alloc = std.heap.stackFallback(4096, alloc_arena);
|
||||||
const alloc = stack_alloc.get();
|
const alloc = stack_alloc.get();
|
||||||
const prepended = try std.fmt.allocPrint(
|
const prepended = try std.fmt.allocPrint(
|
||||||
alloc,
|
alloc,
|
||||||
|
Reference in New Issue
Block a user