From 1fa830cc739c593030f8de31f75aad79053ae6e9 Mon Sep 17 00:00:00 2001 From: ilk Date: Thu, 16 May 2024 23:59:02 +0300 Subject: [PATCH 1/5] feat(shell-integration): add automatic integration for Elvish Fish automatic integration taken as an example. Just like fish, Elvish checks `XDG_DATA_DIRS` for its modules. Thus, Fish integration in zig is reused, and integration in Elvish now removes `GHOSTTY_FISH_XDG_DIR` environment variable on launch. --- src/config/Config.zig | 1 + src/shell-integration/README.md | 14 +++++++++++- .../elvish/lib/ghostty-integration.elv | 7 +++--- src/termio/shell_integration.zig | 22 +++++++++++++++++++ 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/config/Config.zig b/src/config/Config.zig index 2d540e895..b6400ff9f 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -3414,6 +3414,7 @@ pub const ShellIntegration = enum { bash, fish, zsh, + elvish, }; /// Shell integration features diff --git a/src/shell-integration/README.md b/src/shell-integration/README.md index c27b45891..2ad83169a 100644 --- a/src/shell-integration/README.md +++ b/src/shell-integration/README.md @@ -19,9 +19,20 @@ its normal startup files, which becomes our script's responsibility (along with disabling POSIX mode). Bash shell integration can also be sourced manually from `bash/ghostty.bash`. - ### Elvish +For [Elvish](https://elv.sh), `$GHOSTTY_RESOURCES_DIR/src/shell-integration` +contains an `./elvish/lib/ghostty-integration.elv` file. + +Elvish, on startup, searches for paths defined in `XDG_DATA_DIRS` +variable for `./elvish/lib/*.elv` files and imports them. They are thus +made available for use as modules by way of `use `. + +Ghostty launches Elvish, passing the environment with `XDG_DATA_DIRS`prepended +with `$GHOSTTY_RESOURCES_DIR/src/shell-integration`. It contains +`./elvish/lib/ghostty-integration.elv`. The user can then import it +by `use ghostty-integration`, which will run the integration routines. + The [Elvish](https://elv.sh) shell integration is supported by the community and is not officially supported by Ghostty. We distribute it for ease of access and use but do not provide support for it. @@ -43,3 +54,4 @@ For `zsh`, Ghostty sets `ZDOTDIR` so that it loads our configuration from the `zsh` directory. The existing `ZDOTDIR` is retained so that after loading the Ghostty shell integration the normal Zsh loading sequence occurs. + diff --git a/src/shell-integration/elvish/lib/ghostty-integration.elv b/src/shell-integration/elvish/lib/ghostty-integration.elv index 6af878ba5..9e7f0bd5f 100644 --- a/src/shell-integration/elvish/lib/ghostty-integration.elv +++ b/src/shell-integration/elvish/lib/ghostty-integration.elv @@ -1,6 +1,6 @@ { fn restore-xdg-dirs { - var integration-dir = $E:GHOSTTY_FISH_XDG_DIR + var integration-dir = $E:GHOSTTY_INTEGRATION_DIR var xdg-dirs = [(str:split ':' $E:XDG_DATA_DIRS)] var len = (count $xdg-dirs) @@ -27,9 +27,9 @@ } else { set-env XDG_DATA_DIRS (str:join ':' $xdg-dirs) } - unset-env GHOSTTY_FISH_XDG_DIR + unset-env GHOSTTY_INTEGRATION_DIR } - if (and (has-env GHOSTTY_FISH_XDG_DIR) (has-env XDG_DATA_DIRS)) { + if (and (has-env GHOSTTY_INTEGRATION_DIR) (has-env XDG_DATA_DIRS)) { restore-xdg-dirs } } @@ -117,4 +117,3 @@ edit:add-var sudo~ $sudo-with-terminfo~ } } - diff --git a/src/termio/shell_integration.zig b/src/termio/shell_integration.zig index fb57595f0..e05c57a0f 100644 --- a/src/termio/shell_integration.zig +++ b/src/termio/shell_integration.zig @@ -12,6 +12,7 @@ pub const Shell = enum { bash, fish, zsh, + elvish, }; /// The result of setting up a shell integration. @@ -47,6 +48,7 @@ pub fn setup( .bash => "bash", .fish => "fish", .zsh => "zsh", + .elvish => "elvish", } else exe: { // The command can include arguments. Look for the first space // and use the basename of the first part as the command's exe. @@ -76,6 +78,14 @@ pub fn setup( }; } + if (std.mem.eql(u8, "elvish", exe)) { + try setupElvish(alloc_arena, resource_dir, env); + break :shell .{ + .shell = .elvish, + .command = command, + }; + } + if (std.mem.eql(u8, "zsh", exe)) { try setupZsh(resource_dir, env); break :shell .{ @@ -452,6 +462,18 @@ fn setupFish( } } +/// Setup the Elvish automatic shell integration. +/// This reuses integration primitives of Fish, as Elvish also +/// loads config in XDG_DATA_DIRS (except it imports +/// "./elvish/lib/*.elv" files). +fn setupElvish( + alloc_arena: Allocator, + resource_dir: []const u8, + env: *EnvMap, +) !void { + try setupFish(alloc_arena, resource_dir, env); +} + /// Setup the zsh automatic shell integration. This works by setting /// ZDOTDIR to our resources dir so that zsh will load our config. This /// config then loads the true user config. From 7377ca89172ce2d464dfb142f3519b85471bf769 Mon Sep 17 00:00:00 2001 From: ilk Date: Fri, 17 May 2024 18:02:03 +0300 Subject: [PATCH 2/5] refactor(shell-integration): refactor to make cases alphabetical also refactors elvish file to evade unobvious returns and tries to fix some build errors --- src/config/Config.zig | 4 +-- src/termio/Exec.zig | 1 + src/termio/shell_integration.zig | 43 +++++++++++++------------------- 3 files changed, 20 insertions(+), 28 deletions(-) diff --git a/src/config/Config.zig b/src/config/Config.zig index b6400ff9f..eb2cf0915 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -847,7 +847,7 @@ keybind: Keybinds = .{}, /// /// * `detect` - Detect the shell based on the filename. /// -/// * `bash`, `fish`, `zsh` - Use this specific shell injection scheme. +/// * `bash`, `elvish`, `fish`, `zsh` - Use this specific shell injection scheme. /// /// The default value is `detect`. @"shell-integration": ShellIntegration = .detect, @@ -3412,9 +3412,9 @@ pub const ShellIntegration = enum { none, detect, bash, + elvish, fish, zsh, - elvish, }; /// Shell integration features diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index 9063ca67f..1287600eb 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -1024,6 +1024,7 @@ const Subprocess = struct { .none => break :shell .{ null, default_shell_command }, .detect => null, .bash => .bash, + .elvish => .elvish, .fish => .fish, .zsh => .zsh, }; diff --git a/src/termio/shell_integration.zig b/src/termio/shell_integration.zig index e05c57a0f..aed7ae49f 100644 --- a/src/termio/shell_integration.zig +++ b/src/termio/shell_integration.zig @@ -10,9 +10,9 @@ const log = std.log.scoped(.shell_integration); /// Shell types we support pub const Shell = enum { bash, + elvish, fish, zsh, - elvish, }; /// The result of setting up a shell integration. @@ -46,9 +46,9 @@ pub fn setup( ) !?ShellIntegration { const exe = if (force_shell) |shell| switch (shell) { .bash => "bash", + .elvish => "elvish", .fish => "fish", .zsh => "zsh", - .elvish => "elvish", } else exe: { // The command can include arguments. Look for the first space // and use the basename of the first part as the command's exe. @@ -70,18 +70,18 @@ pub fn setup( }; } - if (std.mem.eql(u8, "fish", exe)) { - try setupFish(alloc_arena, resource_dir, env); + if (std.mem.eql(u8, "elvish", exe)) { + try setupXdgDataDirs(alloc_arena, resource_dir, env); break :shell .{ - .shell = .fish, + .shell = .elvish, .command = command, }; } - if (std.mem.eql(u8, "elvish", exe)) { - try setupElvish(alloc_arena, resource_dir, env); + if (std.mem.eql(u8, "fish", exe)) { + try setupXdgDataDirs(alloc_arena, resource_dir, env); break :shell .{ - .shell = .elvish, + .shell = .fish, .command = command, }; } @@ -415,11 +415,14 @@ test "bash: preserve ENV" { } } -/// Setup the fish automatic shell integration. This works by -/// modify XDG_DATA_DIRS to include the resource directory. -/// Fish will automatically load configuration in XDG_DATA_DIRS -/// "fish/vendor_conf.d/*.fish". -fn setupFish( +/// Setup automatic shell integration for shells that include +/// their modules from paths in `XDG_DATA_DIRS` env variable. +/// +/// Path of shell-integration dir is prepended to `XDG_DATA_DIRS`. +/// It is also saved in `GHOSTTY_INTEGRATION_DIR` variable so that +/// the shell can refer to it and safely remove this directory +/// from `XDG_DATA_DIRS` when integration is complete. +fn setupXdgDataDirs( alloc_arena: Allocator, resource_dir: []const u8, env: *EnvMap, @@ -436,7 +439,7 @@ fn setupFish( // 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 // so that our modifications don't interfere with other commands. - try env.put("GHOSTTY_FISH_XDG_DIR", integ_dir); + try env.put("GHOSTTY_INTEGRATION_DIR", integ_dir); if (env.get("XDG_DATA_DIRS")) |old| { // We have an old value, We need to prepend our value to it. @@ -462,18 +465,6 @@ fn setupFish( } } -/// Setup the Elvish automatic shell integration. -/// This reuses integration primitives of Fish, as Elvish also -/// loads config in XDG_DATA_DIRS (except it imports -/// "./elvish/lib/*.elv" files). -fn setupElvish( - alloc_arena: Allocator, - resource_dir: []const u8, - env: *EnvMap, -) !void { - try setupFish(alloc_arena, resource_dir, env); -} - /// Setup the zsh automatic shell integration. This works by setting /// ZDOTDIR to our resources dir so that zsh will load our config. This /// config then loads the true user config. From 2863d2c148e1e5d8b6a48a90696d9ae489f4ef57 Mon Sep 17 00:00:00 2001 From: ilk Date: Fri, 17 May 2024 18:02:03 +0300 Subject: [PATCH 3/5] refactor(shell-integration): refactor to make cases alphabetical also fix: build errors also fix: name mismatch (GHOSTTY_FISH_XDG_DIR -> GHOSTTY_INTEGRATION_DIR) also refactor elvish file to evade unobvious returns --- .../fish/vendor_conf.d/ghostty-shell-integration.fish | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) mode change 100755 => 100644 src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish diff --git a/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish b/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish old mode 100755 new mode 100644 index fb3088865..2716be110 --- a/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish +++ b/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish @@ -6,7 +6,7 @@ function ghostty_restore_xdg_data_dir -d "restore the original XDG_DATA_DIR value" # If we don't have our own data dir then we don't need to do anything. - if not set -q GHOSTTY_FISH_XDG_DIR + if not set -q GHOSTTY_INTEGRATION_DIR return end @@ -19,7 +19,7 @@ function ghostty_restore_xdg_data_dir -d "restore the original XDG_DATA_DIR valu set --function --path xdg_data_dirs "$XDG_DATA_DIRS" # If our data dir is in the list then remove it. - if set --function index (contains --index "$GHOSTTY_FISH_XDG_DIR" $xdg_data_dirs) + if set --function index (contains --index "$GHOSTTY_INTEGRATION_DIR" $xdg_data_dirs) set --erase --function xdg_data_dirs[$index] end @@ -30,7 +30,7 @@ function ghostty_restore_xdg_data_dir -d "restore the original XDG_DATA_DIR valu set --erase --global XDG_DATA_DIRS end - set --erase GHOSTTY_FISH_XDG_DIR + set --erase GHOSTTY_INTEGRATION_DIR end function ghostty_exit -d "exit the shell integration setup" From 982e4c9570892cc45a2b42ef0d14ab1192aaf6a7 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 27 May 2024 16:16:43 -0700 Subject: [PATCH 4/5] prettier --- src/shell-integration/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shell-integration/README.md b/src/shell-integration/README.md index 2ad83169a..4a8579f8a 100644 --- a/src/shell-integration/README.md +++ b/src/shell-integration/README.md @@ -19,6 +19,7 @@ its normal startup files, which becomes our script's responsibility (along with disabling POSIX mode). Bash shell integration can also be sourced manually from `bash/ghostty.bash`. + ### Elvish For [Elvish](https://elv.sh), `$GHOSTTY_RESOURCES_DIR/src/shell-integration` @@ -54,4 +55,3 @@ For `zsh`, Ghostty sets `ZDOTDIR` so that it loads our configuration from the `zsh` directory. The existing `ZDOTDIR` is retained so that after loading the Ghostty shell integration the normal Zsh loading sequence occurs. - From 66a9b1b99f833bccf9ea9de44203fa24ffdbbeab Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 27 May 2024 16:18:51 -0700 Subject: [PATCH 5/5] rename env var --- src/shell-integration/elvish/lib/ghostty-integration.elv | 6 +++--- .../fish/vendor_conf.d/ghostty-shell-integration.fish | 6 +++--- src/termio/shell_integration.zig | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/shell-integration/elvish/lib/ghostty-integration.elv b/src/shell-integration/elvish/lib/ghostty-integration.elv index 9e7f0bd5f..bd86b5e2a 100644 --- a/src/shell-integration/elvish/lib/ghostty-integration.elv +++ b/src/shell-integration/elvish/lib/ghostty-integration.elv @@ -1,6 +1,6 @@ { fn restore-xdg-dirs { - var integration-dir = $E:GHOSTTY_INTEGRATION_DIR + var integration-dir = $E:GHOSTTY_SHELL_INTEGRATION_XDG_DIR var xdg-dirs = [(str:split ':' $E:XDG_DATA_DIRS)] var len = (count $xdg-dirs) @@ -27,9 +27,9 @@ } else { set-env XDG_DATA_DIRS (str:join ':' $xdg-dirs) } - unset-env GHOSTTY_INTEGRATION_DIR + unset-env GHOSTTY_SHELL_INTEGRATION_XDG_DIR } - if (and (has-env GHOSTTY_INTEGRATION_DIR) (has-env XDG_DATA_DIRS)) { + if (and (has-env GHOSTTY_SHELL_INTEGRATION_XDG_DIR) (has-env XDG_DATA_DIRS)) { restore-xdg-dirs } } diff --git a/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish b/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish index 2716be110..6ccec3933 100644 --- a/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish +++ b/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish @@ -6,7 +6,7 @@ function ghostty_restore_xdg_data_dir -d "restore the original XDG_DATA_DIR value" # If we don't have our own data dir then we don't need to do anything. - if not set -q GHOSTTY_INTEGRATION_DIR + if not set -q GHOSTTY_SHELL_INTEGRATION_XDG_DIR return end @@ -19,7 +19,7 @@ function ghostty_restore_xdg_data_dir -d "restore the original XDG_DATA_DIR valu set --function --path xdg_data_dirs "$XDG_DATA_DIRS" # If our data dir is in the list then remove it. - if set --function index (contains --index "$GHOSTTY_INTEGRATION_DIR" $xdg_data_dirs) + if set --function index (contains --index "$GHOSTTY_SHELL_INTEGRATION_XDG_DIR" $xdg_data_dirs) set --erase --function xdg_data_dirs[$index] end @@ -30,7 +30,7 @@ function ghostty_restore_xdg_data_dir -d "restore the original XDG_DATA_DIR valu set --erase --global XDG_DATA_DIRS end - set --erase GHOSTTY_INTEGRATION_DIR + set --erase GHOSTTY_SHELL_INTEGRATION_XDG_DIR end function ghostty_exit -d "exit the shell integration setup" diff --git a/src/termio/shell_integration.zig b/src/termio/shell_integration.zig index aed7ae49f..392fb5b48 100644 --- a/src/termio/shell_integration.zig +++ b/src/termio/shell_integration.zig @@ -419,8 +419,8 @@ test "bash: preserve ENV" { /// their modules from paths in `XDG_DATA_DIRS` env variable. /// /// Path of shell-integration dir is prepended to `XDG_DATA_DIRS`. -/// It is also saved in `GHOSTTY_INTEGRATION_DIR` variable so that -/// the shell can refer to it and safely remove this directory +/// It is also saved in `GHOSTTY_SHELL_INTEGRATION_XDG_DIR` variable +/// so that the shell can refer to it and safely remove this directory /// from `XDG_DATA_DIRS` when integration is complete. fn setupXdgDataDirs( alloc_arena: Allocator, @@ -439,7 +439,7 @@ fn setupXdgDataDirs( // 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 // so that our modifications don't interfere with other commands. - try env.put("GHOSTTY_INTEGRATION_DIR", integ_dir); + try env.put("GHOSTTY_SHELL_INTEGRATION_XDG_DIR", integ_dir); if (env.get("XDG_DATA_DIRS")) |old| { // We have an old value, We need to prepend our value to it.