aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/termio/shell_integration.zig
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-09-10 21:51:57 +0300
committerGitHub <noreply@github.com>2026-09-10 21:51:57 +0300
commit425c86179791cd32e2d4ce0ae7f269940b4b0663 (patch)
treee52c4383a6d69216cbe2a32638bf90841d7e2189 /vendor/libghostty-vt/src/termio/shell_integration.zig
parent90e947a6e26e02f10ec7f1694c4eb5b78a86406b (diff)
chore: upgrade libghostty and preserve terminal behavior (#3906)
Diffstat (limited to 'vendor/libghostty-vt/src/termio/shell_integration.zig')
-rw-r--r--vendor/libghostty-vt/src/termio/shell_integration.zig110
1 files changed, 68 insertions, 42 deletions
diff --git a/vendor/libghostty-vt/src/termio/shell_integration.zig b/vendor/libghostty-vt/src/termio/shell_integration.zig
index 2dd09ee2..d56a84cc 100644
--- a/vendor/libghostty-vt/src/termio/shell_integration.zig
+++ b/vendor/libghostty-vt/src/termio/shell_integration.zig
@@ -2,10 +2,11 @@ const std = @import("std");
const builtin = @import("builtin");
const Allocator = std.mem.Allocator;
const ArenaAllocator = std.heap.ArenaAllocator;
-const EnvMap = std.process.EnvMap;
+const EnvMap = std.process.Environ.Map;
const config = @import("../config.zig");
const homedir = @import("../os/homedir.zig");
const internal_os = @import("../os/main.zig");
+const global = @import("../global.zig");
const log = std.log.scoped(.shell_integration);
@@ -97,7 +98,7 @@ test "force shell" {
inline for (@typeInfo(Shell).@"enum".fields) |field| {
const shell = @field(Shell, field.name);
- var res: TmpResourcesDir = try .init(alloc, shell);
+ var res: TmpResourcesDir = try .init(shell);
defer res.deinit();
const result = try setup(
@@ -373,12 +374,12 @@ fn setupBash(
"{s}/shell-integration/bash/ghostty.bash",
.{resource_dir},
);
- if (std.fs.openFileAbsolute(script_path, .{})) |file| {
- file.close();
+ if (std.Io.Dir.openFileAbsolute(global.io(), script_path, .{})) |file| {
+ file.close(global.io());
try env.put("ENV", script_path);
} else |err| {
log.warn("unable to open {s}: {}", .{ script_path, err });
- env.remove("GHOSTTY_BASH_ENV");
+ _ = env.swapRemove("GHOSTTY_BASH_ENV");
return null;
}
@@ -390,8 +391,10 @@ fn setupBash(
// In POSIX mode, HISTFILE defaults to ~/.sh_history, so unless we're
// staying in POSIX mode (--posix), change it back to ~/.bash_history.
if (env.get("HISTFILE") == null) {
+ var environ_map = try global.environMap();
+ defer environ_map.deinit();
var home_buf: [1024]u8 = undefined;
- if (try homedir.home(&home_buf)) |home| {
+ if (try homedir.home(global.io(), &environ_map, &home_buf)) |home| {
var histfile_buf: [std.fs.max_path_bytes]u8 = undefined;
const histfile = try std.fmt.bufPrint(
&histfile_buf,
@@ -404,7 +407,7 @@ fn setupBash(
}
// Return a copy of our modified command line to use as the shell command.
- return .{ .shell = try alloc.dupeZ(u8, try cmd.toOwnedSlice()) };
+ return .{ .shell = try alloc.dupeZ(u8, cmd.buffer.written()) };
}
test "bash" {
@@ -413,7 +416,7 @@ test "bash" {
defer arena.deinit();
const alloc = arena.allocator();
- var res: TmpResourcesDir = try .init(alloc, .bash);
+ var res: TmpResourcesDir = try .init(.bash);
defer res.deinit();
var env = EnvMap.init(alloc);
@@ -436,7 +439,7 @@ test "bash: unsupported options" {
defer arena.deinit();
const alloc = arena.allocator();
- var res: TmpResourcesDir = try .init(alloc, .bash);
+ var res: TmpResourcesDir = try .init(.bash);
defer res.deinit();
const cmdlines = [_][:0]const u8{
@@ -462,7 +465,7 @@ test "bash: inject flags" {
defer arena.deinit();
const alloc = arena.allocator();
- var res: TmpResourcesDir = try .init(alloc, .bash);
+ var res: TmpResourcesDir = try .init(.bash);
defer res.deinit();
// bash --norc
@@ -492,7 +495,7 @@ test "bash: rcfile" {
defer arena.deinit();
const alloc = arena.allocator();
- var res: TmpResourcesDir = try .init(alloc, .bash);
+ var res: TmpResourcesDir = try .init(.bash);
defer res.deinit();
var env = EnvMap.init(alloc);
@@ -519,7 +522,7 @@ test "bash: HISTFILE" {
defer arena.deinit();
const alloc = arena.allocator();
- var res: TmpResourcesDir = try .init(alloc, .bash);
+ var res: TmpResourcesDir = try .init(.bash);
defer res.deinit();
// HISTFILE unset
@@ -551,7 +554,7 @@ test "bash: ENV" {
defer arena.deinit();
const alloc = arena.allocator();
- var res: TmpResourcesDir = try .init(alloc, .bash);
+ var res: TmpResourcesDir = try .init(.bash);
defer res.deinit();
var env = EnvMap.init(alloc);
@@ -575,7 +578,7 @@ test "bash: additional arguments" {
defer arena.deinit();
const alloc = arena.allocator();
- var res: TmpResourcesDir = try .init(alloc, .bash);
+ var res: TmpResourcesDir = try .init(.bash);
defer res.deinit();
var env = EnvMap.init(alloc);
@@ -603,7 +606,7 @@ test "bash: missing resources" {
var tmp_dir = testing.tmpDir(.{});
defer tmp_dir.cleanup();
- const resources_dir = try tmp_dir.dir.realpathAlloc(alloc, ".");
+ const resources_dir = try tmp_dir.dir.realPathFileAlloc(testing.io, ".", alloc);
defer alloc.free(resources_dir);
var env = EnvMap.init(alloc);
@@ -633,11 +636,15 @@ fn setupXdgDataDirs(
"{s}/shell-integration",
.{resource_dir},
);
- var integ_dir = std.fs.openDirAbsolute(integ_path, .{}) catch |err| {
+ var integ_dir = std.Io.Dir.openDirAbsolute(
+ global.io(),
+ integ_path,
+ .{},
+ ) catch |err| {
log.warn("unable to open {s}: {}", .{ integ_path, err });
return false;
};
- integ_dir.close();
+ integ_dir.close(global.io());
// Set an env var so we can remove this from XDG_DATA_DIRS later.
// This happens in the shell integration config itself. We do this
@@ -659,7 +666,7 @@ fn setupXdgDataDirs(
const xdg_data_dirs_key = "XDG_DATA_DIRS";
try env.put(
xdg_data_dirs_key,
- try internal_os.prependEnv(
+ try prependEnv(
stack_alloc,
env.get(xdg_data_dirs_key) orelse "/usr/local/share:/usr/share",
integ_path,
@@ -669,6 +676,23 @@ fn setupXdgDataDirs(
return true;
}
+/// Prepend a value to an environment variable such as PATH.
+/// The returned value is always allocated so it must be freed.
+fn prependEnv(
+ alloc: Allocator,
+ current: []const u8,
+ value: []const u8,
+) Allocator.Error![]u8 {
+ // If there is no prior value, we return it as-is
+ if (current.len == 0) return try alloc.dupe(u8, value);
+
+ return try std.fmt.allocPrint(alloc, "{s}{c}{s}", .{
+ value,
+ std.fs.path.delimiter,
+ current,
+ });
+}
+
test "xdg: empty XDG_DATA_DIRS" {
if (builtin.os.tag == .windows) return error.SkipZigTest;
@@ -678,7 +702,7 @@ test "xdg: empty XDG_DATA_DIRS" {
defer arena.deinit();
const alloc = arena.allocator();
- var res: TmpResourcesDir = try .init(alloc, .fish);
+ var res: TmpResourcesDir = try .init(.fish);
defer res.deinit();
var env = EnvMap.init(alloc);
@@ -706,7 +730,7 @@ test "xdg: existing XDG_DATA_DIRS" {
defer arena.deinit();
const alloc = arena.allocator();
- var res: TmpResourcesDir = try .init(alloc, .fish);
+ var res: TmpResourcesDir = try .init(.fish);
defer res.deinit();
var env = EnvMap.init(alloc);
@@ -736,7 +760,7 @@ test "xdg: missing resources" {
var tmp_dir = testing.tmpDir(.{});
defer tmp_dir.cleanup();
- const resources_dir = try tmp_dir.dir.realpathAlloc(alloc, ".");
+ const resources_dir = try tmp_dir.dir.realPathFileAlloc(testing.io, ".", alloc);
defer alloc.free(resources_dir);
var env = EnvMap.init(alloc);
@@ -815,7 +839,7 @@ fn setupNushell(
}
// Return a copy of our modified command line to use as the shell command.
- return .{ .shell = try alloc.dupeZ(u8, try cmd.toOwnedSlice()) };
+ return .{ .shell = try alloc.dupeZ(u8, cmd.buffer.written()) };
}
test "nushell" {
@@ -824,7 +848,7 @@ test "nushell" {
defer arena.deinit();
const alloc = arena.allocator();
- var res: TmpResourcesDir = try .init(alloc, .nushell);
+ var res: TmpResourcesDir = try .init(.nushell);
defer res.deinit();
var env = EnvMap.init(alloc);
@@ -850,7 +874,7 @@ test "nushell: unsupported options" {
defer arena.deinit();
const alloc = arena.allocator();
- var res: TmpResourcesDir = try .init(alloc, .nushell);
+ var res: TmpResourcesDir = try .init(.nushell);
defer res.deinit();
const cmdlines = [_][:0]const u8{
@@ -879,7 +903,7 @@ test "nushell: missing resources" {
var tmp_dir = testing.tmpDir(.{});
defer tmp_dir.cleanup();
- const resources_dir = try tmp_dir.dir.realpathAlloc(alloc, ".");
+ const resources_dir = try tmp_dir.dir.realPathFileAlloc(testing.io, ".", alloc);
defer alloc.free(resources_dir);
var env = EnvMap.init(alloc);
@@ -910,11 +934,15 @@ fn setupZsh(
"{s}/shell-integration/zsh",
.{resource_dir},
);
- var integ_dir = std.fs.openDirAbsolute(integ_path, .{}) catch |err| {
+ var integ_dir = std.Io.Dir.openDirAbsolute(
+ global.io(),
+ integ_path,
+ .{},
+ ) catch |err| {
log.warn("unable to open {s}: {}", .{ integ_path, err });
return null;
};
- integ_dir.close();
+ integ_dir.close(global.io());
try env.put("ZDOTDIR", integ_path);
return try command.clone(alloc);
@@ -927,7 +955,7 @@ test "zsh" {
defer arena.deinit();
const alloc = arena.allocator();
- var res: TmpResourcesDir = try .init(testing.allocator, .zsh);
+ var res: TmpResourcesDir = try .init(.zsh);
defer res.deinit();
var env = EnvMap.init(testing.allocator);
@@ -946,7 +974,7 @@ test "zsh: ZDOTDIR" {
defer arena.deinit();
const alloc = arena.allocator();
- var res: TmpResourcesDir = try .init(testing.allocator, .zsh);
+ var res: TmpResourcesDir = try .init(.zsh);
defer res.deinit();
var env = EnvMap.init(testing.allocator);
@@ -969,7 +997,7 @@ test "zsh: missing resources" {
var tmp_dir = testing.tmpDir(.{});
defer tmp_dir.cleanup();
- const resources_dir = try tmp_dir.dir.realpathAlloc(alloc, ".");
+ const resources_dir = try tmp_dir.dir.realPathFileAlloc(testing.io, ".", alloc);
defer alloc.free(resources_dir);
var env = EnvMap.init(alloc);
@@ -981,12 +1009,11 @@ test "zsh: missing resources" {
/// Test helper that creates a temporary resources directory with shell integration paths.
const TmpResourcesDir = struct {
- allocator: Allocator,
tmp_dir: std.testing.TmpDir,
- path: []const u8,
+ path: [:0]const u8,
shell_path: []const u8,
- fn init(allocator: Allocator, shell: Shell) !TmpResourcesDir {
+ fn init(shell: Shell) !TmpResourcesDir {
var tmp_dir = std.testing.tmpDir(.{});
errdefer tmp_dir.cleanup();
@@ -996,20 +1023,20 @@ const TmpResourcesDir = struct {
"shell-integration/{s}",
.{@tagName(shell)},
);
- try tmp_dir.dir.makePath(relative_shell_path);
+ try tmp_dir.dir.createDirPath(std.testing.io, relative_shell_path);
- const path = try tmp_dir.dir.realpathAlloc(allocator, ".");
- errdefer allocator.free(path);
+ const path = try tmp_dir.dir.realPathFileAlloc(std.testing.io, ".", std.testing.allocator);
+ errdefer std.testing.allocator.free(path);
const shell_path = try std.fmt.allocPrint(
- allocator,
+ std.testing.allocator,
"{s}/{s}",
.{ path, relative_shell_path },
);
- errdefer allocator.free(shell_path);
+ errdefer std.testing.allocator.free(shell_path);
switch (shell) {
- .bash => try tmp_dir.dir.writeFile(.{
+ .bash => try tmp_dir.dir.writeFile(std.testing.io, .{
.sub_path = "shell-integration/bash/ghostty.bash",
.data = "",
}),
@@ -1017,7 +1044,6 @@ const TmpResourcesDir = struct {
}
return .{
- .allocator = allocator,
.tmp_dir = tmp_dir,
.path = path,
.shell_path = shell_path,
@@ -1025,8 +1051,8 @@ const TmpResourcesDir = struct {
}
fn deinit(self: *TmpResourcesDir) void {
- self.allocator.free(self.shell_path);
- self.allocator.free(self.path);
+ std.testing.allocator.free(self.shell_path);
+ std.testing.allocator.free(self.path);
self.tmp_dir.cleanup();
}
};