shell-integration: switch to $GHOSTTY_SHELL_FEATURES

This change consolidates all three opt-out shell integration environment
variables into a single opt-in $GHOSTTY_SHELL_FEATURES variable. Its
value is a comma-delimited list of the enabled shell feature names (e.g.
"cursor,title").

$GHOSTTY_SHELL_FEATURES is set at runtime and automatically added to the
shell environment. Its value is based on the shell-integration-features
configuration option.

$GHOSTTY_SHELL_FEATURES is only set when at least one shell feature is
enabled. It won't be set when 'shell-integration-features = false'.

$GHOSTTY_SHELL_FEATURES lists only the enabled shell feature names. We
could have alternatively gone in the opposite direction and listed the
disabled features, letting the scripts assume each feature is on by
default like we did before, but I think this explicit approach is a
little safer and easier to reason about / debug.

It also doesn't support the "no-" negation prefix used by the config
system (e.g. "cursor,no-title"). This simplifies the implementation
requirements of our (multiple) shell integration scripts, and because
$GHOSTTY_SHELL_FEATURES is derived from shell-integration-features,
the user-facing configuration interface retains that expressiveness.

$GHOSTTY_SHELL_FEATURES is intended to primarily be an internal concern:
an interface between the runtime and our shell integration scripts. It
could be used by people with particular use cases who want to manually
source those scripts, but that isn't the intended audience.

... and because the previous $GHOSTTY_SHELL_INTEGRATION_NO_* variables
were also meant to be an internal concern, this change does not include
backwards compatibility support for those names.

One last advantage of a using a single $GHOSTTY_SHELL_FEATURES variable
is that it can be easily forwarded to e.g. ssh sessions or other shell
environments.
This commit is contained in:
Jon Parise
2025-03-22 08:28:56 -04:00
parent 747c43ffa0
commit 314d52ac3a
5 changed files with 36 additions and 35 deletions

View File

@@ -150,9 +150,18 @@ pub fn setupFeatures(
env: *EnvMap,
features: config.ShellIntegrationFeatures,
) !void {
if (!features.cursor) try env.put("GHOSTTY_SHELL_INTEGRATION_NO_CURSOR", "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");
var enabled = try std.BoundedArray(u8, 256).init(0);
inline for (@typeInfo(@TypeOf(features)).@"struct".fields) |f| {
if (@field(features, f.name)) {
if (enabled.len > 0) try enabled.append(',');
try enabled.appendSlice(f.name);
}
}
if (enabled.len > 0) {
try env.put("GHOSTTY_SHELL_FEATURES", enabled.slice());
}
}
test "setup features" {
@@ -162,15 +171,13 @@ test "setup features" {
defer arena.deinit();
const alloc = arena.allocator();
// Test: all features enabled (no environment variables should be set)
// Test: all features enabled
{
var env = EnvMap.init(alloc);
defer env.deinit();
try setupFeatures(&env, .{ .cursor = true, .sudo = true, .title = true });
try testing.expect(env.get("GHOSTTY_SHELL_INTEGRATION_NO_CURSOR") == null);
try testing.expect(env.get("GHOSTTY_SHELL_INTEGRATION_NO_SUDO") == null);
try testing.expect(env.get("GHOSTTY_SHELL_INTEGRATION_NO_TITLE") == null);
try testing.expectEqualStrings("cursor,sudo,title", env.get("GHOSTTY_SHELL_FEATURES").?);
}
// Test: all features disabled
@@ -179,9 +186,7 @@ test "setup features" {
defer env.deinit();
try setupFeatures(&env, .{ .cursor = false, .sudo = false, .title = false });
try testing.expectEqualStrings("1", env.get("GHOSTTY_SHELL_INTEGRATION_NO_CURSOR").?);
try testing.expectEqualStrings("1", env.get("GHOSTTY_SHELL_INTEGRATION_NO_SUDO").?);
try testing.expectEqualStrings("1", env.get("GHOSTTY_SHELL_INTEGRATION_NO_TITLE").?);
try testing.expect(env.get("GHOSTTY_SHELL_FEATURES") == null);
}
// Test: mixed features
@@ -190,9 +195,7 @@ test "setup features" {
defer env.deinit();
try setupFeatures(&env, .{ .cursor = false, .sudo = true, .title = false });
try testing.expectEqualStrings("1", env.get("GHOSTTY_SHELL_INTEGRATION_NO_CURSOR").?);
try testing.expect(env.get("GHOSTTY_SHELL_INTEGRATION_NO_SUDO") == null);
try testing.expectEqualStrings("1", env.get("GHOSTTY_SHELL_INTEGRATION_NO_TITLE").?);
try testing.expectEqualStrings("sudo", env.get("GHOSTTY_SHELL_FEATURES").?);
}
}