mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-14 19:45:49 +00:00
Provide shell ssh integration for nushell
This commit is contained in:
@@ -88,3 +88,16 @@ if [[ -n $GHOSTTY_RESOURCES_DIR ]]; then
|
||||
source "$GHOSTTY_RESOURCES_DIR"/shell-integration/zsh/ghostty-integration
|
||||
fi
|
||||
```
|
||||
|
||||
### Nushell
|
||||
|
||||
For `nushell` Ghostty prepends to the `XDG_DATA_DIRS` directory. Nushell automatically
|
||||
loads configuration files in `<XDG_DATA_DIRS>/nushell/vendor/autoload/*.nu` on startup. These
|
||||
directories are represented in `Nu` by `$nu.vendor-autoload-dirs`. For more details see
|
||||
|
||||
[Nushell documentation](https://www.nushell.sh/book/configuration.html#configuration-overview)
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> Ghostty only prepends to `XDG_DATA_DIRS` in the case where the `ssh-*` features are enabled.
|
||||
> There is no shell integration for the other currently supported features.
|
||||
|
||||
114
src/shell-integration/nushell/vendor/autoload/ghostty-integration.nu
vendored
Normal file
114
src/shell-integration/nushell/vendor/autoload/ghostty-integration.nu
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
# Enables SSH environment variable compatibility.
|
||||
# Converts TERM from xterm-ghostty to xterm-256color
|
||||
# and propagates COLORTERM, TERM_PROGRAM, and TERM_PROGRAM_VERSION
|
||||
# check your sshd_config on remote host to see if these variables are accepted
|
||||
def set_ssh_env []: nothing -> record<ssh_term: string, ssh_opts: list<string>> {
|
||||
return {ssh_term: "xterm-256color", ssh_opts: ["-o", "SetEnv COLORTERM=truecolor", "-o", "SendEnv TERM_PROGRAM TERM_PROGRAM_VERSION"]}
|
||||
}
|
||||
|
||||
# Enables automatic terminfo installation on remote hosts.
|
||||
# Attempts to install Ghostty's terminfo entry using infocmp and tic when
|
||||
# connecting to hosts that lack it.
|
||||
# Requires infocmp to be available locally and tic to be available on remote hosts.
|
||||
# Caches installations to avoid repeat installations.
|
||||
def set_ssh_terminfo [ssh_opts: list<string>, ssh_args: list<string>] {
|
||||
mut ssh_opts = $ssh_opts
|
||||
let ssh_cfg = (
|
||||
run-external "ssh" "-G" ...($ssh_args)
|
||||
| lines
|
||||
| each {|e| if $e =~ '\buser\b' or $e =~ '\bhostname\b' {split row ' '}}
|
||||
)
|
||||
let ssh_user = $ssh_cfg.0.1
|
||||
let ssh_hostname = $ssh_cfg.1.1
|
||||
let ssh_id = $"($ssh_user)@($ssh_hostname)"
|
||||
let ghostty_bin = $env.GHOSTTY_BIN_DIR + "/ghostty"
|
||||
let check_cache_cmd = [$ghostty_bin, "+ssh-cache", $"--host=($ssh_id)"]
|
||||
|
||||
let is_cached = (
|
||||
run-external ...$check_cache_cmd
|
||||
| complete
|
||||
| get exit_code
|
||||
| $in == 0
|
||||
)
|
||||
|
||||
if not $is_cached {
|
||||
let ssh_opts_copy = $ssh_opts
|
||||
let terminfo_data = try {infocmp -0 -x xterm-ghostty} catch {
|
||||
print "Warning: Could not generate terminfo data."
|
||||
return {ssh_term: "xterm-256color", ssh_opts: $ssh_opts_copy}
|
||||
}
|
||||
|
||||
print $"Setting up xterm-ghostty terminfo on ($ssh_hostname)..."
|
||||
|
||||
let tmp_dir = $"/tmp/ghostty-ssh-($ssh_user).XXXXXX"
|
||||
let mktemp_cmd = ["mktemp", "-d", $tmp_dir]
|
||||
let ctrl_dir = try {
|
||||
run-external ...$mktemp_cmd
|
||||
| str trim
|
||||
} catch {
|
||||
$"/tmp/ghostty-ssh-($ssh_user).($nu.pid)"
|
||||
}
|
||||
let ctrl_path = $"($ctrl_dir)/socket"
|
||||
|
||||
let master_parts = $ssh_opts ++ ["-o", "ControlMaster=yes", "-o", $"ControlPath=($ctrl_path)", "-o", "ControlPersist=60s"] ++ $ssh_args
|
||||
|
||||
let infocmp_cmd = ["ssh"] ++ $master_parts ++ ["infocmp", "xterm-ghostty"]
|
||||
|
||||
let terminfo_present = (
|
||||
run-external ...$infocmp_cmd
|
||||
| complete
|
||||
| get exit_code
|
||||
| $in == 0
|
||||
)
|
||||
|
||||
if (not $terminfo_present) {
|
||||
let install_terminfo_cmd = ["ssh"] ++ $master_parts ++ ["mkdir", "-p", "~/.terminfo", "&&", "tic", "-x", "-"]
|
||||
|
||||
($terminfo_data | run-external ...$install_terminfo_cmd) | complete | get exit_code | if $in != 0 {
|
||||
print "Warning: Failed to install terminfo."
|
||||
return {ssh_term: "xterm-256color", ssh_opts: $ssh_opts}
|
||||
}
|
||||
let state_dir = try { $env.XDG_STATE_HOME } catch { $env.HOME | path join ".local/state" }
|
||||
let ghostty_state_dir = $state_dir | path join "ghostty"
|
||||
|
||||
let cache_add_cmd = [$ghostty_bin, "+ssh-cache", $"--add=($ssh_id)"]
|
||||
# Bug?: If I dont add TMPDIR, it complains about renameacrossmountpoints
|
||||
with-env { TMPDIR: $ghostty_state_dir } {
|
||||
run-external ...$cache_add_cmd o+e>| ignore
|
||||
}
|
||||
}
|
||||
$ssh_opts ++= ["-o", $"ControlPath=($ctrl_path)"]
|
||||
}
|
||||
|
||||
return {ssh_term: "xterm-ghostty", ssh_opts: $ssh_opts}
|
||||
}
|
||||
|
||||
# SSH Integration
|
||||
export def --wrapped ssh [...ssh_args: string] {
|
||||
if ($ssh_args | is-empty) {
|
||||
run-external "ssh"
|
||||
}
|
||||
mut session = {ssh_term: "", ssh_opts: []}
|
||||
let shell_features = $env.GHOSTTY_SHELL_FEATURES | split row ','
|
||||
|
||||
if "ssh-env" in $shell_features {
|
||||
$session = set_ssh_env
|
||||
}
|
||||
if "ssh-terminfo" in $shell_features {
|
||||
$session = set_ssh_terminfo $session.ssh_opts $ssh_args
|
||||
}
|
||||
|
||||
let ssh_parts = $session.ssh_opts ++ $ssh_args
|
||||
with-env { TERM: $session.ssh_term } {
|
||||
run-external "ssh" ...$ssh_parts
|
||||
}
|
||||
}
|
||||
|
||||
# Removes Ghostty's data directory from XDG_DATA_DIRS
|
||||
let ghostty_data_dir = $env.GHOSTTY_SHELL_INTEGRATION_XDG_DIR
|
||||
$env.XDG_DATA_DIRS = $env.XDG_DATA_DIRS
|
||||
| split row ':'
|
||||
| where (
|
||||
| $it !~ $ghostty_data_dir
|
||||
)
|
||||
| str join ':'
|
||||
@@ -68,6 +68,7 @@ pub fn setup(
|
||||
command,
|
||||
env,
|
||||
exe,
|
||||
features,
|
||||
);
|
||||
|
||||
// Setup our feature env vars
|
||||
@@ -76,13 +77,15 @@ pub fn setup(
|
||||
return result;
|
||||
}
|
||||
|
||||
fn setupShell(
|
||||
alloc_arena: Allocator,
|
||||
resource_dir: []const u8,
|
||||
command: config.Command,
|
||||
env: *EnvMap,
|
||||
exe: []const u8,
|
||||
) !?ShellIntegration {
|
||||
fn setupShell(alloc_arena: Allocator, resource_dir: []const u8, command: config.Command, env: *EnvMap, exe: []const u8, features: config.ShellIntegrationFeatures) !?ShellIntegration {
|
||||
if (std.mem.eql(u8, "nu", exe)) {
|
||||
try setupNu(alloc_arena, resource_dir, env, features);
|
||||
return null;
|
||||
// // return .{
|
||||
// // .shell = .nu,
|
||||
// // .command = try command.clone(alloc_arena),
|
||||
// // };
|
||||
}
|
||||
if (std.mem.eql(u8, "bash", exe)) {
|
||||
// Apple distributes their own patched version of Bash 3.2
|
||||
// on macOS that disables the ENV-based POSIX startup path.
|
||||
@@ -652,6 +655,19 @@ test "xdg: existing XDG_DATA_DIRS" {
|
||||
try testing.expectEqualStrings("./shell-integration:/opt/share", env.get("XDG_DATA_DIRS").?);
|
||||
}
|
||||
|
||||
/// Setup the nushell shell integration. This works by setting
|
||||
/// XDG_DATA_DIRS so that it can be picked automatically by
|
||||
/// nushell on startup.
|
||||
/// Only implements `ssh-*` shell features. Rest are not supported.
|
||||
fn setupNu(alloc_arena: Allocator, resource_dir: []const u8, env: *EnvMap, features: config.ShellIntegrationFeatures) !void {
|
||||
// This makes sure that `Nu` loads our integration file
|
||||
// and wraps the `ssh` function only if the `ssh` features
|
||||
// are enabled.
|
||||
// Otherwise, it does not do anything.
|
||||
if (features.@"ssh-env" or features.@"ssh-terminfo") {
|
||||
try setupXdgDataDirs(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.
|
||||
|
||||
Reference in New Issue
Block a user